diff --git a/Documentation/btrfs-receive.txt b/Documentation/btrfs-receive.txt index 82611603..78dc5116 100644 --- a/Documentation/btrfs-receive.txt +++ b/Documentation/btrfs-receive.txt @@ -38,6 +38,9 @@ Use this option to specify a file to use instead. Terminate after receiving an in the data stream. Without this option, the receiver terminates only if an error is recognized or on EOF. +--max-errors :: +Terminate as soon as N errors happened while processing commands from the send +stream. Default value is 1. A value of 0 means no limit. EXIT STATUS ----------- diff --git a/cmds-receive.c b/cmds-receive.c index 8d85ca92..87dc1b4e 100644 --- a/cmds-receive.c +++ b/cmds-receive.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -816,7 +817,8 @@ static struct btrfs_send_ops send_ops = { .utimes = process_utimes, }; -static int do_receive(struct btrfs_receive *r, const char *tomnt, int r_fd) +static int do_receive(struct btrfs_receive *r, const char *tomnt, int r_fd, + u64 max_errors) { int ret; char *dest_dir_full_path; @@ -868,7 +870,8 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt, int r_fd) while (!end) { ret = btrfs_read_and_process_send_stream(r_fd, &send_ops, r, - r->honor_end_cmd); + r->honor_end_cmd, + max_errors); if (ret < 0) goto out; if (ret) @@ -911,6 +914,11 @@ out: return ret; } +static const struct option long_opts[] = { + { "max-errors", 1, NULL, 'E' }, + { NULL, 0, NULL, 0 } +}; + int cmd_receive(int argc, char **argv) { int c; @@ -918,7 +926,7 @@ int cmd_receive(int argc, char **argv) char *fromfile = NULL; struct btrfs_receive r; int receive_fd = fileno(stdin); - + u64 max_errors = 1; int ret; memset(&r, 0, sizeof(r)); @@ -926,7 +934,7 @@ int cmd_receive(int argc, char **argv) r.write_fd = -1; r.dest_dir_fd = -1; - while ((c = getopt(argc, argv, "evf:")) != -1) { + while ((c = getopt_long(argc, argv, "evf:", long_opts, NULL)) != -1) { switch (c) { case 'v': g_verbose++; @@ -937,6 +945,9 @@ int cmd_receive(int argc, char **argv) case 'e': r.honor_end_cmd = 1; break; + case 'E': + max_errors = arg_strtou64(optarg); + break; case '?': default: fprintf(stderr, "ERROR: receive args invalid.\n"); @@ -957,7 +968,7 @@ int cmd_receive(int argc, char **argv) } } - ret = do_receive(&r, tomnt, receive_fd); + ret = do_receive(&r, tomnt, receive_fd, max_errors); return !!ret; } @@ -983,5 +994,8 @@ const char * const cmd_receive_usage[] = { " in the data stream. Without this option,", " the receiver terminates only if an error", " is recognized or on EOF.", + "--max-errors Terminate as soon as N errors happened while", + " processing commands from the send stream.", + " Default value is 1. A value of 0 means no limit.", NULL }; diff --git a/send-stream.c b/send-stream.c index 88e18e2c..ef4d8899 100644 --- a/send-stream.c +++ b/send-stream.c @@ -435,13 +435,21 @@ out: return ret; } +/* + * If max_errors is 0, then don't stop processing the stream if one of the + * callbacks in btrfs_send_ops structure returns an error. If greater than + * zero, stop after max_errors errors happened. + */ int btrfs_read_and_process_send_stream(int fd, struct btrfs_send_ops *ops, void *user, - int honor_end_cmd) + int honor_end_cmd, + u64 max_errors) { int ret; struct btrfs_send_stream s; struct btrfs_stream_header hdr; + u64 errors = 0; + int last_err = 0; s.fd = fd; s.ops = ops; @@ -471,9 +479,12 @@ int btrfs_read_and_process_send_stream(int fd, while (1) { ret = read_and_process_cmd(&s); - if (ret < 0) - goto out; - if (ret) { + if (ret < 0) { + last_err = ret; + errors++; + if (max_errors > 0 && errors >= max_errors) + goto out; + } else if (ret > 0) { if (!honor_end_cmd) ret = 0; goto out; @@ -481,5 +492,8 @@ int btrfs_read_and_process_send_stream(int fd, } out: + if (last_err && !ret) + ret = last_err; + return ret; } diff --git a/send-stream.h b/send-stream.h index 17bc6692..293bf6af 100644 --- a/send-stream.h +++ b/send-stream.h @@ -58,7 +58,8 @@ struct btrfs_send_ops { int btrfs_read_and_process_send_stream(int fd, struct btrfs_send_ops *ops, void *user, - int honor_end_cmd); + int honor_end_cmd, + u64 max_errors); #ifdef __cplusplus }