1 /* Basic test for scandir function.
2    Copyright (C) 2015-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <stdbool.h>
20 #include <dirent.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #ifndef D
27 # define D(x) x
28 #endif
29 
30 static void prepare (void);
31 static int do_test (void);
32 #define PREPARE(argc, argv)     prepare ()
33 #define TEST_FUNCTION           do_test ()
34 #include "../test-skeleton.c"
35 
36 static const char *scandir_test_dir;
37 
38 static void
prepare(void)39 prepare (void)
40 {
41   size_t test_dir_len = strlen (test_dir);
42   static const char dir_name[] = "/tst-scandir.XXXXXX";
43 
44   size_t dirbuflen = test_dir_len + sizeof (dir_name);
45   char *dirbuf = malloc (dirbuflen);
46   if (dirbuf == NULL)
47     {
48       puts ("out of memory");
49       exit (1);
50     }
51 
52   snprintf (dirbuf, dirbuflen, "%s%s", test_dir, dir_name);
53   if (mkdtemp (dirbuf) == NULL)
54     {
55       puts ("cannot create temporary directory");
56       exit (1);
57     }
58 
59   add_temp_file (dirbuf);
60   scandir_test_dir = dirbuf;
61 }
62 
63 /* The directory should be empty save the . and .. files.  */
64 static void
verify_empty(const char * dirname)65 verify_empty (const char *dirname)
66 {
67   DIR *dir = opendir (dirname);
68   if (dir == NULL)
69     {
70       printf ("opendir (%s): %s\n", dirname, strerror (errno));
71       exit (1);
72     }
73 
74   struct dirent64 *d;
75   while ((d = readdir64 (dir)) != NULL)
76     if (strcmp (d->d_name, ".") != 0 && strcmp (d->d_name, "..") != 0)
77       {
78         printf ("temp directory contains file \"%s\"\n", d->d_name);
79         exit (1);
80       }
81 
82   closedir (dir);
83 }
84 
85 static void
make_file(const char * dirname,const char * filename)86 make_file (const char *dirname, const char *filename)
87 {
88   char *name = NULL;
89   if (asprintf (&name, "%s/%s", dirname, filename) < 0)
90     {
91       puts ("out of memory");
92       exit (1);
93     }
94 
95   int fd = open (name, O_WRONLY | O_CREAT | O_EXCL, 0600);
96   if (fd < 0)
97     {
98       printf ("cannot create \"%s\": %s\n", name, strerror (errno));
99       exit (1);
100     }
101   close (fd);
102 
103   free (name);
104 }
105 
106 static void
remove_file(const char * dirname,const char * filename)107 remove_file (const char *dirname, const char *filename)
108 {
109   char *name = NULL;
110   if (asprintf (&name, "%s/%s", dirname, filename) < 0)
111     {
112       puts ("out of memory");
113       exit (1);
114     }
115 
116   remove (name);
117 
118   free (name);
119 }
120 
121 static void
freelist(struct D (dirent)** list,size_t n)122 freelist (struct D(dirent) **list, size_t n)
123 {
124   for (size_t i = 0; i < n; ++i)
125     free (list[i]);
126   free (list);
127 }
128 
129 static int
select_a(const struct D (dirent)* d)130 select_a (const struct D(dirent) *d)
131 {
132   return d->d_name[0] == 'a';
133 }
134 
135 static int
do_test(void)136 do_test (void)
137 {
138   verify_empty (scandir_test_dir);
139 
140   make_file (scandir_test_dir, "c");
141   make_file (scandir_test_dir, "aa");
142   make_file (scandir_test_dir, "b");
143   make_file (scandir_test_dir, "a");
144 
145 
146   /* First a basic test with no select or compare functions.  */
147 
148   struct D(dirent) **list;
149   int n = D(scandir) (scandir_test_dir, &list, NULL, NULL);
150   if (n < 0)
151     {
152       printf ("scandir failed on %s: %s\n",
153               scandir_test_dir, strerror (errno));
154       return 1;
155     }
156   if (n != 6)
157     {
158       printf ("scandir returned %d entries instead of 6\n", n);
159       return 1;
160     }
161 
162   struct
163   {
164     const char *name;
165     bool found;
166   } expected[] =
167     {
168       { ".", },
169       { "..", },
170       { "a", },
171       { "aa", },
172       { "b", },
173       { "c", },
174     };
175 
176   /* Verify the results, ignoring the order.  */
177   for (int i = 0; i < n; ++i)
178     {
179       for (size_t j = 0; j < sizeof expected / sizeof expected[0]; ++j)
180         if (!strcmp (list[i]->d_name, expected[j].name))
181           {
182             expected[j].found = true;
183             goto found;
184           }
185 
186       printf ("scandir yields unexpected entry [%d] \"%s\"\n",
187               i, list[i]->d_name);
188       return 1;
189 
190     found:;
191     }
192 
193   for (size_t j = 0; j < sizeof expected / sizeof expected[0]; ++j)
194     if (!expected[j].found)
195       {
196         printf ("failed to produce \"%s\"\n", expected[j].name);
197         return 1;
198       }
199 
200   freelist (list, n);
201 
202 
203   /* Now a test with a comparison function.  */
204 
205   n = D(scandir) (scandir_test_dir, &list, NULL, &D(alphasort));
206   if (n < 0)
207     {
208       printf ("scandir failed on %s: %s\n",
209               scandir_test_dir, strerror (errno));
210       return 1;
211     }
212   if (n != 6)
213     {
214       printf ("scandir returned %d entries instead of 6\n", n);
215       return 1;
216     }
217 
218   assert (sizeof expected / sizeof expected[0] == 6);
219   for (int i = 0; i < n; ++i)
220     if (strcmp (list[i]->d_name, expected[i].name))
221       {
222         printf ("scandir yields [%d] of \"%s\", expected \"%s\"\n",
223                 i, list[i]->d_name, expected[i].name);
224         return 1;
225       }
226 
227   freelist (list, n);
228 
229 
230   /* Now a test with a select function but no comparison function.  */
231 
232   n = D(scandir) (scandir_test_dir, &list, &select_a, NULL);
233   if (n < 0)
234     {
235       printf ("scandir failed on %s: %s\n",
236               scandir_test_dir, strerror (errno));
237       return 1;
238     }
239   if (n != 2)
240     {
241       printf ("scandir returned %d entries instead of 2\n", n);
242       return 1;
243     }
244 
245   if (strcmp (list[0]->d_name, "a") && strcmp (list[0]->d_name, "aa"))
246     {
247       printf ("scandir yields [0] \"%s\", expected \"a\" or \"aa\"\n",
248               list[0]->d_name);
249       return 1;
250     }
251   if (strcmp (list[1]->d_name, "a") && strcmp (list[1]->d_name, "aa"))
252     {
253       printf ("scandir yields [1] \"%s\", expected \"a\" or \"aa\"\n",
254               list[1]->d_name);
255       return 1;
256     }
257   if (!strcmp (list[0]->d_name, list[1]->d_name))
258     {
259       printf ("scandir yields \"%s\" twice!\n", list[0]->d_name);
260       return 1;
261     }
262 
263   freelist (list, n);
264 
265 
266   /* Now a test with both functions.  */
267 
268   n = D(scandir) (scandir_test_dir, &list, &select_a, &D(alphasort));
269   if (n < 0)
270     {
271       printf ("scandir failed on %s: %s\n",
272               scandir_test_dir, strerror (errno));
273       return 1;
274     }
275   if (n != 2)
276     {
277       printf ("scandir returned %d entries instead of 2\n", n);
278       return 1;
279     }
280 
281   if (strcmp (list[0]->d_name, "a") || strcmp (list[1]->d_name, "aa"))
282     {
283       printf ("scandir yields {\"%s\", \"%s\"}, expected {\"a\", \"aa\"}\n",
284               list[0]->d_name, list[1]->d_name);
285       return 1;
286     }
287 
288   freelist (list, n);
289 
290 
291   /* Clean up the test directory.  */
292   remove_file (scandir_test_dir, "c");
293   remove_file (scandir_test_dir, "aa");
294   remove_file (scandir_test_dir, "b");
295   remove_file (scandir_test_dir, "a");
296 
297   return 0;
298 }
299