file_windows.odin 22 KB

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