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