229 lines
6.9 KiB
ObjectPascal
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.
|