Skip to content

Commit d2dacb5

Browse files
committed
fix large file read and write bug
1 parent 9022d9e commit d2dacb5

File tree

9 files changed

+518
-4
lines changed

9 files changed

+518
-4
lines changed

CONTRIBUTORS.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ Leslie Brody (Les1966)
1010
Michael M (M1xa)
1111
Matt Peterson (MattPeterson1)
1212

13-
13+
Illumina Inc.
14+
Gery Vessere ([email protected])

Release/src/streams/windows/fileio.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ size_t _write_file_async(_In_ streams::details::_file_info_impl *fInfo, _In_ str
394394
else
395395
{
396396
pOverlapped->Offset = (DWORD)position;
397-
pOverlapped->OffsetHigh = 0x0;
397+
pOverlapped->OffsetHigh = (DWORD)(position >> 32);
398398
}
399399

400400
_WriteRequest<streams::details::_file_info_impl>* req = nullptr;
@@ -491,7 +491,7 @@ size_t _read_file_async(_In_ streams::details::_file_info_impl *fInfo, _In_ stre
491491
auto pOverlapped = new EXTENDED_OVERLAPPED(_ReadFileCompletionRoutine<streams::details::_file_info_impl>);
492492
pOverlapped->m_scheduler = scheduler.get();
493493
pOverlapped->Offset = (DWORD)offset;
494-
pOverlapped->OffsetHigh = 0;
494+
pOverlapped->OffsetHigh = (DWORD)(offset >> 32);
495495

496496
_ReadRequest<streams::details::_file_info_impl>* req = nullptr;
497497

Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
/****************************** Module Header ******************************\
2+
* Module Name: CppSparseFile.cpp
3+
* Project: CppSparseFile
4+
* Copyright (c) Microsoft Corporation.
5+
*
6+
* CppSparseFile demonstrates the common operations on sparse files. A sparse
7+
* file is a type of computer file that attempts to use file system space more
8+
* efficiently when blocks allocated to the file are mostly empty. This is
9+
* achieved by writing brief information (metadata) representing the empty
10+
* blocks to disk instead of the actual "empty" space which makes up the
11+
* block, using less disk space. You can find in this example the creation of
12+
* sparse file, the detection of sparse attribute, the retrieval of sparse
13+
* file size, and the query of sparse file layout.
14+
*
15+
* This source is subject to the Microsoft Public License.
16+
* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
17+
* All other rights reserved.
18+
*
19+
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
20+
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
21+
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
22+
\***************************************************************************/
23+
24+
#pragma region Includes
25+
#include "stdafx.h"
26+
#include "CppSparseFile.h"
27+
#pragma endregion
28+
29+
30+
/*!
31+
* VolumeSupportsSparseFiles determines if the volume supports sparse streams.
32+
*
33+
* \param lpRootPathName
34+
* Volume root path e.g. C:\
35+
*/
36+
BOOL VolumeSupportsSparseFiles(LPCTSTR lpRootPathName)
37+
{
38+
DWORD dwVolFlags;
39+
GetVolumeInformation(lpRootPathName, NULL, MAX_PATH, NULL, NULL,
40+
&dwVolFlags, NULL, MAX_PATH);
41+
42+
return (dwVolFlags & FILE_SUPPORTS_SPARSE_FILES) ? TRUE : FALSE;
43+
}
44+
45+
46+
/*!
47+
* IsSparseFile determines if a file is sparse.
48+
*
49+
* \param lpFileName
50+
* File name
51+
*/
52+
BOOL IsSparseFile(LPCTSTR lpFileName)
53+
{
54+
// Open the file for read
55+
HANDLE hFile = CreateFile(lpFileName, GENERIC_READ, 0, NULL,
56+
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
57+
if (hFile == INVALID_HANDLE_VALUE)
58+
return FALSE;
59+
60+
// Get file information
61+
BY_HANDLE_FILE_INFORMATION bhfi;
62+
GetFileInformationByHandle(hFile, &bhfi);
63+
CloseHandle(hFile);
64+
65+
return (bhfi.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) ? TRUE : FALSE;
66+
}
67+
68+
69+
/*!
70+
* Get sparse file sizes.
71+
*
72+
* \param lpFileName
73+
* File name
74+
*
75+
* \see
76+
* http://msdn.microsoft.com/en-us/library/aa365276.aspx
77+
*/
78+
BOOL GetSparseFileSize(LPCTSTR lpFileName)
79+
{
80+
// Retrieves the size of the specified file, in bytes. The size includes
81+
// both allocated ranges and sparse ranges.
82+
HANDLE hFile = CreateFile(lpFileName, GENERIC_READ, 0, NULL,
83+
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
84+
if (hFile == INVALID_HANDLE_VALUE)
85+
return FALSE;
86+
LARGE_INTEGER liSparseFileSize;
87+
GetFileSizeEx(hFile, &liSparseFileSize);
88+
89+
// Retrieves the file's actual size on disk, in bytes. The size does not
90+
// include the sparse ranges.
91+
LARGE_INTEGER liSparseFileCompressedSize;
92+
liSparseFileCompressedSize.LowPart = GetCompressedFileSize(lpFileName,
93+
(LPDWORD)&liSparseFileCompressedSize.HighPart);
94+
95+
// Print the result
96+
wprintf(L"\nFile total size: %I64uKB\nActual size on disk: %I64uKB\n",
97+
liSparseFileSize.QuadPart / 1024,
98+
liSparseFileCompressedSize.QuadPart / 1024);
99+
100+
CloseHandle(hFile);
101+
return TRUE;
102+
}
103+
104+
105+
/*!
106+
* Create a sparse file.
107+
*
108+
* \param lpFileName
109+
* The name of the sparse file
110+
*/
111+
HANDLE CreateSparseFile(LPCTSTR lpFileName)
112+
{
113+
// Create a normal file
114+
HANDLE hSparseFile = CreateFile(lpFileName, GENERIC_WRITE, 0, NULL,
115+
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
116+
117+
if (hSparseFile == INVALID_HANDLE_VALUE)
118+
return hSparseFile;
119+
120+
// Use the DeviceIoControl function with the FSCTL_SET_SPARSE control
121+
// code to mark the file as sparse. If you don't mark the file as sparse,
122+
// the FSCTL_SET_ZERO_DATA control code will actually write zero bytes to
123+
// the file instead of marking the region as sparse zero area.
124+
DWORD dwTemp;
125+
DeviceIoControl(hSparseFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dwTemp,
126+
NULL);
127+
128+
return hSparseFile;
129+
}
130+
131+
132+
/*!
133+
* Converting a file region to A sparse zero area.
134+
*
135+
* \param hSparseFile
136+
* Handle of the sparse file
137+
*
138+
* \param start
139+
* Start address of the sparse zero area
140+
*
141+
* \param size
142+
* Size of the sparse zero block. The minimum sparse size is 64KB.
143+
*
144+
* \remarks
145+
* Note that SetSparseRange does not perform actual file I/O, and unlike the
146+
* WriteFile function, it does not move the current file I/O pointer or sets
147+
* the end-of-file pointer. That is, if you want to place a sparse zero block
148+
* in the end of the file, you must move the file pointer accordingly using
149+
* the FileStream.Seek function, otherwise DeviceIoControl will have no effect
150+
*/
151+
void SetSparseRange(HANDLE hSparseFile, LONGLONG start, LONGLONG size)
152+
{
153+
// Specify the starting and the ending address (not the size) of the
154+
// sparse zero block
155+
FILE_ZERO_DATA_INFORMATION fzdi;
156+
fzdi.FileOffset.QuadPart = start;
157+
fzdi.BeyondFinalZero.QuadPart = start + size;
158+
159+
// Mark the range as sparse zero block
160+
DWORD dwTemp;
161+
DeviceIoControl(hSparseFile, FSCTL_SET_ZERO_DATA, &fzdi, sizeof(fzdi),
162+
NULL, 0, &dwTemp, NULL);
163+
}
164+
165+
166+
/*!
167+
* Query the sparse file layout.
168+
*
169+
* \param lpFileName
170+
* File name
171+
*/
172+
BOOL GetSparseRanges(LPCTSTR lpFileName)
173+
{
174+
// Open the file for read
175+
HANDLE hFile = CreateFile(lpFileName, GENERIC_READ, 0, NULL,
176+
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
177+
if (hFile == INVALID_HANDLE_VALUE)
178+
return FALSE;
179+
180+
LARGE_INTEGER liFileSize;
181+
GetFileSizeEx(hFile, &liFileSize);
182+
183+
// Range to be examined (the whole file)
184+
FILE_ALLOCATED_RANGE_BUFFER queryRange;
185+
queryRange.FileOffset.QuadPart = 0;
186+
queryRange.Length = liFileSize;
187+
188+
// Allocated areas info
189+
FILE_ALLOCATED_RANGE_BUFFER allocRanges[1024];
190+
191+
DWORD nbytes;
192+
BOOL fFinished;
193+
_putws(L"\nAllocated ranges in the file:");
194+
do
195+
{
196+
fFinished = DeviceIoControl(hFile, FSCTL_QUERY_ALLOCATED_RANGES,
197+
&queryRange, sizeof(queryRange), allocRanges,
198+
sizeof(allocRanges), &nbytes, NULL);
199+
200+
if (!fFinished)
201+
{
202+
DWORD dwError = GetLastError();
203+
204+
// ERROR_MORE_DATA is the only error that is normal
205+
if (dwError != ERROR_MORE_DATA)
206+
{
207+
wprintf(L"DeviceIoControl failed w/err 0x%08lx\n", dwError);
208+
CloseHandle(hFile);
209+
return FALSE;
210+
}
211+
}
212+
213+
// Calculate the number of records returned
214+
DWORD dwAllocRangeCount = nbytes /
215+
sizeof(FILE_ALLOCATED_RANGE_BUFFER);
216+
217+
// Print each allocated range
218+
for (DWORD i = 0; i < dwAllocRangeCount; i++)
219+
{
220+
wprintf(L"allocated range: [%I64u] [%I64u]\n",
221+
allocRanges[i].FileOffset.QuadPart,
222+
allocRanges[i].Length.QuadPart);
223+
}
224+
225+
// Set starting address and size for the next query
226+
if (!fFinished && dwAllocRangeCount > 0)
227+
{
228+
queryRange.FileOffset.QuadPart =
229+
allocRanges[dwAllocRangeCount - 1].FileOffset.QuadPart +
230+
allocRanges[dwAllocRangeCount - 1].Length.QuadPart;
231+
232+
queryRange.Length.QuadPart = liFileSize.QuadPart -
233+
queryRange.FileOffset.QuadPart;
234+
}
235+
236+
} while (!fFinished);
237+
238+
CloseHandle(hFile);
239+
return TRUE;
240+
}
241+
//
242+
//
243+
//int _tmain(int argc, _TCHAR* argv[])
244+
//{
245+
// /////////////////////////////////////////////////////////////////////////
246+
// // Determine if the volume support sparse streams.
247+
// //
248+
//
249+
// if (!VolumeSupportsSparseFiles(L"C:\\"))
250+
// {
251+
// wprintf(L"Volume %s does not support sparse streams\n", L"C:\\");
252+
// return 1;
253+
// }
254+
//
255+
//
256+
// /////////////////////////////////////////////////////////////////////////
257+
// // Create a sparse file.
258+
// //
259+
//
260+
// LPCWSTR lpFileName = L"SparseFile.tmp";
261+
// wprintf(L"Create sparse file: %s\n", lpFileName);
262+
//
263+
// HANDLE hSparseFile = CreateSparseFile(lpFileName);
264+
// if (hSparseFile == INVALID_HANDLE_VALUE)
265+
// {
266+
// wprintf(L"CreateFile failed w/err 0x%08lx\n", GetLastError());
267+
// return 1;
268+
// }
269+
//
270+
// // Write a large block of data
271+
//
272+
// const DWORD dwBlockLength = 512 * 1024; // 512KB
273+
// BYTE* lpBlock = new BYTE[dwBlockLength];
274+
// for (DWORD i = 0; i < dwBlockLength; i++)
275+
// {
276+
// lpBlock[i] = 0xFF;
277+
// }
278+
// DWORD dwBytesWritten;
279+
// WriteFile(hSparseFile, lpBlock, dwBlockLength, &dwBytesWritten, NULL);
280+
// delete[] lpBlock;
281+
//
282+
// // Set some sparse ranges in the block
283+
//
284+
// SetSparseRange(hSparseFile, 0, 64 * 1024 /*64KB*/);
285+
// SetSparseRange(hSparseFile, 128 * 1024, 128 * 1024);
286+
//
287+
// // Set sparse block at the end of the file
288+
//
289+
// // 1GB sparse zeros are extended to the end of the file
290+
// SetFilePointer(hSparseFile, 0x40000000 /*1GB*/, NULL, FILE_END);
291+
// SetEndOfFile(hSparseFile);
292+
//
293+
// // Flush and close the file
294+
// CloseHandle(hSparseFile);
295+
//
296+
//
297+
// /////////////////////////////////////////////////////////////////////////
298+
// // Determine if a file is sparse.
299+
// //
300+
//
301+
// BOOL fIsSparse = IsSparseFile(lpFileName);
302+
// wprintf(L"The file is%s sparse\n", fIsSparse ? L"" : L" not");
303+
//
304+
//
305+
// /////////////////////////////////////////////////////////////////////////
306+
// // Get file size.
307+
// //
308+
//
309+
// GetSparseFileSize(lpFileName);
310+
//
311+
//
312+
// /////////////////////////////////////////////////////////////////////////
313+
// // Query the sparse file layout.
314+
// //
315+
//
316+
// GetSparseRanges(lpFileName);
317+
//
318+
//
319+
// return 0;
320+
//}

0 commit comments

Comments
 (0)