]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/image.c
machined/machinectl: add logic to show list of available images
[thirdparty/systemd.git] / src / 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
22#include <sys/statfs.h>
23
24#include "strv.h"
25#include "utf8.h"
26#include "btrfs-util.h"
27#include "image.h"
28#include "bus-label.h"
29
30Image *image_unref(Image *i) {
31 if (!i)
32 return NULL;
33
34 free(i->name);
35 free(i->path);
36 free(i);
37 return NULL;
38}
39
40static int add_image(
41 Hashmap *h,
42 ImageType t,
43 const char *name,
44 const char *path,
45 bool read_only,
46 usec_t mtime,
47 usec_t btime) {
48
49 _cleanup_(image_unrefp) Image *i = NULL;
50 int r;
51
52 assert(h);
53 assert(t >= 0);
54 assert(t < _IMAGE_TYPE_MAX);
55 assert(name);
56
57 i = new(Image, 1);
58 if (!i)
59 return -ENOMEM;
60
61 i->type = t;
62 i->read_only = read_only;
63 i->mtime = mtime;
64 i->btime = btime;
65
66 i->name = strdup(name);
67 if (!i->name)
68 return -ENOMEM;
69
70 if (path) {
71 i->path = strdup(path);
72 if (!i->path)
73 return -ENOMEM;
74 }
75
76 r = hashmap_put(h, i->name, i);
77 if (r < 0)
78 return r;
79
80 i = NULL;
81 return 0;
82}
83
84int image_discover(Hashmap *h) {
85 const char *path;
86 int r;
87
88 assert(h);
89
90 FOREACH_STRING(path, "/var/lib/container", "/var/lib/machine") {
91 _cleanup_closedir_ DIR *d = NULL;
92 struct dirent *de;
93
94 d = opendir(path);
95 if (!d) {
96 if (errno == ENOENT)
97 return 0;
98
99 return -errno;
100 }
101
102 FOREACH_DIRENT_ALL(de, d, return -errno) {
103 struct stat st;
104
105 if (STR_IN_SET(de->d_name, ".", ".."))
106 continue;
107
108 /* Temporary files for atomically creating new files */
109 if (startswith(de->d_name, ".#"))
110 continue;
111
112 if (string_has_cc(de->d_name, NULL))
113 continue;
114
115 if (!utf8_is_valid(de->d_name))
116 continue;
117
118 if (hashmap_contains(h, de->d_name))
119 continue;
120
121 /* We explicitly *do* follow symlinks here,
122 * since we want to allow symlinking trees
123 * into /var/lib/container/, and treat them
124 * normally. */
125 if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) {
126 if (errno == ENOENT)
127 continue;
128
129 return -errno;
130 }
131
132 if (S_ISDIR(st.st_mode)) {
133
134 /* btrfs subvolumes have inode 256 */
135 if (st.st_ino == 256) {
136 _cleanup_close_ int fd = -1;
137 struct statfs sfs;
138
139 fd = openat(dirfd(d), de->d_name, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
140 if (fd < 0) {
141 if (errno == ENOENT)
142 continue;
143
144 return -errno;
145 }
146
147 if (fstatfs(fd, &sfs) < 0)
148 return -errno;
149
150 if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) {
151 usec_t btime = 0;
152 int ro;
153
154 /* It's a btrfs subvolume */
155
156 ro = btrfs_subvol_is_read_only_fd(fd);
157 if (ro < 0)
158 return ro;
159
160 /* r = btrfs_subvol_get_btime(fd, &btime); */
161 /* if (r < 0) */
162 /* return r; */
163
164 r = add_image(h,
165 IMAGE_SUBVOLUME,
166 de->d_name,
167 path,
168 ro,
169 0,
170 btime);
171
172 if (r < 0)
173 return r;
174
175 continue;
176 }
177 }
178
179 /* It's just a normal directory. */
180
181 r = add_image(h,
182 IMAGE_DIRECTORY,
183 de->d_name,
184 path,
185 false,
186 0,
187 0);
188 if (r < 0)
189 return r;
190
191 } else if (S_ISREG(st.st_mode) &&
192 endswith(de->d_name, ".gpt")) {
193
194 /* It's a GPT block device */
195
196 r = add_image(h,
197 IMAGE_GPT,
198 de->d_name,
199 path,
200 !!(st.st_mode & 0111),
201 timespec_load(&st.st_mtim),
202 0);
203 if (r < 0)
204 return r;
205 }
206 }
207 }
208
209 return 0;
210}
211
212void image_hashmap_free(Hashmap *map) {
213 Image *i;
214
215 while ((i = hashmap_steal_first(map)))
216 image_unref(i);
217
218 hashmap_free(map);
219}
220
221char *image_bus_path(const char *name) {
222 _cleanup_free_ char *e = NULL;
223
224 assert(name);
225
226 e = bus_label_escape(name);
227 if (!e)
228 return NULL;
229
230 return strappend("/org/freedesktop/machine1/image/", e);
231}
232
233static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
234 [IMAGE_DIRECTORY] = "directory",
235 [IMAGE_SUBVOLUME] = "subvolume",
236 [IMAGE_GPT] = "gpt",
237};
238
239DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType);