file_posix.odin 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. //+private
  2. //+build darwin, netbsd, freebsd, openbsd
  3. package os2
  4. import "base:runtime"
  5. import "core:io"
  6. import "core:c"
  7. import "core:time"
  8. import "core:sys/posix"
  9. // Most implementations will EINVAL at some point when doing big writes.
  10. // In practice a read/write call would probably never read/write these big buffers all at once,
  11. // which is why the number of bytes is returned and why there are procs that will call this in a
  12. // loop for you.
  13. // We set a max of 1GB to keep alignment and to be safe.
  14. MAX_RW :: 1 << 30
  15. File_Impl :: struct {
  16. file: File,
  17. name: string,
  18. cname: cstring,
  19. fd: posix.FD,
  20. }
  21. @(init)
  22. init_std_files :: proc() {
  23. // NOTE: is this (paths) also the case on non darwin?
  24. stdin = __new_file(posix.STDIN_FILENO)
  25. (^File_Impl)(stdin.impl).name = "/dev/stdin"
  26. (^File_Impl)(stdin.impl).cname = "/dev/stdin"
  27. stdout = __new_file(posix.STDIN_FILENO)
  28. (^File_Impl)(stdout.impl).name = "/dev/stdout"
  29. (^File_Impl)(stdout.impl).cname = "/dev/stdout"
  30. stderr = __new_file(posix.STDIN_FILENO)
  31. (^File_Impl)(stderr.impl).name = "/dev/stderr"
  32. (^File_Impl)(stderr.impl).cname = "/dev/stderr"
  33. }
  34. _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
  35. if name == "" {
  36. err = .Invalid_Path
  37. return
  38. }
  39. sys_flags := posix.O_Flags{.NOCTTY, .CLOEXEC}
  40. if .Write in flags {
  41. if .Read in flags {
  42. sys_flags += {.RDWR}
  43. } else {
  44. sys_flags += {.WRONLY}
  45. }
  46. }
  47. if .Append in flags { sys_flags += {.APPEND} }
  48. if .Create in flags { sys_flags += {.CREAT} }
  49. if .Excl in flags { sys_flags += {.EXCL} }
  50. if .Sync in flags { sys_flags += {.DSYNC} }
  51. if .Trunc in flags { sys_flags += {.TRUNC} }
  52. if .Inheritable in flags { sys_flags -= {.CLOEXEC} }
  53. TEMP_ALLOCATOR_GUARD()
  54. cname := temp_cstring(name)
  55. fd := posix.open(cname, sys_flags, transmute(posix.mode_t)posix._mode_t(perm))
  56. if fd < 0 {
  57. err = _get_platform_error()
  58. return
  59. }
  60. return _new_file(uintptr(fd), name)
  61. }
  62. _new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
  63. if name == "" {
  64. err = .Invalid_Path
  65. return
  66. } else if handle == ~uintptr(0) {
  67. err = .Invalid_File
  68. return
  69. }
  70. crname := _posix_absolute_path(posix.FD(handle), name, file_allocator()) or_return
  71. rname := string(crname)
  72. f = __new_file(posix.FD(handle))
  73. impl := (^File_Impl)(f.impl)
  74. impl.name = rname
  75. impl.cname = crname
  76. return f, nil
  77. }
  78. __new_file :: proc(handle: posix.FD) -> ^File {
  79. impl := new(File_Impl, file_allocator())
  80. impl.file.impl = impl
  81. impl.fd = posix.FD(handle)
  82. impl.file.stream = {
  83. data = impl,
  84. procedure = _file_stream_proc,
  85. }
  86. impl.file.fstat = _fstat
  87. return &impl.file
  88. }
  89. _close :: proc(f: ^File_Impl) -> (err: Error) {
  90. if f == nil { return nil }
  91. if posix.close(f.fd) != .OK {
  92. err = _get_platform_error()
  93. }
  94. delete(f.cname, file_allocator())
  95. free(f, file_allocator())
  96. return
  97. }
  98. _fd :: proc(f: ^File) -> uintptr {
  99. return uintptr(__fd(f))
  100. }
  101. __fd :: proc(f: ^File) -> posix.FD {
  102. if f != nil && f.impl != nil {
  103. return (^File_Impl)(f.impl).fd
  104. }
  105. return -1
  106. }
  107. _name :: proc(f: ^File) -> string {
  108. if f != nil && f.impl != nil {
  109. return (^File_Impl)(f.impl).name
  110. }
  111. return ""
  112. }
  113. _sync :: proc(f: ^File) -> Error {
  114. if posix.fsync(__fd(f)) != .OK {
  115. return _get_platform_error()
  116. }
  117. return nil
  118. }
  119. _truncate :: proc(f: ^File, size: i64) -> Error {
  120. if posix.ftruncate(__fd(f), posix.off_t(size)) != .OK {
  121. return _get_platform_error()
  122. }
  123. return nil
  124. }
  125. _remove :: proc(name: string) -> Error {
  126. TEMP_ALLOCATOR_GUARD()
  127. cname := temp_cstring(name)
  128. if posix.remove(cname) != 0 {
  129. return _get_platform_error()
  130. }
  131. return nil
  132. }
  133. _rename :: proc(old_path, new_path: string) -> Error {
  134. TEMP_ALLOCATOR_GUARD()
  135. cold := temp_cstring(old_path)
  136. cnew := temp_cstring(new_path)
  137. if posix.rename(cold, cnew) != 0 {
  138. return _get_platform_error()
  139. }
  140. return nil
  141. }
  142. _link :: proc(old_name, new_name: string) -> Error {
  143. TEMP_ALLOCATOR_GUARD()
  144. cold := temp_cstring(old_name)
  145. cnew := temp_cstring(new_name)
  146. if posix.link(cold, cnew) != .OK {
  147. return _get_platform_error()
  148. }
  149. return nil
  150. }
  151. _symlink :: proc(old_name, new_name: string) -> Error {
  152. TEMP_ALLOCATOR_GUARD()
  153. cold := temp_cstring(old_name)
  154. cnew := temp_cstring(new_name)
  155. if posix.symlink(cold, cnew) != .OK {
  156. return _get_platform_error()
  157. }
  158. return nil
  159. }
  160. _read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, err: Error) {
  161. TEMP_ALLOCATOR_GUARD()
  162. cname := temp_cstring(name)
  163. buf: [dynamic]byte
  164. buf.allocator = allocator
  165. defer if err != nil { delete(buf) }
  166. // Loop this because the file might've grown between lstat() and readlink().
  167. for {
  168. stat: posix.stat_t
  169. if posix.lstat(cname, &stat) != .OK {
  170. err = _get_platform_error()
  171. return
  172. }
  173. bufsiz := int(stat.st_size + 1 if stat.st_size > 0 else posix.PATH_MAX)
  174. if bufsiz == len(buf) {
  175. bufsiz *= 2
  176. }
  177. // Overflow.
  178. if bufsiz <= 0 {
  179. err = Platform_Error(posix.Errno.E2BIG)
  180. return
  181. }
  182. resize(&buf, bufsiz) or_return
  183. size := posix.readlink(cname, raw_data(buf), uint(bufsiz))
  184. if size < 0 {
  185. err = _get_platform_error()
  186. return
  187. }
  188. // File has probably grown between lstat() and readlink().
  189. if size == bufsiz {
  190. continue
  191. }
  192. s = string(buf[:size])
  193. return
  194. }
  195. }
  196. _chdir :: proc(name: string) -> Error {
  197. TEMP_ALLOCATOR_GUARD()
  198. cname := temp_cstring(name)
  199. if posix.chdir(cname) != .OK {
  200. return _get_platform_error()
  201. }
  202. return nil
  203. }
  204. _fchdir :: proc(f: ^File) -> Error {
  205. if posix.fchdir(__fd(f)) != .OK {
  206. return _get_platform_error()
  207. }
  208. return nil
  209. }
  210. _fchmod :: proc(f: ^File, mode: int) -> Error {
  211. if posix.fchmod(__fd(f), transmute(posix.mode_t)posix._mode_t(mode)) != .OK {
  212. return _get_platform_error()
  213. }
  214. return nil
  215. }
  216. _chmod :: proc(name: string, mode: int) -> Error {
  217. TEMP_ALLOCATOR_GUARD()
  218. cname := temp_cstring(name)
  219. if posix.chmod(cname, transmute(posix.mode_t)posix._mode_t(mode)) != .OK {
  220. return _get_platform_error()
  221. }
  222. return nil
  223. }
  224. _fchown :: proc(f: ^File, uid, gid: int) -> Error {
  225. if posix.fchown(__fd(f), posix.uid_t(uid), posix.gid_t(gid)) != .OK {
  226. return _get_platform_error()
  227. }
  228. return nil
  229. }
  230. _chown :: proc(name: string, uid, gid: int) -> Error {
  231. TEMP_ALLOCATOR_GUARD()
  232. cname := temp_cstring(name)
  233. if posix.chown(cname, posix.uid_t(uid), posix.gid_t(gid)) != .OK {
  234. return _get_platform_error()
  235. }
  236. return nil
  237. }
  238. _lchown :: proc(name: string, uid, gid: int) -> Error {
  239. TEMP_ALLOCATOR_GUARD()
  240. cname := temp_cstring(name)
  241. if posix.lchown(cname, posix.uid_t(uid), posix.gid_t(gid)) != .OK {
  242. return _get_platform_error()
  243. }
  244. return nil
  245. }
  246. _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
  247. times := [2]posix.timeval{
  248. {
  249. tv_sec = posix.time_t(atime._nsec/1e9), /* seconds */
  250. tv_usec = posix.suseconds_t(atime._nsec%1e9/1000), /* microseconds */
  251. },
  252. {
  253. tv_sec = posix.time_t(mtime._nsec/1e9), /* seconds */
  254. tv_usec = posix.suseconds_t(mtime._nsec%1e9/1000), /* microseconds */
  255. },
  256. }
  257. TEMP_ALLOCATOR_GUARD()
  258. cname := temp_cstring(name)
  259. if posix.utimes(cname, &times) != .OK {
  260. return _get_platform_error()
  261. }
  262. return nil
  263. }
  264. _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
  265. times := [2]posix.timespec{
  266. {
  267. tv_sec = posix.time_t(atime._nsec/1e9), /* seconds */
  268. tv_nsec = c.long(atime._nsec%1e9), /* nanoseconds */
  269. },
  270. {
  271. tv_sec = posix.time_t(mtime._nsec/1e9), /* seconds */
  272. tv_nsec = c.long(mtime._nsec%1e9), /* nanoseconds */
  273. },
  274. }
  275. if posix.futimens(__fd(f), &times) != .OK {
  276. return _get_platform_error()
  277. }
  278. return nil
  279. }
  280. _exists :: proc(path: string) -> bool {
  281. TEMP_ALLOCATOR_GUARD()
  282. cpath := temp_cstring(path)
  283. return posix.access(cpath) == .OK
  284. }
  285. _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
  286. f := (^File_Impl)(stream_data)
  287. fd := f.fd
  288. switch mode {
  289. case .Read:
  290. if len(p) <= 0 {
  291. return
  292. }
  293. to_read := uint(min(len(p), MAX_RW))
  294. n = i64(posix.read(fd, raw_data(p), to_read))
  295. switch {
  296. case n == 0:
  297. err = .EOF
  298. case n < 0:
  299. err = .Unknown
  300. }
  301. return
  302. case .Read_At:
  303. if len(p) <= 0 {
  304. return
  305. }
  306. if offset < 0 {
  307. err = .Invalid_Offset
  308. return
  309. }
  310. to_read := uint(min(len(p), MAX_RW))
  311. n = i64(posix.pread(fd, raw_data(p), to_read, posix.off_t(offset)))
  312. switch {
  313. case n == 0:
  314. err = .EOF
  315. case n < 0:
  316. err = .Unknown
  317. }
  318. return
  319. case .Write:
  320. p := p
  321. for len(p) > 0 {
  322. to_write := uint(min(len(p), MAX_RW))
  323. if _n := i64(posix.write(fd, raw_data(p), to_write)); _n <= 0 {
  324. err = .Unknown
  325. return
  326. } else {
  327. p = p[_n:]
  328. n += _n
  329. }
  330. }
  331. return
  332. case .Write_At:
  333. p := p
  334. offset := offset
  335. if offset < 0 {
  336. err = .Invalid_Offset
  337. return
  338. }
  339. for len(p) > 0 {
  340. to_write := uint(min(len(p), MAX_RW))
  341. if _n := i64(posix.pwrite(fd, raw_data(p), to_write, posix.off_t(offset))); _n <= 0 {
  342. err = .Unknown
  343. return
  344. } else {
  345. p = p[_n:]
  346. n += _n
  347. offset += _n
  348. }
  349. }
  350. return
  351. case .Seek:
  352. #assert(int(posix.Whence.SET) == int(io.Seek_From.Start))
  353. #assert(int(posix.Whence.CUR) == int(io.Seek_From.Current))
  354. #assert(int(posix.Whence.END) == int(io.Seek_From.End))
  355. switch whence {
  356. case .Start, .Current, .End:
  357. break
  358. case:
  359. err = .Invalid_Whence
  360. return
  361. }
  362. n = i64(posix.lseek(fd, posix.off_t(offset), posix.Whence(whence)))
  363. if n < 0 {
  364. #partial switch posix.get_errno() {
  365. case .EINVAL:
  366. err = .Invalid_Offset
  367. case:
  368. err = .Unknown
  369. }
  370. }
  371. return
  372. case .Size:
  373. stat: posix.stat_t
  374. if posix.fstat(fd, &stat) != .OK {
  375. err = .Unknown
  376. return
  377. }
  378. n = i64(stat.st_size)
  379. return
  380. case .Flush:
  381. ferr := _sync(&f.file)
  382. err = error_to_io_error(ferr)
  383. return
  384. case .Close, .Destroy:
  385. ferr := _close(f)
  386. err = error_to_io_error(ferr)
  387. return
  388. case .Query:
  389. return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
  390. case:
  391. return 0, .Empty
  392. }
  393. }