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);
110 mountflags
|= MS_REMOUNT
;
113 if (mount(source
, new, filesystemtype
, mountflags
, optstr
)) {
114 ERROR("failed to mount %s %s: %m\n", source
, new);
118 DEBUG("mount %s%s %s (%s)\n", (mountflags
& MS_BIND
)?"-B ":"", source
, new,
119 (mountflags
& MS_RDONLY
)?"ro":"rw");
124 int add_mount(const char *source
, const char *target
, const char *filesystemtype
,
125 unsigned long mountflags
, const char *optstr
, int error
)
127 assert(target
!= NULL
);
129 if (avl_find(&mounts
, target
))
133 m
= calloc(1, sizeof(struct mount
));
135 m
->avl
.key
= m
->target
= strdup(target
);
137 m
->source
= strdup(source
);
139 m
->filesystemtype
= strdup(filesystemtype
);
140 m
->mountflags
= mountflags
;
143 avl_insert(&mounts
, &m
->avl
);
144 DEBUG("adding mount %s %s bind(%d) ro(%d) err(%d)\n", m
->source
, m
->target
,
145 !!(m
->mountflags
& MS_BIND
), !!(m
->mountflags
& MS_RDONLY
), m
->error
!= 0);
150 int add_mount_bind(const char *path
, int readonly
, int error
)
152 unsigned long mountflags
= MS_BIND
;
155 mountflags
|= MS_RDONLY
;
157 return add_mount(path
, path
, NULL
, mountflags
, NULL
, error
);
163 OCI_MOUNT_DESTINATION
,
169 static const struct blobmsg_policy oci_mount_policy
[] = {
170 [OCI_MOUNT_SOURCE
] = { "source", BLOBMSG_TYPE_STRING
},
171 [OCI_MOUNT_DESTINATION
] = { "destination", BLOBMSG_TYPE_STRING
},
172 [OCI_MOUNT_TYPE
] = { "type", BLOBMSG_TYPE_STRING
},
173 [OCI_MOUNT_OPTIONS
] = { "options", BLOBMSG_TYPE_ARRAY
},
177 struct list_head list
;
181 static int parseOCImountopts(struct blob_attr
*msg
, unsigned long *mount_flags
, char **mount_data
, int *error
)
183 struct blob_attr
*cur
;
185 unsigned long mf
= 0;
187 struct list_head fsopts
= LIST_HEAD_INIT(fsopts
);
189 struct mount_opt
*opt
;
191 blobmsg_for_each_attr(cur
, msg
, rem
) {
192 tmp
= blobmsg_get_string(cur
);
193 if (!strcmp("ro", tmp
))
195 else if (!strcmp("rw", tmp
))
197 else if (!strcmp("bind", tmp
))
199 else if (!strcmp("rbind", tmp
))
200 mf
|= MS_BIND
| MS_REC
;
201 else if (!strcmp("sync", tmp
))
202 mf
|= MS_SYNCHRONOUS
;
203 else if (!strcmp("async", tmp
))
204 mf
&= ~MS_SYNCHRONOUS
;
205 else if (!strcmp("atime", tmp
))
207 else if (!strcmp("noatime", tmp
))
209 else if (!strcmp("defaults", tmp
))
210 mf
= 0; /* rw, suid, dev, exec, auto, nouser, and async */
211 else if (!strcmp("dev", tmp
))
213 else if (!strcmp("nodev", tmp
))
215 else if (!strcmp("diratime", tmp
))
216 mf
&= ~MS_NODIRATIME
;
217 else if (!strcmp("nodiratime", tmp
))
219 else if (!strcmp("dirsync", tmp
))
221 else if (!strcmp("exec", tmp
))
223 else if (!strcmp("noexec", tmp
))
225 else if (!strcmp("mand", tmp
))
227 else if (!strcmp("nomand", tmp
))
229 else if (!strcmp("relatime", tmp
))
231 else if (!strcmp("norelatime", tmp
))
233 else if (!strcmp("strictatime", tmp
))
234 mf
|= MS_STRICTATIME
;
235 else if (!strcmp("nostrictatime", tmp
))
236 mf
&= ~MS_STRICTATIME
;
237 else if (!strcmp("lazytime", tmp
))
239 else if (!strcmp("nostrictatime", tmp
))
241 else if (!strcmp("suid", tmp
))
243 else if (!strcmp("nosuid", tmp
))
245 else if (!strcmp("remount", tmp
))
247 else if(!strcmp("nofail", tmp
))
249 else if (!strcmp("auto", tmp
) ||
250 !strcmp("noauto", tmp
) ||
251 !strcmp("user", tmp
) ||
252 !strcmp("group", tmp
) ||
253 !strcmp("_netdev", tmp
))
254 DEBUG("ignoring built-in mount option %s\n", tmp
);
256 /* filesystem-specific free-form option */
257 opt
= calloc(1, sizeof(*opt
));
259 list_add_tail(&opt
->list
, &fsopts
);
265 list_for_each_entry(opt
, &fsopts
, list
) {
269 len
+= strlen(opt
->optstr
);
275 *mount_data
= calloc(len
+ 1, sizeof(char));
280 list_for_each_entry(opt
, &fsopts
, list
) {
282 strcat(*mount_data
, ",");
284 strcat(*mount_data
, opt
->optstr
);
290 DEBUG("mount flags(%08lx) fsopts(\"%s\")\n", mf
, *mount_data
?:"");
295 int parseOCImount(struct blob_attr
*msg
)
297 struct blob_attr
*tb
[__OCI_MOUNT_MAX
];
298 unsigned long mount_flags
= 0;
299 char *mount_data
= NULL
;
302 blobmsg_parse(oci_mount_policy
, __OCI_MOUNT_MAX
, tb
, blobmsg_data(msg
), blobmsg_len(msg
));
304 if (!tb
[OCI_MOUNT_DESTINATION
])
307 if (tb
[OCI_MOUNT_OPTIONS
]) {
308 ret
= parseOCImountopts(tb
[OCI_MOUNT_OPTIONS
], &mount_flags
, &mount_data
, &err
);
313 add_mount(tb
[OCI_MOUNT_SOURCE
] ? blobmsg_get_string(tb
[OCI_MOUNT_SOURCE
]) : NULL
,
314 blobmsg_get_string(tb
[OCI_MOUNT_DESTINATION
]),
315 tb
[OCI_MOUNT_TYPE
] ? blobmsg_get_string(tb
[OCI_MOUNT_TYPE
]) : NULL
,
316 mount_flags
, mount_data
, err
);
322 int mount_all(const char *jailroot
) {
326 avl_for_each_element(&libraries
, l
, avl
)
327 add_mount_bind(l
->path
, 1, -1);
329 avl_for_each_element(&mounts
, m
, avl
)
330 if (do_mount(jailroot
, m
->source
, m
->target
, m
->filesystemtype
, m
->mountflags
, m
->optstr
, m
->error
))
336 void mount_list_init(void) {
337 avl_init(&mounts
, avl_strcmp
, false, NULL
);
340 static int add_script_interp(const char *path
, const char *map
, int size
)
343 while (start
< size
&& map
[start
] != '/') {
347 ERROR("bad script interp (%s)\n", path
);
350 int stop
= start
+ 1;
351 while (stop
< size
&& map
[stop
] > 0x20 && map
[stop
] <= 0x7e) {
354 if (stop
>= size
|| (stop
-start
) > PATH_MAX
) {
355 ERROR("bad script interp (%s)\n", path
);
359 strncpy(buf
, map
+start
, stop
-start
);
360 return add_path_and_deps(buf
, 1, -1, 0);
363 int add_path_and_deps(const char *path
, int readonly
, int error
, int lib
)
365 assert(path
!= NULL
);
367 if (lib
== 0 && path
[0] != '/') {
368 ERROR("%s is not an absolute path\n", path
);
374 if (path
[0] == '/') {
375 if (avl_find(&mounts
, path
))
377 fd
= open(path
, O_RDONLY
|O_CLOEXEC
);
380 add_mount_bind(path
, readonly
, error
);
382 if (avl_find(&libraries
, path
))
385 fd
= lib_open(&fullpath
, path
);
389 alloc_library(fullpath
, path
);
395 if (fstat(fd
, &s
) == -1) {
396 ERROR("fstat(%s) failed: %m\n", path
);
401 if (!S_ISREG(s
.st_mode
)) {
406 /* too small to be an ELF or a script -> "normal" file */
412 map
= mmap(NULL
, s
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
413 if (map
== MAP_FAILED
) {
414 ERROR("failed to mmap %s: %m\n", path
);
419 if (map
[0] == '#' && map
[1] == '!') {
420 ret
= add_script_interp(path
, map
, s
.st_size
);
424 if (map
[0] == ELFMAG0
&& map
[1] == ELFMAG1
&& map
[2] == ELFMAG2
&& map
[3] == ELFMAG3
) {
425 ret
= elf_load_deps(path
, map
);
435 munmap(map
, s
.st_size
);