1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
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.
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.
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/>.
22 #include <sys/statfs.h>
28 #include "btrfs-util.h"
29 #include "path-util.h"
31 #include "machine-image.h"
33 static const char image_search_path[] =
35 "/var/lib/container\0"
36 "/usr/local/lib/machines\0"
37 "/usr/lib/machines\0";
39 Image *image_unref(Image *i) {
59 _cleanup_(image_unrefp) Image *i = NULL;
62 assert(t < _IMAGE_TYPE_MAX);
72 i->read_only = read_only;
75 i->size = i->size_exclusive = (uint64_t) -1;
76 i->limit = i->limit_exclusive = (uint64_t) -1;
78 i->name = strdup(pretty);
83 i->path = strjoin(path, "/", filename, NULL);
85 i->path = strdup(filename);
90 path_kill_slashes(i->path);
98 static int image_make(
102 const char *filename,
111 /* We explicitly *do* follow symlinks here, since we want to
112 * allow symlinking trees into /var/lib/container/, and treat
115 if (fstatat(dfd, filename, &st, 0) < 0)
119 (path && path_startswith(path, "/usr")) ||
120 (faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS);
122 if (S_ISDIR(st.st_mode)) {
130 /* btrfs subvolumes have inode 256 */
131 if (st.st_ino == 256) {
132 _cleanup_close_ int fd = -1;
135 fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
139 if (fstatfs(fd, &sfs) < 0)
142 if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) {
143 BtrfsSubvolInfo info;
144 BtrfsQuotaInfo quota;
146 /* It's a btrfs subvolume */
148 r = btrfs_subvol_get_info_fd(fd, &info);
152 r = image_new(IMAGE_SUBVOLUME,
156 info.read_only || read_only,
163 r = btrfs_subvol_get_quota_fd(fd, "a);
165 (*ret)->size = quota.referred;
166 (*ret)->size_exclusive = quota.exclusive;
168 (*ret)->limit = quota.referred_max;
169 (*ret)->limit_exclusive = quota.exclusive_max;
176 /* It's just a normal directory. */
178 r = image_new(IMAGE_DIRECTORY,
191 } else if (S_ISREG(st.st_mode) && endswith(filename, ".gpt")) {
194 /* It's a GPT block device */
199 fd_getcrtime_at(dfd, filename, &crtime, 0);
202 pretty = strndupa(filename, strlen(filename) - 4);
204 r = image_new(IMAGE_GPT,
208 !(st.st_mode & 0222) || read_only,
210 timespec_load(&st.st_mtim),
215 (*ret)->size = (*ret)->size_exclusive = st.st_blocks * 512;
216 (*ret)->limit = (*ret)->limit_exclusive = st.st_size;
224 int image_find(const char *name, Image **ret) {
230 /* There are no images with invalid names */
231 if (!image_name_is_valid(name))
234 NULSTR_FOREACH(path, image_search_path) {
235 _cleanup_closedir_ DIR *d = NULL;
245 r = image_make(NULL, dirfd(d), path, name, ret);
246 if (r == 0 || r == -ENOENT) {
247 _cleanup_free_ char *gpt = NULL;
249 gpt = strappend(name, ".gpt");
253 r = image_make(NULL, dirfd(d), path, gpt, ret);
254 if (r == 0 || r == -ENOENT)
263 if (streq(name, ".host"))
264 return image_make(".host", AT_FDCWD, NULL, "/", ret);
269 int image_discover(Hashmap *h) {
275 NULSTR_FOREACH(path, image_search_path) {
276 _cleanup_closedir_ DIR *d = NULL;
287 FOREACH_DIRENT_ALL(de, d, return -errno) {
288 _cleanup_(image_unrefp) Image *image = NULL;
290 if (!image_name_is_valid(de->d_name))
293 if (hashmap_contains(h, de->d_name))
296 r = image_make(NULL, dirfd(d), path, de->d_name, &image);
297 if (r == 0 || r == -ENOENT)
302 r = hashmap_put(h, image->name, image);
310 if (!hashmap_contains(h, ".host")) {
311 _cleanup_(image_unrefp) Image *image = NULL;
313 r = image_make(".host", AT_FDCWD, NULL, "/", &image);
317 r = hashmap_put(h, image->name, image);
328 void image_hashmap_free(Hashmap *map) {
331 while ((i = hashmap_steal_first(map)))
337 int image_remove(Image *i) {
340 if (path_equal(i->path, "/") ||
341 path_startswith(i->path, "/usr"))
346 case IMAGE_SUBVOLUME:
347 return btrfs_subvol_remove(i->path);
349 case IMAGE_DIRECTORY:
351 return rm_rf_dangerous(i->path, false, true, false);
358 int image_rename(Image *i, const char *new_name) {
359 _cleanup_free_ char *new_path = NULL, *nn = NULL;
364 if (!image_name_is_valid(new_name))
367 if (path_equal(i->path, "/") ||
368 path_startswith(i->path, "/usr"))
371 r = image_find(new_name, NULL);
379 case IMAGE_SUBVOLUME:
380 case IMAGE_DIRECTORY:
381 new_path = file_in_same_dir(i->path, new_name);
387 fn = strappenda(new_name, ".gpt");
388 new_path = file_in_same_dir(i->path, fn);
399 nn = strdup(new_name);
403 if (renameat2(AT_FDCWD, i->path, AT_FDCWD, new_path, RENAME_NOREPLACE) < 0)
417 int image_clone(Image *i, const char *new_name, bool read_only) {
418 const char *new_path;
423 if (!image_name_is_valid(new_name))
426 r = image_find(new_name, NULL);
434 case IMAGE_SUBVOLUME:
435 case IMAGE_DIRECTORY:
436 new_path = strappenda("/var/lib/container/", new_name);
438 r = btrfs_subvol_snapshot(i->path, new_path, read_only, true);
442 new_path = strappenda("/var/lib/container/", new_name, ".gpt");
444 r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, false, FS_NOCOW_FL);
457 int image_read_only(Image *i, bool b) {
461 if (path_equal(i->path, "/") ||
462 path_startswith(i->path, "/usr"))
467 case IMAGE_SUBVOLUME:
468 r = btrfs_subvol_set_read_only(i->path, b);
476 if (stat(i->path, &st) < 0)
479 if (chmod(i->path, (st.st_mode & 0444) | (b ? 0000 : 0200)) < 0)
482 /* If the images is now read-only, it's a good time to
483 * defrag it, given that no write patterns will
484 * fragment it again. */
486 (void) btrfs_defrag(i->path);
490 case IMAGE_DIRECTORY:
498 static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
499 [IMAGE_DIRECTORY] = "directory",
500 [IMAGE_SUBVOLUME] = "subvolume",
504 DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType);