2 * Copyright (C) 2007 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 * Copyright (C) 2001-2007 Miklos Szeredi
6 * Based on example shipped with FUSE.
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>.
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
20 #define FUSE_USE_VERSION 26
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
);
43 typedef struct private_cowfs_t private_cowfs_t
;
45 struct private_cowfs_t
{
46 /** public cowfs interface */
48 /** fuse channel to mountpoint */
49 struct fuse_chan
*chan
;
52 /** mountpoint of cowfs FUSE */
54 /** master filesystem path */
56 /** host filesystem path */
58 /** scenario filesystem path */
60 /** fd of read only master filesystem */
62 /** copy on write overlay to master */
64 /** optional scenario COW overlay */
66 /** thread processing FUSE */
71 * get this pointer stored in fuse context
73 static private_cowfs_t
*get_this()
75 return (fuse_get_context())->private_data
;
79 * make a path relative
81 static void rel(const char **path
)
94 * get the highest overlay in which path exists
96 static int get_rd(const char *path
)
98 private_cowfs_t
*this = get_this();
100 if (this->scen_fd
> 0 && faccessat(this->scen_fd
, path
, F_OK
, 0) == 0)
102 return this->scen_fd
;
104 if (faccessat(this->host_fd
, path
, F_OK
, 0) == 0)
106 return this->host_fd
;
108 return this->master_fd
;
112 * get the highest overlay available, to write something
114 static int get_wr(const char *path
)
116 private_cowfs_t
*this = get_this();
117 if (this->scen_fd
> 0)
119 return this->scen_fd
;
121 return this->host_fd
;
125 * create full "path" at "wr" the same way they exist at "rd"
127 static bool clone_path(int rd
, int wr
, const char *path
)
131 full
= strdupa(path
);
134 while ((pos
= strchr(pos
, '/')))
137 if (fstatat(wr
, full
, &st
, 0) < 0)
139 /* TODO: handle symlinks!? */
140 if (fstatat(rd
, full
, &st
, 0) < 0)
144 if (mkdirat(wr
, full
, st
.st_mode
) < 0)
156 * copy a (special) file from a readonly to a read-write overlay
158 static int copy(const char *path
)
171 /* already writeable */
174 if (fstatat(rd
, path
, &st
, 0) < 0)
178 if (!clone_path(rd
, wr
, path
))
182 if (mknodat(wr
, path
, st
.st_mode
, st
.st_rdev
) < 0)
186 /* copy if no special file */
189 from
= openat(rd
, path
, O_RDONLY
, st
.st_mode
);
194 to
= openat(wr
, path
, O_WRONLY
, st
.st_mode
);
200 while ((len
= read(from
, buf
, sizeof(buf
))) > 0)
202 if (write(to
, buf
, len
) < len
)
204 /* TODO: only on len < 0 ? */
221 * FUSE getattr method
223 static int cowfs_getattr(const char *path
, struct stat
*stbuf
)
227 if (fstatat(get_rd(path
), path
, stbuf
, AT_SYMLINK_NOFOLLOW
) < 0)
237 static int cowfs_access(const char *path
, int mask
)
241 if (faccessat(get_rd(path
), path
, mask
, 0) < 0)
249 * FUSE readlink method
251 static int cowfs_readlink(const char *path
, char *buf
, size_t size
)
257 res
= readlinkat(get_rd(path
), path
, buf
, size
- 1);
267 * get a directory stream of two concatenated paths
269 static DIR* get_dir(char *dir
, const char *subdir
)
278 full
= alloca(strlen(dir
) + strlen(subdir
) + 1);
280 strcat(full
, subdir
);
282 return opendir(full
);
286 * check if a directory stream contains a directory
288 static bool contains_dir(DIR *d
, char *dirname
)
295 while ((ent
= readdir(d
)))
297 if (streq(ent
->d_name
, dirname
))
307 * FUSE readdir method
309 static int cowfs_readdir(const char *path
, void *buf
, fuse_fill_dir_t filler
,
310 off_t offset
, struct fuse_file_info
*fi
)
312 private_cowfs_t
*this = get_this();
317 memset(&st
, 0, sizeof(st
));
319 d1
= get_dir(this->master
, path
);
320 d2
= get_dir(this->host
, path
);
321 d3
= get_dir(this->scen
, path
);
325 while ((ent
= readdir(d1
)))
327 if (!contains_dir(d2
, ent
->d_name
) &&
328 !contains_dir(d3
, ent
->d_name
))
330 st
.st_ino
= ent
->d_ino
;
331 st
.st_mode
= ent
->d_type
<< 12;
332 filler(buf
, ent
->d_name
, &st
, 0);
340 while ((ent
= readdir(d2
)))
342 if (!contains_dir(d3
, ent
->d_name
))
344 st
.st_ino
= ent
->d_ino
;
345 st
.st_mode
= ent
->d_type
<< 12;
346 filler(buf
, ent
->d_name
, &st
, 0);
354 while ((ent
= readdir(d3
)))
356 st
.st_ino
= ent
->d_ino
;
357 st
.st_mode
= ent
->d_type
<< 12;
358 filler(buf
, ent
->d_name
, &st
, 0);
368 static int cowfs_mknod(const char *path
, mode_t mode
, dev_t rdev
)
374 if (!clone_path(get_rd(path
), fd
, path
))
379 if (mknodat(fd
, path
, mode
, rdev
) < 0)
389 static int cowfs_mkdir(const char *path
, mode_t mode
)
395 if (!clone_path(get_rd(path
), fd
, path
))
399 if (mkdirat(fd
, path
, mode
) < 0)
409 static int cowfs_unlink(const char *path
)
413 /* TODO: whiteout master */
414 if (unlinkat(get_wr(path
), path
, 0) < 0)
424 static int cowfs_rmdir(const char *path
)
428 /* TODO: whiteout master */
429 if (unlinkat(get_wr(path
), path
, AT_REMOVEDIR
) < 0)
437 * FUSE symlink method
439 static int cowfs_symlink(const char *from
, const char *to
)
442 const char *fromrel
= from
;
448 if (!clone_path(get_rd(fromrel
), fd
, fromrel
))
452 if (symlinkat(from
, fd
, to
) < 0)
462 static int cowfs_rename(const char *from
, const char *to
)
465 private_cowfs_t
*this = get_this();
471 if (fd
== this->master_fd
)
480 if (renameat(fd
, from
, get_wr(to
), to
) < 0)
490 static int cowfs_link(const char *from
, const char *to
)
500 if (!clone_path(rd
, wr
, to
))
502 DBG1("cloning path '%s' failed", to
);
505 if (linkat(rd
, from
, wr
, to
, 0) < 0)
507 DBG1("linking '%s' to '%s' failed", from
, to
);
516 static int cowfs_chmod(const char *path
, mode_t mode
)
520 private_cowfs_t
*this = get_this();
524 if (fd
== this->master_fd
)
526 if (fstatat(fd
, path
, &st
, 0) < 0)
530 if (st
.st_mode
== mode
)
540 if (fchmodat(fd
, path
, mode
, 0) < 0)
550 static int cowfs_chown(const char *path
, uid_t uid
, gid_t gid
)
554 private_cowfs_t
*this = get_this();
558 if (fd
== this->master_fd
)
560 if (fstatat(fd
, path
, &st
, 0) < 0)
564 if (st
.st_uid
== uid
&& st
.st_gid
== gid
)
574 if (fchownat(fd
, path
, uid
, gid
, AT_SYMLINK_NOFOLLOW
) < 0)
582 * FUSE truncate method
584 static int cowfs_truncate(const char *path
, off_t size
)
589 private_cowfs_t
*this = get_this();
593 if (fd
== this->master_fd
)
595 if (fstatat(fd
, path
, &st
, 0) < 0)
599 if (st
.st_size
== size
)
609 fd
= openat(fd
, path
, O_WRONLY
);
614 if (ftruncate(fd
, size
) < 0)
624 * FUSE utimens method
626 static int cowfs_utimens(const char *path
, const struct timespec ts
[2])
628 struct timeval tv
[2];
630 private_cowfs_t
*this = get_this();
634 if (fd
== this->master_fd
)
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;
648 if (futimesat(fd
, path
, tv
) < 0)
658 static int cowfs_open(const char *path
, struct fuse_file_info
*fi
)
665 fd
= openat(fd
, path
, fi
->flags
);
677 static int cowfs_read(const char *path
, char *buf
, size_t size
, off_t offset
,
678 struct fuse_file_info
*fi
)
686 file
= openat(fd
, path
, O_RDONLY
);
692 res
= pread(file
, buf
, size
, offset
);
704 static int cowfs_write(const char *path
, const char *buf
, size_t size
,
705 off_t offset
, struct fuse_file_info
*fi
)
707 private_cowfs_t
*this = get_this();
713 if (fd
== this->master_fd
)
721 file
= openat(fd
, path
, O_WRONLY
);
726 res
= pwrite(file
, buf
, size
, offset
);
738 static int cowfs_statfs(const char *path
, struct statvfs
*stbuf
)
740 private_cowfs_t
*this = get_this();
744 if (this->scen_fd
> 0)
749 if (fstatvfs(fd
, stbuf
) < 0)
760 static void *cowfs_init(struct fuse_conn_info
*conn
)
762 struct fuse_context
*ctx
;
764 ctx
= fuse_get_context();
766 return ctx
->private_data
;
770 * FUSE method vectors
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
,
784 .chmod
= cowfs_chmod
,
785 .chown
= cowfs_chown
,
786 .truncate
= cowfs_truncate
,
787 .utimens
= cowfs_utimens
,
790 .write
= cowfs_write
,
791 .statfs
= cowfs_statfs
,
796 * Implementation of cowfs_t.set_scenario.
798 static bool set_scenario(private_cowfs_t
*this, char *path
)
805 if (this->scen_fd
> 0)
807 close(this->scen_fd
);
812 this->scen_fd
= open(path
, O_RDONLY
| O_DIRECTORY
);
813 if (this->scen_fd
< 0)
815 DBG1("failed to open scenario overlay directory '%s': %m", path
);
818 this->scen
= strdup(path
);
824 * stop, umount and destroy a cowfs FUSE filesystem
826 static void destroy(private_cowfs_t
*this)
828 fuse_exit(this->fuse
);
829 fuse_unmount(this->mount
, this->chan
);
830 pthread_join(this->thread
, NULL
);
831 fuse_destroy(this->fuse
);
836 close(this->master_fd
);
837 close(this->host_fd
);
838 if (this->scen_fd
> 0)
840 close(this->scen_fd
);
846 * creates a new cowfs fuse instance
848 cowfs_t
*cowfs_create(char *master
, char *host
, char *mount
)
850 struct fuse_args args
= {0, NULL
, 0};
851 private_cowfs_t
*this = malloc_thing(private_cowfs_t
);
853 this->public.set_scenario
= (bool(*)(cowfs_t
*, char *path
))set_scenario
;
854 this->public.destroy
= (void(*)(cowfs_t
*))destroy
;
856 this->master_fd
= open(master
, O_RDONLY
| O_DIRECTORY
);
857 if (this->master_fd
< 0)
859 DBG1("failed to open master filesystem '%s'", master
);
862 this->host_fd
= open(host
, O_RDONLY
| O_DIRECTORY
);
863 if (this->master_fd
< 0)
865 DBG1("failed to open host filesystem '%s'", host
);
866 close(this->master_fd
);
871 this->chan
= fuse_mount(mount
, &args
);
872 if (this->chan
== NULL
)
874 DBG1("mounting cowfs FUSE on '%s' failed", mount
);
875 close(this->master_fd
);
876 close(this->host_fd
);
881 this->fuse
= fuse_new(this->chan
, &args
, &cowfs_operations
,
882 sizeof(cowfs_operations
), this);
883 if (this->fuse
== NULL
)
885 DBG1("creating cowfs FUSE handle failed");
886 close(this->master_fd
);
887 close(this->host_fd
);
888 fuse_unmount(mount
, this->chan
);
893 this->mount
= strdup(mount
);
894 this->master
= strdup(master
);
895 this->host
= strdup(host
);
898 if (pthread_create(&this->thread
, NULL
, (void*)fuse_loop
, this->fuse
) != 0)
900 DBG1("creating thread to handle FUSE failed");
901 fuse_unmount(mount
, this->chan
);
905 close(this->master_fd
);
906 close(this->host_fd
);
911 return &this->public;