/* * $Id; stfufs-client-fuse.c $ * * Author: Tomi Ollila -- too ät iki piste fi * * Copyright (c) 2007 Tomi Ollila * All rights reserved * * Created: Tue Aug 28 21:47:30 EEST 2007 too * Last modified: Tue Jan 29 19:17:06 EET 2008 too */ #include #include #include #include #include #include #define FUSE_USE_VERSION 26 #include #include #define STFUFS_CLIENT 1 #define STFUFS_PROTOCOL 1 #include "stfufs.h" #include "stfufs-errmaps.h" #include "stfufs-buffer.h" #define SIMPLEXDR_INLINE #include "simplexdr.h" #define AMIGA_LIST_INLINE #include "amiga_list.h" #include "version.h" #ifndef null #define null ((void *)0) #endif /* #define stfufs_getattr null */ /* #define stfufs_readlink null */ #define stfufs_getdir null /* #define stfufs_mknod null */ /* #define stfufs_mkdir null */ /* #define stfufs_unlink null */ /* #define stfufs_rmdir null */ /* #define stfufs_symlink null */ /* #define stfufs_rename null */ /* #define stfufs_link null */ /* #define stfufs_chmod null */ /* #define stfufs_chown null */ /* #define stfufs_truncate null */ /* #define stfufs_utime null */ /* #define stfufs_open null */ /* #define stfufs_read null */ /* #define stfufs_write null */ /* #define stfufs_statfs null */ #define stfufs_flush null /* #define stfufs_release null */ #define stfufs_fsync null #define stfufs_setxattr null #define stfufs_getxattr null #define stfufs_listxattr null #define stfufs_removexattr null #define stfufs_opendir null /* #define stfufs_readdir null */ #define stfufs_releasedir null #define stfufs_fsyncdir null /*#define stfufs_init null */ #define stfufs_destroy null #define stfufs_access null #define stfufs_create null #define stfufs_ftruncate null #define stfufs_fgetattr null #define stfufs_lock null #define stfufs_utimens null #define stfufs_bmap null #define CONCURRENT_REQUESTS 64 struct { int fd; pthread_mutex_t entry_mutex; pthread_cond_t entry_cond; pthread_mutex_t write_mutex; int pending_requests; AList freerequests; struct RequestNode { AListNode listnode; uint32_t type; uint32_t refno; pthread_mutex_t node_mutex; pthread_cond_t node_cond; uint8_t * (*cbfunc)(void * cbdata, uint32_t rlen, uint8_t * p); void * cbdata; int32_t errno_; } requests[CONCURRENT_REQUESTS]; } G; static uint8_t * stfufs_invalid(void * cbdata, uint32_t rlen, uint8_t * p) { (void)cbdata; d1(("--- %s(%p, %d, %p)", __func__, cbdata, rlen, p)); return stfufs_buffer_discard(p, (rlen + 3) & ~3); } /* this is called before fuse_main() */ static void stfufs_init_g(void) { int i; d1(("%s(): pid %d", __func__, getpid())); memset(&G, 0, sizeof G); pthread_mutex_init(&G.entry_mutex, NULL); pthread_mutex_init(&G.write_mutex, NULL); pthread_cond_init(&G.entry_cond, NULL); amiga_list_init(&G.freerequests); for (i = 0; i < CONCURRENT_REQUESTS; i++) { pthread_mutex_init(&G.requests[i].node_mutex, NULL); pthread_cond_init(&G.requests[i].node_cond, NULL); G.requests[i].refno = i; G.requests[i].cbfunc = stfufs_invalid; G.requests[i].cbdata = null; amiga_list_addtail(&G.freerequests, &G.requests[i].listnode); } } /* inline functions are part of C99 standard. */ static inline char* str_unconst(const char* p) { return p; } /* like GNU mc */ static /* inline */ int stfufs_client_request_handle(int type, struct iovec* iov, int iovlen, int clen, uint8_t * (*cbfunc)(void *, uint32_t, uint8_t *), void * cbdata) { struct RequestNode * rn; int error, wl; uint8_t * p = iov[0].iov_base; pthread_mutex_lock(&G.entry_mutex); if (G.pending_requests >= CONCURRENT_REQUESTS) { do pthread_cond_wait(&G.entry_cond, &G.entry_mutex); while (G.pending_requests >= CONCURRENT_REQUESTS); } G.pending_requests++; rn = (struct RequestNode *)amiga_list_remheadq(&G.freerequests); pthread_mutex_unlock(&G.entry_mutex); pthread_mutex_lock(&rn->node_mutex); rn->type = type; p = xencode_uint32(p, type); /* index = rn - G.requests; */ p = xencode_uint32(p, rn->refno); p = xencode_uint32(p, clen); #define REQUEST_HEADER_LEN 12 rn->cbfunc = cbfunc; rn->cbdata = cbdata; pthread_mutex_lock(&G.write_mutex); wl = writev(G.fd, iov, iovlen); pthread_mutex_unlock(&G.write_mutex); d1(("(%d = writev(%d, %p, %d)) != %d (%s)", wl, G.fd, iov, iovlen, clen + REQUEST_HEADER_LEN, strerror(errno))); /* w/ this cannot use non-4-divisible clen */ if (wl == clen + REQUEST_HEADER_LEN) { rn->errno_ = -1; do pthread_cond_wait(&rn->node_cond, &rn->node_mutex); while (rn->errno_ < 0); error = rn->errno_; } else error = EACCES; rn->refno += 0x100; /* XXX define, match with 0xFF.. */ pthread_mutex_unlock(&rn->node_mutex); pthread_mutex_lock(&G.entry_mutex); amiga_list_addtail(&G.freerequests, &rn->listnode); if (G.pending_requests >= CONCURRENT_REQUESTS) pthread_cond_signal(&G.entry_cond); G.pending_requests--; pthread_mutex_unlock(&G.entry_mutex); return -error; } static void * stfufs_client_reader_thread(void * arg_unused) { uint8_t * p = stfufs_buffer_init(G.fd); (void)arg_unused; while (1) { uint32_t type, index_, refno, errno_, rlen; struct RequestNode * rn; p = stfufs_buffer_reserve(p, 16); if (p == null) break; /* XXX */ type = xdecode_uint32(&p); refno = xdecode_uint32(&p); errno_ = xdecode_uint32(&p); rlen = xdecode_uint32(&p); d1(("response read: type %x refno %x errno %d rlen %d", type, refno, errno_, rlen)); index_ = refno & 0xFF; /* XXX define, match with 0x100 */ if (index_ >= CONCURRENT_REQUESTS) break; /* XXX */ rn = &G.requests[index_]; pthread_mutex_lock(&rn->node_mutex); if (refno != rn->refno) { /* interrupted */ d1(("interrupted -- or an error as interrupts do not arrive yet")); pthread_mutex_unlock(&rn->node_mutex); if ((p = stfufs_buffer_discard(p, (rlen + 3) & ~3)) == null) break; /* XXX */ continue; } if (type != rn->type) break; /* XXX sanity check. */ if (errno_ == 0) /* note: rlen == 0 if errno != 0 */ if ((p = rn->cbfunc(rn->cbdata, rlen, p)) == null) break; /* XXX */ if (errno_ >= stfufs_client_errmap_size) errno_ = EDEFAULT; rn->errno_ = stfufs_client_errmap[errno_]; d1(("signal rn #%d", index_)); pthread_cond_signal(&rn->node_cond); rn->type = 0; rn->cbfunc = stfufs_invalid; pthread_mutex_unlock(&rn->node_mutex); } d1(("exiting reader thread")); /* harakiri (sshfs terms :) */ kill(getpid(), SIGTERM); /* XXX oujes :/ */ return null; } /*#undef stfufs_init */ static void * stfufs_init(struct fuse_conn_info * fci) { pthread_t thread_id; /* XXX */ int maxmsgsize = G.pending_requests; G.pending_requests = 0; d1(("--- stfufs_init(%p) mms %d", fci, maxmsgsize)); /* XXX see signal handling in sshfs (for reference, at least) */ if (pthread_create(&thread_id, NULL, stfufs_client_reader_thread, NULL)) /* XXX error message */ exit(1); pthread_detach(thread_id); fci->async_read = false; fci->max_write = maxmsgsize; fci->max_readahead = maxmsgsize; return null; } static uint8_t * stfufs_getattr_cb(void * cbdata, uint32_t rlen, uint8_t * p) { struct stat * st = (struct stat *)cbdata; (void)rlen; d1(("--- %s(%p, %d, %p)", __func__, cbdata, rlen, p)); if ((p = stfufs_buffer_reserve(p, 8+8+4+4+4+4+8+8+4+8+4+4+4)) == null) return null; st->st_dev = xdecode_uint64(&p); st->st_ino = xdecode_uint64(&p); st->st_mode = xdecode_uint32(&p); st->st_nlink = xdecode_uint32(&p); st->st_uid = xdecode_uint32(&p); st->st_gid = xdecode_uint32(&p); st->st_rdev = xdecode_uint64(&p); st->st_size = xdecode_uint64(&p); st->st_blksize = xdecode_uint32(&p); st->st_blocks = xdecode_uint64(&p); st->st_atime = xdecode_uint32(&p); st->st_mtime = xdecode_uint32(&p); st->st_ctime = xdecode_uint32(&p); return p; } static int stfufs_getattr(const char * path, struct stat * statbuf) { uint8_t buf[32]; struct iovec iov[3]; int iovlen; int plen = strlen(path) + 1; int alen = (plen + 3) & ~3; d1(("--- %s(\"%s\", %p) %d %d", __func__, path, statbuf, plen, alen)); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN; iov[1].iov_base = str_unconst(path); /* const void * when writing... */ iov[1].iov_len = plen; if (plen != alen) { iov[2].iov_base = str_unconst(G_pad000); /*const void * when writing */ iov[2].iov_len = alen - plen; iovlen = 3; } else iovlen = 2; return stfufs_client_request_handle(STFUFS__GETATTR, iov, iovlen, alen, stfufs_getattr_cb, (void*)statbuf); } static uint8_t * stfufs_readlink_cb(void * cbdata, uint32_t rlen, uint8_t * p) { char * lbuf = ((struct iovec *)cbdata)->iov_base; size_t size = ((struct iovec *)cbdata)->iov_len; size_t min; int alen = (rlen + 3) & ~3; d1(("--- %s(%p, %d, %p)", __func__, cbdata, rlen, p)); if ((p = stfufs_buffer_reserve(p, alen)) == null) return null; /* actually, it is an error if rlen > size, protecting for such */ min = size < rlen? size: rlen; ((struct iovec *)cbdata)->iov_len = min; /* scatter ACN */ memcpy(lbuf, p, min); /* server NUL-terminates */ return p + alen; } static int stfufs_readlink(const char * path, char * lbuf, size_t size) { uint8_t buf[32]; struct iovec iov[3]; int iovlen; int plen = strlen(path) + 1; int alen = (plen + 3) & ~3; struct iovec lbufdata; int error; d1(("--- %s(\"%s\", %p, %d)", __func__, path, lbuf, size)); /* XXX use sizeof BUFFER.b - xxx */ /* using scatter read will make this limit server-bound, later... */ if (size > 4100) size = 4100; (void)xencode_uint32(buf + REQUEST_HEADER_LEN, size); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN + 4; iov[1].iov_base = str_unconst(path); /* const void * when writing... */ iov[1].iov_len = plen; if (plen != alen) { iov[2].iov_base = str_unconst(G_pad000); /*const void * when writing */ iov[2].iov_len = alen - plen; iovlen = 3; } else iovlen = 2; alen += 4; lbufdata.iov_base = lbuf; lbufdata.iov_len = size; error = stfufs_client_request_handle(STFUFS__READLINK, iov, iovlen, alen, stfufs_readlink_cb, (void*)&lbufdata); if (error < 0) return error; if (lbufdata.iov_len > 4097) return ENAMETOOLONG; d1(("readlink rv: %d", lbufdata.iov_len)); #if 0 return lbufdata.iov_len; #else return 0; #endif } static uint8_t * stfufs_ok_cb(void * cbdata, uint32_t rlen, uint8_t * p) { (void)cbdata; (void)rlen; d1(("--- %s(%p, %d, %p)", __func__, cbdata, rlen, p)); return p; } static int stfufs_mknod(const char * path, mode_t mode, dev_t dev) { uint8_t buf[32], *p; struct iovec iov[3]; int iovlen; int plen = strlen(path) + 1; int alen = (plen + 3) & ~3; d1(("--- %s(\"%s\", %o, %lld)", __func__, path, mode, dev)); p = xencode_uint32(buf + REQUEST_HEADER_LEN, mode); (void)xencode_uint64(p, dev); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN + 4 + 8; iov[1].iov_base = str_unconst(path); /* const void * when writing... */ iov[1].iov_len = plen; if (plen != alen) { iov[2].iov_base = str_unconst(G_pad000); /*const void * when writing */ iov[2].iov_len = alen - plen; iovlen = 3; } else iovlen = 2; alen += 4 + 8; return stfufs_client_request_handle(STFUFS__MKNOD, iov, iovlen, alen, stfufs_ok_cb, null); } static int stfufs_mkdir(const char * path, mode_t mode) { uint8_t buf[32]; struct iovec iov[3]; int iovlen; int plen = strlen(path) + 1; int alen = (plen + 3) & ~3; d1(("--- %s(\"%s\", %o)", __func__, path, mode)); (void)xencode_uint32(buf + REQUEST_HEADER_LEN, mode); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN + 4; iov[1].iov_base = str_unconst(path); /* const void * when writing... */ iov[1].iov_len = plen; if (plen != alen) { iov[2].iov_base = str_unconst(G_pad000); /*const void * when writing */ iov[2].iov_len = alen - plen; iovlen = 3; } else iovlen = 2; alen += 4; return stfufs_client_request_handle(STFUFS__MKDIR, iov, iovlen, alen, stfufs_ok_cb, null); } static int stfufs_unlink(const char * path) { uint8_t buf[32]; struct iovec iov[3]; int iovlen; int plen = strlen(path) + 1; int alen = (plen + 3) & ~3; d1(("--- %s(\"%s\")", __func__, path)); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN; iov[1].iov_base = str_unconst(path); /* const void * when writing... */ iov[1].iov_len = plen; if (plen != alen) { iov[2].iov_base = str_unconst(G_pad000); /*const void * when writing */ iov[2].iov_len = alen - plen; iovlen = 3; } else iovlen = 2; return stfufs_client_request_handle(STFUFS__UNLINK, iov, iovlen, alen, stfufs_ok_cb, null); } /* exactly the same as above. refactor! */ static int stfufs_rmdir(const char * path) { uint8_t buf[32]; struct iovec iov[3]; int iovlen; int plen = strlen(path) + 1; int alen = (plen + 3) & ~3; d1(("--- %s(\"%s\")", __func__, path)); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN; iov[1].iov_base = str_unconst(path); /* const void * when writing... */ iov[1].iov_len = plen; if (plen != alen) { iov[2].iov_base = str_unconst(G_pad000); /*const void * when writing */ iov[2].iov_len = alen - plen; iovlen = 3; } else iovlen = 2; return stfufs_client_request_handle(STFUFS__RMDIR, iov, iovlen, alen, stfufs_ok_cb, null); } static int stfufs_symlink(const char * from, const char * to) { uint8_t buf[32]; struct iovec iov[4]; int iovlen; int flen = strlen(from) + 1; int tlen = strlen(to) + 1; int plen = flen + tlen; int alen = (plen + 3) & ~3; d1(("--- %s(\"%s\", \"%s\")", __func__, from, to)); (void)xencode_uint32(buf + REQUEST_HEADER_LEN, flen); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN + 4; iov[1].iov_base = str_unconst(from); /* const void * when writing... */ iov[1].iov_len = flen; iov[2].iov_base = str_unconst(to); /* const void * when writing... */ iov[2].iov_len = tlen; if (plen != alen) { iov[3].iov_base = str_unconst(G_pad000); /*const void * when writing */ iov[3].iov_len = alen - plen; iovlen = 4; } else iovlen = 3; alen += 4; return stfufs_client_request_handle(STFUFS__SYMLINK, iov, iovlen, alen, stfufs_ok_cb, null); } /* exactly the same as above. refactor! */ static int stfufs_rename(const char * from, const char * to) { uint8_t buf[32]; struct iovec iov[4]; int iovlen; int flen = strlen(from) + 1; int tlen = strlen(to) + 1; int plen = flen + tlen; int alen = (plen + 3) & ~3; d1(("--- %s(\"%s\", \"%s\")", __func__, from, to)); (void)xencode_uint32(buf + REQUEST_HEADER_LEN, flen); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN + 4; iov[1].iov_base = str_unconst(from); /* const void * when writing... */ iov[1].iov_len = flen; iov[2].iov_base = str_unconst(to); /* const void * when writing... */ iov[2].iov_len = tlen; if (plen != alen) { iov[3].iov_base = str_unconst(G_pad000); /*const void * when writing */ iov[3].iov_len = alen - plen; iovlen = 4; } else iovlen = 3; alen += 4; return stfufs_client_request_handle(STFUFS__RENAME, iov, iovlen, alen, stfufs_ok_cb, null); } /* exactly the same as above. refactor! */ static int stfufs_link(const char * from, const char * to) { uint8_t buf[32]; struct iovec iov[4]; int iovlen; int flen = strlen(from) + 1; int tlen = strlen(to) + 1; int plen = flen + tlen; int alen = (plen + 3) & ~3; d1(("--- %s(\"%s\", \"%s\")", __func__, from, to)); (void)xencode_uint32(buf + REQUEST_HEADER_LEN, flen); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN + 4; iov[1].iov_base = str_unconst(from); /* const void * when writing... */ iov[1].iov_len = flen; iov[2].iov_base = str_unconst(to); /* const void * when writing... */ iov[2].iov_len = tlen; if (plen != alen) { iov[3].iov_base = str_unconst(G_pad000); /*const void * when writing */ iov[3].iov_len = alen - plen; iovlen = 4; } else iovlen = 3; alen += 4; return stfufs_client_request_handle(STFUFS__LINK, iov, iovlen, alen, stfufs_ok_cb, null); } /* like mkdir. refactor */ static int stfufs_chmod(const char * path, mode_t mode) { uint8_t buf[32]; struct iovec iov[3]; int iovlen; int plen = strlen(path) + 1; int alen = (plen + 3) & ~3; d1(("--- %s(\"%s\", %o)", __func__, path, mode)); (void)xencode_uint32(buf + REQUEST_HEADER_LEN, mode); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN + 4; iov[1].iov_base = str_unconst(path); /* const void * when writing... */ iov[1].iov_len = plen; if (plen != alen) { iov[2].iov_base = str_unconst(G_pad000); /*const void * when writing */ iov[2].iov_len = alen - plen; iovlen = 3; } else iovlen = 2; alen += 4; return stfufs_client_request_handle(STFUFS__CHMOD, iov, iovlen, alen, stfufs_ok_cb, null); } static int stfufs_chown(const char * path, uid_t uid, gid_t gid) { uint8_t buf[32], *p; struct iovec iov[3]; int iovlen; int plen = strlen(path) + 1; int alen = (plen + 3) & ~3; d1(("--- %s(\"%s\", %d, %d)", __func__, path, uid, gid)); p = xencode_uint32(buf + REQUEST_HEADER_LEN, uid); (void) xencode_uint32(p, gid); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN + 8; iov[1].iov_base = str_unconst(path); /* const void * when writing... */ iov[1].iov_len = plen; if (plen != alen) { iov[2].iov_base = str_unconst(G_pad000); /*const void * when writing */ iov[2].iov_len = alen - plen; iovlen = 3; } else iovlen = 2; alen += 8; return stfufs_client_request_handle(STFUFS__CHOWN, iov, iovlen, alen, stfufs_ok_cb, null); } static int stfufs_truncate(const char * path, off_t size) { uint8_t buf[32]; struct iovec iov[3]; int iovlen; int plen = strlen(path) + 1; int alen = (plen + 3) & ~3; d1(("--- %s(\"%s\", %lld)", __func__, path, size)); (void)xencode_uint64(buf + REQUEST_HEADER_LEN, size); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN + 8; iov[1].iov_base = str_unconst(path); /* const void * when writing... */ iov[1].iov_len = plen; if (plen != alen) { iov[2].iov_base = str_unconst(G_pad000); /*const void * when writing */ iov[2].iov_len = alen - plen; iovlen = 3; } else iovlen = 2; alen += 8; return stfufs_client_request_handle(STFUFS__TRUNCATE, iov, iovlen, alen, stfufs_ok_cb, null); } /* XXX deprecated, just noticed (2008-01-05 16:05) */ static int stfufs_utime(const char * path, struct utimbuf * utbuf) { uint8_t buf[32], *p; struct iovec iov[3]; int iovlen; int plen = strlen(path) + 1; int alen = (plen + 3) & ~3; d1(("--- %s(\"%s\", %p)", __func__, path, utbuf)); p = xencode_uint32(buf + REQUEST_HEADER_LEN, utbuf->actime); (void)xencode_uint32(p, utbuf->modtime); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN + 8; iov[1].iov_base = str_unconst(path); /* const void * when writing... */ iov[1].iov_len = plen; if (plen != alen) { iov[2].iov_base = str_unconst(G_pad000); /*const void * when writing */ iov[2].iov_len = alen - plen; iovlen = 3; } else iovlen = 2; alen += 8; return stfufs_client_request_handle(STFUFS__UTIME, iov, iovlen, alen, stfufs_ok_cb, null); } static uint8_t * stfufs_retval_cb(void * cbdata, uint32_t rlen, uint8_t * p) { uint32_t * rv32p = (uint32_t *)cbdata; (void)rlen; d1(("--- %s(%p, %d, %p)", __func__, cbdata, rlen, p)); if ((p = stfufs_buffer_reserve(p, 4)) == null) return null; *rv32p = xdecode_uint32(&p); return p; } static int stfufs_open(const char * path, struct fuse_file_info * fi) { uint8_t buf[32]; struct iovec iov[3]; int iovlen, error; int plen = strlen(path) + 1; int alen = (plen + 3) & ~3; uint32_t fd; d1(("--- %s(\"%s\", %p) %d", __func__, path, fi, fi->flags)); (void)xencode_uint32(buf + REQUEST_HEADER_LEN, fi->flags); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN + 4; iov[1].iov_base = str_unconst(path); /* const void * when writing... */ iov[1].iov_len = plen; if (plen != alen) { iov[2].iov_base = str_unconst(G_pad000); /*const void * when writing */ iov[2].iov_len = alen - plen; iovlen = 3; } else iovlen = 2; alen += 4; error = stfufs_client_request_handle(STFUFS__OPEN, iov, iovlen, alen, stfufs_retval_cb, (void *)&fd); if (error < 0) return error; fi->fh = fd; return 0; } static uint8_t * stfufs_read_cb(void * cbdata, uint32_t rlen, uint8_t * p) { uint8_t * data = ((struct iovec *)cbdata)->iov_base; size_t size = ((struct iovec *)cbdata)->iov_len; uint32_t i, alen; d1(("--- %s(%p, %d, %p) size %d", __func__, cbdata, rlen, p, size)); alen = rlen & ~3; if (rlen < size) memset(data + alen, 0, size - alen); ((struct iovec *)cbdata)->iov_len = rlen; /* return value */ i = stfufs_buffer_rest(p); if (i >= rlen) { memcpy(data, p, rlen); if (rlen == alen) return p + alen; return stfufs_buffer_discard(p + alen, 4); /* return stfufs_buffer_discard(p, (rlen + 3) & ~3); */ } memcpy(data, p, i); return stfufs_buffer_scatter(data, i, rlen); } /* Note: sparse file support would be nice (but not trivial) */ static int stfufs_read(const char * path, char * data, size_t size, off_t offset, struct fuse_file_info * fi) { uint8_t buf[32], *p; struct iovec iov[1]; struct iovec readdata; int error; (void)path; d1(("--- %s(\"%s\", %d, %lld)", __func__, path, size, offset)); p = xencode_uint32(buf + REQUEST_HEADER_LEN, (uint32_t)fi->fh); p = xencode_uint32(p, size); (void)xencode_uint64(p, offset); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN + 4 + 4 + 8; readdata.iov_base = data; readdata.iov_len = size; error = stfufs_client_request_handle(STFUFS__READ, iov, 1, 4 + 4 + 8, stfufs_read_cb, &readdata); if (error < 0) return error; return readdata.iov_len; } static int stfufs_write(const char * path, const char * data, size_t size, off_t offset, struct fuse_file_info * fi) { uint8_t buf[32], *p; struct iovec iov[3]; int iovlen, error, alen; uint32_t rlen; (void)path; d1(("--- %s(\"%s\", %d, %lld)", __func__, path, size, offset)); p = xencode_uint32(buf + REQUEST_HEADER_LEN, (uint32_t)fi->fh); p = xencode_uint32(p, size); p = xencode_uint64(p, offset); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN + 4 + 4 + 8; iov[1].iov_base = str_unconst(data); /* const void * when writing... */ iov[1].iov_len = size; if ((size & 3) != 0) { int pad = 4 - (size & 3); iov[2].iov_base = str_unconst(G_pad000); iov[2].iov_len = pad; size += pad; iovlen = 3; } else iovlen = 2; alen = 4 + 8 + 4 + size; error = stfufs_client_request_handle(STFUFS__WRITE, iov, iovlen, alen, stfufs_retval_cb, (void *)&rlen); if (error < 0) return error; return (int)rlen; /* XXX */ } static uint8_t * stfufs_statfs_cb(void * cbdata, uint32_t rlen, uint8_t * p) { struct statvfs * stbuf = (struct statvfs *)cbdata; (void)rlen; d1(("--- %s(%p, %d, %p)", __func__, cbdata, rlen, p)); if ((p = stfufs_buffer_reserve(p, 2*4 + 6*8 + 3*4)) == null) return null; stbuf->f_bsize = xdecode_uint32(&p); stbuf->f_frsize = xdecode_uint32(&p); stbuf->f_blocks = xdecode_uint64(&p); stbuf->f_bfree = xdecode_uint64(&p); stbuf->f_bavail = xdecode_uint64(&p); stbuf->f_files = xdecode_uint64(&p); stbuf->f_ffree = xdecode_uint64(&p); stbuf->f_favail = xdecode_uint64(&p); stbuf->f_fsid = xdecode_uint32(&p); stbuf->f_flag = xdecode_uint32(&p); stbuf->f_namemax = xdecode_uint32(&p); return p; } static int stfufs_statfs(const char * path, struct statvfs * stbuf) { uint8_t buf[32]; struct iovec iov[3]; int iovlen; int plen = strlen(path) + 1; int alen = (plen + 3) & ~3; d1(("--- %s(\"%s\", %p) %d %d", __func__, path, buf, plen, alen)); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN; iov[1].iov_base = str_unconst(path); /* const void * when writing... */ iov[1].iov_len = plen; if (plen != alen) { iov[2].iov_base = str_unconst(G_pad000); /*const void * when writing */ iov[2].iov_len = alen - plen; iovlen = 3; } else iovlen = 2; return stfufs_client_request_handle(STFUFS__STATFS, iov, iovlen, alen, stfufs_statfs_cb, (void*)stbuf); } static int stfufs_release(const char * path, struct fuse_file_info * fi) { uint8_t buf[32], *p; struct iovec iov[1]; (void)path; d1(("--- %s(\"%s\", %lld)", __func__, path, fi->fh)); p = xencode_uint32(buf + REQUEST_HEADER_LEN, (uint32_t)fi->fh); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN + 4; return stfufs_client_request_handle(STFUFS__RELEASE, iov, 1, 4, stfufs_ok_cb, null); } struct stfufs_readdir_st { void * fbuf; fuse_fill_dir_t filler; off_t offset; }; static uint8_t * stfufs_readdir_cb(void * cbdata, uint32_t rlen, uint8_t * p) { struct stfufs_readdir_st * rdd_data = (struct stfufs_readdir_st *)cbdata; void * fbuf = rdd_data->fbuf; fuse_fill_dir_t filler = rdd_data->filler; off_t offset = rdd_data->offset; bool fill = true; struct stat st; memset(&st, 0, sizeof st); d1(("--- %s(%p, %d, %p)", __func__, cbdata, rlen, p)); while (rlen != 0) { uint8_t * q; if ((p = stfufs_buffer_reserve(p, rlen)) == null) return null; q = p + rlen; st.st_ino = xdecode_uint64(&p); st.st_mode = xdecode_uint8(&p) << 12; d1(("dir entry: %s", p)); if (fill && filler(fbuf, U2S(p, char, *, *), &st, offset) != 0) fill = false; /* next header */ p = stfufs_buffer_reserve(q, 16); #if 0 type = xdecode_uint32(&p); refno = xdecode_uint32(&p); errno_ = xdecode_uint32(&p); #else p += 12; #endif rlen = xdecode_uint32(&p); } return p; } static int stfufs_readdir(const char* path, void* fbuf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info * fi) { uint8_t buf[32]; struct iovec iov[3]; int iovlen; int plen = strlen(path) + 1; int alen = (plen + 3) & ~3; struct stfufs_readdir_st rdd_data; rdd_data.fbuf = fbuf; rdd_data.filler = filler; rdd_data.offset = offset; (void)fi; d1(("--- %s(\"%s\", %p) %d %d", __func__, path, buf, plen, alen)); (void)xencode_uint64(buf + REQUEST_HEADER_LEN, offset); iov[0].iov_base = buf; /* stfufs_client_request_handle() will fill this */ iov[0].iov_len = REQUEST_HEADER_LEN + 8; iov[1].iov_base = str_unconst(path); /* const void * when writing... */ iov[1].iov_len = plen; if (plen != alen) { iov[2].iov_base = str_unconst(G_pad000); /*const void * when writing */ iov[2].iov_len = alen - plen; iovlen = 3; } else iovlen = 2; alen += 8; return stfufs_client_request_handle(STFUFS__READDIR, iov, iovlen, alen, stfufs_readdir_cb, (void *)&rdd_data); } struct fuse_operations stfufs_operations = { /*getattr*/ stfufs_getattr, /*readlink*/ stfufs_readlink, /*getdir*/ stfufs_getdir, /*mknod*/ stfufs_mknod, /*mkdir*/ stfufs_mkdir, /*unlink*/ stfufs_unlink, /*rmdir*/ stfufs_rmdir, /*symlink*/ stfufs_symlink, /*rename*/ stfufs_rename, /*link*/ stfufs_link, /*chmod*/ stfufs_chmod, /*chown*/ stfufs_chown, /*truncate*/ stfufs_truncate, /*utime*/ stfufs_utime, /*open*/ stfufs_open, /*read*/ stfufs_read, /*write*/ stfufs_write, /*statfs*/ stfufs_statfs, /*flush*/ stfufs_flush, /*release*/ stfufs_release, /*fsync*/ stfufs_fsync, /*setxattr*/ stfufs_setxattr, /*getxattr*/ stfufs_getxattr, /*listxattr*/ stfufs_listxattr, /*removexattr*/ stfufs_removexattr, /* 2.3 */ /*opendir*/ stfufs_opendir, /*readdir*/ stfufs_readdir, /*releasedir*/ stfufs_releasedir, /*fsyncdir*/ stfufs_fsyncdir, /*init*/ stfufs_init, /*destroy*/ stfufs_destroy, /* 2.5 */ /*access*/ stfufs_access, /*create*/ stfufs_create, /*ftruncate*/ stfufs_ftruncate, /*fgetattr*/ stfufs_fgetattr, /* 2.6 */ /*lock*/ stfufs_lock, /*utimens*/ stfufs_utimens, /*bmap*/ stfufs_bmap }; void usage(char * progname) { fprintf(stderr, "\nUsage: %s [user@]host:path mountpoint [options]\n", progname); fprintf(stderr, "\n Options:\n\n" "\t -c port -- connect to given server port\n" "\t -b port -- bind to port so server can connect back there\n" "\t -p [host:][port] -- proxy traffic via this host/port\n" "\n\t Without any of the above options given server\n" "\t chooses suitable port for client to connect.\n\n" "\t -v -- stfufs version information\n" ); fprintf(stderr, "\n Example: %s too@remote:Mail remoteMail\n\n", progname); } enum { KEY_CONN = 1, KEY_BIND, KEY_PROXY, KEY_HELP, KEY_VERSION }; static int stfufs_handle_args(void * data, const char * arg, int key, struct fuse_args * outargs) { struct clientargs * cargs = (struct clientargs *)data; d1(("key: %d, arg: '%s'", key, arg)); switch (key) { case FUSE_OPT_KEY_OPT: d0(("option '%s'", arg)); return 1; /* we do not handle this option */ case FUSE_OPT_KEY_NONOPT: return stfufs_client_remotehost_arg(cargs, arg); case KEY_CONN: return stfufs_client_conn_arg(cargs, arg); case KEY_BIND: return stfufs_client_bind_arg(cargs, arg); case KEY_PROXY: return stfufs_client_proxy_arg(cargs, arg); case KEY_HELP: usage(outargs->argv[0]); fuse_opt_add_arg(outargs, "-ho"); fuse_main(outargs->argc, outargs->argv, &stfufs_operations, null); exit(1); case KEY_VERSION: { extern const char ident[]; /* XXX from proper header, please */ fprintf(stderr, "\n %s\n", ident); } exit(0); default: die("arg parse internal error: key %d:%s", key, arg); } } static struct fuse_opt stfufs_opts[] = { FUSE_OPT_KEY("-c ", KEY_CONN), FUSE_OPT_KEY("-b ", KEY_BIND), FUSE_OPT_KEY("-p ", KEY_PROXY), FUSE_OPT_KEY("-h", KEY_HELP), FUSE_OPT_KEY("--help", KEY_HELP), FUSE_OPT_KEY("-v", KEY_VERSION), { null, 0, 0 } /* FUSE_OPT_END */ }; static void stfufs_client_set_fuse_fsname_arg(struct clientargs * cargs, struct fuse_args * fargs) { char buf[1024]; /* thanks sshfs */ #if FUSE_VERSION >= 27 snprintf(buf, sizeof buf, "-osubtype=stfufs,fsname=%s:%s", cargs->sshhost, cargs->sshpath); #else snprintf(buf, sizeof buf, "-ofsname=sshfs#%s:%s", cargs->sshhost, cargs->sshpath); #endif fuse_opt_insert_arg(fargs, 1, buf); } static int stfufs_client_start(struct fuse_args * fargs) { struct clientargs cargs; char * prgname = fargs->argv[0]; stfufs_client_args_init(&cargs); fuse_opt_parse(fargs, &cargs, stfufs_opts, stfufs_handle_args); stfufs_client_set_fuse_fsname_arg(&cargs, fargs); return stfufs_client_create_connection(prgname, &cargs, &G.pending_requests /* XXX */); } void stfufs_client_fuse_main(int argc, char ** argv) { /*struct fuse_args args = FUSE_ARGS_INIT(argc, argv); */ struct fuse_args args = { 0, 0, 0 }; args.argc = argc; args.argv = argv; stfufs_init_g(); G.fd = stfufs_client_start(&args); exit(fuse_main(args.argc, args.argv, &stfufs_operations, NULL)); } /* * Local variables: * mode: c * c-file-style: "stroustrup" * tab-width: 8 * End: */