| // Copyright Joyent, Inc. and other Node contributors. |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a |
| // copy of this software and associated documentation files (the |
| // "Software"), to deal in the Software without restriction, including |
| // without limitation the rights to use, copy, modify, merge, publish, |
| // distribute, sublicense, and/or sell copies of the Software, and to permit |
| // persons to whom the Software is furnished to do so, subject to the |
| // following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included |
| // in all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
| // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
| // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
| // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
| // USE OR OTHER DEALINGS IN THE SOFTWARE. |
| |
| #include "node.h" |
| #include "node_file.h" |
| #include "node_buffer.h" |
| #include "node_stat_watcher.h" |
| #include "req_wrap.h" |
| |
| #include <fcntl.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <assert.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <limits.h> |
| |
| #if defined(__MINGW32__) || defined(_MSC_VER) |
| # include <io.h> |
| #endif |
| |
| namespace node { |
| |
| using namespace v8; |
| |
| #define MIN(a,b) ((a) < (b) ? (a) : (b)) |
| |
| #define TYPE_ERROR(msg) \ |
| ThrowException(Exception::TypeError(String::New(msg))); |
| |
| #define THROW_BAD_ARGS TYPE_ERROR("Bad argument") |
| |
| class FSReqWrap: public ReqWrap<uv_fs_t> { |
| public: |
| void* operator new(size_t size, char* storage) { return storage; } |
| |
| FSReqWrap(const char* syscall) |
| : syscall_(syscall), |
| dest_len_(0) { |
| } |
| |
| inline const char* syscall() const { return syscall_; } |
| inline const char* dest() const { return dest_; } |
| inline unsigned int dest_len() const { return dest_len_; } |
| inline void dest_len(unsigned int dest_len) { dest_len_ = dest_len; } |
| |
| private: |
| const char* syscall_; |
| unsigned int dest_len_; |
| char dest_[1]; |
| }; |
| |
| |
| static Persistent<String> oncomplete_sym; |
| |
| |
| #define ASSERT_OFFSET(a) \ |
| if (!(a)->IsUndefined() && !(a)->IsNull() && !IsInt64((a)->NumberValue())) { \ |
| return ThrowException(Exception::TypeError(String::New("Not an integer"))); \ |
| } |
| #define ASSERT_TRUNCATE_LENGTH(a) \ |
| if (!(a)->IsUndefined() && !(a)->IsNull() && !IsInt64((a)->NumberValue())) { \ |
| return ThrowException(Exception::TypeError(String::New("Not an integer"))); \ |
| } |
| #define GET_OFFSET(a) ((a)->IsNumber() ? (a)->IntegerValue() : -1) |
| #define GET_TRUNCATE_LENGTH(a) ((a)->IntegerValue()) |
| |
| static inline bool IsInt64(double x) { |
| return x == static_cast<double>(static_cast<int64_t>(x)); |
| } |
| |
| |
| static void After(uv_fs_t *req) { |
| HandleScope scope; |
| |
| FSReqWrap* req_wrap = (FSReqWrap*) req->data; |
| assert(&req_wrap->req_ == req); |
| |
| // there is always at least one argument. "error" |
| int argc = 1; |
| |
| // Allocate space for two args. We may only use one depending on the case. |
| // (Feel free to increase this if you need more) |
| Local<Value> argv[2]; |
| |
| // NOTE: This may be needed to be changed if something returns a -1 |
| // for a success, which is possible. |
| if (req->result == -1) { |
| // If the request doesn't have a path parameter set. |
| |
| if (!req->path) { |
| argv[0] = UVException(req->errorno, |
| NULL, |
| req_wrap->syscall()); |
| } else if ((req->errorno == UV_EEXIST || |
| req->errorno == UV_ENOTEMPTY || |
| req->errorno == UV_EPERM) && |
| req_wrap->dest_len() > 0) { |
| argv[0] = UVException(req->errorno, |
| NULL, |
| req_wrap->syscall(), |
| req_wrap->dest()); |
| } else { |
| argv[0] = UVException(req->errorno, |
| NULL, |
| req_wrap->syscall(), |
| static_cast<const char*>(req->path)); |
| } |
| } else { |
| // error value is empty or null for non-error. |
| argv[0] = Local<Value>::New(Null()); |
| |
| // All have at least two args now. |
| argc = 2; |
| |
| switch (req->fs_type) { |
| // These all have no data to pass. |
| case UV_FS_CLOSE: |
| case UV_FS_RENAME: |
| case UV_FS_UNLINK: |
| case UV_FS_RMDIR: |
| case UV_FS_MKDIR: |
| case UV_FS_FTRUNCATE: |
| case UV_FS_FSYNC: |
| case UV_FS_FDATASYNC: |
| case UV_FS_LINK: |
| case UV_FS_SYMLINK: |
| case UV_FS_CHMOD: |
| case UV_FS_FCHMOD: |
| case UV_FS_CHOWN: |
| case UV_FS_FCHOWN: |
| // These, however, don't. |
| argc = 1; |
| break; |
| |
| case UV_FS_UTIME: |
| case UV_FS_FUTIME: |
| argc = 0; |
| break; |
| |
| case UV_FS_OPEN: |
| argv[1] = Integer::New(req->result); |
| break; |
| |
| case UV_FS_WRITE: |
| argv[1] = Integer::New(req->result); |
| break; |
| |
| case UV_FS_STAT: |
| case UV_FS_LSTAT: |
| case UV_FS_FSTAT: |
| argv[1] = BuildStatsObject(static_cast<const uv_statbuf_t*>(req->ptr)); |
| break; |
| |
| case UV_FS_READLINK: |
| argv[1] = String::New(static_cast<char*>(req->ptr)); |
| break; |
| |
| case UV_FS_READ: |
| // Buffer interface |
| argv[1] = Integer::New(req->result); |
| break; |
| |
| case UV_FS_READDIR: |
| { |
| char *namebuf = static_cast<char*>(req->ptr); |
| int nnames = req->result; |
| |
| Local<Array> names = Array::New(nnames); |
| |
| for (int i = 0; i < nnames; i++) { |
| Local<String> name = String::New(namebuf); |
| names->Set(Integer::New(i), name); |
| #ifndef NDEBUG |
| namebuf += strlen(namebuf); |
| assert(*namebuf == '\0'); |
| namebuf += 1; |
| #else |
| namebuf += strlen(namebuf) + 1; |
| #endif |
| } |
| |
| argv[1] = names; |
| } |
| break; |
| |
| default: |
| assert(0 && "Unhandled eio response"); |
| } |
| } |
| |
| if (oncomplete_sym.IsEmpty()) { |
| oncomplete_sym = NODE_PSYMBOL("oncomplete"); |
| } |
| MakeCallback(req_wrap->object_, oncomplete_sym, argc, argv); |
| |
| uv_fs_req_cleanup(&req_wrap->req_); |
| delete req_wrap; |
| } |
| |
| // This struct is only used on sync fs calls. |
| // For async calls FSReqWrap is used. |
| struct fs_req_wrap { |
| fs_req_wrap() {} |
| ~fs_req_wrap() { uv_fs_req_cleanup(&req); } |
| // Ensure that copy ctor and assignment operator are not used. |
| fs_req_wrap(const fs_req_wrap& req); |
| fs_req_wrap& operator=(const fs_req_wrap& req); |
| uv_fs_t req; |
| }; |
| |
| |
| #define ASYNC_DEST_CALL(func, callback, dest_path, ...) \ |
| FSReqWrap* req_wrap; \ |
| char* dest_str = (dest_path); \ |
| int dest_len = dest_str == NULL ? 0 : strlen(dest_str); \ |
| char* storage = new char[sizeof(*req_wrap) + dest_len]; \ |
| req_wrap = new (storage) FSReqWrap(#func); \ |
| req_wrap->dest_len(dest_len); \ |
| if (dest_str != NULL) { \ |
| memcpy(const_cast<char*>(req_wrap->dest()), \ |
| dest_str, \ |
| dest_len + 1); \ |
| } \ |
| int r = uv_fs_##func(uv_default_loop(), \ |
| &req_wrap->req_, \ |
| __VA_ARGS__, \ |
| After); \ |
| req_wrap->object_->Set(oncomplete_sym, callback); \ |
| req_wrap->Dispatched(); \ |
| if (r < 0) { \ |
| uv_fs_t* req = &req_wrap->req_; \ |
| req->result = r; \ |
| req->path = NULL; \ |
| req->errorno = uv_last_error(uv_default_loop()).code; \ |
| After(req); \ |
| } \ |
| return scope.Close(req_wrap->object_); |
| |
| #define ASYNC_CALL(func, callback, ...) \ |
| ASYNC_DEST_CALL(func, callback, NULL, __VA_ARGS__) \ |
| |
| #define SYNC_DEST_CALL(func, path, dest, ...) \ |
| fs_req_wrap req_wrap; \ |
| int result = uv_fs_##func(uv_default_loop(), \ |
| &req_wrap.req, \ |
| __VA_ARGS__, \ |
| NULL); \ |
| if (result < 0) { \ |
| int code = uv_last_error(uv_default_loop()).code; \ |
| if (dest != NULL && \ |
| (code == UV_EEXIST || \ |
| code == UV_ENOTEMPTY || \ |
| code == UV_EPERM)) { \ |
| return ThrowException(UVException(code, #func, "", dest)); \ |
| } else { \ |
| return ThrowException(UVException(code, #func, "", path)); \ |
| } \ |
| } \ |
| |
| #define SYNC_CALL(func, path, ...) \ |
| SYNC_DEST_CALL(func, path, NULL, __VA_ARGS__) \ |
| |
| #define SYNC_REQ req_wrap.req |
| |
| #define SYNC_RESULT result |
| |
| |
| static Handle<Value> Close(const Arguments& args) { |
| HandleScope scope; |
| |
| if (args.Length() < 1 || !args[0]->IsInt32()) { |
| return THROW_BAD_ARGS; |
| } |
| |
| int fd = args[0]->Int32Value(); |
| |
| if (args[1]->IsFunction()) { |
| ASYNC_CALL(close, args[1], fd) |
| } else { |
| SYNC_CALL(close, 0, fd) |
| return Undefined(); |
| } |
| } |
| |
| |
| static Persistent<FunctionTemplate> stats_constructor_template; |
| |
| static Persistent<String> dev_symbol; |
| static Persistent<String> ino_symbol; |
| static Persistent<String> mode_symbol; |
| static Persistent<String> nlink_symbol; |
| static Persistent<String> uid_symbol; |
| static Persistent<String> gid_symbol; |
| static Persistent<String> rdev_symbol; |
| static Persistent<String> size_symbol; |
| static Persistent<String> blksize_symbol; |
| static Persistent<String> blocks_symbol; |
| static Persistent<String> atime_symbol; |
| static Persistent<String> mtime_symbol; |
| static Persistent<String> ctime_symbol; |
| |
| Local<Object> BuildStatsObject(const uv_statbuf_t* s) { |
| HandleScope scope; |
| |
| if (dev_symbol.IsEmpty()) { |
| dev_symbol = NODE_PSYMBOL("dev"); |
| ino_symbol = NODE_PSYMBOL("ino"); |
| mode_symbol = NODE_PSYMBOL("mode"); |
| nlink_symbol = NODE_PSYMBOL("nlink"); |
| uid_symbol = NODE_PSYMBOL("uid"); |
| gid_symbol = NODE_PSYMBOL("gid"); |
| rdev_symbol = NODE_PSYMBOL("rdev"); |
| size_symbol = NODE_PSYMBOL("size"); |
| blksize_symbol = NODE_PSYMBOL("blksize"); |
| blocks_symbol = NODE_PSYMBOL("blocks"); |
| atime_symbol = NODE_PSYMBOL("atime"); |
| mtime_symbol = NODE_PSYMBOL("mtime"); |
| ctime_symbol = NODE_PSYMBOL("ctime"); |
| } |
| |
| Local<Object> stats = |
| stats_constructor_template->GetFunction()->NewInstance(); |
| |
| if (stats.IsEmpty()) return Local<Object>(); |
| |
| // The code below is very nasty-looking but it prevents a segmentation fault |
| // when people run JS code like the snippet below. It's apparently more |
| // common than you would expect, several people have reported this crash... |
| // |
| // function crash() { |
| // fs.statSync('.'); |
| // crash(); |
| // } |
| // |
| // We need to check the return value of Integer::New() and Date::New() |
| // and make sure that we bail out when V8 returns an empty handle. |
| #define X(name) \ |
| { \ |
| Local<Value> val = Integer::New(s->st_##name); \ |
| if (val.IsEmpty()) return Local<Object>(); \ |
| stats->Set(name##_symbol, val); \ |
| } |
| X(dev) |
| X(mode) |
| X(nlink) |
| X(uid) |
| X(gid) |
| X(rdev) |
| # if defined(__POSIX__) |
| X(blksize) |
| # endif |
| #undef X |
| |
| #define X(name) \ |
| { \ |
| Local<Value> val = Number::New(static_cast<double>(s->st_##name)); \ |
| if (val.IsEmpty()) return Local<Object>(); \ |
| stats->Set(name##_symbol, val); \ |
| } |
| X(ino) |
| X(size) |
| # if defined(__POSIX__) |
| X(blocks) |
| # endif |
| #undef X |
| |
| #define X(name) \ |
| { \ |
| Local<Value> val = NODE_UNIXTIME_V8(s->st_##name); \ |
| if (val.IsEmpty()) return Local<Object>(); \ |
| stats->Set(name##_symbol, val); \ |
| } |
| X(atime) |
| X(mtime) |
| X(ctime) |
| #undef X |
| |
| return scope.Close(stats); |
| } |
| |
| static Handle<Value> Stat(const Arguments& args) { |
| HandleScope scope; |
| |
| if (args.Length() < 1) return TYPE_ERROR("path required"); |
| if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); |
| |
| String::Utf8Value path(args[0]); |
| |
| if (args[1]->IsFunction()) { |
| ASYNC_CALL(stat, args[1], *path) |
| } else { |
| SYNC_CALL(stat, *path, *path) |
| return scope.Close( |
| BuildStatsObject(static_cast<const uv_statbuf_t*>(SYNC_REQ.ptr))); |
| } |
| } |
| |
| static Handle<Value> LStat(const Arguments& args) { |
| HandleScope scope; |
| |
| if (args.Length() < 1) return TYPE_ERROR("path required"); |
| if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); |
| |
| String::Utf8Value path(args[0]); |
| |
| if (args[1]->IsFunction()) { |
| ASYNC_CALL(lstat, args[1], *path) |
| } else { |
| SYNC_CALL(lstat, *path, *path) |
| return scope.Close( |
| BuildStatsObject(static_cast<const uv_statbuf_t*>(SYNC_REQ.ptr))); |
| } |
| } |
| |
| static Handle<Value> FStat(const Arguments& args) { |
| HandleScope scope; |
| |
| if (args.Length() < 1 || !args[0]->IsInt32()) { |
| return THROW_BAD_ARGS; |
| } |
| |
| int fd = args[0]->Int32Value(); |
| |
| if (args[1]->IsFunction()) { |
| ASYNC_CALL(fstat, args[1], fd) |
| } else { |
| SYNC_CALL(fstat, 0, fd) |
| return scope.Close( |
| BuildStatsObject(static_cast<const uv_statbuf_t*>(SYNC_REQ.ptr))); |
| } |
| } |
| |
| static Handle<Value> Symlink(const Arguments& args) { |
| HandleScope scope; |
| |
| int len = args.Length(); |
| if (len < 1) return TYPE_ERROR("dest path required"); |
| if (len < 2) return TYPE_ERROR("src path required"); |
| if (!args[0]->IsString()) return TYPE_ERROR("dest path must be a string"); |
| if (!args[1]->IsString()) return TYPE_ERROR("src path must be a string"); |
| |
| String::Utf8Value dest(args[0]); |
| String::Utf8Value path(args[1]); |
| int flags = 0; |
| |
| if (args[2]->IsString()) { |
| String::Utf8Value mode(args[2]); |
| if (strcmp(*mode, "dir") == 0) { |
| flags |= UV_FS_SYMLINK_DIR; |
| } else if (strcmp(*mode, "junction") == 0) { |
| flags |= UV_FS_SYMLINK_JUNCTION; |
| } else if (strcmp(*mode, "file") != 0) { |
| return ThrowException(Exception::Error( |
| String::New("Unknown symlink type"))); |
| } |
| } |
| |
| if (args[3]->IsFunction()) { |
| ASYNC_DEST_CALL(symlink, args[3], *dest, *dest, *path, flags) |
| } else { |
| SYNC_DEST_CALL(symlink, *path, *dest, *dest, *path, flags) |
| return Undefined(); |
| } |
| } |
| |
| static Handle<Value> Link(const Arguments& args) { |
| HandleScope scope; |
| |
| int len = args.Length(); |
| if (len < 1) return TYPE_ERROR("dest path required"); |
| if (len < 2) return TYPE_ERROR("src path required"); |
| if (!args[0]->IsString()) return TYPE_ERROR("dest path must be a string"); |
| if (!args[1]->IsString()) return TYPE_ERROR("src path must be a string"); |
| |
| String::Utf8Value orig_path(args[0]); |
| String::Utf8Value new_path(args[1]); |
| |
| if (args[2]->IsFunction()) { |
| ASYNC_DEST_CALL(link, args[2], *new_path, *orig_path, *new_path) |
| } else { |
| SYNC_DEST_CALL(link, *orig_path, *new_path, *orig_path, *new_path) |
| return Undefined(); |
| } |
| } |
| |
| static Handle<Value> ReadLink(const Arguments& args) { |
| HandleScope scope; |
| |
| if (args.Length() < 1) return TYPE_ERROR("path required"); |
| if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); |
| |
| String::Utf8Value path(args[0]); |
| |
| if (args[1]->IsFunction()) { |
| ASYNC_CALL(readlink, args[1], *path) |
| } else { |
| SYNC_CALL(readlink, *path, *path) |
| return scope.Close(String::New((char*)SYNC_REQ.ptr)); |
| } |
| } |
| |
| static Handle<Value> Rename(const Arguments& args) { |
| HandleScope scope; |
| |
| int len = args.Length(); |
| if (len < 1) return TYPE_ERROR("old path required"); |
| if (len < 2) return TYPE_ERROR("new path required"); |
| if (!args[0]->IsString()) return TYPE_ERROR("old path must be a string"); |
| if (!args[1]->IsString()) return TYPE_ERROR("new path must be a string"); |
| |
| String::Utf8Value old_path(args[0]); |
| String::Utf8Value new_path(args[1]); |
| |
| if (args[2]->IsFunction()) { |
| ASYNC_DEST_CALL(rename, args[2], *new_path, *old_path, *new_path) |
| } else { |
| SYNC_DEST_CALL(rename, *old_path, *new_path, *old_path, *new_path) |
| return Undefined(); |
| } |
| } |
| |
| static Handle<Value> FTruncate(const Arguments& args) { |
| HandleScope scope; |
| |
| if (args.Length() < 2 || !args[0]->IsInt32()) { |
| return THROW_BAD_ARGS; |
| } |
| |
| int fd = args[0]->Int32Value(); |
| |
| ASSERT_TRUNCATE_LENGTH(args[1]); |
| int64_t len = GET_TRUNCATE_LENGTH(args[1]); |
| |
| if (args[2]->IsFunction()) { |
| ASYNC_CALL(ftruncate, args[2], fd, len) |
| } else { |
| SYNC_CALL(ftruncate, 0, fd, len) |
| return Undefined(); |
| } |
| } |
| |
| static Handle<Value> Fdatasync(const Arguments& args) { |
| HandleScope scope; |
| |
| if (args.Length() < 1 || !args[0]->IsInt32()) { |
| return THROW_BAD_ARGS; |
| } |
| |
| int fd = args[0]->Int32Value(); |
| |
| if (args[1]->IsFunction()) { |
| ASYNC_CALL(fdatasync, args[1], fd) |
| } else { |
| SYNC_CALL(fdatasync, 0, fd) |
| return Undefined(); |
| } |
| } |
| |
| static Handle<Value> Fsync(const Arguments& args) { |
| HandleScope scope; |
| |
| if (args.Length() < 1 || !args[0]->IsInt32()) { |
| return THROW_BAD_ARGS; |
| } |
| |
| int fd = args[0]->Int32Value(); |
| |
| if (args[1]->IsFunction()) { |
| ASYNC_CALL(fsync, args[1], fd) |
| } else { |
| SYNC_CALL(fsync, 0, fd) |
| return Undefined(); |
| } |
| } |
| |
| static Handle<Value> Unlink(const Arguments& args) { |
| HandleScope scope; |
| |
| if (args.Length() < 1) return TYPE_ERROR("path required"); |
| if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); |
| |
| String::Utf8Value path(args[0]); |
| |
| if (args[1]->IsFunction()) { |
| ASYNC_CALL(unlink, args[1], *path) |
| } else { |
| SYNC_CALL(unlink, *path, *path) |
| return Undefined(); |
| } |
| } |
| |
| static Handle<Value> RMDir(const Arguments& args) { |
| HandleScope scope; |
| |
| if (args.Length() < 1) return TYPE_ERROR("path required"); |
| if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); |
| |
| String::Utf8Value path(args[0]); |
| |
| if (args[1]->IsFunction()) { |
| ASYNC_CALL(rmdir, args[1], *path) |
| } else { |
| SYNC_CALL(rmdir, *path, *path) |
| return Undefined(); |
| } |
| } |
| |
| static Handle<Value> MKDir(const Arguments& args) { |
| HandleScope scope; |
| |
| if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsInt32()) { |
| return THROW_BAD_ARGS; |
| } |
| |
| String::Utf8Value path(args[0]); |
| int mode = static_cast<int>(args[1]->Int32Value()); |
| |
| if (args[2]->IsFunction()) { |
| ASYNC_CALL(mkdir, args[2], *path, mode) |
| } else { |
| SYNC_CALL(mkdir, *path, *path, mode) |
| return Undefined(); |
| } |
| } |
| |
| static Handle<Value> ReadDir(const Arguments& args) { |
| HandleScope scope; |
| |
| if (args.Length() < 1) return TYPE_ERROR("path required"); |
| if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); |
| |
| String::Utf8Value path(args[0]); |
| |
| if (args[1]->IsFunction()) { |
| ASYNC_CALL(readdir, args[1], *path, 0 /*flags*/) |
| } else { |
| SYNC_CALL(readdir, *path, *path, 0 /*flags*/) |
| |
| char *namebuf = static_cast<char*>(SYNC_REQ.ptr); |
| int nnames = req_wrap.req.result; |
| Local<Array> names = Array::New(nnames); |
| |
| for (int i = 0; i < nnames; i++) { |
| Local<String> name = String::New(namebuf); |
| names->Set(Integer::New(i), name); |
| #ifndef NDEBUG |
| namebuf += strlen(namebuf); |
| assert(*namebuf == '\0'); |
| namebuf += 1; |
| #else |
| namebuf += strlen(namebuf) + 1; |
| #endif |
| } |
| |
| return scope.Close(names); |
| } |
| } |
| |
| static Handle<Value> Open(const Arguments& args) { |
| HandleScope scope; |
| |
| int len = args.Length(); |
| if (len < 1) return TYPE_ERROR("path required"); |
| if (len < 2) return TYPE_ERROR("flags required"); |
| if (len < 3) return TYPE_ERROR("mode required"); |
| if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); |
| if (!args[1]->IsInt32()) return TYPE_ERROR("flags must be an int"); |
| if (!args[2]->IsInt32()) return TYPE_ERROR("mode must be an int"); |
| |
| String::Utf8Value path(args[0]); |
| int flags = args[1]->Int32Value(); |
| int mode = static_cast<int>(args[2]->Int32Value()); |
| |
| if (args[3]->IsFunction()) { |
| ASYNC_CALL(open, args[3], *path, flags, mode) |
| } else { |
| SYNC_CALL(open, *path, *path, flags, mode) |
| int fd = SYNC_RESULT; |
| return scope.Close(Integer::New(fd)); |
| } |
| } |
| |
| // bytesWritten = write(fd, data, position, enc, callback) |
| // Wrapper for write(2). |
| // |
| // 0 fd integer. file descriptor |
| // 1 buffer the data to write |
| // 2 offset where in the buffer to start from |
| // 3 length how much to write |
| // 4 position if integer, position to write at in the file. |
| // if null, write from the current position |
| static Handle<Value> Write(const Arguments& args) { |
| HandleScope scope; |
| |
| if (!args[0]->IsInt32()) { |
| return THROW_BAD_ARGS; |
| } |
| |
| int fd = args[0]->Int32Value(); |
| |
| if (!Buffer::HasInstance(args[1])) { |
| return ThrowException(Exception::Error( |
| String::New("Second argument needs to be a buffer"))); |
| } |
| |
| Local<Object> buffer_obj = args[1]->ToObject(); |
| char *buffer_data = Buffer::Data(buffer_obj); |
| size_t buffer_length = Buffer::Length(buffer_obj); |
| |
| size_t off = args[2]->Int32Value(); |
| if (off >= buffer_length) { |
| return ThrowException(Exception::Error( |
| String::New("Offset is out of bounds"))); |
| } |
| |
| ssize_t len = args[3]->Int32Value(); |
| if (off + len > buffer_length) { |
| return ThrowException(Exception::Error( |
| String::New("off + len > buffer.length"))); |
| } |
| |
| ASSERT_OFFSET(args[4]); |
| int64_t pos = GET_OFFSET(args[4]); |
| |
| char * buf = (char*)buffer_data + off; |
| Local<Value> cb = args[5]; |
| |
| if (cb->IsFunction()) { |
| ASYNC_CALL(write, cb, fd, buf, len, pos) |
| } else { |
| SYNC_CALL(write, 0, fd, buf, len, pos) |
| return scope.Close(Integer::New(SYNC_RESULT)); |
| } |
| } |
| |
| /* |
| * Wrapper for read(2). |
| * |
| * bytesRead = fs.read(fd, buffer, offset, length, position) |
| * |
| * 0 fd integer. file descriptor |
| * 1 buffer instance of Buffer |
| * 2 offset integer. offset to start reading into inside buffer |
| * 3 length integer. length to read |
| * 4 position file position - null for current position |
| * |
| */ |
| static Handle<Value> Read(const Arguments& args) { |
| HandleScope scope; |
| |
| if (args.Length() < 2 || !args[0]->IsInt32()) { |
| return THROW_BAD_ARGS; |
| } |
| |
| int fd = args[0]->Int32Value(); |
| |
| Local<Value> cb; |
| |
| size_t len; |
| int64_t pos; |
| |
| char * buf = NULL; |
| |
| if (!Buffer::HasInstance(args[1])) { |
| return ThrowException(Exception::Error( |
| String::New("Second argument needs to be a buffer"))); |
| } |
| |
| Local<Object> buffer_obj = args[1]->ToObject(); |
| char *buffer_data = Buffer::Data(buffer_obj); |
| size_t buffer_length = Buffer::Length(buffer_obj); |
| |
| size_t off = args[2]->Int32Value(); |
| if (off >= buffer_length) { |
| return ThrowException(Exception::Error( |
| String::New("Offset is out of bounds"))); |
| } |
| |
| len = args[3]->Int32Value(); |
| if (off + len > buffer_length) { |
| return ThrowException(Exception::Error( |
| String::New("Length extends beyond buffer"))); |
| } |
| |
| pos = GET_OFFSET(args[4]); |
| |
| buf = buffer_data + off; |
| |
| cb = args[5]; |
| |
| if (cb->IsFunction()) { |
| ASYNC_CALL(read, cb, fd, buf, len, pos); |
| } else { |
| SYNC_CALL(read, 0, fd, buf, len, pos) |
| Local<Integer> bytesRead = Integer::New(SYNC_RESULT); |
| return scope.Close(bytesRead); |
| } |
| } |
| |
| |
| /* fs.chmod(path, mode); |
| * Wrapper for chmod(1) / EIO_CHMOD |
| */ |
| static Handle<Value> Chmod(const Arguments& args) { |
| HandleScope scope; |
| |
| if(args.Length() < 2 || !args[0]->IsString() || !args[1]->IsInt32()) { |
| return THROW_BAD_ARGS; |
| } |
| String::Utf8Value path(args[0]); |
| int mode = static_cast<int>(args[1]->Int32Value()); |
| |
| if(args[2]->IsFunction()) { |
| ASYNC_CALL(chmod, args[2], *path, mode); |
| } else { |
| SYNC_CALL(chmod, *path, *path, mode); |
| return Undefined(); |
| } |
| } |
| |
| |
| /* fs.fchmod(fd, mode); |
| * Wrapper for fchmod(1) / EIO_FCHMOD |
| */ |
| static Handle<Value> FChmod(const Arguments& args) { |
| HandleScope scope; |
| |
| if(args.Length() < 2 || !args[0]->IsInt32() || !args[1]->IsInt32()) { |
| return THROW_BAD_ARGS; |
| } |
| int fd = args[0]->Int32Value(); |
| int mode = static_cast<int>(args[1]->Int32Value()); |
| |
| if(args[2]->IsFunction()) { |
| ASYNC_CALL(fchmod, args[2], fd, mode); |
| } else { |
| SYNC_CALL(fchmod, 0, fd, mode); |
| return Undefined(); |
| } |
| } |
| |
| |
| /* fs.chown(path, uid, gid); |
| * Wrapper for chown(1) / EIO_CHOWN |
| */ |
| static Handle<Value> Chown(const Arguments& args) { |
| HandleScope scope; |
| |
| int len = args.Length(); |
| if (len < 1) return TYPE_ERROR("path required"); |
| if (len < 2) return TYPE_ERROR("uid required"); |
| if (len < 3) return TYPE_ERROR("gid required"); |
| if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); |
| if (!args[1]->IsUint32()) return TYPE_ERROR("uid must be an unsigned int"); |
| if (!args[2]->IsUint32()) return TYPE_ERROR("gid must be an unsigned int"); |
| |
| String::Utf8Value path(args[0]); |
| uv_uid_t uid = static_cast<uv_uid_t>(args[1]->Uint32Value()); |
| uv_gid_t gid = static_cast<uv_gid_t>(args[2]->Uint32Value()); |
| |
| if (args[3]->IsFunction()) { |
| ASYNC_CALL(chown, args[3], *path, uid, gid); |
| } else { |
| SYNC_CALL(chown, *path, *path, uid, gid); |
| return Undefined(); |
| } |
| } |
| |
| |
| /* fs.fchown(fd, uid, gid); |
| * Wrapper for fchown(1) / EIO_FCHOWN |
| */ |
| static Handle<Value> FChown(const Arguments& args) { |
| HandleScope scope; |
| |
| int len = args.Length(); |
| if (len < 1) return TYPE_ERROR("fd required"); |
| if (len < 2) return TYPE_ERROR("uid required"); |
| if (len < 3) return TYPE_ERROR("gid required"); |
| if (!args[0]->IsInt32()) return TYPE_ERROR("fd must be an int"); |
| if (!args[1]->IsUint32()) return TYPE_ERROR("uid must be an unsigned int"); |
| if (!args[2]->IsUint32()) return TYPE_ERROR("gid must be an unsigned int"); |
| |
| int fd = args[0]->Int32Value(); |
| uv_uid_t uid = static_cast<uv_uid_t>(args[1]->Uint32Value()); |
| uv_gid_t gid = static_cast<uv_gid_t>(args[2]->Uint32Value()); |
| |
| if (args[3]->IsFunction()) { |
| ASYNC_CALL(fchown, args[3], fd, uid, gid); |
| } else { |
| SYNC_CALL(fchown, 0, fd, uid, gid); |
| return Undefined(); |
| } |
| } |
| |
| |
| static Handle<Value> UTimes(const Arguments& args) { |
| HandleScope scope; |
| |
| int len = args.Length(); |
| if (len < 1) return TYPE_ERROR("path required"); |
| if (len < 2) return TYPE_ERROR("atime required"); |
| if (len < 3) return TYPE_ERROR("mtime required"); |
| if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); |
| if (!args[1]->IsNumber()) return TYPE_ERROR("atime must be a number"); |
| if (!args[2]->IsNumber()) return TYPE_ERROR("mtime must be a number"); |
| |
| const String::Utf8Value path(args[0]); |
| const double atime = static_cast<double>(args[1]->NumberValue()); |
| const double mtime = static_cast<double>(args[2]->NumberValue()); |
| |
| if (args[3]->IsFunction()) { |
| ASYNC_CALL(utime, args[3], *path, atime, mtime); |
| } else { |
| SYNC_CALL(utime, *path, *path, atime, mtime); |
| return Undefined(); |
| } |
| } |
| |
| static Handle<Value> FUTimes(const Arguments& args) { |
| HandleScope scope; |
| |
| int len = args.Length(); |
| if (len < 1) return TYPE_ERROR("fd required"); |
| if (len < 2) return TYPE_ERROR("atime required"); |
| if (len < 3) return TYPE_ERROR("mtime required"); |
| if (!args[0]->IsInt32()) return TYPE_ERROR("fd must be an int"); |
| if (!args[1]->IsNumber()) return TYPE_ERROR("atime must be a number"); |
| if (!args[2]->IsNumber()) return TYPE_ERROR("mtime must be a number"); |
| |
| const int fd = args[0]->Int32Value(); |
| const double atime = static_cast<double>(args[1]->NumberValue()); |
| const double mtime = static_cast<double>(args[2]->NumberValue()); |
| |
| if (args[3]->IsFunction()) { |
| ASYNC_CALL(futime, args[3], fd, atime, mtime); |
| } else { |
| SYNC_CALL(futime, 0, fd, atime, mtime); |
| return Undefined(); |
| } |
| } |
| |
| |
| void File::Initialize(Handle<Object> target) { |
| HandleScope scope; |
| |
| NODE_SET_METHOD(target, "close", Close); |
| NODE_SET_METHOD(target, "open", Open); |
| NODE_SET_METHOD(target, "read", Read); |
| NODE_SET_METHOD(target, "fdatasync", Fdatasync); |
| NODE_SET_METHOD(target, "fsync", Fsync); |
| NODE_SET_METHOD(target, "rename", Rename); |
| NODE_SET_METHOD(target, "ftruncate", FTruncate); |
| NODE_SET_METHOD(target, "rmdir", RMDir); |
| NODE_SET_METHOD(target, "mkdir", MKDir); |
| NODE_SET_METHOD(target, "readdir", ReadDir); |
| NODE_SET_METHOD(target, "stat", Stat); |
| NODE_SET_METHOD(target, "lstat", LStat); |
| NODE_SET_METHOD(target, "fstat", FStat); |
| NODE_SET_METHOD(target, "link", Link); |
| NODE_SET_METHOD(target, "symlink", Symlink); |
| NODE_SET_METHOD(target, "readlink", ReadLink); |
| NODE_SET_METHOD(target, "unlink", Unlink); |
| NODE_SET_METHOD(target, "write", Write); |
| |
| NODE_SET_METHOD(target, "chmod", Chmod); |
| NODE_SET_METHOD(target, "fchmod", FChmod); |
| //NODE_SET_METHOD(target, "lchmod", LChmod); |
| |
| NODE_SET_METHOD(target, "chown", Chown); |
| NODE_SET_METHOD(target, "fchown", FChown); |
| //NODE_SET_METHOD(target, "lchown", LChown); |
| |
| NODE_SET_METHOD(target, "utimes", UTimes); |
| NODE_SET_METHOD(target, "futimes", FUTimes); |
| } |
| |
| void InitFs(Handle<Object> target) { |
| HandleScope scope; |
| // Initialize the stats object |
| Local<FunctionTemplate> stat_templ = FunctionTemplate::New(); |
| stats_constructor_template = Persistent<FunctionTemplate>::New(stat_templ); |
| target->Set(String::NewSymbol("Stats"), |
| stats_constructor_template->GetFunction()); |
| File::Initialize(target); |
| |
| oncomplete_sym = NODE_PSYMBOL("oncomplete"); |
| |
| StatWatcher::Initialize(target); |
| } |
| |
| } // end namespace node |
| |
| NODE_MODULE(node_fs, node::InitFs) |