/* * Lock a (portion of) a file */ int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) { struct inode * inode = filp->f_dentry->d_inode; int status = 0; dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n", inode->i_dev, inode->i_ino, fl->fl_type, fl->fl_flags, (long long)fl->fl_start, (long long)fl->fl_end); if (!inode) return -EINVAL; /* No mandatory locks over NFS */ if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) return -ENOLCK; /* Fake OK code if mounted without NLM support */ if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) { if (cmd == F_GETLK) status = LOCK_USE_CLNT; goto out_ok; } /* * No BSD flocks over NFS allowed. * Note: we could try to fake a POSIX lock request here by * using ((u32) filp | 0x80000000) or some such as the pid. * 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|FL_BROKEN)) != FL_POSIX) return -ENOLCK; /* * Flush all pending writes before doing anything * with locks.. */ down(&filp->f_dentry->d_inode->i_sem); status = nfs_wb_all(inode); up(&filp->f_dentry->d_inode->i_sem); if (status < 0) return status; if ((status = nlmclnt_proc(inode, cmd, fl)) < 0) return status; else status = 0; /* * Make sure we clear the cache whenever we try to get the lock. * This makes locking act as a cache coherency point. */ out_ok: if ((cmd == F_SETLK || cmd == F_SETLKW) && fl->fl_type != F_UNLCK) { down(&filp->f_dentry->d_inode->i_sem); nfs_wb_all(inode); /* we may have slept */ nfs_zap_caches(inode); up(&filp->f_dentry->d_inode->i_sem); } return status; }
static int nfs4_file_open(struct inode *inode, struct file *filp) { struct nfs_open_context *ctx; struct dentry *dentry = filp->f_path.dentry; struct dentry *parent = NULL; struct inode *dir; unsigned openflags = filp->f_flags; struct iattr attr; int opened = 0; int err; /* * If no cached dentry exists or if it's negative, NFSv4 handled the * opens in ->lookup() or ->create(). * * We only get this far for a cached positive dentry. We skipped * revalidation, so handle it here by dropping the dentry and returning * -EOPENSTALE. The VFS will retry the lookup/create/open. */ dprintk("NFS: open file(%s/%s)\n", dentry->d_parent->d_name.name, dentry->d_name.name); if ((openflags & O_ACCMODE) == 3) openflags--; /* We can't create new files here */ openflags &= ~(O_CREAT|O_EXCL); parent = dget_parent(dentry); dir = parent->d_inode; ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode); err = PTR_ERR(ctx); if (IS_ERR(ctx)) goto out; attr.ia_valid = ATTR_OPEN; if (openflags & O_TRUNC) { attr.ia_valid |= ATTR_SIZE; attr.ia_size = 0; nfs_wb_all(inode); } inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr, &opened); if (IS_ERR(inode)) { err = PTR_ERR(inode); switch (err) { case -EPERM: case -EACCES: case -EDQUOT: case -ENOSPC: case -EROFS: goto out_put_ctx; default: goto out_drop; } } if (inode != dentry->d_inode) goto out_drop; nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_file_set_open_context(filp, ctx); nfs_fscache_open_file(inode, filp); err = 0; out_put_ctx: put_nfs_open_context(ctx); out: dput(parent); return err; out_drop: d_drop(dentry); err = -EOPENSTALE; goto out_put_ctx; }
static int nfs3_return_delegation(struct inode *inode) { nfs_wb_all(inode); return 0; }
static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep, loff_t offset, loff_t len) { struct inode *inode = file_inode(filep); struct nfs_server *server = NFS_SERVER(inode); struct nfs42_falloc_args args = { .falloc_fh = NFS_FH(inode), .falloc_offset = offset, .falloc_length = len, .falloc_bitmask = server->cache_consistency_bitmask, }; struct nfs42_falloc_res res = { .falloc_server = server, }; int status; msg->rpc_argp = &args; msg->rpc_resp = &res; status = nfs42_set_rw_stateid(&args.falloc_stateid, filep, FMODE_WRITE); if (status) return status; res.falloc_fattr = nfs_alloc_fattr(); if (!res.falloc_fattr) return -ENOMEM; status = nfs4_call_sync(server->client, server, msg, &args.seq_args, &res.seq_res, 0); if (status == 0) status = nfs_post_op_update_inode(inode, res.falloc_fattr); kfree(res.falloc_fattr); return status; } static int nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep, loff_t offset, loff_t len) { struct nfs_server *server = NFS_SERVER(file_inode(filep)); struct nfs4_exception exception = { }; int err; do { err = _nfs42_proc_fallocate(msg, filep, offset, len); if (err == -ENOTSUPP) return -EOPNOTSUPP; err = nfs4_handle_exception(server, err, &exception); } while (exception.retry); return err; } int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ALLOCATE], }; struct inode *inode = file_inode(filep); int err; if (!nfs_server_capable(inode, NFS_CAP_ALLOCATE)) return -EOPNOTSUPP; mutex_lock(&inode->i_mutex); err = nfs42_proc_fallocate(&msg, filep, offset, len); if (err == -EOPNOTSUPP) NFS_SERVER(inode)->caps &= ~NFS_CAP_ALLOCATE; mutex_unlock(&inode->i_mutex); return err; } int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DEALLOCATE], }; struct inode *inode = file_inode(filep); int err; if (!nfs_server_capable(inode, NFS_CAP_DEALLOCATE)) return -EOPNOTSUPP; nfs_wb_all(inode); mutex_lock(&inode->i_mutex); err = nfs42_proc_fallocate(&msg, filep, offset, len); if (err == 0) truncate_pagecache_range(inode, offset, (offset + len) -1); if (err == -EOPNOTSUPP) NFS_SERVER(inode)->caps &= ~NFS_CAP_DEALLOCATE; mutex_unlock(&inode->i_mutex); return err; } static loff_t _nfs42_proc_llseek(struct file *filep, loff_t offset, int whence) { struct inode *inode = file_inode(filep); struct nfs42_seek_args args = { .sa_fh = NFS_FH(inode), .sa_offset = offset, .sa_what = (whence == SEEK_HOLE) ? NFS4_CONTENT_HOLE : NFS4_CONTENT_DATA, }; struct nfs42_seek_res res; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEEK], .rpc_argp = &args, .rpc_resp = &res, }; struct nfs_server *server = NFS_SERVER(inode); int status; if (!nfs_server_capable(inode, NFS_CAP_SEEK)) return -ENOTSUPP; status = nfs42_set_rw_stateid(&args.sa_stateid, filep, FMODE_READ); if (status) return status; nfs_wb_all(inode); status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); if (status == -ENOTSUPP) server->caps &= ~NFS_CAP_SEEK; if (status) return status; return vfs_setpos(filep, res.sr_offset, inode->i_sb->s_maxbytes); } loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence) { struct nfs_server *server = NFS_SERVER(file_inode(filep)); struct nfs4_exception exception = { }; loff_t err; do { err = _nfs42_proc_llseek(filep, offset, whence); if (err >= 0) break; if (err == -ENOTSUPP) return -EOPNOTSUPP; err = nfs4_handle_exception(server, err, &exception); } while (exception.retry); return err; } static void nfs42_layoutstat_prepare(struct rpc_task *task, void *calldata) { struct nfs42_layoutstat_data *data = calldata; struct nfs_server *server = NFS_SERVER(data->args.inode); nfs41_setup_sequence(nfs4_get_session(server), &data->args.seq_args, &data->res.seq_res, task); } static void nfs42_layoutstat_done(struct rpc_task *task, void *calldata) { struct nfs42_layoutstat_data *data = calldata; if (!nfs4_sequence_done(task, &data->res.seq_res)) return; switch (task->tk_status) { case 0: break; case -ENOTSUPP: case -EOPNOTSUPP: NFS_SERVER(data->inode)->caps &= ~NFS_CAP_LAYOUTSTATS; default: dprintk("%s server returns %d\n", __func__, task->tk_status); } } static void nfs42_layoutstat_release(void *calldata) { struct nfs42_layoutstat_data *data = calldata; struct nfs_server *nfss = NFS_SERVER(data->args.inode); if (nfss->pnfs_curr_ld->cleanup_layoutstats) nfss->pnfs_curr_ld->cleanup_layoutstats(data); pnfs_put_layout_hdr(NFS_I(data->args.inode)->layout); smp_mb__before_atomic(); clear_bit(NFS_INO_LAYOUTSTATS, &NFS_I(data->args.inode)->flags); smp_mb__after_atomic(); nfs_iput_and_deactive(data->inode); kfree(data->args.devinfo); kfree(data); } static const struct rpc_call_ops nfs42_layoutstat_ops = { .rpc_call_prepare = nfs42_layoutstat_prepare, .rpc_call_done = nfs42_layoutstat_done, .rpc_release = nfs42_layoutstat_release, }; int nfs42_proc_layoutstats_generic(struct nfs_server *server, struct nfs42_layoutstat_data *data) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTSTATS], .rpc_argp = &data->args, .rpc_resp = &data->res, }; struct rpc_task_setup task_setup = { .rpc_client = server->client, .rpc_message = &msg, .callback_ops = &nfs42_layoutstat_ops, .callback_data = data, .flags = RPC_TASK_ASYNC, }; struct rpc_task *task; data->inode = nfs_igrab_and_active(data->args.inode); if (!data->inode) { nfs42_layoutstat_release(data); return -EAGAIN; } nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0); task = rpc_run_task(&task_setup); if (IS_ERR(task)) return PTR_ERR(task); return 0; } static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f, struct file *dst_f, loff_t src_offset, loff_t dst_offset, loff_t count) { struct inode *src_inode = file_inode(src_f); struct inode *dst_inode = file_inode(dst_f); struct nfs_server *server = NFS_SERVER(dst_inode); struct nfs42_clone_args args = { .src_fh = NFS_FH(src_inode), .dst_fh = NFS_FH(dst_inode), .src_offset = src_offset, .dst_offset = dst_offset, .count = count, .dst_bitmask = server->cache_consistency_bitmask, }; struct nfs42_clone_res res = { .server = server, }; int status; msg->rpc_argp = &args; msg->rpc_resp = &res; status = nfs42_set_rw_stateid(&args.src_stateid, src_f, FMODE_READ); if (status) return status; status = nfs42_set_rw_stateid(&args.dst_stateid, dst_f, FMODE_WRITE); if (status) return status; res.dst_fattr = nfs_alloc_fattr(); if (!res.dst_fattr) return -ENOMEM; status = nfs4_call_sync(server->client, server, msg, &args.seq_args, &res.seq_res, 0); if (status == 0) status = nfs_post_op_update_inode(dst_inode, res.dst_fattr); kfree(res.dst_fattr); return status; } int nfs42_proc_clone(struct file *src_f, struct file *dst_f, loff_t src_offset, loff_t dst_offset, loff_t count) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLONE], }; struct inode *inode = file_inode(src_f); struct nfs_server *server = NFS_SERVER(file_inode(src_f)); struct nfs4_exception exception = { }; int err; if (!nfs_server_capable(inode, NFS_CAP_CLONE)) return -EOPNOTSUPP; do { err = _nfs42_proc_clone(&msg, src_f, dst_f, src_offset, dst_offset, count); if (err == -ENOTSUPP || err == -EOPNOTSUPP) { NFS_SERVER(inode)->caps &= ~NFS_CAP_CLONE; return -EOPNOTSUPP; } err = nfs4_handle_exception(server, err, &exception); } while (exception.retry); return err; }
/* * RENAME * FIXME: Some nfsds, like the Linux user space nfsd, may generate a * different file handle for the same inode after a rename (e.g. when * moving to a different directory). A fail-safe method to do so would * be to look up old_dir/old_name, create a link to new_dir/new_name and * rename the old file using the sillyrename stuff. This way, the original * file in old_dir will go away when the last process iput()s the inode. * * FIXED. * * It actually works quite well. One needs to have the possibility for * at least one ".nfs..." file in each directory the file ever gets * moved or linked to which happens automagically with the new * implementation that only depends on the dcache stuff instead of * using the inode layer * * Unfortunately, things are a little more complicated than indicated * above. For a cross-directory move, we want to make sure we can get * rid of the old inode after the operation. This means there must be * no pending writes (if it's a file), and the use count must be 1. * If these conditions are met, we can drop the dentries before doing * the rename. */ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { struct inode *old_inode = old_dentry->d_inode; struct inode *new_inode = new_dentry->d_inode; struct dentry *dentry = NULL, *rehash = NULL; int error = -EBUSY; /* * To prevent any new references to the target during the rename, * we unhash the dentry and free the inode in advance. */ lock_kernel(); if (!d_unhashed(new_dentry)) { d_drop(new_dentry); rehash = new_dentry; } dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n", old_dentry->d_parent->d_name.name, old_dentry->d_name.name, new_dentry->d_parent->d_name.name, new_dentry->d_name.name, atomic_read(&new_dentry->d_count)); /* * First check whether the target is busy ... we can't * safely do _any_ rename if the target is in use. * * For files, make a copy of the dentry and then do a * silly-rename. If the silly-rename succeeds, the * copied dentry is hashed and becomes the new target. */ if (!new_inode) goto go_ahead; if (S_ISDIR(new_inode->i_mode)) goto out; else if (atomic_read(&new_dentry->d_count) > 1) { int err; /* copy the target dentry's name */ dentry = d_alloc(new_dentry->d_parent, &new_dentry->d_name); if (!dentry) goto out; /* silly-rename the existing target ... */ err = nfs_sillyrename(new_dir, new_dentry); if (!err) { new_dentry = rehash = dentry; new_inode = NULL; /* instantiate the replacement target */ d_instantiate(new_dentry, NULL); } /* dentry still busy? */ if (atomic_read(&new_dentry->d_count) > 1) { #ifdef NFS_PARANOIA printk("nfs_rename: target %s/%s busy, d_count=%d\n", new_dentry->d_parent->d_name.name, new_dentry->d_name.name, atomic_read(&new_dentry->d_count)); #endif goto out; } } go_ahead: /* * ... prune child dentries and writebacks if needed. */ if (atomic_read(&old_dentry->d_count) > 1) { nfs_wb_all(old_inode); shrink_dcache_parent(old_dentry); } if (new_inode) d_delete(new_dentry); nfs_begin_data_update(old_dir); nfs_begin_data_update(new_dir); nfs_begin_data_update(old_inode); error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name, new_dir, &new_dentry->d_name); nfs_end_data_update(old_inode); nfs_end_data_update(new_dir); nfs_end_data_update(old_dir); out: if (rehash) d_rehash(rehash); if (!error) { if (!S_ISDIR(old_inode->i_mode)) d_move(old_dentry, new_dentry); nfs_renew_times(new_dentry); nfs_set_verifier(new_dentry, nfs_save_change_attribute(new_dir)); } /* new dentry created? */ if (dentry) dput(dentry); unlock_kernel(); return error; }
static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep, loff_t offset, loff_t len) { struct inode *inode = file_inode(filep); struct nfs_server *server = NFS_SERVER(inode); struct nfs42_falloc_args args = { .falloc_fh = NFS_FH(inode), .falloc_offset = offset, .falloc_length = len, .falloc_bitmask = server->cache_consistency_bitmask, }; struct nfs42_falloc_res res = { .falloc_server = server, }; int status; msg->rpc_argp = &args; msg->rpc_resp = &res; status = nfs42_set_rw_stateid(&args.falloc_stateid, filep, FMODE_WRITE); if (status) return status; res.falloc_fattr = nfs_alloc_fattr(); if (!res.falloc_fattr) return -ENOMEM; status = nfs4_call_sync(server->client, server, msg, &args.seq_args, &res.seq_res, 0); if (status == 0) status = nfs_post_op_update_inode(inode, res.falloc_fattr); kfree(res.falloc_fattr); return status; } static int nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep, loff_t offset, loff_t len) { struct nfs_server *server = NFS_SERVER(file_inode(filep)); struct nfs4_exception exception = { }; int err; do { err = _nfs42_proc_fallocate(msg, filep, offset, len); if (err == -ENOTSUPP) return -EOPNOTSUPP; err = nfs4_handle_exception(server, err, &exception); } while (exception.retry); return err; } int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ALLOCATE], }; struct inode *inode = file_inode(filep); int err; if (!nfs_server_capable(inode, NFS_CAP_ALLOCATE)) return -EOPNOTSUPP; mutex_lock(&inode->i_mutex); err = nfs42_proc_fallocate(&msg, filep, offset, len); if (err == -EOPNOTSUPP) NFS_SERVER(inode)->caps &= ~NFS_CAP_ALLOCATE; mutex_unlock(&inode->i_mutex); return err; } int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DEALLOCATE], }; struct inode *inode = file_inode(filep); int err; if (!nfs_server_capable(inode, NFS_CAP_DEALLOCATE)) return -EOPNOTSUPP; nfs_wb_all(inode); mutex_lock(&inode->i_mutex); err = nfs42_proc_fallocate(&msg, filep, offset, len); if (err == 0) truncate_pagecache_range(inode, offset, (offset + len) -1); if (err == -EOPNOTSUPP) NFS_SERVER(inode)->caps &= ~NFS_CAP_DEALLOCATE; mutex_unlock(&inode->i_mutex); return err; } loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence) { struct inode *inode = file_inode(filep); struct nfs42_seek_args args = { .sa_fh = NFS_FH(inode), .sa_offset = offset, .sa_what = (whence == SEEK_HOLE) ? NFS4_CONTENT_HOLE : NFS4_CONTENT_DATA, }; struct nfs42_seek_res res; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEEK], .rpc_argp = &args, .rpc_resp = &res, }; struct nfs_server *server = NFS_SERVER(inode); int status; if (!nfs_server_capable(inode, NFS_CAP_SEEK)) return -ENOTSUPP; status = nfs42_set_rw_stateid(&args.sa_stateid, filep, FMODE_READ); if (status) return status; nfs_wb_all(inode); status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); if (status == -ENOTSUPP) server->caps &= ~NFS_CAP_SEEK; if (status) return status; return vfs_setpos(filep, res.sr_offset, inode->i_sb->s_maxbytes); }
static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep, struct nfs_lock_context *lock, loff_t offset, loff_t len) { struct inode *inode = file_inode(filep); struct nfs_server *server = NFS_SERVER(inode); struct nfs42_falloc_args args = { .falloc_fh = NFS_FH(inode), .falloc_offset = offset, .falloc_length = len, .falloc_bitmask = server->cache_consistency_bitmask, }; struct nfs42_falloc_res res = { .falloc_server = server, }; int status; msg->rpc_argp = &args; msg->rpc_resp = &res; status = nfs4_set_rw_stateid(&args.falloc_stateid, lock->open_context, lock, FMODE_WRITE); if (status) return status; res.falloc_fattr = nfs_alloc_fattr(); if (!res.falloc_fattr) return -ENOMEM; status = nfs4_call_sync(server->client, server, msg, &args.seq_args, &res.seq_res, 0); if (status == 0) status = nfs_post_op_update_inode(inode, res.falloc_fattr); kfree(res.falloc_fattr); return status; } static int nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep, loff_t offset, loff_t len) { struct nfs_server *server = NFS_SERVER(file_inode(filep)); struct nfs4_exception exception = { }; struct nfs_lock_context *lock; int err; lock = nfs_get_lock_context(nfs_file_open_context(filep)); if (IS_ERR(lock)) return PTR_ERR(lock); exception.inode = file_inode(filep); exception.state = lock->open_context->state; do { err = _nfs42_proc_fallocate(msg, filep, lock, offset, len); if (err == -ENOTSUPP) { err = -EOPNOTSUPP; break; } err = nfs4_handle_exception(server, err, &exception); } while (exception.retry); nfs_put_lock_context(lock); return err; } int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ALLOCATE], }; struct inode *inode = file_inode(filep); int err; if (!nfs_server_capable(inode, NFS_CAP_ALLOCATE)) return -EOPNOTSUPP; inode_lock(inode); err = nfs42_proc_fallocate(&msg, filep, offset, len); if (err == -EOPNOTSUPP) NFS_SERVER(inode)->caps &= ~NFS_CAP_ALLOCATE; inode_unlock(inode); return err; } int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DEALLOCATE], }; struct inode *inode = file_inode(filep); int err; if (!nfs_server_capable(inode, NFS_CAP_DEALLOCATE)) return -EOPNOTSUPP; nfs_wb_all(inode); inode_lock(inode); err = nfs42_proc_fallocate(&msg, filep, offset, len); if (err == 0) truncate_pagecache_range(inode, offset, (offset + len) -1); if (err == -EOPNOTSUPP) NFS_SERVER(inode)->caps &= ~NFS_CAP_DEALLOCATE; inode_unlock(inode); return err; } static ssize_t _nfs42_proc_copy(struct file *src, loff_t pos_src, struct nfs_lock_context *src_lock, struct file *dst, loff_t pos_dst, struct nfs_lock_context *dst_lock, size_t count) { struct nfs42_copy_args args = { .src_fh = NFS_FH(file_inode(src)), .src_pos = pos_src, .dst_fh = NFS_FH(file_inode(dst)), .dst_pos = pos_dst, .count = count, }; struct nfs42_copy_res res; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY], .rpc_argp = &args, .rpc_resp = &res, }; struct inode *dst_inode = file_inode(dst); struct nfs_server *server = NFS_SERVER(dst_inode); int status; status = nfs4_set_rw_stateid(&args.src_stateid, src_lock->open_context, src_lock, FMODE_READ); if (status) return status; status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context, dst_lock, FMODE_WRITE); if (status) return status; status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); if (status == -ENOTSUPP) server->caps &= ~NFS_CAP_COPY; if (status) return status; if (res.write_res.verifier.committed != NFS_FILE_SYNC) { status = nfs_commit_file(dst, &res.write_res.verifier.verifier); if (status) return status; } truncate_pagecache_range(dst_inode, pos_dst, pos_dst + res.write_res.count); return res.write_res.count; } ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src, struct file *dst, loff_t pos_dst, size_t count) { struct nfs_server *server = NFS_SERVER(file_inode(dst)); struct nfs_lock_context *src_lock; struct nfs_lock_context *dst_lock; struct nfs4_exception src_exception = { }; struct nfs4_exception dst_exception = { }; ssize_t err, err2; if (!nfs_server_capable(file_inode(dst), NFS_CAP_COPY)) return -EOPNOTSUPP; src_lock = nfs_get_lock_context(nfs_file_open_context(src)); if (IS_ERR(src_lock)) return PTR_ERR(src_lock); src_exception.inode = file_inode(src); src_exception.state = src_lock->open_context->state; dst_lock = nfs_get_lock_context(nfs_file_open_context(dst)); if (IS_ERR(dst_lock)) { err = PTR_ERR(dst_lock); goto out_put_src_lock; } dst_exception.inode = file_inode(dst); dst_exception.state = dst_lock->open_context->state; do { inode_lock(file_inode(dst)); err = _nfs42_proc_copy(src, pos_src, src_lock, dst, pos_dst, dst_lock, count); inode_unlock(file_inode(dst)); if (err == -ENOTSUPP) { err = -EOPNOTSUPP; break; } err2 = nfs4_handle_exception(server, err, &src_exception); err = nfs4_handle_exception(server, err, &dst_exception); if (!err) err = err2; } while (src_exception.retry || dst_exception.retry); nfs_put_lock_context(dst_lock); out_put_src_lock: nfs_put_lock_context(src_lock); return err; } static loff_t _nfs42_proc_llseek(struct file *filep, struct nfs_lock_context *lock, loff_t offset, int whence) { struct inode *inode = file_inode(filep); struct nfs42_seek_args args = { .sa_fh = NFS_FH(inode), .sa_offset = offset, .sa_what = (whence == SEEK_HOLE) ? NFS4_CONTENT_HOLE : NFS4_CONTENT_DATA, }; struct nfs42_seek_res res; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEEK], .rpc_argp = &args, .rpc_resp = &res, }; struct nfs_server *server = NFS_SERVER(inode); int status; if (!nfs_server_capable(inode, NFS_CAP_SEEK)) return -ENOTSUPP; status = nfs4_set_rw_stateid(&args.sa_stateid, lock->open_context, lock, FMODE_READ); if (status) return status; nfs_wb_all(inode); status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); if (status == -ENOTSUPP) server->caps &= ~NFS_CAP_SEEK; if (status) return status; return vfs_setpos(filep, res.sr_offset, inode->i_sb->s_maxbytes); } loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence) { struct nfs_server *server = NFS_SERVER(file_inode(filep)); struct nfs4_exception exception = { }; struct nfs_lock_context *lock; loff_t err; lock = nfs_get_lock_context(nfs_file_open_context(filep)); if (IS_ERR(lock)) return PTR_ERR(lock); exception.inode = file_inode(filep); exception.state = lock->open_context->state; do { err = _nfs42_proc_llseek(filep, lock, offset, whence); if (err >= 0) break; if (err == -ENOTSUPP) { err = -EOPNOTSUPP; break; } err = nfs4_handle_exception(server, err, &exception); } while (exception.retry); nfs_put_lock_context(lock); return err; } static void nfs42_layoutstat_prepare(struct rpc_task *task, void *calldata) { struct nfs42_layoutstat_data *data = calldata; struct nfs_server *server = NFS_SERVER(data->args.inode); nfs41_setup_sequence(nfs4_get_session(server), &data->args.seq_args, &data->res.seq_res, task); } static void nfs42_layoutstat_done(struct rpc_task *task, void *calldata) { struct nfs42_layoutstat_data *data = calldata; struct inode *inode = data->inode; struct pnfs_layout_hdr *lo; if (!nfs4_sequence_done(task, &data->res.seq_res)) return; switch (task->tk_status) { case 0: break; case -NFS4ERR_EXPIRED: case -NFS4ERR_STALE_STATEID: case -NFS4ERR_OLD_STATEID: case -NFS4ERR_BAD_STATEID: spin_lock(&inode->i_lock); lo = NFS_I(inode)->layout; if (lo && nfs4_stateid_match(&data->args.stateid, &lo->plh_stateid)) { LIST_HEAD(head); /* * Mark the bad layout state as invalid, then retry * with the current stateid. */ set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags); pnfs_mark_matching_lsegs_invalid(lo, &head, NULL, 0); spin_unlock(&inode->i_lock); pnfs_free_lseg_list(&head); } else spin_unlock(&inode->i_lock); break; case -ENOTSUPP: case -EOPNOTSUPP: NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTSTATS; default: break; } dprintk("%s server returns %d\n", __func__, task->tk_status); } static void nfs42_layoutstat_release(void *calldata) { struct nfs42_layoutstat_data *data = calldata; struct nfs_server *nfss = NFS_SERVER(data->args.inode); if (nfss->pnfs_curr_ld->cleanup_layoutstats) nfss->pnfs_curr_ld->cleanup_layoutstats(data); pnfs_put_layout_hdr(NFS_I(data->args.inode)->layout); smp_mb__before_atomic(); clear_bit(NFS_INO_LAYOUTSTATS, &NFS_I(data->args.inode)->flags); smp_mb__after_atomic(); nfs_iput_and_deactive(data->inode); kfree(data->args.devinfo); kfree(data); } static const struct rpc_call_ops nfs42_layoutstat_ops = { .rpc_call_prepare = nfs42_layoutstat_prepare, .rpc_call_done = nfs42_layoutstat_done, .rpc_release = nfs42_layoutstat_release, }; int nfs42_proc_layoutstats_generic(struct nfs_server *server, struct nfs42_layoutstat_data *data) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTSTATS], .rpc_argp = &data->args, .rpc_resp = &data->res, }; struct rpc_task_setup task_setup = { .rpc_client = server->client, .rpc_message = &msg, .callback_ops = &nfs42_layoutstat_ops, .callback_data = data, .flags = RPC_TASK_ASYNC, }; struct rpc_task *task; data->inode = nfs_igrab_and_active(data->args.inode); if (!data->inode) { nfs42_layoutstat_release(data); return -EAGAIN; } nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0); task = rpc_run_task(&task_setup); if (IS_ERR(task)) return PTR_ERR(task); return 0; } static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f, struct file *dst_f, struct nfs_lock_context *src_lock, struct nfs_lock_context *dst_lock, loff_t src_offset, loff_t dst_offset, loff_t count) { struct inode *src_inode = file_inode(src_f); struct inode *dst_inode = file_inode(dst_f); struct nfs_server *server = NFS_SERVER(dst_inode); struct nfs42_clone_args args = { .src_fh = NFS_FH(src_inode), .dst_fh = NFS_FH(dst_inode), .src_offset = src_offset, .dst_offset = dst_offset, .count = count, .dst_bitmask = server->cache_consistency_bitmask, }; struct nfs42_clone_res res = { .server = server, }; int status; msg->rpc_argp = &args; msg->rpc_resp = &res; status = nfs4_set_rw_stateid(&args.src_stateid, src_lock->open_context, src_lock, FMODE_READ); if (status) return status; status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context, dst_lock, FMODE_WRITE); if (status) return status; res.dst_fattr = nfs_alloc_fattr(); if (!res.dst_fattr) return -ENOMEM; status = nfs4_call_sync(server->client, server, msg, &args.seq_args, &res.seq_res, 0); if (status == 0) status = nfs_post_op_update_inode(dst_inode, res.dst_fattr); kfree(res.dst_fattr); return status; } int nfs42_proc_clone(struct file *src_f, struct file *dst_f, loff_t src_offset, loff_t dst_offset, loff_t count) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLONE], }; struct inode *inode = file_inode(src_f); struct nfs_server *server = NFS_SERVER(file_inode(src_f)); struct nfs_lock_context *src_lock; struct nfs_lock_context *dst_lock; struct nfs4_exception src_exception = { }; struct nfs4_exception dst_exception = { }; int err, err2; if (!nfs_server_capable(inode, NFS_CAP_CLONE)) return -EOPNOTSUPP; src_lock = nfs_get_lock_context(nfs_file_open_context(src_f)); if (IS_ERR(src_lock)) return PTR_ERR(src_lock); src_exception.inode = file_inode(src_f); src_exception.state = src_lock->open_context->state; dst_lock = nfs_get_lock_context(nfs_file_open_context(dst_f)); if (IS_ERR(dst_lock)) { err = PTR_ERR(dst_lock); goto out_put_src_lock; } dst_exception.inode = file_inode(dst_f); dst_exception.state = dst_lock->open_context->state; do { err = _nfs42_proc_clone(&msg, src_f, dst_f, src_lock, dst_lock, src_offset, dst_offset, count); if (err == -ENOTSUPP || err == -EOPNOTSUPP) { NFS_SERVER(inode)->caps &= ~NFS_CAP_CLONE; err = -EOPNOTSUPP; break; } err2 = nfs4_handle_exception(server, err, &src_exception); err = nfs4_handle_exception(server, err, &dst_exception); if (!err) err = err2; } while (src_exception.retry || dst_exception.retry); nfs_put_lock_context(dst_lock); out_put_src_lock: nfs_put_lock_context(src_lock); return err; }