ruby-libstorj 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.gitmodules +3 -0
- data/.rspec +1 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +111 -0
- data/Guardfile +21 -0
- data/LICENSE +502 -0
- data/README.md +262 -0
- data/Rakefile +76 -0
- data/ext/libstorj/.gitignore +47 -0
- data/ext/libstorj/.travis.yml +27 -0
- data/ext/libstorj/Doxyfile +2427 -0
- data/ext/libstorj/LICENSE +502 -0
- data/ext/libstorj/Makefile.am +6 -0
- data/ext/libstorj/README.md +198 -0
- data/ext/libstorj/autogen.sh +3 -0
- data/ext/libstorj/configure.ac +64 -0
- data/ext/libstorj/depends/Makefile +153 -0
- data/ext/libstorj/depends/config.guess +1462 -0
- data/ext/libstorj/depends/config.sub +1823 -0
- data/ext/libstorj/depends/extract-osx-sdk.sh +33 -0
- data/ext/libstorj/depends/packages/cctools.mk +7 -0
- data/ext/libstorj/depends/packages/clang.mk +7 -0
- data/ext/libstorj/depends/packages/gmp.mk +23 -0
- data/ext/libstorj/depends/packages/gnutls.mk +25 -0
- data/ext/libstorj/depends/packages/json-c.mk +7 -0
- data/ext/libstorj/depends/packages/libcurl.mk +39 -0
- data/ext/libstorj/depends/packages/libmicrohttpd.mk +7 -0
- data/ext/libstorj/depends/packages/libuv.mk +7 -0
- data/ext/libstorj/depends/packages/nettle.mk +30 -0
- data/ext/libstorj/libstorj.pc.in +11 -0
- data/ext/libstorj/src/Makefile.am +23 -0
- data/ext/libstorj/src/bip39.c +233 -0
- data/ext/libstorj/src/bip39.h +64 -0
- data/ext/libstorj/src/bip39_english.h +2074 -0
- data/ext/libstorj/src/cli.c +1494 -0
- data/ext/libstorj/src/crypto.c +525 -0
- data/ext/libstorj/src/crypto.h +178 -0
- data/ext/libstorj/src/downloader.c +1923 -0
- data/ext/libstorj/src/downloader.h +163 -0
- data/ext/libstorj/src/http.c +688 -0
- data/ext/libstorj/src/http.h +175 -0
- data/ext/libstorj/src/rs.c +962 -0
- data/ext/libstorj/src/rs.h +99 -0
- data/ext/libstorj/src/storj.c +1523 -0
- data/ext/libstorj/src/storj.h +1014 -0
- data/ext/libstorj/src/uploader.c +2736 -0
- data/ext/libstorj/src/uploader.h +181 -0
- data/ext/libstorj/src/utils.c +336 -0
- data/ext/libstorj/src/utils.h +65 -0
- data/ext/libstorj/test/Makefile.am +27 -0
- data/ext/libstorj/test/mockbridge.c +260 -0
- data/ext/libstorj/test/mockbridge.json +687 -0
- data/ext/libstorj/test/mockbridgeinfo.json +1836 -0
- data/ext/libstorj/test/mockfarmer.c +358 -0
- data/ext/libstorj/test/storjtests.h +41 -0
- data/ext/libstorj/test/tests.c +1617 -0
- data/ext/libstorj/test/tests_rs.c +869 -0
- data/ext/ruby-libstorj/extconf.rb +8 -0
- data/ext/ruby-libstorj/ruby-libstorj.cc +17 -0
- data/lib/ruby-libstorj.rb +1 -0
- data/lib/ruby-libstorj/arg_forwarding_task.rb +58 -0
- data/lib/ruby-libstorj/env.rb +178 -0
- data/lib/ruby-libstorj/ext/bucket.rb +71 -0
- data/lib/ruby-libstorj/ext/create_bucket_request.rb +53 -0
- data/lib/ruby-libstorj/ext/curl_code.rb +139 -0
- data/lib/ruby-libstorj/ext/ext.rb +71 -0
- data/lib/ruby-libstorj/ext/file.rb +84 -0
- data/lib/ruby-libstorj/ext/get_bucket_request.rb +45 -0
- data/lib/ruby-libstorj/ext/json_request.rb +51 -0
- data/lib/ruby-libstorj/ext/list_files_request.rb +63 -0
- data/lib/ruby-libstorj/ext/types.rb +226 -0
- data/lib/ruby-libstorj/ext/upload_options.rb +38 -0
- data/lib/ruby-libstorj/libstorj.rb +22 -0
- data/lib/ruby-libstorj/mixins/storj.rb +27 -0
- data/lib/ruby-libstorj/struct.rb +42 -0
- data/ruby-libstorj.gemspec +57 -0
- data/spec/helpers/options.yml.example +22 -0
- data/spec/helpers/shared_rake_examples.rb +132 -0
- data/spec/helpers/storj_options.rb +96 -0
- data/spec/helpers/upload.data +3 -0
- data/spec/helpers/upload.data.sha256 +1 -0
- data/spec/libstorj_spec.rb +0 -0
- data/spec/ruby-libstorj/arg_forwarding_task_spec.rb +311 -0
- data/spec/ruby-libstorj/env_spec.rb +353 -0
- data/spec/ruby-libstorj/ext_spec.rb +75 -0
- data/spec/ruby-libstorj/json_request_spec.rb +13 -0
- data/spec/ruby-libstorj/libstorj_spec.rb +81 -0
- data/spec/ruby-libstorj/struct_spec.rb +64 -0
- data/spec/spec_helper.rb +113 -0
- metadata +136 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file crypto.h
|
|
3
|
+
* @brief Storj crypto utilities.
|
|
4
|
+
*
|
|
5
|
+
* Helper crypto utilities
|
|
6
|
+
*/
|
|
7
|
+
#ifndef STORJ_CRYPTO_H
|
|
8
|
+
#define STORJ_CRYPTO_H
|
|
9
|
+
|
|
10
|
+
#include <nettle/aes.h>
|
|
11
|
+
#include <nettle/ripemd160.h>
|
|
12
|
+
#include <nettle/hmac.h>
|
|
13
|
+
#include <nettle/pbkdf2.h>
|
|
14
|
+
#include <nettle/sha.h>
|
|
15
|
+
#include <nettle/ctr.h>
|
|
16
|
+
#include <nettle/gcm.h>
|
|
17
|
+
#include <nettle/base64.h>
|
|
18
|
+
|
|
19
|
+
#include "bip39.h"
|
|
20
|
+
#include "utils.h"
|
|
21
|
+
|
|
22
|
+
#define DETERMINISTIC_KEY_SIZE 64
|
|
23
|
+
#define DETERMINISTIC_KEY_HEX_SIZE 32
|
|
24
|
+
#define BUCKET_NAME_MAGIC "398734aab3c4c30c9f22590e83a95f7e43556a45fc2b3060e0c39fde31f50272"
|
|
25
|
+
|
|
26
|
+
static const uint8_t BUCKET_META_MAGIC[32] = {66,150,71,16,50,114,88,160,163,35,154,65,162,213,226,215,70,138,57,61,52,19,210,170,38,164,162,200,86,201,2,81};
|
|
27
|
+
|
|
28
|
+
int sha256_of_str(const uint8_t *str, int str_len, uint8_t *digest);
|
|
29
|
+
|
|
30
|
+
int sha512_of_str(const uint8_t *str, int str_len, uint8_t *digest);
|
|
31
|
+
|
|
32
|
+
int ripemd160_of_str(const uint8_t *str, int str_len, uint8_t *digest);
|
|
33
|
+
|
|
34
|
+
int ripemd160sha256(uint8_t *data, uint64_t data_size, uint8_t *digest);
|
|
35
|
+
|
|
36
|
+
int ripemd160sha256_as_string(uint8_t *data, uint64_t data_size, char *digest);
|
|
37
|
+
|
|
38
|
+
int double_ripemd160sha256(uint8_t *data, uint64_t data_size, uint8_t *digest);
|
|
39
|
+
|
|
40
|
+
int double_ripemd160sha256_as_string(uint8_t *data, uint64_t data_size,
|
|
41
|
+
char **digest);
|
|
42
|
+
|
|
43
|
+
void pbkdf2_hmac_sha512(unsigned key_length,
|
|
44
|
+
const uint8_t *key,
|
|
45
|
+
unsigned iterations,
|
|
46
|
+
unsigned salt_length, const uint8_t *salt,
|
|
47
|
+
unsigned length, uint8_t *dst);
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @brief Generate a bucket's key
|
|
51
|
+
*
|
|
52
|
+
* @param[in] Character array of the mnemonic
|
|
53
|
+
* @param[in] bucket_id Character array of bucket id
|
|
54
|
+
* @param[out] bucket_key 64 byte character array that is the bucket's key
|
|
55
|
+
* @return A non-zero error value on failure and 0 on success.
|
|
56
|
+
*/
|
|
57
|
+
int generate_bucket_key(const char *mnemonic, const char *bucket_id,
|
|
58
|
+
char **bucket_key);
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @brief Generate a file's key
|
|
62
|
+
*
|
|
63
|
+
* @param[in] Character array of the mnemonic
|
|
64
|
+
* @param[in] bucket_id Character array of bucket id
|
|
65
|
+
* @param[in] index Character array of index
|
|
66
|
+
* @param[out] file_key 64 byte character array that is the bucket's key
|
|
67
|
+
* @return A non-zero error value on failure and 0 on success.
|
|
68
|
+
*/
|
|
69
|
+
int generate_file_key(const char *mnemonic,
|
|
70
|
+
const char *bucket_id,
|
|
71
|
+
const char *index,
|
|
72
|
+
char **file_key);
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @brief Calculate deterministic key by getting sha512 of key + id
|
|
76
|
+
*
|
|
77
|
+
* @param[in] Character array of the key
|
|
78
|
+
* @param[in] key_len Integer value of length of key
|
|
79
|
+
* @param[in] id Character array id
|
|
80
|
+
* @param[out] buffer 64 byte character array of the deterministic key
|
|
81
|
+
* @return A non-zero error value on failure and 0 on success.
|
|
82
|
+
*/
|
|
83
|
+
int get_deterministic_key(const char *key, int key_len,
|
|
84
|
+
const char *id, char **buffer);
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @brief Increment the iv for ctr decryption/encryption
|
|
88
|
+
*
|
|
89
|
+
* This function will modify iv and increment the counter based
|
|
90
|
+
* on the bytes position and the AES block size, useful for decrypting
|
|
91
|
+
* shards asynchronously.
|
|
92
|
+
*
|
|
93
|
+
* The iv must be 16 bytes, the AES block size, and the bytes_position
|
|
94
|
+
* must a multiple of 16.
|
|
95
|
+
*
|
|
96
|
+
* @param[out] iv The ctr/iv to be incremented
|
|
97
|
+
* @return A non-zero value on failure
|
|
98
|
+
*/
|
|
99
|
+
int increment_ctr_aes_iv(uint8_t *iv, uint64_t bytes_position);
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @brief Will derive an encryption key from passhrase
|
|
103
|
+
*
|
|
104
|
+
* Will use PBKDF2 to generate an encryption key from the passphrase.
|
|
105
|
+
*
|
|
106
|
+
* @param[in] passphrase - The passhrase
|
|
107
|
+
* @param[in] salt - The salt used in the key derivation function
|
|
108
|
+
* @return A key or NULL on failure.
|
|
109
|
+
*/
|
|
110
|
+
uint8_t *key_from_passphrase(const char *passphrase, const char *salt);
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @brief Will encrypt data with passphrase
|
|
114
|
+
*
|
|
115
|
+
* Data is encrypted using AES-256-CTR with a key generated from a key
|
|
116
|
+
* derivation function with the passphrase.
|
|
117
|
+
*
|
|
118
|
+
* @param[in] passphrase - The passhrase used to encrypt the data
|
|
119
|
+
* @param[in] salt - The salt used in the key derivation function
|
|
120
|
+
* @param[in] data - The data to be encrypted
|
|
121
|
+
* @param[out] result - The encrypted data encoded as hex string
|
|
122
|
+
* @return A non-zero error value on failure and 0 on success.
|
|
123
|
+
*/
|
|
124
|
+
int encrypt_data(const char *passphrase,
|
|
125
|
+
const char *salt,
|
|
126
|
+
const char *data,
|
|
127
|
+
char **result);
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @brief Will decrypt data with passphrase
|
|
131
|
+
*
|
|
132
|
+
* Data is decrypted using AES-256-CTR with a key generated from a key
|
|
133
|
+
* derivation function with the passphrase.
|
|
134
|
+
*
|
|
135
|
+
* @param[in] passphrase - The passhrase used to encrypt the data
|
|
136
|
+
* @param[in] salt - The salt used in the key derivation function
|
|
137
|
+
* @param[in] data - The hex string of encoded data
|
|
138
|
+
* @param[out] result - The decrypted data
|
|
139
|
+
*/
|
|
140
|
+
int decrypt_data(const char *passphrase,
|
|
141
|
+
const char *salt,
|
|
142
|
+
const char *data,
|
|
143
|
+
char **result);
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @brief Will encrypt file meta
|
|
147
|
+
*
|
|
148
|
+
* This will encrypt file meta information using AES-256-GCM. The
|
|
149
|
+
* resulting buffer will concat digest, iv and cipher text as base54
|
|
150
|
+
* null terminated string.
|
|
151
|
+
*
|
|
152
|
+
* @param[in] filemeta - The null terminated filename
|
|
153
|
+
* @param[in] encrypt_key - The key used to encrypt the file meta (32 bytes)
|
|
154
|
+
* @param[in] encrypt_iv - The iv to use for encryption (32 bytes)
|
|
155
|
+
* @param[out] buffer_base64 - The base64 encoded encrypted data including
|
|
156
|
+
* digest, iv and cipher text
|
|
157
|
+
* @return A non-zero value on error, zero on success.
|
|
158
|
+
*/
|
|
159
|
+
int encrypt_meta(const char *filemeta,
|
|
160
|
+
uint8_t *encrypt_key,
|
|
161
|
+
uint8_t *encrypt_iv,
|
|
162
|
+
char **buffer_base64);
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* @brief Will decrypt file meta
|
|
166
|
+
*
|
|
167
|
+
* This will decrypt file meta information.
|
|
168
|
+
*
|
|
169
|
+
* @param[in] buffer_base64 - The base64 encrypted data
|
|
170
|
+
* @param[in] decrypt_key - The key used to decrypt the file (32 bytes)
|
|
171
|
+
* @param[out] filemeta - The null terminated filename
|
|
172
|
+
* @return A non-zero value on error, zero on success.
|
|
173
|
+
*/
|
|
174
|
+
int decrypt_meta(const char *buffer_base64,
|
|
175
|
+
uint8_t *decrypt_key,
|
|
176
|
+
char **filemeta);
|
|
177
|
+
|
|
178
|
+
#endif /* STORJ_CRYPTO_H */
|
|
@@ -0,0 +1,1923 @@
|
|
|
1
|
+
#include "downloader.h"
|
|
2
|
+
|
|
3
|
+
static void free_exchange_report(storj_exchange_report_t *report)
|
|
4
|
+
{
|
|
5
|
+
free(report->data_hash);
|
|
6
|
+
free(report->reporter_id);
|
|
7
|
+
free(report->farmer_id);
|
|
8
|
+
free(report->client_id);
|
|
9
|
+
free(report);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static void free_download_state(storj_download_state_t *state)
|
|
13
|
+
{
|
|
14
|
+
for (int i = 0; i < state->total_pointers; i++) {
|
|
15
|
+
storj_pointer_t *pointer = &state->pointers[i];
|
|
16
|
+
|
|
17
|
+
free(pointer->token);
|
|
18
|
+
free(pointer->shard_hash);
|
|
19
|
+
free(pointer->farmer_id);
|
|
20
|
+
free(pointer->farmer_address);
|
|
21
|
+
|
|
22
|
+
free_exchange_report(pointer->report);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (state->excluded_farmer_ids) {
|
|
26
|
+
free(state->excluded_farmer_ids);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (state->decrypt_key) {
|
|
30
|
+
memset_zero(state->decrypt_key, SHA256_DIGEST_SIZE);
|
|
31
|
+
free(state->decrypt_key);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (state->decrypt_ctr) {
|
|
35
|
+
memset_zero(state->decrypt_ctr, AES_BLOCK_SIZE);
|
|
36
|
+
free(state->decrypt_ctr);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (state->info) {
|
|
40
|
+
if (state->info->erasure) {
|
|
41
|
+
free((char *)state->info->erasure);
|
|
42
|
+
}
|
|
43
|
+
free((char *)state->info->hmac);
|
|
44
|
+
free(state->info);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (state->hmac) {
|
|
48
|
+
free((char *)state->hmac);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
free(state->pointers);
|
|
52
|
+
free(state);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static void request_pointers(uv_work_t *work)
|
|
56
|
+
{
|
|
57
|
+
json_request_download_t *req = work->data;
|
|
58
|
+
storj_download_state_t *state = req->state;
|
|
59
|
+
|
|
60
|
+
int status_code = 0;
|
|
61
|
+
int request_status = fetch_json(req->http_options, req->options, req->method,
|
|
62
|
+
req->path, req->body, req->auth,
|
|
63
|
+
&req->response, &status_code);
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
if (request_status) {
|
|
67
|
+
state->log->warn(state->env->log_options, state->handle,
|
|
68
|
+
"Request pointers error: %i", request_status);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
req->status_code = status_code;
|
|
72
|
+
|
|
73
|
+
if (!req->response) {
|
|
74
|
+
req->status_code = -1;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
static void request_replace_pointer(uv_work_t *work)
|
|
80
|
+
{
|
|
81
|
+
json_request_replace_pointer_t *req = work->data;
|
|
82
|
+
storj_download_state_t *state = req->state;
|
|
83
|
+
|
|
84
|
+
int status_code = 0;
|
|
85
|
+
|
|
86
|
+
int excluded_farmer_ids_len = (req->excluded_farmer_ids) ? strlen(req->excluded_farmer_ids) : 0;
|
|
87
|
+
char query_args[BUFSIZ];
|
|
88
|
+
memset(query_args, '\0', BUFSIZ);
|
|
89
|
+
snprintf(query_args, BUFSIZ,
|
|
90
|
+
"?limit=1&skip=%i&exclude=%s",
|
|
91
|
+
req->pointer_index,
|
|
92
|
+
req->excluded_farmer_ids);
|
|
93
|
+
|
|
94
|
+
int path_len = 9 + strlen(req->bucket_id) + 7 +
|
|
95
|
+
strlen(req->file_id) + strlen(query_args);
|
|
96
|
+
char *path = calloc(path_len + 1, sizeof(char));
|
|
97
|
+
if (!path) {
|
|
98
|
+
req->error_status = STORJ_MEMORY_ERROR;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
strcat(path, "/buckets/");
|
|
103
|
+
strcat(path, req->bucket_id);
|
|
104
|
+
strcat(path, "/files/");
|
|
105
|
+
strcat(path, req->file_id);
|
|
106
|
+
strcat(path, query_args);
|
|
107
|
+
|
|
108
|
+
int request_status = fetch_json(req->http_options, req->options, "GET",
|
|
109
|
+
path, NULL, true,
|
|
110
|
+
&req->response, &status_code);
|
|
111
|
+
|
|
112
|
+
if (request_status) {
|
|
113
|
+
state->log->warn(state->env->log_options, state->handle,
|
|
114
|
+
"Request replace pointer error: %i", request_status);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
req->status_code = status_code;
|
|
118
|
+
|
|
119
|
+
if (!req->response) {
|
|
120
|
+
req->status_code = -1;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
free(path);
|
|
124
|
+
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
static void set_pointer_from_json(storj_download_state_t *state,
|
|
128
|
+
storj_pointer_t *p,
|
|
129
|
+
struct json_object *json,
|
|
130
|
+
bool is_replaced)
|
|
131
|
+
{
|
|
132
|
+
if (!json_object_is_type(json, json_type_object)) {
|
|
133
|
+
state->error_status = STORJ_BRIDGE_JSON_ERROR;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
struct json_object *token_value;
|
|
138
|
+
char *token = NULL;
|
|
139
|
+
if (json_object_object_get_ex(json, "token", &token_value)) {
|
|
140
|
+
token = (char *)json_object_get_string(token_value);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
struct json_object *hash_value;
|
|
144
|
+
if (!json_object_object_get_ex(json, "hash", &hash_value)) {
|
|
145
|
+
state->error_status = STORJ_BRIDGE_JSON_ERROR;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
char *hash = (char *)json_object_get_string(hash_value);
|
|
149
|
+
|
|
150
|
+
struct json_object *size_value;
|
|
151
|
+
if (!json_object_object_get_ex(json, "size", &size_value)) {
|
|
152
|
+
state->error_status = STORJ_BRIDGE_JSON_ERROR;
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
uint64_t size = json_object_get_int64(size_value);
|
|
156
|
+
|
|
157
|
+
struct json_object *parity_value;
|
|
158
|
+
bool parity = false;
|
|
159
|
+
if (json_object_object_get_ex(json, "parity", &parity_value)) {
|
|
160
|
+
parity = json_object_get_boolean(parity_value);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
struct json_object *index_value;
|
|
164
|
+
if (!json_object_object_get_ex(json, "index", &index_value)) {
|
|
165
|
+
state->error_status = STORJ_BRIDGE_JSON_ERROR;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
uint32_t index = json_object_get_int(index_value);
|
|
169
|
+
|
|
170
|
+
struct json_object *farmer_value;
|
|
171
|
+
char *address = NULL;
|
|
172
|
+
uint32_t port = 0;
|
|
173
|
+
char *farmer_id = NULL;
|
|
174
|
+
if (json_object_object_get_ex(json, "farmer", &farmer_value) &&
|
|
175
|
+
json_object_is_type(farmer_value, json_type_object)) {
|
|
176
|
+
|
|
177
|
+
struct json_object *address_value;
|
|
178
|
+
if (!json_object_object_get_ex(farmer_value, "address",
|
|
179
|
+
&address_value)) {
|
|
180
|
+
state->error_status = STORJ_BRIDGE_JSON_ERROR;
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
address = (char *)json_object_get_string(address_value);
|
|
184
|
+
|
|
185
|
+
struct json_object *port_value;
|
|
186
|
+
if (!json_object_object_get_ex(farmer_value, "port", &port_value)) {
|
|
187
|
+
state->error_status = STORJ_BRIDGE_JSON_ERROR;
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
port = json_object_get_int(port_value);
|
|
191
|
+
|
|
192
|
+
struct json_object *farmer_id_value;
|
|
193
|
+
if (!json_object_object_get_ex(farmer_value, "nodeID",
|
|
194
|
+
&farmer_id_value)) {
|
|
195
|
+
state->error_status = STORJ_BRIDGE_JSON_ERROR;
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
farmer_id = (char *)json_object_get_string(farmer_id_value);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (is_replaced) {
|
|
202
|
+
p->replace_count += 1;
|
|
203
|
+
} else {
|
|
204
|
+
p->replace_count = 0;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Check to see if we have a token for this shard, otherwise
|
|
208
|
+
// we will immediatly move this shard to POINTER_MISSING
|
|
209
|
+
// so that it can be retried and possibly recovered.
|
|
210
|
+
if (address && token) {
|
|
211
|
+
// reset the status
|
|
212
|
+
p->status = POINTER_CREATED;
|
|
213
|
+
} else {
|
|
214
|
+
state->log->warn(state->env->log_options,
|
|
215
|
+
state->handle,
|
|
216
|
+
"Missing shard %s at index %i",
|
|
217
|
+
hash,
|
|
218
|
+
index);
|
|
219
|
+
p->status = POINTER_MISSING;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
p->size = size;
|
|
223
|
+
p->parity = parity;
|
|
224
|
+
p->downloaded_size = 0;
|
|
225
|
+
p->index = index;
|
|
226
|
+
p->farmer_port = port;
|
|
227
|
+
|
|
228
|
+
if (is_replaced) {
|
|
229
|
+
free(p->token);
|
|
230
|
+
free(p->shard_hash);
|
|
231
|
+
free(p->farmer_address);
|
|
232
|
+
free(p->farmer_id);
|
|
233
|
+
}
|
|
234
|
+
if (token) {
|
|
235
|
+
p->token = strdup(token);
|
|
236
|
+
} else {
|
|
237
|
+
p->token = NULL;
|
|
238
|
+
}
|
|
239
|
+
p->shard_hash = strdup(hash);
|
|
240
|
+
if (address) {
|
|
241
|
+
p->farmer_address = strdup(address);
|
|
242
|
+
} else {
|
|
243
|
+
p->farmer_address = NULL;
|
|
244
|
+
}
|
|
245
|
+
if (farmer_id) {
|
|
246
|
+
p->farmer_id = strdup(farmer_id);
|
|
247
|
+
} else {
|
|
248
|
+
p->farmer_id = NULL;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// setup exchange report values
|
|
252
|
+
if (is_replaced) {
|
|
253
|
+
free_exchange_report(p->report);
|
|
254
|
+
}
|
|
255
|
+
p->report = malloc(
|
|
256
|
+
sizeof(storj_exchange_report_t));
|
|
257
|
+
|
|
258
|
+
if (!p->report) {
|
|
259
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const char *client_id = state->env->bridge_options->user;
|
|
264
|
+
p->report->reporter_id = strdup(client_id);
|
|
265
|
+
p->report->client_id = strdup(client_id);
|
|
266
|
+
p->report->data_hash = strdup(hash);
|
|
267
|
+
if (farmer_id) {
|
|
268
|
+
p->report->farmer_id = strdup(farmer_id);
|
|
269
|
+
} else {
|
|
270
|
+
p->report->farmer_id = NULL;
|
|
271
|
+
}
|
|
272
|
+
p->report->send_status = 0; // not sent
|
|
273
|
+
p->report->send_count = 0;
|
|
274
|
+
|
|
275
|
+
// these values will be changed in after_request_shard
|
|
276
|
+
p->report->start = 0;
|
|
277
|
+
p->report->end = 0;
|
|
278
|
+
p->report->code = STORJ_REPORT_FAILURE;
|
|
279
|
+
p->report->message = STORJ_REPORT_DOWNLOAD_ERROR;
|
|
280
|
+
|
|
281
|
+
p->work = NULL;
|
|
282
|
+
|
|
283
|
+
if (!state->shard_size) {
|
|
284
|
+
// TODO make sure all except last shard is the same size
|
|
285
|
+
state->shard_size = size;
|
|
286
|
+
state->log->debug(state->env->log_options,
|
|
287
|
+
state->handle,
|
|
288
|
+
"Shard size set to %" PRIu64,
|
|
289
|
+
state->shard_size);
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
static void append_pointers_to_state(storj_download_state_t *state,
|
|
294
|
+
struct json_object *res)
|
|
295
|
+
{
|
|
296
|
+
int length = json_object_array_length(res);
|
|
297
|
+
|
|
298
|
+
if (length == 0) {
|
|
299
|
+
state->log->debug(state->env->log_options,
|
|
300
|
+
state->handle,
|
|
301
|
+
"Finished requesting pointers");
|
|
302
|
+
state->pointers_completed = true;
|
|
303
|
+
} else if (length > 0) {
|
|
304
|
+
|
|
305
|
+
int prev_total_pointers = state->total_pointers;
|
|
306
|
+
int total_pointers = state->total_pointers + length;
|
|
307
|
+
|
|
308
|
+
if (state->total_pointers > 0) {
|
|
309
|
+
state->pointers = realloc(state->pointers,
|
|
310
|
+
total_pointers * sizeof(storj_pointer_t));
|
|
311
|
+
} else {
|
|
312
|
+
state->pointers = malloc(length * sizeof(storj_pointer_t) * 100);
|
|
313
|
+
}
|
|
314
|
+
if (!state->pointers) {
|
|
315
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
state->total_pointers = total_pointers;
|
|
320
|
+
state->total_shards = total_pointers;
|
|
321
|
+
|
|
322
|
+
for (int i = 0; i < length; i++) {
|
|
323
|
+
|
|
324
|
+
// get the relative index
|
|
325
|
+
int j = i + prev_total_pointers;
|
|
326
|
+
|
|
327
|
+
struct json_object *json = json_object_array_get_idx(res, i);
|
|
328
|
+
|
|
329
|
+
set_pointer_from_json(state, &state->pointers[j], json, false);
|
|
330
|
+
|
|
331
|
+
// Keep track of the number of data and parity pointers
|
|
332
|
+
storj_pointer_t *pointer = &state->pointers[j];
|
|
333
|
+
if (pointer->parity) {
|
|
334
|
+
state->total_parity_pointers += 1;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
static void after_request_pointers(uv_work_t *work, int status)
|
|
342
|
+
{
|
|
343
|
+
json_request_download_t *req = work->data;
|
|
344
|
+
storj_download_state_t *state = req->state;
|
|
345
|
+
|
|
346
|
+
state->pending_work_count--;
|
|
347
|
+
state->requesting_pointers = false;
|
|
348
|
+
|
|
349
|
+
if (req->response) {
|
|
350
|
+
state->log->debug(state->env->log_options, state->handle,
|
|
351
|
+
"Finished request pointers - JSON Response %s",
|
|
352
|
+
json_object_to_json_string(req->response));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (status != 0) {
|
|
356
|
+
|
|
357
|
+
state->error_status = STORJ_BRIDGE_POINTER_ERROR;
|
|
358
|
+
|
|
359
|
+
} else if (req->status_code == 429 || req->status_code == 420) {
|
|
360
|
+
|
|
361
|
+
state->error_status = STORJ_BRIDGE_RATE_ERROR;
|
|
362
|
+
|
|
363
|
+
} else if (req->status_code != 200) {
|
|
364
|
+
if (req->status_code > 0 && req->status_code < 500) {
|
|
365
|
+
state->error_status = STORJ_BRIDGE_POINTER_ERROR;
|
|
366
|
+
} else {
|
|
367
|
+
state->pointer_fail_count += 1;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
state->log->debug(state->env->log_options, state->handle,
|
|
371
|
+
"Request pointers fail count: %i",
|
|
372
|
+
state->pointer_fail_count);
|
|
373
|
+
|
|
374
|
+
if (state->pointer_fail_count >= STORJ_MAX_POINTER_TRIES) {
|
|
375
|
+
state->pointer_fail_count = 0;
|
|
376
|
+
state->error_status = STORJ_BRIDGE_POINTER_ERROR;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
} else if (!json_object_is_type(req->response, json_type_array)) {
|
|
380
|
+
state->error_status = STORJ_BRIDGE_JSON_ERROR;
|
|
381
|
+
} else {
|
|
382
|
+
append_pointers_to_state(state, req->response);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
queue_next_work(state);
|
|
386
|
+
|
|
387
|
+
if (req->response) {
|
|
388
|
+
json_object_put(req->response);
|
|
389
|
+
}
|
|
390
|
+
free(req->path);
|
|
391
|
+
free(req);
|
|
392
|
+
free(work);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
static void after_request_replace_pointer(uv_work_t *work, int status)
|
|
396
|
+
{
|
|
397
|
+
json_request_replace_pointer_t *req = work->data;
|
|
398
|
+
storj_download_state_t *state = req->state;
|
|
399
|
+
|
|
400
|
+
state->pending_work_count--;
|
|
401
|
+
state->requesting_pointers = false;
|
|
402
|
+
|
|
403
|
+
state->log->debug(state->env->log_options, state->handle,
|
|
404
|
+
"Finished request replace pointer %i - JSON Response: %s",
|
|
405
|
+
req->pointer_index,
|
|
406
|
+
json_object_to_json_string(req->response));
|
|
407
|
+
|
|
408
|
+
if (status != 0) {
|
|
409
|
+
|
|
410
|
+
state->error_status = STORJ_BRIDGE_REPOINTER_ERROR;
|
|
411
|
+
|
|
412
|
+
} else if (req->error_status) {
|
|
413
|
+
|
|
414
|
+
state->error_status = req->error_status;
|
|
415
|
+
|
|
416
|
+
} else if (req->status_code == 429 || req->status_code == 420) {
|
|
417
|
+
|
|
418
|
+
state->error_status = STORJ_BRIDGE_RATE_ERROR;
|
|
419
|
+
|
|
420
|
+
} else if (req->status_code != 200) {
|
|
421
|
+
|
|
422
|
+
if (req->status_code > 0 && req->status_code < 500) {
|
|
423
|
+
state->pointers[req->pointer_index].status = POINTER_MISSING;
|
|
424
|
+
} else {
|
|
425
|
+
// Update status so that it will be retried
|
|
426
|
+
state->pointers[req->pointer_index].status = POINTER_ERROR_REPORTED;
|
|
427
|
+
state->pointer_fail_count += 1;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
state->log->debug(state->env->log_options, state->handle,
|
|
431
|
+
"Request replace pointer fail count: %i",
|
|
432
|
+
state->pointer_fail_count);
|
|
433
|
+
|
|
434
|
+
if (state->pointer_fail_count >= STORJ_MAX_POINTER_TRIES) {
|
|
435
|
+
// Skip retrying mark as missing
|
|
436
|
+
state->pointer_fail_count = 0;
|
|
437
|
+
state->pointers[req->pointer_index].status = POINTER_MISSING;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
} else if (!json_object_is_type(req->response, json_type_array)) {
|
|
441
|
+
state->error_status = STORJ_BRIDGE_JSON_ERROR;
|
|
442
|
+
} else {
|
|
443
|
+
struct json_object *json = json_object_array_get_idx(req->response, 0);
|
|
444
|
+
|
|
445
|
+
set_pointer_from_json(state,
|
|
446
|
+
&state->pointers[req->pointer_index],
|
|
447
|
+
json,
|
|
448
|
+
true);
|
|
449
|
+
|
|
450
|
+
if (state->pointers[req->pointer_index].index != req->pointer_index) {
|
|
451
|
+
|
|
452
|
+
state->log->error(state->env->log_options,
|
|
453
|
+
state->handle,
|
|
454
|
+
"Replacement shard index %i does not match %i",
|
|
455
|
+
state->pointers[req->pointer_index].index,
|
|
456
|
+
req->pointer_index);
|
|
457
|
+
|
|
458
|
+
state->error_status = STORJ_BRIDGE_JSON_ERROR;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
queue_next_work(state);
|
|
463
|
+
|
|
464
|
+
json_object_put(req->response);
|
|
465
|
+
free(work->data);
|
|
466
|
+
free(work);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
static void queue_request_pointers(storj_download_state_t *state)
|
|
470
|
+
{
|
|
471
|
+
if (state->requesting_pointers || state->canceled) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// queue request to replace pointer if any pointers have failure
|
|
476
|
+
for (int i = 0; i < state->total_pointers; i++) {
|
|
477
|
+
|
|
478
|
+
storj_pointer_t *pointer = &state->pointers[i];
|
|
479
|
+
|
|
480
|
+
if (pointer->replace_count >= STORJ_DEFAULT_MIRRORS) {
|
|
481
|
+
state->log->warn(state->env->log_options,
|
|
482
|
+
state->handle,
|
|
483
|
+
"Unable to download shard %s at index %i",
|
|
484
|
+
pointer->shard_hash,
|
|
485
|
+
pointer->index);
|
|
486
|
+
pointer->replace_count = 0;
|
|
487
|
+
pointer->status = POINTER_MISSING;
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (pointer->status == POINTER_ERROR_REPORTED) {
|
|
492
|
+
|
|
493
|
+
// exclude this farmer id from future requests
|
|
494
|
+
state->log->debug(state->env->log_options,
|
|
495
|
+
state->handle,
|
|
496
|
+
"Adding farmer_id %s to excluded list",
|
|
497
|
+
pointer->report->farmer_id);
|
|
498
|
+
|
|
499
|
+
if (!state->excluded_farmer_ids) {
|
|
500
|
+
state->excluded_farmer_ids = calloc(42, sizeof(char));
|
|
501
|
+
if (!state->excluded_farmer_ids) {
|
|
502
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
strcat(state->excluded_farmer_ids, pointer->report->farmer_id);
|
|
506
|
+
} else {
|
|
507
|
+
state->excluded_farmer_ids =
|
|
508
|
+
realloc(state->excluded_farmer_ids,
|
|
509
|
+
strlen(state->excluded_farmer_ids) + 42);
|
|
510
|
+
if (!state->excluded_farmer_ids) {
|
|
511
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
strcat(state->excluded_farmer_ids, ",");
|
|
515
|
+
strcat(state->excluded_farmer_ids, pointer->report->farmer_id);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
json_request_replace_pointer_t *req =
|
|
519
|
+
malloc(sizeof(json_request_replace_pointer_t));
|
|
520
|
+
if (!req) {
|
|
521
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
req->pointer_index = i;
|
|
526
|
+
|
|
527
|
+
req->http_options = state->env->http_options;
|
|
528
|
+
req->options = state->env->bridge_options;
|
|
529
|
+
req->bucket_id = state->bucket_id;
|
|
530
|
+
req->file_id = state->file_id;
|
|
531
|
+
req->state = state;
|
|
532
|
+
req->excluded_farmer_ids = state->excluded_farmer_ids;
|
|
533
|
+
req->error_status = 0;
|
|
534
|
+
req->response = NULL;
|
|
535
|
+
req->status_code = 0;
|
|
536
|
+
|
|
537
|
+
uv_work_t *work = malloc(sizeof(uv_work_t));
|
|
538
|
+
if (!work) {
|
|
539
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
work->data = req;
|
|
543
|
+
|
|
544
|
+
state->log->info(state->env->log_options,
|
|
545
|
+
state->handle,
|
|
546
|
+
"Requesting replacement pointer at index: %i",
|
|
547
|
+
req->pointer_index);
|
|
548
|
+
|
|
549
|
+
state->pending_work_count++;
|
|
550
|
+
int status = uv_queue_work(state->env->loop,
|
|
551
|
+
(uv_work_t*) work,
|
|
552
|
+
request_replace_pointer,
|
|
553
|
+
after_request_replace_pointer);
|
|
554
|
+
|
|
555
|
+
if (status) {
|
|
556
|
+
state->error_status = STORJ_QUEUE_ERROR;
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
pointer->status = POINTER_BEING_REPLACED;
|
|
561
|
+
|
|
562
|
+
// we're done until the next pass
|
|
563
|
+
state->requesting_pointers = true;
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// only request the next set of pointers if we're not finished
|
|
570
|
+
if (state->pointers_completed) {
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
json_request_download_t *req = malloc(sizeof(json_request_download_t));
|
|
575
|
+
if (!req) {
|
|
576
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
char query_args[BUFSIZ];
|
|
581
|
+
memset(query_args, '\0', BUFSIZ);
|
|
582
|
+
snprintf(query_args, BUFSIZ, "?limit=3&skip=%d", state->total_pointers);
|
|
583
|
+
|
|
584
|
+
int path_len = 9 + strlen(state->bucket_id) + 7 +
|
|
585
|
+
strlen(state->file_id) + strlen(query_args);
|
|
586
|
+
|
|
587
|
+
char *path = calloc(path_len + 1, sizeof(char));
|
|
588
|
+
if (!path) {
|
|
589
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
strcat(path, "/buckets/");
|
|
593
|
+
strcat(path, state->bucket_id);
|
|
594
|
+
strcat(path, "/files/");
|
|
595
|
+
strcat(path, state->file_id);
|
|
596
|
+
strcat(path, query_args);
|
|
597
|
+
|
|
598
|
+
req->http_options = state->env->http_options;
|
|
599
|
+
req->options = state->env->bridge_options;
|
|
600
|
+
req->method = "GET";
|
|
601
|
+
req->path = path;
|
|
602
|
+
req->body = NULL;
|
|
603
|
+
req->auth = true;
|
|
604
|
+
|
|
605
|
+
req->state = state;
|
|
606
|
+
|
|
607
|
+
uv_work_t *work = malloc(sizeof(uv_work_t));
|
|
608
|
+
if (!work) {
|
|
609
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
work->data = req;
|
|
613
|
+
|
|
614
|
+
state->log->info(state->env->log_options,
|
|
615
|
+
state->handle,
|
|
616
|
+
"Requesting next set of pointers, total pointers: %i",
|
|
617
|
+
state->total_pointers);
|
|
618
|
+
|
|
619
|
+
state->pending_work_count++;
|
|
620
|
+
int status = uv_queue_work(state->env->loop, (uv_work_t*) work,
|
|
621
|
+
request_pointers, after_request_pointers);
|
|
622
|
+
|
|
623
|
+
if (status) {
|
|
624
|
+
state->error_status = STORJ_QUEUE_ERROR;
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
state->requesting_pointers = true;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
static void request_shard(uv_work_t *work)
|
|
632
|
+
{
|
|
633
|
+
shard_request_download_t *req = work->data;
|
|
634
|
+
|
|
635
|
+
int status_code;
|
|
636
|
+
int write_code = 0;
|
|
637
|
+
|
|
638
|
+
req->start = get_time_milliseconds();
|
|
639
|
+
|
|
640
|
+
uint64_t file_position = req->pointer_index * req->state->shard_size;
|
|
641
|
+
|
|
642
|
+
int error_status = fetch_shard(req->http_options,
|
|
643
|
+
req->farmer_id,
|
|
644
|
+
req->farmer_proto,
|
|
645
|
+
req->farmer_host,
|
|
646
|
+
req->farmer_port,
|
|
647
|
+
req->shard_hash,
|
|
648
|
+
req->shard_total_bytes,
|
|
649
|
+
req->token,
|
|
650
|
+
req->state->destination,
|
|
651
|
+
file_position,
|
|
652
|
+
&status_code,
|
|
653
|
+
&write_code,
|
|
654
|
+
&req->progress_handle,
|
|
655
|
+
req->canceled);
|
|
656
|
+
|
|
657
|
+
req->end = get_time_milliseconds();
|
|
658
|
+
|
|
659
|
+
if (write_code != 0) {
|
|
660
|
+
req->state->log->error(req->state->env->log_options, req->state->handle,
|
|
661
|
+
"Put shard read error: %i", write_code);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (error_status) {
|
|
665
|
+
req->error_status = error_status;
|
|
666
|
+
} else if (status_code != 200) {
|
|
667
|
+
switch(status_code) {
|
|
668
|
+
case 401:
|
|
669
|
+
case 403:
|
|
670
|
+
req->error_status = STORJ_FARMER_AUTH_ERROR;
|
|
671
|
+
break;
|
|
672
|
+
case 504:
|
|
673
|
+
req->error_status = STORJ_FARMER_TIMEOUT_ERROR;
|
|
674
|
+
break;
|
|
675
|
+
default:
|
|
676
|
+
req->error_status = STORJ_FARMER_REQUEST_ERROR;
|
|
677
|
+
}
|
|
678
|
+
} else {
|
|
679
|
+
req->error_status = 0;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
static void free_request_shard_work(uv_handle_t *progress_handle)
|
|
684
|
+
{
|
|
685
|
+
uv_work_t *work = progress_handle->data;
|
|
686
|
+
shard_request_download_t *req = work->data;
|
|
687
|
+
|
|
688
|
+
free(req);
|
|
689
|
+
free(work);
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
static uint64_t calculate_data_filesize(storj_download_state_t *state)
|
|
693
|
+
{
|
|
694
|
+
uint64_t total_bytes = 0;
|
|
695
|
+
|
|
696
|
+
for (int i = 0; i < state->total_pointers; i++) {
|
|
697
|
+
storj_pointer_t *pointer = &state->pointers[i];
|
|
698
|
+
if (pointer->parity) {
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
total_bytes += pointer->size;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
return total_bytes;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
static void report_progress(storj_download_state_t *state)
|
|
708
|
+
{
|
|
709
|
+
uint64_t downloaded_bytes = 0;
|
|
710
|
+
uint64_t total_bytes = 0;
|
|
711
|
+
|
|
712
|
+
for (int i = 0; i < state->total_pointers; i++) {
|
|
713
|
+
|
|
714
|
+
storj_pointer_t *pointer = &state->pointers[i];
|
|
715
|
+
|
|
716
|
+
downloaded_bytes += pointer->downloaded_size;
|
|
717
|
+
total_bytes += pointer->size;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
double total_progress = (double)downloaded_bytes / (double)total_bytes;
|
|
721
|
+
|
|
722
|
+
state->progress_cb(total_progress,
|
|
723
|
+
downloaded_bytes,
|
|
724
|
+
total_bytes,
|
|
725
|
+
state->handle);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
static void after_request_shard(uv_work_t *work, int status)
|
|
729
|
+
{
|
|
730
|
+
shard_request_download_t *req = work->data;
|
|
731
|
+
|
|
732
|
+
req->state->pending_work_count--;
|
|
733
|
+
req->state->resolving_shards -= 1;
|
|
734
|
+
|
|
735
|
+
uv_handle_t *progress_handle = (uv_handle_t *) &req->progress_handle;
|
|
736
|
+
|
|
737
|
+
// free the download progress
|
|
738
|
+
free(progress_handle->data);
|
|
739
|
+
|
|
740
|
+
// assign work so that we can free after progress_handle is closed
|
|
741
|
+
progress_handle->data = work;
|
|
742
|
+
|
|
743
|
+
// update the pointer status
|
|
744
|
+
storj_pointer_t *pointer = &req->state->pointers[req->pointer_index];
|
|
745
|
+
|
|
746
|
+
pointer->report->start = req->start;
|
|
747
|
+
pointer->report->end = req->end;
|
|
748
|
+
|
|
749
|
+
if (req->error_status) {
|
|
750
|
+
|
|
751
|
+
req->state->log->warn(req->state->env->log_options,
|
|
752
|
+
req->state->handle,
|
|
753
|
+
"Error downloading shard: %s, reason: %s",
|
|
754
|
+
req->shard_hash,
|
|
755
|
+
storj_strerror(req->error_status));
|
|
756
|
+
|
|
757
|
+
pointer->status = POINTER_ERROR;
|
|
758
|
+
|
|
759
|
+
switch(req->error_status) {
|
|
760
|
+
case STORJ_FARMER_INTEGRITY_ERROR:
|
|
761
|
+
pointer->report->code = STORJ_REPORT_FAILURE;
|
|
762
|
+
pointer->report->message = STORJ_REPORT_FAILED_INTEGRITY;
|
|
763
|
+
default:
|
|
764
|
+
pointer->report->code = STORJ_REPORT_FAILURE;
|
|
765
|
+
pointer->report->message = STORJ_REPORT_DOWNLOAD_ERROR;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
} else {
|
|
769
|
+
|
|
770
|
+
req->state->log->info(req->state->env->log_options,
|
|
771
|
+
req->state->handle,
|
|
772
|
+
"Finished downloading shard: %s",
|
|
773
|
+
req->shard_hash);
|
|
774
|
+
|
|
775
|
+
pointer->report->code = STORJ_REPORT_SUCCESS;
|
|
776
|
+
pointer->report->message = STORJ_REPORT_SHARD_DOWNLOADED;
|
|
777
|
+
pointer->status = POINTER_DOWNLOADED;
|
|
778
|
+
|
|
779
|
+
// Make sure the downloaded size is updated
|
|
780
|
+
pointer->downloaded_size = pointer->size;
|
|
781
|
+
|
|
782
|
+
report_progress(req->state);
|
|
783
|
+
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
queue_next_work(req->state);
|
|
787
|
+
|
|
788
|
+
// close the async progress handle
|
|
789
|
+
uv_close(progress_handle, free_request_shard_work);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
static void progress_request_shard(uv_async_t* async)
|
|
793
|
+
{
|
|
794
|
+
shard_download_progress_t *progress = async->data;
|
|
795
|
+
|
|
796
|
+
storj_download_state_t *state = progress->state;
|
|
797
|
+
|
|
798
|
+
state->pointers[progress->pointer_index].downloaded_size = progress->bytes;
|
|
799
|
+
|
|
800
|
+
report_progress(state);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
static void queue_request_shards(storj_download_state_t *state)
|
|
804
|
+
{
|
|
805
|
+
if (state->canceled) {
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
int i = 0;
|
|
810
|
+
|
|
811
|
+
while (state->resolving_shards < state->download_max_concurrency &&
|
|
812
|
+
i < state->total_pointers) {
|
|
813
|
+
|
|
814
|
+
storj_pointer_t *pointer = &state->pointers[i];
|
|
815
|
+
|
|
816
|
+
if (pointer->status == POINTER_CREATED) {
|
|
817
|
+
shard_request_download_t *req = malloc(sizeof(shard_request_download_t));
|
|
818
|
+
if (!req) {
|
|
819
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
req->http_options = state->env->http_options;
|
|
824
|
+
req->farmer_id = pointer->farmer_id;
|
|
825
|
+
req->farmer_proto = "http";
|
|
826
|
+
req->farmer_host = pointer->farmer_address;
|
|
827
|
+
req->farmer_port = pointer->farmer_port;
|
|
828
|
+
req->shard_hash = pointer->shard_hash;
|
|
829
|
+
req->shard_total_bytes = pointer->size;
|
|
830
|
+
req->byte_position = state->shard_size * i;
|
|
831
|
+
req->token = pointer->token;
|
|
832
|
+
req->error_status = 0;
|
|
833
|
+
|
|
834
|
+
req->pointer_index = pointer->index;
|
|
835
|
+
|
|
836
|
+
req->state = state;
|
|
837
|
+
req->canceled = &state->canceled;
|
|
838
|
+
|
|
839
|
+
uv_work_t *work = malloc(sizeof(uv_work_t));
|
|
840
|
+
if (!work) {
|
|
841
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
work->data = req;
|
|
846
|
+
|
|
847
|
+
state->resolving_shards += 1;
|
|
848
|
+
pointer->status = POINTER_BEING_DOWNLOADED;
|
|
849
|
+
pointer->work = work;
|
|
850
|
+
|
|
851
|
+
state->log->info(state->env->log_options,
|
|
852
|
+
state->handle,
|
|
853
|
+
"Queue request shard: %s",
|
|
854
|
+
req->shard_hash);
|
|
855
|
+
|
|
856
|
+
// setup download progress reporting
|
|
857
|
+
shard_download_progress_t *progress =
|
|
858
|
+
malloc(sizeof(shard_download_progress_t));
|
|
859
|
+
if (!progress) {
|
|
860
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
progress->pointer_index = pointer->index;
|
|
865
|
+
progress->bytes = 0;
|
|
866
|
+
progress->state = state;
|
|
867
|
+
|
|
868
|
+
req->progress_handle.data = progress;
|
|
869
|
+
|
|
870
|
+
uv_async_init(state->env->loop, &req->progress_handle,
|
|
871
|
+
progress_request_shard);
|
|
872
|
+
|
|
873
|
+
// queue download
|
|
874
|
+
state->pending_work_count++;
|
|
875
|
+
int status = uv_queue_work(state->env->loop, (uv_work_t*) work,
|
|
876
|
+
request_shard, after_request_shard);
|
|
877
|
+
if (status) {
|
|
878
|
+
state->error_status = STORJ_QUEUE_ERROR;
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
i++;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
static void send_exchange_report(uv_work_t *work)
|
|
888
|
+
{
|
|
889
|
+
shard_send_report_t *req = work->data;
|
|
890
|
+
storj_download_state_t *state = req->state;
|
|
891
|
+
|
|
892
|
+
struct json_object *body = json_object_new_object();
|
|
893
|
+
|
|
894
|
+
json_object_object_add(body, "dataHash",
|
|
895
|
+
json_object_new_string(req->report->data_hash));
|
|
896
|
+
|
|
897
|
+
json_object_object_add(body, "reporterId",
|
|
898
|
+
json_object_new_string(req->report->reporter_id));
|
|
899
|
+
|
|
900
|
+
json_object_object_add(body, "farmerId",
|
|
901
|
+
json_object_new_string(req->report->farmer_id));
|
|
902
|
+
|
|
903
|
+
json_object_object_add(body, "clientId",
|
|
904
|
+
json_object_new_string(req->report->client_id));
|
|
905
|
+
|
|
906
|
+
json_object_object_add(body, "exchangeStart",
|
|
907
|
+
json_object_new_int64(req->report->start));
|
|
908
|
+
|
|
909
|
+
json_object_object_add(body, "exchangeEnd",
|
|
910
|
+
json_object_new_int64(req->report->end));
|
|
911
|
+
|
|
912
|
+
json_object_object_add(body, "exchangeResultCode",
|
|
913
|
+
json_object_new_int(req->report->code));
|
|
914
|
+
|
|
915
|
+
json_object_object_add(body, "exchangeResultMessage",
|
|
916
|
+
json_object_new_string(req->report->message));
|
|
917
|
+
|
|
918
|
+
int status_code = 0;
|
|
919
|
+
|
|
920
|
+
// there should be an empty object in response
|
|
921
|
+
struct json_object *response = NULL;
|
|
922
|
+
int request_status = fetch_json(req->http_options,
|
|
923
|
+
req->options, "POST",
|
|
924
|
+
"/reports/exchanges", body,
|
|
925
|
+
true, &response, &status_code);
|
|
926
|
+
|
|
927
|
+
|
|
928
|
+
if (request_status) {
|
|
929
|
+
state->log->warn(state->env->log_options, state->handle,
|
|
930
|
+
"Send exchange report error: %i", request_status);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
req->status_code = status_code;
|
|
934
|
+
|
|
935
|
+
// free all memory for body and response
|
|
936
|
+
if (response) {
|
|
937
|
+
json_object_put(response);
|
|
938
|
+
}
|
|
939
|
+
json_object_put(body);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
static void after_send_exchange_report(uv_work_t *work, int status)
|
|
943
|
+
{
|
|
944
|
+
shard_send_report_t *req = work->data;
|
|
945
|
+
|
|
946
|
+
req->state->pending_work_count--;
|
|
947
|
+
|
|
948
|
+
// set status so that this pointer can be replaced
|
|
949
|
+
if (req->report->send_count >= STORJ_MAX_REPORT_TRIES ||
|
|
950
|
+
req->status_code == 201) {
|
|
951
|
+
|
|
952
|
+
storj_pointer_t *pointer = &req->state->pointers[req->pointer_index];
|
|
953
|
+
|
|
954
|
+
if (pointer->status == POINTER_ERROR) {
|
|
955
|
+
pointer->status = POINTER_ERROR_REPORTED;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
if (req->status_code == 201) {
|
|
960
|
+
// set the status so that this pointer can be replaced
|
|
961
|
+
req->report->send_status = 2; // report has been sent
|
|
962
|
+
} else {
|
|
963
|
+
req->report->send_status = 0; // reset report back to unsent
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
queue_next_work(req->state);
|
|
967
|
+
|
|
968
|
+
free(work->data);
|
|
969
|
+
free(work);
|
|
970
|
+
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
static void queue_send_exchange_reports(storj_download_state_t *state)
|
|
974
|
+
{
|
|
975
|
+
|
|
976
|
+
if (state->canceled) {
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
for (int i = 0; i < state->total_pointers; i++) {
|
|
981
|
+
|
|
982
|
+
storj_pointer_t *pointer = &state->pointers[i];
|
|
983
|
+
|
|
984
|
+
if (pointer->report->send_status < 1 &&
|
|
985
|
+
pointer->report->send_count < STORJ_MAX_REPORT_TRIES &&
|
|
986
|
+
pointer->report->start > 0 &&
|
|
987
|
+
pointer->report->end > 0) {
|
|
988
|
+
|
|
989
|
+
uv_work_t *work = malloc(sizeof(uv_work_t));
|
|
990
|
+
if (!work) {
|
|
991
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
shard_send_report_t *req = malloc(sizeof(shard_send_report_t));
|
|
996
|
+
if (!req) {
|
|
997
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
req->http_options = state->env->http_options;
|
|
1002
|
+
req->options = state->env->bridge_options;
|
|
1003
|
+
req->status_code = 0;
|
|
1004
|
+
req->report = pointer->report;
|
|
1005
|
+
req->report->send_status = 1; // being reported
|
|
1006
|
+
req->report->send_count += 1;
|
|
1007
|
+
req->state = state;
|
|
1008
|
+
req->pointer_index = i;
|
|
1009
|
+
|
|
1010
|
+
work->data = req;
|
|
1011
|
+
|
|
1012
|
+
state->pending_work_count++;
|
|
1013
|
+
int status = uv_queue_work(state->env->loop, (uv_work_t*) work,
|
|
1014
|
+
send_exchange_report,
|
|
1015
|
+
after_send_exchange_report);
|
|
1016
|
+
if (status) {
|
|
1017
|
+
state->error_status = STORJ_QUEUE_ERROR;
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
static void determine_decryption_key_v1(storj_download_state_t *state)
|
|
1025
|
+
{
|
|
1026
|
+
uint8_t *index = NULL;
|
|
1027
|
+
char *file_key_as_str = NULL;
|
|
1028
|
+
|
|
1029
|
+
file_key_as_str = calloc(DETERMINISTIC_KEY_SIZE + 1, sizeof(char));
|
|
1030
|
+
if (!file_key_as_str) {
|
|
1031
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
1032
|
+
goto cleanup;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
if (generate_file_key(state->env->encrypt_options->mnemonic,
|
|
1036
|
+
state->bucket_id,
|
|
1037
|
+
state->info->index, &file_key_as_str)) {
|
|
1038
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
1039
|
+
goto cleanup;
|
|
1040
|
+
}
|
|
1041
|
+
file_key_as_str[DETERMINISTIC_KEY_SIZE] = '\0';
|
|
1042
|
+
|
|
1043
|
+
uint8_t *decrypt_key = str2hex(strlen(file_key_as_str), file_key_as_str);
|
|
1044
|
+
if (!decrypt_key) {
|
|
1045
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
1046
|
+
goto cleanup;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
state->decrypt_key = decrypt_key;
|
|
1050
|
+
|
|
1051
|
+
index = str2hex(strlen(state->info->index), (char *)state->info->index);
|
|
1052
|
+
if (!index) {
|
|
1053
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
1054
|
+
goto cleanup;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
uint8_t *decrypt_ctr = calloc(AES_BLOCK_SIZE, sizeof(uint8_t));
|
|
1058
|
+
if (!decrypt_ctr) {
|
|
1059
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
1060
|
+
goto cleanup;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
memcpy(decrypt_ctr, index, AES_BLOCK_SIZE);
|
|
1064
|
+
state->decrypt_ctr = decrypt_ctr;
|
|
1065
|
+
|
|
1066
|
+
cleanup:
|
|
1067
|
+
if (file_key_as_str) {
|
|
1068
|
+
free(file_key_as_str);
|
|
1069
|
+
}
|
|
1070
|
+
if (index) {
|
|
1071
|
+
free(index);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
static void determine_decryption_key_v0(storj_download_state_t *state)
|
|
1076
|
+
{
|
|
1077
|
+
char *file_key = calloc(DETERMINISTIC_KEY_SIZE + 1, sizeof(char));
|
|
1078
|
+
if (!file_key) {
|
|
1079
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
if (generate_file_key(state->env->encrypt_options->mnemonic,
|
|
1084
|
+
state->bucket_id,
|
|
1085
|
+
state->file_id, &file_key)) {
|
|
1086
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
file_key[DETERMINISTIC_KEY_SIZE] = '\0';
|
|
1090
|
+
|
|
1091
|
+
uint8_t *decrypt_key = calloc(SHA256_DIGEST_SIZE + 1, sizeof(uint8_t));
|
|
1092
|
+
if (!decrypt_key) {
|
|
1093
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
sha256_of_str((uint8_t *)file_key, DETERMINISTIC_KEY_SIZE, decrypt_key);
|
|
1098
|
+
decrypt_key[SHA256_DIGEST_SIZE] = '\0';
|
|
1099
|
+
|
|
1100
|
+
memset_zero(file_key, DETERMINISTIC_KEY_SIZE + 1);
|
|
1101
|
+
free(file_key);
|
|
1102
|
+
|
|
1103
|
+
state->decrypt_key = decrypt_key;
|
|
1104
|
+
|
|
1105
|
+
uint8_t *file_id_hash = calloc(RIPEMD160_DIGEST_SIZE + 1, sizeof(uint8_t));
|
|
1106
|
+
if (!file_id_hash) {
|
|
1107
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
ripemd160_of_str((uint8_t *)state->file_id,
|
|
1111
|
+
strlen(state->file_id), file_id_hash);
|
|
1112
|
+
file_id_hash[RIPEMD160_DIGEST_SIZE] = '\0';
|
|
1113
|
+
|
|
1114
|
+
uint8_t *decrypt_ctr = calloc(AES_BLOCK_SIZE, sizeof(uint8_t));
|
|
1115
|
+
if (!decrypt_ctr) {
|
|
1116
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
memcpy(decrypt_ctr, file_id_hash, AES_BLOCK_SIZE);
|
|
1120
|
+
|
|
1121
|
+
free(file_id_hash);
|
|
1122
|
+
|
|
1123
|
+
state->decrypt_ctr = decrypt_ctr;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
static void determine_decryption_key(storj_download_state_t *state)
|
|
1127
|
+
{
|
|
1128
|
+
if (!state->env->encrypt_options ||
|
|
1129
|
+
!state->env->encrypt_options->mnemonic) {
|
|
1130
|
+
|
|
1131
|
+
state->decrypt_key = NULL;
|
|
1132
|
+
state->decrypt_ctr = NULL;
|
|
1133
|
+
} else {
|
|
1134
|
+
if (state->info->index) {
|
|
1135
|
+
// calculate decryption key based on the index
|
|
1136
|
+
determine_decryption_key_v1(state);
|
|
1137
|
+
} else {
|
|
1138
|
+
// calculate decryption key based on the file_id
|
|
1139
|
+
determine_decryption_key_v0(state);
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
static void after_request_info(uv_work_t *work, int status)
|
|
1145
|
+
{
|
|
1146
|
+
file_info_request_t *req = work->data;
|
|
1147
|
+
|
|
1148
|
+
req->state->pending_work_count--;
|
|
1149
|
+
req->state->requesting_info = false;
|
|
1150
|
+
|
|
1151
|
+
if (status != 0) {
|
|
1152
|
+
req->state->error_status = STORJ_BRIDGE_FILEINFO_ERROR;
|
|
1153
|
+
} else if (req->status_code == 200 || req->status_code == 304) {
|
|
1154
|
+
req->state->info = req->info;
|
|
1155
|
+
if (req->info->erasure) {
|
|
1156
|
+
if (strcmp(req->info->erasure, "reedsolomon") == 0) {
|
|
1157
|
+
req->state->rs = true;
|
|
1158
|
+
req->state->truncated = false;
|
|
1159
|
+
} else {
|
|
1160
|
+
req->state->error_status = STORJ_FILE_UNSUPPORTED_ERASURE;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// Now that we have info we can calculate the decryption key
|
|
1165
|
+
determine_decryption_key(req->state);
|
|
1166
|
+
|
|
1167
|
+
} else if (req->error_status) {
|
|
1168
|
+
switch(req->error_status) {
|
|
1169
|
+
case STORJ_BRIDGE_REQUEST_ERROR:
|
|
1170
|
+
case STORJ_BRIDGE_INTERNAL_ERROR:
|
|
1171
|
+
req->state->info_fail_count += 1;
|
|
1172
|
+
break;
|
|
1173
|
+
default:
|
|
1174
|
+
req->state->error_status = req->error_status;
|
|
1175
|
+
break;
|
|
1176
|
+
}
|
|
1177
|
+
if (req->state->info_fail_count >= STORJ_MAX_INFO_TRIES) {
|
|
1178
|
+
req->state->info_fail_count = 0;
|
|
1179
|
+
req->state->error_status = req->error_status;
|
|
1180
|
+
}
|
|
1181
|
+
} else {
|
|
1182
|
+
req->state->error_status = STORJ_BRIDGE_FILEINFO_ERROR;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
queue_next_work(req->state);
|
|
1186
|
+
|
|
1187
|
+
free(req);
|
|
1188
|
+
free(work);
|
|
1189
|
+
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
static void request_info(uv_work_t *work)
|
|
1193
|
+
{
|
|
1194
|
+
file_info_request_t *req = work->data;
|
|
1195
|
+
storj_download_state_t *state = req->state;
|
|
1196
|
+
|
|
1197
|
+
int path_len = 9 + strlen(req->bucket_id) + 7 + strlen(req->file_id) + 5;
|
|
1198
|
+
char *path = calloc(path_len + 1, sizeof(char));
|
|
1199
|
+
if (!path) {
|
|
1200
|
+
req->error_status = STORJ_MEMORY_ERROR;
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
strcat(path, "/buckets/");
|
|
1205
|
+
strcat(path, req->bucket_id);
|
|
1206
|
+
strcat(path, "/files/");
|
|
1207
|
+
strcat(path, req->file_id);
|
|
1208
|
+
strcat(path, "/info");
|
|
1209
|
+
|
|
1210
|
+
int status_code = 0;
|
|
1211
|
+
struct json_object *response = NULL;
|
|
1212
|
+
int request_status = fetch_json(req->http_options,
|
|
1213
|
+
req->options,
|
|
1214
|
+
"GET",
|
|
1215
|
+
path,
|
|
1216
|
+
NULL,
|
|
1217
|
+
true,
|
|
1218
|
+
&response,
|
|
1219
|
+
&status_code);
|
|
1220
|
+
|
|
1221
|
+
req->status_code = status_code;
|
|
1222
|
+
|
|
1223
|
+
state->log->debug(state->env->log_options,
|
|
1224
|
+
state->handle,
|
|
1225
|
+
"fn[request_info] - JSON Response: %s",
|
|
1226
|
+
json_object_to_json_string(response));
|
|
1227
|
+
|
|
1228
|
+
if (request_status) {
|
|
1229
|
+
req->error_status = STORJ_BRIDGE_REQUEST_ERROR;
|
|
1230
|
+
state->log->warn(state->env->log_options, state->handle,
|
|
1231
|
+
"Request file info error: %i", request_status);
|
|
1232
|
+
|
|
1233
|
+
} else if (status_code == 200 || status_code == 304) {
|
|
1234
|
+
|
|
1235
|
+
req->info = malloc(sizeof(storj_file_meta_t));
|
|
1236
|
+
req->info->created = NULL;
|
|
1237
|
+
req->info->filename = NULL;
|
|
1238
|
+
req->info->mimetype = NULL;
|
|
1239
|
+
req->info->erasure = NULL;
|
|
1240
|
+
req->info->size = 0;
|
|
1241
|
+
req->info->hmac = NULL;
|
|
1242
|
+
req->info->id = NULL;
|
|
1243
|
+
req->info->decrypted = false;
|
|
1244
|
+
req->info->index = NULL;
|
|
1245
|
+
|
|
1246
|
+
struct json_object *erasure_obj;
|
|
1247
|
+
struct json_object *erasure_value;
|
|
1248
|
+
char *erasure = NULL;
|
|
1249
|
+
if (json_object_object_get_ex(response, "erasure", &erasure_obj)) {
|
|
1250
|
+
if (json_object_object_get_ex(erasure_obj, "type", &erasure_value)) {
|
|
1251
|
+
erasure = (char *)json_object_get_string(erasure_value);
|
|
1252
|
+
} else {
|
|
1253
|
+
state->log->warn(state->env->log_options, state->handle,
|
|
1254
|
+
"value missing from erasure response");
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
if (erasure) {
|
|
1259
|
+
req->info->erasure = strdup(erasure);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
struct json_object *index_value;
|
|
1263
|
+
char *index = NULL;
|
|
1264
|
+
if (json_object_object_get_ex(response, "index", &index_value)) {
|
|
1265
|
+
index = (char *)json_object_get_string(index_value);
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
if (index) {
|
|
1269
|
+
req->info->index = strdup(index);
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
struct json_object *hmac_obj;
|
|
1273
|
+
if (!json_object_object_get_ex(response, "hmac", &hmac_obj)) {
|
|
1274
|
+
state->log->warn(state->env->log_options, state->handle,
|
|
1275
|
+
"hmac missing from response");
|
|
1276
|
+
goto clean_up;
|
|
1277
|
+
}
|
|
1278
|
+
if (!json_object_is_type(hmac_obj, json_type_object)) {
|
|
1279
|
+
state->log->warn(state->env->log_options, state->handle,
|
|
1280
|
+
"hmac not an object");
|
|
1281
|
+
goto clean_up;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// check the type of hmac
|
|
1285
|
+
struct json_object *hmac_type;
|
|
1286
|
+
if (!json_object_object_get_ex(hmac_obj, "type", &hmac_type)) {
|
|
1287
|
+
state->log->warn(state->env->log_options, state->handle,
|
|
1288
|
+
"hmac.type missing from response");
|
|
1289
|
+
goto clean_up;
|
|
1290
|
+
}
|
|
1291
|
+
if (!json_object_is_type(hmac_type, json_type_string)) {
|
|
1292
|
+
state->log->warn(state->env->log_options, state->handle,
|
|
1293
|
+
"hmac.type not a string");
|
|
1294
|
+
goto clean_up;
|
|
1295
|
+
}
|
|
1296
|
+
char *hmac_type_str = (char *)json_object_get_string(hmac_type);
|
|
1297
|
+
if (0 != strcmp(hmac_type_str, "sha512")) {
|
|
1298
|
+
state->log->warn(state->env->log_options, state->handle,
|
|
1299
|
+
"hmac.type is unknown");
|
|
1300
|
+
goto clean_up;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// get the hmac value
|
|
1304
|
+
struct json_object *hmac_value;
|
|
1305
|
+
if (!json_object_object_get_ex(hmac_obj, "value", &hmac_value)) {
|
|
1306
|
+
state->log->warn(state->env->log_options, state->handle,
|
|
1307
|
+
"hmac.value missing from response");
|
|
1308
|
+
goto clean_up;
|
|
1309
|
+
}
|
|
1310
|
+
if (!json_object_is_type(hmac_value, json_type_string)) {
|
|
1311
|
+
state->log->warn(state->env->log_options, state->handle,
|
|
1312
|
+
"hmac.value not a string");
|
|
1313
|
+
goto clean_up;
|
|
1314
|
+
}
|
|
1315
|
+
char *hmac = (char *)json_object_get_string(hmac_value);
|
|
1316
|
+
req->info->hmac = strdup(hmac);
|
|
1317
|
+
|
|
1318
|
+
} else if (status_code == 403 || status_code == 401) {
|
|
1319
|
+
req->error_status = STORJ_BRIDGE_AUTH_ERROR;
|
|
1320
|
+
} else if (status_code == 404 || status_code == 400) {
|
|
1321
|
+
req->error_status = STORJ_BRIDGE_FILE_NOTFOUND_ERROR;
|
|
1322
|
+
} else if (status_code == 500) {
|
|
1323
|
+
req->error_status = STORJ_BRIDGE_INTERNAL_ERROR;
|
|
1324
|
+
} else {
|
|
1325
|
+
req->error_status = STORJ_BRIDGE_REQUEST_ERROR;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
clean_up:
|
|
1329
|
+
if (response) {
|
|
1330
|
+
json_object_put(response);
|
|
1331
|
+
}
|
|
1332
|
+
free(path);
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
static void queue_request_info(storj_download_state_t *state)
|
|
1336
|
+
{
|
|
1337
|
+
if (state->requesting_info || state->canceled) {
|
|
1338
|
+
return;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
uv_work_t *work = malloc(sizeof(uv_work_t));
|
|
1342
|
+
if (!work) {
|
|
1343
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
state->requesting_info = true;
|
|
1348
|
+
|
|
1349
|
+
file_info_request_t *req = malloc(sizeof(file_info_request_t));
|
|
1350
|
+
req->http_options = state->env->http_options;
|
|
1351
|
+
req->options = state->env->bridge_options;
|
|
1352
|
+
req->status_code = 0;
|
|
1353
|
+
req->bucket_id = state->bucket_id;
|
|
1354
|
+
req->file_id = state->file_id;
|
|
1355
|
+
req->error_status = 0;
|
|
1356
|
+
req->info = NULL;
|
|
1357
|
+
req->state = state;
|
|
1358
|
+
|
|
1359
|
+
work->data = req;
|
|
1360
|
+
|
|
1361
|
+
state->pending_work_count++;
|
|
1362
|
+
int status = uv_queue_work(state->env->loop, (uv_work_t*) work,
|
|
1363
|
+
request_info,
|
|
1364
|
+
after_request_info);
|
|
1365
|
+
if (status) {
|
|
1366
|
+
state->error_status = STORJ_QUEUE_ERROR;
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
static int prepare_file_hmac(storj_download_state_t *state)
|
|
1373
|
+
{
|
|
1374
|
+
// initialize the hmac with the decrypt key
|
|
1375
|
+
struct hmac_sha512_ctx hmac_ctx;
|
|
1376
|
+
hmac_sha512_set_key(&hmac_ctx, SHA256_DIGEST_SIZE, state->decrypt_key);
|
|
1377
|
+
|
|
1378
|
+
for (int i = 0; i < state->total_pointers; i++) {
|
|
1379
|
+
|
|
1380
|
+
storj_pointer_t *pointer = &state->pointers[i];
|
|
1381
|
+
|
|
1382
|
+
if (!pointer->shard_hash ||
|
|
1383
|
+
strlen(pointer->shard_hash) != RIPEMD160_DIGEST_SIZE * 2) {
|
|
1384
|
+
return 1;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
struct base16_decode_ctx base16_ctx;
|
|
1388
|
+
base16_decode_init(&base16_ctx);
|
|
1389
|
+
|
|
1390
|
+
size_t decode_len = 0;
|
|
1391
|
+
uint8_t hash[RIPEMD160_DIGEST_SIZE];
|
|
1392
|
+
if (!base16_decode_update(&base16_ctx,
|
|
1393
|
+
&decode_len,
|
|
1394
|
+
hash,
|
|
1395
|
+
RIPEMD160_DIGEST_SIZE * 2,
|
|
1396
|
+
(uint8_t *)pointer->shard_hash)) {
|
|
1397
|
+
return 1;
|
|
1398
|
+
|
|
1399
|
+
}
|
|
1400
|
+
if (!base16_decode_final(&base16_ctx) ||
|
|
1401
|
+
decode_len != RIPEMD160_DIGEST_SIZE) {
|
|
1402
|
+
return 1;
|
|
1403
|
+
}
|
|
1404
|
+
hmac_sha512_update(&hmac_ctx, RIPEMD160_DIGEST_SIZE, hash);
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
uint8_t digest_raw[SHA512_DIGEST_SIZE];
|
|
1408
|
+
hmac_sha512_digest(&hmac_ctx, SHA512_DIGEST_SIZE, digest_raw);
|
|
1409
|
+
|
|
1410
|
+
size_t digest_len = BASE16_ENCODE_LENGTH(SHA512_DIGEST_SIZE);
|
|
1411
|
+
state->hmac = calloc(digest_len + 1, sizeof(char));
|
|
1412
|
+
if (!state->hmac) {
|
|
1413
|
+
return 1;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
base16_encode_update((uint8_t *)state->hmac, SHA512_DIGEST_SIZE, digest_raw);
|
|
1417
|
+
|
|
1418
|
+
return 0;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
static bool has_missing_shard(storj_download_state_t *state)
|
|
1422
|
+
{
|
|
1423
|
+
bool missing = false;
|
|
1424
|
+
for (int i = 0; i < state->total_pointers; i++) {
|
|
1425
|
+
storj_pointer_t *pointer = &state->pointers[i];
|
|
1426
|
+
if (pointer->status == POINTER_MISSING) {
|
|
1427
|
+
missing = true;
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
return missing;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
static bool can_recover_shards(storj_download_state_t *state)
|
|
1434
|
+
{
|
|
1435
|
+
if (state->pointers_completed) {
|
|
1436
|
+
uint32_t missing_pointers = 0;
|
|
1437
|
+
|
|
1438
|
+
for (int i = 0; i < state->total_pointers; i++) {
|
|
1439
|
+
storj_pointer_t *pointer = &state->pointers[i];
|
|
1440
|
+
if (pointer->status == POINTER_MISSING) {
|
|
1441
|
+
missing_pointers += 1;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
if (missing_pointers > state->total_parity_pointers) {
|
|
1446
|
+
return false;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
return true;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
static void after_recover_shards(uv_work_t *work, int status)
|
|
1454
|
+
{
|
|
1455
|
+
file_request_recover_t *req = work->data;
|
|
1456
|
+
storj_download_state_t *state = req->state;
|
|
1457
|
+
|
|
1458
|
+
state->pending_work_count--;
|
|
1459
|
+
state->recovering_shards = false;
|
|
1460
|
+
state->truncated = true;
|
|
1461
|
+
|
|
1462
|
+
if (status != 0) {
|
|
1463
|
+
req->state->error_status = STORJ_QUEUE_ERROR;
|
|
1464
|
+
} else if (req->error_status) {
|
|
1465
|
+
req->state->error_status = req->error_status;
|
|
1466
|
+
} else {
|
|
1467
|
+
// Recovery was successful and the pointers have been finished
|
|
1468
|
+
for (int i = 0; i < state->total_pointers; i++) {
|
|
1469
|
+
state->pointers[i].status = POINTER_FINISHED;
|
|
1470
|
+
state->completed_shards += 1;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
queue_next_work(state);
|
|
1475
|
+
|
|
1476
|
+
memset_zero(req->decrypt_key, SHA256_DIGEST_SIZE);
|
|
1477
|
+
free(req->decrypt_key);
|
|
1478
|
+
|
|
1479
|
+
memset_zero(req->decrypt_ctr, AES_BLOCK_SIZE);
|
|
1480
|
+
free(req->decrypt_ctr);
|
|
1481
|
+
|
|
1482
|
+
free(req->zilch);
|
|
1483
|
+
free(req);
|
|
1484
|
+
free(work);
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
static void recover_shards(uv_work_t *work)
|
|
1488
|
+
{
|
|
1489
|
+
file_request_recover_t *req = work->data;
|
|
1490
|
+
storj_download_state_t *state = req->state;
|
|
1491
|
+
reed_solomon* rs = NULL;
|
|
1492
|
+
uint8_t *data_map = NULL;
|
|
1493
|
+
uint8_t **data_blocks = NULL;
|
|
1494
|
+
uint8_t **fec_blocks = NULL;
|
|
1495
|
+
|
|
1496
|
+
int error = 0;
|
|
1497
|
+
|
|
1498
|
+
struct aes256_ctx ctx;
|
|
1499
|
+
uint64_t bytes_decrypted = 0;
|
|
1500
|
+
size_t len = AES_BLOCK_SIZE * 8;
|
|
1501
|
+
|
|
1502
|
+
// Make sure that the file is the correct size before recovering
|
|
1503
|
+
// shards in case that the last shard is the one being recovered.
|
|
1504
|
+
#ifdef _WIN32
|
|
1505
|
+
|
|
1506
|
+
HANDLE prefile = (HANDLE)_get_osfhandle(req->fd);
|
|
1507
|
+
if (prefile == INVALID_HANDLE_VALUE) {
|
|
1508
|
+
req->error_status = STORJ_FILE_RESIZE_ERROR;
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
LARGE_INTEGER presize;
|
|
1513
|
+
presize.HighPart = (uint32_t)((req->filesize & 0xFFFFFFFF00000000LL) >> 32);
|
|
1514
|
+
presize.LowPart = (uint32_t)(req->filesize & 0xFFFFFFFFLL);
|
|
1515
|
+
|
|
1516
|
+
if (!SetFilePointerEx(prefile, presize, 0, FILE_BEGIN)) {
|
|
1517
|
+
req->error_status = STORJ_FILE_RESIZE_ERROR;
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
if (!SetEndOfFile(prefile)) {
|
|
1522
|
+
req->error_status = STORJ_FILE_RESIZE_ERROR;
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
#else
|
|
1527
|
+
if (ftruncate(req->fd, req->filesize)) {
|
|
1528
|
+
// errno for more details
|
|
1529
|
+
req->error_status = STORJ_FILE_RESIZE_ERROR;
|
|
1530
|
+
}
|
|
1531
|
+
#endif
|
|
1532
|
+
|
|
1533
|
+
error = map_file(req->fd, req->filesize, &data_map, false);
|
|
1534
|
+
if (error) {
|
|
1535
|
+
req->error_status = STORJ_MAPPING_ERROR;
|
|
1536
|
+
goto finish;
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
if (!req->has_missing) {
|
|
1540
|
+
goto decrypt;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
fec_init();
|
|
1544
|
+
|
|
1545
|
+
rs = reed_solomon_new(req->data_shards, req->parity_shards);
|
|
1546
|
+
if (!rs) {
|
|
1547
|
+
req->error_status = STORJ_MEMORY_ERROR;
|
|
1548
|
+
goto finish;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
data_blocks = (uint8_t**)malloc(req->data_shards * sizeof(uint8_t *));
|
|
1552
|
+
if (!data_blocks) {
|
|
1553
|
+
req->error_status = STORJ_MEMORY_ERROR;
|
|
1554
|
+
goto finish;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
fec_blocks = (uint8_t**)malloc(req->parity_shards * sizeof(uint8_t *));
|
|
1558
|
+
if (!fec_blocks) {
|
|
1559
|
+
req->error_status = STORJ_MEMORY_ERROR;
|
|
1560
|
+
goto finish;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
for (int i = 0; i < req->data_shards; i++) {
|
|
1564
|
+
data_blocks[i] = data_map + i * req->shard_size;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
for (int i = 0; i < req->parity_shards; i++) {
|
|
1568
|
+
fec_blocks[i] = data_map + (req->data_shards + i) * req->shard_size;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
uint32_t total_shards = req->data_shards + req->parity_shards;
|
|
1572
|
+
|
|
1573
|
+
state->log->debug(state->env->log_options, state->handle,
|
|
1574
|
+
"Recovering shards, data_shards: %i, " \
|
|
1575
|
+
"parity_shards: %i, shard_size: %" PRIu64 ", " \
|
|
1576
|
+
"file_size: %" PRIu64,
|
|
1577
|
+
req->data_shards,
|
|
1578
|
+
req->parity_shards,
|
|
1579
|
+
req->shard_size,
|
|
1580
|
+
req->data_filesize);
|
|
1581
|
+
|
|
1582
|
+
error = reed_solomon_reconstruct(rs, data_blocks, fec_blocks,
|
|
1583
|
+
req->zilch, total_shards,
|
|
1584
|
+
req->shard_size, req->data_filesize);
|
|
1585
|
+
|
|
1586
|
+
if (error) {
|
|
1587
|
+
req->error_status = STORJ_FILE_RECOVER_ERROR;
|
|
1588
|
+
goto finish;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
|
|
1592
|
+
decrypt:
|
|
1593
|
+
|
|
1594
|
+
aes256_set_encrypt_key(&ctx, req->decrypt_key);
|
|
1595
|
+
|
|
1596
|
+
while (bytes_decrypted < req->data_filesize) {
|
|
1597
|
+
|
|
1598
|
+
if (bytes_decrypted + len > req->data_filesize) {
|
|
1599
|
+
len = req->data_filesize - bytes_decrypted;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
ctr_crypt(&ctx, (nettle_cipher_func *)aes256_encrypt,
|
|
1603
|
+
AES_BLOCK_SIZE, req->decrypt_ctr,
|
|
1604
|
+
len,
|
|
1605
|
+
(uint8_t *)data_map + bytes_decrypted,
|
|
1606
|
+
(uint8_t *)data_map + bytes_decrypted);
|
|
1607
|
+
|
|
1608
|
+
bytes_decrypted += len;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
finish:
|
|
1612
|
+
if (data_map) {
|
|
1613
|
+
error = unmap_file(data_map, req->filesize);
|
|
1614
|
+
if (error) {
|
|
1615
|
+
req->error_status = STORJ_UNMAPPING_ERROR;
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
if (data_blocks) {
|
|
1620
|
+
free(data_blocks);
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
if (fec_blocks) {
|
|
1624
|
+
free(fec_blocks);
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
if (rs) {
|
|
1628
|
+
reed_solomon_release(rs);
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
#ifdef _WIN32
|
|
1632
|
+
|
|
1633
|
+
HANDLE file = (HANDLE)_get_osfhandle(req->fd);
|
|
1634
|
+
if (file == INVALID_HANDLE_VALUE) {
|
|
1635
|
+
req->error_status = STORJ_FILE_RESIZE_ERROR;
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
LARGE_INTEGER size;
|
|
1640
|
+
size.HighPart = (uint32_t)((req->data_filesize & 0xFFFFFFFF00000000LL) >> 32);
|
|
1641
|
+
size.LowPart = (uint32_t)(req->data_filesize & 0xFFFFFFFFLL);
|
|
1642
|
+
|
|
1643
|
+
if (!SetFilePointerEx(file, size, 0, FILE_BEGIN)) {
|
|
1644
|
+
req->error_status = STORJ_FILE_RESIZE_ERROR;
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
if (!SetEndOfFile(file)) {
|
|
1649
|
+
req->error_status = STORJ_FILE_RESIZE_ERROR;
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
#else
|
|
1654
|
+
if (ftruncate(req->fd, req->data_filesize)) {
|
|
1655
|
+
// errno for more details
|
|
1656
|
+
req->error_status = STORJ_FILE_RESIZE_ERROR;
|
|
1657
|
+
}
|
|
1658
|
+
#endif
|
|
1659
|
+
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
static void queue_recover_shards(storj_download_state_t *state)
|
|
1663
|
+
{
|
|
1664
|
+
if (!state->recovering_shards && state->pointers_completed) {
|
|
1665
|
+
|
|
1666
|
+
int total_missing = 0;
|
|
1667
|
+
bool has_missing = false;
|
|
1668
|
+
bool is_ready = true;
|
|
1669
|
+
|
|
1670
|
+
uint8_t *zilch = (uint8_t *)calloc(1, state->total_pointers);
|
|
1671
|
+
|
|
1672
|
+
for (int i = 0; i < state->total_pointers; i++) {
|
|
1673
|
+
storj_pointer_t *pointer = &state->pointers[i];
|
|
1674
|
+
if (pointer->status == POINTER_MISSING) {
|
|
1675
|
+
total_missing += 1;
|
|
1676
|
+
has_missing = true;
|
|
1677
|
+
zilch[i] = 1;
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
if (pointer->status != POINTER_MISSING &&
|
|
1681
|
+
pointer->status != POINTER_DOWNLOADED) {
|
|
1682
|
+
is_ready = false;
|
|
1683
|
+
state->log->debug(state->env->log_options,
|
|
1684
|
+
state->handle,
|
|
1685
|
+
"Pointer %i not ready with status: %i",
|
|
1686
|
+
i, pointer->status);
|
|
1687
|
+
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
if (!is_ready) {
|
|
1692
|
+
free(zilch);
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
state->log->info(state->env->log_options,
|
|
1697
|
+
state->handle,
|
|
1698
|
+
"Queuing recovery of %i of %i shards",
|
|
1699
|
+
total_missing, state->total_shards);
|
|
1700
|
+
|
|
1701
|
+
file_request_recover_t *req = malloc(sizeof(file_request_recover_t));
|
|
1702
|
+
if (!req) {
|
|
1703
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
uv_work_t *work = malloc(sizeof(uv_work_t));
|
|
1708
|
+
if (!work) {
|
|
1709
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
req->fd = fileno(state->destination);
|
|
1714
|
+
req->filesize = state->shard_size * state->total_pointers;
|
|
1715
|
+
req->data_filesize = calculate_data_filesize(state);
|
|
1716
|
+
req->data_shards = state->total_pointers - state->total_parity_pointers;
|
|
1717
|
+
req->parity_shards = state->total_parity_pointers;
|
|
1718
|
+
req->shard_size = state->shard_size;
|
|
1719
|
+
req->zilch = zilch;
|
|
1720
|
+
req->has_missing = has_missing;
|
|
1721
|
+
|
|
1722
|
+
if (state->decrypt_key && state->decrypt_ctr) {
|
|
1723
|
+
req->decrypt_key = calloc(SHA256_DIGEST_SIZE, sizeof(uint8_t));
|
|
1724
|
+
if (!req->decrypt_key) {
|
|
1725
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
1726
|
+
return;
|
|
1727
|
+
}
|
|
1728
|
+
req->decrypt_ctr = calloc(AES_BLOCK_SIZE, sizeof(uint8_t));
|
|
1729
|
+
if (!req->decrypt_ctr) {
|
|
1730
|
+
state->error_status = STORJ_MEMORY_ERROR;
|
|
1731
|
+
return;
|
|
1732
|
+
}
|
|
1733
|
+
memcpy(req->decrypt_key, state->decrypt_key, SHA256_DIGEST_SIZE);
|
|
1734
|
+
memcpy(req->decrypt_ctr, state->decrypt_ctr, AES_BLOCK_SIZE);
|
|
1735
|
+
|
|
1736
|
+
increment_ctr_aes_iv(req->decrypt_ctr, 0);
|
|
1737
|
+
} else {
|
|
1738
|
+
req->decrypt_key = NULL;
|
|
1739
|
+
req->decrypt_ctr = NULL;
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
req->state = state;
|
|
1743
|
+
req->error_status = 0;
|
|
1744
|
+
|
|
1745
|
+
work->data = req;
|
|
1746
|
+
|
|
1747
|
+
state->pending_work_count++;
|
|
1748
|
+
int status = uv_queue_work(state->env->loop, (uv_work_t*) work,
|
|
1749
|
+
recover_shards, after_recover_shards);
|
|
1750
|
+
|
|
1751
|
+
if (status) {
|
|
1752
|
+
state->error_status = STORJ_QUEUE_ERROR;
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
state->recovering_shards = true;
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
static void queue_next_work(storj_download_state_t *state)
|
|
1761
|
+
{
|
|
1762
|
+
// report any errors
|
|
1763
|
+
if (state->error_status != 0) {
|
|
1764
|
+
if (!state->finished && state->pending_work_count == 0) {
|
|
1765
|
+
|
|
1766
|
+
state->finished = true;
|
|
1767
|
+
state->finished_cb(state->error_status,
|
|
1768
|
+
state->destination,
|
|
1769
|
+
state->handle);
|
|
1770
|
+
|
|
1771
|
+
free_download_state(state);
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
// report download complete
|
|
1778
|
+
if (state->pointers_completed &&
|
|
1779
|
+
state->completed_shards == state->total_shards &&
|
|
1780
|
+
state->truncated) {
|
|
1781
|
+
|
|
1782
|
+
if (!state->finished && state->pending_work_count == 0) {
|
|
1783
|
+
|
|
1784
|
+
// calculate the hmac of all shard hashes
|
|
1785
|
+
if (prepare_file_hmac(state)) {
|
|
1786
|
+
state->error_status = STORJ_FILE_GENERATE_HMAC_ERROR;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
if (state->info && state->info->hmac) {
|
|
1790
|
+
if (0 != strcmp(state->info->hmac, state->hmac)) {
|
|
1791
|
+
state->error_status = STORJ_FILE_DECRYPTION_ERROR;
|
|
1792
|
+
}
|
|
1793
|
+
} else {
|
|
1794
|
+
state->log->warn(state->env->log_options,
|
|
1795
|
+
state->handle,
|
|
1796
|
+
"Unable to verify decryption integrity" \
|
|
1797
|
+
", missing hmac from file info.");
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
state->finished = true;
|
|
1801
|
+
state->finished_cb(state->error_status, state->destination, state->handle);
|
|
1802
|
+
|
|
1803
|
+
free_download_state(state);
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
goto finish_up;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
queue_request_pointers(state);
|
|
1811
|
+
|
|
1812
|
+
if (!state->info) {
|
|
1813
|
+
queue_request_info(state);
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
if (state->info) {
|
|
1817
|
+
queue_request_shards(state);
|
|
1818
|
+
|
|
1819
|
+
if (state->rs) {
|
|
1820
|
+
if (can_recover_shards(state)) {
|
|
1821
|
+
queue_recover_shards(state);
|
|
1822
|
+
} else {
|
|
1823
|
+
state->error_status = STORJ_FILE_SHARD_MISSING_ERROR;
|
|
1824
|
+
queue_next_work(state);
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
} else {
|
|
1828
|
+
if (!has_missing_shard(state)) {
|
|
1829
|
+
queue_recover_shards(state);
|
|
1830
|
+
} else {
|
|
1831
|
+
state->error_status = STORJ_FILE_SHARD_MISSING_ERROR;
|
|
1832
|
+
queue_next_work(state);
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
queue_send_exchange_reports(state);
|
|
1839
|
+
|
|
1840
|
+
finish_up:
|
|
1841
|
+
|
|
1842
|
+
state->log->debug(state->env->log_options, state->handle,
|
|
1843
|
+
"Pending work count: %d", state->pending_work_count);
|
|
1844
|
+
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
STORJ_API int storj_bridge_resolve_file_cancel(storj_download_state_t *state)
|
|
1848
|
+
{
|
|
1849
|
+
if (state->canceled) {
|
|
1850
|
+
return 0;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
state->canceled = true;
|
|
1854
|
+
state->error_status = STORJ_TRANSFER_CANCELED;
|
|
1855
|
+
|
|
1856
|
+
// loop over all pointers, and cancel any that are queued to be downloaded
|
|
1857
|
+
// any downloads that are in-progress will monitor the state->canceled
|
|
1858
|
+
// status and exit when set to true
|
|
1859
|
+
for (int i = 0; i < state->total_pointers; i++) {
|
|
1860
|
+
storj_pointer_t *pointer = &state->pointers[i];
|
|
1861
|
+
if (pointer->status == POINTER_BEING_DOWNLOADED) {
|
|
1862
|
+
uv_cancel((uv_req_t *)pointer->work);
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
return 0;
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
STORJ_API storj_download_state_t *storj_bridge_resolve_file(storj_env_t *env,
|
|
1870
|
+
const char *bucket_id,
|
|
1871
|
+
const char *file_id,
|
|
1872
|
+
FILE *destination,
|
|
1873
|
+
void *handle,
|
|
1874
|
+
storj_progress_cb progress_cb,
|
|
1875
|
+
storj_finished_download_cb finished_cb)
|
|
1876
|
+
{
|
|
1877
|
+
storj_download_state_t *state = malloc(sizeof(storj_download_state_t));
|
|
1878
|
+
if (!state) {
|
|
1879
|
+
return NULL;
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
// setup download state
|
|
1883
|
+
state->total_bytes = 0;
|
|
1884
|
+
state->info = NULL;
|
|
1885
|
+
state->requesting_info = false;
|
|
1886
|
+
state->info_fail_count = 0;
|
|
1887
|
+
state->env = env;
|
|
1888
|
+
state->file_id = file_id;
|
|
1889
|
+
state->bucket_id = bucket_id;
|
|
1890
|
+
state->destination = destination;
|
|
1891
|
+
state->progress_cb = progress_cb;
|
|
1892
|
+
state->finished_cb = finished_cb;
|
|
1893
|
+
state->finished = false;
|
|
1894
|
+
state->total_shards = 0;
|
|
1895
|
+
state->download_max_concurrency = STORJ_DOWNLOAD_CONCURRENCY;
|
|
1896
|
+
state->completed_shards = 0;
|
|
1897
|
+
state->resolving_shards = 0;
|
|
1898
|
+
state->total_pointers = 0;
|
|
1899
|
+
state->total_parity_pointers = 0;
|
|
1900
|
+
state->rs = false;
|
|
1901
|
+
state->recovering_shards = false;
|
|
1902
|
+
state->truncated = true;
|
|
1903
|
+
state->pointers = NULL;
|
|
1904
|
+
state->pointers_completed = false;
|
|
1905
|
+
state->pointer_fail_count = 0;
|
|
1906
|
+
state->requesting_pointers = false;
|
|
1907
|
+
state->error_status = STORJ_TRANSFER_OK;
|
|
1908
|
+
state->writing = false;
|
|
1909
|
+
state->shard_size = 0;
|
|
1910
|
+
state->excluded_farmer_ids = NULL;
|
|
1911
|
+
state->hmac = NULL;
|
|
1912
|
+
state->pending_work_count = 0;
|
|
1913
|
+
state->canceled = false;
|
|
1914
|
+
state->log = env->log;
|
|
1915
|
+
state->handle = handle;
|
|
1916
|
+
state->decrypt_key = NULL;
|
|
1917
|
+
state->decrypt_ctr = NULL;
|
|
1918
|
+
|
|
1919
|
+
// start download
|
|
1920
|
+
queue_next_work(state);
|
|
1921
|
+
|
|
1922
|
+
return state;
|
|
1923
|
+
}
|