From b423733b2614946d2a7cf80ee877f4ad5ca11c64 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Sat, 12 Dec 2015 14:29:44 +0100 Subject: [SERVER] Support looking on disk if an unknown image is requested --- src/server/fileutil.c | 3 +- src/server/image.c | 129 ++++++++++++++++++++++++++++++++++++++++++-------- src/server/image.h | 2 +- src/server/net.c | 2 +- src/server/uplink.c | 8 ++-- 5 files changed, 116 insertions(+), 28 deletions(-) diff --git a/src/server/fileutil.c b/src/server/fileutil.c index 645519b..4aaced4 100644 --- a/src/server/fileutil.c +++ b/src/server/fileutil.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -27,7 +28,7 @@ bool file_isWritable(char *file) fd = open( file, O_WRONLY | O_CREAT, 0600 ); if ( fd < 0 ) return false; close( fd ); - unlink( file ); + remove( file ); return true; } diff --git a/src/server/image.c b/src/server/image.c index 76876bb..b046af5 100644 --- a/src/server/image.c +++ b/src/server/image.c @@ -23,6 +23,9 @@ #include #include #include +#include + +#define PATHLEN (2000) // ########################################## @@ -843,7 +846,6 @@ bool image_create(char *image, int revision, uint64_t size) logadd( LOG_ERROR, "revision id invalid: %d", revision ); return false; } - const int PATHLEN = 2000; char path[PATHLEN], cache[PATHLEN]; char *lastSlash = strrchr( image, '/' ); if ( lastSlash == NULL ) { @@ -855,10 +857,6 @@ bool image_create(char *image, int revision, uint64_t size) *lastSlash = '/'; snprintf( path, PATHLEN, "%s/%s.r%d", _basePath, image, revision ); } - if ( file_isReadable( path ) ) { - logadd( LOG_ERROR, "Image %s with rid %d already exists!", image, revision ); - return false; - } snprintf( cache, PATHLEN, "%s.map", path ); size = (size + DNBD3_BLOCK_SIZE - 1) & ~(uint64_t)(DNBD3_BLOCK_SIZE - 1); const int mapsize = IMGSIZE_TO_MAPBYTES(size); @@ -898,22 +896,46 @@ bool image_create(char *image, int revision, uint64_t size) return false; } +static dnbd3_image_t *loadImageProxy(char * const name, const uint16_t revision, const size_t len); +static dnbd3_image_t *loadImageServer(char * const name, const uint16_t requestedRid); + /** - * Does the same as image_get, but if the image is not found locally, or if - * revision 0 is requested, it will try to clone it from an authoritative - * dnbd3 server and return the image. If the return value is not NULL, + * Does the same as image_get, but if the image is not known locally, or if + * revision 0 is requested, it will: + * a) Try to clone it from an authoritative dnbd3 server, if + * the server is running in proxy mode. + * b) Try to load it from disk by constructing the appropriate file name, if not + * running in proxy mode. + * + * If the return value is not NULL, * image_release needs to be called on the image at some point. * Locks on: remoteCloneLock, imageListLock, _images[].lock */ -dnbd3_image_t* image_getOrClone(char *name, uint16_t revision) +dnbd3_image_t* image_getOrLoad(char * const name, const uint16_t revision) { - if ( !_isProxy ) return image_get( name, revision, true ); - int i; + // not proxy, specific revision - nothing to do + if ( !_isProxy && revision != 0 ) return image_get( name, revision, true ); const size_t len = strlen( name ); // Sanity check - if ( len == 0 || name[len - 1] == '/' || name[0] == '/' ) return NULL ; + if ( len == 0 || name[len - 1] == '/' || name[0] == '/' ) return NULL; + // Call specific function depending on whether this is a proxy or not + if ( _isProxy ) { + return loadImageProxy( name, revision, len ); + } else { + return loadImageServer( name, revision ); + } +} + +/** + * Called if specific rid is not loaded, or if rid is 0 (some version might be loaded locally, + * but we should check if there's a higher rid on a remote server). + */ +static dnbd3_image_t *loadImageProxy(char * const name, const uint16_t revision, const size_t len) +{ + int i; // Already existing locally? dnbd3_image_t *image = image_get( name, revision, true ); + // exists and specific revision - nothing to do if ( image != NULL && revision != 0 ) return image; // Doesn't exist or is rid 0, try remote if not already tried it recently @@ -927,8 +949,7 @@ dnbd3_image_t* image_getOrClone(char *name, uint16_t revision) useIndex = i; if ( remoteCloneCache[i].deadline < now ) break; pthread_mutex_unlock( &remoteCloneLock ); // Was recently checked... - if ( image != NULL ) return image; - return image_get( name, revision, true ); + return image; } } // Re-check to prevent two clients at the same time triggering this, @@ -954,15 +975,16 @@ dnbd3_image_t* image_getOrClone(char *name, uint16_t revision) int uplinkSock = -1; dnbd3_host_t *uplinkServer = NULL; const int count = altservers_get( servers, 4, false ); - uint16_t remoteVersion, remoteRid; + uint16_t remoteProtocolVersion; + uint16_t remoteRid = revision; uint64_t remoteImageSize; for (i = 0; i < count; ++i) { int sock = sock_connect( &servers[i], 750, _uplinkTimeout ); if ( sock < 0 ) continue; if ( !dnbd3_select_image( sock, name, revision, FLAGS8_SERVER ) ) goto server_fail; char *remoteName; - if ( !dnbd3_select_image_reply( &serialized, sock, &remoteVersion, &remoteName, &remoteRid, &remoteImageSize ) ) goto server_fail; - if ( remoteVersion < MIN_SUPPORTED_SERVER || remoteRid == 0 ) goto server_fail; + if ( !dnbd3_select_image_reply( &serialized, sock, &remoteProtocolVersion, &remoteName, &remoteRid, &remoteImageSize ) ) goto server_fail; + if ( remoteProtocolVersion < MIN_SUPPORTED_SERVER || remoteRid == 0 ) goto server_fail; if ( revision != 0 && remoteRid != revision ) goto server_fail; if ( revision == 0 && image != NULL && image->rid >= remoteRid ) goto server_fail; // Not actually a failure: Highest remote rid is <= highest local rid - don't clone! if ( remoteImageSize < DNBD3_BLOCK_SIZE || remoteName == NULL || strcmp( name, remoteName ) != 0 ) goto server_fail; @@ -983,16 +1005,81 @@ dnbd3_image_t* image_getOrClone(char *name, uint16_t revision) image = image_get( name, remoteRid, false ); if ( image != NULL && uplinkSock != -1 && uplinkServer != NULL ) { // If so, init the uplink and pass it the socket - if ( !uplink_init( image, uplinkSock, uplinkServer ) ) close( uplinkSock ); - i = 0; - while ( !image->working && ++i < 100 ) - usleep( 2000 ); + if ( !uplink_init( image, uplinkSock, uplinkServer ) ) { + close( uplinkSock ); + } else { + // Clumsy busy wait, but this should only take as long as it takes to start a thread, so is it really worth using a signalling mechanism? + i = 0; + while ( !image->working && ++i < 100 ) + usleep( 2000 ); + } } else if ( uplinkSock >= 0 ) { close( uplinkSock ); } return image; } +/** + * Called if specific rid is not loaded, or if rid is 0, in which case we check on + * disk which revision is latest. + */ +static dnbd3_image_t *loadImageServer(char * const name, const uint16_t requestedRid) +{ + char imageFile[PATHLEN] = ""; + uint16_t detectedRid = 0; + + if ( _vmdkLegacyMode ) { + // TODO + assert( 0 ); + detectedRid = requestedRid; + } else if ( requestedRid != 0 ) { + snprintf( imageFile, PATHLEN, "%s/%s.r%d", _basePath, name, requestedRid ); + detectedRid = requestedRid; + } else { + glob_t g = { 0 }; + snprintf( imageFile, PATHLEN, "%s/%s.r*", _basePath, name ); + const int ret = glob( imageFile, GLOB_NOSORT | GLOB_MARK, NULL, &g ); + imageFile[0] = '\0'; + if ( ret == 0 ) { + long int best = 0; + for ( size_t i = 0; i < g.gl_pathc; ++i ) { + char *rev = strrchr( g.gl_pathv[i], 'r' ); + if ( rev == NULL ) continue; + rev++; + if ( *rev < '0' || *rev > '9' ) continue; + char *err = NULL; + long int val = strtol( rev, &err, 10 ); + if ( err == NULL || *err != '\0' ) continue; + if ( val > best ) { + best = val; + snprintf( imageFile, PATHLEN, "%s", g.gl_pathv[i] ); + } + } + if ( best > 0 && best < 65536 ) { + detectedRid = (uint16_t)best; + } + } + globfree( &g ); + } + // No file was determined, or it doesn't seem to exist/be readable + if ( imageFile[0] == '\0' || !file_isReadable( imageFile ) ) { // XXX glob fallback to rid-1? Rework above + return image_get( name, detectedRid, true ); + } + // Now lock on the loading mutex, then check again if the image exists (we're multi-threaded) + pthread_mutex_lock( &reloadLock ); + dnbd3_image_t* image = image_get( name, detectedRid, true ); + if ( image != NULL ) { + // The image magically appeared in the meantime + pthread_mutex_unlock( &reloadLock ); + return image; + } + // Still not loaded, let's try to do so + image_load( _basePath, imageFile, false ); + pthread_mutex_unlock( &reloadLock ); + // If loading succeeded, this will return the image + return image_get( name, requestedRid, true ); +} + /** * Prepare a cloned image: * 1. Allocate empty image file and its cache map diff --git a/src/server/image.h b/src/server/image.h index ab37fd0..25fde37 100644 --- a/src/server/image.h +++ b/src/server/image.h @@ -23,7 +23,7 @@ bool image_saveCacheMap(dnbd3_image_t *image); dnbd3_image_t* image_get(char *name, uint16_t revision, bool checkIfWorking); -dnbd3_image_t* image_getOrClone(char *name, uint16_t revision); +dnbd3_image_t* image_getOrLoad(char *name, uint16_t revision); dnbd3_image_t* image_lock(dnbd3_image_t *image); diff --git a/src/server/net.c b/src/server/net.c index 43a55cf..ad228f4 100644 --- a/src/server/net.c +++ b/src/server/net.c @@ -187,7 +187,7 @@ void *net_client_handler(void *dnbd3_client) logadd( LOG_DEBUG1, "Incomplete handshake received\n" ); } } else { - client->image = image = image_getOrClone( image_name, rid ); + client->image = image = image_getOrLoad( image_name, rid ); if ( image == NULL ) { //logadd( LOG_DEBUG1, "Client requested non-existent image '%s' (rid:%d), rejected\n", image_name, (int)rid ); } else if ( !image->working ) { diff --git a/src/server/uplink.c b/src/server/uplink.c index 3ed2ef5..7fa3c96 100644 --- a/src/server/uplink.c +++ b/src/server/uplink.c @@ -281,10 +281,6 @@ static void* uplink_mainloop(void *data) const int fd = link->fd; link->fd = link->betterFd; if ( fd != -1 ) close( fd ); - // If we don't have a crc32 list yet, see if the new server has one - if ( link->image->crc32 == NULL ) { - uplink_addCrc32( link ); - } link->betterFd = -1; link->currentServer = link->betterServer; link->replicationHandle = 0; @@ -294,6 +290,10 @@ static void* uplink_mainloop(void *data) logadd( LOG_DEBUG1, "(Uplink %s) Now connected to %s\n", link->image->lower_name, buffer + 1 ); setThreadName( buffer ); } + // If we don't have a crc32 list yet, see if the new server has one + if ( link->image->crc32 == NULL ) { + uplink_addCrc32( link ); + } // Re-send all pending requests uplink_sendRequests( link, false ); uplink_sendReplicationRequest( link ); -- cgit v1.2.3-55-g7522