gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/util/fs_util.cc (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #include "test/util/fs_util.h"
    16  
    17  #include <dirent.h>
    18  #ifdef __linux__
    19  #include <linux/magic.h>
    20  #endif  // __linux__
    21  #include <sys/stat.h>
    22  #include <sys/statfs.h>
    23  #include <sys/types.h>
    24  #include <unistd.h>
    25  
    26  #include "gmock/gmock.h"
    27  #include "absl/strings/match.h"
    28  #include "absl/strings/str_cat.h"
    29  #include "absl/strings/str_split.h"
    30  #include "absl/strings/string_view.h"
    31  #include "absl/time/clock.h"
    32  #include "absl/time/time.h"
    33  #include "test/util/cleanup.h"
    34  #include "test/util/file_descriptor.h"
    35  #include "test/util/posix_error.h"
    36  
    37  namespace gvisor {
    38  namespace testing {
    39  
    40  namespace {
    41  PosixError WriteContentsToFD(int fd, absl::string_view contents) {
    42    int written = 0;
    43    while (static_cast<absl::string_view::size_type>(written) < contents.size()) {
    44      int wrote = write(fd, contents.data() + written, contents.size() - written);
    45      if (wrote < 0) {
    46        if (errno == EINTR) {
    47          continue;
    48        }
    49        return PosixError(
    50            errno, absl::StrCat("WriteContentsToFD fd: ", fd, " write failure."));
    51      }
    52      written += wrote;
    53    }
    54    return NoError();
    55  }
    56  }  // namespace
    57  
    58  namespace internal {
    59  
    60  // Given a collection of file paths, append them all together,
    61  // ensuring that the proper path separators are inserted between them.
    62  std::string JoinPathImpl(std::initializer_list<absl::string_view> paths) {
    63    std::string result;
    64  
    65    if (paths.size() != 0) {
    66      // This size calculation is worst-case: it assumes one extra "/" for every
    67      // path other than the first.
    68      size_t total_size = paths.size() - 1;
    69      for (const absl::string_view path : paths) total_size += path.size();
    70      result.resize(total_size);
    71  
    72      auto begin = result.begin();
    73      auto out = begin;
    74      bool trailing_slash = false;
    75      for (absl::string_view path : paths) {
    76        if (path.empty()) continue;
    77        if (path.front() == '/') {
    78          if (trailing_slash) {
    79            path.remove_prefix(1);
    80          }
    81        } else {
    82          if (!trailing_slash && out != begin) *out++ = '/';
    83        }
    84        const size_t this_size = path.size();
    85        memcpy(&*out, path.data(), this_size);
    86        out += this_size;
    87        trailing_slash = out[-1] == '/';
    88      }
    89      result.erase(out - begin);
    90    }
    91    return result;
    92  }
    93  }  // namespace internal
    94  
    95  // Returns a status or the current working directory.
    96  PosixErrorOr<std::string> GetCWD() {
    97    char buffer[PATH_MAX + 1] = {};
    98    if (getcwd(buffer, PATH_MAX) == nullptr) {
    99      return PosixError(errno, "GetCWD() failed");
   100    }
   101  
   102    return std::string(buffer);
   103  }
   104  
   105  PosixErrorOr<struct stat> Stat(absl::string_view path) {
   106    struct stat stat_buf;
   107    int res = stat(std::string(path).c_str(), &stat_buf);
   108    if (res < 0) {
   109      return PosixError(errno, absl::StrCat("stat ", path));
   110    }
   111    return stat_buf;
   112  }
   113  
   114  PosixErrorOr<struct stat> Lstat(absl::string_view path) {
   115    struct stat stat_buf;
   116    int res = lstat(std::string(path).c_str(), &stat_buf);
   117    if (res < 0) {
   118      return PosixError(errno, absl::StrCat("lstat ", path));
   119    }
   120    return stat_buf;
   121  }
   122  
   123  PosixErrorOr<struct stat> Fstat(int fd) {
   124    struct stat stat_buf;
   125    int res = fstat(fd, &stat_buf);
   126    if (res < 0) {
   127      return PosixError(errno, absl::StrCat("fstat ", fd));
   128    }
   129    return stat_buf;
   130  }
   131  
   132  PosixErrorOr<bool> Exists(absl::string_view path) {
   133    struct stat stat_buf;
   134    int res = lstat(std::string(path).c_str(), &stat_buf);
   135    if (res < 0) {
   136      if (errno == ENOENT) {
   137        return false;
   138      }
   139      return PosixError(errno, absl::StrCat("lstat ", path));
   140    }
   141    return true;
   142  }
   143  
   144  PosixErrorOr<bool> IsDirectory(absl::string_view path) {
   145    ASSIGN_OR_RETURN_ERRNO(struct stat stat_buf, Lstat(path));
   146    if (S_ISDIR(stat_buf.st_mode)) {
   147      return true;
   148    }
   149  
   150    return false;
   151  }
   152  
   153  PosixError Delete(absl::string_view path) {
   154    int res = unlink(std::string(path).c_str());
   155    if (res < 0) {
   156      return PosixError(errno, absl::StrCat("unlink ", path));
   157    }
   158  
   159    return NoError();
   160  }
   161  
   162  PosixError Truncate(absl::string_view path, int length) {
   163    int res = truncate(std::string(path).c_str(), length);
   164    if (res < 0) {
   165      return PosixError(errno,
   166                        absl::StrCat("truncate ", path, " to length ", length));
   167    }
   168  
   169    return NoError();
   170  }
   171  
   172  PosixError Chmod(absl::string_view path, int mode) {
   173    int res = chmod(std::string(path).c_str(), mode);
   174    if (res < 0) {
   175      return PosixError(errno, absl::StrCat("chmod ", path));
   176    }
   177  
   178    return NoError();
   179  }
   180  
   181  PosixError MknodAt(const FileDescriptor& dfd, absl::string_view path, int mode,
   182                     dev_t dev) {
   183    int res = mknodat(dfd.get(), std::string(path).c_str(), mode, dev);
   184    if (res < 0) {
   185      return PosixError(errno, absl::StrCat("mknod ", path));
   186    }
   187  
   188    return NoError();
   189  }
   190  
   191  PosixError Unlink(absl::string_view path) {
   192    int res = unlink(std::string(path).c_str());
   193    if (res < 0) {
   194      return PosixError(errno, absl::StrCat("unlink ", path));
   195    }
   196    return NoError();
   197  }
   198  
   199  PosixError UnlinkAt(const FileDescriptor& dfd, absl::string_view path,
   200                      int flags) {
   201    int res = unlinkat(dfd.get(), std::string(path).c_str(), flags);
   202    if (res < 0) {
   203      return PosixError(errno, absl::StrCat("unlink ", path));
   204    }
   205  
   206    return NoError();
   207  }
   208  
   209  PosixError Mkdir(absl::string_view path, int mode) {
   210    int res = mkdir(std::string(path).c_str(), mode);
   211    if (res < 0) {
   212      return PosixError(errno,
   213                        absl::StrFormat("mkdir \"%s\" mode %#o", path, mode));
   214    }
   215  
   216    return NoError();
   217  }
   218  
   219  PosixError Rmdir(absl::string_view path) {
   220    int res = rmdir(std::string(path).c_str());
   221    if (res < 0) {
   222      return PosixError(errno, absl::StrCat("rmdir ", path));
   223    }
   224  
   225    return NoError();
   226  }
   227  
   228  PosixError SetContents(absl::string_view path, absl::string_view contents) {
   229    ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(path));
   230    if (!exists) {
   231      return PosixError(
   232          ENOENT, absl::StrCat("SetContents file ", path, " doesn't exist."));
   233    }
   234  
   235    ASSIGN_OR_RETURN_ERRNO(auto fd, Open(std::string(path), O_WRONLY | O_TRUNC));
   236    return WriteContentsToFD(fd.get(), contents);
   237  }
   238  
   239  // Create a file with the given contents (if it does not already exist with the
   240  // given mode) and then set the contents.
   241  PosixError CreateWithContents(absl::string_view path,
   242                                absl::string_view contents, int mode) {
   243    ASSIGN_OR_RETURN_ERRNO(
   244        auto fd, Open(std::string(path), O_WRONLY | O_CREAT | O_TRUNC, mode));
   245    return WriteContentsToFD(fd.get(), contents);
   246  }
   247  
   248  PosixError GetContents(absl::string_view path, std::string* output) {
   249    ASSIGN_OR_RETURN_ERRNO(auto fd, Open(std::string(path), O_RDONLY));
   250    output->clear();
   251  
   252    // Keep reading until we hit an EOF or an error.
   253    return GetContentsFD(fd.get(), output);
   254  }
   255  
   256  PosixErrorOr<std::string> GetContents(absl::string_view path) {
   257    std::string ret;
   258    RETURN_IF_ERRNO(GetContents(path, &ret));
   259    return ret;
   260  }
   261  
   262  PosixErrorOr<std::string> GetContentsFD(int fd) {
   263    std::string ret;
   264    RETURN_IF_ERRNO(GetContentsFD(fd, &ret));
   265    return ret;
   266  }
   267  
   268  PosixError GetContentsFD(int fd, std::string* output) {
   269    // Keep reading until we hit an EOF or an error.
   270    while (true) {
   271      char buf[16 * 1024] = {};  // Read in 16KB chunks.
   272      int bytes_read = read(fd, buf, sizeof(buf));
   273      if (bytes_read < 0) {
   274        if (errno == EINTR) {
   275          continue;
   276        }
   277        return PosixError(errno, "GetContentsFD read failure.");
   278      }
   279  
   280      if (bytes_read == 0) {
   281        break;  // EOF.
   282      }
   283  
   284      output->append(buf, bytes_read);
   285    }
   286    return NoError();
   287  }
   288  
   289  PosixErrorOr<std::string> ReadLink(absl::string_view path) {
   290    char buf[PATH_MAX + 1] = {};
   291    int ret = readlink(std::string(path).c_str(), buf, PATH_MAX);
   292    if (ret < 0) {
   293      return PosixError(errno, absl::StrCat("readlink ", path));
   294    }
   295  
   296    return std::string(buf, ret);
   297  }
   298  
   299  PosixError WalkTree(
   300      absl::string_view path, bool recursive,
   301      const std::function<void(absl::string_view, const struct stat&)>& cb) {
   302    DIR* dir = opendir(std::string(path).c_str());
   303    if (dir == nullptr) {
   304      return PosixError(errno, absl::StrCat("opendir ", path));
   305    }
   306    auto dir_closer = Cleanup([&dir]() { closedir(dir); });
   307    while (true) {
   308      // Readdir(3): If the end of the directory stream is reached, NULL is
   309      // returned and errno is not changed.  If an error occurs, NULL is returned
   310      // and errno is set appropriately.  To distinguish end of stream and from an
   311      // error, set errno to zero before calling readdir() and then check the
   312      // value of errno if NULL is returned.
   313      errno = 0;
   314      struct dirent* dp = readdir(dir);
   315      if (dp == nullptr) {
   316        if (errno != 0) {
   317          return PosixError(errno, absl::StrCat("readdir ", path));
   318        }
   319        break;  // We're done.
   320      }
   321  
   322      if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
   323        // Skip dots.
   324        continue;
   325      }
   326  
   327      auto full_path = JoinPath(path, dp->d_name);
   328      ASSIGN_OR_RETURN_ERRNO(struct stat s, Stat(full_path));
   329      if (S_ISDIR(s.st_mode) && recursive) {
   330        RETURN_IF_ERRNO(WalkTree(full_path, recursive, cb));
   331      } else {
   332        cb(full_path, s);
   333      }
   334    }
   335    // We're done walking so let's invoke our cleanup callback now.
   336    dir_closer.Release()();
   337  
   338    // And we have to dispatch the callback on the base directory.
   339    ASSIGN_OR_RETURN_ERRNO(struct stat s, Stat(path));
   340    cb(path, s);
   341  
   342    return NoError();
   343  }
   344  
   345  PosixErrorOr<std::vector<std::string>> ListDir(absl::string_view abspath,
   346                                                 bool skipdots) {
   347    std::vector<std::string> files;
   348  
   349    DIR* dir = opendir(std::string(abspath).c_str());
   350    if (dir == nullptr) {
   351      return PosixError(errno, absl::StrCat("opendir ", abspath));
   352    }
   353    auto dir_closer = Cleanup([&dir]() { closedir(dir); });
   354    while (true) {
   355      // Readdir(3): If the end of the directory stream is reached, NULL is
   356      // returned and errno is not changed.  If an error occurs, NULL is returned
   357      // and errno is set appropriately.  To distinguish end of stream and from an
   358      // error, set errno to zero before calling readdir() and then check the
   359      // value of errno if NULL is returned.
   360      errno = 0;
   361      struct dirent* dp = readdir(dir);
   362      if (dp == nullptr) {
   363        if (errno != 0) {
   364          return PosixError(errno, absl::StrCat("readdir ", abspath));
   365        }
   366        break;  // We're done.
   367      }
   368  
   369      if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
   370        if (skipdots) {
   371          continue;
   372        }
   373      }
   374      files.push_back(std::string(dp->d_name));
   375    }
   376  
   377    return files;
   378  }
   379  
   380  PosixError DirContains(absl::string_view path,
   381                         const std::vector<std::string>& expect,
   382                         const std::vector<std::string>& exclude) {
   383    ASSIGN_OR_RETURN_ERRNO(auto listing, ListDir(path, false));
   384  
   385    for (auto& expected_entry : expect) {
   386      auto cursor = std::find(listing.begin(), listing.end(), expected_entry);
   387      if (cursor == listing.end()) {
   388        return PosixError(ENOENT, absl::StrFormat("Failed to find '%s' in '%s'",
   389                                                  expected_entry, path));
   390      }
   391    }
   392    for (auto& excluded_entry : exclude) {
   393      auto cursor = std::find(listing.begin(), listing.end(), excluded_entry);
   394      if (cursor != listing.end()) {
   395        return PosixError(ENOENT, absl::StrCat("File '", excluded_entry,
   396                                               "' found in path '", path, "'"));
   397      }
   398    }
   399    return NoError();
   400  }
   401  
   402  PosixError EventuallyDirContains(absl::string_view path,
   403                                   const std::vector<std::string>& expect,
   404                                   const std::vector<std::string>& exclude) {
   405    constexpr int kRetryCount = 100;
   406    const absl::Duration kRetryDelay = absl::Milliseconds(100);
   407  
   408    for (int i = 0; i < kRetryCount; ++i) {
   409      auto res = DirContains(path, expect, exclude);
   410      if (res.ok()) {
   411        return res;
   412      }
   413      if (i < kRetryCount - 1) {
   414        // Sleep if this isn't the final iteration.
   415        absl::SleepFor(kRetryDelay);
   416      }
   417    }
   418    return PosixError(ETIMEDOUT,
   419                      "Timed out while waiting for directory to contain files ");
   420  }
   421  
   422  PosixError RecursivelyDelete(absl::string_view path, int* undeleted_dirs,
   423                               int* undeleted_files) {
   424    ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(path));
   425    if (!exists) {
   426      return PosixError(ENOENT, absl::StrCat(path, " does not exist"));
   427    }
   428  
   429    ASSIGN_OR_RETURN_ERRNO(bool dir, IsDirectory(path));
   430    if (!dir) {
   431      // Nothing recursive needs to happen we can just call Delete.
   432      auto status = Delete(path);
   433      if (!status.ok() && undeleted_files) {
   434        (*undeleted_files)++;
   435      }
   436      return status;
   437    }
   438  
   439    return WalkTree(path, /*recursive=*/true,
   440                    [&](absl::string_view absolute_path, const struct stat& s) {
   441                      if (S_ISDIR(s.st_mode)) {
   442                        auto rm_status = Rmdir(absolute_path);
   443                        if (!rm_status.ok() && undeleted_dirs) {
   444                          (*undeleted_dirs)++;
   445                        }
   446                      } else {
   447                        auto delete_status = Delete(absolute_path);
   448                        if (!delete_status.ok() && undeleted_files) {
   449                          (*undeleted_files)++;
   450                        }
   451                      }
   452                    });
   453  }
   454  
   455  PosixError RecursivelyCreateDir(absl::string_view path) {
   456    if (path.empty() || path == "/") {
   457      return PosixError(EINVAL, "Cannot create root!");
   458    }
   459  
   460    // Does it already exist, if so we're done.
   461    ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(path));
   462    if (exists) {
   463      return NoError();
   464    }
   465  
   466    // Do we need to create directories under us?
   467    auto dirname = Dirname(path);
   468    ASSIGN_OR_RETURN_ERRNO(exists, Exists(dirname));
   469    if (!exists) {
   470      RETURN_IF_ERRNO(RecursivelyCreateDir(dirname));
   471    }
   472  
   473    return Mkdir(path);
   474  }
   475  
   476  // Makes a path absolute with respect to an optional base. If no base is
   477  // provided it will use the current working directory.
   478  PosixErrorOr<std::string> MakeAbsolute(absl::string_view filename,
   479                                         absl::string_view base) {
   480    if (filename.empty()) {
   481      return PosixError(EINVAL, "filename cannot be empty.");
   482    }
   483  
   484    if (filename[0] == '/') {
   485      // This path is already absolute.
   486      return std::string(filename);
   487    }
   488  
   489    std::string actual_base;
   490    if (!base.empty()) {
   491      actual_base = std::string(base);
   492    } else {
   493      auto cwd_or = GetCWD();
   494      RETURN_IF_ERRNO(cwd_or.error());
   495      actual_base = cwd_or.ValueOrDie();
   496    }
   497  
   498    // Reverse iterate removing trailing slashes, effectively right trim '/'.
   499    for (int i = actual_base.size() - 1; i >= 0 && actual_base[i] == '/'; --i) {
   500      actual_base.erase(i, 1);
   501    }
   502  
   503    if (filename == ".") {
   504      return actual_base.empty() ? "/" : actual_base;
   505    }
   506  
   507    return absl::StrCat(actual_base, "/", filename);
   508  }
   509  
   510  std::string CleanPath(const absl::string_view unclean_path) {
   511    std::string path = std::string(unclean_path);
   512    const char* src = path.c_str();
   513    std::string::iterator dst = path.begin();
   514  
   515    // Check for absolute path and determine initial backtrack limit.
   516    const bool is_absolute_path = *src == '/';
   517    if (is_absolute_path) {
   518      *dst++ = *src++;
   519      while (*src == '/') ++src;
   520    }
   521    std::string::const_iterator backtrack_limit = dst;
   522  
   523    // Process all parts
   524    while (*src) {
   525      bool parsed = false;
   526  
   527      if (src[0] == '.') {
   528        //  1dot ".<whateverisnext>", check for END or SEP.
   529        if (src[1] == '/' || !src[1]) {
   530          if (*++src) {
   531            ++src;
   532          }
   533          parsed = true;
   534        } else if (src[1] == '.' && (src[2] == '/' || !src[2])) {
   535          // 2dot END or SEP (".." | "../<whateverisnext>").
   536          src += 2;
   537          if (dst != backtrack_limit) {
   538            // We can backtrack the previous part
   539            for (--dst; dst != backtrack_limit && dst[-1] != '/'; --dst) {
   540              // Empty.
   541            }
   542          } else if (!is_absolute_path) {
   543            // Failed to backtrack and we can't skip it either. Rewind and copy.
   544            src -= 2;
   545            *dst++ = *src++;
   546            *dst++ = *src++;
   547            if (*src) {
   548              *dst++ = *src;
   549            }
   550            // We can never backtrack over a copied "../" part so set new limit.
   551            backtrack_limit = dst;
   552          }
   553          if (*src) {
   554            ++src;
   555          }
   556          parsed = true;
   557        }
   558      }
   559  
   560      // If not parsed, copy entire part until the next SEP or EOS.
   561      if (!parsed) {
   562        while (*src && *src != '/') {
   563          *dst++ = *src++;
   564        }
   565        if (*src) {
   566          *dst++ = *src++;
   567        }
   568      }
   569  
   570      // Skip consecutive SEP occurrences
   571      while (*src == '/') {
   572        ++src;
   573      }
   574    }
   575  
   576    // Calculate and check the length of the cleaned path.
   577    int path_length = dst - path.begin();
   578    if (path_length != 0) {
   579      // Remove trailing '/' except if it is root path ("/" ==> path_length := 1)
   580      if (path_length > 1 && path[path_length - 1] == '/') {
   581        --path_length;
   582      }
   583      path.resize(path_length);
   584    } else {
   585      // The cleaned path is empty; assign "." as per the spec.
   586      path.assign(1, '.');
   587    }
   588    return path;
   589  }
   590  
   591  PosixErrorOr<std::string> GetRelativePath(absl::string_view source,
   592                                            absl::string_view dest) {
   593    if (!absl::StartsWith(source, "/") || !absl::StartsWith(dest, "/")) {
   594      // At least one of the inputs is not an absolute path.
   595      return PosixError(
   596          EINVAL,
   597          "GetRelativePath: At least one of the inputs is not an absolute path.");
   598    }
   599    const std::string clean_source = CleanPath(source);
   600    const std::string clean_dest = CleanPath(dest);
   601    auto source_parts = absl::StrSplit(clean_source, '/', absl::SkipEmpty());
   602    auto dest_parts = absl::StrSplit(clean_dest, '/', absl::SkipEmpty());
   603    auto source_iter = source_parts.begin();
   604    auto dest_iter = dest_parts.begin();
   605  
   606    // Advance past common prefix.
   607    while (source_iter != source_parts.end() && dest_iter != dest_parts.end() &&
   608           *source_iter == *dest_iter) {
   609      ++source_iter;
   610      ++dest_iter;
   611    }
   612  
   613    // Build result backtracking.
   614    std::string result = "";
   615    while (source_iter != source_parts.end()) {
   616      absl::StrAppend(&result, "../");
   617      ++source_iter;
   618    }
   619  
   620    // Add remaining path to dest.
   621    while (dest_iter != dest_parts.end()) {
   622      absl::StrAppend(&result, *dest_iter, "/");
   623      ++dest_iter;
   624    }
   625  
   626    if (result.empty()) {
   627      return std::string(".");
   628    }
   629  
   630    // Remove trailing slash.
   631    result.erase(result.size() - 1);
   632    return result;
   633  }
   634  
   635  absl::string_view Dirname(absl::string_view path) {
   636    return SplitPath(path).first;
   637  }
   638  
   639  absl::string_view Basename(absl::string_view path) {
   640    return SplitPath(path).second;
   641  }
   642  
   643  std::pair<absl::string_view, absl::string_view> SplitPath(
   644      absl::string_view path) {
   645    std::string::size_type pos = path.find_last_of('/');
   646  
   647    // Handle the case with no '/' in 'path'.
   648    if (pos == absl::string_view::npos) {
   649      return std::make_pair(path.substr(0, 0), path);
   650    }
   651  
   652    // Handle the case with a single leading '/' in 'path'.
   653    if (pos == 0) {
   654      return std::make_pair(path.substr(0, 1), absl::ClippedSubstr(path, 1));
   655    }
   656  
   657    return std::make_pair(path.substr(0, pos),
   658                          absl::ClippedSubstr(path, pos + 1));
   659  }
   660  
   661  std::string JoinPath(absl::string_view path1, absl::string_view path2) {
   662    if (path1.empty()) {
   663      return std::string(path2);
   664    }
   665    if (path2.empty()) {
   666      return std::string(path1);
   667    }
   668  
   669    if (path1.back() == '/') {
   670      if (path2.front() == '/') {
   671        return absl::StrCat(path1, absl::ClippedSubstr(path2, 1));
   672      }
   673    } else {
   674      if (path2.front() != '/') {
   675        return absl::StrCat(path1, "/", path2);
   676      }
   677    }
   678    return absl::StrCat(path1, path2);
   679  }
   680  
   681  PosixErrorOr<std::string> ProcessExePath(int pid) {
   682    if (pid <= 0) {
   683      return PosixError(EINVAL, "Invalid pid specified");
   684    }
   685  
   686    return ReadLink(absl::StrCat("/proc/", pid, "/exe"));
   687  }
   688  
   689  #ifdef __linux__
   690  PosixErrorOr<bool> IsTmpfs(const std::string& path) {
   691    struct statfs stat;
   692    if (statfs(path.c_str(), &stat)) {
   693      if (errno == ENOENT) {
   694        // Nothing at path, don't raise this as an error. Instead, just report no
   695        // tmpfs at path.
   696        return false;
   697      }
   698      return PosixError(errno,
   699                        absl::StrFormat("statfs(\"%s\", %#p)", path, &stat));
   700    }
   701    return stat.f_type == TMPFS_MAGIC;
   702  }
   703  #endif  // __linux__
   704  
   705  PosixErrorOr<bool> IsOverlayfs(const std::string& path) {
   706    struct statfs stat;
   707    if (statfs(path.c_str(), &stat)) {
   708      if (errno == ENOENT) {
   709        // Nothing at path, don't raise this as an error. Instead, just report no
   710        // overlayfs at path.
   711        return false;
   712      }
   713      return PosixError(errno,
   714                        absl::StrFormat("statfs(\"%s\", %#p)", path, &stat));
   715    }
   716    return stat.f_type == OVERLAYFS_SUPER_MAGIC;
   717  }
   718  
   719  PosixError CheckSameFile(const FileDescriptor& fd1, const FileDescriptor& fd2) {
   720    struct stat stat_result1, stat_result2;
   721    int res = fstat(fd1.get(), &stat_result1);
   722    if (res < 0) {
   723      return PosixError(errno, absl::StrCat("fstat ", fd1.get()));
   724    }
   725  
   726    res = fstat(fd2.get(), &stat_result2);
   727    if (res < 0) {
   728      return PosixError(errno, absl::StrCat("fstat ", fd2.get()));
   729    }
   730    EXPECT_EQ(stat_result1.st_dev, stat_result2.st_dev);
   731    EXPECT_EQ(stat_result1.st_ino, stat_result2.st_ino);
   732  
   733    return NoError();
   734  }
   735  
   736  ::testing::Matcher<mode_t> PermissionIs(mode_t want) {
   737    return MakeMatcher(new ModePermissionMatcher(want));
   738  }
   739  
   740  }  // namespace testing
   741  }  // namespace gvisor