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" \n File total size: %I64uKB\n Actual 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" \n Allocated 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