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