/* * 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; int status; dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", desc->page_index); desc->plus = NFS_USE_READDIRPLUS(inode); page = read_cache_page(&inode->i_data, desc->page_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->page = page; desc->ptr = kmap(page); status = find_dirent(desc, page); if (status < 0) dir_page_release(desc); out: dfprintk(VFS, "NFS: find_dirent_page() returns %d\n", status); return status; read_error: page_cache_release(page); return -EIO; }
/* * Use the cached Readdirplus results in order to avoid a LOOKUP call * whenever we believe that the parent directory has not changed. * * We assume that any file creation/rename changes the directory mtime. * As this results in a page cache invalidation whenever it occurs, * we don't require any other tests for cache coherency. */ static int nfs_cached_lookup(struct inode *dir, struct dentry *dentry, struct nfs_fh *fh, struct nfs_fattr *fattr) { nfs_readdir_descriptor_t desc; struct nfs_server *server; struct nfs_entry entry; struct page *page; unsigned long timestamp; int res; if (!NFS_USE_READDIRPLUS(dir)) return -ENOENT; server = NFS_SERVER(dir); /* Don't use readdirplus unless the cache is stable */ if ((server->flags & NFS_MOUNT_NOAC) != 0 || nfs_caches_unstable(dir) || nfs_attribute_timeout(dir)) return -ENOENT; if ((NFS_FLAGS(dir) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA)) != 0) return -ENOENT; timestamp = NFS_I(dir)->readdir_timestamp; entry.fh = fh; entry.fattr = fattr; desc.decode = NFS_PROTO(dir)->decode_dirent; desc.entry = &entry; desc.page_index = 0; desc.plus = 1; for(;(page = find_get_page(dir->i_mapping, desc.page_index)); desc.page_index++) { res = -EIO; if (PageUptodate(page)) { void * kaddr = kmap_atomic(page, KM_USER0); desc.ptr = kaddr; res = find_dirent_name(&desc, page, dentry); kunmap_atomic(kaddr, KM_USER0); } page_cache_release(page); if (res == 0) goto out_found; if (res != -EAGAIN) break; } return -ENOENT; out_found: fattr->timestamp = timestamp; return 0; }
/* 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; struct nfs_fh fh; struct nfs_fattr fattr; long res; lock_kernel(); res = nfs_revalidate_inode(NFS_SERVER(inode), inode); if (res < 0) { unlock_kernel(); 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)); desc->file = filp; desc->target = filp->f_pos; desc->decode = NFS_PROTO(inode)->decode_dirent; desc->plus = NFS_USE_READDIRPLUS(inode); my_entry.cookie = my_entry.prev_cookie = 0; my_entry.eof = 0; my_entry.fh = &fh; my_entry.fattr = &fattr; desc->entry = &my_entry; while(!desc->entry->eof) { res = readdir_search_pagecache(desc); if (res == -EBADCOOKIE) { /* This means either end of directory */ if (desc->entry->cookie != desc->target) { /* Or that the server has 'lost' a cookie */ res = uncached_readdir(desc, dirent, filldir); if (res >= 0) continue; } res = 0; break; } if (res == -ETOOSMALL && desc->plus) { NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS; nfs_zap_caches(inode); desc->plus = 0; desc->entry->eof = 0; continue; } if (res < 0) break; res = nfs_do_filldir(desc, dirent, filldir); if (res < 0) { res = 0; break; } } unlock_kernel(); if (desc->error < 0) return desc->error; if (res < 0) return res; return 0; }