file_windows.odin 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955
  1. #+private
  2. package os2
  3. import "base:runtime"
  4. import "core:io"
  5. import "core:mem"
  6. import "core:sync"
  7. import "core:time"
  8. import "core:unicode/utf16"
  9. import win32 "core:sys/windows"
  10. INVALID_HANDLE :: ~uintptr(0)
  11. S_IWRITE :: 0o200
  12. _ERROR_BAD_NETPATH :: 53
  13. MAX_RW :: 1<<30
  14. File_Impl_Kind :: enum u8 {
  15. File,
  16. Console,
  17. Pipe,
  18. }
  19. File_Impl :: struct {
  20. file: File,
  21. fd: rawptr,
  22. name: string,
  23. wname: win32.wstring,
  24. kind: File_Impl_Kind,
  25. allocator: runtime.Allocator,
  26. r_buf: []byte,
  27. w_buf: []byte,
  28. w_n: int,
  29. max_consecutive_empty_writes: int,
  30. rw_mutex: sync.RW_Mutex, // read write calls
  31. p_mutex: sync.Mutex, // pread pwrite calls
  32. }
  33. @(init)
  34. init_std_files :: proc() {
  35. new_std :: proc(impl: ^File_Impl, code: u32, name: string) -> ^File {
  36. impl.file.impl = impl
  37. impl.allocator = runtime.nil_allocator()
  38. impl.fd = win32.GetStdHandle(code)
  39. impl.name = name
  40. impl.wname = nil
  41. handle := _handle(&impl.file)
  42. kind := File_Impl_Kind.File
  43. if m: u32; win32.GetConsoleMode(handle, &m) {
  44. kind = .Console
  45. }
  46. if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE {
  47. kind = .Pipe
  48. }
  49. impl.kind = kind
  50. impl.file.stream = {
  51. data = impl,
  52. procedure = _file_stream_proc,
  53. }
  54. impl.file.fstat = _fstat
  55. return &impl.file
  56. }
  57. @(static) files: [3]File_Impl
  58. stdin = new_std(&files[0], win32.STD_INPUT_HANDLE, "<stdin>")
  59. stdout = new_std(&files[1], win32.STD_OUTPUT_HANDLE, "<stdout>")
  60. stderr = new_std(&files[2], win32.STD_ERROR_HANDLE, "<stderr>")
  61. }
  62. _handle :: proc(f: ^File) -> win32.HANDLE {
  63. return win32.HANDLE(_fd(f))
  64. }
  65. _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: uintptr, err: Error) {
  66. if len(name) == 0 {
  67. err = .Not_Exist
  68. return
  69. }
  70. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  71. path := _fix_long_path(name, temp_allocator) or_return
  72. access: u32
  73. switch flags & {.Read, .Write} {
  74. case {.Read}: access = win32.FILE_GENERIC_READ
  75. case {.Write}: access = win32.FILE_GENERIC_WRITE
  76. case {.Read, .Write}: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE
  77. }
  78. if .Create in flags {
  79. access |= win32.FILE_GENERIC_WRITE
  80. }
  81. if .Append in flags {
  82. access &~= win32.FILE_GENERIC_WRITE
  83. access |= win32.FILE_APPEND_DATA
  84. }
  85. share_mode := u32(win32.FILE_SHARE_READ | win32.FILE_SHARE_WRITE)
  86. sa := win32.SECURITY_ATTRIBUTES {
  87. nLength = size_of(win32.SECURITY_ATTRIBUTES),
  88. bInheritHandle = .Inheritable in flags,
  89. }
  90. create_mode: u32 = win32.OPEN_EXISTING
  91. switch {
  92. case flags & {.Create, .Excl} == {.Create, .Excl}:
  93. create_mode = win32.CREATE_NEW
  94. case flags & {.Create, .Trunc} == {.Create, .Trunc}:
  95. create_mode = win32.CREATE_ALWAYS
  96. case flags & {.Create} == {.Create}:
  97. create_mode = win32.OPEN_ALWAYS
  98. case flags & {.Trunc} == {.Trunc}:
  99. create_mode = win32.TRUNCATE_EXISTING
  100. }
  101. attrs: u32 = win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS
  102. if perm & S_IWRITE == 0 {
  103. attrs = win32.FILE_ATTRIBUTE_READONLY
  104. if create_mode == win32.CREATE_ALWAYS {
  105. // NOTE(bill): Open has just asked to create a file in read-only mode.
  106. // If the file already exists, to make it akin to a *nix open call,
  107. // the call preserves the existing permissions.
  108. h := win32.CreateFileW(path, access, share_mode, &sa, win32.TRUNCATE_EXISTING, win32.FILE_ATTRIBUTE_NORMAL, nil)
  109. if h == win32.INVALID_HANDLE {
  110. switch e := win32.GetLastError(); e {
  111. case win32.ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, win32.ERROR_PATH_NOT_FOUND:
  112. // file does not exist, create the file
  113. case 0:
  114. return uintptr(h), nil
  115. case:
  116. return 0, _get_platform_error()
  117. }
  118. }
  119. }
  120. }
  121. h := win32.CreateFileW(path, access, share_mode, &sa, create_mode, attrs, nil)
  122. if h == win32.INVALID_HANDLE {
  123. return 0, _get_platform_error()
  124. }
  125. return uintptr(h), nil
  126. }
  127. _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
  128. flags := flags if flags != nil else {.Read}
  129. handle := _open_internal(name, flags, perm) or_return
  130. return _new_file(handle, name, file_allocator())
  131. }
  132. _new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
  133. if handle == INVALID_HANDLE {
  134. return
  135. }
  136. impl := new(File_Impl, allocator) or_return
  137. defer if err != nil {
  138. free(impl, allocator)
  139. }
  140. impl.file.impl = impl
  141. impl.allocator = allocator
  142. impl.fd = rawptr(handle)
  143. impl.name = clone_string(name, impl.allocator) or_return
  144. impl.wname = win32_utf8_to_wstring(name, impl.allocator) or_return
  145. handle := _handle(&impl.file)
  146. kind := File_Impl_Kind.File
  147. if m: u32; win32.GetConsoleMode(handle, &m) {
  148. kind = .Console
  149. }
  150. if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE {
  151. kind = .Pipe
  152. }
  153. impl.kind = kind
  154. impl.file.stream = {
  155. data = impl,
  156. procedure = _file_stream_proc,
  157. }
  158. impl.file.fstat = _fstat
  159. return &impl.file, nil
  160. }
  161. @(require_results)
  162. _open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm := 0o777) -> (f: ^File, err: Error) {
  163. assert(buffer_size > 0)
  164. flags := flags if flags != nil else {.Read}
  165. handle := _open_internal(name, flags, perm) or_return
  166. return _new_file_buffered(handle, name, buffer_size)
  167. }
  168. _new_file_buffered :: proc(handle: uintptr, name: string, buffer_size: uint) -> (f: ^File, err: Error) {
  169. f, err = _new_file(handle, name, file_allocator())
  170. if f != nil && err == nil {
  171. impl := (^File_Impl)(f.impl)
  172. impl.r_buf = make([]byte, buffer_size, file_allocator())
  173. impl.w_buf = make([]byte, buffer_size, file_allocator())
  174. }
  175. return
  176. }
  177. _clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
  178. if f == nil || f.impl == nil {
  179. return
  180. }
  181. clonefd: win32.HANDLE
  182. process := win32.GetCurrentProcess()
  183. if !win32.DuplicateHandle(
  184. process,
  185. win32.HANDLE(_fd(f)),
  186. process,
  187. &clonefd,
  188. 0,
  189. false,
  190. win32.DUPLICATE_SAME_ACCESS,
  191. ) {
  192. err = _get_platform_error()
  193. return
  194. }
  195. defer if err != nil { win32.CloseHandle(clonefd) }
  196. return _new_file(uintptr(clonefd), name(f), file_allocator())
  197. }
  198. _fd :: proc(f: ^File) -> uintptr {
  199. if f == nil || f.impl == nil {
  200. return INVALID_HANDLE
  201. }
  202. return uintptr((^File_Impl)(f.impl).fd)
  203. }
  204. _destroy :: proc(f: ^File_Impl) -> Error {
  205. if f == nil {
  206. return nil
  207. }
  208. a := f.allocator
  209. err0 := free(f.wname, a)
  210. err1 := delete(f.name, a)
  211. err2 := delete(f.r_buf, a)
  212. err3 := delete(f.w_buf, a)
  213. err4 := free(f, a)
  214. err0 or_return
  215. err1 or_return
  216. err2 or_return
  217. err3 or_return
  218. err4 or_return
  219. return nil
  220. }
  221. _close :: proc(f: ^File_Impl) -> Error {
  222. if f == nil {
  223. return nil
  224. }
  225. if !win32.CloseHandle(win32.HANDLE(f.fd)) {
  226. return .Closed
  227. }
  228. return _destroy(f)
  229. }
  230. _name :: proc(f: ^File) -> string {
  231. return (^File_Impl)(f.impl).name if f != nil && f.impl != nil else ""
  232. }
  233. _seek :: proc(f: ^File_Impl, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
  234. handle := _handle(&f.file)
  235. if handle == win32.INVALID_HANDLE {
  236. return 0, .Invalid_File
  237. }
  238. if f.kind == .Pipe {
  239. return 0, .Invalid_File
  240. }
  241. sync.guard(&f.rw_mutex)
  242. w: u32
  243. switch whence {
  244. case .Start: w = win32.FILE_BEGIN
  245. case .Current: w = win32.FILE_CURRENT
  246. case .End: w = win32.FILE_END
  247. case:
  248. return 0, .Invalid_Whence
  249. }
  250. hi := i32(offset>>32)
  251. lo := i32(offset)
  252. dw_ptr := win32.SetFilePointer(handle, lo, &hi, w)
  253. if dw_ptr == win32.INVALID_SET_FILE_POINTER {
  254. return 0, _get_platform_error()
  255. }
  256. return i64(hi)<<32 + i64(dw_ptr), nil
  257. }
  258. _read :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
  259. return _read_internal(f, p)
  260. }
  261. _read_internal :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
  262. length := len(p)
  263. if length == 0 {
  264. return
  265. }
  266. read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
  267. if len(b) == 0 {
  268. return 0, nil
  269. }
  270. // TODO(bill): should this be moved to `File_Impl` instead?
  271. BUF_SIZE :: 386
  272. buf16: [BUF_SIZE]u16
  273. buf8: [4*BUF_SIZE]u8
  274. for n < len(b) && err == nil {
  275. min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
  276. max_read := u32(min(BUF_SIZE, min_read))
  277. if max_read == 0 {
  278. break
  279. }
  280. single_read_length: u32
  281. ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
  282. if !ok {
  283. err = _get_platform_error()
  284. }
  285. buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
  286. src := buf8[:buf8_len]
  287. ctrl_z := false
  288. for i := 0; i < len(src) && n+i < len(b); i += 1 {
  289. x := src[i]
  290. if x == 0x1a { // ctrl-z
  291. ctrl_z = true
  292. break
  293. }
  294. b[n] = x
  295. n += 1
  296. }
  297. if ctrl_z || single_read_length < max_read {
  298. break
  299. }
  300. // NOTE(bill): if the last two values were a newline, then it is expected that
  301. // this is the end of the input
  302. if n >= 2 && single_read_length == max_read && string(b[n-2:n]) == "\r\n" {
  303. break
  304. }
  305. }
  306. return
  307. }
  308. handle := _handle(&f.file)
  309. single_read_length: win32.DWORD
  310. total_read: int
  311. sync.shared_guard(&f.rw_mutex) // multiple readers
  312. if sync.guard(&f.p_mutex) {
  313. to_read := min(win32.DWORD(length), MAX_RW)
  314. ok: win32.BOOL
  315. if f.kind == .Console {
  316. n, cerr := read_console(handle, p[total_read:][:to_read])
  317. total_read += n
  318. if cerr != nil {
  319. return i64(total_read), cerr
  320. }
  321. } else {
  322. ok = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil)
  323. }
  324. if single_read_length > 0 && ok {
  325. total_read += int(single_read_length)
  326. } else if single_read_length == 0 && ok {
  327. // ok and 0 bytes means EOF:
  328. // https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
  329. err = .EOF
  330. } else {
  331. err = _get_platform_error()
  332. }
  333. }
  334. return i64(total_read), err
  335. }
  336. _read_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (n: i64, err: Error) {
  337. pread :: proc(f: ^File_Impl, data: []byte, offset: i64) -> (n: i64, err: Error) {
  338. buf := data
  339. if len(buf) > MAX_RW {
  340. buf = buf[:MAX_RW]
  341. }
  342. curr_offset := _seek(f, 0, .Current) or_return
  343. defer _seek(f, curr_offset, .Start)
  344. o := win32.OVERLAPPED{
  345. OffsetHigh = u32(offset>>32),
  346. Offset = u32(offset),
  347. }
  348. // TODO(bill): Determine the correct behaviour for consoles
  349. h := _handle(&f.file)
  350. done: win32.DWORD
  351. if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
  352. err = _get_platform_error()
  353. done = 0
  354. }
  355. n = i64(done)
  356. return
  357. }
  358. sync.guard(&f.p_mutex)
  359. p, offset := p, offset
  360. for len(p) > 0 {
  361. m := pread(f, p, offset) or_return
  362. n += m
  363. p = p[m:]
  364. offset += i64(m)
  365. }
  366. return
  367. }
  368. _write :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
  369. return _write_internal(f, p)
  370. }
  371. _write_internal :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
  372. if len(p) == 0 {
  373. return
  374. }
  375. single_write_length: win32.DWORD
  376. total_write: i64
  377. length := i64(len(p))
  378. handle := _handle(&f.file)
  379. sync.guard(&f.rw_mutex)
  380. for total_write < length {
  381. remaining := length - total_write
  382. to_write := win32.DWORD(min(i32(remaining), MAX_RW))
  383. e := win32.WriteFile(handle, &p[total_write], to_write, &single_write_length, nil)
  384. if single_write_length <= 0 || !e {
  385. n = i64(total_write)
  386. err = _get_platform_error()
  387. return
  388. }
  389. total_write += i64(single_write_length)
  390. }
  391. return i64(total_write), nil
  392. }
  393. _write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (n: i64, err: Error) {
  394. pwrite :: proc(f: ^File_Impl, data: []byte, offset: i64) -> (n: i64, err: Error) {
  395. buf := data
  396. if len(buf) > MAX_RW {
  397. buf = buf[:MAX_RW]
  398. }
  399. curr_offset := _seek(f, 0, .Current) or_return
  400. defer _seek(f, curr_offset, .Start)
  401. o := win32.OVERLAPPED{
  402. OffsetHigh = u32(offset>>32),
  403. Offset = u32(offset),
  404. }
  405. h := _handle(&f.file)
  406. done: win32.DWORD
  407. if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
  408. err = _get_platform_error()
  409. done = 0
  410. }
  411. n = i64(done)
  412. return
  413. }
  414. sync.guard(&f.p_mutex)
  415. p, offset := p, offset
  416. for len(p) > 0 {
  417. m := pwrite(f, p, offset) or_return
  418. n += m
  419. p = p[m:]
  420. offset += i64(m)
  421. }
  422. return
  423. }
  424. _file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) {
  425. length: win32.LARGE_INTEGER
  426. handle := _handle(&f.file)
  427. if f.kind == .Pipe {
  428. bytes_available: u32
  429. if win32.PeekNamedPipe(handle, nil, 0, nil, &bytes_available, nil) {
  430. return i64(bytes_available), nil
  431. } else {
  432. err = _get_platform_error()
  433. return
  434. }
  435. }
  436. if !win32.GetFileSizeEx(handle, &length) {
  437. err = _get_platform_error()
  438. }
  439. n = i64(length)
  440. return
  441. }
  442. _sync :: proc(f: ^File) -> Error {
  443. if f != nil && f.impl != nil {
  444. return _flush_internal((^File_Impl)(f.impl))
  445. }
  446. return nil
  447. }
  448. _flush :: proc(f: ^File_Impl) -> Error {
  449. return _flush_internal(f)
  450. }
  451. _flush_internal :: proc(f: ^File_Impl) -> Error {
  452. handle := _handle(&f.file)
  453. if !win32.FlushFileBuffers(handle) {
  454. return _get_platform_error()
  455. }
  456. return nil
  457. }
  458. _truncate :: proc(f: ^File, size: i64) -> Error {
  459. if f == nil || f.impl == nil {
  460. return nil
  461. }
  462. curr_off := seek(f, 0, .Current) or_return
  463. defer seek(f, curr_off, .Start)
  464. seek(f, size, .Start) or_return
  465. handle := _handle(f)
  466. if !win32.SetEndOfFile(handle) {
  467. return _get_platform_error()
  468. }
  469. return nil
  470. }
  471. _remove :: proc(name: string) -> Error {
  472. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  473. p := _fix_long_path(name, temp_allocator) or_return
  474. err, err1: Error
  475. if !win32.DeleteFileW(p) {
  476. err = _get_platform_error()
  477. }
  478. if err == nil {
  479. return nil
  480. }
  481. if !win32.RemoveDirectoryW(p) {
  482. err1 = _get_platform_error()
  483. }
  484. if err1 == nil {
  485. return nil
  486. }
  487. if err != err1 {
  488. a := win32.GetFileAttributesW(p)
  489. if a == win32.INVALID_FILE_ATTRIBUTES {
  490. err = _get_platform_error()
  491. } else {
  492. if a & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
  493. err = err1
  494. } else if a & win32.FILE_ATTRIBUTE_READONLY != 0 {
  495. if win32.SetFileAttributesW(p, a &~ win32.FILE_ATTRIBUTE_READONLY) {
  496. err = nil
  497. if !win32.DeleteFileW(p) {
  498. err = _get_platform_error()
  499. }
  500. }
  501. }
  502. }
  503. }
  504. return err
  505. }
  506. _rename :: proc(old_path, new_path: string) -> Error {
  507. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  508. from := _fix_long_path(old_path, temp_allocator) or_return
  509. to := _fix_long_path(new_path, temp_allocator) or_return
  510. if win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
  511. return nil
  512. }
  513. return _get_platform_error()
  514. }
  515. _link :: proc(old_name, new_name: string) -> Error {
  516. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  517. o := _fix_long_path(old_name, temp_allocator) or_return
  518. n := _fix_long_path(new_name, temp_allocator) or_return
  519. if win32.CreateHardLinkW(n, o, nil) {
  520. return nil
  521. }
  522. return _get_platform_error()
  523. }
  524. _symlink :: proc(old_name, new_name: string) -> Error {
  525. return .Unsupported
  526. }
  527. _open_sym_link :: proc(p: [^]u16) -> (handle: win32.HANDLE, err: Error) {
  528. attrs := u32(win32.FILE_FLAG_BACKUP_SEMANTICS)
  529. attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT
  530. handle = win32.CreateFileW(p, 0, 0, nil, win32.OPEN_EXISTING, attrs, nil)
  531. if handle == win32.INVALID_HANDLE {
  532. return nil, _get_platform_error()
  533. }
  534. return
  535. }
  536. _normalize_link_path :: proc(p: []u16, allocator: runtime.Allocator) -> (str: string, err: Error) {
  537. has_prefix :: proc(p: []u16, str: string) -> bool {
  538. if len(p) < len(str) {
  539. return false
  540. }
  541. // assume ascii
  542. for i in 0..<len(str) {
  543. if p[i] != u16(str[i]) {
  544. return false
  545. }
  546. }
  547. return true
  548. }
  549. has_unc_prefix :: proc(p: []u16) -> bool {
  550. return has_prefix(p, `\??\`)
  551. }
  552. if !has_unc_prefix(p) {
  553. return win32_utf16_to_utf8(p, allocator)
  554. }
  555. ws := p[4:]
  556. switch {
  557. case len(ws) >= 2 && ws[1] == ':':
  558. return win32_utf16_to_utf8(ws, allocator)
  559. case has_prefix(ws, `UNC\`):
  560. ws[3] = '\\' // override data in buffer
  561. return win32_utf16_to_utf8(ws[3:], allocator)
  562. }
  563. handle := _open_sym_link(raw_data(p)) or_return
  564. defer win32.CloseHandle(handle)
  565. n := win32.GetFinalPathNameByHandleW(handle, nil, 0, win32.VOLUME_NAME_DOS)
  566. if n == 0 {
  567. return "", _get_platform_error()
  568. }
  569. temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
  570. buf := make([]u16, n+1, temp_allocator)
  571. n = win32.GetFinalPathNameByHandleW(handle, raw_data(buf), u32(len(buf)), win32.VOLUME_NAME_DOS)
  572. if n == 0 {
  573. return "", _get_platform_error()
  574. }
  575. ws = buf[:n]
  576. if has_unc_prefix(ws) {
  577. ws = ws[4:]
  578. if len(ws) > 3 && has_prefix(ws, `UNC`) {
  579. ws[2] = '\\'
  580. return win32_utf16_to_utf8(ws[2:], allocator)
  581. }
  582. return win32_utf16_to_utf8(ws, allocator)
  583. }
  584. return "", .Invalid_Path
  585. }
  586. _read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, err: Error) {
  587. MAXIMUM_REPARSE_DATA_BUFFER_SIZE :: 16 * 1024
  588. @thread_local
  589. rdb_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
  590. temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
  591. p := _fix_long_path(name, temp_allocator) or_return
  592. handle := _open_sym_link(p) or_return
  593. defer win32.CloseHandle(handle)
  594. bytes_returned: u32
  595. if !win32.DeviceIoControl(handle, win32.FSCTL_GET_REPARSE_POINT, nil, 0, &rdb_buf[0], len(rdb_buf)-1, &bytes_returned, nil) {
  596. err = _get_platform_error()
  597. return
  598. }
  599. mem.zero_slice(rdb_buf[:min(bytes_returned+1, len(rdb_buf))])
  600. rdb := (^win32.REPARSE_DATA_BUFFER)(&rdb_buf[0])
  601. switch rdb.ReparseTag {
  602. case win32.IO_REPARSE_TAG_SYMLINK:
  603. rb := (^win32.SYMBOLIC_LINK_REPARSE_BUFFER)(&rdb.rest)
  604. pb := win32.wstring(&rb.PathBuffer)
  605. pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0
  606. p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength]
  607. if rb.Flags & win32.SYMLINK_FLAG_RELATIVE != 0 {
  608. return win32_utf16_to_utf8(p, allocator)
  609. }
  610. return _normalize_link_path(p, allocator)
  611. case win32.IO_REPARSE_TAG_MOUNT_POINT:
  612. rb := (^win32.MOUNT_POINT_REPARSE_BUFFER)(&rdb.rest)
  613. pb := win32.wstring(&rb.PathBuffer)
  614. pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0
  615. p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength]
  616. return _normalize_link_path(p, allocator)
  617. }
  618. // Path wasn't a symlink/junction but another reparse point kind
  619. return "", nil
  620. }
  621. _fchdir :: proc(f: ^File) -> Error {
  622. if f == nil || f.impl == nil {
  623. return nil
  624. }
  625. impl := (^File_Impl)(f.impl)
  626. if !win32.SetCurrentDirectoryW(impl.wname) {
  627. return _get_platform_error()
  628. }
  629. return nil
  630. }
  631. _fchmod :: proc(f: ^File, mode: int) -> Error {
  632. if f == nil || f.impl == nil {
  633. return nil
  634. }
  635. d: win32.BY_HANDLE_FILE_INFORMATION
  636. if !win32.GetFileInformationByHandle(_handle(f), &d) {
  637. return _get_platform_error()
  638. }
  639. attrs := d.dwFileAttributes
  640. if mode & S_IWRITE != 0 {
  641. attrs &~= win32.FILE_ATTRIBUTE_READONLY
  642. } else {
  643. attrs |= win32.FILE_ATTRIBUTE_READONLY
  644. }
  645. info: win32.FILE_BASIC_INFO
  646. info.FileAttributes = attrs
  647. if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(d)) {
  648. return _get_platform_error()
  649. }
  650. return nil
  651. }
  652. _fchown :: proc(f: ^File, uid, gid: int) -> Error {
  653. return .Unsupported
  654. }
  655. _chdir :: proc(name: string) -> Error {
  656. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  657. p := _fix_long_path(name, temp_allocator) or_return
  658. if !win32.SetCurrentDirectoryW(p) {
  659. return _get_platform_error()
  660. }
  661. return nil
  662. }
  663. _chmod :: proc(name: string, mode: int) -> Error {
  664. f := open(name, {.Write}) or_return
  665. defer close(f)
  666. return _fchmod(f, mode)
  667. }
  668. _chown :: proc(name: string, uid, gid: int) -> Error {
  669. return .Unsupported
  670. }
  671. _lchown :: proc(name: string, uid, gid: int) -> Error {
  672. return .Unsupported
  673. }
  674. _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
  675. f := open(name, {.Write}) or_return
  676. defer close(f)
  677. return _fchtimes(f, atime, mtime)
  678. }
  679. _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
  680. if f == nil || f.impl == nil {
  681. return nil
  682. }
  683. atime, mtime := atime, mtime
  684. if time.time_to_unix_nano(atime) < time.time_to_unix_nano(mtime) {
  685. atime = mtime
  686. }
  687. info: win32.FILE_BASIC_INFO
  688. info.LastAccessTime = time_as_filetime(atime)
  689. info.LastWriteTime = time_as_filetime(mtime)
  690. if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(info)) {
  691. return _get_platform_error()
  692. }
  693. return nil
  694. }
  695. _exists :: proc(path: string) -> bool {
  696. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  697. wpath, _ := _fix_long_path(path, temp_allocator)
  698. attribs := win32.GetFileAttributesW(wpath)
  699. return attribs != win32.INVALID_FILE_ATTRIBUTES
  700. }
  701. @(private="package")
  702. _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
  703. f := (^File_Impl)(stream_data)
  704. ferr: Error
  705. switch mode {
  706. case .Read:
  707. n, ferr = _read(f, p)
  708. err = error_to_io_error(ferr)
  709. return
  710. case .Read_At:
  711. n, ferr = _read_at(f, p, offset)
  712. err = error_to_io_error(ferr)
  713. return
  714. case .Write:
  715. n, ferr = _write(f, p)
  716. err = error_to_io_error(ferr)
  717. return
  718. case .Write_At:
  719. n, ferr = _write_at(f, p, offset)
  720. err = error_to_io_error(ferr)
  721. return
  722. case .Seek:
  723. n, ferr = _seek(f, offset, whence)
  724. err = error_to_io_error(ferr)
  725. return
  726. case .Size:
  727. n, ferr = _file_size(f)
  728. err = error_to_io_error(ferr)
  729. return
  730. case .Flush:
  731. ferr = _flush(f)
  732. err = error_to_io_error(ferr)
  733. return
  734. case .Close, .Destroy:
  735. ferr = _close(f)
  736. err = error_to_io_error(ferr)
  737. return
  738. case .Query:
  739. return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
  740. }
  741. return 0, .Empty
  742. }
  743. @(private="package", require_results)
  744. win32_utf8_to_wstring :: proc(s: string, allocator: runtime.Allocator) -> (ws: [^]u16, err: runtime.Allocator_Error) {
  745. ws = raw_data(win32_utf8_to_utf16(s, allocator) or_return)
  746. return
  747. }
  748. @(private="package", require_results)
  749. win32_utf8_to_utf16 :: proc(s: string, allocator: runtime.Allocator) -> (ws: []u16, err: runtime.Allocator_Error) {
  750. if len(s) < 1 {
  751. return
  752. }
  753. b := transmute([]byte)s
  754. cstr := raw_data(b)
  755. n := win32.MultiByteToWideChar(win32.CP_UTF8, win32.MB_ERR_INVALID_CHARS, cstr, i32(len(s)), nil, 0)
  756. if n == 0 {
  757. return nil, nil
  758. }
  759. text := make([]u16, n+1, allocator) or_return
  760. n1 := win32.MultiByteToWideChar(win32.CP_UTF8, win32.MB_ERR_INVALID_CHARS, cstr, i32(len(s)), raw_data(text), n)
  761. if n1 == 0 {
  762. delete(text, allocator)
  763. return
  764. }
  765. text[n] = 0
  766. for n >= 1 && text[n-1] == 0 {
  767. n -= 1
  768. }
  769. ws = text[:n]
  770. return
  771. }
  772. @(private="package", require_results)
  773. win32_wstring_to_utf8 :: proc(s: [^]u16, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) {
  774. if s == nil || s[0] == 0 {
  775. return "", nil
  776. }
  777. n := 0
  778. for s[n] != 0 {
  779. n += 1
  780. }
  781. return win32_utf16_to_utf8(s[:n], allocator)
  782. }
  783. @(private="package", require_results)
  784. win32_utf16_to_utf8 :: proc(s: []u16, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) {
  785. if len(s) == 0 {
  786. return
  787. }
  788. n := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, raw_data(s), i32(len(s)), nil, 0, nil, nil)
  789. if n == 0 {
  790. return
  791. }
  792. // If N < 0 the call to WideCharToMultiByte assume the wide string is null terminated
  793. // and will scan it to find the first null terminated character. The resulting string will
  794. // also be null terminated.
  795. // If N > 0 it assumes the wide string is not null terminated and the resulting string
  796. // will not be null terminated.
  797. text := make([]byte, n, allocator) or_return
  798. n1 := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, raw_data(s), i32(len(s)), raw_data(text), n, nil, nil)
  799. if n1 == 0 {
  800. delete(text, allocator)
  801. return
  802. }
  803. for i in 0..<n {
  804. if text[i] == 0 {
  805. n = i
  806. break
  807. }
  808. }
  809. res = string(text[:n])
  810. return
  811. }