libfuse
mount_util.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Architecture-independent mounting code.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file COPYING.LIB.
9*/
10
11#include "fuse_config.h"
12#include "mount_util.h"
13
14#include <stdio.h>
15#include <unistd.h>
16#include <stdlib.h>
17#include <string.h>
18#include <signal.h>
19#include <dirent.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <limits.h>
23#include <paths.h>
24#if !defined( __NetBSD__) && !defined(__FreeBSD__) && !defined(__DragonFly__)
25#include <mntent.h>
26#else
27#define IGNORE_MTAB
28#endif
29#include <sys/stat.h>
30#include <sys/wait.h>
31
32#include "fuse_mount_compat.h"
33
34#include <sys/param.h>
35
36#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
37#define umount2(mnt, flags) unmount(mnt, ((flags) == 2) ? MNT_FORCE : 0)
38#endif
39
40#ifdef IGNORE_MTAB
41#define mtab_needs_update(mnt) 0
42#else
43static int mtab_needs_update(const char *mnt)
44{
45 int res;
46 struct stat stbuf;
47
48 /* If mtab is within new mount, don't touch it */
49 if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
50 _PATH_MOUNTED[strlen(mnt)] == '/')
51 return 0;
52
53 /*
54 * Skip mtab update if /etc/mtab:
55 *
56 * - doesn't exist,
57 * - is a symlink,
58 * - is on a read-only filesystem.
59 */
60 res = lstat(_PATH_MOUNTED, &stbuf);
61 if (res == -1) {
62 if (errno == ENOENT)
63 return 0;
64 } else {
65 uid_t ruid;
66 int err;
67
68 if (S_ISLNK(stbuf.st_mode))
69 return 0;
70
71 ruid = getuid();
72 if (ruid != 0)
73 setreuid(0, -1);
74
75 res = access(_PATH_MOUNTED, W_OK);
76 err = (res == -1) ? errno : 0;
77 if (ruid != 0)
78 setreuid(ruid, -1);
79
80 if (err == EROFS)
81 return 0;
82 }
83
84 return 1;
85}
86#endif /* IGNORE_MTAB */
87
88static int add_mount(const char *progname, const char *fsname,
89 const char *mnt, const char *type, const char *opts)
90{
91 int res;
92 int status;
93 sigset_t blockmask;
94 sigset_t oldmask;
95
96 sigemptyset(&blockmask);
97 sigaddset(&blockmask, SIGCHLD);
98 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
99 if (res == -1) {
100 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
101 return -1;
102 }
103
104 res = fork();
105 if (res == -1) {
106 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
107 goto out_restore;
108 }
109 if (res == 0) {
110 char *env = NULL;
111
112 sigprocmask(SIG_SETMASK, &oldmask, NULL);
113
114 if(setuid(geteuid()) == -1) {
115 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
116 res = -1;
117 goto out_restore;
118 }
119
120 execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
121 "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
122 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
123 progname, strerror(errno));
124 exit(1);
125 }
126 res = waitpid(res, &status, 0);
127 if (res == -1)
128 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
129
130 if (status != 0)
131 res = -1;
132
133 out_restore:
134 sigprocmask(SIG_SETMASK, &oldmask, NULL);
135
136 return res;
137}
138
139int fuse_mnt_add_mount(const char *progname, const char *fsname,
140 const char *mnt, const char *type, const char *opts)
141{
142 if (!mtab_needs_update(mnt))
143 return 0;
144
145 return add_mount(progname, fsname, mnt, type, opts);
146}
147
148static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
149{
150 int res;
151 int status;
152 sigset_t blockmask;
153 sigset_t oldmask;
154
155 sigemptyset(&blockmask);
156 sigaddset(&blockmask, SIGCHLD);
157 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
158 if (res == -1) {
159 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
160 return -1;
161 }
162
163 res = fork();
164 if (res == -1) {
165 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
166 goto out_restore;
167 }
168 if (res == 0) {
169 char *env = NULL;
170
171 sigprocmask(SIG_SETMASK, &oldmask, NULL);
172
173 if(setuid(geteuid()) == -1) {
174 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
175 res = -1;
176 goto out_restore;
177 }
178
179 if (lazy) {
180 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
181 "-l", NULL, &env);
182 } else {
183 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
184 NULL, &env);
185 }
186 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
187 progname, strerror(errno));
188 exit(1);
189 }
190 res = waitpid(res, &status, 0);
191 if (res == -1)
192 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
193
194 if (status != 0) {
195 res = -1;
196 }
197
198 out_restore:
199 sigprocmask(SIG_SETMASK, &oldmask, NULL);
200 return res;
201
202}
203
204int fuse_mnt_umount(const char *progname, const char *abs_mnt,
205 const char *rel_mnt, int lazy)
206{
207 int res;
208
209 if (!mtab_needs_update(abs_mnt)) {
210 res = umount2(rel_mnt, lazy ? 2 : 0);
211 if (res == -1)
212 fprintf(stderr, "%s: failed to unmount %s: %s\n",
213 progname, abs_mnt, strerror(errno));
214 return res;
215 }
216
217 return exec_umount(progname, rel_mnt, lazy);
218}
219
220static int remove_mount(const char *progname, const char *mnt)
221{
222 int res;
223 int status;
224 sigset_t blockmask;
225 sigset_t oldmask;
226
227 sigemptyset(&blockmask);
228 sigaddset(&blockmask, SIGCHLD);
229 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
230 if (res == -1) {
231 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
232 return -1;
233 }
234
235 res = fork();
236 if (res == -1) {
237 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
238 goto out_restore;
239 }
240 if (res == 0) {
241 char *env = NULL;
242
243 sigprocmask(SIG_SETMASK, &oldmask, NULL);
244
245 if(setuid(geteuid()) == -1) {
246 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
247 res = -1;
248 goto out_restore;
249 }
250
251 execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
252 "--fake", mnt, NULL, &env);
253 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
254 progname, strerror(errno));
255 exit(1);
256 }
257 res = waitpid(res, &status, 0);
258 if (res == -1)
259 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
260
261 if (status != 0)
262 res = -1;
263
264 out_restore:
265 sigprocmask(SIG_SETMASK, &oldmask, NULL);
266 return res;
267}
268
269int fuse_mnt_remove_mount(const char *progname, const char *mnt)
270{
271 if (!mtab_needs_update(mnt))
272 return 0;
273
274 return remove_mount(progname, mnt);
275}
276
277char *fuse_mnt_resolve_path(const char *progname, const char *orig)
278{
279 char buf[PATH_MAX];
280 char *copy;
281 char *dst;
282 char *end;
283 char *lastcomp;
284 const char *toresolv;
285
286 if (!orig[0]) {
287 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
288 orig);
289 return NULL;
290 }
291
292 copy = strdup(orig);
293 if (copy == NULL) {
294 fprintf(stderr, "%s: failed to allocate memory\n", progname);
295 return NULL;
296 }
297
298 toresolv = copy;
299 lastcomp = NULL;
300 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
301 if (end[0] != '/') {
302 char *tmp;
303 end[1] = '\0';
304 tmp = strrchr(copy, '/');
305 if (tmp == NULL) {
306 lastcomp = copy;
307 toresolv = ".";
308 } else {
309 lastcomp = tmp + 1;
310 if (tmp == copy)
311 toresolv = "/";
312 }
313 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
314 lastcomp = NULL;
315 toresolv = copy;
316 }
317 else if (tmp)
318 tmp[0] = '\0';
319 }
320 if (realpath(toresolv, buf) == NULL) {
321 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
322 strerror(errno));
323 free(copy);
324 return NULL;
325 }
326 if (lastcomp == NULL)
327 dst = strdup(buf);
328 else {
329 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
330 if (dst) {
331 unsigned buflen = strlen(buf);
332 if (buflen && buf[buflen-1] == '/')
333 sprintf(dst, "%s%s", buf, lastcomp);
334 else
335 sprintf(dst, "%s/%s", buf, lastcomp);
336 }
337 }
338 free(copy);
339 if (dst == NULL)
340 fprintf(stderr, "%s: failed to allocate memory\n", progname);
341 return dst;
342}
343
344int fuse_mnt_check_fuseblk(void)
345{
346 char buf[256];
347 FILE *f = fopen("/proc/filesystems", "r");
348 if (!f)
349 return 1;
350
351 while (fgets(buf, sizeof(buf), f))
352 if (strstr(buf, "fuseblk\n")) {
353 fclose(f);
354 return 1;
355 }
356
357 fclose(f);
358 return 0;
359}
360
361int fuse_mnt_parse_fuse_fd(const char *mountpoint)
362{
363 int fd = -1;
364 int len = 0;
365
366 if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 &&
367 len == strlen(mountpoint)) {
368 return fd;
369 }
370
371 return -1;
372}