gvisor-lx/pkg/abi/linux/file.go
Leadaxe 117243aa02 snapshot: sagernet/gvisor v0.0.0-20260727.0-sing-box-mod.1 + SPEC 048 guard
Обновление снапшота с v0.0.0-20250811.0 на пин, которого требует
sing-box после мержа 235 коммитов (upstream d620bbbf2 "Update gvisor to
20260727.0"). Прежний снапшот был взят 2026-08-04 ровно с той версии,
на которой тогда стоял апстрим; разрыв возник 2026-08-05 вместе с его
бампом.

За год апстрим-gvisor изменил ~14 000 строк в 292 файлах. Значимое для
нас — сетевой стек: tcp/connect.go (PMTU-discovery + исправление
начального RTT/RTO: раньше задержка ACK внутри стека завышала стартовый
таймаут на несколько RTT), tcp/snd.go, tcp/rcv.go, stack/conntrack.go,
stack/packet_buffer.go. Всего 30 файлов в TCP и 37 в stack.

Баг SPEC 048 апстрим НЕ исправил — проверено по коду новой версии:
handleConnecting по-прежнему проверяет состояние endpoint'а, но не ep.h,
а performHandshake так же зануляет h и отпускает мьютекс до Close().
Поэтому guard перенесён (12 строк) вместе со своим тестом (45 строк).

Red/green проверен на новой базе: без guard'а тест падает с той же
nil-паникой, что в полевом крашдампе; с ним зелёный.
2026-08-05 14:53:31 +03:00

526 lines
13 KiB
Go

// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package linux
import (
"fmt"
"strings"
"structs"
"github.com/sagernet/gvisor/pkg/abi"
)
// Constants for open(2).
const (
O_ACCMODE = 0o00000003
O_RDONLY = 0o00000000
O_WRONLY = 0o00000001
O_RDWR = 0o00000002
O_CREAT = 0o00000100
O_EXCL = 0o00000200
O_NOCTTY = 0o00000400
O_TRUNC = 0o00001000
O_APPEND = 0o00002000
O_NONBLOCK = 0o00004000
O_DSYNC = 0o00010000
O_ASYNC = 0o00020000
O_NOATIME = 0o01000000
O_CLOEXEC = 0o02000000
O_SYNC = 0o04000000 // __O_SYNC in Linux
O_PATH = 0o10000000
O_TMPFILE = 0o20000000 // __O_TMPFILE in Linux
)
// Constants for file mode (struct file::f_mode in Linux).
const (
// FMODE_READ indicates the file is open for reading.
// It is set when MayReadFileWithOpenFlags(flags) is true.
FMODE_READ = 0x1
// FMODE_WRITE indicates the file is open for writing.
// It is set when MayWriteFileWithOpenFlags(flags) is true. When set,
// the FileDescription holds a write count on vd.mount.
FMODE_WRITE = 0x2
// FMODE_CREATED is set when a file is newly created by an open
// operation (O_CREAT and the file did not already exist).
FMODE_CREATED = 0x100000
)
// Constants for fstatat(2).
const (
AT_SYMLINK_NOFOLLOW = 0x100
)
// Constants for mount(2).
const (
MS_RDONLY = 0x1
MS_NOSUID = 0x2
MS_NODEV = 0x4
MS_NOEXEC = 0x8
MS_SYNCHRONOUS = 0x10
MS_REMOUNT = 0x20
MS_MANDLOCK = 0x40
MS_DIRSYNC = 0x80
MS_NOATIME = 0x400
MS_NODIRATIME = 0x800
MS_BIND = 0x1000
MS_MOVE = 0x2000
MS_REC = 0x4000
MS_POSIXACL = 0x10000
MS_UNBINDABLE = 0x20000
MS_PRIVATE = 0x40000
MS_SLAVE = 0x80000
MS_SHARED = 0x100000
MS_RELATIME = 0x200000
MS_KERNMOUNT = 0x400000
MS_I_VERSION = 0x800000
MS_STRICTATIME = 0x1000000
MS_MGC_VAL = 0xC0ED0000
MS_MGC_MSK = 0xffff0000
)
// Constants for umount2(2).
const (
MNT_FORCE = 0x1
MNT_DETACH = 0x2
MNT_EXPIRE = 0x4
UMOUNT_NOFOLLOW = 0x8
)
// Constants for fsopen(2).
const (
FSOPEN_CLOEXEC = 0x1
)
// Constants for fsconfig(2).
const (
FSCONFIG_SET_FLAG = 0x0
FSCONFIG_SET_STRING = 0x1
FSCONFIG_SET_BINARY = 0x2
FSCONFIG_SET_PATH = 0x3
FSCONFIG_SET_PATH_EMPTY = 0x4
FSCONFIG_SET_FD = 0x5
FSCONFIG_CMD_CREATE = 0x6
FSCONFIG_CMD_RECONFIGURE = 0x7
FSCONFIG_CMD_CREATE_EXCL = 0x8
)
// Constants for fsmount(2).
const (
FSMOUNT_CLOEXEC = 0x1
)
// Constants for move_mount(2).
const (
MOVE_MOUNT_F_SYMLINKS = 0x00000001
MOVE_MOUNT_F_AUTOMOUNTS = 0x00000002
MOVE_MOUNT_F_EMPTY_PATH = 0x00000004
MOVE_MOUNT_T_SYMLINKS = 0x00000010
MOVE_MOUNT_T_AUTOMOUNTS = 0x00000020
MOVE_MOUNT_T_EMPTY_PATH = 0x00000040
MOVE_MOUNT_SET_GROUP = 0x00000100
MOVE_MOUNT_BENEATH = 0x00000200
)
// Constants for mount_setattr(2).
const (
MOUNT_ATTR_RDONLY = 0x00000001
MOUNT_ATTR_NOSUID = 0x00000002
MOUNT_ATTR_NODEV = 0x00000004
MOUNT_ATTR_NOEXEC = 0x00000008
MOUNT_ATTR__ATIME = 0x00000070
MOUNT_ATTR_RELATIME = 0x00000000
MOUNT_ATTR_NOATIME = 0x00000010
MOUNT_ATTR_STRICTATIME = 0x00000020
MOUNT_ATTR_NODIRATIME = 0x00000080
MOUNT_ATTR_IDMAP = 0x00100000
MOUNT_ATTR_NOSYMFOLLOW = 0x00200000
AT_RECURSIVE = 0x8000
)
// Constants for open_tree(2).
const (
OPEN_TREE_CLONE = (1 << 0)
OPEN_TREE_NAMESPACE = (1 << 1)
OPEN_TREE_CLOEXEC = O_CLOEXEC
)
// Constants for unlinkat(2).
const (
AT_REMOVEDIR = 0x200
)
// Constants for linkat(2) and fchownat(2).
const (
AT_SYMLINK_FOLLOW = 0x400
AT_EMPTY_PATH = 0x1000
)
// Constants for faccessat2(2).
const (
AT_EACCESS = 0x200
)
// Constants for all file-related ...at(2) syscalls.
const (
AT_FDCWD = -100
)
// Special values for the ns field in utimensat(2).
const (
UTIME_NOW = ((1 << 30) - 1)
UTIME_OMIT = ((1 << 30) - 2)
)
// MaxSymlinkTraversals is the maximum number of links that will be followed by
// the kernel to resolve a symlink.
const MaxSymlinkTraversals = 40
// Constants for flock(2).
const (
LOCK_SH = 1 // shared lock
LOCK_EX = 2 // exclusive lock
LOCK_NB = 4 // or'd with one of the above to prevent blocking
LOCK_UN = 8 // remove lock
)
// Values for mode_t.
const (
S_IFMT = 0o170000
S_IFSOCK = 0o140000
S_IFLNK = 0o120000
S_IFREG = 0o100000
S_IFBLK = 0o60000
S_IFDIR = 0o40000
S_IFCHR = 0o20000
S_IFIFO = 0o10000
FileTypeMask = S_IFMT
ModeSocket = S_IFSOCK
ModeSymlink = S_IFLNK
ModeRegular = S_IFREG
ModeBlockDevice = S_IFBLK
ModeDirectory = S_IFDIR
ModeCharacterDevice = S_IFCHR
ModeNamedPipe = S_IFIFO
S_ISUID = 0o4000
S_ISGID = 0o2000
S_ISVTX = 0o1000
ModeSetUID = S_ISUID
ModeSetGID = S_ISGID
ModeSticky = S_ISVTX
ModeUserAll = 0o700
ModeUserRead = 0o400
ModeUserWrite = 0o200
ModeUserExec = 0o100
ModeGroupAll = 0o070
ModeGroupRead = 0o040
ModeGroupWrite = 0o020
ModeGroupExec = 0o010
ModeOtherAll = 0o007
ModeOtherRead = 0o004
ModeOtherWrite = 0o002
ModeOtherExec = 0o001
PermissionsMask = 0o777
)
// Values for linux_dirent64.d_type.
const (
DT_UNKNOWN = 0
DT_FIFO = 1
DT_CHR = 2
DT_DIR = 4
DT_BLK = 6
DT_REG = 8
DT_LNK = 10
DT_SOCK = 12
DT_WHT = 14
)
// DirentType are the friendly strings for linux_dirent64.d_type.
var DirentType = abi.ValueSet{
DT_UNKNOWN: "DT_UNKNOWN",
DT_FIFO: "DT_FIFO",
DT_CHR: "DT_CHR",
DT_DIR: "DT_DIR",
DT_BLK: "DT_BLK",
DT_REG: "DT_REG",
DT_LNK: "DT_LNK",
DT_SOCK: "DT_SOCK",
DT_WHT: "DT_WHT",
}
// Values for fs on-disk file types.
const (
FT_UNKNOWN = 0
FT_REG_FILE = 1
FT_DIR = 2
FT_CHRDEV = 3
FT_BLKDEV = 4
FT_FIFO = 5
FT_SOCK = 6
FT_SYMLINK = 7
FT_MAX = 8
)
// Conversion from fs on-disk file type to dirent type.
var direntTypeByFileType = [FT_MAX]uint8{
FT_UNKNOWN: DT_UNKNOWN,
FT_REG_FILE: DT_REG,
FT_DIR: DT_DIR,
FT_CHRDEV: DT_CHR,
FT_BLKDEV: DT_BLK,
FT_FIFO: DT_FIFO,
FT_SOCK: DT_SOCK,
FT_SYMLINK: DT_LNK,
}
// FileTypeToDirentType converts the on-disk file type (FT_*) to the directory
// entry type (DT_*).
func FileTypeToDirentType(filetype uint8) uint8 {
if filetype >= FT_MAX {
return DT_UNKNOWN
}
return direntTypeByFileType[filetype]
}
// Values for preadv2/pwritev2.
const (
// NOTE(b/120162627): gVisor does not implement the RWF_HIPRI feature, but
// the flag is accepted as a valid flag argument for preadv2/pwritev2 and
// silently ignored.
RWF_HIPRI = 0x00000001
RWF_DSYNC = 0x00000002
RWF_SYNC = 0x00000004
RWF_VALID = RWF_HIPRI | RWF_DSYNC | RWF_SYNC
)
// SizeOfStat is the size of a Stat struct.
var SizeOfStat = (*Stat)(nil).SizeBytes()
// Flags for statx.
const (
AT_NO_AUTOMOUNT = 0x800
AT_STATX_SYNC_TYPE = 0x6000
AT_STATX_SYNC_AS_STAT = 0x0000
AT_STATX_FORCE_SYNC = 0x2000
AT_STATX_DONT_SYNC = 0x4000
)
// Mask values for statx.
const (
STATX_TYPE = 0x00000001
STATX_MODE = 0x00000002
STATX_NLINK = 0x00000004
STATX_UID = 0x00000008
STATX_GID = 0x00000010
STATX_ATIME = 0x00000020
STATX_MTIME = 0x00000040
STATX_CTIME = 0x00000080
STATX_INO = 0x00000100
STATX_SIZE = 0x00000200
STATX_BLOCKS = 0x00000400
STATX_BASIC_STATS = 0x000007ff
STATX_BTIME = 0x00000800
STATX_MNT_ID = 0x00001000
STATX_ALL = 0x00000fff
STATX__RESERVED = 0x80000000
)
// Bitmasks for Statx.Attributes and Statx.AttributesMask, from
// include/uapi/linux/stat.h.
const (
STATX_ATTR_COMPRESSED = 0x00000004
STATX_ATTR_IMMUTABLE = 0x00000010
STATX_ATTR_APPEND = 0x00000020
STATX_ATTR_NODUMP = 0x00000040
STATX_ATTR_ENCRYPTED = 0x00000800
STATX_ATTR_AUTOMOUNT = 0x00001000
)
// Statx represents struct statx.
//
// +marshal boundCheck slice:StatxSlice
type Statx struct {
_ structs.HostLayout
Mask uint32
Blksize uint32
Attributes uint64
Nlink uint32
UID uint32
GID uint32
Mode uint16
_ uint16
Ino uint64
Size uint64
Blocks uint64
AttributesMask uint64
Atime StatxTimestamp
Btime StatxTimestamp
Ctime StatxTimestamp
Mtime StatxTimestamp
RdevMajor uint32
RdevMinor uint32
DevMajor uint32
DevMinor uint32
MntID uint64
}
// String implements fmt.Stringer.String.
func (s *Statx) String() string {
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}",
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)
}
// SizeOfStatx is the size of a Statx struct.
var SizeOfStatx = (*Statx)(nil).SizeBytes()
// FileMode represents a mode_t.
//
// +marshal
type FileMode uint16
// Permissions returns just the permission bits.
func (m FileMode) Permissions() FileMode {
return m & PermissionsMask
}
// FileType returns just the file type bits.
func (m FileMode) FileType() FileMode {
return m & FileTypeMask
}
// ExtraBits returns everything but the file type and permission bits.
func (m FileMode) ExtraBits() FileMode {
return m &^ (PermissionsMask | FileTypeMask)
}
// IsDir returns true if file type represents a directory.
func (m FileMode) IsDir() bool {
return m.FileType() == S_IFDIR
}
// IsSpecialFile returns true if m is the mode of a "special file": a character
// or block device, FIFO, or socket.
//
// Analogous to include/linux/fs.h:special_file().
func (m FileMode) IsSpecialFile() bool {
switch m.FileType() {
case ModeCharacterDevice, ModeBlockDevice, ModeNamedPipe, ModeSocket:
return true
default:
return false
}
}
// String returns a string representation of m.
func (m FileMode) String() string {
var s []string
if ft := m.FileType(); ft != 0 {
s = append(s, fileType.Parse(uint64(ft)))
}
if eb := m.ExtraBits(); eb != 0 {
s = append(s, modeExtraBits.Parse(uint64(eb)))
}
s = append(s, fmt.Sprintf("0o%o", m.Permissions()))
return strings.Join(s, "|")
}
// DirentType maps file types to dirent types appropriate for (struct
// dirent)::d_type.
func (m FileMode) DirentType() uint8 {
switch m.FileType() {
case ModeSocket:
return DT_SOCK
case ModeSymlink:
return DT_LNK
case ModeRegular:
return DT_REG
case ModeBlockDevice:
return DT_BLK
case ModeDirectory:
return DT_DIR
case ModeCharacterDevice:
return DT_CHR
case ModeNamedPipe:
return DT_FIFO
default:
return DT_UNKNOWN
}
}
var modeExtraBits = abi.FlagSet{
{
Flag: ModeSetUID,
Name: "S_ISUID",
},
{
Flag: ModeSetGID,
Name: "S_ISGID",
},
{
Flag: ModeSticky,
Name: "S_ISVTX",
},
}
var fileType = abi.ValueSet{
ModeSocket: "S_IFSOCK",
ModeSymlink: "S_IFLINK",
ModeRegular: "S_IFREG",
ModeBlockDevice: "S_IFBLK",
ModeDirectory: "S_IFDIR",
ModeCharacterDevice: "S_IFCHR",
ModeNamedPipe: "S_IFIFO",
}
// Constants for memfd_create(2). Source: include/uapi/linux/memfd.h
const (
MFD_CLOEXEC = 0x0001
MFD_ALLOW_SEALING = 0x0002
)
// Constants related to file seals. Source: include/uapi/{asm-generic,linux}/fcntl.h
const (
F_LINUX_SPECIFIC_BASE = 1024
F_ADD_SEALS = F_LINUX_SPECIFIC_BASE + 9
F_GET_SEALS = F_LINUX_SPECIFIC_BASE + 10
F_SEAL_SEAL = 0x0001 // Prevent further seals from being set.
F_SEAL_SHRINK = 0x0002 // Prevent file from shrinking.
F_SEAL_GROW = 0x0004 // Prevent file from growing.
F_SEAL_WRITE = 0x0008 // Prevent writes.
)
// Constants related to fallocate(2). Source: include/uapi/linux/falloc.h
const (
FALLOC_FL_KEEP_SIZE = 0x01
FALLOC_FL_PUNCH_HOLE = 0x02
FALLOC_FL_NO_HIDE_STALE = 0x04
FALLOC_FL_COLLAPSE_RANGE = 0x08
FALLOC_FL_ZERO_RANGE = 0x10
FALLOC_FL_INSERT_RANGE = 0x20
FALLOC_FL_UNSHARE_RANGE = 0x40
)
// Constants related to close_range(2). Source: /include/uapi/linux/close_range.h
const (
CLOSE_RANGE_UNSHARE = uint32(1 << 1)
CLOSE_RANGE_CLOEXEC = uint32(1 << 2)
)