make use of the crypto_tester in the crypto_factory
[strongswan.git] / src / dumm / cowfs.c
1 /*
2 * Copyright (C) 2007 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 * Copyright (C) 2001-2007 Miklos Szeredi
5 *
6 * Based on example shipped with FUSE.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 */
18
19
20 #define FUSE_USE_VERSION 26
21 #define _GNU_SOURCE
22
23 #include <fuse.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <dirent.h>
30 #include <errno.h>
31 #include <sys/time.h>
32 #include <pthread.h>
33
34 #include "cowfs.h"
35
36 #include <library.h>
37 #include <debug.h>
38
39 /** define _XOPEN_SOURCE 500 fails when using libstrongswan, define popen */
40 extern ssize_t pread(int fd, void *buf, size_t count, off_t offset);
41 extern ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
42
43 typedef struct private_cowfs_t private_cowfs_t;
44
45 struct private_cowfs_t {
46 /** public cowfs interface */
47 cowfs_t public;
48 /** fuse channel to mountpoint */
49 struct fuse_chan *chan;
50 /** fuse handle */
51 struct fuse *fuse;
52 /** mountpoint of cowfs FUSE */
53 char *mount;
54 /** master filesystem path */
55 char *master;
56 /** host filesystem path */
57 char *host;
58 /** overlay filesystem path */
59 char *over;
60 /** fd of read only master filesystem */
61 int master_fd;
62 /** copy on write overlay to master */
63 int host_fd;
64 /** optional COW overlay */
65 int over_fd;
66 /** thread processing FUSE */
67 pthread_t thread;
68 };
69
70 /**
71 * get this pointer stored in fuse context
72 */
73 static private_cowfs_t *get_this()
74 {
75 return (fuse_get_context())->private_data;
76 }
77
78 /**
79 * make a path relative
80 */
81 static void rel(const char **path)
82 {
83 if (**path == '/')
84 {
85 (*path)++;
86 }
87 if (**path == '\0')
88 {
89 *path = ".";
90 }
91 }
92
93 /**
94 * get the highest overlay in which path exists
95 */
96 static int get_rd(const char *path)
97 {
98 private_cowfs_t *this = get_this();
99
100 if (this->over_fd > 0 && faccessat(this->over_fd, path, F_OK, 0) == 0)
101 {
102 return this->over_fd;
103 }
104 if (faccessat(this->host_fd, path, F_OK, 0) == 0)
105 {
106 return this->host_fd;
107 }
108 return this->master_fd;
109 }
110
111 /**
112 * get the highest overlay available, to write something
113 */
114 static int get_wr(const char *path)
115 {
116 private_cowfs_t *this = get_this();
117 if (this->over_fd > 0)
118 {
119 return this->over_fd;
120 }
121 return this->host_fd;
122 }
123
124 /**
125 * create full "path" at "wr" the same way they exist at "rd"
126 */
127 static bool clone_path(int rd, int wr, const char *path)
128 {
129 char *pos, *full;
130 struct stat st;
131 full = strdupa(path);
132 pos = full;
133
134 while ((pos = strchr(pos, '/')))
135 {
136 *pos = '\0';
137 if (fstatat(wr, full, &st, 0) < 0)
138 {
139 /* TODO: handle symlinks!? */
140 if (fstatat(rd, full, &st, 0) < 0)
141 {
142 return FALSE;
143 }
144 if (mkdirat(wr, full, st.st_mode) < 0)
145 {
146 return FALSE;
147 }
148 }
149 *pos = '/';
150 pos++;
151 }
152 return TRUE;
153 }
154
155 /**
156 * copy a (special) file from a readonly to a read-write overlay
157 */
158 static int copy(const char *path)
159 {
160 char *buf[4096];
161 int len;
162 int rd, wr;
163 int from, to;
164 struct stat st;
165
166 rd = get_rd(path);
167 wr = get_wr(path);
168
169 if (rd == wr)
170 {
171 /* already writeable */
172 return wr;
173 }
174 if (fstatat(rd, path, &st, 0) < 0)
175 {
176 return -1;
177 }
178 if (!clone_path(rd, wr, path))
179 {
180 return -1;
181 }
182 if (mknodat(wr, path, st.st_mode, st.st_rdev) < 0)
183 {
184 return -1;
185 }
186 /* copy if no special file */
187 if (st.st_size)
188 {
189 from = openat(rd, path, O_RDONLY, st.st_mode);
190 if (from < 0)
191 {
192 return -1;
193 }
194 to = openat(wr, path, O_WRONLY , st.st_mode);
195 if (to < 0)
196 {
197 close(from);
198 return -1;
199 }
200 while ((len = read(from, buf, sizeof(buf))) > 0)
201 {
202 if (write(to, buf, len) < len)
203 {
204 /* TODO: only on len < 0 ? */
205 close(from);
206 close(to);
207 return -1;
208 }
209 }
210 close(from);
211 close(to);
212 if (len < 0)
213 {
214 return -1;
215 }
216 }
217 return wr;
218 }
219
220 /**
221 * FUSE getattr method
222 */
223 static int cowfs_getattr(const char *path, struct stat *stbuf)
224 {
225 rel(&path);
226
227 if (fstatat(get_rd(path), path, stbuf, AT_SYMLINK_NOFOLLOW) < 0)
228 {
229 return -errno;
230 }
231 return 0;
232 }
233
234 /**
235 * FUSE access method
236 */
237 static int cowfs_access(const char *path, int mask)
238 {
239 rel(&path);
240
241 if (faccessat(get_rd(path), path, mask, 0) < 0)
242 {
243 return -errno;
244 }
245 return 0;
246 }
247
248 /**
249 * FUSE readlink method
250 */
251 static int cowfs_readlink(const char *path, char *buf, size_t size)
252 {
253 int res;
254
255 rel(&path);
256
257 res = readlinkat(get_rd(path), path, buf, size - 1);
258 if (res < 0)
259 {
260 return -errno;
261 }
262 buf[res] = '\0';
263 return 0;
264 }
265
266 /**
267 * get a directory stream of two concatenated paths
268 */
269 static DIR* get_dir(char *dir, const char *subdir)
270 {
271 char *full;
272
273 if (dir == NULL)
274 {
275 return NULL;
276 }
277
278 full = alloca(strlen(dir) + strlen(subdir) + 1);
279 strcpy(full, dir);
280 strcat(full, subdir);
281
282 return opendir(full);
283 }
284
285 /**
286 * check if a directory stream contains a directory
287 */
288 static bool contains_dir(DIR *d, char *dirname)
289 {
290 if (d)
291 {
292 struct dirent *ent;
293
294 rewinddir(d);
295 while ((ent = readdir(d)))
296 {
297 if (streq(ent->d_name, dirname))
298 {
299 return TRUE;
300 }
301 }
302 }
303 return FALSE;
304 }
305
306 /**
307 * FUSE readdir method
308 */
309 static int cowfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
310 off_t offset, struct fuse_file_info *fi)
311 {
312 private_cowfs_t *this = get_this();
313 DIR *d1, *d2, *d3;
314 struct stat st;
315 struct dirent *ent;
316
317 memset(&st, 0, sizeof(st));
318
319 d1 = get_dir(this->master, path);
320 d2 = get_dir(this->host, path);
321 d3 = get_dir(this->over, path);
322
323 if (d1)
324 {
325 while ((ent = readdir(d1)))
326 {
327 if (!contains_dir(d2, ent->d_name) &&
328 !contains_dir(d3, ent->d_name))
329 {
330 st.st_ino = ent->d_ino;
331 st.st_mode = ent->d_type << 12;
332 filler(buf, ent->d_name, &st, 0);
333 }
334 }
335 closedir(d1);
336 }
337 if (d2)
338 {
339 rewinddir(d2);
340 while ((ent = readdir(d2)))
341 {
342 if (!contains_dir(d3, ent->d_name))
343 {
344 st.st_ino = ent->d_ino;
345 st.st_mode = ent->d_type << 12;
346 filler(buf, ent->d_name, &st, 0);
347 }
348 }
349 closedir(d2);
350 }
351 if (d3)
352 {
353 rewinddir(d3);
354 while ((ent = readdir(d3)))
355 {
356 st.st_ino = ent->d_ino;
357 st.st_mode = ent->d_type << 12;
358 filler(buf, ent->d_name, &st, 0);
359 }
360 closedir(d3);
361 }
362 return 0;
363 }
364
365 /**
366 * FUSE mknod method
367 */
368 static int cowfs_mknod(const char *path, mode_t mode, dev_t rdev)
369 {
370 int fd;
371 rel(&path);
372
373 fd = get_wr(path);
374 if (!clone_path(get_rd(path), fd, path))
375 {
376 return -errno;
377 }
378
379 if (mknodat(fd, path, mode, rdev) < 0)
380 {
381 return -errno;
382 }
383 return 0;
384 }
385
386 /**
387 * FUSE mkdir method
388 */
389 static int cowfs_mkdir(const char *path, mode_t mode)
390 {
391 int fd;
392 rel(&path);
393
394 fd = get_wr(path);
395 if (!clone_path(get_rd(path), fd, path))
396 {
397 return -errno;
398 }
399 if (mkdirat(fd, path, mode) < 0)
400 {
401 return -errno;
402 }
403 return 0;
404 }
405
406 /**
407 * FUSE unlink method
408 */
409 static int cowfs_unlink(const char *path)
410 {
411 rel(&path);
412
413 /* TODO: whiteout master */
414 if (unlinkat(get_wr(path), path, 0) < 0)
415 {
416 return -errno;
417 }
418 return 0;
419 }
420
421 /**
422 * FUSE rmdir method
423 */
424 static int cowfs_rmdir(const char *path)
425 {
426 rel(&path);
427
428 /* TODO: whiteout master */
429 if (unlinkat(get_wr(path), path, AT_REMOVEDIR) < 0)
430 {
431 return -errno;
432 }
433 return 0;
434 }
435
436 /**
437 * FUSE symlink method
438 */
439 static int cowfs_symlink(const char *from, const char *to)
440 {
441 int fd;
442 const char *fromrel = from;
443
444 rel(&to);
445 rel(&fromrel);
446
447 fd = get_wr(to);
448 if (!clone_path(get_rd(fromrel), fd, fromrel))
449 {
450 return -errno;
451 }
452 if (symlinkat(from, fd, to) < 0)
453 {
454 return -errno;
455 }
456 return 0;
457 }
458
459 /**
460 * FUSE rename method
461 */
462 static int cowfs_rename(const char *from, const char *to)
463 {
464 int fd;
465
466 rel(&from);
467 rel(&to);
468
469 fd = copy(from);
470 if (fd < 0)
471 {
472 return -errno;
473 }
474 if (renameat(fd, from, get_wr(to), to) < 0)
475 {
476 return -errno;
477 }
478 return 0;
479 }
480
481 /**
482 * FUSE link method
483 */
484 static int cowfs_link(const char *from, const char *to)
485 {
486 int rd, wr;
487
488 rel(&from);
489 rel(&to);
490
491 rd = get_rd(from);
492 wr = get_wr(to);
493
494 if (!clone_path(rd, wr, to))
495 {
496 DBG1("cloning path '%s' failed", to);
497 return -errno;
498 }
499 if (linkat(rd, from, wr, to, 0) < 0)
500 {
501 DBG1("linking '%s' to '%s' failed", from, to);
502 return -errno;
503 }
504 return 0;
505 }
506
507 /**
508 * FUSE chmod method
509 */
510 static int cowfs_chmod(const char *path, mode_t mode)
511 {
512 int fd;
513 struct stat st;
514
515 rel(&path);
516 fd = get_rd(path);
517 if (fstatat(fd, path, &st, 0) < 0)
518 {
519 return -errno;
520 }
521 if (st.st_mode == mode)
522 {
523 return 0;
524 }
525 fd = copy(path);
526 if (fd < 0)
527 {
528 return -errno;
529 }
530 if (fchmodat(fd, path, mode, 0) < 0)
531 {
532 return -errno;
533 }
534 return 0;
535 }
536
537 /**
538 * FUSE chown method
539 */
540 static int cowfs_chown(const char *path, uid_t uid, gid_t gid)
541 {
542 int fd;
543 struct stat st;
544
545 rel(&path);
546 fd = get_rd(path);
547 if (fstatat(fd, path, &st, 0) < 0)
548 {
549 return -errno;
550 }
551 if (st.st_uid == uid && st.st_gid == gid)
552 {
553 return 0;
554 }
555 fd = copy(path);
556 if (fd < 0)
557 {
558 return -errno;
559 }
560 if (fchownat(fd, path, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
561 {
562 return -errno;
563 }
564 return 0;
565 }
566
567 /**
568 * FUSE truncate method
569 */
570 static int cowfs_truncate(const char *path, off_t size)
571 {
572 int fd;
573 struct stat st;
574
575 rel(&path);
576 fd = get_rd(path);
577 if (fstatat(fd, path, &st, 0) < 0)
578 {
579 return -errno;
580 }
581 if (st.st_size == size)
582 {
583 return 0;
584 }
585 fd = copy(path);
586 if (fd < 0)
587 {
588 return -errno;
589 }
590 fd = openat(fd, path, O_WRONLY);
591 if (fd < 0)
592 {
593 return -errno;
594 }
595 if (ftruncate(fd, size) < 0)
596 {
597 close(fd);
598 return -errno;
599 }
600 close(fd);
601 return 0;
602 }
603
604 /**
605 * FUSE utimens method
606 */
607 static int cowfs_utimens(const char *path, const struct timespec ts[2])
608 {
609 struct timeval tv[2];
610 int fd;
611
612 rel(&path);
613 fd = copy(path);
614 if (fd < 0)
615 {
616 return -errno;
617 }
618
619 tv[0].tv_sec = ts[0].tv_sec;
620 tv[0].tv_usec = ts[0].tv_nsec / 1000;
621 tv[1].tv_sec = ts[1].tv_sec;
622 tv[1].tv_usec = ts[1].tv_nsec / 1000;
623
624 if (futimesat(fd, path, tv) < 0)
625 {
626 return -errno;
627 }
628 return 0;
629 }
630
631 /**
632 * FUSE open method
633 */
634 static int cowfs_open(const char *path, struct fuse_file_info *fi)
635 {
636 int fd;
637
638 rel(&path);
639 fd = get_rd(path);
640
641 fd = openat(fd, path, fi->flags);
642 if (fd < 0)
643 {
644 return -errno;
645 }
646 close(fd);
647 return 0;
648 }
649
650 /**
651 * FUSE read method
652 */
653 static int cowfs_read(const char *path, char *buf, size_t size, off_t offset,
654 struct fuse_file_info *fi)
655 {
656 int file, fd, res;
657
658 rel(&path);
659
660 fd = get_rd(path);
661
662 file = openat(fd, path, O_RDONLY);
663 if (file < 0)
664 {
665 return -errno;
666 }
667
668 res = pread(file, buf, size, offset);
669 if (res < 0)
670 {
671 res = -errno;
672 }
673 close(file);
674 return res;
675 }
676
677 /**
678 * FUSE write method
679 */
680 static int cowfs_write(const char *path, const char *buf, size_t size,
681 off_t offset, struct fuse_file_info *fi)
682 {
683 int file, fd, res;
684
685 rel(&path);
686
687 fd = copy(path);
688 if (fd < 0)
689 {
690 return -errno;
691 }
692 file = openat(fd, path, O_WRONLY);
693 if (file < 0)
694 {
695 return -errno;
696 }
697 res = pwrite(file, buf, size, offset);
698 if (res < 0)
699 {
700 res = -errno;
701 }
702 close(file);
703 return res;
704 }
705
706 /**
707 * FUSE statfs method
708 */
709 static int cowfs_statfs(const char *path, struct statvfs *stbuf)
710 {
711 int fd;
712
713 fd = get_rd(path);
714 if (fstatvfs(fd, stbuf) < 0)
715 {
716 return -errno;
717 }
718
719 return 0;
720 }
721
722 /**
723 * FUSE init method
724 */
725 static void *cowfs_init(struct fuse_conn_info *conn)
726 {
727 struct fuse_context *ctx;
728
729 ctx = fuse_get_context();
730
731 return ctx->private_data;
732 }
733
734 /**
735 * FUSE method vectors
736 */
737 static struct fuse_operations cowfs_operations = {
738 .getattr = cowfs_getattr,
739 .access = cowfs_access,
740 .readlink = cowfs_readlink,
741 .readdir = cowfs_readdir,
742 .mknod = cowfs_mknod,
743 .mkdir = cowfs_mkdir,
744 .symlink = cowfs_symlink,
745 .unlink = cowfs_unlink,
746 .rmdir = cowfs_rmdir,
747 .rename = cowfs_rename,
748 .link = cowfs_link,
749 .chmod = cowfs_chmod,
750 .chown = cowfs_chown,
751 .truncate = cowfs_truncate,
752 .utimens = cowfs_utimens,
753 .open = cowfs_open,
754 .read = cowfs_read,
755 .write = cowfs_write,
756 .statfs = cowfs_statfs,
757 .init = cowfs_init,
758 };
759
760 /**
761 * Implementation of cowfs_t.set_overlay.
762 */
763 static bool set_overlay(private_cowfs_t *this, char *path)
764 {
765 if (this->over)
766 {
767 free(this->over);
768 this->over = NULL;
769 }
770 if (this->over_fd > 0)
771 {
772 close(this->over_fd);
773 this->over_fd = -1;
774 }
775 if (path)
776 {
777 this->over_fd = open(path, O_RDONLY | O_DIRECTORY);
778 if (this->over_fd < 0)
779 {
780 DBG1("failed to open overlay directory '%s': %m", path);
781 return FALSE;
782 }
783 this->over = strdup(path);
784 }
785 return TRUE;
786 }
787
788 /**
789 * stop, umount and destroy a cowfs FUSE filesystem
790 */
791 static void destroy(private_cowfs_t *this)
792 {
793 fuse_exit(this->fuse);
794 fuse_unmount(this->mount, this->chan);
795 pthread_join(this->thread, NULL);
796 fuse_destroy(this->fuse);
797 free(this->mount);
798 free(this->master);
799 free(this->host);
800 free(this->over);
801 close(this->master_fd);
802 close(this->host_fd);
803 if (this->over_fd > 0)
804 {
805 close(this->over_fd);
806 }
807 free(this);
808 }
809
810 /**
811 * creates a new cowfs fuse instance
812 */
813 cowfs_t *cowfs_create(char *master, char *host, char *mount)
814 {
815 struct fuse_args args = {0, NULL, 0};
816 private_cowfs_t *this = malloc_thing(private_cowfs_t);
817
818 this->public.set_overlay = (bool(*)(cowfs_t*, char *path))set_overlay;
819 this->public.destroy = (void(*)(cowfs_t*))destroy;
820
821 this->master_fd = open(master, O_RDONLY | O_DIRECTORY);
822 if (this->master_fd < 0)
823 {
824 DBG1("failed to open master filesystem '%s'", master);
825 free(this);
826 return NULL;
827 }
828 this->host_fd = open(host, O_RDONLY | O_DIRECTORY);
829 if (this->host_fd < 0)
830 {
831 DBG1("failed to open host filesystem '%s'", host);
832 close(this->master_fd);
833 free(this);
834 return NULL;
835 }
836 this->over_fd = -1;
837
838 this->chan = fuse_mount(mount, &args);
839 if (this->chan == NULL)
840 {
841 DBG1("mounting cowfs FUSE on '%s' failed", mount);
842 close(this->master_fd);
843 close(this->host_fd);
844 free(this);
845 return NULL;
846 }
847
848 this->fuse = fuse_new(this->chan, &args, &cowfs_operations,
849 sizeof(cowfs_operations), this);
850 if (this->fuse == NULL)
851 {
852 DBG1("creating cowfs FUSE handle failed");
853 close(this->master_fd);
854 close(this->host_fd);
855 fuse_unmount(mount, this->chan);
856 free(this);
857 return NULL;
858 }
859
860 this->mount = strdup(mount);
861 this->master = strdup(master);
862 this->host = strdup(host);
863 this->over = NULL;
864
865 if (pthread_create(&this->thread, NULL, (void*)fuse_loop, this->fuse) != 0)
866 {
867 DBG1("creating thread to handle FUSE failed");
868 fuse_unmount(mount, this->chan);
869 free(this->mount);
870 free(this->master);
871 free(this->host);
872 close(this->master_fd);
873 close(this->host_fd);
874 free(this);
875 return NULL;
876 }
877
878 return &this->public;
879 }
880