main
Raw Download raw file
  1//go:build windows
  2
  3package fs
  4
  5import (
  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 *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateFileW
 15
 16const NullHandle windows.Handle = 0
 17
 18// AccessMask defines standard, specific, and generic rights.
 19//
 20// Used with CreateFile and NtCreateFile (and co.).
 21//
 22//	Bitmask:
 23//	 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
 24//	 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
 25//	+---------------+---------------+-------------------------------+
 26//	|G|G|G|G|Resvd|A| StandardRights|         SpecificRights        |
 27//	|R|W|E|A|     |S|               |                               |
 28//	+-+-------------+---------------+-------------------------------+
 29//
 30//	GR     Generic Read
 31//	GW     Generic Write
 32//	GE     Generic Exectue
 33//	GA     Generic All
 34//	Resvd  Reserved
 35//	AS     Access Security System
 36//
 37// https://learn.microsoft.com/en-us/windows/win32/secauthz/access-mask
 38//
 39// https://learn.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights
 40//
 41// https://learn.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants
 42type AccessMask = windows.ACCESS_MASK
 43
 44//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
 45const (
 46	// Not actually any.
 47	//
 48	// For CreateFile: "query certain metadata such as file, directory, or device attributes without accessing that file or device"
 49	// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters
 50	FILE_ANY_ACCESS AccessMask = 0
 51
 52	GENERIC_READ           AccessMask = 0x8000_0000
 53	GENERIC_WRITE          AccessMask = 0x4000_0000
 54	GENERIC_EXECUTE        AccessMask = 0x2000_0000
 55	GENERIC_ALL            AccessMask = 0x1000_0000
 56	ACCESS_SYSTEM_SECURITY AccessMask = 0x0100_0000
 57
 58	// Specific Object Access
 59	// from ntioapi.h
 60
 61	FILE_READ_DATA      AccessMask = (0x0001) // file & pipe
 62	FILE_LIST_DIRECTORY AccessMask = (0x0001) // directory
 63
 64	FILE_WRITE_DATA AccessMask = (0x0002) // file & pipe
 65	FILE_ADD_FILE   AccessMask = (0x0002) // directory
 66
 67	FILE_APPEND_DATA          AccessMask = (0x0004) // file
 68	FILE_ADD_SUBDIRECTORY     AccessMask = (0x0004) // directory
 69	FILE_CREATE_PIPE_INSTANCE AccessMask = (0x0004) // named pipe
 70
 71	FILE_READ_EA         AccessMask = (0x0008) // file & directory
 72	FILE_READ_PROPERTIES AccessMask = FILE_READ_EA
 73
 74	FILE_WRITE_EA         AccessMask = (0x0010) // file & directory
 75	FILE_WRITE_PROPERTIES AccessMask = FILE_WRITE_EA
 76
 77	FILE_EXECUTE  AccessMask = (0x0020) // file
 78	FILE_TRAVERSE AccessMask = (0x0020) // directory
 79
 80	FILE_DELETE_CHILD AccessMask = (0x0040) // directory
 81
 82	FILE_READ_ATTRIBUTES AccessMask = (0x0080) // all
 83
 84	FILE_WRITE_ATTRIBUTES AccessMask = (0x0100) // all
 85
 86	FILE_ALL_ACCESS      AccessMask = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)
 87	FILE_GENERIC_READ    AccessMask = (STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE)
 88	FILE_GENERIC_WRITE   AccessMask = (STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE)
 89	FILE_GENERIC_EXECUTE AccessMask = (STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE)
 90
 91	SPECIFIC_RIGHTS_ALL AccessMask = 0x0000FFFF
 92
 93	// Standard Access
 94	// from ntseapi.h
 95
 96	DELETE       AccessMask = 0x0001_0000
 97	READ_CONTROL AccessMask = 0x0002_0000
 98	WRITE_DAC    AccessMask = 0x0004_0000
 99	WRITE_OWNER  AccessMask = 0x0008_0000
100	SYNCHRONIZE  AccessMask = 0x0010_0000
101
102	STANDARD_RIGHTS_REQUIRED AccessMask = 0x000F_0000
103
104	STANDARD_RIGHTS_READ    AccessMask = READ_CONTROL
105	STANDARD_RIGHTS_WRITE   AccessMask = READ_CONTROL
106	STANDARD_RIGHTS_EXECUTE AccessMask = READ_CONTROL
107
108	STANDARD_RIGHTS_ALL AccessMask = 0x001F_0000
109)
110
111type FileShareMode uint32
112
113//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
114const (
115	FILE_SHARE_NONE        FileShareMode = 0x00
116	FILE_SHARE_READ        FileShareMode = 0x01
117	FILE_SHARE_WRITE       FileShareMode = 0x02
118	FILE_SHARE_DELETE      FileShareMode = 0x04
119	FILE_SHARE_VALID_FLAGS FileShareMode = 0x07
120)
121
122type FileCreationDisposition uint32
123
124//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
125const (
126	// from winbase.h
127
128	CREATE_NEW        FileCreationDisposition = 0x01
129	CREATE_ALWAYS     FileCreationDisposition = 0x02
130	OPEN_EXISTING     FileCreationDisposition = 0x03
131	OPEN_ALWAYS       FileCreationDisposition = 0x04
132	TRUNCATE_EXISTING FileCreationDisposition = 0x05
133)
134
135// Create disposition values for NtCreate*
136type NTFileCreationDisposition uint32
137
138//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
139const (
140	// From ntioapi.h
141
142	FILE_SUPERSEDE           NTFileCreationDisposition = 0x00
143	FILE_OPEN                NTFileCreationDisposition = 0x01
144	FILE_CREATE              NTFileCreationDisposition = 0x02
145	FILE_OPEN_IF             NTFileCreationDisposition = 0x03
146	FILE_OVERWRITE           NTFileCreationDisposition = 0x04
147	FILE_OVERWRITE_IF        NTFileCreationDisposition = 0x05
148	FILE_MAXIMUM_DISPOSITION NTFileCreationDisposition = 0x05
149)
150
151// CreateFile and co. take flags or attributes together as one parameter.
152// Define alias until we can use generics to allow both
153//
154// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
155type FileFlagOrAttribute uint32
156
157//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
158const (
159	// from winnt.h
160
161	FILE_FLAG_WRITE_THROUGH       FileFlagOrAttribute = 0x8000_0000
162	FILE_FLAG_OVERLAPPED          FileFlagOrAttribute = 0x4000_0000
163	FILE_FLAG_NO_BUFFERING        FileFlagOrAttribute = 0x2000_0000
164	FILE_FLAG_RANDOM_ACCESS       FileFlagOrAttribute = 0x1000_0000
165	FILE_FLAG_SEQUENTIAL_SCAN     FileFlagOrAttribute = 0x0800_0000
166	FILE_FLAG_DELETE_ON_CLOSE     FileFlagOrAttribute = 0x0400_0000
167	FILE_FLAG_BACKUP_SEMANTICS    FileFlagOrAttribute = 0x0200_0000
168	FILE_FLAG_POSIX_SEMANTICS     FileFlagOrAttribute = 0x0100_0000
169	FILE_FLAG_OPEN_REPARSE_POINT  FileFlagOrAttribute = 0x0020_0000
170	FILE_FLAG_OPEN_NO_RECALL      FileFlagOrAttribute = 0x0010_0000
171	FILE_FLAG_FIRST_PIPE_INSTANCE FileFlagOrAttribute = 0x0008_0000
172)
173
174// NtCreate* functions take a dedicated CreateOptions parameter.
175//
176// https://learn.microsoft.com/en-us/windows/win32/api/Winternl/nf-winternl-ntcreatefile
177//
178// https://learn.microsoft.com/en-us/windows/win32/devnotes/nt-create-named-pipe-file
179type NTCreateOptions uint32
180
181//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
182const (
183	// From ntioapi.h
184
185	FILE_DIRECTORY_FILE            NTCreateOptions = 0x0000_0001
186	FILE_WRITE_THROUGH             NTCreateOptions = 0x0000_0002
187	FILE_SEQUENTIAL_ONLY           NTCreateOptions = 0x0000_0004
188	FILE_NO_INTERMEDIATE_BUFFERING NTCreateOptions = 0x0000_0008
189
190	FILE_SYNCHRONOUS_IO_ALERT    NTCreateOptions = 0x0000_0010
191	FILE_SYNCHRONOUS_IO_NONALERT NTCreateOptions = 0x0000_0020
192	FILE_NON_DIRECTORY_FILE      NTCreateOptions = 0x0000_0040
193	FILE_CREATE_TREE_CONNECTION  NTCreateOptions = 0x0000_0080
194
195	FILE_COMPLETE_IF_OPLOCKED NTCreateOptions = 0x0000_0100
196	FILE_NO_EA_KNOWLEDGE      NTCreateOptions = 0x0000_0200
197	FILE_DISABLE_TUNNELING    NTCreateOptions = 0x0000_0400
198	FILE_RANDOM_ACCESS        NTCreateOptions = 0x0000_0800
199
200	FILE_DELETE_ON_CLOSE        NTCreateOptions = 0x0000_1000
201	FILE_OPEN_BY_FILE_ID        NTCreateOptions = 0x0000_2000
202	FILE_OPEN_FOR_BACKUP_INTENT NTCreateOptions = 0x0000_4000
203	FILE_NO_COMPRESSION         NTCreateOptions = 0x0000_8000
204)
205
206type FileSQSFlag = FileFlagOrAttribute
207
208//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
209const (
210	// from winbase.h
211
212	SECURITY_ANONYMOUS      FileSQSFlag = FileSQSFlag(SecurityAnonymous << 16)
213	SECURITY_IDENTIFICATION FileSQSFlag = FileSQSFlag(SecurityIdentification << 16)
214	SECURITY_IMPERSONATION  FileSQSFlag = FileSQSFlag(SecurityImpersonation << 16)
215	SECURITY_DELEGATION     FileSQSFlag = FileSQSFlag(SecurityDelegation << 16)
216
217	SECURITY_SQOS_PRESENT     FileSQSFlag = 0x0010_0000
218	SECURITY_VALID_SQOS_FLAGS FileSQSFlag = 0x001F_0000
219)
220
221// GetFinalPathNameByHandle flags
222//
223// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew#parameters
224type GetFinalPathFlag uint32
225
226//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
227const (
228	GetFinalPathDefaultFlag GetFinalPathFlag = 0x0
229
230	FILE_NAME_NORMALIZED GetFinalPathFlag = 0x0
231	FILE_NAME_OPENED     GetFinalPathFlag = 0x8
232
233	VOLUME_NAME_DOS  GetFinalPathFlag = 0x0
234	VOLUME_NAME_GUID GetFinalPathFlag = 0x1
235	VOLUME_NAME_NT   GetFinalPathFlag = 0x2
236	VOLUME_NAME_NONE GetFinalPathFlag = 0x4
237)
238
239// getFinalPathNameByHandle facilitates calling the Windows API GetFinalPathNameByHandle
240// with the given handle and flags. It transparently takes care of creating a buffer of the
241// correct size for the call.
242//
243// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew
244func GetFinalPathNameByHandle(h windows.Handle, flags GetFinalPathFlag) (string, error) {
245	b := stringbuffer.NewWString()
246	//TODO: can loop infinitely if Win32 keeps returning the same (or a larger) n?
247	for {
248		n, err := windows.GetFinalPathNameByHandle(h, b.Pointer(), b.Cap(), uint32(flags))
249		if err != nil {
250			return "", err
251		}
252		// If the buffer wasn't large enough, n will be the total size needed (including null terminator).
253		// Resize and try again.
254		if n > b.Cap() {
255			b.ResizeTo(n)
256			continue
257		}
258		// If the buffer is large enough, n will be the size not including the null terminator.
259		// Convert to a Go string and return.
260		return b.String(), nil
261	}
262}