OLD | NEW |
| (Empty) |
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 #ifdef WINCE | |
20 | |
21 static HANDLE CreateFileA(LPCSTR lpFileName, | |
22 DWORD dwDesiredAccess, | |
23 DWORD dwShareMode, | |
24 LPSECURITY_ATTRIBUTES lpSecurityAttributes, | |
25 DWORD dwCreationDisposition, | |
26 DWORD dwFlagsAndAttributes, | |
27 HANDLE hTemplateFile) | |
28 { | |
29 PRUnichar wFileName[MAX_PATH]; | |
30 MultiByteToWideChar(CP_ACP, 0, lpFileName, -1, wFileName, MAX_PATH); | |
31 return CreateFileW(wFileName, dwDesiredAccess, dwShareMode, | |
32 lpSecurityAttributes, dwCreationDisposition, | |
33 dwFlagsAndAttributes, hTemplateFile); | |
34 } | |
35 | |
36 /* | |
37 * We seem to call FindFirstFileA and FindNextFileA just to get | |
38 * the file names in a directory listing. If so, we could define | |
39 * a custom WIN32_FIND_DATAA structure with just the cFileName | |
40 * member, and the CopyFindFileDataW2A function could just | |
41 * copy/convert the cFileName member. | |
42 */ | |
43 static void CopyFindFileDataW2A(LPWIN32_FIND_DATAW from, | |
44 LPWIN32_FIND_DATAA to) | |
45 { | |
46 /* | |
47 * WIN32_FIND_DATAA and WIN32_FIND_DATAW are slightly different. | |
48 * The dwReserved0, dwReserved1, and cAlternateFileName members | |
49 * exist only in WIN32_FIND_DATAA. The dwOID member exists only | |
50 * in WIN32_FIND_DATAW. | |
51 */ | |
52 to->dwFileAttributes = from->dwFileAttributes; | |
53 to->ftCreationTime = from->ftCreationTime; | |
54 to->ftLastAccessTime = from->ftLastAccessTime; | |
55 to->ftLastWriteTime = from->ftLastWriteTime; | |
56 to->nFileSizeHigh = from->nFileSizeHigh; | |
57 to->nFileSizeLow = from->nFileSizeLow; | |
58 to->dwReserved0 = 0; | |
59 to->dwReserved1 = 0; | |
60 WideCharToMultiByte(CP_ACP, 0, from->cFileName, -1, | |
61 to->cFileName, MAX_PATH, NULL, NULL); | |
62 to->cAlternateFileName[0] = '\0'; | |
63 } | |
64 | |
65 static HANDLE FindFirstFileA(LPCSTR lpFileName, | |
66 LPWIN32_FIND_DATAA lpFindFileData) | |
67 { | |
68 PRUnichar wFileName[MAX_PATH]; | |
69 HANDLE hFindFile; | |
70 WIN32_FIND_DATAW wFindFileData; | |
71 | |
72 MultiByteToWideChar(CP_ACP, 0, lpFileName, -1, wFileName, MAX_PATH); | |
73 hFindFile = FindFirstFileW(wFileName, &wFindFileData); | |
74 if (hFindFile != INVALID_HANDLE_VALUE) { | |
75 CopyFindFileDataW2A(&wFindFileData, lpFindFileData); | |
76 } | |
77 return hFindFile; | |
78 } | |
79 | |
80 static BOOL FindNextFileA(HANDLE hFindFile, | |
81 LPWIN32_FIND_DATAA lpFindFileData) | |
82 { | |
83 WIN32_FIND_DATAW wFindFileData; | |
84 BOOL rv; | |
85 | |
86 rv = FindNextFileW(hFindFile, &wFindFileData); | |
87 if (rv) { | |
88 CopyFindFileDataW2A(&wFindFileData, lpFindFileData); | |
89 } | |
90 return rv; | |
91 } | |
92 | |
93 static BOOL GetFileAttributesExA(LPCSTR lpFileName, | |
94 GET_FILEEX_INFO_LEVELS fInfoLevelId, | |
95 LPVOID lpFileInformation) | |
96 { | |
97 PRUnichar wFileName[MAX_PATH]; | |
98 MultiByteToWideChar(CP_ACP, 0, lpFileName, -1, wFileName, MAX_PATH); | |
99 return GetFileAttributesExW(wFileName, fInfoLevelId, lpFileInformation); | |
100 } | |
101 | |
102 static BOOL DeleteFileA(LPCSTR lpFileName) | |
103 { | |
104 PRUnichar wFileName[MAX_PATH]; | |
105 MultiByteToWideChar(CP_ACP, 0, lpFileName, -1, wFileName, MAX_PATH); | |
106 return DeleteFileW(wFileName); | |
107 } | |
108 | |
109 static BOOL MoveFileA(LPCSTR from, LPCSTR to) | |
110 { | |
111 PRUnichar wFrom[MAX_PATH]; | |
112 PRUnichar wTo[MAX_PATH]; | |
113 MultiByteToWideChar(CP_ACP, 0, from, -1, wFrom, MAX_PATH); | |
114 MultiByteToWideChar(CP_ACP, 0, to, -1, wTo, MAX_PATH); | |
115 return MoveFileW(wFrom, wTo); | |
116 } | |
117 | |
118 static BOOL CreateDirectoryA(LPCSTR lpPathName, | |
119 LPSECURITY_ATTRIBUTES lpSecurityAttributes) | |
120 { | |
121 PRUnichar wPathName[MAX_PATH]; | |
122 MultiByteToWideChar(CP_ACP, 0, lpPathName, -1, wPathName, MAX_PATH); | |
123 return CreateDirectoryW(wPathName, lpSecurityAttributes); | |
124 } | |
125 | |
126 static BOOL RemoveDirectoryA(LPCSTR lpPathName) | |
127 { | |
128 PRUnichar wPathName[MAX_PATH]; | |
129 MultiByteToWideChar(CP_ACP, 0, lpPathName, -1, wPathName, MAX_PATH); | |
130 return RemoveDirectoryW(wPathName); | |
131 } | |
132 | |
133 static long GetDriveType(const char *lpRootPathName) | |
134 { | |
135 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); | |
136 return 0; // The drive type cannot be determined. | |
137 } | |
138 | |
139 static DWORD GetFullPathName(const char *lpFileName, | |
140 DWORD nBufferLength, | |
141 const char *lpBuffer, | |
142 const char **lpFilePart) | |
143 { | |
144 // needs work dft | |
145 DWORD len = strlen(lpFileName); | |
146 if (len > nBufferLength) | |
147 return len; | |
148 | |
149 strncpy((char *)lpBuffer, lpFileName, len); | |
150 ((char *)lpBuffer)[len] = '\0'; | |
151 | |
152 if (lpFilePart) { | |
153 char *sep = strrchr(lpBuffer, '\\'); | |
154 if (sep) { | |
155 sep++; // pass the seperator | |
156 *lpFilePart = sep; | |
157 } else { | |
158 *lpFilePart = lpBuffer; | |
159 } | |
160 } | |
161 return len; | |
162 } | |
163 | |
164 static BOOL LockFile(HANDLE hFile, | |
165 DWORD dwFileOffsetLow, | |
166 DWORD dwFileOffsetHigh, | |
167 DWORD nNumberOfBytesToLockLow, | |
168 DWORD nNumberOfBytesToLockHigh) | |
169 { | |
170 OVERLAPPED overlapped = {0}; | |
171 overlapped.Offset = dwFileOffsetLow; | |
172 overlapped.OffsetHigh = dwFileOffsetHigh; | |
173 return LockFileEx(hFile, | |
174 LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, | |
175 0, // reserved | |
176 nNumberOfBytesToLockLow, | |
177 nNumberOfBytesToLockHigh, &overlapped); | |
178 } | |
179 | |
180 static BOOL UnlockFile(HANDLE hFile, | |
181 DWORD dwFileOffsetLow, | |
182 DWORD dwFileOffsetHigh, | |
183 DWORD nNumberOfBytesToUnlockLow, | |
184 DWORD nNumberOfBytesToUnlockHigh) | |
185 { | |
186 OVERLAPPED overlapped = {0}; | |
187 overlapped.Offset = dwFileOffsetLow; | |
188 overlapped.OffsetHigh = dwFileOffsetHigh; | |
189 return UnlockFileEx(hFile, | |
190 0, // reserved | |
191 nNumberOfBytesToUnlockLow, | |
192 nNumberOfBytesToUnlockHigh, &overlapped); | |
193 } | |
194 | |
195 static unsigned char *_mbsdec(const unsigned char *string1, | |
196 const unsigned char *string2) | |
197 { | |
198 // needs work dft | |
199 return NULL; | |
200 } | |
201 | |
202 static unsigned char *_mbsinc(const unsigned char *inCurrent) | |
203 { | |
204 // needs work dft | |
205 return (unsigned char *)(inCurrent + 1); | |
206 } | |
207 | |
208 #endif | |
209 | |
210 struct _MDLock _pr_ioq_lock; | |
211 | |
212 /* | |
213 * NSPR-to-NT access right mapping table for files. | |
214 */ | |
215 static DWORD fileAccessTable[] = { | |
216 FILE_GENERIC_READ, | |
217 FILE_GENERIC_WRITE, | |
218 FILE_GENERIC_EXECUTE | |
219 }; | |
220 | |
221 /* | |
222 * NSPR-to-NT access right mapping table for directories. | |
223 */ | |
224 static DWORD dirAccessTable[] = { | |
225 FILE_GENERIC_READ, | |
226 FILE_GENERIC_WRITE|FILE_DELETE_CHILD, | |
227 FILE_GENERIC_EXECUTE | |
228 }; | |
229 | |
230 /* Windows CE has GetFileAttributesEx. */ | |
231 #ifndef WINCE | |
232 typedef BOOL (WINAPI *GetFileAttributesExFn)(LPCTSTR, | |
233 GET_FILEEX_INFO_LEVELS, | |
234 LPVOID); | |
235 static GetFileAttributesExFn getFileAttributesEx; | |
236 static void InitGetFileInfo(void); | |
237 #endif | |
238 | |
239 static void InitUnicodeSupport(void); | |
240 | |
241 static PRBool IsPrevCharSlash(const char *str, const char *current); | |
242 | |
243 void | |
244 _PR_MD_INIT_IO() | |
245 { | |
246 WORD WSAVersion = 0x0101; | |
247 WSADATA WSAData; | |
248 int err; | |
249 | |
250 err = WSAStartup( WSAVersion, &WSAData ); | |
251 PR_ASSERT(0 == err); | |
252 | |
253 #ifdef DEBUG | |
254 /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */ | |
255 { | |
256 SYSTEMTIME systime; | |
257 union { | |
258 PRTime prt; | |
259 FILETIME ft; | |
260 } filetime; | |
261 BOOL rv; | |
262 | |
263 systime.wYear = 1970; | |
264 systime.wMonth = 1; | |
265 /* wDayOfWeek is ignored */ | |
266 systime.wDay = 1; | |
267 systime.wHour = 0; | |
268 systime.wMinute = 0; | |
269 systime.wSecond = 0; | |
270 systime.wMilliseconds = 0; | |
271 | |
272 rv = SystemTimeToFileTime(&systime, &filetime.ft); | |
273 PR_ASSERT(0 != rv); | |
274 PR_ASSERT(filetime.prt == _pr_filetime_offset); | |
275 } | |
276 #endif /* DEBUG */ | |
277 | |
278 _PR_NT_InitSids(); | |
279 | |
280 #ifndef WINCE | |
281 InitGetFileInfo(); | |
282 #endif | |
283 | |
284 InitUnicodeSupport(); | |
285 | |
286 _PR_MD_InitSockets(); | |
287 } | |
288 | |
289 PRStatus | |
290 _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks) | |
291 { | |
292 DWORD rv; | |
293 | |
294 PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ? | |
295 INFINITE : PR_IntervalToMilliseconds(ticks); | |
296 rv = WaitForSingleObject(thread->md.blocked_sema, msecs); | |
297 switch(rv) | |
298 { | |
299 case WAIT_OBJECT_0: | |
300 return PR_SUCCESS; | |
301 break; | |
302 case WAIT_TIMEOUT: | |
303 _PR_THREAD_LOCK(thread); | |
304 if (thread->state == _PR_IO_WAIT) { | |
305 ; | |
306 } else { | |
307 if (thread->wait.cvar != NULL) { | |
308 thread->wait.cvar = NULL; | |
309 _PR_THREAD_UNLOCK(thread); | |
310 } else { | |
311 /* The CVAR was notified just as the timeout | |
312 * occurred. This led to us being notified twice. | |
313 * call WaitForSingleObject() to clear the semaphore. | |
314 */ | |
315 _PR_THREAD_UNLOCK(thread); | |
316 rv = WaitForSingleObject(thread->md.blocked_sema, 0); | |
317 PR_ASSERT(rv == WAIT_OBJECT_0); | |
318 } | |
319 } | |
320 return PR_SUCCESS; | |
321 break; | |
322 default: | |
323 return PR_FAILURE; | |
324 break; | |
325 } | |
326 } | |
327 PRStatus | |
328 _PR_MD_WAKEUP_WAITER(PRThread *thread) | |
329 { | |
330 if ( _PR_IS_NATIVE_THREAD(thread) ) | |
331 { | |
332 if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE) | |
333 return PR_FAILURE; | |
334 else | |
335 return PR_SUCCESS; | |
336 } | |
337 } | |
338 | |
339 | |
340 /* --- FILE IO ----------------------------------------------------------- */ | |
341 /* | |
342 * _PR_MD_OPEN() -- Open a file | |
343 * | |
344 * returns: a fileHandle | |
345 * | |
346 * The NSPR open flags (osflags) are translated into flags for Win95 | |
347 * | |
348 * Mode seems to be passed in as a unix style file permissions argument | |
349 * as in 0666, in the case of opening the logFile. | |
350 * | |
351 */ | |
352 PROsfd | |
353 _PR_MD_OPEN(const char *name, PRIntn osflags, int mode) | |
354 { | |
355 HANDLE file; | |
356 PRInt32 access = 0; | |
357 PRInt32 flags = 0; | |
358 PRInt32 flag6 = 0; | |
359 | |
360 if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH; | |
361 | |
362 if (osflags & PR_RDONLY || osflags & PR_RDWR) | |
363 access |= GENERIC_READ; | |
364 if (osflags & PR_WRONLY || osflags & PR_RDWR) | |
365 access |= GENERIC_WRITE; | |
366 | |
367 if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) | |
368 flags = CREATE_NEW; | |
369 else if (osflags & PR_CREATE_FILE) { | |
370 if (osflags & PR_TRUNCATE) | |
371 flags = CREATE_ALWAYS; | |
372 else | |
373 flags = OPEN_ALWAYS; | |
374 } else { | |
375 if (osflags & PR_TRUNCATE) | |
376 flags = TRUNCATE_EXISTING; | |
377 else | |
378 flags = OPEN_EXISTING; | |
379 } | |
380 | |
381 file = CreateFileA(name, | |
382 access, | |
383 FILE_SHARE_READ|FILE_SHARE_WRITE, | |
384 NULL, | |
385 flags, | |
386 flag6, | |
387 NULL); | |
388 if (file == INVALID_HANDLE_VALUE) { | |
389 _PR_MD_MAP_OPEN_ERROR(GetLastError()); | |
390 return -1; | |
391 } | |
392 | |
393 return (PROsfd)file; | |
394 } | |
395 | |
396 PROsfd | |
397 _PR_MD_OPEN_FILE(const char *name, PRIntn osflags, int mode) | |
398 { | |
399 HANDLE file; | |
400 PRInt32 access = 0; | |
401 PRInt32 flags = 0; | |
402 PRInt32 flag6 = 0; | |
403 SECURITY_ATTRIBUTES sa; | |
404 LPSECURITY_ATTRIBUTES lpSA = NULL; | |
405 PSECURITY_DESCRIPTOR pSD = NULL; | |
406 PACL pACL = NULL; | |
407 | |
408 if (osflags & PR_CREATE_FILE) { | |
409 if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable, | |
410 &pSD, &pACL) == PR_SUCCESS) { | |
411 sa.nLength = sizeof(sa); | |
412 sa.lpSecurityDescriptor = pSD; | |
413 sa.bInheritHandle = FALSE; | |
414 lpSA = &sa; | |
415 } | |
416 } | |
417 | |
418 if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH; | |
419 | |
420 if (osflags & PR_RDONLY || osflags & PR_RDWR) | |
421 access |= GENERIC_READ; | |
422 if (osflags & PR_WRONLY || osflags & PR_RDWR) | |
423 access |= GENERIC_WRITE; | |
424 | |
425 if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) | |
426 flags = CREATE_NEW; | |
427 else if (osflags & PR_CREATE_FILE) { | |
428 if (osflags & PR_TRUNCATE) | |
429 flags = CREATE_ALWAYS; | |
430 else | |
431 flags = OPEN_ALWAYS; | |
432 } else { | |
433 if (osflags & PR_TRUNCATE) | |
434 flags = TRUNCATE_EXISTING; | |
435 else | |
436 flags = OPEN_EXISTING; | |
437 } | |
438 | |
439 file = CreateFileA(name, | |
440 access, | |
441 FILE_SHARE_READ|FILE_SHARE_WRITE, | |
442 lpSA, | |
443 flags, | |
444 flag6, | |
445 NULL); | |
446 if (lpSA != NULL) { | |
447 _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); | |
448 } | |
449 if (file == INVALID_HANDLE_VALUE) { | |
450 _PR_MD_MAP_OPEN_ERROR(GetLastError()); | |
451 return -1; | |
452 } | |
453 | |
454 return (PROsfd)file; | |
455 } | |
456 | |
457 PRInt32 | |
458 _PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len) | |
459 { | |
460 PRUint32 bytes; | |
461 int rv, err; | |
462 | |
463 rv = ReadFile((HANDLE)fd->secret->md.osfd, | |
464 (LPVOID)buf, | |
465 len, | |
466 &bytes, | |
467 NULL); | |
468 | |
469 if (rv == 0) | |
470 { | |
471 err = GetLastError(); | |
472 /* ERROR_HANDLE_EOF can only be returned by async io */ | |
473 PR_ASSERT(err != ERROR_HANDLE_EOF); | |
474 if (err == ERROR_BROKEN_PIPE) | |
475 return 0; | |
476 else { | |
477 _PR_MD_MAP_READ_ERROR(err); | |
478 return -1; | |
479 } | |
480 } | |
481 return bytes; | |
482 } | |
483 | |
484 PRInt32 | |
485 _PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 len) | |
486 { | |
487 PROsfd f = fd->secret->md.osfd; | |
488 PRInt32 bytes; | |
489 int rv; | |
490 | |
491 rv = WriteFile((HANDLE)f, | |
492 buf, | |
493 len, | |
494 &bytes, | |
495 NULL ); | |
496 | |
497 if (rv == 0) | |
498 { | |
499 _PR_MD_MAP_WRITE_ERROR(GetLastError()); | |
500 return -1; | |
501 } | |
502 return bytes; | |
503 } /* --- end _PR_MD_WRITE() --- */ | |
504 | |
505 PROffset32 | |
506 _PR_MD_LSEEK(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence) | |
507 { | |
508 DWORD moveMethod; | |
509 PROffset32 rv; | |
510 | |
511 switch (whence) { | |
512 case PR_SEEK_SET: | |
513 moveMethod = FILE_BEGIN; | |
514 break; | |
515 case PR_SEEK_CUR: | |
516 moveMethod = FILE_CURRENT; | |
517 break; | |
518 case PR_SEEK_END: | |
519 moveMethod = FILE_END; | |
520 break; | |
521 default: | |
522 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); | |
523 return -1; | |
524 } | |
525 | |
526 rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, NULL, moveMethod); | |
527 | |
528 /* | |
529 * If the lpDistanceToMoveHigh argument (third argument) is | |
530 * NULL, SetFilePointer returns 0xffffffff on failure. | |
531 */ | |
532 if (-1 == rv) { | |
533 _PR_MD_MAP_LSEEK_ERROR(GetLastError()); | |
534 } | |
535 return rv; | |
536 } | |
537 | |
538 PROffset64 | |
539 _PR_MD_LSEEK64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence) | |
540 { | |
541 DWORD moveMethod; | |
542 LARGE_INTEGER li; | |
543 DWORD err; | |
544 | |
545 switch (whence) { | |
546 case PR_SEEK_SET: | |
547 moveMethod = FILE_BEGIN; | |
548 break; | |
549 case PR_SEEK_CUR: | |
550 moveMethod = FILE_CURRENT; | |
551 break; | |
552 case PR_SEEK_END: | |
553 moveMethod = FILE_END; | |
554 break; | |
555 default: | |
556 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); | |
557 return -1; | |
558 } | |
559 | |
560 li.QuadPart = offset; | |
561 li.LowPart = SetFilePointer((HANDLE)fd->secret->md.osfd, | |
562 li.LowPart, &li.HighPart, moveMethod); | |
563 | |
564 if (0xffffffff == li.LowPart && (err = GetLastError()) != NO_ERROR) { | |
565 _PR_MD_MAP_LSEEK_ERROR(err); | |
566 li.QuadPart = -1; | |
567 } | |
568 return li.QuadPart; | |
569 } | |
570 | |
571 /* | |
572 * This is documented to succeed on read-only files, but Win32's | |
573 * FlushFileBuffers functions fails with "access denied" in such a | |
574 * case. So we only signal an error if the error is *not* "access | |
575 * denied". | |
576 */ | |
577 PRInt32 | |
578 _PR_MD_FSYNC(PRFileDesc *fd) | |
579 { | |
580 /* | |
581 * From the documentation: | |
582 * | |
583 * On Windows NT, the function FlushFileBuffers fails if hFile | |
584 * is a handle to console output. That is because console | |
585 * output is not buffered. The function returns FALSE, and | |
586 * GetLastError returns ERROR_INVALID_HANDLE. | |
587 * | |
588 * On the other hand, on Win95, it returns without error. I cannot | |
589 * assume that 0, 1, and 2 are console, because if someone closes | |
590 * System.out and then opens a file, they might get file descriptor | |
591 * 1. An error on *that* version of 1 should be reported, whereas | |
592 * an error on System.out (which was the original 1) should be | |
593 * ignored. So I use isatty() to ensure that such an error was due | |
594 * to this bogosity, and if it was, I ignore the error. | |
595 */ | |
596 | |
597 BOOL ok = FlushFileBuffers((HANDLE)fd->secret->md.osfd); | |
598 | |
599 if (!ok) { | |
600 DWORD err = GetLastError(); | |
601 if (err != ERROR_ACCESS_DENIED) { // from winerror.h | |
602 _PR_MD_MAP_FSYNC_ERROR(err); | |
603 return -1; | |
604 } | |
605 } | |
606 return 0; | |
607 } | |
608 | |
609 PRInt32 | |
610 _MD_CloseFile(PROsfd osfd) | |
611 { | |
612 PRInt32 rv; | |
613 | |
614 rv = (CloseHandle((HANDLE)osfd))?0:-1; | |
615 if (rv == -1) | |
616 _PR_MD_MAP_CLOSE_ERROR(GetLastError()); | |
617 return rv; | |
618 } | |
619 | |
620 | |
621 /* --- DIR IO ------------------------------------------------------------ */ | |
622 #define GetFileFromDIR(d) (d)->d_entry.cFileName | |
623 #define FileIsHidden(d) ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) | |
624 | |
625 static void FlipSlashes(char *cp, size_t len) | |
626 { | |
627 while (len-- > 0) { | |
628 if (cp[0] == '/') { | |
629 cp[0] = PR_DIRECTORY_SEPARATOR; | |
630 } | |
631 cp = _mbsinc(cp); | |
632 } | |
633 } /* end FlipSlashes() */ | |
634 | |
635 | |
636 /* | |
637 ** | |
638 ** Local implementations of standard Unix RTL functions which are not provided | |
639 ** by the VC RTL. | |
640 ** | |
641 */ | |
642 | |
643 PRInt32 | |
644 _PR_MD_CLOSE_DIR(_MDDir *d) | |
645 { | |
646 if ( d ) { | |
647 if (FindClose(d->d_hdl)) { | |
648 d->magic = (PRUint32)-1; | |
649 return 0; | |
650 } else { | |
651 _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError()); | |
652 return -1; | |
653 } | |
654 } | |
655 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); | |
656 return -1; | |
657 } | |
658 | |
659 | |
660 PRStatus | |
661 _PR_MD_OPEN_DIR(_MDDir *d, const char *name) | |
662 { | |
663 char filename[ MAX_PATH ]; | |
664 size_t len; | |
665 | |
666 len = strlen(name); | |
667 /* Need 5 bytes for \*.* and the trailing null byte. */ | |
668 if (len + 5 > MAX_PATH) { | |
669 PR_SetError(PR_NAME_TOO_LONG_ERROR, 0); | |
670 return PR_FAILURE; | |
671 } | |
672 strcpy(filename, name); | |
673 | |
674 /* | |
675 * If 'name' ends in a slash or backslash, do not append | |
676 * another backslash. | |
677 */ | |
678 if (IsPrevCharSlash(filename, filename + len)) { | |
679 len--; | |
680 } | |
681 strcpy(&filename[len], "\\*.*"); | |
682 FlipSlashes( filename, strlen(filename) ); | |
683 | |
684 d->d_hdl = FindFirstFileA( filename, &(d->d_entry) ); | |
685 if ( d->d_hdl == INVALID_HANDLE_VALUE ) { | |
686 _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); | |
687 return PR_FAILURE; | |
688 } | |
689 d->firstEntry = PR_TRUE; | |
690 d->magic = _MD_MAGIC_DIR; | |
691 return PR_SUCCESS; | |
692 } | |
693 | |
694 char * | |
695 _PR_MD_READ_DIR(_MDDir *d, PRIntn flags) | |
696 { | |
697 PRInt32 err; | |
698 BOOL rv; | |
699 char *fileName; | |
700 | |
701 if ( d ) { | |
702 while (1) { | |
703 if (d->firstEntry) { | |
704 d->firstEntry = PR_FALSE; | |
705 rv = 1; | |
706 } else { | |
707 rv = FindNextFileA(d->d_hdl, &(d->d_entry)); | |
708 } | |
709 if (rv == 0) { | |
710 break; | |
711 } | |
712 fileName = GetFileFromDIR(d); | |
713 if ( (flags & PR_SKIP_DOT) && | |
714 (fileName[0] == '.') && (fileName[1] == '\0')) | |
715 continue; | |
716 if ( (flags & PR_SKIP_DOT_DOT) && | |
717 (fileName[0] == '.') && (fileName[1] == '.') && | |
718 (fileName[2] == '\0')) | |
719 continue; | |
720 if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d)) | |
721 continue; | |
722 return fileName; | |
723 } | |
724 err = GetLastError(); | |
725 PR_ASSERT(NO_ERROR != err); | |
726 _PR_MD_MAP_READDIR_ERROR(err); | |
727 return NULL; | |
728 } | |
729 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); | |
730 return NULL; | |
731 } | |
732 | |
733 PRInt32 | |
734 _PR_MD_DELETE(const char *name) | |
735 { | |
736 if (DeleteFileA(name)) { | |
737 return 0; | |
738 } else { | |
739 _PR_MD_MAP_DELETE_ERROR(GetLastError()); | |
740 return -1; | |
741 } | |
742 } | |
743 | |
744 void | |
745 _PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm) | |
746 { | |
747 PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime)); | |
748 CopyMemory(prtm, filetime, sizeof(PRTime)); | |
749 #if defined(__MINGW32__) | |
750 *prtm = (*prtm - _pr_filetime_offset) / 10LL; | |
751 #else | |
752 *prtm = (*prtm - _pr_filetime_offset) / 10i64; | |
753 #endif | |
754 | |
755 #ifdef DEBUG | |
756 /* Doublecheck our calculation. */ | |
757 { | |
758 SYSTEMTIME systime; | |
759 PRExplodedTime etm; | |
760 PRTime cmp; /* for comparison */ | |
761 BOOL rv; | |
762 | |
763 rv = FileTimeToSystemTime(filetime, &systime); | |
764 PR_ASSERT(0 != rv); | |
765 | |
766 /* | |
767 * PR_ImplodeTime ignores wday and yday. | |
768 */ | |
769 etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC; | |
770 etm.tm_sec = systime.wSecond; | |
771 etm.tm_min = systime.wMinute; | |
772 etm.tm_hour = systime.wHour; | |
773 etm.tm_mday = systime.wDay; | |
774 etm.tm_month = systime.wMonth - 1; | |
775 etm.tm_year = systime.wYear; | |
776 /* | |
777 * It is not well-documented what time zone the FILETIME's | |
778 * are in. WIN32_FIND_DATA is documented to be in UTC (GMT). | |
779 * But BY_HANDLE_FILE_INFORMATION is unclear about this. | |
780 * By our best judgement, we assume that FILETIME is in UTC. | |
781 */ | |
782 etm.tm_params.tp_gmt_offset = 0; | |
783 etm.tm_params.tp_dst_offset = 0; | |
784 cmp = PR_ImplodeTime(&etm); | |
785 | |
786 /* | |
787 * SYSTEMTIME is in milliseconds precision, so we convert PRTime's | |
788 * microseconds to milliseconds before doing the comparison. | |
789 */ | |
790 PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC)); | |
791 } | |
792 #endif /* DEBUG */ | |
793 } | |
794 | |
795 PRInt32 | |
796 _PR_MD_STAT(const char *fn, struct stat *info) | |
797 { | |
798 #ifdef WINCE | |
799 // needs work. dft | |
800 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); | |
801 return -1; | |
802 #else | |
803 PRInt32 rv; | |
804 | |
805 rv = _stat(fn, (struct _stat *)info); | |
806 if (-1 == rv) { | |
807 /* | |
808 * Check for MSVC runtime library _stat() bug. | |
809 * (It's really a bug in FindFirstFile().) | |
810 * If a pathname ends in a backslash or slash, | |
811 * e.g., c:\temp\ or c:/temp/, _stat() will fail. | |
812 * Note: a pathname ending in a slash (e.g., c:/temp/) | |
813 * can be handled by _stat() on NT but not on Win95. | |
814 * | |
815 * We remove the backslash or slash at the end and | |
816 * try again. | |
817 */ | |
818 | |
819 size_t len = strlen(fn); | |
820 if (len > 0 && len <= _MAX_PATH | |
821 && IsPrevCharSlash(fn, fn + len)) { | |
822 char newfn[_MAX_PATH + 1]; | |
823 | |
824 strcpy(newfn, fn); | |
825 newfn[len - 1] = '\0'; | |
826 rv = _stat(newfn, (struct _stat *)info); | |
827 } | |
828 } | |
829 | |
830 if (-1 == rv) { | |
831 _PR_MD_MAP_STAT_ERROR(errno); | |
832 } | |
833 return rv; | |
834 #endif | |
835 } | |
836 | |
837 #define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\') | |
838 | |
839 static PRBool | |
840 IsPrevCharSlash(const char *str, const char *current) | |
841 { | |
842 const char *prev; | |
843 | |
844 if (str >= current) | |
845 return PR_FALSE; | |
846 prev = _mbsdec(str, current); | |
847 return (prev == current - 1) && _PR_IS_SLASH(*prev); | |
848 } | |
849 | |
850 /* | |
851 * IsRootDirectory -- | |
852 * | |
853 * Return PR_TRUE if the pathname 'fn' is a valid root directory, | |
854 * else return PR_FALSE. The char buffer pointed to by 'fn' must | |
855 * be writable. During the execution of this function, the contents | |
856 * of the buffer pointed to by 'fn' may be modified, but on return | |
857 * the original contents will be restored. 'buflen' is the size of | |
858 * the buffer pointed to by 'fn'. | |
859 * | |
860 * Root directories come in three formats: | |
861 * 1. / or \, meaning the root directory of the current drive. | |
862 * 2. C:/ or C:\, where C is a drive letter. | |
863 * 3. \\<server name>\<share point name>\ or | |
864 * \\<server name>\<share point name>, meaning the root directory | |
865 * of a UNC (Universal Naming Convention) name. | |
866 */ | |
867 | |
868 static PRBool | |
869 IsRootDirectory(char *fn, size_t buflen) | |
870 { | |
871 char *p; | |
872 PRBool slashAdded = PR_FALSE; | |
873 PRBool rv = PR_FALSE; | |
874 | |
875 if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') { | |
876 return PR_TRUE; | |
877 } | |
878 | |
879 if (isalpha(fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2]) | |
880 && fn[3] == '\0') { | |
881 rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE; | |
882 return rv; | |
883 } | |
884 | |
885 /* The UNC root directory */ | |
886 | |
887 if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) { | |
888 /* The 'server' part should have at least one character. */ | |
889 p = &fn[2]; | |
890 if (*p == '\0' || _PR_IS_SLASH(*p)) { | |
891 return PR_FALSE; | |
892 } | |
893 | |
894 /* look for the next slash */ | |
895 do { | |
896 p = _mbsinc(p); | |
897 } while (*p != '\0' && !_PR_IS_SLASH(*p)); | |
898 if (*p == '\0') { | |
899 return PR_FALSE; | |
900 } | |
901 | |
902 /* The 'share' part should have at least one character. */ | |
903 p++; | |
904 if (*p == '\0' || _PR_IS_SLASH(*p)) { | |
905 return PR_FALSE; | |
906 } | |
907 | |
908 /* look for the final slash */ | |
909 do { | |
910 p = _mbsinc(p); | |
911 } while (*p != '\0' && !_PR_IS_SLASH(*p)); | |
912 if (_PR_IS_SLASH(*p) && p[1] != '\0') { | |
913 return PR_FALSE; | |
914 } | |
915 if (*p == '\0') { | |
916 /* | |
917 * GetDriveType() doesn't work correctly if the | |
918 * path is of the form \\server\share, so we add | |
919 * a final slash temporarily. | |
920 */ | |
921 if ((p + 1) < (fn + buflen)) { | |
922 *p++ = '\\'; | |
923 *p = '\0'; | |
924 slashAdded = PR_TRUE; | |
925 } else { | |
926 return PR_FALSE; /* name too long */ | |
927 } | |
928 } | |
929 rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE; | |
930 /* restore the 'fn' buffer */ | |
931 if (slashAdded) { | |
932 *--p = '\0'; | |
933 } | |
934 } | |
935 return rv; | |
936 } | |
937 | |
938 #ifndef WINCE | |
939 /* | |
940 * InitGetFileInfo -- | |
941 * | |
942 * Called during IO init. Checks for the existence of the system function | |
943 * GetFileAttributeEx, which when available is used in GETFILEINFO calls. | |
944 * If the routine exists, then the address of the routine is stored in the | |
945 * variable getFileAttributesEx, which will be used to call the routine. | |
946 */ | |
947 static void InitGetFileInfo(void) | |
948 { | |
949 HMODULE module; | |
950 module = GetModuleHandle("Kernel32.dll"); | |
951 if (!module) { | |
952 PR_LOG(_pr_io_lm, PR_LOG_DEBUG, | |
953 ("InitGetFileInfo: GetModuleHandle() failed: %d", | |
954 GetLastError())); | |
955 return; | |
956 } | |
957 | |
958 getFileAttributesEx = (GetFileAttributesExFn) | |
959 GetProcAddress(module, "GetFileAttributesExA"); | |
960 } | |
961 | |
962 /* | |
963 * If GetFileAttributeEx doesn't exist, we call FindFirstFile as a | |
964 * fallback. | |
965 */ | |
966 static BOOL | |
967 GetFileAttributesExFB(const char *fn, WIN32_FIND_DATA *findFileData) | |
968 { | |
969 HANDLE hFindFile; | |
970 | |
971 /* | |
972 * FindFirstFile() expands wildcard characters. So | |
973 * we make sure the pathname contains no wildcard. | |
974 */ | |
975 if (NULL != _mbspbrk(fn, "?*")) { | |
976 SetLastError(ERROR_INVALID_NAME); | |
977 return FALSE; | |
978 } | |
979 | |
980 hFindFile = FindFirstFile(fn, findFileData); | |
981 if (INVALID_HANDLE_VALUE == hFindFile) { | |
982 DWORD len; | |
983 char *filePart; | |
984 char pathbuf[MAX_PATH + 1]; | |
985 | |
986 /* | |
987 * FindFirstFile() does not work correctly on root directories. | |
988 * It also doesn't work correctly on a pathname that ends in a | |
989 * slash. So we first check to see if the pathname specifies a | |
990 * root directory. If not, and if the pathname ends in a slash, | |
991 * we remove the final slash and try again. | |
992 */ | |
993 | |
994 /* | |
995 * If the pathname does not contain ., \, and /, it cannot be | |
996 * a root directory or a pathname that ends in a slash. | |
997 */ | |
998 if (NULL == _mbspbrk(fn, ".\\/")) { | |
999 return FALSE; | |
1000 } | |
1001 len = GetFullPathName(fn, sizeof(pathbuf), pathbuf, | |
1002 &filePart); | |
1003 if (0 == len) { | |
1004 return FALSE; | |
1005 } | |
1006 if (len > sizeof(pathbuf)) { | |
1007 SetLastError(ERROR_FILENAME_EXCED_RANGE); | |
1008 return FALSE; | |
1009 } | |
1010 if (IsRootDirectory(pathbuf, sizeof(pathbuf))) { | |
1011 findFileData->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; | |
1012 /* The file size doesn't have a meaning for directories. */ | |
1013 findFileData->nFileSizeHigh = 0; | |
1014 findFileData->nFileSizeLow = 0; | |
1015 /* | |
1016 * For a directory, these timestamps all specify when the | |
1017 * directory is created. The creation time doesn't make | |
1018 * sense for root directories, so we set it to (NSPR) time 0. | |
1019 */ | |
1020 memcpy(&findFileData->ftCreationTime, &_pr_filetime_offset, 8); | |
1021 findFileData->ftLastAccessTime = findFileData->ftCreationTime; | |
1022 findFileData->ftLastWriteTime = findFileData->ftCreationTime; | |
1023 return TRUE; | |
1024 } | |
1025 if (!IsPrevCharSlash(pathbuf, pathbuf + len)) { | |
1026 return FALSE; | |
1027 } else { | |
1028 pathbuf[len - 1] = '\0'; | |
1029 hFindFile = FindFirstFile(pathbuf, findFileData); | |
1030 if (INVALID_HANDLE_VALUE == hFindFile) { | |
1031 return FALSE; | |
1032 } | |
1033 } | |
1034 } | |
1035 | |
1036 FindClose(hFindFile); | |
1037 return TRUE; | |
1038 } | |
1039 #endif | |
1040 | |
1041 PRInt32 | |
1042 _PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info) | |
1043 { | |
1044 #ifdef WINCE | |
1045 WIN32_FILE_ATTRIBUTE_DATA findFileData; | |
1046 #else | |
1047 WIN32_FIND_DATA findFileData; | |
1048 #endif | |
1049 BOOL rv; | |
1050 | |
1051 if (NULL == fn || '\0' == *fn) { | |
1052 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); | |
1053 return -1; | |
1054 } | |
1055 | |
1056 #ifdef WINCE | |
1057 rv = GetFileAttributesExA(fn, GetFileExInfoStandard, &findFileData); | |
1058 #else | |
1059 /* GetFileAttributesEx is supported on Win 2K and up. */ | |
1060 if (getFileAttributesEx) { | |
1061 rv = getFileAttributesEx(fn, GetFileExInfoStandard, &findFileData); | |
1062 } else { | |
1063 rv = GetFileAttributesExFB(fn, &findFileData); | |
1064 } | |
1065 #endif | |
1066 if (!rv) { | |
1067 _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); | |
1068 return -1; | |
1069 } | |
1070 | |
1071 if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { | |
1072 info->type = PR_FILE_DIRECTORY; | |
1073 } else { | |
1074 info->type = PR_FILE_FILE; | |
1075 } | |
1076 | |
1077 info->size = findFileData.nFileSizeHigh; | |
1078 info->size = (info->size << 32) + findFileData.nFileSizeLow; | |
1079 | |
1080 _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime); | |
1081 | |
1082 if (0 == findFileData.ftCreationTime.dwLowDateTime && | |
1083 0 == findFileData.ftCreationTime.dwHighDateTime) { | |
1084 info->creationTime = info->modifyTime; | |
1085 } else { | |
1086 _PR_FileTimeToPRTime(&findFileData.ftCreationTime, | |
1087 &info->creationTime); | |
1088 } | |
1089 | |
1090 return 0; | |
1091 } | |
1092 | |
1093 PRInt32 | |
1094 _PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info) | |
1095 { | |
1096 PRFileInfo64 info64; | |
1097 PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64); | |
1098 if (0 == rv) | |
1099 { | |
1100 info->type = info64.type; | |
1101 info->size = (PRUint32) info64.size; | |
1102 info->modifyTime = info64.modifyTime; | |
1103 info->creationTime = info64.creationTime; | |
1104 } | |
1105 return rv; | |
1106 } | |
1107 | |
1108 PRInt32 | |
1109 _PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info) | |
1110 { | |
1111 int rv; | |
1112 | |
1113 BY_HANDLE_FILE_INFORMATION hinfo; | |
1114 | |
1115 rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo); | |
1116 if (rv == FALSE) { | |
1117 _PR_MD_MAP_FSTAT_ERROR(GetLastError()); | |
1118 return -1; | |
1119 } | |
1120 | |
1121 if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) | |
1122 info->type = PR_FILE_DIRECTORY; | |
1123 else | |
1124 info->type = PR_FILE_FILE; | |
1125 | |
1126 info->size = hinfo.nFileSizeHigh; | |
1127 info->size = (info->size << 32) + hinfo.nFileSizeLow; | |
1128 | |
1129 _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) ); | |
1130 _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) ); | |
1131 | |
1132 return 0; | |
1133 } | |
1134 | |
1135 PRInt32 | |
1136 _PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info) | |
1137 { | |
1138 PRFileInfo64 info64; | |
1139 int rv = _PR_MD_GETOPENFILEINFO64(fd, &info64); | |
1140 if (0 == rv) | |
1141 { | |
1142 info->type = info64.type; | |
1143 info->modifyTime = info64.modifyTime; | |
1144 info->creationTime = info64.creationTime; | |
1145 LL_L2I(info->size, info64.size); | |
1146 } | |
1147 return rv; | |
1148 } | |
1149 | |
1150 PRStatus | |
1151 _PR_MD_SET_FD_INHERITABLE(PRFileDesc *fd, PRBool inheritable) | |
1152 { | |
1153 #ifdef WINCE | |
1154 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); | |
1155 return PR_FAILURE; | |
1156 #else | |
1157 BOOL rv; | |
1158 | |
1159 /* | |
1160 * The SetHandleInformation function fails with the | |
1161 * ERROR_CALL_NOT_IMPLEMENTED error on Win95. | |
1162 */ | |
1163 rv = SetHandleInformation( | |
1164 (HANDLE)fd->secret->md.osfd, | |
1165 HANDLE_FLAG_INHERIT, | |
1166 inheritable ? HANDLE_FLAG_INHERIT : 0); | |
1167 if (0 == rv) { | |
1168 _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); | |
1169 return PR_FAILURE; | |
1170 } | |
1171 return PR_SUCCESS; | |
1172 #endif | |
1173 } | |
1174 | |
1175 void | |
1176 _PR_MD_INIT_FD_INHERITABLE(PRFileDesc *fd, PRBool imported) | |
1177 { | |
1178 if (imported) { | |
1179 fd->secret->inheritable = _PR_TRI_UNKNOWN; | |
1180 } else { | |
1181 fd->secret->inheritable = _PR_TRI_FALSE; | |
1182 } | |
1183 } | |
1184 | |
1185 void | |
1186 _PR_MD_QUERY_FD_INHERITABLE(PRFileDesc *fd) | |
1187 { | |
1188 #ifdef WINCE | |
1189 fd->secret->inheritable = _PR_TRI_FALSE; | |
1190 #else | |
1191 DWORD flags; | |
1192 | |
1193 PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable); | |
1194 if (GetHandleInformation((HANDLE)fd->secret->md.osfd, &flags)) { | |
1195 if (flags & HANDLE_FLAG_INHERIT) { | |
1196 fd->secret->inheritable = _PR_TRI_TRUE; | |
1197 } else { | |
1198 fd->secret->inheritable = _PR_TRI_FALSE; | |
1199 } | |
1200 } | |
1201 #endif | |
1202 } | |
1203 | |
1204 PRInt32 | |
1205 _PR_MD_RENAME(const char *from, const char *to) | |
1206 { | |
1207 /* Does this work with dot-relative pathnames? */ | |
1208 if (MoveFileA(from, to)) { | |
1209 return 0; | |
1210 } else { | |
1211 _PR_MD_MAP_RENAME_ERROR(GetLastError()); | |
1212 return -1; | |
1213 } | |
1214 } | |
1215 | |
1216 PRInt32 | |
1217 _PR_MD_ACCESS(const char *name, PRAccessHow how) | |
1218 { | |
1219 #ifdef WINCE | |
1220 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); | |
1221 return -1; | |
1222 #else | |
1223 PRInt32 rv; | |
1224 switch (how) { | |
1225 case PR_ACCESS_WRITE_OK: | |
1226 rv = _access(name, 02); | |
1227 break; | |
1228 case PR_ACCESS_READ_OK: | |
1229 rv = _access(name, 04); | |
1230 break; | |
1231 case PR_ACCESS_EXISTS: | |
1232 return _access(name, 00); | |
1233 break; | |
1234 default: | |
1235 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); | |
1236 return -1; | |
1237 } | |
1238 if (rv < 0) | |
1239 _PR_MD_MAP_ACCESS_ERROR(errno); | |
1240 return rv; | |
1241 #endif | |
1242 } | |
1243 | |
1244 PRInt32 | |
1245 _PR_MD_MKDIR(const char *name, PRIntn mode) | |
1246 { | |
1247 /* XXXMB - how to translate the "mode"??? */ | |
1248 if (CreateDirectoryA(name, NULL)) { | |
1249 return 0; | |
1250 } else { | |
1251 _PR_MD_MAP_MKDIR_ERROR(GetLastError()); | |
1252 return -1; | |
1253 } | |
1254 } | |
1255 | |
1256 PRInt32 | |
1257 _PR_MD_MAKE_DIR(const char *name, PRIntn mode) | |
1258 { | |
1259 BOOL rv; | |
1260 SECURITY_ATTRIBUTES sa; | |
1261 LPSECURITY_ATTRIBUTES lpSA = NULL; | |
1262 PSECURITY_DESCRIPTOR pSD = NULL; | |
1263 PACL pACL = NULL; | |
1264 | |
1265 if (_PR_NT_MakeSecurityDescriptorACL(mode, dirAccessTable, | |
1266 &pSD, &pACL) == PR_SUCCESS) { | |
1267 sa.nLength = sizeof(sa); | |
1268 sa.lpSecurityDescriptor = pSD; | |
1269 sa.bInheritHandle = FALSE; | |
1270 lpSA = &sa; | |
1271 } | |
1272 rv = CreateDirectoryA(name, lpSA); | |
1273 if (lpSA != NULL) { | |
1274 _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); | |
1275 } | |
1276 if (rv) { | |
1277 return 0; | |
1278 } else { | |
1279 _PR_MD_MAP_MKDIR_ERROR(GetLastError()); | |
1280 return -1; | |
1281 } | |
1282 } | |
1283 | |
1284 PRInt32 | |
1285 _PR_MD_RMDIR(const char *name) | |
1286 { | |
1287 if (RemoveDirectoryA(name)) { | |
1288 return 0; | |
1289 } else { | |
1290 _PR_MD_MAP_RMDIR_ERROR(GetLastError()); | |
1291 return -1; | |
1292 } | |
1293 } | |
1294 | |
1295 PRStatus | |
1296 _PR_MD_LOCKFILE(PROsfd f) | |
1297 { | |
1298 PRStatus rc = PR_SUCCESS; | |
1299 DWORD rv; | |
1300 | |
1301 rv = LockFile( (HANDLE)f, | |
1302 0l, 0l, | |
1303 0x0l, 0xffffffffl ); | |
1304 if ( rv == 0 ) { | |
1305 DWORD rc = GetLastError(); | |
1306 PR_LOG( _pr_io_lm, PR_LOG_ERROR, | |
1307 ("_PR_MD_LOCKFILE() failed. Error: %d", rc )); | |
1308 rc = PR_FAILURE; | |
1309 } | |
1310 | |
1311 return rc; | |
1312 } /* end _PR_MD_LOCKFILE() */ | |
1313 | |
1314 PRStatus | |
1315 _PR_MD_TLOCKFILE(PROsfd f) | |
1316 { | |
1317 PR_SetError( PR_NOT_IMPLEMENTED_ERROR, 0 ); | |
1318 return PR_FAILURE; | |
1319 } /* end _PR_MD_TLOCKFILE() */ | |
1320 | |
1321 | |
1322 PRStatus | |
1323 _PR_MD_UNLOCKFILE(PROsfd f) | |
1324 { | |
1325 PRInt32 rv; | |
1326 | |
1327 rv = UnlockFile( (HANDLE) f, | |
1328 0l, 0l, | |
1329 0x0l, 0xffffffffl ); | |
1330 | |
1331 if ( rv ) | |
1332 { | |
1333 return PR_SUCCESS; | |
1334 } | |
1335 else | |
1336 { | |
1337 _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); | |
1338 return PR_FAILURE; | |
1339 } | |
1340 } /* end _PR_MD_UNLOCKFILE() */ | |
1341 | |
1342 PRInt32 | |
1343 _PR_MD_PIPEAVAILABLE(PRFileDesc *fd) | |
1344 { | |
1345 if (NULL == fd) | |
1346 PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); | |
1347 else | |
1348 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); | |
1349 return -1; | |
1350 } | |
1351 | |
1352 #ifdef MOZ_UNICODE | |
1353 | |
1354 typedef HANDLE (WINAPI *CreateFileWFn) (LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIB
UTES, DWORD, DWORD, HANDLE); | |
1355 static CreateFileWFn createFileW = CreateFileW; | |
1356 typedef HANDLE (WINAPI *FindFirstFileWFn) (LPCWSTR, LPWIN32_FIND_DATAW); | |
1357 static FindFirstFileWFn findFirstFileW = FindFirstFileW; | |
1358 typedef BOOL (WINAPI *FindNextFileWFn) (HANDLE, LPWIN32_FIND_DATAW); | |
1359 static FindNextFileWFn findNextFileW = FindNextFileW; | |
1360 typedef DWORD (WINAPI *GetFullPathNameWFn) (LPCWSTR, DWORD, LPWSTR, LPWSTR *); | |
1361 static GetFullPathNameWFn getFullPathNameW = GetFullPathNameW; | |
1362 typedef UINT (WINAPI *GetDriveTypeWFn) (LPCWSTR); | |
1363 static GetDriveTypeWFn getDriveTypeW = GetDriveTypeW; | |
1364 | |
1365 #endif /* MOZ_UNICODE */ | |
1366 | |
1367 PRBool _pr_useUnicode = PR_FALSE; | |
1368 | |
1369 static void InitUnicodeSupport(void) | |
1370 { | |
1371 #ifdef WINCE | |
1372 /* The A functions don't even exist in Windows Mobile. */ | |
1373 _pr_useUnicode = PR_TRUE; | |
1374 #else | |
1375 /* | |
1376 * The W functions exist on Win9x as stubs that fail with the | |
1377 * ERROR_CALL_NOT_IMPLEMENTED error. We plan to emulate the | |
1378 * MSLU W functions on Win9x in the future. | |
1379 */ | |
1380 | |
1381 /* Find out if we are running on a Unicode enabled version of Windows */ | |
1382 OSVERSIONINFOA osvi = {0}; | |
1383 | |
1384 osvi.dwOSVersionInfoSize = sizeof(osvi); | |
1385 if (GetVersionExA(&osvi)) { | |
1386 _pr_useUnicode = (osvi.dwPlatformId >= VER_PLATFORM_WIN32_NT); | |
1387 } else { | |
1388 _pr_useUnicode = PR_FALSE; | |
1389 } | |
1390 #ifdef DEBUG | |
1391 /* | |
1392 * In debug builds, allow explicit use of ANSI methods to simulate | |
1393 * a Win9x environment for testing purposes. | |
1394 */ | |
1395 if (getenv("WINAPI_USE_ANSI")) | |
1396 _pr_useUnicode = PR_FALSE; | |
1397 #endif | |
1398 #endif | |
1399 } | |
1400 | |
1401 #ifdef MOZ_UNICODE | |
1402 | |
1403 /* ================ UTF16 Interfaces ================================ */ | |
1404 static void FlipSlashesW(PRUnichar *cp, size_t len) | |
1405 { | |
1406 while (len-- > 0) { | |
1407 if (cp[0] == L'/') { | |
1408 cp[0] = L'\\'; | |
1409 } | |
1410 cp++; | |
1411 } | |
1412 } /* end FlipSlashesW() */ | |
1413 | |
1414 PROsfd | |
1415 _PR_MD_OPEN_FILE_UTF16(const PRUnichar *name, PRIntn osflags, int mode) | |
1416 { | |
1417 HANDLE file; | |
1418 PRInt32 access = 0; | |
1419 PRInt32 flags = 0; | |
1420 PRInt32 flag6 = 0; | |
1421 SECURITY_ATTRIBUTES sa; | |
1422 LPSECURITY_ATTRIBUTES lpSA = NULL; | |
1423 PSECURITY_DESCRIPTOR pSD = NULL; | |
1424 PACL pACL = NULL; | |
1425 | |
1426 if (osflags & PR_CREATE_FILE) { | |
1427 if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable, | |
1428 &pSD, &pACL) == PR_SUCCESS) { | |
1429 sa.nLength = sizeof(sa); | |
1430 sa.lpSecurityDescriptor = pSD; | |
1431 sa.bInheritHandle = FALSE; | |
1432 lpSA = &sa; | |
1433 } | |
1434 } | |
1435 | |
1436 if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH; | |
1437 | |
1438 if (osflags & PR_RDONLY || osflags & PR_RDWR) | |
1439 access |= GENERIC_READ; | |
1440 if (osflags & PR_WRONLY || osflags & PR_RDWR) | |
1441 access |= GENERIC_WRITE; | |
1442 | |
1443 if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) | |
1444 flags = CREATE_NEW; | |
1445 else if (osflags & PR_CREATE_FILE) { | |
1446 if (osflags & PR_TRUNCATE) | |
1447 flags = CREATE_ALWAYS; | |
1448 else | |
1449 flags = OPEN_ALWAYS; | |
1450 } else { | |
1451 if (osflags & PR_TRUNCATE) | |
1452 flags = TRUNCATE_EXISTING; | |
1453 else | |
1454 flags = OPEN_EXISTING; | |
1455 } | |
1456 | |
1457 file = createFileW(name, | |
1458 access, | |
1459 FILE_SHARE_READ|FILE_SHARE_WRITE, | |
1460 lpSA, | |
1461 flags, | |
1462 flag6, | |
1463 NULL); | |
1464 if (lpSA != NULL) { | |
1465 _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); | |
1466 } | |
1467 if (file == INVALID_HANDLE_VALUE) { | |
1468 _PR_MD_MAP_OPEN_ERROR(GetLastError()); | |
1469 return -1; | |
1470 } | |
1471 | |
1472 return (PROsfd)file; | |
1473 } | |
1474 | |
1475 PRStatus | |
1476 _PR_MD_OPEN_DIR_UTF16(_MDDirUTF16 *d, const PRUnichar *name) | |
1477 { | |
1478 PRUnichar filename[ MAX_PATH ]; | |
1479 int len; | |
1480 | |
1481 len = wcslen(name); | |
1482 /* Need 5 bytes for \*.* and the trailing null byte. */ | |
1483 if (len + 5 > MAX_PATH) { | |
1484 PR_SetError(PR_NAME_TOO_LONG_ERROR, 0); | |
1485 return PR_FAILURE; | |
1486 } | |
1487 wcscpy(filename, name); | |
1488 | |
1489 /* | |
1490 * If 'name' ends in a slash or backslash, do not append | |
1491 * another backslash. | |
1492 */ | |
1493 if (filename[len - 1] == L'/' || filename[len - 1] == L'\\') { | |
1494 len--; | |
1495 } | |
1496 wcscpy(&filename[len], L"\\*.*"); | |
1497 FlipSlashesW( filename, wcslen(filename) ); | |
1498 | |
1499 d->d_hdl = findFirstFileW( filename, &(d->d_entry) ); | |
1500 if ( d->d_hdl == INVALID_HANDLE_VALUE ) { | |
1501 _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); | |
1502 return PR_FAILURE; | |
1503 } | |
1504 d->firstEntry = PR_TRUE; | |
1505 d->magic = _MD_MAGIC_DIR; | |
1506 return PR_SUCCESS; | |
1507 } | |
1508 | |
1509 PRUnichar * | |
1510 _PR_MD_READ_DIR_UTF16(_MDDirUTF16 *d, PRIntn flags) | |
1511 { | |
1512 PRInt32 err; | |
1513 BOOL rv; | |
1514 PRUnichar *fileName; | |
1515 | |
1516 if ( d ) { | |
1517 while (1) { | |
1518 if (d->firstEntry) { | |
1519 d->firstEntry = PR_FALSE; | |
1520 rv = 1; | |
1521 } else { | |
1522 rv = findNextFileW(d->d_hdl, &(d->d_entry)); | |
1523 } | |
1524 if (rv == 0) { | |
1525 break; | |
1526 } | |
1527 fileName = GetFileFromDIR(d); | |
1528 if ( (flags & PR_SKIP_DOT) && | |
1529 (fileName[0] == L'.') && (fileName[1] == L'\0')) | |
1530 continue; | |
1531 if ( (flags & PR_SKIP_DOT_DOT) && | |
1532 (fileName[0] == L'.') && (fileName[1] == L'.') && | |
1533 (fileName[2] == L'\0')) | |
1534 continue; | |
1535 if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d)) | |
1536 continue; | |
1537 return fileName; | |
1538 } | |
1539 err = GetLastError(); | |
1540 PR_ASSERT(NO_ERROR != err); | |
1541 _PR_MD_MAP_READDIR_ERROR(err); | |
1542 return NULL; | |
1543 } | |
1544 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); | |
1545 return NULL; | |
1546 } | |
1547 | |
1548 PRInt32 | |
1549 _PR_MD_CLOSE_DIR_UTF16(_MDDirUTF16 *d) | |
1550 { | |
1551 if ( d ) { | |
1552 if (FindClose(d->d_hdl)) { | |
1553 d->magic = (PRUint32)-1; | |
1554 return 0; | |
1555 } else { | |
1556 _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError()); | |
1557 return -1; | |
1558 } | |
1559 } | |
1560 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); | |
1561 return -1; | |
1562 } | |
1563 | |
1564 #define _PR_IS_W_SLASH(ch) ((ch) == L'/' || (ch) == L'\\') | |
1565 | |
1566 /* | |
1567 * IsRootDirectoryW -- | |
1568 * | |
1569 * Return PR_TRUE if the pathname 'fn' is a valid root directory, | |
1570 * else return PR_FALSE. The PRUnichar buffer pointed to by 'fn' must | |
1571 * be writable. During the execution of this function, the contents | |
1572 * of the buffer pointed to by 'fn' may be modified, but on return | |
1573 * the original contents will be restored. 'buflen' is the size of | |
1574 * the buffer pointed to by 'fn', in PRUnichars. | |
1575 * | |
1576 * Root directories come in three formats: | |
1577 * 1. / or \, meaning the root directory of the current drive. | |
1578 * 2. C:/ or C:\, where C is a drive letter. | |
1579 * 3. \\<server name>\<share point name>\ or | |
1580 * \\<server name>\<share point name>, meaning the root directory | |
1581 * of a UNC (Universal Naming Convention) name. | |
1582 */ | |
1583 | |
1584 static PRBool | |
1585 IsRootDirectoryW(PRUnichar *fn, size_t buflen) | |
1586 { | |
1587 PRUnichar *p; | |
1588 PRBool slashAdded = PR_FALSE; | |
1589 PRBool rv = PR_FALSE; | |
1590 | |
1591 if (_PR_IS_W_SLASH(fn[0]) && fn[1] == L'\0') { | |
1592 return PR_TRUE; | |
1593 } | |
1594 | |
1595 if (iswalpha(fn[0]) && fn[1] == L':' && _PR_IS_W_SLASH(fn[2]) | |
1596 && fn[3] == L'\0') { | |
1597 rv = getDriveTypeW(fn) > 1 ? PR_TRUE : PR_FALSE; | |
1598 return rv; | |
1599 } | |
1600 | |
1601 /* The UNC root directory */ | |
1602 | |
1603 if (_PR_IS_W_SLASH(fn[0]) && _PR_IS_W_SLASH(fn[1])) { | |
1604 /* The 'server' part should have at least one character. */ | |
1605 p = &fn[2]; | |
1606 if (*p == L'\0' || _PR_IS_W_SLASH(*p)) { | |
1607 return PR_FALSE; | |
1608 } | |
1609 | |
1610 /* look for the next slash */ | |
1611 do { | |
1612 p++; | |
1613 } while (*p != L'\0' && !_PR_IS_W_SLASH(*p)); | |
1614 if (*p == L'\0') { | |
1615 return PR_FALSE; | |
1616 } | |
1617 | |
1618 /* The 'share' part should have at least one character. */ | |
1619 p++; | |
1620 if (*p == L'\0' || _PR_IS_W_SLASH(*p)) { | |
1621 return PR_FALSE; | |
1622 } | |
1623 | |
1624 /* look for the final slash */ | |
1625 do { | |
1626 p++; | |
1627 } while (*p != L'\0' && !_PR_IS_W_SLASH(*p)); | |
1628 if (_PR_IS_W_SLASH(*p) && p[1] != L'\0') { | |
1629 return PR_FALSE; | |
1630 } | |
1631 if (*p == L'\0') { | |
1632 /* | |
1633 * GetDriveType() doesn't work correctly if the | |
1634 * path is of the form \\server\share, so we add | |
1635 * a final slash temporarily. | |
1636 */ | |
1637 if ((p + 1) < (fn + buflen)) { | |
1638 *p++ = L'\\'; | |
1639 *p = L'\0'; | |
1640 slashAdded = PR_TRUE; | |
1641 } else { | |
1642 return PR_FALSE; /* name too long */ | |
1643 } | |
1644 } | |
1645 rv = getDriveTypeW(fn) > 1 ? PR_TRUE : PR_FALSE; | |
1646 /* restore the 'fn' buffer */ | |
1647 if (slashAdded) { | |
1648 *--p = L'\0'; | |
1649 } | |
1650 } | |
1651 return rv; | |
1652 } | |
1653 | |
1654 PRInt32 | |
1655 _PR_MD_GETFILEINFO64_UTF16(const PRUnichar *fn, PRFileInfo64 *info) | |
1656 { | |
1657 HANDLE hFindFile; | |
1658 WIN32_FIND_DATAW findFileData; | |
1659 PRUnichar pathbuf[MAX_PATH + 1]; | |
1660 | |
1661 if (NULL == fn || L'\0' == *fn) { | |
1662 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); | |
1663 return -1; | |
1664 } | |
1665 | |
1666 /* | |
1667 * FindFirstFile() expands wildcard characters. So | |
1668 * we make sure the pathname contains no wildcard. | |
1669 */ | |
1670 if (NULL != wcspbrk(fn, L"?*")) { | |
1671 PR_SetError(PR_FILE_NOT_FOUND_ERROR, 0); | |
1672 return -1; | |
1673 } | |
1674 | |
1675 hFindFile = findFirstFileW(fn, &findFileData); | |
1676 if (INVALID_HANDLE_VALUE == hFindFile) { | |
1677 DWORD len; | |
1678 PRUnichar *filePart; | |
1679 | |
1680 /* | |
1681 * FindFirstFile() does not work correctly on root directories. | |
1682 * It also doesn't work correctly on a pathname that ends in a | |
1683 * slash. So we first check to see if the pathname specifies a | |
1684 * root directory. If not, and if the pathname ends in a slash, | |
1685 * we remove the final slash and try again. | |
1686 */ | |
1687 | |
1688 /* | |
1689 * If the pathname does not contain ., \, and /, it cannot be | |
1690 * a root directory or a pathname that ends in a slash. | |
1691 */ | |
1692 if (NULL == wcspbrk(fn, L".\\/")) { | |
1693 _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); | |
1694 return -1; | |
1695 } | |
1696 len = getFullPathNameW(fn, sizeof(pathbuf)/sizeof(pathbuf[0]), pathbuf, | |
1697 &filePart); | |
1698 if (0 == len) { | |
1699 _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); | |
1700 return -1; | |
1701 } | |
1702 if (len > sizeof(pathbuf)/sizeof(pathbuf[0])) { | |
1703 PR_SetError(PR_NAME_TOO_LONG_ERROR, 0); | |
1704 return -1; | |
1705 } | |
1706 if (IsRootDirectoryW(pathbuf, sizeof(pathbuf)/sizeof(pathbuf[0]))) { | |
1707 info->type = PR_FILE_DIRECTORY; | |
1708 info->size = 0; | |
1709 /* | |
1710 * These timestamps don't make sense for root directories. | |
1711 */ | |
1712 info->modifyTime = 0; | |
1713 info->creationTime = 0; | |
1714 return 0; | |
1715 } | |
1716 if (!_PR_IS_W_SLASH(pathbuf[len - 1])) { | |
1717 _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); | |
1718 return -1; | |
1719 } else { | |
1720 pathbuf[len - 1] = L'\0'; | |
1721 hFindFile = findFirstFileW(pathbuf, &findFileData); | |
1722 if (INVALID_HANDLE_VALUE == hFindFile) { | |
1723 _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); | |
1724 return -1; | |
1725 } | |
1726 } | |
1727 } | |
1728 | |
1729 FindClose(hFindFile); | |
1730 | |
1731 if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { | |
1732 info->type = PR_FILE_DIRECTORY; | |
1733 } else { | |
1734 info->type = PR_FILE_FILE; | |
1735 } | |
1736 | |
1737 info->size = findFileData.nFileSizeHigh; | |
1738 info->size = (info->size << 32) + findFileData.nFileSizeLow; | |
1739 | |
1740 _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime); | |
1741 | |
1742 if (0 == findFileData.ftCreationTime.dwLowDateTime && | |
1743 0 == findFileData.ftCreationTime.dwHighDateTime) { | |
1744 info->creationTime = info->modifyTime; | |
1745 } else { | |
1746 _PR_FileTimeToPRTime(&findFileData.ftCreationTime, | |
1747 &info->creationTime); | |
1748 } | |
1749 | |
1750 return 0; | |
1751 } | |
1752 /* ================ end of UTF16 Interfaces ================================ */ | |
1753 #endif /* MOZ_UNICODE */ | |
OLD | NEW |