Received: by 2002:ac0:a5a7:0:0:0:0:0 with SMTP id m36-v6csp5055926imm; Tue, 21 Aug 2018 05:40:32 -0700 (PDT) X-Google-Smtp-Source: AA+uWPyHUwXXxmSR5cS29+RJqY7pnY75L0FMS6HuJFrdynQB+cZG/pq8CX92DyVdkA4iCyK7Yndy X-Received: by 2002:a62:2119:: with SMTP id h25-v6mr52528656pfh.112.1534855232018; Tue, 21 Aug 2018 05:40:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1534855231; cv=none; d=google.com; s=arc-20160816; b=mpAI4S8wKIOJGrvA1FRrVYpGCVwi6WScXuU+4GILRkW0DCyiV0+M10oER/WekaouyI bu5Ph82XplN1Jz6aX7PLiTTTdalmQ3wPCJJQ1oJQ0631+nqIfJoxWslscVLIuSRMigwR N0p/UxV/Ht/iaEDhPAORMQMACaKTL3Bfao4OonNO79dHoTv8vQ7WncjbZ7D5v04GCLV/ 5Cws37WxD4cGB26hpiLXLfWikgpM0IwDWad7cDiOO1F9E6tuUUT8Qd+EmbQvR/B3tFAX ffeTdTkeYDcm30QMpkcWtQNULb671sCXJkyBiQTDWfcASg44GIlvk9nCWcl84d3pIc/T /KXw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=+nWV4RV1oIhkNs1+rlLXBOBysJvpJXee0Cf1ASm9568=; b=D64oAAz8N0+rb6TRGBB+rOEjuKq1t8BsoUYzttI6/vnpXMZE+xL6si+E7OUN4zHsXG 0Z0DwWywqaYuHb9+FIM0MxGgkAmlUkTnDYqPnkhBOQ0M6MtHDTBClHL/wvYHVY25d+ZS QsIlwfmY6Uz8DZRsBqsGP41IsXReac4Hf4oITjLZubt7z/cPQoBu0dysAA1MPr+4MuZn 9Cd0RQucvB4hnfsidzmpgo6/Ns+veI9ifpZYpF020+qzyggZmEA4COKhuGKZQNRD0T2U 3hAsQ78kMnMvT24OcT17OgQav1EQaku2XS94AQ5/IMdg7pRxo0/8IfiffUCzF6dgH1aQ gCQA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=redhat.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id f5-v6si13119014plf.411.2018.08.21.05.40.16; Tue, 21 Aug 2018 05:40:31 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727299AbeHUP4h (ORCPT + 99 others); Tue, 21 Aug 2018 11:56:37 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:37234 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727019AbeHUP4h (ORCPT ); Tue, 21 Aug 2018 11:56:37 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 344DD401EF0D; Tue, 21 Aug 2018 12:36:38 +0000 (UTC) Received: from localhost (ovpn-116-132.ams2.redhat.com [10.36.116.132]) by smtp.corp.redhat.com (Postfix) with ESMTPS id CDF0F2026D64; Tue, 21 Aug 2018 12:36:37 +0000 (UTC) From: Niels de Vos To: Miklos Szeredi Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Marcin Sulikowski , Niels de Vos Subject: [PATCH v4] fuse: add support for copy_file_range() Date: Tue, 21 Aug 2018 14:36:31 +0200 Message-Id: <20180821123631.5673-1-ndevos@redhat.com> In-Reply-To: <20180821101233.GD2650@ndevos-x270> References: <20180821101233.GD2650@ndevos-x270> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.4 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.5]); Tue, 21 Aug 2018 12:36:38 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.5]); Tue, 21 Aug 2018 12:36:38 +0000 (UTC) for IP:'10.11.54.4' DOMAIN:'int-mx04.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'ndevos@redhat.com' RCPT:'' Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org There are several FUSE filesystems that can implement server-side copy or other efficient copy/duplication/clone methods. The copy_file_range() syscall is the standard interface that users have access to while not depending on external libraries that bypass FUSE. Signed-off-by: Niels de Vos --- libfuse: https://github.com/libfuse/libfuse/pull/259 v2: return ssize_t instead of long v3: add nodeid_out to fuse_copy_file_range_in for libfuse expectations v4: use fuse_write_out as response, correct write-back handling --- fs/fuse/file.c | 80 +++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 3 ++ include/uapi/linux/fuse.h | 104 +++++++++++++++++++++----------------- 3 files changed, 142 insertions(+), 45 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index a201fb0ac64f..6aea3675fd88 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -3009,6 +3009,85 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, return err; } +static ssize_t fuse_copy_file_range(struct file *file_in, loff_t pos_in, + struct file *file_out, loff_t pos_out, + size_t len, unsigned int flags) +{ + struct fuse_file *ff_in = file_in->private_data; + struct fuse_file *ff_out = file_out->private_data; + struct inode *inode_out = file_inode(file_out); + struct fuse_inode *fi_out = get_fuse_inode(inode_out); + struct fuse_conn *fc = ff_in->fc; + FUSE_ARGS(args); + struct fuse_copy_file_range_in inarg = { + .fh_in = ff_in->fh, + .off_in = pos_in, + .nodeid_out = ff_out->nodeid, + .fh_out = ff_out->fh, + .off_out = pos_out, + .len = len, + .flags = flags + }; + struct fuse_write_out outarg; + ssize_t err; + /* mark unstable when write-back is not used, and file_out gets + * extended */ + bool is_unstable = (!fc->writeback_cache) && + ((pos_out + len) > inode_out->i_size); + + if (fc->no_copy_file_range) + return -EOPNOTSUPP; + + inode_lock(inode_out); + + if (fc->writeback_cache) { + err = filemap_write_and_wait_range(inode_out->i_mapping, + pos_out, pos_out + len); + if (err) + goto out; + + fuse_sync_writes(inode_out); + } + + if (is_unstable) + set_bit(FUSE_I_SIZE_UNSTABLE, &fi_out->state); + + args.in.h.opcode = FUSE_COPY_FILE_RANGE; + args.in.h.nodeid = ff_in->nodeid; + args.in.numargs = 1; + args.in.args[0].size = sizeof(inarg); + args.in.args[0].value = &inarg; + args.out.numargs = 1; + args.out.args[0].size = sizeof(outarg); + args.out.args[0].value = &outarg; + err = fuse_simple_request(fc, &args); + if (err == -ENOSYS) { + fc->no_copy_file_range = 1; + err = -EOPNOTSUPP; + } + if (err) + goto out; + + if (fc->writeback_cache) { + bool changed = fuse_write_update_size(inode_out, + pos_out + outarg.size); + + if (changed) + file_update_time(file_out); + } + + fuse_invalidate_attr(inode_out); + + err = outarg.size; +out: + if (is_unstable) + clear_bit(FUSE_I_SIZE_UNSTABLE, &fi_out->state); + + inode_unlock(inode_out); + + return err; +} + static const struct file_operations fuse_file_operations = { .llseek = fuse_file_llseek, .read_iter = fuse_file_read_iter, @@ -3025,6 +3104,7 @@ static const struct file_operations fuse_file_operations = { .compat_ioctl = fuse_file_compat_ioctl, .poll = fuse_file_poll, .fallocate = fuse_file_fallocate, + .copy_file_range = fuse_copy_file_range, }; static const struct file_operations fuse_direct_io_file_operations = { diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 5256ad333b05..ea848bb7d9e2 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -637,6 +637,9 @@ struct fuse_conn { /** Allow other than the mounter user to access the filesystem ? */ unsigned allow_other:1; + /** Does the filesystem support copy_file_range? */ + unsigned no_copy_file_range:1; + /** The number of requests waiting for completion */ atomic_t num_waiting; diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 92fa24c24c92..c806a17beaef 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -116,6 +116,9 @@ * * 7.27 * - add FUSE_ABORT_ERROR + * + * 7.28 + * - add FUSE_COPY_FILE_RANGE */ #ifndef _LINUX_FUSE_H @@ -337,53 +340,54 @@ struct fuse_file_lock { #define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0) enum fuse_opcode { - FUSE_LOOKUP = 1, - FUSE_FORGET = 2, /* no reply */ - FUSE_GETATTR = 3, - FUSE_SETATTR = 4, - FUSE_READLINK = 5, - FUSE_SYMLINK = 6, - FUSE_MKNOD = 8, - FUSE_MKDIR = 9, - FUSE_UNLINK = 10, - FUSE_RMDIR = 11, - FUSE_RENAME = 12, - FUSE_LINK = 13, - FUSE_OPEN = 14, - FUSE_READ = 15, - FUSE_WRITE = 16, - FUSE_STATFS = 17, - FUSE_RELEASE = 18, - FUSE_FSYNC = 20, - FUSE_SETXATTR = 21, - FUSE_GETXATTR = 22, - FUSE_LISTXATTR = 23, - FUSE_REMOVEXATTR = 24, - FUSE_FLUSH = 25, - FUSE_INIT = 26, - FUSE_OPENDIR = 27, - FUSE_READDIR = 28, - FUSE_RELEASEDIR = 29, - FUSE_FSYNCDIR = 30, - FUSE_GETLK = 31, - FUSE_SETLK = 32, - FUSE_SETLKW = 33, - FUSE_ACCESS = 34, - FUSE_CREATE = 35, - FUSE_INTERRUPT = 36, - FUSE_BMAP = 37, - FUSE_DESTROY = 38, - FUSE_IOCTL = 39, - FUSE_POLL = 40, - FUSE_NOTIFY_REPLY = 41, - FUSE_BATCH_FORGET = 42, - FUSE_FALLOCATE = 43, - FUSE_READDIRPLUS = 44, - FUSE_RENAME2 = 45, - FUSE_LSEEK = 46, + FUSE_LOOKUP = 1, + FUSE_FORGET = 2, /* no reply */ + FUSE_GETATTR = 3, + FUSE_SETATTR = 4, + FUSE_READLINK = 5, + FUSE_SYMLINK = 6, + FUSE_MKNOD = 8, + FUSE_MKDIR = 9, + FUSE_UNLINK = 10, + FUSE_RMDIR = 11, + FUSE_RENAME = 12, + FUSE_LINK = 13, + FUSE_OPEN = 14, + FUSE_READ = 15, + FUSE_WRITE = 16, + FUSE_STATFS = 17, + FUSE_RELEASE = 18, + FUSE_FSYNC = 20, + FUSE_SETXATTR = 21, + FUSE_GETXATTR = 22, + FUSE_LISTXATTR = 23, + FUSE_REMOVEXATTR = 24, + FUSE_FLUSH = 25, + FUSE_INIT = 26, + FUSE_OPENDIR = 27, + FUSE_READDIR = 28, + FUSE_RELEASEDIR = 29, + FUSE_FSYNCDIR = 30, + FUSE_GETLK = 31, + FUSE_SETLK = 32, + FUSE_SETLKW = 33, + FUSE_ACCESS = 34, + FUSE_CREATE = 35, + FUSE_INTERRUPT = 36, + FUSE_BMAP = 37, + FUSE_DESTROY = 38, + FUSE_IOCTL = 39, + FUSE_POLL = 40, + FUSE_NOTIFY_REPLY = 41, + FUSE_BATCH_FORGET = 42, + FUSE_FALLOCATE = 43, + FUSE_READDIRPLUS = 44, + FUSE_RENAME2 = 45, + FUSE_LSEEK = 46, + FUSE_COPY_FILE_RANGE = 47, /* CUSE specific operations */ - CUSE_INIT = 4096, + CUSE_INIT = 4096, }; enum fuse_notify_code { @@ -792,4 +796,14 @@ struct fuse_lseek_out { uint64_t offset; }; +struct fuse_copy_file_range_in { + uint64_t fh_in; + uint64_t off_in; + uint64_t nodeid_out; + uint64_t fh_out; + uint64_t off_out; + uint64_t len; + uint64_t flags; +}; + #endif /* _LINUX_FUSE_H */ -- 2.17.1