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 "config.h"
10#include "mount_util.h"
11#include <stdio.h>
12#include <unistd.h>
13#include <stdlib.h>
14#include <string.h>
15#include <dirent.h>
16#include <errno.h>
17#include <limits.h>
18#include <mntent.h>
19#include <sys/stat.h>
20#include <sys/wait.h>
21#include <sys/mount.h>
22#include <sys/param.h>
23
24static int mtab_needs_update(const char *mnt)
25{
26	int res;
27	struct stat stbuf;
28
29	/* If mtab is within new mount, don't touch it */
30	if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
31	    _PATH_MOUNTED[strlen(mnt)] == '/')
32		return 0;
33
34	/*
35	 * Skip mtab update if /etc/mtab:
36	 *
37	 *  - doesn't exist,
38	 *  - is a symlink,
39	 *  - is on a read-only filesystem.
40	 */
41	res = lstat(_PATH_MOUNTED, &stbuf);
42	if (res == -1) {
43		if (errno == ENOENT)
44			return 0;
45	} else {
46		if (S_ISLNK(stbuf.st_mode))
47			return 0;
48
49		res = access(_PATH_MOUNTED, W_OK);
50		if (res == -1 && errno == EROFS)
51			return 0;
52	}
53
54	return 1;
55}
56
57int fuse_mnt_add_mount(const char *progname, const char *fsname,
58                       const char *mnt, const char *type, const char *opts)
59{
60    int res;
61    int status;
62
63    if (!mtab_needs_update(mnt))
64        return 0;
65
66    res = fork();
67    if (res == -1) {
68        fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
69        return -1;
70    }
71    if (res == 0) {
72        char templ[] = "/tmp/fusermountXXXXXX";
73        char *tmp;
74
75        setuid(geteuid());
76
77        /*
78         * hide in a directory, where mount isn't able to resolve
79         * fsname as a valid path
80         */
81        tmp = mkdtemp(templ);
82        if (!tmp) {
83            fprintf(stderr, "%s: failed to create temporary directory\n",
84                    progname);
85            exit(1);
86        }
87        if (chdir(tmp)) {
88            fprintf(stderr, "%s: failed to chdir to %s: %s\n",
89                    progname, tmp, strerror(errno));
90            exit(1);
91        }
92        rmdir(tmp);
93        execl("/bin/mount", "/bin/mount", "-i", "-f", "-t", type, "-o", opts,
94              fsname, mnt, NULL);
95        fprintf(stderr, "%s: failed to execute /bin/mount: %s\n", progname,
96                strerror(errno));
97        exit(1);
98    }
99    res = waitpid(res, &status, 0);
100    if (res == -1) {
101        fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
102        return -1;
103    }
104    if (status != 0)
105        return -1;
106
107    return 0;
108}
109
110int fuse_mnt_umount(const char *progname, const char *mnt, int lazy)
111{
112    int res;
113    int status;
114
115    if (!mtab_needs_update(mnt)) {
116        res = umount2(mnt, lazy ? 2 : 0);
117        if (res == -1)
118            fprintf(stderr, "%s: failed to unmount %s: %s\n", progname,
119                    mnt, strerror(errno));
120        return res;
121    }
122
123    res = fork();
124    if (res == -1) {
125        fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
126        return -1;
127    }
128    if (res == 0) {
129        setuid(geteuid());
130        execl("/bin/umount", "/bin/umount", "-i", mnt, lazy ? "-l" : NULL,
131              NULL);
132        fprintf(stderr, "%s: failed to execute /bin/umount: %s\n", progname,
133                strerror(errno));
134        exit(1);
135    }
136    res = waitpid(res, &status, 0);
137    if (res == -1) {
138        fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
139        return -1;
140    }
141    if (status != 0)
142        return -1;
143
144    return 0;
145}
146
147char *fuse_mnt_resolve_path(const char *progname, const char *orig)
148{
149    char buf[PATH_MAX];
150    char *copy;
151    char *dst;
152    char *end;
153    char *lastcomp;
154    const char *toresolv;
155
156    if (!orig[0]) {
157        fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname, orig);
158        return NULL;
159    }
160
161    copy = strdup(orig);
162    if (copy == NULL) {
163        fprintf(stderr, "%s: failed to allocate memory\n", progname);
164        return NULL;
165    }
166
167    toresolv = copy;
168    lastcomp = NULL;
169    for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
170    if (end[0] != '/') {
171        char *tmp;
172        end[1] = '\0';
173        tmp = strrchr(copy, '/');
174        if (tmp == NULL) {
175            lastcomp = copy;
176            toresolv = ".";
177        } else {
178            lastcomp = tmp + 1;
179            if (tmp == copy)
180                toresolv = "/";
181        }
182        if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
183            lastcomp = NULL;
184            toresolv = copy;
185        }
186        else if (tmp)
187            tmp[0] = '\0';
188    }
189    if (realpath(toresolv, buf) == NULL) {
190        fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
191                strerror(errno));
192        free(copy);
193        return NULL;
194    }
195    if (lastcomp == NULL)
196        dst = strdup(buf);
197    else {
198        dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
199        if (dst) {
200            unsigned buflen = strlen(buf);
201            if (buflen && buf[buflen-1] == '/')
202                sprintf(dst, "%s%s", buf, lastcomp);
203            else
204                sprintf(dst, "%s/%s", buf, lastcomp);
205        }
206    }
207    free(copy);
208    if (dst == NULL)
209        fprintf(stderr, "%s: failed to allocate memory\n", progname);
210    return dst;
211}
212
213int fuse_mnt_check_fuseblk(void)
214{
215    char buf[256];
216    FILE *f = fopen("/proc/filesystems", "r");
217    if (!f)
218        return 1;
219
220    while (fgets(buf, sizeof(buf), f))
221        if (strstr(buf, "fuseblk\n")) {
222            fclose(f);
223            return 1;
224        }
225
226    fclose(f);
227    return 0;
228}
229