summaryrefslogtreecommitdiffstats
path: root/src/server/image.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/image.c')
-rw-r--r--src/server/image.c289
1 files changed, 73 insertions, 216 deletions
diff --git a/src/server/image.c b/src/server/image.c
index 02a383f..673a269 100644
--- a/src/server/image.c
+++ b/src/server/image.c
@@ -20,12 +20,6 @@
#define PATHLEN (2000)
#define NONWORKING_RECHECK_INTERVAL_SECONDS (60)
-#ifdef HAVE_FDATASYNC
-#define dnbd3_fdatasync fdatasync
-#else
-#define dnbd3_fdatasync fsync
-#endif
-
// ##########################################
static dnbd3_image_t *_images[SERVER_MAX_IMAGES];
@@ -49,7 +43,6 @@ static imagecache remoteCloneCache[CACHELEN];
static bool isForbiddenExtension(const char* name);
static dnbd3_image_t* image_remove(dnbd3_image_t *image);
static dnbd3_image_t* image_free(dnbd3_image_t *image);
-static bool image_isHashBlockComplete(const uint8_t * const cacheMap, const uint64_t block, const uint64_t fileSize);
static bool image_load_all_internal(char *base, char *path);
static bool image_addToList(dnbd3_image_t *image);
static bool image_load(char *base, char *path, int withUplink);
@@ -70,43 +63,6 @@ void image_serverStartup()
}
/**
- * Returns true if the given image is complete.
- * DOES NOT LOCK
- */
-bool image_isComplete(dnbd3_image_t *image)
-{
- assert( image != NULL );
- if ( image->working && image->cache_map == NULL ) {
- return true;
- }
- if ( image->virtualFilesize == 0 ) {
- return false;
- }
- bool complete = true;
- int j;
- const int map_len_bytes = IMGSIZE_TO_MAPBYTES( image->virtualFilesize );
- for (j = 0; j < map_len_bytes - 1; ++j) {
- if ( image->cache_map[j] != 0xFF ) {
- complete = false;
- break;
- }
- }
- if ( complete ) // Every block except the last one is complete
- { // Last one might need extra treatment if it's not a full byte
- const int blocks_in_last_byte = (image->virtualFilesize >> 12) & 7;
- uint8_t last_byte = 0;
- if ( blocks_in_last_byte == 0 ) {
- last_byte = 0xFF;
- } else {
- for (j = 0; j < blocks_in_last_byte; ++j)
- last_byte |= (uint8_t)(1 << j);
- }
- complete = ((image->cache_map[map_len_bytes - 1] & last_byte) == last_byte);
- }
- return complete;
-}
-
-/**
* Update cache-map of given image for the given byte range
* start (inclusive) - end (exclusive)
* Locks on: images[].lock
@@ -125,29 +81,36 @@ void image_updateCachemap(dnbd3_image_t *image, uint64_t start, uint64_t end, co
start &= ~(uint64_t)(DNBD3_BLOCK_SIZE - 1);
end = (uint64_t)(end + DNBD3_BLOCK_SIZE - 1) & ~(uint64_t)(DNBD3_BLOCK_SIZE - 1);
}
- bool dirty = false;
+ bool setNewBlocks = false;
uint64_t pos = start;
spin_lock( &image->lock );
if ( image->cache_map == NULL ) {
// Image seems already complete
- spin_unlock( &image->lock );
- logadd( LOG_DEBUG1, "image_updateCachemap with no cache_map: %s", image->path );
- return;
+ if ( set ) {
+ // This makes no sense
+ spin_unlock( &image->lock );
+ logadd( LOG_DEBUG1, "image_updateCachemap(true) with no cache_map: %s", image->path );
+ return;
+ }
+ // Recreate a cache map, set it to all 1 initially as we assume the image was complete
+ const int byteSize = IMGSIZE_TO_MAPBYTES( image->virtualFilesize );
+ image->cache_map = malloc( byteSize );
+ memset( image->cache_map, 0xff, byteSize );
}
while ( pos < end ) {
const size_t map_y = (int)( pos >> 15 );
const int map_x = (int)( (pos >> 12) & 7 ); // mod 8
const int bit_mask = 1 << map_x;
if ( set ) {
- if ( (image->cache_map[map_y] & bit_mask) == 0 ) dirty = true;
+ if ( (image->cache_map[map_y] & bit_mask) == 0 ) setNewBlocks = true;
image->cache_map[map_y] |= (uint8_t)bit_mask;
} else {
image->cache_map[map_y] &= (uint8_t)~bit_mask;
}
pos += DNBD3_BLOCK_SIZE;
}
- if ( dirty && image->crc32 != NULL ) {
- // If dirty is set, at least one of the blocks was not cached before, so queue all hash blocks
+ if ( setNewBlocks && image->crc32 != NULL ) {
+ // If setNewBlocks is set, at least one of the blocks was not cached before, so queue all hash blocks
// for checking, even though this might lead to checking some hash block again, if it was
// already complete and the block range spanned at least two hash blocks.
// First set start and end to borders of hash blocks
@@ -157,7 +120,7 @@ void image_updateCachemap(dnbd3_image_t *image, uint64_t start, uint64_t end, co
while ( pos < end ) {
if ( image->cache_map == NULL ) break;
const int block = (int)( pos / HASH_BLOCK_SIZE );
- if ( image_isHashBlockComplete( image->cache_map, block, image->virtualFilesize ) ) {
+ if ( image_isHashBlockComplete( image->cache_map, block, image->realFilesize ) ) {
spin_unlock( &image->lock );
integrity_check( image, block );
spin_lock( &image->lock );
@@ -169,112 +132,54 @@ void image_updateCachemap(dnbd3_image_t *image, uint64_t start, uint64_t end, co
}
/**
- * Mark image as complete by freeing the cache_map and deleting the map file on disk
+ * Returns true if the given image is complete.
+ * Also frees cache_map and deletes it on disk
+ * if it hasn't been complete before
* Locks on: image.lock
*/
-void image_markComplete(dnbd3_image_t *image)
+bool image_isComplete(dnbd3_image_t *image)
{
- char mapfile[PATHLEN] = "";
assert( image != NULL );
spin_lock( &image->lock );
- if ( image->cache_map != NULL ) {
- free( image->cache_map );
- image->cache_map = NULL;
- snprintf( mapfile, PATHLEN, "%s.map", image->path );
- }
- spin_unlock( &image->lock );
- if ( mapfile[0] != '\0' ) {
- remove( mapfile );
- }
-}
-
-/**
- * Save cache map of every image
- */
-void image_saveAllCacheMaps()
-{
- spin_lock( &imageListLock );
- for (int i = 0; i < _num_images; ++i) {
- if ( _images[i] == NULL ) continue;
- dnbd3_image_t * const image = _images[i];
- spin_lock( &image->lock );
- image->users++;
- spin_unlock( &imageListLock );
- spin_unlock( &image->lock );
- image_saveCacheMap( image );
- spin_lock( &imageListLock );
- spin_lock( &image->lock );
- image->users--;
+ if ( image->virtualFilesize == 0 ) {
spin_unlock( &image->lock );
+ return false;
}
- spin_unlock( &imageListLock );
-}
-
-/**
- * Saves the cache map of the given image.
- * Return true on success.
- * Locks on: imageListLock, image.lock
- */
-bool image_saveCacheMap(dnbd3_image_t *image)
-{
- if ( image == NULL || image->cache_map == NULL ) return true;
- image = image_lock( image ); // Again after locking:
- if ( image == NULL ) return true;
- spin_lock( &image->lock );
- // Lock and get a copy of the cache map, as it could be freed by another thread that is just about to
- // figure out that this image's cache copy is complete
- if ( image->cache_map == NULL || image->virtualFilesize < DNBD3_BLOCK_SIZE ) {
+ if ( image->cache_map == NULL ) {
spin_unlock( &image->lock );
- image_release( image );
return true;
}
- const size_t size = IMGSIZE_TO_MAPBYTES(image->virtualFilesize);
- uint8_t *map = malloc( size );
- memcpy( map, image->cache_map, size );
- // Unlock. Use path and cacheFd without locking. path should never change after initialization of the image,
- // cacheFd is written to and we don't want to hold a spinlock during I/O
- spin_unlock( &image->lock );
- assert( image->path != NULL );
- char mapfile[strlen( image->path ) + 4 + 1];
- strcpy( mapfile, image->path );
- strcat( mapfile, ".map" );
-
- int fd = open( mapfile, O_WRONLY | O_CREAT, 0644 );
- if ( fd < 0 ) {
- const int err = errno;
- image_release( image );
- free( map );
- logadd( LOG_WARNING, "Could not open file to write cache map to disk (errno=%d) file %s", err, mapfile );
- return false;
- }
-
- size_t done = 0;
- while ( done < size ) {
- const ssize_t ret = write( fd, map, size - done );
- if ( ret == -1 ) {
- if ( errno == EINTR ) continue;
- logadd( LOG_WARNING, "Could not write cache map (errno=%d) file %s", errno, mapfile );
- break;
- }
- if ( ret <= 0 ) {
- logadd( LOG_WARNING, "Unexpected return value %d for write() to %s", (int)ret, mapfile );
+ bool complete = true;
+ int j;
+ const int map_len_bytes = IMGSIZE_TO_MAPBYTES( image->virtualFilesize );
+ for (j = 0; j < map_len_bytes - 1; ++j) {
+ if ( image->cache_map[j] != 0xFF ) {
+ complete = false;
break;
}
- done += (size_t)ret;
}
- if ( image->cacheFd != -1 ) {
- if ( dnbd3_fdatasync( image->cacheFd ) == -1 ) {
- logadd( LOG_ERROR, "fsync() on image file %s failed with errno %d", image->path, errno );
- logadd( LOG_ERROR, "Bailing out immediately" );
- exit( 1 );
+ if ( complete ) { // Every block except the last one is complete
+ // Last one might need extra treatment if it's not a full byte
+ const int blocks_in_last_byte = (image->virtualFilesize >> 12) & 7;
+ uint8_t last_byte = 0;
+ if ( blocks_in_last_byte == 0 ) {
+ last_byte = 0xFF;
+ } else {
+ for (j = 0; j < blocks_in_last_byte; ++j)
+ last_byte |= (uint8_t)(1 << j);
}
+ complete = ((image->cache_map[map_len_bytes - 1] & last_byte) == last_byte);
}
- if ( dnbd3_fdatasync( fd ) == -1 ) {
- logadd( LOG_WARNING, "fsync() on image map %s failed with errno %d", mapfile, errno );
+ if ( !complete ) {
+ spin_unlock( &image->lock );
+ return false;
}
- image_release( image ); // Release only after both hit the disk
- close( fd );
- free( map );
+ char mapfile[PATHLEN] = "";
+ free( image->cache_map );
+ image->cache_map = NULL;
+ snprintf( mapfile, PATHLEN, "%s.map", image->path );
+ spin_unlock( &image->lock );
+ unlink( mapfile );
return true;
}
@@ -435,7 +340,6 @@ dnbd3_image_t* image_get(char *name, uint16_t revision, bool checkIfWorking)
img->atime = now;
img->masterCrc32 = candidate->masterCrc32;
img->readFd = -1;
- img->cacheFd = -1;
img->rid = candidate->rid;
img->users = 1;
img->working = false;
@@ -467,15 +371,7 @@ dnbd3_image_t* image_get(char *name, uint16_t revision, bool checkIfWorking)
// Check if image is incomplete, handle
if ( candidate->cache_map != NULL ) {
- // -- Incomplete - rw check
- if ( candidate->cacheFd == -1 ) { // Make sure file is open for writing
- image_reopenCacheFd( candidate, false );
- // It might have failed - still offer proxy mode, we just can't cache
- if ( candidate->cacheFd == -1 ) {
- logadd( LOG_WARNING, "Cannot re-open %s for writing - replication disabled", candidate->path );
- }
- }
- if ( candidate->uplink == NULL && candidate->cacheFd != -1 ) {
+ if ( candidate->uplink == NULL ) {
uplink_init( candidate, -1, NULL, -1 );
}
}
@@ -484,49 +380,6 @@ dnbd3_image_t* image_get(char *name, uint16_t revision, bool checkIfWorking)
}
/**
- * Open the given image's main image file in
- * rw mode, assigning it to the cacheFd struct member.
- *
- * @param force If cacheFd was previously assigned a file descriptor (not == -1),
- * it will be closed first. Otherwise, nothing will happen and true will be returned
- * immediately.
- */
-bool image_reopenCacheFd(dnbd3_image_t *image, const bool force)
-{
- if ( image->cacheFd != -1 && !force )
- return true;
- const int nfd = open( image->path, O_RDWR );
- if ( nfd == -1 ) {
- if ( force ) {
- // Opening new one failed, force is enabled -- close old one
- spin_lock( &image->lock );
- int ofd = image->cacheFd;
- image->cacheFd = -1;
- spin_unlock( &image->lock );
- if ( ofd != -1 ) {
- close( ofd );
- }
- }
- return false;
- }
- // Open succeeded, now switch out while holding lock to make it look somewhat atomic
- spin_lock( &image->lock );
- int closeFd = -1;
- if ( force || image->cacheFd == -1 ) {
- closeFd = image->cacheFd;
- image->cacheFd = nfd;
- } else {
- closeFd = nfd;
- }
- spin_unlock( &image->lock );
- if ( closeFd != -1 ) { // Failed
- close( closeFd );
- }
- return true; // We either replaced the fd in force mode or the old one was -1, or
- // force was false and cacheFd != -1. Either way we consider that success
-}
-
-/**
* Lock the image by increasing its users count
* Returns the image on success, NULL if it is not found in the image list
* Every call to image_lock() needs to be followed by a call to image_release() at some point.
@@ -636,7 +489,12 @@ void image_killUplinks()
if ( _images[i] == NULL ) continue;
spin_lock( &_images[i]->lock );
if ( _images[i]->uplink != NULL ) {
- _images[i]->uplink->shutdown = true;
+ spin_lock( &_images[i]->uplink->queueLock );
+ if ( !_images[i]->uplink->shutdown ) {
+ thread_detach( _images[i]->uplink->thread );
+ _images[i]->uplink->shutdown = true;
+ }
+ spin_unlock( &_images[i]->uplink->queueLock );
signal_call( _images[i]->uplink->signal );
}
spin_unlock( &_images[i]->lock );
@@ -741,15 +599,17 @@ static dnbd3_image_t* image_free(dnbd3_image_t *image)
logadd( LOG_INFO, "Freeing image %s:%d", image->name, (int)image->rid );
}
//
- image_saveCacheMap( image );
uplink_shutdown( image );
spin_lock( &image->lock );
free( image->cache_map );
free( image->crc32 );
free( image->path );
free( image->name );
+ image->cache_map = NULL;
+ image->crc32 = NULL;
+ image->path = NULL;
+ image->name = NULL;
spin_unlock( &image->lock );
- if ( image->cacheFd != -1 ) close( image->cacheFd );
if ( image->readFd != -1 ) close( image->readFd );
spin_destroy( &image->lock );
//
@@ -758,7 +618,7 @@ static dnbd3_image_t* image_free(dnbd3_image_t *image)
return NULL ;
}
-static bool image_isHashBlockComplete(const uint8_t * const cacheMap, const uint64_t block, const uint64_t realFilesize)
+bool image_isHashBlockComplete(const uint8_t * const cacheMap, const uint64_t block, const uint64_t realFilesize)
{
if ( cacheMap == NULL ) return true;
const uint64_t end = (block + 1) * HASH_BLOCK_SIZE;
@@ -953,6 +813,7 @@ static bool image_load(char *base, char *path, int withUplink)
// XXX: Maybe try sha-256 or 512 first if you're paranoid (to be implemented)
// 2. Load CRC-32 list of image
+ bool doFullCheck = false;
uint32_t masterCrc = 0;
const int hashBlockCount = IMGSIZE_TO_HASHBLOCKS( virtualFilesize );
crc32list = image_loadCrcList( path, virtualFilesize, &masterCrc );
@@ -961,7 +822,7 @@ static bool image_load(char *base, char *path, int withUplink)
if ( crc32list != NULL ) {
if ( !image_checkRandomBlocks( 4, fdImage, realFilesize, crc32list, cache_map ) ) {
logadd( LOG_ERROR, "quick crc32 check of %s failed. Data corruption?", path );
- goto load_error;
+ doFullCheck = true;
}
}
@@ -1012,7 +873,6 @@ static bool image_load(char *base, char *path, int withUplink)
image->rid = (uint16_t)revision;
image->users = 0;
image->readFd = -1;
- image->cacheFd = -1;
image->working = (image->cache_map == NULL );
timing_get( &image->nextCompletenessEstimate );
image->completenessEstimate = -1;
@@ -1032,22 +892,13 @@ static bool image_load(char *base, char *path, int withUplink)
crc32list = NULL;
// Get rid of cache map if image is complete
- if ( image->cache_map != NULL && image_isComplete( image ) ) {
- image_markComplete( image );
- image->working = true;
+ if ( image->cache_map != NULL ) {
+ image_isComplete( image );
}
- // Image is definitely incomplete, open image file for writing, so we can update the cache
+ // Image is definitely incomplete, initialize uplink worker
if ( image->cache_map != NULL ) {
image->working = false;
- image->cacheFd = open( path, O_WRONLY );
- if ( image->cacheFd < 0 ) {
- // Proxy mode without disk caching is pointless, bail out
- image->cacheFd = -1;
- logadd( LOG_ERROR, "Could not open incomplete image %s for writing!", path );
- image = image_free( image );
- goto load_error;
- }
if ( withUplink ) {
uplink_init( image, -1, NULL, -1 );
}
@@ -1060,11 +911,16 @@ static bool image_load(char *base, char *path, int withUplink)
fdImage = -1;
} else {
logadd( LOG_ERROR, "Image list full: Could not add image %s", path );
- image->readFd = -1;
+ image->readFd = -1; // Keep fdImage instead, will be closed below
image = image_free( image );
goto load_error;
}
logadd( LOG_DEBUG1, "Loaded image '%s:%d'\n", image->name, (int)image->rid );
+ // CRC errors found...
+ if ( doFullCheck ) {
+ logadd( LOG_INFO, "Queueing full CRC32 check for '%s:%d'\n", image->name, (int)image->rid );
+ integrity_check( image, -1 );
+ }
function_return = true;
@@ -1407,7 +1263,7 @@ server_fail: ;
while ( !image->working && ++i < 100 )
usleep( 2000 );
}
- } else if ( uplinkSock >= 0 ) {
+ } else if ( uplinkSock != -1 ) {
close( uplinkSock );
}
return image;
@@ -1719,8 +1575,9 @@ int image_getCompletenessEstimate(dnbd3_image_t * const image)
}
/**
- * Check the CRC-32 of the given blocks. The array blocks is of variable length.
+ * Check the CRC-32 of the given blocks. The array "blocks" is of variable length.
* !! pass -1 as the last block so the function knows when to stop !!
+ * Does NOT check whether block index is within image.
* Returns true or false
*/
bool image_checkBlocksCrc32(const int fd, uint32_t *crc32list, const int *blocks, const uint64_t realFilesize)