path_linux.odin 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. //+private
  2. package os2
  3. import "core:strconv"
  4. import "base:runtime"
  5. import "core:sys/unix"
  6. _Path_Separator :: '/'
  7. _Path_Separator_String :: "/"
  8. _Path_List_Separator :: ':'
  9. _S_IFMT :: 0o170000 // Type of file mask
  10. _S_IFIFO :: 0o010000 // Named pipe (fifo)
  11. _S_IFCHR :: 0o020000 // Character special
  12. _S_IFDIR :: 0o040000 // Directory
  13. _S_IFBLK :: 0o060000 // Block special
  14. _S_IFREG :: 0o100000 // Regular
  15. _S_IFLNK :: 0o120000 // Symbolic link
  16. _S_IFSOCK :: 0o140000 // Socket
  17. _OPENDIR_FLAGS :: _O_RDONLY|_O_NONBLOCK|_O_DIRECTORY|_O_LARGEFILE|_O_CLOEXEC
  18. _is_path_separator :: proc(c: byte) -> bool {
  19. return c == '/'
  20. }
  21. _mkdir :: proc(path: string, perm: File_Mode) -> Error {
  22. // NOTE: These modes would require sys_mknod, however, that would require
  23. // additional arguments to this function.
  24. if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
  25. return .Invalid_Argument
  26. }
  27. TEMP_ALLOCATOR_GUARD()
  28. path_cstr := temp_cstring(path) or_return
  29. return _ok_or_error(unix.sys_mkdir(path_cstr, uint(perm & 0o777)))
  30. }
  31. _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
  32. _mkdirat :: proc(dfd: int, path: []u8, perm: int, has_created: ^bool) -> Error {
  33. if len(path) == 0 {
  34. return _ok_or_error(unix.sys_close(dfd))
  35. }
  36. i: int
  37. for /**/; i < len(path) - 1 && path[i] != '/'; i += 1 {}
  38. path[i] = 0
  39. new_dfd := unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
  40. switch new_dfd {
  41. case -ENOENT:
  42. if res := unix.sys_mkdirat(dfd, cstring(&path[0]), uint(perm)); res < 0 {
  43. return _get_platform_error(res)
  44. }
  45. has_created^ = true
  46. if new_dfd = unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS); new_dfd < 0 {
  47. return _get_platform_error(new_dfd)
  48. }
  49. fallthrough
  50. case 0:
  51. if res := unix.sys_close(dfd); res < 0 {
  52. return _get_platform_error(res)
  53. }
  54. // skip consecutive '/'
  55. for i += 1; i < len(path) && path[i] == '/'; i += 1 {}
  56. return _mkdirat(new_dfd, path[i:], perm, has_created)
  57. case:
  58. return _get_platform_error(new_dfd)
  59. }
  60. unreachable()
  61. }
  62. if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
  63. return .Invalid_Argument
  64. }
  65. TEMP_ALLOCATOR_GUARD()
  66. // need something we can edit, and use to generate cstrings
  67. allocated: bool
  68. path_bytes: []u8
  69. if len(path) > _CSTRING_NAME_HEAP_THRESHOLD {
  70. allocated = true
  71. path_bytes = make([]u8, len(path) + 1)
  72. } else {
  73. path_bytes = make([]u8, len(path) + 1, temp_allocator())
  74. }
  75. // NULL terminate the byte slice to make it a valid cstring
  76. copy(path_bytes, path)
  77. path_bytes[len(path)] = 0
  78. dfd: int
  79. if path_bytes[0] == '/' {
  80. dfd = unix.sys_open("/", _OPENDIR_FLAGS)
  81. path_bytes = path_bytes[1:]
  82. } else {
  83. dfd = unix.sys_open(".", _OPENDIR_FLAGS)
  84. }
  85. if dfd < 0 {
  86. return _get_platform_error(dfd)
  87. }
  88. has_created: bool
  89. _mkdirat(dfd, path_bytes, int(perm & 0o777), &has_created) or_return
  90. if has_created {
  91. return nil
  92. }
  93. return .Exist
  94. //return has_created ? nil : .Exist
  95. }
  96. dirent64 :: struct {
  97. d_ino: u64,
  98. d_off: u64,
  99. d_reclen: u16,
  100. d_type: u8,
  101. d_name: [1]u8,
  102. }
  103. _remove_all :: proc(path: string) -> Error {
  104. DT_DIR :: 4
  105. _remove_all_dir :: proc(dfd: int) -> Error {
  106. n := 64
  107. buf := make([]u8, n)
  108. defer delete(buf)
  109. loop: for {
  110. getdents_res := unix.sys_getdents64(dfd, &buf[0], n)
  111. switch getdents_res {
  112. case -EINVAL:
  113. delete(buf)
  114. n *= 2
  115. buf = make([]u8, n)
  116. continue loop
  117. case -4096..<0:
  118. return _get_platform_error(getdents_res)
  119. case 0:
  120. break loop
  121. }
  122. d: ^dirent64
  123. for i := 0; i < getdents_res; i += int(d.d_reclen) {
  124. d = (^dirent64)(rawptr(&buf[i]))
  125. d_name_cstr := cstring(&d.d_name[0])
  126. buf_len := uintptr(d.d_reclen) - offset_of(d.d_name)
  127. /* check for current directory (.) */
  128. #no_bounds_check if buf_len > 1 && d.d_name[0] == '.' && d.d_name[1] == 0 {
  129. continue
  130. }
  131. /* check for parent directory (..) */
  132. #no_bounds_check if buf_len > 2 && d.d_name[0] == '.' && d.d_name[1] == '.' && d.d_name[2] == 0 {
  133. continue
  134. }
  135. unlink_res: int
  136. switch d.d_type {
  137. case DT_DIR:
  138. new_dfd := unix.sys_openat(dfd, d_name_cstr, _OPENDIR_FLAGS)
  139. if new_dfd < 0 {
  140. return _get_platform_error(new_dfd)
  141. }
  142. defer unix.sys_close(new_dfd)
  143. _remove_all_dir(new_dfd) or_return
  144. unlink_res = unix.sys_unlinkat(dfd, d_name_cstr, int(unix.AT_REMOVEDIR))
  145. case:
  146. unlink_res = unix.sys_unlinkat(dfd, d_name_cstr)
  147. }
  148. if unlink_res < 0 {
  149. return _get_platform_error(unlink_res)
  150. }
  151. }
  152. }
  153. return nil
  154. }
  155. TEMP_ALLOCATOR_GUARD()
  156. path_cstr := temp_cstring(path) or_return
  157. fd := unix.sys_open(path_cstr, _OPENDIR_FLAGS)
  158. switch fd {
  159. case -ENOTDIR:
  160. return _ok_or_error(unix.sys_unlink(path_cstr))
  161. case -4096..<0:
  162. return _get_platform_error(fd)
  163. }
  164. defer unix.sys_close(fd)
  165. _remove_all_dir(fd) or_return
  166. return _ok_or_error(unix.sys_rmdir(path_cstr))
  167. }
  168. _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
  169. // NOTE(tetra): I would use PATH_MAX here, but I was not able to find
  170. // an authoritative value for it across all systems.
  171. // The largest value I could find was 4096, so might as well use the page size.
  172. // NOTE(jason): Avoiding libc, so just use 4096 directly
  173. PATH_MAX :: 4096
  174. buf := make([dynamic]u8, PATH_MAX, allocator)
  175. for {
  176. #no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf)))
  177. if res >= 0 {
  178. return string_from_null_terminated_bytes(buf[:]), nil
  179. }
  180. if res != -ERANGE {
  181. return "", _get_platform_error(res)
  182. }
  183. resize(&buf, len(buf)+PATH_MAX)
  184. }
  185. unreachable()
  186. }
  187. _setwd :: proc(dir: string) -> Error {
  188. dir_cstr := temp_cstring(dir) or_return
  189. return _ok_or_error(unix.sys_chdir(dir_cstr))
  190. }
  191. _get_full_path :: proc(fd: int, allocator: runtime.Allocator) -> string {
  192. PROC_FD_PATH :: "/proc/self/fd/"
  193. buf: [32]u8
  194. copy(buf[:], PROC_FD_PATH)
  195. strconv.itoa(buf[len(PROC_FD_PATH):], fd)
  196. fullpath: string
  197. err: Error
  198. if fullpath, err = _read_link_cstr(cstring(&buf[0]), allocator); err != nil || fullpath[0] != '/' {
  199. return ""
  200. }
  201. return fullpath
  202. }