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