From 59de892b49d588acd2d3e5e632d9b3bad8a7c03e Mon Sep 17 00:00:00 2001 From: ln-tech Date: Tue, 22 Oct 2019 08:05:22 +0200 Subject: cleaned code and atomic_ints against --- src/fuse/connection.c | 47 +++++-- src/fuse/connection.h | 10 +- src/fuse/main.c | 380 ++++++++++++++++++++++++++++++++++++++++++-------- src/fuse/main.h | 31 ++++ 4 files changed, 401 insertions(+), 67 deletions(-) create mode 100644 src/fuse/main.h (limited to 'src/fuse') diff --git a/src/fuse/connection.c b/src/fuse/connection.c index 98b1d36..4c28dd8 100644 --- a/src/fuse/connection.c +++ b/src/fuse/connection.c @@ -14,6 +14,7 @@ #include #include #include +#include "main.h" /* Constants */ static const size_t SHORTBUF = 100; @@ -344,9 +345,11 @@ static void* connection_receiveThreadMain(void *sockPtr) int sockFd = (int)(size_t)sockPtr; dnbd3_reply_t reply; pthread_detach( pthread_self() ); + while ( keepRunning ) { int ret; + struct fuse_bufvec splice_buf; do { ret = dnbd3_read_reply( sockFd, &reply, true ); if ( ret == REPLY_OK ) break; @@ -369,10 +372,18 @@ static void* connection_receiveThreadMain(void *sockPtr) } } else { // Found a match + request->buffer = malloc(request->length); // char* + if (request->mode == SPLICE) { + splice_buf = FUSE_BUFVEC_INIT(request->length); + splice_buf.buf[0].mem = request->buffer; + splice_buf.buf[0].pos = request->offset; + } const ssize_t ret = sock_recv( sockFd, request->buffer, request->length ); if ( ret != (ssize_t)request->length ) { logadd( LOG_DEBUG1, "receiving payload for a block reply failed" ); connection_read( request ); + free(request->buffer); + request->buffer = NULL; goto fail; } // Check RTT @@ -390,10 +401,30 @@ static void* connection_receiveThreadMain(void *sockPtr) } unlock_rw( &altLock ); } + int rep = -errno; + if (request->mode == NO_SPLICE) { + rep = fuse_reply_buf(request->fuse_req, request->buffer, request->length); + } + else if (request->mode == SPLICE) { + rep = fuse_reply_data(request->fuse_req, &splice_buf, FUSE_BUF_FORCE_SPLICE); + // free(splice_buf); + } + if (rep != -errno) { + // no error + } + else { + printf("ERROR ON FUSE REPLY %i \n", rep); + fuse_reply_err(request->fuse_req, rep); + } + free(request->buffer); + request->buffer = NULL; + //free(request->fuse_req); + //request->fuse_req = NULL; + free(request); // Success, wake up caller - request->success = true; - request->finished = true; - signal_call( request->signal ); + //request->success = true; + //request->finished = true; + //signal_call( request->signal ); } } else if ( reply.cmd == CMD_GET_SERVERS ) { // List of known alt servers @@ -686,9 +717,9 @@ static void probeAltServers() } // Success, wake up caller logadd( LOG_DEBUG1, "[RTT] Successful direct probe" ); - request->success = true; - request->finished = true; - signal_call( request->signal ); + //request->success = true; + //request->finished = true; + //signal_call( request->signal ); } else { // Wasn't a request that's in our request queue if ( !throwDataAway( sock, testLength ) ) { @@ -886,8 +917,8 @@ static bool throwDataAway(int sockFd, uint32_t amount) static void enqueueRequest(dnbd3_async_t *request) { request->next = NULL; - request->finished = false; - request->success = false; + //request->finished = false; + //request->success = false; //logadd( LOG_DEBUG2, "Queue: %p @ %s : %d", request, file, line ); // Measure latency and add to switch formula timing_get( &request->time ); diff --git a/src/fuse/connection.h b/src/fuse/connection.h index cae554c..c04d75b 100644 --- a/src/fuse/connection.h +++ b/src/fuse/connection.h @@ -6,18 +6,22 @@ #include #include #include +#define FUSE_USE_VERSION 30 +#include + +#define NO_SPLICE 0 +#define SPLICE 1 struct _dnbd3_async; typedef struct _dnbd3_async { struct _dnbd3_async *next; // Next in this linked list (provate field, not set by caller) - dnbd3_signal_t* signal; // Used to signal the caller char* buffer; // Caller-provided buffer to be filled ticks time; // When request was put on wire, 0 if not measuring uint64_t offset; uint32_t length; - bool finished; // Will be set to true if the request has been handled - bool success; // Will be set to true if the request succeeded + int mode; // 0 splice off, 1 splice on, ... + fuse_req_t *fuse_req; } dnbd3_async_t; bool connection_init(const char *hosts, const char *image, const uint16_t rid, const bool learnNewServers); diff --git a/src/fuse/main.c b/src/fuse/main.c index 1a5643c..7c5af92 100644 --- a/src/fuse/main.c +++ b/src/fuse/main.c @@ -13,10 +13,16 @@ #include "../shared/log.h" #define FUSE_USE_VERSION 30 -#include +#include +#include +//#include #include #include #include +#include +#include +#include +#include /* for printing uint */ #define __STDC_FORMAT_MACROS #include @@ -24,11 +30,15 @@ #include #include #include +#include "main.h" #define debugf(...) do { logadd( LOG_DEBUG1, __VA_ARGS__ ); } while (0) +#define MODE 1 static const char * const IMAGE_PATH = "/img"; static const char * const STATS_PATH = "/status"; +static const char *IMAGE_NAME = "img"; +static const char *STATS_NAME = "status"; static uint64_t imageSize; /* Debug/Benchmark variables */ @@ -36,50 +46,12 @@ static bool useDebug = false; static log_info logInfo; static struct timespec startupTime; static uid_t owner; -static bool keepRunning = true; +//static bool keepRunning = true; static void (*fuse_sigIntHandler)(int) = NULL; static void (*fuse_sigTermHandler)(int) = NULL; -static struct fuse_operations dnbd3_fuse_no_operations; - -#define SIGPOOLSIZE 6 -static pthread_spinlock_t sigLock; -static dnbd3_signal_t *signalPool[SIGPOOLSIZE]; -static dnbd3_signal_t **sigEnd = signalPool + SIGPOOLSIZE; -static void signalInit() -{ - pthread_spin_init( &sigLock, PTHREAD_PROCESS_PRIVATE ); - for ( size_t i = 0; i < SIGPOOLSIZE; ++i ) { - signalPool[i] = NULL; - } -} -static inline dnbd3_signal_t *signalGet() -{ - pthread_spin_lock( &sigLock ); - for ( dnbd3_signal_t **it = signalPool; it < sigEnd; ++it ) { - if ( *it != NULL ) { - dnbd3_signal_t *ret = *it; - *it = NULL; - pthread_spin_unlock( &sigLock ); - return ret; - } - } - pthread_spin_unlock( &sigLock ); - return signal_newBlocking(); -} -static inline void signalPut(dnbd3_signal_t *signal) -{ - pthread_spin_lock( &sigLock ); - for ( dnbd3_signal_t **it = signalPool; it < sigEnd; ++it ) { - if ( *it == NULL ) { - *it = signal; - pthread_spin_unlock( &sigLock ); - return; - } - } - pthread_spin_unlock( &sigLock ); - signal_close( signal ); -} +//static struct fuse_operations dnbd3_fuse_no_operations; +/* static int image_getattr(const char *path, struct stat *stbuf) { int res = 0; @@ -103,7 +75,51 @@ static int image_getattr(const char *path, struct stat *stbuf) } return res; } +*/ +static int image_stat(fuse_ino_t ino, struct stat *stbuf) +{ + stbuf->st_ctim = stbuf->st_atim = stbuf->st_mtim = startupTime; + stbuf->st_uid = owner; + stbuf->st_ino = ino; + switch (ino) { + case 1: + stbuf->st_mode = S_IFDIR | 0550; + stbuf->st_nlink = 2; + break; + + case 2: + stbuf->st_mode = S_IFREG | 0440; + stbuf->st_nlink = 1; + stbuf->st_size = imageSize; + break; + case 3: + stbuf->st_mode = S_IFREG | 0440; + stbuf->st_nlink = 1; + stbuf->st_size = 4096; + clock_gettime( CLOCK_REALTIME, &stbuf->st_mtim ); + break; + default: + return -1; + } + return 0; +} + +static void image_ll_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + struct stat stbuf; + + (void) fi; + + memset(&stbuf, 0, sizeof(stbuf)); + if (image_stat(ino, &stbuf) == -1) + fuse_reply_err(req, ENOENT); + else + fuse_reply_attr(req, &stbuf, 1.0); // 1.0 seconds validity timeout +} + + +/* static int image_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset UNUSED, struct fuse_file_info *fi UNUSED) { if ( strcmp( path, "/" ) != 0 ) { @@ -115,7 +131,70 @@ static int image_readdir(const char *path, void *buf, fuse_fill_dir_t filler, of filler( buf, STATS_PATH + 1, NULL, 0 ); return 0; } +*/ +static void image_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) +{ + struct fuse_entry_param e; + if (strcmp(name, IMAGE_NAME) == 0 || strcmp(name, STATS_NAME) == 0) { + memset(&e, 0, sizeof(e)); + if (strcmp(name, IMAGE_NAME) == 0) e.ino = 2; + else e.ino = 3; + e.attr_timeout = 1.0; + e.entry_timeout = 1.0; + image_stat(e.ino, &e.attr); + + fuse_reply_entry(req, &e); + } + else fuse_reply_err(req, ENOENT); +} + +struct dirbuf { + char *p; + size_t size; +}; + +static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name, fuse_ino_t ino) +{ + struct stat stbuf; + size_t oldsize = b->size; + b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0); + b->p = (char *) realloc(b->p, b->size); + memset(&stbuf, 0, sizeof(stbuf)); + stbuf.st_ino = ino; + fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf, b->size); + return; +} + +#define min(x, y) ((x) < (y) ? (x) : (y)) + +static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize, off_t off, size_t maxsize) +{ + if (off < bufsize) + return fuse_reply_buf(req, buf + off, min(bufsize - off, maxsize)); + else + return fuse_reply_buf(req, NULL, 0); +} + +static void image_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) +{ + (void) fi; + + if (ino != 1) + fuse_reply_err(req, ENOTDIR); + else { + struct dirbuf b; + + memset(&b, 0, sizeof(b)); + dirbuf_add(req, &b, ".", 1); + dirbuf_add(req, &b, "..", 1); + dirbuf_add(req, &b, IMAGE_NAME, 2); + dirbuf_add(req, &b, STATS_NAME, 3); + reply_buf_limited(req, b.p, b.size, off, size); + free(b.p); + } +} +/* static int image_open(const char *path, struct fuse_file_info *fi) { if ( strcmp( path, IMAGE_PATH ) != 0 && strcmp( path, STATS_PATH ) != 0 ) { @@ -126,6 +205,16 @@ static int image_open(const char *path, struct fuse_file_info *fi) } return 0; } +*/ +static void image_ll_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + if (ino != 2 && ino != 3) + fuse_reply_err(req, EISDIR); + else if ((fi->flags & 3) != O_RDONLY) + fuse_reply_err(req, EACCES); + else + fuse_reply_open(req, fi); +} static int fillStatsFile(char *buf, size_t size, off_t offset) { if ( offset == 0 ) { @@ -142,7 +231,7 @@ static int fillStatsFile(char *buf, size_t size, off_t offset) { memcpy( buf, buffer + offset, len ); return len; } - +/* static int image_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi UNUSED) { if ( size > __INT_MAX__ ) { @@ -166,7 +255,6 @@ static int image_read(const char *path, char *buf, size_t size, off_t offset, st } if ( useDebug ) { - /* count the requested blocks */ uint64_t startBlock = offset / ( 4096 ); const uint64_t endBlock = ( offset + size - 1 ) / ( 4096 ); @@ -202,9 +290,72 @@ static int image_read(const char *path, char *buf, size_t size, off_t offset, st return -EIO; } } +*/ +static void image_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info *fi) +{ + assert(ino == 2 || ino == 3); + + (void)fi; + int len = 0; + char *buf = NULL; + + if (size > __INT_MAX__) + { + // fuse docs say we MUST fill the buffer with exactly size bytes and return size, + // otherwise the buffer will we padded with zeros. Since the return value is just + // an int, we could not properly fulfill read requests > 2GB. Since there is no + // mention of a guarantee that this will never happen, better add a safety check. + // Way to go fuse. + // return -EIO; + fuse_reply_err(req, EIO); + } + if (ino == 3) + { + buf = (char *)malloc(4096); // they use 4096 byte buffer in fillStatsFile() for the status-file + len = fillStatsFile(buf, size, offset); + fuse_reply_buf(req, buf, len); + free(buf); + buf = NULL; + } + + if ((uint64_t)offset >= imageSize) + { + fuse_reply_err(req, 0); + } + + if (offset + size > imageSize) + { + size = imageSize - offset; + } + //debugf("\n OFFSET %d SIZE %d IMAGE %d \n", offset, size, imageSize); + if (useDebug) + { + uint64_t startBlock = offset / (4096); + const uint64_t endBlock = (offset + size - 1) / (4096); + + for (; startBlock <= endBlock; startBlock++) + { + ++logInfo.blockRequestCount[startBlock]; + } + } + if (ino == 2 && size != 0) // with size == 0 there is nothing to do + { + dnbd3_async_t *request = malloc(sizeof(dnbd3_async_t)); + request->length = (uint32_t)size; + request->offset = offset; + request->fuse_req = req; + request->mode = MODE; + + if (!connection_read(request)) fuse_reply_err(req, EINVAL); + + //free(request->buffer); + //free(request->fuse_req); + //free(request); + } +} static void image_sigHandler(int signum) { - keepRunning = false; + //keepRunning = false; if ( signum == SIGINT && fuse_sigIntHandler != NULL ) { fuse_sigIntHandler(signum); } @@ -213,6 +364,7 @@ static void image_sigHandler(int signum) { } } +/* static void* image_init(struct fuse_conn_info *conn UNUSED) { if ( !connection_initThreads() ) { @@ -234,6 +386,95 @@ static void* image_init(struct fuse_conn_info *conn UNUSED) logadd( LOG_DEBUG1, "Previous SIGTERM handler was %p", (void*)(uintptr_t)fuse_sigIntHandler ); return NULL; } +*/ +static void image_ll_init(void *userdata, struct fuse_conn_info *conn) +{ +/* +Zero copy data transfer ("splicing") will be used under the following circumstances: + +FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.want, and +the kernel supports splicing from the fuse device (FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.capable), and +flags does not contain FUSE_BUF_NO_SPLICE +The amount of data that is provided in file-descriptor backed buffers (i.e., buffers for which bufv[n].flags == FUSE_BUF_FD) is at least twice the page size. +In order for SPLICE_F_MOVE to be used, the following additional conditions have to be fulfilled: + +FUSE_CAP_SPLICE_MOVE is set in fuse_conn_info.want, and +the kernel supports it (i.e, FUSE_CAP_SPLICE_MOVE is set in fuse_conn_info.capable), and +flags contains FUSE_BUF_SPLICE_MOVE +*/ + (void) userdata; + + printf("Protocol version: %d.%d\n", conn->proto_major, + conn->proto_minor); + printf("Capabilities:\n"); + if(conn->capable & FUSE_CAP_FLOCK_LOCKS) + printf("\tFUSE_CAP_WRITEBACK_CACHE\n"); + if(conn->capable & FUSE_CAP_ASYNC_READ) + printf("\tFUSE_CAP_ASYNC_READ\n"); + if(conn->capable & FUSE_CAP_POSIX_LOCKS) + printf("\tFUSE_CAP_POSIX_LOCKS\n"); + if(conn->capable & FUSE_CAP_ATOMIC_O_TRUNC) + printf("\tFUSE_CAP_ATOMIC_O_TRUNC\n"); + if(conn->capable & FUSE_CAP_EXPORT_SUPPORT) + printf("\tFUSE_CAP_EXPORT_SUPPORT\n"); + if(conn->capable & FUSE_CAP_DONT_MASK) + printf("\tFUSE_CAP_DONT_MASK\n"); + if(conn->capable & FUSE_CAP_SPLICE_MOVE) + printf("\tFUSE_CAP_SPLICE_MOVE\n"); + if(conn->capable & FUSE_CAP_SPLICE_READ) + printf("\tFUSE_CAP_SPLICE_READ\n"); + if(conn->capable & FUSE_CAP_SPLICE_WRITE) + printf("\tFUSE_CAP_SPLICE_WRITE\n"); + if(conn->capable & FUSE_CAP_FLOCK_LOCKS) + printf("\tFUSE_CAP_FLOCK_LOCKS\n"); + if(conn->capable & FUSE_CAP_IOCTL_DIR) + printf("\tFUSE_CAP_IOCTL_DIR\n"); + if(conn->capable & FUSE_CAP_IOCTL_DIR) + printf("\tFUSE_CAP_AUTO_INVAL_DATA\n"); + if(conn->capable & FUSE_CAP_BIG_WRITES) + printf("\tFUSE_CAP_READDIRPLUS\n"); + if(conn->capable & FUSE_CAP_BIG_WRITES) + printf("\tFUSE_CAP_READDIRPLUS_AUTO\n"); + if(conn->capable & FUSE_CAP_ASYNC_READ) + printf("\tFUSE_CAP_ASYNC_DIO\n"); + if(conn->capable & FUSE_CAP_FLOCK_LOCKS) + printf("\tFUSE_CAP_WRITEBACK_CACHE\n"); + if(conn->capable & FUSE_CAP_EXPORT_SUPPORT) + printf("\tFUSE_CAP_NO_OPEN_SUPPORT\n"); + if(conn->capable & FUSE_CAP_ASYNC_READ) + printf("\tFUSE_CAP_PARALLEL_DIROPS\n"); + if(conn->capable & FUSE_CAP_POSIX_LOCKS) + printf("\tFUSE_CAP_POSIX_ACL\n"); + + conn->want |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE | FUSE_CAP_SPLICE_READ; + //printf("%d", conn->want); + if(conn->want & FUSE_CAP_SPLICE_WRITE) + printf("\tFUSE_CAP_SPLICE_WRITE on\n"); + if(conn->capable & FUSE_CAP_SPLICE_MOVE) + printf("\tFUSE_CAP_SPLICE_MOVE on\n"); + if(conn->capable & FUSE_CAP_SPLICE_READ) + printf("\tFUSE_CAP_SPLICE_READ on\n"); + + + if ( !connection_initThreads() ) { + logadd( LOG_ERROR, "Could not initialize threads for dnbd3 connection, exiting..." ); + exit( EXIT_FAILURE ); + } + // Prepare our handler + struct sigaction newHandler; + memset( &newHandler, 0, sizeof(newHandler) ); + newHandler.sa_handler = &image_sigHandler; + sigemptyset( &newHandler.sa_mask ); + struct sigaction oldHandler; + // Retrieve old handlers when setting + sigaction( SIGINT, &newHandler, &oldHandler ); + fuse_sigIntHandler = oldHandler.sa_handler; + logadd( LOG_DEBUG1, "Previous SIGINT handler was %p", (void*)(uintptr_t)fuse_sigIntHandler ); + sigaction( SIGTERM, &newHandler, &oldHandler ); + fuse_sigTermHandler = oldHandler.sa_handler; + logadd( LOG_DEBUG1, "Previous SIGTERM handler was %p", (void*)(uintptr_t)fuse_sigIntHandler ); + //return NULL; +} /* close the connection */ static void image_destroy(void *private_data UNUSED) @@ -246,12 +487,13 @@ static void image_destroy(void *private_data UNUSED) } /* map the implemented fuse operations */ -static struct fuse_operations image_oper = { - .getattr = image_getattr, - .readdir = image_readdir, - .open = image_open, - .read = image_read, - .init = image_init, +static struct fuse_lowlevel_ops image_oper = { + .lookup = image_ll_lookup, + .getattr = image_ll_getattr, + .readdir = image_ll_readdir, + .open = image_ll_open, + .read = image_ll_read, + .init = image_ll_init, .destroy = image_destroy, }; @@ -259,14 +501,14 @@ static void printVersion() { char *arg[] = { "foo", "-V" }; printf( "DNBD3-Fuse Version 1.2.3.4, protocol version %d\n", (int)PROTOCOL_VERSION ); - fuse_main( 2, arg, &dnbd3_fuse_no_operations, NULL ); + //fuse_main( 2, arg, &dnbd3_fuse_no_operations, NULL ); exit( 0 ); } static void printUsage(char *argv0, int exitCode) { char *arg[] = { argv0, "-h" }; - fuse_main( 2, arg, &dnbd3_fuse_no_operations, NULL ); + //fuse_main( 2, arg, &dnbd3_fuse_no_operations, NULL ); printf( "\n" ); printf( "Usage: %s [--debug] [--option mountOpts] --host --image [--rid revision] \n", argv0 ); printf( "Or: %s [-d] [-o mountOpts] -h -i [-r revision] \n", argv0 ); @@ -306,6 +548,11 @@ int main(int argc, char *argv[]) int newArgc; int opt, lidx; bool learnNewServers = true; +// struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + struct fuse_chan *ch; + char *mountpoint; + int err = -1; + mountpoint = argv[5]; if ( argc <= 1 || strcmp( argv[1], "--help" ) == 0 || strcmp( argv[1], "--usage" ) == 0 ) { printUsage( argv[0], 0 ); @@ -404,7 +651,8 @@ int main(int argc, char *argv[]) // Since dnbd3 is always read only and the remote image will not change newArgv[newArgc++] = "-o"; - newArgv[newArgc++] = "ro,auto_cache,default_permissions"; + //newArgv[newArgc++] = "ro,auto_cache,default_permissions"; + newArgv[newArgc++] = "ro,splice_read,default_permissions"; // Mount point goes last newArgv[newArgc++] = argv[optind]; @@ -415,6 +663,26 @@ int main(int argc, char *argv[]) putchar('\n'); clock_gettime( CLOCK_REALTIME, &startupTime ); owner = getuid(); - signalInit(); - return fuse_main( newArgc, newArgv, &image_oper, NULL ); + + // LL partnewArgv[newArgc++] + struct fuse_args args = FUSE_ARGS_INIT(newArgc, newArgv); + if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) != -1 && (ch = fuse_mount(mountpoint, &args)) != NULL) { + struct fuse_session *se; + + se = fuse_lowlevel_new(&args, &image_oper, sizeof(image_oper), NULL); + if (se != NULL) { + if (fuse_set_signal_handlers(se) != -1) { + fuse_session_add_chan(se, ch); + err = fuse_session_loop(se); + fuse_remove_signal_handlers(se); + fuse_session_remove_chan(ch); + } + fuse_session_destroy(se); + } + fuse_unmount(mountpoint, ch); + } + fuse_opt_free_args(&args); + + return err ? 1 : 0; + // return fuse_main( newArgc, newArgv, &image_oper, NULL ); } diff --git a/src/fuse/main.h b/src/fuse/main.h new file mode 100644 index 0000000..02ddade --- /dev/null +++ b/src/fuse/main.h @@ -0,0 +1,31 @@ +#ifndef _MAIN_H_ +#define _MAIN_H_ + +#include "../shared/fdsignal.h" +#include "../shared/timing.h" +#include +#include +#include +#include + + +static inline dnbd3_signal_t *signalGet(); +//static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name, fuse_ino_t ino); +static int fillStatsFile(char *buf, size_t size, off_t offset); +static void image_destroy(void *private_data); +static void image_ll_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); +static void image_ll_init(void *userdata, struct fuse_conn_info *conn); +static void image_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name); +static void image_ll_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); +static void image_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi); +static void image_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info *fi); +static void image_sigHandler(int signum); +static int image_stat(fuse_ino_t ino, struct stat *stbuf); +int main(int argc, char *argv[]); +static void printUsage(char *argv0, int exitCode); +static void printVersion(); +static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize, off_t off, size_t maxsize); +static void signalInit(); +static inline void signalPut(dnbd3_signal_t *signal); + +#endif /* MAIN_H_ */ -- cgit v1.2.3-55-g7522