summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2018-06-13 16:14:39 +0200
committerSimon Rettberg2018-06-13 16:14:39 +0200
commit6c87083685744b6aa899de50128ab74ef8f89982 (patch)
tree36c569b0daec966d8a505ccd79f57b6c76888861
parent[FUSE] Return 0 instead of EIO if trying to read past end (diff)
downloaddnbd3-6c87083685744b6aa899de50128ab74ef8f89982.tar.gz
dnbd3-6c87083685744b6aa899de50128ab74ef8f89982.tar.xz
dnbd3-6c87083685744b6aa899de50128ab74ef8f89982.zip
[SERVER] Make sure image has read fd before reading
-rw-r--r--src/server/image.c81
-rw-r--r--src/server/image.h2
-rw-r--r--src/server/integrity.c6
3 files changed, 60 insertions, 29 deletions
diff --git a/src/server/image.c b/src/server/image.c
index 338cf5a..2df4565 100644
--- a/src/server/image.c
+++ b/src/server/image.c
@@ -279,6 +279,51 @@ bool image_saveCacheMap(dnbd3_image_t *image)
}
/**
+ * Make sure readFd is open, useful when closeUnusedFd is active.
+ * This function assumes you called image_lock first, so its known
+ * to be active and the fd won't be closed halfway through the
+ * function.
+ * Does not update atime, so the fd might be closed again very soon.
+ * Since the caller should have image_lock()ed first, it could do
+ * a quick operation on it before calling image_release which
+ * guarantees that the fd will not be closed meanwhile.
+ */
+bool image_ensureOpen(dnbd3_image_t *image)
+{
+ if ( image->readFd != -1 ) return image;
+ int newFd = open( image->path, O_RDONLY );
+ if ( newFd != -1 ) {
+ // Check size
+ const off_t flen = lseek( newFd, 0, SEEK_END );
+ if ( flen == -1 ) {
+ logadd( LOG_WARNING, "Could not seek to end of %s (errno %d)", image->path, errno );
+ close( newFd );
+ newFd = -1;
+ } else if ( (uint64_t)flen != image->realFilesize ) {
+ logadd( LOG_WARNING, "Size of active image with closed fd changed from %" PRIu64 " to %" PRIu64, image->realFilesize, (uint64_t)flen );
+ close( newFd );
+ newFd = -1;
+ }
+ }
+ if ( newFd == -1 ) {
+ spin_lock( &image->lock );
+ image->working = false;
+ spin_unlock( &image->lock );
+ return false;
+ }
+ spin_lock( &image->lock );
+ if ( image->readFd == -1 ) {
+ image->readFd = newFd;
+ spin_unlock( &image->lock );
+ } else {
+ // There was a race while opening the file (happens cause not locked cause blocking), we lost the race so close new fd and proceed
+ spin_unlock( &image->lock );
+ close( newFd );
+ }
+ return image->readFd != -1;
+}
+
+/**
* Get an image by name+rid. This function increases a reference counter,
* so you HAVE TO CALL image_release for every image_get() call at some
* point...
@@ -321,36 +366,14 @@ dnbd3_image_t* image_get(char *name, uint16_t revision, bool checkIfWorking)
// TODO: But remember size-changed images forever
if ( candidate->working || checkIfWorking ) {
// Is marked working, but might not have an fd open
- if ( candidate->readFd == -1 ) {
- int newFd = open( candidate->path, O_RDONLY );
- if ( newFd != -1 ) {
- // Check size
- const off_t flen = lseek( newFd, 0, SEEK_END );
- if ( flen == -1 || (uint64_t)flen != candidate->realFilesize ) {
- logadd( LOG_WARNING, "Size of active image with closed fd changed from %" PRIu64 " to %" PRIu64, candidate->realFilesize, (uint64_t)flen );
- close( newFd );
- newFd = -1;
- }
- }
- if ( newFd == -1 ) {
- spin_lock( &candidate->lock );
- candidate->working = false;
- timing_get( &candidate->lastWorkCheck );
- spin_unlock( &candidate->lock );
- if ( _removeMissingImages ) {
- candidate = image_remove( candidate ); // No release here, the image is still returned and should be released by caller
- }
- return candidate;
- }
+ if ( !image_ensureOpen( candidate ) ) {
spin_lock( &candidate->lock );
- if ( candidate->readFd == -1 ) {
- candidate->readFd = newFd;
- spin_unlock( &candidate->lock );
- } else {
- // There was a race while opening the file (happens while not locked cause blocking), we lost the race to close new fd and proceed
- spin_unlock( &candidate->lock );
- close( newFd );
+ timing_get( &candidate->lastWorkCheck );
+ spin_unlock( &candidate->lock );
+ if ( _removeMissingImages ) {
+ candidate = image_remove( candidate ); // No release here, the image is still returned and should be released by caller
}
+ return candidate;
}
}
@@ -1812,7 +1835,7 @@ void image_closeUnusedFd()
continue;
spin_lock( &image->lock );
spin_unlock( &imageListLock );
- if ( image->users == 0 && timing_reached( &image->atime, &deadline ) ) {
+ if ( image->users == 0 && image->uplink == NULL && timing_reached( &image->atime, &deadline ) ) {
snprintf( imgstr, sizeof(imgstr), "%s:%d", image->name, (int)image->rid );
fd = image->readFd;
image->readFd = -1;
diff --git a/src/server/image.h b/src/server/image.h
index 2290744..a06c8c7 100644
--- a/src/server/image.h
+++ b/src/server/image.h
@@ -17,6 +17,8 @@ void image_saveAllCacheMaps();
bool image_saveCacheMap(dnbd3_image_t *image);
+bool image_ensureOpen(dnbd3_image_t *image);
+
dnbd3_image_t* image_get(char *name, uint16_t revision, bool checkIfWorking);
dnbd3_image_t* image_getOrLoad(char *name, uint16_t revision);
diff --git a/src/server/integrity.c b/src/server/integrity.c
index e07a076..e8124f4 100644
--- a/src/server/integrity.c
+++ b/src/server/integrity.c
@@ -117,6 +117,12 @@ static void* integrity_main(void * data UNUSED)
if ( i + 1 == queueLen ) queueLen--;
if ( image == NULL ) continue;
// We have the image. Call image_release() some time
+ // Make sure the image is open for reading (closeUnusedFd)
+ if ( !image_ensureOpen( image ) ) {
+ logadd( LOG_MINOR, "Cannot hash check block %d of %s -- no readFd", checkQueue[i].block, image->path );
+ image_release( image );
+ continue;
+ }
spin_lock( &image->lock );
if ( image->crc32 != NULL && image->realFilesize != 0 ) {
int const blocks[2] = { checkQueue[i].block, -1 };