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