blob: 981f69e8f3655d66f49b8bc879048cdeb8430bbc [file] [log] [blame]
// Copyright 2013 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 "nacl_io/html5fs/html5_fs_node.h"
#include <errno.h>
#include <fcntl.h>
#include <ppapi/c/pp_completion_callback.h>
#include <ppapi/c/pp_directory_entry.h>
#include <ppapi/c/pp_errors.h>
#include <ppapi/c/pp_file_info.h>
#include <ppapi/c/ppb_file_io.h>
#include <string.h>
#include <vector>
#include "nacl_io/filesystem.h"
#include "nacl_io/getdents_helper.h"
#include "nacl_io/kernel_handle.h"
#include "nacl_io/osdirent.h"
#include "nacl_io/pepper_interface.h"
#include "sdk_util/auto_lock.h"
namespace nacl_io {
namespace {
struct OutputBuffer {
void* data;
int element_count;
};
void* GetOutputBuffer(void* user_data, uint32_t count, uint32_t size) {
OutputBuffer* output = static_cast<OutputBuffer*>(user_data);
output->element_count = count;
if (count) {
output->data = malloc(count * size);
if (!output->data)
output->element_count = 0;
} else {
output->data = NULL;
}
return output->data;
}
int32_t OpenFlagsToPPAPIOpenFlags(int open_flags) {
int32_t ppapi_flags = 0;
switch (open_flags & 3) {
default:
case O_RDONLY:
ppapi_flags = PP_FILEOPENFLAG_READ;
break;
case O_WRONLY:
ppapi_flags = PP_FILEOPENFLAG_WRITE;
break;
case O_RDWR:
ppapi_flags = PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE;
break;
}
if (open_flags & O_CREAT)
ppapi_flags |= PP_FILEOPENFLAG_CREATE;
if (open_flags & O_TRUNC)
ppapi_flags |= PP_FILEOPENFLAG_TRUNCATE;
if (open_flags & O_EXCL)
ppapi_flags |= PP_FILEOPENFLAG_EXCLUSIVE;
return ppapi_flags;
}
} // namespace
Error Html5FsNode::FSync() {
// Cannot call Flush on a directory; simply do nothing.
if (IsaDir())
return 0;
int32_t result =
file_io_iface_->Flush(fileio_resource_, PP_BlockUntilComplete());
if (result != PP_OK)
return PPErrorToErrno(result);
return 0;
}
Error Html5FsNode::GetDents(size_t offs,
struct dirent* pdir,
size_t size,
int* out_bytes) {
*out_bytes = 0;
// If this is not a directory, fail
if (!IsaDir())
return ENOTDIR;
// TODO(binji): Better handling of ino numbers.
const ino_t kCurDirIno = -1;
const ino_t kParentDirIno = -2;
GetDentsHelper helper(kCurDirIno, kParentDirIno);
OutputBuffer output_buf = {NULL, 0};
PP_ArrayOutput output = {&GetOutputBuffer, &output_buf};
int32_t result = file_ref_iface_->ReadDirectoryEntries(
fileref_resource_, output, PP_BlockUntilComplete());
if (result != PP_OK)
return PPErrorToErrno(result);
PP_DirectoryEntry* entries = static_cast<PP_DirectoryEntry*>(output_buf.data);
for (int i = 0; i < output_buf.element_count; ++i) {
PP_Var file_name_var = file_ref_iface_->GetName(entries[i].file_ref);
// Release the file reference.
filesystem_->ppapi()->ReleaseResource(entries[i].file_ref);
if (file_name_var.type != PP_VARTYPE_STRING)
continue;
uint32_t file_name_length;
const char* file_name =
var_iface_->VarToUtf8(file_name_var, &file_name_length);
if (file_name) {
file_name_length =
std::min(static_cast<size_t>(file_name_length),
MEMBER_SIZE(dirent, d_name) - 1); // -1 for NULL.
// TODO(binji): Better handling of ino numbers.
helper.AddDirent(1, file_name, file_name_length);
}
var_iface_->Release(file_name_var);
}
// Release the output buffer.
free(output_buf.data);
return helper.GetDents(offs, pdir, size, out_bytes);
}
Error Html5FsNode::GetStat(struct stat* stat) {
AUTO_LOCK(node_lock_);
PP_FileInfo info;
int32_t result =
file_ref_iface_->Query(fileref_resource_, &info, PP_BlockUntilComplete());
if (result != PP_OK)
return PPErrorToErrno(result);
// Fill in known info here.
memcpy(stat, &stat_, sizeof(stat_));
// Fill in the additional info from ppapi.
switch (info.type) {
case PP_FILETYPE_REGULAR:
stat->st_mode |= S_IFREG;
break;
case PP_FILETYPE_DIRECTORY:
stat->st_mode |= S_IFDIR;
break;
case PP_FILETYPE_OTHER:
default:
break;
}
stat->st_size = static_cast<off_t>(info.size);
stat->st_atime = info.last_access_time;
stat->st_mtime = info.last_modified_time;
stat->st_ctime = info.creation_time;
return 0;
}
Error Html5FsNode::Read(const HandleAttr& attr,
void* buf,
size_t count,
int* out_bytes) {
*out_bytes = 0;
if (IsaDir())
return EISDIR;
int32_t result = file_io_iface_->Read(fileio_resource_,
attr.offs,
static_cast<char*>(buf),
static_cast<int32_t>(count),
PP_BlockUntilComplete());
if (result < 0)
return PPErrorToErrno(result);
*out_bytes = result;
return 0;
}
Error Html5FsNode::FTruncate(off_t size) {
if (IsaDir())
return EISDIR;
int32_t result = file_io_iface_->SetLength(
fileio_resource_, size, PP_BlockUntilComplete());
if (result != PP_OK)
return PPErrorToErrno(result);
return 0;
}
Error Html5FsNode::Write(const HandleAttr& attr,
const void* buf,
size_t count,
int* out_bytes) {
*out_bytes = 0;
if (IsaDir())
return EISDIR;
int32_t result = file_io_iface_->Write(fileio_resource_,
attr.offs,
static_cast<const char*>(buf),
static_cast<int32_t>(count),
PP_BlockUntilComplete());
if (result < 0)
return PPErrorToErrno(result);
*out_bytes = result;
return 0;
}
int Html5FsNode::GetType() {
return fileio_resource_ ? S_IFREG : S_IFDIR;
}
Error Html5FsNode::GetSize(off_t* out_size) {
*out_size = 0;
if (IsaDir())
return 0;
AUTO_LOCK(node_lock_);
PP_FileInfo info;
int32_t result =
file_io_iface_->Query(fileio_resource_, &info, PP_BlockUntilComplete());
if (result != PP_OK)
return PPErrorToErrno(result);
*out_size = info.size;
return 0;
}
Html5FsNode::Html5FsNode(Filesystem* filesystem, PP_Resource fileref_resource)
: Node(filesystem),
fileref_resource_(fileref_resource),
fileio_resource_(0) {
}
Error Html5FsNode::Init(int open_flags) {
Error error = Node::Init(open_flags);
if (error)
return error;
file_io_iface_ = filesystem_->ppapi()->GetFileIoInterface();
file_ref_iface_ = filesystem_->ppapi()->GetFileRefInterface();
var_iface_ = filesystem_->ppapi()->GetVarInterface();
if (!(file_io_iface_ && file_ref_iface_ && var_iface_)) {
LOG_ERROR("Got NULL interface(s): %s%s%s",
file_ref_iface_ ? "" : "FileRef",
file_io_iface_ ? "" : "FileIo ",
var_iface_ ? "" : "Var ");
return EIO;
}
// First query the FileRef to see if it is a file or directory.
PP_FileInfo file_info;
int32_t query_result = file_ref_iface_->Query(
fileref_resource_, &file_info, PP_BlockUntilComplete());
// If this is a directory, do not get a FileIO.
if (query_result == PP_OK && file_info.type == PP_FILETYPE_DIRECTORY)
return 0;
fileio_resource_ =
file_io_iface_->Create(filesystem_->ppapi()->GetInstance());
if (!fileio_resource_) {
LOG_ERROR("Couldn't create FileIo resource.");
return EIO;
}
int32_t open_result =
file_io_iface_->Open(fileio_resource_,
fileref_resource_,
OpenFlagsToPPAPIOpenFlags(open_flags),
PP_BlockUntilComplete());
if (open_result != PP_OK)
return PPErrorToErrno(open_result);
return 0;
}
void Html5FsNode::Destroy() {
FSync();
if (fileio_resource_) {
file_io_iface_->Close(fileio_resource_);
filesystem_->ppapi()->ReleaseResource(fileio_resource_);
}
filesystem_->ppapi()->ReleaseResource(fileref_resource_);
fileio_resource_ = 0;
fileref_resource_ = 0;
Node::Destroy();
}
} // namespace nacl_io