Annotation of qemu/block/curl.c, revision 1.1.1.3

1.1       root        1: /*
                      2:  * QEMU Block driver for CURL images
                      3:  *
                      4:  * Copyright (c) 2009 Alexander Graf <[email protected]>
                      5:  *
                      6:  * Permission is hereby granted, free of charge, to any person obtaining a copy
                      7:  * of this software and associated documentation files (the "Software"), to deal
                      8:  * in the Software without restriction, including without limitation the rights
                      9:  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                     10:  * copies of the Software, and to permit persons to whom the Software is
                     11:  * furnished to do so, subject to the following conditions:
                     12:  *
                     13:  * The above copyright notice and this permission notice shall be included in
                     14:  * all copies or substantial portions of the Software.
                     15:  *
                     16:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                     17:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                     18:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
                     19:  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                     20:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                     21:  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
                     22:  * THE SOFTWARE.
                     23:  */
                     24: #include "qemu-common.h"
                     25: #include "block_int.h"
                     26: #include <curl/curl.h>
                     27: 
                     28: // #define DEBUG
                     29: // #define DEBUG_VERBOSE
                     30: 
                     31: #ifdef DEBUG_CURL
                     32: #define dprintf(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0)
                     33: #else
                     34: #define dprintf(fmt, ...) do { } while (0)
                     35: #endif
                     36: 
                     37: #define CURL_NUM_STATES 8
                     38: #define CURL_NUM_ACB    8
                     39: #define SECTOR_SIZE     512
                     40: #define READ_AHEAD_SIZE (256 * 1024)
                     41: 
                     42: #define FIND_RET_NONE   0
                     43: #define FIND_RET_OK     1
                     44: #define FIND_RET_WAIT   2
                     45: 
                     46: struct BDRVCURLState;
                     47: 
                     48: typedef struct CURLAIOCB {
                     49:     BlockDriverAIOCB common;
                     50:     QEMUIOVector *qiov;
                     51:     size_t start;
                     52:     size_t end;
                     53: } CURLAIOCB;
                     54: 
                     55: typedef struct CURLState
                     56: {
                     57:     struct BDRVCURLState *s;
                     58:     CURLAIOCB *acb[CURL_NUM_ACB];
                     59:     CURL *curl;
                     60:     char *orig_buf;
                     61:     size_t buf_start;
                     62:     size_t buf_off;
                     63:     size_t buf_len;
                     64:     char range[128];
                     65:     char errmsg[CURL_ERROR_SIZE];
                     66:     char in_use;
                     67: } CURLState;
                     68: 
                     69: typedef struct BDRVCURLState {
                     70:     CURLM *multi;
                     71:     size_t len;
                     72:     CURLState states[CURL_NUM_STATES];
                     73:     char *url;
                     74:     size_t readahead_size;
                     75: } BDRVCURLState;
                     76: 
                     77: static void curl_clean_state(CURLState *s);
                     78: static void curl_multi_do(void *arg);
                     79: 
                     80: static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
                     81:                         void *s, void *sp)
                     82: {
                     83:     dprintf("CURL (AIO): Sock action %d on fd %d\n", action, fd);
                     84:     switch (action) {
                     85:         case CURL_POLL_IN:
1.1.1.2   root       86:             qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, NULL, NULL, s);
1.1       root       87:             break;
                     88:         case CURL_POLL_OUT:
1.1.1.2   root       89:             qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, NULL, NULL, s);
1.1       root       90:             break;
                     91:         case CURL_POLL_INOUT:
                     92:             qemu_aio_set_fd_handler(fd, curl_multi_do,
1.1.1.2   root       93:                                     curl_multi_do, NULL, NULL, s);
1.1       root       94:             break;
                     95:         case CURL_POLL_REMOVE:
1.1.1.2   root       96:             qemu_aio_set_fd_handler(fd, NULL, NULL, NULL, NULL, NULL);
1.1       root       97:             break;
                     98:     }
                     99: 
                    100:     return 0;
                    101: }
                    102: 
                    103: static size_t curl_size_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
                    104: {
                    105:     CURLState *s = ((CURLState*)opaque);
                    106:     size_t realsize = size * nmemb;
                    107:     long long fsize;
                    108: 
                    109:     if(sscanf(ptr, "Content-Length: %lld", &fsize) == 1)
                    110:         s->s->len = fsize;
                    111: 
                    112:     return realsize;
                    113: }
                    114: 
                    115: static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
                    116: {
                    117:     CURLState *s = ((CURLState*)opaque);
                    118:     size_t realsize = size * nmemb;
                    119:     int i;
                    120: 
                    121:     dprintf("CURL: Just reading %lld bytes\n", (unsigned long long)realsize);
                    122: 
                    123:     if (!s || !s->orig_buf)
                    124:         goto read_end;
                    125: 
                    126:     memcpy(s->orig_buf + s->buf_off, ptr, realsize);
                    127:     s->buf_off += realsize;
                    128: 
                    129:     for(i=0; i<CURL_NUM_ACB; i++) {
                    130:         CURLAIOCB *acb = s->acb[i];
                    131: 
                    132:         if (!acb)
                    133:             continue;
                    134: 
                    135:         if ((s->buf_off >= acb->end)) {
                    136:             qemu_iovec_from_buffer(acb->qiov, s->orig_buf + acb->start,
                    137:                                    acb->end - acb->start);
                    138:             acb->common.cb(acb->common.opaque, 0);
                    139:             qemu_aio_release(acb);
                    140:             s->acb[i] = NULL;
                    141:         }
                    142:     }
                    143: 
                    144: read_end:
                    145:     return realsize;
                    146: }
                    147: 
                    148: static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
                    149:                          CURLAIOCB *acb)
                    150: {
                    151:     int i;
                    152:     size_t end = start + len;
                    153: 
                    154:     for (i=0; i<CURL_NUM_STATES; i++) {
                    155:         CURLState *state = &s->states[i];
                    156:         size_t buf_end = (state->buf_start + state->buf_off);
                    157:         size_t buf_fend = (state->buf_start + state->buf_len);
                    158: 
                    159:         if (!state->orig_buf)
                    160:             continue;
                    161:         if (!state->buf_off)
                    162:             continue;
                    163: 
                    164:         // Does the existing buffer cover our section?
                    165:         if ((start >= state->buf_start) &&
                    166:             (start <= buf_end) &&
                    167:             (end >= state->buf_start) &&
                    168:             (end <= buf_end))
                    169:         {
                    170:             char *buf = state->orig_buf + (start - state->buf_start);
                    171: 
                    172:             qemu_iovec_from_buffer(acb->qiov, buf, len);
                    173:             acb->common.cb(acb->common.opaque, 0);
                    174: 
                    175:             return FIND_RET_OK;
                    176:         }
                    177: 
                    178:         // Wait for unfinished chunks
                    179:         if ((start >= state->buf_start) &&
                    180:             (start <= buf_fend) &&
                    181:             (end >= state->buf_start) &&
                    182:             (end <= buf_fend))
                    183:         {
                    184:             int j;
                    185: 
                    186:             acb->start = start - state->buf_start;
                    187:             acb->end = acb->start + len;
                    188: 
                    189:             for (j=0; j<CURL_NUM_ACB; j++) {
                    190:                 if (!state->acb[j]) {
                    191:                     state->acb[j] = acb;
                    192:                     return FIND_RET_WAIT;
                    193:                 }
                    194:             }
                    195:         }
                    196:     }
                    197: 
                    198:     return FIND_RET_NONE;
                    199: }
                    200: 
                    201: static void curl_multi_do(void *arg)
                    202: {
                    203:     BDRVCURLState *s = (BDRVCURLState *)arg;
                    204:     int running;
                    205:     int r;
                    206:     int msgs_in_queue;
                    207: 
                    208:     if (!s->multi)
                    209:         return;
                    210: 
                    211:     do {
                    212:         r = curl_multi_socket_all(s->multi, &running);
                    213:     } while(r == CURLM_CALL_MULTI_PERFORM);
                    214: 
                    215:     /* Try to find done transfers, so we can free the easy
                    216:      * handle again. */
                    217:     do {
                    218:         CURLMsg *msg;
                    219:         msg = curl_multi_info_read(s->multi, &msgs_in_queue);
                    220: 
                    221:         if (!msg)
                    222:             break;
                    223:         if (msg->msg == CURLMSG_NONE)
                    224:             break;
                    225: 
                    226:         switch (msg->msg) {
                    227:             case CURLMSG_DONE:
                    228:             {
                    229:                 CURLState *state = NULL;
                    230:                 curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char**)&state);
                    231:                 curl_clean_state(state);
                    232:                 break;
                    233:             }
                    234:             default:
                    235:                 msgs_in_queue = 0;
                    236:                 break;
                    237:         }
                    238:     } while(msgs_in_queue);
                    239: }
                    240: 
                    241: static CURLState *curl_init_state(BDRVCURLState *s)
                    242: {
                    243:     CURLState *state = NULL;
                    244:     int i, j;
                    245: 
                    246:     do {
                    247:         for (i=0; i<CURL_NUM_STATES; i++) {
                    248:             for (j=0; j<CURL_NUM_ACB; j++)
                    249:                 if (s->states[i].acb[j])
                    250:                     continue;
                    251:             if (s->states[i].in_use)
                    252:                 continue;
                    253: 
                    254:             state = &s->states[i];
                    255:             state->in_use = 1;
                    256:             break;
                    257:         }
                    258:         if (!state) {
                    259:             usleep(100);
                    260:             curl_multi_do(s);
                    261:         }
                    262:     } while(!state);
                    263: 
                    264:     if (state->curl)
                    265:         goto has_curl;
                    266: 
                    267:     state->curl = curl_easy_init();
                    268:     if (!state->curl)
                    269:         return NULL;
                    270:     curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
                    271:     curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5);
                    272:     curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
                    273:     curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state);
                    274:     curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state);
                    275:     curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1);
                    276:     curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1);
                    277:     curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1);
                    278:     curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
                    279:     
                    280: #ifdef DEBUG_VERBOSE
                    281:     curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1);
                    282: #endif
                    283: 
                    284: has_curl:
                    285: 
                    286:     state->s = s;
                    287: 
                    288:     return state;
                    289: }
                    290: 
                    291: static void curl_clean_state(CURLState *s)
                    292: {
                    293:     if (s->s->multi)
                    294:         curl_multi_remove_handle(s->s->multi, s->curl);
                    295:     s->in_use = 0;
                    296: }
                    297: 
                    298: static int curl_open(BlockDriverState *bs, const char *filename, int flags)
                    299: {
                    300:     BDRVCURLState *s = bs->opaque;
                    301:     CURLState *state = NULL;
                    302:     double d;
                    303: 
                    304:     #define RA_OPTSTR ":readahead="
                    305:     char *file;
                    306:     char *ra;
                    307:     const char *ra_val;
                    308:     int parse_state = 0;
                    309: 
                    310:     static int inited = 0;
                    311: 
1.1.1.3 ! root      312:     file = qemu_strdup(filename);
1.1       root      313:     s->readahead_size = READ_AHEAD_SIZE;
                    314: 
                    315:     /* Parse a trailing ":readahead=#:" param, if present. */
                    316:     ra = file + strlen(file) - 1;
                    317:     while (ra >= file) {
                    318:         if (parse_state == 0) {
                    319:             if (*ra == ':')
                    320:                 parse_state++;
                    321:             else
                    322:                 break;
                    323:         } else if (parse_state == 1) {
                    324:             if (*ra > '9' || *ra < '0') {
                    325:                 char *opt_start = ra - strlen(RA_OPTSTR) + 1;
                    326:                 if (opt_start > file &&
                    327:                     strncmp(opt_start, RA_OPTSTR, strlen(RA_OPTSTR)) == 0) {
                    328:                     ra_val = ra + 1;
                    329:                     ra -= strlen(RA_OPTSTR) - 1;
                    330:                     *ra = '\0';
                    331:                     s->readahead_size = atoi(ra_val);
                    332:                     break;
                    333:                 } else {
                    334:                     break;
                    335:                 }
                    336:             }
                    337:         }
                    338:         ra--;
                    339:     }
                    340: 
                    341:     if ((s->readahead_size & 0x1ff) != 0) {
                    342:         fprintf(stderr, "HTTP_READAHEAD_SIZE %Zd is not a multiple of 512\n",
                    343:                 s->readahead_size);
                    344:         goto out_noclean;
                    345:     }
                    346: 
                    347:     if (!inited) {
                    348:         curl_global_init(CURL_GLOBAL_ALL);
                    349:         inited = 1;
                    350:     }
                    351: 
                    352:     dprintf("CURL: Opening %s\n", file);
                    353:     s->url = file;
                    354:     state = curl_init_state(s);
                    355:     if (!state)
                    356:         goto out_noclean;
                    357: 
                    358:     // Get file size
                    359: 
                    360:     curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1);
                    361:     curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_size_cb);
                    362:     if (curl_easy_perform(state->curl))
                    363:         goto out;
                    364:     curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
                    365:     curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
                    366:     curl_easy_setopt(state->curl, CURLOPT_NOBODY, 0);
                    367:     if (d)
                    368:         s->len = (size_t)d;
                    369:     else if(!s->len)
                    370:         goto out;
                    371:     dprintf("CURL: Size = %lld\n", (long long)s->len);
                    372: 
                    373:     curl_clean_state(state);
                    374:     curl_easy_cleanup(state->curl);
                    375:     state->curl = NULL;
                    376: 
                    377:     // Now we know the file exists and its size, so let's
                    378:     // initialize the multi interface!
                    379: 
                    380:     s->multi = curl_multi_init();
                    381:     curl_multi_setopt( s->multi, CURLMOPT_SOCKETDATA, s); 
                    382:     curl_multi_setopt( s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb ); 
                    383:     curl_multi_do(s);
                    384: 
                    385:     return 0;
                    386: 
                    387: out:
                    388:     fprintf(stderr, "CURL: Error opening file: %s\n", state->errmsg);
                    389:     curl_easy_cleanup(state->curl);
                    390:     state->curl = NULL;
                    391: out_noclean:
                    392:     qemu_free(file);
                    393:     return -EINVAL;
                    394: }
                    395: 
                    396: static void curl_aio_cancel(BlockDriverAIOCB *blockacb)
                    397: {
                    398:     // Do we have to implement canceling? Seems to work without...
                    399: }
                    400: 
                    401: static AIOPool curl_aio_pool = {
                    402:     .aiocb_size         = sizeof(CURLAIOCB),
                    403:     .cancel             = curl_aio_cancel,
                    404: };
                    405: 
                    406: static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs,
                    407:         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
                    408:         BlockDriverCompletionFunc *cb, void *opaque)
                    409: {
                    410:     BDRVCURLState *s = bs->opaque;
                    411:     CURLAIOCB *acb;
                    412:     size_t start = sector_num * SECTOR_SIZE;
                    413:     size_t end;
                    414:     CURLState *state;
                    415: 
                    416:     acb = qemu_aio_get(&curl_aio_pool, bs, cb, opaque);
                    417:     if (!acb)
                    418:         return NULL;
                    419: 
                    420:     acb->qiov = qiov;
                    421: 
                    422:     // In case we have the requested data already (e.g. read-ahead),
                    423:     // we can just call the callback and be done.
                    424: 
                    425:     switch (curl_find_buf(s, start, nb_sectors * SECTOR_SIZE, acb)) {
                    426:         case FIND_RET_OK:
                    427:             qemu_aio_release(acb);
                    428:             // fall through
                    429:         case FIND_RET_WAIT:
                    430:             return &acb->common;
                    431:         default:
                    432:             break;
                    433:     }
                    434: 
                    435:     // No cache found, so let's start a new request
                    436: 
                    437:     state = curl_init_state(s);
                    438:     if (!state)
                    439:         return NULL;
                    440: 
                    441:     acb->start = 0;
                    442:     acb->end = (nb_sectors * SECTOR_SIZE);
                    443: 
                    444:     state->buf_off = 0;
                    445:     if (state->orig_buf)
                    446:         qemu_free(state->orig_buf);
                    447:     state->buf_start = start;
                    448:     state->buf_len = acb->end + s->readahead_size;
                    449:     end = MIN(start + state->buf_len, s->len) - 1;
                    450:     state->orig_buf = qemu_malloc(state->buf_len);
                    451:     state->acb[0] = acb;
                    452: 
                    453:     snprintf(state->range, 127, "%lld-%lld", (long long)start, (long long)end);
                    454:     dprintf("CURL (AIO): Reading %d at %lld (%s)\n", (nb_sectors * SECTOR_SIZE), start, state->range);
                    455:     curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range);
                    456: 
                    457:     curl_multi_add_handle(s->multi, state->curl);
                    458:     curl_multi_do(s);
                    459: 
                    460:     return &acb->common;
                    461: }
                    462: 
                    463: static void curl_close(BlockDriverState *bs)
                    464: {
                    465:     BDRVCURLState *s = bs->opaque;
                    466:     int i;
                    467: 
                    468:     dprintf("CURL: Close\n");
                    469:     for (i=0; i<CURL_NUM_STATES; i++) {
                    470:         if (s->states[i].in_use)
                    471:             curl_clean_state(&s->states[i]);
                    472:         if (s->states[i].curl) {
                    473:             curl_easy_cleanup(s->states[i].curl);
                    474:             s->states[i].curl = NULL;
                    475:         }
                    476:         if (s->states[i].orig_buf) {
                    477:             qemu_free(s->states[i].orig_buf);
                    478:             s->states[i].orig_buf = NULL;
                    479:         }
                    480:     }
                    481:     if (s->multi)
                    482:         curl_multi_cleanup(s->multi);
                    483:     if (s->url)
                    484:         free(s->url);
                    485: }
                    486: 
                    487: static int64_t curl_getlength(BlockDriverState *bs)
                    488: {
                    489:     BDRVCURLState *s = bs->opaque;
                    490:     return s->len;
                    491: }
                    492: 
                    493: static BlockDriver bdrv_http = {
                    494:     .format_name     = "http",
                    495:     .protocol_name   = "http",
                    496: 
                    497:     .instance_size   = sizeof(BDRVCURLState),
                    498:     .bdrv_open       = curl_open,
                    499:     .bdrv_close      = curl_close,
                    500:     .bdrv_getlength  = curl_getlength,
                    501: 
                    502:     .bdrv_aio_readv  = curl_aio_readv,
                    503: };
                    504: 
                    505: static BlockDriver bdrv_https = {
                    506:     .format_name     = "https",
                    507:     .protocol_name   = "https",
                    508: 
                    509:     .instance_size   = sizeof(BDRVCURLState),
                    510:     .bdrv_open       = curl_open,
                    511:     .bdrv_close      = curl_close,
                    512:     .bdrv_getlength  = curl_getlength,
                    513: 
                    514:     .bdrv_aio_readv  = curl_aio_readv,
                    515: };
                    516: 
                    517: static BlockDriver bdrv_ftp = {
                    518:     .format_name     = "ftp",
                    519:     .protocol_name   = "ftp",
                    520: 
                    521:     .instance_size   = sizeof(BDRVCURLState),
                    522:     .bdrv_open       = curl_open,
                    523:     .bdrv_close      = curl_close,
                    524:     .bdrv_getlength  = curl_getlength,
                    525: 
                    526:     .bdrv_aio_readv  = curl_aio_readv,
                    527: };
                    528: 
                    529: static BlockDriver bdrv_ftps = {
                    530:     .format_name     = "ftps",
                    531:     .protocol_name   = "ftps",
                    532: 
                    533:     .instance_size   = sizeof(BDRVCURLState),
                    534:     .bdrv_open       = curl_open,
                    535:     .bdrv_close      = curl_close,
                    536:     .bdrv_getlength  = curl_getlength,
                    537: 
                    538:     .bdrv_aio_readv  = curl_aio_readv,
                    539: };
                    540: 
                    541: static BlockDriver bdrv_tftp = {
                    542:     .format_name     = "tftp",
                    543:     .protocol_name   = "tftp",
                    544: 
                    545:     .instance_size   = sizeof(BDRVCURLState),
                    546:     .bdrv_open       = curl_open,
                    547:     .bdrv_close      = curl_close,
                    548:     .bdrv_getlength  = curl_getlength,
                    549: 
                    550:     .bdrv_aio_readv  = curl_aio_readv,
                    551: };
                    552: 
                    553: static void curl_block_init(void)
                    554: {
                    555:     bdrv_register(&bdrv_http);
                    556:     bdrv_register(&bdrv_https);
                    557:     bdrv_register(&bdrv_ftp);
                    558:     bdrv_register(&bdrv_ftps);
                    559:     bdrv_register(&bdrv_tftp);
                    560: }
                    561: 
                    562: block_init(curl_block_init);

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.