file_linux.odin 14 KB

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