(root)/
glibc-2.38/
posix/
tst-glob_lstat_compat.c
       1  /* Test glob compat symbol which avoid call GLOB_ALTDIRFUNC/gl_lstat.
       2     Copyright (C) 2017-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <glob.h>
      20  #include <errno.h>
      21  #include <stdlib.h>
      22  #include <string.h>
      23  #include <sys/types.h>
      24  #include <dirent.h>
      25  #include <sys/types.h>
      26  #include <sys/stat.h>
      27  #include <unistd.h>
      28  #include <stdbool.h>
      29  #include <stdio.h>
      30  
      31  #include <shlib-compat.h>
      32  #include <support/check.h>
      33  #include <support/temp_file.h>
      34  
      35  __typeof (glob) glob;
      36  /* On alpha glob exists in version GLIBC_2_0, GLIBC_2_1, and GLIBC_2_27.
      37     This test needs to access the version prior to GLIBC_2_27, which is
      38     GLIBC_2_1 on alpha, GLIBC_2_0 elsewhere.  */
      39  #ifdef __alpha__
      40  compat_symbol_reference (libc, glob, glob, GLIBC_2_1);
      41  #else
      42  compat_symbol_reference (libc, glob, glob, GLIBC_2_0);
      43  #endif
      44  
      45  /* Compat glob should not call gl_lstat since for some old binaries it
      46     might be uninitialized (for instance GNUmake).  Check if it is indeed
      47     not called.  */
      48  static bool stat_called;
      49  static bool lstat_called;
      50  
      51  static struct
      52  {
      53    const char *name;
      54    int level;
      55    int type;
      56  } filesystem[] =
      57  {
      58    { ".", 1, DT_DIR },
      59    { "..", 1, DT_DIR },
      60    { "dir1lev1", 1, DT_UNKNOWN },
      61      { ".", 2, DT_DIR },
      62      { "..", 2, DT_DIR },
      63      { "file1lev2", 2, DT_REG },
      64      { "file2lev2", 2, DT_REG },
      65  };
      66  static const size_t nfiles = sizeof (filesystem) / sizeof (filesystem [0]);
      67  
      68  typedef struct
      69  {
      70    int level;
      71    int idx;
      72    struct dirent d;
      73    char room_for_dirent[NAME_MAX];
      74  } my_DIR;
      75  
      76  static long int
      77  find_file (const char *s)
      78  {
      79    int level = 1;
      80    long int idx = 0;
      81  
      82    while (s[0] == '/')
      83      {
      84        if (s[1] == '\0')
      85  	{
      86  	  s = ".";
      87  	  break;
      88  	}
      89        ++s;
      90      }
      91  
      92    if (strcmp (s, ".") == 0)
      93      return 0;
      94  
      95    if (s[0] == '.' && s[1] == '/')
      96      s += 2;
      97  
      98    while (*s != '\0')
      99      {
     100        char *endp = strchrnul (s, '/');
     101  
     102        while (idx < nfiles && filesystem[idx].level >= level)
     103  	{
     104  	  if (filesystem[idx].level == level
     105  	      && memcmp (s, filesystem[idx].name, endp - s) == 0
     106  	      && filesystem[idx].name[endp - s] == '\0')
     107  	    break;
     108  	  ++idx;
     109  	}
     110  
     111        if (idx == nfiles || filesystem[idx].level < level)
     112  	{
     113  	  errno = ENOENT;
     114  	  return -1;
     115  	}
     116  
     117        if (*endp == '\0')
     118  	return idx + 1;
     119  
     120        if (filesystem[idx].type != DT_DIR
     121  	  && (idx + 1 >= nfiles
     122  	      || filesystem[idx].level >= filesystem[idx + 1].level))
     123  	{
     124  	  errno = ENOTDIR;
     125  	  return -1;
     126  	}
     127  
     128        ++idx;
     129  
     130        s = endp + 1;
     131        ++level;
     132      }
     133  
     134    errno = ENOENT;
     135    return -1;
     136  }
     137  
     138  static void *
     139  my_opendir (const char *s)
     140  {
     141    long int idx = find_file (s);
     142    if (idx == -1 || filesystem[idx].type != DT_DIR)
     143      return NULL;
     144  
     145    my_DIR *dir = malloc (sizeof (my_DIR));
     146    if (dir == NULL)
     147      FAIL_EXIT1 ("cannot allocate directory handle");
     148  
     149    dir->level = filesystem[idx].level;
     150    dir->idx = idx;
     151  
     152    return dir;
     153  }
     154  
     155  static struct dirent *
     156  my_readdir (void *gdir)
     157  {
     158    my_DIR *dir = gdir;
     159  
     160    if (dir->idx == -1)
     161      return NULL;
     162  
     163    while (dir->idx < nfiles && filesystem[dir->idx].level > dir->level)
     164      ++dir->idx;
     165  
     166    if (dir->idx == nfiles || filesystem[dir->idx].level < dir->level)
     167      {
     168        dir->idx = -1;
     169        return NULL;
     170      }
     171  
     172    dir->d.d_ino = 1;		/* glob should not skip this entry.  */
     173  
     174    dir->d.d_type = filesystem[dir->idx].type;
     175  
     176    strcpy (dir->d.d_name, filesystem[dir->idx].name);
     177  
     178    ++dir->idx;
     179  
     180    return &dir->d;
     181  }
     182  
     183  static void
     184  my_closedir (void *dir)
     185  {
     186    free (dir);
     187  }
     188  
     189  static int
     190  my_stat (const char *name, struct stat *st)
     191  {
     192    stat_called = true;
     193  
     194    long int idx = find_file (name);
     195    if (idx == -1)
     196      return -1;
     197  
     198    memset (st, '\0', sizeof (*st));
     199  
     200    if (filesystem[idx].type == DT_UNKNOWN)
     201      st->st_mode = DTTOIF (idx + 1 < nfiles
     202  			  && filesystem[idx].level < filesystem[idx + 1].level
     203  			  ? DT_DIR : DT_REG) | 0777;
     204    else
     205      st->st_mode = DTTOIF (filesystem[idx].type) | 0777;
     206    return 0;
     207  }
     208  
     209  static int
     210  my_lstat (const char *name, struct stat *st)
     211  {
     212    lstat_called = true;
     213  
     214    long int idx = find_file (name);
     215    if (idx == -1)
     216      return -1;
     217  
     218    memset (st, '\0', sizeof (*st));
     219  
     220    if (filesystem[idx].type == DT_UNKNOWN)
     221      st->st_mode = DTTOIF (idx + 1 < nfiles
     222  			  && filesystem[idx].level < filesystem[idx + 1].level
     223  			  ? DT_DIR : DT_REG) | 0777;
     224    else
     225      st->st_mode = DTTOIF (filesystem[idx].type) | 0777;
     226    return 0;
     227  }
     228  
     229  static int
     230  do_test (void)
     231  {
     232    glob_t gl;
     233  
     234    memset (&gl, '\0', sizeof (gl));
     235  
     236    gl.gl_closedir = my_closedir;
     237    gl.gl_readdir = my_readdir;
     238    gl.gl_opendir = my_opendir;
     239    gl.gl_lstat = my_lstat;
     240    gl.gl_stat = my_stat;
     241  
     242    int flags = GLOB_ALTDIRFUNC;
     243  
     244    stat_called = false;
     245    lstat_called = false;
     246  
     247    TEST_VERIFY_EXIT (glob ("*/file1lev2", flags, NULL, &gl) == 0);
     248    TEST_VERIFY_EXIT (gl.gl_pathc == 1);
     249    TEST_VERIFY_EXIT (strcmp (gl.gl_pathv[0], "dir1lev1/file1lev2") == 0);
     250  
     251    TEST_VERIFY_EXIT (stat_called == true);
     252    TEST_VERIFY_EXIT (lstat_called == false);
     253  
     254    return 0;
     255  }
     256  
     257  #include <support/test-driver.c>