FPPS4/sys/vfs/vnode.pas

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.