view src/common/fscall.c @ 875:e5b305913b1c

common: enforce max read & write size in fscall_{read,write} The RPC structures have a size limitation and these checks exist to avoid silent integer truncation. In the future, we could replace these with loops to invoke the RPC as many times as is necessary to read/write the entire requested length. Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Sun, 18 Dec 2022 09:45:56 -0500
parents eca18abb5295
children ea51bd7cb9f5
line wrap: on
line source

/*
 * Copyright (c) 2016-2020,2022 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
 * Copyright (c) 2016 Steve Dougherty
 *
 * 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.
 */

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

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

static int __fscall_req(int fd, uint32_t opcode,
			int (*xmit)(XDR *, void *),
			void *req)
{
	struct rpc_header_req header;
	struct buffer rpc_buffer;
	char _rpc_buffer[1024];
	XDR xdr;
	int ret;

	header.opcode = opcode;

	buffer_init_static(&rpc_buffer, _rpc_buffer, 0, sizeof(_rpc_buffer),
			   true);

	xdrfd_create(&xdr, fd, &rpc_buffer, XDR_ENCODE);

	ret = NERR_RPC_ERROR;

	/* send generic RPC header */
	if (!xdr_rpc_header_req(&xdr, &header))
		goto err;

	if (xmit && !xmit(&xdr, req))
		goto err;

	ret = 0;

err:
	xdr_destroy(&xdr);

	return ret;
}

static int __fscall_res(int fd,
			int (*xmit)(XDR *, void *),
			void *res, size_t ressize)
{
	struct rpc_header_res header;
	XDR xdr;
	int ret;

	if (res)
		memset(res, 0, ressize);

	xdrfd_create(&xdr, fd, NULL, XDR_DECODE);

	ret = NERR_RPC_ERROR;

	if (!xdr_rpc_header_res(&xdr, &header))
		goto err;

	ret = header.err;
	if (ret)
		goto err;

	if (xmit && !xmit(&xdr, res))
		ret = NERR_RPC_ERROR;
	else
		ret = 0;

err:
	xdr_destroy(&xdr);

	return ret;
}

static int __fscall(int fd, uint32_t opcode,
		    int (*reqxmit)(XDR *, void *),
		    int (*resxmit)(XDR *, void *),
		    void *req, void *res,
		    size_t ressize)
{
	int ret;

	ret = __fscall_req(fd, opcode, reqxmit, req);
	if (ret)
		return ret;

	return __fscall_res(fd, resxmit, res, ressize);
}

int fscall_login(struct fscall_state *state, const char *conn,
		 const struct xuuid *volid)
{
	struct rpc_login_req login_req;
	struct rpc_login_res login_res;
	int ret;

	login_req.conn = (char *) conn;
	login_req.volid = *volid;

	ret = __fscall(state->sock, NRPC_LOGIN,
		       (void *) xdr_rpc_login_req,
		       (void *) xdr_rpc_login_res,
		       &login_req,
		       &login_res,
		       sizeof(login_res));
	if (ret)
		return ret;

	state->root_oid = login_res.root;

	return 0;
}

int fscall_open(struct fscall_state *state, const struct noid *oid,
		uint64_t ino, uint32_t *handle)
{
	struct rpc_open_req open_req;
	struct rpc_open_res open_res;
	int ret;

	/* only one of oid and ino is allowed */
	if ((oid && !noid_is_null(oid) && ino) ||
	    ((!oid || noid_is_null(oid)) && !ino))
		return -EINVAL;

	if (oid)
		open_req.oid = *oid;
	else
		memset(&open_req.oid, 0, sizeof(open_req.oid));

	open_req.ino = ino;
	memset(&open_req.clock, 0, sizeof(open_req.clock));

	ret = __fscall(state->sock, NRPC_OPEN,
		       (void *) xdr_rpc_open_req,
		       (void *) xdr_rpc_open_res,
		       &open_req,
		       &open_res,
		       sizeof(open_res));
	if (ret)
		return ret;

	*handle = open_res.handle;

	return 0;
}

int fscall_close(struct fscall_state *state, const uint32_t handle)
{
	struct rpc_close_req close_req;

	close_req.handle = handle;

	return __fscall(state->sock, NRPC_CLOSE,
			(void *) xdr_rpc_close_req,
			NULL,
			&close_req,
			NULL,
			0);
}

int fscall_getattr(struct fscall_state *state, const uint32_t handle,
		   struct nattr *attr)
{
	struct rpc_getattr_req getattr_req;
	struct rpc_getattr_res getattr_res;
	int ret;

	getattr_req.handle = handle;

	ret = __fscall(state->sock, NRPC_GETATTR,
		       (void *) xdr_rpc_getattr_req,
		       (void *) xdr_rpc_getattr_res,
		       &getattr_req,
		       &getattr_res,
		       sizeof(getattr_res));
	if (ret)
		return ret;

	*attr = getattr_res.attr;

	return 0;
}

int fscall_setattr(struct fscall_state *state, const uint32_t handle,
		   struct nattr *attr, bool mode_is_valid,
		   bool size_is_valid, bool atime_is_valid,
		   bool btime_is_valid, bool ctime_is_valid,
		   bool mtime_is_valid, bool owner_is_valid,
		   bool group_is_valid)
{
	struct rpc_setattr_req setattr_req;
	struct rpc_setattr_res setattr_res;
	int ret;

	setattr_req.handle = handle;
	setattr_req.attr = *attr;
	setattr_req.mode_is_valid = mode_is_valid;
	setattr_req.size_is_valid = size_is_valid;
	setattr_req.atime_is_valid = atime_is_valid;
	setattr_req.btime_is_valid = btime_is_valid;
	setattr_req.ctime_is_valid = ctime_is_valid;
	setattr_req.mtime_is_valid = mtime_is_valid;
	setattr_req.owner_is_valid = owner_is_valid;
	setattr_req.group_is_valid = group_is_valid;

	ret = __fscall(state->sock, NRPC_SETATTR,
		       (void *) xdr_rpc_setattr_req,
		       (void *) xdr_rpc_setattr_res,
		       &setattr_req,
		       &setattr_res,
		       sizeof(setattr_res));
	if (ret)
		return ret;

	*attr = setattr_res.attr;

	return 0;
}

int fscall_lookup(struct fscall_state *state, const uint32_t parent_handle,
		  const char *name, struct noid *child)
{
	struct rpc_lookup_req lookup_req;
	struct rpc_lookup_res lookup_res;
	int ret;

	lookup_req.parent = parent_handle;
	memset(&lookup_req.desired, 0, sizeof(struct noid));
	lookup_req.name = (char *) name;

	ret = __fscall(state->sock, NRPC_LOOKUP,
		       (void *) xdr_rpc_lookup_req,
		       (void *) xdr_rpc_lookup_res,
		       &lookup_req,
		       &lookup_res,
		       sizeof(lookup_res));
	if (ret)
		return ret;

	*child = lookup_res.child;

	return 0;
}

int fscall_create(struct fscall_state *state, const uint32_t parent_handle,
		  const char *name, const uint32_t owner, const uint32_t group,
		  const uint16_t mode, const uint64_t dev, struct noid *child)
{
	struct rpc_create_req create_req;
	struct rpc_create_res create_res;
	int ret;

	/* dev should only be non-zero when we're making a char/block device */
	if ((NATTR_ISBLK(mode) || NATTR_ISCHR(mode)) == !dev)
		return -EINVAL;

	create_req.parent = parent_handle;
	create_req.name = (char *) name;
	create_req.owner = owner;
	create_req.group = group;
	create_req.mode = mode;
	create_req.dev = dev;

	ret = __fscall(state->sock, NRPC_CREATE,
		       (void *) xdr_rpc_create_req,
		       (void *) xdr_rpc_create_res,
		       &create_req,
		       &create_res,
		       sizeof(create_res));
	if (ret)
		return ret;

	*child = create_res.oid;

	return 0;
}

int fscall_symlink(struct fscall_state *state, const uint32_t parent_handle,
		   const char *name, const char *target)
{
	struct rpc_link_req link_req;
	int ret;

	link_req.parent = parent_handle;
	link_req.name = (char *) name;
	link_req.target_name = (char *) target;
	link_req.target_handle = 0;
	link_req.symlink = true;

	ret = __fscall(state->sock, NRPC_LINK,
		       (void *) xdr_rpc_link_req,
		       NULL,
		       &link_req,
		       NULL,
		       0);
	if (ret)
		return ret;

	return 0;
}

int fscall_link(struct fscall_state *state, const uint32_t parent_handle,
		const char *name, const uint32_t target_handle)
{
	struct rpc_link_req link_req;
	int ret;

	link_req.parent = parent_handle;
	link_req.name = (char *) name;
	link_req.target_name = NULL;
	link_req.target_handle = target_handle;
	link_req.symlink = false;

	ret = __fscall(state->sock, NRPC_LINK,
		       (void *) xdr_rpc_link_req,
		       NULL,
		       &link_req,
		       NULL,
		       0);
	if (ret)
		return ret;

	return 0;
}

int fscall_unlink(struct fscall_state *state, const uint32_t parent_handle,
		  const char *name, const struct noid *desired, bool rmdir)

{
	struct rpc_unlink_req unlink_req;
	int ret;

	unlink_req.parent = parent_handle;
	unlink_req.name = (char *) name;
	unlink_req.rmdir = rmdir;

	if (desired)
		unlink_req.desired = *desired;
	else
		memset(&unlink_req.desired, 0, sizeof(unlink_req.desired));

	ret = __fscall(state->sock, NRPC_UNLINK,
		       (void *) xdr_rpc_unlink_req,
		       NULL,
		       &unlink_req,
		       NULL,
		       0);
	if (ret)
		return ret;

	return 0;
}

int fscall_read(struct fscall_state *state, const uint32_t handle,
		void *buf, size_t len, uint64_t off)
{
	struct rpc_read_req read_req;
	struct rpc_read_res read_res;
	int ret;

	STATIC_ASSERT(sizeof(read_req.length) == sizeof(uint32_t));

	if (len > UINT32_MAX)
		return NERR_E2BIG;

	read_req.handle = handle;
	read_req.offset = off;
	read_req.length = len;

	ret = __fscall(state->sock, NRPC_READ,
		       (void *) xdr_rpc_read_req,
		       (void *) xdr_rpc_read_res,
		       &read_req,
		       &read_res,
		       sizeof(read_res));
	if (ret)
		return ret;

	FIXME("NRPC_READ may return length != the requested length");
	memcpy(buf, read_res.data.data_val, read_res.data.data_len);

	free(read_res.data.data_val);

	return 0;
}

int fscall_write(struct fscall_state *state, const uint32_t handle,
		 const void *buf, size_t len, uint64_t off)
{
	struct rpc_write_req write_req;

	STATIC_ASSERT(sizeof(write_req.data.data_len) == sizeof(uint32_t));

	if (len > UINT32_MAX)
		return NERR_E2BIG;

	write_req.handle = handle;
	write_req.offset = off;
	write_req.data.data_len = len;
	write_req.data.data_val = (void *) buf;

	return __fscall(state->sock, NRPC_WRITE,
			(void *) xdr_rpc_write_req,
			NULL,
			&write_req,
			NULL,
			0);
}

int fscall_getdent(struct fscall_state *state, const uint32_t handle,
		   const uint64_t off, struct ndirent *child)
{
	struct rpc_getdent_req getdent_req;
	struct rpc_getdent_res getdent_res;
	int ret;

	getdent_req.parent = handle;
	getdent_req.offset = off;

	ret = __fscall(state->sock, NRPC_GETDENT,
		       (void *) xdr_rpc_getdent_req,
		       (void *) xdr_rpc_getdent_res,
		       &getdent_req,
		       &getdent_res,
		       sizeof(getdent_res));
	if (ret)
		return ret;

	*child = getdent_res.ent;

	return 0;
}

int fscall_obj_info(struct fscall_state *state, const struct noid *oid,
		    struct obj_info **infos, size_t *ninfos)
{
	struct rpc_obj_info_req obj_info_req;
	struct rpc_obj_info_res obj_info_res;
	int ret;

	obj_info_req.oid = *oid;

	ret = __fscall(state->sock, NRPC_OBJ_INFO,
		       (void *) xdr_rpc_obj_info_req,
		       (void *) xdr_rpc_obj_info_res,
		       &obj_info_req,
		       &obj_info_res,
		       sizeof(obj_info_res));
	if (ret)
		return ret;

	/* steal the length & value */
	*ninfos = obj_info_res.info.info_len;
	*infos = obj_info_res.info.info_val;

	return 0;
}

int fscall_vdev_import(struct fscall_state *state, const char *type,
		       const char *path, bool create,
		       struct xuuid *uuid)
{
	struct rpc_vdev_import_req vdev_import_req;
	struct rpc_vdev_import_res vdev_import_res;
	int ret;

	vdev_import_req.type = (char *) type;
	vdev_import_req.path = (char *) path;
	vdev_import_req.create = create;

	ret = __fscall(state->sock, NRPC_VDEV_IMPORT,
		       (void *) xdr_rpc_vdev_import_req,
		       (void *) xdr_rpc_vdev_import_res,
		       &vdev_import_req,
		       &vdev_import_res,
		       sizeof(vdev_import_res));
	if (ret)
		return ret;

	*uuid = vdev_import_res.uuid;

	return 0;
}

int fscall_vdev_list(struct fscall_state *state, struct vdev_info **vdevs,
		     size_t *nvdevs)
{
	struct rpc_vdev_list_res list_res;
	int ret;

	ret = __fscall(state->sock, NRPC_VDEV_LIST,
		       NULL,
		       (void *) xdr_rpc_vdev_list_res,
		       NULL,
		       &list_res,
		       sizeof(list_res));
	if (ret)
		return ret;

	/* steal the length & value */
	*nvdevs = list_res.vdevs.vdevs_len;
	*vdevs = list_res.vdevs.vdevs_val;

	return 0;
}

int fscall_vol_clone(struct fscall_state *state, const struct xuuid *vdevid,
		     const struct xuuid *volid)
{
	struct rpc_vol_create_req vol_create_req;
	struct rpc_vol_create_res vol_create_res;

	vol_create_req.vdevid = *vdevid;
	vol_create_req.volid = *volid;

	return __fscall(state->sock, NRPC_VOL_CREATE,
			(void *) xdr_rpc_vol_create_req,
			(void *) xdr_rpc_vol_create_res,
			&vol_create_req,
			&vol_create_res,
			sizeof(vol_create_res));
}

int fscall_vol_create(struct fscall_state *state, const struct xuuid *vdevid,
		      struct xuuid *volid)
{
	struct rpc_vol_create_req vol_create_req;
	struct rpc_vol_create_res vol_create_res;
	int ret;

	vol_create_req.vdevid = *vdevid;
	vol_create_req.volid = xuuid_null_uuid;

	ret = __fscall(state->sock, NRPC_VOL_CREATE,
		       (void *) xdr_rpc_vol_create_req,
		       (void *) xdr_rpc_vol_create_res,
		       &vol_create_req,
		       &vol_create_res,
		       sizeof(vol_create_res));
	if (ret)
		return ret;

	*volid = vol_create_res.volid;

	return 0;
}

int fscall_vol_list(struct fscall_state *state, const struct xuuid *vdevid,
		    struct vol_info **vols, size_t *nvols)
{
	struct rpc_vol_list_req list_req;
	struct rpc_vol_list_res list_res;
	int ret;

	list_req.vdevid = vdevid ? *vdevid : xuuid_null_uuid;

	ret = __fscall(state->sock, NRPC_VOL_LIST,
		       (void *) xdr_rpc_vol_list_req,
		       (void *) xdr_rpc_vol_list_res,
		       &list_req,
		       &list_res,
		       sizeof(list_res));
	if (ret)
		return ret;

	/* steal the length & value */
	*nvols = list_res.vols.vols_len;
	*vols = list_res.vols.vols_val;

	return 0;
}

static int __fscall_handshake(int fd)
{
	struct rpc_handshake_req request;
	XDR xdr;
	int ret;

	request.vers = NRPC_VERSION;

	xdrfd_create(&xdr, fd, NULL, XDR_ENCODE);

	ret = NERR_RPC_ERROR;

	if (!xdr_rpc_handshake_req(&xdr, &request))
		goto err;

	ret = __fscall_res(fd, NULL, NULL, 0);

err:
	xdr_destroy(&xdr);

	return ret;
}

int fscall_connect(struct fscall_state *state, int fd)
{
	int ret;

	ret = __fscall_handshake(fd);
	if (ret)
		return ret;

	state->sock = fd;

	return 0;
}

void fscall_disconnect(struct fscall_state *state)
{
	xclose(state->sock);
}

int fscall_mount(struct fscall_state *state, const struct xuuid *volid)
{
	int ret;

	ret = fscall_login(state, "unused", volid);
	if (ret)
		return ret;

	ret = fscall_open(state, &state->root_oid, 0, &state->root_ohandle);
	if (ret)
		return ret;

	return 0;
}