Files
Asuro/src/driver/storage/driver.storage.fdtable.pas
T
2026-03-15 23:58:52 +00:00

229 lines
6.9 KiB
ObjectPascal

// Copyright 2021 Aaron Hance
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
{
Driver->Storage->FDTable - Per-process file descriptor table.
Each process owns a TFDTable (allocated on process create, freed on reap).
File handles are 1-based indices into the table (0 = invalid).
The VFS layer calls fd_alloc / fd_get / fd_close instead of touching
a global OpenFiles array.
@author(Aaron Hance <[email protected]>)
}
unit driver.storage.fdtable;
interface
uses
memory.heap,
driver.storage.types,
core.util, arch.x86.util;
const
MAX_FDS = 64;
type
PFileDescriptor = ^TFileDescriptor;
TFileDescriptor = record
InUse : boolean;
AsyncRefs : uint32; { in-flight async ops using this descriptor }
Volume : PStorage_Volume;
Directory : pchar; { directory path within the volume }
FileName : pchar; { filename within that directory }
VFSDir : pchar; { full VFS parent directory path, e.g. '/sys' }
OpenMode : uint8; { TOpenMode ordinal — avoids VFS type dependency }
DataSize : uint32; { cached file size in bytes (from fileSizeCallback) }
DeviceOps : pointer; { PVFSDeviceOps — non-nil for device FDs }
DeviceData : pointer; { opaque data passed to device callbacks }
FSPrivate : pointer; { opaque FS-level per-file metadata (from openFileCallback) }
FSCloseHook : pointer; { procedure(pointer) — frees FSPrivate on close }
end;
PFDTable = ^TFDTable;
TFDTable = record
Entries : array[0..MAX_FDS-1] of TFileDescriptor;
end;
{ Allocate and zero-initialise a new FD table on the heap.
Called once per process at creation time.
Returns nil on out-of-memory. }
function fd_table_new : PFDTable;
{ Free all open descriptors in the table, then free the table itself.
Called when a process is reaped. Safe to pass nil. }
procedure fd_table_free(table : PFDTable);
{ Find the first free slot in the table.
Returns 1-based handle (1..MAX_FDS) on success, 0 if table is full. }
function fd_alloc(table : PFDTable) : uint32;
{ Get a pointer to the descriptor for a 1-based handle.
Returns nil if handle is out of range or the slot is not in use. }
function fd_get(table : PFDTable; handle : uint32) : PFileDescriptor;
function fd_begin_async(entry : PFileDescriptor) : boolean;
procedure fd_end_async(entry : PFileDescriptor);
function fd_has_async(entry : PFileDescriptor) : boolean;
{ Close a single file descriptor: free its buffers and core.strings,
mark the slot as not-in-use.
Returns true if the handle was valid and open, false otherwise. }
function fd_close(table : PFDTable; handle : uint32) : boolean;
{ Close ALL open file descriptors in the table.
Used during process cleanup. }
procedure fd_close_all(table : PFDTable);
{ Close all file descriptors in the table that reference a specific volume.
Called when a volume is removed to prevent dangling pointers. }
procedure fd_close_for_volume(table : PFDTable; vol : PStorage_Volume);
implementation
function fd_table_new : PFDTable;
var
tbl : PFDTable;
begin
tbl := PFDTable(kalloc(SizeOf(TFDTable)));
if tbl <> nil then
memset(uint32(tbl), 0, SizeOf(TFDTable));
fd_table_new := tbl;
end;
type
TFSPrivateClose = procedure(data : pointer);
procedure fd_free_entry(entry : PFileDescriptor);
begin
if entry = nil then exit;
if not entry^.InUse then exit;
{ Free FS-level per-file metadata first }
if (entry^.FSPrivate <> nil) and (entry^.FSCloseHook <> nil) then
TFSPrivateClose(entry^.FSCloseHook)(entry^.FSPrivate);
entry^.FSPrivate := nil;
entry^.FSCloseHook := nil;
if entry^.Directory <> nil then begin
kfree(void(entry^.Directory));
entry^.Directory := nil;
end;
if entry^.FileName <> nil then begin
kfree(void(entry^.FileName));
entry^.FileName := nil;
end;
if entry^.VFSDir <> nil then begin
kfree(void(entry^.VFSDir));
entry^.VFSDir := nil;
end;
entry^.InUse := false;
entry^.AsyncRefs := 0;
entry^.DataSize := 0;
entry^.DeviceOps := nil;
entry^.DeviceData := nil;
end;
procedure fd_table_free(table : PFDTable);
begin
if table = nil then exit;
fd_close_all(table);
kfree(puint32(table));
end;
function fd_alloc(table : PFDTable) : uint32;
var
i : uint32;
begin
fd_alloc := 0;
if table = nil then exit;
for i := 0 to MAX_FDS - 1 do begin
if not table^.Entries[i].InUse then begin
fd_alloc := i + 1; { 1-based }
exit;
end;
end;
end;
function fd_get(table : PFDTable; handle : uint32) : PFileDescriptor;
begin
fd_get := nil;
if table = nil then exit;
if (handle = 0) or (handle > MAX_FDS) then exit;
if not table^.Entries[handle - 1].InUse then exit;
fd_get := @table^.Entries[handle - 1];
end;
function fd_begin_async(entry : PFileDescriptor) : boolean;
begin
fd_begin_async := false;
if entry = nil then exit;
asm pushf; cli end;
if entry^.InUse then begin
entry^.AsyncRefs := entry^.AsyncRefs + 1;
fd_begin_async := true;
end;
asm popf end;
end;
procedure fd_end_async(entry : PFileDescriptor);
begin
if entry = nil then exit;
asm pushf; cli end;
if entry^.AsyncRefs > 0 then
entry^.AsyncRefs := entry^.AsyncRefs - 1;
asm popf end;
end;
function fd_has_async(entry : PFileDescriptor) : boolean;
begin
fd_has_async := false;
if entry = nil then exit;
fd_has_async := entry^.AsyncRefs > 0;
end;
function fd_close(table : PFDTable; handle : uint32) : boolean;
begin
fd_close := false;
if table = nil then exit;
if (handle = 0) or (handle > MAX_FDS) then exit;
if not table^.Entries[handle - 1].InUse then exit;
fd_free_entry(@table^.Entries[handle - 1]);
fd_close := true;
end;
procedure fd_close_all(table : PFDTable);
var
i : uint32;
begin
if table = nil then exit;
for i := 0 to MAX_FDS - 1 do begin
if table^.Entries[i].InUse then
fd_free_entry(@table^.Entries[i]);
end;
end;
procedure fd_close_for_volume(table : PFDTable; vol : PStorage_Volume);
var
i : uint32;
begin
if table = nil then exit;
if vol = nil then exit;
for i := 0 to MAX_FDS - 1 do begin
if table^.Entries[i].InUse and (table^.Entries[i].Volume = vol) then
fd_free_entry(@table^.Entries[i]);
end;
end;
end.