mirror of
https://github.com/rwinkhart/go-winio.git
synced 2026-08-27 20:36:32 -04:00
Update tests; run fuzzing (#294)
Run fuzzing tests in CI. Use race detector when running tests. Add missing `t.Helper()` calls. Update test helpers in `pkg/bindfilter` to use `RtlGetNtVersionNumbers` instead of reading registry, and skip tests if not running as admin. Signed-off-by: Hamza El-Saawy <hamzaelsaawy@microsoft.com>
This commit is contained in:
@@ -23,7 +23,7 @@ jobs:
|
|||||||
- name: Run golangci-lint
|
- name: Run golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v3
|
uses: golangci/golangci-lint-action@v3
|
||||||
with:
|
with:
|
||||||
version: v1.52
|
version: v1.53
|
||||||
args: >-
|
args: >-
|
||||||
--verbose
|
--verbose
|
||||||
--timeout=5m
|
--timeout=5m
|
||||||
@@ -73,6 +73,7 @@ jobs:
|
|||||||
- go-generate
|
- go-generate
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [windows-2019, windows-2022, ubuntu-latest]
|
os: [windows-2019, windows-2022, ubuntu-latest]
|
||||||
steps:
|
steps:
|
||||||
@@ -88,7 +89,15 @@ jobs:
|
|||||||
run: go install gotest.tools/gotestsum@${{ env.GOTESTSUM_VERSION }}
|
run: go install gotest.tools/gotestsum@${{ env.GOTESTSUM_VERSION }}
|
||||||
|
|
||||||
- name: Test repo
|
- name: Test repo
|
||||||
run: gotestsum --format standard-verbose --debug -- -gcflags=all=-d=checkptr -v ./...
|
run: gotestsum --format standard-verbose --debug -- -gcflags=all=-d=checkptr -race -v ./...
|
||||||
|
|
||||||
|
# Fuzzing was added in go1.18, so all stable/supported versions of go should support it.
|
||||||
|
# hvsock fuzzing fails on windows 2019, even though tests pass.
|
||||||
|
#
|
||||||
|
# If fuzzing tests are added to different packages, add them here.
|
||||||
|
- name: Fuzz repo
|
||||||
|
if: ${{ matrix.os == 'windows-2022' }}
|
||||||
|
run: gotestsum --format standard-verbose --debug -- -run "^#" -fuzztime 500x -fuzz "FuzzHvSock"
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build Repo
|
name: Build Repo
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ linters:
|
|||||||
- gofmt # files are gofmt'ed
|
- gofmt # files are gofmt'ed
|
||||||
- gosec # security
|
- gosec # security
|
||||||
- nilerr # returns nil even with non-nil error
|
- nilerr # returns nil even with non-nil error
|
||||||
|
- thelper # test helpers without t.Helper()
|
||||||
- unparam # unused function params
|
- unparam # unused function params
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func ensurePresent(t *testing.T, m map[string]string, keys ...string) {
|
func ensurePresent(t *testing.T, m map[string]string, keys ...string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
if _, ok := m[k]; !ok {
|
if _, ok := m[k]; !ok {
|
||||||
t.Error(k, "not present in tar header")
|
t.Error(k, "not present in tar header")
|
||||||
@@ -25,6 +27,8 @@ func ensurePresent(t *testing.T, m map[string]string, keys ...string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setSparse(t *testing.T, f *os.File) {
|
func setSparse(t *testing.T, f *os.File) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
if err := windows.DeviceIoControl(windows.Handle(f.Fd()), windows.FSCTL_SET_SPARSE, nil, 0, nil, 0, nil, nil); err != nil {
|
if err := windows.DeviceIoControl(windows.Handle(f.Fd()), windows.FSCTL_SET_SPARSE, nil, 0, nil, 0, nil, nil); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -32,6 +36,8 @@ func setSparse(t *testing.T, f *os.File) {
|
|||||||
|
|
||||||
// compareReaders validates that two readers contain the exact same data.
|
// compareReaders validates that two readers contain the exact same data.
|
||||||
func compareReaders(t *testing.T, rActual io.Reader, rExpected io.Reader) {
|
func compareReaders(t *testing.T, rActual io.Reader, rExpected io.Reader) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
const size = 8 * 1024
|
const size = 8 * 1024
|
||||||
var bufExpected, bufActual [size]byte
|
var bufExpected, bufActual [size]byte
|
||||||
var readCount int64
|
var readCount int64
|
||||||
@@ -71,6 +77,8 @@ func TestRoundTrip(t *testing.T) {
|
|||||||
//nolint:gosec // G306: Expect WriteFile permissions to be 0600 or less
|
//nolint:gosec // G306: Expect WriteFile permissions to be 0600 or less
|
||||||
for name, setup := range map[string]func(*testing.T) string{
|
for name, setup := range map[string]func(*testing.T) string{
|
||||||
"normalFile": func(t *testing.T) string {
|
"normalFile": func(t *testing.T) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
path := filepath.Join(t.TempDir(), "foo.txt")
|
path := filepath.Join(t.TempDir(), "foo.txt")
|
||||||
if err := os.WriteFile(path, []byte("testing 1 2 3\n"), 0644); err != nil {
|
if err := os.WriteFile(path, []byte("testing 1 2 3\n"), 0644); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -78,6 +86,8 @@ func TestRoundTrip(t *testing.T) {
|
|||||||
return path
|
return path
|
||||||
},
|
},
|
||||||
"normalFileEmpty": func(t *testing.T) string {
|
"normalFileEmpty": func(t *testing.T) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
path := filepath.Join(t.TempDir(), "foo.txt")
|
path := filepath.Join(t.TempDir(), "foo.txt")
|
||||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
|
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -87,6 +97,8 @@ func TestRoundTrip(t *testing.T) {
|
|||||||
return path
|
return path
|
||||||
},
|
},
|
||||||
"sparseFileEmpty": func(t *testing.T) string {
|
"sparseFileEmpty": func(t *testing.T) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
path := filepath.Join(t.TempDir(), "foo.txt")
|
path := filepath.Join(t.TempDir(), "foo.txt")
|
||||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
|
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -97,6 +109,8 @@ func TestRoundTrip(t *testing.T) {
|
|||||||
return path
|
return path
|
||||||
},
|
},
|
||||||
"sparseFileWithNoAllocatedRanges": func(t *testing.T) string {
|
"sparseFileWithNoAllocatedRanges": func(t *testing.T) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
path := filepath.Join(t.TempDir(), "foo.txt")
|
path := filepath.Join(t.TempDir(), "foo.txt")
|
||||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
|
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -112,6 +126,8 @@ func TestRoundTrip(t *testing.T) {
|
|||||||
return path
|
return path
|
||||||
},
|
},
|
||||||
"sparseFileWithOneAllocatedRange": func(t *testing.T) string {
|
"sparseFileWithOneAllocatedRange": func(t *testing.T) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
path := filepath.Join(t.TempDir(), "foo.txt")
|
path := filepath.Join(t.TempDir(), "foo.txt")
|
||||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
|
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -125,6 +141,8 @@ func TestRoundTrip(t *testing.T) {
|
|||||||
return path
|
return path
|
||||||
},
|
},
|
||||||
"sparseFileWithMultipleAllocatedRanges": func(t *testing.T) string {
|
"sparseFileWithMultipleAllocatedRanges": func(t *testing.T) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
path := filepath.Join(t.TempDir(), "foo.txt")
|
path := filepath.Join(t.TempDir(), "foo.txt")
|
||||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
|
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import (
|
|||||||
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
||||||
//sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
|
//sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
|
||||||
|
|
||||||
|
//todo (go1.19): switch to [atomic.Bool]
|
||||||
|
|
||||||
type atomicBool int32
|
type atomicBool int32
|
||||||
|
|
||||||
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
|
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import (
|
|||||||
// so we check that the current.AllocationSize is >= expected.AllocationSize.
|
// so we check that the current.AllocationSize is >= expected.AllocationSize.
|
||||||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/5afa7f66-619c-48f3-955f-68c4ece704ae
|
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/5afa7f66-619c-48f3-955f-68c4ece704ae
|
||||||
func checkFileStandardInfo(t *testing.T, current, expected *FileStandardInfo) {
|
func checkFileStandardInfo(t *testing.T, current, expected *FileStandardInfo) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
if current.AllocationSize < expected.AllocationSize {
|
if current.AllocationSize < expected.AllocationSize {
|
||||||
t.Fatalf("FileStandardInfo unexpectedly had AllocationSize %d, expecting >=%d", current.AllocationSize, expected.AllocationSize)
|
t.Fatalf("FileStandardInfo unexpectedly had AllocationSize %d, expecting >=%d", current.AllocationSize, expected.AllocationSize)
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -37,6 +37,7 @@ func serverListen(u testUtil) (l *HvsockListener, a *HvsockAddr) {
|
|||||||
u.T.Logf("address collision %v", a)
|
u.T.Logf("address collision %v", a)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
u.T.Logf("listening on %v", a)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
u.Must(err, "could not listen")
|
u.Must(err, "could not listen")
|
||||||
@@ -579,9 +580,11 @@ type testUtil struct {
|
|||||||
T testing.TB
|
T testing.TB
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUtil(t testing.TB) testUtil {
|
func newUtil(tb testing.TB) testUtil {
|
||||||
|
tb.Helper()
|
||||||
|
|
||||||
return testUtil{
|
return testUtil{
|
||||||
T: t,
|
T: tb,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -223,6 +223,8 @@ func TestCloseAbortsListen(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ensureEOFOnClose(t *testing.T, r io.Reader, w io.Closer) {
|
func ensureEOFOnClose(t *testing.T, r io.Reader, w io.Closer) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
b := make([]byte, 10)
|
b := make([]byte, 10)
|
||||||
w.Close()
|
w.Close()
|
||||||
n, err := r.Read(b)
|
n, err := r.Read(b)
|
||||||
|
|||||||
@@ -8,14 +8,15 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"golang.org/x/sys/windows/registry"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestApplyFileBinding(t *testing.T) {
|
func TestApplyFileBinding(t *testing.T) {
|
||||||
|
requireElevated(t)
|
||||||
|
|
||||||
source := t.TempDir()
|
source := t.TempDir()
|
||||||
destination := t.TempDir()
|
destination := t.TempDir()
|
||||||
fileName := "testFile.txt"
|
fileName := "testFile.txt"
|
||||||
@@ -55,12 +56,15 @@ func TestApplyFileBinding(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func removeFileBinding(t *testing.T, mountpoint string) {
|
func removeFileBinding(t *testing.T, mountpoint string) {
|
||||||
|
t.Helper()
|
||||||
if err := RemoveFileBinding(mountpoint); err != nil {
|
if err := RemoveFileBinding(mountpoint); err != nil {
|
||||||
t.Logf("failed to remove file binding from %s: %q", mountpoint, err)
|
t.Logf("failed to remove file binding from %s: %q", mountpoint, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApplyFileBindingReadOnly(t *testing.T) {
|
func TestApplyFileBindingReadOnly(t *testing.T) {
|
||||||
|
requireElevated(t)
|
||||||
|
|
||||||
source := t.TempDir()
|
source := t.TempDir()
|
||||||
destination := t.TempDir()
|
destination := t.TempDir()
|
||||||
fileName := "testFile.txt"
|
fileName := "testFile.txt"
|
||||||
@@ -108,19 +112,14 @@ func TestApplyFileBindingReadOnly(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEnsureOnlyOneTargetCanBeMounted(t *testing.T) {
|
func TestEnsureOnlyOneTargetCanBeMounted(t *testing.T) {
|
||||||
version, err := getWindowsBuildNumber()
|
requireElevated(t)
|
||||||
if err != nil {
|
requireBuild(t, RS5+1) // support added after RS5
|
||||||
t.Fatalf("couldn't get version number: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if version <= 17763 {
|
|
||||||
t.Skip("not supported on RS5 or earlier")
|
|
||||||
}
|
|
||||||
source := t.TempDir()
|
source := t.TempDir()
|
||||||
secondarySource := t.TempDir()
|
secondarySource := t.TempDir()
|
||||||
destination := t.TempDir()
|
destination := t.TempDir()
|
||||||
|
|
||||||
err = ApplyFileBinding(destination, source, false)
|
err := ApplyFileBinding(destination, source, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -159,14 +158,9 @@ func checkSourceIsMountedOnDestination(src, dst string) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetBindMappings(t *testing.T) {
|
func TestGetBindMappings(t *testing.T) {
|
||||||
version, err := getWindowsBuildNumber()
|
requireElevated(t)
|
||||||
if err != nil {
|
requireBuild(t, RS5+1) // support added after RS5
|
||||||
t.Fatalf("couldn't get version number: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if version <= 17763 {
|
|
||||||
t.Skip("not supported on RS5 or earlier")
|
|
||||||
}
|
|
||||||
// GetBindMappings will expand short paths like ADMINI~1 and PROGRA~1 to their
|
// GetBindMappings will expand short paths like ADMINI~1 and PROGRA~1 to their
|
||||||
// full names. In order to properly match the names later, we expand them here.
|
// full names. In order to properly match the names later, we expand them here.
|
||||||
srcShort := t.TempDir()
|
srcShort := t.TempDir()
|
||||||
@@ -198,6 +192,8 @@ func TestGetBindMappings(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRemoveFileBinding(t *testing.T) {
|
func TestRemoveFileBinding(t *testing.T) {
|
||||||
|
requireElevated(t)
|
||||||
|
|
||||||
srcShort := t.TempDir()
|
srcShort := t.TempDir()
|
||||||
source, err := getFinalPath(srcShort)
|
source, err := getFinalPath(srcShort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -238,32 +234,9 @@ func TestRemoveFileBinding(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getWindowsBuildNumber() (int, error) {
|
|
||||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("read CurrentVersion reg key: %w", err)
|
|
||||||
}
|
|
||||||
defer k.Close()
|
|
||||||
buildNumStr, _, err := k.GetStringValue("CurrentBuild")
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("read CurrentBuild reg value: %w", err)
|
|
||||||
}
|
|
||||||
buildNum, err := strconv.Atoi(buildNumStr)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return buildNum, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetBindMappingsSymlinks(t *testing.T) {
|
func TestGetBindMappingsSymlinks(t *testing.T) {
|
||||||
version, err := getWindowsBuildNumber()
|
requireElevated(t)
|
||||||
if err != nil {
|
requireBuild(t, RS5+1) // support added after RS5
|
||||||
t.Fatalf("couldn't get version number: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if version <= 17763 {
|
|
||||||
t.Skip("not supported on RS5 or earlier")
|
|
||||||
}
|
|
||||||
|
|
||||||
srcShort := t.TempDir()
|
srcShort := t.TempDir()
|
||||||
sourceNested := filepath.Join(srcShort, "source")
|
sourceNested := filepath.Join(srcShort, "source")
|
||||||
@@ -307,3 +280,23 @@ func TestGetBindMappingsSymlinks(t *testing.T) {
|
|||||||
t.Fatalf("expected to find %s mounted on %s, but could not", source, destination)
|
t.Fatalf("expected to find %s mounted on %s, but could not", source, destination)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func requireElevated(tb testing.TB) {
|
||||||
|
tb.Helper()
|
||||||
|
if !windows.GetCurrentProcessToken().IsElevated() {
|
||||||
|
tb.Skip("requires elevated privileges")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const RS5 = 17763
|
||||||
|
|
||||||
|
//todo: also check that `bindfltapi.dll` exists
|
||||||
|
|
||||||
|
// require current build to be >= build
|
||||||
|
func requireBuild(tb testing.TB, build uint32) {
|
||||||
|
tb.Helper()
|
||||||
|
_, _, b := windows.RtlGetNtVersionNumbers()
|
||||||
|
if b < build {
|
||||||
|
tb.Skipf("requires build %d+; current build is %d", build, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func mustGUIDFromString(t *testing.T, s string) guid.GUID {
|
func mustGUIDFromString(t *testing.T, s string) guid.GUID {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
g, err := guid.FromString(s)
|
g, err := guid.FromString(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func mustNewV4(t *testing.T) GUID {
|
func mustNewV4(t *testing.T) GUID {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
g, err := NewV4()
|
g, err := NewV4()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -15,6 +17,8 @@ func mustNewV4(t *testing.T) GUID {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func mustNewV5(t *testing.T, namespace GUID, name []byte) GUID {
|
func mustNewV5(t *testing.T, namespace GUID, name []byte) GUID {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
g, err := NewV5(namespace, name)
|
g, err := NewV5(namespace, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -23,6 +27,8 @@ func mustNewV5(t *testing.T, namespace GUID, name []byte) GUID {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func mustFromString(t *testing.T, s string) GUID {
|
func mustFromString(t *testing.T, s string) GUID {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
g, err := FromString(s)
|
g, err := FromString(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ func TestGrantVmGroupAccess(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func verifyVMAccountDACLs(t *testing.T, name string, permissions []string) {
|
func verifyVMAccountDACLs(t *testing.T, name string, permissions []string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
cmd := exec.Command("icacls", name)
|
cmd := exec.Command("icacls", name)
|
||||||
outb, err := cmd.CombinedOutput()
|
outb, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user