diff options
author | Simon Rettberg | 2024-04-22 16:34:39 +0200 |
---|---|---|
committer | Simon Rettberg | 2024-05-13 18:26:42 +0200 |
commit | 98604003ec9b3fdfdaf74cbd708b6584ad84b950 (patch) | |
tree | 21c8b47708529630c6085257b32ad96b0d09ab61 | |
parent | [FUSE] Use CURLOPT_HEADERFUNCTION instead of curl_easy_header (diff) | |
download | dnbd3-98604003ec9b3fdfdaf74cbd708b6584ad84b950.tar.gz dnbd3-98604003ec9b3fdfdaf74cbd708b6584ad84b950.tar.xz dnbd3-98604003ec9b3fdfdaf74cbd708b6584ad84b950.zip |
[FUSE] Rename version -> revision in /create
Also turn into normal POST with urlencoded payload.
-rw-r--r-- | src/fuse/cowDoc/readme.md | 17 | ||||
-rw-r--r-- | src/fuse/cowfile.c | 96 |
2 files changed, 59 insertions, 54 deletions
diff --git a/src/fuse/cowDoc/readme.md b/src/fuse/cowDoc/readme.md index a289209..2b752bb 100644 --- a/src/fuse/cowDoc/readme.md +++ b/src/fuse/cowDoc/readme.md @@ -238,8 +238,8 @@ This can help in fine-tuning `COW_MIN_UPLOAD_DELAY`. - `COW_MAX_PARALLEL_UPLOADS` defines the maximum number of parallel cluster uploads. This number is used once the image has been unmounted to upload the remaining modified clusters. - # REST API + The following Rest API is used to transmit the data and commands to the cow server: ## Required methods @@ -247,11 +247,20 @@ The following Rest API is used to transmit the data and commands to the cow serv ### v1/file/create #### POST +##### Parameters + +| Name | Located in | Description | Required | Schema | +| ---- | ---------- | ----------- | -------- | ---- | +| imageName | post | Name of image | Yes | relative path | +| revision | post | revision id of image | Yes | integer | +| bitfieldSize | post | number of bits per L2 cluster | Yes | integer | + ##### Responses | Code | Description | | ---- | ----------- | | 200 | Success | +| 404 | Source image not found | This request is used as soon as a new cow session is created. The returned uuid is used in all subsequent requests to identify the session. @@ -264,15 +273,16 @@ This request is used as soon as a new cow session is created. The returned uuid | Name | Located in | Description | Required | Schema | | ---- | ---------- | ----------- | -------- | ---- | | uuid | query | | Yes | string (uuid) | -| clusterNumber | query | | Yes | integer | +| clusterindex | query | | Yes | integer | ##### Responses | Code | Description | | ---- | ----------- | | 200 | Success | +| 503 | Server can't keep up, if Retry-After header is present, it can request a backoff interval, specified in seconds. | -Used to upload a cluster. The cluster number is the absolute cluster number. The body contains an "application/octet-stream", where the first bytes are the bit field, directly followed by the actual cluster data. +Used to upload a cluster. The cluster number is the absolute cluster number. The body contains an "application/octet-stream", where the first bytes are the bit field, directly followed by the actual cluster data. The cluster data is sparse, i.e. only blocks for which the bit is set are present, all other blocks are skipped. ### v1/file/merge @@ -285,6 +295,7 @@ Used to upload a cluster. The cluster number is the absolute cluster number. The | uuid | Form | | Yes | string (uuid) | | originalFileSize | Form | | Yes | integer | | newFileSize | Form | | Yes | integer | + ##### Responses | Code | Description | diff --git a/src/fuse/cowfile.c b/src/fuse/cowfile.c index f19150a..67f3eab 100644 --- a/src/fuse/cowfile.c +++ b/src/fuse/cowfile.c @@ -171,85 +171,76 @@ static bool checkBit( atomic_uchar *bitfield, int64_t n ) /** - * @brief Implementation of CURLOPT_WRITEFUNCTION , this function will be called when - * the server sends back data. - * for more details see: https://curl.se/libcurl/c/CURLOPT_WRITEFUNCTION .html - * - * @param buffer that contains the response data from the server - * @param itemSize size of one item - * @param nitems number of items - * @param response userdata which will later contain the uuid - * @return size_t size that have been read + * Generic callback for writing received data to a 500 byte buffer. + * MAKE SURE THE BUFFER IS EMPTY AT THE START! (i.e. buffer[0] = '\0') */ -static size_t curlCallbackCreateSession( char *buffer, size_t itemSize, size_t nitems, void *response ) +static size_t curlWriteCb500( char *buffer, size_t itemSize, size_t nitems, void *userpointer ) { - uint64_t done = strlen( response ); - uint64_t bytes = itemSize * nitems; - if ( done + bytes > UUID_STRLEN ) { - logadd( LOG_INFO, "strlen(response): %"PRIu64" bytes: %"PRIu64"\n", done, bytes ); - return bytes; - } + char *dest = (char*)userpointer; + size_t done = strlen( dest ); + size_t bytes = itemSize * nitems; - strncat( response, buffer, UUID_STRLEN - done + 1 ); + assert( done < 500 ); + if ( done < 499 ) { + size_t n = MIN( bytes, 499 - done ); + memcpy( dest + done, buffer, n ); + dest[done + n] = '\0'; + } return bytes; } /** * @brief Create a Session with the cow server and gets the session uuid. - * - * @param imageName - * @param version of the original Image */ -static bool createSession( const char *imageName, uint16_t version ) +static bool createSession( const char *imageName, uint16_t rid ) { CURLcode res; char url[COW_URL_STRING_SIZE]; + char body[1000], reply[500]; + const char *nameEsc; + snprintf( url, COW_URL_STRING_SIZE, COW_API_CREATE, cowServerAddress ); logadd( LOG_INFO, "COW_API_CREATE URL: %s", url ); curl_easy_setopt( curl, CURLOPT_POST, 1L ); curl_easy_setopt( curl, CURLOPT_URL, url ); - curl_mime *mime; - curl_mimepart *part; - mime = curl_mime_init( curl ); - part = curl_mime_addpart( mime ); - curl_mime_name( part, "imageName" ); - curl_mime_data( part, imageName, CURL_ZERO_TERMINATED ); - part = curl_mime_addpart( mime ); - curl_mime_name( part, "version" ); - char buf[sizeof( int ) * 3 + 2]; - snprintf( buf, sizeof buf, "%d", version ); - curl_mime_data( part, buf, CURL_ZERO_TERMINATED ); - - part = curl_mime_addpart( mime ); - curl_mime_name( part, "bitfieldSize" ); - snprintf( buf, sizeof buf, "%d", metadata->bitfieldSize ); - curl_mime_data( part, buf, CURL_ZERO_TERMINATED ); - - curl_easy_setopt( curl, CURLOPT_MIMEPOST, mime ); - - metadata->uuid[0] = '\0'; - curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, curlCallbackCreateSession ); - curl_easy_setopt( curl, CURLOPT_WRITEDATA, &metadata->uuid ); + nameEsc = curl_easy_escape( curl, imageName, 0 ); + if ( nameEsc == NULL ) { + logadd( LOG_ERROR, "Error escaping imageName" ); + nameEsc = imageName; // Hope for the best + } + snprintf( body, sizeof body, "revision=%d&bitfieldSize=%d&imageName=%s", + (int)rid, (int)metadata->bitfieldSize, nameEsc ); + if ( nameEsc != imageName ) { + curl_free( (char*)nameEsc ); + } + curl_easy_setopt( curl, CURLOPT_POSTFIELDS, body ); + + reply[0] = '\0'; + curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, curlWriteCb500 ); + curl_easy_setopt( curl, CURLOPT_WRITEDATA, reply ); res = curl_easy_perform( curl ); - curl_mime_free( mime ); /* Check for errors */ if ( res != CURLE_OK ) { - logadd( LOG_ERROR, "COW_API_CREATE failed: %s\n", curl_easy_strerror( res ) ); + logadd( LOG_ERROR, "COW_API_CREATE failed: curl says %s", curl_easy_strerror( res ) ); return false; } long http_code = 0; curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &http_code ); if ( http_code < 200 || http_code >= 300 ) { - logadd( LOG_ERROR, "COW_API_CREATE failed http: %ld\n", http_code ); + logadd( LOG_ERROR, "COW_API_CREATE failed: http code %ld, %s", http_code, reply ); + return false; + } + if ( strlen( reply ) > UUID_STRLEN ) { + logadd( LOG_ERROR, "Returned session id is too long: '%s'", reply ); return false; } + strncpy( metadata->uuid, reply, sizeof(metadata->uuid) ); curl_easy_reset( curl ); - metadata->uuid[UUID_STRLEN] = '\0'; - logadd( LOG_DEBUG1, "Cow session started, uuid: %s\n", metadata->uuid ); + logadd( LOG_DEBUG1, "Cow session started, uuid: %s", metadata->uuid ); return true; } @@ -329,7 +320,7 @@ static bool postMergeRequest() { CURLcode res; char url[COW_URL_STRING_SIZE]; - char body[500]; + char body[500], reply[500]; char *uuid; curl_easy_reset( curl ); @@ -337,6 +328,8 @@ static bool postMergeRequest() snprintf( url, COW_URL_STRING_SIZE, COW_API_START_MERGE, cowServerAddress ); curl_easy_setopt( curl, CURLOPT_URL, url ); curl_easy_setopt( curl, CURLOPT_POST, 1L ); + curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, curlWriteCb500 ); + curl_easy_setopt( curl, CURLOPT_WRITEDATA, reply ); uuid = curl_easy_escape( curl, metadata->uuid, 0 ); if ( uuid == NULL ) { @@ -350,15 +343,16 @@ static bool postMergeRequest() } curl_easy_setopt( curl, CURLOPT_POSTFIELDS, body ); + reply[0] = '\0'; res = curl_easy_perform( curl ); if ( res != CURLE_OK ) { - logadd( LOG_WARNING, "COW_API_START_MERGE failed: %s", curl_easy_strerror( res ) ); + logadd( LOG_WARNING, "COW_API_START_MERGE failed. curl reported: %s", curl_easy_strerror( res ) ); return false; } long http_code = 0; curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &http_code ); if ( http_code < 200 || http_code >= 300 ) { - logadd( LOG_WARNING, "COW_API_START_MERGE failed http: %ld", http_code ); + logadd( LOG_WARNING, "COW_API_START_MERGE failed with http: %ld: %s", http_code, reply ); return false; } return true; |