file_windows.odin 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. //+private
  2. package os2
  3. import "core:io"
  4. import "core:mem"
  5. import "core:sync"
  6. import "core:runtime"
  7. import "core:strings"
  8. import "core:time"
  9. import "core:unicode/utf16"
  10. import win32 "core:sys/windows"
  11. INVALID_HANDLE :: ~uintptr(0)
  12. S_IWRITE :: 0o200
  13. _ERROR_BAD_NETPATH :: 53
  14. MAX_RW :: 1<<30
  15. _file_allocator :: proc() -> runtime.Allocator {
  16. return heap_allocator()
  17. }
  18. _temp_allocator :: proc() -> runtime.Allocator {
  19. // TODO(bill): make this not depend on the context allocator
  20. return context.temp_allocator
  21. }
  22. _File_Kind :: enum u8 {
  23. File,
  24. Console,
  25. Pipe,
  26. }
  27. _File :: struct {
  28. fd: rawptr,
  29. name: string,
  30. wname: win32.wstring,
  31. kind: _File_Kind,
  32. allocator: runtime.Allocator,
  33. rw_mutex: sync.RW_Mutex, // read write calls
  34. p_mutex: sync.Mutex, // pread pwrite calls
  35. }
  36. _handle :: proc(f: ^File) -> win32.HANDLE {
  37. return win32.HANDLE(_fd(f))
  38. }
  39. _open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (handle: uintptr, err: Error) {
  40. if len(name) == 0 {
  41. err = .Not_Exist
  42. return
  43. }
  44. path := _fix_long_path(name)
  45. access: u32
  46. switch flags & {.Read, .Write} {
  47. case {.Read}: access = win32.FILE_GENERIC_READ
  48. case {.Write}: access = win32.FILE_GENERIC_WRITE
  49. case {.Read, .Write}: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE
  50. }
  51. if .Create in flags {
  52. access |= win32.FILE_GENERIC_WRITE
  53. }
  54. if .Append in flags {
  55. access &~= win32.FILE_GENERIC_WRITE
  56. access |= win32.FILE_APPEND_DATA
  57. }
  58. share_mode := u32(win32.FILE_SHARE_READ | win32.FILE_SHARE_WRITE)
  59. sa: ^win32.SECURITY_ATTRIBUTES
  60. if .Close_On_Exec not_in flags {
  61. sa = &win32.SECURITY_ATTRIBUTES{}
  62. sa.nLength = size_of(win32.SECURITY_ATTRIBUTES)
  63. sa.bInheritHandle = true
  64. }
  65. create_mode: u32 = win32.OPEN_EXISTING
  66. switch {
  67. case flags & {.Create, .Excl} == {.Create, .Excl}:
  68. create_mode = win32.CREATE_NEW
  69. case flags & {.Create, .Trunc} == {.Create, .Trunc}:
  70. create_mode = win32.CREATE_ALWAYS
  71. case flags & {.Create} == {.Create}:
  72. create_mode = win32.OPEN_ALWAYS
  73. case flags & {.Trunc} == {.Trunc}:
  74. create_mode = win32.TRUNCATE_EXISTING
  75. }
  76. attrs: u32 = win32.FILE_ATTRIBUTE_NORMAL
  77. if perm & S_IWRITE == 0 {
  78. attrs = win32.FILE_ATTRIBUTE_READONLY
  79. if create_mode == win32.CREATE_ALWAYS {
  80. // NOTE(bill): Open has just asked to create a file in read-only mode.
  81. // If the file already exists, to make it akin to a *nix open call,
  82. // the call preserves the existing permissions.
  83. h := win32.CreateFileW(path, access, share_mode, sa, win32.TRUNCATE_EXISTING, win32.FILE_ATTRIBUTE_NORMAL, nil)
  84. if h == win32.INVALID_HANDLE {
  85. switch e := win32.GetLastError(); e {
  86. case win32.ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, win32.ERROR_PATH_NOT_FOUND:
  87. // file does not exist, create the file
  88. case 0:
  89. return uintptr(h), nil
  90. case:
  91. return 0, Platform_Error(e)
  92. }
  93. }
  94. }
  95. }
  96. h := win32.CreateFileW(path, access, share_mode, sa, create_mode, attrs, nil)
  97. if h == win32.INVALID_HANDLE {
  98. return 0, _get_platform_error()
  99. }
  100. return uintptr(h), nil
  101. }
  102. _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) {
  103. flags := flags if flags != nil else {.Read}
  104. handle := _open_internal(name, flags + {.Close_On_Exec}, perm) or_return
  105. return _new_file(handle, name), nil
  106. }
  107. _new_file :: proc(handle: uintptr, name: string) -> ^File {
  108. if handle == INVALID_HANDLE {
  109. return nil
  110. }
  111. f := new(File, _file_allocator())
  112. f.impl.allocator = _file_allocator()
  113. f.impl.fd = rawptr(fd)
  114. f.impl.name = strings.clone(name, f.impl.allocator)
  115. f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator)
  116. handle := _handle(f)
  117. kind := _File_Kind.File
  118. if m: u32; win32.GetConsoleMode(handle, &m) {
  119. kind = .Console
  120. }
  121. if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE {
  122. kind = .Pipe
  123. }
  124. f.impl.kind = kind
  125. return f
  126. }
  127. _fd :: proc(f: ^File) -> uintptr {
  128. if f == nil {
  129. return INVALID_HANDLE
  130. }
  131. return uintptr(f.impl.fd)
  132. }
  133. _destroy :: proc(f: ^File) -> Error {
  134. if f == nil {
  135. return nil
  136. }
  137. a := f.impl.allocator
  138. free(f.impl.wname, a)
  139. delete(f.impl.name, a)
  140. free(f, a)
  141. return nil
  142. }
  143. _close :: proc(f: ^File) -> Error {
  144. if f == nil {
  145. return nil
  146. }
  147. if !win32.CloseHandle(win32.HANDLE(f.impl.fd)) {
  148. return .Closed
  149. }
  150. return _destroy(f)
  151. }
  152. _name :: proc(f: ^File) -> string {
  153. return f.impl.name if f != nil else ""
  154. }
  155. _seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
  156. handle := _handle(f)
  157. if handle == win32.INVALID_HANDLE {
  158. return 0, .Invalid_File
  159. }
  160. if f.impl.kind == .Pipe {
  161. return 0, .Invalid_File
  162. }
  163. sync.guard(&f.impl.rw_mutex)
  164. w: u32
  165. switch whence {
  166. case .Start: w = win32.FILE_BEGIN
  167. case .Current: w = win32.FILE_CURRENT
  168. case .End: w = win32.FILE_END
  169. }
  170. hi := i32(offset>>32)
  171. lo := i32(offset)
  172. dw_ptr := win32.SetFilePointer(handle, lo, &hi, w)
  173. if dw_ptr == win32.INVALID_SET_FILE_POINTER {
  174. return 0, _get_platform_error()
  175. }
  176. return i64(hi)<<32 + i64(dw_ptr), nil
  177. }
  178. _read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
  179. read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
  180. if len(b) == 0 {
  181. return 0, nil
  182. }
  183. // TODO(bill): should this be moved to `_File` instead?
  184. BUF_SIZE :: 386
  185. buf16: [BUF_SIZE]u16
  186. buf8: [4*BUF_SIZE]u8
  187. for n < len(b) && err == nil {
  188. min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
  189. max_read := u32(min(BUF_SIZE, min_read))
  190. if max_read == 0 {
  191. break
  192. }
  193. single_read_length: u32
  194. ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
  195. if !ok {
  196. err = _get_platform_error()
  197. }
  198. buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
  199. src := buf8[:buf8_len]
  200. ctrl_z := false
  201. for i := 0; i < len(src) && n+i < len(b); i += 1 {
  202. x := src[i]
  203. if x == 0x1a { // ctrl-z
  204. ctrl_z = true
  205. break
  206. }
  207. b[n] = x
  208. n += 1
  209. }
  210. if ctrl_z || single_read_length < max_read {
  211. break
  212. }
  213. // NOTE(bill): if the last two values were a newline, then it is expected that
  214. // this is the end of the input
  215. if n >= 2 && single_read_length == max_read && string(b[n-2:n]) == "\r\n" {
  216. break
  217. }
  218. }
  219. return
  220. }
  221. handle := _handle(f)
  222. single_read_length: win32.DWORD
  223. total_read: int
  224. length := len(p)
  225. sync.shared_guard(&f.impl.rw_mutex) // multiple readers
  226. if sync.guard(&f.impl.p_mutex) {
  227. to_read := min(win32.DWORD(length), MAX_RW)
  228. ok: win32.BOOL
  229. if f.impl.kind == .Console {
  230. n, err := read_console(handle, p[total_read:][:to_read])
  231. total_read += n
  232. if err != nil {
  233. return int(total_read), err
  234. }
  235. } else {
  236. ok = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil)
  237. }
  238. if single_read_length > 0 && ok {
  239. total_read += int(single_read_length)
  240. } else {
  241. err = _get_platform_error()
  242. }
  243. }
  244. return int(total_read), nil
  245. }
  246. _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
  247. pread :: proc(f: ^File, data: []byte, offset: i64) -> (n: int, err: Error) {
  248. buf := data
  249. if len(buf) > MAX_RW {
  250. buf = buf[:MAX_RW]
  251. }
  252. curr_offset := seek(f, offset, .Current) or_return
  253. defer seek(f, curr_offset, .Start)
  254. o := win32.OVERLAPPED{
  255. OffsetHigh = u32(offset>>32),
  256. Offset = u32(offset),
  257. }
  258. // TODO(bill): Determine the correct behaviour for consoles
  259. h := _handle(f)
  260. done: win32.DWORD
  261. if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
  262. err = _get_platform_error()
  263. done = 0
  264. }
  265. n = int(done)
  266. return
  267. }
  268. sync.guard(&f.impl.p_mutex)
  269. p, offset := p, offset
  270. for len(p) > 0 {
  271. m := pread(f, p, offset) or_return
  272. n += m
  273. p = p[m:]
  274. offset += i64(m)
  275. }
  276. return
  277. }
  278. _read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
  279. // TODO(bill)
  280. return
  281. }
  282. _write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
  283. if len(p) == 0 {
  284. return
  285. }
  286. single_write_length: win32.DWORD
  287. total_write: i64
  288. length := i64(len(p))
  289. handle := _handle(f)
  290. sync.guard(&f.impl.rw_mutex)
  291. for total_write < length {
  292. remaining := length - total_write
  293. to_write := win32.DWORD(min(i32(remaining), MAX_RW))
  294. e := win32.WriteFile(handle, &p[total_write], to_write, &single_write_length, nil)
  295. if single_write_length <= 0 || !e {
  296. n = int(total_write)
  297. err = _get_platform_error()
  298. return
  299. }
  300. total_write += i64(single_write_length)
  301. }
  302. return int(total_write), nil
  303. }
  304. _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
  305. pwrite :: proc(f: ^File, data: []byte, offset: i64) -> (n: int, err: Error) {
  306. buf := data
  307. if len(buf) > MAX_RW {
  308. buf = buf[:MAX_RW]
  309. }
  310. curr_offset := seek(f, offset, .Current) or_return
  311. defer seek(f, curr_offset, .Start)
  312. o := win32.OVERLAPPED{
  313. OffsetHigh = u32(offset>>32),
  314. Offset = u32(offset),
  315. }
  316. h := _handle(f)
  317. done: win32.DWORD
  318. if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
  319. err = _get_platform_error()
  320. done = 0
  321. }
  322. n = int(done)
  323. return
  324. }
  325. sync.guard(&f.impl.p_mutex)
  326. p, offset := p, offset
  327. for len(p) > 0 {
  328. m := pwrite(f, p, offset) or_return
  329. n += m
  330. p = p[m:]
  331. offset += i64(m)
  332. }
  333. return
  334. }
  335. _write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
  336. // TODO(bill)
  337. return
  338. }
  339. _file_size :: proc(f: ^File) -> (n: i64, err: Error) {
  340. length: win32.LARGE_INTEGER
  341. handle := _handle(f)
  342. if !win32.GetFileSizeEx(handle, &length) {
  343. err = _get_platform_error()
  344. }
  345. n = i64(length)
  346. return
  347. }
  348. _sync :: proc(f: ^File) -> Error {
  349. return _flush(f)
  350. }
  351. _flush :: proc(f: ^File) -> Error {
  352. handle := _handle(f)
  353. if !win32.FlushFileBuffers(handle) {
  354. return _get_platform_error()
  355. }
  356. return nil
  357. }
  358. _truncate :: proc(f: ^File, size: i64) -> Error {
  359. if f == nil {
  360. return nil
  361. }
  362. curr_off := seek(f, 0, .Current) or_return
  363. defer seek(f, curr_off, .Start)
  364. seek(f, size, .Start) or_return
  365. handle := _handle(f)
  366. if !win32.SetEndOfFile(handle) {
  367. return _get_platform_error()
  368. }
  369. return nil
  370. }
  371. _remove :: proc(name: string) -> Error {
  372. p := _fix_long_path(name)
  373. err, err1: Error
  374. if !win32.DeleteFileW(p) {
  375. err = _get_platform_error()
  376. }
  377. if err == nil {
  378. return nil
  379. }
  380. if !win32.RemoveDirectoryW(p) {
  381. err1 = _get_platform_error()
  382. }
  383. if err1 == nil {
  384. return nil
  385. }
  386. if err != err1 {
  387. a := win32.GetFileAttributesW(p)
  388. if a == ~u32(0) {
  389. err = _get_platform_error()
  390. } else {
  391. if a & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
  392. err = err1
  393. } else if a & win32.FILE_ATTRIBUTE_READONLY != 0 {
  394. if win32.SetFileAttributesW(p, a &~ win32.FILE_ATTRIBUTE_READONLY) {
  395. err = nil
  396. if !win32.DeleteFileW(p) {
  397. err = _get_platform_error()
  398. }
  399. }
  400. }
  401. }
  402. }
  403. return err
  404. }
  405. _rename :: proc(old_path, new_path: string) -> Error {
  406. from := _fix_long_path(old_path)
  407. to := _fix_long_path(new_path)
  408. if win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
  409. return nil
  410. }
  411. return _get_platform_error()
  412. }
  413. _link :: proc(old_name, new_name: string) -> Error {
  414. o := _fix_long_path(old_name)
  415. n := _fix_long_path(new_name)
  416. if win32.CreateHardLinkW(n, o, nil) {
  417. return nil
  418. }
  419. return _get_platform_error()
  420. }
  421. _symlink :: proc(old_name, new_name: string) -> Error {
  422. return .Unsupported
  423. }
  424. _open_sym_link :: proc(p: [^]u16) -> (handle: win32.HANDLE, err: Error) {
  425. attrs := u32(win32.FILE_FLAG_BACKUP_SEMANTICS)
  426. attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT
  427. handle = win32.CreateFileW(p, 0, 0, nil, win32.OPEN_EXISTING, attrs, nil)
  428. if handle == win32.INVALID_HANDLE {
  429. return nil, _get_platform_error()
  430. }
  431. return
  432. }
  433. _normalize_link_path :: proc(p: []u16, allocator: runtime.Allocator) -> (str: string, err: Error) {
  434. has_prefix :: proc(p: []u16, str: string) -> bool {
  435. if len(p) < len(str) {
  436. return false
  437. }
  438. // assume ascii
  439. for i in 0..<len(str) {
  440. if p[i] != u16(str[i]) {
  441. return false
  442. }
  443. }
  444. return true
  445. }
  446. has_unc_prefix :: proc(p: []u16) -> bool {
  447. return has_prefix(p, `\??\`)
  448. }
  449. if !has_unc_prefix(p) {
  450. return win32.utf16_to_utf8(p, allocator)
  451. }
  452. ws := p[4:]
  453. switch {
  454. case len(ws) >= 2 && ws[1] == ':':
  455. return win32.utf16_to_utf8(ws, allocator)
  456. case has_prefix(ws, `UNC\`):
  457. ws[3] = '\\' // override data in buffer
  458. return win32.utf16_to_utf8(ws[3:], allocator)
  459. }
  460. handle := _open_sym_link(raw_data(p)) or_return
  461. defer win32.CloseHandle(handle)
  462. n := win32.GetFinalPathNameByHandleW(handle, nil, 0, win32.VOLUME_NAME_DOS)
  463. if n == 0 {
  464. return "", _get_platform_error()
  465. }
  466. buf := make([]u16, n+1, _temp_allocator())
  467. n = win32.GetFinalPathNameByHandleW(handle, raw_data(buf), u32(len(buf)), win32.VOLUME_NAME_DOS)
  468. if n == 0 {
  469. return "", _get_platform_error()
  470. }
  471. ws = buf[:n]
  472. if has_unc_prefix(ws) {
  473. ws = ws[4:]
  474. if len(ws) > 3 && has_prefix(ws, `UNC`) {
  475. ws[2] = '\\'
  476. return win32.utf16_to_utf8(ws[2:], allocator)
  477. }
  478. return win32.utf16_to_utf8(ws, allocator)
  479. }
  480. return "", .Invalid_Path
  481. }
  482. _read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, err: Error) {
  483. MAXIMUM_REPARSE_DATA_BUFFER_SIZE :: 16 * 1024
  484. @thread_local
  485. rdb_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
  486. p := _fix_long_path(name)
  487. handle := _open_sym_link(p) or_return
  488. defer win32.CloseHandle(handle)
  489. bytes_returned: u32
  490. if !win32.DeviceIoControl(handle, win32.FSCTL_GET_REPARSE_POINT, nil, 0, &rdb_buf[0], len(rdb_buf)-1, &bytes_returned, nil) {
  491. err = _get_platform_error()
  492. return
  493. }
  494. mem.zero_slice(rdb_buf[:min(bytes_returned+1, len(rdb_buf))])
  495. rdb := (^win32.REPARSE_DATA_BUFFER)(&rdb_buf[0])
  496. switch rdb.ReparseTag {
  497. case win32.IO_REPARSE_TAG_SYMLINK:
  498. rb := (^win32.SYMBOLIC_LINK_REPARSE_BUFFER)(&rdb.rest)
  499. pb := win32.wstring(&rb.PathBuffer)
  500. pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0
  501. p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength]
  502. if rb.Flags & win32.SYMLINK_FLAG_RELATIVE != 0 {
  503. return win32.utf16_to_utf8(p, allocator)
  504. }
  505. return _normalize_link_path(p, allocator)
  506. case win32.IO_REPARSE_TAG_MOUNT_POINT:
  507. rb := (^win32.MOUNT_POINT_REPARSE_BUFFER)(&rdb.rest)
  508. pb := win32.wstring(&rb.PathBuffer)
  509. pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0
  510. p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength]
  511. return _normalize_link_path(p, allocator)
  512. }
  513. // Path wasn't a symlink/junction but another reparse point kind
  514. return "", nil
  515. }
  516. _fchdir :: proc(f: ^File) -> Error {
  517. if f == nil {
  518. return nil
  519. }
  520. if !win32.SetCurrentDirectoryW(f.impl.wname) {
  521. return _get_platform_error()
  522. }
  523. return nil
  524. }
  525. _fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
  526. if f == nil {
  527. return nil
  528. }
  529. d: win32.BY_HANDLE_FILE_INFORMATION
  530. if !win32.GetFileInformationByHandle(_handle(f), &d) {
  531. return _get_platform_error()
  532. }
  533. attrs := d.dwFileAttributes
  534. if mode & S_IWRITE != 0 {
  535. attrs &~= win32.FILE_ATTRIBUTE_READONLY
  536. } else {
  537. attrs |= win32.FILE_ATTRIBUTE_READONLY
  538. }
  539. info: win32.FILE_BASIC_INFO
  540. info.FileAttributes = attrs
  541. if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(d)) {
  542. return _get_platform_error()
  543. }
  544. return nil
  545. }
  546. _fchown :: proc(f: ^File, uid, gid: int) -> Error {
  547. return .Unsupported
  548. }
  549. _chdir :: proc(name: string) -> Error {
  550. p := _fix_long_path(name)
  551. if !win32.SetCurrentDirectoryW(p) {
  552. return _get_platform_error()
  553. }
  554. return nil
  555. }
  556. _chmod :: proc(name: string, mode: File_Mode) -> Error {
  557. f := open(name, {.Write}) or_return
  558. defer close(f)
  559. return _fchmod(f, mode)
  560. }
  561. _chown :: proc(name: string, uid, gid: int) -> Error {
  562. return .Unsupported
  563. }
  564. _lchown :: proc(name: string, uid, gid: int) -> Error {
  565. return .Unsupported
  566. }
  567. _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
  568. f := open(name, {.Write}) or_return
  569. defer close(f)
  570. return _fchtimes(f, atime, mtime)
  571. }
  572. _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
  573. if f == nil {
  574. return nil
  575. }
  576. d: win32.BY_HANDLE_FILE_INFORMATION
  577. if !win32.GetFileInformationByHandle(_handle(f), &d) {
  578. return _get_platform_error()
  579. }
  580. to_windows_time :: #force_inline proc(t: time.Time) -> win32.LARGE_INTEGER {
  581. // a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC)
  582. return win32.LARGE_INTEGER(time.time_to_unix_nano(t) * 100 + 116444736000000000)
  583. }
  584. atime, mtime := atime, mtime
  585. if time.time_to_unix_nano(atime) < time.time_to_unix_nano(mtime) {
  586. atime = mtime
  587. }
  588. info: win32.FILE_BASIC_INFO
  589. info.LastAccessTime = to_windows_time(atime)
  590. info.LastWriteTime = to_windows_time(mtime)
  591. if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(d)) {
  592. return _get_platform_error()
  593. }
  594. return nil
  595. }
  596. _exists :: proc(path: string) -> bool {
  597. wpath := _fix_long_path(path)
  598. attribs := win32.GetFileAttributesW(wpath)
  599. return i32(attribs) != win32.INVALID_FILE_ATTRIBUTES
  600. }
  601. _is_file :: proc(path: string) -> bool {
  602. wpath := _fix_long_path(path)
  603. attribs := win32.GetFileAttributesW(wpath)
  604. if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
  605. return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == 0
  606. }
  607. return false
  608. }
  609. _is_dir :: proc(path: string) -> bool {
  610. wpath := _fix_long_path(path)
  611. attribs := win32.GetFileAttributesW(wpath)
  612. if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
  613. return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != 0
  614. }
  615. return false
  616. }