Skip to content

Commit 1ea26f1

Browse files
manninglucasgvisor-bot
authored andcommitted
Add support for STATX_MNT_ID in statx.
PiperOrigin-RevId: 889466880
1 parent 7491461 commit 1ea26f1

5 files changed

Lines changed: 77 additions & 5 deletions

File tree

pkg/abi/linux/file.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ const (
266266
STATX_BLOCKS = 0x00000400
267267
STATX_BASIC_STATS = 0x000007ff
268268
STATX_BTIME = 0x00000800
269+
STATX_MNT_ID = 0x00001000
269270
STATX_ALL = 0x00000fff
270271
STATX__RESERVED = 0x80000000
271272
)
@@ -306,12 +307,13 @@ type Statx struct {
306307
RdevMinor uint32
307308
DevMajor uint32
308309
DevMinor uint32
310+
MntID uint64
309311
}
310312

311313
// String implements fmt.Stringer.String.
312314
func (s *Statx) String() string {
313-
return fmt.Sprintf("Statx{Mask: %#x, Mode: %s, UID: %d, GID: %d, Ino: %d, DevMajor: %d, DevMinor: %d, Size: %d, Blocks: %d, Blksize: %d, Nlink: %d, Atime: %s, Btime: %s, Ctime: %s, Mtime: %s, Attributes: %d, AttributesMask: %d, RdevMajor: %d, RdevMinor: %d}",
314-
s.Mask, FileMode(s.Mode), s.UID, s.GID, s.Ino, s.DevMajor, s.DevMinor, s.Size, s.Blocks, s.Blksize, s.Nlink, s.Atime.ToTime(), s.Btime.ToTime(), s.Ctime.ToTime(), s.Mtime.ToTime(), s.Attributes, s.AttributesMask, s.RdevMajor, s.RdevMinor)
315+
return fmt.Sprintf("Statx{Mask: %#x, Mode: %s, UID: %d, GID: %d, Ino: %d, DevMajor: %d, DevMinor: %d, Size: %d, Blocks: %d, Blksize: %d, Nlink: %d, Atime: %s, Btime: %s, Ctime: %s, Mtime: %s, Attributes: %d, AttributesMask: %d, RdevMajor: %d, RdevMinor: %d, MntId: %d}",
316+
s.Mask, FileMode(s.Mode), s.UID, s.GID, s.Ino, s.DevMajor, s.DevMinor, s.Size, s.Blocks, s.Blksize, s.Nlink, s.Atime.ToTime(), s.Btime.ToTime(), s.Ctime.ToTime(), s.Mtime.ToTime(), s.Attributes, s.AttributesMask, s.RdevMajor, s.RdevMinor, s.MntID)
315317
}
316318

317319
// SizeOfStatx is the size of a Statx struct.

pkg/sentry/vfs/file_description.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -540,17 +540,26 @@ func (fd *FileDescription) OnClose(ctx context.Context) error {
540540

541541
// Stat returns metadata for the file represented by fd.
542542
func (fd *FileDescription) Stat(ctx context.Context, opts StatOptions) (linux.Statx, error) {
543+
var (
544+
stat linux.Statx
545+
err error
546+
)
543547
if fd.opts.UseDentryMetadata {
544548
vfsObj := fd.vd.mount.vfs
545549
rp := vfsObj.getResolvingPath(auth.CredentialsFromContext(ctx), &PathOperation{
546550
Root: fd.vd,
547551
Start: fd.vd,
548552
})
549-
stat, err := fd.vd.mount.fs.impl.StatAt(ctx, rp, opts)
553+
stat, err = fd.vd.mount.fs.impl.StatAt(ctx, rp, opts)
550554
rp.Release(ctx)
551-
return stat, err
555+
} else {
556+
stat, err = fd.impl.Stat(ctx, opts)
552557
}
553-
return fd.impl.Stat(ctx, opts)
558+
if err == nil && opts.Mask&linux.STATX_MNT_ID != 0 {
559+
stat.MntID = fd.vd.mount.ID
560+
stat.Mask |= linux.STATX_MNT_ID
561+
}
562+
return stat, err
554563
}
555564

556565
// SetStat updates metadata for the file represented by fd.

pkg/sentry/vfs/vfs.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,10 @@ func (vfs *VirtualFilesystem) StatAt(ctx context.Context, creds *auth.Credential
640640
}
641641
stat, err := rp.mount.fs.impl.StatAt(ctx, rp, *opts)
642642
if err == nil {
643+
if opts.Mask&linux.STATX_MNT_ID != 0 {
644+
stat.MntID = rp.mount.ID
645+
stat.Mask |= linux.STATX_MNT_ID
646+
}
643647
return stat, nil
644648
}
645649
if !rp.handleError(ctx, err) {

test/syscalls/linux/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4084,6 +4084,7 @@ cc_binary(
40844084
linkstatic = 1,
40854085
malloc = "//test/util:errno_safe_allocator",
40864086
deps = select_gtest() + [
4087+
"//test/util:capability_util",
40874088
"//test/util:cleanup",
40884089
"//test/util:file_descriptor",
40894090
"//test/util:fs_util",

test/syscalls/linux/stat.cc

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
#include <errno.h>
1616
#include <fcntl.h>
17+
#include <linux/capability.h>
18+
#include <sys/mount.h>
1719
#include <sys/stat.h>
1820
#include <sys/statfs.h>
1921
#include <sys/types.h>
@@ -31,6 +33,8 @@
3133
#include "test/util/cleanup.h"
3234
#include "test/util/file_descriptor.h"
3335
#include "test/util/fs_util.h"
36+
#include "test/util/linux_capability_util.h"
37+
#include "test/util/posix_error.h"
3438
#include "test/util/save_util.h"
3539
#include "test/util/temp_path.h"
3640
#include "test/util/test_util.h"
@@ -680,6 +684,10 @@ TEST(SimpleStatTest, AnonDeviceAllocatesUniqueInodesAcrossSaveRestore) {
680684
#define STATX_ALL 0x00000fffU
681685
#endif // STATX_ALL
682686

687+
#ifndef STATX_MNT_ID
688+
#define STATX_MNT_ID 0x00001000U
689+
#endif // STATX_MNT_ID
690+
683691
// struct kernel_statx_timestamp is a Linux statx_timestamp struct.
684692
struct kernel_statx_timestamp {
685693
int64_t tv_sec;
@@ -710,6 +718,7 @@ struct kernel_statx {
710718
uint32_t stx_rdev_minor;
711719
uint32_t stx_dev_major;
712720
uint32_t stx_dev_minor;
721+
uint64_t stx_mnt_id;
713722
uint64_t __spare2[14];
714723
};
715724

@@ -835,6 +844,53 @@ TEST_F(StatTest, StatIgnoreNoAutomount) {
835844
SyscallSucceeds());
836845
}
837846

847+
TEST_F(StatTest, StatxMntId) {
848+
SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 &&
849+
errno == ENOSYS);
850+
851+
struct kernel_statx stx;
852+
EXPECT_THAT(statx(AT_FDCWD, test_file_name_.c_str(), 0, STATX_MNT_ID, &stx),
853+
SyscallSucceeds());
854+
EXPECT_TRUE(stx.stx_mask & STATX_MNT_ID);
855+
EXPECT_NE(stx.stx_mnt_id, 0);
856+
857+
// Check that two files in the same mount have the same mount ID.
858+
TempPath file2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
859+
struct kernel_statx stx2;
860+
EXPECT_THAT(statx(AT_FDCWD, file2.path().c_str(), 0, STATX_MNT_ID, &stx2),
861+
SyscallSucceeds());
862+
EXPECT_TRUE(stx2.stx_mask & STATX_MNT_ID);
863+
EXPECT_EQ(stx.stx_mnt_id, stx2.stx_mnt_id);
864+
}
865+
866+
TEST_F(StatTest, StatxMntIdBindMount) {
867+
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
868+
SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 &&
869+
errno == ENOSYS);
870+
871+
TempPath dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
872+
TempPath dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
873+
874+
struct kernel_statx stx1;
875+
EXPECT_THAT(statx(AT_FDCWD, dir1.path().c_str(), 0, STATX_MNT_ID, &stx1),
876+
SyscallSucceeds());
877+
878+
ASSERT_THAT(mount(dir1.path().c_str(), dir2.path().c_str(), nullptr, MS_BIND,
879+
nullptr),
880+
SyscallSucceeds());
881+
auto cleanup = Cleanup([&dir2]() {
882+
ASSERT_THAT(umount(dir2.path().c_str()), SyscallSucceeds());
883+
});
884+
885+
struct kernel_statx stx2;
886+
EXPECT_THAT(statx(AT_FDCWD, dir2.path().c_str(), 0, STATX_MNT_ID, &stx2),
887+
SyscallSucceeds());
888+
889+
EXPECT_TRUE(stx1.stx_mask & STATX_MNT_ID);
890+
EXPECT_TRUE(stx2.stx_mask & STATX_MNT_ID);
891+
EXPECT_NE(stx1.stx_mnt_id, stx2.stx_mnt_id);
892+
}
893+
838894
} // namespace
839895

840896
} // namespace testing

0 commit comments

Comments
 (0)