2
0

file_posix.odin 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  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_GUARD()
  62. cname := temp_cstring(name)
  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. _close :: proc(f: ^File_Impl) -> (err: Error) {
  99. if f == nil { return nil }
  100. if posix.close(f.fd) != .OK {
  101. err = _get_platform_error()
  102. }
  103. allocator := f.allocator
  104. delete(f.cname, allocator)
  105. free(f, allocator)
  106. return
  107. }
  108. _fd :: proc(f: ^File) -> uintptr {
  109. return uintptr(__fd(f))
  110. }
  111. __fd :: proc(f: ^File) -> posix.FD {
  112. if f != nil && f.impl != nil {
  113. return (^File_Impl)(f.impl).fd
  114. }
  115. return -1
  116. }
  117. _name :: proc(f: ^File) -> string {
  118. if f != nil && f.impl != nil {
  119. return (^File_Impl)(f.impl).name
  120. }
  121. return ""
  122. }
  123. _sync :: proc(f: ^File) -> Error {
  124. if posix.fsync(__fd(f)) != .OK {
  125. return _get_platform_error()
  126. }
  127. return nil
  128. }
  129. _truncate :: proc(f: ^File, size: i64) -> Error {
  130. if posix.ftruncate(__fd(f), posix.off_t(size)) != .OK {
  131. return _get_platform_error()
  132. }
  133. return nil
  134. }
  135. _remove :: proc(name: string) -> Error {
  136. TEMP_ALLOCATOR_GUARD()
  137. cname := temp_cstring(name)
  138. if posix.remove(cname) != 0 {
  139. return _get_platform_error()
  140. }
  141. return nil
  142. }
  143. _rename :: proc(old_path, new_path: string) -> Error {
  144. TEMP_ALLOCATOR_GUARD()
  145. cold := temp_cstring(old_path)
  146. cnew := temp_cstring(new_path)
  147. if posix.rename(cold, cnew) != 0 {
  148. return _get_platform_error()
  149. }
  150. return nil
  151. }
  152. _link :: proc(old_name, new_name: string) -> Error {
  153. TEMP_ALLOCATOR_GUARD()
  154. cold := temp_cstring(old_name)
  155. cnew := temp_cstring(new_name)
  156. if posix.link(cold, cnew) != .OK {
  157. return _get_platform_error()
  158. }
  159. return nil
  160. }
  161. _symlink :: proc(old_name, new_name: string) -> Error {
  162. TEMP_ALLOCATOR_GUARD()
  163. cold := temp_cstring(old_name)
  164. cnew := temp_cstring(new_name)
  165. if posix.symlink(cold, cnew) != .OK {
  166. return _get_platform_error()
  167. }
  168. return nil
  169. }
  170. _read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, err: Error) {
  171. TEMP_ALLOCATOR_GUARD()
  172. cname := temp_cstring(name)
  173. buf: [dynamic]byte
  174. buf.allocator = allocator
  175. defer if err != nil { delete(buf) }
  176. // Loop this because the file might've grown between lstat() and readlink().
  177. for {
  178. stat: posix.stat_t
  179. if posix.lstat(cname, &stat) != .OK {
  180. err = _get_platform_error()
  181. return
  182. }
  183. bufsiz := int(stat.st_size + 1 if stat.st_size > 0 else posix.PATH_MAX)
  184. if bufsiz == len(buf) {
  185. bufsiz *= 2
  186. }
  187. // Overflow.
  188. if bufsiz <= 0 {
  189. err = Platform_Error(posix.Errno.E2BIG)
  190. return
  191. }
  192. resize(&buf, bufsiz) or_return
  193. size := posix.readlink(cname, raw_data(buf), uint(bufsiz))
  194. if size < 0 {
  195. err = _get_platform_error()
  196. return
  197. }
  198. // File has probably grown between lstat() and readlink().
  199. if size == bufsiz {
  200. continue
  201. }
  202. s = string(buf[:size])
  203. return
  204. }
  205. }
  206. _chdir :: proc(name: string) -> Error {
  207. TEMP_ALLOCATOR_GUARD()
  208. cname := temp_cstring(name)
  209. if posix.chdir(cname) != .OK {
  210. return _get_platform_error()
  211. }
  212. return nil
  213. }
  214. _fchdir :: proc(f: ^File) -> Error {
  215. if posix.fchdir(__fd(f)) != .OK {
  216. return _get_platform_error()
  217. }
  218. return nil
  219. }
  220. _fchmod :: proc(f: ^File, mode: int) -> Error {
  221. if posix.fchmod(__fd(f), transmute(posix.mode_t)posix._mode_t(mode)) != .OK {
  222. return _get_platform_error()
  223. }
  224. return nil
  225. }
  226. _chmod :: proc(name: string, mode: int) -> Error {
  227. TEMP_ALLOCATOR_GUARD()
  228. cname := temp_cstring(name)
  229. if posix.chmod(cname, transmute(posix.mode_t)posix._mode_t(mode)) != .OK {
  230. return _get_platform_error()
  231. }
  232. return nil
  233. }
  234. _fchown :: proc(f: ^File, uid, gid: int) -> Error {
  235. if posix.fchown(__fd(f), posix.uid_t(uid), posix.gid_t(gid)) != .OK {
  236. return _get_platform_error()
  237. }
  238. return nil
  239. }
  240. _chown :: proc(name: string, uid, gid: int) -> Error {
  241. TEMP_ALLOCATOR_GUARD()
  242. cname := temp_cstring(name)
  243. if posix.chown(cname, posix.uid_t(uid), posix.gid_t(gid)) != .OK {
  244. return _get_platform_error()
  245. }
  246. return nil
  247. }
  248. _lchown :: proc(name: string, uid, gid: int) -> Error {
  249. TEMP_ALLOCATOR_GUARD()
  250. cname := temp_cstring(name)
  251. if posix.lchown(cname, posix.uid_t(uid), posix.gid_t(gid)) != .OK {
  252. return _get_platform_error()
  253. }
  254. return nil
  255. }
  256. _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
  257. times := [2]posix.timeval{
  258. {
  259. tv_sec = posix.time_t(atime._nsec/1e9), /* seconds */
  260. tv_usec = posix.suseconds_t(atime._nsec%1e9/1000), /* microseconds */
  261. },
  262. {
  263. tv_sec = posix.time_t(mtime._nsec/1e9), /* seconds */
  264. tv_usec = posix.suseconds_t(mtime._nsec%1e9/1000), /* microseconds */
  265. },
  266. }
  267. TEMP_ALLOCATOR_GUARD()
  268. cname := temp_cstring(name)
  269. if posix.utimes(cname, &times) != .OK {
  270. return _get_platform_error()
  271. }
  272. return nil
  273. }
  274. _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
  275. times := [2]posix.timespec{
  276. {
  277. tv_sec = posix.time_t(atime._nsec/1e9), /* seconds */
  278. tv_nsec = c.long(atime._nsec%1e9), /* nanoseconds */
  279. },
  280. {
  281. tv_sec = posix.time_t(mtime._nsec/1e9), /* seconds */
  282. tv_nsec = c.long(mtime._nsec%1e9), /* nanoseconds */
  283. },
  284. }
  285. if posix.futimens(__fd(f), &times) != .OK {
  286. return _get_platform_error()
  287. }
  288. return nil
  289. }
  290. _exists :: proc(path: string) -> bool {
  291. TEMP_ALLOCATOR_GUARD()
  292. cpath := temp_cstring(path)
  293. return posix.access(cpath) == .OK
  294. }
  295. _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
  296. f := (^File_Impl)(stream_data)
  297. fd := f.fd
  298. switch mode {
  299. case .Read:
  300. if len(p) <= 0 {
  301. return
  302. }
  303. to_read := uint(min(len(p), MAX_RW))
  304. n = i64(posix.read(fd, raw_data(p), to_read))
  305. switch {
  306. case n == 0:
  307. err = .EOF
  308. case n < 0:
  309. err = .Unknown
  310. }
  311. return
  312. case .Read_At:
  313. if len(p) <= 0 {
  314. return
  315. }
  316. if offset < 0 {
  317. err = .Invalid_Offset
  318. return
  319. }
  320. to_read := uint(min(len(p), MAX_RW))
  321. n = i64(posix.pread(fd, raw_data(p), to_read, posix.off_t(offset)))
  322. switch {
  323. case n == 0:
  324. err = .EOF
  325. case n < 0:
  326. err = .Unknown
  327. }
  328. return
  329. case .Write:
  330. p := p
  331. for len(p) > 0 {
  332. to_write := uint(min(len(p), MAX_RW))
  333. if _n := i64(posix.write(fd, raw_data(p), to_write)); _n <= 0 {
  334. err = .Unknown
  335. return
  336. } else {
  337. p = p[_n:]
  338. n += _n
  339. }
  340. }
  341. return
  342. case .Write_At:
  343. p := p
  344. offset := offset
  345. if offset < 0 {
  346. err = .Invalid_Offset
  347. return
  348. }
  349. for len(p) > 0 {
  350. to_write := uint(min(len(p), MAX_RW))
  351. if _n := i64(posix.pwrite(fd, raw_data(p), to_write, posix.off_t(offset))); _n <= 0 {
  352. err = .Unknown
  353. return
  354. } else {
  355. p = p[_n:]
  356. n += _n
  357. offset += _n
  358. }
  359. }
  360. return
  361. case .Seek:
  362. #assert(int(posix.Whence.SET) == int(io.Seek_From.Start))
  363. #assert(int(posix.Whence.CUR) == int(io.Seek_From.Current))
  364. #assert(int(posix.Whence.END) == int(io.Seek_From.End))
  365. switch whence {
  366. case .Start, .Current, .End:
  367. break
  368. case:
  369. err = .Invalid_Whence
  370. return
  371. }
  372. n = i64(posix.lseek(fd, posix.off_t(offset), posix.Whence(whence)))
  373. if n < 0 {
  374. #partial switch posix.get_errno() {
  375. case .EINVAL:
  376. err = .Invalid_Offset
  377. case:
  378. err = .Unknown
  379. }
  380. }
  381. return
  382. case .Size:
  383. stat: posix.stat_t
  384. if posix.fstat(fd, &stat) != .OK {
  385. err = .Unknown
  386. return
  387. }
  388. n = i64(stat.st_size)
  389. return
  390. case .Flush:
  391. ferr := _sync(&f.file)
  392. err = error_to_io_error(ferr)
  393. return
  394. case .Close, .Destroy:
  395. ferr := _close(f)
  396. err = error_to_io_error(ferr)
  397. return
  398. case .Query:
  399. return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
  400. case:
  401. return 0, .Empty
  402. }
  403. }