Bump to 1.14.1
[platform/upstream/augeas.git] / tests / test-getcwd.c
1 /* Test of getcwd() function.
2    Copyright (C) 2009-2016 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 #include <config.h>
18
19 #include <unistd.h>
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28
29 #include "pathmax.h"
30 #include "macros.h"
31
32 #if ! HAVE_GETPAGESIZE
33 # define getpagesize() 0
34 #endif
35
36 /* This size is chosen to be larger than PATH_MAX (4k), yet smaller than
37    the 16kB pagesize on ia64 linux.  Those conditions make the code below
38    trigger a bug in glibc's getcwd implementation before 2.4.90-10.  */
39 #define TARGET_LEN (5 * 1024)
40
41 #if defined HAVE_OPENAT || (defined GNULIB_OPENAT && defined HAVE_FDOPENDIR)
42 # define HAVE_OPENAT_SUPPORT 1
43 #else
44 # define HAVE_OPENAT_SUPPORT 0
45 #endif
46
47 /* Keep this test in sync with m4/getcwd-abort-bug.m4.  */
48 static int
49 test_abort_bug (void)
50 {
51   char *cwd;
52   size_t initial_cwd_len;
53   int fail = 0;
54
55   /* The bug is triggered when PATH_MAX < getpagesize (), so skip
56      this relatively expensive and invasive test if that's not true.  */
57 #ifdef PATH_MAX
58   int bug_possible = PATH_MAX < getpagesize ();
59 #else
60   int bug_possible = 0;
61 #endif
62   if (! bug_possible)
63     return 0;
64
65   cwd = getcwd (NULL, 0);
66   if (cwd == NULL)
67     return 2;
68
69   initial_cwd_len = strlen (cwd);
70   free (cwd);
71
72   if (HAVE_OPENAT_SUPPORT)
73     {
74       static char const dir_name[] = "confdir-14B---";
75       size_t desired_depth = ((TARGET_LEN - 1 - initial_cwd_len)
76                               / sizeof dir_name);
77       size_t d;
78       for (d = 0; d < desired_depth; d++)
79         {
80           if (mkdir (dir_name, S_IRWXU) < 0 || chdir (dir_name) < 0)
81             {
82               if (! (errno == ERANGE || errno == ENAMETOOLONG
83                      || errno == ENOENT))
84                 fail = 3; /* Unable to construct deep hierarchy.  */
85               break;
86             }
87         }
88
89       /* If libc has the bug in question, this invocation of getcwd
90          results in a failed assertion.  */
91       cwd = getcwd (NULL, 0);
92       if (cwd == NULL)
93         fail = 4; /* getcwd didn't assert, but it failed for a long name
94                      where the answer could have been learned.  */
95       free (cwd);
96
97       /* Call rmdir first, in case the above chdir failed.  */
98       rmdir (dir_name);
99       while (0 < d--)
100         {
101           if (chdir ("..") < 0)
102             {
103               fail = 5;
104               break;
105             }
106           rmdir (dir_name);
107         }
108     }
109
110   return fail;
111 }
112
113 /* The length of this name must be 8.  */
114 #define DIR_NAME "confdir3"
115 #define DIR_NAME_LEN 8
116 #define DIR_NAME_SIZE (DIR_NAME_LEN + 1)
117
118 /* The length of "../".  */
119 #define DOTDOTSLASH_LEN 3
120
121 /* Leftover bytes in the buffer, to work around library or OS bugs.  */
122 #define BUF_SLOP 20
123
124 /* Keep this test in sync with m4/getcwd-path-max.m4.  */
125 static int
126 test_long_name (void)
127 {
128 #ifndef PATH_MAX
129   /* The Hurd doesn't define this, so getcwd can't exhibit the bug --
130      at least not on a local file system.  And if we were to start worrying
131      about remote file systems, we'd have to enable the wrapper function
132      all of the time, just to be safe.  That's not worth the cost.  */
133   return 0;
134 #elif ((INT_MAX / (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1) \
135         - DIR_NAME_SIZE - BUF_SLOP) \
136        <= PATH_MAX)
137   /* FIXME: Assuming there's a system for which this is true,
138      this should be done in a compile test.  */
139   return 0;
140 #else
141   char buf[PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1)
142            + DIR_NAME_SIZE + BUF_SLOP];
143   char *cwd = getcwd (buf, PATH_MAX);
144   size_t initial_cwd_len;
145   size_t cwd_len;
146   int fail = 0;
147   size_t n_chdirs = 0;
148
149   if (cwd == NULL)
150     return 1;
151
152   cwd_len = initial_cwd_len = strlen (cwd);
153
154   while (1)
155     {
156 # ifdef HAVE_GETCWD_SHORTER
157       /* On OS/X <= 10.9 for example, we're restricted to shorter paths
158          as lstat() doesn't support more than PATH_MAX.  */
159       size_t dotdot_max = PATH_MAX * 2;
160 # else
161       size_t dotdot_max = PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN);
162 # endif
163       char *c = NULL;
164
165       cwd_len += DIR_NAME_SIZE;
166       /* If mkdir or chdir fails, it could be that this system cannot create
167          any file with an absolute name longer than PATH_MAX, such as cygwin.
168          If so, leave fail as 0, because the current working directory can't
169          be too long for getcwd if it can't even be created.  For other
170          errors, be pessimistic and consider that as a failure, too.  */
171       if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
172         {
173           if (! (errno == ERANGE || errno == ENAMETOOLONG || errno == ENOENT))
174             fail = 2;
175           break;
176         }
177
178       if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE)
179         {
180           c = getcwd (buf, PATH_MAX);
181           if (!c && errno == ENOENT)
182             {
183               fail = 3;
184               break;
185             }
186           if (c)
187             {
188               fail = 4;
189               break;
190             }
191           if (! (errno == ERANGE || errno == ENAMETOOLONG))
192             {
193               fail = 5;
194               break;
195             }
196         }
197
198       if (dotdot_max <= cwd_len - initial_cwd_len)
199         {
200           if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len)
201             break;
202           c = getcwd (buf, cwd_len + 1);
203           if (!c)
204             {
205               if (! (errno == ERANGE || errno == ENOENT
206                      || errno == ENAMETOOLONG))
207                 {
208                   fail = 6;
209                   break;
210                 }
211               if (HAVE_OPENAT_SUPPORT || errno == ERANGE || errno == ENOENT)
212                 {
213                   fail = 7;
214                   break;
215                 }
216             }
217         }
218
219       if (c && strlen (c) != cwd_len)
220         {
221           fail = 8;
222           break;
223         }
224       ++n_chdirs;
225     }
226
227   /* Leaving behind such a deep directory is not polite.
228      So clean up here, right away, even though the driving
229      shell script would also clean up.  */
230   {
231     size_t i;
232
233     /* Try rmdir first, in case the chdir failed.  */
234     rmdir (DIR_NAME);
235     for (i = 0; i <= n_chdirs; i++)
236       {
237         if (chdir ("..") < 0)
238           break;
239         if (rmdir (DIR_NAME) != 0)
240           break;
241       }
242   }
243
244   return fail;
245 #endif
246 }
247
248 int
249 main (int argc, char **argv)
250 {
251   return test_abort_bug () * 10 + test_long_name ();
252 }