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 /** overlay filesystem path */
60 /** fd of read only master filesystem */
62 /** copy on write overlay to master */
64 /** optional 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->over_fd
> 0 && faccessat(this->over_fd
, path
, F_OK
, 0) == 0)
102 return this->over_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->over_fd
> 0)
119 return this->over_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->over
, 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
)
474 if (renameat(fd
, from
, get_wr(to
), to
) < 0)
484 static int cowfs_link(const char *from
, const char *to
)
494 if (!clone_path(rd
, wr
, to
))
496 DBG1("cloning path '%s' failed", to
);
499 if (linkat(rd
, from
, wr
, to
, 0) < 0)
501 DBG1("linking '%s' to '%s' failed", from
, to
);
510 static int cowfs_chmod(const char *path
, mode_t mode
)
517 if (fstatat(fd
, path
, &st
, 0) < 0)
521 if (st
.st_mode
== mode
)
530 if (fchmodat(fd
, path
, mode
, 0) < 0)
540 static int cowfs_chown(const char *path
, uid_t uid
, gid_t gid
)
547 if (fstatat(fd
, path
, &st
, 0) < 0)
551 if (st
.st_uid
== uid
&& st
.st_gid
== gid
)
560 if (fchownat(fd
, path
, uid
, gid
, AT_SYMLINK_NOFOLLOW
) < 0)
568 * FUSE truncate method
570 static int cowfs_truncate(const char *path
, off_t size
)
577 if (fstatat(fd
, path
, &st
, 0) < 0)
581 if (st
.st_size
== size
)
590 fd
= openat(fd
, path
, O_WRONLY
);
595 if (ftruncate(fd
, size
) < 0)
605 * FUSE utimens method
607 static int cowfs_utimens(const char *path
, const struct timespec ts
[2])
609 struct timeval tv
[2];
619 tv
[0].tv_sec
= ts
[0].tv_sec
;
620 tv
[0].tv_usec
= ts
[0].tv_nsec
/ 1000;
621 tv
[1].tv_sec
= ts
[1].tv_sec
;
622 tv
[1].tv_usec
= ts
[1].tv_nsec
/ 1000;
624 if (futimesat(fd
, path
, tv
) < 0)
634 static int cowfs_open(const char *path
, struct fuse_file_info
*fi
)
641 fd
= openat(fd
, path
, fi
->flags
);
653 static int cowfs_read(const char *path
, char *buf
, size_t size
, off_t offset
,
654 struct fuse_file_info
*fi
)
662 file
= openat(fd
, path
, O_RDONLY
);
668 res
= pread(file
, buf
, size
, offset
);
680 static int cowfs_write(const char *path
, const char *buf
, size_t size
,
681 off_t offset
, struct fuse_file_info
*fi
)
692 file
= openat(fd
, path
, O_WRONLY
);
697 res
= pwrite(file
, buf
, size
, offset
);
709 static int cowfs_statfs(const char *path
, struct statvfs
*stbuf
)
714 if (fstatvfs(fd
, stbuf
) < 0)
725 static void *cowfs_init(struct fuse_conn_info
*conn
)
727 struct fuse_context
*ctx
;
729 ctx
= fuse_get_context();
731 return ctx
->private_data
;
735 * FUSE method vectors
737 static struct fuse_operations cowfs_operations
= {
738 .getattr
= cowfs_getattr
,
739 .access
= cowfs_access
,
740 .readlink
= cowfs_readlink
,
741 .readdir
= cowfs_readdir
,
742 .mknod
= cowfs_mknod
,
743 .mkdir
= cowfs_mkdir
,
744 .symlink
= cowfs_symlink
,
745 .unlink
= cowfs_unlink
,
746 .rmdir
= cowfs_rmdir
,
747 .rename
= cowfs_rename
,
749 .chmod
= cowfs_chmod
,
750 .chown
= cowfs_chown
,
751 .truncate
= cowfs_truncate
,
752 .utimens
= cowfs_utimens
,
755 .write
= cowfs_write
,
756 .statfs
= cowfs_statfs
,
761 * Implementation of cowfs_t.set_overlay.
763 static bool set_overlay(private_cowfs_t
*this, char *path
)
770 if (this->over_fd
> 0)
772 close(this->over_fd
);
777 this->over_fd
= open(path
, O_RDONLY
| O_DIRECTORY
);
778 if (this->over_fd
< 0)
780 DBG1("failed to open overlay directory '%s': %m", path
);
783 this->over
= strdup(path
);
789 * stop, umount and destroy a cowfs FUSE filesystem
791 static void destroy(private_cowfs_t
*this)
793 fuse_exit(this->fuse
);
794 fuse_unmount(this->mount
, this->chan
);
795 pthread_join(this->thread
, NULL
);
796 fuse_destroy(this->fuse
);
801 close(this->master_fd
);
802 close(this->host_fd
);
803 if (this->over_fd
> 0)
805 close(this->over_fd
);
811 * creates a new cowfs fuse instance
813 cowfs_t
*cowfs_create(char *master
, char *host
, char *mount
)
815 struct fuse_args args
= {0, NULL
, 0};
816 private_cowfs_t
*this = malloc_thing(private_cowfs_t
);
818 this->public.set_overlay
= (bool(*)(cowfs_t
*, char *path
))set_overlay
;
819 this->public.destroy
= (void(*)(cowfs_t
*))destroy
;
821 this->master_fd
= open(master
, O_RDONLY
| O_DIRECTORY
);
822 if (this->master_fd
< 0)
824 DBG1("failed to open master filesystem '%s'", master
);
828 this->host_fd
= open(host
, O_RDONLY
| O_DIRECTORY
);
829 if (this->host_fd
< 0)
831 DBG1("failed to open host filesystem '%s'", host
);
832 close(this->master_fd
);
838 this->chan
= fuse_mount(mount
, &args
);
839 if (this->chan
== NULL
)
841 DBG1("mounting cowfs FUSE on '%s' failed", mount
);
842 close(this->master_fd
);
843 close(this->host_fd
);
848 this->fuse
= fuse_new(this->chan
, &args
, &cowfs_operations
,
849 sizeof(cowfs_operations
), this);
850 if (this->fuse
== NULL
)
852 DBG1("creating cowfs FUSE handle failed");
853 close(this->master_fd
);
854 close(this->host_fd
);
855 fuse_unmount(mount
, this->chan
);
860 this->mount
= strdup(mount
);
861 this->master
= strdup(master
);
862 this->host
= strdup(host
);
865 if (pthread_create(&this->thread
, NULL
, (void*)fuse_loop
, this->fuse
) != 0)
867 DBG1("creating thread to handle FUSE failed");
868 fuse_unmount(mount
, this->chan
);
872 close(this->master_fd
);
873 close(this->host_fd
);
878 return &this->public;