]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/storage/aufs.c
storage: use userns_exec_full()
[mirror_lxc.git] / src / lxc / storage / aufs.c
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
31 #include "aufs.h"
32 #include "log.h"
33 #include "rsync.h"
34 #include "storage.h"
35 #include "utils.h"
36
37 lxc_log_define(aufs, lxc);
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
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
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)
75 {
76 char cmd_output[MAXPATHLEN];
77
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
92 if (am_unpriv() && chown_mapped_root(new->dest, conf) < 0)
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 }
119 if (am_unpriv() && chown_mapped_root(delta, conf) < 0)
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 }
160 if (am_unpriv() && chown_mapped_root(ndelta, conf) < 0)
161 WARN("Failed to update ownership of %s", ndelta);
162
163 struct rsync_data_char rdata;
164 rdata.src = odelta;
165 rdata.dest = ndelta;
166 if (am_unpriv())
167 ret = userns_exec_full(conf, lxc_rsync_delta_wrapper,
168 &rdata, "lxc_rsync_delta_wrapper");
169 else
170 ret = run_command(cmd_output, sizeof(cmd_output),
171 lxc_rsync_delta_wrapper,
172 (void *)&rdata);
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 */
208 int aufs_create(struct lxc_storage *bdev, const char *dest, const char *n,
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
250 int aufs_destroy(struct lxc_storage *orig)
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
263 bool aufs_detect(const char *path)
264 {
265 if (!strncmp(path, "aufs:", 5))
266 return true;
267
268 return false;
269 }
270
271 int aufs_mount(struct lxc_storage *bdev)
272 {
273 char *tmp, *options, *dup, *lower, *upper;
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);
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)
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
343 int aufs_umount(struct lxc_storage *bdev)
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 }
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];
391 char *rootfs_path = NULL;
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
404 /* When rootfs == NULL we have a container without a rootfs. */
405 if (rootfs && rootfs->path)
406 rootfs_path = rootfs->path;
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
425 if (rootfs_path) {
426 ret = snprintf(lxcpath, MAXPATHLEN, "%s/%s", lxc_path, lxc_name);
427 if (ret < 0 || ret >= MAXPATHLEN)
428 goto err;
429
430 rootfsdir = aufs_get_rootfs(rootfs->path, &rootfslen);
431 if (!rootfsdir)
432 goto err;
433 }
434
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");
447
448 fret = 0;
449
450 err:
451 free(rootfsdir);
452 lxc_free_array((void **)opts, free);
453 return fret;
454 }
455