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