NFS: add COPY_NOTIFY operation
[linux-block.git] / fs / nfs / nfs42proc.c
index 5196bfa7894d21c0eb1220f4d9bb9e51e2593afb..6317dd89cf43e4240e3610b27099194868d1f8ae 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright (c) 2014 Anna Schumaker <Anna.Schumaker@Netapp.com>
  */
 #include <linux/fs.h>
+#include <linux/sunrpc/addr.h>
 #include <linux/sunrpc/sched.h>
 #include <linux/nfs.h>
 #include <linux/nfs3.h>
 #include "pnfs.h"
 #include "nfs4session.h"
 #include "internal.h"
+#include "delegation.h"
 
 #define NFSDBG_FACILITY NFSDBG_PROC
 static int nfs42_do_offload_cancel_async(struct file *dst, nfs4_stateid *std);
 
+static void nfs42_set_netaddr(struct file *filep, struct nfs42_netaddr *naddr)
+{
+       struct nfs_client *clp = (NFS_SERVER(file_inode(filep)))->nfs_client;
+       unsigned short port = 2049;
+
+       rcu_read_lock();
+       naddr->netid_len = scnprintf(naddr->netid,
+                                       sizeof(naddr->netid), "%s",
+                                       rpc_peeraddr2str(clp->cl_rpcclient,
+                                       RPC_DISPLAY_NETID));
+       naddr->addr_len = scnprintf(naddr->addr,
+                                       sizeof(naddr->addr),
+                                       "%s.%u.%u",
+                                       rpc_peeraddr2str(clp->cl_rpcclient,
+                                       RPC_DISPLAY_ADDR),
+                                       port >> 8, port & 255);
+       rcu_read_unlock();
+}
+
 static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
                struct nfs_lock_context *lock, loff_t offset, loff_t len)
 {
@@ -459,6 +480,76 @@ static int nfs42_do_offload_cancel_async(struct file *dst,
        return status;
 }
 
+int _nfs42_proc_copy_notify(struct file *src, struct file *dst,
+                           struct nfs42_copy_notify_args *args,
+                           struct nfs42_copy_notify_res *res)
+{
+       struct nfs_server *src_server = NFS_SERVER(file_inode(src));
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY_NOTIFY],
+               .rpc_argp = args,
+               .rpc_resp = res,
+       };
+       int status;
+       struct nfs_open_context *ctx;
+       struct nfs_lock_context *l_ctx;
+
+       ctx = get_nfs_open_context(nfs_file_open_context(src));
+       l_ctx = nfs_get_lock_context(ctx);
+       if (IS_ERR(l_ctx))
+               return PTR_ERR(l_ctx);
+
+       status = nfs4_set_rw_stateid(&args->cna_src_stateid, ctx, l_ctx,
+                                    FMODE_READ);
+       nfs_put_lock_context(l_ctx);
+       if (status)
+               return status;
+
+       status = nfs4_call_sync(src_server->client, src_server, &msg,
+                               &args->cna_seq_args, &res->cnr_seq_res, 0);
+       if (status == -ENOTSUPP)
+               src_server->caps &= ~NFS_CAP_COPY_NOTIFY;
+
+       put_nfs_open_context(nfs_file_open_context(src));
+       return status;
+}
+
+int nfs42_proc_copy_notify(struct file *src, struct file *dst,
+                               struct nfs42_copy_notify_res *res)
+{
+       struct nfs_server *src_server = NFS_SERVER(file_inode(src));
+       struct nfs42_copy_notify_args *args;
+       struct nfs4_exception exception = {
+               .inode = file_inode(src),
+       };
+       int status;
+
+       if (!(src_server->caps & NFS_CAP_COPY_NOTIFY))
+               return -EOPNOTSUPP;
+
+       args = kzalloc(sizeof(struct nfs42_copy_notify_args), GFP_NOFS);
+       if (args == NULL)
+               return -ENOMEM;
+
+       args->cna_src_fh  = NFS_FH(file_inode(src)),
+       args->cna_dst.nl4_type = NL4_NETADDR;
+       nfs42_set_netaddr(dst, &args->cna_dst.u.nl4_addr);
+       exception.stateid = &args->cna_src_stateid;
+
+       do {
+               status = _nfs42_proc_copy_notify(src, dst, args, res);
+               if (status == -ENOTSUPP) {
+                       status = -EOPNOTSUPP;
+                       goto out;
+               }
+               status = nfs4_handle_exception(src_server, status, &exception);
+       } while (exception.retry);
+
+out:
+       kfree(args);
+       return status;
+}
+
 static loff_t _nfs42_proc_llseek(struct file *filep,
                struct nfs_lock_context *lock, loff_t offset, int whence)
 {