Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *  fs/bfs/dir.c
0004  *  BFS directory operations.
0005  *  Copyright (C) 1999-2018  Tigran Aivazian <aivazian.tigran@gmail.com>
0006  *  Made endianness-clean by Andrew Stribblehill <ads@wompom.org> 2005
0007  */
0008 
0009 #include <linux/time.h>
0010 #include <linux/string.h>
0011 #include <linux/fs.h>
0012 #include <linux/buffer_head.h>
0013 #include <linux/sched.h>
0014 #include "bfs.h"
0015 
0016 #undef DEBUG
0017 
0018 #ifdef DEBUG
0019 #define dprintf(x...)   printf(x)
0020 #else
0021 #define dprintf(x...)
0022 #endif
0023 
0024 static int bfs_add_entry(struct inode *dir, const struct qstr *child, int ino);
0025 static struct buffer_head *bfs_find_entry(struct inode *dir,
0026                 const struct qstr *child,
0027                 struct bfs_dirent **res_dir);
0028 
0029 static int bfs_readdir(struct file *f, struct dir_context *ctx)
0030 {
0031     struct inode *dir = file_inode(f);
0032     struct buffer_head *bh;
0033     struct bfs_dirent *de;
0034     unsigned int offset;
0035     int block;
0036 
0037     if (ctx->pos & (BFS_DIRENT_SIZE - 1)) {
0038         printf("Bad f_pos=%08lx for %s:%08lx\n",
0039                     (unsigned long)ctx->pos,
0040                     dir->i_sb->s_id, dir->i_ino);
0041         return -EINVAL;
0042     }
0043 
0044     while (ctx->pos < dir->i_size) {
0045         offset = ctx->pos & (BFS_BSIZE - 1);
0046         block = BFS_I(dir)->i_sblock + (ctx->pos >> BFS_BSIZE_BITS);
0047         bh = sb_bread(dir->i_sb, block);
0048         if (!bh) {
0049             ctx->pos += BFS_BSIZE - offset;
0050             continue;
0051         }
0052         do {
0053             de = (struct bfs_dirent *)(bh->b_data + offset);
0054             if (de->ino) {
0055                 int size = strnlen(de->name, BFS_NAMELEN);
0056                 if (!dir_emit(ctx, de->name, size,
0057                         le16_to_cpu(de->ino),
0058                         DT_UNKNOWN)) {
0059                     brelse(bh);
0060                     return 0;
0061                 }
0062             }
0063             offset += BFS_DIRENT_SIZE;
0064             ctx->pos += BFS_DIRENT_SIZE;
0065         } while ((offset < BFS_BSIZE) && (ctx->pos < dir->i_size));
0066         brelse(bh);
0067     }
0068     return 0;
0069 }
0070 
0071 const struct file_operations bfs_dir_operations = {
0072     .read       = generic_read_dir,
0073     .iterate_shared = bfs_readdir,
0074     .fsync      = generic_file_fsync,
0075     .llseek     = generic_file_llseek,
0076 };
0077 
0078 static int bfs_create(struct user_namespace *mnt_userns, struct inode *dir,
0079               struct dentry *dentry, umode_t mode, bool excl)
0080 {
0081     int err;
0082     struct inode *inode;
0083     struct super_block *s = dir->i_sb;
0084     struct bfs_sb_info *info = BFS_SB(s);
0085     unsigned long ino;
0086 
0087     inode = new_inode(s);
0088     if (!inode)
0089         return -ENOMEM;
0090     mutex_lock(&info->bfs_lock);
0091     ino = find_first_zero_bit(info->si_imap, info->si_lasti + 1);
0092     if (ino > info->si_lasti) {
0093         mutex_unlock(&info->bfs_lock);
0094         iput(inode);
0095         return -ENOSPC;
0096     }
0097     set_bit(ino, info->si_imap);
0098     info->si_freei--;
0099     inode_init_owner(&init_user_ns, inode, dir, mode);
0100     inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
0101     inode->i_blocks = 0;
0102     inode->i_op = &bfs_file_inops;
0103     inode->i_fop = &bfs_file_operations;
0104     inode->i_mapping->a_ops = &bfs_aops;
0105     inode->i_ino = ino;
0106     BFS_I(inode)->i_dsk_ino = ino;
0107     BFS_I(inode)->i_sblock = 0;
0108     BFS_I(inode)->i_eblock = 0;
0109     insert_inode_hash(inode);
0110         mark_inode_dirty(inode);
0111     bfs_dump_imap("create", s);
0112 
0113     err = bfs_add_entry(dir, &dentry->d_name, inode->i_ino);
0114     if (err) {
0115         inode_dec_link_count(inode);
0116         mutex_unlock(&info->bfs_lock);
0117         iput(inode);
0118         return err;
0119     }
0120     mutex_unlock(&info->bfs_lock);
0121     d_instantiate(dentry, inode);
0122     return 0;
0123 }
0124 
0125 static struct dentry *bfs_lookup(struct inode *dir, struct dentry *dentry,
0126                         unsigned int flags)
0127 {
0128     struct inode *inode = NULL;
0129     struct buffer_head *bh;
0130     struct bfs_dirent *de;
0131     struct bfs_sb_info *info = BFS_SB(dir->i_sb);
0132 
0133     if (dentry->d_name.len > BFS_NAMELEN)
0134         return ERR_PTR(-ENAMETOOLONG);
0135 
0136     mutex_lock(&info->bfs_lock);
0137     bh = bfs_find_entry(dir, &dentry->d_name, &de);
0138     if (bh) {
0139         unsigned long ino = (unsigned long)le16_to_cpu(de->ino);
0140         brelse(bh);
0141         inode = bfs_iget(dir->i_sb, ino);
0142     }
0143     mutex_unlock(&info->bfs_lock);
0144     return d_splice_alias(inode, dentry);
0145 }
0146 
0147 static int bfs_link(struct dentry *old, struct inode *dir,
0148                         struct dentry *new)
0149 {
0150     struct inode *inode = d_inode(old);
0151     struct bfs_sb_info *info = BFS_SB(inode->i_sb);
0152     int err;
0153 
0154     mutex_lock(&info->bfs_lock);
0155     err = bfs_add_entry(dir, &new->d_name, inode->i_ino);
0156     if (err) {
0157         mutex_unlock(&info->bfs_lock);
0158         return err;
0159     }
0160     inc_nlink(inode);
0161     inode->i_ctime = current_time(inode);
0162     mark_inode_dirty(inode);
0163     ihold(inode);
0164     d_instantiate(new, inode);
0165     mutex_unlock(&info->bfs_lock);
0166     return 0;
0167 }
0168 
0169 static int bfs_unlink(struct inode *dir, struct dentry *dentry)
0170 {
0171     int error = -ENOENT;
0172     struct inode *inode = d_inode(dentry);
0173     struct buffer_head *bh;
0174     struct bfs_dirent *de;
0175     struct bfs_sb_info *info = BFS_SB(inode->i_sb);
0176 
0177     mutex_lock(&info->bfs_lock);
0178     bh = bfs_find_entry(dir, &dentry->d_name, &de);
0179     if (!bh || (le16_to_cpu(de->ino) != inode->i_ino))
0180         goto out_brelse;
0181 
0182     if (!inode->i_nlink) {
0183         printf("unlinking non-existent file %s:%lu (nlink=%d)\n",
0184                     inode->i_sb->s_id, inode->i_ino,
0185                     inode->i_nlink);
0186         set_nlink(inode, 1);
0187     }
0188     de->ino = 0;
0189     mark_buffer_dirty_inode(bh, dir);
0190     dir->i_ctime = dir->i_mtime = current_time(dir);
0191     mark_inode_dirty(dir);
0192     inode->i_ctime = dir->i_ctime;
0193     inode_dec_link_count(inode);
0194     error = 0;
0195 
0196 out_brelse:
0197     brelse(bh);
0198     mutex_unlock(&info->bfs_lock);
0199     return error;
0200 }
0201 
0202 static int bfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
0203               struct dentry *old_dentry, struct inode *new_dir,
0204               struct dentry *new_dentry, unsigned int flags)
0205 {
0206     struct inode *old_inode, *new_inode;
0207     struct buffer_head *old_bh, *new_bh;
0208     struct bfs_dirent *old_de, *new_de;
0209     struct bfs_sb_info *info;
0210     int error = -ENOENT;
0211 
0212     if (flags & ~RENAME_NOREPLACE)
0213         return -EINVAL;
0214 
0215     old_bh = new_bh = NULL;
0216     old_inode = d_inode(old_dentry);
0217     if (S_ISDIR(old_inode->i_mode))
0218         return -EINVAL;
0219 
0220     info = BFS_SB(old_inode->i_sb);
0221 
0222     mutex_lock(&info->bfs_lock);
0223     old_bh = bfs_find_entry(old_dir, &old_dentry->d_name, &old_de);
0224 
0225     if (!old_bh || (le16_to_cpu(old_de->ino) != old_inode->i_ino))
0226         goto end_rename;
0227 
0228     error = -EPERM;
0229     new_inode = d_inode(new_dentry);
0230     new_bh = bfs_find_entry(new_dir, &new_dentry->d_name, &new_de);
0231 
0232     if (new_bh && !new_inode) {
0233         brelse(new_bh);
0234         new_bh = NULL;
0235     }
0236     if (!new_bh) {
0237         error = bfs_add_entry(new_dir, &new_dentry->d_name,
0238                     old_inode->i_ino);
0239         if (error)
0240             goto end_rename;
0241     }
0242     old_de->ino = 0;
0243     old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir);
0244     mark_inode_dirty(old_dir);
0245     if (new_inode) {
0246         new_inode->i_ctime = current_time(new_inode);
0247         inode_dec_link_count(new_inode);
0248     }
0249     mark_buffer_dirty_inode(old_bh, old_dir);
0250     error = 0;
0251 
0252 end_rename:
0253     mutex_unlock(&info->bfs_lock);
0254     brelse(old_bh);
0255     brelse(new_bh);
0256     return error;
0257 }
0258 
0259 const struct inode_operations bfs_dir_inops = {
0260     .create         = bfs_create,
0261     .lookup         = bfs_lookup,
0262     .link           = bfs_link,
0263     .unlink         = bfs_unlink,
0264     .rename         = bfs_rename,
0265 };
0266 
0267 static int bfs_add_entry(struct inode *dir, const struct qstr *child, int ino)
0268 {
0269     const unsigned char *name = child->name;
0270     int namelen = child->len;
0271     struct buffer_head *bh;
0272     struct bfs_dirent *de;
0273     int block, sblock, eblock, off, pos;
0274     int i;
0275 
0276     dprintf("name=%s, namelen=%d\n", name, namelen);
0277 
0278     if (!namelen)
0279         return -ENOENT;
0280     if (namelen > BFS_NAMELEN)
0281         return -ENAMETOOLONG;
0282 
0283     sblock = BFS_I(dir)->i_sblock;
0284     eblock = BFS_I(dir)->i_eblock;
0285     for (block = sblock; block <= eblock; block++) {
0286         bh = sb_bread(dir->i_sb, block);
0287         if (!bh)
0288             return -EIO;
0289         for (off = 0; off < BFS_BSIZE; off += BFS_DIRENT_SIZE) {
0290             de = (struct bfs_dirent *)(bh->b_data + off);
0291             if (!de->ino) {
0292                 pos = (block - sblock) * BFS_BSIZE + off;
0293                 if (pos >= dir->i_size) {
0294                     dir->i_size += BFS_DIRENT_SIZE;
0295                     dir->i_ctime = current_time(dir);
0296                 }
0297                 dir->i_mtime = current_time(dir);
0298                 mark_inode_dirty(dir);
0299                 de->ino = cpu_to_le16((u16)ino);
0300                 for (i = 0; i < BFS_NAMELEN; i++)
0301                     de->name[i] =
0302                         (i < namelen) ? name[i] : 0;
0303                 mark_buffer_dirty_inode(bh, dir);
0304                 brelse(bh);
0305                 return 0;
0306             }
0307         }
0308         brelse(bh);
0309     }
0310     return -ENOSPC;
0311 }
0312 
0313 static inline int bfs_namecmp(int len, const unsigned char *name,
0314                             const char *buffer)
0315 {
0316     if ((len < BFS_NAMELEN) && buffer[len])
0317         return 0;
0318     return !memcmp(name, buffer, len);
0319 }
0320 
0321 static struct buffer_head *bfs_find_entry(struct inode *dir,
0322             const struct qstr *child,
0323             struct bfs_dirent **res_dir)
0324 {
0325     unsigned long block = 0, offset = 0;
0326     struct buffer_head *bh = NULL;
0327     struct bfs_dirent *de;
0328     const unsigned char *name = child->name;
0329     int namelen = child->len;
0330 
0331     *res_dir = NULL;
0332     if (namelen > BFS_NAMELEN)
0333         return NULL;
0334 
0335     while (block * BFS_BSIZE + offset < dir->i_size) {
0336         if (!bh) {
0337             bh = sb_bread(dir->i_sb, BFS_I(dir)->i_sblock + block);
0338             if (!bh) {
0339                 block++;
0340                 continue;
0341             }
0342         }
0343         de = (struct bfs_dirent *)(bh->b_data + offset);
0344         offset += BFS_DIRENT_SIZE;
0345         if (le16_to_cpu(de->ino) &&
0346                 bfs_namecmp(namelen, name, de->name)) {
0347             *res_dir = de;
0348             return bh;
0349         }
0350         if (offset < bh->b_size)
0351             continue;
0352         brelse(bh);
0353         bh = NULL;
0354         offset = 0;
0355         block++;
0356     }
0357     brelse(bh);
0358     return NULL;
0359 }