1 |
dpavlin |
4 |
/* |
2 |
|
|
FUSE: Filesystem in Userspace |
3 |
|
|
Copyright (C) 2001-2004 Miklos Szeredi <miklos@szeredi.hu> |
4 |
|
|
|
5 |
|
|
This program can be distributed under the terms of the GNU GPL. |
6 |
|
|
See the file COPYING. |
7 |
|
|
*/ |
8 |
|
|
/* This program does the mounting and unmounting of FUSE filesystems */ |
9 |
|
|
|
10 |
|
|
/* |
11 |
|
|
* NOTE: This program should be part of (or be called from) /bin/mount |
12 |
|
|
* |
13 |
|
|
* Unless that is done, operations on /etc/mtab are not under lock, and so |
14 |
|
|
* data in this file may be lost. (I will _not_ reimplement that locking, |
15 |
|
|
* and anyway that should be done in libc, if possible. But probably it |
16 |
|
|
* isn't). |
17 |
|
|
*/ |
18 |
|
|
|
19 |
|
|
#include <config.h> |
20 |
|
|
|
21 |
|
|
#include <stdio.h> |
22 |
|
|
#include <stdlib.h> |
23 |
|
|
#include <string.h> |
24 |
|
|
#include <unistd.h> |
25 |
|
|
#include <errno.h> |
26 |
|
|
#include <fcntl.h> |
27 |
|
|
#include <pwd.h> |
28 |
|
|
#include <mntent.h> |
29 |
|
|
#include <sys/param.h> |
30 |
|
|
#include <sys/wait.h> |
31 |
|
|
#include <sys/stat.h> |
32 |
|
|
#include <sys/mount.h> |
33 |
|
|
#include <sys/fsuid.h> |
34 |
|
|
#include <sys/socket.h> |
35 |
|
|
#include <sys/un.h> |
36 |
|
|
#include <linux/fuse.h> |
37 |
|
|
|
38 |
|
|
#define FUSE_DEV "/proc/fs/fuse/dev" |
39 |
|
|
|
40 |
|
|
#define FUSE_COMMFD_ENV "_FUSE_COMMFD" |
41 |
|
|
|
42 |
|
|
const char *progname; |
43 |
|
|
|
44 |
|
|
static const char *get_user_name() |
45 |
|
|
{ |
46 |
|
|
struct passwd *pw = getpwuid(getuid()); |
47 |
|
|
if (pw != NULL && pw->pw_name != NULL) |
48 |
|
|
return pw->pw_name; |
49 |
|
|
else { |
50 |
|
|
fprintf(stderr, "%s: could not determine username\n", progname); |
51 |
|
|
return NULL; |
52 |
|
|
} |
53 |
|
|
} |
54 |
|
|
|
55 |
|
|
/* use a lock file so that multiple fusermount processes don't try and |
56 |
|
|
modify the mtab file at once! */ |
57 |
|
|
static int lock_mtab() |
58 |
|
|
{ |
59 |
|
|
const char *mtab_lock = _PATH_MOUNTED ".fuselock"; |
60 |
|
|
int mtablock; |
61 |
|
|
int res; |
62 |
|
|
|
63 |
|
|
mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600); |
64 |
|
|
if (mtablock >= 0) { |
65 |
|
|
res = lockf(mtablock, F_LOCK, 0); |
66 |
|
|
if (res < 0) |
67 |
|
|
perror("error getting lock"); |
68 |
|
|
} else |
69 |
|
|
fprintf(stderr, "unable to open fuse lock file, continuing anyway\n"); |
70 |
|
|
|
71 |
|
|
return mtablock; |
72 |
|
|
} |
73 |
|
|
|
74 |
|
|
static void unlock_mtab(int mtablock) |
75 |
|
|
{ |
76 |
|
|
if (mtablock >= 0) { |
77 |
|
|
lockf(mtablock, F_ULOCK, 0); |
78 |
|
|
close(mtablock); |
79 |
|
|
} |
80 |
|
|
} |
81 |
|
|
|
82 |
|
|
static int add_mount(const char *fsname, const char *mnt, const char *type) |
83 |
|
|
{ |
84 |
|
|
int res; |
85 |
|
|
const char *mtab = _PATH_MOUNTED; |
86 |
|
|
struct mntent ent; |
87 |
|
|
FILE *fp; |
88 |
|
|
char *opts; |
89 |
|
|
|
90 |
|
|
fp = setmntent(mtab, "a"); |
91 |
|
|
if (fp == NULL) { |
92 |
|
|
fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab, |
93 |
|
|
strerror(errno)); |
94 |
|
|
return -1; |
95 |
|
|
} |
96 |
|
|
|
97 |
|
|
if (getuid() != 0) { |
98 |
|
|
const char *user = get_user_name(); |
99 |
|
|
if (user == NULL) |
100 |
|
|
return -1; |
101 |
|
|
|
102 |
|
|
opts = malloc(strlen(user) + 128); |
103 |
|
|
if (opts != NULL) |
104 |
|
|
sprintf(opts, "rw,nosuid,nodev,user=%s", user); |
105 |
|
|
} |
106 |
|
|
else |
107 |
|
|
opts = strdup("rw,nosuid,nodev"); |
108 |
|
|
|
109 |
|
|
if (opts == NULL) { |
110 |
|
|
fprintf(stderr, "%s: failed to allocate memory\n", progname); |
111 |
|
|
return -1; |
112 |
|
|
} |
113 |
|
|
|
114 |
|
|
ent.mnt_fsname = (char *) fsname; |
115 |
|
|
ent.mnt_dir = (char *) mnt; |
116 |
|
|
ent.mnt_type = (char *) type; |
117 |
|
|
ent.mnt_opts = opts; |
118 |
|
|
ent.mnt_freq = 0; |
119 |
|
|
ent.mnt_passno = 0; |
120 |
|
|
res = addmntent(fp, &ent); |
121 |
|
|
if (res != 0) { |
122 |
|
|
fprintf(stderr, "%s: failed to add entry to %s: %s\n", progname, |
123 |
|
|
mtab, strerror(errno)); |
124 |
|
|
return -1; |
125 |
|
|
} |
126 |
|
|
|
127 |
|
|
endmntent(fp); |
128 |
|
|
return 0; |
129 |
|
|
} |
130 |
|
|
|
131 |
|
|
static int remove_mount(const char *mnt, int quiet, int lazy) |
132 |
|
|
{ |
133 |
|
|
int res; |
134 |
|
|
const char *mtab = _PATH_MOUNTED; |
135 |
|
|
const char *mtab_new = _PATH_MOUNTED "~fuse~"; |
136 |
|
|
struct mntent *entp; |
137 |
|
|
FILE *fp; |
138 |
|
|
FILE *newfp; |
139 |
|
|
const char *user = NULL; |
140 |
|
|
int found; |
141 |
|
|
|
142 |
|
|
fp = setmntent(mtab, "r"); |
143 |
|
|
if (fp == NULL) { |
144 |
|
|
fprintf(stderr, "%s failed to open %s: %s\n", progname, mtab, |
145 |
|
|
strerror(errno)); |
146 |
|
|
return -1; |
147 |
|
|
} |
148 |
|
|
|
149 |
|
|
newfp = setmntent(mtab_new, "w"); |
150 |
|
|
if (newfp == NULL) { |
151 |
|
|
fprintf(stderr, "%s failed to open %s: %s\n", progname, mtab_new, |
152 |
|
|
strerror(errno)); |
153 |
|
|
return -1; |
154 |
|
|
} |
155 |
|
|
|
156 |
|
|
if (getuid() != 0) { |
157 |
|
|
user = get_user_name(); |
158 |
|
|
if (user == NULL) |
159 |
|
|
return -1; |
160 |
|
|
} |
161 |
|
|
|
162 |
|
|
found = 0; |
163 |
|
|
while ((entp = getmntent(fp)) != NULL) { |
164 |
|
|
int remove = 0; |
165 |
|
|
if (!found && strcmp(entp->mnt_dir, mnt) == 0 && |
166 |
|
|
strcmp(entp->mnt_type, "fuse") == 0) { |
167 |
|
|
if (user == NULL) |
168 |
|
|
remove = 1; |
169 |
|
|
else { |
170 |
|
|
char *p = strstr(entp->mnt_opts, "user="); |
171 |
|
|
if (p != NULL && strcmp(p + 5, user) == 0) |
172 |
|
|
remove = 1; |
173 |
|
|
} |
174 |
|
|
} |
175 |
|
|
if (remove) |
176 |
|
|
found = 1; |
177 |
|
|
else { |
178 |
|
|
res = addmntent(newfp, entp); |
179 |
|
|
if (res != 0) { |
180 |
|
|
fprintf(stderr, "%s: failed to add entry to %s: %s", progname, |
181 |
|
|
mtab_new, strerror(errno)); |
182 |
|
|
|
183 |
|
|
} |
184 |
|
|
} |
185 |
|
|
} |
186 |
|
|
|
187 |
|
|
endmntent(fp); |
188 |
|
|
endmntent(newfp); |
189 |
|
|
|
190 |
|
|
if (found) { |
191 |
|
|
res = umount2(mnt, lazy ? 2 : 0); |
192 |
|
|
if (res == -1) { |
193 |
|
|
if (!quiet) |
194 |
|
|
fprintf(stderr, "%s: failed to unmount %s: %s\n", |
195 |
|
|
progname, mnt,strerror(errno)); |
196 |
|
|
found = -1; |
197 |
|
|
} |
198 |
|
|
} |
199 |
|
|
|
200 |
|
|
if (found == 1) { |
201 |
|
|
res = rename(mtab_new, mtab); |
202 |
|
|
if (res == -1) { |
203 |
|
|
fprintf(stderr, "%s: failed to rename %s to %s: %s\n", progname, |
204 |
|
|
mtab_new, mtab, strerror(errno)); |
205 |
|
|
return -1; |
206 |
|
|
} |
207 |
|
|
} |
208 |
|
|
else { |
209 |
|
|
if (!found && !quiet) |
210 |
|
|
fprintf(stderr, "%s: entry for %s not found in %s\n", progname, |
211 |
|
|
mnt, mtab); |
212 |
|
|
unlink(mtab_new); |
213 |
|
|
return -1; |
214 |
|
|
} |
215 |
|
|
|
216 |
|
|
return 0; |
217 |
|
|
} |
218 |
|
|
|
219 |
|
|
/* Until there is a nice interface for capabilities in _libc_, this will |
220 |
|
|
remain here. I don't think it is fair to expect users to compile libcap |
221 |
|
|
for this program. And anyway what's all this fuss about versioning the |
222 |
|
|
kernel interface? It is quite good as is. */ |
223 |
|
|
#define _LINUX_CAPABILITY_VERSION 0x19980330 |
224 |
|
|
|
225 |
|
|
typedef struct __user_cap_header_struct { |
226 |
|
|
unsigned int version; |
227 |
|
|
int pid; |
228 |
|
|
} *cap_user_header_t; |
229 |
|
|
|
230 |
|
|
typedef struct __user_cap_data_struct { |
231 |
|
|
unsigned int effective; |
232 |
|
|
unsigned int permitted; |
233 |
|
|
unsigned int inheritable; |
234 |
|
|
} *cap_user_data_t; |
235 |
|
|
|
236 |
|
|
int capget(cap_user_header_t header, cap_user_data_t data); |
237 |
|
|
int capset(cap_user_header_t header, cap_user_data_t data); |
238 |
|
|
|
239 |
|
|
#define CAP_SYS_ADMIN 21 |
240 |
|
|
|
241 |
|
|
static uid_t oldfsuid; |
242 |
|
|
static gid_t oldfsgid; |
243 |
|
|
static struct __user_cap_data_struct oldcaps; |
244 |
|
|
|
245 |
|
|
static int drop_privs() |
246 |
|
|
{ |
247 |
|
|
int res; |
248 |
|
|
struct __user_cap_header_struct head; |
249 |
|
|
struct __user_cap_data_struct newcaps; |
250 |
|
|
|
251 |
|
|
head.version = _LINUX_CAPABILITY_VERSION; |
252 |
|
|
head.pid = 0; |
253 |
|
|
res = capget(&head, &oldcaps); |
254 |
|
|
if (res == -1) { |
255 |
|
|
fprintf(stderr, "%s: failed to get capabilities: %s\n", progname, |
256 |
|
|
strerror(errno)); |
257 |
|
|
return -1; |
258 |
|
|
} |
259 |
|
|
|
260 |
|
|
oldfsuid = setfsuid(getuid()); |
261 |
|
|
oldfsgid = setfsgid(getgid()); |
262 |
|
|
newcaps = oldcaps; |
263 |
|
|
/* Keep CAP_SYS_ADMIN for mount */ |
264 |
|
|
newcaps.effective &= (1 << CAP_SYS_ADMIN); |
265 |
|
|
|
266 |
|
|
head.version = _LINUX_CAPABILITY_VERSION; |
267 |
|
|
head.pid = 0; |
268 |
|
|
res = capset(&head, &newcaps); |
269 |
|
|
if (res == -1) { |
270 |
|
|
fprintf(stderr, "%s: failed to set capabilities: %s\n", progname, |
271 |
|
|
strerror(errno)); |
272 |
|
|
return -1; |
273 |
|
|
} |
274 |
|
|
return 0; |
275 |
|
|
} |
276 |
|
|
|
277 |
|
|
static void restore_privs() |
278 |
|
|
{ |
279 |
|
|
struct __user_cap_header_struct head; |
280 |
|
|
int res; |
281 |
|
|
|
282 |
|
|
head.version = _LINUX_CAPABILITY_VERSION; |
283 |
|
|
head.pid = 0; |
284 |
|
|
res = capset(&head, &oldcaps); |
285 |
|
|
if (res == -1) |
286 |
|
|
fprintf(stderr, "%s: failed to restore capabilities: %s\n", progname, |
287 |
|
|
strerror(errno)); |
288 |
|
|
|
289 |
|
|
setfsuid(oldfsuid); |
290 |
|
|
setfsgid(oldfsgid); |
291 |
|
|
} |
292 |
|
|
|
293 |
|
|
static int begins_with(const char *s, const char *beg) |
294 |
|
|
{ |
295 |
|
|
if (strncmp(s, beg, strlen(beg)) == 0) |
296 |
|
|
return 1; |
297 |
|
|
else |
298 |
|
|
return 0; |
299 |
|
|
} |
300 |
|
|
|
301 |
|
|
static int do_mount(const char *mnt, const char *type, mode_t rootmode, |
302 |
|
|
int fd, const char *opts, char **fsnamep) |
303 |
|
|
{ |
304 |
|
|
int res; |
305 |
|
|
int flags = MS_NOSUID | MS_NODEV; |
306 |
|
|
char *optbuf; |
307 |
|
|
const char *s; |
308 |
|
|
char *d; |
309 |
|
|
char *fsname = NULL; |
310 |
|
|
|
311 |
|
|
if (getuid() != 0) { |
312 |
|
|
res = drop_privs(); |
313 |
|
|
if (res == -1) |
314 |
|
|
return -1; |
315 |
|
|
} |
316 |
|
|
|
317 |
|
|
optbuf = malloc(strlen(opts) + 64); |
318 |
|
|
if (!optbuf) { |
319 |
|
|
fprintf(stderr, "%s: failed to allocate memory\n", progname); |
320 |
|
|
return -1; |
321 |
|
|
} |
322 |
|
|
|
323 |
|
|
for (s = opts, d = optbuf; *s;) { |
324 |
|
|
unsigned len; |
325 |
|
|
const char *fsname_str = "fsname="; |
326 |
|
|
for (len = 0; s[len] && s[len] != ','; len++); |
327 |
|
|
if (begins_with(s, fsname_str)) { |
328 |
|
|
unsigned fsname_str_len = strlen(fsname_str); |
329 |
|
|
if (fsname) |
330 |
|
|
free(fsname); |
331 |
|
|
fsname = malloc(len - fsname_str_len + 1); |
332 |
|
|
if (!fsname) { |
333 |
|
|
fprintf(stderr, "%s: failed to allocate memory\n", progname); |
334 |
|
|
free(optbuf); |
335 |
|
|
return -1; |
336 |
|
|
} |
337 |
|
|
memcpy(fsname, s + fsname_str_len, len - fsname_str_len); |
338 |
|
|
fsname[len - fsname_str_len] = '\0'; |
339 |
|
|
} else if (!begins_with(s, "fd=") && |
340 |
|
|
!begins_with(s, "rootmode=") && |
341 |
|
|
!begins_with(s, "uid=")) { |
342 |
|
|
memcpy(d, s, len); |
343 |
|
|
d += len; |
344 |
|
|
*d++ = ','; |
345 |
|
|
} |
346 |
|
|
s += len; |
347 |
|
|
if (*s) |
348 |
|
|
s++; |
349 |
|
|
} |
350 |
|
|
sprintf(d, "fd=%i,rootmode=%o,uid=%i", fd, rootmode, getuid()); |
351 |
|
|
if (fsname == NULL) { |
352 |
|
|
fsname = strdup(FUSE_DEV); |
353 |
|
|
if (!fsname) { |
354 |
|
|
fprintf(stderr, "%s: failed to allocate memory\n", progname); |
355 |
|
|
free(optbuf); |
356 |
|
|
return -1; |
357 |
|
|
} |
358 |
|
|
} |
359 |
|
|
|
360 |
|
|
res = mount(fsname, mnt, type, flags, optbuf); |
361 |
|
|
free(optbuf); |
362 |
|
|
if (res == -1) { |
363 |
|
|
fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno)); |
364 |
|
|
free(fsname); |
365 |
|
|
} |
366 |
|
|
*fsnamep = fsname; |
367 |
|
|
|
368 |
|
|
if (getuid() != 0) |
369 |
|
|
restore_privs(); |
370 |
|
|
|
371 |
|
|
return res; |
372 |
|
|
} |
373 |
|
|
|
374 |
|
|
static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd) |
375 |
|
|
{ |
376 |
|
|
int res; |
377 |
|
|
const char *mnt = *mntp; |
378 |
|
|
|
379 |
|
|
res = lstat(mnt, stbuf); |
380 |
|
|
if (res == -1) { |
381 |
|
|
fprintf(stderr, "%s: failed to access mountpoint %s: %s\n", |
382 |
|
|
progname, mnt, strerror(errno)); |
383 |
|
|
return -1; |
384 |
|
|
} |
385 |
|
|
|
386 |
|
|
/* No permission checking is done for root */ |
387 |
|
|
if (getuid() == 0) |
388 |
|
|
return 0; |
389 |
|
|
|
390 |
|
|
if (!S_ISDIR(stbuf->st_mode)) { |
391 |
|
|
fprintf(stderr, "%s: mountpoint %s is not a directory\n", |
392 |
|
|
progname, mnt); |
393 |
|
|
return -1; |
394 |
|
|
} |
395 |
|
|
|
396 |
|
|
*currdir_fd = open(".", O_RDONLY); |
397 |
|
|
if (*currdir_fd == -1) { |
398 |
|
|
fprintf(stderr, "%s: failed to open current directory: %s\n", |
399 |
|
|
progname, strerror(errno)); |
400 |
|
|
return -1; |
401 |
|
|
} |
402 |
|
|
res = chdir(mnt); |
403 |
|
|
if (res == -1) { |
404 |
|
|
fprintf(stderr, "%s: failed to chdir to mountpoint: %s\n", |
405 |
|
|
progname, strerror(errno)); |
406 |
|
|
return -1; |
407 |
|
|
} |
408 |
|
|
mnt = *mntp = "."; |
409 |
|
|
res = lstat(mnt, stbuf); |
410 |
|
|
if (res == -1) { |
411 |
|
|
fprintf(stderr, "%s: failed to access mountpoint %s: %s\n", |
412 |
|
|
progname, mnt, strerror(errno)); |
413 |
|
|
return -1; |
414 |
|
|
} |
415 |
|
|
|
416 |
|
|
if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) { |
417 |
|
|
fprintf(stderr, "%s: mountpoint %s not owned by user\n", |
418 |
|
|
progname, mnt); |
419 |
|
|
return -1; |
420 |
|
|
} |
421 |
|
|
|
422 |
|
|
res = access(mnt, W_OK); |
423 |
|
|
if (res == -1) { |
424 |
|
|
fprintf(stderr, "%s: user has no write access to mountpoint %s\n", |
425 |
|
|
progname, mnt); |
426 |
|
|
return -1; |
427 |
|
|
} |
428 |
|
|
|
429 |
|
|
return 0; |
430 |
|
|
} |
431 |
|
|
|
432 |
|
|
static int mount_fuse(const char *mnt, const char *opts) |
433 |
|
|
{ |
434 |
|
|
int res; |
435 |
|
|
int fd; |
436 |
|
|
const char *dev = FUSE_DEV; |
437 |
|
|
const char *type = "fuse"; |
438 |
|
|
struct stat stbuf; |
439 |
|
|
int mtablock; |
440 |
|
|
char *fsname; |
441 |
|
|
const char *real_mnt = mnt; |
442 |
|
|
int currdir_fd = -1; |
443 |
|
|
|
444 |
|
|
fd = open(dev, O_RDWR); |
445 |
|
|
if (fd == -1 |
446 |
|
|
#ifndef AUTO_MODPROBE |
447 |
|
|
&& getuid() == 0 |
448 |
|
|
#endif |
449 |
|
|
) { |
450 |
|
|
int status; |
451 |
|
|
pid_t pid = fork(); |
452 |
|
|
if (pid == 0) { |
453 |
|
|
setuid(0); |
454 |
|
|
execl("/sbin/modprobe", "/sbin/modprobe", "fuse", NULL); |
455 |
|
|
exit(1); |
456 |
|
|
} |
457 |
|
|
if (pid != -1) |
458 |
|
|
waitpid(pid, &status, 0); |
459 |
|
|
|
460 |
|
|
fd = open(dev, O_RDWR); |
461 |
|
|
} |
462 |
|
|
if (fd == -1) { |
463 |
|
|
fprintf(stderr, "%s: unable to open fuse device %s: %s\n", progname, |
464 |
|
|
dev, strerror(errno)); |
465 |
|
|
return -1; |
466 |
|
|
} |
467 |
|
|
|
468 |
|
|
res = check_perm(&real_mnt, &stbuf, &currdir_fd); |
469 |
|
|
if (res == -1) |
470 |
|
|
return -1; |
471 |
|
|
|
472 |
|
|
res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT, fd, opts, &fsname); |
473 |
|
|
if (res == -1) |
474 |
|
|
return -1; |
475 |
|
|
|
476 |
|
|
if (currdir_fd != -1) { |
477 |
|
|
fchdir(currdir_fd); |
478 |
|
|
close(currdir_fd); |
479 |
|
|
} |
480 |
|
|
|
481 |
|
|
if (geteuid() == 0) { |
482 |
|
|
mtablock = lock_mtab(); |
483 |
|
|
res = add_mount(fsname, mnt, type); |
484 |
|
|
free(fsname); |
485 |
|
|
unlock_mtab(mtablock); |
486 |
|
|
if (res == -1) { |
487 |
|
|
umount2(mnt, 2); /* lazy umount */ |
488 |
|
|
return -1; |
489 |
|
|
} |
490 |
|
|
} else |
491 |
|
|
free(fsname); |
492 |
|
|
|
493 |
|
|
return fd; |
494 |
|
|
} |
495 |
|
|
|
496 |
|
|
static char *resolve_path(const char *orig, int unmount) |
497 |
|
|
{ |
498 |
|
|
char buf[PATH_MAX]; |
499 |
|
|
char *dst; |
500 |
|
|
|
501 |
|
|
if (unmount) { |
502 |
|
|
char *end; |
503 |
|
|
/* Resolving at unmount can only be done very carefully, not touching |
504 |
|
|
the mountpoint... So for the moment it's not done. |
505 |
|
|
|
506 |
|
|
Just remove trailing slashes instead. |
507 |
|
|
*/ |
508 |
|
|
dst = strdup(orig); |
509 |
|
|
if (dst == NULL) { |
510 |
|
|
fprintf(stderr, "%s: failed to allocate memory\n", progname); |
511 |
|
|
return NULL; |
512 |
|
|
} |
513 |
|
|
|
514 |
|
|
for (end = dst + strlen(dst) - 1; end > dst && *end == '/'; end --) |
515 |
|
|
*end = '\0'; |
516 |
|
|
|
517 |
|
|
return dst; |
518 |
|
|
} |
519 |
|
|
|
520 |
|
|
if (realpath(orig, buf) == NULL) { |
521 |
|
|
fprintf(stderr, "%s: Bad mount point %s: %s\n", progname, orig, |
522 |
|
|
strerror(errno)); |
523 |
|
|
return NULL; |
524 |
|
|
} |
525 |
|
|
|
526 |
|
|
dst = strdup(buf); |
527 |
|
|
if (dst == NULL) |
528 |
|
|
fprintf(stderr, "%s: failed to allocate memory\n", progname); |
529 |
|
|
return dst; |
530 |
|
|
} |
531 |
|
|
|
532 |
|
|
static int send_fd(int sock_fd, int fd) |
533 |
|
|
{ |
534 |
|
|
int retval; |
535 |
|
|
struct msghdr msg; |
536 |
|
|
struct cmsghdr *p_cmsg; |
537 |
|
|
struct iovec vec; |
538 |
|
|
char cmsgbuf[CMSG_SPACE(sizeof(fd))]; |
539 |
|
|
int *p_fds; |
540 |
|
|
char sendchar = 0; |
541 |
|
|
|
542 |
|
|
msg.msg_control = cmsgbuf; |
543 |
|
|
msg.msg_controllen = sizeof(cmsgbuf); |
544 |
|
|
p_cmsg = CMSG_FIRSTHDR(&msg); |
545 |
|
|
p_cmsg->cmsg_level = SOL_SOCKET; |
546 |
|
|
p_cmsg->cmsg_type = SCM_RIGHTS; |
547 |
|
|
p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); |
548 |
|
|
p_fds = (int *) CMSG_DATA(p_cmsg); |
549 |
|
|
*p_fds = fd; |
550 |
|
|
msg.msg_controllen = p_cmsg->cmsg_len; |
551 |
|
|
msg.msg_name = NULL; |
552 |
|
|
msg.msg_namelen = 0; |
553 |
|
|
msg.msg_iov = &vec; |
554 |
|
|
msg.msg_iovlen = 1; |
555 |
|
|
msg.msg_flags = 0; |
556 |
|
|
/* "To pass file descriptors or credentials you need to send/read at |
557 |
|
|
* least one byte" (man 7 unix) */ |
558 |
|
|
vec.iov_base = &sendchar; |
559 |
|
|
vec.iov_len = sizeof(sendchar); |
560 |
|
|
while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR); |
561 |
|
|
if (retval != 1) { |
562 |
|
|
perror("sending file descriptor"); |
563 |
|
|
return -1; |
564 |
|
|
} |
565 |
|
|
return 0; |
566 |
|
|
} |
567 |
|
|
|
568 |
|
|
static void usage() |
569 |
|
|
{ |
570 |
|
|
fprintf(stderr, |
571 |
|
|
"%s: [options] mountpoint\n" |
572 |
|
|
"Options:\n" |
573 |
|
|
" -h print help\n" |
574 |
|
|
" -o opt[,opt...] mount options\n" |
575 |
|
|
" -u unmount\n" |
576 |
|
|
" -q quiet\n" |
577 |
|
|
" -z lazy unmount\n", |
578 |
|
|
progname); |
579 |
|
|
exit(1); |
580 |
|
|
} |
581 |
|
|
|
582 |
|
|
int main(int argc, char *argv[]) |
583 |
|
|
{ |
584 |
|
|
int a; |
585 |
|
|
int fd; |
586 |
|
|
int res; |
587 |
|
|
char *origmnt; |
588 |
|
|
char *mnt; |
589 |
|
|
int unmount = 0; |
590 |
|
|
int lazy = 0; |
591 |
|
|
char *commfd; |
592 |
|
|
int quiet = 0; |
593 |
|
|
int cfd; |
594 |
|
|
const char *opts = ""; |
595 |
|
|
|
596 |
|
|
progname = argv[0]; |
597 |
|
|
|
598 |
|
|
for (a = 1; a < argc; a++) { |
599 |
|
|
if (argv[a][0] != '-') |
600 |
|
|
break; |
601 |
|
|
|
602 |
|
|
switch (argv[a][1]) { |
603 |
|
|
case 'h': |
604 |
|
|
usage(); |
605 |
|
|
break; |
606 |
|
|
|
607 |
|
|
case 'o': |
608 |
|
|
a++; |
609 |
|
|
if (a == argc) { |
610 |
|
|
fprintf(stderr, "%s: Missing argument to -o\n", progname); |
611 |
|
|
exit(1); |
612 |
|
|
} |
613 |
|
|
opts = argv[a]; |
614 |
|
|
break; |
615 |
|
|
|
616 |
|
|
case 'u': |
617 |
|
|
unmount = 1; |
618 |
|
|
break; |
619 |
|
|
|
620 |
|
|
case 'z': |
621 |
|
|
lazy = 1; |
622 |
|
|
break; |
623 |
|
|
|
624 |
|
|
case 'q': |
625 |
|
|
quiet = 1; |
626 |
|
|
break; |
627 |
|
|
|
628 |
|
|
default: |
629 |
|
|
fprintf(stderr, "%s: Unknown option %s\n", progname, argv[a]); |
630 |
|
|
fprintf(stderr, "Try `%s -h' for more information\n", progname); |
631 |
|
|
exit(1); |
632 |
|
|
} |
633 |
|
|
} |
634 |
|
|
|
635 |
|
|
if (a == argc) { |
636 |
|
|
fprintf(stderr, "%s: Missing mountpoint argument\n", progname); |
637 |
|
|
exit(1); |
638 |
|
|
} |
639 |
|
|
|
640 |
|
|
origmnt = argv[a++]; |
641 |
|
|
|
642 |
|
|
if (getuid() != 0) |
643 |
|
|
drop_privs(); |
644 |
|
|
|
645 |
|
|
mnt = resolve_path(origmnt, unmount); |
646 |
|
|
if (mnt == NULL) |
647 |
|
|
exit(1); |
648 |
|
|
|
649 |
|
|
if (getuid() != 0) |
650 |
|
|
restore_privs(); |
651 |
|
|
|
652 |
|
|
if (unmount) { |
653 |
|
|
if (geteuid() == 0) { |
654 |
|
|
int mtablock = lock_mtab(); |
655 |
|
|
res = remove_mount(mnt, quiet, lazy); |
656 |
|
|
unlock_mtab(mtablock); |
657 |
|
|
} else { |
658 |
|
|
res = umount2(mnt, lazy ? 2 : 0); |
659 |
|
|
if (res == -1) { |
660 |
|
|
if (!quiet) |
661 |
|
|
fprintf(stderr, "%s: failed to unmount %s: %s\n", |
662 |
|
|
progname, mnt, strerror(errno)); |
663 |
|
|
} |
664 |
|
|
} |
665 |
|
|
if (res == -1) |
666 |
|
|
exit(1); |
667 |
|
|
return 0; |
668 |
|
|
} |
669 |
|
|
|
670 |
|
|
commfd = getenv(FUSE_COMMFD_ENV); |
671 |
|
|
if (commfd == NULL) { |
672 |
|
|
fprintf(stderr, "%s: old style mounting not supported\n", progname); |
673 |
|
|
exit(1); |
674 |
|
|
} |
675 |
|
|
|
676 |
|
|
fd = mount_fuse(mnt, opts); |
677 |
|
|
if (fd == -1) |
678 |
|
|
exit(1); |
679 |
|
|
|
680 |
|
|
cfd = atoi(commfd); |
681 |
|
|
res = send_fd(cfd, fd); |
682 |
|
|
if (res == -1) |
683 |
|
|
exit(1); |
684 |
|
|
|
685 |
|
|
return 0; |
686 |
|
|
} |