view src/fs/nomadfs.c @ 1276:6dd652e45be5

fs: remove debug hexdumping of file data Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Sun, 18 Dec 2022 09:54:35 -0500
parents 90d2af779270
children ea51bd7cb9f5
line wrap: on
line source

/*
 * Copyright (c) 2016-2020,2022 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#define FUSE_USE_VERSION 31

#include <fuse_lowlevel.h>

#include <jeffpc/error.h>
#include <jeffpc/sock.h>

#include <nomad/rpc.h>
#include <nomad/fscall.h>

#define REPLY_TIMEOUT	1.0
#define ATTR_TIMEOUT	1.0
#define ENTRY_TIMEOUT	1.0

/*
 * Since we use <allocator host id, uniq> as the inode number, we must make
 * sure that the inode number type is large enough to hold the two pieces.
 */
STATIC_ASSERT(sizeof(fuse_ino_t) >= (sizeof(uint32_t) * 2));

/*
 * fuse assumes that the root inode number is FUSE_ROOT_ID.  Since that's
 * not guaranteed, we swap the root object's inode number with whatever
 * object happens to be using FUSE_ROOT_ID for its inode number.
 */
static fuse_ino_t root_ino_buddy;
static struct fscall_state state;

static inline uint64_t ino_fuse2nomad(fuse_ino_t ino)
{
	if (ino == FUSE_ROOT_ID)
		return root_ino_buddy;
	else if (ino == root_ino_buddy)
		return FUSE_ROOT_ID;
	else
		return ino;
}

static inline fuse_ino_t ino_nomad2fuse(uint64_t ino)
{
	if (ino == FUSE_ROOT_ID)
		return root_ino_buddy;
	else if (ino == root_ino_buddy)
		return FUSE_ROOT_ID;
	else
		return ino;
}

/*
 * Helpers to wrap fscall into more high-level operations
 */
static int __getattr(const struct noid *oid, uint64_t ino, struct nattr *nattr)
{
	uint32_t ohandle;
	int ret;

	ret = fscall_open(&state, oid, ino, &ohandle);
	if (ret)
		return ret;

	ret = fscall_getattr(&state, ohandle, nattr);
	if (ret)
		goto err_close;

	return fscall_close(&state, ohandle);

err_close:
	fscall_close(&state, ohandle);

	return ret;
}

static int __create(fuse_req_t req, fuse_ino_t parent, const char *name,
		    mode_t mode, dev_t dev, struct noid *child_oid)
{
	const struct fuse_ctx *fuse_ctx;
	uint32_t dir_ohandle;
	int ret;

	fuse_ctx = fuse_req_ctx(req);
	if (!fuse_ctx)
		return -EPERM; /* TODO: is there a better errno? */

	ret = fscall_open(&state, NULL, ino_fuse2nomad(parent), &dir_ohandle);
	if (ret)
		return ret;

	STATIC_ASSERT(sizeof(dev_t) <= sizeof(uint64_t));

	/* dev needs to be zero for everything except char/block devices */
	if (!(S_ISBLK(mode) || S_ISCHR(mode)))
		dev = 0;

	ret = fscall_create(&state, dir_ohandle, name, fuse_ctx->uid,
			    fuse_ctx->gid, mode_to_nmode(mode), dev, child_oid);
	if (ret)
		goto err;

	return fscall_close(&state, dir_ohandle);

err:
	fscall_close(&state, dir_ohandle);

	return ret;
}

/*
 * Fuse operations
 */
static void nomadfs_getattr(fuse_req_t req, fuse_ino_t ino,
			    struct fuse_file_info *fi)
{
	struct nattr nattr;
	struct stat statbuf;
	int ret;

	ret = __getattr(NULL, ino_fuse2nomad(ino), &nattr);
	if (ret)
		goto err;

	nattr_to_stat(&nattr, &statbuf);
	statbuf.st_ino = ino_nomad2fuse(nattr.ino);

	/* XXX: somehow, we're returning good data, but gets interpreted wrong */
	/* XXX: seems to be a 32-bit. vs. 64-bit problem */
	fuse_reply_attr(req, &statbuf, REPLY_TIMEOUT);
	return;

err:
	fuse_reply_err(req, -nerr_to_errno(ret));
}

static void nomadfs_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
			    int to_set, struct fuse_file_info *fi)
{
	struct stat statbuf;
	struct nattr nattr;
	uint32_t ohandle;
	int ret;

	/*
	 * If we're not setting the mode (and therefore didn't get one), we
	 * need to make a fake but valid mode for the benefit of stat_to_nattr
	 */
	if (!(to_set & FUSE_SET_ATTR_MODE))
		attr->st_mode = S_IFREG;

	stat_to_nattr(attr, &nattr);

	ret = fscall_open(&state, NULL, ino_fuse2nomad(ino), &ohandle);
	if (ret)
		goto err;

	ret = fscall_setattr(&state, ohandle, &nattr,
			     (to_set & FUSE_SET_ATTR_MODE) ? true : false,
			     (to_set & FUSE_SET_ATTR_SIZE) ? true : false,
			     false, /* FIXME: atime vs. atime_now */
			     false, /* btime */
			     false, /* ctime */
			     false, /* FIXME: mtime vs. mtime_now */
			     (to_set & FUSE_SET_ATTR_UID) ? true : false,
			     (to_set & FUSE_SET_ATTR_GID) ? true : false);
	if (ret)
		goto err_close;

	ret = fscall_close(&state, ohandle);
	if (ret)
		goto err;

	nattr_to_stat(&nattr, &statbuf);
	statbuf.st_ino = ino_nomad2fuse(nattr.ino);

	fuse_reply_attr(req, &statbuf, REPLY_TIMEOUT);

	return;

err_close:
	fscall_close(&state, ohandle);

err:
	fuse_reply_err(req, -nerr_to_errno(ret));
}

static void nomadfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{
	struct fuse_entry_param e;
	uint32_t child_ohandle;
	uint32_t dir_ohandle;
	struct noid child_oid;
	struct nattr nattr;
	int ret;

	if (parent == FUSE_ROOT_ID) {
		dir_ohandle = state.root_ohandle;
	} else {
		ret = fscall_open(&state, NULL, ino_fuse2nomad(parent),
				  &dir_ohandle);
		if (ret)
			goto err;
	}

	ret = fscall_lookup(&state, dir_ohandle, name, &child_oid);
	if (ret)
		goto err_close_dir;

	ret = fscall_open(&state, &child_oid, 0, &child_ohandle);
	if (ret)
		goto err_close_dir;

	ret = fscall_getattr(&state, child_ohandle, &nattr);
	if (ret)
		goto err_close_child;

	ret = fscall_close(&state, child_ohandle);
	if (ret)
		goto err_close_dir;

	if (parent != FUSE_ROOT_ID)
		fscall_close(&state, dir_ohandle);

	memset(&e, 0, sizeof(e));
	nattr_to_stat(&nattr, &e.attr);
	e.ino = e.attr.st_ino = ino_nomad2fuse(nattr.ino);
	e.attr_timeout = ATTR_TIMEOUT;
	e.entry_timeout = ENTRY_TIMEOUT;

	fuse_reply_entry(req, &e);

	return;

err_close_child:
	fscall_close(&state, child_ohandle);

err_close_dir:
	if (parent != FUSE_ROOT_ID)
		fscall_close(&state, dir_ohandle);

err:
	fuse_reply_err(req, -nerr_to_errno(ret));
}

static void nomadfs_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
			  mode_t mode, dev_t dev)
{
	struct fuse_entry_param e;
	struct noid child_oid;
	struct nattr nattr;
	int ret;

	ret = __create(req, parent, name, mode, dev, &child_oid);
	if (ret)
		goto err;

	ret = __getattr(&child_oid, 0, &nattr);
	if (ret)
		goto err;

	memset(&e, 0, sizeof(e));
	nattr_to_stat(&nattr, &e.attr);
	e.ino = e.attr.st_ino = ino_nomad2fuse(nattr.ino);
	e.attr_timeout = ATTR_TIMEOUT;
	e.entry_timeout = ENTRY_TIMEOUT;

	fuse_reply_entry(req, &e);

	return;

err:
	fuse_reply_err(req, -nerr_to_errno(ret));
}

static void nomadfs_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
			  mode_t mode)
{
	nomadfs_mknod(req, parent, name, mode, 0);
}

static void __unlink(fuse_req_t req, fuse_ino_t parent, const char *name,
		     bool rmdir)
{
	uint32_t dir_ohandle;
	int ret;

	ret = fscall_open(&state, NULL, ino_fuse2nomad(parent), &dir_ohandle);
	if (ret)
		goto err;

	ret = fscall_unlink(&state, dir_ohandle, name, NULL, rmdir);
	if (ret)
		goto err_close;

	ret = fscall_close(&state, dir_ohandle);
	if (ret)
		goto err;

	fuse_reply_err(req, 0);

	return;

err_close:
	fscall_close(&state, dir_ohandle);

err:
	fuse_reply_err(req, -nerr_to_errno(ret));
}

static void nomadfs_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
{
	__unlink(req, parent, name, false);
}

static void nomadfs_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
{
	__unlink(req, parent, name, true);
}

static int reply_slice(fuse_req_t req, void *buf, size_t bufsize,
		       off_t fuse_off, size_t fuse_size)
{
	if (fuse_off < bufsize)
		return fuse_reply_buf(req, buf + fuse_off,
				      (bufsize - fuse_off < fuse_size) ?
				      (bufsize - fuse_off) : fuse_size);
	else
		return fuse_reply_buf(req, NULL, 0);
}

static int dirent_add(fuse_req_t req, void **_buf, size_t *_bufsize,
		      const char *name, const struct noid *oid,
		      uint64_t ino)
{
	const size_t oldbufsize = *_bufsize;
	size_t bufsize = *_bufsize;
	struct stat statbuf;
	void *buf = *_buf;
	void *tmp;

	bufsize += fuse_add_direntry(req, NULL, 0, name, NULL, 0);

	tmp = realloc(buf, bufsize);
	if (!tmp)
		return -ENOMEM;
	*_buf = buf = tmp;
	*_bufsize = bufsize;

	memset(&statbuf, 0, sizeof(statbuf));
	statbuf.st_ino = ino_nomad2fuse(ino);
	fuse_add_direntry(req, buf + oldbufsize, bufsize - oldbufsize, name,
			  &statbuf, bufsize);

	return 0;
}

static void nomadfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t fuse_size,
			    off_t fuse_off, struct fuse_file_info *fi)
{
	uint32_t ohandle = fi->fh;
	uint64_t off;
	size_t bufsize;
	void *buf;
	int ret;

	bufsize = 0;
	buf = NULL;

	off = 0;

	do {
		struct ndirent dirent;

		ret = fscall_getdent(&state, ohandle, off, &dirent);
		if (ret && ret != NERR_ENOENT)
			goto err;
		if (ret == NERR_ENOENT)
			break;

		ASSERT3U(dirent.type, !=, NDIRENT_TYPE_GRAFT);

		ret = dirent_add(req, &buf, &bufsize, dirent.name,
				 &dirent.oid, dirent.ino);
		if (ret) {
			fuse_reply_err(req, -ret);
			return;
		}

		off = dirent.dir_offset + 1;
	} while (!ret);

	reply_slice(req, buf, bufsize, fuse_off, fuse_size);

	free(buf);

	return;

err:
	fuse_reply_err(req, -nerr_to_errno(ret));
}

static void nomadfs_create(fuse_req_t req, fuse_ino_t parent, const char *name,
			   mode_t mode, struct fuse_file_info *fi)
{
	struct fuse_entry_param e;
	struct noid child_oid;
	struct nattr nattr;
	uint32_t ohandle;
	int ret;

	ret = __create(req, parent, name, mode, 0, &child_oid);
	if (ret)
		goto err;

	ret = fscall_open(&state, &child_oid, 0, &ohandle);
	if (ret)
		goto err;

	ret = fscall_getattr(&state, ohandle, &nattr);
	if (ret)
		goto err_close;

	fi->fh = ohandle;

	memset(&e, 0, sizeof(e));
	nattr_to_stat(&nattr, &e.attr);
	e.ino = e.attr.st_ino = ino_nomad2fuse(nattr.ino);
	e.attr_timeout = ATTR_TIMEOUT;
	e.entry_timeout = ENTRY_TIMEOUT;

	fuse_reply_create(req, &e, fi);

	return;

err_close:
	fscall_close(&state, ohandle);

err:
	fuse_reply_err(req, -nerr_to_errno(ret));
}

static void nomadfs_open(fuse_req_t req, fuse_ino_t ino,
			 struct fuse_file_info *fi)
{
	uint32_t ohandle;
	int ret;

	ret = fscall_open(&state, NULL, ino_fuse2nomad(ino), &ohandle);
	if (ret)
		goto err;

	fi->fh = ohandle;

	fuse_reply_open(req, fi);

	return;

err:
	fuse_reply_err(req, -nerr_to_errno(ret));
}

static void nomadfs_release(fuse_req_t req, fuse_ino_t ino,
			    struct fuse_file_info *fi)
{
	uint32_t ohandle = fi->fh;
	int ret;

	ret = fscall_close(&state, ohandle);
	if (ret)
		goto err;

	fuse_reply_err(req, 0);
	return;

err:
	fuse_reply_err(req, -nerr_to_errno(ret));
}

static void nomadfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
			 off_t off, struct fuse_file_info *fi)
{
	uint32_t ohandle = fi->fh;
	char *buf;
	int ret;

	buf = alloca(size);

	ret = fscall_read(&state, ohandle, buf, size, off);
	if (ret)
		goto err;

	fuse_reply_buf(req, buf, size);

	return;

err:
	fuse_reply_err(req, -nerr_to_errno(ret));
}

static void nomadfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
			  size_t size, off_t off, struct fuse_file_info *fi)
{
	uint32_t ohandle = fi->fh;
	int ret;

	ret = fscall_write(&state, ohandle, buf, size, off);
	if (ret)
		fuse_reply_err(req, -nerr_to_errno(ret));
	else
		fuse_reply_write(req, size);
}

static struct fuse_lowlevel_ops nomad_ops = {
	.getattr	= nomadfs_getattr,
	.setattr	= nomadfs_setattr,
	.lookup		= nomadfs_lookup,
	.mknod		= nomadfs_mknod,
	.mkdir		= nomadfs_mkdir,
	.unlink		= nomadfs_unlink,
	.rmdir		= nomadfs_rmdir,
	.readdir	= nomadfs_readdir,
	.create		= nomadfs_create,
	.open		= nomadfs_open,
	.release	= nomadfs_release,
	.opendir	= nomadfs_open,
	.releasedir	= nomadfs_release,
	.read		= nomadfs_read,
	.write		= nomadfs_write,
};

int main(int argc, char *argv[])
{
	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
	struct fuse_session *se;
	struct fuse_chan *ch;
	struct nattr root_attrs;
	const char *volstr;
	struct xuuid tmp;
	char *mountpoint;
	int ret;
	int fd;

	volstr = getenv("VOL");
	if (!volstr)
		panic("missing VOL env var");

	xuuid_parse(&tmp, volstr);

	fd = connect_ip("localhost", 2323, true, true, IP_TCP);
	if (fd < 0)
		panic("failed to connect to nomad-client: %s", xstrerror(fd));

	ret = fscall_connect(&state, fd);
	if (ret)
		panic("failed RPC handshake with nomad-client: %s",
		      xstrerror(ret));

	ret = fscall_mount(&state, &tmp);
	if (ret)
		panic("failed to mount volume: %s", xstrerror(ret));

	ret = __getattr(&state.root_oid, 0, &root_attrs);
	if (ret)
		panic("failed to getattr volume root: %s", xstrerror(ret));

	/* see comment by root_ino_buddy definition */
	root_ino_buddy = root_attrs.ino;

	ret = 1;

	if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) == -1)
		goto err;

	if ((ch = fuse_mount(mountpoint, &args)) == NULL)
		goto err;

	se = fuse_lowlevel_new(&args, &nomad_ops, sizeof(nomad_ops), NULL);
	if (!se)
		goto err_unmount;

	if (fuse_set_signal_handlers(se) == -1)
		goto err_session;

	fuse_session_add_chan(se, ch);
	ret = fuse_session_loop(se);
	fuse_remove_signal_handlers(se);
	fuse_session_remove_chan(ch);

err_session:
	fuse_session_destroy(se);

err_unmount:
	fuse_unmount(mountpoint, ch);

err:
	fuse_opt_free_args(&args);

	return ret ? 1 : 0;
}