Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ResolvePath tests #276

Merged
merged 1 commit into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions internal/computestorage/computestorage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//go:build windows

package computestorage

import (
"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(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(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.

53 changes: 32 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 := openMetadata(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,33 @@ func ResolvePath(path string) (string, error) {
}
return rPath, err
}

// openMetadata takes a path, opens it with only meta-data access, and returns the resulting handle.
// It works for both file and directory paths.
func openMetadata(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