blob: 9182676a5ad60cbe60a39dd43bb4c1fff3e8e650 [file] [log] [blame]
/* Copyright (c) 2012 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 "handlers.h"
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "nacl_io/osdirent.h"
#include "nacl_io_demo.h"
#define MAX_OPEN_FILES 10
#define MAX_OPEN_DIRS 10
#if defined(WIN32)
#define stat _stat
#endif
/**
* A mapping from int -> FILE*, so the JavaScript messages can refer to an open
* File. */
static FILE* g_OpenFiles[MAX_OPEN_FILES];
/**
* A mapping from int -> DIR*, so the JavaScript messages can refer to an open
* Directory. */
static void* g_OpenDirs[MAX_OPEN_DIRS];
/**
* Add |object| to |map| and return the index it was added at.
* @param[in] map The map to add the object to.
* @param[in] max_map_size The maximum map size.
* @param[in] object The object to add to the map.
* @return int The index of the added object, or -1 if there is no more space.
*/
static int AddToMap(void** map, int max_map_size, void* object) {
int i;
assert(object != NULL);
for (i = 0; i < max_map_size; ++i) {
if (map[i] == NULL) {
map[i] = object;
return i;
}
}
return -1;
}
/**
* Remove an object at index |i| from |map|.
* @param[in] map The map to remove from.
* @param[in] max_map_size The size of the map.
* @param[in] i The index to remove.
*/
static void RemoveFromMap(void** map, int max_map_size, int i) {
assert(i >= 0 && i < max_map_size);
map[i] = NULL;
}
/**
* Get the object from |map| at index |i|.
* @param[in] map The map to access.
* @param[in] max_map_size The size of the map.
* @param[in] i The index to access.
* @return the object at |map|. This will be NULL if there is no object at |i|.
*/
static void* GetFromMap(void** map, int max_map_size, int i) {
assert(i >= 0 && i < max_map_size);
return map[i];
}
/**
* Get an object given a string |s| containing the index.
* @param[in] map The map to access.
* @param[in] max_map_size The size of the map.
* @param[in] s The string containing the object index.
* @param[out] index The index of the object as an int.
* @return The object, or NULL if the index is invalid.
*/
static void* GetFromIndexString(void** map,
int max_map_size,
const char* s,
int* index) {
char* endptr;
int result = strtol(s, &endptr, 10);
if (endptr != s + strlen(s)) {
/* Garbage at the end of the number...? */
return NULL;
}
if (index)
*index = result;
return GetFromMap(map, max_map_size, result);
}
/**
* Add the file to the g_OpenFiles map.
* @param[in] file The file to add to g_OpenFiles.
* @return int The index of the FILE in g_OpenFiles, or -1 if there are too many
* open files. */
static int AddFileToMap(FILE* file) {
return AddToMap((void**)g_OpenFiles, MAX_OPEN_FILES, file);
}
/**
* Remove the file from the g_OpenFiles map.
* @param[in] i The index of the file handle to remove. */
static void RemoveFileFromMap(int i) {
RemoveFromMap((void**)g_OpenFiles, MAX_OPEN_FILES, i);
}
/**
* Get a file, given a string containing the index.
* @param[in] s The string containing the file index.
* @param[out] file_index The index of this file.
* @return The FILE* for this file, or NULL if the index is invalid.
*/
static FILE* GetFileFromIndexString(const char* s, int* file_index) {
return (FILE*)GetFromIndexString(
(void**)g_OpenFiles, MAX_OPEN_FILES, s, file_index);
}
/* Win32 doesn't support DIR/opendir/readdir/closedir. */
#if !defined(WIN32)
/**
* Add the dir to the g_OpenDirs map.
* @param[in] dir The dir to add to g_OpenDirs.
* @return int The index of the DIR in g_OpenDirs, or -1 if there are too many
* open dirs. */
static int AddDirToMap(DIR* dir) {
return AddToMap((void**)g_OpenDirs, MAX_OPEN_DIRS, dir);
}
/**
* Remove the dir from the g_OpenDirs map.
* @param[in] i The index of the dir handle to remove. */
static void RemoveDirFromMap(int i) {
RemoveFromMap((void**)g_OpenDirs, MAX_OPEN_DIRS, i);
}
/**
* Get a dir, given a string containing the index.
* @param[in] s The string containing the dir index.
* @param[out] dir_index The index of this dir.
* @return The DIR* for this dir, or NULL if the index is invalid.
*/
static DIR* GetDirFromIndexString(const char* s, int* dir_index) {
return (DIR*)GetFromIndexString(
(void**)g_OpenDirs, MAX_OPEN_DIRS, s, dir_index);
}
#endif
/**
* Handle a call to fopen() made by JavaScript.
*
* fopen expects 2 parameters:
* 0: the path of the file to open
* 1: the mode string
* on success, fopen returns a result in |output| separated by \1:
* 0: "fopen"
* 1: the filename opened
* 2: the file index
* on failure, fopen returns an error string in |output|.
*
* @param[in] num_params The number of params in |params|.
* @param[in] params An array of strings, parameters to this function.
* @param[out] output A string to write informational function output to.
* @return An errorcode; 0 means success, anything else is a failure. */
int HandleFopen(int num_params, char** params, char** output) {
FILE* file;
int file_index;
const char* filename;
const char* mode;
if (num_params != 2) {
*output = PrintfToNewString("Error: fopen takes 2 parameters.");
return 1;
}
filename = params[0];
mode = params[1];
file = fopen(filename, mode);
if (!file) {
*output = PrintfToNewString("Error: fopen returned a NULL FILE*.");
return 2;
}
file_index = AddFileToMap(file);
if (file_index == -1) {
*output = PrintfToNewString(
"Error: Example only allows %d open file handles.", MAX_OPEN_FILES);
return 3;
}
*output = PrintfToNewString("fopen\1%s\1%d", filename, file_index);
return 0;
}
/**
* Handle a call to fwrite() made by JavaScript.
*
* fwrite expects 2 parameters:
* 0: The index of the file (which is mapped to a FILE*)
* 1: A string to write to the file
* on success, fwrite returns a result in |output| separated by \1:
* 0: "fwrite"
* 1: the file index
* 2: the number of bytes written
* on failure, fwrite returns an error string in |output|.
*
* @param[in] num_params The number of params in |params|.
* @param[in] params An array of strings, parameters to this function.
* @param[out] output A string to write informational function output to.
* @return An errorcode; 0 means success, anything else is a failure. */
int HandleFwrite(int num_params, char** params, char** output) {
FILE* file;
const char* file_index_string;
const char* data;
size_t data_len;
size_t bytes_written;
if (num_params != 2) {
*output = PrintfToNewString("Error: fwrite takes 2 parameters.");
return 1;
}
file_index_string = params[0];
file = GetFileFromIndexString(file_index_string, NULL);
data = params[1];
data_len = strlen(data);
if (!file) {
*output =
PrintfToNewString("Error: Unknown file handle %s.", file_index_string);
return 2;
}
bytes_written = fwrite(data, 1, data_len, file);
if (ferror(file)) {
*output = PrintfToNewString(
"Error: Wrote %d bytes, but ferror() returns true.", bytes_written);
return 3;
}
*output =
PrintfToNewString("fwrite\1%s\1%d", file_index_string, bytes_written);
return 0;
}
/**
* Handle a call to fread() made by JavaScript.
*
* fread expects 2 parameters:
* 0: The index of the file (which is mapped to a FILE*)
* 1: The number of bytes to read from the file.
* on success, fread returns a result in |output| separated by \1:
* 0: "fread"
* 1: the file index
* 2: the data read from the file
* on failure, fread returns an error string in |output|.
*
* @param[in] num_params The number of params in |params|.
* @param[in] params An array of strings, parameters to this function.
* @param[out] output A string to write informational function output to.
* @return An errorcode; 0 means success, anything else is a failure. */
int HandleFread(int num_params, char** params, char** output) {
FILE* file;
const char* file_index_string;
char* buffer;
size_t data_len;
size_t bytes_read;
if (num_params != 2) {
*output = PrintfToNewString("Error: fread takes 2 parameters.");
return 1;
}
file_index_string = params[0];
file = GetFileFromIndexString(file_index_string, NULL);
data_len = strtol(params[1], NULL, 10);
if (!file) {
*output =
PrintfToNewString("Error: Unknown file handle %s.", file_index_string);
return 2;
}
buffer = (char*)malloc(data_len + 1);
bytes_read = fread(buffer, 1, data_len, file);
buffer[bytes_read] = 0;
if (ferror(file)) {
*output = PrintfToNewString(
"Error: Read %d bytes, but ferror() returns true.", bytes_read);
return 3;
}
*output = PrintfToNewString("fread\1%s\1%s", file_index_string, buffer);
free(buffer);
return 0;
}
/**
* Handle a call to fseek() made by JavaScript.
*
* fseek expects 3 parameters:
* 0: The index of the file (which is mapped to a FILE*)
* 1: The offset to seek to
* 2: An integer representing the whence parameter of standard fseek.
* whence = 0: seek from the beginning of the file
* whence = 1: seek from the current file position
* whence = 2: seek from the end of the file
* on success, fseek returns a result in |output| separated by \1:
* 0: "fseek"
* 1: the file index
* 2: The new file position
* on failure, fseek returns an error string in |output|.
*
* @param[in] num_params The number of params in |params|.
* @param[in] params An array of strings, parameters to this function.
* @param[out] output A string to write informational function output to.
* @return An errorcode; 0 means success, anything else is a failure. */
int HandleFseek(int num_params, char** params, char** output) {
FILE* file;
const char* file_index_string;
long offset;
int whence;
int result;
if (num_params != 3) {
*output = PrintfToNewString("Error: fseek takes 3 parameters.");
return 1;
}
file_index_string = params[0];
file = GetFileFromIndexString(file_index_string, NULL);
offset = strtol(params[1], NULL, 10);
whence = strtol(params[2], NULL, 10);
if (!file) {
*output =
PrintfToNewString("Error: Unknown file handle %s.", file_index_string);
return 2;
}
result = fseek(file, offset, whence);
if (result) {
*output = PrintfToNewString("Error: fseek returned error %d.", result);
return 3;
}
offset = ftell(file);
if (offset < 0) {
*output = PrintfToNewString(
"Error: fseek succeeded, but ftell returned error %d.", offset);
return 4;
}
*output = PrintfToNewString("fseek\1%s\1%d", file_index_string, offset);
return 0;
}
/**
* Handle a call to fclose() made by JavaScript.
*
* fclose expects 1 parameter:
* 0: The index of the file (which is mapped to a FILE*)
* on success, fclose returns a result in |output| separated by \1:
* 0: "fclose"
* 1: the file index
* on failure, fclose returns an error string in |output|.
*
* @param[in] num_params The number of params in |params|.
* @param[in] params An array of strings, parameters to this function.
* @param[out] output A string to write informational function output to.
* @return An errorcode; 0 means success, anything else is a failure. */
int HandleFclose(int num_params, char** params, char** output) {
FILE* file;
int file_index;
const char* file_index_string;
int result;
if (num_params != 1) {
*output = PrintfToNewString("Error: fclose takes 1 parameters.");
return 1;
}
file_index_string = params[0];
file = GetFileFromIndexString(file_index_string, &file_index);
if (!file) {
*output =
PrintfToNewString("Error: Unknown file handle %s.", file_index_string);
return 2;
}
result = fclose(file);
if (result) {
*output = PrintfToNewString("Error: fclose returned error %d.", result);
return 3;
}
RemoveFileFromMap(file_index);
*output = PrintfToNewString("fclose\1%s", file_index_string);
return 0;
}
/**
* Handle a call to stat() made by JavaScript.
*
* stat expects 1 parameter:
* 0: The name of the file
* on success, stat returns a result in |output| separated by \1:
* 0: "stat"
* 1: the file name
* 2: the size of the file
* on failure, stat returns an error string in |output|.
*
* @param[in] num_params The number of params in |params|.
* @param[in] params An array of strings, parameters to this function.
* @param[out] output A string to write informational function output to.
* @return An errorcode; 0 means success, anything else is a failure. */
int HandleStat(int num_params, char** params, char** output) {
const char* filename;
int result;
struct stat buf;
if (num_params != 1) {
*output = PrintfToNewString("Error: stat takes 1 parameter.");
return 1;
}
filename = params[0];
memset(&buf, 0, sizeof(buf));
result = stat(filename, &buf);
if (result == -1) {
*output = PrintfToNewString("Error: stat returned error %d.", errno);
return 2;
}
*output = PrintfToNewString("stat\1%s\1%d", filename, buf.st_size);
return 0;
}
/**
* Handle a call to opendir() made by JavaScript.
*
* opendir expects 1 parameter:
* 0: The name of the directory
* on success, opendir returns a result in |output| separated by \1:
* 0: "opendir"
* 1: the directory name
* 2: the index of the directory
* on failure, opendir returns an error string in |output|.
*
* @param[in] num_params The number of params in |params|.
* @param[in] params An array of strings, parameters to this function.
* @param[out] output A string to write informational function output to.
* @return An errorcode; 0 means success, anything else is a failure. */
int HandleOpendir(int num_params, char** params, char** output) {
#if defined(WIN32)
*output = PrintfToNewString("Error: Win32 does not support opendir.");
return 1;
#else
DIR* dir;
int dir_index;
const char* dirname;
if (num_params != 1) {
*output = PrintfToNewString("Error: opendir takes 1 parameter.");
return 1;
}
dirname = params[0];
dir = opendir(dirname);
if (!dir) {
*output = PrintfToNewString("Error: opendir returned a NULL DIR*.");
return 2;
}
dir_index = AddDirToMap(dir);
if (dir_index == -1) {
*output = PrintfToNewString(
"Error: Example only allows %d open dir handles.", MAX_OPEN_DIRS);
return 3;
}
*output = PrintfToNewString("opendir\1%s\1%d", dirname, dir_index);
return 0;
#endif
}
/**
* Handle a call to readdir() made by JavaScript.
*
* readdir expects 1 parameter:
* 0: The index of the directory (which is mapped to a DIR*)
* on success, opendir returns a result in |output| separated by \1:
* 0: "readdir"
* 1: the inode number of the entry
* 2: the name of the entry
* on failure, readdir returns an error string in |output|.
*
* @param[in] num_params The number of params in |params|.
* @param[in] params An array of strings, parameters to this function.
* @param[out] output A string to write informational function output to.
* @return An errorcode; 0 means success, anything else is a failure. */
int HandleReaddir(int num_params, char** params, char** output) {
#if defined(WIN32)
*output = PrintfToNewString("Error: Win32 does not support readdir.");
return 1;
#else
DIR* dir;
const char* dir_index_string;
struct dirent* entry;
if (num_params != 1) {
*output = PrintfToNewString("Error: readdir takes 1 parameter.");
return 1;
}
dir_index_string = params[0];
dir = GetDirFromIndexString(dir_index_string, NULL);
if (!dir) {
*output = PrintfToNewString("Error: Unknown dir handle %s.",
dir_index_string);
return 2;
}
entry = readdir(dir);
if (entry != NULL) {
*output = PrintfToNewString("readdir\1%s\1%d\1%s", dir_index_string,
entry->d_ino, entry->d_name);
} else {
*output = PrintfToNewString("readdir\1%s\1\1", dir_index_string);
}
return 0;
#endif
}
/**
* Handle a call to closedir() made by JavaScript.
*
* closedir expects 1 parameter:
* 0: The index of the directory (which is mapped to a DIR*)
* on success, closedir returns a result in |output| separated by \1:
* 0: "closedir"
* 1: the name of the directory
* on failure, closedir returns an error string in |output|.
*
* @param[in] num_params The number of params in |params|.
* @param[in] params An array of strings, parameters to this function.
* @param[out] output A string to write informational function output to.
* @return An errorcode; 0 means success, anything else is a failure. */
int HandleClosedir(int num_params, char** params, char** output) {
#if defined(WIN32)
*output = PrintfToNewString("Error: Win32 does not support closedir.");
return 1;
#else
DIR* dir;
int dir_index;
const char* dir_index_string;
int result;
if (num_params != 1) {
*output = PrintfToNewString("Error: closedir takes 1 parameters.");
return 1;
}
dir_index_string = params[0];
dir = GetDirFromIndexString(dir_index_string, &dir_index);
if (!dir) {
*output = PrintfToNewString("Error: Unknown dir handle %s.",
dir_index_string);
return 2;
}
result = closedir(dir);
if (result) {
*output = PrintfToNewString("Error: closedir returned error %d.", result);
return 3;
}
RemoveDirFromMap(dir_index);
*output = PrintfToNewString("closedir\1%s", dir_index_string);
return 0;
#endif
}
/**
* Handle a call to mkdir() made by JavaScript.
*
* mkdir expects 1 parameter:
* 0: The name of the directory
* 1: The mode to use for the new directory, in octal.
* on success, mkdir returns a result in |output| separated by \1:
* 0: "mkdir"
* 1: the name of the directory
* on failure, mkdir returns an error string in |output|.
*
* @param[in] num_params The number of params in |params|.
* @param[in] params An array of strings, parameters to this function.
* @param[out] output A string to write informational function output to.
* @return An errorcode; 0 means success, anything else is a failure. */
int HandleMkdir(int num_params, char** params, char** output) {
const char* dirname;
int result;
int mode;
if (num_params != 2) {
*output = PrintfToNewString("Error: mkdir takes 2 parameters.");
return 1;
}
dirname = params[0];
mode = strtol(params[1], NULL, 8);
result = mkdir(dirname, mode);
if (result != 0) {
*output = PrintfToNewString("Error: mkdir returned error: %d", errno);
return 2;
}
*output = PrintfToNewString("mkdir\1%s", dirname);
return 0;
}