xref: /libuv/test/test-fs-readdir.c (revision 8a499e13)
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 
cleanup_test_files(void)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 
empty_closedir_cb(uv_fs_t * req)49 static void empty_closedir_cb(uv_fs_t* req) {
50   ASSERT_PTR_EQ(req, &closedir_req);
51   ASSERT_EQ(req->fs_type, UV_FS_CLOSEDIR);
52   ASSERT_OK(req->result);
53   ++empty_closedir_cb_count;
54   uv_fs_req_cleanup(req);
55 }
56 
empty_readdir_cb(uv_fs_t * req)57 static void empty_readdir_cb(uv_fs_t* req) {
58   uv_dir_t* dir;
59   int r;
60 
61   ASSERT_PTR_EQ(req, &readdir_req);
62   ASSERT_EQ(req->fs_type, UV_FS_READDIR);
63   ASSERT_OK(req->result);
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_OK(r);
71 }
72 
empty_opendir_cb(uv_fs_t * req)73 static void empty_opendir_cb(uv_fs_t* req) {
74   uv_dir_t* dir;
75   int r;
76 
77   ASSERT_PTR_EQ(req, &opendir_req);
78   ASSERT_EQ(req->fs_type, UV_FS_OPENDIR);
79   ASSERT_OK(req->result);
80   ASSERT_NOT_NULL(req->ptr);
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_OK(r);
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  */
TEST_IMPL(fs_readdir_empty_dir)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_OK(r);
119   ASSERT_EQ(opendir_req.fs_type, UV_FS_OPENDIR);
120   ASSERT_OK(opendir_req.result);
121   ASSERT_NOT_NULL(opendir_req.ptr);
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_OK(nb_entries_read);
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_OK(closedir_req.result);
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_OK(r);
151   ASSERT_OK(empty_opendir_cb_count);
152   ASSERT_OK(empty_closedir_cb_count);
153   r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
154   ASSERT_OK(r);
155   ASSERT_EQ(1, empty_opendir_cb_count);
156   ASSERT_EQ(1, empty_closedir_cb_count);
157   uv_fs_rmdir(uv_default_loop(), &rmdir_req, path, NULL);
158   uv_fs_req_cleanup(&rmdir_req);
159   MAKE_VALGRIND_HAPPY(uv_default_loop());
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 
non_existing_opendir_cb(uv_fs_t * req)170 static void non_existing_opendir_cb(uv_fs_t* req) {
171   ASSERT_PTR_EQ(req, &opendir_req);
172   ASSERT_EQ(req->fs_type, UV_FS_OPENDIR);
173   ASSERT_EQ(req->result, UV_ENOENT);
174   ASSERT_NULL(req->ptr);
175 
176   uv_fs_req_cleanup(req);
177   ++non_existing_opendir_cb_count;
178 }
179 
TEST_IMPL(fs_readdir_non_existing_dir)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_EQ(r, UV_ENOENT);
192   ASSERT_EQ(opendir_req.fs_type, UV_FS_OPENDIR);
193   ASSERT_EQ(opendir_req.result, UV_ENOENT);
194   ASSERT_NULL(opendir_req.ptr);
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_OK(r);
206   ASSERT_OK(non_existing_opendir_cb_count);
207   r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
208   ASSERT_OK(r);
209   ASSERT_EQ(1, non_existing_opendir_cb_count);
210 
211   MAKE_VALGRIND_HAPPY(uv_default_loop());
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 
file_opendir_cb(uv_fs_t * req)222 static void file_opendir_cb(uv_fs_t* req) {
223   ASSERT_PTR_EQ(req, &opendir_req);
224   ASSERT_EQ(req->fs_type, UV_FS_OPENDIR);
225   ASSERT_EQ(req->result, UV_ENOTDIR);
226   ASSERT_NULL(req->ptr);
227 
228   uv_fs_req_cleanup(req);
229   ++file_opendir_cb_count;
230 }
231 
TEST_IMPL(fs_readdir_file)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_EQ(r, UV_ENOTDIR);
245   ASSERT_EQ(opendir_req.fs_type, UV_FS_OPENDIR);
246   ASSERT_EQ(opendir_req.result, UV_ENOTDIR);
247   ASSERT_NULL(opendir_req.ptr);
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_OK(r);
257   ASSERT_OK(file_opendir_cb_count);
258   r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
259   ASSERT_OK(r);
260   ASSERT_EQ(1, file_opendir_cb_count);
261   MAKE_VALGRIND_HAPPY(uv_default_loop());
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 
non_empty_closedir_cb(uv_fs_t * req)275 static void non_empty_closedir_cb(uv_fs_t* req) {
276   ASSERT_PTR_EQ(req, &closedir_req);
277   ASSERT_OK(req->result);
278   uv_fs_req_cleanup(req);
279   ++non_empty_closedir_cb_count;
280 }
281 
non_empty_readdir_cb(uv_fs_t * req)282 static void non_empty_readdir_cb(uv_fs_t* req) {
283   uv_dir_t* dir;
284 
285   ASSERT_PTR_EQ(req, &readdir_req);
286   ASSERT_EQ(req->fs_type, UV_FS_READDIR);
287   dir = req->ptr;
288 
289   if (req->result == 0) {
290     uv_fs_req_cleanup(req);
291     ASSERT_EQ(3, non_empty_readdir_cb_count);
292     uv_fs_closedir(uv_default_loop(),
293                    &closedir_req,
294                    dir,
295                    non_empty_closedir_cb);
296   } else {
297     ASSERT_EQ(1, req->result);
298     ASSERT_PTR_EQ(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_EQ(dirents[0].type, UV_DIRENT_DIR);
305     else
306       ASSERT_EQ(dirents[0].type, UV_DIRENT_FILE);
307 #else
308     ASSERT_EQ(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 
non_empty_opendir_cb(uv_fs_t * req)322 static void non_empty_opendir_cb(uv_fs_t* req) {
323   uv_dir_t* dir;
324   int r;
325 
326   ASSERT_PTR_EQ(req, &opendir_req);
327   ASSERT_EQ(req->fs_type, UV_FS_OPENDIR);
328   ASSERT_OK(req->result);
329   ASSERT_NOT_NULL(req->ptr);
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_OK(r);
340   uv_fs_req_cleanup(req);
341   ++non_empty_opendir_cb_count;
342 }
343 
TEST_IMPL(fs_readdir_non_empty_dir)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_OK(r);
357 
358   /* Create two files synchronously. */
359   r = uv_fs_open(uv_default_loop(),
360                  &create_req,
361                  "test_dir/file1",
362                  UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR,
363                  NULL);
364   ASSERT_GE(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_OK(r);
371   uv_fs_req_cleanup(&close_req);
372 
373   r = uv_fs_open(uv_default_loop(),
374                  &create_req,
375                  "test_dir/file2",
376                  UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR,
377                  NULL);
378   ASSERT_GE(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_OK(r);
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_OK(r);
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_OK(r);
401   ASSERT_EQ(opendir_req.fs_type, UV_FS_OPENDIR);
402   ASSERT_OK(opendir_req.result);
403   ASSERT_NOT_NULL(opendir_req.ptr);
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_EQ(dirents[0].type, UV_DIRENT_DIR);
421     else
422       ASSERT_EQ(dirents[0].type, UV_DIRENT_FILE);
423 #else
424     ASSERT_EQ(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_EQ(3, entries_count);
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_OK(closedir_req.result);
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_OK(r);
449   ASSERT_OK(non_empty_opendir_cb_count);
450   ASSERT_OK(non_empty_closedir_cb_count);
451   r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
452   ASSERT_OK(r);
453   ASSERT_EQ(1, non_empty_opendir_cb_count);
454   ASSERT_EQ(1, non_empty_closedir_cb_count);
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(uv_default_loop());
461   return 0;
462  }
463