procd: Add %m to several functions that return errno.
[project/procd.git] / jail / fs.c
1 /*
2  * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
3  * Copyright (C) 2015 Etienne Champetier <champetier.etienne@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License version 2.1
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #define _GNU_SOURCE
16
17 #include <assert.h>
18 #include <elf.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <linux/limits.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <sys/mman.h>
26 #include <unistd.h>
27
28 #include <libubox/avl.h>
29 #include <libubox/avl-cmp.h>
30
31 #include "elf.h"
32 #include "fs.h"
33 #include "jail.h"
34 #include "log.h"
35
36 struct mount {
37         struct avl_node avl;
38         const char *path;
39         int readonly;
40         int error;
41 };
42
43 struct avl_tree mounts;
44
45 int add_mount(const char *path, int readonly, int error)
46 {
47         assert(path != NULL);
48
49         if (avl_find(&mounts, path))
50                 return 1;
51
52         struct mount *m;
53         m = calloc(1, sizeof(struct mount));
54         assert(m != NULL);
55         m->avl.key = m->path = strdup(path);
56         m->readonly = readonly;
57         m->error = error;
58
59         avl_insert(&mounts, &m->avl);
60         DEBUG("adding mount %s ro(%d) err(%d)\n", m->path, m->readonly, m->error != 0);
61         return 0;
62 }
63
64 int mount_all(const char *jailroot) {
65         struct library *l;
66         struct mount *m;
67
68         avl_for_each_element(&libraries, l, avl)
69                 add_mount(l->path, 1, -1);
70
71         avl_for_each_element(&mounts, m, avl)
72                 if (mount_bind(jailroot, m->path, m->readonly, m->error))
73                         return -1;
74
75         return 0;
76 }
77
78 void mount_list_init(void) {
79         avl_init(&mounts, avl_strcmp, false, NULL);
80 }
81
82 static int add_script_interp(const char *path, const char *map, int size)
83 {
84         int start = 2;
85         while (start < size && map[start] != '/') {
86                 start++;
87         }
88         if (start >= size) {
89                 ERROR("bad script interp (%s)\n", path);
90                 return -1;
91         }
92         int stop = start + 1;
93         while (stop < size && map[stop] > 0x20 && map[stop] <= 0x7e) {
94                 stop++;
95         }
96         if (stop >= size || (stop-start) > PATH_MAX) {
97                 ERROR("bad script interp (%s)\n", path);
98                 return -1;
99         }
100         char buf[PATH_MAX];
101         strncpy(buf, map+start, stop-start);
102         return add_path_and_deps(buf, 1, -1, 0);
103 }
104
105 int add_path_and_deps(const char *path, int readonly, int error, int lib)
106 {
107         assert(path != NULL);
108
109         if (lib == 0 && path[0] != '/') {
110                 ERROR("%s is not an absolute path\n", path);
111                 return error;
112         }
113
114         char *map = NULL;
115         int fd, ret = -1;
116         if (path[0] == '/') {
117                 if (avl_find(&mounts, path))
118                         return 0;
119                 fd = open(path, O_RDONLY|O_CLOEXEC);
120                 if (fd == -1)
121                         return error;
122                 add_mount(path, readonly, error);
123         } else {
124                 if (avl_find(&libraries, path))
125                         return 0;
126                 char *fullpath;
127                 fd = lib_open(&fullpath, path);
128                 if (fd == -1)
129                         return error;
130                 if (fullpath) {
131                         alloc_library(fullpath, path);
132                         free(fullpath);
133                 }
134         }
135
136         struct stat s;
137         if (fstat(fd, &s) == -1) {
138                 ERROR("fstat(%s) failed: %m\n", path);
139                 ret = error;
140                 goto out;
141         }
142
143         if (!S_ISREG(s.st_mode)) {
144                 ret = 0;
145                 goto out;
146         }
147
148         /* too small to be an ELF or a script -> "normal" file */
149         if (s.st_size < 4) {
150                 ret = 0;
151                 goto out;
152         }
153
154         map = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
155         if (map == MAP_FAILED) {
156                 ERROR("failed to mmap %s: %m\n", path);
157                 ret = -1;
158                 goto out;
159         }
160
161         if (map[0] == '#' && map[1] == '!') {
162                 ret = add_script_interp(path, map, s.st_size);
163                 goto out;
164         }
165
166         if (map[0] == ELFMAG0 && map[1] == ELFMAG1 && map[2] == ELFMAG2 && map[3] == ELFMAG3) {
167                 ret = elf_load_deps(path, map);
168                 goto out;
169         }
170
171         ret = 0;
172
173 out:
174         if (fd >= 0)
175                 close(fd);
176         if (map)
177                 munmap(map, s.st_size);
178
179         return ret;
180 }