]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/machine-image.c
Merge pull request #1668 from ssahani/net1
[thirdparty/systemd.git] / src / shared / machine-image.c
CommitLineData
cd61c3bf
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
ebd93cb6 22#include <fcntl.h>
8e0b6570
LP
23#include <linux/fs.h>
24#include <sys/statfs.h>
cd61c3bf 25
cd61c3bf 26#include "btrfs-util.h"
ebd93cb6 27#include "copy.h"
3ffd4af2
LP
28#include "fd-util.h"
29#include "machine-image.h"
30535c16 30#include "mkdir.h"
8e0b6570 31#include "path-util.h"
c6878637 32#include "rm-rf.h"
07630cea 33#include "string-util.h"
8e0b6570
LP
34#include "strv.h"
35#include "utf8.h"
cd61c3bf 36
c2ce6a3d 37static const char image_search_path[] =
42c6f2c9 38 "/var/lib/machines\0"
7d105503 39 "/var/lib/container\0" /* legacy */
42c6f2c9
LP
40 "/usr/local/lib/machines\0"
41 "/usr/lib/machines\0";
c2ce6a3d 42
cd61c3bf
LP
43Image *image_unref(Image *i) {
44 if (!i)
45 return NULL;
46
47 free(i->name);
48 free(i->path);
49 free(i);
50 return NULL;
51}
52
8e0b6570
LP
53static char **image_settings_path(Image *image) {
54 _cleanup_strv_free_ char **l = NULL;
55 char **ret;
56 const char *fn, *s;
57 unsigned i = 0;
58
59 assert(image);
60
61 l = new0(char*, 4);
62 if (!l)
63 return NULL;
64
65 fn = strjoina(image->name, ".nspawn");
66
67 FOREACH_STRING(s, "/etc/systemd/nspawn/", "/run/systemd/nspawn/") {
68 l[i] = strappend(s, fn);
69 if (!l[i])
70 return NULL;
71
72 i++;
73 }
74
75 l[i] = file_in_same_dir(image->path, fn);
76 if (!l[i])
77 return NULL;
78
79 ret = l;
80 l = NULL;
81
82 return ret;
83}
84
c2ce6a3d 85static int image_new(
cd61c3bf 86 ImageType t,
5fc7f358 87 const char *pretty,
cd61c3bf 88 const char *path,
5fc7f358 89 const char *filename,
cd61c3bf 90 bool read_only,
10f9c755 91 usec_t crtime,
cd61c3bf 92 usec_t mtime,
c2ce6a3d 93 Image **ret) {
cd61c3bf
LP
94
95 _cleanup_(image_unrefp) Image *i = NULL;
cd61c3bf 96
cd61c3bf
LP
97 assert(t >= 0);
98 assert(t < _IMAGE_TYPE_MAX);
5fc7f358
LP
99 assert(pretty);
100 assert(filename);
c2ce6a3d 101 assert(ret);
cd61c3bf 102
c2ce6a3d 103 i = new0(Image, 1);
cd61c3bf
LP
104 if (!i)
105 return -ENOMEM;
106
107 i->type = t;
108 i->read_only = read_only;
10f9c755 109 i->crtime = crtime;
cd61c3bf 110 i->mtime = mtime;
c19de711 111 i->usage = i->usage_exclusive = (uint64_t) -1;
b6b18498 112 i->limit = i->limit_exclusive = (uint64_t) -1;
cd61c3bf 113
5fc7f358 114 i->name = strdup(pretty);
cd61c3bf
LP
115 if (!i->name)
116 return -ENOMEM;
117
5fc7f358
LP
118 if (path)
119 i->path = strjoin(path, "/", filename, NULL);
120 else
121 i->path = strdup(filename);
ebeccf9e 122
5fc7f358
LP
123 if (!i->path)
124 return -ENOMEM;
125
126 path_kill_slashes(i->path);
cd61c3bf 127
c2ce6a3d 128 *ret = i;
cd61c3bf 129 i = NULL;
c2ce6a3d 130
cd61c3bf
LP
131 return 0;
132}
133
5fc7f358
LP
134static int image_make(
135 const char *pretty,
136 int dfd,
137 const char *path,
138 const char *filename,
139 Image **ret) {
140
c2ce6a3d 141 struct stat st;
5fc7f358 142 bool read_only;
cd61c3bf
LP
143 int r;
144
5fc7f358 145 assert(filename);
cd61c3bf 146
c2ce6a3d 147 /* We explicitly *do* follow symlinks here, since we want to
5f129649 148 * allow symlinking trees into /var/lib/machines/, and treat
c2ce6a3d 149 * them normally. */
cd61c3bf 150
5fc7f358 151 if (fstatat(dfd, filename, &st, 0) < 0)
c2ce6a3d 152 return -errno;
cd61c3bf 153
5fc7f358
LP
154 read_only =
155 (path && path_startswith(path, "/usr")) ||
08ff5529 156 (faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS);
86e339c8 157
c2ce6a3d 158 if (S_ISDIR(st.st_mode)) {
01b72568
LP
159 _cleanup_close_ int fd = -1;
160 unsigned file_attr = 0;
cd61c3bf 161
c2ce6a3d
LP
162 if (!ret)
163 return 1;
cd61c3bf 164
5fc7f358
LP
165 if (!pretty)
166 pretty = filename;
167
01b72568
LP
168 fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
169 if (fd < 0)
170 return -errno;
171
c2ce6a3d
LP
172 /* btrfs subvolumes have inode 256 */
173 if (st.st_ino == 256) {
cd61c3bf 174
21222ea5
LP
175 r = btrfs_is_filesystem(fd);
176 if (r < 0)
177 return r;
178 if (r) {
10f9c755 179 BtrfsSubvolInfo info;
cd61c3bf 180
c2ce6a3d 181 /* It's a btrfs subvolume */
cd61c3bf 182
5bcd08db 183 r = btrfs_subvol_get_info_fd(fd, 0, &info);
10f9c755
LP
184 if (r < 0)
185 return r;
c2ce6a3d
LP
186
187 r = image_new(IMAGE_SUBVOLUME,
5fc7f358 188 pretty,
c2ce6a3d 189 path,
5fc7f358
LP
190 filename,
191 info.read_only || read_only,
10f9c755 192 info.otime,
c2ce6a3d 193 0,
c2ce6a3d
LP
194 ret);
195 if (r < 0)
196 return r;
197
5bcd08db
LP
198 if (btrfs_quota_scan_ongoing(fd) == 0) {
199 BtrfsQuotaInfo quota;
b6b18498 200
5bcd08db
LP
201 r = btrfs_subvol_get_subtree_quota_fd(fd, 0, &quota);
202 if (r >= 0) {
203 (*ret)->usage = quota.referenced;
204 (*ret)->usage_exclusive = quota.exclusive;
205
206 (*ret)->limit = quota.referenced_max;
207 (*ret)->limit_exclusive = quota.exclusive_max;
208 }
b6b18498
LP
209 }
210
c2ce6a3d 211 return 1;
cd61c3bf 212 }
c2ce6a3d 213 }
cd61c3bf 214
01b72568
LP
215 /* If the IMMUTABLE bit is set, we consider the
216 * directory read-only. Since the ioctl is not
217 * supported everywhere we ignore failures. */
218 (void) read_attr_fd(fd, &file_attr);
cd61c3bf 219
01b72568 220 /* It's just a normal directory. */
c2ce6a3d 221 r = image_new(IMAGE_DIRECTORY,
5fc7f358 222 pretty,
c2ce6a3d 223 path,
5fc7f358 224 filename,
01b72568 225 read_only || (file_attr & FS_IMMUTABLE_FL),
c2ce6a3d
LP
226 0,
227 0,
228 ret);
229 if (r < 0)
230 return r;
cd61c3bf 231
c2ce6a3d 232 return 1;
cd61c3bf 233
aceac2f0 234 } else if (S_ISREG(st.st_mode) && endswith(filename, ".raw")) {
10f9c755 235 usec_t crtime = 0;
cd61c3bf 236
aceac2f0 237 /* It's a RAW disk image */
cd61c3bf 238
c2ce6a3d
LP
239 if (!ret)
240 return 1;
cd61c3bf 241
5fc7f358 242 fd_getcrtime_at(dfd, filename, &crtime, 0);
10f9c755 243
5fc7f358
LP
244 if (!pretty)
245 pretty = strndupa(filename, strlen(filename) - 4);
10f9c755 246
aceac2f0 247 r = image_new(IMAGE_RAW,
5fc7f358 248 pretty,
c2ce6a3d 249 path,
5fc7f358
LP
250 filename,
251 !(st.st_mode & 0222) || read_only,
10f9c755 252 crtime,
c2ce6a3d 253 timespec_load(&st.st_mtim),
c2ce6a3d
LP
254 ret);
255 if (r < 0)
256 return r;
cd61c3bf 257
c19de711 258 (*ret)->usage = (*ret)->usage_exclusive = st.st_blocks * 512;
b6b18498
LP
259 (*ret)->limit = (*ret)->limit_exclusive = st.st_size;
260
c2ce6a3d
LP
261 return 1;
262 }
cd61c3bf 263
c2ce6a3d
LP
264 return 0;
265}
cd61c3bf 266
c2ce6a3d
LP
267int image_find(const char *name, Image **ret) {
268 const char *path;
269 int r;
cd61c3bf 270
c2ce6a3d 271 assert(name);
cd61c3bf 272
c2ce6a3d
LP
273 /* There are no images with invalid names */
274 if (!image_name_is_valid(name))
275 return 0;
cd61c3bf 276
c2ce6a3d
LP
277 NULSTR_FOREACH(path, image_search_path) {
278 _cleanup_closedir_ DIR *d = NULL;
cd61c3bf 279
c2ce6a3d
LP
280 d = opendir(path);
281 if (!d) {
282 if (errno == ENOENT)
283 continue;
cd61c3bf 284
c2ce6a3d
LP
285 return -errno;
286 }
cd61c3bf 287
5fc7f358
LP
288 r = image_make(NULL, dirfd(d), path, name, ret);
289 if (r == 0 || r == -ENOENT) {
aceac2f0 290 _cleanup_free_ char *raw = NULL;
5fc7f358 291
aceac2f0
LP
292 raw = strappend(name, ".raw");
293 if (!raw)
5fc7f358
LP
294 return -ENOMEM;
295
aceac2f0 296 r = image_make(NULL, dirfd(d), path, raw, ret);
5fc7f358
LP
297 if (r == 0 || r == -ENOENT)
298 continue;
299 }
c2ce6a3d
LP
300 if (r < 0)
301 return r;
cd61c3bf 302
c2ce6a3d
LP
303 return 1;
304 }
305
5fc7f358 306 if (streq(name, ".host"))
27c88c4e 307 return image_make(".host", AT_FDCWD, NULL, "/", ret);
5fc7f358 308
c2ce6a3d
LP
309 return 0;
310};
311
312int image_discover(Hashmap *h) {
313 const char *path;
314 int r;
315
316 assert(h);
317
318 NULSTR_FOREACH(path, image_search_path) {
319 _cleanup_closedir_ DIR *d = NULL;
320 struct dirent *de;
321
322 d = opendir(path);
323 if (!d) {
324 if (errno == ENOENT)
a67a4c8c 325 continue;
c2ce6a3d
LP
326
327 return -errno;
328 }
329
330 FOREACH_DIRENT_ALL(de, d, return -errno) {
331 _cleanup_(image_unrefp) Image *image = NULL;
332
333 if (!image_name_is_valid(de->d_name))
334 continue;
335
336 if (hashmap_contains(h, de->d_name))
337 continue;
338
5fc7f358 339 r = image_make(NULL, dirfd(d), path, de->d_name, &image);
c2ce6a3d
LP
340 if (r == 0 || r == -ENOENT)
341 continue;
342 if (r < 0)
343 return r;
344
345 r = hashmap_put(h, image->name, image);
346 if (r < 0)
347 return r;
348
349 image = NULL;
cd61c3bf
LP
350 }
351 }
352
5fc7f358
LP
353 if (!hashmap_contains(h, ".host")) {
354 _cleanup_(image_unrefp) Image *image = NULL;
355
356 r = image_make(".host", AT_FDCWD, NULL, "/", &image);
357 if (r < 0)
358 return r;
359
360 r = hashmap_put(h, image->name, image);
361 if (r < 0)
362 return r;
363
364 image = NULL;
365
366 }
367
cd61c3bf
LP
368 return 0;
369}
370
371void image_hashmap_free(Hashmap *map) {
372 Image *i;
373
374 while ((i = hashmap_steal_first(map)))
375 image_unref(i);
376
377 hashmap_free(map);
378}
379
08682124 380int image_remove(Image *i) {
30535c16 381 _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
8e0b6570
LP
382 _cleanup_strv_free_ char **settings = NULL;
383 char **j;
30535c16
LP
384 int r;
385
08682124
LP
386 assert(i);
387
388 if (path_equal(i->path, "/") ||
389 path_startswith(i->path, "/usr"))
390 return -EROFS;
391
8e0b6570
LP
392 settings = image_settings_path(i);
393 if (!settings)
394 return -ENOMEM;
395
30535c16
LP
396 /* Make sure we don't interfere with a running nspawn */
397 r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
398 if (r < 0)
399 return r;
400
ebd93cb6
LP
401 switch (i->type) {
402
403 case IMAGE_SUBVOLUME:
5bcd08db 404 r = btrfs_subvol_remove(i->path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
8e0b6570
LP
405 if (r < 0)
406 return r;
407 break;
ebd93cb6
LP
408
409 case IMAGE_DIRECTORY:
01b72568
LP
410 /* Allow deletion of read-only directories */
411 (void) chattr_path(i->path, false, FS_IMMUTABLE_FL);
8e0b6570
LP
412 r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
413 if (r < 0)
414 return r;
415
416 break;
01b72568 417
aceac2f0 418 case IMAGE_RAW:
41d1ed05
LP
419 if (unlink(i->path) < 0)
420 return -errno;
8e0b6570 421 break;
ebd93cb6
LP
422
423 default:
15411c0c 424 return -EOPNOTSUPP;
ebd93cb6 425 }
8e0b6570
LP
426
427 STRV_FOREACH(j, settings) {
428 if (unlink(*j) < 0 && errno != ENOENT)
429 log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", *j);
430 }
431
432 return 0;
433}
434
435static int rename_settings_file(const char *path, const char *new_name) {
436 _cleanup_free_ char *rs = NULL;
437 const char *fn;
438
439 fn = strjoina(new_name, ".nspawn");
440
441 rs = file_in_same_dir(path, fn);
442 if (!rs)
443 return -ENOMEM;
444
445 return rename_noreplace(AT_FDCWD, path, AT_FDCWD, rs);
ebd93cb6
LP
446}
447
448int image_rename(Image *i, const char *new_name) {
30535c16 449 _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT, name_lock = LOCK_FILE_INIT;
ebd93cb6 450 _cleanup_free_ char *new_path = NULL, *nn = NULL;
8e0b6570 451 _cleanup_strv_free_ char **settings = NULL;
01b72568 452 unsigned file_attr = 0;
8e0b6570 453 char **j;
ebd93cb6
LP
454 int r;
455
456 assert(i);
457
458 if (!image_name_is_valid(new_name))
459 return -EINVAL;
460
461 if (path_equal(i->path, "/") ||
462 path_startswith(i->path, "/usr"))
463 return -EROFS;
464
8e0b6570
LP
465 settings = image_settings_path(i);
466 if (!settings)
467 return -ENOMEM;
468
30535c16
LP
469 /* Make sure we don't interfere with a running nspawn */
470 r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
471 if (r < 0)
472 return r;
473
474 /* Make sure nobody takes the new name, between the time we
475 * checked it is currently unused in all search paths, and the
476 * time we take possesion of it */
477 r = image_name_lock(new_name, LOCK_EX|LOCK_NB, &name_lock);
478 if (r < 0)
479 return r;
480
ebd93cb6
LP
481 r = image_find(new_name, NULL);
482 if (r < 0)
483 return r;
484 if (r > 0)
485 return -EEXIST;
486
487 switch (i->type) {
488
ebd93cb6 489 case IMAGE_DIRECTORY:
01b72568
LP
490 /* Turn of the immutable bit while we rename the image, so that we can rename it */
491 (void) read_attr_path(i->path, &file_attr);
492
493 if (file_attr & FS_IMMUTABLE_FL)
494 (void) chattr_path(i->path, false, FS_IMMUTABLE_FL);
495
496 /* fall through */
497
498 case IMAGE_SUBVOLUME:
ebd93cb6
LP
499 new_path = file_in_same_dir(i->path, new_name);
500 break;
501
aceac2f0 502 case IMAGE_RAW: {
ebd93cb6
LP
503 const char *fn;
504
63c372cb 505 fn = strjoina(new_name, ".raw");
ebd93cb6
LP
506 new_path = file_in_same_dir(i->path, fn);
507 break;
508 }
509
510 default:
15411c0c 511 return -EOPNOTSUPP;
ebd93cb6
LP
512 }
513
514 if (!new_path)
515 return -ENOMEM;
516
517 nn = strdup(new_name);
518 if (!nn)
519 return -ENOMEM;
520
f85ef957
AC
521 r = rename_noreplace(AT_FDCWD, i->path, AT_FDCWD, new_path);
522 if (r < 0)
523 return r;
ebd93cb6 524
01b72568
LP
525 /* Restore the immutable bit, if it was set before */
526 if (file_attr & FS_IMMUTABLE_FL)
527 (void) chattr_path(new_path, true, FS_IMMUTABLE_FL);
528
ebd93cb6
LP
529 free(i->path);
530 i->path = new_path;
531 new_path = NULL;
532
533 free(i->name);
534 i->name = nn;
535 nn = NULL;
536
8e0b6570
LP
537 STRV_FOREACH(j, settings) {
538 r = rename_settings_file(*j, new_name);
539 if (r < 0 && r != -ENOENT)
540 log_debug_errno(r, "Failed to rename settings file %s, ignoring: %m", *j);
541 }
542
ebd93cb6
LP
543 return 0;
544}
545
8e0b6570
LP
546static int clone_settings_file(const char *path, const char *new_name) {
547 _cleanup_free_ char *rs = NULL;
548 const char *fn;
549
550 fn = strjoina(new_name, ".nspawn");
551
552 rs = file_in_same_dir(path, fn);
553 if (!rs)
554 return -ENOMEM;
555
556 return copy_file_atomic(path, rs, 0664, false, 0);
557}
558
ebd93cb6 559int image_clone(Image *i, const char *new_name, bool read_only) {
30535c16 560 _cleanup_release_lock_file_ LockFile name_lock = LOCK_FILE_INIT;
8e0b6570 561 _cleanup_strv_free_ char **settings = NULL;
ebd93cb6 562 const char *new_path;
8e0b6570 563 char **j;
ebd93cb6
LP
564 int r;
565
566 assert(i);
567
568 if (!image_name_is_valid(new_name))
569 return -EINVAL;
570
8e0b6570
LP
571 settings = image_settings_path(i);
572 if (!settings)
573 return -ENOMEM;
574
30535c16
LP
575 /* Make sure nobody takes the new name, between the time we
576 * checked it is currently unused in all search paths, and the
577 * time we take possesion of it */
578 r = image_name_lock(new_name, LOCK_EX|LOCK_NB, &name_lock);
579 if (r < 0)
580 return r;
581
ebd93cb6
LP
582 r = image_find(new_name, NULL);
583 if (r < 0)
584 return r;
585 if (r > 0)
586 return -EEXIST;
587
588 switch (i->type) {
589
590 case IMAGE_SUBVOLUME:
591 case IMAGE_DIRECTORY:
63c372cb 592 new_path = strjoina("/var/lib/machines/", new_name);
ebd93cb6 593
5bcd08db
LP
594 r = btrfs_subvol_snapshot(i->path, new_path, (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA);
595
596 /* Enable "subtree" quotas for the copy, if we didn't
597 * copy any quota from the source. */
598 (void) btrfs_subvol_auto_qgroup(i->path, 0, true);
599
ebd93cb6
LP
600 break;
601
aceac2f0 602 case IMAGE_RAW:
63c372cb 603 new_path = strjoina("/var/lib/machines/", new_name, ".raw");
ebd93cb6 604
f2068bcc 605 r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, false, FS_NOCOW_FL);
ebd93cb6
LP
606 break;
607
608 default:
15411c0c 609 return -EOPNOTSUPP;
ebd93cb6
LP
610 }
611
612 if (r < 0)
613 return r;
614
8e0b6570
LP
615 STRV_FOREACH(j, settings) {
616 r = clone_settings_file(*j, new_name);
617 if (r < 0 && r != -ENOENT)
618 log_debug_errno(r, "Failed to clone settings %s, ignoring: %m", *j);
619 }
620
ebd93cb6
LP
621 return 0;
622}
623
624int image_read_only(Image *i, bool b) {
30535c16 625 _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
ebd93cb6
LP
626 int r;
627 assert(i);
628
629 if (path_equal(i->path, "/") ||
630 path_startswith(i->path, "/usr"))
631 return -EROFS;
632
30535c16
LP
633 /* Make sure we don't interfere with a running nspawn */
634 r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
635 if (r < 0)
636 return r;
637
ebd93cb6
LP
638 switch (i->type) {
639
640 case IMAGE_SUBVOLUME:
5bcd08db
LP
641
642 /* Note that we set the flag only on the top-level
643 * subvolume of the image. */
644
ebd93cb6
LP
645 r = btrfs_subvol_set_read_only(i->path, b);
646 if (r < 0)
647 return r;
01b72568
LP
648
649 break;
650
651 case IMAGE_DIRECTORY:
652 /* For simple directory trees we cannot use the access
653 mode of the top-level directory, since it has an
654 effect on the container itself. However, we can
655 use the "immutable" flag, to at least make the
656 top-level directory read-only. It's not as good as
657 a read-only subvolume, but at least something, and
658 we can read the value back.*/
659
660 r = chattr_path(i->path, b, FS_IMMUTABLE_FL);
661 if (r < 0)
662 return r;
663
ebd93cb6
LP
664 break;
665
aceac2f0 666 case IMAGE_RAW: {
ebd93cb6
LP
667 struct stat st;
668
669 if (stat(i->path, &st) < 0)
670 return -errno;
671
672 if (chmod(i->path, (st.st_mode & 0444) | (b ? 0000 : 0200)) < 0)
673 return -errno;
f2068bcc
LP
674
675 /* If the images is now read-only, it's a good time to
676 * defrag it, given that no write patterns will
677 * fragment it again. */
678 if (b)
679 (void) btrfs_defrag(i->path);
ebd93cb6
LP
680 break;
681 }
682
ebd93cb6 683 default:
15411c0c 684 return -EOPNOTSUPP;
ebd93cb6
LP
685 }
686
687 return 0;
08682124
LP
688}
689
30535c16
LP
690int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local) {
691 _cleanup_free_ char *p = NULL;
692 LockFile t = LOCK_FILE_INIT;
693 struct stat st;
694 int r;
695
696 assert(path);
697 assert(global);
698 assert(local);
699
700 /* Locks an image path. This actually creates two locks: one
701 * "local" one, next to the image path itself, which might be
702 * shared via NFS. And another "global" one, in /run, that
703 * uses the device/inode number. This has the benefit that we
704 * can even lock a tree that is a mount point, correctly. */
705
706 if (path_equal(path, "/"))
707 return -EBUSY;
708
709 if (!path_is_absolute(path))
710 return -EINVAL;
711
712 if (stat(path, &st) >= 0) {
713 if (asprintf(&p, "/run/systemd/nspawn/locks/inode-%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino) < 0)
714 return -ENOMEM;
715 }
716
717 r = make_lock_file_for(path, operation, &t);
718 if (r < 0)
719 return r;
720
721 if (p) {
7e7cddb2 722 mkdir_p("/run/systemd/nspawn/locks", 0700);
30535c16
LP
723
724 r = make_lock_file(p, operation, global);
725 if (r < 0) {
726 release_lock_file(&t);
727 return r;
728 }
729 }
730
731 *local = t;
732 return 0;
733}
734
cb81cd80 735int image_set_limit(Image *i, uint64_t referenced_max) {
d6ce17c7
LP
736 assert(i);
737
738 if (path_equal(i->path, "/") ||
739 path_startswith(i->path, "/usr"))
740 return -EROFS;
741
742 if (i->type != IMAGE_SUBVOLUME)
15411c0c 743 return -EOPNOTSUPP;
d6ce17c7 744
5bcd08db
LP
745 /* We set the quota both for the subvolume as well as for the
746 * subtree. The latter is mostly for historical reasons, since
747 * we didn't use to have a concept of subtree quota, and hence
748 * only modified the subvolume quota. */
749
750 (void) btrfs_qgroup_set_limit(i->path, 0, referenced_max);
751 (void) btrfs_subvol_auto_qgroup(i->path, 0, true);
752 return btrfs_subvol_set_subtree_quota_limit(i->path, 0, referenced_max);
d6ce17c7
LP
753}
754
30535c16
LP
755int image_name_lock(const char *name, int operation, LockFile *ret) {
756 const char *p;
757
758 assert(name);
759 assert(ret);
760
761 /* Locks an image name, regardless of the precise path used. */
762
763 if (!image_name_is_valid(name))
764 return -EINVAL;
765
766 if (streq(name, ".host"))
767 return -EBUSY;
768
7e7cddb2 769 mkdir_p("/run/systemd/nspawn/locks", 0700);
63c372cb 770 p = strjoina("/run/systemd/nspawn/locks/name-", name);
30535c16
LP
771
772 return make_lock_file(p, operation, ret);
773}
774
775bool image_name_is_valid(const char *s) {
776 if (!filename_is_valid(s))
777 return false;
778
779 if (string_has_cc(s, NULL))
780 return false;
781
782 if (!utf8_is_valid(s))
783 return false;
784
785 /* Temporary files for atomically creating new files */
786 if (startswith(s, ".#"))
787 return false;
788
789 return true;
790}
791
cd61c3bf
LP
792static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
793 [IMAGE_DIRECTORY] = "directory",
794 [IMAGE_SUBVOLUME] = "subvolume",
aceac2f0 795 [IMAGE_RAW] = "raw",
cd61c3bf
LP
796};
797
798DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType);