test_storagedir.c (10441B)
1 /* Copyright (c) 2017-2021, The Tor Project, Inc. */ 2 /* See LICENSE for licensing information */ 3 4 #include "core/or/or.h" 5 #include "lib/crypt_ops/crypto_rand.h" 6 #include "lib/fs/storagedir.h" 7 #include "lib/encoding/confline.h" 8 #include "test/test.h" 9 10 #ifdef HAVE_UTIME_H 11 #include <utime.h> 12 #endif 13 14 static void 15 test_storagedir_empty(void *arg) 16 { 17 char *dirname = tor_strdup(get_fname_rnd("store_dir")); 18 storage_dir_t *d = NULL; 19 (void)arg; 20 21 tt_int_op(FN_NOENT, OP_EQ, file_status(dirname)); 22 23 d = storage_dir_new(dirname, 10); 24 tt_assert(d); 25 26 tt_int_op(FN_DIR, OP_EQ, file_status(dirname)); 27 28 tt_int_op(0, OP_EQ, smartlist_len(storage_dir_list(d))); 29 tt_u64_op(0, OP_EQ, storage_dir_get_usage(d)); 30 31 storage_dir_free(d); 32 d = storage_dir_new(dirname, 10); 33 tt_assert(d); 34 35 tt_int_op(FN_DIR, OP_EQ, file_status(dirname)); 36 37 tt_int_op(0, OP_EQ, smartlist_len(storage_dir_list(d))); 38 tt_u64_op(0, OP_EQ, storage_dir_get_usage(d)); 39 40 done: 41 storage_dir_free(d); 42 tor_free(dirname); 43 } 44 45 static void 46 test_storagedir_basic(void *arg) 47 { 48 char *dirname = tor_strdup(get_fname_rnd("store_dir")); 49 storage_dir_t *d = NULL; 50 uint8_t *junk = NULL, *bytes = NULL; 51 const size_t junklen = 1024; 52 char *fname1 = NULL, *fname2 = NULL; 53 const char hello_str[] = "then what are we but cold, alone ... ?"; 54 tor_mmap_t *mapping = NULL; 55 (void)arg; 56 57 junk = tor_malloc(junklen); 58 crypto_rand((void*)junk, junklen); 59 60 d = storage_dir_new(dirname, 10); 61 tt_assert(d); 62 tt_u64_op(0, OP_EQ, storage_dir_get_usage(d)); 63 64 int r; 65 r = storage_dir_save_string_to_file(d, hello_str, 1, &fname1); 66 tt_int_op(r, OP_EQ, 0); 67 tt_ptr_op(fname1, OP_NE, NULL); 68 tt_u64_op(strlen(hello_str), OP_EQ, storage_dir_get_usage(d)); 69 70 r = storage_dir_save_bytes_to_file(d, junk, junklen, 1, &fname2); 71 tt_int_op(r, OP_EQ, 0); 72 tt_ptr_op(fname2, OP_NE, NULL); 73 74 tt_str_op(fname1, OP_NE, fname2); 75 76 tt_int_op(2, OP_EQ, smartlist_len(storage_dir_list(d))); 77 tt_u64_op(junklen + strlen(hello_str), OP_EQ, storage_dir_get_usage(d)); 78 tt_assert(smartlist_contains_string(storage_dir_list(d), fname1)); 79 tt_assert(smartlist_contains_string(storage_dir_list(d), fname2)); 80 81 storage_dir_free(d); 82 d = storage_dir_new(dirname, 10); 83 tt_assert(d); 84 tt_int_op(2, OP_EQ, smartlist_len(storage_dir_list(d))); 85 tt_u64_op(junklen + strlen(hello_str), OP_EQ, storage_dir_get_usage(d)); 86 tt_assert(smartlist_contains_string(storage_dir_list(d), fname1)); 87 tt_assert(smartlist_contains_string(storage_dir_list(d), fname2)); 88 89 size_t n; 90 bytes = storage_dir_read(d, fname2, 1, &n); 91 tt_assert(bytes); 92 tt_u64_op(n, OP_EQ, junklen); 93 tt_mem_op(bytes, OP_EQ, junk, junklen); 94 95 mapping = storage_dir_map(d, fname1); 96 tt_assert(mapping); 97 tt_u64_op(mapping->size, OP_EQ, strlen(hello_str)); 98 tt_mem_op(mapping->data, OP_EQ, hello_str, strlen(hello_str)); 99 100 done: 101 tor_free(dirname); 102 tor_free(junk); 103 tor_free(bytes); 104 tor_munmap_file(mapping); 105 storage_dir_free(d); 106 tor_free(fname1); 107 tor_free(fname2); 108 } 109 110 static void 111 test_storagedir_deletion(void *arg) 112 { 113 (void)arg; 114 char *dirname = tor_strdup(get_fname_rnd("store_dir")); 115 storage_dir_t *d = NULL; 116 char *fn1 = NULL, *fn2 = NULL; 117 char *bytes = NULL; 118 int r; 119 const char str1[] = "There are nine and sixty ways to disguise communiques"; 120 const char str2[] = "And rather more than one of them is right"; 121 122 // Make sure the directory is there. */ 123 d = storage_dir_new(dirname, 10); 124 storage_dir_free(d); 125 d = NULL; 126 127 tor_asprintf(&fn1, "%s/1007", dirname); 128 r = write_str_to_file(fn1, str1, 0); 129 tt_int_op(r, OP_EQ, 0); 130 131 tor_asprintf(&fn2, "%s/1003.tmp", dirname); 132 r = write_str_to_file(fn2, str2, 0); 133 tt_int_op(r, OP_EQ, 0); 134 135 // The tempfile should be deleted the next time we list the directory. 136 d = storage_dir_new(dirname, 10); 137 tt_int_op(1, OP_EQ, smartlist_len(storage_dir_list(d))); 138 tt_u64_op(strlen(str1), OP_EQ, storage_dir_get_usage(d)); 139 tt_int_op(FN_FILE, OP_EQ, file_status(fn1)); 140 tt_int_op(FN_NOENT, OP_EQ, file_status(fn2)); 141 142 bytes = (char*) storage_dir_read(d, "1007", 1, NULL); 143 tt_str_op(bytes, OP_EQ, str1); 144 145 // Should have no effect; file already gone. 146 storage_dir_remove_file(d, "1003.tmp"); 147 tt_int_op(1, OP_EQ, smartlist_len(storage_dir_list(d))); 148 tt_u64_op(strlen(str1), OP_EQ, storage_dir_get_usage(d)); 149 150 // Actually remove a file. 151 storage_dir_remove_file(d, "1007"); 152 tt_int_op(FN_NOENT, OP_EQ, file_status(fn1)); 153 tt_int_op(0, OP_EQ, smartlist_len(storage_dir_list(d))); 154 tt_u64_op(0, OP_EQ, storage_dir_get_usage(d)); 155 156 done: 157 tor_free(dirname); 158 tor_free(fn1); 159 tor_free(fn2); 160 storage_dir_free(d); 161 tor_free(bytes); 162 } 163 164 static void 165 test_storagedir_full(void *arg) 166 { 167 (void)arg; 168 169 char *dirname = tor_strdup(get_fname_rnd("store_dir")); 170 storage_dir_t *d = NULL; 171 const char str[] = "enemies of the peephole"; 172 int r; 173 174 d = storage_dir_new(dirname, 3); 175 tt_assert(d); 176 177 r = storage_dir_save_string_to_file(d, str, 1, NULL); 178 tt_int_op(r, OP_EQ, 0); 179 r = storage_dir_save_string_to_file(d, str, 1, NULL); 180 tt_int_op(r, OP_EQ, 0); 181 r = storage_dir_save_string_to_file(d, str, 1, NULL); 182 tt_int_op(r, OP_EQ, 0); 183 184 // These should fail! 185 r = storage_dir_save_string_to_file(d, str, 1, NULL); 186 tt_int_op(r, OP_EQ, -1); 187 r = storage_dir_save_string_to_file(d, str, 1, NULL); 188 tt_int_op(r, OP_EQ, -1); 189 190 tt_u64_op(strlen(str) * 3, OP_EQ, storage_dir_get_usage(d)); 191 192 done: 193 tor_free(dirname); 194 storage_dir_free(d); 195 } 196 197 static void 198 test_storagedir_cleaning(void *arg) 199 { 200 (void)arg; 201 202 char *dirname = tor_strdup(get_fname_rnd("store_dir")); 203 storage_dir_t *d = NULL; 204 const char str[] = 205 "On a mountain halfway between Reno and Rome / " 206 "We have a machine in a plexiglass dome / " 207 "Which listens and looks into everyone's home." 208 " -- Dr. Seuss"; 209 char *fns[8]; 210 int r, i; 211 212 memset(fns, 0, sizeof(fns)); 213 d = storage_dir_new(dirname, 10); 214 tt_assert(d); 215 216 for (i = 0; i < 8; ++i) { 217 r = storage_dir_save_string_to_file(d, str+i*2, 1, &fns[i]); 218 tt_int_op(r, OP_EQ, 0); 219 } 220 221 /* Now we're going to make sure all the files have distinct mtimes. */ 222 time_t now = time(NULL); 223 struct utimbuf ub; 224 ub.actime = now; 225 ub.modtime = now - 1000; 226 for (i = 0; i < 8; ++i) { 227 char *f = NULL; 228 tor_asprintf(&f, "%s/%s", dirname, fns[i]); 229 r = utime(f, &ub); 230 tor_free(f); 231 tt_int_op(r, OP_EQ, 0); 232 ub.modtime += 5; 233 } 234 235 const uint64_t usage_orig = storage_dir_get_usage(d); 236 /* No changes needed if we are already under target. */ 237 storage_dir_shrink(d, 1024*1024, 0); 238 tt_u64_op(usage_orig, OP_EQ, storage_dir_get_usage(d)); 239 240 /* Get rid of at least one byte. This will delete fns[0]. */ 241 storage_dir_shrink(d, usage_orig - 1, 0); 242 tt_u64_op(usage_orig, OP_GT, storage_dir_get_usage(d)); 243 tt_u64_op(usage_orig - strlen(str), OP_EQ, storage_dir_get_usage(d)); 244 245 /* Get rid of at least two files. This will delete fns[1] and fns[2]. */ 246 storage_dir_shrink(d, 1024*1024, 2); 247 tt_u64_op(usage_orig - strlen(str)*3 + 6, OP_EQ, storage_dir_get_usage(d)); 248 249 /* Get rid of everything. */ 250 storage_dir_remove_all(d); 251 tt_u64_op(0, OP_EQ, storage_dir_get_usage(d)); 252 253 done: 254 tor_free(dirname); 255 storage_dir_free(d); 256 for (i = 0; i < 8; ++i) { 257 tor_free(fns[i]); 258 } 259 } 260 261 static void 262 test_storagedir_save_labeled(void *arg) 263 { 264 (void)arg; 265 char *dirname = tor_strdup(get_fname_rnd("store_dir")); 266 storage_dir_t *d = NULL; 267 uint8_t *inp = tor_malloc_zero(8192); 268 config_line_t *labels = NULL; 269 char *fname = NULL; 270 uint8_t *saved = NULL; 271 272 d = storage_dir_new(dirname, 10); 273 tt_assert(d); 274 275 crypto_rand((char *)inp, 8192); 276 277 config_line_append(&labels, "Foo", "bar baz"); 278 config_line_append(&labels, "quux", "quuzXxz"); 279 const char expected[] = 280 "Foo bar baz\n" 281 "quux quuzXxz\n"; 282 283 int r = storage_dir_save_labeled_to_file(d, labels, inp, 8192, &fname); 284 tt_int_op(r, OP_EQ, 0); 285 286 size_t n = 0; 287 saved = storage_dir_read(d, fname, 1, &n); 288 tt_assert(memchr(saved, '\0', n)); 289 tt_str_op((char*)saved, OP_EQ, expected); /* NUL guarantees strcmp works */ 290 tt_mem_op(saved+strlen(expected)+1, OP_EQ, inp, 8192); 291 292 done: 293 storage_dir_free(d); 294 tor_free(dirname); 295 tor_free(inp); 296 tor_free(fname); 297 config_free_lines(labels); 298 tor_free(saved); 299 } 300 301 static void 302 test_storagedir_read_labeled(void *arg) 303 { 304 (void)arg; 305 char *dirname = tor_strdup(get_fname_rnd("store_dir")); 306 storage_dir_t *d = NULL; 307 uint8_t *inp = tor_malloc_zero(8192); 308 config_line_t *labels = NULL, *labels2 = NULL; 309 char *fname = NULL; 310 tor_mmap_t *map = NULL; 311 uint8_t *as_read = NULL; 312 313 d = storage_dir_new(dirname, 10); 314 tt_assert(d); 315 316 tor_snprintf((char*)inp, 8192, 317 "Hello world\n" 318 "This is a test\n" 319 "Yadda yadda.\n"); 320 size_t bodylen = 8192 - strlen((char*)inp) - 1; 321 crypto_rand((char *)inp+strlen((char*)inp)+1, bodylen); 322 323 int r = storage_dir_save_bytes_to_file(d, inp, 8192, 1, &fname); 324 tt_int_op(r, OP_EQ, 0); 325 326 /* Try mapping */ 327 const uint8_t *datap = NULL; 328 size_t sz = 0; 329 map = storage_dir_map_labeled(d, fname, &labels, &datap, &sz); 330 tt_assert(map); 331 tt_assert(datap); 332 tt_u64_op(sz, OP_EQ, bodylen); 333 tt_mem_op(datap, OP_EQ, inp+strlen((char*)inp)+1, bodylen); 334 tt_assert(labels); 335 tt_str_op(labels->key, OP_EQ, "Hello"); 336 tt_str_op(labels->value, OP_EQ, "world"); 337 tt_assert(labels->next); 338 tt_str_op(labels->next->key, OP_EQ, "This"); 339 tt_str_op(labels->next->value, OP_EQ, "is a test"); 340 tt_assert(labels->next->next); 341 tt_str_op(labels->next->next->key, OP_EQ, "Yadda"); 342 tt_str_op(labels->next->next->value, OP_EQ, "yadda."); 343 tt_ptr_op(labels->next->next->next, OP_EQ, NULL); 344 345 /* Try reading this time. */ 346 sz = 0; 347 as_read = storage_dir_read_labeled(d, fname, &labels2, &sz); 348 tt_assert(as_read); 349 tt_u64_op(sz, OP_EQ, bodylen); 350 tt_mem_op(as_read, OP_EQ, inp+strlen((char*)inp)+1, bodylen); 351 tt_assert(config_lines_eq(labels, labels2)); 352 353 done: 354 storage_dir_free(d); 355 tor_free(dirname); 356 tor_free(inp); 357 tor_free(fname); 358 config_free_lines(labels); 359 config_free_lines(labels2); 360 tor_munmap_file(map); 361 tor_free(as_read); 362 } 363 364 #define ENT(name) \ 365 { #name, test_storagedir_ ## name, TT_FORK, NULL, NULL } 366 367 struct testcase_t storagedir_tests[] = { 368 ENT(empty), 369 ENT(basic), 370 ENT(deletion), 371 ENT(full), 372 ENT(cleaning), 373 ENT(save_labeled), 374 ENT(read_labeled), 375 END_OF_TESTCASES 376 };