github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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 UnlinkAt(const FileDescriptor& dfd, absl::string_view path,
   192                      int flags) {
   193    int res = unlinkat(dfd.get(), std::string(path).c_str(), flags);
   194    if (res < 0) {
   195      return PosixError(errno, absl::StrCat("unlink ", path));
   196    }
   197  
   198    return NoError();
   199  }
   200  
   201  PosixError Mkdir(absl::string_view path, int mode) {
   202    int res = mkdir(std::string(path).c_str(), mode);
   203    if (res < 0) {
   204      return PosixError(errno, absl::StrCat("mkdir ", path, " mode ", mode));
   205    }
   206  
   207    return NoError();
   208  }
   209  
   210  PosixError Rmdir(absl::string_view path) {
   211    int res = rmdir(std::string(path).c_str());
   212    if (res < 0) {
   213      return PosixError(errno, absl::StrCat("rmdir ", path));
   214    }
   215  
   216    return NoError();
   217  }
   218  
   219  PosixError SetContents(absl::string_view path, absl::string_view contents) {
   220    ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(path));
   221    if (!exists) {
   222      return PosixError(
   223          ENOENT, absl::StrCat("SetContents file ", path, " doesn't exist."));
   224    }
   225  
   226    ASSIGN_OR_RETURN_ERRNO(auto fd, Open(std::string(path), O_WRONLY | O_TRUNC));
   227    return WriteContentsToFD(fd.get(), contents);
   228  }
   229  
   230  // Create a file with the given contents (if it does not already exist with the
   231  // given mode) and then set the contents.
   232  PosixError CreateWithContents(absl::string_view path,
   233                                absl::string_view contents, int mode) {
   234    ASSIGN_OR_RETURN_ERRNO(
   235        auto fd, Open(std::string(path), O_WRONLY | O_CREAT | O_TRUNC, mode));
   236    return WriteContentsToFD(fd.get(), contents);
   237  }
   238  
   239  PosixError GetContents(absl::string_view path, std::string* output) {
   240    ASSIGN_OR_RETURN_ERRNO(auto fd, Open(std::string(path), O_RDONLY));
   241    output->clear();
   242  
   243    // Keep reading until we hit an EOF or an error.
   244    return GetContentsFD(fd.get(), output);
   245  }
   246  
   247  PosixErrorOr<std::string> GetContents(absl::string_view path) {
   248    std::string ret;
   249    RETURN_IF_ERRNO(GetContents(path, &ret));
   250    return ret;
   251  }
   252  
   253  PosixErrorOr<std::string> GetContentsFD(int fd) {
   254    std::string ret;
   255    RETURN_IF_ERRNO(GetContentsFD(fd, &ret));
   256    return ret;
   257  }
   258  
   259  PosixError GetContentsFD(int fd, std::string* output) {
   260    // Keep reading until we hit an EOF or an error.
   261    while (true) {
   262      char buf[16 * 1024] = {};  // Read in 16KB chunks.
   263      int bytes_read = read(fd, buf, sizeof(buf));
   264      if (bytes_read < 0) {
   265        if (errno == EINTR) {
   266          continue;
   267        }
   268        return PosixError(errno, "GetContentsFD read failure.");
   269      }
   270  
   271      if (bytes_read == 0) {
   272        break;  // EOF.
   273      }
   274  
   275      output->append(buf, bytes_read);
   276    }
   277    return NoError();
   278  }
   279  
   280  PosixErrorOr<std::string> ReadLink(absl::string_view path) {
   281    char buf[PATH_MAX + 1] = {};
   282    int ret = readlink(std::string(path).c_str(), buf, PATH_MAX);
   283    if (ret < 0) {
   284      return PosixError(errno, absl::StrCat("readlink ", path));
   285    }
   286  
   287    return std::string(buf, ret);
   288  }
   289  
   290  PosixError WalkTree(
   291      absl::string_view path, bool recursive,
   292      const std::function<void(absl::string_view, const struct stat&)>& cb) {
   293    DIR* dir = opendir(std::string(path).c_str());
   294    if (dir == nullptr) {
   295      return PosixError(errno, absl::StrCat("opendir ", path));
   296    }
   297    auto dir_closer = Cleanup([&dir]() { closedir(dir); });
   298    while (true) {
   299      // Readdir(3): If the end of the directory stream is reached, NULL is
   300      // returned and errno is not changed.  If an error occurs, NULL is returned
   301      // and errno is set appropriately.  To distinguish end of stream and from an
   302      // error, set errno to zero before calling readdir() and then check the
   303      // value of errno if NULL is returned.
   304      errno = 0;
   305      struct dirent* dp = readdir(dir);
   306      if (dp == nullptr) {
   307        if (errno != 0) {
   308          return PosixError(errno, absl::StrCat("readdir ", path));
   309        }
   310        break;  // We're done.
   311      }
   312  
   313      if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
   314        // Skip dots.
   315        continue;
   316      }
   317  
   318      auto full_path = JoinPath(path, dp->d_name);
   319      ASSIGN_OR_RETURN_ERRNO(struct stat s, Stat(full_path));
   320      if (S_ISDIR(s.st_mode) && recursive) {
   321        RETURN_IF_ERRNO(WalkTree(full_path, recursive, cb));
   322      } else {
   323        cb(full_path, s);
   324      }
   325    }
   326    // We're done walking so let's invoke our cleanup callback now.
   327    dir_closer.Release()();
   328  
   329    // And we have to dispatch the callback on the base directory.
   330    ASSIGN_OR_RETURN_ERRNO(struct stat s, Stat(path));
   331    cb(path, s);
   332  
   333    return NoError();
   334  }
   335  
   336  PosixErrorOr<std::vector<std::string>> ListDir(absl::string_view abspath,
   337                                                 bool skipdots) {
   338    std::vector<std::string> files;
   339  
   340    DIR* dir = opendir(std::string(abspath).c_str());
   341    if (dir == nullptr) {
   342      return PosixError(errno, absl::StrCat("opendir ", abspath));
   343    }
   344    auto dir_closer = Cleanup([&dir]() { closedir(dir); });
   345    while (true) {
   346      // Readdir(3): If the end of the directory stream is reached, NULL is
   347      // returned and errno is not changed.  If an error occurs, NULL is returned
   348      // and errno is set appropriately.  To distinguish end of stream and from an
   349      // error, set errno to zero before calling readdir() and then check the
   350      // value of errno if NULL is returned.
   351      errno = 0;
   352      struct dirent* dp = readdir(dir);
   353      if (dp == nullptr) {
   354        if (errno != 0) {
   355          return PosixError(errno, absl::StrCat("readdir ", abspath));
   356        }
   357        break;  // We're done.
   358      }
   359  
   360      if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
   361        if (skipdots) {
   362          continue;
   363        }
   364      }
   365      files.push_back(std::string(dp->d_name));
   366    }
   367  
   368    return files;
   369  }
   370  
   371  PosixError DirContains(absl::string_view path,
   372                         const std::vector<std::string>& expect,
   373                         const std::vector<std::string>& exclude) {
   374    ASSIGN_OR_RETURN_ERRNO(auto listing, ListDir(path, false));
   375  
   376    for (auto& expected_entry : expect) {
   377      auto cursor = std::find(listing.begin(), listing.end(), expected_entry);
   378      if (cursor == listing.end()) {
   379        return PosixError(ENOENT, absl::StrFormat("Failed to find '%s' in '%s'",
   380                                                  expected_entry, path));
   381      }
   382    }
   383    for (auto& excluded_entry : exclude) {
   384      auto cursor = std::find(listing.begin(), listing.end(), excluded_entry);
   385      if (cursor != listing.end()) {
   386        return PosixError(ENOENT, absl::StrCat("File '", excluded_entry,
   387                                               "' found in path '", path, "'"));
   388      }
   389    }
   390    return NoError();
   391  }
   392  
   393  PosixError EventuallyDirContains(absl::string_view path,
   394                                   const std::vector<std::string>& expect,
   395                                   const std::vector<std::string>& exclude) {
   396    constexpr int kRetryCount = 100;
   397    const absl::Duration kRetryDelay = absl::Milliseconds(100);
   398  
   399    for (int i = 0; i < kRetryCount; ++i) {
   400      auto res = DirContains(path, expect, exclude);
   401      if (res.ok()) {
   402        return res;
   403      }
   404      if (i < kRetryCount - 1) {
   405        // Sleep if this isn't the final iteration.
   406        absl::SleepFor(kRetryDelay);
   407      }
   408    }
   409    return PosixError(ETIMEDOUT,
   410                      "Timed out while waiting for directory to contain files ");
   411  }
   412  
   413  PosixError RecursivelyDelete(absl::string_view path, int* undeleted_dirs,
   414                               int* undeleted_files) {
   415    ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(path));
   416    if (!exists) {
   417      return PosixError(ENOENT, absl::StrCat(path, " does not exist"));
   418    }
   419  
   420    ASSIGN_OR_RETURN_ERRNO(bool dir, IsDirectory(path));
   421    if (!dir) {
   422      // Nothing recursive needs to happen we can just call Delete.
   423      auto status = Delete(path);
   424      if (!status.ok() && undeleted_files) {
   425        (*undeleted_files)++;
   426      }
   427      return status;
   428    }
   429  
   430    return WalkTree(path, /*recursive=*/true,
   431                    [&](absl::string_view absolute_path, const struct stat& s) {
   432                      if (S_ISDIR(s.st_mode)) {
   433                        auto rm_status = Rmdir(absolute_path);
   434                        if (!rm_status.ok() && undeleted_dirs) {
   435                          (*undeleted_dirs)++;
   436                        }
   437                      } else {
   438                        auto delete_status = Delete(absolute_path);
   439                        if (!delete_status.ok() && undeleted_files) {
   440                          (*undeleted_files)++;
   441                        }
   442                      }
   443                    });
   444  }
   445  
   446  PosixError RecursivelyCreateDir(absl::string_view path) {
   447    if (path.empty() || path == "/") {
   448      return PosixError(EINVAL, "Cannot create root!");
   449    }
   450  
   451    // Does it already exist, if so we're done.
   452    ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(path));
   453    if (exists) {
   454      return NoError();
   455    }
   456  
   457    // Do we need to create directories under us?
   458    auto dirname = Dirname(path);
   459    ASSIGN_OR_RETURN_ERRNO(exists, Exists(dirname));
   460    if (!exists) {
   461      RETURN_IF_ERRNO(RecursivelyCreateDir(dirname));
   462    }
   463  
   464    return Mkdir(path);
   465  }
   466  
   467  // Makes a path absolute with respect to an optional base. If no base is
   468  // provided it will use the current working directory.
   469  PosixErrorOr<std::string> MakeAbsolute(absl::string_view filename,
   470                                         absl::string_view base) {
   471    if (filename.empty()) {
   472      return PosixError(EINVAL, "filename cannot be empty.");
   473    }
   474  
   475    if (filename[0] == '/') {
   476      // This path is already absolute.
   477      return std::string(filename);
   478    }
   479  
   480    std::string actual_base;
   481    if (!base.empty()) {
   482      actual_base = std::string(base);
   483    } else {
   484      auto cwd_or = GetCWD();
   485      RETURN_IF_ERRNO(cwd_or.error());
   486      actual_base = cwd_or.ValueOrDie();
   487    }
   488  
   489    // Reverse iterate removing trailing slashes, effectively right trim '/'.
   490    for (int i = actual_base.size() - 1; i >= 0 && actual_base[i] == '/'; --i) {
   491      actual_base.erase(i, 1);
   492    }
   493  
   494    if (filename == ".") {
   495      return actual_base.empty() ? "/" : actual_base;
   496    }
   497  
   498    return absl::StrCat(actual_base, "/", filename);
   499  }
   500  
   501  std::string CleanPath(const absl::string_view unclean_path) {
   502    std::string path = std::string(unclean_path);
   503    const char* src = path.c_str();
   504    std::string::iterator dst = path.begin();
   505  
   506    // Check for absolute path and determine initial backtrack limit.
   507    const bool is_absolute_path = *src == '/';
   508    if (is_absolute_path) {
   509      *dst++ = *src++;
   510      while (*src == '/') ++src;
   511    }
   512    std::string::const_iterator backtrack_limit = dst;
   513  
   514    // Process all parts
   515    while (*src) {
   516      bool parsed = false;
   517  
   518      if (src[0] == '.') {
   519        //  1dot ".<whateverisnext>", check for END or SEP.
   520        if (src[1] == '/' || !src[1]) {
   521          if (*++src) {
   522            ++src;
   523          }
   524          parsed = true;
   525        } else if (src[1] == '.' && (src[2] == '/' || !src[2])) {
   526          // 2dot END or SEP (".." | "../<whateverisnext>").
   527          src += 2;
   528          if (dst != backtrack_limit) {
   529            // We can backtrack the previous part
   530            for (--dst; dst != backtrack_limit && dst[-1] != '/'; --dst) {
   531              // Empty.
   532            }
   533          } else if (!is_absolute_path) {
   534            // Failed to backtrack and we can't skip it either. Rewind and copy.
   535            src -= 2;
   536            *dst++ = *src++;
   537            *dst++ = *src++;
   538            if (*src) {
   539              *dst++ = *src;
   540            }
   541            // We can never backtrack over a copied "../" part so set new limit.
   542            backtrack_limit = dst;
   543          }
   544          if (*src) {
   545            ++src;
   546          }
   547          parsed = true;
   548        }
   549      }
   550  
   551      // If not parsed, copy entire part until the next SEP or EOS.
   552      if (!parsed) {
   553        while (*src && *src != '/') {
   554          *dst++ = *src++;
   555        }
   556        if (*src) {
   557          *dst++ = *src++;
   558        }
   559      }
   560  
   561      // Skip consecutive SEP occurrences
   562      while (*src == '/') {
   563        ++src;
   564      }
   565    }
   566  
   567    // Calculate and check the length of the cleaned path.
   568    int path_length = dst - path.begin();
   569    if (path_length != 0) {
   570      // Remove trailing '/' except if it is root path ("/" ==> path_length := 1)
   571      if (path_length > 1 && path[path_length - 1] == '/') {
   572        --path_length;
   573      }
   574      path.resize(path_length);
   575    } else {
   576      // The cleaned path is empty; assign "." as per the spec.
   577      path.assign(1, '.');
   578    }
   579    return path;
   580  }
   581  
   582  PosixErrorOr<std::string> GetRelativePath(absl::string_view source,
   583                                            absl::string_view dest) {
   584    if (!absl::StartsWith(source, "/") || !absl::StartsWith(dest, "/")) {
   585      // At least one of the inputs is not an absolute path.
   586      return PosixError(
   587          EINVAL,
   588          "GetRelativePath: At least one of the inputs is not an absolute path.");
   589    }
   590    const std::string clean_source = CleanPath(source);
   591    const std::string clean_dest = CleanPath(dest);
   592    auto source_parts = absl::StrSplit(clean_source, '/', absl::SkipEmpty());
   593    auto dest_parts = absl::StrSplit(clean_dest, '/', absl::SkipEmpty());
   594    auto source_iter = source_parts.begin();
   595    auto dest_iter = dest_parts.begin();
   596  
   597    // Advance past common prefix.
   598    while (source_iter != source_parts.end() && dest_iter != dest_parts.end() &&
   599           *source_iter == *dest_iter) {
   600      ++source_iter;
   601      ++dest_iter;
   602    }
   603  
   604    // Build result backtracking.
   605    std::string result = "";
   606    while (source_iter != source_parts.end()) {
   607      absl::StrAppend(&result, "../");
   608      ++source_iter;
   609    }
   610  
   611    // Add remaining path to dest.
   612    while (dest_iter != dest_parts.end()) {
   613      absl::StrAppend(&result, *dest_iter, "/");
   614      ++dest_iter;
   615    }
   616  
   617    if (result.empty()) {
   618      return std::string(".");
   619    }
   620  
   621    // Remove trailing slash.
   622    result.erase(result.size() - 1);
   623    return result;
   624  }
   625  
   626  absl::string_view Dirname(absl::string_view path) {
   627    return SplitPath(path).first;
   628  }
   629  
   630  absl::string_view Basename(absl::string_view path) {
   631    return SplitPath(path).second;
   632  }
   633  
   634  std::pair<absl::string_view, absl::string_view> SplitPath(
   635      absl::string_view path) {
   636    std::string::size_type pos = path.find_last_of('/');
   637  
   638    // Handle the case with no '/' in 'path'.
   639    if (pos == absl::string_view::npos) {
   640      return std::make_pair(path.substr(0, 0), path);
   641    }
   642  
   643    // Handle the case with a single leading '/' in 'path'.
   644    if (pos == 0) {
   645      return std::make_pair(path.substr(0, 1), absl::ClippedSubstr(path, 1));
   646    }
   647  
   648    return std::make_pair(path.substr(0, pos),
   649                          absl::ClippedSubstr(path, pos + 1));
   650  }
   651  
   652  std::string JoinPath(absl::string_view path1, absl::string_view path2) {
   653    if (path1.empty()) {
   654      return std::string(path2);
   655    }
   656    if (path2.empty()) {
   657      return std::string(path1);
   658    }
   659  
   660    if (path1.back() == '/') {
   661      if (path2.front() == '/') {
   662        return absl::StrCat(path1, absl::ClippedSubstr(path2, 1));
   663      }
   664    } else {
   665      if (path2.front() != '/') {
   666        return absl::StrCat(path1, "/", path2);
   667      }
   668    }
   669    return absl::StrCat(path1, path2);
   670  }
   671  
   672  PosixErrorOr<std::string> ProcessExePath(int pid) {
   673    if (pid <= 0) {
   674      return PosixError(EINVAL, "Invalid pid specified");
   675    }
   676  
   677    return ReadLink(absl::StrCat("/proc/", pid, "/exe"));
   678  }
   679  
   680  #ifdef __linux__
   681  PosixErrorOr<bool> IsTmpfs(const std::string& path) {
   682    struct statfs stat;
   683    if (statfs(path.c_str(), &stat)) {
   684      if (errno == ENOENT) {
   685        // Nothing at path, don't raise this as an error. Instead, just report no
   686        // tmpfs at path.
   687        return false;
   688      }
   689      return PosixError(errno,
   690                        absl::StrFormat("statfs(\"%s\", %#p)", path, &stat));
   691    }
   692    return stat.f_type == TMPFS_MAGIC;
   693  }
   694  #endif  // __linux__
   695  
   696  PosixErrorOr<bool> IsOverlayfs(const std::string& path) {
   697    struct statfs stat;
   698    if (statfs(path.c_str(), &stat)) {
   699      if (errno == ENOENT) {
   700        // Nothing at path, don't raise this as an error. Instead, just report no
   701        // overlayfs at path.
   702        return false;
   703      }
   704      return PosixError(errno,
   705                        absl::StrFormat("statfs(\"%s\", %#p)", path, &stat));
   706    }
   707    return stat.f_type == OVERLAYFS_SUPER_MAGIC;
   708  }
   709  
   710  PosixError CheckSameFile(const FileDescriptor& fd1, const FileDescriptor& fd2) {
   711    struct stat stat_result1, stat_result2;
   712    int res = fstat(fd1.get(), &stat_result1);
   713    if (res < 0) {
   714      return PosixError(errno, absl::StrCat("fstat ", fd1.get()));
   715    }
   716  
   717    res = fstat(fd2.get(), &stat_result2);
   718    if (res < 0) {
   719      return PosixError(errno, absl::StrCat("fstat ", fd2.get()));
   720    }
   721    EXPECT_EQ(stat_result1.st_dev, stat_result2.st_dev);
   722    EXPECT_EQ(stat_result1.st_ino, stat_result2.st_ino);
   723  
   724    return NoError();
   725  }
   726  }  // namespace testing
   727  }  // namespace gvisor