static int set_root_info(struct btrfs_send *sctx, const char *subvol, u64 *root_id) { int ret; ret = init_root_path(sctx, subvol); if (ret < 0) goto out; ret = get_root_id(sctx, subvol_strip_mountpoint(sctx->root_path, subvol), root_id); if (ret < 0) { error("cannot resolve rootid for %s", subvol); goto out; } out: return ret; }
/** * Bulk filtering in the DB. */ static int list_bulk(void) { attr_set_t root_attrs, attrs; entry_id_t root_id, id; int rc; struct stat st; struct lmgr_iterator_t *it; /* no tranvsersal => no wagon * so we need the path from the DB. */ query_mask |= ATTR_MASK_fullpath; ATTR_MASK_INIT(&root_attrs); rc = get_root_id(&root_id); if (rc) return rc; /* root is not a part of the DB: print it now */ ATTR_MASK_SET(&root_attrs, fullpath); strcpy(ATTR(&root_attrs, fullpath), config.global_config.fs_path); if (lstat(ATTR(&root_attrs, fullpath), &st) == 0) { PosixStat2EntryAttr(&st, &root_attrs, TRUE); ListMgr_GenerateFields(&root_attrs, disp_mask | query_mask); } /* root has no name... */ ATTR_MASK_SET(&root_attrs, name); ATTR(&root_attrs, name)[0] = '\0'; /* match condition on dirs parent */ if (!is_expr || (EntryMatches(&root_id, &root_attrs, &match_expr, NULL) == POLICY_MATCH)) { /* don't display dirs if no_dir is specified */ if (! (prog_options.no_dir && ATTR_MASK_TEST(&root_attrs, type) && !strcasecmp(ATTR(&root_attrs, type), STR_TYPE_DIR))) { wagon_t w; w.id = root_id; w.fullname = ATTR(&root_attrs, fullpath); print_entry(&w, &root_attrs); } } /* list all, including dirs */ it = ListMgr_Iterator(&lmgr, &entry_filter, NULL, NULL); if (!it) { DisplayLog(LVL_MAJOR, FIND_TAG, "ERROR: cannot retrieve entry list from database"); return -1; } attrs.attr_mask = disp_mask | query_mask; while ((rc = ListMgr_GetNext(it, &id, &attrs)) == DB_SUCCESS) { if (!is_expr || (EntryMatches(&id, &attrs, &match_expr, NULL) == POLICY_MATCH)) { /* don't display dirs if no_dir is specified */ if (! (prog_options.no_dir && ATTR_MASK_TEST(&attrs, type) && !strcasecmp(ATTR(&attrs, type), STR_TYPE_DIR))) { wagon_t w; w.id = id; w.fullname = ATTR(&attrs, fullpath); print_entry(&w, &attrs); } /* don't display non dirs is dir_only is specified */ else if (! (prog_options.dir_only && ATTR_MASK_TEST(&attrs, type) && strcasecmp(ATTR(&attrs, type), STR_TYPE_DIR))) { wagon_t w; w.id = id; w.fullname = ATTR(&attrs, fullpath); print_entry(&w, &attrs); } else /* return entry don't match? */ DisplayLog(LVL_DEBUG, FIND_TAG, "Warning: returned DB entry doesn't match filter: %s", ATTR(&attrs, fullpath)); } ListMgr_FreeAttrs(&attrs); /* prepare next call */ attrs.attr_mask = disp_mask | query_mask; } ListMgr_CloseIterator(it); return 0; }
/** * List the content of the given id/path list */ static int list_content(char ** id_list, int id_count) { wagon_t *ids; int i, rc; attr_set_t root_attrs; entry_id_t root_id; int is_id; rc = get_root_id(&root_id); if (rc) return rc; ids = MemCalloc(id_count, sizeof(wagon_t)); if (!ids) return -ENOMEM; for (i = 0; i < id_count; i++) { is_id = TRUE; /* is it a path or fid? */ if (sscanf(id_list[i], SFID, RFID(&ids[i].id)) != FID_SCAN_CNT) { is_id = FALSE; /* take it as a path */ rc = Path2Id(id_list[i], &ids[i].id); if (!rc) { ids[i].fullname = id_list[i]; if (FINAL_SLASH(ids[i].fullname)) REMOVE_FINAL_SLASH(ids[i].fullname); } } else { #if _HAVE_FID /* Take it as an FID. */ char path[RBH_PATH_MAX]; rc = Lustre_GetFullPath( &ids[i].id, path, sizeof(path)); if (!rc) ids[i].fullname = strdup(path); #endif } if (rc) { DisplayLog(LVL_MAJOR, FIND_TAG, "Invalid parameter: %s: %s", id_list[i], strerror(-rc)); goto out; } /* get root attrs to print it (if it matches program options) */ root_attrs.attr_mask = disp_mask | query_mask; rc = ListMgr_Get(&lmgr, &ids[i].id, &root_attrs); if (rc == 0) dircb(&ids[i], &root_attrs, 1, NULL); else { DisplayLog(LVL_VERB, FIND_TAG, "Notice: no attrs in DB for %s", id_list[i]); if (!is_id) { struct stat st; ATTR_MASK_SET(&root_attrs, fullpath); strcpy(ATTR(&root_attrs, fullpath), id_list[i]); if (lstat(ATTR(&root_attrs, fullpath ), &st) == 0) { PosixStat2EntryAttr(&st, &root_attrs, TRUE); ListMgr_GenerateFields( &root_attrs, disp_mask | query_mask); } } else if (entry_id_equal(&ids[i].id, &root_id)) { /* this is root id */ struct stat st; ATTR_MASK_SET(&root_attrs, fullpath); strcpy(ATTR(&root_attrs, fullpath), config.global_config.fs_path); if (lstat(ATTR(&root_attrs, fullpath ), &st) == 0) { PosixStat2EntryAttr(&st, &root_attrs, TRUE); ListMgr_GenerateFields( &root_attrs, disp_mask | query_mask); } } dircb(&ids[i], &root_attrs, 1, NULL); } rc = rbh_scrub(&lmgr, &ids[i], 1, disp_mask | query_mask, dircb, NULL); } out: /* ids have been processed, free them */ MemFree(ids); return rc; }
int cmd_send(int argc, char **argv) { char *subvol = NULL; int ret; char outname[PATH_MAX]; struct btrfs_send send; u32 i; char *mount_root = NULL; char *snapshot_parent = NULL; 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[] = { { "verbose", no_argument, NULL, 'v' }, { "quiet", no_argument, NULL, 'q' }, { "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA } }; int c = getopt_long(argc, argv, "vqec:f:i:p:", long_options, NULL); if (c < 0) break; switch (c) { case 'v': g_verbose++; break; case 'q': g_verbose = 0; break; case 'e': new_end_cmd_semantic = 1; break; case 'c': subvol = realpath(optarg, NULL); if (!subvol) { ret = -errno; error("realpath %s failed: %s\n", optarg, strerror(-ret)); goto out; } ret = set_root_info(&send, subvol, &root_id); if (ret < 0) goto out; ret = is_subvol_ro(&send, subvol); if (ret < 0) goto out; if (!ret) { ret = -EINVAL; error("cloned subvolume %s is not read-only", subvol); goto out; } ret = add_clone_source(&send, root_id); if (ret < 0) { error("cannot add clone source: %s", strerror(-ret)); goto out; } free(subvol); subvol = NULL; free_send_info(&send); full_send = 0; break; case 'f': if (arg_copy_path(outname, optarg, sizeof(outname))) { error("output file path too long (%zu)", strlen(optarg)); ret = 1; goto out; } break; case 'p': if (snapshot_parent) { error("you cannot have more than one parent (-p)"); ret = 1; goto out; } snapshot_parent = realpath(optarg, NULL); if (!snapshot_parent) { ret = -errno; error("realpath %s failed: %s", optarg, strerror(-ret)); goto out; } ret = is_subvol_ro(&send, snapshot_parent); if (ret < 0) goto out; if (!ret) { ret = -EINVAL; error("parent subvolume %s is not read-only", snapshot_parent); goto out; } full_send = 0; break; case 'i': error("option -i was removed, use -c instead"); ret = 1; goto out; case GETOPT_VAL_SEND_NO_DATA: send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA; break; case '?': default: error("send arguments invalid"); ret = 1; goto out; } } if (check_argc_min(argc - optind, 1)) usage(cmd_send_usage); if (outname[0]) { int tmpfd; /* * Try to use an existing file first. Even if send runs as * root, it might not have permissions to create file (eg. on a * NFS) but it should still be able to use a pre-created file. */ tmpfd = open(outname, O_WRONLY | O_TRUNC); if (tmpfd < 0) { if (errno == ENOENT) tmpfd = open(outname, O_CREAT | O_WRONLY | O_TRUNC, 0600); } send.dump_fd = tmpfd; if (send.dump_fd == -1) { ret = -errno; error("cannot create '%s': %s", outname, strerror(-ret)); goto out; } } if (isatty(send.dump_fd)) { error( "not dumping send stream into a terminal, redirect it into a file"); ret = 1; goto out; } /* use first send subvol to determine mount_root */ subvol = realpath(argv[optind], NULL); if (!subvol) { ret = -errno; error("unable to resolve %s", argv[optind]); goto out; } ret = init_root_path(&send, subvol); if (ret < 0) goto out; if (snapshot_parent != NULL) { ret = get_root_id(&send, subvol_strip_mountpoint(send.root_path, snapshot_parent), &parent_root_id); if (ret < 0) { error("could not resolve rootid for %s", snapshot_parent); goto out; } ret = add_clone_source(&send, parent_root_id); if (ret < 0) { error("cannot add clone source: %s", strerror(-ret)); goto out; } } for (i = optind; i < argc; i++) { free(subvol); subvol = realpath(argv[i], NULL); if (!subvol) { ret = -errno; error("unable to resolve %s", argv[i]); goto out; } ret = find_mount_root(subvol, &mount_root); if (ret < 0) { error("find_mount_root failed on %s: %s", subvol, strerror(-ret)); goto out; } if (ret > 0) { error("%s does not belong to btrfs mount point", subvol); ret = -EINVAL; goto out; } if (strcmp(send.root_path, mount_root) != 0) { ret = -EINVAL; error("all subvolumes must be from the same filesystem"); goto out; } free(mount_root); ret = is_subvol_ro(&send, subvol); if (ret < 0) goto out; if (!ret) { ret = -EINVAL; error("subvolume %s is not read-only", subvol); goto out; } } if ((send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA) && g_verbose > 1) if (g_verbose > 1) fprintf(stderr, "Mode NO_FILE_DATA enabled\n"); for (i = optind; i < argc; i++) { int is_first_subvol; int is_last_subvol; free(subvol); subvol = argv[i]; if (g_verbose > 0) fprintf(stderr, "At subvol %s\n", subvol); subvol = realpath(subvol, NULL); if (!subvol) { ret = -errno; error("realpath %s failed: %s", argv[i], strerror(-ret)); goto out; } if (!full_send && !snapshot_parent) { ret = set_root_info(&send, subvol, &root_id); if (ret < 0) goto out; ret = find_good_parent(&send, root_id, &parent_root_id); if (ret < 0) { error("parent determination failed for %lld", root_id); goto out; } } if (new_end_cmd_semantic) { /* require new kernel */ is_first_subvol = (i == optind); is_last_subvol = (i == argc - 1); } else { /* be compatible to old and new kernel */ is_first_subvol = 1; is_last_subvol = 1; } ret = do_send(&send, parent_root_id, is_first_subvol, is_last_subvol, subvol, send_flags); if (ret < 0) goto out; if (!full_send && !snapshot_parent) { /* done with this subvol, so add it to the clone sources */ ret = add_clone_source(&send, root_id); if (ret < 0) { error("cannot add clone source: %s", strerror(-ret)); goto out; } free_send_info(&send); } } ret = 0; out: free(subvol); free(snapshot_parent); free(send.clone_sources); free_send_info(&send); return !!ret; }
int cmd_send_start(int argc, char **argv) { char *subvol = NULL; int c; int ret; char *outname = NULL; struct btrfs_send send; u32 i; char *mount_root = NULL; char *snapshot_parent = NULL; u64 root_id; u64 parent_root_id = 0; int full_send = 1; memset(&send, 0, sizeof(send)); send.dump_fd = fileno(stdout); while ((c = getopt(argc, argv, "vc:f:i:p:")) != -1) { switch (c) { case 'v': g_verbose++; break; case 'c': subvol = realpath(optarg, NULL); if (!subvol) { ret = -errno; fprintf(stderr, "ERROR: realpath %s failed. " "%s\n", optarg, strerror(-ret)); goto out; } ret = init_root_path(&send, subvol); if (ret < 0) goto out; ret = get_root_id(&send, get_subvol_name(send.root_path, subvol), &root_id); if (ret < 0) { fprintf(stderr, "ERROR: could not resolve " "root_id for %s\n", subvol); goto out; } add_clone_source(&send, root_id); subvol_uuid_search_finit(&send.sus); free(subvol); subvol = NULL; if (send.mnt_fd >= 0) { close(send.mnt_fd); send.mnt_fd = -1; } free(send.root_path); send.root_path = NULL; full_send = 0; break; case 'f': outname = optarg; break; case 'p': if (snapshot_parent) { fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n"); ret = 1; goto out; } snapshot_parent = realpath(optarg, NULL); if (!snapshot_parent) { ret = -errno; fprintf(stderr, "ERROR: realpath %s failed. " "%s\n", optarg, strerror(-ret)); goto out; } full_send = 0; break; case 'i': fprintf(stderr, "ERROR: -i was removed, use -c instead\n"); ret = 1; goto out; case '?': default: fprintf(stderr, "ERROR: send args invalid.\n"); ret = 1; goto out; } } if (optind == argc) { fprintf(stderr, "ERROR: send needs path to snapshot\n"); ret = 1; goto out; } if (outname != NULL) { send.dump_fd = creat(outname, 0600); if (send.dump_fd == -1) { ret = -errno; fprintf(stderr, "ERROR: can't create '%s': %s\n", outname, strerror(-ret)); goto out; } } if (isatty(send.dump_fd)) { fprintf(stderr, "ERROR: not dumping send stream into a terminal, " "redirect it into a file\n"); ret = 1; goto out; } /* use first send subvol to determine mount_root */ subvol = argv[optind]; subvol = realpath(argv[optind], NULL); if (!subvol) { ret = -errno; fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]); goto out; } ret = init_root_path(&send, subvol); if (ret < 0) goto out; if (snapshot_parent != NULL) { ret = get_root_id(&send, get_subvol_name(send.root_path, snapshot_parent), &parent_root_id); if (ret < 0) { fprintf(stderr, "ERROR: could not resolve root_id " "for %s\n", snapshot_parent); goto out; } add_clone_source(&send, parent_root_id); } for (i = optind; i < argc; i++) { free(subvol); subvol = realpath(argv[i], NULL); if (!subvol) { ret = -errno; fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]); goto out; } ret = find_mount_root(subvol, &mount_root); if (ret < 0) { fprintf(stderr, "ERROR: find_mount_root failed on %s: " "%s\n", subvol, strerror(-ret)); goto out; } if (strcmp(send.root_path, mount_root) != 0) { ret = -EINVAL; fprintf(stderr, "ERROR: all subvols must be from the " "same fs.\n"); goto out; } free(mount_root); ret = is_subvol_ro(&send, subvol); if (ret < 0) goto out; if (!ret) { ret = -EINVAL; fprintf(stderr, "ERROR: %s is not read-only.\n", subvol); goto out; } } for (i = optind; i < argc; i++) { free(subvol); subvol = argv[i]; fprintf(stderr, "At subvol %s\n", subvol); subvol = realpath(subvol, NULL); if (!subvol) { ret = -errno; fprintf(stderr, "ERROR: realpath %s failed. " "%s\n", argv[i], strerror(-ret)); goto out; } ret = get_root_id(&send, get_subvol_name(send.root_path, subvol), &root_id); if (ret < 0) { fprintf(stderr, "ERROR: could not resolve root_id " "for %s\n", subvol); goto out; } if (!full_send && !parent_root_id) { ret = find_good_parent(&send, root_id, &parent_root_id); if (ret < 0) { fprintf(stderr, "ERROR: parent determination failed for %lld\n", root_id); goto out; } } ret = is_subvol_ro(&send, subvol); if (ret < 0) goto out; if (!ret) { ret = -EINVAL; fprintf(stderr, "ERROR: %s is not read-only.\n", subvol); goto out; } ret = do_send(&send, root_id, parent_root_id); if (ret < 0) goto out; /* done with this subvol, so add it to the clone sources */ add_clone_source(&send, root_id); parent_root_id = 0; full_send = 0; } ret = 0; out: free(subvol); free(snapshot_parent); free(send.clone_sources); if (send.mnt_fd >= 0) close(send.mnt_fd); free(send.root_path); subvol_uuid_search_finit(&send.sus); return ret; }