static int ploop_get_dev_and_mnt(struct ploop_disk_images_data *di, char *dev, int dev_len, char *mnt, int mnt_len) { if (ploop_lock_dd(di)) return SYSEXIT_LOCK; if (ploop_find_dev(di->runtime->component_name, di->images[0]->file, dev, dev_len)) { ploop_unlock_dd(di); return SYSEXIT_PARAM; } if (ploop_get_mnt_by_dev(dev, mnt, mnt_len)) { ploop_err(0, "Unable to find mount point for %s", dev); ploop_unlock_dd(di); return SYSEXIT_PARAM; } ploop_unlock_dd(di); return 0; }
int ploop_create_snapshot(struct ploop_disk_images_data *di, struct ploop_snapshot_param *param) { int ret; if (ploop_lock_dd(di)) return SYSEXIT_LOCK; ret = do_create_snapshot(di, param->guid, param->snap_dir, 0); ploop_unlock_dd(di); return ret; }
int ploop_delete_snapshot(struct ploop_disk_images_data *di, const char *guid) { int ret; if (ploop_lock_dd(di)) return SYSEXIT_LOCK; ret = do_delete_snapshot(di, guid); if (ret == 0) merge_temporary_snapshots(di); ploop_unlock_dd(di); return ret; }
int ploop_create_snapshot_offline(struct ploop_disk_images_data *di, struct ploop_snapshot_param *param) { int ret; if (ploop_lock_dd(di)) return SYSEXIT_LOCK; ret = do_create_snapshot(di, param->guid, param->snap_dir, param->cbt_uuid, SNAP_TYPE_OFFLINE); ploop_unlock_dd(di); return ret; }
int ploop_discard(struct ploop_disk_images_data *di, struct ploop_discard_param *param) { int ret; char dev[PATH_MAX]; char mnt[PATH_MAX]; int mounted = 0; if (ploop_lock_dd(di)) return SYSEXIT_LOCK; ret = ploop_find_dev(di->runtime->component_name, di->images[0]->file, dev, sizeof(dev)); if (ret == -1) { ploop_unlock_dd(di); return SYSEXIT_LOCK; } else if (ret == 0) { if (ploop_get_mnt_by_dev(dev, mnt, sizeof(mnt))) { ploop_err(0, "Unable to find mount point for %s", dev); ploop_unlock_dd(di); return SYSEXIT_PARAM; } } else { struct ploop_mount_param mount_param = {}; if (!param->automount) { ploop_err(0, "Unable to discard: image is not mounted"); ploop_unlock_dd(di); return SYSEXIT_PARAM; } ret = auto_mount_image(di, &mount_param); if (ret) { ploop_unlock_dd(di); return ret; } mounted = 1; snprintf(dev, sizeof(dev), "%s", mount_param.device); snprintf(mnt, sizeof(mnt), "%s", mount_param.target); free_mount_param(&mount_param); } ploop_unlock_dd(di); ret = do_ploop_discard(di, dev, mnt, param->minlen_b, param->to_free, param->stop); if (mounted && ploop_lock_dd(di) == 0) { ploop_umount(dev, di); ploop_unlock_dd(di); } 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]; 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_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 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, 1); 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 1; /* 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; }