diff options
Diffstat (limited to 'fs/nfs/dir.c')
-rw-r--r-- | fs/nfs/dir.c | 656 |
1 files changed, 292 insertions, 364 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 2d2ee4a02..f35ef3bdb 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -35,8 +35,6 @@ #define NFS_PARANOIA 1 /* #define NFS_DEBUG_VERBOSE 1 */ -static int nfs_safe_remove(struct dentry *); - 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); @@ -71,6 +69,70 @@ struct inode_operations nfs_dir_inode_operations = { }; 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 inode *inode = file->f_dentry->d_inode; + void *buffer = (void *)kmap(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->index); + + again: + error = NFS_PROTO(inode)->readdir(file, desc->entry->cookie, buffer, + NFS_SERVER(inode)->dtsize, plus); + /* 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; + SetPageUptodate(page); + kunmap(page); + /* Ensure consistent page alignment of the data. + * Note: assumes we have exclusive access to this mapping either + * throught inode->i_sem or some other mechanism. + */ + if (page->index == 0) + invalidate_inode_pages(inode); + UnlockPage(page); + return 0; + error: + SetPageError(page); + kunmap(page); + UnlockPage(page); + invalidate_inode_pages(inode); + desc->error = error; + return -EIO; +} /* * Given a pointer to a buffer that has already been filled by a call @@ -81,309 +143,217 @@ typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int); * read. */ static inline -long find_dirent(struct page *page, loff_t offset, - struct nfs_entry *entry, - decode_dirent_t decode, int plus, int use_cookie) +int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page) { - u8 *p = (u8 *)kmap(page), - *start = p; - unsigned long base = page_offset(page), - pg_offset = 0; - int loop_count = 0; + struct nfs_entry *entry = desc->entry; + char *start = (char *)kmap(page), + *p = start; + int loop_count = 0, + status = 0; - if (!p) - return -EIO; for(;;) { - p = (u8*)decode((__u32*)p, entry, plus); - if (IS_ERR(p)) + p = (char *)desc->decode((u32*)p, entry, desc->plus); + if (IS_ERR(p)) { + status = PTR_ERR(p); break; - pg_offset = p - start; - entry->prev = entry->offset; - entry->offset = base + pg_offset; - if ((use_cookie ? entry->cookie : entry->offset) > offset) + } + 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(); } } - kunmap(page); - return (IS_ERR(p)) ? PTR_ERR(p) : (long)pg_offset; + 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. - * - * Returns -EIO if the page is not available, or up to date. */ static inline -long find_dirent_page(struct inode *inode, loff_t offset, - struct nfs_entry *entry) +int find_dirent_page(nfs_readdir_descriptor_t *desc) { - decode_dirent_t decode = NFS_PROTO(inode)->decode_dirent; + struct inode *inode = desc->file->f_dentry->d_inode; struct page *page; - unsigned long index = entry->offset >> PAGE_CACHE_SHIFT; - long status = -EIO; - int plus = NFS_USE_READDIRPLUS(inode), - use_cookie = NFS_MONOTONE_COOKIES(inode); - - dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", entry->offset & PAGE_CACHE_MASK); - - if (entry->page) - page_cache_release(entry->page); - - page = find_get_page(&inode->i_data, index); + unsigned long index = desc->page_index; + int status; - if (page && Page_Uptodate(page)) - status = find_dirent(page, offset, entry, decode, plus, use_cookie); + dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", desc->page_index); - /* NB: on successful return we will be holding the page */ - if (status < 0) { - entry->page = NULL; - if (page) - page_cache_release(page); - } else - entry->page = page; + if (desc->page) { + page_cache_release(desc->page); + desc->page = NULL; + } - dfprintk(VFS, "NFS: find_dirent_page() returns %ld\n", status); + page = read_cache_page(&inode->i_data, index, + (filler_t *)nfs_readdir_filler, desc); + if (IS_ERR(page)) { + status = PTR_ERR(page); + goto out; + } + if (!Page_Uptodate(page)) + goto read_error; + + /* 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; + read_error: + page_cache_release(page); + return -EIO; } - /* * 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 position 'offset'. - * The latter may either be an offset into the page cache, or (better) - * a cookie depending on whether we're interested in strictly following - * the RFC wrt. not assuming monotonicity of cookies or not. - * - * For most systems, the latter is more reliable since it naturally - * copes with holes in the directory. + * The target for the search is 'desc->target'. */ static inline -long search_cached_dirent_pages(struct inode *inode, loff_t offset, - struct nfs_entry *entry) +int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) { - long res = 0; + int res = 0; int loop_count = 0; - dfprintk(VFS, "NFS: search_cached_dirent_pages() searching for cookie %Ld\n", (long long)offset); + dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (long long)desc->target); for (;;) { - res = find_dirent_page(inode, offset, entry); - if (res == -EAGAIN) { - /* Align to beginning of next page */ - entry->offset &= PAGE_CACHE_MASK; - entry->offset += PAGE_CACHE_SIZE; - } + res = find_dirent_page(desc); if (res != -EAGAIN) break; + /* Align to beginning of next page */ + desc->page_offset = 0; + desc->page_index ++; if (loop_count++ > 200) { loop_count = 0; schedule(); } } - if (res < 0 && entry->page) { - page_cache_release(entry->page); - entry->page = NULL; - } - dfprintk(VFS, "NFS: search_cached_dirent_pages() returned %ld\n", res); - return res; -} - - -/* 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 inline -long try_to_get_dirent_page(struct file *file, struct inode *inode, - struct nfs_entry *entry) -{ - struct dentry *dir = file->f_dentry; - struct page *page; - __u32 *p; - unsigned long index = entry->offset >> PAGE_CACHE_SHIFT; - long res = 0; - unsigned int dtsize = NFS_SERVER(inode)->dtsize; - int plus = NFS_USE_READDIRPLUS(inode); - - dfprintk(VFS, "NFS: try_to_get_dirent_page() reading directory page @ index %ld\n", index); - - page = grab_cache_page(&inode->i_data, index); - - if (!page) { - res = -ENOMEM; - goto out; - } - - if (Page_Uptodate(page)) { - dfprintk(VFS, "NFS: try_to_get_dirent_page(): page already up to date.\n"); - goto unlock_out; - } - - p = (__u32 *)kmap(page); - - if (dtsize > PAGE_CACHE_SIZE) - dtsize = PAGE_CACHE_SIZE; - res = NFS_PROTO(inode)->readdir(dir, entry->cookie, p, dtsize, plus); - - kunmap(page); - - if (res < 0) - goto error; - if (PageError(page)) - ClearPageError(page); - SetPageUptodate(page); - - unlock_out: - UnlockPage(page); - page_cache_release(page); - out: - dfprintk(VFS, "NFS: try_to_get_dirent_page() returns %ld\n", res); + dfprintk(VFS, "NFS: readdir_search_pagecache() returned %d\n", res); return res; - error: - SetPageError(page); - goto unlock_out; } -/* Recover from a revalidation flush. The case here is that - * the inode for the directory got invalidated somehow, and - * all of our cached information is lost. In order to get - * a correct cookie for the current readdir request from the - * user, we must (re-)fetch all the older readdir page cache - * entries. - * - * Returns < 0 if some error occurs. +/* + * Once we've found the start of the dirent within a page: fill 'er up... */ -static inline -long refetch_to_readdir(struct file *file, struct inode *inode, - loff_t off, struct nfs_entry *entry) +static +int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, + filldir_t filldir) { - struct nfs_entry my_dirent, - *dirent = &my_dirent; - long res; - int plus = NFS_USE_READDIRPLUS(inode), - use_cookie = NFS_MONOTONE_COOKIES(inode), - loop_count = 0; - - dfprintk(VFS, "NFS: refetch_to_readdir() searching for cookie %Ld\n", (long long)off); - *dirent = *entry; - entry->page = NULL; - - for (res = 0;res >= 0;) { - if (loop_count++ > 200) { - loop_count = 0; - schedule(); - } + struct file *file = desc->file; + struct nfs_entry *entry = desc->entry; + char *start = (char *)kmap(desc->page), + *p = start + desc->page_offset; + unsigned long fileid; + int loop_count = 0, + res = 0; - /* Search for last cookie in page cache */ - res = search_cached_dirent_pages(inode, off, dirent); + dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)desc->target); - if (res >= 0) { - /* Cookie was found */ - if ((use_cookie?dirent->cookie:dirent->offset) > off) { - *entry = *dirent; - dirent->page = NULL; - break; + 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 ++; } - continue; - } - - if (dirent->page) - page_cache_release(dirent->page); - dirent->page = NULL; - - if (res != -EIO) { - *entry = *dirent; break; } - - /* Read in a new page */ - res = try_to_get_dirent_page(file, inode, dirent); - if (res == -EBADCOOKIE) { - memset(dirent, 0, sizeof(*dirent)); - nfs_zap_caches(inode); - res = 0; - } - /* We requested READDIRPLUS, but the server doesn't grok it */ - if (plus && res == -ENOTSUPP) { - NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS; - memset(dirent, 0, sizeof(*dirent)); - nfs_zap_caches(inode); - plus = 0; - res = 0; + desc->page_offset = p - start; + if (loop_count++ > 200) { + loop_count = 0; + schedule(); } } - if (dirent->page) - page_cache_release(dirent->page); + kunmap(desc->page); + page_cache_release(desc->page); + desc->page = NULL; - dfprintk(VFS, "NFS: refetch_to_readdir() returns %ld\n", res); + dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (long long)desc->target, res); return res; } /* - * Once we've found the start of the dirent within a page: fill 'er up... + * 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 -int nfs_do_filldir(struct file *file, struct inode *inode, - struct nfs_entry *entry, void *dirent, filldir_t filldir) +static inline +int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, + filldir_t filldir) { - decode_dirent_t decode = NFS_PROTO(inode)->decode_dirent; - struct page *page = entry->page; - __u8 *p, - *start; - unsigned long base = page_offset(page), - offset = entry->offset, - pg_offset, - fileid; - int plus = NFS_USE_READDIRPLUS(inode), - use_cookie = NFS_MONOTONE_COOKIES(inode), - loop_count = 0, - res = 0; - - dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ offset %ld\n", entry->offset); - pg_offset = offset & ~PAGE_CACHE_MASK; - start = (u8*)kmap(page); - p = start + pg_offset; + struct file *file = desc->file; + struct inode *inode = file->f_dentry->d_inode; + struct page *page = NULL; + 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; + } - for(;;) { - /* Note: entry->prev contains the offset of the start of the - * current dirent */ - fileid = nfs_fileid_to_ino_t(entry->ino); - if (use_cookie) - res = filldir(dirent, entry->name, entry->len, entry->prev_cookie, fileid); + page = page_cache_alloc(); + if (!page) { + status = -ENOMEM; + goto out; + } + p = (u32 *)kmap(page); + status = NFS_PROTO(inode)->readdir(file, desc->target, p, + NFS_SERVER(inode)->dtsize, 0); + if (status >= 0) { + p = desc->decode(p, desc->entry, 0); + if (IS_ERR(p)) + status = PTR_ERR(p); else - res = filldir(dirent, entry->name, entry->len, entry->prev, fileid); - if (res < 0) - break; - file->f_pos = (use_cookie) ? entry->cookie : entry->offset; - p = (u8*)decode((__u32*)p, entry, plus); - if (!p || IS_ERR(p)) - break; - pg_offset = p - start; - entry->prev = entry->offset; - entry->offset = base + pg_offset; - if (loop_count++ > 200) { - loop_count = 0; - schedule(); - } + desc->entry->prev_cookie = desc->target; } kunmap(page); - - dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ offset %ld; returning = %d\n", entry->offset, res); - return res; + 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 @@ -393,10 +363,9 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct dentry *dentry = filp->f_dentry; struct inode *inode = dentry->d_inode; - struct page *page; - struct nfs_entry my_entry, - *entry = &my_entry; - loff_t offset; + nfs_readdir_descriptor_t my_desc, + *desc = &my_desc; + struct nfs_entry my_entry; long res; res = nfs_revalidate(dentry); @@ -409,36 +378,41 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) * read from the last dirent to revalidate f_pos * itself. */ - memset(entry, 0, sizeof(*entry)); + memset(desc, 0, sizeof(*desc)); + memset(&my_entry, 0, sizeof(my_entry)); - offset = filp->f_pos; + desc->file = filp; + desc->target = filp->f_pos; + desc->entry = &my_entry; + desc->decode = NFS_PROTO(inode)->decode_dirent; - while(!entry->eof) { - res = search_cached_dirent_pages(inode, offset, entry); - - if (res < 0) { - if (entry->eof) - break; - res = refetch_to_readdir(filp, inode, offset, entry); - if (res < 0) + 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; - page = entry->page; - if (!page) - printk(KERN_ERR "NFS: Missing page...\n"); - res = nfs_do_filldir(filp, inode, entry, dirent, filldir); - page_cache_release(page); - entry->page = NULL; + res = nfs_do_filldir(desc, dirent, filldir); if (res < 0) { res = 0; break; } - offset = filp->f_pos; } - if (entry->page) - page_cache_release(entry->page); - if (res < 0 && res != -EBADCOOKIE) + if (desc->page) + page_cache_release(desc->page); + if (desc->error < 0) + return desc->error; + if (res < 0) return res; return 0; } @@ -583,26 +557,18 @@ out_bad: /* * 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) +static int 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) { - int error; - - dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; - /* Unhash it first */ - d_drop(dentry); - error = nfs_safe_remove(dentry); - if (error) - printk("NFS: can't silly-delete %s/%s, error=%d\n", - dentry->d_parent->d_name.name, - dentry->d_name.name, error); + /* Unhash it, so that ->d_iput() would be called */ + return 1; } + return 0; } @@ -627,36 +593,30 @@ static void nfs_dentry_release(struct dentry *dentry) nfs_fh_free(dentry->d_fsdata); } -struct dentry_operations nfs_dentry_operations = { - d_revalidate: nfs_lookup_revalidate, - d_delete: nfs_dentry_delete, - d_release: nfs_dentry_release, -}; - -#if 0 /* dead code */ -#ifdef NFS_PARANOIA /* - * Display all dentries holding the specified inode. + * Called when the dentry loses inode. + * We use it to clean up silly-renamed files. */ -static void show_dentry(struct list_head * dlist) +static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) { - struct list_head *tmp = dlist; - - while ((tmp = tmp->next) != dlist) { - struct dentry * dentry = list_entry(tmp, struct dentry, d_alias); - const char * unhashed = ""; - - if (d_unhashed(dentry)) - unhashed = "(unhashed)"; - - printk("show_dentry: %s/%s, d_count=%d%s\n", - dentry->d_parent->d_name.name, - dentry->d_name.name, dentry->d_count, - unhashed); + if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { + struct dentry *dir = dentry->d_parent; + struct inode *dir_i = dir->d_inode; + int error; + + nfs_zap_caches(dir_i); + NFS_CACHEINV(inode); + error = NFS_PROTO(dir_i)->remove(dir, &dentry->d_name); } + iput(inode); } -#endif /* NFS_PARANOIA */ -#endif /* 0 */ + +struct dentry_operations nfs_dentry_operations = { + d_revalidate: nfs_lookup_revalidate, + d_delete: nfs_dentry_delete, + d_release: nfs_dentry_release, + d_iput: nfs_dentry_iput, +}; static struct dentry *nfs_lookup(struct inode *dir_i, struct dentry * dentry) { @@ -715,7 +675,6 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, nfs_renew_times(dentry); error = 0; } - NFS_CACHEINV(dentry->d_parent->d_inode); return error; } @@ -809,7 +768,6 @@ static int nfs_mkdir(struct inode *dir_i, struct dentry *dentry, int mode) d_drop(dentry); #endif nfs_zap_caches(dir_i); - dir_i->i_nlink++; error = NFS_PROTO(dir_i)->mkdir(dir, &dentry->d_name, &attr, &fhandle, &fattr); if (!error && fhandle.size != 0) @@ -830,13 +788,6 @@ static int nfs_rmdir(struct inode *dir_i, struct dentry *dentry) nfs_zap_caches(dir_i); error = NFS_PROTO(dir_i)->rmdir(dir, &dentry->d_name); - /* Update i_nlink and invalidate dentry. */ - if (!error) { - d_drop(dentry); - if (dir_i->i_nlink) - dir_i->i_nlink--; - } - return error; } @@ -919,7 +870,7 @@ out: * 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 + * We invalidate the attribute cache 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) @@ -927,28 +878,12 @@ static int nfs_safe_remove(struct dentry *dentry) struct dentry *dir = dentry->d_parent; struct inode *dir_i = dir->d_inode; struct inode *inode = dentry->d_inode; - int error, rehash = 0; + int error = -EBUSY, rehash = 0; dfprintk(VFS, "NFS: safe_remove(%s/%s, %ld)\n", dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino); - /* N.B. not needed now that d_delete is done in advance? */ - error = -EBUSY; - if (!inode) { -#ifdef NFS_PARANOIA -printk("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("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; - } /* * Unhash the dentry while we remove the file ... */ @@ -956,24 +891,26 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); d_drop(dentry); rehash = 1; } + if (dentry->d_count > 1) { +#ifdef NFS_PARANOIA + printk("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); + NFS_CACHEINV(inode); error = NFS_PROTO(dir_i)->remove(dir, &dentry->d_name); if (error < 0) goto out; /* - * Update i_nlink and free the inode + * Free the inode */ - if (inode) { - if (inode->i_nlink) - inode->i_nlink --; - d_delete(dentry); - } - /* - * Rehash the negative dentry if the operation succeeded. - */ - if (rehash) - d_add(dentry, NULL); + d_delete(dentry); out: + if (rehash) + d_rehash(dentry); return error; } @@ -1067,14 +1004,8 @@ nfs_link(struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry) */ d_drop(dentry); nfs_zap_caches(dir_i); + NFS_CACHEINV(inode); error = NFS_PROTO(dir_i)->link(old_dentry, dir, &dentry->d_name); - if (!error) { - /* - * Update the link count immediately, as some apps - * (e.g. pine) test this after making a link. - */ - inode->i_nlink++; - } return error; } @@ -1107,8 +1038,17 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, { struct inode *old_inode = old_dentry->d_inode; struct inode *new_inode = new_dentry->d_inode; - struct dentry *dentry = NULL; - int error, rehash = 0; + struct dentry *dentry = NULL, *rehash = NULL; + int error = -EBUSY; + + /* + * To prevent any new references to the target during the rename, + * we unhash the dentry and free the inode in advance. + */ + if (!d_unhashed(new_dentry)) { + 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, @@ -1125,7 +1065,6 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, */ if (!new_inode) goto go_ahead; - error = -EBUSY; if (S_ISDIR(new_inode->i_mode)) goto out; else if (new_dentry->d_count > 1) { @@ -1139,10 +1078,10 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, /* silly-rename the existing target ... */ err = nfs_sillyrename(new_dir, new_dentry); if (!err) { - new_dentry = dentry; + new_dentry = rehash = dentry; new_inode = NULL; - /* hash the replacement target */ - d_add(new_dentry, NULL); + /* instantiate the replacement target */ + d_instantiate(new_dentry, NULL); } /* dentry still busy? */ @@ -1166,14 +1105,6 @@ go_ahead: shrink_dcache_parent(old_dentry); } - /* - * To prevent any new references to the target during the rename, - * we unhash the dentry and free the inode in advance. - */ - if (!d_unhashed(new_dentry)) { - d_drop(new_dentry); - rehash = 1; - } if (new_inode) d_delete(new_dentry); @@ -1183,15 +1114,12 @@ go_ahead: &old_dentry->d_name, new_dentry->d_parent, &new_dentry->d_name); - NFS_CACHEINV(old_dir); - NFS_CACHEINV(new_dir); - /* Update the dcache if needed */ +out: if (rehash) - d_add(new_dentry, NULL); + d_rehash(rehash); if (!error && !S_ISDIR(old_inode->i_mode)) d_move(old_dentry, new_dentry); -out: /* new dentry created? */ if (dentry) dput(dentry); |