/* * A check for whether or not the parent directory has changed. * In the case it has, we assume that the dentries are untrustworthy * and may need to be looked up again. */ static inline int nfs_check_verifier(struct inode *dir, struct dentry *dentry) { if (IS_ROOT(dentry)) return 1; if ((NFS_FLAGS(dir) & NFS_INO_INVALID_ATTR) != 0 || nfs_attribute_timeout(dir)) return 0; return nfs_verify_change_attribute(dir, (unsigned long)dentry->d_fsdata); }
/* 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; struct rpc_cred *cred = nfs_file_cred(file); unsigned long timestamp; int error; dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->index); again: timestamp = jiffies; error = NFS_PROTO(inode)->readdir(file->f_dentry, cred, desc->entry->cookie, page, NFS_SERVER(inode)->dtsize, desc->plus); if (error < 0) { /* We requested READDIRPLUS, but the server doesn't grok it */ if (error == -ENOTSUPP && desc->plus) { NFS_SERVER(inode)->caps &= ~NFS_CAP_READDIRPLUS; NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS; desc->plus = 0; goto again; } goto error; } SetPageUptodate(page); NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME; /* 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->i_mapping); NFS_I(inode)->readdir_timestamp = timestamp; } unlock_page(page); return 0; error: SetPageError(page); unlock_page(page); nfs_zap_caches(inode); desc->error = error; 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; }
/* * 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 inode *inode = file->f_dentry->d_inode; struct rpc_cred *cred = nfs_file_cred(file); struct page *page = NULL; int status; dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (long long)desc->target); page = alloc_page(GFP_HIGHUSER); if (!page) { status = -ENOMEM; goto out; } desc->error = NFS_PROTO(inode)->readdir(file->f_dentry, cred, desc->target, page, NFS_SERVER(inode)->dtsize, desc->plus); NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME; desc->page = page; desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ if (desc->error >= 0) { if ((status = dir_decode(desc)) == 0) desc->entry->prev_cookie = desc->target; } else status = -EIO; if (status < 0) goto out_release; 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->entry->cookie = desc->entry->prev_cookie = 0; desc->entry->eof = 0; out: dfprintk(VFS, "NFS: uncached_readdir() returns %d\n", status); return status; out_release: dir_page_release(desc); goto out; }
/* 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; struct rpc_cred *cred = nfs_file_cred(file); void *buffer = kmap(page); 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(inode, cred, desc->entry->cookie, buffer, NFS_SERVER(inode)->dtsize, desc->plus); /* We requested READDIRPLUS, but the server doesn't grok it */ if (desc->plus && error == -ENOTSUPP) { NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS; desc->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; }
/* * The "read_inode" function doesn't actually do anything: * the real data is filled in later in nfs_fhget. Here we * just mark the cache times invalid, and zero out i_mode * (the latter makes "nfs_refresh_inode" do the right thing * wrt pipe inodes) */ static void nfs_read_inode(struct inode * inode) { inode->i_blksize = inode->i_sb->s_blocksize; inode->i_mode = 0; inode->i_rdev = 0; /* We can't support UPDATE_ATIME(), since the server will reset it */ inode->i_flags |= S_NOATIME; NFS_FILEID(inode) = 0; NFS_FSID(inode) = 0; NFS_FLAGS(inode) = 0; INIT_LIST_HEAD(&inode->u.nfs_i.read); INIT_LIST_HEAD(&inode->u.nfs_i.dirty); INIT_LIST_HEAD(&inode->u.nfs_i.commit); INIT_LIST_HEAD(&inode->u.nfs_i.writeback); inode->u.nfs_i.nread = 0; inode->u.nfs_i.ndirty = 0; inode->u.nfs_i.ncommit = 0; inode->u.nfs_i.npages = 0; NFS_CACHEINV(inode); NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); NFS_ATTRTIMEO_UPDATE(inode) = jiffies; }
/* 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; }
int nfs_permission(struct inode *inode, int mask, struct nameidata *nd) { struct nfs_access_cache *cache = &NFS_I(inode)->cache_access; struct rpc_cred *cred; int mode = inode->i_mode; int res; if (mask == 0) return 0; if (mask & MAY_WRITE) { /* * * Nobody gets write access to a read-only fs. * */ if (IS_RDONLY(inode) && (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) return -EROFS; /* * * Nobody gets write access to an immutable file. * */ if (IS_IMMUTABLE(inode)) return -EACCES; } /* Are we checking permissions on anything other than lookup/execute? */ if ((mask & MAY_EXEC) == 0) { /* We only need to check permissions on file open() and access() */ if (!nd || !(nd->flags & (LOOKUP_OPEN|LOOKUP_ACCESS))) return 0; /* NFSv4 has atomic_open... */ if (NFS_PROTO(inode)->version > 3 && (nd->flags & LOOKUP_OPEN)) return 0; } lock_kernel(); if (!NFS_PROTO(inode)->access) goto out_notsup; cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); if (cache->cred == cred && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode)) && !(NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR)) { if (!(res = cache->err)) { /* Is the mask a subset of an accepted mask? */ if ((cache->mask & mask) == mask) goto out; } else { /* ...or is it a superset of a rejected mask? */ if ((cache->mask & mask) == cache->mask) goto out; } } res = NFS_PROTO(inode)->access(inode, cred, mask); if (!res || res == -EACCES) goto add_cache; out: put_rpccred(cred); unlock_kernel(); return res; out_notsup: nfs_revalidate_inode(NFS_SERVER(inode), inode); res = vfs_permission(inode, mask); unlock_kernel(); return res; add_cache: cache->jiffies = jiffies; if (cache->cred) put_rpccred(cache->cred); cache->cred = cred; cache->mask = mask; cache->err = res; unlock_kernel(); return res; }