Skip to content

Commit

Permalink
unix: add Statx on Linux
Browse files Browse the repository at this point in the history
statx(2) allows to get enhanced file status information (not currently
available through the existing stat syscalls), lightweight stat,
heavyweight stat. The Statx_t type used by Statx has consistent field
sizes on all arches (with year-2038-capable timestamps) and is closer to
the BSD implementation of Stat_t.

See http://man7.org/linux/man-pages/man2/statx.2.html for details. The
syscall was added in Linux kernel 4.11.

See https://github.com/tklauser/statx for an example of how this
function and types can be used to report stat(1)-like file status
information.

Change-Id: I9e9642b5b42a393f94fd532453888ce9ba4f0003
Reviewed-on: https://go-review.googlesource.com/87555
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
  • Loading branch information
tklauser committed Jan 15, 2018
1 parent 52ba35d commit fff93fa
Show file tree
Hide file tree
Showing 38 changed files with 952 additions and 37 deletions.
34 changes: 30 additions & 4 deletions unix/linux/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,12 @@ package unix
#include <sys/utsname.h>
#include <sys/wait.h>
#include <linux/filter.h>
#include <linux/icmpv6.h>
#include <linux/keyctl.h>
#include <linux/netlink.h>
#include <linux/perf_event.h>
#include <linux/rtnetlink.h>
#include <linux/icmpv6.h>
#include <linux/stat.h>
#include <asm/termbits.h>
#include <asm/ptrace.h>
#include <time.h>
Expand Down Expand Up @@ -116,6 +117,21 @@ struct stat {
#endif
// These are defined in linux/fcntl.h, but including it globally causes
// conflicts with fcntl.h
#ifndef AT_STATX_SYNC_TYPE
# define AT_STATX_SYNC_TYPE 0x6000 // Type of synchronisation required from statx()
#endif
#ifndef AT_STATX_SYNC_AS_STAT
# define AT_STATX_SYNC_AS_STAT 0x0000 // - Do whatever stat() does
#endif
#ifndef AT_STATX_FORCE_SYNC
# define AT_STATX_FORCE_SYNC 0x2000 // - Force the attributes to be sync'd with the server
#endif
#ifndef AT_STATX_DONT_SYNC
# define AT_STATX_DONT_SYNC 0x4000 // - Don't sync attributes with the server
#endif
#ifdef TCSETS2
// On systems that have "struct termios2" use this as type Termios.
typedef struct termios2 termios_t;
Expand Down Expand Up @@ -249,6 +265,10 @@ type Stat_t C.struct_stat

type Statfs_t C.struct_statfs

type StatxTimestamp C.struct_statx_timestamp

type Statx_t C.struct_statx

type Dirent C.struct_dirent

type Fsid C.fsid_t
Expand Down Expand Up @@ -513,9 +533,15 @@ type Ustat_t C.struct_ustat
type EpollEvent C.struct_my_epoll_event

const (
AT_FDCWD = C.AT_FDCWD
AT_NO_AUTOMOUNT = C.AT_NO_AUTOMOUNT
AT_REMOVEDIR = C.AT_REMOVEDIR
AT_EMPTY_PATH = C.AT_EMPTY_PATH
AT_FDCWD = C.AT_FDCWD
AT_NO_AUTOMOUNT = C.AT_NO_AUTOMOUNT
AT_REMOVEDIR = C.AT_REMOVEDIR

AT_STATX_SYNC_AS_STAT = C.AT_STATX_SYNC_AS_STAT
AT_STATX_FORCE_SYNC = C.AT_STATX_FORCE_SYNC
AT_STATX_DONT_SYNC = C.AT_STATX_DONT_SYNC

AT_SYMLINK_FOLLOW = C.AT_SYMLINK_FOLLOW
AT_SYMLINK_NOFOLLOW = C.AT_SYMLINK_NOFOLLOW
)
Expand Down
2 changes: 2 additions & 0 deletions unix/mkerrors.sh
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ struct ltchars {
#include <linux/vm_sockets.h>
#include <linux/taskstats.h>
#include <linux/genetlink.h>
#include <linux/stat.h>
#include <linux/watchdog.h>
#include <net/route.h>
#include <asm/termbits.h>
Expand Down Expand Up @@ -428,6 +429,7 @@ ccflags="$@"
$2 ~ /^(TASKSTATS|TS)_/ ||
$2 ~ /^CGROUPSTATS_/ ||
$2 ~ /^GENL_/ ||
$2 ~ /^STATX_/ ||
$2 ~ /^UTIME_/ ||
$2 ~ /^XATTR_(CREATE|REPLACE)/ ||
$2 ~ /^ATTR_(BIT_MAP_COUNT|(CMN|VOL|FILE)_)/ ||
Expand Down
4 changes: 4 additions & 0 deletions unix/mkpost.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ func main() {
convertUtsnameRegex := regexp.MustCompile(`((Sys|Node|Domain)name|Release|Version|Machine)(\s+)\[(\d+)\]u?int8`)
b = convertUtsnameRegex.ReplaceAll(b, []byte("$1$3[$4]byte"))

// Remove spare fields (e.g. in Statx_t)
spareFieldsRegex := regexp.MustCompile(`X__spare\S*`)
b = spareFieldsRegex.ReplaceAll(b, []byte("_"))

// We refuse to export private fields on s390x
if goarch == "s390x" && goos == "linux" {
// Remove cgo padding fields
Expand Down
1 change: 1 addition & 0 deletions unix/syscall_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -1318,6 +1318,7 @@ func Setgid(uid int) (err error) {

//sys Setpriority(which int, who int, prio int) (err error)
//sys Setxattr(path string, attr string, data []byte, flags int) (err error)
//sys Statx(dirfd int, path string, flags int, mask int, stat *Statx_t) (err error)
//sys Sync()
//sys Syncfs(fd int) (err error)
//sysnb Sysinfo(info *Sysinfo_t) (err error)
Expand Down
90 changes: 90 additions & 0 deletions unix/syscall_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,96 @@ func TestSchedSetaffinity(t *testing.T) {
}
}

func TestStatx(t *testing.T) {
var stx unix.Statx_t
err := unix.Statx(unix.AT_FDCWD, ".", 0, 0, &stx)
if err == unix.ENOSYS {
t.Skip("statx syscall is not available, skipping test")
} else if err != nil {
t.Fatalf("Statx: %v", err)
}

defer chtmpdir(t)()
touch(t, "file1")

var st unix.Stat_t
err = unix.Stat("file1", &st)
if err != nil {
t.Fatalf("Stat: %v", err)
}

flags := unix.AT_STATX_SYNC_AS_STAT
err = unix.Statx(unix.AT_FDCWD, "file1", flags, unix.STATX_ALL, &stx)
if err != nil {
t.Fatalf("Statx: %v", err)
}

if uint32(stx.Mode) != st.Mode {
t.Errorf("Statx: returned stat mode does not match Stat")
}

atime := unix.StatxTimestamp{Sec: int64(st.Atim.Sec), Nsec: uint32(st.Atim.Nsec)}
ctime := unix.StatxTimestamp{Sec: int64(st.Ctim.Sec), Nsec: uint32(st.Ctim.Nsec)}
mtime := unix.StatxTimestamp{Sec: int64(st.Mtim.Sec), Nsec: uint32(st.Mtim.Nsec)}

if stx.Atime != atime {
t.Errorf("Statx: returned stat atime does not match Stat")
}
if stx.Ctime != ctime {
t.Errorf("Statx: returned stat ctime does not match Stat")
}
if stx.Mtime != mtime {
t.Errorf("Statx: returned stat mtime does not match Stat")
}

err = os.Symlink("file1", "symlink1")
if err != nil {
t.Fatal(err)
}

err = unix.Lstat("symlink1", &st)
if err != nil {
t.Fatalf("Lstat: %v", err)
}

err = unix.Statx(unix.AT_FDCWD, "symlink1", flags, unix.STATX_BASIC_STATS, &stx)
if err != nil {
t.Fatalf("Statx: %v", err)
}

// follow symlink, expect a regulat file
if stx.Mode&unix.S_IFREG == 0 {
t.Errorf("Statx: didn't follow symlink")
}

err = unix.Statx(unix.AT_FDCWD, "symlink1", flags|unix.AT_SYMLINK_NOFOLLOW, unix.STATX_ALL, &stx)
if err != nil {
t.Fatalf("Statx: %v", err)
}

// follow symlink, expect a symlink
if stx.Mode&unix.S_IFLNK == 0 {
t.Errorf("Statx: unexpectedly followed symlink")
}
if uint32(stx.Mode) != st.Mode {
t.Errorf("Statx: returned stat mode does not match Lstat")
}

atime = unix.StatxTimestamp{Sec: int64(st.Atim.Sec), Nsec: uint32(st.Atim.Nsec)}
ctime = unix.StatxTimestamp{Sec: int64(st.Ctim.Sec), Nsec: uint32(st.Ctim.Nsec)}
mtime = unix.StatxTimestamp{Sec: int64(st.Mtim.Sec), Nsec: uint32(st.Mtim.Nsec)}

if stx.Atime != atime {
t.Errorf("Statx: returned stat atime does not match Lstat")
}
if stx.Ctime != ctime {
t.Errorf("Statx: returned stat ctime does not match Lstat")
}
if stx.Mtime != mtime {
t.Errorf("Statx: returned stat mtime does not match Lstat")
}
}

// utilities taken from os/os_test.go

func touch(t *testing.T, name string) {
Expand Down
21 changes: 21 additions & 0 deletions unix/zerrors_linux_386.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions unix/zerrors_linux_amd64.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions unix/zerrors_linux_arm.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions unix/zerrors_linux_arm64.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions unix/zerrors_linux_mips.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions unix/zerrors_linux_mips64.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions unix/zerrors_linux_mips64le.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit fff93fa

Please sign in to comment.