VirtualBox

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

Last change on this file since 57634 was 57634, checked in by vboxsync, 9 years ago

fileio-win.cpp: W10 return ERROR_INVALID_FUNCTION in RTFileQueryInfo when presented with a console I/O handle, ignore it.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 35.6 KB
Line 
1/* $Id: fileio-win.cpp 57634 2015-09-07 10:29:53Z vboxsync $ */
2/** @file
3 * IPRT - File I/O, native implementation for the Windows host platform.
4 */
5
6/*
7 * Copyright (C) 2006-2015 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 <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 "internal/file.h"
47#include "internal/fs.h"
48#include "internal/path.h"
49
50
51/*********************************************************************************************************************************
52* Defined Constants And Macros *
53*********************************************************************************************************************************/
54typedef BOOL WINAPI FNVERIFYCONSOLEIOHANDLE(HANDLE);
55typedef FNVERIFYCONSOLEIOHANDLE *PFNVERIFYCONSOLEIOHANDLE; /* No, nobody fell on the keyboard, really! */
56
57/**
58 * This is wrapper around the ugly SetFilePointer api.
59 *
60 * It's equivalent to SetFilePointerEx which we so unfortunately cannot use because of
61 * it not being present in NT4 GA.
62 *
63 * @returns Success indicator. Extended error information obtainable using GetLastError().
64 * @param hFile Filehandle.
65 * @param offSeek Offset to seek.
66 * @param poffNew Where to store the new file offset. NULL allowed.
67 * @param uMethod Seek method. (The windows one!)
68 */
69DECLINLINE(bool) MySetFilePointer(RTFILE hFile, uint64_t offSeek, uint64_t *poffNew, unsigned uMethod)
70{
71 bool fRc;
72 LARGE_INTEGER off;
73
74 off.QuadPart = offSeek;
75#if 1
76 if (off.LowPart != INVALID_SET_FILE_POINTER)
77 {
78 off.LowPart = SetFilePointer((HANDLE)RTFileToNative(hFile), off.LowPart, &off.HighPart, uMethod);
79 fRc = off.LowPart != INVALID_SET_FILE_POINTER;
80 }
81 else
82 {
83 SetLastError(NO_ERROR);
84 off.LowPart = SetFilePointer((HANDLE)RTFileToNative(hFile), off.LowPart, &off.HighPart, uMethod);
85 fRc = GetLastError() == NO_ERROR;
86 }
87#else
88 fRc = SetFilePointerEx((HANDLE)RTFileToNative(hFile), off, &off, uMethod);
89#endif
90 if (fRc && poffNew)
91 *poffNew = off.QuadPart;
92 return fRc;
93}
94
95
96/**
97 * This is a helper to check if an attempt was made to grow a file beyond the
98 * limit of the filesystem.
99 *
100 * @returns true for file size limit exceeded.
101 * @param hFile Filehandle.
102 * @param offSeek Offset to seek.
103 * @param uMethod The seek method.
104 */
105DECLINLINE(bool) IsBeyondLimit(RTFILE hFile, uint64_t offSeek, unsigned uMethod)
106{
107 bool fIsBeyondLimit = false;
108
109 /*
110 * Get the current file position and try set the new one.
111 * If it fails with a seek error it's because we hit the file system limit.
112 */
113/** @todo r=bird: I'd be very interested to know on which versions of windows and on which file systems
114 * this supposedly works. The fastfat sources in the latest WDK makes no limit checks during
115 * file seeking, only at the time of writing (and some other odd ones we cannot make use of). */
116 uint64_t offCurrent;
117 if (MySetFilePointer(hFile, 0, &offCurrent, FILE_CURRENT))
118 {
119 if (!MySetFilePointer(hFile, offSeek, NULL, uMethod))
120 fIsBeyondLimit = GetLastError() == ERROR_SEEK;
121 else /* Restore file pointer on success. */
122 MySetFilePointer(hFile, offCurrent, NULL, FILE_BEGIN);
123 }
124
125 return fIsBeyondLimit;
126}
127
128
129RTR3DECL(int) RTFileFromNative(PRTFILE pFile, RTHCINTPTR uNative)
130{
131 HANDLE h = (HANDLE)uNative;
132 AssertCompile(sizeof(h) == sizeof(uNative));
133 if (h == INVALID_HANDLE_VALUE)
134 {
135 AssertMsgFailed(("%p\n", uNative));
136 *pFile = NIL_RTFILE;
137 return VERR_INVALID_HANDLE;
138 }
139 *pFile = (RTFILE)h;
140 return VINF_SUCCESS;
141}
142
143
144RTR3DECL(RTHCINTPTR) RTFileToNative(RTFILE hFile)
145{
146 AssertReturn(hFile != NIL_RTFILE, (RTHCINTPTR)INVALID_HANDLE_VALUE);
147 return (RTHCINTPTR)hFile;
148}
149
150
151RTR3DECL(int) RTFileOpen(PRTFILE pFile, const char *pszFilename, uint64_t fOpen)
152{
153 /*
154 * Validate input.
155 */
156 if (!pFile)
157 {
158 AssertMsgFailed(("Invalid pFile\n"));
159 return VERR_INVALID_PARAMETER;
160 }
161 *pFile = NIL_RTFILE;
162 if (!pszFilename)
163 {
164 AssertMsgFailed(("Invalid pszFilename\n"));
165 return VERR_INVALID_PARAMETER;
166 }
167
168 /*
169 * Merge forced open flags and validate them.
170 */
171 int rc = rtFileRecalcAndValidateFlags(&fOpen);
172 if (RT_FAILURE(rc))
173 return rc;
174
175 /*
176 * Determine disposition, access, share mode, creation flags, and security attributes
177 * for the CreateFile API call.
178 */
179 DWORD dwCreationDisposition;
180 switch (fOpen & RTFILE_O_ACTION_MASK)
181 {
182 case RTFILE_O_OPEN:
183 dwCreationDisposition = fOpen & RTFILE_O_TRUNCATE ? TRUNCATE_EXISTING : OPEN_EXISTING;
184 break;
185 case RTFILE_O_OPEN_CREATE:
186 dwCreationDisposition = OPEN_ALWAYS;
187 break;
188 case RTFILE_O_CREATE:
189 dwCreationDisposition = CREATE_NEW;
190 break;
191 case RTFILE_O_CREATE_REPLACE:
192 dwCreationDisposition = CREATE_ALWAYS;
193 break;
194 default:
195 AssertMsgFailed(("Impossible fOpen=%#llx\n", fOpen));
196 return VERR_INVALID_PARAMETER;
197 }
198
199 DWORD dwDesiredAccess;
200 switch (fOpen & RTFILE_O_ACCESS_MASK)
201 {
202 case RTFILE_O_READ:
203 dwDesiredAccess = FILE_GENERIC_READ; /* RTFILE_O_APPEND is ignored. */
204 break;
205 case RTFILE_O_WRITE:
206 dwDesiredAccess = fOpen & RTFILE_O_APPEND
207 ? FILE_GENERIC_WRITE & ~FILE_WRITE_DATA
208 : FILE_GENERIC_WRITE;
209 break;
210 case RTFILE_O_READWRITE:
211 dwDesiredAccess = fOpen & RTFILE_O_APPEND
212 ? FILE_GENERIC_READ | (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA)
213 : FILE_GENERIC_READ | FILE_GENERIC_WRITE;
214 break;
215 default:
216 AssertMsgFailed(("Impossible fOpen=%#llx\n", fOpen));
217 return VERR_INVALID_PARAMETER;
218 }
219 if (dwCreationDisposition == TRUNCATE_EXISTING)
220 /* Required for truncating the file (see MSDN), it is *NOT* part of FILE_GENERIC_WRITE. */
221 dwDesiredAccess |= GENERIC_WRITE;
222
223 /* RTFileSetMode needs following rights as well. */
224 switch (fOpen & RTFILE_O_ACCESS_ATTR_MASK)
225 {
226 case RTFILE_O_ACCESS_ATTR_READ: dwDesiredAccess |= FILE_READ_ATTRIBUTES | SYNCHRONIZE; break;
227 case RTFILE_O_ACCESS_ATTR_WRITE: dwDesiredAccess |= FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break;
228 case RTFILE_O_ACCESS_ATTR_READWRITE: dwDesiredAccess |= FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break;
229 default:
230 /* Attributes access is the same as the file access. */
231 switch (fOpen & RTFILE_O_ACCESS_MASK)
232 {
233 case RTFILE_O_READ: dwDesiredAccess |= FILE_READ_ATTRIBUTES | SYNCHRONIZE; break;
234 case RTFILE_O_WRITE: dwDesiredAccess |= FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break;
235 case RTFILE_O_READWRITE: dwDesiredAccess |= FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break;
236 default:
237 AssertMsgFailed(("Impossible fOpen=%#llx\n", fOpen));
238 return VERR_INVALID_PARAMETER;
239 }
240 }
241
242 DWORD dwShareMode;
243 switch (fOpen & RTFILE_O_DENY_MASK)
244 {
245 case RTFILE_O_DENY_NONE: dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; break;
246 case RTFILE_O_DENY_READ: dwShareMode = FILE_SHARE_WRITE; break;
247 case RTFILE_O_DENY_WRITE: dwShareMode = FILE_SHARE_READ; break;
248 case RTFILE_O_DENY_READWRITE: dwShareMode = 0; break;
249
250 case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_NONE: dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE; break;
251 case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_READ: dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_WRITE; break;
252 case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_WRITE: dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ; break;
253 case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_READWRITE:dwShareMode = FILE_SHARE_DELETE; break;
254 default:
255 AssertMsgFailed(("Impossible fOpen=%#llx\n", fOpen));
256 return VERR_INVALID_PARAMETER;
257 }
258
259 SECURITY_ATTRIBUTES SecurityAttributes;
260 PSECURITY_ATTRIBUTES pSecurityAttributes = NULL;
261 if (fOpen & RTFILE_O_INHERIT)
262 {
263 SecurityAttributes.nLength = sizeof(SecurityAttributes);
264 SecurityAttributes.lpSecurityDescriptor = NULL;
265 SecurityAttributes.bInheritHandle = TRUE;
266 pSecurityAttributes = &SecurityAttributes;
267 }
268
269 DWORD dwFlagsAndAttributes;
270 dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
271 if (fOpen & RTFILE_O_WRITE_THROUGH)
272 dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;
273 if (fOpen & RTFILE_O_ASYNC_IO)
274 dwFlagsAndAttributes |= FILE_FLAG_OVERLAPPED;
275 if (fOpen & RTFILE_O_NO_CACHE)
276 {
277 dwFlagsAndAttributes |= FILE_FLAG_NO_BUFFERING;
278 dwDesiredAccess &= ~FILE_APPEND_DATA;
279 }
280
281 /*
282 * Open/Create the file.
283 */
284 PRTUTF16 pwszFilename;
285 rc = RTStrToUtf16(pszFilename, &pwszFilename);
286 if (RT_FAILURE(rc))
287 return rc;
288
289 HANDLE hFile = CreateFileW(pwszFilename,
290 dwDesiredAccess,
291 dwShareMode,
292 pSecurityAttributes,
293 dwCreationDisposition,
294 dwFlagsAndAttributes,
295 NULL);
296 if (hFile != INVALID_HANDLE_VALUE)
297 {
298 bool fCreated = dwCreationDisposition == CREATE_ALWAYS
299 || dwCreationDisposition == CREATE_NEW
300 || (dwCreationDisposition == OPEN_ALWAYS && GetLastError() == 0);
301
302 /*
303 * Turn off indexing of directory through Windows Indexing Service.
304 */
305 if ( fCreated
306 && (fOpen & RTFILE_O_NOT_CONTENT_INDEXED))
307 {
308 if (!SetFileAttributesW(pwszFilename, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED))
309 rc = RTErrConvertFromWin32(GetLastError());
310 }
311 /*
312 * Do we need to truncate the file?
313 */
314 else if ( !fCreated
315 && (fOpen & (RTFILE_O_TRUNCATE | RTFILE_O_ACTION_MASK))
316 == (RTFILE_O_TRUNCATE | RTFILE_O_OPEN_CREATE))
317 {
318 if (!SetEndOfFile(hFile))
319 rc = RTErrConvertFromWin32(GetLastError());
320 }
321 if (RT_SUCCESS(rc))
322 {
323 *pFile = (RTFILE)hFile;
324 Assert((HANDLE)*pFile == hFile);
325 RTUtf16Free(pwszFilename);
326 return VINF_SUCCESS;
327 }
328
329 CloseHandle(hFile);
330 }
331 else
332 rc = RTErrConvertFromWin32(GetLastError());
333 RTUtf16Free(pwszFilename);
334 return rc;
335}
336
337
338RTR3DECL(int) RTFileOpenBitBucket(PRTFILE phFile, uint64_t fAccess)
339{
340 AssertReturn( fAccess == RTFILE_O_READ
341 || fAccess == RTFILE_O_WRITE
342 || fAccess == RTFILE_O_READWRITE,
343 VERR_INVALID_PARAMETER);
344 return RTFileOpen(phFile, "NUL", fAccess | RTFILE_O_DENY_NONE | RTFILE_O_OPEN);
345}
346
347
348RTR3DECL(int) RTFileClose(RTFILE hFile)
349{
350 if (hFile == NIL_RTFILE)
351 return VINF_SUCCESS;
352 if (CloseHandle((HANDLE)RTFileToNative(hFile)))
353 return VINF_SUCCESS;
354 return RTErrConvertFromWin32(GetLastError());
355}
356
357
358RTFILE rtFileGetStandard(RTHANDLESTD enmStdHandle)
359{
360 DWORD dwStdHandle;
361 switch (enmStdHandle)
362 {
363 case RTHANDLESTD_INPUT: dwStdHandle = STD_INPUT_HANDLE; break;
364 case RTHANDLESTD_OUTPUT: dwStdHandle = STD_OUTPUT_HANDLE; break;
365 case RTHANDLESTD_ERROR: dwStdHandle = STD_ERROR_HANDLE; break;
366 break;
367 default:
368 AssertFailedReturn(NIL_RTFILE);
369 }
370
371 HANDLE hNative = GetStdHandle(dwStdHandle);
372 if (hNative == INVALID_HANDLE_VALUE)
373 return NIL_RTFILE;
374
375 RTFILE hFile = (RTFILE)(uintptr_t)hNative;
376 AssertReturn((HANDLE)(uintptr_t)hFile == hNative, NIL_RTFILE);
377 return hFile;
378}
379
380
381RTR3DECL(int) RTFileSeek(RTFILE hFile, int64_t offSeek, unsigned uMethod, uint64_t *poffActual)
382{
383 static ULONG aulSeekRecode[] =
384 {
385 FILE_BEGIN,
386 FILE_CURRENT,
387 FILE_END,
388 };
389
390 /*
391 * Validate input.
392 */
393 if (uMethod > RTFILE_SEEK_END)
394 {
395 AssertMsgFailed(("Invalid uMethod=%d\n", uMethod));
396 return VERR_INVALID_PARAMETER;
397 }
398
399 /*
400 * Execute the seek.
401 */
402 if (MySetFilePointer(hFile, offSeek, poffActual, aulSeekRecode[uMethod]))
403 return VINF_SUCCESS;
404 return RTErrConvertFromWin32(GetLastError());
405}
406
407
408RTR3DECL(int) RTFileRead(RTFILE hFile, void *pvBuf, size_t cbToRead, size_t *pcbRead)
409{
410 if (cbToRead <= 0)
411 return VINF_SUCCESS;
412 ULONG cbToReadAdj = (ULONG)cbToRead;
413 AssertReturn(cbToReadAdj == cbToRead, VERR_NUMBER_TOO_BIG);
414
415 ULONG cbRead = 0;
416 if (ReadFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToReadAdj, &cbRead, NULL))
417 {
418 if (pcbRead)
419 /* Caller can handle partial reads. */
420 *pcbRead = cbRead;
421 else
422 {
423 /* Caller expects everything to be read. */
424 while (cbToReadAdj > cbRead)
425 {
426 ULONG cbReadPart = 0;
427 if (!ReadFile((HANDLE)RTFileToNative(hFile), (char*)pvBuf + cbRead, cbToReadAdj - cbRead, &cbReadPart, NULL))
428 return RTErrConvertFromWin32(GetLastError());
429 if (cbReadPart == 0)
430 return VERR_EOF;
431 cbRead += cbReadPart;
432 }
433 }
434 return VINF_SUCCESS;
435 }
436
437 /*
438 * If it's a console, we might bump into out of memory conditions in the
439 * ReadConsole call.
440 */
441 DWORD dwErr = GetLastError();
442 if (dwErr == ERROR_NOT_ENOUGH_MEMORY)
443 {
444 ULONG cbChunk = cbToReadAdj / 2;
445 if (cbChunk > 16*_1K)
446 cbChunk = 16*_1K;
447 else
448 cbChunk = RT_ALIGN_32(cbChunk, 256);
449
450 cbRead = 0;
451 while (cbToReadAdj > cbRead)
452 {
453 ULONG cbToRead = RT_MIN(cbChunk, cbToReadAdj - cbRead);
454 ULONG cbReadPart = 0;
455 if (!ReadFile((HANDLE)RTFileToNative(hFile), (char *)pvBuf + cbRead, cbToRead, &cbReadPart, NULL))
456 {
457 /* If we failed because the buffer is too big, shrink it and
458 try again. */
459 dwErr = GetLastError();
460 if ( dwErr == ERROR_NOT_ENOUGH_MEMORY
461 && cbChunk > 8)
462 {
463 cbChunk /= 2;
464 continue;
465 }
466 return RTErrConvertFromWin32(dwErr);
467 }
468 cbRead += cbReadPart;
469
470 /* Return if the caller can handle partial reads, otherwise try
471 fill the buffer all the way up. */
472 if (pcbRead)
473 {
474 *pcbRead = cbRead;
475 break;
476 }
477 if (cbReadPart == 0)
478 return VERR_EOF;
479 }
480 return VINF_SUCCESS;
481 }
482
483 return RTErrConvertFromWin32(dwErr);
484}
485
486
487RTR3DECL(int) RTFileWrite(RTFILE hFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
488{
489 if (cbToWrite <= 0)
490 return VINF_SUCCESS;
491 ULONG cbToWriteAdj = (ULONG)cbToWrite;
492 AssertReturn(cbToWriteAdj == cbToWrite, VERR_NUMBER_TOO_BIG);
493
494 ULONG cbWritten = 0;
495 if (WriteFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToWriteAdj, &cbWritten, NULL))
496 {
497 if (pcbWritten)
498 /* Caller can handle partial writes. */
499 *pcbWritten = cbWritten;
500 else
501 {
502 /* Caller expects everything to be written. */
503 while (cbToWriteAdj > cbWritten)
504 {
505 ULONG cbWrittenPart = 0;
506 if (!WriteFile((HANDLE)RTFileToNative(hFile), (char*)pvBuf + cbWritten,
507 cbToWriteAdj - cbWritten, &cbWrittenPart, NULL))
508 {
509 int rc = RTErrConvertFromWin32(GetLastError());
510 if ( rc == VERR_DISK_FULL
511 && IsBeyondLimit(hFile, cbToWriteAdj - cbWritten, FILE_CURRENT)
512 )
513 rc = VERR_FILE_TOO_BIG;
514 return rc;
515 }
516 if (cbWrittenPart == 0)
517 return VERR_WRITE_ERROR;
518 cbWritten += cbWrittenPart;
519 }
520 }
521 return VINF_SUCCESS;
522 }
523
524 /*
525 * If it's a console, we might bump into out of memory conditions in the
526 * WriteConsole call.
527 */
528 DWORD dwErr = GetLastError();
529 if (dwErr == ERROR_NOT_ENOUGH_MEMORY)
530 {
531 ULONG cbChunk = cbToWriteAdj / 2;
532 if (cbChunk > _32K)
533 cbChunk = _32K;
534 else
535 cbChunk = RT_ALIGN_32(cbChunk, 256);
536
537 cbWritten = 0;
538 while (cbToWriteAdj > cbWritten)
539 {
540 ULONG cbToWrite = RT_MIN(cbChunk, cbToWriteAdj - cbWritten);
541 ULONG cbWrittenPart = 0;
542 if (!WriteFile((HANDLE)RTFileToNative(hFile), (const char *)pvBuf + cbWritten, cbToWrite, &cbWrittenPart, NULL))
543 {
544 /* If we failed because the buffer is too big, shrink it and
545 try again. */
546 dwErr = GetLastError();
547 if ( dwErr == ERROR_NOT_ENOUGH_MEMORY
548 && cbChunk > 8)
549 {
550 cbChunk /= 2;
551 continue;
552 }
553 int rc = RTErrConvertFromWin32(dwErr);
554 if ( rc == VERR_DISK_FULL
555 && IsBeyondLimit(hFile, cbToWriteAdj - cbWritten, FILE_CURRENT))
556 rc = VERR_FILE_TOO_BIG;
557 return rc;
558 }
559 cbWritten += cbWrittenPart;
560
561 /* Return if the caller can handle partial writes, otherwise try
562 write out everything. */
563 if (pcbWritten)
564 {
565 *pcbWritten = cbWritten;
566 break;
567 }
568 if (cbWrittenPart == 0)
569 return VERR_WRITE_ERROR;
570 }
571 return VINF_SUCCESS;
572 }
573
574 int rc = RTErrConvertFromWin32(dwErr);
575 if ( rc == VERR_DISK_FULL
576 && IsBeyondLimit(hFile, cbToWriteAdj - cbWritten, FILE_CURRENT))
577 rc = VERR_FILE_TOO_BIG;
578 return rc;
579}
580
581
582RTR3DECL(int) RTFileFlush(RTFILE hFile)
583{
584 if (!FlushFileBuffers((HANDLE)RTFileToNative(hFile)))
585 {
586 int rc = GetLastError();
587 Log(("FlushFileBuffers failed with %d\n", rc));
588 return RTErrConvertFromWin32(rc);
589 }
590 return VINF_SUCCESS;
591}
592
593
594RTR3DECL(int) RTFileSetSize(RTFILE hFile, uint64_t cbSize)
595{
596 /*
597 * Get current file pointer.
598 */
599 int rc;
600 uint64_t offCurrent;
601 if (MySetFilePointer(hFile, 0, &offCurrent, FILE_CURRENT))
602 {
603 /*
604 * Set new file pointer.
605 */
606 if (MySetFilePointer(hFile, cbSize, NULL, FILE_BEGIN))
607 {
608 /* set file pointer */
609 if (SetEndOfFile((HANDLE)RTFileToNative(hFile)))
610 {
611 /*
612 * Restore file pointer and return.
613 * If the old pointer was beyond the new file end, ignore failure.
614 */
615 if ( MySetFilePointer(hFile, offCurrent, NULL, FILE_BEGIN)
616 || offCurrent > cbSize)
617 return VINF_SUCCESS;
618 }
619
620 /*
621 * Failed, try restoring the file pointer.
622 */
623 rc = GetLastError();
624 MySetFilePointer(hFile, offCurrent, NULL, FILE_BEGIN);
625 }
626 else
627 rc = GetLastError();
628 }
629 else
630 rc = GetLastError();
631
632 return RTErrConvertFromWin32(rc);
633}
634
635
636RTR3DECL(int) RTFileGetSize(RTFILE hFile, uint64_t *pcbSize)
637{
638 /*
639 * GetFileSize works for most handles.
640 */
641 ULARGE_INTEGER Size;
642 Size.LowPart = GetFileSize((HANDLE)RTFileToNative(hFile), &Size.HighPart);
643 if (Size.LowPart != INVALID_FILE_SIZE)
644 {
645 *pcbSize = Size.QuadPart;
646 return VINF_SUCCESS;
647 }
648 int rc = RTErrConvertFromWin32(GetLastError());
649
650 /*
651 * Could it be a volume or a disk?
652 */
653 DISK_GEOMETRY DriveGeo;
654 DWORD cbDriveGeo;
655 if (DeviceIoControl((HANDLE)RTFileToNative(hFile),
656 IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
657 &DriveGeo, sizeof(DriveGeo), &cbDriveGeo, NULL))
658 {
659 if ( DriveGeo.MediaType == FixedMedia
660 || DriveGeo.MediaType == RemovableMedia)
661 {
662 *pcbSize = DriveGeo.Cylinders.QuadPart
663 * DriveGeo.TracksPerCylinder
664 * DriveGeo.SectorsPerTrack
665 * DriveGeo.BytesPerSector;
666
667 GET_LENGTH_INFORMATION DiskLenInfo;
668 DWORD Ignored;
669 if (DeviceIoControl((HANDLE)RTFileToNative(hFile),
670 IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
671 &DiskLenInfo, sizeof(DiskLenInfo), &Ignored, (LPOVERLAPPED)NULL))
672 {
673 /* IOCTL_DISK_GET_LENGTH_INFO is supported -- override cbSize. */
674 *pcbSize = DiskLenInfo.Length.QuadPart;
675 }
676 return VINF_SUCCESS;
677 }
678 }
679
680 /*
681 * Return the GetFileSize result if not a volume/disk.
682 */
683 return rc;
684}
685
686
687RTR3DECL(int) RTFileGetMaxSizeEx(RTFILE hFile, PRTFOFF pcbMax)
688{
689 /** @todo r=bird:
690 * We might have to make this code OS version specific... In the worse
691 * case, we'll have to try GetVolumeInformationByHandle on vista and fall
692 * back on NtQueryVolumeInformationFile(,,,, FileFsAttributeInformation)
693 * else where, and check for known file system names. (For LAN shares we'll
694 * have to figure out the remote file system.) */
695 return VERR_NOT_IMPLEMENTED;
696}
697
698
699RTR3DECL(bool) RTFileIsValid(RTFILE hFile)
700{
701 if (hFile != NIL_RTFILE)
702 {
703 DWORD dwType = GetFileType((HANDLE)RTFileToNative(hFile));
704 switch (dwType)
705 {
706 case FILE_TYPE_CHAR:
707 case FILE_TYPE_DISK:
708 case FILE_TYPE_PIPE:
709 case FILE_TYPE_REMOTE:
710 return true;
711
712 case FILE_TYPE_UNKNOWN:
713 if (GetLastError() == NO_ERROR)
714 return true;
715 break;
716
717 default:
718 break;
719 }
720 }
721 return false;
722}
723
724
725#define LOW_DWORD(u64) ((DWORD)u64)
726#define HIGH_DWORD(u64) (((DWORD *)&u64)[1])
727
728RTR3DECL(int) RTFileLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock)
729{
730 Assert(offLock >= 0);
731
732 /* Check arguments. */
733 if (fLock & ~RTFILE_LOCK_MASK)
734 {
735 AssertMsgFailed(("Invalid fLock=%08X\n", fLock));
736 return VERR_INVALID_PARAMETER;
737 }
738
739 /* Prepare flags. */
740 Assert(RTFILE_LOCK_WRITE);
741 DWORD dwFlags = (fLock & RTFILE_LOCK_WRITE) ? LOCKFILE_EXCLUSIVE_LOCK : 0;
742 Assert(RTFILE_LOCK_WAIT);
743 if (!(fLock & RTFILE_LOCK_WAIT))
744 dwFlags |= LOCKFILE_FAIL_IMMEDIATELY;
745
746 /* Windows structure. */
747 OVERLAPPED Overlapped;
748 memset(&Overlapped, 0, sizeof(Overlapped));
749 Overlapped.Offset = LOW_DWORD(offLock);
750 Overlapped.OffsetHigh = HIGH_DWORD(offLock);
751
752 /* Note: according to Microsoft, LockFileEx API call is available starting from NT 3.5 */
753 if (LockFileEx((HANDLE)RTFileToNative(hFile), dwFlags, 0, LOW_DWORD(cbLock), HIGH_DWORD(cbLock), &Overlapped))
754 return VINF_SUCCESS;
755
756 return RTErrConvertFromWin32(GetLastError());
757}
758
759
760RTR3DECL(int) RTFileChangeLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock)
761{
762 Assert(offLock >= 0);
763
764 /* Check arguments. */
765 if (fLock & ~RTFILE_LOCK_MASK)
766 {
767 AssertMsgFailed(("Invalid fLock=%08X\n", fLock));
768 return VERR_INVALID_PARAMETER;
769 }
770
771 /* Remove old lock. */
772 int rc = RTFileUnlock(hFile, offLock, cbLock);
773 if (RT_FAILURE(rc))
774 return rc;
775
776 /* Set new lock. */
777 rc = RTFileLock(hFile, fLock, offLock, cbLock);
778 if (RT_SUCCESS(rc))
779 return rc;
780
781 /* Try to restore old lock. */
782 unsigned fLockOld = (fLock & RTFILE_LOCK_WRITE) ? fLock & ~RTFILE_LOCK_WRITE : fLock | RTFILE_LOCK_WRITE;
783 rc = RTFileLock(hFile, fLockOld, offLock, cbLock);
784 if (RT_SUCCESS(rc))
785 return VERR_FILE_LOCK_VIOLATION;
786 else
787 return VERR_FILE_LOCK_LOST;
788}
789
790
791RTR3DECL(int) RTFileUnlock(RTFILE hFile, int64_t offLock, uint64_t cbLock)
792{
793 Assert(offLock >= 0);
794
795 if (UnlockFile((HANDLE)RTFileToNative(hFile),
796 LOW_DWORD(offLock), HIGH_DWORD(offLock),
797 LOW_DWORD(cbLock), HIGH_DWORD(cbLock)))
798 return VINF_SUCCESS;
799
800 return RTErrConvertFromWin32(GetLastError());
801}
802
803
804
805RTR3DECL(int) RTFileQueryInfo(RTFILE hFile, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
806{
807 /*
808 * Validate input.
809 */
810 if (hFile == NIL_RTFILE)
811 {
812 AssertMsgFailed(("Invalid hFile=%RTfile\n", hFile));
813 return VERR_INVALID_PARAMETER;
814 }
815 if (!pObjInfo)
816 {
817 AssertMsgFailed(("Invalid pObjInfo=%p\n", pObjInfo));
818 return VERR_INVALID_PARAMETER;
819 }
820 if ( enmAdditionalAttribs < RTFSOBJATTRADD_NOTHING
821 || enmAdditionalAttribs > RTFSOBJATTRADD_LAST)
822 {
823 AssertMsgFailed(("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs));
824 return VERR_INVALID_PARAMETER;
825 }
826
827 /*
828 * Query file info.
829 */
830 HANDLE hHandle = (HANDLE)RTFileToNative(hFile);
831
832 BY_HANDLE_FILE_INFORMATION Data;
833 if (!GetFileInformationByHandle(hHandle, &Data))
834 {
835 /*
836 * Console I/O handles make trouble here. On older windows versions they
837 * end up with ERROR_INVALID_HANDLE when handed to the above API, while on
838 * more recent ones they cause different errors to appear.
839 *
840 * Thus, we must ignore the latter and doubly verify invalid handle claims.
841 * We use the undocumented VerifyConsoleIoHandle to do this, falling back on
842 * GetFileType should it not be there.
843 */
844 DWORD dwErr = GetLastError();
845 if (dwErr == ERROR_INVALID_HANDLE)
846 {
847 static PFNVERIFYCONSOLEIOHANDLE s_pfnVerifyConsoleIoHandle = NULL;
848 static bool volatile s_fInitialized = false;
849 PFNVERIFYCONSOLEIOHANDLE pfnVerifyConsoleIoHandle;
850 if (s_fInitialized)
851 pfnVerifyConsoleIoHandle = s_pfnVerifyConsoleIoHandle;
852 else
853 {
854 pfnVerifyConsoleIoHandle = (PFNVERIFYCONSOLEIOHANDLE)RTLdrGetSystemSymbol("kernel32.dll", "VerifyConsoleIoHandle");
855 ASMAtomicWriteBool(&s_fInitialized, true);
856 }
857 if ( pfnVerifyConsoleIoHandle
858 ? !pfnVerifyConsoleIoHandle(hHandle)
859 : GetFileType(hHandle) == FILE_TYPE_UNKNOWN && GetLastError() != NO_ERROR)
860 return VERR_INVALID_HANDLE;
861 }
862 /*
863 * On Windows 10 and (hopefully) 8.1 we get ERROR_INVALID_FUNCTION with console I/O
864 * handles. We must ignore these just like the above invalid handle error.
865 */
866 else if (dwErr != ERROR_INVALID_FUNCTION)
867 return RTErrConvertFromWin32(dwErr);
868
869 RT_ZERO(Data);
870 Data.dwFileAttributes = RTFS_DOS_NT_DEVICE;
871 }
872
873 /*
874 * Setup the returned data.
875 */
876 pObjInfo->cbObject = ((uint64_t)Data.nFileSizeHigh << 32)
877 | (uint64_t)Data.nFileSizeLow;
878 pObjInfo->cbAllocated = pObjInfo->cbObject;
879
880 Assert(sizeof(uint64_t) == sizeof(Data.ftCreationTime));
881 RTTimeSpecSetNtTime(&pObjInfo->BirthTime, *(uint64_t *)&Data.ftCreationTime);
882 RTTimeSpecSetNtTime(&pObjInfo->AccessTime, *(uint64_t *)&Data.ftLastAccessTime);
883 RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, *(uint64_t *)&Data.ftLastWriteTime);
884 pObjInfo->ChangeTime = pObjInfo->ModificationTime;
885
886 pObjInfo->Attr.fMode = rtFsModeFromDos((Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT, "", 0);
887
888 /*
889 * Requested attributes (we cannot provide anything actually).
890 */
891 switch (enmAdditionalAttribs)
892 {
893 case RTFSOBJATTRADD_NOTHING:
894 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
895 break;
896
897 case RTFSOBJATTRADD_UNIX:
898 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
899 pObjInfo->Attr.u.Unix.uid = ~0U;
900 pObjInfo->Attr.u.Unix.gid = ~0U;
901 pObjInfo->Attr.u.Unix.cHardlinks = Data.nNumberOfLinks ? Data.nNumberOfLinks : 1;
902 pObjInfo->Attr.u.Unix.INodeIdDevice = 0; /** @todo Use the volume serial number (see GetFileInformationByHandle). */
903 pObjInfo->Attr.u.Unix.INodeId = 0; /** @todo Use the fileid (see GetFileInformationByHandle). */
904 pObjInfo->Attr.u.Unix.fFlags = 0;
905 pObjInfo->Attr.u.Unix.GenerationId = 0;
906 pObjInfo->Attr.u.Unix.Device = 0;
907 break;
908
909 case RTFSOBJATTRADD_UNIX_OWNER:
910 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
911 pObjInfo->Attr.u.UnixOwner.uid = ~0U;
912 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
913 break;
914
915 case RTFSOBJATTRADD_UNIX_GROUP:
916 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
917 pObjInfo->Attr.u.UnixGroup.gid = ~0U;
918 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
919 break;
920
921 case RTFSOBJATTRADD_EASIZE:
922 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
923 pObjInfo->Attr.u.EASize.cb = 0;
924 break;
925
926 default:
927 AssertMsgFailed(("Impossible!\n"));
928 return VERR_INTERNAL_ERROR;
929 }
930
931 return VINF_SUCCESS;
932}
933
934
935RTR3DECL(int) RTFileSetTimes(RTFILE hFile, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
936 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
937{
938 if (!pAccessTime && !pModificationTime && !pBirthTime)
939 return VINF_SUCCESS; /* NOP */
940
941 FILETIME CreationTimeFT;
942 PFILETIME pCreationTimeFT = NULL;
943 if (pBirthTime)
944 pCreationTimeFT = RTTimeSpecGetNtFileTime(pBirthTime, &CreationTimeFT);
945
946 FILETIME LastAccessTimeFT;
947 PFILETIME pLastAccessTimeFT = NULL;
948 if (pAccessTime)
949 pLastAccessTimeFT = RTTimeSpecGetNtFileTime(pAccessTime, &LastAccessTimeFT);
950
951 FILETIME LastWriteTimeFT;
952 PFILETIME pLastWriteTimeFT = NULL;
953 if (pModificationTime)
954 pLastWriteTimeFT = RTTimeSpecGetNtFileTime(pModificationTime, &LastWriteTimeFT);
955
956 int rc = VINF_SUCCESS;
957 if (!SetFileTime((HANDLE)RTFileToNative(hFile), pCreationTimeFT, pLastAccessTimeFT, pLastWriteTimeFT))
958 {
959 DWORD Err = GetLastError();
960 rc = RTErrConvertFromWin32(Err);
961 Log(("RTFileSetTimes(%RTfile, %p, %p, %p, %p): SetFileTime failed with lasterr %d (%Rrc)\n",
962 hFile, pAccessTime, pModificationTime, pChangeTime, pBirthTime, Err, rc));
963 }
964 return rc;
965}
966
967
968/* This comes from a source file with a different set of system headers (DDK)
969 * so it can't be declared in a common header, like internal/file.h.
970 */
971extern int rtFileNativeSetAttributes(HANDLE FileHandle, ULONG FileAttributes);
972
973
974RTR3DECL(int) RTFileSetMode(RTFILE hFile, RTFMODE fMode)
975{
976 /*
977 * Normalize the mode and call the API.
978 */
979 fMode = rtFsModeNormalize(fMode, NULL, 0);
980 if (!rtFsModeIsValid(fMode))
981 return VERR_INVALID_PARAMETER;
982
983 ULONG FileAttributes = (fMode & RTFS_DOS_MASK) >> RTFS_DOS_SHIFT;
984 int Err = rtFileNativeSetAttributes((HANDLE)hFile, FileAttributes);
985 if (Err != ERROR_SUCCESS)
986 {
987 int rc = RTErrConvertFromWin32(Err);
988 Log(("RTFileSetMode(%RTfile, %RTfmode): rtFileNativeSetAttributes (0x%08X) failed with err %d (%Rrc)\n",
989 hFile, fMode, FileAttributes, Err, rc));
990 return rc;
991 }
992 return VINF_SUCCESS;
993}
994
995
996RTR3DECL(int) RTFileQueryFsSizes(RTFILE hFile, PRTFOFF pcbTotal, RTFOFF *pcbFree,
997 uint32_t *pcbBlock, uint32_t *pcbSector)
998{
999 /** @todo implement this using NtQueryVolumeInformationFile(hFile,,,,
1000 * FileFsSizeInformation). */
1001 return VERR_NOT_SUPPORTED;
1002}
1003
1004
1005RTR3DECL(int) RTFileDelete(const char *pszFilename)
1006{
1007 PRTUTF16 pwszFilename;
1008 int rc = RTStrToUtf16(pszFilename, &pwszFilename);
1009 if (RT_SUCCESS(rc))
1010 {
1011 if (!DeleteFileW(pwszFilename))
1012 rc = RTErrConvertFromWin32(GetLastError());
1013 RTUtf16Free(pwszFilename);
1014 }
1015
1016 return rc;
1017}
1018
1019
1020RTDECL(int) RTFileRename(const char *pszSrc, const char *pszDst, unsigned fRename)
1021{
1022 /*
1023 * Validate input.
1024 */
1025 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
1026 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
1027 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
1028
1029 /*
1030 * Hand it on to the worker.
1031 */
1032 int rc = rtPathWin32MoveRename(pszSrc, pszDst,
1033 fRename & RTPATHRENAME_FLAGS_REPLACE ? MOVEFILE_REPLACE_EXISTING : 0,
1034 RTFS_TYPE_FILE);
1035
1036 LogFlow(("RTFileMove(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n",
1037 pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
1038 return rc;
1039
1040}
1041
1042
1043RTDECL(int) RTFileMove(const char *pszSrc, const char *pszDst, unsigned fMove)
1044{
1045 /*
1046 * Validate input.
1047 */
1048 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
1049 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
1050 AssertMsgReturn(!(fMove & ~RTFILEMOVE_FLAGS_REPLACE), ("%#x\n", fMove), VERR_INVALID_PARAMETER);
1051
1052 /*
1053 * Hand it on to the worker.
1054 */
1055 int rc = rtPathWin32MoveRename(pszSrc, pszDst,
1056 fMove & RTFILEMOVE_FLAGS_REPLACE
1057 ? MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING
1058 : MOVEFILE_COPY_ALLOWED,
1059 RTFS_TYPE_FILE);
1060
1061 LogFlow(("RTFileMove(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n",
1062 pszSrc, pszSrc, pszDst, pszDst, fMove, rc));
1063 return rc;
1064}
1065
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