Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(124)

Side by Side Diff: base/disk.cc

Issue 624713003: Keep only base/extractor.[cc|h]. (Closed) Base URL: https://chromium.googlesource.com/external/omaha.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/disk.h ('k') | base/disk_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2004-2009 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 // ========================================================================
15 //
16 // Disk functions
17
18 #include "omaha/base/disk.h"
19
20 #include <winioctl.h>
21 #include "omaha/base/commontypes.h"
22 #include "omaha/base/const_config.h"
23 #include "omaha/base/debug.h"
24 #include "omaha/base/error.h"
25 #include "omaha/base/file.h"
26 #include "omaha/base/localization.h"
27 #include "omaha/base/logging.h"
28 #include "omaha/base/shell.h"
29 #include "omaha/base/string.h"
30 #include "omaha/base/synchronized.h"
31 #include "omaha/base/system.h"
32 #include "omaha/base/timer.h"
33 #include "omaha/base/utils.h"
34
35 namespace omaha {
36
37 #define kNetdiskVendorId "netdisk"
38
39 // see also: http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q264203
40
41 #if _MSC_VER < 1400
42 // Not defined in the headers we have; from MSDN:
43 #define IOCTL_STORAGE_QUERY_PROPERTY \
44 CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
45
46 typedef struct _STORAGE_DEVICE_DESCRIPTOR {
47 ULONG Version;
48 ULONG Size;
49 UCHAR DeviceType;
50 UCHAR DeviceTypeModifier;
51 BOOLEAN RemovableMedia;
52 BOOLEAN CommandQueueing;
53 ULONG VendorIdOffset;
54 ULONG ProductIdOffset;
55 ULONG ProductRevisionOffset;
56 ULONG SerialNumberOffset;
57 STORAGE_BUS_TYPE BusType;
58 ULONG RawPropertiesLength;
59 UCHAR RawDeviceProperties[1];
60 } STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;
61
62 typedef enum _STORAGE_QUERY_TYPE {
63 PropertyStandardQuery = 0,
64 PropertyExistsQuery,
65 PropertyMaskQuery,
66 PropertyQueryMaxDefined
67 } STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE;
68
69 typedef enum _STORAGE_PROPERTY_ID {
70 StorageDeviceProperty = 0,
71 StorageAdapterProperty,
72 StorageDeviceIdProperty
73 } STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID;
74
75 typedef struct _STORAGE_PROPERTY_QUERY {
76 STORAGE_PROPERTY_ID PropertyId;
77 STORAGE_QUERY_TYPE QueryType;
78 UCHAR AdditionalParameters[1];
79 } STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
80
81 // -------
82 #endif
83
84 #define kIoctlBufferSize 1024
85
86 #define kMaxDrivesCached 3
87 #define kMaxDriveLen 20
88 static TCHAR g_cache_drive[kMaxDrivesCached][kMaxDriveLen+1];
89 static bool g_cache_external[kMaxDrivesCached];
90 static int g_cache_pos;
91 static LLock g_cache_lock;
92
93 bool IsDiskExternal(const TCHAR *drive) {
94 ASSERT(drive, (L""));
95 ASSERT(lstrlen(drive) < kMaxDriveLen, (L""));
96
97 DisableThreadErrorUI disable_error_dialog_box;
98
99 {
100 __mutexScope(g_cache_lock);
101 for (int i = 0; i < kMaxDrivesCached; i++)
102 if (!lstrcmp(drive, g_cache_drive[i])) {
103 UTIL_LOG(L1, (L"cached disk ext %s %d", drive, g_cache_external[i]));
104 return g_cache_external[i];
105 }
106 }
107
108 #ifdef _DEBUG
109 Timer timer(true);
110 #endif
111
112 byte buffer[kIoctlBufferSize+1];
113
114 bool external = false;
115 HANDLE device = ::CreateFile(drive,
116 GENERIC_READ,
117 FILE_SHARE_READ | FILE_SHARE_WRITE,
118 NULL,
119 OPEN_EXISTING,
120 NULL,
121 NULL);
122 if (device == INVALID_HANDLE_VALUE) {
123 UTIL_LOG(L1, (L"disk external could not open drive %s", drive));
124 goto done;
125 }
126 STORAGE_DEVICE_DESCRIPTOR *device_desc;
127 STORAGE_PROPERTY_QUERY query;
128 DWORD out_bytes;
129 query.PropertyId = StorageDeviceProperty;
130 query.QueryType = PropertyStandardQuery;
131 *(query.AdditionalParameters) = 0;
132
133 device_desc = reinterpret_cast<STORAGE_DEVICE_DESCRIPTOR*>(buffer);
134 // should not be needed, but just to be safer
135 ZeroMemory(buffer, kIoctlBufferSize);
136
137 BOOL ok = ::DeviceIoControl(device,
138 IOCTL_STORAGE_QUERY_PROPERTY,
139 &query,
140 sizeof(STORAGE_PROPERTY_QUERY),
141 buffer,
142 kIoctlBufferSize,
143 &out_bytes,
144 (LPOVERLAPPED)NULL);
145
146 if (ok &&
147 device_desc->VendorIdOffset &&
148 stristr(reinterpret_cast<char*>(buffer + device_desc->VendorIdOffset),
149 kNetdiskVendorId)) {
150 external = true;
151 UTIL_LOG(L1, (L"ximeta netdisk %s", drive));
152 }
153
154 if (ok &&
155 (device_desc->BusType == BusTypeUsb ||
156 device_desc->BusType == BusType1394)) {
157 external = true;
158 }
159 if (!ok) {
160 UTIL_LOG(L1, (L"disk external ioctl failed %s", drive));
161 }
162 CloseHandle(device);
163 done:
164 UTIL_LOG(L1, (L"disk external %s %d time %s",
165 drive, external, String_DoubleToString(timer.GetMilliseconds(), 3)));
166
167 {
168 __mutexScope(g_cache_lock);
169 lstrcpyn(g_cache_drive[g_cache_pos], drive, kMaxDriveLen+1);
170 g_cache_external[g_cache_pos] = external;
171 if (++g_cache_pos >= kMaxDrivesCached) g_cache_pos = 0;
172 }
173
174 return external;
175 }
176
177 // find the first fixed local disk with at least the space requested
178 // confirms that we can create a directory on the drive
179 // returns the drive in the drive parameter
180 // returns E_FAIL if no drive with enough space could be found
181 HRESULT FindFirstLocalDriveWithEnoughSpace(const uint64 space_required,
182 CString *drive) {
183 ASSERT1(drive);
184
185 DisableThreadErrorUI disable_error_dialog_box;
186
187 const int kMaxNumDrives = 26;
188 static const size_t kBufLen = (STR_SIZE("c:\\\0") * kMaxNumDrives) + 1;
189
190 // obtain the fixed system drives
191 TCHAR buf[kBufLen];
192 DWORD str_len = ::GetLogicalDriveStrings(kBufLen, buf);
193 if (str_len > 0 && str_len < kBufLen) {
194 for (TCHAR* ptr = buf; *ptr != L'\0'; ptr += (lstrlen(ptr) + 1)) {
195 UINT drive_type = GetDriveType(ptr);
196 if (drive_type == DRIVE_FIXED) {
197 CString test_drive(ptr);
198 if (!IsDiskExternal(CString(L"\\\\?\\") + test_drive.Left(2))) {
199 uint64 free_disk_space = 0;
200 HRESULT hr = GetFreeDiskSpace(test_drive, &free_disk_space);
201
202 if (SUCCEEDED(hr) && space_required <= free_disk_space) {
203 CString temp_dir;
204 // confirm that we can create a directory on this drive
205 bool found = false;
206 while (!found) {
207 temp_dir = test_drive +
208 NOTRANSL(L"test") +
209 itostr(static_cast<uint32>(::GetTickCount()));
210 if (!File::Exists (temp_dir)) found = true;
211 }
212
213 if (SUCCEEDED(CreateDir(temp_dir, NULL))) {
214 VERIFY1(SUCCEEDED(DeleteDirectory(temp_dir)));
215 *drive = test_drive;
216 UTIL_LOG(L1, (L"drive %s enough space %d", test_drive.GetString(),
217 free_disk_space));
218 return S_OK;
219 }
220 }
221 }
222 }
223 }
224 }
225
226 return E_FAIL;
227 }
228
229 // Get free disk space of a drive containing the specified folder
230 HRESULT GetFreeDiskSpace(uint32 csidl, uint64* free_disk_space) {
231 ASSERT1(free_disk_space);
232
233 CString path;
234 RET_IF_FAILED(Shell::GetSpecialFolder(csidl, false, &path));
235
236 return GetFreeDiskSpace(path, free_disk_space);
237 }
238
239 // Get free disk space of a drive containing the specified folder
240 HRESULT GetFreeDiskSpace(const TCHAR* folder, uint64* free_disk_space) {
241 ASSERT1(folder && *folder);
242 ASSERT1(free_disk_space);
243
244 DisableThreadErrorUI disable_error_dialog_box;
245
246 CString drive(folder);
247
248 // (Stupid API used by System::GetDiskStatistics will work with any folder -
249 // as long as it EXISTS. Since the data storage folder might not exist yet
250 // (e.g., on a clean install) we'll just truncate it down to a drive letter.)
251 drive = drive.Left(3); // "X:\"
252 ASSERT1(String_EndsWith(drive, _T(":\\"), false));
253
254 // Get the free disk space available to this user on this drive
255 uint64 free_bytes_current_user = 0LL;
256 uint64 total_bytes_current_user = 0LL;
257 uint64 free_bytes_all_users = 0LL;
258 RET_IF_FAILED(System::GetDiskStatistics(drive,
259 &free_bytes_current_user,
260 &total_bytes_current_user,
261 &free_bytes_all_users));
262
263 *free_disk_space = std::min(free_bytes_current_user, free_bytes_all_users);
264
265 return S_OK;
266 }
267
268 // Has enough free disk space on a drive containing the specified folder
269 HRESULT HasEnoughFreeDiskSpace(uint32 csidl, uint64 disk_space_needed) {
270 uint64 free_disk_space = 0;
271 if (SUCCEEDED(GetFreeDiskSpace(csidl, &free_disk_space))) {
272 return (disk_space_needed <= free_disk_space) ?
273 S_OK : CI_E_NOT_ENOUGH_DISK_SPACE;
274 }
275 return S_OK;
276 }
277
278 // Has enough free disk space on a drive containing the specified folder
279 HRESULT HasEnoughFreeDiskSpace(const TCHAR* folder, uint64 disk_space_needed) {
280 uint64 free_disk_space = 0;
281 if (SUCCEEDED(GetFreeDiskSpace(folder, &free_disk_space))) {
282 return (disk_space_needed <= free_disk_space) ?
283 S_OK : CI_E_NOT_ENOUGH_DISK_SPACE;
284 }
285 return S_OK;
286 }
287
288 // The ::CreateFile() call will fail for mapped local drives (subst).
289 bool IsHotPluggable(const TCHAR* drive) {
290 ASSERT(drive, (L""));
291
292 // Disable potential error dialogs during this check
293 DisableThreadErrorUI disable_error_dialog_box;
294
295 //
296 // We set the default return value to true so that
297 // we treat the disk as hot-pluggable in case we
298 // don't know.
299 //
300 bool ret = true;
301
302 if (drive && lstrlen(drive) >= 2) {
303 CString volume_path(_T("\\\\.\\"));
304 // We don't want the trailing backslash.
305 volume_path.Append(drive, 2);
306
307 CHandle volume(::CreateFile(volume_path, GENERIC_READ,
308 FILE_SHARE_READ | FILE_SHARE_WRITE,
309 NULL, OPEN_EXISTING, 0, NULL));
310
311 if (volume != INVALID_HANDLE_VALUE) {
312 STORAGE_HOTPLUG_INFO shi = {0};
313 shi.Size = sizeof(shi);
314 DWORD bytes_returned = 0;
315 if (::DeviceIoControl(volume, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0,
316 &shi, sizeof(STORAGE_HOTPLUG_INFO), &bytes_returned,
317 NULL)) {
318 ret = (shi.DeviceHotplug != false);
319 } else {
320 UTIL_LOG(LW, (_T("[::DeviceIoControl failed][%u]"), ::GetLastError()));
321 }
322 } else {
323 UTIL_LOG(LW, (_T("[::CreateFile failed][%u]"), ::GetLastError()));
324 }
325 } else {
326 ASSERT(false, (L"Invalid path"));
327 }
328
329 return ret;
330 }
331
332 bool IsLargeDrive(const TCHAR* drive) {
333 ASSERT1(drive && *drive);
334
335 DisableThreadErrorUI disable_error_dialog_box;
336
337 ULARGE_INTEGER caller_free_bytes = {0};
338 ULARGE_INTEGER total_bytes = {0};
339 ULARGE_INTEGER total_free_bytes = {0};
340
341 if (!::GetDiskFreeSpaceEx(drive,
342 &caller_free_bytes,
343 &total_bytes,
344 &total_free_bytes)) {
345 HRESULT hr = HRESULTFromLastError();
346 UTIL_LOG(LEVEL_ERROR,
347 (_T("[IsLargeDrive - failed to GetDiskFreeSpaceEx][0x%x]"), hr));
348 return false;
349 }
350
351 return (total_bytes.QuadPart > kLargeDriveSize);
352 }
353
354 HRESULT DevicePathToDosPath(const TCHAR* device_path, CString* dos_path) {
355 ASSERT1(device_path);
356 ASSERT1(dos_path);
357 UTIL_LOG(L4, (_T("[DevicePathToDosPath][device_path=%s]"), device_path));
358
359 dos_path->Empty();
360
361 TCHAR drive_strings[MAX_PATH] = _T("");
362 if (!::GetLogicalDriveStrings(arraysize(drive_strings), drive_strings)) {
363 UTIL_LOG(L4, (_T("[DevicePathToDosPath-GetLogicalDriveStrings fail][0x%x]"),
364 HRESULTFromLastError()));
365 return HRESULTFromLastError();
366 }
367
368 // Drive strings are stored as a set of null terminated strings, with an
369 // extra null after the last string. Each drive string is of the form "C:\".
370 // We convert it to the form "C:", which is the format expected by
371 // ::QueryDosDevice().
372 TCHAR drive_colon[3] = _T(" :");
373 for (const TCHAR* next_drive_letter = drive_strings;
374 *next_drive_letter;
375 next_drive_letter += _tcslen(next_drive_letter) + 1) {
376 // Dos device of the form "C:".
377 *drive_colon = *next_drive_letter;
378 TCHAR device_name[MAX_PATH] = _T("");
379 if (!::QueryDosDevice(drive_colon, device_name, arraysize(device_name))) {
380 UTIL_LOG(LEVEL_ERROR, (_T("[QueryDosDevice failed][0x%x]"),
381 HRESULTFromLastError()));
382 continue;
383 }
384
385 UTIL_LOG(L4, (_T("[DevicePathToDosPath found drive]")
386 _T("[logical drive %s][device name %s]"),
387 drive_colon, device_name));
388
389 size_t name_length = _tcslen(device_name);
390 if (_tcsnicmp(device_path, device_name, name_length) == 0) {
391 // Construct DOS path.
392 dos_path->Format(_T("%s%s"), drive_colon, device_path + name_length);
393 UTIL_LOG(L4, (_T("[DevicePathToDosPath][dos_path=%s]"), *dos_path));
394 return S_OK;
395 }
396 }
397
398 return HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE);
399 }
400
401 } // namespace omaha
OLDNEW
« no previous file with comments | « base/disk.h ('k') | base/disk_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698