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
;
183 #define MS_LAZYTIME (1 << 25)
186 static int parseOCImountopts(struct blob_attr
*msg
, unsigned long *mount_flags
, char **mount_data
, int *error
)
188 struct blob_attr
*cur
;
190 unsigned long mf
= 0;
192 struct list_head fsopts
= LIST_HEAD_INIT(fsopts
);
194 struct mount_opt
*opt
;
196 blobmsg_for_each_attr(cur
, msg
, rem
) {
197 tmp
= blobmsg_get_string(cur
);
198 if (!strcmp("ro", tmp
))
200 else if (!strcmp("rw", tmp
))
202 else if (!strcmp("bind", tmp
))
204 else if (!strcmp("rbind", tmp
))
205 mf
|= MS_BIND
| MS_REC
;
206 else if (!strcmp("sync", tmp
))
207 mf
|= MS_SYNCHRONOUS
;
208 else if (!strcmp("async", tmp
))
209 mf
&= ~MS_SYNCHRONOUS
;
210 else if (!strcmp("atime", tmp
))
212 else if (!strcmp("noatime", tmp
))
214 else if (!strcmp("defaults", tmp
))
215 mf
= 0; /* rw, suid, dev, exec, auto, nouser, and async */
216 else if (!strcmp("dev", tmp
))
218 else if (!strcmp("nodev", tmp
))
220 else if (!strcmp("diratime", tmp
))
221 mf
&= ~MS_NODIRATIME
;
222 else if (!strcmp("nodiratime", tmp
))
224 else if (!strcmp("dirsync", tmp
))
226 else if (!strcmp("exec", tmp
))
228 else if (!strcmp("noexec", tmp
))
230 else if (!strcmp("mand", tmp
))
232 else if (!strcmp("nomand", tmp
))
234 else if (!strcmp("relatime", tmp
))
236 else if (!strcmp("norelatime", tmp
))
238 else if (!strcmp("strictatime", tmp
))
239 mf
|= MS_STRICTATIME
;
240 else if (!strcmp("nostrictatime", tmp
))
241 mf
&= ~MS_STRICTATIME
;
242 else if (!strcmp("lazytime", tmp
))
244 else if (!strcmp("nolazytime", tmp
))
246 else if (!strcmp("suid", tmp
))
248 else if (!strcmp("nosuid", tmp
))
250 else if (!strcmp("remount", tmp
))
252 else if(!strcmp("nofail", tmp
))
254 else if (!strcmp("auto", tmp
) ||
255 !strcmp("noauto", tmp
) ||
256 !strcmp("user", tmp
) ||
257 !strcmp("group", tmp
) ||
258 !strcmp("_netdev", tmp
))
259 DEBUG("ignoring built-in mount option %s\n", tmp
);
261 /* filesystem-specific free-form option */
262 opt
= calloc(1, sizeof(*opt
));
264 list_add_tail(&opt
->list
, &fsopts
);
270 list_for_each_entry(opt
, &fsopts
, list
) {
274 len
+= strlen(opt
->optstr
);
280 *mount_data
= calloc(len
+ 1, sizeof(char));
285 list_for_each_entry(opt
, &fsopts
, list
) {
287 strcat(*mount_data
, ",");
289 strcat(*mount_data
, opt
->optstr
);
295 DEBUG("mount flags(%08lx) fsopts(\"%s\")\n", mf
, *mount_data
?:"");
300 int parseOCImount(struct blob_attr
*msg
)
302 struct blob_attr
*tb
[__OCI_MOUNT_MAX
];
303 unsigned long mount_flags
= 0;
304 char *mount_data
= NULL
;
307 blobmsg_parse(oci_mount_policy
, __OCI_MOUNT_MAX
, tb
, blobmsg_data(msg
), blobmsg_len(msg
));
309 if (!tb
[OCI_MOUNT_DESTINATION
])
312 if (tb
[OCI_MOUNT_OPTIONS
]) {
313 ret
= parseOCImountopts(tb
[OCI_MOUNT_OPTIONS
], &mount_flags
, &mount_data
, &err
);
318 add_mount(tb
[OCI_MOUNT_SOURCE
] ? blobmsg_get_string(tb
[OCI_MOUNT_SOURCE
]) : NULL
,
319 blobmsg_get_string(tb
[OCI_MOUNT_DESTINATION
]),
320 tb
[OCI_MOUNT_TYPE
] ? blobmsg_get_string(tb
[OCI_MOUNT_TYPE
]) : NULL
,
321 mount_flags
, mount_data
, err
);
327 int mount_all(const char *jailroot
) {
331 avl_for_each_element(&libraries
, l
, avl
)
332 add_mount_bind(l
->path
, 1, -1);
334 avl_for_each_element(&mounts
, m
, avl
)
335 if (do_mount(jailroot
, m
->source
, m
->target
, m
->filesystemtype
, m
->mountflags
, m
->optstr
, m
->error
))
341 void mount_list_init(void) {
342 avl_init(&mounts
, avl_strcmp
, false, NULL
);
345 static int add_script_interp(const char *path
, const char *map
, int size
)
348 while (start
< size
&& map
[start
] != '/') {
352 ERROR("bad script interp (%s)\n", path
);
355 int stop
= start
+ 1;
356 while (stop
< size
&& map
[stop
] > 0x20 && map
[stop
] <= 0x7e) {
359 if (stop
>= size
|| (stop
-start
) > PATH_MAX
) {
360 ERROR("bad script interp (%s)\n", path
);
364 strncpy(buf
, map
+start
, stop
-start
);
365 return add_path_and_deps(buf
, 1, -1, 0);
368 int add_path_and_deps(const char *path
, int readonly
, int error
, int lib
)
370 assert(path
!= NULL
);
372 if (lib
== 0 && path
[0] != '/') {
373 ERROR("%s is not an absolute path\n", path
);
379 if (path
[0] == '/') {
380 if (avl_find(&mounts
, path
))
382 fd
= open(path
, O_RDONLY
|O_CLOEXEC
);
385 add_mount_bind(path
, readonly
, error
);
387 if (avl_find(&libraries
, path
))
390 fd
= lib_open(&fullpath
, path
);
394 alloc_library(fullpath
, path
);
400 if (fstat(fd
, &s
) == -1) {
401 ERROR("fstat(%s) failed: %m\n", path
);
406 if (!S_ISREG(s
.st_mode
)) {
411 /* too small to be an ELF or a script -> "normal" file */
417 map
= mmap(NULL
, s
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
418 if (map
== MAP_FAILED
) {
419 ERROR("failed to mmap %s: %m\n", path
);
424 if (map
[0] == '#' && map
[1] == '!') {
425 ret
= add_script_interp(path
, map
, s
.st_size
);
429 if (map
[0] == ELFMAG0
&& map
[1] == ELFMAG1
&& map
[2] == ELFMAG2
&& map
[3] == ELFMAG3
) {
430 ret
= elf_load_deps(path
, map
);
440 munmap(map
, s
.st_size
);