diff -u --recursive --new-file linux-2.6.0-22-atomic_open1/fs/exec.c linux-2.6.0-23-atomic_open2/fs/exec.c --- linux-2.6.0-22-atomic_open1/fs/exec.c 2003-11-12 01:17:56.000000000 -0500 +++ linux-2.6.0-23-atomic_open2/fs/exec.c 2003-11-11 18:27:37.000000000 -0500 @@ -121,7 +121,7 @@ struct nameidata nd; int error; - nd.intent.open.flags = O_RDONLY; + nd.intent.open.flags = FMODE_READ; error = __user_walk(library, LOOKUP_FOLLOW|LOOKUP_OPEN, &nd); if (error) goto out; @@ -471,8 +471,12 @@ struct file *open_exec(const char *name) { struct nameidata nd; - int err = path_lookup(name, LOOKUP_FOLLOW, &nd); - struct file *file = ERR_PTR(err); + int err; + struct file *file; + + nd.intent.open.flags = FMODE_READ; + err = path_lookup(name, LOOKUP_FOLLOW|LOOKUP_OPEN, &nd); + file = ERR_PTR(err); if (!err) { struct inode *inode = nd.dentry->d_inode; diff -u --recursive --new-file linux-2.6.0-22-atomic_open1/fs/nfs/dir.c linux-2.6.0-23-atomic_open2/fs/nfs/dir.c --- linux-2.6.0-22-atomic_open1/fs/nfs/dir.c 2003-11-11 18:27:02.000000000 -0500 +++ linux-2.6.0-23-atomic_open2/fs/nfs/dir.c 2003-11-13 17:50:45.000000000 -0500 @@ -761,10 +761,101 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { - struct dentry *cached; + struct dentry *cached = NULL; + struct inode *inode = NULL; + int error = 0; + + if (dentry->d_name.len > NFS_SERVER(dir)->namelen) { + error = -ENAMETOOLONG; + goto out; + } + dentry->d_op = NFS_PROTO(dir)->dentry_ops; cached = nfs_dcache_lookup(dentry->d_parent, &dentry->d_name); + + /* Check that we are indeed trying to open this file */ + if (!nd) + goto no_open; + if ((nd->flags & LOOKUP_CONTINUE) || !(nd->flags & LOOKUP_OPEN)) + goto no_open; + /* NFS does not (yet) have a stateful open for directories */ + if (nd->flags & LOOKUP_DIRECTORY) + goto no_open; + /* Are we trying to write to a read only partition? */ + if (IS_RDONLY(dir) && (nd->intent.open.flags & (O_CREAT|O_TRUNC|FMODE_WRITE))) + goto no_open; + if (nd->intent.open.flags & O_CREAT) { + /* + * Let nfs_create() do an atomic create+open + * if we have no cached dentry to revalidate + */ + if (!cached) + goto no_entry; + /* ...or if the cached dentry is negative */ + if (cached->d_inode == NULL) + return cached; + if ((nd->intent.open.flags & O_EXCL) != 0) { + /* + * We have a cached positive dentry, but want to do + * exclusive create. Check if the file still exists + * on the server. + */ + if (nfs_lookup_revalidate(cached, nd)) + return cached; + dput(cached); + cached = NULL; + goto no_entry; + } + } + /* Deal with submounts before we try opening files on the server */ + if (cached && cached->d_inode && + S_ISDIR(cached->d_inode->i_mode) && + have_submounts(cached)) + return cached; + + /* Open the file on the server */ + lock_kernel(); + inode = nfs4_atomic_open(dir, dentry, nd); + unlock_kernel(); + if (IS_ERR(inode)) { + error = PTR_ERR(inode); + switch (error) { + /* This turned out not to be a regular file */ + case -ELOOP: + case -EISDIR: + case -EINVAL: + goto no_open; + /* Make a negative dentry */ + case -ENOENT: + inode = NULL; + break; + default: + goto out; + } + } + /* Does this file match the dcache dentry? */ if (cached != NULL) { + /* Yes: so return it */ + if (cached->d_inode == inode) { + if (inode) + iput(inode); + return cached; + } + /* drop the cached dentry */ + d_drop(cached); + dput(cached); + cached = NULL; + } +no_entry: + d_add(dentry, inode); + nfs_renew_times(dentry); +out: + if (cached) + dput(cached); + BUG_ON(error > 0); + return ERR_PTR(error); +no_open: + if (cached) { if (nfs_lookup_revalidate(cached, nd)) return cached; dput(cached); @@ -1352,13 +1443,6 @@ int mode = inode->i_mode; int res; - /* Are we checking permissions on anything other than lookup? */ - if (!(mask & MAY_EXEC)) { - /* We only need to check permissions on file open() and access() */ - if (!nd || !(nd->flags & (LOOKUP_OPEN|LOOKUP_ACCESS))) - return 0; - } - if (mask & MAY_WRITE) { /* * @@ -1378,6 +1462,18 @@ return -EACCES; } + /* Are we checking permissions on anything other than lookup? */ + if (!(mask & MAY_EXEC)) { + /* We only need to check permissions on file open() and access() */ + if (!nd) + return 0; + if (!(nd->flags & (LOOKUP_OPEN|LOOKUP_ACCESS))) + return 0; + /* NFSv4 has atomic_open... */ + if (NFS_PROTO(inode)->version > 3 && (nd->flags & LOOKUP_OPEN)) + return 0; + } + lock_kernel(); if (!NFS_PROTO(inode)->access) diff -u --recursive --new-file linux-2.6.0-22-atomic_open1/fs/nfs/inode.c linux-2.6.0-23-atomic_open2/fs/nfs/inode.c --- linux-2.6.0-22-atomic_open1/fs/nfs/inode.c 2003-11-11 18:27:02.000000000 -0500 +++ linux-2.6.0-23-atomic_open2/fs/nfs/inode.c 2003-11-11 18:27:37.000000000 -0500 @@ -303,7 +303,6 @@ server = NFS_SB(sb); sb->s_magic = NFS_SUPER_MAGIC; - sb->s_op = &nfs_sops; /* Did getting the root inode fail? */ if (nfs_get_root(&root_inode, authflavor, sb, &server->fh) < 0) @@ -513,6 +512,7 @@ goto out_shutdown; } + sb->s_op = &nfs_sops; err = nfs_sb_init(sb, authflavor); if (err != 0) goto out_noinit; @@ -828,7 +828,12 @@ filemap_fdatawait(inode->i_mapping); if (error) goto out; + /* Optimize away unnecessary truncates */ + if ((attr->ia_valid & ATTR_SIZE) && i_size_read(inode) == attr->ia_size) + attr->ia_valid &= ~ATTR_SIZE; } + if (!attr->ia_valid) + goto out; error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr); if (error) @@ -1348,6 +1353,48 @@ #ifdef CONFIG_NFS_V4 +static void nfs4_clear_inode(struct inode *); + +static struct super_operations nfs4_sops = { + .alloc_inode = nfs_alloc_inode, + .destroy_inode = nfs_destroy_inode, + .write_inode = nfs_write_inode, + .delete_inode = nfs_delete_inode, + .put_super = nfs_put_super, + .statfs = nfs_statfs, + .clear_inode = nfs4_clear_inode, + .umount_begin = nfs_umount_begin, + .show_options = nfs_show_options, +}; + +/* + * Clean out any remaining NFSv4 state that might be left over due + * to open() calls that passed nfs_atomic_lookup, but failed to call + * nfs_open(). + */ +static void nfs4_clear_inode(struct inode *inode) +{ + struct nfs_inode *nfsi = NFS_I(inode); + + while (!list_empty(&nfsi->open_states)) { + struct nfs4_state *state; + + state = list_entry(nfsi->open_states.next, + struct nfs4_state, + inode_states); + dprintk("%s(%s/%Ld): found unclaimed NFSv4 state %p\n", + __FUNCTION__, + inode->i_sb->s_id, + (long long)NFS_FILEID(inode), + state); + list_del(&state->inode_states); + nfs4_put_open_state(state); + } + /* Now call standard NFS clear_inode() code */ + nfs_clear_inode(inode); +} + + static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, int silent) { struct nfs_server *server; @@ -1472,6 +1519,7 @@ if ((server->idmap = nfs_idmap_new(server)) == NULL) printk(KERN_WARNING "NFS: couldn't start IDmap\n"); + sb->s_op = &nfs4_sops; err = nfs_sb_init(sb, authflavour); if (err == 0) return 0; diff -u --recursive --new-file linux-2.6.0-22-atomic_open1/fs/nfs/nfs4proc.c linux-2.6.0-23-atomic_open2/fs/nfs/nfs4proc.c --- linux-2.6.0-22-atomic_open1/fs/nfs/nfs4proc.c 2003-11-11 18:27:02.000000000 -0500 +++ linux-2.6.0-23-atomic_open2/fs/nfs/nfs4proc.c 2003-11-11 18:27:37.000000000 -0500 @@ -45,6 +45,7 @@ #include #include #include +#include #define NFSDBG_FACILITY NFSDBG_PROC @@ -509,6 +510,9 @@ return status; } +/* + * Returns an nfs4_state + an referenced inode + */ struct nfs4_state * nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred) { @@ -617,19 +621,23 @@ up(&sp->so_sema); nfs4_put_state_owner(sp); - iput(inode); return state; out_up: up(&sp->so_sema); nfs4_put_state_owner(sp); - if (state) + if (state) { nfs4_put_open_state(state); - if (inode) + state = NULL; + } + if (inode) { iput(inode); + inode = NULL; + } status = nfs4_handle_error(server, status); if (!status) goto retry; + BUG_ON(status < -1000 || status > 0); out: return ERR_PTR(status); } @@ -718,6 +726,31 @@ return status; } +struct inode * +nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +{ + struct iattr attr; + struct rpc_cred *cred; + struct nfs4_state *state; + + if (nd->flags & LOOKUP_CREATE) { + attr.ia_mode = nd->intent.open.create_mode; + attr.ia_valid = ATTR_MODE; + if (!IS_POSIXACL(dir)) + attr.ia_mode &= ~current->fs->umask; + } else { + attr.ia_valid = 0; + BUG_ON(nd->intent.open.flags & O_CREAT); + } + + cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + state = nfs4_do_open(dir, &dentry->d_name, nd->intent.open.flags, &attr, cred); + put_rpccred(cred); + if (IS_ERR(state)) + return (struct inode *)state; + return state->inode; +} + static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) @@ -808,28 +841,39 @@ struct inode * inode = dentry->d_inode; int size_change = sattr->ia_valid & ATTR_SIZE; struct nfs4_state *state = NULL; - int status; + int need_iput = 0; + int status; fattr->valid = 0; if (size_change) { - struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); - state = nfs4_do_open(dentry->d_parent->d_inode, + state = nfs4_find_state_bypid(inode, current->pid); + + if (!state) { + struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); + state = nfs4_do_open(dentry->d_parent->d_inode, &dentry->d_name, FMODE_WRITE, NULL, cred); - put_rpccred(cred); + put_rpccred(cred); + need_iput = 1; + } if (IS_ERR(state)) return PTR_ERR(state); if (state->inode != inode) { - printk(KERN_WARNING "nfs: raced in setattr, returning -EIO\n"); - nfs4_put_open_state(state); - return -EIO; + printk(KERN_WARNING "nfs: raced in setattr (%p != %p), returning -EIO\n", inode, state->inode); + status = -EIO; + goto out; } } status = nfs4_do_setattr(NFS_SERVER(inode), fattr, NFS_FH(inode), sattr, state); - if (state) +out: + if (state) { + inode = state->inode; nfs4_put_open_state(state); + if (need_iput) + iput(inode); + } return status; } @@ -1085,18 +1129,18 @@ state = nfs4_do_open(dir, name, flags, sattr, cred); put_rpccred(cred); if (!IS_ERR(state)) { - inode = igrab(state->inode); + 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_put_open_state(state); iput(inode); inode = ERR_PTR(status); } } - nfs4_put_open_state(state); } else inode = (struct inode *)state; return inode; @@ -1672,43 +1716,28 @@ nfs4_proc_file_open(struct inode *inode, struct file *filp) { struct dentry *dentry = filp->f_dentry; - struct inode *dir = dentry->d_parent->d_inode; - struct rpc_cred *cred; struct nfs4_state *state; - int flags = filp->f_flags; - int status = 0; dprintk("nfs4_proc_file_open: starting on (%.*s/%.*s)\n", (int)dentry->d_parent->d_name.len, dentry->d_parent->d_name.name, (int)dentry->d_name.len, dentry->d_name.name); - if ((flags + 1) & O_ACCMODE) - flags++; - - lock_kernel(); -/* -* We have already opened the file "O_EXCL" in nfs4_proc_create!! -* This ugliness will go away with lookup-intent... -*/ - cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); - state = nfs4_do_open(dir, &dentry->d_name, flags, NULL, cred); - if (IS_ERR(state)) { - status = PTR_ERR(state); - state = NULL; - } else if (filp->f_mode & FMODE_WRITE) - nfs_set_mmcred(inode, cred); - if (inode != filp->f_dentry->d_inode) { + /* Find our open stateid */ + state = nfs4_find_state_bypid(inode, current->pid); + if (state == NULL) { printk(KERN_WARNING "NFS: v4 raced in function %s\n", __FUNCTION__); - status = -EIO; /* ERACE actually */ - nfs4_put_open_state(state); - state = NULL; + return -EIO; /* ERACE actually */ + } + nfs4_put_open_state(state); + if (filp->f_mode & FMODE_WRITE) { + lock_kernel(); + nfs_set_mmcred(inode, state->owner->so_cred); + unlock_kernel(); } filp->private_data = state; - put_rpccred(cred); - unlock_kernel(); - return status; + return 0; } /* diff -u --recursive --new-file linux-2.6.0-22-atomic_open1/fs/nfs/nfs4state.c linux-2.6.0-23-atomic_open2/fs/nfs/nfs4state.c --- linux-2.6.0-22-atomic_open1/fs/nfs/nfs4state.c 2003-11-11 18:25:37.000000000 -0500 +++ linux-2.6.0-23-atomic_open2/fs/nfs/nfs4state.c 2003-11-11 18:27:37.000000000 -0500 @@ -349,7 +349,6 @@ atomic_inc(&owner->so_count); list_add(&state->inode_states, &nfsi->open_states); state->inode = inode; - atomic_inc(&inode->i_count); spin_unlock(&inode->i_lock); } else { spin_unlock(&inode->i_lock); @@ -384,7 +383,6 @@ } while (!status); } up(&owner->so_sema); - iput(inode); nfs4_free_open_state(state); nfs4_put_state_owner(owner); } diff -u --recursive --new-file linux-2.6.0-22-atomic_open1/include/linux/nfs_fs.h linux-2.6.0-23-atomic_open2/include/linux/nfs_fs.h --- linux-2.6.0-22-atomic_open1/include/linux/nfs_fs.h 2003-11-11 18:27:02.000000000 -0500 +++ linux-2.6.0-23-atomic_open2/include/linux/nfs_fs.h 2003-11-11 18:27:37.000000000 -0500 @@ -570,6 +570,7 @@ extern int nfs4_proc_renew(struct nfs4_client *); extern int nfs4_do_close(struct inode *, struct nfs4_state *); extern int nfs4_wait_clnt_recover(struct rpc_clnt *, struct nfs4_client *); +extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); /* nfs4renewd.c */ extern void nfs4_schedule_state_renewal(struct nfs4_client *); @@ -585,6 +586,7 @@ extern void nfs4_put_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 struct nfs4_state *nfs4_find_state_bypid(struct inode *, pid_t); extern void nfs4_increment_seqid(u32 status, struct nfs4_state_owner *sp); extern int nfs4_handle_error(struct nfs_server *, int); extern void nfs4_schedule_state_recovery(struct nfs4_client *);