[Tux3] Tux3 fuse port

Tero Roponen tero.roponen at gmail.com
Sun Sep 7 04:27:13 PDT 2008



On Sat, 6 Sep 2008, Daniel Phillips wrote:

> Conrad, I would like to hand this back to you and go back to working
> on the guts of tux3.  The biggest issue is, you can't just use the
> path that fuse hands you as a filename.  At least it has to be broken
> into a directory path and a filename.  But that is not the way we
> should do it here, notice that fuse has fuse_file_info->fh where we
> can store the tux3 inode in the fuse open, then ignore the filename
> in all other operations except for debug traces.  Exceptions to this
> are unlink and rename, where the only choice I can see is to split
> the fuse name into path and file.  I am not sure why fuse does not do
> that for us.

Hi,

this is my attempt to port the tux3 fuse implementation to lowlevel ops.
I can't guarantee that it works, because this is just a quick weekend hack
to get familiar with tux3. I hope somebody finds this useful, but
unfortunately I don't have time to maintain this myself.

---

/*
 * FUSE-Tux3: Mount tux3 in userspace.
 * Copyright (C) 2008 Conrad Meyer <konrad at tylerc.org>
 * Large portions completely stolen from Daniel Phillip's tux3.c.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * Compile: gcc -std=gnu99 buffer.c diskio.c fuse-tux3.c \
 *              -D_FILE_OFFSET_BITS=64 -lfuse -o fuse-tux3
 * (-D_FILE_OFFSET_BITS=64 might be only on 64 bit platforms, not sure.)
 * Run:
 * 0. sudo mknod -m 666 /dev/fuse c 10 229
 *    Install libfuse and headers: sudo apt-get install libfuse-dev
 *    Install fuse-utils: sudo apt-get install fuse-utils
 *    build fuse kernel module: cd linux && make ;-)
 *    insert fuse kernel module: sudo insmod fs/fuse/fuse.ko
 * 1. Create a tux3 fs on __fuse__tux3fs using some combination of dd
 *    and ./tux3 make __fuse__tux3fs.
 * 2. Mount on foo/ like: ./tux3fuse -f foo/     (-f for foreground)
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <sys/types.h>
#include "trace.h"
#include "tux3.h"
#include "buffer.h"
#include "diskio.h"

#define FUSE_USE_VERSION 27
#include <fuse.h>
#include <fuse/fuse_lowlevel.h>
#include <fuse/fuse_lowlevel_compat.h>

#define include_inode_c
#include "inode.c"

static fd_t fd;
static u64 volsize;
static struct sb *sb;
static struct dev *dev;

static struct inode *ino2inode(fuse_ino_t ino)
{
	if (ino == 1)
		return sb->rootdir;
	return (struct inode *)ino;
}

static void tux3_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{
	struct inode *inode = tuxopen(ino2inode(parent), name, strlen(name));

	if (inode)
	{
		struct fuse_entry_param ep = {
			.attr.st_mode  = inode->i_mode,
			.attr.st_atime = inode->i_atime,
			.attr.st_mtime = inode->i_mtime,
			.attr.st_ctime = inode->i_ctime,
			.attr.st_size  = inode->i_size,
			.attr.st_uid   = inode->i_uid,
			.attr.st_gid   = inode->i_gid,
			.attr.st_nlink = inode->i_links,

			.ino = (fuse_ino_t)inode,
			.generation = 1,
			.attr_timeout = 1.0,
			.entry_timeout = 1.0,
		};

		fuse_reply_entry(req, &ep);
	} else {
		fuse_reply_err(req, ENOENT);
	}
}

static void tux3_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
	printf("---- open file ----\n");
	printf("flags: %i\n", fi->flags);
	fi->flags |= 0666;
	fuse_reply_open(req, fi);
}

static void tux3_read(fuse_req_t req, fuse_ino_t ino, size_t size,
	off_t offset, struct fuse_file_info *fi)
{
	printf("---- read file ----\n");
	struct inode *inode = ino2inode(ino);
	struct file *file = &(struct file){ .f_inode = inode };

	printf("userspace tries to seek to %Li\n", (L)offset);
	if (offset >= inode->i_size)
	{
		printf("EOF!\n");
		fuse_reply_err(req, EINVAL);
		return;
	}
	tuxseek(file, offset);

	char buf[size];
	int read = tuxread(file, buf, size);
	if (read < 0)
	{
		errno = -read;
		goto eek;
	}

	if (offset + read > inode->i_size)
	{
		fuse_reply_err(req, EINVAL);
		return;
	}

	fuse_reply_buf(req, buf, read);
	return;

eek:
	fprintf(stderr, "Eek! %s\n", strerror(errno));
	fuse_reply_err(req, errno);
}

static void tux3_create(fuse_req_t req, fuse_ino_t parent, const char *name,
	mode_t mode, struct fuse_file_info *fi)
{
	printf("---- create file ----\n");
	struct inode *inode = tuxcreate(ino2inode(parent), name, strlen(name),
		&(struct iattr){ .mode = mode | 0666 });
	if (inode)
	{
		struct fuse_entry_param fep = {
			.attr.st_mode  = inode->i_mode,
			.attr.st_atime = inode->i_atime,
			.attr.st_mtime = inode->i_mtime,
			.attr.st_ctime = inode->i_ctime,
			.attr.st_size  = inode->i_size,
			.attr.st_uid   = inode->i_uid,
			.attr.st_gid   = inode->i_gid,
			.attr.st_nlink = inode->i_links,

			.ino = (fuse_ino_t)inode,
			.generation = 1,
			.attr_timeout = 1.0,
			.entry_timeout = 1.0,
		};

		dump_attrs(inode);

		fuse_reply_create(req, &fep, fi);
	} else {
		fuse_reply_err(req, ENOMEM);
	}
}

static void tux3_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
{
	printf("---- create directory ----\n");
	struct inode *inode = tuxcreate(ino2inode(parent), name, strlen(name),
		&(struct iattr){ .mode = mode | 0666 });
	if (inode)
	{
		struct fuse_entry_param fep = {
			.ino = (fuse_ino_t)inode,
			.generation = 1,
			.attr.st_mode = mode | 0666,
			.attr_timeout = 1.0,
			.entry_timeout = 1.0,
		};

		fuse_reply_entry(req, &fep);
	} else {
		fuse_reply_err(req, ENOMEM);
	}
}

static void tux3_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
	size_t size, off_t offset, struct fuse_file_info *fi)
{
	struct inode *inode = ino2inode(ino);
	struct file *file = &(struct file){ .f_inode = inode };

	if (offset) {
		u64 seek = offset;
		printf("seek to %Li\n", (L)seek);
		tuxseek(file, seek);
	}

	int written = 0;
	if ((written = tuxwrite(file, buf, size)) < 0)
	{
		errno = -written;
		goto eek;
	}

	tuxsync(inode);
	if ((errno = -sync_super(sb)))
		goto eek;

	fuse_reply_write(req, written);
	return;
eek:
	fprintf(stderr, "Eek! %s\n", strerror(errno));
	fuse_reply_err(req, errno);
}

static void _tux3_getattr(struct inode *inode, struct stat *st)
{
	st->st_mode  = inode->i_mode;
	st->st_atime = inode->i_atime;
	st->st_mtime = inode->i_mtime;
	st->st_ctime = inode->i_ctime;
	st->st_size  = inode->i_size;
	st->st_uid   = inode->i_uid;
	st->st_gid   = inode->i_gid;
	st->st_nlink = inode->i_links;
}

static void tux3_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
	struct stat st;
	_tux3_getattr(ino2inode(ino), &st);
	fuse_reply_attr(req, &st, 0.0);
}

struct fillstate { char *dirent; int done; };

int tux3_filler(void *info, char *name, unsigned namelen, loff_t offset, unsigned inode, unsigned type)
{
	struct fillstate *state = info;
	if (state->done || namelen > EXT2_NAME_LEN)
		return -EINVAL;
	printf("'%.*s'\n", namelen, name);
	memcpy(state->dirent, name, namelen);
	(state->dirent)[namelen] = 0;
	state->done = 1;
	return 0;
}

/* FIXME: this needs to be implemented properly. */
static void tux3_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset,
	struct fuse_file_info *fi)
{
	struct inode *inode = ino2inode(ino);
	struct file *dirfile = &(struct file){ .f_inode = inode, .f_pos = offset };
	char dirent[EXT2_NAME_LEN + 1];
	char buf[1024]; //XXX

	while (dirfile->f_pos < dirfile->f_inode->i_size) {
		if ((errno = -ext2_readdir(dirfile, &(struct fillstate){ .dirent = dirent }, tux3_filler)))
		{
			fuse_reply_err(req, errno);
			return;
		}

		struct stat st;
		struct inode *inode2 = tuxopen(inode, dirent, strlen(dirent));
		if (!inode2)
			continue;

		_tux3_getattr(inode2, &st);

		int len = fuse_dirent_size(strlen(dirent));
		fuse_add_direntry(req, buf, sizeof(buf), dirent, &st, dirfile->f_pos);
		fuse_reply_buf(req, buf, len);
		return;
	}

	fuse_reply_buf(req, NULL, 0);
}

static void tux3_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
{
	printf("---- delete file ----\n");
	struct buffer *buffer;
	ext2_dirent *entry = ext2_find_entry(ino2inode(parent), name, strlen(name), &buffer);
	if (!entry)
		goto noent;
	struct inode inode = { .sb = sb, .inum = entry->inum };

	if ((errno = -open_inode(&inode)))
		goto eek;
	if ((errno = -tree_chop(&inode.btree, &(struct delete_info){ .key = 0 }, -1)))
		goto eek;
	if ((errno = -ext2_delete_entry(buffer, entry)))
		goto eek;

	fuse_reply_err(req, 0);
	return;
noent:
	errno = ENOENT;
eek:
	fprintf(stderr, "Eek! %s\n", strerror(errno));
	fuse_reply_err(req, errno);
}

static void tux3_init(void *data, struct fuse_conn_info *conn)
{
	const char *volname = data;
	if (!(fd = open(volname, O_RDWR, S_IRWXU)))
		error("volume %s not found", volname);

	volsize = 0;
	if (fdsize64(fd, &volsize))
		error("fdsize64 failed for '%s' (%s)", volname, strerror(errno));
	dev = malloc(sizeof(*dev));
	*dev = (struct dev){ fd, .bits = 12 };
	init_buffers(dev, 1<<20);
	sb = malloc(sizeof(*sb));
	*sb = (struct sb){
		.max_inodes_per_block = 64,
		.entries_per_node = 20,
		.devmap = new_map(dev, NULL),
		.blockbits = dev->bits,
		.blocksize = 1 << dev->bits,
		.blockmask = (1 << dev->bits) - 1,
		.volblocks = volsize >> dev->bits,
		.freeblocks = volsize >> dev->bits,
		.itable = (struct btree){ .sb = sb, .ops = &itable_ops,
			.entries_per_leaf = 1 << (dev->bits - 6) } };

	if ((errno = -load_sb(sb)))
		goto eek;
	if (!(sb->bitmap = new_inode(sb, 0)))
		goto eek;
	if (!(sb->rootdir = new_inode(sb, 0xd)))
		goto eek;
	if ((errno = -open_inode(sb->bitmap)))
		goto eek;
	if ((errno = -open_inode(sb->rootdir)))
		goto eek;
	return;
eek:
	fprintf(stderr, "Eek! %s\n", strerror(errno));
	exit(1);
}

/* Stub methods */
static void tux3_destroy(void *userdata)
{
}

static void tux3_forget(fuse_req_t req, fuse_ino_t ino, unsigned long nlookup)
{
	fuse_reply_none(req);
}

static void tux3_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
	int to_set, struct fuse_file_info *fi)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_readlink(fuse_req_t req, fuse_ino_t ino)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
	mode_t mode, dev_t rdev)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_link(fuse_req_t req, fuse_ino_t ino,
	fuse_ino_t newparent, const char *newname)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_symlink(fuse_req_t req, const char *link,
	fuse_ino_t parent, const char *name)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_rename(fuse_req_t req, fuse_ino_t parent,
	const char *name, fuse_ino_t newparent, const char *newname)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_statfs(fuse_req_t req, fuse_ino_t ino)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_access(fuse_req_t req, fuse_ino_t ino, int mask)
{
	/* Allow all accesses, for now */
	fuse_reply_err(req, 0);
}

static void tux3_opendir(fuse_req_t req, fuse_ino_t ino,
	struct fuse_file_info *fi)
{
	fuse_reply_open(req, fi); /* Success */
}

static void tux3_releasedir(fuse_req_t req, fuse_ino_t ino,
	struct fuse_file_info *fi)
{
	fuse_reply_err(req, 0); /* Success */
}

static void tux3_fsyncdir(fuse_req_t req, fuse_ino_t ino,
	int datasync, struct fuse_file_info *fi)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
	struct fuse_file_info *fi)
{
	fuse_reply_err(req, ENOSYS);
}
static void tux3_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
	const char *value, size_t size, int flags)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_getlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
	struct flock *lock)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_setlk(fuse_req_t req, fuse_ino_t ino,
	struct fuse_file_info *fi, struct flock *lock, int sleep)
{
	fuse_reply_err(req, ENOSYS);
}

static void tux3_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize, uint64_t idx)
{
	fuse_reply_err(req, ENOSYS);
}

static struct fuse_lowlevel_ops tux3_ops = {
	.init = tux3_init,
	.destroy = tux3_destroy,
	.lookup = tux3_lookup,
	.forget = tux3_forget,
	.getattr = tux3_getattr,
	.setattr = tux3_setattr,
	.readlink = tux3_readlink,
	.mknod = tux3_mknod,
	.mkdir = tux3_mkdir,
	.rmdir = tux3_rmdir,
	.link = tux3_link,
	.symlink = tux3_symlink,
	.unlink = tux3_unlink,
	.rename = tux3_rename,
	.create = tux3_create,
	.open = tux3_open,
	.read = tux3_read,
	.write = tux3_write,
	.statfs = tux3_statfs,
	.access = tux3_access,
	.opendir = tux3_opendir,
	.readdir = tux3_readdir,
	.releasedir = tux3_releasedir,
	.fsyncdir = tux3_fsyncdir,
	.flush = tux3_flush,
	.release = tux3_release,
	.fsync = tux3_fsync,
	.setxattr = tux3_setxattr,
	.getxattr = tux3_getxattr,
	.listxattr = tux3_listxattr,
	.removexattr = tux3_removexattr,
	.getlk = tux3_getlk,
	.setlk = tux3_setlk,
	.bmap = tux3_bmap,
};

int main(int argc, char *argv[])
{
	struct fuse_args args = FUSE_ARGS_INIT(argc-1, argv+1);

	char *mountpoint;
	int foreground;
	int err = -1;

	if (argc < 3)
		error("usage: %s <volname> <mountpoint>", argv[0]);

	if (fuse_parse_cmdline(&args, &mountpoint, NULL, &foreground) != -1)
	{
		int fd = open(mountpoint, O_RDONLY);
		struct fuse_chan *fc = fuse_mount(mountpoint, &args);
		if (fc)
		{
			struct fuse_session *fs = fuse_lowlevel_new(&args,
				&tux3_ops,
				sizeof(tux3_ops),
				argv[1]);

			if (fs)
			{
				if (fuse_set_signal_handlers(fs) != -1)
				{
					fuse_session_add_chan(fs, fc);
					fuse_daemonize(foreground);
					fchdir(fd);
					close(fd);
					err = fuse_session_loop(fs);
					fuse_remove_signal_handlers(fs);
					fuse_session_remove_chan(fc);
				}

				fuse_session_destroy(fs);
			}

			fuse_unmount(mountpoint, fc);
		}
	}

	fuse_opt_free_args(&args);
	return err ? 1 : 0;
}

_______________________________________________
Tux3 mailing list
Tux3 at tux3.org
http://tux3.org/cgi-bin/mailman/listinfo/tux3



More information about the Tux3 mailing list