path_linux.odin 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. //+private
  2. package os2
  3. import "core:strings"
  4. import "core:strconv"
  5. import "base:runtime"
  6. import "core:sys/linux"
  7. _Path_Separator :: '/'
  8. _Path_Separator_String :: "/"
  9. _Path_List_Separator :: ':'
  10. _OPENDIR_FLAGS : linux.Open_Flags : {.NONBLOCK, .DIRECTORY, .LARGEFILE, .CLOEXEC}
  11. _is_path_separator :: proc(c: byte) -> bool {
  12. return c == '/'
  13. }
  14. _mkdir :: proc(path: string, perm: int) -> Error {
  15. TEMP_ALLOCATOR_GUARD()
  16. path_cstr := temp_cstring(path) or_return
  17. return _get_platform_error(linux.mkdir(path_cstr, transmute(linux.Mode)u32(perm)))
  18. }
  19. _mkdir_all :: proc(path: string, perm: int) -> Error {
  20. mkdirat :: proc(dfd: linux.Fd, path: []u8, perm: int, has_created: ^bool) -> Error {
  21. i: int
  22. for ; i < len(path) - 1 && path[i] != '/'; i += 1 {}
  23. if i == 0 {
  24. return _get_platform_error(linux.close(dfd))
  25. }
  26. path[i] = 0
  27. new_dfd, errno := linux.openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
  28. #partial switch errno {
  29. case .ENOENT:
  30. if errno = linux.mkdirat(dfd, cstring(&path[0]), transmute(linux.Mode)u32(perm)); errno != .NONE {
  31. return _get_platform_error(errno)
  32. }
  33. has_created^ = true
  34. if new_dfd, errno = linux.openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS); errno != .NONE {
  35. return _get_platform_error(errno)
  36. }
  37. fallthrough
  38. case .NONE:
  39. if errno = linux.close(dfd); errno != .NONE {
  40. return _get_platform_error(errno)
  41. }
  42. // skip consecutive '/'
  43. for i += 1; i < len(path) && path[i] == '/'; i += 1 {}
  44. return mkdirat(new_dfd, path[i:], perm, has_created)
  45. }
  46. return _get_platform_error(errno)
  47. }
  48. TEMP_ALLOCATOR_GUARD()
  49. // need something we can edit, and use to generate cstrings
  50. path_bytes := make([]u8, len(path) + 1, temp_allocator())
  51. // zero terminate the byte slice to make it a valid cstring
  52. copy(path_bytes, path)
  53. path_bytes[len(path)] = 0
  54. dfd: linux.Fd
  55. errno: linux.Errno
  56. if path_bytes[0] == '/' {
  57. dfd, errno = linux.open("/", _OPENDIR_FLAGS)
  58. path_bytes = path_bytes[1:]
  59. } else {
  60. dfd, errno = linux.open(".", _OPENDIR_FLAGS)
  61. }
  62. if errno != .NONE {
  63. return _get_platform_error(errno)
  64. }
  65. has_created: bool
  66. mkdirat(dfd, path_bytes, perm, &has_created) or_return
  67. return nil if has_created else .Exist
  68. }
  69. _remove_all :: proc(path: string) -> Error {
  70. DT_DIR :: 4
  71. remove_all_dir :: proc(dfd: linux.Fd) -> Error {
  72. n := 64
  73. buf := make([]u8, n)
  74. defer delete(buf)
  75. loop: for {
  76. buflen, errno := linux.getdents(dfd, buf[:])
  77. #partial switch errno {
  78. case .EINVAL:
  79. delete(buf)
  80. n *= 2
  81. buf = make([]u8, n)
  82. continue loop
  83. case .NONE:
  84. if buflen == 0 { break loop }
  85. case:
  86. return _get_platform_error(errno)
  87. }
  88. offset: int
  89. for d in linux.dirent_iterate_buf(buf[:buflen], &offset) {
  90. d_name_str := linux.dirent_name(d)
  91. d_name_cstr := strings.unsafe_string_to_cstring(d_name_str)
  92. /* check for current or parent directory (. or ..) */
  93. if d_name_str == "." || d_name_str == ".." {
  94. continue
  95. }
  96. #partial switch d.type {
  97. case .DIR:
  98. new_dfd: linux.Fd
  99. new_dfd, errno = linux.openat(dfd, d_name_cstr, _OPENDIR_FLAGS)
  100. if errno != .NONE {
  101. return _get_platform_error(errno)
  102. }
  103. defer linux.close(new_dfd)
  104. remove_all_dir(new_dfd) or_return
  105. errno = linux.unlinkat(dfd, d_name_cstr, {.REMOVEDIR})
  106. case:
  107. errno = linux.unlinkat(dfd, d_name_cstr, nil)
  108. }
  109. if errno != .NONE {
  110. return _get_platform_error(errno)
  111. }
  112. }
  113. }
  114. return nil
  115. }
  116. TEMP_ALLOCATOR_GUARD()
  117. path_cstr := temp_cstring(path) or_return
  118. fd, errno := linux.open(path_cstr, _OPENDIR_FLAGS)
  119. #partial switch errno {
  120. case .NONE:
  121. break
  122. case .ENOTDIR:
  123. return _get_platform_error(linux.unlink(path_cstr))
  124. case:
  125. return _get_platform_error(errno)
  126. }
  127. defer linux.close(fd)
  128. remove_all_dir(fd) or_return
  129. return _get_platform_error(linux.rmdir(path_cstr))
  130. }
  131. _get_working_directory :: proc(allocator: runtime.Allocator) -> (string, Error) {
  132. // NOTE(tetra): I would use PATH_MAX here, but I was not able to find
  133. // an authoritative value for it across all systems.
  134. // The largest value I could find was 4096, so might as well use the page size.
  135. // NOTE(jason): Avoiding libc, so just use 4096 directly
  136. PATH_MAX :: 4096
  137. buf := make([dynamic]u8, PATH_MAX, allocator)
  138. for {
  139. #no_bounds_check n, errno := linux.getcwd(buf[:])
  140. if errno == .NONE {
  141. return string(buf[:n-1]), nil
  142. }
  143. if errno != .ERANGE {
  144. return "", _get_platform_error(errno)
  145. }
  146. resize(&buf, len(buf)+PATH_MAX)
  147. }
  148. unreachable()
  149. }
  150. _set_working_directory :: proc(dir: string) -> Error {
  151. dir_cstr := temp_cstring(dir) or_return
  152. return _get_platform_error(linux.chdir(dir_cstr))
  153. }
  154. _get_full_path :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (fullpath: string, err: Error) {
  155. PROC_FD_PATH :: "/proc/self/fd/"
  156. buf: [32]u8
  157. copy(buf[:], PROC_FD_PATH)
  158. strconv.itoa(buf[len(PROC_FD_PATH):], int(fd))
  159. if fullpath, err = _read_link_cstr(cstring(&buf[0]), allocator); err != nil || fullpath[0] != '/' {
  160. delete(fullpath, allocator)
  161. fullpath = ""
  162. }
  163. return
  164. }