| ... | ... | @@ -2906,275 +2906,6 @@ pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 { |
| 2906 | 2906 | return buffer[0..end_index]; |
| 2907 | 2907 | } |
| 2908 | 2908 | |
| 2909 | | pub const DeleteFileError = error{ |
| 2910 | | FileNotFound, |
| 2911 | | AccessDenied, |
| 2912 | | NameTooLong, |
| 2913 | | /// Also known as sharing violation. |
| 2914 | | FileBusy, |
| 2915 | | Unexpected, |
| 2916 | | NotDir, |
| 2917 | | IsDir, |
| 2918 | | DirNotEmpty, |
| 2919 | | NetworkNotFound, |
| 2920 | | }; |
| 2921 | | |
| 2922 | | pub const DeleteFileOptions = struct { |
| 2923 | | dir: ?HANDLE, |
| 2924 | | remove_dir: bool = false, |
| 2925 | | }; |
| 2926 | | |
| 2927 | | pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFileError!void { |
| 2928 | | const path_len_bytes = @as(u16, @intCast(sub_path_w.len * 2)); |
| 2929 | | var nt_name: UNICODE_STRING = .{ |
| 2930 | | .Length = path_len_bytes, |
| 2931 | | .MaximumLength = path_len_bytes, |
| 2932 | | // The Windows API makes this mutable, but it will not mutate here. |
| 2933 | | .Buffer = @constCast(sub_path_w.ptr), |
| 2934 | | }; |
| 2935 | | |
| 2936 | | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { |
| 2937 | | // Windows does not recognize this, but it does work with empty string. |
| 2938 | | nt_name.Length = 0; |
| 2939 | | } |
| 2940 | | if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { |
| 2941 | | // Can't remove the parent directory with an open handle. |
| 2942 | | return error.FileBusy; |
| 2943 | | } |
| 2944 | | |
| 2945 | | var io: IO_STATUS_BLOCK = undefined; |
| 2946 | | var tmp_handle: HANDLE = undefined; |
| 2947 | | var rc = ntdll.NtCreateFile( |
| 2948 | | &tmp_handle, |
| 2949 | | .{ .STANDARD = .{ |
| 2950 | | .RIGHTS = .{ .DELETE = true }, |
| 2951 | | .SYNCHRONIZE = true, |
| 2952 | | } }, |
| 2953 | | &.{ |
| 2954 | | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| 2955 | | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else options.dir, |
| 2956 | | .Attributes = .{}, |
| 2957 | | .ObjectName = &nt_name, |
| 2958 | | .SecurityDescriptor = null, |
| 2959 | | .SecurityQualityOfService = null, |
| 2960 | | }, |
| 2961 | | &io, |
| 2962 | | null, |
| 2963 | | .{}, |
| 2964 | | .VALID_FLAGS, |
| 2965 | | .OPEN, |
| 2966 | | .{ |
| 2967 | | .DIRECTORY_FILE = options.remove_dir, |
| 2968 | | .NON_DIRECTORY_FILE = !options.remove_dir, |
| 2969 | | .OPEN_REPARSE_POINT = true, // would we ever want to delete the target instead? |
| 2970 | | }, |
| 2971 | | null, |
| 2972 | | 0, |
| 2973 | | ); |
| 2974 | | switch (rc) { |
| 2975 | | .SUCCESS => {}, |
| 2976 | | .OBJECT_NAME_INVALID => unreachable, |
| 2977 | | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, |
| 2978 | | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, |
| 2979 | | .BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found |
| 2980 | | .BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't |
| 2981 | | .INVALID_PARAMETER => unreachable, |
| 2982 | | .FILE_IS_A_DIRECTORY => return error.IsDir, |
| 2983 | | .NOT_A_DIRECTORY => return error.NotDir, |
| 2984 | | .SHARING_VIOLATION => return error.FileBusy, |
| 2985 | | .ACCESS_DENIED => return error.AccessDenied, |
| 2986 | | .DELETE_PENDING => return, |
| 2987 | | else => return unexpectedStatus(rc), |
| 2988 | | } |
| 2989 | | defer CloseHandle(tmp_handle); |
| 2990 | | |
| 2991 | | // FileDispositionInformationEx has varying levels of support: |
| 2992 | | // - FILE_DISPOSITION_INFORMATION_EX requires >= win10_rs1 |
| 2993 | | // (INVALID_INFO_CLASS is returned if not supported) |
| 2994 | | // - Requires the NTFS filesystem |
| 2995 | | // (on filesystems like FAT32, INVALID_PARAMETER is returned) |
| 2996 | | // - FILE_DISPOSITION_POSIX_SEMANTICS requires >= win10_rs1 |
| 2997 | | // - FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5 |
| 2998 | | // (NOT_SUPPORTED is returned if a flag is unsupported) |
| 2999 | | // |
| 3000 | | // The strategy here is just to try using FileDispositionInformationEx and fall back to |
| 3001 | | // FileDispositionInformation if the return value lets us know that some aspect of it is not supported. |
| 3002 | | const need_fallback = need_fallback: { |
| 3003 | | // Deletion with posix semantics if the filesystem supports it. |
| 3004 | | var info: FILE.DISPOSITION.INFORMATION.EX = .{ .Flags = .{ |
| 3005 | | .DELETE = true, |
| 3006 | | .POSIX_SEMANTICS = true, |
| 3007 | | .IGNORE_READONLY_ATTRIBUTE = true, |
| 3008 | | } }; |
| 3009 | | rc = ntdll.NtSetInformationFile( |
| 3010 | | tmp_handle, |
| 3011 | | &io, |
| 3012 | | &info, |
| 3013 | | @sizeOf(FILE.DISPOSITION.INFORMATION.EX), |
| 3014 | | .DispositionEx, |
| 3015 | | ); |
| 3016 | | switch (rc) { |
| 3017 | | .SUCCESS => return, |
| 3018 | | // The filesystem does not support FileDispositionInformationEx |
| 3019 | | .INVALID_PARAMETER, |
| 3020 | | // The operating system does not support FileDispositionInformationEx |
| 3021 | | .INVALID_INFO_CLASS, |
| 3022 | | // The operating system does not support one of the flags |
| 3023 | | .NOT_SUPPORTED, |
| 3024 | | => break :need_fallback true, |
| 3025 | | // For all other statuses, fall down to the switch below to handle them. |
| 3026 | | else => break :need_fallback false, |
| 3027 | | } |
| 3028 | | }; |
| 3029 | | |
| 3030 | | if (need_fallback) { |
| 3031 | | // Deletion with file pending semantics, which requires waiting or moving |
| 3032 | | // files to get them removed (from here). |
| 3033 | | var file_dispo: FILE.DISPOSITION.INFORMATION = .{ |
| 3034 | | .DeleteFile = TRUE, |
| 3035 | | }; |
| 3036 | | rc = ntdll.NtSetInformationFile( |
| 3037 | | tmp_handle, |
| 3038 | | &io, |
| 3039 | | &file_dispo, |
| 3040 | | @sizeOf(FILE.DISPOSITION.INFORMATION), |
| 3041 | | .Disposition, |
| 3042 | | ); |
| 3043 | | } |
| 3044 | | switch (rc) { |
| 3045 | | .SUCCESS => {}, |
| 3046 | | .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty, |
| 3047 | | .INVALID_PARAMETER => unreachable, |
| 3048 | | .CANNOT_DELETE => return error.AccessDenied, |
| 3049 | | .MEDIA_WRITE_PROTECTED => return error.AccessDenied, |
| 3050 | | .ACCESS_DENIED => return error.AccessDenied, |
| 3051 | | else => return unexpectedStatus(rc), |
| 3052 | | } |
| 3053 | | } |
| 3054 | | |
| 3055 | | pub const RenameError = error{ |
| 3056 | | IsDir, |
| 3057 | | NotDir, |
| 3058 | | FileNotFound, |
| 3059 | | NoDevice, |
| 3060 | | AccessDenied, |
| 3061 | | PipeBusy, |
| 3062 | | PathAlreadyExists, |
| 3063 | | Unexpected, |
| 3064 | | NameTooLong, |
| 3065 | | NetworkNotFound, |
| 3066 | | AntivirusInterference, |
| 3067 | | BadPathName, |
| 3068 | | CrossDevice, |
| 3069 | | } || UnexpectedError; |
| 3070 | | |
| 3071 | | pub fn RenameFile( |
| 3072 | | /// May only be `null` if `old_path_w` is a fully-qualified absolute path. |
| 3073 | | old_dir_fd: ?HANDLE, |
| 3074 | | old_path_w: []const u16, |
| 3075 | | /// May only be `null` if `new_path_w` is a fully-qualified absolute path, |
| 3076 | | /// or if the file is not being moved to a different directory. |
| 3077 | | new_dir_fd: ?HANDLE, |
| 3078 | | new_path_w: []const u16, |
| 3079 | | replace_if_exists: bool, |
| 3080 | | ) RenameError!void { |
| 3081 | | const src_fd = OpenFile(old_path_w, .{ |
| 3082 | | .dir = old_dir_fd, |
| 3083 | | .access_mask = .{ |
| 3084 | | .STANDARD = .{ |
| 3085 | | .RIGHTS = .{ .DELETE = true }, |
| 3086 | | .SYNCHRONIZE = true, |
| 3087 | | }, |
| 3088 | | .GENERIC = .{ .WRITE = true }, |
| 3089 | | }, |
| 3090 | | .creation = .OPEN, |
| 3091 | | .filter = .any, // This function is supposed to rename both files and directories. |
| 3092 | | .follow_symlinks = false, |
| 3093 | | }) catch |err| switch (err) { |
| 3094 | | error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`. |
| 3095 | | else => |e| return e, |
| 3096 | | }; |
| 3097 | | defer CloseHandle(src_fd); |
| 3098 | | |
| 3099 | | var rc: NTSTATUS = undefined; |
| 3100 | | // FileRenameInformationEx has varying levels of support: |
| 3101 | | // - FILE_RENAME_INFORMATION_EX requires >= win10_rs1 |
| 3102 | | // (INVALID_INFO_CLASS is returned if not supported) |
| 3103 | | // - Requires the NTFS filesystem |
| 3104 | | // (on filesystems like FAT32, INVALID_PARAMETER is returned) |
| 3105 | | // - FILE_RENAME_POSIX_SEMANTICS requires >= win10_rs1 |
| 3106 | | // - FILE_RENAME_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5 |
| 3107 | | // (NOT_SUPPORTED is returned if a flag is unsupported) |
| 3108 | | // |
| 3109 | | // The strategy here is just to try using FileRenameInformationEx and fall back to |
| 3110 | | // FileRenameInformation if the return value lets us know that some aspect of it is not supported. |
| 3111 | | const need_fallback = need_fallback: { |
| 3112 | | var rename_info: FILE.RENAME_INFORMATION = .init(.{ |
| 3113 | | .Flags = .{ |
| 3114 | | .REPLACE_IF_EXISTS = replace_if_exists, |
| 3115 | | .POSIX_SEMANTICS = true, |
| 3116 | | .IGNORE_READONLY_ATTRIBUTE = true, |
| 3117 | | }, |
| 3118 | | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir_fd, |
| 3119 | | .FileName = new_path_w, |
| 3120 | | }); |
| 3121 | | var io_status_block: IO_STATUS_BLOCK = undefined; |
| 3122 | | const rename_info_buf = rename_info.toBuffer(); |
| 3123 | | rc = ntdll.NtSetInformationFile( |
| 3124 | | src_fd, |
| 3125 | | &io_status_block, |
| 3126 | | rename_info_buf.ptr, |
| 3127 | | @intCast(rename_info_buf.len), // already checked for error.NameTooLong |
| 3128 | | .RenameEx, |
| 3129 | | ); |
| 3130 | | switch (rc) { |
| 3131 | | .SUCCESS => return, |
| 3132 | | // The filesystem does not support FileDispositionInformationEx |
| 3133 | | .INVALID_PARAMETER, |
| 3134 | | // The operating system does not support FileDispositionInformationEx |
| 3135 | | .INVALID_INFO_CLASS, |
| 3136 | | // The operating system does not support one of the flags |
| 3137 | | .NOT_SUPPORTED, |
| 3138 | | => break :need_fallback true, |
| 3139 | | // For all other statuses, fall down to the switch below to handle them. |
| 3140 | | else => break :need_fallback false, |
| 3141 | | } |
| 3142 | | }; |
| 3143 | | |
| 3144 | | if (need_fallback) { |
| 3145 | | var rename_info: FILE.RENAME_INFORMATION = .init(.{ |
| 3146 | | .Flags = .{ .REPLACE_IF_EXISTS = replace_if_exists }, |
| 3147 | | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir_fd, |
| 3148 | | .FileName = new_path_w, |
| 3149 | | }); |
| 3150 | | var io_status_block: IO_STATUS_BLOCK = undefined; |
| 3151 | | const rename_info_buf = rename_info.toBuffer(); |
| 3152 | | rc = ntdll.NtSetInformationFile( |
| 3153 | | src_fd, |
| 3154 | | &io_status_block, |
| 3155 | | rename_info_buf.ptr, |
| 3156 | | @intCast(rename_info_buf.len), // already checked for error.NameTooLong |
| 3157 | | .Rename, |
| 3158 | | ); |
| 3159 | | } |
| 3160 | | |
| 3161 | | switch (rc) { |
| 3162 | | .SUCCESS => {}, |
| 3163 | | .INVALID_HANDLE => unreachable, |
| 3164 | | .INVALID_PARAMETER => unreachable, |
| 3165 | | .OBJECT_PATH_SYNTAX_BAD => unreachable, |
| 3166 | | .ACCESS_DENIED => return error.AccessDenied, |
| 3167 | | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, |
| 3168 | | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, |
| 3169 | | .NOT_SAME_DEVICE => return error.CrossDevice, |
| 3170 | | .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, |
| 3171 | | .DIRECTORY_NOT_EMPTY => return error.PathAlreadyExists, |
| 3172 | | .FILE_IS_A_DIRECTORY => return error.IsDir, |
| 3173 | | .NOT_A_DIRECTORY => return error.NotDir, |
| 3174 | | else => return unexpectedStatus(rc), |
| 3175 | | } |
| 3176 | | } |
| 3177 | | |
| 3178 | 2909 | pub const GetStdHandleError = error{ |
| 3179 | 2910 | NoStandardHandleAttached, |
| 3180 | 2911 | Unexpected, |
| ... | ... | @@ -3508,18 +3239,6 @@ test GetFinalPathNameByHandle { |
| 3508 | 3239 | _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0..required_len_in_u16]); |
| 3509 | 3240 | } |
| 3510 | 3241 | |
| 3511 | | pub const GetFileSizeError = error{Unexpected}; |
| 3512 | | |
| 3513 | | pub fn GetFileSizeEx(hFile: HANDLE) GetFileSizeError!u64 { |
| 3514 | | var file_size: LARGE_INTEGER = undefined; |
| 3515 | | if (kernel32.GetFileSizeEx(hFile, &file_size) == 0) { |
| 3516 | | switch (GetLastError()) { |
| 3517 | | else => |err| return unexpectedError(err), |
| 3518 | | } |
| 3519 | | } |
| 3520 | | return @as(u64, @bitCast(file_size)); |
| 3521 | | } |
| 3522 | | |
| 3523 | 3242 | pub fn getpeername(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.socklen_t) i32 { |
| 3524 | 3243 | return ws2_32.getpeername(s, name, @as(*i32, @ptrCast(namelen))); |
| 3525 | 3244 | } |
| ... | ... | @@ -3714,69 +3433,6 @@ pub const CreateProcessFlags = packed struct(u32) { |
| 3714 | 3433 | create_ignore_system_default: bool = false, |
| 3715 | 3434 | }; |
| 3716 | 3435 | |
| 3717 | | pub fn CreateProcessW( |
| 3718 | | lpApplicationName: ?LPCWSTR, |
| 3719 | | lpCommandLine: ?LPWSTR, |
| 3720 | | lpProcessAttributes: ?*SECURITY_ATTRIBUTES, |
| 3721 | | lpThreadAttributes: ?*SECURITY_ATTRIBUTES, |
| 3722 | | bInheritHandles: BOOL, |
| 3723 | | dwCreationFlags: CreateProcessFlags, |
| 3724 | | lpEnvironment: ?[*:0]u16, |
| 3725 | | lpCurrentDirectory: ?LPCWSTR, |
| 3726 | | lpStartupInfo: *STARTUPINFOW, |
| 3727 | | lpProcessInformation: *PROCESS_INFORMATION, |
| 3728 | | ) CreateProcessError!void { |
| 3729 | | if (kernel32.CreateProcessW( |
| 3730 | | lpApplicationName, |
| 3731 | | lpCommandLine, |
| 3732 | | lpProcessAttributes, |
| 3733 | | lpThreadAttributes, |
| 3734 | | bInheritHandles, |
| 3735 | | dwCreationFlags, |
| 3736 | | lpEnvironment, |
| 3737 | | lpCurrentDirectory, |
| 3738 | | lpStartupInfo, |
| 3739 | | lpProcessInformation, |
| 3740 | | ) == 0) { |
| 3741 | | switch (GetLastError()) { |
| 3742 | | .FILE_NOT_FOUND => return error.FileNotFound, |
| 3743 | | .PATH_NOT_FOUND => return error.FileNotFound, |
| 3744 | | .DIRECTORY => return error.FileNotFound, |
| 3745 | | .ACCESS_DENIED => return error.AccessDenied, |
| 3746 | | .INVALID_PARAMETER => unreachable, |
| 3747 | | .INVALID_NAME => return error.InvalidName, |
| 3748 | | .FILENAME_EXCED_RANGE => return error.NameTooLong, |
| 3749 | | .SHARING_VIOLATION => return error.FileBusy, |
| 3750 | | // These are all the system errors that are mapped to ENOEXEC by |
| 3751 | | // the undocumented _dosmaperr (old CRT) or __acrt_errno_map_os_error |
| 3752 | | // (newer CRT) functions. Their code can be found in crt/src/dosmap.c (old SDK) |
| 3753 | | // or urt/misc/errno.cpp (newer SDK) in the Windows SDK. |
| 3754 | | .BAD_FORMAT, |
| 3755 | | .INVALID_STARTING_CODESEG, // MIN_EXEC_ERROR in errno.cpp |
| 3756 | | .INVALID_STACKSEG, |
| 3757 | | .INVALID_MODULETYPE, |
| 3758 | | .INVALID_EXE_SIGNATURE, |
| 3759 | | .EXE_MARKED_INVALID, |
| 3760 | | .BAD_EXE_FORMAT, |
| 3761 | | .ITERATED_DATA_EXCEEDS_64k, |
| 3762 | | .INVALID_MINALLOCSIZE, |
| 3763 | | .DYNLINK_FROM_INVALID_RING, |
| 3764 | | .IOPL_NOT_ENABLED, |
| 3765 | | .INVALID_SEGDPL, |
| 3766 | | .AUTODATASEG_EXCEEDS_64k, |
| 3767 | | .RING2SEG_MUST_BE_MOVABLE, |
| 3768 | | .RELOC_CHAIN_XEEDS_SEGLIM, |
| 3769 | | .INFLOOP_IN_RELOC_CHAIN, // MAX_EXEC_ERROR in errno.cpp |
| 3770 | | // This one is not mapped to ENOEXEC but it is possible, for example |
| 3771 | | // when calling CreateProcessW on a plain text file with a .exe extension |
| 3772 | | .EXE_MACHINE_TYPE_MISMATCH, |
| 3773 | | => return error.InvalidExe, |
| 3774 | | .COMMITMENT_LIMIT => return error.SystemResources, |
| 3775 | | else => |err| return unexpectedError(err), |
| 3776 | | } |
| 3777 | | } |
| 3778 | | } |
| 3779 | | |
| 3780 | 3436 | pub const LoadLibraryError = error{ |
| 3781 | 3437 | FileNotFound, |
| 3782 | 3438 | Unexpected, |
| ... | ... | @@ -3843,10 +3499,6 @@ pub fn QueryPerformanceCounter() u64 { |
| 3843 | 3499 | return @as(u64, @bitCast(result)); |
| 3844 | 3500 | } |
| 3845 | 3501 | |
| 3846 | | pub fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*anyopaque, Context: ?*anyopaque) void { |
| 3847 | | assert(kernel32.InitOnceExecuteOnce(InitOnce, InitFn, Parameter, Context) != 0); |
| 3848 | | } |
| 3849 | | |
| 3850 | 3502 | /// This is a workaround for the C backend until zig has the ability to put |
| 3851 | 3503 | /// C code in inline assembly. |
| 3852 | 3504 | extern fn zig_thumb_windows_teb() callconv(.c) *anyopaque; |
| ... | ... | @@ -6072,24 +5724,6 @@ pub const PROCESS_MEMORY_COUNTERS_EX = extern struct { |
| 6072 | 5724 | PrivateUsage: SIZE_T, |
| 6073 | 5725 | }; |
| 6074 | 5726 | |
| 6075 | | pub const GetProcessMemoryInfoError = error{ |
| 6076 | | AccessDenied, |
| 6077 | | InvalidHandle, |
| 6078 | | Unexpected, |
| 6079 | | }; |
| 6080 | | |
| 6081 | | pub fn GetProcessMemoryInfo(hProcess: HANDLE) GetProcessMemoryInfoError!VM_COUNTERS { |
| 6082 | | var vmc: VM_COUNTERS = undefined; |
| 6083 | | const rc = ntdll.NtQueryInformationProcess(hProcess, .VmCounters, &vmc, @sizeOf(VM_COUNTERS), null); |
| 6084 | | switch (rc) { |
| 6085 | | .SUCCESS => return vmc, |
| 6086 | | .ACCESS_DENIED => return error.AccessDenied, |
| 6087 | | .INVALID_HANDLE => return error.InvalidHandle, |
| 6088 | | .INVALID_PARAMETER => unreachable, |
| 6089 | | else => return unexpectedStatus(rc), |
| 6090 | | } |
| 6091 | | } |
| 6092 | | |
| 6093 | 5727 | pub const PERFORMANCE_INFORMATION = extern struct { |
| 6094 | 5728 | cb: DWORD, |
| 6095 | 5729 | CommitTotal: SIZE_T, |
| ... | ... | @@ -6623,7 +6257,11 @@ pub fn WriteProcessMemory(handle: HANDLE, addr: ?LPVOID, buffer: []const u8) Wri |
| 6623 | 6257 | } |
| 6624 | 6258 | } |
| 6625 | 6259 | |
| 6626 | | pub const ProcessBaseAddressError = GetProcessMemoryInfoError || ReadMemoryError; |
| 6260 | pub const ProcessBaseAddressError = error{ |
| 6261 | AccessDenied, |
| 6262 | InvalidHandle, |
| 6263 | Unexpected, |
| 6264 | } || ReadMemoryError; |
| 6627 | 6265 | |
| 6628 | 6266 | /// Returns the base address of the process loaded into memory. |
| 6629 | 6267 | pub fn ProcessBaseAddress(handle: HANDLE) ProcessBaseAddressError!HMODULE { |