123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651 |
- // Copyright 2019 The Chromium 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 "storage/browser/file_system/obfuscated_file_util_memory_delegate.h"
- #include <algorithm>
- #include <utility>
- #include "base/allocator/partition_allocator/partition_alloc_constants.h"
- #include "base/files/file_util.h"
- #include "base/numerics/checked_math.h"
- #include "base/numerics/safe_conversions.h"
- #include "base/system/sys_info.h"
- #include "build/build_config.h"
- #include "net/base/io_buffer.h"
- #include "net/base/net_errors.h"
- namespace {
- // We are giving a relatively large quota to in-memory filesystem (see
- // quota_settings.cc), but we do not allocate this memory beforehand for the
- // filesystem. Therefore, specially on low-end devices, a website can get to a
- // state where it has not used all its quota, but there is no more memory
- // available and memory allocation fails and results in Chrome crash.
- // By checking for availability of memory before allocating it, we reduce the
- // crash possibility.
- // Note that quota assignment is the same for on-disk filesystem and the
- // assigned quota is not guaranteed to be allocatable later.
- bool IsMemoryAvailable(size_t required_memory) {
- #if BUILDFLAG(IS_FUCHSIA)
- // This function is not implemented on FUCHSIA, yet. (crbug.com/986608)
- return true;
- #else
- uint64_t max_allocatable =
- std::min(base::SysInfo::AmountOfAvailablePhysicalMemory(),
- static_cast<uint64_t>(partition_alloc::MaxDirectMapped()));
- return max_allocatable >= required_memory;
- #endif
- }
- } // namespace
- namespace storage {
- // Struct keeping one entry of the directory tree.
- struct ObfuscatedFileUtilMemoryDelegate::Entry {
- enum Type { kDirectory, kFile };
- Entry(Type type) : type(type) {
- creation_time = base::Time::Now();
- last_modified = creation_time;
- last_accessed = last_modified;
- }
- Entry(const Entry&) = delete;
- Entry& operator=(const Entry&) = delete;
- Entry(Entry&&) = default;
- ~Entry() = default;
- Type type;
- base::Time creation_time;
- base::Time last_modified;
- base::Time last_accessed;
- std::map<base::FilePath::StringType, Entry> directory_content;
- std::vector<uint8_t> file_content;
- };
- // Keeps a decomposed FilePath.
- struct ObfuscatedFileUtilMemoryDelegate::DecomposedPath {
- // Entry in the directory structure that the input |path| referes to,
- // nullptr if the entry does not exist.
- Entry* entry = nullptr;
- // Parent of the |path| in the directory structure, nullptr if not exists.
- Entry* parent = nullptr;
- // Normalized components of the path after the |root_|. E.g., if the root
- // is 'foo/' and the path is 'foo/./bar/baz', it will be ['bar', 'baz'].
- std::vector<base::FilePath::StringType> components;
- };
- ObfuscatedFileUtilMemoryDelegate::ObfuscatedFileUtilMemoryDelegate(
- const base::FilePath& file_system_directory)
- : root_(std::make_unique<Entry>(Entry::kDirectory)),
- root_path_components_(file_system_directory.GetComponents()) {
- DETACH_FROM_SEQUENCE(sequence_checker_);
- }
- ObfuscatedFileUtilMemoryDelegate::~ObfuscatedFileUtilMemoryDelegate() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- }
- absl::optional<ObfuscatedFileUtilMemoryDelegate::DecomposedPath>
- ObfuscatedFileUtilMemoryDelegate::ParsePath(const base::FilePath& path) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DecomposedPath dp;
- dp.components = path.GetComponents();
- // Ensure |path| is under |root_|.
- if (dp.components.size() < root_path_components_.size())
- return absl::nullopt;
- for (size_t i = 0; i < root_path_components_.size(); i++)
- if (dp.components[i] != root_path_components_[i])
- return absl::nullopt;
- dp.components.erase(dp.components.begin(),
- dp.components.begin() + root_path_components_.size());
- // Normalize path.
- for (size_t i = 0; i < dp.components.size(); i++) {
- if (dp.components[i] == base::FilePath::kCurrentDirectory) {
- dp.components.erase(dp.components.begin() + i);
- i--;
- } else if (dp.components[i] == base::FilePath::kParentDirectory) {
- // Beyond |root|?
- if (!i)
- return absl::nullopt;
- dp.components.erase(dp.components.begin() + i - 1,
- dp.components.begin() + i + 1);
- i -= 2;
- }
- }
- // Find entry and parent.
- dp.parent = nullptr;
- dp.entry = root_.get();
- for (size_t i = 0; i < dp.components.size(); i++) {
- auto child = dp.entry->directory_content.find(dp.components[i]);
- if (child == dp.entry->directory_content.end()) {
- // If just the last component is not found and the last found part is a
- // directory keep the parent.
- if (i == dp.components.size() - 1 && dp.entry->type == Entry::kDirectory)
- dp.parent = dp.entry;
- else
- dp.parent = nullptr;
- dp.entry = nullptr;
- break;
- }
- dp.parent = dp.entry;
- dp.entry = &child->second;
- }
- return dp;
- }
- bool ObfuscatedFileUtilMemoryDelegate::DirectoryExists(
- const base::FilePath& path) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- absl::optional<DecomposedPath> dp = ParsePath(path);
- return dp && dp->entry && dp->entry->type == Entry::kDirectory;
- }
- base::File::Error ObfuscatedFileUtilMemoryDelegate::CreateDirectory(
- const base::FilePath& path,
- bool exclusive,
- bool recursive) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- absl::optional<DecomposedPath> dp = ParsePath(path);
- if (!dp)
- return base::File::FILE_ERROR_NOT_FOUND;
- // If path already exists, ensure it's not a file and exclusive access is not
- // requested.
- if (dp->entry) {
- if (exclusive || dp->entry->type == Entry::kFile)
- return base::File::FILE_ERROR_EXISTS;
- return base::File::FILE_OK;
- }
- // If parent exists, add the new directory.
- if (dp->parent) {
- dp->parent->directory_content.emplace(dp->components.back(),
- Entry::kDirectory);
- return base::File::FILE_OK;
- }
- // Recursively add all required ancesstors if allowed.
- if (!recursive)
- return base::File::FILE_ERROR_NOT_FOUND;
- Entry* entry = root_.get();
- bool check_existings = true;
- for (const auto& c : dp->components) {
- if (check_existings) {
- auto child = entry->directory_content.find(c);
- if (child != entry->directory_content.end()) {
- entry = &child->second;
- continue;
- }
- check_existings = false;
- }
- entry =
- &entry->directory_content.emplace(c, Entry::kDirectory).first->second;
- }
- return base::File::FILE_OK;
- }
- bool ObfuscatedFileUtilMemoryDelegate::DeleteFileOrDirectory(
- const base::FilePath& path,
- bool recursive) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- absl::optional<DecomposedPath> dp = ParsePath(path);
- if (!dp)
- return false;
- if (!dp->entry)
- return true;
- if (!recursive && dp->entry->directory_content.size())
- return false;
- dp->parent->directory_content.erase(dp->components.back());
- return true;
- }
- bool ObfuscatedFileUtilMemoryDelegate::IsLink(const base::FilePath& file_path) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // In-memory file system does not support links.
- return false;
- }
- bool ObfuscatedFileUtilMemoryDelegate::PathExists(const base::FilePath& path) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- absl::optional<DecomposedPath> dp = ParsePath(path);
- return dp && dp->entry;
- }
- base::File ObfuscatedFileUtilMemoryDelegate::CreateOrOpen(
- const base::FilePath& path,
- uint32_t file_flags) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // TODO:(https://crbug.com/936722): Once the output of this function is
- // changed to base::File::Error, it can use CreateOrOpenInternal to perform
- // the task and return the result.
- return base::File(base::File::FILE_ERROR_INVALID_OPERATION);
- }
- void ObfuscatedFileUtilMemoryDelegate::CreateOrOpenInternal(
- const DecomposedPath& dp,
- uint32_t file_flags) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!dp.entry) {
- dp.parent->directory_content.emplace(dp.components.back(), Entry::kFile);
- return;
- }
- if (dp.entry->file_content.size()) {
- if (file_flags &
- (base::File::FLAG_OPEN_TRUNCATED | base::File::FLAG_CREATE_ALWAYS)) {
- dp.entry->file_content.clear();
- }
- }
- }
- base::File::Error ObfuscatedFileUtilMemoryDelegate::DeleteFile(
- const base::FilePath& path) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- absl::optional<DecomposedPath> dp = ParsePath(path);
- if (!dp || !dp->entry)
- return base::File::FILE_ERROR_NOT_FOUND;
- if (dp->entry->type != Entry::kFile)
- return base::File::FILE_ERROR_NOT_A_FILE;
- dp->parent->directory_content.erase(dp->components.back());
- return base::File::FILE_OK;
- }
- base::File::Error ObfuscatedFileUtilMemoryDelegate::EnsureFileExists(
- const base::FilePath& path,
- bool* created) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- absl::optional<DecomposedPath> dp = ParsePath(path);
- *created = false;
- if (!dp || !dp->parent)
- return base::File::FILE_ERROR_NOT_FOUND;
- if (dp->entry) {
- return dp->entry->type == Entry::kFile ? base::File::FILE_OK
- : base::File::FILE_ERROR_NOT_FOUND;
- }
- CreateOrOpenInternal(*dp, base::File::FLAG_CREATE);
- *created = true;
- return base::File::FILE_OK;
- }
- base::File::Error ObfuscatedFileUtilMemoryDelegate::GetFileInfo(
- const base::FilePath& path,
- base::File::Info* file_info) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- absl::optional<DecomposedPath> dp = ParsePath(path);
- if (!dp || !dp->entry)
- return base::File::FILE_ERROR_NOT_FOUND;
- file_info->size = dp->entry->file_content.size();
- file_info->is_directory = (dp->entry->type == Entry::kDirectory);
- file_info->is_symbolic_link = false;
- file_info->creation_time = dp->entry->creation_time;
- file_info->last_modified = dp->entry->last_modified;
- file_info->last_accessed = dp->entry->last_accessed;
- DCHECK(file_info->size == 0 || !file_info->is_directory);
- return base::File::FILE_OK;
- }
- base::File::Error ObfuscatedFileUtilMemoryDelegate::Touch(
- const base::FilePath& path,
- const base::Time& last_access_time,
- const base::Time& last_modified_time) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- absl::optional<DecomposedPath> dp = ParsePath(path);
- if (!dp || !dp->entry)
- return base::File::FILE_ERROR_FAILED;
- dp->entry->last_accessed = last_access_time;
- dp->entry->last_modified = last_modified_time;
- return base::File::FILE_OK;
- }
- base::File::Error ObfuscatedFileUtilMemoryDelegate::Truncate(
- const base::FilePath& path,
- int64_t length) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- absl::optional<DecomposedPath> dp = ParsePath(path);
- if (!dp || !dp->entry || dp->entry->type != Entry::kFile)
- return base::File::FILE_ERROR_NOT_FOUND;
- // Fail if enough memory is not available.
- if (!base::IsValueInRangeForNumericType<size_t>(length) ||
- (static_cast<size_t>(length) > dp->entry->file_content.capacity() &&
- !IsMemoryAvailable(static_cast<size_t>(length)))) {
- return base::File::FILE_ERROR_NO_SPACE;
- }
- dp->entry->file_content.resize(static_cast<size_t>(length));
- return base::File::FILE_OK;
- }
- NativeFileUtil::CopyOrMoveMode
- ObfuscatedFileUtilMemoryDelegate::CopyOrMoveModeForDestination(
- const FileSystemURL& /*dest_url*/,
- bool copy) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return copy ? NativeFileUtil::CopyOrMoveMode::COPY_SYNC
- : NativeFileUtil::CopyOrMoveMode::MOVE;
- }
- base::File::Error ObfuscatedFileUtilMemoryDelegate::CopyOrMoveFile(
- const base::FilePath& src_path,
- const base::FilePath& dest_path,
- FileSystemOperation::CopyOrMoveOptionSet options,
- NativeFileUtil::CopyOrMoveMode mode) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- absl::optional<DecomposedPath> src_dp = ParsePath(src_path);
- absl::optional<DecomposedPath> dest_dp = ParsePath(dest_path);
- if (!src_dp || !src_dp->entry || !dest_dp || !dest_dp->parent)
- return base::File::FILE_ERROR_NOT_FOUND;
- bool src_is_directory = src_dp->entry->type == Entry::kDirectory;
- // For directories, only 'move' is supported.
- if (src_is_directory && mode != NativeFileUtil::CopyOrMoveMode::MOVE) {
- return base::File::FILE_ERROR_NOT_A_FILE;
- }
- base::Time last_modified = src_dp->entry->last_modified;
- if (dest_dp->entry) {
- if (dest_dp->entry->type != src_dp->entry->type)
- return base::File::FILE_ERROR_INVALID_OPERATION;
- #if BUILDFLAG(IS_WIN)
- // Overwriting an empty directory with another directory isn't
- // supported natively on Windows.
- // To keep the behavior indistinguishable from on-disk operation,
- // in-memory implementation also fails.
- if (src_is_directory)
- return base::File::FILE_ERROR_NOT_A_FILE;
- #endif
- }
- switch (mode) {
- case NativeFileUtil::CopyOrMoveMode::COPY_NOSYNC:
- case NativeFileUtil::CopyOrMoveMode::COPY_SYNC:
- DCHECK(!src_is_directory);
- // Fail if enough memory is not available.
- if (!IsMemoryAvailable(src_dp->entry->file_content.size()))
- return base::File::FILE_ERROR_NO_SPACE;
- if (!CopyOrMoveFileInternal(*src_dp, *dest_dp, false))
- return base::File::FILE_ERROR_FAILED;
- break;
- case NativeFileUtil::CopyOrMoveMode::MOVE:
- if (src_is_directory) {
- if (!MoveDirectoryInternal(*src_dp, *dest_dp))
- return base::File::FILE_ERROR_FAILED;
- } else {
- if (!CopyOrMoveFileInternal(*src_dp, *dest_dp, true))
- return base::File::FILE_ERROR_FAILED;
- }
- break;
- }
- if (options.Has(
- FileSystemOperation::CopyOrMoveOption::kPreserveLastModified)) {
- Touch(dest_path, last_modified, last_modified);
- }
- // Don't bother with the kPreserveDestinationPermissions option, since
- // this is not relevant to in-memory files.
- return base::File::FILE_OK;
- }
- bool ObfuscatedFileUtilMemoryDelegate::MoveDirectoryInternal(
- const DecomposedPath& src_dp,
- const DecomposedPath& dest_dp) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(src_dp.entry->type == Entry::kDirectory);
- if (!dest_dp.entry) {
- dest_dp.parent->directory_content.insert(
- std::make_pair(dest_dp.components.back(), std::move(*src_dp.entry)));
- } else {
- dest_dp.entry->directory_content.insert(
- std::make_move_iterator(src_dp.entry->directory_content.begin()),
- std::make_move_iterator(src_dp.entry->directory_content.end()));
- }
- src_dp.parent->directory_content.erase(src_dp.components.back());
- return true;
- }
- bool ObfuscatedFileUtilMemoryDelegate::CopyOrMoveFileInternal(
- const DecomposedPath& src_dp,
- const DecomposedPath& dest_dp,
- bool move) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(src_dp.entry->type == Entry::kFile);
- if (dest_dp.entry)
- dest_dp.parent->directory_content.erase(dest_dp.components.back());
- if (move) {
- dest_dp.parent->directory_content.insert(
- std::make_pair(dest_dp.components.back(), std::move(*src_dp.entry)));
- src_dp.parent->directory_content.erase(src_dp.components.back());
- return true;
- }
- // Copy the file.
- Entry* entry = &dest_dp.parent->directory_content
- .emplace(dest_dp.components.back(), Entry::kFile)
- .first->second;
- entry->creation_time = src_dp.entry->creation_time;
- entry->last_modified = src_dp.entry->last_modified;
- entry->last_accessed = src_dp.entry->last_accessed;
- entry->file_content = src_dp.entry->file_content;
- return true;
- }
- size_t ObfuscatedFileUtilMemoryDelegate::ComputeDirectorySize(
- const base::FilePath& path) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- absl::optional<DecomposedPath> dp = ParsePath(path);
- if (!dp || !dp->entry || dp->entry->type != Entry::kDirectory)
- return 0;
- base::CheckedNumeric<size_t> running_sum = 0;
- std::vector<Entry*> directories;
- directories.push_back(dp->entry);
- while (!directories.empty()) {
- Entry* current = directories.back();
- directories.pop_back();
- for (auto& child : current->directory_content) {
- if (child.second.type == Entry::kDirectory)
- directories.push_back(&child.second);
- else
- running_sum += child.second.file_content.size();
- }
- }
- return running_sum.ValueOrDefault(0);
- }
- int ObfuscatedFileUtilMemoryDelegate::ReadFile(const base::FilePath& path,
- int64_t offset,
- scoped_refptr<net::IOBuffer> buf,
- int buf_len) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- absl::optional<DecomposedPath> dp = ParsePath(path);
- if (!dp || dp->entry->type != Entry::kFile)
- return net::ERR_FILE_NOT_FOUND;
- int64_t remaining = dp->entry->file_content.size() - offset;
- if (offset < 0)
- return net::ERR_INVALID_ARGUMENT;
- // Seeking past the end of the file is ok, but returns nothing.
- // This matches FileStream::Context behavior.
- if (remaining < 0)
- return 0;
- if (buf_len > remaining)
- buf_len = static_cast<int>(remaining);
- memcpy(buf->data(), dp->entry->file_content.data() + offset, buf_len);
- return buf_len;
- }
- int ObfuscatedFileUtilMemoryDelegate::WriteFile(
- const base::FilePath& path,
- int64_t offset,
- scoped_refptr<net::IOBuffer> buf,
- int buf_len) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- absl::optional<DecomposedPath> dp = ParsePath(path);
- if (!dp || !dp->entry || dp->entry->type != Entry::kFile)
- return net::ERR_FILE_NOT_FOUND;
- size_t offset_u = static_cast<size_t>(offset);
- // Fail if |offset| or |buf_len| not valid.
- if (offset < 0 || buf_len < 0)
- return net::ERR_REQUEST_RANGE_NOT_SATISFIABLE;
- // Fail if result doesn't fit in a std::vector.
- if (std::numeric_limits<size_t>::max() - offset_u <
- static_cast<size_t>(buf_len))
- return net::ERR_REQUEST_RANGE_NOT_SATISFIABLE;
- const size_t last_position = offset_u + buf_len;
- if (last_position > dp->entry->file_content.capacity()) {
- // Fail if enough memory is not available.
- if (!IsMemoryAvailable(last_position))
- return net::ERR_FILE_NO_SPACE;
- // If required memory is bigger than half of the max allocatable memory block,
- // reserve first to avoid STL getting more than required memory.
- // See crbug.com/1043914 for more context.
- // |MaxDirectMapped| function is not implemented on FUCHSIA, yet.
- // (crbug.com/986608)
- #if !BUILDFLAG(IS_FUCHSIA)
- if (last_position >= partition_alloc::MaxDirectMapped() / 2) {
- // TODO(https://crbug.com/1043914): Allocated memory is rounded up to
- // 100MB blocks to reduce memory allocation delays. Switch to a more
- // proper container to remove this dependency.
- const size_t round_up_size = 100 * 1024 * 1024;
- size_t rounded_up = ((last_position / round_up_size) + 1) * round_up_size;
- if (!IsMemoryAvailable(rounded_up))
- return net::ERR_FILE_NO_SPACE;
- dp->entry->file_content.reserve(rounded_up);
- }
- #endif
- }
- if (offset_u == dp->entry->file_content.size()) {
- dp->entry->file_content.insert(dp->entry->file_content.end(), buf->data(),
- buf->data() + buf_len);
- } else {
- if (last_position > dp->entry->file_content.size())
- dp->entry->file_content.resize(last_position);
- // if |offset_u| is larger than the original file size, there will be null
- // bytes between the end of the file and |offset_u|.
- memcpy(dp->entry->file_content.data() + offset, buf->data(), buf_len);
- }
- return buf_len;
- }
- base::File::Error ObfuscatedFileUtilMemoryDelegate::CreateFileForTesting(
- const base::FilePath& path,
- base::span<const char> content) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- bool created;
- base::File::Error result = EnsureFileExists(path, &created);
- if (result != base::File::FILE_OK)
- return result;
- absl::optional<DecomposedPath> dp = ParsePath(path);
- DCHECK(dp && dp->entry->type == Entry::kFile);
- dp->entry->file_content =
- std::vector<uint8_t>(content.begin(), content.end());
- return base::File::FILE_OK;
- }
- base::File::Error ObfuscatedFileUtilMemoryDelegate::CopyInForeignFile(
- const base::FilePath& src_path,
- const base::FilePath& dest_path,
- FileSystemOperation::CopyOrMoveOptionSet /* options */,
- NativeFileUtil::CopyOrMoveMode /* mode */) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- absl::optional<DecomposedPath> dest_dp = ParsePath(dest_path);
- if (!dest_dp || !dest_dp->parent)
- return base::File::FILE_ERROR_NOT_FOUND;
- base::File::Info source_info;
- if (!base::GetFileInfo(src_path, &source_info))
- return base::File::FILE_ERROR_NOT_FOUND;
- if (source_info.is_directory)
- return base::File::FILE_ERROR_NOT_A_FILE;
- // |size_t| limits the maximum size that the memory file can keep and |int|
- // limits the maximum size that base::ReadFile function reads.
- if (source_info.size > std::numeric_limits<size_t>::max() ||
- source_info.size > std::numeric_limits<int>::max()) {
- return base::File::FILE_ERROR_NO_SPACE;
- }
- // Fail if enough memory is not available.
- if (!IsMemoryAvailable(static_cast<size_t>(source_info.size)))
- return base::File::FILE_ERROR_NO_SPACE;
- // Create file.
- Entry* entry = &dest_dp->parent->directory_content
- .emplace(dest_dp->components.back(), Entry::kFile)
- .first->second;
- entry->creation_time = source_info.creation_time;
- entry->last_modified = source_info.last_modified;
- entry->last_accessed = source_info.last_accessed;
- // Read content.
- entry->file_content.resize(source_info.size);
- int read_bytes = base::ReadFile(
- src_path, reinterpret_cast<char*>(entry->file_content.data()),
- source_info.size);
- if (read_bytes != source_info.size) {
- // Delete file and return error if source could not be fully read or any
- // error happens.
- dest_dp->parent->directory_content.erase(dest_dp->components.back());
- return base::File::FILE_ERROR_FAILED;
- }
- return base::File::FILE_OK;
- }
- } // namespace storage
|