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