| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "mtpd/mtpd_server_impl.h" |
| |
| #include <base/logging.h> |
| #include <base/rand_util.h> |
| #include <base/stl_util.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/tracked_objects.h> |
| #include <chromeos/dbus/service_constants.h> |
| |
| namespace mtpd { |
| |
| namespace { |
| |
| // Maximum number of bytes to read from the device at one time. This is set low |
| // enough such that a reasonable device can read this much data before D-Bus |
| // times out. |
| const uint32_t kMaxReadCount = 1024 * 1024; |
| |
| void AddError(brillo::ErrorPtr* error, |
| const tracked_objects::Location& location, |
| const std::string& message) { |
| brillo::Error::AddTo(error, |
| location, |
| brillo::errors::dbus::kDomain, |
| kMtpdServiceError, |
| message); |
| } |
| |
| void AddInvalidHandleError(brillo::ErrorPtr* error, |
| const tracked_objects::Location& location, |
| const std::string& handle) { |
| brillo::Error::AddToPrintf(error, |
| location, |
| brillo::errors::dbus::kDomain, |
| kMtpdServiceError, |
| "Invalid handle %s", handle.c_str()); |
| } |
| |
| } // namespace |
| |
| MtpdServer::MtpdServer(scoped_refptr<dbus::Bus> bus) |
| : org::chromium::MtpdAdaptor(this), |
| dbus_object_(nullptr, bus, dbus::ObjectPath(kMtpdServicePath)), |
| device_manager_(this) {} |
| |
| MtpdServer::~MtpdServer() {} |
| |
| std::vector<std::string> MtpdServer::EnumerateStorages() { |
| return device_manager_.EnumerateStorages(); |
| } |
| |
| std::vector<uint8_t> MtpdServer::GetStorageInfo( |
| const std::string& storage_name) { |
| const StorageInfo* info = device_manager_.GetStorageInfo(storage_name); |
| return info ? info->ToDBusFormat() : StorageInfo().ToDBusFormat(); |
| } |
| |
| std::vector<uint8_t> MtpdServer::GetStorageInfoFromDevice( |
| const std::string& storage_name) { |
| const StorageInfo* info = |
| device_manager_.GetStorageInfoFromDevice(storage_name); |
| return info ? info->ToDBusFormat() : StorageInfo().ToDBusFormat(); |
| } |
| |
| bool MtpdServer::OpenStorage(brillo::ErrorPtr* error, |
| const std::string& storage_name, |
| const std::string& mode, |
| std::string* id) { |
| if (!(mode == kReadOnlyMode || mode == kReadWriteMode)) { |
| brillo::Error::AddToPrintf(error, |
| FROM_HERE, |
| brillo::errors::dbus::kDomain, |
| kMtpdServiceError, |
| "Cannot open %s in mode: %s", |
| storage_name.c_str(), mode.c_str()); |
| return false; |
| } |
| |
| if (!device_manager_.HasStorage(storage_name)) { |
| brillo::Error::AddToPrintf(error, |
| FROM_HERE, |
| brillo::errors::dbus::kDomain, |
| kMtpdServiceError, |
| "Cannot open unknown storage %s", |
| storage_name.c_str()); |
| return false; |
| } |
| |
| std::string new_id; |
| uint32_t random_data[4]; |
| do { |
| base::RandBytes(random_data, sizeof(random_data)); |
| new_id = base::HexEncode(random_data, sizeof(random_data)); |
| } while (base::ContainsKey(handle_map_, new_id)); |
| |
| handle_map_.insert( |
| std::make_pair(new_id, std::make_pair(storage_name, mode))); |
| *id = new_id; |
| return true; |
| } |
| |
| bool MtpdServer::CloseStorage(brillo::ErrorPtr* error, |
| const std::string& handle) { |
| if (handle_map_.erase(handle) == 0) { |
| AddInvalidHandleError(error, FROM_HERE, handle); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool MtpdServer::ReadDirectoryEntryIds( |
| brillo::ErrorPtr* error, |
| const std::string& handle, |
| uint32_t file_id, |
| std::vector<uint32_t>* directory_listing) { |
| std::string storage_name = LookupHandle(handle); |
| if (storage_name.empty()) { |
| AddInvalidHandleError(error, FROM_HERE, handle); |
| return false; |
| } |
| |
| if (!device_manager_.ReadDirectoryEntryIds(storage_name, file_id, |
| directory_listing)) { |
| AddError(error, FROM_HERE, "ReadDirectoryEntryIds failed"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool MtpdServer::GetFileInfo(brillo::ErrorPtr* error, |
| const std::string& handle, |
| const std::vector<uint32_t>& file_ids, |
| std::vector<uint8_t>* serialized_file_entries) { |
| if (file_ids.empty()) { |
| AddError(error, FROM_HERE, "GetFileInfo called with no file ids"); |
| return false; |
| } |
| |
| std::string storage_name = LookupHandle(handle); |
| if (storage_name.empty()) { |
| AddInvalidHandleError(error, FROM_HERE, handle); |
| return false; |
| } |
| |
| std::vector<FileEntry> file_info; |
| if (!device_manager_.GetFileInfo(storage_name, file_ids, &file_info)) { |
| AddError(error, FROM_HERE, "GetFileInfo failed"); |
| return false; |
| } |
| |
| *serialized_file_entries = FileEntry::FileEntriesToDBusFormat(file_info); |
| return true; |
| } |
| |
| bool MtpdServer::ReadFileChunk(brillo::ErrorPtr* error, |
| const std::string& handle, |
| uint32_t file_id, |
| uint32_t offset, |
| uint32_t count, |
| std::vector<uint8_t>* file_contents) { |
| if (count > kMaxReadCount || count == 0) { |
| AddError(error, FROM_HERE, "Invalid count for ReadFileChunk"); |
| return false; |
| } |
| std::string storage_name = LookupHandle(handle); |
| if (storage_name.empty()) { |
| AddInvalidHandleError(error, FROM_HERE, handle); |
| return false; |
| } |
| |
| if (!device_manager_.ReadFileChunk(storage_name, file_id, offset, count, |
| file_contents)) { |
| AddError(error, FROM_HERE, "ReadFileChunk failed"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool MtpdServer::CopyFileFromLocal(brillo::ErrorPtr* error, |
| const std::string& handle, |
| const base::ScopedFD& file_descriptor, |
| uint32_t parent_id, |
| const std::string& file_name) { |
| const std::string storage_name = LookupHandle(handle); |
| if (storage_name.empty() || !IsOpenedWithWrite(handle)) { |
| AddInvalidHandleError(error, FROM_HERE, handle); |
| return false; |
| } |
| |
| if (!device_manager_.CopyFileFromLocal(storage_name, file_descriptor.get(), |
| parent_id, file_name)) { |
| AddError(error, FROM_HERE, "CopyFileFromLocal failed"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool MtpdServer::DeleteObject(brillo::ErrorPtr* error, |
| const std::string& handle, |
| uint32_t object_id) { |
| const std::string storage_name = LookupHandle(handle); |
| if (storage_name.empty() || !IsOpenedWithWrite(handle)) { |
| AddInvalidHandleError(error, FROM_HERE, handle); |
| return false; |
| } |
| |
| if (!device_manager_.DeleteObject(storage_name, object_id)) { |
| AddError(error, FROM_HERE, "DeleteObject failed"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool MtpdServer::RenameObject(brillo::ErrorPtr* error, |
| const std::string& handle, |
| uint32_t object_id, |
| const std::string& new_name) { |
| const std::string storage_name = LookupHandle(handle); |
| if (storage_name.empty() || !IsOpenedWithWrite(handle)) { |
| AddInvalidHandleError(error, FROM_HERE, handle); |
| return false; |
| } |
| |
| if (!device_manager_.RenameObject(storage_name, object_id, new_name)) { |
| AddError(error, FROM_HERE, "RenameObject failed"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool MtpdServer::CreateDirectory(brillo::ErrorPtr* error, |
| const std::string& handle, |
| uint32_t parent_id, |
| const std::string& directory_name) { |
| const std::string storage_name = LookupHandle(handle); |
| if (storage_name.empty() || !IsOpenedWithWrite(handle)) { |
| AddInvalidHandleError(error, FROM_HERE, handle); |
| return false; |
| } |
| |
| if (!device_manager_.CreateDirectory(storage_name, parent_id, |
| directory_name)) { |
| AddError(error, FROM_HERE, "CreateDirectory failed."); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool MtpdServer::IsAlive() { |
| return true; |
| } |
| |
| void MtpdServer::StorageAttached(const std::string& storage_name) { |
| // Fire DBus signal. |
| SendMTPStorageAttachedSignal(storage_name); |
| } |
| |
| void MtpdServer::StorageDetached(const std::string& storage_name) { |
| // Fire DBus signal. |
| SendMTPStorageDetachedSignal(storage_name); |
| } |
| |
| int MtpdServer::GetDeviceEventDescriptor() const { |
| return device_manager_.GetDeviceEventDescriptor(); |
| } |
| |
| void MtpdServer::ProcessDeviceEvents() { |
| device_manager_.ProcessDeviceEvents(); |
| } |
| |
| std::string MtpdServer::LookupHandle(const std::string& handle) { |
| HandleMap::const_iterator it = handle_map_.find(handle); |
| return (it == handle_map_.end()) ? std::string() : it->second.first; |
| } |
| |
| bool MtpdServer::IsOpenedWithWrite(const std::string& handle) { |
| HandleMap::const_iterator it = handle_map_.find(handle); |
| return (it == handle_map_.end()) ? false |
| : it->second.second == kReadWriteMode; |
| } |
| |
| void MtpdServer::RegisterAsync( |
| const brillo::dbus_utils::AsyncEventSequencer::CompletionAction& cb) { |
| RegisterWithDBusObject(&dbus_object_); |
| dbus_object_.RegisterAsync(cb); |
| } |
| |
| } // namespace mtpd |