diff --git a/interp/os_notunix.go b/interp/os_notunix.go index 66ed777a..1f92f932 100644 --- a/interp/os_notunix.go +++ b/interp/os_notunix.go @@ -6,7 +6,10 @@ package interp import ( + "context" "fmt" + + "mvdan.cc/sh/v3/syntax" ) func mkfifo(path string, mode uint32) error { @@ -17,3 +20,11 @@ func mkfifo(path string, mode uint32) error { func hasPermissionToDir(string) bool { return true } + +// unTestOwnOrGrp panics. Under Unix, it implements the -O and -G unary tests, +// but under Windows, it's unclear how to implement those tests, since Windows +// doesn't have the concept of a file owner, just ACLs, and it's unclear how +// to map the one to the other. +func (r *Runner) unTestOwnOrGrp(ctx context.Context, op syntax.UnTestOperator, x string) bool { + panic(fmt.Sprintf("unhandled unary test op: %v", op)) +} diff --git a/interp/os_unix.go b/interp/os_unix.go index 3a82d69b..e7b4f159 100644 --- a/interp/os_unix.go +++ b/interp/os_unix.go @@ -6,7 +6,13 @@ package interp import ( + "context" + "os/user" + "strconv" + "syscall" + "golang.org/x/sys/unix" + "mvdan.cc/sh/v3/syntax" ) func mkfifo(path string, mode uint32) error { @@ -18,3 +24,22 @@ func mkfifo(path string, mode uint32) error { func hasPermissionToDir(path string) bool { return unix.Access(path, unix.X_OK) == nil } + +// unTestOwnOrGrp implements the -O and -G unary tests. If the file does not +// exist, or the current user cannot be retrieved, returns false. +func (r *Runner) unTestOwnOrGrp(ctx context.Context, op syntax.UnTestOperator, x string) bool { + info, err := r.stat(ctx, x) + if err != nil { + return false + } + u, err := user.Current() + if err != nil { + return false + } + if op == syntax.TsUsrOwn { + uid, _ := strconv.Atoi(u.Uid) + return uint32(uid) == info.Sys().(*syscall.Stat_t).Uid + } + gid, _ := strconv.Atoi(u.Gid) + return uint32(gid) == info.Sys().(*syscall.Stat_t).Gid +} diff --git a/interp/test.go b/interp/test.go index bd19fe12..68109f20 100644 --- a/interp/test.go +++ b/interp/test.go @@ -8,11 +8,7 @@ import ( "fmt" "os" "os/exec" - "os/user" "regexp" - "runtime" - "strconv" - "syscall" "golang.org/x/term" @@ -205,23 +201,7 @@ func (r *Runner) unTest(ctx context.Context, op syntax.UnTestOperator, x string) case syntax.TsNot: return x == "" case syntax.TsUsrOwn, syntax.TsGrpOwn: - if runtime.GOOS == "windows" { - panic(fmt.Sprintf("unhandled unary test op: %v", op)) - } - fi, err := os.Stat(x) - if err != nil { - return false - } - u, err := user.Current() - if err != nil { - return false - } - if op == syntax.TsUsrOwn { - uid, _ := strconv.Atoi(u.Uid) - return uint32(uid) == fi.Sys().(*syscall.Stat_t).Uid - } - gid, _ := strconv.Atoi(u.Gid) - return uint32(gid) == fi.Sys().(*syscall.Stat_t).Gid + return r.unTestOwnOrGrp(ctx, op, x) default: panic(fmt.Sprintf("unhandled unary test op: %v", op)) }