1/* Test for ftw function related to symbolic links for BZ #23501
2 Copyright (C) 2019-2024 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 <ftw.h>
20#include <stdio.h>
21#include <string.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <unistd.h>
25#include <fcntl.h>
26#include <errno.h>
27
28#include <support/support.h>
29#include <support/check.h>
30
31#define TSTDIR "tst-ftw-lnk.d"
32
33static void
34un (const char *file)
35{
36 struct stat st;
37 /* Does the file exist? */
38 if (lstat (file: file, buf: &st) < 0
39 && errno == ENOENT)
40 return;
41
42 /* If so, try to remove it. */
43 if (unlink (name: file) < 0)
44 FAIL_EXIT1 ("Unable to unlink %s", file);
45}
46
47static void
48debug_cb (const char *which, const char *fpath,
49 const struct stat *sb, int typeflags)
50{
51 const char *sb_type = "???";
52 const char *ftw_type = "???";
53
54 /* Coding style here is intentionally "wrong" to increase readability. */
55 if (S_ISREG (sb->st_mode)) sb_type = "REG";
56 if (S_ISDIR (sb->st_mode)) sb_type = "DIR";
57 if (S_ISLNK (sb->st_mode)) sb_type = "LNK";
58
59 if (typeflags == FTW_F) ftw_type = "F";
60 if (typeflags == FTW_D) ftw_type = "D";
61 if (typeflags == FTW_DNR) ftw_type = "DNR";
62 if (typeflags == FTW_DP) ftw_type = "DP";
63 if (typeflags == FTW_NS) ftw_type = "NS";
64 if (typeflags == FTW_SL) ftw_type = "SL";
65 if (typeflags == FTW_SLN) ftw_type = "SLN";
66
67 printf (format: "%s %5d %-3s %-3s %s\n", which, (int)(sb->st_ino % 100000), sb_type, ftw_type, fpath);
68}
69
70int good_cb = 0;
71#define EXPECTED_GOOD 12
72
73/* See if the stat buffer SB refers to the file AS_FNAME. */
74static void
75check_same_stats (const struct stat *sb, const char *as_fname)
76{
77 struct stat as;
78 if (lstat (file: as_fname, buf: &as) < 0)
79 FAIL_EXIT1 ("unable to stat %s for comparison", as_fname);
80
81 if (as.st_mode == sb->st_mode
82 && as.st_ino == sb->st_ino
83 && as.st_size == sb->st_size)
84 good_cb ++;
85 else
86 printf (format: "statbuf data doesn't match %s\n", as_fname);
87}
88
89static int
90callback_phys (const char *fpath, const struct stat *sb, int typeflags, struct FTW *ftwbuf)
91{
92 debug_cb (which: "P", fpath, sb, typeflags);
93
94 /* This callback is for when the FTW_PHYS flag is set. The results
95 should reflect the physical filesystem entry, not what it might
96 point to. */
97
98 /* link1-bad is a dangling symlink, but we're reporting on the link
99 anyway (ala lstat ()). */
100 if (strcmp (fpath, "./link1-bad") == 0)
101 {
102 if (S_ISLNK (sb->st_mode) && typeflags == FTW_SL)
103 good_cb ++;
104 else
105 printf (format: "link1-bad had wrong phys stats\n");
106
107 check_same_stats (sb, as_fname: "link1-bad");
108 }
109
110 /* link2-ok is a regular non-dangling symlink. */
111 if (strcmp (fpath, "./link2-ok") == 0)
112 {
113 if (S_ISLNK (sb->st_mode) && typeflags == FTW_SL)
114 good_cb ++;
115 else
116 printf (format: "link2-ok had wrong phys stats\n");
117
118 check_same_stats (sb, as_fname: "link2-ok");
119 }
120
121 /* This is the file link2-ok points to. */
122 if (strcmp (fpath, "./link2-tgt") == 0)
123 {
124 if (S_ISREG (sb->st_mode) && typeflags == FTW_F)
125 good_cb ++;
126 else
127 printf (format: "link2-tgt had wrong phys stats\n");
128
129 check_same_stats (sb, as_fname: "link2-tgt");
130 }
131
132 return 0;
133}
134
135static int
136callback_log (const char *fpath, const struct stat *sb, int typeflags, struct FTW *ftwbuf)
137{
138 debug_cb (which: "L", fpath, sb, typeflags);
139
140 /* This callback is for when the FTW_PHYS flags is NOT set. The
141 results should reflect the logical file, i.e. symlinks should be
142 followed. */
143
144 /* We would normally report what link1-bad links to, but link1-bad
145 is a dangling symlink. This is an exception to FTW_PHYS in that
146 we report FTW_SLN (dangling symlink) but the stat data is
147 correctly set to the link itself (ala lstat ()). */
148 if (strcmp (fpath, "./link1-bad") == 0)
149 {
150 if (S_ISLNK (sb->st_mode) && typeflags == FTW_SLN)
151 good_cb ++;
152 else
153 printf (format: "link1-bad had wrong logical stats\n");
154
155 check_same_stats (sb, as_fname: "link1-bad");
156 }
157
158 /* link2-ok points to link2-tgt, so we expect data reflecting
159 link2-tgt (ala stat ()). */
160 if (strcmp (fpath, "./link2-ok") == 0)
161 {
162 if (S_ISREG (sb->st_mode) && typeflags == FTW_F)
163 good_cb ++;
164 else
165 printf (format: "link2-ok had wrong logical stats\n");
166
167 check_same_stats (sb, as_fname: "link2-tgt");
168 }
169
170 /* This is the file link2-ok points to. */
171 if (strcmp (fpath, "./link2-tgt") == 0)
172 {
173 if (S_ISREG (sb->st_mode) && typeflags == FTW_F)
174 good_cb ++;
175 else
176 printf (format: "link2-tgt had wrong logical stats\n");
177
178 check_same_stats (sb, as_fname: "link2-tgt");
179 }
180
181 return 0;
182}
183
184static int
185do_test (void)
186{
187 struct stat st;
188
189 if (chdir (path: support_objdir_root) < 0)
190 FAIL_EXIT1 ("cannot chdir to objdir root");
191
192 if (chdir (path: "io") < 0)
193 FAIL_EXIT1 ("cannot chdir to objdir/io subdir");
194
195 if (stat (TSTDIR, buf: &st) >= 0)
196 {
197 /* Directory does exist, delete any potential conflicts. */
198 if (chdir (TSTDIR) < 0)
199 FAIL_EXIT1 ("cannot chdir to %s\n", TSTDIR);
200 un (file: "link1-bad");
201 un (file: "link1-tgt");
202 un (file: "link2-ok");
203 un (file: "link2-tgt");
204 }
205 else
206 {
207 /* Directory does not exist, create it. */
208 mkdir (TSTDIR, mode: 0777);
209 if (chdir (TSTDIR) < 0)
210 FAIL_EXIT1 ("cannot chdir to %s\n", TSTDIR);
211 }
212
213 /* At this point, we're inside our test directory, and need to
214 prepare it. */
215
216 if (symlink (from: "link1-tgt", to: "link1-bad") < 0)
217 FAIL_EXIT1 ("symlink link1-bad failed");
218 if (symlink (from: "link2-tgt", to: "link2-ok") < 0)
219 FAIL_EXIT1 ("symlink link2-ok failed");
220 if (open (file: "link2-tgt", O_RDWR|O_CREAT, 0777) < 0)
221 FAIL_EXIT1 ("create of link2-tgt failed");
222
223 /* Now we run the tests. */
224
225 nftw (dir: ".", func: callback_phys, descriptors: 10, FTW_PHYS);
226 nftw (dir: ".", func: callback_log, descriptors: 10, flag: 0);
227
228 /* Did we see the expected number of correct callbacks? */
229
230 if (good_cb != EXPECTED_GOOD)
231 {
232 FAIL_EXIT1 ("Saw %d good callbacks, expected %d\n",
233 good_cb, EXPECTED_GOOD);
234 }
235
236 return 0;
237}
238
239#include <support/test-driver.c>
240

source code of glibc/io/tst-ftw-lnk.c