int ploop_copy_init(struct ploop_disk_images_data *di, struct ploop_copy_param *param, struct ploop_copy_handle **h) { int ret, err; int blocksize; char *image = NULL; char *format = NULL; char device[64]; char partdev[64]; struct ploop_copy_handle *_h = NULL; int is_remote; char mnt[PATH_MAX] = ""; is_remote = is_fd_socket(param->ofd); if (is_remote < 0) { ploop_err(0, "Invalid output fd %d: must be a file, " "a pipe or a socket", param->ofd); return SYSEXIT_PARAM; } if (param->ofd == STDOUT_FILENO) ploop_set_verbose_level(PLOOP_LOG_NOSTDOUT); else if (param->ofd == STDERR_FILENO) ploop_set_verbose_level(PLOOP_LOG_NOCONSOLE); if (ploop_lock_dd(di)) return SYSEXIT_LOCK; if (ploop_find_dev_by_dd(di, device, sizeof(device))) { ploop_err(0, "Can't find running ploop device"); ret = SYSEXIT_SYS; goto err; } ret = get_image_info(device, &image, &format, &blocksize); if (ret) goto err; _h = alloc_ploop_copy_handle(S2B(blocksize)); if (_h == NULL) { ploop_err(0, "alloc_ploop_copy_handle"); ret = SYSEXIT_MALLOC; goto err; } _h->raw = strcmp(format, "raw") == 0; _h->ofd = param->ofd; _h->is_remote = is_remote; _h->async = param->async; _h->devfd = open(device, O_RDONLY|O_CLOEXEC); if (_h->devfd == -1) { ploop_err(errno, "Can't open device %s", device); ret = SYSEXIT_DEVICE; goto err; } ret = get_partition_device_name(device, partdev, sizeof(partdev)); if (ret) goto err; _h->partfd = open(partdev, O_RDONLY|O_CLOEXEC); if (_h->partfd == -1) { ploop_err(errno, "Can't open device %s", partdev); ret = SYSEXIT_DEVICE; goto err; } ret = SYSEXIT_OPEN; err = ploop_get_mnt_by_dev(device, mnt, sizeof(mnt)); if (err == -1) goto err; else if (err == 0) { _h->mntfd = open(mnt, O_RDONLY|O_NONBLOCK|O_DIRECTORY); if (_h->mntfd < 0) { ploop_err(errno, "Can't open %s", mnt); goto err; } } ploop_log(0, "Send image %s dev=%s mnt=%s fmt=%s blocksize=%d local=%d", image, device, mnt, format, blocksize, !is_remote); if (open_delta(&_h->idelta, image, O_RDONLY|O_DIRECT, OD_ALLOW_DIRTY)) { ret = SYSEXIT_OPEN; goto err; } ret = complete_running_operation(di, device); if (ret) goto err; _h->cl = register_cleanup_hook(cancel_sender, _h); pthread_mutex_lock(&_h->sd.wait_mutex); err: if (ret) { ploop_copy_release(_h); free_ploop_copy_handle(_h); } else *h = _h; free(image); ploop_unlock_dd(di); return ret; }
int ploop_copy_receive(struct ploop_copy_receive_param *arg) { int ofd, ret; __u64 cluster = 0; void *iobuf = NULL; if (!arg) return SYSEXIT_PARAM; if (is_fd_pipe(arg->ifd) != 1) { ploop_err(errno, "Invalid input fd %d: must be " "a pipe or a socket", arg->ifd); return SYSEXIT_PARAM; } if (arg->feedback_fd >= 0 && is_fd_pipe(arg->feedback_fd) != 1) { ploop_err(errno, "Invalid feedback fd %d: must be " "a pipe or a socket", arg->feedback_fd); return SYSEXIT_PARAM; } /* If feedback is to be send to stdout or stderr, * we have to disable logging to appropriate fd. * * As currently there's no way to disable just stderr, * so in this case we have to disable stdout as well. */ if (arg->feedback_fd == STDOUT_FILENO) ploop_set_verbose_level(PLOOP_LOG_NOSTDOUT); else if (arg->feedback_fd == STDERR_FILENO) ploop_set_verbose_level(PLOOP_LOG_NOCONSOLE); ofd = open(arg->file, O_WRONLY|O_CREAT|O_EXCL, 0600); if (ofd < 0) { ploop_err(errno, "Can't open %s", arg->file); return SYSEXIT_CREAT; } /* Read data */ for (;;) { int n; struct xfer_desc desc; if (nread(arg->ifd, &desc, sizeof(desc)) < 0) { ploop_err(errno, "Error in nread(desc)"); ret = SYSEXIT_READ; goto out; } if (desc.marker != PLOOPCOPY_MARKER) { ploop_err(0, "Stream corrupted"); ret = SYSEXIT_PROTOCOL; goto out; } if (desc.size > cluster) { free(iobuf); iobuf = NULL; cluster = desc.size; if (p_memalign(&iobuf, 4096, cluster)) { ret = SYSEXIT_MALLOC; goto out; } } if (desc.size == 0) break; if (nread(arg->ifd, iobuf, desc.size)) { ploop_err(errno, "Error in nread data"); ret = SYSEXIT_READ; goto out; } if (desc.size == SYNC_MARK) { int st; /* ignore received data, instead do sync */ st = fdatasync(ofd); if (arg->feedback_fd >= 0) { /* Tell the sending side how it went */ int w; w = write(arg->feedback_fd, st ? STATUS_FAIL : STATUS_OK, LEN_STATUS); /* check write error only if no error yet */ if (!st && w != LEN_STATUS) { ploop_err(errno, "Error in write(%d)", arg->feedback_fd); ret = SYSEXIT_WRITE; goto out; } } if (st) { ploop_err(errno, "Error in fdatasync()"); ret = SYSEXIT_WRITE; goto out; } continue; } n = pwrite(ofd, iobuf, desc.size, desc.pos); if (n != desc.size) { if (n < 0) ploop_err(errno, "Error in pwrite"); else ploop_err(0, "Error: short pwrite"); ret = SYSEXIT_WRITE; goto out; } } if (fdatasync(ofd)) { ploop_err(errno, "Error in fdatasync"); ret = SYSEXIT_WRITE; goto out; } ret = 0; out: if (close(ofd)) { ploop_err(errno, "Error in close"); if (!ret) ret = SYSEXIT_WRITE; } if (ret) unlink(arg->file); free(iobuf); return ret; }
int ploop_copy_send(struct ploop_copy_send_param *arg) { struct delta idelta = { .fd = -1 }; int tracker_on = 0; int fs_frozen = 0; int devfd = -1; int mntfd = -1; int ret = 0; char *send_from = NULL; char *format = NULL; void *iobuf[2] = {}; int blocksize; __u64 cluster; __u64 pos; __u64 iterpos; __u64 trackpos; __u64 trackend; __u64 xferred; int iter; struct ploop_track_extent e; int i; pthread_t send_th = 0; struct send_data sd = { .mutex = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .cond_sent = PTHREAD_COND_INITIALIZER, }; if (!arg) return SYSEXIT_PARAM; sd.fd = arg->ofd; sd.is_pipe = is_fd_pipe(arg->ofd); if (sd.is_pipe < 0) { ploop_err(0, "Invalid output fd %d: must be a file, " "a pipe or a socket", arg->ofd); return SYSEXIT_PARAM; } if (arg->feedback_fd >= 0 && is_fd_pipe(arg->feedback_fd) != 1) { ploop_err(errno, "Invalid feedback fd %d: must be " "a pipe or a socket", arg->feedback_fd); return SYSEXIT_PARAM; } /* If data is to be send to stdout or stderr, * we have to disable logging to appropriate fd. * * As currently there's no way to disable just stderr, * so in this case we have to disable stdout as well. */ if (arg->ofd == STDOUT_FILENO) ploop_set_verbose_level(PLOOP_LOG_NOSTDOUT); else if (arg->ofd == STDERR_FILENO) ploop_set_verbose_level(PLOOP_LOG_NOCONSOLE); devfd = open(arg->device, O_RDONLY); if (devfd < 0) { ploop_err(errno, "Can't open device %s", arg->device); ret = SYSEXIT_DEVICE; goto done; } mntfd = open_mount_point(arg->device); if (mntfd < 0) { /* Error is printed by open_mount_point() */ ret = SYSEXIT_OPEN; goto done; } ret = get_image_info(arg->device, &send_from, &format, &blocksize); if (ret) goto done; cluster = S2B(blocksize); ret = SYSEXIT_MALLOC; for (i = 0; i < 2; i++) if (p_memalign(&iobuf[i], 4096, cluster)) goto done; ret = complete_running_operation(NULL, arg->device); if (ret) goto done; ret = ioctl_device(devfd, PLOOP_IOC_TRACK_INIT, &e); if (ret) goto done; tracker_on = 1; if (open_delta_simple(&idelta, send_from, O_RDONLY|O_DIRECT, OD_NOFLAGS)) { ret = SYSEXIT_OPEN; goto done; } ret = pthread_create(&send_th, NULL, send_thread, &sd); if (ret) { ploop_err(ret, "Can't create send thread"); ret = SYSEXIT_SYS; goto done; } ploop_log(-1, "Sending %s", send_from); trackend = e.end; for (pos = 0; pos < trackend; ) { int n; trackpos = pos + cluster; ret = ioctl_device(devfd, PLOOP_IOC_TRACK_SETPOS, &trackpos); if (ret) goto done; n = do_pread(cluster, pos); if (n == 0) /* EOF */ break; async_send(n, pos); pos += n; } /* First copy done */ iter = 1; iterpos = 0; xferred = 0; for (;;) { int err; err = ioctl(devfd, PLOOP_IOC_TRACK_READ, &e); if (err == 0) { //fprintf(stderr, "TRACK %llu-%llu\n", e.start, e.end); fflush(stdout); if (e.end > trackend) trackend = e.end; if (e.start < iterpos) iter++; iterpos = e.end; xferred += e.end - e.start; for (pos = e.start; pos < e.end; ) { int n; int copy = e.end - pos; if (copy > cluster) copy = cluster; if (pos + copy > trackpos) { trackpos = pos + copy; if (ioctl(devfd, PLOOP_IOC_TRACK_SETPOS, &trackpos)) { ploop_err(errno, "PLOOP_IOC_TRACK_SETPOS"); ret = SYSEXIT_DEVIOC; goto done; } } n = do_pread(copy, pos); if (n == 0) { ploop_err(0, "Unexpected EOF"); ret = SYSEXIT_READ; goto done; } async_send(n, pos); pos += n; } } else { if (errno == EAGAIN) /* no more dirty blocks */ break; ploop_err(errno, "PLOOP_IOC_TRACK_READ"); ret = SYSEXIT_DEVIOC; goto done; } if (iter > 10 || (iter > 1 && xferred > trackend)) break; } /* Live iterative transfers are done. Either we transferred * everything or iterations did not converge. In any case * now we must suspend VE disk activity. Now it is just * call of an external program (something sort of * "killall -9 writetest; sleep 1; umount /mnt2"), actual * implementation must be intergrated to vzctl/vzmigrate * and suspend VE with subsequent fsyncing FS. */ /* Send the sync command to receiving side. Since older ploop * might be present on the other side, we need to not break the * backward compatibility, so just send the first few (SYNC_MARK) * bytes of delta file contents. New ploop_receive() interprets * this as "sync me" command, while the old one just writes those * bytes which is useless but harmless. */ if (sd.is_pipe) { char buf[LEN_STATUS + 1] = {}; ret = do_pread(4096, 0); if (ret < SYNC_MARK) { ploop_err(errno, "Short read"); ret = SYSEXIT_READ; goto done; } TS("SEND 0 %d (sync)", SYNC_MARK); async_send(SYNC_MARK, 0); /* Now we should wait for the other side to finish syncing * before freezing the container, to optimize CT frozen time. */ if (arg->feedback_fd < 0) { /* No descriptor to receive a response back is given. * As ugly as it looks, let's just sleep for some time * hoping the other side will finish sync. */ TS("SLEEP 5"); sleep(5); goto sync_done; } /* Wait for feedback from the receiving side */ /* FIXME: use select/poll with a timeout */ if (read(arg->feedback_fd, buf, LEN_STATUS) != LEN_STATUS) { ploop_err(errno, "Can't read feedback"); ret = SYSEXIT_PROTOCOL; goto done; } if (strncmp(buf, STATUS_OK, LEN_STATUS) == 0) { goto sync_done; } else if (strncmp(buf, STATUS_FAIL, LEN_STATUS) == 0) { ploop_err(0, "Remote side reported sync failure"); ret = SYSEXIT_FSYNC; goto done; } else { ploop_err(0, "Got back feedback: %s", buf); ret = SYSEXIT_PROTOCOL; goto done; } } else { /* Writing to local file */ fdatasync(arg->ofd); } sync_done: /* Freeze the container */ TS("FLUSH"); ret = run_cmd(arg->flush_cmd); if (ret) goto done; /* Sync fs */ TS("SYNCFS"); if (sys_syncfs(mntfd)) { ploop_err(errno, "syncfs() failed"); ret = SYSEXIT_FSYNC; goto done; } /* Flush journal and freeze fs (this also clears the fs dirty bit) */ TS("FIFREEZE"); ret = ioctl_device(mntfd, FIFREEZE, 0); if (ret) goto done; fs_frozen = 1; TS("IOC_SYNC"); ret = ioctl_device(devfd, PLOOP_IOC_SYNC, 0); if (ret) goto done; iter = 1; iterpos = 0; for (;;) { int err; struct ploop_track_extent e; err = ioctl(devfd, PLOOP_IOC_TRACK_READ, &e); if (err == 0) { __u64 pos; //fprintf(stderr, "TRACK %llu-%llu\n", e.start, e.end); fflush(stdout); if (e.end > trackend) trackend = e.end; if (e.start < iterpos) iter++; iterpos = e.end; for (pos = e.start; pos < e.end; ) { int n; int copy = e.end - pos; if (copy > cluster) copy = cluster; if (pos + copy > trackpos) { trackpos = pos + copy; ret = ioctl(devfd, PLOOP_IOC_TRACK_SETPOS, &trackpos); if (ret) goto done; } TS("READ %llu %d", pos, copy); n = do_pread(copy, pos); if (n == 0) { ploop_err(0, "Unexpected EOF"); ret = SYSEXIT_READ; goto done; } TS("SEND %llu %d", pos, n); async_send(n, pos); pos += n; } } else { if (errno == EAGAIN) break; ploop_err(errno, "PLOOP_IOC_TRACK_READ"); ret = SYSEXIT_DEVIOC; goto done; } if (iter > 2) { ploop_err(0, "Too many iterations on frozen FS, aborting"); ret = SYSEXIT_LOOP; goto done; } } /* Must clear dirty flag on ploop1 image. */ if (strcmp(format, "ploop1") == 0) { int n; struct ploop_pvd_header *vh; TS("READ 0 4096"); n = do_pread(4096, 0); if (n < SECTOR_SIZE) { ploop_err(errno, "Short read"); ret = SYSEXIT_READ; goto done; } vh = iobuf[i]; vh->m_DiskInUse = 0; TS("SEND 0 %d (1st sector)", SECTOR_SIZE); async_send(SECTOR_SIZE, 0); } TS("IOCTL TRACK_STOP"); ret = ioctl(devfd, PLOOP_IOC_TRACK_STOP, 0); if (ret) goto done; tracker_on = 0; TS("SEND 0 0 (close)"); async_send(0, 0); pthread_join(send_th, NULL); send_th = 0; done: if (send_th) pthread_cancel(send_th); if (fs_frozen) (void)ioctl_device(mntfd, FITHAW, 0); if (tracker_on) (void)ioctl_device(devfd, PLOOP_IOC_TRACK_ABORT, 0); free(iobuf[0]); free(iobuf[1]); if (devfd >=0) close(devfd); if (mntfd >=0) close(mntfd); free(send_from); if (idelta.fd >= 0) close_delta(&idelta); TS("DONE"); return ret; } #undef do_pread #undef async_send /* Deprecated, please use ploop_copy_send() instead */ int ploop_send(const char *device, int ofd, const char *flush_cmd, int is_pipe) { struct ploop_copy_send_param s = { .device = device, .ofd = ofd, .flush_cmd = flush_cmd, }; return ploop_copy_send(&s); }
static int plooptool_replace(int argc, char **argv) { int i; char dev[PATH_MAX]; char *device = NULL; char *mnt = NULL; struct ploop_replace_param param = { .level = -1, }; while ((i = getopt(argc, argv, "d:m:l:i:u:o:")) != EOF) { switch (i) { case 'd': device = optarg; break; case 'm': mnt = optarg; break; case 'l': param.level = atoi(optarg); break; case 'u': param.guid = parse_uuid(optarg); if (!param.guid) return SYSEXIT_PARAM; break; case 'i': param.file = strdup(optarg); break; case 'o': param.cur_file = strdup(optarg); break; default: usage_replace(); return SYSEXIT_PARAM; } } argc -= optind; argv += optind; if (!param.file) { fprintf(stderr, "Error: image file not specified (use -i)\n"); usage_replace(); return SYSEXIT_PARAM; } if ((argc == 1) && is_xml_fname(argv[0])) { int ret; struct ploop_disk_images_data *di; /* only one way of choosing delta to replace */ if ( (!!param.guid) + (param.level != -1) + (!!param.cur_file) != 1) { fprintf(stderr, "Error: either one of uuid (-u), " "level (-l) or current file (-o) " "must be specified\n"); usage_replace(); return SYSEXIT_PARAM; } ret = ploop_open_dd(&di, argv[0]); if (ret) return ret; ret = ploop_replace_image(di, ¶m); ploop_close_dd(di); return ret; } else { int level = param.level; if (argc > 0) { usage_replace(); return SYSEXIT_PARAM; } if ((!!device) + (!!mnt) != 1) { fprintf(stderr, "Error: either device (-d), mount " "point (-m) or DiskDescriptor.xml " "must be specified\n"); usage_replace(); return SYSEXIT_PARAM; } if (mnt) { if (ploop_get_dev_by_mnt(mnt, dev, sizeof(dev))) { fprintf(stderr, "Unable to find ploop device " "by mount point %s\n", mnt); return SYSEXIT_PARAM; } device = dev; } /* Either level or current delta must be specified */ if ((level != -1) + (!!param.cur_file) != 1) { fprintf(stderr, "Error: either one of level (-l) or " "current delta file (-o) must be " "specified\n"); usage_replace(); return SYSEXIT_PARAM; } if (param.cur_file) { int ret; level = find_level_by_delta(device, param.cur_file); if (level < 0) { fprintf(stderr, "Can't find level by " "delta file name %s", param.cur_file); return SYSEXIT_PARAM; } ret = check_deltas_same(param.cur_file, param.file); if (ret) return ret; } return replace_delta(device, level, param.file); } } int main(int argc, char **argv) { char * cmd; int v = 3; /* global options */ while (argc > 1 && argv[1][0] == '-') { switch (argv[1][1]) { case 'v': switch (argv[1][2]) { case '\0': v++; break; case 'v': /* -vvv... */ v += strlen(&argv[1][1]); break; default: /* -vNN */ v = atoi(&argv[1][2]); } break; case '-': /* long option */ /* fall through */ default: fprintf(stderr, "Bad option %s\n", argv[1]); usage_summary(); return SYSEXIT_PARAM; } argc--; argv++; } if (argc < 2) { usage_summary(); return SYSEXIT_PARAM; } cmd = argv[1]; argc--; argv++; ploop_set_verbose_level(v); init_signals(); if (strcmp(cmd, "init") == 0) return plooptool_init(argc, argv); if (strcmp(cmd, "start") == 0) return plooptool_start(argc, argv); if (strcmp(cmd, "stop") == 0) return plooptool_stop(argc, argv); if (strcmp(cmd, "clear") == 0) return plooptool_clear(argc, argv); if (strcmp(cmd, "mount") == 0) return plooptool_mount(argc, argv); if (strcmp(cmd, "umount") == 0) return plooptool_umount(argc, argv); if (strcmp(cmd, "delete") == 0 || strcmp(cmd, "rm") == 0) return plooptool_rm(argc, argv); if (strcmp(cmd, "snapshot") == 0) return plooptool_snapshot(argc, argv); if (strcmp(cmd, "tsnapshot") == 0) return plooptool_tsnapshot(argc, argv); if (strcmp(cmd, "snapshot-switch") == 0) return plooptool_snapshot_switch(argc, argv); if (strcmp(cmd, "snapshot-delete") == 0) return plooptool_snapshot_delete(argc, argv); if (strcmp(cmd, "snapshot-merge") == 0) return plooptool_snapshot_merge(argc, argv); if (strcmp(cmd, "snapshot-list") == 0) return plooptool_snapshot_list(argc, argv); if (strcmp(cmd, "getdev") == 0) return plooptool_getdevice(argc, argv); if (strcmp(cmd, "resize") == 0) return plooptool_resize(argc, argv); if (strcmp(cmd, "convert") == 0) return plooptool_convert(argc, argv); if (strcmp(cmd, "info") == 0) return plooptool_info(argc, argv); if (strcmp(cmd, "list") == 0) return plooptool_list(argc, argv); if (strcmp(cmd, "check") == 0) return plooptool_check(argc, argv); if (strcmp(cmd, "fsck") == 0) { fprintf(stderr, "WARNING: ploop fsck command is obsoleted, " "please use ploop check\n"); return plooptool_check(argc, argv); } if (strcmp(cmd, "grow") == 0) return plooptool_grow(argc, argv); if (strcmp(cmd, "merge") == 0) return plooptool_merge(argc, argv); if (strcmp(cmd, "stat") == 0) return plooptool_stat(argc, argv); if (strcmp(cmd, "copy") == 0) return plooptool_copy(argc, argv); if (strcmp(cmd, "replace") == 0) return plooptool_replace(argc, argv); if (cmd[0] != '-') { char ** nargs; nargs = calloc(argc+1, sizeof(char*)); nargs[0] = malloc(sizeof("ploop-") + strlen(cmd)); sprintf(nargs[0], "ploop-%s", cmd); memcpy(nargs + 1, argv + 1, (argc - 1)*sizeof(char*)); nargs[argc] = NULL; execvp(nargs[0], nargs); } usage_summary(); return SYSEXIT_PARAM; }