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