aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Prestwood <james.prestwood@intel.com>2017-03-08 15:18:19 -0800
committerGeoff Gustafson <geoff@linux.intel.com>2017-03-08 15:18:19 -0800
commit6b7abbd61cd4105c2a278492fede1e8df0a7c288 (patch)
tree61637fbf91afc49fde1c258741b4a64e6af6a3ac
parent70a30cc653ee6792b4de44f29e78d4bed6b6dfbb (diff)
[fs] File System module (#510)
Signed-off-by: James Prestwood <james.prestwood@intel.com>
-rw-r--r--docs/API.md2
-rw-r--r--docs/fs.md175
-rw-r--r--samples/FsAsync.js44
-rw-r--r--samples/FsSync.js37
-rwxr-xr-xscripts/analyze.sh17
-rw-r--r--src/Makefile.base1
-rw-r--r--src/zjs_fs.c1099
-rw-r--r--src/zjs_fs.h8
-rw-r--r--src/zjs_modules.c4
-rw-r--r--tests/test-fs.js195
10 files changed, 1582 insertions, 0 deletions
diff --git a/docs/API.md b/docs/API.md
index 0d89e17..07de10e 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -45,6 +45,8 @@ General
[Events](./events.md)
+[File System](./fs.md)
+
[Performance](./performance.md)
[Timers](./timers.md)
diff --git a/docs/fs.md b/docs/fs.md
new file mode 100644
index 0000000..b6564a9
--- /dev/null
+++ b/docs/fs.md
@@ -0,0 +1,175 @@
+ZJS API for File System
+==================
+
+* [Introduction](#introduction)
+* [API Documentation](#api-documentation)
+* [Sample Apps](#sample-apps)
+
+Introduction
+------------
+ZJS provides File System API's which match Node.js' FS module. We describe them
+here as there could potentially be minor differences. It should be noted that by
+default the FS module only contains the synchronous Node.js API's. The
+asynchronous API's can be compiled in by enabling the pre-processor define
+`ZJS_FS_ASYNC_APIS`. They are compiled out because all of Zephyr's File
+System API's are synchronous, so making the JavaScript API's asynchronous was
+only adding ROM space.
+
+On the Arduino 101 the flash file system uses SPI and the pins are shared with
+IO10-13. For this reason you will not be able to use these GPIO pins at the
+same time as the file system.
+
+Available file modes:
+
+`'r'` - Open file for only reading. An error will be thrown if the file does
+not exist.
+
+`'r+'` - Open a file for reading and writing. An error will be thrown if the
+file does not exist.
+
+`'w'` - Opens a file for writing. The file will be overwritten if it already
+exists.
+
+`'w+'` - Opens a file for writing and reading. The file will be overwritten if
+it already exists.
+
+`'a'` - Opens a file for appending. The write pointer will always seek
+to the end of the file during a write.
+
+`'a+'` - Opens a file for appending and reading. As with `'a'` the write
+pointer will seek to the end for writes, but reads can be done from the
+start of the file (read pointer saved across different read calls).
+
+Web IDL
+-------
+This IDL provides an overview of the interface; see below for documentation of
+specific API functions.
+
+```javascript
+// require returns a FS object
+// var fs = require('fs');
+
+interface Stat {
+ boolean isFile();
+ boolean isDirectory();
+};
+```
+
+API Documentation
+-----------------
+
+### FS.openSync
+`object openSync(string path, string mode);`
+
+Opens a file.
+
+`path` is the name/path of the file to open.
+
+`mode` is the mode to open the file in (r/w/a/r+/w+/a+).
+
+Returns an object representing the file descriptor.
+
+### FS.closeSync
+`void closeSync(object fd);`
+
+Closes a file.
+
+`fd` is the descriptor returned from `openSync()`.
+
+### FS.unlinkSync
+`void unlinkSync(string path);`
+
+Unlink (remove) a file from the file system.
+
+`path` is the file to remove.
+
+### FS.rmdirSync
+`void rmdirSync(string path);`
+
+Remove a directory from the file system.
+
+`path` is the name of the directory.
+
+### FS.writeSync
+`number writeSync(object fd, [String|Buffer] data, number offset, number length, optional number position);`
+
+Write bytes to an opened file.
+
+`fd` is the file descriptor returned from `openSync()`.
+
+`data` is either a string or buffer to write.
+
+`offset` is the position in `data` to start writing from.
+
+`length` is the number of bytes to write from `data`.
+
+`position` is the offset from the beginning of the file where `data` should be
+written. Default is 0.
+
+Returns the number of bytes actually written (this may be different from `length`).
+
+### FS.readSync
+`number readSync(object fd, buffer data, number offset, number length, number position);`
+
+Read bytes from a file.
+
+`fd` is the file descriptor returned from `openSync()`.
+
+`data` is a buffer where the data will be read into.
+
+`offset` is the offset in `data` to start writing at.
+
+`length` is the number of bytes to read.
+
+`position` is the position in the file to start reading from.
+
+Returns the number of bytes actually read. This may be different from `length`
+if there was a read error or if the file had no more data left to read.
+
+### FS.truncateSync
+`void truncateSync(string path, number length);`
+
+Truncate a file. If the length passed in is shorter than the existing file
+length then the trailing file data will be lost.
+
+`path` is the name of the file.
+
+`length` is the new length of the file.
+
+### FS.mkdirSync
+`void mkdirSync(string path)`
+
+Create a directory. If the directory already exists there is no effect.
+
+`path` is the name of the directory.
+
+### FS.readdirSync
+`string[] readdirSync(string path);`
+
+Read the contents of a directory.
+
+`path` directory path to read.
+
+Returns an array of filenames and directories found in `path`.
+
+### FS.statSync
+`Stat statSync(string path);`
+
+Get stats about a file or directory.
+
+`path` name of file or directory.
+
+Returns a `Stat` object for that file or directory.
+
+### FS.writeFileSync
+`void writeFileSync(string file, [string|buffer] data);`
+
+Open and write data to a file. This will replace the file if it already exists.
+
+`file` is the name of the file to write to.
+
+`data` is the bytes to write into the file.
+
+Sample Apps
+-----------
+* [FS test](../tests/test-fs.js)
diff --git a/samples/FsAsync.js b/samples/FsAsync.js
new file mode 100644
index 0000000..75ac962
--- /dev/null
+++ b/samples/FsAsync.js
@@ -0,0 +1,44 @@
+var fs = require('fs');
+
+console.log('File System sample');
+
+fs.open('testfile.txt', 'w+', function(err, fd) {
+ console.log('opened file error=' + err);
+ fs.stat('testfile.txt', function(err, stats) {
+ if (stats.isFile()) {
+ console.log('fd is file');
+ } else if (stats.isDirectory()) {
+ console.log('fd is directory');
+ } else {
+ console.log('fd has unknown type');
+ }
+ });
+ var wbuf = new Buffer('write some bytes');
+ fs.write(fd, wbuf, 0, wbuf.length, 0, function(err, written, buffer) {
+ console.log('wrote ' + written + ' bytes');
+ var rbuf = new Buffer(16);
+ fs.read(fd, rbuf, 0, 16, 0, function(err, num, buf) {
+ console.log('read from file; error=' + err);
+ console.log('buffer=' + buf.toString('ascii'));
+ });
+ });
+});
+
+fs.open('file.txt', 'w', function(err, fd) {
+ console.log('opened file error=' + err);
+});
+
+fs.writeFile('string.txt', 'string data', {}, function(err) {
+ console.log('wrote string data file error=' + err);
+});
+fs.writeFile('buffer.txt', new Buffer('buffer data'), function(err) {
+ console.log('wrote buffer data file error=' + err);
+});
+
+setTimeout(function() {
+ fs.readdir('/', function(err, files) {
+ for (var i = 0; i < files.length; ++i) {
+ console.log('files[' + i + ']=' + files[i]);
+ }
+ });
+}, 1000);
diff --git a/samples/FsSync.js b/samples/FsSync.js
new file mode 100644
index 0000000..4d31f39
--- /dev/null
+++ b/samples/FsSync.js
@@ -0,0 +1,37 @@
+var fs = require('fs');
+
+console.log('File System sample');
+
+var fd = fs.openSync('testfile.txt', 'w+');
+
+var stats = fs.statSync('testfile.txt');
+
+if (stats.isFile()) {
+ console.log('fd is file');
+} else if (stats.isDirectory()) {
+ console.log('fd is directory');
+} else {
+ console.log('fd has unknown type');
+}
+
+var wbuf = new Buffer('write some bytes');
+
+var written = fs.writeSync(fd, wbuf, 0, wbuf.length, 0);
+
+console.log('wrote ' + written + ' bytes');
+
+
+var rbuf = new Buffer(16);
+var ret = fs.readSync(fd, rbuf, 0, 16, 0);
+console.log('read ' + ret + ' bytes from file');
+console.log('buffer=' + rbuf.toString('ascii'));
+
+var fd1 = fs.openSync('file.txt', 'w');
+
+fs.writeFileSync('string.txt', 'string data', {});
+fs.writeFileSync('buffer.txt', new Buffer('buffer data'));
+
+var files = fs.readdirSync('/');
+for (var i = 0; i < files.length; ++i) {
+ console.log('files[' + i + ']=' + files[i]);
+}
diff --git a/scripts/analyze.sh b/scripts/analyze.sh
index 9beb7aa..e37e69b 100755
--- a/scripts/analyze.sh
+++ b/scripts/analyze.sh
@@ -236,6 +236,23 @@ if check_for_require aio || check_config_file ZJS_AIO; then
echo "export ZJS_AIO=y" >> zjs.conf.tmp
fi
+if check_for_require fs || check_config_file ZJS_FS; then
+ >&2 echo Using module: FS
+ MODULES+=" -DBUILD_MODULE_FS -DBUILD_MODULE_BUFFER"
+ if [ $BOARD = "arduino_101" ]; then
+ echo "CONFIG_FS_FAT_FLASH_DISK_W25QXXDV=y" >> prj.conf.tmp
+ fi
+ echo "CONFIG_FILE_SYSTEM=y" >> prj.conf.tmp
+ echo "CONFIG_FILE_SYSTEM_FAT=y" >> prj.conf.tmp
+ echo "CONFIG_DISK_ACCESS_FLASH=y" >> prj.conf.tmp
+
+ echo "CONFIG_FLASH=y" >> prj.conf.tmp
+ echo "CONFIG_SPI=y" >> prj.conf.tmp
+ echo "CONFIG_GPIO=y" >> prj.conf.tmp
+ echo "export ZJS_FS=y" >> zjs.conf.tmp
+ echo "export ZJS_BUFFER=y" >> zjs.conf.tmp
+fi
+
if check_for_require i2c || check_config_file ZJS_I2C; then
>&2 echo Using module: I2C
MODULES+=" -DBUILD_MODULE_I2C"
diff --git a/src/Makefile.base b/src/Makefile.base
index 9cb3589..e9f32b2 100644
--- a/src/Makefile.base
+++ b/src/Makefile.base
@@ -45,6 +45,7 @@ obj-$(ZJS_BUFFER) += zjs_buffer.o
obj-$(ZJS_CONSOLE) += zjs_console.o
obj-$(ZJS_DGRAM) += zjs_dgram.o
obj-$(ZJS_EVENTS) += zjs_event.o
+obj-$(ZJS_FS) += zjs_fs.o
obj-$(ZJS_GPIO) += zjs_gpio.o
obj-$(ZJS_BLE) += zjs_ble.o
obj-$(ZJS_PWM) += zjs_pwm.o
diff --git a/src/zjs_fs.c b/src/zjs_fs.c
new file mode 100644
index 0000000..c5ca9b4
--- /dev/null
+++ b/src/zjs_fs.c
@@ -0,0 +1,1099 @@
+// Copyright (c) 2016, Intel Corporation.
+#ifdef BUILD_MODULE_FS
+
+#include <zephyr.h>
+#include <fs.h>
+
+#include <string.h>
+
+#include "zjs_callbacks.h"
+#include "zjs_common.h"
+#include "zjs_util.h"
+#include "zjs_buffer.h"
+
+typedef enum {
+ // Open for reading, file must already exist
+ MODE_R,
+ // Open for reading/writing, file must already exist
+ MODE_R_PLUS,
+ // Open file for writing, file will be created or overwritten
+ MODE_W,
+ // Open file for reading/writing, file will be created or overwritten
+ MODE_W_PLUS,
+ // Open file for appending, file is created if it does not exist
+ MODE_A,
+ // Open a file for appending/reading, file is created if it does not exist
+ MODE_A_PLUS
+} FileMode;
+
+#define MAX_PATH_LENGTH 128
+
+/*
+ * TODO: Someday we may want to keep a list of all open file descriptors. In
+ * the ashell use case the user may not clean up a descriptor which could
+ * get leaked.
+ */
+typedef struct file_handle {
+ fs_file_t fp;
+ FileMode mode;
+ jerry_value_t fd_val;
+ int error;
+ uint32_t rpos;
+} file_handle_t;
+
+static jerry_value_t invalid_args(void)
+{
+ return zjs_error("invalid arguments");
+}
+
+static file_handle_t* new_file(void)
+{
+ file_handle_t* handle = zjs_malloc(sizeof(file_handle_t));
+
+ memset(handle, 0, sizeof(file_handle_t));
+
+ return handle;
+}
+
+static int file_exists(const char *path)
+{
+ int res;
+ struct fs_dirent entry;
+
+ res = fs_stat(path, &entry);
+
+ return !res;
+}
+
+static uint16_t get_mode(char* str)
+{
+ uint16_t mode = 0;
+ if (strcmp(str, "r") == 0) {
+ mode = MODE_R;
+ } else if (strcmp(str, "r+") == 0) {
+ mode = MODE_R_PLUS;
+ } else if (strcmp(str, "w") == 0) {
+ mode = MODE_W;
+ } else if (strcmp(str, "w+") == 0) {
+ mode = MODE_W_PLUS;
+ } else if (strcmp(str, "a") == 0) {
+ mode = MODE_A;
+ } else if (strcmp(str, "a+") == 0) {
+ mode = MODE_A_PLUS;
+ }
+ return mode;
+}
+
+static jerry_value_t is_file(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc)
+{
+ struct fs_dirent* entry;
+
+ if (!jerry_get_object_native_handle(this, (uintptr_t*)&entry)) {
+ return zjs_error("native handle not found");
+ }
+ if (entry->type == FS_DIR_ENTRY_FILE) {
+ return jerry_create_boolean(true);
+ } else {
+ return jerry_create_boolean(false);
+ }
+}
+
+static jerry_value_t is_directory(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc)
+{
+ struct fs_dirent* entry;
+
+ if (!jerry_get_object_native_handle(this, (uintptr_t*)&entry)) {
+ return zjs_error("native handle not found");
+ }
+ if (entry->type == FS_DIR_ENTRY_DIR) {
+ return jerry_create_boolean(true);
+ } else {
+ return jerry_create_boolean(false);
+ }
+}
+
+static void free_stats(const uintptr_t native)
+{
+ struct zfs_dirent* entry = (struct zfs_dirent*)native;
+ if (entry) {
+ zjs_free(entry);
+ }
+}
+
+static jerry_value_t create_stats_obj(struct fs_dirent* entry)
+{
+
+ jerry_value_t stats_obj = jerry_create_object();
+
+ struct fs_dirent* new_entry = zjs_malloc(sizeof(struct fs_dirent));
+ if (!new_entry) {
+ return zjs_error("malloc failed");
+ }
+ memcpy(new_entry, entry, sizeof(struct fs_dirent));
+
+ jerry_set_object_native_handle(stats_obj, (uintptr_t)new_entry, free_stats);
+
+ zjs_obj_add_function(stats_obj, is_file, "isFile");
+ zjs_obj_add_function(stats_obj, is_directory, "isDirectory");
+
+ return stats_obj;
+}
+
+static jerry_value_t zjs_open(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc,
+ uint8_t async)
+{
+ if (argc < 3 && async) {
+ return invalid_args();
+ }
+ if (!jerry_value_is_string(argv[0])) {
+ return invalid_args();
+ }
+ if (!jerry_value_is_string(argv[1])) {
+ return invalid_args();
+ }
+ if (async) {
+ if (!jerry_value_is_function(argv[argc - 1])) {
+ return invalid_args();
+ }
+ }
+
+ file_handle_t* handle = new_file();
+ if (!handle) {
+ return zjs_error("malloc failed");
+ }
+ handle->fd_val = jerry_create_object();
+
+ jerry_size_t size = MAX_PATH_LENGTH;
+ char path[size];
+
+ zjs_copy_jstring(argv[0], path, &size);
+ if (!size) {
+ return zjs_error("size mismatch");
+ }
+
+ size = 4;
+ char mode[size];
+
+ zjs_copy_jstring(argv[1], mode, &size);
+ if (!size) {
+ return zjs_error("size mismatch");
+ }
+
+ DBG_PRINT("Opening file: %s, mode: %s\n", path, mode);
+
+ handle->mode = get_mode(mode);
+
+ if ((handle->mode == MODE_R || handle->mode == MODE_R_PLUS)
+ && !file_exists(path)) {
+ jerry_release_value(handle->fd_val);
+ zjs_free(handle);
+ return zjs_error("file doesn't exist");
+ }
+
+ handle->error = fs_open(&handle->fp, path);
+ if (handle->error != 0) {
+ ERR_PRINT("could not open file: %s, error=%d\n", path, handle->error);
+ jerry_release_value(handle->fd_val);
+ zjs_free(handle);
+ return zjs_error("could not open file");
+ }
+
+ jerry_set_object_native_handle(handle->fd_val, (uintptr_t)handle, NULL);
+
+#ifdef ZJS_FS_ASYNC_APIS
+ if (async) {
+ zjs_callback_id id = zjs_add_callback_once(argv[argc - 1],
+ this,
+ handle,
+ NULL);
+
+ jerry_value_t args[2];
+
+ args[0] = jerry_create_number(handle->error);
+ args[1] = handle->fd_val;
+
+ zjs_signal_callback(id, args, sizeof(jerry_value_t) * 2);
+
+ return ZJS_UNDEFINED;
+ }
+#endif
+
+ return handle->fd_val;
+}
+
+static jerry_value_t zjs_open_sync(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_open(function_obj, this, argv, argc, 0);
+}
+
+#ifdef ZJS_FS_ASYNC_APIS
+static jerry_value_t zjs_open_async(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_open(function_obj, this, argv, argc, 1);
+}
+#endif
+
+static jerry_value_t zjs_close(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc,
+ uint8_t async)
+{
+ file_handle_t* handle;
+
+ if (!jerry_value_is_object(argv[0])) {
+ return invalid_args();
+ }
+ if (async) {
+ if (!jerry_value_is_function(argv[1])) {
+ return invalid_args();
+ }
+ }
+ if (!jerry_get_object_native_handle(argv[0], (uintptr_t*)&handle)) {
+ return zjs_error("native handle not found");
+ }
+
+ handle->error = fs_close(&handle->fp);
+
+#ifdef ZJS_FS_ASYNC_APIS
+ if (async) {
+ zjs_callback_id id = zjs_add_callback_once(argv[1],
+ this,
+ handle,
+ NULL);
+
+ jerry_value_t error = jerry_create_number(handle->error);
+
+ zjs_signal_callback(id, &error, sizeof(jerry_value_t));
+ }
+#endif
+
+ zjs_free(handle);
+
+ return ZJS_UNDEFINED;
+}
+
+static jerry_value_t zjs_close_sync(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_close(function_obj, this, argv, argc, 0);
+}
+
+#ifdef ZJS_FS_ASYNC_APIS
+static jerry_value_t zjs_close_async(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_close(function_obj, this, argv, argc, 1);
+}
+#endif
+
+static jerry_value_t zjs_unlink(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc,
+ uint8_t async)
+{
+ int ret = 0;
+
+ if (!jerry_value_is_string(argv[0])) {
+ return invalid_args();
+ }
+ if (async) {
+ if (!jerry_value_is_function(argv[1])) {
+ return invalid_args();
+ }
+ }
+
+ jerry_size_t size = MAX_PATH_LENGTH;
+ char path[size];
+
+ zjs_copy_jstring(argv[0], path, &size);
+ if (!size) {
+ return zjs_error("size mismatch");
+ }
+
+ ret = fs_unlink(path);
+
+#ifdef ZJS_FS_ASYNC_APIS
+ if (async) {
+ zjs_callback_id id = zjs_add_callback_once(argv[1],
+ this,
+ NULL,
+ NULL);
+
+ jerry_value_t error = jerry_create_number(ret);
+ zjs_signal_callback(id, &error, sizeof(jerry_value_t));
+ }
+#endif
+
+ return ZJS_UNDEFINED;
+}
+
+static jerry_value_t zjs_unlink_sync(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_unlink(function_obj, this, argv, argc, 0);
+}
+
+#ifdef ZJS_FS_ASYNC_APIS
+static jerry_value_t zjs_unlink_async(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_unlink(function_obj, this, argv, argc, 1);
+}
+#endif
+
+static jerry_value_t zjs_read(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc,
+ uint8_t async)
+{
+ file_handle_t* handle;
+ int err = 0;
+
+ if (argc < 6 && async) {
+ return invalid_args();
+ }
+
+ if (!jerry_value_is_object(argv[0])) {
+ return invalid_args();
+ }
+ if (!jerry_value_is_object(argv[1])) {
+ return invalid_args();
+ }
+ if (!jerry_value_is_number(argv[2])) {
+ return invalid_args();
+ }
+ if (!jerry_value_is_number(argv[3])) {
+ return invalid_args();
+ }
+ if (!jerry_value_is_number(argv[4]) && !jerry_value_is_null(argv[4])) {
+ return invalid_args();
+ }
+ if (async) {
+ if (!jerry_value_is_function(argv[5])) {
+ return invalid_args();
+ }
+ }
+ if (!jerry_get_object_native_handle(argv[0], (uintptr_t*)&handle)) {
+ return zjs_error("native handle not found");
+ }
+
+ if (handle->mode == MODE_W || handle->mode == MODE_A) {
+ return zjs_error("file is not open for reading");
+ }
+
+ zjs_buffer_t* buffer = zjs_buffer_find(argv[1]);
+ double offset = jerry_get_number_value(argv[2]);
+ double length = jerry_get_number_value(argv[3]);
+
+ if (offset < 0 || length < 0) {
+ return invalid_args();
+ }
+ if (offset >= buffer->bufsize) {
+ return zjs_error("offset overflows buffer");
+ }
+ if (offset + length > buffer->bufsize) {
+ return zjs_error("offset + length overflows buffer");
+ }
+
+ // if mode == a+
+ if (handle->mode == MODE_A_PLUS) {
+ // mode is a+, seek to read position
+ if (fs_seek(&handle->fp, handle->rpos, SEEK_SET) != 0) {
+ return zjs_error("error seeking to position");
+ }
+ }
+ if (jerry_value_is_number(argv[4])) {
+ // if position was a number, set as the new read position
+ double position = jerry_get_number_value(argv[4]);
+ if (position < 0) {
+ return invalid_args();
+ }
+ handle->rpos = position;
+ // if a position was specified, seek to it before reading
+ if (fs_seek(&handle->fp, (uint32_t)position, SEEK_SET) != 0) {
+ return zjs_error("error seeking to position");
+ }
+ }
+
+ DBG_PRINT("reading into fp=%p, buffer=%p, offset=%lu, length=%lu\n", &handle->fp, buffer->buffer, (uint32_t)offset, (uint32_t)length);
+
+ uint32_t ret = fs_read(&handle->fp, buffer->buffer + (uint32_t)offset, (uint32_t)length);
+
+ if (ret != (uint32_t)length) {
+ DBG_PRINT("could not read %lu bytes, only %lu were read\n", (uint32_t)length, ret);
+ err = -1;
+ }
+ handle->rpos += ret;
+
+#ifdef ZJS_FS_ASYNC_APIS
+ if (async) {
+ jerry_value_t args[3];
+
+ args[0] = jerry_create_number(err);
+ args[1] = jerry_create_number(ret);
+ args[2] = argv[1];
+
+ zjs_callback_id id = zjs_add_callback_once(argv[5],
+ this,
+ NULL,
+ NULL);
+
+ zjs_signal_callback(id, args, sizeof(jerry_value_t) * 3);
+
+ return ZJS_UNDEFINED;
+ }
+#endif
+ return jerry_create_number(ret);
+}
+
+static jerry_value_t zjs_read_sync(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_read(function_obj, this, argv, argc, 0);
+}
+
+#ifdef ZJS_FS_ASYNC_APIS
+static jerry_value_t zjs_read_async(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_read(function_obj, this, argv, argc, 1);
+}
+#endif
+
+static jerry_value_t zjs_write(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc,
+ uint8_t async)
+{
+ file_handle_t* handle;
+ double position = 0;
+ double offset = 0;
+ double length = 0;
+ uint8_t from_cur = 0;
+#ifdef ZJS_FS_ASYNC_APIS
+ jerry_value_t js_cb = ZJS_UNDEFINED;
+#endif
+
+ if (argc < 2) {
+ return invalid_args();
+ }
+ if (jerry_value_is_object(argv[1])) {
+ // buffer
+ if (argc > 2) {
+ if (jerry_value_is_number(argv[2])) {
+ offset = jerry_get_number_value(argv[2]);
+ } else {
+ return invalid_args();
+ }
+ }
+ if (argc > 3) {
+ if (jerry_value_is_number(argv[3])) {
+ length = jerry_get_number_value(argv[3]);
+ } else {
+ return invalid_args();
+ }
+ }
+ if (argc > 4) {
+ if (jerry_value_is_number(argv[4])) {
+ position = jerry_get_number_value(argv[4]);
+ } else {
+ // position != number
+ from_cur = 1;
+ }
+ }
+#ifdef ZJS_FS_ASYNC_APIS
+ if (!jerry_value_is_function(argv[argc - 1])) {
+ return invalid_args();
+ } else {
+ js_cb = argv[argc - 1];
+ }
+#endif
+ } else if (jerry_value_is_string(argv[1])) {
+ /*
+ * TODO: Eventually support string if its desired. It makes argument
+ * parsing a huge pain, so just supporting buffer for now seems
+ * like the right decision.
+ */
+ return zjs_error("string input not supported");
+ } else {
+ return invalid_args();
+ }
+
+ if (!jerry_get_object_native_handle(argv[0], (uintptr_t*)&handle)) {
+ return zjs_error("native handle not found");
+ }
+
+ if (handle->mode == MODE_R) {
+ return zjs_error("file is not open for writing");
+ }
+
+ zjs_buffer_t* buffer = zjs_buffer_find(argv[1]);
+
+ if (offset && !length) {
+ length = buffer->bufsize - offset;
+ } else if (!length) {
+ length = buffer->bufsize;
+ }
+
+ if (offset < 0 || length < 0 || position < 0) {
+ return invalid_args();
+ }
+ if (offset >= buffer->bufsize) {
+ return zjs_error("offset overflows buffer");
+ }
+ if (offset + length > buffer->bufsize) {
+ return zjs_error("offset + length overflows buffer");
+ }
+
+ if (handle->mode == MODE_A || handle->mode == MODE_A_PLUS) {
+ // if in append mode, seek to end (ignoring position parameter)
+ if (fs_seek(&handle->fp, 0, SEEK_END) != 0) {
+ return zjs_error("error seeking start");
+ }
+ } else if (!from_cur) {
+ // if a position was specified, seek to it before writing
+ if (fs_seek(&handle->fp, (uint32_t)position, SEEK_SET) != 0) {
+ return zjs_error("error seeking to position\n");
+ }
+ }
+
+ DBG_PRINT("writing to fp=%p, buffer=%p, offset=%lu, length=%lu\n", &handle->fp, buffer->buffer, (uint32_t)offset, (uint32_t)length);
+
+ uint32_t written = fs_write(&handle->fp, buffer->buffer + (uint32_t)offset, (uint32_t)length);
+
+ if (written != (uint32_t)length) {
+ DBG_PRINT("could not write %lu bytes, only %lu were written\n", (uint32_t)length, written);
+ }
+
+#ifdef ZJS_FS_ASYNC_APIS
+ if (async) {
+ jerry_value_t args[3];
+
+ args[0] = jerry_create_number(0);
+ args[1] = jerry_create_number(written);
+ args[2] = argv[1];
+
+ zjs_callback_id id = zjs_add_callback_once(js_cb,
+ this,
+ NULL,
+ NULL);
+
+ zjs_signal_callback(id, args, sizeof(jerry_value_t) * 3);
+
+ return ZJS_UNDEFINED;
+ }
+#endif
+ return jerry_create_number(written);
+}
+
+static jerry_value_t zjs_write_sync(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_write(function_obj, this, argv, argc, 0);
+}
+
+#ifdef ZJS_FS_ASYNC_APIS
+static jerry_value_t zjs_write_async(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_write(function_obj, this, argv, argc, 1);
+}
+#endif
+
+static jerry_value_t zjs_truncate(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc,
+ uint8_t async)
+{
+ fs_file_t fp;
+ if (!jerry_value_is_number(argv[1])) {
+ return invalid_args();
+ }
+ if (async) {
+ if (!jerry_value_is_function(argv[2])) {
+ return invalid_args();
+ }
+ }
+ if (jerry_value_is_object(argv[0])) {
+ file_handle_t* handle;
+ if (!jerry_get_object_native_handle(argv[0], (uintptr_t*)&handle)) {
+ return zjs_error("native handle not found");
+ }
+ fp = handle->fp;
+ } else if (jerry_value_is_string(argv[0])) {
+ jerry_size_t size = MAX_PATH_LENGTH;
+ char path[size];
+
+ zjs_copy_jstring(argv[0], path, &size);
+ if (!size) {
+ return zjs_error("size mismatch");
+ }
+ if (!fs_open(&fp, path)) {
+ return zjs_error("error opening file for truncation");
+ }
+ } else {
+ return invalid_args();
+ }
+
+ uint32_t length = jerry_get_number_value(argv[1]);
+
+ if (!fs_truncate(&fp, length)) {
+ return zjs_error("error calling fs_truncate()");
+ }
+
+#ifdef ZJS_FS_ASYNC_APIS
+ if (async) {
+ zjs_callback_id id = zjs_add_callback_once(argv[2],
+ this,
+ NULL,
+ NULL);
+
+ zjs_signal_callback(id, NULL, 0);
+ }
+#endif
+ return ZJS_UNDEFINED;
+}
+
+static jerry_value_t zjs_truncate_sync(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_truncate(function_obj, this, argv, argc, 0);
+}
+
+#ifdef ZJS_FS_ASYNC_APIS
+static jerry_value_t zjs_truncate_async(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_truncate(function_obj, this, argv, argc, 1);
+}
+#endif
+
+static jerry_value_t zjs_mkdir(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc,
+ uint8_t async)
+{
+ jerry_value_t js_cb = ZJS_UNDEFINED;
+ uint32_t mode = MODE_A_PLUS;
+ jerry_size_t size;
+ if (!jerry_value_is_string(argv[0])) {
+ return invalid_args();
+ }
+ if (argc > 2) {
+ if (async) {
+ if (!jerry_value_is_function(argv[2])) {
+ return invalid_args();
+ } else {
+ js_cb = argv[2];
+ }
+ }
+ if (!jerry_value_is_string(argv[1])) {
+ return invalid_args();
+ } else {
+ size = 4;
+ char mode_str[size];
+
+ zjs_copy_jstring(argv[1], mode_str, &size);
+ if (!size) {
+ return zjs_error("size mismatch");
+ }
+
+ mode = get_mode(mode_str);
+ }
+ } else {
+ if (async) {
+ if (!jerry_value_is_function(argv[1])) {
+ return invalid_args();
+ } else {
+ js_cb = argv[1];
+ }
+ }
+ }
+ size = MAX_PATH_LENGTH;
+ char path[size];
+
+ zjs_copy_jstring(argv[0], path, &size);
+ if (!size) {
+ return zjs_error("size mismatch");
+ }
+
+ if (!fs_mkdir(path)) {
+ return zjs_error("error creating directory");
+ }
+#ifdef ZJS_FS_ASYNC_APIS
+ if (async) {
+ zjs_callback_id id = zjs_add_callback_once(js_cb,
+ this,
+ NULL,
+ NULL);
+
+ zjs_signal_callback(id, NULL, 0);
+ }
+#endif
+
+ return ZJS_UNDEFINED;
+}
+
+static jerry_value_t zjs_mkdir_sync(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_mkdir(function_obj, this, argv, argc, 0);
+}
+
+#ifdef ZJS_FS_ASYNC_APIS
+static jerry_value_t zjs_mkdir_async(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_mkdir(function_obj, this, argv, argc, 1);
+}
+#endif
+
+static jerry_value_t zjs_readdir(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc,
+ uint8_t async)
+{
+ jerry_value_t array;
+
+ if (!jerry_value_is_string(argv[0])) {
+ return zjs_error("first parameter was not path");
+ }
+ jerry_size_t size = MAX_PATH_LENGTH;
+ char path[size];
+
+ zjs_copy_jstring(argv[0], path, &size);
+ if (!size) {
+ return zjs_error("size mismatch");
+ }
+
+ int num_files = 0;
+ int res;
+ fs_dir_t dp;
+ static struct fs_dirent entry;
+
+ res = fs_opendir(&dp, path);
+ if (res) {
+ return zjs_error("Error opening dir");
+ }
+
+ DBG_PRINT("Searching for files and sub directories in %s\n", path);
+ for (;;) {
+ res = fs_readdir(&dp, &entry);
+
+ /* entry.name[0] == 0 means end-of-dir */
+ if (res || entry.name[0] == 0) {
+ break;
+ }
+
+ /* Delete file or sub directory */
+ num_files++;
+
+ DBG_PRINT("found file %s\n", entry.name);
+ }
+
+ fs_closedir(&dp);
+
+ res = fs_opendir(&dp, path);
+ if (res) {
+ return zjs_error("Error opening dir");
+ }
+
+ DBG_PRINT("Adding files and sub directories in %s to array\n", path);
+
+ array = jerry_create_array(num_files);
+
+ uint32_t i;
+ for (i = 0; i < num_files; ++i) {
+ res = fs_readdir(&dp, &entry);
+
+ /* entry.name[0] == 0 means end-of-dir */
+ if (res || entry.name[0] == 0) {
+ break;
+ }
+
+ jerry_value_t value = jerry_create_string(entry.name);
+
+ jerry_set_property_by_index (array, i, value);
+ }
+
+ fs_closedir(&dp);
+
+#ifdef ZJS_FS_ASYNC_APIS
+ if (async) {
+ zjs_callback_id id = zjs_add_callback_once(argv[1],
+ this,
+ NULL,
+ NULL);
+
+ jerry_value_t args[2];
+
+ args[0] = jerry_create_number(res);
+ args[1] = array;
+
+ zjs_signal_callback(id, args, sizeof(jerry_value_t) * 2);
+
+ return ZJS_UNDEFINED;
+ }
+#endif
+ return array;
+}
+
+static jerry_value_t zjs_readdir_sync(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_readdir(function_obj, this, argv, argc, 0);
+}
+
+#ifdef ZJS_FS_ASYNC_APIS
+static jerry_value_t zjs_readdir_async(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_readdir(function_obj, this, argv, argc, 1);
+}
+#endif
+
+static jerry_value_t zjs_stat(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc,
+ uint8_t async)
+{
+ if (!jerry_value_is_string(argv[0])) {
+ return invalid_args();
+ }
+ if (async) {
+ if (!jerry_value_is_function(argv[1])) {
+ return invalid_args();
+ }
+ }
+ jerry_size_t size = MAX_PATH_LENGTH;
+ char path[size];
+
+ zjs_copy_jstring(argv[0], path, &size);
+ if (!size) {
+ return zjs_error("size mismatch");
+ }
+
+ int ret;
+ struct fs_dirent entry;
+
+ ret = fs_stat(path, &entry);
+
+#ifdef ZJS_FS_ASYNC_APIS
+ if (async) {
+ jerry_value_t args[2];
+
+ args[0] = jerry_create_number(ret);
+ args[1] = create_stats_obj(&entry);
+
+ zjs_callback_id id = zjs_add_callback_once(argv[1],
+ this,
+ NULL,
+ NULL);
+
+ zjs_signal_callback(id, args, sizeof(jerry_value_t) * 2);
+
+ return ZJS_UNDEFINED;
+ }
+#endif
+ return create_stats_obj(&entry);
+}
+
+static jerry_value_t zjs_stat_sync(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_stat(function_obj, this, argv, argc, 0);
+}
+
+#ifdef ZJS_FS_ASYNC_APIS
+static jerry_value_t zjs_stat_async(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_stat(function_obj, this, argv, argc, 1);
+}
+#endif
+
+static jerry_value_t zjs_write_file(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc,
+ uint8_t async)
+{
+ uint8_t is_buf = 0;
+ jerry_size_t size;
+ int error = 0;
+ jerry_value_t js_cb = ZJS_UNDEFINED;
+ char* data = NULL;
+ uint32_t length;
+ if (!jerry_value_is_string(argv[0])) {
+ return invalid_args();
+ }
+ if (jerry_value_is_string(argv[1])) {
+ size = 256;
+ data = zjs_alloc_from_jstring(argv[1], &size);
+ length = size;
+ } else if (jerry_value_is_object(argv[1])) {
+ zjs_buffer_t* buffer = zjs_buffer_find(argv[1]);
+ data = buffer->buffer;
+ length = buffer->bufsize;
+ is_buf = 1;
+ } else {
+ return invalid_args();
+ }
+ // Options provided
+ if (argc > 3) {
+ if (!jerry_value_is_object(argv[2])) {
+ if (data && !is_buf) {
+ zjs_free(data);
+ }
+ return invalid_args();
+ }
+ // Options object has no effect on Zephyr, 'mode' and 'flag' properties
+ // are not used for opening a file, so their values are irrelevant
+#ifdef ZJS_FS_ASYNC_APIS
+ if (async) {
+ if (!jerry_value_is_function(argv[3])) {
+ return invalid_args();
+ } else {
+ js_cb = argv[3];
+ }
+ }
+#endif
+ } else if (async) {
+ js_cb = argv[argc - 1];
+ }
+
+ size = 32;
+ char* path = zjs_alloc_from_jstring(argv[0], &size);
+ if (!path) {
+ if (data && !is_buf) {
+ zjs_free(data);
+ }
+ return zjs_error("path string too long\n");
+ }
+
+ fs_file_t fp;
+ error = fs_open(&fp, path);
+ if (error != 0) {
+ ERR_PRINT("error opening file, error=%d\n", error);
+ goto Finished;
+ }
+ ssize_t written = fs_write(&fp, data, length);
+
+ if (written != length) {
+ ERR_PRINT("could not write %lu bytes, only %lu were written\n", length, written);
+ error = -1;
+ }
+
+ error = fs_close(&fp);
+ if (error != 0) {
+ ERR_PRINT("error closing file\n");
+ }
+
+Finished:
+#ifdef ZJS_FS_ASYNC_APIS
+ if (async) {
+ zjs_callback_id id = zjs_add_callback_once(js_cb,
+ this,
+ NULL,
+ NULL);
+
+ jerry_value_t err = jerry_create_number(error);
+
+ zjs_signal_callback(id, &err, sizeof(jerry_value_t));
+ }
+#endif
+ if (data && !is_buf) {
+ zjs_free(data);
+ }
+ if (path) {
+ zjs_free(path);
+ }
+ return ZJS_UNDEFINED;
+}
+
+static jerry_value_t zjs_write_file_sync(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_write_file(function_obj, this, argv, argc, 0);
+}
+
+#ifdef ZJS_FS_ASYNC_APIS
+static jerry_value_t zjs_write_file_async(const jerry_value_t function_obj,
+ const jerry_value_t this,
+ const jerry_value_t argv[],
+ const jerry_length_t argc) {
+ return zjs_write_file(function_obj, this, argv, argc, 1);
+}
+#endif
+
+jerry_value_t zjs_fs_init()
+{
+ jerry_value_t fs = jerry_create_object();
+
+ zjs_obj_add_function(fs, zjs_open_sync, "openSync");
+ zjs_obj_add_function(fs, zjs_close_sync, "closeSync");
+ zjs_obj_add_function(fs, zjs_unlink_sync, "unlinkSync");
+ zjs_obj_add_function(fs, zjs_unlink_sync, "rmdirSync");
+ zjs_obj_add_function(fs, zjs_read_sync, "readSync");
+ zjs_obj_add_function(fs, zjs_write_sync, "writeSync");
+ zjs_obj_add_function(fs, zjs_truncate_sync, "truncateSync");
+ zjs_obj_add_function(fs, zjs_mkdir_sync, "mkdirSync");
+ zjs_obj_add_function(fs, zjs_readdir_sync, "readdirSync");
+ zjs_obj_add_function(fs, zjs_stat_sync, "statSync");
+ zjs_obj_add_function(fs, zjs_write_file_sync, "writeFileSync");
+
+#ifdef ZJS_FS_ASYNC_APIS
+ zjs_obj_add_function(fs, zjs_open_async, "open");
+ zjs_obj_add_function(fs, zjs_close_async, "close");
+ zjs_obj_add_function(fs, zjs_unlink_async, "unlink");
+ zjs_obj_add_function(fs, zjs_unlink_async, "rmdir");
+ zjs_obj_add_function(fs, zjs_read_async, "read");
+ zjs_obj_add_function(fs, zjs_write_async, "write");
+ zjs_obj_add_function(fs, zjs_truncate_async, "truncate");
+ zjs_obj_add_function(fs, zjs_mkdir_async, "mkdir");
+ zjs_obj_add_function(fs, zjs_readdir_async, "readdir");
+ zjs_obj_add_function(fs, zjs_stat_async, "stat");
+ zjs_obj_add_function(fs, zjs_write_file_async, "writeFile");
+#endif
+
+ return fs;
+}
+#endif
diff --git a/src/zjs_fs.h b/src/zjs_fs.h
new file mode 100644
index 0000000..3c90517
--- /dev/null
+++ b/src/zjs_fs.h
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, Intel Corporation.
+
+#ifndef SRC_ZJS_FS_H_
+#define SRC_ZJS_FS_H_
+
+jerry_value_t zjs_fs_init();
+
+#endif /* SRC_ZJS_FS_H_ */
diff --git a/src/zjs_modules.c b/src/zjs_modules.c
index 531b595..7058a66 100644
--- a/src/zjs_modules.c
+++ b/src/zjs_modules.c
@@ -39,6 +39,7 @@
#include "zjs_i2c.h"
#include "zjs_pwm.h"
#include "zjs_uart.h"
+#include "zjs_fs.h"
#ifdef CONFIG_BOARD_ARDUINO_101
#include "zjs_a101_pins.h"
#endif
@@ -83,6 +84,9 @@ module_t zjs_modules_array[] = {
#ifdef BUILD_MODULE_I2C
{ "i2c", zjs_i2c_init },
#endif
+#ifdef BUILD_MODULE_FS
+ { "fs", zjs_fs_init },
+#endif
#ifdef CONFIG_BOARD_ARDUINO_101
#ifdef BUILD_MODULE_A101
{ "arduino101_pins", zjs_a101_init },
diff --git a/tests/test-fs.js b/tests/test-fs.js
new file mode 100644
index 0000000..5a797fa
--- /dev/null
+++ b/tests/test-fs.js
@@ -0,0 +1,195 @@
+var fs = require('fs');
+
+var total = 0;
+var passed = 0;
+var pass = true;
+
+function assert(actual, description) {
+ total += 1;
+
+ var label = "\033[1m\033[31mFAIL\033[0m";
+ if (actual === true) {
+ passed += 1;
+ label = "\033[1m\033[32mPASS\033[0m";
+ }
+
+ console.log(label + " - " + description);
+}
+
+function expectThrow(description, func) {
+ var threw = false;
+ try {
+ func();
+ }
+ catch (err) {
+ threw = true;
+ }
+ assert(threw, description);
+}
+
+// Clean up any test files left from previous tests
+var stats = fs.statSync('testfile.txt');
+if (stats.isFile() || stats.isDirectory()) {
+ fs.unlinkSync('testfile.txt');
+ console.log('removing testfile.txt');
+}
+
+// test file creation
+var fd = fs.openSync('testfile.txt', 'a');
+fs.closeSync(fd);
+var fd_stats = fs.statSync('testfile.txt');
+var success = false;
+if (fd_stats.isFile()) {
+ success = true;
+}
+assert(success, "create file");
+
+// test file removal
+fs.unlinkSync('testfile.txt');
+fd_stats = fs.statSync('testfile.txt');
+success = false;
+if (!fd_stats.isFile()) {
+ success = true;
+}
+assert(success, "remove file");
+
+//test invalid file open (on a non-existing file)
+expectThrow("open(r) on non-existing file", function () {
+ fd = fs.openSync('testfile.txt', 'r');
+});
+
+expectThrow("open(r+) on non-existing file", function () {
+ fd = fs.openSync('testfile.txt', 'r+');
+});
+
+// Test appending options
+fs.writeFileSync("testfile.txt", new Buffer("test"));
+
+fd = fs.openSync('testfile.txt', 'a');
+
+// test reading from 'a' file
+expectThrow("can't read from append file", function() {
+ var rbuf = new Buffer(wbuf.length);
+ var rlen = fs.readSync(fd, rbuf, 0, rbuf.length, 0);
+});
+
+// test writing to end of 'a' file
+var wbuf = new Buffer('write');
+var wlen = fs.writeSync(fd, wbuf, 0, wbuf.length, 0);
+assert((wbuf.length == wlen), "writing to append file")
+
+fs.closeSync(fd);
+
+// test reading from a+ file
+fd = fs.openSync('testfile.txt', 'a+');
+var rbuf = new Buffer(4);
+var rlen = fs.readSync(fd, rbuf, 0, 4, 0);
+assert((rbuf.length == rlen), "read from a+ file")
+assert((rbuf.toString('ascii') == "test"),
+ "read correct data from a+ file: " + rbuf.toString('ascii'));
+
+// test that read position was kept
+rbuf = new Buffer(5);
+rlen = fs.readSync(fd, rbuf, 0, 5, null);
+assert((rbuf.length == rlen), "read from a+ file (again)")
+assert((rbuf.toString('ascii') == "write"),
+ "read correct data from a+ file: " + rbuf.toString('ascii'));
+
+fs.closeSync(fd);
+
+// test reading past end of file
+fd = fs.openSync('testfile.txt', 'r');
+rbuf = new Buffer(12);
+rlen = fs.readSync(fd, rbuf, 0, 12, 0);
+assert((rlen < rbuf.length), "try reading past end of file");
+assert((rbuf.toString('ascii') == "testwrite"),
+ "read data that existed in r file: " + rbuf.toString('ascii'));
+
+// test write to read only file
+expectThrow("tried writing to read only file", function() {
+ fs.writeSync(fd, new Buffer("dummy"), 0, 5, 0);
+});
+
+fs.closeSync(fd);
+
+// test null position parameter
+fd = fs.openSync('testfile.txt', 'r');
+rbuf = new Buffer(4);
+rlen = fs.readSync(fd, rbuf, 0, 4, null);
+assert((rlen == rbuf.length),
+ "try reading with null position first: " + rbuf.length);
+assert((rbuf.toString('ascii') == "test"),
+ "read data correct with null position first: " + rbuf.toString('ascii'));
+
+rbuf = new Buffer(5);
+rlen = fs.readSync(fd, rbuf, 0, 5, null);
+assert((rlen == rbuf.length),
+ "try reading with null position (again): " + rbuf.length);
+assert((rbuf.toString('ascii') == "write"),
+ "read data correct with null position (again): " + rbuf.toString('ascii'));
+
+rbuf = new Buffer(4);
+rlen = fs.readSync(fd, rbuf, 0, 4, 0);
+assert((rlen == rbuf.length),
+ "try reading with 0 position (after null): " + rbuf.length);
+assert((rbuf.toString('ascii') == "test"),
+ "read data correct with 0 position (after null): " + rbuf.toString('ascii'));
+
+fs.closeSync(fd);
+
+// test write with only buffer
+fd = fs.openSync('testfile.txt', 'w+');
+wbuf = new Buffer("writetest");
+wlen = fs.writeSync(fd, wbuf);
+assert((wlen == wbuf.length), "write with just buffer: " + wlen);
+
+rbuf = new Buffer(9);
+rlen = fs.readSync(fd, rbuf, 0, rbuf.length, 0);
+
+assert((rlen == rbuf.length),
+ "try reading (write with no length): " + rbuf.toString('ascii'));
+assert((rbuf.toString('ascii') == "writetest"),
+ "read data correct (write with no length): " + rbuf.toString('ascii'));
+
+// test write with buffer + offset
+wbuf = new Buffer("_____12345");
+wlen = fs.writeSync(fd, wbuf, 5);
+assert((wlen == 5), "write with just buffer: " + wlen);
+
+rbuf = new Buffer(5);
+rlen = fs.readSync(fd, rbuf, 0, rbuf.length, 0);
+assert((rlen == rbuf.length),
+ "try reading (write with buf/off): " + rbuf.toString('ascii'));
+assert((rbuf.toString('ascii') == "12345"),
+ "read data correct (write with buf/off): " + rbuf.toString('ascii'));
+
+// test write with buffer + offset + length
+wbuf = new Buffer("_____123456789");
+wlen = fs.writeSync(fd, wbuf, 5, 9);
+assert((wlen == 9), "write with just buffer: " + wlen);
+
+rbuf = new Buffer(9);
+rlen = fs.readSync(fd, rbuf, 0, rbuf.length, 0);
+assert((rlen == rbuf.length),
+ "try reading (write with buf/off/len): " + rbuf.toString('ascii'));
+assert((rbuf.toString('ascii') == "123456789"),
+ "read data correct (write with buf/off/len): " + rbuf.toString('ascii'));
+
+// test write with buffer + offset + length + position
+wbuf = new Buffer("_____test");
+wlen = fs.writeSync(fd, wbuf, 5, 4, 5);
+assert((wlen == 4), "write with just buffer: " + wlen);
+
+rbuf = new Buffer(9);
+rlen = fs.readSync(fd, rbuf, 0, rbuf.length, 0);
+assert((rlen == rbuf.length),
+ "try reading (write with buf/off/len/pos): " + rbuf.toString('ascii'));
+assert((rbuf.toString('ascii') == "12345test"),
+ "read data correct (write with buf/off/len/pos): " + rbuf.toString('ascii'));
+
+
+fs.closeSync(fd);
+
+fs.unlinkSync('testfile.txt');
+
+console.log("TOTAL: " + passed + " of " + total + " passed");