1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2009 Michihiro NAKAJIMA 5 * All rights reserved. 6 */ 7 8 #if defined(_WIN32) && !defined(__CYGWIN__) 9 10 #include "bsdtar_platform.h" 11 #include <ctype.h> 12 #include <errno.h> 13 #include <fcntl.h> 14 #include <io.h> 15 #include <stddef.h> 16 #ifdef HAVE_SYS_UTIME_H 17 #include <sys/utime.h> 18 #endif 19 #include <sys/stat.h> 20 #include <process.h> 21 #include <stdlib.h> 22 #include <wchar.h> 23 #include <windows.h> 24 #include <sddl.h> 25 26 #include "bsdtar.h" 27 #include "err.h" 28 29 /* This may actually not be needed anymore. 30 * TODO: Review the error handling for chdir() failures and 31 * simply dump this if it's not really needed. */ 32 static void __tar_dosmaperr(unsigned long); 33 34 /* 35 * Prepend "\\?\" to the path name and convert it to unicode to permit 36 * an extended-length path for a maximum total path length of 32767 37 * characters. 38 * see also http://msdn.microsoft.com/en-us/library/aa365247.aspx 39 */ 40 static wchar_t * 41 permissive_name(const char *name) 42 { 43 wchar_t *wn, *wnp; 44 wchar_t *ws, *wsp; 45 DWORD l, len, slen, alloclen; 46 int unc; 47 48 len = (DWORD)strlen(name); 49 wn = malloc((len + 1) * sizeof(wchar_t)); 50 if (wn == NULL) 51 return (NULL); 52 l = MultiByteToWideChar(CP_ACP, 0, name, len, wn, len); 53 if (l == 0) { 54 free(wn); 55 return (NULL); 56 } 57 wn[l] = L'\0'; 58 59 /* Get a full path names */ 60 l = GetFullPathNameW(wn, 0, NULL, NULL); 61 if (l == 0) { 62 free(wn); 63 return (NULL); 64 } 65 wnp = malloc(l * sizeof(wchar_t)); 66 if (wnp == NULL) { 67 free(wn); 68 return (NULL); 69 } 70 len = GetFullPathNameW(wn, l, wnp, NULL); 71 free(wn); 72 wn = wnp; 73 74 if (wnp[0] == L'\\' && wnp[1] == L'\\' && 75 wnp[2] == L'?' && wnp[3] == L'\\') 76 /* We have already permissive names. */ 77 return (wn); 78 79 if (wnp[0] == L'\\' && wnp[1] == L'\\' && 80 wnp[2] == L'.' && wnp[3] == L'\\') { 81 /* Device names */ 82 if (((wnp[4] >= L'a' && wnp[4] <= L'z') || 83 (wnp[4] >= L'A' && wnp[4] <= L'Z')) && 84 wnp[5] == L':' && wnp[6] == L'\\') 85 wnp[2] = L'?';/* Not device names. */ 86 return (wn); 87 } 88 89 unc = 0; 90 if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') { 91 wchar_t *p = &wnp[2]; 92 93 /* Skip server-name letters. */ 94 while (*p != L'\\' && *p != L'\0') 95 ++p; 96 if (*p == L'\\') { 97 wchar_t *rp = ++p; 98 /* Skip share-name letters. */ 99 while (*p != L'\\' && *p != L'\0') 100 ++p; 101 if (*p == L'\\' && p != rp) { 102 /* Now, match patterns such as 103 * "\\server-name\share-name\" */ 104 wnp += 2; 105 len -= 2; 106 unc = 1; 107 } 108 } 109 } 110 111 alloclen = slen = 4 + (unc * 4) + len + 1; 112 ws = wsp = malloc(slen * sizeof(wchar_t)); 113 if (ws == NULL) { 114 free(wn); 115 return (NULL); 116 } 117 /* prepend "\\?\" */ 118 wcsncpy(wsp, L"\\\\?\\", 4); 119 wsp += 4; 120 slen -= 4; 121 if (unc) { 122 /* append "UNC\" ---> "\\?\UNC\" */ 123 wcsncpy(wsp, L"UNC\\", 4); 124 wsp += 4; 125 slen -= 4; 126 } 127 wcsncpy(wsp, wnp, slen); 128 free(wn); 129 ws[alloclen - 1] = L'\0'; 130 return (ws); 131 } 132 133 int 134 __tar_chdir(const char *path) 135 { 136 wchar_t *ws; 137 int r; 138 139 r = SetCurrentDirectoryA(path); 140 if (r == 0) { 141 if (GetLastError() != ERROR_FILE_NOT_FOUND) { 142 __tar_dosmaperr(GetLastError()); 143 return (-1); 144 } 145 } else 146 return (0); 147 ws = permissive_name(path); 148 if (ws == NULL) { 149 errno = EINVAL; 150 return (-1); 151 } 152 r = SetCurrentDirectoryW(ws); 153 free(ws); 154 if (r == 0) { 155 __tar_dosmaperr(GetLastError()); 156 return (-1); 157 } 158 return (0); 159 } 160 161 /* 162 * The following function was modified from PostgreSQL sources and is 163 * subject to the copyright below. 164 */ 165 /*------------------------------------------------------------------------- 166 * 167 * win32error.c 168 * Map win32 error codes to errno values 169 * 170 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group 171 * 172 * IDENTIFICATION 173 * $PostgreSQL: pgsql/src/port/win32error.c,v 1.4 2008/01/01 19:46:00 momjian Exp $ 174 * 175 *------------------------------------------------------------------------- 176 */ 177 /* 178 PostgreSQL Database Management System 179 (formerly known as Postgres, then as Postgres95) 180 181 Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group 182 183 Portions Copyright (c) 1994, The Regents of the University of California 184 185 Permission to use, copy, modify, and distribute this software and its 186 documentation for any purpose, without fee, and without a written agreement 187 is hereby granted, provided that the above copyright notice and this 188 paragraph and the following two paragraphs appear in all copies. 189 190 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR 191 DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING 192 LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS 193 DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE 194 POSSIBILITY OF SUCH DAMAGE. 195 196 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 197 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 198 AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 199 ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO 200 PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 201 */ 202 203 static const struct { 204 DWORD winerr; 205 int doserr; 206 } doserrors[] = 207 { 208 { ERROR_INVALID_FUNCTION, EINVAL }, 209 { ERROR_FILE_NOT_FOUND, ENOENT }, 210 { ERROR_PATH_NOT_FOUND, ENOENT }, 211 { ERROR_TOO_MANY_OPEN_FILES, EMFILE }, 212 { ERROR_ACCESS_DENIED, EACCES }, 213 { ERROR_INVALID_HANDLE, EBADF }, 214 { ERROR_ARENA_TRASHED, ENOMEM }, 215 { ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, 216 { ERROR_INVALID_BLOCK, ENOMEM }, 217 { ERROR_BAD_ENVIRONMENT, E2BIG }, 218 { ERROR_BAD_FORMAT, ENOEXEC }, 219 { ERROR_INVALID_ACCESS, EINVAL }, 220 { ERROR_INVALID_DATA, EINVAL }, 221 { ERROR_INVALID_DRIVE, ENOENT }, 222 { ERROR_CURRENT_DIRECTORY, EACCES }, 223 { ERROR_NOT_SAME_DEVICE, EXDEV }, 224 { ERROR_NO_MORE_FILES, ENOENT }, 225 { ERROR_LOCK_VIOLATION, EACCES }, 226 { ERROR_SHARING_VIOLATION, EACCES }, 227 { ERROR_BAD_NETPATH, ENOENT }, 228 { ERROR_NETWORK_ACCESS_DENIED, EACCES }, 229 { ERROR_BAD_NET_NAME, ENOENT }, 230 { ERROR_FILE_EXISTS, EEXIST }, 231 { ERROR_CANNOT_MAKE, EACCES }, 232 { ERROR_FAIL_I24, EACCES }, 233 { ERROR_INVALID_PARAMETER, EINVAL }, 234 { ERROR_NO_PROC_SLOTS, EAGAIN }, 235 { ERROR_DRIVE_LOCKED, EACCES }, 236 { ERROR_BROKEN_PIPE, EPIPE }, 237 { ERROR_DISK_FULL, ENOSPC }, 238 { ERROR_INVALID_TARGET_HANDLE, EBADF }, 239 { ERROR_INVALID_HANDLE, EINVAL }, 240 { ERROR_WAIT_NO_CHILDREN, ECHILD }, 241 { ERROR_CHILD_NOT_COMPLETE, ECHILD }, 242 { ERROR_DIRECT_ACCESS_HANDLE, EBADF }, 243 { ERROR_NEGATIVE_SEEK, EINVAL }, 244 { ERROR_SEEK_ON_DEVICE, EACCES }, 245 { ERROR_DIR_NOT_EMPTY, ENOTEMPTY }, 246 { ERROR_NOT_LOCKED, EACCES }, 247 { ERROR_BAD_PATHNAME, ENOENT }, 248 { ERROR_MAX_THRDS_REACHED, EAGAIN }, 249 { ERROR_LOCK_FAILED, EACCES }, 250 { ERROR_ALREADY_EXISTS, EEXIST }, 251 { ERROR_FILENAME_EXCED_RANGE, ENOENT }, 252 { ERROR_NESTING_NOT_ALLOWED, EAGAIN }, 253 { ERROR_NOT_ENOUGH_QUOTA, ENOMEM } 254 }; 255 256 static void 257 __tar_dosmaperr(unsigned long e) 258 { 259 int i; 260 261 if (e == 0) { 262 errno = 0; 263 return; 264 } 265 266 for (i = 0; i < (int)sizeof(doserrors); i++) { 267 if (doserrors[i].winerr == e) { 268 errno = doserrors[i].doserr; 269 return; 270 } 271 } 272 273 /* fprintf(stderr, "unrecognized win32 error code: %lu", e); */ 274 errno = EINVAL; 275 return; 276 } 277 278 #endif 279