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
27static uv_fs_t opendir_req;
28static uv_fs_t readdir_req;
29static uv_fs_t closedir_req;
30
31static uv_dirent_t dirents[1];
32
33static int empty_opendir_cb_count;
34static int empty_closedir_cb_count;
35
36static 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
49static 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
57static 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 r = uv_fs_closedir(uv_default_loop(),
66 &closedir_req,
67 dir,
68 empty_closedir_cb);
69 ASSERT(r == 0);
70 uv_fs_req_cleanup(req);
71}
72
73static 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 */
98TEST_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
168static int non_existing_opendir_cb_count;
169
170static 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
180TEST_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
220static int file_opendir_cb_count;
221
222static 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
232TEST_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
271static int non_empty_opendir_cb_count;
272static int non_empty_readdir_cb_count;
273static int non_empty_closedir_cb_count;
274
275static 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
282static 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
322static 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
344TEST_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