github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/dokan/cgo.go (about) 1 // Copyright 2015-2018 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 //go:build windows 6 // +build windows 7 8 package dokan 9 10 /* 11 #include "bridge.h" 12 */ 13 import "C" 14 15 import ( 16 "context" 17 "errors" 18 "fmt" 19 "io" 20 "reflect" 21 "runtime" 22 "syscall" 23 "time" 24 "unicode/utf16" 25 "unsafe" 26 27 "github.com/keybase/client/go/kbfs/dokan/winacl" 28 "golang.org/x/sys/windows" 29 ) 30 31 // SID wraps syscall.SID for users. 32 type SID syscall.SID 33 34 const ( 35 kbfsLibdokanDebug = MountFlag(C.kbfsLibdokanDebug) 36 kbfsLibdokanStderr = MountFlag(C.kbfsLibdokanStderr) 37 kbfsLibdokanRemovable = MountFlag(C.kbfsLibdokanRemovable) 38 kbfsLibdokanMountManager = MountFlag(C.kbfsLibdokanMountManager) 39 kbfsLibdokanCurrentSession = MountFlag(C.kbfsLibdokanCurrentSession) 40 kbfsLibdokanUseFindFilesWithPattern = MountFlag(C.kbfsLibdokanUseFindFilesWithPattern) 41 ) 42 43 const ntstatusOk = C.NTSTATUS(0) 44 45 func checkFileDirectoryFile(err error, isDir bool, createOptions uint32) { 46 if createOptions&FileDirectoryFile != 0 && createOptions&FileNonDirectoryFile != 0 { 47 debugf("checkFileDirectoryFile both FileDirectoryFile FileNonDirectoryFile set") 48 } 49 switch { 50 case err == nil: 51 if (!isDir && createOptions&FileDirectoryFile != 0) || 52 (isDir && createOptions&FileNonDirectoryFile != 0) { 53 debugf("checkFileDirectoryFile INCONSISTENCY %v %08X", isDir, createOptions) 54 } 55 case err == ErrNotADirectory: 56 if createOptions&FileDirectoryFile == 0 { 57 debugf("checkFileDirectoryFile ErrNotADirectory but no createOptions&FileDirectoryFile") 58 } 59 case err == ErrFileIsADirectory: 60 if createOptions&FileNonDirectoryFile == 0 { 61 debugf("checkFileDirectoryFile ErrFileIsADirectory but no createOptions&FileNonDirectoryFile") 62 } 63 } 64 } 65 66 //export kbfsLibdokanCreateFile 67 func kbfsLibdokanCreateFile( 68 fname C.LPCWSTR, 69 psec C.PDOKAN_IO_SECURITY_CONTEXT, 70 DesiredAccess C.ACCESS_MASK, //nolint 71 FileAttributes C.ULONG, //nolint 72 ShareAccess C.ULONG, //nolint 73 cCreateDisposition C.ULONG, 74 CreateOptions C.ULONG, //nolint 75 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 76 var cd = CreateData{ 77 DesiredAccess: uint32(DesiredAccess), 78 FileAttributes: FileAttribute(FileAttributes), 79 ShareAccess: uint32(ShareAccess), 80 CreateDisposition: CreateDisposition(cCreateDisposition), 81 CreateOptions: uint32(CreateOptions), 82 } 83 debugf("CreateFile '%v' %#v pid: %v\n", 84 d16{fname}, cd, pfi.ProcessId) 85 fs := getfs(pfi) 86 ctx, cancel := fs.WithContext(globalContext()) 87 if cancel != nil { 88 defer cancel() 89 } 90 fi, status, err := fs.CreateFile(ctx, makeFI(fname, pfi), &cd) 91 if isDebug { 92 checkFileDirectoryFile(err, status.IsDir(), uint32(CreateOptions)) 93 debugf("CreateFile result: %v new-entry: %v raw %v", status.IsDir(), status.IsNew(), status) 94 if err == nil && status&isValid == 0 { 95 debugf("CreateFile invalid status for successful operation!") 96 } 97 } 98 if status.IsDir() { 99 pfi.IsDirectory = 1 100 } 101 if err == nil && !status.IsNew() && cd.CreateDisposition.isSignalExisting() { 102 debugf("CreateFile adding ErrObjectNameCollision") 103 err = ErrObjectNameCollision 104 } 105 return fiStore(pfi, fi, err) 106 } 107 108 func (cd CreateDisposition) isSignalExisting() bool { 109 return cd == FileOpenIf || cd == FileSupersede || cd == FileOverwriteIf 110 } 111 112 func globalContext() context.Context { 113 return context.Background() 114 } 115 func getContext(pfi C.PDOKAN_FILE_INFO) (context.Context, context.CancelFunc) { 116 return getfs(pfi).WithContext(globalContext()) 117 } 118 119 //export kbfsLibdokanCleanup 120 func kbfsLibdokanCleanup(fname C.LPCWSTR, pfi C.PDOKAN_FILE_INFO) { 121 debugf("Cleanup '%v' %v\n", d16{fname}, *pfi) 122 ctx, cancel := getContext(pfi) 123 if cancel != nil { 124 defer cancel() 125 } 126 getfi(pfi).Cleanup(ctx, makeFI(fname, pfi)) 127 } 128 129 //export kbfsLibdokanCloseFile 130 func kbfsLibdokanCloseFile(fname C.LPCWSTR, pfi C.PDOKAN_FILE_INFO) { 131 debugf("CloseFile '%v' %v\n", d16{fname}, *pfi) 132 ctx, cancel := getContext(pfi) 133 if cancel != nil { 134 defer cancel() 135 } 136 getfi(pfi).CloseFile(ctx, makeFI(fname, pfi)) 137 fiTableFreeFile(uint32(pfi.DokanOptions.GlobalContext), uint32(pfi.Context)) 138 pfi.Context = 0 139 } 140 141 //export kbfsLibdokanReadFile 142 func kbfsLibdokanReadFile( 143 fname C.LPCWSTR, 144 Buffer C.LPVOID, //nolint 145 NumberOfBytesToRead C.DWORD, //nolint 146 NumberOfBytesRead C.LPDWORD, //nolint 147 Offset C.LONGLONG, //nolint 148 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 149 debugf("ReadFile '%v' %d bytes @ %d %v", d16{fname}, NumberOfBytesToRead, Offset, *pfi) 150 ctx, cancel := getContext(pfi) 151 if cancel != nil { 152 defer cancel() 153 } 154 n, err := getfi(pfi).ReadFile( 155 ctx, 156 makeFI(fname, pfi), 157 bufToSlice(unsafe.Pointer(Buffer), uint32(NumberOfBytesToRead)), 158 int64(Offset)) 159 *NumberOfBytesRead = C.DWORD(n) 160 // EOF is success with Windows... 161 if err == io.EOF { 162 err = nil 163 } 164 debug("->", n, err) 165 return errToNT(err) 166 } 167 168 //export kbfsLibdokanWriteFile 169 func kbfsLibdokanWriteFile( 170 fname C.LPCWSTR, 171 Buffer C.LPCVOID, //nolint 172 NumberOfBytesToWrite C.DWORD, //nolint 173 NumberOfBytesWritten C.LPDWORD, //nolint 174 Offset C.LONGLONG, //nolint 175 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 176 debugf("WriteFile '%v' %d bytes @ %d %v", d16{fname}, NumberOfBytesToWrite, Offset, *pfi) 177 ctx, cancel := getContext(pfi) 178 if cancel != nil { 179 defer cancel() 180 } 181 n, err := getfi(pfi).WriteFile( 182 ctx, 183 makeFI(fname, pfi), 184 bufToSlice(unsafe.Pointer(Buffer), uint32(NumberOfBytesToWrite)), 185 int64(Offset)) 186 *NumberOfBytesWritten = C.DWORD(n) 187 debug("->", n, err) 188 return errToNT(err) 189 } 190 191 //export kbfsLibdokanFlushFileBuffers 192 func kbfsLibdokanFlushFileBuffers( 193 fname C.LPCWSTR, 194 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 195 debugf("FlushFileBuffers '%v' %v", d16{fname}, *pfi) 196 ctx, cancel := getContext(pfi) 197 if cancel != nil { 198 defer cancel() 199 } 200 err := getfi(pfi).FlushFileBuffers(ctx, makeFI(fname, pfi)) 201 return errToNT(err) 202 } 203 204 func u32zeroToOne(u uint32) uint32 { 205 if u == 0 { 206 return 1 207 } 208 return u 209 } 210 211 //export kbfsLibdokanGetFileInformation 212 func kbfsLibdokanGetFileInformation( 213 fname C.LPCWSTR, 214 sbuf C.LPBY_HANDLE_FILE_INFORMATION, 215 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 216 debugf("GetFileInformation '%v' %v", d16{fname}, *pfi) 217 ctx, cancel := getContext(pfi) 218 if cancel != nil { 219 defer cancel() 220 } 221 st, err := getfi(pfi).GetFileInformation(ctx, makeFI(fname, pfi)) 222 debugf("-> %#v, %v", st, err) 223 if st != nil { 224 sbuf.dwFileAttributes = C.DWORD(st.FileAttributes) 225 sbuf.ftCreationTime = packTime(st.Creation) 226 sbuf.ftLastAccessTime = packTime(st.LastAccess) 227 sbuf.ftLastWriteTime = packTime(st.LastWrite) 228 sbuf.dwVolumeSerialNumber = C.DWORD(st.VolumeSerialNumber) 229 sbuf.nFileSizeHigh = C.DWORD(st.FileSize >> 32) 230 sbuf.nFileSizeLow = C.DWORD(st.FileSize) 231 sbuf.nNumberOfLinks = C.DWORD(u32zeroToOne(st.NumberOfLinks)) 232 sbuf.nFileIndexHigh = C.DWORD(st.FileIndex >> 32) 233 sbuf.nFileIndexLow = C.DWORD(st.FileIndex) 234 } 235 return errToNT(err) 236 } 237 238 var errFindNoSpace = errors.New("Find out of space") 239 240 //export kbfsLibdokanFindFiles 241 func kbfsLibdokanFindFiles( 242 PathName C.LPCWSTR, //nolint 243 FindData C.PFillFindData, //nolint call this function with PWIN32_FIND_DATAW 244 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 245 debugf("FindFiles '%v' %v", d16{PathName}, *pfi) 246 return kbfsLibdokanFindFilesImpl(PathName, "", FindData, pfi) 247 } 248 249 //export kbfsLibdokanFindFilesWithPattern 250 func kbfsLibdokanFindFilesWithPattern( 251 PathName C.LPCWSTR, //nolint 252 SearchPattern C.LPCWSTR, //nolint 253 FindData C.PFillFindData, //nolint call this function with PWIN32_FIND_DATAW 254 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 255 pattern := lpcwstrToString(SearchPattern) 256 debugf("FindFilesWithPattern '%v' %v %q", d16{PathName}, *pfi, pattern) 257 return kbfsLibdokanFindFilesImpl(PathName, pattern, FindData, pfi) 258 } 259 260 func kbfsLibdokanFindFilesImpl( 261 PathName C.LPCWSTR, //nolint 262 pattern string, 263 FindData C.PFillFindData, //nolint 264 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 265 debugf("FindFiles '%v' %v", d16{PathName}, *pfi) 266 ctx, cancel := getContext(pfi) 267 if cancel != nil { 268 defer cancel() 269 } 270 var fdata C.WIN32_FIND_DATAW 271 fun := func(ns *NamedStat) error { 272 fdata.dwFileAttributes = C.DWORD(ns.FileAttributes) 273 fdata.ftCreationTime = packTime(ns.Creation) 274 fdata.ftLastAccessTime = packTime(ns.LastAccess) 275 fdata.ftLastWriteTime = packTime(ns.LastWrite) 276 fdata.nFileSizeHigh = C.DWORD(ns.FileSize >> 32) 277 fdata.nFileSizeLow = C.DWORD(ns.FileSize) 278 fdata.dwReserved0 = C.DWORD(ns.ReparsePointTag) 279 stringToUtf16BufferPtr(ns.Name, 280 unsafe.Pointer(&fdata.cFileName), 281 C.DWORD(C.MAX_PATH)) 282 if ns.ShortName != "" { 283 stringToUtf16BufferPtr(ns.ShortName, 284 unsafe.Pointer(&fdata.cFileName), 285 C.DWORD(14)) 286 } 287 288 v := C.kbfsLibdokanFill_find(FindData, &fdata, pfi) 289 if v != 0 { 290 return errFindNoSpace 291 } 292 return nil 293 } 294 err := getfi(pfi).FindFiles(ctx, makeFI(PathName, pfi), pattern, fun) 295 return errToNT(err) 296 } 297 298 //export kbfsLibdokanSetFileAttributes 299 func kbfsLibdokanSetFileAttributes( 300 fname C.LPCWSTR, 301 fileAttributes C.DWORD, 302 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 303 debugf("SetFileAttributes '%v' %X %v", d16{fname}, fileAttributes, pfi) 304 ctx, cancel := getContext(pfi) 305 if cancel != nil { 306 defer cancel() 307 } 308 err := getfi(pfi).SetFileAttributes(ctx, makeFI(fname, pfi), FileAttribute(fileAttributes)) 309 return errToNT(err) 310 } 311 312 //export kbfsLibdokanSetFileTime 313 func kbfsLibdokanSetFileTime( 314 fname C.LPCWSTR, 315 creation *C.FILETIME, 316 lastAccess *C.FILETIME, 317 lastWrite *C.FILETIME, 318 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 319 debugf("SetFileTime '%v' %v", d16{fname}, *pfi) 320 ctx, cancel := getContext(pfi) 321 if cancel != nil { 322 defer cancel() 323 } 324 var t0, t1, t2 time.Time 325 if creation != nil { 326 t0 = unpackTime(*creation) 327 } 328 if lastAccess != nil { 329 t1 = unpackTime(*lastAccess) 330 } 331 if lastWrite != nil { 332 t2 = unpackTime(*lastWrite) 333 } 334 err := getfi(pfi).SetFileTime(ctx, makeFI(fname, pfi), t0, t1, t2) 335 return errToNT(err) 336 } 337 338 //export kbfsLibdokanDeleteFile 339 func kbfsLibdokanDeleteFile( 340 fname C.LPCWSTR, 341 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 342 debugf("DeleteFile '%v' %v", d16{fname}, *pfi) 343 ctx, cancel := getContext(pfi) 344 if cancel != nil { 345 defer cancel() 346 } 347 err := getfi(pfi).CanDeleteFile(ctx, makeFI(fname, pfi)) 348 return errToNT(err) 349 } 350 351 //export kbfsLibdokanDeleteDirectory 352 func kbfsLibdokanDeleteDirectory( 353 fname C.LPCWSTR, 354 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 355 debugf("DeleteDirectory '%v' %v", d16{fname}, *pfi) 356 ctx, cancel := getContext(pfi) 357 if cancel != nil { 358 defer cancel() 359 } 360 err := getfi(pfi).CanDeleteDirectory(ctx, makeFI(fname, pfi)) 361 return errToNT(err) 362 } 363 364 //export kbfsLibdokanMoveFile 365 func kbfsLibdokanMoveFile( 366 oldFName C.LPCWSTR, 367 newFName C.LPCWSTR, 368 replaceExisiting C.BOOL, 369 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 370 newPath := lpcwstrToString(newFName) 371 debugf("MoveFile '%v' %v target %v", d16{oldFName}, *pfi, newPath) 372 ctx, cancel := getContext(pfi) 373 if cancel != nil { 374 defer cancel() 375 } 376 // On error nil, not a dummy file like in getfi. 377 file := fiTableGetFile(uint32(pfi.Context)) 378 err := getfs(pfi).MoveFile(ctx, file, makeFI(oldFName, pfi), newPath, bool(replaceExisiting != 0)) 379 return errToNT(err) 380 } 381 382 //export kbfsLibdokanSetEndOfFile 383 func kbfsLibdokanSetEndOfFile( 384 fname C.LPCWSTR, 385 length C.LONGLONG, 386 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 387 debugf("SetEndOfFile '%v' %d %v", d16{fname}, length, *pfi) 388 ctx, cancel := getContext(pfi) 389 if cancel != nil { 390 defer cancel() 391 } 392 err := getfi(pfi).SetEndOfFile(ctx, makeFI(fname, pfi), int64(length)) 393 return errToNT(err) 394 } 395 396 //export kbfsLibdokanSetAllocationSize 397 func kbfsLibdokanSetAllocationSize( 398 fname C.LPCWSTR, 399 length C.LONGLONG, 400 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 401 debugf("SetAllocationSize '%v' %d %v", d16{fname}, length, *pfi) 402 ctx, cancel := getContext(pfi) 403 if cancel != nil { 404 defer cancel() 405 } 406 err := getfi(pfi).SetAllocationSize(ctx, makeFI(fname, pfi), int64(length)) 407 return errToNT(err) 408 } 409 410 //export kbfsLibdokanLockFile 411 func kbfsLibdokanLockFile( 412 fname C.LPCWSTR, 413 offset C.LONGLONG, 414 length C.LONGLONG, 415 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 416 debugf("LockFile '%v' %v", d16{fname}, *pfi) 417 ctx, cancel := getContext(pfi) 418 if cancel != nil { 419 defer cancel() 420 } 421 422 err := getfi(pfi).LockFile(ctx, makeFI(fname, pfi), int64(offset), int64(length)) 423 return errToNT(err) 424 } 425 426 //export kbfsLibdokanUnlockFile 427 func kbfsLibdokanUnlockFile( 428 fname C.LPCWSTR, 429 offset C.LONGLONG, 430 length C.LONGLONG, 431 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 432 debugf("UnlockFile '%v' %v", d16{fname}, *pfi) 433 ctx, cancel := getContext(pfi) 434 if cancel != nil { 435 defer cancel() 436 } 437 err := getfi(pfi).UnlockFile(ctx, makeFI(fname, pfi), int64(offset), int64(length)) 438 return errToNT(err) 439 } 440 441 //export kbfsLibdokanGetDiskFreeSpace 442 func kbfsLibdokanGetDiskFreeSpace( 443 FreeBytesAvailable *C.ULONGLONG, //nolint 444 TotalNumberOfBytes *C.ULONGLONG, //nolint 445 TotalNumberOfFreeBytes *C.ULONGLONG, //nolint 446 FileInfo C.PDOKAN_FILE_INFO) C.NTSTATUS { //nolint 447 debug("GetDiskFreeSpace", *FileInfo) 448 fs := getfs(FileInfo) 449 ctx, cancel := fs.WithContext(globalContext()) 450 if cancel != nil { 451 defer cancel() 452 } 453 space, err := fs.GetDiskFreeSpace(ctx) 454 debug("->", space, err) 455 if err != nil { 456 return errToNT(err) 457 } 458 if FreeBytesAvailable != nil { 459 *FreeBytesAvailable = C.ULONGLONG(space.FreeBytesAvailable) 460 } 461 if TotalNumberOfBytes != nil { 462 *TotalNumberOfBytes = C.ULONGLONG(space.TotalNumberOfBytes) 463 } 464 if TotalNumberOfFreeBytes != nil { 465 *TotalNumberOfFreeBytes = C.ULONGLONG(space.TotalNumberOfFreeBytes) 466 } 467 return ntstatusOk 468 } 469 470 //export kbfsLibdokanGetVolumeInformation 471 func kbfsLibdokanGetVolumeInformation( 472 VolumeNameBuffer C.LPWSTR, //nolint 473 VolumeNameSize C.DWORD, //nolint in num of chars 474 VolumeSerialNumber C.LPDWORD, //nolint 475 MaximumComponentLength C.LPDWORD, //nolint in num of chars 476 FileSystemFlags C.LPDWORD, //nolint 477 FileSystemNameBuffer C.LPWSTR, //nolint 478 FileSystemNameSize C.DWORD, //nolint in num of chars 479 FileInfo C.PDOKAN_FILE_INFO) C.NTSTATUS { //nolint 480 debug("GetVolumeInformation", VolumeNameSize, MaximumComponentLength, FileSystemNameSize, *FileInfo) 481 fs := getfs(FileInfo) 482 ctx, cancel := fs.WithContext(globalContext()) 483 if cancel != nil { 484 defer cancel() 485 } 486 vi, err := fs.GetVolumeInformation(ctx) 487 debug("->", vi, err) 488 if err != nil { 489 return errToNT(err) 490 } 491 if VolumeNameBuffer != nil { 492 stringToUtf16Buffer(vi.VolumeName, VolumeNameBuffer, VolumeNameSize) 493 } 494 if VolumeSerialNumber != nil { 495 *VolumeSerialNumber = C.DWORD(vi.VolumeSerialNumber) 496 } 497 if MaximumComponentLength != nil { 498 *MaximumComponentLength = C.DWORD(vi.MaximumComponentLength) 499 } 500 if FileSystemFlags != nil { 501 *FileSystemFlags = C.DWORD(vi.FileSystemFlags) 502 } 503 if FileSystemNameBuffer != nil { 504 stringToUtf16Buffer(vi.FileSystemName, FileSystemNameBuffer, FileSystemNameSize) 505 } 506 507 return ntstatusOk 508 } 509 510 //export kbfsLibdokanMounted 511 func kbfsLibdokanMounted(pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 512 debug("Mounted") 513 // Signal that the filesystem is mounted and can be used. 514 fsTableGetErrChan(uint32(pfi.DokanOptions.GlobalContext)) <- nil 515 // Dokan wants a NTSTATUS here, but is discarding it. 516 return ntstatusOk 517 } 518 519 //export kbfsLibdokanGetFileSecurity 520 func kbfsLibdokanGetFileSecurity( 521 fname C.LPCWSTR, 522 //A pointer to SECURITY_INFORMATION value being requested 523 input C.PSECURITY_INFORMATION, 524 // A pointer to SECURITY_DESCRIPTOR buffer to be filled 525 output C.PSECURITY_DESCRIPTOR, 526 outlen C.ULONG, // length of Security descriptor buffer 527 LengthNeeded *C.ULONG, //nolint 528 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 529 var si winacl.SecurityInformation 530 if input != nil { 531 si = winacl.SecurityInformation(*input) 532 } 533 debug("GetFileSecurity", si) 534 ctx, cancel := getContext(pfi) 535 if cancel != nil { 536 defer cancel() 537 } 538 buf := bufToSlice(unsafe.Pointer(output), uint32(outlen)) 539 sd := winacl.NewSecurityDescriptorWithBuffer( 540 buf) 541 err := getfi(pfi).GetFileSecurity(ctx, makeFI(fname, pfi), si, sd) 542 if err != nil { 543 return errToNT(err) 544 } 545 if LengthNeeded != nil { 546 *LengthNeeded = C.ULONG(sd.Size()) 547 } 548 if sd.HasOverflowed() { 549 debug("Too small buffer", outlen, "would have needed", sd.Size()) 550 return errToNT(StatusBufferOverflow) 551 } 552 debugf("%X", buf) 553 debug("-> ok,", sd.Size(), "bytes") 554 return ntstatusOk 555 } 556 557 //export kbfsLibdokanSetFileSecurity 558 func kbfsLibdokanSetFileSecurity( 559 fname C.LPCWSTR, 560 SecurityInformation C.PSECURITY_INFORMATION, //nolint 561 SecurityDescriptor C.PSECURITY_DESCRIPTOR, //nolint 562 SecurityDescriptorLength C.ULONG, //nolint 563 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 564 debug("SetFileSecurity TODO") 565 return ntstatusOk 566 } 567 568 /* FIXME add support for multiple streams per file? 569 //export kbfsLibdokanFindStreams 570 func kbfsLibdokanFindStreams ( 571 fname C.LPCWSTR, 572 // call this function with PWIN32_FIND_STREAM_DATA 573 FindStreamData uintptr, 574 pfi C.PDOKAN_FILE_INFO) C.NTSTATUS { 575 576 577 } 578 */ 579 580 // FileInfo contains information about a file including the path. 581 type FileInfo struct { 582 ptr C.PDOKAN_FILE_INFO 583 rawPath C.LPCWSTR 584 } 585 586 func makeFI(fname C.LPCWSTR, pfi C.PDOKAN_FILE_INFO) *FileInfo { 587 return &FileInfo{pfi, fname} 588 } 589 590 func packTime(t time.Time) C.FILETIME { 591 if t.IsZero() { 592 return C.FILETIME{} 593 } 594 ft := syscall.NsecToFiletime(t.UnixNano()) 595 return C.FILETIME{dwLowDateTime: C.DWORD(ft.LowDateTime), dwHighDateTime: C.DWORD(ft.HighDateTime)} 596 } 597 func unpackTime(c C.FILETIME) time.Time { 598 // Zero means ignore. Sometimes -1 is passed for ignore too. 599 if c == (C.FILETIME{}) || c == (C.FILETIME{0xFFFFffff, 0xFFFFffff}) { 600 return time.Time{} 601 } 602 ft := syscall.Filetime{LowDateTime: uint32(c.dwLowDateTime), HighDateTime: uint32(c.dwHighDateTime)} 603 // This is valid, see docs and code for package time. 604 return time.Unix(0, ft.Nanoseconds()) 605 } 606 607 func getfs(fi C.PDOKAN_FILE_INFO) FileSystem { 608 return fsTableGet(uint32(fi.DokanOptions.GlobalContext)) 609 } 610 611 func getfi(fi C.PDOKAN_FILE_INFO) File { 612 file := fiTableGetFile(uint32(fi.Context)) 613 if file == nil { 614 file = &errorFile{getfs(fi)} 615 } 616 return file 617 } 618 619 func fiStore(pfi C.PDOKAN_FILE_INFO, fi File, err error) C.NTSTATUS { 620 debug("->", fi, err) 621 if fi != nil { 622 pfi.Context = C.ULONG64(fiTableStoreFile(uint32(pfi.DokanOptions.GlobalContext), fi)) 623 } 624 return errToNT(err) 625 } 626 627 func errToNT(err error) C.NTSTATUS { 628 // NTSTATUS constants are defined as unsigned but the type is signed 629 // and the values overflow on purpose. This is horrible. 630 var code uint32 631 if err != nil { 632 debug("ERROR:", err) 633 n, ok := err.(NtStatus) 634 if ok { 635 code = uint32(n) 636 } else { 637 code = uint32(ErrAccessDenied) 638 } 639 } 640 return C.NTSTATUS(code) 641 } 642 643 type dokanCtx struct { 644 ptr *C.struct_kbfsLibdokanCtx 645 slot uint32 646 } 647 648 func allocCtx(slot uint32) *dokanCtx { 649 return &dokanCtx{C.kbfsLibdokanAllocCtx(C.ULONG64(slot)), slot} 650 } 651 652 func (ctx *dokanCtx) Run(path string, flags MountFlag) error { 653 ctx.ptr.dokan_options.Options = C.ULONG(flags) 654 if isDebug { 655 ctx.ptr.dokan_options.Options |= C.kbfsLibdokanDebug | C.kbfsLibdokanStderr 656 } 657 C.kbfsLibdokanSet_path(ctx.ptr, stringToUtf16Ptr(path)) 658 ec := C.kbfsLibdokanRun(ctx.ptr) 659 if ec != 0 { 660 return fmt.Errorf("Dokan failed: code=%d %q", ec, dokanErrString(int32(ec))) 661 } 662 return nil 663 } 664 665 // nolint 666 func dokanErrString(code int32) string { 667 switch code { 668 case C.kbfsLibDokan_ERROR: 669 return "General error" 670 case C.kbfsLibDokan_DRIVE_LETTER_ERROR: 671 return "Drive letter error" 672 case C.kbfsLibDokan_DRIVER_INSTALL_ERROR: 673 return "Driver install error" 674 case C.kbfsLibDokan_START_ERROR: 675 return "Start error" 676 case C.kbfsLibDokan_MOUNT_ERROR: 677 return "Mount error" 678 case C.kbfsLibDokan_MOUNT_POINT_ERROR: 679 return "Mount point error" 680 case C.kbfsLibDokan_VERSION_ERROR: 681 return "Version error" 682 case C.kbfsLibDokan_DLL_LOAD_ERROR: 683 return "Error loading Dokan DLL" 684 default: 685 return "UNKNOWN" 686 } 687 } 688 689 func (ctx *dokanCtx) Free() { 690 debug("dokanCtx.Free") 691 C.kbfsLibdokanFree(ctx.ptr) 692 fsTableFree(ctx.slot) 693 } 694 695 // getRequestorToken returns the syscall.Token associated with 696 // the requestor of this file system operation. Remember to 697 // call Close on the Token. 698 func (fi *FileInfo) getRequestorToken() (syscall.Token, error) { 699 raw := C.kbfsLibdokan_OpenRequestorToken(fi.ptr) 700 hdl := syscall.Handle(uintptr(raw)) 701 var err error 702 if hdl == syscall.InvalidHandle { 703 // Tokens are value types, so returning nil is impossible, 704 // returning an InvalidHandle is the best way. 705 err = errors.New("Invalid handle from DokanOpenRequestorHandle") 706 } 707 return syscall.Token(hdl), err 708 } 709 710 // isRequestorUserSidEqualTo returns true if the sid passed as 711 // the argument is equal to the sid of the user associated with 712 // the filesystem request. 713 func (fi *FileInfo) isRequestorUserSidEqualTo(sid *winacl.SID) bool { 714 tok, err := fi.getRequestorToken() 715 if err != nil { 716 debug("IsRequestorUserSidEqualTo:", err) 717 return false 718 } 719 defer tok.Close() 720 tokUser, err := tok.GetTokenUser() 721 if err != nil { 722 debug("IsRequestorUserSidEqualTo: GetTokenUser:", err) 723 return false 724 } 725 res, _, _ := syscall.Syscall(procEqualSid.Addr(), 2, 726 uintptr(unsafe.Pointer(sid)), 727 uintptr(unsafe.Pointer(tokUser.User.Sid)), 728 0) 729 if isDebug { 730 u1, _ := (*syscall.SID)(sid).String() 731 u2, _ := tokUser.User.Sid.String() 732 debugf("IsRequestorUserSidEqualTo: EqualSID(%q,%q) => %v (expecting non-zero)\n", u1, u2, res) 733 } 734 runtime.KeepAlive(sid) 735 runtime.KeepAlive(tokUser.User.Sid) 736 return res != 0 737 } 738 739 var ( 740 modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") 741 procEqualSid = modadvapi32.NewProc("EqualSid") 742 ) 743 744 // unmount a drive mounted by dokan. 745 func unmount(path string) error { 746 debug("Unmount: Calling Dokan.Unmount") 747 res := C.kbfsLibdokan_RemoveMountPoint((*C.WCHAR)(stringToUtf16Ptr(path))) 748 if res == C.FALSE { 749 debug("Unmount: Failed!") 750 return errors.New("kbfsLibdokan_RemoveMountPoint failed") 751 } 752 debug("Unmount: Success!") 753 return nil 754 } 755 756 // lpcwstrToString converts a nul-terminated Windows wide string to a Go string, 757 func lpcwstrToString(ptr C.LPCWSTR) string { 758 if ptr == nil { 759 return "" 760 } 761 var len = 0 762 for tmp := ptr; *tmp != 0; tmp = (C.LPCWSTR)(unsafe.Pointer((uintptr(unsafe.Pointer(tmp)) + 2))) { 763 len++ 764 } 765 raw := ptrUcs2Slice(unsafe.Pointer(ptr), len) 766 return string(utf16.Decode(raw)) 767 } 768 769 // stringToUtf16Buffer pokes a string into a Windows wide string buffer. 770 // On overflow does not poke anything and returns false. 771 func stringToUtf16Buffer(s string, ptr C.LPWSTR, blenUcs2 C.DWORD) bool { 772 return stringToUtf16BufferPtr(s, unsafe.Pointer(ptr), blenUcs2) 773 } 774 func stringToUtf16BufferPtr(s string, ptr unsafe.Pointer, blenUcs2 C.DWORD) bool { 775 if ptr == nil || blenUcs2 == 0 { 776 return false 777 } 778 src := utf16.Encode([]rune(s)) 779 tgt := ptrUcs2Slice(ptr, int(blenUcs2)) 780 if len(src)+1 >= len(tgt) { 781 tgt[0] = 0 782 return false 783 } 784 copy(tgt, src) 785 tgt[len(src)] = 0 786 return true 787 } 788 789 // stringToUtf16Ptr return a pointer to the string as utf16 with zero 790 // termination. 791 func stringToUtf16Ptr(s string) unsafe.Pointer { 792 tmp := utf16.Encode([]rune(s + "\000")) 793 return unsafe.Pointer(&tmp[0]) 794 } 795 796 // ptrUcs2Slice takes a C Windows wide string and length in UCS2 797 // and returns it aliased as a uint16 slice. 798 func ptrUcs2Slice(ptr unsafe.Pointer, lenUcs2 int) []uint16 { 799 return *(*[]uint16)(unsafe.Pointer(&reflect.SliceHeader{ 800 Data: uintptr(ptr), 801 Len: lenUcs2, 802 Cap: lenUcs2})) 803 } 804 805 // d16 wraps C wide string pointers to a struct with nice printing 806 // with zero cost when not debugging and pretty prints when debugging. 807 type d16 struct{ ptr C.LPCWSTR } 808 809 func (s d16) String() string { 810 return lpcwstrToString(s.ptr) 811 }