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.