summaryrefslogtreecommitdiffstats
path: root/src/fuse/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fuse/main.c')
-rw-r--r--src/fuse/main.c427
1 files changed, 250 insertions, 177 deletions
diff --git a/src/fuse/main.c b/src/fuse/main.c
index 1a5643c..e06f6e8 100644
--- a/src/fuse/main.c
+++ b/src/fuse/main.c
@@ -5,18 +5,26 @@
* See the file COPYING.
*
* Changed by Stephan Schwaer
+ * FUSE lowlevel by Alan Reichert
* */
#include "connection.h"
#include "helper.h"
-#include "../shared/protocol.h"
-#include "../shared/log.h"
+#include <dnbd3/version.h>
+#include <dnbd3/build.h>
+#include <dnbd3/shared/protocol.h>
+#include <dnbd3/shared/log.h>
#define FUSE_USE_VERSION 30
-#include <fuse.h>
+#include <dnbd3/config.h>
+#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
/* for printing uint */
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
@@ -27,8 +35,14 @@
#define debugf(...) do { logadd( LOG_DEBUG1, __VA_ARGS__ ); } while (0)
-static const char * const IMAGE_PATH = "/img";
-static const char * const STATS_PATH = "/status";
+#define INO_ROOT (1)
+#define INO_STATS (2)
+#define INO_IMAGE (3)
+
+static const char *IMAGE_NAME = "img";
+static const char *STATS_NAME = "status";
+
+static struct fuse_session *_fuseSession = NULL;
static uint64_t imageSize;
/* Debug/Benchmark variables */
@@ -36,237 +50,243 @@ static bool useDebug = false;
static log_info logInfo;
static struct timespec startupTime;
static uid_t owner;
-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 int image_getattr(const char *path, struct stat *stbuf)
+static int reply_buf_limited( fuse_req_t req, const char *buf, size_t bufsize, off_t off, size_t maxsize );
+static void fillStatsFile( fuse_req_t req, 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 int image_stat( fuse_ino_t ino, struct stat *stbuf );
+static void printUsage( char *argv0, int exitCode );
+static void printVersion();
+
+static int image_stat( fuse_ino_t ino, struct stat *stbuf )
{
- int res = 0;
- memset( stbuf, 0, sizeof( struct stat ) );
- stbuf->st_ctim = stbuf->st_atim = stbuf->st_mtim = startupTime;
- stbuf->st_uid = owner;
- if ( strcmp( path, "/" ) == 0 ) {
+ switch ( ino ) {
+ case INO_ROOT:
stbuf->st_mode = S_IFDIR | 0550;
stbuf->st_nlink = 2;
- } else if ( strcmp( path, IMAGE_PATH ) == 0 ) {
+ stbuf->st_mtim = startupTime;
+ break;
+ case INO_IMAGE:
stbuf->st_mode = S_IFREG | 0440;
stbuf->st_nlink = 1;
stbuf->st_size = imageSize;
- } else if ( strcmp( path, STATS_PATH ) == 0 ) {
+ stbuf->st_mtim = startupTime;
+ break;
+ case INO_STATS:
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;
+ }
+ stbuf->st_ctim = stbuf->st_atim = startupTime;
+ stbuf->st_uid = owner;
+ stbuf->st_ino = ino;
+ return 0;
+}
+
+static void image_ll_getattr( fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi )
+{
+ struct stat stbuf = { 0 };
+ ( void ) fi;
+
+ if ( image_stat( ino, &stbuf ) == -1 ) {
+ fuse_reply_err( req, ENOENT );
} else {
- res = -ENOENT;
+ fuse_reply_attr( req, &stbuf, ino == INO_IMAGE ? 1200 : 1 ); // seconds validity timeout
}
- return res;
}
-static int image_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset UNUSED, struct fuse_file_info *fi UNUSED)
+static void image_ll_lookup( fuse_req_t req, fuse_ino_t parent, const char *name )
{
- if ( strcmp( path, "/" ) != 0 ) {
- return -ENOENT;
+ ( void )parent;
+
+ if ( strcmp( name, IMAGE_NAME ) == 0 || strcmp( name, STATS_NAME ) == 0 ) {
+ struct fuse_entry_param e = { 0 };
+ if ( strcmp( name, IMAGE_NAME ) == 0 ) {
+ e.ino = INO_IMAGE;
+ e.attr_timeout = e.entry_timeout = 1200;
+ } else {
+ e.ino = INO_STATS;
+ e.attr_timeout = e.entry_timeout = 0;
+ }
+ if ( image_stat( e.ino, &e.attr ) == 0 ) {
+ fuse_reply_entry( req, &e );
+ return;
+ }
}
- filler( buf, ".", NULL, 0 );
- filler( buf, "..", NULL, 0 );
- filler( buf, IMAGE_PATH + 1, NULL, 0 );
- filler( buf, STATS_PATH + 1, NULL, 0 );
- return 0;
+ fuse_reply_err( req, ENOENT );
}
-static int image_open(const char *path, struct fuse_file_info *fi)
+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 )
{
- if ( strcmp( path, IMAGE_PATH ) != 0 && strcmp( path, STATS_PATH ) != 0 ) {
- return -ENOENT;
+ struct stat stbuf = { .st_ino = ino };
+ size_t oldsize = b->size;
+ b->size += fuse_add_direntry( req, NULL, 0, name, NULL, 0 );
+ b->p = ( char * ) realloc( b->p, b->size );
+ fuse_add_direntry( req, b->p + oldsize, b->size - oldsize, name, &stbuf, b->size );
+ return;
+}
+
+static int reply_buf_limited( fuse_req_t req, const char *buf, size_t bufsize, off_t off, size_t maxsize )
+{
+ if ( off >= 0 && off < (off_t)bufsize ) {
+ return fuse_reply_buf( req, buf + off, MIN( bufsize - off, maxsize ) );
}
- if ( ( fi->flags & 3 ) != O_RDONLY ) {
- return -EACCES;
+ 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 != INO_ROOT ) {
+ fuse_reply_err( req, ENOTDIR );
+ } else {
+ struct dirbuf b;
+ memset( &b, 0, sizeof( b ) );
+ dirbuf_add( req, &b, ".", INO_ROOT );
+ dirbuf_add( req, &b, "..", INO_ROOT );
+ dirbuf_add( req, &b, IMAGE_NAME, INO_IMAGE );
+ dirbuf_add( req, &b, STATS_NAME, INO_STATS );
+ reply_buf_limited( req, b.p, b.size, off, size );
+ free( b.p );
}
- return 0;
}
-static int fillStatsFile(char *buf, size_t size, off_t offset) {
- if ( offset == 0 ) {
- return (int)connection_printStats( buf, size );
+static void image_ll_open( fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi )
+{
+ if ( ino != INO_IMAGE && ino != INO_STATS ) {
+ fuse_reply_err( req, EISDIR );
+ } else if ( ( fi->flags & 3 ) != O_RDONLY ) {
+ fuse_reply_err( req, EACCES );
+ } else {
+ // auto caching
+ fi->keep_cache = 1;
+ fuse_reply_open( req, fi );
}
+}
+
+static void fillStatsFile( fuse_req_t req, size_t size, off_t offset ) {
char buffer[4096];
int ret = (int)connection_printStats( buffer, sizeof buffer );
int len = MIN( ret - (int)offset, (int)size );
- if ( len == 0 )
- return 0;
if ( len < 0 ) {
- return -EOF;
+ fuse_reply_err( req, 0 );
+ return;
}
- memcpy( buf, buffer + offset, len );
- return len;
+ fuse_reply_buf( req, buffer + offset, len );
}
-static int image_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi UNUSED)
+static void image_ll_read( fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info *fi )
{
- 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;
- }
- if ( path[1] == STATS_PATH[1] ) {
- return fillStatsFile( buf, size, offset );
+ assert( ino == INO_STATS || ino == INO_IMAGE );
+
+ ( void )fi;
+
+ if ( ino == INO_STATS ) {
+ fillStatsFile( req, size, offset );
+ return;
}
if ( (uint64_t)offset >= imageSize ) {
- return 0;
+ fuse_reply_err( req, 0 );
+ return;
}
-
if ( offset + size > imageSize ) {
size = imageSize - offset;
}
+ if ( size == 0 || size > UINT32_MAX ) {
+ fuse_reply_err( req, 0 );
+ return;
+ }
if ( useDebug ) {
- /* count the requested blocks */
uint64_t startBlock = offset / ( 4096 );
const uint64_t endBlock = ( offset + size - 1 ) / ( 4096 );
- for ( ; startBlock <= endBlock; startBlock++ ) {
+ for ( ; startBlock <= endBlock; startBlock++ )
+ {
++logInfo.blockRequestCount[startBlock];
}
}
-
- dnbd3_async_t request;
- request.buffer = buf;
- request.length = (uint32_t)size;
- request.offset = offset;
- request.signal = signalGet();
-
- if ( !connection_read( &request ) ) {
- signalPut( request.signal );
- return -EINVAL;
- }
- while ( !request.finished ) {
- int ret = signal_wait( request.signal, 5000 );
- if ( !keepRunning ) {
- connection_close();
- break;
- }
- if ( ret < 0 ) {
- debugf( "fuse_read signal wait returned %d", ret );
- }
- }
- signalPut( request.signal );
- if ( request.success ) {
- return request.length;
- } else {
- return -EIO;
+ dnbd3_async_t *request = malloc( sizeof(dnbd3_async_t) + size );
+ request->length = (uint32_t)size;
+ request->offset = offset;
+ request->fuse_req = req;
+
+ if ( !connection_read( request ) ) {
+ fuse_reply_err( req, EIO );
+ free( request );
}
}
-static void image_sigHandler(int signum) {
- keepRunning = false;
- if ( signum == SIGINT && fuse_sigIntHandler != NULL ) {
- fuse_sigIntHandler(signum);
- }
- if ( signum == SIGTERM && fuse_sigTermHandler != NULL ) {
- fuse_sigTermHandler(signum);
- }
+static void noopSigHandler( int signum )
+{
+ (void)signum;
}
-static void* image_init(struct fuse_conn_info *conn UNUSED)
+static void image_ll_init( void *userdata, struct fuse_conn_info *conn )
{
+ ( void ) userdata;
+ ( void ) conn;
if ( !connection_initThreads() ) {
logadd( LOG_ERROR, "Could not initialize threads for dnbd3 connection, exiting..." );
- exit( EXIT_FAILURE );
+ if ( _fuseSession != NULL ) {
+ fuse_session_exit( _fuseSession );
+ }
}
- // 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)
+static void image_destroy( void *private_data UNUSED )
{
if ( useDebug ) {
printLog( &logInfo );
}
connection_close();
- return;
}
/* 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,
};
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 );
+ printf( "dnbd3-fuse version: %s\n", DNBD3_VERSION_LONG );
+ printf( "Built: %s\n", DNBD3_BUILD_DATE );
+ printf( "Protocol version: %d\n", (int)PROTOCOL_VERSION );
+ struct fuse_args args = FUSE_ARGS_INIT( 2, arg );
+ fuse_parse_cmdline( &args, NULL, NULL, NULL );
exit( 0 );
}
-static void printUsage(char *argv0, int exitCode)
+static void printUsage( char *argv0, int exitCode )
{
char *arg[] = { argv0, "-h" };
- fuse_main( 2, arg, &dnbd3_fuse_no_operations, NULL );
+ struct fuse_args args = FUSE_ARGS_INIT( 2, arg );
+ fuse_parse_cmdline( &args, NULL, NULL, NULL );
printf( "\n" );
printf( "Usage: %s [--debug] [--option mountOpts] --host <serverAddress(es)> --image <imageName> [--rid revision] <mountPoint>\n", argv0 );
printf( "Or: %s [-d] [-o mountOpts] -h <serverAddress(es)> -i <imageName> [-r revision] <mountPoint>\n", argv0 );
@@ -284,19 +304,19 @@ static void printUsage(char *argv0, int exitCode)
static const char *optString = "dfHh:i:l:o:r:SsVv";
static const struct option longOpts[] = {
- { "debug", no_argument, NULL, 'd' },
- { "help", no_argument, NULL, 'H' },
- { "host", required_argument, NULL, 'h' },
- { "image", required_argument, NULL, 'i' },
- { "log", required_argument, NULL, 'l' },
- { "option", required_argument, NULL, 'o' },
- { "rid", required_argument, NULL, 'r' },
- { "sticky", no_argument, NULL, 'S' },
- { "version", no_argument, NULL, 'v' },
- { 0, 0, 0, 0 }
+ { "debug", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'H' },
+ { "host", required_argument, NULL, 'h' },
+ { "image", required_argument, NULL, 'i' },
+ { "log", required_argument, NULL, 'l' },
+ { "option", required_argument, NULL, 'o' },
+ { "rid", required_argument, NULL, 'r' },
+ { "sticky", no_argument, NULL, 'S' },
+ { "version", no_argument, NULL, 'v' },
+ { 0, 0, 0, 0 }
};
-int main(int argc, char *argv[])
+int main( int argc, char *argv[] )
{
char *server_address = NULL;
char *image_Name = NULL;
@@ -306,6 +326,12 @@ int main(int argc, char *argv[])
int newArgc;
int opt, lidx;
bool learnNewServers = true;
+ bool single_thread = false;
+ struct fuse_chan *ch;
+ char *mountpoint;
+ int foreground = 0;
+
+ log_init();
if ( argc <= 1 || strcmp( argv[1], "--help" ) == 0 || strcmp( argv[1], "--usage" ) == 0 ) {
printUsage( argv[0], 0 );
@@ -316,9 +342,10 @@ int main(int argc, char *argv[])
log_setConsoleTimestamps( true );
log_setFileMask( 65535 );
- newArgv = calloc( argc + 10, sizeof(char*) );
+ newArgv = calloc( argc + 10, sizeof( char* ) );
newArgv[0] = argv[0];
newArgc = 1;
+
while ( ( opt = getopt_long( argc, argv, optString, longOpts, &lidx ) ) != -1 ) {
switch ( opt ) {
case 'h':
@@ -328,7 +355,7 @@ int main(int argc, char *argv[])
image_Name = optarg;
break;
case 'r':
- rid = (uint16_t)atoi(optarg);
+ rid = (uint16_t)atoi( optarg );
break;
case 'o':
newArgv[newArgc++] = "-o";
@@ -357,15 +384,16 @@ int main(int argc, char *argv[])
case 'd':
useDebug = true;
newArgv[newArgc++] = "-d";
+ foreground = 1;
break;
case 's':
- newArgv[newArgc++] = "-s";
+ single_thread = true;
break;
case 'S':
learnNewServers = false;
break;
case 'f':
- newArgv[newArgc++] = "-f";
+ foreground = 1;
break;
default:
printUsage( argv[0], EXIT_FAILURE );
@@ -386,6 +414,17 @@ int main(int argc, char *argv[])
}
}
+ // Prepare our handler
+ struct sigaction newHandler;
+ memset( &newHandler, 0, sizeof( newHandler ) );
+ newHandler.sa_handler = &noopSigHandler;
+ sigemptyset( &newHandler.sa_mask );
+ sigaction( SIGHUP, &newHandler, NULL );
+ sigset_t sigmask;
+ sigemptyset( &sigmask );
+ sigaddset( &sigmask, SIGHUP );
+ pthread_sigmask( SIG_BLOCK, &sigmask, NULL );
+
if ( !connection_init( server_address, image_Name, rid, learnNewServers ) ) {
logadd( LOG_ERROR, "Could not connect to any server. Bye.\n" );
return EXIT_FAILURE;
@@ -404,17 +443,51 @@ 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,default_permissions";
// Mount point goes last
newArgv[newArgc++] = argv[optind];
- printf( "ImagePathName: %s\nFuseArgs:",IMAGE_PATH );
+ printf( "ImagePathName: /%s\nFuseArgs:", IMAGE_NAME );
for ( int i = 0; i < newArgc; ++i ) {
printf( " '%s'", newArgv[i] );
}
- putchar('\n');
+ putchar( '\n' );
clock_gettime( CLOCK_REALTIME, &startupTime );
owner = getuid();
- signalInit();
- return fuse_main( newArgc, newArgv, &image_oper, NULL );
+
+ // Fuse lowlevel loop
+ struct fuse_args args = FUSE_ARGS_INIT( newArgc, newArgv );
+ int fuse_err = 1;
+ if ( fuse_parse_cmdline( &args, &mountpoint, NULL, NULL ) == -1 ) {
+ logadd( LOG_ERROR, "FUSE: Parsing command line failed" );
+ } else if ( ( ch = fuse_mount( mountpoint, &args ) ) == NULL ) {
+ logadd( LOG_ERROR, "Mounting file system failed" );
+ } else {
+ _fuseSession = fuse_lowlevel_new( &args, &image_oper, sizeof( image_oper ), NULL );
+ if ( _fuseSession == NULL ) {
+ logadd( LOG_ERROR, "Could not initialize fuse session" );
+ } else {
+ if ( fuse_set_signal_handlers( _fuseSession ) == -1 ) {
+ logadd( LOG_ERROR, "Could not install fuse signal handlers" );
+ } else {
+ fuse_session_add_chan( _fuseSession, ch );
+ fuse_daemonize( foreground );
+ if ( single_thread ) {
+ fuse_err = fuse_session_loop( _fuseSession );
+ } else {
+ fuse_err = fuse_session_loop_mt( _fuseSession ); //MT produces errors (race conditions) in libfuse and didnt improve speed at all
+ }
+ fuse_remove_signal_handlers( _fuseSession );
+ fuse_session_remove_chan( ch );
+ }
+ fuse_session_destroy( _fuseSession );
+ _fuseSession = NULL;
+ }
+ fuse_unmount( mountpoint, ch );
+ }
+ fuse_opt_free_args( &args );
+ free( newArgv );
+ connection_join();
+ logadd( LOG_DEBUG1, "Terminating. FUSE REPLIED: %d\n", fuse_err );
+ return fuse_err;
}