afsd: Tidy up system calls
[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     static const char prefix[] = "/afs/";
78     char *path;
79
80     asprintf(&path, "%s%s", prefix, apath);
81
82     return path;
83 }
84
85 static void *
86 fuafsd_init(struct fuse_conn_info *conn)
87 {
88     uafs_Run();
89
90     uafs_setMountDir("/afs");
91
92     return NULL;
93 }
94
95 /* Wrappers around libuafs calls for FUSE */
96
97 static int
98 fuafsd_getattr(const char *apath, struct stat *stbuf)
99 {
100         int code;
101         char *path = afs_path(apath);
102
103         code = uafs_lstat(path, stbuf);
104
105         free(path);
106
107         if (code < 0) {
108                 return -errno;
109         }
110         return 0;
111 }
112
113 static int
114 fuafsd_opendir(const char *apath, struct fuse_file_info * fi)
115 {
116         usr_DIR * dirp;
117         char *path = afs_path(apath);
118
119         dirp = uafs_opendir(path);
120
121         free(path);
122
123         if (!dirp) {
124                 return -errno;
125         }
126
127         fi->fh = (uintptr_t)dirp;
128
129         return 0;
130 }
131
132 static int
133 fuafsd_readdir(const char *path, void * buf, fuse_fill_dir_t filler,
134                off_t offset, struct fuse_file_info * fi)
135 {
136         usr_DIR * dirp;
137         struct usr_dirent * direntP;
138
139         dirp = (usr_DIR *)(uintptr_t)fi->fh;
140
141         errno = 0;
142         while ((direntP = uafs_readdir(dirp))) {
143                 if (filler(buf, direntP->d_name, NULL, 0)) {
144                         /* buffer is full */
145                         return 0;
146                 }
147         }
148
149         return -errno;
150 }
151
152 static int
153 fuafsd_releasedir(const char *path, struct fuse_file_info * fi)
154 {
155         return uafs_closedir((usr_DIR *)(uintptr_t)fi->fh);
156 }
157
158 static int
159 fuafsd_create(const char *apath, mode_t mode, struct fuse_file_info * fi)
160 {
161         int fd;
162         char *path = afs_path(apath);
163
164         fd = uafs_open(path, fi->flags, mode);
165
166         free(path);
167
168         if (fd < 0) {
169                 return -errno;
170         }
171
172         fi->fh = fd;
173
174         return 0;
175 }
176
177 static int
178 fuafsd_open(const char *path, struct fuse_file_info * fi)
179 {
180         return fuafsd_create(path, 0, fi);
181 }
182
183 static int
184 fuafsd_read(const char *path, char * buf, size_t len, off_t offset,
185         struct fuse_file_info * fi)
186 {
187         int fd, code;
188
189         fd = fi->fh;
190
191         code = uafs_pread(fd, buf, len, offset);
192         if (code < 0) {
193                 return -errno;
194         }
195
196         return code;
197 }
198
199 static int
200 fuafsd_readlink(const char *apath, char * buf, size_t len)
201 {
202         int code;
203         char *path = afs_path(apath);
204
205         code = uafs_readlink(path, buf, len);
206
207         free(path);
208
209         if (code < 0) {
210                 return -errno;
211         }
212
213         buf[code] = '\0';
214
215         return 0;
216 }
217
218 static int
219 fuafsd_mkdir(const char *apath, mode_t mode)
220 {
221         int code;
222         char *path = afs_path(apath);
223
224         code = uafs_mkdir(path, mode);
225
226         free(path);
227
228         if (code < 0) {
229                 return -errno;
230         }
231         return 0;
232 }
233
234 static int
235 fuafsd_unlink(const char *apath)
236 {
237         int code;
238         char *path = afs_path(apath);
239
240         code = uafs_unlink(path);
241
242         free(path);
243
244         if (code < 0) {
245                 return -errno;
246         }
247         return 0;
248 }
249
250 static int
251 fuafsd_rmdir(const char *apath)
252 {
253         int code;
254         char *path = afs_path(apath);
255
256         code = uafs_rmdir(path);
257
258         free(path);
259
260         if (code < 0) {
261                 return -errno;
262         }
263         return 0;
264 }
265
266 static int
267 fuafsd_symlink(const char *atarget, const char *asource)
268 {
269         int code;
270         char *target = afs_path(atarget);
271         char *source = afs_path(asource);
272
273         code = uafs_symlink(target, source);
274
275         free(target);
276         free(source);
277
278         if (code < 0) {
279                 return -errno;
280         }
281         return 0;
282 }
283
284 static int
285 fuafsd_rename(const char *aold, const char *anew)
286 {
287         int code;
288         char *old = afs_path(aold);
289         char *new = afs_path(anew);
290
291         code = uafs_rename(old, new);
292
293         free(old);
294         free(new);
295
296         if (code < 0) {
297                 return -errno;
298         }
299         return 0;
300 }
301
302 static int
303 fuafsd_link(const char *aexisting, const char *anew)
304 {
305         int code;
306         char *existing = afs_path(aexisting);
307         char *new = afs_path(anew);
308
309         code = uafs_link(existing, new);
310
311         free(existing);
312         free(new);
313
314         if (code < 0) {
315                 return -errno;
316         }
317         return 0;
318 }
319
320 static int
321 fuafsd_chmod(const char *apath, mode_t mode)
322 {
323         int code;
324         char *path = afs_path(apath);
325
326         code = uafs_chmod(path, mode);
327
328         free(path);
329
330         if (code < 0) {
331                 return -errno;
332         }
333         return 0;
334 }
335
336 static int
337 fuafsd_truncate(const char *apath, off_t length)
338 {
339         int code;
340         char *path = afs_path(apath);
341
342         code = uafs_truncate(path, length);
343
344         free(path);
345
346         if (code < 0) {
347                 return -errno;
348         }
349         return 0;
350 }
351
352 static int
353 fuafsd_write(const char *path, const char *abuf, size_t len, off_t offset,
354              struct fuse_file_info * fi)
355 {
356         int fd, code;
357         char *buf = malloc(len);
358
359         fd = fi->fh;
360         memcpy(buf, abuf, len);
361
362         code = uafs_pwrite(fd, buf, len, offset);
363
364         free(buf);
365
366         if (code < 0) {
367                 return -errno;
368         }
369
370         return code;
371 }
372
373 static int
374 fuafsd_statvfs(const char * path, struct statvfs * buf)
375 {
376         if (uafs_statvfs(buf) < 0) {
377                 return -errno;
378         }
379
380         /*
381          * FUSE ignores frsize, and uses bsize for displaying e.g. available
382          * space. Just set bsize to frsize so we get a consistent `df` output.
383          */
384         buf->f_bsize = buf->f_frsize;
385         return 0;
386 }
387
388 static int
389 fuafsd_release(const char *path, struct fuse_file_info * fi)
390 {
391         int fd;
392
393         fd = fi->fh;
394
395         if (uafs_close(fd) < 0) {
396                 return -errno;
397         }
398
399         return 0;
400 }
401
402 static void
403 fuafsd_destroy(void * private_data)
404 {
405         uafs_Shutdown();
406 }
407
408 static struct fuse_operations fuafsd_oper = {
409         .init       = fuafsd_init,
410         .getattr    = fuafsd_getattr,
411         .opendir    = fuafsd_opendir,
412         .readdir    = fuafsd_readdir,
413         .releasedir = fuafsd_releasedir,
414         .open       = fuafsd_open,
415         .create     = fuafsd_create,
416         .read       = fuafsd_read,
417         .readlink   = fuafsd_readlink,
418         .mkdir      = fuafsd_mkdir,
419         .rmdir      = fuafsd_rmdir,
420         .link       = fuafsd_link,
421         .unlink     = fuafsd_unlink,
422         .symlink    = fuafsd_symlink,
423         .rename     = fuafsd_rename,
424         .chmod      = fuafsd_chmod,
425         .truncate   = fuafsd_truncate,
426         .write      = fuafsd_write,
427         .statfs     = fuafsd_statvfs,
428         .release    = fuafsd_release,
429         .destroy    = fuafsd_destroy
430 };
431
432 /* Command line argument processing */
433
434 /*
435  * See fuafsd_fuse_opt below.
436  */
437 static int
438 fuafsd_cmd_check(struct cmd_syndesc * ts, void *beforeRock)
439 {
440         fuafsd_cmd_accept = 1;
441         return 1;
442 }
443
444 /*
445  * Split arguments into FUSE and afsd/libcmd arguments. To determine whether an
446  * argument is meant for FUSE or for the cmd interface, we pass the given
447  * argument to cmd_Dispatch, and see if our beforeProc is run (which we set to
448  * fuafsd_cmd_check). If it was run, the argument is acceptable to cmd; if it
449  * was not run, cmd doesn't know what to do with the argument, so we assume the
450  * argument is meant for FUSE. We can tell if fuafsd_cmd_check was run by the
451  * global fuafsd_cmd_accept bool, which is set to true when fuafsd_cmd_check is
452  * run.
453  */
454 static void
455 split_args(const struct fuse_args *args)
456 {
457         int i;
458
459         cmd_SetBeforeProc(fuafsd_cmd_check, NULL);
460
461         nullfd = open("/dev/null", O_WRONLY);
462         stderr_save = dup(2);
463         if (nullfd < 0 || stderr_save < 0) {
464                 perror("open/dup");
465                 exit(1);
466         }
467
468         for (i = 0; args->argv[i]; ++i) {
469                 int code;
470                 char *arg = args->argv[i];
471                 char *argv[3] = {args->argv[0], arg, NULL};
472
473                 if (fuafsd_cmd_accept) {
474                         fuse_opt_add_arg(&afsd_args, arg);
475                         fuafsd_cmd_accept = 0;
476                         continue;
477                 }
478
479                 /* redirect stderr to null, so libcmd doesn't print out
480                  * an error message for unknown options */
481                 if (dup2(nullfd, 2) < 0) {
482                         perror("dup2");
483                         exit(1);
484                 }
485
486                 code = cmd_Dispatch(2, argv);
487
488                 if (dup2(stderr_save, 2) < 0) {
489                         perror("dup2");
490                         exit(1);
491                 }
492
493                 /* fuafsd_cmd_check should prevent the dispatch from succeeding;
494                  * the only way we should be able to succeed is if -help was
495                  * specified, so just exit */
496                 if (code == 0) {
497                         exit(1);
498                 }
499
500                 if (fuafsd_cmd_accept || code == CMD_TOOFEW) {
501                         /* libcmd accepted the argument; must go to afsd */
502                         fuse_opt_add_arg(&afsd_args, arg);
503
504                         if (code == CMD_TOOFEW) {
505                                 /* flag takes another argument; get the next one, too */
506                                 fuafsd_cmd_accept = 1;
507                         } else {
508                                 fuafsd_cmd_accept = 0;
509                         }
510
511                 } else {
512                         /* libcmd doesn't recognize the argument; give it to FUSE */
513                         fuse_opt_add_arg(&fuse_args, arg);
514                 }
515         }
516
517         if (close(nullfd) < 0) {
518                 perror("close");
519         }
520         if (close(stderr_save) < 0) {
521                 perror("close");
522         }
523
524         cmd_SetBeforeProc(NULL, NULL);
525 }
526
527 /*
528  * First we divide the given arguments into FUSE and cmd arguments, pass the
529  * FUSE arguments to FUSE, and call cmd_Dispatch in the FUSE init function.
530  */
531 int
532 main(int argc, char **argv)
533 {
534         int code;
535         struct fuse_args args = FUSE_ARGS_INIT(argc-1, &argv[1]);
536         fuse_opt_add_arg(&afsd_args, argv[0]);
537
538 #ifdef AFS_SUN511_ENV
539         /* for some reason, Solaris 11 FUSE takes the filesystem name from
540          * argv[0], and ignores the -ofsname option */
541         fuse_opt_add_arg(&fuse_args, "AFS");
542 #else
543         fuse_opt_add_arg(&fuse_args, argv[0]);
544 #endif
545
546         /* let us determine file inode numbers, not FUSE. also make "AFS" appear
547          * in df/mount/mnttab/etc output. */
548         fuse_opt_add_arg(&fuse_args, "-ouse_ino,fsname=AFS");
549
550         if (getuid() == 0) {
551             /* allow other users to access the mountpoint. only do this for
552              * root, since non-root may or may not be able to do this */
553             fuse_opt_add_arg(&fuse_args, "-oallow_other");
554         }
555
556         code = uafs_Setup("/afs");
557         if (code) {
558                 errno = code;
559                 perror("libuafs");
560                 return 1;
561         }
562
563         split_args(&args);
564
565         uafs_ParseArgs(afsd_args.argc, afsd_args.argv);
566
567         /* pass "-- /mount/dir" to fuse to specify dir to mount; "--" is
568          * just to make sure fuse doesn't interpret the mount dir as a flag
569          */
570 #ifndef AFS_SUN511_ENV
571         /* This seems to screw up option parsing on Solaris 11 FUSE, so just
572          * don't do it. This makes it slightly more annoying to mount on a dir
573          * called -foo or something, but oh well. */
574         fuse_opt_add_arg(&fuse_args, "--");
575 #endif
576         fuse_opt_add_arg(&fuse_args, uafs_MountDir());
577
578         return fuse_main(fuse_args.argc, fuse_args.argv, &fuafsd_oper, NULL);
579 }