X-Git-Url: http://git.alex.org.uk diff --git a/nbd-server.c b/nbd-server.c index e733c3e..5725af7 100644 --- a/nbd-server.c +++ b/nbd-server.c @@ -160,6 +160,7 @@ int dontfork = 0; #define F_FLUSH 128 /**< Whether server wants FLUSH to be sent by the client */ #define F_FUA 256 /**< Whether server wants FUA to be sent by the client */ #define F_ROTATIONAL 512 /**< Whether server wants the client to implement the elevator algorithm */ +#define F_TEMPORARY 1024 /**< Whether the backing file is temporary and should be created then unlinked */ GHashTable *children; char pidfname[256]; /**< name of our PID file */ char pidftemplate[256]; /**< template to be used for the filename of the PID file */ @@ -774,6 +775,7 @@ GArray* parse_cfile(gchar* f, GError** e) { { "flush", FALSE, PARAM_BOOL, &(s.flags), F_FLUSH }, { "fua", FALSE, PARAM_BOOL, &(s.flags), F_FUA }, { "rotational", FALSE, PARAM_BOOL, &(s.flags), F_ROTATIONAL }, + { "temporary", FALSE, PARAM_BOOL, &(s.flags), F_TEMPORARY }, { "listenaddr", FALSE, PARAM_STRING, &(s.listenaddr), 0 }, { "maxconnections", FALSE, PARAM_INT, &(s.max_connections), 0 }, }; @@ -793,7 +795,9 @@ GArray* parse_cfile(gchar* f, GError** e) { GQuark errdomain; GArray *retval=NULL; gchar **groups; - gboolean value; + gboolean bval; + gint ival; + gchar* sval; gchar* startgroup; gint i; gint j; @@ -827,25 +831,29 @@ GArray* parse_cfile(gchar* f, GError** e) { g_assert(p[j].ptype==PARAM_INT||p[j].ptype==PARAM_STRING||p[j].ptype==PARAM_BOOL); switch(p[j].ptype) { case PARAM_INT: - *((gint*)p[j].target) = - g_key_file_get_integer(cfile, + ival = g_key_file_get_integer(cfile, groups[i], p[j].paramname, &err); + if(!err) { + *((gint*)p[j].target) = ival; + } break; case PARAM_STRING: - *((gchar**)p[j].target) = - g_key_file_get_string(cfile, + sval = g_key_file_get_string(cfile, groups[i], p[j].paramname, &err); + if(!err) { + *((gchar**)p[j].target) = sval; + } break; case PARAM_BOOL: - value = g_key_file_get_boolean(cfile, + bval = g_key_file_get_boolean(cfile, groups[i], p[j].paramname, &err); if(!err) { - if(value) { + if(bval) { *((gint*)p[j].target) |= p[j].flagval; } else { *((gint*)p[j].target) &= ~(p[j].flagval); @@ -853,11 +861,6 @@ GArray* parse_cfile(gchar* f, GError** e) { } break; } - if(!strcmp(p[j].paramname, "port") && !strcmp(p[j].target, modernport)) { - g_set_error(e, errdomain, CFILE_INCORRECT_PORT, "Config file specifies new-style port for oldstyle export"); - g_key_file_free(cfile); - return NULL; - } if(err) { if(err->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND) { if(!p[j].required) { @@ -1020,7 +1023,9 @@ off_t size_autodetect(int fhandle) { stat_buf.st_size = 0; error = fstat(fhandle, &stat_buf); if (!error) { - if(stat_buf.st_size > 0) + /* always believe stat if a regular file as it might really + * be zero length */ + if (S_ISREG(stat_buf.st_mode) || (stat_buf.st_size > 0)) return (off_t)stat_buf.st_size; } else { err("fstat failed: %m"); @@ -1389,11 +1394,11 @@ CLIENT* negotiate(int net, CLIENT *client, GArray* servers, int phase) { } if (write(net, &magic, sizeof(magic)) < 0) { err_nonfatal("Negotiation failed: %m"); - if(client) + if(phase & NEG_OLD) exit(EXIT_FAILURE); } } - if(phase & NEG_MODERN) { + if ((phase & NEG_MODERN) && (phase & NEG_INIT)) { /* modern */ uint32_t reserved; uint32_t opt; @@ -1643,6 +1648,8 @@ void setupexport(CLIENT* client) { int i; off_t laststartoff = 0, lastsize = 0; int multifile = (client->server->flags & F_MULTIFILE); + int temporary = (client->server->flags & F_TEMPORARY) && !multifile; + int cancreate = (client->server->expected_size) && !multifile; client->export = g_array_new(TRUE, TRUE, sizeof(FILE_INFO)); @@ -1653,24 +1660,35 @@ void setupexport(CLIENT* client) { FILE_INFO fi; gchar *tmpname; gchar* error_string; - mode_t mode = (client->server->flags & F_READONLY) ? O_RDONLY : O_RDWR; - if(multifile) { - tmpname=g_strdup_printf("%s.%d", client->exportname, i); + if (i) + cancreate = 0; + /* if expected_size is specified, and this is the first file, we can create the file */ + mode_t mode = (client->server->flags & F_READONLY) ? + O_RDONLY : (O_RDWR | (cancreate?O_CREAT:0)); + + if (temporary) { + tmpname=g_strdup_printf("%s.%d-XXXXXX", client->exportname, i); + DEBUG( "Opening %s\n", tmpname ); + fi.fhandle = mkstemp(tmpname); } else { - tmpname=g_strdup(client->exportname); - } - DEBUG( "Opening %s\n", tmpname ); - fi.fhandle = open(tmpname, mode); - if(fi.fhandle == -1 && mode == O_RDWR) { - /* Try again because maybe media was read-only */ - fi.fhandle = open(tmpname, O_RDONLY); - if(fi.fhandle != -1) { - /* Opening the base file in copyonwrite mode is - * okay */ - if(!(client->server->flags & F_COPYONWRITE)) { - client->server->flags |= F_AUTOREADONLY; - client->server->flags |= F_READONLY; + if(multifile) { + tmpname=g_strdup_printf("%s.%d", client->exportname, i); + } else { + tmpname=g_strdup(client->exportname); + } + DEBUG( "Opening %s\n", tmpname ); + fi.fhandle = open(tmpname, mode, 0x600); + if(fi.fhandle == -1 && mode == O_RDWR) { + /* Try again because maybe media was read-only */ + fi.fhandle = open(tmpname, O_RDONLY); + if(fi.fhandle != -1) { + /* Opening the base file in copyonwrite mode is + * okay */ + if(!(client->server->flags & F_COPYONWRITE)) { + client->server->flags |= F_AUTOREADONLY; + client->server->flags |= F_READONLY; + } } } } @@ -1682,6 +1700,10 @@ void setupexport(CLIENT* client) { tmpname); err(error_string); } + + if (temporary) + unlink(tmpname); /* File will stick around whilst FD open */ + fi.startoff = laststartoff + lastsize; g_array_append_val(client->export, fi); g_free(tmpname); @@ -1691,7 +1713,17 @@ void setupexport(CLIENT* client) { laststartoff = fi.startoff; lastsize = size_autodetect(fi.fhandle); - if(!multifile) + /* If we created the file, it will be length zero */ + if (!lastsize && cancreate) { + /* we can ignore errors as we recalculate the size */ + ftruncate (fi.fhandle, client->server->expected_size); + lastsize = size_autodetect(fi.fhandle); + if (lastsize != client->server->expected_size) + err("Could not expand file"); + break; /* don't look for any more files */ + } + + if(!multifile || temporary) break; } @@ -2272,6 +2304,7 @@ void glib_message_syslog_redirect(const gchar *log_domain, break; case G_LOG_LEVEL_DEBUG: level=LOG_DEBUG; + break; default: level=LOG_ERR; }