#include
#include
#include
#include
#include
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
#include "ppapi/cpp/file_ref.h"
#include "NaClFileSystem.h"
#include "moai_nacl.h"
#include "geturl_handler.h"
//AJV TODO - this needs to be refactored, will be switched to caching files
// as needed, check disk first then download.
// auto-caching will double disk space for packages apps though...
NaClFileSystem *NaClFileSystem::mSingletonInstance = NULL;
//----------------------------------------------------------------//
NaClFileSystem::~NaClFileSystem () {
mCore = NULL;
mInstance = NULL;
}
//----------------------------------------------------------------//
NaClFileSystem::NaClFileSystem ( pp::Core* core, pp::Instance *instance ) :
mCCFactory ( this ),
mFileSystem ( instance, PP_FILESYSTEMTYPE_LOCALPERSISTENT )
{
mCore = core;
mInstance = instance;
mSingletonInstance = this;
mFileSystemOpened = false;
}
//----------------------------------------------------------------//
void NaClFileSystem::Init () {
pp::CompletionCallback cc ( OpenFileSystemMainThread, this );
mCore->CallOnMainThread ( 0, cc , 0 );
while ( !mFileSystemOpened ) {
usleep ( 1000 );
}
}
//----------------------------------------------------------------//
void NaClFileSystem::OpenFileSystemMainThread ( void* userData, int32_t result ) {
pp::CompletionCallback cc = Get ()->mCCFactory.NewCallback ( &NaClFileSystem::OpenFileSystemCallback );
int32_t res = Get ()->mFileSystem.Open ( 1024, cc );
if ( PP_OK_COMPLETIONPENDING != res ) {
cc.Run ( res );
}
}
//----------------------------------------------------------------//
void NaClFileSystem::OpenFileSystemCallback ( int32_t result ) {
if ( result != PP_OK ) {
NACL_LOG ( "ERROR: NaClFileSystem Open File system result %d\n", result );
//AJV TODO send message back to browser
g_instance->PostMessage ( pp::Var ( "ALERT:Warning: You are in Incognito mode, data for this session will not be saved" ));
mFileSystemOpened = true;
}
else {
mFileSystemOpened = true;
}
}
//----------------------------------------------------------------//
NaClFile * NaClFileSystem::fopen ( const char * path, const char *mode ) {
//AJV support binary and text read/write? nacl file system makes no distinction, until then...
//int modeLen = strlen ( mode );
//AJV hack to differentiate local files from hosted
char *NaClFilePath = strstr ( path, "/NaClFileSys" );
const char *redirectPath = path;
bool isOnDisk = false;
if ( NaClFilePath ) {
redirectPath = path + strlen ( "/NaClFileSys" );
isOnDisk = true;
}
NaClFile * newFile = new NaClFile ();
newFile->mPath = redirectPath;
NACL_LOG ( "NaClFileSystem::fopen %s, mode %s, is on disk %d \n", newFile->mPath, mode, isOnDisk );
if ( mCore->IsMainThread () ) {
NACL_LOG( "ERROR: Cannot perform blocking file I/O on main thread\n" );
}
if ( mode[0] == 'r' ) {
if ( isOnDisk ) {
newFile->mFlags = PP_FILEOPENFLAG_READ;
}
else {
pp::CompletionCallback cc ( RequestURLMainThread, newFile );
if ( !mCore->IsMainThread ()) {
mCore->CallOnMainThread ( 0, cc , 0 );
}
else {
RequestURLMainThread ( newFile, 0 );
newFile->mIsHttpLoaded = true;
}
while ( !newFile->mIsHttpLoaded ) {
usleep ( 1000 );
}
}
}
else if ( mode[0] == 'w' ) {
if ( isOnDisk ) {
newFile->mFlags = PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_TRUNCATE;
}
else {
NACL_LOG ( "ERROR: Cannot write to file not on local file system\n" );
}
}
else {
NACL_LOG ( "NaClFileSystem::fopen - Unsupported mode %s\n", mode );
}
if ( isOnDisk ) {
pp::CompletionCallback cc ( OpenFileMainThread, newFile );
newFile->mIsFileLocked = true;
if ( !mCore->IsMainThread ()) {
mCore->CallOnMainThread ( 0, cc , 0 );
}
else {
OpenFileMainThread ( newFile, 0 );
}
while ( newFile->mIsFileLocked ) {
usleep ( 1000 );
}
if ( newFile->mIsFileOpen ) {
newFile->mIsFileExist = true;
}
//AJV read the entire file into memory :(
//TODO not this.
if ( newFile->mIsFileOpen && newFile->mFlags == PP_FILEOPENFLAG_READ ) {
//get the size
newFile->mIsFileLocked = true;
pp::CompletionCallback ccSetSize ( SetSizeFileMainThread, newFile );
if ( !mCore->IsMainThread ()) {
mCore->CallOnMainThread ( 0, ccSetSize , 0 );
}
else {
SetSizeFileMainThread ( newFile, 0 );
}
while ( newFile->mIsFileLocked ) {
usleep ( 1000 );
}
newFile->mSize = newFile->mFileInfo.size;
//now actually read it in
newFile->mIsFileLocked = true;
newFile->mData = new char [ newFile->mSize ];
pp::CompletionCallback ccRead ( ReadFileMainThread, newFile );
if ( !mCore->IsMainThread ()) {
mCore->CallOnMainThread ( 0, ccRead , 0 );
}
else {
ReadFileMainThread ( newFile, 0 );
newFile->mIsFileLocked = false;
}
while ( newFile->mIsFileLocked ) {
usleep ( 1000 );
}
newFile->mOffset = 0;
NACL_LOG( "NaClFileSystem Read in file %s with %d bytes\n", path, newFile->mSize );
}
}
if ( newFile->mIsFileExist ) {
return newFile;
}
else {
return NULL;
}
}
//----------------------------------------------------------------//
void NaClFileSystem::RequestURLMainThread ( void * userData, int32_t result ) {
NaClFile * file = static_cast < NaClFile * > ( userData );
GetURLHandler* handler = GetURLHandler::Create( Get ()->mInstance, file->mPath );
handler->SetUserData ( file );
if (handler != NULL) {
handler->Start( HttpLoaded );
}
}
//----------------------------------------------------------------//
void NaClFileSystem::OpenFileMainThread ( void * userData, int32_t result ) {
NaClFile * file = static_cast < NaClFile * > ( userData );
pp::CompletionCallback cc ( OpenFileCallback, file );
file->mFileRef = new pp::FileRef ( mSingletonInstance->mFileSystem, file->mPath );
file->mFileIO = new pp::FileIO ( mSingletonInstance->mInstance );
file->mFileIO->Open ( *( file->mFileRef ), file->mFlags, cc );
}
//----------------------------------------------------------------//
void NaClFileSystem::OpenFileCallback ( void * userData, int32_t result ) {
NaClFile * file = static_cast < NaClFile * > ( userData );
if ( result == PP_OK ) {
file->mIsFileOpen = true;
}
file->mIsFileLocked = false;
}
//----------------------------------------------------------------//
int NaClFileSystem::stat ( const char *path, struct stat *buf ) {
NaClFile * newFile = new NaClFile ();
newFile->mPath = path;
//TODO, check if file is cached
pp::CompletionCallback cc ( RequestURLStatsMainThread, newFile );
if ( !mCore->IsMainThread ()) {
mCore->CallOnMainThread ( 0, cc , 0 );
}
else {
NACL_LOG( "ERROR: Cannot load files on main thread\n" );
RequestURLStatsMainThread ( newFile, 0 );
newFile->mIsHttpLoaded = true;
}
while ( !newFile->mIsHttpLoaded ) {
usleep ( 1000 );
}
if ( newFile->mIsFileExist ) {
delete newFile;
return 0;
}
else {
delete newFile;
return -1;
}
}
void NaClFileSystem::SetSizeFileMainThread ( void * userData, int32_t result ) {
NaClFile * file = static_cast < NaClFile * > ( userData );
pp::CompletionCallback cc ( FileOperationDone, file );
file->mFileIO->Query ( &file->mFileInfo, cc );
}
//----------------------------------------------------------------//
void NaClFileSystem::RequestURLStatsMainThread ( void * userData, int32_t result ) {
NaClFile * file = static_cast < NaClFile * > ( userData );
GetURLHandler* handler = GetURLHandler::Create( Get ()->mInstance, file->mPath );
handler->SetMethod ( GetURLHandler::HEAD );
handler->SetUserData ( file );
if (handler != NULL) {
handler->Start( HttpLoaded );
}
}
//----------------------------------------------------------------//
int NaClFileSystem::fclose ( NaClFile *file ) {
NACL_LOG( "NaClFileSystem::fclose %s \n\n", file->mPath );
file->mIsFileLocked = true;
if ( !mCore->IsMainThread ()) {
pp::CompletionCallback cc ( DeletePepperFileMainThread, file );
mCore->CallOnMainThread ( 0, cc , 0 );
}
else {
DeletePepperFileMainThread ( file, 0 );
file->mIsFileLocked = false;
}
while ( file->mIsFileLocked ) {
usleep ( 1000 );
}
delete file;
return 0;
}
void NaClFileSystem::DeletePepperFileMainThread ( void * userData, int32_t result ) {
NaClFile * file = static_cast < NaClFile * > ( userData );
if ( file->mFileRef ) {
delete file->mFileRef;
}
if ( file->mFileIO ) {
delete file->mFileIO;
}
file->mIsFileLocked = false;
}
//----------------------------------------------------------------//
size_t NaClFileSystem::fread ( void *ptr, size_t size, size_t count, NaClFile *file ) {
if( file && ( file->mIsHttpLoaded || file->mIsFileOpen )) {
int readSize = size * count;
int remainingSize = file->mSize - file->mOffset;
if ( readSize <= remainingSize ) {
memcpy ( ptr, file->mData + file->mOffset, readSize );
file->mOffset += readSize;
return readSize;
}
else {
memcpy ( ptr, file->mData + file->mOffset, remainingSize );
file->mOffset += remainingSize;
return remainingSize;
}
}
//AJV unused for now due to complications with other std file I/O
//e.g. getc, ungetc
/*else if ( file && file->mIsFileOpen ) {
NACL_LOG( "NaClFileSystem::fread filesys read\n" );
file->mIsFileLocked = true;
file->mExternalBuffer = ( char* ) ptr;
file->mSize = size * count;
pp::CompletionCallback cc ( ReadFileMainThread, file );
if ( !mCore->IsMainThread ()) {
mCore->CallOnMainThread ( 0, cc , 0 );
}
else {
NACL_LOG( "ERROR: Cannot read files on main thread\n" );
ReadFileMainThread ( file, 0 );
file->mIsFileLocked = false;
}
while ( file->mIsFileLocked ) {
usleep ( 1000 );
}
file->mExternalBuffer = NULL;
file->mOffset += size * count;
}*/
else {
NACL_LOG( "NaClFileSystem::fread - invalid file" );
}
return 0;
}
size_t NaClFileSystem::fwrite ( const void *ptr, size_t size, size_t count, NaClFile *file ) {
if( file && file->mIsFileOpen ) {
file->mIsFileLocked = true;
file->mExternalBuffer = ( char* ) ptr;
file->mSize = size * count;
pp::CompletionCallback cc ( WriteFileMainThread, file );
if ( !mCore->IsMainThread ()) {
mCore->CallOnMainThread ( 0, cc , 0 );
}
else {
NACL_LOG ( "ERROR: Cannot write files on main thread\n" );
WriteFileMainThread ( file, 0 );
file->mIsFileLocked = false;
}
while ( file->mIsFileLocked ) {
usleep ( 1000 );
}
file->mExternalBuffer = NULL;
file->mOffset += size * count;
}
return 0;
}
//----------------------------------------------------------------//
void NaClFileSystem::WriteFileMainThread ( void * userData, int32_t result ) {
NaClFile * file = static_cast < NaClFile * > ( userData );
pp::CompletionCallback cc ( FileOperationDone, file );
file->mFileIO->Write ( file->mOffset, file->mExternalBuffer, file->mSize, cc );
}
//----------------------------------------------------------------//
void NaClFileSystem::ReadFileMainThread ( void * userData, int32_t result ) {
NaClFile * file = static_cast < NaClFile * > ( userData );
pp::CompletionCallback cc ( FileOperationDone, file );
file->mFileIO->Read ( file->mOffset, file->mData, file->mSize, cc );
}
//----------------------------------------------------------------//
void NaClFileSystem::FileOperationDone ( void * userData, int32_t result ) {
NaClFile * file = static_cast < NaClFile * > ( userData );
file->mIsFileLocked = false;
}
//----------------------------------------------------------------//
void NaClFileSystem::HttpLoaded ( GetURLHandler *handler, const char *buffer, int32_t size ) {
NaClFile *file = static_cast < NaClFile * > ( handler->GetUserData ());
if ( handler->GetStatusCode () == 200 ) {
file->mIsFileExist = true;
}
else {
file->mIsFileExist = false;
}
if ( file->mIsFileExist && size ) {
NACL_LOG ( "NaClFileSystem::HttpLoaded file %s, size: %d\n", file->mPath, size );
file->mSize = size;
file->mData = new char [ size ];
memcpy ( file->mData, buffer, size );
}
file->mIsHttpLoaded = true;
}
//----------------------------------------------------------------//
NaClFileSystem * NaClFileSystem::Get () {
return mSingletonInstance;
}
NaClFile::NaClFile () {
mData = NULL;
mIsFileExist = false;
mFileRef = NULL;
mFileIO = NULL;
mFlags = 0;
mIsFileLocked = false;
mIsFileOpen = false;
mIsHttpLoaded = false;
mOffset = 0;
mSize = 0;
}
NaClFile::~NaClFile () {
mIsHttpLoaded = false;
mSize = 0;
if ( mData ) {
delete [] mData;
}
mOffset = 0;
mIsFileExist = false;
}