| // Copyright 2018 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // Adapted from platform_file_posix.cc |
| |
| #include "base/files/file_starboard.h" |
| |
| #include <errno.h> |
| |
| #include "base/files/file.h" |
| #include "base/files/file_path.h" |
| #include "base/notreached.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "starboard/common/metrics/stats_tracker.h" |
| #include "starboard/file.h" |
| |
| namespace base { |
| |
| namespace { |
| SbFileError g_sb_file_error = kSbFileOk; |
| } // namespace |
| |
| void SetLastFileError(File::Error error) { |
| g_sb_file_error = static_cast<SbFileError>(error); |
| } |
| |
| void RecordFileWriteStat(int write_file_result) { |
| auto& stats_tracker = |
| starboard::StatsTrackerContainer::GetInstance()->stats_tracker(); |
| if (write_file_result <= 0) { |
| stats_tracker.FileWriteFail(); |
| } else { |
| stats_tracker.FileWriteSuccess(); |
| stats_tracker.FileWriteBytesWritten(/*bytes_written=*/write_file_result); |
| } |
| } |
| |
| // Make sure our Whence mappings match the system headers. |
| static_assert(File::FROM_BEGIN == static_cast<int>(kSbFileFromBegin) && |
| File::FROM_CURRENT == static_cast<int>(kSbFileFromCurrent) && |
| File::FROM_END == static_cast<int>(kSbFileFromEnd), |
| "Whence enums from base must match those of Starboard."); |
| |
| bool File::IsValid() const { |
| return file_.is_valid(); |
| } |
| |
| PlatformFile File::GetPlatformFile() const { |
| return file_.get(); |
| } |
| |
| PlatformFile File::TakePlatformFile() { |
| return file_.release(); |
| } |
| |
| void File::Close() { |
| if (!IsValid()) |
| return; |
| |
| SCOPED_FILE_TRACE("Close"); |
| internal::AssertBlockingAllowed(); |
| file_.reset(); |
| } |
| |
| int64_t File::Seek(Whence whence, int64_t offset) { |
| internal::AssertBlockingAllowed(); |
| DCHECK(IsValid()); |
| |
| SCOPED_FILE_TRACE_WITH_SIZE("Seek", offset); |
| return SbFileSeek(file_.get(), static_cast<SbFileWhence>(whence), offset); |
| } |
| |
| int File::Read(int64_t offset, char* data, int size) { |
| internal::AssertBlockingAllowed(); |
| DCHECK(IsValid()); |
| if (size < 0) { |
| return -1; |
| } |
| |
| SCOPED_FILE_TRACE_WITH_SIZE("Read", size); |
| |
| int original_position = SbFileSeek(file_.get(), kSbFileFromCurrent, 0); |
| if (original_position < 0) { |
| return -1; |
| } |
| |
| int position = SbFileSeek(file_.get(), kSbFileFromBegin, offset); |
| int result = 0; |
| if (position == offset) { |
| result = ReadAtCurrentPos(data, size); |
| } |
| |
| // Restore position regardless of result of write. |
| position = SbFileSeek(file_.get(), kSbFileFromBegin, original_position); |
| if (result < 0) { |
| return result; |
| } |
| |
| if (position < 0) { |
| return -1; |
| } |
| |
| return result; |
| } |
| |
| int File::ReadAtCurrentPos(char* data, int size) { |
| internal::AssertBlockingAllowed(); |
| DCHECK(IsValid()); |
| if (size < 0) |
| return -1; |
| |
| SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size); |
| |
| return SbFileReadAll(file_.get(), data, size); |
| } |
| |
| int File::ReadNoBestEffort(int64_t offset, char* data, int size) { |
| internal::AssertBlockingAllowed(); |
| DCHECK(IsValid()); |
| SCOPED_FILE_TRACE_WITH_SIZE("ReadNoBestEffort", size); |
| |
| int original_position = SbFileSeek(file_.get(), kSbFileFromCurrent, 0); |
| if (original_position < 0) { |
| return -1; |
| } |
| |
| int position = SbFileSeek(file_.get(), kSbFileFromBegin, offset); |
| int result = 0; |
| if (position == offset) { |
| result = SbFileRead(file_.get(), data, size); |
| } |
| |
| // Restore position regardless of result of read. |
| position = SbFileSeek(file_.get(), kSbFileFromBegin, original_position); |
| if (result < 0) { |
| return result; |
| } |
| |
| if (position < 0) { |
| return -1; |
| } |
| |
| return result; |
| } |
| |
| int File::ReadAtCurrentPosNoBestEffort(char* data, int size) { |
| internal::AssertBlockingAllowed(); |
| DCHECK(IsValid()); |
| if (size < 0) |
| return -1; |
| |
| SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPosNoBestEffort", size); |
| return SbFileRead(file_.get(), data, size); |
| } |
| |
| int File::Write(int64_t offset, const char* data, int size) { |
| internal::AssertBlockingAllowed(); |
| |
| if (append_) { |
| return WriteAtCurrentPos(data, size); |
| } |
| |
| int original_position = SbFileSeek(file_.get(), kSbFileFromCurrent, 0); |
| if (original_position < 0) { |
| return -1; |
| } |
| |
| int64_t position = SbFileSeek(file_.get(), kSbFileFromBegin, offset); |
| int result = 0; |
| if (position == offset) { |
| result = WriteAtCurrentPos(data, size); |
| } |
| |
| // Restore position regardless of result of write. |
| position = SbFileSeek(file_.get(), kSbFileFromBegin, original_position); |
| if (result < 0) { |
| return result; |
| } |
| |
| if (position < 0) { |
| return -1; |
| } |
| |
| return result; |
| } |
| |
| int File::WriteAtCurrentPos(const char* data, int size) { |
| internal::AssertBlockingAllowed(); |
| DCHECK(IsValid()); |
| if (size < 0) |
| return -1; |
| |
| SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size); |
| int write_result = SbFileWriteAll(file_.get(), data, size); |
| RecordFileWriteStat(write_result); |
| return write_result; |
| } |
| |
| int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) { |
| internal::AssertBlockingAllowed(); |
| DCHECK(IsValid()); |
| if (size < 0) |
| return -1; |
| |
| SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPosNoBestEffort", size); |
| int write_result = SbFileWrite(file_.get(), data, size); |
| RecordFileWriteStat(write_result); |
| return write_result; |
| } |
| |
| int64_t File::GetLength() { |
| DCHECK(IsValid()); |
| |
| SCOPED_FILE_TRACE("GetLength"); |
| |
| File::Info file_info; |
| if (!GetInfo(&file_info)) { |
| return -1; |
| } |
| |
| return file_info.size; |
| } |
| |
| bool File::SetLength(int64_t length) { |
| internal::AssertBlockingAllowed(); |
| DCHECK(IsValid()); |
| |
| SCOPED_FILE_TRACE_WITH_SIZE("SetLength", length); |
| return SbFileTruncate(file_.get(), length); |
| } |
| |
| bool File::SetTimes(Time last_access_time, Time last_modified_time) { |
| internal::AssertBlockingAllowed(); |
| DCHECK(IsValid()); |
| |
| SCOPED_FILE_TRACE("SetTimes"); |
| |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| bool File::GetInfo(Info* info) { |
| DCHECK(IsValid()); |
| |
| SCOPED_FILE_TRACE("GetInfo"); |
| |
| if (!info || !SbFileIsValid(file_.get())) |
| return false; |
| |
| SbFileInfo file_info; |
| if (!SbFileGetInfo(file_.get(), &file_info)) |
| return false; |
| |
| info->is_directory = file_info.is_directory; |
| info->is_symbolic_link = file_info.is_symbolic_link; |
| info->size = file_info.size; |
| info->last_modified = base::Time::FromDeltaSinceWindowsEpoch( |
| base::TimeDelta::FromMicroseconds(file_info.last_modified)); |
| info->last_accessed = base::Time::FromDeltaSinceWindowsEpoch( |
| base::TimeDelta::FromMicroseconds(file_info.last_accessed)); |
| info->creation_time = base::Time::FromDeltaSinceWindowsEpoch( |
| base::TimeDelta::FromMicroseconds(file_info.creation_time)); |
| return true; |
| } |
| |
| File::Error File::GetLastFileError() { |
| return base::File::OSErrorToFileError(g_sb_file_error); |
| } |
| |
| // Static. |
| File::Error File::OSErrorToFileError(SbSystemError sb_system_error) { |
| switch (static_cast<SbFileError>(sb_system_error)) { |
| case kSbFileOk: |
| return FILE_OK; |
| case kSbFileErrorFailed: |
| return FILE_ERROR_FAILED; |
| case kSbFileErrorInUse: |
| return FILE_ERROR_IN_USE; |
| case kSbFileErrorExists: |
| return FILE_ERROR_EXISTS; |
| case kSbFileErrorNotFound: |
| return FILE_ERROR_NOT_FOUND; |
| case kSbFileErrorAccessDenied: |
| return FILE_ERROR_ACCESS_DENIED; |
| case kSbFileErrorTooManyOpened: |
| return FILE_ERROR_TOO_MANY_OPENED; |
| case kSbFileErrorNoMemory: |
| return FILE_ERROR_NO_MEMORY; |
| case kSbFileErrorNoSpace: |
| return FILE_ERROR_NO_SPACE; |
| case kSbFileErrorNotADirectory: |
| return FILE_ERROR_NOT_A_DIRECTORY; |
| case kSbFileErrorInvalidOperation: |
| return FILE_ERROR_INVALID_OPERATION; |
| case kSbFileErrorSecurity: |
| return FILE_ERROR_SECURITY; |
| case kSbFileErrorAbort: |
| return FILE_ERROR_ABORT; |
| case kSbFileErrorNotAFile: |
| return FILE_ERROR_NOT_A_FILE; |
| case kSbFileErrorNotEmpty: |
| return FILE_ERROR_NOT_EMPTY; |
| case kSbFileErrorInvalidUrl: |
| return FILE_ERROR_INVALID_URL; |
| case kSbFileErrorIO: |
| return FILE_ERROR_IO; |
| default: |
| NOTREACHED() << "Unrecognized SbSystemError: " << sb_system_error; |
| break; |
| } |
| return FILE_ERROR_FAILED; |
| } |
| |
| void File::DoInitialize(const FilePath& path, uint32_t flags) { |
| internal::AssertBlockingAllowed(); |
| DCHECK(!IsValid()); |
| |
| created_ = false; |
| append_ = flags & FLAG_APPEND; |
| file_name_ = path.AsUTF8Unsafe(); |
| |
| int open_flags = 0; |
| switch (flags & (FLAG_OPEN | FLAG_CREATE | FLAG_OPEN_ALWAYS | |
| FLAG_CREATE_ALWAYS | FLAG_OPEN_TRUNCATED)) { |
| case FLAG_OPEN: |
| open_flags = kSbFileOpenOnly; |
| break; |
| |
| case FLAG_CREATE: |
| open_flags = kSbFileCreateOnly; |
| break; |
| |
| case FLAG_OPEN_ALWAYS: |
| open_flags = kSbFileOpenAlways; |
| break; |
| |
| case FLAG_CREATE_ALWAYS: |
| open_flags = kSbFileCreateAlways; |
| break; |
| |
| case FLAG_OPEN_TRUNCATED: |
| open_flags = kSbFileOpenTruncated; |
| DCHECK(flags & FLAG_WRITE); |
| break; |
| |
| default: |
| NOTREACHED() << "Passed incompatible flags: " << flags; |
| error_details_ = FILE_ERROR_FAILED; |
| } |
| |
| if (flags & FLAG_READ) { |
| open_flags |= kSbFileRead; |
| } |
| |
| if (flags & FLAG_WRITE || flags & FLAG_APPEND) { |
| open_flags |= kSbFileWrite; |
| } |
| |
| file_.reset(SbFileOpen(path.value().c_str(), open_flags, &created_, |
| &g_sb_file_error)); |
| |
| if (!file_.is_valid()) { |
| error_details_ = OSErrorToFileError(g_sb_file_error); |
| } else { |
| error_details_ = FILE_OK; |
| if (append_) { |
| SbFileSeek(file_.get(), kSbFileFromEnd, 0); |
| } |
| } |
| |
| if (flags & FLAG_DELETE_ON_CLOSE) { |
| NOTREACHED() << "Not supported on Starboard platforms right now."; |
| } |
| |
| async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC); |
| } |
| |
| bool File::Flush() { |
| internal::AssertBlockingAllowed(); |
| DCHECK(IsValid()); |
| SCOPED_FILE_TRACE("Flush"); |
| |
| return SbFileFlush(file_.get()); |
| } |
| |
| void File::SetPlatformFile(PlatformFile file) { |
| DCHECK(!file_.is_valid()); |
| file_.reset(file); |
| } |
| |
| File File::Duplicate() const { |
| NOTREACHED(); |
| return File(); |
| } |
| |
| } // namespace base |