Release 2.9.23
[nbd.git] / nbd-server.c
index e733c3e..5725af7 100644 (file)
@@ -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;
     }