1
2 /*
3 * s3backer - FUSE-based single file backing store via Amazon S3
4 *
5 * Copyright 2008-2011 Archie L. Cobbs <archie@dellroad.org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301, USA.
21 *
22 * In addition, as a special exception, the copyright holders give
23 * permission to link the code of portions of this program with the
24 * OpenSSL library under certain conditions as described in each
25 * individual source file, and distribute linked combinations including
26 * the two.
27 *
28 * You must obey the GNU General Public License in all respects for all
29 * of the code used other than OpenSSL. If you modify file(s) with this
30 * exception, you may extend this exception to your version of the
31 * file(s), but you are not obligated to do so. If you do not wish to do
32 * so, delete this exception statement from your version. If you delete
33 * this exception statement from all source files in the program, then
34 * also delete it here.
35 */
36
37 #include "s3backer.h"
38 #include "http_io.h"
39 #include "block_part.h"
40 #include "test_io.h"
41
42 /* Do we want random errors? */
43 #define RANDOM_ERROR_PERCENT 0
44
45 /* Internal state */
46 struct test_io_private {
47 struct http_io_conf *config;
48 u_char zero_block[0];
49 };
50
51 /* s3backer_store functions */
52 static int test_io_create_threads(struct s3backer_store *s3b);
53 static int test_io_meta_data(struct s3backer_store *s3b, off_t *file_sizep, u_int *block_sizep);
54 static int test_io_set_mount_token(struct s3backer_store *s3b, int32_t *old_valuep, int32_t new_value);
55 static int test_io_read_block(struct s3backer_store *s3b, s3b_block_t block_num, void *dest,
56 u_char *actual_md5, const u_char *expect_md5, int strict);
57 static int test_io_write_block(struct s3backer_store *s3b, s3b_block_t block_num, const void *src, u_char *md5,
58 check_cancel_t *check_cancel, void *check_cancel_arg);
59 static int test_io_read_block_part(struct s3backer_store *s3b, s3b_block_t block_num, u_int off, u_int len, void *dest);
60 static int test_io_write_block_part(struct s3backer_store *s3b, s3b_block_t block_num, u_int off, u_int len, const void *src);
61 static int test_io_list_blocks(struct s3backer_store *s3b, block_list_func_t *callback, void *arg);
62 static int test_io_flush(struct s3backer_store *s3b);
63 static void test_io_destroy(struct s3backer_store *s3b);
64
65 /*
66 * Constructor
67 *
68 * On error, returns NULL and sets `errno'.
69 */
70 struct s3backer_store *
test_io_create(struct http_io_conf * config)71 test_io_create(struct http_io_conf *config)
72 {
73 struct s3backer_store *s3b;
74 struct test_io_private *priv;
75
76 /* Initialize structures */
77 if ((s3b = calloc(1, sizeof(*s3b))) == NULL)
78 return NULL;
79 s3b->create_threads = test_io_create_threads;
80 s3b->meta_data = test_io_meta_data;
81 s3b->set_mount_token = test_io_set_mount_token;
82 s3b->read_block = test_io_read_block;
83 s3b->write_block = test_io_write_block;
84 s3b->read_block_part = test_io_read_block_part;
85 s3b->write_block_part = test_io_write_block_part;
86 s3b->list_blocks = test_io_list_blocks;
87 s3b->flush = test_io_flush;
88 s3b->destroy = test_io_destroy;
89 if ((priv = calloc(1, sizeof(*priv) + config->block_size)) == NULL) {
90 free(s3b);
91 errno = ENOMEM;
92 return NULL;
93 }
94 priv->config = config;
95 s3b->data = priv;
96
97 /* Random initialization */
98 srandom((u_int)time(NULL));
99
100 /* Done */
101 return s3b;
102 }
103
104 static int
test_io_create_threads(struct s3backer_store * s3b)105 test_io_create_threads(struct s3backer_store *s3b)
106 {
107 return 0;
108 }
109
110 static int
test_io_meta_data(struct s3backer_store * s3b,off_t * file_sizep,u_int * block_sizep)111 test_io_meta_data(struct s3backer_store *s3b, off_t *file_sizep, u_int *block_sizep)
112 {
113 return 0;
114 }
115
116 static int
test_io_set_mount_token(struct s3backer_store * s3b,int32_t * old_valuep,int32_t new_value)117 test_io_set_mount_token(struct s3backer_store *s3b, int32_t *old_valuep, int32_t new_value)
118 {
119 if (old_valuep != NULL)
120 *old_valuep = 0;
121 return 0;
122 }
123
124 static int
test_io_flush(struct s3backer_store * const s3b)125 test_io_flush(struct s3backer_store *const s3b)
126 {
127 return 0;
128 }
129
130 static void
test_io_destroy(struct s3backer_store * const s3b)131 test_io_destroy(struct s3backer_store *const s3b)
132 {
133 struct test_io_private *const priv = s3b->data;
134
135 free(priv);
136 free(s3b);
137 }
138
139 static int
test_io_read_block(struct s3backer_store * const s3b,s3b_block_t block_num,void * dest,u_char * actual_md5,const u_char * expect_md5,int strict)140 test_io_read_block(struct s3backer_store *const s3b, s3b_block_t block_num, void *dest,
141 u_char *actual_md5, const u_char *expect_md5, int strict)
142 {
143 struct test_io_private *const priv = s3b->data;
144 struct http_io_conf *const config = priv->config;
145 u_char md5[MD5_DIGEST_LENGTH];
146 char path[PATH_MAX];
147 int zero_block;
148 MD5_CTX ctx;
149 int fd;
150 int r;
151
152 /* Logging */
153 if (config->debug)
154 (*config->log)(LOG_DEBUG, "test_io: read %0*jx started", S3B_BLOCK_NUM_DIGITS, (uintmax_t)block_num);
155
156 /* Random delay */
157 usleep((random() % 200) * 1000);
158
159 /* Random error */
160 if ((random() % 100) < RANDOM_ERROR_PERCENT) {
161 (*config->log)(LOG_ERR, "test_io: random failure reading %0*jx", S3B_BLOCK_NUM_DIGITS, (uintmax_t)block_num);
162 return EAGAIN;
163 }
164
165 /* Generate path */
166 snprintf(path, sizeof(path), "%s/%s%0*jx", config->bucket, config->prefix, S3B_BLOCK_NUM_DIGITS, (uintmax_t)block_num);
167
168 /* Read block */
169 if ((fd = open(path, O_RDONLY)) != -1) {
170 int total;
171
172 /* Read file */
173 for (total = 0; total < config->block_size; total += r) {
174 if ((r = read(fd, (char *)dest + total, config->block_size - total)) == -1) {
175 r = errno;
176 (*config->log)(LOG_ERR, "can't read %s: %s", path, strerror(r));
177 close(fd);
178 return r;
179 }
180 if (r == 0)
181 break;
182 }
183 close(fd);
184
185 /* Check for short read */
186 if (total != config->block_size) {
187 (*config->log)(LOG_ERR, "%s: file is truncated (only read %d out of %u bytes)", path, total, config->block_size);
188 return EIO;
189 }
190
191 /* Done */
192 r = 0;
193 } else
194 r = errno;
195
196 /* Convert ENOENT into a read of all zeroes */
197 if ((zero_block = (r == ENOENT))) {
198 memset(dest, 0, config->block_size);
199 r = 0;
200 }
201
202 /* Check for other error */
203 if (r != 0) {
204 (*config->log)(LOG_ERR, "can't open %s: %s", path, strerror(r));
205 return r;
206 }
207
208 /* Compute MD5 */
209 if (zero_block)
210 memset(md5, 0, MD5_DIGEST_LENGTH);
211 else {
212 MD5_Init(&ctx);
213 MD5_Update(&ctx, dest, config->block_size);
214 MD5_Final(md5, &ctx);
215 }
216 if (actual_md5 != NULL)
217 memcpy(actual_md5, md5, MD5_DIGEST_LENGTH);
218
219 /* Check expected MD5 */
220 if (expect_md5 != NULL) {
221 const int match = memcmp(md5, expect_md5, MD5_DIGEST_LENGTH) == 0;
222
223 if (strict) {
224 if (!match) {
225 (*config->log)(LOG_ERR,
226 "%s: wrong MD5 checksum?! %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
227 " != %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", path,
228 (u_int)md5[0], (u_int)md5[1], (u_int)md5[2], (u_int)md5[3],
229 (u_int)md5[4], (u_int)md5[5], (u_int)md5[6], (u_int)md5[7],
230 (u_int)md5[8], (u_int)md5[9], (u_int)md5[10], (u_int)md5[11],
231 (u_int)md5[12], (u_int)md5[13], (u_int)md5[14], (u_int)md5[15],
232 (u_int)expect_md5[0], (u_int)expect_md5[1], (u_int)expect_md5[2], (u_int)expect_md5[3],
233 (u_int)expect_md5[4], (u_int)expect_md5[5], (u_int)expect_md5[6], (u_int)expect_md5[7],
234 (u_int)expect_md5[8], (u_int)expect_md5[9], (u_int)expect_md5[10], (u_int)expect_md5[11],
235 (u_int)expect_md5[12], (u_int)expect_md5[13], (u_int)expect_md5[14], (u_int)expect_md5[15]);
236 return EINVAL;
237 }
238 } else if (match)
239 r = EEXIST;
240 }
241
242 /* Logging */
243 if (config->debug) {
244 (*config->log)(LOG_DEBUG,
245 "test_io: read %0*jx complete, MD5 %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%s%s",
246 S3B_BLOCK_NUM_DIGITS, (uintmax_t)block_num,
247 (u_int)md5[0], (u_int)md5[1], (u_int)md5[2], (u_int)md5[3],
248 (u_int)md5[4], (u_int)md5[5], (u_int)md5[6], (u_int)md5[7],
249 (u_int)md5[8], (u_int)md5[9], (u_int)md5[10], (u_int)md5[11],
250 (u_int)md5[12], (u_int)md5[13], (u_int)md5[14], (u_int)md5[15],
251 zero_block ? " (zero)" : "", r == EEXIST ? " (expected md5 match)" : "");
252 }
253
254 /* Done */
255 return r;
256 }
257
258 static int
test_io_write_block(struct s3backer_store * const s3b,s3b_block_t block_num,const void * src,u_char * caller_md5,check_cancel_t * check_cancel,void * check_cancel_arg)259 test_io_write_block(struct s3backer_store *const s3b, s3b_block_t block_num, const void *src, u_char *caller_md5,
260 check_cancel_t *check_cancel, void *check_cancel_arg)
261 {
262 struct test_io_private *const priv = s3b->data;
263 struct http_io_conf *const config = priv->config;
264 char block_hash_buf[S3B_BLOCK_NUM_DIGITS + 2];
265 u_char md5[MD5_DIGEST_LENGTH];
266 char temp[PATH_MAX];
267 char path[PATH_MAX];
268 MD5_CTX ctx;
269 int total;
270 int fd;
271 int r;
272
273 /* Check for zero block */
274 if (src != NULL && memcmp(src, priv->zero_block, config->block_size) == 0)
275 src = NULL;
276
277 /* Compute MD5 */
278 if (src != NULL) {
279 MD5_Init(&ctx);
280 MD5_Update(&ctx, src, config->block_size);
281 MD5_Final(md5, &ctx);
282 } else
283 memset(md5, 0, MD5_DIGEST_LENGTH);
284
285 /* Return MD5 to caller */
286 if (caller_md5 != NULL)
287 memcpy(caller_md5, md5, MD5_DIGEST_LENGTH);
288
289 /* Logging */
290 if (config->debug) {
291 (*config->log)(LOG_DEBUG,
292 "test_io: write %0*jx started, MD5 %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%s",
293 S3B_BLOCK_NUM_DIGITS, (uintmax_t)block_num,
294 (u_int)md5[0], (u_int)md5[1], (u_int)md5[2], (u_int)md5[3],
295 (u_int)md5[4], (u_int)md5[5], (u_int)md5[6], (u_int)md5[7],
296 (u_int)md5[8], (u_int)md5[9], (u_int)md5[10], (u_int)md5[11],
297 (u_int)md5[12], (u_int)md5[13], (u_int)md5[14], (u_int)md5[15],
298 src == NULL ? " (zero block)" : "");
299 }
300
301 /* Random delay */
302 usleep((random() % 200) * 1000);
303
304 /* Random error */
305 if ((random() % 100) < RANDOM_ERROR_PERCENT) {
306 (*config->log)(LOG_ERR, "test_io: random failure writing %0*jx", S3B_BLOCK_NUM_DIGITS, (uintmax_t)block_num);
307 return EAGAIN;
308 }
309
310 /* Generate path */
311 http_io_format_block_hash(config, block_hash_buf, sizeof(block_hash_buf), block_num);
312 snprintf(path, sizeof(path), "%s/%s%s%0*jx",
313 config->bucket, config->prefix, block_hash_buf, S3B_BLOCK_NUM_DIGITS, (uintmax_t)block_num);
314
315 /* Delete zero blocks */
316 if (src == NULL) {
317 if (unlink(path) == -1 && errno != ENOENT) {
318 r = errno;
319 (*config->log)(LOG_ERR, "can't unlink %s: %s", path, strerror(r));
320 return r;
321 }
322 return 0;
323 }
324
325 /* Write into temporary file */
326 snprintf(temp, sizeof(temp), "%s.XXXXXX", path);
327 if ((fd = mkstemp(temp)) == -1) {
328 r = errno;
329 (*config->log)(LOG_ERR, "%s: %s", temp, strerror(r));
330 return r;
331 }
332 for (total = 0; total < config->block_size; total += r) {
333 if ((r = write(fd, (const char *)src + total, config->block_size - total)) == -1) {
334 r = errno;
335 (*config->log)(LOG_ERR, "can't write %s: %s", temp, strerror(r));
336 close(fd);
337 (void)unlink(temp);
338 return r;
339 }
340 }
341 close(fd);
342
343 /* Rename file */
344 if (rename(temp, path) == -1) {
345 r = errno;
346 (*config->log)(LOG_ERR, "can't rename %s: %s", temp, strerror(r));
347 (void)unlink(temp);
348 return r;
349 }
350
351 /* Logging */
352 if (config->debug)
353 (*config->log)(LOG_DEBUG, "test_io: write %0*jx complete", S3B_BLOCK_NUM_DIGITS, (uintmax_t)block_num);
354
355 /* Done */
356 return 0;
357 }
358
359 static int
test_io_read_block_part(struct s3backer_store * s3b,s3b_block_t block_num,u_int off,u_int len,void * dest)360 test_io_read_block_part(struct s3backer_store *s3b, s3b_block_t block_num, u_int off, u_int len, void *dest)
361 {
362 struct test_io_private *const priv = s3b->data;
363 struct http_io_conf *const config = priv->config;
364
365 return block_part_read_block_part(s3b, block_num, config->block_size, off, len, dest);
366 }
367
368 static int
test_io_write_block_part(struct s3backer_store * s3b,s3b_block_t block_num,u_int off,u_int len,const void * src)369 test_io_write_block_part(struct s3backer_store *s3b, s3b_block_t block_num, u_int off, u_int len, const void *src)
370 {
371 struct test_io_private *const priv = s3b->data;
372 struct http_io_conf *const config = priv->config;
373
374 return block_part_write_block_part(s3b, block_num, config->block_size, off, len, src);
375 }
376
377 static int
test_io_list_blocks(struct s3backer_store * s3b,block_list_func_t * callback,void * arg)378 test_io_list_blocks(struct s3backer_store *s3b, block_list_func_t *callback, void *arg)
379 {
380 struct test_io_private *const priv = s3b->data;
381 struct http_io_conf *const config = priv->config;
382 s3b_block_t block_num;
383 struct dirent *dent;
384 DIR *dir;
385 int i;
386
387 /* Open directory */
388 if ((dir = opendir(config->bucket)) == NULL)
389 return errno;
390
391 /* Scan directory */
392 for (i = 0; (dent = readdir(dir)) != NULL; i++) {
393 if (http_io_parse_block(config, dent->d_name, &block_num) == 0)
394 (*callback)(arg, block_num);
395 }
396
397 /* Close directory */
398 closedir(dir);
399
400 /* Done */
401 return 0;
402 }
403
404