• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2     FUSE: Filesystem in Userspace
3     Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
4 
5     This program can be distributed under the terms of the GNU LGPLv2.
6     See the file COPYING.LIB.
7 */
8 
9 #include "config.h"
10 #include "mount_util.h"
11 #include <stdio.h>
12 #include <unistd.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <dirent.h>
16 #include <errno.h>
17 #include <limits.h>
18 #include <paths.h>
19 #include <sys/stat.h>
20 #include <sys/wait.h>
21 #ifdef __SOLARIS__
22 #else /* __SOLARIS__ */
23 #include <mntent.h>
24 #include <sys/mount.h>
25 #include <sys/param.h>
26 #endif /* __SOLARIS__ */
27 
28 #ifdef __SOLARIS__
29 
30 char *mkdtemp(char *template);
31 
32 #ifndef _PATH_MOUNTED
33 #define _PATH_MOUNTED "/etc/mnttab"
34 #endif /* _PATH_MOUNTED */
35 
36 #ifndef IGNORE_MTAB
mtab_needs_update(const char * mnt)37 static int mtab_needs_update(const char *mnt)
38 {
39     struct stat stbuf;
40 
41     /* If mtab is within new mount, don't touch it */
42     if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
43         _PATH_MOUNTED[strlen(mnt)] == '/')
44         return 0;
45 
46     if (lstat(_PATH_MOUNTED, &stbuf) != -1 && S_ISLNK(stbuf.st_mode))
47         return 0;
48 
49     return 1;
50 }
51 #endif /* IGNORE_MTAB */
52 
fuse_mnt_add_mount(const char * progname,const char * fsname,const char * mnt,const char * type,const char * opts)53 int fuse_mnt_add_mount(const char *progname, const char *fsname,
54                        const char *mnt, const char *type, const char *opts)
55 {
56     int res;
57     int status;
58 
59 #ifndef IGNORE_MTAB
60     if (!mtab_needs_update(mnt))
61         return 0;
62 #endif /* IGNORE_MTAB */
63 
64     res = fork();
65     if (res == -1) {
66         fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
67         return -1;
68     }
69     if (res == 0) {
70         char *env = NULL;
71         char templ[] = "/tmp/fusermountXXXXXX";
72         char *tmp;
73 
74         setuid(geteuid());
75 
76         /*
77          * hide in a directory, where mount isn't able to resolve
78          * fsname as a valid path
79          */
80         tmp = mkdtemp(templ);
81         if (!tmp) {
82             fprintf(stderr, "%s: failed to create temporary directory\n",
83                     progname);
84             exit(1);
85         }
86         if (chdir(tmp)) {
87             fprintf(stderr, "%s: failed to chdir to %s: %s\n",
88                     progname, tmp, strerror(errno));
89             exit(1);
90         }
91         rmdir(tmp);
92         execle("/sbin/mount", "/sbin/mount", "-F", type, "-o", opts,
93               fsname, mnt, NULL, &env);
94         fprintf(stderr, "%s: failed to execute /sbin/mount: %s\n", progname,
95                 strerror(errno));
96         exit(1);
97     }
98     res = waitpid(res, &status, 0);
99     if (res == -1) {
100         fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
101         return -1;
102     }
103     if (status != 0)
104         return -1;
105 
106     return 0;
107 }
108 
fuse_mnt_umount(const char * progname,const char * mnt,int lazy)109 int fuse_mnt_umount(const char *progname, const char *mnt, int lazy)
110 {
111     int res;
112     int status;
113 
114 #ifndef IGNORE_MTAB
115     if (!mtab_needs_update(mnt))
116         return 0;
117 #endif /* IGNORE_MTAB */
118 
119     res = fork();
120     if (res == -1) {
121         fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
122         return -1;
123     }
124     if (res == 0) {
125         char *env = NULL;
126 
127         setuid(geteuid());
128         if (lazy) {
129             execle("/sbin/umount", "/sbin/umount", mnt,
130                    NULL, &env);
131         } else {
132             execle("/sbin/umount", "/sbin/umount", "-f", mnt,
133                    NULL, &env);
134         }
135         fprintf(stderr, "%s: failed to execute /sbin/umount: %s\n", progname,
136                 strerror(errno));
137         exit(1);
138     }
139     res = waitpid(res, &status, 0);
140     if (res == -1) {
141         fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
142         return -1;
143     }
144     if (status != 0)
145         return -1;
146 
147     return 0;
148 }
149 
fuse_mnt_resolve_path(const char * progname,const char * orig)150 char *fuse_mnt_resolve_path(const char *progname, const char *orig)
151 {
152     char buf[PATH_MAX];
153     char *copy;
154     char *dst;
155     char *end;
156     char *lastcomp;
157     const char *toresolv;
158 
159     if (!orig[0]) {
160         fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname, orig);
161         return NULL;
162     }
163 
164     copy = strdup(orig);
165     if (copy == NULL) {
166         fprintf(stderr, "%s: failed to allocate memory\n", progname);
167         return NULL;
168     }
169 
170     toresolv = copy;
171     lastcomp = NULL;
172     for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
173     if (end[0] != '/') {
174         char *tmp;
175         end[1] = '\0';
176         tmp = strrchr(copy, '/');
177         if (tmp == NULL) {
178             lastcomp = copy;
179             toresolv = ".";
180         } else {
181             lastcomp = tmp + 1;
182             if (tmp == copy)
183                 toresolv = "/";
184         }
185         if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
186             lastcomp = NULL;
187             toresolv = copy;
188         }
189         else if (tmp)
190             tmp[0] = '\0';
191     }
192     if (realpath(toresolv, buf) == NULL) {
193         fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
194                 strerror(errno));
195         free(copy);
196         return NULL;
197     }
198     if (lastcomp == NULL)
199         dst = strdup(buf);
200     else {
201         dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
202         if (dst) {
203             unsigned buflen = strlen(buf);
204             if (buflen && buf[buflen-1] == '/')
205                 sprintf(dst, "%s%s", buf, lastcomp);
206             else
207                 sprintf(dst, "%s/%s", buf, lastcomp);
208         }
209     }
210     free(copy);
211     if (dst == NULL)
212         fprintf(stderr, "%s: failed to allocate memory\n", progname);
213     return dst;
214 }
215 
fuse_mnt_check_empty(const char * progname,const char * mnt,mode_t rootmode,off_t rootsize)216 int fuse_mnt_check_empty(const char *progname, const char *mnt,
217                          mode_t rootmode, off_t rootsize)
218 {
219     int isempty = 1;
220 
221     if (S_ISDIR(rootmode)) {
222         struct dirent *ent;
223         DIR *dp = opendir(mnt);
224         if (dp == NULL) {
225             fprintf(stderr, "%s: failed to open mountpoint for reading: %s\n",
226                     progname, strerror(errno));
227             return -1;
228         }
229         while ((ent = readdir(dp)) != NULL) {
230             if (strcmp(ent->d_name, ".") != 0 &&
231                 strcmp(ent->d_name, "..") != 0) {
232                 isempty = 0;
233                 break;
234             }
235         }
236         closedir(dp);
237     } else if (rootsize)
238         isempty = 0;
239 
240     if (!isempty) {
241         fprintf(stderr, "%s: mountpoint is not empty\n", progname);
242         fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
243         return -1;
244     }
245     return 0;
246 }
247 
fuse_mnt_check_fuseblk(void)248 int fuse_mnt_check_fuseblk(void)
249 {
250     char buf[256];
251     FILE *f = fopen("/proc/filesystems", "r");
252     if (!f)
253         return 1;
254 
255     while (fgets(buf, sizeof(buf), f))
256         if (strstr(buf, "fuseblk\n")) {
257             fclose(f);
258             return 1;
259         }
260 
261     fclose(f);
262     return 0;
263 }
264 
265 #else /* __SOLARIS__ */
266 
mtab_needs_update(const char * mnt)267 static int mtab_needs_update(const char *mnt)
268 {
269 	int res;
270 	struct stat stbuf;
271 
272 	/* If mtab is within new mount, don't touch it */
273 	if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
274 	    _PATH_MOUNTED[strlen(mnt)] == '/')
275 		return 0;
276 
277 	/*
278 	 * Skip mtab update if /etc/mtab:
279 	 *
280 	 *  - doesn't exist,
281 	 *  - is a symlink,
282 	 *  - is on a read-only filesystem.
283 	 */
284 	res = lstat(_PATH_MOUNTED, &stbuf);
285 	if (res == -1) {
286 		if (errno == ENOENT)
287 			return 0;
288 	} else {
289 		if (S_ISLNK(stbuf.st_mode))
290 			return 0;
291 
292 		res = access(_PATH_MOUNTED, W_OK);
293 		if (res == -1 && errno == EROFS)
294 			return 0;
295 	}
296 
297 	return 1;
298 }
299 
fuse_mnt_add_mount(const char * progname,const char * fsname,const char * mnt,const char * type,const char * opts)300 int fuse_mnt_add_mount(const char *progname, const char *fsname,
301                        const char *mnt, const char *type, const char *opts)
302 {
303     int res;
304 
305     if (!mtab_needs_update(mnt))
306         return 0;
307 
308     res = fork();
309     if (res == -1) {
310         fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
311         return 0;
312     }
313     if (res == 0) {
314         char *env = NULL;
315         char templ[] = "/tmp/fusermountXXXXXX";
316         char *tmp;
317 
318         if (setuid(geteuid()))
319             fprintf(stderr, "%s: failed to setuid : %s\n", progname,
320                              strerror(errno));
321 
322         /*
323          * hide in a directory, where mount isn't able to resolve
324          * fsname as a valid path
325          */
326         tmp = mkdtemp(templ);
327         if (!tmp) {
328             fprintf(stderr, "%s: failed to create temporary directory\n",
329                     progname);
330             exit(1);
331         }
332         if (chdir(tmp)) {
333             fprintf(stderr, "%s: failed to chdir to %s: %s\n",
334                     progname, tmp, strerror(errno));
335             exit(1);
336         }
337         rmdir(tmp);
338         execle("/bin/mount", "/bin/mount", "-i", "-f", "-t", type, "-o", opts,
339                fsname, mnt, NULL, &env);
340         fprintf(stderr, "%s: failed to execute /bin/mount: %s\n", progname,
341                 strerror(errno));
342         exit(1);
343     }
344     return 0;
345 }
346 
fuse_mnt_umount(const char * progname,const char * mnt,int lazy)347 int fuse_mnt_umount(const char *progname, const char *mnt, int lazy)
348 {
349     int res;
350     int status;
351 
352     if (!mtab_needs_update(mnt)) {
353         res = umount2(mnt, lazy ? 2 : 0);
354         if (res == -1)
355             fprintf(stderr, "%s: failed to unmount %s: %s\n", progname,
356                     mnt, strerror(errno));
357         return res;
358     }
359 
360     res = fork();
361     if (res == -1) {
362         fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
363         return -1;
364     }
365     if (res == 0) {
366         char *env = NULL;
367 
368         if (setuid(geteuid()))
369             fprintf(stderr, "%s: failed to setuid : %s\n", progname,
370                          strerror(errno));
371         if (lazy) {
372             execle("/bin/umount", "/bin/umount", "-i", mnt, "-l",
373                    NULL, &env);
374         } else {
375             execle("/bin/umount", "/bin/umount", "-i", mnt,
376                    NULL, &env);
377         }
378         fprintf(stderr, "%s: failed to execute /bin/umount: %s\n", progname,
379                 strerror(errno));
380         exit(1);
381     }
382     res = waitpid(res, &status, 0);
383     if (res == -1) {
384         fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
385         return -1;
386     }
387     if (status != 0)
388         return -1;
389 
390     return 0;
391 }
392 
fuse_mnt_resolve_path(const char * progname,const char * orig)393 char *fuse_mnt_resolve_path(const char *progname, const char *orig)
394 {
395     char buf[PATH_MAX];
396     char *copy;
397     char *dst;
398     char *end;
399     char *lastcomp;
400     const char *toresolv;
401 
402     if (!orig[0]) {
403         fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname, orig);
404         return NULL;
405     }
406 
407     copy = strdup(orig);
408     if (copy == NULL) {
409         fprintf(stderr, "%s: failed to allocate memory\n", progname);
410         return NULL;
411     }
412 
413     toresolv = copy;
414     lastcomp = NULL;
415     for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
416     if (end[0] != '/') {
417         char *tmp;
418         end[1] = '\0';
419         tmp = strrchr(copy, '/');
420         if (tmp == NULL) {
421             lastcomp = copy;
422             toresolv = ".";
423         } else {
424             lastcomp = tmp + 1;
425             if (tmp == copy)
426                 toresolv = "/";
427         }
428         if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
429             lastcomp = NULL;
430             toresolv = copy;
431         }
432         else if (tmp)
433             tmp[0] = '\0';
434     }
435     if (realpath(toresolv, buf) == NULL) {
436         fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
437                 strerror(errno));
438         free(copy);
439         return NULL;
440     }
441     if (lastcomp == NULL)
442         dst = strdup(buf);
443     else {
444         dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
445         if (dst) {
446             unsigned buflen = strlen(buf);
447             if (buflen && buf[buflen-1] == '/')
448                 sprintf(dst, "%s%s", buf, lastcomp);
449             else
450                 sprintf(dst, "%s/%s", buf, lastcomp);
451         }
452     }
453     free(copy);
454     if (dst == NULL)
455         fprintf(stderr, "%s: failed to allocate memory\n", progname);
456     return dst;
457 }
458 
fuse_mnt_check_fuseblk(void)459 int fuse_mnt_check_fuseblk(void)
460 {
461     char buf[256];
462     FILE *f = fopen("/proc/filesystems", "r");
463     if (!f)
464         return 1;
465 
466     while (fgets(buf, sizeof(buf), f))
467         if (strstr(buf, "fuseblk\n")) {
468             fclose(f);
469             return 1;
470         }
471 
472     fclose(f);
473     return 0;
474 }
475 
476 #endif /* __SOLARIS__ */
477