]>
Commit | Line | Data |
---|---|---|
cc73685d | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
38683db4 | 2 | |
d38dd64a CB |
3 | #ifndef _GNU_SOURCE |
4 | #define _GNU_SOURCE 1 | |
5 | #endif | |
38683db4 CB |
6 | #include <errno.h> |
7 | #include <stdio.h> | |
8 | #include <stdlib.h> | |
9 | #include <string.h> | |
10 | ||
12ae2a33 CB |
11 | #include "lxc.h" |
12 | ||
38683db4 | 13 | #include "conf.h" |
d38dd64a | 14 | #include "config.h" |
38683db4 CB |
15 | #include "confile.h" |
16 | #include "log.h" | |
6333c915 | 17 | #include "macro.h" |
e2c6671d | 18 | #include "memory_utils.h" |
28d832c4 CB |
19 | #include "overlay.h" |
20 | #include "rsync.h" | |
21 | #include "storage.h" | |
ba115175 | 22 | #include "storage_utils.h" |
38683db4 CB |
23 | #include "utils.h" |
24 | ||
10bc1861 | 25 | lxc_log_define(overlay, lxc); |
38683db4 | 26 | |
5c484f79 | 27 | static char *ovl_name; |
88232d3f | 28 | static char *ovl_version[] = {"overlay", "overlayfs"}; |
5c484f79 | 29 | |
5c484f79 | 30 | static char *ovl_detect_name(void); |
17a367d8 | 31 | static int ovl_do_rsync(const char *src, const char *dest, |
3188197d | 32 | struct lxc_conf *conf); |
88232d3f CB |
33 | static int ovl_remount_on_enodev(const char *lower, const char *target, |
34 | const char *name, unsigned long mountflags, | |
35 | const void *options); | |
38683db4 | 36 | |
10bc1861 | 37 | int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char *oldname, |
83e79752 CB |
38 | const char *cname, const char *oldpath, const char *lxcpath, |
39 | int snap, uint64_t newsize, struct lxc_conf *conf) | |
38683db4 | 40 | { |
241978fa | 41 | int ret; |
41dc7155 | 42 | const char *src; |
4d463f62 | 43 | |
38683db4 | 44 | if (!snap) { |
241978fa CB |
45 | ERROR("The overlay storage driver can only be used for " |
46 | "snapshots"); | |
38683db4 CB |
47 | return -22; |
48 | } | |
49 | ||
50 | if (!orig->src || !orig->dest) | |
51 | return -1; | |
52 | ||
241978fa | 53 | new->dest = must_make_path(lxcpath, cname, "rootfs", NULL); |
ba115175 | 54 | |
241978fa CB |
55 | ret = mkdir_p(new->dest, 0755); |
56 | if (ret < 0 && errno != EEXIST) { | |
57 | SYSERROR("Failed to create directory \"%s\"", new->dest); | |
38683db4 | 58 | return -1; |
241978fa | 59 | } |
38683db4 | 60 | |
0589d744 | 61 | if (am_guest_unpriv() || !list_empty(&conf->id_map)) { |
241978fa CB |
62 | ret = chown_mapped_root(new->dest, conf); |
63 | if (ret < 0) | |
64 | WARN("Failed to update ownership of %s", new->dest); | |
65 | } | |
38683db4 CB |
66 | |
67 | if (strcmp(orig->type, "dir") == 0) { | |
facdf925 CB |
68 | __do_free char *delta = NULL, *work = NULL; |
69 | int len; | |
38683db4 | 70 | |
facdf925 | 71 | delta = must_make_path(lxcpath, cname, LXC_OVERLAY_DELTA_PATH, NULL); |
38683db4 | 72 | |
facdf925 CB |
73 | ret = mkdir_p(delta, 0755); |
74 | if (ret < 0 && errno != EEXIST) | |
75 | return log_error_errno(-errno, errno, "Failed to create directory \"%s\"", delta); | |
241978fa | 76 | |
facdf925 CB |
77 | /* |
78 | * Make workdir for overlayfs.v22 or higher: | |
5c484f79 | 79 | * The workdir will be |
facdf925 | 80 | * /var/lib/lxc/c2/LXC_OVERLAY_WORK_PATH |
5c484f79 CB |
81 | * and is used to prepare files before they are atomically |
82 | * switched to the overlay destination. Workdirs need to be on | |
83 | * the same filesystem as the upperdir so it's OK for it to be | |
84 | * empty. | |
85 | */ | |
facdf925 | 86 | work = must_make_path(lxcpath, cname, LXC_OVERLAY_WORK_PATH, NULL); |
241978fa | 87 | |
facdf925 CB |
88 | ret = mkdir_p(work, 0755); |
89 | if (ret < 0 && errno != EEXIST) | |
90 | return log_error_errno(-errno, errno, "Failed to create directory \"%s\"", work); | |
241978fa | 91 | |
0589d744 | 92 | if (am_guest_unpriv() || !list_empty(&conf->id_map)) { |
facdf925 CB |
93 | __do_free char *lxc_overlay_delta_dir = NULL, |
94 | *lxc_overlay_private_dir = NULL; | |
95 | ||
96 | lxc_overlay_private_dir = must_make_path(lxcpath, cname, LXC_OVERLAY_PRIVATE_DIR, NULL); | |
97 | ret = chown_mapped_root(lxc_overlay_private_dir, conf); | |
98 | if (ret < 0) | |
99 | WARN("Failed to update ownership of %s", lxc_overlay_private_dir); | |
100 | ||
101 | lxc_overlay_delta_dir = must_make_path(lxcpath, cname, LXC_OVERLAY_DELTA_PATH, NULL); | |
102 | ret = chown_mapped_root(lxc_overlay_delta_dir, conf); | |
103 | if (ret < 0) | |
104 | WARN("Failed to update ownership of %s", lxc_overlay_delta_dir); | |
241978fa | 105 | |
241978fa CB |
106 | ret = chown_mapped_root(work, conf); |
107 | if (ret < 0) | |
108 | WARN("Failed to update ownership of %s", work); | |
109 | } | |
38683db4 | 110 | |
4d463f62 | 111 | src = lxc_storage_get_path(orig->src, orig->type); |
facdf925 CB |
112 | len = STRLITERALLEN("overlay") + STRLITERALLEN(":") + |
113 | strlen(src) + STRLITERALLEN(":") + strlen(delta) + 1; | |
114 | ||
38683db4 | 115 | new->src = malloc(len); |
facdf925 CB |
116 | if (!new->src) |
117 | return log_error_errno(-ENOMEM, ENOMEM, "Failed to allocate memory"); | |
241978fa | 118 | |
ba115175 | 119 | ret = snprintf(new->src, len, "overlay:%s:%s", src, delta); |
2048ac1a | 120 | if (ret < 0 || ret >= len) |
facdf925 | 121 | return log_error_errno(-EIO, EIO, "Failed to create string"); |
241978fa CB |
122 | } else if (!strcmp(orig->type, "overlayfs") || |
123 | !strcmp(orig->type, "overlay")) { | |
facdf925 CB |
124 | __do_free char *clean_old_path = NULL, *clean_new_path = NULL, |
125 | *ndelta = NULL, *osrc = NULL, *work = NULL; | |
126 | char *nsrc, *odelta, *s1, *s2, *s3; | |
70e95c8d | 127 | size_t len, name_len; |
ba115175 CB |
128 | |
129 | osrc = strdup(orig->src); | |
facdf925 CB |
130 | if (!osrc) |
131 | return log_error_errno(-22, ENOMEM, "Failed to duplicate string \"%s\"", orig->src); | |
ba115175 | 132 | |
09f6f8c4 | 133 | nsrc = osrc; |
facdf925 CB |
134 | if (strncmp(osrc, "overlay:", STRLITERALLEN("overlay:")) == 0) |
135 | nsrc += STRLITERALLEN("overlay:"); | |
136 | else if (strncmp(osrc, "overlayfs:", STRLITERALLEN("overlayfs:")) == 0) | |
137 | nsrc += STRLITERALLEN("overlayfs:"); | |
ba115175 CB |
138 | |
139 | odelta = strchr(nsrc, ':'); | |
facdf925 CB |
140 | if (!odelta) |
141 | return log_error_errno(-22, ENOENT, "Failed to find \":\" in \"%s\"", nsrc); | |
ba115175 | 142 | |
38683db4 CB |
143 | *odelta = '\0'; |
144 | odelta++; | |
facdf925 | 145 | ndelta = must_make_path(lxcpath, cname, LXC_OVERLAY_DELTA_PATH, NULL); |
ba115175 | 146 | |
facdf925 CB |
147 | ret = mkdir_p(ndelta, 0755); |
148 | if (ret < 0 && errno != EEXIST) | |
149 | return log_error_errno(-errno, errno, "Failed to create directory \"%s\"", ndelta); | |
38683db4 | 150 | |
241978fa CB |
151 | /* Make workdir for overlayfs.v22 or higher (See the comment |
152 | * further up.). | |
5c484f79 | 153 | */ |
facdf925 CB |
154 | work = must_make_path(lxcpath, cname, LXC_OVERLAY_WORK_PATH, NULL); |
155 | ret = mkdir_p(work, 0755); | |
156 | if (ret < 0 && errno != EEXIST) | |
157 | return log_error_errno(-errno, errno, "Failed to create directory \"%s\"", ndelta); | |
241978fa | 158 | |
0589d744 | 159 | if (am_guest_unpriv() || !list_empty(&conf->id_map)) { |
facdf925 CB |
160 | __do_free char *lxc_overlay_delta_dir = NULL, |
161 | *lxc_overlay_private_dir = NULL; | |
241978fa | 162 | |
facdf925 CB |
163 | lxc_overlay_private_dir = must_make_path(lxcpath, cname, LXC_OVERLAY_PRIVATE_DIR, NULL); |
164 | ret = chown_mapped_root(lxc_overlay_private_dir, conf); | |
165 | if (ret < 0) | |
166 | WARN("Failed to update ownership of %s", lxc_overlay_private_dir); | |
167 | ||
168 | lxc_overlay_delta_dir = must_make_path(lxcpath, cname, LXC_OVERLAY_DELTA_PATH, NULL); | |
169 | ret = chown_mapped_root(lxc_overlay_delta_dir, conf); | |
170 | if (ret < 0) | |
171 | WARN("Failed to update ownership of %s", lxc_overlay_delta_dir); | |
ba115175 | 172 | |
241978fa CB |
173 | ret = chown_mapped_root(work, conf); |
174 | if (ret < 0) | |
175 | WARN("Failed to update ownership of %s", work); | |
176 | } | |
38683db4 | 177 | |
facdf925 | 178 | len = STRLITERALLEN("overlay") + STRLITERALLEN(":") + strlen(nsrc) + STRLITERALLEN(":") + strlen(ndelta) + 1; |
38683db4 | 179 | new->src = malloc(len); |
facdf925 CB |
180 | if (!new->src) |
181 | return log_error_errno(-ENOMEM, ENOMEM, "Failed to allocate memory"); | |
182 | ||
ba115175 | 183 | ret = snprintf(new->src, len, "overlay:%s:%s", nsrc, ndelta); |
facdf925 CB |
184 | if (ret < 0 || (size_t)ret >= len) |
185 | return log_error_errno(-EIO, EIO, "Failed to create string"); | |
38683db4 | 186 | |
17a367d8 | 187 | ret = ovl_do_rsync(odelta, ndelta, conf); |
70e95c8d CB |
188 | if (ret < 0) |
189 | return -1; | |
190 | ||
191 | /* When we create an overlay snapshot of an overlay container in | |
192 | * the snapshot directory under "<lxcpath>/<name>/snaps/" we | |
193 | * don't need to record a dependency. If we would restore would | |
194 | * also fail. | |
195 | */ | |
28e54be1 | 196 | clean_old_path = path_simplify(oldpath); |
70e95c8d | 197 | if (!clean_old_path) |
facdf925 | 198 | return log_error_errno(-ENOMEM, ENOMEM, "Failed to create clean path for \"%s\"", oldpath); |
70e95c8d | 199 | |
28e54be1 | 200 | clean_new_path = path_simplify(lxcpath); |
facdf925 CB |
201 | if (!clean_new_path) |
202 | return log_error_errno(-ENOMEM, ENOMEM, "Failed to create clean path for \"%s\"", lxcpath); | |
70e95c8d CB |
203 | |
204 | s1 = strrchr(clean_old_path, '/'); | |
facdf925 CB |
205 | if (!s1) |
206 | return log_error_errno(-ENOENT, ENOENT, "Failed to detect \"/\" in string \"%s\"", clean_old_path); | |
70e95c8d CB |
207 | |
208 | s2 = strrchr(clean_new_path, '/'); | |
facdf925 CB |
209 | if (!s2) |
210 | return log_error_errno(-ENOENT, ENOENT, "Failed to detect \"/\" in string \"%s\"", clean_new_path); | |
70e95c8d | 211 | |
6333c915 | 212 | if (!strncmp(s1, "/snaps", STRLITERALLEN("/snaps"))) { |
70e95c8d CB |
213 | s1 = clean_new_path; |
214 | s2 = clean_old_path; | |
215 | s3 = (char *)cname; | |
6333c915 | 216 | } else if (!strncmp(s2, "/snaps", STRLITERALLEN("/snaps"))) { |
70e95c8d CB |
217 | s1 = clean_old_path; |
218 | s2 = clean_new_path; | |
219 | s3 = (char *)oldname; | |
70e95c8d | 220 | } else { |
70e95c8d CB |
221 | return 0; |
222 | } | |
223 | ||
7f022483 | 224 | len = strlen(s1); |
70e95c8d CB |
225 | if (!strncmp(s1, s2, len)) { |
226 | char *tmp; | |
227 | ||
228 | tmp = (char *)(s2 + len + 1); | |
facdf925 | 229 | if (*tmp == '\0') |
70e95c8d | 230 | return 0; |
70e95c8d CB |
231 | |
232 | name_len = strlen(s3); | |
facdf925 | 233 | if (strncmp(s3, tmp, name_len)) |
70e95c8d | 234 | return 0; |
70e95c8d | 235 | |
7f022483 | 236 | return LXC_CLONE_SNAPSHOT; |
70e95c8d CB |
237 | } |
238 | ||
70e95c8d | 239 | return 0; |
38683db4 | 240 | } else { |
facdf925 CB |
241 | /* |
242 | * Note, supporting this will require ovl_mount supporting | |
5c484f79 CB |
243 | * mounting of the underlay. No big deal, just needs to be done. |
244 | */ | |
facdf925 | 245 | return log_error_errno(-EINVAL, EINVAL, "overlay clone of %s container is not yet supported", orig->type); |
38683db4 CB |
246 | } |
247 | ||
248 | return 0; | |
249 | } | |
250 | ||
241978fa CB |
251 | /* To say "lxc-create -t ubuntu -n o1 -B overlay" means you want |
252 | * "<lxcpath>/<lxcname>/rootfs" to have the created container, while all changes | |
facdf925 | 253 | * after starting the container are written to "<lxcpath>/<lxcname>/LXC_OVERLAY_DELTA_PATH". |
38683db4 | 254 | */ |
10bc1861 | 255 | int ovl_create(struct lxc_storage *bdev, const char *dest, const char *n, |
facdf925 | 256 | struct bdev_specs *specs, const struct lxc_conf *conf) |
38683db4 | 257 | { |
facdf925 | 258 | __do_free char *delta = NULL, *tmp = NULL; |
241978fa | 259 | int ret; |
facdf925 | 260 | size_t len; |
38683db4 | 261 | |
241978fa | 262 | len = strlen(dest); |
facdf925 CB |
263 | if (len < 8 || strcmp(dest + len - STRLITERALLEN("/rootfs"), "/rootfs")) |
264 | return log_error_errno(-ENOENT, ENOENT, "Failed to detect \"/rootfs\" in \"%s\"", dest); | |
38683db4 | 265 | |
241978fa | 266 | bdev->dest = strdup(dest); |
facdf925 CB |
267 | if (!bdev->dest) |
268 | return log_error_errno(-ENOMEM, ENOMEM, "Failed to duplicate string \"%s\"", dest); | |
38683db4 | 269 | |
facdf925 CB |
270 | tmp = strndup(dest, len - STRLITERALLEN("/rootfs")); |
271 | if (!tmp) | |
272 | return log_error_errno(-ENOMEM, ENOMEM, "Failed to duplicate string \"%s\"", dest); | |
273 | ||
274 | delta = must_make_path(tmp, LXC_OVERLAY_DELTA_PATH, NULL); | |
38683db4 | 275 | |
241978fa | 276 | ret = mkdir_p(delta, 0755); |
facdf925 CB |
277 | if (ret < 0 && errno != EEXIST) |
278 | return log_error_errno(-errno, errno, "Failed to create directory \"%s\"", delta); | |
279 | ||
0589d744 | 280 | if (am_guest_unpriv() || !list_empty(&conf->id_map)) { |
facdf925 CB |
281 | __do_free char *lxc_overlay_private_dir = NULL; |
282 | ||
283 | lxc_overlay_private_dir = must_make_path(tmp, LXC_OVERLAY_PRIVATE_DIR, NULL); | |
284 | ret = chown_mapped_root(lxc_overlay_private_dir, conf); | |
285 | if (ret < 0) | |
286 | WARN("Failed to update ownership of %s", lxc_overlay_private_dir); | |
287 | ||
288 | ret = chown_mapped_root(delta, conf); | |
289 | if (ret < 0) | |
290 | WARN("Failed to update ownership of %s", delta); | |
38683db4 CB |
291 | } |
292 | ||
241978fa | 293 | /* overlay:lower:upper */ |
facdf925 CB |
294 | len = STRLITERALLEN("overlay") + STRLITERALLEN(":") + len + STRLITERALLEN(":") + strlen(delta) + 1; |
295 | bdev->src = malloc(len); | |
296 | if (!bdev->src) | |
297 | return log_error_errno(-ENOMEM, ENOMEM, "Failed to allocate memory"); | |
241978fa | 298 | |
facdf925 CB |
299 | ret = snprintf(bdev->src, len, "overlay:%s:%s", dest, delta); |
300 | if (ret < 0 || (size_t)ret >= len) | |
301 | return log_error_errno(-EIO, EIO, "Failed to create rootfs path"); | |
38683db4 | 302 | |
241978fa | 303 | ret = mkdir_p(bdev->dest, 0755); |
facdf925 CB |
304 | if (ret < 0 && errno != EEXIST) |
305 | return log_error_errno(-errno, errno, "Failed to create directory \"%s\"", bdev->dest); | |
38683db4 CB |
306 | |
307 | return 0; | |
308 | } | |
309 | ||
10bc1861 | 310 | int ovl_destroy(struct lxc_storage *orig) |
5c484f79 | 311 | { |
ba115175 | 312 | char *upper = orig->src; |
5c484f79 | 313 | |
17a367d8 CB |
314 | /* For an overlay container the rootfs is considered immutable |
315 | * and cannot be removed when restoring from a snapshot. | |
316 | */ | |
317 | if (orig->flags & LXC_STORAGE_INTERNAL_OVERLAY_RESTORE) | |
318 | return 0; | |
319 | ||
09f6f8c4 | 320 | if (strncmp(upper, "overlay:", 8) == 0) |
241978fa | 321 | upper += 8; |
09f6f8c4 | 322 | else if (strncmp(upper, "overlayfs:", 10) == 0) |
241978fa | 323 | upper += 10; |
ba115175 CB |
324 | |
325 | upper = strchr(upper, ':'); | |
5c484f79 CB |
326 | if (!upper) |
327 | return -22; | |
328 | upper++; | |
ba115175 | 329 | |
5c484f79 CB |
330 | return lxc_rmdir_onedev(upper, NULL); |
331 | } | |
332 | ||
3d2ae1e2 | 333 | bool ovl_detect(const char *path) |
5c484f79 | 334 | { |
09f6f8c4 | 335 | if (!strncmp(path, "overlay:", 8)) |
3d2ae1e2 | 336 | return true; |
f7ac4459 | 337 | |
09f6f8c4 | 338 | if (!strncmp(path, "overlayfs:", 10)) |
3d2ae1e2 | 339 | return true; |
f7ac4459 | 340 | |
3d2ae1e2 | 341 | return false; |
5c484f79 CB |
342 | } |
343 | ||
10bc1861 | 344 | int ovl_mount(struct lxc_storage *bdev) |
5c484f79 | 345 | { |
a08bfbe3 CB |
346 | __do_free char *options = NULL, *options_work = NULL; |
347 | unsigned long mntflags = 0; | |
348 | char *mntdata = NULL; | |
e2c6671d CB |
349 | char *tmp, *dup, *lower, *upper; |
350 | char *work, *lastslash; | |
241978fa | 351 | size_t len, len2; |
5c484f79 CB |
352 | int ret, ret2; |
353 | ||
ba115175 | 354 | if (strcmp(bdev->type, "overlay") && strcmp(bdev->type, "overlayfs")) |
5c484f79 | 355 | return -22; |
ba115175 | 356 | |
5c484f79 CB |
357 | if (!bdev->src || !bdev->dest) |
358 | return -22; | |
359 | ||
360 | if (!ovl_name) | |
361 | ovl_name = ovl_detect_name(); | |
362 | ||
241978fa CB |
363 | /* Separately mount it first: |
364 | * mount -t overlay * -o upperdir=${upper},lowerdir=${lower} lower dest | |
5c484f79 | 365 | */ |
241978fa | 366 | dup = strdup(bdev->src); |
facdf925 CB |
367 | if (!dup) |
368 | return log_error_errno(-ENOMEM, ENOMEM, "Failed to allocate memory"); | |
09f6f8c4 CB |
369 | upper = dup; |
370 | lower = dup; | |
241978fa | 371 | |
facdf925 CB |
372 | if (strncmp(dup, "overlay:", STRLITERALLEN("overlay:")) == 0) |
373 | lower += STRLITERALLEN("overlay:"); | |
374 | else if (strncmp(dup, "overlayfs:", STRLITERALLEN("overlayfs:")) == 0) | |
375 | lower += STRLITERALLEN("overlayfs:"); | |
09f6f8c4 CB |
376 | if (upper != lower) |
377 | upper = lower; | |
241978fa | 378 | |
09f6f8c4 CB |
379 | /* support multiple lower layers */ |
380 | while ((tmp = strstr(upper, ":/"))) { | |
381 | tmp++; | |
9208af16 CB |
382 | upper = tmp; |
383 | } | |
241978fa CB |
384 | |
385 | upper--; | |
386 | if (upper == lower) { | |
387 | free(dup); | |
5c484f79 | 388 | return -22; |
241978fa | 389 | } |
5c484f79 CB |
390 | *upper = '\0'; |
391 | upper++; | |
392 | ||
241978fa | 393 | /* if delta doesn't yet exist, create it */ |
adcdf4e4 | 394 | ret = mkdir_p(upper, 0755); |
241978fa CB |
395 | if (ret < 0 && errno != EEXIST) { |
396 | SYSERROR("Failed to create directory \"%s\"", upper); | |
397 | free(dup); | |
5c484f79 | 398 | return -22; |
241978fa | 399 | } |
5c484f79 | 400 | |
241978fa | 401 | /* overlayfs.v22 or higher needs workdir option: |
5c484f79 | 402 | * if upper is |
facdf925 | 403 | * /var/lib/lxc/c2/LXC_OVERLAY_DELTA_PATH |
5c484f79 | 404 | * then workdir is |
facdf925 | 405 | * /var/lib/lxc/c2/LXC_OVERLAY_WORK_PATH |
5c484f79 CB |
406 | */ |
407 | lastslash = strrchr(upper, '/'); | |
241978fa CB |
408 | if (!lastslash) { |
409 | ERROR("Failed to detect \"/\" in string \"%s\"", upper); | |
410 | free(dup); | |
5c484f79 | 411 | return -22; |
241978fa CB |
412 | } |
413 | ||
facdf925 CB |
414 | upper[lastslash - upper] = '\0'; |
415 | work = must_make_path(upper, LXC_OVERLAY_WORK_DIR, NULL); | |
416 | upper[lastslash - upper] = '/'; | |
241978fa | 417 | |
d94eb390 | 418 | ret = parse_mntopts_legacy(bdev->mntopts, &mntflags, &mntdata); |
241978fa CB |
419 | if (ret < 0) { |
420 | ERROR("Failed to parse mount options"); | |
5c484f79 | 421 | free(mntdata); |
241978fa CB |
422 | free(dup); |
423 | free(work); | |
5c484f79 CB |
424 | return -22; |
425 | } | |
426 | ||
241978fa CB |
427 | ret = mkdir_p(work, 0755); |
428 | if (ret < 0 && errno != EEXIST) { | |
429 | SYSERROR("Failed to create directory \"%s\"", work); | |
5c484f79 | 430 | free(mntdata); |
241978fa CB |
431 | free(dup); |
432 | free(work); | |
5c484f79 CB |
433 | return -22; |
434 | } | |
435 | ||
436 | /* | |
437 | * TODO: | |
438 | * We should check whether bdev->src is a blockdev but for now only | |
439 | * support overlays of a basic directory | |
440 | */ | |
441 | ||
442 | if (mntdata) { | |
241978fa CB |
443 | len = strlen(lower) + strlen(upper) + |
444 | strlen("upperdir=,lowerdir=,") + strlen(mntdata) + 1; | |
e2c6671d | 445 | options = must_realloc(NULL, len); |
241978fa CB |
446 | ret = snprintf(options, len, "upperdir=%s,lowerdir=%s,%s", |
447 | upper, lower, mntdata); | |
5c484f79 | 448 | |
241978fa CB |
449 | len2 = strlen(lower) + strlen(upper) + strlen(work) + |
450 | strlen("upperdir=,lowerdir=,workdir=") + | |
451 | strlen(mntdata) + 1; | |
e2c6671d | 452 | options_work = must_realloc(NULL, len2); |
241978fa CB |
453 | ret2 = snprintf(options, len2, |
454 | "upperdir=%s,lowerdir=%s,workdir=%s,%s", upper, | |
455 | lower, work, mntdata); | |
5c484f79 | 456 | } else { |
241978fa CB |
457 | len = strlen(lower) + strlen(upper) + |
458 | strlen("upperdir=,lowerdir=") + 1; | |
e2c6671d | 459 | options = must_realloc(NULL, len); |
241978fa CB |
460 | ret = snprintf(options, len, "upperdir=%s,lowerdir=%s", upper, |
461 | lower); | |
5c484f79 | 462 | |
241978fa CB |
463 | len2 = strlen(lower) + strlen(upper) + strlen(work) + |
464 | strlen("upperdir=,lowerdir=,workdir=") + 1; | |
e2c6671d | 465 | options_work = must_realloc(NULL, len2); |
241978fa CB |
466 | ret2 = snprintf(options_work, len2, |
467 | "upperdir=%s,lowerdir=%s,workdir=%s", upper, | |
468 | lower, work); | |
5c484f79 CB |
469 | } |
470 | ||
2048ac1a | 471 | if (ret < 0 || (size_t)ret >= len || ret2 < 0 || (size_t)ret2 >= len2) { |
241978fa | 472 | ERROR("Failed to create string"); |
5c484f79 | 473 | free(mntdata); |
241978fa CB |
474 | free(dup); |
475 | free(work); | |
5c484f79 CB |
476 | return -1; |
477 | } | |
478 | ||
241978fa | 479 | /* Assume we need a workdir as we are on a overlay version >= v22. */ |
88232d3f CB |
480 | ret = ovl_remount_on_enodev(lower, bdev->dest, ovl_name, |
481 | MS_MGC_VAL | mntflags, options_work); | |
5c484f79 | 482 | if (ret < 0) { |
7874d81a | 483 | SYSINFO("Failed to mount \"%s\" on \"%s\" with options \"%s\". " |
484 | "Retrying without workdir", | |
485 | lower, bdev->dest, options_work); | |
5c484f79 | 486 | |
241978fa CB |
487 | /* Assume we cannot use a workdir as we are on a version <= v21. |
488 | */ | |
88232d3f | 489 | ret = ovl_remount_on_enodev(lower, bdev->dest, ovl_name, |
241978fa | 490 | MS_MGC_VAL | mntflags, options); |
5c484f79 | 491 | if (ret < 0) |
b5be6a7c | 492 | SYSERROR("Failed to mount \"%s\" on \"%s\" with options \"%s\"", |
493 | lower, bdev->dest, options); | |
5c484f79 | 494 | else |
241978fa | 495 | INFO("Mounted \"%s\" on \"%s\" with options \"%s\"", |
88232d3f | 496 | lower, bdev->dest, options); |
5c484f79 | 497 | } else { |
241978fa | 498 | INFO("Mounted \"%s\" on \"%s\" with options \"%s\"", lower, |
88232d3f | 499 | bdev->dest, options_work); |
5c484f79 | 500 | } |
241978fa CB |
501 | |
502 | free(dup); | |
503 | free(work); | |
5c484f79 CB |
504 | return ret; |
505 | } | |
506 | ||
10bc1861 | 507 | int ovl_umount(struct lxc_storage *bdev) |
5c484f79 | 508 | { |
241978fa CB |
509 | int ret; |
510 | ||
ba115175 | 511 | if (strcmp(bdev->type, "overlay") && strcmp(bdev->type, "overlayfs")) |
5c484f79 | 512 | return -22; |
ba115175 | 513 | |
5c484f79 CB |
514 | if (!bdev->src || !bdev->dest) |
515 | return -22; | |
ba115175 | 516 | |
241978fa CB |
517 | ret = umount(bdev->dest); |
518 | if (ret < 0) | |
519 | SYSERROR("Failed to unmount \"%s\"", bdev->dest); | |
520 | else | |
521 | TRACE("Unmounted \"%s\"", bdev->dest); | |
522 | ||
523 | return ret; | |
5c484f79 CB |
524 | } |
525 | ||
41dc7155 | 526 | const char *ovl_get_lower(const char *rootfs_path) |
ba115175 | 527 | { |
41dc7155 | 528 | const char *s1 = rootfs_path; |
241978fa | 529 | |
41dc7155 CB |
530 | if (strncmp(rootfs_path, "overlay:", 8) == 0) |
531 | s1 += 8; | |
532 | else if (strncmp(rootfs_path, "overlayfs:", 10) == 0) | |
533 | s1 += 10; | |
ba115175 CB |
534 | |
535 | s1 = strstr(s1, ":/"); | |
536 | if (!s1) | |
537 | return NULL; | |
538 | s1++; | |
539 | ||
540 | return s1; | |
541 | } | |
542 | ||
5c484f79 CB |
543 | char *ovl_get_rootfs(const char *rootfs_path, size_t *rootfslen) |
544 | { | |
545 | char *rootfsdir = NULL; | |
546 | char *s1 = NULL; | |
547 | char *s2 = NULL; | |
548 | char *s3 = NULL; | |
549 | ||
550 | if (!rootfs_path || !rootfslen) | |
551 | return NULL; | |
552 | ||
553 | s1 = strdup(rootfs_path); | |
554 | if (!s1) | |
555 | return NULL; | |
556 | ||
41dc7155 CB |
557 | s2 = s1; |
558 | if (strncmp(rootfs_path, "overlay:", 8) == 0) | |
559 | s2 += 8; | |
560 | else if (strncmp(rootfs_path, "overlayfs:", 10) == 0) | |
561 | s2 += 10; | |
562 | ||
563 | s3 = strstr(s2, ":/"); | |
564 | if (s3) | |
565 | *s3 = '\0'; | |
566 | ||
567 | rootfsdir = strdup(s2); | |
a61b4e85 | 568 | free(s1); |
5c484f79 | 569 | if (!rootfsdir) |
a61b4e85 | 570 | return NULL; |
5c484f79 CB |
571 | |
572 | *rootfslen = strlen(rootfsdir); | |
573 | ||
574 | return rootfsdir; | |
575 | } | |
576 | ||
577 | int ovl_mkdir(const struct mntent *mntent, const struct lxc_rootfs *rootfs, | |
578 | const char *lxc_name, const char *lxc_path) | |
579 | { | |
339de297 | 580 | char lxcpath[PATH_MAX]; |
241978fa CB |
581 | char **opts; |
582 | int ret; | |
180c477a | 583 | size_t arrlen, i, len, rootfslen; |
5c484f79 | 584 | int fret = -1; |
180c477a | 585 | size_t dirlen = 0; |
241978fa CB |
586 | char *rootfs_dir = NULL, *rootfs_path = NULL, *upperdir = NULL, |
587 | *workdir = NULL; | |
5c484f79 | 588 | |
9769034f CB |
589 | /* When rootfs == NULL we have a container without a rootfs. */ |
590 | if (rootfs && rootfs->path) | |
591 | rootfs_path = rootfs->path; | |
5c484f79 CB |
592 | |
593 | opts = lxc_string_split(mntent->mnt_opts, ','); | |
594 | if (opts) | |
595 | arrlen = lxc_array_len((void **)opts); | |
596 | else | |
597 | goto err; | |
598 | ||
599 | for (i = 0; i < arrlen; i++) { | |
241978fa CB |
600 | if (strstr(opts[i], "upperdir=") && |
601 | (strlen(opts[i]) > (len = strlen("upperdir=")))) | |
5c484f79 | 602 | upperdir = opts[i] + len; |
241978fa CB |
603 | else if (strstr(opts[i], "workdir=") && |
604 | (strlen(opts[i]) > (len = strlen("workdir=")))) | |
5c484f79 CB |
605 | workdir = opts[i] + len; |
606 | } | |
607 | ||
9769034f | 608 | if (rootfs_path) { |
339de297 CB |
609 | ret = snprintf(lxcpath, PATH_MAX, "%s/%s", lxc_path, lxc_name); |
610 | if (ret < 0 || ret >= PATH_MAX) | |
9769034f | 611 | goto err; |
5c484f79 | 612 | |
241978fa CB |
613 | rootfs_dir = ovl_get_rootfs(rootfs_path, &rootfslen); |
614 | if (!rootfs_dir) | |
9769034f | 615 | goto err; |
5c484f79 | 616 | |
9769034f CB |
617 | dirlen = strlen(lxcpath); |
618 | } | |
5c484f79 CB |
619 | |
620 | /* | |
621 | * We neither allow users to create upperdirs and workdirs outside the | |
622 | * containerdir nor inside the rootfs. The latter might be debatable. | |
9769034f | 623 | * When we have a container without a rootfs we skip the checks. |
5c484f79 | 624 | */ |
9769034f CB |
625 | ret = 0; |
626 | if (upperdir) { | |
627 | if (!rootfs_path) | |
628 | ret = mkdir_p(upperdir, 0755); | |
241978fa CB |
629 | else if (!strncmp(upperdir, lxcpath, dirlen) && |
630 | strncmp(upperdir, rootfs_dir, rootfslen)) | |
9769034f | 631 | ret = mkdir_p(upperdir, 0755); |
a24c5678 | 632 | |
9769034f | 633 | if (ret < 0) |
a24c5678 | 634 | SYSWARN("Failed to create directory \"%s\"", upperdir); |
9769034f CB |
635 | } |
636 | ||
637 | ret = 0; | |
638 | if (workdir) { | |
639 | if (!rootfs_path) | |
640 | ret = mkdir_p(workdir, 0755); | |
241978fa CB |
641 | else if (!strncmp(workdir, lxcpath, dirlen) && |
642 | strncmp(workdir, rootfs_dir, rootfslen)) | |
9769034f | 643 | ret = mkdir_p(workdir, 0755); |
a24c5678 | 644 | |
9769034f | 645 | if (ret < 0) |
a24c5678 | 646 | SYSWARN("Failed to create directory \"%s\"", workdir); |
9769034f | 647 | } |
5c484f79 CB |
648 | |
649 | fret = 0; | |
650 | ||
651 | err: | |
241978fa | 652 | free(rootfs_dir); |
5c484f79 CB |
653 | lxc_free_array((void **)opts, free); |
654 | return fret; | |
655 | } | |
656 | ||
241978fa | 657 | /* To be called from lxcapi_clone() in lxccontainer.c: When we clone a container |
38683db4 CB |
658 | * with overlay lxc.mount.entry entries we need to update absolute paths for |
659 | * upper- and workdir. This update is done in two locations: | |
be0bc4d1 | 660 | * lxc_conf->unexpanded_config and lxc_conf->mount_entries. Both updates are done |
241978fa CB |
661 | * independent of each other since lxc_conf->mountlist may contain more mount |
662 | * entries (e.g. from other included files) than lxc_conf->unexpanded_config. | |
38683db4 | 663 | */ |
83e79752 CB |
664 | int ovl_update_abs_paths(struct lxc_conf *lxc_conf, const char *lxc_path, |
665 | const char *lxc_name, const char *newpath, | |
666 | const char *newname) | |
38683db4 | 667 | { |
339de297 CB |
668 | char new_upper[PATH_MAX], new_work[PATH_MAX], old_upper[PATH_MAX], |
669 | old_work[PATH_MAX]; | |
84760c11 | 670 | size_t i; |
be0bc4d1 | 671 | struct string_entry *entry; |
241978fa | 672 | char *cleanpath = NULL; |
38683db4 CB |
673 | int fret = -1; |
674 | int ret = 0; | |
38683db4 CB |
675 | const char *ovl_dirs[] = {"br", "upperdir", "workdir"}; |
676 | ||
677 | cleanpath = strdup(newpath); | |
678 | if (!cleanpath) | |
679 | goto err; | |
680 | ||
681 | remove_trailing_slashes(cleanpath); | |
682 | ||
5c484f79 CB |
683 | /* |
684 | * We have to update lxc_conf->unexpanded_config separately from | |
be0bc4d1 | 685 | * lxc_conf->mount_entries. |
5c484f79 | 686 | */ |
38683db4 CB |
687 | for (i = 0; i < sizeof(ovl_dirs) / sizeof(ovl_dirs[0]); i++) { |
688 | if (!clone_update_unexp_ovl_paths(lxc_conf, lxc_path, newpath, | |
689 | lxc_name, newname, | |
690 | ovl_dirs[i])) | |
691 | goto err; | |
692 | } | |
693 | ||
241978fa | 694 | ret = |
339de297 CB |
695 | snprintf(old_work, PATH_MAX, "workdir=%s/%s", lxc_path, lxc_name); |
696 | if (ret < 0 || ret >= PATH_MAX) | |
38683db4 CB |
697 | goto err; |
698 | ||
241978fa | 699 | ret = |
339de297 CB |
700 | snprintf(new_work, PATH_MAX, "workdir=%s/%s", cleanpath, newname); |
701 | if (ret < 0 || ret >= PATH_MAX) | |
38683db4 CB |
702 | goto err; |
703 | ||
be0bc4d1 | 704 | list_for_each_entry(entry, &lxc_conf->mount_entries, head) { |
241978fa CB |
705 | char *mnt_entry = NULL, *new_mnt_entry = NULL, *tmp = NULL, |
706 | *tmp_mnt_entry = NULL; | |
707 | ||
be0bc4d1 | 708 | mnt_entry = entry->val; |
38683db4 CB |
709 | |
710 | if (strstr(mnt_entry, "overlay")) | |
711 | tmp = "upperdir"; | |
38683db4 CB |
712 | if (!tmp) |
713 | continue; | |
714 | ||
339de297 | 715 | ret = snprintf(old_upper, PATH_MAX, "%s=%s/%s", tmp, lxc_path, |
241978fa | 716 | lxc_name); |
339de297 | 717 | if (ret < 0 || ret >= PATH_MAX) |
38683db4 CB |
718 | goto err; |
719 | ||
339de297 | 720 | ret = snprintf(new_upper, PATH_MAX, "%s=%s/%s", tmp, |
241978fa | 721 | cleanpath, newname); |
339de297 | 722 | if (ret < 0 || ret >= PATH_MAX) |
38683db4 CB |
723 | goto err; |
724 | ||
be0bc4d1 CB |
725 | if (strstr(mnt_entry, old_upper)) |
726 | tmp_mnt_entry = lxc_string_replace(old_upper, new_upper, mnt_entry); | |
38683db4 CB |
727 | |
728 | if (strstr(mnt_entry, old_work)) { | |
729 | if (tmp_mnt_entry) | |
be0bc4d1 CB |
730 | new_mnt_entry = lxc_string_replace(old_work, |
731 | new_work, | |
732 | tmp_mnt_entry); | |
38683db4 | 733 | else |
be0bc4d1 CB |
734 | new_mnt_entry = lxc_string_replace(old_work, |
735 | new_work, | |
736 | mnt_entry); | |
38683db4 CB |
737 | } |
738 | ||
739 | if (new_mnt_entry) { | |
be0bc4d1 CB |
740 | free(entry->val); |
741 | entry->val = strdup(new_mnt_entry); | |
38683db4 | 742 | } else if (tmp_mnt_entry) { |
be0bc4d1 CB |
743 | free(entry->val); |
744 | entry->val = strdup(tmp_mnt_entry); | |
38683db4 CB |
745 | } |
746 | ||
747 | free(new_mnt_entry); | |
748 | free(tmp_mnt_entry); | |
749 | } | |
750 | ||
751 | fret = 0; | |
752 | err: | |
753 | free(cleanpath); | |
754 | return fret; | |
755 | } | |
756 | ||
88232d3f CB |
757 | static int ovl_remount_on_enodev(const char *lower, const char *target, |
758 | const char *name, unsigned long mountflags, | |
759 | const void *options) | |
760 | { | |
241978fa CB |
761 | int ret; |
762 | ret = mount(lower, target, ovl_name, MS_MGC_VAL | mountflags, options); | |
763 | if (ret < 0 && errno == ENODEV) /* Try other module name. */ | |
88232d3f CB |
764 | ret = mount(lower, target, |
765 | ovl_name == ovl_version[0] ? ovl_version[1] | |
766 | : ovl_version[0], | |
767 | MS_MGC_VAL | mountflags, options); | |
241978fa | 768 | return ret; |
5c484f79 CB |
769 | } |
770 | ||
771 | static char *ovl_detect_name(void) | |
772 | { | |
241978fa | 773 | FILE *f; |
88232d3f | 774 | char *v = ovl_version[0]; |
5c484f79 CB |
775 | char *line = NULL; |
776 | size_t len = 0; | |
241978fa CB |
777 | |
778 | f = fopen("/proc/filesystems", "r"); | |
5c484f79 CB |
779 | if (!f) |
780 | return v; | |
781 | ||
782 | while (getline(&line, &len, f) != -1) { | |
88232d3f CB |
783 | if (strcmp(line, "nodev\toverlayfs\n") == 0) { |
784 | v = ovl_version[1]; | |
5c484f79 CB |
785 | break; |
786 | } | |
787 | } | |
788 | ||
789 | fclose(f); | |
790 | free(line); | |
791 | return v; | |
792 | } | |
793 | ||
17a367d8 | 794 | static int ovl_do_rsync(const char *src, const char *dest, |
241978fa | 795 | struct lxc_conf *conf) |
5c484f79 CB |
796 | { |
797 | int ret = -1; | |
17a367d8 | 798 | struct rsync_data_char rdata = {0}; |
339de297 | 799 | char cmd_output[PATH_MAX] = {0}; |
5c484f79 | 800 | |
17a367d8 CB |
801 | rdata.src = (char *)src; |
802 | rdata.dest = (char *)dest; | |
e0010464 | 803 | if (am_guest_unpriv()) |
5c05427a CB |
804 | ret = userns_exec_full(conf, lxc_rsync_exec_wrapper, &rdata, |
805 | "lxc_rsync_exec_wrapper"); | |
17a367d8 | 806 | else |
241978fa CB |
807 | ret = run_command(cmd_output, sizeof(cmd_output), |
808 | lxc_rsync_exec_wrapper, (void *)&rdata); | |
17a367d8 CB |
809 | if (ret < 0) |
810 | ERROR("Failed to rsync from \"%s\" into \"%s\"%s%s", src, dest, | |
811 | cmd_output[0] != '\0' ? ": " : "", | |
812 | cmd_output[0] != '\0' ? cmd_output : ""); | |
5c484f79 CB |
813 | |
814 | return ret; | |
815 | } |