mirror of
https://github.com/denoland/deno.git
synced 2024-11-25 15:29:32 -05:00
feat(fs): add more unix-only fields to FileInfo (#3680)
This commit is contained in:
parent
5856d21a2e
commit
91757f63fd
5 changed files with 204 additions and 44 deletions
|
@ -1,5 +1,6 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
import { StatResponse } from "./stat.ts";
|
import { StatResponse } from "./stat.ts";
|
||||||
|
import { build } from "./build.ts";
|
||||||
|
|
||||||
/** A FileInfo describes a file and is returned by `stat`, `lstat`,
|
/** A FileInfo describes a file and is returned by `stat`, `lstat`,
|
||||||
* `statSync`, `lstatSync`.
|
* `statSync`, `lstatSync`.
|
||||||
|
@ -22,13 +23,38 @@ export interface FileInfo {
|
||||||
* be available on all platforms.
|
* be available on all platforms.
|
||||||
*/
|
*/
|
||||||
created: number | null;
|
created: number | null;
|
||||||
|
|
||||||
|
/** The file or directory name. */
|
||||||
|
name: string | null;
|
||||||
|
|
||||||
|
/** ID of the device containing the file. Unix only. */
|
||||||
|
dev: number | null;
|
||||||
|
|
||||||
|
/** Inode number. Unix only. */
|
||||||
|
ino: number | null;
|
||||||
|
|
||||||
/** The underlying raw st_mode bits that contain the standard Unix permissions
|
/** The underlying raw st_mode bits that contain the standard Unix permissions
|
||||||
* for this file/directory. TODO Match behavior with Go on windows for mode.
|
* for this file/directory. TODO Match behavior with Go on windows for mode.
|
||||||
*/
|
*/
|
||||||
mode: number | null;
|
mode: number | null;
|
||||||
|
|
||||||
/** The file or directory name. */
|
/** Number of hard links pointing to this file. Unix only. */
|
||||||
name: string | null;
|
nlink: number | null;
|
||||||
|
|
||||||
|
/** User ID of the owner of this file. Unix only. */
|
||||||
|
uid: number | null;
|
||||||
|
|
||||||
|
/** User ID of the owner of this file. Unix only. */
|
||||||
|
gid: number | null;
|
||||||
|
|
||||||
|
/** Device ID of this file. Unix only. */
|
||||||
|
rdev: number | null;
|
||||||
|
|
||||||
|
/** Blocksize for filesystem I/O. Unix only. */
|
||||||
|
blksize: number | null;
|
||||||
|
|
||||||
|
/** Number of blocks allocated to the file, in 512-byte units. Unix only. */
|
||||||
|
blocks: number | null;
|
||||||
|
|
||||||
/** Returns whether this is info for a regular file. This result is mutually
|
/** Returns whether this is info for a regular file. This result is mutually
|
||||||
* exclusive to `FileInfo.isDirectory` and `FileInfo.isSymlink`.
|
* exclusive to `FileInfo.isDirectory` and `FileInfo.isSymlink`.
|
||||||
|
@ -54,17 +80,37 @@ export class FileInfoImpl implements FileInfo {
|
||||||
modified: number | null;
|
modified: number | null;
|
||||||
accessed: number | null;
|
accessed: number | null;
|
||||||
created: number | null;
|
created: number | null;
|
||||||
mode: number | null;
|
|
||||||
name: string | null;
|
name: string | null;
|
||||||
|
|
||||||
|
dev: number | null;
|
||||||
|
ino: number | null;
|
||||||
|
mode: number | null;
|
||||||
|
nlink: number | null;
|
||||||
|
uid: number | null;
|
||||||
|
gid: number | null;
|
||||||
|
rdev: number | null;
|
||||||
|
blksize: number | null;
|
||||||
|
blocks: number | null;
|
||||||
|
|
||||||
/* @internal */
|
/* @internal */
|
||||||
constructor(private _res: StatResponse) {
|
constructor(private _res: StatResponse) {
|
||||||
|
const isUnix = build.os === "mac" || build.os === "linux";
|
||||||
const modified = this._res.modified;
|
const modified = this._res.modified;
|
||||||
const accessed = this._res.accessed;
|
const accessed = this._res.accessed;
|
||||||
const created = this._res.created;
|
const created = this._res.created;
|
||||||
const hasMode = this._res.hasMode;
|
|
||||||
const mode = this._res.mode; // negative for invalid mode (Windows)
|
|
||||||
const name = this._res.name;
|
const name = this._res.name;
|
||||||
|
// Unix only
|
||||||
|
const {
|
||||||
|
dev,
|
||||||
|
ino,
|
||||||
|
mode,
|
||||||
|
nlink,
|
||||||
|
uid,
|
||||||
|
gid,
|
||||||
|
rdev,
|
||||||
|
blksize,
|
||||||
|
blocks
|
||||||
|
} = this._res;
|
||||||
|
|
||||||
this._isFile = this._res.isFile;
|
this._isFile = this._res.isFile;
|
||||||
this._isSymlink = this._res.isSymlink;
|
this._isSymlink = this._res.isSymlink;
|
||||||
|
@ -72,9 +118,17 @@ export class FileInfoImpl implements FileInfo {
|
||||||
this.modified = modified ? modified : null;
|
this.modified = modified ? modified : null;
|
||||||
this.accessed = accessed ? accessed : null;
|
this.accessed = accessed ? accessed : null;
|
||||||
this.created = created ? created : null;
|
this.created = created ? created : null;
|
||||||
// null on Windows
|
|
||||||
this.mode = hasMode ? mode : null;
|
|
||||||
this.name = name ? name : null;
|
this.name = name ? name : null;
|
||||||
|
// Only non-null if on Unix
|
||||||
|
this.dev = isUnix ? dev : null;
|
||||||
|
this.ino = isUnix ? ino : null;
|
||||||
|
this.mode = isUnix ? mode : null;
|
||||||
|
this.nlink = isUnix ? nlink : null;
|
||||||
|
this.uid = isUnix ? uid : null;
|
||||||
|
this.gid = isUnix ? gid : null;
|
||||||
|
this.rdev = isUnix ? rdev : null;
|
||||||
|
this.blksize = isUnix ? blksize : null;
|
||||||
|
this.blocks = isUnix ? blocks : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
isFile(): boolean {
|
isFile(): boolean {
|
||||||
|
|
31
cli/js/lib.deno_runtime.d.ts
vendored
31
cli/js/lib.deno_runtime.d.ts
vendored
|
@ -732,12 +732,28 @@ declare namespace Deno {
|
||||||
* be available on all platforms.
|
* be available on all platforms.
|
||||||
*/
|
*/
|
||||||
created: number | null;
|
created: number | null;
|
||||||
|
/** The file or directory name. */
|
||||||
|
name: string | null;
|
||||||
|
/** ID of the device containing the file. Unix only. */
|
||||||
|
dev: number | null;
|
||||||
|
/** Inode number. Unix only. */
|
||||||
|
ino: number | null;
|
||||||
/** The underlying raw st_mode bits that contain the standard Unix permissions
|
/** The underlying raw st_mode bits that contain the standard Unix permissions
|
||||||
* for this file/directory. TODO Match behavior with Go on windows for mode.
|
* for this file/directory. TODO Match behavior with Go on windows for mode.
|
||||||
*/
|
*/
|
||||||
mode: number | null;
|
mode: number | null;
|
||||||
/** The file or directory name. */
|
/** Number of hard links pointing to this file. Unix only. */
|
||||||
name: string | null;
|
nlink: number | null;
|
||||||
|
/** User ID of the owner of this file. Unix only. */
|
||||||
|
uid: number | null;
|
||||||
|
/** User ID of the owner of this file. Unix only. */
|
||||||
|
gid: number | null;
|
||||||
|
/** Device ID of this file. Unix only. */
|
||||||
|
rdev: number | null;
|
||||||
|
/** Blocksize for filesystem I/O. Unix only. */
|
||||||
|
blksize: number | null;
|
||||||
|
/** Number of blocks allocated to the file, in 512-byte units. Unix only. */
|
||||||
|
blocks: number | null;
|
||||||
/** Returns whether this is info for a regular file. This result is mutually
|
/** Returns whether this is info for a regular file. This result is mutually
|
||||||
* exclusive to `FileInfo.isDirectory` and `FileInfo.isSymlink`.
|
* exclusive to `FileInfo.isDirectory` and `FileInfo.isSymlink`.
|
||||||
*/
|
*/
|
||||||
|
@ -827,9 +843,16 @@ declare namespace Deno {
|
||||||
modified: number;
|
modified: number;
|
||||||
accessed: number;
|
accessed: number;
|
||||||
created: number;
|
created: number;
|
||||||
mode: number;
|
|
||||||
hasMode: boolean;
|
|
||||||
name: string | null;
|
name: string | null;
|
||||||
|
dev: number;
|
||||||
|
ino: number;
|
||||||
|
mode: number;
|
||||||
|
nlink: number;
|
||||||
|
uid: number;
|
||||||
|
gid: number;
|
||||||
|
rdev: number;
|
||||||
|
blksize: number;
|
||||||
|
blocks: number;
|
||||||
}
|
}
|
||||||
/** Queries the file system for information on the path provided. If the given
|
/** Queries the file system for information on the path provided. If the given
|
||||||
* path is a symlink information about the symlink will be returned.
|
* path is a symlink information about the symlink will be returned.
|
||||||
|
|
|
@ -10,9 +10,17 @@ export interface StatResponse {
|
||||||
modified: number;
|
modified: number;
|
||||||
accessed: number;
|
accessed: number;
|
||||||
created: number;
|
created: number;
|
||||||
mode: number;
|
|
||||||
hasMode: boolean; // false on windows
|
|
||||||
name: string | null;
|
name: string | null;
|
||||||
|
// Unix only members
|
||||||
|
dev: number;
|
||||||
|
ino: number;
|
||||||
|
mode: number;
|
||||||
|
nlink: number;
|
||||||
|
uid: number;
|
||||||
|
gid: number;
|
||||||
|
rdev: number;
|
||||||
|
blksize: number;
|
||||||
|
blocks: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Queries the file system for information on the path provided. If the given
|
/** Queries the file system for information on the path provided. If the given
|
||||||
|
|
|
@ -170,3 +170,53 @@ testPerm({ read: true }, async function lstatNotFound(): Promise<void> {
|
||||||
assert(caughtError);
|
assert(caughtError);
|
||||||
assertEquals(badInfo, undefined);
|
assertEquals(badInfo, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isWindows = Deno.build.os === "win";
|
||||||
|
|
||||||
|
// OS dependent tests
|
||||||
|
if (isWindows) {
|
||||||
|
testPerm(
|
||||||
|
{ read: true, write: true },
|
||||||
|
async function statNoUnixFields(): Promise<void> {
|
||||||
|
const enc = new TextEncoder();
|
||||||
|
const data = enc.encode("Hello");
|
||||||
|
const tempDir = Deno.makeTempDirSync();
|
||||||
|
const filename = tempDir + "/test.txt";
|
||||||
|
Deno.writeFileSync(filename, data, { perm: 0o666 });
|
||||||
|
const s = Deno.statSync(filename);
|
||||||
|
assert(s.dev === null);
|
||||||
|
assert(s.ino === null);
|
||||||
|
assert(s.mode === null);
|
||||||
|
assert(s.nlink === null);
|
||||||
|
assert(s.uid === null);
|
||||||
|
assert(s.gid === null);
|
||||||
|
assert(s.rdev === null);
|
||||||
|
assert(s.blksize === null);
|
||||||
|
assert(s.blocks === null);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
testPerm(
|
||||||
|
{ read: true, write: true },
|
||||||
|
async function statUnixFields(): Promise<void> {
|
||||||
|
const enc = new TextEncoder();
|
||||||
|
const data = enc.encode("Hello");
|
||||||
|
const tempDir = Deno.makeTempDirSync();
|
||||||
|
const filename = tempDir + "/test.txt";
|
||||||
|
const filename2 = tempDir + "/test2.txt";
|
||||||
|
Deno.writeFileSync(filename, data, { perm: 0o666 });
|
||||||
|
// Create a link
|
||||||
|
Deno.linkSync(filename, filename2);
|
||||||
|
const s = Deno.statSync(filename);
|
||||||
|
assert(s.dev !== null);
|
||||||
|
assert(s.ino !== null);
|
||||||
|
assertEquals(s.mode & 0o666, 0o666);
|
||||||
|
assertEquals(s.nlink, 2);
|
||||||
|
assert(s.uid !== null);
|
||||||
|
assert(s.gid !== null);
|
||||||
|
assert(s.rdev !== null);
|
||||||
|
assert(s.blksize !== null);
|
||||||
|
assert(s.blocks !== null);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@ use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::UNIX_EPOCH;
|
use std::time::UNIX_EPOCH;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
use std::os::unix::fs::MetadataExt;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
|
@ -226,14 +228,55 @@ macro_rules! to_seconds {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(unix))]
|
#[inline(always)]
|
||||||
fn get_mode(perm: &fs::Permissions) -> u32 {
|
fn get_stat_json(
|
||||||
perm.mode()
|
metadata: fs::Metadata,
|
||||||
}
|
maybe_name: Option<String>,
|
||||||
|
) -> Result<Value, ErrBox> {
|
||||||
|
// Unix stat member (number types only). 0 if not on unix.
|
||||||
|
macro_rules! usm {
|
||||||
|
($member: ident) => {{
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
metadata.$member()
|
||||||
|
}
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
{
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(any(unix)))]
|
let mut json_val = json!({
|
||||||
fn get_mode(_perm: &fs::Permissions) -> u32 {
|
"isFile": metadata.is_file(),
|
||||||
0
|
"isSymlink": metadata.file_type().is_symlink(),
|
||||||
|
"len": metadata.len(),
|
||||||
|
// In seconds. Available on both Unix or Windows.
|
||||||
|
"modified":to_seconds!(metadata.modified()),
|
||||||
|
"accessed":to_seconds!(metadata.accessed()),
|
||||||
|
"created":to_seconds!(metadata.created()),
|
||||||
|
// Following are only valid under Unix.
|
||||||
|
"dev": usm!(dev),
|
||||||
|
"ino": usm!(ino),
|
||||||
|
"mode": usm!(mode),
|
||||||
|
"nlink": usm!(nlink),
|
||||||
|
"uid": usm!(uid),
|
||||||
|
"gid": usm!(gid),
|
||||||
|
"rdev": usm!(rdev),
|
||||||
|
// TODO(kevinkassimo): *time_nsec requires BigInt.
|
||||||
|
// Probably should be treated as String if we need to add them.
|
||||||
|
"blksize": usm!(blksize),
|
||||||
|
"blocks": usm!(blocks),
|
||||||
|
});
|
||||||
|
|
||||||
|
// "name" is an optional field by our design.
|
||||||
|
if let Some(name) = maybe_name {
|
||||||
|
if let serde_json::Value::Object(ref mut m) = json_val {
|
||||||
|
m.insert("name".to_owned(), json!(name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(json_val)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -265,17 +308,7 @@ fn op_stat(
|
||||||
} else {
|
} else {
|
||||||
fs::metadata(&filename)?
|
fs::metadata(&filename)?
|
||||||
};
|
};
|
||||||
|
get_stat_json(metadata, None)
|
||||||
Ok(json!({
|
|
||||||
"isFile": metadata.is_file(),
|
|
||||||
"isSymlink": metadata.file_type().is_symlink(),
|
|
||||||
"len": metadata.len(),
|
|
||||||
"modified":to_seconds!(metadata.modified()),
|
|
||||||
"accessed":to_seconds!(metadata.accessed()),
|
|
||||||
"created":to_seconds!(metadata.created()),
|
|
||||||
"mode": get_mode(&metadata.permissions()),
|
|
||||||
"hasMode": cfg!(target_family = "unix"), // false on windows,
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,19 +368,11 @@ fn op_read_dir(
|
||||||
.map(|entry| {
|
.map(|entry| {
|
||||||
let entry = entry.unwrap();
|
let entry = entry.unwrap();
|
||||||
let metadata = entry.metadata().unwrap();
|
let metadata = entry.metadata().unwrap();
|
||||||
let file_type = metadata.file_type();
|
get_stat_json(
|
||||||
|
metadata,
|
||||||
json!({
|
Some(entry.file_name().to_str().unwrap().to_owned()),
|
||||||
"isFile": file_type.is_file(),
|
)
|
||||||
"isSymlink": file_type.is_symlink(),
|
.unwrap()
|
||||||
"len": metadata.len(),
|
|
||||||
"modified": to_seconds!(metadata.modified()),
|
|
||||||
"accessed": to_seconds!(metadata.accessed()),
|
|
||||||
"created": to_seconds!(metadata.created()),
|
|
||||||
"mode": get_mode(&metadata.permissions()),
|
|
||||||
"name": entry.file_name().to_str().unwrap(),
|
|
||||||
"hasMode": cfg!(target_family = "unix"), // false on windows,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue