diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-07-29 03:58:24 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1997-07-29 03:58:24 +0000 |
commit | 1c5c0c934f91fbce2825acbb849e98781e774c1d (patch) | |
tree | 12b5ae03516d4103bc070e4579ae1f7f71c27d24 /fs | |
parent | 4fe70c31de87823ac9e804f4795589ba74dc6971 (diff) |
Merge with 2.1.47. Some more cleanup and module fixes.
Diffstat (limited to 'fs')
-rw-r--r-- | fs/dcache.c | 49 | ||||
-rw-r--r-- | fs/fat/inode.c | 12 | ||||
-rw-r--r-- | fs/fat/misc.c | 2 | ||||
-rw-r--r-- | fs/hpfs/hpfs_fs.c | 36 | ||||
-rw-r--r-- | fs/inode.c | 54 | ||||
-rw-r--r-- | fs/lockd/svcsubs.c | 16 | ||||
-rw-r--r-- | fs/minix/bitmap.c | 3 | ||||
-rw-r--r-- | fs/minix/dir.c | 1 | ||||
-rw-r--r-- | fs/minix/file.c | 8 | ||||
-rw-r--r-- | fs/minix/namei.c | 3 | ||||
-rw-r--r-- | fs/msdos/namei.c | 240 | ||||
-rw-r--r-- | fs/namei.c | 30 | ||||
-rw-r--r-- | fs/nfs/write.c | 2 | ||||
-rw-r--r-- | fs/nfsd/export.c | 100 | ||||
-rw-r--r-- | fs/nfsd/lockd.c | 2 | ||||
-rw-r--r-- | fs/nfsd/nfs3proc.c | 7 | ||||
-rw-r--r-- | fs/nfsd/nfsctl.c | 5 | ||||
-rw-r--r-- | fs/nfsd/nfsfh.c | 84 | ||||
-rw-r--r-- | fs/nfsd/nfsproc.c | 167 | ||||
-rw-r--r-- | fs/nfsd/nfsxdr.c | 6 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 584 | ||||
-rw-r--r-- | fs/open.c | 35 | ||||
-rw-r--r-- | fs/romfs/inode.c | 124 | ||||
-rw-r--r-- | fs/super.c | 1 |
24 files changed, 815 insertions, 756 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 395aed829..97937822f 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -17,6 +17,7 @@ #include <linux/mm.h> #include <linux/fs.h> #include <linux/malloc.h> +#include <linux/init.h> /* * This is the single most critical data structure when it comes @@ -229,6 +230,46 @@ struct dentry * d_lookup(struct dentry * dir, struct qstr * name) return __dlookup(d_hash(dir, name->hash), dir, name); } +/* + * An insecure source has sent us a dentry, here we verify it. + * + * This is just to make knfsd able to have the dentry pointer + * in the NFS file handle. + * + * NOTE! Do _not_ dereference the pointers before we have + * validated them. We can test the pointer values, but we + * must not actually use them until we have found a valid + * copy of the pointer in kernel space.. + */ +int d_validate(struct dentry *dentry, struct dentry *dparent, + unsigned int hash, unsigned int len) +{ + struct list_head *base = d_hash(dparent, hash); + struct list_head *lhp = base; + + while ((lhp = lhp->next) != base) { + if (dentry == list_entry(lhp, struct dentry, d_hash)) + goto found_it; + } + + /* Special case, local mount points don't live in the hashes. + * So if we exhausted the chain, search the super blocks. + */ + if (dentry && dentry == dparent) { + struct super_block *sb; + + for (sb = super_blocks + 0; sb < super_blocks + NR_SUPER; sb++) { + if (sb->s_root == dentry) + goto found_it; + } + } + return 0; +found_it: + return (dentry->d_parent == dparent) && + (dentry->d_name.hash == hash) && + (dentry->d_name.len == len); +} + static inline void d_insert_to_parent(struct dentry * entry, struct dentry * parent) { list_add(&entry->d_hash, d_hash(dget(parent), entry->d_name.hash)); @@ -313,13 +354,17 @@ void d_move(struct dentry * dentry, struct dentry * newdir, struct qstr * newnam d_insert_to_parent(dentry, newdir); } +/* + * This is broken in more ways than one. Unchecked recursion, + * unchecked buffer size. Get rid of it. + */ int d_path(struct dentry * entry, struct dentry * chroot, char * buf) { if (IS_ROOT(entry) || (chroot && entry == chroot)) { *buf = '/'; return 1; } else { - int len = d_path(entry->d_parent, chroot, buf); + int len = d_path(entry->d_covers->d_parent, chroot, buf); buf += len; if (len > 1) { @@ -331,7 +376,7 @@ int d_path(struct dentry * entry, struct dentry * chroot, char * buf) } } -void dcache_init(void) +__initfunc(void dcache_init(void)) { int i; struct list_head *d = dentry_hashtable; diff --git a/fs/fat/inode.c b/fs/fat/inode.c index e35722aff..8ffd91e3d 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -44,8 +44,18 @@ void fat_put_inode(struct inode *inode) MSDOS_I(inode)->i_linked = NULL; } if (MSDOS_I(inode)->i_busy) fat_cache_inval_inode(inode); - return; } +} + +void fat_delete_inode(struct inode *inode) +{ + struct inode *depend, *linked; + struct super_block *sb; + + depend = MSDOS_I(inode)->i_depend; + linked = MSDOS_I(inode)->i_linked; + sb = inode->i_sb; + inode->i_size = 0; fat_truncate(inode); if (depend) { diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 034f62c1f..ee0df4a15 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -40,7 +40,7 @@ void fat_fs_panic(struct super_block *s,const char *msg) not_ro = !(s->s_flags & MS_RDONLY); if (not_ro) s->s_flags |= MS_RDONLY; - printk("Filesystem panic (dev %s).", kdevname(s->s_dev)); + printk("Filesystem panic (dev %s).\n %s\n", kdevname(s->s_dev), msg); if (not_ro) printk(" File system has been set read-only\n"); } diff --git a/fs/hpfs/hpfs_fs.c b/fs/hpfs/hpfs_fs.c index 878a3f069..70f293821 100644 --- a/fs/hpfs/hpfs_fs.c +++ b/fs/hpfs/hpfs_fs.c @@ -128,15 +128,16 @@ typedef void nonconst; static void hpfs_read_inode(struct inode *); static void hpfs_put_super(struct super_block *); -static void hpfs_statfs(struct super_block *, struct statfs *, int); +static int hpfs_statfs(struct super_block *, struct statfs *, int); static int hpfs_remount_fs(struct super_block *, int *, char *); static const struct super_operations hpfs_sops = { hpfs_read_inode, /* read_inode */ - NULL, /* notify_change */ NULL, /* write_inode */ NULL, /* put_inode */ + NULL, /* delete_inode */ + NULL, /* notify_change */ hpfs_put_super, /* put_super */ NULL, /* write_super */ hpfs_statfs, /* statfs */ @@ -175,6 +176,7 @@ static const struct inode_operations hpfs_file_iops = NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ generic_readpage, /* readpage */ NULL, /* writepage */ (int (*)(struct inode *, int)) @@ -189,7 +191,7 @@ static long hpfs_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count); static int hpfs_readdir(struct inode *inode, struct file *filp, void *dirent, filldir_t filldir); -static int hpfs_lookup(struct inode *, const char *, int, struct inode **); +static int hpfs_lookup(struct inode *, struct dentry *); static const struct file_operations hpfs_dir_ops = { @@ -485,10 +487,10 @@ struct super_block *hpfs_read_super(struct super_block *s, * all set. try it out. */ - s->s_mounted = iget(s, s->s_hpfs_root); + s->s_root = d_alloc_root(iget(s, s->s_hpfs_root), NULL); unlock_super(s); - if (!s->s_mounted) { + if (!s->s_root) { printk("HPFS: hpfs_read_super: inode get failed\n"); s->s_dev = 0; MOD_DEC_USE_COUNT; @@ -501,7 +503,8 @@ struct super_block *hpfs_read_super(struct super_block *s, root_dno = fnode_dno(dev, s->s_hpfs_root); if (root_dno) - de = map_dirent(s->s_mounted, root_dno, "\001\001", 2, &qbh); + de = map_dirent(s->s_root->d_inode, root_dno, + "\001\001", 2, &qbh); if (!root_dno || !de) { printk("HPFS: " "hpfs_read_super: root dir isn't in the root dir\n"); @@ -510,9 +513,9 @@ struct super_block *hpfs_read_super(struct super_block *s, return 0; } - s->s_mounted->i_atime = local_to_gmt(de->read_date); - s->s_mounted->i_mtime = local_to_gmt(de->write_date); - s->s_mounted->i_ctime = local_to_gmt(de->creation_date); + s->s_root->d_inode->i_atime = local_to_gmt(de->read_date); + s->s_root->d_inode->i_mtime = local_to_gmt(de->write_date); + s->s_root->d_inode->i_ctime = local_to_gmt(de->creation_date); brelse4(&qbh); return s; @@ -739,7 +742,7 @@ static void hpfs_put_super(struct super_block *s) * directory band -- not exactly right but pretty analogous. */ -static void hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz) +static int hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz) { struct statfs tmp; @@ -763,7 +766,8 @@ static void hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz) tmp.f_files = s->s_hpfs_dirband_size; tmp.f_ffree = s->s_hpfs_n_free_dnodes; tmp.f_namelen = 254; - copy_to_user(buf, &tmp, bufsiz); + + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } /* @@ -1115,17 +1119,17 @@ static secno bplus_lookup(struct inode *inode, struct bplus_header *b, * the boondocks.) */ -static int hpfs_lookup(struct inode *dir, const char *name, int len, - struct inode **result) +static int hpfs_lookup(struct inode *dir, struct dentry *dentry) { struct quad_buffer_head qbh; struct hpfs_dirent *de; struct inode *inode; ino_t ino; + const char *name = dentry->d_name.name; + int len = dentry->d_name.len; /* In case of madness */ - *result = 0; if (dir == 0) return -ENOENT; if (!S_ISDIR(dir->i_mode)) @@ -1197,7 +1201,7 @@ static int hpfs_lookup(struct inode *dir, const char *name, int len, * Made it. */ - *result = inode; + d_instantiate(dentry, inode); iput(dir); return 0; @@ -1697,7 +1701,7 @@ static void *map_4sectors(kdev_t dev, unsigned secno, if (!data) goto bail; - qbh->bh[0] = bh = breada(dev, secno, 512, 0, UINT_MAX); + qbh->bh[0] = bh = bread(dev, secno, 512); if (!bh) goto bail0; memcpy(data, bh->b_data, 512); diff --git a/fs/inode.c b/fs/inode.c index 8813bbd45..8fc081b77 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -141,32 +141,31 @@ static inline void write_inode(struct inode *inode) inode->i_sb->s_op->write_inode(inode); } +static inline void sync_one(struct list_head *head, struct list_head *clean, + struct list_head *placement, struct inode *inode) +{ + list_del(placement); + if (test_bit(I_LOCK, &inode->i_state)) { + list_add(placement, head); + spin_unlock(&inode_lock); + __wait_on_inode(inode); + } else { + list_add(placement, clean); + clear_bit(I_DIRTY, &inode->i_state); + set_bit(I_LOCK, &inode->i_state); + spin_unlock(&inode_lock); + write_inode(inode); + unlock_inode(inode); + } + spin_lock(&inode_lock); +} + static inline void sync_list(struct list_head *head, struct list_head *clean) { struct list_head * tmp; - while ((tmp = head->prev) != head) { - struct inode *inode = list_entry(tmp, struct inode, i_list); - list_del(tmp); - - /* - * If the inode is locked, it's already being written out. - * We have to wait for it, though. - */ - if (test_bit(I_LOCK, &inode->i_state)) { - list_add(tmp, head); - spin_unlock(&inode_lock); - __wait_on_inode(inode); - } else { - list_add(tmp, clean); - clear_bit(I_DIRTY, &inode->i_state); - set_bit(I_LOCK, &inode->i_state); - spin_unlock(&inode_lock); - write_inode(inode); - unlock_inode(inode); - } - spin_lock(&inode_lock); - } + while ((tmp = head->prev) != head) + sync_one(head, clean, tmp, list_entry(tmp, struct inode, i_list)); } /* @@ -182,6 +181,17 @@ void sync_inodes(kdev_t dev) } /* + * Needed by knfsd + */ +void write_inode_now(struct inode *inode) +{ + spin_lock(&inode_lock); + if (test_bit(I_DIRTY, &inode->i_state)) + sync_one(&inode_dirty, &inode_in_use, &inode->i_list, inode); + spin_unlock(&inode_lock); +} + +/* * This is called by the filesystem to tell us * that the inode is no longer useful. We just * terminate it with extreme predjudice. diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index afc3fb2f0..3526867ca 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -24,7 +24,7 @@ * Global file hash table */ #define FILE_NRHASH 32 -#define FILE_HASH(dev, ino) (((dev) + (ino)) & FILE_NRHASH) +#define FILE_HASH(dhash) ((dhash) & FILE_NRHASH) static struct nlm_file * nlm_files[FILE_NRHASH]; static struct semaphore nlm_file_sema = MUTEX; @@ -43,21 +43,21 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, { struct nlm_file *file; struct knfs_fh *fh = (struct knfs_fh *) f; - unsigned int hash = FILE_HASH(fh->fh_dev, fh->fh_ino); + unsigned int hash = FILE_HASH(fh->fh_dhash); u32 nfserr; - dprintk("lockd: nlm_file_lookup(%04x/%ld)\n", fh->fh_dev, fh->fh_ino); + dprintk("lockd: nlm_file_lookup(%p)\n", fh->fh_dentry); /* Lock file table */ down(&nlm_file_sema); for (file = nlm_files[hash]; file; file = file->f_next) { - if (file->f_handle.fh_ino == fh->fh_ino + if (file->f_handle.fh_dentry == fh->fh_dentry && !memcmp(&file->f_handle, fh, sizeof(*fh))) goto found; } - dprintk("lockd: creating file for %04x/%ld\n", fh->fh_dev, fh->fh_ino); + dprintk("lockd: creating file for %p\n", fh->fh_dentry); if (!(file = (struct nlm_file *) kmalloc(sizeof(*file), GFP_KERNEL))) { up(&nlm_file_sema); return nlm_lck_denied_nolocks; @@ -93,11 +93,11 @@ found: static inline void nlm_delete_file(struct nlm_file *file) { - struct inode *inode = nlmsvc_file_inode(file); + struct dentry *dentry = file->f_file.f_dentry; struct nlm_file **fp, *f; - dprintk("lockd: closing file %04x/%ld\n", inode->i_dev, inode->i_ino); - fp = nlm_files + FILE_HASH(inode->i_dev, inode->i_ino); + dprintk("lockd: closing file %p\n", dentry); + fp = nlm_files + FILE_HASH(dentry->d_name.hash); while ((f = *fp) != NULL) { if (f == file) { *fp = file->f_next; diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c index f47f779d5..9e27ee118 100644 --- a/fs/minix/bitmap.c +++ b/fs/minix/bitmap.c @@ -191,7 +191,7 @@ void minix_free_inode(struct inode * inode) printk("free_inode: inode has no device\n"); return; } - if (inode->i_count != 1) { + if (inode->i_count > 1) { printk("free_inode: inode has count=%d\n",inode->i_count); return; } @@ -251,7 +251,6 @@ struct inode * minix_new_inode(const struct inode * dir) iput(inode); return NULL; } - inode->i_count = 1; inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; diff --git a/fs/minix/dir.c b/fs/minix/dir.c index ec5113c4a..31ac394a2 100644 --- a/fs/minix/dir.c +++ b/fs/minix/dir.c @@ -92,5 +92,6 @@ static int minix_readdir(struct inode * inode, struct file * filp, } while (offset < 1024 && filp->f_pos < inode->i_size); brelse(bh); } + UPDATE_ATIME(inode); return 0; } diff --git a/fs/minix/file.c b/fs/minix/file.c index 7ca7cb075..ecd0d24d9 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -108,7 +108,13 @@ static long minix_file_write(struct inode * inode, struct file * filp, } } p = (pos % BLOCK_SIZE) + bh->b_data; - copy_from_user(p,buf,c); + c -= copy_from_user(p,buf,c); + if (!c) { + brelse(bh); + if (!written) + written = -EFAULT; + break; + } update_vm_cache(inode, pos, p, c); mark_buffer_uptodate(bh, 1); mark_buffer_dirty(bh, 0); diff --git a/fs/minix/namei.c b/fs/minix/namei.c index 718d3dd07..19c7df247 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -109,7 +109,7 @@ int minix_lookup(struct inode * dir, struct dentry *dentry) bh = minix_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); if (bh) { - unsigned long ino = le32_to_cpu(de->inode); + int ino = de->inode; brelse (bh); inode = iget(dir->i_sb, ino); @@ -593,6 +593,7 @@ int minix_link(struct inode * inode, struct inode * dir, inode->i_nlink++; inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); + inode->i_count++; d_instantiate(dentry, inode); return 0; } diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index 9481a763c..637649dae 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -53,9 +53,10 @@ void msdos_put_super(struct super_block *sb) struct super_operations msdos_sops = { msdos_read_inode, - fat_notify_change, fat_write_inode, fat_put_inode, + fat_delete_inode, + fat_notify_change, msdos_put_super, NULL, /* added in 0.96c */ fat_statfs, @@ -190,79 +191,56 @@ static int msdos_find(struct inode *dir,const char *name,int len, } /***** Get inode using directory and name */ -int msdos_lookup(struct inode *dir,const char *name,int len, - struct inode **result) +int msdos_lookup(struct inode *dir,struct dentry *dentry) { struct super_block *sb = dir->i_sb; int ino,res; struct msdos_dir_entry *de; struct buffer_head *bh; - struct inode *next; + struct inode *next, *inode; PRINTK (("msdos_lookup\n")); - *result = NULL; - if (!dir) return -ENOENT; - if (!S_ISDIR(dir->i_mode)) { - iput(dir); - return -ENOENT; - } - PRINTK (("msdos_lookup 2\n")); - if (len == 1 && name[0] == '.') { - *result = dir; - return 0; - } - if (len == 2 && name[0] == '.' && name[1] == '.') { - ino = fat_parent_ino(dir,0); - iput(dir); - if (ino < 0) return ino; - if (!(*result = iget(dir->i_sb,ino))) return -EACCES; + if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,&bh,&de,&ino)) < 0) { + d_add(dentry, NULL); return 0; } - PRINTK (("msdos_lookup 3\n")); - if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) { - iput(dir); - return res; - } PRINTK (("msdos_lookup 4\n")); if (bh) fat_brelse(sb, bh); PRINTK (("msdos_lookup 4.5\n")); - if (!(*result = iget(dir->i_sb,ino))) { - iput(dir); + if (!(inode = iget(dir->i_sb,ino))) return -EACCES; - } PRINTK (("msdos_lookup 5\n")); - if (!(*result)->i_sb || - ((*result)->i_sb->s_magic != MSDOS_SUPER_MAGIC)) { + if (!inode->i_sb || + (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC)) { /* crossed a mount point into a non-msdos fs */ - iput(dir); + d_add(dentry, inode); return 0; } - if (MSDOS_I(*result)->i_busy) { /* mkdir in progress */ - iput(*result); - iput(dir); - return -ENOENT; + if (MSDOS_I(inode)->i_busy) { /* mkdir in progress */ + iput(inode); + d_add(dentry, NULL); + return 0; } PRINTK (("msdos_lookup 6\n")); - while (MSDOS_I(*result)->i_old) { - next = MSDOS_I(*result)->i_old; - iput(*result); - if (!(*result = iget(next->i_sb,next->i_ino))) { + while (MSDOS_I(inode)->i_old) { + next = MSDOS_I(inode)->i_old; + iput(inode); + if (!(inode = iget(next->i_sb,next->i_ino))) { fat_fs_panic(dir->i_sb,"msdos_lookup: Can't happen"); - iput(dir); return -ENOENT; } } PRINTK (("msdos_lookup 7\n")); - iput(dir); + d_add(dentry, inode); PRINTK (("msdos_lookup 8\n")); return 0; } /***** Creates a directory entry (name is already formatted). */ -static int msdos_create_entry(struct inode *dir, const char *name,int len, +static int msdos_create_entry(struct inode *dir, const char *name, int is_dir, int is_hid, struct inode **result) { struct super_block *sb = dir->i_sb; @@ -270,6 +248,7 @@ static int msdos_create_entry(struct inode *dir, const char *name,int len, struct msdos_dir_entry *de; int res,ino; + *result = NULL; if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) { if (res != -ENOENT) return res; if (dir->i_ino == MSDOS_ROOT_INO) return -ENOSPC; @@ -300,23 +279,22 @@ static int msdos_create_entry(struct inode *dir, const char *name,int len, } /***** Create a file or directory */ -int msdos_create(struct inode *dir,const char *name,int len,int mode, - struct inode **result) +int msdos_create(struct inode *dir,struct dentry *dentry,int mode) { struct super_block *sb = dir->i_sb; struct buffer_head *bh; struct msdos_dir_entry *de; + struct inode *inode; char msdos_name[MSDOS_NAME]; int ino,res,is_hid; if (!dir) return -ENOENT; if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check, - name,len,msdos_name,0, - MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) { - iput(dir); + dentry->d_name.name,dentry->d_name.len, + msdos_name,0, + MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) return res; - } - is_hid = (name[0]=='.') && (msdos_name[0]!='.'); + is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.'); fat_lock_creation(); /* Scan for existing file twice, so that creating a file fails * with -EINVAL if the other (dotfile/nondotfile) exists. @@ -325,19 +303,17 @@ int msdos_create(struct inode *dir,const char *name,int len,int mode, if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_HID) >= 0) { fat_unlock_creation(); fat_brelse(sb, bh); - iput(dir); return is_hid ? -EEXIST : -EINVAL; } if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_NOTHID) >= 0) { fat_unlock_creation(); fat_brelse(sb, bh); - iput(dir); return is_hid ? -EINVAL : -EEXIST; } - res = msdos_create_entry(dir,msdos_name,len,S_ISDIR(mode),is_hid, - result); + res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),is_hid, + &inode); fat_unlock_creation(); - iput(dir); + d_instantiate(dentry, inode); return res; } @@ -388,7 +364,7 @@ static int msdos_empty(struct inode *dir) } /***** Remove a directory */ -int msdos_rmdir(struct inode *dir,const char *name,int len) +int msdos_rmdir(struct inode *dir,struct dentry *dentry) { struct super_block *sb = dir->i_sb; int res,ino; @@ -399,11 +375,10 @@ int msdos_rmdir(struct inode *dir,const char *name,int len) bh = NULL; inode = NULL; res = -EPERM; - if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) + if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len, + &bh,&de,&ino)) < 0) goto rmdir_done; - if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) goto rmdir_done; - res = -ENOENT; - if (!(inode = iget(dir->i_sb,ino))) goto rmdir_done; + inode = dentry->d_inode; res = -ENOTDIR; if (!S_ISDIR(inode->i_mode)) goto rmdir_done; res = -EBUSY; @@ -419,16 +394,15 @@ int msdos_rmdir(struct inode *dir,const char *name,int len) mark_inode_dirty(dir); de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); + d_delete(dentry); res = 0; rmdir_done: fat_brelse(sb, bh); - iput(dir); - iput(inode); return res; } /***** Make a directory */ -int msdos_mkdir(struct inode *dir,const char *name,int len,int mode) +int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode) { struct super_block *sb = dir->i_sb; struct buffer_head *bh; @@ -438,37 +412,34 @@ int msdos_mkdir(struct inode *dir,const char *name,int len,int mode) int ino,res,is_hid; if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check, - name,len,msdos_name,0, - MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) { - iput(dir); + dentry->d_name.name,dentry->d_name.len, + msdos_name,0, + MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) return res; - } - is_hid = (name[0]=='.') && (msdos_name[0]!='.'); + is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.'); fat_lock_creation(); if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) { fat_unlock_creation(); fat_brelse(sb, bh); - iput(dir); return -EEXIST; } - if ((res = msdos_create_entry(dir,msdos_name,len,1,is_hid, + if ((res = msdos_create_entry(dir,msdos_name,1,is_hid, &inode)) < 0) { fat_unlock_creation(); - iput(dir); return res; } dir->i_nlink++; inode->i_nlink = 2; /* no need to mark them dirty */ MSDOS_I(inode)->i_busy = 1; /* prevent lookups */ if ((res = fat_add_cluster(inode)) < 0) goto mkdir_error; - if ((res = msdos_create_entry(inode,MSDOS_DOT,1,1,0,&dot)) < 0) + if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0) goto mkdir_error; dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */ MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start; dot->i_nlink = inode->i_nlink; mark_inode_dirty(dot); iput(dot); - if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,2,1,0,&dot)) < 0) + if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0) goto mkdir_error; fat_unlock_creation(); dot->i_size = dir->i_size; @@ -477,12 +448,10 @@ int msdos_mkdir(struct inode *dir,const char *name,int len,int mode) mark_inode_dirty(dot); MSDOS_I(inode)->i_busy = 0; iput(dot); - iput(inode); - iput(dir); + d_instantiate(dentry, inode); return 0; mkdir_error: - iput(inode); - if (msdos_rmdir(dir,name,len) < 0) + if (msdos_rmdir(dir,dentry) < 0) fat_fs_panic(dir->i_sb,"rmdir in mkdir failed"); fat_unlock_creation(); return res; @@ -491,8 +460,7 @@ mkdir_error: /***** Unlink a file */ static int msdos_unlinkx( struct inode *dir, - const char *name, - int len, + struct dentry *dentry, int nospc) /* Flag special file ? */ { struct super_block *sb = dir->i_sb; @@ -503,12 +471,10 @@ static int msdos_unlinkx( bh = NULL; inode = NULL; - if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) - goto unlink_done; - if (!(inode = iget(dir->i_sb,ino))) { - res = -ENOENT; + if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len, + &bh,&de,&ino)) < 0) goto unlink_done; - } + inode = dentry->d_inode; if (!S_ISREG(inode->i_mode) && nospc){ res = -EPERM; goto unlink_done; @@ -524,28 +490,28 @@ static int msdos_unlinkx( mark_inode_dirty(dir); de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); + d_delete(dentry); /* This also frees the inode */ unlink_done: fat_brelse(sb, bh); - iput(inode); - iput(dir); return res; } /***** Unlink, as called for msdosfs */ -int msdos_unlink(struct inode *dir,const char *name,int len) +int msdos_unlink(struct inode *dir,struct dentry *dentry) { - return msdos_unlinkx (dir,name,len,1); + return msdos_unlinkx (dir,dentry,1); } /***** Unlink, as called for umsdosfs */ -int msdos_unlink_umsdos(struct inode *dir,const char *name,int len) +int msdos_unlink_umsdos(struct inode *dir,struct dentry *dentry) { - return msdos_unlinkx (dir,name,len,0); + return msdos_unlinkx (dir,dentry,0); } /***** Rename within a directory */ -static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len, - struct inode *new_dir,char *new_name,int new_len, +static int rename_same_dir(struct inode *old_dir,char *old_name, + struct dentry *old_dentry, + struct inode *new_dir,char *new_name,struct dentry *new_dentry, struct buffer_head *old_bh, struct msdos_dir_entry *old_de,int old_ino,int is_hid) { @@ -563,10 +529,7 @@ static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len, return -ENOENT; } if (exists) { - if (!(new_inode = iget(new_dir->i_sb,new_ino))) { - fat_brelse(sb, new_bh); - return -EIO; - } + new_inode = new_dentry->d_inode; error = S_ISDIR(new_inode->i_mode) ? (old_de->attr & ATTR_DIR) ? msdos_empty(new_inode) @@ -576,7 +539,6 @@ static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len, : 0; if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM; if (error) { - iput(new_inode); fat_brelse(sb, new_bh); return error; } @@ -589,50 +551,49 @@ static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len, mark_inode_dirty(new_inode); new_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, new_bh, 1); - iput(new_inode); fat_brelse(sb, new_bh); } memcpy(old_de->name,new_name,MSDOS_NAME); + /* Update the dcache */ + d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name); + d_delete(new_dentry); set_hid: old_de->attr = is_hid ? (old_de->attr | ATTR_HIDDEN) : (old_de->attr &~ ATTR_HIDDEN); fat_mark_buffer_dirty(sb, old_bh, 1); /* update binary info for conversion, i_attrs */ - if ((old_inode = iget(old_dir->i_sb,old_ino)) != NULL) { - MSDOS_I(old_inode)->i_attrs = is_hid - ? (MSDOS_I(old_inode)->i_attrs | ATTR_HIDDEN) - : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN); - iput(old_inode); - } + old_inode = old_dentry->d_inode; + MSDOS_I(old_inode)->i_attrs = is_hid + ? (MSDOS_I(old_inode)->i_attrs | ATTR_HIDDEN) + : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN); return 0; } /***** Rename across directories - a nonphysical move */ -static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len, - struct inode *new_dir,char *new_name,int new_len, +static int rename_diff_dir(struct inode *old_dir,char *old_name, + struct dentry *old_dentry, + struct inode *new_dir,char *new_name,struct dentry *new_dentry, struct buffer_head *old_bh, struct msdos_dir_entry *old_de,int old_ino,int is_hid) { struct super_block *sb = old_dir->i_sb; struct buffer_head *new_bh,*free_bh,*dotdot_bh; struct msdos_dir_entry *new_de,*free_de,*dotdot_de; - struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode,*walk; + struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode; + struct dentry *walk; int new_ino,free_ino,dotdot_ino; int error,exists,ino; if (old_dir->i_dev != new_dir->i_dev) return -EINVAL; if (old_ino == new_dir->i_ino) return -EINVAL; - if (!(walk = iget(new_dir->i_sb,new_dir->i_ino))) return -EIO; + walk = new_dentry; /* prevent moving directory below itself */ - while (walk->i_ino != MSDOS_ROOT_INO) { - ino = fat_parent_ino(walk,1); - iput(walk); - if (ino < 0) return ino; - if (ino == old_ino) return -EINVAL; - if (!(walk = iget(new_dir->i_sb,ino))) return -EIO; + for (;;) { + if (walk == old_dentry) return -EINVAL; + if (walk == walk->d_parent) break; + walk = walk->d_parent; } - iput(walk); /* find free spot */ while ((error = fat_scan(new_dir,NULL,&free_bh,&free_de,&free_ino, SCAN_ANY)) < 0) { @@ -641,14 +602,8 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len, if (error) return error; } exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0; - if (!(old_inode = iget(old_dir->i_sb,old_ino))) { - fat_brelse(sb, free_bh); - if (exists) - fat_brelse(sb, new_bh); - return -EIO; - } + old_inode = old_dentry->d_inode; if (*(unsigned char *) old_de->name == DELETED_FLAG) { - iput(old_inode); fat_brelse(sb, free_bh); if (exists) fat_brelse(sb, new_bh); @@ -656,11 +611,7 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len, } new_inode = NULL; /* to make GCC happy */ if (exists) { /* Trash the old file! */ - if (!(new_inode = iget(new_dir->i_sb,new_ino))) { - iput(old_inode); - fat_brelse(sb, new_bh); - return -EIO; - } + new_inode = new_dentry->d_inode; error = S_ISDIR(new_inode->i_mode) ? (old_de->attr & ATTR_DIR) ? msdos_empty(new_inode) @@ -670,8 +621,6 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len, : 0; if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM; if (error) { - iput(new_inode); - iput(old_inode); fat_brelse(sb, new_bh); return error; } @@ -690,10 +639,8 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len, free_de->name[0] = DELETED_FLAG; /* Don't mark free_bh as dirty. Both states are supposed to be equivalent. */ fat_brelse(sb, free_bh); - if (exists) { - iput(new_inode); + if (exists) fat_brelse(sb, new_bh); - } return -EIO; } if (exists && S_ISDIR(new_inode->i_mode)) { @@ -715,7 +662,6 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len, /* Two references now exist to free_inode so increase count */ free_inode->i_count++; /* free_inode is put after putting new_inode and old_inode */ - iput(new_inode); fat_brelse(sb, new_bh); } if (S_ISDIR(old_inode->i_mode)) { @@ -737,16 +683,18 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len, iput(dotdot_inode); fat_brelse(sb, dotdot_bh); } + /* Update the dcache */ + d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name); + d_delete(new_dentry); error = 0; rename_done: fat_brelse(sb, free_bh); - iput(old_inode); return error; } /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */ -int msdos_rename(struct inode *old_dir,const char *old_name,int old_len, - struct inode *new_dir,const char *new_name,int new_len) +int msdos_rename(struct inode *old_dir,struct dentry *old_dentry, + struct inode *new_dir,struct dentry *new_dentry) { struct super_block *sb = old_dir->i_sb; char old_msdos_name[MSDOS_NAME],new_msdos_name[MSDOS_NAME]; @@ -756,28 +704,30 @@ int msdos_rename(struct inode *old_dir,const char *old_name,int old_len, int is_hid,old_hid; /* if new file and old file are hidden */ if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->options.name_check, - old_name,old_len,old_msdos_name,1, + old_dentry->d_name.name, + old_dentry->d_name.len,old_msdos_name,1, MSDOS_SB(old_dir->i_sb)->options.dotsOK)) < 0) goto rename_done; if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->options.name_check, - new_name,new_len,new_msdos_name,0, + new_dentry->d_name.name, + new_dentry->d_name.len,new_msdos_name,0, MSDOS_SB(new_dir->i_sb)->options.dotsOK)) < 0) goto rename_done; - is_hid = (new_name[0]=='.') && (new_msdos_name[0]!='.'); - old_hid = (old_name[0]=='.') && (old_msdos_name[0]!='.'); + is_hid = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.'); + old_hid = (old_dentry->d_name.name[0]=='.') && (old_msdos_name[0]!='.'); if ((error = fat_scan(old_dir,old_msdos_name,&old_bh,&old_de, &old_ino,old_hid?SCAN_HID:SCAN_NOTHID)) < 0) goto rename_done; fat_lock_creation(); if (old_dir == new_dir) - error = rename_same_dir(old_dir,old_msdos_name,old_len,new_dir, - new_msdos_name,new_len,old_bh,old_de,old_ino,is_hid); - else error = rename_diff_dir(old_dir,old_msdos_name,old_len,new_dir, - new_msdos_name,new_len,old_bh,old_de,old_ino,is_hid); + error = rename_same_dir(old_dir,old_msdos_name,old_dentry, + new_dir,new_msdos_name,new_dentry, + old_bh,old_de,old_ino,is_hid); + else error = rename_diff_dir(old_dir,old_msdos_name,old_dentry, + new_dir,new_msdos_name,new_dentry, + old_bh,old_de,old_ino,is_hid); fat_unlock_creation(); fat_brelse(sb, old_bh); rename_done: - iput(old_dir); - iput(new_dir); return error; } diff --git a/fs/namei.c b/fs/namei.c index 2ec173f0d..8870e3a99 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -338,26 +338,39 @@ done_error: return result; } -/* - * This should check "link_count", but doesn't do that yet.. - */ static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry) { struct inode * inode = dentry->d_inode; if (inode && inode->i_op && inode->i_op->follow_link) { - struct dentry *result; + if (current->link_count < 5) { + struct dentry * result; - /* This eats the base */ - result = inode->i_op->follow_link(inode, base); - base = dentry; - dentry = result; + current->link_count++; + /* This eats the base */ + result = inode->i_op->follow_link(inode, base); + current->link_count--; + dput(dentry); + return result; + } + dput(dentry); + dentry = ERR_PTR(-ELOOP); } dput(base); return dentry; } /* + * Allow a filesystem to translate the character set of + * a file name. This allows for filesystems that are not + * case-sensitive, for example. + * + * This is only a dummy define right now, but eventually + * it might become something like "(parent)->d_charmap[c]" + */ +#define name_translate_char(parent, c) (c) + +/* * Name resolution. * * This is the basic name resolution function, turning a pathname @@ -397,6 +410,7 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, int follo c = *name; do { len++; name++; + c = name_translate_char(base, c); hash = partial_name_hash(c, hash); c = *name; } while (c && (c != '/')); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 9241c679e..449fc0d4e 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -424,7 +424,6 @@ nfs_writepage(struct inode *inode, struct page *page) /* * Update and possibly write a cached page of an NFS file. - * The page is already locked when we get here. * * XXX: Keep an eye on generic_file_read to make sure it doesn't do bad * things with a page scheduled for an RPC call (e.g. invalidate it). @@ -441,6 +440,7 @@ nfs_updatepage(struct inode *inode, struct page *page, const char *buffer, inode->i_dev, inode->i_ino, count, page->offset+offset, sync); + set_bit(PG_locked, &page->flags); page_addr = (u8 *) page_address(page); /* If wsize is smaller than page size, update and write diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index c83150b5f..221483587 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -90,9 +90,25 @@ exp_get(svc_client *clp, dev_t dev, ino_t ino) } /* + * Look up the root inode of the parent fs. + * We have to go through iget in order to allow for wait_on_inode. + */ +static inline int +nfsd_parentdev(dev_t *devp) +{ + struct super_block *sb; + + if (!(sb = get_super(*devp)) || !sb->s_root->d_covers) + return 0; + if (*devp == sb->s_root->d_covers->d_inode->i_dev) + return 0; + *devp = sb->s_root->d_covers->d_inode->i_dev; + return 1; +} + +/* * Find the parent export entry for a given fs. This function is used * only by the export syscall to keep the export tree consistent. - * nfsd_parentdev(dev) returns the device on which dev is mounted. */ static svc_export * exp_parent(svc_client *clp, dev_t dev) @@ -118,6 +134,7 @@ exp_export(struct nfsctl_export *nxp) svc_client *clp; svc_export *exp, *parent; svc_export **head; + struct dentry *dentry = NULL; struct inode *inode = NULL; int i, err; dev_t dev; @@ -128,7 +145,7 @@ exp_export(struct nfsctl_export *nxp) !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX)) return -EINVAL; - dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n", + dprintk("exp_export called for %s:%s (%x/%d fl %x).\n", nxp->ex_client, nxp->ex_path, nxp->ex_dev, nxp->ex_ino, nxp->ex_flags); dev = nxp->ex_dev; @@ -161,8 +178,19 @@ exp_export(struct nfsctl_export *nxp) goto finish; } - /* Look up the inode */ - if (!(inode = nfsd_iget(nxp->ex_dev, nxp->ex_ino))) { + /* Look up the dentry */ + dentry = lookup_dentry(nxp->ex_path, NULL, 0); + if (IS_ERR(dentry)) { + err = -EINVAL; + goto finish; + } + inode = dentry->d_inode; + if(!inode) { + err = -ENOENT; + goto finish; + } + if(inode->i_dev != nxp->ex_dev || inode->i_ino != nxp->ex_ino) { + /* I'm just being paranoid... */ err = -EINVAL; goto finish; } @@ -175,9 +203,9 @@ exp_export(struct nfsctl_export *nxp) /* If this is a sub-export, must be root of FS */ if ((parent = exp_parent(clp, dev)) != NULL) { - struct super_block *sb; + struct super_block *sb = inode->i_sb; - if ((sb = inode->i_sb) && (inode != sb->s_mounted)) { + if (sb && (inode != sb->s_root->d_inode)) { err = -EINVAL; goto finish; } @@ -192,7 +220,7 @@ exp_export(struct nfsctl_export *nxp) strcpy(exp->ex_path, nxp->ex_path); exp->ex_client = clp; exp->ex_parent = parent; - exp->ex_inode = inode; + exp->ex_dentry = dentry; exp->ex_flags = nxp->ex_flags; exp->ex_dev = dev; exp->ex_ino = ino; @@ -219,9 +247,10 @@ exp_export(struct nfsctl_export *nxp) err = 0; finish: - /* Release inode */ - if (err < 0 && inode) - iput(inode); + /* Release dentry */ + if (err < 0 && dentry) + dput(dentry); + /* Unlock hashtable */ exp_unlock(); return err; @@ -236,6 +265,7 @@ exp_do_unexport(svc_export *unexp) { svc_export *exp; svc_client *clp; + struct dentry *dentry; struct inode *inode; int i; @@ -247,11 +277,12 @@ exp_do_unexport(svc_export *unexp) exp->ex_parent = unexp->ex_parent; } - inode = unexp->ex_inode; + dentry = unexp->ex_dentry; + inode = dentry->d_inode; if (unexp->ex_dev != inode->i_dev || unexp->ex_ino != inode->i_ino) - printk(KERN_WARNING "nfsd: bad inode in unexport!\n"); + printk(KERN_WARNING "nfsd: bad dentry in unexport!\n"); else - iput(unexp->ex_inode); + dput(dentry); kfree(unexp); } @@ -326,13 +357,28 @@ exp_rootfh(struct svc_client *clp, dev_t dev, ino_t ino, struct knfs_fh *f) { struct svc_export *exp = NULL; struct svc_fh fh; + struct dentry *dentry; + struct inode *inode; - dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino); + dprintk("nfsd: exp_rootfh(%s:%x/%d)\n", clp->cl_ident, dev, ino); if (!(exp = exp_get(clp, dev, ino))) return -EPERM; - exp->ex_inode->i_count++; - fh_compose(&fh, exp, exp->ex_inode); + + dentry = exp->ex_dentry; + inode = dentry->d_inode; + if(!inode) { + printk("exp_rootfh: Aieee, NULL d_inode\n"); + return -EPERM; + } + if(inode->i_dev != dev || inode->i_ino != ino) { + printk("exp_rootfh: Aieee, ino/dev mismatch\n"); + printk("exp_rootfh: arg[dev(%x):ino(%d)] inode[dev(%x):ino(%ld)]\n", + dev, ino, inode->i_dev, inode->i_ino); + } + + dget(dentry); + fh_compose(&fh, exp, dentry); memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh)); fh_put(&fh); @@ -613,25 +659,6 @@ exp_verify_string(char *cp, int max) return 0; } -#if 0 -/* - * Get the inode associated with a pathname. Used by exp_export. - */ -struct inode * -exp_lnamei(char *pathname, int *errp) -{ - struct inode *inode; - unsigned long oldfs; - - oldfs = get_fs(); - set_fs(KERNEL_DS); - *errp = lnamei(pathname, &inode); - set_fs(oldfs); - - return inode; -} -#endif - /* * Initialize the exports module. */ @@ -648,7 +675,6 @@ nfsd_export_init(void) clients = NULL; initialized = 1; - return; } /* @@ -672,6 +698,4 @@ nfsd_export_shutdown(void) } exp_unlock(); dprintk("nfsd: export shutdown complete.\n"); - - return; } diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c index 3732ebcce..6e161d62b 100644 --- a/fs/nfsd/lockd.c +++ b/fs/nfsd/lockd.c @@ -24,7 +24,7 @@ nlm_fopen(struct svc_rqst *rqstp, struct knfs_fh *f, struct file *filp) fh.fh_handle = *f; fh.fh_export = NULL; - fh.fh_inode = NULL; + fh.fh_dverified = 0; nfserr = nfsd_open(rqstp, &fh, S_IFREG, 0, filp); fh_put(&fh); diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index fb1c0878b..c748e118d 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -58,7 +58,7 @@ nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, SVCFH_INO(&argp->fh)); resp->fh = argp->fh; - nfserr = fh_lookup(rqstp, &resp->fh, 0, MAY_NOP); + nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP); RETURN(nfserr); } @@ -227,7 +227,7 @@ nfsd3_proc_create(struct svc_rqst *rqstp, struct nfsd3_createargs *argp, attr = &argp->attrs; /* Get the directory inode */ - nfserr = fh_lookup(rqstp, dirfhp, S_IFDIR, MAY_CREATE); + nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_CREATE); if (nfserr) RETURN(nfserr); @@ -241,8 +241,7 @@ nfsd3_proc_create(struct svc_rqst *rqstp, struct nfsd3_createargs *argp, /* Now create the file and set attributes */ nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len, - attr, S_IFREG, 0, newfhp, - argp->createmode); + attr, S_IFREG, 0, newfhp); RETURN(nfserr); } diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 88b69cb40..1db374bc6 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -133,9 +133,10 @@ nfsctl_getfh(struct nfsctl_fhparm *data, struct knfs_fh *res) #endif int -asmlinkage handle_sys_nfsservctl(int cmd, struct nfsctl_arg *argp, - union nfsctl_res *resp) +asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp) { + struct nfsctl_arg * argp = opaque_argp; + union nfsctl_res * resp = opaque_resp; struct nfsctl_arg * arg = NULL; union nfsctl_res * res = NULL; int err; diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 526a4455b..3ea0ee988 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -18,44 +18,23 @@ #define NFSDDBG_FACILITY NFSDDBG_FH /* - * Get the inode version number - */ -static inline int -nfsd_iversion(struct inode *inode) -{ - if (inode->i_sb->s_magic == EXT2_SUPER_MAGIC) - return inode->u.ext2_i.i_version; - return 0; -} - -/* - * Get the inode given a file handle. + * Perform sanity checks on the dentry in a client's file handle. */ u32 -fh_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) +fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) { struct svc_export *exp; + struct dentry *dentry; struct inode *inode; struct knfs_fh *fh = &fhp->fh_handle; - /* Already checked */ - if (fhp->fh_inode) + if(fhp->fh_dverified) return 0; - dprintk("nfsd: fh_lookup(exp %x/%ld fh %x/%ld)\n", - fh->fh_xdev, fh->fh_xino, fh->fh_dev, fh->fh_ino); + dprintk("nfsd: fh_lookup(exp %x/%d fh %p)\n", + fh->fh_xdev, fh->fh_xino, fh->fh_dentry); - /* Make sure that clients don't cheat */ - if (fh->fh_dev != fh->fh_xdev) { - printk(KERN_NOTICE "nfsd: fh with bad dev fields " - "(%x != %x) from %08lx:%d\n", - fh->fh_dev, fh->fh_xdev, - ntohl(rqstp->rq_addr.sin_addr.s_addr), - ntohs(rqstp->rq_addr.sin_port)); - return nfserr_perm; - } - - /* Look up the export entry */ + /* Look up the export entry. */ exp = exp_get(rqstp->rq_client, fh->fh_xdev, fh->fh_xino); if (!exp) { /* nfsdstats.fhstale++; */ @@ -71,22 +50,26 @@ fh_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) return nfserr_perm; } - /* Set user creds if we haven't done so already */ + /* Set user creds if we haven't done so already. */ nfsd_setuser(rqstp, exp); - /* Get the inode */ - if (!(inode = nfsd_iget(fh->fh_dev, fh->fh_ino)) - || !inode->i_nlink || fh->fh_version != nfsd_iversion(inode)) { - if (inode) - iput(inode); + dentry = fh->fh_dentry; + + if(!d_validate(dentry, fh->fh_dparent, fh->fh_dhash, fh->fh_dlen) || + !(inode = dentry->d_inode) || + !inode->i_nlink) { + /* Currently we cannot tell the difference between + * a bogus pointer and a true unlink between fh + * uses. But who cares about accurate error reporting + * to buggy/malicious clients... -DaveM + */ + /* nfsdstats.fhstale++; */ - return nfserr_stale; /* unlinked in the meanwhile */ + return nfserr_stale; } - /* This is basically what wait_on_inode does */ - while (inode->i_lock) - sleep_on(&inode->i_wait); - fhp->fh_inode = inode; + dget(dentry); + fhp->fh_dverified = 1; fhp->fh_export = exp; /* Type check. The correct error return for type mismatches @@ -100,25 +83,28 @@ fh_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) if (type < 0 && (inode->i_mode & S_IFMT) == -type) return (type == -S_IFDIR)? nfserr_notdir : nfserr_isdir; - /* Finally, check access permissions */ - return nfsd_permission(fhp->fh_export, inode, access); + /* Finally, check access permissions. */ + return nfsd_permission(fhp->fh_export, dentry, access); } /* * Compose file handle for NFS reply. */ void -fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct inode *inode) +fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry) { - dprintk("nfsd: fh_compose(exp %x/%ld fh %x/%ld)\n", - exp->ex_dev, exp->ex_ino, inode->i_dev, inode->i_ino); + dprintk("nfsd: fh_compose(exp %x/%d dentry %p)\n", + exp->ex_dev, exp->ex_ino, dentry); fh_init(fhp); /* initialize empty fh */ - fhp->fh_inode = inode; - fhp->fh_export = exp; - fhp->fh_handle.fh_dev = inode->i_dev; - fhp->fh_handle.fh_ino = inode->i_ino; + fhp->fh_handle.fh_dentry = dentry; + fhp->fh_handle.fh_dparent = dentry->d_parent; + fhp->fh_handle.fh_dhash = dentry->d_name.hash; + fhp->fh_handle.fh_dlen = dentry->d_name.len; fhp->fh_handle.fh_xdev = exp->ex_dev; fhp->fh_handle.fh_xino = exp->ex_ino; - fhp->fh_handle.fh_version = nfsd_iversion(inode); + fhp->fh_export = exp; + + /* We stuck it there, we know it's good. */ + fhp->fh_dverified = 1; } diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 05b13deab..5b6e6cdf1 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -55,12 +55,10 @@ static int nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, struct nfsd_attrstat *resp) { - dprintk("nfsd: GETATTR %x/%ld\n", - SVCFH_DEV(&argp->fh), - SVCFH_INO(&argp->fh)); + dprintk("nfsd: GETATTR %p\n", SVCFH_DENTRY(&argp->fh)); fh_copy(&resp->fh, &argp->fh); - RETURN(fh_lookup(rqstp, &resp->fh, 0, MAY_NOP)); + RETURN(fh_verify(rqstp, &resp->fh, 0, MAY_NOP)); } /* @@ -70,9 +68,7 @@ static int nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp, struct nfsd_attrstat *resp) { - dprintk("nfsd: SETATTR %x/%ld\n", - SVCFH_DEV(&argp->fh), - SVCFH_INO(&argp->fh)); + dprintk("nfsd: SETATTR %p\n", SVCFH_DENTRY(&argp->fh)); fh_copy(&resp->fh, &argp->fh); RETURN(nfsd_setattr(rqstp, &resp->fh, &argp->attrs)); @@ -87,10 +83,7 @@ nfsd_proc_lookup(struct svc_rqst *rqstp, struct nfsd_diropargs *argp, { int nfserr; - dprintk("nfsd: LOOKUP %x/%ld %s\n", - SVCFH_DEV(&argp->fh), - SVCFH_INO(&argp->fh), - argp->name); + dprintk("nfsd: LOOKUP %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name); nfserr = nfsd_lookup(rqstp, &argp->fh, argp->name, @@ -111,9 +104,7 @@ nfsd_proc_readlink(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, u32 *path; int dummy, nfserr; - dprintk("nfsd: READLINK %x/%ld\n", - SVCFH_DEV(&argp->fh), - SVCFH_INO(&argp->fh)); + dprintk("nfsd: READLINK %p\n", SVCFH_DENTRY(&argp->fh)); /* Reserve room for status and path length */ svcbuf_reserve(&rqstp->rq_resbuf, &path, &dummy, 2); @@ -136,10 +127,9 @@ nfsd_proc_read(struct svc_rqst *rqstp, struct nfsd_readargs *argp, u32 * buffer; int nfserr, avail; - dprintk("nfsd: READ %x/%ld %d bytes at %d\n", - SVCFH_DEV(&argp->fh), - SVCFH_INO(&argp->fh), - argp->count, argp->offset); + dprintk("nfsd: READ %p %d bytes at %d\n", + SVCFH_DENTRY(&argp->fh), + argp->count, argp->offset); /* Obtain buffer pointer for payload. 19 is 1 word for * status, 17 words for fattr, and 1 word for the byte count. @@ -173,10 +163,9 @@ nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp, { int nfserr; - dprintk("nfsd: WRITE %x/%ld %d bytes at %d\n", - SVCFH_DEV(&argp->fh), - SVCFH_INO(&argp->fh), - argp->len, argp->offset); + dprintk("nfsd: WRITE %p %d bytes at %d\n", + SVCFH_DENTRY(&argp->fh), + argp->len, argp->offset); nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh), argp->offset, @@ -204,23 +193,22 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, int rdonly = 0, exists; dev_t rdev = NODEV; - dprintk("nfsd: CREATE %x/%ld %s\n", - SVCFH_DEV(&argp->fh), - SVCFH_INO(&argp->fh), - argp->name); + dprintk("nfsd: CREATE %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name); dirfhp = &argp->fh; newfhp = &resp->fh; attr = &argp->attrs; /* Get the directory inode */ - nfserr = fh_lookup(rqstp, dirfhp, S_IFDIR, MAY_EXEC); + nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_EXEC); if (nfserr) RETURN(nfserr); - dirp = dirfhp->fh_inode; + dirp = dirfhp->fh_handle.fh_dentry->d_inode; /* Check for MAY_WRITE separately. */ - nfserr = nfsd_permission(dirfhp->fh_export, dirp, MAY_WRITE); + nfserr = nfsd_permission(dirfhp->fh_export, + dirfhp->fh_handle.fh_dentry, + MAY_WRITE); if (nfserr == nfserr_rofs) { rdonly = 1; /* Non-fatal error for echo > /dev/null */ } else if (nfserr) { @@ -230,7 +218,16 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, /* First, check if the file already exists. */ exists = !nfsd_lookup(rqstp, dirfhp, argp->name, argp->len, newfhp); - inode = newfhp->fh_inode; + + if (newfhp->fh_dverified) + inode = newfhp->fh_handle.fh_dentry->d_inode; + + /* Get rid of this soon... */ + if (exists && !inode) { + printk("nfsd_proc_create: Wheee... exists but d_inode==NULL\n"); + nfserr = nfserr_rofs; + goto done; + } /* Unfudge the mode bits */ if (attr->ia_valid & ATTR_MODE) { @@ -311,10 +308,7 @@ nfsd_proc_remove(struct svc_rqst *rqstp, struct nfsd_diropargs *argp, { int nfserr; - dprintk("nfsd: REMOVE %x/%ld %s\n", - SVCFH_DEV(&argp->fh), - SVCFH_INO(&argp->fh), - argp->name); + dprintk("nfsd: REMOVE %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name); /* Unlink. -SIFDIR means file must not be a directory */ nfserr = nfsd_unlink(rqstp, &argp->fh, -S_IFDIR, argp->name, argp->len); @@ -328,13 +322,9 @@ nfsd_proc_rename(struct svc_rqst *rqstp, struct nfsd_renameargs *argp, { int nfserr; - dprintk("nfsd: RENAME %x/%ld %s -> %x/%ld %s\n", - SVCFH_DEV(&argp->ffh), - SVCFH_INO(&argp->ffh), - argp->fname, - SVCFH_DEV(&argp->tfh), - SVCFH_INO(&argp->tfh), - argp->tname); + dprintk("nfsd: RENAME %p %s -> %p %s\n", + SVCFH_DENTRY(&argp->ffh), argp->fname, + SVCFH_DENTRY(&argp->tfh), argp->tname); nfserr = nfsd_rename(rqstp, &argp->ffh, argp->fname, argp->flen, &argp->tfh, argp->tname, argp->tlen); @@ -349,12 +339,10 @@ nfsd_proc_link(struct svc_rqst *rqstp, struct nfsd_linkargs *argp, { int nfserr; - dprintk("nfsd: LINK %x/%ld -> %x/%ld %s\n", - SVCFH_DEV(&argp->ffh), - SVCFH_INO(&argp->ffh), - SVCFH_DEV(&argp->tfh), - SVCFH_INO(&argp->tfh), - argp->tname); + dprintk("nfsd: LINK %p -> %p %s\n", + SVCFH_DENTRY(&argp->ffh), + SVCFH_DENTRY(&argp->tfh), + argp->tname); nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen, &argp->ffh); @@ -370,10 +358,9 @@ nfsd_proc_symlink(struct svc_rqst *rqstp, struct nfsd_symlinkargs *argp, struct svc_fh newfh; int nfserr; - dprintk("nfsd: SYMLINK %x/%ld %s -> %s\n", - SVCFH_DEV(&argp->ffh), - SVCFH_INO(&argp->ffh), - argp->fname, argp->tname); + dprintk("nfsd: SYMLINK %p %s -> %s\n", + SVCFH_DENTRY(&argp->ffh), + argp->fname, argp->tname); /* * Create the link, look up new file and set attrs. @@ -381,7 +368,7 @@ nfsd_proc_symlink(struct svc_rqst *rqstp, struct nfsd_symlinkargs *argp, nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen, argp->tname, argp->tlen, &newfh); - if (nfserr) + if (!nfserr) nfserr = nfsd_setattr(rqstp, &newfh, &argp->attrs); fh_put(&argp->ffh); @@ -398,11 +385,11 @@ nfsd_proc_mkdir(struct svc_rqst *rqstp, struct nfsd_createargs *argp, { int nfserr; - dprintk("nfsd: MKDIR %x/%ld %s\n", - SVCFH_DEV(&argp->fh), - SVCFH_INO(&argp->fh), - argp->name); + dprintk("nfsd: MKDIR %p %s\n", + SVCFH_DENTRY(&argp->fh), + argp->name); + resp->fh.fh_dverified = 0; /* paranoia */ nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len, &argp->attrs, S_IFDIR, 0, &resp->fh); fh_put(&argp->fh); @@ -418,10 +405,7 @@ nfsd_proc_rmdir(struct svc_rqst *rqstp, struct nfsd_diropargs *argp, { int nfserr; - dprintk("nfsd: RMDIR %x/%ld %s\n", - SVCFH_DEV(&argp->fh), - SVCFH_INO(&argp->fh), - argp->name); + dprintk("nfsd: RMDIR %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name); nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len); fh_put(&argp->fh); @@ -438,10 +422,9 @@ nfsd_proc_readdir(struct svc_rqst *rqstp, struct nfsd_readdirargs *argp, u32 * buffer; int nfserr, count; - dprintk("nfsd: READDIR %x/%ld %d bytes at %d\n", - SVCFH_DEV(&argp->fh), - SVCFH_INO(&argp->fh), - argp->count, argp->cookie); + dprintk("nfsd: READDIR %p %d bytes at %d\n", + SVCFH_DENTRY(&argp->fh), + argp->count, argp->cookie); /* Reserve buffer space for status */ svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count, 1); @@ -470,9 +453,7 @@ nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, { int nfserr; - dprintk("nfsd: STATFS %x/%ld\n", - SVCFH_DEV(&argp->fh), - SVCFH_INO(&argp->fh)); + dprintk("nfsd: STATFS %p\n", SVCFH_DENTRY(&argp->fh)); nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats); fh_put(&argp->fh); @@ -488,34 +469,34 @@ nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, struct nfsd_void { int dummy; }; #define PROC(name, argt, rest, relt, cache) \ - { (svc_procfunc) nfsd_proc_##name, \ - (kxdrproc_t) nfssvc_decode_##argt, \ - (kxdrproc_t) nfssvc_encode_##rest, \ - (kxdrproc_t) nfssvc_release_##relt, \ - sizeof(struct nfsd_##argt), \ - sizeof(struct nfsd_##rest), \ - 0, \ - cache \ + { (svc_procfunc) nfsd_proc_##name, \ + (kxdrproc_t) nfssvc_decode_##argt, \ + (kxdrproc_t) nfssvc_encode_##rest, \ + (kxdrproc_t) nfssvc_release_##relt, \ + sizeof(struct nfsd_##argt), \ + sizeof(struct nfsd_##rest), \ + 0, \ + cache \ } struct svc_procedure nfsd_procedures2[18] = { - PROC(null, void, void, none, RC_NOCACHE), - PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE), - PROC(setattr, sattrargs, attrstat, fhandle, RC_REPLBUFF), - PROC(none, void, void, none, RC_NOCACHE), - PROC(lookup, diropargs, diropres, fhandle, RC_NOCACHE), - PROC(readlink, fhandle, readlinkres, none, RC_NOCACHE), - PROC(read, readargs, readres, fhandle, RC_NOCACHE), - PROC(none, void, void, none, RC_NOCACHE), - PROC(write, writeargs, attrstat, fhandle, RC_REPLBUFF), - PROC(create, createargs, diropres, fhandle, RC_REPLBUFF), - PROC(remove, diropargs, void, none, RC_REPLSTAT), - PROC(rename, renameargs, void, none, RC_REPLSTAT), - PROC(link, linkargs, void, none, RC_REPLSTAT), - PROC(symlink, symlinkargs, void, none, RC_REPLSTAT), - PROC(mkdir, createargs, diropres, fhandle, RC_REPLBUFF), - PROC(rmdir, diropargs, void, none, RC_REPLSTAT), - PROC(readdir, readdirargs, readdirres, none, RC_REPLSTAT), - PROC(statfs, fhandle, statfsres, none, RC_NOCACHE), + PROC(null, void, void, none, RC_NOCACHE), + PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE), + PROC(setattr, sattrargs, attrstat, fhandle, RC_REPLBUFF), + PROC(none, void, void, none, RC_NOCACHE), + PROC(lookup, diropargs, diropres, fhandle, RC_NOCACHE), + PROC(readlink, fhandle, readlinkres, none, RC_NOCACHE), + PROC(read, readargs, readres, fhandle, RC_NOCACHE), + PROC(none, void, void, none, RC_NOCACHE), + PROC(write, writeargs, attrstat, fhandle, RC_REPLBUFF), + PROC(create, createargs, diropres, fhandle, RC_REPLBUFF), + PROC(remove, diropargs, void, none, RC_REPLSTAT), + PROC(rename, renameargs, void, none, RC_REPLSTAT), + PROC(link, linkargs, void, none, RC_REPLSTAT), + PROC(symlink, symlinkargs, void, none, RC_REPLSTAT), + PROC(mkdir, createargs, diropres, fhandle, RC_REPLBUFF), + PROC(rmdir, diropargs, void, none, RC_REPLSTAT), + PROC(readdir, readdirargs, readdirres, none, RC_REPLSTAT), + PROC(statfs, fhandle, statfsres, none, RC_NOCACHE), }; diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c index 34b9ff3f2..cec3ba7f3 100644 --- a/fs/nfsd/nfsxdr.c +++ b/fs/nfsd/nfsxdr.c @@ -363,7 +363,7 @@ int nfssvc_encode_attrstat(struct svc_rqst *rqstp, u32 *p, struct nfsd_attrstat *resp) { - if (!(p = encode_fattr(rqstp, p, resp->fh.fh_inode))) + if (!(p = encode_fattr(rqstp, p, resp->fh.fh_handle.fh_dentry->d_inode))) return 0; return xdr_ressize_check(rqstp, p); } @@ -373,7 +373,7 @@ nfssvc_encode_diropres(struct svc_rqst *rqstp, u32 *p, struct nfsd_diropres *resp) { if (!(p = encode_fh(p, &resp->fh)) - || !(p = encode_fattr(rqstp, p, resp->fh.fh_inode))) + || !(p = encode_fattr(rqstp, p, resp->fh.fh_handle.fh_dentry->d_inode))) return 0; return xdr_ressize_check(rqstp, p); } @@ -391,7 +391,7 @@ int nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p, struct nfsd_readres *resp) { - if (!(p = encode_fattr(rqstp, p, resp->fh.fh_inode))) + if (!(p = encode_fattr(rqstp, p, resp->fh.fh_handle.fh_dentry->d_inode))) return 0; *p++ = htonl(resp->count); p += XDR_QUADLEN(resp->count); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index b6fdb460d..4fa6c08fb 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -5,12 +5,11 @@ * other parts of the kernel because they weren't in ksyms.c, others * are partial duplicates with added or changed functionality. * - * Note that several functions lock the inode upon which they want - * to act, most notably those that create directory entries. The - * unlock operation can take place either by calling fh_unlock within - * the function directly, or at a later time in fh_put(). So if you - * notice code paths that apparently fail to unlock the inode, don't - * worry--they have been taken care of. + * Note that several functions dget() the dentry upon which they want + * to act, most notably those that create directory entries. Response + * dentry's are dput()'d if necessary in the release callback. + * So if you notice code paths that apparently fail to dput() the + * dentry, don't worry--they have been taken care of. * * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de> */ @@ -59,10 +58,9 @@ * add a hash table here. */ struct raparms { - struct raparms * p_next; + struct raparms *p_next; unsigned int p_count; - dev_t p_dev; - ino_t p_ino; + struct dentry *p_dentry; unsigned long p_reada, p_ramax, p_raend, @@ -85,12 +83,14 @@ fs_off_limits(struct super_block *sb) } /* - * Check whether directory is a mount point + * Check whether directory is a mount point, but it is alright if + * this is precisely the local mount point being exported. */ static inline int -nfsd_iscovered(struct inode *inode) +nfsd_iscovered(struct dentry *dentry, struct svc_export *exp) { - return inode->i_mount != NULL; + return (dentry != dentry->d_covers && + dentry != exp->ex_dentry); } /* @@ -101,72 +101,40 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, int len, struct svc_fh *resfh) { struct svc_export *exp; - struct super_block *sb; - struct inode *dirp, *inode; - int perm, err, dotdot = 0; + struct dentry *dparent; + int err; - dprintk("nfsd: nfsd_lookup(fh %x/%ld, %s)\n", - SVCFH_DEV(fhp), SVCFH_INO(fhp), name); + dprintk("nfsd: nfsd_lookup(fh %p, %s)\n", SVCFH_DENTRY(fhp), name); - /* Obtain inode and export */ - if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_NOP)) != 0) + /* Obtain dentry and export. */ + if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_NOP)) != 0) return err; - dirp = fhp->fh_inode; - exp = fhp->fh_export; - /* check permissions before traversing mount-points */ - perm = nfsd_permission(exp, dirp, MAY_EXEC); - - dotdot = (len == 2 && name[0] == '.' && name[1] == '.'); - if (dotdot) { - if (dirp == current->fs->root) { - dirp->i_count++; - *resfh = *fhp; - return 0; - } - - if (dirp->i_dev == exp->ex_dev && dirp->i_ino == exp->ex_ino) { - dirp->i_count++; - *resfh = *fhp; - return 0; - } - } else if (len == 1 && name[0] == '.') { - len = 0; - } else if (fs_off_limits(dirp->i_sb)) { - /* No lookups on NFS mounts and procfs */ - return nfserr_noent; - } else if (nfsd_iscovered(dirp)) { - /* broken NFS client */ - return nfserr_acces; - } - if (!dirp->i_op || !dirp->i_op->lookup) - return nfserr_notdir; - if (perm != 0) - return perm; - if (!len) { - dirp->i_count++; - *resfh = *fhp; - return 0; - } + dparent = fhp->fh_handle.fh_dentry; + exp = fhp->fh_export; - dirp->i_count++; /* lookup eats the dirp inode */ - err = dirp->i_op->lookup(dirp, name, len, &inode); + /* Fast path... */ + err = nfsd_permission(exp, dparent, MAY_EXEC); + if ((err == 0) && + !fs_off_limits(dparent->d_inode->i_sb) && + !nfsd_iscovered(dparent, exp)) { + struct dentry *dchild; - if (err) - return nfserrno(-err); + dchild = lookup_dentry(name, dget(dparent), 1); + err = PTR_ERR(dchild); + if (IS_ERR(dchild)) + return nfserrno(-err); - /* Note that lookup() has already done a call to iget() so that - * the inode returned never refers to an inode covered by a mount. - * When this has happened, return the covered inode. - */ - if (!dotdot && (sb = inode->i_sb) && (inode == sb->s_mounted)) { - iput(inode); - inode = sb->s_covered; - inode->i_count++; + fh_compose(resfh, exp, dchild); + return (dchild->d_inode ? 0 : nfserr_noent); } - fh_compose(resfh, exp, inode); - return 0; + /* Slow path... */ + if (fs_off_limits(dparent->d_inode->i_sb)) + return nfserr_noent; + if (nfsd_iscovered(dparent, exp)) + return nfserr_acces; + return err; } /* @@ -175,6 +143,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, int nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap) { + struct dentry *dentry; struct inode *inode; int accmode = MAY_SATTR; int ftype = 0; @@ -187,16 +156,16 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap) ftype = S_IFREG; /* Get inode */ - if ((err = fh_lookup(rqstp, fhp, ftype, accmode)) != 0) + if ((err = fh_verify(rqstp, fhp, ftype, accmode)) != 0) return err; - fh_lock(fhp); /* lock inode */ - inode = fhp->fh_inode; + dentry = fhp->fh_handle.fh_dentry; + inode = dentry->d_inode; /* The size case is special... */ if ((iap->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) { if (iap->ia_size < inode->i_size) { - err = nfsd_permission(fhp->fh_export, inode, MAY_TRUNC); + err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC); if (err != 0) return err; } @@ -205,7 +174,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap) inode->i_size = iap->ia_size; if (inode->i_op && inode->i_op->truncate) inode->i_op->truncate(inode); - inode->i_dirt = 1; + mark_inode_dirty(inode); put_write_access(inode); iap->ia_valid &= ATTR_SIZE; iap->ia_valid |= ATTR_MTIME; @@ -234,11 +203,11 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap) if (iap->ia_valid) { iap->ia_valid |= ATTR_CTIME; iap->ia_ctime = CURRENT_TIME; - err = nfsd_notify_change(inode, iap); + err = notify_change(inode, iap); if (err) return nfserrno(-err); if (EX_ISSYNC(fhp->fh_export)) - nfsd_write_inode(inode); + write_inode_now(inode); } return 0; @@ -252,13 +221,16 @@ int nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int wflag, struct file *filp) { + struct dentry *dentry; struct inode *inode; int access, err; access = wflag? MAY_WRITE : MAY_READ; - if ((err = fh_lookup(rqstp, fhp, type, access)) != 0) + if ((err = fh_verify(rqstp, fhp, type, access)) != 0) return err; - inode = fhp->fh_inode; + + dentry = fhp->fh_handle.fh_dentry; + inode = dentry->d_inode; /* Disallow access to files with the append-only bit set or * with mandatory locking enabled */ @@ -275,7 +247,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, filp->f_count = 1; filp->f_flags = wflag? O_WRONLY : O_RDONLY; filp->f_mode = wflag? FMODE_WRITE : FMODE_READ; - filp->f_inode = inode; + filp->f_dentry = dentry; if (filp->f_op->open) { err = filp->f_op->open(inode, filp); @@ -291,7 +263,6 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, } } - inode->i_count++; return 0; } @@ -301,16 +272,17 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, void nfsd_close(struct file *filp) { - struct inode *inode; + struct dentry *dentry = filp->f_dentry; + struct inode *inode = dentry->d_inode; - inode = filp->f_inode; if (!inode->i_count) printk(KERN_WARNING "nfsd: inode count == 0!\n"); + if (!dentry->d_count) + printk(KERN_WARNING "nfsd: wheee, dentry count == 0!\n"); if (filp->f_op && filp->f_op->release) filp->f_op->release(inode, filp); if (filp->f_mode & FMODE_WRITE) put_write_access(inode); - iput(inode); } /* @@ -326,12 +298,12 @@ nfsd_sync(struct inode *inode, struct file *filp) * Obtain the readahead parameters for the given file */ static inline struct raparms * -nfsd_get_raparms(dev_t dev, ino_t ino) +nfsd_get_raparms(struct dentry *dentry) { struct raparms *ra, **rap, **frap = NULL; for (rap = &raparm_cache; (ra = *rap); rap = &ra->p_next) { - if (ra->p_dev != dev || ra->p_ino != ino) { + if (ra->p_dentry != dentry) { if (ra->p_count == 0) frap = rap; } else @@ -342,6 +314,7 @@ nfsd_get_raparms(dev_t dev, ino_t ino) rap = frap; ra = *frap; memset(ra, 0, sizeof(*ra)); + ra->p_dentry = dentry; found: if (rap != &raparm_cache) { *rap = ra->p_next; @@ -361,6 +334,7 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, char *buf, unsigned long *count) { struct raparms *ra; + struct dentry *dentry; struct inode *inode; struct file file; unsigned long oldfs; @@ -368,14 +342,15 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, char *buf, if ((err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_READ, &file)) != 0) return err; - inode = file.f_inode; + dentry = file.f_dentry; + inode = dentry->d_inode; if (!file.f_op->read) { nfsd_close(&file); return nfserr_perm; } /* Get readahead parameters */ - if ((ra = nfsd_get_raparms(inode->i_dev, inode->i_ino)) != NULL) { + if ((ra = nfsd_get_raparms(dentry)) != NULL) { file.f_reada = ra->p_reada; file.f_ramax = ra->p_ramax; file.f_raend = ra->p_raend; @@ -385,7 +360,7 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, char *buf, file.f_pos = offset; oldfs = get_fs(); set_fs(KERNEL_DS); - err = file.f_op->read(file.f_inode, &file, buf, *count); + err = file.f_op->read(inode, &file, buf, *count); set_fs(oldfs); /* Write back readahead params */ @@ -419,6 +394,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, { struct svc_export *exp; struct file file; + struct dentry *dentry; struct inode *inode; unsigned long oldfs; int err; @@ -432,7 +408,8 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, return nfserr_perm; } - inode = fhp->fh_inode; + dentry = file.f_dentry; + inode = dentry->d_inode; exp = fhp->fh_export; /* @@ -459,7 +436,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, ia.ia_valid = ATTR_MODE; ia.ia_mode = inode->i_mode & ~(S_ISUID | S_ISGID); - nfsd_notify_change(inode, &ia); + notify_change(inode, &ia); } fh_unlock(fhp); /* unlock inode */ @@ -494,10 +471,10 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, #endif } - if (inode->i_dirt) { + if (test_bit(I_DIRTY, &inode->i_state)) { dprintk("nfsd: write sync %d\n", current->pid); nfsd_sync(inode, &file); - nfsd_write_inode(inode); + write_inode_now(inode); } wake_up(&inode->i_wait); last_ino = inode->i_ino; @@ -519,110 +496,120 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, char *fname, int flen, struct iattr *iap, int type, dev_t rdev, struct svc_fh *resfhp) { - struct inode *dirp, *inode = NULL; + struct dentry *dentry, *dchild; + struct inode *dirp; int err; if (!flen) return nfserr_perm; - if (!(iap->ia_valid & ATTR_MODE)) iap->ia_mode = 0; - - if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0) + if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0) return err; - fh_lock(fhp); /* lock directory */ - dirp = fhp->fh_inode; - dirp->i_count++; /* dirop eats the inode */ + dentry = fhp->fh_handle.fh_dentry; + dirp = dentry->d_inode; + + /* Get all the sanity checks out of the way before we lock the parent. */ + if(!dirp->i_op || !dirp->i_op->lookup) { + return nfserrno(-ENOTDIR); + } else if(type == S_IFREG) { + if(!dirp->i_op->create) + return nfserr_perm; + } else if(type == S_IFDIR) { + if(!dirp->i_op->mkdir) + return nfserr_perm; + } else if((type == S_IFCHR) || (type == S_IFBLK) || (type == S_IFIFO)) { + if(!dirp->i_op->mknod) + return nfserr_perm; + } else { + return nfserr_perm; + } + if(!resfhp->fh_dverified) { + dchild = lookup_dentry(fname, dget(dentry), 0); + err = PTR_ERR(dchild); + if(IS_ERR(dchild)) + return nfserrno(-err); + } else + dchild = resfhp->fh_handle.fh_dentry; + + /* Looks good, lock the directory. */ + fh_lock(fhp); switch (type) { case S_IFREG: - if (!dirp->i_op || !dirp->i_op->create) - return nfserr_perm; - err = dirp->i_op->create(dirp, fname, flen, - iap->ia_mode, &inode); + err = dirp->i_op->create(dirp, dchild, iap->ia_mode); break; case S_IFDIR: - if (!dirp->i_op || !dirp->i_op->mkdir) - return nfserr_perm; - err = dirp->i_op->mkdir(dirp, fname, flen, iap->ia_mode); + err = dirp->i_op->mkdir(dirp, dchild, iap->ia_mode); break; case S_IFCHR: case S_IFBLK: case S_IFIFO: - if (!dirp->i_op || !dirp->i_op->mknod) - return nfserr_perm; - err = dirp->i_op->mknod(dirp, fname, flen, iap->ia_mode, rdev); + err = dirp->i_op->mknod(dirp, dchild, iap->ia_mode, rdev); break; - default: - iput(dirp); - err = -EACCES; } - fh_unlock(fhp); if (err < 0) return nfserrno(-err); - - /* - * If the VFS call doesn't return the inode, look it up now. - */ - if (inode == NULL) { - dirp->i_count++; - err = dirp->i_op->lookup(dirp, fname, flen, &inode); - if (err < 0) - return -nfserrno(err); /* Huh?! */ - } - if (EX_ISSYNC(fhp->fh_export)) - nfsd_write_inode(dirp); + write_inode_now(dirp); - /* Assemble the file handle for the newly created file */ - fh_compose(resfhp, fhp->fh_export, inode); + /* If needed, assemble the file handle for the newly created file. */ + if(!resfhp->fh_dverified) + fh_compose(resfhp, fhp->fh_export, dchild); /* Set file attributes. Mode has already been set and * setting uid/gid works only for root. Irix appears to * send along the gid when it tries to implement setgid * directories via NFS. */ - if ((iap->ia_valid &= (ATTR_UID|ATTR_GID|ATTR_MODE)) != 0) { - if ((err = nfsd_setattr(rqstp, resfhp, iap)) != 0) { - fh_put(resfhp); - return err; - } - } + err = 0; + if ((iap->ia_valid &= (ATTR_UID|ATTR_GID|ATTR_MODE)) != 0) + err = nfsd_setattr(rqstp, resfhp, iap); - return 0; + return err; } /* * Truncate a file. * The calling routines must make sure to update the ctime * field and call notify_change. + * + * XXX Nobody calls this thing? -DaveM */ int nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size) { + struct dentry *dentry; struct inode *inode; + struct iattr newattrs; int err; - if ((err = fh_lookup(rqstp, fhp, S_IFREG, MAY_WRITE|MAY_TRUNC)) != 0) + if ((err = fh_verify(rqstp, fhp, S_IFREG, MAY_WRITE|MAY_TRUNC)) != 0) return err; - fh_lock(fhp); /* lock inode if not yet locked */ - inode = fhp->fh_inode; + dentry = fhp->fh_handle.fh_dentry; + inode = dentry->d_inode; if ((err = get_write_access(inode)) != 0) - return nfserrno(-err); - inode->i_size = size; - if (inode->i_op && inode->i_op->truncate) - inode->i_op->truncate(inode); - inode->i_dirt = 1; + goto out; + + /* Things look sane, lock and do it. */ + fh_lock(fhp); + newattrs.ia_size = size; + newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; + err = notify_change(inode, &newattrs); + if (!err) { + vmtruncate(inode, size); + if (inode->i_op && inode->i_op->truncate) + inode->i_op->truncate(inode); + } put_write_access(inode); - fh_unlock(fhp); - - return 0; +out: + return (err ? nfserrno(-err) : 0); } /* @@ -632,18 +619,21 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size) int nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp) { + struct dentry *dentry; struct inode *inode; unsigned long oldfs; int err; - if ((err = fh_lookup(rqstp, fhp, S_IFLNK, MAY_READ)) != 0) + if ((err = fh_verify(rqstp, fhp, S_IFLNK, MAY_READ)) != 0) return err; - inode = fhp->fh_inode; + + dentry = fhp->fh_handle.fh_dentry; + inode = dentry->d_inode; if (!inode->i_op || !inode->i_op->readlink) return nfserr_io; - inode->i_count++; + UPDATE_ATIME(inode); oldfs = get_fs(); set_fs(KERNEL_DS); err = inode->i_op->readlink(inode, buf, *lenp); set_fs(oldfs); @@ -664,42 +654,45 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *path, int plen, struct svc_fh *resfhp) { - struct inode *dirp, *inode; + struct dentry *dentry, *dnew; + struct inode *dirp; int err; if (!flen || !plen) return nfserr_noent; - if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0) + if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0) return err; - dirp = fhp->fh_inode; - if (nfsd_iscovered(dirp)) - return nfserr_perm; - if (!dirp->i_op || !dirp->i_op->symlink) - return nfserr_perm; + dentry = fhp->fh_handle.fh_dentry; + dirp = dentry->d_inode; - fh_lock(fhp); /* lock inode */ - dirp->i_count++; - err = dirp->i_op->symlink(dirp, fname, flen, path); - fh_unlock(fhp); /* unlock inode */ + if (nfsd_iscovered(dentry, fhp->fh_export) || + !dirp->i_op || + !dirp->i_op->symlink) + return nfserr_perm; - if (err) - return nfserrno(-err); + dnew = lookup_dentry(fname, dget(dentry), 0); + err = PTR_ERR(dnew); + if (IS_ERR(dnew)) + goto out; - if (EX_ISSYNC(fhp->fh_export)) - nfsd_write_inode(dirp); + err = -EEXIST; + if(dnew->d_inode) + goto compose_and_out; - /* - * Okay, now look up the inode of the new symlink. - */ - dirp->i_count++; /* lookup eats the dirp inode */ - err = dirp->i_op->lookup(dirp, fname, flen, &inode); - if (err) - return nfserrno(-err); + fh_lock(fhp); + err = dirp->i_op->symlink(dirp, dnew, path); + fh_unlock(fhp); - fh_compose(resfhp, fhp->fh_export, inode); - return 0; + if (!err) { + if (EX_ISSYNC(fhp->fh_export)) + write_inode_now(dirp); + } +compose_and_out: + fh_compose(resfhp, fhp->fh_export, dnew); +out: + return (err ? nfserrno(-err) : 0); } /* @@ -709,37 +702,69 @@ int nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int len, struct svc_fh *tfhp) { + struct dentry *ddir, *dnew; struct inode *dirp, *dest; int err; - if ((err = fh_lookup(rqstp, ffhp, S_IFDIR, MAY_CREATE) != 0) || - (err = fh_lookup(rqstp, tfhp, S_IFREG, MAY_NOP)) != 0) + if ((err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_CREATE) != 0) || + (err = fh_verify(rqstp, tfhp, S_IFREG, MAY_NOP)) != 0) return err; - dirp = ffhp->fh_inode; - dest = tfhp->fh_inode; + ddir = ffhp->fh_handle.fh_dentry; + dirp = ddir->d_inode; + + dnew = lookup_dentry(fname, dget(ddir), 1); + err = PTR_ERR(dnew); + if (IS_ERR(dnew)) + return nfserrno(-err); + + err = -EEXIST; + if (dnew->d_inode) + goto dput_and_out; + dest = tfhp->fh_handle.fh_dentry->d_inode; + + err = -EPERM; if (!len) - return nfserr_perm; - if (nfsd_iscovered(dirp)) - return nfserr_acces; + goto dput_and_out; + + err = -EACCES; + if (nfsd_iscovered(ddir, ffhp->fh_export)) + goto dput_and_out; if (dirp->i_dev != dest->i_dev) - return nfserr_acces; /* FIXME: nxdev for NFSv3 */ + goto dput_and_out; /* FIXME: nxdev for NFSv3 */ + + err = -EPERM; if (IS_IMMUTABLE(dest) /* || IS_APPEND(dest) */ ) - return nfserr_perm; + goto dput_and_out; if (!dirp->i_op || !dirp->i_op->link) - return nfserr_perm; + goto dput_and_out; - fh_lock(ffhp); /* lock directory inode */ - dirp->i_count++; - err = dirp->i_op->link(dest, dirp, fname, len); - fh_unlock(ffhp); /* unlock inode */ + fh_lock(ffhp); + err = dirp->i_op->link(dest, dirp, dnew); + fh_unlock(ffhp); if (!err && EX_ISSYNC(ffhp->fh_export)) { - nfsd_write_inode(dirp); - nfsd_write_inode(dest); + write_inode_now(dirp); + write_inode_now(dest); } +dput_and_out: + dput(dnew); + return (err ? nfserrno(-err) : 0); +} - return err? nfserrno(-err) : 0; +/* More "hidden treasure" from the generic VFS. -DaveM */ +static inline void nfsd_double_down(struct semaphore *s1, struct semaphore *s2) +{ + if((unsigned long) s1 < (unsigned long) s2) { + down(s1); + down(s2); + } else if(s1 == s2) { + down(s1); + atomic_dec(&s1->count); + } else { + down(s2); + down(s1); + } } /* @@ -749,14 +774,19 @@ int nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, struct svc_fh *tfhp, char *tname, int tlen) { + struct dentry *fdentry, *tdentry, *odentry, *ndentry; struct inode *fdir, *tdir; int err; - if ((err = fh_lookup(rqstp, ffhp, S_IFDIR, MAY_REMOVE) != 0) - || (err = fh_lookup(rqstp, tfhp, S_IFDIR, MAY_CREATE)) != 0) + if ((err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_REMOVE) != 0) + || (err = fh_verify(rqstp, tfhp, S_IFDIR, MAY_CREATE)) != 0) return err; - fdir = ffhp->fh_inode; - tdir = tfhp->fh_inode; + + fdentry = ffhp->fh_handle.fh_dentry; + fdir = fdentry->d_inode; + + tdentry = tfhp->fh_handle.fh_dentry; + tdir = tdentry->d_inode; if (!flen || (fname[0] == '.' && (flen == 1 || (flen == 2 && fname[1] == '.'))) || @@ -764,23 +794,37 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, (tlen == 1 || (tlen == 2 && tname[1] == '.')))) return nfserr_perm; - if (fdir->i_dev != tdir->i_dev) - return nfserr_acces; /* nfserr_nxdev */ - if (!fdir->i_op || !fdir->i_op->rename) - return nfserr_perm; + odentry = lookup_dentry(fname, dget(fdentry), 1); + err = PTR_ERR(odentry); + if (IS_ERR(odentry)) + goto out_no_unlock; - fh_lock(tfhp); /* lock destination directory */ - tdir->i_count++; - fdir->i_count++; - err = fdir->i_op->rename(fdir, fname, flen, tdir, tname, tlen); - fh_unlock(tfhp); /* unlock inode */ + ndentry = lookup_dentry(tname, dget(tdentry), 1); + err = PTR_ERR(ndentry); + if (IS_ERR(ndentry)) + goto out_dput_old; + nfsd_double_down(&tdir->i_sem, &fdir->i_sem); + err = -EXDEV; + if (fdir->i_dev != tdir->i_dev) + goto out_unlock; + err = -EPERM; + if (!fdir->i_op || !fdir->i_op->rename) + goto out_unlock; + err = fdir->i_op->rename(fdir, odentry, tdir, ndentry); if (!err && EX_ISSYNC(tfhp->fh_export)) { - nfsd_write_inode(fdir); - nfsd_write_inode(tdir); + write_inode_now(fdir); + write_inode_now(tdir); } - - return err? nfserrno(-err) : 0; +out_unlock: + up(&tdir->i_sem); + up(&fdir->i_sem); + + dput(ndentry); +out_dput_old: + dput(odentry); +out_no_unlock: + return (err ? nfserrno(-err) : 0); } /* @@ -790,35 +834,40 @@ int nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, char *fname, int flen) { + struct dentry *dentry, *rdentry; struct inode *dirp; int err; if (!flen || isdotent(fname, flen)) return nfserr_acces; - - if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_REMOVE)) != 0) + if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_REMOVE)) != 0) return err; - fh_lock(fhp); /* lock inode */ - dirp = fhp->fh_inode; + dentry = fhp->fh_handle.fh_dentry; + dirp = dentry->d_inode; + + rdentry = lookup_dentry(fname, dget(dentry), 0); + err = PTR_ERR(rdentry); + if (IS_ERR(rdentry)) + goto out; + fh_lock(fhp); if (type == S_IFDIR) { - if (!dirp->i_op || !dirp->i_op->rmdir) - return nfserr_notdir; - dirp->i_count++; - err = dirp->i_op->rmdir(dirp, fname, flen); - } else { /* other than S_IFDIR */ - if (!dirp->i_op || !dirp->i_op->unlink) - return nfserr_perm; - dirp->i_count++; - err = dirp->i_op->unlink(dirp, fname, flen); + err = -ENOTDIR; + if (dirp->i_op && dirp->i_op->rmdir) + err = dirp->i_op->rmdir(dirp, rdentry); + } else { + err = -EPERM; + if (dirp->i_op && dirp->i_op->unlink) + err = dirp->i_op->unlink(dirp, rdentry); } + fh_unlock(fhp); - fh_unlock(fhp); /* unlock inode */ + dput(rdentry); if (!err && EX_ISSYNC(fhp->fh_export)) - nfsd_write_inode(dirp); - - return err? nfserrno(-err) : 0; + write_inode_now(dirp); +out: + return (err ? nfserrno(-err) : 0); } /* @@ -829,6 +878,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, encode_dent_fn func, u32 *buffer, int *countp) { struct readdir_cd cd; + struct inode *inode; struct file file; u32 *p; int oldlen, eof, err; @@ -856,6 +906,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, * readdir() is not guaranteed to fill up the entire buffer, but * may choose to do less. */ + inode = file.f_dentry->d_inode; do { oldlen = cd.buflen; @@ -864,7 +915,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, file.f_inode->i_dev, file.f_inode->i_ino, (int) file.f_pos, (int) oldlen, (int) cd.buflen); */ - err = file.f_op->readdir(file.f_inode, &file, + err = file.f_op->readdir(inode, &file, &cd, (filldir_t) func); if (err < 0) { @@ -904,14 +955,16 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, int nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct statfs *stat) { + struct dentry *dentry; struct inode *inode; struct super_block *sb; unsigned long oldfs; int err; - if ((err = fh_lookup(rqstp, fhp, 0, MAY_NOP)) != 0) + if ((err = fh_verify(rqstp, fhp, 0, MAY_NOP)) != 0) return err; - inode = fhp->fh_inode; + dentry = fhp->fh_handle.fh_dentry; + inode = dentry->d_inode; if (!(sb = inode->i_sb) || !sb->s_op->statfs) return nfserr_io; @@ -928,8 +981,9 @@ nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct statfs *stat) * Check for a user's access permissions to this inode. */ int -nfsd_permission(struct svc_export *exp, struct inode *inode, int acc) +nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) { + struct inode *inode = dentry->d_inode; int err; if (acc == MAY_NOP) @@ -954,7 +1008,7 @@ nfsd_permission(struct svc_export *exp, struct inode *inode, int acc) if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { if (EX_RDONLY(exp) || IS_RDONLY(inode)) return nfserr_rofs; - if (S_ISDIR(inode->i_mode) && nfsd_iscovered(inode)) + if (S_ISDIR(inode->i_mode) && nfsd_iscovered(dentry, exp)) return nfserr_perm; if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) return nfserr_perm; @@ -984,80 +1038,6 @@ nfsd_permission(struct svc_export *exp, struct inode *inode, int acc) } /* - * Look up the inode for a given FH. - */ -struct inode * -nfsd_iget(dev_t dev, ino_t ino) -{ - struct super_block *sb; - - if (!(sb = get_super(dev)) || fs_off_limits(sb)) - return NULL; - return __iget(sb, ino, 0); -} - -/* - * Write the inode if dirty (copy of fs/inode.c:write_inode) - */ -void -nfsd_write_inode(struct inode *inode) -{ - struct super_block *sb = inode->i_sb; - - if (!inode->i_dirt) - return; - while (inode->i_lock) { - sleep_on(&inode->i_wait); - if (!inode->i_dirt) - return; - } - if (!sb || !sb->s_op || !sb->s_op->write_inode) { - inode->i_dirt = 0; - return; - } - inode->i_lock = 1; - sb->s_op->write_inode(inode); - inode->i_lock = 0; - wake_up(&inode->i_wait); -} - -/* - * Look up the root inode of the parent fs. - * We have to go through iget in order to allow for wait_on_inode. - */ -int -nfsd_parentdev(dev_t* devp) -{ - struct super_block *sb; - - if (!(sb = get_super(*devp)) || !sb->s_covered) - return 0; - if (*devp == sb->s_covered->i_dev) - return 0; - *devp = sb->s_covered->i_dev; - return 1; -} - -/* - * This is a copy from fs/inode.c because it wasn't exported. - */ -int -nfsd_notify_change(struct inode *inode, struct iattr *attr) -{ - int retval; - - if (inode->i_sb && inode->i_sb->s_op && - inode->i_sb->s_op->notify_change) - return inode->i_sb->s_op->notify_change(inode, attr); - - if ((retval = inode_change_ok(inode, attr)) != 0) - return retval; - - inode_setattr(inode, attr); - return 0; -} - -/* * Initialize readahead param cache */ void @@ -543,44 +543,21 @@ out: return error; } -asmlinkage int sys_lchown(const char * filename, uid_t user, gid_t group) -{ - struct dentry * dentry; - int error; - - lock_kernel(); - dentry = lnamei(filename); - - error = PTR_ERR(dentry); - if (IS_ERR(dentry)) - goto out; - - error = chown_common(dentry, user, group); - - dput(dentry); -out: - unlock_kernel(); - return(error); -} - asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group) { struct dentry * dentry; int error; lock_kernel(); - dentry = namei(filename); + dentry = lnamei(filename); error = PTR_ERR(dentry); - if (IS_ERR(dentry)) - goto out; - - error = chown_common(dentry, user, group); - - dput(dentry); -out: + if (!IS_ERR(dentry)) { + error = chown_common(dentry, user, group); + dput(dentry); + } unlock_kernel(); - return(error); + return error; } asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group) diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index 5d936ee59..d9a334f85 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -18,12 +18,19 @@ * Changes * Changed for 2.1.19 modules * Jan 1997 Initial release + * Jun 1997 2.1.43+ changes + * Jul 1997 proper page locking in readpage + * Changed to work with 2.1.45+ fs + * Fixed follow_link */ /* todo: - * use malloced memory for file names? - * considering write access... - * network (tftp) files? + * - see Documentation/filesystems/romfs.txt + * - use malloced memory for file names? + * - considering write access... + * - network (tftp) files? + * - in the ancient times something leaked to made umounts + * impossible, but I've not seen it in the last months */ /* @@ -74,7 +81,7 @@ romfs_read_super(struct super_block *s, void *data, int silent) MOD_INC_USE_COUNT; - /* I would parse the options, but there are none.. :) */ + /* I would parse the options here, but there are none.. :) */ lock_super(s); set_blocksize(dev, ROMBSIZE); @@ -82,6 +89,7 @@ romfs_read_super(struct super_block *s, void *data, int silent) s->s_blocksize_bits = ROMBSBITS; bh = bread(dev, 0, ROMBSIZE); if (!bh) { + /* XXX merge with other printk? */ printk ("romfs: unable to read superblock\n"); goto outnobh; } @@ -113,10 +121,11 @@ romfs_read_super(struct super_block *s, void *data, int silent) brelse(bh); s->s_op = &romfs_ops; + s->s_root = d_alloc_root(iget(s, sz), NULL); unlock_super(s); - if (!(s->s_mounted = iget(s, sz))) + if (!s->s_root) goto outnobh; /* Ehrhm; sorry.. :) And thanks to Hans-Joachim Widmaier :) */ @@ -145,10 +154,9 @@ romfs_put_super(struct super_block *sb) return; } - /* That's simple too. */ -static void +static int romfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize) { struct statfs tmp; @@ -157,9 +165,12 @@ romfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize) tmp.f_type = ROMFS_MAGIC; tmp.f_bsize = ROMBSIZE; tmp.f_blocks = (sb->u.romfs_sb.s_maxsize+ROMBSIZE-1)>>ROMBSBITS; - copy_to_user(buf, &tmp, bufsize); + /* XXX tmp.f_namelen = relevant? */ + return copy_to_user(buf, &tmp, bufsize) ? -EFAULT : 0; } +/* some helper routines */ + static int romfs_strnlen(struct inode *i, unsigned long offset, unsigned long count) { @@ -238,8 +249,6 @@ romfs_copyfrom(struct inode *i, void *dest, unsigned long offset, unsigned long return res; } -/* Directory operations */ - static int romfs_readdir(struct inode *i, struct file *filp, void *dirent, filldir_t filldir) { @@ -295,14 +304,16 @@ romfs_readdir(struct inode *i, struct file *filp, void *dirent, filldir_t filldi } static int -romfs_lookup(struct inode *dir, const char *name, int len, struct inode **result) +romfs_lookup(struct inode *dir, struct dentry *dentry) { unsigned long offset, maxoff; int fslen, res; + struct inode *inode; char fsname[ROMFS_MAXFN]; /* XXX dynamic? */ struct romfs_inode ri; + const char *name; /* got from dentry */ + int len; - *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { res = -EBADF; goto out; @@ -317,6 +328,12 @@ romfs_lookup(struct inode *dir, const char *name, int len, struct inode **result maxoff = dir->i_sb->u.romfs_sb.s_maxsize; offset = ntohl(ri.spec) & ROMFH_MASK; + /* ok, now find the file, whose name is in "dentry", in the + * directory specified by "dir". */ + + name = dentry->d_name.name; + len = dentry->d_name.len; + for(;;) { if (!offset || offset >= maxoff || romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) { @@ -350,20 +367,19 @@ romfs_lookup(struct inode *dir, const char *name, int len, struct inode **result if ((ntohl(ri.next) & ROMFH_TYPE) == ROMFH_HRD) offset = ntohl(ri.spec) & ROMFH_MASK; - res = 0; - if (!(*result = iget(dir->i_sb, offset))) - res = -EACCES; + res = -EACCES; + if ((inode = iget(dir->i_sb, offset))!=NULL) { + res = 0; + d_add(dentry, inode); + } out: - iput(dir); return res; } /* * Ok, we do readpage, to be able to execute programs. Unfortunately, - * bmap is not applicable, since we have looser alignments. - * - * XXX I'm not quite sure that I need to muck around the PG_xx bits.. + * we can't use bmap, since we have looser alignments. */ static int @@ -373,8 +389,12 @@ romfs_readpage(struct inode * inode, struct page * page) unsigned long offset, avail, readlen; int result = -EIO; - buf = page_address(page); atomic_inc(&page->count); + set_bit(PG_locked, &page->flags); + + buf = page_address(page); + clear_bit(PG_uptodate, &page->flags); + clear_bit(PG_error, &page->flags); offset = page->offset; if (offset < inode->i_size) { avail = inode->i_size-offset; @@ -383,13 +403,19 @@ romfs_readpage(struct inode * inode, struct page * page) if (readlen < PAGE_SIZE) { memset((void *)(buf+readlen),0,PAGE_SIZE-readlen); } - result = 0; set_bit(PG_uptodate, &page->flags); - } else { - memset((void *)buf, 0, PAGE_SIZE); + result = 0; } } + if (result) { + set_bit(PG_error, &page->flags); + memset((void *)buf, 0, PAGE_SIZE); + } + + clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); free_page(buf); + return result; } @@ -417,6 +443,46 @@ out: return mylen; } +static struct dentry *romfs_follow_link(struct inode *inode, struct dentry *base) +{ + char *link; + int len, cnt; + struct dentry *dentry; + + /* Note: 2.1.46+ calls this for our strange directories... + * What I do is not really right, but I like it better for now, + * than a separate i_op table. Anyway, our directories won't + * have multiple "real" links to them, so it maybe loses nothing. */ + if (!S_ISLNK(inode->i_mode)) { + dentry = dget(i_dentry(inode)); + goto outnobuf; + } + + len = inode->i_size; + + dentry = ERR_PTR(-EAGAIN); /* correct? */ + if (!(link = kmalloc(len+1, GFP_KERNEL))) + goto outnobuf; + + cnt = romfs_copyfrom(inode, link, inode->u.romfs_i.i_dataoffset, len); + if (len != cnt) { + dentry = ERR_PTR(-EIO); + goto out; + } else + link[len] = 0; + + dentry = lookup_dentry(link, base, 1); + kfree(link); + + if (0) { +out: + kfree(link); +outnobuf: + dput(base); + } + return dentry; +} + /* Mapping from our types to the kernel */ static struct file_operations romfs_file_operations = { @@ -447,6 +513,7 @@ static struct inode_operations romfs_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ romfs_readpage, /* readpage */ NULL, /* writepage */ NULL, /* bmap -- not really */ @@ -471,7 +538,7 @@ static struct file_operations romfs_dir_operations = { NULL /* revalidate */ }; -/* Merged dir/symlink op table. readdir/lookup/readlink +/* Merged dir/symlink op table. readdir/lookup/readlink/follow_link * will protect from type mismatch. */ @@ -487,6 +554,7 @@ static struct inode_operations romfs_dirlink_inode_operations = { NULL, /* mknod */ NULL, /* rename */ romfs_readlink, /* readlink */ + romfs_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -519,9 +587,9 @@ romfs_read_inode(struct inode *i) int nextfh, ino; struct romfs_inode ri; - i->i_op = NULL; - ino = i->i_ino & ROMFH_MASK; + i->i_op = NULL; + i->i_mode = 0; /* Loop for finding the real hard link */ for(;;) { @@ -549,6 +617,7 @@ romfs_read_inode(struct inode *i) ino = ((ROMFH_SIZE+ino+1+ROMFH_PAD)&ROMFH_MASK); else ino = 0; + i->u.romfs_i.i_metasize = ino; i->u.romfs_i.i_dataoffset = ino+(i->i_ino&ROMFH_MASK); @@ -573,9 +642,10 @@ romfs_read_inode(struct inode *i) static struct super_operations romfs_ops = { romfs_read_inode, /* read inode */ - NULL, /* notify change */ NULL, /* write inode */ NULL, /* put inode */ + NULL, /* delete inode */ + NULL, /* notify change */ romfs_put_super, /* put super */ NULL, /* write super */ romfs_statfs, /* statfs */ diff --git a/fs/super.c b/fs/super.c index f143f9348..a7a2d434e 100644 --- a/fs/super.c +++ b/fs/super.c @@ -637,6 +637,7 @@ static int umount_dev(kdev_t dev) if (MAJOR(dev) >= MAX_BLKDEV) return -ENXIO; + fsync_dev(dev); retval = do_umount(dev,0); if (!retval) { fsync_dev(dev); |