]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/storage/zfs.c
tree-wide: fix includes to fix bionic builds
[mirror_lxc.git] / src / lxc / storage / zfs.c
CommitLineData
8907f444
CB
1/*
2 * lxc: linux Container library
3 *
4 * (C) Copyright IBM Corp. 2007, 2008
5 *
6 * Authors:
7 * Daniel Lezcano <daniel.lezcano at free.fr>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
d38dd64a
CB
24#ifndef _GNU_SOURCE
25#define _GNU_SOURCE 1
26#endif
3ef1df7c 27#include <errno.h>
8907f444 28#include <stdint.h>
3d2ae1e2 29#include <stdio.h>
8907f444
CB
30#include <stdlib.h>
31#include <string.h>
32#include <sys/mount.h>
3d2ae1e2 33#include <unistd.h>
8907f444 34
8907f444
CB
35#include "config.h"
36#include "log.h"
3ef1df7c 37#include "parse.h"
28d832c4
CB
38#include "rsync.h"
39#include "storage.h"
8907f444 40#include "utils.h"
28d832c4 41#include "zfs.h"
8907f444 42
10bc1861 43lxc_log_define(zfs, lxc);
8907f444 44
3ef1df7c
CB
45struct zfs_args {
46 const char *dataset;
47 const char *snapshot;
48 const char *options;
49 void *argv;
50};
51
52int zfs_detect_exec_wrapper(void *data)
53{
54 struct zfs_args *args = data;
55
56 execlp("zfs", "zfs", "get", "type", "-H", "-o", "name", args->dataset,
57 (char *)NULL);
58
59 return -1;
60}
61
62int zfs_create_exec_wrapper(void *args)
63{
64 struct zfs_args *zfs_args = args;
65
66 execvp("zfs", zfs_args->argv);
67
68 return -1;
69}
70
71int zfs_delete_exec_wrapper(void *args)
72{
73 struct zfs_args *zfs_args = args;
74
75 execlp("zfs", "zfs", "destroy", "-r", zfs_args->dataset, (char *)NULL);
76
77 return -1;
78}
79
80int zfs_snapshot_exec_wrapper(void *args)
81{
82 struct zfs_args *zfs_args = args;
83
84 execlp("zfs", "zfs", "snapshot", "-r", zfs_args->snapshot, (char *)NULL);
85
86 return -1;
87}
88
89int zfs_clone_exec_wrapper(void *args)
90{
91 struct zfs_args *zfs_args = args;
92
93 execlp("zfs", "zfs", "clone", "-p", "-o", "canmount=noauto", "-o",
94 zfs_args->options, zfs_args->snapshot, zfs_args->dataset,
95 (char *)NULL);
96
97 return -1;
98}
99
100int zfs_get_parent_snapshot_exec_wrapper(void *args)
101{
102 struct zfs_args *zfs_args = args;
103
104 execlp("zfs", "zfs", "get", "origin", "-o", "value", "-H",
105 zfs_args->dataset, (char *)NULL);
106
107 return -1;
108}
8907f444 109
3d2ae1e2 110static bool zfs_list_entry(const char *path, char *output, size_t inlen)
8907f444
CB
111{
112 struct lxc_popen_FILE *f;
3d2ae1e2 113 bool found = false;
8907f444
CB
114
115 f = lxc_popen("zfs list 2> /dev/null");
116 if (f == NULL) {
117 SYSERROR("popen failed");
3d2ae1e2 118 return false;
8907f444
CB
119 }
120
121 while (fgets(output, inlen, f->f)) {
122 if (strstr(output, path)) {
3d2ae1e2 123 found = true;
8907f444
CB
124 break;
125 }
126 }
3d2ae1e2 127 (void)lxc_pclose(f);
8907f444
CB
128
129 return found;
130}
131
3d2ae1e2 132bool zfs_detect(const char *path)
8907f444 133{
3ef1df7c
CB
134 int ret;
135 char *dataset;
136 struct zfs_args cmd_args = {0};
137 char cmd_output[MAXPATHLEN] = {0};
138
f7ac4459 139 if (!strncmp(path, "zfs:", 4))
3d2ae1e2 140 return true;
f7ac4459 141
3ef1df7c
CB
142 /* This is a legacy zfs setup where the rootfs path
143 * "<lxcpath>/<lxcname>/rootfs" is given.
144 */
145 if (*path == '/') {
146 bool found;
147 char *output = malloc(LXC_LOG_BUFFER_SIZE);
148
149 if (!output) {
150 ERROR("out of memory");
151 return false;
152 }
153
154 found = zfs_list_entry(path, output, LXC_LOG_BUFFER_SIZE);
155 free(output);
156 return found;
157 }
8907f444 158
3ef1df7c
CB
159 cmd_args.dataset = path;
160 ret = run_command(cmd_output, sizeof(cmd_output),
161 zfs_detect_exec_wrapper, (void *)&cmd_args);
162 if (ret < 0) {
163 ERROR("Failed to detect zfs dataset \"%s\": %s", path, cmd_output);
3d2ae1e2 164 return false;
8907f444
CB
165 }
166
3ef1df7c
CB
167 if (cmd_output[0] == '\0')
168 return false;
8907f444 169
3ef1df7c
CB
170 /* remove any possible leading and trailing whitespace */
171 dataset = cmd_output;
172 dataset += lxc_char_left_gc(dataset, strlen(dataset));
173 dataset[lxc_char_right_gc(dataset, strlen(dataset))] = '\0';
174
175 if (strcmp(dataset, path))
176 return false;
177
178 return true;
8907f444
CB
179}
180
10bc1861 181int zfs_mount(struct lxc_storage *bdev)
8907f444 182{
db46c213 183 int ret;
3ef1df7c 184 size_t oldlen, newlen, totallen;
41dc7155
CB
185 char *mntdata, *tmp;
186 const char *src;
db46c213 187 unsigned long mntflags;
3ef1df7c 188 char cmd_output[MAXPATHLEN] = {0};
db46c213 189
8907f444
CB
190 if (strcmp(bdev->type, "zfs"))
191 return -22;
192
193 if (!bdev->src || !bdev->dest)
194 return -22;
195
3ef1df7c
CB
196 ret = parse_mntopts(bdev->mntopts, &mntflags, &mntdata);
197 if (ret < 0) {
198 ERROR("Failed to parse mount options");
8907f444
CB
199 free(mntdata);
200 return -22;
201 }
202
3ef1df7c
CB
203 /* This is a legacy zfs setup where the rootfs path
204 * "<lxcpath>/<lxcname>/rootfs" is given and we do a bind-mount.
205 */
db46c213 206 src = lxc_storage_get_path(bdev->src, bdev->type);
3ef1df7c
CB
207 if (*src == '/') {
208 bool found;
209
210 found = zfs_list_entry(src, cmd_output, sizeof(cmd_output));
211 if (!found) {
212 ERROR("Failed to find zfs entry \"%s\"", src);
213 return -1;
214 }
215
216 tmp = strchr(cmd_output, ' ');
217 if (!tmp) {
218 ERROR("Failed to detect zfs dataset associated with "
219 "\"%s\"", src);
220 return -1;
221 }
222 *tmp = '\0';
223 src = cmd_output;
224 }
225
226 /* ','
227 * +
228 * strlen("zfsutil")
229 * +
230 * ','
231 * +
232 * strlen(mntpoint=)
233 * +
234 * strlen(src)
235 * +
236 * '\0'
237 */
238 newlen = 1 + 7 + 1 + 9 + strlen(src) + 1;
239 oldlen = mntdata ? strlen(mntdata) : 0;
240 totallen = (newlen + oldlen);
241 tmp = realloc(mntdata, totallen);
242 if (!tmp) {
243 ERROR("Failed to reallocate memory");
244 free(mntdata);
245 return -1;
246 }
247 mntdata = tmp;
248
249 ret = snprintf((mntdata + oldlen), newlen, ",zfsutil,mntpoint=%s", src);
250 if (ret < 0 || (size_t)ret >= newlen) {
251 ERROR("Failed to create string");
252 free(mntdata);
253 return -1;
254 }
255
256 ret = mount(src, bdev->dest, "zfs", mntflags, mntdata);
8907f444 257 free(mntdata);
3ef1df7c
CB
258 if (ret < 0 && errno != EBUSY) {
259 SYSERROR("Failed to mount \"%s\" on \"%s\"", src, bdev->dest);
260 return -1;
261 }
8907f444 262
3ef1df7c
CB
263 TRACE("Mounted \"%s\" on \"%s\"", src, bdev->dest);
264 return 0;
8907f444
CB
265}
266
10bc1861 267int zfs_umount(struct lxc_storage *bdev)
8907f444 268{
3ef1df7c
CB
269 int ret;
270
8907f444
CB
271 if (strcmp(bdev->type, "zfs"))
272 return -22;
273
274 if (!bdev->src || !bdev->dest)
275 return -22;
276
3ef1df7c
CB
277 ret = umount(bdev->dest);
278 if (ret < 0)
279 SYSERROR("Failed to unmount \"%s\"", bdev->dest);
280 else
281 TRACE("Unmounted \"%s\"", bdev->dest);
282
283 return ret;
8907f444
CB
284}
285
10bc1861
CB
286bool zfs_copy(struct lxc_conf *conf, struct lxc_storage *orig,
287 struct lxc_storage *new, uint64_t newsize)
8907f444 288{
8907f444 289 int ret;
3ef1df7c
CB
290 char cmd_output[MAXPATHLEN], option[MAXPATHLEN];
291 struct rsync_data data = {0, 0};
292 struct zfs_args cmd_args = {0};
41dc7155
CB
293 const char *argv[] = {"zfs", /* 0 */
294 "create", /* 1 */
295 "-o", "", /* 2, 3 */
296 "-o", "canmount=noauto", /* 4, 5 */
297 "-p", /* 6 */
298 "", /* 7 */
299 NULL};
3ef1df7c
CB
300
301 /* mountpoint */
302 ret = snprintf(option, MAXPATHLEN, "mountpoint=%s", new->dest);
303 if (ret < 0 || ret >= MAXPATHLEN) {
304 ERROR("Failed to create string");
305 return false;
306 }
307 argv[3] = option;
308 argv[7] = lxc_storage_get_path(new->src, new->type);
8907f444 309
3ef1df7c
CB
310 cmd_args.argv = argv;
311 ret = run_command(cmd_output, sizeof(cmd_output),
312 zfs_create_exec_wrapper, (void *)&cmd_args);
313 if (ret < 0) {
314 ERROR("Failed to create zfs dataset \"%s\": %s", new->src, cmd_output);
315 return false;
316 } else if (cmd_output[0] != '\0') {
317 INFO("Created zfs dataset \"%s\": %s", new->src, cmd_output);
8907f444 318 } else {
3ef1df7c 319 TRACE("Created zfs dataset \"%s\"", new->src);
8907f444
CB
320 }
321
3ef1df7c
CB
322 ret = mkdir_p(new->dest, 0755);
323 if (ret < 0 && errno != EEXIST) {
324 SYSERROR("Failed to create directory \"%s\"", new->dest);
325 return false;
326 }
8907f444 327
3ef1df7c
CB
328 data.orig = orig;
329 data.new = new;
330 ret = run_command(cmd_output, sizeof(cmd_output),
17a367d8 331 lxc_storage_rsync_exec_wrapper, (void *)&data);
3ef1df7c
CB
332 if (ret < 0) {
333 ERROR("Failed to rsync from \"%s\" into \"%s\": %s", orig->dest,
334 new->dest, cmd_output);
335 return false;
336 }
337 TRACE("Rsynced from \"%s\" to \"%s\"", orig->dest, new->dest);
8907f444 338
3ef1df7c
CB
339 return true;
340}
8907f444 341
3ef1df7c 342/* create read-only snapshot and create a clone from it */
10bc1861
CB
343bool zfs_snapshot(struct lxc_conf *conf, struct lxc_storage *orig,
344 struct lxc_storage *new, uint64_t newsize)
3ef1df7c
CB
345{
346 int ret;
347 size_t snapshot_len, len;
41dc7155
CB
348 char *tmp, *snap_name, *snapshot;
349 const char *orig_src;
3ef1df7c
CB
350 struct zfs_args cmd_args = {0};
351 char cmd_output[MAXPATHLEN] = {0}, option[MAXPATHLEN];
352
353 orig_src = lxc_storage_get_path(orig->src, orig->type);
354 if (*orig_src == '/') {
355 bool found;
356
357 found = zfs_list_entry(orig_src, cmd_output, sizeof(cmd_output));
358 if (!found) {
359 ERROR("Failed to find zfs entry \"%s\"", orig_src);
360 return false;
8907f444 361 }
8907f444 362
3ef1df7c
CB
363 tmp = strchr(cmd_output, ' ');
364 if (!tmp) {
365 ERROR("Failed to detect zfs dataset associated with "
366 "\"%s\"", orig_src);
367 return false;
8907f444 368 }
3ef1df7c
CB
369 *tmp = '\0';
370 orig_src = cmd_output;
371 }
372
373 snapshot = strdup(orig_src);
374 if (!snapshot) {
375 ERROR("Failed to duplicate string \"%s\"", orig_src);
376 return false;
377 }
378
379 snap_name = strrchr(new->src, '/');
380 if (!snap_name) {
381 ERROR("Failed to detect \"/\" in \"%s\"", new->src);
382 free(snapshot);
383 return false;
384 }
385 snap_name++;
386
387 /* strlen(snapshot)
388 * +
389 * @
390 * +
391 * strlen(cname)
392 * +
393 * \0
394 */
395 snapshot_len = strlen(snapshot);
396 len = snapshot_len + 1 + strlen(snap_name) + 1;
397 tmp = realloc(snapshot, len);
398 if (!tmp) {
399 ERROR("Failed to reallocate memory");
400 free(snapshot);
401 return false;
402 }
403 snapshot = tmp;
404
405 len -= snapshot_len;
406 ret = snprintf(snapshot + snapshot_len, len, "@%s", snap_name);
407 if (ret < 0 || ret >= len) {
408 ERROR("Failed to create string");
409 free(snapshot);
410 return false;
8907f444 411 }
3ef1df7c
CB
412
413 cmd_args.snapshot = snapshot;
414 ret = run_command(cmd_output, sizeof(cmd_output),
415 zfs_snapshot_exec_wrapper, (void *)&cmd_args);
416 if (ret < 0) {
417 ERROR("Failed to create zfs snapshot \"%s\": %s", snapshot, cmd_output);
418 free(snapshot);
419 return false;
420 } else if (cmd_output[0] != '\0') {
421 INFO("Created zfs snapshot \"%s\": %s", snapshot, cmd_output);
422 } else {
423 TRACE("Created zfs snapshot \"%s\"", snapshot);
424 }
425
426 ret = snprintf(option, MAXPATHLEN, "mountpoint=%s", new->dest);
427 if (ret < 0 || ret >= MAXPATHLEN) {
428 ERROR("Failed to create string");
429 free(snapshot);
430 return -1;
431 }
432
433 cmd_args.dataset = lxc_storage_get_path(new->src, new->type);
434 cmd_args.snapshot = snapshot;
435 cmd_args.options = option;
436 ret = run_command(cmd_output, sizeof(cmd_output),
437 zfs_clone_exec_wrapper, (void *)&cmd_args);
438 if (ret < 0)
439 ERROR("Failed to create zfs dataset \"%s\": %s", new->src, cmd_output);
440 else if (cmd_output[0] != '\0')
441 INFO("Created zfs dataset \"%s\": %s", new->src, cmd_output);
442 else
443 TRACE("Created zfs dataset \"%s\"", new->src);
444
445 free(snapshot);
446 return true;
8907f444
CB
447}
448
10bc1861
CB
449int zfs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
450 const char *oldname, const char *cname, const char *oldpath,
451 const char *lxcpath, int snap, uint64_t newsize,
452 struct lxc_conf *conf)
8907f444 453{
3ef1df7c 454 int ret;
41dc7155
CB
455 char *dataset, *tmp;
456 const char *orig_src;
3ef1df7c
CB
457 size_t dataset_len, len;
458 char cmd_output[MAXPATHLEN] = {0};
8907f444
CB
459
460 if (!orig->src || !orig->dest)
461 return -1;
462
463 if (snap && strcmp(orig->type, "zfs")) {
3d2ae1e2
CB
464 ERROR("zfs snapshot from %s backing store is not supported",
465 orig->type);
8907f444
CB
466 return -1;
467 }
468
3ef1df7c
CB
469 orig_src = lxc_storage_get_path(orig->src, orig->type);
470 if (!strcmp(orig->type, "zfs")) {
471 size_t len;
472 if (*orig_src == '/') {
473 bool found;
474
475 found = zfs_list_entry(orig_src, cmd_output,
476 sizeof(cmd_output));
477 if (!found) {
478 ERROR("Failed to find zfs entry \"%s\"", orig_src);
479 return -1;
480 }
481
482 tmp = strchr(cmd_output, ' ');
483 if (!tmp) {
484 ERROR("Failed to detect zfs dataset associated "
485 "with \"%s\"", orig_src);
486 return -1;
487 }
488 *tmp = '\0';
489 orig_src = cmd_output;
490 }
491
492 tmp = strrchr(orig_src, '/');
493 if (!tmp) {
494 ERROR("Failed to detect \"/\" in \"%s\"", orig_src);
495 return -1;
496 }
497
498 len = tmp - orig_src;
499 dataset = strndup(orig_src, len);
500 if (!dataset) {
501 ERROR("Failed to duplicate string \"%zu\" "
502 "bytes of string \"%s\"", len, orig_src);
503 return -1;
504 }
505 } else {
506 tmp = (char *)lxc_global_config_value("lxc.bdev.zfs.root");
507 if (!tmp) {
508 ERROR("The \"lxc.bdev.zfs.root\" property is not set");
509 return -1;
510 }
511
512 dataset = strdup(tmp);
513 if (!dataset) {
514 ERROR("Failed to duplicate string \"%s\"", tmp);
515 return -1;
516 }
517 }
518
519 /* strlen("zfs:") = 4
520 * +
521 * strlen(dataset)
522 * +
523 * / = 1
524 * +
525 * strlen(cname)
526 * +
527 * \0
528 */
529 dataset_len = strlen(dataset);
530 len = 4 + dataset_len + 1 + strlen(cname) + 1;
531 new->src = realloc(dataset, len);
532 if (!new->src) {
533 ERROR("Failed to reallocate memory");
534 free(dataset);
535 return -1;
536 }
537 memmove(new->src + 4, new->src, dataset_len);
538 memmove(new->src, "zfs:", 4);
539
540 len -= dataset_len - 4;
541 ret = snprintf(new->src + dataset_len + 4, len, "/%s", cname);
542 if (ret < 0 || ret >= len) {
543 ERROR("Failed to create string");
8907f444 544 return -1;
3ef1df7c 545 }
8907f444 546
3ef1df7c
CB
547 /* strlen(lxcpath)
548 * +
549 * /
550 * +
551 * strlen(cname)
552 * +
553 * /
554 * +
555 * strlen("rootfs")
556 * +
557 * \0
558 */
559 len = strlen(lxcpath) + 1 + strlen(cname) + 1 + strlen("rootfs") + 1;
560 new->dest = malloc(len);
561 if (!new->dest) {
562 ERROR("Failed to allocate memory");
8907f444 563 return -1;
3ef1df7c 564 }
8907f444 565
3ef1df7c
CB
566 ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname);
567 if (ret < 0 || ret >= len) {
568 ERROR("Failed to create string \"%s/%s/rootfs\"", lxcpath, cname);
8907f444 569 return -1;
3ef1df7c 570 }
8907f444 571
3ef1df7c
CB
572 ret = mkdir_p(new->dest, 0755);
573 if (ret < 0 && errno != EEXIST) {
574 SYSERROR("Failed to create directory \"%s\"", new->dest);
575 return -1;
576 }
577
578 return 0;
8907f444
CB
579}
580
10bc1861 581int zfs_destroy(struct lxc_storage *orig)
8907f444 582{
3ef1df7c 583 int ret;
41dc7155
CB
584 char *dataset, *tmp;
585 const char *src;
3ef1df7c
CB
586 bool found;
587 char *parent_snapshot = NULL;
588 struct zfs_args cmd_args = {0};
589 char cmd_output[MAXPATHLEN] = {0};
590
591 src = lxc_storage_get_path(orig->src, orig->type);
592
593 /* This is a legacy zfs setup where the rootfs path
594 * "<lxcpath>/<lxcname>/rootfs" is given.
595 */
596 if (*src == '/') {
597 char *tmp;
598
599 found = zfs_list_entry(src, cmd_output, sizeof(cmd_output));
600 if (!found) {
601 ERROR("Failed to find zfs entry \"%s\"", orig->src);
602 return -1;
603 }
604
605 tmp = strchr(cmd_output, ' ');
606 if (!tmp) {
607 ERROR("Failed to detect zfs dataset associated with "
608 "\"%s\"", cmd_output);
609 return -1;
610 }
611 *tmp = '\0';
612 dataset = cmd_output;
613 } else {
614 cmd_args.dataset = src;
615 ret = run_command(cmd_output, sizeof(cmd_output),
616 zfs_detect_exec_wrapper, (void *)&cmd_args);
617 if (ret < 0) {
618 ERROR("Failed to detect zfs dataset \"%s\": %s", src,
619 cmd_output);
620 return -1;
621 }
622
623 if (cmd_output[0] == '\0') {
624 ERROR("Failed to detect zfs dataset \"%s\"", src);
625 return -1;
626 }
8907f444 627
3ef1df7c
CB
628 /* remove any possible leading and trailing whitespace */
629 dataset = cmd_output;
630 dataset += lxc_char_left_gc(dataset, strlen(dataset));
631 dataset[lxc_char_right_gc(dataset, strlen(dataset))] = '\0';
632
633 if (strcmp(dataset, src)) {
634 ERROR("Detected dataset \"%s\" does not match expected "
635 "dataset \"%s\"", dataset, src);
636 return -1;
637 }
638 }
639
640 cmd_args.dataset = strdup(dataset);
641 if (!cmd_args.dataset) {
642 ERROR("Failed to duplicate string \"%s\"", dataset);
8907f444 643 return -1;
3ef1df7c 644 }
8907f444 645
3ef1df7c
CB
646 ret = run_command(cmd_output, sizeof(cmd_output),
647 zfs_get_parent_snapshot_exec_wrapper,
648 (void *)&cmd_args);
649 if (ret < 0) {
650 ERROR("Failed to retrieve parent snapshot of zfs dataset "
651 "\"%s\": %s", dataset, cmd_output);
652 free((void *)cmd_args.dataset);
8907f444 653 return -1;
3ef1df7c
CB
654 } else {
655 INFO("Retrieved parent snapshot of zfs dataset \"%s\": %s", src,
656 cmd_output);
657 }
658
659 /* remove any possible leading and trailing whitespace */
660 tmp = cmd_output;
661 tmp += lxc_char_left_gc(tmp, strlen(tmp));
662 tmp[lxc_char_right_gc(tmp, strlen(tmp))] = '\0';
663
664 /* check whether the dataset has a parent snapshot */
665 if (*tmp != '-' && *(tmp + 1) != '\0') {
666 parent_snapshot = strdup(tmp);
667 if (!parent_snapshot) {
668 ERROR("Failed to duplicate string \"%s\"", tmp);
669 free((void *)cmd_args.dataset);
670 return -1;
671 }
8907f444
CB
672 }
673
3ef1df7c
CB
674 /* delete dataset */
675 ret = run_command(cmd_output, sizeof(cmd_output),
676 zfs_delete_exec_wrapper, (void *)&cmd_args);
677 if (ret < 0) {
678 ERROR("Failed to delete zfs dataset \"%s\": %s", dataset,
679 cmd_output);
680 free((void *)cmd_args.dataset);
681 free(parent_snapshot);
8907f444 682 return -1;
3ef1df7c
CB
683 } else if (cmd_output[0] != '\0') {
684 INFO("Deleted zfs dataset \"%s\": %s", src, cmd_output);
685 } else {
686 INFO("Deleted zfs dataset \"%s\"", src);
687 }
8907f444 688
3ef1df7c 689 free((void *)cmd_args.dataset);
8907f444 690
3ef1df7c
CB
691 /* Not a clone so nothing more to do. */
692 if (!parent_snapshot)
693 return 0;
db46c213 694
3ef1df7c
CB
695 /* delete parent snapshot */
696 cmd_args.dataset = parent_snapshot;
697 ret = run_command(cmd_output, sizeof(cmd_output),
698 zfs_delete_exec_wrapper, (void *)&cmd_args);
699 if (ret < 0)
700 ERROR("Failed to delete zfs snapshot \"%s\": %s", dataset, cmd_output);
701 else if (cmd_output[0] != '\0')
702 INFO("Deleted zfs snapshot \"%s\": %s", src, cmd_output);
703 else
704 INFO("Deleted zfs snapshot \"%s\"", src);
db46c213 705
3ef1df7c
CB
706 free((void *)cmd_args.dataset);
707 return ret;
db46c213
CB
708}
709
10bc1861 710int zfs_create(struct lxc_storage *bdev, const char *dest, const char *n,
3d2ae1e2 711 struct bdev_specs *specs)
8907f444
CB
712{
713 const char *zfsroot;
8907f444 714 int ret;
db46c213 715 size_t len;
3ef1df7c
CB
716 struct zfs_args cmd_args = {0};
717 char cmd_output[MAXPATHLEN], option[MAXPATHLEN];
41dc7155
CB
718 const char *argv[] = {"zfs", /* 0 */
719 "create", /* 1 */
720 "-o", "", /* 2, 3 */
721 "-o", "canmount=noauto", /* 4, 5 */
722 "-p", /* 6 */
723 "", /* 7 */
724 NULL};
8907f444
CB
725
726 if (!specs || !specs->zfs.zfsroot)
727 zfsroot = lxc_global_config_value("lxc.bdev.zfs.root");
728 else
729 zfsroot = specs->zfs.zfsroot;
730
db46c213
CB
731 bdev->dest = strdup(dest);
732 if (!bdev->dest) {
3ef1df7c 733 ERROR("Failed to duplicate string \"%s\"", dest);
8907f444
CB
734 return -1;
735 }
db46c213 736
3ef1df7c 737 len = strlen(zfsroot) + 1 + strlen(n) + 1;
db46c213
CB
738 /* strlen("zfs:") */
739 len += 4;
740 bdev->src = malloc(len);
3ef1df7c
CB
741 if (!bdev->src) {
742 ERROR("Failed to allocate memory");
8907f444 743 return -1;
3ef1df7c 744 }
8907f444 745
3ef1df7c
CB
746 ret = snprintf(bdev->src, len, "zfs:%s/%s", zfsroot, n);
747 if (ret < 0 || ret >= len) {
748 ERROR("Failed to create string");
8907f444 749 return -1;
3ef1df7c
CB
750 }
751 argv[7] = lxc_storage_get_path(bdev->src, bdev->type);
8907f444 752
3ef1df7c
CB
753 ret = snprintf(option, MAXPATHLEN, "mountpoint=%s", bdev->dest);
754 if (ret < 0 || ret >= MAXPATHLEN) {
755 ERROR("Failed to create string");
db46c213 756 return -1;
3ef1df7c
CB
757 }
758 argv[3] = option;
8907f444 759
3ef1df7c 760 cmd_args.argv = argv;
db46c213
CB
761 ret = run_command(cmd_output, sizeof(cmd_output),
762 zfs_create_exec_wrapper, (void *)&cmd_args);
24f84f1e 763 if (ret < 0) {
3ef1df7c 764 ERROR("Failed to create zfs dataset \"%s\": %s", bdev->src, cmd_output);
24f84f1e
CB
765 return -1;
766 } else if (cmd_output[0] != '\0') {
3ef1df7c 767 INFO("Created zfs dataset \"%s\": %s", bdev->src, cmd_output);
24f84f1e 768 } else {
3ef1df7c 769 TRACE("Created zfs dataset \"%s\"", bdev->src);
24f84f1e 770 }
3ef1df7c
CB
771
772 ret = mkdir_p(bdev->dest, 0755);
773 if (ret < 0 && errno != EEXIST) {
774 SYSERROR("Failed to create directory \"%s\"", bdev->dest);
775 return -1;
776 }
777
db46c213 778 return ret;
8907f444 779}