616 lines
14 KiB
C
616 lines
14 KiB
C
/*
|
|
* Copyright © 2007 Alistair Crooks. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote
|
|
* products derived from this software without specific prior written
|
|
* permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <ctype.h>
|
|
#include <dirent.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <fuse.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "virtdir.h"
|
|
#include "defs.h"
|
|
|
|
#ifndef PREFIX
|
|
#define PREFIX ""
|
|
#endif
|
|
|
|
#ifndef DEF_CONF_FILE
|
|
#define DEF_CONF_FILE "/etc/icfs.conf"
|
|
#endif
|
|
|
|
DEFINE_ARRAY(strv_t, char *);
|
|
|
|
static struct stat vfs; /* stat info of directory */
|
|
static virtdir_t tree; /* virtual directory tree */
|
|
static int verbose; /* how chatty are we? */
|
|
|
|
|
|
|
|
|
|
|
|
/********************************************************************/
|
|
|
|
/* convert a string to lower case */
|
|
static char *
|
|
strtolower(const char *path, char *name, size_t size)
|
|
{
|
|
const char *cp;
|
|
char *np;
|
|
|
|
for (cp = path, np = name ; (int)(np - name) < size ; np++, cp++) {
|
|
*np = tolower((unsigned)*cp);
|
|
}
|
|
return name;
|
|
}
|
|
|
|
/* add a name and its lower case entry */
|
|
static void
|
|
add_entry(virtdir_t *tp, const char *name, uint8_t type)
|
|
{
|
|
char icname[MAXPATHLEN];
|
|
char *root;
|
|
int len;
|
|
|
|
root = virtdir_rootdir(&tree);
|
|
len = strlen(root);
|
|
strtolower(&name[len], icname, sizeof(icname));
|
|
virtdir_add(tp, &name[len], strlen(name) - len, type, icname,
|
|
strlen(icname));
|
|
}
|
|
|
|
/* file system operations start here */
|
|
|
|
/* perform the stat operation */
|
|
static int
|
|
icfs_getattr(const char *path, struct stat *st)
|
|
{
|
|
virt_dirent_t *ep;
|
|
char name[MAXPATHLEN];
|
|
|
|
(void) memset(st, 0x0, sizeof(*st));
|
|
if (strcmp(path, "/") == 0) {
|
|
st->st_mode = S_IFDIR | 0755;
|
|
st->st_nlink = 2;
|
|
return 0;
|
|
}
|
|
if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
|
|
if (stat(name, st) < 0) {
|
|
return -errno;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* readdir operation */
|
|
static int
|
|
icfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
|
off_t offset, struct fuse_file_info *fi)
|
|
{
|
|
virt_dirent_t *ep;
|
|
VIRTDIR *dirp;
|
|
char name[MAXPATHLEN];
|
|
|
|
(void) fi;
|
|
|
|
if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
|
|
if ((dirp = openvirtdir(&tree, ep->name)) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
filler(buf, ".", NULL, 0);
|
|
filler(buf, "..", NULL, 0);
|
|
while ((ep = readvirtdir(dirp)) != NULL) {
|
|
strtolower(ep->d_name, name, sizeof(name));
|
|
(void) filler(buf, name, NULL, 0);
|
|
}
|
|
(void) closevirtdir(dirp);
|
|
return 0;
|
|
}
|
|
|
|
/* open the file in the file system */
|
|
static int
|
|
icfs_open(const char *path, struct fuse_file_info *fi)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* read the file's contents in the file system */
|
|
static int
|
|
icfs_read(const char *path, char *buf, size_t size, off_t offset,
|
|
struct fuse_file_info * fi)
|
|
{
|
|
virt_dirent_t *ep;
|
|
char name[MAXPATHLEN];
|
|
int fd;
|
|
int cc;
|
|
|
|
(void) fi;
|
|
|
|
if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
|
|
if ((fd = open(name, O_RDONLY, 0666)) < 0) {
|
|
return -ENOENT;
|
|
}
|
|
if (lseek(fd, offset, SEEK_SET) < 0) {
|
|
(void) close(fd);
|
|
return -EBADF;
|
|
}
|
|
if ((cc = read(fd, buf, size)) < 0) {
|
|
(void) close(fd);
|
|
return -errno;
|
|
}
|
|
(void) close(fd);
|
|
return cc;
|
|
}
|
|
|
|
/* write the file's contents in the file system */
|
|
static int
|
|
icfs_write(const char *path, const char *buf, size_t size, off_t offset,
|
|
struct fuse_file_info * fi)
|
|
{
|
|
virt_dirent_t *ep;
|
|
char name[MAXPATHLEN];
|
|
int fd;
|
|
int cc;
|
|
|
|
(void) fi;
|
|
|
|
if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
|
|
if ((fd = open(name, O_WRONLY, 0666)) < 0) {
|
|
return -ENOENT;
|
|
}
|
|
if (lseek(fd, offset, SEEK_SET) < 0) {
|
|
(void) close(fd);
|
|
return -EBADF;
|
|
}
|
|
if ((cc = write(fd, buf, size)) < 0) {
|
|
(void) close(fd);
|
|
return -errno;
|
|
}
|
|
(void) close(fd);
|
|
return cc;
|
|
}
|
|
|
|
/* fill in the statvfs struct */
|
|
static int
|
|
icfs_statfs(const char *path, struct statvfs *st)
|
|
{
|
|
(void) memset(st, 0x0, sizeof(*st));
|
|
st->f_bsize = st->f_frsize = st->f_iosize = 512;
|
|
st->f_owner = vfs.st_uid;
|
|
st->f_files = 1;
|
|
return 0;
|
|
}
|
|
|
|
/* "remove" a file */
|
|
static int
|
|
icfs_unlink(const char *path)
|
|
{
|
|
virt_dirent_t *ep;
|
|
char name[MAXPATHLEN];
|
|
|
|
if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
|
|
if (unlink(name) < 0) {
|
|
return -errno;
|
|
}
|
|
/* XXX - delete entry */
|
|
return 0;
|
|
}
|
|
|
|
/* check the access on a file */
|
|
static int
|
|
icfs_access(const char *path, int acc)
|
|
{
|
|
virt_dirent_t *ep;
|
|
char name[MAXPATHLEN];
|
|
|
|
if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
|
|
if (access(name, acc) < 0) {
|
|
return -errno;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* change the mode of a file */
|
|
static int
|
|
icfs_chmod(const char *path, mode_t mode)
|
|
{
|
|
virt_dirent_t *ep;
|
|
char name[MAXPATHLEN];
|
|
|
|
if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
|
|
if (chmod(name, mode) < 0) {
|
|
return -errno;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* change the owner and group of a file */
|
|
static int
|
|
icfs_chown(const char *path, uid_t uid, gid_t gid)
|
|
{
|
|
virt_dirent_t *ep;
|
|
char name[MAXPATHLEN];
|
|
|
|
if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
|
|
if (lchown(name, uid, gid) < 0) {
|
|
return -errno;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* "rename" a file */
|
|
static int
|
|
icfs_rename(const char *from, const char *to)
|
|
{
|
|
#if 0
|
|
char fromname[MAXPATHLEN];
|
|
char toname[MAXPATHLEN];
|
|
|
|
virt_dirent_t *ep;
|
|
|
|
if ((ep = virtdir_find_tgt(&tree, from, strlen(from))) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
(void) snprintf(toname, sizeof(toname), "%s%s", dirs.v[0], to);
|
|
if (!mkdirs(toname)) {
|
|
return -ENOENT;
|
|
}
|
|
if (rename(fromname, toname) < 0) {
|
|
return -EPERM;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/* create a file */
|
|
static int
|
|
icfs_create(const char *path, mode_t mode, struct fuse_file_info *fi)
|
|
{
|
|
virt_dirent_t *ep;
|
|
char *slash;
|
|
char name[MAXPATHLEN];
|
|
int fd;
|
|
|
|
if ((slash = strrchr(path, '/')) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
if ((ep = virtdir_find_tgt(&tree, path, (int)(slash - path) - 1)) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s%s", virtdir_rootdir(&tree), ep->name, slash);
|
|
if ((fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) {
|
|
return -EPERM;
|
|
}
|
|
(void) close(fd);
|
|
add_entry(&tree, name, 'f');
|
|
return 0;
|
|
}
|
|
|
|
/* create a special node */
|
|
static int
|
|
icfs_mknod(const char *path, mode_t mode, dev_t d)
|
|
{
|
|
virt_dirent_t *ep;
|
|
char *slash;
|
|
char name[MAXPATHLEN];
|
|
|
|
if ((slash = strrchr(path, '/')) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
if ((ep = virtdir_find_tgt(&tree, path, (int)(slash - path) - 1)) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s/%s", virtdir_rootdir(&tree), ep->name, slash + 1);
|
|
if (mknod(name, mode, d) < 0) {
|
|
return -EPERM;
|
|
}
|
|
add_entry(&tree, name, ((mode & S_IFMT) == S_IFCHR) ? 'c' : 'b');
|
|
return 0;
|
|
}
|
|
|
|
/* create a directory */
|
|
static int
|
|
icfs_mkdir(const char *path, mode_t mode)
|
|
{
|
|
virt_dirent_t *ep;
|
|
char *slash;
|
|
char name[MAXPATHLEN];
|
|
|
|
if ((slash = strrchr(path, '/')) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
if ((ep = virtdir_find_tgt(&tree, path, (int)(slash - path) - 1)) == NULL) {
|
|
return -EEXIST;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s/%s", virtdir_rootdir(&tree), ep->name, slash + 1);
|
|
if (mkdir(name, mode) < 0) {
|
|
return -EPERM;
|
|
}
|
|
add_entry(&tree, name, 'd');
|
|
return 0;
|
|
}
|
|
|
|
/* create a symbolic link */
|
|
static int
|
|
icfs_symlink(const char *path, const char *tgt)
|
|
{
|
|
virt_dirent_t *ep;
|
|
char *slash;
|
|
char name[MAXPATHLEN];
|
|
|
|
if ((slash = strrchr(path, '/')) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
if ((ep = virtdir_find_tgt(&tree, path, (int)(slash - path) - 1)) == NULL) {
|
|
return -EEXIST;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s/%s", virtdir_rootdir(&tree), ep->name, slash + 1);
|
|
if (symlink(name, tgt) < 0) {
|
|
return -EPERM;
|
|
}
|
|
add_entry(&tree, name, 'l');
|
|
return 0;
|
|
}
|
|
|
|
/* create a link */
|
|
static int
|
|
icfs_link(const char *path, const char *tgt)
|
|
{
|
|
virt_dirent_t *ep;
|
|
char *slash;
|
|
char name[MAXPATHLEN];
|
|
|
|
if ((slash = strrchr(path, '/')) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
if ((ep = virtdir_find_tgt(&tree, path, (int)(slash - path) - 1)) == NULL) {
|
|
return -EEXIST;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s/%s", virtdir_rootdir(&tree), ep->name, slash + 1);
|
|
if (link(name, tgt) < 0) {
|
|
return -errno;
|
|
}
|
|
add_entry(&tree, name, 'f'); /* XXX */
|
|
return 0;
|
|
}
|
|
|
|
/* read the contents of a symbolic link */
|
|
static int
|
|
icfs_readlink(const char *path, char *buf, size_t size)
|
|
{
|
|
virt_dirent_t *ep;
|
|
char name[MAXPATHLEN];
|
|
|
|
if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
|
|
if (readlink(name, buf, size) < 0) {
|
|
return -errno;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* remove a directory */
|
|
static int
|
|
icfs_rmdir(const char *path)
|
|
{
|
|
virt_dirent_t *ep;
|
|
char name[MAXPATHLEN];
|
|
|
|
if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
|
|
if (rmdir(name) < 0) {
|
|
return -errno;
|
|
}
|
|
/* XXX - delete entry */
|
|
return 0;
|
|
}
|
|
|
|
/* truncate a file */
|
|
static int
|
|
icfs_truncate(const char *path, off_t size)
|
|
{
|
|
virt_dirent_t *ep;
|
|
char name[MAXPATHLEN];
|
|
|
|
if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
|
|
if (truncate(name, size) < 0) {
|
|
return -errno;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* set utimes on a file */
|
|
static int
|
|
icfs_utime(const char *path, struct utimbuf *t)
|
|
{
|
|
virt_dirent_t *ep;
|
|
char name[MAXPATHLEN];
|
|
|
|
if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
|
|
return -ENOENT;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
|
|
if (utime(name, t) < 0) {
|
|
return -errno;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* operations struct */
|
|
static struct fuse_operations icfs_oper = {
|
|
.getattr = icfs_getattr,
|
|
.readlink = icfs_readlink,
|
|
.mknod = icfs_mknod,
|
|
.mkdir = icfs_mkdir,
|
|
.unlink = icfs_unlink,
|
|
.rmdir = icfs_rmdir,
|
|
.symlink = icfs_symlink,
|
|
.rename = icfs_rename,
|
|
.link = icfs_link,
|
|
.chmod = icfs_chmod,
|
|
.chown = icfs_chown,
|
|
.truncate = icfs_truncate,
|
|
.utime = icfs_utime,
|
|
.open = icfs_open,
|
|
.read = icfs_read,
|
|
.write = icfs_write,
|
|
.statfs = icfs_statfs,
|
|
.readdir = icfs_readdir,
|
|
.access = icfs_access,
|
|
.create = icfs_create
|
|
};
|
|
|
|
/* build up a virtdir from the information in the file system */
|
|
static int
|
|
dodir(virtdir_t *tp, char *rootdir, const char *subdir)
|
|
{
|
|
struct dirent *dp;
|
|
struct stat st;
|
|
struct stat dir;
|
|
struct stat f;
|
|
struct stat l;
|
|
char icname[MAXPATHLEN];
|
|
char name[MAXPATHLEN];
|
|
char type;
|
|
DIR *dirp;
|
|
int len;
|
|
|
|
if (tp->v == NULL) {
|
|
(void) stat(".", &dir);
|
|
(void) memcpy(&f, &dir, sizeof(f));
|
|
f.st_mode = S_IFREG | 0644;
|
|
(void) memcpy(&l, &f, sizeof(l));
|
|
l.st_mode = S_IFLNK | 0755;
|
|
virtdir_init(tp, rootdir, &dir, &f, &l);
|
|
virtdir_add(tp, "/", 1, 'd', "/", 1);
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s/%s", rootdir, subdir);
|
|
if ((dirp = opendir(name)) == NULL) {
|
|
warn("dodir: can't opendir `%s'", name);
|
|
return 0;
|
|
}
|
|
len = strlen(tp->rootdir);
|
|
while ((dp = readdir(dirp)) != NULL) {
|
|
if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
|
|
continue;
|
|
}
|
|
(void) snprintf(name, sizeof(name), "%s%s%s%s%s", rootdir, (subdir[0] == 0x0) ? "" : "/", subdir, "/", dp->d_name);
|
|
if (stat(name, &st) < 0) {
|
|
warnx("can't stat `%s'", name);
|
|
continue;
|
|
}
|
|
switch (st.st_mode & S_IFMT) {
|
|
case S_IFDIR:
|
|
type = 'd';
|
|
break;
|
|
case S_IFREG:
|
|
type = 'f';
|
|
break;
|
|
case S_IFLNK:
|
|
type = 'l';
|
|
break;
|
|
case S_IFBLK:
|
|
type = 'b';
|
|
break;
|
|
case S_IFCHR:
|
|
type = 'c';
|
|
break;
|
|
default:
|
|
type = '?';
|
|
break;
|
|
}
|
|
if (!virtdir_find(tp, &name[len], strlen(name) - len)) {
|
|
strtolower(&name[len], icname, sizeof(icname));
|
|
virtdir_add(tp, &name[len], strlen(name) - len, type, icname,
|
|
strlen(icname));
|
|
}
|
|
if (type == 'd') {
|
|
dodir(tp, rootdir, &name[len + 1]);
|
|
}
|
|
}
|
|
(void) closedir(dirp);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int i;
|
|
|
|
while ((i = getopt(argc, argv, "f:v")) != -1) {
|
|
switch(i) {
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
}
|
|
}
|
|
#if 0
|
|
(void) daemon(1, 1);
|
|
#endif
|
|
dodir(&tree, argv[optind], "");
|
|
return fuse_main(argc, argv, &icfs_oper, NULL);
|
|
}
|