summaryrefslogtreecommitdiff
path: root/libfuse-lite/mount_util.c (plain)
blob: 1a7ac3c7d1284ee242d7456632d9bedefc416d65
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 "config.h"
10#include "mount_util.h"
11#include <stdio.h>
12#include <unistd.h>
13#include <stdlib.h>
14#include <string.h>
15#include <dirent.h>
16#include <errno.h>
17#include <limits.h>
18#include <sys/stat.h>
19#include <sys/wait.h>
20#ifdef __SOLARIS__
21#else /* __SOLARIS__ */
22#include <mntent.h>
23#include <sys/mount.h>
24#include <sys/param.h>
25#endif /* __SOLARIS__ */
26
27#ifdef __SOLARIS__
28
29char *mkdtemp(char *template);
30
31#ifndef _PATH_MOUNTED
32#define _PATH_MOUNTED "/etc/mnttab"
33#endif /* _PATH_MOUNTED */
34
35#ifndef IGNORE_MTAB
36static int mtab_needs_update(const char *mnt)
37{
38 struct stat stbuf;
39
40 /* If mtab is within new mount, don't touch it */
41 if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
42 _PATH_MOUNTED[strlen(mnt)] == '/')
43 return 0;
44
45 if (lstat(_PATH_MOUNTED, &stbuf) != -1 && S_ISLNK(stbuf.st_mode))
46 return 0;
47
48 return 1;
49}
50#endif /* IGNORE_MTAB */
51
52int fuse_mnt_add_mount(const char *progname, const char *fsname,
53 const char *mnt, const char *type, const char *opts)
54{
55 int res;
56 int status;
57
58#ifndef IGNORE_MTAB
59 if (!mtab_needs_update(mnt))
60 return 0;
61#endif /* IGNORE_MTAB */
62
63 res = fork();
64 if (res == -1) {
65 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
66 return -1;
67 }
68 if (res == 0) {
69 char templ[] = "/tmp/fusermountXXXXXX";
70 char *tmp;
71
72 setuid(geteuid());
73
74 /*
75 * hide in a directory, where mount isn't able to resolve
76 * fsname as a valid path
77 */
78 tmp = mkdtemp(templ);
79 if (!tmp) {
80 fprintf(stderr, "%s: failed to create temporary directory\n",
81 progname);
82 exit(1);
83 }
84 if (chdir(tmp)) {
85 fprintf(stderr, "%s: failed to chdir to %s: %s\n",
86 progname, tmp, strerror(errno));
87 exit(1);
88 }
89 rmdir(tmp);
90 execl("/sbin/mount", "/sbin/mount", "-F", type, "-o", opts,
91 fsname, mnt, NULL);
92 fprintf(stderr, "%s: failed to execute /sbin/mount: %s\n", progname,
93 strerror(errno));
94 exit(1);
95 }
96 res = waitpid(res, &status, 0);
97 if (res == -1) {
98 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
99 return -1;
100 }
101 if (status != 0)
102 return -1;
103
104 return 0;
105}
106
107int fuse_mnt_umount(const char *progname, const char *mnt, int lazy)
108{
109 int res;
110 int status;
111
112#ifndef IGNORE_MTAB
113 if (!mtab_needs_update(mnt))
114 return 0;
115#endif /* IGNORE_MTAB */
116
117 res = fork();
118 if (res == -1) {
119 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
120 return -1;
121 }
122 if (res == 0) {
123 setuid(geteuid());
124 execl("/sbin/umount", "/sbin/umount", !lazy ? "-f" : NULL, mnt,
125 NULL);
126 fprintf(stderr, "%s: failed to execute /sbin/umount: %s\n", progname,
127 strerror(errno));
128 exit(1);
129 }
130 res = waitpid(res, &status, 0);
131 if (res == -1) {
132 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
133 return -1;
134 }
135 if (status != 0)
136 return -1;
137
138 return 0;
139}
140
141char *fuse_mnt_resolve_path(const char *progname, const char *orig)
142{
143 char buf[PATH_MAX];
144 char *copy;
145 char *dst;
146 char *end;
147 char *lastcomp;
148 const char *toresolv;
149
150 if (!orig[0]) {
151 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname, orig);
152 return NULL;
153 }
154
155 copy = strdup(orig);
156 if (copy == NULL) {
157 fprintf(stderr, "%s: failed to allocate memory\n", progname);
158 return NULL;
159 }
160
161 toresolv = copy;
162 lastcomp = NULL;
163 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
164 if (end[0] != '/') {
165 char *tmp;
166 end[1] = '\0';
167 tmp = strrchr(copy, '/');
168 if (tmp == NULL) {
169 lastcomp = copy;
170 toresolv = ".";
171 } else {
172 lastcomp = tmp + 1;
173 if (tmp == copy)
174 toresolv = "/";
175 }
176 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
177 lastcomp = NULL;
178 toresolv = copy;
179 }
180 else if (tmp)
181 tmp[0] = '\0';
182 }
183 if (realpath(toresolv, buf) == NULL) {
184 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
185 strerror(errno));
186 free(copy);
187 return NULL;
188 }
189 if (lastcomp == NULL)
190 dst = strdup(buf);
191 else {
192 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
193 if (dst) {
194 unsigned buflen = strlen(buf);
195 if (buflen && buf[buflen-1] == '/')
196 sprintf(dst, "%s%s", buf, lastcomp);
197 else
198 sprintf(dst, "%s/%s", buf, lastcomp);
199 }
200 }
201 free(copy);
202 if (dst == NULL)
203 fprintf(stderr, "%s: failed to allocate memory\n", progname);
204 return dst;
205}
206
207int fuse_mnt_check_empty(const char *progname, const char *mnt,
208 mode_t rootmode, off_t rootsize)
209{
210 int isempty = 1;
211
212 if (S_ISDIR(rootmode)) {
213 struct dirent *ent;
214 DIR *dp = opendir(mnt);
215 if (dp == NULL) {
216 fprintf(stderr, "%s: failed to open mountpoint for reading: %s\n",
217 progname, strerror(errno));
218 return -1;
219 }
220 while ((ent = readdir(dp)) != NULL) {
221 if (strcmp(ent->d_name, ".") != 0 &&
222 strcmp(ent->d_name, "..") != 0) {
223 isempty = 0;
224 break;
225 }
226 }
227 closedir(dp);
228 } else if (rootsize)
229 isempty = 0;
230
231 if (!isempty) {
232 fprintf(stderr, "%s: mountpoint is not empty\n", progname);
233 fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
234 return -1;
235 }
236 return 0;
237}
238
239int fuse_mnt_check_fuseblk(void)
240{
241 char buf[256];
242 FILE *f = fopen("/proc/filesystems", "r");
243 if (!f)
244 return 1;
245
246 while (fgets(buf, sizeof(buf), f))
247 if (strstr(buf, "fuseblk\n")) {
248 fclose(f);
249 return 1;
250 }
251
252 fclose(f);
253 return 0;
254}
255
256#else /* __SOLARIS__ */
257
258static int mtab_needs_update(const char *mnt)
259{
260 int res;
261 struct stat stbuf;
262
263 /* If mtab is within new mount, don't touch it */
264 if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
265 _PATH_MOUNTED[strlen(mnt)] == '/')
266 return 0;
267
268 /*
269 * Skip mtab update if /etc/mtab:
270 *
271 * - doesn't exist,
272 * - is a symlink,
273 * - is on a read-only filesystem.
274 */
275 res = lstat(_PATH_MOUNTED, &stbuf);
276 if (res == -1) {
277 if (errno == ENOENT)
278 return 0;
279 } else {
280 if (S_ISLNK(stbuf.st_mode))
281 return 0;
282
283 res = access(_PATH_MOUNTED, W_OK);
284 if (res == -1 && errno == EROFS)
285 return 0;
286 }
287
288 return 1;
289}
290
291int fuse_mnt_add_mount(const char *progname, const char *fsname,
292 const char *mnt, const char *type, const char *opts)
293{
294 int res;
295
296 if (!mtab_needs_update(mnt))
297 return 0;
298
299 res = fork();
300 if (res == -1) {
301 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
302 return 0;
303 }
304 if (res == 0) {
305 char templ[] = "/tmp/fusermountXXXXXX";
306 char *tmp;
307
308 if (setuid(geteuid()))
309 fprintf(stderr, "%s: failed to setuid : %s\n", progname,
310 strerror(errno));
311
312 /*
313 * hide in a directory, where mount isn't able to resolve
314 * fsname as a valid path
315 */
316 tmp = mkdtemp(templ);
317 if (!tmp) {
318 fprintf(stderr, "%s: failed to create temporary directory\n",
319 progname);
320 exit(1);
321 }
322 if (chdir(tmp)) {
323 fprintf(stderr, "%s: failed to chdir to %s: %s\n",
324 progname, tmp, strerror(errno));
325 exit(1);
326 }
327 rmdir(tmp);
328 execl("/bin/mount", "/bin/mount", "-i", "-f", "-t", type, "-o", opts,
329 fsname, mnt, NULL);
330 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n", progname,
331 strerror(errno));
332 exit(1);
333 }
334 return 0;
335}
336
337int fuse_mnt_umount(const char *progname, const char *mnt, int lazy)
338{
339 int res;
340 int status;
341
342 if (!mtab_needs_update(mnt)) {
343 res = umount2(mnt, lazy ? 2 : 0);
344 if (res == -1)
345 fprintf(stderr, "%s: failed to unmount %s: %s\n", progname,
346 mnt, strerror(errno));
347 return res;
348 }
349
350 res = fork();
351 if (res == -1) {
352 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
353 return -1;
354 }
355 if (res == 0) {
356 if (setuid(geteuid()))
357 fprintf(stderr, "%s: failed to setuid : %s\n", progname,
358 strerror(errno));
359 execl("/bin/umount", "/bin/umount", "-i", mnt, lazy ? "-l" : NULL,
360 NULL);
361 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n", progname,
362 strerror(errno));
363 exit(1);
364 }
365 res = waitpid(res, &status, 0);
366 if (res == -1) {
367 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
368 return -1;
369 }
370 if (status != 0)
371 return -1;
372
373 return 0;
374}
375
376char *fuse_mnt_resolve_path(const char *progname, const char *orig)
377{
378 char buf[PATH_MAX];
379 char *copy;
380 char *dst;
381 char *end;
382 char *lastcomp;
383 const char *toresolv;
384
385 if (!orig[0]) {
386 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname, orig);
387 return NULL;
388 }
389
390 copy = strdup(orig);
391 if (copy == NULL) {
392 fprintf(stderr, "%s: failed to allocate memory\n", progname);
393 return NULL;
394 }
395
396 toresolv = copy;
397 lastcomp = NULL;
398 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
399 if (end[0] != '/') {
400 char *tmp;
401 end[1] = '\0';
402 tmp = strrchr(copy, '/');
403 if (tmp == NULL) {
404 lastcomp = copy;
405 toresolv = ".";
406 } else {
407 lastcomp = tmp + 1;
408 if (tmp == copy)
409 toresolv = "/";
410 }
411 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
412 lastcomp = NULL;
413 toresolv = copy;
414 }
415 else if (tmp)
416 tmp[0] = '\0';
417 }
418 if (realpath(toresolv, buf) == NULL) {
419 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
420 strerror(errno));
421 free(copy);
422 return NULL;
423 }
424 if (lastcomp == NULL)
425 dst = strdup(buf);
426 else {
427 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
428 if (dst) {
429 unsigned buflen = strlen(buf);
430 if (buflen && buf[buflen-1] == '/')
431 sprintf(dst, "%s%s", buf, lastcomp);
432 else
433 sprintf(dst, "%s/%s", buf, lastcomp);
434 }
435 }
436 free(copy);
437 if (dst == NULL)
438 fprintf(stderr, "%s: failed to allocate memory\n", progname);
439 return dst;
440}
441
442int fuse_mnt_check_fuseblk(void)
443{
444 char buf[256];
445 FILE *f = fopen("/proc/filesystems", "r");
446 if (!f)
447 return 1;
448
449 while (fgets(buf, sizeof(buf), f))
450 if (strstr(buf, "fuseblk\n")) {
451 fclose(f);
452 return 1;
453 }
454
455 fclose(f);
456 return 0;
457}
458
459#endif /* __SOLARIS__ */
460