file_windows.odin 18 KB

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