VirtualBox

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

Last change on this file since 45733 was 39027, checked in by vboxsync, 13 years ago

RTFileGetSize/win: Made it grok volume & disks (some disks anyway).

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