/*
* 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 == 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, 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))
size -= len - sizeof(buf);
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 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
}