#define FUSE_USE_VERSION 26
static const char * modiffsVersion = "2010.12.24";
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/xattr.h>
#include <dirent.h>
#include <unistd.h>
#include <fuse.h>
#include <time.h>
#define STACK_SIZE 250
typedef struct stack_s {
char * items[STACK_SIZE];
int size;
} stack;
int stack_exist(stack * s, char * elem) {
int i = 0;
for (i=0; i < s->size; i++) {
if (strcmp(elem, s->items[i]) == 0) {
return 1;
}
}
return 0;
}
void stack_push(stack * s, char * elem) {
s->items[s->size++] = elem;
}
int dir_depth(const char * path) {
int depth = 0;
char * temp = strchr(path, '/');
while (temp != NULL) {
depth++;
temp = strchr(temp + 1, '/');
}
return depth;
}
char * full_path(char * upath, char * d_name) {
char * ret = (char *) malloc(sizeof(char)*(strlen(upath) + strlen(d_name) + 1));
strcpy(ret, upath);
strcat(ret, d_name);
return ret;
}
// Global to store our read-write path
char * rw_path;
// Translate an modiffs path into it's underlying filesystem path
static char * translate_path(const char * path) {
char * rPath= malloc(sizeof(char)*(strlen(path)+strlen(rw_path)+1));
strcpy(rPath, rw_path);
if (rPath[strlen(rPath)-1]=='/') {
rPath[strlen(rPath)-1]='\0';
}
int depth = dir_depth(path);
if (strcmp("/", path) == 0) {
strcat(rPath, "/");
} else if (depth < 5) {
strcat(rPath, "/");
} else {
int i = 0;
char * temp = strchr(path, '/');
while (i < (depth-1)) {
i++;
temp = strchr(temp + 1, '/');
}
strcat(rPath, "/");
strcat(rPath, temp);
}
return rPath;
}
/*
* level:
* 0: year
* 1: month
* 2: day
* 3: hour
*/
char * last_attr(const char * path, int level) {
int depth = 0;
char * cont = (char *) malloc(sizeof(char)*strlen(path));
strcpy(cont, path);
char * temp = strtok(cont, "/");
while (depth < level && temp != NULL) {
depth++;
temp = strtok(NULL, "/");
}
return temp;
}
/******************************
*
* Callbacks for FUSE
*
******************************/
static int modiffs_getattr(const char * path, struct stat * stbuf) {
int res = 0;
int depth = dir_depth(path);
memset(stbuf, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 1;
stbuf->st_ctime = time(NULL);
stbuf->st_atime = time(NULL);
stbuf->st_mtime = time(NULL);
stbuf->st_size = 4096;
} else if (depth < 5) {
stbuf->st_mode = S_IFDIR | 0444;
stbuf->st_nlink = 1;
stbuf->st_ctime = time(NULL);
stbuf->st_atime = time(NULL);
stbuf->st_mtime = time(NULL);
stbuf->st_size = 4096;
} else {
char * upath = translate_path(path);
res = lstat(upath, stbuf);
free(upath);
if(res == -1) {
return -errno;
}
}
return res;
}
static int modiffs_readlink(const char *path, char *buf, size_t size)
{
int res;
char * upath = translate_path(path);
res = readlink(upath, buf, size - 1);
free(upath);
if(res == -1) {
return -errno;
}
buf[res] = '\0';
return 0;
}
static int modiffs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
DIR * dp;
struct dirent * de;
int res;
struct tm *ts;
(void) offset;
(void) fi;
int depth;
char * upath = translate_path(path);
dp = opendir(upath);
if(dp == NULL) {
res = -errno;
return res;
}
filler(buf, ".", NULL, 0);
filler(buf, "..", NULL, 0);
if (strcmp("/", path) == 0) {
stack years;
years.size = 0;
while((de = readdir(dp)) != NULL) {
struct stat st_modif;
memset(&st_modif, 0, sizeof(st_modif));
char * year = (char *) malloc(sizeof(char) * 5);
stat(full_path(upath, de->d_name), &st_modif);
ts = localtime(&(st_modif.st_mtime));
strftime(year, 255, "%Y", ts);
if (stack_exist(&years, year) == 0) {
stack_push(&years, year);
filler(buf, year, NULL, 0);
}
}
} else {
depth = dir_depth(path);
if (depth == 1) {
stack months;
months.size = 0;
while((de = readdir(dp)) != NULL) {
struct stat st_modif;
memset(&st_modif, 0, sizeof(st_modif));
char * year = (char *) malloc(sizeof(char)*5);
char * month = (char *) malloc(sizeof(char)*3);
stat(full_path(upath, de->d_name), &st_modif);
ts = localtime(&(st_modif.st_mtime));
strftime(year, 255, "%Y", ts);
strftime(month, 255, "%m", ts);
if (strcmp(last_attr(path,0), year) == 0) {
if (stack_exist(&months, month) == 0) {
filler(buf, month, NULL, 0);
stack_push(&months, month);
}
}
}
} else if (depth == 2) {
stack days;
days.size = 0;
while((de = readdir(dp)) != NULL) {
struct stat st_modif;
memset(&st_modif, 0, sizeof(st_modif));
char * year = (char *) malloc(sizeof(char)*5);
char * month = (char *) malloc(sizeof(char)*3);
char * day = (char *) malloc(sizeof(char)*3);
stat(full_path(upath, de->d_name), &st_modif);
ts = localtime(&(st_modif.st_mtime));
strftime(year, 255, "%Y", ts);
strftime(month, 255, "%m", ts);
strftime(day, 255, "%d", ts);
if (strcmp(last_attr(path,0), year) == 0 &&
strcmp(last_attr(path,1), month) == 0) {
if (stack_exist(&days, day) == 0) {
stack_push(&days, day);
filler(buf, day, NULL, 0);
}
}
}
} else if (depth == 3) {
stack hours;
hours.size = 0;
while((de = readdir(dp)) != NULL) {
struct stat st_modif;
memset(&st_modif, 0, sizeof(st_modif));
char * year = (char *) malloc(sizeof(char)*5);
char * month = (char *) malloc(sizeof(char)*3);
char * day = (char *) malloc(sizeof(char)*3);
char * hour = (char *) malloc(sizeof(char)*3);
stat(full_path(upath, de->d_name), &st_modif);
ts = localtime(&(st_modif.st_mtime));
strftime(year, 255, "%Y", ts);
strftime(month, 255, "%m", ts);
strftime(day, 255, "%d", ts);
strftime(hour, 255, "%H", ts);
if (strcmp(last_attr(path,0), year) == 0 &&
strcmp(last_attr(path,1), month) == 0 &&
strcmp(last_attr(path,2), day) == 0) {
if (stack_exist(&hours, hour) == 0) {
stack_push(&hours, hour);
filler(buf, hour, NULL, 0);
}
}
}
} else {
while((de = readdir(dp)) != NULL) {
if (strcmp(".", de->d_name) == 0 || strcmp("..", de->d_name) == 0) {
continue;
}
struct stat st_modif;
memset(&st_modif, 0, sizeof(st_modif));
char * year = (char *) malloc(sizeof(char)*5);
char * month = (char *) malloc(sizeof(char)*3);
char * day = (char *) malloc(sizeof(char)*3);
char * hour = (char *) malloc(sizeof(char)*3);
stat(full_path(upath, de->d_name), &st_modif);
ts = localtime(&(st_modif.st_mtime));
strftime(year, 255, "%Y", ts);
strftime(month, 255, "%m", ts);
strftime(day, 255, "%d", ts);
strftime(hour, 255, "%H", ts);
if (strcmp(last_attr(path,0), year) == 0 &&
strcmp(last_attr(path,1), month) == 0 &&
strcmp(last_attr(path,2), day) == 0 &&
strcmp(last_attr(path,3), hour) == 0) {
struct stat st;
memset(&st, 0, sizeof(st));
st.st_ino = de->d_ino;
st.st_mode = de->d_type << 12;
if (filler(buf, de->d_name, &st, 0)) {
break;
}
}
}
}
}
free(upath);
closedir(dp);
return 0;
}
static int modiffs_mknod(const char *path, mode_t mode, dev_t rdev)
{
(void)path;
(void)mode;
(void)rdev;
return -EROFS;
}
static int modiffs_mkdir(const char *path, mode_t mode)
{
(void)path;
(void)mode;
return -EROFS;
}
static int modiffs_unlink(const char *path)
{
(void)path;
return -EROFS;
}
static int modiffs_rmdir(const char *path)
{
(void)path;
return -EROFS;
}
static int modiffs_symlink(const char *from, const char *to)
{
(void)from;
(void)to;
return -EROFS;
}
static int modiffs_rename(const char *from, const char *to)
{
(void)from;
(void)to;
return -EROFS;
}
static int modiffs_link(const char *from, const char *to)
{
(void)from;
(void)to;
return -EROFS;
}
static int modiffs_chmod(const char *path, mode_t mode)
{
(void)path;
(void)mode;
return -EROFS;
}
static int modiffs_chown(const char *path, uid_t uid, gid_t gid)
{
(void)path;
(void)uid;
(void)gid;
return -EROFS;
}
static int modiffs_truncate(const char *path, off_t size)
{
(void)path;
(void)size;
return -EROFS;
}
static int modiffs_utime(const char *path, struct utimbuf *buf)
{
(void)path;
(void)buf;
return -EROFS;
}
static int modiffs_open(const char *path, struct fuse_file_info *finfo)
{
int res;
int flags = finfo->flags;
if ((flags & O_WRONLY) || (flags & O_RDWR) || (flags & O_CREAT) || (flags & O_EXCL) || (flags & O_TRUNC) || (flags & O_APPEND)) {
return -EROFS;
}
char * upath = translate_path(path);
res = open(upath, flags);
free(upath);
if(res == -1) {
return -errno;
}
close(res);
return 0;
}
static int modiffs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *finfo)
{
int fd;
int res;
(void)finfo;
char * upath = translate_path(path);
fd = open(upath, O_RDONLY);
free(upath);
if(fd == -1) {
res = -errno;
return res;
}
res = pread(fd, buf, size, offset);
if(res == -1) {
res = -errno;
}
close(fd);
return 0;
}
static int modiffs_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *finfo)
{
(void)path;
(void)buf;
(void)size;
(void)offset;
(void)finfo;
return -EROFS;
}
static int modiffs_statfs(const char *path, struct statvfs *st_buf)
{
int res;
char * upath = translate_path(path);
res = statvfs(upath, st_buf);
free(upath);
if (res == -1) {
return -errno;
}
return 0;
}
static int modiffs_release(const char *path, struct fuse_file_info *finfo)
{
(void) path;
(void) finfo;
return 0;
}
static int modiffs_fsync(const char *path, int crap, struct fuse_file_info *finfo)
{
(void) path;
(void) crap;
(void) finfo;
return 0;
}
static int modiffs_access(const char *path, int mode)
{
int res;
char *upath = translate_path(path);
/* Don't pretend that we allow writing
* Chris AtLee <chris@atlee.ca>
*/
if (mode & W_OK)
return -EROFS;
res = access(upath, mode);
free(upath);
if (res == -1) {
return -errno;
}
return res;
}
struct fuse_operations modiffs_oper = {
.getattr = modiffs_getattr,
.readlink = modiffs_readlink,
.readdir = modiffs_readdir,
.mknod = modiffs_mknod,
.mkdir = modiffs_mkdir,
.symlink = modiffs_symlink,
.unlink = modiffs_unlink,
.rmdir = modiffs_rmdir,
.rename = modiffs_rename,
.link = modiffs_link,
.chmod = modiffs_chmod,
.chown = modiffs_chown,
.truncate = modiffs_truncate,
.utime = modiffs_utime,
.open = modiffs_open,
.read = modiffs_read,
.write = modiffs_write,
.statfs = modiffs_statfs,
.release = modiffs_release,
.fsync = modiffs_fsync,
.access = modiffs_access
};
enum {
KEY_HELP,
KEY_VERSION,
};
static void usage(const char* progname)
{
fprintf(stdout,
"usage: %s readwritepath mountpoint [options]\n"
"\n"
" Mounts readwritepath as a read-only mount at mountpoint\n"
"\n"
"general options:\n"
" -o opt,[opt...] mount options\n"
" -h --help print help\n"
" -V --version print version\n"
"\n", progname);
}
static int modiffs_parse_opt(void *data, const char *arg, int key,
struct fuse_args *outargs)
{
(void) data;
switch (key)
{
case FUSE_OPT_KEY_NONOPT:
if (rw_path == 0)
{
rw_path = strdup(arg);
return 0;
}
else
{
return 1;
}
case FUSE_OPT_KEY_OPT:
return 1;
case KEY_HELP:
usage(outargs->argv[0]);
exit(0);
case KEY_VERSION:
fprintf(stdout, "ROFS version %s\n", modiffsVersion);
exit(0);
default:
fprintf(stderr, "see `%s -h' for usage\n", outargs->argv[0]);
exit(1);
}
return 1;
}
static struct fuse_opt modiffs_opts[] = {
FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP),
FUSE_OPT_KEY("-V", KEY_VERSION),
FUSE_OPT_KEY("--version", KEY_VERSION),
FUSE_OPT_END
};
int main(int argc, char *argv[])
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
int res;
res = fuse_opt_parse(&args, &rw_path, modiffs_opts, modiffs_parse_opt);
if (res != 0)
{
fprintf(stderr, "Invalid arguments\n");
fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
exit(1);
}
if (rw_path == 0)
{
fprintf(stderr, "Missing readwritepath\n");
fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
exit(1);
}
fuse_main(args.argc, args.argv, &modiffs_oper, NULL);
return 0;
}