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