Branch data Line data Source code
1 : : /*
2 : : * IRMAP -- inode reverse mapping.
3 : : *
4 : : * Helps us to map inode number (and device) back to path
5 : : * so that we can restore inotify/fanotify-s.
6 : : *
7 : : * Scanning _is_ slow, so we limit it with hints, which are
8 : : * heurisitical known places where notifies are typically put.
9 : : */
10 : :
11 : : #include <stdbool.h>
12 : : #include <fcntl.h>
13 : : #include <dirent.h>
14 : : #include <string.h>
15 : : #include <stdio.h>
16 : : #include <sys/stat.h>
17 : : #include <unistd.h>
18 : :
19 : : #include "xmalloc.h"
20 : : #include "irmap.h"
21 : : #include "mount.h"
22 : : #include "log.h"
23 : : #include "util.h"
24 : : #include "image.h"
25 : : #include "stats.h"
26 : : #include "pstree.h"
27 : :
28 : : #include "protobuf.h"
29 : : #include "protobuf/fsnotify.pb-c.h"
30 : : #include "protobuf/fh.pb-c.h"
31 : :
32 : : #undef LOG_PREFIX
33 : : #define LOG_PREFIX "irmap: "
34 : :
35 : : #define IRMAP_CACHE_BITS 5
36 : : #define IRMAP_CACHE_SIZE (1 << IRMAP_CACHE_BITS)
37 : : #define IRMAP_CACHE_MASK (IRMAP_CACHE_SIZE - 1)
38 : :
39 : : static inline int irmap_hashfn(unsigned int s_dev, unsigned long i_ino)
40 : : {
41 : 0 : return (s_dev + i_ino) & IRMAP_CACHE_MASK;
42 : : }
43 : :
44 : : struct irmap {
45 : : unsigned int dev;
46 : : unsigned long ino;
47 : : char *path;
48 : : struct irmap *next;
49 : : bool revalidate;
50 : : int nr_kids;
51 : : struct irmap *kids;
52 : : };
53 : :
54 : : static struct irmap *cache[IRMAP_CACHE_SIZE];
55 : :
56 : : static struct irmap hints[] = {
57 : : { .path = "/etc", .nr_kids = -1, },
58 : : { .path = "/var/spool", .nr_kids = -1, },
59 : : { .path = "/lib/udev", .nr_kids = -1, },
60 : : { .path = "/no-such-path", .nr_kids = -1, },
61 : : { },
62 : : };
63 : :
64 : : /*
65 : : * Update inode (and device) number and cache the entry
66 : : */
67 : 0 : static int irmap_update_stat(struct irmap *i)
68 : : {
69 : : struct stat st;
70 : : int mntns_root;
71 : : unsigned hv;
72 : :
73 [ # # ]: 0 : if (i->ino)
74 : : return 0;
75 : :
76 : 0 : mntns_root = get_service_fd(ROOT_FD_OFF);
77 : :
78 : 0 : pr_debug("Refresh stat for %s\n", i->path);
79 [ # # ]: 0 : if (fstatat(mntns_root, i->path + 1, &st, AT_SYMLINK_NOFOLLOW)) {
80 : 0 : pr_perror("Can't stat %s", i->path);
81 : 0 : return -1;
82 : : }
83 : :
84 : 0 : i->revalidate = false;
85 : 0 : i->dev = st.st_dev;
86 : 0 : i->ino = st.st_ino;
87 [ # # ]: 0 : if (!S_ISDIR(st.st_mode))
88 : 0 : i->nr_kids = 0; /* don't irmap_update_dir */
89 : :
90 : 0 : hv = irmap_hashfn(i->dev, i->ino);
91 : 0 : i->next = cache[hv];
92 : 0 : cache[hv] = i;
93 : :
94 : 0 : return 0;
95 : : }
96 : :
97 : : /*
98 : : * Update list of children, but don't cache any. Later
99 : : * we'll scan them one-by-one and cache.
100 : : */
101 : 0 : static int irmap_update_dir(struct irmap *t)
102 : : {
103 : : int fd, nr = 0, dlen, mntns_root;
104 : : DIR *dfd;
105 : : struct dirent *de;
106 : :
107 [ # # ]: 0 : if (t->nr_kids >= 0)
108 : : return 0;
109 : :
110 : 0 : mntns_root = get_service_fd(ROOT_FD_OFF);
111 : :
112 : 0 : pr_debug("Refilling %s dir\n", t->path);
113 : 0 : fd = openat(mntns_root, t->path + 1, O_RDONLY);
114 [ # # ]: 0 : if (fd < 0) {
115 : 0 : pr_perror("Can't open %s", t->path);
116 : 0 : return -1;
117 : : }
118 : :
119 : 0 : dlen = strlen(t->path);
120 : 0 : dfd = fdopendir(fd);
121 [ # # ]: 0 : if (!dfd) {
122 : 0 : pr_perror("Can't opendir %s", t->path);
123 : 0 : return -1;
124 : : }
125 : :
126 : 0 : errno = 0;
127 [ # # ]: 0 : while ((de = readdir(dfd)) != NULL) {
128 : : struct irmap *k;
129 : :
130 [ # # ]: 0 : if (dir_dots(de))
131 : 0 : continue;
132 : :
133 : 0 : nr++;
134 [ # # ][ # # ]: 0 : if (xrealloc_safe(&t->kids, nr * sizeof(struct irmap)))
[ # # ]
135 : : goto out_err;
136 : :
137 : 0 : k = &t->kids[nr - 1];
138 : :
139 : 0 : k->kids = NULL; /* for xrealloc above */
140 : 0 : k->ino = 0; /* for irmap_update_stat */
141 : 0 : k->nr_kids = -1; /* for irmap_update_dir */
142 : :
143 [ # # ]: 0 : k->path = xmalloc(dlen + strlen(de->d_name) + 2);
144 [ # # ]: 0 : if (!k->path)
145 : : goto out_err;
146 : :
147 : 0 : sprintf(k->path, "%s/%s", t->path, de->d_name);
148 : : }
149 : :
150 [ # # ]: 0 : if (errno) {
151 : 0 : pr_perror("Readdir failed");
152 : 0 : goto out_err;
153 : : }
154 : :
155 : 0 : closedir(dfd);
156 : 0 : close(fd);
157 : 0 : t->nr_kids = nr;
158 : 0 : return 0;
159 : :
160 : : out_err:
161 [ # # ]: 0 : xfree(t->kids);
162 : 0 : closedir(dfd);
163 : 0 : close(fd);
164 : 0 : return -1;
165 : : }
166 : :
167 : 0 : static struct irmap *irmap_scan(struct irmap *t, unsigned int dev, unsigned long ino)
168 : : {
169 : : struct irmap *c;
170 : : int i;
171 : :
172 [ # # ]: 0 : if (irmap_update_stat(t))
173 : : return NULL;
174 : :
175 [ # # ][ # # ]: 0 : if (t->dev == dev && t->ino == ino)
176 : : return t;
177 : :
178 [ # # ]: 0 : if (irmap_update_dir(t))
179 : : return NULL;
180 : :
181 [ # # ]: 0 : for (i = 0; i < t->nr_kids; i++) {
182 : 0 : c = irmap_scan(&t->kids[i], dev, ino);
183 [ # # ]: 0 : if (c)
184 : : return c;
185 : : }
186 : :
187 : : return NULL;
188 : : }
189 : :
190 : 0 : static int irmap_revalidate(struct irmap *c, struct irmap **p)
191 : : {
192 : : struct stat st;
193 : : int mntns_root;
194 : :
195 : 0 : mntns_root = get_service_fd(ROOT_FD_OFF);
196 : :
197 : 0 : pr_debug("Revalidate stat for %s\n", c->path);
198 [ # # ]: 0 : if (fstatat(mntns_root, c->path + 1, &st, AT_SYMLINK_NOFOLLOW)) {
199 : : /* File can be (re)moved, so just treat it as invalid */
200 : 0 : pr_perror("Can't stat %s", c->path);
201 : 0 : goto invalid;
202 : : }
203 : :
204 [ # # ]: 0 : if (c->dev != st.st_dev)
205 : : goto invalid;
206 [ # # ]: 0 : if (c->ino != st.st_ino)
207 : : goto invalid;
208 : :
209 : 0 : c->revalidate = false;
210 : 0 : return 0;
211 : :
212 : : invalid:
213 : 0 : pr_debug("\t%x:%lx is invalid\n", c->dev, c->ino);
214 : 0 : *p = c->next;
215 [ # # ]: 0 : xfree(c->path);
216 [ # # ]: 0 : xfree(c);
217 : : return 1;
218 : : }
219 : :
220 : 0 : char *irmap_lookup(unsigned int s_dev, unsigned long i_ino)
221 : : {
222 : : struct irmap *c, *h, **p;
223 : : char *path = NULL;
224 : : int hv;
225 : :
226 : : s_dev = kdev_to_odev(s_dev);
227 : :
228 : 0 : pr_debug("Resolving %x:%lx path\n", s_dev, i_ino);
229 : :
230 [ # # ]: 0 : if (mntns_collect_root(root_item->pid.real) < 0)
231 : : goto out;
232 : :
233 : 0 : timing_start(TIME_IRMAP_RESOLVE);
234 : :
235 : : hv = irmap_hashfn(s_dev, i_ino);
236 [ # # ]: 0 : for (p = &cache[hv]; *p; p = &(*p)->next) {
237 : : c = *p;
238 [ # # ][ # # ]: 0 : if (!(c->dev == s_dev && c->ino == i_ino))
239 : 0 : continue;
240 : :
241 [ # # ][ # # ]: 0 : if (c->revalidate && irmap_revalidate(c, p))
242 : 0 : continue;
243 : :
244 : 0 : pr_debug("\tFound %s in cache\n", c->path);
245 : 0 : path = c->path;
246 : 0 : goto out;
247 : : }
248 : :
249 [ # # ]: 0 : for (h = hints; h->path; h++) {
250 : 0 : pr_debug("Scanning %s hint\n", h->path);
251 : 0 : c = irmap_scan(h, s_dev, i_ino);
252 [ # # ]: 0 : if (c) {
253 : 0 : pr_debug("\tScanned %s\n", c->path);
254 : 0 : path = c->path;
255 : 0 : goto out;
256 : : }
257 : : }
258 : :
259 : : out:
260 : 0 : timing_stop(TIME_IRMAP_RESOLVE);
261 : 0 : return path;
262 : : }
263 : :
264 : : /*
265 : : * IRMAP pre-cache -- do early irmap scan on pre-dump to reduce
266 : : * the freeze time on dump
267 : : */
268 : :
269 : : struct irmap_predump {
270 : : unsigned int dev;
271 : : unsigned long ino;
272 : : FhEntry fh;
273 : : struct irmap_predump *next;
274 : : };
275 : :
276 : : static struct irmap_predump *predump_queue;
277 : :
278 : 36 : int irmap_queue_cache(unsigned int dev, unsigned long ino,
279 : : FhEntry *fh)
280 : : {
281 : : struct irmap_predump *ip;
282 : :
283 [ - + ]: 36 : ip = xmalloc(sizeof(*ip));
284 [ + - ]: 36 : if (!ip)
285 : : return -1;
286 : :
287 : 36 : ip->dev = dev;
288 : 36 : ip->ino = ino;
289 : 36 : ip->fh = *fh;
290 : 36 : fh->handle = NULL; /* don't free in free_fhandle */
291 : :
292 : 36 : pr_debug("Queue %x:%lx for pre-dump\n", dev, ino);
293 : :
294 : 36 : ip->next = predump_queue;
295 : 36 : predump_queue = ip;
296 : 36 : return 0;
297 : : }
298 : :
299 : 1344 : int irmap_predump_run(void)
300 : : {
301 : : int ret = 0, fd;
302 : : struct irmap_predump *ip;
303 : :
304 : 1344 : fd = open_image_at(AT_FDCWD, CR_FD_IRMAP_CACHE, O_DUMP);
305 [ + - ]: 1344 : if (fd < 0)
306 : : return -1;
307 : :
308 : 1344 : pr_info("Running irmap pre-dump\n");
309 : :
310 [ + + ]: 1380 : for (ip = predump_queue; ip; ip = ip->next) {
311 : 36 : pr_debug("\tchecking %x:%lx\n", ip->dev, ip->ino);
312 : 36 : ret = check_open_handle(ip->dev, ip->ino, &ip->fh);
313 [ - + ]: 36 : if (ret) {
314 : 0 : pr_err("Failed to resolve %x:%lx\n", ip->dev, ip->ino);
315 : 0 : break;
316 : : }
317 : :
318 [ - + ]: 36 : if (ip->fh.path) {
319 : 0 : IrmapCacheEntry ic = IRMAP_CACHE_ENTRY__INIT;
320 : :
321 : 0 : pr_info("Irmap cache %x:%lx -> %s\n", ip->dev, ip->ino, ip->fh.path);
322 : 0 : ic.dev = ip->dev;
323 : 0 : ic.inode = ip->ino;
324 : 0 : ic.path = ip->fh.path;
325 : :
326 : 0 : ret = pb_write_one(fd, &ic, PB_IRMAP_CACHE);
327 [ # # ]: 0 : if (ret)
328 : : break;
329 : : }
330 : : }
331 : :
332 : 1344 : close(fd);
333 : 1344 : return ret;
334 : : }
335 : :
336 : 0 : static int irmap_cache_one(IrmapCacheEntry *ie)
337 : : {
338 : : struct irmap *ic;
339 : : unsigned hv;
340 : :
341 [ # # ]: 0 : ic = xmalloc(sizeof(*ic));
342 [ # # ]: 0 : if (!ic)
343 : : return -1;
344 : :
345 : 0 : ic->dev = ie->dev;
346 : 0 : ic->ino = ie->inode;
347 [ # # ]: 0 : ic->path = xstrdup(ie->path);
348 [ # # ]: 0 : if (!ie->path) {
349 [ # # ]: 0 : xfree(ic);
350 : : return -1;
351 : : }
352 : :
353 : 0 : ic->nr_kids = 0;
354 : : /*
355 : : * We've loaded entry from cache, thus we'll need to check
356 : : * whether it's still valid when find it in cache.
357 : : */
358 : 0 : ic->revalidate = true;
359 : :
360 : 0 : pr_debug("Pre-cache %x:%lx -> %s\n", ic->dev, ic->ino, ic->path);
361 : :
362 : 0 : hv = irmap_hashfn(ic->dev, ic->ino);
363 : 0 : ic->next = cache[hv];
364 : 0 : cache[hv] = ic;
365 : :
366 : 0 : return 0;
367 : : }
368 : :
369 : 1792 : static int open_irmap_cache(int *fd)
370 : : {
371 : : int dir = AT_FDCWD;
372 : :
373 : 1792 : pr_info("Searching irmap cache in work dir\n");
374 : : in:
375 : 3136 : *fd = open_image_at(dir, CR_FD_IRMAP_CACHE, O_RSTR | O_OPT);
376 [ + + ]: 3136 : if (dir != AT_FDCWD)
377 : 1344 : close(dir);
378 : :
379 [ + + ]: 3136 : if (*fd >= 0) {
380 : 1344 : pr_info("... done\n");
381 : 1344 : return 1;
382 : : }
383 : :
384 [ + - ][ + - ]: 1792 : if (*fd == -ENOENT && dir == AT_FDCWD) {
385 : 1792 : pr_info("Searching irmap cache in parent\n");
386 : 1792 : dir = openat(get_service_fd(IMG_FD_OFF), CR_PARENT_LINK, O_RDONLY);
387 [ + + ]: 1792 : if (dir >= 0)
388 : : goto in;
389 : : }
390 : :
391 [ + - ]: 448 : if (*fd != -ENOENT)
392 : : return -1;
393 : :
394 : 448 : pr_info("No irmap cache\n");
395 : 448 : return 0;
396 : : }
397 : :
398 : 1792 : int irmap_load_cache(void)
399 : : {
400 : : int fd, ret;
401 : :
402 : 1792 : ret = open_irmap_cache(&fd);
403 [ + + ]: 1792 : if (ret <= 0)
404 : : return ret;
405 : :
406 : 1344 : pr_info("Loading irmap cache\n");
407 : : while (1) {
408 : : IrmapCacheEntry *ic;
409 : :
410 : 1344 : ret = pb_read_one_eof(fd, &ic, PB_IRMAP_CACHE);
411 [ - + ]: 1344 : if (ret <= 0)
412 : : break;
413 : :
414 : 0 : ret = irmap_cache_one(ic);
415 [ # # ]: 0 : if (ret < 0)
416 : : break;
417 : :
418 : 0 : irmap_cache_entry__free_unpacked(ic, NULL);
419 : 0 : }
420 : :
421 : 1344 : close(fd);
422 : 1344 : return ret;
423 : : }
|