root/maint/gnulib/tests/test-getcwd.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. test_abort_bug
  2. test_long_name
  3. main

   1 /* Test of getcwd() function.
   2    Copyright (C) 2009-2021 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 <https://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 "qemu.h"
  31 #include "macros.h"
  32 
  33 #if !(HAVE_GETPAGESIZE || defined getpagesize)
  34 # define getpagesize() 0
  35 #endif
  36 
  37 /* This size is chosen to be larger than PATH_MAX (4k), yet smaller than
  38    the 16kB pagesize on ia64 linux.  Those conditions make the code below
  39    trigger a bug in glibc's getcwd implementation before 2.4.90-10.  */
  40 #define TARGET_LEN (5 * 1024)
  41 
  42 #if defined HAVE_OPENAT || (defined GNULIB_OPENAT && defined HAVE_FDOPENDIR)
  43 # define HAVE_OPENAT_SUPPORT 1
  44 #else
  45 # define HAVE_OPENAT_SUPPORT 0
  46 #endif
  47 
  48 /* Keep this test in sync with m4/getcwd-abort-bug.m4.  */
  49 static int
  50 test_abort_bug (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  51 {
  52   char *cwd;
  53   size_t initial_cwd_len;
  54   int fail = 0;
  55 
  56   /* The bug is triggered when PATH_MAX < getpagesize (), so skip
  57      this relatively expensive and invasive test if that's not true.  */
  58 #ifdef PATH_MAX
  59   int bug_possible = PATH_MAX < getpagesize ();
  60 #else
  61   int bug_possible = 0;
  62 #endif
  63   if (! bug_possible)
  64     return 0;
  65 
  66   cwd = getcwd (NULL, 0);
  67   if (cwd == NULL)
  68     return 2;
  69 
  70   initial_cwd_len = strlen (cwd);
  71   free (cwd);
  72 
  73   if (HAVE_OPENAT_SUPPORT)
  74     {
  75       static char const dir_name[] = "confdir-14B---";
  76       size_t desired_depth = ((TARGET_LEN - 1 - initial_cwd_len)
  77                               / sizeof dir_name);
  78       size_t d;
  79       for (d = 0; d < desired_depth; d++)
  80         {
  81           if (mkdir (dir_name, S_IRWXU) < 0 || chdir (dir_name) < 0)
  82             {
  83               if (! (errno == ERANGE || errno == ENAMETOOLONG
  84                      || errno == ENOENT))
  85                 fail = 3; /* Unable to construct deep hierarchy.  */
  86               break;
  87             }
  88         }
  89 
  90       /* If libc has the bug in question, this invocation of getcwd
  91          results in a failed assertion.  */
  92       cwd = getcwd (NULL, 0);
  93       if (cwd == NULL)
  94         fail = 4; /* getcwd didn't assert, but it failed for a long name
  95                      where the answer could have been learned.  */
  96       free (cwd);
  97 
  98       /* Call rmdir first, in case the above chdir failed.  */
  99       rmdir (dir_name);
 100       while (0 < d--)
 101         {
 102           if (chdir ("..") < 0)
 103             {
 104               fail = 5;
 105               break;
 106             }
 107           rmdir (dir_name);
 108         }
 109     }
 110 
 111   return fail;
 112 }
 113 
 114 /* The length of this name must be 8.  */
 115 #define DIR_NAME "confdir3"
 116 #define DIR_NAME_LEN 8
 117 #define DIR_NAME_SIZE (DIR_NAME_LEN + 1)
 118 
 119 /* The length of "../".  */
 120 #define DOTDOTSLASH_LEN 3
 121 
 122 /* Leftover bytes in the buffer, to work around library or OS bugs.  */
 123 #define BUF_SLOP 20
 124 
 125 /* Keep this test in sync with m4/getcwd-path-max.m4.  */
 126 static int
 127 test_long_name (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 128 {
 129 #ifndef PATH_MAX
 130   /* The Hurd doesn't define this, so getcwd can't exhibit the bug --
 131      at least not on a local file system.  And if we were to start worrying
 132      about remote file systems, we'd have to enable the wrapper function
 133      all of the time, just to be safe.  That's not worth the cost.  */
 134   return 0;
 135 #elif ((INT_MAX / (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1) \
 136         - DIR_NAME_SIZE - BUF_SLOP) \
 137        <= PATH_MAX)
 138   /* FIXME: Assuming there's a system for which this is true,
 139      this should be done in a compile test.  */
 140   return 0;
 141 #else
 142   /* For a process running under QEMU user-mode, the "/" directory is not
 143      really the root directory, but the value of the QEMU_LD_PREFIX environment
 144      variable or of the -L command-line option.  This causes the logic from
 145      glibc/sysdeps/posix/getcwd.c to fail.  In this case, skip the test.  */
 146   if (is_running_under_qemu_user ())
 147     return 77;
 148 
 149   char buf[PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1)
 150            + DIR_NAME_SIZE + BUF_SLOP];
 151   char *cwd = getcwd (buf, PATH_MAX);
 152   size_t initial_cwd_len;
 153   size_t cwd_len;
 154   int fail = 0;
 155   size_t n_chdirs = 0;
 156 
 157   if (cwd == NULL)
 158     return 1;
 159 
 160   cwd_len = initial_cwd_len = strlen (cwd);
 161 
 162   while (1)
 163     {
 164 # ifdef HAVE_GETCWD_SHORTER
 165       /* On OS/X <= 10.9 for example, we're restricted to shorter paths
 166          as lstat() doesn't support more than PATH_MAX.  */
 167       size_t dotdot_max = PATH_MAX * 2;
 168 # else
 169       size_t dotdot_max = PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN);
 170 # endif
 171       char *c = NULL;
 172 
 173       cwd_len += DIR_NAME_SIZE;
 174       /* If mkdir or chdir fails, it could be that this system cannot create
 175          any file with an absolute name longer than PATH_MAX, such as cygwin.
 176          If so, leave fail as 0, because the current working directory can't
 177          be too long for getcwd if it can't even be created.  On Linux with
 178          the 9p file system, mkdir fails with error EINVAL when cwd_len gets
 179          too long; ignore this failure because the getcwd() system call
 180          produces good results whereas the gnulib substitute calls getdents64
 181          which fails with error EPROTO.
 182          For other errors, be pessimistic and consider that as a failure,
 183          too.  */
 184       if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
 185         {
 186           if (! (errno == ERANGE || errno == ENAMETOOLONG || errno == ENOENT))
 187             #ifdef __linux__
 188             if (! (errno == EINVAL))
 189             #endif
 190               fail = 2;
 191           break;
 192         }
 193 
 194       if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE)
 195         {
 196           c = getcwd (buf, PATH_MAX);
 197           if (!c && errno == ENOENT)
 198             {
 199               fail = 3;
 200               break;
 201             }
 202           if (c)
 203             {
 204               fail = 4;
 205               break;
 206             }
 207           if (! (errno == ERANGE || errno == ENAMETOOLONG))
 208             {
 209               fail = 5;
 210               break;
 211             }
 212         }
 213 
 214       if (dotdot_max <= cwd_len - initial_cwd_len)
 215         {
 216           if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len)
 217             break;
 218           c = getcwd (buf, cwd_len + 1);
 219           if (!c)
 220             {
 221               if (! (errno == ERANGE || errno == ENOENT
 222                      || errno == ENAMETOOLONG))
 223                 {
 224                   fail = 6;
 225                   break;
 226                 }
 227               if (HAVE_OPENAT_SUPPORT || errno == ERANGE || errno == ENOENT)
 228                 {
 229                   fail = 7;
 230                   break;
 231                 }
 232             }
 233         }
 234 
 235       if (c && strlen (c) != cwd_len)
 236         {
 237           fail = 8;
 238           break;
 239         }
 240       ++n_chdirs;
 241     }
 242 
 243   /* Leaving behind such a deep directory is not polite.
 244      So clean up here, right away, even though the driving
 245      shell script would also clean up.  */
 246   {
 247     size_t i;
 248 
 249     /* Try rmdir first, in case the chdir failed.  */
 250     rmdir (DIR_NAME);
 251     for (i = 0; i <= n_chdirs; i++)
 252       {
 253         if (chdir ("..") < 0)
 254           break;
 255         if (rmdir (DIR_NAME) != 0)
 256           break;
 257       }
 258   }
 259 
 260   return fail;
 261 #endif
 262 }
 263 
 264 int
 265 main (int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 266 {
 267   int err1 = test_abort_bug ();
 268   int err2 = test_long_name ();
 269   return err1 * 10 + (err1 != 0 && err2 == 77 ? 0 : err2);
 270 }

/* [previous][next][first][last][top][bottom][index][help] */