Home | History | Annotate | Download | only in image_writer
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <windows.h>
      6 #include <setupapi.h>
      7 #include <winioctl.h>
      8 
      9 #include "chrome/utility/image_writer/error_messages.h"
     10 #include "chrome/utility/image_writer/image_writer.h"
     11 
     12 namespace image_writer {
     13 
     14 const size_t kStorageQueryBufferSize = 1024;
     15 
     16 bool ImageWriter::IsValidDevice() {
     17   base::win::ScopedHandle device_handle(
     18       CreateFile(device_path_.value().c_str(),
     19                  GENERIC_READ | GENERIC_WRITE,
     20                  FILE_SHARE_READ | FILE_SHARE_WRITE,
     21                  NULL,
     22                  OPEN_EXISTING,
     23                  FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH,
     24                  NULL));
     25   if (device_handle == INVALID_HANDLE_VALUE) {
     26     Error(error::kOpenDevice);
     27     return false;
     28   }
     29 
     30   STORAGE_PROPERTY_QUERY query = STORAGE_PROPERTY_QUERY();
     31   query.PropertyId = StorageDeviceProperty;
     32   query.QueryType = PropertyStandardQuery;
     33   DWORD bytes_returned;
     34 
     35   scoped_ptr<char[]> output_buf(new char[kStorageQueryBufferSize]);
     36   BOOL status = DeviceIoControl(
     37       device_handle,                   // Device handle.
     38       IOCTL_STORAGE_QUERY_PROPERTY,    // Flag to request device properties.
     39       &query,                          // Query parameters.
     40       sizeof(STORAGE_PROPERTY_QUERY),  // query parameters size.
     41       output_buf.get(),                // output buffer.
     42       kStorageQueryBufferSize,         // Size of buffer.
     43       &bytes_returned,                 // Number of bytes returned.
     44                                        // Must not be null.
     45       NULL);                           // Optional unused overlapped perameter.
     46 
     47   if (!status) {
     48     PLOG(ERROR) << "Storage property query failed";
     49     return false;
     50   }
     51 
     52   STORAGE_DEVICE_DESCRIPTOR* device_descriptor =
     53       reinterpret_cast<STORAGE_DEVICE_DESCRIPTOR*>(output_buf.get());
     54 
     55   return device_descriptor->RemovableMedia == TRUE;
     56 }
     57 
     58 bool ImageWriter::OpenDevice() {
     59   // Windows requires that device files be opened with FILE_FLAG_NO_BUFFERING
     60   // and FILE_FLAG_WRITE_THROUGH.  These two flags are not part of base::File.
     61   device_file_ =
     62       base::File(CreateFile(device_path_.value().c_str(),
     63                             GENERIC_READ | GENERIC_WRITE,
     64                             FILE_SHARE_READ | FILE_SHARE_WRITE,
     65                             NULL,
     66                             OPEN_EXISTING,
     67                             FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH,
     68                             NULL));
     69   return device_file_.IsValid();
     70 }
     71 
     72 void ImageWriter::UnmountVolumes(const base::Closure& continuation) {
     73   if (!InitializeFiles()) {
     74     return;
     75   }
     76 
     77   STORAGE_DEVICE_NUMBER sdn = {0};
     78   DWORD bytes_returned;
     79 
     80   BOOL status = DeviceIoControl(
     81       device_file_.GetPlatformFile(),
     82       IOCTL_STORAGE_GET_DEVICE_NUMBER,
     83       NULL,             // Unused, must be NULL.
     84       0,                // Unused, must be 0.
     85       &sdn,             // An input buffer to hold the STORAGE_DEVICE_NUMBER
     86       sizeof(sdn),      // The size of the input buffer.
     87       &bytes_returned,  // the actual number of bytes returned.
     88       NULL);            // Unused overlap.
     89   if (!status) {
     90     PLOG(ERROR) << "Unable to get device number.";
     91     return;
     92   }
     93 
     94   ULONG device_number = sdn.DeviceNumber;
     95 
     96   TCHAR volume_path[MAX_PATH + 1];
     97   HANDLE volume_finder = FindFirstVolume(volume_path, MAX_PATH + 1);
     98   if (volume_finder == INVALID_HANDLE_VALUE) {
     99     return;
    100   }
    101 
    102   HANDLE volume_handle;
    103   bool first_volume = true;
    104   bool success = true;
    105 
    106   while (first_volume ||
    107          FindNextVolume(volume_finder, volume_path, MAX_PATH + 1)) {
    108     first_volume = false;
    109 
    110     size_t length = wcsnlen(volume_path, MAX_PATH + 1);
    111     if (length < 1) {
    112       continue;
    113     }
    114     volume_path[length - 1] = L'\0';
    115 
    116     volume_handle = CreateFile(volume_path,
    117                                GENERIC_READ | GENERIC_WRITE,
    118                                FILE_SHARE_READ | FILE_SHARE_WRITE,
    119                                NULL,
    120                                OPEN_EXISTING,
    121                                0,
    122                                NULL);
    123     if (volume_handle == INVALID_HANDLE_VALUE) {
    124       PLOG(ERROR) << "Opening volume handle failed.";
    125       success = false;
    126       break;
    127     }
    128 
    129     volume_handles_.push_back(volume_handle);
    130 
    131     VOLUME_DISK_EXTENTS disk_extents = {0};
    132     status = DeviceIoControl(volume_handle,
    133                              IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
    134                              NULL,
    135                              0,
    136                              &disk_extents,
    137                              sizeof(disk_extents),
    138                              &bytes_returned,
    139                              NULL);
    140 
    141     if (!status) {
    142       DWORD error = GetLastError();
    143       if (error == ERROR_MORE_DATA || error == ERROR_INVALID_FUNCTION ||
    144           error == ERROR_NOT_READY) {
    145         continue;
    146       } else {
    147         PLOG(ERROR) << "Unable to get volume disk extents.";
    148         success = false;
    149         break;
    150       }
    151     }
    152 
    153     if (disk_extents.NumberOfDiskExtents != 1 ||
    154         disk_extents.Extents[0].DiskNumber != device_number) {
    155       continue;
    156     }
    157 
    158     status = DeviceIoControl(volume_handle,
    159                              FSCTL_LOCK_VOLUME,
    160                              NULL,
    161                              0,
    162                              NULL,
    163                              0,
    164                              &bytes_returned,
    165                              NULL);
    166     if (!status) {
    167       PLOG(ERROR) << "Unable to lock volume.";
    168       success = false;
    169       break;
    170     }
    171 
    172     status = DeviceIoControl(volume_handle,
    173                              FSCTL_DISMOUNT_VOLUME,
    174                              NULL,
    175                              0,
    176                              NULL,
    177                              0,
    178                              &bytes_returned,
    179                              NULL);
    180     if (!status) {
    181       DWORD error = GetLastError();
    182       if (error != ERROR_NOT_SUPPORTED) {
    183         PLOG(ERROR) << "Unable to dismount volume.";
    184         success = false;
    185         break;
    186       }
    187     }
    188   }
    189 
    190   if (volume_finder != INVALID_HANDLE_VALUE) {
    191     FindVolumeClose(volume_finder);
    192   }
    193 
    194   if (success)
    195     continuation.Run();
    196 }
    197 
    198 }  // namespace image_writer
    199