-- 作者:卷积内核
-- 发布时间:6/29/2009 11:14:00 AM
-- 操作稀疏文件源代码
#define _WIN32_WINNT 0x0500 // Windows 2000 or later #include <windows.h> #include <stdio.h> #define ALLOWED_ATTRIBUTES (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) void PrintError(DWORD dwErr) { char szMsg[256]; DWORD dwFlags = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_SYSTEM; if (!::FormatMessage(dwFlags, NULL, dwErr, 0, szMsg, sizeof(szMsg), NULL)) strcpy(szMsg, "Unknown error."); printf(szMsg); printf("\n"); } void CopyRange(HANDLE hFrom, HANDLE hTo, LARGE_INTEGER iPos, LARGE_INTEGER iSize) { BYTE buf[64*1024]; DWORD dwBytesRead, dwBytesWritten; LONG iPosHigh = iPos.HighPart; DWORD dr = ::SetFilePointer(hFrom, iPos.LowPart, &iPosHigh, FILE_BEGIN); if (dr == INVALID_SET_FILE_POINTER) { dr = ::GetLastError(); if (dr != NO_ERROR) throw dr; } iPosHigh = iPos.HighPart; dr = ::SetFilePointer(hTo, iPos.LowPart, &iPosHigh, FILE_BEGIN); if (dr == INVALID_SET_FILE_POINTER) { dr = ::GetLastError(); if (dr != NO_ERROR) throw dr; } while (iSize.QuadPart > 0) { if (!::ReadFile(hFrom, buf, (DWORD)min(sizeof(buf), iSize.QuadPart), &dwBytesRead, NULL)) throw ::GetLastError(); if (!::WriteFile(hTo, buf, dwBytesRead, &dwBytesWritten, NULL)) throw ::GetLastError(); if (dwBytesWritten < dwBytesRead) throw (DWORD)ERROR_HANDLE_DISK_FULL; iSize.QuadPart -= dwBytesRead; } } void main(int argc, char *argv[]) { HANDLE hInFile, hOutFile; int iRetCode = EXIT_SUCCESS; if (argc != 3) { printf("\nStream copy program:.\n\nUsage:\n CS fromstream tostream\n\nExample:\n CS c:\\some.txt d:\\file.dat:text\n\n"); exit(EXIT_SUCCESS); } try { BY_HANDLE_FILE_INFORMATION bhfi; char szPath[MAX_PATH], szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szFName[_MAX_FNAME], szExt[_MAX_EXT]; LPSTR pszName; char szVolName[MAX_PATH], szFSName[MAX_PATH]; DWORD dwSN, dwMaxLen, dwVolFlags; BOOL bSparseCopy = TRUE; DWORD dr; // Source drive supports sparse files? if (!::GetFullPathName(argv[1], MAX_PATH, szPath, &pszName)) throw ::GetLastError(); _splitpath(szPath, szDrive, szDir, szFName, szExt); if (strlen(szDrive)) { strcat(szDrive, "\\"); if (!::GetVolumeInformation(szDrive, szVolName, MAX_PATH, &dwSN, &dwMaxLen, &dwVolFlags, szFSName, MAX_PATH)) throw ::GetLastError(); bSparseCopy = dwVolFlags & FILE_SUPPORTS_SPARSE_FILES; } // Target drive supports sparse files? if (bSparseCopy) { // No need to check if source drive does not support if (!::GetFullPathName(argv[2], MAX_PATH, szPath, &pszName)) throw ::GetLastError(); _splitpath(szPath, szDrive, szDir, szFName, szExt); if (strlen(szDrive)) { strcat(szDrive, "\\"); if (!::GetVolumeInformation(szDrive, szVolName, MAX_PATH, &dwSN, &dwMaxLen, &dwVolFlags, szFSName, MAX_PATH)) throw ::GetLastError(); bSparseCopy = dwVolFlags & FILE_SUPPORTS_SPARSE_FILES; } } hInFile = ::CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hInFile == INVALID_HANDLE_VALUE) throw ::GetLastError(); if (!::GetFileInformationByHandle(hInFile, &bhfi)) throw ::GetLastError(); bSparseCopy = bSparseCopy && (bhfi.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE); // Check if the source file is sparse hOutFile = ::CreateFile(argv[2], GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hOutFile == INVALID_HANDLE_VALUE) throw ::GetLastError(); if (bSparseCopy) { // Perform sparse-aware copy // Get file size LARGE_INTEGER iFileSize; iFileSize.LowPart = ::GetFileSize(hInFile, (LPDWORD)&iFileSize.HighPart); if (iFileSize.LowPart == (DWORD)-1) { dr = ::GetLastError(); if (dr != NO_ERROR) throw dr; } DWORD dwTemp; if (!::DeviceIoControl(hOutFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dwTemp, NULL)) throw ::GetLastError(); // Main loop querying and copying allocated area ranges FILE_ALLOCATED_RANGE_BUFFER queryrange; FILE_ALLOCATED_RANGE_BUFFER ranges[1024]; DWORD nbytes, n, i; BOOL br; queryrange.FileOffset.QuadPart = 0; queryrange.Length = iFileSize; do { // This loop executes more than once only if the file has more than 1024 allocated areas br = ::DeviceIoControl(hInFile, FSCTL_QUERY_ALLOCATED_RANGES, &queryrange, sizeof(queryrange), ranges, sizeof(ranges), &nbytes, NULL); if (!br) { dr = ::GetLastError(); if (dr != ERROR_MORE_DATA) throw dr; } n = nbytes / sizeof(FILE_ALLOCATED_RANGE_BUFFER); // Calculate the number of records returned for (i=0; i<n; i++) // Main loop CopyRange(hInFile, hOutFile, ranges[i].FileOffset, ranges[i].Length); // Set starting address and size for the next query if (!br && n > 0) { queryrange.FileOffset.QuadPart = ranges[n-1].FileOffset.QuadPart + ranges[n-1].Length.QuadPart; queryrange.Length.QuadPart = iFileSize.QuadPart - queryrange.FileOffset.QuadPart; } } while (!br); // Continue loop if ERROR_MORE_DATA // Set end of file (required because there can be a sparse area in the end) dr = ::SetFilePointer(hOutFile, iFileSize.LowPart, &iFileSize.HighPart, FILE_BEGIN); if (dr == INVALID_SET_FILE_POINTER) { dr = ::GetLastError(); if (dr != NO_ERROR) throw dr; } if (!::SetEndOfFile(hOutFile)) throw ::GetLastError(); } else { // Not a sparse file, or sparse copy is impossible BYTE buf[64*1024]; DWORD dwBytesRead, dwBytesWritten; do { if (!::ReadFile(hInFile, buf, sizeof(buf), &dwBytesRead, NULL)) throw ::GetLastError(); if (dwBytesRead) { if (!::WriteFile(hOutFile, buf, dwBytesRead, &dwBytesWritten, NULL)) throw ::GetLastError(); if (dwBytesWritten < dwBytesRead) throw (DWORD)ERROR_HANDLE_DISK_FULL; } } while (dwBytesRead == sizeof(buf)); } ::CloseHandle(hInFile); // Set output file attributes if (!::SetFileTime(hOutFile, &bhfi.ftCreationTime, &bhfi.ftLastAccessTime, &bhfi.ftLastWriteTime)) throw ::GetLastError(); ::CloseHandle(hOutFile); if (!::SetFileAttributes(argv[2], bhfi.dwFileAttributes & ALLOWED_ATTRIBUTES)) throw ::GetLastError(); } catch (DWORD dwErrCode) { PrintError(dwErrCode); iRetCode = EXIT_FAILURE; } exit(iRetCode); }
|