aboutsummaryrefslogtreecommitdiff
blob: 416fa016e3c362f89348d177fcc87f2c8569be90 (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
/* This comes from gnulib: gnulib/m4/getcwd-path-max.m4
 * On glibc systems, we currently exit(1).  Sandbox should not get in the
 * way of this, nor should it crash.
 */

#include "tests.h"

#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 ()
{
  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, it could be that this system cannot create
	 any file with an absolute name longer than PATH_MAX, such as cygwin.
	 If so, leave fail as 0, because the current working directory can't
	 be too long for getcwd if it can't even be created.  For other
	 errors, be pessimistic and consider that as a failure, too.  */
      if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
	{
	  if (! (errno == ERANGE || ENAMETOOLONG == errno))
	    fail = 2;
	  break;
	}

      if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE)
	{
	  c = getcwd (buf, PATH_MAX);
	  if (!c && errno == ENOENT)
	    {
	      fail = 1;
	      break;
	    }
	  if (c || ! (errno == ERANGE || ENAMETOOLONG == errno))
	    {
	      fail = 2;
	      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)
	    {
	      if (! (errno == ERANGE || errno == ENOENT
		     || ENAMETOOLONG == errno))
		{
		  fail = 2;
		  break;
		}
	      if (AT_FDCWD || errno == ERANGE || errno == ENOENT)
		{
		  fail = 1;
		  break;
		}
	    }
	}

      if (c && strlen (c) != cwd_len)
	{
	  fail = 2;
	  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;

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

  exit (fail);
}