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 */
70 static private_cowfs_t
*get_this()
72 return (fuse_get_context())->private_data
;
75 static void rel(const char **path
)
88 * get the filesystem to read something from
90 static int get_rd(const char *path
)
92 private_cowfs_t
*this = get_this();
94 if (this->scen_fd
> 0 && faccessat(this->scen_fd
, path
, F_OK
, 0) == 0)
98 if (faccessat(this->host_fd
, path
, F_OK
, 0) == 0)
100 return this->host_fd
;
102 return this->master_fd
;
106 * get the filesystem to write something to
108 static int get_wr(const char *path
)
110 private_cowfs_t
*this = get_this();
111 if (this->scen_fd
> 0)
113 return this->scen_fd
;
115 return this->host_fd
;
119 * create all directories need to create the file "path"
121 static bool clone_path(int rd
, int wr
, const char *path
)
125 full
= strdupa(path
);
128 while ((pos
= strchr(pos
, '/')))
131 if (fstatat(wr
, full
, &st
, 0) < 0)
133 /* TODO: handle symlinks! */
134 if (fstatat(rd
, full
, &st
, 0) < 0)
138 if (mkdirat(wr
, full
, st
.st_mode
) < 0)
150 * copy a file from a readonly to a read-write overlay
152 static int copy(const char *path
)
165 /* already writeable */
168 if (fstatat(rd
, path
, &st
, 0) < 0)
172 from
= openat(rd
, path
, O_RDONLY
, st
.st_mode
);
177 if (!clone_path(rd
, wr
, path
))
181 to
= openat(wr
, path
, O_WRONLY
| O_CREAT
, st
.st_mode
);
187 while ((len
= read(from
, buf
, sizeof(buf
))) > 0)
189 if (write(to
, buf
, len
) < len
)
206 * FUSE getattr method
208 static int cowfs_getattr(const char *path
, struct stat
*stbuf
)
212 if (fstatat(get_rd(path
), path
, stbuf
, AT_SYMLINK_NOFOLLOW
) < 0)
222 static int cowfs_access(const char *path
, int mask
)
226 if (faccessat(get_rd(path
), path
, mask
, 0) < 0)
234 * FUSE readlink method
236 static int cowfs_readlink(const char *path
, char *buf
, size_t size
)
242 res
= readlinkat(get_rd(path
), path
, buf
, size
- 1);
252 * get a directory stream of two concatenated paths
254 static DIR* get_dir(char *dir
, const char *subdir
)
263 full
= alloca(strlen(dir
) + strlen(subdir
) + 1);
265 strcat(full
, subdir
);
267 return opendir(full
);
271 * check if a directory stream contains a directory
273 static bool contains_dir(DIR *d
, char *dirname
)
280 while ((ent
= readdir(d
)))
282 if (streq(ent
->d_name
, dirname
))
292 * FUSE readdir method
294 static int cowfs_readdir(const char *path
, void *buf
, fuse_fill_dir_t filler
,
295 off_t offset
, struct fuse_file_info
*fi
)
297 private_cowfs_t
*this = get_this();
302 memset(&st
, 0, sizeof(st
));
304 d1
= get_dir(this->master
, path
);
305 d2
= get_dir(this->host
, path
);
306 d3
= get_dir(this->scen
, path
);
310 while ((ent
= readdir(d1
)))
312 if (!contains_dir(d2
, ent
->d_name
) &&
313 !contains_dir(d3
, ent
->d_name
))
315 st
.st_ino
= ent
->d_ino
;
316 st
.st_mode
= ent
->d_type
<< 12;
317 filler(buf
, ent
->d_name
, &st
, 0);
325 while ((ent
= readdir(d2
)))
327 if (!contains_dir(d3
, ent
->d_name
))
329 st
.st_ino
= ent
->d_ino
;
330 st
.st_mode
= ent
->d_type
<< 12;
331 filler(buf
, ent
->d_name
, &st
, 0);
339 while ((ent
= readdir(d3
)))
341 st
.st_ino
= ent
->d_ino
;
342 st
.st_mode
= ent
->d_type
<< 12;
343 filler(buf
, ent
->d_name
, &st
, 0);
353 static int cowfs_mknod(const char *path
, mode_t mode
, dev_t rdev
)
359 if (!clone_path(get_rd(path
), fd
, path
))
364 if (mknodat(fd
, path
, mode
, rdev
) < 0)
374 static int cowfs_mkdir(const char *path
, mode_t mode
)
380 if (!clone_path(get_rd(path
), fd
, path
))
384 if (mkdirat(fd
, path
, mode
) < 0)
394 static int cowfs_unlink(const char *path
)
398 /* TODO: whiteout master */
399 if (unlinkat(get_wr(path
), path
, 0) < 0)
409 static int cowfs_rmdir(const char *path
)
413 /* TODO: whiteout master */
414 if (unlinkat(get_wr(path
), path
, AT_REMOVEDIR
) < 0)
422 * FUSE symlink method
424 static int cowfs_symlink(const char *from
, const char *to
)
428 /* TODO: relative from? */
429 if (symlinkat(from
, get_wr(to
), to
) < 0)
439 static int cowfs_rename(const char *from
, const char *to
)
442 private_cowfs_t
*this = get_this();
448 if (fd
== this->master_fd
)
457 if (renameat(fd
, from
, get_wr(to
), to
) < 0)
467 static int cowfs_link(const char *from
, const char *to
)
472 if (linkat(get_rd(from
), from
, get_wr(to
), to
, 0) < 0)
482 static int cowfs_chmod(const char *path
, mode_t mode
)
486 private_cowfs_t
*this = get_this();
490 if (fd
== this->master_fd
)
492 if (fstatat(fd
, path
, &st
, 0) < 0)
496 if (st
.st_mode
== mode
)
506 if (fchmodat(fd
, path
, mode
, 0) < 0)
516 static int cowfs_chown(const char *path
, uid_t uid
, gid_t gid
)
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_uid
== uid
&& st
.st_gid
== gid
)
540 if (fchownat(fd
, path
, uid
, gid
, AT_SYMLINK_NOFOLLOW
) < 0)
548 * FUSE truncate method
550 static int cowfs_truncate(const char *path
, off_t size
)
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_size
== size
)
574 fd
= openat(fd
, path
, O_WRONLY
);
579 if (ftruncate(fd
, size
) < 0)
589 * FUSE utimens method
591 static int cowfs_utimens(const char *path
, const struct timespec ts
[2])
593 struct timeval tv
[2];
595 private_cowfs_t
*this = get_this();
599 if (fd
== this->master_fd
)
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;
613 if (futimesat(fd
, path
, tv
) < 0)
623 static int cowfs_open(const char *path
, struct fuse_file_info
*fi
)
630 fd
= openat(fd
, path
, fi
->flags
);
642 static int cowfs_read(const char *path
, char *buf
, size_t size
, off_t offset
,
643 struct fuse_file_info
*fi
)
651 file
= openat(fd
, path
, O_RDONLY
);
656 res
= pread(file
, buf
, size
, offset
);
668 static int cowfs_write(const char *path
, const char *buf
, size_t size
,
669 off_t offset
, struct fuse_file_info
*fi
)
671 private_cowfs_t
*this = get_this();
677 if (fd
== this->master_fd
)
685 file
= openat(fd
, path
, O_WRONLY
);
690 res
= pwrite(file
, buf
, size
, offset
);
702 static int cowfs_statfs(const char *path
, struct statvfs
*stbuf
)
704 private_cowfs_t
*this = get_this();
708 if (this->scen_fd
> 0)
713 if (fstatvfs(fd
, stbuf
) < 0)
724 static void *cowfs_init(struct fuse_conn_info
*conn
)
726 struct fuse_context
*ctx
;
728 ctx
= fuse_get_context();
730 return ctx
->private_data
;
734 * FUSE method vectors
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
,
748 .chmod
= cowfs_chmod
,
749 .chown
= cowfs_chown
,
750 .truncate
= cowfs_truncate
,
751 .utimens
= cowfs_utimens
,
754 .write
= cowfs_write
,
755 .statfs
= cowfs_statfs
,
760 * Implementation of cowfs_t.set_scenario.
762 static void set_scenario(private_cowfs_t
*this, char *path
)
768 if (this->scen_fd
> 0)
770 close(this->scen_fd
);
772 this->scen
= strdup(path
);
773 this->scen_fd
= open(path
, O_RDONLY
| O_DIRECTORY
);
777 * stop, umount and destroy a cowfs FUSE filesystem
779 static void destroy(private_cowfs_t
*this)
781 fuse_exit(this->fuse
);
782 fuse_unmount(this->mount
, this->chan
);
783 pthread_join(this->thread
, NULL
);
784 fuse_destroy(this->fuse
);
789 close(this->master_fd
);
790 close(this->host_fd
);
791 if (this->scen_fd
> 0)
793 close(this->scen_fd
);
799 * creates a new cowfs fuse instance
801 cowfs_t
*cowfs_create(char *master
, char *host
, char *mount
)
803 struct fuse_args args
= {0, NULL
, 0};
804 private_cowfs_t
*this = malloc_thing(private_cowfs_t
);
806 this->public.set_scenario
= (void(*)(cowfs_t
*, char *path
))set_scenario
;
807 this->public.destroy
= (void(*)(cowfs_t
*))destroy
;
809 this->master_fd
= open(master
, O_RDONLY
| O_DIRECTORY
);
810 if (this->master_fd
< 0)
812 DBG1("failed to open master filesystem '%s'", master
);
815 this->host_fd
= open(host
, O_RDONLY
| O_DIRECTORY
);
816 if (this->master_fd
< 0)
818 DBG1("failed to open host filesystem '%s'", host
);
819 close(this->master_fd
);
824 this->chan
= fuse_mount(mount
, &args
);
825 if (this->chan
== NULL
)
827 DBG1("mounting cowfs FUSE on '%s' failed", mount
);
828 close(this->master_fd
);
829 close(this->host_fd
);
834 this->fuse
= fuse_new(this->chan
, &args
, &cowfs_operations
,
835 sizeof(cowfs_operations
), this);
836 if (this->fuse
== NULL
)
838 DBG1("creating cowfs FUSE handle failed");
839 close(this->master_fd
);
840 close(this->host_fd
);
841 fuse_unmount(mount
, this->chan
);
846 this->mount
= strdup(mount
);
847 this->master
= strdup(master
);
848 this->host
= strdup(host
);
851 if (pthread_create(&this->thread
, NULL
, (void*)fuse_loop_mt
, this->fuse
) != 0)
853 DBG1("creating thread to handle FUSE failed");
854 fuse_unmount(mount
, this->chan
);
858 close(this->master_fd
);
859 close(this->host_fd
);
864 return &this->public;