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