1 // FSDrives.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../../C/Alloc.h"
6
7 #include "../../../Common/ComTry.h"
8 #include "../../../Common/Defs.h"
9 #include "../../../Common/IntToString.h"
10 #include "../../../Common/StringConvert.h"
11
12 #include "../../../Windows/FileDir.h"
13 #include "../../../Windows/FileIO.h"
14 #include "../../../Windows/FileName.h"
15 #include "../../../Windows/FileSystem.h"
16 #include "../../../Windows/PropVariant.h"
17
18 #include "../../PropID.h"
19
20 #include "FSDrives.h"
21 #include "FSFolder.h"
22 #include "LangUtils.h"
23 #include "SysIconUtils.h"
24
25 #include "resource.h"
26
27 using namespace NWindows;
28 using namespace NFile;
29 using namespace NFind;
30
31 static const char * const kVolPrefix = "\\\\.\\";
32 static const char * const kSuperPrefix = "\\\\?\\";
33
GetDeviceFileIoName() const34 FString CDriveInfo::GetDeviceFileIoName() const
35 {
36 FString f (kVolPrefix);
37 f += Name;
38 return f;
39 }
40
41 struct CPhysTempBuffer
42 {
43 void *buffer;
CPhysTempBufferCPhysTempBuffer44 CPhysTempBuffer(): buffer(0) {}
~CPhysTempBufferCPhysTempBuffer45 ~CPhysTempBuffer() { MidFree(buffer); }
46 };
47
CopyFileSpec(CFSTR fromPath,CFSTR toPath,bool writeToDisk,UInt64 fileSize,UInt32 bufferSize,UInt64 progressStart,IProgress * progress)48 static HRESULT CopyFileSpec(CFSTR fromPath, CFSTR toPath, bool writeToDisk, UInt64 fileSize,
49 UInt32 bufferSize, UInt64 progressStart, IProgress *progress)
50 {
51 NIO::CInFile inFile;
52 if (!inFile.Open(fromPath))
53 return GetLastError();
54 if (fileSize == (UInt64)(Int64)-1)
55 {
56 if (!inFile.GetLength(fileSize))
57 ::GetLastError();
58 }
59
60 NIO::COutFile outFile;
61 if (writeToDisk)
62 {
63 if (!outFile.Open(toPath, FILE_SHARE_WRITE, OPEN_EXISTING, 0))
64 return GetLastError();
65 }
66 else
67 if (!outFile.Create(toPath, true))
68 return GetLastError();
69
70 CPhysTempBuffer tempBuffer;
71 tempBuffer.buffer = MidAlloc(bufferSize);
72 if (!tempBuffer.buffer)
73 return E_OUTOFMEMORY;
74
75 for (UInt64 pos = 0; pos < fileSize;)
76 {
77 UInt64 progressCur = progressStart + pos;
78 RINOK(progress->SetCompleted(&progressCur));
79 UInt64 rem = fileSize - pos;
80 UInt32 curSize = (UInt32)MyMin(rem, (UInt64)bufferSize);
81 UInt32 processedSize;
82 if (!inFile.Read(tempBuffer.buffer, curSize, processedSize))
83 return GetLastError();
84 if (processedSize == 0)
85 break;
86 curSize = processedSize;
87 if (writeToDisk)
88 {
89 const UInt32 kMask = 0x1FF;
90 curSize = (curSize + kMask) & ~kMask;
91 if (curSize > bufferSize)
92 return E_FAIL;
93 }
94
95 if (!outFile.Write(tempBuffer.buffer, curSize, processedSize))
96 return GetLastError();
97 if (curSize != processedSize)
98 return E_FAIL;
99 pos += curSize;
100 }
101
102 return S_OK;
103 }
104
105 static const Byte kProps[] =
106 {
107 kpidName,
108 // kpidOutName,
109 kpidTotalSize,
110 kpidFreeSpace,
111 kpidType,
112 kpidVolumeName,
113 kpidFileSystem,
114 kpidClusterSize
115 };
116
117 static const char * const kDriveTypes[] =
118 {
119 "Unknown"
120 , "No Root Dir"
121 , "Removable"
122 , "Fixed"
123 , "Remote"
124 , "CD-ROM"
125 , "RAM disk"
126 };
127
LoadItems()128 STDMETHODIMP CFSDrives::LoadItems()
129 {
130 _drives.Clear();
131
132 FStringVector driveStrings;
133 MyGetLogicalDriveStrings(driveStrings);
134
135 FOR_VECTOR (i, driveStrings)
136 {
137 CDriveInfo di;
138
139 const FString &driveName = driveStrings[i];
140
141 di.FullSystemName = driveName;
142 if (!driveName.IsEmpty())
143 di.Name.SetFrom(driveName, driveName.Len() - 1);
144 di.ClusterSize = 0;
145 di.DriveSize = 0;
146 di.FreeSpace = 0;
147 di.DriveType = NSystem::MyGetDriveType(driveName);
148 bool needRead = true;
149
150 if (di.DriveType == DRIVE_CDROM || di.DriveType == DRIVE_REMOVABLE)
151 {
152 /*
153 DWORD dwSerialNumber;`
154 if (!::GetVolumeInformation(di.FullSystemName,
155 NULL, 0, &dwSerialNumber, NULL, NULL, NULL, 0))
156 */
157 {
158 needRead = false;
159 }
160 }
161
162 if (needRead)
163 {
164 DWORD volumeSerialNumber, maximumComponentLength, fileSystemFlags;
165 NSystem::MyGetVolumeInformation(driveName,
166 di.VolumeName,
167 &volumeSerialNumber, &maximumComponentLength, &fileSystemFlags,
168 di.FileSystemName);
169
170 NSystem::MyGetDiskFreeSpace(driveName,
171 di.ClusterSize, di.DriveSize, di.FreeSpace);
172 di.KnownSizes = true;
173 di.KnownSize = true;
174 }
175
176 _drives.Add(di);
177 }
178
179 if (_volumeMode)
180 {
181 // we must use IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
182 for (unsigned n = 0; n < 16; n++) // why 16 ?
183 {
184 FString name ("PhysicalDrive");
185 name.Add_UInt32(n);
186
187 FString fullPath (kVolPrefix);
188 fullPath += name;
189
190 CFileInfo fi;
191 if (!fi.Find(fullPath))
192 continue;
193
194 CDriveInfo di;
195 di.Name = name;
196 di.FullSystemName = fullPath;
197 di.ClusterSize = 0;
198 di.DriveSize = fi.Size;
199 di.FreeSpace = 0;
200 di.DriveType = 0;
201
202 di.IsPhysicalDrive = true;
203 di.KnownSize = true;
204
205 _drives.Add(di);
206 }
207 }
208
209 return S_OK;
210 }
211
GetNumberOfItems(UInt32 * numItems)212 STDMETHODIMP CFSDrives::GetNumberOfItems(UInt32 *numItems)
213 {
214 *numItems = _drives.Size();
215 return S_OK;
216 }
217
GetProperty(UInt32 itemIndex,PROPID propID,PROPVARIANT * value)218 STDMETHODIMP CFSDrives::GetProperty(UInt32 itemIndex, PROPID propID, PROPVARIANT *value)
219 {
220 if (itemIndex >= (UInt32)_drives.Size())
221 return E_INVALIDARG;
222 NCOM::CPropVariant prop;
223 const CDriveInfo &di = _drives[itemIndex];
224 switch (propID)
225 {
226 case kpidIsDir: prop = !_volumeMode; break;
227 case kpidName: prop = fs2us(di.Name); break;
228 case kpidOutName:
229 if (!di.Name.IsEmpty() && di.Name.Back() == ':')
230 {
231 FString s = di.Name;
232 s.DeleteBack();
233 AddExt(s, itemIndex);
234 prop = fs2us(s);
235 }
236 break;
237
238 case kpidTotalSize: if (di.KnownSize) prop = di.DriveSize; break;
239 case kpidFreeSpace: if (di.KnownSizes) prop = di.FreeSpace; break;
240 case kpidClusterSize: if (di.KnownSizes) prop = di.ClusterSize; break;
241 case kpidType:
242 if (di.DriveType < ARRAY_SIZE(kDriveTypes))
243 prop = kDriveTypes[di.DriveType];
244 break;
245 case kpidVolumeName: prop = di.VolumeName; break;
246 case kpidFileSystem: prop = di.FileSystemName; break;
247 }
248 prop.Detach(value);
249 return S_OK;
250 }
251
BindToFolderSpec(CFSTR name,IFolderFolder ** resultFolder)252 HRESULT CFSDrives::BindToFolderSpec(CFSTR name, IFolderFolder **resultFolder)
253 {
254 *resultFolder = 0;
255 if (_volumeMode)
256 return S_OK;
257 NFsFolder::CFSFolder *fsFolderSpec = new NFsFolder::CFSFolder;
258 CMyComPtr<IFolderFolder> subFolder = fsFolderSpec;
259 FString path;
260 if (_superMode)
261 path = kSuperPrefix;
262 path += name;
263 RINOK(fsFolderSpec->Init(path));
264 *resultFolder = subFolder.Detach();
265 return S_OK;
266 }
267
BindToFolder(UInt32 index,IFolderFolder ** resultFolder)268 STDMETHODIMP CFSDrives::BindToFolder(UInt32 index, IFolderFolder **resultFolder)
269 {
270 *resultFolder = 0;
271 if (index >= (UInt32)_drives.Size())
272 return E_INVALIDARG;
273 const CDriveInfo &di = _drives[index];
274 /*
275 if (_volumeMode)
276 {
277 *resultFolder = 0;
278 CPhysDriveFolder *folderSpec = new CPhysDriveFolder;
279 CMyComPtr<IFolderFolder> subFolder = folderSpec;
280 RINOK(folderSpec->Init(di.Name));
281 *resultFolder = subFolder.Detach();
282 return S_OK;
283 }
284 */
285 return BindToFolderSpec(di.FullSystemName, resultFolder);
286 }
287
BindToFolder(const wchar_t * name,IFolderFolder ** resultFolder)288 STDMETHODIMP CFSDrives::BindToFolder(const wchar_t *name, IFolderFolder **resultFolder)
289 {
290 return BindToFolderSpec(us2fs(name), resultFolder);
291 }
292
BindToParentFolder(IFolderFolder ** resultFolder)293 STDMETHODIMP CFSDrives::BindToParentFolder(IFolderFolder **resultFolder)
294 {
295 *resultFolder = 0;
296 return S_OK;
297 }
298
IMP_IFolderFolder_Props(CFSDrives)299 IMP_IFolderFolder_Props(CFSDrives)
300
301 STDMETHODIMP CFSDrives::GetFolderProperty(PROPID propID, PROPVARIANT *value)
302 {
303 COM_TRY_BEGIN
304 NCOM::CPropVariant prop;
305 switch (propID)
306 {
307 case kpidType: prop = "FSDrives"; break;
308 case kpidPath:
309 if (_volumeMode)
310 prop = kVolPrefix;
311 else if (_superMode)
312 prop = kSuperPrefix;
313 else
314 prop = (UString)LangString(IDS_COMPUTER) + WCHAR_PATH_SEPARATOR;
315 break;
316 }
317 prop.Detach(value);
318 return S_OK;
319 COM_TRY_END
320 }
321
322
GetSystemIconIndex(UInt32 index,Int32 * iconIndex)323 STDMETHODIMP CFSDrives::GetSystemIconIndex(UInt32 index, Int32 *iconIndex)
324 {
325 *iconIndex = 0;
326 const CDriveInfo &di = _drives[index];
327 if (di.IsPhysicalDrive)
328 return S_OK;
329 int iconIndexTemp;
330 if (GetRealIconIndex(di.FullSystemName, 0, iconIndexTemp) != 0)
331 {
332 *iconIndex = iconIndexTemp;
333 return S_OK;
334 }
335 return GetLastError();
336 }
337
AddExt(FString & s,unsigned index) const338 void CFSDrives::AddExt(FString &s, unsigned index) const
339 {
340 s += '.';
341 const CDriveInfo &di = _drives[index];
342 const char *ext;
343 if (di.DriveType == DRIVE_CDROM)
344 ext = "iso";
345 else if (di.FileSystemName.IsPrefixedBy_Ascii_NoCase("NTFS"))
346 ext = "ntfs";
347 else if (di.FileSystemName.IsPrefixedBy_Ascii_NoCase("FAT"))
348 ext = "fat";
349 else
350 ext = "img";
351 s += ext;
352 }
353
GetFileSize(unsigned index,UInt64 & fileSize) const354 HRESULT CFSDrives::GetFileSize(unsigned index, UInt64 &fileSize) const
355 {
356 NIO::CInFile inFile;
357 if (!inFile.Open(_drives[index].GetDeviceFileIoName()))
358 return GetLastError();
359 if (!inFile.SizeDefined)
360 return E_FAIL;
361 fileSize = inFile.Size;
362 return S_OK;
363 }
364
CopyTo(Int32 moveMode,const UInt32 * indices,UInt32 numItems,Int32,Int32,const wchar_t * path,IFolderOperationsExtractCallback * callback)365 STDMETHODIMP CFSDrives::CopyTo(Int32 moveMode, const UInt32 *indices, UInt32 numItems,
366 Int32 /* includeAltStreams */, Int32 /* replaceAltStreamColon */,
367 const wchar_t *path, IFolderOperationsExtractCallback *callback)
368 {
369 if (numItems == 0)
370 return S_OK;
371
372 if (moveMode)
373 return E_NOTIMPL;
374
375 if (!_volumeMode)
376 return E_NOTIMPL;
377
378 UInt64 totalSize = 0;
379 UInt32 i;
380 for (i = 0; i < numItems; i++)
381 {
382 const CDriveInfo &di = _drives[indices[i]];
383 if (di.KnownSize)
384 totalSize += di.DriveSize;
385 }
386 RINOK(callback->SetTotal(totalSize));
387 RINOK(callback->SetNumFiles(numItems));
388
389 FString destPath = us2fs(path);
390 if (destPath.IsEmpty())
391 return E_INVALIDARG;
392
393 bool isAltDest = NName::IsAltPathPrefix(destPath);
394 bool isDirectPath = (!isAltDest && !IsPathSepar(destPath.Back()));
395
396 if (isDirectPath)
397 {
398 if (numItems > 1)
399 return E_INVALIDARG;
400 }
401
402 UInt64 completedSize = 0;
403 RINOK(callback->SetCompleted(&completedSize));
404
405 for (i = 0; i < numItems; i++)
406 {
407 unsigned index = indices[i];
408 const CDriveInfo &di = _drives[index];
409 FString destPath2 = destPath;
410
411 if (!isDirectPath)
412 {
413 FString destName = di.Name;
414 if (!destName.IsEmpty() && destName.Back() == ':')
415 {
416 destName.DeleteBack();
417 AddExt(destName, index);
418 }
419 destPath2 += destName;
420 }
421
422 FString srcPath = di.GetDeviceFileIoName();
423
424 UInt64 fileSize = 0;
425 if (GetFileSize(index, fileSize) != S_OK)
426 {
427 return E_FAIL;
428 }
429 if (!di.KnownSize)
430 {
431 totalSize += fileSize;
432 RINOK(callback->SetTotal(totalSize));
433 }
434
435 Int32 writeAskResult;
436 CMyComBSTR destPathResult;
437 RINOK(callback->AskWrite(fs2us(srcPath), BoolToInt(false), NULL, &fileSize,
438 fs2us(destPath2), &destPathResult, &writeAskResult));
439
440 if (!IntToBool(writeAskResult))
441 {
442 if (totalSize >= fileSize)
443 totalSize -= fileSize;
444 RINOK(callback->SetTotal(totalSize));
445 continue;
446 }
447
448 RINOK(callback->SetCurrentFilePath(fs2us(srcPath)));
449
450 static const UInt32 kBufferSize = (4 << 20);
451 UInt32 bufferSize = (di.DriveType == DRIVE_REMOVABLE) ? (18 << 10) * 4 : kBufferSize;
452 RINOK(CopyFileSpec(srcPath, us2fs(destPathResult), false, fileSize, bufferSize, completedSize, callback));
453 completedSize += fileSize;
454 }
455
456 return S_OK;
457 }
458
CopyFrom(Int32,const wchar_t *,const wchar_t * const *,UInt32,IProgress *)459 STDMETHODIMP CFSDrives::CopyFrom(Int32 /* moveMode */, const wchar_t * /* fromFolderPath */,
460 const wchar_t * const * /* itemsPaths */, UInt32 /* numItems */, IProgress * /* progress */)
461 {
462 return E_NOTIMPL;
463 }
464
CopyFromFile(UInt32,const wchar_t *,IProgress *)465 STDMETHODIMP CFSDrives::CopyFromFile(UInt32 /* index */, const wchar_t * /* fullFilePath */, IProgress * /* progress */)
466 {
467 return E_NOTIMPL;
468 }
469
CreateFolder(const wchar_t *,IProgress *)470 STDMETHODIMP CFSDrives::CreateFolder(const wchar_t * /* name */, IProgress * /* progress */)
471 {
472 return E_NOTIMPL;
473 }
474
CreateFile(const wchar_t *,IProgress *)475 STDMETHODIMP CFSDrives::CreateFile(const wchar_t * /* name */, IProgress * /* progress */)
476 {
477 return E_NOTIMPL;
478 }
479
Rename(UInt32,const wchar_t *,IProgress *)480 STDMETHODIMP CFSDrives::Rename(UInt32 /* index */, const wchar_t * /* newName */, IProgress * /* progress */)
481 {
482 return E_NOTIMPL;
483 }
484
Delete(const UInt32 *,UInt32,IProgress *)485 STDMETHODIMP CFSDrives::Delete(const UInt32 * /* indices */, UInt32 /* numItems */, IProgress * /* progress */)
486 {
487 return E_NOTIMPL;
488 }
489
SetProperty(UInt32,PROPID,const PROPVARIANT *,IProgress *)490 STDMETHODIMP CFSDrives::SetProperty(UInt32 /* index */, PROPID /* propID */,
491 const PROPVARIANT * /* value */, IProgress * /* progress */)
492 {
493 return E_NOTIMPL;
494 }
495