diff --git a/README.txt b/README.txt index 434b054..d0a946c 100644 --- a/README.txt +++ b/README.txt @@ -81,6 +81,12 @@ the source in the interim. To support transparently replacing an existing server, flexnbd can switch addresses once it has received a successful migration. +When `flexnbd listen` is run, it will create a file named +.INCOMPLETE next to FILE. This will be removed when a migration +has successfully completed. The .INCOMPLETE file will be created before +the listening socket is opened. + + Options ^^^^^^^ As for 'serve', with these additions: diff --git a/src/flexnbd.c b/src/flexnbd.c index 65a4363..2bbd489 100644 --- a/src/flexnbd.c +++ b/src/flexnbd.c @@ -283,6 +283,85 @@ int flexnbd_default_deny( struct flexnbd * flexnbd ) } +char * flexnbd_incomplete_filename( struct flexnbd * flexnbd ) +{ + NULLCHECK( flexnbd ); + struct server * serve = flexnbd_server( flexnbd ); + + return serve->filename_incomplete; +} + +void make_writable( const char * filename ) +{ + NULLCHECK( filename ); + FATAL_IF_NEGATIVE( chmod( filename, S_IWUSR ), + "Couldn't chmod %s: %s", + filename, + strerror( errno ) ); +} + +/** Drops a marker file on the filesystem to show that the image we're + * serving hasn't yet finished its migration yet + */ +void flexnbd_mark_incomplete( struct flexnbd * flexnbd ) +{ + char * filename = + flexnbd_incomplete_filename( flexnbd ); + int fd; + + NULLCHECK( filename ); + + /* It's OK if the file already exists - it's perfectly possible + * that a previous process died part-way through and left it + * behind. However, we might have left the file mode in a bad + * state. + */ + + struct stat ignored; + int exists = stat( filename, &ignored ) == 0; + if ( exists ) { + /* definitely there, need to chmod */ + debug( "%s exists, making it writable", filename ); + make_writable( filename ); + } + else if ( ENOENT != errno ) { + /* Can't tell if it's there or not, weirdness. */ + fatal( "Unable to stat %s", filename ); + } + else { /* definitely not there. NOP. */ } + + + fd = open( filename, O_CREAT|O_WRONLY, 0 ); + FATAL_IF_NEGATIVE( fd, + "Couldn't open %s: %s", + filename, + strerror( errno ) ); + + /* Minor race here - in principle we could see the file + * disappear before the chmod */ + close( fd ); +} + + +/** Removes the .INCOMPLETE marker file from the filesystem. Call this + * only when you know the migration has completed successfully. + */ +void flexnbd_mark_complete( struct flexnbd * flexnbd ) +{ + char * filename = + flexnbd_incomplete_filename( flexnbd ); + + NULLCHECK( filename ); + + make_writable( filename ); + + FATAL_IF_NEGATIVE( unlink( filename ), + "Couldn't unlink %s: %s", + filename, + strerror( errno ) ); +} + + int flexnbd_serve( struct flexnbd * flexnbd ) { NULLCHECK( flexnbd ); diff --git a/src/flexnbd.h b/src/flexnbd.h index 20d442c..25a05b5 100644 --- a/src/flexnbd.h +++ b/src/flexnbd.h @@ -69,6 +69,9 @@ void flexnbd_set_server( struct flexnbd * flexnbd, struct server * serve ); void flexnbd_switch( struct flexnbd * flexnbd, struct server *(listen_cb)(struct listen *) ); int flexnbd_signal_fd( struct flexnbd * flexnbd ); +void flexnbd_mark_incomplete( struct flexnbd * flexnbd ); +void flexnbd_mark_complete( struct flexnbd * flexnbd ); + int flexnbd_serve( struct flexnbd * flexnbd ); struct server * flexnbd_server( struct flexnbd * flexnbd ); diff --git a/src/listen.c b/src/listen.c index 00fa799..de46e57 100644 --- a/src/listen.c +++ b/src/listen.c @@ -83,6 +83,7 @@ int do_listen( struct listen * listen ) flexnbd_lock_switch( listen->flexnbd ); { flexnbd_set_server( listen->flexnbd, listen->init_serve ); + flexnbd_mark_incomplete( listen->flexnbd ); } flexnbd_unlock_switch( listen->flexnbd ); @@ -93,6 +94,8 @@ int do_listen( struct listen * listen ) if( have_control ) { + flexnbd_mark_complete( listen->flexnbd ); + info( "Taking control."); flexnbd_switch( listen->flexnbd, listen_switch ); /* WATCH FOR RACES HERE: the server hasn't been