X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fnfs%2Finode.c;h=60b30747801240cc624dd555e717971908577911;hb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;hp=54fa2c0799caa2b019bdcf1fdeb0fa78d1c7d90d;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 54fa2c079..60b307478 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -35,10 +35,13 @@ #include #include #include +#include #include #include +#include "delegation.h" + #define NFSDBG_FACILITY NFSDBG_VFS #define NFS_PARANOIA 1 @@ -55,9 +58,8 @@ static int nfs_update_inode(struct inode *, struct nfs_fattr *, unsigned long); static struct inode *nfs_alloc_inode(struct super_block *sb); static void nfs_destroy_inode(struct inode *); -static void nfs_write_inode(struct inode *,int); +static int nfs_write_inode(struct inode *,int); static void nfs_delete_inode(struct inode *); -static void nfs_put_super(struct super_block *); static void nfs_clear_inode(struct inode *); static void nfs_umount_begin(struct super_block *); static int nfs_statfs(struct super_block *, struct kstatfs *); @@ -68,7 +70,6 @@ static struct super_operations nfs_sops = { .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 = nfs_clear_inode, .umount_begin = nfs_umount_begin, @@ -110,12 +111,16 @@ nfs_fattr_to_ino_t(struct nfs_fattr *fattr) return nfs_fileid_to_ino_t(fattr->fileid); } -static void +static int nfs_write_inode(struct inode *inode, int sync) { int flags = sync ? FLUSH_WAIT : 0; + int ret; - nfs_commit_inode(inode, 0, 0, flags); + ret = nfs_commit_inode(inode, 0, 0, flags); + if (ret < 0) + return ret; + return 0; } static void @@ -123,8 +128,9 @@ nfs_delete_inode(struct inode * inode) { dprintk("NFS: delete_inode(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); + nfs_wb_all(inode); /* - * The following can never actually happen... + * The following should never happen... */ if (nfs_have_writebacks(inode)) { printk(KERN_ERR "nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino); @@ -141,37 +147,16 @@ static void nfs_clear_inode(struct inode *inode) { struct nfs_inode *nfsi = NFS_I(inode); - struct rpc_cred *cred = nfsi->mm_cred; + struct rpc_cred *cred; - if (cred) - put_rpccred(cred); + nfs_wb_all(inode); + BUG_ON (!list_empty(&nfsi->open_files)); cred = nfsi->cache_access.cred; if (cred) put_rpccred(cred); BUG_ON(atomic_read(&nfsi->data_updates) != 0); } -void -nfs_put_super(struct super_block *sb) -{ - struct nfs_server *server = NFS_SB(sb); - - nfs4_renewd_prepare_shutdown(server); - - if (server->client != NULL) - rpc_shutdown_client(server->client); - if (server->client_sys != NULL) - rpc_shutdown_client(server->client_sys); - - if (!(server->flags & NFS_MOUNT_NONLM)) - lockd_down(); /* release rpc.lockd */ - rpciod_down(); /* release rpciod */ - - destroy_nfsv4_state(server); - - kfree(server->hostname); -} - void nfs_umount_begin(struct super_block *sb) { @@ -237,7 +222,7 @@ nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh, struct nfs_fsinfo *f error = server->rpc_ops->getroot(server, rootfh, fsinfo); if (error < 0) { - printk(KERN_NOTICE "nfs_get_root: getattr error = %d\n", -error); + dprintk("nfs_get_root: getattr error = %d\n", -error); return ERR_PTR(error); } @@ -262,6 +247,7 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) struct nfs_pathconf pathinfo = { .fattr = &fattr, }; + int no_root_error = 0; /* 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)); @@ -272,12 +258,15 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) root_inode = nfs_get_root(sb, &server->fh, &fsinfo); /* Did getting the root inode fail? */ - if (IS_ERR(root_inode)) + if (IS_ERR(root_inode)) { + no_root_error = PTR_ERR(root_inode); goto out_no_root; + } sb->s_root = d_alloc_root(root_inode); - if (!sb->s_root) + if (!sb->s_root) { + no_root_error = -ENOMEM; goto out_no_root; - + } sb->s_root->d_op = server->rpc_ops->dentry_ops; /* Get some general file system info */ @@ -289,14 +278,6 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) server->rsize = nfs_block_size(fsinfo.rtpref, NULL); if (server->wsize == 0) server->wsize = nfs_block_size(fsinfo.wtpref, NULL); - if (sb->s_blocksize == 0) { - if (fsinfo.wtmult == 0) { - sb->s_blocksize = 512; - sb->s_blocksize_bits = 9; - } else - sb->s_blocksize = nfs_block_bits(fsinfo.wtmult, - &sb->s_blocksize_bits); - } if (fsinfo.rtmax >= 512 && server->rsize > fsinfo.rtmax) server->rsize = nfs_block_size(fsinfo.rtmax, NULL); @@ -315,6 +296,11 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) server->wsize = server->wpages << PAGE_CACHE_SHIFT; } + if (sb->s_blocksize == 0) + sb->s_blocksize = nfs_block_bits(server->wsize, + &sb->s_blocksize_bits); + server->wtmult = nfs_block_bits(fsinfo.wtmult, NULL); + server->dtsize = nfs_block_size(fsinfo.dtpref, NULL); if (server->dtsize > PAGE_CACHE_SIZE) server->dtsize = PAGE_CACHE_SIZE; @@ -328,6 +314,9 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) } server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; + if (server->flags & NFS_MOUNT_TAGXID) + sb->s_flags |= MS_TAGXID; + sb->s_maxbytes = fsinfo.maxfilesize; if (sb->s_maxbytes > MAX_LFS_FILESIZE) sb->s_maxbytes = MAX_LFS_FILESIZE; @@ -337,10 +326,10 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) return 0; /* Yargs. It didn't work out. */ out_no_root: - printk("nfs_read_super: get root inode failed\n"); + dprintk("nfs_sb_init: get root inode failed: errno %d\n", -no_root_error); if (!IS_ERR(root_inode)) iput(root_inode); - return -EINVAL; + return no_root_error; } /* @@ -382,6 +371,7 @@ nfs_create_client(struct nfs_server *server, const struct nfs_mount_data *data) 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_tagxid = (server->flags & NFS_MOUNT_TAGXID) ? 1 : 0; clnt->cl_chatty = 1; return clnt; @@ -401,7 +391,6 @@ static int nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) { struct nfs_server *server; - int err = -EIO; rpc_authflavor_t authflavor; server = NFS_SB(sb); @@ -420,10 +409,14 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) server->acdirmin = data->acdirmin*HZ; server->acdirmax = data->acdirmax*HZ; + /* Start lockd here, before we might error out */ + if (!(server->flags & NFS_MOUNT_NONLM)) + lockd_up(); + server->namelen = data->namlen; server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL); if (!server->hostname) - goto out_fail; + return -ENOMEM; strcpy(server->hostname, data->hostname); /* Check NFS protocol revision and initialize RPC op vector @@ -434,11 +427,11 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) server->caps |= NFS_CAP_READDIRPLUS; if (data->version < 4) { printk(KERN_NOTICE "NFS: NFSv3 not supported by mount program.\n"); - goto out_fail; + return -EIO; } #else printk(KERN_NOTICE "NFS: NFSv3 not supported.\n"); - goto out_fail; + return -EIO; #endif } else { server->rpc_ops = &nfs_v2_clientops; @@ -452,31 +445,20 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) /* Create RPC client handles */ server->client = nfs_create_client(server, data); - if (server->client == NULL) - goto out_fail; + if (IS_ERR(server->client)) + return PTR_ERR(server->client); /* RFC 2623, sec 2.3.2 */ if (authflavor != RPC_AUTH_UNIX) { server->client_sys = rpc_clone_client(server->client); - if (server->client_sys == NULL) - goto out_shutdown; + if (IS_ERR(server->client_sys)) + return PTR_ERR(server->client_sys); if (!rpcauth_create(RPC_AUTH_UNIX, server->client_sys)) - goto out_shutdown; + return -ENOMEM; } else { atomic_inc(&server->client->cl_count); server->client_sys = server->client; } - /* Fire up rpciod if not yet running */ - if (rpciod_up() != 0) { - printk(KERN_WARNING "NFS: couldn't start rpciod!\n"); - goto out_shutdown; - } - - sb->s_op = &nfs_sops; - err = nfs_sb_init(sb, authflavor); - if (err != 0) - goto out_noinit; - if (server->flags & NFS_MOUNT_VER3) { if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) server->namelen = NFS3_MAXNAMLEN; @@ -485,21 +467,8 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) server->namelen = NFS2_MAXNAMLEN; } - /* Check whether to start the lockd process */ - if (!(server->flags & NFS_MOUNT_NONLM)) - lockd_up(); - return 0; -out_noinit: - rpciod_down(); -out_shutdown: - if (server->client) - rpc_shutdown_client(server->client); - if (server->client_sys) - rpc_shutdown_client(server->client_sys); -out_fail: - if (server->hostname) - kfree(server->hostname); - return err; + sb->s_op = &nfs_sops; + return nfs_sb_init(sb, authflavor); } static int @@ -522,6 +491,7 @@ nfs_statfs(struct super_block *sb, struct kstatfs *buf) if (error < 0) goto out_err; + buf->f_frsize = server->wtmult; buf->f_bsize = sb->s_blocksize; blockbits = sb->s_blocksize_bits; blockres = (1 << blockbits) - 1; @@ -559,6 +529,7 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) { NFS_MOUNT_NOAC, ",noac", "" }, { NFS_MOUNT_NONLM, ",nolock", ",lock" }, { NFS_MOUNT_BROKEN_SUID, ",broken_suid", "" }, + { NFS_MOUNT_TAGXID, ",tagxid", "" }, { 0, NULL, NULL } }; struct proc_nfs_info *nfs_infop; @@ -638,7 +609,7 @@ nfs_find_actor(struct inode *inode, void *opaque) if (NFS_FILEID(inode) != fattr->fileid) return 0; - if (memcmp(NFS_FH(inode), fh, sizeof(struct nfs_fh)) != 0) + if (nfs_compare_fh(NFS_FH(inode), fh)) return 0; if (is_bad_inode(inode)) return 0; @@ -649,11 +620,10 @@ static int nfs_init_locked(struct inode *inode, void *opaque) { struct nfs_find_desc *desc = (struct nfs_find_desc *)opaque; - struct nfs_fh *fh = desc->fh; struct nfs_fattr *fattr = desc->fattr; NFS_FILEID(inode) = fattr->fileid; - memcpy(NFS_FH(inode), fh, sizeof(struct nfs_fh)); + nfs_copy_fh(NFS_FH(inode), desc->fh); return 0; } @@ -724,8 +694,10 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) nfsi->change_attr = fattr->change_attr; inode->i_size = nfs_size_to_loff_t(fattr->size); inode->i_nlink = fattr->nlink; - inode->i_uid = fattr->uid; - inode->i_gid = fattr->gid; + inode->i_uid = INOXID_UID(XID_TAG(inode), fattr->uid, fattr->gid); + inode->i_gid = INOXID_GID(XID_TAG(inode), fattr->uid, fattr->gid); + inode->i_xid = INOXID_XID(XID_TAG(inode), fattr->uid, fattr->gid, 0); + /* maybe fattr->xid someday */ if (fattr->valid & (NFS_ATTR_FATTR_V3 | NFS_ATTR_FATTR_V4)) { /* * report the blocks in 512byte units @@ -751,7 +723,12 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) out: return inode; - +/* +fail_dlim: + make_bad_inode(inode); + iput(inode); + inode = NULL; +*/ out_no_inode: printk("nfs_fhget: iget failed\n"); goto out; @@ -797,6 +774,8 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr) inode->i_uid = attr->ia_uid; if ((attr->ia_valid & ATTR_GID) != 0) inode->i_gid = attr->ia_gid; + if ((attr->ia_valid & ATTR_XID) != 0) + inode->i_xid = attr->ia_xid; if ((attr->ia_valid & ATTR_SIZE) != 0) { inode->i_size = attr->ia_size; vmtruncate(inode, attr->ia_size); @@ -855,53 +834,114 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) return err; } +struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, struct rpc_cred *cred) +{ + struct nfs_open_context *ctx; + + ctx = (struct nfs_open_context *)kmalloc(sizeof(*ctx), GFP_KERNEL); + if (ctx != NULL) { + atomic_set(&ctx->count, 1); + ctx->dentry = dget(dentry); + ctx->cred = get_rpccred(cred); + ctx->state = NULL; + ctx->lockowner = current->files; + ctx->error = 0; + init_waitqueue_head(&ctx->waitq); + } + return ctx; +} + +struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx) +{ + if (ctx != NULL) + atomic_inc(&ctx->count); + return ctx; +} + +void put_nfs_open_context(struct nfs_open_context *ctx) +{ + if (atomic_dec_and_test(&ctx->count)) { + if (ctx->state != NULL) + nfs4_close_state(ctx->state, ctx->mode); + if (ctx->cred != NULL) + put_rpccred(ctx->cred); + dput(ctx->dentry); + kfree(ctx); + } +} + /* * Ensure that mmap has a recent RPC credential for use when writing out * shared pages */ -void -nfs_set_mmcred(struct inode *inode, struct rpc_cred *cred) +void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct nfs_inode *nfsi = NFS_I(inode); + + filp->private_data = get_nfs_open_context(ctx); + spin_lock(&inode->i_lock); + list_add(&ctx->list, &nfsi->open_files); + spin_unlock(&inode->i_lock); +} + +struct nfs_open_context *nfs_find_open_context(struct inode *inode, int mode) { - struct rpc_cred **p = &NFS_I(inode)->mm_cred, - *oldcred = *p; + struct nfs_inode *nfsi = NFS_I(inode); + struct nfs_open_context *pos, *ctx = NULL; - *p = get_rpccred(cred); - if (oldcred) - put_rpccred(oldcred); + spin_lock(&inode->i_lock); + list_for_each_entry(pos, &nfsi->open_files, list) { + if ((pos->mode & mode) == mode) { + ctx = get_nfs_open_context(pos); + break; + } + } + spin_unlock(&inode->i_lock); + return ctx; +} + +void nfs_file_clear_open_context(struct file *filp) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct nfs_open_context *ctx = (struct nfs_open_context *)filp->private_data; + + if (ctx) { + filp->private_data = NULL; + spin_lock(&inode->i_lock); + list_del(&ctx->list); + spin_unlock(&inode->i_lock); + put_nfs_open_context(ctx); + } } /* - * These are probably going to contain hooks for - * allocating and releasing RPC credentials for - * the file. I'll have to think about Tronds patch - * a bit more.. + * These allocate and release file read/write context information. */ int nfs_open(struct inode *inode, struct file *filp) { - struct rpc_auth *auth; + struct nfs_open_context *ctx; struct rpc_cred *cred; - auth = NFS_CLIENT(inode)->cl_auth; - cred = rpcauth_lookupcred(auth, 0); - filp->private_data = cred; - if ((filp->f_mode & FMODE_WRITE) != 0) { - nfs_set_mmcred(inode, cred); + if ((cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0)) == NULL) + return -ENOMEM; + ctx = alloc_nfs_open_context(filp->f_dentry, cred); + put_rpccred(cred); + if (ctx == NULL) + return -ENOMEM; + ctx->mode = filp->f_mode; + nfs_file_set_open_context(filp, ctx); + put_nfs_open_context(ctx); + if ((filp->f_mode & FMODE_WRITE) != 0) nfs_begin_data_update(inode); - } return 0; } int nfs_release(struct inode *inode, struct file *filp) { - struct rpc_cred *cred; - - lock_kernel(); if ((filp->f_mode & FMODE_WRITE) != 0) nfs_end_data_update(inode); - cred = nfs_file_cred(filp); - if (cred) - put_rpccred(cred); - unlock_kernel(); + nfs_file_clear_open_context(filp); return 0; } @@ -942,7 +982,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) /* Protect against RPC races by saving the change attribute */ verifier = nfs_save_change_attribute(inode); - status = NFS_PROTO(inode)->getattr(inode, &fattr); + status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), &fattr); if (status) { dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n", inode->i_sb->s_id, @@ -998,6 +1038,30 @@ out: return status; } +int nfs_attribute_timeout(struct inode *inode) +{ + struct nfs_inode *nfsi = NFS_I(inode); + + if (nfs_have_delegation(inode, FMODE_READ)) + return 0; + return time_after(jiffies, nfsi->read_cache_jiffies+nfsi->attrtimeo); +} + +/** + * nfs_revalidate_inode - Revalidate the inode attributes + * @server - pointer to nfs_server struct + * @inode - pointer to inode struct + * + * Updates inode attribute information by retrieving the data from the server. + */ +int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) +{ + if (!(NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA)) + && !nfs_attribute_timeout(inode)) + return NFS_STALE(inode) ? -ESTALE : 0; + return __nfs_revalidate_inode(server, inode); +} + /** * nfs_begin_data_update * @inode - pointer to inode @@ -1019,11 +1083,13 @@ void nfs_end_data_update(struct inode *inode) { struct nfs_inode *nfsi = NFS_I(inode); - /* Mark the attribute cache for revalidation */ - nfsi->flags |= NFS_INO_INVALID_ATTR; - /* Directories and symlinks: invalidate page cache too */ - if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) - nfsi->flags |= NFS_INO_INVALID_DATA; + if (!nfs_have_delegation(inode, FMODE_READ)) { + /* Mark the attribute cache for revalidation */ + nfsi->flags |= NFS_INO_INVALID_ATTR; + /* Directories and symlinks: invalidate page cache too */ + if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) + nfsi->flags |= NFS_INO_INVALID_DATA; + } nfsi->cache_change_attribute ++; atomic_dec(&nfsi->data_updates); } @@ -1063,6 +1129,13 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) struct nfs_inode *nfsi = NFS_I(inode); loff_t cur_size, new_isize; int data_unstable; + uid_t uid; + gid_t gid; + xid_t xid = 0; + + /* Do we hold a delegation? */ + if (nfs_have_delegation(inode, FMODE_READ)) + return 0; /* Are we in the process of updating data on the server? */ data_unstable = nfs_caches_unstable(inode); @@ -1102,10 +1175,15 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) } else if (S_ISREG(inode->i_mode) && new_isize > cur_size) nfsi->flags |= NFS_INO_INVALID_ATTR; + uid = INOXID_UID(XID_TAG(inode), fattr->uid, fattr->gid); + gid = INOXID_GID(XID_TAG(inode), fattr->uid, fattr->gid); + xid = INOXID_XID(XID_TAG(inode), fattr->uid, fattr->gid, 0); + /* Have any file permissions changed? */ if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO) - || inode->i_uid != fattr->uid - || inode->i_gid != fattr->gid) + || inode->i_uid != uid + || inode->i_gid != gid + || inode->i_xid != xid) nfsi->flags |= NFS_INO_INVALID_ATTR; /* Has the link count changed? */ @@ -1139,6 +1217,9 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign unsigned int invalid = 0; loff_t cur_isize; int data_unstable; + uid_t uid; + gid_t gid; + xid_t xid = 0; dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n", __FUNCTION__, inode->i_sb->s_id, inode->i_ino, @@ -1221,9 +1302,14 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime)); memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime)); + uid = INOXID_UID(XID_TAG(inode), fattr->uid, fattr->gid); + gid = INOXID_GID(XID_TAG(inode), fattr->uid, fattr->gid); + xid = INOXID_XID(XID_TAG(inode), fattr->uid, fattr->gid, 0); + if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO) || - inode->i_uid != fattr->uid || - inode->i_gid != fattr->gid) { + inode->i_uid != uid || + inode->i_gid != gid || + inode->i_xid != xid) { struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred; if (*cred) { put_rpccred(*cred); @@ -1234,8 +1320,9 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign inode->i_mode = fattr->mode; inode->i_nlink = fattr->nlink; - inode->i_uid = fattr->uid; - inode->i_gid = fattr->gid; + inode->i_uid = uid; + inode->i_gid = gid; + inode->i_xid = xid; if (fattr->valid & (NFS_ATTR_FATTR_V3 | NFS_ATTR_FATTR_V4)) { /* @@ -1261,7 +1348,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) invalid &= ~NFS_INO_INVALID_DATA; - nfsi->flags |= invalid; + if (!nfs_have_delegation(inode, FMODE_READ)) + nfsi->flags |= invalid; return 0; out_changed: @@ -1301,7 +1389,7 @@ static int nfs_compare_super(struct super_block *sb, void *data) return 0; if (old->addr.sin_port != server->addr.sin_port) return 0; - return !memcmp(&old->fh, &server->fh, sizeof(struct nfs_fh)); + return !nfs_compare_fh(&old->fh, &server->fh); } static struct super_block *nfs_get_sb(struct file_system_type *fs_type, @@ -1325,11 +1413,6 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type, /* Zero out the NFS state stuff */ init_nfsv4_state(server); - root = &server->fh; - memcpy(root, &data->root, sizeof(*root)); - if (root->size < sizeof(root->data)) - memset(root->data+root->size, 0, sizeof(root->data)-root->size); - if (data->version != NFS_MOUNT_VERSION) { printk("nfs warning: mount version %s than kernel\n", data->version < NFS_MOUNT_VERSION ? "older" : "newer"); @@ -1339,19 +1422,25 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type, data->bsize = 0; if (data->version < 4) { data->flags &= ~NFS_MOUNT_VER3; - memset(root, 0, sizeof(*root)); - root->size = NFS2_FHSIZE; - memcpy(root->data, data->old_root.data, NFS2_FHSIZE); + data->root.size = NFS2_FHSIZE; + memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE); } if (data->version < 5) data->flags &= ~NFS_MOUNT_SECFLAVOUR; } + root = &server->fh; + if (data->flags & NFS_MOUNT_VER3) + root->size = data->root.size; + else + root->size = NFS2_FHSIZE; if (root->size > sizeof(root->data)) { printk("nfs_get_sb: invalid root filehandle\n"); kfree(server); return ERR_PTR(-EINVAL); } + memcpy(root->data, data->root.data, root->size); + /* We now require that the mount process passes the remote address */ memcpy(&server->addr, &data->addr, sizeof(server->addr)); if (server->addr.sin_addr.s_addr == INADDR_ANY) { @@ -1369,6 +1458,13 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type, s->s_flags = flags; + /* Fire up rpciod if not yet running */ + if (rpciod_up() != 0) { + printk(KERN_WARNING "NFS: couldn't start rpciod!\n"); + kfree(server); + return ERR_PTR(-EIO); + } + error = nfs_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0); if (error) { up_write(&s->s_umount); @@ -1382,7 +1478,25 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type, static void nfs_kill_super(struct super_block *s) { struct nfs_server *server = NFS_SB(s); + kill_anon_super(s); + + nfs4_renewd_prepare_shutdown(server); + + if (server->client != NULL && !IS_ERR(server->client)) + rpc_shutdown_client(server->client); + if (server->client_sys != NULL && !IS_ERR(server->client_sys)) + rpc_shutdown_client(server->client_sys); + + if (!(server->flags & NFS_MOUNT_NONLM)) + lockd_down(); /* release rpc.lockd */ + + rpciod_down(); /* release rpciod */ + + destroy_nfsv4_state(server); + + if (server->hostname != NULL) + kfree(server->hostname); kfree(server); } @@ -1398,12 +1512,12 @@ static struct file_system_type nfs_fs_type = { 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, @@ -1419,6 +1533,12 @@ static void nfs4_clear_inode(struct inode *inode) { struct nfs_inode *nfsi = NFS_I(inode); + /* If we are holding a delegation, return it! */ + if (nfsi->delegation != NULL) + nfs_inode_return_delegation(inode); + /* First call standard NFS clear_inode() code */ + nfs_clear_inode(inode); + /* Now clear out any remaining state */ while (!list_empty(&nfsi->open_states)) { struct nfs4_state *state; @@ -1433,8 +1553,6 @@ static void nfs4_clear_inode(struct inode *inode) BUG_ON(atomic_read(&state->count) != 1); nfs4_close_state(state, state->state); } - /* Now call standard NFS clear_inode() code */ - nfs_clear_inode(inode); } @@ -1494,7 +1612,7 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, clp = nfs4_get_client(&server->addr.sin_addr); if (!clp) { printk(KERN_WARNING "NFS: failed to create NFS4 client.\n"); - goto out_fail; + return -EIO; } /* Now create transport and client */ @@ -1532,8 +1650,13 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); nfs_idmap_new(clp); } - if (list_empty(&clp->cl_superblocks)) - clear_bit(NFS4CLNT_OK, &clp->cl_state); + if (list_empty(&clp->cl_superblocks)) { + err = nfs4_init_client(clp); + if (err != 0) { + up_write(&clp->cl_sem); + goto out_fail; + } + } list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks); clnt = rpc_clone_client(clp->cl_rpcclient); if (!IS_ERR(clnt)) @@ -1543,44 +1666,29 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, if (IS_ERR(clnt)) { printk(KERN_WARNING "NFS: cannot create RPC client.\n"); - err = PTR_ERR(clnt); - goto out_remove_list; - } - err = -ENOMEM; - if (server->nfs4_state->cl_idmap == NULL) { - printk(KERN_WARNING "NFS: failed to create idmapper.\n"); - goto out_shutdown; + 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) { + printk(KERN_WARNING "NFS: failed to create idmapper.\n"); + return -ENOMEM; + } + if (clnt->cl_auth->au_flavor != authflavour) { if (rpcauth_create(authflavour, clnt) == NULL) { printk(KERN_WARNING "NFS: couldn't create credcache!\n"); - goto out_shutdown; + return -ENOMEM; } } - /* Fire up rpciod if not yet running */ - if (rpciod_up() != 0) { - printk(KERN_WARNING "NFS: couldn't start rpciod!\n"); - goto out_shutdown; - } - sb->s_op = &nfs4_sops; err = nfs_sb_init(sb, authflavour); if (err == 0) return 0; - rpciod_down(); -out_shutdown: - rpc_shutdown_client(server->client); -out_remove_list: - down_write(&server->nfs4_state->cl_sem); - list_del_init(&server->nfs4_siblings); - up_write(&server->nfs4_state->cl_sem); - destroy_nfsv4_state(server); out_fail: if (clp) nfs4_put_client(clp); @@ -1686,6 +1794,13 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type, s->s_flags = flags; + /* Fire up rpciod if not yet running */ + if (rpciod_up() != 0) { + printk(KERN_WARNING "NFS: couldn't start rpciod!\n"); + s = ERR_PTR(-EIO); + goto out_free; + } + error = nfs4_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0); if (error) { up_write(&s->s_umount); @@ -1705,22 +1820,31 @@ out_free: return s; } +static void nfs4_kill_super(struct super_block *sb) +{ + nfs_return_all_delegations(sb); + nfs_kill_super(sb); +} + static struct file_system_type nfs4_fs_type = { .owner = THIS_MODULE, .name = "nfs4", .get_sb = nfs4_get_sb, - .kill_sb = nfs_kill_super, + .kill_sb = nfs4_kill_super, .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, }; -#define nfs4_zero_state(nfsi) \ +#define nfs4_init_once(nfsi) \ do { \ INIT_LIST_HEAD(&(nfsi)->open_states); \ + nfsi->delegation = NULL; \ + nfsi->delegation_state = 0; \ + init_rwsem(&nfsi->rwsem); \ } while(0) #define register_nfs4fs() register_filesystem(&nfs4_fs_type) #define unregister_nfs4fs() unregister_filesystem(&nfs4_fs_type) #else -#define nfs4_zero_state(nfsi) \ +#define nfs4_init_once(nfsi) \ do { } while (0) #define register_nfs4fs() (0) #define unregister_nfs4fs() @@ -1742,8 +1866,6 @@ static struct inode *nfs_alloc_inode(struct super_block *sb) if (!nfsi) return NULL; nfsi->flags = 0; - nfsi->mm_cred = NULL; - nfs4_zero_state(nfsi); return &nfsi->vfs_inode; } @@ -1759,14 +1881,17 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { inode_init_once(&nfsi->vfs_inode); + spin_lock_init(&nfsi->req_lock); INIT_LIST_HEAD(&nfsi->dirty); INIT_LIST_HEAD(&nfsi->commit); + INIT_LIST_HEAD(&nfsi->open_files); INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC); atomic_set(&nfsi->data_updates, 0); nfsi->ndirty = 0; nfsi->ncommit = 0; nfsi->npages = 0; init_waitqueue_head(&nfsi->nfs_i_wait); + nfs4_init_once(nfsi); } } @@ -1774,7 +1899,7 @@ int nfs_init_inodecache(void) { nfs_inode_cachep = kmem_cache_create("nfs_inode_cache", sizeof(struct nfs_inode), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (nfs_inode_cachep == NULL) return -ENOMEM;