file_linux.odin 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. //+private
  2. package os2
  3. import "core:io"
  4. import "core:time"
  5. import "core:strings"
  6. import "core:runtime"
  7. import "core:sys/unix"
  8. INVALID_HANDLE :: -1
  9. _O_RDONLY :: 0o0
  10. _O_WRONLY :: 0o1
  11. _O_RDWR :: 0o2
  12. _O_CREAT :: 0o100
  13. _O_EXCL :: 0o200
  14. _O_TRUNC :: 0o1000
  15. _O_APPEND :: 0o2000
  16. _O_NONBLOCK :: 0o4000
  17. _O_LARGEFILE :: 0o100000
  18. _O_DIRECTORY :: 0o200000
  19. _O_NOFOLLOW :: 0o400000
  20. _O_SYNC :: 0o4010000
  21. _O_CLOEXEC :: 0o2000000
  22. _O_PATH :: 0o10000000
  23. _AT_FDCWD :: -100
  24. _CSTRING_NAME_HEAP_THRESHOLD :: 512
  25. _File :: struct {
  26. name: string,
  27. fd: int,
  28. allocator: runtime.Allocator,
  29. }
  30. _file_allocator :: proc() -> runtime.Allocator {
  31. return heap_allocator()
  32. }
  33. _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error) {
  34. name_cstr, allocated := _name_to_cstring(name)
  35. defer if allocated {
  36. delete(name_cstr)
  37. }
  38. flags_i: int
  39. switch flags & O_RDONLY|O_WRONLY|O_RDWR {
  40. case O_RDONLY: flags_i = _O_RDONLY
  41. case O_WRONLY: flags_i = _O_WRONLY
  42. case O_RDWR: flags_i = _O_RDWR
  43. }
  44. flags_i |= (_O_APPEND * int(.Append in flags))
  45. flags_i |= (_O_CREAT * int(.Create in flags))
  46. flags_i |= (_O_EXCL * int(.Excl in flags))
  47. flags_i |= (_O_SYNC * int(.Sync in flags))
  48. flags_i |= (_O_TRUNC * int(.Trunc in flags))
  49. flags_i |= (_O_CLOEXEC * int(.Close_On_Exec in flags))
  50. fd := unix.sys_open(name_cstr, flags_i, int(perm))
  51. if fd < 0 {
  52. return nil, _get_platform_error(fd)
  53. }
  54. return _new_file(uintptr(fd), name), nil
  55. }
  56. _new_file :: proc(fd: uintptr, _: string) -> ^File {
  57. file := new(File, _file_allocator())
  58. file.impl.fd = int(fd)
  59. file.impl.allocator = _file_allocator()
  60. file.impl.name = _get_full_path(file.impl.fd, file.impl.allocator)
  61. return file
  62. }
  63. _destroy :: proc(f: ^File) -> Error {
  64. if f == nil {
  65. return nil
  66. }
  67. delete(f.impl.name, f.impl.allocator)
  68. free(f, f.impl.allocator)
  69. return nil
  70. }
  71. _close :: proc(f: ^File) -> Error {
  72. res := unix.sys_close(f.impl.fd)
  73. return _ok_or_error(res)
  74. }
  75. _fd :: proc(f: ^File) -> uintptr {
  76. if f == nil {
  77. return ~uintptr(0)
  78. }
  79. return uintptr(f.impl.fd)
  80. }
  81. _name :: proc(f: ^File) -> string {
  82. return f.impl.name if f != nil else ""
  83. }
  84. _seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
  85. res := unix.sys_lseek(f.impl.fd, offset, int(whence))
  86. if res < 0 {
  87. return -1, _get_platform_error(int(res))
  88. }
  89. return res, nil
  90. }
  91. _read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
  92. if len(p) == 0 {
  93. return 0, nil
  94. }
  95. n = unix.sys_read(f.impl.fd, &p[0], len(p))
  96. if n < 0 {
  97. return -1, _get_platform_error(n)
  98. }
  99. return n, nil
  100. }
  101. _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
  102. if offset < 0 {
  103. return 0, .Invalid_Offset
  104. }
  105. b, offset := p, offset
  106. for len(b) > 0 {
  107. m := unix.sys_pread(f.impl.fd, &b[0], len(b), offset)
  108. if m < 0 {
  109. return -1, _get_platform_error(m)
  110. }
  111. n += m
  112. b = b[m:]
  113. offset += i64(m)
  114. }
  115. return
  116. }
  117. _read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
  118. //TODO
  119. return
  120. }
  121. _write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
  122. if len(p) == 0 {
  123. return 0, nil
  124. }
  125. n = unix.sys_write(f.impl.fd, &p[0], uint(len(p)))
  126. if n < 0 {
  127. return -1, _get_platform_error(n)
  128. }
  129. return int(n), nil
  130. }
  131. _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
  132. if offset < 0 {
  133. return 0, .Invalid_Offset
  134. }
  135. b, offset := p, offset
  136. for len(b) > 0 {
  137. m := unix.sys_pwrite(f.impl.fd, &b[0], len(b), offset)
  138. if m < 0 {
  139. return -1, _get_platform_error(m)
  140. }
  141. n += m
  142. b = b[m:]
  143. offset += i64(m)
  144. }
  145. return
  146. }
  147. _write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
  148. //TODO
  149. return
  150. }
  151. _file_size :: proc(f: ^File) -> (n: i64, err: Error) {
  152. s: _Stat = ---
  153. res := unix.sys_fstat(f.impl.fd, &s)
  154. if res < 0 {
  155. return -1, _get_platform_error(res)
  156. }
  157. return s.size, nil
  158. }
  159. _sync :: proc(f: ^File) -> Error {
  160. return _ok_or_error(unix.sys_fsync(f.impl.fd))
  161. }
  162. _flush :: proc(f: ^File) -> Error {
  163. return _ok_or_error(unix.sys_fsync(f.impl.fd))
  164. }
  165. _truncate :: proc(f: ^File, size: i64) -> Error {
  166. return _ok_or_error(unix.sys_ftruncate(f.impl.fd, size))
  167. }
  168. _remove :: proc(name: string) -> Error {
  169. name_cstr, allocated := _name_to_cstring(name)
  170. defer if allocated {
  171. delete(name_cstr)
  172. }
  173. fd := unix.sys_open(name_cstr, int(File_Flags.Read))
  174. if fd < 0 {
  175. return _get_platform_error(fd)
  176. }
  177. defer unix.sys_close(fd)
  178. if _is_dir_fd(fd) {
  179. return _ok_or_error(unix.sys_rmdir(name_cstr))
  180. }
  181. return _ok_or_error(unix.sys_unlink(name_cstr))
  182. }
  183. _rename :: proc(old_name, new_name: string) -> Error {
  184. old_name_cstr, old_allocated := _name_to_cstring(old_name)
  185. new_name_cstr, new_allocated := _name_to_cstring(new_name)
  186. defer if old_allocated {
  187. delete(old_name_cstr)
  188. }
  189. defer if new_allocated {
  190. delete(new_name_cstr)
  191. }
  192. return _ok_or_error(unix.sys_rename(old_name_cstr, new_name_cstr))
  193. }
  194. _link :: proc(old_name, new_name: string) -> Error {
  195. old_name_cstr, old_allocated := _name_to_cstring(old_name)
  196. new_name_cstr, new_allocated := _name_to_cstring(new_name)
  197. defer if old_allocated {
  198. delete(old_name_cstr)
  199. }
  200. defer if new_allocated {
  201. delete(new_name_cstr)
  202. }
  203. return _ok_or_error(unix.sys_link(old_name_cstr, new_name_cstr))
  204. }
  205. _symlink :: proc(old_name, new_name: string) -> Error {
  206. old_name_cstr, old_allocated := _name_to_cstring(old_name)
  207. new_name_cstr, new_allocated := _name_to_cstring(new_name)
  208. defer if old_allocated {
  209. delete(old_name_cstr)
  210. }
  211. defer if new_allocated {
  212. delete(new_name_cstr)
  213. }
  214. return _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr))
  215. }
  216. _read_link_cstr :: proc(name_cstr: cstring, allocator := context.allocator) -> (string, Error) {
  217. bufsz : uint = 256
  218. buf := make([]byte, bufsz, allocator)
  219. for {
  220. rc := unix.sys_readlink(name_cstr, &(buf[0]), bufsz)
  221. if rc < 0 {
  222. delete(buf)
  223. return "", _get_platform_error(rc)
  224. } else if rc == int(bufsz) {
  225. bufsz *= 2
  226. delete(buf)
  227. buf = make([]byte, bufsz, allocator)
  228. } else {
  229. return strings.string_from_ptr(&buf[0], rc), nil
  230. }
  231. }
  232. }
  233. _read_link :: proc(name: string, allocator := context.allocator) -> (string, Error) {
  234. name_cstr, allocated := _name_to_cstring(name)
  235. defer if allocated {
  236. delete(name_cstr)
  237. }
  238. return _read_link_cstr(name_cstr, allocator)
  239. }
  240. _unlink :: proc(name: string) -> Error {
  241. name_cstr, allocated := _name_to_cstring(name)
  242. defer if allocated {
  243. delete(name_cstr)
  244. }
  245. return _ok_or_error(unix.sys_unlink(name_cstr))
  246. }
  247. _chdir :: proc(name: string) -> Error {
  248. name_cstr, allocated := _name_to_cstring(name)
  249. defer if allocated {
  250. delete(name_cstr)
  251. }
  252. return _ok_or_error(unix.sys_chdir(name_cstr))
  253. }
  254. _fchdir :: proc(f: ^File) -> Error {
  255. return _ok_or_error(unix.sys_fchdir(f.impl.fd))
  256. }
  257. _chmod :: proc(name: string, mode: File_Mode) -> Error {
  258. name_cstr, allocated := _name_to_cstring(name)
  259. defer if allocated {
  260. delete(name_cstr)
  261. }
  262. return _ok_or_error(unix.sys_chmod(name_cstr, int(mode)))
  263. }
  264. _fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
  265. return _ok_or_error(unix.sys_fchmod(f.impl.fd, int(mode)))
  266. }
  267. // NOTE: will throw error without super user priviledges
  268. _chown :: proc(name: string, uid, gid: int) -> Error {
  269. name_cstr, allocated := _name_to_cstring(name)
  270. defer if allocated {
  271. delete(name_cstr)
  272. }
  273. return _ok_or_error(unix.sys_chown(name_cstr, uid, gid))
  274. }
  275. // NOTE: will throw error without super user priviledges
  276. _lchown :: proc(name: string, uid, gid: int) -> Error {
  277. name_cstr, allocated := _name_to_cstring(name)
  278. defer if allocated {
  279. delete(name_cstr)
  280. }
  281. return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid))
  282. }
  283. // NOTE: will throw error without super user priviledges
  284. _fchown :: proc(f: ^File, uid, gid: int) -> Error {
  285. return _ok_or_error(unix.sys_fchown(f.impl.fd, uid, gid))
  286. }
  287. _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
  288. name_cstr, allocated := _name_to_cstring(name)
  289. defer if allocated {
  290. delete(name_cstr)
  291. }
  292. times := [2]Unix_File_Time {
  293. { atime._nsec, 0 },
  294. { mtime._nsec, 0 },
  295. }
  296. return _ok_or_error(unix.sys_utimensat(_AT_FDCWD, name_cstr, &times, 0))
  297. }
  298. _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
  299. times := [2]Unix_File_Time {
  300. { atime._nsec, 0 },
  301. { mtime._nsec, 0 },
  302. }
  303. return _ok_or_error(unix.sys_utimensat(f.impl.fd, nil, &times, 0))
  304. }
  305. _exists :: proc(name: string) -> bool {
  306. name_cstr, allocated := _name_to_cstring(name)
  307. defer if allocated {
  308. delete(name_cstr)
  309. }
  310. return unix.sys_access(name_cstr, F_OK) == 0
  311. }
  312. _is_file :: proc(name: string) -> bool {
  313. name_cstr, allocated := _name_to_cstring(name)
  314. defer if allocated {
  315. delete(name_cstr)
  316. }
  317. s: _Stat
  318. res := unix.sys_stat(name_cstr, &s)
  319. if res < 0 {
  320. return false
  321. }
  322. return S_ISREG(s.mode)
  323. }
  324. _is_file_fd :: proc(fd: int) -> bool {
  325. s: _Stat
  326. res := unix.sys_fstat(fd, &s)
  327. if res < 0 { // error
  328. return false
  329. }
  330. return S_ISREG(s.mode)
  331. }
  332. _is_dir :: proc(name: string) -> bool {
  333. name_cstr, allocated := _name_to_cstring(name)
  334. defer if allocated {
  335. delete(name_cstr)
  336. }
  337. s: _Stat
  338. res := unix.sys_stat(name_cstr, &s)
  339. if res < 0 {
  340. return false
  341. }
  342. return S_ISDIR(s.mode)
  343. }
  344. _is_dir_fd :: proc(fd: int) -> bool {
  345. s: _Stat
  346. res := unix.sys_fstat(fd, &s)
  347. if res < 0 { // error
  348. return false
  349. }
  350. return S_ISDIR(s.mode)
  351. }
  352. // Ideally we want to use the temp_allocator. PATH_MAX on Linux is commonly
  353. // defined as 512, however, it is well known that paths can exceed that limit.
  354. // So, in theory you could have a path larger than the entire temp_allocator's
  355. // buffer. Therefor, any large paths will use context.allocator.
  356. _name_to_cstring :: proc(name: string) -> (cname: cstring, allocated: bool) {
  357. if len(name) > _CSTRING_NAME_HEAP_THRESHOLD {
  358. cname = strings.clone_to_cstring(name)
  359. allocated = true
  360. return
  361. }
  362. cname = strings.clone_to_cstring(name, context.temp_allocator)
  363. return
  364. }