blob: 01b4bb14906fed33884cf720403541444a05308d [file] [log] [blame]
// 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 "pepper_file.h"
#include <assert.h>
#include "nacl_io/pepper_interface.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/ppb_file_io.h"
#include "ppapi/cpp/file_ref.h"
#include "file_system.h"
const size_t PepperFile::kBufSize;
PepperFileHandler::PepperFileHandler(pp::FileSystem* file_system)
: ref_(1), file_system_(file_system) {
assert(file_system);
}
PepperFileHandler::~PepperFileHandler() {
assert(!ref_);
}
void PepperFileHandler::addref() {
++ref_;
}
void PepperFileHandler::release() {
if (!--ref_)
delete this;
}
FileStream* PepperFileHandler::open(int fd, const char* pathname, int oflag,
int* err) {
PepperFile* file = new PepperFile(fd, oflag, file_system_);
int32_t ret = file->open(pathname);
if (ret == 0) {
return file;
} else {
*err = nacl_io::PPErrorToErrno(ret);
file->release();
return NULL;
}
}
int PepperFileHandler::stat(const char* pathname, nacl_abi_stat* out) {
memset(out, 0, sizeof(nacl_abi_stat));
return 0;
}
//------------------------------------------------------------------------------
PepperFile::PepperFile(int fd, int oflag, pp::FileSystem* file_system)
: ref_(1), fd_(fd), oflag_(oflag), factory_(this), file_system_(file_system),
file_io_(NULL), offset_(0), file_info_(), write_sent_(false) {
}
PepperFile::~PepperFile() {
assert(!ref_);
}
void PepperFile::addref() {
++ref_;
}
void PepperFile::release() {
if (!--ref_)
delete this;
}
FileStream* PepperFile::dup(int fd) {
assert(0);
return NULL;
}
int32_t PepperFile::open(const char* pathname) {
int32_t result = PP_OK_COMPLETIONPENDING;
pp::Module::Get()->core()->CallOnMainThread(0,
factory_.NewCallback(&PepperFile::Open, pathname, &result));
FileSystem* sys = FileSystem::GetFileSystem();
while (result == PP_OK_COMPLETIONPENDING)
sys->cond().wait(sys->mutex());
return result;
}
void PepperFile::close() {
int32_t result = PP_OK_COMPLETIONPENDING;
pp::Module::Get()->core()->CallOnMainThread(0,
factory_.NewCallback(&PepperFile::Close, &result));
FileSystem* sys = FileSystem::GetFileSystem();
while (result == PP_OK_COMPLETIONPENDING)
sys->cond().wait(sys->mutex());
}
int PepperFile::read(char* buf, size_t count, size_t* nread) {
if (!is_open())
return EIO;
FileSystem* sys = FileSystem::GetFileSystem();
if (is_block() && in_buf_.empty()) {
int32_t result = PP_OK_COMPLETIONPENDING;
pp::Module::Get()->core()->CallOnMainThread(0,
factory_.NewCallback(&PepperFile::Read, count, &result));
while (result == PP_OK_COMPLETIONPENDING)
sys->cond().wait(sys->mutex());
if (result < 0) {
*nread = -1;
return EIO;
}
}
*nread = 0;
while (*nread < count) {
if (in_buf_.empty())
break;
buf[(*nread)++] = in_buf_.front();
offset_++;
in_buf_.pop_front();
}
return 0;
}
int PepperFile::write(const char* buf, size_t count, size_t* nwrote) {
if (!is_open())
return EIO;
out_buf_.insert(out_buf_.end(), buf, buf + count);
if (is_block()) {
int32_t result = PP_OK_COMPLETIONPENDING;
pp::Module::Get()->core()->CallOnMainThread(0,
factory_.NewCallback(&PepperFile::Write, &result));
FileSystem* sys = FileSystem::GetFileSystem();
while (result == PP_OK_COMPLETIONPENDING)
sys->cond().wait(sys->mutex());
if ((size_t)result != count) {
*nwrote = -1;
return EIO;
} else {
*nwrote = count;
return 0;
}
} else {
if (!write_sent_) {
write_sent_ = true;
pp::Module::Get()->core()->CallOnMainThread(0,
factory_.NewCallback(&PepperFile::Write, (int32_t*)NULL));
}
*nwrote = count;
return 0;
}
}
int PepperFile::seek(nacl_abi_off_t offset, int whence,
nacl_abi_off_t* new_offset) {
switch (whence) {
case SEEK_SET:
offset_ = offset;
if (new_offset)
*new_offset = offset_;
return 0;
case SEEK_CUR:
offset_ += offset;
if (new_offset)
*new_offset = offset_;
return 0;
case SEEK_END:
offset_ = file_info_.size + offset;
if (new_offset)
*new_offset = offset_;
return 0;
default:
if (new_offset)
*new_offset = -1;
return EINVAL;
}
}
int PepperFile::fstat(nacl_abi_stat* out) {
memset(out, 0, sizeof(nacl_abi_stat));
out->nacl_abi_st_size = file_info_.size;
return 0;
}
int PepperFile::fcntl(int cmd, va_list ap) {
if (cmd == F_GETFL) {
return oflag_;
} else if (cmd == F_SETFL) {
int oflag = va_arg(ap, long);
if (is_block() && (oflag & O_NONBLOCK)) {
pp::Module::Get()->core()->CallOnMainThread(0,
factory_.NewCallback(&PepperFile::Read,
kBufSize, (int32_t*)NULL));
}
oflag_ = oflag;
return 0;
} else {
return -1;
}
}
bool PepperFile::is_read_ready() {
return !in_buf_.empty();
}
bool PepperFile::is_write_ready() {
return out_buf_.size() < kBufSize;
}
bool PepperFile::is_exception() {
return !is_open();
}
void PepperFile::Open(int32_t result, const char* pathname, int32_t* pres) {
FileSystem* sys = FileSystem::GetFileSystem();
Mutex::Lock lock(sys->mutex());
pp::FileRef file_ref(*file_system_, pathname);
file_io_ = new pp::FileIO(sys->instance());
int open_flags;
if ((oflag_ & O_ACCMODE) == O_WRONLY)
open_flags = PP_FILEOPENFLAG_WRITE;
else if ((oflag_ & O_ACCMODE) == O_RDONLY)
open_flags = PP_FILEOPENFLAG_READ;
else
open_flags = PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE;
if (oflag_ & O_CREAT)
open_flags |= PP_FILEOPENFLAG_CREATE;
if (oflag_ & O_TRUNC)
open_flags |= PP_FILEOPENFLAG_TRUNCATE;
*pres = file_io_->Open(file_ref, open_flags,
factory_.NewCallback(&PepperFile::OnOpen, pres));
if (*pres != PP_OK_COMPLETIONPENDING)
sys->cond().broadcast();
}
void PepperFile::OnOpen(int32_t result, int32_t* pres) {
FileSystem* sys = FileSystem::GetFileSystem();
Mutex::Lock lock(sys->mutex());
if (result == PP_OK) {
result = file_io_->Query(&file_info_,
factory_.NewCallback(&PepperFile::OnQuery, pres));
if (result == PP_OK_COMPLETIONPENDING)
return;
}
delete file_io_;
file_io_ = NULL;
*pres = result;
sys->cond().broadcast();
}
void PepperFile::OnQuery(int32_t result, int32_t* pres) {
FileSystem* sys = FileSystem::GetFileSystem();
Mutex::Lock lock(sys->mutex());
if (result == PP_OK) {
if (oflag_ & O_APPEND) {
offset_ = file_info_.size;
} else {
if (!is_block())
Read(PP_OK, kBufSize, NULL);
}
} else {
delete file_io_;
file_io_ = NULL;
}
*pres = result;
sys->cond().broadcast();
}
void PepperFile::Read(int32_t result, size_t count, int32_t* pres) {
FileSystem* sys = FileSystem::GetFileSystem();
Mutex::Lock lock(sys->mutex());
assert(file_io_);
read_buf_.resize(count);
result = file_io_->Read(offset_, &read_buf_[0], read_buf_.size(),
factory_.NewCallback(&PepperFile::OnRead, pres));
if (result != PP_OK_COMPLETIONPENDING) {
delete file_io_;
file_io_ = NULL;
if (pres)
*pres = result;
sys->cond().broadcast();
}
}
void PepperFile::OnRead(int32_t result, int32_t* pres) {
FileSystem* sys = FileSystem::GetFileSystem();
Mutex::Lock lock(sys->mutex());
if (result >= 0) {
in_buf_.insert(in_buf_.end(), &read_buf_[0], &read_buf_[0] + result);
if (result && !is_block() && in_buf_.size() < kBufSize)
Read(PP_OK, kBufSize, NULL);
} else {
delete file_io_;
file_io_ = NULL;
}
if (pres)
*pres = result;
sys->cond().broadcast();
}
void PepperFile::Write(int32_t result, int32_t* pres) {
FileSystem* sys = FileSystem::GetFileSystem();
Mutex::Lock lock(sys->mutex());
assert(file_io_);
if (result == PP_OK) {
if (write_buf_.size()) {
// Previous write operation is in progress.
pp::Module::Get()->core()->CallOnMainThread(1,
factory_.NewCallback(&PepperFile::Write, &result));
return;
}
assert(out_buf_.size());
write_buf_.swap(out_buf_);
result = file_io_->Write(offset_, &write_buf_[0], write_buf_.size(),
factory_.NewCallback(&PepperFile::OnWrite, pres));
write_sent_ = false;
} else {
result = PP_ERROR_FAILED;
}
if (result != PP_OK_COMPLETIONPENDING) {
delete file_io_;
file_io_ = NULL;
if (pres)
*pres = result;
sys->cond().broadcast();
}
}
void PepperFile::OnWrite(int32_t result, int32_t* pres) {
FileSystem* sys = FileSystem::GetFileSystem();
Mutex::Lock lock(sys->mutex());
if ((size_t)result != write_buf_.size()) {
delete file_io_;
file_io_ = NULL;
} else {
offset_ += result;
}
if (pres)
*pres = result;
write_buf_.clear();
sys->cond().broadcast();
}
void PepperFile::Close(int32_t result, int32_t* pres) {
FileSystem* sys = FileSystem::GetFileSystem();
Mutex::Lock lock(sys->mutex());
delete file_io_;
file_io_ = NULL;
if (pres)
*pres = PP_OK;
sys->cond().broadcast();
}