blob: de1bd67140210fc0014d5927539c04516d317faa [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>
14#include <dirent.h>
15#include <errno.h>
16#include <fcntl.h>
17#include <limits.h>
18#include <mntent.h>
19#include <sys/stat.h>
20#include <sys/wait.h>
21#include <sys/mount.h>
22#include <sys/param.h>
23
24#include <paths.h>
25
26static int mtab_needs_update(const char *mnt)
27{
28 int res;
29 struct stat stbuf;
30
31 /* If mtab is within new mount, don't touch it */
32 if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
33 _PATH_MOUNTED[strlen(mnt)] == '/')
34 return 0;
35
36 /*
37 * Skip mtab update if /etc/mtab:
38 *
39 * - doesn't exist,
40 * - is a symlink,
41 * - is on a read-only filesystem.
42 */
43 res = lstat(_PATH_MOUNTED, &stbuf);
44 if (res == -1) {
45 if (errno == ENOENT)
46 return 0;
47 } else {
48 if (S_ISLNK(stbuf.st_mode))
49 return 0;
50
51 res = access(_PATH_MOUNTED, W_OK);
52 if (res == -1 && errno == EROFS)
53 return 0;
54 }
55
56 return 1;
57}
58
59static int add_mount_legacy(const char *progname, const char *fsname,
60 const char *mnt, const char *type, const char *opts)
61{
62 int res;
63 int status;
64 sigset_t blockmask;
65 sigset_t oldmask;
66
67 sigemptyset(&blockmask);
68 sigaddset(&blockmask, SIGCHLD);
69 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
70 if (res == -1) {
71 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
72 return -1;
73 }
74
75 res = fork();
76 if (res == -1) {
77 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
78 goto out_restore;
79 }
80 if (res == 0) {
81 char templ[] = "/tmp/fusermountXXXXXX";
82 char *tmp;
83
84 sigprocmask(SIG_SETMASK, &oldmask, NULL);
85 setuid(geteuid());
86
87 /*
88 * hide in a directory, where mount isn't able to resolve
89 * fsname as a valid path
90 */
91 tmp = mkdtemp(templ);
92 if (!tmp) {
93 fprintf(stderr,
94 "%s: failed to create temporary directory\n",
95 progname);
96 exit(1);
97 }
98 if (chdir(tmp)) {
99 fprintf(stderr, "%s: failed to chdir to %s: %s\n",
100 progname, tmp, strerror(errno));
101 exit(1);
102 }
103 rmdir(tmp);
104 execl("/bin/mount", "/bin/mount", "-i", "-f", "-t", type,
105 "-o", opts, fsname, mnt, NULL);
106 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
107 progname, strerror(errno));
108 exit(1);
109 }
110 res = waitpid(res, &status, 0);
111 if (res == -1)
112 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
113
114 if (status != 0)
115 res = -1;
116
117 out_restore:
118 sigprocmask(SIG_SETMASK, &oldmask, NULL);
119
120 return res;
121}
122
123static int add_mount(const char *progname, const char *fsname,
124 const char *mnt, const char *type, const char *opts)
125{
126 int res;
127 int status;
128 sigset_t blockmask;
129 sigset_t oldmask;
130
131 sigemptyset(&blockmask);
132 sigaddset(&blockmask, SIGCHLD);
133 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
134 if (res == -1) {
135 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
136 return -1;
137 }
138
139 res = fork();
140 if (res == -1) {
141 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
142 goto out_restore;
143 }
144 if (res == 0) {
145 /*
146 * Hide output, because old versions don't support
147 * --no-canonicalize
148 */
149 int fd = open("/dev/null", O_RDONLY);
150 dup2(fd, 1);
151 dup2(fd, 2);
152
153 sigprocmask(SIG_SETMASK, &oldmask, NULL);
154 setuid(geteuid());
155 execl("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
156 "-f", "-t", type, "-o", opts, fsname, mnt, NULL);
157 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
158 progname, strerror(errno));
159 exit(1);
160 }
161 res = waitpid(res, &status, 0);
162 if (res == -1)
163 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
164
165 if (status != 0)
166 res = -1;
167
168 out_restore:
169 sigprocmask(SIG_SETMASK, &oldmask, NULL);
170
171 return res;
172}
173
174int fuse_mnt_add_mount(const char *progname, const char *fsname,
175 const char *mnt, const char *type, const char *opts)
176{
177 int res;
178
179 if (!mtab_needs_update(mnt))
180 return 0;
181
182 res = add_mount(progname, fsname, mnt, type, opts);
183 if (res == -1)
184 res = add_mount_legacy(progname, fsname, mnt, type, opts);
185
186 return res;
187}
188
189static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
190{
191 int res;
192 int status;
193 sigset_t blockmask;
194 sigset_t oldmask;
195
196 sigemptyset(&blockmask);
197 sigaddset(&blockmask, SIGCHLD);
198 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
199 if (res == -1) {
200 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
201 return -1;
202 }
203
204 res = fork();
205 if (res == -1) {
206 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
207 goto out_restore;
208 }
209 if (res == 0) {
210 sigprocmask(SIG_SETMASK, &oldmask, NULL);
211 setuid(geteuid());
212 execl("/bin/umount", "/bin/umount", "-i", rel_mnt,
213 lazy ? "-l" : NULL, NULL);
214 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
215 progname, strerror(errno));
216 exit(1);
217 }
218 res = waitpid(res, &status, 0);
219 if (res == -1)
220 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
221
222 if (status != 0) {
223 res = -1;
224 }
225
226 out_restore:
227 sigprocmask(SIG_SETMASK, &oldmask, NULL);
228 return res;
229
230}
231
232int fuse_mnt_umount(const char *progname, const char *abs_mnt,
233 const char *rel_mnt, int lazy)
234{
235 int res;
236
237 if (!mtab_needs_update(abs_mnt)) {
238 res = umount2(rel_mnt, lazy ? 2 : 0);
239 if (res == -1)
240 fprintf(stderr, "%s: failed to unmount %s: %s\n",
241 progname, abs_mnt, strerror(errno));
242 return res;
243 }
244
245 return exec_umount(progname, rel_mnt, lazy);
246}
247
248char *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
315int fuse_mnt_check_empty(const char *progname, const char *mnt,
316 mode_t rootmode, off64_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
348int fuse_mnt_check_fuseblk(void)
349{
350 char buf[256];
351 FILE *f = fopen("/proc/filesystems", "r");
352 if (!f)
353 return 1;
354
355 while (fgets(buf, sizeof(buf), f))
356 if (strstr(buf, "fuseblk\n")) {
357 fclose(f);
358 return 1;
359 }
360
361 fclose(f);
362 return 0;
363}