Skip to content

Commit

Permalink
Add ResolvePath tests
Browse files Browse the repository at this point in the history
Add required computestorage APIs to allow for running tests of
`ResolvePath`.

Signed-off-by: Hamza El-Saawy <[email protected]>
  • Loading branch information
helsaawy committed Apr 6, 2023
1 parent 4b2c5cb commit 826c769
Show file tree
Hide file tree
Showing 8 changed files with 489 additions and 21 deletions.
49 changes: 49 additions & 0 deletions internal/computestorage/computestorage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//go:build windows

package computestorage

import (
"context"
"fmt"

"golang.org/x/sys/windows"

"github.com/Microsoft/go-winio/internal/interop"
)

//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go computestorage.go

// https://learn.microsoft.com/en-us/virtualization/api/hcs/reference/hcsformatwritablelayervhd
//
//sys hcsFormatWritableLayerVhd(handle windows.Handle) (hr error) = computestorage.HcsFormatWritableLayerVhd?

// FormatWritableLayerVhd formats a virtual disk for use as a writable container layer.
//
// If the VHD is not mounted it will be temporarily mounted.
//
// NOTE: This API had a breaking change in the operating system after Windows Server 2019.
// On ws2019 the API expects to get passed a file handle from CreateFile for the vhd that
// the caller wants to format. On > ws2019, its expected that the caller passes a vhd handle
// that can be obtained from the virtdisk APIs.
func FormatWritableLayerVhd(ctx context.Context, vhdHandle windows.Handle) (err error) {
err = hcsFormatWritableLayerVhd(vhdHandle)
if err != nil {
return fmt.Errorf("failed to format writable layer vhd: %w", err)
}
return nil
}

// https://learn.microsoft.com/en-us/virtualization/api/hcs/reference/hcsgetlayervhdmountpath
//
//sys hcsGetLayerVhdMountPath(vhdHandle windows.Handle, mountPath **uint16) (hr error) = computestorage.HcsGetLayerVhdMountPath?

// GetLayerVhdMountPath returns the volume path for a virtual disk of a writable container layer.
func GetLayerVhdMountPath(ctx context.Context, vhdHandle windows.Handle) (path string, err error) {
var mountPath *uint16
err = hcsGetLayerVhdMountPath(vhdHandle, &mountPath)
if err != nil {
return "", fmt.Errorf("failed to get vhd mount path: %w", err)
}
path = interop.ConvertAndFreeCoTaskMemString(mountPath)
return path, nil
}
1 change: 1 addition & 0 deletions internal/computestorage/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package computestorage
77 changes: 77 additions & 0 deletions internal/computestorage/zsyscall_windows.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions internal/interop/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package interop
25 changes: 25 additions & 0 deletions internal/interop/interop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//go:build windows

package interop

import (
"syscall"
"unsafe"
)

//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go interop.go

//sys coTaskMemFree(buffer unsafe.Pointer) = api_ms_win_core_com_l1_1_0.CoTaskMemFree

func ConvertAndFreeCoTaskMemString(buffer *uint16) string {
str := syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(buffer))[:])
coTaskMemFree(unsafe.Pointer(buffer))
return str
}

func Win32FromHresult(hr uintptr) syscall.Errno {
if hr&0x1fff0000 == 0x00070000 {
return syscall.Errno(hr & 0xffff)
}
return syscall.Errno(hr)
}
51 changes: 51 additions & 0 deletions internal/interop/zsyscall_windows.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 40 additions & 21 deletions pkg/fs/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,9 @@ import (
// It is intended to address short-comings of [filepath.EvalSymlinks], which does not work
// well on Windows.
func ResolvePath(path string) (string, error) {
// We are not able to use builtin Go functionality for opening a directory path:
// - os.Open on a directory returns a os.File where Fd() is a search handle from FindFirstFile.
// - syscall.Open does not provide a way to specify FILE_FLAG_BACKUP_SEMANTICS, which is needed to
// open a directory.
//
// We could use os.Open if the path is a file, but it's easier to just use the same code for both.
// Therefore, we call windows.CreateFile directly.
h, err := fs.CreateFile(
path,
fs.FILE_ANY_ACCESS, // access
fs.FILE_SHARE_READ|fs.FILE_SHARE_WRITE|fs.FILE_SHARE_DELETE,
nil, // security attributes
fs.OPEN_EXISTING,
fs.FILE_FLAG_BACKUP_SEMANTICS, // Needed to open a directory handle.
fs.NullHandle, // template file
)
h, err := openPath(path)
if err != nil {
return "", &os.PathError{
Op: "CreateFile",
Path: path,
Err: err,
}
return "", err
}
defer windows.CloseHandle(h) //nolint:errcheck

Expand Down Expand Up @@ -126,3 +107,41 @@ func ResolvePath(path string) (string, error) {
}
return rPath, err
}

// openPath takes a path, opens it with only meta-data access, and returns the resulting handle.
// It works for both file and directory paths.
//
// We are not able to use builtin Go functionality for opening a directory path:
// - os.Open on a directory returns a os.File where Fd() is a search handle from FindFirstFile.
// - syscall.Open does not provide a way to specify FILE_FLAG_BACKUP_SEMANTICS, which is needed to
// open a directory.
//
// We could use os.Open if the path is a file, but it's easier to just use the same code for both.
// Therefore, we call windows.CreateFile directly.
func openPath(path string) (windows.Handle, error) {
// We are not able to use builtin Go functionality for opening a directory path:
// - os.Open on a directory returns a os.File where Fd() is a search handle from FindFirstFile.
// - syscall.Open does not provide a way to specify FILE_FLAG_BACKUP_SEMANTICS, which is needed to
// open a directory.
//
// We could use os.Open if the path is a file, but it's easier to just use the same code for both.
// Therefore, we call windows.CreateFile directly.
h, err := fs.CreateFile(
path,
fs.FILE_ANY_ACCESS,
fs.FILE_SHARE_READ|fs.FILE_SHARE_WRITE|fs.FILE_SHARE_DELETE,
nil, // security attributes
fs.OPEN_EXISTING,
fs.FILE_FLAG_BACKUP_SEMANTICS, // Needed to open a directory handle.
fs.NullHandle,
)

if err != nil {
return 0, &os.PathError{
Op: "CreateFile",
Path: path,
Err: err,
}
}
return h, nil
}
Loading

0 comments on commit 826c769

Please sign in to comment.