xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/FileManager/FSDrives.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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(NULL) {}
~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,
49     bool writeToDisk, UInt64 fileSize,
50     UInt32 bufferSize, UInt64 progressStart, IProgress *progress)
51 {
52   NIO::CInFile inFile;
53   if (!inFile.Open(fromPath))
54     return GetLastError_noZero_HRESULT();
55   if (fileSize == (UInt64)(Int64)-1)
56   {
57     if (!inFile.GetLength(fileSize))
58       return GetLastError_noZero_HRESULT();
59   }
60 
61   NIO::COutFile outFile;
62   if (writeToDisk)
63   {
64     if (!outFile.Open(toPath, FILE_SHARE_WRITE, OPEN_EXISTING, 0))
65       return GetLastError_noZero_HRESULT();
66   }
67   else
68     if (!outFile.Create_ALWAYS(toPath))
69       return GetLastError_noZero_HRESULT();
70 
71   CPhysTempBuffer tempBuffer;
72   tempBuffer.buffer = MidAlloc(bufferSize);
73   if (!tempBuffer.buffer)
74     return E_OUTOFMEMORY;
75 
76   for (UInt64 pos = 0; pos < fileSize;)
77   {
78     {
79       const UInt64 progressCur = progressStart + pos;
80       RINOK(progress->SetCompleted(&progressCur))
81     }
82     const UInt64 rem = fileSize - pos;
83     UInt32 curSize = (UInt32)MyMin(rem, (UInt64)bufferSize);
84     UInt32 processedSize;
85     if (!inFile.Read(tempBuffer.buffer, curSize, processedSize))
86       return GetLastError_noZero_HRESULT();
87     if (processedSize == 0)
88       break;
89     curSize = processedSize;
90     if (writeToDisk)
91     {
92       const UInt32 kMask = 0x1FF;
93       curSize = (curSize + kMask) & ~kMask;
94       if (curSize > bufferSize)
95         return E_FAIL;
96     }
97     if (!outFile.Write(tempBuffer.buffer, curSize, processedSize))
98       return GetLastError_noZero_HRESULT();
99     if (curSize != processedSize)
100       return E_FAIL;
101     pos += curSize;
102   }
103 
104   return S_OK;
105 }
106 
107 static const Byte kProps[] =
108 {
109   kpidName,
110   // kpidOutName,
111   kpidTotalSize,
112   kpidFreeSpace,
113   kpidType,
114   kpidVolumeName,
115   kpidFileSystem,
116   kpidClusterSize
117 };
118 
119 static const char * const kDriveTypes[] =
120 {
121     "Unknown"
122   , "No Root Dir"
123   , "Removable"
124   , "Fixed"
125   , "Remote"
126   , "CD-ROM"
127   , "RAM disk"
128 };
129 
Z7_COM7F_IMF(CFSDrives::LoadItems ())130 Z7_COM7F_IMF(CFSDrives::LoadItems())
131 {
132   _drives.Clear();
133 
134   FStringVector driveStrings;
135   MyGetLogicalDriveStrings(driveStrings);
136 
137   FOR_VECTOR (i, driveStrings)
138   {
139     CDriveInfo di;
140     const FString &driveName = driveStrings[i];
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       FString fullPath (kVolPrefix);
187       fullPath += name;
188       CFileInfo fi;
189       if (!fi.Find(fullPath))
190         continue;
191 
192       CDriveInfo di;
193       di.Name = name;
194       // if (_volumeMode == true) we use CDriveInfo::FullSystemName only in GetSystemIconIndex().
195       // And we need name without "\\\\.\\" prefix in GetSystemIconIndex().
196       // So we don't set di.FullSystemName = fullPath;
197       di.FullSystemName = name;
198       di.ClusterSize = 0;
199       di.DriveSize = fi.Size;
200       di.FreeSpace = 0;
201       di.DriveType = 0;
202       di.IsPhysicalDrive = true;
203       di.KnownSize = true;
204       _drives.Add(di);
205     }
206   }
207 
208   return S_OK;
209 }
210 
Z7_COM7F_IMF(CFSDrives::GetNumberOfItems (UInt32 * numItems))211 Z7_COM7F_IMF(CFSDrives::GetNumberOfItems(UInt32 *numItems))
212 {
213   *numItems = _drives.Size();
214   return S_OK;
215 }
216 
Z7_COM7F_IMF(CFSDrives::GetProperty (UInt32 itemIndex,PROPID propID,PROPVARIANT * value))217 Z7_COM7F_IMF(CFSDrives::GetProperty(UInt32 itemIndex, PROPID propID, PROPVARIANT *value))
218 {
219   if (itemIndex >= _drives.Size())
220     return E_INVALIDARG;
221   NCOM::CPropVariant prop;
222   const CDriveInfo &di = _drives[itemIndex];
223   switch (propID)
224   {
225     case kpidIsDir:  prop = !_volumeMode; break;
226     case kpidName:  prop = fs2us(di.Name); break;
227     case kpidOutName:
228       if (!di.Name.IsEmpty() && di.Name.Back() == ':')
229       {
230         FString s = di.Name;
231         s.DeleteBack();
232         AddExt(s, itemIndex);
233         prop = fs2us(s);
234       }
235       break;
236 
237     case kpidTotalSize:   if (di.KnownSize) prop = di.DriveSize; break;
238     case kpidFreeSpace:   if (di.KnownSizes) prop = di.FreeSpace; break;
239     case kpidClusterSize: if (di.KnownSizes) prop = di.ClusterSize; break;
240     case kpidType:
241       if (di.DriveType < Z7_ARRAY_SIZE(kDriveTypes))
242         prop = kDriveTypes[di.DriveType];
243       break;
244     case kpidVolumeName:  prop = di.VolumeName; break;
245     case kpidFileSystem:  prop = di.FileSystemName; break;
246   }
247   prop.Detach(value);
248   return S_OK;
249 }
250 
BindToFolderSpec(CFSTR name,IFolderFolder ** resultFolder)251 HRESULT CFSDrives::BindToFolderSpec(CFSTR name, IFolderFolder **resultFolder)
252 {
253   *resultFolder = NULL;
254   if (_volumeMode)
255     return S_OK;
256   NFsFolder::CFSFolder *fsFolderSpec = new NFsFolder::CFSFolder;
257   CMyComPtr<IFolderFolder> subFolder = fsFolderSpec;
258   FString path;
259   if (_superMode)
260     path = kSuperPrefix;
261   path += name;
262   RINOK(fsFolderSpec->Init(path))
263   *resultFolder = subFolder.Detach();
264   return S_OK;
265 }
266 
Z7_COM7F_IMF(CFSDrives::BindToFolder (UInt32 index,IFolderFolder ** resultFolder))267 Z7_COM7F_IMF(CFSDrives::BindToFolder(UInt32 index, IFolderFolder **resultFolder))
268 {
269   *resultFolder = NULL;
270   if (index >= _drives.Size())
271     return E_INVALIDARG;
272   const CDriveInfo &di = _drives[index];
273   /*
274   if (_volumeMode)
275   {
276     *resultFolder = 0;
277     CPhysDriveFolder *folderSpec = new CPhysDriveFolder;
278     CMyComPtr<IFolderFolder> subFolder = folderSpec;
279     RINOK(folderSpec->Init(di.Name));
280     *resultFolder = subFolder.Detach();
281     return S_OK;
282   }
283   */
284   return BindToFolderSpec(di.FullSystemName, resultFolder);
285 }
286 
Z7_COM7F_IMF(CFSDrives::BindToFolder (const wchar_t * name,IFolderFolder ** resultFolder))287 Z7_COM7F_IMF(CFSDrives::BindToFolder(const wchar_t *name, IFolderFolder **resultFolder))
288 {
289   return BindToFolderSpec(us2fs(name), resultFolder);
290 }
291 
Z7_COM7F_IMF(CFSDrives::BindToParentFolder (IFolderFolder ** resultFolder))292 Z7_COM7F_IMF(CFSDrives::BindToParentFolder(IFolderFolder **resultFolder))
293 {
294   *resultFolder = NULL;
295   return S_OK;
296 }
297 
298 IMP_IFolderFolder_Props(CFSDrives)
299 
Z7_COM7F_IMF(CFSDrives::GetFolderProperty (PROPID propID,PROPVARIANT * value))300 Z7_COM7F_IMF(CFSDrives::GetFolderProperty(PROPID propID, PROPVARIANT *value))
301 {
302   COM_TRY_BEGIN
303   NCOM::CPropVariant prop;
304   switch (propID)
305   {
306     case kpidType: prop = "FSDrives"; break;
307     case kpidPath:
308       if (_volumeMode)
309         prop = kVolPrefix;
310       else if (_superMode)
311         prop = kSuperPrefix;
312       else
313         prop = (UString)LangString(IDS_COMPUTER) + WCHAR_PATH_SEPARATOR;
314       break;
315   }
316   prop.Detach(value);
317   return S_OK;
318   COM_TRY_END
319 }
320 
321 
Z7_COM7F_IMF(CFSDrives::GetSystemIconIndex (UInt32 index,Int32 * iconIndex))322 Z7_COM7F_IMF(CFSDrives::GetSystemIconIndex(UInt32 index, Int32 *iconIndex))
323 {
324   *iconIndex = -1;
325   const CDriveInfo &di = _drives[index];
326   return Shell_GetFileInfo_SysIconIndex_for_Path_return_HRESULT(
327       di.FullSystemName,
328       _volumeMode ?
329           FILE_ATTRIBUTE_ARCHIVE:
330           FILE_ATTRIBUTE_DIRECTORY,
331       iconIndex);
332 }
333 
AddExt(FString & s,unsigned index) const334 void CFSDrives::AddExt(FString &s, unsigned index) const
335 {
336   s.Add_Dot();
337   const CDriveInfo &di = _drives[index];
338   UString n = di.FileSystemName;
339   n.MakeLower_Ascii();
340   const char *ext;
341   if (di.DriveType == DRIVE_CDROM)
342     ext = "iso";
343   else
344   {
345     unsigned i;
346     for (i = 0; i < n.Len(); i++)
347     {
348       const wchar_t c = n[i];
349       if (c < 'a' || c > 'z')
350         break;
351     }
352     if (i != 0)
353     {
354       n.DeleteFrom(i);
355       s += us2fs(n);
356       return;
357     }
358     ext = "img";
359   }
360   /*
361        if (n.IsPrefixedBy_Ascii_NoCase("NTFS")) ext = "ntfs";
362   else if (n.IsPrefixedBy_Ascii_NoCase("UDF")) ext = "udf";
363   else if (n.IsPrefixedBy_Ascii_NoCase("exFAT")) ext = "exfat";
364   */
365   s += ext;
366 }
367 
GetFileSize(unsigned index,UInt64 & fileSize) const368 HRESULT CFSDrives::GetFileSize(unsigned index, UInt64& fileSize) const
369 {
370 #ifdef Z7_DEVICE_FILE
371   NIO::CInFile inFile;
372   if (!inFile.Open(_drives[index].GetDeviceFileIoName()))
373     return GetLastError_noZero_HRESULT();
374   if (inFile.SizeDefined)
375   {
376     fileSize = inFile.Size;
377     return S_OK;
378   }
379 #else
380   UNUSED_VAR(index)
381 #endif
382   fileSize = 0;
383   return E_FAIL;
384 }
385 
Z7_COM7F_IMF(CFSDrives::CopyTo (Int32 moveMode,const UInt32 * indices,UInt32 numItems,Int32,Int32,const wchar_t * path,IFolderOperationsExtractCallback * callback))386 Z7_COM7F_IMF(CFSDrives::CopyTo(Int32 moveMode, const UInt32 *indices, UInt32 numItems,
387     Int32 /* includeAltStreams */, Int32 /* replaceAltStreamColon */,
388     const wchar_t *path, IFolderOperationsExtractCallback *callback))
389 {
390   if (numItems == 0)
391     return S_OK;
392   if (moveMode)
393     return E_NOTIMPL;
394   if (!_volumeMode)
395     return E_NOTIMPL;
396 
397   UInt64 totalSize = 0;
398   UInt32 i;
399   for (i = 0; i < numItems; i++)
400   {
401     const CDriveInfo &di = _drives[indices[i]];
402     if (di.KnownSize)
403       totalSize += di.DriveSize;
404   }
405   RINOK(callback->SetTotal(totalSize))
406   RINOK(callback->SetNumFiles(numItems))
407 
408   const FString destPath = us2fs(path);
409   if (destPath.IsEmpty())
410     return E_INVALIDARG;
411 
412   const bool isAltDest = NName::IsAltPathPrefix(destPath);
413   const bool isDirectPath = (!isAltDest && !IsPathSepar(destPath.Back()));
414 
415   if (isDirectPath)
416   {
417     if (numItems > 1)
418       return E_INVALIDARG;
419   }
420 
421   UInt64 completedSize = 0;
422   RINOK(callback->SetCompleted(&completedSize))
423   for (i = 0; i < numItems; i++)
424   {
425     const unsigned index = indices[i];
426     const CDriveInfo &di = _drives[index];
427     FString destPath2 = destPath;
428 
429     if (!isDirectPath)
430     {
431       FString destName = di.Name;
432       if (!destName.IsEmpty() && destName.Back() == ':')
433       {
434         destName.DeleteBack();
435         AddExt(destName, index);
436       }
437       destPath2 += destName;
438     }
439 
440     const FString srcPath = di.GetDeviceFileIoName();
441 
442     UInt64 fileSize = 0;
443     if (GetFileSize(index, fileSize) != S_OK)
444     {
445       return E_FAIL;
446     }
447     if (!di.KnownSize)
448     {
449       totalSize += fileSize;
450       RINOK(callback->SetTotal(totalSize))
451     }
452 
453     Int32 writeAskResult;
454     CMyComBSTR destPathResult;
455     RINOK(callback->AskWrite(fs2us(srcPath), BoolToInt(false), NULL, &fileSize,
456         fs2us(destPath2), &destPathResult, &writeAskResult))
457 
458     if (!IntToBool(writeAskResult))
459     {
460       if (totalSize >= fileSize)
461         totalSize -= fileSize;
462       RINOK(callback->SetTotal(totalSize))
463       continue;
464     }
465 
466     RINOK(callback->SetCurrentFilePath(fs2us(srcPath)))
467 
468     const UInt32 kBufferSize = (4 << 20);
469     const UInt32 bufferSize = (di.DriveType == DRIVE_REMOVABLE) ? (18 << 10) * 4 : kBufferSize;
470     RINOK(CopyFileSpec(srcPath, us2fs(destPathResult), false, fileSize, bufferSize, completedSize, callback))
471     completedSize += fileSize;
472   }
473 
474   return S_OK;
475 }
476 
Z7_COM7F_IMF(CFSDrives::CopyFrom (Int32,const wchar_t *,const wchar_t * const *,UInt32,IProgress *))477 Z7_COM7F_IMF(CFSDrives::CopyFrom(Int32 /* moveMode */, const wchar_t * /* fromFolderPath */,
478     const wchar_t * const * /* itemsPaths */, UInt32 /* numItems */, IProgress * /* progress */))
479 {
480   return E_NOTIMPL;
481 }
482 
Z7_COM7F_IMF(CFSDrives::CopyFromFile (UInt32,const wchar_t *,IProgress *))483 Z7_COM7F_IMF(CFSDrives::CopyFromFile(UInt32 /* index */, const wchar_t * /* fullFilePath */, IProgress * /* progress */))
484 {
485   return E_NOTIMPL;
486 }
487 
Z7_COM7F_IMF(CFSDrives::CreateFolder (const wchar_t *,IProgress *))488 Z7_COM7F_IMF(CFSDrives::CreateFolder(const wchar_t * /* name */, IProgress * /* progress */))
489 {
490   return E_NOTIMPL;
491 }
492 
Z7_COM7F_IMF(CFSDrives::CreateFile (const wchar_t *,IProgress *))493 Z7_COM7F_IMF(CFSDrives::CreateFile(const wchar_t * /* name */, IProgress * /* progress */))
494 {
495   return E_NOTIMPL;
496 }
497 
Z7_COM7F_IMF(CFSDrives::Rename (UInt32,const wchar_t *,IProgress *))498 Z7_COM7F_IMF(CFSDrives::Rename(UInt32 /* index */, const wchar_t * /* newName */, IProgress * /* progress */))
499 {
500   return E_NOTIMPL;
501 }
502 
Z7_COM7F_IMF(CFSDrives::Delete (const UInt32 *,UInt32,IProgress *))503 Z7_COM7F_IMF(CFSDrives::Delete(const UInt32 * /* indices */, UInt32 /* numItems */, IProgress * /* progress */))
504 {
505   return E_NOTIMPL;
506 }
507 
Z7_COM7F_IMF(CFSDrives::SetProperty (UInt32,PROPID,const PROPVARIANT *,IProgress *))508 Z7_COM7F_IMF(CFSDrives::SetProperty(UInt32 /* index */, PROPID /* propID */,
509     const PROPVARIANT * /* value */, IProgress * /* progress */))
510 {
511   return E_NOTIMPL;
512 }
513