From 322115962618afd0875664c555ae15e1a9b0ee9e Mon Sep 17 00:00:00 2001 From: Larry Clapp Date: Sat, 13 Jul 2024 13:37:26 -0500 Subject: [PATCH] interp: implement -O and -G tests Implement -O (syntax.TsUsrOwn, "file exists and is owned by the effective user id") and -G (syntax.TsGrpOwn, "file exists and is owned by the effective group id") tests, for non-Windows only. Under Windows, still panics, as before. -O and -G are hard/not applicable for Windows, since it doesn't really have the concept of "file owners", only ACLs, and it's not clear how to translate one to the other. --- interp/os_notunix.go | 11 +++++++++++ interp/os_unix.go | 25 +++++++++++++++++++++++++ interp/test.go | 2 ++ 3 files changed, 38 insertions(+) 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 3f8ffca7..68109f20 100644 --- a/interp/test.go +++ b/interp/test.go @@ -200,6 +200,8 @@ func (r *Runner) unTest(ctx context.Context, op syntax.UnTestOperator, x string) return r.lookupVar(x).Kind == expand.NameRef case syntax.TsNot: return x == "" + case syntax.TsUsrOwn, syntax.TsGrpOwn: + return r.unTestOwnOrGrp(ctx, op, x) default: panic(fmt.Sprintf("unhandled unary test op: %v", op)) }