Ticket #62994: deepdir.c

File deepdir.c, 6.4 KB (added by ballapete (Peter "Pete" Dyballa), 3 years ago)

Test programme of Paul Eggert from gnulib support

Line 
1#define HAVE_UNISTD_H 11
2#define HAVE_SYS_PARAM_H 1
3#include <errno.h>
4#include <stdlib.h>
5#if HAVE_UNISTD_H
6# include <unistd.h>
7#else
8# include <direct.h>
9#endif
10#include <string.h>
11#include <limits.h>
12#include <sys/stat.h>
13#include <sys/types.h>
14#include <fcntl.h>
15
16
17/* Arrange to define PATH_MAX, like "pathmax.h" does. */
18#if HAVE_UNISTD_H
19# include <unistd.h>
20#endif
21#include <limits.h>
22#if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
23# include <sys/param.h>
24#endif
25#if !defined PATH_MAX && defined MAXPATHLEN
26# define PATH_MAX MAXPATHLEN
27#endif
28#ifdef __hpux
29# undef PATH_MAX
30# define PATH_MAX 1024
31#endif
32#if defined _WIN32 && ! defined __CYGWIN__
33# undef PATH_MAX
34# define PATH_MAX 260
35#endif
36
37
38#ifndef AT_FDCWD
39# define AT_FDCWD 0
40#endif
41#ifdef ENAMETOOLONG
42# define is_ENAMETOOLONG(x) ((x) == ENAMETOOLONG)
43#else
44# define is_ENAMETOOLONG(x) 0
45#endif
46
47/* Use the getcwd function, not any macro.  */
48#undef getcwd
49
50
51
52#if defined _WIN32 && !defined __CYGWIN__
53#define access    _access
54#define chdir     _chdir
55#define chmod     _chmod
56#define close     _close
57#define creat     _creat
58#define dup       _dup
59#define dup2      _dup2
60#define ecvt      _ecvt
61#define execl     _execl
62#define execle    _execle
63#define execlp    _execlp
64#define execv     _execv
65#define execve    _execve
66#define execvp    _execvp
67#define execvpe   _execvpe
68#define fcloseall _fcloseall
69#define fcvt      _fcvt
70#define fdopen    _fdopen
71#define fileno    _fileno
72#define gcvt      _gcvt
73#define getcwd    _getcwd
74#define getpid    _getpid
75#define getw      _getw
76#define isatty    _isatty
77#define j0        _j0
78#define j1        _j1
79#define jn        _jn
80#define lfind     _lfind
81#define lsearch   _lsearch
82#define lseek     _lseek
83#define memccpy   _memccpy
84#define mkdir     _mkdir
85#define mktemp    _mktemp
86#define open      _open
87#define putenv    _putenv
88#define putw      _putw
89#define read      _read
90#define rmdir     _rmdir
91#define strdup    _strdup
92#define swab      _swab
93#define tempnam   _tempnam
94#define tzset     _tzset
95#define umask     _umask
96#define unlink    _unlink
97#define utime     _utime
98#define wcsdup    _wcsdup
99#define write     _write
100#define y0        _y0
101#define y1        _y1
102#define yn        _yn
103#endif
104
105
106#ifndef S_IRWXU
107# define S_IRWXU 0700
108#endif
109
110/* The length of this name must be 8.  */
111#define DIR_NAME "confdir3"
112#define DIR_NAME_LEN 8
113#define DIR_NAME_SIZE (DIR_NAME_LEN + 1)
114
115/* The length of "../".  */
116#define DOTDOTSLASH_LEN 3
117
118/* Leftover bytes in the buffer, to work around library or OS bugs.  */
119#define BUF_SLOP 20
120
121int
122main ()
123{
124#ifndef PATH_MAX
125  /* The Hurd doesn't define this, so getcwd can't exhibit the bug --
126     at least not on a local file system.  And if we were to start worrying
127     about remote file systems, we'd have to enable the wrapper function
128     all of the time, just to be safe.  That's not worth the cost.  */
129  exit (0);
130#elif ((INT_MAX / (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1) \
131        - DIR_NAME_SIZE - BUF_SLOP) \
132       <= PATH_MAX)
133  /* FIXME: Assuming there's a system for which this is true,
134     this should be done in a compile test.  */
135  exit (0);
136#else
137  char buf[PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1)
138           + DIR_NAME_SIZE + BUF_SLOP];
139  char *cwd = getcwd (buf, PATH_MAX);
140  size_t initial_cwd_len;
141  size_t cwd_len;
142  int fail = 0;
143  size_t n_chdirs = 0;
144
145  if (cwd == NULL)
146    exit (10);
147
148  cwd_len = initial_cwd_len = strlen (cwd);
149
150  while (1)
151    {
152      size_t dotdot_max = PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN);
153      char *c = NULL;
154
155      cwd_len += DIR_NAME_SIZE;
156      /* If mkdir or chdir fails, it could be that this system cannot create
157         any file with an absolute name longer than PATH_MAX, such as cygwin.
158         If so, leave fail as 0, because the current working directory can't
159         be too long for getcwd if it can't even be created.  On Linux with
160         the 9p file system, mkdir fails with error EINVAL when cwd_len gets
161         too long; ignore this failure because the getcwd() system call
162         produces good results whereas the gnulib substitute calls getdents64
163         which fails with error EPROTO.
164         For other errors, be pessimistic and consider that as a failure,
165         too.  */
166      if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
167        {
168          if (! (errno == ERANGE || is_ENAMETOOLONG (errno)))
169            #ifdef __linux__
170            if (! (errno == EINVAL))
171            #endif
172              fail = 20;
173          break;
174        }
175
176      if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE)
177        {
178          struct stat sb;
179
180          c = getcwd (buf, PATH_MAX);
181          if (!c && errno == ENOENT)
182            {
183              fail = 11;
184              break;
185            }
186          if (c)
187            {
188              fail = 31;
189              break;
190            }
191          if (! (errno == ERANGE || is_ENAMETOOLONG (errno)))
192            {
193              fail = 21;
194              break;
195            }
196
197          /* Our replacement needs to be able to stat() long ../../paths,
198             so generate a path larger than PATH_MAX to check,
199             avoiding the replacement if we can't stat().  */
200          c = getcwd (buf, cwd_len + 1);
201          if (c && !AT_FDCWD && stat (c, &sb) != 0 && is_ENAMETOOLONG (errno))
202            {
203              fail = 32;
204              break;
205            }
206        }
207
208      if (dotdot_max <= cwd_len - initial_cwd_len)
209        {
210          if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len)
211            break;
212          c = getcwd (buf, cwd_len + 1);
213          if (!c)
214            {
215              if (! (errno == ERANGE || errno == ENOENT
216                     || is_ENAMETOOLONG (errno)))
217                {
218                  fail = 22;
219                  break;
220                }
221              if (AT_FDCWD || errno == ERANGE || errno == ENOENT)
222                {
223                  fail = 12;
224                  break;
225                }
226            }
227        }
228
229      if (c && strlen (c) != cwd_len)
230        {
231          fail = 23;
232          break;
233        }
234      ++n_chdirs;
235    }
236
237  /* Leaving behind such a deep directory is not polite.
238     So clean up here, right away, even though the driving
239     shell script would also clean up.  */
240  {
241    size_t i;
242
243    /* Try rmdir first, in case the chdir failed.  */
244    rmdir (DIR_NAME);
245    for (i = 0; i <= n_chdirs; i++)
246      {
247        if (chdir ("..") < 0)
248          break;
249        if (rmdir (DIR_NAME) != 0)
250          break;
251      }
252  }
253
254  exit (fail);
255#endif
256}