stat_windows.odin 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. package os
  2. import "core:time"
  3. import win32 "core:sys/windows"
  4. @(private)
  5. full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Errno) {
  6. name := name;
  7. if name == "" {
  8. name = ".";
  9. }
  10. p := win32.utf8_to_utf16(name, context.temp_allocator);
  11. buf := make([dynamic]u16, 100, allocator);
  12. defer delete(buf);
  13. for {
  14. n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil);
  15. if n == 0 {
  16. return "", Errno(win32.GetLastError());
  17. }
  18. if n <= u32(len(buf)) {
  19. return win32.utf16_to_utf8(buf[:n], allocator), ERROR_NONE;
  20. }
  21. resize(&buf, len(buf)*2);
  22. }
  23. return;
  24. }
  25. @(private)
  26. _stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Errno) {
  27. if len(name) == 0 {
  28. return {}, ERROR_PATH_NOT_FOUND;
  29. }
  30. context.allocator = allocator;
  31. wname := win32.utf8_to_wstring(fix_long_path(name), context.temp_allocator);
  32. fa: win32.WIN32_FILE_ATTRIBUTE_DATA;
  33. ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa);
  34. if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
  35. // Not a symlink
  36. return file_info_from_win32_file_attribute_data(&fa, name);
  37. }
  38. err := 0 if ok else win32.GetLastError();
  39. if err == win32.ERROR_SHARING_VIOLATION {
  40. fd: win32.WIN32_FIND_DATAW;
  41. sh := win32.FindFirstFileW(wname, &fd);
  42. if sh == win32.INVALID_HANDLE_VALUE {
  43. e = Errno(win32.GetLastError());
  44. return;
  45. }
  46. win32.FindClose(sh);
  47. return file_info_from_win32_find_data(&fd, name);
  48. }
  49. h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil);
  50. if h == win32.INVALID_HANDLE_VALUE {
  51. e = Errno(win32.GetLastError());
  52. return;
  53. }
  54. defer win32.CloseHandle(h);
  55. return file_info_from_get_file_information_by_handle(name, h);
  56. }
  57. lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) {
  58. attrs := win32.FILE_FLAG_BACKUP_SEMANTICS;
  59. attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT;
  60. return _stat(name, attrs, allocator);
  61. }
  62. stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) {
  63. attrs := win32.FILE_FLAG_BACKUP_SEMANTICS;
  64. return _stat(name, attrs, allocator);
  65. }
  66. fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Errno) {
  67. if fd == 0 {
  68. return {}, ERROR_INVALID_HANDLE;
  69. }
  70. context.allocator = allocator;
  71. path, err := cleanpath_from_handle(fd);
  72. if err != ERROR_NONE {
  73. return {}, err;
  74. }
  75. h := win32.HANDLE(fd);
  76. switch win32.GetFileType(h) {
  77. case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
  78. fi: File_Info;
  79. fi.fullpath = path;
  80. fi.name = basename(path);
  81. fi.mode |= file_type_mode(h);
  82. return fi, ERROR_NONE;
  83. }
  84. return file_info_from_get_file_information_by_handle(path, h);
  85. }
  86. @(private)
  87. cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
  88. buf := buf;
  89. N := 0;
  90. for c, i in buf {
  91. if c == 0 { break; }
  92. N = i+1;
  93. }
  94. buf = buf[:N];
  95. if len(buf) >= 4 {
  96. if buf[0] == '\\' &&
  97. buf[1] == '\\' &&
  98. buf[2] == '?' &&
  99. buf[3] == '\\' {
  100. buf = buf[4:];
  101. }
  102. }
  103. return buf;
  104. }
  105. @(private)
  106. cleanpath_from_handle :: proc(fd: Handle) -> (string, Errno) {
  107. if fd == 0 {
  108. return "", ERROR_INVALID_HANDLE;
  109. }
  110. h := win32.HANDLE(fd);
  111. MAX_PATH := win32.DWORD(260) + 1;
  112. buf: []u16;
  113. for {
  114. buf = make([]u16, MAX_PATH, context.temp_allocator);
  115. err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0);
  116. switch Errno(err) {
  117. case ERROR_PATH_NOT_FOUND, ERROR_INVALID_PARAMETER:
  118. return "", Errno(err);
  119. case ERROR_NOT_ENOUGH_MEMORY:
  120. MAX_PATH = MAX_PATH*2 + 1;
  121. continue;
  122. }
  123. break;
  124. }
  125. return cleanpath_from_buf(buf), ERROR_NONE;
  126. }
  127. @(private)
  128. cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
  129. if fd == 0 {
  130. return nil, ERROR_INVALID_HANDLE;
  131. }
  132. h := win32.HANDLE(fd);
  133. MAX_PATH := win32.DWORD(260) + 1;
  134. buf: []u16;
  135. for {
  136. buf = make([]u16, MAX_PATH, context.temp_allocator);
  137. err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0);
  138. switch Errno(err) {
  139. case ERROR_PATH_NOT_FOUND, ERROR_INVALID_PARAMETER:
  140. return nil, Errno(err);
  141. case ERROR_NOT_ENOUGH_MEMORY:
  142. MAX_PATH = MAX_PATH*2 + 1;
  143. continue;
  144. }
  145. break;
  146. }
  147. return cleanpath_strip_prefix(buf), ERROR_NONE;
  148. }
  149. @(private)
  150. cleanpath_from_buf :: proc(buf: []u16) -> string {
  151. buf := buf;
  152. buf = cleanpath_strip_prefix(buf);
  153. return win32.utf16_to_utf8(buf, context.allocator);
  154. }
  155. @(private)
  156. basename :: proc(name: string) -> (base: string) {
  157. name := name;
  158. if len(name) > 3 && name[:3] == `\\?` {
  159. name = name[3:];
  160. }
  161. if len(name) == 2 && name[1] == ':' {
  162. return ".";
  163. } else if len(name) > 2 && name[1] == ':' {
  164. name = name[2:];
  165. }
  166. i := len(name)-1;
  167. for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i -= 1 {
  168. name = name[:i];
  169. }
  170. for i -= 1; i >= 0; i -= 1 {
  171. if name[i] == '/' || name[i] == '\\' {
  172. name = name[i+1:];
  173. break;
  174. }
  175. }
  176. return name;
  177. }
  178. @(private)
  179. file_type_mode :: proc(h: win32.HANDLE) -> File_Mode {
  180. switch win32.GetFileType(h) {
  181. case win32.FILE_TYPE_PIPE:
  182. return File_Mode_Named_Pipe;
  183. case win32.FILE_TYPE_CHAR:
  184. return File_Mode_Device | File_Mode_Char_Device;
  185. }
  186. return 0;
  187. }
  188. @(private)
  189. file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) {
  190. if FileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
  191. mode |= 0o444;
  192. } else {
  193. mode |= 0o666;
  194. }
  195. is_sym := false;
  196. if FileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
  197. is_sym = false;
  198. } else {
  199. is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT;
  200. }
  201. if is_sym {
  202. mode |= File_Mode_Sym_Link;
  203. } else {
  204. if FileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
  205. mode |= 0o111 | File_Mode_Dir;
  206. }
  207. if h != nil {
  208. mode |= file_type_mode(h);
  209. }
  210. }
  211. return;
  212. }
  213. @(private)
  214. file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) {
  215. fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow);
  216. fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0);
  217. fi.is_dir = fi.mode & File_Mode_Dir != 0;
  218. fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime));
  219. fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime));
  220. fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime));
  221. fi.fullpath, e = full_path_from_name(name);
  222. fi.name = basename(fi.fullpath);
  223. return;
  224. }
  225. @(private)
  226. file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Errno) {
  227. fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow);
  228. fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0);
  229. fi.is_dir = fi.mode & File_Mode_Dir != 0;
  230. fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime));
  231. fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime));
  232. fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime));
  233. fi.fullpath, e = full_path_from_name(name);
  234. fi.name = basename(fi.fullpath);
  235. return;
  236. }
  237. @(private)
  238. file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Errno) {
  239. d: win32.BY_HANDLE_FILE_INFORMATION;
  240. if !win32.GetFileInformationByHandle(h, &d) {
  241. err := Errno(win32.GetLastError());
  242. return {}, err;
  243. }
  244. ti: win32.FILE_ATTRIBUTE_TAG_INFO;
  245. if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) {
  246. err := win32.GetLastError();
  247. if err != u32(ERROR_INVALID_PARAMETER) {
  248. return {}, Errno(err);
  249. }
  250. // Indicate this is a symlink on FAT file systems
  251. ti.ReparseTag = 0;
  252. }
  253. fi: File_Info;
  254. fi.fullpath = path;
  255. fi.name = basename(path);
  256. fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow);
  257. fi.mode |= file_mode_from_file_attributes(ti.FileAttributes, h, ti.ReparseTag);
  258. fi.is_dir = fi.mode & File_Mode_Dir != 0;
  259. fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime));
  260. fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime));
  261. fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime));
  262. return fi, ERROR_NONE;
  263. }