Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fs: add inoString to fs.Stats #16857

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/api/fs.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ similar to this:
Stats {
dev: 2114,
ino: 48064969,
inoString: '48064969',
mode: 33188,
nlink: 1,
uid: 85,
Expand All @@ -364,6 +365,11 @@ object alternate representations of the various times. The `Date` and number
values are not connected. Assigning a new number value, or mutating the `Date`
value, will not be reflected in the corresponding alternate representation.

*Note*: Since `ino` is an unsigned 64-bit integer, it may lost accuracy in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A nit: may lost -> may lose.

JavaScript. This situation may cause some strange bugs in filesystem. So if you
want to get an accurate [inode][] value, please use `inoString` which is a
certain string.


### Stat Time Values

Expand Down
9 changes: 6 additions & 3 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ function Stats(
atim_msec,
mtim_msec,
ctim_msec,
birthtim_msec
birthtim_msec,
ino_str
) {
this.dev = dev;
this.mode = mode;
Expand All @@ -212,6 +213,7 @@ function Stats(
this.rdev = rdev;
this.blksize = blksize;
this.ino = ino;
this.inoString = ino_str;
this.size = size;
this.blocks = blocks;
this.atimeMs = atim_msec;
Expand Down Expand Up @@ -258,14 +260,15 @@ Stats.prototype.isSocket = function() {
};

const statValues = binding.getStatValues();
const getStatInoString = binding.getStatInoString;

function statsFromValues() {
return new Stats(statValues[0], statValues[1], statValues[2], statValues[3],
statValues[4], statValues[5],
statValues[6] < 0 ? undefined : statValues[6], statValues[7],
statValues[8], statValues[9] < 0 ? undefined : statValues[9],
statValues[10], statValues[11], statValues[12],
statValues[13]);
statValues[13], getStatInoString(0));
}

// Don't allow mode to accidentally be overwritten.
Expand Down Expand Up @@ -1446,7 +1449,7 @@ function statsFromPrevValues() {
statValues[21], statValues[22],
statValues[23] < 0 ? undefined : statValues[23],
statValues[24], statValues[25], statValues[26],
statValues[27]);
statValues[27], getStatInoString(1));
}
function StatWatcher() {
EventEmitter.call(this);
Expand Down
6 changes: 6 additions & 0 deletions src/env-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ inline Environment::Environment(IsolateData* isolate_data,
handle_cleanup_waiting_(0),
http_parser_buffer_(nullptr),
fs_stats_field_array_(nullptr),
fs_ino_array_(nullptr),
context_(context->GetIsolate(), context) {
// We'll be creating new objects so make sure we've entered the context.
v8::HandleScope handle_scope(isolate());
Expand Down Expand Up @@ -368,6 +369,7 @@ inline Environment::~Environment() {
delete[] heap_space_statistics_buffer_;
delete[] http_parser_buffer_;
delete http2_state_;
if (nullptr != fs_ino_array_) delete[] fs_ino_array_;
free(performance_state_);
}

Expand Down Expand Up @@ -539,6 +541,10 @@ inline void Environment::set_fs_stats_field_array(double* fields) {
fs_stats_field_array_ = fields;
}

inline char* Environment::fs_ino_array() const {
return fs_ino_array_;
}

inline performance::performance_state* Environment::performance_state() {
return performance_state_;
}
Expand Down
4 changes: 4 additions & 0 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ void Environment::Start(int argc,
HandleScope handle_scope(isolate());
Context::Scope context_scope(context());

char* temp = new char[2 * FS_INO_STRING_OFFSET];
memset(temp, 0, sizeof(char) * 2 * FS_INO_STRING_OFFSET);
fs_ino_array_ = temp;

uv_check_init(event_loop(), immediate_check_handle());
uv_unref(reinterpret_cast<uv_handle_t*>(immediate_check_handle()));

Expand Down
5 changes: 5 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ struct nghttp2_rcbuf;

namespace node {

const size_t FS_INO_STRING_OFFSET = 32;

namespace performance {
struct performance_state;
}
Expand Down Expand Up @@ -615,6 +617,8 @@ class Environment {
inline double* fs_stats_field_array() const;
inline void set_fs_stats_field_array(double* fields);

inline char* fs_ino_array() const;

inline performance::performance_state* performance_state();
inline std::map<std::string, uint64_t>* performance_marks();

Expand Down Expand Up @@ -744,6 +748,7 @@ class Environment {
http2::http2_state* http2_state_ = nullptr;

double* fs_stats_field_array_;
char* fs_ino_array_;

struct AtExitCallback {
void (*cb_)(void* arg);
Expand Down
41 changes: 33 additions & 8 deletions src/node_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ void After(uv_fs_t *req) {
// All have at least two args now.
argc = 2;

uv_stat_t* stat = nullptr;
switch (req->fs_type) {
// These all have no data to pass.
case UV_FS_ACCESS:
Expand All @@ -216,8 +217,9 @@ void After(uv_fs_t *req) {
case UV_FS_LSTAT:
case UV_FS_FSTAT:
argc = 1;
FillStatsArray(env->fs_stats_field_array(),
static_cast<const uv_stat_t*>(req->ptr));
stat = static_cast<uv_stat_t*>(req->ptr);
FillStatsArray(env->fs_stats_field_array(), stat);
SetStatsIno(env->fs_ino_array(), stat);
break;

case UV_FS_UTIME:
Expand Down Expand Up @@ -465,6 +467,16 @@ void FillStatsArray(double* fields, const uv_stat_t* s) {
#undef X
}

#ifdef _WIN32
const char INO_STRING_FORMATTER[] = "%I64u";
#else
const char INO_STRING_FORMATTER[] = "%llu";
#endif

void SetStatsIno(char* ino, const uv_stat_t* s) {
snprintf(ino, FS_INO_STRING_OFFSET - 1, INO_STRING_FORMATTER, s->st_ino);
}

// Used to speed up module loading. Returns the contents of the file as
// a string or undefined when the file cannot be opened. The speedup
// comes from not creating Error objects on failure.
Expand Down Expand Up @@ -556,8 +568,9 @@ static void Stat(const FunctionCallbackInfo<Value>& args) {
ASYNC_CALL(stat, args[1], UTF8, *path)
} else {
SYNC_CALL(stat, *path, *path)
FillStatsArray(env->fs_stats_field_array(),
static_cast<const uv_stat_t*>(SYNC_REQ.ptr));
const uv_stat_t* stat = static_cast<const uv_stat_t*>(SYNC_REQ.ptr);
FillStatsArray(env->fs_stats_field_array(), stat);
SetStatsIno(env->fs_ino_array(), stat);
}
}

Expand All @@ -574,8 +587,9 @@ static void LStat(const FunctionCallbackInfo<Value>& args) {
ASYNC_CALL(lstat, args[1], UTF8, *path)
} else {
SYNC_CALL(lstat, *path, *path)
FillStatsArray(env->fs_stats_field_array(),
static_cast<const uv_stat_t*>(SYNC_REQ.ptr));
const uv_stat_t* stat = static_cast<const uv_stat_t*>(SYNC_REQ.ptr);
FillStatsArray(env->fs_stats_field_array(), stat);
SetStatsIno(env->fs_ino_array(), stat);
}
}

Expand All @@ -593,8 +607,9 @@ static void FStat(const FunctionCallbackInfo<Value>& args) {
ASYNC_CALL(fstat, args[1], UTF8, fd)
} else {
SYNC_CALL(fstat, nullptr, fd)
FillStatsArray(env->fs_stats_field_array(),
static_cast<const uv_stat_t*>(SYNC_REQ.ptr));
const uv_stat_t* stat = static_cast<const uv_stat_t*>(SYNC_REQ.ptr);
FillStatsArray(env->fs_stats_field_array(), stat);
SetStatsIno(env->fs_ino_array(), stat);
}
}

Expand Down Expand Up @@ -1415,6 +1430,15 @@ void GetStatValues(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(fields_array);
}

void GetStatInoString(const FunctionCallbackInfo<Value>& args) {
CHECK_EQ(args.Length(), 1);
size_t idx = args[0]->Uint32Value();
Environment* env = Environment::GetCurrent(args);
char* ino = env->fs_ino_array();
args.GetReturnValue().Set(String::NewFromUtf8(env->isolate(),
ino + (idx * FS_INO_STRING_OFFSET)));
}

void InitFs(Local<Object> target,
Local<Value> unused,
Local<Context> context,
Expand Down Expand Up @@ -1461,6 +1485,7 @@ void InitFs(Local<Object> target,
env->SetMethod(target, "mkdtemp", Mkdtemp);

env->SetMethod(target, "getStatValues", GetStatValues);
env->SetMethod(target, "getStatInoString", GetStatInoString);

StatWatcher::Initialize(env, target);

Expand Down
2 changes: 2 additions & 0 deletions src/node_internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ void ProcessEmitWarning(Environment* env, const char* fmt, ...);

void FillStatsArray(double* fields, const uv_stat_t* s);

void SetStatsIno(char* ino, const uv_stat_t* s);

void SetupProcessObject(Environment* env,
int argc,
const char* const* argv,
Expand Down
2 changes: 2 additions & 0 deletions src/node_stat_watcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ void StatWatcher::Callback(uv_fs_poll_t* handle,

FillStatsArray(env->fs_stats_field_array(), curr);
FillStatsArray(env->fs_stats_field_array() + 14, prev);
SetStatsIno(env->fs_ino_array(), curr);
SetStatsIno(env->fs_ino_array() + FS_INO_STRING_OFFSET, prev);
Local<Value> arg = Integer::New(env->isolate(), status);
wrap->MakeCallback(env->onchange_string(), 1, &arg);
}
Expand Down
8 changes: 7 additions & 1 deletion test/parallel/test-fs-stat.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ fs.stat(__filename, common.mustCall(function(err, s) {
'dev', 'mode', 'nlink', 'uid',
'gid', 'rdev', 'ino', 'size',
'atime', 'mtime', 'ctime', 'birthtime',
'atimeMs', 'mtimeMs', 'ctimeMs', 'birthtimeMs'
'atimeMs', 'mtimeMs', 'ctimeMs', 'birthtimeMs',
'inoString'
];
if (!common.isWindows) {
keys.push('blocks', 'blksize');
Expand Down Expand Up @@ -129,4 +130,9 @@ fs.stat(__filename, common.mustCall(function(err, s) {
dateFields.forEach((k) => {
assert.strictEqual(typeof parsed[k], 'string', `${k} should be a string`);
});

assert.strictEqual(typeof parsed.inoString,
'string',
'inoString should be a string');
assert.strictEqual(/^\d+$/g.test(parsed.inoString), true);
}));
3 changes: 2 additions & 1 deletion test/parallel/test-fs-watchfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ const expectedStatObject = new fs.Stats(
Date.UTC(1970, 0, 1, 0, 0, 0), // atime
Date.UTC(1970, 0, 1, 0, 0, 0), // mtime
Date.UTC(1970, 0, 1, 0, 0, 0), // ctime
Date.UTC(1970, 0, 1, 0, 0, 0) // birthtime
Date.UTC(1970, 0, 1, 0, 0, 0), // birthtime
'0' // ino string
);

common.refreshTmpDir();
Expand Down