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