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