forked from rosa/hakurei
This only implements what is required by package container for now. Signed-off-by: Ophestra <cat@gensokyo.uk>
268 lines
6.3 KiB
Go
268 lines
6.3 KiB
Go
package ext
|
|
|
|
import (
|
|
"os"
|
|
"runtime"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
// include/uapi/linux/mount.h
|
|
|
|
/*
|
|
* move_mount() flags.
|
|
*/
|
|
const (
|
|
MOVE_MOUNT_F_SYMLINKS = 1 << iota /* Follow symlinks on from path */
|
|
MOVE_MOUNT_F_AUTOMOUNTS /* Follow automounts on from path */
|
|
MOVE_MOUNT_F_EMPTY_PATH /* Empty from path permitted */
|
|
_
|
|
MOVE_MOUNT_T_SYMLINKS /* Follow symlinks on to path */
|
|
MOVE_MOUNT_T_AUTOMOUNTS /* Follow automounts on to path */
|
|
MOVE_MOUNT_T_EMPTY_PATH /* Empty to path permitted */
|
|
_
|
|
MOVE_MOUNT_SET_GROUP /* Set sharing group instead */
|
|
MOVE_MOUNT_BENEATH /* Mount beneath top mount */
|
|
)
|
|
|
|
/*
|
|
* fsopen() flags.
|
|
*/
|
|
const (
|
|
FSOPEN_CLOEXEC = 1 << iota
|
|
)
|
|
|
|
/*
|
|
* fspick() flags.
|
|
*/
|
|
const (
|
|
FSPICK_CLOEXEC = 1 << iota
|
|
FSPICK_SYMLINK_NOFOLLOW
|
|
FSPICK_NO_AUTOMOUNT
|
|
FSPICK_EMPTY_PATH
|
|
)
|
|
|
|
/*
|
|
* The type of fsconfig() call made.
|
|
*/
|
|
const (
|
|
FSCONFIG_SET_FLAG = iota /* Set parameter, supplying no value */
|
|
FSCONFIG_SET_STRING /* Set parameter, supplying a string value */
|
|
FSCONFIG_SET_BINARY /* Set parameter, supplying a binary blob value */
|
|
FSCONFIG_SET_PATH /* Set parameter, supplying an object by path */
|
|
FSCONFIG_SET_PATH_EMPTY /* Set parameter, supplying an object by (empty) path */
|
|
FSCONFIG_SET_FD /* Set parameter, supplying an object by fd */
|
|
FSCONFIG_CMD_CREATE /* Create new or reuse existing superblock */
|
|
FSCONFIG_CMD_RECONFIGURE /* Invoke superblock reconfiguration */
|
|
FSCONFIG_CMD_CREATE_EXCL /* Create new superblock, fail if reusing existing superblock */
|
|
)
|
|
|
|
/*
|
|
* fsmount() flags.
|
|
*/
|
|
const (
|
|
FSMOUNT_CLOEXEC = 1 << iota
|
|
)
|
|
|
|
/*
|
|
* Mount attributes.
|
|
*/
|
|
const (
|
|
MOUNT_ATTR_RDONLY = 0x00000001 /* Mount read-only */
|
|
MOUNT_ATTR_NOSUID = 0x00000002 /* Ignore suid and sgid bits */
|
|
MOUNT_ATTR_NODEV = 0x00000004 /* Disallow access to device special files */
|
|
MOUNT_ATTR_NOEXEC = 0x00000008 /* Disallow program execution */
|
|
MOUNT_ATTR__ATIME = 0x00000070 /* Setting on how atime should be updated */
|
|
MOUNT_ATTR_RELATIME = 0x00000000 /* - Update atime relative to mtime/ctime. */
|
|
MOUNT_ATTR_NOATIME = 0x00000010 /* - Do not update access times. */
|
|
MOUNT_ATTR_STRICTATIME = 0x00000020 /* - Always perform atime updates */
|
|
MOUNT_ATTR_NODIRATIME = 0x00000080 /* Do not update directory access times */
|
|
MOUNT_ATTR_IDMAP = 0x00100000 /* Idmap mount to @userns_fd in struct mount_attr. */
|
|
MOUNT_ATTR_NOSYMFOLLOW = 0x00200000 /* Do not follow symlinks */
|
|
)
|
|
|
|
// FS provides low-level wrappers around the suite of file-descriptor-based
|
|
// mount facilities in Linux.
|
|
type FS struct {
|
|
fd uintptr
|
|
c runtime.Cleanup
|
|
}
|
|
|
|
// newFS allocates a new [FS] for the specified fd.
|
|
func newFS(fd uintptr) *FS {
|
|
fs := FS{fd: fd}
|
|
fs.c = runtime.AddCleanup(&fs, func(fd uintptr) {
|
|
_ = syscall.Close(int(fd))
|
|
}, fd)
|
|
return &fs
|
|
}
|
|
|
|
// Close closes the underlying filesystem context.
|
|
func (fs *FS) Close() error {
|
|
if fs == nil {
|
|
return syscall.EINVAL
|
|
}
|
|
err := syscall.Close(int(fs.fd))
|
|
fs.c.Stop()
|
|
return err
|
|
}
|
|
|
|
// OpenFS creates a new filesystem context.
|
|
func OpenFS(fsname string, flags int) (fs *FS, err error) {
|
|
var s *byte
|
|
s, err = syscall.BytePtrFromString(fsname)
|
|
if err != nil {
|
|
return
|
|
}
|
|
fd, _, errno := syscall.Syscall(
|
|
SYS_FSOPEN,
|
|
uintptr(unsafe.Pointer(s)),
|
|
uintptr(flags|FSOPEN_CLOEXEC),
|
|
0,
|
|
)
|
|
if errno != 0 {
|
|
err = os.NewSyscallError("fsopen", errno)
|
|
} else {
|
|
fs = newFS(fd)
|
|
}
|
|
return
|
|
}
|
|
|
|
// PickFS selects filesystem for reconfiguration.
|
|
func PickFS(dirfd int, pathname string, flags int) (fs *FS, err error) {
|
|
var s *byte
|
|
s, err = syscall.BytePtrFromString(pathname)
|
|
if err != nil {
|
|
return
|
|
}
|
|
fd, _, errno := syscall.Syscall(
|
|
SYS_FSPICK,
|
|
uintptr(dirfd),
|
|
uintptr(unsafe.Pointer(s)),
|
|
uintptr(flags|FSPICK_CLOEXEC),
|
|
)
|
|
if errno != 0 {
|
|
err = os.NewSyscallError("fspick", errno)
|
|
} else {
|
|
fs = newFS(fd)
|
|
}
|
|
return
|
|
}
|
|
|
|
// config configures new or existing filesystem context.
|
|
func (fs *FS) config(cmd uint, key *byte, value unsafe.Pointer, aux int) (err error) {
|
|
_, _, errno := syscall.Syscall6(
|
|
SYS_FSCONFIG,
|
|
fs.fd,
|
|
uintptr(cmd),
|
|
uintptr(unsafe.Pointer(key)),
|
|
uintptr(value),
|
|
uintptr(aux),
|
|
0,
|
|
)
|
|
if errno != 0 {
|
|
err = os.NewSyscallError("fsconfig", errno)
|
|
}
|
|
return
|
|
}
|
|
|
|
// SetFlag sets the flag parameter named by key. ([FSCONFIG_SET_FLAG])
|
|
func (fs *FS) SetFlag(key string) (err error) {
|
|
var s *byte
|
|
s, err = syscall.BytePtrFromString(key)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return fs.config(FSCONFIG_SET_FLAG, s, nil, 0)
|
|
}
|
|
|
|
// SetString sets the string parameter named by key to the value specified by
|
|
// value. ([FSCONFIG_SET_STRING])
|
|
func (fs *FS) SetString(key, value string) (err error) {
|
|
var s0 *byte
|
|
s0, err = syscall.BytePtrFromString(key)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var s1 *byte
|
|
s1, err = syscall.BytePtrFromString(value)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return fs.config(FSCONFIG_SET_STRING, s0, unsafe.Pointer(s1), 0)
|
|
}
|
|
|
|
// mount instantiates mount object from filesystem context.
|
|
func (fs *FS) mount(flags, attrFlags int) (fsfd int, err error) {
|
|
r, _, errno := syscall.Syscall(
|
|
SYS_FSMOUNT,
|
|
fs.fd,
|
|
uintptr(flags|FSMOUNT_CLOEXEC),
|
|
uintptr(attrFlags),
|
|
)
|
|
fsfd = int(r)
|
|
if errno != 0 {
|
|
err = os.NewSyscallError("fsmount", errno)
|
|
}
|
|
return
|
|
}
|
|
|
|
// MoveMount moves or attaches mount object to filesystem.
|
|
func MoveMount(
|
|
fromDirfd int,
|
|
fromPathname string,
|
|
toDirfd int,
|
|
toPathname string,
|
|
flags int,
|
|
) (err error) {
|
|
var s0 *byte
|
|
s0, err = syscall.BytePtrFromString(fromPathname)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var s1 *byte
|
|
s1, err = syscall.BytePtrFromString(toPathname)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
_, _, errno := syscall.Syscall6(
|
|
SYS_MOVE_MOUNT,
|
|
uintptr(fromDirfd),
|
|
uintptr(unsafe.Pointer(s0)),
|
|
uintptr(toDirfd),
|
|
uintptr(unsafe.Pointer(s1)),
|
|
uintptr(flags),
|
|
0,
|
|
)
|
|
if errno != 0 {
|
|
err = os.NewSyscallError("move_mount", errno)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Mount attaches the underlying filesystem context to the specified pathname.
|
|
func (fs *FS) Mount(pathname string, attrFlags int) error {
|
|
if err := fs.config(FSCONFIG_CMD_CREATE_EXCL, nil, nil, 0); err != nil {
|
|
return err
|
|
}
|
|
fd, err := fs.mount(0, attrFlags)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = MoveMount(
|
|
fd, "",
|
|
-1, pathname,
|
|
MOVE_MOUNT_F_EMPTY_PATH,
|
|
)
|
|
closeErr := syscall.Close(fd)
|
|
if err == nil {
|
|
err = closeErr
|
|
}
|
|
return err
|
|
}
|