6ce22d48dc4b9494b0a42c697052d805fd44cbea
[openafs.git] / src / afsd / afsd_fuse.c
1 /*
2  * Copyright (c) 2008-2010 Sine Nomine Associates
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of Sine Nomine Associates nor the names of its
17  *    contributors may be used to endorse or promote products derived from
18  *    this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
21  * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
30  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 /*
34  * afsd_fuse.c - Driver for afsd.fuse, and glue between FUSE and libuafs
35  */
36
37 #include <afsconfig.h>
38 #include <afs/param.h>
39
40 #include <sysincludes.h>
41 #include <afs/afsutil.h>
42 #include <afs_usrops.h>
43 #include <afs/cmd.h>
44 #include <afs/afs_args.h>
45
46 #include "afsd.h"
47
48 #include <errno.h>
49 #include <stddef.h>
50 #include <stdio.h>
51
52 #define FUSE_USE_VERSION 27
53 #include <fuse.h>
54
55 /* command-line arguments to pass to afsd and the cmd_ library */
56 static struct fuse_args afsd_args = FUSE_ARGS_INIT(0, NULL);
57
58 /* command-line arguments to pass to FUSE */
59 static struct fuse_args fuse_args = FUSE_ARGS_INIT(0, NULL);
60
61 /* used for command-line parsing in fuafsd_fuse_opt below */
62 static int fuafsd_cmd_accept = 0;
63 static int nullfd;
64 static int stderr_save;
65
66 /**
67  * Turn FUSE-y paths into libuafs-y paths.
68  *
69  * @param[in] apath  a path in the form of /localcell/foo/bar
70  *
71  * @return a path (non-const) in the form /afs/localcell/foo/bar to be freed
72  *         by the caller
73  */
74 static char *
75 afs_path(const char *apath)
76 {
77     size_t len;
78     static const char prefix[] = "/afs/";
79     char *path;
80
81     len = strlen(apath) + sizeof(prefix);
82
83     path = malloc(len);
84
85     sprintf(path, "%s%s", prefix, apath);
86
87     return path;
88 }
89
90 static void *
91 fuafsd_init(struct fuse_conn_info *conn)
92 {
93     uafs_Run();
94
95     uafs_setMountDir("/afs");
96
97     return NULL;
98 }
99
100 /* Wrappers around libuafs calls for FUSE */
101
102 static int
103 fuafsd_getattr(const char *apath, struct stat *stbuf)
104 {
105         int code;
106         char *path = afs_path(apath);
107
108         code = uafs_lstat(path, stbuf);
109
110         free(path);
111
112         if (code < 0) {
113                 return -errno;
114         }
115         return 0;
116 }
117
118 static int
119 fuafsd_opendir(const char *apath, struct fuse_file_info * fi)
120 {
121         usr_DIR * dirp;
122         char *path = afs_path(apath);
123
124         dirp = uafs_opendir(path);
125
126         free(path);
127
128         if (!dirp) {
129                 return -errno;
130         }
131
132         fi->fh = (uintptr_t)dirp;
133
134         return 0;
135 }
136
137 static int
138 fuafsd_readdir(const char *path, void * buf, fuse_fill_dir_t filler,
139                off_t offset, struct fuse_file_info * fi)
140 {
141         usr_DIR * dirp;
142         struct usr_dirent * direntP;
143
144         dirp = (usr_DIR *)(uintptr_t)fi->fh;
145
146         errno = 0;
147         while ((direntP = uafs_readdir(dirp))) {
148                 if (filler(buf, direntP->d_name, NULL, 0)) {
149                         /* buffer is full */
150                         return 0;
151                 }
152         }
153
154         return -errno;
155 }
156
157 static int
158 fuafsd_releasedir(const char *path, struct fuse_file_info * fi)
159 {
160         return uafs_closedir((usr_DIR *)(uintptr_t)fi->fh);
161 }
162
163 static int
164 fuafsd_create(const char *apath, mode_t mode, struct fuse_file_info * fi)
165 {
166         int fd;
167         char *path = afs_path(apath);
168
169         fd = uafs_open(path, fi->flags, mode);
170
171         free(path);
172
173         if (fd < 0) {
174                 return -errno;
175         }
176
177         fi->fh = fd;
178
179         return 0;
180 }
181
182 static int
183 fuafsd_open(const char *path, struct fuse_file_info * fi)
184 {
185         return fuafsd_create(path, 0, fi);
186 }
187
188 static int
189 fuafsd_read(const char *path, char * buf, size_t len, off_t offset,
190         struct fuse_file_info * fi)
191 {
192         int fd, code;
193
194         fd = fi->fh;
195
196         code = uafs_pread(fd, buf, len, offset);
197         if (code < 0) {
198                 return -errno;
199         }
200
201         return code;
202 }
203
204 static int
205 fuafsd_readlink(const char *apath, char * buf, size_t len)
206 {
207         int code;
208         char *path = afs_path(apath);
209
210         code = uafs_readlink(path, buf, len);
211
212         free(path);
213
214         if (code < 0) {
215                 return -errno;
216         }
217
218         buf[code] = '\0';
219
220         return 0;
221 }
222
223 static int
224 fuafsd_mkdir(const char *apath, mode_t mode)
225 {
226         int code;
227         char *path = afs_path(apath);
228
229         code = uafs_mkdir(path, mode);
230
231         free(path);
232
233         if (code < 0) {
234                 return -errno;
235         }
236         return 0;
237 }
238
239 static int
240 fuafsd_unlink(const char *apath)
241 {
242         int code;
243         char *path = afs_path(apath);
244
245         code = uafs_unlink(path);
246
247         free(path);
248
249         if (code < 0) {
250                 return -errno;
251         }
252         return 0;
253 }
254
255 static int
256 fuafsd_rmdir(const char *apath)
257 {
258         int code;
259         char *path = afs_path(apath);
260
261         code = uafs_rmdir(path);
262
263         free(path);
264
265         if (code < 0) {
266                 return -errno;
267         }
268         return 0;
269 }
270
271 static int
272 fuafsd_symlink(const char *atarget, const char *asource)
273 {
274         int code;
275         char *target = afs_path(atarget);
276         char *source = afs_path(asource);
277
278         code = uafs_symlink(target, source);
279
280         free(target);
281         free(source);
282
283         if (code < 0) {
284                 return -errno;
285         }
286         return 0;
287 }
288
289 static int
290 fuafsd_rename(const char *aold, const char *anew)
291 {
292         int code;
293         char *old = afs_path(aold);
294         char *new = afs_path(anew);
295
296         code = uafs_rename(old, new);
297
298         free(old);
299         free(new);
300
301         if (code < 0) {
302                 return -errno;
303         }
304         return 0;
305 }
306
307 static int
308 fuafsd_link(const char *aexisting, const char *anew)
309 {
310         int code;
311         char *existing = afs_path(aexisting);
312         char *new = afs_path(anew);
313
314         code = uafs_link(existing, new);
315
316         free(existing);
317         free(new);
318
319         if (code < 0) {
320                 return -errno;
321         }
322         return 0;
323 }
324
325 static int
326 fuafsd_chmod(const char *apath, mode_t mode)
327 {
328         int code;
329         char *path = afs_path(apath);
330
331         code = uafs_chmod(path, mode);
332
333         free(path);
334
335         if (code < 0) {
336                 return -errno;
337         }
338         return 0;
339 }
340
341 static int
342 fuafsd_truncate(const char *apath, off_t length)
343 {
344         int code;
345         char *path = afs_path(apath);
346
347         code = uafs_truncate(path, length);
348
349         free(path);
350
351         if (code < 0) {
352                 return -errno;
353         }
354         return 0;
355 }
356
357 static int
358 fuafsd_write(const char *path, const char *abuf, size_t len, off_t offset,
359              struct fuse_file_info * fi)
360 {
361         int fd, code;
362         char *buf = malloc(len);
363
364         fd = fi->fh;
365         memcpy(buf, abuf, len);
366
367         code = uafs_pwrite(fd, buf, len, offset);
368
369         free(buf);
370
371         if (code < 0) {
372                 return -errno;
373         }
374
375         return code;
376 }
377
378 static int
379 fuafsd_statvfs(const char * path, struct statvfs * buf)
380 {
381         if (uafs_statvfs(buf) < 0) {
382                 return -errno;
383         }
384
385         /*
386          * FUSE ignores frsize, and uses bsize for displaying e.g. available
387          * space. Just set bsize to frsize so we get a consistent `df` output.
388          */
389         buf->f_bsize = buf->f_frsize;
390         return 0;
391 }
392
393 static int
394 fuafsd_release(const char *path, struct fuse_file_info * fi)
395 {
396         int fd;
397
398         fd = fi->fh;
399
400         if (uafs_close(fd) < 0) {
401                 return -errno;
402         }
403
404         return 0;
405 }
406
407 static void
408 fuafsd_destroy(void * private_data)
409 {
410         uafs_Shutdown();
411 }
412
413 static struct fuse_operations fuafsd_oper = {
414         .init       = fuafsd_init,
415         .getattr    = fuafsd_getattr,
416         .opendir    = fuafsd_opendir,
417         .readdir    = fuafsd_readdir,
418         .releasedir = fuafsd_releasedir,
419         .open       = fuafsd_open,
420         .create     = fuafsd_create,
421         .read       = fuafsd_read,
422         .readlink   = fuafsd_readlink,
423         .mkdir      = fuafsd_mkdir,
424         .rmdir      = fuafsd_rmdir,
425         .link       = fuafsd_link,
426         .unlink     = fuafsd_unlink,
427         .symlink    = fuafsd_symlink,
428         .rename     = fuafsd_rename,
429         .chmod      = fuafsd_chmod,
430         .truncate   = fuafsd_truncate,
431         .write      = fuafsd_write,
432         .statfs     = fuafsd_statvfs,
433         .release    = fuafsd_release,
434         .destroy    = fuafsd_destroy
435 };
436
437 /* Command line argument processing */
438
439 /*
440  * See fuafsd_fuse_opt below.
441  */
442 static int
443 fuafsd_cmd_check(struct cmd_syndesc * ts, void *beforeRock)
444 {
445         fuafsd_cmd_accept = 1;
446         return 1;
447 }
448
449 /*
450  * Split arguments into FUSE and afsd/libcmd arguments. To determine whether an
451  * argument is meant for FUSE or for the cmd interface, we pass the given
452  * argument to cmd_Dispatch, and see if our beforeProc is run (which we set to
453  * fuafsd_cmd_check). If it was run, the argument is acceptable to cmd; if it
454  * was not run, cmd doesn't know what to do with the argument, so we assume the
455  * argument is meant for FUSE. We can tell if fuafsd_cmd_check was run by the
456  * global fuafsd_cmd_accept bool, which is set to true when fuafsd_cmd_check is
457  * run.
458  */
459 static void
460 split_args(const struct fuse_args *args)
461 {
462         int i;
463
464         cmd_SetBeforeProc(fuafsd_cmd_check, NULL);
465
466         nullfd = open("/dev/null", O_WRONLY);
467         stderr_save = dup(2);
468         if (nullfd < 0 || stderr_save < 0) {
469                 perror("open/dup");
470                 exit(1);
471         }
472
473         for (i = 0; args->argv[i]; ++i) {
474                 int code;
475                 char *arg = args->argv[i];
476                 char *argv[3] = {args->argv[0], arg, NULL};
477
478                 if (fuafsd_cmd_accept) {
479                         fuse_opt_add_arg(&afsd_args, arg);
480                         fuafsd_cmd_accept = 0;
481                         continue;
482                 }
483
484                 /* redirect stderr to null, so libcmd doesn't print out
485                  * an error message for unknown options */
486                 if (dup2(nullfd, 2) < 0) {
487                         perror("dup2");
488                         exit(1);
489                 }
490
491                 code = cmd_Dispatch(2, argv);
492
493                 if (dup2(stderr_save, 2) < 0) {
494                         perror("dup2");
495                         exit(1);
496                 }
497
498                 /* fuafsd_cmd_check should prevent the dispatch from succeeding;
499                  * the only way we should be able to succeed is if -help was
500                  * specified, so just exit */
501                 if (code == 0) {
502                         exit(1);
503                 }
504
505                 if (fuafsd_cmd_accept || code == CMD_TOOFEW) {
506                         /* libcmd accepted the argument; must go to afsd */
507                         fuse_opt_add_arg(&afsd_args, arg);
508
509                         if (code == CMD_TOOFEW) {
510                                 /* flag takes another argument; get the next one, too */
511                                 fuafsd_cmd_accept = 1;
512                         } else {
513                                 fuafsd_cmd_accept = 0;
514                         }
515
516                 } else {
517                         /* libcmd doesn't recognize the argument; give it to FUSE */
518                         fuse_opt_add_arg(&fuse_args, arg);
519                 }
520         }
521
522         if (close(nullfd) < 0) {
523                 perror("close");
524         }
525         if (close(stderr_save) < 0) {
526                 perror("close");
527         }
528
529         cmd_SetBeforeProc(NULL, NULL);
530 }
531
532 /*
533  * First we divide the given arguments into FUSE and cmd arguments, pass the
534  * FUSE arguments to FUSE, and call cmd_Dispatch in the FUSE init function.
535  */
536 int
537 main(int argc, char **argv)
538 {
539         int code;
540         struct fuse_args args = FUSE_ARGS_INIT(argc-1, &argv[1]);
541         fuse_opt_add_arg(&afsd_args, argv[0]);
542
543 #ifdef AFS_SUN511_ENV
544         /* for some reason, Solaris 11 FUSE takes the filesystem name from
545          * argv[0], and ignores the -ofsname option */
546         fuse_opt_add_arg(&fuse_args, "AFS");
547 #else
548         fuse_opt_add_arg(&fuse_args, argv[0]);
549 #endif
550
551         /* let us determine file inode numbers, not FUSE. also make "AFS" appear
552          * in df/mount/mnttab/etc output. */
553         fuse_opt_add_arg(&fuse_args, "-ouse_ino,fsname=AFS");
554
555         if (getuid() == 0) {
556             /* allow other users to access the mountpoint. only do this for
557              * root, since non-root may or may not be able to do this */
558             fuse_opt_add_arg(&fuse_args, "-oallow_other");
559         }
560
561         code = uafs_Setup("/afs");
562         if (code) {
563                 errno = code;
564                 perror("libuafs");
565                 return 1;
566         }
567
568         split_args(&args);
569
570         uafs_ParseArgs(afsd_args.argc, afsd_args.argv);
571
572         /* pass "-- /mount/dir" to fuse to specify dir to mount; "--" is
573          * just to make sure fuse doesn't interpret the mount dir as a flag
574          */
575 #ifndef AFS_SUN511_ENV
576         /* This seems to screw up option parsing on Solaris 11 FUSE, so just
577          * don't do it. This makes it slightly more annoying to mount on a dir
578          * called -foo or something, but oh well. */
579         fuse_opt_add_arg(&fuse_args, "--");
580 #endif
581         fuse_opt_add_arg(&fuse_args, uafs_MountDir());
582
583         return fuse_main(fuse_args.argc, fuse_args.argv, &fuafsd_oper, NULL);
584 }