libfuse
mount_util.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Architecture-independent mounting code.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt.
9*/
10
11#include "fuse_config.h"
12#include "mount_util.h"
13#include "fuse_log.h"
14
15#include <stdio.h>
16#include <unistd.h>
17#include <stdlib.h>
18#include <string.h>
19#include <signal.h>
20#include <dirent.h>
21#include <errno.h>
22#include <fcntl.h>
23#include <limits.h>
24#include <paths.h>
25#if !defined( __NetBSD__) && !defined(__FreeBSD__) && !defined(__DragonFly__) && !defined(__ANDROID__)
26#include <mntent.h>
27#else
28#define IGNORE_MTAB
29#endif
30#include <sys/stat.h>
31#include <sys/wait.h>
32
33#include "fuse_mount_compat.h"
34
35#include <sys/param.h>
36
37#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
38#define umount2(mnt, flags) unmount(mnt, ((flags) == 2) ? MNT_FORCE : 0)
39#endif
40
41#ifdef IGNORE_MTAB
42#define mtab_needs_update(mnt) 0
43#else
44static int mtab_needs_update(const char *mnt)
45{
46 int res;
47 struct stat stbuf;
48
49 /* If mtab is within new mount, don't touch it */
50 if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
51 _PATH_MOUNTED[strlen(mnt)] == '/')
52 return 0;
53
54 /*
55 * Skip mtab update if /etc/mtab:
56 *
57 * - doesn't exist,
58 * - is on a read-only filesystem.
59 */
60 res = lstat(_PATH_MOUNTED, &stbuf);
61 if (res == -1) {
62 if (errno == ENOENT)
63 return 0;
64 } else {
65 uid_t ruid;
66 int err;
67
68 ruid = getuid();
69 if (ruid != 0)
70 setreuid(0, -1);
71
72 res = access(_PATH_MOUNTED, W_OK);
73 err = (res == -1) ? errno : 0;
74 if (ruid != 0)
75 setreuid(ruid, -1);
76
77 if (err == EROFS)
78 return 0;
79
80 res = access("/run/mount/utab", F_OK);
81 if (res == -1)
82 return 0;
83 }
84
85 return 1;
86}
87#endif /* IGNORE_MTAB */
88
89/*
90 * These values become command line arguments of /bin/mount and /bin/umount.
91 * BusyBox mount(8) parses an argument starting with '-' as an option even
92 * behind an end-of-options marker ("--"); NULL would end the argument list
93 * early.
94 *
95 * @return 1 if @value is unsafe as a /bin/mount or /bin/umount argument.
96 */
97static int unsafe_operand(const char *value)
98{
99 return value == NULL || value[0] == '-';
100}
101
102static int add_mount(const char *progname, const char *fsname,
103 const char *mnt, const char *type, const char *opts)
104{
105 int res;
106 int status;
107 sigset_t blockmask;
108 sigset_t oldmask;
109
110 if (unsafe_operand(fsname) || unsafe_operand(mnt) ||
111 unsafe_operand(type) || unsafe_operand(opts)) {
112 fuse_log(FUSE_LOG_DEBUG,
113 "%s: option-like mount argument, skipping mtab update\n",
114 progname);
115 return 0;
116 }
117
118 sigemptyset(&blockmask);
119 sigaddset(&blockmask, SIGCHLD);
120 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
121 if (res == -1) {
122 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
123 return -1;
124 }
125
126 res = fork();
127 if (res == -1) {
128 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
129 goto out_restore;
130 }
131 if (res == 0) {
132 char *env = NULL;
133
134 sigprocmask(SIG_SETMASK, &oldmask, NULL);
135
136 if(setuid(geteuid()) == -1) {
137 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
138 res = -1;
139 goto out_restore;
140 }
141
142 /*
143 * fsname comes from -ofsname=, so it can start with '-'. The
144 * setuid() above raises the real uid to 0, so mount(8) is not
145 * in restricted mode either. Terminate the options with "--"
146 * to keep it from parsing the operands as further options.
147 */
148 execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
149 "-f", "-t", type, "-o", opts, "--", fsname, mnt, NULL,
150 &env);
151 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
152 progname, strerror(errno));
153 exit(1);
154 }
155 res = waitpid(res, &status, 0);
156 if (res == -1)
157 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
158
159 if (status != 0)
160 res = -1;
161
162 out_restore:
163 sigprocmask(SIG_SETMASK, &oldmask, NULL);
164
165 return res;
166}
167
168int fuse_mnt_add_mount(const char *progname, const char *fsname,
169 const char *mnt, const char *type, const char *opts)
170{
171 if (!mtab_needs_update(mnt))
172 return 0;
173
174 return add_mount(progname, fsname, mnt, type, opts);
175}
176
177static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
178{
179 int res;
180 int status;
181 sigset_t blockmask;
182 sigset_t oldmask;
183
184 sigemptyset(&blockmask);
185 sigaddset(&blockmask, SIGCHLD);
186 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
187 if (res == -1) {
188 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
189 return -1;
190 }
191
192 res = fork();
193 if (res == -1) {
194 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
195 goto out_restore;
196 }
197 if (res == 0) {
198 char *env = NULL;
199
200 sigprocmask(SIG_SETMASK, &oldmask, NULL);
201
202 if(setuid(geteuid()) == -1) {
203 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
204 res = -1;
205 goto out_restore;
206 }
207
208 if (lazy) {
209 execle("/bin/umount", "/bin/umount", "-i", "-l",
210 "--", rel_mnt, NULL, &env);
211 } else {
212 execle("/bin/umount", "/bin/umount", "-i",
213 "--", rel_mnt, NULL, &env);
214 }
215 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
216 progname, strerror(errno));
217 exit(1);
218 }
219 res = waitpid(res, &status, 0);
220 if (res == -1)
221 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
222
223 if (status != 0) {
224 res = -1;
225 }
226
227 out_restore:
228 sigprocmask(SIG_SETMASK, &oldmask, NULL);
229 return res;
230
231}
232
233int fuse_mnt_umount(const char *progname, const char *abs_mnt,
234 const char *rel_mnt, int lazy)
235{
236 int res;
237
238 /*
239 * umount(8) may read an option-like rel_mnt as an option: unmount here
240 * instead and leave the mtab/utab record behind
241 */
242 if (!mtab_needs_update(abs_mnt) || unsafe_operand(rel_mnt)) {
243 res = umount2(rel_mnt, lazy ? 2 : 0);
244 if (res == -1)
245 fprintf(stderr, "%s: failed to unmount %s: %s\n",
246 progname, abs_mnt, strerror(errno));
247 return res;
248 }
249
250 return exec_umount(progname, rel_mnt, lazy);
251}
252
253static int remove_mount(const char *progname, const char *mnt)
254{
255 int res;
256 int status;
257 sigset_t blockmask;
258 sigset_t oldmask;
259
260 if (unsafe_operand(mnt))
261 return 0;
262
263 sigemptyset(&blockmask);
264 sigaddset(&blockmask, SIGCHLD);
265 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
266 if (res == -1) {
267 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
268 return -1;
269 }
270
271 res = fork();
272 if (res == -1) {
273 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
274 goto out_restore;
275 }
276 if (res == 0) {
277 char *env = NULL;
278
279 sigprocmask(SIG_SETMASK, &oldmask, NULL);
280
281 if(setuid(geteuid()) == -1) {
282 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
283 res = -1;
284 goto out_restore;
285 }
286
287 execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
288 "--fake", "--", mnt, NULL, &env);
289 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
290 progname, strerror(errno));
291 exit(1);
292 }
293 res = waitpid(res, &status, 0);
294 if (res == -1)
295 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
296
297 if (status != 0)
298 res = -1;
299
300 out_restore:
301 sigprocmask(SIG_SETMASK, &oldmask, NULL);
302 return res;
303}
304
305int fuse_mnt_remove_mount(const char *progname, const char *mnt)
306{
307 if (!mtab_needs_update(mnt))
308 return 0;
309
310 return remove_mount(progname, mnt);
311}
312
313char *fuse_mnt_resolve_path(const char *progname, const char *orig)
314{
315 char buf[PATH_MAX];
316 char *copy;
317 char *dst;
318 char *end;
319 char *lastcomp;
320 const char *toresolv;
321
322 if (!orig[0]) {
323 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
324 orig);
325 return NULL;
326 }
327
328 copy = strdup(orig);
329 if (copy == NULL) {
330 fprintf(stderr, "%s: failed to allocate memory\n", progname);
331 return NULL;
332 }
333
334 toresolv = copy;
335 lastcomp = NULL;
336 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
337 if (end[0] != '/') {
338 char *tmp;
339 end[1] = '\0';
340 tmp = strrchr(copy, '/');
341 if (tmp == NULL) {
342 lastcomp = copy;
343 toresolv = ".";
344 } else {
345 lastcomp = tmp + 1;
346 if (tmp == copy)
347 toresolv = "/";
348 }
349 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
350 lastcomp = NULL;
351 toresolv = copy;
352 }
353 else if (tmp)
354 tmp[0] = '\0';
355 }
356 if (realpath(toresolv, buf) == NULL) {
357 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
358 strerror(errno));
359 free(copy);
360 return NULL;
361 }
362 if (lastcomp == NULL)
363 dst = strdup(buf);
364 else {
365 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
366 if (dst) {
367 unsigned buflen = strlen(buf);
368 if (buflen && buf[buflen-1] == '/')
369 sprintf(dst, "%s%s", buf, lastcomp);
370 else
371 sprintf(dst, "%s/%s", buf, lastcomp);
372 }
373 }
374 free(copy);
375 if (dst == NULL)
376 fprintf(stderr, "%s: failed to allocate memory\n", progname);
377 return dst;
378}
379
380int fuse_mnt_check_fuseblk(void)
381{
382 char buf[256];
383 FILE *f = fopen("/proc/filesystems", "r");
384 if (!f)
385 return 1;
386
387 while (fgets(buf, sizeof(buf), f))
388 if (strstr(buf, "fuseblk\n")) {
389 fclose(f);
390 return 1;
391 }
392
393 fclose(f);
394 return 0;
395}
396
397int fuse_mnt_parse_fuse_fd(const char *mountpoint)
398{
399 int fd = -1;
400 int len = 0;
401
402 if (mountpoint == NULL) {
403 fprintf(stderr, "Invalid null-ptr mount-point!\n");
404 return -1;
405 }
406
407 if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 &&
408 len == strlen(mountpoint)) {
409 return fd;
410 }
411
412 return -1;
413}
void fuse_log(enum fuse_log_level level, const char *fmt,...) __attribute__((format(printf