cmd/sharefs: check option parsing behaviour
All checks were successful
Test / Create distribution (push) Successful in 44s
Test / ShareFS (push) Successful in 39s
Test / Sandbox (push) Successful in 47s
Test / Sandbox (race detector) (push) Successful in 46s
Test / Hakurei (race detector) (push) Successful in 54s
Test / Hpkg (push) Successful in 50s
Test / Hakurei (push) Successful in 55s
Test / Flake checks (push) Successful in 1m35s

This change makes it possible to check parseOpts behaviour as part of Go tests.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2025-12-27 17:33:12 +09:00
parent 2f8ca83376
commit 775a9f57c9
3 changed files with 145 additions and 29 deletions

View File

@@ -24,6 +24,7 @@ import (
"os"
"os/exec"
"os/signal"
"path"
"runtime"
"runtime/cgo"
"strconv"
@@ -43,6 +44,9 @@ type (
// closure represents a C function pointer.
closure = C.closure
// fuseArgs represents the fuse_args structure.
fuseArgs = C.struct_fuse_args
// setupState holds state used for setup. Its cgo handle is included in
// sharefs_private and considered opaque to non-setup callbacks.
setupState struct {
@@ -127,7 +131,14 @@ func sharefs_destroy(private_data unsafe.Pointer) {
}
// showHelp prints the help message.
func showHelp(args *C.struct_fuse_args) {
func showHelp(args *fuseArgs) {
executableName := sharefsName
if args.argc > 0 {
executableName = path.Base(C.GoString(*args.argv))
} else if name, err := os.Executable(); err == nil {
executableName = path.Base(name)
}
fmt.Printf("usage: %s [options] <mountpoint>\n\n", executableName)
fmt.Println("Filesystem options:")
@@ -141,7 +152,7 @@ func showHelp(args *C.struct_fuse_args) {
}
// parseOpts parses fuse options via fuse_opt_parse.
func parseOpts(args *C.struct_fuse_args, setup *setupState) (ok bool) {
func parseOpts(args *fuseArgs, setup *setupState, log *log.Logger) (ok bool) {
var unsafeOpts struct {
// Pathname to writable source directory.
source *C.char
@@ -233,25 +244,28 @@ func parseOpts(args *C.struct_fuse_args, setup *setupState) (ok bool) {
return true
}
// copyStrings returns a copy of s with null-termination.
func copyStrings(s ...string) **C.char {
// copyArgs returns a heap allocated copy of an argument slice in fuse_args representation.
func copyArgs(s ...string) fuseArgs {
if len(s) == 0 {
return nil
return fuseArgs{argc: 0, argv: nil, allocated: 0}
}
args := unsafe.Slice((**C.char)(C.malloc(C.size_t(uintptr(len(s))*unsafe.Sizeof(s[0])))), len(s))
for i, arg := range s {
args[i] = C.CString(arg)
}
return &args[0]
return fuseArgs{argc: C.int(len(s)), argv: &args[0], allocated: 1}
}
// freeArgs frees the contents of argument list.
func freeArgs(args *fuseArgs) { C.fuse_opt_free_args(args) }
// unsafeAddArgument adds an argument to fuseArgs via fuse_opt_add_arg.
// The last byte of arg must be 0.
func unsafeAddArgument(args *C.struct_fuse_args, arg string) {
func unsafeAddArgument(args *fuseArgs, arg string) {
C.fuse_opt_add_arg(args, (*C.char)(unsafe.Pointer(unsafe.StringData(arg))))
}
func _main(argc int, argv **C.char) (exitCode int) {
func _main(s ...string) (exitCode int) {
msg := message.New(log.Default())
container.TryArgv0(msg)
runtime.LockOSThread()
@@ -262,7 +276,8 @@ func _main(argc int, argv **C.char) (exitCode int) {
var pinner runtime.Pinner
defer pinner.Unpin()
args := C.struct_fuse_args{argc: C.int(argc), argv: argv, allocated: 1}
args := copyArgs(s...)
defer freeArgs(&args)
// this causes the kernel to enforce access control based on
// struct stat populated by sharefs_getattr
@@ -278,12 +293,9 @@ func _main(argc int, argv **C.char) (exitCode int) {
if C.fuse_parse_cmdline(&args, &opts) != 0 {
return 1
}
defer func() {
if opts.mountpoint != nil {
C.free(unsafe.Pointer(opts.mountpoint))
}
C.fuse_opt_free_args(&args)
}()
if opts.mountpoint != nil {
defer C.free(unsafe.Pointer(opts.mountpoint))
}
if opts.show_version != 0 {
fmt.Println("hakurei version", info.Version())
@@ -315,7 +327,7 @@ func _main(argc int, argv **C.char) (exitCode int) {
os.Args[pathnameArg] = container.Nonexistent
}
if !parseOpts(&args, &setup) {
if !parseOpts(&args, &setup, msg.GetLogger()) {
return 1
}