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 "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 #include <paths.h>
20 #ifndef __NetBSD__
21 #include <mntent.h>
22 #endif
23 #include <sys/stat.h>
24 #include <sys/wait.h>
25 #include <sys/mount.h>
26 #include <sys/param.h>
27
28 #ifdef __NetBSD__
29 #define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
30 #define mtab_needs_update(mnt) 0
31 #else
mtab_needs_update(const char * mnt)32 static int mtab_needs_update(const char *mnt)
33 {
34 int res;
35 struct stat stbuf;
36
37 /* If mtab is within new mount, don't touch it */
38 if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
39 _PATH_MOUNTED[strlen(mnt)] == '/')
40 return 0;
41
42 /*
43 * Skip mtab update if /etc/mtab:
44 *
45 * - doesn't exist,
46 * - is a symlink,
47 * - is on a read-only filesystem.
48 */
49 res = lstat(_PATH_MOUNTED, &stbuf);
50 if (res == -1) {
51 if (errno == ENOENT)
52 return 0;
53 } else {
54 uid_t ruid;
55 int err;
56
57 if (S_ISLNK(stbuf.st_mode))
58 return 0;
59
60 ruid = getuid();
61 if (ruid != 0)
62 setreuid(0, -1);
63
64 res = access(_PATH_MOUNTED, W_OK);
65 err = (res == -1) ? errno : 0;
66 if (ruid != 0)
67 setreuid(ruid, -1);
68
69 if (err == EROFS)
70 return 0;
71 }
72
73 return 1;
74 }
75 #endif /* __NetBSD__ */
76
add_mount(const char * progname,const char * fsname,const char * mnt,const char * type,const char * opts)77 static int add_mount(const char *progname, const char *fsname,
78 const char *mnt, const char *type, const char *opts)
79 {
80 int res;
81 int status;
82 sigset_t blockmask;
83 sigset_t oldmask;
84
85 sigemptyset(&blockmask);
86 sigaddset(&blockmask, SIGCHLD);
87 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
88 if (res == -1) {
89 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
90 return -1;
91 }
92
93 res = fork();
94 if (res == -1) {
95 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
96 goto out_restore;
97 }
98 if (res == 0) {
99 char *env = NULL;
100
101 sigprocmask(SIG_SETMASK, &oldmask, NULL);
102 setuid(geteuid());
103 execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
104 "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
105 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
106 progname, strerror(errno));
107 exit(1);
108 }
109 res = waitpid(res, &status, 0);
110 if (res == -1)
111 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
112
113 if (status != 0)
114 res = -1;
115
116 out_restore:
117 sigprocmask(SIG_SETMASK, &oldmask, NULL);
118
119 return res;
120 }
121
fuse_mnt_add_mount(const char * progname,const char * fsname,const char * mnt,const char * type,const char * opts)122 int fuse_mnt_add_mount(const char *progname, const char *fsname,
123 const char *mnt, const char *type, const char *opts)
124 {
125 if (!mtab_needs_update(mnt))
126 return 0;
127
128 return add_mount(progname, fsname, mnt, type, opts);
129 }
130
exec_umount(const char * progname,const char * rel_mnt,int lazy)131 static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
132 {
133 int res;
134 int status;
135 sigset_t blockmask;
136 sigset_t oldmask;
137
138 sigemptyset(&blockmask);
139 sigaddset(&blockmask, SIGCHLD);
140 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
141 if (res == -1) {
142 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
143 return -1;
144 }
145
146 res = fork();
147 if (res == -1) {
148 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
149 goto out_restore;
150 }
151 if (res == 0) {
152 char *env = NULL;
153
154 sigprocmask(SIG_SETMASK, &oldmask, NULL);
155 setuid(geteuid());
156 if (lazy) {
157 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
158 "-l", NULL, &env);
159 } else {
160 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
161 NULL, &env);
162 }
163 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
164 progname, strerror(errno));
165 exit(1);
166 }
167 res = waitpid(res, &status, 0);
168 if (res == -1)
169 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
170
171 if (status != 0) {
172 res = -1;
173 }
174
175 out_restore:
176 sigprocmask(SIG_SETMASK, &oldmask, NULL);
177 return res;
178
179 }
180
fuse_mnt_umount(const char * progname,const char * abs_mnt,const char * rel_mnt,int lazy)181 int fuse_mnt_umount(const char *progname, const char *abs_mnt,
182 const char *rel_mnt, int lazy)
183 {
184 int res;
185
186 if (!mtab_needs_update(abs_mnt)) {
187 res = umount2(rel_mnt, lazy ? 2 : 0);
188 if (res == -1)
189 fprintf(stderr, "%s: failed to unmount %s: %s\n",
190 progname, abs_mnt, strerror(errno));
191 return res;
192 }
193
194 return exec_umount(progname, rel_mnt, lazy);
195 }
196
remove_mount(const char * progname,const char * mnt)197 static int remove_mount(const char *progname, const char *mnt)
198 {
199 int res;
200 int status;
201 sigset_t blockmask;
202 sigset_t oldmask;
203
204 sigemptyset(&blockmask);
205 sigaddset(&blockmask, SIGCHLD);
206 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
207 if (res == -1) {
208 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
209 return -1;
210 }
211
212 res = fork();
213 if (res == -1) {
214 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
215 goto out_restore;
216 }
217 if (res == 0) {
218 char *env = NULL;
219
220 sigprocmask(SIG_SETMASK, &oldmask, NULL);
221 setuid(geteuid());
222 execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
223 "--fake", mnt, NULL, &env);
224 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
225 progname, strerror(errno));
226 exit(1);
227 }
228 res = waitpid(res, &status, 0);
229 if (res == -1)
230 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
231
232 if (status != 0)
233 res = -1;
234
235 out_restore:
236 sigprocmask(SIG_SETMASK, &oldmask, NULL);
237 return res;
238 }
239
fuse_mnt_remove_mount(const char * progname,const char * mnt)240 int fuse_mnt_remove_mount(const char *progname, const char *mnt)
241 {
242 if (!mtab_needs_update(mnt))
243 return 0;
244
245 return remove_mount(progname, mnt);
246 }
247
fuse_mnt_resolve_path(const char * progname,const char * orig)248 char *fuse_mnt_resolve_path(const char *progname, const char *orig)
249 {
250 char buf[PATH_MAX];
251 char *copy;
252 char *dst;
253 char *end;
254 char *lastcomp;
255 const char *toresolv;
256
257 if (!orig[0]) {
258 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
259 orig);
260 return NULL;
261 }
262
263 copy = strdup(orig);
264 if (copy == NULL) {
265 fprintf(stderr, "%s: failed to allocate memory\n", progname);
266 return NULL;
267 }
268
269 toresolv = copy;
270 lastcomp = NULL;
271 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
272 if (end[0] != '/') {
273 char *tmp;
274 end[1] = '\0';
275 tmp = strrchr(copy, '/');
276 if (tmp == NULL) {
277 lastcomp = copy;
278 toresolv = ".";
279 } else {
280 lastcomp = tmp + 1;
281 if (tmp == copy)
282 toresolv = "/";
283 }
284 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
285 lastcomp = NULL;
286 toresolv = copy;
287 }
288 else if (tmp)
289 tmp[0] = '\0';
290 }
291 if (realpath(toresolv, buf) == NULL) {
292 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
293 strerror(errno));
294 free(copy);
295 return NULL;
296 }
297 if (lastcomp == NULL)
298 dst = strdup(buf);
299 else {
300 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
301 if (dst) {
302 unsigned buflen = strlen(buf);
303 if (buflen && buf[buflen-1] == '/')
304 sprintf(dst, "%s%s", buf, lastcomp);
305 else
306 sprintf(dst, "%s/%s", buf, lastcomp);
307 }
308 }
309 free(copy);
310 if (dst == NULL)
311 fprintf(stderr, "%s: failed to allocate memory\n", progname);
312 return dst;
313 }
314
fuse_mnt_check_empty(const char * progname,const char * mnt,mode_t rootmode,off_t rootsize)315 int fuse_mnt_check_empty(const char *progname, const char *mnt,
316 mode_t rootmode, off_t rootsize)
317 {
318 int isempty = 1;
319
320 if (S_ISDIR(rootmode)) {
321 struct dirent *ent;
322 DIR *dp = opendir(mnt);
323 if (dp == NULL) {
324 fprintf(stderr,
325 "%s: failed to open mountpoint for reading: %s\n",
326 progname, strerror(errno));
327 return -1;
328 }
329 while ((ent = readdir(dp)) != NULL) {
330 if (strcmp(ent->d_name, ".") != 0 &&
331 strcmp(ent->d_name, "..") != 0) {
332 isempty = 0;
333 break;
334 }
335 }
336 closedir(dp);
337 } else if (rootsize)
338 isempty = 0;
339
340 if (!isempty) {
341 fprintf(stderr, "%s: mountpoint is not empty\n", progname);
342 fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
343 return -1;
344 }
345 return 0;
346 }
347