int ploop_di_add_image(struct ploop_disk_images_data *di, const char *fname, const char *guid, const char *parent_guid) { int ret; char *top_guid; top_guid = strdup(guid); if (top_guid == NULL) return SYSEXIT_MALLOC; ret = ploop_add_image_entry(di, fname, guid); if (ret) { free(top_guid); return ret; } ret = ploop_add_snapshot_entry(di, guid, parent_guid, 0); if (ret) { free(top_guid); return ret; } ploop_log(3, "Adding snapshot %s", guid); free(di->top_guid); di->top_guid = top_guid; return 0; }
static int blk_discard(int fd, __u32 cluster, __u64 start, __u64 len) { __u64 max_discard_len = S2B(B2S(UINT_MAX) / cluster * cluster); while (len > 0) { __u64 range[2]; int ret; range[0] = start; range[1] = MIN(len, max_discard_len); if (start % S2B(cluster) && len > range[1]) range[1] -= start % S2B(cluster); ploop_log(1, "Call BLKDISCARD start=%" PRIu64 " length=%" PRIu64, (uint64_t)range[0], (uint64_t)range[1]); ret = ioctl_device(fd, BLKDISCARD, range); if (ret) return ret; start += range[1]; len -= range[1]; } return 0; }
static void *sender_thread(void *data) { struct ploop_copy_handle *h = data; struct sender_data *sd = &h->sd; int done; pthread_mutex_lock(&sd->mutex); ploop_dbg(3, "start sender_thread"); pthread_barrier_wait(&sd->barrier); do { pthread_cond_wait(&sd->cond, &sd->mutex); wakeup(&sd->wait_mutex, &sd->wait_cond); sd->ret = send_buf(h, sd->buf, sd->len, sd->pos); if (sd->ret) sd->err_no = errno; done = (sd->len == 0 && sd->pos == 0); } while (!done); pthread_mutex_unlock(&sd->mutex); ploop_log(3, "send_thread exited ret=%d", sd->ret); return NULL; }
static int freeze(struct ploop_copy_handle *h) { int ret; ret = ioctl(h->partfd, PLOOP_IOC_FREEZE); if (ret) { if (errno == EINVAL) ret = freeze_fs(h); else ploop_err(errno, "Failed to freeze device"); if (ret) goto err; } else { ploop_log(0, "Freezing device..."); h->dev_frozen = 1; } ploop_dbg(3, "IOC_SYNC"); ret = ioctl_device(h->devfd, PLOOP_IOC_SYNC, 0); if (ret) goto err; return 0; err: ploop_copy_release(h); return ret; }
int ploop_di_remove_image(struct ploop_disk_images_data *di, const char *guid, int renew_top_uuid, char **fname) { int 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, guid); if (nr_ch != 0) { ploop_err(0, "Unable to delete snapshot %s: " "it has %d child%s", guid, nr_ch, (nr_ch == 1) ? "" : "ren"); return SYSEXIT_PARAM; } if (guidcmp(snapshot->parent_guid, NONE_UUID) == 0) { ploop_err(0, "Unable to delete 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; } ploop_log(3, "del snapshot %s", guid); // update top uuid if (renew_top_uuid && guidcmp(guid, di->top_guid) == 0) ploop_di_change_guid(di, snapshot->parent_guid, TOPDELTA_UUID); 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--; free_snapshot_data(snapshot); free_image_data(image); return 0; }
int ploop_copy_start(struct ploop_copy_handle *h, struct ploop_copy_stat *stat) { int ret; struct ploop_track_extent e; ssize_t n; __u64 pos; ret = pthread_create(&h->send_th, NULL, sender_thread, h); if (ret) { ploop_err(ret, "Can't create send thread"); ret = SYSEXIT_SYS; goto err; } pthread_barrier_wait(&h->sd.barrier); ploop_dbg(3, "pcopy track init"); ret = ioctl_device(h->devfd, PLOOP_IOC_TRACK_INIT, &e); if (ret) goto err; h->tracker_on = 1; h->trackend = e.end; ploop_log(3, "pcopy start %s e.end=%" PRIu64, h->async ? "async" : "", (uint64_t)e.end); for (pos = 0; pos <= h->trackend; ) { h->trackpos = pos + h->cluster; ret = ioctl_device(h->devfd, PLOOP_IOC_TRACK_SETPOS, &h->trackpos); if (ret) goto err; ret = send_image_block(h, h->cluster, pos, &n); if (ret) goto err; if (n == 0) /* EOF */ break; pos += n; if (pos > h->eof_offset) h->eof_offset = pos; } wait_sender(h); stat->xferred_total = stat->xferred = pos; send_cmd(h, PCOPY_CMD_SYNC); ploop_dbg(3, "pcopy start finished"); return 0; err: ploop_copy_release(h); return ret; }
static int do_truncate(int fd, int mntn_type, off_t old_size, off_t new_size) { int ret; switch (mntn_type) { case PLOOP_MNTN_OFF: case PLOOP_MNTN_MERGE: case PLOOP_MNTN_GROW: case PLOOP_MNTN_TRACK: break; case PLOOP_MNTN_BALLOON: ploop_err(0, "Error: mntn_type is PLOOP_MNTN_BALLOON " "after IOC_BALLOON"); return(SYSEXIT_PROTOCOL); case PLOOP_MNTN_FBLOADED: case PLOOP_MNTN_RELOC: ploop_err(0, "Can't truncate hidden balloon before previous " "balloon operation (%s) is completed. Use \"ploop-balloon " "complete\".", mntn2str(mntn_type)); return(SYSEXIT_EBUSY); default: ploop_err(0, "Error: unknown mntn_type (%u)", mntn_type); return(SYSEXIT_PROTOCOL); } if (new_size == old_size) { ploop_log(0, "Nothing to do: new_size == old_size"); } else if (ftruncate(fd, new_size)) { ploop_err(errno, "Can't truncate hidden balloon"); fsync_balloon(fd); return(SYSEXIT_FTRUNCATE); } else { ret = fsync_balloon(fd); if (ret) return ret; ploop_log(0, "Successfully truncated balloon from %llu to %llu bytes", (unsigned long long)old_size, (unsigned long long)new_size); } return 0; }
int create_snapshot(const char *device, const char *delta, int syncfs) { int ret; int lfd = -1; int fd = -1; off_t bdsize; struct ploop_ctl_delta req; __u32 blocksize; int version; ret = ploop_complete_running_operation(device); if (ret) return ret; ret = get_image_param_online(device, &bdsize, &blocksize, &version); if (ret) return ret; lfd = open(device, O_RDONLY); 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; ploop_log(0, "Creating snapshot dev=%s img=%s", device, delta); ret = create_snapshot_ioctl(lfd, fd, &req); if (ret) unlink(delta); err: if (lfd >= 0) close(lfd); if (fd >= 0) close(fd); return ret; }
static int ploop_trim(const char *mount_point, __u64 minlen_b, __u64 cluster) { struct fstrim_range range = {}; int fd, ret = -1; struct sigaction sa = { .sa_handler = stop_trim_handler, }; sigemptyset(&sa.sa_mask); if (sigaction(SIGUSR1, &sa, NULL)) { ploop_err(errno, "Can't set signal handler"); exit(1); } fd = open(mount_point, O_RDONLY); if (fd < 0) { ploop_err(errno, "Can't open mount point %s", mount_point); return -1; } sys_syncfs(fd); if (minlen_b < cluster) minlen_b = cluster; else minlen_b = (minlen_b + cluster - 1) / cluster * cluster; range.minlen = MAX(MAX_DISCARD_CLU * cluster, minlen_b); for (; range.minlen >= minlen_b; range.minlen /= 2) { ploop_log(1, "Call FITRIM, for minlen=%" PRIu64, (uint64_t)range.minlen); /* range.len is reseted by FITRIM */ range.len = ULLONG_MAX; ret = ioctl(fd, FITRIM, &range); if (ret < 0) { if (trim_stop) ret = 0; else ploop_err(errno, "Can't trim file system"); break; } /* last iteration should go with range.minlen == minlen_b */ if (range.minlen != minlen_b && range.minlen / 2 < minlen_b) range.minlen = minlen_b * 2; } close(fd); return ret; }
static int send_optional_header(struct ploop_copy_handle *copy_h) { int ret; struct ploop_pvd_header *vh; size_t block_size; struct ploop_pvd_ext_block_check *hc; struct ploop_pvd_ext_block_element_header *h; __u8 *block = NULL, *data; vh = (struct ploop_pvd_header *)copy_h->idelta.hdr0; block_size = vh->m_Sectors * SECTOR_SIZE; if (p_memalign((void **)&block, 4096, block_size)) return SYSEXIT_MALLOC; hc = (struct ploop_pvd_ext_block_check *)block; h = (struct ploop_pvd_ext_block_element_header *)(hc + 1); data = (__u8 *)(h + 1); h->magic = EXT_MAGIC_DIRTY_BITMAP; ret = save_dirty_bitmap(copy_h->devfd, ©_h->idelta, copy_h->eof_offset, data, &h->size, NULL, cbt_writer, copy_h); if (ret) { if (ret == SYSEXIT_NOCBT) ret = 0; goto out; } vh->m_DiskInUse = SIGNATURE_DISK_CLOSED_V21; vh->m_FormatExtensionOffset = (copy_h->eof_offset + SECTOR_SIZE - 1) / SECTOR_SIZE; if (send_buf(copy_h, vh, sizeof(*vh), 0)) { ploop_err(errno, "Can't write header"); ret = SYSEXIT_WRITE; goto out; } ploop_log(3, "Send extension header offset=%llu size=%d", vh->m_FormatExtensionOffset * SECTOR_SIZE, h->size); hc->m_Magic = FORMAT_EXTENSION_MAGIC; MD5((const unsigned char *)(hc + 1), block_size - sizeof(*hc), hc->m_Md5); if (send_buf(copy_h, block, block_size, vh->m_FormatExtensionOffset * SECTOR_SIZE)) { ploop_err(errno, "Can't write optional header"); ret = SYSEXIT_WRITE; goto out; } out: free(block); return ret; }
int ploop_complete_running_operation(const char *device) { struct ploop_balloon_ctl b_ctl; int fd, ret; fd = open_device(device); if (fd == -1) return SYSEXIT_OPEN; bzero(&b_ctl, sizeof(b_ctl)); b_ctl.keep_intact = 1; ret = ioctl(fd, PLOOP_IOC_BALLOON, &b_ctl); if (ret) { ploop_err(errno, "Unable to get in-kernel maintenance state"); ret = SYSEXIT_DEVIOC; goto err; } if (b_ctl.mntn_type == PLOOP_MNTN_OFF) goto err; ploop_log(0, "Completing an on-going operation %s for device %s", mntn2str(b_ctl.mntn_type), device); switch (b_ctl.mntn_type) { case PLOOP_MNTN_MERGE: ret = ioctl_device(fd, PLOOP_IOC_MERGE, 0); break; case PLOOP_MNTN_GROW: ret = ioctl_device(fd, PLOOP_IOC_GROW, 0); break; case PLOOP_MNTN_RELOC: case PLOOP_MNTN_FBLOADED: ret = ploop_balloon_complete(device); break; case PLOOP_MNTN_TRACK: ret = ioctl_device(fd, PLOOP_IOC_TRACK_ABORT, 0); break; case PLOOP_MNTN_DISCARD: ret = ploop_balloon_complete(device); break; case PLOOP_MNTN_BALLOON: /* FIXME : ploop_balloon_check_and_repair(device, mount_point, 1; */ ret = 0; break; } err: close(fd); return ret; }
/* Convert to new format with constant TopGUID */ static int convert_disk_descriptor(struct ploop_disk_images_data *di) { if (di->top_guid == NULL) { ploop_err(0, "Invalid DiskDescriptor.xml: TopGUID not found"); return -1; } if (!guidcmp(di->top_guid, TOPDELTA_UUID)) return 0; ploop_log(0, "DiskDescriptor.xml is in old format: converting"); if ((find_image_by_guid(di, TOPDELTA_UUID) != NULL) || (find_snapshot_by_guid(di, TOPDELTA_UUID) != -1)) { ploop_err(0, "Can't convert: %s is in use", TOPDELTA_UUID); return -1; } ploop_log(0, "Changing %s to %s", di->top_guid, TOPDELTA_UUID); ploop_di_change_guid(di, di->top_guid, TOPDELTA_UUID); return 0; }
static int reset_top_delta(struct ploop_disk_images_data *di, struct ploop_snapshot_switch_param *param) { int ret; char *old_top_delta_fname = NULL; char conf[PATH_MAX]; char conf_tmp[PATH_MAX]; const char *guid = param->guid; ret = ploop_di_remove_image(di, di->top_guid, 0, &old_top_delta_fname); if (ret) return ret; ploop_di_change_guid(di, guid, TOPDELTA_UUID); 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; if (rename(conf_tmp, conf)) { ploop_err(errno, "Can't rename %s %s", conf_tmp, conf); ret = SYSEXIT_RENAME; goto err; } /* 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)) ploop_err(errno, "Can't unlink %s", old_top_delta_fname); } err: if (ret && unlink(conf_tmp)) ploop_err(errno, "Can't unlink %s", conf_tmp); free(old_top_delta_fname); return ret; }
int ploop_balloon_complete(const char *device) { int fd, err; struct ploop_balloon_ctl b_ctl; fd = open_device(device); if (fd == -1) return SYSEXIT_OPEN; err = ioctl(fd, PLOOP_IOC_DISCARD_FINI); if (err && errno != EBUSY) { ploop_err(errno, "Can't finalize discard mode"); err = SYSEXIT_DEVIOC; goto out; } memset(&b_ctl, 0, sizeof(b_ctl)); b_ctl.keep_intact = 1; err = ioctl_device(fd, PLOOP_IOC_BALLOON, &b_ctl); if (err) goto out; switch (b_ctl.mntn_type) { case PLOOP_MNTN_BALLOON: case PLOOP_MNTN_MERGE: case PLOOP_MNTN_GROW: case PLOOP_MNTN_TRACK: case PLOOP_MNTN_OFF: ploop_log(0, "Nothing to complete: kernel is in \"%s\" state", mntn2str(b_ctl.mntn_type)); goto out; case PLOOP_MNTN_RELOC: case PLOOP_MNTN_FBLOADED: break; default: ploop_err(0, "Error: unknown mntn_type (%u)", b_ctl.mntn_type); err = SYSEXIT_PROTOCOL; goto out; } err = ploop_balloon_relocation(fd, &b_ctl, device); out: close(fd); return err; }
static int blkpg_resize_partition(int fd, struct GptEntry *pe) { struct blkpg_ioctl_arg ioctl_arg; struct blkpg_partition part; bzero(&part, sizeof(part)); part.pno = 1; part.start = S2B(pe->starting_lba); part.length = S2B(pe->ending_lba - pe->starting_lba + 1); ploop_log(3, "update partition table start=%llu length=%llu", part.start, part.length); ioctl_arg.op = BLKPG_RESIZE_PARTITION; ioctl_arg.flags = 0; ioctl_arg.datalen = sizeof(struct blkpg_partition); ioctl_arg.data = ∂ return ioctl_device(fd, BLKPG, &ioctl_arg); }
static int freeze_fs(struct ploop_copy_handle *h) { int ret; if (h->mntfd != -1) { /* Sync fs */ ploop_log(0, "Freezing fs..."); if (sys_syncfs(h->mntfd)) { ploop_err(errno, "syncfs() failed"); return SYSEXIT_FSYNC; } /* Flush journal and freeze fs (this also clears the fs dirty bit) */ ploop_dbg(3, "FIFREEZE"); ret = ioctl_device(h->mntfd, FIFREEZE, 0); if (ret) return ret; h->fs_frozen = 1; } return 0; }
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_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; __u64 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; if (pos > h->eof_offset) h->eof_offset = pos; } } 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; }
static int do_inflate(int fd, int mntn_type, off_t old_size, off_t *new_size, int *drop_state) { struct stat st; int err; *drop_state = 0; switch (mntn_type) { case PLOOP_MNTN_BALLOON: break; case PLOOP_MNTN_MERGE: case PLOOP_MNTN_GROW: case PLOOP_MNTN_TRACK: ploop_err(0, "Can't inflate hidden balloon while another " "maintenance operation is in progress (%s)", mntn2str(mntn_type)); return(SYSEXIT_EBUSY); case PLOOP_MNTN_FBLOADED: case PLOOP_MNTN_RELOC: ploop_err(0, "Can't inflate hidden balloon before previous " "balloon operation (%s) is completed. Use " "\"ploop-balloon complete\".", mntn2str(mntn_type)); return(SYSEXIT_EBUSY); case PLOOP_MNTN_OFF: ploop_err(0, "Error: mntn_type is PLOOP_MNTN_OFF after " "IOC_BALLOON"); return(SYSEXIT_PROTOCOL); default: ploop_err(0, "Error: unknown mntn_type (%u)", mntn_type); return(SYSEXIT_PROTOCOL); } err = sys_fallocate(fd, 0, 0, *new_size); if (err) ploop_err(errno, "Can't fallocate balloon"); if (fstat(fd, &st)) { ploop_err(errno, "Can't stat balloon (2)"); if (ftruncate(fd, old_size)) ploop_err(errno, "Can't revert old_size back"); return(err ? SYSEXIT_FALLOCATE : SYSEXIT_FSTAT); } if (err) { if (st.st_size != old_size) { if (ftruncate(fd, old_size)) ploop_err(errno, "Can't revert old_size back (2)"); else *drop_state = 1; } return(SYSEXIT_FALLOCATE); } if (st.st_size < *new_size) { ploop_err(0, "Error: after fallocate(%d, 0, 0, %llu) fstat " "reported size == %llu", fd, (unsigned long long)*new_size, (unsigned long long)st.st_size); if (ftruncate(fd, old_size)) ploop_err(errno, "Can't revert old_size back (3)"); else *drop_state = 1; return(SYSEXIT_FALLOCATE); } *new_size = st.st_size; err = fsync_balloon(fd); if (err) return err; ploop_log(0, "Successfully inflated balloon from %llu to %llu bytes", (unsigned long long)old_size, (unsigned long long)*new_size); return 0; }
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_balloon_change_size(const char *device, int balloonfd, off_t new_size) { int fd = -1; int ret; off_t old_size; __u32 dev_start; /* /sys/block/ploop0/ploop0p1/start */ __u32 n_free_blocks; __u32 freezed_a_h; struct ploop_balloon_ctl b_ctl; struct stat st; struct pfiemap *pfiemap = NULL; struct freemap *freemap = NULL; struct freemap *rangemap = NULL; struct relocmap *relocmap = NULL; struct ploop_freeblks_ctl *freeblks = NULL; struct ploop_relocblks_ctl *relocblks = NULL; __u32 *reverse_map = NULL; __u32 reverse_map_len; int top_level; struct delta delta = { .fd = -1 }; int entries_used; int drop_state = 0; if (fstat(balloonfd, &st)) { ploop_err(errno, "Can't get balloon file size"); return SYSEXIT_FSTAT; } old_size = st.st_size; new_size = (S2B(new_size) + st.st_blksize - 1) & ~(st.st_blksize - 1); ploop_log(0, "Changing balloon size old_size=%ld new_size=%ld", (long)old_size, (long)new_size); pfiemap = fiemap_alloc(128); freemap = freemap_alloc(128); rangemap = freemap_alloc(128); relocmap = relocmap_alloc(128); if (!pfiemap || !freemap || !rangemap || !relocmap) { ret = SYSEXIT_MALLOC; goto err; } fd = open_device(device); if (fd == -1) { ret = SYSEXIT_OPEN; goto err; } memset(&b_ctl, 0, sizeof(b_ctl)); if (old_size < new_size) b_ctl.inflate = 1; ret = ioctl_device(fd, PLOOP_IOC_BALLOON, &b_ctl); if (ret) goto err; drop_state = 1; if (old_size >= new_size) { ret = do_truncate(balloonfd, b_ctl.mntn_type, old_size, new_size); goto err; } if (dev_num2dev_start(device, st.st_dev, &dev_start)) { ploop_err(0, "Can't find out offset from start of ploop " "device (%s) to start of partition", device); ret = SYSEXIT_SYSFS; goto err; } ret = open_top_delta(device, &delta, &top_level); if (ret) goto err; ret = do_inflate(balloonfd, b_ctl.mntn_type, old_size, &new_size, &drop_state); if (ret) goto err; reverse_map_len = delta.l2_size + delta.l2_size; reverse_map = alloc_reverse_map(reverse_map_len); if (reverse_map == NULL) { ret = SYSEXIT_MALLOC; goto err; } ret = fiemap_get(balloonfd, S2B(dev_start), old_size, new_size, &pfiemap); if (ret) goto err; fiemap_adjust(pfiemap, delta.blocksize); ret = fiemap_build_rmap(pfiemap, reverse_map, reverse_map_len, &delta); if (ret) goto err; ret = rmap2freemap(reverse_map, 0, reverse_map_len, &freemap, &entries_used); if (ret) goto err; if (entries_used == 0) { drop_state = 1; ploop_log(0, "No unused cluster blocks found"); goto out; } ret = freemap2freeblks(freemap, top_level, &freeblks, &n_free_blocks); if (ret) goto err; ret = ioctl_device(fd, PLOOP_IOC_FREEBLKS, freeblks); if (ret) goto err; freezed_a_h = freeblks->alloc_head; if (freezed_a_h > reverse_map_len) { ploop_err(0, "Image corrupted: a_h=%u > rlen=%u", freezed_a_h, reverse_map_len); ret = SYSEXIT_PLOOPFMT; goto err; } ret = range_build(freezed_a_h, n_free_blocks, reverse_map, reverse_map_len, &delta, freemap, &rangemap, &relocmap); if (ret) goto err; ret = relocmap2relocblks(relocmap, top_level, freezed_a_h, n_free_blocks, &relocblks); if (ret) goto err; ret = ioctl_device(fd, PLOOP_IOC_RELOCBLKS, relocblks); if (ret) goto err; ploop_log(0, "TRUNCATED: %u cluster-blocks (%llu bytes)", relocblks->alloc_head, (unsigned long long)(relocblks->alloc_head * S2B(delta.blocksize))); out: ret = 0; err: if (drop_state) { memset(&b_ctl, 0, sizeof(b_ctl)); (void)ioctl_device(fd, PLOOP_IOC_BALLOON, &b_ctl); } if (fd != -1) close(fd); free(pfiemap); free(freemap); free(rangemap); free(relocmap); free(reverse_map); free(freeblks); free(relocblks); if (delta.fd != -1) close_delta(&delta); return ret; } int ploop_balloon_get_state(const char *device, __u32 *state) { int fd, ret; struct ploop_balloon_ctl b_ctl; fd = open_device(device); if (fd == -1) return SYSEXIT_OPEN; bzero(&b_ctl, sizeof(b_ctl)); b_ctl.keep_intact = 1; ret = ioctl_device(fd, PLOOP_IOC_BALLOON, &b_ctl); if (ret) goto err; *state = b_ctl.mntn_type; err: close(fd); return ret; }
static int __ploop_discard(struct ploop_disk_images_data *di, int fd, const char *device, const char *mount_point, __u64 minlen_b, __u32 cluster, __u32 to_free, __u64 blk_discard_range[2], const int *stop) { pid_t tpid; int err = 0, ret, status; __u32 size = 0; struct ploop_cleanup_hook *h; if (blk_discard_range != NULL) ploop_log(0, "Discard %s start=%" PRIu64 " length=%" PRIu64, device, (uint64_t)blk_discard_range[0], (uint64_t)blk_discard_range[1]); else ploop_log(3, "Trying to find free extents bigger than %" PRIu64 " bytes", (uint64_t)minlen_b); if (ploop_lock_di(di)) return SYSEXIT_LOCK; ret = ioctl_device(fd, PLOOP_IOC_DISCARD_INIT, NULL); ploop_unlock_di(di); if (ret) { ploop_err(errno, "Can't initialize discard mode"); return ret; } tpid = fork(); if (tpid < 0) { ploop_err(errno, "Can't fork"); ret = ioctl_device(fd, PLOOP_IOC_DISCARD_FINI, NULL); if (ret) { ploop_err(errno, "Can't finalize discard mode"); return ret; } } h = register_cleanup_hook(cancel_discard, (void *) device); if (tpid == 0) { if (blk_discard_range != NULL) ret = blk_discard(fd, cluster, blk_discard_range[0], blk_discard_range[1]); else ret = ploop_trim(mount_point, minlen_b, cluster); if (ioctl_device(fd, PLOOP_IOC_DISCARD_FINI, NULL)) ploop_err(errno, "Can't finalize discard mode"); exit(ret != 0); } while (1) { struct ploop_balloon_ctl b_ctl; ploop_log(3, "Waiting"); ret = ioctl(fd, PLOOP_IOC_DISCARD_WAIT, NULL); if (ret < 0) { ploop_err(errno, "Waiting for a discard request failed"); break; } else if (ret == 0) break; /* FIXME PLOOP_IOC_DISCARD_WAIT should return size */ ret = ioctl(fd, PLOOP_IOC_FBFILTER, 0); if (ret < 0) { ploop_err(errno, "Can't filter free blocks"); break; } else if (ret == 0) { /* Nothing to do */ ret = ioctl_device(fd, PLOOP_IOC_FBDROP, 0); if (ret) break; continue; } else size += ret; /* serialize ploop operations vs ploop_complete_running_operation() * NB: PLOOP_IOC_BALLOON may change mntn from PLOOP_MNTN_DISCARD: * to PLOOP_MNTN_FBLOADED */ if (ploop_lock_di(di)) { ret = SYSEXIT_LOCK; break; } memset(&b_ctl, 0, sizeof(b_ctl)); b_ctl.keep_intact = 1; ret = ioctl_device(fd, PLOOP_IOC_BALLOON, &b_ctl); if (ret) { ploop_unlock_di(di); break; } if (b_ctl.mntn_type == PLOOP_MNTN_OFF) { ploop_log(0, "Unexpected maintenance type 0x%x", b_ctl.mntn_type); ret = -1; ploop_unlock_di(di); break; } if (size >= to_free || (stop && *stop)) { ploop_log(3, "Killing the trim process %d", tpid); kill(tpid, SIGUSR1); ret = ioctl(fd, PLOOP_IOC_DISCARD_FINI); if (ret < 0 && errno != EBUSY) ploop_err(errno, "Can't finalize a discard mode"); } ploop_log(0, "Starting relocation"); ret = ploop_balloon_relocation(fd, &b_ctl, device); ploop_unlock_di(di); if (ret) break; } if (ret) { err = -1; ret = ioctl(fd, PLOOP_IOC_DISCARD_FINI); if (ret < 0) { if (errno == EBUSY) ploop_log(-1, "Discard finalized, but " "relocation is still not completed"); else ploop_err(errno, "Can't finalize discard mode"); } kill(tpid, SIGKILL); } else { ploop_log(0, "%d clusters have been relocated", size); } unregister_cleanup_hook(h); while ((ret = waitpid(tpid, &status, 0))) if (errno != EINTR) break; if (ret == -1) { if (errno != ECHILD) ploop_err(errno, "wait() failed"); err = -1; } else if (WIFEXITED(status)) { ret = WEXITSTATUS(status); if (ret) { ploop_err(0, "The trim process failed with code %d", ret); err = -1; } } else if (WIFSIGNALED(status)) { ploop_err(0, "The trim process killed by signal %d", WTERMSIG(status)); err = -1; } else { ploop_err(0, "The trim process died abnormally"); err = -1; } return err; }
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; }
/* 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 */ static 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)) 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_find_child_by_guid(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, 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; }
static int do_create_snapshot(struct ploop_disk_images_data *di, const char *guid, const char *snap_dir, 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; 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); 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); 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_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; } ploop_log(3, "merge snapshot %s -> %s", snapshot->guid, snapshot->parent_guid); /* 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; }
static int ploop_balloon_relocation(int fd, struct ploop_balloon_ctl *b_ctl, const char *device) { int ret = -1; __u32 n_free_blocks = 0; __u32 freezed_a_h; struct freemap *freemap = NULL; struct freemap *rangemap = NULL; struct relocmap *relocmap = NULL; struct ploop_freeblks_ctl *freeblks = NULL; struct ploop_relocblks_ctl *relocblks = NULL;; __u32 *reverse_map = NULL; __u32 reverse_map_len; int top_level; struct delta delta = {}; freemap = freemap_alloc(128); rangemap = freemap_alloc(128); relocmap = relocmap_alloc(128); if (freemap == NULL || rangemap == NULL || relocmap == NULL) { ret = SYSEXIT_MALLOC; goto err; } top_level = b_ctl->level; freezed_a_h = b_ctl->alloc_head; if (b_ctl->mntn_type == PLOOP_MNTN_RELOC) goto reloc; if (b_ctl->mntn_type != PLOOP_MNTN_FBLOADED) { ploop_err(0, "Error: non-suitable mntn_type (%u)", b_ctl->mntn_type); ret = SYSEXIT_PROTOCOL; goto err; } ret = freeblks_alloc(&freeblks, 0); if (ret) goto err; ret = ioctl_device(fd, PLOOP_IOC_FBGET, freeblks); if (ret) goto err; if (freeblks->n_extents == 0) goto reloc; ret = freeblks_alloc(&freeblks, freeblks->n_extents); if (ret) goto err; ret = ioctl_device(fd, PLOOP_IOC_FBGET, freeblks); if (ret) goto err; ret = freeblks2freemap(freeblks, &freemap, &n_free_blocks); if (ret) goto err; ret = open_top_delta(device, &delta, &top_level); if (ret) goto err; reverse_map_len = delta.l2_size + delta.l2_size; reverse_map = alloc_reverse_map(reverse_map_len); if (reverse_map == NULL) { close_delta(&delta); ret = SYSEXIT_MALLOC; goto err; } ret = range_build(freezed_a_h, n_free_blocks, reverse_map, reverse_map_len, &delta, freemap, &rangemap, &relocmap); close_delta(&delta); if (ret) goto err; reloc: ret = relocmap2relocblks(relocmap, top_level, freezed_a_h, n_free_blocks, &relocblks); if (ret) goto err; ret = ioctl_device(fd, PLOOP_IOC_RELOCBLKS, relocblks); if (ret) goto err; ploop_log(0, "TRUNCATED: %u cluster-blocks (%llu bytes)", relocblks->alloc_head, (unsigned long long)(relocblks->alloc_head * S2B(delta.blocksize))); err: free(freemap); free(rangemap); free(relocmap); free(reverse_map); free(freeblks); free(relocblks); 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|O_TRUNC, 0600); if (ofd < 0) { ploop_err(errno, "Can't open %s", arg->file); return SYSEXIT_CREAT; } ploop_dbg(3, "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); ret = 0; switch (desc.type) { case PCOPY_PKT_DATA: case PCOPY_PKT_DATA_ASYNC: { n = TEMP_FAILURE_RETRY(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; } break; } default: ploop_err(0, "ploop_copy_receiver: unsupported command type%d", desc.type); ret = SYSEXIT_PARAM; break; } /* send reply */ if (desc.type != PCOPY_PKT_DATA_ASYNC && 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(3, "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; }
int ploop_copy_stop(struct ploop_copy_handle *h, struct ploop_copy_stat *stat) { int ret; int iter; ploop_log(3, "pcopy last"); ret = freeze(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; } } if (!h->raw) { /* Must clear dirty flag on ploop1 image. */ 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(3, "Update header"); ret = send_buf(h, vh, 4096, 0); if (ret) goto err; ret = send_optional_header(h); 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(3, "SEND 0 0 (close)"); send_async(h, NULL, 0, 0); pthread_join(h->send_th, NULL); h->send_th = 0; ploop_dbg(3, "pcopy stop done"); err: ploop_copy_release(h); return ret; }
int ploop_balloon_check_and_repair(const char *device, const char *mount_point, int repair) { int ret, fd = -1; int balloonfd = -1; __u32 n_free_blocks; __u32 freezed_a_h; __u32 dev_start; /* /sys/block/ploop0/ploop0p1/start */ struct ploop_balloon_ctl b_ctl; struct stat st; struct pfiemap *pfiemap = NULL; struct freemap *freemap = NULL; struct freemap *rangemap = NULL; struct relocmap *relocmap = NULL; struct ploop_freeblks_ctl *freeblks = NULL; struct ploop_relocblks_ctl *relocblks= NULL; char *msg = repair ? "repair" : "check"; __u32 *reverse_map = NULL; __u32 reverse_map_len; int top_level; int entries_used; struct delta delta = {}; int drop_state = 0; ret = get_balloon(mount_point, &st, &balloonfd); if (ret) return ret; if (st.st_size == 0) { ploop_log(0, "Nothing to do: hidden balloon is empty"); close(balloonfd); return 0; } pfiemap = fiemap_alloc(128); freemap = freemap_alloc(128); rangemap = freemap_alloc(128); relocmap = relocmap_alloc(128); if (!pfiemap || !freemap || !rangemap || !relocmap) { ret = SYSEXIT_MALLOC; goto err; } fd = open_device(device); if (fd == -1) { ret = SYSEXIT_OPEN; goto err; } memset(&b_ctl, 0, sizeof(b_ctl)); /* block other maintenance ops even if we only check balloon */ b_ctl.inflate = 1; ret = ioctl_device(fd, PLOOP_IOC_BALLOON, &b_ctl); if (ret) goto err; switch (b_ctl.mntn_type) { case PLOOP_MNTN_BALLOON: drop_state = 1; ret = open_top_delta(device, &delta, &top_level); if (ret) goto err; reverse_map_len = delta.l2_size + delta.l2_size; reverse_map = alloc_reverse_map(reverse_map_len); if (reverse_map == NULL) { ret = SYSEXIT_MALLOC; goto err; } break; case PLOOP_MNTN_MERGE: case PLOOP_MNTN_GROW: case PLOOP_MNTN_TRACK: ploop_err(0, "Can't %s hidden balloon while another " "maintenance operation is in progress (%s)", msg, mntn2str(b_ctl.mntn_type)); ret = SYSEXIT_EBUSY; goto err; case PLOOP_MNTN_FBLOADED: case PLOOP_MNTN_RELOC: ploop_err(0, "Can't %s hidden balloon before previous " "balloon operation (%s) is completed. Use " "\"ploop-balloon complete\".", msg, mntn2str(b_ctl.mntn_type)); ret = SYSEXIT_EBUSY; goto err; case PLOOP_MNTN_OFF: ploop_err(0, "Error: mntn_type is PLOOP_MNTN_OFF after " "IOC_BALLOON"); ret = SYSEXIT_PROTOCOL; goto err; default: ploop_err(0, "Error: unknown mntn_type (%u)", b_ctl.mntn_type); ret = SYSEXIT_PROTOCOL; goto err; } if (dev_num2dev_start(device, st.st_dev, &dev_start)) { ploop_err(0, "Can't find out offset from start of ploop " "device (%s) to start of partition where fs (%s) " "resides", device, mount_point); ret = SYSEXIT_SYSFS; goto err; } ret = fiemap_get(balloonfd, S2B(dev_start), 0, st.st_size, &pfiemap); if (ret) goto err; fiemap_adjust(pfiemap, delta.blocksize); ret = fiemap_build_rmap(pfiemap, reverse_map, reverse_map_len, &delta); if (ret) goto err; ret = rmap2freemap(reverse_map, 0, reverse_map_len, &freemap, &entries_used); if (ret) goto err; if (entries_used == 0) { ploop_log(0, "No free blocks found"); goto err; } ret = freemap2freeblks(freemap, top_level, &freeblks, &n_free_blocks); if (ret) goto err; if (!repair) { ploop_log(0, "Found %u free blocks. Consider using " "\"ploop-balloon repair\"", n_free_blocks); ret = 0; goto err; } else { ploop_log(0, "Found %u free blocks", n_free_blocks); } ret = ioctl_device(fd, PLOOP_IOC_FREEBLKS, freeblks); if (ret) goto err; drop_state = 0; freezed_a_h = freeblks->alloc_head; if (freezed_a_h > reverse_map_len) { ploop_err(0, "Image corrupted: a_h=%u > rlen=%u", freezed_a_h, reverse_map_len); ret = SYSEXIT_PLOOPFMT; goto err; } ret = range_build(freezed_a_h, n_free_blocks, reverse_map, reverse_map_len, &delta, freemap, &rangemap, &relocmap); if (ret) goto err; ret = relocmap2relocblks(relocmap, top_level, freezed_a_h, n_free_blocks, &relocblks); if (ret) goto err; ret = ioctl_device(fd, PLOOP_IOC_RELOCBLKS, relocblks); if (ret) goto err; ploop_log(0, "TRUNCATED: %u cluster-blocks (%llu bytes)", relocblks->alloc_head, (unsigned long long)(relocblks->alloc_head * S2B(delta.blocksize))); err: if (drop_state) { memset(&b_ctl, 0, sizeof(b_ctl)); (void)ioctl_device(fd, PLOOP_IOC_BALLOON, &b_ctl); } // FIXME: close_delta() if (balloonfd >= 0) close(balloonfd); if (fd >= 0) close(fd); free(pfiemap); free(freemap); free(rangemap); free(relocmap); free(reverse_map); free(freeblks); free(relocblks); return ret; }