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