]>
Commit | Line | Data |
---|---|---|
38683db4 CB |
1 | /* |
2 | * lxc: linux Container library | |
3 | * | |
4 | * (C) Copyright IBM Corp. 2007, 2008 | |
5 | * | |
6 | * Authors: | |
7 | * Daniel Lezcano <daniel.lezcano at free.fr> | |
8 | * | |
9 | * This library is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License as published by the Free Software Foundation; either | |
12 | * version 2.1 of the License, or (at your option) any later version. | |
13 | * | |
14 | * This library is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * Lesser General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU Lesser General Public | |
20 | * License along with this library; if not, write to the Free Software | |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
22 | */ | |
23 | ||
24 | #define _GNU_SOURCE | |
25 | #include <errno.h> | |
26 | #include <stdio.h> | |
27 | #include <stdlib.h> | |
28 | #include <string.h> | |
29 | ||
30 | #include "bdev.h" | |
31 | #include "conf.h" | |
32 | #include "confile.h" | |
33 | #include "log.h" | |
34 | #include "lxccontainer.h" | |
3c16d0cb | 35 | #include "lxcoverlay.h" |
38683db4 CB |
36 | #include "utils.h" |
37 | ||
38 | lxc_log_define(overlay, lxc); | |
39 | ||
5c484f79 CB |
40 | static char *ovl_name; |
41 | ||
38683db4 CB |
42 | struct ovl_rsync_data { |
43 | struct bdev *orig; | |
44 | struct bdev *new; | |
45 | }; | |
46 | ||
47 | /* defined in lxccontainer.c: needs to become common helper */ | |
48 | extern int do_rsync(const char *src, const char *dest); | |
49 | ||
50 | /* defined in lxccontainer.c: needs to become common helper */ | |
51 | extern char *dir_new_path(char *src, const char *oldname, const char *name, | |
52 | const char *oldpath, const char *lxcpath); | |
53 | ||
5c484f79 CB |
54 | static char *ovl_detect_name(void); |
55 | static int ovl_do_rsync(struct bdev *orig, struct bdev *new, struct lxc_conf *conf); | |
56 | static int ovl_rsync(struct ovl_rsync_data *data); | |
57 | static int ovl_rsync_wrapper(void *data); | |
38683db4 | 58 | |
83e79752 CB |
59 | int ovl_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname, |
60 | const char *cname, const char *oldpath, const char *lxcpath, | |
61 | int snap, uint64_t newsize, struct lxc_conf *conf) | |
38683db4 CB |
62 | { |
63 | if (!snap) { | |
64 | ERROR("overlayfs is only for snapshot clones"); | |
65 | return -22; | |
66 | } | |
67 | ||
68 | if (!orig->src || !orig->dest) | |
69 | return -1; | |
70 | ||
71 | new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath); | |
72 | if (!new->dest) | |
73 | return -1; | |
74 | if (mkdir_p(new->dest, 0755) < 0) | |
75 | return -1; | |
76 | ||
77 | if (am_unpriv() && chown_mapped_root(new->dest, conf) < 0) | |
78 | WARN("Failed to update ownership of %s", new->dest); | |
79 | ||
80 | if (strcmp(orig->type, "dir") == 0) { | |
81 | char *delta, *lastslash; | |
82 | char *work; | |
83 | int ret, len, lastslashidx; | |
84 | ||
5c484f79 CB |
85 | /* |
86 | * if we have | |
87 | * /var/lib/lxc/c2/rootfs | |
88 | * then delta will be | |
89 | * /var/lib/lxc/c2/delta0 | |
90 | */ | |
38683db4 CB |
91 | lastslash = strrchr(new->dest, '/'); |
92 | if (!lastslash) | |
93 | return -22; | |
94 | if (strlen(lastslash) < 7) | |
95 | return -22; | |
96 | lastslash++; | |
97 | lastslashidx = lastslash - new->dest; | |
98 | ||
99 | delta = malloc(lastslashidx + 7); | |
100 | if (!delta) | |
101 | return -1; | |
5c484f79 CB |
102 | strncpy(delta, new->dest, lastslashidx + 1); |
103 | strcpy(delta + lastslashidx, "delta0"); | |
38683db4 CB |
104 | if ((ret = mkdir(delta, 0755)) < 0) { |
105 | SYSERROR("error: mkdir %s", delta); | |
106 | free(delta); | |
107 | return -1; | |
108 | } | |
109 | if (am_unpriv() && chown_mapped_root(delta, conf) < 0) | |
110 | WARN("Failed to update ownership of %s", delta); | |
111 | ||
5c484f79 CB |
112 | /* |
113 | * Make workdir for overlayfs.v22 or higher: | |
114 | * The workdir will be | |
115 | * /var/lib/lxc/c2/olwork | |
116 | * and is used to prepare files before they are atomically | |
117 | * switched to the overlay destination. Workdirs need to be on | |
118 | * the same filesystem as the upperdir so it's OK for it to be | |
119 | * empty. | |
120 | */ | |
38683db4 CB |
121 | work = malloc(lastslashidx + 7); |
122 | if (!work) { | |
123 | free(delta); | |
124 | return -1; | |
125 | } | |
5c484f79 CB |
126 | strncpy(work, new->dest, lastslashidx + 1); |
127 | strcpy(work + lastslashidx, "olwork"); | |
38683db4 CB |
128 | if (mkdir(work, 0755) < 0) { |
129 | SYSERROR("error: mkdir %s", work); | |
130 | free(delta); | |
131 | free(work); | |
132 | return -1; | |
133 | } | |
134 | if (am_unpriv() && chown_mapped_root(work, conf) < 0) | |
135 | WARN("Failed to update ownership of %s", work); | |
136 | free(work); | |
137 | ||
138 | // the src will be 'overlayfs:lowerdir:upperdir' | |
139 | len = strlen(delta) + strlen(orig->src) + 12; | |
140 | new->src = malloc(len); | |
141 | if (!new->src) { | |
142 | free(delta); | |
143 | return -ENOMEM; | |
144 | } | |
145 | ret = snprintf(new->src, len, "overlayfs:%s:%s", orig->src, delta); | |
146 | free(delta); | |
147 | if (ret < 0 || ret >= len) | |
148 | return -ENOMEM; | |
149 | } else if (strcmp(orig->type, "overlayfs") == 0) { | |
5c484f79 CB |
150 | /* |
151 | * What exactly do we want to do here? I think we want to use | |
152 | * the original lowerdir, with a private delta which is | |
153 | * originally rsynced from the original delta | |
154 | */ | |
38683db4 CB |
155 | char *osrc, *odelta, *nsrc, *ndelta, *work; |
156 | char *lastslash; | |
157 | int len, ret, lastslashidx; | |
158 | if (!(osrc = strdup(orig->src))) | |
159 | return -22; | |
160 | nsrc = strchr(osrc, ':') + 1; | |
161 | if (nsrc != osrc + 10 || (odelta = strchr(nsrc, ':')) == NULL) { | |
162 | free(osrc); | |
163 | return -22; | |
164 | } | |
165 | *odelta = '\0'; | |
166 | odelta++; | |
167 | ndelta = dir_new_path(odelta, oldname, cname, oldpath, lxcpath); | |
168 | if (!ndelta) { | |
169 | free(osrc); | |
170 | return -ENOMEM; | |
171 | } | |
172 | if ((ret = mkdir(ndelta, 0755)) < 0 && errno != EEXIST) { | |
173 | SYSERROR("error: mkdir %s", ndelta); | |
174 | free(osrc); | |
175 | free(ndelta); | |
176 | return -1; | |
177 | } | |
178 | if (am_unpriv() && chown_mapped_root(ndelta, conf) < 0) | |
179 | WARN("Failed to update ownership of %s", ndelta); | |
180 | ||
5c484f79 CB |
181 | /* |
182 | * make workdir for overlayfs.v22 or higher (see comment further | |
183 | * up) | |
184 | */ | |
38683db4 CB |
185 | lastslash = strrchr(ndelta, '/'); |
186 | if (!lastslash) | |
187 | return -1; | |
188 | lastslash++; | |
189 | lastslashidx = lastslash - ndelta; | |
190 | ||
191 | work = malloc(lastslashidx + 7); | |
192 | if (!work) | |
193 | return -1; | |
5c484f79 CB |
194 | strncpy(work, ndelta, lastslashidx + 1); |
195 | strcpy(work + lastslashidx, "olwork"); | |
38683db4 CB |
196 | if ((mkdir(work, 0755) < 0) && errno != EEXIST) { |
197 | SYSERROR("error: mkdir %s", work); | |
198 | free(work); | |
199 | return -1; | |
200 | } | |
201 | if (am_unpriv() && chown_mapped_root(work, conf) < 0) | |
202 | WARN("Failed to update ownership of %s", work); | |
203 | free(work); | |
204 | ||
205 | len = strlen(nsrc) + strlen(ndelta) + 12; | |
206 | new->src = malloc(len); | |
207 | if (!new->src) { | |
208 | free(osrc); | |
209 | free(ndelta); | |
210 | return -ENOMEM; | |
211 | } | |
212 | ret = snprintf(new->src, len, "overlayfs:%s:%s", nsrc, ndelta); | |
213 | free(osrc); | |
214 | free(ndelta); | |
215 | if (ret < 0 || ret >= len) | |
216 | return -ENOMEM; | |
217 | ||
218 | return ovl_do_rsync(orig, new, conf); | |
219 | } else { | |
220 | ERROR("overlayfs clone of %s container is not yet supported", | |
5c484f79 CB |
221 | orig->type); |
222 | /* | |
223 | * Note, supporting this will require ovl_mount supporting | |
224 | * mounting of the underlay. No big deal, just needs to be done. | |
225 | */ | |
38683db4 CB |
226 | return -1; |
227 | } | |
228 | ||
229 | return 0; | |
230 | } | |
231 | ||
38683db4 CB |
232 | /* |
233 | * to say 'lxc-create -t ubuntu -n o1 -B overlayfs' means you want | |
234 | * $lxcpath/$lxcname/rootfs to have the created container, while all | |
235 | * changes after starting the container are written to | |
236 | * $lxcpath/$lxcname/delta0 | |
237 | */ | |
83e79752 | 238 | int ovl_create(struct bdev *bdev, const char *dest, const char *n, |
38683db4 CB |
239 | struct bdev_specs *specs) |
240 | { | |
241 | char *delta; | |
242 | int ret, len = strlen(dest), newlen; | |
243 | ||
5c484f79 | 244 | if (len < 8 || strcmp(dest + len - 7, "/rootfs") != 0) |
38683db4 CB |
245 | return -1; |
246 | ||
247 | if (!(bdev->dest = strdup(dest))) { | |
248 | ERROR("Out of memory"); | |
249 | return -1; | |
250 | } | |
251 | ||
5c484f79 | 252 | delta = alloca(strlen(dest) + 1); |
38683db4 | 253 | strcpy(delta, dest); |
5c484f79 | 254 | strcpy(delta + len - 6, "delta0"); |
38683db4 CB |
255 | |
256 | if (mkdir_p(delta, 0755) < 0) { | |
257 | ERROR("Error creating %s", delta); | |
258 | return -1; | |
259 | } | |
260 | ||
5c484f79 | 261 | // overlayfs:lower:upper |
38683db4 CB |
262 | newlen = (2 * len) + strlen("overlayfs:") + 2; |
263 | bdev->src = malloc(newlen); | |
264 | if (!bdev->src) { | |
265 | ERROR("Out of memory"); | |
266 | return -1; | |
267 | } | |
268 | ret = snprintf(bdev->src, newlen, "overlayfs:%s:%s", dest, delta); | |
269 | if (ret < 0 || ret >= newlen) | |
270 | return -1; | |
271 | ||
272 | if (mkdir_p(bdev->dest, 0755) < 0) { | |
273 | ERROR("Error creating %s", bdev->dest); | |
274 | return -1; | |
275 | } | |
276 | ||
277 | return 0; | |
278 | } | |
279 | ||
5c484f79 CB |
280 | int ovl_destroy(struct bdev *orig) |
281 | { | |
282 | char *upper; | |
283 | ||
284 | if (strncmp(orig->src, "overlayfs:", 10) != 0) | |
285 | return -22; | |
286 | upper = strchr(orig->src + 10, ':'); | |
287 | if (!upper) | |
288 | return -22; | |
289 | upper++; | |
290 | return lxc_rmdir_onedev(upper, NULL); | |
291 | } | |
292 | ||
293 | int ovl_detect(const char *path) | |
294 | { | |
295 | if (strncmp(path, "overlayfs:", 10) == 0) | |
296 | return 1; // take their word for it | |
297 | return 0; | |
298 | } | |
299 | ||
300 | char *ovl_getlower(char *p) | |
301 | { | |
302 | char *p1 = strchr(p, ':'); | |
303 | if (p1) | |
304 | *p1 = '\0'; | |
305 | return p; | |
306 | } | |
307 | ||
308 | int ovl_mount(struct bdev *bdev) | |
309 | { | |
310 | char *options, *dup, *lower, *upper; | |
311 | char *options_work, *work, *lastslash; | |
312 | int lastslashidx; | |
313 | int len, len2; | |
314 | unsigned long mntflags; | |
315 | char *mntdata; | |
316 | int ret, ret2; | |
317 | ||
318 | if (strcmp(bdev->type, "overlayfs")) | |
319 | return -22; | |
320 | if (!bdev->src || !bdev->dest) | |
321 | return -22; | |
322 | ||
323 | if (!ovl_name) | |
324 | ovl_name = ovl_detect_name(); | |
325 | ||
326 | /* | |
327 | * separately mount it first: | |
328 | * mount -t overlayfs * -oupperdir=${upper},lowerdir=${lower} lower dest | |
329 | */ | |
330 | dup = alloca(strlen(bdev->src) + 1); | |
331 | strcpy(dup, bdev->src); | |
332 | if (!(lower = strchr(dup, ':'))) | |
333 | return -22; | |
334 | if (!(upper = strchr(++lower, ':'))) | |
335 | return -22; | |
336 | *upper = '\0'; | |
337 | upper++; | |
338 | ||
339 | // if delta doesn't yet exist, create it | |
340 | if (mkdir_p(upper, 0755) < 0 && errno != EEXIST) | |
341 | return -22; | |
342 | ||
343 | /* | |
344 | * overlayfs.v22 or higher needs workdir option: | |
345 | * if upper is | |
346 | * /var/lib/lxc/c2/delta0 | |
347 | * then workdir is | |
348 | * /var/lib/lxc/c2/olwork | |
349 | */ | |
350 | lastslash = strrchr(upper, '/'); | |
351 | if (!lastslash) | |
352 | return -22; | |
353 | lastslash++; | |
354 | lastslashidx = lastslash - upper; | |
355 | ||
356 | work = alloca(lastslashidx + 7); | |
357 | strncpy(work, upper, lastslashidx + 7); | |
358 | strcpy(work + lastslashidx, "olwork"); | |
359 | ||
360 | if (parse_mntopts(bdev->mntopts, &mntflags, &mntdata) < 0) { | |
361 | free(mntdata); | |
362 | return -22; | |
363 | } | |
364 | ||
365 | if (mkdir_p(work, 0755) < 0 && errno != EEXIST) { | |
366 | free(mntdata); | |
367 | return -22; | |
368 | } | |
369 | ||
370 | /* | |
371 | * TODO: | |
372 | * We should check whether bdev->src is a blockdev but for now only | |
373 | * support overlays of a basic directory | |
374 | */ | |
375 | ||
376 | if (mntdata) { | |
377 | len = strlen(lower) + strlen(upper) + strlen("upperdir=,lowerdir=,") + strlen(mntdata) + 1; | |
378 | options = alloca(len); | |
379 | ret = snprintf(options, len, "upperdir=%s,lowerdir=%s,%s", upper, lower, mntdata); | |
380 | ||
381 | len2 = strlen(lower) + strlen(upper) + strlen(work) | |
382 | + strlen("upperdir=,lowerdir=,workdir=") + strlen(mntdata) + 1; | |
383 | options_work = alloca(len2); | |
384 | ret2 = snprintf(options, len2, "upperdir=%s,lowerdir=%s,workdir=%s,%s", | |
385 | upper, lower, work, mntdata); | |
386 | } else { | |
387 | len = strlen(lower) + strlen(upper) + strlen("upperdir=,lowerdir=") + 1; | |
388 | options = alloca(len); | |
389 | ret = snprintf(options, len, "upperdir=%s,lowerdir=%s", upper, lower); | |
390 | ||
391 | len2 = strlen(lower) + strlen(upper) + strlen(work) | |
392 | + strlen("upperdir=,lowerdir=,workdir=") + 1; | |
393 | options_work = alloca(len2); | |
394 | ret2 = snprintf(options_work, len2, "upperdir=%s,lowerdir=%s,workdir=%s", | |
395 | upper, lower, work); | |
396 | } | |
397 | ||
398 | if (ret < 0 || ret >= len || ret2 < 0 || ret2 >= len2) { | |
399 | free(mntdata); | |
400 | return -1; | |
401 | } | |
402 | ||
403 | // mount without workdir option for overlayfs before v21 | |
404 | ret = mount(lower, bdev->dest, ovl_name, MS_MGC_VAL | mntflags, options); | |
405 | if (ret < 0) { | |
406 | INFO("overlayfs: error mounting %s onto %s options %s. retry with workdir", | |
407 | lower, bdev->dest, options); | |
408 | ||
409 | // retry with workdir option for overlayfs v22 and higher | |
410 | ret = mount(lower, bdev->dest, ovl_name, MS_MGC_VAL | mntflags, options_work); | |
411 | if (ret < 0) | |
412 | SYSERROR("overlayfs: error mounting %s onto %s options %s", | |
413 | lower, bdev->dest, options_work); | |
414 | else | |
415 | INFO("overlayfs: mounted %s onto %s options %s", | |
416 | lower, bdev->dest, options_work); | |
417 | } else { | |
418 | INFO("overlayfs: mounted %s onto %s options %s", | |
419 | lower, bdev->dest, options); | |
420 | } | |
421 | return ret; | |
422 | } | |
423 | ||
424 | int ovl_umount(struct bdev *bdev) | |
425 | { | |
426 | if (strcmp(bdev->type, "overlayfs")) | |
427 | return -22; | |
428 | if (!bdev->src || !bdev->dest) | |
429 | return -22; | |
430 | return umount(bdev->dest); | |
431 | } | |
432 | ||
433 | char *ovl_get_rootfs(const char *rootfs_path, size_t *rootfslen) | |
434 | { | |
435 | char *rootfsdir = NULL; | |
436 | char *s1 = NULL; | |
437 | char *s2 = NULL; | |
438 | char *s3 = NULL; | |
439 | ||
440 | if (!rootfs_path || !rootfslen) | |
441 | return NULL; | |
442 | ||
443 | s1 = strdup(rootfs_path); | |
444 | if (!s1) | |
445 | return NULL; | |
446 | ||
447 | if ((s2 = strstr(s1, ":/"))) { | |
448 | s2 = s2 + 1; | |
449 | if ((s3 = strstr(s2, ":/"))) | |
450 | *s3 = '\0'; | |
451 | rootfsdir = strdup(s2); | |
452 | if (!rootfsdir) { | |
453 | free(s1); | |
454 | return NULL; | |
455 | } | |
456 | } | |
457 | ||
458 | if (!rootfsdir) | |
459 | rootfsdir = s1; | |
460 | else | |
461 | free(s1); | |
462 | ||
463 | *rootfslen = strlen(rootfsdir); | |
464 | ||
465 | return rootfsdir; | |
466 | } | |
467 | ||
468 | int ovl_mkdir(const struct mntent *mntent, const struct lxc_rootfs *rootfs, | |
469 | const char *lxc_name, const char *lxc_path) | |
470 | { | |
471 | char lxcpath[MAXPATHLEN]; | |
472 | char *rootfsdir = NULL; | |
473 | char *upperdir = NULL; | |
474 | char *workdir = NULL; | |
475 | char **opts = NULL; | |
476 | int fret = -1; | |
477 | int ret = 0; | |
478 | size_t arrlen = 0; | |
479 | size_t dirlen = 0; | |
480 | size_t i; | |
481 | size_t len = 0; | |
482 | size_t rootfslen = 0; | |
483 | ||
484 | if (!rootfs->path || !lxc_name || !lxc_path) | |
485 | goto err; | |
486 | ||
487 | opts = lxc_string_split(mntent->mnt_opts, ','); | |
488 | if (opts) | |
489 | arrlen = lxc_array_len((void **)opts); | |
490 | else | |
491 | goto err; | |
492 | ||
493 | for (i = 0; i < arrlen; i++) { | |
494 | if (strstr(opts[i], "upperdir=") && (strlen(opts[i]) > (len = strlen("upperdir=")))) | |
495 | upperdir = opts[i] + len; | |
496 | else if (strstr(opts[i], "workdir=") && (strlen(opts[i]) > (len = strlen("workdir=")))) | |
497 | workdir = opts[i] + len; | |
498 | } | |
499 | ||
500 | ret = snprintf(lxcpath, MAXPATHLEN, "%s/%s", lxc_path, lxc_name); | |
501 | if (ret < 0 || ret >= MAXPATHLEN) | |
502 | goto err; | |
503 | ||
504 | rootfsdir = ovl_get_rootfs(rootfs->path, &rootfslen); | |
505 | if (!rootfsdir) | |
506 | goto err; | |
507 | ||
508 | dirlen = strlen(lxcpath); | |
509 | ||
510 | /* | |
511 | * We neither allow users to create upperdirs and workdirs outside the | |
512 | * containerdir nor inside the rootfs. The latter might be debatable. | |
513 | */ | |
514 | if (upperdir) | |
515 | if ((strncmp(upperdir, lxcpath, dirlen) == 0) && (strncmp(upperdir, rootfsdir, rootfslen) != 0)) | |
516 | if (mkdir_p(upperdir, 0755) < 0) { | |
517 | WARN("Failed to create upperdir"); | |
518 | } | |
519 | ||
520 | if (workdir) | |
521 | if ((strncmp(workdir, lxcpath, dirlen) == 0) && (strncmp(workdir, rootfsdir, rootfslen) != 0)) | |
522 | if (mkdir_p(workdir, 0755) < 0) { | |
523 | WARN("Failed to create workdir"); | |
524 | } | |
525 | ||
526 | fret = 0; | |
527 | ||
528 | err: | |
529 | free(rootfsdir); | |
530 | lxc_free_array((void **)opts, free); | |
531 | return fret; | |
532 | } | |
533 | ||
38683db4 CB |
534 | /* |
535 | * To be called from lxcapi_clone() in lxccontainer.c: When we clone a container | |
536 | * with overlay lxc.mount.entry entries we need to update absolute paths for | |
537 | * upper- and workdir. This update is done in two locations: | |
538 | * lxc_conf->unexpanded_config and lxc_conf->mount_list. Both updates are done | |
539 | * independent of each other since lxc_conf->mountlist may container more mount | |
540 | * entries (e.g. from other included files) than lxc_conf->unexpanded_config . | |
541 | */ | |
83e79752 CB |
542 | int ovl_update_abs_paths(struct lxc_conf *lxc_conf, const char *lxc_path, |
543 | const char *lxc_name, const char *newpath, | |
544 | const char *newname) | |
38683db4 CB |
545 | { |
546 | char new_upper[MAXPATHLEN]; | |
547 | char new_work[MAXPATHLEN]; | |
548 | char old_upper[MAXPATHLEN]; | |
549 | char old_work[MAXPATHLEN]; | |
550 | char *cleanpath = NULL; | |
551 | int i; | |
552 | int fret = -1; | |
553 | int ret = 0; | |
554 | struct lxc_list *iterator; | |
555 | const char *ovl_dirs[] = {"br", "upperdir", "workdir"}; | |
556 | ||
557 | cleanpath = strdup(newpath); | |
558 | if (!cleanpath) | |
559 | goto err; | |
560 | ||
561 | remove_trailing_slashes(cleanpath); | |
562 | ||
5c484f79 CB |
563 | /* |
564 | * We have to update lxc_conf->unexpanded_config separately from | |
565 | * lxc_conf->mount_list. | |
566 | */ | |
38683db4 CB |
567 | for (i = 0; i < sizeof(ovl_dirs) / sizeof(ovl_dirs[0]); i++) { |
568 | if (!clone_update_unexp_ovl_paths(lxc_conf, lxc_path, newpath, | |
569 | lxc_name, newname, | |
570 | ovl_dirs[i])) | |
571 | goto err; | |
572 | } | |
573 | ||
574 | ret = snprintf(old_work, MAXPATHLEN, "workdir=%s/%s", lxc_path, lxc_name); | |
575 | if (ret < 0 || ret >= MAXPATHLEN) | |
576 | goto err; | |
577 | ||
578 | ret = snprintf(new_work, MAXPATHLEN, "workdir=%s/%s", cleanpath, newname); | |
579 | if (ret < 0 || ret >= MAXPATHLEN) | |
580 | goto err; | |
581 | ||
582 | lxc_list_for_each(iterator, &lxc_conf->mount_list) { | |
583 | char *mnt_entry = NULL; | |
584 | char *new_mnt_entry = NULL; | |
585 | char *tmp = NULL; | |
586 | char *tmp_mnt_entry = NULL; | |
587 | mnt_entry = iterator->elem; | |
588 | ||
589 | if (strstr(mnt_entry, "overlay")) | |
590 | tmp = "upperdir"; | |
591 | else if (strstr(mnt_entry, "aufs")) | |
592 | tmp = "br"; | |
593 | ||
594 | if (!tmp) | |
595 | continue; | |
596 | ||
597 | ret = snprintf(old_upper, MAXPATHLEN, "%s=%s/%s", tmp, lxc_path, lxc_name); | |
598 | if (ret < 0 || ret >= MAXPATHLEN) | |
599 | goto err; | |
600 | ||
601 | ret = snprintf(new_upper, MAXPATHLEN, "%s=%s/%s", tmp, cleanpath, newname); | |
602 | if (ret < 0 || ret >= MAXPATHLEN) | |
603 | goto err; | |
604 | ||
605 | if (strstr(mnt_entry, old_upper)) { | |
606 | tmp_mnt_entry = lxc_string_replace(old_upper, new_upper, mnt_entry); | |
607 | } | |
608 | ||
609 | if (strstr(mnt_entry, old_work)) { | |
610 | if (tmp_mnt_entry) | |
611 | new_mnt_entry = lxc_string_replace(old_work, new_work, tmp_mnt_entry); | |
612 | else | |
613 | new_mnt_entry = lxc_string_replace(old_work, new_work, mnt_entry); | |
614 | } | |
615 | ||
616 | if (new_mnt_entry) { | |
617 | free(iterator->elem); | |
618 | iterator->elem = strdup(new_mnt_entry); | |
619 | } else if (tmp_mnt_entry) { | |
620 | free(iterator->elem); | |
621 | iterator->elem = strdup(tmp_mnt_entry); | |
622 | } | |
623 | ||
624 | free(new_mnt_entry); | |
625 | free(tmp_mnt_entry); | |
626 | } | |
627 | ||
628 | fret = 0; | |
629 | err: | |
630 | free(cleanpath); | |
631 | return fret; | |
632 | } | |
633 | ||
5c484f79 CB |
634 | static int ovl_rsync(struct ovl_rsync_data *data) |
635 | { | |
636 | int ret; | |
637 | ||
638 | if (setgid(0) < 0) { | |
639 | ERROR("Failed to setgid to 0"); | |
640 | return -1; | |
641 | } | |
642 | if (setgroups(0, NULL) < 0) | |
643 | WARN("Failed to clear groups"); | |
644 | if (setuid(0) < 0) { | |
645 | ERROR("Failed to setuid to 0"); | |
646 | return -1; | |
647 | } | |
648 | ||
649 | if (unshare(CLONE_NEWNS) < 0) { | |
650 | SYSERROR("Unable to unshare mounts ns"); | |
651 | return -1; | |
652 | } | |
653 | if (detect_shared_rootfs()) { | |
654 | if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) { | |
655 | SYSERROR("Failed to make / rslave"); | |
656 | ERROR("Continuing..."); | |
657 | } | |
658 | } | |
659 | if (ovl_mount(data->orig) < 0) { | |
660 | ERROR("Failed mounting original container fs"); | |
661 | return -1; | |
662 | } | |
663 | if (ovl_mount(data->new) < 0) { | |
664 | ERROR("Failed mounting new container fs"); | |
665 | return -1; | |
666 | } | |
667 | ret = do_rsync(data->orig->dest, data->new->dest); | |
668 | ||
669 | ovl_umount(data->new); | |
670 | ovl_umount(data->orig); | |
671 | ||
672 | if (ret < 0) { | |
673 | ERROR("rsyncing %s to %s", data->orig->dest, data->new->dest); | |
674 | return -1; | |
675 | } | |
676 | ||
677 | return 0; | |
678 | } | |
679 | ||
680 | static char *ovl_detect_name(void) | |
681 | { | |
682 | char *v = "overlayfs"; | |
683 | char *line = NULL; | |
684 | size_t len = 0; | |
685 | FILE *f = fopen("/proc/filesystems", "r"); | |
686 | if (!f) | |
687 | return v; | |
688 | ||
689 | while (getline(&line, &len, f) != -1) { | |
690 | if (strcmp(line, "nodev\toverlay\n") == 0) { | |
691 | v = "overlay"; | |
692 | break; | |
693 | } | |
694 | } | |
695 | ||
696 | fclose(f); | |
697 | free(line); | |
698 | return v; | |
699 | } | |
700 | ||
701 | static int ovl_do_rsync(struct bdev *orig, struct bdev *new, struct lxc_conf *conf) | |
702 | { | |
703 | int ret = -1; | |
704 | struct ovl_rsync_data rdata; | |
705 | ||
706 | rdata.orig = orig; | |
707 | rdata.new = new; | |
708 | if (am_unpriv()) | |
709 | ret = userns_exec_1(conf, ovl_rsync_wrapper, &rdata); | |
710 | else | |
711 | ret = ovl_rsync(&rdata); | |
712 | if (ret) | |
713 | ERROR("copying overlayfs delta"); | |
714 | ||
715 | return ret; | |
716 | } | |
717 | ||
718 | static int ovl_rsync_wrapper(void *data) | |
719 | { | |
720 | struct ovl_rsync_data *arg = data; | |
721 | return ovl_rsync(arg); | |
722 | } | |
723 |