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

/rgfs.c (30688dbc84e8ae48b3d27c70404adc9e0b8dad82) (29508 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_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;

	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);
	}

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

	if (r < 0) {
		xlog("Cannot send [%zd]!\n", r);
		exit(EXIT_FAILURE);
        } else if ((size_t) r < buf_len) {
		xlog("Invalid send r=%ld < buf_len=%zu!\n", r, buf_len);
		exit(EXIT_FAILURE);
	}

	return r;
}

/*
 * 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);

		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 == -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 (off=%d to_read=%u)\n", 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 (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 = off > 4096 ? 4096 : off;
			for (uint16_t i = 0; i < max; i++)
				sprintf(debug + i * 2, "%02hhx", * (unsigned char *) (buf + i));
			xlog("  %s: RECV %u: %s\n", op, off, debug);
		}

		if (off == 4 + to_read) {
			xlog(" Readed 4 + %u\n", to_read);
			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++] = (len - 1 - 2) >> 8;
	buf[i++] = len - 1 - 2;
	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;
	if (len > sizeof(buf)) {
		xlog(" buffer too small\n");
		return -EIO;
	}

	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[16 * 4096], cmd;
	uint16_t path_len, len;
	uint64_t u64;

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

	// Limit how much data we request to the server
	// 1 + 4 + 8 is the answer's header.
	if (size > sizeof(buf) - 1 - 4)
		size = sizeof(buf) - 1 - 4;

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

	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, 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 */
			xlog("  received %llu bytes block\n", r - i);
			memcpy(out, buf + i, r - i);
			err = r - 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_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[4 * 4096], cmd;
	uint16_t path_len, len;
	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)) {
		xlog(" buffer too small\n");
		return -EIO;
	}

	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 0;
}

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 0;
}

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 0;
}

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
};

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_PORT");
	if (s)
		rgfs_port = atoi(s);

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

	xlog("server=%s port=%d url=%s\n", rgfs_server, 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 160 52a45e91ab9c3d923b6c401dd14b1cfc0fed12eb .gitignore
100644 blob 34520 dba13ed2ddf783ee8118c6a581dbf75305f816a3 LICENSE
100644 blob 665 9467d461f79bf4723b7de94db043b2cb1fe57754 Makefile.in
100644 blob 29 e214257f87a28e8fb0413b627cf7ee76ade2e94c Makefile.include.in
100644 blob 302 ad312665f4c0e01302a79eba2e5681c4a002e3f2 README
100644 blob 195 9a1f283f5ae0a1189f1613a36e5c48e129796823 TODO
100755 blob 30 92c4bc48245c00408cd7e1fd89bc1a03058f4ce4 configure
100755 blob 17506 9320430ec7cae0ebd1f2edd0ca465674fd92dc7c duilder
100644 blob 1374 89a257998e246a24805bf0e5e53807013ae2c284 duilder.conf
100644 blob 29508 30688dbc84e8ae48b3d27c70404adc9e0b8dad82 rgfs.c
100644 blob 837 83798611843ddcee6436ae28d1adf3e92c446b3e rgfs.spec.in
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 master