w95io.c (33019B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /* Windows 95 IO module 7 * 8 * Assumes synchronous I/O. 9 * 10 */ 11 12 #include "primpl.h" 13 #include <direct.h> 14 #include <mbstring.h> 15 #ifdef MOZ_UNICODE 16 # include <wchar.h> 17 #endif /* MOZ_UNICODE */ 18 19 struct _MDLock _pr_ioq_lock; 20 21 /* 22 * NSPR-to-NT access right mapping table for files. 23 */ 24 static DWORD fileAccessTable[] = {FILE_GENERIC_READ, FILE_GENERIC_WRITE, 25 FILE_GENERIC_EXECUTE}; 26 27 /* 28 * NSPR-to-NT access right mapping table for directories. 29 */ 30 static DWORD dirAccessTable[] = {FILE_GENERIC_READ, 31 FILE_GENERIC_WRITE | FILE_DELETE_CHILD, 32 FILE_GENERIC_EXECUTE}; 33 34 static PRBool IsPrevCharSlash(const char* str, const char* current); 35 36 void _PR_MD_INIT_IO() { 37 WORD WSAVersion = 0x0101; 38 WSADATA WSAData; 39 int err; 40 41 err = WSAStartup(WSAVersion, &WSAData); 42 PR_ASSERT(0 == err); 43 44 #ifdef DEBUG 45 /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */ 46 { 47 SYSTEMTIME systime; 48 union { 49 PRTime prt; 50 FILETIME ft; 51 } filetime; 52 BOOL rv; 53 54 systime.wYear = 1970; 55 systime.wMonth = 1; 56 /* wDayOfWeek is ignored */ 57 systime.wDay = 1; 58 systime.wHour = 0; 59 systime.wMinute = 0; 60 systime.wSecond = 0; 61 systime.wMilliseconds = 0; 62 63 rv = SystemTimeToFileTime(&systime, &filetime.ft); 64 PR_ASSERT(0 != rv); 65 PR_ASSERT(filetime.prt == _pr_filetime_offset); 66 } 67 #endif /* DEBUG */ 68 69 _PR_NT_InitSids(); 70 71 _PR_MD_InitSockets(); 72 } 73 74 PRStatus _PR_MD_WAIT(PRThread* thread, PRIntervalTime ticks) { 75 DWORD rv; 76 77 PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) 78 ? INFINITE 79 : PR_IntervalToMilliseconds(ticks); 80 rv = WaitForSingleObject(thread->md.blocked_sema, msecs); 81 switch (rv) { 82 case WAIT_OBJECT_0: 83 return PR_SUCCESS; 84 case WAIT_TIMEOUT: 85 _PR_THREAD_LOCK(thread); 86 if (thread->state == _PR_IO_WAIT) { 87 ; 88 } else { 89 if (thread->wait.cvar != NULL) { 90 thread->wait.cvar = NULL; 91 _PR_THREAD_UNLOCK(thread); 92 } else { 93 /* The CVAR was notified just as the timeout 94 * occurred. This led to us being notified twice. 95 * call WaitForSingleObject() to clear the semaphore. 96 */ 97 _PR_THREAD_UNLOCK(thread); 98 rv = WaitForSingleObject(thread->md.blocked_sema, 0); 99 PR_ASSERT(rv == WAIT_OBJECT_0); 100 } 101 } 102 return PR_SUCCESS; 103 default: 104 return PR_FAILURE; 105 } 106 } 107 PRStatus _PR_MD_WAKEUP_WAITER(PRThread* thread) { 108 if (_PR_IS_NATIVE_THREAD(thread)) { 109 if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE) { 110 return PR_FAILURE; 111 } else { 112 return PR_SUCCESS; 113 } 114 } 115 } 116 117 /* --- FILE IO ----------------------------------------------------------- */ 118 /* 119 * _PR_MD_OPEN() -- Open a file 120 * 121 * returns: a fileHandle 122 * 123 * The NSPR open flags (osflags) are translated into flags for Win95 124 * 125 * Mode seems to be passed in as a unix style file permissions argument 126 * as in 0666, in the case of opening the logFile. 127 * 128 */ 129 PROsfd _PR_MD_OPEN(const char* name, PRIntn osflags, int mode) { 130 HANDLE file; 131 PRInt32 access = 0; 132 PRInt32 flags = 0; 133 PRInt32 flag6 = 0; 134 135 if (osflags & PR_SYNC) { 136 flag6 = FILE_FLAG_WRITE_THROUGH; 137 } 138 139 if (osflags & PR_RDONLY || osflags & PR_RDWR) { 140 access |= GENERIC_READ; 141 } 142 if (osflags & PR_WRONLY || osflags & PR_RDWR) { 143 access |= GENERIC_WRITE; 144 } 145 146 if (osflags & PR_CREATE_FILE && osflags & PR_EXCL) { 147 flags = CREATE_NEW; 148 } else if (osflags & PR_CREATE_FILE) { 149 if (osflags & PR_TRUNCATE) { 150 flags = CREATE_ALWAYS; 151 } else { 152 flags = OPEN_ALWAYS; 153 } 154 } else { 155 if (osflags & PR_TRUNCATE) { 156 flags = TRUNCATE_EXISTING; 157 } else { 158 flags = OPEN_EXISTING; 159 } 160 } 161 162 file = CreateFileA(name, access, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, 163 flags, flag6, NULL); 164 if (file == INVALID_HANDLE_VALUE) { 165 _PR_MD_MAP_OPEN_ERROR(GetLastError()); 166 return -1; 167 } 168 169 return (PROsfd)file; 170 } 171 172 PROsfd _PR_MD_OPEN_FILE(const char* name, PRIntn osflags, int mode) { 173 HANDLE file; 174 PRInt32 access = 0; 175 PRInt32 flags = 0; 176 PRInt32 flag6 = 0; 177 SECURITY_ATTRIBUTES sa; 178 LPSECURITY_ATTRIBUTES lpSA = NULL; 179 PSECURITY_DESCRIPTOR pSD = NULL; 180 PACL pACL = NULL; 181 182 if (osflags & PR_CREATE_FILE) { 183 if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable, &pSD, &pACL) == 184 PR_SUCCESS) { 185 sa.nLength = sizeof(sa); 186 sa.lpSecurityDescriptor = pSD; 187 sa.bInheritHandle = FALSE; 188 lpSA = &sa; 189 } 190 } 191 192 if (osflags & PR_SYNC) { 193 flag6 = FILE_FLAG_WRITE_THROUGH; 194 } 195 196 if (osflags & PR_RDONLY || osflags & PR_RDWR) { 197 access |= GENERIC_READ; 198 } 199 if (osflags & PR_WRONLY || osflags & PR_RDWR) { 200 access |= GENERIC_WRITE; 201 } 202 203 if (osflags & PR_CREATE_FILE && osflags & PR_EXCL) { 204 flags = CREATE_NEW; 205 } else if (osflags & PR_CREATE_FILE) { 206 if (osflags & PR_TRUNCATE) { 207 flags = CREATE_ALWAYS; 208 } else { 209 flags = OPEN_ALWAYS; 210 } 211 } else { 212 if (osflags & PR_TRUNCATE) { 213 flags = TRUNCATE_EXISTING; 214 } else { 215 flags = OPEN_EXISTING; 216 } 217 } 218 219 file = CreateFileA(name, access, FILE_SHARE_READ | FILE_SHARE_WRITE, lpSA, 220 flags, flag6, NULL); 221 if (lpSA != NULL) { 222 _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); 223 } 224 if (file == INVALID_HANDLE_VALUE) { 225 _PR_MD_MAP_OPEN_ERROR(GetLastError()); 226 return -1; 227 } 228 229 return (PROsfd)file; 230 } 231 232 PRInt32 _PR_MD_READ(PRFileDesc* fd, void* buf, PRInt32 len) { 233 PRUint32 bytes; 234 int rv, err; 235 236 rv = ReadFile((HANDLE)fd->secret->md.osfd, (LPVOID)buf, len, &bytes, NULL); 237 238 if (rv == 0) { 239 err = GetLastError(); 240 /* ERROR_HANDLE_EOF can only be returned by async io */ 241 PR_ASSERT(err != ERROR_HANDLE_EOF); 242 if (err == ERROR_BROKEN_PIPE) { 243 return 0; 244 } else { 245 _PR_MD_MAP_READ_ERROR(err); 246 return -1; 247 } 248 } 249 return bytes; 250 } 251 252 PRInt32 _PR_MD_WRITE(PRFileDesc* fd, const void* buf, PRInt32 len) { 253 PROsfd f = fd->secret->md.osfd; 254 PRInt32 bytes; 255 int rv; 256 257 rv = WriteFile((HANDLE)f, buf, len, &bytes, NULL); 258 259 if (rv == 0) { 260 _PR_MD_MAP_WRITE_ERROR(GetLastError()); 261 return -1; 262 } 263 return bytes; 264 } /* --- end _PR_MD_WRITE() --- */ 265 266 PROffset32 _PR_MD_LSEEK(PRFileDesc* fd, PROffset32 offset, 267 PRSeekWhence whence) { 268 DWORD moveMethod; 269 PROffset32 rv; 270 271 switch (whence) { 272 case PR_SEEK_SET: 273 moveMethod = FILE_BEGIN; 274 break; 275 case PR_SEEK_CUR: 276 moveMethod = FILE_CURRENT; 277 break; 278 case PR_SEEK_END: 279 moveMethod = FILE_END; 280 break; 281 default: 282 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 283 return -1; 284 } 285 286 rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, NULL, moveMethod); 287 288 /* 289 * If the lpDistanceToMoveHigh argument (third argument) is 290 * NULL, SetFilePointer returns 0xffffffff on failure. 291 */ 292 if (-1 == rv) { 293 _PR_MD_MAP_LSEEK_ERROR(GetLastError()); 294 } 295 return rv; 296 } 297 298 PROffset64 _PR_MD_LSEEK64(PRFileDesc* fd, PROffset64 offset, 299 PRSeekWhence whence) { 300 DWORD moveMethod; 301 LARGE_INTEGER li; 302 DWORD err; 303 304 switch (whence) { 305 case PR_SEEK_SET: 306 moveMethod = FILE_BEGIN; 307 break; 308 case PR_SEEK_CUR: 309 moveMethod = FILE_CURRENT; 310 break; 311 case PR_SEEK_END: 312 moveMethod = FILE_END; 313 break; 314 default: 315 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 316 return -1; 317 } 318 319 li.QuadPart = offset; 320 li.LowPart = SetFilePointer((HANDLE)fd->secret->md.osfd, li.LowPart, 321 &li.HighPart, moveMethod); 322 323 if (0xffffffff == li.LowPart && (err = GetLastError()) != NO_ERROR) { 324 _PR_MD_MAP_LSEEK_ERROR(err); 325 li.QuadPart = -1; 326 } 327 return li.QuadPart; 328 } 329 330 /* 331 * This is documented to succeed on read-only files, but Win32's 332 * FlushFileBuffers functions fails with "access denied" in such a 333 * case. So we only signal an error if the error is *not* "access 334 * denied". 335 */ 336 PRInt32 _PR_MD_FSYNC(PRFileDesc* fd) { 337 /* 338 * From the documentation: 339 * 340 * On Windows NT, the function FlushFileBuffers fails if hFile 341 * is a handle to console output. That is because console 342 * output is not buffered. The function returns FALSE, and 343 * GetLastError returns ERROR_INVALID_HANDLE. 344 * 345 * On the other hand, on Win95, it returns without error. I cannot 346 * assume that 0, 1, and 2 are console, because if someone closes 347 * System.out and then opens a file, they might get file descriptor 348 * 1. An error on *that* version of 1 should be reported, whereas 349 * an error on System.out (which was the original 1) should be 350 * ignored. So I use isatty() to ensure that such an error was due 351 * to this bogosity, and if it was, I ignore the error. 352 */ 353 354 BOOL ok = FlushFileBuffers((HANDLE)fd->secret->md.osfd); 355 356 if (!ok) { 357 DWORD err = GetLastError(); 358 if (err != ERROR_ACCESS_DENIED) { // from winerror.h 359 _PR_MD_MAP_FSYNC_ERROR(err); 360 return -1; 361 } 362 } 363 return 0; 364 } 365 366 PRInt32 _MD_CloseFile(PROsfd osfd) { 367 PRInt32 rv; 368 369 rv = (CloseHandle((HANDLE)osfd)) ? 0 : -1; 370 if (rv == -1) { 371 _PR_MD_MAP_CLOSE_ERROR(GetLastError()); 372 } 373 return rv; 374 } 375 376 /* --- DIR IO ------------------------------------------------------------ */ 377 #define GetFileFromDIR(d) (d)->d_entry.cFileName 378 #define FileIsHidden(d) ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) 379 380 static void FlipSlashes(char* cp, size_t len) { 381 while (len-- > 0) { 382 if (cp[0] == '/') { 383 cp[0] = PR_DIRECTORY_SEPARATOR; 384 } 385 cp = _mbsinc(cp); 386 } 387 } /* end FlipSlashes() */ 388 389 /* 390 ** 391 ** Local implementations of standard Unix RTL functions which are not provided 392 ** by the VC RTL. 393 ** 394 */ 395 396 PRInt32 _PR_MD_CLOSE_DIR(_MDDir* d) { 397 if (d) { 398 if (FindClose(d->d_hdl)) { 399 d->magic = (PRUint32)-1; 400 return 0; 401 } else { 402 _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError()); 403 return -1; 404 } 405 } 406 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 407 return -1; 408 } 409 410 PRStatus _PR_MD_OPEN_DIR(_MDDir* d, const char* name) { 411 char filename[MAX_PATH]; 412 size_t len; 413 414 len = strlen(name); 415 /* Need 5 bytes for \*.* and the trailing null byte. */ 416 if (len + 5 > MAX_PATH) { 417 PR_SetError(PR_NAME_TOO_LONG_ERROR, 0); 418 return PR_FAILURE; 419 } 420 strcpy(filename, name); 421 422 /* 423 * If 'name' ends in a slash or backslash, do not append 424 * another backslash. 425 */ 426 if (IsPrevCharSlash(filename, filename + len)) { 427 len--; 428 } 429 strcpy(&filename[len], "\\*.*"); 430 FlipSlashes(filename, strlen(filename)); 431 432 d->d_hdl = FindFirstFileA(filename, &(d->d_entry)); 433 if (d->d_hdl == INVALID_HANDLE_VALUE) { 434 _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); 435 return PR_FAILURE; 436 } 437 d->firstEntry = PR_TRUE; 438 d->magic = _MD_MAGIC_DIR; 439 return PR_SUCCESS; 440 } 441 442 char* _PR_MD_READ_DIR(_MDDir* d, PRIntn flags) { 443 PRInt32 err; 444 BOOL rv; 445 char* fileName; 446 447 if (d) { 448 while (1) { 449 if (d->firstEntry) { 450 d->firstEntry = PR_FALSE; 451 rv = 1; 452 } else { 453 rv = FindNextFileA(d->d_hdl, &(d->d_entry)); 454 } 455 if (rv == 0) { 456 break; 457 } 458 fileName = GetFileFromDIR(d); 459 if ((flags & PR_SKIP_DOT) && (fileName[0] == '.') && 460 (fileName[1] == '\0')) { 461 continue; 462 } 463 if ((flags & PR_SKIP_DOT_DOT) && (fileName[0] == '.') && 464 (fileName[1] == '.') && (fileName[2] == '\0')) { 465 continue; 466 } 467 if ((flags & PR_SKIP_HIDDEN) && FileIsHidden(d)) { 468 continue; 469 } 470 return fileName; 471 } 472 err = GetLastError(); 473 PR_ASSERT(NO_ERROR != err); 474 _PR_MD_MAP_READDIR_ERROR(err); 475 return NULL; 476 } 477 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 478 return NULL; 479 } 480 481 PRInt32 _PR_MD_DELETE(const char* name) { 482 if (DeleteFileA(name)) { 483 return 0; 484 } else { 485 _PR_MD_MAP_DELETE_ERROR(GetLastError()); 486 return -1; 487 } 488 } 489 490 void _PR_FileTimeToPRTime(const FILETIME* filetime, PRTime* prtm) { 491 PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime)); 492 CopyMemory(prtm, filetime, sizeof(PRTime)); 493 #if defined(__MINGW32__) 494 *prtm = (*prtm - _pr_filetime_offset) / 10LL; 495 #else 496 *prtm = (*prtm - _pr_filetime_offset) / 10i64; 497 #endif 498 499 #ifdef DEBUG 500 /* Doublecheck our calculation. */ 501 { 502 SYSTEMTIME systime; 503 PRExplodedTime etm; 504 PRTime cmp; /* for comparison */ 505 BOOL rv; 506 507 rv = FileTimeToSystemTime(filetime, &systime); 508 PR_ASSERT(0 != rv); 509 510 /* 511 * PR_ImplodeTime ignores wday and yday. 512 */ 513 etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC; 514 etm.tm_sec = systime.wSecond; 515 etm.tm_min = systime.wMinute; 516 etm.tm_hour = systime.wHour; 517 etm.tm_mday = systime.wDay; 518 etm.tm_month = systime.wMonth - 1; 519 etm.tm_year = systime.wYear; 520 /* 521 * It is not well-documented what time zone the FILETIME's 522 * are in. WIN32_FIND_DATA is documented to be in UTC (GMT). 523 * But BY_HANDLE_FILE_INFORMATION is unclear about this. 524 * By our best judgement, we assume that FILETIME is in UTC. 525 */ 526 etm.tm_params.tp_gmt_offset = 0; 527 etm.tm_params.tp_dst_offset = 0; 528 cmp = PR_ImplodeTime(&etm); 529 530 /* 531 * SYSTEMTIME is in milliseconds precision, so we convert PRTime's 532 * microseconds to milliseconds before doing the comparison. 533 */ 534 PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC)); 535 } 536 #endif /* DEBUG */ 537 } 538 539 PRInt32 _PR_MD_STAT(const char* fn, struct stat* info) { 540 PRInt32 rv; 541 542 rv = _stat(fn, (struct _stat*)info); 543 if (-1 == rv) { 544 /* 545 * Check for MSVC runtime library _stat() bug. 546 * (It's really a bug in FindFirstFile().) 547 * If a pathname ends in a backslash or slash, 548 * e.g., c:\temp\ or c:/temp/, _stat() will fail. 549 * Note: a pathname ending in a slash (e.g., c:/temp/) 550 * can be handled by _stat() on NT but not on Win95. 551 * 552 * We remove the backslash or slash at the end and 553 * try again. 554 */ 555 556 size_t len = strlen(fn); 557 if (len > 0 && len <= _MAX_PATH && IsPrevCharSlash(fn, fn + len)) { 558 char newfn[_MAX_PATH + 1]; 559 560 strcpy(newfn, fn); 561 newfn[len - 1] = '\0'; 562 rv = _stat(newfn, (struct _stat*)info); 563 } 564 } 565 566 if (-1 == rv) { 567 _PR_MD_MAP_STAT_ERROR(errno); 568 } 569 return rv; 570 } 571 572 #define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\') 573 574 static PRBool IsPrevCharSlash(const char* str, const char* current) { 575 const char* prev; 576 577 if (str >= current) { 578 return PR_FALSE; 579 } 580 prev = _mbsdec(str, current); 581 return (prev == current - 1) && _PR_IS_SLASH(*prev); 582 } 583 584 /* 585 * IsRootDirectory -- 586 * 587 * Return PR_TRUE if the pathname 'fn' is a valid root directory, 588 * else return PR_FALSE. The char buffer pointed to by 'fn' must 589 * be writable. During the execution of this function, the contents 590 * of the buffer pointed to by 'fn' may be modified, but on return 591 * the original contents will be restored. 'buflen' is the size of 592 * the buffer pointed to by 'fn'. 593 * 594 * Root directories come in three formats: 595 * 1. / or \, meaning the root directory of the current drive. 596 * 2. C:/ or C:\, where C is a drive letter. 597 * 3. \\<server name>\<share point name>\ or 598 * \\<server name>\<share point name>, meaning the root directory 599 * of a UNC (Universal Naming Convention) name. 600 */ 601 602 static PRBool IsRootDirectory(char* fn, size_t buflen) { 603 char* p; 604 PRBool slashAdded = PR_FALSE; 605 PRBool rv = PR_FALSE; 606 607 if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') { 608 return PR_TRUE; 609 } 610 611 if (isalpha((unsigned char)fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2]) && fn[3] == '\0') { 612 rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE; 613 return rv; 614 } 615 616 /* The UNC root directory */ 617 618 if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) { 619 /* The 'server' part should have at least one character. */ 620 p = &fn[2]; 621 if (*p == '\0' || _PR_IS_SLASH(*p)) { 622 return PR_FALSE; 623 } 624 625 /* look for the next slash */ 626 do { 627 p = _mbsinc(p); 628 } while (*p != '\0' && !_PR_IS_SLASH(*p)); 629 if (*p == '\0') { 630 return PR_FALSE; 631 } 632 633 /* The 'share' part should have at least one character. */ 634 p++; 635 if (*p == '\0' || _PR_IS_SLASH(*p)) { 636 return PR_FALSE; 637 } 638 639 /* look for the final slash */ 640 do { 641 p = _mbsinc(p); 642 } while (*p != '\0' && !_PR_IS_SLASH(*p)); 643 if (_PR_IS_SLASH(*p) && p[1] != '\0') { 644 return PR_FALSE; 645 } 646 if (*p == '\0') { 647 /* 648 * GetDriveType() doesn't work correctly if the 649 * path is of the form \\server\share, so we add 650 * a final slash temporarily. 651 */ 652 if ((p + 1) < (fn + buflen)) { 653 *p++ = '\\'; 654 *p = '\0'; 655 slashAdded = PR_TRUE; 656 } else { 657 return PR_FALSE; /* name too long */ 658 } 659 } 660 rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE; 661 /* restore the 'fn' buffer */ 662 if (slashAdded) { 663 *--p = '\0'; 664 } 665 } 666 return rv; 667 } 668 669 PRInt32 _PR_MD_GETFILEINFO64(const char* fn, PRFileInfo64* info) { 670 WIN32_FILE_ATTRIBUTE_DATA findFileData; 671 BOOL rv; 672 673 if (NULL == fn || '\0' == *fn) { 674 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 675 return -1; 676 } 677 678 rv = GetFileAttributesEx(fn, GetFileExInfoStandard, &findFileData); 679 if (!rv) { 680 _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); 681 return -1; 682 } 683 684 if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 685 info->type = PR_FILE_DIRECTORY; 686 } else { 687 info->type = PR_FILE_FILE; 688 } 689 690 info->size = findFileData.nFileSizeHigh; 691 info->size = (info->size << 32) + findFileData.nFileSizeLow; 692 693 _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime); 694 695 if (0 == findFileData.ftCreationTime.dwLowDateTime && 696 0 == findFileData.ftCreationTime.dwHighDateTime) { 697 info->creationTime = info->modifyTime; 698 } else { 699 _PR_FileTimeToPRTime(&findFileData.ftCreationTime, &info->creationTime); 700 } 701 702 return 0; 703 } 704 705 PRInt32 _PR_MD_GETFILEINFO(const char* fn, PRFileInfo* info) { 706 PRFileInfo64 info64; 707 PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64); 708 if (0 == rv) { 709 info->type = info64.type; 710 info->size = (PRUint32)info64.size; 711 info->modifyTime = info64.modifyTime; 712 info->creationTime = info64.creationTime; 713 } 714 return rv; 715 } 716 717 PRInt32 _PR_MD_GETOPENFILEINFO64(const PRFileDesc* fd, PRFileInfo64* info) { 718 int rv; 719 720 BY_HANDLE_FILE_INFORMATION hinfo; 721 722 rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo); 723 if (rv == FALSE) { 724 _PR_MD_MAP_FSTAT_ERROR(GetLastError()); 725 return -1; 726 } 727 728 if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 729 info->type = PR_FILE_DIRECTORY; 730 } else { 731 info->type = PR_FILE_FILE; 732 } 733 734 info->size = hinfo.nFileSizeHigh; 735 info->size = (info->size << 32) + hinfo.nFileSizeLow; 736 737 _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime)); 738 _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime)); 739 740 return 0; 741 } 742 743 PRInt32 _PR_MD_GETOPENFILEINFO(const PRFileDesc* fd, PRFileInfo* info) { 744 PRFileInfo64 info64; 745 int rv = _PR_MD_GETOPENFILEINFO64(fd, &info64); 746 if (0 == rv) { 747 info->type = info64.type; 748 info->modifyTime = info64.modifyTime; 749 info->creationTime = info64.creationTime; 750 LL_L2I(info->size, info64.size); 751 } 752 return rv; 753 } 754 755 PRStatus _PR_MD_SET_FD_INHERITABLE(PRFileDesc* fd, PRBool inheritable) { 756 BOOL rv; 757 758 /* 759 * The SetHandleInformation function fails with the 760 * ERROR_CALL_NOT_IMPLEMENTED error on Win95. 761 */ 762 rv = SetHandleInformation((HANDLE)fd->secret->md.osfd, HANDLE_FLAG_INHERIT, 763 inheritable ? HANDLE_FLAG_INHERIT : 0); 764 if (0 == rv) { 765 _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); 766 return PR_FAILURE; 767 } 768 return PR_SUCCESS; 769 } 770 771 void _PR_MD_INIT_FD_INHERITABLE(PRFileDesc* fd, PRBool imported) { 772 if (imported) { 773 fd->secret->inheritable = _PR_TRI_UNKNOWN; 774 } else { 775 fd->secret->inheritable = _PR_TRI_FALSE; 776 } 777 } 778 779 void _PR_MD_QUERY_FD_INHERITABLE(PRFileDesc* fd) { 780 DWORD flags; 781 782 PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable); 783 if (GetHandleInformation((HANDLE)fd->secret->md.osfd, &flags)) { 784 if (flags & HANDLE_FLAG_INHERIT) { 785 fd->secret->inheritable = _PR_TRI_TRUE; 786 } else { 787 fd->secret->inheritable = _PR_TRI_FALSE; 788 } 789 } 790 } 791 792 PRInt32 _PR_MD_RENAME(const char* from, const char* to) { 793 /* Does this work with dot-relative pathnames? */ 794 if (MoveFileA(from, to)) { 795 return 0; 796 } else { 797 _PR_MD_MAP_RENAME_ERROR(GetLastError()); 798 return -1; 799 } 800 } 801 802 PRInt32 _PR_MD_ACCESS(const char* name, PRAccessHow how) { 803 PRInt32 rv; 804 switch (how) { 805 case PR_ACCESS_WRITE_OK: 806 rv = _access(name, 02); 807 break; 808 case PR_ACCESS_READ_OK: 809 rv = _access(name, 04); 810 break; 811 case PR_ACCESS_EXISTS: 812 return _access(name, 00); 813 break; 814 default: 815 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 816 return -1; 817 } 818 if (rv < 0) { 819 _PR_MD_MAP_ACCESS_ERROR(errno); 820 } 821 return rv; 822 } 823 824 PRInt32 _PR_MD_MKDIR(const char* name, PRIntn mode) { 825 /* XXXMB - how to translate the "mode"??? */ 826 if (CreateDirectoryA(name, NULL)) { 827 return 0; 828 } else { 829 _PR_MD_MAP_MKDIR_ERROR(GetLastError()); 830 return -1; 831 } 832 } 833 834 PRInt32 _PR_MD_MAKE_DIR(const char* name, PRIntn mode) { 835 BOOL rv; 836 SECURITY_ATTRIBUTES sa; 837 LPSECURITY_ATTRIBUTES lpSA = NULL; 838 PSECURITY_DESCRIPTOR pSD = NULL; 839 PACL pACL = NULL; 840 841 if (_PR_NT_MakeSecurityDescriptorACL(mode, dirAccessTable, &pSD, &pACL) == 842 PR_SUCCESS) { 843 sa.nLength = sizeof(sa); 844 sa.lpSecurityDescriptor = pSD; 845 sa.bInheritHandle = FALSE; 846 lpSA = &sa; 847 } 848 rv = CreateDirectoryA(name, lpSA); 849 if (lpSA != NULL) { 850 _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); 851 } 852 if (rv) { 853 return 0; 854 } else { 855 _PR_MD_MAP_MKDIR_ERROR(GetLastError()); 856 return -1; 857 } 858 } 859 860 PRInt32 _PR_MD_RMDIR(const char* name) { 861 if (RemoveDirectoryA(name)) { 862 return 0; 863 } else { 864 _PR_MD_MAP_RMDIR_ERROR(GetLastError()); 865 return -1; 866 } 867 } 868 869 PRStatus _PR_MD_LOCKFILE(PROsfd f) { 870 PRStatus rc = PR_SUCCESS; 871 DWORD rv; 872 873 rv = LockFile((HANDLE)f, 0l, 0l, 0x0l, 0xffffffffl); 874 if (rv == 0) { 875 DWORD err = GetLastError(); 876 _PR_MD_MAP_DEFAULT_ERROR(err); 877 PR_LOG(_pr_io_lm, PR_LOG_ERROR, 878 ("_PR_MD_LOCKFILE() failed. Error: %d", err)); 879 rc = PR_FAILURE; 880 } 881 882 return rc; 883 } /* end _PR_MD_LOCKFILE() */ 884 885 PRStatus _PR_MD_TLOCKFILE(PROsfd f) { 886 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); 887 return PR_FAILURE; 888 } /* end _PR_MD_TLOCKFILE() */ 889 890 PRStatus _PR_MD_UNLOCKFILE(PROsfd f) { 891 PRInt32 rv; 892 893 rv = UnlockFile((HANDLE)f, 0l, 0l, 0x0l, 0xffffffffl); 894 895 if (rv) { 896 return PR_SUCCESS; 897 } else { 898 _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); 899 return PR_FAILURE; 900 } 901 } /* end _PR_MD_UNLOCKFILE() */ 902 903 PRInt32 _PR_MD_PIPEAVAILABLE(PRFileDesc* fd) { 904 if (NULL == fd) { 905 PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); 906 } else { 907 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); 908 } 909 return -1; 910 } 911 912 #ifdef MOZ_UNICODE 913 914 typedef HANDLE(WINAPI* CreateFileWFn)(LPCWSTR, DWORD, DWORD, 915 LPSECURITY_ATTRIBUTES, DWORD, DWORD, 916 HANDLE); 917 static CreateFileWFn createFileW = CreateFileW; 918 typedef HANDLE(WINAPI* FindFirstFileWFn)(LPCWSTR, LPWIN32_FIND_DATAW); 919 static FindFirstFileWFn findFirstFileW = FindFirstFileW; 920 typedef BOOL(WINAPI* FindNextFileWFn)(HANDLE, LPWIN32_FIND_DATAW); 921 static FindNextFileWFn findNextFileW = FindNextFileW; 922 typedef DWORD(WINAPI* GetFullPathNameWFn)(LPCWSTR, DWORD, LPWSTR, LPWSTR*); 923 static GetFullPathNameWFn getFullPathNameW = GetFullPathNameW; 924 typedef UINT(WINAPI* GetDriveTypeWFn)(LPCWSTR); 925 static GetDriveTypeWFn getDriveTypeW = GetDriveTypeW; 926 927 #endif /* MOZ_UNICODE */ 928 929 #ifdef MOZ_UNICODE 930 931 /* ================ UTF16 Interfaces ================================ */ 932 static void FlipSlashesW(PRUnichar* cp, size_t len) { 933 while (len-- > 0) { 934 if (cp[0] == L'/') { 935 cp[0] = L'\\'; 936 } 937 cp++; 938 } 939 } /* end FlipSlashesW() */ 940 941 PROsfd _PR_MD_OPEN_FILE_UTF16(const PRUnichar* name, PRIntn osflags, int mode) { 942 HANDLE file; 943 PRInt32 access = 0; 944 PRInt32 flags = 0; 945 PRInt32 flag6 = 0; 946 SECURITY_ATTRIBUTES sa; 947 LPSECURITY_ATTRIBUTES lpSA = NULL; 948 PSECURITY_DESCRIPTOR pSD = NULL; 949 PACL pACL = NULL; 950 951 if (osflags & PR_CREATE_FILE) { 952 if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable, &pSD, &pACL) == 953 PR_SUCCESS) { 954 sa.nLength = sizeof(sa); 955 sa.lpSecurityDescriptor = pSD; 956 sa.bInheritHandle = FALSE; 957 lpSA = &sa; 958 } 959 } 960 961 if (osflags & PR_SYNC) { 962 flag6 = FILE_FLAG_WRITE_THROUGH; 963 } 964 965 if (osflags & PR_RDONLY || osflags & PR_RDWR) { 966 access |= GENERIC_READ; 967 } 968 if (osflags & PR_WRONLY || osflags & PR_RDWR) { 969 access |= GENERIC_WRITE; 970 } 971 972 if (osflags & PR_CREATE_FILE && osflags & PR_EXCL) { 973 flags = CREATE_NEW; 974 } else if (osflags & PR_CREATE_FILE) { 975 if (osflags & PR_TRUNCATE) { 976 flags = CREATE_ALWAYS; 977 } else { 978 flags = OPEN_ALWAYS; 979 } 980 } else { 981 if (osflags & PR_TRUNCATE) { 982 flags = TRUNCATE_EXISTING; 983 } else { 984 flags = OPEN_EXISTING; 985 } 986 } 987 988 file = createFileW(name, access, FILE_SHARE_READ | FILE_SHARE_WRITE, lpSA, 989 flags, flag6, NULL); 990 if (lpSA != NULL) { 991 _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); 992 } 993 if (file == INVALID_HANDLE_VALUE) { 994 _PR_MD_MAP_OPEN_ERROR(GetLastError()); 995 return -1; 996 } 997 998 return (PROsfd)file; 999 } 1000 1001 PRStatus _PR_MD_OPEN_DIR_UTF16(_MDDirUTF16* d, const PRUnichar* name) { 1002 PRUnichar filename[MAX_PATH]; 1003 int len; 1004 1005 len = wcslen(name); 1006 /* Need 5 bytes for \*.* and the trailing null byte. */ 1007 if (len + 5 > MAX_PATH) { 1008 PR_SetError(PR_NAME_TOO_LONG_ERROR, 0); 1009 return PR_FAILURE; 1010 } 1011 wcscpy(filename, name); 1012 1013 /* 1014 * If 'name' ends in a slash or backslash, do not append 1015 * another backslash. 1016 */ 1017 if (filename[len - 1] == L'/' || filename[len - 1] == L'\\') { 1018 len--; 1019 } 1020 wcscpy(&filename[len], L"\\*.*"); 1021 FlipSlashesW(filename, wcslen(filename)); 1022 1023 d->d_hdl = findFirstFileW(filename, &(d->d_entry)); 1024 if (d->d_hdl == INVALID_HANDLE_VALUE) { 1025 _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); 1026 return PR_FAILURE; 1027 } 1028 d->firstEntry = PR_TRUE; 1029 d->magic = _MD_MAGIC_DIR; 1030 return PR_SUCCESS; 1031 } 1032 1033 PRUnichar* _PR_MD_READ_DIR_UTF16(_MDDirUTF16* d, PRIntn flags) { 1034 PRInt32 err; 1035 BOOL rv; 1036 PRUnichar* fileName; 1037 1038 if (d) { 1039 while (1) { 1040 if (d->firstEntry) { 1041 d->firstEntry = PR_FALSE; 1042 rv = 1; 1043 } else { 1044 rv = findNextFileW(d->d_hdl, &(d->d_entry)); 1045 } 1046 if (rv == 0) { 1047 break; 1048 } 1049 fileName = GetFileFromDIR(d); 1050 if ((flags & PR_SKIP_DOT) && (fileName[0] == L'.') && 1051 (fileName[1] == L'\0')) { 1052 continue; 1053 } 1054 if ((flags & PR_SKIP_DOT_DOT) && (fileName[0] == L'.') && 1055 (fileName[1] == L'.') && (fileName[2] == L'\0')) { 1056 continue; 1057 } 1058 if ((flags & PR_SKIP_HIDDEN) && FileIsHidden(d)) { 1059 continue; 1060 } 1061 return fileName; 1062 } 1063 err = GetLastError(); 1064 PR_ASSERT(NO_ERROR != err); 1065 _PR_MD_MAP_READDIR_ERROR(err); 1066 return NULL; 1067 } 1068 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 1069 return NULL; 1070 } 1071 1072 PRInt32 _PR_MD_CLOSE_DIR_UTF16(_MDDirUTF16* d) { 1073 if (d) { 1074 if (FindClose(d->d_hdl)) { 1075 d->magic = (PRUint32)-1; 1076 return 0; 1077 } else { 1078 _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError()); 1079 return -1; 1080 } 1081 } 1082 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 1083 return -1; 1084 } 1085 1086 # define _PR_IS_W_SLASH(ch) ((ch) == L'/' || (ch) == L'\\') 1087 1088 /* 1089 * IsRootDirectoryW -- 1090 * 1091 * Return PR_TRUE if the pathname 'fn' is a valid root directory, 1092 * else return PR_FALSE. The PRUnichar buffer pointed to by 'fn' must 1093 * be writable. During the execution of this function, the contents 1094 * of the buffer pointed to by 'fn' may be modified, but on return 1095 * the original contents will be restored. 'buflen' is the size of 1096 * the buffer pointed to by 'fn', in PRUnichars. 1097 * 1098 * Root directories come in three formats: 1099 * 1. / or \, meaning the root directory of the current drive. 1100 * 2. C:/ or C:\, where C is a drive letter. 1101 * 3. \\<server name>\<share point name>\ or 1102 * \\<server name>\<share point name>, meaning the root directory 1103 * of a UNC (Universal Naming Convention) name. 1104 */ 1105 1106 static PRBool IsRootDirectoryW(PRUnichar* fn, size_t buflen) { 1107 PRUnichar* p; 1108 PRBool slashAdded = PR_FALSE; 1109 PRBool rv = PR_FALSE; 1110 1111 if (_PR_IS_W_SLASH(fn[0]) && fn[1] == L'\0') { 1112 return PR_TRUE; 1113 } 1114 1115 if (iswalpha(fn[0]) && fn[1] == L':' && _PR_IS_W_SLASH(fn[2]) && 1116 fn[3] == L'\0') { 1117 rv = getDriveTypeW(fn) > 1 ? PR_TRUE : PR_FALSE; 1118 return rv; 1119 } 1120 1121 /* The UNC root directory */ 1122 1123 if (_PR_IS_W_SLASH(fn[0]) && _PR_IS_W_SLASH(fn[1])) { 1124 /* The 'server' part should have at least one character. */ 1125 p = &fn[2]; 1126 if (*p == L'\0' || _PR_IS_W_SLASH(*p)) { 1127 return PR_FALSE; 1128 } 1129 1130 /* look for the next slash */ 1131 do { 1132 p++; 1133 } while (*p != L'\0' && !_PR_IS_W_SLASH(*p)); 1134 if (*p == L'\0') { 1135 return PR_FALSE; 1136 } 1137 1138 /* The 'share' part should have at least one character. */ 1139 p++; 1140 if (*p == L'\0' || _PR_IS_W_SLASH(*p)) { 1141 return PR_FALSE; 1142 } 1143 1144 /* look for the final slash */ 1145 do { 1146 p++; 1147 } while (*p != L'\0' && !_PR_IS_W_SLASH(*p)); 1148 if (_PR_IS_W_SLASH(*p) && p[1] != L'\0') { 1149 return PR_FALSE; 1150 } 1151 if (*p == L'\0') { 1152 /* 1153 * GetDriveType() doesn't work correctly if the 1154 * path is of the form \\server\share, so we add 1155 * a final slash temporarily. 1156 */ 1157 if ((p + 1) < (fn + buflen)) { 1158 *p++ = L'\\'; 1159 *p = L'\0'; 1160 slashAdded = PR_TRUE; 1161 } else { 1162 return PR_FALSE; /* name too long */ 1163 } 1164 } 1165 rv = getDriveTypeW(fn) > 1 ? PR_TRUE : PR_FALSE; 1166 /* restore the 'fn' buffer */ 1167 if (slashAdded) { 1168 *--p = L'\0'; 1169 } 1170 } 1171 return rv; 1172 } 1173 1174 PRInt32 _PR_MD_GETFILEINFO64_UTF16(const PRUnichar* fn, PRFileInfo64* info) { 1175 HANDLE hFindFile; 1176 WIN32_FIND_DATAW findFileData; 1177 PRUnichar pathbuf[MAX_PATH + 1]; 1178 1179 if (NULL == fn || L'\0' == *fn) { 1180 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 1181 return -1; 1182 } 1183 1184 /* 1185 * FindFirstFile() expands wildcard characters. So 1186 * we make sure the pathname contains no wildcard. 1187 */ 1188 if (NULL != wcspbrk(fn, L"?*")) { 1189 PR_SetError(PR_FILE_NOT_FOUND_ERROR, 0); 1190 return -1; 1191 } 1192 1193 hFindFile = findFirstFileW(fn, &findFileData); 1194 if (INVALID_HANDLE_VALUE == hFindFile) { 1195 DWORD len; 1196 PRUnichar* filePart; 1197 1198 /* 1199 * FindFirstFile() does not work correctly on root directories. 1200 * It also doesn't work correctly on a pathname that ends in a 1201 * slash. So we first check to see if the pathname specifies a 1202 * root directory. If not, and if the pathname ends in a slash, 1203 * we remove the final slash and try again. 1204 */ 1205 1206 /* 1207 * If the pathname does not contain ., \, and /, it cannot be 1208 * a root directory or a pathname that ends in a slash. 1209 */ 1210 if (NULL == wcspbrk(fn, L".\\/")) { 1211 _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); 1212 return -1; 1213 } 1214 len = getFullPathNameW(fn, sizeof(pathbuf) / sizeof(pathbuf[0]), pathbuf, 1215 &filePart); 1216 if (0 == len) { 1217 _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); 1218 return -1; 1219 } 1220 if (len > sizeof(pathbuf) / sizeof(pathbuf[0])) { 1221 PR_SetError(PR_NAME_TOO_LONG_ERROR, 0); 1222 return -1; 1223 } 1224 if (IsRootDirectoryW(pathbuf, sizeof(pathbuf) / sizeof(pathbuf[0]))) { 1225 info->type = PR_FILE_DIRECTORY; 1226 info->size = 0; 1227 /* 1228 * These timestamps don't make sense for root directories. 1229 */ 1230 info->modifyTime = 0; 1231 info->creationTime = 0; 1232 return 0; 1233 } 1234 if (!_PR_IS_W_SLASH(pathbuf[len - 1])) { 1235 _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); 1236 return -1; 1237 } else { 1238 pathbuf[len - 1] = L'\0'; 1239 hFindFile = findFirstFileW(pathbuf, &findFileData); 1240 if (INVALID_HANDLE_VALUE == hFindFile) { 1241 _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); 1242 return -1; 1243 } 1244 } 1245 } 1246 1247 FindClose(hFindFile); 1248 1249 if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 1250 info->type = PR_FILE_DIRECTORY; 1251 } else { 1252 info->type = PR_FILE_FILE; 1253 } 1254 1255 info->size = findFileData.nFileSizeHigh; 1256 info->size = (info->size << 32) + findFileData.nFileSizeLow; 1257 1258 _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime); 1259 1260 if (0 == findFileData.ftCreationTime.dwLowDateTime && 1261 0 == findFileData.ftCreationTime.dwHighDateTime) { 1262 info->creationTime = info->modifyTime; 1263 } else { 1264 _PR_FileTimeToPRTime(&findFileData.ftCreationTime, &info->creationTime); 1265 } 1266 1267 return 0; 1268 } 1269 /* ================ end of UTF16 Interfaces ================================ */ 1270 #endif /* MOZ_UNICODE */