libfuse
mount_util.c
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
43 static 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 
88 static 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 
139 int 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 
148 static 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 
204 int 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 
220 static 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 
269 int 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 
277 char *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 
344 int 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 
361 int 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 }