chiark / gitweb /
sysv-generator: properly add Makefile symlink
[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 Image *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
40 static 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
84 int 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
212 void 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
221 char *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
233 static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
234         [IMAGE_DIRECTORY] = "directory",
235         [IMAGE_SUBVOLUME] = "subvolume",
236         [IMAGE_GPT] = "gpt",
237 };
238
239 DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType);