file_windows.odin 19 KB

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