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__
40compat_symbol_reference (libc, glob, glob, GLIBC_2_1);
41#else
42compat_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. */
48static bool stat_called;
49static bool lstat_called;
50
51static 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};
66static const size_t nfiles = sizeof (filesystem) / sizeof (filesystem [0]);
67
68typedef struct
69{
70 int level;
71 int idx;
72 struct dirent d;
73 char room_for_dirent[NAME_MAX];
74} my_DIR;
75
76static long int
77find_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: s, c: '/');
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
138static void *
139my_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 (size: 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
155static struct dirent *
156my_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
183static void
184my_closedir (void *dir)
185{
186 free (ptr: dir);
187}
188
189static int
190my_stat (const char *name, struct stat *st)
191{
192 stat_called = true;
193
194 long int idx = find_file (s: 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
209static int
210my_lstat (const char *name, struct stat *st)
211{
212 lstat_called = true;
213
214 long int idx = find_file (s: 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
229static int
230do_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

source code of glibc/posix/tst-glob_lstat_compat.c