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

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
1.1.1.4   root       32: #define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0)
1.1       root       33: #else
1.1.1.4   root       34: #define DPRINTF(fmt, ...) do { } while (0)
1.1       root       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;
1.1.1.5 ! root       50:     QEMUBH *bh;
1.1       root       51:     QEMUIOVector *qiov;
1.1.1.5 ! root       52: 
        !            53:     int64_t sector_num;
        !            54:     int nb_sectors;
        !            55: 
1.1       root       56:     size_t start;
                     57:     size_t end;
                     58: } CURLAIOCB;
                     59: 
                     60: typedef struct CURLState
                     61: {
                     62:     struct BDRVCURLState *s;
                     63:     CURLAIOCB *acb[CURL_NUM_ACB];
                     64:     CURL *curl;
                     65:     char *orig_buf;
                     66:     size_t buf_start;
                     67:     size_t buf_off;
                     68:     size_t buf_len;
                     69:     char range[128];
                     70:     char errmsg[CURL_ERROR_SIZE];
                     71:     char in_use;
                     72: } CURLState;
                     73: 
                     74: typedef struct BDRVCURLState {
                     75:     CURLM *multi;
                     76:     size_t len;
                     77:     CURLState states[CURL_NUM_STATES];
                     78:     char *url;
                     79:     size_t readahead_size;
                     80: } BDRVCURLState;
                     81: 
                     82: static void curl_clean_state(CURLState *s);
                     83: static void curl_multi_do(void *arg);
1.1.1.5 ! root       84: static int curl_aio_flush(void *opaque);
1.1       root       85: 
                     86: static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
                     87:                         void *s, void *sp)
                     88: {
1.1.1.4   root       89:     DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd);
1.1       root       90:     switch (action) {
                     91:         case CURL_POLL_IN:
1.1.1.5 ! root       92:             qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, curl_aio_flush,
        !            93:                                     NULL, s);
1.1       root       94:             break;
                     95:         case CURL_POLL_OUT:
1.1.1.5 ! root       96:             qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, curl_aio_flush,
        !            97:                                     NULL, s);
1.1       root       98:             break;
                     99:         case CURL_POLL_INOUT:
1.1.1.5 ! root      100:             qemu_aio_set_fd_handler(fd, curl_multi_do, curl_multi_do,
        !           101:                                     curl_aio_flush, NULL, s);
1.1       root      102:             break;
                    103:         case CURL_POLL_REMOVE:
1.1.1.2   root      104:             qemu_aio_set_fd_handler(fd, NULL, NULL, NULL, NULL, NULL);
1.1       root      105:             break;
                    106:     }
                    107: 
                    108:     return 0;
                    109: }
                    110: 
                    111: static size_t curl_size_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
                    112: {
                    113:     CURLState *s = ((CURLState*)opaque);
                    114:     size_t realsize = size * nmemb;
1.1.1.4   root      115:     size_t fsize;
1.1       root      116: 
1.1.1.4   root      117:     if(sscanf(ptr, "Content-Length: %zd", &fsize) == 1) {
1.1       root      118:         s->s->len = fsize;
1.1.1.4   root      119:     }
1.1       root      120: 
                    121:     return realsize;
                    122: }
                    123: 
                    124: static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
                    125: {
                    126:     CURLState *s = ((CURLState*)opaque);
                    127:     size_t realsize = size * nmemb;
                    128:     int i;
                    129: 
1.1.1.4   root      130:     DPRINTF("CURL: Just reading %zd bytes\n", realsize);
1.1       root      131: 
                    132:     if (!s || !s->orig_buf)
                    133:         goto read_end;
                    134: 
                    135:     memcpy(s->orig_buf + s->buf_off, ptr, realsize);
                    136:     s->buf_off += realsize;
                    137: 
                    138:     for(i=0; i<CURL_NUM_ACB; i++) {
                    139:         CURLAIOCB *acb = s->acb[i];
                    140: 
                    141:         if (!acb)
                    142:             continue;
                    143: 
                    144:         if ((s->buf_off >= acb->end)) {
                    145:             qemu_iovec_from_buffer(acb->qiov, s->orig_buf + acb->start,
                    146:                                    acb->end - acb->start);
                    147:             acb->common.cb(acb->common.opaque, 0);
                    148:             qemu_aio_release(acb);
                    149:             s->acb[i] = NULL;
                    150:         }
                    151:     }
                    152: 
                    153: read_end:
                    154:     return realsize;
                    155: }
                    156: 
                    157: static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
                    158:                          CURLAIOCB *acb)
                    159: {
                    160:     int i;
                    161:     size_t end = start + len;
                    162: 
                    163:     for (i=0; i<CURL_NUM_STATES; i++) {
                    164:         CURLState *state = &s->states[i];
                    165:         size_t buf_end = (state->buf_start + state->buf_off);
                    166:         size_t buf_fend = (state->buf_start + state->buf_len);
                    167: 
                    168:         if (!state->orig_buf)
                    169:             continue;
                    170:         if (!state->buf_off)
                    171:             continue;
                    172: 
                    173:         // Does the existing buffer cover our section?
                    174:         if ((start >= state->buf_start) &&
                    175:             (start <= buf_end) &&
                    176:             (end >= state->buf_start) &&
                    177:             (end <= buf_end))
                    178:         {
                    179:             char *buf = state->orig_buf + (start - state->buf_start);
                    180: 
                    181:             qemu_iovec_from_buffer(acb->qiov, buf, len);
                    182:             acb->common.cb(acb->common.opaque, 0);
                    183: 
                    184:             return FIND_RET_OK;
                    185:         }
                    186: 
                    187:         // Wait for unfinished chunks
                    188:         if ((start >= state->buf_start) &&
                    189:             (start <= buf_fend) &&
                    190:             (end >= state->buf_start) &&
                    191:             (end <= buf_fend))
                    192:         {
                    193:             int j;
                    194: 
                    195:             acb->start = start - state->buf_start;
                    196:             acb->end = acb->start + len;
                    197: 
                    198:             for (j=0; j<CURL_NUM_ACB; j++) {
                    199:                 if (!state->acb[j]) {
                    200:                     state->acb[j] = acb;
                    201:                     return FIND_RET_WAIT;
                    202:                 }
                    203:             }
                    204:         }
                    205:     }
                    206: 
                    207:     return FIND_RET_NONE;
                    208: }
                    209: 
                    210: static void curl_multi_do(void *arg)
                    211: {
                    212:     BDRVCURLState *s = (BDRVCURLState *)arg;
                    213:     int running;
                    214:     int r;
                    215:     int msgs_in_queue;
                    216: 
                    217:     if (!s->multi)
                    218:         return;
                    219: 
                    220:     do {
                    221:         r = curl_multi_socket_all(s->multi, &running);
                    222:     } while(r == CURLM_CALL_MULTI_PERFORM);
                    223: 
                    224:     /* Try to find done transfers, so we can free the easy
                    225:      * handle again. */
                    226:     do {
                    227:         CURLMsg *msg;
                    228:         msg = curl_multi_info_read(s->multi, &msgs_in_queue);
                    229: 
                    230:         if (!msg)
                    231:             break;
                    232:         if (msg->msg == CURLMSG_NONE)
                    233:             break;
                    234: 
                    235:         switch (msg->msg) {
                    236:             case CURLMSG_DONE:
                    237:             {
                    238:                 CURLState *state = NULL;
                    239:                 curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char**)&state);
1.1.1.5 ! root      240: 
        !           241:                 /* ACBs for successful messages get completed in curl_read_cb */
        !           242:                 if (msg->data.result != CURLE_OK) {
        !           243:                     int i;
        !           244:                     for (i = 0; i < CURL_NUM_ACB; i++) {
        !           245:                         CURLAIOCB *acb = state->acb[i];
        !           246: 
        !           247:                         if (acb == NULL) {
        !           248:                             continue;
        !           249:                         }
        !           250: 
        !           251:                         acb->common.cb(acb->common.opaque, -EIO);
        !           252:                         qemu_aio_release(acb);
        !           253:                         state->acb[i] = NULL;
        !           254:                     }
        !           255:                 }
        !           256: 
1.1       root      257:                 curl_clean_state(state);
                    258:                 break;
                    259:             }
                    260:             default:
                    261:                 msgs_in_queue = 0;
                    262:                 break;
                    263:         }
                    264:     } while(msgs_in_queue);
                    265: }
                    266: 
                    267: static CURLState *curl_init_state(BDRVCURLState *s)
                    268: {
                    269:     CURLState *state = NULL;
                    270:     int i, j;
                    271: 
                    272:     do {
                    273:         for (i=0; i<CURL_NUM_STATES; i++) {
                    274:             for (j=0; j<CURL_NUM_ACB; j++)
                    275:                 if (s->states[i].acb[j])
                    276:                     continue;
                    277:             if (s->states[i].in_use)
                    278:                 continue;
                    279: 
                    280:             state = &s->states[i];
                    281:             state->in_use = 1;
                    282:             break;
                    283:         }
                    284:         if (!state) {
                    285:             usleep(100);
                    286:             curl_multi_do(s);
                    287:         }
                    288:     } while(!state);
                    289: 
                    290:     if (state->curl)
                    291:         goto has_curl;
                    292: 
                    293:     state->curl = curl_easy_init();
                    294:     if (!state->curl)
                    295:         return NULL;
                    296:     curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
                    297:     curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5);
                    298:     curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
                    299:     curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state);
                    300:     curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state);
                    301:     curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1);
                    302:     curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1);
                    303:     curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1);
                    304:     curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
1.1.1.5 ! root      305:     curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1);
        !           306: 
1.1       root      307: #ifdef DEBUG_VERBOSE
                    308:     curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1);
                    309: #endif
                    310: 
                    311: has_curl:
                    312: 
                    313:     state->s = s;
                    314: 
                    315:     return state;
                    316: }
                    317: 
                    318: static void curl_clean_state(CURLState *s)
                    319: {
                    320:     if (s->s->multi)
                    321:         curl_multi_remove_handle(s->s->multi, s->curl);
                    322:     s->in_use = 0;
                    323: }
                    324: 
                    325: static int curl_open(BlockDriverState *bs, const char *filename, int flags)
                    326: {
                    327:     BDRVCURLState *s = bs->opaque;
                    328:     CURLState *state = NULL;
                    329:     double d;
                    330: 
                    331:     #define RA_OPTSTR ":readahead="
                    332:     char *file;
                    333:     char *ra;
                    334:     const char *ra_val;
                    335:     int parse_state = 0;
                    336: 
                    337:     static int inited = 0;
                    338: 
1.1.1.5 ! root      339:     file = g_strdup(filename);
1.1       root      340:     s->readahead_size = READ_AHEAD_SIZE;
                    341: 
                    342:     /* Parse a trailing ":readahead=#:" param, if present. */
                    343:     ra = file + strlen(file) - 1;
                    344:     while (ra >= file) {
                    345:         if (parse_state == 0) {
                    346:             if (*ra == ':')
                    347:                 parse_state++;
                    348:             else
                    349:                 break;
                    350:         } else if (parse_state == 1) {
                    351:             if (*ra > '9' || *ra < '0') {
                    352:                 char *opt_start = ra - strlen(RA_OPTSTR) + 1;
                    353:                 if (opt_start > file &&
                    354:                     strncmp(opt_start, RA_OPTSTR, strlen(RA_OPTSTR)) == 0) {
                    355:                     ra_val = ra + 1;
                    356:                     ra -= strlen(RA_OPTSTR) - 1;
                    357:                     *ra = '\0';
                    358:                     s->readahead_size = atoi(ra_val);
                    359:                     break;
                    360:                 } else {
                    361:                     break;
                    362:                 }
                    363:             }
                    364:         }
                    365:         ra--;
                    366:     }
                    367: 
                    368:     if ((s->readahead_size & 0x1ff) != 0) {
1.1.1.4   root      369:         fprintf(stderr, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512\n",
1.1       root      370:                 s->readahead_size);
                    371:         goto out_noclean;
                    372:     }
                    373: 
                    374:     if (!inited) {
                    375:         curl_global_init(CURL_GLOBAL_ALL);
                    376:         inited = 1;
                    377:     }
                    378: 
1.1.1.4   root      379:     DPRINTF("CURL: Opening %s\n", file);
1.1       root      380:     s->url = file;
                    381:     state = curl_init_state(s);
                    382:     if (!state)
                    383:         goto out_noclean;
                    384: 
                    385:     // Get file size
                    386: 
                    387:     curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1);
                    388:     curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_size_cb);
                    389:     if (curl_easy_perform(state->curl))
                    390:         goto out;
                    391:     curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
                    392:     curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
                    393:     curl_easy_setopt(state->curl, CURLOPT_NOBODY, 0);
                    394:     if (d)
                    395:         s->len = (size_t)d;
                    396:     else if(!s->len)
                    397:         goto out;
1.1.1.4   root      398:     DPRINTF("CURL: Size = %zd\n", s->len);
1.1       root      399: 
                    400:     curl_clean_state(state);
                    401:     curl_easy_cleanup(state->curl);
                    402:     state->curl = NULL;
                    403: 
                    404:     // Now we know the file exists and its size, so let's
                    405:     // initialize the multi interface!
                    406: 
                    407:     s->multi = curl_multi_init();
                    408:     curl_multi_setopt( s->multi, CURLMOPT_SOCKETDATA, s); 
                    409:     curl_multi_setopt( s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb ); 
                    410:     curl_multi_do(s);
                    411: 
                    412:     return 0;
                    413: 
                    414: out:
                    415:     fprintf(stderr, "CURL: Error opening file: %s\n", state->errmsg);
                    416:     curl_easy_cleanup(state->curl);
                    417:     state->curl = NULL;
                    418: out_noclean:
1.1.1.5 ! root      419:     g_free(file);
1.1       root      420:     return -EINVAL;
                    421: }
                    422: 
1.1.1.5 ! root      423: static int curl_aio_flush(void *opaque)
        !           424: {
        !           425:     BDRVCURLState *s = opaque;
        !           426:     int i, j;
        !           427: 
        !           428:     for (i=0; i < CURL_NUM_STATES; i++) {
        !           429:         for(j=0; j < CURL_NUM_ACB; j++) {
        !           430:             if (s->states[i].acb[j]) {
        !           431:                 return 1;
        !           432:             }
        !           433:         }
        !           434:     }
        !           435:     return 0;
        !           436: }
        !           437: 
1.1       root      438: static void curl_aio_cancel(BlockDriverAIOCB *blockacb)
                    439: {
                    440:     // Do we have to implement canceling? Seems to work without...
                    441: }
                    442: 
                    443: static AIOPool curl_aio_pool = {
                    444:     .aiocb_size         = sizeof(CURLAIOCB),
                    445:     .cancel             = curl_aio_cancel,
                    446: };
                    447: 
1.1.1.5 ! root      448: 
        !           449: static void curl_readv_bh_cb(void *p)
1.1       root      450: {
                    451:     CURLState *state;
                    452: 
1.1.1.5 ! root      453:     CURLAIOCB *acb = p;
        !           454:     BDRVCURLState *s = acb->common.bs->opaque;
1.1       root      455: 
1.1.1.5 ! root      456:     qemu_bh_delete(acb->bh);
        !           457:     acb->bh = NULL;
        !           458: 
        !           459:     size_t start = acb->sector_num * SECTOR_SIZE;
        !           460:     size_t end;
1.1       root      461: 
                    462:     // In case we have the requested data already (e.g. read-ahead),
                    463:     // we can just call the callback and be done.
1.1.1.5 ! root      464:     switch (curl_find_buf(s, start, acb->nb_sectors * SECTOR_SIZE, acb)) {
1.1       root      465:         case FIND_RET_OK:
                    466:             qemu_aio_release(acb);
                    467:             // fall through
                    468:         case FIND_RET_WAIT:
1.1.1.5 ! root      469:             return;
1.1       root      470:         default:
                    471:             break;
                    472:     }
                    473: 
                    474:     // No cache found, so let's start a new request
                    475:     state = curl_init_state(s);
1.1.1.5 ! root      476:     if (!state) {
        !           477:         acb->common.cb(acb->common.opaque, -EIO);
        !           478:         qemu_aio_release(acb);
        !           479:         return;
        !           480:     }
1.1       root      481: 
                    482:     acb->start = 0;
1.1.1.5 ! root      483:     acb->end = (acb->nb_sectors * SECTOR_SIZE);
1.1       root      484: 
                    485:     state->buf_off = 0;
                    486:     if (state->orig_buf)
1.1.1.5 ! root      487:         g_free(state->orig_buf);
1.1       root      488:     state->buf_start = start;
                    489:     state->buf_len = acb->end + s->readahead_size;
                    490:     end = MIN(start + state->buf_len, s->len) - 1;
1.1.1.5 ! root      491:     state->orig_buf = g_malloc(state->buf_len);
1.1       root      492:     state->acb[0] = acb;
                    493: 
1.1.1.4   root      494:     snprintf(state->range, 127, "%zd-%zd", start, end);
                    495:     DPRINTF("CURL (AIO): Reading %d at %zd (%s)\n",
1.1.1.5 ! root      496:             (acb->nb_sectors * SECTOR_SIZE), start, state->range);
1.1       root      497:     curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range);
                    498: 
                    499:     curl_multi_add_handle(s->multi, state->curl);
                    500:     curl_multi_do(s);
                    501: 
1.1.1.5 ! root      502: }
        !           503: 
        !           504: static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs,
        !           505:         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
        !           506:         BlockDriverCompletionFunc *cb, void *opaque)
        !           507: {
        !           508:     CURLAIOCB *acb;
        !           509: 
        !           510:     acb = qemu_aio_get(&curl_aio_pool, bs, cb, opaque);
        !           511: 
        !           512:     if (!acb) {
        !           513:         return NULL;
        !           514:     }
        !           515: 
        !           516:     acb->qiov = qiov;
        !           517:     acb->sector_num = sector_num;
        !           518:     acb->nb_sectors = nb_sectors;
        !           519: 
        !           520:     acb->bh = qemu_bh_new(curl_readv_bh_cb, acb);
        !           521: 
        !           522:     if (!acb->bh) {
        !           523:         DPRINTF("CURL: qemu_bh_new failed\n");
        !           524:         return NULL;
        !           525:     }
        !           526: 
        !           527:     qemu_bh_schedule(acb->bh);
1.1       root      528:     return &acb->common;
                    529: }
                    530: 
                    531: static void curl_close(BlockDriverState *bs)
                    532: {
                    533:     BDRVCURLState *s = bs->opaque;
                    534:     int i;
                    535: 
1.1.1.4   root      536:     DPRINTF("CURL: Close\n");
1.1       root      537:     for (i=0; i<CURL_NUM_STATES; i++) {
                    538:         if (s->states[i].in_use)
                    539:             curl_clean_state(&s->states[i]);
                    540:         if (s->states[i].curl) {
                    541:             curl_easy_cleanup(s->states[i].curl);
                    542:             s->states[i].curl = NULL;
                    543:         }
                    544:         if (s->states[i].orig_buf) {
1.1.1.5 ! root      545:             g_free(s->states[i].orig_buf);
1.1       root      546:             s->states[i].orig_buf = NULL;
                    547:         }
                    548:     }
                    549:     if (s->multi)
                    550:         curl_multi_cleanup(s->multi);
                    551:     if (s->url)
                    552:         free(s->url);
                    553: }
                    554: 
                    555: static int64_t curl_getlength(BlockDriverState *bs)
                    556: {
                    557:     BDRVCURLState *s = bs->opaque;
                    558:     return s->len;
                    559: }
                    560: 
                    561: static BlockDriver bdrv_http = {
                    562:     .format_name     = "http",
                    563:     .protocol_name   = "http",
                    564: 
                    565:     .instance_size   = sizeof(BDRVCURLState),
1.1.1.4   root      566:     .bdrv_file_open  = curl_open,
1.1       root      567:     .bdrv_close      = curl_close,
                    568:     .bdrv_getlength  = curl_getlength,
                    569: 
                    570:     .bdrv_aio_readv  = curl_aio_readv,
                    571: };
                    572: 
                    573: static BlockDriver bdrv_https = {
                    574:     .format_name     = "https",
                    575:     .protocol_name   = "https",
                    576: 
                    577:     .instance_size   = sizeof(BDRVCURLState),
1.1.1.4   root      578:     .bdrv_file_open  = curl_open,
1.1       root      579:     .bdrv_close      = curl_close,
                    580:     .bdrv_getlength  = curl_getlength,
                    581: 
                    582:     .bdrv_aio_readv  = curl_aio_readv,
                    583: };
                    584: 
                    585: static BlockDriver bdrv_ftp = {
                    586:     .format_name     = "ftp",
                    587:     .protocol_name   = "ftp",
                    588: 
                    589:     .instance_size   = sizeof(BDRVCURLState),
1.1.1.4   root      590:     .bdrv_file_open  = curl_open,
1.1       root      591:     .bdrv_close      = curl_close,
                    592:     .bdrv_getlength  = curl_getlength,
                    593: 
                    594:     .bdrv_aio_readv  = curl_aio_readv,
                    595: };
                    596: 
                    597: static BlockDriver bdrv_ftps = {
                    598:     .format_name     = "ftps",
                    599:     .protocol_name   = "ftps",
                    600: 
                    601:     .instance_size   = sizeof(BDRVCURLState),
1.1.1.4   root      602:     .bdrv_file_open  = curl_open,
1.1       root      603:     .bdrv_close      = curl_close,
                    604:     .bdrv_getlength  = curl_getlength,
                    605: 
                    606:     .bdrv_aio_readv  = curl_aio_readv,
                    607: };
                    608: 
                    609: static BlockDriver bdrv_tftp = {
                    610:     .format_name     = "tftp",
                    611:     .protocol_name   = "tftp",
                    612: 
                    613:     .instance_size   = sizeof(BDRVCURLState),
1.1.1.4   root      614:     .bdrv_file_open  = curl_open,
1.1       root      615:     .bdrv_close      = curl_close,
                    616:     .bdrv_getlength  = curl_getlength,
                    617: 
                    618:     .bdrv_aio_readv  = curl_aio_readv,
                    619: };
                    620: 
                    621: static void curl_block_init(void)
                    622: {
                    623:     bdrv_register(&bdrv_http);
                    624:     bdrv_register(&bdrv_https);
                    625:     bdrv_register(&bdrv_ftp);
                    626:     bdrv_register(&bdrv_ftps);
                    627:     bdrv_register(&bdrv_tftp);
                    628: }
                    629: 
                    630: 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.