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 "config.h"
12 #include "mount_util.h"
13 #include <stdio.h>
14 #include <unistd.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <signal.h>
18 #include <dirent.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <limits.h>
22 #include <paths.h>
23 #if !defined( __NetBSD__) && !defined(__FreeBSD__)
24 #include <mntent.h>
25 #else
26 #define IGNORE_MTAB
27 #endif
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <sys/mount.h>
31 #include <sys/param.h>
32 
33 #if defined(__NetBSD__) || defined(__FreeBSD__)
34 #define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
35 #endif
36 
37 #ifdef IGNORE_MTAB
38 #define mtab_needs_update(mnt) 0
39 #else
40 static int mtab_needs_update(const char *mnt)
41 {
42  int res;
43  struct stat stbuf;
44 
45  /* If mtab is within new mount, don't touch it */
46  if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
47  _PATH_MOUNTED[strlen(mnt)] == '/')
48  return 0;
49 
50  /*
51  * Skip mtab update if /etc/mtab:
52  *
53  * - doesn't exist,
54  * - is a symlink,
55  * - is on a read-only filesystem.
56  */
57  res = lstat(_PATH_MOUNTED, &stbuf);
58  if (res == -1) {
59  if (errno == ENOENT)
60  return 0;
61  } else {
62  uid_t ruid;
63  int err;
64 
65  if (S_ISLNK(stbuf.st_mode))
66  return 0;
67 
68  ruid = getuid();
69  if (ruid != 0)
70  setreuid(0, -1);
71 
72  res = access(_PATH_MOUNTED, W_OK);
73  err = (res == -1) ? errno : 0;
74  if (ruid != 0)
75  setreuid(ruid, -1);
76 
77  if (err == EROFS)
78  return 0;
79  }
80 
81  return 1;
82 }
83 #endif /* IGNORE_MTAB */
84 
85 static int add_mount(const char *progname, const char *fsname,
86  const char *mnt, const char *type, const char *opts)
87 {
88  int res;
89  int status;
90  sigset_t blockmask;
91  sigset_t oldmask;
92 
93  sigemptyset(&blockmask);
94  sigaddset(&blockmask, SIGCHLD);
95  res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
96  if (res == -1) {
97  fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
98  return -1;
99  }
100 
101  res = fork();
102  if (res == -1) {
103  fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
104  goto out_restore;
105  }
106  if (res == 0) {
107  char *env = NULL;
108 
109  sigprocmask(SIG_SETMASK, &oldmask, NULL);
110 
111  if(setuid(geteuid()) == -1) {
112  fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
113  res = -1;
114  goto out_restore;
115  }
116 
117  execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
118  "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
119  fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
120  progname, strerror(errno));
121  exit(1);
122  }
123  res = waitpid(res, &status, 0);
124  if (res == -1)
125  fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
126 
127  if (status != 0)
128  res = -1;
129 
130  out_restore:
131  sigprocmask(SIG_SETMASK, &oldmask, NULL);
132 
133  return res;
134 }
135 
136 int fuse_mnt_add_mount(const char *progname, const char *fsname,
137  const char *mnt, const char *type, const char *opts)
138 {
139  if (!mtab_needs_update(mnt))
140  return 0;
141 
142  return add_mount(progname, fsname, mnt, type, opts);
143 }
144 
145 static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
146 {
147  int res;
148  int status;
149  sigset_t blockmask;
150  sigset_t oldmask;
151 
152  sigemptyset(&blockmask);
153  sigaddset(&blockmask, SIGCHLD);
154  res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
155  if (res == -1) {
156  fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
157  return -1;
158  }
159 
160  res = fork();
161  if (res == -1) {
162  fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
163  goto out_restore;
164  }
165  if (res == 0) {
166  char *env = NULL;
167 
168  sigprocmask(SIG_SETMASK, &oldmask, NULL);
169 
170  if(setuid(geteuid()) == -1) {
171  fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
172  res = -1;
173  goto out_restore;
174  }
175 
176  if (lazy) {
177  execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
178  "-l", NULL, &env);
179  } else {
180  execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
181  NULL, &env);
182  }
183  fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
184  progname, strerror(errno));
185  exit(1);
186  }
187  res = waitpid(res, &status, 0);
188  if (res == -1)
189  fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
190 
191  if (status != 0) {
192  res = -1;
193  }
194 
195  out_restore:
196  sigprocmask(SIG_SETMASK, &oldmask, NULL);
197  return res;
198 
199 }
200 
201 int fuse_mnt_umount(const char *progname, const char *abs_mnt,
202  const char *rel_mnt, int lazy)
203 {
204  int res;
205 
206  if (!mtab_needs_update(abs_mnt)) {
207  res = umount2(rel_mnt, lazy ? 2 : 0);
208  if (res == -1)
209  fprintf(stderr, "%s: failed to unmount %s: %s\n",
210  progname, abs_mnt, strerror(errno));
211  return res;
212  }
213 
214  return exec_umount(progname, rel_mnt, lazy);
215 }
216 
217 static int remove_mount(const char *progname, const char *mnt)
218 {
219  int res;
220  int status;
221  sigset_t blockmask;
222  sigset_t oldmask;
223 
224  sigemptyset(&blockmask);
225  sigaddset(&blockmask, SIGCHLD);
226  res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
227  if (res == -1) {
228  fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
229  return -1;
230  }
231 
232  res = fork();
233  if (res == -1) {
234  fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
235  goto out_restore;
236  }
237  if (res == 0) {
238  char *env = NULL;
239 
240  sigprocmask(SIG_SETMASK, &oldmask, NULL);
241 
242  if(setuid(geteuid()) == -1) {
243  fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
244  res = -1;
245  goto out_restore;
246  }
247 
248  execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
249  "--fake", mnt, NULL, &env);
250  fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
251  progname, strerror(errno));
252  exit(1);
253  }
254  res = waitpid(res, &status, 0);
255  if (res == -1)
256  fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
257 
258  if (status != 0)
259  res = -1;
260 
261  out_restore:
262  sigprocmask(SIG_SETMASK, &oldmask, NULL);
263  return res;
264 }
265 
266 int fuse_mnt_remove_mount(const char *progname, const char *mnt)
267 {
268  if (!mtab_needs_update(mnt))
269  return 0;
270 
271  return remove_mount(progname, mnt);
272 }
273 
274 char *fuse_mnt_resolve_path(const char *progname, const char *orig)
275 {
276  char buf[PATH_MAX];
277  char *copy;
278  char *dst;
279  char *end;
280  char *lastcomp;
281  const char *toresolv;
282 
283  if (!orig[0]) {
284  fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
285  orig);
286  return NULL;
287  }
288 
289  copy = strdup(orig);
290  if (copy == NULL) {
291  fprintf(stderr, "%s: failed to allocate memory\n", progname);
292  return NULL;
293  }
294 
295  toresolv = copy;
296  lastcomp = NULL;
297  for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
298  if (end[0] != '/') {
299  char *tmp;
300  end[1] = '\0';
301  tmp = strrchr(copy, '/');
302  if (tmp == NULL) {
303  lastcomp = copy;
304  toresolv = ".";
305  } else {
306  lastcomp = tmp + 1;
307  if (tmp == copy)
308  toresolv = "/";
309  }
310  if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
311  lastcomp = NULL;
312  toresolv = copy;
313  }
314  else if (tmp)
315  tmp[0] = '\0';
316  }
317  if (realpath(toresolv, buf) == NULL) {
318  fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
319  strerror(errno));
320  free(copy);
321  return NULL;
322  }
323  if (lastcomp == NULL)
324  dst = strdup(buf);
325  else {
326  dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
327  if (dst) {
328  unsigned buflen = strlen(buf);
329  if (buflen && buf[buflen-1] == '/')
330  sprintf(dst, "%s%s", buf, lastcomp);
331  else
332  sprintf(dst, "%s/%s", buf, lastcomp);
333  }
334  }
335  free(copy);
336  if (dst == NULL)
337  fprintf(stderr, "%s: failed to allocate memory\n", progname);
338  return dst;
339 }
340 
341 int fuse_mnt_check_fuseblk(void)
342 {
343  char buf[256];
344  FILE *f = fopen("/proc/filesystems", "r");
345  if (!f)
346  return 1;
347 
348  while (fgets(buf, sizeof(buf), f))
349  if (strstr(buf, "fuseblk\n")) {
350  fclose(f);
351  return 1;
352  }
353 
354  fclose(f);
355  return 0;
356 }
357 
358 int fuse_mnt_parse_fuse_fd(const char *mountpoint)
359 {
360  int fd = -1;
361  int len = 0;
362 
363  if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 &&
364  len == strlen(mountpoint)) {
365  return fd;
366  }
367 
368  return -1;
369 }