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