(root)/
glibc-2.38/
posix/
bug-glob2.c
       1  /* Test glob memory management.
       2     for the filesystem access functions.
       3     Copyright (C) 2001-2023 Free Software Foundation, Inc.
       4     This file is part of the GNU C Library.
       5  
       6     The GNU C Library is free software; you can redistribute it and/or
       7     modify it under the terms of the GNU Lesser General Public
       8     License as published by the Free Software Foundation; either
       9     version 2.1 of the License, or (at your option) any later version.
      10  
      11     The GNU C Library is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14     Lesser General Public License for more details.
      15  
      16     You should have received a copy of the GNU Lesser General Public
      17     License along with the GNU C Library; if not, see
      18     <https://www.gnu.org/licenses/>.  */
      19  
      20  #include <errno.h>
      21  #include <error.h>
      22  #include <dirent.h>
      23  #include <glob.h>
      24  #include <mcheck.h>
      25  #include <stdlib.h>
      26  #include <stdio.h>
      27  #include <string.h>
      28  #include <sys/stat.h>
      29  
      30  // #define DEBUG
      31  #ifdef DEBUG
      32  # define PRINTF(fmt, args...) \
      33    do					\
      34      {					\
      35        int save_errno = errno;		\
      36        printf (fmt, ##args);		\
      37        errno = save_errno;		\
      38      } while (0)
      39  #else
      40  # define PRINTF(fmt, args...)
      41  #endif
      42  
      43  #define LONG_NAME \
      44    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
      45    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
      46    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
      47    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
      48    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
      49    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
      50    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
      51    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
      52    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
      53    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      54  
      55  static struct
      56  {
      57    const char *name;
      58    int level;
      59    int type;
      60    mode_t mode;
      61  } filesystem[] =
      62  {
      63    { ".", 1, DT_DIR, 0755 },
      64    { "..", 1, DT_DIR, 0755 },
      65    { "dir", 1, DT_DIR, 0755 },
      66      { ".", 2, DT_DIR, 0755 },
      67      { "..", 2, DT_DIR, 0755 },
      68      { "readable", 2, DT_DIR, 0755 },
      69        { ".", 3, DT_DIR, 0755 },
      70        { "..", 3, DT_DIR, 0755 },
      71        { "a", 3, DT_REG, 0644 },
      72        { LONG_NAME, 3, DT_REG, 0644 },
      73      { "unreadable", 2, DT_DIR, 0111 },
      74        { ".", 3, DT_DIR, 0111 },
      75        { "..", 3, DT_DIR, 0755 },
      76        { "a", 3, DT_REG, 0644 },
      77      { "zz-readable", 2, DT_DIR, 0755 },
      78        { ".", 3, DT_DIR, 0755 },
      79        { "..", 3, DT_DIR, 0755 },
      80        { "a", 3, DT_REG, 0644 }
      81  };
      82  #define nfiles (sizeof (filesystem) / sizeof (filesystem[0]))
      83  
      84  
      85  typedef struct
      86  {
      87    int level;
      88    int idx;
      89    struct dirent d;
      90    char room_for_dirent[sizeof (LONG_NAME)];
      91  } my_DIR;
      92  
      93  
      94  static long int
      95  find_file (const char *s)
      96  {
      97    int level = 1;
      98    long int idx = 0;
      99  
     100    if (strcmp (s, ".") == 0)
     101      return 0;
     102  
     103    if (s[0] == '.' && s[1] == '/')
     104      s += 2;
     105  
     106    while (*s != '\0')
     107      {
     108        char *endp = strchrnul (s, '/');
     109  
     110        PRINTF ("looking for %.*s, level %d\n", (int) (endp - s), s, level);
     111  
     112        while (idx < nfiles && filesystem[idx].level >= level)
     113  	{
     114  	  if (filesystem[idx].level == level
     115  	      && memcmp (s, filesystem[idx].name, endp - s) == 0
     116  	      && filesystem[idx].name[endp - s] == '\0')
     117  	    break;
     118  	  ++idx;
     119  	}
     120  
     121        if (idx == nfiles || filesystem[idx].level < level)
     122  	{
     123  	  errno = ENOENT;
     124  	  return -1;
     125  	}
     126  
     127        if (*endp == '\0')
     128  	return idx + 1;
     129  
     130        if (filesystem[idx].type != DT_DIR
     131  	  && (idx + 1 >= nfiles
     132  	      || filesystem[idx].level >= filesystem[idx + 1].level))
     133  	{
     134  	  errno = ENOTDIR;
     135  	  return -1;
     136  	}
     137  
     138        ++idx;
     139  
     140        s = endp + 1;
     141        ++level;
     142      }
     143  
     144    errno = ENOENT;
     145    return -1;
     146  }
     147  
     148  
     149  static void *
     150  my_opendir (const char *s)
     151  {
     152    long int idx = find_file (s);
     153    my_DIR *dir;
     154  
     155    if (idx == -1)
     156      {
     157        PRINTF ("my_opendir(\"%s\") == NULL (%m)\n", s);
     158        return NULL;
     159      }
     160  
     161    if ((filesystem[idx].mode & 0400) == 0)
     162      {
     163        errno = EACCES;
     164        PRINTF ("my_opendir(\"%s\") == NULL (%m)\n", s);
     165        return NULL;
     166      }
     167  
     168    dir = (my_DIR *) malloc (sizeof (my_DIR));
     169    if (dir == NULL)
     170      {
     171        printf ("cannot allocate directory handle: %m\n");
     172        exit (EXIT_FAILURE);
     173      }
     174  
     175    dir->level = filesystem[idx].level;
     176    dir->idx = idx;
     177  
     178    PRINTF ("my_opendir(\"%s\") == { level: %d, idx: %ld }\n",
     179  	  s, filesystem[idx].level, idx);
     180  
     181    return dir;
     182  }
     183  
     184  
     185  static struct dirent *
     186  my_readdir (void *gdir)
     187  {
     188    my_DIR *dir = gdir;
     189  
     190    if (dir->idx == -1)
     191      {
     192        PRINTF ("my_readdir ({ level: %d, idx: %ld }) = NULL\n",
     193  	      dir->level, (long int) dir->idx);
     194        return NULL;
     195      }
     196  
     197    while (dir->idx < nfiles && filesystem[dir->idx].level > dir->level)
     198      ++dir->idx;
     199  
     200    if (dir->idx == nfiles || filesystem[dir->idx].level < dir->level)
     201      {
     202        dir->idx = -1;
     203        PRINTF ("my_readdir ({ level: %d, idx: %ld }) = NULL\n",
     204  	      dir->level, (long int) dir->idx);
     205        return NULL;
     206      }
     207  
     208    dir->d.d_ino = 1;		/* glob should not skip this entry.  */
     209  
     210    dir->d.d_type = filesystem[dir->idx].type;
     211  
     212    strcpy (dir->d.d_name, filesystem[dir->idx].name);
     213  
     214    PRINTF ("my_readdir ({ level: %d, idx: %ld }) = { d_ino: %ld, d_type: %d, d_name: \"%s\" }\n",
     215  	  dir->level, (long int) dir->idx, dir->d.d_ino, dir->d.d_type,
     216  	  dir->d.d_name);
     217  
     218    ++dir->idx;
     219  
     220    return &dir->d;
     221  }
     222  
     223  
     224  static void
     225  my_closedir (void *dir)
     226  {
     227    PRINTF ("my_closedir ()\n");
     228    free (dir);
     229  }
     230  
     231  
     232  /* We use this function for lstat as well since we don't have any.  */
     233  static int
     234  my_stat (const char *name, struct stat *st)
     235  {
     236    long int idx = find_file (name);
     237  
     238    if (idx == -1)
     239      {
     240        PRINTF ("my_stat (\"%s\", ...) = -1 (%m)\n", name);
     241        return -1;
     242      }
     243  
     244    memset (st, '\0', sizeof (*st));
     245  
     246    if (filesystem[idx].type == DT_UNKNOWN)
     247      st->st_mode = DTTOIF (idx + 1 < nfiles
     248  			  && filesystem[idx].level < filesystem[idx + 1].level
     249  			  ? DT_DIR : DT_REG) | filesystem[idx].mode;
     250    else
     251      st->st_mode = DTTOIF (filesystem[idx].type) | filesystem[idx].mode;
     252  
     253    PRINTF ("my_stat (\"%s\", { st_mode: %o }) = 0\n", name, st->st_mode);
     254  
     255    return 0;
     256  }
     257  
     258  
     259  static void
     260  init_glob_altdirfuncs (glob_t *pglob)
     261  {
     262    pglob->gl_closedir = my_closedir;
     263    pglob->gl_readdir = my_readdir;
     264    pglob->gl_opendir = my_opendir;
     265    pglob->gl_lstat = my_stat;
     266    pglob->gl_stat = my_stat;
     267  }
     268  
     269  
     270  int
     271  do_test (void)
     272  {
     273    mtrace ();
     274  
     275    glob_t gl;
     276    memset (&gl, 0, sizeof (gl));
     277    init_glob_altdirfuncs (&gl);
     278  
     279    if (glob ("dir/*able/*", GLOB_ERR | GLOB_ALTDIRFUNC, NULL, &gl)
     280        != GLOB_ABORTED)
     281      {
     282        puts ("glob did not fail with GLOB_ABORTED");
     283        exit (EXIT_FAILURE);
     284      }
     285  
     286    globfree (&gl);
     287  
     288    memset (&gl, 0, sizeof (gl));
     289    init_glob_altdirfuncs (&gl);
     290  
     291    gl.gl_offs = 3;
     292    if (glob ("dir2/*", GLOB_DOOFFS, NULL, &gl) != GLOB_NOMATCH)
     293      {
     294        puts ("glob did not fail with GLOB_NOMATCH");
     295        exit (EXIT_FAILURE);
     296      }
     297  
     298    globfree (&gl);
     299  
     300    muntrace ();
     301  
     302    return 0;
     303  }
     304  
     305  #define TEST_FUNCTION do_test ()
     306  #include "../test-skeleton.c"