diff -u --recursive --new-file linux-2.6.0-21-sock_disconnect/fs/nfs/dir.c linux-2.6.0-22-atomic_open1/fs/nfs/dir.c --- linux-2.6.0-21-sock_disconnect/fs/nfs/dir.c 2003-11-10 12:50:27.000000000 -0500 +++ linux-2.6.0-22-atomic_open1/fs/nfs/dir.c 2003-11-11 02:28:01.000000000 -0500 @@ -72,6 +72,26 @@ .setattr = nfs_setattr, }; +#ifdef CONFIG_NFS_V4 + +static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *); +struct inode_operations nfs4_dir_inode_operations = { + .create = nfs_create, + .lookup = nfs_atomic_lookup, + .link = nfs_link, + .unlink = nfs_unlink, + .symlink = nfs_symlink, + .mkdir = nfs_mkdir, + .rmdir = nfs_rmdir, + .mknod = nfs_mknod, + .rename = nfs_rename, + .permission = nfs_permission, + .getattr = nfs_getattr, + .setattr = nfs_setattr, +}; + +#endif /* CONFIG_NFS_V4 */ + /* * Open file */ @@ -670,7 +690,7 @@ goto out; error = -ENOMEM; - dentry->d_op = &nfs_dentry_operations; + dentry->d_op = NFS_PROTO(dir)->dentry_ops; lock_kernel(); @@ -702,6 +722,57 @@ return ERR_PTR(error); } +#ifdef CONFIG_NFS_V4 +/* + * FIXME: + * We need to funnel all NFSv4 lookups through nfs_lookup(), since + * NFSv4 OPEN calls are not idempotent. + * We still want to be able to cache dentries in the dcache though. + * + * Hack: Hide the cached dentries from the VFS by allowing + * d_lookup() to find files iff string length is negative. + */ +static int nfs_dentry_compare(struct dentry *parent, struct qstr *qstr, + struct qstr *name) +{ + int len = -name->len; + + if (qstr->len != len) + return -1; + return memcmp(qstr->name, name->name, len); +} + +struct dentry_operations nfs4_dentry_operations = { + .d_revalidate = nfs_lookup_revalidate, + .d_compare = nfs_dentry_compare, + .d_delete = nfs_dentry_delete, + .d_iput = nfs_dentry_iput, +}; + +static struct dentry *nfs_dcache_lookup(struct dentry *parent, struct qstr *qstr) +{ + struct qstr name = { + .len = -qstr->len, + .name = qstr->name, + .hash = qstr->hash + }; + return d_lookup(parent, &name); +} + +static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +{ + struct dentry *cached; + + cached = nfs_dcache_lookup(dentry->d_parent, &dentry->d_name); + if (cached != NULL) { + if (nfs_lookup_revalidate(cached, nd)) + return cached; + dput(cached); + } + return nfs_lookup(dir, dentry, nd); +} +#endif /* CONFIG_NFSV4 */ + static inline int find_dirent_name(nfs_readdir_descriptor_t *desc, struct page *page, struct dentry *dentry) { diff -u --recursive --new-file linux-2.6.0-21-sock_disconnect/fs/nfs/inode.c linux-2.6.0-22-atomic_open1/fs/nfs/inode.c --- linux-2.6.0-21-sock_disconnect/fs/nfs/inode.c 2003-11-10 18:49:40.000000000 -0500 +++ linux-2.6.0-22-atomic_open1/fs/nfs/inode.c 2003-11-10 18:48:49.000000000 -0500 @@ -312,7 +312,7 @@ if (!sb->s_root) goto out_no_root; - sb->s_root->d_op = &nfs_dentry_operations; + sb->s_root->d_op = server->rpc_ops->dentry_ops; /* Get some general file system info */ if (server->rpc_ops->fsinfo(server, &server->fh, &fsinfo) < 0) { @@ -745,7 +745,7 @@ inode->i_data.a_ops = &nfs_file_aops; inode->i_data.backing_dev_info = &NFS_SB(sb)->backing_dev_info; } else if (S_ISDIR(inode->i_mode)) { - inode->i_op = &nfs_dir_inode_operations; + inode->i_op = NFS_SB(sb)->rpc_ops->dir_inode_ops; inode->i_fop = &nfs_dir_operations; if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS) && fattr->size <= NFS_LIMIT_READDIRPLUS) diff -u --recursive --new-file linux-2.6.0-21-sock_disconnect/fs/nfs/nfs3proc.c linux-2.6.0-22-atomic_open1/fs/nfs/nfs3proc.c --- linux-2.6.0-21-sock_disconnect/fs/nfs/nfs3proc.c 2003-11-10 12:50:28.000000000 -0500 +++ linux-2.6.0-22-atomic_open1/fs/nfs/nfs3proc.c 2003-11-10 16:10:27.000000000 -0500 @@ -898,6 +898,8 @@ struct nfs_rpc_ops nfs_v3_clientops = { .version = 3, /* protocol version */ + .dentry_ops = &nfs_dentry_operations, + .dir_inode_ops = &nfs_dir_inode_operations, .getroot = nfs3_proc_get_root, .getattr = nfs3_proc_getattr, .setattr = nfs3_proc_setattr, diff -u --recursive --new-file linux-2.6.0-21-sock_disconnect/fs/nfs/nfs4proc.c linux-2.6.0-22-atomic_open1/fs/nfs/nfs4proc.c --- linux-2.6.0-21-sock_disconnect/fs/nfs/nfs4proc.c 2003-11-10 13:04:02.000000000 -0500 +++ linux-2.6.0-22-atomic_open1/fs/nfs/nfs4proc.c 2003-11-10 16:10:22.000000000 -0500 @@ -1922,6 +1922,8 @@ struct nfs_rpc_ops nfs_v4_clientops = { .version = 4, /* protocol version */ + .dentry_ops = &nfs4_dentry_operations, + .dir_inode_ops = &nfs4_dir_inode_operations, .getroot = nfs4_proc_get_root, .getattr = nfs4_proc_getattr, .setattr = nfs4_proc_setattr, diff -u --recursive --new-file linux-2.6.0-21-sock_disconnect/fs/nfs/proc.c linux-2.6.0-22-atomic_open1/fs/nfs/proc.c --- linux-2.6.0-21-sock_disconnect/fs/nfs/proc.c 2003-11-10 12:53:56.000000000 -0500 +++ linux-2.6.0-22-atomic_open1/fs/nfs/proc.c 2003-11-10 16:10:06.000000000 -0500 @@ -656,6 +656,8 @@ struct nfs_rpc_ops nfs_v2_clientops = { .version = 2, /* protocol version */ + .dentry_ops = &nfs_dentry_operations, + .dir_inode_ops = &nfs_dir_inode_operations, .getroot = nfs_proc_get_root, .getattr = nfs_proc_getattr, .setattr = nfs_proc_setattr, diff -u --recursive --new-file linux-2.6.0-21-sock_disconnect/include/linux/nfs_fs.h linux-2.6.0-22-atomic_open1/include/linux/nfs_fs.h --- linux-2.6.0-21-sock_disconnect/include/linux/nfs_fs.h 2003-11-10 13:03:19.000000000 -0500 +++ linux-2.6.0-22-atomic_open1/include/linux/nfs_fs.h 2003-11-10 17:51:19.000000000 -0500 @@ -559,6 +559,9 @@ }; +extern struct dentry_operations nfs4_dentry_operations; +extern struct inode_operations nfs4_dir_inode_operations; + /* nfs4proc.c */ extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short); extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); diff -u --recursive --new-file linux-2.6.0-21-sock_disconnect/include/linux/nfs_xdr.h linux-2.6.0-22-atomic_open1/include/linux/nfs_xdr.h --- linux-2.6.0-21-sock_disconnect/include/linux/nfs_xdr.h 2003-11-10 13:02:38.000000000 -0500 +++ linux-2.6.0-22-atomic_open1/include/linux/nfs_xdr.h 2003-11-10 17:50:05.000000000 -0500 @@ -637,6 +637,8 @@ */ struct nfs_rpc_ops { int version; /* Protocol version */ + struct dentry_operations *dentry_ops; + struct inode_operations *dir_inode_ops; int (*getroot) (struct nfs_server *, struct nfs_fh *, struct nfs_fattr *);