2 * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
3 * Copyright (C) 2015 Etienne Champetier <champetier.etienne@gmail.com>
4 * Copyright (C) 2020 Daniel Golle <daniel@makrotopia.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License version 2.1
8 * as published by the Free Software Foundation
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
22 #include <linux/limits.h>
30 #include <libubox/avl.h>
31 #include <libubox/avl-cmp.h>
32 #include <libubox/blobmsg.h>
33 #include <libubox/list.h>
44 const char *filesystemtype
;
45 unsigned long mountflags
;
50 struct avl_tree mounts
;
52 int mkdir_p(char *dir
, mode_t mask
)
54 char *l
= strrchr(dir
, '/');
62 if (mkdir_p(dir
, mask
))
67 ret
= mkdir(dir
, mask
);
68 if (ret
&& errno
== EEXIST
)
72 ERROR("mkdir(%s, %d) failed: %m\n", dir
, mask
);
77 static int do_mount(const char *root
, const char *source
, const char *target
, const char *filesystemtype
,
78 unsigned long orig_mountflags
, const char *optstr
, int error
)
83 bool is_bind
= (orig_mountflags
& MS_BIND
);
84 unsigned long mountflags
= orig_mountflags
;
86 if (is_bind
&& stat(source
, &s
)) {
87 ERROR("stat(%s) failed: %m\n", source
);
91 snprintf(new, sizeof(new), "%s%s", root
, target
?target
:source
);
93 if (!is_bind
|| S_ISDIR(s
.st_mode
)) {
96 mkdir_p(dirname(new), 0755);
97 snprintf(new, sizeof(new), "%s%s", root
, target
?target
:source
);
98 fd
= creat(new, 0644);
100 ERROR("creat(%s) failed: %m\n", new);
106 if (mountflags
& MS_BIND
) {
107 if (mount(source
, new, filesystemtype
, MS_BIND
, optstr
)) {
108 ERROR("failed to mount -B %s %s: %m\n", source
, new);
111 mountflags
|= MS_REMOUNT
;
114 if (mount(source
, new, filesystemtype
, mountflags
, optstr
)) {
115 ERROR("failed to mount %s %s: %m\n", source
, new);
119 DEBUG("mount %s%s %s (%s)\n", (mountflags
& MS_BIND
)?"-B ":"", source
, new,
120 (mountflags
& MS_RDONLY
)?"ro":"rw");
125 int add_mount(const char *source
, const char *target
, const char *filesystemtype
,
126 unsigned long mountflags
, const char *optstr
, int error
)
128 assert(target
!= NULL
);
130 if (avl_find(&mounts
, target
))
134 m
= calloc(1, sizeof(struct mount
));
136 m
->avl
.key
= m
->target
= strdup(target
);
138 m
->source
= strdup(source
);
140 m
->filesystemtype
= strdup(filesystemtype
);
141 m
->mountflags
= mountflags
;
144 avl_insert(&mounts
, &m
->avl
);
145 DEBUG("adding mount %s %s bind(%d) ro(%d) err(%d)\n", m
->source
, m
->target
,
146 !!(m
->mountflags
& MS_BIND
), !!(m
->mountflags
& MS_RDONLY
), m
->error
!= 0);
151 int add_mount_bind(const char *path
, int readonly
, int error
)
153 unsigned long mountflags
= MS_BIND
;
156 mountflags
|= MS_RDONLY
;
158 return add_mount(path
, path
, NULL
, mountflags
, NULL
, error
);
164 OCI_MOUNT_DESTINATION
,
170 static const struct blobmsg_policy oci_mount_policy
[] = {
171 [OCI_MOUNT_SOURCE
] = { "source", BLOBMSG_TYPE_STRING
},
172 [OCI_MOUNT_DESTINATION
] = { "destination", BLOBMSG_TYPE_STRING
},
173 [OCI_MOUNT_TYPE
] = { "type", BLOBMSG_TYPE_STRING
},
174 [OCI_MOUNT_OPTIONS
] = { "options", BLOBMSG_TYPE_ARRAY
},
178 struct list_head list
;
182 static int parseOCImountopts(struct blob_attr
*msg
, unsigned long *mount_flags
, char **mount_data
, int *error
)
184 struct blob_attr
*cur
;
186 unsigned long mf
= 0;
188 struct list_head fsopts
= LIST_HEAD_INIT(fsopts
);
190 struct mount_opt
*opt
;
192 blobmsg_for_each_attr(cur
, msg
, rem
) {
193 tmp
= blobmsg_get_string(cur
);
194 if (!strcmp("ro", tmp
))
196 else if (!strcmp("rw", tmp
))
198 else if (!strcmp("bind", tmp
))
200 else if (!strcmp("rbind", tmp
))
201 mf
|= MS_BIND
| MS_REC
;
202 else if (!strcmp("sync", tmp
))
203 mf
|= MS_SYNCHRONOUS
;
204 else if (!strcmp("async", tmp
))
205 mf
&= ~MS_SYNCHRONOUS
;
206 else if (!strcmp("atime", tmp
))
208 else if (!strcmp("noatime", tmp
))
210 else if (!strcmp("defaults", tmp
))
211 mf
= 0; /* rw, suid, dev, exec, auto, nouser, and async */
212 else if (!strcmp("dev", tmp
))
214 else if (!strcmp("nodev", tmp
))
216 else if (!strcmp("diratime", tmp
))
217 mf
&= ~MS_NODIRATIME
;
218 else if (!strcmp("nodiratime", tmp
))
220 else if (!strcmp("dirsync", tmp
))
222 else if (!strcmp("exec", tmp
))
224 else if (!strcmp("noexec", tmp
))
226 else if (!strcmp("mand", tmp
))
228 else if (!strcmp("nomand", tmp
))
230 else if (!strcmp("relatime", tmp
))
232 else if (!strcmp("norelatime", tmp
))
234 else if (!strcmp("strictatime", tmp
))
235 mf
|= MS_STRICTATIME
;
236 else if (!strcmp("nostrictatime", tmp
))
237 mf
&= ~MS_STRICTATIME
;
238 else if (!strcmp("lazytime", tmp
))
240 else if (!strcmp("nostrictatime", tmp
))
242 else if (!strcmp("suid", tmp
))
244 else if (!strcmp("nosuid", tmp
))
246 else if (!strcmp("remount", tmp
))
248 else if(!strcmp("nofail", tmp
))
250 else if (!strcmp("auto", tmp
) ||
251 !strcmp("noauto", tmp
) ||
252 !strcmp("user", tmp
) ||
253 !strcmp("group", tmp
) ||
254 !strcmp("_netdev", tmp
))
255 DEBUG("ignoring built-in mount option %s\n", tmp
);
257 /* filesystem-specific free-form option */
258 opt
= calloc(1, sizeof(*opt
));
260 list_add_tail(&opt
->list
, &fsopts
);
266 list_for_each_entry(opt
, &fsopts
, list
) {
270 len
+= strlen(opt
->optstr
);
276 *mount_data
= calloc(len
+ 1, sizeof(char));
281 list_for_each_entry(opt
, &fsopts
, list
) {
283 strcat(*mount_data
, ",");
285 strcat(*mount_data
, opt
->optstr
);
291 DEBUG("mount flags(%08lx) fsopts(\"%s\")\n", mf
, *mount_data
?:"");
296 int parseOCImount(struct blob_attr
*msg
)
298 struct blob_attr
*tb
[__OCI_MOUNT_MAX
];
299 unsigned long mount_flags
= 0;
300 char *mount_data
= NULL
;
303 blobmsg_parse(oci_mount_policy
, __OCI_MOUNT_MAX
, tb
, blobmsg_data(msg
), blobmsg_len(msg
));
305 if (!tb
[OCI_MOUNT_DESTINATION
])
308 if (tb
[OCI_MOUNT_OPTIONS
]) {
309 ret
= parseOCImountopts(tb
[OCI_MOUNT_OPTIONS
], &mount_flags
, &mount_data
, &err
);
314 add_mount(tb
[OCI_MOUNT_SOURCE
] ? blobmsg_get_string(tb
[OCI_MOUNT_SOURCE
]) : NULL
,
315 blobmsg_get_string(tb
[OCI_MOUNT_DESTINATION
]),
316 tb
[OCI_MOUNT_TYPE
] ? blobmsg_get_string(tb
[OCI_MOUNT_TYPE
]) : NULL
,
317 mount_flags
, mount_data
, err
);
323 int mount_all(const char *jailroot
) {
327 avl_for_each_element(&libraries
, l
, avl
)
328 add_mount_bind(l
->path
, 1, -1);
330 avl_for_each_element(&mounts
, m
, avl
)
331 if (do_mount(jailroot
, m
->source
, m
->target
, m
->filesystemtype
, m
->mountflags
, m
->optstr
, m
->error
))
337 void mount_list_init(void) {
338 avl_init(&mounts
, avl_strcmp
, false, NULL
);
341 static int add_script_interp(const char *path
, const char *map
, int size
)
344 while (start
< size
&& map
[start
] != '/') {
348 ERROR("bad script interp (%s)\n", path
);
351 int stop
= start
+ 1;
352 while (stop
< size
&& map
[stop
] > 0x20 && map
[stop
] <= 0x7e) {
355 if (stop
>= size
|| (stop
-start
) > PATH_MAX
) {
356 ERROR("bad script interp (%s)\n", path
);
360 strncpy(buf
, map
+start
, stop
-start
);
361 return add_path_and_deps(buf
, 1, -1, 0);
364 int add_path_and_deps(const char *path
, int readonly
, int error
, int lib
)
366 assert(path
!= NULL
);
368 if (lib
== 0 && path
[0] != '/') {
369 ERROR("%s is not an absolute path\n", path
);
375 if (path
[0] == '/') {
376 if (avl_find(&mounts
, path
))
378 fd
= open(path
, O_RDONLY
|O_CLOEXEC
);
381 add_mount_bind(path
, readonly
, error
);
383 if (avl_find(&libraries
, path
))
386 fd
= lib_open(&fullpath
, path
);
390 alloc_library(fullpath
, path
);
396 if (fstat(fd
, &s
) == -1) {
397 ERROR("fstat(%s) failed: %m\n", path
);
402 if (!S_ISREG(s
.st_mode
)) {
407 /* too small to be an ELF or a script -> "normal" file */
413 map
= mmap(NULL
, s
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
414 if (map
== MAP_FAILED
) {
415 ERROR("failed to mmap %s: %m\n", path
);
420 if (map
[0] == '#' && map
[1] == '!') {
421 ret
= add_script_interp(path
, map
, s
.st_size
);
425 if (map
[0] == ELFMAG0
&& map
[1] == ELFMAG1
&& map
[2] == ELFMAG2
&& map
[3] == ELFMAG3
) {
426 ret
= elf_load_deps(path
, map
);
436 munmap(map
, s
.st_size
);