From e8dbdfdefeb00e626119f9e2dcbd1f7c6c126dfe Mon Sep 17 00:00:00 2001 From: Ferdinand Prantl Date: Thu, 2 Jan 2014 13:24:20 +0100 Subject: [PATCH 01/33] Add stat, lstat, fstat, chown and fchown working with SIDs on Windows Add native methods getown and fgetown getting a file ownership and chown and fchown changing a file ownership. Add the exposed methods to the JavaScript file-ext.js. Add tests for stat and chown methods. --- README.md | 66 +++- autores.cc | 12 + autores.h | 459 +++++++++++++++++++++++++++ binding.gyp | 5 +- fs-ext.cc | 6 + fs-ext.js | 237 +++++++++++++- fs-win.cc | 688 +++++++++++++++++++++++++++++++++++++++++ fs-win.h | 17 + run_tests | 5 + tests/test-fs-chown.js | 51 +++ tests/test-fs-stat.js | 57 ++++ winwrap.cc | 48 +++ winwrap.h | 14 + 13 files changed, 1660 insertions(+), 5 deletions(-) create mode 100644 autores.cc create mode 100644 autores.h create mode 100644 fs-win.cc create mode 100644 fs-win.h create mode 100644 tests/test-fs-chown.js create mode 100644 tests/test-fs-stat.js create mode 100644 winwrap.cc create mode 100644 winwrap.h diff --git a/README.md b/README.md index 3e8736c..ced46f0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ fs-ext ====== -Extras not included in Node's fs module. +Extras not included in Node's fs module +and cross-platform file ownership handling. Installation ------------ @@ -71,5 +72,68 @@ Just like for utime(2), the absence of the `atime` and `mtime` means 'now'. Synchronous version of utime(). Throws an exception on error. +### fs.stat(path, [callback]) +Replaces the `fs.stat` returning the string representation of +[SIDs](http://msdn.microsoft.com/en-us/library/windows/desktop/aa379594.aspx) +of the owning user and group in the `uid` and `gid` attributes of the output +`stats` object. +### fs.statSync(path) + +Replaces the `fs.statSync` returning the string representation of +[SIDs](http://msdn.microsoft.com/en-us/library/windows/desktop/aa379594.aspx) +of the owning user and group in the `uid` and `gid` attributes of the output +`stats` object. + + +### fs.lstat(path, [callback]) + +Replaces the `fs.lstat` returning the string representation of +[SIDs](http://msdn.microsoft.com/en-us/library/windows/desktop/aa379594.aspx) +of the owning user and group in the `uid` and `gid` attributes of the output +`stats` object. + +### fs.lstatSync(path) + +Replaces the `fs.lstatSync` returning the string representation of +[SIDs](http://msdn.microsoft.com/en-us/library/windows/desktop/aa379594.aspx) +of the owning user and group in the `uid` and `gid` attributes of the output +`stats` object. + + +### fs.fstat(fd, [callback]) + +Replaces the `fs.fstat` returning the string representation of +[SIDs](http://msdn.microsoft.com/en-us/library/windows/desktop/aa379594.aspx) +of the owning user and group in the `uid` and `gid` attributes of the output +`stats` object. + +### fs.fstatSync(fd) + +Replaces the `fs.fstatSync` returning the string representation of +[SIDs](http://msdn.microsoft.com/en-us/library/windows/desktop/aa379594.aspx) +of the owning user and group in the `uid` and `gid` attributes of the output +`stats` object. + + +### fs.chown(path, [callback]) + +Replaces the `fs.chown` expecting `uid` and `gid` as the string representation +of [SIDs](http://msdn.microsoft.com/en-us/library/windows/desktop/aa379594.aspx). + +### fs.chownSync(path) + +Replaces the `fs.chownSync` expecting `uid` and `gid` as the string representation +of [SIDs](http://msdn.microsoft.com/en-us/library/windows/desktop/aa379594.aspx). + + +### fs.fchown(fd, [callback]) + +Replaces the `fs.fchown` expecting `uid` and `gid` as the string representation +of [SIDs](http://msdn.microsoft.com/en-us/library/windows/desktop/aa379594.aspx). + +### fs.fchownSync(fd) + +Replaces the `fs.fchownSync` expecting `uid` and `gid` as the string representation +of [SIDs](http://msdn.microsoft.com/en-us/library/windows/desktop/aa379594.aspx). diff --git a/autores.cc b/autores.cc new file mode 100644 index 0000000..e05ebb6 --- /dev/null +++ b/autores.cc @@ -0,0 +1,12 @@ +#ifdef _WIN32 + +#include "autores.h" + +namespace autores { + +// the process heap will be initialized on the first usage +HANDLE HeapBase::processHeap = NULL; + +} // namespace autores + +#endif // _WIN32 diff --git a/autores.h b/autores.h new file mode 100644 index 0000000..a2c9db6 --- /dev/null +++ b/autores.h @@ -0,0 +1,459 @@ +#ifndef AUTORES_H +#define AUTORES_H + +#ifdef _WIN32 +#include +#endif + +#include +#include +#include + +namespace autores { + +// abstract class for wrappers of resources which need to be freed; +// the destructor disposes of the wrapped resource autmatically +// +// descendant class template: +// template class ManagedResource : +// public ManagedResource, T> { ... }; +// variable declaration: +// ManagedResource resource = OpenResource(...); +template < + typename Derived, + typename T + > +class AutoRes { + protected: + T handle; + + public: + // the default constructor; creates an empty wrapper + AutoRes() : handle(Derived::InitialValue()) {} + + // initializing constructor; this object will own the handle + AutoRes(T handle) : handle(handle) {} + + // ownership moving constructor; the source object will become empty + // and this object will own its handle + AutoRes(AutoRes & source) : handle(source.Detach()) {} + + // the destructor disposes of the wrapped handle, if it is valid + ~AutoRes() { + static_cast(this)->Dispose(); + } + + // ownership moving assignment operator + Derived & operator =(Derived & source) { + // dispose of the owned handle before hosting the new one + static_cast(this)->Dispose(); + // read the handle from the source object and leave it empty + // so that its destructor doesn't dispose of it when owned here + handle = source.Detach(); + return static_cast(*this); + } + + Derived & operator =(T source) { + // dispose of the owned handle before hosting the new one + static_cast(this)->Dispose(); + handle = source; + return static_cast(*this); + } + + // getting a pointer to the wrapper returns a pointer to the + // the wrapped value to be able to ue it as output parameter + // as well as the wrapped value alone would be + T * operator &() { + return &handle; + } + + // the wrapper instance can be used as input parameter as well + // as the wrapped value alone would be, thanks to this cast + operator T() const { + return handle; + } + + // gets the wrapped handle in statements where the casting + // operator is not practicable + T Get() const { + return handle; + } + + // returns the wrapped handle and removes it from the wrapper; so that + // when the wrapper is disposed of, the handle will stay intact + T Detach() { + T result = handle; + // the wrapper is left with an invalid value + handle = Derived::InitialValue(); + return result; + } + + // disposes of the wrapped handle, if the handle is valid + bool Dispose() { + // proceed only if the wrapped handle is valid + if (static_cast(this)->IsValid()) { + // delegate the disposal to the descended class; the DisposeInternal + // should be protected and AutoRes should be friend to the descendant + static_cast(this)->DisposeInternal(); + // leave the wrapper with an invalid value to allow multiple calls + // to this metod without failures + handle = Derived::InitialValue(); + } + return true; + } + + // checks whether a valid handle is stored in the wrapper; more values + // can be invalid, but at least one must be the InitialValue + bool IsValid() const { + return Derived::IsValidValue(handle); + } + + // checks whether the specified handle is valid + static bool IsValidValue(T handle) { + return handle != Derived::InitialValue(); + } + + // returns an invalid handle value stored in an empty wrapper + static T InitialValue() { + return NULL; + } +}; + +// abstract class for wrappers of allocated memory blocks +// +// descendant class template: +// template class ManagedMemory : +// public AutoMem, T> { ... }; +// variable declaration: +// ManagedMemory item = AllocateAndInitializeItem(...); +template < + typename Derived, + typename T + > +class AutoMem : public AutoRes< + Derived, T + > { + private: + typedef AutoRes< + Derived, T + > Base; + + protected: + typedef Base Res; + + bool DisposeInternal() { + return Derived::Unallocate(Base::handle); + } + + T & Dereference() { + // the value in the dereferenced memory is being accessed; + // it must not be posible if the wrapper is empty + assert(static_cast(this)->IsValid()); + return Base::handle; + } + + T const & Dereference() const { + // the value in the dereferenced memory is being accessed; + // it must not be posible if the wrapper is empty + assert(static_cast(this)->IsValid()); + return Base::handle; + } + + public: + AutoMem() {} + + AutoMem(T handle) : Base(handle) {} + + // ownership moving constructor + AutoMem(AutoMem & source) : Base(source) {} + + Derived & operator =(Derived & source) { + return Base::operator =(source); + } + + Derived & operator =(T source) { + return Base::operator =(source); + } + + // chain the dereferencing operator to make the members of the + // wrapped value accessible via the wrapper instance the same way + T operator ->() { + return Dereference(); + } +}; + +// wraps a pointer to an object allocated by new and disposed by delete +// +// variable declaration: +// CppObj item = new Item(...); +template < + typename T + > +class CppObj : public AutoMem< + CppObj, T + > { + private: + typedef AutoMem< + CppObj, T + > Base; + + friend class Base::Res; + + public: + CppObj() {} + + CppObj(T handle) : Base(handle) {} + + CppObj(CppObj & source) : Base(source) {} + + // ownership moving assignment operator + CppObj & operator =(CppObj & source) { + Base::operator =(source); + return *this; + } + + CppObj & operator =(T source) { + Base::operator =(source); + return *this; + } + + static bool Unallocate(T handle) { + delete handle; + return true; + } +}; + +#ifdef _WIN32 +// wraps a handle to a kernel object which is disposed by CloseHandle +// +// variable declaration: +// WinHandle file = CreateFile(...); +template < + typename T + > +class WinHandle : public AutoRes< + WinHandle, T + > { + private: + typedef AutoRes< + WinHandle, T + > Base; + + friend class Base; + + protected: + bool DisposeInternal() { + return CloseHandle(Base::handle) != FALSE; + } + + public: + WinHandle() {} + + WinHandle(T handle) : Base(handle) {} + + WinHandle(WinHandle & source) : Base(source) {} + + // ownership moving assignment operator + WinHandle & operator =(WinHandle & source) { + Base::operator =(source); + return *this; + } + + WinHandle & operator =(T source) { + Base::operator =(source); + return *this; + } + + static bool IsValidValue(T handle) { + return Base::IsValidValue(handle) && handle != INVALID_HANDLE_VALUE; + } +}; + +// wraps a pointer to memory allocated by LocalAlloc and disposed by LocalFree +// +// variable declaration: +// LocalMem memory = LocalAlloc(LMEM_FIXED, size); +template < + typename T + > +class LocalMem : public AutoMem< + LocalMem, T + > { + private: + typedef AutoMem< + LocalMem, T + > Base; + + friend class Base::Res; + + public: + LocalMem() {} + + LocalMem(T handle) : Base(handle) {} + + LocalMem(LocalMem & source) : Base(source) {} + + // ownership moving assignment operator + LocalMem & operator =(LocalMem & source) { + Base::operator =(source); + return *this; + } + + LocalMem & operator =(T source) { + Base::operator =(source); + return *this; + } + + static T Allocate(size_t size) { + return (T) LocalAlloc(LMEM_FIXED, size); + } + + static bool Unallocate(T handle) { + return LocalFree(handle) == NULL; + } +}; + +// wraps a pointer to memory allocated by GlobalAlloc and disposed by GlobalFree +// +// variable declaration: +// GlobalMem memory = GlobAlloc(GMEM_FIXED, size); +template < + typename T + > +class GlobalMem : public AutoMem< + GlobalMem, T + > { + private: + typedef AutoMem< + GlobalMem, T + > Base; + + friend class Base::Res; + + public: + GlobalMem() {} + + GlobalMem(T handle) : Base(handle) {} + + GlobalMem(GlobalMem & source) : Base(source) {} + + // ownership moving assignment operator + GlobalMem & operator =(GlobalMem & source) { + Base::operator =(source); + return *this; + } + + GlobalMem & operator =(T source) { + Base::operator =(source); + return *this; + } + + static T Allocate(size_t size) { + return (T) GlobalAlloc(GMEM_FIXED, size); + } + + static bool Unallocate(T handle) { + return GlobalFree(handle) == NULL; + } +}; + +// base class storing the heap which the memory block was allocated from; +// the descended class can set it or rely on the process heap by default +class HeapBase { + private: + static HANDLE processHeap; + + protected: + mutable HANDLE heap; + + public: + HeapBase() : heap(NULL) {} + + HeapBase(HANDLE heap) : heap(heap) {} + + HeapBase(HeapBase const & source) : heap(source.heap) {} + + HANDLE Heap() const { + if (heap == NULL) { + heap = ProcessHeap(); + } + return heap; + } + + static HANDLE ProcessHeap() { + if (processHeap == NULL) { + processHeap = GetProcessHeap(); + } + return processHeap; + } +}; + +// wraps a pointer to memory allocated by HeapAlloc and disposed by HeapFree +// +// variable declaration: +// HeapMem memory = HeapAlloc(GetProcessHeap(), 0, size); +template < + typename T + > +class HeapMem : public AutoMem< + HeapMem, T + >, + public HeapBase { + private: + typedef AutoMem< + HeapMem, T + > Base; + + friend class Base::Res; + + protected: + bool DisposeInternal() { + return Unallocate(Base::handle, HeapBase::Heap()); + } + + public: + HeapMem() {} + + HeapMem(T handle) : Base(handle) {} + + HeapMem(T handle, HANDLE heap) : Base(handle), HeapBase(heap) {} + + HeapMem(HeapMem & source) : Base(source), HeapBase(source.heap) {} + + // ownership moving assignment operator + HeapMem & operator =(HeapMem & source) { + Base::operator =(source); + heap = source.heap; + return *this; + } + + HeapMem & operator =(T source) { + Base::operator =(source); + return *this; + } + + HeapMem & Assign(T source, HANDLE heap = NULL) { + Base::operator =(source); + heap = heap; + return *this; + } + + static T Allocate(size_t size, HANDLE heap = NULL) { + if (heap == NULL) { + heap = HeapBase::ProcessHeap(); + } + return (T) HeapAlloc(heap, 0, size); + } + + static bool Unallocate(T handle, HANDLE heap = NULL) { + if (heap == NULL) { + heap = HeapBase::ProcessHeap(); + } + return HeapFree(heap, 0, handle) != FALSE; + } +}; +#endif // _WIN32 + +} // namespace autores + +#endif // AUTORES_H diff --git a/binding.gyp b/binding.gyp index be1f9c4..a7f3050 100644 --- a/binding.gyp +++ b/binding.gyp @@ -3,7 +3,10 @@ { "target_name": "fs-ext", "sources": [ - "fs-ext.cc" + "fs-ext.cc", + "fs-win.cc", + "autores.cc", + "winwrap.cc" ] } ] diff --git a/fs-ext.cc b/fs-ext.cc index d9e730a..f61e2cd 100644 --- a/fs-ext.cc +++ b/fs-ext.cc @@ -38,6 +38,8 @@ #include #endif +#include "fs-win.h" + using namespace v8; using namespace node; @@ -497,6 +499,10 @@ init (Handle target) f_favail_symbol = NODE_PSYMBOL("f_favail"); f_ffree_symbol = NODE_PSYMBOL("f_ffree"); #endif + +#ifdef _WIN32 + fs_win::init(target); +#endif } #if NODE_MODULE_VERSION > 1 diff --git a/fs-ext.js b/fs-ext.js index ae800a8..375b20b 100644 --- a/fs-ext.js +++ b/fs-ext.js @@ -112,11 +112,242 @@ exports.statVFS = function(path, callback) { return binding.statVFS(path, callback); }; -// populate with fs functions from there -for (var key in fs) { - exports[key] = fs[key]; +// merges all members from the source object to the target object; +// it's like the underscore.extend +function merge(target, source) { + var key; + for (key in source) { + if (source.hasOwnProperty(key)) { + target[key] = source[key]; + } + } } +// stat and chown function group is reimplemented to work +// with SIDs for Windows only +var fsExt = process.platform.match(/^win/i) ? + (function () { + + // the result of fs.readlink is the exact string used when the link + // was created by `ln -s`, which can be a relative path; however, + // the path was relative to the current directory when the command + // was executed; not to the path of the link; if it wasn't so, this + // method will resolve to an invalid path and that's wahy you should + // always create links using the absolute target path + function resolveLink(fpath, lpath) { + // check if the path is absolute on both Windows and POSIX platforms + if (/^([a-z]:)?[\/\\]/i.test(lpath)) { + return lpath; + } + return path.join(path.dirname(fpath), lpath); + } + + var path = require("path"), + + // declare the extra methods for the built-in fs module + // which provide the POSIX functionality on Windows + fsExt = (function () { + + // merges the ownership to the stats + function completeStats(stats, fd, callback) { + // allow calling with both fd and path + (typeof fd === "string" ? binding.getown : + binding.fgetown)(fd, function(error, ownership) { + if (error) { + callback(error); + } else { + // replace the uid and gid members in the original stats + // with the values containing SIDs + merge(stats, ownership); + callback(undefined, stats); + } + }); + } + + // merges the ownership to the stats + function completeStatsSync(stats, fd) { + // allow calling with both fd and path + var ownership = (typeof fd === "string" ? + binding.getown : binding.fgetown)(fd); + // replace the uid and gid members in the original stats + // with the values containing SIDs + merge(stats, ownership); + return stats; + } + + return { + + // fs.fstat returning uid and gid as SIDs + fstat: function(fd, callback) { + // get the built-in stats which work on Windows too + fs.fstat(fd, function(error, stats) { + if (error) { + callback(error); + } else { + // replace the ownership information (uid and gid) + // with the data useful on Windows - principal SIDs + completeStats(stats, fd, callback); + } + }); + }, + + // fs.fstatSync returning uid and gid as SIDs + fstatSync: function(fd) { + // get the built-in stats which work on Windows too + var stats = fs.fstatSync(fd); + // replace the ownership information (uid and gid) + // with the data useful on Windows - principal SIDs + return completeStatsSync(stats, fd); + }, + + // fs.stat returning uid and gid as SIDs + stat: function(fpath, callback) { + // get the built-in stats which work on Windows too + fs.lstat(fpath, function(error, stats) { + if (error) { + callback(error); + } else { + // GetNamedSecurityInfo, which is used by binding.getown, + // doesn't resolve sybolic links automatically; do the + // resolution here and call the lstat implementation + if (stats.isSymbolicLink()) { + fs.readlink(fpath, function(error, lpath) { + if (error) { + callback(error); + } else { + fpath = resolveLink(fpath, lpath); + fsExt.lstat(fpath, callback); + } + }); + } else { + // replace the ownership information (uid and gid) + // with the data useful on Windows - principal SIDs + completeStats(stats, fpath, callback); + } + } + }); + }, + + // fs.statSync returning uid and gid as SIDs + statSync: function(fpath) { + // get the built-in stats which work on Windows too + // GetNamedSecurityInfo, which is used by binding.getown, + // doesn't resolve sybolic links automatically; do the + // resolution here and call the lstat implementation + var stats = fs.lstatSync(fpath); + if (stats.isSymbolicLink()) { + var lpath = fs.readlinkSync(fpath); + fpath = resolveLink(fpath, lpath); + return fsExt.lstatSync(fpath); + } + // replace the ownership information (uid and gid) + // with the data useful on Windows - principal SIDs + return completeStatsSync(stats, fpath); + }, + + // fs.lstat returning uid and gid as SIDs + lstat: function(fpath, callback) { + // get the built-in stats which work on Windows too + fs.lstat(fpath, function(error, stats) { + if (error) { + callback(error); + } else { + // replace the ownership information (uid and gid) + // with the data useful on Windows - principal SIDs + completeStats(stats, fpath, callback); + } + }); + }, + + // fs.lstatSync returning uid and gid as SIDs + lstatSync: function(fpath) { + // get the built-in stats which work on Windows too + // GetNamedSecurityInfo, which is used by binding.getown, + // doesn't resolve sybolic links automatically; it's + // suitable for the lstat implementation as-is + var stats = fs.lstatSync(fpath); + // replace the ownership information (uid and gid) + // with the data useful on Windows - principal SIDs + return completeStatsSync(stats, fpath); + }, + + // fs.fchown accepting uid and gid as SIDs + fchown: function(fd, uid, gid, callback) { + binding.fchown(fd, uid, gid, function(error) { + callback(error); + }); + }, + + // fs.fchownSync accepting uid and gid as SIDs + fchownSync: function(fd, uid, gid) { + binding.fchown(fd, uid, gid); + }, + + // fs.chown accepting uid and gid as SIDs + chown: function(fpath, uid, gid, callback) { + fs.lstat(fpath, function(error, stats) { + if (error) { + callback(error); + } else { + if (stats.isSymbolicLink()) { + fs.readlink(fpath, function(error, lpath) { + if (error) { + callback(error); + } else { + fpath = resolveLink(fpath, lpath); + fsExt.lchown(fpath, uid, gid, callback); + } + }); + } else { + fsExt.lchown(fpath, uid, gid, callback); + } + } + }); + }, + + // fs.chownSync accepting uid and gid as SIDs + chownSync: function(fpath, uid, gid) { + // SetNamedSecurityInfo, which is used by binding.chown, + // doesn't resolve sybolic links automatically; do the + // resolution here and call the lchown implementation + var stats = fs.lstatSync(fpath); + if (stats.isSymbolicLink()) { + var lpath = fs.readlinkSync(fpath); + fpath = resolveLink(fpath, lpath); + } + fsExt.lchownSync(fpath, uid, gid); + }, + + // fs.lchown accepting uid and gid as SIDs + lchown: function(fpath, uid, gid, callback) { + binding.chown(fpath, uid, gid, function(error) { + callback(error); + }); + }, + + // fs.lchownSync accepting uid and gid as SIDs + lchownSync: function(fpath, uid, gid) { + // SetNamedSecurityInfo, which is used by binding.chown, + // doesn't resolve sybolic links automatically; it's + // suitable for the lchown implementation as-is + binding.chown(fpath, uid, gid); + } + + }; + + }()); + + }()) + : + // the implementation doesn't need the native add-on on POSIX + {}; + +// populate with fs functions from there +merge(exports, fs); + +// replace the functoins enhanced on Windows +merge(exports, fsExt); + // put constants into constants module (don't like doing this but...) for (var key in binding) { if (/^[A-Z_]+$/.test(key) && !constants.hasOwnProperty(key)) { diff --git a/fs-win.cc b/fs-win.cc new file mode 100644 index 0000000..0a5f94a --- /dev/null +++ b/fs-win.cc @@ -0,0 +1,688 @@ +#ifdef _WIN32 + +#include "fs-win.h" +#include "autores.h" +#include "winwrap.h" + +#include +#include +#include +#include + +// methods: +// fgetown, getown, +// fchown, chown +// +// method implementation pattern: +// +// register method_impl as exports.method +// method_impl { +// if sync: call method_sync, return convert_result +// if async: queue method_async with after_async +// } +// method_async { +// call method_sync +// } +// after_async { +// for OPERATION_METHOD: return convert_result to callback +// } + +namespace fs_win { + +using namespace node; +using namespace v8; +using namespace autores; + +// helpers for returning errors from native methods +#define THROW_TYPE_ERROR(msg) \ + ThrowException(Exception::TypeError(String::New(msg))) +#define LAST_WINAPI_ERROR \ + ((int) GetLastError()) +#define THROW_WINAPI_ERROR(err) \ + ThrowException(WinapiErrnoException(err)) +#define THROW_LAST_WINAPI_ERROR \ + THROW_WINAPI_ERROR(LAST_WINAPI_ERROR) + +// members names of result object literals +static Persistent uid_symbol; +static Persistent gid_symbol; + +// ------------------------------------------------ +// internal functions to support the native exports + +// class helper to enable and disable taking object ownership in this +// process; it's used explicitly by calling Enable and Disable to be +// able to check for errors, but it supports RAII too for error cases +class TakingOwhership { + private: + WinHandle process; + bool enabled; + + // changes the privileges necessary for taking ownership + // in the current process - either enabling or disabling it + BOOL SetPrivileges(BOOL enable) { + LPCTSTR const names[] = { + SE_TAKE_OWNERSHIP_NAME, SE_SECURITY_NAME, + SE_BACKUP_NAME, SE_RESTORE_NAME + }; + + HeapMem privileges = + HeapMem::Allocate(FIELD_OFFSET( + TOKEN_PRIVILEGES, Privileges[sizeof(names) / sizeof(names[0])])); + if (privileges == NULL) { + return FALSE; + } + privileges->PrivilegeCount = sizeof(names) / sizeof(names[0]); + for (size_t i = 0; i < privileges->PrivilegeCount; ++i) { + if (LookupPrivilegeValue(NULL, names[i], + &privileges->Privileges[i].Luid) == FALSE) { + return FALSE; + } + privileges->Privileges[i].Attributes = + enable != FALSE ? SE_PRIVILEGE_ENABLED : 0; + } + + if (AdjustTokenPrivileges(process, FALSE, privileges, + sizeof(privileges), NULL, NULL) == FALSE) { + return FALSE; + } + if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { + SetLastError(ERROR_NOT_ALL_ASSIGNED); + return FALSE; + } + + return TRUE; + } + + public: + TakingOwhership() : enabled(false) {} + + ~TakingOwhership() { + Disable(); + } + + DWORD Enable() { + if (OpenProcessToken(GetCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES, &process) == FALSE) { + return LAST_WINAPI_ERROR; + } + if (SetPrivileges(TRUE) == FALSE) { + return LAST_WINAPI_ERROR; + } + enabled = true; + return ERROR_SUCCESS; + } + + DWORD Disable() { + if (enabled) { + if (SetPrivileges(FALSE) == FALSE) { + return LAST_WINAPI_ERROR; + } + if (!process.Dispose()) { + return LAST_WINAPI_ERROR; + } + enabled = false; + } + return ERROR_SUCCESS; + } +}; + +// ----------------------------------------- +// support for asynchronous method execution + +// codes of exposed native methods +typedef enum { + OPERATION_FGETOWN, + OPERATION_GETOWN, + OPERATION_FCHOWN, + OPERATION_CHOWN +} operation_t; + +// passes input/output parameters between the native method entry point +// and the worker method doing the work, which is called asynchronously +struct async_data_t { + uv_work_t request; + Persistent callback; + DWORD error; + + operation_t operation; + int fd; + HeapMem path; + LocalMem susid, sgsid; + + async_data_t(Local lcallback) { + if (!lcallback.IsEmpty()) { + callback = Persistent::New(lcallback); + } + request.data = this; + } + + ~async_data_t() { + if (!callback.IsEmpty()) { + callback.Dispose(); + } + } +}; + +// makes a JavaScript result object literal of user and group SIDs +static Local convert_ownership(LPSTR uid, LPSTR gid) { + Local result = Object::New(); + if (!result.IsEmpty()) { + result->Set(uid_symbol, String::New(uid)); + result->Set(gid_symbol, String::New(gid)); + } + return result; +} + +// called after an asynchronously called method (method_sync) has +// finished to convert the results to JavaScript objects and pass +// them to JavaScript callback +static void after_async(uv_work_t * req) { + assert(req != NULL); + HandleScope scope; + + Local argv[2]; + int argc = 1; + + async_data_t * async_data = static_cast(req->data); + if (async_data->error != ERROR_SUCCESS) { + argv[0] = WinapiErrnoException(async_data->error); + } else { + // in case of success, make the first argument (error) null + argv[0] = Local::New(Null()); + // in case of success, populate the second and other arguments + switch (async_data->operation) { + case OPERATION_FGETOWN: + case OPERATION_GETOWN: { + argv[1] = convert_ownership( + async_data->susid, async_data->sgsid); + argc = 2; + break; + } + case OPERATION_FCHOWN: + case OPERATION_CHOWN: + break; + default: + assert(FALSE && "Unknown operation"); + } + } + + // pass the results to the external callback + TryCatch tryCatch; + async_data->callback->Call(Context::GetCurrent()->Global(), argc, argv); + if (tryCatch.HasCaught()) { + FatalException(tryCatch); + } + + async_data->callback.Dispose(); + delete async_data; +} + +// ------------------------------------------------------- +// fgetown - gets the file or directory ownership as SIDs: +// { uid, gid } fgetown( fd, [callback] ) + +static int fgetown_sync(int fd, LPSTR *uid, LPSTR *gid) { + assert(uid != NULL); + assert(gid != NULL); + + HANDLE fh = (HANDLE) _get_osfhandle(fd); + + PSID usid = NULL, gsid = NULL; + LocalMem sd; + DWORD error = GetSecurityInfo(fh, SE_FILE_OBJECT, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, + &usid, &gsid, NULL, NULL, &sd); + if (error != ERROR_SUCCESS) { + return error; + } + + LocalMem susid; + if (ConvertSidToStringSid(usid, &susid) == FALSE) { + return LAST_WINAPI_ERROR; + } + + LocalMem sgsid; + if (ConvertSidToStringSid(gsid, &sgsid) == FALSE) { + return LAST_WINAPI_ERROR; + } + + *uid = susid.Detach(); + *gid = sgsid.Detach(); + + return ERROR_SUCCESS; +} + +// passes the execution to fgetown_sync; the results will be processed +// by after_async +static void fgetown_async(uv_work_t * req) { + assert(req != NULL); + async_data_t * async_data = static_cast(req->data); + async_data->error = fgetown_sync(async_data->fd, + &async_data->susid, &async_data->sgsid); +} + +// the native entry point for the exposed fgetown function +static Handle fgetown_impl(Arguments const & args) { + HandleScope scope; + + int argc = args.Length(); + if (argc < 1) + return THROW_TYPE_ERROR("fd required"); + if (argc > 2) + return THROW_TYPE_ERROR("too many arguments"); + if (!args[0]->IsInt32()) + return THROW_TYPE_ERROR("fd must be an int"); + if (argc > 1 && !args[1]->IsFunction()) + return THROW_TYPE_ERROR("callback must be a function"); + + int fd = args[0]->Int32Value(); + + // if no callback was provided, assume the synchronous scenario, + // call the method_sync immediately and return its results + if (!args[1]->IsFunction()) { + LocalMem susid, sgsid; + DWORD error = fgetown_sync(fd, &susid, &sgsid); + if (error != ERROR_SUCCESS) { + return THROW_WINAPI_ERROR(error); + } + Local result = convert_ownership(susid, sgsid); + return scope.Close(result); + } + + // prepare parameters for the method_sync to be called later + // from the method_async called from the worker thread + CppObj async_data = new async_data_t( + Local::Cast(args[1])); + async_data->operation = OPERATION_FGETOWN; + async_data->fd = fd; + + // queue the method_async to be called when posibble and + // after_async to send its result to the external callback + uv_queue_work(uv_default_loop(), &async_data->request, + fgetown_async, (uv_after_work_cb) after_async); + + async_data.Detach(); + return Undefined(); +} + +// ------------------------------------------------------ +// getown - gets the file or directory ownership as SIDs: +// { uid, gid } getown( path, [callback] ) + +// gets the file ownership (uid and gid) for the file path +static int getown_sync(LPCSTR path, LPSTR *uid, LPSTR *gid) { + assert(path != NULL); + assert(uid != NULL); + assert(gid != NULL); + + PSID usid = NULL, gsid = NULL; + LocalMem sd; + DWORD error = GetNamedSecurityInfo(path, SE_FILE_OBJECT, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, + &usid, &gsid, NULL, NULL, &sd); + if (error != ERROR_SUCCESS) { + return error; + } + + LocalMem susid; + if (ConvertSidToStringSid(usid, &susid) == FALSE) { + return LAST_WINAPI_ERROR; + } + + LocalMem sgsid; + if (ConvertSidToStringSid(gsid, &sgsid) == FALSE) { + return LAST_WINAPI_ERROR; + } + + *uid = susid.Detach(); + *gid = sgsid.Detach(); + + return ERROR_SUCCESS; +} + +// passes the execution to getown_sync; the results will be processed +// by after_async +static void getown_async(uv_work_t * req) { + assert(req != NULL); + async_data_t * async_data = static_cast(req->data); + async_data->error = getown_sync(async_data->path, + &async_data->susid, &async_data->sgsid); +} + +// the native entry point for the exposed getown function +static Handle getown_impl(Arguments const & args) { + HandleScope scope; + + int argc = args.Length(); + if (argc < 1) + return THROW_TYPE_ERROR("path required"); + if (argc > 2) + return THROW_TYPE_ERROR("too many arguments"); + if (!args[0]->IsString()) + return THROW_TYPE_ERROR("path must be a string"); + if (argc > 1 && !args[1]->IsFunction()) + return THROW_TYPE_ERROR("callback must be a function"); + + String::Utf8Value path(args[0]->ToString()); + + // if no callback was provided, assume the synchronous scenario, + // call the method_sync immediately and return its results + if (!args[1]->IsFunction()) { + LocalMem susid, sgsid; + DWORD error = getown_sync(*path, &susid, &sgsid); + if (error != ERROR_SUCCESS) { + return THROW_WINAPI_ERROR(error); + } + Local result = convert_ownership(susid, sgsid); + return scope.Close(result); + } + + // prepare parameters for the method_sync to be called later + // from the method_async called from the worker thread + CppObj async_data = new async_data_t( + Local::Cast(args[1])); + async_data->operation = OPERATION_GETOWN; + async_data->path = HeapStrDup(HeapBase::ProcessHeap(), *path); + if (!async_data->path.IsValid()) { + return THROW_LAST_WINAPI_ERROR; + } + + // queue the method_async to be called when posibble and + // after_async to send its result to the external callback + uv_queue_work(uv_default_loop(), &async_data->request, + getown_async, (uv_after_work_cb) after_async); + + async_data.Detach(); + return Undefined(); +} + +// -------------------------------------------------------- +// fchown - sets the file or directory ownership with SIDs: +// fchown( fd, uid, gid, [callback] ) + +// change the ownership (uid and gid) of the file specified by the +// file descriptor; either uid or gid can be empty ("") to change +// just one of them +static int fchown_sync(int fd, LPCSTR uid, LPCSTR gid) { + assert(uid != NULL); + assert(gid != NULL); + + // get the OS file handle for the specified file descriptor + HANDLE fh = (HANDLE) _get_osfhandle(fd); + + // convert the input SIDs from strings to SID structures + LocalMem usid; + if (*uid && ConvertStringSidToSid(uid, &usid) == FALSE) { + return LAST_WINAPI_ERROR; + } + LocalMem gsid; + if (*gid && ConvertStringSidToSid(gid, &gsid) == FALSE) { + return LAST_WINAPI_ERROR; + } + + // enable taking object ownership in the current process + // if the effective user has enough permissions + TakingOwhership takingOwhership; + DWORD error = takingOwhership.Enable(); + if (error != ERROR_SUCCESS) { + return error; + } + + // take ownership of the object specified by the file handle + if (*uid && *gid) { + if (SetSecurityInfo(fh, SE_FILE_OBJECT, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, + usid, gsid, NULL, NULL) != ERROR_SUCCESS) { + return LAST_WINAPI_ERROR; + } + } else if (*uid) { + if (SetSecurityInfo(fh, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, + usid, NULL, NULL, NULL) != ERROR_SUCCESS) { + return LAST_WINAPI_ERROR; + } + } else if (*gid) { + if (SetSecurityInfo(fh, SE_FILE_OBJECT, GROUP_SECURITY_INFORMATION, + NULL, gsid, NULL, NULL) != ERROR_SUCCESS) { + return LAST_WINAPI_ERROR; + } + } + + // disnable taking object ownership in the current process + // not to leak the availability of this privileged operation + error = takingOwhership.Disable(); + if (error != ERROR_SUCCESS) { + return error; + } + + return ERROR_SUCCESS; +} + +// passes the execution to fchown_sync; the results will be processed +// by after_async +static void fchown_async(uv_work_t * req) { + assert(req != NULL); + async_data_t * async_data = static_cast(req->data); + async_data->error = fchown_sync(async_data->fd, + async_data->susid, async_data->sgsid); +} + +// the native entry point for the exposed fchown function +static Handle fchown_impl(Arguments const & args) { + HandleScope scope; + + int argc = args.Length(); + if (argc < 1) + return THROW_TYPE_ERROR("fd required"); + if (argc > 4) + return THROW_TYPE_ERROR("too many arguments"); + if (!args[0]->IsInt32()) + return THROW_TYPE_ERROR("fd must be an int"); + if (argc < 2) + return THROW_TYPE_ERROR("uid required"); + if (!args[1]->IsString() && !args[1]->IsUndefined()) + return THROW_TYPE_ERROR("uid must be a string or undefined"); + if (argc < 3) + return THROW_TYPE_ERROR("gid required"); + if (!args[2]->IsString() && !args[2]->IsUndefined()) + return THROW_TYPE_ERROR("gid must be a string or undefined"); + if (argc > 3 && !args[3]->IsFunction()) + return THROW_TYPE_ERROR("callback must be a function"); + if (args[1]->IsUndefined() && args[2]->IsUndefined()) + return THROW_TYPE_ERROR("either uid or gid must be defined"); + + int fd = args[0]->Int32Value(); + String::AsciiValue susid(args[1]->IsString() ? + args[1]->ToString() : String::New("")); + String::AsciiValue sgsid(args[2]->IsString() ? + args[2]->ToString() : String::New("")); + + // if no callback was provided, assume the synchronous scenario, + // call the method_sync immediately and return its results + if (!args[3]->IsFunction()) { + DWORD error = fchown_sync(fd, *susid, *sgsid); + if (error != ERROR_SUCCESS) { + return THROW_WINAPI_ERROR(error); + } + return Undefined(); + } + + // prepare parameters for the method_sync to be called later + // from the method_async called from the worker thread + CppObj async_data = new async_data_t( + Local::Cast(args[3])); + async_data->operation = OPERATION_FCHOWN; + async_data->fd = fd; + async_data->susid = LocalStrDup(*sgsid); + if (!async_data->susid.IsValid()) { + return THROW_LAST_WINAPI_ERROR; + } + async_data->sgsid = LocalStrDup(*susid); + if (!async_data->sgsid.IsValid()) { + return THROW_LAST_WINAPI_ERROR; + } + + // queue the method_async to be called when posibble and + // after_async to send its result to the external callback + uv_queue_work(uv_default_loop(), &async_data->request, + fchown_async, (uv_after_work_cb) after_async); + + async_data.Detach(); + return Undefined(); +} + +// ------------------------------------------------------- +// chown - sets the file or directory ownership with SIDs: +// chown( name, uid, gid, [callback] ) + +// change the ownership (uid and gid) of the file specified by the +// file path; either uid or gid can be empty ("") to change +// just one of them +static int chown_sync(LPCSTR path, LPCSTR uid, LPCSTR gid) { + assert(path != NULL); + assert(uid != NULL); + assert(gid != NULL); + + // convert the input SIDs from strings to SID structures + LocalMem usid; + if (*uid && ConvertStringSidToSid(uid, &usid) == FALSE) { + return LAST_WINAPI_ERROR; + } + LocalMem gsid; + if (*gid && ConvertStringSidToSid(gid, &gsid) == FALSE) { + return LAST_WINAPI_ERROR; + } + + // enable taking object ownership in the current process + // if the effective user has enough permissions + TakingOwhership takingOwhership; + DWORD error = takingOwhership.Enable(); + if (error != ERROR_SUCCESS) { + return error; + } + + // take ownership of the object specified by its path + if (*uid && *gid) { + if (SetNamedSecurityInfo(const_cast(path), SE_FILE_OBJECT, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, + usid, gsid, NULL, NULL) != ERROR_SUCCESS) { + return LAST_WINAPI_ERROR; + } + } else if (*uid) { + if (SetNamedSecurityInfo(const_cast(path), + SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, + usid, gsid, NULL, NULL) != ERROR_SUCCESS) { + return LAST_WINAPI_ERROR; + } + } else if (*gid) { + if (SetNamedSecurityInfo(const_cast(path), + SE_FILE_OBJECT, GROUP_SECURITY_INFORMATION, + NULL, gsid, NULL, NULL) != ERROR_SUCCESS) { + return LAST_WINAPI_ERROR; + } + } + + // disnable taking object ownership in the current process + // not to leak the availability of this privileged operation + error = takingOwhership.Disable(); + if (error != ERROR_SUCCESS) { + return error; + } + + return ERROR_SUCCESS; +} + +// passes the execution to chown_sync; the results will be processed +// by after_async +static void chown_async(uv_work_t * req) { + assert(req != NULL); + async_data_t * async_data = static_cast(req->data); + async_data->error = chown_sync(async_data->path, + async_data->susid, async_data->sgsid); +} + +// the native entry point for the exposed chown function +static Handle chown_impl(Arguments const & args) { + HandleScope scope; + + int argc = args.Length(); + if (argc < 1) + return THROW_TYPE_ERROR("path required"); + if (argc > 4) + return THROW_TYPE_ERROR("too many arguments"); + if (!args[0]->IsString()) + return THROW_TYPE_ERROR("path must be a string"); + if (argc < 2) + return THROW_TYPE_ERROR("uid required"); + if (!args[1]->IsString() && !args[1]->IsUndefined()) + return THROW_TYPE_ERROR("uid must be a string or undefined"); + if (argc < 3) + return THROW_TYPE_ERROR("gid required"); + if (!args[2]->IsString() && !args[2]->IsUndefined()) + return THROW_TYPE_ERROR("gid must be a string or undefined"); + if (argc > 3 && !args[3]->IsFunction()) + return THROW_TYPE_ERROR("callback must be a function"); + if (args[1]->IsUndefined() && args[2]->IsUndefined()) + return THROW_TYPE_ERROR("either uid or gid must be defined"); + + String::Utf8Value path(args[0]->ToString()); + String::AsciiValue susid(args[1]->IsString() ? + args[1]->ToString() : String::New("")); + String::AsciiValue sgsid(args[2]->IsString() ? + args[2]->ToString() : String::New("")); + + // if no callback was provided, assume the synchronous scenario, + // call the method_sync immediately and return its results + if (!args[3]->IsFunction()) { + DWORD error = chown_sync(*path, *susid, *sgsid); + if (error != ERROR_SUCCESS) { + return THROW_WINAPI_ERROR(error); + } + return Undefined(); + } + + // prepare parameters for the method_sync to be called later + // from the method_async called from the worker thread + CppObj async_data = new async_data_t( + Local::Cast(args[3])); + async_data->operation = OPERATION_CHOWN; + async_data->path = HeapStrDup(HeapBase::ProcessHeap(), *path); + if (!async_data->path.IsValid()) { + return THROW_LAST_WINAPI_ERROR; + } + async_data->susid = LocalStrDup(*sgsid); + if (!async_data->susid.IsValid()) { + return THROW_LAST_WINAPI_ERROR; + } + async_data->sgsid = LocalStrDup(*susid); + if (!async_data->sgsid.IsValid()) { + return THROW_LAST_WINAPI_ERROR; + } + + // queue the method_async to be called when posibble and + // after_async to send its result to the external callback + uv_queue_work(uv_default_loop(), &async_data->request, + chown_async, (uv_after_work_cb) after_async); + + async_data.Detach(); + return Undefined(); +} + +// exposes methods implemented by this sub-package and initializes the +// string symbols for the converted resulting object literals; to be +// called from the add-on module-initializing function +void init(Handle target) { + HandleScope scope; + + NODE_SET_METHOD(target, "fgetown", fgetown_impl); + NODE_SET_METHOD(target, "getown", getown_impl); + NODE_SET_METHOD(target, "fchown", fchown_impl); + NODE_SET_METHOD(target, "chown", chown_impl); + + uid_symbol = NODE_PSYMBOL("uid"); + gid_symbol = NODE_PSYMBOL("gid"); +} + +} // namespace fs_win + +#endif // _WIN32 diff --git a/fs-win.h b/fs-win.h new file mode 100644 index 0000000..f40a4c0 --- /dev/null +++ b/fs-win.h @@ -0,0 +1,17 @@ +#ifndef FS_WIN_H +#define FS_WIN_H + +#include +#include + +namespace fs_win { + +using namespace node; +using namespace v8; + +// to be called during the node add-on initialization +void init(Handle target); + +} // namespace fs_win + +#endif // FS_WIN_H diff --git a/run_tests b/run_tests index 72b1c8e..3f6a1dc 100755 --- a/run_tests +++ b/run_tests @@ -6,6 +6,11 @@ node tests/test-fs-flock.js node tests/test-fs-utime.js +node tests/test-fs-stat.js + +# this test must be run as root +#sudo node tests/test-fs-chown.js + # for stress testing only # node tests/test-fs-seek_stress.js # node tests/test-fs-flock_stress.js diff --git a/tests/test-fs-chown.js b/tests/test-fs-chown.js new file mode 100644 index 0000000..a0b0bc9 --- /dev/null +++ b/tests/test-fs-chown.js @@ -0,0 +1,51 @@ +// tests the fs methods chown and fchown +"use strict"; +var assert = require("assert"), + path = require("path"), + fs = require("../fs-ext"), + tmp_dir = "/tmp", + file_path = path.join(tmp_dir, "fs-ext_chown.test"), + uid = process.getuid(), + other_uid = 65534, + fd; + +function check_stats(uid) { + var stats = fs.statSync(file_path); + assert.equal(stats.uid, uid); + assert.equal(stats.gid, uid); +} + +fd = fs.openSync(file_path, "w"); +fs.closeSync(fd); +fs.chmodSync(file_path, "0666"); + +fs.chownSync(file_path, other_uid, other_uid); +check_stats(other_uid); + +fd = fs.openSync(file_path, "w+"); +fs.fchownSync(fd, uid, uid); +fs.closeSync(fd); +check_stats(uid); + +fs.chown(file_path, other_uid, other_uid, function (error) { + assert.ok(!error); + check_stats(other_uid); + + fd = fs.openSync(file_path, "w+"); + fs.fchown(fd, uid, uid, function (error) { + fs.closeSync(fd); + assert.ok(!error); + check_stats(uid); + }); +}); + +process.addListener("exit", function() { + try { + fs.closeSync(fd); + } catch (error) {} + try { + fs.unlinkSync(file_path); + } catch (error) { + console.warn(" deleting", file_path, "failed"); + } +}); diff --git a/tests/test-fs-stat.js b/tests/test-fs-stat.js new file mode 100644 index 0000000..94ee1ee --- /dev/null +++ b/tests/test-fs-stat.js @@ -0,0 +1,57 @@ +// tests the fs methods stat, lstat and fstat +"use strict"; +var assert = require("assert"), + path = require("path"), + fs = require("../fs-ext"), + tmp_dir = "/tmp", + file_path = path.join(tmp_dir, "fs-ext_stat.test"), + fd, stats; + +function check_stats(stats) { + if (process.platform.match(/^win/i)) { + assert.equal(typeof stats.uid, "string"); + assert.equal(stats.uid.indexOf("S-"), 0); + assert.equal(typeof stats.gid, "string"); + assert.equal(stats.gid.indexOf("S-"), 0); + } else { + assert.equal(typeof stats.uid, "number"); + assert.equal(typeof stats.gid, "number"); + } +} + +fd = fs.openSync(file_path, "w"); +fs.closeSync(fd); +fs.chmodSync(file_path, "0666"); + +stats = fs.statSync(file_path); +check_stats(stats); +fs.stat(file_path, function (error, stats) { + assert.ok(!error && stats); + check_stats(stats); +}); + +stats = fs.lstatSync(file_path); +check_stats(stats); +fs.lstat(file_path, function (error, stats) { + assert.ok(!error && stats); + check_stats(stats); +}); + +fd = fs.openSync(file_path, "r"); +stats = fs.fstatSync(fd); +check_stats(stats); +fs.fstat(fd, function (error, stats) { + assert.ok(!error && stats); + check_stats(stats); +}); + +process.addListener("exit", function() { + try { + fs.closeSync(fd); + } catch (error) {} + try { + fs.unlinkSync(file_path); + } catch (error) { + console.warn(" deleting", file_path, "failed"); + } +}); diff --git a/winwrap.cc b/winwrap.cc new file mode 100644 index 0000000..2952563 --- /dev/null +++ b/winwrap.cc @@ -0,0 +1,48 @@ +#ifdef _WIN32 + +#include "winwrap.h" +#include "autores.h" + +#include + +using namespace autores; + +// duplicates a string using the memory allocating method of a memory +// managing template descended from AutoMem +template LPTSTR StrDup(LPCTSTR source) { + assert(source != NULL); + // the allocating method accepts byte size; not item count + size_t size = (_tcslen(source) + 1) * sizeof(TCHAR); + LPTSTR target = A::Allocate(size); + if (target != NULL) { + // copy including the terminating zero character + CopyMemory(target, source, size); + } + return target; +} + +// duplicates a string using the LocalAlloc to allocate memory +LPTSTR LocalStrDup(LPCTSTR source) { + return StrDup >(source); +} + +// duplicates a string using the GlobalAlloc to allocate memory +LPTSTR GlobalStrDup(LPCTSTR source) { + return StrDup >(source); +} + +// duplicates a string using the HeapAlloc to allocate memory +LPTSTR HeapStrDup(HANDLE heap, LPCTSTR source) { + assert(heap != NULL); + assert(source != NULL); + // the allocating method accepts byte size; not item count + size_t size = (_tcslen(source) + 1) * sizeof(TCHAR); + LPTSTR target = HeapMem::Allocate(size, heap); + if (target != NULL) { + // copy including the terminating zero character + CopyMemory(target, source, size); + } + return target; +} + +#endif // _WIN32 diff --git a/winwrap.h b/winwrap.h new file mode 100644 index 0000000..08f9c1b --- /dev/null +++ b/winwrap.h @@ -0,0 +1,14 @@ +#ifndef WINWRAP_H +#define WINWRAP_H + +#include +#include + +// duplicates a string using the LocalAlloc to allocate memory +LPTSTR LocalStrDup(LPCTSTR source); +// duplicates a string using the GlobalAlloc to allocate memory +LPTSTR GlobalStrDup(LPCTSTR source); +// duplicates a string using the HeapAlloc to allocate memory +LPTSTR HeapStrDup(HANDLE heap, LPCTSTR source); + +#endif // WINWRAP_H From 3611c09281b0a9261a16e1aba91f7f47795f882a Mon Sep 17 00:00:00 2001 From: Ferdinand Prantl Date: Sun, 5 Jan 2014 13:45:25 +0100 Subject: [PATCH 02/33] Fix merging the fs extras, fix tests for Windows --- fs-ext.js | 3 ++- tests/test-fs-chown.js | 8 +++++--- tests/test-fs-stat.js | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/fs-ext.js b/fs-ext.js index 375b20b..0af50ee 100644 --- a/fs-ext.js +++ b/fs-ext.js @@ -337,6 +337,7 @@ var fsExt = process.platform.match(/^win/i) ? }()); + return fsExt; }()) : // the implementation doesn't need the native add-on on POSIX @@ -345,7 +346,7 @@ var fsExt = process.platform.match(/^win/i) ? // populate with fs functions from there merge(exports, fs); -// replace the functoins enhanced on Windows +// replace the functions enhanced on Windows merge(exports, fsExt); // put constants into constants module (don't like doing this but...) diff --git a/tests/test-fs-chown.js b/tests/test-fs-chown.js index a0b0bc9..a98827d 100644 --- a/tests/test-fs-chown.js +++ b/tests/test-fs-chown.js @@ -3,10 +3,12 @@ var assert = require("assert"), path = require("path"), fs = require("../fs-ext"), - tmp_dir = "/tmp", + tmp_dir = process.env.TMP || process.env.TEMP || "/tmp", file_path = path.join(tmp_dir, "fs-ext_chown.test"), - uid = process.getuid(), - other_uid = 65534, + // use the current process user or Administrator on Windows + uid = process.getuid && process.getuid() || "S-1-5-32-500", + // use the "nobody" user or the Users group on Windows + other_uid = process.platform.match(/^win/i) ? "S-1-5-32-513" : 65534, fd; function check_stats(uid) { diff --git a/tests/test-fs-stat.js b/tests/test-fs-stat.js index 94ee1ee..47d5ca6 100644 --- a/tests/test-fs-stat.js +++ b/tests/test-fs-stat.js @@ -3,7 +3,7 @@ var assert = require("assert"), path = require("path"), fs = require("../fs-ext"), - tmp_dir = "/tmp", + tmp_dir = process.env.TMP || process.env.TEMP || "/tmp", file_path = path.join(tmp_dir, "fs-ext_stat.test"), fd, stats; From 2d2cf02b0ca56ec2a633441895b3888c6c99fa25 Mon Sep 17 00:00:00 2001 From: Ferdinand Prantl Date: Sun, 5 Jan 2014 13:54:07 +0100 Subject: [PATCH 03/33] Fix the chown test for Linux --- tests/test-fs-chown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-fs-chown.js b/tests/test-fs-chown.js index a98827d..6b9bcf2 100644 --- a/tests/test-fs-chown.js +++ b/tests/test-fs-chown.js @@ -6,7 +6,7 @@ var assert = require("assert"), tmp_dir = process.env.TMP || process.env.TEMP || "/tmp", file_path = path.join(tmp_dir, "fs-ext_chown.test"), // use the current process user or Administrator on Windows - uid = process.getuid && process.getuid() || "S-1-5-32-500", + uid = process.getuid ? process.getuid() : "S-1-5-32-500", // use the "nobody" user or the Users group on Windows other_uid = process.platform.match(/^win/i) ? "S-1-5-32-513" : 65534, fd; From 03ad34cedeea77fc54946deb2b787197c3f0ae68 Mon Sep 17 00:00:00 2001 From: Alexandru Vladutu Date: Mon, 3 Feb 2014 14:43:15 +0000 Subject: [PATCH 04/33] Added example.js --- README.md | 20 ++++++++++++-------- example.js | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 example.js diff --git a/README.md b/README.md index ced46f0..27cf0ae 100644 --- a/README.md +++ b/README.md @@ -17,14 +17,18 @@ Usage fs-ext imports all of the methods from the core 'fs' module, so you don't need two objects. - var fs = require('fs-ext'); - var fd = fs.openSync('foo.txt', 'r'); - fs.flock(fd, 'ex', function (err) { - if (err) { - return console.log("Couldn't lock file"); - } - // file is locked - }) +```js +var fs = require('fs-ext'); +var fd = fs.openSync('foo.txt', 'r'); +fs.flock(fd, 'ex', function (err) { + if (err) { + return console.log("Couldn't lock file"); + } + // file is locked +}) +``` + +For an advanced example checkout `example.js`. API --- diff --git a/example.js b/example.js new file mode 100644 index 0000000..9223a91 --- /dev/null +++ b/example.js @@ -0,0 +1,54 @@ +var fs = require('./'); + +/** + * Function that locks the current file + * for `timeout` miliseconds + * + * The `counter` represents an index of how many + * times the function has been called so far + * (it acts as an id) + * + * The callback `cb` is optional + */ +function getCurrentFileSize(counter, timeout, cb) { + var fd = fs.openSync(__filename, 'r'); + + console.log("Trying to aquire lock for the %s time", counter); + + fs.flock(fd, 'exnb', function(err) { + if (err) { + return console.log("Couldn't lock file", counter); + } + + console.log('Aquired lock', counter); + + // unlock after `timeout` + setTimeout(function() { + fs.flock(fd, 'un', function(err) { + if (err) { + return console.log("Couldn't unlock file", counter); + } + + if (cb) { cb(); } + }); + }, timeout); + }); +} + +getCurrentFileSize(1, 300, function() { + // this will succeed because we're calling the function + // after unlock + getCurrentFileSize(3, 2000); +}); +// this will fail because #1 locks the file first +getCurrentFileSize(2, 1000); + +// The output should be: +/* +Trying to aquire lock for the 1 time +Trying to aquire lock for the 2 time +Aquired lock 1 +Couldn't lock file 2 +Trying to aquire lock for the 3 time +Aquired lock 3 +*/ From 9916cf7a46d03bfadf5b7259e45e5263c61a67fd Mon Sep 17 00:00:00 2001 From: Miroslav Bajtos Date: Thu, 15 May 2014 06:47:48 -0700 Subject: [PATCH 05/33] win32: Replace LOCK_* constants with macros Change LOCK_* from constants to preprocessor macros, because that's what the code exporting them to javascript expects. --- fs-ext.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs-ext.cc b/fs-ext.cc index f61e2cd..61546e7 100644 --- a/fs-ext.cc +++ b/fs-ext.cc @@ -76,10 +76,10 @@ static Persistent f_ffree_symbol; #endif #ifdef _WIN32 - const int LOCK_SH=1; - const int LOCK_EX=2; - const int LOCK_NB=4; - const int LOCK_UN=8; + #define LOCK_SH 1 + #define LOCK_EX 2 + #define LOCK_NB 4 + #define LOCK_UN 8 #endif enum From 6ae66e950a879c3261dec6cae680babb9127c005 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 29 Oct 2014 09:41:16 +0100 Subject: [PATCH 06/33] this commit fixes that node does not crash with an invalid file descriptor on windows --- fs-ext.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs-ext.cc b/fs-ext.cc index 61546e7..58d9054 100644 --- a/fs-ext.cc +++ b/fs-ext.cc @@ -187,6 +187,13 @@ static void EIO_Seek(uv_work_t *req) { } #ifdef _WIN32 + +static void uv__crt_invalid_parameter_handler(const wchar_t* expression, + const wchar_t* function, const wchar_t * file, unsigned int line, + uintptr_t reserved) { + /* No-op. */ +} + #define LK_LEN 0xffff0000 static int _win32_flock(int fd, int oper) { @@ -454,6 +461,10 @@ init (Handle target) { HandleScope scope; +#ifdef _WIN32 + _set_invalid_parameter_handler(uv__crt_invalid_parameter_handler); +#endif + #ifdef SEEK_SET NODE_DEFINE_CONSTANT(target, SEEK_SET); #endif From a67168abe7466a8ae005eac2f93bffd263a487a5 Mon Sep 17 00:00:00 2001 From: Alex Kocharin Date: Thu, 13 Nov 2014 04:10:52 +0300 Subject: [PATCH 07/33] port library to use NAN fix #33 --- binding.gyp | 1 + fs-ext.cc | 130 +++++++++++++++++++++++++-------------------------- package.json | 2 +- 3 files changed, 67 insertions(+), 66 deletions(-) diff --git a/binding.gyp b/binding.gyp index a7f3050..35a2d2e 100644 --- a/binding.gyp +++ b/binding.gyp @@ -2,6 +2,7 @@ "targets": [ { "target_name": "fs-ext", + "include_dirs" : [ " #include #include +#include #ifndef _WIN32 #include @@ -43,11 +44,10 @@ using namespace v8; using namespace node; -#define THROW_BAD_ARGS \ - ThrowException(Exception::TypeError(String::New("Bad argument"))) +#define THROW_BAD_ARGS NanThrowTypeError("Bad argument") struct store_data_t { - Persistent *cb; + NanCallback *cb; int fs_op; // operation type within this module int fd; int oper; @@ -91,7 +91,7 @@ enum }; static void EIO_After(uv_work_t *req) { - HandleScope scope; + NanScope(); store_data_t *store_data = static_cast(req->data); @@ -108,7 +108,7 @@ static void EIO_After(uv_work_t *req) { argv[0] = ErrnoException(store_data->error); } else { // error value is empty or null for non-error. - argv[0] = Local::New(Null()); + argv[0] = NanNull(); switch (store_data->fs_op) { // These operations have no data to pass other than "error". @@ -119,22 +119,22 @@ static void EIO_After(uv_work_t *req) { case FS_OP_SEEK: argc = 2; - argv[1] = Number::New(store_data->offset); + argv[1] = NanNew(store_data->offset); break; case FS_OP_STATVFS: #ifndef _WIN32 argc = 2; - statvfs_result = Object::New(); + statvfs_result = NanNew(); argv[1] = statvfs_result; - statvfs_result->Set(f_namemax_symbol, Integer::New(store_data->statvfs_buf.f_namemax)); - statvfs_result->Set(f_bsize_symbol, Integer::New(store_data->statvfs_buf.f_bsize)); - statvfs_result->Set(f_frsize_symbol, Integer::New(store_data->statvfs_buf.f_frsize)); - statvfs_result->Set(f_blocks_symbol, Number::New(store_data->statvfs_buf.f_blocks)); - statvfs_result->Set(f_bavail_symbol, Number::New(store_data->statvfs_buf.f_bavail)); - statvfs_result->Set(f_bfree_symbol, Number::New(store_data->statvfs_buf.f_bfree)); - statvfs_result->Set(f_files_symbol, Number::New(store_data->statvfs_buf.f_files)); - statvfs_result->Set(f_favail_symbol, Number::New(store_data->statvfs_buf.f_favail)); - statvfs_result->Set(f_ffree_symbol, Number::New(store_data->statvfs_buf.f_ffree)); + statvfs_result->Set(NanNew(f_namemax_symbol), NanNew(store_data->statvfs_buf.f_namemax)); + statvfs_result->Set(NanNew(f_bsize_symbol), NanNew(store_data->statvfs_buf.f_bsize)); + statvfs_result->Set(NanNew(f_frsize_symbol), NanNew(store_data->statvfs_buf.f_frsize)); + statvfs_result->Set(NanNew(f_blocks_symbol), NanNew(store_data->statvfs_buf.f_blocks)); + statvfs_result->Set(NanNew(f_bavail_symbol), NanNew(store_data->statvfs_buf.f_bavail)); + statvfs_result->Set(NanNew(f_bfree_symbol), NanNew(store_data->statvfs_buf.f_bfree)); + statvfs_result->Set(NanNew(f_files_symbol), NanNew(store_data->statvfs_buf.f_files)); + statvfs_result->Set(NanNew(f_favail_symbol), NanNew(store_data->statvfs_buf.f_favail)); + statvfs_result->Set(NanNew(f_ffree_symbol), NanNew(store_data->statvfs_buf.f_ffree)); #else argc = 1; #endif @@ -146,14 +146,14 @@ static void EIO_After(uv_work_t *req) { TryCatch try_catch; - (*(store_data->cb))->Call(v8::Context::GetCurrent()->Global(), argc, argv); + store_data->cb->Call(argc, argv); if (try_catch.HasCaught()) { FatalException(try_catch); } // Dispose of the persistent handle - cb_destroy(store_data->cb); + delete store_data->cb; delete store_data; delete req; } @@ -258,8 +258,8 @@ static void EIO_Flock(uv_work_t *req) { } -static Handle Flock(const Arguments& args) { - HandleScope scope; +static NAN_METHOD(Flock) { + NanScope(); if (args.Length() < 2 || !args[0]->IsInt32() || !args[1]->IsInt32()) { return THROW_BAD_ARGS; @@ -272,11 +272,11 @@ static Handle Flock(const Arguments& args) { flock_data->oper = args[1]->Int32Value(); if (args[2]->IsFunction()) { - flock_data->cb = cb_persist(args[2]); + flock_data->cb = new NanCallback((Local) args[2].As()); uv_work_t *req = new uv_work_t; req->data = flock_data; uv_queue_work(uv_default_loop(), req, EIO_Flock, (uv_after_work_cb)EIO_After); - return Undefined(); + NanReturnUndefined(); } else { #ifdef _WIN32 int i = _win32_flock(flock_data->fd, flock_data->oper); @@ -284,8 +284,8 @@ static Handle Flock(const Arguments& args) { int i = flock(flock_data->fd, flock_data->oper); #endif delete flock_data; - if (i != 0) return ThrowException(ErrnoException(errno)); - return Undefined(); + if (i != 0) return NanThrowError(ErrnoException(errno)); + NanReturnUndefined(); } } @@ -299,21 +299,21 @@ static inline int IsInt64(double x) { #ifndef _LARGEFILE_SOURCE #define ASSERT_OFFSET(a) \ if (!(a)->IsUndefined() && !(a)->IsNull() && !(a)->IsInt32()) { \ - return ThrowException(Exception::TypeError(String::New("Not an integer"))); \ + return NanThrowTypeError("Not an integer"); \ } #define GET_OFFSET(a) ((a)->IsNumber() ? (a)->Int32Value() : -1) #else #define ASSERT_OFFSET(a) \ if (!(a)->IsUndefined() && !(a)->IsNull() && !IsInt64((a)->NumberValue())) { \ - return ThrowException(Exception::TypeError(String::New("Not an integer"))); \ + return NanThrowTypeError("Not an integer"); \ } #define GET_OFFSET(a) ((a)->IsNumber() ? (a)->IntegerValue() : -1) #endif // fs.seek(fd, position, whence [, callback] ) -static Handle Seek(const Arguments& args) { - HandleScope scope; +static NAN_METHOD(Seek) { + NanScope(); if (args.Length() < 3 || !args[0]->IsInt32() || @@ -328,13 +328,13 @@ static Handle Seek(const Arguments& args) { if ( ! args[3]->IsFunction()) { off_t offs_result = lseek(fd, offs, whence); - if (offs_result == -1) return ThrowException(ErrnoException(errno)); - return scope.Close(Number::New(offs_result)); + if (offs_result == -1) return NanThrowError(ErrnoException(errno)); + NanReturnValue(NanNew(offs_result)); } store_data_t* seek_data = new store_data_t(); - seek_data->cb = cb_persist(args[3]); + seek_data->cb = new NanCallback((Local) args[3].As()); seek_data->fs_op = FS_OP_SEEK; seek_data->fd = fd; seek_data->offset = offs; @@ -344,7 +344,7 @@ static Handle Seek(const Arguments& args) { req->data = seek_data; uv_queue_work(uv_default_loop(), req, EIO_Seek, (uv_after_work_cb)EIO_After); - return Undefined(); + NanReturnUndefined(); } @@ -366,8 +366,8 @@ static void EIO_UTime(uv_work_t *req) { // Wrapper for utime(2). // fs.utime( path, atime, mtime, [callback] ) -static Handle UTime(const Arguments& args) { - HandleScope scope; +static NAN_METHOD(UTime) { + NanScope(); if (args.Length() < 3 || args.Length() > 4 || @@ -387,13 +387,13 @@ static Handle UTime(const Arguments& args) { buf.actime = atime; buf.modtime = mtime; int ret = utime(*path, &buf); - if (ret != 0) return ThrowException(ErrnoException(errno, "utime", "", *path)); - return Undefined(); + if (ret != 0) return NanThrowError(ErrnoException(errno, "utime", "", *path)); + NanReturnUndefined(); } store_data_t* utime_data = new store_data_t(); - utime_data->cb = cb_persist(args[3]); + utime_data->cb = new NanCallback((Local) args[3].As()); utime_data->fs_op = FS_OP_UTIME; utime_data->path = strdup(*path); utime_data->utime_buf.actime = atime; @@ -403,14 +403,14 @@ static Handle UTime(const Arguments& args) { req->data = utime_data; uv_queue_work(uv_default_loop(), req, EIO_UTime, (uv_after_work_cb)EIO_After); - return Undefined(); + NanReturnUndefined(); } // Wrapper for statvfs(2). // fs.statVFS( path, [callback] ) -static Handle StatVFS(const Arguments& args) { - HandleScope scope; +static NAN_METHOD(StatVFS) { + NanScope(); if (args.Length() < 1 || !args[0]->IsString() ) { @@ -424,28 +424,28 @@ static Handle StatVFS(const Arguments& args) { #ifndef _WIN32 struct statvfs buf; int ret = statvfs(*path, &buf); - if (ret != 0) return ThrowException(ErrnoException(errno, "statvfs", "", *path)); - Handle result = Object::New(); - result->Set(f_namemax_symbol, Integer::New(buf.f_namemax)); - result->Set(f_bsize_symbol, Integer::New(buf.f_bsize)); - result->Set(f_frsize_symbol, Integer::New(buf.f_frsize)); + if (ret != 0) return NanThrowError(ErrnoException(errno, "statvfs", "", *path)); + Handle result = NanNew(); + result->Set(NanNew(f_namemax_symbol), NanNew(buf.f_namemax)); + result->Set(NanNew(f_bsize_symbol), NanNew(buf.f_bsize)); + result->Set(NanNew(f_frsize_symbol), NanNew(buf.f_frsize)); - result->Set(f_blocks_symbol, Number::New(buf.f_blocks)); - result->Set(f_bavail_symbol, Number::New(buf.f_bavail)); - result->Set(f_bfree_symbol, Number::New(buf.f_bfree)); + result->Set(NanNew(f_blocks_symbol), NanNew(buf.f_blocks)); + result->Set(NanNew(f_bavail_symbol), NanNew(buf.f_bavail)); + result->Set(NanNew(f_bfree_symbol), NanNew(buf.f_bfree)); - result->Set(f_files_symbol, Number::New(buf.f_files)); - result->Set(f_favail_symbol, Number::New(buf.f_favail)); - result->Set(f_ffree_symbol, Number::New(buf.f_ffree)); - return result; + result->Set(NanNew(f_files_symbol), NanNew(buf.f_files)); + result->Set(NanNew(f_favail_symbol), NanNew(buf.f_favail)); + result->Set(NanNew(f_ffree_symbol), NanNew(buf.f_ffree)); + NanReturnValue(result); #else - return Undefined(); + NanReturnUndefined(); #endif } store_data_t* statvfs_data = new store_data_t(); - statvfs_data->cb = cb_persist(args[1]); + statvfs_data->cb = new NanCallback((Local) args[1].As()); statvfs_data->fs_op = FS_OP_STATVFS; statvfs_data->path = strdup(*path); @@ -453,13 +453,13 @@ static Handle StatVFS(const Arguments& args) { req->data = statvfs_data; uv_queue_work(uv_default_loop(), req, EIO_StatVFS,(uv_after_work_cb)EIO_After); - return Undefined(); + NanReturnUndefined(); } extern "C" void init (Handle target) { - HandleScope scope; + NanScope(); #ifdef _WIN32 _set_invalid_parameter_handler(uv__crt_invalid_parameter_handler); @@ -498,17 +498,17 @@ init (Handle target) NODE_SET_METHOD(target, "utime", UTime); NODE_SET_METHOD(target, "statVFS", StatVFS); #ifndef _WIN32 - f_namemax_symbol = NODE_PSYMBOL("f_namemax"); - f_bsize_symbol = NODE_PSYMBOL("f_bsize"); - f_frsize_symbol = NODE_PSYMBOL("f_frsize"); + NanAssignPersistent(f_namemax_symbol, NanNew("f_namemax")); + NanAssignPersistent(f_bsize_symbol, NanNew("f_bsize")); + NanAssignPersistent(f_frsize_symbol, NanNew("f_frsize")); - f_blocks_symbol = NODE_PSYMBOL("f_blocks"); - f_bavail_symbol = NODE_PSYMBOL("f_bavail"); - f_bfree_symbol = NODE_PSYMBOL("f_bfree"); + NanAssignPersistent(f_blocks_symbol, NanNew("f_blocks")); + NanAssignPersistent(f_bavail_symbol, NanNew("f_bavail")); + NanAssignPersistent(f_bfree_symbol, NanNew("f_bfree")); - f_files_symbol = NODE_PSYMBOL("f_files"); - f_favail_symbol = NODE_PSYMBOL("f_favail"); - f_ffree_symbol = NODE_PSYMBOL("f_ffree"); + NanAssignPersistent(f_files_symbol, NanNew("f_files")); + NanAssignPersistent(f_favail_symbol, NanNew("f_favail")); + NanAssignPersistent(f_ffree_symbol, NanNew("f_ffree")); #endif #ifdef _WIN32 diff --git a/package.json b/package.json index 16ec173..bb43c9c 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "engines": { "node": ">= v0.8.0" }, - "dependencies": { }, + "dependencies": { "nan": "1 >=1.4" }, "licenses": [ { "type": "MIT" } ], From 382a7f3e5a59f593d0dd7d575778f91a2fa6fdd6 Mon Sep 17 00:00:00 2001 From: Tim Cuthbertson Date: Wed, 28 May 2014 12:08:13 +1000 Subject: [PATCH 08/33] added fcntl() binding Currently, it only supports GETFD|SETFD actions with flag FD_CLOEXEC --- fs-ext.cc | 66 ++++++++- fs-ext.js | 34 +++++ run_tests | 2 + tests/test-fs-fcntl.js | 330 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 431 insertions(+), 1 deletion(-) create mode 100644 tests/test-fs-fcntl.js diff --git a/fs-ext.cc b/fs-ext.cc index fc3a4ef..370c8d0 100644 --- a/fs-ext.cc +++ b/fs-ext.cc @@ -51,6 +51,7 @@ struct store_data_t { int fs_op; // operation type within this module int fd; int oper; + int arg; off_t offset; struct utimbuf utime_buf; #ifndef _WIN32 @@ -87,7 +88,8 @@ enum FS_OP_FLOCK, FS_OP_SEEK, FS_OP_UTIME, - FS_OP_STATVFS + FS_OP_STATVFS, + FS_OP_FCNTL, }; static void EIO_After(uv_work_t *req) { @@ -139,6 +141,10 @@ static void EIO_After(uv_work_t *req) { argc = 1; #endif break; + case FS_OP_FCNTL: + argc = 2; + argv[1] = NanNew(store_data->result); + break; default: assert(0 && "Unhandled op type value"); } @@ -186,6 +192,14 @@ static void EIO_Seek(uv_work_t *req) { } +static void EIO_Fcntl(uv_work_t *req) { + store_data_t* data = static_cast(req->data); + int result = data->result = fcntl(data->fd, data->oper, data->arg); + if (result == -1) { + data->error = errno; + } +} + #ifdef _WIN32 static void uv__crt_invalid_parameter_handler(const wchar_t* expression, @@ -347,6 +361,43 @@ static NAN_METHOD(Seek) { NanReturnUndefined(); } +// fs.fcntl(fd, cmd, [arg]) + +static NAN_METHOD(Fcntl) { + NanScope(); + + if (args.Length() < 3 || + !args[0]->IsInt32() || + !args[1]->IsInt32() || + !args[2]->IsInt32()) { + return THROW_BAD_ARGS; + } + + int fd = args[0]->Int32Value(); + int cmd = args[1]->Int32Value(); + int arg = args[2]->Int32Value(); + + if ( ! args[3]->IsFunction()) { + int result = fcntl(fd, cmd, arg); + if (result == -1) return ThrowException(ErrnoException(errno)); + NanReturnValue(NanNew(result)); + } + + store_data_t* data = new store_data_t(); + + data->cb = new NanCallback((Local) args[3].As()); + data->fs_op = FS_OP_FCNTL; + data->fd = fd; + data->oper = cmd; + data->arg = arg; + + uv_work_t *req = new uv_work_t; + req->data = data; + uv_queue_work(uv_default_loop(), req, EIO_Fcntl, (uv_after_work_cb)EIO_After); + + NanReturnUndefined(); +} + static void EIO_UTime(uv_work_t *req) { store_data_t* utime_data = static_cast(req->data); @@ -493,7 +544,20 @@ init (Handle target) NODE_DEFINE_CONSTANT(target, LOCK_UN); #endif +#ifdef F_GETFD + NODE_DEFINE_CONSTANT(target, F_GETFD); +#endif + +#ifdef F_SETFD + NODE_DEFINE_CONSTANT(target, F_SETFD); +#endif + +#ifdef FD_CLOEXEC + NODE_DEFINE_CONSTANT(target, FD_CLOEXEC); +#endif + NODE_SET_METHOD(target, "seek", Seek); + NODE_SET_METHOD(target, "fcntl", Fcntl); NODE_SET_METHOD(target, "flock", Flock); NODE_SET_METHOD(target, "utime", UTime); NODE_SET_METHOD(target, "statVFS", StatVFS); diff --git a/fs-ext.js b/fs-ext.js index 0af50ee..bf52ac8 100644 --- a/fs-ext.js +++ b/fs-ext.js @@ -48,6 +48,24 @@ function stringToFlockFlags(flag) { } } +// used by Fcntl +function stringToFcntlFlags(flag) { + if (typeof flag !== 'string') { + return flag; + } + + switch (flag) { + case 'getfd': + return binding.F_GETFD; + + case 'setfd': + return binding.F_SETFD; + + default: + throw new Error('Unknown fcntl flag: ' + flag); + } +} + function noop() {} exports.flock = function(fd, flags, callback) { @@ -67,6 +85,22 @@ exports.flockSync = function(fd, flags) { return binding.flock(fd, oper); }; +exports.fcntl = function(fd, cmd, arg, callback) { + cmd = stringToFcntlFlags(cmd); + if (arguments.length < 4) { + callback = arg; + arg = 0; + } + if(!arg) arg = 0; + return binding.fcntl(fd, cmd, arg, callback); +}; + +exports.fcntlSync = function(fd, cmd, arg) { + cmd = stringToFcntlFlags(cmd); + if (!arg) arg = 0; + return binding.fcntl(fd, cmd, arg); +}; + exports.seek = function(fd, position, whence, callback) { callback = arguments[arguments.length - 1]; if (typeof(callback) !== 'function') { diff --git a/run_tests b/run_tests index 3f6a1dc..ac16964 100755 --- a/run_tests +++ b/run_tests @@ -1,5 +1,7 @@ #!/bin/bash +node tests/test-fs-fcntl + node tests/test-fs-seek.js node tests/test-fs-flock.js diff --git a/tests/test-fs-fcntl.js b/tests/test-fs-fcntl.js new file mode 100644 index 0000000..3149c37 --- /dev/null +++ b/tests/test-fs-fcntl.js @@ -0,0 +1,330 @@ + +// Test these APIs as published in extension module 'fs-ext' +// +// fs.fcntl(fd, cmd, [arg], [callback]) +// +// console.log( require.resolve('../fs-ext')); + +var assert = require('assert'), + path = require('path'), + util = require('util'), + fs = require('../fs-ext'); + +var tests_ok = 0, + tests_run = 0; + +var debug_me = true; + debug_me = false; + +var tmp_dir = "/tmp", + file_path = path.join(tmp_dir, 'what.when.fcntl.test'), + file_path_not = path.join(tmp_dir, 'what.not.fcntl.test'); + +var file_fd, + err; + + +// Report on test results - - - - - - - - - - - - + +// Clean up and report on final success or failure of tests here +process.addListener('exit', function() { + + try { + fs.closeSync(file_fd); + } catch (e) { + // might not be open, that's okay. + } + + remove_file_wo_error(file_path); + + console.log('Tests run: %d ok: %d', tests_run, tests_ok); + assert.equal(tests_ok, tests_run, 'One or more subtests failed'); +}); + + +// Test helpers - - - - - - - - - - - - - - - + +function remove_file_wo_error(file_path) { + try { + fs.unlinkSync(file_path); + } catch (e) { + // might not exist, that's okay. + } +} + +function expect_errno(api_name, resource, err, expected_errno) { + var fault_msg; + + if (debug_me) console.log(' expected_errno(err): ' + err ); + + if ( err && err.code !== expected_errno ) { + fault_msg = api_name + '(): expected error ' + expected_errno + ', got another error'; + } else if ( !err ) { + fault_msg = api_name + '(): expected error ' + expected_errno + ', got another error'; + } + + if ( ! fault_msg ) { + tests_ok++; + if (debug_me) console.log(' FAILED OK: ' + api_name ); + } else { + console.log('FAILURE: ' + arguments.callee.name + ': ' + fault_msg); + console.log(' ARGS: ', util.inspect(arguments)); + } +} + +function expect_ok(api_name, resource, err) { + var fault_msg; + + if ( err ) { + fault_msg = api_name + '(): returned error'; + } + + if ( ! fault_msg ) { + tests_ok++; + if (debug_me) console.log(' OK: ' + api_name ); + } else { + console.log('FAILURE: ' + arguments.callee.name + ': ' + fault_msg); + console.log(' ARGS: ', util.inspect(arguments)); + } +} + + +// Setup for testing - - - - - - - - - - - - + +// Check whether this version of node.js has these APIs to test +//XXX Consider just exiting without error after displaying notices + +tests_run++; +if ( typeof fs.fcntl !== 'function' ) { + console.log('fs.fcntl API is missing'); +} else { + tests_ok++; +} + +tests_run++; +if ( typeof fs.fcntlSync !== 'function' ) { + console.log('fs.fcntlSync API is missing'); +} else { + tests_ok++; +} + + +// If any pre-checks and setup fail, quit before tests +if ( tests_run !== tests_ok ) { + process.exit(1); +} + +// Delete any prior copy of test data file(s) +remove_file_wo_error(file_path); + +// Create a new file +tests_run++; +try { + file_fd = fs.openSync(file_path, 'w'); + tests_ok++; +} catch (e) { + console.log(' Unable to create test data file %j', file_path); + console.log(' Error was: %j', e); +} + + +if ( tests_run !== tests_ok ) { + process.exit(1); +} + + +// Test that constants are published - - - - - - - - + +var fs_binding = require('../build/Release/fs-ext'); + +var constant_names = [ 'F_SETFD', 'F_GETFD', 'FD_CLOEXEC' ]; + +constant_names.forEach(function(name){ + + if (debug_me) console.log(' %s %j %j', name, fs_binding[name], typeof fs_binding[name]); + + tests_run++; + if ( fs_binding[name] !== undefined && + typeof fs_binding[name] === 'number' ) { + tests_ok++; + } else { + console.log('FAILURE: %s is not defined correctly', name); + console.log(' %s %j %j', name, fs_binding[name], typeof fs_binding[name]); + } +}); + + +// Test bad argument handling - - - - - - - - - - - + +// fd value is undefined + +tests_run++; +try { + err = fs.fcntlSync(undefined, 0); +} catch (e) { + err = e; +} + +if (err) { + if (debug_me) console.log(' err %j', err); + tests_ok++; +} else { + if (debug_me) console.log(' expected error from undefined fd argument'); +} + + +// fd value is non-number + +tests_run++; +try { + err = fs.fcntlSync('foo', 0); +} catch (e) { + err = e; +} + +if (err) { + if (debug_me) console.log(' err %j', err); + tests_ok++; +} else { + if (debug_me) console.log(' expected error from non-numeric fd argument'); +} + + +// fd value is negative + +tests_run++; +try { + err = fs.fcntlSync(-9, 0); +} catch (e) { + err = e; +} +expect_errno('fcntlSync', -9, err, 'EBADF'); + + +// fd value is 'impossible' + +tests_run++; +try { + err = fs.fcntlSync(98765, 0); +} catch (e) { + err = e; +} +expect_errno('fcntlSync', 98765, err, 'EBADF'); + + +// flags value is invalid + + +tests_run++; +try { + err = fs.fcntlSync(file_fd, 'foo'); +} catch (e) { + err = e; +} + +// "message": "Unknown fcntl flag: foo" +if (err) { + if (debug_me) console.log(' err %j', err); + tests_ok++; +} else { + if (debug_me) console.log(' expected error from non-numeric fd argument'); +} + + +// Test valid calls: fcntlSync - - - - - - - - - - + +tests_run++; +try { + err = null; + fs.fcntlSync(file_fd, 'getfd'); +} catch (e) { + err = e; +} +expect_ok('fcntlSync', file_fd, err); + + +// operation setfd then getfd + +tests_run++; +try { + err = null; + var flags = fs.fcntlSync(file_fd, 'getfd'); + console.log("initial flags:" + (flags)); + + fs.fcntlSync(file_fd, 'setfd', flags | fs_binding.FD_CLOEXEC); + flags = fs.fcntlSync(file_fd, 'getfd'); + if ((flags & fs_binding.FD_CLOEXEC) !== fs_binding.FD_CLOEXEC) { + throw new Error("Expected FD_CLOEXEC to be set: " + flags); + } + + fs.fcntlSync(file_fd, 'setfd', flags & (~fs_binding.FD_CLOEXEC)); + flags = fs.fcntlSync(file_fd, 'getfd'); + if ((flags & fs_binding.FD_CLOEXEC) === fs_binding.FD_CLOEXEC) { + throw new Error("Expected FD_CLOEXEC to be cleared"); + } +} catch (e) { + err = e; +} +expect_ok('fcntlSync', file_fd, err); + +// Test valid calls: fcntl - - - - - - - - - - - + +// SEEK_SET to 0 + +tests_run++; +fs.fcntl(file_fd, 'getfd', function(err, flags) { + expect_ok('fcntl', file_fd, err); + + tests_run++; + fs.fcntl(file_fd, 'setfd', flags | fs_binding.FD_CLOEXEC, function(err) { + expect_ok('fcntl', file_fd, err); + + tests_run++; + fs.fcntl(file_fd, 'getfd', function(err, flags) { + expect_ok('fcntl', file_fd, err); + + if ((flags & fs_binding.FD_CLOEXEC) !== fs_binding.FD_CLOEXEC) { + tests_run++; + expect_ok('fcntl', file_fd, new Error("did not set cloexec")); + } + + tests_run++; + fs.fcntl(file_fd, 'setfd', flags & (~fs_binding.FD_CLOEXEC), function(err, flags) { + expect_ok('fcntl', file_fd, err); + + tests_run++; + fs.fcntl(file_fd, 'getfd', function(err, flags) { + expect_ok('fcntl', file_fd, err); + + if ((flags & fs_binding.FD_CLOEXEC) === fs_binding.FD_CLOEXEC) { + tests_run++; + expect_ok('fcntl', file_fd, new Error("did not clear cloexec")); + } + + + // Test invalid calls: fcntl - - - - - - - - - + + // offset value is negative + tests_run++; + + try { + fs.fcntl(file_fd, 'foo', function(err) { + console.log(' unexpected callback from fcntl() with bad argument'); + }); + err = undefined; + } catch (e) { + err = e; + } + // "message": "Unknown fcntl flag: foo" + if (err) { + if (debug_me) console.log(' err %j', err); + tests_ok++; + } else { + if (debug_me) console.log(' unexpected success from fcntl() with bad argument'); + } + }); + }); + }); + }); +}); + From dd70f74b47b4d26d2526d31b8cc44a1ca315335a Mon Sep 17 00:00:00 2001 From: Tim Cuthbertson Date: Wed, 28 May 2014 12:28:07 +1000 Subject: [PATCH 09/33] add fcntl to README.md --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index 27cf0ae..92d35e0 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,23 @@ to the various LOCK_SH, LOCK_EX, LOCK_SH|LOCK_NB, etc. Synchronous flock(2). Throws an exception on error. +### fs.fcntl(fd, cmd, [arg], [callback]) + +Asynchronous fcntl(2). + +callback will be given two arguments (err, result). + +The supported commands are: + +- 'getfd' ( F_GETFD ) +- 'setfd' ( F_SETFD ) + +Requiring this module adds `FD_CLOEXEC` to the constants module, for use with F_SETFD. + +### fs.fcntlSync(fd, flags) + +Synchronous fcntl(2). Throws an exception on error. + ### fs.seek(fd, offset, whence, [callback]) Asynchronous lseek(2). From e1f59613afd51311f0ebd6b9660294b0f2bd08c3 Mon Sep 17 00:00:00 2001 From: Matt Sergeant Date: Mon, 17 Nov 2014 09:10:08 -0500 Subject: [PATCH 10/33] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bb43c9c..b632ba2 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "name": "fs-ext", "description": "Extensions to core 'fs' module.", "keywords": ["fs", "filesystem", "flock", "seek"], - "version": "0.3.2", + "version": "0.4.0", "homepage": "https://github.com/baudehlo/node-fs-ext/", "repository": { "type": "git", From 1b29b004f8d7657dd6ed039b5523957d01322fa1 Mon Sep 17 00:00:00 2001 From: Alex Kocharin Date: Tue, 25 Nov 2014 03:27:56 +0300 Subject: [PATCH 11/33] ThrowException -> NanThrowError --- fs-ext.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs-ext.cc b/fs-ext.cc index 370c8d0..b7474cd 100644 --- a/fs-ext.cc +++ b/fs-ext.cc @@ -379,7 +379,7 @@ static NAN_METHOD(Fcntl) { if ( ! args[3]->IsFunction()) { int result = fcntl(fd, cmd, arg); - if (result == -1) return ThrowException(ErrnoException(errno)); + if (result == -1) return NanThrowError(ErrnoException(errno)); NanReturnValue(NanNew(result)); } From 84afbc6f5d965b7a16c0d97523637402f01dfb2e Mon Sep 17 00:00:00 2001 From: Matt Sergeant Date: Mon, 24 Nov 2014 19:53:46 -0500 Subject: [PATCH 12/33] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b632ba2..a543013 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "name": "fs-ext", "description": "Extensions to core 'fs' module.", "keywords": ["fs", "filesystem", "flock", "seek"], - "version": "0.4.0", + "version": "0.4.1", "homepage": "https://github.com/baudehlo/node-fs-ext/", "repository": { "type": "git", From 9572ed3c640ea5073494b24dee9c6f29420ca146 Mon Sep 17 00:00:00 2001 From: Parag Nemade Date: Thu, 4 Dec 2014 12:42:52 +0530 Subject: [PATCH 13/33] Add license text file --- LICENSE.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..c9b44cb --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,18 @@ +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. From aa6d7465c3b2c446cf317844139d19d8aded7963 Mon Sep 17 00:00:00 2001 From: Matt Sergeant Date: Wed, 21 Jan 2015 08:58:16 -0500 Subject: [PATCH 14/33] Lock Nan to 1.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a543013..6bf901c 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "engines": { "node": ">= v0.8.0" }, - "dependencies": { "nan": "1 >=1.4" }, + "dependencies": { "nan": "~1.4" }, "licenses": [ { "type": "MIT" } ], From 459899f4657001e1a2a7493576022080c45faefc Mon Sep 17 00:00:00 2001 From: Matt Sergeant Date: Wed, 21 Jan 2015 08:58:37 -0500 Subject: [PATCH 15/33] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6bf901c..596c3c6 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "name": "fs-ext", "description": "Extensions to core 'fs' module.", "keywords": ["fs", "filesystem", "flock", "seek"], - "version": "0.4.1", + "version": "0.4.2", "homepage": "https://github.com/baudehlo/node-fs-ext/", "repository": { "type": "git", From 9f12162de308e4cab27196b759491335c9103b71 Mon Sep 17 00:00:00 2001 From: Matt Sergeant Date: Wed, 21 Jan 2015 08:59:16 -0500 Subject: [PATCH 16/33] Bump version again --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 596c3c6..3450733 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "name": "fs-ext", "description": "Extensions to core 'fs' module.", "keywords": ["fs", "filesystem", "flock", "seek"], - "version": "0.4.2", + "version": "0.4.3", "homepage": "https://github.com/baudehlo/node-fs-ext/", "repository": { "type": "git", From 67aaae06217ccfeea4241aa991835b2ca6250e98 Mon Sep 17 00:00:00 2001 From: Geoff Greer Date: Mon, 23 Feb 2015 09:49:36 -0800 Subject: [PATCH 17/33] Update Nan to 1.6. New Nan is stricter about conversions, so static_cast unsigned longs to v8-supported 32-bit uints. Overflow shouldn't be a problem for these. --- fs-ext.cc | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fs-ext.cc b/fs-ext.cc index b7474cd..1ea7c05 100644 --- a/fs-ext.cc +++ b/fs-ext.cc @@ -128,9 +128,9 @@ static void EIO_After(uv_work_t *req) { argc = 2; statvfs_result = NanNew(); argv[1] = statvfs_result; - statvfs_result->Set(NanNew(f_namemax_symbol), NanNew(store_data->statvfs_buf.f_namemax)); - statvfs_result->Set(NanNew(f_bsize_symbol), NanNew(store_data->statvfs_buf.f_bsize)); - statvfs_result->Set(NanNew(f_frsize_symbol), NanNew(store_data->statvfs_buf.f_frsize)); + statvfs_result->Set(NanNew(f_namemax_symbol), NanNew(static_cast(store_data->statvfs_buf.f_namemax))); + statvfs_result->Set(NanNew(f_bsize_symbol), NanNew(static_cast(store_data->statvfs_buf.f_bsize))); + statvfs_result->Set(NanNew(f_frsize_symbol), NanNew(static_cast(store_data->statvfs_buf.f_frsize))); statvfs_result->Set(NanNew(f_blocks_symbol), NanNew(store_data->statvfs_buf.f_blocks)); statvfs_result->Set(NanNew(f_bavail_symbol), NanNew(store_data->statvfs_buf.f_bavail)); statvfs_result->Set(NanNew(f_bfree_symbol), NanNew(store_data->statvfs_buf.f_bfree)); @@ -477,9 +477,9 @@ static NAN_METHOD(StatVFS) { int ret = statvfs(*path, &buf); if (ret != 0) return NanThrowError(ErrnoException(errno, "statvfs", "", *path)); Handle result = NanNew(); - result->Set(NanNew(f_namemax_symbol), NanNew(buf.f_namemax)); - result->Set(NanNew(f_bsize_symbol), NanNew(buf.f_bsize)); - result->Set(NanNew(f_frsize_symbol), NanNew(buf.f_frsize)); + result->Set(NanNew(f_namemax_symbol), NanNew(static_cast(buf.f_namemax))); + result->Set(NanNew(f_bsize_symbol), NanNew(static_cast(buf.f_bsize))); + result->Set(NanNew(f_frsize_symbol), NanNew(static_cast(buf.f_frsize))); result->Set(NanNew(f_blocks_symbol), NanNew(buf.f_blocks)); result->Set(NanNew(f_bavail_symbol), NanNew(buf.f_bavail)); diff --git a/package.json b/package.json index 3450733..e6be516 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "engines": { "node": ">= v0.8.0" }, - "dependencies": { "nan": "~1.4" }, + "dependencies": { "nan": "~1.6" }, "licenses": [ { "type": "MIT" } ], From d42f0a6d9afa3d1185a7430f7473db6628ba05b3 Mon Sep 17 00:00:00 2001 From: Ilkka Myller Date: Wed, 6 May 2015 00:06:14 +0300 Subject: [PATCH 18/33] Update to nan ~1.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e6be516..f10f26c 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "engines": { "node": ">= v0.8.0" }, - "dependencies": { "nan": "~1.6" }, + "dependencies": { "nan": "~1.8" }, "licenses": [ { "type": "MIT" } ], From 6f49abb53d1760a2dbfeb94ae284a31cdfad879b Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Mon, 8 Jun 2015 12:22:40 -0700 Subject: [PATCH 19/33] Use nan 1.9 for ErrnoException and FatalException --- fs-ext.cc | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fs-ext.cc b/fs-ext.cc index 1ea7c05..6238a2e 100644 --- a/fs-ext.cc +++ b/fs-ext.cc @@ -107,7 +107,7 @@ static void EIO_After(uv_work_t *req) { // for a success, which is possible. if (store_data->result == -1) { // If the request doesn't have a path parameter set. - argv[0] = ErrnoException(store_data->error); + argv[0] = NanErrnoException(store_data->error); } else { // error value is empty or null for non-error. argv[0] = NanNull(); @@ -155,7 +155,7 @@ static void EIO_After(uv_work_t *req) { store_data->cb->Call(argc, argv); if (try_catch.HasCaught()) { - FatalException(try_catch); + NanFatalException(try_catch); } // Dispose of the persistent handle @@ -298,7 +298,7 @@ static NAN_METHOD(Flock) { int i = flock(flock_data->fd, flock_data->oper); #endif delete flock_data; - if (i != 0) return NanThrowError(ErrnoException(errno)); + if (i != 0) return NanThrowError(NanErrnoException(errno)); NanReturnUndefined(); } } @@ -342,7 +342,7 @@ static NAN_METHOD(Seek) { if ( ! args[3]->IsFunction()) { off_t offs_result = lseek(fd, offs, whence); - if (offs_result == -1) return NanThrowError(ErrnoException(errno)); + if (offs_result == -1) return NanThrowError(NanErrnoException(errno)); NanReturnValue(NanNew(offs_result)); } @@ -379,7 +379,7 @@ static NAN_METHOD(Fcntl) { if ( ! args[3]->IsFunction()) { int result = fcntl(fd, cmd, arg); - if (result == -1) return NanThrowError(ErrnoException(errno)); + if (result == -1) return NanThrowError(NanErrnoException(errno)); NanReturnValue(NanNew(result)); } @@ -438,7 +438,7 @@ static NAN_METHOD(UTime) { buf.actime = atime; buf.modtime = mtime; int ret = utime(*path, &buf); - if (ret != 0) return NanThrowError(ErrnoException(errno, "utime", "", *path)); + if (ret != 0) return NanThrowError(NanErrnoException(errno, "utime", "", *path)); NanReturnUndefined(); } @@ -475,7 +475,7 @@ static NAN_METHOD(StatVFS) { #ifndef _WIN32 struct statvfs buf; int ret = statvfs(*path, &buf); - if (ret != 0) return NanThrowError(ErrnoException(errno, "statvfs", "", *path)); + if (ret != 0) return NanThrowError(NanErrnoException(errno, "statvfs", "", *path)); Handle result = NanNew(); result->Set(NanNew(f_namemax_symbol), NanNew(static_cast(buf.f_namemax))); result->Set(NanNew(f_bsize_symbol), NanNew(static_cast(buf.f_bsize))); diff --git a/package.json b/package.json index f10f26c..1eae2ca 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "engines": { "node": ">= v0.8.0" }, - "dependencies": { "nan": "~1.8" }, + "dependencies": { "nan": "^1.9" }, "licenses": [ { "type": "MIT" } ], From a99b1eac78a51d6ed6dd59e49a3aa2406bc167d9 Mon Sep 17 00:00:00 2001 From: Geoff Greer Date: Sat, 29 Aug 2015 23:54:22 -0700 Subject: [PATCH 20/33] Update dep to Nan 2.x. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1eae2ca..8acadf5 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "engines": { "node": ">= v0.8.0" }, - "dependencies": { "nan": "^1.9" }, + "dependencies": { "nan": "^2.0" }, "licenses": [ { "type": "MIT" } ], From 70cffca3fd433093c59aa75336e98b7a5371d956 Mon Sep 17 00:00:00 2001 From: Geoff Greer Date: Sat, 29 Aug 2015 23:54:50 -0700 Subject: [PATCH 21/33] Use Nan 2.x API. Still need to fix NanAssignPersistent. --- fs-ext.cc | 214 ++++++++++++++++++++++++++---------------------------- 1 file changed, 102 insertions(+), 112 deletions(-) diff --git a/fs-ext.cc b/fs-ext.cc index 6238a2e..d1f1e6a 100644 --- a/fs-ext.cc +++ b/fs-ext.cc @@ -44,10 +44,10 @@ using namespace v8; using namespace node; -#define THROW_BAD_ARGS NanThrowTypeError("Bad argument") +#define THROW_BAD_ARGS Nan::ThrowTypeError("Bad argument") struct store_data_t { - NanCallback *cb; + Nan::Callback *cb; int fs_op; // operation type within this module int fd; int oper; @@ -93,7 +93,7 @@ enum }; static void EIO_After(uv_work_t *req) { - NanScope(); + Nan::HandleScope scope; store_data_t *store_data = static_cast(req->data); @@ -107,10 +107,10 @@ static void EIO_After(uv_work_t *req) { // for a success, which is possible. if (store_data->result == -1) { // If the request doesn't have a path parameter set. - argv[0] = NanErrnoException(store_data->error); + argv[0] = Nan::NanErrnoException(store_data->error); } else { // error value is empty or null for non-error. - argv[0] = NanNull(); + argv[0] = Nan::Null(); switch (store_data->fs_op) { // These operations have no data to pass other than "error". @@ -121,41 +121,41 @@ static void EIO_After(uv_work_t *req) { case FS_OP_SEEK: argc = 2; - argv[1] = NanNew(store_data->offset); + argv[1] = Nan::New(store_data->offset); break; case FS_OP_STATVFS: #ifndef _WIN32 argc = 2; - statvfs_result = NanNew(); + statvfs_result = Nan::New(); argv[1] = statvfs_result; - statvfs_result->Set(NanNew(f_namemax_symbol), NanNew(static_cast(store_data->statvfs_buf.f_namemax))); - statvfs_result->Set(NanNew(f_bsize_symbol), NanNew(static_cast(store_data->statvfs_buf.f_bsize))); - statvfs_result->Set(NanNew(f_frsize_symbol), NanNew(static_cast(store_data->statvfs_buf.f_frsize))); - statvfs_result->Set(NanNew(f_blocks_symbol), NanNew(store_data->statvfs_buf.f_blocks)); - statvfs_result->Set(NanNew(f_bavail_symbol), NanNew(store_data->statvfs_buf.f_bavail)); - statvfs_result->Set(NanNew(f_bfree_symbol), NanNew(store_data->statvfs_buf.f_bfree)); - statvfs_result->Set(NanNew(f_files_symbol), NanNew(store_data->statvfs_buf.f_files)); - statvfs_result->Set(NanNew(f_favail_symbol), NanNew(store_data->statvfs_buf.f_favail)); - statvfs_result->Set(NanNew(f_ffree_symbol), NanNew(store_data->statvfs_buf.f_ffree)); + statvfs_result->Set(Nan::New(f_namemax_symbol), Nan::New(static_cast(store_data->statvfs_buf.f_namemax))); + statvfs_result->Set(Nan::New(f_bsize_symbol), Nan::New(static_cast(store_data->statvfs_buf.f_bsize))); + statvfs_result->Set(Nan::New(f_frsize_symbol), Nan::New(static_cast(store_data->statvfs_buf.f_frsize))); + statvfs_result->Set(Nan::New(f_blocks_symbol), Nan::New(store_data->statvfs_buf.f_blocks)); + statvfs_result->Set(Nan::New(f_bavail_symbol), Nan::New(store_data->statvfs_buf.f_bavail)); + statvfs_result->Set(Nan::New(f_bfree_symbol), Nan::New(store_data->statvfs_buf.f_bfree)); + statvfs_result->Set(Nan::New(f_files_symbol), Nan::New(store_data->statvfs_buf.f_files)); + statvfs_result->Set(Nan::New(f_favail_symbol), Nan::New(store_data->statvfs_buf.f_favail)); + statvfs_result->Set(Nan::New(f_ffree_symbol), Nan::New(store_data->statvfs_buf.f_ffree)); #else argc = 1; #endif break; case FS_OP_FCNTL: argc = 2; - argv[1] = NanNew(store_data->result); + argv[1] = Nan::New(store_data->result); break; default: assert(0 && "Unhandled op type value"); } } - TryCatch try_catch; + Nan::TryCatch try_catch; store_data->cb->Call(argc, argv); if (try_catch.HasCaught()) { - NanFatalException(try_catch); + Nan::FatalException(try_catch); } // Dispose of the persistent handle @@ -273,24 +273,22 @@ static void EIO_Flock(uv_work_t *req) { } static NAN_METHOD(Flock) { - NanScope(); - - if (args.Length() < 2 || !args[0]->IsInt32() || !args[1]->IsInt32()) { + if (info.Length() < 2 || !info[0]->IsInt32() || !info[1]->IsInt32()) { return THROW_BAD_ARGS; } store_data_t* flock_data = new store_data_t(); flock_data->fs_op = FS_OP_FLOCK; - flock_data->fd = args[0]->Int32Value(); - flock_data->oper = args[1]->Int32Value(); + flock_data->fd = info[0]->Int32Value(); + flock_data->oper = info[1]->Int32Value(); - if (args[2]->IsFunction()) { - flock_data->cb = new NanCallback((Local) args[2].As()); + if (info[2]->IsFunction()) { + flock_data->cb = new Nan::Callback((Local) info[2].As()); uv_work_t *req = new uv_work_t; req->data = flock_data; uv_queue_work(uv_default_loop(), req, EIO_Flock, (uv_after_work_cb)EIO_After); - NanReturnUndefined(); + info.GetReturnValue().SetUndefined(); } else { #ifdef _WIN32 int i = _win32_flock(flock_data->fd, flock_data->oper); @@ -298,8 +296,8 @@ static NAN_METHOD(Flock) { int i = flock(flock_data->fd, flock_data->oper); #endif delete flock_data; - if (i != 0) return NanThrowError(NanErrnoException(errno)); - NanReturnUndefined(); + if (i != 0) return Nan::ThrowError(Nan::NanErrnoException(errno)); + info.GetReturnValue().SetUndefined(); } } @@ -313,13 +311,13 @@ static inline int IsInt64(double x) { #ifndef _LARGEFILE_SOURCE #define ASSERT_OFFSET(a) \ if (!(a)->IsUndefined() && !(a)->IsNull() && !(a)->IsInt32()) { \ - return NanThrowTypeError("Not an integer"); \ + return Nan::ThrowTypeError("Not an integer"); \ } #define GET_OFFSET(a) ((a)->IsNumber() ? (a)->Int32Value() : -1) #else #define ASSERT_OFFSET(a) \ if (!(a)->IsUndefined() && !(a)->IsNull() && !IsInt64((a)->NumberValue())) { \ - return NanThrowTypeError("Not an integer"); \ + return Nan::ThrowTypeError("Not an integer"); \ } #define GET_OFFSET(a) ((a)->IsNumber() ? (a)->IntegerValue() : -1) #endif @@ -327,28 +325,26 @@ static inline int IsInt64(double x) { // fs.seek(fd, position, whence [, callback] ) static NAN_METHOD(Seek) { - NanScope(); - - if (args.Length() < 3 || - !args[0]->IsInt32() || - !args[2]->IsInt32()) { + if (info.Length() < 3 || + !info[0]->IsInt32() || + !info[2]->IsInt32()) { return THROW_BAD_ARGS; } - int fd = args[0]->Int32Value(); - ASSERT_OFFSET(args[1]); - off_t offs = GET_OFFSET(args[1]); - int whence = args[2]->Int32Value(); + int fd = info[0]->Int32Value(); + ASSERT_OFFSET(info[1]); + off_t offs = GET_OFFSET(info[1]); + int whence = info[2]->Int32Value(); - if ( ! args[3]->IsFunction()) { + if ( ! info[3]->IsFunction()) { off_t offs_result = lseek(fd, offs, whence); - if (offs_result == -1) return NanThrowError(NanErrnoException(errno)); - NanReturnValue(NanNew(offs_result)); + if (offs_result == -1) return Nan::ThrowError(Nan::NanErrnoException(errno)); + info.GetReturnValue().Set(Nan::New(offs_result)); } store_data_t* seek_data = new store_data_t(); - seek_data->cb = new NanCallback((Local) args[3].As()); + seek_data->cb = new Nan::Callback((Local) info[3].As()); seek_data->fs_op = FS_OP_SEEK; seek_data->fd = fd; seek_data->offset = offs; @@ -358,34 +354,32 @@ static NAN_METHOD(Seek) { req->data = seek_data; uv_queue_work(uv_default_loop(), req, EIO_Seek, (uv_after_work_cb)EIO_After); - NanReturnUndefined(); + info.GetReturnValue().SetUndefined(); } // fs.fcntl(fd, cmd, [arg]) static NAN_METHOD(Fcntl) { - NanScope(); - - if (args.Length() < 3 || - !args[0]->IsInt32() || - !args[1]->IsInt32() || - !args[2]->IsInt32()) { + if (info.Length() < 3 || + !info[0]->IsInt32() || + !info[1]->IsInt32() || + !info[2]->IsInt32()) { return THROW_BAD_ARGS; } - int fd = args[0]->Int32Value(); - int cmd = args[1]->Int32Value(); - int arg = args[2]->Int32Value(); + int fd = info[0]->Int32Value(); + int cmd = info[1]->Int32Value(); + int arg = info[2]->Int32Value(); - if ( ! args[3]->IsFunction()) { + if ( ! info[3]->IsFunction()) { int result = fcntl(fd, cmd, arg); - if (result == -1) return NanThrowError(NanErrnoException(errno)); - NanReturnValue(NanNew(result)); + if (result == -1) return Nan::ThrowError(Nan::NanErrnoException(errno)); + info.GetReturnValue().Set(Nan::New(result)); } store_data_t* data = new store_data_t(); - data->cb = new NanCallback((Local) args[3].As()); + data->cb = new Nan::Callback((Local) info[3].As()); data->fs_op = FS_OP_FCNTL; data->fd = fd; data->oper = cmd; @@ -395,7 +389,7 @@ static NAN_METHOD(Fcntl) { req->data = data; uv_queue_work(uv_default_loop(), req, EIO_Fcntl, (uv_after_work_cb)EIO_After); - NanReturnUndefined(); + info.GetReturnValue().SetUndefined(); } @@ -418,33 +412,31 @@ static void EIO_UTime(uv_work_t *req) { // fs.utime( path, atime, mtime, [callback] ) static NAN_METHOD(UTime) { - NanScope(); - - if (args.Length() < 3 || - args.Length() > 4 || - !args[0]->IsString() || - !args[1]->IsNumber() || - !args[2]->IsNumber() ) { + if (info.Length() < 3 || + info.Length() > 4 || + !info[0]->IsString() || + !info[1]->IsNumber() || + !info[2]->IsNumber() ) { return THROW_BAD_ARGS; } - String::Utf8Value path(args[0]->ToString()); - time_t atime = args[1]->IntegerValue(); - time_t mtime = args[2]->IntegerValue(); + String::Utf8Value path(info[0]->ToString()); + time_t atime = info[1]->IntegerValue(); + time_t mtime = info[2]->IntegerValue(); // Synchronous call needs much less work - if ( ! args[3]->IsFunction()) { + if ( ! info[3]->IsFunction()) { struct utimbuf buf; buf.actime = atime; buf.modtime = mtime; int ret = utime(*path, &buf); - if (ret != 0) return NanThrowError(NanErrnoException(errno, "utime", "", *path)); - NanReturnUndefined(); + if (ret != 0) return Nan::ThrowError(Nan::NanErrnoException(errno, "utime", "", *path)); + info.GetReturnValue().SetUndefined(); } store_data_t* utime_data = new store_data_t(); - utime_data->cb = new NanCallback((Local) args[3].As()); + utime_data->cb = new Nan::Callback((Local) info[3].As()); utime_data->fs_op = FS_OP_UTIME; utime_data->path = strdup(*path); utime_data->utime_buf.actime = atime; @@ -454,49 +446,47 @@ static NAN_METHOD(UTime) { req->data = utime_data; uv_queue_work(uv_default_loop(), req, EIO_UTime, (uv_after_work_cb)EIO_After); - NanReturnUndefined(); + info.GetReturnValue().SetUndefined(); } // Wrapper for statvfs(2). // fs.statVFS( path, [callback] ) static NAN_METHOD(StatVFS) { - NanScope(); - - if (args.Length() < 1 || - !args[0]->IsString() ) { + if (info.Length() < 1 || + !info[0]->IsString() ) { return THROW_BAD_ARGS; } - String::Utf8Value path(args[0]->ToString()); + String::Utf8Value path(info[0]->ToString()); // Synchronous call needs much less work - if (!args[1]->IsFunction()) { + if (!info[1]->IsFunction()) { #ifndef _WIN32 struct statvfs buf; int ret = statvfs(*path, &buf); - if (ret != 0) return NanThrowError(NanErrnoException(errno, "statvfs", "", *path)); - Handle result = NanNew(); - result->Set(NanNew(f_namemax_symbol), NanNew(static_cast(buf.f_namemax))); - result->Set(NanNew(f_bsize_symbol), NanNew(static_cast(buf.f_bsize))); - result->Set(NanNew(f_frsize_symbol), NanNew(static_cast(buf.f_frsize))); + if (ret != 0) return Nan::ThrowError(Nan::NanErrnoException(errno, "statvfs", "", *path)); + Handle result = Nan::New(); + result->Set(Nan::New(f_namemax_symbol), Nan::New(static_cast(buf.f_namemax))); + result->Set(Nan::New(f_bsize_symbol), Nan::New(static_cast(buf.f_bsize))); + result->Set(Nan::New(f_frsize_symbol), Nan::New(static_cast(buf.f_frsize))); - result->Set(NanNew(f_blocks_symbol), NanNew(buf.f_blocks)); - result->Set(NanNew(f_bavail_symbol), NanNew(buf.f_bavail)); - result->Set(NanNew(f_bfree_symbol), NanNew(buf.f_bfree)); + result->Set(Nan::New(f_blocks_symbol), Nan::New(buf.f_blocks)); + result->Set(Nan::New(f_bavail_symbol), Nan::New(buf.f_bavail)); + result->Set(Nan::New(f_bfree_symbol), Nan::New(buf.f_bfree)); - result->Set(NanNew(f_files_symbol), NanNew(buf.f_files)); - result->Set(NanNew(f_favail_symbol), NanNew(buf.f_favail)); - result->Set(NanNew(f_ffree_symbol), NanNew(buf.f_ffree)); - NanReturnValue(result); + result->Set(Nan::New(f_files_symbol), Nan::New(buf.f_files)); + result->Set(Nan::New(f_favail_symbol), Nan::New(buf.f_favail)); + result->Set(Nan::New(f_ffree_symbol), Nan::New(buf.f_ffree)); + info.GetReturnValue().Set(result); #else - NanReturnUndefined(); + info.GetReturnValue().SetUndefined(); #endif } store_data_t* statvfs_data = new store_data_t(); - statvfs_data->cb = new NanCallback((Local) args[1].As()); + statvfs_data->cb = new Nan::Callback((Local) info[1].As()); statvfs_data->fs_op = FS_OP_STATVFS; statvfs_data->path = strdup(*path); @@ -504,13 +494,13 @@ static NAN_METHOD(StatVFS) { req->data = statvfs_data; uv_queue_work(uv_default_loop(), req, EIO_StatVFS,(uv_after_work_cb)EIO_After); - NanReturnUndefined(); + info.GetReturnValue().SetUndefined(); } -extern "C" void -init (Handle target) +extern "C" +NAN_MODULE_INIT(init) { - NanScope(); + Nan::HandleScope scope; #ifdef _WIN32 _set_invalid_parameter_handler(uv__crt_invalid_parameter_handler); @@ -556,23 +546,23 @@ init (Handle target) NODE_DEFINE_CONSTANT(target, FD_CLOEXEC); #endif - NODE_SET_METHOD(target, "seek", Seek); - NODE_SET_METHOD(target, "fcntl", Fcntl); - NODE_SET_METHOD(target, "flock", Flock); - NODE_SET_METHOD(target, "utime", UTime); - NODE_SET_METHOD(target, "statVFS", StatVFS); + Nan::Set(target, Nan::New("seek").ToLocalChecked(), Nan::New(Seek)->GetFunction()); + Nan::Set(target, Nan::New("fcntl").ToLocalChecked(), Nan::New(Fcntl)->GetFunction()); + Nan::Set(target, Nan::New("flock").ToLocalChecked(), Nan::New(Flock)->GetFunction()); + Nan::Set(target, Nan::New("utime").ToLocalChecked(), Nan::New(UTime)->GetFunction()); + Nan::Set(target, Nan::New("statVFS").ToLocalChecked(), Nan::New(StatVFS)->GetFunction()); #ifndef _WIN32 - NanAssignPersistent(f_namemax_symbol, NanNew("f_namemax")); - NanAssignPersistent(f_bsize_symbol, NanNew("f_bsize")); - NanAssignPersistent(f_frsize_symbol, NanNew("f_frsize")); + NanAssignPersistent(f_namemax_symbol, Nan::New("f_namemax").ToLocalChecked()); + NanAssignPersistent(f_bsize_symbol, Nan::New("f_bsize").ToLocalChecked()); + NanAssignPersistent(f_frsize_symbol, Nan::New("f_frsize").ToLocalChecked()); - NanAssignPersistent(f_blocks_symbol, NanNew("f_blocks")); - NanAssignPersistent(f_bavail_symbol, NanNew("f_bavail")); - NanAssignPersistent(f_bfree_symbol, NanNew("f_bfree")); + NanAssignPersistent(f_blocks_symbol, Nan::New("f_blocks").ToLocalChecked()); + NanAssignPersistent(f_bavail_symbol, Nan::New("f_bavail").ToLocalChecked()); + NanAssignPersistent(f_bfree_symbol, Nan::New("f_bfree").ToLocalChecked()); - NanAssignPersistent(f_files_symbol, NanNew("f_files")); - NanAssignPersistent(f_favail_symbol, NanNew("f_favail")); - NanAssignPersistent(f_ffree_symbol, NanNew("f_ffree")); + NanAssignPersistent(f_files_symbol, Nan::New("f_files").ToLocalChecked()); + NanAssignPersistent(f_favail_symbol, Nan::New("f_favail").ToLocalChecked()); + NanAssignPersistent(f_ffree_symbol, Nan::New("f_ffree").ToLocalChecked()); #endif #ifdef _WIN32 From ba66743239802fe8bc9ac51f9572b66b015a25c5 Mon Sep 17 00:00:00 2001 From: Geoff Greer Date: Sun, 30 Aug 2015 00:37:42 -0700 Subject: [PATCH 22/33] Hooray, it compiles. --- fs-ext.cc | 82 +++++++++++++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/fs-ext.cc b/fs-ext.cc index d1f1e6a..5a820eb 100644 --- a/fs-ext.cc +++ b/fs-ext.cc @@ -63,17 +63,17 @@ struct store_data_t { }; #ifndef _WIN32 -static Persistent f_namemax_symbol; -static Persistent f_bsize_symbol; -static Persistent f_frsize_symbol; +static Nan::Persistent f_namemax_symbol; +static Nan::Persistent f_bsize_symbol; +static Nan::Persistent f_frsize_symbol; -static Persistent f_blocks_symbol; -static Persistent f_bavail_symbol; -static Persistent f_bfree_symbol; +static Nan::Persistent f_blocks_symbol; +static Nan::Persistent f_bavail_symbol; +static Nan::Persistent f_bfree_symbol; -static Persistent f_files_symbol; -static Persistent f_favail_symbol; -static Persistent f_ffree_symbol; +static Nan::Persistent f_files_symbol; +static Nan::Persistent f_favail_symbol; +static Nan::Persistent f_ffree_symbol; #endif #ifdef _WIN32 @@ -128,15 +128,15 @@ static void EIO_After(uv_work_t *req) { argc = 2; statvfs_result = Nan::New(); argv[1] = statvfs_result; - statvfs_result->Set(Nan::New(f_namemax_symbol), Nan::New(static_cast(store_data->statvfs_buf.f_namemax))); - statvfs_result->Set(Nan::New(f_bsize_symbol), Nan::New(static_cast(store_data->statvfs_buf.f_bsize))); - statvfs_result->Set(Nan::New(f_frsize_symbol), Nan::New(static_cast(store_data->statvfs_buf.f_frsize))); - statvfs_result->Set(Nan::New(f_blocks_symbol), Nan::New(store_data->statvfs_buf.f_blocks)); - statvfs_result->Set(Nan::New(f_bavail_symbol), Nan::New(store_data->statvfs_buf.f_bavail)); - statvfs_result->Set(Nan::New(f_bfree_symbol), Nan::New(store_data->statvfs_buf.f_bfree)); - statvfs_result->Set(Nan::New(f_files_symbol), Nan::New(store_data->statvfs_buf.f_files)); - statvfs_result->Set(Nan::New(f_favail_symbol), Nan::New(store_data->statvfs_buf.f_favail)); - statvfs_result->Set(Nan::New(f_ffree_symbol), Nan::New(store_data->statvfs_buf.f_ffree)); + Nan::Set(statvfs_result, Nan::New(f_namemax_symbol), Nan::New(static_cast(store_data->statvfs_buf.f_namemax))); + Nan::Set(statvfs_result, Nan::New(f_bsize_symbol), Nan::New(static_cast(store_data->statvfs_buf.f_bsize))); + Nan::Set(statvfs_result, Nan::New(f_frsize_symbol), Nan::New(static_cast(store_data->statvfs_buf.f_frsize))); + Nan::Set(statvfs_result, Nan::New(f_blocks_symbol), Nan::New(store_data->statvfs_buf.f_blocks)); + Nan::Set(statvfs_result, Nan::New(f_bavail_symbol), Nan::New(store_data->statvfs_buf.f_bavail)); + Nan::Set(statvfs_result, Nan::New(f_bfree_symbol), Nan::New(store_data->statvfs_buf.f_bfree)); + Nan::Set(statvfs_result, Nan::New(f_files_symbol), Nan::New(store_data->statvfs_buf.f_files)); + Nan::Set(statvfs_result, Nan::New(f_favail_symbol), Nan::New(store_data->statvfs_buf.f_favail)); + Nan::Set(statvfs_result, Nan::New(f_ffree_symbol), Nan::New(store_data->statvfs_buf.f_ffree)); #else argc = 1; #endif @@ -467,17 +467,17 @@ static NAN_METHOD(StatVFS) { int ret = statvfs(*path, &buf); if (ret != 0) return Nan::ThrowError(Nan::NanErrnoException(errno, "statvfs", "", *path)); Handle result = Nan::New(); - result->Set(Nan::New(f_namemax_symbol), Nan::New(static_cast(buf.f_namemax))); - result->Set(Nan::New(f_bsize_symbol), Nan::New(static_cast(buf.f_bsize))); - result->Set(Nan::New(f_frsize_symbol), Nan::New(static_cast(buf.f_frsize))); + Nan::Set(result, Nan::New(f_namemax_symbol), Nan::New(static_cast(buf.f_namemax))); + Nan::Set(result, Nan::New(f_bsize_symbol), Nan::New(static_cast(buf.f_bsize))); + Nan::Set(result, Nan::New(f_frsize_symbol), Nan::New(static_cast(buf.f_frsize))); - result->Set(Nan::New(f_blocks_symbol), Nan::New(buf.f_blocks)); - result->Set(Nan::New(f_bavail_symbol), Nan::New(buf.f_bavail)); - result->Set(Nan::New(f_bfree_symbol), Nan::New(buf.f_bfree)); + Nan::Set(result, Nan::New(f_blocks_symbol), Nan::New(buf.f_blocks)); + Nan::Set(result, Nan::New(f_bavail_symbol), Nan::New(buf.f_bavail)); + Nan::Set(result, Nan::New(f_bfree_symbol), Nan::New(buf.f_bfree)); - result->Set(Nan::New(f_files_symbol), Nan::New(buf.f_files)); - result->Set(Nan::New(f_favail_symbol), Nan::New(buf.f_favail)); - result->Set(Nan::New(f_ffree_symbol), Nan::New(buf.f_ffree)); + Nan::Set(result, Nan::New(f_files_symbol), Nan::New(buf.f_files)); + Nan::Set(result, Nan::New(f_favail_symbol), Nan::New(buf.f_favail)); + Nan::Set(result, Nan::New(f_ffree_symbol), Nan::New(buf.f_ffree)); info.GetReturnValue().Set(result); #else info.GetReturnValue().SetUndefined(); @@ -546,23 +546,23 @@ NAN_MODULE_INIT(init) NODE_DEFINE_CONSTANT(target, FD_CLOEXEC); #endif - Nan::Set(target, Nan::New("seek").ToLocalChecked(), Nan::New(Seek)->GetFunction()); - Nan::Set(target, Nan::New("fcntl").ToLocalChecked(), Nan::New(Fcntl)->GetFunction()); - Nan::Set(target, Nan::New("flock").ToLocalChecked(), Nan::New(Flock)->GetFunction()); - Nan::Set(target, Nan::New("utime").ToLocalChecked(), Nan::New(UTime)->GetFunction()); - Nan::Set(target, Nan::New("statVFS").ToLocalChecked(), Nan::New(StatVFS)->GetFunction()); + Nan::Set(target, Nan::New("seek").ToLocalChecked(), Nan::GetFunction(Nan::New(Seek)).ToLocalChecked()); + Nan::Set(target, Nan::New("fcntl").ToLocalChecked(), Nan::GetFunction(Nan::New(Fcntl)).ToLocalChecked()); + Nan::Set(target, Nan::New("flock").ToLocalChecked(), Nan::GetFunction(Nan::New(Flock)).ToLocalChecked()); + Nan::Set(target, Nan::New("utime").ToLocalChecked(), Nan::GetFunction(Nan::New(UTime)).ToLocalChecked()); + Nan::Set(target, Nan::New("statVFS").ToLocalChecked(), Nan::GetFunction(Nan::New(StatVFS)).ToLocalChecked()); #ifndef _WIN32 - NanAssignPersistent(f_namemax_symbol, Nan::New("f_namemax").ToLocalChecked()); - NanAssignPersistent(f_bsize_symbol, Nan::New("f_bsize").ToLocalChecked()); - NanAssignPersistent(f_frsize_symbol, Nan::New("f_frsize").ToLocalChecked()); + f_namemax_symbol.Reset(Nan::New("f_namemax").ToLocalChecked()); + f_bsize_symbol.Reset(Nan::New("f_bsize").ToLocalChecked()); + f_frsize_symbol.Reset(Nan::New("f_frsize").ToLocalChecked()); - NanAssignPersistent(f_blocks_symbol, Nan::New("f_blocks").ToLocalChecked()); - NanAssignPersistent(f_bavail_symbol, Nan::New("f_bavail").ToLocalChecked()); - NanAssignPersistent(f_bfree_symbol, Nan::New("f_bfree").ToLocalChecked()); + f_blocks_symbol.Reset(Nan::New("f_blocks").ToLocalChecked()); + f_bavail_symbol.Reset(Nan::New("f_bavail").ToLocalChecked()); + f_bfree_symbol.Reset(Nan::New("f_bfree").ToLocalChecked()); - NanAssignPersistent(f_files_symbol, Nan::New("f_files").ToLocalChecked()); - NanAssignPersistent(f_favail_symbol, Nan::New("f_favail").ToLocalChecked()); - NanAssignPersistent(f_ffree_symbol, Nan::New("f_ffree").ToLocalChecked()); + f_files_symbol.Reset(Nan::New("f_files").ToLocalChecked()); + f_favail_symbol.Reset(Nan::New("f_favail").ToLocalChecked()); + f_ffree_symbol.Reset(Nan::New("f_ffree").ToLocalChecked()); #endif #ifdef _WIN32 From 5e48877ffcb373e9d877965c17012fd8d7a2da49 Mon Sep 17 00:00:00 2001 From: Geoff Greer Date: Sun, 30 Aug 2015 16:40:22 -0700 Subject: [PATCH 23/33] Return after setting return value for sync methods. Fixes tests. --- fs-ext.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs-ext.cc b/fs-ext.cc index 5a820eb..996ee41 100644 --- a/fs-ext.cc +++ b/fs-ext.cc @@ -340,6 +340,7 @@ static NAN_METHOD(Seek) { off_t offs_result = lseek(fd, offs, whence); if (offs_result == -1) return Nan::ThrowError(Nan::NanErrnoException(errno)); info.GetReturnValue().Set(Nan::New(offs_result)); + return; } store_data_t* seek_data = new store_data_t(); @@ -375,6 +376,7 @@ static NAN_METHOD(Fcntl) { int result = fcntl(fd, cmd, arg); if (result == -1) return Nan::ThrowError(Nan::NanErrnoException(errno)); info.GetReturnValue().Set(Nan::New(result)); + return; } store_data_t* data = new store_data_t(); @@ -432,6 +434,7 @@ static NAN_METHOD(UTime) { int ret = utime(*path, &buf); if (ret != 0) return Nan::ThrowError(Nan::NanErrnoException(errno, "utime", "", *path)); info.GetReturnValue().SetUndefined(); + return; } store_data_t* utime_data = new store_data_t(); @@ -482,6 +485,7 @@ static NAN_METHOD(StatVFS) { #else info.GetReturnValue().SetUndefined(); #endif + return; } store_data_t* statvfs_data = new store_data_t(); From 20155690d77dd58e9a0418aa7a5f23a7942b6c29 Mon Sep 17 00:00:00 2001 From: Geoff Greer Date: Sun, 30 Aug 2015 16:45:25 -0700 Subject: [PATCH 24/33] Cleanup. Prefer methods to explicit function invocations. You never know when someone might use inheritance later. --- fs-ext.cc | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/fs-ext.cc b/fs-ext.cc index 996ee41..a3e3162 100644 --- a/fs-ext.cc +++ b/fs-ext.cc @@ -128,15 +128,15 @@ static void EIO_After(uv_work_t *req) { argc = 2; statvfs_result = Nan::New(); argv[1] = statvfs_result; - Nan::Set(statvfs_result, Nan::New(f_namemax_symbol), Nan::New(static_cast(store_data->statvfs_buf.f_namemax))); - Nan::Set(statvfs_result, Nan::New(f_bsize_symbol), Nan::New(static_cast(store_data->statvfs_buf.f_bsize))); - Nan::Set(statvfs_result, Nan::New(f_frsize_symbol), Nan::New(static_cast(store_data->statvfs_buf.f_frsize))); - Nan::Set(statvfs_result, Nan::New(f_blocks_symbol), Nan::New(store_data->statvfs_buf.f_blocks)); - Nan::Set(statvfs_result, Nan::New(f_bavail_symbol), Nan::New(store_data->statvfs_buf.f_bavail)); - Nan::Set(statvfs_result, Nan::New(f_bfree_symbol), Nan::New(store_data->statvfs_buf.f_bfree)); - Nan::Set(statvfs_result, Nan::New(f_files_symbol), Nan::New(store_data->statvfs_buf.f_files)); - Nan::Set(statvfs_result, Nan::New(f_favail_symbol), Nan::New(store_data->statvfs_buf.f_favail)); - Nan::Set(statvfs_result, Nan::New(f_ffree_symbol), Nan::New(store_data->statvfs_buf.f_ffree)); + statvfs_result->Set(Nan::New(f_namemax_symbol), Nan::New(static_cast(store_data->statvfs_buf.f_namemax))); + statvfs_result->Set(Nan::New(f_bsize_symbol), Nan::New(static_cast(store_data->statvfs_buf.f_bsize))); + statvfs_result->Set(Nan::New(f_frsize_symbol), Nan::New(static_cast(store_data->statvfs_buf.f_frsize))); + statvfs_result->Set(Nan::New(f_blocks_symbol), Nan::New(store_data->statvfs_buf.f_blocks)); + statvfs_result->Set(Nan::New(f_bavail_symbol), Nan::New(store_data->statvfs_buf.f_bavail)); + statvfs_result->Set(Nan::New(f_bfree_symbol), Nan::New(store_data->statvfs_buf.f_bfree)); + statvfs_result->Set(Nan::New(f_files_symbol), Nan::New(store_data->statvfs_buf.f_files)); + statvfs_result->Set(Nan::New(f_favail_symbol), Nan::New(store_data->statvfs_buf.f_favail)); + statvfs_result->Set(Nan::New(f_ffree_symbol), Nan::New(store_data->statvfs_buf.f_ffree)); #else argc = 1; #endif @@ -469,18 +469,18 @@ static NAN_METHOD(StatVFS) { struct statvfs buf; int ret = statvfs(*path, &buf); if (ret != 0) return Nan::ThrowError(Nan::NanErrnoException(errno, "statvfs", "", *path)); - Handle result = Nan::New(); - Nan::Set(result, Nan::New(f_namemax_symbol), Nan::New(static_cast(buf.f_namemax))); - Nan::Set(result, Nan::New(f_bsize_symbol), Nan::New(static_cast(buf.f_bsize))); - Nan::Set(result, Nan::New(f_frsize_symbol), Nan::New(static_cast(buf.f_frsize))); + Local result = Nan::New(); + result->Set(Nan::New(f_namemax_symbol), Nan::New(static_cast(buf.f_namemax))); + result->Set(Nan::New(f_bsize_symbol), Nan::New(static_cast(buf.f_bsize))); + result->Set(Nan::New(f_frsize_symbol), Nan::New(static_cast(buf.f_frsize))); - Nan::Set(result, Nan::New(f_blocks_symbol), Nan::New(buf.f_blocks)); - Nan::Set(result, Nan::New(f_bavail_symbol), Nan::New(buf.f_bavail)); - Nan::Set(result, Nan::New(f_bfree_symbol), Nan::New(buf.f_bfree)); + result->Set(Nan::New(f_blocks_symbol), Nan::New(buf.f_blocks)); + result->Set(Nan::New(f_bavail_symbol), Nan::New(buf.f_bavail)); + result->Set(Nan::New(f_bfree_symbol), Nan::New(buf.f_bfree)); - Nan::Set(result, Nan::New(f_files_symbol), Nan::New(buf.f_files)); - Nan::Set(result, Nan::New(f_favail_symbol), Nan::New(buf.f_favail)); - Nan::Set(result, Nan::New(f_ffree_symbol), Nan::New(buf.f_ffree)); + result->Set(Nan::New(f_files_symbol), Nan::New(buf.f_files)); + result->Set(Nan::New(f_favail_symbol), Nan::New(buf.f_favail)); + result->Set(Nan::New(f_ffree_symbol), Nan::New(buf.f_ffree)); info.GetReturnValue().Set(result); #else info.GetReturnValue().SetUndefined(); @@ -550,11 +550,11 @@ NAN_MODULE_INIT(init) NODE_DEFINE_CONSTANT(target, FD_CLOEXEC); #endif - Nan::Set(target, Nan::New("seek").ToLocalChecked(), Nan::GetFunction(Nan::New(Seek)).ToLocalChecked()); - Nan::Set(target, Nan::New("fcntl").ToLocalChecked(), Nan::GetFunction(Nan::New(Fcntl)).ToLocalChecked()); - Nan::Set(target, Nan::New("flock").ToLocalChecked(), Nan::GetFunction(Nan::New(Flock)).ToLocalChecked()); - Nan::Set(target, Nan::New("utime").ToLocalChecked(), Nan::GetFunction(Nan::New(UTime)).ToLocalChecked()); - Nan::Set(target, Nan::New("statVFS").ToLocalChecked(), Nan::GetFunction(Nan::New(StatVFS)).ToLocalChecked()); + target->Set(Nan::New("seek").ToLocalChecked(), Nan::New(Seek)->GetFunction()); + target->Set(Nan::New("fcntl").ToLocalChecked(), Nan::New(Fcntl)->GetFunction()); + target->Set(Nan::New("flock").ToLocalChecked(), Nan::New(Flock)->GetFunction()); + target->Set(Nan::New("utime").ToLocalChecked(), Nan::New(UTime)->GetFunction()); + target->Set(Nan::New("statVFS").ToLocalChecked(), Nan::New(StatVFS)->GetFunction()); #ifndef _WIN32 f_namemax_symbol.Reset(Nan::New("f_namemax").ToLocalChecked()); f_bsize_symbol.Reset(Nan::New("f_bsize").ToLocalChecked()); From e03e22f60b496301484c5d535334709e3d181917 Mon Sep 17 00:00:00 2001 From: Peter Sorowka Date: Fri, 15 Jan 2016 23:51:42 +0100 Subject: [PATCH 25/33] Replace deprecated NanErrnoException with ErrnoException --- fs-ext.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs-ext.cc b/fs-ext.cc index a3e3162..86279fe 100644 --- a/fs-ext.cc +++ b/fs-ext.cc @@ -107,7 +107,7 @@ static void EIO_After(uv_work_t *req) { // for a success, which is possible. if (store_data->result == -1) { // If the request doesn't have a path parameter set. - argv[0] = Nan::NanErrnoException(store_data->error); + argv[0] = Nan::ErrnoException(store_data->error); } else { // error value is empty or null for non-error. argv[0] = Nan::Null(); @@ -296,7 +296,7 @@ static NAN_METHOD(Flock) { int i = flock(flock_data->fd, flock_data->oper); #endif delete flock_data; - if (i != 0) return Nan::ThrowError(Nan::NanErrnoException(errno)); + if (i != 0) return Nan::ThrowError(Nan::ErrnoException(errno)); info.GetReturnValue().SetUndefined(); } } @@ -338,7 +338,7 @@ static NAN_METHOD(Seek) { if ( ! info[3]->IsFunction()) { off_t offs_result = lseek(fd, offs, whence); - if (offs_result == -1) return Nan::ThrowError(Nan::NanErrnoException(errno)); + if (offs_result == -1) return Nan::ThrowError(Nan::ErrnoException(errno)); info.GetReturnValue().Set(Nan::New(offs_result)); return; } @@ -374,7 +374,7 @@ static NAN_METHOD(Fcntl) { if ( ! info[3]->IsFunction()) { int result = fcntl(fd, cmd, arg); - if (result == -1) return Nan::ThrowError(Nan::NanErrnoException(errno)); + if (result == -1) return Nan::ThrowError(Nan::ErrnoException(errno)); info.GetReturnValue().Set(Nan::New(result)); return; } @@ -432,7 +432,7 @@ static NAN_METHOD(UTime) { buf.actime = atime; buf.modtime = mtime; int ret = utime(*path, &buf); - if (ret != 0) return Nan::ThrowError(Nan::NanErrnoException(errno, "utime", "", *path)); + if (ret != 0) return Nan::ThrowError(Nan::ErrnoException(errno, "utime", "", *path)); info.GetReturnValue().SetUndefined(); return; } @@ -468,7 +468,7 @@ static NAN_METHOD(StatVFS) { #ifndef _WIN32 struct statvfs buf; int ret = statvfs(*path, &buf); - if (ret != 0) return Nan::ThrowError(Nan::NanErrnoException(errno, "statvfs", "", *path)); + if (ret != 0) return Nan::ThrowError(Nan::ErrnoException(errno, "statvfs", "", *path)); Local result = Nan::New(); result->Set(Nan::New(f_namemax_symbol), Nan::New(static_cast(buf.f_namemax))); result->Set(Nan::New(f_bsize_symbol), Nan::New(static_cast(buf.f_bsize))); From 035e9cc4d8cb2fc36d8e9dbdd71cf7b6b04575a4 Mon Sep 17 00:00:00 2001 From: TheRoSS Date: Tue, 7 Mar 2017 14:48:43 +0300 Subject: [PATCH 26/33] fix, Segmentation fault while running test 'fd value is negative' --- fs-ext.cc | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/fs-ext.cc b/fs-ext.cc index 86279fe..5de33f0 100644 --- a/fs-ext.cc +++ b/fs-ext.cc @@ -167,14 +167,14 @@ static void EIO_After(uv_work_t *req) { static void EIO_StatVFS(uv_work_t *req) { store_data_t* statvfs_data = static_cast(req->data); statvfs_data->result = 0; -#ifndef _WIN32 +#ifndef _WIN32 struct statvfs *data = &(statvfs_data->statvfs_buf); if (statvfs(statvfs_data->path, data)) { statvfs_data->result = -1; memset(data, 0, sizeof(struct statvfs)); }; #endif - free(statvfs_data->path); + free(statvfs_data->path); ; } @@ -219,7 +219,7 @@ static int _win32_flock(int fd, int oper) { fh = (HANDLE)_get_osfhandle(fd); if (fh == (HANDLE)-1) return -1; - + memset(&o, 0, sizeof(o)); switch(oper) { @@ -266,7 +266,7 @@ static void EIO_Flock(uv_work_t *req) { #else int i = flock(flock_data->fd, flock_data->oper); #endif - + flock_data->result = i; flock_data->error = errno; @@ -278,7 +278,7 @@ static NAN_METHOD(Flock) { } store_data_t* flock_data = new store_data_t(); - + flock_data->fs_op = FS_OP_FLOCK; flock_data->fd = info[0]->Int32Value(); flock_data->oper = info[1]->Int32Value(); @@ -296,7 +296,7 @@ static NAN_METHOD(Flock) { int i = flock(flock_data->fd, flock_data->oper); #endif delete flock_data; - if (i != 0) return Nan::ThrowError(Nan::ErrnoException(errno)); + if (i != 0) return Nan::ThrowError(Nan::ErrnoException(errno, "Flock", "")); info.GetReturnValue().SetUndefined(); } } @@ -325,8 +325,8 @@ static inline int IsInt64(double x) { // fs.seek(fd, position, whence [, callback] ) static NAN_METHOD(Seek) { - if (info.Length() < 3 || - !info[0]->IsInt32() || + if (info.Length() < 3 || + !info[0]->IsInt32() || !info[2]->IsInt32()) { return THROW_BAD_ARGS; } @@ -407,7 +407,7 @@ static void EIO_UTime(uv_work_t *req) { } else { utime_data->result = i; } - + } // Wrapper for utime(2). @@ -462,10 +462,10 @@ static NAN_METHOD(StatVFS) { } String::Utf8Value path(info[0]->ToString()); - + // Synchronous call needs much less work if (!info[1]->IsFunction()) { -#ifndef _WIN32 +#ifndef _WIN32 struct statvfs buf; int ret = statvfs(*path, &buf); if (ret != 0) return Nan::ThrowError(Nan::ErrnoException(errno, "statvfs", "", *path)); @@ -473,11 +473,11 @@ static NAN_METHOD(StatVFS) { result->Set(Nan::New(f_namemax_symbol), Nan::New(static_cast(buf.f_namemax))); result->Set(Nan::New(f_bsize_symbol), Nan::New(static_cast(buf.f_bsize))); result->Set(Nan::New(f_frsize_symbol), Nan::New(static_cast(buf.f_frsize))); - + result->Set(Nan::New(f_blocks_symbol), Nan::New(buf.f_blocks)); result->Set(Nan::New(f_bavail_symbol), Nan::New(buf.f_bavail)); result->Set(Nan::New(f_bfree_symbol), Nan::New(buf.f_bfree)); - + result->Set(Nan::New(f_files_symbol), Nan::New(buf.f_files)); result->Set(Nan::New(f_favail_symbol), Nan::New(buf.f_favail)); result->Set(Nan::New(f_ffree_symbol), Nan::New(buf.f_ffree)); @@ -559,11 +559,11 @@ NAN_MODULE_INIT(init) f_namemax_symbol.Reset(Nan::New("f_namemax").ToLocalChecked()); f_bsize_symbol.Reset(Nan::New("f_bsize").ToLocalChecked()); f_frsize_symbol.Reset(Nan::New("f_frsize").ToLocalChecked()); - + f_blocks_symbol.Reset(Nan::New("f_blocks").ToLocalChecked()); f_bavail_symbol.Reset(Nan::New("f_bavail").ToLocalChecked()); f_bfree_symbol.Reset(Nan::New("f_bfree").ToLocalChecked()); - + f_files_symbol.Reset(Nan::New("f_files").ToLocalChecked()); f_favail_symbol.Reset(Nan::New("f_favail").ToLocalChecked()); f_ffree_symbol.Reset(Nan::New("f_ffree").ToLocalChecked()); From 4055b249c7d955d3d4ce1948e93dcdc3ffc16ab1 Mon Sep 17 00:00:00 2001 From: TheRoSS Date: Tue, 7 Mar 2017 14:59:28 +0300 Subject: [PATCH 27/33] fix, Segmentation faults on node 0.10(.48) --- fs-ext.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs-ext.cc b/fs-ext.cc index 5de33f0..559bfb4 100644 --- a/fs-ext.cc +++ b/fs-ext.cc @@ -107,7 +107,7 @@ static void EIO_After(uv_work_t *req) { // for a success, which is possible. if (store_data->result == -1) { // If the request doesn't have a path parameter set. - argv[0] = Nan::ErrnoException(store_data->error); + argv[0] = Nan::ErrnoException(store_data->error, "EIO_After", ""); } else { // error value is empty or null for non-error. argv[0] = Nan::Null(); @@ -338,7 +338,7 @@ static NAN_METHOD(Seek) { if ( ! info[3]->IsFunction()) { off_t offs_result = lseek(fd, offs, whence); - if (offs_result == -1) return Nan::ThrowError(Nan::ErrnoException(errno)); + if (offs_result == -1) return Nan::ThrowError(Nan::ErrnoException(errno, "Seek", "")); info.GetReturnValue().Set(Nan::New(offs_result)); return; } @@ -374,7 +374,7 @@ static NAN_METHOD(Fcntl) { if ( ! info[3]->IsFunction()) { int result = fcntl(fd, cmd, arg); - if (result == -1) return Nan::ThrowError(Nan::ErrnoException(errno)); + if (result == -1) return Nan::ThrowError(Nan::ErrnoException(errno, "Fcntl", "")); info.GetReturnValue().Set(Nan::New(result)); return; } From 604d4e39440cc95b6b1b8cca9c5ac45e4be49c06 Mon Sep 17 00:00:00 2001 From: Matt Sergeant Date: Tue, 7 Mar 2017 08:51:52 -0500 Subject: [PATCH 28/33] Add eslint and travis and codecov --- .eslintrc | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ .travis.yml | 23 ++++++++++++++++++++++ package.json | 8 +++++++- 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 .eslintrc create mode 100644 .travis.yml diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..ee42d67 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,54 @@ +{ + "env": { + "node": true, + "es6": true + }, + "rules": { + "dot-notation": 2, + "indent": [2, 4, {"SwitchCase": 1}], + "one-var": [2, "never"], + "no-trailing-spaces": [2, { "skipBlankLines": true }], + "keyword-spacing": [2, { + "overrides": { }, + "before": true, + "after": true + }], + "no-delete-var": 2, + "no-label-var": 2, + "no-shadow-restricted-names": 2, + "no-unused-vars": [ 1, { "args": "none" }], + "no-undef": 2, + "no-undef-init": 2, + "no-dupe-args": 2, + "no-dupe-keys": 2, + "no-duplicate-case": 2, + "no-empty": 2, + "no-extra-semi": 2, + "no-func-assign": 2, + "no-invalid-regexp": 2, + "no-unreachable": 2, + "valid-typeof": 2, + "array-callback-return": 2, + "block-scoped-var": 2, + "no-floating-decimal": 2, + "no-lone-blocks": 2, + "no-redeclare": [2, {"builtinGlobals": true}], + "no-return-assign": 2, + "no-sequences": 2, + "no-unused-labels": 2, + "no-useless-concat": 2, + "no-warning-comments": 1, + "radix": 2, + "strict": [2, "global"], + "brace-style": [2, "stroustrup", { "allowSingleLine": true }], + "comma-spacing": ["error", { "before": false, "after": true }], + "comma-style": ["error", "last"], + "eol-last": 2, + "max-statements-per-line": [2, {"max": 2}], + "new-cap": 2, + "new-parens": 2, + "no-lonely-if": 2, + "no-mixed-spaces-and-tabs": 2, + "no-whitespace-before-property": 2 + } +} diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..4f42e94 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,23 @@ +notifications: + email: + - dev@ideal.com + slack: ideal:Ac38yYQo0Q74kYY7udUzoWLD +language: node_js +dist: trusty +node_js: + - "0.10.45" + - "7" + - "6" +script: + - npm run lint + - npm test + +after_success: + - npm install istanbul codecov + - npm run cover + - ./node_modules/.bin/codecov + +cache: + directories: + - node_modules + diff --git a/package.json b/package.json index 8acadf5..57d0e3f 100644 --- a/package.json +++ b/package.json @@ -23,5 +23,11 @@ }, "scripts": { "install": "node-gyp configure build", - "test": "./run_tests" } + "test": "./run_tests", + "lint": "node ./node_modules/eslint/bin/eslint \"*.js\" \"tests/**/*.js\"", + "cover": "NODE_ENV=cov ./node_modules/.bin/istanbul cover _mocha" + }, + "devDependencies": { + "eslint" : "^2.13.0" + } } From 99b5e10aaa1795f3988a25ebd52b825568833a49 Mon Sep 17 00:00:00 2001 From: Matt Sergeant Date: Tue, 7 Mar 2017 08:53:10 -0500 Subject: [PATCH 29/33] Add node_modules --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 378eac2..e3fbd98 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ build +node_modules From 6cc997bba1c20ce4d31bcc4fedbff5f01ac5b822 Mon Sep 17 00:00:00 2001 From: Matt Sergeant Date: Tue, 7 Mar 2017 09:13:52 -0500 Subject: [PATCH 30/33] Fix lint errors --- .eslintrc | 3 +- example.js | 2 + fs-ext.js | 12 ++- tests/test-fs-fcntl.js | 112 +++++++++++-------- tests/test-fs-flock.js | 155 +++++++++++++++----------- tests/test-fs-flock_stress.js | 71 ++++++------ tests/test-fs-seek.js | 198 ++++++++++++++++++++-------------- tests/test-fs-seek_stress.js | 105 +++++++++--------- tests/test-fs-statvfs.js | 2 + tests/test-fs-utime.js | 106 ++++++++++-------- tests/test-fs-utime_stress.js | 90 +++++++++------- 11 files changed, 496 insertions(+), 360 deletions(-) diff --git a/.eslintrc b/.eslintrc index ee42d67..ba936cf 100644 --- a/.eslintrc +++ b/.eslintrc @@ -5,8 +5,7 @@ }, "rules": { "dot-notation": 2, - "indent": [2, 4, {"SwitchCase": 1}], - "one-var": [2, "never"], + "indent": [2, 2, {"SwitchCase": 1}], "no-trailing-spaces": [2, { "skipBlankLines": true }], "keyword-spacing": [2, { "overrides": { }, diff --git a/example.js b/example.js index 9223a91..1d72ee5 100644 --- a/example.js +++ b/example.js @@ -1,3 +1,5 @@ +"use strict"; + var fs = require('./'); /** diff --git a/fs-ext.js b/fs-ext.js index bf52ac8..d59d179 100644 --- a/fs-ext.js +++ b/fs-ext.js @@ -17,6 +17,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. +"use strict"; + var binding = require('./build/Release/fs-ext'); var fs = require('fs'); var constants = require('constants'); @@ -91,7 +93,7 @@ exports.fcntl = function(fd, cmd, arg, callback) { callback = arg; arg = 0; } - if(!arg) arg = 0; + if (!arg) arg = 0; return binding.fcntl(fd, cmd, arg, callback); }; @@ -384,9 +386,9 @@ merge(exports, fs); merge(exports, fsExt); // put constants into constants module (don't like doing this but...) -for (var key in binding) { - if (/^[A-Z_]+$/.test(key) && !constants.hasOwnProperty(key)) { - constants[key] = binding[key]; - } +for (key in binding) { + if (/^[A-Z_]+$/.test(key) && !constants.hasOwnProperty(key)) { + constants[key] = binding[key]; + } } diff --git a/tests/test-fs-fcntl.js b/tests/test-fs-fcntl.js index 3149c37..06cb6e7 100644 --- a/tests/test-fs-fcntl.js +++ b/tests/test-fs-fcntl.js @@ -1,3 +1,4 @@ +"use strict"; // Test these APIs as published in extension module 'fs-ext' // @@ -6,22 +7,20 @@ // console.log( require.resolve('../fs-ext')); var assert = require('assert'), - path = require('path'), - util = require('util'), - fs = require('../fs-ext'); + path = require('path'), + util = require('util'), + fs = require('../fs-ext'); var tests_ok = 0, - tests_run = 0; + tests_run = 0; -var debug_me = true; - debug_me = false; +var debug_me = false; var tmp_dir = "/tmp", - file_path = path.join(tmp_dir, 'what.when.fcntl.test'), - file_path_not = path.join(tmp_dir, 'what.not.fcntl.test'); + file_path = path.join(tmp_dir, 'what.when.fcntl.test'); var file_fd, - err; + err; // Report on test results - - - - - - - - - - - - @@ -31,7 +30,8 @@ process.addListener('exit', function() { try { fs.closeSync(file_fd); - } catch (e) { + } + catch (e) { // might not be open, that's okay. } @@ -47,7 +47,8 @@ process.addListener('exit', function() { function remove_file_wo_error(file_path) { try { fs.unlinkSync(file_path); - } catch (e) { + } + catch (e) { // might not exist, that's okay. } } @@ -58,15 +59,17 @@ function expect_errno(api_name, resource, err, expected_errno) { if (debug_me) console.log(' expected_errno(err): ' + err ); if ( err && err.code !== expected_errno ) { - fault_msg = api_name + '(): expected error ' + expected_errno + ', got another error'; - } else if ( !err ) { + fault_msg = api_name + '(): expected error ' + expected_errno + ', got another error'; + } + else if ( !err ) { fault_msg = api_name + '(): expected error ' + expected_errno + ', got another error'; } if ( ! fault_msg ) { tests_ok++; if (debug_me) console.log(' FAILED OK: ' + api_name ); - } else { + } + else { console.log('FAILURE: ' + arguments.callee.name + ': ' + fault_msg); console.log(' ARGS: ', util.inspect(arguments)); } @@ -82,7 +85,8 @@ function expect_ok(api_name, resource, err) { if ( ! fault_msg ) { tests_ok++; if (debug_me) console.log(' OK: ' + api_name ); - } else { + } + else { console.log('FAILURE: ' + arguments.callee.name + ': ' + fault_msg); console.log(' ARGS: ', util.inspect(arguments)); } @@ -96,21 +100,23 @@ function expect_ok(api_name, resource, err) { tests_run++; if ( typeof fs.fcntl !== 'function' ) { - console.log('fs.fcntl API is missing'); -} else { + console.log('fs.fcntl API is missing'); +} +else { tests_ok++; } tests_run++; if ( typeof fs.fcntlSync !== 'function' ) { console.log('fs.fcntlSync API is missing'); -} else { +} +else { tests_ok++; } // If any pre-checks and setup fail, quit before tests -if ( tests_run !== tests_ok ) { +if ( tests_run !== tests_ok ) { process.exit(1); } @@ -122,18 +128,19 @@ tests_run++; try { file_fd = fs.openSync(file_path, 'w'); tests_ok++; -} catch (e) { +} +catch (e) { console.log(' Unable to create test data file %j', file_path); console.log(' Error was: %j', e); } -if ( tests_run !== tests_ok ) { +if ( tests_run !== tests_ok ) { process.exit(1); } -// Test that constants are published - - - - - - - - +// Test that constants are published - - - - - - - - var fs_binding = require('../build/Release/fs-ext'); @@ -147,8 +154,9 @@ constant_names.forEach(function(name){ if ( fs_binding[name] !== undefined && typeof fs_binding[name] === 'number' ) { tests_ok++; - } else { - console.log('FAILURE: %s is not defined correctly', name); + } + else { + console.log('FAILURE: %s is not defined correctly', name); console.log(' %s %j %j', name, fs_binding[name], typeof fs_binding[name]); } }); @@ -161,15 +169,17 @@ constant_names.forEach(function(name){ tests_run++; try { err = fs.fcntlSync(undefined, 0); -} catch (e) { +} +catch (e) { err = e; } if (err) { if (debug_me) console.log(' err %j', err); tests_ok++; -} else { - if (debug_me) console.log(' expected error from undefined fd argument'); +} +else if (debug_me) { + console.log(' expected error from undefined fd argument'); } @@ -178,35 +188,39 @@ if (err) { tests_run++; try { err = fs.fcntlSync('foo', 0); -} catch (e) { +} +catch (e) { err = e; } if (err) { if (debug_me) console.log(' err %j', err); tests_ok++; -} else { - if (debug_me) console.log(' expected error from non-numeric fd argument'); +} +else if (debug_me) { + console.log(' expected error from non-numeric fd argument'); } -// fd value is negative +// fd value is negative tests_run++; try { err = fs.fcntlSync(-9, 0); -} catch (e) { +} +catch (e) { err = e; } expect_errno('fcntlSync', -9, err, 'EBADF'); -// fd value is 'impossible' +// fd value is 'impossible' tests_run++; try { err = fs.fcntlSync(98765, 0); -} catch (e) { +} +catch (e) { err = e; } expect_errno('fcntlSync', 98765, err, 'EBADF'); @@ -218,26 +232,29 @@ expect_errno('fcntlSync', 98765, err, 'EBADF'); tests_run++; try { err = fs.fcntlSync(file_fd, 'foo'); -} catch (e) { +} +catch (e) { err = e; } -// "message": "Unknown fcntl flag: foo" +// "message": "Unknown fcntl flag: foo" if (err) { if (debug_me) console.log(' err %j', err); tests_ok++; -} else { - if (debug_me) console.log(' expected error from non-numeric fd argument'); +} +else if (debug_me) { + console.log(' expected error from non-numeric fd argument'); } -// Test valid calls: fcntlSync - - - - - - - - - - +// Test valid calls: fcntlSync - - - - - - - - - - tests_run++; try { err = null; fs.fcntlSync(file_fd, 'getfd'); -} catch (e) { +} +catch (e) { err = e; } expect_ok('fcntlSync', file_fd, err); @@ -262,12 +279,13 @@ try { if ((flags & fs_binding.FD_CLOEXEC) === fs_binding.FD_CLOEXEC) { throw new Error("Expected FD_CLOEXEC to be cleared"); } -} catch (e) { +} +catch (e) { err = e; } expect_ok('fcntlSync', file_fd, err); -// Test valid calls: fcntl - - - - - - - - - - - +// Test valid calls: fcntl - - - - - - - - - - - // SEEK_SET to 0 @@ -302,7 +320,7 @@ fs.fcntl(file_fd, 'getfd', function(err, flags) { } - // Test invalid calls: fcntl - - - - - - - - - + // Test invalid calls: fcntl - - - - - - - - - // offset value is negative tests_run++; @@ -312,15 +330,17 @@ fs.fcntl(file_fd, 'getfd', function(err, flags) { console.log(' unexpected callback from fcntl() with bad argument'); }); err = undefined; - } catch (e) { + } + catch (e) { err = e; } // "message": "Unknown fcntl flag: foo" if (err) { if (debug_me) console.log(' err %j', err); tests_ok++; - } else { - if (debug_me) console.log(' unexpected success from fcntl() with bad argument'); + } + else if (debug_me) { + console.log(' unexpected success from fcntl() with bad argument'); } }); }); diff --git a/tests/test-fs-flock.js b/tests/test-fs-flock.js index f863feb..ed989af 100644 --- a/tests/test-fs-flock.js +++ b/tests/test-fs-flock.js @@ -1,10 +1,11 @@ +"use strict"; // Test these APIs as published in extension module 'fs-ext' // // fs.flock(fd, flags, [callback]) // -// Asynchronous flock(2). No arguments other than a possible error are -// passed to the callback. Flags can be 'sh', 'ex', 'shnb', 'exnb', 'un' +// Asynchronous flock(2). No arguments other than a possible error are +// passed to the callback. Flags can be 'sh', 'ex', 'shnb', 'exnb', 'un' // and correspond to the various LOCK_SH, LOCK_EX, LOCK_SH|LOCK_NB, etc. // // fs.flockSync(fd, flags) @@ -23,22 +24,21 @@ // console.log( require.resolve('../fs-ext')); var assert = require('assert'), - path = require('path'), - util = require('util'), - fs = require('../fs-ext'); + path = require('path'), + util = require('util'), + fs = require('../fs-ext'); var tests_ok = 0, - tests_run = 0; + tests_run = 0; -var debug_me = true; - debug_me = false; +var debug_me = false; var tmp_dir = "/tmp", - file_path = path.join(tmp_dir, 'what.when.flock.test'), - file_path_not = path.join(tmp_dir, 'what.not.flock.test'); + file_path = path.join(tmp_dir, 'what.when.flock.test'), + file_path_not = path.join(tmp_dir, 'what.not.flock.test'); var file_fd, - err; + err; // Report on test results - - - - - - - - - - - - @@ -48,7 +48,8 @@ process.addListener('exit', function() { try { fs.closeSync(file_fd); - } catch (e) { + } + catch (e) { // might not be open, that's okay. } @@ -64,7 +65,8 @@ process.addListener('exit', function() { function remove_file_wo_error(file_path) { try { fs.unlinkSync(file_path); - } catch (e) { + } + catch (e) { // might not exist, that's okay. } } @@ -75,15 +77,17 @@ function expect_errno(api_name, resource, err, expected_errno) { if (debug_me) console.log(' expected_errno(err): ' + err ); if ( err && err.code !== expected_errno ) { - fault_msg = api_name + '(): expected error ' + expected_errno + ', got another error'; - } else if ( !err ) { + fault_msg = api_name + '(): expected error ' + expected_errno + ', got another error'; + } + else if ( !err ) { fault_msg = api_name + '(): expected error ' + expected_errno + ', got another error'; } if ( ! fault_msg ) { tests_ok++; if (debug_me) console.log(' FAILED OK: ' + api_name ); - } else { + } + else { console.log('FAILURE: ' + arguments.callee.name + ': ' + fault_msg); console.log(' ARGS: ', util.inspect(arguments)); } @@ -99,7 +103,8 @@ function expect_ok(api_name, resource, err) { if ( ! fault_msg ) { tests_ok++; if (debug_me) console.log(' OK: ' + api_name ); - } else { + } + else { console.log('FAILURE: ' + arguments.callee.name + ': ' + fault_msg); console.log(' ARGS: ', util.inspect(arguments)); } @@ -113,21 +118,23 @@ function expect_ok(api_name, resource, err) { tests_run++; if ( typeof fs.flock !== 'function' ) { - console.log('fs.flock API is missing'); -} else { + console.log('fs.flock API is missing'); +} +else { tests_ok++; } tests_run++; if ( typeof fs.flockSync !== 'function' ) { console.log('fs.flockSync API is missing'); -} else { +} +else { tests_ok++; } // If any pre-checks and setup fail, quit before tests -if ( tests_run !== tests_ok ) { +if ( tests_run !== tests_ok ) { process.exit(1); } @@ -139,18 +146,19 @@ tests_run++; try { file_fd = fs.openSync(file_path, 'w'); tests_ok++; -} catch (e) { +} +catch (e) { console.log(' Unable to create test data file %j', file_path); console.log(' Error was: %j', e); } -if ( tests_run !== tests_ok ) { +if ( tests_run !== tests_ok ) { process.exit(1); } -// Test that constants are published - - - - - - - - +// Test that constants are published - - - - - - - - var fs_binding = require('../build/Release/fs-ext'); @@ -164,8 +172,9 @@ constant_names.forEach(function(name){ if ( fs_binding[name] !== undefined && typeof fs_binding[name] === 'number' ) { tests_ok++; - } else { - console.log('FAILURE: %s is not defined correctly', name); + } + else { + console.log('FAILURE: %s is not defined correctly', name); console.log(' %s %j %j', name, fs_binding[name], typeof fs_binding[name]); } }); @@ -173,57 +182,63 @@ constant_names.forEach(function(name){ // Test bad argument handling - - - - - - - - - - - -// fd value is undefined +// fd value is undefined tests_run++; try { err = fs.flockSync(undefined, 'un'); -} catch (e) { +} +catch (e) { err = e; } if (err) { if (debug_me) console.log(' err %j', err); tests_ok++; -} else { - if (debug_me) console.log(' expected error from undefined fd argument'); +} +else if (debug_me) { + console.log(' expected error from undefined fd argument'); } -// fd value is non-number +// fd value is non-number tests_run++; try { err = fs.flockSync('foo', 'un'); -} catch (e) { +} +catch (e) { err = e; } if (err) { if (debug_me) console.log(' err %j', err); tests_ok++; -} else { - if (debug_me) console.log(' expected error from non-numeric fd argument'); +} +else if (debug_me) { + console.log(' expected error from non-numeric fd argument'); } -// fd value is negative +// fd value is negative tests_run++; try { err = fs.flockSync(-9, 'un'); -} catch (e) { +} +catch (e) { err = e; } expect_errno('flockSync', -9, err, 'EBADF'); -// fd value is 'impossible' +// fd value is 'impossible' tests_run++; try { err = fs.flockSync(98765, 'un'); -} catch (e) { +} +catch (e) { err = e; } expect_errno('flockSync', 98765, err, 'EBADF'); @@ -236,20 +251,22 @@ expect_errno('flockSync', 98765, err, 'EBADF'); tests_run++; try { err = fs.flockSync(file_fd, 'foo'); -} catch (e) { +} +catch (e) { err = e; } -// "message": "Unknown flock flag: foo" +// "message": "Unknown flock flag: foo" if (err) { if (debug_me) console.log(' err %j', err); tests_ok++; -} else { - if (debug_me) console.log(' expected error from non-numeric fd argument'); +} +else if (debug_me) { + console.log(' expected error from non-numeric fd argument'); } -// Test valid calls: flockSync - - - - - - - - - - +// Test valid calls: flockSync - - - - - - - - - - // Flags can be 'sh', 'ex', 'shnb', 'exnb', 'un'. @@ -258,7 +275,8 @@ if (err) { tests_run++; try { err = fs.flockSync(file_fd, 'un'); -} catch (e) { +} +catch (e) { err = e; } expect_ok('flockSync', file_fd, err); @@ -269,7 +287,8 @@ expect_ok('flockSync', file_fd, err); tests_run++; try { err = fs.flockSync(file_fd, 'sh'); -} catch (e) { +} +catch (e) { err = e; } expect_ok('flockSync', file_fd, err); @@ -277,7 +296,8 @@ expect_ok('flockSync', file_fd, err); tests_run++; try { err = fs.flockSync(file_fd, 'un'); -} catch (e) { +} +catch (e) { err = e; } expect_ok('flockSync', file_fd, err); @@ -288,7 +308,8 @@ expect_ok('flockSync', file_fd, err); tests_run++; try { err = fs.flockSync(file_fd, 'ex'); -} catch (e) { +} +catch (e) { err = e; } expect_ok('flockSync', file_fd, err); @@ -296,7 +317,8 @@ expect_ok('flockSync', file_fd, err); tests_run++; try { err = fs.flockSync(file_fd, 'un'); -} catch (e) { +} +catch (e) { err = e; } expect_ok('flockSync', file_fd, err); @@ -307,7 +329,8 @@ expect_ok('flockSync', file_fd, err); tests_run++; try { err = fs.flockSync(file_fd, 'shnb'); -} catch (e) { +} +catch (e) { err = e; } expect_ok('flockSync', file_fd, err); @@ -315,7 +338,8 @@ expect_ok('flockSync', file_fd, err); tests_run++; try { err = fs.flockSync(file_fd, 'un'); -} catch (e) { +} +catch (e) { err = e; } expect_ok('flockSync', file_fd, err); @@ -326,7 +350,8 @@ expect_ok('flockSync', file_fd, err); tests_run++; try { err = fs.flockSync(file_fd, 'exnb'); -} catch (e) { +} +catch (e) { err = e; } expect_ok('flockSync', file_fd, err); @@ -334,7 +359,8 @@ expect_ok('flockSync', file_fd, err); tests_run++; try { err = fs.flockSync(file_fd, 'un'); -} catch (e) { +} +catch (e) { err = e; } expect_ok('flockSync', file_fd, err); @@ -346,20 +372,21 @@ expect_ok('flockSync', file_fd, err); -// Test valid calls: flock - - - - - - - - - - - +// Test valid calls: flock - - - - - - - - - - - // SEEK_SET to 0 tests_run++; - tests_run++; +tests_run++; fs.flock(file_fd, 'sh', function(err, extra) { expect_ok('flock', file_fd, err); // After a change to returning arguments to async callback routines, // check that this API still receives only one argument. if ( extra === undefined ) { - tests_ok++; - } else { + tests_ok++; + } + else { console.log(' async flock() callback received more than one argument'); } @@ -371,7 +398,7 @@ fs.flock(file_fd, 'sh', function(err, extra) { fs.flock(file_fd, 'un', function(err) { expect_ok('flock', file_fd, err); - // Test invalid calls: flock - - - - - - - - - + // Test invalid calls: flock - - - - - - - - - // offset value is negative tests_run++; @@ -381,15 +408,17 @@ fs.flock(file_fd, 'sh', function(err, extra) { console.log(' unexpected callback from flock() with bad argument'); }); err = undefined; - } catch (e) { + } + catch (e) { err = e; } - // "message": "Unknown flock flag: foo" + // "message": "Unknown flock flag: foo" if (err) { if (debug_me) console.log(' err %j', err); tests_ok++; - } else { - if (debug_me) console.log(' unexpected success from flock() with bad argument'); + } + else if (debug_me) { + console.log(' unexpected success from flock() with bad argument'); } }); }); @@ -397,8 +426,8 @@ fs.flock(file_fd, 'sh', function(err, extra) { //------------------------------------------------------------------------------ -//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -//- - - - - - - - - - - - - - - - - - - - - - - - - - +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//- - - - - - - - - - - - - - - - - - - - - - - - - - // // Errors we have seen: // diff --git a/tests/test-fs-flock_stress.js b/tests/test-fs-flock_stress.js index b89489f..814b960 100644 --- a/tests/test-fs-flock_stress.js +++ b/tests/test-fs-flock_stress.js @@ -1,11 +1,11 @@ - +"use strict"; // Stress test these APIs as published in extension module 'fs-ext' // Specifically, try to exercise any memory leaks by simple repetition. // // fs.flock(fd, flags, [callback]) // -// Asynchronous flock(2). No arguments other than a possible error are -// passed to the callback. Flags can be 'sh', 'ex', 'shnb', 'exnb', 'un' +// Asynchronous flock(2). No arguments other than a possible error are +// passed to the callback. Flags can be 'sh', 'ex', 'shnb', 'exnb', 'un' // and correspond to the various LOCK_SH, LOCK_EX, LOCK_SH|LOCK_NB, etc. // // fs.flockSync(fd, flags) @@ -22,22 +22,21 @@ // console.log( require.resolve('../fs-ext')); var assert = require('assert'), - path = require('path'), - util = require('util'), - fs = require('../fs-ext'); + path = require('path'), + util = require('util'), + fs = require('../fs-ext'); var tests_ok = 0, - tests_run = 0; + tests_run = 0; -var debug_me = true; - debug_me = false; +var debug_me = false; var tmp_dir = "/tmp", - file_path = path.join(tmp_dir, 'what.when.flock.test'), - file_path_not = path.join(tmp_dir, 'what.not.flock.test'); + file_path = path.join(tmp_dir, 'what.when.flock.test'), + file_path_not = path.join(tmp_dir, 'what.not.flock.test'); var file_fd, - err; + err; // Report on test results - - - - - - - - - - - - @@ -52,7 +51,8 @@ process.addListener('exit', function() { try { fs.closeSync(file_fd); - } catch (e) { + } + catch (e) { // might not be open, that's okay. } @@ -68,16 +68,17 @@ process.addListener('exit', function() { function remove_file_wo_error(file_path) { try { fs.unlinkSync(file_path); - } catch (e) { + } + catch (e) { // might not exist, that's okay. } } function display_memory_usage_now() { var usage = process.memoryUsage(); - console.log(' memory: heapUsed %d rss %d', + console.log(' memory: heapUsed %d rss %d', usage.heapUsed, usage.rss); - console.log(' heapTotal %d vsize %d', + console.log(' heapTotal %d vsize %d', usage.heapTotal, usage.vsize); } @@ -87,15 +88,17 @@ function expect_errno(api_name, resource, err, expected_errno) { if (debug_me) console.log(' expected_errno(err): ' + err ); if ( err && err.code !== expected_errno ) { - fault_msg = api_name + '(): expected error ' + expected_errno + ', got another error'; - } else if ( !err ) { + fault_msg = api_name + '(): expected error ' + expected_errno + ', got another error'; + } + else if ( !err ) { fault_msg = api_name + '(): expected error ' + expected_errno + ', got another error'; } if ( ! fault_msg ) { tests_ok++; if (debug_me) console.log(' FAILED OK: ' + api_name ); - } else { + } + else { console.log('FAILURE: ' + arguments.callee.name + ': ' + fault_msg); console.log(' ARGS: ', util.inspect(arguments)); } @@ -111,7 +114,8 @@ function expect_ok(api_name, resource, err) { if ( ! fault_msg ) { tests_ok++; if (debug_me) console.log(' OK: ' + api_name ); - } else { + } + else { console.log('FAILURE: ' + arguments.callee.name + ': ' + fault_msg); console.log(' ARGS: ', util.inspect(arguments)); console.log(' err: %j', err ); @@ -121,7 +125,7 @@ function expect_ok(api_name, resource, err) { // Setup for testing - - - - - - - - - - - - -// We assume that test-fs-flock.js has run successfully before this +// We assume that test-fs-flock.js has run successfully before this // test and so we omit several duplicate tests. // Delete any prior copy of test data file(s) @@ -132,13 +136,14 @@ tests_run++; try { file_fd = fs.openSync(file_path, 'w'); tests_ok++; -} catch (e) { +} +catch (e) { console.log(' Unable to create test data file %j', file_path); console.log(' Error was: %j', e); } -if ( tests_run !== tests_ok ) { +if ( tests_run !== tests_ok ) { process.exit(1); } @@ -146,8 +151,8 @@ if ( tests_run !== tests_ok ) { // Stress testing - - - - - - - - - - - - - var how_many_times, - how_many_secs, - how_many_done; + how_many_secs, + how_many_done; console.log(' Start time is %s', new Date()); @@ -156,13 +161,13 @@ display_memory_usage_now(); console.log(''); -// Repeat a successful flockSync() call -if( 1 ) { +// Repeat a successful flockSync() call +if ( 1 ) { how_many_times = 10000000; //how_many_times = 1000000; //how_many_times = 4; - for( var i=0 ; i 0 ) { - setTimeout(ho_hum,1000); + setTimeout(ho_hum, 1000); return; } console.log(' After "do nothing" testing for %d seconds:', how_many_secs); display_memory_usage_now(); console.log(' Time is %s', new Date()); - }, + }, 1000 ); } -// Repeat a successful seekSync() call -if( 1 ) { +// Repeat a successful seekSync() call +if ( 1 ) { how_many_times = 10000000; //how_many_times = 4; - for( var i=0 ; i Date: Tue, 7 Mar 2017 09:17:44 -0500 Subject: [PATCH 31/33] Add codecov token --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4f42e94..e8ad233 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,3 +21,6 @@ cache: directories: - node_modules +env: + global: + - CODECOV_TOKEN=88ae9a05-ad28-4ce5-baa4-34bb0c4a055c From 0c3bf226df915c16b3d7447557e5c3d5ae0193d1 Mon Sep 17 00:00:00 2001 From: Matt Sergeant Date: Tue, 7 Mar 2017 13:26:19 -0500 Subject: [PATCH 32/33] V0.6.0 (#65) * Bump version * Change email * Add windows testing * Remove slack notification --- .travis.yml | 3 +-- appveyor.yml | 19 +++++++++++++++++++ package.json | 2 +- 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 appveyor.yml diff --git a/.travis.yml b/.travis.yml index e8ad233..d985b88 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ notifications: email: - - dev@ideal.com - slack: ideal:Ac38yYQo0Q74kYY7udUzoWLD + - helpme+github@gmail.com language: node_js dist: trusty node_js: diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..5b2c656 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,19 @@ +environment: + nodejs_version: "6" + +install: + - npm install + +before_build: +build: off +after_build: + +before_test: + - node --version + - npm --version + +test_script: + - npm test + +after_test: + diff --git a/package.json b/package.json index 57d0e3f..b5d298c 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "name": "fs-ext", "description": "Extensions to core 'fs' module.", "keywords": ["fs", "filesystem", "flock", "seek"], - "version": "0.4.3", + "version": "0.6.0", "homepage": "https://github.com/baudehlo/node-fs-ext/", "repository": { "type": "git", From b2c91485026048851f91d1522a3dc1ff1e861df9 Mon Sep 17 00:00:00 2001 From: Matt Sergeant Date: Tue, 7 Mar 2017 13:40:47 -0500 Subject: [PATCH 33/33] Lint fixes --- fs-ext.js | 358 +++++++++++++++++++++-------------------- tests/test-fs-chown.js | 26 +-- tests/test-fs-stat.js | 21 ++- 3 files changed, 210 insertions(+), 195 deletions(-) diff --git a/fs-ext.js b/fs-ext.js index d59d179..063ba19 100644 --- a/fs-ext.js +++ b/fs-ext.js @@ -180,198 +180,204 @@ var fsExt = process.platform.match(/^win/i) ? var path = require("path"), - // declare the extra methods for the built-in fs module - // which provide the POSIX functionality on Windows - fsExt = (function () { - - // merges the ownership to the stats - function completeStats(stats, fd, callback) { - // allow calling with both fd and path - (typeof fd === "string" ? binding.getown : - binding.fgetown)(fd, function(error, ownership) { + // declare the extra methods for the built-in fs module + // which provide the POSIX functionality on Windows + fsExt = (function () { + + // merges the ownership to the stats + function completeStats(stats, fd, callback) { + // allow calling with both fd and path + (typeof fd === "string" ? binding.getown : + binding.fgetown)(fd, function(error, ownership) { if (error) { callback(error); - } else { + } + else { // replace the uid and gid members in the original stats // with the values containing SIDs merge(stats, ownership); callback(undefined, stats); } }); - } - - // merges the ownership to the stats - function completeStatsSync(stats, fd) { - // allow calling with both fd and path - var ownership = (typeof fd === "string" ? - binding.getown : binding.fgetown)(fd); - // replace the uid and gid members in the original stats - // with the values containing SIDs - merge(stats, ownership); - return stats; - } - - return { - - // fs.fstat returning uid and gid as SIDs - fstat: function(fd, callback) { - // get the built-in stats which work on Windows too - fs.fstat(fd, function(error, stats) { - if (error) { - callback(error); - } else { - // replace the ownership information (uid and gid) - // with the data useful on Windows - principal SIDs - completeStats(stats, fd, callback); - } - }); - }, - - // fs.fstatSync returning uid and gid as SIDs - fstatSync: function(fd) { - // get the built-in stats which work on Windows too - var stats = fs.fstatSync(fd); - // replace the ownership information (uid and gid) - // with the data useful on Windows - principal SIDs - return completeStatsSync(stats, fd); - }, - - // fs.stat returning uid and gid as SIDs - stat: function(fpath, callback) { - // get the built-in stats which work on Windows too - fs.lstat(fpath, function(error, stats) { - if (error) { - callback(error); - } else { - // GetNamedSecurityInfo, which is used by binding.getown, - // doesn't resolve sybolic links automatically; do the - // resolution here and call the lstat implementation - if (stats.isSymbolicLink()) { - fs.readlink(fpath, function(error, lpath) { - if (error) { - callback(error); - } else { - fpath = resolveLink(fpath, lpath); - fsExt.lstat(fpath, callback); - } - }); - } else { - // replace the ownership information (uid and gid) - // with the data useful on Windows - principal SIDs - completeStats(stats, fpath, callback); - } - } - }); - }, - - // fs.statSync returning uid and gid as SIDs - statSync: function(fpath) { - // get the built-in stats which work on Windows too - // GetNamedSecurityInfo, which is used by binding.getown, - // doesn't resolve sybolic links automatically; do the - // resolution here and call the lstat implementation - var stats = fs.lstatSync(fpath); - if (stats.isSymbolicLink()) { - var lpath = fs.readlinkSync(fpath); - fpath = resolveLink(fpath, lpath); - return fsExt.lstatSync(fpath); + } + + // merges the ownership to the stats + function completeStatsSync(stats, fd) { + // allow calling with both fd and path + var ownership = (typeof fd === "string" ? + binding.getown : binding.fgetown)(fd); + // replace the uid and gid members in the original stats + // with the values containing SIDs + merge(stats, ownership); + return stats; + } + + return { + + // fs.fstat returning uid and gid as SIDs + fstat: function(fd, callback) { + // get the built-in stats which work on Windows too + fs.fstat(fd, function(error, stats) { + if (error) { + callback(error); + } + else { + // replace the ownership information (uid and gid) + // with the data useful on Windows - principal SIDs + completeStats(stats, fd, callback); } - // replace the ownership information (uid and gid) - // with the data useful on Windows - principal SIDs - return completeStatsSync(stats, fpath); - }, - - // fs.lstat returning uid and gid as SIDs - lstat: function(fpath, callback) { - // get the built-in stats which work on Windows too - fs.lstat(fpath, function(error, stats) { - if (error) { - callback(error); - } else { - // replace the ownership information (uid and gid) - // with the data useful on Windows - principal SIDs - completeStats(stats, fpath, callback); - } - }); - }, - - // fs.lstatSync returning uid and gid as SIDs - lstatSync: function(fpath) { - // get the built-in stats which work on Windows too - // GetNamedSecurityInfo, which is used by binding.getown, - // doesn't resolve sybolic links automatically; it's - // suitable for the lstat implementation as-is - var stats = fs.lstatSync(fpath); - // replace the ownership information (uid and gid) - // with the data useful on Windows - principal SIDs - return completeStatsSync(stats, fpath); - }, - - // fs.fchown accepting uid and gid as SIDs - fchown: function(fd, uid, gid, callback) { - binding.fchown(fd, uid, gid, function(error) { + }); + }, + + // fs.fstatSync returning uid and gid as SIDs + fstatSync: function(fd) { + // get the built-in stats which work on Windows too + var stats = fs.fstatSync(fd); + // replace the ownership information (uid and gid) + // with the data useful on Windows - principal SIDs + return completeStatsSync(stats, fd); + }, + + // fs.stat returning uid and gid as SIDs + stat: function(fpath, callback) { + // get the built-in stats which work on Windows too + fs.lstat(fpath, function(error, stats) { + if (error) { callback(error); - }); - }, - - // fs.fchownSync accepting uid and gid as SIDs - fchownSync: function(fd, uid, gid) { - binding.fchown(fd, uid, gid); - }, - - // fs.chown accepting uid and gid as SIDs - chown: function(fpath, uid, gid, callback) { - fs.lstat(fpath, function(error, stats) { - if (error) { - callback(error); - } else { - if (stats.isSymbolicLink()) { - fs.readlink(fpath, function(error, lpath) { - if (error) { - callback(error); - } else { - fpath = resolveLink(fpath, lpath); - fsExt.lchown(fpath, uid, gid, callback); - } - }); - } else { - fsExt.lchown(fpath, uid, gid, callback); + } + else if (stats.isSymbolicLink()) { + // GetNamedSecurityInfo, which is used by binding.getown, + // doesn't resolve sybolic links automatically; do the + // resolution here and call the lstat implementation + + fs.readlink(fpath, function(error, lpath) { + if (error) { + callback(error); + } + else { + fpath = resolveLink(fpath, lpath); + fsExt.lstat(fpath, callback); } - } - }); - }, - - // fs.chownSync accepting uid and gid as SIDs - chownSync: function(fpath, uid, gid) { - // SetNamedSecurityInfo, which is used by binding.chown, - // doesn't resolve sybolic links automatically; do the - // resolution here and call the lchown implementation - var stats = fs.lstatSync(fpath); - if (stats.isSymbolicLink()) { - var lpath = fs.readlinkSync(fpath); - fpath = resolveLink(fpath, lpath); + }); } - fsExt.lchownSync(fpath, uid, gid); - }, + else { + // replace the ownership information (uid and gid) + // with the data useful on Windows - principal SIDs + completeStats(stats, fpath, callback); + } + }); + }, + + // fs.statSync returning uid and gid as SIDs + statSync: function(fpath) { + // get the built-in stats which work on Windows too + // GetNamedSecurityInfo, which is used by binding.getown, + // doesn't resolve sybolic links automatically; do the + // resolution here and call the lstat implementation + var stats = fs.lstatSync(fpath); + if (stats.isSymbolicLink()) { + var lpath = fs.readlinkSync(fpath); + fpath = resolveLink(fpath, lpath); + return fsExt.lstatSync(fpath); + } + // replace the ownership information (uid and gid) + // with the data useful on Windows - principal SIDs + return completeStatsSync(stats, fpath); + }, + + // fs.lstat returning uid and gid as SIDs + lstat: function(fpath, callback) { + // get the built-in stats which work on Windows too + fs.lstat(fpath, function(error, stats) { + if (error) { + callback(error); + } + else { + // replace the ownership information (uid and gid) + // with the data useful on Windows - principal SIDs + completeStats(stats, fpath, callback); + } + }); + }, + + // fs.lstatSync returning uid and gid as SIDs + lstatSync: function(fpath) { + // get the built-in stats which work on Windows too + // GetNamedSecurityInfo, which is used by binding.getown, + // doesn't resolve sybolic links automatically; it's + // suitable for the lstat implementation as-is + var stats = fs.lstatSync(fpath); + // replace the ownership information (uid and gid) + // with the data useful on Windows - principal SIDs + return completeStatsSync(stats, fpath); + }, + + // fs.fchown accepting uid and gid as SIDs + fchown: function(fd, uid, gid, callback) { + binding.fchown(fd, uid, gid, function(error) { + callback(error); + }); + }, - // fs.lchown accepting uid and gid as SIDs - lchown: function(fpath, uid, gid, callback) { - binding.chown(fpath, uid, gid, function(error) { + // fs.fchownSync accepting uid and gid as SIDs + fchownSync: function(fd, uid, gid) { + binding.fchown(fd, uid, gid); + }, + + // fs.chown accepting uid and gid as SIDs + chown: function(fpath, uid, gid, callback) { + fs.lstat(fpath, function(error, stats) { + if (error) { callback(error); - }); - }, - - // fs.lchownSync accepting uid and gid as SIDs - lchownSync: function(fpath, uid, gid) { - // SetNamedSecurityInfo, which is used by binding.chown, - // doesn't resolve sybolic links automatically; it's - // suitable for the lchown implementation as-is - binding.chown(fpath, uid, gid); + } + else if (stats.isSymbolicLink()) { + fs.readlink(fpath, function(error, lpath) { + if (error) { + callback(error); + } + else { + fpath = resolveLink(fpath, lpath); + fsExt.lchown(fpath, uid, gid, callback); + } + }); + } + else { + fsExt.lchown(fpath, uid, gid, callback); + } + }); + }, + + // fs.chownSync accepting uid and gid as SIDs + chownSync: function(fpath, uid, gid) { + // SetNamedSecurityInfo, which is used by binding.chown, + // doesn't resolve sybolic links automatically; do the + // resolution here and call the lchown implementation + var stats = fs.lstatSync(fpath); + if (stats.isSymbolicLink()) { + var lpath = fs.readlinkSync(fpath); + fpath = resolveLink(fpath, lpath); } + fsExt.lchownSync(fpath, uid, gid); + }, + + // fs.lchown accepting uid and gid as SIDs + lchown: function(fpath, uid, gid, callback) { + binding.chown(fpath, uid, gid, function(error) { + callback(error); + }); + }, + + // fs.lchownSync accepting uid and gid as SIDs + lchownSync: function(fpath, uid, gid) { + // SetNamedSecurityInfo, which is used by binding.chown, + // doesn't resolve sybolic links automatically; it's + // suitable for the lchown implementation as-is + binding.chown(fpath, uid, gid); + } - }; + }; - }()); + }()); return fsExt; }()) @@ -386,7 +392,7 @@ merge(exports, fs); merge(exports, fsExt); // put constants into constants module (don't like doing this but...) -for (key in binding) { +for (var key in binding) { if (/^[A-Z_]+$/.test(key) && !constants.hasOwnProperty(key)) { constants[key] = binding[key]; } diff --git a/tests/test-fs-chown.js b/tests/test-fs-chown.js index 6b9bcf2..8edcac6 100644 --- a/tests/test-fs-chown.js +++ b/tests/test-fs-chown.js @@ -1,15 +1,15 @@ // tests the fs methods chown and fchown "use strict"; var assert = require("assert"), - path = require("path"), - fs = require("../fs-ext"), - tmp_dir = process.env.TMP || process.env.TEMP || "/tmp", - file_path = path.join(tmp_dir, "fs-ext_chown.test"), - // use the current process user or Administrator on Windows - uid = process.getuid ? process.getuid() : "S-1-5-32-500", - // use the "nobody" user or the Users group on Windows - other_uid = process.platform.match(/^win/i) ? "S-1-5-32-513" : 65534, - fd; + path = require("path"), + fs = require("../fs-ext"), + tmp_dir = process.env.TMP || process.env.TEMP || "/tmp", + file_path = path.join(tmp_dir, "fs-ext_chown.test"), + // use the current process user or Administrator on Windows + uid = process.getuid ? process.getuid() : "S-1-5-32-500", + // use the "nobody" user or the Users group on Windows + other_uid = process.platform.match(/^win/i) ? "S-1-5-32-513" : 65534, + fd; function check_stats(uid) { var stats = fs.statSync(file_path); @@ -44,10 +44,14 @@ fs.chown(file_path, other_uid, other_uid, function (error) { process.addListener("exit", function() { try { fs.closeSync(fd); - } catch (error) {} + } + catch (error) { + // Do nothing + } try { fs.unlinkSync(file_path); - } catch (error) { + } + catch (error) { console.warn(" deleting", file_path, "failed"); } }); diff --git a/tests/test-fs-stat.js b/tests/test-fs-stat.js index 47d5ca6..ec424e3 100644 --- a/tests/test-fs-stat.js +++ b/tests/test-fs-stat.js @@ -1,11 +1,11 @@ // tests the fs methods stat, lstat and fstat "use strict"; var assert = require("assert"), - path = require("path"), - fs = require("../fs-ext"), - tmp_dir = process.env.TMP || process.env.TEMP || "/tmp", - file_path = path.join(tmp_dir, "fs-ext_stat.test"), - fd, stats; + path = require("path"), + fs = require("../fs-ext"), + tmp_dir = process.env.TMP || process.env.TEMP || "/tmp", + file_path = path.join(tmp_dir, "fs-ext_stat.test"), + fd, stats; function check_stats(stats) { if (process.platform.match(/^win/i)) { @@ -13,7 +13,8 @@ function check_stats(stats) { assert.equal(stats.uid.indexOf("S-"), 0); assert.equal(typeof stats.gid, "string"); assert.equal(stats.gid.indexOf("S-"), 0); - } else { + } + else { assert.equal(typeof stats.uid, "number"); assert.equal(typeof stats.gid, "number"); } @@ -48,10 +49,14 @@ fs.fstat(fd, function (error, stats) { process.addListener("exit", function() { try { fs.closeSync(fd); - } catch (error) {} + } + catch (error) { + // Do nothing + } try { fs.unlinkSync(file_path); - } catch (error) { + } + catch (error) { console.warn(" deleting", file_path, "failed"); } });