summary refs log tree commit diff stats
path: root/block
diff options
context:
space:
mode:
Diffstat (limited to 'block')
-rw-r--r--block/block-backend.c5
-rw-r--r--block/commit.c4
-rw-r--r--block/qapi.c20
-rw-r--r--block/throttle-groups.c28
-rw-r--r--block/vmdk.c44
-rw-r--r--block/vpc.c30
-rw-r--r--block/vvfat.c90
7 files changed, 147 insertions, 74 deletions
diff --git a/block/block-backend.c b/block/block-backend.c
index fe3542b3f8..968438c149 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -83,7 +83,6 @@ static const AIOCBInfo block_backend_aiocb_info = {
 
 static void drive_info_del(DriveInfo *dinfo);
 static BlockBackend *bdrv_first_blk(BlockDriverState *bs);
-static char *blk_get_attached_dev_id(BlockBackend *blk);
 
 /* All BlockBackends */
 static QTAILQ_HEAD(, BlockBackend) block_backends =
@@ -343,7 +342,7 @@ void blk_unref(BlockBackend *blk)
  * Behaves similarly to blk_next() but iterates over all BlockBackends, even the
  * ones which are hidden (i.e. are not referenced by the monitor).
  */
-static BlockBackend *blk_all_next(BlockBackend *blk)
+BlockBackend *blk_all_next(BlockBackend *blk)
 {
     return blk ? QTAILQ_NEXT(blk, link)
                : QTAILQ_FIRST(&block_backends);
@@ -726,7 +725,7 @@ void *blk_get_attached_dev(BlockBackend *blk)
 
 /* Return the qdev ID, or if no ID is assigned the QOM path, of the block
  * device attached to the BlockBackend. */
-static char *blk_get_attached_dev_id(BlockBackend *blk)
+char *blk_get_attached_dev_id(BlockBackend *blk)
 {
     DeviceState *dev;
 
diff --git a/block/commit.c b/block/commit.c
index 13143608f8..5cc910f567 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -90,7 +90,9 @@ static void commit_complete(BlockJob *job, void *opaque)
 
     /* Make sure overlay_bs and top stay around until bdrv_set_backing_hd() */
     bdrv_ref(top);
-    bdrv_ref(overlay_bs);
+    if (overlay_bs) {
+        bdrv_ref(overlay_bs);
+    }
 
     /* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
      * the normal backing chain can be restored. */
diff --git a/block/qapi.c b/block/qapi.c
index 080eb8f115..95b2e2daa5 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -322,11 +322,21 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
 {
     BlockInfo *info = g_malloc0(sizeof(*info));
     BlockDriverState *bs = blk_bs(blk);
+    char *qdev;
+
     info->device = g_strdup(blk_name(blk));
     info->type = g_strdup("unknown");
     info->locked = blk_dev_is_medium_locked(blk);
     info->removable = blk_dev_has_removable_media(blk);
 
+    qdev = blk_get_attached_dev_id(blk);
+    if (qdev && *qdev) {
+        info->has_qdev = true;
+        info->qdev = qdev;
+    } else {
+        g_free(qdev);
+    }
+
     if (blk_dev_has_tray(blk)) {
         info->has_tray_open = true;
         info->tray_open = blk_dev_is_tray_open(blk);
@@ -462,8 +472,14 @@ BlockInfoList *qmp_query_block(Error **errp)
     BlockBackend *blk;
     Error *local_err = NULL;
 
-    for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
-        BlockInfoList *info = g_malloc0(sizeof(*info));
+    for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) {
+        BlockInfoList *info;
+
+        if (!*blk_name(blk) && !blk_get_attached_dev(blk)) {
+            continue;
+        }
+
+        info = g_malloc0(sizeof(*info));
         bdrv_query_info(blk, &info->value, &local_err);
         if (local_err) {
             error_propagate(errp, local_err);
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index da2b490c38..890bfded3f 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -61,6 +61,7 @@ typedef struct ThrottleGroup {
     QLIST_HEAD(, BlockBackendPublic) head;
     BlockBackend *tokens[2];
     bool any_timer_armed[2];
+    QEMUClockType clock_type;
 
     /* These two are protected by the global throttle_groups_lock */
     unsigned refcount;
@@ -98,6 +99,12 @@ ThrottleState *throttle_group_incref(const char *name)
     if (!tg) {
         tg = g_new0(ThrottleGroup, 1);
         tg->name = g_strdup(name);
+        tg->clock_type = QEMU_CLOCK_REALTIME;
+
+        if (qtest_enabled()) {
+            /* For testing block IO throttling only */
+            tg->clock_type = QEMU_CLOCK_VIRTUAL;
+        }
         qemu_mutex_init(&tg->lock);
         throttle_init(&tg->ts);
         QLIST_INIT(&tg->head);
@@ -310,7 +317,7 @@ static void schedule_next_request(BlockBackend *blk, bool is_write)
             token = blk;
         } else {
             ThrottleTimers *tt = &blk_get_public(token)->throttle_timers;
-            int64_t now = qemu_clock_get_ns(tt->clock_type);
+            int64_t now = qemu_clock_get_ns(tg->clock_type);
             timer_mod(tt->timers[is_write], now);
             tg->any_timer_armed[is_write] = true;
         }
@@ -419,18 +426,10 @@ void throttle_group_restart_blk(BlockBackend *blk)
 void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg)
 {
     BlockBackendPublic *blkp = blk_get_public(blk);
-    ThrottleTimers *tt = &blkp->throttle_timers;
     ThrottleState *ts = blkp->throttle_state;
     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
     qemu_mutex_lock(&tg->lock);
-    /* throttle_config() cancels the timers */
-    if (timer_pending(tt->timers[0])) {
-        tg->any_timer_armed[0] = false;
-    }
-    if (timer_pending(tt->timers[1])) {
-        tg->any_timer_armed[1] = false;
-    }
-    throttle_config(ts, tt, cfg);
+    throttle_config(ts, tg->clock_type, cfg);
     qemu_mutex_unlock(&tg->lock);
 
     throttle_group_restart_blk(blk);
@@ -497,13 +496,6 @@ void throttle_group_register_blk(BlockBackend *blk, const char *groupname)
     BlockBackendPublic *blkp = blk_get_public(blk);
     ThrottleState *ts = throttle_group_incref(groupname);
     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
-    int clock_type = QEMU_CLOCK_REALTIME;
-
-    if (qtest_enabled()) {
-        /* For testing block IO throttling only */
-        clock_type = QEMU_CLOCK_VIRTUAL;
-    }
-
     blkp->throttle_state = ts;
 
     qemu_mutex_lock(&tg->lock);
@@ -518,7 +510,7 @@ void throttle_group_register_blk(BlockBackend *blk, const char *groupname)
 
     throttle_timers_init(&blkp->throttle_timers,
                          blk_get_aio_context(blk),
-                         clock_type,
+                         tg->clock_type,
                          read_timer_cb,
                          write_timer_cb,
                          blk);
diff --git a/block/vmdk.c b/block/vmdk.c
index 24d71b5982..0fc97391a6 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -242,10 +242,11 @@ static void vmdk_free_last_extent(BlockDriverState *bs)
     s->extents = g_renew(VmdkExtent, s->extents, s->num_extents);
 }
 
-static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
+/* Return -ve errno, or 0 on success and write CID into *pcid. */
+static int vmdk_read_cid(BlockDriverState *bs, int parent, uint32_t *pcid)
 {
     char *desc;
-    uint32_t cid = 0xffffffff;
+    uint32_t cid;
     const char *p_name, *cid_str;
     size_t cid_str_size;
     BDRVVmdkState *s = bs->opaque;
@@ -254,8 +255,7 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
     desc = g_malloc0(DESC_SIZE);
     ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE);
     if (ret < 0) {
-        g_free(desc);
-        return 0;
+        goto out;
     }
 
     if (parent) {
@@ -268,13 +268,21 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
 
     desc[DESC_SIZE - 1] = '\0';
     p_name = strstr(desc, cid_str);
-    if (p_name != NULL) {
-        p_name += cid_str_size;
-        sscanf(p_name, "%" SCNx32, &cid);
+    if (p_name == NULL) {
+        ret = -EINVAL;
+        goto out;
     }
+    p_name += cid_str_size;
+    if (sscanf(p_name, "%" SCNx32, &cid) != 1) {
+        ret = -EINVAL;
+        goto out;
+    }
+    *pcid = cid;
+    ret = 0;
 
+out:
     g_free(desc);
-    return cid;
+    return ret;
 }
 
 static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
@@ -322,7 +330,10 @@ static int vmdk_is_cid_valid(BlockDriverState *bs)
     if (!s->cid_checked && bs->backing) {
         BlockDriverState *p_bs = bs->backing->bs;
 
-        cur_pcid = vmdk_read_cid(p_bs, 0);
+        if (vmdk_read_cid(p_bs, 0, &cur_pcid) != 0) {
+            /* read failure: report as not valid */
+            return 0;
+        }
         if (s->parent_cid != cur_pcid) {
             /* CID not valid */
             return 0;
@@ -975,8 +986,14 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
     if (ret) {
         goto fail;
     }
-    s->cid = vmdk_read_cid(bs, 0);
-    s->parent_cid = vmdk_read_cid(bs, 1);
+    ret = vmdk_read_cid(bs, 0, &s->cid);
+    if (ret) {
+        goto fail;
+    }
+    ret = vmdk_read_cid(bs, 1, &s->parent_cid);
+    if (ret) {
+        goto fail;
+    }
     qemu_co_mutex_init(&s->lock);
 
     /* Disable migration when VMDK images are used */
@@ -2008,8 +2025,11 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
             ret = -EINVAL;
             goto exit;
         }
-        parent_cid = vmdk_read_cid(blk_bs(blk), 0);
+        ret = vmdk_read_cid(blk_bs(blk), 0, &parent_cid);
         blk_unref(blk);
+        if (ret) {
+            goto exit;
+        }
         snprintf(parent_desc_line, BUF_SIZE,
                 "parentFileNameHint=\"%s\"", backing_file);
     }
diff --git a/block/vpc.c b/block/vpc.c
index 8057d42a23..10e6519d78 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -460,17 +460,23 @@ static int vpc_reopen_prepare(BDRVReopenState *state,
 /*
  * Returns the absolute byte offset of the given sector in the image file.
  * If the sector is not allocated, -1 is returned instead.
+ * If an error occurred trying to write an updated block bitmap back to
+ * the file, -2 is returned, and the error value is written to *err.
+ * This can only happen for a write operation.
  *
  * The parameter write must be 1 if the offset will be used for a write
  * operation (the block bitmaps is updated then), 0 otherwise.
+ * If write is true then err must not be NULL.
  */
 static inline int64_t get_image_offset(BlockDriverState *bs, uint64_t offset,
-                                       bool write)
+                                       bool write, int *err)
 {
     BDRVVPCState *s = bs->opaque;
     uint64_t bitmap_offset, block_offset;
     uint32_t pagetable_index, offset_in_block;
 
+    assert(!(write && err == NULL));
+
     pagetable_index = offset / s->block_size;
     offset_in_block = offset % s->block_size;
 
@@ -487,10 +493,15 @@ static inline int64_t get_image_offset(BlockDriverState *bs, uint64_t offset,
        correctness. */
     if (write && (s->last_bitmap_offset != bitmap_offset)) {
         uint8_t bitmap[s->bitmap_size];
+        int r;
 
         s->last_bitmap_offset = bitmap_offset;
         memset(bitmap, 0xff, s->bitmap_size);
-        bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size);
+        r = bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size);
+        if (r < 0) {
+            *err = r;
+            return -2;
+        }
     }
 
     return block_offset;
@@ -561,7 +572,7 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t offset)
     if (ret < 0)
         goto fail;
 
-    return get_image_offset(bs, offset, false);
+    return get_image_offset(bs, offset, false, NULL);
 
 fail:
     s->free_data_block_offset -= (s->block_size + s->bitmap_size);
@@ -601,7 +612,7 @@ vpc_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
     qemu_iovec_init(&local_qiov, qiov->niov);
 
     while (bytes > 0) {
-        image_offset = get_image_offset(bs, offset, false);
+        image_offset = get_image_offset(bs, offset, false, NULL);
         n_bytes = MIN(bytes, s->block_size - (offset % s->block_size));
 
         if (image_offset == -1) {
@@ -650,7 +661,11 @@ vpc_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
     qemu_iovec_init(&local_qiov, qiov->niov);
 
     while (bytes > 0) {
-        image_offset = get_image_offset(bs, offset, true);
+        image_offset = get_image_offset(bs, offset, true, &ret);
+        if (image_offset == -2) {
+            /* Failed to write block bitmap: can't proceed with write */
+            goto fail;
+        }
         n_bytes = MIN(bytes, s->block_size - (offset % s->block_size));
 
         if (image_offset == -1) {
@@ -702,7 +717,7 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
 
     qemu_co_mutex_lock(&s->lock);
 
-    offset = get_image_offset(bs, sector_num << BDRV_SECTOR_BITS, false);
+    offset = get_image_offset(bs, sector_num << BDRV_SECTOR_BITS, false, NULL);
     start = offset;
     allocated = (offset != -1);
     *pnum = 0;
@@ -727,7 +742,8 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
         if (nb_sectors == 0) {
             break;
         }
-        offset = get_image_offset(bs, sector_num << BDRV_SECTOR_BITS, false);
+        offset = get_image_offset(bs, sector_num << BDRV_SECTOR_BITS, false,
+                                  NULL);
     } while (offset == -1);
 
     qemu_co_mutex_unlock(&s->lock);
diff --git a/block/vvfat.c b/block/vvfat.c
index 4dae790203..a9e207f7f0 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -71,6 +71,17 @@ void nonono(const char* file, int line, const char* msg) {
 
 #endif
 
+/* bootsector OEM name. see related compatibility problems at:
+ * https://jdebp.eu/FGA/volume-boot-block-oem-name-field.html
+ * http://seasip.info/Misc/oemid.html
+ */
+#define BOOTSECTOR_OEM_NAME "MSWIN4.1"
+
+#define DIR_DELETED 0xe5
+#define DIR_KANJI DIR_DELETED
+#define DIR_KANJI_FAKE 0x05
+#define DIR_FREE 0x00
+
 /* dynamic array functions */
 typedef struct array_t {
     char* pointer;
@@ -104,6 +115,7 @@ static inline int array_ensure_allocated(array_t* array, int index)
         array->pointer = g_realloc(array->pointer, new_size);
         if (!array->pointer)
             return -1;
+        memset(array->pointer + array->size, 0, new_size - array->size);
         array->size = new_size;
         array->next = index + 1;
     }
@@ -466,7 +478,7 @@ static direntry_t *create_long_filename(BDRVVVFATState *s, const char *filename)
 
 static char is_free(const direntry_t* direntry)
 {
-    return direntry->name[0]==0xe5 || direntry->name[0]==0x00;
+    return direntry->name[0] == DIR_DELETED || direntry->name[0] == DIR_FREE;
 }
 
 static char is_volume_label(const direntry_t* direntry)
@@ -487,7 +499,7 @@ static char is_short_name(const direntry_t* direntry)
 
 static char is_directory(const direntry_t* direntry)
 {
-    return direntry->attributes & 0x10 && direntry->name[0] != 0xe5;
+    return direntry->attributes & 0x10 && direntry->name[0] != DIR_DELETED;
 }
 
 static inline char is_dot(const direntry_t* direntry)
@@ -537,7 +549,7 @@ static direntry_t *create_short_filename(BDRVVVFATState *s,
     const gchar *p, *last_dot = NULL;
     gunichar c;
     bool lossy_conversion = false;
-    char tail[11];
+    char tail[8];
 
     if (!entry) {
         return NULL;
@@ -589,8 +601,8 @@ static direntry_t *create_short_filename(BDRVVVFATState *s,
         }
     }
 
-    if (entry->name[0] == 0xe5) {
-        entry->name[0] = 0x05;
+    if (entry->name[0] == DIR_KANJI) {
+        entry->name[0] = DIR_KANJI_FAKE;
     }
 
     /* numeric-tail generation */
@@ -602,7 +614,8 @@ static direntry_t *create_short_filename(BDRVVVFATState *s,
     for (i = lossy_conversion ? 1 : 0; i < 999999; i++) {
         direntry_t *entry1;
         if (i > 0) {
-            int len = sprintf(tail, "~%d", i);
+            int len = snprintf(tail, sizeof(tail), "~%u", (unsigned)i);
+            assert(len <= 7);
             memcpy(entry->name + MIN(j, 8 - len), tail, len);
         }
         for (entry1 = array_get(&(s->directory), directory_start);
@@ -1023,7 +1036,7 @@ static int init_directories(BDRVVVFATState* s,
     bootsector->jump[0]=0xeb;
     bootsector->jump[1]=0x3e;
     bootsector->jump[2]=0x90;
-    memcpy(bootsector->name, "MSWIN4.1", 8);
+    memcpy(bootsector->name, BOOTSECTOR_OEM_NAME, 8);
     bootsector->sector_size=cpu_to_le16(0x200);
     bootsector->sectors_per_cluster=s->sectors_per_cluster;
     bootsector->reserved_sectors=cpu_to_le16(1);
@@ -1658,6 +1671,7 @@ typedef struct {
      * filename length is 0x3f * 13 bytes.
      */
     unsigned char name[0x3f * 13 + 1];
+    gunichar2 name2[0x3f * 13 + 1];
     int checksum, len;
     int sequence_number;
 } long_file_name;
@@ -1679,16 +1693,21 @@ static int parse_long_name(long_file_name* lfn,
         return 1;
 
     if (pointer[0] & 0x40) {
+        /* first entry; do some initialization */
         lfn->sequence_number = pointer[0] & 0x3f;
         lfn->checksum = pointer[13];
         lfn->name[0] = 0;
         lfn->name[lfn->sequence_number * 13] = 0;
-    } else if ((pointer[0] & 0x3f) != --lfn->sequence_number)
+    } else if ((pointer[0] & 0x3f) != --lfn->sequence_number) {
+        /* not the expected sequence number */
         return -1;
-    else if (pointer[13] != lfn->checksum)
+    } else if (pointer[13] != lfn->checksum) {
+        /* not the expected checksum */
         return -2;
-    else if (pointer[12] || pointer[26] || pointer[27])
+    } else if (pointer[12] || pointer[26] || pointer[27]) {
+        /* invalid zero fields */
         return -3;
+    }
 
     offset = 13 * (lfn->sequence_number - 1);
     for (i = 0, j = 1; i < 13; i++, j+=2) {
@@ -1697,16 +1716,29 @@ static int parse_long_name(long_file_name* lfn,
         else if (j == 26)
             j = 28;
 
-        if (pointer[j+1] == 0)
-            lfn->name[offset + i] = pointer[j];
-        else if (pointer[j+1] != 0xff || (pointer[0] & 0x40) == 0)
-            return -4;
-        else
-            lfn->name[offset + i] = 0;
+        if (pointer[j] == 0 && pointer[j + 1] == 0) {
+            /* end of long file name */
+            break;
+        }
+        gunichar2 c = (pointer[j + 1] << 8) + pointer[j];
+        lfn->name2[offset + i] = c;
     }
 
-    if (pointer[0] & 0x40)
-        lfn->len = offset + strlen((char*)lfn->name + offset);
+    if (pointer[0] & 0x40) {
+        /* first entry; set len */
+        lfn->len = offset + i;
+    }
+    if ((pointer[0] & 0x3f) == 0x01) {
+        /* last entry; finalize entry */
+        glong olen;
+        gchar *utf8 = g_utf16_to_utf8(lfn->name2, lfn->len, NULL, &olen, NULL);
+        if (!utf8) {
+            return -4;
+        }
+        lfn->len = olen;
+        memcpy(lfn->name, utf8, olen + 1);
+        g_free(utf8);
+    }
 
     return 0;
 }
@@ -1722,12 +1754,14 @@ static int parse_short_name(BDRVVVFATState* s,
 
     for (j = 7; j >= 0 && direntry->name[j] == ' '; j--);
     for (i = 0; i <= j; i++) {
-        if (direntry->name[i] <= ' ' || direntry->name[i] > 0x7f)
+        uint8_t c = direntry->name[i];
+        if (c != to_valid_short_char(c)) {
             return -1;
-        else if (s->downcase_short_names)
+        } else if (s->downcase_short_names) {
             lfn->name[i] = qemu_tolower(direntry->name[i]);
-        else
+        } else {
             lfn->name[i] = direntry->name[i];
+        }
     }
 
     for (j = 2; j >= 0 && direntry->name[8 + j] == ' '; j--) {
@@ -1737,7 +1771,7 @@ static int parse_short_name(BDRVVVFATState* s,
         lfn->name[i + j + 1] = '\0';
         for (;j >= 0; j--) {
             uint8_t c = direntry->name[8 + j];
-            if (c <= ' ' || c > 0x7f) {
+            if (c != to_valid_short_char(c)) {
                 return -2;
             } else if (s->downcase_short_names) {
                 lfn->name[i + j] = qemu_tolower(c);
@@ -1748,8 +1782,8 @@ static int parse_short_name(BDRVVVFATState* s,
     } else
         lfn->name[i + j + 1] = '\0';
 
-    if (lfn->name[0] == 0x05) {
-        lfn->name[0] = 0xe5;
+    if (lfn->name[0] == DIR_KANJI_FAKE) {
+        lfn->name[0] = DIR_KANJI;
     }
     lfn->len = strlen((char*)lfn->name);
 
@@ -2955,7 +2989,6 @@ DLOG(checkpoint());
     /*
      * Some sanity checks:
      * - do not allow writing to the boot sector
-     * - do not allow to write non-ASCII filenames
      */
 
     if (sector_num < s->offset_to_fat)
@@ -2989,13 +3022,8 @@ DLOG(checkpoint());
                 direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num));
 
                 for (k = 0; k < (end - begin) * 0x10; k++) {
-                    /* do not allow non-ASCII filenames */
-                    if (parse_long_name(&lfn, direntries + k) < 0) {
-                        fprintf(stderr, "Warning: non-ASCII filename\n");
-                        return -1;
-                    }
                     /* no access to the direntry of a read-only file */
-                    else if (is_short_name(direntries+k) &&
+                    if (is_short_name(direntries + k) &&
                             (direntries[k].attributes & 1)) {
                         if (memcmp(direntries + k,
                                     array_get(&(s->directory), dir_index + k),