chiark / gitweb /
8f577adb59c36044aedc29c945c6cf2d5b0e38b2
[elogind.git] / src / machine / image.c
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
30 static const char image_search_path[] =
31         "/var/lib/container\0"
32         "/var/lib/machine\0";
33
34 Image *image_unref(Image *i) {
35         if (!i)
36                 return NULL;
37
38         free(i->name);
39         free(i->path);
40         free(i);
41         return NULL;
42 }
43
44 static int image_new(
45                 ImageType t,
46                 const char *name,
47                 const char *path,
48                 bool read_only,
49                 usec_t mtime,
50                 usec_t btime,
51                 Image **ret) {
52
53         _cleanup_(image_unrefp) Image *i = NULL;
54
55         assert(t >= 0);
56         assert(t < _IMAGE_TYPE_MAX);
57         assert(name);
58         assert(ret);
59
60         i = new0(Image, 1);
61         if (!i)
62                 return -ENOMEM;
63
64         i->type = t;
65         i->read_only = read_only;
66         i->mtime = mtime;
67         i->btime = btime;
68
69         i->name = strdup(name);
70         if (!i->name)
71                 return -ENOMEM;
72
73         if (path) {
74                 i->path = strdup(path);
75                 if (!i->path)
76                         return -ENOMEM;
77         }
78
79         *ret = i;
80         i = NULL;
81
82         return 0;
83 }
84
85 static int image_make(int dfd, const char *name, const char *path, Image **ret) {
86         struct stat st;
87         int r;
88
89         assert(dfd >= 0);
90         assert(name);
91
92         /* We explicitly *do* follow symlinks here, since we want to
93          * allow symlinking trees into /var/lib/container/, and treat
94          * them normally. */
95
96         if (fstatat(dfd, name, &st, 0) < 0)
97                 return -errno;
98
99         if (S_ISDIR(st.st_mode)) {
100
101                 if (!ret)
102                         return 1;
103
104                 /* btrfs subvolumes have inode 256 */
105                 if (st.st_ino == 256) {
106                         _cleanup_close_ int fd = -1;
107                         struct statfs sfs;
108
109                         fd = openat(dfd, name, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
110                         if (fd < 0)
111                                 return -errno;
112
113                         if (fstatfs(fd, &sfs) < 0)
114                                 return -errno;
115
116                         if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) {
117                                 usec_t btime = 0;
118                                 int ro;
119
120                                 /* It's a btrfs subvolume */
121
122                                 ro = btrfs_subvol_is_read_only_fd(fd);
123                                 if (ro < 0)
124                                         return ro;
125
126                                 /* r = btrfs_subvol_get_btime(fd, &btime); */
127                                 /* if (r < 0) */
128                                 /*         return r; */
129
130                                 r = image_new(IMAGE_SUBVOLUME,
131                                               name,
132                                               path,
133                                               ro,
134                                               0,
135                                               btime,
136                                               ret);
137                                 if (r < 0)
138                                         return r;
139
140                                 return 1;
141                         }
142                 }
143
144                 /* It's just a normal directory. */
145
146                 r = image_new(IMAGE_DIRECTORY,
147                               name,
148                               path,
149                               false,
150                               0,
151                               0,
152                               ret);
153                 if (r < 0)
154                         return r;
155
156                 return 1;
157
158         } else if (S_ISREG(st.st_mode) && endswith(name, ".gpt")) {
159
160                 /* It's a GPT block device */
161
162                 if (!ret)
163                         return 1;
164
165                 r = image_new(IMAGE_GPT,
166                               name,
167                               path,
168                               !!(st.st_mode & 0111),
169                               timespec_load(&st.st_mtim),
170                               0,
171                               ret);
172                 if (r < 0)
173                         return r;
174
175                 return 1;
176         }
177
178         return 0;
179 }
180
181 int image_find(const char *name, Image **ret) {
182         const char *path;
183         int r;
184
185         assert(name);
186
187         /* There are no images with invalid names */
188         if (!image_name_is_valid(name))
189                 return 0;
190
191         NULSTR_FOREACH(path, image_search_path) {
192                 _cleanup_closedir_ DIR *d = NULL;
193
194                 d = opendir(path);
195                 if (!d) {
196                         if (errno == ENOENT)
197                                 continue;
198
199                         return -errno;
200                 }
201
202                 r = image_make(dirfd(d), name, path, ret);
203                 if (r == 0 || r == -ENOENT)
204                         continue;
205                 if (r < 0)
206                         return r;
207
208                 return 1;
209         }
210
211         return 0;
212 };
213
214 int image_discover(Hashmap *h) {
215         const char *path;
216         int r;
217
218         assert(h);
219
220         NULSTR_FOREACH(path, image_search_path) {
221                 _cleanup_closedir_ DIR *d = NULL;
222                 struct dirent *de;
223
224                 d = opendir(path);
225                 if (!d) {
226                         if (errno == ENOENT)
227                                 return 0;
228
229                         return -errno;
230                 }
231
232                 FOREACH_DIRENT_ALL(de, d, return -errno) {
233                         _cleanup_(image_unrefp) Image *image = NULL;
234
235                         if (!image_name_is_valid(de->d_name))
236                                 continue;
237
238                         if (hashmap_contains(h, de->d_name))
239                                 continue;
240
241                         r = image_make(dirfd(d), de->d_name, path, &image);
242                         if (r == 0 || r == -ENOENT)
243                                 continue;
244                         if (r < 0)
245                                 return r;
246
247                         r = hashmap_put(h, image->name, image);
248                         if (r < 0)
249                                 return r;
250
251                         image = NULL;
252                 }
253         }
254
255         return 0;
256 }
257
258 void image_hashmap_free(Hashmap *map) {
259         Image *i;
260
261         while ((i = hashmap_steal_first(map)))
262                 image_unref(i);
263
264         hashmap_free(map);
265 }
266
267 char *image_bus_path(const char *name) {
268         _cleanup_free_ char *e = NULL;
269
270         assert(name);
271
272         e = bus_label_escape(name);
273         if (!e)
274                 return NULL;
275
276         return strappend("/org/freedesktop/machine1/image/", e);
277 }
278
279 static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
280         [IMAGE_DIRECTORY] = "directory",
281         [IMAGE_SUBVOLUME] = "subvolume",
282         [IMAGE_GPT] = "gpt",
283 };
284
285 DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType);