blob: 86d5f21b29716b3e2a7efa3a1b8e1e4a57f97cf8 [file] [log] [blame]
// 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