(root)/
glibc-2.38/
posix/
tst-spawn-chdir.c
       1  /* Test the posix_spawn_file_actions_addchdir_np function.
       2     Copyright (C) 2018-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 <array_length.h>
      20  #include <errno.h>
      21  #include <fcntl.h>
      22  #include <spawn.h>
      23  #include <stdbool.h>
      24  #include <stdlib.h>
      25  #include <string.h>
      26  #include <support/check.h>
      27  #include <support/support.h>
      28  #include <support/temp_file.h>
      29  #include <support/test-driver.h>
      30  #include <support/xstdio.h>
      31  #include <support/xunistd.h>
      32  #include <unistd.h>
      33  
      34  /* Reads the file at PATH, which must consist of exactly one line.
      35     Removes the line terminator at the end of the file.  */
      36  static char *
      37  read_one_line (const char *path)
      38  {
      39    FILE *fp = xfopen (path, "r");
      40    char *buffer = NULL;
      41    size_t length = 0;
      42    ssize_t ret = getline (&buffer, &length, fp);
      43    if (ferror (fp))
      44      FAIL_EXIT1 ("getline: %m");
      45    if (ret < 1)
      46      FAIL_EXIT1 ("getline returned %zd", ret);
      47    if (fgetc (fp) != EOF)
      48      FAIL_EXIT1 ("trailing bytes in %s", path);
      49    if (ferror (fp))
      50      FAIL_EXIT1 ("fgetc: %m");
      51    xfclose (fp);
      52    if (buffer[ret - 1] != '\n')
      53      FAIL_EXIT1 ("missing line terminator in %s", path);
      54    buffer[ret - 1] = 0;
      55    return buffer;
      56  }
      57  
      58  /* Return the path to the "pwd" program.  */
      59  const char *
      60  get_pwd_program (void)
      61  {
      62    const char *const paths[] = { "/bin/pwd", "/usr/bin/pwd" };
      63    for (size_t i = 0; i < array_length (paths); ++i)
      64      if (access (paths[i], X_OK) == 0)
      65        return paths[i];
      66    FAIL_EXIT1 ("cannot find pwd program");
      67  }
      68  
      69  /* Adds chdir operations to ACTIONS, using PATH.  If DO_FCHDIR, use
      70     the open function and TMPFD to emulate chdir using fchdir.  */
      71  static void
      72  add_chdir (posix_spawn_file_actions_t *actions, const char *path,
      73             bool do_fchdir, int tmpfd)
      74  {
      75    if (do_fchdir)
      76      {
      77        TEST_COMPARE (posix_spawn_file_actions_addopen
      78                      (actions, tmpfd, path, O_DIRECTORY | O_RDONLY, 0), 0);
      79        TEST_COMPARE (posix_spawn_file_actions_addfchdir_np
      80                      (actions, tmpfd), 0);
      81        TEST_COMPARE (posix_spawn_file_actions_addclose (actions, tmpfd), 0);
      82      }
      83    else
      84      TEST_COMPARE (posix_spawn_file_actions_addchdir_np (actions, path), 0);
      85  }
      86  
      87  static int
      88  do_test (void)
      89  {
      90    /* Directory for temporary file data.  Each subtest uses a numeric
      91       subdirectory.  */
      92    char *directory = support_create_temp_directory ("tst-spawn-chdir-");
      93    {
      94      /* Avoid symbolic links, to get more consistent behavior from the
      95         pwd command.  */
      96      char *tmp = realpath (directory, NULL);
      97      if (tmp == NULL)
      98        FAIL_EXIT1 ("realpath: %m");
      99      free (directory);
     100      directory = tmp;
     101    }
     102  
     103    char *original_cwd = get_current_dir_name ();
     104    if (original_cwd == NULL)
     105      FAIL_EXIT1 ("get_current_dir_name: %m");
     106  
     107    int iteration = 0;
     108    for (int do_spawnp = 0; do_spawnp < 2; ++do_spawnp)
     109      for (int do_overwrite = 0; do_overwrite < 2; ++do_overwrite)
     110        for (int do_fchdir = 0; do_fchdir < 2; ++do_fchdir)
     111          {
     112            /* This subtest does not make sense for fchdir.  */
     113            if (do_overwrite && do_fchdir)
     114              continue;
     115  
     116            ++iteration;
     117            if (test_verbose > 0)
     118              printf ("info: iteration=%d do_spawnp=%d do_overwrite=%d"
     119                      " do_fchdir=%d\n",
     120                      iteration, do_spawnp, do_overwrite, do_fchdir);
     121  
     122            /* The "pwd" program runs in this directory.  */
     123            char *iteration_directory = xasprintf ("%s/%d", directory, iteration);
     124            add_temp_file (iteration_directory);
     125            xmkdir (iteration_directory, 0777);
     126  
     127            /* This file receives output from "pwd".  */
     128            char *output_file_path
     129              = xasprintf ("%s/output-file", iteration_directory);
     130            add_temp_file (output_file_path);
     131  
     132            /* This subdirectory is used for chdir ordering checks.  */
     133            char *subdir_path = xasprintf ("%s/subdir", iteration_directory);
     134            add_temp_file (subdir_path);
     135            xmkdir (subdir_path, 0777);
     136  
     137            /* Also used for checking the order of actions.  */
     138            char *probe_file_path
     139              = xasprintf ("%s/subdir/probe-file", iteration_directory);
     140            add_temp_file (probe_file_path);
     141            TEST_COMPARE (access (probe_file_path, F_OK), -1);
     142            TEST_COMPARE (errno, ENOENT);
     143  
     144            /* This symbolic link is used in a relative path with
     145               posix_spawn.  */
     146            char *pwd_symlink_path
     147              = xasprintf ("%s/subdir/pwd-symlink", iteration_directory);
     148            xsymlink (get_pwd_program (), pwd_symlink_path);
     149            add_temp_file (pwd_symlink_path);
     150  
     151            posix_spawn_file_actions_t actions;
     152            TEST_COMPARE (posix_spawn_file_actions_init (&actions), 0);
     153            add_chdir (&actions, subdir_path, do_fchdir, 4);
     154            TEST_COMPARE (posix_spawn_file_actions_addopen
     155                          (&actions, 3, /* Arbitrary unused descriptor.  */
     156                           "probe-file",
     157                           O_WRONLY | O_CREAT | O_EXCL, 0777), 0);
     158            TEST_COMPARE (posix_spawn_file_actions_addclose (&actions, 3), 0);
     159            /* Run the actual in iteration_directory.  */
     160            add_chdir (&actions, "..", do_fchdir, 5);
     161            TEST_COMPARE (posix_spawn_file_actions_addopen
     162                          (&actions, STDOUT_FILENO, "output-file",
     163                           O_WRONLY | O_CREAT | O_EXCL, 0777), 0);
     164  
     165            /* Check that posix_spawn_file_actions_addchdir_np made a copy
     166               of the path.  */
     167            if (do_overwrite)
     168              subdir_path[0] = '\0';
     169  
     170            char *const argv[] = { (char *) "pwd", NULL };
     171            char *const envp[] = { NULL } ;
     172            pid_t pid;
     173            if (do_spawnp)
     174              TEST_COMPARE (posix_spawnp (&pid, "pwd", &actions,
     175                                          NULL, argv, envp), 0);
     176            else
     177              TEST_COMPARE (posix_spawn (&pid, "subdir/pwd-symlink", &actions,
     178                                         NULL, argv, envp), 0);
     179            TEST_VERIFY (pid > 0);
     180            int status;
     181            xwaitpid (pid, &status, 0);
     182            TEST_COMPARE (status, 0);
     183  
     184            /* Check that the current directory did not change.  */
     185            {
     186              char *cwd = get_current_dir_name ();
     187              if (cwd == NULL)
     188                FAIL_EXIT1 ("get_current_dir_name: %m");
     189              TEST_COMPARE_BLOB (original_cwd, strlen (original_cwd),
     190                                 cwd, strlen (cwd));
     191              free (cwd);
     192            }
     193  
     194  
     195            /* Check the output from "pwd".  */
     196            {
     197              char *pwd = read_one_line (output_file_path);
     198              TEST_COMPARE_BLOB (iteration_directory, strlen (iteration_directory),
     199                                 pwd, strlen (pwd));
     200              free (pwd);
     201            }
     202  
     203            /* This file must now exist.  */
     204            TEST_COMPARE (access (probe_file_path, F_OK), 0);
     205  
     206            TEST_COMPARE (posix_spawn_file_actions_destroy (&actions), 0);
     207            free (pwd_symlink_path);
     208            free (probe_file_path);
     209            free (subdir_path);
     210            free (output_file_path);
     211          }
     212  
     213    free (directory);
     214  
     215    return 0;
     216  }
     217  
     218  #include <support/test-driver.c>