file_posix.odin 11 KB

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