summaryrefslogtreecommitdiff
path: root/m4/getcwd-path-max.m4
blob: ec2063d656c4ca19d043cbc6d102d7e76a6253c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#serial 5
# Check for several getcwd bugs with long paths.
# If so, arrange to compile the wrapper function.

# This is necessary for at least GNU libc on linux-2.4.19 and 2.4.20.
# I've heard that this is due to a Linux kernel bug, and that it has
# been fixed between 2.4.21-pre3 and 2.4.21-pre4.  */

# Copyright (C) 2003, 2004 Free Software Foundation, Inc.

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

# From Jim Meyering

AC_DEFUN([gl_FUNC_GETCWD_PATH_MAX],
[
  AC_CHECK_DECLS_ONCE(getcwd)
  AC_CHECK_HEADERS_ONCE(fcntl.h)
  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
  AC_CACHE_CHECK([whether getcwd handles long paths properly],
                 gl_cv_func_getcwd_path_max,
  [
  # Arrange for deletion of the temporary directory this test creates.
  ac_clean_files="$ac_clean_files confdir3"
  AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#if HAVE_FCNTL_H
# include <fcntl.h>
#endif

#ifndef AT_FDCWD
# define AT_FDCWD 0
#endif
#ifdef ENAMETOOLONG
# define is_ENAMETOOLONG(x) ((x) == ENAMETOOLONG)
#else
# define is_ENAMETOOLONG(x) 0
#endif

/* Don't get link errors because mkdir is redefined to rpl_mkdir.  */
#undef mkdir

#ifndef S_IRWXU
# define S_IRWXU 0700
#endif

/* The length of this name must be 8.  */
#define DIR_NAME "confdir3"
#define DIR_NAME_LEN 8
#define DIR_NAME_SIZE (DIR_NAME_LEN + 1)

/* The length of "../".  */
#define DOTDOTSLASH_LEN 3

/* Leftover bytes in the buffer, to work around library or OS bugs.  */
#define BUF_SLOP 20

int
main (void)
{
#ifndef PATH_MAX
  /* The Hurd doesn't define this, so getcwd can't exhibit the bug --
     at least not on a local file system.  And if we were to start worrying
     about remote file systems, we'd have to enable the wrapper function
     all of the time, just to be safe.  That's not worth the cost.  */
  exit (0);
#elif ((INT_MAX / (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1) \
        - DIR_NAME_SIZE - BUF_SLOP) \
       <= PATH_MAX)
  /* FIXME: Assuming there's a system for which this is true,
     this should be done in a compile test.  */
  exit (0);
#else
  char buf[PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1)
	   + DIR_NAME_SIZE + BUF_SLOP];
  char *cwd = getcwd (buf, PATH_MAX);
  size_t initial_cwd_len;
  size_t cwd_len;
  int fail = 0;
  size_t n_chdirs = 0;

  if (cwd == NULL)
    exit (1);

  cwd_len = initial_cwd_len = strlen (cwd);

  while (1)
    {
      size_t dotdot_max = PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN);
      char *c = NULL;

      cwd_len += DIR_NAME_SIZE;
      /* If mkdir or chdir fails, be pessimistic and consider that
	 as a failure, too.  */
      if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
	{
	  fail = 1;
	  break;
	}

      if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE)
	{
	  c = getcwd (buf, PATH_MAX);
	  if (c || errno != ERANGE)
	    {
	      fail = 1;
	      break;
	    }
	}

      if (dotdot_max <= cwd_len - initial_cwd_len)
	{
	  if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len)
	    break;
	  c = getcwd (buf, cwd_len + 1);
	  if (!c && (AT_FDCWD || !is_ENAMETOOLONG (errno)))
	    {
	      fail = 1;
	      break;
	    }
	}

      if (c && strlen (c) != cwd_len)
	{
	  fail = 1;
	  break;
	}
      ++n_chdirs;
    }

  /* Leaving behind such a deep directory is not polite.
     So clean up here, right away, even though the driving
     shell script would also clean up.  */
  {
    size_t i;

    /* Unlink first, in case the chdir failed.  */
    unlink (DIR_NAME);
    for (i = 0; i <= n_chdirs; i++)
      {
	if (chdir ("..") < 0)
	  break;
	rmdir (DIR_NAME);
      }
  }

  exit (fail);
#endif
}
  ]])],
       [gl_cv_func_getcwd_path_max=yes],
       [gl_cv_func_getcwd_path_max=no],
       [gl_cv_func_getcwd_path_max=no])])
])