VirtualBox

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

Last change on this file since 96076 was 96076, checked in by vboxsync, 3 years ago

IPRT/RTFileOpen: Added a RTFILE_O_TEMP_AUTO_DELETE flag for implementing tmpfile/tmpfile_s and similar. bugref:10261

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

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