From: David Sterba <dsterba@suse.com>
To: linux-btrfs@vger.kernel.org
Cc: nborisov@suse.com, osandov@osandov.com, David Sterba <dsterba@suse.com>
Subject: [PATCH RFC] btrfs-progs: send protocol v2 stub, UTIMES2, OTIME
Date: Mon, 18 Oct 2021 16:47:17 +0200 [thread overview]
Message-ID: <20211018144717.20275-1-dsterba@suse.com> (raw)
In-Reply-To: <20211018144109.18442-1-dsterba@suse.com>
This is counterpart for the protocol version update.
- version 2 protocol
- new protocol command UTIMES2, same as utimes with additional otime
data
- send: add command line options to specify version, compare against
current running kernel supported version
- receive: parse UTIMES2
- receive: parse OTIME
TODO:
- libbtrfs compatibility is missing, ie. this will break anything that
uses send stream (snapper), this needs library version update and
maybe some ifdefs in the headers
Signed-off-by: David Sterba <dsterba@suse.com>
---
cmds/receive-dump.c | 31 ++++++++++++++++-
cmds/receive.c | 79 ++++++++++++++++++++++++++++++++++++++++++++
cmds/send.c | 66 ++++++++++++++++++++++++++++++++++--
common/send-stream.c | 14 ++++++++
common/send-stream.h | 5 +++
ioctl.h | 12 +++++--
kernel-shared/send.h | 11 +++++-
7 files changed, 212 insertions(+), 6 deletions(-)
diff --git a/cmds/receive-dump.c b/cmds/receive-dump.c
index 47a0a30e47db..7250228e6c42 100644
--- a/cmds/receive-dump.c
+++ b/cmds/receive-dump.c
@@ -312,6 +312,33 @@ static int print_utimes(const char *path, struct timespec *at,
at_str, mt_str, ct_str);
}
+static int print_utimes2(const char *path, struct timespec *at,
+ struct timespec *mt, struct timespec *ct,
+ struct timespec *ot, void *user)
+{
+ char at_str[TIME_STRING_MAX];
+ char mt_str[TIME_STRING_MAX];
+ char ct_str[TIME_STRING_MAX];
+ char ot_str[TIME_STRING_MAX];
+
+ if (sprintf_timespec(at, at_str, TIME_STRING_MAX - 1) < 0 ||
+ sprintf_timespec(mt, mt_str, TIME_STRING_MAX - 1) < 0 ||
+ sprintf_timespec(ct, ct_str, TIME_STRING_MAX - 1) < 0 ||
+ sprintf_timespec(ot, ot_str, TIME_STRING_MAX - 1) < 0)
+ return -EINVAL;
+ return PRINT_DUMP(user, path, "utimes2", "atime=%s mtime=%s ctime=%s otime=%s",
+ at_str, mt_str, ct_str, ot_str);
+}
+
+static int print_otime(const char *path, struct timespec *ot, void *user)
+{
+ char ot_str[TIME_STRING_MAX];
+
+ if (sprintf_timespec(ot, ot_str, TIME_STRING_MAX - 1) < 0)
+ return -EINVAL;
+ return PRINT_DUMP(user, path, "otime", "otime=%s", ot_str);
+}
+
static int print_update_extent(const char *path, u64 offset, u64 len,
void *user)
{
@@ -340,5 +367,7 @@ struct btrfs_send_ops btrfs_print_send_ops = {
.chmod = print_chmod,
.chown = print_chown,
.utimes = print_utimes,
- .update_extent = print_update_extent
+ .update_extent = print_update_extent,
+ .utimes2 = print_utimes2,
+ .otime = print_otime,
};
diff --git a/cmds/receive.c b/cmds/receive.c
index 4d123a1f8782..dfb37a502598 100644
--- a/cmds/receive.c
+++ b/cmds/receive.c
@@ -967,6 +967,83 @@ static int process_utimes(const char *path, struct timespec *at,
return ret;
}
+static int process_utimes2(const char *path, struct timespec *at,
+ struct timespec *mt, struct timespec *ct,
+ struct timespec *ot, void *user)
+{
+ int ret = 0;
+ struct btrfs_receive *rctx = user;
+ char full_path[PATH_MAX];
+ struct timespec tv[2];
+
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
+ if (ret < 0) {
+ error("utimes2: path invalid: %s", path);
+ goto out;
+ }
+
+ if (bconf.verbose >= 3)
+ fprintf(stderr, "utimes2 %s\n", path);
+
+ tv[0] = *at;
+ tv[1] = *mt;
+ ret = utimensat(AT_FDCWD, full_path, tv, AT_SYMLINK_NOFOLLOW);
+ if (ret < 0) {
+ ret = -errno;
+ error("utimes2 %s failed: %m", path);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+/* TODO: Copied from receive-dump.c */
+static int sprintf_timespec(struct timespec *ts, char *dest, int max_size)
+{
+ struct tm tm;
+ int ret;
+
+ if (!localtime_r(&ts->tv_sec, &tm)) {
+ error("failed to convert time %lld.%.9ld to local time",
+ (long long)ts->tv_sec, ts->tv_nsec);
+ return -EINVAL;
+ }
+ ret = strftime(dest, max_size, "%FT%T%z", &tm);
+ if (ret == 0) {
+ error(
+ "time %lld.%ld is too long to convert into readable string",
+ (long long)ts->tv_sec, ts->tv_nsec);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+static int process_otime(const char *path, struct timespec *ot, void *user)
+{
+ int ret;
+ struct btrfs_receive *rctx = user;
+ char full_path[PATH_MAX];
+
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
+ if (ret < 0) {
+ error("otime: path invalid: %s", path);
+ goto out;
+ }
+
+ if (bconf.verbose >= 3) {
+ char ot_str[128];
+
+ if (sprintf_timespec(ot, ot_str, sizeof(ot_str) - 1) < 0)
+ goto out;
+ fprintf(stderr, "otime %s\n", ot_str);
+ }
+
+out:
+ return 0;
+}
+
static int process_update_extent(const char *path, u64 offset, u64 len,
void *user)
{
@@ -1004,6 +1081,8 @@ static struct btrfs_send_ops send_ops = {
.chown = process_chown,
.utimes = process_utimes,
.update_extent = process_update_extent,
+ .utimes2 = process_utimes2,
+ .otime = process_otime,
};
static int do_receive(struct btrfs_receive *rctx, const char *tomnt,
diff --git a/cmds/send.c b/cmds/send.c
index 1810233137aa..f529f0b0a6a0 100644
--- a/cmds/send.c
+++ b/cmds/send.c
@@ -57,6 +57,8 @@ struct btrfs_send {
u64 clone_sources_count;
char *root_path;
+ u32 proto;
+ u32 proto_supported;
};
static int get_root_id(struct btrfs_send *sctx, const char *path, u64 *root_id)
@@ -257,6 +259,13 @@ static int do_send(struct btrfs_send *send, u64 parent_root_id,
memset(&io_send, 0, sizeof(io_send));
io_send.send_fd = pipefd[1];
send->send_fd = pipefd[0];
+ io_send.flags = flags;
+
+ if (send->proto_supported > 1) {
+ /* Versioned stream supported, requesting default or specific number */
+ io_send.version = send->proto;
+ io_send.flags |= BTRFS_SEND_FLAG_VERSION;
+ }
if (!ret)
ret = pthread_create(&t_read, NULL, read_sent_data, send);
@@ -267,7 +276,6 @@ static int do_send(struct btrfs_send *send, u64 parent_root_id,
goto out;
}
- io_send.flags = flags;
io_send.clone_sources = (__u64*)send->clone_sources;
io_send.clone_sources_count = send->clone_sources_count;
io_send.parent_root = parent_root_id;
@@ -275,6 +283,7 @@ static int do_send(struct btrfs_send *send, u64 parent_root_id,
io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
if (!is_last_subvol)
io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
+
ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
if (ret < 0) {
ret = -errno;
@@ -419,6 +428,33 @@ static void free_send_info(struct btrfs_send *sctx)
sctx->root_path = NULL;
}
+static u32 get_sysfs_proto_supported(void)
+{
+ int fd;
+ int ret;
+ char buf[32] = {};
+ char *end = NULL;
+ u64 version;
+
+ fd = sysfs_open_file("features/send_stream_version");
+ if (fd < 0) {
+ /* No file is either no version support or old kernel with just v1 */
+ return 1;
+ }
+ ret = sysfs_read_file(fd, buf, sizeof(buf));
+ close(fd);
+ if (ret <= 0)
+ return 1;
+ version = strtoull(buf, &end, 10);
+ if (version == ULLONG_MAX && errno == ERANGE)
+ return 1;
+ if (version > U32_MAX) {
+ warning("sysfs/send_stream_version too big: %llu", version);
+ version = 1;
+ }
+ return version;
+}
+
static const char * const cmd_send_usage[] = {
"btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
"Send the subvolume(s) to stdout.",
@@ -447,6 +483,7 @@ static const char * const cmd_send_usage[] = {
" does not contain any file data and thus cannot be used",
" to transfer changes. This mode is faster and useful to",
" show the differences in metadata.",
+ "--proto N request maximum protocol version N (default: highest supported by running kernel)",
"-v|--verbose deprecated, alias for global -v option",
"-q|--quiet deprecated, alias for global -q option",
HELPINFO_INSERT_GLOBALS,
@@ -472,6 +509,7 @@ static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
memset(&send, 0, sizeof(send));
send.dump_fd = fileno(stdout);
+ send.proto = 0;
outname[0] = 0;
/*
@@ -487,11 +525,12 @@ static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
optind = 0;
while (1) {
- enum { GETOPT_VAL_SEND_NO_DATA = 256 };
+ enum { GETOPT_VAL_SEND_NO_DATA = 256, GETOPT_VAL_PROTO };
static const struct option long_options[] = {
{ "verbose", no_argument, NULL, 'v' },
{ "quiet", no_argument, NULL, 'q' },
{ "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA },
+ { "proto" , required_argument, NULL, GETOPT_VAL_PROTO },
{ NULL, 0, NULL, 0 }
};
int c = getopt_long(argc, argv, "vqec:f:i:p:", long_options, NULL);
@@ -580,6 +619,17 @@ static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
case GETOPT_VAL_SEND_NO_DATA:
send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
break;
+ case GETOPT_VAL_PROTO: {
+ u64 tmp;
+
+ tmp = arg_strtou64(optarg);
+ if (tmp > U32_MAX) {
+ error("protocol version number too big %llu", tmp);
+ ret = 1;
+ goto out;
+ }
+ send.proto = tmp;
+ break; }
default:
usage_unknown_option(cmd, argv);
}
@@ -687,6 +737,18 @@ static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
if ((send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA) && bconf.verbose > 1)
if (bconf.verbose > 1)
fprintf(stderr, "Mode NO_FILE_DATA enabled\n");
+ send.proto_supported = get_sysfs_proto_supported();
+ if (send.proto_supported == 1) {
+ if (send.proto > send.proto_supported) {
+ error("requested version %u but kernel supports only %u",
+ send.proto, send.proto_supported);
+ ret = -EPROTO;
+ goto out;
+ }
+ }
+ if (bconf.verbose > 1)
+ fprintf(stderr, "Protocol version requested: %u (supported %u)\n",
+ send.proto, send.proto_supported);
for (i = optind; i < argc; i++) {
int is_first_subvol;
diff --git a/common/send-stream.c b/common/send-stream.c
index d07748ced1fe..0b0558cec671 100644
--- a/common/send-stream.c
+++ b/common/send-stream.c
@@ -314,6 +314,7 @@ static int read_and_process_cmd(struct btrfs_send_stream *sctx)
struct timespec at;
struct timespec ct;
struct timespec mt;
+ struct timespec ot;
u8 uuid[BTRFS_UUID_SIZE];
u8 clone_uuid[BTRFS_UUID_SIZE];
u64 tmp;
@@ -451,6 +452,19 @@ static int read_and_process_cmd(struct btrfs_send_stream *sctx)
TLV_GET_U64(sctx, BTRFS_SEND_A_SIZE, &tmp);
ret = sctx->ops->update_extent(path, offset, tmp, sctx->user);
break;
+ case BTRFS_SEND_C_UTIMES2:
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_TIMESPEC(sctx, BTRFS_SEND_A_ATIME, &at);
+ TLV_GET_TIMESPEC(sctx, BTRFS_SEND_A_MTIME, &mt);
+ TLV_GET_TIMESPEC(sctx, BTRFS_SEND_A_CTIME, &ct);
+ TLV_GET_TIMESPEC(sctx, BTRFS_SEND_A_OTIME, &ot);
+ ret = sctx->ops->utimes2(path, &at, &mt, &ct, &ot, sctx->user);
+ break;
+ case BTRFS_SEND_C_OTIME:
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_TIMESPEC(sctx, BTRFS_SEND_A_OTIME, &ot);
+ ret = sctx->ops->otime(path, &ot, sctx->user);
+ break;
case BTRFS_SEND_C_END:
ret = 1;
break;
diff --git a/common/send-stream.h b/common/send-stream.h
index 2de51eac5548..be6c71031db0 100644
--- a/common/send-stream.h
+++ b/common/send-stream.h
@@ -53,6 +53,11 @@ struct btrfs_send_ops {
struct timespec *mt, struct timespec *ct,
void *user);
int (*update_extent)(const char *path, u64 offset, u64 len, void *user);
+ /* V2 */
+ int (*utimes2)(const char *path, struct timespec *at,
+ struct timespec *mt, struct timespec *ct,
+ struct timespec *ot, void *user);
+ int (*otime)(const char *path, struct timespec *ot, void *user);
};
int btrfs_read_and_process_send_stream(int fd,
diff --git a/ioctl.h b/ioctl.h
index 368a87b27711..2901d92b64e8 100644
--- a/ioctl.h
+++ b/ioctl.h
@@ -655,10 +655,16 @@ BUILD_ASSERT(sizeof(struct btrfs_ioctl_received_subvol_args_32) == 192);
*/
#define BTRFS_SEND_FLAG_OMIT_END_CMD 0x4
+/*
+ * Read the protocol version in the structure
+ */
+#define BTRFS_SEND_FLAG_VERSION 0x8
+
#define BTRFS_SEND_FLAG_MASK \
(BTRFS_SEND_FLAG_NO_FILE_DATA | \
BTRFS_SEND_FLAG_OMIT_STREAM_HEADER | \
- BTRFS_SEND_FLAG_OMIT_END_CMD)
+ BTRFS_SEND_FLAG_OMIT_END_CMD | \
+ BTRFS_SEND_FLAG_VERSION)
struct btrfs_ioctl_send_args {
__s64 send_fd; /* in */
@@ -666,7 +672,9 @@ struct btrfs_ioctl_send_args {
__u64 __user *clone_sources; /* in */
__u64 parent_root; /* in */
__u64 flags; /* in */
- __u64 reserved[4]; /* in */
+ __u32 version; /* in */
+ __u32 reserved32;
+ __u64 reserved[3]; /* in */
};
/*
* Size of structure depends on pointer width, was not caught in the early
diff --git a/kernel-shared/send.h b/kernel-shared/send.h
index e73f09dfe5d7..8b927313ff76 100644
--- a/kernel-shared/send.h
+++ b/kernel-shared/send.h
@@ -31,7 +31,7 @@ extern "C" {
#endif
#define BTRFS_SEND_STREAM_MAGIC "btrfs-stream"
-#define BTRFS_SEND_STREAM_VERSION 1
+#define BTRFS_SEND_STREAM_VERSION 2
#define BTRFS_SEND_BUF_SIZE (64 * 1024)
#define BTRFS_SEND_READ_SIZE (1024 * 48)
@@ -70,6 +70,7 @@ struct btrfs_tlv_header {
enum btrfs_send_cmd {
BTRFS_SEND_C_UNSPEC,
+ /* Version 1 */
BTRFS_SEND_C_SUBVOL,
BTRFS_SEND_C_SNAPSHOT,
@@ -98,6 +99,14 @@ enum btrfs_send_cmd {
BTRFS_SEND_C_END,
BTRFS_SEND_C_UPDATE_EXTENT,
+ __BTRFS_SEND_C_MAX_V1,
+
+ /* Version 2 */
+ BTRFS_SEND_C_UTIMES2,
+ BTRFS_SEND_C_OTIME,
+ __BTRFS_SEND_C_MAX_V2,
+
+ /* End */
__BTRFS_SEND_C_MAX,
};
#define BTRFS_SEND_C_MAX (__BTRFS_SEND_C_MAX - 1)
--
2.33.0
next prev parent reply other threads:[~2021-10-18 14:47 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-10-18 14:41 [PATCH RFC] btrfs: send: v2 protocol and example OTIME changes David Sterba
2021-10-18 14:47 ` David Sterba [this message]
2021-10-18 21:42 ` [PATCH RFC] btrfs-progs: send protocol v2 stub, UTIMES2, OTIME Omar Sandoval
2021-10-19 12:53 ` David Sterba
2021-10-19 14:11 ` Josef Bacik
2021-10-19 17:27 ` Omar Sandoval
2021-10-18 15:36 ` [PATCH RFC] btrfs: send: v2 protocol and example OTIME changes Filipe Manana
2021-10-18 21:26 ` Omar Sandoval
2021-10-19 10:51 ` David Sterba
2021-10-19 7:30 ` Nikolay Borisov
2021-10-19 17:38 ` Omar Sandoval
2021-10-19 20:25 ` David Sterba
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20211018144717.20275-1-dsterba@suse.com \
--to=dsterba@suse.com \
--cc=linux-btrfs@vger.kernel.org \
--cc=nborisov@suse.com \
--cc=osandov@osandov.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.