[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