| ... | ... | @@ -94,105 +94,6 @@ static clock_serv_t macos_monotonic_clock; |
| 94 | 94 | extern char **environ; |
| 95 | 95 | #endif |
| 96 | 96 | |
| 97 | | #if defined(ZIG_OS_POSIX) |
| 98 | | static void populate_termination(Termination *term, int status) { |
| 99 | | if (WIFEXITED(status)) { |
| 100 | | term->how = TerminationIdClean; |
| 101 | | term->code = WEXITSTATUS(status); |
| 102 | | } else if (WIFSIGNALED(status)) { |
| 103 | | term->how = TerminationIdSignaled; |
| 104 | | term->code = WTERMSIG(status); |
| 105 | | } else if (WIFSTOPPED(status)) { |
| 106 | | term->how = TerminationIdStopped; |
| 107 | | term->code = WSTOPSIG(status); |
| 108 | | } else { |
| 109 | | term->how = TerminationIdUnknown; |
| 110 | | term->code = status; |
| 111 | | } |
| 112 | | } |
| 113 | | |
| 114 | | static void os_spawn_process_posix(ZigList<const char *> &args, Termination *term) { |
| 115 | | const char **argv = heap::c_allocator.allocate<const char *>(args.length + 1); |
| 116 | | for (size_t i = 0; i < args.length; i += 1) { |
| 117 | | argv[i] = args.at(i); |
| 118 | | } |
| 119 | | argv[args.length] = nullptr; |
| 120 | | |
| 121 | | pid_t pid; |
| 122 | | int rc = posix_spawnp(&pid, args.at(0), nullptr, nullptr, const_cast<char *const*>(argv), environ); |
| 123 | | if (rc != 0) { |
| 124 | | zig_panic("unable to spawn %s: %s", args.at(0), strerror(rc)); |
| 125 | | } |
| 126 | | |
| 127 | | int status; |
| 128 | | waitpid(pid, &status, 0); |
| 129 | | populate_termination(term, status); |
| 130 | | } |
| 131 | | #endif |
| 132 | | |
| 133 | | #if defined(ZIG_OS_WINDOWS) |
| 134 | | |
| 135 | | static void os_windows_create_command_line(Buf *command_line, ZigList<const char *> &args) { |
| 136 | | buf_resize(command_line, 0); |
| 137 | | const char *prefix = "\""; |
| 138 | | for (size_t arg_i = 0; arg_i < args.length; arg_i += 1) { |
| 139 | | const char *arg = args.at(arg_i); |
| 140 | | buf_append_str(command_line, prefix); |
| 141 | | prefix = " \""; |
| 142 | | size_t arg_len = strlen(arg); |
| 143 | | for (size_t c_i = 0; c_i < arg_len; c_i += 1) { |
| 144 | | if (arg[c_i] == '\"') { |
| 145 | | zig_panic("TODO"); |
| 146 | | } |
| 147 | | buf_append_char(command_line, arg[c_i]); |
| 148 | | } |
| 149 | | buf_append_char(command_line, '\"'); |
| 150 | | } |
| 151 | | } |
| 152 | | |
| 153 | | static void os_spawn_process_windows(ZigList<const char *> &args, Termination *term) { |
| 154 | | Buf command_line = BUF_INIT; |
| 155 | | os_windows_create_command_line(&command_line, args); |
| 156 | | |
| 157 | | PROCESS_INFORMATION piProcInfo = {0}; |
| 158 | | STARTUPINFOW siStartInfo = {0}; |
| 159 | | siStartInfo.cb = sizeof(STARTUPINFOW); |
| 160 | | |
| 161 | | Slice<uint8_t> exe_slice = str(args.at(0)); |
| 162 | | auto exe_utf16_slice = Slice<WCHAR>::alloc(exe_slice.len + 1); |
| 163 | | exe_utf16_slice.ptr[utf8_to_utf16le(exe_utf16_slice.ptr, exe_slice)] = 0; |
| 164 | | |
| 165 | | auto command_line_utf16 = Slice<WCHAR>::alloc(buf_len(&command_line) + 1); |
| 166 | | command_line_utf16.ptr[utf8_to_utf16le(command_line_utf16.ptr, buf_to_slice(&command_line))] = 0; |
| 167 | | |
| 168 | | BOOL success = CreateProcessW(exe_utf16_slice.ptr, command_line_utf16.ptr, nullptr, nullptr, TRUE, CREATE_UNICODE_ENVIRONMENT, nullptr, nullptr, |
| 169 | | &siStartInfo, &piProcInfo); |
| 170 | | |
| 171 | | if (!success) { |
| 172 | | zig_panic("CreateProcess failed. exe: %s command_line: %s", args.at(0), buf_ptr(&command_line)); |
| 173 | | } |
| 174 | | |
| 175 | | WaitForSingleObject(piProcInfo.hProcess, INFINITE); |
| 176 | | |
| 177 | | DWORD exit_code; |
| 178 | | if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) { |
| 179 | | zig_panic("GetExitCodeProcess failed"); |
| 180 | | } |
| 181 | | term->how = TerminationIdClean; |
| 182 | | term->code = exit_code; |
| 183 | | } |
| 184 | | #endif |
| 185 | | |
| 186 | | void os_spawn_process(ZigList<const char *> &args, Termination *term) { |
| 187 | | #if defined(ZIG_OS_WINDOWS) |
| 188 | | os_spawn_process_windows(args, term); |
| 189 | | #elif defined(ZIG_OS_POSIX) |
| 190 | | os_spawn_process_posix(args, term); |
| 191 | | #else |
| 192 | | #error "missing os_spawn_process implementation" |
| 193 | | #endif |
| 194 | | } |
| 195 | | |
| 196 | 97 | void os_path_dirname(Buf *full_path, Buf *out_dirname) { |
| 197 | 98 | return os_path_split(full_path, out_dirname, nullptr); |
| 198 | 99 | } |
| ... | ... | @@ -280,71 +181,27 @@ void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) { |
| 280 | 181 | buf_append_buf(out_full_path, basename); |
| 281 | 182 | } |
| 282 | 183 | |
| 283 | | Error os_path_real(Buf *rel_path, Buf *out_abs_path) { |
| 284 | | #if defined(ZIG_OS_WINDOWS) |
| 285 | | PathSpace rel_path_space = slice_to_prefixed_file_w(buf_to_slice(rel_path)); |
| 286 | | PathSpace out_abs_path_space; |
| 287 | | |
| 288 | | if (_wfullpath(&out_abs_path_space.data.items[0], &rel_path_space.data.items[0], PATH_MAX_WIDE) == nullptr) { |
| 289 | | zig_panic("_wfullpath failed"); |
| 290 | | } |
| 291 | | utf16le_ptr_to_utf8(out_abs_path, &out_abs_path_space.data.items[0]); |
| 292 | | return ErrorNone; |
| 293 | | #elif defined(ZIG_OS_POSIX) |
| 294 | | buf_resize(out_abs_path, PATH_MAX + 1); |
| 295 | | char *result = realpath(buf_ptr(rel_path), buf_ptr(out_abs_path)); |
| 296 | | if (!result) { |
| 297 | | int err = errno; |
| 298 | | if (err == EACCES) { |
| 299 | | return ErrorAccess; |
| 300 | | } else if (err == ENOENT) { |
| 301 | | return ErrorFileNotFound; |
| 302 | | } else if (err == ENOMEM) { |
| 303 | | return ErrorNoMem; |
| 304 | | } else { |
| 305 | | return ErrorFileSystem; |
| 306 | | } |
| 307 | | } |
| 308 | | buf_resize(out_abs_path, strlen(buf_ptr(out_abs_path))); |
| 309 | | return ErrorNone; |
| 310 | | #else |
| 311 | | #error "missing os_path_real implementation" |
| 312 | | #endif |
| 313 | | } |
| 314 | | |
| 315 | | #if defined(ZIG_OS_WINDOWS) |
| 316 | | // Ported from std/os/path.zig |
| 317 | | static bool isAbsoluteWindows(Slice<uint8_t> path) { |
| 318 | | if (path.ptr[0] == '/') |
| 319 | | return true; |
| 320 | | |
| 321 | | if (path.ptr[0] == '\\') { |
| 322 | | return true; |
| 323 | | } |
| 324 | | if (path.len < 3) { |
| 325 | | return false; |
| 326 | | } |
| 327 | | if (path.ptr[1] == ':') { |
| 328 | | if (path.ptr[2] == '/') |
| 329 | | return true; |
| 330 | | if (path.ptr[2] == '\\') |
| 331 | | return true; |
| 332 | | } |
| 333 | | return false; |
| 334 | | } |
| 335 | | #endif |
| 336 | | |
| 337 | | bool os_path_is_absolute(Buf *path) { |
| 338 | | #if defined(ZIG_OS_WINDOWS) |
| 339 | | return isAbsoluteWindows(buf_to_slice(path)); |
| 340 | | #elif defined(ZIG_OS_POSIX) |
| 341 | | return buf_ptr(path)[0] == '/'; |
| 342 | | #else |
| 343 | | #error "missing os_path_is_absolute implementation" |
| 344 | | #endif |
| 345 | | } |
| 346 | 184 | |
| 347 | | #if defined(ZIG_OS_WINDOWS) |
| 185 | #if defined(ZIG_OS_WINDOWS)		 |
| 186 | // Ported from std/os/path.zig		 |
| 187 | static bool isAbsoluteWindows(Slice<uint8_t> path) {		 |
| 188 | if (path.ptr[0] == '/')		 |
| 189 | return true;		 |
| 190 | |
| 191 | if (path.ptr[0] == '\\') {		 |
| 192 | return true;		 |
| 193 | }		 |
| 194 | if (path.len < 3) {		 |
| 195 | return false;		 |
| 196 | }		 |
| 197 | if (path.ptr[1] == ':') {		 |
| 198 | if (path.ptr[2] == '/')		 |
| 199 | return true;		 |
| 200 | if (path.ptr[2] == '\\')		 |
| 201 | return true;		 |
| 202 | }		 |
| 203 | return false;		 |
| 204 | }		 |
| 348 | 205 | |
| 349 | 206 | enum WindowsPathKind { |
| 350 | 207 | WindowsPathKindNone, |
| ... | ... | @@ -687,7 +544,7 @@ static Buf os_path_resolve_posix(Buf **paths_ptr, size_t paths_len) { |
| 687 | 544 | size_t max_size = 0; |
| 688 | 545 | for (size_t i = 0; i < paths_len; i += 1) { |
| 689 | 546 | Buf *p = paths_ptr[i]; |
| 690 | | if (os_path_is_absolute(p)) { |
| 547 | if (buf_ptr(p)[0] == '/') { |
| 691 | 548 | first_index = i; |
| 692 | 549 | have_abs = true; |
| 693 | 550 | max_size = 0; |
| ... | ... | @@ -786,256 +643,6 @@ Error os_fetch_file(FILE *f, Buf *out_buf) { |
| 786 | 643 | zig_unreachable(); |
| 787 | 644 | } |
| 788 | 645 | |
| 789 | | Error os_file_exists(Buf *full_path, bool *result) { |
| 790 | | #if defined(ZIG_OS_WINDOWS) |
| 791 | | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); |
| 792 | | *result = GetFileAttributesW(&path_space.data.items[0]) != INVALID_FILE_ATTRIBUTES; |
| 793 | | return ErrorNone; |
| 794 | | #else |
| 795 | | *result = access(buf_ptr(full_path), F_OK) != -1; |
| 796 | | return ErrorNone; |
| 797 | | #endif |
| 798 | | } |
| 799 | | |
| 800 | | #if defined(ZIG_OS_POSIX) |
| 801 | | static Error os_exec_process_posix(ZigList<const char *> &args, |
| 802 | | Termination *term, Buf *out_stderr, Buf *out_stdout) |
| 803 | | { |
| 804 | | int stdin_pipe[2]; |
| 805 | | int stdout_pipe[2]; |
| 806 | | int stderr_pipe[2]; |
| 807 | | int err_pipe[2]; |
| 808 | | |
| 809 | | int err; |
| 810 | | if ((err = pipe(stdin_pipe))) |
| 811 | | zig_panic("pipe failed"); |
| 812 | | if ((err = pipe(stdout_pipe))) |
| 813 | | zig_panic("pipe failed"); |
| 814 | | if ((err = pipe(stderr_pipe))) |
| 815 | | zig_panic("pipe failed"); |
| 816 | | if ((err = pipe(err_pipe))) |
| 817 | | zig_panic("pipe failed"); |
| 818 | | |
| 819 | | pid_t pid = fork(); |
| 820 | | if (pid == -1) |
| 821 | | zig_panic("fork failed: %s", strerror(errno)); |
| 822 | | if (pid == 0) { |
| 823 | | // child |
| 824 | | if (dup2(stdin_pipe[0], STDIN_FILENO) == -1) |
| 825 | | zig_panic("dup2 failed"); |
| 826 | | |
| 827 | | if (dup2(stdout_pipe[1], STDOUT_FILENO) == -1) |
| 828 | | zig_panic("dup2 failed"); |
| 829 | | |
| 830 | | if (dup2(stderr_pipe[1], STDERR_FILENO) == -1) |
| 831 | | zig_panic("dup2 failed"); |
| 832 | | |
| 833 | | const char **argv = heap::c_allocator.allocate<const char *>(args.length + 1); |
| 834 | | argv[args.length] = nullptr; |
| 835 | | for (size_t i = 0; i < args.length; i += 1) { |
| 836 | | argv[i] = args.at(i); |
| 837 | | } |
| 838 | | execvp(argv[0], const_cast<char * const *>(argv)); |
| 839 | | Error report_err = ErrorUnexpected; |
| 840 | | if (errno == ENOENT) { |
| 841 | | report_err = ErrorFileNotFound; |
| 842 | | } |
| 843 | | if (write(err_pipe[1], &report_err, sizeof(Error)) == -1) { |
| 844 | | zig_panic("write failed"); |
| 845 | | } |
| 846 | | exit(1); |
| 847 | | } else { |
| 848 | | // parent |
| 849 | | close(stdin_pipe[0]); |
| 850 | | close(stdin_pipe[1]); |
| 851 | | close(stdout_pipe[1]); |
| 852 | | close(stderr_pipe[1]); |
| 853 | | |
| 854 | | int status; |
| 855 | | waitpid(pid, &status, 0); |
| 856 | | populate_termination(term, status); |
| 857 | | |
| 858 | | FILE *stdout_f = fdopen(stdout_pipe[0], "rb"); |
| 859 | | FILE *stderr_f = fdopen(stderr_pipe[0], "rb"); |
| 860 | | Error err1 = os_fetch_file(stdout_f, out_stdout); |
| 861 | | Error err2 = os_fetch_file(stderr_f, out_stderr); |
| 862 | | |
| 863 | | fclose(stdout_f); |
| 864 | | fclose(stderr_f); |
| 865 | | |
| 866 | | if (err1) return err1; |
| 867 | | if (err2) return err2; |
| 868 | | |
| 869 | | Error child_err = ErrorNone; |
| 870 | | if (write(err_pipe[1], &child_err, sizeof(Error)) == -1) { |
| 871 | | zig_panic("write failed"); |
| 872 | | } |
| 873 | | close(err_pipe[1]); |
| 874 | | if (read(err_pipe[0], &child_err, sizeof(Error)) == -1) { |
| 875 | | zig_panic("write failed"); |
| 876 | | } |
| 877 | | close(err_pipe[0]); |
| 878 | | return child_err; |
| 879 | | } |
| 880 | | } |
| 881 | | #endif |
| 882 | | |
| 883 | | #if defined(ZIG_OS_WINDOWS) |
| 884 | | |
| 885 | | //static void win32_panic(const char *str) { |
| 886 | | // DWORD err = GetLastError(); |
| 887 | | // LPSTR messageBuffer = nullptr; |
| 888 | | // FormatMessageA( |
| 889 | | // FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, |
| 890 | | // NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); |
| 891 | | // zig_panic(str, messageBuffer); |
| 892 | | // LocalFree(messageBuffer); |
| 893 | | //} |
| 894 | | |
| 895 | | static Error os_exec_process_windows(ZigList<const char *> &args, |
| 896 | | Termination *term, Buf *out_stderr, Buf *out_stdout) |
| 897 | | { |
| 898 | | Buf command_line = BUF_INIT; |
| 899 | | os_windows_create_command_line(&command_line, args); |
| 900 | | |
| 901 | | HANDLE g_hChildStd_IN_Rd = NULL; |
| 902 | | HANDLE g_hChildStd_IN_Wr = NULL; |
| 903 | | HANDLE g_hChildStd_OUT_Rd = NULL; |
| 904 | | HANDLE g_hChildStd_OUT_Wr = NULL; |
| 905 | | HANDLE g_hChildStd_ERR_Rd = NULL; |
| 906 | | HANDLE g_hChildStd_ERR_Wr = NULL; |
| 907 | | |
| 908 | | SECURITY_ATTRIBUTES saAttr; |
| 909 | | saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); |
| 910 | | saAttr.bInheritHandle = TRUE; |
| 911 | | saAttr.lpSecurityDescriptor = NULL; |
| 912 | | |
| 913 | | if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) { |
| 914 | | zig_panic("StdoutRd CreatePipe"); |
| 915 | | } |
| 916 | | |
| 917 | | if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) { |
| 918 | | zig_panic("Stdout SetHandleInformation"); |
| 919 | | } |
| 920 | | |
| 921 | | if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr, 0)) { |
| 922 | | zig_panic("stderr CreatePipe"); |
| 923 | | } |
| 924 | | |
| 925 | | if (!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) { |
| 926 | | zig_panic("stderr SetHandleInformation"); |
| 927 | | } |
| 928 | | |
| 929 | | if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) { |
| 930 | | zig_panic("Stdin CreatePipe"); |
| 931 | | } |
| 932 | | |
| 933 | | if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) { |
| 934 | | zig_panic("Stdin SetHandleInformation"); |
| 935 | | } |
| 936 | | |
| 937 | | |
| 938 | | PROCESS_INFORMATION piProcInfo = {0}; |
| 939 | | STARTUPINFO siStartInfo = {0}; |
| 940 | | siStartInfo.cb = sizeof(STARTUPINFO); |
| 941 | | siStartInfo.hStdError = g_hChildStd_ERR_Wr; |
| 942 | | siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; |
| 943 | | siStartInfo.hStdInput = g_hChildStd_IN_Rd; |
| 944 | | siStartInfo.dwFlags |= STARTF_USESTDHANDLES; |
| 945 | | |
| 946 | | const char *exe = args.at(0); |
| 947 | | BOOL success = CreateProcess(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr, |
| 948 | | &siStartInfo, &piProcInfo); |
| 949 | | |
| 950 | | if (!success) { |
| 951 | | if (GetLastError() == ERROR_FILE_NOT_FOUND) { |
| 952 | | CloseHandle(piProcInfo.hProcess); |
| 953 | | CloseHandle(piProcInfo.hThread); |
| 954 | | return ErrorFileNotFound; |
| 955 | | } |
| 956 | | zig_panic("CreateProcess failed. exe: %s command_line: %s", exe, buf_ptr(&command_line)); |
| 957 | | } |
| 958 | | |
| 959 | | if (!CloseHandle(g_hChildStd_IN_Wr)) { |
| 960 | | zig_panic("stdinwr closehandle"); |
| 961 | | } |
| 962 | | |
| 963 | | CloseHandle(g_hChildStd_IN_Rd); |
| 964 | | CloseHandle(g_hChildStd_ERR_Wr); |
| 965 | | CloseHandle(g_hChildStd_OUT_Wr); |
| 966 | | |
| 967 | | static const size_t BUF_SIZE = 4 * 1024; |
| 968 | | { |
| 969 | | DWORD dwRead; |
| 970 | | char chBuf[BUF_SIZE]; |
| 971 | | |
| 972 | | buf_resize(out_stdout, 0); |
| 973 | | for (;;) { |
| 974 | | success = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUF_SIZE, &dwRead, NULL); |
| 975 | | if (!success || dwRead == 0) break; |
| 976 | | |
| 977 | | buf_append_mem(out_stdout, chBuf, dwRead); |
| 978 | | } |
| 979 | | CloseHandle(g_hChildStd_OUT_Rd); |
| 980 | | } |
| 981 | | { |
| 982 | | DWORD dwRead; |
| 983 | | char chBuf[BUF_SIZE]; |
| 984 | | |
| 985 | | buf_resize(out_stderr, 0); |
| 986 | | for (;;) { |
| 987 | | success = ReadFile( g_hChildStd_ERR_Rd, chBuf, BUF_SIZE, &dwRead, NULL); |
| 988 | | if (!success || dwRead == 0) break; |
| 989 | | |
| 990 | | buf_append_mem(out_stderr, chBuf, dwRead); |
| 991 | | } |
| 992 | | CloseHandle(g_hChildStd_ERR_Rd); |
| 993 | | } |
| 994 | | |
| 995 | | WaitForSingleObject(piProcInfo.hProcess, INFINITE); |
| 996 | | |
| 997 | | DWORD exit_code; |
| 998 | | if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) { |
| 999 | | zig_panic("GetExitCodeProcess failed"); |
| 1000 | | } |
| 1001 | | term->how = TerminationIdClean; |
| 1002 | | term->code = exit_code; |
| 1003 | | |
| 1004 | | CloseHandle(piProcInfo.hProcess); |
| 1005 | | CloseHandle(piProcInfo.hThread); |
| 1006 | | |
| 1007 | | return ErrorNone; |
| 1008 | | } |
| 1009 | | #endif |
| 1010 | | |
| 1011 | | Error os_execv(const char *exe, const char **argv) { |
| 1012 | | #if defined(ZIG_OS_WINDOWS) |
| 1013 | | return ErrorUnsupportedOperatingSystem; |
| 1014 | | #else |
| 1015 | | execv(exe, (char *const *)argv); |
| 1016 | | switch (errno) { |
| 1017 | | case ENOMEM: |
| 1018 | | return ErrorSystemResources; |
| 1019 | | case EIO: |
| 1020 | | return ErrorFileSystem; |
| 1021 | | default: |
| 1022 | | return ErrorUnexpected; |
| 1023 | | } |
| 1024 | | #endif |
| 1025 | | } |
| 1026 | | |
| 1027 | | Error os_exec_process(ZigList<const char *> &args, |
| 1028 | | Termination *term, Buf *out_stderr, Buf *out_stdout) |
| 1029 | | { |
| 1030 | | #if defined(ZIG_OS_WINDOWS) |
| 1031 | | return os_exec_process_windows(args, term, out_stderr, out_stdout); |
| 1032 | | #elif defined(ZIG_OS_POSIX) |
| 1033 | | return os_exec_process_posix(args, term, out_stderr, out_stdout); |
| 1034 | | #else |
| 1035 | | #error "missing os_exec_process implementation" |
| 1036 | | #endif |
| 1037 | | } |
| 1038 | | |
| 1039 | 646 | Error os_write_file(Buf *full_path, Buf *contents) { |
| 1040 | 647 | #if defined(ZIG_OS_WINDOWS) |
| 1041 | 648 | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); |
| ... | ... | @@ -1074,35 +681,6 @@ static Error copy_open_files(FILE *src_f, FILE *dest_f) { |
| 1074 | 681 | } |
| 1075 | 682 | } |
| 1076 | 683 | |
| 1077 | | Error os_dump_file(Buf *src_path, FILE *dest_file) { |
| 1078 | | Error err; |
| 1079 | | |
| 1080 | | #if defined(ZIG_OS_WINDOWS) |
| 1081 | | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(src_path)); |
| 1082 | | FILE *src_f = _wfopen(&path_space.data.items[0], L"rb"); |
| 1083 | | #else |
| 1084 | | FILE *src_f = fopen(buf_ptr(src_path), "rb"); |
| 1085 | | #endif |
| 1086 | | if (!src_f) { |
| 1087 | | int err = errno; |
| 1088 | | if (err == ENOENT) { |
| 1089 | | return ErrorFileNotFound; |
| 1090 | | } else if (err == EACCES || err == EPERM) { |
| 1091 | | return ErrorAccess; |
| 1092 | | } else { |
| 1093 | | return ErrorFileSystem; |
| 1094 | | } |
| 1095 | | } |
| 1096 | | copy_open_files(src_f, dest_file); |
| 1097 | | if ((err = copy_open_files(src_f, dest_file))) { |
| 1098 | | fclose(src_f); |
| 1099 | | return err; |
| 1100 | | } |
| 1101 | | |
| 1102 | | fclose(src_f); |
| 1103 | | return ErrorNone; |
| 1104 | | } |
| 1105 | | |
| 1106 | 684 | #if defined(ZIG_OS_WINDOWS) |
| 1107 | 685 | static void windows_filetime_to_os_timestamp(FILETIME *ft, OsTimeStamp *mtime) { |
| 1108 | 686 | mtime->sec = (((ULONGLONG) ft->dwHighDateTime) << 32) + ft->dwLowDateTime; |
| ... | ... | @@ -1116,88 +694,6 @@ static FILETIME windows_os_timestamp_to_filetime(OsTimeStamp mtime) { |
| 1116 | 694 | } |
| 1117 | 695 | #endif |
| 1118 | 696 | |
| 1119 | | static Error set_file_times(OsFile file, OsTimeStamp ts) { |
| 1120 | | #if defined(ZIG_OS_WINDOWS) |
| 1121 | | FILETIME ft = windows_os_timestamp_to_filetime(ts); |
| 1122 | | if (SetFileTime(file, nullptr, &ft, &ft) == 0) { |
| 1123 | | return ErrorUnexpected; |
| 1124 | | } |
| 1125 | | return ErrorNone; |
| 1126 | | #else |
| 1127 | | struct timespec times[2] = { |
| 1128 | | { (time_t)ts.sec, (long)ts.nsec }, |
| 1129 | | { (time_t)ts.sec, (long)ts.nsec }, |
| 1130 | | }; |
| 1131 | | if (futimens(file, times) == -1) { |
| 1132 | | switch (errno) { |
| 1133 | | case EBADF: |
| 1134 | | zig_panic("futimens EBADF"); |
| 1135 | | default: |
| 1136 | | return ErrorUnexpected; |
| 1137 | | } |
| 1138 | | } |
| 1139 | | return ErrorNone; |
| 1140 | | #endif |
| 1141 | | } |
| 1142 | | |
| 1143 | | Error os_update_file(Buf *src_path, Buf *dst_path) { |
| 1144 | | Error err; |
| 1145 | | |
| 1146 | | OsFile src_file; |
| 1147 | | OsFileAttr src_attr; |
| 1148 | | if ((err = os_file_open_r(src_path, &src_file, &src_attr))) { |
| 1149 | | return err; |
| 1150 | | } |
| 1151 | | |
| 1152 | | OsFile dst_file; |
| 1153 | | OsFileAttr dst_attr; |
| 1154 | | if ((err = os_file_open_w(dst_path, &dst_file, &dst_attr, src_attr.mode))) { |
| 1155 | | os_file_close(&src_file); |
| 1156 | | return err; |
| 1157 | | } |
| 1158 | | |
| 1159 | | if (src_attr.size == dst_attr.size && |
| 1160 | | src_attr.mode == dst_attr.mode && |
| 1161 | | src_attr.mtime.sec == dst_attr.mtime.sec && |
| 1162 | | src_attr.mtime.nsec == dst_attr.mtime.nsec) |
| 1163 | | { |
| 1164 | | os_file_close(&src_file); |
| 1165 | | os_file_close(&dst_file); |
| 1166 | | return ErrorNone; |
| 1167 | | } |
| 1168 | | #if defined(ZIG_OS_WINDOWS) |
| 1169 | | if (SetEndOfFile(dst_file) == 0) { |
| 1170 | | return ErrorUnexpected; |
| 1171 | | } |
| 1172 | | #else |
| 1173 | | if (ftruncate(dst_file, 0) == -1) { |
| 1174 | | return ErrorUnexpected; |
| 1175 | | } |
| 1176 | | #endif |
| 1177 | | #if defined(ZIG_OS_WINDOWS) |
| 1178 | | FILE *src_libc_file = _fdopen(_open_osfhandle((intptr_t)src_file, _O_RDONLY), "rb"); |
| 1179 | | FILE *dst_libc_file = _fdopen(_open_osfhandle((intptr_t)dst_file, 0), "wb"); |
| 1180 | | #else |
| 1181 | | FILE *src_libc_file = fdopen(src_file, "rb"); |
| 1182 | | FILE *dst_libc_file = fdopen(dst_file, "wb"); |
| 1183 | | #endif |
| 1184 | | assert(src_libc_file); |
| 1185 | | assert(dst_libc_file); |
| 1186 | | |
| 1187 | | if ((err = copy_open_files(src_libc_file, dst_libc_file))) { |
| 1188 | | fclose(src_libc_file); |
| 1189 | | fclose(dst_libc_file); |
| 1190 | | return err; |
| 1191 | | } |
| 1192 | | if (fflush(dst_libc_file) == -1) { |
| 1193 | | return ErrorUnexpected; |
| 1194 | | } |
| 1195 | | err = set_file_times(dst_file, src_attr.mtime); |
| 1196 | | fclose(src_libc_file); |
| 1197 | | fclose(dst_libc_file); |
| 1198 | | return err; |
| 1199 | | } |
| 1200 | | |
| 1201 | 697 | Error os_copy_file(Buf *src_path, Buf *dest_path) { |
| 1202 | 698 | #if defined(ZIG_OS_WINDOWS) |
| 1203 | 699 | PathSpace src_path_space = slice_to_prefixed_file_w(buf_to_slice(src_path)); |
| ... | ... | @@ -1358,14 +854,6 @@ bool os_stderr_tty(void) { |
| 1358 | 854 | #endif |
| 1359 | 855 | } |
| 1360 | 856 | |
| 1361 | | Error os_delete_file(Buf *path) { |
| 1362 | | if (remove(buf_ptr(path))) { |
| 1363 | | return ErrorFileSystem; |
| 1364 | | } else { |
| 1365 | | return ErrorNone; |
| 1366 | | } |
| 1367 | | } |
| 1368 | | |
| 1369 | 857 | Error os_rename(Buf *src_path, Buf *dest_path) { |
| 1370 | 858 | if (buf_eql_buf(src_path, dest_path)) { |
| 1371 | 859 | return ErrorNone; |
| ... | ... | @@ -1384,30 +872,6 @@ Error os_rename(Buf *src_path, Buf *dest_path) { |
| 1384 | 872 | return ErrorNone; |
| 1385 | 873 | } |
| 1386 | 874 | |
| 1387 | | OsTimeStamp os_timestamp_calendar(void) { |
| 1388 | | OsTimeStamp result; |
| 1389 | | #if defined(ZIG_OS_WINDOWS) |
| 1390 | | FILETIME ft; |
| 1391 | | GetSystemTimeAsFileTime(&ft); |
| 1392 | | windows_filetime_to_os_timestamp(&ft, &result); |
| 1393 | | #elif defined(__MACH__) |
| 1394 | | mach_timespec_t mts; |
| 1395 | | |
| 1396 | | kern_return_t err = clock_get_time(macos_calendar_clock, &mts); |
| 1397 | | assert(!err); |
| 1398 | | |
| 1399 | | result.sec = mts.tv_sec; |
| 1400 | | result.nsec = mts.tv_nsec; |
| 1401 | | #else |
| 1402 | | struct timespec tms; |
| 1403 | | clock_gettime(CLOCK_REALTIME, &tms); |
| 1404 | | |
| 1405 | | result.sec = tms.tv_sec; |
| 1406 | | result.nsec = tms.tv_nsec; |
| 1407 | | #endif |
| 1408 | | return result; |
| 1409 | | } |
| 1410 | | |
| 1411 | 875 | OsTimeStamp os_timestamp_monotonic(void) { |
| 1412 | 876 | OsTimeStamp result; |
| 1413 | 877 | #if defined(ZIG_OS_WINDOWS) |
| ... | ... | @@ -1501,49 +965,8 @@ Error os_make_dir(Buf *path) { |
| 1501 | 965 | #endif |
| 1502 | 966 | } |
| 1503 | 967 | |
| 1504 | | static void init_rand() { |
| 1505 | | #if defined(ZIG_OS_WINDOWS) |
| 1506 | | char bytes[sizeof(unsigned)]; |
| 1507 | | unsigned seed; |
| 1508 | | RtlGenRandom(bytes, sizeof(unsigned)); |
| 1509 | | memcpy(&seed, bytes, sizeof(unsigned)); |
| 1510 | | srand(seed); |
| 1511 | | #elif defined(ZIG_OS_LINUX) |
| 1512 | | unsigned char *ptr_random = (unsigned char*)getauxval(AT_RANDOM); |
| 1513 | | unsigned seed; |
| 1514 | | memcpy(&seed, ptr_random, sizeof(seed)); |
| 1515 | | srand(seed); |
| 1516 | | #elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) |
| 1517 | | unsigned seed; |
| 1518 | | size_t len = sizeof(seed); |
| 1519 | | int mib[2] = { CTL_KERN, KERN_ARND }; |
| 1520 | | if (sysctl(mib, 2, &seed, &len, NULL, 0) != 0) { |
| 1521 | | zig_panic("unable to query random data from sysctl"); |
| 1522 | | } |
| 1523 | | srand(seed); |
| 1524 | | #else |
| 1525 | | int fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC); |
| 1526 | | if (fd == -1) { |
| 1527 | | zig_panic("unable to open /dev/urandom"); |
| 1528 | | } |
| 1529 | | char bytes[sizeof(unsigned)]; |
| 1530 | | ssize_t amt_read; |
| 1531 | | while ((amt_read = read(fd, bytes, sizeof(unsigned))) == -1) { |
| 1532 | | if (errno == EINTR) continue; |
| 1533 | | zig_panic("unable to read /dev/urandom"); |
| 1534 | | } |
| 1535 | | if (amt_read != sizeof(unsigned)) { |
| 1536 | | zig_panic("unable to read enough bytes from /dev/urandom"); |
| 1537 | | } |
| 1538 | | close(fd); |
| 1539 | | unsigned seed; |
| 1540 | | memcpy(&seed, bytes, sizeof(unsigned)); |
| 1541 | | srand(seed); |
| 1542 | | #endif |
| 1543 | | } |
| 1544 | 968 | |
| 1545 | 969 | int os_init(void) { |
| 1546 | | init_rand(); |
| 1547 | 970 | #if defined(ZIG_OS_WINDOWS) |
| 1548 | 971 | _setmode(fileno(stdout), _O_BINARY); |
| 1549 | 972 | _setmode(fileno(stderr), _O_BINARY); |
| ... | ... | @@ -1580,71 +1003,6 @@ int os_init(void) { |
| 1580 | 1003 | return 0; |
| 1581 | 1004 | } |
| 1582 | 1005 | |
| 1583 | | Error os_self_exe_path(Buf *out_path) { |
| 1584 | | #if defined(ZIG_OS_WINDOWS) |
| 1585 | | PathSpace path_space; |
| 1586 | | DWORD copied_amt = GetModuleFileNameW(nullptr, &path_space.data.items[0], PATH_MAX_WIDE); |
| 1587 | | if (copied_amt <= 0) { |
| 1588 | | return ErrorFileNotFound; |
| 1589 | | } |
| 1590 | | utf16le_ptr_to_utf8(out_path, &path_space.data.items[0]); |
| 1591 | | return ErrorNone; |
| 1592 | | |
| 1593 | | #elif defined(ZIG_OS_DARWIN) |
| 1594 | | // How long is the executable's path? |
| 1595 | | uint32_t u32_len = 0; |
| 1596 | | int ret1 = _NSGetExecutablePath(nullptr, &u32_len); |
| 1597 | | assert(ret1 != 0); |
| 1598 | | |
| 1599 | | Buf *tmp = buf_alloc_fixed(u32_len); |
| 1600 | | |
| 1601 | | // Fill the executable path. |
| 1602 | | int ret2 = _NSGetExecutablePath(buf_ptr(tmp), &u32_len); |
| 1603 | | assert(ret2 == 0); |
| 1604 | | |
| 1605 | | // According to libuv project, PATH_MAX*2 works around a libc bug where |
| 1606 | | // the resolved path is sometimes bigger than PATH_MAX. |
| 1607 | | buf_resize(out_path, PATH_MAX*2); |
| 1608 | | char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path)); |
| 1609 | | if (!real_path) { |
| 1610 | | buf_init_from_buf(out_path, tmp); |
| 1611 | | return ErrorNone; |
| 1612 | | } |
| 1613 | | |
| 1614 | | // Resize out_path for the correct length. |
| 1615 | | buf_resize(out_path, strlen(buf_ptr(out_path))); |
| 1616 | | |
| 1617 | | return ErrorNone; |
| 1618 | | #elif defined(ZIG_OS_LINUX) |
| 1619 | | buf_resize(out_path, PATH_MAX); |
| 1620 | | ssize_t amt = readlink("/proc/self/exe", buf_ptr(out_path), buf_len(out_path)); |
| 1621 | | if (amt == -1) { |
| 1622 | | return ErrorUnexpected; |
| 1623 | | } |
| 1624 | | buf_resize(out_path, amt); |
| 1625 | | return ErrorNone; |
| 1626 | | #elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_DRAGONFLY) |
| 1627 | | buf_resize(out_path, PATH_MAX); |
| 1628 | | int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; |
| 1629 | | size_t cb = PATH_MAX; |
| 1630 | | if (sysctl(mib, 4, buf_ptr(out_path), &cb, nullptr, 0) != 0) { |
| 1631 | | return ErrorUnexpected; |
| 1632 | | } |
| 1633 | | buf_resize(out_path, cb - 1); |
| 1634 | | return ErrorNone; |
| 1635 | | #elif defined(ZIG_OS_NETBSD) |
| 1636 | | buf_resize(out_path, PATH_MAX); |
| 1637 | | int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; |
| 1638 | | size_t cb = PATH_MAX; |
| 1639 | | if (sysctl(mib, 4, buf_ptr(out_path), &cb, nullptr, 0) != 0) { |
| 1640 | | return ErrorUnexpected; |
| 1641 | | } |
| 1642 | | buf_resize(out_path, cb - 1); |
| 1643 | | return ErrorNone; |
| 1644 | | #endif |
| 1645 | | return ErrorFileNotFound; |
| 1646 | | } |
| 1647 | | |
| 1648 | 1006 | #define VT_RED "\x1b[31;1m" |
| 1649 | 1007 | #define VT_GREEN "\x1b[32;1m" |
| 1650 | 1008 | #define VT_CYAN "\x1b[36;1m" |
| ... | ... | @@ -1954,392 +1312,3 @@ PathSpace slice_to_prefixed_file_w(Slice<uint8_t> path) { |
| 1954 | 1312 | return path_space; |
| 1955 | 1313 | } |
| 1956 | 1314 | #endif |
| 1957 | | |
| 1958 | | // Ported from std.os.getAppDataDir |
| 1959 | | Error os_get_app_data_dir(Buf *out_path, const char *appname) { |
| 1960 | | #if defined(ZIG_OS_WINDOWS) |
| 1961 | | WCHAR *dir_path_ptr; |
| 1962 | | switch (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &dir_path_ptr)) { |
| 1963 | | case S_OK: |
| 1964 | | // defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); |
| 1965 | | utf16le_ptr_to_utf8(out_path, dir_path_ptr); |
| 1966 | | CoTaskMemFree(dir_path_ptr); |
| 1967 | | buf_appendf(out_path, "\\%s", appname); |
| 1968 | | return ErrorNone; |
| 1969 | | case E_OUTOFMEMORY: |
| 1970 | | return ErrorNoMem; |
| 1971 | | default: |
| 1972 | | return ErrorUnexpected; |
| 1973 | | } |
| 1974 | | zig_unreachable(); |
| 1975 | | #elif defined(ZIG_OS_DARWIN) |
| 1976 | | const char *home_dir = getenv("HOME"); |
| 1977 | | if (home_dir == nullptr) { |
| 1978 | | // TODO use /etc/passwd |
| 1979 | | return ErrorFileNotFound; |
| 1980 | | } |
| 1981 | | buf_resize(out_path, 0); |
| 1982 | | buf_appendf(out_path, "%s/Library/Application Support/%s", home_dir, appname); |
| 1983 | | return ErrorNone; |
| 1984 | | #elif defined(ZIG_OS_POSIX) |
| 1985 | | const char *cache_dir = getenv("XDG_CACHE_HOME"); |
| 1986 | | if (cache_dir == nullptr) { |
| 1987 | | cache_dir = getenv("HOME"); |
| 1988 | | if (cache_dir == nullptr) { |
| 1989 | | // TODO use /etc/passwd |
| 1990 | | return ErrorFileNotFound; |
| 1991 | | } |
| 1992 | | if (cache_dir[0] == 0) { |
| 1993 | | return ErrorFileNotFound; |
| 1994 | | } |
| 1995 | | buf_init_from_str(out_path, cache_dir); |
| 1996 | | if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') { |
| 1997 | | buf_append_char(out_path, '/'); |
| 1998 | | } |
| 1999 | | buf_appendf(out_path, ".cache/%s", appname); |
| 2000 | | } else { |
| 2001 | | if (cache_dir[0] == 0) { |
| 2002 | | return ErrorFileNotFound; |
| 2003 | | } |
| 2004 | | buf_init_from_str(out_path, cache_dir); |
| 2005 | | if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') { |
| 2006 | | buf_append_char(out_path, '/'); |
| 2007 | | } |
| 2008 | | buf_appendf(out_path, "%s", appname); |
| 2009 | | } |
| 2010 | | return ErrorNone; |
| 2011 | | #endif |
| 2012 | | } |
| 2013 | | |
| 2014 | | #if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) |
| 2015 | | static int self_exe_shared_libs_callback(struct dl_phdr_info *info, size_t size, void *data) { |
| 2016 | | ZigList<Buf *> *libs = reinterpret_cast< ZigList<Buf *> *>(data); |
| 2017 | | if (info->dlpi_name[0] == '/') { |
| 2018 | | libs->append(buf_create_from_str(info->dlpi_name)); |
| 2019 | | } |
| 2020 | | return 0; |
| 2021 | | } |
| 2022 | | #endif |
| 2023 | | |
| 2024 | | Error os_self_exe_shared_libs(ZigList<Buf *> &paths) { |
| 2025 | | #if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) |
| 2026 | | paths.resize(0); |
| 2027 | | dl_iterate_phdr(self_exe_shared_libs_callback, &paths); |
| 2028 | | return ErrorNone; |
| 2029 | | #elif defined(ZIG_OS_DARWIN) |
| 2030 | | paths.resize(0); |
| 2031 | | uint32_t img_count = _dyld_image_count(); |
| 2032 | | for (uint32_t i = 0; i != img_count; i += 1) { |
| 2033 | | const char *name = _dyld_get_image_name(i); |
| 2034 | | paths.append(buf_create_from_str(name)); |
| 2035 | | } |
| 2036 | | return ErrorNone; |
| 2037 | | #elif defined(ZIG_OS_WINDOWS) |
| 2038 | | // zig is built statically on windows, so we can return an empty list |
| 2039 | | paths.resize(0); |
| 2040 | | return ErrorNone; |
| 2041 | | #else |
| 2042 | | #error unimplemented |
| 2043 | | #endif |
| 2044 | | } |
| 2045 | | |
| 2046 | | Error os_file_open_rw(Buf *full_path, OsFile *out_file, OsFileAttr *attr, bool need_write, uint32_t mode) { |
| 2047 | | #if defined(ZIG_OS_WINDOWS) |
| 2048 | | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); |
| 2049 | | HANDLE result = CreateFileW(&path_space.data.items[0], |
| 2050 | | need_write ? (GENERIC_READ|GENERIC_WRITE) : GENERIC_READ, |
| 2051 | | need_write ? 0 : FILE_SHARE_READ, |
| 2052 | | nullptr, |
| 2053 | | need_write ? OPEN_ALWAYS : OPEN_EXISTING, |
| 2054 | | FILE_ATTRIBUTE_NORMAL, nullptr); |
| 2055 | | |
| 2056 | | if (result == INVALID_HANDLE_VALUE) { |
| 2057 | | DWORD err = GetLastError(); |
| 2058 | | switch (err) { |
| 2059 | | case ERROR_SHARING_VIOLATION: |
| 2060 | | return ErrorSharingViolation; |
| 2061 | | case ERROR_ALREADY_EXISTS: |
| 2062 | | return ErrorPathAlreadyExists; |
| 2063 | | case ERROR_FILE_EXISTS: |
| 2064 | | return ErrorPathAlreadyExists; |
| 2065 | | case ERROR_FILE_NOT_FOUND: |
| 2066 | | return ErrorFileNotFound; |
| 2067 | | case ERROR_PATH_NOT_FOUND: |
| 2068 | | return ErrorFileNotFound; |
| 2069 | | case ERROR_ACCESS_DENIED: |
| 2070 | | return ErrorAccess; |
| 2071 | | case ERROR_PIPE_BUSY: |
| 2072 | | return ErrorPipeBusy; |
| 2073 | | default: |
| 2074 | | return ErrorUnexpected; |
| 2075 | | } |
| 2076 | | } |
| 2077 | | *out_file = result; |
| 2078 | | |
| 2079 | | if (attr != nullptr) { |
| 2080 | | BY_HANDLE_FILE_INFORMATION file_info; |
| 2081 | | if (!GetFileInformationByHandle(result, &file_info)) { |
| 2082 | | CloseHandle(result); |
| 2083 | | return ErrorUnexpected; |
| 2084 | | } |
| 2085 | | windows_filetime_to_os_timestamp(&file_info.ftLastWriteTime, &attr->mtime); |
| 2086 | | attr->inode = (((uint64_t)file_info.nFileIndexHigh) << 32) | file_info.nFileIndexLow; |
| 2087 | | attr->mode = 0; |
| 2088 | | attr->size = (((uint64_t)file_info.nFileSizeHigh) << 32) | file_info.nFileSizeLow; |
| 2089 | | } |
| 2090 | | |
| 2091 | | return ErrorNone; |
| 2092 | | #else |
| 2093 | | for (;;) { |
| 2094 | | int fd = open(buf_ptr(full_path), |
| 2095 | | need_write ? (O_RDWR|O_CLOEXEC|O_CREAT) : (O_RDONLY|O_CLOEXEC), mode); |
| 2096 | | if (fd == -1) { |
| 2097 | | switch (errno) { |
| 2098 | | case EINTR: |
| 2099 | | continue; |
| 2100 | | case EINVAL: |
| 2101 | | zig_unreachable(); |
| 2102 | | case EFAULT: |
| 2103 | | zig_unreachable(); |
| 2104 | | case EACCES: |
| 2105 | | case EPERM: |
| 2106 | | return ErrorAccess; |
| 2107 | | case EISDIR: |
| 2108 | | return ErrorIsDir; |
| 2109 | | case ENOENT: |
| 2110 | | return ErrorFileNotFound; |
| 2111 | | default: |
| 2112 | | return ErrorFileSystem; |
| 2113 | | } |
| 2114 | | } |
| 2115 | | struct stat statbuf; |
| 2116 | | if (fstat(fd, &statbuf) == -1) { |
| 2117 | | close(fd); |
| 2118 | | return ErrorFileSystem; |
| 2119 | | } |
| 2120 | | if (S_ISDIR(statbuf.st_mode)) { |
| 2121 | | close(fd); |
| 2122 | | return ErrorIsDir; |
| 2123 | | } |
| 2124 | | *out_file = fd; |
| 2125 | | |
| 2126 | | if (attr != nullptr) { |
| 2127 | | attr->inode = statbuf.st_ino; |
| 2128 | | #if defined(ZIG_OS_DARWIN) |
| 2129 | | attr->mtime.sec = statbuf.st_mtimespec.tv_sec; |
| 2130 | | attr->mtime.nsec = statbuf.st_mtimespec.tv_nsec; |
| 2131 | | #else |
| 2132 | | attr->mtime.sec = statbuf.st_mtim.tv_sec; |
| 2133 | | attr->mtime.nsec = statbuf.st_mtim.tv_nsec; |
| 2134 | | #endif |
| 2135 | | attr->mode = statbuf.st_mode; |
| 2136 | | attr->size = statbuf.st_size; |
| 2137 | | } |
| 2138 | | return ErrorNone; |
| 2139 | | } |
| 2140 | | #endif |
| 2141 | | } |
| 2142 | | |
| 2143 | | Error os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr) { |
| 2144 | | return os_file_open_rw(full_path, out_file, attr, false, 0); |
| 2145 | | } |
| 2146 | | |
| 2147 | | Error os_file_open_w(Buf *full_path, OsFile *out_file, OsFileAttr *attr, uint32_t mode) { |
| 2148 | | return os_file_open_rw(full_path, out_file, attr, true, mode); |
| 2149 | | } |
| 2150 | | |
| 2151 | | Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) { |
| 2152 | | #if defined(ZIG_OS_WINDOWS) |
| 2153 | | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); |
| 2154 | | for (;;) { |
| 2155 | | HANDLE result = CreateFileW(&path_space.data.items[0], GENERIC_READ | GENERIC_WRITE, |
| 2156 | | 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); |
| 2157 | | |
| 2158 | | if (result == INVALID_HANDLE_VALUE) { |
| 2159 | | DWORD err = GetLastError(); |
| 2160 | | switch (err) { |
| 2161 | | case ERROR_SHARING_VIOLATION: |
| 2162 | | // TODO wait for the lock instead of sleeping |
| 2163 | | Sleep(10); |
| 2164 | | continue; |
| 2165 | | case ERROR_ALREADY_EXISTS: |
| 2166 | | return ErrorPathAlreadyExists; |
| 2167 | | case ERROR_FILE_EXISTS: |
| 2168 | | return ErrorPathAlreadyExists; |
| 2169 | | case ERROR_FILE_NOT_FOUND: |
| 2170 | | return ErrorFileNotFound; |
| 2171 | | case ERROR_PATH_NOT_FOUND: |
| 2172 | | return ErrorFileNotFound; |
| 2173 | | case ERROR_ACCESS_DENIED: |
| 2174 | | return ErrorAccess; |
| 2175 | | case ERROR_PIPE_BUSY: |
| 2176 | | return ErrorPipeBusy; |
| 2177 | | default: |
| 2178 | | return ErrorUnexpected; |
| 2179 | | } |
| 2180 | | } |
| 2181 | | *out_file = result; |
| 2182 | | return ErrorNone; |
| 2183 | | } |
| 2184 | | #else |
| 2185 | | int fd; |
| 2186 | | for (;;) { |
| 2187 | | fd = open(buf_ptr(full_path), O_RDWR|O_CLOEXEC|O_CREAT, 0666); |
| 2188 | | if (fd == -1) { |
| 2189 | | switch (errno) { |
| 2190 | | case EINTR: |
| 2191 | | continue; |
| 2192 | | case EINVAL: |
| 2193 | | zig_unreachable(); |
| 2194 | | case EFAULT: |
| 2195 | | zig_unreachable(); |
| 2196 | | case EACCES: |
| 2197 | | case EPERM: |
| 2198 | | return ErrorAccess; |
| 2199 | | case EISDIR: |
| 2200 | | return ErrorIsDir; |
| 2201 | | case ENOENT: |
| 2202 | | return ErrorFileNotFound; |
| 2203 | | case ENOTDIR: |
| 2204 | | return ErrorNotDir; |
| 2205 | | default: |
| 2206 | | return ErrorFileSystem; |
| 2207 | | } |
| 2208 | | } |
| 2209 | | break; |
| 2210 | | } |
| 2211 | | for (;;) { |
| 2212 | | struct flock lock; |
| 2213 | | lock.l_type = F_WRLCK; |
| 2214 | | lock.l_whence = SEEK_SET; |
| 2215 | | lock.l_start = 0; |
| 2216 | | lock.l_len = 0; |
| 2217 | | if (fcntl(fd, F_SETLKW, &lock) == -1) { |
| 2218 | | switch (errno) { |
| 2219 | | case EINTR: |
| 2220 | | continue; |
| 2221 | | case EBADF: |
| 2222 | | zig_unreachable(); |
| 2223 | | case EFAULT: |
| 2224 | | zig_unreachable(); |
| 2225 | | case EINVAL: |
| 2226 | | zig_unreachable(); |
| 2227 | | default: |
| 2228 | | close(fd); |
| 2229 | | return ErrorFileSystem; |
| 2230 | | } |
| 2231 | | } |
| 2232 | | break; |
| 2233 | | } |
| 2234 | | *out_file = fd; |
| 2235 | | return ErrorNone; |
| 2236 | | #endif |
| 2237 | | } |
| 2238 | | |
| 2239 | | Error os_file_read(OsFile file, void *ptr, size_t *len) { |
| 2240 | | #if defined(ZIG_OS_WINDOWS) |
| 2241 | | DWORD amt_read; |
| 2242 | | if (ReadFile(file, ptr, *len, &amt_read, nullptr) == 0) |
| 2243 | | return ErrorUnexpected; |
| 2244 | | *len = amt_read; |
| 2245 | | return ErrorNone; |
| 2246 | | #else |
| 2247 | | for (;;) { |
| 2248 | | ssize_t rc = read(file, ptr, *len); |
| 2249 | | if (rc == -1) { |
| 2250 | | switch (errno) { |
| 2251 | | case EINTR: |
| 2252 | | continue; |
| 2253 | | case EBADF: |
| 2254 | | zig_unreachable(); |
| 2255 | | case EFAULT: |
| 2256 | | zig_unreachable(); |
| 2257 | | case EISDIR: |
| 2258 | | return ErrorIsDir; |
| 2259 | | default: |
| 2260 | | return ErrorFileSystem; |
| 2261 | | } |
| 2262 | | } |
| 2263 | | *len = rc; |
| 2264 | | return ErrorNone; |
| 2265 | | } |
| 2266 | | #endif |
| 2267 | | } |
| 2268 | | |
| 2269 | | Error os_file_read_all(OsFile file, Buf *contents) { |
| 2270 | | Error err; |
| 2271 | | size_t index = 0; |
| 2272 | | for (;;) { |
| 2273 | | size_t amt = buf_len(contents) - index; |
| 2274 | | |
| 2275 | | if (amt < 4096) { |
| 2276 | | buf_resize(contents, buf_len(contents) + (4096 - amt)); |
| 2277 | | amt = buf_len(contents) - index; |
| 2278 | | } |
| 2279 | | |
| 2280 | | if ((err = os_file_read(file, buf_ptr(contents) + index, &amt))) |
| 2281 | | return err; |
| 2282 | | |
| 2283 | | if (amt == 0) { |
| 2284 | | buf_resize(contents, index); |
| 2285 | | return ErrorNone; |
| 2286 | | } |
| 2287 | | |
| 2288 | | index += amt; |
| 2289 | | } |
| 2290 | | } |
| 2291 | | |
| 2292 | | Error os_file_overwrite(OsFile file, Buf *contents) { |
| 2293 | | #if defined(ZIG_OS_WINDOWS) |
| 2294 | | if (SetFilePointer(file, 0, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) |
| 2295 | | return ErrorFileSystem; |
| 2296 | | if (!SetEndOfFile(file)) |
| 2297 | | return ErrorFileSystem; |
| 2298 | | DWORD bytes_written; |
| 2299 | | if (!WriteFile(file, buf_ptr(contents), buf_len(contents), &bytes_written, nullptr)) |
| 2300 | | return ErrorFileSystem; |
| 2301 | | return ErrorNone; |
| 2302 | | #else |
| 2303 | | if (lseek(file, 0, SEEK_SET) == -1) |
| 2304 | | return ErrorUnexpectedSeekFailure; |
| 2305 | | if (ftruncate(file, 0) == -1) |
| 2306 | | return ErrorUnexpectedFileTruncationFailure; |
| 2307 | | for (;;) { |
| 2308 | | if (write(file, buf_ptr(contents), buf_len(contents)) == -1) { |
| 2309 | | switch (errno) { |
| 2310 | | case EINTR: |
| 2311 | | continue; |
| 2312 | | case EINVAL: |
| 2313 | | zig_unreachable(); |
| 2314 | | case EBADF: |
| 2315 | | zig_unreachable(); |
| 2316 | | case EFAULT: |
| 2317 | | zig_unreachable(); |
| 2318 | | case EDQUOT: |
| 2319 | | return ErrorDiskQuota; |
| 2320 | | case ENOSPC: |
| 2321 | | return ErrorDiskSpace; |
| 2322 | | case EFBIG: |
| 2323 | | return ErrorFileTooBig; |
| 2324 | | case EIO: |
| 2325 | | return ErrorFileSystem; |
| 2326 | | case EPERM: |
| 2327 | | return ErrorAccess; |
| 2328 | | default: |
| 2329 | | return ErrorUnexpectedWriteFailure; |
| 2330 | | } |
| 2331 | | } |
| 2332 | | return ErrorNone; |
| 2333 | | } |
| 2334 | | #endif |
| 2335 | | } |
| 2336 | | |
| 2337 | | void os_file_close(OsFile *file) { |
| 2338 | | #if defined(ZIG_OS_WINDOWS) |
| 2339 | | CloseHandle(*file); |
| 2340 | | *file = NULL; |
| 2341 | | #else |
| 2342 | | close(*file); |
| 2343 | | *file = -1; |
| 2344 | | #endif |
| 2345 | | } |