file_linux.odin 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. #+private
  2. package os2
  3. import "base:runtime"
  4. import "core:io"
  5. import "core:time"
  6. import "core:sync"
  7. import "core:sys/linux"
  8. // Most implementations will EINVAL at some point when doing big writes.
  9. // In practice a read/write call would probably never read/write these big buffers all at once,
  10. // which is why the number of bytes is returned and why there are procs that will call this in a
  11. // loop for you.
  12. // We set a max of 1GB to keep alignment and to be safe.
  13. MAX_RW :: 1 << 30
  14. File_Impl :: struct {
  15. file: File,
  16. name: string,
  17. fd: linux.Fd,
  18. allocator: runtime.Allocator,
  19. buffer: []byte,
  20. rw_mutex: sync.RW_Mutex, // read write calls
  21. p_mutex: sync.Mutex, // pread pwrite calls
  22. }
  23. _stdin := File{
  24. stream = {
  25. procedure = _file_stream_proc,
  26. },
  27. fstat = _fstat,
  28. }
  29. _stdout := File{
  30. stream = {
  31. procedure = _file_stream_proc,
  32. },
  33. fstat = _fstat,
  34. }
  35. _stderr := File{
  36. stream = {
  37. procedure = _file_stream_proc,
  38. },
  39. fstat = _fstat,
  40. }
  41. @init
  42. _standard_stream_init :: proc() {
  43. new_std :: proc(impl: ^File_Impl, fd: linux.Fd, name: string) -> ^File {
  44. impl.file.impl = impl
  45. impl.fd = linux.Fd(fd)
  46. impl.allocator = runtime.nil_allocator()
  47. impl.name = name
  48. impl.file.stream = {
  49. data = impl,
  50. procedure = _file_stream_proc,
  51. }
  52. impl.file.fstat = _fstat
  53. return &impl.file
  54. }
  55. @(static) files: [3]File_Impl
  56. stdin = new_std(&files[0], 0, "/proc/self/fd/0")
  57. stdout = new_std(&files[1], 1, "/proc/self/fd/1")
  58. stderr = new_std(&files[2], 2, "/proc/self/fd/2")
  59. }
  60. _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
  61. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  62. name_cstr := clone_to_cstring(name, temp_allocator) or_return
  63. // Just default to using O_NOCTTY because needing to open a controlling
  64. // terminal would be incredibly rare. This has no effect on files while
  65. // allowing us to open serial devices.
  66. sys_flags: linux.Open_Flags = {.NOCTTY, .CLOEXEC}
  67. when size_of(rawptr) == 4 {
  68. sys_flags += {.LARGEFILE}
  69. }
  70. switch flags & (O_RDONLY|O_WRONLY|O_RDWR) {
  71. case O_RDONLY:
  72. case O_WRONLY: sys_flags += {.WRONLY}
  73. case O_RDWR: sys_flags += {.RDWR}
  74. }
  75. if .Append in flags { sys_flags += {.APPEND} }
  76. if .Create in flags { sys_flags += {.CREAT} }
  77. if .Excl in flags { sys_flags += {.EXCL} }
  78. if .Sync in flags { sys_flags += {.DSYNC} }
  79. if .Trunc in flags { sys_flags += {.TRUNC} }
  80. if .Inheritable in flags { sys_flags -= {.CLOEXEC} }
  81. fd, errno := linux.open(name_cstr, sys_flags, transmute(linux.Mode)u32(perm))
  82. if errno != .NONE {
  83. return nil, _get_platform_error(errno)
  84. }
  85. return _new_file(uintptr(fd), name, file_allocator())
  86. }
  87. _new_file :: proc(fd: uintptr, _: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
  88. impl := new(File_Impl, allocator) or_return
  89. defer if err != nil {
  90. free(impl, allocator)
  91. }
  92. impl.file.impl = impl
  93. impl.fd = linux.Fd(fd)
  94. impl.allocator = allocator
  95. impl.name = _get_full_path(impl.fd, impl.allocator) or_return
  96. impl.file.stream = {
  97. data = impl,
  98. procedure = _file_stream_proc,
  99. }
  100. impl.file.fstat = _fstat
  101. return &impl.file, nil
  102. }
  103. _clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
  104. if f == nil || f.impl == nil {
  105. return
  106. }
  107. fd := (^File_Impl)(f.impl).fd
  108. clonefd, errno := linux.dup(fd)
  109. if errno != nil {
  110. err = _get_platform_error(errno)
  111. return
  112. }
  113. defer if err != nil { linux.close(clonefd) }
  114. return _new_file(uintptr(clonefd), "", file_allocator())
  115. }
  116. @(require_results)
  117. _open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm := 0o777) -> (f: ^File, err: Error) {
  118. assert(buffer_size > 0)
  119. f, err = _open(name, flags, perm)
  120. if f != nil && err == nil {
  121. impl := (^File_Impl)(f.impl)
  122. impl.buffer = make([]byte, buffer_size, file_allocator())
  123. f.stream.procedure = _file_stream_buffered_proc
  124. }
  125. return
  126. }
  127. _destroy :: proc(f: ^File_Impl) -> Error {
  128. if f == nil {
  129. return nil
  130. }
  131. a := f.allocator
  132. err0 := delete(f.name, a)
  133. err1 := delete(f.buffer, a)
  134. err2 := free(f, a)
  135. err0 or_return
  136. err1 or_return
  137. err2 or_return
  138. return nil
  139. }
  140. _close :: proc(f: ^File_Impl) -> Error {
  141. if f == nil{
  142. return nil
  143. }
  144. errno := linux.close(f.fd)
  145. if errno == .EBADF { // avoid possible double free
  146. return _get_platform_error(errno)
  147. }
  148. _destroy(f)
  149. return _get_platform_error(errno)
  150. }
  151. _fd :: proc(f: ^File) -> uintptr {
  152. if f == nil || f.impl == nil {
  153. return ~uintptr(0)
  154. }
  155. impl := (^File_Impl)(f.impl)
  156. return uintptr(impl.fd)
  157. }
  158. _name :: proc(f: ^File) -> string {
  159. return (^File_Impl)(f.impl).name if f != nil && f.impl != nil else ""
  160. }
  161. _seek :: proc(f: ^File_Impl, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
  162. // We have to handle this here, because Linux returns EINVAL for both
  163. // invalid offsets and invalid whences.
  164. switch whence {
  165. case .Start, .Current, .End:
  166. break
  167. case:
  168. return 0, .Invalid_Whence
  169. }
  170. n, errno := linux.lseek(f.fd, offset, linux.Seek_Whence(whence))
  171. #partial switch errno {
  172. case .EINVAL:
  173. return 0, .Invalid_Offset
  174. case .NONE:
  175. return n, nil
  176. case:
  177. return -1, _get_platform_error(errno)
  178. }
  179. }
  180. _read :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) {
  181. if len(p) <= 0 {
  182. return 0, nil
  183. }
  184. n, errno := linux.read(f.fd, p[:min(len(p), MAX_RW)])
  185. if errno != .NONE {
  186. return -1, _get_platform_error(errno)
  187. }
  188. return i64(n), io.Error.EOF if n == 0 else nil
  189. }
  190. _read_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (i64, Error) {
  191. if len(p) <= 0 {
  192. return 0, nil
  193. }
  194. if offset < 0 {
  195. return 0, .Invalid_Offset
  196. }
  197. n, errno := linux.pread(f.fd, p[:min(len(p), MAX_RW)], offset)
  198. if errno != .NONE {
  199. return -1, _get_platform_error(errno)
  200. }
  201. if n == 0 {
  202. return 0, .EOF
  203. }
  204. return i64(n), nil
  205. }
  206. _write :: proc(f: ^File_Impl, p: []byte) -> (nt: i64, err: Error) {
  207. p := p
  208. for len(p) > 0 {
  209. n, errno := linux.write(f.fd, p[:min(len(p), MAX_RW)])
  210. if errno != .NONE {
  211. err = _get_platform_error(errno)
  212. return
  213. }
  214. p = p[n:]
  215. nt += i64(n)
  216. }
  217. return
  218. }
  219. _write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (nt: i64, err: Error) {
  220. if offset < 0 {
  221. return 0, .Invalid_Offset
  222. }
  223. p := p
  224. offset := offset
  225. for len(p) > 0 {
  226. n, errno := linux.pwrite(f.fd, p[:min(len(p), MAX_RW)], offset)
  227. if errno != .NONE {
  228. err = _get_platform_error(errno)
  229. return
  230. }
  231. p = p[n:]
  232. nt += i64(n)
  233. offset += i64(n)
  234. }
  235. return
  236. }
  237. @(no_sanitize_memory)
  238. _file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) {
  239. // TODO: Identify 0-sized "pseudo" files and return No_Size. This would
  240. // eliminate the need for the _read_entire_pseudo_file procs.
  241. s: linux.Stat = ---
  242. errno := linux.fstat(f.fd, &s)
  243. if errno != .NONE {
  244. return -1, _get_platform_error(errno)
  245. }
  246. if s.mode & linux.S_IFMT == linux.S_IFREG {
  247. return i64(s.size), nil
  248. }
  249. return 0, .No_Size
  250. }
  251. _sync :: proc(f: ^File) -> Error {
  252. impl := (^File_Impl)(f.impl)
  253. return _get_platform_error(linux.fsync(impl.fd))
  254. }
  255. _flush :: proc(f: ^File_Impl) -> Error {
  256. return _get_platform_error(linux.fsync(f.fd))
  257. }
  258. _truncate :: proc(f: ^File, size: i64) -> Error {
  259. impl := (^File_Impl)(f.impl)
  260. return _get_platform_error(linux.ftruncate(impl.fd, size))
  261. }
  262. _remove :: proc(name: string) -> Error {
  263. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  264. name_cstr := clone_to_cstring(name, temp_allocator) or_return
  265. if fd, errno := linux.open(name_cstr, _OPENDIR_FLAGS + {.NOFOLLOW}); errno == .NONE {
  266. linux.close(fd)
  267. return _get_platform_error(linux.rmdir(name_cstr))
  268. }
  269. return _get_platform_error(linux.unlink(name_cstr))
  270. }
  271. _rename :: proc(old_name, new_name: string) -> Error {
  272. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  273. old_name_cstr := clone_to_cstring(old_name, temp_allocator) or_return
  274. new_name_cstr := clone_to_cstring(new_name, temp_allocator) or_return
  275. return _get_platform_error(linux.rename(old_name_cstr, new_name_cstr))
  276. }
  277. _link :: proc(old_name, new_name: string) -> Error {
  278. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  279. old_name_cstr := clone_to_cstring(old_name, temp_allocator) or_return
  280. new_name_cstr := clone_to_cstring(new_name, temp_allocator) or_return
  281. return _get_platform_error(linux.link(old_name_cstr, new_name_cstr))
  282. }
  283. _symlink :: proc(old_name, new_name: string) -> Error {
  284. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  285. old_name_cstr := clone_to_cstring(old_name, temp_allocator) or_return
  286. new_name_cstr := clone_to_cstring(new_name, temp_allocator) or_return
  287. return _get_platform_error(linux.symlink(old_name_cstr, new_name_cstr))
  288. }
  289. _read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (string, Error) {
  290. bufsz : uint = 256
  291. buf := make([]byte, bufsz, allocator)
  292. for {
  293. sz, errno := linux.readlink(name_cstr, buf[:])
  294. if errno != .NONE {
  295. delete(buf, allocator)
  296. return "", _get_platform_error(errno)
  297. } else if sz == int(bufsz) {
  298. bufsz *= 2
  299. delete(buf, allocator)
  300. buf = make([]byte, bufsz, allocator)
  301. } else {
  302. return string(buf[:sz]), nil
  303. }
  304. }
  305. }
  306. _read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, e: Error) {
  307. temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
  308. name_cstr := clone_to_cstring(name, temp_allocator) or_return
  309. return _read_link_cstr(name_cstr, allocator)
  310. }
  311. _chdir :: proc(name: string) -> Error {
  312. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  313. name_cstr := clone_to_cstring(name, temp_allocator) or_return
  314. return _get_platform_error(linux.chdir(name_cstr))
  315. }
  316. _fchdir :: proc(f: ^File) -> Error {
  317. impl := (^File_Impl)(f.impl)
  318. return _get_platform_error(linux.fchdir(impl.fd))
  319. }
  320. _chmod :: proc(name: string, mode: int) -> Error {
  321. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  322. name_cstr := clone_to_cstring(name, temp_allocator) or_return
  323. return _get_platform_error(linux.chmod(name_cstr, transmute(linux.Mode)(u32(mode))))
  324. }
  325. _fchmod :: proc(f: ^File, mode: int) -> Error {
  326. impl := (^File_Impl)(f.impl)
  327. return _get_platform_error(linux.fchmod(impl.fd, transmute(linux.Mode)(u32(mode))))
  328. }
  329. // NOTE: will throw error without super user priviledges
  330. _chown :: proc(name: string, uid, gid: int) -> Error {
  331. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  332. name_cstr := clone_to_cstring(name, temp_allocator) or_return
  333. return _get_platform_error(linux.chown(name_cstr, linux.Uid(uid), linux.Gid(gid)))
  334. }
  335. // NOTE: will throw error without super user priviledges
  336. _lchown :: proc(name: string, uid, gid: int) -> Error {
  337. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  338. name_cstr := clone_to_cstring(name, temp_allocator) or_return
  339. return _get_platform_error(linux.lchown(name_cstr, linux.Uid(uid), linux.Gid(gid)))
  340. }
  341. // NOTE: will throw error without super user priviledges
  342. _fchown :: proc(f: ^File, uid, gid: int) -> Error {
  343. impl := (^File_Impl)(f.impl)
  344. return _get_platform_error(linux.fchown(impl.fd, linux.Uid(uid), linux.Gid(gid)))
  345. }
  346. _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
  347. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  348. name_cstr := clone_to_cstring(name, temp_allocator) or_return
  349. times := [2]linux.Time_Spec {
  350. {
  351. uint(atime._nsec) / uint(time.Second),
  352. uint(atime._nsec) % uint(time.Second),
  353. },
  354. {
  355. uint(mtime._nsec) / uint(time.Second),
  356. uint(mtime._nsec) % uint(time.Second),
  357. },
  358. }
  359. return _get_platform_error(linux.utimensat(linux.AT_FDCWD, name_cstr, &times[0], nil))
  360. }
  361. _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
  362. times := [2]linux.Time_Spec {
  363. {
  364. uint(atime._nsec) / uint(time.Second),
  365. uint(atime._nsec) % uint(time.Second),
  366. },
  367. {
  368. uint(mtime._nsec) / uint(time.Second),
  369. uint(mtime._nsec) % uint(time.Second),
  370. },
  371. }
  372. impl := (^File_Impl)(f.impl)
  373. return _get_platform_error(linux.utimensat(impl.fd, nil, &times[0], nil))
  374. }
  375. _exists :: proc(name: string) -> bool {
  376. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  377. name_cstr, _ := clone_to_cstring(name, temp_allocator)
  378. return linux.access(name_cstr, linux.F_OK) == .NONE
  379. }
  380. /* For reading Linux system files that stat to size 0 */
  381. _read_entire_pseudo_file :: proc { _read_entire_pseudo_file_string, _read_entire_pseudo_file_cstring }
  382. _read_entire_pseudo_file_string :: proc(name: string, allocator: runtime.Allocator) -> (b: []u8, e: Error) {
  383. temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
  384. name_cstr := clone_to_cstring(name, temp_allocator) or_return
  385. return _read_entire_pseudo_file_cstring(name_cstr, allocator)
  386. }
  387. _read_entire_pseudo_file_cstring :: proc(name: cstring, allocator: runtime.Allocator) -> ([]u8, Error) {
  388. fd, errno := linux.open(name, {})
  389. if errno != .NONE {
  390. return nil, _get_platform_error(errno)
  391. }
  392. defer linux.close(fd)
  393. BUF_SIZE_STEP :: 128
  394. contents := make([dynamic]u8, 0, BUF_SIZE_STEP, allocator)
  395. n: int
  396. i: int
  397. for {
  398. resize(&contents, i + BUF_SIZE_STEP)
  399. n, errno = linux.read(fd, contents[i:i+BUF_SIZE_STEP])
  400. if errno != .NONE {
  401. delete(contents)
  402. return nil, _get_platform_error(errno)
  403. }
  404. if n < BUF_SIZE_STEP {
  405. break
  406. }
  407. i += BUF_SIZE_STEP
  408. }
  409. resize(&contents, i + n)
  410. return contents[:], nil
  411. }
  412. @(private="package")
  413. _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
  414. f := (^File_Impl)(stream_data)
  415. ferr: Error
  416. switch mode {
  417. case .Read:
  418. n, ferr = _read(f, p)
  419. err = error_to_io_error(ferr)
  420. return
  421. case .Read_At:
  422. n, ferr = _read_at(f, p, offset)
  423. err = error_to_io_error(ferr)
  424. return
  425. case .Write:
  426. n, ferr = _write(f, p)
  427. err = error_to_io_error(ferr)
  428. return
  429. case .Write_At:
  430. n, ferr = _write_at(f, p, offset)
  431. err = error_to_io_error(ferr)
  432. return
  433. case .Seek:
  434. n, ferr = _seek(f, offset, whence)
  435. err = error_to_io_error(ferr)
  436. return
  437. case .Size:
  438. n, ferr = _file_size(f)
  439. err = error_to_io_error(ferr)
  440. return
  441. case .Flush:
  442. ferr = _flush(f)
  443. err = error_to_io_error(ferr)
  444. return
  445. case .Close, .Destroy:
  446. ferr = _close(f)
  447. err = error_to_io_error(ferr)
  448. return
  449. case .Query:
  450. return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
  451. }
  452. return 0, .Empty
  453. }
  454. @(private="package")
  455. _file_stream_buffered_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
  456. f := (^File_Impl)(stream_data)
  457. ferr: Error
  458. switch mode {
  459. case .Read:
  460. n, ferr = _read(f, p)
  461. err = error_to_io_error(ferr)
  462. return
  463. case .Read_At:
  464. n, ferr = _read_at(f, p, offset)
  465. err = error_to_io_error(ferr)
  466. return
  467. case .Write:
  468. n, ferr = _write(f, p)
  469. err = error_to_io_error(ferr)
  470. return
  471. case .Write_At:
  472. n, ferr = _write_at(f, p, offset)
  473. err = error_to_io_error(ferr)
  474. return
  475. case .Seek:
  476. n, ferr = _seek(f, offset, whence)
  477. err = error_to_io_error(ferr)
  478. return
  479. case .Size:
  480. n, ferr = _file_size(f)
  481. err = error_to_io_error(ferr)
  482. return
  483. case .Flush:
  484. ferr = _flush(f)
  485. err = error_to_io_error(ferr)
  486. return
  487. case .Close, .Destroy:
  488. ferr = _close(f)
  489. err = error_to_io_error(ferr)
  490. return
  491. case .Query:
  492. return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
  493. }
  494. return 0, .Empty
  495. }