xref: /libuv/test/test-fs-readdir.c (revision 1cbffcbd)
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 
cleanup_test_files(void)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 
empty_closedir_cb(uv_fs_t * req)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 
empty_readdir_cb(uv_fs_t * req)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 
empty_opendir_cb(uv_fs_t * req)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  */
TEST_IMPL(fs_readdir_empty_dir)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 
non_existing_opendir_cb(uv_fs_t * req)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 
TEST_IMPL(fs_readdir_non_existing_dir)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 
file_opendir_cb(uv_fs_t * req)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 
TEST_IMPL(fs_readdir_file)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 
non_empty_closedir_cb(uv_fs_t * req)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 
non_empty_readdir_cb(uv_fs_t * req)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 
non_empty_opendir_cb(uv_fs_t * req)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 
TEST_IMPL(fs_readdir_non_empty_dir)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 
readdir_symlink_readdir_cb(uv_fs_t * req)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 
readdir_symlink_opendir_cb(uv_fs_t * req)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 
cleanup_symlink_test_files(void)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 
TEST_IMPL(fs_readdir_symlink)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