All of the above --- Index: linux-2.6.11/fs/Kconfig =================================================================== --- linux-2.6.11.orig/fs/Kconfig +++ linux-2.6.11/fs/Kconfig @@ -1515,6 +1515,20 @@ config RPCSEC_GSS_SPKM3 If unsure, say N. +config RPCSEC_GSS_KEYRING + bool "Secure RPC: keyring support (EXPERIMENTAL)" + depends on SUNRPC_GSS && KEYS && EXPERIMENTAL + help + Use the new RPCSEC_GSS upcall mechanism based on keyrings. + This allows individual threads, processes or groups of + processes to specify their own authentication tokens, + providing much the same functionality that AFS pags used to. + + Note: requires the new helper program /sbin/request-key, as + well as an updated rpc.gssd daemon in order to work. + + If unsure, say N + config SMB_FS tristate "SMB file system support (to mount Windows shares etc.)" depends on INET Index: linux-2.6.11/fs/lockd/clntproc.c =================================================================== --- linux-2.6.11.orig/fs/lockd/clntproc.c +++ linux-2.6.11/fs/lockd/clntproc.c @@ -322,14 +322,13 @@ static int nlm_wait_on_grace(wait_queue_ /* * Generic NLM call */ -int +static int nlmclnt_call(struct nlm_rqst *req, u32 proc) { struct nlm_host *host = req->a_host; struct rpc_clnt *clnt; struct nlm_args *argp = &req->a_args; struct nlm_res *resp = &req->a_res; - struct file *filp = argp->lock.fl.fl_file; struct rpc_message msg = { .rpc_argp = argp, .rpc_resp = resp, @@ -339,9 +338,6 @@ nlmclnt_call(struct nlm_rqst *req, u32 p dprintk("lockd: call procedure %d on %s\n", (int)proc, host->h_name); - if (filp) - msg.rpc_cred = nfs_file_cred(filp); - do { if (host->h_reclaiming && !argp->reclaim) goto in_grace_period; @@ -428,14 +424,13 @@ nlmsvc_async_call(struct nlm_rqst *req, return status; } -int +static int nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback) { struct nlm_host *host = req->a_host; struct rpc_clnt *clnt; struct nlm_args *argp = &req->a_args; struct nlm_res *resp = &req->a_res; - struct file *file = argp->lock.fl.fl_file; struct rpc_message msg = { .rpc_argp = argp, .rpc_resp = resp, @@ -450,11 +445,9 @@ nlmclnt_async_call(struct nlm_rqst *req, return -ENOLCK; msg.rpc_proc = &clnt->cl_procinfo[proc]; - /* bootstrap and kick off the async RPC call */ - if (file) - msg.rpc_cred = nfs_file_cred(file); /* Increment host refcount */ nlm_get_host(host); + /* bootstrap and kick off the async RPC call */ status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req); if (status < 0) nlm_release_host(host); @@ -516,6 +509,24 @@ static void nlmclnt_locks_init_private(s fl->fl_ops = &nlmclnt_lock_ops; } +static void do_vfs_lock(struct file_lock *fl) +{ + int res = 0; + switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) { + case FL_POSIX: + res = posix_lock_file_wait(fl->fl_file, fl); + break; + case FL_FLOCK: + res = flock_lock_file_wait(fl->fl_file, fl); + break; + default: + BUG(); + } + if (res < 0) + printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", + __FUNCTION__); +} + /* * LOCK: Try to create a lock * @@ -564,9 +575,7 @@ nlmclnt_lock(struct nlm_rqst *req, struc fl->fl_u.nfs_fl.state = host->h_state; fl->fl_u.nfs_fl.flags |= NFS_LCK_GRANTED; fl->fl_flags |= FL_SLEEP; - if (posix_lock_file_wait(fl->fl_file, fl) < 0) - printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", - __FUNCTION__); + do_vfs_lock(fl); } status = nlm_stat_to_errno(resp->status); out: @@ -635,7 +644,7 @@ nlmclnt_unlock(struct nlm_rqst *req, str nlmclnt_unlock_callback); /* Hrmf... Do the unlock early since locks_remove_posix() * really expects us to free the lock synchronously */ - posix_lock_file(fl->fl_file, fl); + do_vfs_lock(fl); if (status < 0) { nlmclnt_release_lockargs(req); kfree(req); @@ -648,7 +657,7 @@ nlmclnt_unlock(struct nlm_rqst *req, str if (status < 0) return status; - posix_lock_file(fl->fl_file, fl); + do_vfs_lock(fl); if (resp->status == NLM_LCK_GRANTED) return 0; Index: linux-2.6.11/fs/lockd/host.c =================================================================== --- linux-2.6.11.orig/fs/lockd/host.c +++ linux-2.6.11/fs/lockd/host.c @@ -110,7 +110,6 @@ nlm_lookup_host(int server, struct socka host->h_addr.sin_port = 0; /* ouch! */ host->h_version = version; host->h_proto = proto; - host->h_authflavor = RPC_AUTH_UNIX; host->h_rpcclnt = NULL; init_MUTEX(&host->h_sema); host->h_nextrebind = jiffies + NLM_HOST_REBIND; @@ -191,8 +190,9 @@ nlm_bind_host(struct nlm_host *host) xprt_set_timeout(&xprt->timeout, 5, nlmsvc_timeout); + /* Existing NLM servers accept AUTH_UNIX only */ clnt = rpc_create_client(xprt, host->h_name, &nlm_program, - host->h_version, host->h_authflavor); + host->h_version, RPC_AUTH_UNIX); if (IS_ERR(clnt)) { xprt_destroy(xprt); goto forgetit; Index: linux-2.6.11/fs/locks.c =================================================================== --- linux-2.6.11.orig/fs/locks.c +++ linux-2.6.11/fs/locks.c @@ -1876,8 +1876,13 @@ void locks_remove_flock(struct file *fil return; if (filp->f_op && filp->f_op->flock) { - struct file_lock fl = { .fl_flags = FL_FLOCK, - .fl_type = F_UNLCK }; + struct file_lock fl = { + .fl_pid = current->tgid, + .fl_file = filp, + .fl_flags = FL_FLOCK, + .fl_type = F_UNLCK, + .fl_end = OFFSET_MAX, + }; filp->f_op->flock(filp, F_SETLKW, &fl); } Index: linux-2.6.11/fs/namei.c =================================================================== --- linux-2.6.11.orig/fs/namei.c +++ linux-2.6.11/fs/namei.c @@ -681,7 +681,7 @@ fail: * * We expect 'base' to be positive and a directory. */ -int fastcall link_path_walk(const char * name, struct nameidata *nd) +static fastcall int __link_path_walk(const char * name, struct nameidata *nd) { struct path next; struct inode *inode; @@ -703,6 +703,7 @@ int fastcall link_path_walk(const char * struct qstr this; unsigned int c; + nd->flags |= LOOKUP_CONTINUE; err = exec_permission_lite(inode, nd); if (err == -EAGAIN) { err = permission(inode, MAY_EXEC, nd); @@ -755,7 +756,6 @@ int fastcall link_path_walk(const char * if (err < 0) break; } - nd->flags |= LOOKUP_CONTINUE; /* This does the actual lookups.. */ err = do_lookup(nd, &this, &next); if (err) @@ -881,6 +881,37 @@ return_err: return err; } +/* + * Wrapper to retry pathname resolution whenever the underlying + * file system returns an ESTALE. + * + * Retry the whole path once, forcing real lookup requests + * instead of relying on the dcache. + */ +int fastcall link_path_walk(const char *name, struct nameidata *nd) +{ + struct nameidata save = *nd; + int result; + + /* make sure the stuff we saved doesn't go away */ + dget(save.dentry); + mntget(save.mnt); + + result = __link_path_walk(name, nd); + if (result == -ESTALE) { + *nd = save; + dget(nd->dentry); + mntget(nd->mnt); + nd->flags |= LOOKUP_REVAL; + result = __link_path_walk(name, nd); + } + + dput(save.dentry); + mntput(save.mnt); + + return result; +} + int fastcall path_walk(const char * name, struct nameidata *nd) { current->total_link_count = 0; Index: linux-2.6.11/fs/nfs/Makefile =================================================================== --- linux-2.6.11.orig/fs/nfs/Makefile +++ linux-2.6.11/fs/nfs/Makefile @@ -5,7 +5,8 @@ obj-$(CONFIG_NFS_FS) += nfs.o nfs-y := dir.o file.o inode.o nfs2xdr.o pagelist.o \ - proc.o read.o symlink.o unlink.o write.o + proc.o read.o symlink.o unlink.o write.o \ + namespace.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ Index: linux-2.6.11/fs/nfs/callback.c =================================================================== --- linux-2.6.11.orig/fs/nfs/callback.c +++ linux-2.6.11/fs/nfs/callback.c @@ -14,6 +14,7 @@ #include #include #include +#include "nfs4_fs.h" #include "callback.h" #define NFSDBG_FACILITY NFSDBG_CALLBACK Index: linux-2.6.11/fs/nfs/callback_proc.c =================================================================== --- linux-2.6.11.orig/fs/nfs/callback_proc.c +++ linux-2.6.11/fs/nfs/callback_proc.c @@ -8,6 +8,7 @@ #include #include #include +#include "nfs4_fs.h" #include "callback.h" #include "delegation.h" Index: linux-2.6.11/fs/nfs/callback_xdr.c =================================================================== --- linux-2.6.11.orig/fs/nfs/callback_xdr.c +++ linux-2.6.11/fs/nfs/callback_xdr.c @@ -10,6 +10,7 @@ #include #include #include +#include "nfs4_fs.h" #include "callback.h" #define CB_OP_TAGLEN_MAXSZ (512) Index: linux-2.6.11/fs/nfs/delegation.c =================================================================== --- linux-2.6.11.orig/fs/nfs/delegation.c +++ linux-2.6.11/fs/nfs/delegation.c @@ -16,6 +16,7 @@ #include #include +#include "nfs4_fs.h" #include "delegation.h" static struct nfs_delegation *nfs_alloc_delegation(void) Index: linux-2.6.11/fs/nfs/dir.c =================================================================== --- linux-2.6.11.orig/fs/nfs/dir.c +++ linux-2.6.11/fs/nfs/dir.c @@ -32,6 +32,7 @@ #include #include +#include "nfs4_fs.h" #include "delegation.h" #define NFS_PARANOIA 1 @@ -90,6 +91,9 @@ struct inode_operations nfs4_dir_inode_o .permission = nfs_permission, .getattr = nfs_getattr, .setattr = nfs_setattr, + .getxattr = nfs4_getxattr, + .setxattr = nfs4_setxattr, + .listxattr = nfs4_listxattr, }; #endif /* CONFIG_NFS_V4 */ @@ -529,13 +533,24 @@ static inline void nfs_renew_times(struc } static inline -int nfs_lookup_verify_inode(struct inode *inode, int isopen) +int nfs_lookup_verify_inode(struct inode *inode, struct nameidata *nd) { struct nfs_server *server = NFS_SERVER(inode); - if (isopen && !(server->flags & NFS_MOUNT_NOCTO)) - return __nfs_revalidate_inode(server, inode); + if (nd != NULL) { + int ndflags = nd->flags; + /* VFS wants an on-the-wire revalidation */ + if (ndflags & LOOKUP_REVAL) + goto out_force; + /* This is an open(2) */ + if ((ndflags & LOOKUP_OPEN) && + !(ndflags & LOOKUP_CONTINUE) && + !(server->flags & NFS_MOUNT_NOCTO)) + goto out_force; + } return nfs_revalidate_inode(server, inode); +out_force: + return __nfs_revalidate_inode(server, inode); } /* @@ -579,16 +594,12 @@ static int nfs_lookup_revalidate(struct 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; @@ -602,11 +613,12 @@ static int nfs_lookup_revalidate(struct } /* Revalidate parent directory attribute cache */ - nfs_revalidate_inode(NFS_SERVER(dir), dir); + if (nfs_revalidate_inode(NFS_SERVER(dir), dir) < 0) + goto out_zap_parent; /* Force a full look up iff the parent directory has changed */ if (nfs_check_verifier(dir, dentry)) { - if (nfs_lookup_verify_inode(inode, isopen)) + if (nfs_lookup_verify_inode(inode, nd)) goto out_zap_parent; goto out_valid; } @@ -702,6 +714,17 @@ int nfs_is_exclusive_create(struct inode return (nd->intent.open.flags & O_EXCL) != 0; } +static inline int nfs_reval_fsid(struct inode *dir, + struct nfs_fh *fh, struct nfs_fattr *fattr) +{ + struct nfs_server *server = NFS_SERVER(dir); + + if (!nfs_fsid_equal(&server->fsid, &fattr->fsid)) + /* Revalidate fsid on root dir */ + return __nfs_revalidate_inode(server, dir->i_sb->s_root->d_inode); + return 0; +} + static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) { struct dentry *res; @@ -722,7 +745,9 @@ static struct dentry *nfs_lookup(struct lock_kernel(); /* Revalidate parent directory attribute cache */ - nfs_revalidate_inode(NFS_SERVER(dir), dir); + error = nfs_revalidate_inode(NFS_SERVER(dir), dir); + if (error < 0) + goto out_err; /* If we're doing an exclusive create, optimize away the lookup */ if (nfs_is_exclusive_create(dir, nd)) @@ -731,10 +756,11 @@ static struct dentry *nfs_lookup(struct error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); if (error == -ENOENT) goto no_entry; - if (error < 0) { - res = ERR_PTR(error); - goto out_unlock; - } + if (error < 0) + goto out_err; + error = nfs_reval_fsid(dir, &fhandle, &fattr); + if (error < 0) + goto out_err; res = ERR_PTR(-EACCES); inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr); if (!inode) @@ -749,6 +775,9 @@ out_unlock: unlock_kernel(); out: return res; +out_err: + res = ERR_PTR(error); + goto out_unlock; } #ifdef CONFIG_NFS_V4 @@ -780,6 +809,7 @@ static struct dentry *nfs_atomic_lookup( { struct dentry *res = NULL; struct inode *inode = NULL; + int error; /* Check that we are indeed trying to open this file */ if (!is_atomic_open(dir, nd)) @@ -798,7 +828,11 @@ static struct dentry *nfs_atomic_lookup( /* Open the file on the server */ lock_kernel(); /* Revalidate parent directory attribute cache */ - nfs_revalidate_inode(NFS_SERVER(dir), dir); + error = nfs_revalidate_inode(NFS_SERVER(dir), dir); + if (error < 0) { + res = ERR_PTR(error); + goto out; + } if (nd->intent.open.flags & O_CREAT) { nfs_begin_data_update(dir); @@ -808,7 +842,7 @@ static struct dentry *nfs_atomic_lookup( inode = nfs4_atomic_open(dir, dentry, nd); unlock_kernel(); if (IS_ERR(inode)) { - int error = PTR_ERR(inode); + error = PTR_ERR(inode); switch (error) { /* Make a negative dentry */ case -ENOENT: @@ -938,7 +972,7 @@ static struct dentry *nfs_readdir_lookup /* * Code common to create, mkdir, and mknod. */ -static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, +int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, struct nfs_fattr *fattr) { struct inode *inode; @@ -959,14 +993,12 @@ static int nfs_instantiate(struct dentry if (error < 0) goto out_err; } - inode = nfs_fhget(dentry->d_sb, fhandle, fattr); - if (inode) { - d_instantiate(dentry, inode); - nfs_renew_times(dentry); - nfs_set_verifier(dentry, nfs_save_change_attribute(dentry->d_parent->d_inode)); - return 0; - } error = -ENOMEM; + inode = nfs_fhget(dentry->d_sb, fhandle, fattr); + if (inode == NULL) + goto out_err; + d_instantiate(dentry, inode); + return 0; out_err: d_drop(dentry); return error; @@ -982,7 +1014,6 @@ static int nfs_create(struct inode *dir, struct nameidata *nd) { struct iattr attr; - struct inode *inode; int error; int open_flags = 0; @@ -997,18 +1028,17 @@ static int nfs_create(struct inode *dir, lock_kernel(); nfs_begin_data_update(dir); - inode = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags); + error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags); nfs_end_data_update(dir); - if (!IS_ERR(inode)) { - d_instantiate(dentry, inode); - nfs_renew_times(dentry); - nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); - error = 0; - } else { - error = PTR_ERR(inode); - d_drop(dentry); - } + if (error != 0) + goto out_err; + nfs_renew_times(dentry); + nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); + unlock_kernel(); + return 0; +out_err: unlock_kernel(); + d_drop(dentry); return error; } @@ -1019,9 +1049,7 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) { struct iattr attr; - struct nfs_fattr fattr; - struct nfs_fh fhandle; - int error; + int status; dfprintk(VFS, "NFS: mknod(%s/%ld, %s\n", dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); @@ -1034,15 +1062,18 @@ nfs_mknod(struct inode *dir, struct dent lock_kernel(); nfs_begin_data_update(dir); - error = NFS_PROTO(dir)->mknod(dir, &dentry->d_name, &attr, rdev, - &fhandle, &fattr); + status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev); nfs_end_data_update(dir); - if (!error) - error = nfs_instantiate(dentry, &fhandle, &fattr); - else - d_drop(dentry); + if (status != 0) + goto out_err; + nfs_renew_times(dentry); + nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); unlock_kernel(); - return error; + return 0; +out_err: + unlock_kernel(); + d_drop(dentry); + return status; } /* @@ -1051,8 +1082,6 @@ nfs_mknod(struct inode *dir, struct dent static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct iattr attr; - struct nfs_fattr fattr; - struct nfs_fh fhandle; int error; dfprintk(VFS, "NFS: mkdir(%s/%ld, %s\n", dir->i_sb->s_id, @@ -1062,23 +1091,17 @@ static int nfs_mkdir(struct inode *dir, attr.ia_mode = mode | S_IFDIR; lock_kernel(); -#if 0 - /* - * Always drop the dentry, we can't always depend on - * the fattr returned by the server (AIX seems to be - * broken). We're better off doing another lookup than - * depending on potentially bogus information. - */ - d_drop(dentry); -#endif nfs_begin_data_update(dir); - error = NFS_PROTO(dir)->mkdir(dir, &dentry->d_name, &attr, &fhandle, - &fattr); + error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr); nfs_end_data_update(dir); - if (!error) - error = nfs_instantiate(dentry, &fhandle, &fattr); - else - d_drop(dentry); + if (error != 0) + goto out_err; + nfs_renew_times(dentry); + nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); + unlock_kernel(); + return 0; +out_err: + d_drop(dentry); unlock_kernel(); return error; } @@ -1107,7 +1130,7 @@ static int nfs_sillyrename(struct inode static unsigned int sillycounter; const int i_inosize = sizeof(dir->i_ino)*2; const int countersize = sizeof(sillycounter)*2; - const int slen = strlen(".nfs") + i_inosize + countersize; + const int slen = sizeof(".nfs") + i_inosize + countersize - 1; char silly[slen+1]; struct qstr qsilly; struct dentry *sdentry; @@ -1498,34 +1521,52 @@ out: int nfs_permission(struct inode *inode, int mask, struct nameidata *nd) { struct rpc_cred *cred; - int res; + int res = 0; if (mask == 0) - return 0; + goto out; + /* Is this sys_access() ? */ + if (nd != NULL && (nd->flags & LOOKUP_ACCESS)) + goto force_lookup; - /* 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; + switch (inode->i_mode & S_IFMT) { + case S_IFLNK: + goto out; + case S_IFREG: + /* NFSv4 has atomic_open... */ + if (nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN) + && nd != NULL + && (nd->flags & LOOKUP_OPEN)) + goto out; + break; + case S_IFDIR: + /* + * Optimize away all write operations, since the server + * will check permissions when we perform the op. + */ + if ((mask & MAY_WRITE) && !(mask & MAY_READ)) + goto out; } +force_lookup: lock_kernel(); if (!NFS_PROTO(inode)->access) goto out_notsup; cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); - res = nfs_do_access(inode, cred, mask); - put_rpccred(cred); + if (!IS_ERR(cred)) { + res = nfs_do_access(inode, cred, mask); + put_rpccred(cred); + } else + res = PTR_ERR(cred); unlock_kernel(); +out: return res; out_notsup: - nfs_revalidate_inode(NFS_SERVER(inode), inode); - res = generic_permission(inode, mask, NULL); + res = nfs_revalidate_inode(NFS_SERVER(inode), inode); + if (res == 0) + res = generic_permission(inode, mask, NULL); unlock_kernel(); return res; } Index: linux-2.6.11/fs/nfs/file.c =================================================================== --- linux-2.6.11.orig/fs/nfs/file.c +++ linux-2.6.11/fs/nfs/file.c @@ -31,6 +31,7 @@ #include #include +#include "nfs4_fs.h" #include "delegation.h" #define NFSDBG_FACILITY NFSDBG_FILE @@ -44,6 +45,8 @@ static ssize_t nfs_file_write(struct kio static int nfs_file_flush(struct file *); static int nfs_fsync(struct file *, struct dentry *dentry, int datasync); static int nfs_check_flags(int flags); +static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl); +static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl); struct file_operations nfs_file_operations = { .llseek = remote_llseek, @@ -57,6 +60,7 @@ struct file_operations nfs_file_operatio .release = nfs_file_release, .fsync = nfs_fsync, .lock = nfs_lock, + .flock = nfs_flock, .sendfile = nfs_file_sendfile, .check_flags = nfs_check_flags, }; @@ -67,6 +71,19 @@ struct inode_operations nfs_file_inode_o .setattr = nfs_setattr, }; +#ifdef CONFIG_NFS_V4 + +struct inode_operations nfs4_file_inode_operations = { + .permission = nfs_permission, + .getattr = nfs_getattr, + .setattr = nfs_setattr, + .getxattr = nfs4_getxattr, + .setxattr = nfs4_setxattr, + .listxattr = nfs4_listxattr, +}; + +#endif /* CONFIG_NFS_V4 */ + /* Hack for future NFS swap support */ #ifndef IS_SWAPFILE # define IS_SWAPFILE(inode) (0) @@ -312,6 +329,25 @@ static int do_getlk(struct file *filp, i return status; } +static int do_vfs_lock(struct file *file, struct file_lock *fl) +{ + int res = 0; + switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) { + case FL_POSIX: + res = posix_lock_file_wait(file, fl); + break; + case FL_FLOCK: + res = flock_lock_file_wait(file, fl); + break; + default: + BUG(); + } + if (res < 0) + printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", + __FUNCTION__); + return res; +} + static int do_unlk(struct file *filp, int cmd, struct file_lock *fl) { struct inode *inode = filp->f_mapping->host; @@ -338,7 +374,7 @@ static int do_unlk(struct file *filp, in if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)) status = NFS_PROTO(inode)->lock(filp, cmd, fl); else - status = posix_lock_file_wait(filp, fl); + status = do_vfs_lock(filp, fl); unlock_kernel(); rpc_clnt_sigunmask(NFS_CLIENT(inode), &oldset); return status; @@ -377,9 +413,9 @@ static int do_setlk(struct file *filp, i * the process exits. */ if (status == -EINTR || status == -ERESTARTSYS) - posix_lock_file_wait(filp, fl); + do_vfs_lock(filp, fl); } else - status = posix_lock_file_wait(filp, fl); + status = do_vfs_lock(filp, fl); unlock_kernel(); if (status < 0) goto out; @@ -401,8 +437,7 @@ out: /* * Lock a (portion of) a file */ -int -nfs_lock(struct file *filp, int cmd, struct file_lock *fl) +static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) { struct inode * inode = filp->f_mapping->host; @@ -418,6 +453,27 @@ nfs_lock(struct file *filp, int cmd, str if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) return -ENOLCK; + if (IS_GETLK(cmd)) + return do_getlk(filp, cmd, fl); + if (fl->fl_type == F_UNLCK) + return do_unlk(filp, cmd, fl); + return do_setlk(filp, cmd, fl); +} + +/* + * Lock a (portion of) a file + */ +static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) +{ + struct inode * inode = filp->f_mapping->host; + + dprintk("NFS: nfs_flock(f=%s/%ld, t=%x, fl=%x)\n", + inode->i_sb->s_id, inode->i_ino, + fl->fl_type, fl->fl_flags); + + if (!inode) + return -EINVAL; + /* * No BSD flocks over NFS allowed. * Note: we could try to fake a POSIX lock request here by @@ -425,11 +481,14 @@ nfs_lock(struct file *filp, int cmd, str * Not sure whether that would be unique, though, or whether * that would break in other places. */ - if (!fl->fl_owner || !(fl->fl_flags & FL_POSIX)) + if (!(fl->fl_flags & FL_FLOCK)) return -ENOLCK; - if (IS_GETLK(cmd)) - return do_getlk(filp, cmd, fl); + /* We're simulating flock() locks using posix locks on the server */ + fl->fl_owner = (fl_owner_t)filp; + fl->fl_start = 0; + fl->fl_end = OFFSET_MAX; + if (fl->fl_type == F_UNLCK) return do_unlk(filp, cmd, fl); return do_setlk(filp, cmd, fl); Index: linux-2.6.11/fs/nfs/idmap.c =================================================================== --- linux-2.6.11.orig/fs/nfs/idmap.c +++ linux-2.6.11/fs/nfs/idmap.c @@ -46,10 +46,10 @@ #include #include -#include #include #include +#include "nfs4_fs.h" #define IDMAP_HASH_SZ 128 Index: linux-2.6.11/fs/nfs/inode.c =================================================================== --- linux-2.6.11.orig/fs/nfs/inode.c +++ linux-2.6.11/fs/nfs/inode.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #include #include +#include "nfs4_fs.h" #include "delegation.h" #define NFSDBG_FACILITY NFSDBG_VFS @@ -64,6 +66,8 @@ static void nfs_umount_begin(struct supe static int nfs_statfs(struct super_block *, struct kstatfs *); static int nfs_show_options(struct seq_file *, struct vfsmount *); +static struct rpc_program nfs_program; + static struct super_operations nfs_sops = { .alloc_inode = nfs_alloc_inode, .destroy_inode = nfs_destroy_inode, @@ -78,7 +82,7 @@ static struct super_operations nfs_sops /* * RPC cruft for NFS */ -struct rpc_stat nfs_rpcstat = { +static struct rpc_stat nfs_rpcstat = { .program = &nfs_program }; static struct rpc_version * nfs_version[] = { @@ -95,7 +99,7 @@ static struct rpc_version * nfs_version[ #endif }; -struct rpc_program nfs_program = { +static struct rpc_program nfs_program = { .name = "nfs", .number = NFS_PROGRAM, .nrvers = sizeof(nfs_version) / sizeof(nfs_version[0]), @@ -104,6 +108,60 @@ struct rpc_program nfs_program = { .pipe_dir_name = "/nfs", }; +#ifdef CONFIG_SYSCTL +/* Follow the established convention in NLM */ +#define CTL_UNNUMBERED -2 + +static ctl_table nfs_sysctls[] = { + { + .ctl_name = CTL_UNNUMBERED, + .procname = "nfs_mountpoint_timeout", + .data = &nfs_mountpoint_expiry_timeout, + .maxlen = sizeof(nfs_mountpoint_expiry_timeout), + .mode = 0644, + .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies, + }, + { .ctl_name = 0 } +}; + +static ctl_table nfs_sysctl_dir[] = { + { + .ctl_name = CTL_UNNUMBERED, + .procname = "nfs", + .mode = 0555, + .child = nfs_sysctls, + }, + { .ctl_name = 0 } +}; + +static ctl_table nfs_sysctl_root[] = { + { + .ctl_name = CTL_FS, + .procname = "fs", + .mode = 0555, + .child = nfs_sysctl_dir, + }, + { .ctl_name = 0 } +}; + +static struct ctl_table_header *nfs_sysctl_table; + +static inline int nfs_init_sysctl(void) +{ + nfs_sysctl_table = register_sysctl_table(nfs_sysctl_root, 0); + return nfs_sysctl_table != NULL ? 0 : -ENOMEM; +} + +static inline void nfs_destroy_sysctl(void) +{ + unregister_sysctl_table(nfs_sysctl_table); +} +#else +#define nfs_init_sysctl() (0) +#define nfs_destroy_sysctl() do { } while(0) +#endif /* CONFIG_SYSCTL */ + static inline unsigned long nfs_fattr_to_ino_t(struct nfs_fattr *fattr) { @@ -138,16 +196,13 @@ nfs_delete_inode(struct inode * inode) clear_inode(inode); } -/* - * For the moment, the only task for the NFS clear_inode method is to - * release the mmap credential - */ static void nfs_clear_inode(struct inode *inode) { struct nfs_inode *nfsi = NFS_I(inode); struct rpc_cred *cred; + nfs4_zap_acl_attr(inode); nfs_wb_all(inode); BUG_ON (!list_empty(&nfsi->open_files)); cred = nfsi->cache_access.cred; @@ -209,6 +264,14 @@ nfs_block_size(unsigned long bsize, unsi return nfs_block_bits(bsize, nrbitsp); } +static inline void +nfs_super_set_maxbytes(struct super_block *sb, __u64 maxfilesize) +{ + sb->s_maxbytes = (loff_t)maxfilesize; + if (sb->s_maxbytes > MAX_LFS_FILESIZE || sb->s_maxbytes <= 0) + sb->s_maxbytes = MAX_LFS_FILESIZE; +} + /* * Obtain the root inode of the file system. */ @@ -225,6 +288,7 @@ nfs_get_root(struct super_block *sb, str return ERR_PTR(error); } + server->fsid = fsinfo->fattr->fsid; rooti = nfs_fhget(sb, rootfh, fsinfo->fattr); if (!rooti) return ERR_PTR(-ENOMEM); @@ -247,6 +311,7 @@ nfs_sb_init(struct super_block *sb, rpc_ .fattr = &fattr, }; int no_root_error = 0; + unsigned long max_rpc_payload; /* We probably want something more informative here */ snprintf(sb->s_id, sizeof(sb->s_id), "%x:%x", MAJOR(sb->s_dev), MINOR(sb->s_dev)); @@ -283,6 +348,12 @@ nfs_sb_init(struct super_block *sb, rpc_ if (fsinfo.wtmax >= 512 && server->wsize > fsinfo.wtmax) server->wsize = nfs_block_size(fsinfo.wtmax, NULL); + max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL); + if (server->rsize > max_rpc_payload) + server->rsize = max_rpc_payload; + if (server->wsize > max_rpc_payload) + server->wsize = max_rpc_payload; + server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; if (server->rpages > NFS_READ_MAXIOV) { server->rpages = NFS_READ_MAXIOV; @@ -313,9 +384,10 @@ nfs_sb_init(struct super_block *sb, rpc_ } server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; - sb->s_maxbytes = fsinfo.maxfilesize; - if (sb->s_maxbytes > MAX_LFS_FILESIZE) - sb->s_maxbytes = MAX_LFS_FILESIZE; + nfs_super_set_maxbytes(sb, fsinfo.maxfilesize); + + server->client->cl_intr = (server->flags & NFS_MOUNT_INTR) ? 1 : 0; + server->client->cl_softrtry = (server->flags & NFS_MOUNT_SOFT) ? 1 : 0; /* We're airborne Set socket buffersize */ rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); @@ -364,9 +436,8 @@ nfs_create_client(struct nfs_server *ser goto out_fail; } - clnt->cl_intr = (server->flags & NFS_MOUNT_INTR) ? 1 : 0; - clnt->cl_softrtry = (server->flags & NFS_MOUNT_SOFT) ? 1 : 0; - clnt->cl_droppriv = (server->flags & NFS_MOUNT_BROKEN_SUID) ? 1 : 0; + clnt->cl_intr = 1; + clnt->cl_softrtry = 1; clnt->cl_chatty = 1; return clnt; @@ -538,7 +609,6 @@ static int nfs_show_options(struct seq_f { NFS_MOUNT_NOCTO, ",nocto", "" }, { NFS_MOUNT_NOAC, ",noac", "" }, { NFS_MOUNT_NONLM, ",nolock", ",lock" }, - { NFS_MOUNT_BROKEN_SUID, ",broken_suid", "" }, { 0, NULL, NULL } }; struct proc_nfs_info *nfs_infop; @@ -679,7 +749,7 @@ nfs_fhget(struct super_block *sb, struct /* Why so? Because we want revalidate for devices/FIFOs, and * that's precisely what we have in nfs_file_inode_operations. */ - inode->i_op = &nfs_file_inode_operations; + inode->i_op = NFS_SB(sb)->rpc_ops->file_inode_ops; if (S_ISREG(inode->i_mode)) { inode->i_fop = &nfs_file_operations; inode->i_data.a_ops = &nfs_file_aops; @@ -690,6 +760,11 @@ nfs_fhget(struct super_block *sb, struct if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS) && fattr->size <= NFS_LIMIT_READDIRPLUS) NFS_FLAGS(inode) |= NFS_INO_ADVISE_RDPLUS; + /* Deal with crossing mountpoints */ + if (!nfs_fsid_equal(&NFS_SB(sb)->fsid, &fattr->fsid)) { + inode->i_op = &nfs_mountpoint_inode_operations; + inode->i_fop = NULL; + } } else if (S_ISLNK(inode->i_mode)) inode->i_op = &nfs_symlink_inode_operations; else @@ -792,7 +867,7 @@ nfs_setattr(struct dentry *dentry, struc * Wait for the inode to get unlocked. * (Used for NFS_INO_LOCKED and NFS_INO_REVALIDATING). */ -int +static int nfs_wait_on_inode(struct inode *inode, int flag) { struct rpc_clnt *clnt = NFS_CLIENT(inode); @@ -856,6 +931,12 @@ struct nfs_open_context *get_nfs_open_co void put_nfs_open_context(struct nfs_open_context *ctx) { if (atomic_dec_and_test(&ctx->count)) { + if (!list_empty(&ctx->list)) { + struct inode *inode = ctx->dentry->d_inode; + spin_lock(&inode->i_lock); + list_del(&ctx->list); + spin_unlock(&inode->i_lock); + } if (ctx->state != NULL) nfs4_close_state(ctx->state, ctx->mode); if (ctx->cred != NULL) @@ -904,7 +985,7 @@ void nfs_file_clear_open_context(struct if (ctx) { filp->private_data = NULL; spin_lock(&inode->i_lock); - list_del(&ctx->list); + list_move_tail(&ctx->list, &NFS_I(inode)->open_files); spin_unlock(&inode->i_lock); put_nfs_open_context(ctx); } @@ -918,8 +999,9 @@ int nfs_open(struct inode *inode, struct struct nfs_open_context *ctx; struct rpc_cred *cred; - if ((cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0)) == NULL) - return -ENOMEM; + cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); + if (IS_ERR(cred)) + return PTR_ERR(cred); ctx = alloc_nfs_open_context(filp->f_dentry, cred); put_rpccred(cred); if (ctx == NULL) @@ -1197,6 +1279,7 @@ int nfs_refresh_inode(struct inode *inod */ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsigned long verifier) { + struct nfs_server *server; struct nfs_inode *nfsi = NFS_I(inode); __u64 new_size; loff_t new_isize; @@ -1226,6 +1309,12 @@ static int nfs_update_inode(struct inode if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) goto out_changed; + server = NFS_SERVER(inode); + /* Update the fsid if and only if this is the root directory */ + if (inode == inode->i_sb->s_root->d_inode + && !nfs_fsid_equal(&server->fsid, &fattr->fsid)) + server->fsid = fattr->fsid; + /* * Update the read time so we don't revalidate too often. */ @@ -1294,6 +1383,7 @@ static int nfs_update_inode(struct inode inode->i_nlink = fattr->nlink; inode->i_uid = fattr->uid; inode->i_gid = fattr->gid; + nfs4_zap_acl_attr(inode); if (fattr->valid & (NFS_ATTR_FATTR_V3 | NFS_ATTR_FATTR_V4)) { /* @@ -1343,9 +1433,170 @@ static int nfs_update_inode(struct inode } /* + * nfs_try_migrate_filehandle - Check if we can migrate the inode filehandle + * @inode - pointer to inode + * @fh - the filehandle resulting from lookup() + * @fattr - attributes associated with the new filehandle + * + * Do our very best to update existing inodes when the user wants to migrate + * this filesystem to a replica server. + * + * Note that here be HUGE dragons, with endless possibilities for causing + * trouble... + */ +int nfs_try_migrate_filehandle(struct inode *inode, struct nfs_fh *fh, struct nfs_fattr *fattr, uint32_t generation) +{ + struct nfs_inode *nfsi = NFS_I(inode); + + /* Argh! The basic file type has changed */ + if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) + goto out_bad; + /* Fileid + filehandle are the same. Good! */ + if (nfs_compare_fh(&nfsi->fh, fh) == 0 && nfsi->fileid == fattr->fileid) + goto out_good; + if (fattr->valid && NFS_ATTR_FATTR_V4) { + /* Do the NFSv4 change attributes match our cached value? */ + if (nfsi->change_attr != fattr->change_attr) + goto out_bad; + } else { + /* Does the ctime match? */ + if (!timespec_equal(&fattr->ctime, &inode->i_ctime)) + goto out_bad; + } + /* Does the file size match? */ + if (nfs_size_to_loff_t(fattr->size) != inode->i_size) + goto out_bad; + /* FIXME: Here lie the biggest dragons: + * Try considering all possible races w.r.t. iget5_locked() + */ + nfs_copy_fh(&nfsi->fh, fh); + if (nfsi->fileid != fattr->fileid) { + /* The very concept of migrating to a new hash bucket + * is so full of holes and races that it defies belief! + */ + remove_inode_hash(inode); + nfsi->fileid = fattr->fileid; + inode->i_ino = nfs_fattr_to_ino_t(fattr); + insert_inode_hash(inode); + } +out_good: + inode->i_generation = generation; + return 0; +out_bad: + return -EIO; +} + +/* + * nfs_try_migrate_inode - Update an inode's filehandle after migration + * @inode - pointer to inode to migrate + * @dentry - pointer to dentry + */ +int nfs_try_migrate_inode(struct inode *inode, struct dentry *dentry) +{ + struct nfs_fh fh; + struct nfs_fattr fattr; + struct dentry *next, *next_parent; + uint32_t generation; + int status; + + if (dentry == NULL) { + status = -ENOENT; + dentry = d_find_alias(inode); + if (dentry == NULL) + goto out; + } else + dget(dentry); +repeat: + /* Has this inode already been revalidated? */ + status = 0; + generation = NFS_SERVER(inode)->generation; + if ((long)generation - (long)inode->i_generation <= 0) + goto out; + /* No. Search for a previously revalidated path element */ + next = dget(dentry); + next_parent = dget_parent(dentry); + while((long)generation - (long)next_parent->d_inode->i_generation > 0) { + BUG_ON(IS_ROOT(next_parent)); + dput(next); + next = next_parent; + next_parent = dget_parent(next); + } + status = NFS_PROTO(inode)->lookup(next_parent->d_inode, &next->d_name, + &fh, &fattr); + if (status == 0) + status = nfs_try_migrate_filehandle(next->d_inode, &fh, &fattr, generation); + switch (status) { + case -ESTALE: + if (IS_ROOT(next_parent)) + break; + case 0: + if (dentry->d_inode == inode) + break; + dput(next_parent); + dput(next); + goto repeat; + default: + d_drop(next); + } + dput(next_parent); + dput(next); +out: + dput(dentry); + dprintk("%s: returned error %d\n", __FUNCTION__, status); + return status; +} + +/* * File system information */ +/* + * nfs_path - reconstruct the path given an arbitrary dentry + * @base - arbitrary string to prepend to the path + * @dentry - pointer to dentry + * @buffer - result buffer + * @buflen - length of buffer + * + * Helper function for constructing the path from the + * root dentry to an arbitrary hashed dentry. + * + * This is mainly for use in figuring out the path on the + * server side when automounting on top of an existing partition. + */ +static char *nfs_path(const char *base, const struct dentry *dentry, + char *buffer, ssize_t buflen) +{ + char *end = buffer+buflen; + int namelen; + + *--end = '\0'; + buflen--; + spin_lock(&dcache_lock); + while (!IS_ROOT(dentry)) { + namelen = dentry->d_name.len; + buflen -= namelen + 1; + if (buflen < 0) + goto Elong; + end -= namelen; + memcpy(end, dentry->d_name.name, namelen); + *--end = '/'; + dentry = dentry->d_parent; + } + spin_unlock(&dcache_lock); + namelen = strlen(base); + /* Strip off excess slashes in base string */ + while (namelen > 0 && base[namelen - 1] == '/') + namelen--; + buflen -= namelen; + if (buflen < 0) + goto Elong; + end -= namelen; + memcpy(end, base, namelen); + return end; +Elong: + return ERR_PTR(-ENAMETOOLONG); +} + static int nfs_set_super(struct super_block *s, void *data) { s->s_fs_info = data; @@ -1466,6 +1717,7 @@ static void nfs_kill_super(struct super_ if (server->hostname != NULL) kfree(server->hostname); kfree(server); + nfs_release_automount_timer(); } static struct file_system_type nfs_fs_type = { @@ -1478,8 +1730,53 @@ static struct file_system_type nfs_fs_ty #ifdef CONFIG_NFS_V4 -static void nfs4_clear_inode(struct inode *); +#define XATTR_NAME_NFSV4_ACL "system.nfs4_acl" + +int +nfs4_setxattr(struct dentry *dentry, const char *key, const void *buf, + size_t buflen, int flags) +{ + struct inode *inode = dentry->d_inode; + + if (strcmp(key, XATTR_NAME_NFSV4_ACL) != 0) + return -EINVAL; + if (!S_ISREG(inode->i_mode) && + (!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX)) + return -EPERM; + + return nfs4_proc_set_acl(inode, buf, buflen); +} + +/* The getxattr man page suggests returning -ENODATA for unknown attributes, + * and that's what we'll do for e.g. user attributes that haven't been set. + * But we'll follow ext2/ext3's lead by returning -EOPNOTSUPP for unsupported + * attributes in kernel-managed attribute namespaces. */ +ssize_t +nfs4_getxattr(struct dentry *dentry, const char *key, void *buf, + size_t buflen) +{ + struct inode *inode = dentry->d_inode; + + if (strcmp(key, XATTR_NAME_NFSV4_ACL) != 0) + return -EOPNOTSUPP; + + return nfs4_proc_get_acl(inode, buf, buflen); +} + +ssize_t +nfs4_listxattr(struct dentry *dentry, char *buf, size_t buflen) +{ + size_t len = strlen(XATTR_NAME_NFSV4_ACL) + 1; + + if (buf && buflen < len) + return -ERANGE; + if (buf) + memcpy(buf, XATTR_NAME_NFSV4_ACL, len); + return len; +} + +static void nfs4_clear_inode(struct inode *); static struct super_operations nfs4_sops = { .alloc_inode = nfs_alloc_inode, @@ -1542,6 +1839,7 @@ static int nfs4_fill_super(struct super_ if (data->wsize != 0) server->wsize = nfs_block_size(data->wsize, NULL); server->flags = data->flags & NFS_MOUNT_FLAGMASK; + server->caps = NFS_CAP_ATOMIC_OPEN; server->acregmin = data->acregmin*HZ; server->acregmax = data->acregmax*HZ; @@ -1609,9 +1907,17 @@ static int nfs4_fill_super(struct super_ err = PTR_ERR(clnt); goto out_fail; } + clnt->cl_intr = 1; + clnt->cl_softrtry = 1; clnt->cl_chatty = 1; clp->cl_rpcclient = clnt; clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0); + if (IS_ERR(clp->cl_cred)) { + up_write(&clp->cl_sem); + err = PTR_ERR(clp->cl_cred); + clp->cl_cred = NULL; + goto out_fail; + } memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); nfs_idmap_new(clp); } @@ -1634,8 +1940,6 @@ static int nfs4_fill_super(struct super_ return PTR_ERR(clnt); } - clnt->cl_intr = (server->flags & NFS4_MOUNT_INTR) ? 1 : 0; - clnt->cl_softrtry = (server->flags & NFS4_MOUNT_SOFT) ? 1 : 0; server->client = clnt; if (server->nfs4_state->cl_idmap == NULL) { @@ -1805,6 +2109,7 @@ static void nfs4_kill_super(struct super if (server->hostname != NULL) kfree(server->hostname); kfree(server); + nfs_release_automount_timer(); } static struct file_system_type nfs4_fs_type = { @@ -1815,6 +2120,59 @@ static struct file_system_type nfs4_fs_t .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, }; +/* Constructs the SERVER-side path */ +static inline char *nfs4_path(const struct dentry *dentry, char *buffer, ssize_t buflen) +{ + return nfs_path(NFS_SB(dentry->d_sb)->mnt_path, dentry, buffer, buflen); +} + +static inline char *nfs4_dup_path(const struct dentry *dentry) +{ + char *page = (char *) __get_free_page(GFP_USER); + char *path; + + path = nfs4_path(dentry, page, PAGE_SIZE); + if (!IS_ERR(path)) { + int len = PAGE_SIZE + page - path; + char *tmp = path; + + path = kmalloc(len, GFP_KERNEL); + if (path) + memcpy(path, tmp, len); + else + path = ERR_PTR(-ENOMEM); + } + free_page((unsigned long)page); + return path; +} + +static struct super_block *nfs4_clone_client(struct nfs_server *server, const struct dentry *dentry) +{ + struct nfs4_client *clp = server->nfs4_state; + struct super_block *sb; + + server->mnt_path = nfs4_dup_path(dentry); + if (IS_ERR(server->mnt_path)) { + sb = (struct super_block *)server->mnt_path; + goto err; + } + sb = sget(&nfs4_fs_type, nfs4_compare_super, nfs_set_super, server); + if (IS_ERR(sb) || sb->s_root) + goto free_path; + nfs4_server_capabilities(server, &server->fh); + + down_write(&clp->cl_sem); + atomic_inc(&clp->cl_count); + list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks); + up_write(&clp->cl_sem); + return sb; +free_path: + kfree(server->mnt_path); +err: + server->mnt_path = NULL; + return sb; +} + #define nfs4_init_once(nfsi) \ do { \ INIT_LIST_HEAD(&(nfsi)->open_states); \ @@ -1825,12 +2183,157 @@ static struct file_system_type nfs4_fs_t #define register_nfs4fs() register_filesystem(&nfs4_fs_type) #define unregister_nfs4fs() unregister_filesystem(&nfs4_fs_type) #else +#define nfs4_clone_client(a,b) ERR_PTR(-EINVAL) #define nfs4_init_once(nfsi) \ do { } while (0) #define register_nfs4fs() (0) #define unregister_nfs4fs() #endif +static inline struct super_block *nfs_clone_client(struct nfs_server *server) +{ + return sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server); +} + +struct nfs_clone_mount { + const struct super_block *sb; + const struct dentry *dentry; + struct nfs_fh *fh; + struct nfs_fattr *fattr; +}; + +static struct super_block *clone_nfs_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data) +{ + struct nfs_clone_mount *data = raw_data; + struct nfs_server *server; + struct nfs_server *parent = NFS_SB(data->sb); + struct super_block *sb = ERR_PTR(-EINVAL); + void *err = ERR_PTR(-ENOMEM); + struct inode *root_inode; + struct nfs_fsinfo fsinfo; + int len; + + server = kmalloc(sizeof(struct nfs_server), GFP_KERNEL); + if (server == NULL) + goto out_err; + memcpy(server, parent, sizeof(*server)); + len = strlen(parent->hostname) + 1; + server->hostname = kmalloc(len, GFP_KERNEL); + if (server->hostname == NULL) + goto free_server; + memcpy(server->hostname, parent->hostname, len); + server->fsid = data->fattr->fsid; + nfs_copy_fh(&server->fh, data->fh); + if (rpciod_up() != 0) + goto free_hostname; + + switch (parent->rpc_ops->version) { + case 2: + case 3: + sb = nfs_clone_client(server); + break; + case 4: + sb = nfs4_clone_client(server, data->dentry); + } + if (IS_ERR((err = sb)) || sb->s_root) + goto kill_rpciod; + sb->s_op = data->sb->s_op; + sb->s_blocksize = data->sb->s_blocksize; + sb->s_blocksize_bits = data->sb->s_blocksize_bits; + sb->s_maxbytes = data->sb->s_maxbytes; + + server->client_sys = NULL; + server->client = rpc_clone_client(parent->client); + if (IS_ERR((err = server->client))) + goto out_deactivate; + if (parent->client_sys != NULL) { + server->client_sys = rpc_clone_client(parent->client_sys); + if (IS_ERR((err = server->client_sys))) + goto out_deactivate; + } + + root_inode = nfs_fhget(sb, data->fh, data->fattr); + if (!root_inode) + goto out_deactivate; + sb->s_root = d_alloc_root(root_inode); + if (!sb->s_root) + goto out_put_root; + fsinfo.fattr = data->fattr; + if (NFS_PROTO(root_inode)->fsinfo(server, data->fh, &fsinfo) == 0) + nfs_super_set_maxbytes(sb, fsinfo.maxfilesize); + sb->s_root->d_op = server->rpc_ops->dentry_ops; + sb->s_flags |= MS_ACTIVE; + return sb; +out_put_root: + iput(root_inode); +out_deactivate: + up_write(&sb->s_umount); + deactivate_super(sb); + return (struct super_block *)err; +kill_rpciod: + rpciod_down(); +free_hostname: + kfree(server->hostname); +free_server: + kfree(server); +out_err: + return (struct super_block *)err; +} + +static struct file_system_type clone_nfs_fs_type = { + .owner = THIS_MODULE, + .name = "nfs", + .get_sb = clone_nfs_sb, + .kill_sb = nfs_kill_super, + .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, +}; + +static inline char *nfs_devname(const struct vfsmount *mnt_parent, + const struct dentry *dentry, + char *buffer, ssize_t buflen) +{ + return nfs_path(mnt_parent->mnt_devname, dentry, buffer, buflen); +} + +/** + * nfs_do_submount - set up mountpoint when crossing a filesystem boundary + * @mnt_parent - mountpoint of parent directory + * @dentry - parent directory + * @fh - filehandle for new root dentry + * @fattr - attributes for new root inode + * + */ +struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, + const struct dentry *dentry, struct nfs_fh *fh, + struct nfs_fattr *fattr) +{ + struct nfs_clone_mount mountdata = { + .sb = mnt_parent->mnt_sb, + .dentry = dentry, + .fh = fh, + .fattr = fattr, + }; + struct vfsmount *mnt = ERR_PTR(-ENOMEM); + char *page = (char *) __get_free_page(GFP_USER); + char *devname; + + dprintk("%s: submounting on %s/%s\n", __FUNCTION__, + dentry->d_parent->d_name.name, + dentry->d_name.name); + if (page == NULL) + goto out; + devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE); + if (!IS_ERR(devname)) + mnt = vfs_kern_mount(&clone_nfs_fs_type, 0, devname, &mountdata); + else + mnt = (struct vfsmount *)devname; + free_page((unsigned long)page); +out: + dprintk("%s: done\n", __FUNCTION__); + return mnt; +} + extern int nfs_init_nfspagecache(void); extern void nfs_destroy_nfspagecache(void); extern int nfs_init_readpagecache(void); @@ -1851,6 +2354,9 @@ static struct inode *nfs_alloc_inode(str if (!nfsi) return NULL; nfsi->flags = 0; +#ifdef CONFIG_NFS_V4 + nfsi->nfs4_acl = NULL; +#endif /* CONFIG_NFS_V4 */ return &nfsi->vfs_inode; } @@ -1905,6 +2411,10 @@ static int __init init_nfs_fs(void) { int err; + err = nfs_init_sysctl(); + if (err) + goto out5; + err = nfs_init_nfspagecache(); if (err) goto out4; @@ -1952,6 +2462,8 @@ out2: out3: nfs_destroy_nfspagecache(); out4: + nfs_destroy_sysctl(); +out5: return err; } @@ -1967,6 +2479,7 @@ static void __exit exit_nfs_fs(void) #ifdef CONFIG_PROC_FS rpc_proc_unregister("nfs"); #endif + nfs_destroy_sysctl(); unregister_filesystem(&nfs_fs_type); unregister_nfs4fs(); } Index: linux-2.6.11/fs/nfs/mount_clnt.c =================================================================== --- linux-2.6.11.orig/fs/nfs/mount_clnt.c +++ linux-2.6.11/fs/nfs/mount_clnt.c @@ -31,7 +31,7 @@ static struct rpc_clnt * mnt_create(char *, struct sockaddr_in *, int, int); -struct rpc_program mnt_program; +static struct rpc_program mnt_program; struct mnt_fhstatus { unsigned int status; @@ -174,7 +174,7 @@ static struct rpc_version * mnt_version[ static struct rpc_stat mnt_stats; -struct rpc_program mnt_program = { +static struct rpc_program mnt_program = { .name = "mount", .number = NFS_MNT_PROGRAM, .nrvers = sizeof(mnt_version)/sizeof(mnt_version[0]), Index: linux-2.6.11/fs/nfs/namespace.c =================================================================== --- /dev/null +++ linux-2.6.11/fs/nfs/namespace.c @@ -0,0 +1,111 @@ +/* + * linux/fs/nfs/namespace.c + * + * Copyright (C) 2005 Trond Myklebust + * + * NFS namespace + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define NFSDBG_FACILITY NFSDBG_VFS + +static LIST_HEAD(nfs_automount_list); +static void nfs_expire_automounts(void *list); +static DECLARE_WORK(nfs_automount_task, nfs_expire_automounts, &nfs_automount_list); +int nfs_mountpoint_expiry_timeout = 500 * HZ; + +/* + * nfs_follow_mountpoint - handle crossing a mountpoint on the server + * @dentry - dentry of mountpoint + * @nd - nameidata info + * + * When we encounter a mountpoint on the server, we want to set up + * a mountpoint on the client too, to prevent inode numbers from + * colliding, and to allow "df" to work properly. + * On NFSv4, we also want to allow for the fact that different + * filesystems may be migrated to different servers in a failover + * situation, and that different filesystems may want to use + * different security flavours. + */ +static int nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) +{ + struct vfsmount *mnt; + struct nfs_server *server = NFS_SERVER(dentry->d_inode); + struct dentry *parent; + struct nfs_fh fh; + struct nfs_fattr fattr; + int err; + + BUG_ON(IS_ROOT(dentry)); + dprintk("%s: enter\n", __FUNCTION__); + dput(nd->dentry); + nd->dentry = dget(dentry); + if (d_mountpoint(nd->dentry)) + goto out_follow; + /* Look it up again */ + parent = dget_parent(nd->dentry); + err = server->rpc_ops->lookup(parent->d_inode, &nd->dentry->d_name, &fh, &fattr); + dput(parent); + if (err != 0) + goto out_err; + mnt = nfs_do_submount(nd->mnt, nd->dentry, &fh, &fattr); + if (IS_ERR(mnt)) { + err = PTR_ERR(mnt); + goto out_err; + } + mntget(mnt); + err = do_add_mount(mnt, nd, nd->mnt->mnt_flags, &nfs_automount_list); + if (err < 0) { + mntput(mnt); + if (err == -EBUSY) + goto out_follow; + goto out_err; + } + mntput(nd->mnt); + dput(nd->dentry); + nd->mnt = mnt; + nd->dentry = dget(mnt->mnt_root); + schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); +out: + dprintk("%s: done, returned %d\n", __FUNCTION__, err); + return err; +out_err: + path_release(nd); + goto out; +out_follow: + while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry)) + ; + err = 0; + goto out; +} + +struct inode_operations nfs_mountpoint_inode_operations = { + .follow_link = nfs_follow_mountpoint, + .getattr = nfs_getattr, +}; + +static void nfs_expire_automounts(void *data) +{ + struct list_head *list = (struct list_head *)data; + + mark_mounts_for_expiry(list); + if (!list_empty(list)) + schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); +} + +void nfs_release_automount_timer(void) +{ + if (list_empty(&nfs_automount_list)) { + cancel_delayed_work(&nfs_automount_task); + flush_scheduled_work(); + } +} Index: linux-2.6.11/fs/nfs/nfs2xdr.c =================================================================== --- linux-2.6.11.orig/fs/nfs/nfs2xdr.c +++ linux-2.6.11/fs/nfs/nfs2xdr.c @@ -131,7 +131,8 @@ xdr_decode_fattr(u32 *p, struct nfs_fatt fattr->du.nfs2.blocksize = ntohl(*p++); rdev = ntohl(*p++); fattr->du.nfs2.blocks = ntohl(*p++); - fattr->fsid_u.nfs3 = ntohl(*p++); + fattr->fsid.major = ntohl(*p++); + fattr->fsid.minor = 0; fattr->fileid = ntohl(*p++); p = xdr_decode_time(p, &fattr->atime); p = xdr_decode_time(p, &fattr->mtime); Index: linux-2.6.11/fs/nfs/nfs3proc.c =================================================================== --- linux-2.6.11.orig/fs/nfs/nfs3proc.c +++ linux-2.6.11/fs/nfs/nfs3proc.c @@ -295,7 +295,7 @@ static int nfs3_proc_commit(struct nfs_w * Create a regular file. * For now, we don't implement O_EXCL. */ -static struct inode * +static int nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, int flags) { @@ -342,29 +342,19 @@ again: break; case NFS3_CREATE_UNCHECKED: - goto exit; + goto out; } goto again; } -exit: - dprintk("NFS reply create: %d\n", status); - + if (status == 0) + status = nfs_instantiate(dentry, &fhandle, &fattr); if (status != 0) goto out; - if (fhandle.size == 0 || !(fattr.valid & NFS_ATTR_FATTR)) { - status = nfs3_proc_lookup(dir, &dentry->d_name, &fhandle, &fattr); - if (status != 0) - goto out; - } /* When we created the file with exclusive semantics, make * sure we set the attributes afterwards. */ if (arg.createmode == NFS3_CREATE_EXCLUSIVE) { - struct nfs3_sattrargs arg = { - .fh = &fhandle, - .sattr = sattr, - }; dprintk("NFS call setattr (post-create)\n"); if (!(sattr->ia_valid & ATTR_ATIME_SET)) @@ -375,20 +365,13 @@ exit: /* Note: we could use a guarded setattr here, but I'm * not sure this buys us anything (and I'd have * to revamp the NFSv3 XDR code) */ - fattr.valid = 0; - status = rpc_call(NFS_CLIENT(dir), NFS3PROC_SETATTR, - &arg, &fattr, 0); + status = nfs3_proc_setattr(dentry, &fattr, sattr); + nfs_refresh_inode(dentry->d_inode, &fattr); dprintk("NFS reply setattr (post-create): %d\n", status); } - if (status == 0) { - struct inode *inode; - inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); - if (inode) - return inode; - status = -ENOMEM; - } out: - return ERR_PTR(status); + dprintk("NFS reply create: %d\n", status); + return status; } static int @@ -540,28 +523,30 @@ nfs3_proc_symlink(struct inode *dir, str } static int -nfs3_proc_mkdir(struct inode *dir, struct qstr *name, struct iattr *sattr, - struct nfs_fh *fhandle, struct nfs_fattr *fattr) +nfs3_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr) { - struct nfs_fattr dir_attr; + struct nfs_fh fhandle; + struct nfs_fattr fattr, dir_attr; struct nfs3_mkdirargs arg = { .fh = NFS_FH(dir), - .name = name->name, - .len = name->len, + .name = dentry->d_name.name, + .len = dentry->d_name.len, .sattr = sattr }; struct nfs3_diropres res = { .dir_attr = &dir_attr, - .fh = fhandle, - .fattr = fattr + .fh = &fhandle, + .fattr = &fattr }; int status; - dprintk("NFS call mkdir %s\n", name->name); + dprintk("NFS call mkdir %s\n", dentry->d_name.name); dir_attr.valid = 0; - fattr->valid = 0; + fattr.valid = 0; status = rpc_call(NFS_CLIENT(dir), NFS3PROC_MKDIR, &arg, &res, 0); nfs_refresh_inode(dir, &dir_attr); + if (status == 0) + status = nfs_instantiate(dentry, &fhandle, &fattr); dprintk("NFS reply mkdir: %d\n", status); return status; } @@ -639,23 +624,24 @@ nfs3_proc_readdir(struct dentry *dentry, } static int -nfs3_proc_mknod(struct inode *dir, struct qstr *name, struct iattr *sattr, - dev_t rdev, struct nfs_fh *fh, struct nfs_fattr *fattr) +nfs3_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr, + dev_t rdev) { - struct nfs_fattr dir_attr; + struct nfs_fh fh; + struct nfs_fattr fattr, dir_attr; struct nfs3_mknodargs arg = { .fh = NFS_FH(dir), - .name = name->name, - .len = name->len, + .name = dentry->d_name.name, + .len = dentry->d_name.len, .sattr = sattr, .rdev = rdev }; struct nfs3_diropres res = { .dir_attr = &dir_attr, - .fh = fh, - .fattr = fattr + .fh = &fh, + .fattr = &fattr }; - int status; + int status; switch (sattr->ia_mode & S_IFMT) { case S_IFBLK: arg.type = NF3BLK; break; @@ -665,12 +651,14 @@ nfs3_proc_mknod(struct inode *dir, struc default: return -EINVAL; } - dprintk("NFS call mknod %s %u:%u\n", name->name, + dprintk("NFS call mknod %s %u:%u\n", dentry->d_name.name, MAJOR(rdev), MINOR(rdev)); dir_attr.valid = 0; - fattr->valid = 0; + fattr.valid = 0; status = rpc_call(NFS_CLIENT(dir), NFS3PROC_MKNOD, &arg, &res, 0); nfs_refresh_inode(dir, &dir_attr); + if (status == 0) + status = nfs_instantiate(dentry, &fh, &fattr); dprintk("NFS reply mknod: %d\n", status); return status; } @@ -838,6 +826,7 @@ struct nfs_rpc_ops nfs_v3_clientops = { .version = 3, /* protocol version */ .dentry_ops = &nfs_dentry_operations, .dir_inode_ops = &nfs_dir_inode_operations, + .file_inode_ops = &nfs_file_inode_operations, .getroot = nfs3_proc_get_root, .getattr = nfs3_proc_getattr, .setattr = nfs3_proc_setattr, Index: linux-2.6.11/fs/nfs/nfs3xdr.c =================================================================== --- linux-2.6.11.orig/fs/nfs/nfs3xdr.c +++ linux-2.6.11/fs/nfs/nfs3xdr.c @@ -160,7 +160,8 @@ xdr_decode_fattr(u32 *p, struct nfs_fatt if (MAJOR(fattr->rdev) != major || MINOR(fattr->rdev) != minor) fattr->rdev = 0; - p = xdr_decode_hyper(p, &fattr->fsid_u.nfs3); + p = xdr_decode_hyper(p, &fattr->fsid.major); + fattr->fsid.minor = 0; p = xdr_decode_hyper(p, &fattr->fileid); p = xdr_decode_time3(p, &fattr->atime); p = xdr_decode_time3(p, &fattr->mtime); Index: linux-2.6.11/fs/nfs/nfs4_fs.h =================================================================== --- /dev/null +++ linux-2.6.11/fs/nfs/nfs4_fs.h @@ -0,0 +1,264 @@ +/* + * linux/fs/nfs/nfs4_fs.h + * + * Copyright (C) 2005 Trond Myklebust + * + * NFSv4-specific filesystem definitions and declarations + */ + +#ifndef __LINUX_FS_NFS_NFS4_FS_H +#define __LINUX_FS_NFS_NFS4_FS_H + +#ifdef CONFIG_NFS_V4 + +struct idmap; + +/* + * In a seqid-mutating op, this macro controls which error return + * values trigger incrementation of the seqid. + * + * from rfc 3010: + * The client MUST monotonically increment the sequence number for the + * CLOSE, LOCK, LOCKU, OPEN, OPEN_CONFIRM, and OPEN_DOWNGRADE + * operations. This is true even in the event that the previous + * operation that used the sequence number received an error. The only + * exception to this rule is if the previous operation received one of + * the following errors: NFSERR_STALE_CLIENTID, NFSERR_STALE_STATEID, + * NFSERR_BAD_STATEID, NFSERR_BAD_SEQID, NFSERR_BADXDR, + * NFSERR_RESOURCE, NFSERR_NOFILEHANDLE. + * + */ +#define seqid_mutating_err(err) \ +(((err) != NFSERR_STALE_CLIENTID) && \ + ((err) != NFSERR_STALE_STATEID) && \ + ((err) != NFSERR_BAD_STATEID) && \ + ((err) != NFSERR_BAD_SEQID) && \ + ((err) != NFSERR_BAD_XDR) && \ + ((err) != NFSERR_RESOURCE) && \ + ((err) != NFSERR_NOFILEHANDLE)) + +enum nfs4_client_state { + NFS4CLNT_OK = 0, +}; + +/* + * The nfs4_client identifies our client state to the server. + */ +struct nfs4_client { + struct list_head cl_servers; /* Global list of servers */ + struct in_addr cl_addr; /* Server identifier */ + u64 cl_clientid; /* constant */ + nfs4_verifier cl_confirm; + unsigned long cl_state; + + u32 cl_lockowner_id; + + /* + * The following rwsem ensures exclusive access to the server + * while we recover the state following a lease expiration. + */ + struct rw_semaphore cl_sem; + + struct list_head cl_delegations; + struct list_head cl_state_owners; + struct list_head cl_unused; + int cl_nunused; + spinlock_t cl_lock; + atomic_t cl_count; + + struct rpc_clnt * cl_rpcclient; + struct rpc_cred * cl_cred; + + struct list_head cl_superblocks; /* List of nfs_server structs */ + + unsigned long cl_lease_time; + unsigned long cl_last_renewal; + struct work_struct cl_renewd; + struct work_struct cl_recoverd; + + wait_queue_head_t cl_waitq; + struct rpc_wait_queue cl_rpcwaitq; + + /* used for the setclientid verifier */ + struct timespec cl_boot_time; + + /* idmapper */ + struct idmap * cl_idmap; + + /* Our own IP address, as a null-terminated string. + * This is used to generate the clientid, and the callback address. + */ + char cl_ipaddr[16]; + unsigned char cl_id_uniquifier; +}; + +/* + * NFS4 state_owners and lock_owners are simply labels for ordered + * sequences of RPC calls. Their sole purpose is to provide once-only + * semantics by allowing the server to identify replayed requests. + * + * The ->so_sema is held during all state_owner seqid-mutating operations: + * OPEN, OPEN_DOWNGRADE, and CLOSE. Its purpose is to properly serialize + * so_seqid. + */ +struct nfs4_state_owner { + struct list_head so_list; /* per-clientid list of state_owners */ + struct nfs4_client *so_client; + u32 so_id; /* 32-bit identifier, unique */ + struct semaphore so_sema; + u32 so_seqid; /* protected by so_sema */ + atomic_t so_count; + + struct rpc_cred *so_cred; /* Associated cred */ + struct list_head so_states; + struct list_head so_delegations; +}; + +/* + * struct nfs4_state maintains the client-side state for a given + * (state_owner,inode) tuple (OPEN) or state_owner (LOCK). + * + * OPEN: + * In order to know when to OPEN_DOWNGRADE or CLOSE the state on the server, + * we need to know how many files are open for reading or writing on a + * given inode. This information too is stored here. + * + * LOCK: one nfs4_state (LOCK) to hold the lock stateid nfs4_state(OPEN) + */ + +struct nfs4_lock_state { + struct list_head ls_locks; /* Other lock stateids */ + fl_owner_t ls_owner; /* POSIX lock owner */ +#define NFS_LOCK_INITIALIZED 1 + int ls_flags; + u32 ls_seqid; + u32 ls_id; + nfs4_stateid ls_stateid; + atomic_t ls_count; +}; + +/* bits for nfs4_state->flags */ +enum { + LK_STATE_IN_USE, + NFS_DELEGATED_STATE, +}; + +struct nfs4_state { + struct list_head open_states; /* List of states for the same state_owner */ + struct list_head inode_states; /* List of states for the same inode */ + struct list_head lock_states; /* List of subservient lock stateids */ + + struct nfs4_state_owner *owner; /* Pointer to the open owner */ + struct inode *inode; /* Pointer to the inode */ + + unsigned long flags; /* Do we hold any locks? */ + struct semaphore lock_sema; /* Serializes file locking operations */ + rwlock_t state_lock; /* Protects the lock_states list */ + + nfs4_stateid stateid; + + unsigned int nreaders; + unsigned int nwriters; + int state; /* State on the server (R,W, or RW) */ + atomic_t count; +}; + + +struct nfs4_exception { + long timeout; + int retry; +}; + +struct nfs4_state_recovery_ops { + int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *); + int (*recover_lock)(struct nfs4_state *, struct file_lock *); +}; + +extern struct dentry_operations nfs4_dentry_operations; +extern struct inode_operations nfs4_dir_inode_operations; +extern struct inode_operations nfs4_file_inode_operations; + +/* inode.c */ +extern ssize_t nfs4_getxattr(struct dentry *, const char *, void *, size_t); +extern int nfs4_setxattr(struct dentry *, const char *, const void *, size_t, int); +extern ssize_t nfs4_listxattr(struct dentry *, char *, size_t); + + +/* nfs4proc.c */ +extern int nfs4_map_errors(int err); +extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short); +extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); +extern int nfs4_proc_async_renew(struct nfs4_client *); +extern int nfs4_proc_renew(struct nfs4_client *); +extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode); +extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); +extern int nfs4_open_revalidate(struct inode *, struct dentry *, int); +extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); +extern ssize_t nfs4_proc_get_acl(struct inode *, void *buf, size_t buflen); +extern int nfs4_proc_set_acl(struct inode *, const void *buf, size_t buflen); +extern void nfs4_zap_acl_attr(struct inode *inode); +extern int nfs4_proc_fs_locations(struct inode *dir, struct dentry *dentry, + struct nfs_fs_locations *fs_locations, struct page *page); + +extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops; +extern struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops; + +extern const u32 nfs4_fattr_bitmap[2]; +extern const u32 nfs4_statfs_bitmap[2]; +extern const u32 nfs4_pathconf_bitmap[2]; +extern const u32 nfs4_fsinfo_bitmap[2]; + +/* nfs4renewd.c */ +extern void nfs4_schedule_state_renewal(struct nfs4_client *); +extern void nfs4_renewd_prepare_shutdown(struct nfs_server *); +extern void nfs4_kill_renewd(struct nfs4_client *); +extern void nfs4_renew_state(void *); + +/* nfs4state.c */ +extern void init_nfsv4_state(struct nfs_server *); +extern void destroy_nfsv4_state(struct nfs_server *); +extern struct nfs4_client *nfs4_get_client(struct in_addr *); +extern void nfs4_put_client(struct nfs4_client *clp); +extern int nfs4_init_client(struct nfs4_client *clp); +extern struct nfs4_client *nfs4_find_client(struct in_addr *); +extern u32 nfs4_alloc_lockowner_id(struct nfs4_client *); + +extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *); +extern void nfs4_put_state_owner(struct nfs4_state_owner *); +extern void nfs4_drop_state_owner(struct nfs4_state_owner *); +extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); +extern void nfs4_put_open_state(struct nfs4_state *); +extern void nfs4_close_state(struct nfs4_state *, mode_t); +extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mode_t mode); +extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); +extern void nfs4_schedule_state_recovery(struct nfs4_client *); +extern struct nfs4_lock_state *nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t); +extern struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t); +extern void nfs4_put_lock_state(struct nfs4_lock_state *state); +extern void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *ls); +extern void nfs4_notify_setlk(struct nfs4_state *, struct file_lock *, struct nfs4_lock_state *); +extern void nfs4_notify_unlck(struct nfs4_state *, struct file_lock *, struct nfs4_lock_state *); +extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t); + +extern const nfs4_stateid zero_stateid; + +/* nfs4xdr.c */ +extern uint32_t *nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus); +extern struct rpc_procinfo nfs4_procedures[]; + +struct nfs4_mount_data; + +/* callback_xdr.c */ +extern struct svc_version nfs4_callback_version1; + +#else + +#define init_nfsv4_state(server) do { } while (0) +#define destroy_nfsv4_state(server) do { } while (0) +#define nfs4_put_state_owner(inode, owner) do { } while (0) +#define nfs4_put_open_state(state) do { } while (0) +#define nfs4_close_state(a, b) do { } while (0) +#define nfs4_zap_acl_attr(inode) do { } while (0) + +#endif /* CONFIG_NFS_V4 */ +#endif /* __LINUX_FS_NFS_NFS4_FS.H */ Index: linux-2.6.11/fs/nfs/nfs4proc.c =================================================================== --- linux-2.6.11.orig/fs/nfs/nfs4proc.c +++ linux-2.6.11/fs/nfs/nfs4proc.c @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #include #include +#include "nfs4_fs.h" #include "delegation.h" #define NFSDBG_FACILITY NFSDBG_PROC @@ -57,16 +59,15 @@ static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); static int nfs4_async_handle_error(struct rpc_task *, struct nfs_server *); static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry); +static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_exception *exception); extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus); extern struct rpc_procinfo nfs4_procedures[]; -extern nfs4_stateid zero_stateid; - /* Prevent leaks of NFSv4 errors into userland */ -static inline int nfs4_map_errors(int err) +int nfs4_map_errors(int err) { if (err < -1000) { - printk(KERN_WARNING "%s could not handle NFSv4 error %d\n", + dprintk("%s could not handle NFSv4 error %d\n", __FUNCTION__, -err); return -EIO; } @@ -102,7 +103,7 @@ const u32 nfs4_statfs_bitmap[2] = { | FATTR4_WORD1_SPACE_TOTAL }; -u32 nfs4_pathconf_bitmap[2] = { +const u32 nfs4_pathconf_bitmap[2] = { FATTR4_WORD0_MAXLINK | FATTR4_WORD0_MAXNAME, 0 @@ -188,6 +189,23 @@ static void update_changeattr(struct ino nfsi->change_attr = cinfo->after; } +static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags) +{ + struct inode *inode = state->inode; + + open_flags &= (FMODE_READ|FMODE_WRITE); + /* Protect against nfs4_find_state() */ + spin_lock(&inode->i_lock); + state->state |= open_flags; + /* NB! List reordering - see the reclaim code for why. */ + if ((open_flags & FMODE_WRITE) && 0 == state->nwriters++) + list_move(&state->open_states, &state->owner->so_states); + if (open_flags & FMODE_READ) + state->nreaders++; + memcpy(&state->stateid, stateid, sizeof(state->stateid)); + spin_unlock(&inode->i_lock); +} + /* * OPEN_RECLAIM: * reclaim state on the server after a reboot. @@ -244,7 +262,7 @@ static int _nfs4_open_reclaim(struct nfs return status; } -int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) +static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) { struct nfs_server *server = NFS_SERVER(state->inode); struct nfs4_exception exception = { }; @@ -332,7 +350,7 @@ int nfs4_open_delegation_recall(struct d return err; } -static int _nfs4_proc_open_confirm(struct rpc_clnt *clnt, const struct nfs_fh *fh, struct nfs4_state_owner *sp, nfs4_stateid *stateid) +static inline int _nfs4_proc_open_confirm(struct rpc_clnt *clnt, const struct nfs_fh *fh, struct nfs4_state_owner *sp, nfs4_stateid *stateid) { struct nfs_open_confirmargs arg = { .fh = fh, @@ -355,11 +373,49 @@ static int _nfs4_proc_open_confirm(struc return status; } -static int _nfs4_do_access(struct inode *inode, struct rpc_cred *cred, int mask) +static int _nfs4_proc_open(struct inode *dir, struct nfs4_state_owner *sp, struct nfs_openargs *o_arg, struct nfs_openres *o_res) +{ + struct nfs_server *server = NFS_SERVER(dir); + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN], + .rpc_argp = o_arg, + .rpc_resp = o_res, + .rpc_cred = sp->so_cred, + }; + int status; + + /* Update sequence id. The caller must serialize! */ + o_arg->seqid = sp->so_seqid; + o_arg->id = sp->so_id; + o_arg->clientid = sp->so_client->cl_clientid; + + status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR); + nfs4_increment_seqid(status, sp); + if (status != 0) + goto out; + update_changeattr(dir, &o_res->cinfo); + if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) { + status = _nfs4_proc_open_confirm(server->client, &o_res->fh, + sp, &o_res->stateid); + if (status != 0) + goto out; + } + if (!(o_res->f_attr->valid & NFS_ATTR_FATTR)) + status = server->rpc_ops->getattr(server, &o_res->fh, o_res->f_attr); +out: + return status; +} + +static int _nfs4_do_access(struct inode *inode, struct rpc_cred *cred, int openflags) { struct nfs_access_entry cache; + int mask = 0; int status; + if (openflags & FMODE_READ) + mask |= MAY_READ; + if (openflags & FMODE_WRITE) + mask |= MAY_WRITE; status = nfs_access_get_cached(inode, cred, &cache); if (status == 0) goto out; @@ -379,9 +435,97 @@ out: } /* + * OPEN_EXPIRED: + * reclaim state on the server after a network partition. + * Assumes caller holds the appropriate lock + */ +static int _nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) +{ + struct dentry *parent = dget_parent(dentry); + struct inode *dir = parent->d_inode; + struct inode *inode = state->inode; + struct nfs_server *server = NFS_SERVER(dir); + struct nfs_delegation *delegation = NFS_I(inode)->delegation; + struct nfs_fattr f_attr = { + .valid = 0, + }; + struct nfs_openargs o_arg = { + .fh = NFS_FH(dir), + .open_flags = state->state, + .name = &dentry->d_name, + .bitmask = server->attr_bitmask, + .claim = NFS4_OPEN_CLAIM_NULL, + }; + struct nfs_openres o_res = { + .f_attr = &f_attr, + .server = server, + }; + uint32_t generation; + int status = 0; + + if (delegation != NULL && !(delegation->flags & NFS_DELEGATION_NEED_RECLAIM)) { + status = _nfs4_do_access(inode, sp->so_cred, state->state); + if (status < 0) + goto out; + memcpy(&state->stateid, &delegation->stateid, sizeof(state->stateid)); + set_bit(NFS_DELEGATED_STATE, &state->flags); + goto out; + } + /* If we are in a failover situation, recover path first */ + status = nfs_try_migrate_inode(dir, parent); + if (status != 0) + goto out_nodeleg; + generation = server->generation; + status = _nfs4_proc_open(dir, sp, &o_arg, &o_res); + if (status != 0) + goto out_nodeleg; + status = nfs_try_migrate_filehandle(inode, &o_res.fh, o_res.f_attr, generation); + if (status != 0) + goto out_stale; + memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid)); + if (o_res.delegation_type != 0) { + if (!(delegation->flags & NFS_DELEGATION_NEED_RECLAIM)) + nfs_inode_set_delegation(inode, sp->so_cred, &o_res); + else + nfs_inode_reclaim_delegation(inode, sp->so_cred, &o_res); + } +out_nodeleg: + clear_bit(NFS_DELEGATED_STATE, &state->flags); +out: + dput(parent); + return status; +out_stale: + /* Invalidate the state owner so we don't ever use it again */ + nfs4_drop_state_owner(sp); + d_drop(dentry); + /* Should we be trying to close that stateid? */ + goto out_nodeleg; +} + +static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state) +{ + struct nfs_inode *nfsi = NFS_I(state->inode); + struct nfs_open_context *ctx; + int status; + + spin_lock(&state->inode->i_lock); + list_for_each_entry(ctx, &nfsi->open_files, list) { + if (ctx->state != state) + continue; + get_nfs_open_context(ctx); + spin_unlock(&state->inode->i_lock); + status = _nfs4_open_expired(sp, state, ctx->dentry); + put_nfs_open_context(ctx); + return status; + } + spin_unlock(&state->inode->i_lock); + return -ENOENT; +} + +/* * Returns an nfs4_state + an extra reference to the inode */ -int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred, struct nfs4_state **res) +static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred, struct nfs4_state **res) { struct nfs_delegation *delegation; struct nfs_server *server = NFS_SERVER(inode); @@ -390,7 +534,6 @@ int _nfs4_open_delegated(struct inode *i struct nfs4_state_owner *sp = NULL; struct nfs4_state *state = NULL; int open_flags = flags & (FMODE_READ|FMODE_WRITE); - int mask = 0; int err; /* Protect against reboot recovery - NOTE ORDER! */ @@ -424,20 +567,12 @@ int _nfs4_open_delegated(struct inode *i goto out_err; lock_kernel(); - err = _nfs4_do_access(inode, cred, mask); + err = _nfs4_do_access(inode, cred, open_flags); unlock_kernel(); if (err != 0) goto out_err; - spin_lock(&inode->i_lock); - memcpy(state->stateid.data, delegation->stateid.data, - sizeof(state->stateid.data)); - state->state |= open_flags; - if (open_flags & FMODE_READ) - state->nreaders++; - if (open_flags & FMODE_WRITE) - state->nwriters++; set_bit(NFS_DELEGATED_STATE, &state->flags); - spin_unlock(&inode->i_lock); + update_open_stateid(state, &delegation->stateid, open_flags); out_ok: up(&sp->so_sema); nfs4_put_state_owner(sp); @@ -500,12 +635,6 @@ static int _nfs4_do_open(struct inode *d .f_attr = &f_attr, .server = server, }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN], - .rpc_argp = &o_arg, - .rpc_resp = &o_res, - .rpc_cred = cred, - }; /* Protect against reboot recovery conflicts */ down_read(&clp->cl_sem); @@ -522,26 +651,10 @@ static int _nfs4_do_open(struct inode *d o_arg.u.attrs = sattr; /* Serialization for the sequence id */ down(&sp->so_sema); - o_arg.seqid = sp->so_seqid; - o_arg.id = sp->so_id; - o_arg.clientid = clp->cl_clientid, - status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR); - nfs4_increment_seqid(status, sp); - if (status) + status = _nfs4_proc_open(dir, sp, &o_arg, &o_res); + if (status != 0) goto out_err; - update_changeattr(dir, &o_res.cinfo); - if(o_res.rflags & NFS4_OPEN_RESULT_CONFIRM) { - status = _nfs4_proc_open_confirm(server->client, &o_res.fh, - sp, &o_res.stateid); - if (status != 0) - goto out_err; - } - if (!(f_attr.valid & NFS_ATTR_FATTR)) { - status = server->rpc_ops->getattr(server, &o_res.fh, &f_attr); - if (status < 0) - goto out_err; - } status = -ENOMEM; inode = nfs_fhget(dir->i_sb, &o_res.fh, &f_attr); @@ -550,14 +663,7 @@ static int _nfs4_do_open(struct inode *d state = nfs4_get_open_state(inode, sp); if (!state) goto out_err; - memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid)); - spin_lock(&inode->i_lock); - if (flags & FMODE_READ) - state->nreaders++; - if (flags & FMODE_WRITE) - state->nwriters++; - state->state |= flags & (FMODE_READ|FMODE_WRITE); - spin_unlock(&inode->i_lock); + update_open_stateid(state, &o_res.stateid, flags); if (o_res.delegation_type != 0) nfs_inode_set_delegation(inode, cred, &o_res); up(&sp->so_sema); @@ -581,7 +687,7 @@ out_err: } -struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, struct iattr *sattr, struct rpc_cred *cred) +static struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, struct iattr *sattr, struct rpc_cred *cred) { struct nfs4_exception exception = { }; struct nfs4_state *res; @@ -645,7 +751,7 @@ static int _nfs4_do_setattr(struct nfs_s return rpc_call_sync(server->client, &msg, 0); } -int nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr, +static int nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr, struct nfs_fh *fhandle, struct iattr *sattr, struct nfs4_state *state) { @@ -680,7 +786,6 @@ static void nfs4_close_done(struct rpc_t nfs4_increment_seqid(task->tk_status, sp); switch (task->tk_status) { case 0: - state->state = calldata->arg.open_flags; memcpy(&state->stateid, &calldata->res.stateid, sizeof(state->stateid)); break; @@ -695,6 +800,7 @@ static void nfs4_close_done(struct rpc_t return; } } + state->state = calldata->arg.open_flags; nfs4_put_open_state(state); up(&sp->so_sema); nfs4_put_state_owner(sp); @@ -774,6 +880,8 @@ nfs4_atomic_open(struct inode *dir, stru } cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + if (IS_ERR(cred)) + return (struct inode *)cred; state = nfs4_do_open(dir, dentry, nd->intent.open.flags, &attr, cred); put_rpccred(cred); if (IS_ERR(state)) @@ -789,6 +897,8 @@ nfs4_open_revalidate(struct inode *dir, struct inode *inode; cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + if (IS_ERR(cred)) + return PTR_ERR(cred); state = nfs4_open_delegated(dentry->d_inode, openflags, cred); if (IS_ERR(state)) state = nfs4_do_open(dir, dentry, openflags, NULL, cred); @@ -833,7 +943,7 @@ static int _nfs4_server_capabilities(str return status; } -static int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle) +int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle) { struct nfs4_exception exception = { }; int err; @@ -1009,6 +1119,8 @@ nfs4_proc_setattr(struct dentry *dentry, if (size_change) { struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); + if (IS_ERR(cred)) + return PTR_ERR(cred); state = nfs4_find_state(inode, cred, FMODE_WRITE); if (state == NULL) { state = nfs4_open_delegated(dentry->d_inode, @@ -1315,33 +1427,37 @@ static int nfs4_proc_commit(struct nfs_w * opens the file O_RDONLY. This will all be resolved with the VFS changes. */ -static struct inode * +static int nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, int flags) { - struct inode *inode; - struct nfs4_state *state = NULL; + struct nfs4_state *state; struct rpc_cred *cred; + int status = 0; cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + if (IS_ERR(cred)) { + status = PTR_ERR(cred); + goto out; + } state = nfs4_do_open(dir, dentry, flags, sattr, cred); put_rpccred(cred); - if (!IS_ERR(state)) { - inode = state->inode; - if (flags & O_EXCL) { - struct nfs_fattr fattr; - int status; - status = nfs4_do_setattr(NFS_SERVER(dir), &fattr, - NFS_FH(inode), sattr, state); - if (status != 0) { - nfs4_close_state(state, flags); - iput(inode); - inode = ERR_PTR(status); - } - } - } else - inode = (struct inode *)state; - return inode; + if (IS_ERR(state)) { + status = PTR_ERR(state); + goto out; + } + d_instantiate(dentry, state->inode); + if (flags & O_EXCL) { + struct nfs_fattr fattr; + status = nfs4_do_setattr(NFS_SERVER(dir), &fattr, + NFS_FH(state->inode), sattr, state); + if (status == 0) + goto out; + } else if (flags != 0) + goto out; + nfs4_close_state(state, flags); +out: + return status; } static int _nfs4_proc_remove(struct inode *dir, struct qstr *name) @@ -1539,23 +1655,24 @@ static int nfs4_proc_symlink(struct inod return err; } -static int _nfs4_proc_mkdir(struct inode *dir, struct qstr *name, - struct iattr *sattr, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) +static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry, + struct iattr *sattr) { struct nfs_server *server = NFS_SERVER(dir); + struct nfs_fh fhandle; + struct nfs_fattr fattr; struct nfs4_create_arg arg = { .dir_fh = NFS_FH(dir), .server = server, - .name = name, + .name = &dentry->d_name, .attrs = sattr, .ftype = NF4DIR, .bitmask = server->attr_bitmask, }; struct nfs4_create_res res = { .server = server, - .fh = fhandle, - .fattr = fattr, + .fh = &fhandle, + .fattr = &fattr, }; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE], @@ -1564,24 +1681,24 @@ static int _nfs4_proc_mkdir(struct inode }; int status; - fattr->valid = 0; + fattr.valid = 0; status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - if (!status) + if (!status) { update_changeattr(dir, &res.dir_cinfo); + status = nfs_instantiate(dentry, &fhandle, &fattr); + } return status; } -static int nfs4_proc_mkdir(struct inode *dir, struct qstr *name, - struct iattr *sattr, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) +static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry, + struct iattr *sattr) { struct nfs4_exception exception = { }; int err; do { err = nfs4_handle_exception(NFS_SERVER(dir), - _nfs4_proc_mkdir(dir, name, sattr, - fhandle, fattr), + _nfs4_proc_mkdir(dir, dentry, sattr), &exception); } while (exception.retry); return err; @@ -1596,6 +1713,7 @@ static int _nfs4_proc_readdir(struct den .pages = &page, .pgbase = 0, .count = count, + .bitmask = NFS_SERVER(dentry->d_inode)->attr_bitmask, }; struct nfs4_readdir_res res; struct rpc_message msg = { @@ -1630,22 +1748,23 @@ static int nfs4_proc_readdir(struct dent return err; } -static int _nfs4_proc_mknod(struct inode *dir, struct qstr *name, - struct iattr *sattr, dev_t rdev, struct nfs_fh *fh, - struct nfs_fattr *fattr) +static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry, + struct iattr *sattr, dev_t rdev) { struct nfs_server *server = NFS_SERVER(dir); + struct nfs_fh fh; + struct nfs_fattr fattr; struct nfs4_create_arg arg = { .dir_fh = NFS_FH(dir), .server = server, - .name = name, + .name = &dentry->d_name, .attrs = sattr, .bitmask = server->attr_bitmask, }; struct nfs4_create_res res = { .server = server, - .fh = fh, - .fattr = fattr, + .fh = &fh, + .fattr = &fattr, }; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE], @@ -1655,7 +1774,7 @@ static int _nfs4_proc_mknod(struct inode int status; int mode = sattr->ia_mode; - fattr->valid = 0; + fattr.valid = 0; BUG_ON(!(sattr->ia_valid & ATTR_MODE)); BUG_ON(!S_ISFIFO(mode) && !S_ISBLK(mode) && !S_ISCHR(mode) && !S_ISSOCK(mode)); @@ -1675,21 +1794,21 @@ static int _nfs4_proc_mknod(struct inode arg.ftype = NF4SOCK; status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - if (!status) + if (status == 0) { update_changeattr(dir, &res.dir_cinfo); + status = nfs_instantiate(dentry, &fh, &fattr); + } return status; } -static int nfs4_proc_mknod(struct inode *dir, struct qstr *name, - struct iattr *sattr, dev_t rdev, struct nfs_fh *fh, - struct nfs_fattr *fattr) +static int nfs4_proc_mknod(struct inode *dir, struct dentry *dentry, + struct iattr *sattr, dev_t rdev) { struct nfs4_exception exception = { }; int err; do { err = nfs4_handle_exception(NFS_SERVER(dir), - _nfs4_proc_mknod(dir, name, sattr, rdev, - fh, fattr), + _nfs4_proc_mknod(dir, dentry, sattr, rdev), &exception); } while (exception.retry); return err; @@ -2001,8 +2120,8 @@ nfs4_proc_file_open(struct inode *inode, /* Find our open stateid */ cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); - if (unlikely(cred == NULL)) - return -ENOMEM; + if (IS_ERR(cred)) + return PTR_ERR(cred); ctx = alloc_nfs_open_context(dentry, cred); put_rpccred(cred); if (unlikely(ctx == NULL)) @@ -2037,6 +2156,199 @@ nfs4_proc_file_release(struct inode *ino return 0; } +struct nfs4_cached_acl { + int cached; + size_t len; + char data[]; +}; + +static ssize_t +nfs4_read_cached_acl(struct inode *inode, char *buf, size_t buflen) +{ + struct nfs_inode *nfsi = NFS_I(inode); + struct nfs4_cached_acl *acl; + int ret = -ENOENT; + + spin_lock(&inode->i_lock); + acl = nfsi->nfs4_acl; + if (acl == NULL) + goto out; + if (buf == NULL) /* user is just asking for length */ + goto out_len; + if (acl->cached == 0) + goto out; + ret = -ERANGE; /* see getxattr(2) man page */ + if (acl->len > buflen) + goto out; + memcpy(buf, acl->data, acl->len); +out_len: + ret = acl->len; +out: + spin_unlock(&inode->i_lock); + return ret; +} + +static void +nfs4_set_cached_acl(struct inode *inode, struct nfs4_cached_acl *acl) +{ + struct nfs_inode *nfsi = NFS_I(inode); + + spin_lock(&inode->i_lock); + kfree(nfsi->nfs4_acl); + nfsi->nfs4_acl = acl; + spin_unlock(&inode->i_lock); +} + +static void +nfs4_write_cached_acl(struct inode *inode, const char *buf, size_t acl_len) +{ + struct nfs4_cached_acl *acl; + + if (buf && acl_len <= PAGE_SIZE) { + acl = kmalloc(sizeof(*acl) + acl_len, GFP_KERNEL); + if (acl == NULL) + goto out; + acl->cached = 1; + memcpy(acl->data, buf, acl_len); + } else { + acl = kmalloc(sizeof(*acl), GFP_KERNEL); + if (acl == NULL) + goto out; + acl->cached = 0; + } + acl->len = acl_len; +out: + nfs4_set_cached_acl(inode, acl); +} + +void +nfs4_zap_acl_attr(struct inode *inode) +{ + nfs4_set_cached_acl(inode, NULL); +} + +static int +nfs4_server_supports_acls(struct nfs_server *server) +{ + return (server->caps & NFS_CAP_ACLS) + && (server->acl_bitmask & ACL4_SUPPORT_ALLOW_ACL) + && (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL); +} + +/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_CACHE_SIZE, and that + * it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_CACHE_SIZE) bytes on + * the stack. + */ +#define NFS4ACL_MAXPAGES (XATTR_SIZE_MAX >> PAGE_CACHE_SHIFT) + +static void buf_to_pages(const void *buf, size_t buflen, + struct page **pages, unsigned int *pgbase) +{ + const void *p = buf; + + *pgbase = offset_in_page(buf); + p -= *pgbase; + while (p < buf + buflen) { + *(pages++) = virt_to_page(p); + p += PAGE_CACHE_SIZE; + } +} + +static inline ssize_t +nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen) +{ + struct page *pages[NFS4ACL_MAXPAGES]; + struct nfs_getaclargs args = { + .fh = NFS_FH(inode), + .acl_pages = pages, + .acl_len = buflen, + }; + size_t resp_len = buflen; + void *resp_buf; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL], + .rpc_argp = &args, + .rpc_resp = &resp_len, + }; + struct page *localpage = NULL; + int ret; + + if (buflen < PAGE_SIZE) { + /* As long as we're doing a round trip to the server anyway, + * let's be prepared for a page of acl data. */ + localpage = alloc_page(GFP_KERNEL); + if (localpage == NULL) + return -ENOMEM; + args.acl_pages[0] = localpage; + args.acl_pgbase = 0; + args.acl_len = PAGE_SIZE; + } else + buf_to_pages(buf, buflen, args.acl_pages, &args.acl_pgbase); + ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); + if (ret) + goto out_free; + if (resp_len > args.acl_len) + resp_buf = NULL; + else if (localpage) { + resp_buf = page_address(localpage); + } else + resp_buf = buf; + nfs4_write_cached_acl(inode, resp_buf, resp_len); + ret = -ERANGE; + if (buflen && resp_len > buflen) + goto out_free; + if (localpage) + memcpy(buf, resp_buf, resp_len); + ret = resp_len; +out_free: + if (localpage) + __free_page(localpage); + return ret; +} + +ssize_t +nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen) +{ + struct nfs_server *server = NFS_SERVER(inode); + int ret; + + if (!nfs4_server_supports_acls(server)) + return -EOPNOTSUPP; + ret = nfs_revalidate_inode(server, inode); + if (ret < 0) + return ret; + ret = nfs4_read_cached_acl(inode, buf, buflen); + if (ret != -ENOENT) + return ret; + return nfs4_get_acl_uncached(inode, buf, buflen); +} + +int +nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen) +{ + struct nfs_server *server = NFS_SERVER(inode); + struct page *pages[NFS4ACL_MAXPAGES]; + struct nfs_setaclargs arg = { + .fh = NFS_FH(inode), + .acl_pages = pages, + .acl_len = buflen, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETACL], + .rpc_argp = &arg, + .rpc_resp = NULL, + }; + int ret; + + if (!nfs4_server_supports_acls(server)) + return -EOPNOTSUPP; + buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase); + ret = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0); + if (ret == 0) + nfs4_write_cached_acl(inode, buf, buflen); + return ret; +} + static int nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server) { @@ -2067,7 +2379,7 @@ nfs4_async_handle_error(struct rpc_task return 0; } -int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp) +static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp) { DEFINE_WAIT(wait); sigset_t oldset; @@ -2151,9 +2463,7 @@ int nfs4_handle_exception(struct nfs_ser int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short port) { - static nfs4_verifier sc_verifier; - static int initialized; - + nfs4_verifier sc_verifier; struct nfs4_setclientid setclientid = { .sc_verifier = &sc_verifier, .sc_prog = program, @@ -2164,27 +2474,38 @@ int nfs4_proc_setclientid(struct nfs4_cl .rpc_resp = clp, .rpc_cred = clp->cl_cred, }; + u32 *p; + int loop = 0; + int status; - if (!initialized) { - struct timespec boot_time; - u32 *p; - - initialized = 1; - boot_time = CURRENT_TIME; - p = (u32*)sc_verifier.data; - *p++ = htonl((u32)boot_time.tv_sec); - *p = htonl((u32)boot_time.tv_nsec); - } - setclientid.sc_name_len = scnprintf(setclientid.sc_name, - sizeof(setclientid.sc_name), "%s/%u.%u.%u.%u", - clp->cl_ipaddr, NIPQUAD(clp->cl_addr.s_addr)); - setclientid.sc_netid_len = scnprintf(setclientid.sc_netid, - sizeof(setclientid.sc_netid), "tcp"); - setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr, - sizeof(setclientid.sc_uaddr), "%s.%d.%d", - clp->cl_ipaddr, port >> 8, port & 255); + p = (u32*)sc_verifier.data; + *p++ = htonl((u32)clp->cl_boot_time.tv_sec); + *p = htonl((u32)clp->cl_boot_time.tv_nsec); + + for(;;) { + setclientid.sc_name_len = scnprintf(setclientid.sc_name, + sizeof(setclientid.sc_name), "%s/%u.%u.%u.%u %s %u", + clp->cl_ipaddr, NIPQUAD(clp->cl_addr.s_addr), + clp->cl_cred->cr_ops->cr_name, + clp->cl_id_uniquifier); + setclientid.sc_netid_len = scnprintf(setclientid.sc_netid, + sizeof(setclientid.sc_netid), "tcp"); + setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr, + sizeof(setclientid.sc_uaddr), "%s.%d.%d", + clp->cl_ipaddr, port >> 8, port & 255); - return rpc_call_sync(clp->cl_rpcclient, &msg, 0); + status = rpc_call_sync(clp->cl_rpcclient, &msg, 0); + if (status != -NFS4ERR_CLID_INUSE) + break; + if (signalled()) + break; + if (loop++ & 1) + ssleep(clp->cl_lease_time + 1); + else + if (++clp->cl_id_uniquifier == 0) + break; + } + return status; } int @@ -2361,6 +2682,25 @@ static int nfs4_proc_getlk(struct nfs4_s return err; } +static int do_vfs_lock(struct file *file, struct file_lock *fl) +{ + int res = 0; + switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) { + case FL_POSIX: + res = posix_lock_file_wait(file, fl); + break; + case FL_FLOCK: + res = flock_lock_file_wait(file, fl); + break; + default: + BUG(); + } + if (res < 0) + printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", + __FUNCTION__); + return res; +} + static int _nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request) { struct inode *inode = state->inode; @@ -2408,7 +2748,7 @@ static int _nfs4_proc_unlck(struct nfs4_ out: up(&state->lock_sema); if (status == 0) - posix_lock_file(request->fl_file, request); + do_vfs_lock(request->fl_file, request); up_read(&clp->cl_sem); return status; } @@ -2500,11 +2840,16 @@ static int _nfs4_do_setlk(struct nfs4_st return status; } -int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request) +static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request) { return _nfs4_do_setlk(state, F_SETLK, request, 1); } +static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request) +{ + return _nfs4_do_setlk(state, F_SETLK, request, 0); +} + static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) { struct nfs4_client *clp = state->owner->so_client; @@ -2517,7 +2862,7 @@ static int _nfs4_proc_setlk(struct nfs4_ if (status == 0) { /* Note: we always want to sleep here! */ request->fl_flags |= FL_SLEEP; - if (posix_lock_file_wait(request->fl_file, request) < 0) + if (do_vfs_lock(request->fl_file, request) < 0) printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__); } up_read(&clp->cl_sem); @@ -2574,10 +2919,50 @@ nfs4_proc_lock(struct file *filp, int cm return status; } +int nfs4_proc_fs_locations(struct inode *dir, struct dentry *dentry, + struct nfs_fs_locations *fs_locations, struct page *page) +{ + struct nfs_server *server = NFS_SERVER(dir); + u32 bitmask[2] = { + [0] = server->attr_bitmask[0] | FATTR4_WORD0_FS_LOCATIONS, + [1] = server->attr_bitmask[1], + }; + struct nfs4_fs_locations_arg args = { + .dir_fh = NFS_FH(dir), + .name = &dentry->d_name, + .page = page, + .bitmask = bitmask, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS], + .rpc_argp = &args, + .rpc_resp = &fs_locations, + }; + int status; + + dprintk("%s: start\n", __FUNCTION__); + fs_locations->fattr.valid = 0; + fs_locations->server = server; + status = rpc_call_sync(server->client, &msg, 0); + dprintk("%s: returned status = %d\n", __FUNCTION__, status); + return status; +} + +struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = { + .recover_open = nfs4_open_reclaim, + .recover_lock = nfs4_lock_reclaim, +}; + +struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops = { + .recover_open = nfs4_open_expired, + .recover_lock = nfs4_lock_expired, +}; + struct nfs_rpc_ops nfs_v4_clientops = { .version = 4, /* protocol version */ .dentry_ops = &nfs4_dentry_operations, .dir_inode_ops = &nfs4_dir_inode_operations, + .file_inode_ops = &nfs4_file_inode_operations, .getroot = nfs4_proc_get_root, .getattr = nfs4_proc_getattr, .setattr = nfs4_proc_setattr, Index: linux-2.6.11/fs/nfs/nfs4renewd.c =================================================================== --- linux-2.6.11.orig/fs/nfs/nfs4renewd.c +++ linux-2.6.11/fs/nfs/nfs4renewd.c @@ -53,6 +53,7 @@ #include #include #include +#include "nfs4_fs.h" #define NFSDBG_FACILITY NFSDBG_PROC Index: linux-2.6.11/fs/nfs/nfs4state.c =================================================================== --- linux-2.6.11.orig/fs/nfs/nfs4state.c +++ linux-2.6.11/fs/nfs/nfs4state.c @@ -46,24 +46,18 @@ #include #include +#include "nfs4_fs.h" #include "callback.h" #include "delegation.h" #define OPENOWNER_POOL_SIZE 8 -static DEFINE_SPINLOCK(state_spinlock); - -nfs4_stateid zero_stateid; - -#if 0 -nfs4_stateid one_stateid = - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; -#endif +const nfs4_stateid zero_stateid; +static DEFINE_SPINLOCK(state_spinlock); static LIST_HEAD(nfs4_clientid_list); static void nfs4_recover_state(void *); -extern void nfs4_renew_state(void *); void init_nfsv4_state(struct nfs_server *server) @@ -116,6 +110,7 @@ nfs4_alloc_client(struct in_addr *addr) INIT_LIST_HEAD(&clp->cl_superblocks); init_waitqueue_head(&clp->cl_waitq); rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client"); + clp->cl_boot_time = CURRENT_TIME; clp->cl_state = 1 << NFS4CLNT_OK; return clp; } @@ -205,7 +200,7 @@ nfs4_put_client(struct nfs4_client *clp) nfs4_free_client(clp); } -int nfs4_init_client(struct nfs4_client *clp) +static int __nfs4_init_client(struct nfs4_client *clp) { int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, nfs_callback_tcpport); if (status == 0) @@ -215,6 +210,11 @@ int nfs4_init_client(struct nfs4_client return status; } +int nfs4_init_client(struct nfs4_client *clp) +{ + return nfs4_map_errors(__nfs4_init_client(clp)); +} + u32 nfs4_alloc_lockowner_id(struct nfs4_client *clp) { @@ -274,8 +274,8 @@ nfs4_alloc_state_owner(void) return sp; } -static void -nfs4_unhash_state_owner(struct nfs4_state_owner *sp) +void +nfs4_drop_state_owner(struct nfs4_state_owner *sp) { struct nfs4_client *clp = sp->so_client; spin_lock(&clp->cl_lock); @@ -441,7 +441,9 @@ nfs4_get_open_state(struct inode *inode, if (state == NULL && new != NULL) { state = new; /* Caller *must* be holding owner->so_sem */ - list_add(&state->open_states, &owner->so_states); + /* Note: The reclaim code dictates that we add stateless + * and read-only stateids to the end of the list */ + list_add_tail(&state->open_states, &owner->so_states); state->owner = owner; atomic_inc(&owner->so_count); list_add(&state->inode_states, &nfsi->open_states); @@ -497,8 +499,12 @@ void nfs4_close_state(struct nfs4_state state->nreaders--; if (mode & FMODE_WRITE) state->nwriters--; - if (state->nwriters == 0 && state->nreaders == 0) - list_del_init(&state->inode_states); + if (state->nwriters == 0) { + if (state->nreaders == 0) + list_del_init(&state->inode_states); + /* See reclaim code */ + list_move_tail(&state->open_states, &owner->so_states); + } spin_unlock(&inode->i_lock); newstate = 0; if (state->state != 0) { @@ -708,7 +714,7 @@ void nfs4_increment_seqid(int status, st sp->so_seqid++; /* If the server returns BAD_SEQID, unhash state_owner here */ if (status == -NFS4ERR_BAD_SEQID) - nfs4_unhash_state_owner(sp); + nfs4_drop_state_owner(sp); } static int reclaimer(void *); @@ -753,7 +759,7 @@ nfs4_schedule_state_recovery(struct nfs4 schedule_work(&clp->cl_recoverd); } -static int nfs4_reclaim_locks(struct nfs4_state *state) +static int nfs4_reclaim_locks(struct nfs4_state_recovery_ops *ops, struct nfs4_state *state) { struct inode *inode = state->inode; struct file_lock *fl; @@ -764,7 +770,7 @@ static int nfs4_reclaim_locks(struct nfs continue; if (((struct nfs_open_context *)fl->fl_file->private_data)->state != state) continue; - status = nfs4_lock_reclaim(state, fl); + status = ops->recover_lock(state, fl); if (status >= 0) continue; switch (status) { @@ -786,20 +792,28 @@ out_err: return status; } -static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp) +static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct nfs4_state_owner *sp) { struct nfs4_state *state; struct nfs4_lock_state *lock; int status = 0; + /* Note: we rely on the sp->so_states list being ordered + * so that we always reclaim open(O_RDWR) and/or open(O_WRITE) + * states first. + * This is needed to ensure that the server won't give us any + * read delegations that we have to return if, say, we are + * recovering after a network partition or a reboot from a + * server that doesn't support a grace period. + */ list_for_each_entry(state, &sp->so_states, open_states) { if (state->state == 0) continue; - status = nfs4_open_reclaim(sp, state); + status = ops->recover_open(sp, state); list_for_each_entry(lock, &state->lock_states, ls_locks) lock->ls_flags &= ~NFS_LOCK_INITIALIZED; if (status >= 0) { - status = nfs4_reclaim_locks(state); + status = nfs4_reclaim_locks(ops, state); if (status < 0) goto out_err; list_for_each_entry(lock, &state->lock_states, ls_locks) { @@ -813,8 +827,7 @@ static int nfs4_reclaim_open_state(struc default: printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n", __FUNCTION__, status); - case -NFS4ERR_EXPIRED: - case -NFS4ERR_NO_GRACE: + case -ENOENT: case -NFS4ERR_RECLAIM_BAD: case -NFS4ERR_RECLAIM_CONFLICT: /* @@ -826,6 +839,8 @@ static int nfs4_reclaim_open_state(struc /* Mark the file as being 'closed' */ state->state = 0; break; + case -NFS4ERR_EXPIRED: + case -NFS4ERR_NO_GRACE: case -NFS4ERR_STALE_CLIENTID: goto out_err; } @@ -840,6 +855,7 @@ static int reclaimer(void *ptr) struct reclaimer_args *args = (struct reclaimer_args *)ptr; struct nfs4_client *clp = args->clp; struct nfs4_state_owner *sp; + struct nfs4_state_recovery_ops *ops; int status = 0; daemonize("%u.%u.%u.%u-reclaim", NIPQUAD(clp->cl_addr)); @@ -856,20 +872,34 @@ static int reclaimer(void *ptr) goto out; restart_loop: status = nfs4_proc_renew(clp); - if (status == 0 || status == -NFS4ERR_CB_PATH_DOWN) - goto out; - status = nfs4_init_client(clp); + switch (status) { + case 0: + case -NFS4ERR_CB_PATH_DOWN: + goto out; + case -NFS4ERR_STALE_CLIENTID: + case -NFS4ERR_LEASE_MOVED: + ops = &nfs4_reboot_recovery_ops; + break; + default: + ops = &nfs4_network_partition_recovery_ops; + }; + status = __nfs4_init_client(clp); if (status) goto out_error; - /* Mark all delagations for reclaim */ + /* Mark all delegations for reclaim */ nfs_delegation_mark_reclaim(clp); /* Note: list is protected by exclusive lock on cl->cl_sem */ list_for_each_entry(sp, &clp->cl_state_owners, so_list) { - status = nfs4_reclaim_open_state(sp); + status = nfs4_reclaim_open_state(ops, sp); if (status < 0) { + if (status == -NFS4ERR_NO_GRACE) { + ops = &nfs4_network_partition_recovery_ops; + status = nfs4_reclaim_open_state(ops, sp); + } if (status == -NFS4ERR_STALE_CLIENTID) goto restart_loop; - goto out_error; + if (status == -NFS4ERR_EXPIRED) + goto restart_loop; } } nfs_delegation_reap_unclaimed(clp); Index: linux-2.6.11/fs/nfs/nfs4xdr.c =================================================================== --- linux-2.6.11.orig/fs/nfs/nfs4xdr.c +++ linux-2.6.11/fs/nfs/nfs4xdr.c @@ -51,6 +51,7 @@ #include #include #include +#include "nfs4_fs.h" #define NFSDBG_FACILITY NFSDBG_XDR @@ -82,12 +83,16 @@ static int nfs_stat_to_errno(int); #define encode_getfh_maxsz (op_encode_hdr_maxsz) #define decode_getfh_maxsz (op_decode_hdr_maxsz + 1 + \ ((3+NFS4_FHSIZE) >> 2)) -#define encode_getattr_maxsz (op_encode_hdr_maxsz + 3) +#define nfs4_fattr_bitmap_maxsz 3 +#define encode_getattr_maxsz (op_encode_hdr_maxsz + nfs4_fattr_bitmap_maxsz) #define nfs4_name_maxsz (1 + ((3 + NFS4_MAXNAMLEN) >> 2)) #define nfs4_path_maxsz (1 + ((3 + NFS4_MAXPATHLEN) >> 2)) -#define nfs4_fattr_bitmap_maxsz (36 + 2 * nfs4_name_maxsz) -#define decode_getattr_maxsz (op_decode_hdr_maxsz + 3 + \ - nfs4_fattr_bitmap_maxsz) +/* This is based on getfattr, which uses the most attributes: */ +#define nfs4_fattr_value_maxsz (1 + (1 + 2 + 2 + 4 + 2 + 1 + 1 + 2 + 2 + \ + 3 + 3 + 3 + 2 * nfs4_name_maxsz)) +#define nfs4_fattr_maxsz (nfs4_fattr_bitmap_maxsz + \ + nfs4_fattr_value_maxsz) +#define decode_getattr_maxsz (op_decode_hdr_maxsz + nfs4_fattr_maxsz) #define encode_savefh_maxsz (op_encode_hdr_maxsz) #define decode_savefh_maxsz (op_decode_hdr_maxsz) #define encode_fsinfo_maxsz (op_encode_hdr_maxsz + 2) @@ -122,11 +127,11 @@ static int nfs_stat_to_errno(int); #define encode_symlink_maxsz (op_encode_hdr_maxsz + \ 1 + nfs4_name_maxsz + \ nfs4_path_maxsz + \ - nfs4_fattr_bitmap_maxsz) + nfs4_fattr_maxsz) #define decode_symlink_maxsz (op_decode_hdr_maxsz + 8) #define encode_create_maxsz (op_encode_hdr_maxsz + \ 2 + nfs4_name_maxsz + \ - nfs4_fattr_bitmap_maxsz) + nfs4_fattr_maxsz) #define decode_create_maxsz (op_decode_hdr_maxsz + 8) #define encode_delegreturn_maxsz (op_encode_hdr_maxsz + 4) #define decode_delegreturn_maxsz (op_decode_hdr_maxsz) @@ -205,7 +210,7 @@ static int nfs_stat_to_errno(int); #define NFS4_enc_setattr_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 4 + \ - nfs4_fattr_bitmap_maxsz + \ + nfs4_fattr_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_setattr_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ @@ -360,6 +365,29 @@ static int nfs_stat_to_errno(int); encode_delegreturn_maxsz) #define NFS4_dec_delegreturn_sz (compound_decode_hdr_maxsz + \ decode_delegreturn_maxsz) +#define NFS4_enc_getacl_sz (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_getattr_maxsz) +#define NFS4_dec_getacl_sz (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + op_decode_hdr_maxsz + \ + nfs4_fattr_bitmap_maxsz + 1) +#define NFS4_enc_setacl_sz (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + op_encode_hdr_maxsz + 4 + \ + nfs4_fattr_bitmap_maxsz + 1) +#define NFS4_dec_setacl_sz (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + op_decode_hdr_maxsz + nfs4_fattr_bitmap_maxsz) +#define NFS4_enc_fs_locations_sz \ + (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_getattr_maxsz) +#define NFS4_dec_fs_locations_sz \ + (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + op_decode_hdr_maxsz + \ + nfs4_fattr_bitmap_maxsz) static struct { unsigned int mode; @@ -459,7 +487,7 @@ static int encode_attrs(struct xdr_strea * In the worst-case, this would be * 12(bitmap) + 4(attrlen) + 8(size) + 4(mode) + 4(atime) + 4(mtime) * = 36 bytes, plus any contribution from variable-length fields - * such as owner/group/acl's. + * such as owner/group. */ len = 16; @@ -660,8 +688,6 @@ static int encode_getattr_two(struct xdr static int encode_getfattr(struct xdr_stream *xdr, const u32* bitmask) { - extern u32 nfs4_fattr_bitmap[]; - return encode_getattr_two(xdr, bitmask[0] & nfs4_fattr_bitmap[0], bitmask[1] & nfs4_fattr_bitmap[1]); @@ -669,8 +695,6 @@ static int encode_getfattr(struct xdr_st static int encode_fsinfo(struct xdr_stream *xdr, const u32* bitmask) { - extern u32 nfs4_fsinfo_bitmap[]; - return encode_getattr_two(xdr, bitmask[0] & nfs4_fsinfo_bitmap[0], bitmask[1] & nfs4_fsinfo_bitmap[1]); } @@ -969,7 +993,6 @@ static int encode_putrootfh(struct xdr_s static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx) { - extern nfs4_stateid zero_stateid; nfs4_stateid stateid; uint32_t *p; @@ -1010,8 +1033,13 @@ static int encode_readdir(struct xdr_str WRITE32(readdir->count >> 1); /* We're not doing readdirplus */ WRITE32(readdir->count); WRITE32(2); - WRITE32(FATTR4_WORD0_FILEID); - WRITE32(0); + if (readdir->bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID) { + WRITE32(0); + WRITE32(FATTR4_WORD1_MOUNTED_ON_FILEID); + } else { + WRITE32(FATTR4_WORD0_FILEID); + WRITE32(0); + } /* set up reply kvec * toplevel_status + taglen + rescount + OP_PUTFH + status @@ -1084,6 +1112,25 @@ static int encode_renew(struct xdr_strea } static int +encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg) +{ + uint32_t *p; + + RESERVE_SPACE(4+sizeof(zero_stateid.data)); + WRITE32(OP_SETATTR); + WRITEMEM(zero_stateid.data, sizeof(zero_stateid.data)); + RESERVE_SPACE(2*4); + WRITE32(1); + WRITE32(FATTR4_WORD0_ACL); + if (arg->acl_len % 4) + return -EINVAL; + RESERVE_SPACE(4); + WRITE32(arg->acl_len); + xdr_write_pages(xdr, arg->acl_pages, arg->acl_pgbase, arg->acl_len); + return 0; +} + +static int encode_savefh(struct xdr_stream *xdr) { uint32_t *p; @@ -1627,6 +1674,34 @@ out: } /* + * Encode a GETACL request + */ +static int +nfs4_xdr_enc_getacl(struct rpc_rqst *req, uint32_t *p, + struct nfs_getaclargs *args) +{ + struct xdr_stream xdr; + struct rpc_auth *auth = req->rq_task->tk_auth; + struct compound_hdr hdr = { + .nops = 2, + }; + int replen, status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, args->fh); + if (status) + goto out; + status = encode_getattr_two(&xdr, FATTR4_WORD0_ACL, 0); + /* set up reply buffer: */ + replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_getacl_sz) << 2; + xdr_inline_pages(&req->rq_rcv_buf, replen, + args->acl_pages, args->acl_pgbase, args->acl_len); +out: + return status; +} + +/* * Encode a WRITE request */ static int nfs4_xdr_enc_write(struct rpc_rqst *req, uint32_t *p, struct nfs_writeargs *args) @@ -1692,7 +1767,6 @@ static int nfs4_xdr_enc_fsinfo(struct rp */ static int nfs4_xdr_enc_pathconf(struct rpc_rqst *req, uint32_t *p, const struct nfs4_pathconf_arg *args) { - extern u32 nfs4_pathconf_bitmap[2]; struct xdr_stream xdr; struct compound_hdr hdr = { .nops = 2, @@ -1713,7 +1787,6 @@ static int nfs4_xdr_enc_pathconf(struct */ static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, uint32_t *p, const struct nfs4_statfs_arg *args) { - extern u32 nfs4_statfs_bitmap[]; struct xdr_stream xdr; struct compound_hdr hdr = { .nops = 2, @@ -1823,6 +1896,38 @@ static int nfs4_xdr_enc_delegreturn(stru } /* + * Encode FS_LOCATIONS request + */ +static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, uint32_t *p, struct nfs4_fs_locations_arg *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 3, + }; + struct rpc_auth *auth = req->rq_task->tk_auth; + int replen; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + if ((status = encode_putfh(&xdr, args->dir_fh)) != 0) + goto out; + if ((status = encode_lookup(&xdr, args->name)) != 0) + goto out; + if ((status = encode_getfattr(&xdr, args->bitmask)) != 0) + goto out; + /* set up reply + * toplevel_status + taglen + rescount + OP_PUTFH + status + * + OP_LOOKUP + status + OP_GETATTR + status = 7 + */ + replen = (RPC_REPHDRSIZE + auth->au_rslack + 7) << 2; + xdr_inline_pages(&req->rq_rcv_buf, replen, &args->page, + 0, PAGE_SIZE); +out: + return status; +} + +/* * START OF "GENERIC" DECODE ROUTINES. * These may look a little ugly since they are imported from a "generic" * set of XDR encode/decode routines which are intended to be shared by @@ -1856,7 +1961,7 @@ static int nfs4_xdr_enc_delegreturn(stru } \ } while (0) -static int decode_opaque_inline(struct xdr_stream *xdr, uint32_t *len, char **string) +static int decode_opaque_inline(struct xdr_stream *xdr, unsigned int *len, char **string) { uint32_t *p; @@ -1907,7 +2012,7 @@ static int decode_op_hdr(struct xdr_stre static int decode_ace(struct xdr_stream *xdr, void *ace, struct nfs4_client *clp) { uint32_t *p; - uint32_t strlen; + unsigned int strlen; char *str; READ_BUF(12); @@ -2037,7 +2142,7 @@ static int decode_attr_symlink_support(s return 0; } -static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_fsid *fsid) +static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_fsid *fsid) { uint32_t *p; @@ -2156,6 +2261,45 @@ static int decode_attr_files_total(struc return status; } +static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_fs_locations *res) +{ + int n; + uint32_t *p; + int status = -EIO; + + if (unlikely(bitmap[0] & (FATTR4_WORD0_FS_LOCATIONS -1U))) + goto out; + status = 0; + if (unlikely(!(bitmap[0] & FATTR4_WORD0_FS_LOCATIONS))) + goto out; + status = decode_opaque_inline(xdr, &res->fs_pathlen, &res->fs_path); + if (unlikely(status != 0)) + goto out; + READ_BUF(4); + READ32(n); + if (n <= 0) + goto out_eio; + res->nlocations = 0; + while (res->nlocations < n) { + struct nfs_fs_location *loc = &res->locations[res->nlocations]; + + status = decode_opaque_inline(xdr, &loc->serverlen, &loc->server); + if (unlikely(status != 0)) + goto out_eio; + status = decode_opaque_inline(xdr, &loc->rootpathlen, &loc->rootpath); + if (unlikely(status != 0)) + goto out_eio; + if (res->nlocations < NFS_FS_LOCATIONS_MAXENTRIES) + res->nlocations++; + } +out: + dprintk("%s: fs_locations done, error = %d\n", __FUNCTION__, status); + return status; +out_eio: + status = -EIO; + goto out; +} + static int decode_attr_maxfilesize(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) { uint32_t *p; @@ -2686,10 +2830,14 @@ static int decode_getfattr(struct xdr_st goto xdr_error; if ((status = decode_attr_size(xdr, bitmap, &fattr->size)) != 0) goto xdr_error; - if ((status = decode_attr_fsid(xdr, bitmap, &fattr->fsid_u.nfs4)) != 0) + if ((status = decode_attr_fsid(xdr, bitmap, &fattr->fsid)) != 0) goto xdr_error; if ((status = decode_attr_fileid(xdr, bitmap, &fattr->fileid)) != 0) goto xdr_error; + if ((status = decode_attr_fs_locations(xdr, bitmap, container_of(fattr, + struct nfs_fs_locations, + fattr))) != 0) + goto xdr_error; if ((status = decode_attr_mode(xdr, bitmap, &fattr->mode)) != 0) goto xdr_error; fattr->mode |= fmode; @@ -3122,6 +3270,46 @@ static int decode_renew(struct xdr_strea return decode_op_hdr(xdr, OP_RENEW); } +static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, + size_t *acl_len) +{ + uint32_t *savep; + uint32_t attrlen, + bitmap[2] = {0}; + struct kvec *iov = req->rq_rcv_buf.head; + int status; + + if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) + goto out; + if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) + goto out; + if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0) + goto out; + + if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_ACL)) { + int hdrlen, recvd; + + /* We ignore &savep and don't do consistency checks on + * the attr length. Let userspace figure it out.... */ + hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base; + recvd = req->rq_rcv_buf.len - hdrlen; + if (attrlen > recvd) { + printk(KERN_WARNING "NFS: server cheating in getattr" + " acl reply: attrlen %u > recvd %u\n", + attrlen, recvd); + return -EINVAL; + } + if (attrlen <= *acl_len) + xdr_read_pages(xdr, attrlen); + *acl_len = attrlen; + } + +out: + return status; +} + static int decode_savefh(struct xdr_stream *xdr) { @@ -3175,7 +3363,7 @@ static int decode_setclientid(struct xdr READ_BUF(4); READ32(len); READ_BUF(len); - return -EEXIST; + return -NFSERR_CLID_INUSE; } else return -nfs_stat_to_errno(nfserr); @@ -3413,6 +3601,71 @@ out: } +/* + * Encode an SETACL request + */ +static int +nfs4_xdr_enc_setacl(struct rpc_rqst *req, uint32_t *p, struct nfs_setaclargs *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 2, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, args->fh); + if (status) + goto out; + status = encode_setacl(&xdr, args); +out: + return status; +} +/* + * Decode SETACL response + */ +static int +nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, uint32_t *p, void *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_putfh(&xdr); + if (status) + goto out; + status = decode_setattr(&xdr, res); +out: + return status; +} + +/* + * Decode GETACL response + */ +static int +nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, uint32_t *p, size_t *acl_len) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_putfh(&xdr); + if (status) + goto out; + status = decode_getacl(&xdr, rqstp, acl_len); + +out: + return status; +} /* * Decode CLOSE response @@ -3855,9 +4108,32 @@ static int nfs4_xdr_dec_delegreturn(stru return status; } +/* + * FS_LOCATIONS request + */ +static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req, uint32_t *p, struct nfs_fs_locations *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &req->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status != 0) + goto out; + if ((status = decode_putfh(&xdr)) != 0) + goto out; + if ((status = decode_lookup(&xdr)) != 0) + goto out; + xdr_enter_page(&xdr, PAGE_SIZE); + status = decode_getfattr(&xdr, &res->fattr, res->server); +out: + return status; +} + uint32_t *nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus) { - uint32_t bitmap[1] = {0}; + uint32_t bitmap[2] = {0}; uint32_t len; if (!*p++) { @@ -3881,13 +4157,18 @@ uint32_t *nfs4_decode_dirent(uint32_t *p entry->ino = 1; len = ntohl(*p++); /* bitmap length */ - if (len > 0) { - bitmap[0] = ntohl(*p); - p += len; + if (len-- > 0) { + bitmap[0] = ntohl(*p++); + if (len-- > 0) { + bitmap[1] = ntohl(*p++); + p += len; + } } len = XDR_QUADLEN(ntohl(*p++)); /* attribute buffer length */ if (len > 0) { - if (bitmap[0] == FATTR4_WORD0_FILEID) + if (bitmap[0] == 0 && bitmap[1] == FATTR4_WORD1_MOUNTED_ON_FILEID) + xdr_decode_hyper(p, &entry->ino); + else if (bitmap[0] == FATTR4_WORD0_FILEID) xdr_decode_hyper(p, &entry->ino); p += len; } @@ -4009,6 +4290,9 @@ struct rpc_procinfo nfs4_procedures[] = PROC(READDIR, enc_readdir, dec_readdir), PROC(SERVER_CAPS, enc_server_caps, dec_server_caps), PROC(DELEGRETURN, enc_delegreturn, dec_delegreturn), + PROC(GETACL, enc_getacl, dec_getacl), + PROC(SETACL, enc_setacl, dec_setacl), + PROC(FS_LOCATIONS, enc_fs_locations, dec_fs_locations), }; struct rpc_version nfs_version4 = { Index: linux-2.6.11/fs/nfs/nfsroot.c =================================================================== --- linux-2.6.11.orig/fs/nfs/nfsroot.c +++ linux-2.6.11/fs/nfs/nfsroot.c @@ -124,7 +124,6 @@ enum { Opt_soft, Opt_hard, Opt_intr, Opt_nointr, Opt_posix, Opt_noposix, Opt_cto, Opt_nocto, Opt_ac, Opt_noac, Opt_lock, Opt_nolock, Opt_v2, Opt_v3, Opt_udp, Opt_tcp, - Opt_broken_suid, /* Error token */ Opt_err }; @@ -159,7 +158,6 @@ static match_table_t __initdata tokens = {Opt_udp, "udp"}, {Opt_tcp, "proto=tcp"}, {Opt_tcp, "tcp"}, - {Opt_broken_suid, "broken_suid"}, {Opt_err, NULL} }; @@ -268,9 +266,6 @@ static int __init root_nfs_parse(char *n case Opt_tcp: nfs_data.flags |= NFS_MOUNT_TCP; break; - case Opt_broken_suid: - nfs_data.flags |= NFS_MOUNT_BROKEN_SUID; - break; default : return 0; } @@ -351,7 +346,7 @@ static void __init root_nfs_print(void) #endif -int __init root_nfs_init(void) +static int __init root_nfs_init(void) { #ifdef NFSROOT_DEBUG nfs_debug |= NFSDBG_ROOT; @@ -379,15 +374,15 @@ int __init root_nfs_init(void) * Parse NFS server and directory information passed on the kernel * command line. */ -int __init nfs_root_setup(char *line) +static int __init nfs_root_setup(char *line) { ROOT_DEV = Root_NFS; if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) { strlcpy(nfs_root_name, line, sizeof(nfs_root_name)); } else { - int n = strlen(line) + strlen(NFS_ROOT); + int n = strlen(line) + sizeof(NFS_ROOT) - 1; if (n >= sizeof(nfs_root_name)) - line[sizeof(nfs_root_name) - strlen(NFS_ROOT) - 1] = '\0'; + line[sizeof(nfs_root_name) - sizeof(NFS_ROOT) - 2] = '\0'; sprintf(nfs_root_name, NFS_ROOT, line); } root_server_addr = root_nfs_parse_addr(nfs_root_name); Index: linux-2.6.11/fs/nfs/proc.c =================================================================== --- linux-2.6.11.orig/fs/nfs/proc.c +++ linux-2.6.11/fs/nfs/proc.c @@ -212,7 +212,7 @@ static int nfs_proc_write(struct nfs_wri return status < 0? status : wdata->res.count; } -static struct inode * +static int nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, int flags) { @@ -233,37 +233,34 @@ nfs_proc_create(struct inode *dir, struc fattr.valid = 0; dprintk("NFS call create %s\n", dentry->d_name.name); status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0); + if (status == 0) + status = nfs_instantiate(dentry, &fhandle, &fattr); dprintk("NFS reply create: %d\n", status); - if (status == 0) { - struct inode *inode; - inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); - if (inode) - return inode; - status = -ENOMEM; - } - return ERR_PTR(status); + return status; } /* * In NFSv2, mknod is grafted onto the create call. */ static int -nfs_proc_mknod(struct inode *dir, struct qstr *name, struct iattr *sattr, - dev_t rdev, struct nfs_fh *fhandle, struct nfs_fattr *fattr) +nfs_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr, + dev_t rdev) { + struct nfs_fh fhandle; + struct nfs_fattr fattr; struct nfs_createargs arg = { .fh = NFS_FH(dir), - .name = name->name, - .len = name->len, + .name = dentry->d_name.name, + .len = dentry->d_name.len, .sattr = sattr }; struct nfs_diropok res = { - .fh = fhandle, - .fattr = fattr + .fh = &fhandle, + .fattr = &fattr }; - int status, mode; + int status, mode; - dprintk("NFS call mknod %s\n", name->name); + dprintk("NFS call mknod %s\n", dentry->d_name.name); mode = sattr->ia_mode; if (S_ISFIFO(mode)) { @@ -274,14 +271,16 @@ nfs_proc_mknod(struct inode *dir, struct sattr->ia_size = new_encode_dev(rdev);/* get out your barf bag */ } - fattr->valid = 0; + fattr.valid = 0; status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0); if (status == -EINVAL && S_ISFIFO(mode)) { sattr->ia_mode = mode; - fattr->valid = 0; + fattr.valid = 0; status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0); } + if (status == 0) + status = nfs_instantiate(dentry, &fhandle, &fattr); dprintk("NFS reply mknod: %d\n", status); return status; } @@ -398,24 +397,27 @@ nfs_proc_symlink(struct inode *dir, stru } static int -nfs_proc_mkdir(struct inode *dir, struct qstr *name, struct iattr *sattr, - struct nfs_fh *fhandle, struct nfs_fattr *fattr) +nfs_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr) { + struct nfs_fh fhandle; + struct nfs_fattr fattr; struct nfs_createargs arg = { .fh = NFS_FH(dir), - .name = name->name, - .len = name->len, + .name = dentry->d_name.name, + .len = dentry->d_name.len, .sattr = sattr }; struct nfs_diropok res = { - .fh = fhandle, - .fattr = fattr + .fh = &fhandle, + .fattr = &fattr }; int status; - dprintk("NFS call mkdir %s\n", name->name); - fattr->valid = 0; + dprintk("NFS call mkdir %s\n", dentry->d_name.name); + fattr.valid = 0; status = rpc_call(NFS_CLIENT(dir), NFSPROC_MKDIR, &arg, &res, 0); + if (status == 0) + status = nfs_instantiate(dentry, &fhandle, &fattr); dprintk("NFS reply mkdir: %d\n", status); return status; } @@ -620,6 +622,7 @@ struct nfs_rpc_ops nfs_v2_clientops = { .version = 2, /* protocol version */ .dentry_ops = &nfs_dentry_operations, .dir_inode_ops = &nfs_dir_inode_operations, + .file_inode_ops = &nfs_file_inode_operations, .getroot = nfs_proc_get_root, .getattr = nfs_proc_getattr, .setattr = nfs_proc_setattr, Index: linux-2.6.11/fs/nfs/read.c =================================================================== --- linux-2.6.11.orig/fs/nfs/read.c +++ linux-2.6.11/fs/nfs/read.c @@ -370,7 +370,7 @@ out_bad: return -ENOMEM; } -int +static int nfs_pagein_list(struct list_head *head, int rpages) { LIST_HEAD(one_request); Index: linux-2.6.11/fs/nfs/unlink.c =================================================================== --- linux-2.6.11.orig/fs/nfs/unlink.c +++ linux-2.6.11/fs/nfs/unlink.c @@ -167,6 +167,11 @@ nfs_async_unlink(struct dentry *dentry) goto out; memset(data, 0, sizeof(*data)); + data->cred = rpcauth_lookupcred(clnt->cl_auth, 0); + if (IS_ERR(data->cred)) { + status = PTR_ERR(data->cred); + goto out_free; + } data->dir = dget(dir); data->dentry = dentry; @@ -183,12 +188,14 @@ nfs_async_unlink(struct dentry *dentry) spin_lock(&dentry->d_lock); dentry->d_flags |= DCACHE_NFSFS_RENAMED; spin_unlock(&dentry->d_lock); - data->cred = rpcauth_lookupcred(clnt->cl_auth, 0); rpc_sleep_on(&nfs_delete_queue, task, NULL, NULL); status = 0; out: return status; +out_free: + kfree(data); + return status; } /** Index: linux-2.6.11/fs/nfs/write.c =================================================================== --- linux-2.6.11.orig/fs/nfs/write.c +++ linux-2.6.11/fs/nfs/write.c @@ -80,14 +80,31 @@ static void nfs_writeback_done_partial(s static void nfs_writeback_done_full(struct nfs_write_data *, int); static int nfs_wait_on_write_congestion(struct address_space *, int); static int nfs_wait_on_requests(struct inode *, unsigned long, unsigned int); +static int nfs_flush_inode(struct inode *inode, unsigned long idx_start, + unsigned int npages, int how); static kmem_cache_t *nfs_wdata_cachep; mempool_t *nfs_wdata_mempool; -mempool_t *nfs_commit_mempool; +static mempool_t *nfs_commit_mempool; static DECLARE_WAIT_QUEUE_HEAD(nfs_write_congestion); -void nfs_writedata_release(struct rpc_task *task) +static inline struct nfs_write_data *nfs_commit_alloc(void) +{ + struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, SLAB_NOFS); + if (p) { + memset(p, 0, sizeof(*p)); + INIT_LIST_HEAD(&p->pages); + } + return p; +} + +static inline void nfs_commit_free(struct nfs_write_data *p) +{ + mempool_free(p, nfs_commit_mempool); +} + +static void nfs_writedata_release(struct rpc_task *task) { struct nfs_write_data *wdata = (struct nfs_write_data *)task->tk_calldata; nfs_writedata_free(wdata); @@ -990,7 +1007,7 @@ static int nfs_flush_one(struct list_hea return -ENOMEM; } -int +static int nfs_flush_list(struct list_head *head, int wpages, int how) { LIST_HEAD(one_request); @@ -1240,7 +1257,7 @@ static void nfs_commit_rpcsetup(struct l /* * Commit dirty pages */ -int +static int nfs_commit_list(struct list_head *head, int how) { struct nfs_write_data *data; @@ -1314,8 +1331,8 @@ nfs_commit_done(struct rpc_task *task) } #endif -int nfs_flush_inode(struct inode *inode, unsigned long idx_start, - unsigned int npages, int how) +static int nfs_flush_inode(struct inode *inode, unsigned long idx_start, + unsigned int npages, int how) { struct nfs_inode *nfsi = NFS_I(inode); LIST_HEAD(head); Index: linux-2.6.11/fs/nfsd/nfs4callback.c =================================================================== --- linux-2.6.11.orig/fs/nfsd/nfs4callback.c +++ linux-2.6.11/fs/nfsd/nfs4callback.c @@ -447,7 +447,10 @