mirror of https://github.com/red-prig/fpPS4.git
488 lines
14 KiB
Plaintext
488 lines
14 KiB
Plaintext
unit vnode;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
mqueue,
|
|
kern_mtx,
|
|
vselinfo,
|
|
kern_rangelock,
|
|
time;
|
|
|
|
const
|
|
VI_MOUNT =$0020; // Mount in progress
|
|
VI_AGE =$0040; // Insert vnode at head of free list
|
|
VI_DOOMED =$0080; // This vnode is being recycled
|
|
VI_FREE =$0100; // This vnode is on the freelist
|
|
VI_ACTIVE =$0200; // This vnode is on the active list
|
|
VI_DOINGINACT =$0800; // VOP_INACTIVE is in progress
|
|
VI_OWEINACT =$1000; // Need to call inactive
|
|
|
|
VV_ROOT =$0001; // root of its filesystem
|
|
VV_ISTTY =$0002; // vnode represents a tty
|
|
VV_NOSYNC =$0004; // unlinked, stop syncing
|
|
VV_ETERNALDEV =$0008; // device that is never destroyed
|
|
VV_CACHEDLABEL=$0010; // Vnode has valid cached MAC label
|
|
VV_TEXT =$0020; // vnode is a pure text prototype
|
|
VV_COPYONWRITE=$0040; // vnode is doing copy-on-write
|
|
VV_SYSTEM =$0080; // vnode being used by kernel
|
|
VV_PROCDEP =$0100; // vnode is process dependent
|
|
VV_NOKNOTE =$0200; // don't activate knotes on this vnode
|
|
VV_DELETED =$0400; // should be removed
|
|
VV_MD =$0800; // vnode backs the md device
|
|
VV_FORCEINSMQ =$1000; // force the insmntque to succeed
|
|
|
|
//Flags for va_vaflags.
|
|
VA_UTIMES_NULL=$01; // utimes argument was NULL
|
|
VA_EXCLUSIVE =$02; // exclusive create request
|
|
|
|
{
|
|
* Flags for ioflag. (high 16 bits used to ask for read-ahead and
|
|
* help with write clustering)
|
|
* NB: IO_NDELAY and IO_DIRECT are linked to fcntl.h
|
|
}
|
|
IO_UNIT =$0001; { do I/O as atomic unit }
|
|
IO_APPEND =$0002; { append write to end }
|
|
IO_NDELAY =$0004; { FNDELAY flag set in file table }
|
|
IO_NODELOCKED=$0008; { underlying node already locked }
|
|
IO_ASYNC =$0010; { bawrite rather then bdwrite }
|
|
IO_VMIO =$0020; { data already in VMIO space }
|
|
IO_INVAL =$0040; { invalidate after I/O }
|
|
IO_SYNC =$0080; { do I/O synchronously }
|
|
IO_DIRECT =$0100; { attempt to bypass buffer cache }
|
|
IO_EXT =$0400; { operate on external attributes }
|
|
IO_NORMAL =$0800; { operate on regular data }
|
|
IO_NOMACCHECK=$1000; { MAC checks unnecessary }
|
|
IO_BUFLOCKED =$2000; { ffs flag; indir buf is locked }
|
|
|
|
IO_SEQMAX =$7F; { seq heuristic max value }
|
|
IO_SEQSHIFT =16; { seq heuristic in upper 16 bits }
|
|
|
|
//Flags for accmode_t.
|
|
VEXEC =&000000000100; // execute/search permission
|
|
VWRITE =&000000000200; // write permission
|
|
VREAD =&000000000400; // read permission
|
|
VADMIN =&000000010000; // being the file owner
|
|
VAPPEND=&000000040000; // permission to write/append
|
|
|
|
VEXPLICIT_DENY =&000000100000;
|
|
VDELETE_CHILD =&000001000000;
|
|
VREAD_ATTRIBUTES =&000002000000; // permission to stat(2)
|
|
VWRITE_ATTRIBUTES =&000004000000; // change {m,c,a}time
|
|
VDELETE =&000010000000;
|
|
VREAD_ACL =&000020000000; // read ACL and file mode
|
|
VWRITE_ACL =&000040000000; // change ACL and/or file mode
|
|
VWRITE_OWNER =&000100000000; // change file owner
|
|
VSYNCHRONIZE =&000200000000;
|
|
|
|
VADMIN_PERMS =VADMIN or VWRITE_ATTRIBUTES or VWRITE_ACL or VWRITE_OWNER;
|
|
|
|
//Permissions that were traditionally granted to everyone.
|
|
VSTAT_PERMS =VREAD_ATTRIBUTES or VREAD_ACL;
|
|
|
|
//Permissions that allow to change the state of the file in any way.
|
|
VMODIFY_PERMS =VWRITE or VAPPEND or VADMIN_PERMS or VDELETE_CHILD or VDELETE;
|
|
|
|
// vn_open_flags
|
|
VN_OPEN_NOAUDIT=$00000001;
|
|
|
|
//Flags to various vnode functions.
|
|
SKIPSYSTEM =$0001; // vflush: skip vnodes marked VSYSTEM
|
|
FORCECLOSE =$0002; // vflush: force file closure
|
|
WRITECLOSE =$0004; // vflush: only close writable files
|
|
EARLYFLUSH =$0008; // vflush: early call for ffs_flushfiles
|
|
V_SAVE =$0001; // vinvalbuf: sync file first
|
|
V_ALT =$0002; // vinvalbuf: invalidate only alternate bufs
|
|
V_NORMAL =$0004; // vinvalbuf: invalidate only regular bufs
|
|
V_CLEANONLY=$0008; // vinvalbuf: invalidate only clean bufs
|
|
REVOKEALL =$0001; // vop_revoke: revoke all aliases
|
|
V_WAIT =$0001; // vn_start_write: sleep for suspend
|
|
V_NOWAIT =$0002; // vn_start_write: don't sleep for suspend
|
|
V_XSLEEP =$0004; // vn_start_write: just return after sleep
|
|
|
|
VR_START_WRITE=$0001; // vfs_write_resume: start write atomically
|
|
VR_NO_SUSPCLR =$0002; // vfs_write_resume: do not clear suspension
|
|
|
|
VNOVAL=-1;
|
|
|
|
//Flags for vdesc_flags:
|
|
VDESC_MAX_VPS=16;
|
|
// Low order 16 flag bits are reserved for willrele flags for vp arguments.
|
|
VDESC_VP0_WILLRELE=$0001;
|
|
VDESC_VP1_WILLRELE=$0002;
|
|
VDESC_VP2_WILLRELE=$0004;
|
|
VDESC_VP3_WILLRELE=$0008;
|
|
VDESC_NOMAP_VPP =$0100;
|
|
VDESC_VPP_WILLRELE=$0200;
|
|
|
|
{
|
|
* VDESC_NO_OFFSET is used to identify the end of the offset list
|
|
* and in places where no such field exists.
|
|
}
|
|
VDESC_NO_OFFSET=-1;
|
|
|
|
type
|
|
p_accmode_t=^accmode_t;
|
|
accmode_t=Integer;
|
|
|
|
{ This structure describes the vnode operation taking place. }
|
|
p_vnodeop_desc=^t_vnodeop_desc;
|
|
t_vnodeop_desc=record
|
|
vdesc_name :PChar; { a readable name for debugging }
|
|
vdesc_call :Pointer; { Function to call }
|
|
|
|
{
|
|
* These ops are used by bypass routines to map and locate arguments.
|
|
* Creds and procs are not needed in bypass routines, but sometimes
|
|
* they are useful to (for example) transport layers.
|
|
* Nameidata is useful because it has a cred in it.
|
|
}
|
|
vdesc_vp_offsets :PByte; { list ended by VDESC_NO_OFFSET }
|
|
vdesc_flags :Integer; { VDESC_* flags }
|
|
vdesc_vpp_offset :Integer; { return vpp location }
|
|
end;
|
|
|
|
p_vop_generic_args=^t_vop_generic_args;
|
|
t_vop_generic_args=record
|
|
a_desc:p_vnodeop_desc;
|
|
//other random data follows, presumably
|
|
end;
|
|
|
|
vop_bypass_t=function(ap:Pointer):Integer;
|
|
|
|
pp_vnode=^p_vnode;
|
|
p_vnode=^t_vnode;
|
|
|
|
p_vop_vector=^vop_vector;
|
|
vop_vector=packed record
|
|
vop_default :p_vop_vector;
|
|
vop_bypass :Pointer;
|
|
|
|
vop_islocked :Pointer;
|
|
vop_lookup :Pointer;
|
|
vop_create :Pointer;
|
|
vop_whiteout :Pointer;
|
|
vop_mknod :Pointer;
|
|
vop_open :Pointer;
|
|
vop_close :Pointer;
|
|
vop_access :Pointer;
|
|
vop_accessx :Pointer;
|
|
vop_getattr :Pointer;
|
|
vop_setattr :Pointer;
|
|
vop_markatime :Pointer;
|
|
vop_read :Pointer;
|
|
vop_write :Pointer;
|
|
vop_ioctl :Pointer;
|
|
vop_poll :Pointer;
|
|
vop_kqfilter :Pointer;
|
|
vop_revoke :Pointer;
|
|
vop_fsync :Pointer;
|
|
vop_remove :Pointer;
|
|
vop_link :Pointer;
|
|
vop_rename :Pointer;
|
|
vop_mkdir :Pointer;
|
|
vop_rmdir :Pointer;
|
|
vop_symlink :Pointer;
|
|
vop_readdir :Pointer;
|
|
vop_readlink :Pointer;
|
|
vop_inactive :Pointer;
|
|
vop_reclaim :Pointer;
|
|
vop_lock1 :Pointer;
|
|
vop_unlock :Pointer;
|
|
vop_bmap :Pointer;
|
|
vop_strategy :Pointer;
|
|
vop_getwritemount :Pointer;
|
|
vop_print :Pointer;
|
|
vop_pathconf :Pointer;
|
|
vop_advlock :Pointer;
|
|
vop_advlockasync :Pointer;
|
|
vop_advlockpurge :Pointer;
|
|
vop_reallocblks :Pointer;
|
|
vop_getpages :Pointer;
|
|
vop_putpages :Pointer;
|
|
vop_vptofh :Pointer;
|
|
vop_vptocnp :Pointer;
|
|
vop_allocate :Pointer;
|
|
vop_unp_bind :Pointer;
|
|
vop_unp_connect :Pointer;
|
|
vop_unp_detach :Pointer;
|
|
end;
|
|
|
|
vtype=(VNON,VREG,VDIR,VBLK,VCHR,VLNK,VSOCK,VFIFO,VBAD,VMARKER);
|
|
|
|
p_vpollinfo=^vpollinfo;
|
|
vpollinfo=packed record
|
|
vpi_lock :mtx; // lock to protect below
|
|
vpi_selinfo:t_selinfo; // identity of poller(s)
|
|
vpi_events :Word; // what they are looking for
|
|
vpi_revents:Word; // what has happened
|
|
end;
|
|
|
|
t_vnode=packed object
|
|
|
|
//Fields which define the identity of the vnode
|
|
v_type:vtype; // u vnode type
|
|
v_tag :PChar; // u type of underlying data
|
|
v_op :p_vop_vector; // u vnode operations vector
|
|
v_data:Pointer; // u private data for fs
|
|
|
|
//Filesystem instance stuff
|
|
v_mount:Pointer; //mount
|
|
v_nmntvnodes:TAILQ_ENTRY; //vnode
|
|
|
|
v_un:Pointer; //Type specific fields, only one applies to any given vnode
|
|
|
|
v_hash:DWORD;
|
|
|
|
v_holdcnt :Integer; //i prevents recycling.
|
|
v_usecount:Integer; //i ref count of users
|
|
v_writecount:Integer; //v ref count of writers
|
|
|
|
v_lock:mtx; // u (if fs don't have one)
|
|
v_interlock:mtx; // lock for "i" things
|
|
v_vnlock:p_mtx; //u pointer to vnode lock
|
|
|
|
v_iflag:QWORD; //i vnode flags (see below)
|
|
v_vflag:QWORD; //v vnode flags
|
|
|
|
v_object:Pointer;
|
|
|
|
v_actfreelist:TAILQ_ENTRY;
|
|
|
|
v_pollinfo:p_vpollinfo; // i Poll events, p for *v_pi
|
|
|
|
v_rl:rangelock; //Byte-range lock
|
|
|
|
property v_mountedhere:Pointer read v_un{.vu_mount } write v_un; //mount
|
|
property v_socket :Pointer read v_un{.vu_socket } write v_un; //socket
|
|
property v_rdev :Pointer read v_un{.vu_cdev } write v_un; //cdev
|
|
property v_fifoinfo :Pointer read v_un{.vu_fifoinfo} write v_un; //fifoinfo
|
|
end;
|
|
|
|
p_vattr=^t_vattr;
|
|
t_vattr=record
|
|
va_type :vtype; // vnode type (for create)
|
|
va_mode :SmallInt; // files access mode and type
|
|
va_nlink :SmallInt; // number of references to file
|
|
va_uid :Integer; // owner user id
|
|
va_gid :Integer; // owner group id
|
|
va_fsid :Integer; // filesystem id
|
|
va_fileid :Int64; // file id
|
|
va_size :Int64; // file size in bytes
|
|
va_blocksize:Int64; // blocksize preferred for i/o
|
|
va_atime :timespec; // time of last access
|
|
va_mtime :timespec; // time of last modification
|
|
va_ctime :timespec; // time file changed
|
|
va_birthtime:timespec; // time file created
|
|
va_gen :Int64; // generation number of file
|
|
va_flags :Int64; // flags defined for file
|
|
va_rdev :Integer; // device the special file represents
|
|
va_bytes :Int64; // bytes of disk space held by file
|
|
va_filerev :Int64; // file modification number
|
|
va_vaflags :Integer; // operations flags, see below
|
|
va_spare :Int64; // remain quad aligned
|
|
end;
|
|
|
|
const
|
|
iftovt_tab:array[0..15] of vtype=(
|
|
VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON,
|
|
VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VBAD
|
|
);
|
|
|
|
function VOPARG_OFFSETTO(s_offset:Integer;struct_p:Pointer):Pointer;
|
|
function VCALL(c:Pointer):Integer;
|
|
|
|
function VN_KNLIST_EMPTY(vp:p_vnode):Boolean;
|
|
procedure VN_KNOTE(vp:p_vnode;a:Integer;b:QWORD);
|
|
procedure VN_KNOTE_LOCKED(vp:p_vnode;b:QWORD);
|
|
procedure VN_KNOTE_UNLOCKED(vp:p_vnode;b:QWORD);
|
|
|
|
procedure VI_LOCK(vp:p_vnode);
|
|
function VI_TRYLOCK(vp:p_vnode):Boolean;
|
|
procedure VI_UNLOCK(vp:p_vnode);
|
|
function VI_MTX(vp:p_vnode):p_mtx;
|
|
|
|
function IGNORE_LOCK(vp:p_vnode):Boolean;
|
|
|
|
procedure VOP_ADD_WRITECOUNT(vp:p_vnode;i:Integer);
|
|
|
|
procedure vn_rangelock_unlock(vp:p_vnode;cookie:Pointer);
|
|
procedure vn_rangelock_unlock_range(vp:p_vnode;cookie:Pointer;start,__end:Int64);
|
|
function vn_rangelock_rlock(vp:p_vnode;start,__end:Int64):Pointer;
|
|
function vn_rangelock_wlock(vp:p_vnode;start,__end:Int64):Pointer;
|
|
|
|
function vn_canvmio(vp:p_vnode):Boolean;
|
|
|
|
var
|
|
rootvnode:p_vnode=nil;
|
|
|
|
implementation
|
|
|
|
uses
|
|
sys_event;
|
|
|
|
//
|
|
|
|
function VFS_PROLOGUE(mp:Pointer):Boolean; external;
|
|
procedure VFS_EPILOGUE(_enable_stops:Boolean); external;
|
|
|
|
//
|
|
|
|
function VOPARG_OFFSETTO(s_offset:Integer;struct_p:Pointer):Pointer;
|
|
begin
|
|
Result:=struct_p+s_offset;
|
|
end;
|
|
|
|
function get_vp_cb(vp:p_vnode;offset:Pointer):Pointer; inline;
|
|
var
|
|
v:p_vop_vector;
|
|
p:Pointer;
|
|
begin
|
|
Result:=nil;
|
|
if (vp=nil) then Exit;
|
|
v:=vp^.v_op;
|
|
while (v<>nil) do
|
|
begin
|
|
p:=PPointer(Pointer(v)+ptrint(offset))^;
|
|
if (p<>nil) then
|
|
begin
|
|
Exit(p);
|
|
end;
|
|
p:=v^.vop_bypass;
|
|
if (p<>nil) then
|
|
begin
|
|
Exit(p);
|
|
end;
|
|
v:=v^.vop_default;
|
|
end;
|
|
end;
|
|
|
|
function vcall_panic:Integer; inline;
|
|
begin
|
|
Assert(false,'filesystem goof: vcall_panic');
|
|
Exit(2);
|
|
end;
|
|
|
|
type
|
|
p_vop_vcall_args=^t_vop_vcall_args;
|
|
t_vop_vcall_args=record
|
|
a_desc:p_vnodeop_desc;
|
|
a_vp :p_vnode;
|
|
end;
|
|
|
|
function VCALL(c:Pointer):Integer;
|
|
var
|
|
ap:p_vop_vcall_args;
|
|
s:Boolean;
|
|
begin
|
|
if (c=nil) then Exit(vcall_panic);
|
|
ap:=c;
|
|
if (ap^.a_desc=nil) then Exit(vcall_panic);
|
|
if (ap^.a_vp=nil) then Exit(vcall_panic);
|
|
if (ap^.a_desc^.vdesc_call=nil) then Exit(vcall_panic);
|
|
c:=get_vp_cb(ap^.a_vp,ap^.a_desc^.vdesc_call);
|
|
Assert(c<>nil,'VCALL');
|
|
s:=VFS_PROLOGUE(ap^.a_vp^.v_mount);
|
|
Result:=vop_bypass_t(c)(ap);
|
|
VFS_EPILOGUE(s);
|
|
end;
|
|
|
|
// We don't need to lock the knlist
|
|
function VN_KNLIST_EMPTY(vp:p_vnode):Boolean;
|
|
begin
|
|
if (vp^.v_pollinfo=nil) then Exit(True);
|
|
Result:=M_KNLIST_EMPTY(@vp^.v_pollinfo^.vpi_selinfo.si_note)
|
|
end;
|
|
|
|
procedure VN_KNOTE(vp:p_vnode;a:Integer;b:QWORD);
|
|
begin
|
|
if (not VN_KNLIST_EMPTY(vp)) then
|
|
begin
|
|
KNOTE(@vp^.v_pollinfo^.vpi_selinfo.si_note,b,a or KNF_NOKQLOCK);
|
|
end;
|
|
end;
|
|
|
|
procedure VN_KNOTE_LOCKED(vp:p_vnode;b:QWORD);
|
|
begin
|
|
VN_KNOTE(vp, b, KNF_LISTLOCKED);
|
|
end;
|
|
|
|
procedure VN_KNOTE_UNLOCKED(vp:p_vnode;b:QWORD);
|
|
begin
|
|
VN_KNOTE(vp, b, 0);
|
|
end;
|
|
|
|
procedure VI_LOCK(vp:p_vnode);
|
|
begin
|
|
mtx_lock(vp^.v_interlock);
|
|
end;
|
|
|
|
function VI_TRYLOCK(vp:p_vnode):Boolean;
|
|
begin
|
|
Result:=mtx_trylock(vp^.v_interlock);
|
|
end;
|
|
|
|
procedure VI_UNLOCK(vp:p_vnode);
|
|
begin
|
|
mtx_unlock(vp^.v_interlock);
|
|
end;
|
|
|
|
function VI_MTX(vp:p_vnode):p_mtx;
|
|
begin
|
|
Result:=@vp^.v_interlock;
|
|
end;
|
|
|
|
function IGNORE_LOCK(vp:p_vnode):Boolean;
|
|
begin
|
|
if (vp=nil) then Exit(True);
|
|
Result:=(vp^.v_type=VCHR) or (vp^.v_type=VBAD);
|
|
end;
|
|
|
|
procedure VOP_ADD_WRITECOUNT(vp:p_vnode;i:Integer);
|
|
begin
|
|
System.InterlockedExchangeAdd(vp^.v_writecount,i);
|
|
end;
|
|
|
|
procedure vn_rangelock_unlock(vp:p_vnode;cookie:Pointer);
|
|
begin
|
|
rangelock_unlock(@vp^.v_rl, (cookie), VI_MTX(vp))
|
|
end;
|
|
|
|
procedure vn_rangelock_unlock_range(vp:p_vnode;cookie:Pointer;start,__end:Int64);
|
|
begin
|
|
rangelock_unlock_range(@vp^.v_rl, (cookie), start, __end, VI_MTX(vp))
|
|
end;
|
|
|
|
function vn_rangelock_rlock(vp:p_vnode;start,__end:Int64):Pointer;
|
|
begin
|
|
Result:=rangelock_rlock(@vp^.v_rl, start, __end, VI_MTX(vp))
|
|
end;
|
|
|
|
function vn_rangelock_wlock(vp:p_vnode;start,__end:Int64):Pointer;
|
|
begin
|
|
Result:=rangelock_wlock(@vp^.v_rl, start, __end, VI_MTX(vp))
|
|
end;
|
|
|
|
const
|
|
vmiodirenable=False;
|
|
|
|
function vn_canvmio(vp:p_vnode):Boolean;
|
|
begin
|
|
if (vp<>nil) then
|
|
begin
|
|
if (vp^.v_type=VREG) or
|
|
(vmiodirenable and (vp^.v_type=VDIR)) then
|
|
begin
|
|
Exit(True);
|
|
end;
|
|
end;
|
|
Result:=False;
|
|
end;
|
|
|
|
|
|
end.
|
|
|