#include "node.h" #include "file.h" #include "events.h" #include #include #include #include #include #include #include using namespace v8; using namespace node; #define DEV_SYMBOL String::NewSymbol("dev") #define INO_SYMBOL String::NewSymbol("ino") #define MODE_SYMBOL String::NewSymbol("mode") #define NLINK_SYMBOL String::NewSymbol("nlink") #define UID_SYMBOL String::NewSymbol("uid") #define GID_SYMBOL String::NewSymbol("gid") #define RDEV_SYMBOL String::NewSymbol("rdev") #define SIZE_SYMBOL String::NewSymbol("size") #define BLKSIZE_SYMBOL String::NewSymbol("blksize") #define BLOCKS_SYMBOL String::NewSymbol("blocks") #define ATIME_SYMBOL String::NewSymbol("atime") #define MTIME_SYMBOL String::NewSymbol("mtime") #define CTIME_SYMBOL String::NewSymbol("ctime") #define BAD_ARGUMENTS String::New("Bad argument") static int AfterClose (eio_req *req) { EIOPromise *promise = reinterpret_cast(req->data); if (req->result == 0) { promise->EmitSuccess(0, NULL); } else { promise->EmitError(0, NULL); } return 0; } static Handle Close (const Arguments& args) { if (args.Length() < 1 || !args[0]->IsInt32()) return ThrowException(BAD_ARGUMENTS); HandleScope scope; int fd = args[0]->Int32Value(); EIOPromise *promise = EIOPromise::Create(); eio_close(fd, EIO_PRI_DEFAULT, AfterClose, promise); return scope.Close(promise->Handle()); } static int AfterRename (eio_req *req) { EIOPromise *promise = reinterpret_cast(req->data); if (req->result == 0) { promise->EmitSuccess(0, NULL); } else { promise->EmitError(0, NULL); } return 0; } static Handle Rename (const Arguments& args) { if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString()) return ThrowException(BAD_ARGUMENTS); HandleScope scope; String::Utf8Value path(args[0]->ToString()); String::Utf8Value new_path(args[1]->ToString()); EIOPromise *promise = EIOPromise::Create(); eio_rename(*path, *new_path, EIO_PRI_DEFAULT, AfterRename, promise); return scope.Close(promise->Handle()); } static int AfterUnlink (eio_req *req) { EIOPromise *promise = reinterpret_cast(req->data); if (req->result == 0) { promise->EmitSuccess(0, NULL); } else { promise->EmitError(0, NULL); } return 0; } static Handle Unlink (const Arguments& args) { if (args.Length() < 1 || !args[0]->IsString()) return ThrowException(BAD_ARGUMENTS); HandleScope scope; String::Utf8Value path(args[0]->ToString()); EIOPromise *promise = EIOPromise::Create(); eio_unlink(*path, EIO_PRI_DEFAULT, AfterUnlink, promise); return scope.Close(promise->Handle()); } static int AfterRMDir (eio_req *req) { EIOPromise *promise = reinterpret_cast(req->data); if (req->result == 0) { promise->EmitSuccess(0, NULL); } else { promise->EmitError(0, NULL); } return 0; } static Handle RMDir (const Arguments& args) { if (args.Length() < 1 || !args[0]->IsString()) return ThrowException(BAD_ARGUMENTS); HandleScope scope; String::Utf8Value path(args[0]->ToString()); EIOPromise *promise = EIOPromise::Create(); eio_rmdir(*path, EIO_PRI_DEFAULT, AfterRMDir, promise); return scope.Close(promise->Handle()); } static int AfterOpen (eio_req *req) { EIOPromise *promise = reinterpret_cast(req->data); if (req->result < 0) { promise->EmitError(0, NULL); return 0; } HandleScope scope; Local argv[1] = { Integer::New(req->result) }; promise->EmitSuccess(1, argv); return 0; } static Handle Open (const Arguments& args) { if ( args.Length() < 3 || !args[0]->IsString() || !args[1]->IsInt32() || !args[2]->IsInt32() ) return ThrowException(BAD_ARGUMENTS); HandleScope scope; String::Utf8Value path(args[0]->ToString()); int flags = args[1]->Int32Value(); mode_t mode = static_cast(args[2]->Int32Value()); EIOPromise *promise = EIOPromise::Create(); eio_open(*path, flags, mode, EIO_PRI_DEFAULT, AfterOpen, promise); return scope.Close(promise->Handle()); } static int AfterWrite (eio_req *req) { EIOPromise *promise = reinterpret_cast(req->data); if (req->result < 0) { promise->EmitError(0, NULL); return 0; } HandleScope scope; free(req->ptr2); ssize_t written = req->result; Local argv[1]; argv[0] = written >= 0 ? Integer::New(written) : Integer::New(0); promise->EmitSuccess(1, argv); return 0; } /* node.fs.write(fd, data, position, callback) * Wrapper for write(2). * * 0 fd integer. file descriptor * 1 data the data to write (string = utf8, array = raw) * 2 position if integer, position to write at in the file. * if null, write from the current position * * 3 callback(errorno, written) */ static Handle Write (const Arguments& args) { if ( args.Length() < 3 || !args[0]->IsInt32() ) return ThrowException(BAD_ARGUMENTS); HandleScope scope; int fd = args[0]->Int32Value(); off_t pos = args[2]->IsNumber() ? args[2]->IntegerValue() : -1; char *buf = NULL; size_t len = 0; if (args[1]->IsString()) { // utf8 encoding Local string = args[1]->ToString(); len = string->Utf8Length(); buf = reinterpret_cast(malloc(len)); string->WriteUtf8(buf, len); } else if (args[1]->IsArray()) { // raw encoding Local array = Local::Cast(args[1]); len = array->Length(); buf = reinterpret_cast(malloc(len)); for (unsigned int i = 0; i < len; i++) { Local int_value = array->Get(Integer::New(i)); buf[i] = int_value->Int32Value(); } } else { return ThrowException(BAD_ARGUMENTS); } EIOPromise *promise = EIOPromise::Create(); eio_write(fd, buf, len, pos, EIO_PRI_DEFAULT, AfterWrite, promise); return scope.Close(promise->Handle()); } static int AfterUtf8Read (eio_req *req) { EIOPromise *promise = reinterpret_cast(req->data); if (req->result < 0) { promise->EmitError(0, NULL); return 0; } HandleScope scope; Local argv[2]; if (req->result == 0) { // eof argv[0] = Local::New(Null()); argv[1] = Integer::New(0); } else { char *buf = reinterpret_cast(req->ptr2); argv[0] = String::New(buf, req->result); argv[1] = Integer::New(req->result); } promise->EmitSuccess(2, argv); return 0; } static int AfterRawRead(eio_req *req) { EIOPromise *promise = reinterpret_cast(req->data); if (req->result < 0) { promise->EmitError(0, NULL); return 0; } HandleScope scope; Local argv[2]; if (req->result == 0) { argv[0] = Local::New(Null()); argv[1] = Integer::New(0); } else { char *buf = reinterpret_cast(req->ptr2); size_t len = req->result; Local array = Array::New(len); for (unsigned int i = 0; i < len; i++) { unsigned char val = reinterpret_cast(buf)[i]; array->Set(Integer::New(i), Integer::New(val)); } argv[0] = array; argv[1] = Integer::New(req->result); } promise->EmitSuccess(2, argv); return 0; } /* node.fs.read(fd, length, position, encoding, callback) * Wrapper for read(2). * * 0 fd integer. file descriptor * 1 length integer. length to read * 2 position if integer, position to read from in the file. * if null, read from the current position * 3 encoding either node.fs.UTF8 or node.fs.RAW * * 4 callback(errorno, data) */ static Handle Read (const Arguments& args) { if ( args.Length() < 2 || !args[0]->IsInt32() // fd || !args[1]->IsNumber() // len ) return ThrowException(BAD_ARGUMENTS); HandleScope scope; int fd = args[0]->Int32Value(); size_t len = args[1]->IntegerValue(); off_t pos = args[2]->IsNumber() ? args[2]->IntegerValue() : -1; enum encoding encoding = RAW; if (args[3]->IsInt32()) { encoding = static_cast(args[3]->Int32Value()); } EIOPromise *promise = EIOPromise::Create(); // NOTE: 2nd param: NULL pointer tells eio to allocate it itself eio_read(fd, NULL, len, pos, EIO_PRI_DEFAULT, encoding == UTF8 ? AfterUtf8Read : AfterRawRead, promise); return scope.Close(promise->Handle()); } static int AfterStat (eio_req *req) { EIOPromise *promise = reinterpret_cast(req->data); if (req->result < 0) { promise->EmitError(0, NULL); return 0; } HandleScope scope; Local stats = Object::New(); struct stat *s = reinterpret_cast(req->ptr2); /* ID of device containing file */ stats->Set(DEV_SYMBOL, Integer::New(s->st_dev)); /* inode number */ stats->Set(INO_SYMBOL, Integer::New(s->st_ino)); /* protection */ stats->Set(MODE_SYMBOL, Integer::New(s->st_mode)); /* number of hard links */ stats->Set(NLINK_SYMBOL, Integer::New(s->st_nlink)); /* user ID of owner */ stats->Set(UID_SYMBOL, Integer::New(s->st_uid)); /* group ID of owner */ stats->Set(GID_SYMBOL, Integer::New(s->st_gid)); /* device ID (if special file) */ stats->Set(RDEV_SYMBOL, Integer::New(s->st_rdev)); /* total size, in bytes */ stats->Set(SIZE_SYMBOL, Integer::New(s->st_size)); /* blocksize for filesystem I/O */ stats->Set(BLKSIZE_SYMBOL, Integer::New(s->st_blksize)); /* number of blocks allocated */ stats->Set(BLOCKS_SYMBOL, Integer::New(s->st_blocks)); /* time of last access */ stats->Set(ATIME_SYMBOL, Date::New(1000*static_cast(s->st_atime))); /* time of last modification */ stats->Set(MTIME_SYMBOL, Date::New(1000*static_cast(s->st_mtime))); /* time of last status change */ stats->Set(CTIME_SYMBOL, Date::New(1000*static_cast(s->st_ctime))); Local argv[1] = { stats }; promise->EmitSuccess(1, argv); return 0; } static Handle Stat (const Arguments& args) { if (args.Length() < 1 || !args[0]->IsString()) return ThrowException(BAD_ARGUMENTS); HandleScope scope; String::Utf8Value path(args[0]->ToString()); EIOPromise *promise = EIOPromise::Create(); eio_stat(*path, EIO_PRI_DEFAULT, AfterStat, promise); return scope.Close(promise->Handle()); } static Handle StrError (const Arguments& args) { if (args.Length() < 1) return v8::Undefined(); if (!args[0]->IsNumber()) return v8::Undefined(); HandleScope scope; int errorno = args[0]->IntegerValue(); Local message = String::New(strerror(errorno)); return scope.Close(message); } void File::Initialize (Handle target) { HandleScope scope; // POSIX Wrappers NODE_SET_METHOD(target, "close", Close); NODE_SET_METHOD(target, "open", Open); NODE_SET_METHOD(target, "read", Read); NODE_SET_METHOD(target, "rename", Rename); NODE_SET_METHOD(target, "rmdir", RMDir); NODE_SET_METHOD(target, "stat", Stat); NODE_SET_METHOD(target, "unlink", Unlink); NODE_SET_METHOD(target, "write", Write); NODE_SET_METHOD(target, "strerror", StrError); }