Skip to content

Commit 396e5c9

Browse files
authored
Fixes for S3 uploading bugs. (#1984)
* Fixes two bugs in uploading. First bug prevented the finalisation of the upload in AWS. This was caused by not removing two header entries which caused the signature to be miscalculated. Second was caused by failure to re-check the response code after a redirection. * Adds checks for 307 Temporary Redirect responses from AWS. These can occur when a bucket has just been created, and its name hasn't been added completely to DNS. They are handled in the same way as 301 Moved Permanently.
1 parent 5d03b0b commit 396e5c9

File tree

1 file changed

+81
-52
lines changed

1 file changed

+81
-52
lines changed

hfile_s3.c

Lines changed: 81 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,8 @@ static int redirect_endpoint(hFILE_s3 *fp, kstring_t *header) {
538538
}
539539
}
540540

541+
if (hts_verbose >= HTS_LOG_INFO) fprintf(stderr, "hfile_s3: redirect_endpoint: return %d\n", ret);
542+
541543
return ret;
542544
}
543545

@@ -1120,9 +1122,9 @@ static int set_region(s3_auth_data *ad, kstring_t *region) {
11201122
// Some common code
11211123

11221124
#define S3_MOVED_PERMANENTLY 301
1125+
#define S3_TEMPORARY_REDIRECT 307
11231126
#define S3_BAD_REQUEST 400
11241127

1125-
11261128
static struct {
11271129
kstring_t useragent;
11281130
CURLSH *share;
@@ -1178,7 +1180,7 @@ static char *stristr(char *haystack, char *needle) {
11781180
char *h = haystack;
11791181
char *n = needle;
11801182

1181-
while (toupper(*h) == toupper(*n)) {
1183+
while (toupper_c(*h) == toupper_c(*n)) {
11821184
h++, n++;
11831185
if (!*h || !*n) break;
11841186
}
@@ -1319,6 +1321,15 @@ static struct curl_slist *set_html_headers(hFILE_s3 *fp, kstring_t *auth, kstrin
13191321
struct curl_slist *headers = NULL;
13201322
int err = 0;
13211323

1324+
/* The next two lines have the effect of preventing curl from
1325+
adding these headers. If they exist it can lead to conflicts
1326+
in the signature calculations (not present in all S3 systems).
1327+
*/
1328+
err = add_header(&headers, "Content-Type:");
1329+
err |= add_header(&headers, "Expect:");
1330+
1331+
if (err) goto error;
1332+
13221333
if (auth->l)
13231334
if ((err = add_header(&headers, auth->s)))
13241335
goto error;
@@ -1407,11 +1418,13 @@ uploads and abandon the upload process.
14071418
static int abort_upload(hFILE_s3 *fp) {
14081419
kstring_t url = KS_INITIALIZE;
14091420
kstring_t canonical_query_string = KS_INITIALIZE;
1410-
int ret = -1;
1421+
int ret = -1, save_errno;
14111422
struct curl_slist *headers = NULL;
14121423
char http_request[] = "DELETE";
14131424
CURLcode err;
14141425

1426+
save_errno = errno; // keep the errno that caused the need to abort
1427+
14151428
clear_authorisation_values(fp);
14161429

14171430
if (ksprintf(&canonical_query_string, "uploadId=%s", fp->upload_id.s) < 0) {
@@ -1459,6 +1472,7 @@ static int abort_upload(hFILE_s3 *fp) {
14591472
fp->aborted = 1;
14601473
cleanup(fp);
14611474

1475+
errno = save_errno;
14621476
return ret;
14631477
}
14641478

@@ -1497,6 +1511,7 @@ static int complete_upload(hFILE_s3 *fp, kstring_t *resp) {
14971511
curl_easy_reset(fp->curl);
14981512

14991513
err = curl_easy_setopt(fp->curl, CURLOPT_POST, 1L);
1514+
15001515
err |= curl_easy_setopt(fp->curl, CURLOPT_POSTFIELDS, fp->completion_message.s);
15011516
err |= curl_easy_setopt(fp->curl, CURLOPT_POSTFIELDSIZE, (long) fp->completion_message.l);
15021517
err |= curl_easy_setopt(fp->curl, CURLOPT_WRITEFUNCTION, response_callback);
@@ -1670,6 +1685,7 @@ static int s3_write_close(hFILE *fpv) {
16701685
kstring_t response = {0, 0, NULL};
16711686
int ret = 0;
16721687
CURLcode cret;
1688+
long response_code;
16731689

16741690
if (!fp->aborted) {
16751691

@@ -1679,7 +1695,6 @@ static int s3_write_close(hFILE *fpv) {
16791695
ret = upload_part(fp, &response);
16801696

16811697
if (!ret) {
1682-
long response_code;
16831698
kstring_t etag = {0, 0, NULL};
16841699

16851700
cret = curl_easy_getinfo(fp->curl, CURLINFO_RESPONSE_CODE, &response_code);
@@ -1715,6 +1730,17 @@ static int s3_write_close(hFILE *fpv) {
17151730
if (!ret) {
17161731
if (strstr(response.s, "CompleteMultipartUploadResult") == NULL) {
17171732
ret = -1;
1733+
cret = curl_easy_getinfo(fp->curl, CURLINFO_RESPONSE_CODE, &response_code);
1734+
1735+
if (cret == CURLE_OK) {
1736+
if (hts_verbose >= HTS_LOG_INFO) {
1737+
if (report_s3_error(&response, response_code)) {
1738+
fprintf(stderr, "hfile_s3: warning, unable to report full S3 error status.\n");
1739+
}
1740+
}
1741+
1742+
errno = http_status_errno(response_code);
1743+
}
17181744
}
17191745
}
17201746
} else {
@@ -1746,6 +1772,8 @@ static int handle_bad_request(hFILE_s3 *fp, kstring_t *resp) {
17461772

17471773
ks_free(&region);
17481774

1775+
if (hts_verbose >= HTS_LOG_INFO) fprintf(stderr, "hfile_s3: handle_bad_request: return %d\n", ret);
1776+
17491777
return ret;
17501778
}
17511779

@@ -1852,14 +1880,13 @@ static int get_part(hFILE_s3 *fp, kstring_t *resp) {
18521880
struct curl_slist *headers = NULL;
18531881
int ret = -1;
18541882
char http_request[] = "GET";
1855-
char canonical_query_string = 0;
18561883
CURLcode err;
18571884

18581885
ks_clear(&fp->buffer); // reset storage buffer
18591886
clear_authorisation_values(fp);
18601887

18611888
if (fp->au->is_v4) {
1862-
if (v4_authorisation(fp, http_request, NULL, &canonical_query_string, 0) != 0) {
1889+
if (v4_authorisation(fp, http_request, NULL, "", 0) != 0) {
18631890
goto out;
18641891
}
18651892

@@ -2077,7 +2104,7 @@ static hFILE *s3_write_open(const char *url, s3_auth_data *auth) {
20772104
hFILE_s3 *fp;
20782105
kstring_t response = {0, 0, NULL};
20792106
kstring_t header = {0, 0, NULL};
2080-
int ret, has_user_query = 0;
2107+
int has_user_query = 0;
20812108
char *query_start;
20822109
const char *env;
20832110
CURLcode cret;
@@ -2125,30 +2152,35 @@ static hFILE *s3_write_open(const char *url, s3_auth_data *auth) {
21252152
has_user_query = 1;;
21262153
}
21272154

2128-
ret = initialise_upload(fp, &header, &response, has_user_query);
2155+
if (initialise_upload(fp, &header, &response, has_user_query))
2156+
goto error;
2157+
21292158
cret = curl_easy_getinfo(fp->curl, CURLINFO_RESPONSE_CODE, &response_code);
21302159

2131-
if (ret == 0) {
2132-
if (cret == CURLE_OK) {
2133-
if (response_code == S3_MOVED_PERMANENTLY) {
2134-
if (redirect_endpoint(fp, &header) == 0) {
2135-
ks_clear(&response);
2136-
ks_clear(&header);
2160+
if (cret == CURLE_OK) {
2161+
if (response_code == S3_MOVED_PERMANENTLY || response_code == S3_TEMPORARY_REDIRECT) {
2162+
if (redirect_endpoint(fp, &header) == 0) {
2163+
ks_clear(&response);
2164+
ks_clear(&header);
21372165

2138-
ret = initialise_upload(fp, &header, &response, has_user_query);
2139-
}
2140-
} else if (response_code == S3_BAD_REQUEST) {
2141-
if (handle_bad_request(fp, &response) == 0) {
2142-
ks_clear(&response);
2143-
ks_clear(&header);
2166+
if (initialise_upload(fp, &header, &response, has_user_query))
2167+
goto error;
2168+
}
2169+
} else if (response_code == S3_BAD_REQUEST) {
2170+
if (handle_bad_request(fp, &response) == 0) {
2171+
ks_clear(&response);
2172+
ks_clear(&header);
21442173

2145-
ret = initialise_upload(fp, &header, &response, has_user_query);
2146-
}
2174+
if (initialise_upload(fp, &header, &response, has_user_query))
2175+
goto error;
21472176
}
2148-
} else {
2149-
// unable to get a response code from curl
2150-
ret = -1;
21512177
}
2178+
2179+
// reget the response code (may not have changed)
2180+
cret = curl_easy_getinfo(fp->curl, CURLINFO_RESPONSE_CODE, &response_code);
2181+
} else {
2182+
// unable to get a response code from curl
2183+
goto error;
21522184
}
21532185

21542186
if (response_code >= 300) {
@@ -2164,11 +2196,9 @@ static hFILE *s3_write_open(const char *url, s3_auth_data *auth) {
21642196
errno = http_status_errno(response_code);
21652197
}
21662198

2167-
ret = -1;
2199+
goto error;
21682200
}
21692201

2170-
if (ret) goto error;
2171-
21722202
if (get_upload_id(fp, &response)) goto error;
21732203

21742204
// start the completion message (a formatted list of parts)
@@ -2203,7 +2233,6 @@ static hFILE *s3_read_open(const char *url, s3_auth_data *auth) {
22032233
const char *env;
22042234
kstring_t response = {0, 0, NULL};
22052235
kstring_t file_range = {0, 0, NULL};
2206-
int ret;
22072236
CURLcode cret;
22082237
long response_code = 0;
22092238

@@ -2240,31 +2269,33 @@ static hFILE *s3_read_open(const char *url, s3_auth_data *auth) {
22402269

22412270
kputs(url, &fp->url);
22422271

2243-
ret = initialise_download(fp, &response);
2244-
cret = curl_easy_getinfo(fp->curl, CURLINFO_RESPONSE_CODE, &response_code);
2272+
if (initialise_download(fp, &response))
2273+
goto error;
22452274

2246-
if (ret == 0) {
2247-
if (cret == CURLE_OK) {
2248-
if (response_code == S3_MOVED_PERMANENTLY) {
2249-
ks_clear(&response);
2275+
cret = curl_easy_getinfo(fp->curl, CURLINFO_RESPONSE_CODE, &response_code);
22502276

2251-
if (redirect_endpoint(fp, &response) == 0) {
2252-
ret = initialise_download(fp, &response);
2253-
}
2254-
} else if (response_code == S3_BAD_REQUEST) {
2255-
ks_clear(&response);
2277+
if (cret == CURLE_OK) {
2278+
if (response_code == S3_MOVED_PERMANENTLY || response_code == S3_TEMPORARY_REDIRECT) {
2279+
ks_clear(&response);
22562280

2257-
if (handle_bad_request(fp, &fp->buffer) == 0) {
2258-
ret = initialise_download(fp, &response);
2259-
}
2281+
if (redirect_endpoint(fp, &response) == 0) {
2282+
if (initialise_download(fp, &response))
2283+
goto error;
22602284
}
2285+
} else if (response_code == S3_BAD_REQUEST) {
2286+
ks_clear(&response);
22612287

2262-
// reget the response code (may not have changed)
2263-
cret = curl_easy_getinfo(fp->curl, CURLINFO_RESPONSE_CODE, &response_code);
2264-
} else {
2265-
// unable to get a response code from curl
2266-
ret = -1;
2288+
if (handle_bad_request(fp, &fp->buffer) == 0) {
2289+
if (initialise_download(fp, &response))
2290+
goto error;
2291+
}
22672292
}
2293+
2294+
// reget the response code (may not have changed)
2295+
cret = curl_easy_getinfo(fp->curl, CURLINFO_RESPONSE_CODE, &response_code);
2296+
} else {
2297+
// unable to get a response code from curl
2298+
goto error;
22682299
}
22692300

22702301
if (response_code >= 300) {
@@ -2280,11 +2311,9 @@ static hFILE *s3_read_open(const char *url, s3_auth_data *auth) {
22802311
errno = http_status_errno(response_code);
22812312
}
22822313

2283-
ret = -1;
2314+
goto error;
22842315
}
22852316

2286-
if (ret) goto error;
2287-
22882317
if (get_entry(response.s, "content-range: bytes ", "\n", &file_range) == EOF) {
22892318
fprintf(stderr, "hfile_s3: warning: failed to read file size.\n");
22902319
fp->file_size = -1;
@@ -2373,7 +2402,7 @@ static hFILE *hopen_s3(const char *url, const char *mode)
23732402
{
23742403
hFILE *fp;
23752404

2376-
if (getenv("HTS_S3_V2") == NULL) { // Force the v2 signature code
2405+
if (getenv("HTS_S3_V2") == NULL) {
23772406
fp = s3_open_v4(url, mode, NULL);
23782407
} else {
23792408
fp = s3_open_v2(url, mode, NULL);

0 commit comments

Comments
 (0)