libfuse: fix exec environment for mount and umount
[fuse.git] / lib / mount_util.c
blob589f76d7aa38155caab5403c0b08d73bdbae9dec
1 /*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB.
7 */
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 <signal.h>
16 #include <dirent.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <limits.h>
20 #include <paths.h>
21 #ifndef __NetBSD__
22 #include <mntent.h>
23 #endif
24 #include <sys/stat.h>
25 #include <sys/wait.h>
26 #include <sys/mount.h>
27 #include <sys/param.h>
29 #ifdef __NetBSD__
30 #define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
31 #define mtab_needs_update(mnt) 0
32 #else
33 static int mtab_needs_update(const char *mnt)
35 int res;
36 struct stat stbuf;
38 /* If mtab is within new mount, don't touch it */
39 if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
40 _PATH_MOUNTED[strlen(mnt)] == '/')
41 return 0;
44 * Skip mtab update if /etc/mtab:
46 * - doesn't exist,
47 * - is a symlink,
48 * - is on a read-only filesystem.
50 res = lstat(_PATH_MOUNTED, &stbuf);
51 if (res == -1) {
52 if (errno == ENOENT)
53 return 0;
54 } else {
55 uid_t ruid;
56 int err;
58 if (S_ISLNK(stbuf.st_mode))
59 return 0;
61 ruid = getuid();
62 if (ruid != 0)
63 setreuid(0, -1);
65 res = access(_PATH_MOUNTED, W_OK);
66 err = (res == -1) ? errno : 0;
67 if (ruid != 0)
68 setreuid(ruid, -1);
70 if (err == EROFS)
71 return 0;
74 return 1;
76 #endif /* __NetBSD__ */
78 static int add_mount(const char *progname, const char *fsname,
79 const char *mnt, const char *type, const char *opts)
81 int res;
82 int status;
83 sigset_t blockmask;
84 sigset_t oldmask;
86 sigemptyset(&blockmask);
87 sigaddset(&blockmask, SIGCHLD);
88 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
89 if (res == -1) {
90 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
91 return -1;
94 res = fork();
95 if (res == -1) {
96 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
97 goto out_restore;
99 if (res == 0) {
100 char *env = NULL;
102 sigprocmask(SIG_SETMASK, &oldmask, NULL);
103 setuid(geteuid());
104 execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
105 "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
106 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
107 progname, strerror(errno));
108 exit(1);
110 res = waitpid(res, &status, 0);
111 if (res == -1)
112 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
114 if (status != 0)
115 res = -1;
117 out_restore:
118 sigprocmask(SIG_SETMASK, &oldmask, NULL);
120 return res;
123 int fuse_mnt_add_mount(const char *progname, const char *fsname,
124 const char *mnt, const char *type, const char *opts)
126 if (!mtab_needs_update(mnt))
127 return 0;
129 return add_mount(progname, fsname, mnt, type, opts);
132 static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
134 int res;
135 int status;
136 sigset_t blockmask;
137 sigset_t oldmask;
139 sigemptyset(&blockmask);
140 sigaddset(&blockmask, SIGCHLD);
141 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
142 if (res == -1) {
143 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
144 return -1;
147 res = fork();
148 if (res == -1) {
149 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
150 goto out_restore;
152 if (res == 0) {
153 char *env = NULL;
155 sigprocmask(SIG_SETMASK, &oldmask, NULL);
156 setuid(geteuid());
157 if (lazy) {
158 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
159 "-l", NULL, &env);
160 } else {
161 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
162 NULL, &env);
164 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
165 progname, strerror(errno));
166 exit(1);
168 res = waitpid(res, &status, 0);
169 if (res == -1)
170 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
172 if (status != 0) {
173 res = -1;
176 out_restore:
177 sigprocmask(SIG_SETMASK, &oldmask, NULL);
178 return res;
182 int fuse_mnt_umount(const char *progname, const char *abs_mnt,
183 const char *rel_mnt, int lazy)
185 int res;
187 if (!mtab_needs_update(abs_mnt)) {
188 res = umount2(rel_mnt, lazy ? 2 : 0);
189 if (res == -1)
190 fprintf(stderr, "%s: failed to unmount %s: %s\n",
191 progname, abs_mnt, strerror(errno));
192 return res;
195 return exec_umount(progname, rel_mnt, lazy);
198 static int remove_mount(const char *progname, const char *mnt)
200 int res;
201 int status;
202 sigset_t blockmask;
203 sigset_t oldmask;
205 sigemptyset(&blockmask);
206 sigaddset(&blockmask, SIGCHLD);
207 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
208 if (res == -1) {
209 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
210 return -1;
213 res = fork();
214 if (res == -1) {
215 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
216 goto out_restore;
218 if (res == 0) {
219 char *env = NULL;
221 sigprocmask(SIG_SETMASK, &oldmask, NULL);
222 setuid(geteuid());
223 execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
224 "--fake", mnt, NULL, &env);
225 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
226 progname, strerror(errno));
227 exit(1);
229 res = waitpid(res, &status, 0);
230 if (res == -1)
231 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
233 if (status != 0)
234 res = -1;
236 out_restore:
237 sigprocmask(SIG_SETMASK, &oldmask, NULL);
238 return res;
241 int fuse_mnt_remove_mount(const char *progname, const char *mnt)
243 if (!mtab_needs_update(mnt))
244 return 0;
246 return remove_mount(progname, mnt);
249 char *fuse_mnt_resolve_path(const char *progname, const char *orig)
251 char buf[PATH_MAX];
252 char *copy;
253 char *dst;
254 char *end;
255 char *lastcomp;
256 const char *toresolv;
258 if (!orig[0]) {
259 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
260 orig);
261 return NULL;
264 copy = strdup(orig);
265 if (copy == NULL) {
266 fprintf(stderr, "%s: failed to allocate memory\n", progname);
267 return NULL;
270 toresolv = copy;
271 lastcomp = NULL;
272 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
273 if (end[0] != '/') {
274 char *tmp;
275 end[1] = '\0';
276 tmp = strrchr(copy, '/');
277 if (tmp == NULL) {
278 lastcomp = copy;
279 toresolv = ".";
280 } else {
281 lastcomp = tmp + 1;
282 if (tmp == copy)
283 toresolv = "/";
285 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
286 lastcomp = NULL;
287 toresolv = copy;
289 else if (tmp)
290 tmp[0] = '\0';
292 if (realpath(toresolv, buf) == NULL) {
293 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
294 strerror(errno));
295 free(copy);
296 return NULL;
298 if (lastcomp == NULL)
299 dst = strdup(buf);
300 else {
301 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
302 if (dst) {
303 unsigned buflen = strlen(buf);
304 if (buflen && buf[buflen-1] == '/')
305 sprintf(dst, "%s%s", buf, lastcomp);
306 else
307 sprintf(dst, "%s/%s", buf, lastcomp);
310 free(copy);
311 if (dst == NULL)
312 fprintf(stderr, "%s: failed to allocate memory\n", progname);
313 return dst;
316 int fuse_mnt_check_empty(const char *progname, const char *mnt,
317 mode_t rootmode, off_t rootsize)
319 int isempty = 1;
321 if (S_ISDIR(rootmode)) {
322 struct dirent *ent;
323 DIR *dp = opendir(mnt);
324 if (dp == NULL) {
325 fprintf(stderr,
326 "%s: failed to open mountpoint for reading: %s\n",
327 progname, strerror(errno));
328 return -1;
330 while ((ent = readdir(dp)) != NULL) {
331 if (strcmp(ent->d_name, ".") != 0 &&
332 strcmp(ent->d_name, "..") != 0) {
333 isempty = 0;
334 break;
337 closedir(dp);
338 } else if (rootsize)
339 isempty = 0;
341 if (!isempty) {
342 fprintf(stderr, "%s: mountpoint is not empty\n", progname);
343 fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
344 return -1;
346 return 0;
349 int fuse_mnt_check_fuseblk(void)
351 char buf[256];
352 FILE *f = fopen("/proc/filesystems", "r");
353 if (!f)
354 return 1;
356 while (fgets(buf, sizeof(buf), f))
357 if (strstr(buf, "fuseblk\n")) {
358 fclose(f);
359 return 1;
362 fclose(f);
363 return 0;