catalinux / rgfs (public) (License: GPLv3) (since 2020-11-11) (hash sha1)
Allows mapping of RocketGit storage area into a local directory

/rgfs.c (b865abc85ee496f9d431715907a42123fa5cb1ca) (31213 bytes) (mode 100644) (type blob)

/*
 * rgfs project
 * Main GNUTLS code was borrowed from their public domain example. Thank you!
 */

#include "rgfs_config.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <endian.h>
#include <stdarg.h>

#include <fuse.h>

#include <gnutls/gnutls.h>
#include <gnutls/x509.h>

// a way to distinguish between clients
static int		rgfs_port = 443;
static char		*rgfs_server = "rgfs.rocketgit.com";
static char		*rgfs_proto = "websocket";
static char		*rgfs_url = "/rgfs";
static int		rgfs_debug = 0;
static char		*rgfs_log = "rgfs.log";
static int		rgfs_log_fd = 2;

static gnutls_session_t	session;
static unsigned char connected = 0;
static int sd = -1;
static gnutls_certificate_credentials_t xcred;

static void xlog(char *format, ...)
{
	va_list ap;
	size_t len, len2;
	char line[4 * 4096];
	struct timeval tv;

	if (rgfs_debug == 0)
		return;

	if (rgfs_log_fd == -1)
		return;

	gettimeofday(&tv, NULL);
	len = snprintf(line, sizeof(line),
		"%ld.%06ld ", tv.tv_sec, tv.tv_usec);

	va_start(ap, format);
	len2 = vsnprintf(line + len, sizeof(line) - len, format, ap);
	if (len2 >= sizeof(line) - len)
		len2 = sizeof(line);
	else
		len2 += len;
	va_end(ap);

	write(rgfs_log_fd, line, len2);
}

/*
 * Receiving data
 */
static ssize_t rgfs_recv(void *buf, const size_t buf_max)
{
	ssize_t r;

	do {
		r = gnutls_record_recv(session, buf, buf_max);
	} while ((r == GNUTLS_E_AGAIN) || (r == GNUTLS_E_INTERRUPTED));

	if (r <= 0)
		xlog("Cannot receive [%zd]!\n", r);

	return r;
}

/*
 * Sending data
 */
static ssize_t rgfs_send(const char *op, const void *buf, size_t buf_len)
{
	ssize_t r;
	size_t off;

	if (rgfs_debug > 10) {
		char debug[4096 * 2 + 1];

		uint16_t max = buf_len > 4096 ? 4096 : buf_len;
		for (uint16_t i = 0; i < max; i++)
			sprintf(debug + i * 2, "%02hhx", * (unsigned char *) (buf + i));
		xlog("  %s: SEND %u: %s\n", op, buf_len, debug);
	}

	off = 0;
	do {
		do {
			r = gnutls_record_send(session, buf + off, buf_len - off);
		} while ((r == GNUTLS_E_AGAIN) || (r == GNUTLS_E_INTERRUPTED));

		if (r < 0) {
			xlog("  Cannot send [%zd]!\n", r);
			exit(EXIT_FAILURE);
		}
		off += r;
		if (off == buf_len)
			break;

		// We sent less than buf_len
		xlog("  Partial send r=%ld off=%zu < buf_len=%zu!\n", r, off, buf_len);
	} while (1);

	return buf_len;
}

/*
 * Connect to server
 */
static int rgfs_connect(void)
{
	int r, fd;
	struct addrinfo hints;
	struct addrinfo *result, *rp;
	char port[6];

	snprintf(port, sizeof(port), "%d", rgfs_port);

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	r = getaddrinfo(rgfs_server, port, &hints, &result);
	if (r != 0) {
		xlog("getaddrinfo: %s\n", gai_strerror(r));
		return -1;
	}

	for (rp = result; rp != NULL; rp = rp->ai_next) {
		fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
		if (fd == -1)
			continue;

		r = connect(fd, rp->ai_addr, rp->ai_addrlen);
		if (r != -1) {
			char ip[49], service[NI_MAXSERV];

			r = getnameinfo((struct sockaddr *) rp->ai_addr, rp->ai_addrlen,
				ip, sizeof(ip), service, sizeof(service),
				NI_NUMERICHOST | NI_NUMERICSERV);
			if (r == 0)
				xlog("Connected to %s/%s!\n", ip, service);
			break;
		}
		close(fd);

		xlog("connect error: %m\n");
	}
	freeaddrinfo(result);
	if (rp == NULL) {
		xlog("Could not connect!\n");
		return -1;
	}

	return fd;
}

/*
 * Send first websocket negotiation
 */
static ssize_t rgfs_ws1(void)
{
	char out[4096];
	int len;

	len = snprintf(out, sizeof(out),
		"GET %s HTTP/1.1\r\n"
		"Host: %s:%d\r\n"
		"Connection: keep-alive, Upgrade\r\n"
		"Pragma: no-cache\r\n"
		"Cache-Control: no-cache\r\n"
		"Upgrade: websocket\r\n"
		"\r\n",
		rgfs_url, rgfs_server, rgfs_port);

	xlog("Sending:\n%s", out);
	return rgfs_send("ws req", out, len);
}

/*
 * Send a generic variable
 */
static int rgfs_send_gen_var(const char *var, const char *value)
{
	unsigned int len_var, len_value, i;
	unsigned char buf[4096];

	xlog("Sending generic var [%s]=[%s]...\n", var, value);
	len_var = strlen(var);
	len_value = strlen(value);

	if (1 + 2 + 2 + 2 + len_var + len_value > sizeof(buf)) {
		xlog("Buffer is too small to send generic var!\n");
		return -1;
	}

	i = 0;
	buf[i++] = 0x12; // type=genvar
	buf[i++] = (2 + 2 + len_var + len_value) >> 8; // len H
	buf[i++] = 2 + 2 + len_var + len_value; // len L
	buf[i++] = len_var >> 8;
	buf[i++] = len_var;
	buf[i++] = len_value >> 8;
	buf[i++] = len_value;
	memcpy(buf + i, var, len_var); i += len_var;
	memcpy(buf + i, value, len_value); i += len_value;
	return rgfs_send(var, buf, i);
}

/*
 * Returns 0 if ok, 1 if we should abort the program else => retry.
 */
static int rgfs_tls(void)
{
	int r, ret = -1, off;
	char buf[8192], *desc;
	int type;
	unsigned status;

	do {
		/* Destroy session */
		xlog("session=%p sd=%d\n", session, sd);
		if (session) {
			r = gnutls_bye(session, GNUTLS_SHUT_RDWR);
			if (r != GNUTLS_E_SUCCESS)
				xlog("gnutls warn: cannot say goodbye [%d]!\n", r);
		}

		if (sd != -1) {
			close(sd);
			sd = -1;
		}

		gnutls_deinit(session);

		/* Initialize TLS session */
		r = gnutls_init(&session, GNUTLS_CLIENT);
		if (r != GNUTLS_E_SUCCESS) {
			xlog("gnutls error: cannot init session!\n");
			break;
		}

		r = gnutls_server_name_set(session, GNUTLS_NAME_DNS,
			rgfs_server, strlen(rgfs_server));
		if (r != GNUTLS_E_SUCCESS) {
			xlog("gnutls error: cannot set name [%s]!\n", rgfs_server);
			break;
		}

		/* It is recommended to use the default priorities */
		r = gnutls_set_default_priority(session);
		if (r != GNUTLS_E_SUCCESS) {
			xlog("gnutls error: cannot set default priority!\n");
			break;
		}

		/* put the x509 credentials to the current session */
		r = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
		if (r != GNUTLS_E_SUCCESS) {
			xlog("gnutls error: cannot set credentials!\n");
			break;
		}
		gnutls_session_set_verify_cert(session, rgfs_server, 0);
		// TODO - no error code?

		sd = rgfs_connect();
		if (sd == -1)
			break;

		gnutls_transport_set_int(session, sd);
		gnutls_handshake_set_timeout(session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);

		/* Perform the TLS handshake */
		do {
			r = gnutls_handshake(session);
	        } while (r < 0 && gnutls_error_is_fatal(r) == 0);
		if (r < 0) {
			if (r == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR) {
				gnutls_datum_t out;

				/* check certificate verification status */
				type = gnutls_certificate_type_get(session);
				status = gnutls_session_get_verify_cert_status(session);
				r = gnutls_certificate_verification_status_print(status, type, &out, 0);
				if (r == GNUTLS_E_SUCCESS) {
					xlog("cert verify output: %s\n", out.data);
					gnutls_free(out.data);
				}
			}
			xlog("Handshake failed: %s\n", gnutls_strerror(r));
			break;
		}

		desc = gnutls_session_get_desc(session);
		xlog("Session info: %s\n", desc);
		gnutls_free(desc);

		r = rgfs_ws1();
		if (r == -1)
			break;
		//xlog("ws1 returned %d.\n", r);

		off = 0;
		while (1) {
			r = rgfs_recv(buf + off, sizeof(buf) - off - 1);
			if (r <= 0)
				break;

			buf[off + r] = '\0';
			//xlog("Received[%d + %d]:\n%s\n", off, r, buf);
			if (strstr(buf, "\r\n\r\n"))
				break;
		}
		if (r == 0) { // server closed the connection -> do not retry
			ret = 1;
			break;
		}
		if (r == -1)
			break;
		if (strncmp(buf, "HTTP/1.1 101 Switching Protocols", 32) != 0) {
			xlog("Invalid HTTP answer:\n%s\n", buf);
			break;
		}

		r = rgfs_send_gen_var("ver", RGFS_PROTOCOL_VERSION);
		if (r <= 0)
			break;

		for (unsigned int i = 0; environ[i]; i++) {
			char *p, *q, *k, *v;

			q = environ[i];
			if(strncmp(q, "RGFS_", 5) != 0)
				continue;

			q += 5;
			if (strncmp(q, "SERVER", 6) == 0)
				continue;
			if (strncmp(q, "PORT", 4) == 0)
				continue;
			if (strncmp(q, "URL", 3) == 0)
				continue;
			if (strncmp(q, "DEBUG", 5) == 0)
				continue;
			if (strncmp(q, "LOG", 3) == 0)
				continue;

			p = strchr(q + 1, '=');
			if (!p)
				continue;

			// abc=x
			// q = 0
			//    p = 3
			k = strndup(q, p - q);
			if (!k) {
				xlog("Cannot allocate memory!\n");
				return 1;
			}

			v = strdup(p + 1);
			if (!v) {
				xlog("Cannot allocate memory!\n");
				return 1;
			}

			r = rgfs_send_gen_var(k, v);
			free(v);
			free(k);
			if (r <= 0)
				break;
		}
		if (r <= 0)
			break;

		ret = 0;
	} while (0);

	return ret;
}

static void rgfs_reconnect(void)
{
	int r;

	while (1) {
		xlog("Reconnecting...\n");
		r = rgfs_tls();
		if (r == 0) {
			xlog(" Reconnecting OK!\n");
			connected = 1;
			break;
		}
		if (r == 1) {
			xlog(" Exiting!");
			exit(1);
		}

		xlog(" Sleeping...\n");
		sleep(1);
	}
}

static int rgfs_recv_hl(const char *op, void *buf, size_t buf_size)
{
	int r;
	uint32_t off, to_read = 0;

	off = 0;
	while (1) {
		xlog(" Waiting for data (buf_size=%zu off=%d to_read=%u)\n",
			buf_size, off, to_read);
		if (off == buf_size) {
			xlog("  Buffer is too small!\n");
			exit(EXIT_FAILURE);
		}
		r = rgfs_recv(buf + off, buf_size - off);
		if (r < 0)
			return r;
		if (r == 0)
			return off;

		off += r;
		if ((to_read == 0) && (off >= 4)) {
			to_read = be32toh(*(uint32_t *) buf);
			xlog("  to_read=%u\n", to_read);
		}

		if (rgfs_debug > 10) {
			char debug[4096 * 2 + 1];
			uint16_t max = r > 4096 ? 4096 : r;
			for (uint16_t i = 0; i < max; i++)
				sprintf(debug + i * 2, "%02hhx",
					* (unsigned char *) (buf + off - r + i));
			xlog("  %s: RECV r=%d off=%u: %s\n",
				op, r, off, debug);
		}

		if (off == 4 + to_read) {
			xlog("  Readed done; return %u\n", off);
			return off;
		}
	}
}

static ssize_t rgfs_send_recv(const char *op,
	const void *out_buf, size_t out_buf_len,
	void *in_buf, size_t in_buf_size)
{
	int r;

	if (connected == 0)
		rgfs_reconnect();

	while (1) {
		//xlog(" Sending %zu bytes\n", out_buf_len);
		r = rgfs_send(op, out_buf, out_buf_len);
		if (r <= 0) {
			rgfs_reconnect();
			continue;
		}

		r = rgfs_recv_hl(op, in_buf, in_buf_size);
		if (r <= 0) {
			rgfs_reconnect();
			continue;
		}

		return r;
	}
}

///////////////////////////////////////////////////////////////////// fuse stuff


static int rgfs_fuse_getattr(const char *path, struct stat *s)
{
	int r, i, err = 0;
	unsigned char buf[4 * 4096], cmd;
	uint16_t path_len, len;

	xlog("getattr: path=%s\n", path);

	path_len = strlen(path);
	len = 1 + 2 + path_len;
	if (len > sizeof(buf)) {
		xlog(" buffer too small\n");
		return -EIO;
	}

	i = 0;
	buf[i++] = 0x02;
	buf[i++] = path_len >> 8;
	buf[i++] = path_len;
	memcpy(buf + i, path, path_len); i += path_len;
	r = rgfs_send_recv("getattr", buf, i, buf, sizeof(buf));

	memset(s, 0, sizeof(struct stat));
	i = 4;
	while (i < r) {
		cmd = buf[i++];
		//xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); i += 4; break;
		case 0x01: s->st_mode = be32toh(*(uint32_t *) (buf + i)); i += 4; break;
		case 0x02: s->st_nlink = be64toh(*(uint64_t *) (buf + i)); i += 8; break;
		case 0x03: s->st_size = be64toh(*(uint64_t *) (buf + i)); i += 8; break;
		case 0x04: s->st_atime = be32toh(*(uint32_t *) (buf + i)); i += 4; break;
		case 0x05: s->st_mtime = be32toh(*(uint32_t *) (buf + i)); i += 4; break;
		case 0x06: s->st_ctime = be32toh(*(uint32_t *) (buf + i)); i += 4; break;
		case 0x07: s->st_ino = be64toh(*(uint64_t *) (buf + i)); i += 8; break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	if (err < 0)
		xlog(" server returned error %d!\n", err);

	//xlog("  mode=%o nlink=%lu size=%lu\n",
	//	s->st_mode, s->st_nlink, s->st_size);

	return err;
}

static int rgfs_fuse_readdir(const char *dir, void *out,
	fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi)
{
	int r, ret, i, err = 0;
	unsigned char buf[64 * 4096], cmd;
	uint16_t u16, path_len, len;
	char path[512];
	uint64_t u64;

	(void) fi;
	xlog("readdir: dir=%s offset=%zd\n", dir, offset);

	path_len = strlen(dir);
	len = 1 + 2 + 8 + path_len;

	i = 0;
	buf[i++] = 0x03;
	buf[i++] = (len - 1 - 2) >> 8;
	buf[i++] = len - 1 - 2;
	u64 = htobe64(offset); memcpy(buf + i, &u64, 8); i += 8;
	memcpy(buf + i, dir, path_len); i += path_len;
	r = rgfs_send_recv("readdir", buf, i, buf, sizeof(buf));

	// standard
	filler(out, ".", NULL, 0);
	filler(out, "..", NULL, 0);

	i = 4;
	while (i < r) {
		cmd = buf[i++];
		//xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); i += 4; break;
		case 0x01: /* entry */
			u16 = be16toh(*(uint16_t *) (buf + i)); i += 2;
			if (u16 > sizeof(path) - 1) {
				xlog("path len bigger than buf!\n");
				i += u16;
				break;
			}
			memcpy(path, buf + i, u16); i += u16;
			path[u16] = '\0';
			//xlog("  received path %s\n", path);
			ret = filler(out, path, NULL, 0);
			if (ret != 0)
				xlog(" FILLTER RETURNS 1!\n");
			break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	if (err < 0)
		xlog(" server returned error %d!\n", err);

	return err;
}

static int rgfs_fuse_open(const char *path, struct fuse_file_info *fi)
{
	(void) path;
	(void) fi;
	xlog("open: path=%s\n", path);

	//if ((fi->flags & O_ACCMODE) != O_RDONLY)
	//	return -EACCES;

	return 0;
}

static int rgfs_fuse_read(const char *path, char *out, size_t size,
	off_t offset, struct fuse_file_info *fi)
{
	int r, err = 0, i;
	unsigned char *buf, cmd;
	uint16_t path_len, len;
	uint64_t u64, min;

	(void) fi;
	xlog("read: path=%s size=%zu offset=%zd\n",
		path, size, offset);

	path_len = strlen(path);
	len = 1 + 2 + 8 + 8 + path_len; // request
	min = 4 + 1 + size; // answer
	if (min < len)
		min = len;

	buf = malloc(min);
	if (!buf) {
		xlog("  cannot alloc memory (%llu)!\n", min);
		return -EIO;
	}

	i = 0;
	buf[i++] = 0x04;
	buf[i++] = (len - 1 - 2) >> 8;
	buf[i++] = len - 1 - 2;
	u64 = htobe64(offset); memcpy(buf + i, &u64, 8); i += 8;
	u64 = htobe64(size); memcpy(buf + i, &u64, 8); i += 8;
	memcpy(buf + i, path, path_len); i += path_len;
	r = rgfs_send_recv("read", buf, i, buf, min);

	i = 4;
	if (i < r) {
		cmd = buf[i++];
		//xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); break;
		case 0x01: /* block */
			memcpy(out, buf + i, r - i);
			err = r - i;
			break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	free(buf);

	xlog(" returning %d\n", err);

	return err;
}

static int rgfs_fuse_write(const char *path, const char *in, size_t size,
	off_t offset, struct fuse_file_info *fi)
{
	int r, err = 0, i;
	unsigned char buf[16 * 4096], cmd;
	uint16_t path_len;
	size_t len, over;
	uint64_t u64;
	uint16_t u16;

	(void) fi;

	xlog("write: path=%s size=%zu offset=%zd\n", path, size, offset);

	path_len = strlen(path);
	len = 1 + 2 + 2 + 8 + path_len + size;
	if (len > sizeof(buf)) {
		over = len - sizeof(buf);
		size -= over;
		len -= over;
	}

	i = 0;
	buf[i++] = 0x05;
	buf[i++] = (len - 1 - 2) >> 8;
	buf[i++] = len - 1 - 2;
	u16 = htobe16(path_len); memcpy(buf + i, &u16, 2); i += 2;
	u64 = htobe64(offset); memcpy(buf + i, &u64, 8); i += 8;
	memcpy(buf + i, path, path_len); i += path_len;
	memcpy(buf + i, in, size); i += size;
	r = rgfs_send_recv("write", buf, i, buf, sizeof(buf));

	i = 4;
	if (i < r) {
		cmd = buf[i++];
		//xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); break;
		case 0x01: /* block */
			u64 = be64toh(*(uint64_t *) (buf + i));
			if (u64 != size)
				xlog("  wrote only %llu bytes\n", u64);
			err = u64;
			break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	if (err < 0)
		xlog(" server returned error %d!\n", err);

	return err;
}

static int rgfs_fuse_create(const char *path, mode_t mode,
	struct fuse_file_info *fi)
{
	int err = 0, r, i;
	uint16_t path_len, len;
	unsigned char buf[4 * 4096], cmd;
	uint32_t u32;

	(void) fi;
	xlog("create: path=%s mode=%o\n", path, mode);

	path_len = strlen(path);
	len = 1 + 2 + 4 + path_len;
	if (len > sizeof(buf)) {
		xlog(" buffer too small\n");
		return -EIO;
	}

	i = 0;
	buf[i++] = 0x06;
	buf[i++] = (len - 1 - 2) >> 8;
	buf[i++] = len - 1 - 2;
	u32 = htobe32(mode); memcpy(buf + i, &u32, 4); i += 4;
	memcpy(buf + i, path, path_len); i += path_len;
	r = rgfs_send_recv("create", buf, i, buf, sizeof(buf));

	i = 4;
	if (i < r) {
		cmd = buf[i++];
		//xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	if (err < 0)
		xlog(" server returned error %d!\n", err);

	return err;
}

static int rgfs_fuse_mkdir(const char *path, mode_t mode)
{
	int err = 0, r, i;
	uint16_t path_len, len;
	unsigned char buf[4 * 4096], cmd;
	uint32_t u32;

	xlog("mkdir: path=%s mode=%o\n", path, mode);

	path_len = strlen(path);
	len = 1 + 2 + 4 + path_len;
	if (len > sizeof(buf)) {
		xlog(" buffer too small\n");
		return -EIO;
	}

	i = 0;
	buf[i++] = 0x07;
	buf[i++] = (len - 1 - 2) >> 8;
	buf[i++] = len - 1 - 2;
	u32 = htobe32(mode); memcpy(buf + i, &u32, 4); i += 4;
	memcpy(buf + i, path, path_len); i += path_len;
	r = rgfs_send_recv("mkdir", buf, i, buf, sizeof(buf));

	i = 4;
	if (i < r) {
		cmd = buf[i++];
		//xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	if (err < 0)
		xlog(" server returned error %d!\n", err);

	return err;
}

static int rgfs_fuse_unlink(const char *path)
{
	int err = 0, r, i;
	uint16_t path_len, len;
	unsigned char buf[4 * 4096], cmd;

	xlog("unlink: path=%s\n", path);

	path_len = strlen(path);
	len = 1 + 2 + path_len;
	if (len > sizeof(buf)) {
		xlog(" buffer too small\n");
		return -EIO;
	}

	i = 0;
	buf[i++] = 0x08;
	buf[i++] = (len - 1 - 2) >> 8;
	buf[i++] = len - 1 - 2;
	memcpy(buf + i, path, path_len); i += path_len;
	r = rgfs_send_recv("unlink", buf, i, buf, sizeof(buf));

	i = 4;
	if (i < r) {
		cmd = buf[i++];
		//xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	if (err < 0)
		xlog(" server returned error %d!\n", err);

	return err;
}

static int rgfs_fuse_rmdir(const char *path)
{
	int err = 0, r, i;
	uint16_t path_len, len;
	unsigned char buf[4096], cmd;

	xlog("rmdir: path=%s\n", path);

	path_len = strlen(path);
	len = 1 + 2 + path_len;
	if (len > sizeof(buf)) {
		xlog(" buffer too small\n");
		return -EIO;
	}

	i = 0;
	buf[i++] = 0x09;
	buf[i++] = (len - 1 - 2) >> 8;
	buf[i++] = len - 1 - 2;
	memcpy(buf + i, path, path_len); i += path_len;
	r = rgfs_send_recv("rmdir", buf, i, buf, sizeof(buf));

	i = 4;
	if (i < r) {
		cmd = buf[i++];
		//xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	if (err < 0)
		xlog(" server returned error %d!\n", err);

	return err;
}

static int rgfs_fuse_rename(const char *old, const char *new)
{
	int r, err = 0, i;
	unsigned char buf[4 * 4096], cmd;
	uint16_t old_len, new_len, len, u16;

	xlog("rename: old=%s new=%s\n", old, new);

	old_len = strlen(old);
	new_len = strlen(new);
	len = 1 + 2 + 2 + 2 + old_len + new_len;
	if (len > sizeof(buf)) {
		xlog(" buffer too small\n");
		return -EIO;
	}

	i = 0;
	buf[i++] = 0x0a;
	buf[i++] = (len - 1 - 2) >> 8;
	buf[i++] = len - 1 - 2;
	u16 = htobe16(old_len); memcpy(buf + i, &u16, 2); i += 2;
	u16 = htobe16(new_len); memcpy(buf + i, &u16, 2); i += 2;
	memcpy(buf + i, old, old_len); i += old_len;
	memcpy(buf + i, new, new_len); i += new_len;
	r = rgfs_send_recv("rename", buf, i, buf, sizeof(buf));

	i = 4;
	if (i < r) {
		cmd = buf[i++];
		//xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	if (err < 0)
		xlog(" server returned error %d!\n", err);

	return err;
}

static int rgfs_fuse_truncate(const char *path, off_t off)
{
	int err = 0, r, i;
	uint16_t path_len, len;
	uint64_t u64;
	unsigned char buf[4096], cmd;

	xlog("truncate: path=%s off=%ld\n", path, off);

	path_len = strlen(path);
	len = 1 + 2 + 8 + path_len;
	if (len > sizeof(buf)) {
		xlog(" buffer too small\n");
		return -EIO;
	}

	i = 0;
	buf[i++] = 0x0b;
	buf[i++] = (len - 1 - 2) >> 8;
	buf[i++] = len - 1 - 2;
	u64 = htobe64(off); memcpy(buf + i, &u64, 8); i += 8;
	memcpy(buf + i, path, path_len); i += path_len;
	r = rgfs_send_recv("truncate", buf, i, buf, sizeof(buf));

	i = 4;
	if (i < r) {
		cmd = buf[i++];
		xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	if (err < 0)
		xlog(" server returned error %d!\n", err);

	return err;
}

static int rgfs_fuse_utimens(const char *path, const struct timespec tv[2])
{
	int r, err = 0, i;
	unsigned char buf[4 * 4096], cmd;
	uint16_t len, path_len;
	uint64_t u64;

	// sizeof(time_t) = 8; sizeof(long) = 8 tv[0]=atime, tv[1]=mtime
	xlog("utimens: path=%s atime=%lu.%lu mtime=%lu.%lu\n",
		path, tv[0].tv_sec, tv[0].tv_nsec, tv[1].tv_sec, tv[1].tv_nsec);

	path_len = strlen(path);
	len = 1 + 2 + 4 * 8 + path_len;
	if (len > sizeof(buf)) {
		xlog(" buffer too small\n");
		return -EIO;
	}

	i = 0;
	buf[i++] = 0x0c;
	buf[i++] = (len - 1 - 2) >> 8;
	buf[i++] = len - 1 - 2;
	u64 = htobe64(tv[0].tv_sec); memcpy(buf + i, &u64, 8); i += 8;
	u64 = htobe64(tv[0].tv_nsec); memcpy(buf + i, &u64, 8); i += 8;
	u64 = htobe64(tv[1].tv_sec); memcpy(buf + i, &u64, 8); i += 8;
	u64 = htobe64(tv[1].tv_nsec); memcpy(buf + i, &u64, 8); i += 8;
	memcpy(buf + i, path, path_len); i += path_len;
	r = rgfs_send_recv("utimens", buf, i, buf, sizeof(buf));

	i = 4;
	if (i < r) {
		cmd = buf[i++];
		//xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	if (err < 0)
		xlog(" server returned error %d!\n", err);

	return err;
}

static int rgfs_fuse_readlink(const char *path, char *out, size_t out_size)
{
	int r, err = 0, i;
	char buf[4096], cmd;
	uint16_t len, path_len;
	size_t max;

	xlog("readlink: path=%s\n", path);

	path_len = strlen(path);
	len = 1 + 2 + path_len;
	if (len > sizeof(buf)) {
		xlog(" buffer too small\n");
		return -EIO;
	}

	i = 0;
	buf[i++] = 0x0d;
	buf[i++] = (len - 1 - 2) >> 8;
	buf[i++] = len - 1 - 2;
	memcpy(buf + i, path, path_len); i += path_len;
	r = rgfs_send_recv("readlink", buf, i, buf, sizeof(buf));

	i = 4;
	if (i < r) {
		cmd = buf[i++];
		//xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); break;
		case 0x01: /* ok + data */
			max = r - i;
			if (max > out_size)
				max = out_size;
			strncpy(out, buf + i, max); out_size -= max;
			if (out_size > 0) {
				out[r - i] = '\0';
				max++;
			}
			break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	if (err < 0)
		xlog(" server returned error %d!\n", err);

	return err;
}

static int rgfs_fuse_symlink(const char *target, const char *link)
{
	int r, err = 0, i;
	unsigned char buf[4 * 4096], cmd;
	uint16_t u16, len, target_len, link_len;

	xlog("symlink: %s %s\n", target, link);

	target_len = strlen(target);
	link_len = strlen(link);
	len = 1 + 2 + 2 + 2 + target_len + link_len;
	if (len > sizeof(buf)) {
		xlog(" buffer too small\n");
		return -EIO;
	}

	i = 0;
	buf[i++] = 0x0e;
	buf[i++] = (len - 1 - 2) >> 8;
	buf[i++] = len - 1 - 2;
	u16 = htobe16(target_len); memcpy(buf + i, &u16, 2); i += 2;
	u16 = htobe16(link_len); memcpy(buf + i, &u16, 2); i += 2;
	memcpy(buf + i, target, target_len); i += target_len;
	memcpy(buf + i, link, link_len); i += link_len;
	r = rgfs_send_recv("symlink", buf, i, buf, sizeof(buf));

	i = 4;
	if (i < r) {
		cmd = buf[i++];
		//xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	if (err != 0)
		xlog(" server returned error %d!\n", err);

	return err;
}

static int rgfs_fuse_link(const char *target, const char *link)
{
	int r, err = 0, i;
	unsigned char buf[4 * 4096], cmd;
	uint16_t u16, len, target_len, link_len;

	xlog("link: %s %s\n", target, link);

	target_len = strlen(target);
	link_len = strlen(link);
	len = 1 + 2 + 2 + 2 + target_len + link_len;
	if (len > sizeof(buf)) {
		xlog(" buffer too small\n");
		return -EIO;
	}

	i = 0;
	buf[i++] = 0x0f;
	buf[i++] = (len - 1 - 2) >> 8;
	buf[i++] = len - 1 - 2;
	u16 = htobe16(target_len); memcpy(buf + i, &u16, 2); i += 2;
	u16 = htobe16(link_len); memcpy(buf + i, &u16, 2); i += 2;
	memcpy(buf + i, target, target_len); i += target_len;
	memcpy(buf + i, link, link_len); i += link_len;
	r = rgfs_send_recv("link", buf, i, buf, sizeof(buf));

	i = 4;
	if (i < r) {
		cmd = buf[i++];
		//xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	if (err != 0)
		xlog(" server returned error %d!\n", err);

	return err;
}

static int rgfs_fuse_chown(const char *path, uid_t uid, gid_t gid)
{
	int err = 0, r, i;
	uint16_t path_len, len;
	unsigned char buf[4 * 4096], cmd;
	uint32_t u32;

	xlog("chown: path=%s uid=%u gid=%u\n", path, uid, gid);

	path_len = strlen(path);
	len = 1 + 2 + 4 + 4 + path_len;
	if (len > sizeof(buf)) {
		xlog(" buffer too small\n");
		return -EIO;
	}

	i = 0;
	buf[i++] = 0x10;
	buf[i++] = (len - 1 - 2) >> 8;
	buf[i++] = len - 1 - 2;
	u32 = htobe32(uid); memcpy(buf + i, &u32, 4); i += 4;
	u32 = htobe32(gid); memcpy(buf + i, &u32, 4); i += 4;
	memcpy(buf + i, path, path_len); i += path_len;
	r = rgfs_send_recv("create", buf, i, buf, sizeof(buf));

	i = 4;
	if (i < r) {
		cmd = buf[i++];
		//xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	if (err < 0)
		xlog(" server returned error %d!\n", err);

	return err;
}

static int rgfs_fuse_chmod(const char *path, mode_t mode)
{
	int err = 0, r, i;
	uint16_t path_len, len;
	unsigned char buf[4 * 4096], cmd;
	uint32_t u32;

	xlog("chmod: path=%s mode=%o\n", path, mode);

	path_len = strlen(path);
	len = 1 + 2 + 4 + path_len;
	if (len > sizeof(buf)) {
		xlog(" buffer too small\n");
		return -EIO;
	}

	i = 0;
	buf[i++] = 0x11;
	buf[i++] = (len - 1 - 2) >> 8;
	buf[i++] = len - 1 - 2;
	u32 = htobe32(mode); memcpy(buf + i, &u32, 4); i += 4;
	memcpy(buf + i, path, path_len); i += path_len;
	r = rgfs_send_recv("create", buf, i, buf, sizeof(buf));

	i = 4;
	if (i < r) {
		cmd = buf[i++];
		//xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	if (err < 0)
		xlog(" server returned error %d!\n", err);

	return err;
}

static int rgfs_fuse_getxattr(const char *path, const char *attr_name,
	char *value, size_t value_size)
{
	int r, i, err = 0;
	unsigned char buf[4 * 4096], cmd;
	uint16_t len, u16;

	xlog("getxattr: path=[%s] attr=[%s]\n", path, attr_name);

	uint16_t path_len = strlen(path);
	uint16_t attr_name_len = strlen(attr_name);
	len = 1 + 2 + 2 + 2 + path_len + attr_name_len;
	if (len > sizeof(buf)) {
		xlog(" buffer too small\n");
		return -EIO;
	}

	i = 0;
	buf[i++] = 0x13;
	buf[i++] = (len - 1 - 2) >> 8;
	buf[i++] = (len - 1 - 2);
	buf[i++] = path_len >> 8;
	buf[i++] = path_len;
	buf[i++] = attr_name_len >> 8;
	buf[i++] = attr_name_len;
	memcpy(buf + i, path, path_len); i += path_len;
	memcpy(buf + i, attr_name, attr_name_len); i += attr_name_len;
	r = rgfs_send_recv("getxattr", buf, i, buf, sizeof(buf));

	i = 4;
	while (i < r) {
		cmd = buf[i++];
		//xlog(" cmd=0x%02hhx\n", cmd);
		switch (cmd) {
		case 0x00: err = - be32toh(*(uint32_t *) (buf + i)); i += 4; break;
		case 0x01:
			u16 = be16toh(*(uint16_t *) (buf + i)); i += 2;
			if (u16 > sizeof(path) - 1) {
				xlog("attr value len bigger than buf!\n");
				i += u16;
				break;
			}
			if (u16 < value_size)
				value_size = u16;
			memcpy(value, buf + i, value_size); i += u16;
			value[value_size] = '\0';
			err = value_size;
			break;
		default: xlog("Unexpected subcode 0x%02hhx!\n", cmd); exit(EXIT_FAILURE);
		}
	}

	if (err < 0)
		xlog(" server returned error %d!\n", err);

	return err;
}

static const struct fuse_operations fuse_rgfs = {
	.getattr	= rgfs_fuse_getattr,
	.readdir	= rgfs_fuse_readdir,
	.open		= rgfs_fuse_open,
	.create		= rgfs_fuse_create,
	.mkdir		= rgfs_fuse_mkdir,
	.unlink		= rgfs_fuse_unlink,
	.rmdir		= rgfs_fuse_rmdir,
	.truncate	= rgfs_fuse_truncate,
	.read		= rgfs_fuse_read,
	.write		= rgfs_fuse_write,
	.rename		= rgfs_fuse_rename,
	.utimens	= rgfs_fuse_utimens,
	.readlink	= rgfs_fuse_readlink,
	.symlink	= rgfs_fuse_symlink,
	.link		= rgfs_fuse_link,
	.chown		= rgfs_fuse_chown,
	.chmod		= rgfs_fuse_chmod,
	.getxattr	= rgfs_fuse_getxattr
};

int main(int argc, char *argv[])
{
	int r;
	char *s;

	s = getenv("RGFS_DEBUG");
	if (s)
		rgfs_debug = atoi(s);

	s = getenv("RGFS_LOG");
	if (s)
		rgfs_log = s;
	rgfs_log_fd = open(rgfs_log, O_CREAT | O_WRONLY | O_TRUNC | O_APPEND, 0600);
	if (rgfs_log_fd == -1)
		fprintf(stderr, "Cannot open %s: %m!\n", rgfs_log);

	s = getenv("RGFS_SERVER");
	if (s)
		rgfs_server = s;

	s = getenv("RGFS_PROTO");
	if (s)
		rgfs_proto = s;

	s = getenv("RGFS_PORT");
	if (s)
		rgfs_port = atoi(s);

	s = getenv("RGFS_URL");
	if (s)
		rgfs_url = s;

	xlog("server=%s proto=%s port=%d url=%s\n",
		rgfs_server, rgfs_proto, rgfs_port, rgfs_url);

	if (gnutls_check_version("3.4.6") == NULL) {
		xlog("GnuTLS 3.4.6 or later is required!\n");
		exit(EXIT_FAILURE);
	}

	/* for backwards compatibility with gnutls < 3.3.0 */
	r = gnutls_global_init();
	if (r != GNUTLS_E_SUCCESS) {
		xlog("gnutls error: cannot init!\n");
		exit(EXIT_FAILURE);
	}

	/* X509 stuff */
	r = gnutls_certificate_allocate_credentials(&xcred);
	if (r != GNUTLS_E_SUCCESS) {
		xlog("gnutls error: cannot allocate credentials!\n");
		exit(EXIT_FAILURE);
	}

	/* sets the system trusted CAs for Internet PKI */
	r = gnutls_certificate_set_x509_system_trust(xcred);
	if (r < 0) {
		xlog("gnutls error: cannot set system trust [%d]!\n", r);
		exit(EXIT_FAILURE);
	}

	/* If client holds a certificate it can be set using the following:
	gnutls_certificate_set_x509_key_file (xcred, "cert.pem", "key.pem",
		GNUTLS_X509_FMT_PEM);
	*/

	xlog("Running fuse...\n");
	return fuse_main(argc, argv, &fuse_rgfs, NULL);

	#if 0
	if (r < 0 && gnutls_error_is_fatal(r) == 0) {
		xlog("Warning: %s\n", gnutls_strerror(r));
	} else if (r < 0) {
		xlog("Error: %s\n", gnutls_strerror(r));
		exit(EXIT_FAILURE);
	}
	#endif
}



Mode Type Size Ref File
100644 blob 150 322462071db30d3eb40ccaea4e4858fe2d0cf9c9 .gitignore
100644 blob 34520 dba13ed2ddf783ee8118c6a581dbf75305f816a3 LICENSE
100644 blob 623 a3b52b031bee0831ae626631166a994c6e716ad7 Makefile.in
100644 blob 29 e214257f87a28e8fb0413b627cf7ee76ade2e94c Makefile.include.in
100644 blob 320 51f5dbc45187fa9b0485cd2f39a1f5004c1ea292 README
100644 blob 1462 6b8c543bad406014e37c946b632915a01acf1287 TODO
100755 blob 31 382d4ea2c0c98b1b25ea01f1e194cfc4990ac527 configure
040000 tree - 923654dfd6c378cf072edc658d3136326f1574c9 debian
100755 blob 18252 e2438615edba7066a730ed6a796a5302263f1f37 duilder
100644 blob 1298 a64e87d5459fc9441a20202b3e82c60eb0bb8761 duilder.conf
100644 blob 31213 b865abc85ee496f9d431715907a42123fa5cb1ca rgfs.c
100644 blob 893 43c94e45a5a57d69f5cc6b662dc5c6b6279dfaad rgfs.spec
100644 blob 253 4103978bae579ac9509c42c153fbcddf5e1e3af7 rgfs_config.h.in
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/catalinux/rgfs

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/catalinux/rgfs

Clone this repository using git:
git clone git://git.rocketgit.com/user/catalinux/rgfs

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main