path_linux.odin 6.1 KB

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