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