qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Matthew Booth <mbooth@redhat.com>
To: Kevin Wolf <kwolf@redhat.com>
Cc: peter.maydell@linaro.org, qemu-devel@nongnu.org, stefanha@redhat.com
Subject: Re: [Qemu-devel] [PATCH 1/3] curl: Fix parsing of readahead option from filename
Date: Thu, 01 May 2014 09:56:33 +0100	[thread overview]
Message-ID: <53620C41.3040506@redhat.com> (raw)
In-Reply-To: <20140430151638.GE4380@noname.redhat.com>

On 30/04/14 16:16, Kevin Wolf wrote:
> Am 30.04.2014 um 16:20 hat Matthew Booth geschrieben:
>> curl_parse_filename wasn't removing the option string from the url,
>> resulting in a 404.
>>
>> This change is a essentially a rewrite of that function as I also need
>> to extend it to handle more options. The rewrite is also much easier
>> to read.
>>
>> Signed-off-by: Matthew Booth <mbooth@redhat.com>
>> ---
>>  block/curl.c | 81 ++++++++++++++++++++++++++++++++++++++----------------------
>>  1 file changed, 52 insertions(+), 29 deletions(-)
>>
>> diff --git a/block/curl.c b/block/curl.c
>> index d2f1084..4de6856 100644
>> --- a/block/curl.c
>> +++ b/block/curl.c
>> @@ -46,12 +46,15 @@
>>  #define CURL_NUM_STATES 8
>>  #define CURL_NUM_ACB    8
>>  #define SECTOR_SIZE     512
>> -#define READ_AHEAD_SIZE (256 * 1024)
>> +#define READ_AHEAD_DEFAULT (256 * 1024)
>>  
>>  #define FIND_RET_NONE   0
>>  #define FIND_RET_OK     1
>>  #define FIND_RET_WAIT   2
>>  
>> +#define CURL_BLOCK_OPT_URL       "url"
>> +#define CURL_BLOCK_OPT_READAHEAD "readahead"
>> +
>>  struct BDRVCURLState;
>>  
>>  typedef struct CURLAIOCB {
>> @@ -396,41 +399,60 @@ static void curl_clean_state(CURLState *s)
>>  static void curl_parse_filename(const char *filename, QDict *options,
>>                                  Error **errp)
>>  {
>> -
>> -    #define RA_OPTSTR ":readahead="
>>      char *file;
>> -    char *ra;
>> -    const char *ra_val;
>> -    int parse_state = 0;
>> +    char *end;
>>  
>>      file = g_strdup(filename);
>> +    end = file + strlen(file) - 1;
>> +
>> +    /* Don't fail if we've been passed an empty string.
>> +     * Only examine strings with a trailing ':'
>> +     */
>> +    if (end >= file && *end == ':') {
>> +        /* We exit this loop when we don't find a recognised option immediately
>> +         * preceding the trailing ':' of the form ':<option>=<value>'
>> +         *
>> +         * If filename has a trailing ':' but no preceding options, we don't
>> +         * remove the trailing ':'.
>> +         */
>> +        for (;;) {
>> +            /* Look for the preceding colon */
>> +            char *colon = memrchr(file, ':', end - file);
>> +            if (NULL == colon) {
>> +                break;
>> +            }
>>  
>> -    /* Parse a trailing ":readahead=#:" param, if present. */
>> -    ra = file + strlen(file) - 1;
>> -    while (ra >= file) {
>> -        if (parse_state == 0) {
>> -            if (*ra == ':') {
>> -                parse_state++;
>> -            } else {
>> +            char *opt_start = colon + 1;
>> +
>> +            /* Look for an equals sign */
>> +            char *equals = memchr(opt_start, '=', end - opt_start);
>> +            if (NULL == equals) {
>>                  break;
>>              }
>> -        } else if (parse_state == 1) {
>> -            if (*ra > '9' || *ra < '0') {
>> -                char *opt_start = ra - strlen(RA_OPTSTR) + 1;
>> -                if (opt_start > file &&
>> -                    strncmp(opt_start, RA_OPTSTR, strlen(RA_OPTSTR)) == 0) {
>> -                    ra_val = ra + 1;
>> -                    ra -= strlen(RA_OPTSTR) - 1;
>> -                    *ra = '\0';
>> -                    qdict_put(options, "readahead", qstring_from_str(ra_val));
>> -                }
>> +
>> +            size_t opt_len = equals - opt_start;
>> +            char *value = equals + 1;
>> +            size_t value_len = end - value;
>> +
>> +            if (opt_len == strlen(CURL_BLOCK_OPT_READAHEAD) &&
>> +                memcmp(opt_start, CURL_BLOCK_OPT_READAHEAD, opt_len) == 0) {
>> +                /* This is redundant after the first iteration */
>> +                *end = '\0';
>> +                qdict_put(options, CURL_BLOCK_OPT_READAHEAD,
>> +                          qstring_from_str(value));
>> +            } else {
>> +                /* Unknown option */
>>                  break;
> 
> So if we get an unknown option, we simply abort parsing the filename.
> This means that we'll try to open a URL that still contains an option
> and will probably fail with a rather confusing error message.
> 
> Shouldn't we set a clear error message about the unknown option here
> with error_setg() and immediately return?

TBH I was just trying to stay compatible with the behaviour which most
recently worked. Hence the weirdness with the trailing ':', for example.

I did consider whether to do ignore these options or not. I decided to
ignore them because the option syntax isn't safe: the string
':readahead=1k:' could be found at the end of a valid URL. Ignoring
unknown options reduces the chances of a false positive. For example, in:

http://example.com/path?query=foo:

How do you avoid throwing an error about the unknown option
'//example.com/path?query'? I could probably chuck in a couple of
heuristics to avoid the obvious false positives, but it would be even
messier. The best option is not to do this at all, and use the separated
options command line syntax. In fact, I might add a note to the docs
advising this.

Alternatively I could completely change the syntax. However, the only
'safe' syntax I can think of would involve ugly custom escaping, which
would probably catch out more people than unsafe option parsing. I'm
open to suggestions.

On a related note, do you know if it's possible to specify a backing
file with separated options? i.e.:

qemu-img create -f qcow2 -o
backingfile.url='http://example.com/path',backingfile.readahead='64k'
/tmp/foo.qcow2

I suspect that qcow2 only stores a string, so probably not, but I
thought I'd ask.

Matt
-- 
Matthew Booth
Red Hat Engineering, Virtualisation Team

Phone: +442070094448 (UK)
GPG ID:  D33C3490
GPG FPR: 3733 612D 2D05 5458 8A8A 1600 3441 EA19 D33C 3490

       reply	other threads:[~2014-05-01  8:56 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <1398867658-2069-1-git-send-email-mbooth@redhat.com>
     [not found] ` <1398867658-2069-2-git-send-email-mbooth@redhat.com>
     [not found]   ` <20140430151638.GE4380@noname.redhat.com>
2014-05-01  8:56     ` Matthew Booth [this message]
2014-05-01 12:04       ` [Qemu-devel] [PATCH 1/3] curl: Fix parsing of readahead option from filename Eric Blake
2014-05-05  9:27       ` Kevin Wolf
     [not found] ` <1398867658-2069-3-git-send-email-mbooth@redhat.com>
     [not found]   ` <20140430152017.GF4380@noname.redhat.com>
2014-05-01  8:59     ` [Qemu-devel] [PATCH 2/3] curl: Add sslverify option Matthew Booth

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=53620C41.3040506@redhat.com \
    --to=mbooth@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).