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