1 /* Test for ftw function related to symbolic links for BZ #23501
2    Copyright (C) 2019-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 <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 
33 static void
un(const char * file)34 un (const char *file)
35 {
36   struct stat st;
37   /* Does the file exist?  */
38   if (lstat (file, &st) < 0
39       && errno == ENOENT)
40     return;
41 
42   /* If so, try to remove it.  */
43   if (unlink (file) < 0)
44     FAIL_EXIT1 ("Unable to unlink %s", file);
45 }
46 
47 static void
debug_cb(const char * which,const char * fpath,const struct stat * sb,int typeflags)48 debug_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 ("%s %5d %-3s %-3s %s\n", which, (int)(sb->st_ino % 100000), sb_type, ftw_type, fpath);
68 }
69 
70 int good_cb = 0;
71 #define EXPECTED_GOOD 12
72 
73 /* See if the stat buffer SB refers to the file AS_FNAME.  */
74 static void
check_same_stats(const struct stat * sb,const char * as_fname)75 check_same_stats (const struct stat *sb, const char *as_fname)
76 {
77   struct stat as;
78   if (lstat (as_fname, &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 ("statbuf data doesn't match %s\n", as_fname);
87 }
88 
89 static int
callback_phys(const char * fpath,const struct stat * sb,int typeflags,struct FTW * ftwbuf)90 callback_phys (const char *fpath, const struct stat *sb, int typeflags, struct FTW *ftwbuf)
91 {
92   debug_cb ("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 ("link1-bad had wrong phys stats\n");
106 
107       check_same_stats (sb, "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 ("link2-ok had wrong phys stats\n");
117 
118       check_same_stats (sb, "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 ("link2-tgt had wrong phys stats\n");
128 
129       check_same_stats (sb, "link2-tgt");
130     }
131 
132   return 0;
133 }
134 
135 static int
callback_log(const char * fpath,const struct stat * sb,int typeflags,struct FTW * ftwbuf)136 callback_log (const char *fpath, const struct stat *sb, int typeflags, struct FTW *ftwbuf)
137 {
138   debug_cb ("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 ("link1-bad had wrong logical stats\n");
154 
155       check_same_stats (sb, "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 ("link2-ok had wrong logical stats\n");
166 
167       check_same_stats (sb, "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 ("link2-tgt had wrong logical stats\n");
177 
178       check_same_stats (sb, "link2-tgt");
179     }
180 
181   return 0;
182 }
183 
184 static int
do_test(void)185 do_test (void)
186 {
187   struct stat st;
188 
189   if (chdir (support_objdir_root) < 0)
190     FAIL_EXIT1 ("cannot chdir to objdir root");
191 
192   if (chdir ("io") < 0)
193     FAIL_EXIT1 ("cannot chdir to objdir/io subdir");
194 
195   if (stat (TSTDIR, &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 ("link1-bad");
201       un ("link1-tgt");
202       un ("link2-ok");
203       un ("link2-tgt");
204     }
205   else
206     {
207       /* Directory does not exist, create it.  */
208       mkdir (TSTDIR, 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 ("link1-tgt", "link1-bad") < 0)
217     FAIL_EXIT1 ("symlink link1-bad failed");
218   if (symlink ("link2-tgt", "link2-ok") < 0)
219     FAIL_EXIT1 ("symlink link2-ok failed");
220   if (open ("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 (".", callback_phys, 10, FTW_PHYS);
226   nftw (".", callback_log, 10, 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