file_windows.odin 22 KB

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