VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/fileio-win.cpp@ 82834

最後變更 在這個檔案從82834是 82834,由 vboxsync 提交於 5 年 前

IPRT/win: Reimplemented RTFileSetSize to support truncating files opened with RTFILE_O_APPEND to assist shared folders with POSIX O_APPEND semantics on windows hosts. ticketref:19003

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Id Revision
檔案大小: 48.5 KB
 
1/* $Id: fileio-win.cpp 82834 2020-01-22 16:25:19Z vboxsync $ */
2/** @file
3 * IPRT - File I/O, native implementation for the Windows host platform.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_DIR
32#ifndef _WIN32_WINNT
33# define _WIN32_WINNT 0x0500
34#endif
35#include <iprt/nt/nt-and-windows.h>
36
37#include <iprt/file.h>
38
39#include <iprt/asm.h>
40#include <iprt/assert.h>
41#include <iprt/path.h>
42#include <iprt/string.h>
43#include <iprt/err.h>
44#include <iprt/ldr.h>
45#include <iprt/log.h>
46#include <iprt/utf16.h>
47#include "internal/file.h"
48#include "internal/fs.h"
49#include "internal/path.h"
50#include "internal-r3-win.h" /* For g_enmWinVer + kRTWinOSType_XXX */
51
52
53/*********************************************************************************************************************************
54* Defined Constants And Macros *
55*********************************************************************************************************************************/
56typedef BOOL WINAPI FNVERIFYCONSOLEIOHANDLE(HANDLE);
57typedef FNVERIFYCONSOLEIOHANDLE *PFNVERIFYCONSOLEIOHANDLE; /* No, nobody fell on the keyboard, really! */
58
59/**
60 * This is wrapper around the ugly SetFilePointer api.
61 *
62 * It's equivalent to SetFilePointerEx which we so unfortunately cannot use because of
63 * it not being present in NT4 GA.
64 *
65 * @returns Success indicator. Extended error information obtainable using GetLastError().
66 * @param hFile Filehandle.
67 * @param offSeek Offset to seek.
68 * @param poffNew Where to store the new file offset. NULL allowed.
69 * @param uMethod Seek method. (The windows one!)
70 */
71DECLINLINE(bool) MySetFilePointer(RTFILE hFile, uint64_t offSeek, uint64_t *poffNew, unsigned uMethod)
72{
73 bool fRc;
74 LARGE_INTEGER off;
75
76 off.QuadPart = offSeek;
77#if 1
78 if (off.LowPart != INVALID_SET_FILE_POINTER)
79 {
80 off.LowPart = SetFilePointer((HANDLE)RTFileToNative(hFile), off.LowPart, &off.HighPart, uMethod);
81 fRc = off.LowPart != INVALID_SET_FILE_POINTER;
82 }
83 else
84 {
85 SetLastError(NO_ERROR);
86 off.LowPart = SetFilePointer((HANDLE)RTFileToNative(hFile), off.LowPart, &off.HighPart, uMethod);
87 fRc = GetLastError() == NO_ERROR;
88 }
89#else
90 fRc = SetFilePointerEx((HANDLE)RTFileToNative(hFile), off, &off, uMethod);
91#endif
92 if (fRc && poffNew)
93 *poffNew = off.QuadPart;
94 return fRc;
95}
96
97
98/**
99 * Helper for checking if a VERR_DISK_FULL isn't a VERR_FILE_TOO_BIG.
100 * @returns VERR_DISK_FULL or VERR_FILE_TOO_BIG.
101 */
102static int rtFileWinCheckIfDiskReallyFull(RTFILE hFile, uint64_t cbDesired)
103{
104 /*
105 * Windows doesn't appear to have a way to query the file size limit of a
106 * file system, so we have to deduce the limit from the file system driver name.
107 * This means it will only work for known file systems.
108 */
109 if (cbDesired >= _2G - 1)
110 {
111 uint64_t cbMaxFile = UINT64_MAX;
112 RTFSTYPE enmFsType;
113 int rc = rtNtQueryFsType((HANDLE)RTFileToNative(hFile), &enmFsType);
114 if (RT_SUCCESS(rc))
115 switch (enmFsType)
116 {
117 case RTFSTYPE_NTFS:
118 case RTFSTYPE_EXFAT:
119 case RTFSTYPE_UDF:
120 cbMaxFile = UINT64_C(0xffffffffffffffff); /* (May be limited by IFS.) */
121 break;
122
123 case RTFSTYPE_ISO9660:
124 cbMaxFile = 8 *_1T;
125 break;
126
127 case RTFSTYPE_FAT:
128 cbMaxFile = _4G;
129 break;
130
131 case RTFSTYPE_HPFS:
132 cbMaxFile = _2G;
133 break;
134
135 default:
136 break;
137 }
138 if (cbDesired >= cbMaxFile)
139 return VERR_FILE_TOO_BIG;
140 }
141 return VERR_DISK_FULL;
142}
143
144
145RTR3DECL(int) RTFileFromNative(PRTFILE pFile, RTHCINTPTR uNative)
146{
147 HANDLE h = (HANDLE)uNative;
148 AssertCompile(sizeof(h) == sizeof(uNative));
149 if (h == INVALID_HANDLE_VALUE)
150 {
151 AssertMsgFailed(("%p\n", uNative));
152 *pFile = NIL_RTFILE;
153 return VERR_INVALID_HANDLE;
154 }
155 *pFile = (RTFILE)h;
156 return VINF_SUCCESS;
157}
158
159
160RTR3DECL(RTHCINTPTR) RTFileToNative(RTFILE hFile)
161{
162 AssertReturn(hFile != NIL_RTFILE, (RTHCINTPTR)INVALID_HANDLE_VALUE);
163 return (RTHCINTPTR)hFile;
164}
165
166
167RTR3DECL(int) RTFileOpen(PRTFILE pFile, const char *pszFilename, uint64_t fOpen)
168{
169 return RTFileOpenEx(pszFilename, fOpen, pFile, NULL);
170}
171
172
173RTDECL(int) RTFileOpenEx(const char *pszFilename, uint64_t fOpen, PRTFILE phFile, PRTFILEACTION penmActionTaken)
174{
175 /*
176 * Validate input.
177 */
178 AssertReturn(phFile, VERR_INVALID_PARAMETER);
179 *phFile = NIL_RTFILE;
180 if (penmActionTaken)
181 *penmActionTaken = RTFILEACTION_INVALID;
182 AssertReturn(pszFilename, VERR_INVALID_PARAMETER);
183
184 /*
185 * Merge forced open flags and validate them.
186 */
187 int rc = rtFileRecalcAndValidateFlags(&fOpen);
188 if (RT_FAILURE(rc))
189 return rc;
190
191 /*
192 * Determine disposition, access, share mode, creation flags, and security attributes
193 * for the CreateFile API call.
194 */
195 DWORD dwCreationDisposition;
196 switch (fOpen & RTFILE_O_ACTION_MASK)
197 {
198 case RTFILE_O_OPEN:
199 dwCreationDisposition = fOpen & RTFILE_O_TRUNCATE ? TRUNCATE_EXISTING : OPEN_EXISTING;
200 break;
201 case RTFILE_O_OPEN_CREATE:
202 dwCreationDisposition = OPEN_ALWAYS;
203 break;
204 case RTFILE_O_CREATE:
205 dwCreationDisposition = CREATE_NEW;
206 break;
207 case RTFILE_O_CREATE_REPLACE:
208 dwCreationDisposition = CREATE_ALWAYS;
209 break;
210 default:
211 AssertMsgFailedReturn(("Impossible fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS);
212 }
213
214 DWORD dwDesiredAccess;
215 switch (fOpen & RTFILE_O_ACCESS_MASK)
216 {
217 case RTFILE_O_READ:
218 dwDesiredAccess = FILE_GENERIC_READ; /* RTFILE_O_APPEND is ignored. */
219 break;
220 case RTFILE_O_WRITE:
221 dwDesiredAccess = fOpen & RTFILE_O_APPEND
222 ? FILE_GENERIC_WRITE & ~FILE_WRITE_DATA
223 : FILE_GENERIC_WRITE;
224 break;
225 case RTFILE_O_READWRITE:
226 dwDesiredAccess = fOpen & RTFILE_O_APPEND
227 ? FILE_GENERIC_READ | (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA)
228 : FILE_GENERIC_READ | FILE_GENERIC_WRITE;
229 break;
230 case RTFILE_O_ATTR_ONLY:
231 if (fOpen & RTFILE_O_ACCESS_ATTR_MASK)
232 {
233 dwDesiredAccess = 0;
234 break;
235 }
236 RT_FALL_THRU();
237 default:
238 AssertMsgFailedReturn(("Impossible fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS);
239 }
240 if (dwCreationDisposition == TRUNCATE_EXISTING)
241 /* Required for truncating the file (see MSDN), it is *NOT* part of FILE_GENERIC_WRITE. */
242 dwDesiredAccess |= GENERIC_WRITE;
243
244 /* RTFileSetMode needs following rights as well. */
245 switch (fOpen & RTFILE_O_ACCESS_ATTR_MASK)
246 {
247 case RTFILE_O_ACCESS_ATTR_READ: dwDesiredAccess |= FILE_READ_ATTRIBUTES | SYNCHRONIZE; break;
248 case RTFILE_O_ACCESS_ATTR_WRITE: dwDesiredAccess |= FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break;
249 case RTFILE_O_ACCESS_ATTR_READWRITE: dwDesiredAccess |= FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break;
250 default:
251 /* Attributes access is the same as the file access. */
252 switch (fOpen & RTFILE_O_ACCESS_MASK)
253 {
254 case RTFILE_O_READ: dwDesiredAccess |= FILE_READ_ATTRIBUTES | SYNCHRONIZE; break;
255 case RTFILE_O_WRITE: dwDesiredAccess |= FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break;
256 case RTFILE_O_READWRITE: dwDesiredAccess |= FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break;
257 default:
258 AssertMsgFailedReturn(("Impossible fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS);
259 }
260 }
261
262 DWORD dwShareMode;
263 switch (fOpen & RTFILE_O_DENY_MASK)
264 {
265 case RTFILE_O_DENY_NONE: dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; break;
266 case RTFILE_O_DENY_READ: dwShareMode = FILE_SHARE_WRITE; break;
267 case RTFILE_O_DENY_WRITE: dwShareMode = FILE_SHARE_READ; break;
268 case RTFILE_O_DENY_READWRITE: dwShareMode = 0; break;
269
270 case RTFILE_O_DENY_NOT_DELETE: dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE; break;
271 case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_READ: dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_WRITE; break;
272 case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_WRITE: dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ; break;
273 case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_READWRITE:dwShareMode = FILE_SHARE_DELETE; break;
274 default:
275 AssertMsgFailedReturn(("Impossible fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS);
276 }
277
278 SECURITY_ATTRIBUTES SecurityAttributes;
279 PSECURITY_ATTRIBUTES pSecurityAttributes = NULL;
280 if (fOpen & RTFILE_O_INHERIT)
281 {
282 SecurityAttributes.nLength = sizeof(SecurityAttributes);
283 SecurityAttributes.lpSecurityDescriptor = NULL;
284 SecurityAttributes.bInheritHandle = TRUE;
285 pSecurityAttributes = &SecurityAttributes;
286 }
287
288 DWORD dwFlagsAndAttributes;
289 dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
290 if (fOpen & RTFILE_O_WRITE_THROUGH)
291 dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;
292 if (fOpen & RTFILE_O_ASYNC_IO)
293 dwFlagsAndAttributes |= FILE_FLAG_OVERLAPPED;
294 if (fOpen & RTFILE_O_NO_CACHE)
295 {
296 dwFlagsAndAttributes |= FILE_FLAG_NO_BUFFERING;
297 dwDesiredAccess &= ~FILE_APPEND_DATA;
298 }
299
300 /*
301 * Open/Create the file.
302 */
303 PRTUTF16 pwszFilename;
304 rc = RTPathWinFromUtf8(&pwszFilename, pszFilename, 0 /*fFlags*/);
305 if (RT_SUCCESS(rc))
306 {
307 HANDLE hFile = CreateFileW(pwszFilename,
308 dwDesiredAccess,
309 dwShareMode,
310 pSecurityAttributes,
311 dwCreationDisposition,
312 dwFlagsAndAttributes,
313 NULL);
314 DWORD const dwErr = GetLastError();
315 if (hFile != INVALID_HANDLE_VALUE)
316 {
317 /*
318 * Calculate the action taken value.
319 */
320 RTFILEACTION enmActionTaken;
321 switch (dwCreationDisposition)
322 {
323 case CREATE_NEW:
324 enmActionTaken = RTFILEACTION_CREATED;
325 break;
326 case CREATE_ALWAYS:
327 AssertMsg(dwErr == ERROR_ALREADY_EXISTS || dwErr == NO_ERROR, ("%u\n", dwErr));
328 enmActionTaken = dwErr == ERROR_ALREADY_EXISTS ? RTFILEACTION_REPLACED : RTFILEACTION_CREATED;
329 break;
330 case OPEN_EXISTING:
331 enmActionTaken = RTFILEACTION_OPENED;
332 break;
333 case OPEN_ALWAYS:
334 AssertMsg(dwErr == ERROR_ALREADY_EXISTS || dwErr == NO_ERROR, ("%u\n", dwErr));
335 enmActionTaken = dwErr == ERROR_ALREADY_EXISTS ? RTFILEACTION_OPENED : RTFILEACTION_CREATED;
336 break;
337 case TRUNCATE_EXISTING:
338 enmActionTaken = RTFILEACTION_TRUNCATED;
339 break;
340 default:
341 AssertMsgFailed(("%d %#x\n", dwCreationDisposition, dwCreationDisposition));
342 enmActionTaken = RTFILEACTION_INVALID;
343 break;
344 }
345
346 /*
347 * Turn off indexing of directory through Windows Indexing Service if
348 * we created a new file or replaced an existing one.
349 */
350 if ( (fOpen & RTFILE_O_NOT_CONTENT_INDEXED)
351 && ( enmActionTaken == RTFILEACTION_CREATED
352 || enmActionTaken == RTFILEACTION_REPLACED) )
353 {
354 /** @todo there must be a way to do this via the handle! */
355 if (!SetFileAttributesW(pwszFilename, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED))
356 rc = RTErrConvertFromWin32(GetLastError());
357 }
358 /*
359 * If RTFILEACTION_OPENED, we may need to truncate the file.
360 */
361 else if ( (fOpen & (RTFILE_O_TRUNCATE | RTFILE_O_ACTION_MASK)) == (RTFILE_O_TRUNCATE | RTFILE_O_OPEN_CREATE)
362 && enmActionTaken == RTFILEACTION_OPENED)
363 {
364 if (SetEndOfFile(hFile))
365 enmActionTaken = RTFILEACTION_TRUNCATED;
366 else
367 rc = RTErrConvertFromWin32(GetLastError());
368 }
369 if (penmActionTaken)
370 *penmActionTaken = enmActionTaken;
371 if (RT_SUCCESS(rc))
372 {
373 *phFile = (RTFILE)hFile;
374 Assert((HANDLE)*phFile == hFile);
375 RTPathWinFree(pwszFilename);
376 return VINF_SUCCESS;
377 }
378
379 CloseHandle(hFile);
380 }
381 else
382 {
383 if ( penmActionTaken
384 && dwCreationDisposition == CREATE_NEW
385 && dwErr == ERROR_FILE_EXISTS)
386 *penmActionTaken = RTFILEACTION_ALREADY_EXISTS;
387 rc = RTErrConvertFromWin32(dwErr);
388 }
389 RTPathWinFree(pwszFilename);
390 }
391 return rc;
392}
393
394
395RTR3DECL(int) RTFileOpenBitBucket(PRTFILE phFile, uint64_t fAccess)
396{
397 AssertReturn( fAccess == RTFILE_O_READ
398 || fAccess == RTFILE_O_WRITE
399 || fAccess == RTFILE_O_READWRITE,
400 VERR_INVALID_PARAMETER);
401 return RTFileOpen(phFile, "NUL", fAccess | RTFILE_O_DENY_NONE | RTFILE_O_OPEN);
402}
403
404
405RTR3DECL(int) RTFileClose(RTFILE hFile)
406{
407 if (hFile == NIL_RTFILE)
408 return VINF_SUCCESS;
409 if (CloseHandle((HANDLE)RTFileToNative(hFile)))
410 return VINF_SUCCESS;
411 return RTErrConvertFromWin32(GetLastError());
412}
413
414
415RTFILE rtFileGetStandard(RTHANDLESTD enmStdHandle)
416{
417 DWORD dwStdHandle;
418 switch (enmStdHandle)
419 {
420 case RTHANDLESTD_INPUT: dwStdHandle = STD_INPUT_HANDLE; break;
421 case RTHANDLESTD_OUTPUT: dwStdHandle = STD_OUTPUT_HANDLE; break;
422 case RTHANDLESTD_ERROR: dwStdHandle = STD_ERROR_HANDLE; break;
423 default:
424 AssertFailedReturn(NIL_RTFILE);
425 }
426
427 HANDLE hNative = GetStdHandle(dwStdHandle);
428 if (hNative == INVALID_HANDLE_VALUE)
429 return NIL_RTFILE;
430
431 RTFILE hFile = (RTFILE)(uintptr_t)hNative;
432 AssertReturn((HANDLE)(uintptr_t)hFile == hNative, NIL_RTFILE);
433 return hFile;
434}
435
436
437RTR3DECL(int) RTFileSeek(RTFILE hFile, int64_t offSeek, unsigned uMethod, uint64_t *poffActual)
438{
439 static ULONG aulSeekRecode[] =
440 {
441 FILE_BEGIN,
442 FILE_CURRENT,
443 FILE_END,
444 };
445
446 /*
447 * Validate input.
448 */
449 if (uMethod > RTFILE_SEEK_END)
450 {
451 AssertMsgFailed(("Invalid uMethod=%d\n", uMethod));
452 return VERR_INVALID_PARAMETER;
453 }
454
455 /*
456 * Execute the seek.
457 */
458 if (MySetFilePointer(hFile, offSeek, poffActual, aulSeekRecode[uMethod]))
459 return VINF_SUCCESS;
460 return RTErrConvertFromWin32(GetLastError());
461}
462
463
464RTR3DECL(int) RTFileRead(RTFILE hFile, void *pvBuf, size_t cbToRead, size_t *pcbRead)
465{
466 if (cbToRead <= 0)
467 {
468 if (pcbRead)
469 *pcbRead = 0;
470 return VINF_SUCCESS;
471 }
472 ULONG cbToReadAdj = (ULONG)cbToRead;
473 AssertReturn(cbToReadAdj == cbToRead, VERR_NUMBER_TOO_BIG);
474
475 ULONG cbRead = 0;
476 if (ReadFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToReadAdj, &cbRead, NULL))
477 {
478 if (pcbRead)
479 /* Caller can handle partial reads. */
480 *pcbRead = cbRead;
481 else
482 {
483 /* Caller expects everything to be read. */
484 while (cbToReadAdj > cbRead)
485 {
486 ULONG cbReadPart = 0;
487 if (!ReadFile((HANDLE)RTFileToNative(hFile), (char*)pvBuf + cbRead, cbToReadAdj - cbRead, &cbReadPart, NULL))
488 return RTErrConvertFromWin32(GetLastError());
489 if (cbReadPart == 0)
490 return VERR_EOF;
491 cbRead += cbReadPart;
492 }
493 }
494 return VINF_SUCCESS;
495 }
496
497 /*
498 * If it's a console, we might bump into out of memory conditions in the
499 * ReadConsole call.
500 */
501 DWORD dwErr = GetLastError();
502 if (dwErr == ERROR_NOT_ENOUGH_MEMORY)
503 {
504 ULONG cbChunk = cbToReadAdj / 2;
505 if (cbChunk > 16*_1K)
506 cbChunk = 16*_1K;
507 else
508 cbChunk = RT_ALIGN_32(cbChunk, 256);
509
510 cbRead = 0;
511 while (cbToReadAdj > cbRead)
512 {
513 ULONG cbToRead = RT_MIN(cbChunk, cbToReadAdj - cbRead);
514 ULONG cbReadPart = 0;
515 if (!ReadFile((HANDLE)RTFileToNative(hFile), (char *)pvBuf + cbRead, cbToRead, &cbReadPart, NULL))
516 {
517 /* If we failed because the buffer is too big, shrink it and
518 try again. */
519 dwErr = GetLastError();
520 if ( dwErr == ERROR_NOT_ENOUGH_MEMORY
521 && cbChunk > 8)
522 {
523 cbChunk /= 2;
524 continue;
525 }
526 return RTErrConvertFromWin32(dwErr);
527 }
528 cbRead += cbReadPart;
529
530 /* Return if the caller can handle partial reads, otherwise try
531 fill the buffer all the way up. */
532 if (pcbRead)
533 {
534 *pcbRead = cbRead;
535 break;
536 }
537 if (cbReadPart == 0)
538 return VERR_EOF;
539 }
540 return VINF_SUCCESS;
541 }
542
543 return RTErrConvertFromWin32(dwErr);
544}
545
546
547RTDECL(int) RTFileReadAt(RTFILE hFile, RTFOFF off, void *pvBuf, size_t cbToRead, size_t *pcbRead)
548{
549 ULONG cbToReadAdj = (ULONG)cbToRead;
550 AssertReturn(cbToReadAdj == cbToRead, VERR_NUMBER_TOO_BIG);
551
552 OVERLAPPED Overlapped;
553 Overlapped.Offset = (uint32_t)off;
554 Overlapped.OffsetHigh = (uint32_t)(off >> 32);
555 Overlapped.hEvent = NULL;
556 Overlapped.Internal = 0;
557 Overlapped.InternalHigh = 0;
558
559 ULONG cbRead = 0;
560 if (ReadFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToReadAdj, &cbRead, &Overlapped))
561 {
562 if (pcbRead)
563 /* Caller can handle partial reads. */
564 *pcbRead = cbRead;
565 else
566 {
567 /* Caller expects everything to be read. */
568 while (cbToReadAdj > cbRead)
569 {
570 Overlapped.Offset = (uint32_t)(off + cbRead);
571 Overlapped.OffsetHigh = (uint32_t)((off + cbRead) >> 32);
572 Overlapped.hEvent = NULL;
573 Overlapped.Internal = 0;
574 Overlapped.InternalHigh = 0;
575
576 ULONG cbReadPart = 0;
577 if (!ReadFile((HANDLE)RTFileToNative(hFile), (char *)pvBuf + cbRead, cbToReadAdj - cbRead,
578 &cbReadPart, &Overlapped))
579 return RTErrConvertFromWin32(GetLastError());
580 if (cbReadPart == 0)
581 return VERR_EOF;
582 cbRead += cbReadPart;
583 }
584 }
585 return VINF_SUCCESS;
586 }
587
588 /* We will get an EOF error when using overlapped I/O. So, make sure we don't
589 return it when pcbhRead is not NULL. */
590 DWORD dwErr = GetLastError();
591 if (pcbRead && dwErr == ERROR_HANDLE_EOF)
592 {
593 *pcbRead = 0;
594 return VINF_SUCCESS;
595 }
596 return RTErrConvertFromWin32(dwErr);
597}
598
599
600RTR3DECL(int) RTFileWrite(RTFILE hFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
601{
602 if (cbToWrite <= 0)
603 return VINF_SUCCESS;
604 ULONG const cbToWriteAdj = (ULONG)cbToWrite;
605 AssertReturn(cbToWriteAdj == cbToWrite, VERR_NUMBER_TOO_BIG);
606
607 ULONG cbWritten = 0;
608 if (WriteFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToWriteAdj, &cbWritten, NULL))
609 {
610 if (pcbWritten)
611 /* Caller can handle partial writes. */
612 *pcbWritten = RT_MIN(cbWritten, cbToWriteAdj); /* paranoia^3 */
613 else
614 {
615 /* Caller expects everything to be written. */
616 while (cbWritten < cbToWriteAdj)
617 {
618 ULONG cbWrittenPart = 0;
619 if (!WriteFile((HANDLE)RTFileToNative(hFile), (char*)pvBuf + cbWritten,
620 cbToWriteAdj - cbWritten, &cbWrittenPart, NULL))
621 {
622 int rc = RTErrConvertFromWin32(GetLastError());
623 if (rc == VERR_DISK_FULL)
624 rc = rtFileWinCheckIfDiskReallyFull(hFile, RTFileTell(hFile) + cbToWriteAdj - cbWritten);
625 return rc;
626 }
627 if (cbWrittenPart == 0)
628 return VERR_WRITE_ERROR;
629 cbWritten += cbWrittenPart;
630 }
631 }
632 return VINF_SUCCESS;
633 }
634
635 /*
636 * If it's a console, we might bump into out of memory conditions in the
637 * WriteConsole call.
638 */
639 DWORD dwErr = GetLastError();
640 if (dwErr == ERROR_NOT_ENOUGH_MEMORY)
641 {
642 ULONG cbChunk = cbToWriteAdj / 2;
643 if (cbChunk > _32K)
644 cbChunk = _32K;
645 else
646 cbChunk = RT_ALIGN_32(cbChunk, 256);
647
648 cbWritten = 0;
649 while (cbWritten < cbToWriteAdj)
650 {
651 ULONG cbToWrite = RT_MIN(cbChunk, cbToWriteAdj - cbWritten);
652 ULONG cbWrittenPart = 0;
653 if (!WriteFile((HANDLE)RTFileToNative(hFile), (const char *)pvBuf + cbWritten, cbToWrite, &cbWrittenPart, NULL))
654 {
655 /* If we failed because the buffer is too big, shrink it and
656 try again. */
657 dwErr = GetLastError();
658 if ( dwErr == ERROR_NOT_ENOUGH_MEMORY
659 && cbChunk > 8)
660 {
661 cbChunk /= 2;
662 continue;
663 }
664 int rc = RTErrConvertFromWin32(dwErr);
665 if (rc == VERR_DISK_FULL)
666 rc = rtFileWinCheckIfDiskReallyFull(hFile, RTFileTell(hFile) + cbToWrite);
667 return rc;
668 }
669 cbWritten += cbWrittenPart;
670
671 /* Return if the caller can handle partial writes, otherwise try
672 write out everything. */
673 if (pcbWritten)
674 {
675 *pcbWritten = RT_MIN(cbWritten, cbToWriteAdj); /* paranoia^3 */
676 break;
677 }
678 if (cbWrittenPart == 0)
679 return VERR_WRITE_ERROR;
680 }
681 return VINF_SUCCESS;
682 }
683
684 int rc = RTErrConvertFromWin32(dwErr);
685 if (rc == VERR_DISK_FULL)
686 rc = rtFileWinCheckIfDiskReallyFull(hFile, RTFileTell(hFile) + cbToWriteAdj);
687 return rc;
688}
689
690
691RTDECL(int) RTFileWriteAt(RTFILE hFile, RTFOFF off, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
692{
693 ULONG const cbToWriteAdj = (ULONG)cbToWrite;
694 AssertReturn(cbToWriteAdj == cbToWrite, VERR_NUMBER_TOO_BIG);
695
696 OVERLAPPED Overlapped;
697 Overlapped.Offset = (uint32_t)off;
698 Overlapped.OffsetHigh = (uint32_t)(off >> 32);
699 Overlapped.hEvent = NULL;
700 Overlapped.Internal = 0;
701 Overlapped.InternalHigh = 0;
702
703 ULONG cbWritten = 0;
704 if (WriteFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToWriteAdj, &cbWritten, &Overlapped))
705 {
706 if (pcbWritten)
707 /* Caller can handle partial writes. */
708 *pcbWritten = RT_MIN(cbWritten, cbToWriteAdj); /* paranoia^3 */
709 else
710 {
711 /* Caller expects everything to be written. */
712 while (cbWritten < cbToWriteAdj)
713 {
714 Overlapped.Offset = (uint32_t)(off + cbWritten);
715 Overlapped.OffsetHigh = (uint32_t)((off + cbWritten) >> 32);
716 Overlapped.hEvent = NULL;
717 Overlapped.Internal = 0;
718 Overlapped.InternalHigh = 0;
719
720 ULONG cbWrittenPart = 0;
721 if (!WriteFile((HANDLE)RTFileToNative(hFile), (char*)pvBuf + cbWritten,
722 cbToWriteAdj - cbWritten, &cbWrittenPart, &Overlapped))
723 {
724 int rc = RTErrConvertFromWin32(GetLastError());
725 if (rc == VERR_DISK_FULL)
726 rc = rtFileWinCheckIfDiskReallyFull(hFile, off + cbToWriteAdj);
727 return rc;
728 }
729 if (cbWrittenPart == 0)
730 return VERR_WRITE_ERROR;
731 cbWritten += cbWrittenPart;
732 }
733 }
734 return VINF_SUCCESS;
735 }
736
737 int rc = RTErrConvertFromWin32(GetLastError());
738 if (rc == VERR_DISK_FULL)
739 rc = rtFileWinCheckIfDiskReallyFull(hFile, off + cbToWriteAdj);
740 return rc;
741}
742
743
744RTR3DECL(int) RTFileFlush(RTFILE hFile)
745{
746 if (!FlushFileBuffers((HANDLE)RTFileToNative(hFile)))
747 {
748 int rc = GetLastError();
749 Log(("FlushFileBuffers failed with %d\n", rc));
750 return RTErrConvertFromWin32(rc);
751 }
752 return VINF_SUCCESS;
753}
754
755
756#if 0
757/**
758 * If @a hFile is opened in append mode, try return a handle with
759 * FILE_WRITE_DATA permissions.
760 *
761 * @returns Duplicate handle.
762 * @param hFile The NT handle to check & duplicate.
763 *
764 * @todo It would be much easier to implement this by not dropping the
765 * FILE_WRITE_DATA access and instead have the RTFileWrite APIs
766 * enforce the appending. That will require keeping additional
767 * information along side the handle (instance structure). However, on
768 * windows you can grant append permissions w/o giving people access to
769 * overwrite existing data, so the RTFileOpenEx code would have to deal
770 * with those kinds of STATUS_ACCESS_DENIED too then.
771 */
772static HANDLE rtFileReOpenAppendOnlyWithFullWriteAccess(HANDLE hFile)
773{
774 OBJECT_BASIC_INFORMATION BasicInfo = {0};
775 ULONG cbActual = 0;
776 NTSTATUS rcNt = NtQueryObject(hFile, ObjectBasicInformation, &BasicInfo, sizeof(BasicInfo), &cbActual);
777 if (NT_SUCCESS(rcNt))
778 {
779 if ((BasicInfo.GrantedAccess & (FILE_APPEND_DATA | FILE_WRITE_DATA)) == FILE_APPEND_DATA)
780 {
781 /*
782 * We cannot use NtDuplicateObject here as it is not possible to
783 * upgrade the access on files, only making it more strict. So,
784 * query the path and re-open it (we could do by file/object/whatever
785 * id too, but that may not work with all file systems).
786 */
787 for (uint32_t i = 0; i < 16; i++)
788 {
789 UNICODE_STRING NtName;
790 int rc = RTNtPathFromHandle(&NtName, hFile, 0);
791 AssertRCReturn(rc, INVALID_HANDLE_VALUE);
792
793 HANDLE hDupFile = RTNT_INVALID_HANDLE_VALUE;
794 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
795 OBJECT_ATTRIBUTES ObjAttr;
796 InitializeObjectAttributes(&ObjAttr, &NtName, BasicInfo.Attributes & ~OBJ_INHERIT, NULL, NULL);
797
798 NTSTATUS rcNt = NtCreateFile(&hDupFile,
799 BasicInfo.GrantedAccess | FILE_WRITE_DATA,
800 &ObjAttr,
801 &Ios,
802 NULL /* AllocationSize*/,
803 FILE_ATTRIBUTE_NORMAL,
804 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
805 FILE_OPEN,
806 FILE_OPEN_FOR_BACKUP_INTENT,
807 NULL /*EaBuffer*/,
808 0 /*EaLength*/);
809 RTUtf16Free(NtName.Buffer);
810 if (NT_SUCCESS(rcNt))
811 {
812 /** @todo Check that the two handles are for the same file object. */
813
814 return hDupFile;
815 }
816 }
817 }
818 }
819 return INVALID_HANDLE_VALUE;
820}
821#endif
822
823
824RTR3DECL(int) RTFileSetSize(RTFILE hFile, uint64_t cbSize)
825{
826#if 0
827 HANDLE hNtFile = (HANDLE)RTFileToNative(hFile);
828 HANDLE hDupFile = INVALID_HANDLE_VALUE;
829 union
830 {
831 FILE_END_OF_FILE_INFORMATION Eof;
832 FILE_ALLOCATION_INFORMATION Alloc;
833 } uInfo;
834
835 /*
836 * Change the EOF marker. We may have to
837 */
838 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
839 uInfo.Eof.EndOfFile.QuadPart = cbSize;
840 NTSTATUS rcNt = NtSetInformationFile(hNtFile, &Ios, &uInfo.Eof, sizeof(uInfo.Eof), FileEndOfFileInformation);
841 if (rcNt == STATUS_ACCESS_DENIED)
842 {
843 hDupFile = rtFileReOpenAppendOnlyWithFullWriteAccess(hNtFile);
844 if (hDupFile != INVALID_HANDLE_VALUE)
845 {
846 hNtFile = hDupFile;
847 uInfo.Eof.EndOfFile.QuadPart = cbSize;
848 rcNt = NtSetInformationFile(hNtFile, &Ios, &uInfo.Eof, sizeof(uInfo.Eof), FileEndOfFileInformation);
849 }
850 }
851
852 if (NT_SUCCESS(rcNt))
853 {
854 /*
855 * Change the allocation.
856 */
857 uInfo.Alloc.AllocationSize.QuadPart = cbSize;
858 rcNt = NtSetInformationFile(hNtFile, &Ios, &uInfo.Eof, sizeof(uInfo.Alloc), FileAllocationInformation);
859 }
860
861 /*
862 * Close the temporary file handle:
863 */
864 if (hDupFile != INVALID_HANDLE_VALUE)
865 NtClose(hDupFile);
866
867 if (RT_SUCCESS(rcNt))
868 return VINF_SUCCESS;
869 return RTErrConvertFromNtStatus(rcNt);
870
871#else /* this version of the code will fail to truncate files when RTFILE_O_APPEND is in effect, which isn't what we want... */
872 /*
873 * Get current file pointer.
874 */
875 int rc;
876 uint64_t offCurrent;
877 if (MySetFilePointer(hFile, 0, &offCurrent, FILE_CURRENT))
878 {
879 /*
880 * Set new file pointer.
881 */
882 if (MySetFilePointer(hFile, cbSize, NULL, FILE_BEGIN))
883 {
884 /* set file pointer */
885 if (SetEndOfFile((HANDLE)RTFileToNative(hFile)))
886 {
887 /*
888 * Restore file pointer and return.
889 * If the old pointer was beyond the new file end, ignore failure.
890 */
891 if ( MySetFilePointer(hFile, offCurrent, NULL, FILE_BEGIN)
892 || offCurrent > cbSize)
893 return VINF_SUCCESS;
894 }
895
896 /*
897 * Failed, try restoring the file pointer.
898 */
899 rc = GetLastError();
900 MySetFilePointer(hFile, offCurrent, NULL, FILE_BEGIN);
901
902 if (rc == ERROR_DISK_FULL)
903 return rtFileWinCheckIfDiskReallyFull(hFile, cbSize);
904 }
905 else
906 rc = GetLastError();
907 }
908 else
909 rc = GetLastError();
910
911 return RTErrConvertFromWin32(rc);
912#endif
913}
914
915
916RTR3DECL(int) RTFileQuerySize(RTFILE hFile, uint64_t *pcbSize)
917{
918 /*
919 * GetFileSize works for most handles.
920 */
921 ULARGE_INTEGER Size;
922 Size.LowPart = GetFileSize((HANDLE)RTFileToNative(hFile), &Size.HighPart);
923 if (Size.LowPart != INVALID_FILE_SIZE)
924 {
925 *pcbSize = Size.QuadPart;
926 return VINF_SUCCESS;
927 }
928 int rc = RTErrConvertFromWin32(GetLastError());
929
930 /*
931 * Could it be a volume or a disk?
932 */
933 DISK_GEOMETRY DriveGeo;
934 DWORD cbDriveGeo;
935 if (DeviceIoControl((HANDLE)RTFileToNative(hFile),
936 IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
937 &DriveGeo, sizeof(DriveGeo), &cbDriveGeo, NULL))
938 {
939 if ( DriveGeo.MediaType == FixedMedia
940 || DriveGeo.MediaType == RemovableMedia)
941 {
942 *pcbSize = DriveGeo.Cylinders.QuadPart
943 * DriveGeo.TracksPerCylinder
944 * DriveGeo.SectorsPerTrack
945 * DriveGeo.BytesPerSector;
946
947 GET_LENGTH_INFORMATION DiskLenInfo;
948 DWORD Ignored;
949 if (DeviceIoControl((HANDLE)RTFileToNative(hFile),
950 IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
951 &DiskLenInfo, sizeof(DiskLenInfo), &Ignored, (LPOVERLAPPED)NULL))
952 {
953 /* IOCTL_DISK_GET_LENGTH_INFO is supported -- override cbSize. */
954 *pcbSize = DiskLenInfo.Length.QuadPart;
955 }
956 return VINF_SUCCESS;
957 }
958 }
959
960 /*
961 * Return the GetFileSize result if not a volume/disk.
962 */
963 return rc;
964}
965
966
967RTR3DECL(int) RTFileQueryMaxSizeEx(RTFILE hFile, PRTFOFF pcbMax)
968{
969 /** @todo r=bird:
970 * We might have to make this code OS version specific... In the worse
971 * case, we'll have to try GetVolumeInformationByHandle on vista and fall
972 * back on NtQueryVolumeInformationFile(,,,, FileFsAttributeInformation)
973 * else where, and check for known file system names. (For LAN shares we'll
974 * have to figure out the remote file system.) */
975 RT_NOREF_PV(hFile); RT_NOREF_PV(pcbMax);
976 return VERR_NOT_IMPLEMENTED;
977}
978
979
980RTR3DECL(bool) RTFileIsValid(RTFILE hFile)
981{
982 if (hFile != NIL_RTFILE)
983 {
984 DWORD dwType = GetFileType((HANDLE)RTFileToNative(hFile));
985 switch (dwType)
986 {
987 case FILE_TYPE_CHAR:
988 case FILE_TYPE_DISK:
989 case FILE_TYPE_PIPE:
990 case FILE_TYPE_REMOTE:
991 return true;
992
993 case FILE_TYPE_UNKNOWN:
994 if (GetLastError() == NO_ERROR)
995 return true;
996 break;
997
998 default:
999 break;
1000 }
1001 }
1002 return false;
1003}
1004
1005
1006#define LOW_DWORD(u64) ((DWORD)u64)
1007#define HIGH_DWORD(u64) (((DWORD *)&u64)[1])
1008
1009RTR3DECL(int) RTFileLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock)
1010{
1011 Assert(offLock >= 0);
1012
1013 /* Check arguments. */
1014 if (fLock & ~RTFILE_LOCK_MASK)
1015 {
1016 AssertMsgFailed(("Invalid fLock=%08X\n", fLock));
1017 return VERR_INVALID_PARAMETER;
1018 }
1019
1020 /* Prepare flags. */
1021 Assert(RTFILE_LOCK_WRITE);
1022 DWORD dwFlags = (fLock & RTFILE_LOCK_WRITE) ? LOCKFILE_EXCLUSIVE_LOCK : 0;
1023 Assert(RTFILE_LOCK_WAIT);
1024 if (!(fLock & RTFILE_LOCK_WAIT))
1025 dwFlags |= LOCKFILE_FAIL_IMMEDIATELY;
1026
1027 /* Windows structure. */
1028 OVERLAPPED Overlapped;
1029 memset(&Overlapped, 0, sizeof(Overlapped));
1030 Overlapped.Offset = LOW_DWORD(offLock);
1031 Overlapped.OffsetHigh = HIGH_DWORD(offLock);
1032
1033 /* Note: according to Microsoft, LockFileEx API call is available starting from NT 3.5 */
1034 if (LockFileEx((HANDLE)RTFileToNative(hFile), dwFlags, 0, LOW_DWORD(cbLock), HIGH_DWORD(cbLock), &Overlapped))
1035 return VINF_SUCCESS;
1036
1037 return RTErrConvertFromWin32(GetLastError());
1038}
1039
1040
1041RTR3DECL(int) RTFileChangeLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock)
1042{
1043 Assert(offLock >= 0);
1044
1045 /* Check arguments. */
1046 if (fLock & ~RTFILE_LOCK_MASK)
1047 {
1048 AssertMsgFailed(("Invalid fLock=%08X\n", fLock));
1049 return VERR_INVALID_PARAMETER;
1050 }
1051
1052 /* Remove old lock. */
1053 int rc = RTFileUnlock(hFile, offLock, cbLock);
1054 if (RT_FAILURE(rc))
1055 return rc;
1056
1057 /* Set new lock. */
1058 rc = RTFileLock(hFile, fLock, offLock, cbLock);
1059 if (RT_SUCCESS(rc))
1060 return rc;
1061
1062 /* Try to restore old lock. */
1063 unsigned fLockOld = (fLock & RTFILE_LOCK_WRITE) ? fLock & ~RTFILE_LOCK_WRITE : fLock | RTFILE_LOCK_WRITE;
1064 rc = RTFileLock(hFile, fLockOld, offLock, cbLock);
1065 if (RT_SUCCESS(rc))
1066 return VERR_FILE_LOCK_VIOLATION;
1067 else
1068 return VERR_FILE_LOCK_LOST;
1069}
1070
1071
1072RTR3DECL(int) RTFileUnlock(RTFILE hFile, int64_t offLock, uint64_t cbLock)
1073{
1074 Assert(offLock >= 0);
1075
1076 if (UnlockFile((HANDLE)RTFileToNative(hFile),
1077 LOW_DWORD(offLock), HIGH_DWORD(offLock),
1078 LOW_DWORD(cbLock), HIGH_DWORD(cbLock)))
1079 return VINF_SUCCESS;
1080
1081 return RTErrConvertFromWin32(GetLastError());
1082}
1083
1084
1085
1086RTR3DECL(int) RTFileQueryInfo(RTFILE hFile, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
1087{
1088 /*
1089 * Validate input.
1090 */
1091 if (hFile == NIL_RTFILE)
1092 {
1093 AssertMsgFailed(("Invalid hFile=%RTfile\n", hFile));
1094 return VERR_INVALID_PARAMETER;
1095 }
1096 if (!pObjInfo)
1097 {
1098 AssertMsgFailed(("Invalid pObjInfo=%p\n", pObjInfo));
1099 return VERR_INVALID_PARAMETER;
1100 }
1101 if ( enmAdditionalAttribs < RTFSOBJATTRADD_NOTHING
1102 || enmAdditionalAttribs > RTFSOBJATTRADD_LAST)
1103 {
1104 AssertMsgFailed(("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs));
1105 return VERR_INVALID_PARAMETER;
1106 }
1107
1108 /*
1109 * Query file info.
1110 */
1111 HANDLE hHandle = (HANDLE)RTFileToNative(hFile);
1112#if 1
1113 uint64_t auBuf[168 / sizeof(uint64_t)]; /* Missing FILE_ALL_INFORMATION here. */
1114 int rc = rtPathNtQueryInfoFromHandle(hFile, auBuf, sizeof(auBuf), pObjInfo, enmAdditionalAttribs, NULL, 0);
1115 if (RT_SUCCESS(rc))
1116 return rc;
1117
1118 /*
1119 * Console I/O handles make trouble here. On older windows versions they
1120 * end up with ERROR_INVALID_HANDLE when handed to the above API, while on
1121 * more recent ones they cause different errors to appear.
1122 *
1123 * Thus, we must ignore the latter and doubly verify invalid handle claims.
1124 * We use the undocumented VerifyConsoleIoHandle to do this, falling back on
1125 * GetFileType should it not be there.
1126 */
1127 if ( rc == VERR_INVALID_HANDLE
1128 || rc == VERR_ACCESS_DENIED
1129 || rc == VERR_UNEXPECTED_FS_OBJ_TYPE)
1130 {
1131 static PFNVERIFYCONSOLEIOHANDLE s_pfnVerifyConsoleIoHandle = NULL;
1132 static bool volatile s_fInitialized = false;
1133 PFNVERIFYCONSOLEIOHANDLE pfnVerifyConsoleIoHandle;
1134 if (s_fInitialized)
1135 pfnVerifyConsoleIoHandle = s_pfnVerifyConsoleIoHandle;
1136 else
1137 {
1138 pfnVerifyConsoleIoHandle = (PFNVERIFYCONSOLEIOHANDLE)RTLdrGetSystemSymbol("kernel32.dll", "VerifyConsoleIoHandle");
1139 ASMAtomicWriteBool(&s_fInitialized, true);
1140 }
1141 if ( pfnVerifyConsoleIoHandle
1142 ? !pfnVerifyConsoleIoHandle(hHandle)
1143 : GetFileType(hHandle) == FILE_TYPE_UNKNOWN && GetLastError() != NO_ERROR)
1144 return VERR_INVALID_HANDLE;
1145 }
1146 /*
1147 * On Windows 10 and (hopefully) 8.1 we get ERROR_INVALID_FUNCTION with console
1148 * I/O handles and null device handles. We must ignore these just like the
1149 * above invalid handle error.
1150 */
1151 else if (rc != VERR_INVALID_FUNCTION && rc != VERR_IO_BAD_COMMAND)
1152 return rc;
1153
1154 RT_ZERO(*pObjInfo);
1155 pObjInfo->Attr.enmAdditional = enmAdditionalAttribs;
1156 pObjInfo->Attr.fMode = rtFsModeFromDos(RTFS_DOS_NT_DEVICE, "", 0, 0, 0);
1157 return VINF_SUCCESS;
1158#else
1159
1160 BY_HANDLE_FILE_INFORMATION Data;
1161 if (!GetFileInformationByHandle(hHandle, &Data))
1162 {
1163 /*
1164 * Console I/O handles make trouble here. On older windows versions they
1165 * end up with ERROR_INVALID_HANDLE when handed to the above API, while on
1166 * more recent ones they cause different errors to appear.
1167 *
1168 * Thus, we must ignore the latter and doubly verify invalid handle claims.
1169 * We use the undocumented VerifyConsoleIoHandle to do this, falling back on
1170 * GetFileType should it not be there.
1171 */
1172 DWORD dwErr = GetLastError();
1173 if (dwErr == ERROR_INVALID_HANDLE)
1174 {
1175 static PFNVERIFYCONSOLEIOHANDLE s_pfnVerifyConsoleIoHandle = NULL;
1176 static bool volatile s_fInitialized = false;
1177 PFNVERIFYCONSOLEIOHANDLE pfnVerifyConsoleIoHandle;
1178 if (s_fInitialized)
1179 pfnVerifyConsoleIoHandle = s_pfnVerifyConsoleIoHandle;
1180 else
1181 {
1182 pfnVerifyConsoleIoHandle = (PFNVERIFYCONSOLEIOHANDLE)RTLdrGetSystemSymbol("kernel32.dll", "VerifyConsoleIoHandle");
1183 ASMAtomicWriteBool(&s_fInitialized, true);
1184 }
1185 if ( pfnVerifyConsoleIoHandle
1186 ? !pfnVerifyConsoleIoHandle(hHandle)
1187 : GetFileType(hHandle) == FILE_TYPE_UNKNOWN && GetLastError() != NO_ERROR)
1188 return VERR_INVALID_HANDLE;
1189 }
1190 /*
1191 * On Windows 10 and (hopefully) 8.1 we get ERROR_INVALID_FUNCTION with console I/O
1192 * handles. We must ignore these just like the above invalid handle error.
1193 */
1194 else if (dwErr != ERROR_INVALID_FUNCTION)
1195 return RTErrConvertFromWin32(dwErr);
1196
1197 RT_ZERO(Data);
1198 Data.dwFileAttributes = RTFS_DOS_NT_DEVICE;
1199 }
1200
1201 /*
1202 * Setup the returned data.
1203 */
1204 pObjInfo->cbObject = ((uint64_t)Data.nFileSizeHigh << 32)
1205 | (uint64_t)Data.nFileSizeLow;
1206 pObjInfo->cbAllocated = pObjInfo->cbObject;
1207
1208 Assert(sizeof(uint64_t) == sizeof(Data.ftCreationTime));
1209 RTTimeSpecSetNtTime(&pObjInfo->BirthTime, *(uint64_t *)&Data.ftCreationTime);
1210 RTTimeSpecSetNtTime(&pObjInfo->AccessTime, *(uint64_t *)&Data.ftLastAccessTime);
1211 RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, *(uint64_t *)&Data.ftLastWriteTime);
1212 pObjInfo->ChangeTime = pObjInfo->ModificationTime;
1213
1214 pObjInfo->Attr.fMode = rtFsModeFromDos((Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT, "", 0,
1215 RTFSMODE_SYMLINK_REPARSE_TAG /* (symlink or not, doesn't usually matter here) */);
1216
1217 /*
1218 * Requested attributes (we cannot provide anything actually).
1219 */
1220 switch (enmAdditionalAttribs)
1221 {
1222 case RTFSOBJATTRADD_NOTHING:
1223 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
1224 break;
1225
1226 case RTFSOBJATTRADD_UNIX:
1227 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
1228 pObjInfo->Attr.u.Unix.uid = ~0U;
1229 pObjInfo->Attr.u.Unix.gid = ~0U;
1230 pObjInfo->Attr.u.Unix.cHardlinks = Data.nNumberOfLinks ? Data.nNumberOfLinks : 1;
1231 pObjInfo->Attr.u.Unix.INodeIdDevice = Data.dwVolumeSerialNumber;
1232 pObjInfo->Attr.u.Unix.INodeId = RT_MAKE_U64(Data.nFileIndexLow, Data.nFileIndexHigh);
1233 pObjInfo->Attr.u.Unix.fFlags = 0;
1234 pObjInfo->Attr.u.Unix.GenerationId = 0;
1235 pObjInfo->Attr.u.Unix.Device = 0;
1236 break;
1237
1238 case RTFSOBJATTRADD_UNIX_OWNER:
1239 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
1240 pObjInfo->Attr.u.UnixOwner.uid = ~0U;
1241 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
1242 break;
1243
1244 case RTFSOBJATTRADD_UNIX_GROUP:
1245 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
1246 pObjInfo->Attr.u.UnixGroup.gid = ~0U;
1247 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
1248 break;
1249
1250 case RTFSOBJATTRADD_EASIZE:
1251 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
1252 pObjInfo->Attr.u.EASize.cb = 0;
1253 break;
1254
1255 default:
1256 AssertMsgFailed(("Impossible!\n"));
1257 return VERR_INTERNAL_ERROR;
1258 }
1259
1260 return VINF_SUCCESS;
1261#endif
1262}
1263
1264
1265RTR3DECL(int) RTFileSetTimes(RTFILE hFile, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1266 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1267{
1268 RT_NOREF_PV(pChangeTime); /* Not exposed thru the windows API we're using. */
1269
1270 if (!pAccessTime && !pModificationTime && !pBirthTime)
1271 return VINF_SUCCESS; /* NOP */
1272
1273 FILETIME CreationTimeFT;
1274 PFILETIME pCreationTimeFT = NULL;
1275 if (pBirthTime)
1276 pCreationTimeFT = RTTimeSpecGetNtFileTime(pBirthTime, &CreationTimeFT);
1277
1278 FILETIME LastAccessTimeFT;
1279 PFILETIME pLastAccessTimeFT = NULL;
1280 if (pAccessTime)
1281 pLastAccessTimeFT = RTTimeSpecGetNtFileTime(pAccessTime, &LastAccessTimeFT);
1282
1283 FILETIME LastWriteTimeFT;
1284 PFILETIME pLastWriteTimeFT = NULL;
1285 if (pModificationTime)
1286 pLastWriteTimeFT = RTTimeSpecGetNtFileTime(pModificationTime, &LastWriteTimeFT);
1287
1288 int rc = VINF_SUCCESS;
1289 if (!SetFileTime((HANDLE)RTFileToNative(hFile), pCreationTimeFT, pLastAccessTimeFT, pLastWriteTimeFT))
1290 {
1291 DWORD Err = GetLastError();
1292 rc = RTErrConvertFromWin32(Err);
1293 Log(("RTFileSetTimes(%RTfile, %p, %p, %p, %p): SetFileTime failed with lasterr %d (%Rrc)\n",
1294 hFile, pAccessTime, pModificationTime, pChangeTime, pBirthTime, Err, rc));
1295 }
1296 return rc;
1297}
1298
1299
1300#if 0 /* RTFileSetMode is implemented by RTFileSetMode-r3-nt.cpp */
1301/* This comes from a source file with a different set of system headers (DDK)
1302 * so it can't be declared in a common header, like internal/file.h.
1303 */
1304extern int rtFileNativeSetAttributes(HANDLE FileHandle, ULONG FileAttributes);
1305
1306
1307RTR3DECL(int) RTFileSetMode(RTFILE hFile, RTFMODE fMode)
1308{
1309 /*
1310 * Normalize the mode and call the API.
1311 */
1312 fMode = rtFsModeNormalize(fMode, NULL, 0);
1313 if (!rtFsModeIsValid(fMode))
1314 return VERR_INVALID_PARAMETER;
1315
1316 ULONG FileAttributes = (fMode & RTFS_DOS_MASK) >> RTFS_DOS_SHIFT;
1317 int Err = rtFileNativeSetAttributes((HANDLE)hFile, FileAttributes);
1318 if (Err != ERROR_SUCCESS)
1319 {
1320 int rc = RTErrConvertFromWin32(Err);
1321 Log(("RTFileSetMode(%RTfile, %RTfmode): rtFileNativeSetAttributes (0x%08X) failed with err %d (%Rrc)\n",
1322 hFile, fMode, FileAttributes, Err, rc));
1323 return rc;
1324 }
1325 return VINF_SUCCESS;
1326}
1327#endif
1328
1329
1330/* RTFileQueryFsSizes is implemented by ../nt/RTFileQueryFsSizes-nt.cpp */
1331
1332
1333RTR3DECL(int) RTFileDelete(const char *pszFilename)
1334{
1335 PRTUTF16 pwszFilename;
1336 int rc = RTPathWinFromUtf8(&pwszFilename, pszFilename, 0 /*fFlags*/);
1337 if (RT_SUCCESS(rc))
1338 {
1339 if (!DeleteFileW(pwszFilename))
1340 rc = RTErrConvertFromWin32(GetLastError());
1341 RTPathWinFree(pwszFilename);
1342 }
1343
1344 return rc;
1345}
1346
1347
1348RTDECL(int) RTFileRename(const char *pszSrc, const char *pszDst, unsigned fRename)
1349{
1350 /*
1351 * Validate input.
1352 */
1353 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
1354 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
1355 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
1356
1357 /*
1358 * Hand it on to the worker.
1359 */
1360 int rc = rtPathWin32MoveRename(pszSrc, pszDst,
1361 fRename & RTPATHRENAME_FLAGS_REPLACE ? MOVEFILE_REPLACE_EXISTING : 0,
1362 RTFS_TYPE_FILE);
1363
1364 LogFlow(("RTFileMove(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n",
1365 pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
1366 return rc;
1367
1368}
1369
1370
1371RTDECL(int) RTFileMove(const char *pszSrc, const char *pszDst, unsigned fMove)
1372{
1373 /*
1374 * Validate input.
1375 */
1376 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
1377 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
1378 AssertMsgReturn(!(fMove & ~RTFILEMOVE_FLAGS_REPLACE), ("%#x\n", fMove), VERR_INVALID_PARAMETER);
1379
1380 /*
1381 * Hand it on to the worker.
1382 */
1383 int rc = rtPathWin32MoveRename(pszSrc, pszDst,
1384 fMove & RTFILEMOVE_FLAGS_REPLACE
1385 ? MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING
1386 : MOVEFILE_COPY_ALLOWED,
1387 RTFS_TYPE_FILE);
1388
1389 LogFlow(("RTFileMove(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n",
1390 pszSrc, pszSrc, pszDst, pszDst, fMove, rc));
1391 return rc;
1392}
1393
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette