xref: /third_party/libfuse/lib/mount_util.c (revision 6881f68f)
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}
373