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