]>
Commit | Line | Data |
---|---|---|
00f0a1f8 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 <stdint.h> | |
27 | #include <string.h> | |
28 | #include <sys/stat.h> | |
29 | #include <sys/types.h> | |
30 | ||
28d832c4 | 31 | #include "aufs.h" |
00f0a1f8 | 32 | #include "log.h" |
28d832c4 CB |
33 | #include "rsync.h" |
34 | #include "storage.h" | |
00f0a1f8 CB |
35 | #include "utils.h" |
36 | ||
10bc1861 | 37 | lxc_log_define(aufs, lxc); |
00f0a1f8 CB |
38 | |
39 | /* the bulk of this needs to become a common helper */ | |
40 | extern char *dir_new_path(char *src, const char *oldname, const char *name, | |
41 | const char *oldpath, const char *lxcpath); | |
42 | ||
79f4b264 CB |
43 | int lxc_rsync_delta(struct rsync_data_char *data) |
44 | { | |
45 | int ret; | |
46 | ||
47 | ret = lxc_switch_uid_gid(0, 0); | |
48 | if (ret < 0) | |
49 | return -1; | |
50 | ||
51 | ret = lxc_setgroups(0, NULL); | |
52 | if (ret < 0) | |
53 | return -1; | |
54 | ||
55 | ret = lxc_rsync_exec(data->src, data->dest); | |
56 | if (ret < 0) { | |
57 | ERROR("Failed to rsync from \"%s\" into \"%s\"", data->src, | |
58 | data->dest); | |
59 | return -1; | |
60 | } | |
61 | ||
62 | return 0; | |
63 | } | |
64 | ||
65 | int lxc_rsync_delta_wrapper(void *data) | |
66 | { | |
67 | struct rsync_data_char *arg = data; | |
68 | return lxc_rsync_delta(arg); | |
69 | } | |
70 | ||
10bc1861 CB |
71 | int aufs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, |
72 | const char *oldname, const char *cname, const char *oldpath, | |
73 | const char *lxcpath, int snap, uint64_t newsize, | |
74 | struct lxc_conf *conf) | |
00f0a1f8 | 75 | { |
79f4b264 CB |
76 | char cmd_output[MAXPATHLEN]; |
77 | ||
00f0a1f8 CB |
78 | if (!snap) { |
79 | ERROR("aufs is only for snapshot clones"); | |
80 | return -22; | |
81 | } | |
82 | ||
83 | if (!orig->src || !orig->dest) | |
84 | return -1; | |
85 | ||
86 | new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath); | |
87 | if (!new->dest) | |
88 | return -1; | |
89 | if (mkdir_p(new->dest, 0755) < 0) | |
90 | return -1; | |
91 | ||
5384e99d | 92 | if (am_host_unpriv() && chown_mapped_root(new->dest, conf) < 0) |
00f0a1f8 CB |
93 | WARN("Failed to update ownership of %s", new->dest); |
94 | ||
95 | if (strcmp(orig->type, "dir") == 0) { | |
96 | char *delta, *lastslash; | |
97 | int ret, len, lastslashidx; | |
98 | ||
99 | // if we have /var/lib/lxc/c2/rootfs, then delta will be | |
100 | // /var/lib/lxc/c2/delta0 | |
101 | lastslash = strrchr(new->dest, '/'); | |
102 | if (!lastslash) | |
103 | return -22; | |
104 | if (strlen(lastslash) < 7) | |
105 | return -22; | |
106 | lastslash++; | |
107 | lastslashidx = lastslash - new->dest; | |
108 | ||
109 | delta = malloc(lastslashidx + 7); | |
110 | if (!delta) | |
111 | return -1; | |
112 | strncpy(delta, new->dest, lastslashidx+1); | |
113 | strcpy(delta+lastslashidx, "delta0"); | |
114 | if ((ret = mkdir(delta, 0755)) < 0) { | |
115 | SYSERROR("error: mkdir %s", delta); | |
116 | free(delta); | |
117 | return -1; | |
118 | } | |
5384e99d | 119 | if (am_host_unpriv() && chown_mapped_root(delta, conf) < 0) |
00f0a1f8 CB |
120 | WARN("Failed to update ownership of %s", delta); |
121 | ||
122 | // the src will be 'aufs:lowerdir:upperdir' | |
123 | len = strlen(delta) + strlen(orig->src) + 12; | |
124 | new->src = malloc(len); | |
125 | if (!new->src) { | |
126 | free(delta); | |
127 | return -ENOMEM; | |
128 | } | |
129 | ret = snprintf(new->src, len, "aufs:%s:%s", orig->src, delta); | |
130 | free(delta); | |
131 | if (ret < 0 || ret >= len) | |
132 | return -ENOMEM; | |
133 | } else if (strcmp(orig->type, "aufs") == 0) { | |
134 | // What exactly do we want to do here? | |
135 | // I think we want to use the original lowerdir, with a | |
136 | // private delta which is originally rsynced from the | |
137 | // original delta | |
138 | char *osrc, *odelta, *nsrc, *ndelta; | |
139 | int len, ret; | |
140 | if (!(osrc = strdup(orig->src))) | |
141 | return -22; | |
142 | nsrc = strchr(osrc, ':') + 1; | |
143 | if (nsrc != osrc + 5 || (odelta = strchr(nsrc, ':')) == NULL) { | |
144 | free(osrc); | |
145 | return -22; | |
146 | } | |
147 | *odelta = '\0'; | |
148 | odelta++; | |
149 | ndelta = dir_new_path(odelta, oldname, cname, oldpath, lxcpath); | |
150 | if (!ndelta) { | |
151 | free(osrc); | |
152 | return -ENOMEM; | |
153 | } | |
154 | if ((ret = mkdir(ndelta, 0755)) < 0 && errno != EEXIST) { | |
155 | SYSERROR("error: mkdir %s", ndelta); | |
156 | free(osrc); | |
157 | free(ndelta); | |
158 | return -1; | |
159 | } | |
5384e99d | 160 | if (am_host_unpriv() && chown_mapped_root(ndelta, conf) < 0) |
00f0a1f8 CB |
161 | WARN("Failed to update ownership of %s", ndelta); |
162 | ||
163 | struct rsync_data_char rdata; | |
164 | rdata.src = odelta; | |
165 | rdata.dest = ndelta; | |
5384e99d | 166 | if (am_host_unpriv()) |
5c05427a CB |
167 | ret = userns_exec_full(conf, lxc_rsync_delta_wrapper, |
168 | &rdata, "lxc_rsync_delta_wrapper"); | |
00f0a1f8 | 169 | else |
79f4b264 CB |
170 | ret = run_command(cmd_output, sizeof(cmd_output), |
171 | lxc_rsync_delta_wrapper, | |
172 | (void *)&rdata); | |
00f0a1f8 CB |
173 | if (ret) { |
174 | free(osrc); | |
175 | free(ndelta); | |
176 | ERROR("copying aufs delta"); | |
177 | return -1; | |
178 | } | |
179 | len = strlen(nsrc) + strlen(ndelta) + 12; | |
180 | new->src = malloc(len); | |
181 | if (!new->src) { | |
182 | free(osrc); | |
183 | free(ndelta); | |
184 | return -ENOMEM; | |
185 | } | |
186 | ret = snprintf(new->src, len, "aufs:%s:%s", nsrc, ndelta); | |
187 | free(osrc); | |
188 | free(ndelta); | |
189 | if (ret < 0 || ret >= len) | |
190 | return -ENOMEM; | |
191 | } else { | |
192 | ERROR("aufs clone of %s container is not yet supported", | |
193 | orig->type); | |
194 | // Note, supporting this will require aufs_mount supporting | |
195 | // mounting of the underlay. No big deal, just needs to be done. | |
196 | return -1; | |
197 | } | |
198 | ||
199 | return 0; | |
200 | } | |
201 | ||
202 | /* | |
203 | * to say 'lxc-create -t ubuntu -n o1 -B aufs' means you want | |
204 | * $lxcpath/$lxcname/rootfs to have the created container, while all | |
205 | * changes after starting the container are written to | |
206 | * $lxcpath/$lxcname/delta0 | |
207 | */ | |
10bc1861 | 208 | int aufs_create(struct lxc_storage *bdev, const char *dest, const char *n, |
00f0a1f8 CB |
209 | struct bdev_specs *specs) |
210 | { | |
211 | char *delta; | |
212 | int ret, len = strlen(dest), newlen; | |
213 | ||
214 | if (len < 8 || strcmp(dest+len-7, "/rootfs") != 0) | |
215 | return -1; | |
216 | ||
217 | if (!(bdev->dest = strdup(dest))) { | |
218 | ERROR("Out of memory"); | |
219 | return -1; | |
220 | } | |
221 | ||
222 | delta = alloca(strlen(dest)+1); | |
223 | strcpy(delta, dest); | |
224 | strcpy(delta+len-6, "delta0"); | |
225 | ||
226 | if (mkdir_p(delta, 0755) < 0) { | |
227 | ERROR("Error creating %s", delta); | |
228 | return -1; | |
229 | } | |
230 | ||
231 | /* aufs:lower:upper */ | |
232 | newlen = (2 * len) + strlen("aufs:") + 2; | |
233 | bdev->src = malloc(newlen); | |
234 | if (!bdev->src) { | |
235 | ERROR("Out of memory"); | |
236 | return -1; | |
237 | } | |
238 | ret = snprintf(bdev->src, newlen, "aufs:%s:%s", dest, delta); | |
239 | if (ret < 0 || ret >= newlen) | |
240 | return -1; | |
241 | ||
242 | if (mkdir_p(bdev->dest, 0755) < 0) { | |
243 | ERROR("Error creating %s", bdev->dest); | |
244 | return -1; | |
245 | } | |
246 | ||
247 | return 0; | |
248 | } | |
249 | ||
10bc1861 | 250 | int aufs_destroy(struct lxc_storage *orig) |
00f0a1f8 CB |
251 | { |
252 | char *upper; | |
253 | ||
254 | if (strncmp(orig->src, "aufs:", 5) != 0) | |
255 | return -22; | |
256 | upper = strchr(orig->src + 5, ':'); | |
257 | if (!upper) | |
258 | return -22; | |
259 | upper++; | |
260 | return lxc_rmdir_onedev(upper, NULL); | |
261 | } | |
262 | ||
3d2ae1e2 | 263 | bool aufs_detect(const char *path) |
00f0a1f8 | 264 | { |
f7ac4459 | 265 | if (!strncmp(path, "aufs:", 5)) |
3d2ae1e2 | 266 | return true; |
f7ac4459 | 267 | |
3d2ae1e2 | 268 | return false; |
00f0a1f8 CB |
269 | } |
270 | ||
10bc1861 | 271 | int aufs_mount(struct lxc_storage *bdev) |
00f0a1f8 | 272 | { |
410d0f6e | 273 | char *tmp, *options, *dup, *lower, *upper; |
00f0a1f8 CB |
274 | int len; |
275 | unsigned long mntflags; | |
276 | char *mntdata; | |
277 | int ret; | |
278 | const char *xinopath = "/dev/shm/aufs.xino"; | |
279 | ||
280 | if (strcmp(bdev->type, "aufs")) | |
281 | return -22; | |
282 | if (!bdev->src || !bdev->dest) | |
283 | return -22; | |
284 | ||
285 | // separately mount it first | |
286 | // mount -t aufs -obr=${upper}=rw:${lower}=ro lower dest | |
287 | dup = alloca(strlen(bdev->src)+1); | |
288 | strcpy(dup, bdev->src); | |
410d0f6e CB |
289 | /* support multiple lower layers */ |
290 | if (!(lower = strstr(dup, ":/"))) | |
291 | return -22; | |
292 | lower++; | |
293 | upper = lower; | |
294 | while ((tmp = strstr(++upper, ":/"))) { | |
295 | upper = tmp; | |
296 | } | |
297 | if (--upper == lower) | |
00f0a1f8 CB |
298 | return -22; |
299 | *upper = '\0'; | |
300 | upper++; | |
301 | ||
302 | if (parse_mntopts(bdev->mntopts, &mntflags, &mntdata) < 0) { | |
303 | free(mntdata); | |
304 | return -22; | |
305 | } | |
306 | ||
307 | // TODO We should check whether bdev->src is a blockdev, and if so | |
308 | // but for now, only support aufs of a basic directory | |
309 | ||
310 | // AUFS does not work on top of certain filesystems like (XFS or Btrfs) | |
311 | // so add xino=/dev/shm/aufs.xino parameter to mount options. | |
312 | // The same xino option can be specified to multiple aufs mounts, and | |
313 | // a xino file is not shared among multiple aufs mounts. | |
314 | // | |
315 | // see http://www.mail-archive.com/aufs-users@lists.sourceforge.net/msg02587.html | |
316 | // http://www.mail-archive.com/aufs-users@lists.sourceforge.net/msg05126.html | |
317 | if (mntdata) { | |
318 | len = strlen(lower) + strlen(upper) + strlen(xinopath) + strlen("br==rw:=ro,,xino=") + strlen(mntdata) + 1; | |
319 | options = alloca(len); | |
320 | ret = snprintf(options, len, "br=%s=rw:%s=ro,%s,xino=%s", upper, lower, mntdata, xinopath); | |
321 | } | |
322 | else { | |
323 | len = strlen(lower) + strlen(upper) + strlen(xinopath) + strlen("br==rw:=ro,xino=") + 1; | |
324 | options = alloca(len); | |
325 | ret = snprintf(options, len, "br=%s=rw:%s=ro,xino=%s", upper, lower, xinopath); | |
326 | } | |
327 | ||
328 | if (ret < 0 || ret >= len) { | |
329 | free(mntdata); | |
330 | return -1; | |
331 | } | |
332 | ||
333 | ret = mount(lower, bdev->dest, "aufs", MS_MGC_VAL | mntflags, options); | |
334 | if (ret < 0) | |
335 | SYSERROR("aufs: error mounting %s onto %s options %s", | |
336 | lower, bdev->dest, options); | |
337 | else | |
338 | INFO("aufs: mounted %s onto %s options %s", | |
339 | lower, bdev->dest, options); | |
340 | return ret; | |
341 | } | |
342 | ||
10bc1861 | 343 | int aufs_umount(struct lxc_storage *bdev) |
00f0a1f8 CB |
344 | { |
345 | if (strcmp(bdev->type, "aufs")) | |
346 | return -22; | |
347 | if (!bdev->src || !bdev->dest) | |
348 | return -22; | |
349 | return umount(bdev->dest); | |
350 | } | |
1d52bdf7 CB |
351 | |
352 | char *aufs_get_rootfs(const char *rootfs_path, size_t *rootfslen) | |
353 | { | |
354 | char *rootfsdir = NULL; | |
355 | char *s1 = NULL; | |
356 | char *s2 = NULL; | |
357 | char *s3 = NULL; | |
358 | ||
359 | if (!rootfs_path || !rootfslen) | |
360 | return NULL; | |
361 | ||
362 | s1 = strdup(rootfs_path); | |
363 | if (!s1) | |
364 | return NULL; | |
365 | ||
366 | if ((s2 = strstr(s1, ":/"))) { | |
367 | s2 = s2 + 1; | |
368 | if ((s3 = strstr(s2, ":/"))) | |
369 | *s3 = '\0'; | |
370 | rootfsdir = strdup(s2); | |
371 | if (!rootfsdir) { | |
372 | free(s1); | |
373 | return NULL; | |
374 | } | |
375 | } | |
376 | ||
377 | if (!rootfsdir) | |
378 | rootfsdir = s1; | |
379 | else | |
380 | free(s1); | |
381 | ||
382 | *rootfslen = strlen(rootfsdir); | |
383 | ||
384 | return rootfsdir; | |
385 | } | |
386 | ||
387 | int aufs_mkdir(const struct mntent *mntent, const struct lxc_rootfs *rootfs, | |
388 | const char *lxc_name, const char *lxc_path) | |
389 | { | |
390 | char lxcpath[MAXPATHLEN]; | |
9769034f | 391 | char *rootfs_path = NULL; |
1d52bdf7 CB |
392 | char *rootfsdir = NULL; |
393 | char *scratch = NULL; | |
394 | char *tmp = NULL; | |
395 | char *upperdir = NULL; | |
396 | char **opts = NULL; | |
397 | int fret = -1; | |
398 | int ret = 0; | |
399 | size_t arrlen = 0; | |
400 | size_t i; | |
401 | size_t len = 0; | |
402 | size_t rootfslen = 0; | |
403 | ||
9769034f CB |
404 | /* When rootfs == NULL we have a container without a rootfs. */ |
405 | if (rootfs && rootfs->path) | |
406 | rootfs_path = rootfs->path; | |
1d52bdf7 CB |
407 | |
408 | opts = lxc_string_split(mntent->mnt_opts, ','); | |
409 | if (opts) | |
410 | arrlen = lxc_array_len((void **)opts); | |
411 | else | |
412 | goto err; | |
413 | ||
414 | for (i = 0; i < arrlen; i++) { | |
415 | if (strstr(opts[i], "br=") && (strlen(opts[i]) > (len = strlen("br=")))) | |
416 | tmp = opts[i] + len; | |
417 | } | |
418 | if (!tmp) | |
419 | goto err; | |
420 | ||
421 | upperdir = strtok_r(tmp, ":=", &scratch); | |
422 | if (!upperdir) | |
423 | goto err; | |
424 | ||
9769034f CB |
425 | if (rootfs_path) { |
426 | ret = snprintf(lxcpath, MAXPATHLEN, "%s/%s", lxc_path, lxc_name); | |
427 | if (ret < 0 || ret >= MAXPATHLEN) | |
428 | goto err; | |
1d52bdf7 | 429 | |
9769034f CB |
430 | rootfsdir = aufs_get_rootfs(rootfs->path, &rootfslen); |
431 | if (!rootfsdir) | |
432 | goto err; | |
433 | } | |
1d52bdf7 | 434 | |
9769034f CB |
435 | /* |
436 | * We neither allow users to create upperdirs and workdirs outside the | |
437 | * containerdir nor inside the rootfs. The latter might be debatable. | |
438 | * When we have a container without a rootfs we skip the checks. | |
439 | */ | |
440 | ret = 0; | |
441 | if (!rootfs_path) | |
442 | ret = mkdir_p(upperdir, 0755); | |
443 | else if ((strncmp(upperdir, lxcpath, strlen(lxcpath)) == 0) && (strncmp(upperdir, rootfsdir, rootfslen) != 0)) | |
444 | ret = mkdir_p(upperdir, 0755); | |
445 | if (ret < 0) | |
446 | WARN("Failed to create upperdir"); | |
1d52bdf7 CB |
447 | |
448 | fret = 0; | |
449 | ||
450 | err: | |
451 | free(rootfsdir); | |
452 | lxc_free_array((void **)opts, free); | |
453 | return fret; | |
454 | } | |
455 |