Skip to content

Commit

Permalink
Updated stats constructor to take an object instead of lots of arguments
Browse files Browse the repository at this point in the history
Added birthtime to Inode data
Changed Mutex to named export
Deprecated Stats.clone and BigIntStats.clone
Changed many imports to be type-only
Added StatsLike
Renamed LockedFS type parameter from T to FS
  • Loading branch information
james-pre committed Mar 21, 2024
1 parent 44a5a73 commit ee45994
Show file tree
Hide file tree
Showing 15 changed files with 140 additions and 109 deletions.
6 changes: 3 additions & 3 deletions src/FileIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class FileIndex<T> {
queue.push({ pwd: path, tree: children, parent: inode });
} else {
// This inode doesn't have correct size information, noted with -1.
inode = new IndexFileInode<Stats>(new Stats(FileType.FILE, -1, 0o555));
inode = new IndexFileInode<Stats>(new Stats({ mode: FileType.FILE | 0o555 }));
}
if (!parent) {
continue;
Expand Down Expand Up @@ -268,7 +268,7 @@ export class IndexFileInode<T> extends IndexInode<T> {
}

public toStats(): Stats {
return new Stats(FileType.FILE, 4096, 0o666);
return new Stats({ mode: FileType.FILE | 0o666, size: 4096 });
}
}

Expand All @@ -294,7 +294,7 @@ export class IndexDirInode<T> extends IndexInode<T> {
* @todo Should probably remove this at some point. This isn't the responsibility of the FileIndex.
*/
public get stats(): Stats {
return new Stats(FileType.DIRECTORY, 4096, 0o555);
return new Stats({ mode: FileType.DIRECTORY | 0o555, size: 4096 });
}
/**
* Alias of getStats()
Expand Down
2 changes: 1 addition & 1 deletion src/backends/AsyncMirror.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FileSystem, Sync, FileSystemMetadata } from '../filesystem.js';
import { ApiError, ErrorCode } from '../ApiError.js';
import { File, FileFlag, PreloadFile } from '../file.js';
import { Stats } from '../stats.js';
import type { Stats } from '../stats.js';
import { join } from '../emulation/path.js';
import { Cred } from '../cred.js';
import type { Backend } from './backend.js';
Expand Down
4 changes: 2 additions & 2 deletions src/backends/AsyncStore.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { dirname, basename, join, resolve } from '../emulation/path.js';
import { ApiError, ErrorCode } from '../ApiError.js';
import { Cred } from '../cred.js';
import type { Cred } from '../cred.js';
import { W_OK, R_OK } from '../emulation/constants.js';
import { PreloadFile, File, FileFlag } from '../file.js';
import { Async, FileSystem, type FileSystemMetadata } from '../filesystem.js';
import { randomIno, type Ino, Inode } from '../inode.js';
import { Stats, FileType } from '../stats.js';
import { type Stats, FileType } from '../stats.js';
import { encode, decodeDirListing, encodeDirListing } from '../utils.js';
import { rootIno } from '../inode.js';

Expand Down
15 changes: 7 additions & 8 deletions src/backends/Locked.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import Mutex from '../mutex.js';
import { FileSystem, FileSystemMetadata } from '../filesystem.js';
import { FileFlag } from '../file.js';
import { Stats } from '../stats.js';
import { File } from '../file.js';
import { Cred } from '../cred.js';
import type { Cred } from '../cred.js';
import type { File, FileFlag } from '../file.js';
import type { FileSystem, FileSystemMetadata } from '../filesystem.js';
import { Mutex } from '../mutex.js';
import type { Stats } from '../stats.js';

/**
* This class serializes access to an underlying async filesystem.
Expand All @@ -14,10 +13,10 @@ import { Cred } from '../cred.js';
* LockedFS to avoid having to reason about the correctness of
* multiple requests interleaving.
*/
export class LockedFS<T extends FileSystem> implements FileSystem {
export class LockedFS<FS extends FileSystem> implements FileSystem {
private _mu: Mutex = new Mutex();

constructor(public readonly fs: T) {}
constructor(public readonly fs: FS) {}

public async ready(): Promise<this> {
await this.fs.ready();
Expand Down
12 changes: 5 additions & 7 deletions src/backends/Overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,8 @@ export class UnlockedOverlayFS extends FileSystem {
if (this._deletedFiles.has(p)) {
throw ApiError.ENOENT(p);
}
const oldStat = Stats.clone(await this._readable.stat(p, cred));
// Make the oldStat's mode writable. Preserve the topmost part of the
// mode, which specifies if it is a file or a directory.
const oldStat = new Stats(await this._readable.stat(p, cred));
// Make the oldStat's mode writable. Preserve the topmost part of the mode, which specifies the type
oldStat.mode |= 0o222;
return oldStat;
}
Expand All @@ -214,9 +213,8 @@ export class UnlockedOverlayFS extends FileSystem {
if (this._deletedFiles.has(p)) {
throw ApiError.ENOENT(p);
}
const oldStat = Stats.clone(this._readable.statSync(p, cred));
// Make the oldStat's mode writable. Preserve the topmost part of the
// mode, which specifies if it is a file or a directory.
const oldStat = new Stats(this._readable.statSync(p, cred));
// Make the oldStat's mode writable. Preserve the topmost part of the mode, which specifies the type.
oldStat.mode |= 0o222;
return oldStat;
}
Expand All @@ -228,7 +226,7 @@ export class UnlockedOverlayFS extends FileSystem {
}
// Create an OverlayFile.
const file = await this._readable.openFile(path, FileFlag.FromString('r'), cred);
const stats = Stats.clone(await file.stat());
const stats = new Stats(await file.stat());
const { buffer } = await file.read(new Uint8Array(stats.size));
return new OverlayFile(this, path, flag, stats, buffer);
}
Expand Down
2 changes: 1 addition & 1 deletion src/backends/SyncStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { W_OK, R_OK } from '../emulation/constants.js';
import { FileFlag, PreloadFile } from '../file.js';
import { type FileSystemMetadata, FileSystem, Sync } from '../filesystem.js';
import { randomIno, type Ino, Inode } from '../inode.js';
import { Stats, FileType } from '../stats.js';
import { type Stats, FileType } from '../stats.js';
import { decodeDirListing, encode, encodeDirListing } from '../utils.js';
import { rootIno } from '../inode.js';

Expand Down
4 changes: 2 additions & 2 deletions src/emulation/callbacks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type * as Node from 'fs';
import { ApiError, ErrorCode } from '../ApiError.js';
import { TwoArgCallback, NoArgCallback, ThreeArgCallback, FileContents } from '../filesystem.js';
import { BigIntStats, Stats } from '../stats.js';
import { BigIntStats, type Stats } from '../stats.js';
import { fd2file, nop, normalizeMode, PathLike } from './shared.js';
import * as promises from './promises.js';
import { R_OK } from './constants.js';
Expand Down Expand Up @@ -231,7 +231,7 @@ export function fstat(fd: number, options?: Node.StatOptions | TwoArgCallback<St

fd2file(fd)
.stat()
.then(stats => cb(null, <Stats & BigIntStats>(typeof options == 'object' && options?.bigint ? BigIntStats.clone(stats) : stats)))
.then(stats => cb(null, <Stats & BigIntStats>(typeof options == 'object' && options?.bigint ? new BigIntStats(stats) : stats)))
.catch(cb);
}
fstat satisfies Omit<typeof Node.fstat, '__promisify__'>;
Expand Down
4 changes: 2 additions & 2 deletions src/emulation/dir.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Dirent as _Dirent, Dir as _Dir } from 'fs';
import { NoArgCallback, TwoArgCallback } from '../filesystem.js';
import { Stats } from '../stats.js';
import type { NoArgCallback, TwoArgCallback } from '../filesystem.js';
import type { Stats } from '../stats.js';
import { readdir } from './promises.js';
import { ApiError, ErrorCode } from '../ApiError.js';
import { readdirSync } from './sync.js';
Expand Down
6 changes: 3 additions & 3 deletions src/emulation/promises.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ActionType, File, FileFlag } from '../file.js';
import { normalizePath, normalizeMode, getFdForFile, normalizeOptions, fd2file, fdMap, normalizeTime, cred, nop, resolveFS, fixError, mounts } from './shared.js';
import type { PathLike, BufferToUint8Array } from './shared.js';
import { FileContents, FileSystem } from '../filesystem.js';
import { BigIntStats, FileType, Stats } from '../stats.js';
import { BigIntStats, FileType, type Stats } from '../stats.js';
import { decode, encode } from '../utils.js';
import { Dirent } from './dir.js';
import { dirname, join } from './path.js';
Expand Down Expand Up @@ -284,7 +284,7 @@ export async function stat(path: PathLike, options?: { bigint?: false }): Promis
export async function stat(path: PathLike, options?: Node.StatOptions): Promise<Stats | BigIntStats>;
export async function stat(path: PathLike, options?: Node.StatOptions): Promise<Stats | BigIntStats> {
const stats: Stats = await doOp('stat', true, path, cred);
return options?.bigint ? BigIntStats.clone(stats) : stats;
return options?.bigint ? new BigIntStats(stats) : stats;
}
stat satisfies typeof Node.promises.stat;

Expand All @@ -299,7 +299,7 @@ export async function lstat(path: PathLike, options?: { bigint?: false }): Promi
export async function lstat(path: PathLike, options: { bigint: true }): Promise<BigIntStats>;
export async function lstat(path: PathLike, options?: Node.StatOptions): Promise<Stats | BigIntStats> {
const stats: Stats = await doOp('stat', false, path, cred);
return options?.bigint ? BigIntStats.clone(stats) : stats;
return options?.bigint ? new BigIntStats(stats) : stats;
}
lstat satisfies typeof Node.promises.lstat;

Expand Down
8 changes: 4 additions & 4 deletions src/emulation/sync.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ApiError, ErrorCode } from '../ApiError.js';
import { ActionType, File, FileFlag } from '../file.js';
import { FileContents, FileSystem } from '../filesystem.js';
import { BigIntStats, FileType, Stats } from '../stats.js';
import { BigIntStats, FileType, type Stats } from '../stats.js';
import type { symlink, ReadSyncOptions, StatOptions, BaseEncodingOptions, BufferEncodingOption } from 'fs';
import type * as Node from 'fs';
import {
Expand Down Expand Up @@ -93,7 +93,7 @@ export function statSync(path: PathLike, options?: { bigint: false }): Stats;
export function statSync(path: PathLike, options: { bigint: true }): BigIntStats;
export function statSync(path: PathLike, options?: StatOptions): Stats | BigIntStats {
const stats: Stats = doOp('statSync', true, path, cred);
return options?.bigint ? BigIntStats.clone(stats) : stats;
return options?.bigint ? new BigIntStats(stats) : stats;
}
statSync satisfies typeof Node.statSync;

Expand All @@ -107,7 +107,7 @@ export function lstatSync(path: PathLike, options?: { bigint: false }): Stats;
export function lstatSync(path: PathLike, options: { bigint: true }): BigIntStats;
export function lstatSync(path: PathLike, options?: StatOptions): Stats | BigIntStats {
const stats: Stats = doOp('statSync', false, path, cred);
return options?.bigint ? BigIntStats.clone(stats) : stats;
return options?.bigint ? new BigIntStats(stats) : stats;
}
lstatSync satisfies typeof Node.lstatSync;

Expand Down Expand Up @@ -341,7 +341,7 @@ export function fstatSync(fd: number, options?: { bigint?: false }): Stats;
export function fstatSync(fd: number, options: { bigint: true }): BigIntStats;
export function fstatSync(fd: number, options?: StatOptions): Stats | BigIntStats {
const stats: Stats = fd2file(fd).statSync();
return options?.bigint ? BigIntStats.clone(stats) : stats;
return options?.bigint ? new BigIntStats(stats) : stats;
}
fstatSync satisfies typeof Node.fstatSync;

Expand Down
4 changes: 2 additions & 2 deletions src/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -479,14 +479,14 @@ export abstract class PreloadFile<FS extends FileSystem> extends File {
* Asynchronous `stat`.
*/
public async stat(): Promise<Stats> {
return Stats.clone(this.stats);
return new Stats(this.stats);
}

/**
* Synchronous `stat`.
*/
public statSync(): Stats {
return Stats.clone(this.stats);
return new Stats(this.stats);
}

/**
Expand Down
6 changes: 3 additions & 3 deletions src/filesystem.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ApiError, ErrorCode } from './ApiError.js';
import { Stats } from './stats.js';
import { File, FileFlag } from './file.js';
import { Cred } from './cred.js';
import type { Stats } from './stats.js';
import type { File, FileFlag } from './file.js';
import type { Cred } from './cred.js';

export type NoArgCallback = (e?: ApiError) => unknown;
export type TwoArgCallback<T> = (e?: ApiError, rv?: T) => unknown;
Expand Down
62 changes: 31 additions & 31 deletions src/inode.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { S_IFMT } from './emulation/constants.js';
import { Stats, FileType } from './stats.js';
import { Stats, type StatsLike } from './stats.js';

enum Offset {
ino = 0,
Expand All @@ -9,8 +8,9 @@ enum Offset {
uid = 18, // 22
gid = 22, // 26
atime = 26, // 30
mtime = 34, // 38
ctime = 42, // 46
birthtime = 34, // 38
mtime = 42, // 46
ctime = 50, // 54
}

export type Ino = bigint;
Expand Down Expand Up @@ -41,7 +41,7 @@ export function randomIno(): Ino {
/**
* Generic inode definition that can easily be serialized.
*/
export class Inode {
export class Inode implements StatsLike {
public readonly buffer: ArrayBufferLike;

public get data(): Uint8Array {
Expand All @@ -52,7 +52,7 @@ export class Inode {

constructor(buffer?: ArrayBufferLike) {
const setDefaults = !buffer;
buffer ??= new ArrayBuffer(50);
buffer ??= new ArrayBuffer(58);
this.view = new DataView(buffer);
this.buffer = buffer;

Expand All @@ -65,9 +65,10 @@ export class Inode {
this.nlink = 1;
this.size = 4096;
const now = Date.now();
this.atime = now;
this.mtime = now;
this.ctime = now;
this.atimeMs = now;
this.mtimeMs = now;
this.ctimeMs = now;
this.birthtimeMs = now;
}

public get ino(): Ino {
Expand Down Expand Up @@ -118,44 +119,43 @@ export class Inode {
this.view.setUint32(Offset.gid, value, true);
}

public get atime(): number {
public get atimeMs(): number {
return this.view.getFloat64(Offset.atime, true);
}

public set atime(value: number) {
public set atimeMs(value: number) {
this.view.setFloat64(Offset.atime, value, true);
}

public get mtime(): number {
public get birthtimeMs(): number {
return this.view.getFloat64(Offset.birthtime, true);
}

public set birthtimeMs(value: number) {
this.view.setFloat64(Offset.birthtime, value, true);
}

public get mtimeMs(): number {
return this.view.getFloat64(Offset.mtime, true);
}

public set mtime(value: number) {
public set mtimeMs(value: number) {
this.view.setFloat64(Offset.mtime, value, true);
}

public get ctime(): number {
public get ctimeMs(): number {
return this.view.getFloat64(Offset.ctime, true);
}

public set ctime(value: number) {
public set ctimeMs(value: number) {
this.view.setFloat64(Offset.ctime, value, true);
}

/**
* Handy function that converts the Inode to a Node Stats object.
*/
public toStats(): Stats {
return new Stats(
(this.mode & S_IFMT) === FileType.DIRECTORY ? FileType.DIRECTORY : FileType.FILE,
this.size,
this.mode,
this.atime,
this.mtime,
this.ctime,
this.uid,
this.gid
);
return new Stats(this);
}

/**
Expand Down Expand Up @@ -202,17 +202,17 @@ export class Inode {
hasChanged = true;
}

if (this.atime !== stats.atimeMs) {
this.atime = stats.atimeMs;
if (this.atimeMs !== stats.atimeMs) {
this.atimeMs = stats.atimeMs;
hasChanged = true;
}
if (this.mtime !== stats.mtimeMs) {
this.mtime = stats.mtimeMs;
if (this.mtimeMs !== stats.mtimeMs) {
this.mtimeMs = stats.mtimeMs;
hasChanged = true;
}

if (this.ctime !== stats.ctimeMs) {
this.ctime = stats.ctimeMs;
if (this.ctimeMs !== stats.ctimeMs) {
this.ctimeMs = stats.ctimeMs;
hasChanged = true;
}

Expand Down
2 changes: 1 addition & 1 deletion src/mutex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Non-recursive mutex
* @internal
*/
export default class Mutex {
export class Mutex {
private _locks: Map<string, (() => void)[]> = new Map();

public lock(path: string): Promise<void> {
Expand Down
Loading

0 comments on commit ee45994

Please sign in to comment.