-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathCppSparseFile.cpp
227 lines (197 loc) · 7.54 KB
/
CppSparseFile.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/****************************** Module Header ******************************\
* Module Name: CppSparseFile.cpp
* Project: CppSparseFile
* URL: http://code.msdn.microsoft.com/windowsapps/CppSparseFile-7f28156b
* Copyright (c) Microsoft Corporation.
*
* CppSparseFile demonstrates the common operations on sparse files. A sparse
* file is a type of computer file that attempts to use file system space more
* efficiently when blocks allocated to the file are mostly empty. This is
* achieved by writing brief information (metadata) representing the empty
* blocks to disk instead of the actual "empty" space which makes up the
* block, using less disk space. You can find in this example the creation of
* sparse file, the detection of sparse attribute, the retrieval of sparse
* file size, and the query of sparse file layout.
*
* This source is subject to the Microsoft Public License.
* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
* All other rights reserved.
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/
#pragma region Includes
#include "stdafx.h"
#include "CppSparseFile.h"
#pragma endregion
/*!
* VolumeSupportsSparseFiles determines if the volume supports sparse streams.
*
* \param lpRootPathName
* Volume root path e.g. C:\
*/
BOOL VolumeSupportsSparseFiles(LPCTSTR lpRootPathName)
{
DWORD dwVolFlags;
GetVolumeInformation(lpRootPathName, NULL, MAX_PATH, NULL, NULL, &dwVolFlags, NULL, MAX_PATH);
return (dwVolFlags & FILE_SUPPORTS_SPARSE_FILES) ? TRUE : FALSE;
}
/*!
* IsSparseFile determines if a file is sparse.
*
* \param lpFileName
* File name
*/
BOOL IsSparseFile(LPCTSTR lpFileName)
{
// Open the file for read
HANDLE hFile = CreateFile(lpFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) return FALSE;
// Get file information
BY_HANDLE_FILE_INFORMATION bhfi;
GetFileInformationByHandle(hFile, &bhfi);
CloseHandle(hFile);
return (bhfi.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) ? TRUE : FALSE;
}
/*!
* Get sparse file sizes.
*
* \param lpFileName
* File name
*
* \see
* http://msdn.microsoft.com/en-us/library/aa365276.aspx
*/
BOOL GetSparseFileSize(LPCTSTR lpFileName)
{
// Retrieves the size of the specified file, in bytes. The size includes
// both allocated ranges and sparse ranges.
HANDLE hFile = CreateFile(lpFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) return FALSE;
LARGE_INTEGER liSparseFileSize;
GetFileSizeEx(hFile, &liSparseFileSize);
// Retrieves the file's actual size on disk, in bytes. The size does not
// include the sparse ranges.
LARGE_INTEGER liSparseFileCompressedSize;
liSparseFileCompressedSize.LowPart =
GetCompressedFileSize(lpFileName, (LPDWORD)&liSparseFileCompressedSize.HighPart);
// Print the result
wprintf(L"\nFile total size: %I64uKB\nActual size on disk: %I64uKB\n",
liSparseFileSize.QuadPart / 1024,
liSparseFileCompressedSize.QuadPart / 1024);
CloseHandle(hFile);
return TRUE;
}
/*!
* Create a sparse file.
*
* \param lpFileName
* The name of the sparse file
*/
HANDLE CreateSparseFile(LPCTSTR lpFileName)
{
// Create a normal file
HANDLE hSparseFile = CreateFile(lpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hSparseFile == INVALID_HANDLE_VALUE) return hSparseFile;
// Use the DeviceIoControl function with the FSCTL_SET_SPARSE control
// code to mark the file as sparse. If you don't mark the file as sparse,
// the FSCTL_SET_ZERO_DATA control code will actually write zero bytes to
// the file instead of marking the region as sparse zero area.
DWORD dwTemp;
DeviceIoControl(hSparseFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dwTemp, NULL);
return hSparseFile;
}
/*!
* Converting a file region to A sparse zero area.
*
* \param hSparseFile
* Handle of the sparse file
*
* \param start
* Start address of the sparse zero area
*
* \param size
* Size of the sparse zero block. The minimum sparse size is 64KB.
*
* \remarks
* Note that SetSparseRange does not perform actual file I/O, and unlike the
* WriteFile function, it does not move the current file I/O pointer or sets
* the end-of-file pointer. That is, if you want to place a sparse zero block
* in the end of the file, you must move the file pointer accordingly using
* the FileStream.Seek function, otherwise DeviceIoControl will have no effect
*/
void SetSparseRange(HANDLE hSparseFile, LONGLONG start, LONGLONG size)
{
// Specify the starting and the ending address (not the size) of the
// sparse zero block
FILE_ZERO_DATA_INFORMATION fzdi;
fzdi.FileOffset.QuadPart = start;
fzdi.BeyondFinalZero.QuadPart = start + size;
// Mark the range as sparse zero block
DWORD dwTemp;
DeviceIoControl(hSparseFile, FSCTL_SET_ZERO_DATA, &fzdi, sizeof(fzdi), NULL, 0, &dwTemp, NULL);
}
/*!
* Query the sparse file layout.
*
* \param lpFileName
* File name
*/
BOOL GetSparseRanges(LPCTSTR lpFileName)
{
// Open the file for read
HANDLE hFile = CreateFile(lpFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) return FALSE;
LARGE_INTEGER liFileSize;
GetFileSizeEx(hFile, &liFileSize);
// Range to be examined (the whole file)
FILE_ALLOCATED_RANGE_BUFFER queryRange;
queryRange.FileOffset.QuadPart = 0;
queryRange.Length = liFileSize;
// Allocated areas info
FILE_ALLOCATED_RANGE_BUFFER allocRanges[1024];
DWORD nbytes;
BOOL fFinished;
_putws(L"\nAllocated ranges in the file:");
do
{
fFinished = DeviceIoControl(hFile,
FSCTL_QUERY_ALLOCATED_RANGES,
&queryRange,
sizeof(queryRange),
allocRanges,
sizeof(allocRanges),
&nbytes,
NULL);
if (!fFinished)
{
DWORD dwError = GetLastError();
// ERROR_MORE_DATA is the only error that is normal
if (dwError != ERROR_MORE_DATA)
{
wprintf(L"DeviceIoControl failed w/err 0x%08lx\n", dwError);
CloseHandle(hFile);
return FALSE;
}
}
// Calculate the number of records returned
DWORD dwAllocRangeCount = nbytes / sizeof(FILE_ALLOCATED_RANGE_BUFFER);
// Print each allocated range
for (DWORD i = 0; i < dwAllocRangeCount; i++)
{
wprintf(L"allocated range: [%I64u] [%I64u]\n",
allocRanges[i].FileOffset.QuadPart,
allocRanges[i].Length.QuadPart);
}
// Set starting address and size for the next query
if (!fFinished && dwAllocRangeCount > 0)
{
queryRange.FileOffset.QuadPart = allocRanges[dwAllocRangeCount - 1].FileOffset.QuadPart +
allocRanges[dwAllocRangeCount - 1].Length.QuadPart;
queryRange.Length.QuadPart = liFileSize.QuadPart - queryRange.FileOffset.QuadPart;
}
} while (!fFinished);
CloseHandle(hFile);
return TRUE;
}