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