diff mbox

[v4,3/6] block: Add bdrv_filename()

Message ID 1439939415-18180-4-git-send-email-mreitz@redhat.com
State New
Headers show

Commit Message

Max Reitz Aug. 18, 2015, 11:10 p.m. UTC
Split the part which actually refreshes the BlockDriverState.filename
field off of bdrv_refresh_filename() into a more generic function
bdrv_filename(), which first calls bdrv_refresh_filename() and then
stores a qemu-usable filename into the given buffer instead of
BlockDriverState.filename.

Since bdrv_refresh_filename() therefore no longer refreshes that field,
some calls to that function have to be replaced by calls to
bdrv_filename() "manually" refreshing the BDS filename field (this is
only temporary).

Additionally, a wrapper function bdrv_filename_alloc() is added which
allocates a buffer of size PATH_MAX, call bdrv_filename() on that buffer
and returns it, since needing a temporary buffer for the filename is a
rather common pattern.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 block.c               | 39 ++++++++++++++++++++++++++++++++-------
 block/blkverify.c     |  3 ++-
 block/quorum.c        |  2 +-
 include/block/block.h |  2 ++
 4 files changed, 37 insertions(+), 9 deletions(-)

Comments

Eric Blake Aug. 31, 2015, 9 p.m. UTC | #1
On 08/18/2015 05:10 PM, Max Reitz wrote:
> Split the part which actually refreshes the BlockDriverState.filename
> field off of bdrv_refresh_filename() into a more generic function
> bdrv_filename(), which first calls bdrv_refresh_filename() and then
> stores a qemu-usable filename into the given buffer instead of
> BlockDriverState.filename.
> 
> Since bdrv_refresh_filename() therefore no longer refreshes that field,
> some calls to that function have to be replaced by calls to
> bdrv_filename() "manually" refreshing the BDS filename field (this is
> only temporary).
> 
> Additionally, a wrapper function bdrv_filename_alloc() is added which
> allocates a buffer of size PATH_MAX, call bdrv_filename() on that buffer
> and returns it, since needing a temporary buffer for the filename is a
> rather common pattern.
> 
> Signed-off-by: Max Reitz <mreitz@redhat.com>
> ---
>  block.c               | 39 ++++++++++++++++++++++++++++++++-------
>  block/blkverify.c     |  3 ++-
>  block/quorum.c        |  2 +-
>  include/block/block.h |  2 ++
>  4 files changed, 37 insertions(+), 9 deletions(-)
> 

> +/* First refreshes exact_filename and full_open_options by calling
> + * bdrv_refresh_filename(). Then, if exact_filename is set, it is copied into
> + * the target buffer. Otherwise, full_open_options is converted to a JSON
> + * object, prefixed with "json:" (for use through the JSON pseudo protocol) and
> + * put there.
> + *
> + * If sz > 0, the string put into the buffer will always be null-terminated.
> + *
> + * Returns @dest.
> + */
> +char *bdrv_filename(BlockDriverState *bs, char *dest, size_t sz)
> +{

How does one tell if 'sz' was large enough, vs. too short and therefore
the snprintf() truncated the resulting string?  Would the code be any
simpler if this always returned a freshly g_malloc'd string of the right
length, rather than making the caller have to pre-allocate and guess
whether the allocation was big enough?

> +    bdrv_refresh_filename(bs);
> +
> +    if (sz > INT_MAX) {
> +        sz = INT_MAX;
> +    }
>  
>      if (bs->exact_filename[0]) {
> -        pstrcpy(bs->filename, sizeof(bs->filename), bs->exact_filename);
> +        pstrcpy(dest, sz, bs->exact_filename);
>      } else if (bs->full_open_options) {
>          QString *json = qobject_to_json(QOBJECT(bs->full_open_options));
> -        snprintf(bs->filename, sizeof(bs->filename), "json:%s",
> -                 qstring_get_str(json));
> +        snprintf(dest, sz, "json:%s", qstring_get_str(json));
>          QDECREF(json);
>      }
> +
> +    return dest;

In other words, I think it's very dangerous to use snprintf() without
checking whether the result fit.
Max Reitz Sept. 2, 2015, 2:59 p.m. UTC | #2
On 31.08.2015 23:00, Eric Blake wrote:
> On 08/18/2015 05:10 PM, Max Reitz wrote:
>> Split the part which actually refreshes the BlockDriverState.filename
>> field off of bdrv_refresh_filename() into a more generic function
>> bdrv_filename(), which first calls bdrv_refresh_filename() and then
>> stores a qemu-usable filename into the given buffer instead of
>> BlockDriverState.filename.
>>
>> Since bdrv_refresh_filename() therefore no longer refreshes that field,
>> some calls to that function have to be replaced by calls to
>> bdrv_filename() "manually" refreshing the BDS filename field (this is
>> only temporary).
>>
>> Additionally, a wrapper function bdrv_filename_alloc() is added which
>> allocates a buffer of size PATH_MAX, call bdrv_filename() on that buffer
>> and returns it, since needing a temporary buffer for the filename is a
>> rather common pattern.
>>
>> Signed-off-by: Max Reitz <mreitz@redhat.com>
>> ---
>>  block.c               | 39 ++++++++++++++++++++++++++++++++-------
>>  block/blkverify.c     |  3 ++-
>>  block/quorum.c        |  2 +-
>>  include/block/block.h |  2 ++
>>  4 files changed, 37 insertions(+), 9 deletions(-)
>>
> 
>> +/* First refreshes exact_filename and full_open_options by calling
>> + * bdrv_refresh_filename(). Then, if exact_filename is set, it is copied into
>> + * the target buffer. Otherwise, full_open_options is converted to a JSON
>> + * object, prefixed with "json:" (for use through the JSON pseudo protocol) and
>> + * put there.
>> + *
>> + * If sz > 0, the string put into the buffer will always be null-terminated.
>> + *
>> + * Returns @dest.
>> + */
>> +char *bdrv_filename(BlockDriverState *bs, char *dest, size_t sz)
>> +{
> 
> How does one tell if 'sz' was large enough, vs. too short and therefore
> the snprintf() truncated the resulting string?  Would the code be any
> simpler if this always returned a freshly g_malloc'd string of the right
> length, rather than making the caller have to pre-allocate and guess
> whether the allocation was big enough?
> 
>> +    bdrv_refresh_filename(bs);
>> +
>> +    if (sz > INT_MAX) {
>> +        sz = INT_MAX;
>> +    }
>>  
>>      if (bs->exact_filename[0]) {
>> -        pstrcpy(bs->filename, sizeof(bs->filename), bs->exact_filename);
>> +        pstrcpy(dest, sz, bs->exact_filename);
>>      } else if (bs->full_open_options) {
>>          QString *json = qobject_to_json(QOBJECT(bs->full_open_options));
>> -        snprintf(bs->filename, sizeof(bs->filename), "json:%s",
>> -                 qstring_get_str(json));
>> +        snprintf(dest, sz, "json:%s", qstring_get_str(json));
>>          QDECREF(json);
>>      }
>> +
>> +    return dest;
> 
> In other words, I think it's very dangerous to use snprintf() without
> checking whether the result fit.

There are a couple of places in qemu which copy BDS.filename into a
pre-existing buffer, so I'd rather not just drop bdrv_filename() as it is.

I guess I'll just make it so that calling bdrv_filename() with a NULL
dest will result in the buffer being allocated.

Note however that there are a couple of places in qemu which rely on
filenames not exceeding PATH_MAX (by using PATH_MAX sized buffers for
holding them). Maybe we'll eventually get around to fix them, but right
now it's a limitation not introduced by this series.

Max
diff mbox

Patch

diff --git a/block.c b/block.c
index 1572785..d2a3e8d 100644
--- a/block.c
+++ b/block.c
@@ -1559,7 +1559,7 @@  static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
         }
     }
 
-    bdrv_refresh_filename(bs);
+    bdrv_filename(bs, bs->filename, sizeof(bs->filename));
 
     /* For snapshot=on, create a temporary qcow2 overlay. bs points to the
      * temporary snapshot afterwards. */
@@ -4153,9 +4153,6 @@  static bool append_open_options(QDict *d, BlockDriverState *bs)
  *  - full_open_options: Options which, when given when opening a block device
  *                       (without a filename), result in a BDS (mostly)
  *                       equalling the given one
- *  - filename: If exact_filename is set, it is copied here. Otherwise,
- *              full_open_options is converted to a JSON object, prefixed with
- *              "json:" (for use through the JSON pseudo protocol) and put here.
  */
 void bdrv_refresh_filename(BlockDriverState *bs)
 {
@@ -4242,15 +4239,43 @@  void bdrv_refresh_filename(BlockDriverState *bs)
 
         bs->full_open_options = opts;
     }
+}
+
+/* First refreshes exact_filename and full_open_options by calling
+ * bdrv_refresh_filename(). Then, if exact_filename is set, it is copied into
+ * the target buffer. Otherwise, full_open_options is converted to a JSON
+ * object, prefixed with "json:" (for use through the JSON pseudo protocol) and
+ * put there.
+ *
+ * If sz > 0, the string put into the buffer will always be null-terminated.
+ *
+ * Returns @dest.
+ */
+char *bdrv_filename(BlockDriverState *bs, char *dest, size_t sz)
+{
+    bdrv_refresh_filename(bs);
+
+    if (sz > INT_MAX) {
+        sz = INT_MAX;
+    }
 
     if (bs->exact_filename[0]) {
-        pstrcpy(bs->filename, sizeof(bs->filename), bs->exact_filename);
+        pstrcpy(dest, sz, bs->exact_filename);
     } else if (bs->full_open_options) {
         QString *json = qobject_to_json(QOBJECT(bs->full_open_options));
-        snprintf(bs->filename, sizeof(bs->filename), "json:%s",
-                 qstring_get_str(json));
+        snprintf(dest, sz, "json:%s", qstring_get_str(json));
         QDECREF(json);
     }
+
+    return dest;
+}
+
+/* Wrapper around bdrv_filename() using g_malloc(PATH_MAX) for the destination
+ * buffer.
+ */
+char *bdrv_filename_alloc(BlockDriverState *bs)
+{
+    return bdrv_filename(bs, g_malloc(PATH_MAX), PATH_MAX);
 }
 
 /* This accessor function purpose is to allow the device models to access the
diff --git a/block/blkverify.c b/block/blkverify.c
index d277e63..cb9ce02 100644
--- a/block/blkverify.c
+++ b/block/blkverify.c
@@ -309,7 +309,8 @@  static void blkverify_refresh_filename(BlockDriverState *bs)
     BDRVBlkverifyState *s = bs->opaque;
 
     /* bs->file has already been refreshed */
-    bdrv_refresh_filename(s->test_file);
+    bdrv_filename(s->test_file, s->test_file->filename,
+                  sizeof(s->test_file->filename));
 
     if (bs->file->full_open_options && s->test_file->full_open_options) {
         QDict *opts = qdict_new();
diff --git a/block/quorum.c b/block/quorum.c
index 2f6c45f..00d1fb0 100644
--- a/block/quorum.c
+++ b/block/quorum.c
@@ -1003,7 +1003,7 @@  static void quorum_refresh_filename(BlockDriverState *bs)
     int i;
 
     for (i = 0; i < s->num_children; i++) {
-        bdrv_refresh_filename(s->bs[i]);
+        bdrv_filename(s->bs[i], s->bs[i]->filename, sizeof(s->bs[i]->filename));
         if (!s->bs[i]->full_open_options) {
             return;
         }
diff --git a/include/block/block.h b/include/block/block.h
index a78e4f1..4be6c18 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -267,6 +267,8 @@  BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
     const char *backing_file);
 int bdrv_get_backing_file_depth(BlockDriverState *bs);
 void bdrv_refresh_filename(BlockDriverState *bs);
+char *bdrv_filename(BlockDriverState *bs, char *dest, size_t sz);
+char *bdrv_filename_alloc(BlockDriverState *bs);
 int bdrv_truncate(BlockDriverState *bs, int64_t offset);
 int64_t bdrv_nb_sectors(BlockDriverState *bs);
 int64_t bdrv_getlength(BlockDriverState *bs);