Merge branch 'fuse_2_9_bugfix'
[fuse.git] / lib / mount_util.c
blob3d2f4cd71ae8e8be4d85a0f8f2b0229bf642a8fc
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 "mount_util.h"
10 #include <stdio.h>
11 #include <unistd.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <signal.h>
15 #include <dirent.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <limits.h>
19 #ifndef __NetBSD__
20 #include <mntent.h>
21 #endif
22 #include <sys/stat.h>
23 #include <sys/wait.h>
24 #include <sys/mount.h>
25 #include <sys/param.h>
27 #ifdef __NetBSD__
28 #define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
29 #define mtab_needs_update(mnt) 0
30 #else
31 static int mtab_needs_update(const char *mnt)
33 int res;
34 struct stat stbuf;
36 /* If mtab is within new mount, don't touch it */
37 if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
38 _PATH_MOUNTED[strlen(mnt)] == '/')
39 return 0;
42 * Skip mtab update if /etc/mtab:
44 * - doesn't exist,
45 * - is a symlink,
46 * - is on a read-only filesystem.
48 res = lstat(_PATH_MOUNTED, &stbuf);
49 if (res == -1) {
50 if (errno == ENOENT)
51 return 0;
52 } else {
53 uid_t ruid;
54 int err;
56 if (S_ISLNK(stbuf.st_mode))
57 return 0;
59 ruid = getuid();
60 if (ruid != 0)
61 setreuid(0, -1);
63 res = access(_PATH_MOUNTED, W_OK);
64 err = (res == -1) ? errno : 0;
65 if (ruid != 0)
66 setreuid(ruid, -1);
68 if (err == EROFS)
69 return 0;
72 return 1;
74 #endif /* __NetBSD__ */
76 static int add_mount(const char *progname, const char *fsname,
77 const char *mnt, const char *type, const char *opts)
79 int res;
80 int status;
81 sigset_t blockmask;
82 sigset_t oldmask;
84 sigemptyset(&blockmask);
85 sigaddset(&blockmask, SIGCHLD);
86 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
87 if (res == -1) {
88 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
89 return -1;
92 res = fork();
93 if (res == -1) {
94 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
95 goto out_restore;
97 if (res == 0) {
98 sigprocmask(SIG_SETMASK, &oldmask, NULL);
99 setuid(geteuid());
100 execl("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
101 "-f", "-t", type, "-o", opts, fsname, mnt, NULL);
102 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
103 progname, strerror(errno));
104 exit(1);
106 res = waitpid(res, &status, 0);
107 if (res == -1)
108 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
110 if (status != 0)
111 res = -1;
113 out_restore:
114 sigprocmask(SIG_SETMASK, &oldmask, NULL);
116 return res;
119 int fuse_mnt_add_mount(const char *progname, const char *fsname,
120 const char *mnt, const char *type, const char *opts)
122 if (!mtab_needs_update(mnt))
123 return 0;
125 return add_mount(progname, fsname, mnt, type, opts);
128 static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
130 int res;
131 int status;
132 sigset_t blockmask;
133 sigset_t oldmask;
135 sigemptyset(&blockmask);
136 sigaddset(&blockmask, SIGCHLD);
137 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
138 if (res == -1) {
139 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
140 return -1;
143 res = fork();
144 if (res == -1) {
145 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
146 goto out_restore;
148 if (res == 0) {
149 sigprocmask(SIG_SETMASK, &oldmask, NULL);
150 setuid(geteuid());
151 execl("/bin/umount", "/bin/umount", "-i", rel_mnt,
152 lazy ? "-l" : NULL, NULL);
153 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
154 progname, strerror(errno));
155 exit(1);
157 res = waitpid(res, &status, 0);
158 if (res == -1)
159 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
161 if (status != 0) {
162 res = -1;
165 out_restore:
166 sigprocmask(SIG_SETMASK, &oldmask, NULL);
167 return res;
171 int fuse_mnt_umount(const char *progname, const char *abs_mnt,
172 const char *rel_mnt, int lazy)
174 int res;
176 if (!mtab_needs_update(abs_mnt)) {
177 res = umount2(rel_mnt, lazy ? 2 : 0);
178 if (res == -1)
179 fprintf(stderr, "%s: failed to unmount %s: %s\n",
180 progname, abs_mnt, strerror(errno));
181 return res;
184 return exec_umount(progname, rel_mnt, lazy);
187 static int remove_mount(const char *progname, const char *mnt)
189 int res;
190 int status;
191 sigset_t blockmask;
192 sigset_t oldmask;
194 sigemptyset(&blockmask);
195 sigaddset(&blockmask, SIGCHLD);
196 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
197 if (res == -1) {
198 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
199 return -1;
202 res = fork();
203 if (res == -1) {
204 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
205 goto out_restore;
207 if (res == 0) {
208 sigprocmask(SIG_SETMASK, &oldmask, NULL);
209 setuid(geteuid());
210 execl("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
211 "--fake", mnt, NULL);
212 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
213 progname, strerror(errno));
214 exit(1);
216 res = waitpid(res, &status, 0);
217 if (res == -1)
218 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
220 if (status != 0)
221 res = -1;
223 out_restore:
224 sigprocmask(SIG_SETMASK, &oldmask, NULL);
225 return res;
228 int fuse_mnt_remove_mount(const char *progname, const char *mnt)
230 if (!mtab_needs_update(mnt))
231 return 0;
233 return remove_mount(progname, mnt);
236 char *fuse_mnt_resolve_path(const char *progname, const char *orig)
238 char buf[PATH_MAX];
239 char *copy;
240 char *dst;
241 char *end;
242 char *lastcomp;
243 const char *toresolv;
245 if (!orig[0]) {
246 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
247 orig);
248 return NULL;
251 copy = strdup(orig);
252 if (copy == NULL) {
253 fprintf(stderr, "%s: failed to allocate memory\n", progname);
254 return NULL;
257 toresolv = copy;
258 lastcomp = NULL;
259 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
260 if (end[0] != '/') {
261 char *tmp;
262 end[1] = '\0';
263 tmp = strrchr(copy, '/');
264 if (tmp == NULL) {
265 lastcomp = copy;
266 toresolv = ".";
267 } else {
268 lastcomp = tmp + 1;
269 if (tmp == copy)
270 toresolv = "/";
272 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
273 lastcomp = NULL;
274 toresolv = copy;
276 else if (tmp)
277 tmp[0] = '\0';
279 if (realpath(toresolv, buf) == NULL) {
280 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
281 strerror(errno));
282 free(copy);
283 return NULL;
285 if (lastcomp == NULL)
286 dst = strdup(buf);
287 else {
288 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
289 if (dst) {
290 unsigned buflen = strlen(buf);
291 if (buflen && buf[buflen-1] == '/')
292 sprintf(dst, "%s%s", buf, lastcomp);
293 else
294 sprintf(dst, "%s/%s", buf, lastcomp);
297 free(copy);
298 if (dst == NULL)
299 fprintf(stderr, "%s: failed to allocate memory\n", progname);
300 return dst;
303 int fuse_mnt_check_empty(const char *progname, const char *mnt,
304 mode_t rootmode, off_t rootsize)
306 int isempty = 1;
308 if (S_ISDIR(rootmode)) {
309 struct dirent *ent;
310 DIR *dp = opendir(mnt);
311 if (dp == NULL) {
312 fprintf(stderr,
313 "%s: failed to open mountpoint for reading: %s\n",
314 progname, strerror(errno));
315 return -1;
317 while ((ent = readdir(dp)) != NULL) {
318 if (strcmp(ent->d_name, ".") != 0 &&
319 strcmp(ent->d_name, "..") != 0) {
320 isempty = 0;
321 break;
324 closedir(dp);
325 } else if (rootsize)
326 isempty = 0;
328 if (!isempty) {
329 fprintf(stderr, "%s: mountpoint is not empty\n", progname);
330 fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
331 return -1;
333 return 0;
336 int fuse_mnt_check_fuseblk(void)
338 char buf[256];
339 FILE *f = fopen("/proc/filesystems", "r");
340 if (!f)
341 return 1;
343 while (fgets(buf, sizeof(buf), f))
344 if (strstr(buf, "fuseblk\n")) {
345 fclose(f);
346 return 1;
349 fclose(f);
350 return 0;