util/ulockmgr_server.c: conditionally define closefrom (fix glibc-2.34+)
[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 #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 }