Skip to content

Commit

Permalink
fs: add statfs to fs
Browse files Browse the repository at this point in the history
Add support for statfs system call to the fs module using
uv_fs_statfs() function from libuv.

Refs: nodejs#10745
  • Loading branch information
SheikhSajid committed Feb 11, 2020
1 parent fb437c4 commit f7a3f8e
Show file tree
Hide file tree
Showing 13 changed files with 396 additions and 2 deletions.
128 changes: 127 additions & 1 deletion doc/api/fs.md
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,90 @@ The times in the stat object have the following semantics:
Prior to Node.js 0.12, the `ctime` held the `birthtime` on Windows systems. As
of 0.12, `ctime` is not "creation time", and on Unix systems, it never was.

## Class: `fs.StatFs`

Provides information about a mounted filesystem.

Objects returned from [`fs.statfs()`][] and its synchronous counterpart are of
this type. If `bigint` in the `options` passed to those methods is true, the
numeric values will be `bigint` instead of `number`.

```console
StatFs {
type: 1397114950,
bsize: 4096,
blocks: 121938943,
bfree: 61058895,
bavail: 61058895,
files: 999,
ffree: 1000000,
spare: [ 0, 0, 0, 0 ]
}
```

`bigint` version:

```console
StatFs {
type: 1397114950n,
bsize: 4096n,
blocks: 121938943n,
bfree: 61058895n,
bavail: 61058895n,
files: 999n,
ffree: 1000000n,
spare: [ 0n, 0n, 0n, 0n ]
}
```

### `statfs.type`

* {number|bigint}

Type of filesystem.

### `statfs.bsize`

* {number|bigint}

Optimal transfer block size.

### `statfs.blocks`

* {number|bigint}

Total data blocks in filesystem.

### `statfs.bfree`

* {number|bigint}

Free blocks in filesystem.

### `statfs.bavail`

* {number|bigint}

Free blocks available to unprivileged user.

### `statfs.files`

* {number|bigint}

Total file nodes in filesystem.

### `statfs.ffree`

* {number|bigint}

Free file nodes in filesystem.

### `statfs.spare`

* {Array}

Padding bytes reserved for future use.

## Class: `fs.WriteStream`
<!-- YAML
added: v0.1.93
Expand Down Expand Up @@ -3465,6 +3549,34 @@ changes:

Synchronous stat(2).

## `fs.statfs(path[, options], callback)`
<!-- YAML
added: REPLACEME
-->
* `path` {string|Buffer|URL}
* `options` {Object}
* `bigint` {boolean} Whether the numeric values in the returned
[`fs.StatFs`][] object should be `bigint`. **Default:** `false`.
* `callback` {Function}
* `err` {Error}
* `stats` {fs.StatFs}

Asynchronous statfs(2). The callback gets two arguments `(err, stats)` where
`stats` is an [`fs.StatFs`][] object.

Returns information about the mounted filesystem which contains `path`.

In case of an error, the `err.code` will be one of [Common System Errors][].

## `fs.statfsSync(path[, options])`
* `path` {string|Buffer|URL}
* `options` {Object}
* `bigint` {boolean} Whether the numeric values in the returned
[`fs.StatFs`][] object should be `bigint`. **Default:** `false`.
* Returns: {fs.StatFs}

Synchronous statfs(2).

## `fs.symlink(target, path[, type], callback)`
<!-- YAML
added: v0.1.31
Expand Down Expand Up @@ -5124,7 +5236,19 @@ changes:

The `Promise` is resolved with the [`fs.Stats`][] object for the given `path`.

### `fsPromises.symlink(target, path[, type])`
<<<<<<< HEAD

### `fsPromises.statfs(path[, options])`

* `path` {string|Buffer|URL}
* `options` {Object}
* `bigint` {boolean} Whether the numeric values in the returned
[`fs.StatFs`][] object should be `bigint`. **Default:** `false`.
* Returns: {Promise}

The `Promise` is resolved with the [`fs.StatFs`][] object for the given `path`.

### `fsPromises.symlink(target, path\[, type\])`
<!-- YAML
added: v10.0.0
-->
Expand Down Expand Up @@ -5586,6 +5710,7 @@ the file contents.
[`fs.Dirent`]: #fs_class_fs_dirent
[`fs.FSWatcher`]: #fs_class_fs_fswatcher
[`fs.Stats`]: #fs_class_fs_stats
[`fs.StatFs`]: #fs_class_fs_statfs
[`fs.access()`]: #fs_fs_access_path_mode_callback
[`fs.chmod()`]: #fs_fs_chmod_path_mode_callback
[`fs.chown()`]: #fs_fs_chown_path_uid_gid_callback
Expand All @@ -5609,6 +5734,7 @@ the file contents.
[`fs.realpath()`]: #fs_fs_realpath_path_options_callback
[`fs.rmdir()`]: #fs_fs_rmdir_path_options_callback
[`fs.stat()`]: #fs_fs_stat_path_options_callback
[`fs.statfs()`]: #fs_fs_statfs_path_options_callback
[`fs.symlink()`]: #fs_fs_symlink_target_path_type_callback
[`fs.utimes()`]: #fs_fs_utimes_path_atime_mtime_callback
[`fs.watch()`]: #fs_fs_watch_filename_options_listener
Expand Down
38 changes: 38 additions & 0 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ const {
Dirent,
getDirents,
getOptions,
getStatFsFromBinding,
getValidatedPath,
getValidMode,
handleErrorFromBinding,
Expand Down Expand Up @@ -176,6 +177,19 @@ function makeStatsCallback(cb) {
};
}

// Special case of `makeCallback()` specific to async `statfs()`. Transforms
// the array passed to the callback to a javascript object
function makeStatfsCallback(cb) {
if (typeof cb !== 'function') {
throw new ERR_INVALID_CALLBACK(cb);
}

return (err, statsArray) => {
if (err) return cb(err);
cb(err, getStatFsFromBinding(statsArray));
};
}

const isFd = isUint32;

function isFileType(stats, fileType) {
Expand Down Expand Up @@ -936,6 +950,19 @@ function stat(path, options = { bigint: false }, callback) {
binding.stat(pathModule.toNamespacedPath(path), options.bigint, req);
}

function statfs(path, options = { bigint: false }, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}

callback = makeStatfsCallback(callback);
path = getValidatedPath(path);
const req = new FSReqCallback(options.bigint);
req.oncomplete = callback;
binding.statfs(pathModule.toNamespacedPath(path), options.bigint, req);
}

function fstatSync(fd, options = { bigint: false }) {
validateInt32(fd, 'fd', 0);
const ctx = { fd };
Expand All @@ -962,6 +989,15 @@ function statSync(path, options = { bigint: false }) {
return getStatsFromBinding(stats);
}

function statfsSync(path, options = { bigint: false }) {
path = getValidatedPath(path);
const ctx = { path };
const stats = binding.statfs(pathModule.toNamespacedPath(path),
options.bigint, undefined, ctx);
handleErrorFromBinding(ctx);
return getStatFsFromBinding(stats);
}

function readlink(path, options, callback) {
callback = makeCallback(typeof options === 'function' ? options : callback);
options = getOptions(options, {});
Expand Down Expand Up @@ -1920,7 +1956,9 @@ module.exports = fs = {
rmdir,
rmdirSync,
stat,
statfs,
statSync,
statfsSync,
symlink,
symlinkSync,
truncate,
Expand Down
9 changes: 9 additions & 0 deletions lib/internal/fs/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const {
getDirents,
getOptions,
getStatsFromBinding,
getStatFsFromBinding,
getValidatedPath,
getValidMode,
nullCheck,
Expand Down Expand Up @@ -405,6 +406,13 @@ async function stat(path, options = { bigint: false }) {
return getStatsFromBinding(result);
}

async function statfs(path, options = { bigint: false }) {
path = getValidatedPath(path);
const result = await binding.statfs(pathModule.toNamespacedPath(path),
options.bigint, kUsePromises);
return getStatFsFromBinding(result);
}

async function link(existingPath, newPath) {
existingPath = getValidatedPath(existingPath, 'existingPath');
newPath = getValidatedPath(newPath, 'newPath');
Expand Down Expand Up @@ -536,6 +544,7 @@ module.exports = {
symlink,
lstat,
stat,
statfs,
link,
unlink,
chmod,
Expand Down
20 changes: 20 additions & 0 deletions lib/internal/fs/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,25 @@ function getStatsFromBinding(stats, offset = 0) {
);
}

function StatFs(type, bsize, blocks, bfree, bavail, files, ffree,
spare1, spare2, spare3, spare4) {
this.type = type;
this.bsize = bsize;
this.blocks = blocks;
this.bfree = bfree;
this.bavail = bavail;
this.files = files;
this.ffree = ffree;
this.spare = [ spare1, spare2, spare3, spare4 ];
}

function getStatFsFromBinding(stats) {
return new StatFs(
stats[0], stats[1], stats[2], stats[3], stats[4], stats[5],
stats[6], stats[7], stats[8], stats[9], stats[10]
);
}

function stringToFlags(flags) {
if (typeof flags === 'number') {
return flags;
Expand Down Expand Up @@ -669,6 +688,7 @@ module.exports = {
getDirent,
getDirents,
getOptions,
getStatFsFromBinding,
getValidatedPath,
getValidMode,
handleErrorFromBinding,
Expand Down
8 changes: 8 additions & 0 deletions src/env-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,14 @@ inline AliasedBigUint64Array* Environment::fs_stats_field_bigint_array() {
return &fs_stats_field_bigint_array_;
}

inline AliasedFloat64Array* Environment::fs_statfs_field_array() {
return &fs_statfs_field_array_;
}

inline AliasedBigUint64Array* Environment::fs_statfs_field_bigint_array() {
return &fs_statfs_field_bigint_array_;
}

inline std::vector<std::unique_ptr<fs::FileHandleReadWrap>>&
Environment::file_handle_read_wrap_freelist() {
return file_handle_read_wrap_freelist_;
Expand Down
5 changes: 5 additions & 0 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ Environment::Environment(IsolateData* isolate_data,
thread_id_(thread_id == kNoThreadId ? AllocateThreadId() : thread_id),
fs_stats_field_array_(isolate_, kFsStatsBufferLength),
fs_stats_field_bigint_array_(isolate_, kFsStatsBufferLength),
fs_statfs_field_array_(isolate_, kFsStatfsBufferLength),
fs_statfs_field_bigint_array_(isolate_, kFsStatfsBufferLength),
context_(context->GetIsolate(), context) {
// We'll be creating new objects so make sure we've entered the context.
HandleScope handle_scope(isolate());
Expand Down Expand Up @@ -1068,6 +1070,9 @@ void Environment::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackField("fs_stats_field_array", fs_stats_field_array_);
tracker->TrackField("fs_stats_field_bigint_array",
fs_stats_field_bigint_array_);
tracker->TrackField("fs_statfs_field_array", fs_statfs_field_array_);
tracker->TrackField("fs_statfs_field_bigint_array",
fs_statfs_field_bigint_array_);
tracker->TrackField("cleanup_hooks", cleanup_hooks_);
tracker->TrackField("async_hooks", async_hooks_);
tracker->TrackField("immediate_info", immediate_info_);
Expand Down
24 changes: 24 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,24 @@ enum class FsStatsOffset {
constexpr size_t kFsStatsBufferLength =
static_cast<size_t>(FsStatsOffset::kFsStatsFieldsNumber) * 2;

enum class FsStatfsOffset {
kType = 0,
kBSize,
kBlocks,
kBFree,
kBAvail,
kFiles,
kFFree,
kSpare1,
kSpare2,
kSpare3,
kSpare4,
kFsStatFsFieldsNumber
};

constexpr size_t kFsStatfsBufferLength =
static_cast<size_t>(FsStatfsOffset::kFsStatFsFieldsNumber);

// PER_ISOLATE_* macros: We have a lot of per-isolate properties
// and adding and maintaining their getters and setters by hand would be
// difficult so let's make the preprocessor generate them for us.
Expand Down Expand Up @@ -1025,6 +1043,9 @@ class Environment : public MemoryRetainer {
inline AliasedFloat64Array* fs_stats_field_array();
inline AliasedBigUint64Array* fs_stats_field_bigint_array();

inline AliasedFloat64Array* fs_statfs_field_array();
inline AliasedBigUint64Array* fs_statfs_field_bigint_array();

inline std::vector<std::unique_ptr<fs::FileHandleReadWrap>>&
file_handle_read_wrap_freelist();

Expand Down Expand Up @@ -1386,6 +1407,9 @@ class Environment : public MemoryRetainer {
AliasedFloat64Array fs_stats_field_array_;
AliasedBigUint64Array fs_stats_field_bigint_array_;

AliasedFloat64Array fs_statfs_field_array_;
AliasedBigUint64Array fs_statfs_field_bigint_array_;

std::vector<std::unique_ptr<fs::FileHandleReadWrap>>
file_handle_read_wrap_freelist_;

Expand Down
Loading

0 comments on commit f7a3f8e

Please sign in to comment.