1 /* Test glob compat symbol which avoid call GLOB_ALTDIRFUNC/gl_lstat.
2    Copyright (C) 2017-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 <glob.h>
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/types.h>
24 #include <dirent.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30 
31 #include <shlib-compat.h>
32 #include <support/check.h>
33 #include <support/temp_file.h>
34 
35 __typeof (glob) glob;
36 /* On alpha glob exists in version GLIBC_2_0, GLIBC_2_1, and GLIBC_2_27.
37    This test needs to access the version prior to GLIBC_2_27, which is
38    GLIBC_2_1 on alpha, GLIBC_2_0 elsewhere.  */
39 #ifdef __alpha__
40 compat_symbol_reference (libc, glob, glob, GLIBC_2_1);
41 #else
42 compat_symbol_reference (libc, glob, glob, GLIBC_2_0);
43 #endif
44 
45 /* Compat glob should not call gl_lstat since for some old binaries it
46    might be unitialized (for instance GNUmake).  Check if it is indeed
47    not called.  */
48 static bool stat_called;
49 static bool lstat_called;
50 
51 static struct
52 {
53   const char *name;
54   int level;
55   int type;
56 } filesystem[] =
57 {
58   { ".", 1, DT_DIR },
59   { "..", 1, DT_DIR },
60   { "dir1lev1", 1, DT_UNKNOWN },
61     { ".", 2, DT_DIR },
62     { "..", 2, DT_DIR },
63     { "file1lev2", 2, DT_REG },
64     { "file2lev2", 2, DT_REG },
65 };
66 static const size_t nfiles = sizeof (filesystem) / sizeof (filesystem [0]);
67 
68 typedef struct
69 {
70   int level;
71   int idx;
72   struct dirent d;
73   char room_for_dirent[NAME_MAX];
74 } my_DIR;
75 
76 static long int
find_file(const char * s)77 find_file (const char *s)
78 {
79   int level = 1;
80   long int idx = 0;
81 
82   while (s[0] == '/')
83     {
84       if (s[1] == '\0')
85 	{
86 	  s = ".";
87 	  break;
88 	}
89       ++s;
90     }
91 
92   if (strcmp (s, ".") == 0)
93     return 0;
94 
95   if (s[0] == '.' && s[1] == '/')
96     s += 2;
97 
98   while (*s != '\0')
99     {
100       char *endp = strchrnul (s, '/');
101 
102       while (idx < nfiles && filesystem[idx].level >= level)
103 	{
104 	  if (filesystem[idx].level == level
105 	      && memcmp (s, filesystem[idx].name, endp - s) == 0
106 	      && filesystem[idx].name[endp - s] == '\0')
107 	    break;
108 	  ++idx;
109 	}
110 
111       if (idx == nfiles || filesystem[idx].level < level)
112 	{
113 	  errno = ENOENT;
114 	  return -1;
115 	}
116 
117       if (*endp == '\0')
118 	return idx + 1;
119 
120       if (filesystem[idx].type != DT_DIR
121 	  && (idx + 1 >= nfiles
122 	      || filesystem[idx].level >= filesystem[idx + 1].level))
123 	{
124 	  errno = ENOTDIR;
125 	  return -1;
126 	}
127 
128       ++idx;
129 
130       s = endp + 1;
131       ++level;
132     }
133 
134   errno = ENOENT;
135   return -1;
136 }
137 
138 static void *
my_opendir(const char * s)139 my_opendir (const char *s)
140 {
141   long int idx = find_file (s);
142   if (idx == -1 || filesystem[idx].type != DT_DIR)
143     return NULL;
144 
145   my_DIR *dir = malloc (sizeof (my_DIR));
146   if (dir == NULL)
147     FAIL_EXIT1 ("cannot allocate directory handle");
148 
149   dir->level = filesystem[idx].level;
150   dir->idx = idx;
151 
152   return dir;
153 }
154 
155 static struct dirent *
my_readdir(void * gdir)156 my_readdir (void *gdir)
157 {
158   my_DIR *dir = gdir;
159 
160   if (dir->idx == -1)
161     return NULL;
162 
163   while (dir->idx < nfiles && filesystem[dir->idx].level > dir->level)
164     ++dir->idx;
165 
166   if (dir->idx == nfiles || filesystem[dir->idx].level < dir->level)
167     {
168       dir->idx = -1;
169       return NULL;
170     }
171 
172   dir->d.d_ino = 1;		/* glob should not skip this entry.  */
173 
174   dir->d.d_type = filesystem[dir->idx].type;
175 
176   strcpy (dir->d.d_name, filesystem[dir->idx].name);
177 
178   ++dir->idx;
179 
180   return &dir->d;
181 }
182 
183 static void
my_closedir(void * dir)184 my_closedir (void *dir)
185 {
186   free (dir);
187 }
188 
189 static int
my_stat(const char * name,struct stat * st)190 my_stat (const char *name, struct stat *st)
191 {
192   stat_called = true;
193 
194   long int idx = find_file (name);
195   if (idx == -1)
196     return -1;
197 
198   memset (st, '\0', sizeof (*st));
199 
200   if (filesystem[idx].type == DT_UNKNOWN)
201     st->st_mode = DTTOIF (idx + 1 < nfiles
202 			  && filesystem[idx].level < filesystem[idx + 1].level
203 			  ? DT_DIR : DT_REG) | 0777;
204   else
205     st->st_mode = DTTOIF (filesystem[idx].type) | 0777;
206   return 0;
207 }
208 
209 static int
my_lstat(const char * name,struct stat * st)210 my_lstat (const char *name, struct stat *st)
211 {
212   lstat_called = true;
213 
214   long int idx = find_file (name);
215   if (idx == -1)
216     return -1;
217 
218   memset (st, '\0', sizeof (*st));
219 
220   if (filesystem[idx].type == DT_UNKNOWN)
221     st->st_mode = DTTOIF (idx + 1 < nfiles
222 			  && filesystem[idx].level < filesystem[idx + 1].level
223 			  ? DT_DIR : DT_REG) | 0777;
224   else
225     st->st_mode = DTTOIF (filesystem[idx].type) | 0777;
226   return 0;
227 }
228 
229 static int
do_test(void)230 do_test (void)
231 {
232   glob_t gl;
233 
234   memset (&gl, '\0', sizeof (gl));
235 
236   gl.gl_closedir = my_closedir;
237   gl.gl_readdir = my_readdir;
238   gl.gl_opendir = my_opendir;
239   gl.gl_lstat = my_lstat;
240   gl.gl_stat = my_stat;
241 
242   int flags = GLOB_ALTDIRFUNC;
243 
244   stat_called = false;
245   lstat_called = false;
246 
247   TEST_VERIFY_EXIT (glob ("*/file1lev2", flags, NULL, &gl) == 0);
248   TEST_VERIFY_EXIT (gl.gl_pathc == 1);
249   TEST_VERIFY_EXIT (strcmp (gl.gl_pathv[0], "dir1lev1/file1lev2") == 0);
250 
251   TEST_VERIFY_EXIT (stat_called == true);
252   TEST_VERIFY_EXIT (lstat_called == false);
253 
254   return 0;
255 }
256 
257 #include <support/test-driver.c>
258