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 33 static int empty_opendir_cb_count; 34 static int empty_closedir_cb_count; 35 36 static void cleanup_test_files(void) { 37 uv_fs_t req; 38 39 uv_fs_unlink(NULL, &req, "test_dir/file1", NULL); 40 uv_fs_req_cleanup(&req); 41 uv_fs_unlink(NULL, &req, "test_dir/file2", NULL); 42 uv_fs_req_cleanup(&req); 43 uv_fs_rmdir(NULL, &req, "test_dir/test_subdir", NULL); 44 uv_fs_req_cleanup(&req); 45 uv_fs_rmdir(NULL, &req, "test_dir", NULL); 46 uv_fs_req_cleanup(&req); 47 } 48 49 static void empty_closedir_cb(uv_fs_t* req) { 50 ASSERT(req == &closedir_req); 51 ASSERT(req->fs_type == UV_FS_CLOSEDIR); 52 ASSERT(req->result == 0); 53 ++empty_closedir_cb_count; 54 uv_fs_req_cleanup(req); 55 } 56 57 static void empty_readdir_cb(uv_fs_t* req) { 58 uv_dir_t* dir; 59 int r; 60 61 ASSERT(req == &readdir_req); 62 ASSERT(req->fs_type == UV_FS_READDIR); 63 ASSERT(req->result == 0); 64 dir = req->ptr; 65 uv_fs_req_cleanup(req); 66 r = uv_fs_closedir(uv_default_loop(), 67 &closedir_req, 68 dir, 69 empty_closedir_cb); 70 ASSERT(r == 0); 71 } 72 73 static void empty_opendir_cb(uv_fs_t* req) { 74 uv_dir_t* dir; 75 int r; 76 77 ASSERT(req == &opendir_req); 78 ASSERT(req->fs_type == UV_FS_OPENDIR); 79 ASSERT(req->result == 0); 80 ASSERT(req->ptr != NULL); 81 dir = req->ptr; 82 dir->dirents = dirents; 83 dir->nentries = ARRAY_SIZE(dirents); 84 r = uv_fs_readdir(uv_default_loop(), 85 &readdir_req, 86 dir, 87 empty_readdir_cb); 88 ASSERT(r == 0); 89 uv_fs_req_cleanup(req); 90 ++empty_opendir_cb_count; 91 } 92 93 /* 94 * This test makes sure that both synchronous and asynchronous flavors 95 * of the uv_fs_opendir() -> uv_fs_readdir() -> uv_fs_closedir() sequence work 96 * as expected when processing an empty directory. 97 */ 98 TEST_IMPL(fs_readdir_empty_dir) { 99 const char* path; 100 uv_fs_t mkdir_req; 101 uv_fs_t rmdir_req; 102 int r; 103 int nb_entries_read; 104 uv_dir_t* dir; 105 106 path = "./empty_dir/"; 107 uv_fs_mkdir(uv_default_loop(), &mkdir_req, path, 0777, NULL); 108 uv_fs_req_cleanup(&mkdir_req); 109 110 /* Fill the req to ensure that required fields are cleaned up. */ 111 memset(&opendir_req, 0xdb, sizeof(opendir_req)); 112 113 /* Testing the synchronous flavor. */ 114 r = uv_fs_opendir(uv_default_loop(), 115 &opendir_req, 116 path, 117 NULL); 118 ASSERT(r == 0); 119 ASSERT(opendir_req.fs_type == UV_FS_OPENDIR); 120 ASSERT(opendir_req.result == 0); 121 ASSERT(opendir_req.ptr != NULL); 122 dir = opendir_req.ptr; 123 uv_fs_req_cleanup(&opendir_req); 124 125 /* Fill the req to ensure that required fields are cleaned up. */ 126 memset(&readdir_req, 0xdb, sizeof(readdir_req)); 127 dir->dirents = dirents; 128 dir->nentries = ARRAY_SIZE(dirents); 129 nb_entries_read = uv_fs_readdir(uv_default_loop(), 130 &readdir_req, 131 dir, 132 NULL); 133 ASSERT(nb_entries_read == 0); 134 uv_fs_req_cleanup(&readdir_req); 135 136 /* Fill the req to ensure that required fields are cleaned up. */ 137 memset(&closedir_req, 0xdb, sizeof(closedir_req)); 138 uv_fs_closedir(uv_default_loop(), &closedir_req, dir, NULL); 139 ASSERT(closedir_req.result == 0); 140 uv_fs_req_cleanup(&closedir_req); 141 142 /* Testing the asynchronous flavor. */ 143 144 /* Fill the req to ensure that required fields are cleaned up. */ 145 memset(&opendir_req, 0xdb, sizeof(opendir_req)); 146 memset(&readdir_req, 0xdb, sizeof(readdir_req)); 147 memset(&closedir_req, 0xdb, sizeof(closedir_req)); 148 149 r = uv_fs_opendir(uv_default_loop(), &opendir_req, path, empty_opendir_cb); 150 ASSERT(r == 0); 151 ASSERT(empty_opendir_cb_count == 0); 152 ASSERT(empty_closedir_cb_count == 0); 153 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); 154 ASSERT(r == 0); 155 ASSERT(empty_opendir_cb_count == 1); 156 ASSERT(empty_closedir_cb_count == 1); 157 uv_fs_rmdir(uv_default_loop(), &rmdir_req, path, NULL); 158 uv_fs_req_cleanup(&rmdir_req); 159 MAKE_VALGRIND_HAPPY(); 160 return 0; 161 } 162 163 /* 164 * This test makes sure that reading a non-existing directory with 165 * uv_fs_{open,read}_dir() returns proper error codes. 166 */ 167 168 static int non_existing_opendir_cb_count; 169 170 static void non_existing_opendir_cb(uv_fs_t* req) { 171 ASSERT(req == &opendir_req); 172 ASSERT(req->fs_type == UV_FS_OPENDIR); 173 ASSERT(req->result == UV_ENOENT); 174 ASSERT(req->ptr == NULL); 175 176 uv_fs_req_cleanup(req); 177 ++non_existing_opendir_cb_count; 178 } 179 180 TEST_IMPL(fs_readdir_non_existing_dir) { 181 const char* path; 182 int r; 183 184 path = "./non-existing-dir/"; 185 186 /* Fill the req to ensure that required fields are cleaned up. */ 187 memset(&opendir_req, 0xdb, sizeof(opendir_req)); 188 189 /* Testing the synchronous flavor. */ 190 r = uv_fs_opendir(uv_default_loop(), &opendir_req, path, NULL); 191 ASSERT(r == UV_ENOENT); 192 ASSERT(opendir_req.fs_type == UV_FS_OPENDIR); 193 ASSERT(opendir_req.result == UV_ENOENT); 194 ASSERT(opendir_req.ptr == NULL); 195 uv_fs_req_cleanup(&opendir_req); 196 197 /* Fill the req to ensure that required fields are cleaned up. */ 198 memset(&opendir_req, 0xdb, sizeof(opendir_req)); 199 200 /* Testing the async flavor. */ 201 r = uv_fs_opendir(uv_default_loop(), 202 &opendir_req, 203 path, 204 non_existing_opendir_cb); 205 ASSERT(r == 0); 206 ASSERT(non_existing_opendir_cb_count == 0); 207 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); 208 ASSERT(r == 0); 209 ASSERT(non_existing_opendir_cb_count == 1); 210 211 MAKE_VALGRIND_HAPPY(); 212 return 0; 213 } 214 215 /* 216 * This test makes sure that reading a file as a directory reports correct 217 * error codes. 218 */ 219 220 static int file_opendir_cb_count; 221 222 static void file_opendir_cb(uv_fs_t* req) { 223 ASSERT(req == &opendir_req); 224 ASSERT(req->fs_type == UV_FS_OPENDIR); 225 ASSERT(req->result == UV_ENOTDIR); 226 ASSERT(req->ptr == NULL); 227 228 uv_fs_req_cleanup(req); 229 ++file_opendir_cb_count; 230 } 231 232 TEST_IMPL(fs_readdir_file) { 233 const char* path; 234 int r; 235 236 path = "test/fixtures/empty_file"; 237 238 /* Fill the req to ensure that required fields are cleaned up. */ 239 memset(&opendir_req, 0xdb, sizeof(opendir_req)); 240 241 /* Testing the synchronous flavor. */ 242 r = uv_fs_opendir(uv_default_loop(), &opendir_req, path, NULL); 243 244 ASSERT(r == UV_ENOTDIR); 245 ASSERT(opendir_req.fs_type == UV_FS_OPENDIR); 246 ASSERT(opendir_req.result == UV_ENOTDIR); 247 ASSERT(opendir_req.ptr == NULL); 248 249 uv_fs_req_cleanup(&opendir_req); 250 251 /* Fill the req to ensure that required fields are cleaned up. */ 252 memset(&opendir_req, 0xdb, sizeof(opendir_req)); 253 254 /* Testing the async flavor. */ 255 r = uv_fs_opendir(uv_default_loop(), &opendir_req, path, file_opendir_cb); 256 ASSERT(r == 0); 257 ASSERT(file_opendir_cb_count == 0); 258 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); 259 ASSERT(r == 0); 260 ASSERT(file_opendir_cb_count == 1); 261 MAKE_VALGRIND_HAPPY(); 262 return 0; 263 } 264 265 /* 266 * This test makes sure that reading a non-empty directory with 267 * uv_fs_{open,read}_dir() returns proper directory entries, including the 268 * correct entry types. 269 */ 270 271 static int non_empty_opendir_cb_count; 272 static int non_empty_readdir_cb_count; 273 static int non_empty_closedir_cb_count; 274 275 static void non_empty_closedir_cb(uv_fs_t* req) { 276 ASSERT(req == &closedir_req); 277 ASSERT(req->result == 0); 278 uv_fs_req_cleanup(req); 279 ++non_empty_closedir_cb_count; 280 } 281 282 static void non_empty_readdir_cb(uv_fs_t* req) { 283 uv_dir_t* dir; 284 285 ASSERT(req == &readdir_req); 286 ASSERT(req->fs_type == UV_FS_READDIR); 287 dir = req->ptr; 288 289 if (req->result == 0) { 290 uv_fs_req_cleanup(req); 291 ASSERT(non_empty_readdir_cb_count == 3); 292 uv_fs_closedir(uv_default_loop(), 293 &closedir_req, 294 dir, 295 non_empty_closedir_cb); 296 } else { 297 ASSERT(req->result == 1); 298 ASSERT(dir->dirents == dirents); 299 ASSERT(strcmp(dirents[0].name, "file1") == 0 || 300 strcmp(dirents[0].name, "file2") == 0 || 301 strcmp(dirents[0].name, "test_subdir") == 0); 302 #ifdef HAVE_DIRENT_TYPES 303 if (!strcmp(dirents[0].name, "test_subdir")) 304 ASSERT(dirents[0].type == UV_DIRENT_DIR); 305 else 306 ASSERT(dirents[0].type == UV_DIRENT_FILE); 307 #else 308 ASSERT(dirents[0].type == UV_DIRENT_UNKNOWN); 309 #endif /* HAVE_DIRENT_TYPES */ 310 311 ++non_empty_readdir_cb_count; 312 uv_fs_req_cleanup(req); 313 dir->dirents = dirents; 314 dir->nentries = ARRAY_SIZE(dirents); 315 uv_fs_readdir(uv_default_loop(), 316 &readdir_req, 317 dir, 318 non_empty_readdir_cb); 319 } 320 } 321 322 static void non_empty_opendir_cb(uv_fs_t* req) { 323 uv_dir_t* dir; 324 int r; 325 326 ASSERT(req == &opendir_req); 327 ASSERT(req->fs_type == UV_FS_OPENDIR); 328 ASSERT(req->result == 0); 329 ASSERT(req->ptr != NULL); 330 331 dir = req->ptr; 332 dir->dirents = dirents; 333 dir->nentries = ARRAY_SIZE(dirents); 334 335 r = uv_fs_readdir(uv_default_loop(), 336 &readdir_req, 337 dir, 338 non_empty_readdir_cb); 339 ASSERT(r == 0); 340 uv_fs_req_cleanup(req); 341 ++non_empty_opendir_cb_count; 342 } 343 344 TEST_IMPL(fs_readdir_non_empty_dir) { 345 size_t entries_count; 346 uv_fs_t mkdir_req; 347 uv_fs_t rmdir_req; 348 uv_fs_t create_req; 349 uv_fs_t close_req; 350 uv_dir_t* dir; 351 int r; 352 353 cleanup_test_files(); 354 355 r = uv_fs_mkdir(uv_default_loop(), &mkdir_req, "test_dir", 0755, NULL); 356 ASSERT(r == 0); 357 358 /* Create two files synchronously. */ 359 r = uv_fs_open(uv_default_loop(), 360 &create_req, 361 "test_dir/file1", 362 O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, 363 NULL); 364 ASSERT(r >= 0); 365 uv_fs_req_cleanup(&create_req); 366 r = uv_fs_close(uv_default_loop(), 367 &close_req, 368 create_req.result, 369 NULL); 370 ASSERT(r == 0); 371 uv_fs_req_cleanup(&close_req); 372 373 r = uv_fs_open(uv_default_loop(), 374 &create_req, 375 "test_dir/file2", 376 O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, 377 NULL); 378 ASSERT(r >= 0); 379 uv_fs_req_cleanup(&create_req); 380 r = uv_fs_close(uv_default_loop(), 381 &close_req, 382 create_req.result, 383 NULL); 384 ASSERT(r == 0); 385 uv_fs_req_cleanup(&close_req); 386 387 r = uv_fs_mkdir(uv_default_loop(), 388 &mkdir_req, 389 "test_dir/test_subdir", 390 0755, 391 NULL); 392 ASSERT(r == 0); 393 uv_fs_req_cleanup(&mkdir_req); 394 395 /* Fill the req to ensure that required fields are cleaned up. */ 396 memset(&opendir_req, 0xdb, sizeof(opendir_req)); 397 398 /* Testing the synchronous flavor. */ 399 r = uv_fs_opendir(uv_default_loop(), &opendir_req, "test_dir", NULL); 400 ASSERT(r == 0); 401 ASSERT(opendir_req.fs_type == UV_FS_OPENDIR); 402 ASSERT(opendir_req.result == 0); 403 ASSERT(opendir_req.ptr != NULL); 404 405 entries_count = 0; 406 dir = opendir_req.ptr; 407 dir->dirents = dirents; 408 dir->nentries = ARRAY_SIZE(dirents); 409 uv_fs_req_cleanup(&opendir_req); 410 411 while (uv_fs_readdir(uv_default_loop(), 412 &readdir_req, 413 dir, 414 NULL) != 0) { 415 ASSERT(strcmp(dirents[0].name, "file1") == 0 || 416 strcmp(dirents[0].name, "file2") == 0 || 417 strcmp(dirents[0].name, "test_subdir") == 0); 418 #ifdef HAVE_DIRENT_TYPES 419 if (!strcmp(dirents[0].name, "test_subdir")) 420 ASSERT(dirents[0].type == UV_DIRENT_DIR); 421 else 422 ASSERT(dirents[0].type == UV_DIRENT_FILE); 423 #else 424 ASSERT(dirents[0].type == UV_DIRENT_UNKNOWN); 425 #endif /* HAVE_DIRENT_TYPES */ 426 uv_fs_req_cleanup(&readdir_req); 427 ++entries_count; 428 } 429 430 ASSERT(entries_count == 3); 431 uv_fs_req_cleanup(&readdir_req); 432 433 /* Fill the req to ensure that required fields are cleaned up. */ 434 memset(&closedir_req, 0xdb, sizeof(closedir_req)); 435 uv_fs_closedir(uv_default_loop(), &closedir_req, dir, NULL); 436 ASSERT(closedir_req.result == 0); 437 uv_fs_req_cleanup(&closedir_req); 438 439 /* Testing the asynchronous flavor. */ 440 441 /* Fill the req to ensure that required fields are cleaned up. */ 442 memset(&opendir_req, 0xdb, sizeof(opendir_req)); 443 444 r = uv_fs_opendir(uv_default_loop(), 445 &opendir_req, 446 "test_dir", 447 non_empty_opendir_cb); 448 ASSERT(r == 0); 449 ASSERT(non_empty_opendir_cb_count == 0); 450 ASSERT(non_empty_closedir_cb_count == 0); 451 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); 452 ASSERT(r == 0); 453 ASSERT(non_empty_opendir_cb_count == 1); 454 ASSERT(non_empty_closedir_cb_count == 1); 455 456 uv_fs_rmdir(uv_default_loop(), &rmdir_req, "test_subdir", NULL); 457 uv_fs_req_cleanup(&rmdir_req); 458 459 cleanup_test_files(); 460 MAKE_VALGRIND_HAPPY(); 461 return 0; 462 } 463