summaryrefslogtreecommitdiffstats
path: root/src/server/image.c
diff options
context:
space:
mode:
authorSimon Rettberg2015-12-12 14:29:44 +0100
committerSimon Rettberg2015-12-12 14:29:44 +0100
commitb423733b2614946d2a7cf80ee877f4ad5ca11c64 (patch)
tree05d0267632db3b71bee30ba044c8602b7ec82496 /src/server/image.c
parent[SERVER] Nullpad images virtually at runtime instead of padding the actual file (diff)
downloaddnbd3-b423733b2614946d2a7cf80ee877f4ad5ca11c64.tar.gz
dnbd3-b423733b2614946d2a7cf80ee877f4ad5ca11c64.tar.xz
dnbd3-b423733b2614946d2a7cf80ee877f4ad5ca11c64.zip
[SERVER] Support looking on disk if an unknown image is requested
Diffstat (limited to 'src/server/image.c')
-rw-r--r--src/server/image.c129
1 files changed, 108 insertions, 21 deletions
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 <inttypes.h>
#include <pthread.h>
#include <errno.h>
+#include <glob.h>
+
+#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,10 +1005,14 @@ 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 );
}
@@ -994,6 +1020,67 @@ dnbd3_image_t* image_getOrClone(char *name, uint16_t revision)
}
/**
+ * 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
* 2. Use passed socket to request the crc32 list and save it to disk