file_linux.odin 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. //+private
  2. package os2
  3. import "core:io"
  4. import "core:time"
  5. import "base:runtime"
  6. import "core:sys/linux"
  7. File_Impl :: struct {
  8. file: File,
  9. name: string,
  10. fd: linux.Fd,
  11. allocator: runtime.Allocator,
  12. }
  13. _stdin := File{
  14. impl = &File_Impl{
  15. name = "/proc/self/fd/0",
  16. fd = 0,
  17. allocator = _file_allocator(),
  18. },
  19. stream = {
  20. procedure = _file_stream_proc,
  21. },
  22. fstat = _fstat,
  23. }
  24. _stdout := File{
  25. impl = &File_Impl{
  26. name = "/proc/self/fd/1",
  27. fd = 1,
  28. allocator = _file_allocator(),
  29. },
  30. stream = {
  31. procedure = _file_stream_proc,
  32. },
  33. fstat = _fstat,
  34. }
  35. _stderr := File{
  36. impl = &File_Impl{
  37. name = "/proc/self/fd/2",
  38. fd = 2,
  39. allocator = _file_allocator(),
  40. },
  41. stream = {
  42. procedure = _file_stream_proc,
  43. },
  44. fstat = _fstat,
  45. }
  46. @init
  47. _standard_stream_init :: proc() {
  48. // cannot define these manually because cyclic reference
  49. _stdin.stream.data = &_stdin
  50. _stdout.stream.data = &_stdout
  51. _stderr.stream.data = &_stderr
  52. stdin = &_stdin
  53. stdout = &_stdout
  54. stderr = &_stderr
  55. }
  56. _file_allocator :: proc() -> runtime.Allocator {
  57. return heap_allocator()
  58. }
  59. _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
  60. TEMP_ALLOCATOR_GUARD()
  61. name_cstr := temp_cstring(name) or_return
  62. // Just default to using O_NOCTTY because needing to open a controlling
  63. // terminal would be incredibly rare. This has no effect on files while
  64. // allowing us to open serial devices.
  65. sys_flags: linux.Open_Flags = {.NOCTTY, .CLOEXEC}
  66. switch flags & O_RDONLY|O_WRONLY|O_RDWR {
  67. case O_RDONLY:
  68. case O_WRONLY: sys_flags += {.WRONLY}
  69. case O_RDWR: sys_flags += {.RDWR}
  70. }
  71. if .Append in flags { sys_flags += {.APPEND} }
  72. if .Create in flags { sys_flags += {.CREAT} }
  73. if .Excl in flags { sys_flags += {.EXCL} }
  74. if .Sync in flags { sys_flags += {.DSYNC} }
  75. if .Trunc in flags { sys_flags += {.TRUNC} }
  76. if .Inheritable in flags { sys_flags -= {.CLOEXEC} }
  77. fd, errno := linux.open(name_cstr, sys_flags, transmute(linux.Mode)u32(perm))
  78. if errno != .NONE {
  79. return nil, _get_platform_error(errno)
  80. }
  81. return _new_file(uintptr(fd), name)
  82. }
  83. _new_file :: proc(fd: uintptr, _: string = "") -> (f: ^File, err: Error) {
  84. impl := new(File_Impl, file_allocator()) or_return
  85. defer if err != nil {
  86. free(impl, file_allocator())
  87. }
  88. impl.file.impl = impl
  89. impl.fd = linux.Fd(fd)
  90. impl.allocator = file_allocator()
  91. impl.name = _get_full_path(impl.fd, file_allocator()) or_return
  92. impl.file.stream = {
  93. data = impl,
  94. procedure = _file_stream_proc,
  95. }
  96. impl.file.fstat = _fstat
  97. return &impl.file, nil
  98. }
  99. _destroy :: proc(f: ^File_Impl) -> Error {
  100. if f == nil {
  101. return nil
  102. }
  103. a := f.allocator
  104. delete(f.name, a)
  105. free(f, a)
  106. return nil
  107. }
  108. _close :: proc(f: ^File_Impl) -> Error {
  109. if f == nil{
  110. return nil
  111. }
  112. errno := linux.close(f.fd)
  113. if errno == .EBADF { // avoid possible double free
  114. return _get_platform_error(errno)
  115. }
  116. _destroy(f)
  117. return _get_platform_error(errno)
  118. }
  119. _fd :: proc(f: ^File) -> uintptr {
  120. if f == nil || f.impl == nil {
  121. return ~uintptr(0)
  122. }
  123. impl := (^File_Impl)(f.impl)
  124. return uintptr(impl.fd)
  125. }
  126. _name :: proc(f: ^File) -> string {
  127. return (^File_Impl)(f.impl).name if f != nil && f.impl != nil else ""
  128. }
  129. _seek :: proc(f: ^File_Impl, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
  130. n, errno := linux.lseek(f.fd, offset, linux.Seek_Whence(whence))
  131. if errno != .NONE {
  132. return -1, _get_platform_error(errno)
  133. }
  134. return n, nil
  135. }
  136. _read :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) {
  137. if len(p) == 0 {
  138. return 0, nil
  139. }
  140. n, errno := linux.read(f.fd, p[:])
  141. if errno != .NONE {
  142. return -1, _get_platform_error(errno)
  143. }
  144. return i64(n), io.Error.EOF if n == 0 else nil
  145. }
  146. _read_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (i64, Error) {
  147. if offset < 0 {
  148. return 0, .Invalid_Offset
  149. }
  150. n, errno := linux.pread(f.fd, p[:], offset)
  151. if errno != .NONE {
  152. return -1, _get_platform_error(errno)
  153. }
  154. if n == 0 {
  155. return 0, .EOF
  156. }
  157. return i64(n), nil
  158. }
  159. _write :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) {
  160. if len(p) == 0 {
  161. return 0, nil
  162. }
  163. n, errno := linux.write(f.fd, p[:])
  164. if errno != .NONE {
  165. return -1, _get_platform_error(errno)
  166. }
  167. return i64(n), nil
  168. }
  169. _write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (i64, Error) {
  170. if offset < 0 {
  171. return 0, .Invalid_Offset
  172. }
  173. n, errno := linux.pwrite(f.fd, p[:], offset)
  174. if errno != .NONE {
  175. return -1, _get_platform_error(errno)
  176. }
  177. return i64(n), nil
  178. }
  179. _file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) {
  180. s: linux.Stat = ---
  181. errno := linux.fstat(f.fd, &s)
  182. if errno != .NONE {
  183. return -1, _get_platform_error(errno)
  184. }
  185. return i64(s.size), nil
  186. }
  187. _sync :: proc(f: ^File) -> Error {
  188. impl := (^File_Impl)(f.impl)
  189. return _get_platform_error(linux.fsync(impl.fd))
  190. }
  191. _flush :: proc(f: ^File_Impl) -> Error {
  192. return _get_platform_error(linux.fsync(f.fd))
  193. }
  194. _truncate :: proc(f: ^File, size: i64) -> Error {
  195. impl := (^File_Impl)(f.impl)
  196. return _get_platform_error(linux.ftruncate(impl.fd, size))
  197. }
  198. _remove :: proc(name: string) -> Error {
  199. is_dir_fd :: proc(fd: linux.Fd) -> bool {
  200. s: linux.Stat
  201. if linux.fstat(fd, &s) != .NONE {
  202. return false
  203. }
  204. return linux.S_ISDIR(s.mode)
  205. }
  206. TEMP_ALLOCATOR_GUARD()
  207. name_cstr := temp_cstring(name) or_return
  208. fd, errno := linux.open(name_cstr, {.NOFOLLOW})
  209. #partial switch (errno) {
  210. case .ELOOP:
  211. /* symlink */
  212. case .NONE:
  213. defer linux.close(fd)
  214. if is_dir_fd(fd) {
  215. return _get_platform_error(linux.rmdir(name_cstr))
  216. }
  217. case:
  218. return _get_platform_error(errno)
  219. }
  220. return _get_platform_error(linux.unlink(name_cstr))
  221. }
  222. _rename :: proc(old_name, new_name: string) -> Error {
  223. TEMP_ALLOCATOR_GUARD()
  224. old_name_cstr := temp_cstring(old_name) or_return
  225. new_name_cstr := temp_cstring(new_name) or_return
  226. return _get_platform_error(linux.rename(old_name_cstr, new_name_cstr))
  227. }
  228. _link :: proc(old_name, new_name: string) -> Error {
  229. TEMP_ALLOCATOR_GUARD()
  230. old_name_cstr := temp_cstring(old_name) or_return
  231. new_name_cstr := temp_cstring(new_name) or_return
  232. return _get_platform_error(linux.link(old_name_cstr, new_name_cstr))
  233. }
  234. _symlink :: proc(old_name, new_name: string) -> Error {
  235. TEMP_ALLOCATOR_GUARD()
  236. old_name_cstr := temp_cstring(old_name) or_return
  237. new_name_cstr := temp_cstring(new_name) or_return
  238. return _get_platform_error(linux.symlink(old_name_cstr, new_name_cstr))
  239. }
  240. _read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (string, Error) {
  241. bufsz : uint = 256
  242. buf := make([]byte, bufsz, allocator)
  243. for {
  244. sz, errno := linux.readlink(name_cstr, buf[:])
  245. if errno != .NONE {
  246. delete(buf, allocator)
  247. return "", _get_platform_error(errno)
  248. } else if sz == int(bufsz) {
  249. bufsz *= 2
  250. delete(buf, allocator)
  251. buf = make([]byte, bufsz, allocator)
  252. } else {
  253. return string(buf[:sz]), nil
  254. }
  255. }
  256. }
  257. _read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, e: Error) {
  258. TEMP_ALLOCATOR_GUARD()
  259. name_cstr := temp_cstring(name) or_return
  260. return _read_link_cstr(name_cstr, allocator)
  261. }
  262. _chdir :: proc(name: string) -> Error {
  263. TEMP_ALLOCATOR_GUARD()
  264. name_cstr := temp_cstring(name) or_return
  265. return _get_platform_error(linux.chdir(name_cstr))
  266. }
  267. _fchdir :: proc(f: ^File) -> Error {
  268. impl := (^File_Impl)(f.impl)
  269. return _get_platform_error(linux.fchdir(impl.fd))
  270. }
  271. _chmod :: proc(name: string, mode: int) -> Error {
  272. TEMP_ALLOCATOR_GUARD()
  273. name_cstr := temp_cstring(name) or_return
  274. return _get_platform_error(linux.chmod(name_cstr, transmute(linux.Mode)(u32(mode))))
  275. }
  276. _fchmod :: proc(f: ^File, mode: int) -> Error {
  277. impl := (^File_Impl)(f.impl)
  278. return _get_platform_error(linux.fchmod(impl.fd, transmute(linux.Mode)(u32(mode))))
  279. }
  280. // NOTE: will throw error without super user priviledges
  281. _chown :: proc(name: string, uid, gid: int) -> Error {
  282. TEMP_ALLOCATOR_GUARD()
  283. name_cstr := temp_cstring(name) or_return
  284. return _get_platform_error(linux.chown(name_cstr, linux.Uid(uid), linux.Gid(gid)))
  285. }
  286. // NOTE: will throw error without super user priviledges
  287. _lchown :: proc(name: string, uid, gid: int) -> Error {
  288. TEMP_ALLOCATOR_GUARD()
  289. name_cstr := temp_cstring(name) or_return
  290. return _get_platform_error(linux.lchown(name_cstr, linux.Uid(uid), linux.Gid(gid)))
  291. }
  292. // NOTE: will throw error without super user priviledges
  293. _fchown :: proc(f: ^File, uid, gid: int) -> Error {
  294. impl := (^File_Impl)(f.impl)
  295. return _get_platform_error(linux.fchown(impl.fd, linux.Uid(uid), linux.Gid(gid)))
  296. }
  297. _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
  298. TEMP_ALLOCATOR_GUARD()
  299. name_cstr := temp_cstring(name) or_return
  300. times := [2]linux.Time_Spec {
  301. {
  302. uint(atime._nsec) / uint(time.Second),
  303. uint(atime._nsec) % uint(time.Second),
  304. },
  305. {
  306. uint(mtime._nsec) / uint(time.Second),
  307. uint(mtime._nsec) % uint(time.Second),
  308. },
  309. }
  310. return _get_platform_error(linux.utimensat(linux.AT_FDCWD, name_cstr, &times[0], nil))
  311. }
  312. _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
  313. times := [2]linux.Time_Spec {
  314. {
  315. uint(atime._nsec) / uint(time.Second),
  316. uint(atime._nsec) % uint(time.Second),
  317. },
  318. {
  319. uint(mtime._nsec) / uint(time.Second),
  320. uint(mtime._nsec) % uint(time.Second),
  321. },
  322. }
  323. impl := (^File_Impl)(f.impl)
  324. return _get_platform_error(linux.utimensat(impl.fd, nil, &times[0], nil))
  325. }
  326. _exists :: proc(name: string) -> bool {
  327. TEMP_ALLOCATOR_GUARD()
  328. name_cstr, _ := temp_cstring(name)
  329. res, errno := linux.access(name_cstr, linux.F_OK)
  330. return !res && errno == .NONE
  331. }
  332. /* Certain files in the Linux file system are not actual
  333. * files (e.g. everything in /proc/). Therefore, the
  334. * read_entire_file procs fail to actually read anything
  335. * since these "files" stat to a size of 0. Here, we just
  336. * read until there is nothing left.
  337. */
  338. _read_entire_pseudo_file :: proc { _read_entire_pseudo_file_string, _read_entire_pseudo_file_cstring }
  339. _read_entire_pseudo_file_string :: proc(name: string, allocator: runtime.Allocator) -> (b: []u8, e: Error) {
  340. name_cstr := clone_to_cstring(name, allocator) or_return
  341. defer delete(name, allocator)
  342. return _read_entire_pseudo_file_cstring(name_cstr, allocator)
  343. }
  344. _read_entire_pseudo_file_cstring :: proc(name: cstring, allocator: runtime.Allocator) -> ([]u8, Error) {
  345. fd, errno := linux.open(name, {})
  346. if errno != .NONE {
  347. return nil, _get_platform_error(errno)
  348. }
  349. defer linux.close(fd)
  350. BUF_SIZE_STEP :: 128
  351. contents := make([dynamic]u8, 0, BUF_SIZE_STEP, allocator)
  352. n: int
  353. i: int
  354. for {
  355. resize(&contents, i + BUF_SIZE_STEP)
  356. n, errno = linux.read(fd, contents[i:i+BUF_SIZE_STEP])
  357. if errno != .NONE {
  358. delete(contents)
  359. return nil, _get_platform_error(errno)
  360. }
  361. if n < BUF_SIZE_STEP {
  362. break
  363. }
  364. i += BUF_SIZE_STEP
  365. }
  366. resize(&contents, i + n)
  367. return contents[:], nil
  368. }
  369. @(private="package")
  370. _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
  371. f := (^File_Impl)(stream_data)
  372. ferr: Error
  373. switch mode {
  374. case .Read:
  375. n, ferr = _read(f, p)
  376. err = error_to_io_error(ferr)
  377. return
  378. case .Read_At:
  379. n, ferr = _read_at(f, p, offset)
  380. err = error_to_io_error(ferr)
  381. return
  382. case .Write:
  383. n, ferr = _write(f, p)
  384. err = error_to_io_error(ferr)
  385. return
  386. case .Write_At:
  387. n, ferr = _write_at(f, p, offset)
  388. err = error_to_io_error(ferr)
  389. return
  390. case .Seek:
  391. n, ferr = _seek(f, offset, whence)
  392. err = error_to_io_error(ferr)
  393. return
  394. case .Size:
  395. n, ferr = _file_size(f)
  396. err = error_to_io_error(ferr)
  397. return
  398. case .Flush:
  399. ferr = _flush(f)
  400. err = error_to_io_error(ferr)
  401. return
  402. case .Close, .Destroy:
  403. ferr = _close(f)
  404. err = error_to_io_error(ferr)
  405. return
  406. case .Query:
  407. return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
  408. }
  409. return 0, .Empty
  410. }