path_linux.odin 6.0 KB

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