2 * Copyright (C) 2009 Tobias Brunner
3 * Copyright (C) 2007 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 * Copyright (C) 2001-2007 Miklos Szeredi
7 * Based on example shipped with FUSE.
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 #define FUSE_USE_VERSION 26
38 #include <threading/thread.h>
39 #include <threading/rwlock.h>
40 #include <utils/linked_list.h>
42 /** define _XOPEN_SOURCE 500 fails when using libstrongswan, define popen */
43 extern ssize_t
pread(int fd
, void *buf
, size_t count
, off_t offset
);
44 extern ssize_t
pwrite(int fd
, const void *buf
, size_t count
, off_t offset
);
46 typedef struct private_cowfs_t private_cowfs_t
;
48 struct private_cowfs_t
{
49 /** public cowfs interface */
51 /** fuse channel to mountpoint */
52 struct fuse_chan
*chan
;
55 /** mountpoint of cowfs FUSE */
57 /** master filesystem path */
59 /** host filesystem path */
61 /** overlay filesystems */
62 linked_list_t
*overlays
;
63 /** lock for overlays */
65 /** fd of read only master filesystem */
67 /** copy on write overlay to master */
69 /** thread processing FUSE */
73 typedef struct overlay_t overlay_t
;
76 * data for overlay filesystems
79 /** path to overlay */
88 static void overlay_destroy(overlay_t
*this)
96 * compare two overlays by path
98 static bool overlay_equals(overlay_t
*this, overlay_t
*other
)
100 return streq(this->path
, other
->path
);
104 * remove and destroy the overlay with the given absolute path.
105 * returns FALSE, if not found.
107 static bool overlay_remove(private_cowfs_t
*this, char *path
)
109 overlay_t over
, *current
;
111 if (this->overlays
->find_first(this->overlays
,
112 (linked_list_match_t
)overlay_equals
, (void**)¤t
, &over
) != SUCCESS
)
116 this->overlays
->remove(this->overlays
, current
, NULL
);
117 overlay_destroy(current
);
122 * get this pointer stored in fuse context
124 static private_cowfs_t
*get_this()
126 return (fuse_get_context())->private_data
;
130 * make a path relative
132 static void rel(const char **path
)
145 * get the highest overlay in which path exists
147 static int get_rd(const char *path
)
150 enumerator_t
*enumerator
;
151 private_cowfs_t
*this = get_this();
153 this->lock
->read_lock(this->lock
);
154 enumerator
= this->overlays
->create_enumerator(this->overlays
);
155 while (enumerator
->enumerate(enumerator
, (void**)&over
))
157 if (faccessat(over
->fd
, path
, F_OK
, 0) == 0)
160 enumerator
->destroy(enumerator
);
161 this->lock
->unlock(this->lock
);
165 enumerator
->destroy(enumerator
);
166 this->lock
->unlock(this->lock
);
168 if (faccessat(this->host_fd
, path
, F_OK
, 0) == 0)
170 return this->host_fd
;
172 return this->master_fd
;
176 * get the highest overlay available, to write something
178 static int get_wr(const char *path
)
181 private_cowfs_t
*this = get_this();
182 int fd
= this->host_fd
;
183 this->lock
->read_lock(this->lock
);
184 if (this->overlays
->get_first(this->overlays
, (void**)&over
) == SUCCESS
)
188 this->lock
->unlock(this->lock
);
193 * create full "path" at "wr" the same way they exist at "rd"
195 static bool clone_path(int rd
, int wr
, const char *path
)
199 full
= strdupa(path
);
202 while ((pos
= strchr(pos
, '/')))
205 if (fstatat(wr
, full
, &st
, 0) < 0)
207 /* TODO: handle symlinks!? */
208 if (fstatat(rd
, full
, &st
, 0) < 0)
212 if (mkdirat(wr
, full
, st
.st_mode
) < 0)
224 * copy a (special) file from a readonly to a read-write overlay
226 static int copy(const char *path
)
239 /* already writeable */
242 if (fstatat(rd
, path
, &st
, 0) < 0)
246 if (!clone_path(rd
, wr
, path
))
250 if (mknodat(wr
, path
, st
.st_mode
, st
.st_rdev
) < 0)
254 /* copy if no special file */
257 from
= openat(rd
, path
, O_RDONLY
, st
.st_mode
);
262 to
= openat(wr
, path
, O_WRONLY
, st
.st_mode
);
268 while ((len
= read(from
, buf
, sizeof(buf
))) > 0)
270 if (write(to
, buf
, len
) < len
)
272 /* TODO: only on len < 0 ? */
289 * FUSE getattr method
291 static int cowfs_getattr(const char *path
, struct stat
*stbuf
)
295 if (fstatat(get_rd(path
), path
, stbuf
, AT_SYMLINK_NOFOLLOW
) < 0)
305 static int cowfs_access(const char *path
, int mask
)
309 if (faccessat(get_rd(path
), path
, mask
, 0) < 0)
317 * FUSE readlink method
319 static int cowfs_readlink(const char *path
, char *buf
, size_t size
)
325 res
= readlinkat(get_rd(path
), path
, buf
, size
- 1);
335 * get a directory stream of two concatenated paths
337 static DIR* get_dir(char *dir
, const char *subdir
)
346 full
= alloca(strlen(dir
) + strlen(subdir
) + 1);
348 strcat(full
, subdir
);
350 return opendir(full
);
354 * check if a directory stream contains a directory
356 static bool contains_dir(DIR *d
, char *dirname
)
361 while ((ent
= readdir(d
)))
363 if (streq(ent
->d_name
, dirname
))
372 * check if one of the higher overlays contains a directory
374 static bool overlays_contain_dir(DIR **d
, char *dirname
)
378 if (contains_dir(*d
, dirname
))
387 * FUSE readdir method
389 static int cowfs_readdir(const char *path
, void *buf
, fuse_fill_dir_t filler
,
390 off_t offset
, struct fuse_file_info
*fi
)
392 #define ADD_DIR(overlay, base, path) ({\
393 DIR *dir = get_dir(base, path);\
394 if (dir) { *(--overlay) = dir; }\
396 private_cowfs_t
*this = get_this();
402 enumerator_t
*enumerator
;
404 memset(&st
, 0, sizeof(st
));
406 this->lock
->read_lock(this->lock
);
407 /* create a null-terminated array of DIR objects for all overlays (including
408 * the master and host layer). the order is from bottom to top */
409 count
= this->overlays
->get_count(this->overlays
) + 2;
410 overlays
= calloc(count
+ 1, sizeof(DIR*));
411 d
= &overlays
[count
];
413 enumerator
= this->overlays
->create_enumerator(this->overlays
);
414 while (enumerator
->enumerate(enumerator
, (void**)&over
))
416 ADD_DIR(d
, over
->path
, path
);
418 enumerator
->destroy(enumerator
);
419 this->lock
->unlock(this->lock
);
421 ADD_DIR(d
, this->host
, path
);
422 ADD_DIR(d
, this->master
, path
);
427 while((ent
= readdir(*d
)))
429 if (!overlays_contain_dir(d
+ 1, ent
->d_name
))
431 st
.st_ino
= ent
->d_ino
;
432 st
.st_mode
= ent
->d_type
<< 12;
433 filler(buf
, ent
->d_name
, &st
, 0);
446 static int cowfs_mknod(const char *path
, mode_t mode
, dev_t rdev
)
452 if (!clone_path(get_rd(path
), fd
, path
))
457 if (mknodat(fd
, path
, mode
, rdev
) < 0)
467 static int cowfs_mkdir(const char *path
, mode_t mode
)
473 if (!clone_path(get_rd(path
), fd
, path
))
477 if (mkdirat(fd
, path
, mode
) < 0)
487 static int cowfs_unlink(const char *path
)
491 /* TODO: whiteout master */
492 if (unlinkat(get_wr(path
), path
, 0) < 0)
502 static int cowfs_rmdir(const char *path
)
506 /* TODO: whiteout master */
507 if (unlinkat(get_wr(path
), path
, AT_REMOVEDIR
) < 0)
515 * FUSE symlink method
517 static int cowfs_symlink(const char *from
, const char *to
)
520 const char *fromrel
= from
;
526 if (!clone_path(get_rd(fromrel
), fd
, fromrel
))
530 if (symlinkat(from
, fd
, to
) < 0)
540 static int cowfs_rename(const char *from
, const char *to
)
552 if (renameat(fd
, from
, get_wr(to
), to
) < 0)
562 static int cowfs_link(const char *from
, const char *to
)
572 if (!clone_path(rd
, wr
, to
))
574 DBG1(DBG_LIB
, "cloning path '%s' failed", to
);
577 if (linkat(rd
, from
, wr
, to
, 0) < 0)
579 DBG1(DBG_LIB
, "linking '%s' to '%s' failed", from
, to
);
588 static int cowfs_chmod(const char *path
, mode_t mode
)
595 if (fstatat(fd
, path
, &st
, 0) < 0)
599 if (st
.st_mode
== mode
)
608 if (fchmodat(fd
, path
, mode
, 0) < 0)
618 static int cowfs_chown(const char *path
, uid_t uid
, gid_t gid
)
625 if (fstatat(fd
, path
, &st
, 0) < 0)
629 if (st
.st_uid
== uid
&& st
.st_gid
== gid
)
638 if (fchownat(fd
, path
, uid
, gid
, AT_SYMLINK_NOFOLLOW
) < 0)
646 * FUSE truncate method
648 static int cowfs_truncate(const char *path
, off_t size
)
655 if (fstatat(fd
, path
, &st
, 0) < 0)
659 if (st
.st_size
== size
)
668 fd
= openat(fd
, path
, O_WRONLY
);
673 if (ftruncate(fd
, size
) < 0)
683 * FUSE utimens method
685 static int cowfs_utimens(const char *path
, const struct timespec ts
[2])
687 struct timeval tv
[2];
697 tv
[0].tv_sec
= ts
[0].tv_sec
;
698 tv
[0].tv_usec
= ts
[0].tv_nsec
/ 1000;
699 tv
[1].tv_sec
= ts
[1].tv_sec
;
700 tv
[1].tv_usec
= ts
[1].tv_nsec
/ 1000;
702 if (futimesat(fd
, path
, tv
) < 0)
712 static int cowfs_open(const char *path
, struct fuse_file_info
*fi
)
719 fd
= openat(fd
, path
, fi
->flags
);
731 static int cowfs_read(const char *path
, char *buf
, size_t size
, off_t offset
,
732 struct fuse_file_info
*fi
)
740 file
= openat(fd
, path
, O_RDONLY
);
746 res
= pread(file
, buf
, size
, offset
);
758 static int cowfs_write(const char *path
, const char *buf
, size_t size
,
759 off_t offset
, struct fuse_file_info
*fi
)
770 file
= openat(fd
, path
, O_WRONLY
);
775 res
= pwrite(file
, buf
, size
, offset
);
787 static int cowfs_statfs(const char *path
, struct statvfs
*stbuf
)
792 if (fstatvfs(fd
, stbuf
) < 0)
803 static void *cowfs_init(struct fuse_conn_info
*conn
)
805 struct fuse_context
*ctx
;
807 ctx
= fuse_get_context();
809 return ctx
->private_data
;
813 * FUSE method vectors
815 static struct fuse_operations cowfs_operations
= {
816 .getattr
= cowfs_getattr
,
817 .access
= cowfs_access
,
818 .readlink
= cowfs_readlink
,
819 .readdir
= cowfs_readdir
,
820 .mknod
= cowfs_mknod
,
821 .mkdir
= cowfs_mkdir
,
822 .symlink
= cowfs_symlink
,
823 .unlink
= cowfs_unlink
,
824 .rmdir
= cowfs_rmdir
,
825 .rename
= cowfs_rename
,
827 .chmod
= cowfs_chmod
,
828 .chown
= cowfs_chown
,
829 .truncate
= cowfs_truncate
,
830 .utimens
= cowfs_utimens
,
833 .write
= cowfs_write
,
834 .statfs
= cowfs_statfs
,
839 * Implementation of cowfs_t.add_overlay.
841 static bool add_overlay(private_cowfs_t
*this, char *path
)
843 overlay_t
*over
= malloc_thing(overlay_t
);
844 over
->fd
= open(path
, O_RDONLY
| O_DIRECTORY
);
847 DBG1(DBG_LIB
, "failed to open overlay directory '%s': %m", path
);
851 over
->path
= realpath(path
, NULL
);
852 this->lock
->write_lock(this->lock
);
853 overlay_remove(this, over
->path
);
854 this->overlays
->insert_first(this->overlays
, over
);
855 this->lock
->unlock(this->lock
);
860 * Implementation of cowfs_t.del_overlay.
862 static bool del_overlay(private_cowfs_t
*this, char *path
)
866 this->lock
->write_lock(this->lock
);
867 removed
= overlay_remove(this, realpath(path
, real
));
868 this->lock
->unlock(this->lock
);
873 * Implementation of cowfs_t.pop_overlay.
875 static bool pop_overlay(private_cowfs_t
*this)
878 this->lock
->write_lock(this->lock
);
879 if (this->overlays
->remove_first(this->overlays
, (void**)&over
) != SUCCESS
)
881 this->lock
->unlock(this->lock
);
884 this->lock
->unlock(this->lock
);
885 overlay_destroy(over
);
890 * stop, umount and destroy a cowfs FUSE filesystem
892 static void destroy(private_cowfs_t
*this)
894 fuse_exit(this->fuse
);
895 fuse_unmount(this->mount
, this->chan
);
896 this->thread
->join(this->thread
);
897 fuse_destroy(this->fuse
);
898 this->lock
->destroy(this->lock
);
899 this->overlays
->destroy_function(this->overlays
, (void*)overlay_destroy
);
903 close(this->master_fd
);
904 close(this->host_fd
);
909 * creates a new cowfs fuse instance
911 cowfs_t
*cowfs_create(char *master
, char *host
, char *mount
)
913 struct fuse_args args
= {0, NULL
, 0};
914 private_cowfs_t
*this = malloc_thing(private_cowfs_t
);
916 this->public.add_overlay
= (bool(*)(cowfs_t
*, char *path
))add_overlay
;
917 this->public.del_overlay
= (bool(*)(cowfs_t
*, char *path
))del_overlay
;
918 this->public.pop_overlay
= (bool(*)(cowfs_t
*))pop_overlay
;
919 this->public.destroy
= (void(*)(cowfs_t
*))destroy
;
921 this->master_fd
= open(master
, O_RDONLY
| O_DIRECTORY
);
922 if (this->master_fd
< 0)
924 DBG1(DBG_LIB
, "failed to open master filesystem '%s'", master
);
928 this->host_fd
= open(host
, O_RDONLY
| O_DIRECTORY
);
929 if (this->host_fd
< 0)
931 DBG1(DBG_LIB
, "failed to open host filesystem '%s'", host
);
932 close(this->master_fd
);
937 this->chan
= fuse_mount(mount
, &args
);
938 if (this->chan
== NULL
)
940 DBG1(DBG_LIB
, "mounting cowfs FUSE on '%s' failed", mount
);
941 close(this->master_fd
);
942 close(this->host_fd
);
947 this->fuse
= fuse_new(this->chan
, &args
, &cowfs_operations
,
948 sizeof(cowfs_operations
), this);
949 if (this->fuse
== NULL
)
951 DBG1(DBG_LIB
, "creating cowfs FUSE handle failed");
952 close(this->master_fd
);
953 close(this->host_fd
);
954 fuse_unmount(mount
, this->chan
);
959 this->mount
= strdup(mount
);
960 this->master
= strdup(master
);
961 this->host
= strdup(host
);
962 this->overlays
= linked_list_create();
963 this->lock
= rwlock_create(RWLOCK_TYPE_DEFAULT
);
965 this->thread
= thread_create((thread_main_t
)fuse_loop
, this->fuse
);
968 DBG1(DBG_LIB
, "creating thread to handle FUSE failed");
969 fuse_unmount(mount
, this->chan
);
970 this->lock
->destroy(this->lock
);
971 this->overlays
->destroy(this->overlays
);
975 close(this->master_fd
);
976 close(this->host_fd
);
981 return &this->public;