xnu_system_call_helpers.odin 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package darwin
  2. import "core:c"
  3. import "base:runtime"
  4. // IMPORTANT NOTE: direct syscall usage is not allowed by Apple's review process of apps and should
  5. // be entirely avoided in the builtin Odin collections, these are here for users if they don't
  6. // care about the Apple review process.
  7. // this package uses the sys prefix for the proc names to indicate that these aren't native syscalls but directly call such
  8. sys_write_string :: proc (fd: c.int, message: string) -> bool {
  9. return syscall_write(fd, raw_data(message), cast(u64)len(message))
  10. }
  11. Offset_From :: enum c.int {
  12. SEEK_SET = 0, // the offset is set to offset bytes.
  13. SEEK_CUR = 1, // the offset is set to its current location plus offset bytes.
  14. SEEK_END = 2, // the offset is set to the size of the file plus offset bytes.
  15. SEEK_HOLE = 3, // the offset is set to the start of the next hole greater than or equal to the supplied offset.
  16. SEEK_DATA = 4, // the offset is set to the start of the next non-hole file region greater than or equal to the supplied offset.
  17. }
  18. Open_Flags_Enum :: enum u8 {
  19. RDONLY, /* open for reading only */
  20. WRONLY, /* open for writing only */
  21. RDWR, /* open for reading and writing */
  22. NONBLOCK, /* no delay */
  23. APPEND, /* set append mode */
  24. CREAT, /* create if nonexistant */
  25. TRUNC, /* truncate to zero length */
  26. EXCL, /* error if already exists */
  27. SHLOCK, /* open with shared file lock */
  28. EXLOCK, /* open with exclusive file lock */
  29. DIRECTORY, /* restrict open to only directories */
  30. NOFOLLOW, /* don't follow symlinks */
  31. SYMLINK, /* allow open of a symlink */
  32. EVTONLY, /* descriptor requested for event notifications only */
  33. CLOEXEC, /* causes the descriptor to be closed if you use any of the exec like functions */
  34. NOFOLLOW_ANY, /* no symlinks allowed in path */
  35. }
  36. Open_Flags :: bit_set[Open_Flags_Enum; u16]
  37. Permission_Enum :: enum u8 {
  38. /* For owner */
  39. PERMISSION_OWNER_READ, /* R for owner */
  40. PERMISSION_OWNER_WRITE, /* W for owner */
  41. PERMISSION_OWNER_EXECUTE, /* X for owner */
  42. //IRWXU, /* RWX mask for owner */
  43. /* For group */
  44. PERMISSION_GROUP_READ, /* R for group */
  45. PERMISSION_GROUP_WRITE, /* W for group */
  46. PERMISSION_GROUP_EXECUTE, /* X for group */
  47. //IRWXG, /* RWX mask for group */
  48. /* For other */
  49. PERMISSION_OTHER_READ, /* R for other */
  50. PERMISSION_OTHER_WRITE, /* W for other */
  51. PERMISSION_OTHER_EXECUTE, /* X for other */
  52. //IRWXO, /* RWX mask for other */
  53. /* Special */
  54. PERMISSION_SET_USER_ON_EXECUTION, /* set user id on execution */
  55. PERMISSION_SET_GROUP_ON_EXECUTION, /* set group id on execution */
  56. /* ?? */
  57. PERMISSION_ISVTX, /* save swapped text even after use */
  58. }
  59. Permission :: bit_set[Permission_Enum; u16]
  60. PERMISSION_NONE_NONE :: Permission{}
  61. PERMISSION_OWNER_ALL :: Permission{.PERMISSION_OWNER_READ, .PERMISSION_OWNER_WRITE, .PERMISSION_OWNER_EXECUTE}
  62. PERMISSION_GROUP_ALL :: Permission{.PERMISSION_GROUP_READ, .PERMISSION_GROUP_WRITE, .PERMISSION_GROUP_EXECUTE}
  63. PERMISSION_OTHER_ALL :: Permission{.PERMISSION_OTHER_READ, .PERMISSION_OTHER_WRITE, .PERMISSION_OTHER_EXECUTE}
  64. PERMISSION_ALL_ALL :: PERMISSION_OWNER_ALL | PERMISSION_GROUP_ALL | PERMISSION_OTHER_ALL
  65. _sys_permission_mode :: #force_inline proc (mode: Permission) -> u32 {
  66. cflags: u32 = 0
  67. cflags |= PERMISSION_MASK_IRUSR * u32(Permission.PERMISSION_OWNER_READ in mode)
  68. cflags |= PERMISSION_MASK_IWUSR * u32(Permission.PERMISSION_OWNER_WRITE in mode)
  69. cflags |= PERMISSION_MASK_IXUSR * u32(Permission.PERMISSION_OWNER_WRITE in mode)
  70. cflags |= PERMISSION_MASK_IRGRP * u32(Permission.PERMISSION_GROUP_READ in mode)
  71. cflags |= PERMISSION_MASK_IWGRP * u32(Permission.PERMISSION_GROUP_WRITE in mode)
  72. cflags |= PERMISSION_MASK_IXGRP * u32(Permission.PERMISSION_GROUP_WRITE in mode)
  73. cflags |= PERMISSION_MASK_IROTH * u32(Permission.PERMISSION_OTHER_READ in mode)
  74. cflags |= PERMISSION_MASK_IWOTH * u32(Permission.PERMISSION_OTHER_WRITE in mode)
  75. cflags |= PERMISSION_MASK_IXOTH * u32(Permission.PERMISSION_OTHER_WRITE in mode)
  76. return cflags
  77. }
  78. _sys_open_mode :: #force_inline proc(mode: Open_Flags) -> u32 {
  79. cflags : u32 = 0
  80. cflags |= OPEN_FLAG_RDONLY * u32(Open_Flags.RDONLY in mode)
  81. cflags |= OPEN_FLAG_WRONLY * u32(Open_Flags.WRONLY in mode)
  82. cflags |= OPEN_FLAG_RDWR * u32(Open_Flags.RDWR in mode)
  83. cflags |= OPEN_FLAG_NONBLOCK * u32(Open_Flags.NONBLOCK in mode)
  84. cflags |= OPEN_FLAG_CREAT * u32(Open_Flags.CREAT in mode)
  85. cflags |= OPEN_FLAG_APPEND * u32(Open_Flags.APPEND in mode)
  86. cflags |= OPEN_FLAG_TRUNC * u32(Open_Flags.TRUNC in mode)
  87. cflags |= OPEN_FLAG_EXCL * u32(Open_Flags.EXCL in mode)
  88. cflags |= OPEN_FLAG_SHLOCK * u32(Open_Flags.SHLOCK in mode)
  89. cflags |= OPEN_FLAG_EXLOCK * u32(Open_Flags.EXLOCK in mode)
  90. cflags |= OPEN_FLAG_DIRECTORY * u32(Open_Flags.DIRECTORY in mode)
  91. cflags |= OPEN_FLAG_NOFOLLOW * u32(Open_Flags.NOFOLLOW in mode)
  92. cflags |= OPEN_FLAG_SYMLINK * u32(Open_Flags.SYMLINK in mode)
  93. cflags |= OPEN_FLAG_EVTONLY * u32(Open_Flags.EVTONLY in mode)
  94. cflags |= OPEN_FLAG_CLOEXEC * u32(Open_Flags.CLOEXEC in mode)
  95. cflags |= OPEN_FLAG_NOFOLLOW_ANY * u32(Open_Flags.NOFOLLOW_ANY in mode)
  96. return cflags
  97. }
  98. @(private)
  99. clone_to_cstring :: proc(s: string, allocator: runtime.Allocator, loc := #caller_location) -> cstring {
  100. c := make([]byte, len(s)+1, allocator, loc)
  101. copy(c, s)
  102. c[len(s)] = 0
  103. return cstring(&c[0])
  104. }
  105. sys_open :: proc(path: string, oflag: Open_Flags, mode: Permission) -> (c.int, bool) {
  106. runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
  107. cmode: u32 = 0
  108. cflags: u32 = 0
  109. cpath: cstring = clone_to_cstring(path, context.temp_allocator)
  110. cflags = _sys_permission_mode(mode)
  111. cmode = _sys_open_mode(oflag)
  112. result := syscall_open(cpath, cmode, cflags)
  113. state := result != -1
  114. if state && cflags != 0 {
  115. state = (syscall_fchmod(result, cflags) != -1)
  116. }
  117. return result * cast(c.int)state, state
  118. }
  119. sys_mkdir :: proc(path: string, mode: Permission) -> bool {
  120. runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
  121. cpath: cstring = clone_to_cstring(path, context.temp_allocator)
  122. cflags := _sys_permission_mode(mode)
  123. return syscall_mkdir(cpath, cflags) != -1
  124. }
  125. sys_mkdir_at :: proc(fd: c.int, path: string, mode: Permission) -> bool {
  126. runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
  127. cpath: cstring = clone_to_cstring(path, context.temp_allocator)
  128. cflags := _sys_permission_mode(mode)
  129. return syscall_mkdir_at(fd, cpath, cflags) != -1
  130. }
  131. sys_rmdir :: proc(path: string, mode: Permission) -> bool {
  132. runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
  133. cpath: cstring = clone_to_cstring(path, context.temp_allocator)
  134. cflags := _sys_permission_mode(mode)
  135. return syscall_rmdir(cpath, cflags) != -1
  136. }
  137. sys_rename :: proc(path: string, new_path: string) -> bool {
  138. runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
  139. cpath: cstring = clone_to_cstring(path, context.temp_allocator)
  140. cnpath: cstring = clone_to_cstring(new_path, context.temp_allocator)
  141. return syscall_rename(cpath, cnpath) != -1
  142. }
  143. sys_rename_at :: proc(fd: c.int, path: string, to_fd: c.int, new_path: string) -> bool {
  144. runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
  145. cpath: cstring = clone_to_cstring(path, context.temp_allocator)
  146. cnpath: cstring = clone_to_cstring(new_path, context.temp_allocator)
  147. return syscall_rename_at(fd, cpath, to_fd, cnpath) != -1
  148. }
  149. sys_lseek :: proc(fd: c.int, offset: i64, whence: Offset_From) -> i64 {
  150. return syscall_lseek(fd, offset, cast(c.int)whence)
  151. }
  152. sys_chmod :: proc(path: string, mode: Permission) -> bool {
  153. runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
  154. cpath: cstring = clone_to_cstring(path, context.temp_allocator)
  155. cmode := _sys_permission_mode(mode)
  156. return syscall_chmod(cpath, cmode) != -1
  157. }
  158. sys_lstat :: proc(path: string, status: ^stat) -> bool {
  159. runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
  160. cpath: cstring = clone_to_cstring(path, context.temp_allocator)
  161. return syscall_lstat(cpath, status) != -1
  162. }
  163. sys_shm_open :: proc(name: string, oflag: Open_Flags, mode: Permission) -> (c.int, bool) {
  164. runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
  165. cmode: u32 = 0
  166. cflags: u32 = 0
  167. cname: cstring = clone_to_cstring(name, context.temp_allocator)
  168. cflags = _sys_permission_mode(mode)
  169. cmode = _sys_open_mode(oflag)
  170. result := syscall_shm_open(cname, cmode, cflags)
  171. state := result != -1
  172. // NOTE(beau): Presently fstat doesn't report any changed permissions
  173. // on the file descriptor even with this fchmod (which fails with a
  174. // non-zero return). I can also reproduce this with the syscalls in c
  175. // so I suspect it's not odin's bug. I've left the fchmod in case the
  176. // underlying issue is fixed.
  177. if state && cflags != 0 {
  178. state = (syscall_fchmod(result, cflags) != -1)
  179. }
  180. return result * cast(c.int)state, state
  181. }