(root)/
glibc-2.38/
support/
temp_file.c
       1  /* Temporary file handling for tests.
       2     Copyright (C) 1998-2023 Free Software Foundation, Inc.
       3     Copyright The GNU Tools Authors.
       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  /* This is required to get an mkstemp which can create large files on
      21     some 32-bit platforms. */
      22  #define _FILE_OFFSET_BITS 64
      23  
      24  #include <support/check.h>
      25  #include <support/temp_file.h>
      26  #include <support/temp_file-internal.h>
      27  #include <support/support.h>
      28  
      29  #include <errno.h>
      30  #include <paths.h>
      31  #include <stdio.h>
      32  #include <stdlib.h>
      33  #include <string.h>
      34  #include <xunistd.h>
      35  
      36  /* List of temporary files.  */
      37  static struct temp_name_list
      38  {
      39    struct temp_name_list *next;
      40    char *name;
      41    pid_t owner;
      42    bool toolong;
      43  } *temp_name_list;
      44  
      45  /* Location of the temporary files.  Set by the test skeleton via
      46     support_set_test_dir.  The string is not be freed.  */
      47  static const char *test_dir = _PATH_TMP;
      48  
      49  /* Name of subdirectories in a too long temporary directory tree.  */
      50  static char toolong_subdir[NAME_MAX + 1];
      51  static bool toolong_initialized;
      52  static size_t toolong_path_max;
      53  
      54  static void
      55  add_temp_file_internal (const char *name, bool toolong)
      56  {
      57    struct temp_name_list *newp
      58      = (struct temp_name_list *) xcalloc (sizeof (*newp), 1);
      59    char *newname = strdup (name);
      60    if (newname != NULL)
      61      {
      62        newp->name = newname;
      63        newp->next = temp_name_list;
      64        newp->owner = getpid ();
      65        newp->toolong = toolong;
      66        temp_name_list = newp;
      67      }
      68    else
      69      free (newp);
      70  }
      71  
      72  void
      73  add_temp_file (const char *name)
      74  {
      75    add_temp_file_internal (name, false);
      76  }
      77  
      78  int
      79  create_temp_file_in_dir (const char *base, const char *dir, char **filename)
      80  {
      81    char *fname;
      82    int fd;
      83  
      84    fname = xasprintf ("%s/%sXXXXXX", dir, base);
      85  
      86    fd = mkstemp (fname);
      87    if (fd == -1)
      88      {
      89        printf ("cannot open temporary file '%s': %m\n", fname);
      90        free (fname);
      91        return -1;
      92      }
      93  
      94    add_temp_file (fname);
      95    if (filename != NULL)
      96      *filename = fname;
      97    else
      98      free (fname);
      99  
     100    return fd;
     101  }
     102  
     103  int
     104  create_temp_file (const char *base, char **filename)
     105  {
     106    return create_temp_file_in_dir (base, test_dir, filename);
     107  }
     108  
     109  static char *
     110  create_temp_directory_internal (const char *base, bool toolong)
     111  {
     112    char *path = xasprintf ("%s/%sXXXXXX", test_dir, base);
     113    if (mkdtemp (path) == NULL)
     114      {
     115        printf ("error: mkdtemp (\"%s\"): %m", path);
     116        exit (1);
     117      }
     118    add_temp_file_internal (path, toolong);
     119    return path;
     120  }
     121  
     122  char *
     123  support_create_temp_directory (const char *base)
     124  {
     125    return create_temp_directory_internal (base, false);
     126  }
     127  
     128  static void
     129  ensure_toolong_initialized (void)
     130  {
     131    if (!toolong_initialized)
     132      FAIL_EXIT1 ("uninitialized toolong directory tree\n");
     133  }
     134  
     135  static void
     136  initialize_toolong (const char *base)
     137  {
     138    long name_max = pathconf (base, _PC_NAME_MAX);
     139    name_max = (name_max < 0 ? 64
     140  	      : (name_max < sizeof (toolong_subdir) ? name_max
     141  		 : sizeof (toolong_subdir) - 1));
     142  
     143    long path_max = pathconf (base, _PC_PATH_MAX);
     144    path_max = (path_max < 0 ? 1024
     145  	      : path_max <= PTRDIFF_MAX ? path_max : PTRDIFF_MAX);
     146  
     147    /* Sanity check to ensure that the test does not create temporary directories
     148       in different filesystems because this API doesn't support it.  */
     149    if (toolong_initialized)
     150      {
     151        if (name_max != strlen (toolong_subdir))
     152  	FAIL_UNSUPPORTED ("name_max: Temporary directories in different"
     153  			  " filesystems not supported yet\n");
     154        if (path_max != toolong_path_max)
     155  	FAIL_UNSUPPORTED ("path_max: Temporary directories in different"
     156  			  " filesystems not supported yet\n");
     157        return;
     158      }
     159  
     160    toolong_path_max = path_max;
     161  
     162    size_t len = name_max;
     163    memset (toolong_subdir, 'X', len);
     164    toolong_initialized = true;
     165  }
     166  
     167  char *
     168  support_create_and_chdir_toolong_temp_directory (const char *basename)
     169  {
     170    char *base = create_temp_directory_internal (basename, true);
     171    xchdir (base);
     172  
     173    initialize_toolong (base);
     174  
     175    size_t sz = strlen (toolong_subdir);
     176  
     177    /* Create directories and descend into them so that the final path is larger
     178       than PATH_MAX.  */
     179    for (size_t i = 0; i <= toolong_path_max / sz; i++)
     180      {
     181        int ret = mkdir (toolong_subdir, S_IRWXU);
     182        if (ret != 0 && errno == ENAMETOOLONG)
     183  	FAIL_UNSUPPORTED ("Filesystem does not support creating too long "
     184  			  "directory trees\n");
     185        else if (ret != 0)
     186  	FAIL_EXIT1 ("Failed to create directory tree: %m\n");
     187        xchdir (toolong_subdir);
     188      }
     189    return base;
     190  }
     191  
     192  void
     193  support_chdir_toolong_temp_directory (const char *base)
     194  {
     195    ensure_toolong_initialized ();
     196  
     197    xchdir (base);
     198  
     199    size_t sz = strlen (toolong_subdir);
     200    for (size_t i = 0; i <= toolong_path_max / sz; i++)
     201      xchdir (toolong_subdir);
     202  }
     203  
     204  /* Helper functions called by the test skeleton follow.  */
     205  
     206  static void
     207  remove_toolong_subdirs (const char *base)
     208  {
     209    ensure_toolong_initialized ();
     210  
     211    if (chdir (base) != 0)
     212      {
     213        printf ("warning: toolong cleanup base failed: chdir (\"%s\"): %m\n",
     214  	      base);
     215        return;
     216      }
     217  
     218    /* Descend.  */
     219    int levels = 0;
     220    size_t sz = strlen (toolong_subdir);
     221    for (levels = 0; levels <= toolong_path_max / sz; levels++)
     222      if (chdir (toolong_subdir) != 0)
     223        {
     224  	printf ("warning: toolong cleanup failed: chdir (\"%s\"): %m\n",
     225  		toolong_subdir);
     226  	break;
     227        }
     228  
     229    /* Ascend and remove.  */
     230    while (--levels >= 0)
     231      {
     232        if (chdir ("..") != 0)
     233  	{
     234  	  printf ("warning: toolong cleanup failed: chdir (\"..\"): %m\n");
     235  	  return;
     236  	}
     237        if (remove (toolong_subdir) != 0)
     238  	{
     239  	  printf ("warning: could not remove subdirectory: %s: %m\n",
     240  		  toolong_subdir);
     241  	  return;
     242  	}
     243      }
     244  }
     245  
     246  void
     247  support_delete_temp_files (void)
     248  {
     249    pid_t pid = getpid ();
     250    while (temp_name_list != NULL)
     251      {
     252        /* Only perform the removal if the path was registered in the same
     253  	 process, as identified by the PID.  (This assumes that the
     254  	 parent process which registered the temporary file sticks
     255  	 around, to prevent PID reuse.)  */
     256        if (temp_name_list->owner == pid)
     257  	{
     258  	  if (temp_name_list->toolong)
     259  	    remove_toolong_subdirs (temp_name_list->name);
     260  
     261  	  if (remove (temp_name_list->name) != 0)
     262  	    printf ("warning: could not remove temporary file: %s: %m\n",
     263  		    temp_name_list->name);
     264  	}
     265        free (temp_name_list->name);
     266  
     267        struct temp_name_list *next = temp_name_list->next;
     268        free (temp_name_list);
     269        temp_name_list = next;
     270      }
     271  }
     272  
     273  void
     274  support_print_temp_files (FILE *f)
     275  {
     276    if (temp_name_list != NULL)
     277      {
     278        struct temp_name_list *n;
     279        fprintf (f, "temp_files=(\n");
     280        for (n = temp_name_list; n != NULL; n = n->next)
     281          fprintf (f, "  '%s'\n", n->name);
     282        fprintf (f, ")\n");
     283      }
     284  }
     285  
     286  void
     287  support_set_test_dir (const char *path)
     288  {
     289    test_dir = path;
     290  }