unix: FreeBSD 12 ino64 support

Background:
The 64-bit inode project was merged into the upcoming FreeBSD 12 release.
It changes the ABI for structs holding inodes: stat, statfs, dirent.
New system call numbers were introduced which accept the new struct layouts,
the old ones were marked as COMPAT11.
Their equivalent libc wrappers are using ELF symbol versioning.
The new wrappers have moved from @FBSD_1.0 to @FBSD_1.5.

Backward and forward compatability is achieved by always using the new struct
layouts while converting the old struct instance to the new layout on old kernels.
https://svnweb.freebsd.org/base?view=revision&revision=318736
https://svnweb.freebsd.org/base?view=revision&revision=320278

The same approach is used for Go:
The new Stat_t, Statfs_t and Dirent types hold 64-bit inodes and additional ABI
changes, they are generated from their C definitions in FreeBSD-12 using cgo -godefs.
Each type has an unexported *_freebsd11 counterpart generated the same way.
Previous directly exposed syscalls like Fstat have now a wrapper in place calling
either fstat or fstat_freebsd12 zsyscall wrapper based on the kern.osreldate.
If an old syscall needs to be used, then the returned *_freebsd11 result is converted
to the new layout before returning from the wrapper.

Introduce supportsABI() call to check the kern.osreldate sysctl for the ABI version.
Drop the old struct stat8 definition in favour of the <sys/stat.h> version.
Run the mktypes part of GOOS=freebsd GOARCH={386,amd64,arm} ./mkall.sh
on FreeBSD-12.0-ALPHA6 (r338675), updating all types except Kevent.

Expose Mknodat, both COMPAT11 version (currently missing) and the FreeBSD 12 one.
Some COMPAT11 syscalls have no direct FreeBSD 12 counterpart, in those cases
an *at(AT_FDCWD, ...) is used instead.

Updates golang/go#22448

Change-Id: I87940b88ae358db88103cdcd06f9cafbf4694cfc
Reviewed-on: https://go-review.googlesource.com/c/136816
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Giovanni Bajo <rasky@develer.com>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
This commit is contained in:
Yuval Pavel Zholkover
2018-10-22 06:42:59 +00:00
committed by Tobias Klauser
parent eda9bb28ed
commit 5535b4e6bf
8 changed files with 1086 additions and 400 deletions
+296 -14
View File
@@ -13,9 +13,34 @@
package unix
import (
"sync"
"unsafe"
)
const (
SYS_FSTAT_FREEBSD12 = 551 // { int fstat(int fd, _Out_ struct stat *sb); }
SYS_FSTATAT_FREEBSD12 = 552 // { int fstatat(int fd, _In_z_ char *path, \
SYS_GETDIRENTRIES_FREEBSD12 = 554 // { ssize_t getdirentries(int fd, \
SYS_STATFS_FREEBSD12 = 555 // { int statfs(_In_z_ char *path, \
SYS_FSTATFS_FREEBSD12 = 556 // { int fstatfs(int fd, \
SYS_GETFSSTAT_FREEBSD12 = 557 // { int getfsstat( \
SYS_MKNODAT_FREEBSD12 = 559 // { int mknodat(int fd, _In_z_ char *path, \
)
// See https://www.freebsd.org/doc/en_US.ISO8859-1/books/porters-handbook/versions.html.
var (
osreldateOnce sync.Once
osreldate uint32
)
// INO64_FIRST from /usr/src/lib/libc/sys/compat-ino64.h
const _ino64First = 1200031
func supportsABI(ver uint32) bool {
osreldateOnce.Do(func() { osreldate, _ = SysctlUint32("kern.osreldate") })
return osreldate >= ver
}
// SockaddrDatalink implements the Sockaddr interface for AF_LINK type sockets.
type SockaddrDatalink struct {
Len uint8
@@ -121,17 +146,39 @@ func Getwd() (string, error) {
}
func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
var _p0 unsafe.Pointer
var bufsize uintptr
var (
_p0 unsafe.Pointer
bufsize uintptr
oldBuf []statfs_freebsd11_t
needsConvert bool
)
if len(buf) > 0 {
_p0 = unsafe.Pointer(&buf[0])
bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf))
if supportsABI(_ino64First) {
_p0 = unsafe.Pointer(&buf[0])
bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf))
} else {
n := len(buf)
oldBuf = make([]statfs_freebsd11_t, n)
_p0 = unsafe.Pointer(&oldBuf[0])
bufsize = unsafe.Sizeof(statfs_freebsd11_t{}) * uintptr(n)
needsConvert = true
}
}
r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags))
var sysno uintptr = SYS_GETFSSTAT
if supportsABI(_ino64First) {
sysno = SYS_GETFSSTAT_FREEBSD12
}
r0, _, e1 := Syscall(sysno, uintptr(_p0), bufsize, uintptr(flags))
n = int(r0)
if e1 != 0 {
err = e1
}
if e1 == 0 && needsConvert {
for i := range oldBuf {
buf[i].convertFrom(&oldBuf[i])
}
}
return
}
@@ -225,6 +272,234 @@ func Uname(uname *Utsname) error {
return nil
}
func Stat(path string, st *Stat_t) (err error) {
var oldStat stat_freebsd11_t
if supportsABI(_ino64First) {
return fstatat_freebsd12(AT_FDCWD, path, st, 0)
}
err = stat(path, &oldStat)
if err != nil {
return err
}
st.convertFrom(&oldStat)
return nil
}
func Lstat(path string, st *Stat_t) (err error) {
var oldStat stat_freebsd11_t
if supportsABI(_ino64First) {
return fstatat_freebsd12(AT_FDCWD, path, st, AT_SYMLINK_NOFOLLOW)
}
err = lstat(path, &oldStat)
if err != nil {
return err
}
st.convertFrom(&oldStat)
return nil
}
func Fstat(fd int, st *Stat_t) (err error) {
var oldStat stat_freebsd11_t
if supportsABI(_ino64First) {
return fstat_freebsd12(fd, st)
}
err = fstat(fd, &oldStat)
if err != nil {
return err
}
st.convertFrom(&oldStat)
return nil
}
func Fstatat(fd int, path string, st *Stat_t, flags int) (err error) {
var oldStat stat_freebsd11_t
if supportsABI(_ino64First) {
return fstatat_freebsd12(fd, path, st, flags)
}
err = fstatat(fd, path, &oldStat, flags)
if err != nil {
return err
}
st.convertFrom(&oldStat)
return nil
}
func Statfs(path string, st *Statfs_t) (err error) {
var oldStatfs statfs_freebsd11_t
if supportsABI(_ino64First) {
return statfs_freebsd12(path, st)
}
err = statfs(path, &oldStatfs)
if err != nil {
return err
}
st.convertFrom(&oldStatfs)
return nil
}
func Fstatfs(fd int, st *Statfs_t) (err error) {
var oldStatfs statfs_freebsd11_t
if supportsABI(_ino64First) {
return fstatfs_freebsd12(fd, st)
}
err = fstatfs(fd, &oldStatfs)
if err != nil {
return err
}
st.convertFrom(&oldStatfs)
return nil
}
func Getdents(fd int, buf []byte) (n int, err error) {
return Getdirentries(fd, buf, nil)
}
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
if supportsABI(_ino64First) {
return getdirentries_freebsd12(fd, buf, basep)
}
// The old syscall entries are smaller than the new. Use 1/4 of the original
// buffer size rounded up to DIRBLKSIZ (see /usr/src/lib/libc/sys/getdirentries.c).
oldBufLen := roundup(len(buf)/4, _dirblksiz)
oldBuf := make([]byte, oldBufLen)
n, err = getdirentries(fd, oldBuf, basep)
if err == nil && n > 0 {
n = convertFromDirents11(buf, oldBuf[:n])
}
return
}
func Mknod(path string, mode uint32, dev uint64) (err error) {
var oldDev int
if supportsABI(_ino64First) {
return mknodat_freebsd12(AT_FDCWD, path, mode, dev)
}
oldDev = int(dev)
return mknod(path, mode, oldDev)
}
func Mknodat(fd int, path string, mode uint32, dev uint64) (err error) {
var oldDev int
if supportsABI(_ino64First) {
return mknodat_freebsd12(fd, path, mode, dev)
}
oldDev = int(dev)
return mknodat(fd, path, mode, oldDev)
}
// round x to the nearest multiple of y, larger or equal to x.
//
// from /usr/include/sys/param.h Macros for counting and rounding.
// #define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
func roundup(x, y int) int {
return ((x + y - 1) / y) * y
}
func (s *Stat_t) convertFrom(old *stat_freebsd11_t) {
*s = Stat_t{
Dev: uint64(old.Dev),
Ino: uint64(old.Ino),
Nlink: uint64(old.Nlink),
Mode: old.Mode,
Uid: old.Uid,
Gid: old.Gid,
Rdev: uint64(old.Rdev),
Atim: old.Atim,
Mtim: old.Mtim,
Ctim: old.Ctim,
Birthtim: old.Birthtim,
Size: old.Size,
Blocks: old.Blocks,
Blksize: old.Blksize,
Flags: old.Flags,
Gen: uint64(old.Gen),
}
}
func (s *Statfs_t) convertFrom(old *statfs_freebsd11_t) {
*s = Statfs_t{
Version: _statfsVersion,
Type: old.Type,
Flags: old.Flags,
Bsize: old.Bsize,
Iosize: old.Iosize,
Blocks: old.Blocks,
Bfree: old.Bfree,
Bavail: old.Bavail,
Files: old.Files,
Ffree: old.Ffree,
Syncwrites: old.Syncwrites,
Asyncwrites: old.Asyncwrites,
Syncreads: old.Syncreads,
Asyncreads: old.Asyncreads,
// Spare
Namemax: old.Namemax,
Owner: old.Owner,
Fsid: old.Fsid,
// Charspare
// Fstypename
// Mntfromname
// Mntonname
}
sl := old.Fstypename[:]
n := clen(*(*[]byte)(unsafe.Pointer(&sl)))
copy(s.Fstypename[:], old.Fstypename[:n])
sl = old.Mntfromname[:]
n = clen(*(*[]byte)(unsafe.Pointer(&sl)))
copy(s.Mntfromname[:], old.Mntfromname[:n])
sl = old.Mntonname[:]
n = clen(*(*[]byte)(unsafe.Pointer(&sl)))
copy(s.Mntonname[:], old.Mntonname[:n])
}
func convertFromDirents11(buf []byte, old []byte) int {
const (
fixedSize = int(unsafe.Offsetof(Dirent{}.Name))
oldFixedSize = int(unsafe.Offsetof(dirent_freebsd11{}.Name))
)
dstPos := 0
srcPos := 0
for dstPos+fixedSize < len(buf) && srcPos+oldFixedSize < len(old) {
dstDirent := (*Dirent)(unsafe.Pointer(&buf[dstPos]))
srcDirent := (*dirent_freebsd11)(unsafe.Pointer(&old[srcPos]))
reclen := roundup(fixedSize+int(srcDirent.Namlen)+1, 8)
if dstPos+reclen > len(buf) {
break
}
dstDirent.Fileno = uint64(srcDirent.Fileno)
dstDirent.Off = 0
dstDirent.Reclen = uint16(reclen)
dstDirent.Type = srcDirent.Type
dstDirent.Pad0 = 0
dstDirent.Namlen = uint16(srcDirent.Namlen)
dstDirent.Pad1 = 0
copy(dstDirent.Name[:], srcDirent.Name[:srcDirent.Namlen])
padding := buf[dstPos+fixedSize+int(dstDirent.Namlen) : dstPos+reclen]
for i := range padding {
padding[i] = 0
}
dstPos += int(dstDirent.Reclen)
srcPos += int(srcDirent.Reclen)
}
return dstPos
}
/*
* Exposed directly
*/
@@ -264,13 +539,16 @@ func Uname(uname *Utsname) error {
//sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error)
//sys Flock(fd int, how int) (err error)
//sys Fpathconf(fd int, name int) (val int, err error)
//sys Fstat(fd int, stat *Stat_t) (err error)
//sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error)
//sys Fstatfs(fd int, stat *Statfs_t) (err error)
//sys fstat(fd int, stat *stat_freebsd11_t) (err error)
//sys fstat_freebsd12(fd int, stat *Stat_t) (err error)
//sys fstatat(fd int, path string, stat *stat_freebsd11_t, flags int) (err error)
//sys fstatat_freebsd12(fd int, path string, stat *Stat_t, flags int) (err error)
//sys fstatfs(fd int, stat *statfs_freebsd11_t) (err error)
//sys fstatfs_freebsd12(fd int, stat *Statfs_t) (err error)
//sys Fsync(fd int) (err error)
//sys Ftruncate(fd int, length int64) (err error)
//sys Getdents(fd int, buf []byte) (n int, err error)
//sys Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error)
//sys getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error)
//sys getdirentries_freebsd12(fd int, buf []byte, basep *uintptr) (n int, err error)
//sys Getdtablesize() (size int)
//sysnb Getegid() (egid int)
//sysnb Geteuid() (uid int)
@@ -292,11 +570,13 @@ func Uname(uname *Utsname) error {
//sys Link(path string, link string) (err error)
//sys Linkat(pathfd int, path string, linkfd int, link string, flags int) (err error)
//sys Listen(s int, backlog int) (err error)
//sys Lstat(path string, stat *Stat_t) (err error)
//sys lstat(path string, stat *stat_freebsd11_t) (err error)
//sys Mkdir(path string, mode uint32) (err error)
//sys Mkdirat(dirfd int, path string, mode uint32) (err error)
//sys Mkfifo(path string, mode uint32) (err error)
//sys Mknod(path string, mode uint32, dev int) (err error)
//sys mknod(path string, mode uint32, dev int) (err error)
//sys mknodat(fd int, path string, mode uint32, dev int) (err error)
//sys mknodat_freebsd12(fd int, path string, mode uint32, dev uint64) (err error)
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
//sys Open(path string, mode int, perm uint32) (fd int, err error)
//sys Openat(fdat int, path string, mode int, perm uint32) (fd int, err error)
@@ -326,8 +606,9 @@ func Uname(uname *Utsname) error {
//sysnb Setsid() (pid int, err error)
//sysnb Settimeofday(tp *Timeval) (err error)
//sysnb Setuid(uid int) (err error)
//sys Stat(path string, stat *Stat_t) (err error)
//sys Statfs(path string, stat *Statfs_t) (err error)
//sys stat(path string, stat *stat_freebsd11_t) (err error)
//sys statfs(path string, stat *statfs_freebsd11_t) (err error)
//sys statfs_freebsd12(path string, stat *Statfs_t) (err error)
//sys Symlink(path string, link string) (err error)
//sys Symlinkat(oldpath string, newdirfd int, newpath string) (err error)
//sys Sync() (err error)
@@ -382,6 +663,7 @@ func Uname(uname *Utsname) error {
// Kqueue_portset
// Getattrlist
// Setattrlist
// Getdents
// Getdirentriesattr
// Searchfs
// Delete