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