1 /* Copyright libuv project contributors. All rights reserved. 2 * 3 * Permission is hereby granted, free of charge, to any person obtaining a copy 4 * of this software and associated documentation files (the "Software"), to 5 * deal in the Software without restriction, including without limitation the 6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 * sell copies of the Software, and to permit persons to whom the Software is 8 * furnished to do so, subject to the following conditions: 9 * 10 * The above copyright notice and this permission notice shall be included in 11 * all copies or substantial portions of the Software. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 * IN THE SOFTWARE. 20 */ 21 22 #include "uv.h" 23 #include "task.h" 24 #include <fcntl.h> 25 #include <string.h> 26 27 static uv_fs_t opendir_req; 28 static uv_fs_t readdir_req; 29 static uv_fs_t closedir_req; 30 31 static uv_dirent_t dirents[1]; 32 static uv_dirent_t symlink_dirents[2]; 33 34 static int empty_opendir_cb_count; 35 static int empty_closedir_cb_count; 36 37 static void cleanup_test_files(void) { 38 uv_fs_t req; 39 40 uv_fs_unlink(NULL, &req, "test_dir/file1", NULL); 41 uv_fs_req_cleanup(&req); 42 uv_fs_unlink(NULL, &req, "test_dir/file2", NULL); 43 uv_fs_req_cleanup(&req); 44 uv_fs_rmdir(NULL, &req, "test_dir/test_subdir", NULL); 45 uv_fs_req_cleanup(&req); 46 uv_fs_rmdir(NULL, &req, "test_dir", NULL); 47 uv_fs_req_cleanup(&req); 48 } 49 50 static void empty_closedir_cb(uv_fs_t* req) { 51 ASSERT_PTR_EQ(req, &closedir_req); 52 ASSERT_EQ(req->fs_type, UV_FS_CLOSEDIR); 53 ASSERT_OK(req->result); 54 ++empty_closedir_cb_count; 55 uv_fs_req_cleanup(req); 56 } 57 58 static void empty_readdir_cb(uv_fs_t* req) { 59 uv_dir_t* dir; 60 int r; 61 62 ASSERT_PTR_EQ(req, &readdir_req); 63 ASSERT_EQ(req->fs_type, UV_FS_READDIR); 64 ASSERT_OK(req->result); 65 dir = req->ptr; 66 uv_fs_req_cleanup(req); 67 r = uv_fs_closedir(uv_default_loop(), 68 &closedir_req, 69 dir, 70 empty_closedir_cb); 71 ASSERT_OK(r); 72 } 73 74 static void empty_opendir_cb(uv_fs_t* req) { 75 uv_dir_t* dir; 76 int r; 77 78 ASSERT_PTR_EQ(req, &opendir_req); 79 ASSERT_EQ(req->fs_type, UV_FS_OPENDIR); 80 ASSERT_OK(req->result); 81 ASSERT_NOT_NULL(req->ptr); 82 dir = req->ptr; 83 dir->dirents = dirents; 84 dir->nentries = ARRAY_SIZE(dirents); 85 r = uv_fs_readdir(uv_default_loop(), 86 &readdir_req, 87 dir, 88 empty_readdir_cb); 89 ASSERT_OK(r); 90 uv_fs_req_cleanup(req); 91 ++empty_opendir_cb_count; 92 } 93 94 /* 95 * This test makes sure that both synchronous and asynchronous flavors 96 * of the uv_fs_opendir() -> uv_fs_readdir() -> uv_fs_closedir() sequence work 97 * as expected when processing an empty directory. 98 */ 99 TEST_IMPL(fs_readdir_empty_dir) { 100 const char* path; 101 uv_fs_t mkdir_req; 102 uv_fs_t rmdir_req; 103 int r; 104 int nb_entries_read; 105 uv_dir_t* dir; 106 107 path = "./empty_dir/"; 108 uv_fs_mkdir(uv_default_loop(), &mkdir_req, path, 0777, NULL); 109 uv_fs_req_cleanup(&mkdir_req); 110 111 /* Fill the req to ensure that required fields are cleaned up. */ 112 memset(&opendir_req, 0xdb, sizeof(opendir_req)); 113 114 /* Testing the synchronous flavor. */ 115 r = uv_fs_opendir(uv_default_loop(), 116 &opendir_req, 117 path, 118 NULL); 119 ASSERT_OK(r); 120 ASSERT_EQ(opendir_req.fs_type, UV_FS_OPENDIR); 121 ASSERT_OK(opendir_req.result); 122 ASSERT_NOT_NULL(opendir_req.ptr); 123 dir = opendir_req.ptr; 124 uv_fs_req_cleanup(&opendir_req); 125 126 /* Fill the req to ensure that required fields are cleaned up. */ 127 memset(&readdir_req, 0xdb, sizeof(readdir_req)); 128 dir->dirents = dirents; 129 dir->nentries = ARRAY_SIZE(dirents); 130 nb_entries_read = uv_fs_readdir(uv_default_loop(), 131 &readdir_req, 132 dir, 133 NULL); 134 ASSERT_OK(nb_entries_read); 135 uv_fs_req_cleanup(&readdir_req); 136 137 /* Fill the req to ensure that required fields are cleaned up. */ 138 memset(&closedir_req, 0xdb, sizeof(closedir_req)); 139 uv_fs_closedir(uv_default_loop(), &closedir_req, dir, NULL); 140 ASSERT_OK(closedir_req.result); 141 uv_fs_req_cleanup(&closedir_req); 142 143 /* Testing the asynchronous flavor. */ 144 145 /* Fill the req to ensure that required fields are cleaned up. */ 146 memset(&opendir_req, 0xdb, sizeof(opendir_req)); 147 memset(&readdir_req, 0xdb, sizeof(readdir_req)); 148 memset(&closedir_req, 0xdb, sizeof(closedir_req)); 149 150 r = uv_fs_opendir(uv_default_loop(), &opendir_req, path, empty_opendir_cb); 151 ASSERT_OK(r); 152 ASSERT_OK(empty_opendir_cb_count); 153 ASSERT_OK(empty_closedir_cb_count); 154 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); 155 ASSERT_OK(r); 156 ASSERT_EQ(1, empty_opendir_cb_count); 157 ASSERT_EQ(1, empty_closedir_cb_count); 158 uv_fs_rmdir(uv_default_loop(), &rmdir_req, path, NULL); 159 uv_fs_req_cleanup(&rmdir_req); 160 MAKE_VALGRIND_HAPPY(uv_default_loop()); 161 return 0; 162 } 163 164 /* 165 * This test makes sure that reading a non-existing directory with 166 * uv_fs_{open,read}_dir() returns proper error codes. 167 */ 168 169 static int non_existing_opendir_cb_count; 170 171 static void non_existing_opendir_cb(uv_fs_t* req) { 172 ASSERT_PTR_EQ(req, &opendir_req); 173 ASSERT_EQ(req->fs_type, UV_FS_OPENDIR); 174 ASSERT_EQ(req->result, UV_ENOENT); 175 ASSERT_NULL(req->ptr); 176 177 uv_fs_req_cleanup(req); 178 ++non_existing_opendir_cb_count; 179 } 180 181 TEST_IMPL(fs_readdir_non_existing_dir) { 182 const char* path; 183 int r; 184 185 path = "./non-existing-dir/"; 186 187 /* Fill the req to ensure that required fields are cleaned up. */ 188 memset(&opendir_req, 0xdb, sizeof(opendir_req)); 189 190 /* Testing the synchronous flavor. */ 191 r = uv_fs_opendir(uv_default_loop(), &opendir_req, path, NULL); 192 ASSERT_EQ(r, UV_ENOENT); 193 ASSERT_EQ(opendir_req.fs_type, UV_FS_OPENDIR); 194 ASSERT_EQ(opendir_req.result, UV_ENOENT); 195 ASSERT_NULL(opendir_req.ptr); 196 uv_fs_req_cleanup(&opendir_req); 197 198 /* Fill the req to ensure that required fields are cleaned up. */ 199 memset(&opendir_req, 0xdb, sizeof(opendir_req)); 200 201 /* Testing the async flavor. */ 202 r = uv_fs_opendir(uv_default_loop(), 203 &opendir_req, 204 path, 205 non_existing_opendir_cb); 206 ASSERT_OK(r); 207 ASSERT_OK(non_existing_opendir_cb_count); 208 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); 209 ASSERT_OK(r); 210 ASSERT_EQ(1, non_existing_opendir_cb_count); 211 212 MAKE_VALGRIND_HAPPY(uv_default_loop()); 213 return 0; 214 } 215 216 /* 217 * This test makes sure that reading a file as a directory reports correct 218 * error codes. 219 */ 220 221 static int file_opendir_cb_count; 222 223 static void file_opendir_cb(uv_fs_t* req) { 224 ASSERT_PTR_EQ(req, &opendir_req); 225 ASSERT_EQ(req->fs_type, UV_FS_OPENDIR); 226 ASSERT_EQ(req->result, UV_ENOTDIR); 227 ASSERT_NULL(req->ptr); 228 229 uv_fs_req_cleanup(req); 230 ++file_opendir_cb_count; 231 } 232 233 TEST_IMPL(fs_readdir_file) { 234 const char* path; 235 int r; 236 237 path = "test/fixtures/empty_file"; 238 239 /* Fill the req to ensure that required fields are cleaned up. */ 240 memset(&opendir_req, 0xdb, sizeof(opendir_req)); 241 242 /* Testing the synchronous flavor. */ 243 r = uv_fs_opendir(uv_default_loop(), &opendir_req, path, NULL); 244 245 ASSERT_EQ(r, UV_ENOTDIR); 246 ASSERT_EQ(opendir_req.fs_type, UV_FS_OPENDIR); 247 ASSERT_EQ(opendir_req.result, UV_ENOTDIR); 248 ASSERT_NULL(opendir_req.ptr); 249 250 uv_fs_req_cleanup(&opendir_req); 251 252 /* Fill the req to ensure that required fields are cleaned up. */ 253 memset(&opendir_req, 0xdb, sizeof(opendir_req)); 254 255 /* Testing the async flavor. */ 256 r = uv_fs_opendir(uv_default_loop(), &opendir_req, path, file_opendir_cb); 257 ASSERT_OK(r); 258 ASSERT_OK(file_opendir_cb_count); 259 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); 260 ASSERT_OK(r); 261 ASSERT_EQ(1, file_opendir_cb_count); 262 MAKE_VALGRIND_HAPPY(uv_default_loop()); 263 return 0; 264 } 265 266 /* 267 * This test makes sure that reading a non-empty directory with 268 * uv_fs_{open,read}_dir() returns proper directory entries, including the 269 * correct entry types. 270 */ 271 272 static int non_empty_opendir_cb_count; 273 static int non_empty_readdir_cb_count; 274 static int non_empty_closedir_cb_count; 275 276 static void non_empty_closedir_cb(uv_fs_t* req) { 277 ASSERT_PTR_EQ(req, &closedir_req); 278 ASSERT_OK(req->result); 279 uv_fs_req_cleanup(req); 280 ++non_empty_closedir_cb_count; 281 } 282 283 static void non_empty_readdir_cb(uv_fs_t* req) { 284 uv_dir_t* dir; 285 286 ASSERT_PTR_EQ(req, &readdir_req); 287 ASSERT_EQ(req->fs_type, UV_FS_READDIR); 288 dir = req->ptr; 289 290 if (req->result == 0) { 291 uv_fs_req_cleanup(req); 292 ASSERT_EQ(3, non_empty_readdir_cb_count); 293 uv_fs_closedir(uv_default_loop(), 294 &closedir_req, 295 dir, 296 non_empty_closedir_cb); 297 } else { 298 ASSERT_EQ(1, req->result); 299 ASSERT_PTR_EQ(dir->dirents, dirents); 300 ASSERT(strcmp(dirents[0].name, "file1") == 0 || 301 strcmp(dirents[0].name, "file2") == 0 || 302 strcmp(dirents[0].name, "test_subdir") == 0); 303 #ifdef HAVE_DIRENT_TYPES 304 if (!strcmp(dirents[0].name, "test_subdir")) 305 ASSERT_EQ(dirents[0].type, UV_DIRENT_DIR); 306 else 307 ASSERT_EQ(dirents[0].type, UV_DIRENT_FILE); 308 #else 309 ASSERT_EQ(dirents[0].type, UV_DIRENT_UNKNOWN); 310 #endif /* HAVE_DIRENT_TYPES */ 311 312 ++non_empty_readdir_cb_count; 313 uv_fs_req_cleanup(req); 314 dir->dirents = dirents; 315 dir->nentries = ARRAY_SIZE(dirents); 316 uv_fs_readdir(uv_default_loop(), 317 &readdir_req, 318 dir, 319 non_empty_readdir_cb); 320 } 321 } 322 323 static void non_empty_opendir_cb(uv_fs_t* req) { 324 uv_dir_t* dir; 325 int r; 326 327 ASSERT_PTR_EQ(req, &opendir_req); 328 ASSERT_EQ(req->fs_type, UV_FS_OPENDIR); 329 ASSERT_OK(req->result); 330 ASSERT_NOT_NULL(req->ptr); 331 332 dir = req->ptr; 333 dir->dirents = dirents; 334 dir->nentries = ARRAY_SIZE(dirents); 335 336 r = uv_fs_readdir(uv_default_loop(), 337 &readdir_req, 338 dir, 339 non_empty_readdir_cb); 340 ASSERT_OK(r); 341 uv_fs_req_cleanup(req); 342 ++non_empty_opendir_cb_count; 343 } 344 345 TEST_IMPL(fs_readdir_non_empty_dir) { 346 size_t entries_count; 347 uv_fs_t mkdir_req; 348 uv_fs_t rmdir_req; 349 uv_fs_t create_req; 350 uv_fs_t close_req; 351 uv_dir_t* dir; 352 int r; 353 354 cleanup_test_files(); 355 356 r = uv_fs_mkdir(uv_default_loop(), &mkdir_req, "test_dir", 0755, NULL); 357 ASSERT_OK(r); 358 359 /* Create two files synchronously. */ 360 r = uv_fs_open(uv_default_loop(), 361 &create_req, 362 "test_dir/file1", 363 UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, 364 NULL); 365 ASSERT_GE(r, 0); 366 uv_fs_req_cleanup(&create_req); 367 r = uv_fs_close(uv_default_loop(), 368 &close_req, 369 create_req.result, 370 NULL); 371 ASSERT_OK(r); 372 uv_fs_req_cleanup(&close_req); 373 374 r = uv_fs_open(uv_default_loop(), 375 &create_req, 376 "test_dir/file2", 377 UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, 378 NULL); 379 ASSERT_GE(r, 0); 380 uv_fs_req_cleanup(&create_req); 381 r = uv_fs_close(uv_default_loop(), 382 &close_req, 383 create_req.result, 384 NULL); 385 ASSERT_OK(r); 386 uv_fs_req_cleanup(&close_req); 387 388 r = uv_fs_mkdir(uv_default_loop(), 389 &mkdir_req, 390 "test_dir/test_subdir", 391 0755, 392 NULL); 393 ASSERT_OK(r); 394 uv_fs_req_cleanup(&mkdir_req); 395 396 /* Fill the req to ensure that required fields are cleaned up. */ 397 memset(&opendir_req, 0xdb, sizeof(opendir_req)); 398 399 /* Testing the synchronous flavor. */ 400 r = uv_fs_opendir(uv_default_loop(), &opendir_req, "test_dir", NULL); 401 ASSERT_OK(r); 402 ASSERT_EQ(opendir_req.fs_type, UV_FS_OPENDIR); 403 ASSERT_OK(opendir_req.result); 404 ASSERT_NOT_NULL(opendir_req.ptr); 405 406 entries_count = 0; 407 dir = opendir_req.ptr; 408 dir->dirents = dirents; 409 dir->nentries = ARRAY_SIZE(dirents); 410 uv_fs_req_cleanup(&opendir_req); 411 412 while (uv_fs_readdir(uv_default_loop(), 413 &readdir_req, 414 dir, 415 NULL) != 0) { 416 ASSERT(strcmp(dirents[0].name, "file1") == 0 || 417 strcmp(dirents[0].name, "file2") == 0 || 418 strcmp(dirents[0].name, "test_subdir") == 0); 419 #ifdef HAVE_DIRENT_TYPES 420 if (!strcmp(dirents[0].name, "test_subdir")) 421 ASSERT_EQ(dirents[0].type, UV_DIRENT_DIR); 422 else 423 ASSERT_EQ(dirents[0].type, UV_DIRENT_FILE); 424 #else 425 ASSERT_EQ(dirents[0].type, UV_DIRENT_UNKNOWN); 426 #endif /* HAVE_DIRENT_TYPES */ 427 uv_fs_req_cleanup(&readdir_req); 428 ++entries_count; 429 } 430 431 ASSERT_EQ(3, entries_count); 432 uv_fs_req_cleanup(&readdir_req); 433 434 /* Fill the req to ensure that required fields are cleaned up. */ 435 memset(&closedir_req, 0xdb, sizeof(closedir_req)); 436 uv_fs_closedir(uv_default_loop(), &closedir_req, dir, NULL); 437 ASSERT_OK(closedir_req.result); 438 uv_fs_req_cleanup(&closedir_req); 439 440 /* Testing the asynchronous flavor. */ 441 442 /* Fill the req to ensure that required fields are cleaned up. */ 443 memset(&opendir_req, 0xdb, sizeof(opendir_req)); 444 445 r = uv_fs_opendir(uv_default_loop(), 446 &opendir_req, 447 "test_dir", 448 non_empty_opendir_cb); 449 ASSERT_OK(r); 450 ASSERT_OK(non_empty_opendir_cb_count); 451 ASSERT_OK(non_empty_closedir_cb_count); 452 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); 453 ASSERT_OK(r); 454 ASSERT_EQ(1, non_empty_opendir_cb_count); 455 ASSERT_EQ(1, non_empty_closedir_cb_count); 456 457 uv_fs_rmdir(uv_default_loop(), &rmdir_req, "test_subdir", NULL); 458 uv_fs_req_cleanup(&rmdir_req); 459 460 cleanup_test_files(); 461 MAKE_VALGRIND_HAPPY(uv_default_loop()); 462 return 0; 463 } 464 465 static void readdir_symlink_readdir_cb(uv_fs_t* req) { 466 uv_dir_t* dir; 467 468 ASSERT_PTR_EQ(req, &readdir_req); 469 ASSERT_EQ(req->fs_type, UV_FS_READDIR); 470 dir = req->ptr; 471 472 if (req->result == 0) { 473 uv_fs_req_cleanup(req); 474 ASSERT_EQ(3, non_empty_readdir_cb_count); 475 uv_fs_closedir(uv_default_loop(), 476 &closedir_req, 477 dir, 478 non_empty_closedir_cb); 479 } else { 480 if (strcmp(symlink_dirents[0].name, "test_symlink") == 0) { 481 ASSERT_EQ(symlink_dirents[0].type, UV_DIRENT_LINK); 482 } else { 483 ASSERT_EQ(symlink_dirents[1].type, UV_DIRENT_LINK); 484 } 485 uv_fs_req_cleanup(req); 486 } 487 } 488 489 static void readdir_symlink_opendir_cb(uv_fs_t* req) { 490 uv_dir_t* dir; 491 int r; 492 493 ASSERT_PTR_EQ(req, &opendir_req); 494 ASSERT_EQ(req->fs_type, UV_FS_OPENDIR); 495 ASSERT_OK(req->result); 496 ASSERT_NOT_NULL(req->ptr); 497 498 dir = req->ptr; 499 dir->dirents = symlink_dirents; 500 dir->nentries = ARRAY_SIZE(symlink_dirents); 501 502 r = uv_fs_readdir(uv_default_loop(), 503 &readdir_req, 504 dir, 505 readdir_symlink_readdir_cb); 506 ASSERT_OK(r); 507 uv_fs_req_cleanup(req); 508 } 509 510 static void cleanup_symlink_test_files(void) { 511 uv_fs_t req; 512 513 uv_fs_rmdir(NULL, &req, "test_symlink_dir/test_subdir", NULL); 514 uv_fs_req_cleanup(&req); 515 uv_fs_unlink(NULL, &req, "test_symlink_dir/test_symlink", NULL); 516 uv_fs_req_cleanup(&req); 517 uv_fs_rmdir(NULL, &req, "test_symlink_dir", NULL); 518 uv_fs_req_cleanup(&req); 519 } 520 521 TEST_IMPL(fs_readdir_symlink) { 522 523 uv_fs_t mkdir_req; 524 uv_fs_t symlink_req; 525 int r; 526 527 cleanup_symlink_test_files(); 528 529 r = uv_fs_mkdir(uv_default_loop(), &mkdir_req, "test_symlink_dir", 0755, NULL); 530 ASSERT_OK(r); 531 532 r = uv_fs_mkdir(uv_default_loop(), &mkdir_req, "test_symlink_dir/test_subdir", 0755, NULL); 533 ASSERT_OK(r); 534 535 r = uv_fs_symlink(uv_default_loop(), &symlink_req, "test_symlink_dir/test_subdir", "test_symlink_dir/test_symlink", UV_FS_SYMLINK_DIR, NULL); 536 ASSERT_OK(r); 537 538 r = uv_fs_opendir(uv_default_loop(), &opendir_req, "test_symlink_dir", readdir_symlink_opendir_cb); 539 ASSERT_OK(r); 540 541 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); 542 ASSERT_OK(r); 543 544 cleanup_symlink_test_files(); 545 546 MAKE_VALGRIND_HAPPY(uv_default_loop()); 547 return 0; 548 } 549