/* * Write to a file (through the page cache). */ static ssize_t nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { struct dentry * dentry = file->f_dentry; struct inode * inode = dentry->d_inode; ssize_t result; dfprintk(VFS, "nfs: write(%s/%s(%ld), %lu@%lu)\n", dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, (unsigned long) count, (unsigned long) *ppos); result = -EBUSY; if (IS_SWAPFILE(inode)) goto out_swapfile; result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry); if (result) goto out; result = count; if (!count) goto out; result = generic_file_write(file, buf, count, ppos); out: return result; out_swapfile: printk(KERN_INFO "NFS: attempt to write to active swap file!\n"); goto out; }
static void *nfs_follow_link(struct dentry *dentry, struct nameidata *nd) { struct inode *inode = dentry->d_inode; struct page *page; void *err = ERR_PTR(nfs_revalidate_inode(NFS_SERVER(inode), inode)); if (err) goto read_failed; page = read_cache_page(&inode->i_data, 0, (filler_t *)nfs_symlink_filler, inode); if (IS_ERR(page)) { err = page; goto read_failed; } if (!PageUptodate(page)) { err = ERR_PTR(-EIO); goto getlink_read_error; } nd_set_link(nd, kmap(page)); return page; getlink_read_error: page_cache_release(page); read_failed: nd_set_link(nd, err); return NULL; }
static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct inode *inode = NULL; int error = 0; /* Check that we are indeed trying to open this file */ if (!is_atomic_open(dir, nd)) goto no_open; if (dentry->d_name.len > NFS_SERVER(dir)->namelen) { error = -ENAMETOOLONG; goto out; } dentry->d_op = NFS_PROTO(dir)->dentry_ops; /* Let vfs_create() deal with O_EXCL */ if (nd->intent.open.flags & O_EXCL) goto no_entry; /* Open the file on the server */ lock_kernel(); /* Revalidate parent directory attribute cache */ nfs_revalidate_inode(NFS_SERVER(dir), dir); if (nd->intent.open.flags & O_CREAT) { nfs_begin_data_update(dir); inode = nfs4_atomic_open(dir, dentry, nd); nfs_end_data_update(dir); } else inode = nfs4_atomic_open(dir, dentry, nd); unlock_kernel(); if (IS_ERR(inode)) { error = PTR_ERR(inode); switch (error) { /* Make a negative dentry */ case -ENOENT: inode = NULL; break; /* This turned out not to be a regular file */ case -ELOOP: if (!(nd->intent.open.flags & O_NOFOLLOW)) goto no_open; /* case -EISDIR: */ /* case -EINVAL: */ default: goto out; } } no_entry: d_add(dentry, inode); nfs_renew_times(dentry); nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); out: BUG_ON(error > 0); return ERR_PTR(error); no_open: return nfs_lookup(dir, dentry, nd); }
/* * 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_revalidate_inode(NFS_SERVER(dir), dir)) return 0; return time_after(dentry->d_time, NFS_MTIME_UPDATE(dir)); }
static inline int nfs_lookup_verify_inode(struct inode *inode, int isopen) { struct nfs_server *server = NFS_SERVER(inode); if (isopen && !(server->flags & NFS_MOUNT_NOCTO)) return __nfs_revalidate_inode(server, inode); return nfs_revalidate_inode(server, inode); }
static inline int nfs_lookup_verify_inode(struct inode *inode, int flags) { struct nfs_server *server = NFS_SERVER(inode); /* * If we're interested in close-to-open cache consistency, * then we revalidate the inode upon lookup. */ if (!(server->flags & NFS_MOUNT_NOCTO) && !(flags & LOOKUP_CONTINUE)) NFS_CACHEINV(inode); return nfs_revalidate_inode(server, inode); }
static int nfs_file_mmap(struct file * file, struct vm_area_struct * vma) { struct dentry *dentry = file->f_dentry; int status; dfprintk(VFS, "nfs: mmap(%s/%s)\n", dentry->d_parent->d_name.name, dentry->d_name.name); status = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry); if (!status) status = generic_file_mmap(file, vma); return status; }
static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) { struct inode *inode = NULL; int error; struct nfs_fh fhandle; struct nfs_fattr fattr; 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)->namelen) goto out; error = -ENOMEM; dentry->d_op = NFS_PROTO(dir)->dentry_ops; lock_kernel(); /* Revalidate parent directory attribute cache */ nfs_revalidate_inode(NFS_SERVER(dir), dir); /* If we're doing an exclusive create, optimize away the lookup */ if (nfs_is_exclusive_create(dir, nd)) goto no_entry; error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr); if (error != 0) { error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); if (error == -ENOENT) goto no_entry; if (error != 0) goto out_unlock; } error = -EACCES; inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr); if (!inode) goto out_unlock; no_entry: error = 0; d_add(dentry, inode); nfs_renew_times(dentry); nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); out_unlock: unlock_kernel(); out: BUG_ON(error > 0); return ERR_PTR(error); }
static ssize_t nfs_file_read(struct file * file, char * buf, size_t count, loff_t *ppos) { struct dentry * dentry = file->f_dentry; ssize_t result; dfprintk(VFS, "nfs: read(%s/%s, %lu@%lu)\n", dentry->d_parent->d_name.name, dentry->d_name.name, (unsigned long) count, (unsigned long) *ppos); result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry); if (!result) result = generic_file_read(file, buf, count, ppos); return result; }
/* * Flush all dirty pages, and check for write errors. * */ static int nfs_file_flush(struct file *file, fl_owner_t id) { struct nfs_open_context *ctx = nfs_file_open_context(file); struct inode *inode = file->f_path.dentry->d_inode; int status; dfprintk(VFS, "nfs: flush(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); if ((file->f_mode & FMODE_WRITE) == 0) return 0; nfs_inc_stats(inode, NFSIOS_VFSFLUSH); /* Ensure that data+attribute caches are up to date after close() */ status = nfs_do_fsync(ctx, inode); if (!status) nfs_revalidate_inode(NFS_SERVER(inode), inode); return status; }
/* * Flush all dirty pages, and check for write errors. * */ static int nfs_file_flush(struct file *file) { struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data; struct inode *inode = file->f_dentry->d_inode; int status; dfprintk(VFS, "nfs: flush(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); if ((file->f_mode & FMODE_WRITE) == 0) return 0; lock_kernel(); /* Ensure that data+attribute caches are up to date after close() */ status = nfs_wb_all(inode); if (!status) { status = ctx->error; ctx->error = 0; if (!status) nfs_revalidate_inode(NFS_SERVER(inode), inode); } unlock_kernel(); return status; }
int nfs_permission(struct inode *inode, int mask) { struct nfs_server *server = NFS_SERVER(inode); struct nfs_access_cache *cache = &NFS_I(inode)->cache_access; struct rpc_cred *cred; int mode = inode->i_mode; int error; 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; } if ((server->flags & NFS_MOUNT_NOACL) || !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))) { if (!cache->err) { /* Is the mask a subset of an accepted mask? */ if ((cache->mask & mask) == mask) goto out_cached; } else { /* ...or is it a superset of a rejected mask? */ if ((cache->mask & mask) == cache->mask) goto out_cached; } } error = NFS_PROTO(inode)->access(inode, cred, mask); if (!error || error == -EACCES) { cache->jiffies = jiffies; if (cache->cred) put_rpccred(cache->cred); cache->cred = cred; cache->mask = mask; cache->err = error; return error; } put_rpccred(cred); out_notsup: nfs_revalidate_inode(NFS_SERVER(inode), inode); return vfs_permission(inode, mask); out_cached: put_rpccred(cred); return cache->err; }
struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type) { struct nfs_server *server = NFS_SERVER(inode); struct nfs_fattr fattr; struct page *pages[NFSACL_MAXPAGES] = { }; struct nfs3_getaclargs args = { .fh = NFS_FH(inode), /* The xdr layer may allocate pages here. */ .pages = pages, }; struct nfs3_getaclres res = { .fattr = &fattr, }; struct rpc_message msg = { .rpc_argp = &args, .rpc_resp = &res, }; struct posix_acl *acl; int status, count; if (!nfs_server_capable(inode, NFS_CAP_ACLS)) return ERR_PTR(-EOPNOTSUPP); status = nfs_revalidate_inode(server, inode); if (status < 0) return ERR_PTR(status); if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL) nfs_zap_acl_cache(inode); acl = nfs3_get_cached_acl(inode, type); if (acl != ERR_PTR(-EAGAIN)) return acl; acl = NULL; /* * Only get the access acl when explicitly requested: We don't * need it for access decisions, and only some applications use * it. Applications which request the access acl first are not * penalized from this optimization. */ if (type == ACL_TYPE_ACCESS) args.mask |= NFS_ACLCNT|NFS_ACL; if (S_ISDIR(inode->i_mode)) args.mask |= NFS_DFACLCNT|NFS_DFACL; if (args.mask == 0) return NULL; dprintk("NFS call getacl\n"); msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_GETACL]; nfs_fattr_init(&fattr); status = rpc_call_sync(server->client_acl, &msg, 0); dprintk("NFS reply getacl: %d\n", status); /* pages may have been allocated at the xdr layer. */ for (count = 0; count < NFSACL_MAXPAGES && args.pages[count]; count++) __free_page(args.pages[count]); switch (status) { case 0: status = nfs_refresh_inode(inode, &fattr); break; case -EPFNOSUPPORT: case -EPROTONOSUPPORT: dprintk("NFS_V3_ACL extension not supported; disabling\n"); server->caps &= ~NFS_CAP_ACLS; case -ENOTSUPP: status = -EOPNOTSUPP; default: goto getout; } if ((args.mask & res.mask) != args.mask) { status = -EIO; goto getout; } if (res.acl_access != NULL) { if (posix_acl_equiv_mode(res.acl_access, NULL) == 0) { posix_acl_release(res.acl_access); res.acl_access = NULL; } } nfs3_cache_acls(inode, (res.mask & NFS_ACL) ? res.acl_access : ERR_PTR(-EINVAL), (res.mask & NFS_DFACL) ? res.acl_default : ERR_PTR(-EINVAL)); switch(type) { case ACL_TYPE_ACCESS: acl = res.acl_access; res.acl_access = NULL; break; case ACL_TYPE_DEFAULT: acl = res.acl_default; res.acl_default = NULL; } getout: posix_acl_release(res.acl_access); posix_acl_release(res.acl_default); if (status != 0) { posix_acl_release(acl); acl = ERR_PTR(status); } return acl; }
/* * 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 parent directory is seen to have changed, we throw out the * cached dentry and do a new lookup. */ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) { struct inode *dir; struct inode *inode; struct dentry *parent; int error; struct nfs_fh fhandle; struct nfs_fattr fattr; unsigned long verifier; int isopen = 0; parent = dget_parent(dentry); lock_kernel(); dir = parent->d_inode; inode = dentry->d_inode; if (nd && !(nd->flags & LOOKUP_CONTINUE) && (nd->flags & LOOKUP_OPEN)) isopen = 1; if (!inode) { if (nfs_neg_need_reval(dir, dentry, nd)) goto out_bad; goto out_valid; } if (is_bad_inode(inode)) { dfprintk(VFS, "nfs_lookup_validate: %s/%s has dud inode\n", dentry->d_parent->d_name.name, dentry->d_name.name); goto out_bad; } /* Revalidate parent directory attribute cache */ nfs_revalidate_inode(NFS_SERVER(dir), dir); /* Force a full look up iff the parent directory has changed */ if (nfs_check_verifier(dir, dentry)) { if (nfs_lookup_verify_inode(inode, isopen)) goto out_zap_parent; goto out_valid; } /* * Note: we're not holding inode->i_sem and so may be racing with * operations that change the directory. We therefore save the * change attribute *before* we do the RPC call. */ verifier = nfs_save_change_attribute(dir); error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr); if (!error) { if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0) goto out_bad; if (nfs_lookup_verify_inode(inode, isopen)) goto out_zap_parent; goto out_valid_renew; } if (NFS_STALE(inode)) goto out_bad; error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); if (error) goto out_bad; if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0) goto out_bad; if ((error = nfs_refresh_inode(inode, &fattr)) != 0) goto out_bad; out_valid_renew: nfs_renew_times(dentry); nfs_set_verifier(dentry, verifier); out_valid: unlock_kernel(); dput(parent); return 1; out_zap_parent: nfs_zap_caches(dir); out_bad: NFS_CACHEINV(dir); if (inode && S_ISDIR(inode->i_mode)) { /* Purge readdir caches. */ nfs_zap_caches(inode); /* If we have submounts, don't unhash ! */ if (have_submounts(dentry)) goto out_valid; shrink_dcache_parent(dentry); } d_drop(dentry); unlock_kernel(); dput(parent); 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; }
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; }