* Boston, MA 021110-1307, USA.
*/
-#define _GNU_SOURCE
#include "kerncompat.h"
#include <libgen.h>
#include <mntent.h>
#include <assert.h>
-
+#include <getopt.h>
#include <uuid/uuid.h>
+#include <limits.h>
#include "ctree.h"
#include "ioctl.h"
parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
0, NULL, subvol_search_by_root_id);
- assert(parent2);
+ if (!parent2) {
+ ret = -ENOENT;
+ goto out;
+ }
tmp = parent2->ctransid - parent->ctransid;
if (tmp < 0)
tmp *= -1;
return ret;
}
-static void add_clone_source(struct btrfs_send *s, u64 root_id)
+static int add_clone_source(struct btrfs_send *s, u64 root_id)
{
+ void *tmp;
+
+ tmp = s->clone_sources;
s->clone_sources = realloc(s->clone_sources,
sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
+
+ if (!s->clone_sources) {
+ free(tmp);
+ return -ENOMEM;
+ }
s->clone_sources[s->clone_sources_count++] = root_id;
+
+ return 0;
}
static int write_buf(int fd, const void *buf, int size)
ret = write(fd, (char*)buf + pos, size - pos);
if (ret < 0) {
ret = -errno;
- fprintf(stderr, "ERROR: failed to dump stream. %s",
+ fprintf(stderr, "ERROR: failed to dump stream. %s\n",
strerror(-ret));
goto out;
}
if (!ret) {
ret = -EIO;
- fprintf(stderr, "ERROR: failed to dump stream. %s",
+ fprintf(stderr, "ERROR: failed to dump stream. %s\n",
strerror(-ret));
goto out;
}
}
static int do_send(struct btrfs_send *send, u64 parent_root_id,
- int is_first_subvol, int is_last_subvol, char *subvol)
+ int is_first_subvol, int is_last_subvol, char *subvol,
+ u64 flags)
{
int ret;
pthread_t t_read;
- pthread_attr_t t_attr;
struct btrfs_ioctl_send_args io_send;
void *t_err = NULL;
int subvol_fd = -1;
goto out;
}
- ret = pthread_attr_init(&t_attr);
-
ret = pipe(pipefd);
if (ret < 0) {
ret = -errno;
send->send_fd = pipefd[0];
if (!ret)
- ret = pthread_create(&t_read, &t_attr, dump_thread,
+ ret = pthread_create(&t_read, NULL, dump_thread,
send);
if (ret) {
ret = -ret;
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;
goto out;
}
- pthread_attr_destroy(&t_attr);
-
ret = 0;
out:
ret = find_mount_root(subvol, &s->root_path);
if (ret < 0) {
+ fprintf(stderr,
+ "ERROR: failed to determine mount point for %s: %s\n",
+ subvol, strerror(-ret));
+ ret = -EINVAL;
+ goto out;
+ }
+ if (ret > 0) {
+ fprintf(stderr,
+ "ERROR: %s doesn't belong to btrfs mount point\n",
+ subvol);
ret = -EINVAL;
- fprintf(stderr, "ERROR: failed to determine mount point "
- "for %s\n", subvol);
goto out;
}
int cmd_send(int argc, char **argv)
{
char *subvol = NULL;
- int c;
int ret;
- char *outname = NULL;
+ char outname[PATH_MAX];
struct btrfs_send send;
u32 i;
char *mount_root = NULL;
char *snapshot_parent = NULL;
- u64 root_id;
+ u64 root_id = 0;
u64 parent_root_id = 0;
int full_send = 1;
int new_end_cmd_semantic = 0;
+ u64 send_flags = 0;
memset(&send, 0, sizeof(send));
send.dump_fd = fileno(stdout);
+ outname[0] = 0;
+
+ while (1) {
+ enum { GETOPT_VAL_SEND_NO_DATA = 256 };
+ static const struct option long_options[] = {
+ { "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA }
+ };
+ int c = getopt_long(argc, argv, "vec:f:i:p:", long_options, NULL);
+
+ if (c < 0)
+ break;
- while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
switch (c) {
case 'v':
g_verbose++;
goto out;
}
- add_clone_source(&send, root_id);
+ ret = add_clone_source(&send, root_id);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: not enough memory\n");
+ goto out;
+ }
subvol_uuid_search_finit(&send.sus);
free(subvol);
subvol = NULL;
full_send = 0;
break;
case 'f':
- outname = optarg;
+ if (arg_copy_path(outname, optarg, sizeof(outname))) {
+ fprintf(stderr,
+ "ERROR: output file path too long (%zu)\n",
+ strlen(optarg));
+ ret = 1;
+ goto out;
+ }
break;
case 'p':
if (snapshot_parent) {
"ERROR: -i was removed, use -c instead\n");
ret = 1;
goto out;
+ case GETOPT_VAL_SEND_NO_DATA:
+ send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
+ break;
case '?':
default:
fprintf(stderr, "ERROR: send args invalid.\n");
}
}
- if (optind == argc)
+ if (check_argc_min(argc - optind, 1))
usage(cmd_send_usage);
- if (outname != NULL) {
+ if (outname[0]) {
send.dump_fd = creat(outname, 0600);
if (send.dump_fd == -1) {
ret = -errno;
goto out;
}
- add_clone_source(&send, parent_root_id);
+ ret = add_clone_source(&send, parent_root_id);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: not enough memory\n");
+ goto out;
+ }
}
for (i = optind; i < argc; i++) {
strerror(-ret));
goto out;
}
+ if (ret > 0) {
+ fprintf(stderr,
+ "ERROR: %s doesn't belong to btrfs mount point\n",
+ subvol);
+ ret = -EINVAL;
+ goto out;
+ }
if (strcmp(send.root_path, mount_root) != 0) {
ret = -EINVAL;
fprintf(stderr, "ERROR: all subvols must be from the "
}
}
+ if (send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA)
+ printf("Mode NO_FILE_DATA enabled\n");
+
for (i = optind; i < argc; i++) {
int is_first_subvol;
int is_last_subvol;
is_last_subvol = 1;
}
ret = do_send(&send, parent_root_id, is_first_subvol,
- is_last_subvol, subvol);
+ is_last_subvol, subvol, send_flags);
if (ret < 0)
goto out;
/* done with this subvol, so add it to the clone sources */
- add_clone_source(&send, root_id);
+ ret = add_clone_source(&send, root_id);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: not enough memory\n");
+ goto out;
+ }
parent_root_id = 0;
full_send = 0;
"-f <outfile> Output is normally written to stdout. To write to",
" a file, use this option. An alternative would be to",
" use pipes.",
+ "--no-data send in NO_FILE_DATA mode, Note: the output stream",
+ " 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.",
NULL
};