completed pkcs7 parsing methods
[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 private_cowfs_t *this = get_this();
466
467 rel(&from);
468 rel(&to);
469
470 fd = get_rd(from);
471 if (fd == this->master_fd)
472 {
473 fd = copy(from);
474 if (fd < 0)
475 {
476 return -errno;
477 }
478 }
479
480 if (renameat(fd, from, get_wr(to), to) < 0)
481 {
482 return -errno;
483 }
484 return 0;
485 }
486
487 /**
488 * FUSE link method
489 */
490 static int cowfs_link(const char *from, const char *to)
491 {
492 int rd, wr;
493
494 rel(&from);
495 rel(&to);
496
497 rd = get_rd(from);
498 wr = get_wr(to);
499
500 if (!clone_path(rd, wr, to))
501 {
502 DBG1("cloning path '%s' failed", to);
503 return -errno;
504 }
505 if (linkat(rd, from, wr, to, 0) < 0)
506 {
507 DBG1("linking '%s' to '%s' failed", from, to);
508 return -errno;
509 }
510 return 0;
511 }
512
513 /**
514 * FUSE chmod method
515 */
516 static int cowfs_chmod(const char *path, mode_t mode)
517 {
518 int fd;
519 struct stat st;
520 private_cowfs_t *this = get_this();
521
522 rel(&path);
523 fd = get_rd(path);
524 if (fd == this->master_fd)
525 {
526 if (fstatat(fd, path, &st, 0) < 0)
527 {
528 return -errno;
529 }
530 if (st.st_mode == mode)
531 {
532 return 0;
533 }
534 fd = copy(path);
535 if (fd < 0)
536 {
537 return -errno;
538 }
539 }
540 if (fchmodat(fd, path, mode, 0) < 0)
541 {
542 return -errno;
543 }
544 return 0;
545 }
546
547 /**
548 * FUSE chown method
549 */
550 static int cowfs_chown(const char *path, uid_t uid, gid_t gid)
551 {
552 int fd;
553 struct stat st;
554 private_cowfs_t *this = get_this();
555
556 rel(&path);
557 fd = get_rd(path);
558 if (fd == this->master_fd)
559 {
560 if (fstatat(fd, path, &st, 0) < 0)
561 {
562 return -errno;
563 }
564 if (st.st_uid == uid && st.st_gid == gid)
565 {
566 return 0;
567 }
568 fd = copy(path);
569 if (fd < 0)
570 {
571 return -errno;
572 }
573 }
574 if (fchownat(fd, path, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
575 {
576 return -errno;
577 }
578 return 0;
579 }
580
581 /**
582 * FUSE truncate method
583 */
584 static int cowfs_truncate(const char *path, off_t size)
585 {
586 int fd;
587 struct stat st;
588
589 private_cowfs_t *this = get_this();
590
591 rel(&path);
592 fd = get_rd(path);
593 if (fd == this->master_fd)
594 {
595 if (fstatat(fd, path, &st, 0) < 0)
596 {
597 return -errno;
598 }
599 if (st.st_size == size)
600 {
601 return 0;
602 }
603 fd = copy(path);
604 if (fd < 0)
605 {
606 return -errno;
607 }
608 }
609 fd = openat(fd, path, O_WRONLY);
610 if (fd < 0)
611 {
612 return -errno;
613 }
614 if (ftruncate(fd, size) < 0)
615 {
616 close(fd);
617 return -errno;
618 }
619 close(fd);
620 return 0;
621 }
622
623 /**
624 * FUSE utimens method
625 */
626 static int cowfs_utimens(const char *path, const struct timespec ts[2])
627 {
628 struct timeval tv[2];
629 int fd;
630 private_cowfs_t *this = get_this();
631
632 rel(&path);
633 fd = get_rd(path);
634 if (fd == this->master_fd)
635 {
636 fd = copy(path);
637 if (fd < 0)
638 {
639 return -errno;
640 }
641 }
642
643 tv[0].tv_sec = ts[0].tv_sec;
644 tv[0].tv_usec = ts[0].tv_nsec / 1000;
645 tv[1].tv_sec = ts[1].tv_sec;
646 tv[1].tv_usec = ts[1].tv_nsec / 1000;
647
648 if (futimesat(fd, path, tv) < 0)
649 {
650 return -errno;
651 }
652 return 0;
653 }
654
655 /**
656 * FUSE open method
657 */
658 static int cowfs_open(const char *path, struct fuse_file_info *fi)
659 {
660 int fd;
661
662 rel(&path);
663 fd = get_rd(path);
664
665 fd = openat(fd, path, fi->flags);
666 if (fd < 0)
667 {
668 return -errno;
669 }
670 close(fd);
671 return 0;
672 }
673
674 /**
675 * FUSE read method
676 */
677 static int cowfs_read(const char *path, char *buf, size_t size, off_t offset,
678 struct fuse_file_info *fi)
679 {
680 int file, fd, res;
681
682 rel(&path);
683
684 fd = get_rd(path);
685
686 file = openat(fd, path, O_RDONLY);
687 if (file < 0)
688 {
689 return -errno;
690 }
691
692 res = pread(file, buf, size, offset);
693 if (res < 0)
694 {
695 res = -errno;
696 }
697 close(file);
698 return res;
699 }
700
701 /**
702 * FUSE write method
703 */
704 static int cowfs_write(const char *path, const char *buf, size_t size,
705 off_t offset, struct fuse_file_info *fi)
706 {
707 private_cowfs_t *this = get_this();
708 int file, fd, res;
709
710 rel(&path);
711
712 fd = get_rd(path);
713 if (fd == this->master_fd)
714 {
715 fd = copy(path);
716 if (fd < 0)
717 {
718 return -errno;
719 }
720 }
721 file = openat(fd, path, O_WRONLY);
722 if (file < 0)
723 {
724 return -errno;
725 }
726 res = pwrite(file, buf, size, offset);
727 if (res < 0)
728 {
729 res = -errno;
730 }
731 close(file);
732 return res;
733 }
734
735 /**
736 * FUSE statfs method
737 */
738 static int cowfs_statfs(const char *path, struct statvfs *stbuf)
739 {
740 private_cowfs_t *this = get_this();
741 int fd;
742
743 fd = this->host_fd;
744 if (this->over_fd > 0)
745 {
746 fd = this->over_fd;
747 }
748
749 if (fstatvfs(fd, stbuf) < 0)
750 {
751 return -errno;
752 }
753
754 return 0;
755 }
756
757 /**
758 * FUSE init method
759 */
760 static void *cowfs_init(struct fuse_conn_info *conn)
761 {
762 struct fuse_context *ctx;
763
764 ctx = fuse_get_context();
765
766 return ctx->private_data;
767 }
768
769 /**
770 * FUSE method vectors
771 */
772 static struct fuse_operations cowfs_operations = {
773 .getattr = cowfs_getattr,
774 .access = cowfs_access,
775 .readlink = cowfs_readlink,
776 .readdir = cowfs_readdir,
777 .mknod = cowfs_mknod,
778 .mkdir = cowfs_mkdir,
779 .symlink = cowfs_symlink,
780 .unlink = cowfs_unlink,
781 .rmdir = cowfs_rmdir,
782 .rename = cowfs_rename,
783 .link = cowfs_link,
784 .chmod = cowfs_chmod,
785 .chown = cowfs_chown,
786 .truncate = cowfs_truncate,
787 .utimens = cowfs_utimens,
788 .open = cowfs_open,
789 .read = cowfs_read,
790 .write = cowfs_write,
791 .statfs = cowfs_statfs,
792 .init = cowfs_init,
793 };
794
795 /**
796 * Implementation of cowfs_t.set_overlay.
797 */
798 static bool set_overlay(private_cowfs_t *this, char *path)
799 {
800 if (this->over)
801 {
802 free(this->over);
803 this->over = NULL;
804 }
805 if (this->over_fd > 0)
806 {
807 close(this->over_fd);
808 this->over_fd = -1;
809 }
810 if (path)
811 {
812 this->over_fd = open(path, O_RDONLY | O_DIRECTORY);
813 if (this->over_fd < 0)
814 {
815 DBG1("failed to open overlay directory '%s': %m", path);
816 return FALSE;
817 }
818 this->over = strdup(path);
819 }
820 return TRUE;
821 }
822
823 /**
824 * stop, umount and destroy a cowfs FUSE filesystem
825 */
826 static void destroy(private_cowfs_t *this)
827 {
828 fuse_exit(this->fuse);
829 fuse_unmount(this->mount, this->chan);
830 pthread_join(this->thread, NULL);
831 fuse_destroy(this->fuse);
832 free(this->mount);
833 free(this->master);
834 free(this->host);
835 free(this->over);
836 close(this->master_fd);
837 close(this->host_fd);
838 if (this->over_fd > 0)
839 {
840 close(this->over_fd);
841 }
842 free(this);
843 }
844
845 /**
846 * creates a new cowfs fuse instance
847 */
848 cowfs_t *cowfs_create(char *master, char *host, char *mount)
849 {
850 struct fuse_args args = {0, NULL, 0};
851 private_cowfs_t *this = malloc_thing(private_cowfs_t);
852
853 this->public.set_overlay = (bool(*)(cowfs_t*, char *path))set_overlay;
854 this->public.destroy = (void(*)(cowfs_t*))destroy;
855
856 this->master_fd = open(master, O_RDONLY | O_DIRECTORY);
857 if (this->master_fd < 0)
858 {
859 DBG1("failed to open master filesystem '%s'", master);
860 free(this);
861 }
862 this->host_fd = open(host, O_RDONLY | O_DIRECTORY);
863 if (this->master_fd < 0)
864 {
865 DBG1("failed to open host filesystem '%s'", host);
866 close(this->master_fd);
867 free(this);
868 }
869 this->over_fd = -1;
870
871 this->chan = fuse_mount(mount, &args);
872 if (this->chan == NULL)
873 {
874 DBG1("mounting cowfs FUSE on '%s' failed", mount);
875 close(this->master_fd);
876 close(this->host_fd);
877 free(this);
878 return NULL;
879 }
880
881 this->fuse = fuse_new(this->chan, &args, &cowfs_operations,
882 sizeof(cowfs_operations), this);
883 if (this->fuse == NULL)
884 {
885 DBG1("creating cowfs FUSE handle failed");
886 close(this->master_fd);
887 close(this->host_fd);
888 fuse_unmount(mount, this->chan);
889 free(this);
890 return NULL;
891 }
892
893 this->mount = strdup(mount);
894 this->master = strdup(master);
895 this->host = strdup(host);
896 this->over = NULL;
897
898 if (pthread_create(&this->thread, NULL, (void*)fuse_loop, this->fuse) != 0)
899 {
900 DBG1("creating thread to handle FUSE failed");
901 fuse_unmount(mount, this->chan);
902 free(this->mount);
903 free(this->master);
904 free(this->host);
905 close(this->master_fd);
906 close(this->host_fd);
907 free(this);
908 return NULL;
909 }
910
911 return &this->public;
912 }
913