// FSDrives.cpp #include "StdAfx.h" #include "../../../../C/Alloc.h" #include "../../../Common/ComTry.h" #include "../../../Common/Defs.h" #include "../../../Common/IntToString.h" #include "../../../Common/StringConvert.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/FileIO.h" #include "../../../Windows/FileName.h" #include "../../../Windows/FileSystem.h" #include "../../../Windows/PropVariant.h" #include "../../PropID.h" #include "FSDrives.h" #include "FSFolder.h" #include "LangUtils.h" #include "SysIconUtils.h" #include "resource.h" using namespace NWindows; using namespace NFile; using namespace NFind; static const char * const kVolPrefix = "\\\\.\\"; static const char * const kSuperPrefix = "\\\\?\\"; FString CDriveInfo::GetDeviceFileIoName() const { FString f (kVolPrefix); f += Name; return f; } struct CPhysTempBuffer { void *buffer; CPhysTempBuffer(): buffer(NULL) {} ~CPhysTempBuffer() { MidFree(buffer); } }; static HRESULT CopyFileSpec(CFSTR fromPath, CFSTR toPath, bool writeToDisk, UInt64 fileSize, UInt32 bufferSize, UInt64 progressStart, IProgress *progress) { NIO::CInFile inFile; if (!inFile.Open(fromPath)) return GetLastError_noZero_HRESULT(); if (fileSize == (UInt64)(Int64)-1) { if (!inFile.GetLength(fileSize)) return GetLastError_noZero_HRESULT(); } NIO::COutFile outFile; if (writeToDisk) { if (!outFile.Open(toPath, FILE_SHARE_WRITE, OPEN_EXISTING, 0)) return GetLastError_noZero_HRESULT(); } else if (!outFile.Create_ALWAYS(toPath)) return GetLastError_noZero_HRESULT(); CPhysTempBuffer tempBuffer; tempBuffer.buffer = MidAlloc(bufferSize); if (!tempBuffer.buffer) return E_OUTOFMEMORY; for (UInt64 pos = 0; pos < fileSize;) { { const UInt64 progressCur = progressStart + pos; RINOK(progress->SetCompleted(&progressCur)) } const UInt64 rem = fileSize - pos; UInt32 curSize = (UInt32)MyMin(rem, (UInt64)bufferSize); UInt32 processedSize; if (!inFile.Read(tempBuffer.buffer, curSize, processedSize)) return GetLastError_noZero_HRESULT(); if (processedSize == 0) break; curSize = processedSize; if (writeToDisk) { const UInt32 kMask = 0x1FF; curSize = (curSize + kMask) & ~kMask; if (curSize > bufferSize) return E_FAIL; } if (!outFile.Write(tempBuffer.buffer, curSize, processedSize)) return GetLastError_noZero_HRESULT(); if (curSize != processedSize) return E_FAIL; pos += curSize; } return S_OK; } static const Byte kProps[] = { kpidName, // kpidOutName, kpidTotalSize, kpidFreeSpace, kpidType, kpidVolumeName, kpidFileSystem, kpidClusterSize }; static const char * const kDriveTypes[] = { "Unknown" , "No Root Dir" , "Removable" , "Fixed" , "Remote" , "CD-ROM" , "RAM disk" }; Z7_COM7F_IMF(CFSDrives::LoadItems()) { _drives.Clear(); FStringVector driveStrings; MyGetLogicalDriveStrings(driveStrings); FOR_VECTOR (i, driveStrings) { CDriveInfo di; const FString &driveName = driveStrings[i]; di.FullSystemName = driveName; if (!driveName.IsEmpty()) di.Name.SetFrom(driveName, driveName.Len() - 1); di.ClusterSize = 0; di.DriveSize = 0; di.FreeSpace = 0; di.DriveType = NSystem::MyGetDriveType(driveName); bool needRead = true; if (di.DriveType == DRIVE_CDROM || di.DriveType == DRIVE_REMOVABLE) { /* DWORD dwSerialNumber;` if (!::GetVolumeInformation(di.FullSystemName, NULL, 0, &dwSerialNumber, NULL, NULL, NULL, 0)) */ { needRead = false; } } if (needRead) { DWORD volumeSerialNumber, maximumComponentLength, fileSystemFlags; NSystem::MyGetVolumeInformation(driveName, di.VolumeName, &volumeSerialNumber, &maximumComponentLength, &fileSystemFlags, di.FileSystemName); NSystem::MyGetDiskFreeSpace(driveName, di.ClusterSize, di.DriveSize, di.FreeSpace); di.KnownSizes = true; di.KnownSize = true; } _drives.Add(di); } if (_volumeMode) { // we must use IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS for (unsigned n = 0; n < 16; n++) // why 16 ? { FString name ("PhysicalDrive"); name.Add_UInt32(n); FString fullPath (kVolPrefix); fullPath += name; CFileInfo fi; if (!fi.Find(fullPath)) continue; CDriveInfo di; di.Name = name; // if (_volumeMode == true) we use CDriveInfo::FullSystemName only in GetSystemIconIndex(). // And we need name without "\\\\.\\" prefix in GetSystemIconIndex(). // So we don't set di.FullSystemName = fullPath; di.FullSystemName = name; di.ClusterSize = 0; di.DriveSize = fi.Size; di.FreeSpace = 0; di.DriveType = 0; di.IsPhysicalDrive = true; di.KnownSize = true; _drives.Add(di); } } return S_OK; } Z7_COM7F_IMF(CFSDrives::GetNumberOfItems(UInt32 *numItems)) { *numItems = _drives.Size(); return S_OK; } Z7_COM7F_IMF(CFSDrives::GetProperty(UInt32 itemIndex, PROPID propID, PROPVARIANT *value)) { if (itemIndex >= _drives.Size()) return E_INVALIDARG; NCOM::CPropVariant prop; const CDriveInfo &di = _drives[itemIndex]; switch (propID) { case kpidIsDir: prop = !_volumeMode; break; case kpidName: prop = fs2us(di.Name); break; case kpidOutName: if (!di.Name.IsEmpty() && di.Name.Back() == ':') { FString s = di.Name; s.DeleteBack(); AddExt(s, itemIndex); prop = fs2us(s); } break; case kpidTotalSize: if (di.KnownSize) prop = di.DriveSize; break; case kpidFreeSpace: if (di.KnownSizes) prop = di.FreeSpace; break; case kpidClusterSize: if (di.KnownSizes) prop = di.ClusterSize; break; case kpidType: if (di.DriveType < Z7_ARRAY_SIZE(kDriveTypes)) prop = kDriveTypes[di.DriveType]; break; case kpidVolumeName: prop = di.VolumeName; break; case kpidFileSystem: prop = di.FileSystemName; break; } prop.Detach(value); return S_OK; } HRESULT CFSDrives::BindToFolderSpec(CFSTR name, IFolderFolder **resultFolder) { *resultFolder = NULL; if (_volumeMode) return S_OK; NFsFolder::CFSFolder *fsFolderSpec = new NFsFolder::CFSFolder; CMyComPtr subFolder = fsFolderSpec; FString path; if (_superMode) path = kSuperPrefix; path += name; RINOK(fsFolderSpec->Init(path)) *resultFolder = subFolder.Detach(); return S_OK; } Z7_COM7F_IMF(CFSDrives::BindToFolder(UInt32 index, IFolderFolder **resultFolder)) { *resultFolder = NULL; if (index >= _drives.Size()) return E_INVALIDARG; const CDriveInfo &di = _drives[index]; /* if (_volumeMode) { *resultFolder = 0; CPhysDriveFolder *folderSpec = new CPhysDriveFolder; CMyComPtr subFolder = folderSpec; RINOK(folderSpec->Init(di.Name)); *resultFolder = subFolder.Detach(); return S_OK; } */ return BindToFolderSpec(di.FullSystemName, resultFolder); } Z7_COM7F_IMF(CFSDrives::BindToFolder(const wchar_t *name, IFolderFolder **resultFolder)) { return BindToFolderSpec(us2fs(name), resultFolder); } Z7_COM7F_IMF(CFSDrives::BindToParentFolder(IFolderFolder **resultFolder)) { *resultFolder = NULL; return S_OK; } IMP_IFolderFolder_Props(CFSDrives) Z7_COM7F_IMF(CFSDrives::GetFolderProperty(PROPID propID, PROPVARIANT *value)) { COM_TRY_BEGIN NCOM::CPropVariant prop; switch (propID) { case kpidType: prop = "FSDrives"; break; case kpidPath: if (_volumeMode) prop = kVolPrefix; else if (_superMode) prop = kSuperPrefix; else prop = (UString)LangString(IDS_COMPUTER) + WCHAR_PATH_SEPARATOR; break; } prop.Detach(value); return S_OK; COM_TRY_END } Z7_COM7F_IMF(CFSDrives::GetSystemIconIndex(UInt32 index, Int32 *iconIndex)) { *iconIndex = -1; const CDriveInfo &di = _drives[index]; return Shell_GetFileInfo_SysIconIndex_for_Path_return_HRESULT( di.FullSystemName, _volumeMode ? FILE_ATTRIBUTE_ARCHIVE: FILE_ATTRIBUTE_DIRECTORY, iconIndex); } void CFSDrives::AddExt(FString &s, unsigned index) const { s.Add_Dot(); const CDriveInfo &di = _drives[index]; UString n = di.FileSystemName; n.MakeLower_Ascii(); const char *ext; if (di.DriveType == DRIVE_CDROM) ext = "iso"; else { unsigned i; for (i = 0; i < n.Len(); i++) { const wchar_t c = n[i]; if (c < 'a' || c > 'z') break; } if (i != 0) { n.DeleteFrom(i); s += us2fs(n); return; } ext = "img"; } /* if (n.IsPrefixedBy_Ascii_NoCase("NTFS")) ext = "ntfs"; else if (n.IsPrefixedBy_Ascii_NoCase("UDF")) ext = "udf"; else if (n.IsPrefixedBy_Ascii_NoCase("exFAT")) ext = "exfat"; */ s += ext; } HRESULT CFSDrives::GetFileSize(unsigned index, UInt64& fileSize) const { #ifdef Z7_DEVICE_FILE NIO::CInFile inFile; if (!inFile.Open(_drives[index].GetDeviceFileIoName())) return GetLastError_noZero_HRESULT(); if (inFile.SizeDefined) { fileSize = inFile.Size; return S_OK; } #else UNUSED_VAR(index) #endif fileSize = 0; return E_FAIL; } Z7_COM7F_IMF(CFSDrives::CopyTo(Int32 moveMode, const UInt32 *indices, UInt32 numItems, Int32 /* includeAltStreams */, Int32 /* replaceAltStreamColon */, const wchar_t *path, IFolderOperationsExtractCallback *callback)) { if (numItems == 0) return S_OK; if (moveMode) return E_NOTIMPL; if (!_volumeMode) return E_NOTIMPL; UInt64 totalSize = 0; UInt32 i; for (i = 0; i < numItems; i++) { const CDriveInfo &di = _drives[indices[i]]; if (di.KnownSize) totalSize += di.DriveSize; } RINOK(callback->SetTotal(totalSize)) RINOK(callback->SetNumFiles(numItems)) const FString destPath = us2fs(path); if (destPath.IsEmpty()) return E_INVALIDARG; const bool isAltDest = NName::IsAltPathPrefix(destPath); const bool isDirectPath = (!isAltDest && !IsPathSepar(destPath.Back())); if (isDirectPath) { if (numItems > 1) return E_INVALIDARG; } UInt64 completedSize = 0; RINOK(callback->SetCompleted(&completedSize)) for (i = 0; i < numItems; i++) { const unsigned index = indices[i]; const CDriveInfo &di = _drives[index]; FString destPath2 = destPath; if (!isDirectPath) { FString destName = di.Name; if (!destName.IsEmpty() && destName.Back() == ':') { destName.DeleteBack(); AddExt(destName, index); } destPath2 += destName; } const FString srcPath = di.GetDeviceFileIoName(); UInt64 fileSize = 0; if (GetFileSize(index, fileSize) != S_OK) { return E_FAIL; } if (!di.KnownSize) { totalSize += fileSize; RINOK(callback->SetTotal(totalSize)) } Int32 writeAskResult; CMyComBSTR destPathResult; RINOK(callback->AskWrite(fs2us(srcPath), BoolToInt(false), NULL, &fileSize, fs2us(destPath2), &destPathResult, &writeAskResult)) if (!IntToBool(writeAskResult)) { if (totalSize >= fileSize) totalSize -= fileSize; RINOK(callback->SetTotal(totalSize)) continue; } RINOK(callback->SetCurrentFilePath(fs2us(srcPath))) const UInt32 kBufferSize = (4 << 20); const UInt32 bufferSize = (di.DriveType == DRIVE_REMOVABLE) ? (18 << 10) * 4 : kBufferSize; RINOK(CopyFileSpec(srcPath, us2fs(destPathResult), false, fileSize, bufferSize, completedSize, callback)) completedSize += fileSize; } return S_OK; } Z7_COM7F_IMF(CFSDrives::CopyFrom(Int32 /* moveMode */, const wchar_t * /* fromFolderPath */, const wchar_t * const * /* itemsPaths */, UInt32 /* numItems */, IProgress * /* progress */)) { return E_NOTIMPL; } Z7_COM7F_IMF(CFSDrives::CopyFromFile(UInt32 /* index */, const wchar_t * /* fullFilePath */, IProgress * /* progress */)) { return E_NOTIMPL; } Z7_COM7F_IMF(CFSDrives::CreateFolder(const wchar_t * /* name */, IProgress * /* progress */)) { return E_NOTIMPL; } Z7_COM7F_IMF(CFSDrives::CreateFile(const wchar_t * /* name */, IProgress * /* progress */)) { return E_NOTIMPL; } Z7_COM7F_IMF(CFSDrives::Rename(UInt32 /* index */, const wchar_t * /* newName */, IProgress * /* progress */)) { return E_NOTIMPL; } Z7_COM7F_IMF(CFSDrives::Delete(const UInt32 * /* indices */, UInt32 /* numItems */, IProgress * /* progress */)) { return E_NOTIMPL; } Z7_COM7F_IMF(CFSDrives::SetProperty(UInt32 /* index */, PROPID /* propID */, const PROPVARIANT * /* value */, IProgress * /* progress */)) { return E_NOTIMPL; }