cmd/sharefs: opaque setup state
All checks were successful
Test / Create distribution (push) Successful in 43s
Test / Sandbox (push) Successful in 2m25s
Test / Hakurei (push) Successful in 3m29s
Test / ShareFS (push) Successful in 3m29s
Test / Hpkg (push) Successful in 4m25s
Test / Sandbox (race detector) (push) Successful in 4m35s
Test / Hakurei (race detector) (push) Successful in 5m37s
Test / Flake checks (push) Successful in 1m46s
All checks were successful
Test / Create distribution (push) Successful in 43s
Test / Sandbox (push) Successful in 2m25s
Test / Hakurei (push) Successful in 3m29s
Test / ShareFS (push) Successful in 3m29s
Test / Hpkg (push) Successful in 4m25s
Test / Sandbox (race detector) (push) Successful in 4m35s
Test / Hakurei (race detector) (push) Successful in 5m37s
Test / Flake checks (push) Successful in 1m46s
This allows unrestricted use of the type system and prepares setup code for cross-process initialisation. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -13,11 +13,8 @@
|
|||||||
|
|
||||||
/* sharefs_private is populated by sharefs_init and contains process-wide context */
|
/* sharefs_private is populated by sharefs_init and contains process-wide context */
|
||||||
struct sharefs_private {
|
struct sharefs_private {
|
||||||
int dirfd; /* source dirfd opened during sharefs_init */
|
int dirfd; /* source dirfd opened during sharefs_init */
|
||||||
bool init_failed; /* whether sharefs_init failed */
|
uintptr_t setup; /* cgo handle of opaque setup state */
|
||||||
uintptr_t source_handle; /* cgo handle of pathname to open for dirfd, freed during sharefs_init */
|
|
||||||
uintptr_t setuid; /* uid to set by sharefs_init when running as root */
|
|
||||||
uintptr_t setgid; /* gid to set by sharefs_init when running as root */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int sharefs_getattr(const char *pathname, struct stat *statbuf, struct fuse_file_info *fi);
|
int sharefs_getattr(const char *pathname, struct stat *statbuf, struct fuse_file_info *fi);
|
||||||
|
|||||||
@@ -16,16 +16,17 @@ static inline int _fuse_main(int argc, char *argv[], const struct fuse_operation
|
|||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
|
"encoding/gob"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/cgo"
|
"runtime/cgo"
|
||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/internal/info"
|
"hakurei.app/internal/info"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,23 +34,50 @@ type (
|
|||||||
// closure represents a C function pointer.
|
// closure represents a C function pointer.
|
||||||
closure = C.closure
|
closure = C.closure
|
||||||
|
|
||||||
// fuseArgs represents the fuse_args structure.
|
// setupState holds state used for setup. Its cgo handle is included in
|
||||||
fuseArgs = C.struct_fuse_args
|
// sharefs_private and considered opaque to non-setup callbacks.
|
||||||
|
setupState struct {
|
||||||
|
// Whether sharefs_init failed.
|
||||||
|
initFailed bool
|
||||||
|
|
||||||
|
// Absolute pathname to open for dirfd.
|
||||||
|
Source *check.Absolute
|
||||||
|
// Opened by parent, ignored unless Source is nil.
|
||||||
|
Dirfd int
|
||||||
|
// New uid and gid to set by sharefs_init when starting as root.
|
||||||
|
Setuid, Setgid int
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() { gob.Register(new(setupState)) }
|
||||||
|
|
||||||
|
// destroySetup invalidates the setup [cgo.Handle] in a sharefs_private structure.
|
||||||
|
func destroySetup(private_data unsafe.Pointer) (ok bool) {
|
||||||
|
if private_data == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
priv := (*C.struct_sharefs_private)(private_data)
|
||||||
|
|
||||||
|
if h := cgo.Handle(priv.setup); h != 0 {
|
||||||
|
priv.setup = 0
|
||||||
|
h.Delete()
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
//export sharefs_init
|
//export sharefs_init
|
||||||
func sharefs_init(_ *C.struct_fuse_conn_info, cfg *C.struct_fuse_config) unsafe.Pointer {
|
func sharefs_init(_ *C.struct_fuse_conn_info, cfg *C.struct_fuse_config) unsafe.Pointer {
|
||||||
ctx := C.fuse_get_context()
|
ctx := C.fuse_get_context()
|
||||||
priv := (*C.struct_sharefs_private)(ctx.private_data)
|
priv := (*C.struct_sharefs_private)(ctx.private_data)
|
||||||
source := cgo.Handle(priv.source_handle).Value().(string)
|
setup := cgo.Handle(priv.setup).Value().(*setupState)
|
||||||
|
|
||||||
setuid, setgid := int(priv.setuid), int(priv.setgid)
|
|
||||||
if os.Geteuid() == 0 {
|
if os.Geteuid() == 0 {
|
||||||
if setuid <= 0 || setgid <= 0 {
|
if setup.Setuid <= 0 || setup.Setgid <= 0 {
|
||||||
log.Println("setuid and setgid must not be 0")
|
log.Println("setuid and setgid must not be 0")
|
||||||
goto fail
|
goto fail
|
||||||
}
|
}
|
||||||
if err := syscall.Setresgid(setgid, setgid, setgid); err != nil {
|
if err := syscall.Setresgid(setup.Setgid, setup.Setgid, setup.Setgid); err != nil {
|
||||||
log.Printf("cannot set gid: %v", err)
|
log.Printf("cannot set gid: %v", err)
|
||||||
goto fail
|
goto fail
|
||||||
}
|
}
|
||||||
@@ -57,7 +85,7 @@ func sharefs_init(_ *C.struct_fuse_conn_info, cfg *C.struct_fuse_config) unsafe.
|
|||||||
log.Printf("cannot set supplementary groups: %v", err)
|
log.Printf("cannot set supplementary groups: %v", err)
|
||||||
goto fail
|
goto fail
|
||||||
}
|
}
|
||||||
if err := syscall.Setresuid(setuid, setuid, setuid); err != nil {
|
if err := syscall.Setresuid(setup.Setuid, setup.Setuid, setup.Setuid); err != nil {
|
||||||
log.Printf("cannot set uid: %v", err)
|
log.Printf("cannot set uid: %v", err)
|
||||||
goto fail
|
goto fail
|
||||||
}
|
}
|
||||||
@@ -71,11 +99,13 @@ func sharefs_init(_ *C.struct_fuse_conn_info, cfg *C.struct_fuse_config) unsafe.
|
|||||||
cfg.negative_timeout = 0
|
cfg.negative_timeout = 0
|
||||||
|
|
||||||
// all future filesystem operations happen through this dirfd
|
// all future filesystem operations happen through this dirfd
|
||||||
if fd, err := syscall.Open(source, syscall.O_DIRECTORY|syscall.O_RDONLY, 0); err != nil {
|
if setup.Source == nil {
|
||||||
log.Printf("cannot open %q: %v", source, err)
|
priv.dirfd = C.int(setup.Dirfd)
|
||||||
|
} else if fd, err := syscall.Open(setup.Source.String(), syscall.O_DIRECTORY|syscall.O_RDONLY, 0); err != nil {
|
||||||
|
log.Printf("cannot open %q: %v", setup.Source.String(), err)
|
||||||
goto fail
|
goto fail
|
||||||
} else if err = syscall.Fchdir(fd); err != nil {
|
} else if err = syscall.Fchdir(fd); err != nil {
|
||||||
log.Printf("cannot enter %q: %s", source, err)
|
log.Printf("cannot enter %q: %s", setup.Source.String(), err)
|
||||||
goto fail
|
goto fail
|
||||||
} else {
|
} else {
|
||||||
priv.dirfd = C.int(fd)
|
priv.dirfd = C.int(fd)
|
||||||
@@ -84,11 +114,7 @@ func sharefs_init(_ *C.struct_fuse_conn_info, cfg *C.struct_fuse_config) unsafe.
|
|||||||
return ctx.private_data
|
return ctx.private_data
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
sourceHandle := cgo.Handle(priv.source_handle)
|
setup.initFailed = true
|
||||||
priv.source_handle = 0
|
|
||||||
sourceHandle.Delete()
|
|
||||||
priv.init_failed = true
|
|
||||||
|
|
||||||
C.fuse_exit(ctx.fuse)
|
C.fuse_exit(ctx.fuse)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -96,12 +122,8 @@ fail:
|
|||||||
//export sharefs_destroy
|
//export sharefs_destroy
|
||||||
func sharefs_destroy(private_data unsafe.Pointer) {
|
func sharefs_destroy(private_data unsafe.Pointer) {
|
||||||
if private_data != nil {
|
if private_data != nil {
|
||||||
|
destroySetup(private_data)
|
||||||
priv := (*C.struct_sharefs_private)(private_data)
|
priv := (*C.struct_sharefs_private)(private_data)
|
||||||
if priv.source_handle != 0 {
|
|
||||||
sourceHandle := cgo.Handle(priv.source_handle)
|
|
||||||
priv.source_handle = 0
|
|
||||||
sourceHandle.Delete()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := syscall.Close(int(priv.dirfd)); err != nil {
|
if err := syscall.Close(int(priv.dirfd)); err != nil {
|
||||||
log.Printf("cannot close source directory: %v", err)
|
log.Printf("cannot close source directory: %v", err)
|
||||||
@@ -124,11 +146,7 @@ func showHelp(args *C.struct_fuse_args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseOpts parses fuse options via fuse_opt_parse.
|
// parseOpts parses fuse options via fuse_opt_parse.
|
||||||
func parseOpts(args *C.struct_fuse_args) (
|
func parseOpts(args *C.struct_fuse_args, setup *setupState) bool {
|
||||||
source string,
|
|
||||||
setuid, setgid int,
|
|
||||||
ret int,
|
|
||||||
) {
|
|
||||||
var unsafeOpts struct {
|
var unsafeOpts struct {
|
||||||
// Pathname to writable source directory.
|
// Pathname to writable source directory.
|
||||||
source *C.char
|
source *C.char
|
||||||
@@ -137,6 +155,10 @@ func parseOpts(args *C.struct_fuse_args) (
|
|||||||
setuid *C.char
|
setuid *C.char
|
||||||
// Decimal string representation of gid to set when running as root.
|
// Decimal string representation of gid to set when running as root.
|
||||||
setgid *C.char
|
setgid *C.char
|
||||||
|
|
||||||
|
// Decimal string representation of open file descriptor to read setupState from.
|
||||||
|
// This is an internal detail for containerisation and must not be specified directly.
|
||||||
|
setup *C.char
|
||||||
}
|
}
|
||||||
|
|
||||||
if C.fuse_opt_parse(args, unsafe.Pointer(&unsafeOpts), &[]C.struct_fuse_opt{
|
if C.fuse_opt_parse(args, unsafe.Pointer(&unsafeOpts), &[]C.struct_fuse_opt{
|
||||||
@@ -144,10 +166,11 @@ func parseOpts(args *C.struct_fuse_args) (
|
|||||||
{templ: C.CString("setuid=%s"), offset: C.ulong(unsafe.Offsetof(unsafeOpts.setuid)), value: 0},
|
{templ: C.CString("setuid=%s"), offset: C.ulong(unsafe.Offsetof(unsafeOpts.setuid)), value: 0},
|
||||||
{templ: C.CString("setgid=%s"), offset: C.ulong(unsafe.Offsetof(unsafeOpts.setgid)), value: 0},
|
{templ: C.CString("setgid=%s"), offset: C.ulong(unsafe.Offsetof(unsafeOpts.setgid)), value: 0},
|
||||||
|
|
||||||
|
{templ: C.CString("setup=%s"), offset: C.ulong(unsafe.Offsetof(unsafeOpts.setup)), value: 0},
|
||||||
|
|
||||||
C._FUSE_OPT_END(),
|
C._FUSE_OPT_END(),
|
||||||
}[0], nil) == -1 {
|
}[0], nil) == -1 {
|
||||||
ret = 1
|
return false
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if unsafeOpts.source != nil {
|
if unsafeOpts.source != nil {
|
||||||
@@ -160,34 +183,47 @@ func parseOpts(args *C.struct_fuse_args) (
|
|||||||
defer C.free(unsafe.Pointer(unsafeOpts.setgid))
|
defer C.free(unsafe.Pointer(unsafeOpts.setgid))
|
||||||
}
|
}
|
||||||
|
|
||||||
if unsafeOpts.source == nil || *unsafeOpts.source == 0 {
|
if unsafeOpts.setup != nil {
|
||||||
|
defer C.free(unsafe.Pointer(unsafeOpts.setup))
|
||||||
|
|
||||||
|
if v, err := strconv.Atoi(C.GoString(unsafeOpts.setup)); err != nil || v < 3 {
|
||||||
|
log.Println("invalid value for option setup")
|
||||||
|
return false
|
||||||
|
} else if err = gob.NewDecoder(os.NewFile(uintptr(v), "setup")).Decode(setup); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if unsafeOpts.source == nil {
|
||||||
showHelp(args)
|
showHelp(args)
|
||||||
ret = 1
|
return false
|
||||||
return
|
} else if a, err := check.NewAbs(C.GoString(unsafeOpts.source)); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return false
|
||||||
} else {
|
} else {
|
||||||
source = C.GoString(unsafeOpts.source)
|
setup.Source = a
|
||||||
}
|
}
|
||||||
|
|
||||||
if unsafeOpts.setuid == nil {
|
if unsafeOpts.setuid == nil {
|
||||||
setuid = -1
|
setup.Setuid = -1
|
||||||
} else if v, err := strconv.Atoi(C.GoString(unsafeOpts.setuid)); err != nil || v <= 0 {
|
} else if v, err := strconv.Atoi(C.GoString(unsafeOpts.setuid)); err != nil || v <= 0 {
|
||||||
log.Println("invalid value for option setuid")
|
log.Println("invalid value for option setuid")
|
||||||
ret = 1
|
return false
|
||||||
return
|
|
||||||
} else {
|
} else {
|
||||||
setuid = v
|
setup.Setuid = v
|
||||||
}
|
}
|
||||||
if unsafeOpts.setgid == nil {
|
if unsafeOpts.setgid == nil {
|
||||||
setgid = -1
|
setup.Setgid = -1
|
||||||
} else if v, err := strconv.Atoi(C.GoString(unsafeOpts.setgid)); err != nil || v <= 0 {
|
} else if v, err := strconv.Atoi(C.GoString(unsafeOpts.setgid)); err != nil || v <= 0 {
|
||||||
log.Println("invalid value for option setgid")
|
log.Println("invalid value for option setgid")
|
||||||
ret = 1
|
return false
|
||||||
return
|
|
||||||
} else {
|
} else {
|
||||||
setgid = v
|
setup.Setgid = v
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyStrings returns a copy of s with null-termination.
|
// copyStrings returns a copy of s with null-termination.
|
||||||
@@ -204,7 +240,7 @@ func copyStrings(s ...string) **C.char {
|
|||||||
|
|
||||||
// unsafeAddArgument adds an argument to fuseArgs via fuse_opt_add_arg.
|
// unsafeAddArgument adds an argument to fuseArgs via fuse_opt_add_arg.
|
||||||
// The last byte of arg must be 0.
|
// The last byte of arg must be 0.
|
||||||
func unsafeAddArgument(args *fuseArgs, arg string) {
|
func unsafeAddArgument(args *C.struct_fuse_args, arg string) {
|
||||||
C.fuse_opt_add_arg(args, (*C.char)(unsafe.Pointer(unsafe.StringData(arg))))
|
C.fuse_opt_add_arg(args, (*C.char)(unsafe.Pointer(unsafe.StringData(arg))))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,6 +261,9 @@ func _main(argc int, argv **C.char) int {
|
|||||||
|
|
||||||
var priv C.struct_sharefs_private
|
var priv C.struct_sharefs_private
|
||||||
pinner.Pin(&priv)
|
pinner.Pin(&priv)
|
||||||
|
var setup setupState
|
||||||
|
priv.setup = C.uintptr_t(cgo.NewHandle(&setup))
|
||||||
|
defer destroySetup(unsafe.Pointer(&priv))
|
||||||
|
|
||||||
var opts C.struct_fuse_cmdline_opts
|
var opts C.struct_fuse_cmdline_opts
|
||||||
if C.fuse_parse_cmdline(&args, &opts) != 0 {
|
if C.fuse_parse_cmdline(&args, &opts) != 0 {
|
||||||
@@ -254,29 +293,18 @@ func _main(argc int, argv **C.char) int {
|
|||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
if !parseOpts(&args, &setup) {
|
||||||
source, setuid, setgid, ret := parseOpts(&args)
|
return 1
|
||||||
if ret != 0 {
|
}
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
if a, err := filepath.Abs(source); err != nil {
|
if os.Geteuid() == 0 {
|
||||||
log.Println(err)
|
if setup.Setuid <= 0 || setup.Setgid <= 0 {
|
||||||
return 1
|
log.Println("setuid and setgid must not be 0")
|
||||||
} else {
|
|
||||||
priv.source_handle = C.uintptr_t(cgo.NewHandle(a))
|
|
||||||
}
|
|
||||||
|
|
||||||
if os.Geteuid() == 0 {
|
|
||||||
if setuid <= 0 || setgid <= 0 {
|
|
||||||
log.Println("setuid and setgid must not be 0")
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
} else if setuid > 0 || setgid > 0 {
|
|
||||||
log.Println("setuid and setgid has no effect when not starting as root")
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
priv.setuid, priv.setgid = C.uintptr_t(setuid), C.uintptr_t(setgid)
|
} else if setup.Setuid > 0 || setup.Setgid > 0 {
|
||||||
|
log.Println("setuid and setgid has no effect when not starting as root")
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
op := C.struct_fuse_operations{
|
op := C.struct_fuse_operations{
|
||||||
@@ -343,7 +371,7 @@ func _main(argc int, argv **C.char) int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if priv.init_failed {
|
if setup.initFailed {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
Reference in New Issue
Block a user