file_linux.odin 13 KB

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