Skip to content

Commit bccdb4c

Browse files
committed
FS attribute changes:
removed unused `no_buffer_resize` renamed `no_async` to `no_async_preload` added `no_atime`, `no_suid`, `sync` Changed `InodeFlags` to follow `S_*` instead of `FS_*_FL` Added `FileFlag` (for `FS_*_FL`) to ioctl.ts Improved read test slightly
1 parent ff53f03 commit bccdb4c

File tree

10 files changed

+160
-82
lines changed

10 files changed

+160
-82
lines changed

src/backends/store/fs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
100100
super(store.type ?? 0x6b766673, store.name);
101101
store._fs = this;
102102
this._uuid = store.uuid ?? this.uuid;
103+
this.label = store.label;
103104
debug(this.name + ': supports features: ' + this.store.flags?.join(', '));
104105
}
105106

src/backends/store/store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { withErrno } from 'kerium';
2-
import { err, warn } from 'kerium/log';
2+
import { warn } from 'kerium/log';
33
import type { UUID } from 'node:crypto';
44
import { Resource } from 'utilium/cache.js';
55
import type { UsageInfo } from '../../internal/filesystem.js';

src/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export async function resolveMountConfig<T extends Backend>(configuration: Mount
6565

6666
checkOptions(backend, configuration);
6767
const mount = (await backend.create(configuration)) as FilesystemOf<T>;
68-
if (configuration.disableAsyncCache) mount.attributes.set('no_async');
68+
if (configuration.disableAsyncCache) mount.attributes.set('no_async_preload');
6969
await mount.ready();
7070
return mount;
7171
}

src/internal/filesystem.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,25 +42,24 @@ export interface UsageInfo {
4242
* @internal
4343
*/
4444
export type FileSystemAttributes = {
45-
/** If set, disables `PreloadFile` from using a resizable array buffer. */
46-
no_buffer_resize: void;
47-
4845
/**
4946
* If set disables async file systems from preloading their contents.
5047
* This means *sync operations will not work* (unless the contents are cached)
5148
* It has no affect on sync file systems.
5249
*/
53-
no_async: void;
50+
no_async_preload: void;
5451

5552
/**
5653
* Currently unused. In the future, this will disable caching.
57-
* Not recommended due to performance impact.
54+
* Analogous to `S_DAX` on every file.
5855
*/
5956
no_cache: void;
6057

6158
/**
6259
* If set, the file system should not be written to.
6360
* This should be set for read-only file systems.
61+
* Note this does NOT trigger EROFS errors;
62+
* writes will silently be dropped.
6463
*/
6564
no_write: void;
6665

@@ -75,6 +74,24 @@ export type FileSystemAttributes = {
7574
* @internal
7675
*/
7776
default_stream_write: void;
77+
78+
/**
79+
* Do not update access times.
80+
*/
81+
no_atime: void;
82+
83+
/**
84+
* Ignore suid and sgid bits.
85+
* @todo Implement
86+
* @experimental
87+
*/
88+
no_suid: void;
89+
90+
/**
91+
* Writes are synced at once
92+
* @experimental
93+
*/
94+
sync: void;
7895
};
7996

8097
/**

src/internal/inode.ts

Lines changed: 38 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -214,79 +214,53 @@ export const _inode_fields = [
214214
* 2. 66 bytes. Renamed the first member from `ino` to `data` and added a separate `ino` field
215215
* 3. 72 bytes. Changed the ID fields from 64 to 32 bits and added `flags`.
216216
* 4. >= 128 bytes. Added extended attributes.
217-
* 5. (current) 4 KiB. Changed to a fixed size to make a lot of size-related stuff easier.
217+
* 5. (current) 4 KiB. Changed flags
218218
* @internal @hidden
219219
*/
220220
export const _inode_version = 5;
221221

222222
/**
223-
* Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)
224-
* @see `FS_*_FL` in `include/uapi/linux/fs.h` (around L250)
223+
* Inode flags
224+
* @see `S_*` in `include/linux/fs.h` (around L2325)
225225
* @experimental
226226
*/
227227
export enum InodeFlags {
228-
/** Secure deletion */
229-
SecureRm = 0x00000001,
230-
/** Undelete */
231-
Undelete = 0x00000002,
232-
/** Compress file */
233-
Compress = 0x00000004,
234-
/** Synchronous updates */
235-
Sync = 0x00000008,
228+
/** Writes are synced at once */
229+
Sync = 1 << 0,
230+
/** Do not update access times */
231+
NoAtime = 1 << 1,
232+
/** Append-only file */
233+
Append = 1 << 2,
236234
/** Immutable file */
237-
Immutable = 0x00000010,
238-
/** Writes to file may only append */
239-
Append = 0x00000020,
240-
/** do not dump file */
241-
NoDump = 0x00000040,
242-
/** do not update atime */
243-
NoAtime = 0x00000080,
244-
// Reserved for compression usage...
245-
Dirty = 0x00000100,
246-
/** One or more compressed clusters */
247-
CompressBlk = 0x00000200,
248-
/** Don't compress */
249-
NoCompress = 0x00000400,
250-
// End compression flags --- maybe not all used
251-
/** Encrypted file */
252-
Encrypt = 0x00000800,
253-
/** btree format dir */
254-
Btree = 0x00001000,
255-
/** hash-indexed directory */
256-
// eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
257-
Index = 0x00001000,
258-
/** AFS directory */
259-
IMagic = 0x00002000,
260-
/** Reserved for ext3 */
261-
JournalData = 0x00004000,
262-
/** file tail should not be merged */
263-
NoTail = 0x00008000,
264-
/** dirsync behaviour (directories only) */
265-
DirSync = 0x00010000,
266-
/** Top of directory hierarchies*/
267-
TopDir = 0x00020000,
268-
/** Reserved for ext4 */
269-
HugeFile = 0x00040000,
270-
/** Extents */
271-
Extent = 0x00080000,
272-
/** Verity protected inode */
273-
Verity = 0x00100000,
274-
/** Inode used for large EA */
275-
EaInode = 0x00200000,
276-
/** Reserved for ext4 */
277-
EofBlocks = 0x00400000,
278-
/** Do not cow file */
279-
NoCow = 0x00800000,
280-
/** Inode is DAX */
281-
Dax = 0x02000000,
282-
/** Reserved for ext4 */
283-
InlineData = 0x10000000,
284-
/** Create with parents projid */
285-
ProjInherit = 0x20000000,
286-
/** Folder is case insensitive */
287-
CaseFold = 0x40000000,
288-
/** reserved for ext2 lib */
289-
Reserved = 0x80000000,
235+
Immutable = 1 << 3,
236+
/** removed, but still open directory */
237+
Dead = 1 << 4,
238+
/** Inode is not counted to quota */
239+
NoQuota = 1 << 5,
240+
/** Directory modifications are synchronous */
241+
Dirsync = 1 << 6,
242+
/** Do not update file c/mtime */
243+
NoCMtime = 1 << 7,
244+
/** Do not truncate: swapon got its bmaps */
245+
SwapFile = 1 << 8,
246+
/** Inode is fs-internal */
247+
Private = 1 << 9,
248+
/** Inode has an associated IMA struct */
249+
IMA = 1 << 10,
250+
/** Automount/referral quasi-directory */
251+
AutoMount = 1 << 11,
252+
/** no suid or xattr security attributes */
253+
NoSec = 1 << 12,
254+
/** Direct Access, avoiding the page cache */
255+
DAX = 1 << 13,
256+
/** Encrypted file (using fs/crypto/) */
257+
Encrypted = 1 << 14,
258+
/** Casefolded file */
259+
CaseFold = 1 << 15,
260+
/** Verity file (using fs/verity/) */
261+
Verity = 1 << 16,
262+
/** File is in use by the kernel (eg. fs/cachefiles) */
263+
KernelFile = 1 << 17,
290264
}
291265

292266
/** User visible flags */

src/mixins/async.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export function Async<const T extends abstract new (...args: any[]) => FileSyste
6969
public async ready(): Promise<void> {
7070
await super.ready();
7171
await this.queueDone();
72-
if (this._isInitialized || this.attributes.has('no_async')) return;
72+
if (this._isInitialized || this.attributes.has('no_async_preload')) return;
7373

7474
this.checkSync();
7575

@@ -102,7 +102,7 @@ export function Async<const T extends abstract new (...args: any[]) => FileSyste
102102
}
103103

104104
protected checkSync(): asserts this is { _sync: FileSystem } {
105-
if (this.attributes.has('no_async')) {
105+
if (this.attributes.has('no_async_preload')) {
106106
throw withErrno('ENOTSUP', 'Sync preloading has been disabled for this async file system');
107107
}
108108
if (!this._sync) {

src/vfs/file.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export class SyncHandle {
6969
}
7070

7171
private get _isSync(): boolean {
72-
return !!(this.flag & c.O_SYNC || this.inode.flags! & InodeFlags.Sync);
72+
return !!(this.flag & c.O_SYNC || this.inode.flags! & InodeFlags.Sync || this.fs.attributes.has('sync'));
7373
}
7474

7575
public sync(): void {
@@ -112,8 +112,11 @@ export class SyncHandle {
112112
}
113113

114114
public truncate(length: number): void {
115+
if (length < 0) throw UV('EINVAL', 'truncate', this.path);
115116
if (this.closed) throw UV('EBADF', 'truncate', this.path);
116-
if (!(this.flag & c.O_WRONLY || this.flag & c.O_RDWR)) throw UV('EBADF', 'write', this.path);
117+
if (!(this.flag & c.O_WRONLY || this.flag & c.O_RDWR)) throw UV('EBADF', 'truncate', this.path);
118+
if (this.fs.attributes.has('readonly')) throw UV('EROFS', 'truncate', this.path);
119+
if (this.inode.flags! & InodeFlags.Immutable) throw UV('EPERM', 'truncate', this.path);
117120

118121
this.dirty = true;
119122
this.inode.mtimeMs = Date.now();
@@ -135,6 +138,7 @@ export class SyncHandle {
135138
public write(buffer: Uint8Array, offset: number = 0, length: number = buffer.byteLength - offset, position: number = this.position): number {
136139
if (this.closed) throw UV('EBADF', 'write', this.path);
137140
if (!(this.flag & c.O_WRONLY || this.flag & c.O_RDWR)) throw UV('EBADF', 'write', this.path);
141+
if (this.fs.attributes.has('readonly')) throw UV('EROFS', 'write', this.path);
138142
if (this.inode.flags! & InodeFlags.Immutable) throw UV('EPERM', 'write', this.path);
139143

140144
this.dirty = true;
@@ -167,7 +171,7 @@ export class SyncHandle {
167171
if (this.closed) throw UV('EBADF', 'read', this.path);
168172
if (this.flag & c.O_WRONLY) throw UV('EBADF', 'read', this.path);
169173

170-
if (!(this.inode.flags! & InodeFlags.NoAtime)) {
174+
if (!(this.inode.flags! & InodeFlags.NoAtime) && !this.fs.attributes.has('no_atime')) {
171175
this.dirty = true;
172176
this.inode.atimeMs = Date.now();
173177
}
@@ -224,6 +228,7 @@ export class SyncHandle {
224228
public streamWrite(options: StreamOptions): WritableStream {
225229
if (this.closed) throw UV('EBADF', 'write', this.path);
226230
if (this.inode.flags! & InodeFlags.Immutable) throw UV('EPERM', 'write', this.path);
231+
if (this.fs.attributes.has('readonly')) throw UV('EROFS', 'write', this.path);
227232
return this.fs.streamWrite(this.internalPath, options);
228233
}
229234
}

src/vfs/ioctl.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,76 @@ class fsxattr extends BufferView {
8383
}
8484
}
8585

86+
/**
87+
* Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)
88+
* @see `FS_*_FL` in `include/uapi/linux/fs.h` (around L250)
89+
* @experimental
90+
*/
91+
enum FileFlag {
92+
/** Secure deletion */
93+
SecureRm = 0x00000001,
94+
/** Undelete */
95+
Undelete = 0x00000002,
96+
/** Compress file */
97+
Compress = 0x00000004,
98+
/** Synchronous updates */
99+
Sync = 0x00000008,
100+
/** Immutable file */
101+
Immutable = 0x00000010,
102+
/** Writes to file may only append */
103+
Append = 0x00000020,
104+
/** do not dump file */
105+
NoDump = 0x00000040,
106+
/** do not update atime */
107+
NoAtime = 0x00000080,
108+
// Reserved for compression usage...
109+
Dirty = 0x00000100,
110+
/** One or more compressed clusters */
111+
CompressBlk = 0x00000200,
112+
/** Don't compress */
113+
NoCompress = 0x00000400,
114+
// End compression flags --- maybe not all used
115+
/** Encrypted file */
116+
Encrypt = 0x00000800,
117+
/** btree format dir */
118+
Btree = 0x00001000,
119+
/** hash-indexed directory */
120+
// eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
121+
Index = 0x00001000,
122+
/** AFS directory */
123+
IMagic = 0x00002000,
124+
/** Reserved for ext3 */
125+
JournalData = 0x00004000,
126+
/** file tail should not be merged */
127+
NoTail = 0x00008000,
128+
/** dirsync behaviour (directories only) */
129+
DirSync = 0x00010000,
130+
/** Top of directory hierarchies*/
131+
TopDir = 0x00020000,
132+
/** Reserved for ext4 */
133+
HugeFile = 0x00040000,
134+
/** Extents */
135+
Extent = 0x00080000,
136+
/** Verity protected inode */
137+
Verity = 0x00100000,
138+
/** Inode used for large EA */
139+
EaInode = 0x00200000,
140+
/** Reserved for ext4 */
141+
EofBlocks = 0x00400000,
142+
/** Do not cow file */
143+
NoCow = 0x00800000,
144+
/** Inode is DAX */
145+
Dax = 0x02000000,
146+
/** Reserved for ext4 */
147+
InlineData = 0x10000000,
148+
/** Create with parents projid */
149+
ProjInherit = 0x20000000,
150+
/** Folder is case insensitive */
151+
CaseFold = 0x40000000,
152+
/** reserved for ext2 lib */
153+
Reserved = 0x80000000,
154+
}
155+
86156
/**
87157
* `FS_IOC_*` commands for {@link ioctl | `ioctl`}
88158
* @remarks

src/vfs/promises.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ export class FileHandle implements promises.FileHandle {
147147
public async truncate(length: number = 0): Promise<void> {
148148
if (this.closed) throw UV('EBADF', 'truncate', this.path);
149149
if (length < 0) throw UV('EINVAL', 'truncate', this.path);
150+
if (!(this.flag & constants.O_WRONLY || this.flag & constants.O_RDWR)) throw UV('EBADF', 'truncate', this.path);
151+
if (this.fs.attributes.has('readonly')) throw UV('EROFS', 'truncate', this.path);
152+
if (this.inode.flags! & InodeFlags.Immutable) throw UV('EPERM', 'truncate', this.path);
150153

151154
this.dirty = true;
152155
if (!(this.flag & constants.O_WRONLY || this.flag & constants.O_RDWR)) throw UV('EBADF', 'truncate', this.path);
@@ -211,7 +214,7 @@ export class FileHandle implements promises.FileHandle {
211214
if (this.closed) throw UV('EBADF', 'read', this.path);
212215
if (this.flag & constants.O_WRONLY) throw UV('EBADF', 'read', this.path);
213216

214-
if (!(this.inode.flags! & InodeFlags.NoAtime)) {
217+
if (!(this.inode.flags! & InodeFlags.NoAtime) && !this.fs.attributes.has('no_atime')) {
215218
this.dirty = true;
216219
this.inode.atimeMs = Date.now();
217220
}
@@ -356,10 +359,9 @@ export class FileHandle implements promises.FileHandle {
356359
position: number = this.position
357360
): Promise<number> {
358361
if (this.closed) throw UV('EBADF', 'write', this.path);
359-
360362
if (this.inode.flags! & InodeFlags.Immutable) throw UV('EPERM', 'write', this.path);
361-
362363
if (!(this.flag & constants.O_WRONLY || this.flag & constants.O_RDWR)) throw UV('EBADF', 'write', this.path);
364+
if (this.fs.attributes.has('readonly')) throw UV('EROFS', 'write', this.path);
363365

364366
this.dirty = true;
365367
const end = position + length;
@@ -503,6 +505,7 @@ export class FileHandle implements promises.FileHandle {
503505
public createWriteStream(options: promises.CreateWriteStreamOptions = {}): WriteStream {
504506
if (this.closed) throw UV('EBADF', 'createWriteStream', this.path);
505507
if (this.inode.flags! & InodeFlags.Immutable) throw UV('EPERM', 'createWriteStream', this.path);
508+
if (this.fs.attributes.has('readonly')) throw UV('EROFS', 'createWriteStream', this.path);
506509
return new WriteStream(options, this);
507510
}
508511
}

0 commit comments

Comments
 (0)