gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/stat.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 <errno.h>
    16  #include <fcntl.h>
    17  #include <sys/stat.h>
    18  #include <sys/statfs.h>
    19  #include <sys/types.h>
    20  #include <unistd.h>
    21  
    22  #include <string>
    23  #include <vector>
    24  
    25  #include "gmock/gmock.h"
    26  #include "gtest/gtest.h"
    27  #include "absl/strings/match.h"
    28  #include "absl/strings/str_cat.h"
    29  #include "absl/strings/string_view.h"
    30  #include "test/syscalls/linux/file_base.h"
    31  #include "test/util/cleanup.h"
    32  #include "test/util/file_descriptor.h"
    33  #include "test/util/fs_util.h"
    34  #include "test/util/save_util.h"
    35  #include "test/util/temp_path.h"
    36  #include "test/util/test_util.h"
    37  
    38  #ifndef AT_STATX_FORCE_SYNC
    39  #define AT_STATX_FORCE_SYNC 0x2000
    40  #endif
    41  #ifndef AT_STATX_DONT_SYNC
    42  #define AT_STATX_DONT_SYNC 0x4000
    43  #endif
    44  
    45  namespace gvisor {
    46  namespace testing {
    47  
    48  namespace {
    49  
    50  class StatTest : public FileTest {};
    51  
    52  TEST_F(StatTest, FstatatAbs) {
    53    struct stat st;
    54  
    55    // Check that the stat works.
    56    EXPECT_THAT(fstatat(AT_FDCWD, test_file_name_.c_str(), &st, 0),
    57                SyscallSucceeds());
    58    EXPECT_TRUE(S_ISREG(st.st_mode));
    59  }
    60  
    61  TEST_F(StatTest, FstatatEmptyPath) {
    62    struct stat st;
    63    const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY));
    64  
    65    // Check that the stat works.
    66    EXPECT_THAT(fstatat(fd.get(), "", &st, AT_EMPTY_PATH), SyscallSucceeds());
    67    EXPECT_TRUE(S_ISREG(st.st_mode));
    68  }
    69  
    70  TEST_F(StatTest, FstatatRel) {
    71    struct stat st;
    72    int dirfd;
    73    auto filename = std::string(Basename(test_file_name_));
    74  
    75    // Open the temporary directory read-only.
    76    ASSERT_THAT(dirfd = open(GetAbsoluteTestTmpdir().c_str(), O_RDONLY),
    77                SyscallSucceeds());
    78  
    79    // Check that the stat works.
    80    EXPECT_THAT(fstatat(dirfd, filename.c_str(), &st, 0), SyscallSucceeds());
    81    EXPECT_TRUE(S_ISREG(st.st_mode));
    82    close(dirfd);
    83  }
    84  
    85  TEST_F(StatTest, FstatatSymlink) {
    86    struct stat st;
    87  
    88    // Check that the link is followed.
    89    EXPECT_THAT(fstatat(AT_FDCWD, "/proc/self", &st, 0), SyscallSucceeds());
    90    EXPECT_TRUE(S_ISDIR(st.st_mode));
    91    EXPECT_FALSE(S_ISLNK(st.st_mode));
    92  
    93    // Check that the flag works.
    94    EXPECT_THAT(fstatat(AT_FDCWD, "/proc/self", &st, AT_SYMLINK_NOFOLLOW),
    95                SyscallSucceeds());
    96    EXPECT_TRUE(S_ISLNK(st.st_mode));
    97    EXPECT_FALSE(S_ISDIR(st.st_mode));
    98  }
    99  
   100  TEST_F(StatTest, Nlinks) {
   101    // Skip this test if we are testing overlayfs because overlayfs does not
   102    // (intentionally) return the correct nlink value for directories.
   103    // See fs/overlayfs/inode.c:ovl_getattr().
   104    SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(GetAbsoluteTestTmpdir())));
   105  
   106    TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   107  
   108    // Directory is initially empty, it should contain 2 links (one from itself,
   109    // one from ".").
   110    EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(2));
   111  
   112    // Create a file in the test directory. Files shouldn't increase the link
   113    // count on the base directory.
   114    TempPath file1 =
   115        ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(basedir.path()));
   116    EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(2));
   117  
   118    // Create subdirectories. This should increase the link count by 1 per
   119    // subdirectory.
   120    TempPath dir1 =
   121        ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(basedir.path()));
   122    EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(3));
   123    TempPath dir2 =
   124        ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(basedir.path()));
   125    EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(4));
   126  
   127    // Removing directories should reduce the link count.
   128    dir1.reset();
   129    EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(3));
   130    dir2.reset();
   131    EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(2));
   132  
   133    // Removing files should have no effect on link count.
   134    file1.reset();
   135    EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(2));
   136  }
   137  
   138  TEST_F(StatTest, BlocksIncreaseOnWrite) {
   139    struct stat st;
   140  
   141    // Stat the empty file.
   142    ASSERT_THAT(fstat(test_file_fd_.get(), &st), SyscallSucceeds());
   143  
   144    const int initial_blocks = st.st_blocks;
   145  
   146    // Write to the file, making sure to exceed the block size.
   147    std::vector<char> buf(2 * st.st_blksize, 'a');
   148    ASSERT_THAT(write(test_file_fd_.get(), buf.data(), buf.size()),
   149                SyscallSucceedsWithValue(buf.size()));
   150  
   151    // Stat the file again, and verify that number of allocated blocks has
   152    // increased.
   153    ASSERT_THAT(fstat(test_file_fd_.get(), &st), SyscallSucceeds());
   154    EXPECT_GT(st.st_blocks, initial_blocks);
   155  }
   156  
   157  TEST_F(StatTest, PathNotCleaned) {
   158    TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   159  
   160    // Create a file in the basedir.
   161    TempPath file =
   162        ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(basedir.path()));
   163  
   164    // Stating the file directly should succeed.
   165    struct stat buf;
   166    EXPECT_THAT(lstat(file.path().c_str(), &buf), SyscallSucceeds());
   167  
   168    // Try to stat the file using a directory that does not exist followed by
   169    // "..".  If the path is cleaned prior to stating (which it should not be)
   170    // then this will succeed.
   171    const std::string bad_path = JoinPath("/does_not_exist/..", file.path());
   172    EXPECT_THAT(lstat(bad_path.c_str(), &buf), SyscallFailsWithErrno(ENOENT));
   173  }
   174  
   175  TEST_F(StatTest, PathCanContainDotDot) {
   176    TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   177    TempPath subdir =
   178        ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(basedir.path()));
   179    const std::string subdir_name = std::string(Basename(subdir.path()));
   180  
   181    // Create a file in the subdir.
   182    TempPath file =
   183        ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(subdir.path()));
   184    const std::string file_name = std::string(Basename(file.path()));
   185  
   186    // Stat the file through a path that includes '..' and '.' but still resolves
   187    // to the file.
   188    const std::string good_path =
   189        JoinPath(basedir.path(), subdir_name, "..", subdir_name, ".", file_name);
   190    struct stat buf;
   191    EXPECT_THAT(lstat(good_path.c_str(), &buf), SyscallSucceeds());
   192  }
   193  
   194  TEST_F(StatTest, PathCanContainEmptyComponent) {
   195    TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   196  
   197    // Create a file in the basedir.
   198    TempPath file =
   199        ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(basedir.path()));
   200    const std::string file_name = std::string(Basename(file.path()));
   201  
   202    // Stat the file through a path that includes an empty component.  We have to
   203    // build this ourselves because JoinPath automatically removes empty
   204    // components.
   205    const std::string good_path = absl::StrCat(basedir.path(), "//", file_name);
   206    struct stat buf;
   207    EXPECT_THAT(lstat(good_path.c_str(), &buf), SyscallSucceeds());
   208  }
   209  
   210  TEST_F(StatTest, TrailingSlashNotCleanedReturnsENOTDIR) {
   211    TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   212  
   213    // Create a file in the basedir.
   214    TempPath file =
   215        ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(basedir.path()));
   216  
   217    // Stat the file with an extra "/" on the end of it.  Since file is not a
   218    // directory, this should return ENOTDIR.
   219    const std::string bad_path = absl::StrCat(file.path(), "/");
   220    struct stat buf;
   221    EXPECT_THAT(lstat(bad_path.c_str(), &buf), SyscallFailsWithErrno(ENOTDIR));
   222  }
   223  
   224  TEST_F(StatTest, FstatFileWithOpath) {
   225    struct stat st;
   226    TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   227    FileDescriptor fd =
   228        ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_PATH));
   229  
   230    // Stat the directory.
   231    ASSERT_THAT(fstat(fd.get(), &st), SyscallSucceeds());
   232  }
   233  
   234  TEST_F(StatTest, FstatDirWithOpath) {
   235    struct stat st;
   236    TempPath tmpdir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   237    FileDescriptor dirfd = ASSERT_NO_ERRNO_AND_VALUE(
   238        Open(tmpdir.path().c_str(), O_PATH | O_DIRECTORY));
   239  
   240    // Stat the directory.
   241    ASSERT_THAT(fstat(dirfd.get(), &st), SyscallSucceeds());
   242  }
   243  
   244  // fstatat with an O_PATH fd
   245  TEST_F(StatTest, FstatatDirWithOpath) {
   246    TempPath tmpdir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   247    FileDescriptor dirfd = ASSERT_NO_ERRNO_AND_VALUE(
   248        Open(tmpdir.path().c_str(), O_PATH | O_DIRECTORY));
   249    TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   250  
   251    struct stat st = {};
   252    EXPECT_THAT(fstatat(dirfd.get(), tmpfile.path().c_str(), &st, 0),
   253                SyscallSucceeds());
   254    EXPECT_FALSE(S_ISDIR(st.st_mode));
   255    EXPECT_TRUE(S_ISREG(st.st_mode));
   256  }
   257  
   258  // Test fstatating a symlink directory.
   259  TEST_F(StatTest, FstatatSymlinkDir) {
   260    // Create a directory and symlink to it.
   261    const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   262  
   263    const std::string symlink_to_dir = NewTempAbsPath();
   264    EXPECT_THAT(symlink(dir.path().c_str(), symlink_to_dir.c_str()),
   265                SyscallSucceeds());
   266    auto cleanup = Cleanup([&symlink_to_dir]() {
   267      EXPECT_THAT(unlink(symlink_to_dir.c_str()), SyscallSucceeds());
   268    });
   269  
   270    // Fstatat the link with AT_SYMLINK_NOFOLLOW should return symlink data.
   271    struct stat st = {};
   272    EXPECT_THAT(
   273        fstatat(AT_FDCWD, symlink_to_dir.c_str(), &st, AT_SYMLINK_NOFOLLOW),
   274        SyscallSucceeds());
   275    EXPECT_FALSE(S_ISDIR(st.st_mode));
   276    EXPECT_TRUE(S_ISLNK(st.st_mode));
   277  
   278    // Fstatat the link should return dir data.
   279    EXPECT_THAT(fstatat(AT_FDCWD, symlink_to_dir.c_str(), &st, 0),
   280                SyscallSucceeds());
   281    EXPECT_TRUE(S_ISDIR(st.st_mode));
   282    EXPECT_FALSE(S_ISLNK(st.st_mode));
   283  }
   284  
   285  // Test fstatating a symlink directory with trailing slash.
   286  TEST_F(StatTest, FstatatSymlinkDirWithTrailingSlash) {
   287    // Create a directory and symlink to it.
   288    const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   289    const std::string symlink_to_dir = NewTempAbsPath();
   290    EXPECT_THAT(symlink(dir.path().c_str(), symlink_to_dir.c_str()),
   291                SyscallSucceeds());
   292    auto cleanup = Cleanup([&symlink_to_dir]() {
   293      EXPECT_THAT(unlink(symlink_to_dir.c_str()), SyscallSucceeds());
   294    });
   295  
   296    // Fstatat on the symlink with a trailing slash should return the directory
   297    // data.
   298    struct stat st = {};
   299    EXPECT_THAT(
   300        fstatat(AT_FDCWD, absl::StrCat(symlink_to_dir, "/").c_str(), &st, 0),
   301        SyscallSucceeds());
   302    EXPECT_TRUE(S_ISDIR(st.st_mode));
   303    EXPECT_FALSE(S_ISLNK(st.st_mode));
   304  
   305    // Fstatat on the symlink with a trailing slash with AT_SYMLINK_NOFOLLOW
   306    // should return the directory data.
   307    // Symlink to directory with trailing slash will ignore AT_SYMLINK_NOFOLLOW.
   308    EXPECT_THAT(fstatat(AT_FDCWD, absl::StrCat(symlink_to_dir, "/").c_str(), &st,
   309                        AT_SYMLINK_NOFOLLOW),
   310                SyscallSucceeds());
   311    EXPECT_TRUE(S_ISDIR(st.st_mode));
   312    EXPECT_FALSE(S_ISLNK(st.st_mode));
   313  }
   314  
   315  // Test fstatating a symlink directory with a trailing slash
   316  // should return same stat data with fstatating directory.
   317  TEST_F(StatTest, FstatatSymlinkDirWithTrailingSlashSameInode) {
   318    // Create a directory and symlink to it.
   319    const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   320  
   321    // We are going to assert that the symlink inode id is the same as the linked
   322    // dir's inode id. In order for the inode id to be stable across
   323    // save/restore, it must be kept open. The FileDescriptor type will do that
   324    // for us automatically.
   325    auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY | O_DIRECTORY));
   326  
   327    const std::string symlink_to_dir = NewTempAbsPath();
   328    EXPECT_THAT(symlink(dir.path().c_str(), symlink_to_dir.c_str()),
   329                SyscallSucceeds());
   330    auto cleanup = Cleanup([&symlink_to_dir]() {
   331      EXPECT_THAT(unlink(symlink_to_dir.c_str()), SyscallSucceeds());
   332    });
   333  
   334    // Fstatat on the symlink with a trailing slash should return the directory
   335    // data.
   336    struct stat st = {};
   337    EXPECT_THAT(fstatat(AT_FDCWD, absl::StrCat(symlink_to_dir, "/").c_str(), &st,
   338                        AT_SYMLINK_NOFOLLOW),
   339                SyscallSucceeds());
   340    EXPECT_TRUE(S_ISDIR(st.st_mode));
   341  
   342    // Dir and symlink should point to same inode.
   343    struct stat st_dir = {};
   344    EXPECT_THAT(
   345        fstatat(AT_FDCWD, dir.path().c_str(), &st_dir, AT_SYMLINK_NOFOLLOW),
   346        SyscallSucceeds());
   347    EXPECT_EQ(st.st_ino, st_dir.st_ino);
   348  }
   349  
   350  TEST_F(StatTest, LeadingDoubleSlash) {
   351    // Create a file, and make sure we can stat it.
   352    TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   353    struct stat st;
   354    ASSERT_THAT(lstat(file.path().c_str(), &st), SyscallSucceeds());
   355  
   356    // Now add an extra leading slash.
   357    const std::string double_slash_path = absl::StrCat("/", file.path());
   358    ASSERT_TRUE(absl::StartsWith(double_slash_path, "//"));
   359  
   360    // We should be able to stat the new path, and it should resolve to the same
   361    // file (same device and inode).
   362    struct stat double_slash_st;
   363    ASSERT_THAT(lstat(double_slash_path.c_str(), &double_slash_st),
   364                SyscallSucceeds());
   365    EXPECT_EQ(st.st_dev, double_slash_st.st_dev);
   366    // Inode numbers for gofer-accessed files may change across save/restore.
   367    if (!IsRunningWithSaveRestore()) {
   368      EXPECT_EQ(st.st_ino, double_slash_st.st_ino);
   369    }
   370  }
   371  
   372  // Test that a rename doesn't change the underlying file.
   373  TEST_F(StatTest, StatDoesntChangeAfterRename) {
   374    const TempPath old_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   375    const TempPath new_path(NewTempAbsPath());
   376  
   377    struct stat st_old = {};
   378    struct stat st_new = {};
   379  
   380    ASSERT_THAT(stat(old_file.path().c_str(), &st_old), SyscallSucceeds());
   381    ASSERT_THAT(rename(old_file.path().c_str(), new_path.path().c_str()),
   382                SyscallSucceeds());
   383    ASSERT_THAT(stat(new_path.path().c_str(), &st_new), SyscallSucceeds());
   384  
   385    EXPECT_EQ(st_old.st_nlink, st_new.st_nlink);
   386    EXPECT_EQ(st_old.st_dev, st_new.st_dev);
   387    // Inode numbers for gofer-accessed files on which no reference is held may
   388    // change across save/restore because the information that the gofer client
   389    // uses to track file identity (9P QID path) is inconsistent between gofer
   390    // processes, which are restarted across save/restore.
   391    //
   392    // Overlay filesystems may synthesize directory inode numbers on the fly.
   393    if (!IsRunningWithSaveRestore() &&
   394        !ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(GetAbsoluteTestTmpdir()))) {
   395      EXPECT_EQ(st_old.st_ino, st_new.st_ino);
   396    }
   397    EXPECT_EQ(st_old.st_mode, st_new.st_mode);
   398    EXPECT_EQ(st_old.st_uid, st_new.st_uid);
   399    EXPECT_EQ(st_old.st_gid, st_new.st_gid);
   400    EXPECT_EQ(st_old.st_size, st_new.st_size);
   401  }
   402  
   403  // Test link counts with a regular file as the child.
   404  TEST_F(StatTest, LinkCountsWithRegularFileChild) {
   405    const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   406  
   407    struct stat st_parent_before = {};
   408    ASSERT_THAT(stat(dir.path().c_str(), &st_parent_before), SyscallSucceeds());
   409    EXPECT_EQ(st_parent_before.st_nlink, 2);
   410  
   411    // Adding a regular file doesn't adjust the parent's link count.
   412    const TempPath child =
   413        ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path()));
   414  
   415    struct stat st_parent_after = {};
   416    ASSERT_THAT(stat(dir.path().c_str(), &st_parent_after), SyscallSucceeds());
   417    EXPECT_EQ(st_parent_after.st_nlink, 2);
   418  
   419    // The child should have a single link from the parent.
   420    struct stat st_child = {};
   421    ASSERT_THAT(stat(child.path().c_str(), &st_child), SyscallSucceeds());
   422    EXPECT_TRUE(S_ISREG(st_child.st_mode));
   423    EXPECT_EQ(st_child.st_nlink, 1);
   424  
   425    // Finally unlinking the child should not affect the parent's link count.
   426    ASSERT_THAT(unlink(child.path().c_str()), SyscallSucceeds());
   427    ASSERT_THAT(stat(dir.path().c_str(), &st_parent_after), SyscallSucceeds());
   428    EXPECT_EQ(st_parent_after.st_nlink, 2);
   429  }
   430  
   431  // This test verifies that inodes remain around when there is an open fd
   432  // after link count hits 0.
   433  //
   434  // It is marked NoSave because we don't support saving unlinked files.
   435  TEST_F(StatTest, ZeroLinksOpenFdRegularFileChild_NoSave) {
   436    // Setting the enviornment variable GVISOR_GOFER_UNCACHED to any value
   437    // will prevent this test from running, see the tmpfs lifecycle.
   438    //
   439    // We need to support this because when a file is unlinked and we forward
   440    // the stat to the gofer it would return ENOENT.
   441    const char* uncached_gofer = getenv("GVISOR_GOFER_UNCACHED");
   442    SKIP_IF(uncached_gofer != nullptr);
   443  
   444    const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   445    const TempPath child = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
   446        dir.path(), "hello", TempPath::kDefaultFileMode));
   447  
   448    // The child should have a single link from the parent.
   449    struct stat st_child_before = {};
   450    ASSERT_THAT(stat(child.path().c_str(), &st_child_before), SyscallSucceeds());
   451    EXPECT_TRUE(S_ISREG(st_child_before.st_mode));
   452    EXPECT_EQ(st_child_before.st_nlink, 1);
   453    EXPECT_EQ(st_child_before.st_size, 5);  // Hello is 5 bytes.
   454  
   455    // Open the file so we can fstat after unlinking.
   456    const FileDescriptor fd =
   457        ASSERT_NO_ERRNO_AND_VALUE(Open(child.path(), O_RDONLY));
   458  
   459    // Now a stat should return ENOENT but we should still be able to stat
   460    // via the open fd and fstat.
   461    ASSERT_THAT(unlink(child.path().c_str()), SyscallSucceeds());
   462  
   463    // Since the file has no more links stat should fail.
   464    struct stat st_child_after = {};
   465    ASSERT_THAT(stat(child.path().c_str(), &st_child_after),
   466                SyscallFailsWithErrno(ENOENT));
   467  
   468    // Fstat should still allow us to access the same file via the fd.
   469    struct stat st_child_fd = {};
   470    ASSERT_THAT(fstat(fd.get(), &st_child_fd), SyscallSucceeds());
   471    EXPECT_EQ(st_child_before.st_dev, st_child_fd.st_dev);
   472    EXPECT_EQ(st_child_before.st_ino, st_child_fd.st_ino);
   473    EXPECT_EQ(st_child_before.st_mode, st_child_fd.st_mode);
   474    EXPECT_EQ(st_child_before.st_uid, st_child_fd.st_uid);
   475    EXPECT_EQ(st_child_before.st_gid, st_child_fd.st_gid);
   476    EXPECT_EQ(st_child_before.st_size, st_child_fd.st_size);
   477  
   478    // TODO(b/34861058): This isn't ideal but since fstatfs(2) will always return
   479    // OVERLAYFS_SUPER_MAGIC we have no way to know if this fs is backed by a
   480    // gofer which doesn't support links.
   481    EXPECT_TRUE(st_child_fd.st_nlink == 0 || st_child_fd.st_nlink == 1);
   482  }
   483  
   484  // Test link counts with a directory as the child.
   485  TEST_F(StatTest, LinkCountsWithDirChild) {
   486    // Skip this test if we are testing overlayfs because overlayfs does not
   487    // (intentionally) return the correct nlink value for directories.
   488    // See fs/overlayfs/inode.c:ovl_getattr().
   489    SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(GetAbsoluteTestTmpdir())));
   490  
   491    const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   492  
   493    // Before a child is added the two links are "." and the link from the parent.
   494    struct stat st_parent_before = {};
   495    ASSERT_THAT(stat(dir.path().c_str(), &st_parent_before), SyscallSucceeds());
   496    EXPECT_EQ(st_parent_before.st_nlink, 2);
   497  
   498    // Create a subdirectory and stat for the parent link counts.
   499    const TempPath sub_dir =
   500        ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path()));
   501  
   502    // The three links are ".", the link from the parent, and the link from
   503    // the child as "..".
   504    struct stat st_parent_after = {};
   505    ASSERT_THAT(stat(dir.path().c_str(), &st_parent_after), SyscallSucceeds());
   506    EXPECT_EQ(st_parent_after.st_nlink, 3);
   507  
   508    // The child will have 1 link from the parent and 1 link which represents ".".
   509    struct stat st_child = {};
   510    ASSERT_THAT(stat(sub_dir.path().c_str(), &st_child), SyscallSucceeds());
   511    EXPECT_TRUE(S_ISDIR(st_child.st_mode));
   512    EXPECT_EQ(st_child.st_nlink, 2);
   513  
   514    // Finally delete the child dir and the parent link count should return to 2.
   515    ASSERT_THAT(rmdir(sub_dir.path().c_str()), SyscallSucceeds());
   516    ASSERT_THAT(stat(dir.path().c_str(), &st_parent_after), SyscallSucceeds());
   517  
   518    // Now we should only have links from the parent and "." since the subdir
   519    // has been removed.
   520    EXPECT_EQ(st_parent_after.st_nlink, 2);
   521  }
   522  
   523  // Test statting a child of a non-directory.
   524  TEST_F(StatTest, ChildOfNonDir) {
   525    // Create a path that has a child of a regular file.
   526    const std::string filename = JoinPath(test_file_name_, "child");
   527  
   528    // Statting the path should return ENOTDIR.
   529    struct stat st;
   530    EXPECT_THAT(lstat(filename.c_str(), &st), SyscallFailsWithErrno(ENOTDIR));
   531  }
   532  
   533  // Test lstating a symlink directory.
   534  TEST_F(StatTest, LstatSymlinkDir) {
   535    // Create a directory and symlink to it.
   536    const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   537    const std::string symlink_to_dir = NewTempAbsPath();
   538    EXPECT_THAT(symlink(dir.path().c_str(), symlink_to_dir.c_str()),
   539                SyscallSucceeds());
   540    auto cleanup = Cleanup([&symlink_to_dir]() {
   541      EXPECT_THAT(unlink(symlink_to_dir.c_str()), SyscallSucceeds());
   542    });
   543  
   544    // Lstat on the symlink should return symlink data.
   545    struct stat st = {};
   546    ASSERT_THAT(lstat(symlink_to_dir.c_str(), &st), SyscallSucceeds());
   547    EXPECT_FALSE(S_ISDIR(st.st_mode));
   548    EXPECT_TRUE(S_ISLNK(st.st_mode));
   549  
   550    // Lstat on the symlink with a trailing slash should return the directory
   551    // data.
   552    ASSERT_THAT(lstat(absl::StrCat(symlink_to_dir, "/").c_str(), &st),
   553                SyscallSucceeds());
   554    EXPECT_TRUE(S_ISDIR(st.st_mode));
   555    EXPECT_FALSE(S_ISLNK(st.st_mode));
   556  }
   557  
   558  // Verify that we get an ELOOP from too many symbolic links even when there
   559  // are directories in the middle.
   560  TEST_F(StatTest, LstatELOOPPath) {
   561    const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   562    std::string subdir_base = "subdir";
   563    ASSERT_THAT(mkdir(JoinPath(dir.path(), subdir_base).c_str(), 0755),
   564                SyscallSucceeds());
   565  
   566    std::string target = JoinPath(dir.path(), subdir_base, subdir_base);
   567    std::string dst = JoinPath("..", subdir_base);
   568    ASSERT_THAT(symlink(dst.c_str(), target.c_str()), SyscallSucceeds());
   569    auto cleanup = Cleanup(
   570        [&target]() { EXPECT_THAT(unlink(target.c_str()), SyscallSucceeds()); });
   571  
   572    // Now build a path which is /subdir/subdir/... repeated many times so that
   573    // we can build a path that is shorter than PATH_MAX but can still cause
   574    // too many symbolic links. Note: Every other subdir is actually a directory
   575    // so we're not in a situation where it's a -> b -> a -> b, where a and b
   576    // are symbolic links.
   577    std::string path = dir.path();
   578    std::string subdir_append = absl::StrCat("/", subdir_base);
   579    do {
   580      absl::StrAppend(&path, subdir_append);
   581      // Keep appending /subdir until we would overflow PATH_MAX.
   582    } while ((path.size() + subdir_append.size()) < PATH_MAX);
   583  
   584    struct stat s = {};
   585    ASSERT_THAT(lstat(path.c_str(), &s), SyscallFailsWithErrno(ELOOP));
   586  }
   587  
   588  TEST(SimpleStatTest, DifferentFilesHaveDifferentDeviceInodeNumberPairs) {
   589    TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   590    TempPath file2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   591  
   592    MaybeSave();
   593    struct stat st1 = ASSERT_NO_ERRNO_AND_VALUE(Lstat(file1.path()));
   594    MaybeSave();
   595    struct stat st2 = ASSERT_NO_ERRNO_AND_VALUE(Lstat(file2.path()));
   596    EXPECT_FALSE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
   597        << "both files have device number " << st1.st_dev << " and inode number "
   598        << st1.st_ino;
   599  }
   600  
   601  // Ensure that inode allocation for anonymous devices work correctly across
   602  // save/restore. In particular, inode numbers should be unique across S/R.
   603  TEST(SimpleStatTest, AnonDeviceAllocatesUniqueInodesAcrossSaveRestore) {
   604    // Use sockets as a convenient way to create inodes on an anonymous device.
   605    int fd;
   606    ASSERT_THAT(fd = socket(AF_UNIX, SOCK_STREAM, 0), SyscallSucceeds());
   607    FileDescriptor fd1(fd);
   608    MaybeSave();
   609    ASSERT_THAT(fd = socket(AF_UNIX, SOCK_STREAM, 0), SyscallSucceeds());
   610    FileDescriptor fd2(fd);
   611  
   612    struct stat st1;
   613    struct stat st2;
   614    ASSERT_THAT(fstat(fd1.get(), &st1), SyscallSucceeds());
   615    ASSERT_THAT(fstat(fd2.get(), &st2), SyscallSucceeds());
   616  
   617    // The two fds should have different inode numbers.
   618    EXPECT_NE(st2.st_ino, st1.st_ino);
   619  
   620    // Verify again after another S/R cycle. The inode numbers should remain the
   621    // same.
   622    MaybeSave();
   623  
   624    struct stat st1_after;
   625    struct stat st2_after;
   626    ASSERT_THAT(fstat(fd1.get(), &st1_after), SyscallSucceeds());
   627    ASSERT_THAT(fstat(fd2.get(), &st2_after), SyscallSucceeds());
   628  
   629    EXPECT_EQ(st1_after.st_ino, st1.st_ino);
   630    EXPECT_EQ(st2_after.st_ino, st2.st_ino);
   631  }
   632  
   633  #ifndef SYS_statx
   634  #if defined(__x86_64__)
   635  #define SYS_statx 332
   636  #elif defined(__aarch64__)
   637  #define SYS_statx 291
   638  #elif defined(__riscv)
   639  #define SYS_statx 291
   640  #else
   641  #error "Unknown architecture"
   642  #endif
   643  #endif  // SYS_statx
   644  
   645  #ifndef STATX_ALL
   646  #define STATX_ALL 0x00000fffU
   647  #endif  // STATX_ALL
   648  
   649  // struct kernel_statx_timestamp is a Linux statx_timestamp struct.
   650  struct kernel_statx_timestamp {
   651    int64_t tv_sec;
   652    uint32_t tv_nsec;
   653    int32_t __reserved;
   654  };
   655  
   656  // struct kernel_statx is a Linux statx struct. Old versions of glibc do not
   657  // expose it. See include/uapi/linux/stat.h
   658  struct kernel_statx {
   659    uint32_t stx_mask;
   660    uint32_t stx_blksize;
   661    uint64_t stx_attributes;
   662    uint32_t stx_nlink;
   663    uint32_t stx_uid;
   664    uint32_t stx_gid;
   665    uint16_t stx_mode;
   666    uint16_t __spare0[1];
   667    uint64_t stx_ino;
   668    uint64_t stx_size;
   669    uint64_t stx_blocks;
   670    uint64_t stx_attributes_mask;
   671    struct kernel_statx_timestamp stx_atime;
   672    struct kernel_statx_timestamp stx_btime;
   673    struct kernel_statx_timestamp stx_ctime;
   674    struct kernel_statx_timestamp stx_mtime;
   675    uint32_t stx_rdev_major;
   676    uint32_t stx_rdev_minor;
   677    uint32_t stx_dev_major;
   678    uint32_t stx_dev_minor;
   679    uint64_t __spare2[14];
   680  };
   681  
   682  int statx(int dirfd, const char* pathname, int flags, unsigned int mask,
   683            struct kernel_statx* statxbuf) {
   684    return syscall(SYS_statx, dirfd, pathname, flags, mask, statxbuf);
   685  }
   686  
   687  TEST_F(StatTest, StatxAbsPath) {
   688    SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 &&
   689            errno == ENOSYS);
   690  
   691    struct kernel_statx stx;
   692    EXPECT_THAT(statx(-1, test_file_name_.c_str(), 0, STATX_ALL, &stx),
   693                SyscallSucceeds());
   694    EXPECT_TRUE(S_ISREG(stx.stx_mode));
   695  }
   696  
   697  TEST_F(StatTest, StatxRelPathDirFD) {
   698    SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 &&
   699            errno == ENOSYS);
   700  
   701    struct kernel_statx stx;
   702    auto const dirfd =
   703        ASSERT_NO_ERRNO_AND_VALUE(Open(GetAbsoluteTestTmpdir(), O_RDONLY));
   704    auto filename = std::string(Basename(test_file_name_));
   705  
   706    EXPECT_THAT(statx(dirfd.get(), filename.c_str(), 0, STATX_ALL, &stx),
   707                SyscallSucceeds());
   708    EXPECT_TRUE(S_ISREG(stx.stx_mode));
   709  }
   710  
   711  TEST_F(StatTest, StatxRelPathCwd) {
   712    SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 &&
   713            errno == ENOSYS);
   714  
   715    ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds());
   716    auto filename = std::string(Basename(test_file_name_));
   717    struct kernel_statx stx;
   718    EXPECT_THAT(statx(AT_FDCWD, filename.c_str(), 0, STATX_ALL, &stx),
   719                SyscallSucceeds());
   720    EXPECT_TRUE(S_ISREG(stx.stx_mode));
   721  }
   722  
   723  TEST_F(StatTest, StatxEmptyPath) {
   724    SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 &&
   725            errno == ENOSYS);
   726  
   727    const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY));
   728    struct kernel_statx stx;
   729    EXPECT_THAT(statx(fd.get(), "", AT_EMPTY_PATH, STATX_ALL, &stx),
   730                SyscallSucceeds());
   731    EXPECT_TRUE(S_ISREG(stx.stx_mode));
   732  }
   733  
   734  TEST_F(StatTest, StatxDoesNotRejectExtraneousMaskBits) {
   735    SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 &&
   736            errno == ENOSYS);
   737  
   738    struct kernel_statx stx;
   739    // Set all mask bits except for STATX__RESERVED.
   740    uint mask = 0xffffffff & ~0x80000000;
   741    EXPECT_THAT(statx(-1, test_file_name_.c_str(), 0, mask, &stx),
   742                SyscallSucceeds());
   743    EXPECT_TRUE(S_ISREG(stx.stx_mode));
   744  }
   745  
   746  TEST_F(StatTest, StatxRejectsReservedMaskBit) {
   747    SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 &&
   748            errno == ENOSYS);
   749  
   750    struct kernel_statx stx;
   751    // Set STATX__RESERVED in the mask.
   752    EXPECT_THAT(statx(-1, test_file_name_.c_str(), 0, 0x80000000, &stx),
   753                SyscallFailsWithErrno(EINVAL));
   754  }
   755  
   756  TEST_F(StatTest, StatxSymlink) {
   757    SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 &&
   758            errno == ENOSYS);
   759  
   760    std::string parent_dir = GetAbsoluteTestTmpdir();
   761    TempPath link = ASSERT_NO_ERRNO_AND_VALUE(
   762        TempPath::CreateSymlinkTo(parent_dir, test_file_name_));
   763    std::string p = link.path();
   764  
   765    struct kernel_statx stx;
   766    EXPECT_THAT(statx(AT_FDCWD, p.c_str(), AT_SYMLINK_NOFOLLOW, STATX_ALL, &stx),
   767                SyscallSucceeds());
   768    EXPECT_TRUE(S_ISLNK(stx.stx_mode));
   769    EXPECT_THAT(statx(AT_FDCWD, p.c_str(), 0, STATX_ALL, &stx),
   770                SyscallSucceeds());
   771    EXPECT_TRUE(S_ISREG(stx.stx_mode));
   772  }
   773  
   774  TEST_F(StatTest, StatxInvalidFlags) {
   775    SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 &&
   776            errno == ENOSYS);
   777  
   778    struct kernel_statx stx;
   779    EXPECT_THAT(statx(AT_FDCWD, test_file_name_.c_str(), 12345, 0, &stx),
   780                SyscallFailsWithErrno(EINVAL));
   781  
   782    // Sync flags are mutually exclusive.
   783    EXPECT_THAT(statx(AT_FDCWD, test_file_name_.c_str(),
   784                      AT_STATX_FORCE_SYNC | AT_STATX_DONT_SYNC, 0, &stx),
   785                SyscallFailsWithErrno(EINVAL));
   786  }
   787  
   788  // TODO(b/270247637): AT_NO_AUTOMOUNT flag has no effect because gVisor does
   789  // not support automount yet.
   790  TEST_F(StatTest, StatIgnoreNoAutomount) {
   791    if (IsRunningOnGvisor() || statx(-1, nullptr, 0, 0, nullptr) == 0 ||
   792        errno != ENOSYS) {
   793      struct kernel_statx stx;
   794      EXPECT_THAT(
   795          statx(-1, test_file_name_.c_str(), AT_NO_AUTOMOUNT, STATX_ALL, &stx),
   796          SyscallSucceeds());
   797    }
   798  
   799    struct stat st;
   800    EXPECT_THAT(fstatat(AT_FDCWD, test_file_name_.c_str(), &st, AT_NO_AUTOMOUNT),
   801                SyscallSucceeds());
   802  }
   803  
   804  }  // namespace
   805  
   806  }  // namespace testing
   807  }  // namespace gvisor