qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH 1/3] curl: Fix parsing of readahead option from


From: Kevin Wolf
Subject: Re: [Qemu-devel] [PATCH 1/3] curl: Fix parsing of readahead option from filename
Date: Mon, 5 May 2014 11:27:30 +0200
User-agent: Mutt/1.5.21 (2010-09-15)

Am 01.05.2014 um 10:56 hat Matthew Booth geschrieben:
> 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 <address@hidden>
> >> ---
> >>  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'?

You don't? :-)

It's the same thing for local file names. If you want a safe way for
specifying files that have things like colons in their name, you
shouldn't use the filename string, but specific options that are taken
literally (like file.filename=foo:bar.qcow2), otherwise you get an error
about unknown protocols.

And Eric made the very good point that colons should be percent-encoded
anyway in a URL, so I really wouldn't worry too much about this. There
are enough ways for the user to achieve what they want, we don't have
to provide everything in the shortcut string.

>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.

Let's not go there. We already have a safe syntax, and it's the
separated options.

> 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.

Like Eric said, the json: protocol is how we want to implement this.

If you're okay with specifying the option at runtime, you can already do
that with an option like this:

    -drive file=/tmp/foo.qcow2,backing.file.readahead=64k

Kevin



reply via email to

[Prev in Thread] Current Thread [Next in Thread]