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