(root)/
gcc-13.2.0/
libstdc++-v3/
testsuite/
util/
testsuite_fs.h
       1  // -*- C++ -*-
       2  // Filesystem utils for the C++ library testsuite.
       3  //
       4  // Copyright (C) 2014-2023 Free Software Foundation, Inc.
       5  //
       6  // This file is part of the GNU ISO C++ Library.  This library is free
       7  // software; you can redistribute it and/or modify it under the
       8  // terms of the GNU General Public License as published by the
       9  // Free Software Foundation; either version 3, or (at your option)
      10  // any later version.
      11  //
      12  // This library is distributed in the hope that it will be useful,
      13  // but WITHOUT ANY WARRANTY; without even the implied warranty of
      14  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15  // GNU General Public License for more details.
      16  //
      17  // You should have received a copy of the GNU General Public License along
      18  // with this library; see the file COPYING3.  If not see
      19  // <http://www.gnu.org/licenses/>.
      20  //
      21  
      22  #ifndef _TESTSUITE_FS_H
      23  #define _TESTSUITE_FS_H 1
      24  
      25  // Assume we want std::filesystem in C++17, unless USE_FILESYSTEM_TS defined:
      26  #if __cplusplus >= 201703L && ! defined USE_FILESYSTEM_TS
      27  #include <filesystem>
      28  namespace test_fs = std::filesystem;
      29  #else
      30  #include <experimental/filesystem>
      31  namespace test_fs = std::experimental::filesystem;
      32  #endif
      33  #include <algorithm>
      34  #include <fstream>
      35  #include <random>   // std::random_device
      36  #include <string>
      37  #include <system_error>
      38  #include <cstdio>
      39  #include <unistd.h> // unlink, close, getpid, geteuid
      40  
      41  #if defined(_GNU_SOURCE) || _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200112L
      42  #include <stdlib.h> // mkstemp
      43  #endif
      44  
      45  #ifndef _GLIBCXX_HAVE_SYMLINK
      46  #define NO_SYMLINKS
      47  #endif
      48  
      49  #if !defined (_GLIBCXX_HAVE_SYS_STATVFS_H) \
      50    && !defined (_GLIBCXX_FILESYSTEM_IS_WINDOWS)
      51  #define NO_SPACE
      52  #endif
      53  
      54  #if !(_GLIBCXX_HAVE_SYS_STAT_H \
      55        && (_GLIBCXX_USE_UTIMENSAT || _GLIBCXX_USE_UTIME))
      56  #define NO_LAST_WRITE_TIME 1
      57  #endif
      58  
      59  namespace __gnu_test
      60  {
      61  #define PATH_CHK(p1, p2, fn) \
      62      if ( p1.fn() != p2.fn() ) \
      63        throw test_fs::filesystem_error("comparing '" #fn "' failed", p1, p2, \
      64  	  std::make_error_code(std::errc::invalid_argument) )
      65  
      66    void
      67    compare_paths(const test_fs::path& p1,
      68  		const test_fs::path& p2)
      69    {
      70      PATH_CHK( p1, p2, native );
      71      PATH_CHK( p1, p2, string );
      72      PATH_CHK( p1, p2, empty );
      73      PATH_CHK( p1, p2, has_root_path );
      74      PATH_CHK( p1, p2, has_root_name );
      75      PATH_CHK( p1, p2, has_root_directory );
      76      PATH_CHK( p1, p2, has_relative_path );
      77      PATH_CHK( p1, p2, has_parent_path );
      78      PATH_CHK( p1, p2, has_filename );
      79      PATH_CHK( p1, p2, has_stem );
      80      PATH_CHK( p1, p2, has_extension );
      81      PATH_CHK( p1, p2, is_absolute );
      82      PATH_CHK( p1, p2, is_relative );
      83      auto d1 = std::distance(p1.begin(), p1.end());
      84      auto d2 = std::distance(p2.begin(), p2.end());
      85      if (d1 != d2)
      86        throw test_fs::filesystem_error(
      87  	  "distance(begin1, end1) != distance(begin2, end2)", p1, p2,
      88  	  std::make_error_code(std::errc::invalid_argument) );
      89      if (!std::equal(p1.begin(), p1.end(), p2.begin()))
      90        throw test_fs::filesystem_error(
      91  	  "!equal(begin1, end1, begin2)", p1, p2,
      92  	  std::make_error_code(std::errc::invalid_argument) );
      93  
      94    }
      95  
      96    const std::string test_paths[] = {
      97      "", "/", "//", "/.", "/./", "/a", "/a/", "/a//", "/a/b/c/d", "/a//b",
      98      "a", "a/b", "a/b/", "a/b/c", "a/b/c.d", "a/b/..", "a/b/c.", "a/b/.c"
      99    };
     100  
     101    test_fs::path
     102    root_path()
     103    {
     104  #if defined(__MINGW32__) || defined(__MINGW64__)
     105      return L"c:/";
     106  #else
     107      return "/";
     108  #endif
     109    }
     110  
     111    // This is NOT supposed to be a secure way to get a unique name!
     112    // We just need a path that doesn't exist for testing purposes.
     113    test_fs::path
     114    nonexistent_path(std::string file = __builtin_FILE())
     115    {
     116      // Include the caller's filename to help identify tests that fail to
     117      // clean up the files they create.
     118      // Remove .cc extension:
     119      if (file.length() > 3 && file.compare(file.length() - 3, 3, ".cc") == 0)
     120        file.resize(file.length() - 3);
     121      // And directory:
     122      auto pos = file.find_last_of("/\\");
     123      if (pos != file.npos)
     124        file.erase(0, pos+1);
     125  
     126      file.reserve(file.size() + 40);
     127      file.insert(0, "filesystem-test.");
     128  
     129      // A counter, starting from a random value, to be included as part
     130      // of the filename being returned, and incremented each time
     131      // this function is used.  It allows us to ensure that two calls
     132      // to this function can never return the same filename, something
     133      // testcases do when they need multiple non-existent filenames
     134      // for their purposes.
     135      static unsigned counter = std::random_device{}();
     136      file += '.';
     137      file += std::to_string(counter++);
     138      file += '.';
     139  
     140      test_fs::path p;
     141  #if defined(_GNU_SOURCE) || _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200112L
     142  
     143      // Use mkstemp to determine the name of a file which does not exist yet.
     144      //
     145      // Note that we have seen on some systems (such as RTEMS, for instance)
     146      // that mkstemp behaves very predictably, causing it to always try
     147      // the same sequence of file names.  In other words, if we call mkstemp
     148      // with a pattern, delete the file it created (which is what we do, here),
     149      // and call mkstemp with the same pattern again, it returns the same
     150      // filename once more.  While most implementations introduce a degree
     151      // of randomness, it is not mandated by the standard, and this is why
     152      // we also include a counter in the template passed to mkstemp.
     153      file += "XXXXXX";
     154      int fd = ::mkstemp(&file[0]);
     155      if (fd == -1)
     156        throw test_fs::filesystem_error("mkstemp failed",
     157  	  std::error_code(errno, std::generic_category()));
     158      ::unlink(file.c_str());
     159      ::close(fd);
     160      p = std::move(file);
     161  #else
     162      if (file.length() > 64)
     163        file.resize(64);
     164      // The combination of random counter and PID should be unique for a given
     165      // run of the testsuite.  N.B. getpid() returns a pointer type on vxworks
     166      // in kernel mode.
     167      file += std::to_string((unsigned long) ::getpid());
     168      p = std::move(file);
     169      if (test_fs::exists(p))
     170        throw test_fs::filesystem_error("Failed to generate unique pathname", p,
     171  	  std::make_error_code(std::errc::file_exists));
     172  #endif
     173      return p;
     174    }
     175  
     176    // RAII helper to remove a file on scope exit.
     177    struct scoped_file
     178    {
     179      using path_type = test_fs::path;
     180  
     181      enum adopt_file_t { adopt_file };
     182  
     183      explicit
     184      scoped_file(const path_type& p = nonexistent_path()) : path(p)
     185      { std::ofstream{p.c_str()}; }
     186  
     187      scoped_file(path_type p, adopt_file_t) : path(p) { }
     188  
     189      ~scoped_file() { if (!path.empty()) remove(path); }
     190  
     191      scoped_file(scoped_file&&) = default;
     192      scoped_file& operator=(scoped_file&&) = default;
     193  
     194      path_type path;
     195    };
     196  
     197    inline bool
     198    permissions_are_testable(bool print_msg = true)
     199    {
     200      bool testable = false;
     201  #if !(defined __MINGW32__ || defined __MINGW64__)
     202      if (geteuid() != 0)
     203        testable = true;
     204      // XXX on Linux the CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH capabilities
     205      // can give normal users extra permissions for files and directories.
     206      // We ignore that possibility here.
     207  #endif
     208      if (print_msg && !testable)
     209        std::puts("Skipping tests that depend on filesystem permissions");
     210      return testable;
     211    }
     212  
     213  } // namespace __gnu_test
     214  #endif