int resize_gpt_partition(const char *devname, __u64 new_size) { unsigned char buf[SECTOR_SIZE*GPT_DATA_SIZE]; // LBA1 header, LBA2-34 partition entry int fd; int part, ret; struct GptHeader *pt; struct GptEntry *pe; __u32 pt_crc32, pe_crc32, orig_crc; off_t size; __u64 tmp; ret = has_partition(devname, &part); if (ret) return ret; if (!part) return 0; ret = ploop_get_size(devname, &size); if (ret) return ret; // Resize up to max available space if (new_size == 0) new_size = size; if (new_size > size) { ploop_err(0, "Unable to resize GPT partition:" " incorrect parameter new_size=%llu size=%lu", new_size, (long)size); return SYSEXIT_PARAM; } ploop_log(1, "Resizing GPT partition to %ld", (long)new_size); fd = open(devname, O_RDWR); if (fd == -1) { ploop_err(errno, "open %s", devname); return SYSEXIT_OPEN; } // skip LBA0 Protective MBR ret = pread(fd, buf, sizeof(buf), SECTOR_SIZE); if (ret == -1) { ploop_err(errno, "pread %s", devname); goto err; } pt = (struct GptHeader *)buf; pe = (struct GptEntry *)(&buf[SECTOR_SIZE * GPT_HEADER_SIZE]); // Validate crc orig_crc = pt->header_crc32; pt->header_crc32 = 0; pt_crc32 = ploop_crc32((unsigned char *)pt, pt->header_size); if (pt_crc32 != orig_crc) { ploop_err(0, "GPT validation failed orig crc %x != %x", orig_crc, pt_crc32); ret = -1; goto err; } // change GPT header pt->alternate_lba = new_size - 1; pt->last_usable_lba = new_size - GPT_DATA_SIZE - 1; pe->ending_lba = (pt->last_usable_lba >> 3 << 3) - 1; // Recalculate crc32 pe_crc32 = ploop_crc32((unsigned char *)pe, SECTOR_SIZE * GPT_PT_ENTRY_SIZE); pt->partition_entry_array_crc32 = pe_crc32; pt->header_crc32 = 0; pt_crc32 = ploop_crc32((unsigned char *)pt, pt->header_size); pt->header_crc32 = pt_crc32; ploop_log(0, "Storing GPT"); ret = pwrite(fd, pt, SECTOR_SIZE * GPT_DATA_SIZE, SECTOR_SIZE); if (ret == -1) { ploop_err(errno, "Failed to store primary GPT %s", devname); goto err; } ret = fsync(fd); if (ret) { ploop_err(errno, "Can't fsync %s", devname); ret = SYSEXIT_FSYNC; goto err; } // Store secondary GPT entries tmp = pt->my_lba; pt->my_lba = pt->alternate_lba; pt->alternate_lba = tmp; pt->partition_entry_lba = pt->last_usable_lba + 1; // Recalculate crc32 pt->header_crc32 = 0; pt_crc32 = ploop_crc32((unsigned char *)pt, pt->header_size); pt->header_crc32 = pt_crc32; ret = pwrite(fd, pe, SECTOR_SIZE * GPT_PT_ENTRY_SIZE, (new_size - GPT_DATA_SIZE)*SECTOR_SIZE); if (ret == -1) { ploop_err(errno, "Failed to store secondary GPT %s", devname); goto err; } // Store Secondary GPT header ret = pwrite(fd, pt, SECTOR_SIZE, (new_size - GPT_HEADER_SIZE)*SECTOR_SIZE); if (ret == -1) { ploop_err(errno, "Failed to store secondary GPT header %s", devname); goto err; } if (fsync(fd)) { ploop_err(errno, "Can't fsync %s", devname); ret = SYSEXIT_FSYNC; goto err; } blkpg_resize_partition(fd, pe); ret = 0; err: close(fd); if (ret < 0) ret = SYSEXIT_CHANGE_GPT; 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); }
int ploop_create_temporary_snapshot(struct ploop_disk_images_data *di, struct ploop_tsnapshot_param *param, int *holder_fd) { int ret; struct ploop_mount_param mount_param = { .ro = 1, }; char component_name[PLOOP_COOKIE_SIZE]; if (di == NULL || param == NULL) return SYSEXIT_PARAM; if (param->guid == NULL) { ploop_err(0, "Snapshot guid is not specified"); return SYSEXIT_PARAM; } if (param->component_name == NULL) { ploop_err(0, "Component name is not specified"); return SYSEXIT_PARAM; } if (ploop_lock_dd(di)) return SYSEXIT_LOCK; ret = do_create_snapshot(di, param->guid, param->snap_dir, param->cbt_uuid, SNAP_TYPE_TEMPORARY); if (ret) goto err_unlock; /* FIXME: should be processed from 'struct ploop_mount_param' only ?? */ char *t = di->runtime->component_name; snprintf(component_name, sizeof(component_name), "%s%s", holder_fd == NULL ? TSNAPSHOT_MOUNT_LOCK_MARK : "", param->component_name); di->runtime->component_name = component_name; mount_param.guid = param->guid; mount_param.target = param->target; ret = mount_image(di, &mount_param); di->runtime->component_name = t; if (ret) goto err_merge; strncpy(param->device, mount_param.device, sizeof(param->device)); param->device[sizeof(param->device) - 1] = '\0'; if (holder_fd != NULL) { ret = open_snap_holder(param->device, holder_fd); if (ret) goto err; } ploop_unlock_dd(di); return 0; err: ploop_umount(mount_param.device, di); err_merge: ploop_merge_snapshot_by_guid(di, di->top_guid, NULL); err_unlock: ploop_unlock_dd(di); return ret; } int is_device_inuse(const char *dev) { int count; char fname[PATH_MAX]; char cookie[PLOOP_COOKIE_SIZE] = ""; if (ploop_get_attr(dev, "open_count", &count)) return 1; /* detect if snapshot locked by ploop mount */ snprintf(fname, sizeof(fname), "/sys/block/%s/pstate/cookie", memcmp(dev, "/dev/", 5) == 0 ? dev + 5 : dev); if (read_line_quiet(fname, cookie, sizeof(cookie))) return 1; if (!strncmp(cookie, TSNAPSHOT_MOUNT_LOCK_MARK, sizeof(TSNAPSHOT_MOUNT_LOCK_MARK)-1)) return 0; /* snap holder + mount */ if (count >= 2) return 1; /* if there single reference we should detect is holder is alive */ if (count == 1 && ploop_get_mnt_by_dev(dev, fname, sizeof(fname)) != 0) return 1; return 0; }
int ploop_switch_snapshot_ex(struct ploop_disk_images_data *di, struct ploop_snapshot_switch_param *param) { int ret; int fd; char dev[64]; char uuid[UUID_SIZE]; char file_uuid[UUID_SIZE]; char new_top_delta_fname[PATH_MAX] = ""; char *old_top_delta_fname = NULL; char conf[PATH_MAX]; char conf_tmp[PATH_MAX]; off_t size; const char *guid = param->guid; int flags = param->flags; __u32 blocksize; int version; int snap_id; if (!is_valid_guid(guid)) { ploop_err(0, "Incorrect guid %s", guid); return SYSEXIT_PARAM; } if (ploop_lock_dd(di)) return SYSEXIT_LOCK; if (is_old_snapshot_format(di)) { ret = SYSEXIT_PARAM; goto err_cleanup1; } ret = SYSEXIT_PARAM; if (strcmp(di->top_guid, guid) == 0) { ploop_err(errno, "Nothing to do, already on %s snapshot", guid); goto err_cleanup1; } snap_id = find_snapshot_by_guid(di, guid); if (snap_id== -1) { ploop_err(0, "Can't find snapshot by uuid %s", guid); goto err_cleanup1; } if (di->snapshots[snap_id]->temporary) { ploop_err(0, "Snapshot %s is temporary", guid); goto err_cleanup1; } if (flags & PLOOP_SNAP_SKIP_TOPDELTA_CREATE) { ret = reset_top_delta(di, param); goto err_cleanup1; } // Read image param from snapshot we going to switch on ret = get_image_param(di, guid, &size, &blocksize, &version); if (ret) goto err_cleanup1; ret = gen_uuid_pair(uuid, sizeof(uuid), file_uuid, sizeof(file_uuid)); if (ret) { ploop_err(errno, "Can't generate uuid"); goto err_cleanup1; } if (!(flags & PLOOP_SNAP_SKIP_TOPDELTA_DESTROY)) { // device should be stopped ret = ploop_find_dev_by_dd(di, dev, sizeof(dev)); if (ret == -1) { ret = SYSEXIT_SYS; goto err_cleanup1; } else if (ret == 0) { ret = SYSEXIT_PARAM; ploop_err(0, "Unable to perform switch to snapshot operation" " on running device (%s)", dev); goto err_cleanup1; } ret = ploop_di_remove_image(di, di->top_guid, 0, &old_top_delta_fname); if (ret) goto err_cleanup1; } else if (param->guid_old != NULL) { if (!is_valid_guid(param->guid_old)) { ret = SYSEXIT_PARAM; ploop_err(0, "Incorrect guid %s", param->guid_old); goto err_cleanup1; } if (find_snapshot_by_guid(di, param->guid_old) != -1) { ret = SYSEXIT_PARAM; ploop_err(0, "Incorrect guid_old %s: already exists", param->guid_old); goto err_cleanup1; } ploop_di_change_guid(di, di->top_guid, param->guid_old); } snprintf(new_top_delta_fname, sizeof(new_top_delta_fname), "%s.%s", di->images[0]->file, file_uuid); ret = ploop_di_add_image(di, new_top_delta_fname, TOPDELTA_UUID, guid); if (ret) goto err_cleanup1; get_disk_descriptor_fname(di, conf, sizeof(conf)); snprintf(conf_tmp, sizeof(conf_tmp), "%s.tmp", conf); ret = ploop_store_diskdescriptor(conf_tmp, di); if (ret) goto err_cleanup1; // offline snapshot fd = create_snapshot_delta(new_top_delta_fname, blocksize, size, version); if (fd == -1) { ret = SYSEXIT_CREAT; goto err_cleanup2; } close(fd); if (rename(conf_tmp, conf)) { ploop_err(errno, "Can't rename %s %s", conf_tmp, conf); ret = SYSEXIT_RENAME; goto err_cleanup3; } /* destroy precached info */ drop_statfs_info(di->images[0]->file); if (old_top_delta_fname != NULL) { ploop_log(0, "Removing %s", old_top_delta_fname); if (unlink(old_top_delta_fname) && errno != ENOENT) ploop_err(errno, "Can't unlink %s", old_top_delta_fname); } ploop_log(0, "ploop snapshot has been successfully switched"); err_cleanup3: if (ret && unlink(new_top_delta_fname)) ploop_err(errno, "Can't unlink %s", conf_tmp); err_cleanup2: if (ret && unlink(conf_tmp)) ploop_err(errno, "Can't unlink %s", conf_tmp); err_cleanup1: ploop_unlock_dd(di); free(old_top_delta_fname); return ret; }
int do_create_snapshot(struct ploop_disk_images_data *di, const char *guid, const char *snap_dir, const char *cbt_uuid, int flags) { int ret, rc; int fd; char dev[64]; char snap_guid[UUID_SIZE]; char top_guid[UUID_SIZE]; char file_guid[UUID_SIZE]; char fname[PATH_MAX]; const char *prev_fname = NULL; char conf[PATH_MAX]; char conf_tmp[PATH_MAX]; int online = 0; int temporary = flags & SNAP_TYPE_TEMPORARY; int n; off_t size; __u32 blocksize; int version; uuid_t u; const __u8 *cbt_u = NULL; if (cbt_uuid != NULL) { ploop_log(0, "Create snapshot CBT uuid=%s", cbt_uuid); if (uuid_parse(cbt_uuid, u)) { ploop_log(-1, "Incorrect cbt uuid is specified %s", cbt_uuid); return SYSEXIT_PARAM; } cbt_u = u; } if (guid != NULL && !is_valid_guid(guid)) { ploop_err(0, "Incorrect guid %s", guid); return SYSEXIT_PARAM; } if (is_old_snapshot_format(di)) return SYSEXIT_PARAM; ret = gen_uuid_pair(snap_guid, sizeof(snap_guid), file_guid, sizeof(file_guid)); if (ret) return ret; if (di->vol && di->vol->parent) { ret = ploop_uuid_generate(top_guid, sizeof(top_guid)); if (ret) return ret; } else strcpy(top_guid, TOPDELTA_UUID); if (guid != NULL) { if (find_snapshot_by_guid(di, guid) != -1) { ploop_err(0, "The snapshot %s already exist", guid); return SYSEXIT_PARAM; } strcpy(snap_guid, guid); } n = get_snapshot_count(di); if (n == -1) { return SYSEXIT_PARAM; } else if (n > 128-2) { /* The number of images limited by 128 so the snapshot limit 128 - base_image - one_reserverd */ ploop_err(errno, "Unable to create a snapshot." " The maximum number of snapshots (%d) has been reached", n-1); return SYSEXIT_PARAM; } rc = ploop_find_dev_by_dd(di, dev, sizeof(dev)); if (rc == -1) return SYSEXIT_SYS; if (rc == 0) { if (flags & SNAP_TYPE_OFFLINE) { ret = get_image_param_online(dev, &size, &blocksize, &version); } else { online = 1; ret = complete_running_operation(di, dev); } if (ret) return ret; } else { ret = get_image_param_offline(di, di->top_guid, &size, &blocksize, &version); if (ret == SYSEXIT_OPEN && errno == EBUSY) { /* repair top delta */ char *topdelta[] = {find_image_by_guid(di, di->top_guid), NULL}; blocksize = di->blocksize; ret = check_deltas(di, topdelta, 0, &blocksize, NULL); if (ret) return ret; ret = get_image_param_offline(di, di->top_guid, &size, &blocksize, &version); } if (ret) return ret; } ret = merge_temporary_snapshots(di); if (ret) return ret; if (snap_dir != NULL) { char *name; char *dir; dir = realpath(snap_dir, NULL); if (dir == NULL) { ploop_err(errno, "Error in realpath(%s)", snap_dir); return SYSEXIT_CREAT; } name = strrchr(di->images[0]->file, '/'); if (name != NULL) name++; else name = di->images[0]->file; snprintf(fname, sizeof(fname), "%s/%s.%s", dir, name, file_guid); free(dir); } else snprintf(fname, sizeof(fname), "%s.%s", di->images[0]->file, file_guid); prev_fname = find_image_by_guid(di, di->top_guid); if (prev_fname == NULL) { ploop_err(0, "Unable to find image by uuid %s", di->top_guid); return SYSEXIT_PARAM; } ploop_di_change_guid(di, di->top_guid, snap_guid); if (temporary) ploop_di_set_temporary(di, snap_guid); ret = ploop_di_add_image(di, fname, top_guid, snap_guid); if (ret) return ret; get_disk_descriptor_fname(di, conf, sizeof(conf)); snprintf(conf_tmp, sizeof(conf_tmp), "%s.tmp", conf); ret = ploop_store_diskdescriptor(conf_tmp, di); if (ret) return ret; if (!online) { // offline snapshot ret = 0; fd = create_snapshot_delta(fname, blocksize, size, version); if (fd < 0) { ret = SYSEXIT_CREAT; goto err; } close(fd); if (cbt_u != NULL) ret = write_empty_cbt_to_image(fname, prev_fname, cbt_u); else if (di->mode != PLOOP_RAW_MODE) { if (rc == 0) ret = cbt_dump(di, dev, fname); else ret = ploop_move_cbt(fname, prev_fname); } if (ret) goto err; } else { // Always sync fs ret = create_snapshot(dev, fname, 1, cbt_u, prev_fname); if (ret) goto err; } if (rename(conf_tmp, conf)) { ploop_err(errno, "Can't rename %s %s", conf_tmp, conf); ret = SYSEXIT_RENAME; } if (ret && !online && unlink(fname)) ploop_err(errno, "Can't unlink %s", fname); ploop_log(0, "ploop %s %s has been successfully created", get_snap_str(temporary), snap_guid); err: if (ret && unlink(conf_tmp)) ploop_err(errno, "Can't unlink %s", conf_tmp); return ret; }
/* delete snapshot by guid * 1) if guid is not active and last -> delete guid * 2) if guid is not last merge with child -> delete child */ int do_delete_snapshot(struct ploop_disk_images_data *di, const char *guid) { int ret; char conf[PATH_MAX]; int nelem = 0; char dev[64]; int snap_id; if (is_old_snapshot_format(di) || guid == NULL) return SYSEXIT_PARAM; snap_id = find_snapshot_by_guid(di, guid); if (snap_id == -1) { ploop_err(0, "Can't find snapshot by uuid %s", guid); return SYSEXIT_NOSNAP; } ret = ploop_find_dev_by_dd(di, dev, sizeof(dev)); if (ret == -1) return SYSEXIT_SYS; else if (ret == 0 && strcmp(di->top_guid, guid) == 0) { ret = SYSEXIT_PARAM; ploop_err(0, "Unable to delete active snapshot %s", guid); return SYSEXIT_PARAM; } nelem = ploop_get_child_count_by_uuid(di, guid); if (nelem == 0) { struct ploop_snapshot_data *snap = di->snapshots[snap_id]; if (strcmp(snap->parent_guid, NONE_UUID) == 0) { ploop_err(0, "Unable to delete base image"); return SYSEXIT_PARAM; } if (strcmp(di->top_guid, guid) == 0) { int id = find_snapshot_by_guid(di, snap->parent_guid); if (id == -1) { ploop_err(0, "Can't find snapshot by uuid %s", snap->parent_guid); return SYSEXIT_PARAM; } if (di->snapshots[id]->temporary) { ploop_err(0, "Unable to delete top delta," " parent snapshot is temporary"); return SYSEXIT_PARAM; } } char *fname = find_image_by_guid(di, guid); if (fname == NULL) { ploop_err(0, "Unable to find image by uuid %s", guid); return SYSEXIT_PARAM; } ret = check_snapshot_mount(di, guid, fname, snap->temporary); if (ret) return ret; fname = NULL; /* snapshot is not active and last -> delete */ ret = ploop_di_remove_image(di, guid, 1, &fname); if (ret) return ret; get_disk_descriptor_fname(di, conf, sizeof(conf)); ret = ploop_store_diskdescriptor(conf, di); if (ret) { free(fname); return ret; } ploop_log(0, "Removing %s", fname); if (fname != NULL && unlink(fname)) { ploop_err(errno, "unlink %s", fname); free(fname); return SYSEXIT_UNLINK; } free(fname); if (ret == 0) ploop_log(0, "ploop snapshot %s has been successfully deleted", guid); } else if (nelem == 1) { const char *child_guid = ploop_get_child_by_uuid(di, guid); if (child_guid == NULL) { ploop_err(0, "Can't find child of uuid %s", guid); return SYSEXIT_PARAM; } ret = ploop_merge_snapshot_by_guid(di, strdupa(child_guid), NULL); } else if (!di->snapshots[snap_id]->temporary) { ploop_log(1, "Warning: Unable to delete snapshot %s as there are %d references" " to it; marking it as temporary instead", guid, nelem); di->snapshots[snap_id]->temporary = 1; get_disk_descriptor_fname(di, conf, sizeof(conf)); ret = ploop_store_diskdescriptor(conf, di); } return ret; }
int ploop_copy_stop(struct ploop_copy_handle *h) { int ret; struct ploop_copy_stat stat = {}; int iter; ploop_log(3, "pcopy last"); ret = freeze_fs(h); if (ret) goto err; iter = 1; for (;;) { ret = ploop_copy_next_iteration(h, &stat); if (ret) goto err; else if (stat.xferred == 0) break; if (iter++ > 2) { ploop_err(0, "Too many iterations on frozen FS, aborting"); return SYSEXIT_LOOP; } } /* Must clear dirty flag on ploop1 image. */ if (!h->raw) { struct ploop_pvd_header *vh = get_free_iobuf(h); if (PREAD(&h->idelta, vh, 4096, 0)) { ret = SYSEXIT_READ; goto err; } vh->m_DiskInUse = 0; ploop_dbg(4, "Update header"); ret = send_buf(h, vh, 4096, 0); if (ret) goto err; } ploop_dbg(4, "IOCTL TRACK_STOP"); ret = ioctl(h->devfd, PLOOP_IOC_TRACK_STOP, 0); if (ret) goto err; h->tracker_on = 0; ploop_dbg(4, "SEND 0 0 (close)"); send_async(h, NULL, 0, 0); pthread_join(h->send_th, NULL); h->send_th = 0; ploop_dbg(4, "pcopy stop done"); err: ploop_copy_release(h); return ret; }
int create_snapshot(const char *device, const char *delta, int syncfs, const __u8 *cbt_u, const char *prev_delta) { int ret; int lfd = -1; int fd = -1; off_t bdsize; struct ploop_ctl_delta req; __u32 blocksize; int version; void *or_data = NULL; ret = get_image_param_online(device, &bdsize, &blocksize, &version); if (ret) return ret; lfd = open(device, O_RDONLY|O_CLOEXEC); if (lfd < 0) { ploop_err(errno, "Can't open device %s", device); return SYSEXIT_DEVICE; } fd = create_snapshot_delta(delta, blocksize, bdsize, version); if (fd < 0) { ret = SYSEXIT_OPEN; goto err; } memset(&req, 0, sizeof(req)); req.c.pctl_format = PLOOP_FMT_PLOOP1; req.c.pctl_flags = syncfs ? PLOOP_FLAG_FS_SYNC : 0; req.c.pctl_cluster_log = ffs(blocksize) - 1; req.c.pctl_size = 0; req.c.pctl_chunks = 1; req.f.pctl_type = PLOOP_IO_AUTO; if (cbt_u != NULL) { if ((ret = cbt_snapshot_prepare(lfd, cbt_u, &or_data))) { unlink(delta); goto err; } } ploop_log(0, "Creating snapshot dev=%s img=%s", device, delta); ret = create_snapshot_ioctl(lfd, fd, &req); if (ret) { unlink(delta); goto err; } if (cbt_u != NULL) { if ((ret = cbt_snapshot(lfd, cbt_u, prev_delta, or_data))) { unlink(delta); goto err; } } err: free(or_data); if (lfd >= 0) close(lfd); if (fd >= 0) close(fd); return ret; }
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]; 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; ploop_log(0, "Send image %s device=%s fmt=%s blocksize=%d local=%d", image, device, format, blocksize, !is_remote); _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->devfd = open(device, O_RDONLY); if (_h->devfd == -1) { ploop_err(errno, "Can't open device %s", device); ret = SYSEXIT_DEVICE; goto err; } fcntl(_h->devfd, F_SETFD, FD_CLOEXEC); ret = SYSEXIT_OPEN; err = ploop_get_mnt_by_dev(device, mnt, sizeof(mnt)); if (err == -1) goto err; else if (ret == 0) { _h->mntfd = open(mnt, O_RDONLY); if (_h->mntfd < 0) { ploop_err(errno, "Can't open %s", mnt); goto err; } } if (open_delta_simple(&_h->idelta, image, O_RDONLY|O_DIRECT, OD_NOFLAGS)) { 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_next_iteration(struct ploop_copy_handle *h, struct ploop_copy_stat *stat) { struct ploop_track_extent e; int ret = 0; int done = 0; __u64 pos; __u64 iterpos = 0; stat->xferred = 0; ploop_dbg(3, "pcopy iter %d", h->niter); do { if (ioctl(h->devfd, PLOOP_IOC_TRACK_READ, &e)) { if (errno == EAGAIN) /* no more dirty blocks */ break; ploop_err(errno, "PLOOP_IOC_TRACK_READ"); ret = SYSEXIT_DEVIOC; goto err; } if (e.end > h->trackend) h->trackend = e.end; if (e.start < iterpos) done = 1; iterpos = e.end; stat->xferred += e.end - e.start; for (pos = e.start; pos < e.end; ) { ssize_t n; int copy = e.end - pos; if (copy > h->cluster) copy = h->cluster; if (pos + copy > h->trackpos) { h->trackpos = pos + copy; if (ioctl(h->devfd, PLOOP_IOC_TRACK_SETPOS, &h->trackpos)) { ploop_err(errno, "PLOOP_IOC_TRACK_SETPOS"); ret = SYSEXIT_DEVIOC; goto err; } } ret = send_image_block(h, copy, pos, &n); if (ret) goto err; if (n != copy) { ploop_err(errno, "Short read"); ret = SYSEXIT_READ; goto err; } pos += n; } } while (!done); wait_sender(h); /* sync after each iteration */ ret = send_cmd(h, PCOPY_CMD_SYNC); if (ret) goto err; stat->xferred_total += stat->xferred; ploop_log(3, "pcopy iter %d xferred=%" PRIu64, h->niter++, (uint64_t)stat->xferred); return 0; err: ploop_copy_release(h); return ret; }
int ploop_copy_receiver(struct ploop_copy_receive_param *arg) { int ofd, ret; __u64 cluster = 0; void *iobuf = NULL; int n; struct pcopy_pkt_desc desc; if (!arg) return SYSEXIT_PARAM; if (is_fd_socket(arg->ifd) != 1) { ploop_err(errno, "Invalid input fd %d: must be " "a pipe or a socket", arg->ifd); return SYSEXIT_PARAM; } ofd = open(arg->file, O_WRONLY|O_CREAT, 0600); if (ofd < 0) { ploop_err(errno, "Can't open %s", arg->file); return SYSEXIT_CREAT; } ploop_dbg(4, "RCV start %s", arg->file); for (;;) { if (nread(arg->ifd, &desc, sizeof(desc)) < 0) { ploop_err(errno, "Error in nread(desc)"); ret = SYSEXIT_READ; goto out; } if (desc.marker != PCOPY_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; } ploop_log(3, "RCV type=%d len=%d pos=%" PRIu64, desc.type, desc.size, (uint64_t)desc.pos); switch (desc.type) { case PCOPY_PKT_DATA: { 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; } break; } case PCOPY_PKT_CMD: { unsigned int cmd = ((unsigned int *) iobuf)[0]; switch(cmd) { case PCOPY_CMD_SYNC: ret = data_sync(ofd); if (ret) goto out; break; default: ploop_err(0, "ploop_copy_receiver: unsupported command %d", cmd); ret = SYSEXIT_PARAM; goto out; } break; } default: ploop_err(0, "ploop_copy_receiver: unsupported command type%d", desc.type); ret = SYSEXIT_PARAM; goto out; } /* send reply */ ret = 0; if (nwrite(arg->ifd, &ret, sizeof(int))) { ret = SYSEXIT_WRITE; ploop_err(errno, "failed to send reply"); goto out; } } ret = data_sync(ofd); if (ret) goto out; ploop_dbg(4, "RCV exited"); /* send final reply */ ret = 0; if (nwrite(arg->ifd, &ret, sizeof(int))) { ret = SYSEXIT_WRITE; ploop_err(errno, "failed to send reply"); goto out; } out: if (close(ofd)) { ploop_err(errno, "Error in close"); if (!ret) ret = SYSEXIT_WRITE; } if (ret) unlink(arg->file); free(iobuf); return ret; }
static int do_create_snapshot(struct ploop_disk_images_data *di, const char *guid, int temporary) { int ret; int fd; char dev[64]; char snap_guid[UUID_SIZE]; char file_guid[UUID_SIZE]; char fname[PATH_MAX]; char conf[PATH_MAX]; char conf_tmp[PATH_MAX]; int online = 0; int n; off_t size; __u32 blocksize; int version; if (guid != NULL && !is_valid_guid(guid)) { ploop_err(0, "Incorrect guid %s", guid); return SYSEXIT_PARAM; } if (is_old_snapshot_format(di)) return SYSEXIT_PARAM; ret = gen_uuid_pair(snap_guid, sizeof(snap_guid), file_guid, sizeof(file_guid)); if (ret) { ploop_err(errno, "Can't generate uuid"); return ret; } if (guid != NULL) { if (find_snapshot_by_guid(di, guid) != -1) { ploop_err(0, "The snapshot %s already exist", guid); return SYSEXIT_PARAM; } strcpy(snap_guid, guid); } n = get_snapshot_count(di); if (n == -1) { return SYSEXIT_PARAM; } else if (n > 128-2) { /* The number of images limited by 128 so the snapshot limit 128 - base_image - one_reserverd */ ploop_err(errno, "Unable to create a snapshot." " The maximum number of snapshots (%d) has been reached", n-1); return SYSEXIT_PARAM; } ret = ploop_find_dev_by_dd(di, dev, sizeof(dev)); if (ret == -1) return SYSEXIT_SYS; else if (ret == 0) { online = 1; } else { ret = get_image_param_offline(di, di->top_guid, &size, &blocksize, &version); if (ret == SYSEXIT_OPEN && errno == EBUSY) { /* repair top delta */ char *topdelta[] = {find_image_by_guid(di, di->top_guid), NULL}; blocksize = di->blocksize; ret = check_deltas(di, topdelta, 0, &blocksize); if (ret) return ret; ret = get_image_param_offline(di, di->top_guid, &size, &blocksize, &version); } if (ret) return ret; } ret = merge_temporary_snapshots(di); if (ret) return ret; snprintf(fname, sizeof(fname), "%s.%s", di->images[0]->file, file_guid); ploop_di_change_guid(di, di->top_guid, snap_guid); if (temporary) ploop_di_set_temporary(di, snap_guid); ret = ploop_di_add_image(di, fname, TOPDELTA_UUID, snap_guid); if (ret) return ret; get_disk_descriptor_fname(di, conf, sizeof(conf)); snprintf(conf_tmp, sizeof(conf_tmp), "%s.tmp", conf); ret = ploop_store_diskdescriptor(conf_tmp, di); if (ret) return ret; if (!online) { // offline snapshot fd = create_snapshot_delta(fname, blocksize, size, version); if (fd < 0) { ret = SYSEXIT_CREAT; goto err; } close(fd); } else { // Always sync fs ret = create_snapshot(dev, fname, 1); if (ret) goto err; } if (rename(conf_tmp, conf)) { ploop_err(errno, "Can't rename %s %s", conf_tmp, conf); ret = SYSEXIT_RENAME; } if (ret && !online && unlink(fname)) ploop_err(errno, "Can't unlink %s", fname); ploop_log(0, "ploop %s %s has been successfully created", get_snap_str(temporary), snap_guid); err: if (ret && unlink(conf_tmp)) ploop_err(errno, "Can't unlink %s", conf_tmp); return ret; }
int ploop_store_diskdescriptor(const char *fname, struct ploop_disk_images_data *di) { int i, rc = -1; xmlTextWriterPtr writer = NULL; xmlDocPtr doc = NULL; char tmp[PATH_MAX]; char basedir[PATH_MAX]; FILE *fp = NULL; ploop_log(0, "Storing %s", fname); if (convert_disk_descriptor(di)) return -1; if (di->runtime->xml_fname == NULL) di->runtime->xml_fname = strdup(fname); get_basedir(fname, tmp, sizeof(tmp)); if (tmp[0] == '\0') strcpy(tmp, "./"); if (realpath(tmp, basedir) == NULL) { ploop_err(errno, "Can't resolve %s", tmp); return -1; } doc = xmlNewDoc(BAD_CAST XML_DEFAULT_VERSION); if (doc == NULL) { ploop_err(0, "Error creating xml document tree"); return -1; } /* Create a new XmlWriter for DOM tree, with no compression. */ writer = xmlNewTextWriterTree(doc, NULL, 0); if (writer == NULL) { ploop_err(0, "Error creating xml writer"); goto err; } /* Start the document with the xml default for the version, * encoding ISO 8859-1 and the default for the standalone * declaration. */ rc = xmlTextWriterStartDocument(writer, NULL, NULL, NULL); if (rc < 0) { ploop_err(0, "Error at xmlTextWriterStartDocument"); goto err; } rc = xmlTextWriterStartElement(writer, BAD_CAST "Parallels_disk_image"); if (rc < 0) { ploop_err(0, "Error at xmlTextWriterStartDocument"); goto err; } /********************************************* * Disk_Parameters ********************************************/ rc = xmlTextWriterStartElement(writer, BAD_CAST "Disk_Parameters"); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter Disk_Parameters"); goto err; } rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Disk_size", "%llu", di->size); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter Disk_size"); goto err; } if (di->max_delta_size != 0) { rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Max_delta_size", "%llu", di->max_delta_size); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter Max_delta_size"); goto err; } } rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Cylinders", "%u", di->cylinders); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter Cylinders"); goto err; } rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Heads", "%u", di->heads); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter Heads"); goto err; } rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Sectors", "%llu", di->size /(di->cylinders * di->heads)); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter Sectors"); goto err; } rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Padding", "%u", 0); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter Padding"); goto err; } /* Close Disk_Parameters */ rc = xmlTextWriterEndElement(writer); if (rc < 0) { ploop_err(0, "Error at xmlTextWriterEndElement"); goto err; } /**************************************** * StorageData ****************************************/ rc = xmlTextWriterStartElement(writer, BAD_CAST "StorageData"); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter StorageData"); goto err; } /* Start an element named "Storage" as child of StorageData. */ rc = xmlTextWriterStartElement(writer, BAD_CAST "Storage"); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter Storage"); goto err; } rc = xmlTextWriterWriteElement(writer, BAD_CAST "Start", BAD_CAST "0"); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter Start"); goto err; } rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "End", "%llu", di->size); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter End"); goto err; } rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Blocksize", "%d", di->blocksize); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter Blocksize"); goto err; } if (di->mode == PLOOP_EXPANDED_PREALLOCATED_MODE) { rc = xmlTextWriterWriteElement(writer, BAD_CAST "Preallocated", BAD_CAST "1"); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter Preallocated"); goto err; } } /**************************************** * Images ****************************************/ for (i = 0; i < di->nimages; i++) { rc = xmlTextWriterStartElement(writer, BAD_CAST "Image"); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter Image"); goto err; } rc = xmlTextWriterWriteElement(writer, BAD_CAST "GUID", BAD_CAST di->images[i]->guid); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter GUID"); goto err; } rc = xmlTextWriterWriteElement(writer, BAD_CAST "Type", BAD_CAST (di->mode == PLOOP_RAW_MODE ? "Plain" : "Compressed")); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter Type"); goto err; } normalize_image_name(basedir, di->images[i]->file, tmp, sizeof(tmp)); rc = xmlTextWriterWriteElement(writer, BAD_CAST "File", BAD_CAST tmp); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter File"); goto err; } /* Close Image */ rc = xmlTextWriterEndElement(writer); if (rc < 0) { ploop_err(0, "Error at xmlTextWriterEndElement"); goto err; } } /* Close Storage */ rc = xmlTextWriterEndElement(writer); if (rc < 0) { ploop_err(0, "Error at xmlTextWriterEndElement"); goto err; } /* Close StorageData. */ rc = xmlTextWriterEndElement(writer); if (rc < 0) { ploop_err(0, "Error at xmlTextWriterEndElement"); goto err; } /**************************************** * Snapshots ****************************************/ rc = xmlTextWriterStartElement(writer, BAD_CAST "Snapshots"); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter Snapshots"); goto err; } if (di->top_guid != NULL) { rc = xmlTextWriterWriteElement(writer, BAD_CAST "TopGUID", BAD_CAST di->top_guid); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter TopGUID"); goto err; } } /**************************************** * Shot ****************************************/ for (i = 0; i < di->nsnapshots; i++) { rc = xmlTextWriterStartElement(writer, BAD_CAST "Shot"); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter Shot"); goto err; } rc = xmlTextWriterWriteElement(writer, BAD_CAST "GUID", BAD_CAST di->snapshots[i]->guid); if (rc < 0) { ploop_err(0, "Error at xmlTextWriterWrite GUID"); goto err; } rc = xmlTextWriterWriteElement(writer, BAD_CAST "ParentGUID", BAD_CAST di->snapshots[i]->parent_guid); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter ParentGUID"); goto err; } if (di->snapshots[i]->temporary) { rc = xmlTextWriterWriteElement(writer, BAD_CAST "Temporary", BAD_CAST ""); if (rc < 0) { ploop_err(0, "Error at xmlTextWriter Temporary"); goto err; } } /* Close Shot */ rc = xmlTextWriterEndElement(writer); if (rc < 0) { ploop_err(0, "Error at xmlTextWriterEndElement"); goto err; } } /* Close Snapshots */ rc = xmlTextWriterEndElement(writer); if (rc < 0) { ploop_err(0, "Error at xmlTextWriterEndElement"); goto err; } /* Close Parallels_disk_image */ rc = xmlTextWriterEndElement(writer); if (rc < 0) { ploop_err(0, "Error at xmlTextWriterEndElement"); goto err; } xmlFreeTextWriter(writer); writer = NULL; snprintf(tmp, sizeof(tmp), "%s.tmp", fname); fp = fopen(tmp, "w+"); if (fp == NULL) { ploop_err(errno, "Can't open %s", tmp); goto err; } rc = xmlDocFormatDump(fp, doc, 1); if (rc < 0) { ploop_err(0, "Error at xmlDocFormatDump %s", tmp); goto err; } rc = fsync(fileno(fp)); if (rc) { ploop_err(errno, "Failed to sync %s", tmp); goto err; } fclose(fp); fp = NULL; rc = rename(tmp, fname); if (rc < 0) { ploop_err(errno, "Can't rename %s to %s", tmp, fname); goto err; } rc = 0; err: if (fp) fclose(fp); if (writer) xmlFreeTextWriter(writer); if (doc) xmlFreeDoc(doc); if (rc) return SYSEXIT_DISKDESCR; return 0; }
static int parse_xml(const char *basedir, xmlNode *root_node, struct ploop_disk_images_data *di) { xmlNode *cur_node, *node; char image[PATH_MAX]; const char *data = NULL; const char *file = NULL; const char *guid = NULL; const char *parent_guid = NULL; __u64 val; int is_preallocated = 0; int mode = PLOOP_EXPANDED_MODE; int n; cur_node = seek(root_node, "/Disk_Parameters"); ERR(cur_node, "/Disk_Parameters"); node = seek(cur_node, "Disk_size"); if (node != NULL) { data = get_element_txt(node); if (parse_ul(data, &val) == 0) di->size = val; } node = seek(cur_node, "Max_delta_size"); if (node != NULL) { data = get_element_txt(node); if (parse_ul(data, &val) == 0) di->max_delta_size = val; } node = seek(cur_node, "Cylinders"); if (node != NULL) { data = get_element_txt(node); if (parse_ul(data, &val) == 0) di->cylinders = (unsigned)val; } node = seek(cur_node, "Heads"); if (node != NULL) { data = get_element_txt(node); if (parse_ul(data, &val) == 0) di->heads = (unsigned)val; } node = seek(cur_node, "Sectors"); if (node != NULL) { data = get_element_txt(node); if (parse_ul(data, &val) == 0) di->sectors= (unsigned)val; } cur_node = seek(root_node, "/StorageData/Storage"); ERR(cur_node, "/StorageData/Storage"); for (n = 0; cur_node; cur_node = cur_node->next, n++) { if (cur_node->type != XML_ELEMENT_NODE) continue; if (n > 0) { ploop_err(0, "Invalid disk descriptor file format:" " splitted disk is not supported"); return -1; } node = seek(cur_node, "Blocksize"); if (node != NULL) { data = get_element_txt(node); if (parse_ul(data, &val)) { ploop_err(0, "Invalid disk descriptor file format:" " Invalid Blocksize %s", data); return -1; } di->blocksize = (unsigned)val; } node = seek(cur_node, "Preallocated"); if (node != NULL) { data = get_element_txt(node); if (parse_ul(data, &val) != 0 || val > 1) { ploop_err(0, "Invalid disk descriptor file format:" " Invalid Preallocated tag"); return -1; } is_preallocated = val; } } cur_node = seek(root_node, "/StorageData/Storage/Image"); ERR(cur_node, "/StorageData/Storage/Image"); for (; cur_node; cur_node = cur_node->next) { if (cur_node->type != XML_ELEMENT_NODE) continue; guid = NULL; node = seek(cur_node, "GUID"); if (node != NULL) { guid = get_element_txt(node); if (guid == NULL) guid = ""; } ERR(guid, "GUID"); node = seek(cur_node, "Type"); if (node != NULL) { data = get_element_txt(node); if (data != NULL && !strcmp(data, "Plain")) mode = PLOOP_RAW_MODE; } file = NULL; node = seek(cur_node, "File"); if (node != NULL) { file = get_element_txt(node); if (file != NULL) { if (basedir[0] != 0 && file[0] != '/') snprintf(image, sizeof(image), "%s%s", basedir, file); else snprintf(image, sizeof(image), "%s", file); } } ERR(file, "File"); if (ploop_add_image_entry(di, image, guid)) return -1; } if (is_preallocated) { if (mode == PLOOP_RAW_MODE) { ploop_err(0, "Invalid disk descriptor file format:" " Preallocated is not compatible with Plain image"); return -1; } di->mode = PLOOP_EXPANDED_PREALLOCATED_MODE; } else { di->mode = mode; } cur_node = seek(root_node, "/Snapshots"); ERR(cur_node, "/Snapshots"); node = seek(cur_node, "TopGUID"); if (node != NULL) { data = get_element_txt(node); ERR(data, "TopGUID"); di->top_guid = strdup(data); } cur_node = seek(root_node, "/Snapshots/Shot"); if (cur_node != NULL) { for (; cur_node; cur_node = cur_node->next) { int temporary = 0; if (cur_node->type != XML_ELEMENT_NODE) continue; guid = NULL; node = seek(cur_node, "GUID"); if (node != NULL) guid = get_element_txt(node); ERR(guid, "Snapshots GUID"); parent_guid = NULL; node = seek(cur_node, "ParentGUID"); if (node != NULL) parent_guid = get_element_txt(node); ERR(parent_guid, "ParentGUID"); node = seek(cur_node, "Temporary"); if (node != NULL) temporary = 1; if (ploop_add_snapshot_entry(di, guid, parent_guid, temporary)) return -1; } } return 0; }
int ploop_di_merge_image(struct ploop_disk_images_data *di, const char *guid, char **fname) { int i, snap_id, image_id, nr_ch; struct ploop_image_data *image = NULL; struct ploop_snapshot_data *snapshot = NULL; snap_id = find_snapshot_by_guid(di, guid); if (snap_id == -1) { ploop_err(0, "Unable to find snapshot by uuid %s", guid); return SYSEXIT_PARAM; } snapshot = di->snapshots[snap_id]; image_id = find_image_idx_by_guid(di, guid); if (image_id == -1) { ploop_err(0, "Unable to find image by uuid %s", guid); return SYSEXIT_PARAM; } nr_ch = ploop_get_child_count_by_uuid(di, snapshot->parent_guid); if (nr_ch > 1) { ploop_err(0, "Unable to merge snapshot %s: " "it has %d children", guid, nr_ch); return SYSEXIT_PARAM; } if (guidcmp(snapshot->parent_guid, NONE_UUID) == 0) { ploop_err(0, "Unable to merge image %s: it is a base image", guid); return SYSEXIT_PARAM; } image = di->images[image_id]; if (fname != NULL) { *fname = strdup(image->file); if (*fname == NULL) return SYSEXIT_MALLOC; } /* Caller passed child_guid S2 to delete S1 (S1 <- S2 <- S3) (S2 <- S3) * so it has merge S2 to S1 and we should update all S1 referrences to S2 */ for (i = 0; i < di->nsnapshots; i++) { if (guidcmp(di->snapshots[i]->guid, snapshot->parent_guid) == 0) { strcpy(di->snapshots[i]->guid, guid); /* preserve temporary flag */ di->snapshots[i]->temporary = snapshot->temporary; } } for (i = 0; i < di->nimages; i++) if (guidcmp(di->images[i]->guid, snapshot->parent_guid) == 0) strcpy(di->images[i]->guid, guid); remove_data_from_array((void**)di->snapshots, di->nsnapshots, snap_id); di->nsnapshots--; remove_data_from_array((void**)di->images, di->nimages, image_id); di->nimages--; if (guidcmp(snapshot->guid, TOPDELTA_UUID) == 0) ploop_di_change_guid(di, snapshot->parent_guid, TOPDELTA_UUID); free_snapshot_data(snapshot); free_image_data(image); return 0; }