diff -u --recursive --new-file linux-2.2.17pre19-nfsv3-0.22.3/fs/nfs/Makefile linux-2.2.17-nfsv3/fs/nfs/Makefile --- linux-2.2.17pre19-nfsv3-0.22.3/fs/nfs/Makefile Mon Aug 21 01:26:07 2000 +++ linux-2.2.17-nfsv3/fs/nfs/Makefile Mon Sep 4 15:12:47 2000 @@ -9,7 +9,7 @@ O_TARGET := nfs.o O_OBJS := inode.o file.o read.o write.o dir.o symlink.o proc.o \ - nfs2xdr.o flushd.o + nfs2xdr.o flushd.o unlink.o ifdef CONFIG_ROOT_NFS O_OBJS += nfsroot.o mount_clnt.o diff -u --recursive --new-file linux-2.2.17pre19-nfsv3-0.22.3/fs/nfs/dir.c linux-2.2.17-nfsv3/fs/nfs/dir.c --- linux-2.2.17pre19-nfsv3-0.22.3/fs/nfs/dir.c Mon Aug 21 01:26:07 2000 +++ linux-2.2.17-nfsv3/fs/nfs/dir.c Mon Sep 4 19:23:46 2000 @@ -19,6 +19,7 @@ * to simplify cookie handling. -Trond */ +#include #include #include #include @@ -43,7 +44,6 @@ /* #define NFS_DEBUG_VERBOSE 1 */ static int nfs_safe_remove(struct dentry *); -static int _nfs_safe_remove(struct dentry *, struct rpc_cred *); static ssize_t nfs_dir_read(struct file *, char *, size_t, loff_t *); static int nfs_readdir(struct file *, void *, filldir_t); @@ -611,46 +611,24 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_flags); - if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { - struct rpc_auth *auth = NULL; - struct rpc_cred *cred = nfs_dentry_cred(dentry); - int error; - - dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; - NFS_DENTRY(dentry)->cred = NULL; - if (dentry->d_inode) - auth = NFS_CLIENT(dentry->d_inode)->cl_auth; - /* Unhash it first */ - d_drop(dentry); - error = _nfs_safe_remove(dentry, cred); - if (cred && auth) - rpcauth_releasecred(auth, cred); - if (error) - printk(KERN_INFO "NFS: can't silly-delete %s/%s, error=%d\n", - dentry->d_parent->d_name.name, - dentry->d_name.name, error); - } + if (dentry->d_flags & DCACHE_NFSFS_RENAMED) + nfs_complete_unlink(dentry); } -__inline__ struct nfs_dentry *nfs_fh_alloc(void) +__inline__ struct nfs_fh *nfs_fh_alloc(void) { - struct nfs_dentry *p; + struct nfs_fh *p; p = kmalloc(sizeof(*p), GFP_KERNEL); if (!p) return NULL; memset(p, 0, sizeof(*p)); - p->magic = NFS_DENTRY_MAGIC; return p; } -__inline__ void nfs_fh_free(struct nfs_dentry *p) +__inline__ void nfs_fh_free(struct nfs_fh *p) { - if (p->magic == NFS_DENTRY_MAGIC) { - p->magic = 0; - kfree(p); - } else - printk(KERN_ERR "NFS: corrupt dentry structure!\n"); + kfree(p); } /* @@ -658,9 +636,9 @@ */ static void nfs_dentry_release(struct dentry *dentry) { - if (NFS_DENTRY(dentry)) { - nfs_fh_free(NFS_DENTRY(dentry)); - NFS_DENTRY(dentry) = NULL; + if (dentry->d_fsdata) { + nfs_fh_free(dentry->d_fsdata); + dentry->d_fsdata = NULL; } } @@ -1011,8 +989,7 @@ if (!error) { nfs_renew_times(dentry); d_move(dentry, sdentry); - dentry->d_flags |= DCACHE_NFSFS_RENAMED; - NFS_DENTRY(dentry)->cred = rpcauth_lookupcred(NFS_CLIENT(dentry->d_inode)->cl_auth, 0); + error = nfs_async_unlink(dentry); /* If we return 0 we don't unlink */ } dput(sdentry); @@ -1027,7 +1004,7 @@ * We update inode->i_nlink and free the inode prior to the operation * to avoid possible races if the server reuses the inode. */ -static int _nfs_safe_remove(struct dentry *dentry, struct rpc_cred *cred) +static int nfs_safe_remove(struct dentry *dentry) { struct nfs_fattr dir_attr; struct dentry *dir = dentry->d_parent; @@ -1043,9 +1020,8 @@ rehash = 1; } - dfprintk(VFS, "NFS: safe_remove(%s/%s, %ld)\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - inode->i_ino); + dfprintk(VFS, "NFS: safe_remove(%s/%s)\n", + dentry->d_parent->d_name.name, dentry->d_name.name); /* N.B. not needed now that d_delete is done in advance? */ error = -EBUSY; @@ -1065,8 +1041,9 @@ } nfs_zap_caches(dir_i); - NFS_CACHEINV(inode); - error = NFS_CALL(remove, dir_i, (dir, &dir_attr, &dentry->d_name, cred)); + if (inode) + NFS_CACHEINV(inode); + error = NFS_CALL(remove, dir_i, (dir, &dir_attr, &dentry->d_name)); nfs_refresh_inode(dir_i, &dir_attr); if (error < 0) goto out; @@ -1084,12 +1061,6 @@ return error; } -static int nfs_safe_remove(struct dentry *dentry) -{ - return _nfs_safe_remove(dentry, NULL); -} - - /* We do silly rename. In case sillyrename() returns -EBUSY, the inode * belongs to an active ".nfs..." file and we return -EBUSY. * @@ -1364,9 +1335,11 @@ return 0; err = NFS_CALL(access, i, (de, msk, &fattr, 0)); - if (err == -EACCES && (current->fsuid != current->uid || - current->fsgid != current->gid)) +#ifdef CONFIG_SUNRPC_ABUSIVE_SUID + if (err == -EACCES && current->uid != 0 && current->gid != 0 && + (current->fsuid != current->uid || current->fsgid != current->gid)) err = NFS_CALL(access, i, (de, msk, &fattr, 1)); +#endif nfs_refresh_inode(i, &fattr); out: return err; diff -u --recursive --new-file linux-2.2.17pre19-nfsv3-0.22.3/fs/nfs/dir.c.~1~ linux-2.2.17-nfsv3/fs/nfs/dir.c.~1~ --- linux-2.2.17pre19-nfsv3-0.22.3/fs/nfs/dir.c.~1~ Thu Jan 1 01:00:00 1970 +++ linux-2.2.17-nfsv3/fs/nfs/dir.c.~1~ Mon Sep 4 15:52:14 2000 @@ -0,0 +1,1350 @@ +/* + * linux/fs/nfs/dir.c + * + * Copyright (C) 1992 Rick Sladkey + * + * nfs directory handling functions + * + * 10 Apr 1996 Added silly rename for unlink --okir + * 28 Sep 1996 Improved directory cache --okir + * 23 Aug 1997 Claus Heine claus@momo.math.rwth-aachen.de + * Re-implemented silly rename for unlink, newly implemented + * silly rename for nfs_rename() following the suggestions + * of Olaf Kirch (okir) found in this file. + * Following Linus comments on my original hack, this version + * depends only on the dcache stuff and doesn't touch the inode + * layer (iput() and friends). + * 6 Jun 1999 Cache readdir lookups in the page cache. -DaveM + * 7 Oct 1999 Rewrite of Dave's readdir stuff for NFSv3 support, and in order + * to simplify cookie handling. -Trond + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* for fs functions */ + +#define NFS_PARANOIA 1 +/* #define NFS_DEBUG_VERBOSE 1 */ + +static int nfs_safe_remove(struct dentry *); + +static ssize_t nfs_dir_read(struct file *, char *, size_t, loff_t *); +static int nfs_readdir(struct file *, void *, filldir_t); +static struct dentry *nfs_lookup(struct inode *, struct dentry *); +static int nfs_create(struct inode *, struct dentry *, int); +static int nfs_mkdir(struct inode *, struct dentry *, int); +static int nfs_rmdir(struct inode *, struct dentry *); +static int nfs_unlink(struct inode *, struct dentry *); +static int nfs_symlink(struct inode *, struct dentry *, const char *); +static int nfs_link(struct dentry *, struct inode *, struct dentry *); +static int nfs_mknod(struct inode *, struct dentry *, int, int); +static int nfs_rename(struct inode *, struct dentry *, + struct inode *, struct dentry *); + +static struct file_operations nfs_dir_operations = { + NULL, /* lseek - default */ + nfs_dir_read, /* read - bad */ + NULL, /* write - bad */ + nfs_readdir, /* readdir */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + NULL, /* mmap */ + nfs_open, /* open */ + NULL, /* flush */ + nfs_release, /* release */ + NULL /* fsync */ +}; + +struct inode_operations nfs_dir_inode_operations = { + &nfs_dir_operations, /* default directory file-ops */ + nfs_create, /* create */ + nfs_lookup, /* lookup */ + nfs_link, /* link */ + nfs_unlink, /* unlink */ + nfs_symlink, /* symlink */ + nfs_mkdir, /* mkdir */ + nfs_rmdir, /* rmdir */ + nfs_mknod, /* mknod */ + nfs_rename, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + nfs_permission, /* permission */ + NULL, /* smap */ + NULL, /* updatepage */ + nfs_revalidate, /* revalidate */ +}; + +static ssize_t +nfs_dir_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + return -EISDIR; +} + +typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int); +typedef struct { + struct file *file; + struct page *page; + unsigned long page_index; + unsigned page_offset; + u64 target; + struct nfs_entry *entry; + decode_dirent_t decode; + int plus; + int error; +} nfs_readdir_descriptor_t; + +/* Now we cache directories properly, by stuffing the dirent + * data directly in the page cache. + * + * Inode invalidation due to refresh etc. takes care of + * _everything_, no sloppy entry flushing logic, no extraneous + * copying, network direct to page cache, the way it was meant + * to be. + * + * NOTE: Dirent information verification is done always by the + * page-in of the RPC reply, nowhere else, this simplies + * things substantially. + */ +static +int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page) +{ + struct file *file = desc->file; + struct dentry *dir = file->f_dentry; + struct inode *inode = dir->d_inode; + struct nfs_fattr dir_attr; + void *buffer = (void *)page_address(page); + int plus = NFS_USE_READDIRPLUS(inode); + int error; + + dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->offset); + + again: + error = NFS_CALL(readdir, inode, (dir, &dir_attr, + nfs_file_cred(file), + desc->entry->cookie, buffer, + NFS_SERVER(inode)->dtsize, plus)); + nfs_refresh_inode(inode, &dir_attr); + /* We requested READDIRPLUS, but the server doesn't grok it */ + if (desc->plus && error == -ENOTSUPP) { + NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS; + plus = 0; + goto again; + } + if (error < 0) + goto error; + flush_dcache_page(page_address(page)); /* Is this correct? */ + set_bit(PG_uptodate, &page->flags); + + /* Ensure consistent page alignment of the data. + * Note: assumes we have exclusive access to this inode either + * throught inode->i_sem or some other mechanism. + */ + if (page->offset == 0) + invalidate_inode_pages(inode); + nfs_unlock_page(page); + return 0; + error: + set_bit(PG_error, &page->flags); + nfs_unlock_page(page); + desc->error = error; + return -EIO; +} + +/* + * Given a pointer to a buffer that has already been filled by a call + * to readdir, find the next entry. + * + * If the end of the buffer has been reached, return -EAGAIN, if not, + * return the offset within the buffer of the next entry to be + * read. + */ +static inline +int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page) +{ + struct nfs_entry *entry = desc->entry; + char *start = (char *)page_address(page), + *p = start; + int loop_count = 0, + status = 0; + + for(;;) { + p = (char *)desc->decode((u32*)p, entry, desc->plus); + if (IS_ERR(p)) { + status = PTR_ERR(p); + break; + } + desc->page_offset = p - start; + dfprintk(VFS, "NFS: found cookie %Lu\n", (long long)entry->cookie); + if (entry->prev_cookie == desc->target) + break; + if (loop_count++ > 200) { + loop_count = 0; + schedule(); + } + } + dfprintk(VFS, "NFS: find_dirent() returns %d\n", status); + return status; +} + +/* + * Find the given page, and call find_dirent() in order to try to + * return the next entry. + */ +static inline +int find_dirent_page(nfs_readdir_descriptor_t *desc) +{ + struct inode *inode = desc->file->f_dentry->d_inode; + struct page *page; + unsigned long index = desc->page_index; + int status; + + dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", desc->page_index); + + if (desc->page) { + page_cache_release(desc->page); + desc->page = NULL; + } + + page = read_cache_page(inode, index, + (filler_t *)nfs_readdir_filler, desc); + if (IS_ERR(page)) { + status = PTR_ERR(page); + goto out; + } + + /* NOTE: Someone else may have changed the READDIRPLUS flag */ + desc->plus = NFS_USE_READDIRPLUS(inode); + status = find_dirent(desc, page); + if (status >= 0) + desc->page = page; + else + page_cache_release(page); + out: + dfprintk(VFS, "NFS: find_dirent_page() returns %d\n", status); + return status; +} + +/* + * Recurse through the page cache pages, and return a + * filled nfs_entry structure of the next directory entry if possible. + * + * The target for the search is 'desc->target'. + */ +static inline +int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) +{ + int res = 0; + int loop_count = 0; + + dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (long long)desc->target); + for (;;) { + res = find_dirent_page(desc); + if (res != -EAGAIN) + break; + /* Align to beginning of next page */ + desc->page_offset = 0; + desc->page_index += PAGE_CACHE_SIZE; + if (loop_count++ > 200) { + loop_count = 0; + schedule(); + } + } + dfprintk(VFS, "NFS: readdir_search_pagecache() returned %d\n", res); + return res; +} + +/* + * Once we've found the start of the dirent within a page: fill 'er up... + */ +static +int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, + filldir_t filldir) +{ + struct file *file = desc->file; + struct nfs_entry *entry = desc->entry; + char *start = (char *)page_address(desc->page), + *p = start + desc->page_offset; + unsigned long fileid; + int loop_count = 0, + res = 0; + + dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)desc->target); + + for(;;) { + /* Note: entry->prev_cookie contains the cookie for + * retrieving the current dirent on the server */ + fileid = nfs_fileid_to_ino_t(entry->ino); + res = filldir(dirent, entry->name, entry->len, + entry->prev_cookie, fileid); + if (res < 0) + break; + file->f_pos = desc->target = entry->cookie; + p = (char *)desc->decode((u32 *)p, entry, desc->plus); + if (IS_ERR(p)) { + if (PTR_ERR(p) == -EAGAIN) { + desc->page_offset = 0; + desc->page_index += PAGE_CACHE_SIZE; + } + break; + } + desc->page_offset = p - start; + if (loop_count++ > 200) { + loop_count = 0; + schedule(); + } + } + page_cache_release(desc->page); + desc->page = NULL; + + dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (long long)desc->target, res); + return res; +} + +/* + * If we cannot find a cookie in our cache, we suspect that this is + * because it points to a deleted file, so we ask the server to return + * whatever it thinks is the next entry. We then feed this to filldir. + * If all goes well, we should then be able to find our way round the + * cache on the next call to readdir_search_pagecache(); + * + * NOTE: we cannot add the anonymous page to the pagecache because + * the data it contains might not be page aligned. Besides, + * we should already have a complete representation of the + * directory in the page cache by the time we get here. + */ +static inline +int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, + filldir_t filldir) +{ + struct file *file = desc->file; + struct dentry *dir = file->f_dentry; + struct inode *inode = dir->d_inode; + struct nfs_fattr dir_attr; + struct page *page = NULL; + unsigned long cache_page; + u32 *p; + int status = -EIO; + + dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (long long)desc->target); + if (desc->page) { + page_cache_release(desc->page); + desc->page = NULL; + } + + cache_page = page_cache_alloc(); + if (!cache_page) { + status = -ENOMEM; + goto out; + } + page = page_cache_entry(cache_page); + p = (u32 *)page_address(page); + status = NFS_CALL(readdir, inode, (dir, &dir_attr, + nfs_file_cred(file), + desc->target, p, + NFS_SERVER(inode)->dtsize, 0)); + nfs_refresh_inode(inode, &dir_attr); + if (status >= 0) { + p = desc->decode(p, desc->entry, 0); + if (IS_ERR(p)) + status = PTR_ERR(p); + else + desc->entry->prev_cookie = desc->target; + } + if (status < 0) + goto out_release; + + desc->page_index = 0; + desc->page_offset = 0; + desc->page = page; + status = nfs_do_filldir(desc, dirent, filldir); + + /* Reset read descriptor so it searches the page cache from + * the start upon the next call to readdir_search_pagecache() */ + desc->page_index = 0; + desc->page_offset = 0; + memset(desc->entry, 0, sizeof(*desc->entry)); + out: + dfprintk(VFS, "NFS: uncached_readdir() returns %d\n", status); + return status; + out_release: + page_cache_release(page); + goto out; +} + +/* The file offset position is now represented as a true offset into the + * page cache as is the case in most of the other filesystems. + */ +static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct dentry *dentry = filp->f_dentry; + struct inode *inode = dentry->d_inode; + nfs_readdir_descriptor_t my_desc, + *desc = &my_desc; + struct nfs_entry my_entry; + long res; + + res = nfs_revalidate(dentry); + if (res < 0) + return res; + + /* + * filp->f_pos points to the file offset in the page cache. + * but if the cache has meanwhile been zapped, we need to + * read from the last dirent to revalidate f_pos + * itself. + */ + memset(desc, 0, sizeof(*desc)); + memset(&my_entry, 0, sizeof(my_entry)); + + desc->file = filp; + desc->target = filp->f_pos; + desc->entry = &my_entry; + desc->decode = NFS_PROTO(inode)->decode_dirent; + + while(!desc->entry->eof) { + res = readdir_search_pagecache(desc); + if (res == -EBADCOOKIE) { + /* This means either end of directory */ + if (desc->entry->cookie == desc->target) { + res = 0; + break; + } + /* Or that the server has 'lost' a cookie */ + res = uncached_readdir(desc, dirent, filldir); + if (res >= 0) + continue; + } + if (res < 0) + break; + + res = nfs_do_filldir(desc, dirent, filldir); + if (res < 0) { + res = 0; + break; + } + } + if (desc->page) + page_cache_release(desc->page); + if (desc->error < 0) + return desc->error; + if (res < 0) + return res; + return 0; +} + + +/* + * Whenever an NFS operation succeeds, we know that the dentry + * is valid, so we update the revalidation timestamp. + */ +static inline void +nfs_renew_times(struct dentry * dentry) +{ + dentry->d_time = jiffies; +} + +static inline int nfs_dentry_force_reval(struct dentry *dentry, int flags) +{ + struct inode *inode = dentry->d_inode; + unsigned long timeout = NFS_ATTRTIMEO(inode); + + /* + * If it's the last lookup in a series, we use a stricter + * cache consistency check by looking at the parent mtime. + * + * If it's been modified in the last hour, be really strict. + * (This still means that we can avoid doing unnecessary + * work on directories like /usr/share/bin etc which basically + * never change). + */ + if (!(flags & LOOKUP_CONTINUE)) { + long diff = CURRENT_TIME - dentry->d_parent->d_inode->i_mtime; + + if (diff < 15*60) + timeout = 0; + } + + return time_after(jiffies,dentry->d_time + timeout); +} + +/* + * We judge how long we want to trust negative + * dentries by looking at the parent inode mtime. + * + * If mtime is close to present time, we revalidate + * more often. + */ +#define NFS_REVALIDATE_NEGATIVE (1 * HZ) +static inline int nfs_neg_need_reval(struct dentry *dentry) +{ + struct inode *dir = dentry->d_parent->d_inode; + unsigned long timeout = NFS_ATTRTIMEO(dir); + long diff = CURRENT_TIME - dir->i_mtime; + + if (diff < 5*60 && timeout > NFS_REVALIDATE_NEGATIVE) + timeout = NFS_REVALIDATE_NEGATIVE; + + return time_after(jiffies, dentry->d_time + timeout); +} + +/* + * This is called every time the dcache has a lookup hit, + * and we should check whether we can really trust that + * lookup. + * + * NOTE! The hit can be a negative hit too, don't assume + * we have an inode! + * + * If the dentry is older than the revalidation interval, + * we do a new lookup and verify that the dentry is still + * correct. + */ +static int nfs_lookup_revalidate(struct dentry * dentry, int flags) +{ + struct dentry *dir = dentry->d_parent; + struct inode *inode = dentry->d_inode, + *dir_i = dir->d_inode; + struct nfs_fh fhandle; + struct nfs_fattr fattr, dir_attr; + int error; + + /* + * If we don't have an inode, let's look at the parent + * directory mtime to get a hint about how often we + * should validate things.. + */ + if (!inode) { + if (nfs_neg_need_reval(dentry)) + goto out_bad; + goto out_valid; + } + + if (is_bad_inode(inode)) { + dfprintk(VFS, "nfs_lookup_validate: %s/%s has dud inode\n", + dir->d_name.name, dentry->d_name.name); + goto out_bad; + } + + if (!nfs_dentry_force_reval(dentry, flags)) + goto out_valid; + + if (IS_ROOT(dentry)) { + __nfs_revalidate_inode(dentry); + goto out_valid_renew; + } + + if (NFS_FLAGS(inode) & NFS_INO_STALE) + goto out_bad; + + /* + * Do a new lookup and check the dentry attributes. + */ + error = NFS_CALL(lookup, dir_i, (dir, &dir_attr, + &dentry->d_name, &fhandle, &fattr)); + if (error < 0) + goto out_bad; + + /* Inode number matches? */ + if (!(fattr.valid & NFS_ATTR_FATTR) || + NFS_FSID(inode) != fattr.fsid || + NFS_FILEID(inode) != fattr.fileid) + goto out_bad; + + /* Filehandle matches? */ + if (NFS_FH(dentry)->size == 0) + goto out_bad; + + if (NFS_FH(dentry)->size != fhandle.size || + memcmp(NFS_FH(dentry)->data, fhandle.data, fhandle.size)) + goto out_bad; + + /* Ok, remeber that we successfully checked it.. */ + nfs_refresh_inode(inode, &fattr); + nfs_refresh_inode(dir_i, &dir_attr); + + out_valid_renew: + nfs_renew_times(dentry); + out_valid: + return 1; + out_bad: + if (!list_empty(&dentry->d_subdirs)) + shrink_dcache_parent(dentry); + /* If we have submounts, don't unhash ! */ + if (have_submounts(dentry)) + goto out_valid; + d_drop(dentry); + if (dentry->d_parent->d_inode) + NFS_CACHEINV(dentry->d_parent->d_inode); + if (inode && S_ISDIR(inode->i_mode)) + NFS_CACHEINV(inode); + return 0; +} + +/* + * This is called from dput() when d_count is going to 0. + * We use it to clean up silly-renamed files. + */ +static void nfs_dentry_delete(struct dentry *dentry) +{ + dfprintk(VFS, "NFS: dentry_delete(%s/%s, %x)\n", + dentry->d_parent->d_name.name, dentry->d_name.name, + dentry->d_flags); + + if (dentry->d_flags & DCACHE_NFSFS_RENAMED) + nfs_complete_unlink(dentry); +} + +__inline__ struct nfs_fh *nfs_fh_alloc(void) +{ + struct nfs_fh *p; + + p = kmalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return NULL; + memset(p, 0, sizeof(*p)); + return p; +} + +__inline__ void nfs_fh_free(struct nfs_fh *p) +{ + kfree(p); +} + +/* + * Called when the dentry is being freed to release private memory. + */ +static void nfs_dentry_release(struct dentry *dentry) +{ + if (dentry->d_fsdata) { + nfs_fh_free(dentry->d_fsdata); + dentry->d_fsdata = NULL; + } +} + +struct dentry_operations nfs_dentry_operations = { + nfs_lookup_revalidate, /* d_revalidate(struct dentry *, int) */ + NULL, /* d_hash */ + NULL, /* d_compare */ + nfs_dentry_delete, /* d_delete(struct dentry *) */ + nfs_dentry_release, /* d_release(struct dentry *) */ + NULL /* d_iput */ +}; + +static struct dentry *nfs_lookup(struct inode *dir_i, struct dentry * dentry) +{ + struct dentry *dir = dentry->d_parent; + struct inode *inode; + int error; + struct nfs_fh fhandle; + struct nfs_fattr fattr, dir_attr; + + dfprintk(VFS, "NFS: lookup(%s/%s)\n", + dentry->d_parent->d_name.name, dentry->d_name.name); + + error = -ENAMETOOLONG; + if (dentry->d_name.len > NFS_SERVER(dir_i)->namelen) + goto out; + + dentry->d_op = &nfs_dentry_operations; + + if (!dentry->d_fsdata) { + dentry->d_fsdata = nfs_fh_alloc(); + if (!dentry->d_fsdata) { + error = -ENOMEM; + goto out; + } + } + +#if NFS_FIXME + inode = nfs_dircache_lookup(dir_i, dentry); + if (inode) + goto no_entry; +#endif + + error = NFS_CALL(lookup, dir_i, (dir, &dir_attr, + &dentry->d_name, &fhandle, &fattr)); + nfs_refresh_inode(dir_i, &dir_attr); + inode = NULL; + if (error == -ENOENT) + goto no_entry; + + if (!error) { + error = -EACCES; + inode = nfs_fhget(dentry, &fhandle, &fattr); + if (inode) { + no_entry: + d_add(dentry, inode); + nfs_renew_times(dentry); + error = 0; + } + } +out: + return ERR_PTR(error); +} + +/* + * Code common to create, mkdir, and mknod. + */ +static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, + struct nfs_fattr *fattr) +{ + struct inode *inode; + int error = -EACCES; + + inode = nfs_fhget(dentry, fhandle, fattr); + if (inode) { + d_instantiate(dentry, inode); + nfs_renew_times(dentry); + error = 0; + } + return error; +} + +/* + * Following a failed create operation, we drop the dentry rather + * than retain a negative dentry. This avoids a problem in the event + * that the operation succeeded on the server, but an error in the + * reply path made it appear to have failed. + */ +static int nfs_create(struct inode *dir_i, struct dentry *dentry, int mode) +{ + struct dentry *dir = dentry->d_parent; + struct iattr attr; + struct nfs_fattr fattr, dir_attr; + struct nfs_fh fhandle; + int error; + + dfprintk(VFS, "NFS: create(%x/%ld, %s\n", + dir_i->i_dev, dir_i->i_ino, dentry->d_name.name); + +#ifdef NFSD_BROKEN_UID + /* We set uid/gid in the request because IBM's broken nfsd + * uses the root uid/gid otherwise. Argh! + * (Hopefully the server will override the gid when the directory + * has the sticky bit set. Irix may have a problem here...) + */ + attr.ia_mode = mode; + attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID; + attr.ia_uid = current->fsuid; + attr.ia_gid = current->fsgid; +#else + attr.ia_mode = mode; + attr.ia_valid = ATTR_MODE; +#endif + + /* + * Invalidate the dir cache before the operation to avoid a race. + * The 0 argument passed into the create function should one day + * contain the O_EXCL flag if requested. This allows NFSv3 to + * select the appropriate create strategy. Currently open_namei + * does not pass the create flags. + */ + nfs_zap_caches(dir_i); + error = NFS_CALL(create, dir_i, (dir, &dir_attr, &dentry->d_name, + &attr, 0, &fhandle, &fattr)); + nfs_refresh_inode(dir_i, &dir_attr); + if (!error && fhandle.size != 0) + error = nfs_instantiate(dentry, &fhandle, &fattr); + if (error || fhandle.size == 0) + d_drop(dentry); + return error; +} + +/* + * See comments for nfs_proc_create regarding failed operations. + */ +static int nfs_mknod(struct inode *dir_i, struct dentry *dentry, int mode, int rdev) +{ + struct dentry *dir = dentry->d_parent; + struct iattr attr; + struct nfs_fattr fattr, dir_attr; + struct nfs_fh fhandle; + int error; + + dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n", + dir_i->i_dev, dir_i->i_ino, dentry->d_name.name); + +#ifdef NFSD_BROKEN_UID + attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID; + attr.ia_mode = mode; + attr.ia_uid = current->fsuid; + attr.ia_gid = current->fsgid; +#else + attr.ia_valid = ATTR_MODE; + attr.ia_mode = mode; +#endif + + + nfs_zap_caches(dir_i); + error = NFS_CALL(mknod, dir_i, (dir, &dir_attr, &dentry->d_name, + &attr, rdev, &fhandle, &fattr)); + nfs_refresh_inode(dir_i, &dir_attr); + if (!error && fhandle.size != 0) + error = nfs_instantiate(dentry, &fhandle, &fattr); + if (error || fhandle.size == 0) + d_drop(dentry); + return error; +} + +/* + * See comments for nfs_proc_create regarding failed operations. + */ +static int nfs_mkdir(struct inode *dir_i, struct dentry *dentry, int mode) +{ + struct dentry *dir = dentry->d_parent; + struct iattr attr; + struct nfs_fattr fattr, dir_attr; + struct nfs_fh fhandle; + int error; + + dfprintk(VFS, "NFS: mkdir(%x/%ld, %s)\n", + dir_i->i_dev, dir_i->i_ino, dentry->d_name.name); + +#ifdef NFSD_BROKEN_UID + attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID; + attr.ia_mode = mode | S_IFDIR; + attr.ia_uid = current->fsuid; + attr.ia_gid = current->fsgid; +#else + attr.ia_valid = ATTR_MODE; + attr.ia_mode = mode | S_IFDIR; +#endif + + nfs_zap_caches(dir_i); + error = NFS_CALL(mkdir, dir_i, (dir, &dir_attr, + &dentry->d_name, &attr, &fhandle, &fattr)); + nfs_refresh_inode(dir_i, &dir_attr); + if (!error && fhandle.size != 0) + error = nfs_instantiate(dentry, &fhandle, &fattr); + + if (error || fhandle.size == 0) + d_drop(dentry); + return error; +} + +static int nfs_rmdir(struct inode *dir_i, struct dentry *dentry) +{ + struct dentry *dir = dentry->d_parent; + struct nfs_fattr dir_attr; + int error; + + dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n", + dir_i->i_dev, dir_i->i_ino, dentry->d_name.name); + + nfs_zap_caches(dir_i); + error = NFS_CALL(rmdir, dir_i, (dir, &dir_attr, &dentry->d_name)); + nfs_refresh_inode(dir_i, &dir_attr); + + /* Free the inode */ + if (!error) + d_delete(dentry); + + return error; +} + + +/* Note: we copy the code from lookup_dentry() here, only: we have to + * omit the directory lock. We are already the owner of the lock when + * we reach here. And "down(&dir->i_sem)" would make us sleep forever + * ('cause WE have the lock) + * + * VERY IMPORTANT: calculate the hash for this dentry!!!!!!!! + * Otherwise the cached lookup DEFINITELY WILL fail. And a new dentry + * is created. Without the DCACHE_NFSFS_RENAMED flag. And with d_count + * == 1. And trouble. + * + * Concerning my choice of the temp name: it is just nice to have + * i_ino part of the temp name, as this offers another check whether + * somebody attempts to remove the "silly renamed" dentry itself. + * Which is something that I consider evil. Your opinion may vary. + * BUT: + * Now that I compute the hash value right, it should be possible to simply + * check for the DCACHE_NFSFS_RENAMED flag in dentry->d_flag instead of + * doing the string compare. + * WHICH MEANS: + * This offers the opportunity to shorten the temp name. Currently, I use + * the hex representation of i_ino + an event counter. This sums up to + * as much as 36 characters for a 64 bit machine, and needs 20 chars on + * a 32 bit machine. + * QUINTESSENCE + * The use of i_ino is simply cosmetic. All we need is a unique temp + * file name for the .nfs files. The event counter seemed to be adequate. + * And as we retry in case such a file already exists, we are guaranteed + * to succeed. + */ + +static +struct dentry *nfs_silly_lookup(struct dentry *parent, char *silly, int slen) +{ + struct qstr sqstr; + struct dentry *sdentry; + struct dentry *res; + + sqstr.name = silly; + sqstr.len = slen; + sqstr.hash = full_name_hash(silly, slen); + sdentry = d_lookup(parent, &sqstr); + if (!sdentry) { + sdentry = d_alloc(parent, &sqstr); + if (sdentry == NULL) + return ERR_PTR(-ENOMEM); + res = nfs_lookup(parent->d_inode, sdentry); + if (res) { + dput(sdentry); + return res; + } + } + return sdentry; +} + +static int nfs_sillyrename(struct inode *dir_i, struct dentry *dentry) +{ + struct dentry *dir = dentry->d_parent; + static unsigned int sillycounter = 0; + struct nfs_fattr dir_attr; + const int i_inosize = sizeof(dir_i->i_ino)*2; + const int countersize = sizeof(sillycounter)*2; + const int slen = strlen(".nfs") + i_inosize + countersize; + struct qstr qsilly; + char silly[slen+1]; + struct dentry * sdentry; + int error = -EIO; + + dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n", + dentry->d_parent->d_name.name, dentry->d_name.name, + dentry->d_count); + + /* + * Note that a silly-renamed file can be deleted once it's + * no longer in use -- it's just an ordinary file now. + */ + if (dentry->d_count == 1) { + dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; + goto out; /* No need to silly rename. */ + } + +#ifdef NFS_PARANOIA + if (!dentry->d_inode) + printk(KERN_ERR "NFS: silly-renaming %s/%s, negative dentry??\n", + dentry->d_parent->d_name.name, dentry->d_name.name); +#endif + /* + * We don't allow a dentry to be silly-renamed twice. + */ + error = -EBUSY; + if (dentry->d_flags & DCACHE_NFSFS_RENAMED) + goto out; + + sprintf(silly, ".nfs%*.*lx", + i_inosize, i_inosize, dentry->d_inode->i_ino); + + sdentry = NULL; + do { + char *suffix = silly + slen - countersize; + + if (sdentry) + dput(sdentry); + sillycounter++; + sprintf(suffix, "%*.*x", countersize, countersize, sillycounter); + + dfprintk(VFS, "trying to rename %s to %s\n", + dentry->d_name.name, silly); + + sdentry = nfs_silly_lookup(dentry->d_parent, silly, slen); + /* + * N.B. Better to return EBUSY here ... it could be + * dangerous to delete the file while it's in use. + */ + if (IS_ERR(sdentry)) + goto out; + } while(sdentry->d_inode != NULL); /* need negative lookup */ + + nfs_zap_caches(dir_i); + qsilly.name = silly; + qsilly.len = strlen(silly); + error = NFS_CALL(rename, dir_i, (dir, &dir_attr, &dentry->d_name, + dir, &dir_attr, &qsilly)); + nfs_refresh_inode(dir_i, &dir_attr); + if (!error) { + nfs_renew_times(dentry); + d_move(dentry, sdentry); + error = nfs_async_unlink(dentry); + /* If we return 0 we don't unlink */ + } + dput(sdentry); +out: + return error; +} + +/* + * Remove a file after making sure there are no pending writes, + * and after checking that the file has only one user. + * + * We update inode->i_nlink and free the inode prior to the operation + * to avoid possible races if the server reuses the inode. + */ +static int nfs_safe_remove(struct dentry *dentry) +{ + struct nfs_fattr dir_attr; + struct dentry *dir = dentry->d_parent; + struct inode *dir_i = dir->d_inode, + *inode = dentry->d_inode; + int error, rehash = 0; + + /* + * Unhash the dentry while we remove the file ... + */ + if (!list_empty(&dentry->d_hash)) { + d_drop(dentry); + rehash = 1; + } + + dfprintk(VFS, "NFS: safe_remove(%s/%s)\n", + dentry->d_parent->d_name.name, dentry->d_name.name); + + /* N.B. not needed now that d_delete is done in advance? */ + error = -EBUSY; + if (!inode) { +#ifdef NFS_PARANOIA + printk(KERN_ERR "nfs_safe_remove: %s/%s already negative??\n", + dentry->d_parent->d_name.name, dentry->d_name.name); +#endif + } + + if (dentry->d_count > 1) { +#ifdef NFS_PARANOIA + printk(KERN_INFO "nfs_safe_remove: %s/%s busy, d_count=%d\n", + dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); +#endif + goto out; + } + + nfs_zap_caches(dir_i); + if (inode) + NFS_CACHEINV(inode); + error = NFS_CALL(remove, dir_i, (dir, &dir_attr, &dentry->d_name)); + nfs_refresh_inode(dir_i, &dir_attr); + if (error < 0) + goto out; + + /* + * Free the inode + */ + d_delete(dentry); +out: + /* + * Rehash the negative dentry if the operation succeeded. + */ + if (rehash) + d_rehash(dentry); + return error; +} + +/* We do silly rename. In case sillyrename() returns -EBUSY, the inode + * belongs to an active ".nfs..." file and we return -EBUSY. + * + * If sillyrename() returns 0, we do nothing, otherwise we unlink. + */ +static int nfs_unlink(struct inode *dir, struct dentry *dentry) +{ + int error; + + dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n", + dir->i_dev, dir->i_ino, dentry->d_name.name); + + if (dentry->d_inode) + nfs_wb_all(dentry->d_inode); + error = nfs_sillyrename(dir, dentry); + if (error && error != -EBUSY) { + error = nfs_safe_remove(dentry); + if (!error) { + nfs_renew_times(dentry); + } + } + return error; +} + +static int +nfs_symlink(struct inode *dir_i, struct dentry *dentry, const char *symname) +{ + struct dentry *dir = dentry->d_parent; + struct nfs_fattr dir_attr, sym_attr; + struct nfs_fh sym_fh; + struct iattr attr; + struct qstr qsymname; + int error, mode, maxlen; + + dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n", + dir_i->i_dev, dir_i->i_ino, dentry->d_name.name, symname); + + error = -ENAMETOOLONG; + maxlen = (NFS_PROTO(dir_i)->version==2) ? NFS2_MAXPATHLEN : NFS3_MAXPATHLEN; + if (strlen(symname) > maxlen) + goto out; + +#ifdef NFS_PARANOIA + if (dentry->d_inode) + printk(KERN_WARNING "nfs_proc_symlink: %s/%s not negative!\n", + dentry->d_parent->d_name.name, dentry->d_name.name); +#endif + /* + * Fill in the sattr for the call. + + * Note: SunOS 4.1.2 crashes if the mode isn't initialized! + */ +#ifdef NFSD_BROKEN_UID + attr.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID; + attr.ia_mode = mode = S_IFLNK | S_IRWXUGO; + attr.ia_uid = current->fsuid; + attr.ia_gid = current->fsgid; +#else + attr.ia_valid = ATTR_MODE; + attr.ia_mode = mode = S_IFLNK | S_IRWXUGO; +#endif + + + qsymname.name = symname; + qsymname.len = strlen(symname); + + nfs_zap_caches(dir_i); + error = NFS_CALL(symlink, dir_i, (dir, &dir_attr, + &dentry->d_name, &qsymname, &attr, + &sym_fh, &sym_attr)); + nfs_refresh_inode(dir_i, &dir_attr); + if (!error && sym_fh.size != 0 && (sym_attr.valid & NFS_ATTR_FATTR)) { + error = nfs_instantiate(dentry, &sym_fh, &sym_attr); + } else { + if (error == -EEXIST) + printk(KERN_INFO "nfs_proc_symlink: %s/%s already exists??\n", + dentry->d_parent->d_name.name, + dentry->d_name.name); + d_drop(dentry); + } + +out: + return error; +} + +static int +nfs_link(struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry) +{ + struct dentry *dir = dentry->d_parent; + struct inode *inode = old_dentry->d_inode; + struct nfs_fattr old_attr, dir_attr; + int error; + + dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)\n", + old_dentry->d_parent->d_name.name, old_dentry->d_name.name, + dentry->d_parent->d_name.name, dentry->d_name.name); + + /* + * Drop the dentry in advance to force a new lookup. + * Since nfs_proc_link doesn't return a file handle, + * we can't use the existing dentry. + */ + d_drop(dentry); + nfs_zap_caches(dir_i); + NFS_CACHEINV(inode); + error = NFS_CALL(link, inode, (old_dentry, &old_attr, + dir, &dir_attr, &dentry->d_name)); + nfs_refresh_inode(inode, &old_attr); + nfs_refresh_inode(dir_i, &dir_attr); + return error; +} + +/* + * RENAME + * FIXME: Some nfsds, like the Linux user space nfsd, may generate a + * different file handle for the same inode after a rename (e.g. when + * moving to a different directory). A fail-safe method to do so would + * be to look up old_dir/old_name, create a link to new_dir/new_name and + * rename the old file using the sillyrename stuff. This way, the original + * file in old_dir will go away when the last process iput()s the inode. + * + * FIXED. + * + * It actually works quite well. One needs to have the possibility for + * at least one ".nfs..." file in each directory the file ever gets + * moved or linked to which happens automagically with the new + * implementation that only depends on the dcache stuff instead of + * using the inode layer + * + * Unfortunately, things are a little more complicated than indicated + * above. For a cross-directory move, we want to make sure we can get + * rid of the old inode after the operation. This means there must be + * no pending writes (if it's a file), and the use count must be 1. + * If these conditions are met, we can drop the dentries before doing + * the rename. + * + * FIXME: Sun seems to take this even one step further. The connectathon + * test suite has a file that renames open file A to open file B, + * and expects a silly rename to happen for B. + */ +static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct nfs_fattr old_attr, new_attr; + struct inode * old_inode = old_dentry->d_inode; + struct inode * new_inode = new_dentry->d_inode; + struct dentry * dentry = NULL, *rehash = NULL; + int error; + + /* + * To prevent any new references to the target during the rename, + * we unhash the dentry and free the inode in advance. + */ + if (!list_empty(&new_dentry->d_hash)) { + d_drop(new_dentry); + rehash = new_dentry; + } + + dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n", + old_dentry->d_parent->d_name.name, old_dentry->d_name.name, + new_dentry->d_parent->d_name.name, new_dentry->d_name.name, + new_dentry->d_count); + + /* + * First check whether the target is busy ... we can't + * safely do _any_ rename if the target is in use. + * + * For files, make a copy of the dentry and then do a + * silly-rename. If the silly-rename succeeds, the + * copied dentry is hashed and becomes the new target. + * + * With directories check is done in VFS. + */ + error = -EBUSY; + if (new_inode) + nfs_wb_all(new_inode); + if (new_dentry->d_count > 1 && new_inode) { + int err; + /* copy the target dentry's name */ + dentry = d_alloc(new_dentry->d_parent, + &new_dentry->d_name); + if (!dentry) + goto out; + + /* silly-rename the existing target ... */ + err = nfs_sillyrename(new_dir, new_dentry); + if (!err) { + new_dentry = rehash = dentry; + new_inode = NULL; + /* hash the replacement target */ + d_instantiate(new_dentry, NULL); + } + + /* dentry still busy? */ + if (new_dentry->d_count > 1) { +#ifdef NFS_PARANOIA + printk(KERN_INFO "nfs_rename: target %s/%s busy, d_count=%d\n", + new_dentry->d_parent->d_name.name, + new_dentry->d_name.name,new_dentry->d_count); +#endif + goto out; + } + } + + /* + * ... prune child dentries and writebacks if needed. + */ + if (old_dentry->d_count > 1) { + nfs_wb_all(old_inode); + shrink_dcache_parent(old_dentry); + } + + if (new_dentry->d_count > 1 && new_inode) { +#ifdef NFS_PARANOIA + printk(KERN_INFO "nfs_rename: new dentry %s/%s busy, d_count=%d\n", + new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count); +#endif + goto out; + } + + if (new_inode) + d_delete(new_dentry); + + nfs_zap_caches(new_dir); + nfs_zap_caches(old_dir); + error = NFS_CALL(rename, old_dir, + (old_dentry->d_parent, &old_attr, &old_dentry->d_name, + new_dentry->d_parent, &new_attr, &new_dentry->d_name)); + nfs_refresh_inode(old_dir, &old_attr); + nfs_refresh_inode(new_dir, &new_attr); + +out: + /* Update the dcache if needed */ + if (rehash) + d_rehash(rehash); + if (!error && !S_ISDIR(old_inode->i_mode)) + d_move(old_dentry, new_dentry); + /* new dentry created? */ + if (dentry) + dput(dentry); + return error; +} + +int +nfs_permission(struct inode *i, int msk) +{ + struct nfs_fattr fattr; + struct dentry *de = NULL; + int err = vfs_permission(i, msk); + struct list_head *start, *tmp; + + if (!NFS_PROTO(i)->access) + goto out; + /* + * Trust UNIX mode bits except: + * + * 1) When override capabilities may have been invoked + * 2) When root squashing may be involved + * 3) When ACLs may overturn a negative answer */ + if (!capable(CAP_DAC_OVERRIDE) && !capable(CAP_DAC_READ_SEARCH) + && (current->fsuid != 0) && (current->fsgid != 0) + && err != -EACCES) + goto out; + + tmp = start = &i->i_dentry; + while ((tmp = tmp->next) != start) { + de = list_entry(tmp, struct dentry, d_alias); + if (de->d_inode == i) + break; + } + if (!de || de->d_inode != i) + return 0; + + err = NFS_CALL(access, i, (de, msk, &fattr, 0)); + if (err == -EACCES && (current->fsuid != current->uid || + current->fsgid != current->gid)) + err = NFS_CALL(access, i, (de, msk, &fattr, 1)); + nfs_refresh_inode(i, &fattr); + out: + return err; +} + +/* + * Local variables: + * version-control: t + * kept-new-versions: 5 + * End: + */ diff -u --recursive --new-file linux-2.2.17pre19-nfsv3-0.22.3/fs/nfs/inode.c linux-2.2.17-nfsv3/fs/nfs/inode.c --- linux-2.2.17pre19-nfsv3-0.22.3/fs/nfs/inode.c Mon Aug 21 01:26:07 2000 +++ linux-2.2.17-nfsv3/fs/nfs/inode.c Mon Sep 4 15:50:50 2000 @@ -274,8 +274,9 @@ struct nfs_server *server; struct rpc_xprt *xprt = 0; struct rpc_clnt *clnt = 0; - struct nfs_dentry *root_d_fsdata = NULL; - struct nfs_fh *root = &data->root, *root_fh, fh; + struct nfs_fh *root_fh = NULL, + *root = &data->root, + fh; struct inode *root_inode = NULL; unsigned int authflavor; struct sockaddr_in srvaddr; @@ -418,19 +419,17 @@ * Keep the super block locked while we try to get * the root fh attributes. */ - root_d_fsdata = nfs_fh_alloc(); - if (!root_d_fsdata) + root_fh = nfs_fh_alloc(); + if (!root_fh) goto out_no_fh; - - root_fh = &root_d_fsdata->fh; - memcpy((u8*)root_fh, (u8*)root, sizeof(*root)); + memcpy((u8*)root_fh, (u8*)root, sizeof(*root_fh)); /* Did getting the root inode fail? */ if ((root->size > NFS_SB_FHSIZE(sb) || ! (root_inode = nfs_get_root(sb, root))) && (data->flags & NFS_MOUNT_VER3)) { data->flags &= ~NFS_MOUNT_VER3; - nfs_fh_free(root_d_fsdata); + nfs_fh_free(root_fh); rpciod_down(); rpc_shutdown_client(server->client); goto nfsv3_try_again; @@ -442,7 +441,7 @@ goto failure_put_root; sb->s_root->d_op = &nfs_dentry_operations; - sb->s_root->d_fsdata = root_d_fsdata; + sb->s_root->d_fsdata = root_fh; sb->u.nfs_sb.s_root = root_fh; /* Get some general file system info */ @@ -465,10 +464,6 @@ if (data->wsize == 0) server->wsize = nfs_block_size(fsinfo.wtpref, NULL); - server->dtsize = nfs_block_size(fsinfo.dtpref, NULL); - if (server->dtsize > PAGE_CACHE_SIZE) - server->dtsize = PAGE_CACHE_SIZE; - /* NFSv3: we don't have bsize, but rather rtmult and wtmult... */ if (!fsinfo.bsize) fsinfo.bsize = (fsinfo.rtmult>fsinfo.wtmult) ? fsinfo.rtmult : fsinfo.wtmult; @@ -497,6 +492,12 @@ server->wsize = server->wpages << PAGE_CACHE_SHIFT; } + server->dtsize = nfs_block_size(fsinfo.dtpref, NULL); + if (server->dtsize > PAGE_CACHE_SIZE) + server->dtsize = PAGE_CACHE_SIZE; + if (server->dtsize > server->rsize) + server->dtsize = server->rsize; + maxlen = (version == 2) ? NFS2_MAXNAMLEN : NFS3_MAXNAMLEN; if (server->namelen == 0 || server->namelen > maxlen) @@ -518,8 +519,8 @@ failure_put_root: if (root_inode) iput(root_inode); - if (root_d_fsdata) - nfs_fh_free(root_d_fsdata); + if (root_fh) + nfs_fh_free(root_fh); out_no_fh: rpciod_down(); diff -u --recursive --new-file linux-2.2.17pre19-nfsv3-0.22.3/fs/nfs/nfs3proc.c linux-2.2.17-nfsv3/fs/nfs/nfs3proc.c --- linux-2.2.17pre19-nfsv3-0.22.3/fs/nfs/nfs3proc.c Mon Aug 21 01:26:07 2000 +++ linux-2.2.17-nfsv3/fs/nfs/nfs3proc.c Mon Sep 4 15:10:25 2000 @@ -256,10 +256,10 @@ static int nfs3_proc_remove(struct dentry *dir, struct nfs_fattr *dir_attr, - struct qstr *name, struct rpc_cred *cred) + struct qstr *name) { struct nfs3_diropargs arg = { NFS_FH(dir), name->name, name->len }; - struct rpc_message msg = {NFS3PROC_REMOVE, &arg, dir_attr, cred }; + struct rpc_message msg = {NFS3PROC_REMOVE, &arg, dir_attr, NULL }; int status; dprintk("NFS call remove %s\n", name->name); @@ -270,6 +270,36 @@ } static int +nfs3_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name) +{ + struct nfs3_diropargs *arg; + struct nfs_fattr *res; + + arg = (struct nfs3_diropargs *)kmalloc(sizeof(*arg)+sizeof(*res), GFP_KERNEL); + if (!arg) + return -ENOMEM; + res = (struct nfs_fattr*)(arg + 1); + arg->fh = NFS_FH(dir); + arg->name = name->name; + arg->len = name->len; + msg->rpc_proc = NFS3PROC_REMOVE; + msg->rpc_argp = arg; + msg->rpc_resp = res; + return 0; +} + +static void +nfs3_proc_unlink_done(struct dentry *dir, struct rpc_message *msg) +{ + struct nfs_fattr *dir_attr = (struct nfs_fattr*)msg->rpc_resp; + + nfs_refresh_inode(dir->d_inode, dir_attr); + if (msg->rpc_argp) + kfree(msg->rpc_argp); +} + + +static int nfs3_proc_rename(struct dentry *old_dir, struct nfs_fattr *old_attr, struct qstr *old_name, struct dentry *new_dir, struct nfs_fattr *new_attr, @@ -461,6 +491,8 @@ NULL, /* commit */ nfs3_proc_create, nfs3_proc_remove, + nfs3_proc_unlink_setup, + nfs3_proc_unlink_done, nfs3_proc_rename, nfs3_proc_link, nfs3_proc_symlink, diff -u --recursive --new-file linux-2.2.17pre19-nfsv3-0.22.3/fs/nfs/proc.c linux-2.2.17-nfsv3/fs/nfs/proc.c --- linux-2.2.17pre19-nfsv3-0.22.3/fs/nfs/proc.c Mon Aug 21 01:26:07 2000 +++ linux-2.2.17-nfsv3/fs/nfs/proc.c Mon Sep 4 15:12:27 2000 @@ -239,11 +239,10 @@ } static int -nfs_proc_remove(struct dentry *dir, fattr *dir_attr, - qstr *name, struct rpc_cred *cred) +nfs_proc_remove(struct dentry *dir, fattr *dir_attr, qstr *name) { struct nfs_diropargs arg = { NFS_FH(dir), name->name, name->len }; - struct rpc_message msg = { NFSPROC_REMOVE, &arg, NULL, cred }; + struct rpc_message msg = { NFSPROC_REMOVE, &arg, NULL, NULL }; int status; dir_attr->valid = 0; @@ -255,6 +254,30 @@ } static int +nfs_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name) +{ + struct nfs_diropargs *arg; + + arg = (struct nfs_diropargs *)kmalloc(sizeof(*arg), GFP_KERNEL); + if (!arg) + return -ENOMEM; + arg->fh = NFS_FH(dir); + arg->name = name->name; + arg->len = name->len; + msg->rpc_proc = NFSPROC_REMOVE; + msg->rpc_argp = arg; + return 0; +} + +static void +nfs_proc_unlink_done(struct dentry *dir, struct rpc_message *msg) +{ + NFS_CACHEINV(dir->d_inode); + if (msg->rpc_argp) + kfree(msg->rpc_argp); +} + +static int nfs_proc_rename(struct dentry *old_dir, fattr *old_attr, qstr *old_name, struct dentry *new_dir, fattr *new_attr, qstr *new_name) { @@ -351,12 +374,8 @@ struct nfs_readdirargs arg; struct nfs_readdirres res; struct rpc_message msg = { NFSPROC_READDIR, &arg, &res, cred }; - struct nfs_server *server = NFS_DSERVER(dir); int status; - if (server->rsize < size) - size = server->rsize; - dir_attr->valid = 0; arg.fh = NFS_FH(dir); arg.cookie = cookie; @@ -399,6 +418,8 @@ NULL, /* commit */ nfs_proc_create, nfs_proc_remove, + nfs_proc_unlink_setup, + nfs_proc_unlink_done, nfs_proc_rename, nfs_proc_link, nfs_proc_symlink, diff -u --recursive --new-file linux-2.2.17pre19-nfsv3-0.22.3/fs/nfs/unlink.c linux-2.2.17-nfsv3/fs/nfs/unlink.c --- linux-2.2.17pre19-nfsv3-0.22.3/fs/nfs/unlink.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.17-nfsv3/fs/nfs/unlink.c Mon Sep 4 14:10:31 2000 @@ -0,0 +1,212 @@ +/* + * linux/fs/nfs/unlink.c + * + * nfs sillydelete handling + * + * NOTE: we rely on holding the BKL for list manipulation protection. + */ + +#include +#include +#include +#include +#include +#include + + +struct nfs_unlinkdata { + struct nfs_unlinkdata *next; + struct dentry *dir, *dentry; + struct qstr name; + struct rpc_task task; + struct rpc_cred *cred; + unsigned int count; +}; + +static struct nfs_unlinkdata *nfs_deletes = NULL; +static struct rpc_wait_queue nfs_delete_queue = RPC_INIT_WAITQ("nfs_delete_queue"); + +/** + * nfs_detach_unlinkdata - Remove asynchronous unlink from global list + * @data: pointer to descriptor + */ +static inline void +nfs_detach_unlinkdata(struct nfs_unlinkdata *data) +{ + struct nfs_unlinkdata **q; + + for (q = &nfs_deletes; *q != NULL; q = &((*q)->next)) { + if (*q == data) { + *q = data->next; + break; + } + } +} + +/** + * nfs_put_unlinkdata - release data from a sillydelete operation. + * @data: pointer to unlink structure. + */ +static void +nfs_put_unlinkdata(struct nfs_unlinkdata *data) +{ + if (--data->count == 0) { + nfs_detach_unlinkdata(data); + if (data->name.name != NULL) + kfree(data->name.name); + kfree(data); + } +} + +#define NAME_ALLOC_LEN(len) ((len+16) & ~15) +/** + * nfs_copy_dname - copy dentry name to data structure + * @dentry: pointer to dentry + * @data: nfs_unlinkdata + */ +static inline void +nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data) +{ + char *str; + int len = dentry->d_name.len; + + str = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL); + if (!str) + return; + memcpy(str, dentry->d_name.name, len); + if (!data->name.len) { + data->name.len = len; + data->name.name = str; + } else + kfree(str); +} + +/** + * nfs_async_unlink_init - Initialize the RPC info + * @task: rpc_task of the sillydelete + * + * We delay initializing RPC info until after the call to dentry_iput() + * in order to minimize races against rename(). + */ +static void +nfs_async_unlink_init(struct rpc_task *task) +{ + struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata; + struct dentry *dir = data->dir; + struct rpc_message msg; + int status = -ENOENT; + + if (!data->name.len) + goto out_err; + + memset(&msg, 0, sizeof(msg)); + msg.rpc_cred = data->cred; + status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name); + if (status < 0) + goto out_err; + rpc_call_setup(task, &msg, 0); + return; + out_err: + rpc_exit(task, status); +} + +/** + * nfs_async_unlink_done - Sillydelete post-processing + * @task: rpc_task of the sillydelete + * + * Do the directory attribute update. + */ +static void +nfs_async_unlink_done(struct rpc_task *task) +{ + struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata; + struct dentry *dir = data->dir; + struct inode *dir_i = dir->d_inode; + + nfs_zap_caches(dir_i); + NFS_PROTO(dir_i)->unlink_done(dir, &task->tk_msg); + rpcauth_releasecred(task->tk_auth, data->cred); + data->cred = NULL; + dput(dir); +} + +/** + * nfs_async_unlink_release - Release the sillydelete data. + * @task: rpc_task of the sillydelete + * + * We need to call nfs_put_unlinkdata as a 'tk_release' task since the + * rpc_task would be freed too. + */ +static void +nfs_async_unlink_release(struct rpc_task *task) +{ + struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata; + nfs_put_unlinkdata(data); +} + +/** + * nfs_async_unlink - asynchronous unlinking of a file + * @dir: directory in which the file resides. + * @name: name of the file to unlink. + */ +int +nfs_async_unlink(struct dentry *dentry) +{ + struct dentry *dir = dentry->d_parent; + struct nfs_unlinkdata *data; + struct rpc_task *task; + struct rpc_clnt *clnt = NFS_CLIENT(dir->d_inode); + int status = -ENOMEM; + + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) + goto out; + memset(data, 0, sizeof(*data)); + + data->dir = dget(dir); + data->dentry = dentry; + + data->next = nfs_deletes; + nfs_deletes = data; + data->count = 1; + + task = &data->task; + rpc_init_task(task, clnt, nfs_async_unlink_done , RPC_TASK_ASYNC); + task->tk_calldata = data; + task->tk_action = nfs_async_unlink_init; + task->tk_release = nfs_async_unlink_release; + + dentry->d_flags |= DCACHE_NFSFS_RENAMED; + data->cred = rpcauth_lookupcred(clnt->cl_auth, 0); + + rpc_sleep_on(&nfs_delete_queue, task, NULL, NULL); + status = 0; + out: + return status; +} + +/** + * nfs_complete_remove - Initialize completion of the sillydelete + * @dentry: dentry to delete + * + * Since we're most likely to be called by dentry_iput(), we + * only use the dentry to find the sillydelete. We then copy the name + * into the qstr. + */ +void +nfs_complete_unlink(struct dentry *dentry) +{ + struct nfs_unlinkdata *data; + + for(data = nfs_deletes; data != NULL; data = data->next) { + if (dentry == data->dentry) + break; + } + if (!data) + return; + data->count++; + nfs_copy_dname(dentry, data); + if (data->task.tk_rpcwait == &nfs_delete_queue) + rpc_wake_up_task(&data->task); + nfs_put_unlinkdata(data); +} diff -u --recursive --new-file linux-2.2.17pre19-nfsv3-0.22.3/include/linux/nfs_fs.h linux-2.2.17-nfsv3/include/linux/nfs_fs.h --- linux-2.2.17pre19-nfsv3-0.22.3/include/linux/nfs_fs.h Mon Aug 21 01:26:07 2000 +++ linux-2.2.17-nfsv3/include/linux/nfs_fs.h Mon Sep 4 15:17:26 2000 @@ -48,14 +48,13 @@ * superblock magic number for NFS */ #define NFS_SUPER_MAGIC 0x6969 -#define NFS_DENTRY_MAGIC 0x2439 #define NFS_FILE_MAGIC 0xA4F0 #ifdef __KERNEL__ /* * Convenience macros */ -#define NFS_FH(dentry) (&NFS_DENTRY(dentry)->fh) +#define NFS_FH(dentry) ((struct nfs_fh *) ((dentry)->d_fsdata)) #define NFS_DSERVER(dentry) (&(dentry)->d_sb->u.nfs_sb.s_server) #define NFS_SERVER(inode) (&(inode)->i_sb->u.nfs_sb.s_server) #define NFS_CLIENT(inode) (NFS_SERVER(inode)->client) @@ -90,12 +89,9 @@ #define NFS_FSID(inode) ((inode)->u.nfs_i.fsid) #define NFS_FILE(file) ((struct nfs_file *)(file)->private_data) -#define NFS_DENTRY(dentry) ((struct nfs_dentry*)(dentry)->d_fsdata) - /* Inode Flags */ #define NFS_USE_READDIRPLUS(inode) ((NFS_FLAGS(inode) & NFS_INO_ADVISE_RDPLUS) ? 1 : 0) -#define NFS_MONOTONE_COOKIES(inode) ((NFS_SERVER(inode)->flags & NFS_NONMONOTONE_COOKIES) ? 0 : 1) /* * These are the default flags for swap requests @@ -119,25 +115,6 @@ /* - * Structure for dentry->d_fsdata; - */ -struct nfs_dentry { - unsigned long magic; /* Magic number */ - struct nfs_fh fh; /* File handle */ - struct rpc_cred* cred; /* RPC Credentials */ -}; - -static inline int -nfs_check_dentry(struct dentry *dentry) -{ - if (NFS_DENTRY(dentry)->magic != NFS_DENTRY_MAGIC) { - printk(KERN_ERR "NFS: corrupt dentry structure!\n"); - return 0; - } - return 1; -} - -/* * Structure for file->private_data; */ struct nfs_file { @@ -196,8 +173,8 @@ */ extern struct inode_operations nfs_dir_inode_operations; extern struct dentry_operations nfs_dentry_operations; -extern struct nfs_dentry *nfs_fh_alloc(void); -extern void nfs_fh_free(struct nfs_dentry *); +extern struct nfs_fh *nfs_fh_alloc(void); +extern void nfs_fh_free(struct nfs_fh *); /* @@ -211,6 +188,12 @@ extern int nfs_lock(struct file *, int, struct file_lock *); /* + * linux/fs/nfs/unlink.c + */ +extern int nfs_async_unlink(struct dentry *); +extern void nfs_complete_unlink(struct dentry *); + +/* * linux/fs/nfs/write.c */ extern int nfs_writepage(struct file *, struct page *); @@ -348,16 +331,6 @@ } return NFS_FILE(file)->cred; } - -static __inline__ struct rpc_cred * -nfs_dentry_cred(struct dentry *dentry) -{ - - if (!NFS_DENTRY(dentry) || !nfs_check_dentry(dentry)) - return NULL; - return NFS_DENTRY(dentry)->cred; -} - /* NFS root */ diff -u --recursive --new-file linux-2.2.17pre19-nfsv3-0.22.3/include/linux/nfs_xdr.h linux-2.2.17-nfsv3/include/linux/nfs_xdr.h --- linux-2.2.17pre19-nfsv3-0.22.3/include/linux/nfs_xdr.h Mon Aug 21 01:26:07 2000 +++ linux-2.2.17-nfsv3/include/linux/nfs_xdr.h Mon Sep 4 15:14:20 2000 @@ -338,8 +338,10 @@ int (*create)(struct dentry *, struct nfs_fattr *, struct qstr *, struct iattr *, int flags, struct nfs_fh *, struct nfs_fattr *); - int (*remove)(struct dentry *, struct nfs_fattr *, - struct qstr *, struct rpc_cred *); + int (*remove)(struct dentry *, struct nfs_fattr *, struct qstr *); + int (*unlink_setup) (struct rpc_message *, + struct dentry *, struct qstr *); + void (*unlink_done) (struct dentry *, struct rpc_message *); int (*rename)(struct dentry *, struct nfs_fattr *, struct qstr *, struct dentry *, struct nfs_fattr *, struct qstr *); int (*link)(struct dentry *, struct nfs_fattr *, diff -u --recursive --new-file linux-2.2.17pre19-nfsv3-0.22.3/include/linux/sunrpc/sched.h linux-2.2.17-nfsv3/include/linux/sunrpc/sched.h --- linux-2.2.17pre19-nfsv3-0.22.3/include/linux/sunrpc/sched.h Mon Aug 21 01:26:07 2000 +++ linux-2.2.17-nfsv3/include/linux/sunrpc/sched.h Mon Aug 21 01:34:31 2000 @@ -42,6 +42,7 @@ #endif struct rpc_task * tk_next_task; /* global list of tasks */ struct rpc_task * tk_prev_task; /* global list of tasks */ + struct rpc_task * tk_parent; /* parent task */ struct rpc_clnt * tk_client; /* RPC client */ struct rpc_rqst * tk_rqstp; /* RPC request */ volatile int tk_status; /* result of last operation */ @@ -76,14 +77,14 @@ struct timer_list tk_timer; /* kernel timer */ struct wait_queue * tk_wait; /* sync: sleep on this q */ unsigned long tk_timeout; /* timeout for rpc_sleep() */ - unsigned short tk_flags; /* misc flags */ - unsigned short tk_lock; /* Task lock counter */ + unsigned int tk_flags; /* misc flags */ + unsigned int tk_lock; /* Task lock counter */ unsigned char tk_active : 1,/* Task has been activated */ tk_wakeup : 1;/* Task waiting to wake up */ volatile unsigned char tk_running : 1,/* Task is running */ tk_sleeping : 1;/* Task is truly asleep */ #ifdef RPC_DEBUG - unsigned short tk_pid; /* debugging aid */ + unsigned int tk_pid; /* debugging aid */ #endif }; #define tk_auth tk_client->cl_auth @@ -148,23 +149,16 @@ int rpc_add_wait_queue(struct rpc_wait_queue *, struct rpc_task *); void rpc_remove_wait_queue(struct rpc_task *); -void __rpc_sleep_on(struct rpc_wait_queue *, struct rpc_task *, - rpc_action action, rpc_action timer); void rpc_sleep_on(struct rpc_wait_queue *, struct rpc_task *, rpc_action action, rpc_action timer); void rpc_sleep_locked(struct rpc_wait_queue *, struct rpc_task *, rpc_action action, rpc_action timer); void rpc_add_timer(struct rpc_task *task, rpc_action timer); -void __rpc_wake_up_task(struct rpc_task *); void rpc_wake_up_task(struct rpc_task *); -void __rpc_wake_up(struct rpc_wait_queue *); void rpc_wake_up(struct rpc_wait_queue *); -struct rpc_task *__rpc_wake_up_next(struct rpc_wait_queue *); struct rpc_task *rpc_wake_up_next(struct rpc_wait_queue *); -void __rpc_wake_up_status(struct rpc_wait_queue *, int); void rpc_wake_up_status(struct rpc_wait_queue *, int); int __rpc_lock_task(struct rpc_task *); -void __rpc_unlock_task(struct rpc_task *); void rpc_unlock_task(struct rpc_task *); void rpc_delay(struct rpc_task *, unsigned long); void * rpc_allocate(unsigned int flags, unsigned int); diff -u --recursive --new-file linux-2.2.17pre19-nfsv3-0.22.3/include/linux/sunrpc/xprt.h linux-2.2.17-nfsv3/include/linux/sunrpc/xprt.h --- linux-2.2.17pre19-nfsv3-0.22.3/include/linux/sunrpc/xprt.h Mon Aug 21 01:26:07 2000 +++ linux-2.2.17-nfsv3/include/linux/sunrpc/xprt.h Mon Aug 21 01:39:09 2000 @@ -185,7 +185,9 @@ unsigned long); int xprt_reserve(struct rpc_task *); +int xprt_down_transmit(struct rpc_task *); void xprt_transmit(struct rpc_task *); +void xprt_up_transmit(struct rpc_task *); void xprt_receive(struct rpc_task *); int xprt_adjust_timeout(struct rpc_timeout *); void xprt_release(struct rpc_task *); @@ -205,6 +207,24 @@ { if (xprt_tcp_pending()) __rpciod_tcp_dispatcher(); +} + +static inline +int xprt_connected(struct rpc_xprt *xprt) +{ + return xprt->connected; +} + +static inline +void xprt_set_connected(struct rpc_xprt *xprt) +{ + xprt->connected = 1; +} + +static inline +void xprt_clear_connected(struct rpc_xprt *xprt) +{ + xprt->connected = 0; } #endif /* __KERNEL__*/ diff -u --recursive --new-file linux-2.2.17pre19-nfsv3-0.22.3/net/sunrpc/clnt.c linux-2.2.17-nfsv3/net/sunrpc/clnt.c --- linux-2.2.17pre19-nfsv3-0.22.3/net/sunrpc/clnt.c Mon Aug 21 01:26:07 2000 +++ linux-2.2.17-nfsv3/net/sunrpc/clnt.c Mon Sep 4 19:38:43 2000 @@ -21,6 +21,7 @@ * Copyright (C) 1995,1996 Olaf Kirch */ +#include #include #include @@ -78,6 +79,10 @@ xdr_init(); +#ifdef RPC_DEBUG + rpc_register_sysctl(); +#endif + if (!xprt) goto out; if (vers >= program->nrvers || !(version = program->version[vers])) @@ -503,7 +508,7 @@ struct rpc_clnt *clnt = task->tk_client; struct rpc_xprt *xprt = task->tk_xprt; - task->tk_action = (xprt->connected) ? call_transmit : call_reconnect; + task->tk_action = (xprt_connected(xprt)) ? call_transmit : call_reconnect; if (!clnt->cl_port) { task->tk_action = call_reconnect; @@ -659,7 +664,7 @@ else if (!clnt->cl_port) { task->tk_action = call_bind; clnt->cl_stats->rpcretrans++; - } else if (clnt->cl_xprt->stream && !clnt->cl_xprt->connected) { + } else if (clnt->cl_xprt->stream && !xprt_connected(clnt->cl_xprt)) { task->tk_action = call_reconnect; clnt->cl_stats->rpcretrans++; } else { @@ -700,6 +705,7 @@ if (!(p = call_verify(task))) return; +#ifdef CONFIG_SUNRPC_ABUSIVE_SUID /* * The following is an NFS-specific hack to cater for setuid * processes whose uid is mapped to nobody on the server. @@ -714,6 +720,7 @@ return; } } +#endif task->tk_action = NULL; diff -u --recursive --new-file linux-2.2.17pre19-nfsv3-0.22.3/net/sunrpc/sched.c linux-2.2.17-nfsv3/net/sunrpc/sched.c --- linux-2.2.17pre19-nfsv3-0.22.3/net/sunrpc/sched.c Mon Aug 21 01:26:07 2000 +++ linux-2.2.17-nfsv3/net/sunrpc/sched.c Wed Aug 23 13:03:19 2000 @@ -310,7 +310,7 @@ * NB: An RPC task will only receive interrupt-driven events as long * as it's on a wait queue. */ -void +static void __rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, rpc_action action, rpc_action timer) { @@ -378,7 +378,7 @@ * It would probably suffice to cli/sti the del_timer and remove_wait_queue * operations individually. */ -void +static inline void __rpc_wake_up_task(struct rpc_task *task) { dprintk("RPC: %4d __rpc_wake_up (now %ld inh %d)\n", @@ -401,6 +401,8 @@ return; __rpc_disable_timer(task); + if (task->tk_rpcwait != &schedq) + __rpc_remove_wait_queue(task); /* If the task has been locked, then set tk_wakeup so that * rpc_unlock_task() wakes us up... */ @@ -410,8 +412,6 @@ } else task->tk_wakeup = 0; - if (task->tk_rpcwait != &schedq) - __rpc_remove_wait_queue(task); __rpc_make_runnable(task); dprintk("RPC: __rpc_wake_up done\n"); @@ -448,7 +448,7 @@ /* * Wake up the next task on the wait queue. */ -struct rpc_task * +static inline struct rpc_task * __rpc_wake_up_next(struct rpc_wait_queue *queue) { struct rpc_task *task; @@ -476,19 +476,13 @@ * Wake up all tasks on a queue */ void -__rpc_wake_up(struct rpc_wait_queue *queue) -{ - while (queue->task) - __rpc_wake_up_task(queue->task); -} - -void rpc_wake_up(struct rpc_wait_queue *queue) { unsigned long oldflags; spin_lock_irqsave(&rpc_queue_lock, oldflags); - __rpc_wake_up(queue); + while (queue->task) + __rpc_wake_up_task(queue->task); spin_unlock_irqrestore(&rpc_queue_lock, oldflags); } @@ -496,23 +490,16 @@ * Wake up all tasks on a queue, and set their status value. */ void -__rpc_wake_up_status(struct rpc_wait_queue *queue, int status) +rpc_wake_up_status(struct rpc_wait_queue *queue, int status) { struct rpc_task *task; + unsigned long oldflags; + spin_lock_irqsave(&rpc_queue_lock, oldflags); while ((task = queue->task) != NULL) { task->tk_status = status; __rpc_wake_up_task(task); } -} - -void -rpc_wake_up_status(struct rpc_wait_queue *queue, int status) -{ - unsigned long oldflags; - - spin_lock_irqsave(&rpc_queue_lock, oldflags); - __rpc_wake_up_status(queue, status); spin_unlock_irqrestore(&rpc_queue_lock, oldflags); } @@ -531,7 +518,7 @@ return 0; } -void +static inline void __rpc_unlock_task(struct rpc_task *task) { if (task->tk_lock && !--task->tk_lock && task->tk_wakeup) @@ -968,14 +955,18 @@ * parent task may already have gone away */ static inline struct rpc_task * -rpc_find_parent(struct rpc_task *child) +__rpc_find_parent(struct rpc_task *child) { - struct rpc_task *temp, *parent; + struct rpc_task *head, *parent; - parent = (struct rpc_task *) child->tk_calldata; - for (temp = childq.task; temp; temp = temp->tk_next) { - if (temp == parent) - return parent; + parent = child->tk_parent; + if ((head = childq.task) != NULL) { + struct rpc_task *task = head; + do { + if (task == parent) + return parent; + task = task->tk_next; + } while (task != head); } return NULL; } @@ -987,7 +978,7 @@ unsigned long oldflags; spin_lock_irqsave(&rpc_queue_lock, oldflags); - if ((parent = rpc_find_parent(child)) != NULL) { + if ((parent = __rpc_find_parent(child)) != NULL) { parent->tk_status = child->tk_status; __rpc_wake_up_task(parent); } @@ -1006,7 +997,7 @@ if (!task) goto fail; task->tk_exit = rpc_child_exit; - task->tk_calldata = parent; + task->tk_parent = parent; return task; fail: diff -u --recursive --new-file linux-2.2.17pre19-nfsv3-0.22.3/net/sunrpc/xprt.c linux-2.2.17-nfsv3/net/sunrpc/xprt.c --- linux-2.2.17pre19-nfsv3-0.22.3/net/sunrpc/xprt.c Mon Aug 21 01:26:08 2000 +++ linux-2.2.17-nfsv3/net/sunrpc/xprt.c Thu Aug 31 16:38:24 2000 @@ -412,7 +412,7 @@ } __xprt_disable_tcp_timer(xprt); - xprt->connected = 0; + xprt_clear_connected(xprt); __xprt_remove_pending(xprt); rpc_wake_up_status(&xprt->pending, -ENOTCONN); @@ -445,9 +445,8 @@ __xprt_disconnect(struct rpc_xprt *xprt) { dprintk("RPC: disconnected transport %p\n", xprt); - xprt->connected = 0; + xprt_clear_connected(xprt); __xprt_append_pending(xprt); - rpc_wake_up_status(&xprt->pending, -ENOTCONN); } /* @@ -462,7 +461,7 @@ int status; dprintk("RPC: %4d xprt_reconnect %p connected %d\n", - task->tk_pid, xprt, xprt->connected); + task->tk_pid, xprt, xprt_connected(xprt)); if (xprt->shutdown) return; @@ -519,10 +518,10 @@ } dprintk("RPC: %4d connect status %d connected %d\n", - task->tk_pid, status, xprt->connected); + task->tk_pid, status, xprt_connected(xprt)); start_bh_atomic(); - if (!xprt->connected) { + if (!xprt_connected(xprt)) { task->tk_timeout = xprt->timeout.to_maxval; rpc_sleep_on(&xprt->reconn, task, xprt_reconn_status, NULL); end_bh_atomic(); @@ -540,17 +539,13 @@ } /* - * Reconnect timeout. We just mark the transport as not being in the - * process of reconnecting, and leave the rest to the upper layers. + * Reconnect done */ static void xprt_reconn_status(struct rpc_task *task) { struct rpc_xprt *xprt = task->tk_xprt; - dprintk("RPC: %4d xprt_reconn_timeout %d\n", - task->tk_pid, task->tk_status); - xprt->connecting = 0; rpc_wake_up(&xprt->reconn); } @@ -849,7 +844,7 @@ if (xprt->shutdown) return -EIO; - if (!xprt->connected) + if (!xprt_connected(xprt)) return -ENOTCONN; /* Read in a new fragment marker if necessary */ @@ -956,15 +951,13 @@ while ((xprt = xprt_remove_pending_next()) != NULL) { dprintk("rpciod_tcp_dispatcher: Processing %p\n", xprt); - if (!xprt->connected && !xprt->connecting) { - xprt_close(xprt); - continue; - } - do { result = tcp_input_record(xprt); } while (result >= 0); + if (!xprt_connected(xprt) && !xprt->connecting) + xprt_close(xprt); + if (safe_retry++ > 200) { schedule(); safe_retry = 0; @@ -996,7 +989,7 @@ dprintk("RPC: tcp_data_ready client %p\n", xprt); dprintk("RPC: state %x conn %d dead %d zapped %d\n", - sk->state, xprt->connected, + sk->state, xprt_connected(xprt), sk->dead, sk->zapped); wake: wake_up_interruptible(sk->sleep); @@ -1012,12 +1005,12 @@ goto wake; dprintk("RPC: tcp_state_change client %p...\n", xprt); dprintk("RPC: state %x conn %d dead %d zapped %d\n", - sk->state, xprt->connected, + sk->state, xprt_connected(xprt), sk->dead, sk->zapped); switch (sk->state) { case TCP_ESTABLISHED: - xprt->connected = 1; + xprt_set_connected(xprt); if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->sending) rpc_wake_up_task(xprt->snd_task); rpc_wake_up(&xprt->reconn); @@ -1117,7 +1110,7 @@ * Serialize access to sockets, in order to prevent different * requests from interfering with each other. */ -static int +int xprt_down_transmit(struct rpc_task *task) { struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt; @@ -1143,7 +1136,7 @@ /* * Releases the socket for use by other requests. */ -static inline void +void xprt_up_transmit(struct rpc_task *task) { struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt; @@ -1165,13 +1158,12 @@ struct rpc_rqst *req = task->tk_rqstp; struct rpc_xprt *xprt = req->rq_xprt; - dprintk("RPC: %4d xprt_transmit(%x)\n", task->tk_pid, - *(u32 *)(req->rq_svec[0].iov_base)); + dprintk("RPC: %4d xprt_transmit(%x)\n", task->tk_pid, req->rq_xid); if (xprt->shutdown) task->tk_status = -EIO; - if (!xprt->connected) + if (!xprt_connected(xprt)) task->tk_status = -ENOTCONN; if (task->tk_status < 0) @@ -1271,13 +1263,13 @@ spin_unlock_irqrestore(&xprt_sock_lock, oldflags); return; case -EAGAIN: - /* Keep holding the socket if it is blocked */ - rpc_delay(task, HZ>>4); - return; case -ECONNREFUSED: case -ENOTCONN: - if (!xprt->stream) + /* Keep holding the socket if it is blocked */ + if (!xprt->stream) { + rpc_delay(task, HZ>>4); return; + } default: goto out_release; } @@ -1553,12 +1545,12 @@ if (xprt->prot == IPPROTO_UDP) { sk->data_ready = udp_data_ready; sk->write_space = udp_write_space; - xprt->connected = 1; + xprt_set_connected(xprt); } else { sk->data_ready = tcp_data_ready; sk->state_change = tcp_state_change; sk->write_space = tcp_write_space; - xprt->connected = 0; + xprt_clear_connected(xprt); } /* Reset to new socket */