|
| 1 | +//go:build windows |
| 2 | + |
| 3 | +package fs |
| 4 | + |
| 5 | +import ( |
| 6 | + "golang.org/x/sys/windows" |
| 7 | + |
| 8 | + "github.com/Microsoft/go-winio/internal/stringbuffer" |
| 9 | +) |
| 10 | + |
| 11 | +//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go fs.go |
| 12 | + |
| 13 | +// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew |
| 14 | +//sys CreateFile(name string, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateFileW |
| 15 | + |
| 16 | +const NullHandle windows.Handle = 0 |
| 17 | + |
| 18 | +// AccessMask defines standard, specific, and generic rights. |
| 19 | +// |
| 20 | +// Bitmask: |
| 21 | +// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 |
| 22 | +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 |
| 23 | +// +---------------+---------------+-------------------------------+ |
| 24 | +// |G|G|G|G|Resvd|A| StandardRights| SpecificRights | |
| 25 | +// |R|W|E|A| |S| | | |
| 26 | +// +-+-------------+---------------+-------------------------------+ |
| 27 | +// |
| 28 | +// GR Generic Read |
| 29 | +// GW Generic Write |
| 30 | +// GE Generic Exectue |
| 31 | +// GA Generic All |
| 32 | +// Resvd Reserved |
| 33 | +// AS Access Security System |
| 34 | +// |
| 35 | +// https://learn.microsoft.com/en-us/windows/win32/secauthz/access-mask |
| 36 | +// |
| 37 | +// https://learn.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights |
| 38 | +// |
| 39 | +// https://learn.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants |
| 40 | +type AccessMask = windows.ACCESS_MASK |
| 41 | + |
| 42 | +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. |
| 43 | +const ( |
| 44 | + // Not actually any. |
| 45 | + // |
| 46 | + // For CreateFile: "query certain metadata such as file, directory, or device attributes without accessing that file or device" |
| 47 | + // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters |
| 48 | + FILE_ANY_ACCESS AccessMask = 0 |
| 49 | + |
| 50 | + // Specific Object Access |
| 51 | + // from ntioapi.h |
| 52 | + |
| 53 | + FILE_READ_DATA AccessMask = (0x0001) // file & pipe |
| 54 | + FILE_LIST_DIRECTORY AccessMask = (0x0001) // directory |
| 55 | + |
| 56 | + FILE_WRITE_DATA AccessMask = (0x0002) // file & pipe |
| 57 | + FILE_ADD_FILE AccessMask = (0x0002) // directory |
| 58 | + |
| 59 | + FILE_APPEND_DATA AccessMask = (0x0004) // file |
| 60 | + FILE_ADD_SUBDIRECTORY AccessMask = (0x0004) // directory |
| 61 | + FILE_CREATE_PIPE_INSTANCE AccessMask = (0x0004) // named pipe |
| 62 | + |
| 63 | + FILE_READ_EA AccessMask = (0x0008) // file & directory |
| 64 | + FILE_READ_PROPERTIES AccessMask = FILE_READ_EA |
| 65 | + |
| 66 | + FILE_WRITE_EA AccessMask = (0x0010) // file & directory |
| 67 | + FILE_WRITE_PROPERTIES AccessMask = FILE_WRITE_EA |
| 68 | + |
| 69 | + FILE_EXECUTE AccessMask = (0x0020) // file |
| 70 | + FILE_TRAVERSE AccessMask = (0x0020) // directory |
| 71 | + |
| 72 | + FILE_DELETE_CHILD AccessMask = (0x0040) // directory |
| 73 | + |
| 74 | + FILE_READ_ATTRIBUTES AccessMask = (0x0080) // all |
| 75 | + |
| 76 | + FILE_WRITE_ATTRIBUTES AccessMask = (0x0100) // all |
| 77 | + |
| 78 | + FILE_ALL_ACCESS AccessMask = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF) |
| 79 | + FILE_GENERIC_READ AccessMask = (STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE) |
| 80 | + FILE_GENERIC_WRITE AccessMask = (STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE) |
| 81 | + FILE_GENERIC_EXECUTE AccessMask = (STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE) |
| 82 | + |
| 83 | + SPECIFIC_RIGHTS_ALL AccessMask = 0x0000FFFF |
| 84 | + |
| 85 | + // Standard Access |
| 86 | + // from ntseapi.h |
| 87 | + |
| 88 | + DELETE AccessMask = 0x0001_0000 |
| 89 | + READ_CONTROL AccessMask = 0x0002_0000 |
| 90 | + WRITE_DAC AccessMask = 0x0004_0000 |
| 91 | + WRITE_OWNER AccessMask = 0x0008_0000 |
| 92 | + SYNCHRONIZE AccessMask = 0x0010_0000 |
| 93 | + |
| 94 | + STANDARD_RIGHTS_REQUIRED AccessMask = 0x000F_0000 |
| 95 | + |
| 96 | + STANDARD_RIGHTS_READ AccessMask = READ_CONTROL |
| 97 | + STANDARD_RIGHTS_WRITE AccessMask = READ_CONTROL |
| 98 | + STANDARD_RIGHTS_EXECUTE AccessMask = READ_CONTROL |
| 99 | + |
| 100 | + STANDARD_RIGHTS_ALL AccessMask = 0x001F_0000 |
| 101 | +) |
| 102 | + |
| 103 | +type FileShareMode uint32 |
| 104 | + |
| 105 | +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. |
| 106 | +const ( |
| 107 | + FILE_SHARE_NONE FileShareMode = 0x00 |
| 108 | + FILE_SHARE_READ FileShareMode = 0x01 |
| 109 | + FILE_SHARE_WRITE FileShareMode = 0x02 |
| 110 | + FILE_SHARE_DELETE FileShareMode = 0x04 |
| 111 | + FILE_SHARE_VALID_FLAGS FileShareMode = 0x07 |
| 112 | +) |
| 113 | + |
| 114 | +type FileCreationDisposition uint32 |
| 115 | + |
| 116 | +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. |
| 117 | +const ( |
| 118 | + // from winbase.h |
| 119 | + |
| 120 | + CREATE_NEW FileCreationDisposition = 0x01 |
| 121 | + CREATE_ALWAYS FileCreationDisposition = 0x02 |
| 122 | + OPEN_EXISTING FileCreationDisposition = 0x03 |
| 123 | + OPEN_ALWAYS FileCreationDisposition = 0x04 |
| 124 | + TRUNCATE_EXISTING FileCreationDisposition = 0x05 |
| 125 | +) |
| 126 | + |
| 127 | +// CreateFile and co. take flags or attributes together as one parameter. |
| 128 | +// Define alias until we can use generics to allow both |
| 129 | + |
| 130 | +// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants |
| 131 | +type FileFlagOrAttribute uint32 |
| 132 | + |
| 133 | +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. |
| 134 | +const ( // from winnt.h |
| 135 | + FILE_FLAG_WRITE_THROUGH FileFlagOrAttribute = 0x8000_0000 |
| 136 | + FILE_FLAG_OVERLAPPED FileFlagOrAttribute = 0x4000_0000 |
| 137 | + FILE_FLAG_NO_BUFFERING FileFlagOrAttribute = 0x2000_0000 |
| 138 | + FILE_FLAG_RANDOM_ACCESS FileFlagOrAttribute = 0x1000_0000 |
| 139 | + FILE_FLAG_SEQUENTIAL_SCAN FileFlagOrAttribute = 0x0800_0000 |
| 140 | + FILE_FLAG_DELETE_ON_CLOSE FileFlagOrAttribute = 0x0400_0000 |
| 141 | + FILE_FLAG_BACKUP_SEMANTICS FileFlagOrAttribute = 0x0200_0000 |
| 142 | + FILE_FLAG_POSIX_SEMANTICS FileFlagOrAttribute = 0x0100_0000 |
| 143 | + FILE_FLAG_OPEN_REPARSE_POINT FileFlagOrAttribute = 0x0020_0000 |
| 144 | + FILE_FLAG_OPEN_NO_RECALL FileFlagOrAttribute = 0x0010_0000 |
| 145 | + FILE_FLAG_FIRST_PIPE_INSTANCE FileFlagOrAttribute = 0x0008_0000 |
| 146 | +) |
| 147 | + |
| 148 | +type FileSQSFlag = FileFlagOrAttribute |
| 149 | + |
| 150 | +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. |
| 151 | +const ( // from winbase.h |
| 152 | + SECURITY_ANONYMOUS FileSQSFlag = FileSQSFlag(SecurityAnonymous << 16) |
| 153 | + SECURITY_IDENTIFICATION FileSQSFlag = FileSQSFlag(SecurityIdentification << 16) |
| 154 | + SECURITY_IMPERSONATION FileSQSFlag = FileSQSFlag(SecurityImpersonation << 16) |
| 155 | + SECURITY_DELEGATION FileSQSFlag = FileSQSFlag(SecurityDelegation << 16) |
| 156 | + |
| 157 | + SECURITY_SQOS_PRESENT FileSQSFlag = 0x00100000 |
| 158 | + SECURITY_VALID_SQOS_FLAGS FileSQSFlag = 0x001F0000 |
| 159 | +) |
| 160 | + |
| 161 | +// GetFinalPathNameByHandle flags |
| 162 | +// |
| 163 | +// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew#parameters |
| 164 | +type GetFinalPathFlag uint32 |
| 165 | + |
| 166 | +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. |
| 167 | +const ( |
| 168 | + GetFinalPathDefaultFlag GetFinalPathFlag = 0x0 |
| 169 | + |
| 170 | + FILE_NAME_NORMALIZED GetFinalPathFlag = 0x0 |
| 171 | + FILE_NAME_OPENED GetFinalPathFlag = 0x8 |
| 172 | + |
| 173 | + VOLUME_NAME_DOS GetFinalPathFlag = 0x0 |
| 174 | + VOLUME_NAME_GUID GetFinalPathFlag = 0x1 |
| 175 | + VOLUME_NAME_NT GetFinalPathFlag = 0x2 |
| 176 | + VOLUME_NAME_NONE GetFinalPathFlag = 0x4 |
| 177 | +) |
| 178 | + |
| 179 | +// getFinalPathNameByHandle facilitates calling the Windows API GetFinalPathNameByHandle |
| 180 | +// with the given handle and flags. It transparently takes care of creating a buffer of the |
| 181 | +// correct size for the call. |
| 182 | +// |
| 183 | +// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew |
| 184 | +func GetFinalPathNameByHandle(h windows.Handle, flags GetFinalPathFlag) (string, error) { |
| 185 | + b := stringbuffer.NewWString() |
| 186 | + //TODO: can loop infinitely if Win32 keeps returning the same (or a larger) n? |
| 187 | + for { |
| 188 | + n, err := windows.GetFinalPathNameByHandle(h, b.Pointer(), b.Cap(), uint32(flags)) |
| 189 | + if err != nil { |
| 190 | + return "", err |
| 191 | + } |
| 192 | + // If the buffer wasn't large enough, n will be the total size needed (including null terminator). |
| 193 | + // Resize and try again. |
| 194 | + if n > b.Cap() { |
| 195 | + b.ResizeTo(n) |
| 196 | + continue |
| 197 | + } |
| 198 | + // If the buffer is large enough, n will be the size not including the null terminator. |
| 199 | + // Convert to a Go string and return. |
| 200 | + return b.String(), nil |
| 201 | + } |
| 202 | +} |
0 commit comments