libfuse
mount_util.c
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
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 
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 
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 
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 
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 
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 
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 
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 
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 
348 int fuse_mnt_check_fuseblk(void)
349 {
350  char buf[256];
351  FILE *f = fopen("/proc/filesystems", "r");
352  if (!f)
353  return 1;
354 
355  while (fgets(buf, sizeof(buf), f))
356  if (strstr(buf, "fuseblk\n")) {
357  fclose(f);
358  return 1;
359  }
360 
361  fclose(f);
362  return 0;
363 }