file_windows.odin 18 KB

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