unit vmount;

{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}

interface

uses
 mqueue,
 kern_param,
 time,
 sys_event,
 kern_mtx,
 //kern_synch,
 signal,
 vnode;

const
 STATFS_VERSION=$20030518; // current version number

 //User specifiable flags, stored in mnt_flag.
 MNT_RDONLY     =$0000000000000001; // read only filesystem
 MNT_SYNCHRONOUS=$0000000000000002; // fs written synchronously
 MNT_NOEXEC     =$0000000000000004; // can't exec from filesystem
 MNT_NOSUID     =$0000000000000008; // don't honor setuid fs bits
 MNT_NFS4ACLS   =$0000000000000010; // enable NFS version 4 ACLs
 MNT_UNION      =$0000000000000020; // union with underlying fs
 MNT_ASYNC      =$0000000000000040; // fs written asynchronously
 MNT_SUIDDIR    =$0000000000100000; // special SUID dir handling
 MNT_SOFTDEP    =$0000000000200000; // using soft updates
 MNT_NOSYMFOLLOW=$0000000000400000; // do not follow symlinks
 MNT_GJOURNAL   =$0000000002000000; // GEOM journal support enabled
 MNT_MULTILABEL =$0000000004000000; // MAC support for objects
 MNT_ACLS       =$0000000008000000; // ACL support enabled
 MNT_NOATIME    =$0000000010000000; // dont update file access time
 MNT_NOCLUSTERR =$0000000040000000; // disable cluster read
 MNT_NOCLUSTERW =$0000000080000000; // disable cluster write
 MNT_SUJ        =$0000000100000000; // using journaled soft updates

 //NFS export related mount flags.
 MNT_EXRDONLY   =$0000000000000080; // exported read only
 MNT_EXPORTED   =$0000000000000100; // filesystem is exported
 MNT_DEFEXPORTED=$0000000000000200; // exported to the world
 MNT_EXPORTANON =$0000000000000400; // anon uid mapping for all
 MNT_EXKERB     =$0000000000000800; // exported with Kerberos
 MNT_EXPUBLIC   =$0000000020000000; // public export (WebNFS)

{
  * Flags set by internal operations,
  * but visible to the user.
  * XXX some of these are not quite right.. (I've never seen the root flag set)
}
 MNT_LOCAL =$0000000000001000; // filesystem is stored locally
 MNT_QUOTA =$0000000000002000; // quotas are enabled on fs
 MNT_ROOTFS=$0000000000004000; // identifies the root fs
 MNT_USER  =$0000000000008000; // mounted by a user
 MNT_IGNORE=$0000000000800000; // do not show entry in df

{
  * External filesystem command modifier flags.
  * Unmount can use the MNT_FORCE flag.
  * XXX: These are not STATES and really should be somewhere else.
  * XXX: MNT_BYFSID collides with MNT_ACLS, but because MNT_ACLS is only used for
  *      mount(2) and MNT_BYFSID is only used for unmount(2) it's harmless.
}
 MNT_UPDATE   =$0000000000010000; // not real mount, just update
 MNT_DELEXPORT=$0000000000020000; // delete export host lists
 MNT_RELOAD   =$0000000000040000; // reload filesystem data
 MNT_FORCE    =$0000000000080000; // force unmount or readonly
 MNT_SNAPSHOT =$0000000001000000; // snapshot the filesystem
 MNT_BYFSID   =$0000000008000000; // specify filesystem by ID.

 MNT_VISFLAGMASK=(MNT_RDONLY or MNT_SYNCHRONOUS or MNT_NOEXEC or
    MNT_NOSUID or MNT_UNION or MNT_SUJ or
    MNT_ASYNC or MNT_EXRDONLY or MNT_EXPORTED or
    MNT_DEFEXPORTED or MNT_EXPORTANON or MNT_EXKERB or
    MNT_LOCAL or MNT_USER or MNT_QUOTA or
    MNT_ROOTFS or MNT_NOATIME or MNT_NOCLUSTERR or
    MNT_NOCLUSTERW or MNT_SUIDDIR or MNT_SOFTDEP or
    MNT_IGNORE or MNT_EXPUBLIC or MNT_NOSYMFOLLOW or
    MNT_GJOURNAL or MNT_MULTILABEL or MNT_ACLS or
    MNT_NFS4ACLS);

 MNT_UPDATEMASK=(MNT_NOSUID or MNT_NOEXEC or
    MNT_SYNCHRONOUS or MNT_UNION or MNT_ASYNC or
    MNT_NOATIME or
    MNT_NOSYMFOLLOW or MNT_IGNORE or
    MNT_NOCLUSTERR or MNT_NOCLUSTERW or MNT_SUIDDIR or
    MNT_ACLS or MNT_USER or MNT_NFS4ACLS);

{
  * Internal filesystem control flags stored in mnt_kern_flag.
  *
  * MNTK_UNMOUNT locks the mount entry so that name lookup cannot proceed
  * past the mount point.  This keeps the subtree stable during mounts
  * and unmounts.
  *
  * MNTK_UNMOUNTF permits filesystems to detect a forced unmount while
  * dounmount() is still waiting to lock the mountpoint. This allows
  * the filesystem to cancel operations that might otherwise deadlock
  * with the unmount attempt (used by NFS).
  *
  * MNTK_NOINSMNTQ is strict subset of MNTK_UNMOUNT. They are separated
  * to allow for failed unmount attempt to restore the syncer vnode for
  * the mount.
}
 MNTK_UNMOUNTF          =$00000001; // forced unmount in progress
 MNTK_ASYNC             =$00000002; // filtered async flag
 MNTK_SOFTDEP           =$00000004; // async disabled by softdep
 MNTK_NOINSMNTQ         =$00000008; // insmntque is not allowed
 MNTK_DRAINING          =$00000010; // lock draining is happening
 MNTK_REFEXPIRE         =$00000020; // refcount expiring is happening
 MNTK_EXTENDED_SHARED   =$00000040; // Allow shared locking for more ops
 MNTK_SHARED_WRITES     =$00000080; // Allow shared locking for writes
 MNTK_NO_IOPF           =$00000100; { Disallow page faults during reads
                                      and writes. Filesystem shall properly
                                      handle i/o state on EFAULT.}
 MNTK_VGONE_UPPER       =$00000200;
 MNTK_VGONE_WAITER      =$00000400;
 MNTK_LOOKUP_EXCL_DOTDOT=$00000800;
 MNTK_MARKER            =$00001000;
 MNTK_UNMAPPED_BUFS     =$00002000;
 MNTK_NOASYNC           =$00800000; // disable async
 MNTK_UNMOUNT           =$01000000; // unmount in progress
 MNTK_MWAIT             =$02000000; // waiting for unmount to finish
 MNTK_SUSPEND           =$08000000; // request write suspension
 MNTK_SUSPEND2          =$04000000; // block secondary writes
 MNTK_SUSPENDED         =$10000000; // write operations are suspended
 MNTK_MPSAFE            =$20000000; // Filesystem is MPSAFE.
 MNTK_LOOKUP_SHARED     =$40000000; // FS supports shared lock lookups
 MNTK_NOKNOTE           =$80000000; // Don't send KNOTEs from VOP hooks

{
  * Sysctl CTL_VFS definitions.
  *
  * Second level identifier specifies which filesystem. Second level
  * identifier VFS_VFSCONF returns information about all filesystems.
  * Second level identifier VFS_GENERIC is non-terminal.
}
 VFS_VFSCONF=0; // get configured filesystems
 VFS_GENERIC=0; // generic filesystem information

{
  * Third level identifiers for VFS_GENERIC are given below; third
  * level identifiers for specific filesystems are given in their
  * mount specific header files.
}
 VFS_MAXTYPENUM=1; // int: highest defined filesystem type
 VFS_CONF      =2; // struct: vfsconf for filesystem given as next argument

{
  * Flags for various system call interfaces.
  *
  * waitfor flags to vfs_sync() and getfsstat()
}
 MNT_WAIT   =1; // synchronously wait for I/O to complete
 MNT_NOWAIT =2; // start all I/O, but do not wait for it
 MNT_LAZY   =3; // push data not written by filesystem syncer
 MNT_SUSPEND=4; // Suspend file system after sync

{
 * vfs_busy specific flags and mask.
}
 MBF_NOWAIT    =$01;
 MBF_MNTLSTLOCK=$02;
 MBF_MASK      =(MBF_NOWAIT or MBF_MNTLSTLOCK);

 VFCF_STATIC    =$00010000; // statically compiled into kernel
 VFCF_NETWORK   =$00020000; // may get data over the network
 VFCF_READONLY  =$00040000; // writes are not implemented
 VFCF_SYNTHETIC =$00080000; // data does not represent real files
 VFCF_LOOPBACK  =$00100000; // aliases some other mounted FS
 VFCF_UNICODE   =$00200000; // stores file names as Unicode
 VFCF_JAIL      =$00400000; // can be mounted from within a jail
 VFCF_DELEGADMIN=$00800000; // supports delegated administration
 VFCF_SBDRY     =$01000000; // defer stop requests

 { vfsquery flags }
 VQ_NOTRESP    =$0001; { server down }
 VQ_NEEDAUTH   =$0002; { server bad auth }
 VQ_LOWDISK    =$0004; { we're low on space }
 VQ_MOUNT      =$0008; { new filesystem arrived }
 VQ_UNMOUNT    =$0010; { filesystem has left }
 VQ_DEAD       =$0020; { filesystem is dead, needs force unmount }
 VQ_ASSIST     =$0040; { filesystem needs assistance from external program }
 VQ_NOTRESPLOCK=$0080; { server lockd down }

type
 p_fsid=^fsid_t;
 fsid_t=packed record  // filesystem id type
  val:array[0..1] of Integer;
 end;

{
 * File identifier.
 * These are unique per filesystem on a single machine.
}
 p_fid=^fid;
 fid=packed record
  fid_len  :Word;  // length of data in bytes
  fid_data0:Word;  // force longword alignment
  fid_data :array[0..MAXFIDSZ-1] of Byte; // data (variable length)
 end;

 vnodelst=TAILQ_HEAD; //vnode

 pp_vfsoptlist=^p_vfsoptlist;
 p_vfsoptlist=^vfsoptlist;
 vfsoptlist=TAILQ_HEAD; //vfsopt

 // Mount options list
 p_vfsopt=^vfsopt;
 vfsopt=packed record
  link :TAILQ_ENTRY; //vfsopt
  name :PChar;
  value:Pointer;
  len  :Integer;
  pos  :Integer;
  seen :Integer;
 end;

{
 * Operations supported on mounted filesystem.
}

 PPInteger=^PInteger;

 pp_mount=^p_mount;
 p_mount=^t_mount;

 pp_statfs=^p_statfs;
 p_statfs=^t_statfs;
 p_vfsconf=^vfsconf;

 vfs_cmount_t        =function (ma,data:Pointer;flags:QWORD):Integer;
 vfs_unmount_t       =function (mp:p_mount;mntflags:Integer):Integer;
 vfs_root_t          =function (mp:p_mount;flags:Integer;vpp:pp_vnode):Integer;
 vfs_quotactl_t      =function (mp:p_mount;cmds,uid:Integer;arg:Pointer):Integer;
 vfs_statfs_t        =function (mp:p_mount;sbp:p_statfs):Integer;
 vfs_sync_t          =function (mp:p_mount;waitfor:Integer):Integer;
 vfs_vget_t          =function (mp:p_mount;ino:DWORD;flags:Integer;vpp:pp_vnode):Integer;
 vfs_fhtovp_t        =function (mp:p_mount;fhp:p_fid;flags:Integer;vpp:pp_vnode):Integer;
 vfs_checkexp_t      =function (mp:p_mount;nam:Pointer;extflagsp,numsecflavors:Pinteger;secflavors:PPInteger):Integer;
 vfs_init_t          =function (cf:p_vfsconf):Integer;
 vfs_uninit_t        =function (cf:p_vfsconf):Integer;
 vfs_extattrctl_t    =function (mp:p_mount;cmd:Integer;filename_vp:p_vnode;attrnamespace:Integer;attrname:PChar):Integer;
 vfs_mount_t         =function (mp:p_mount):Integer;
 vfs_sysctl_t        =function (mp:p_mount;op:Integer;req:Pointer):Integer;
 vfs_susp_clean_t    =procedure(mp:p_mount);
 vfs_notify_lowervp_t=procedure(mp:p_mount;lowervp:p_vnode);

 p_vfsops=^vfsops;
 vfsops=packed record
  vfs_mount     :vfs_mount_t     ;
  vfs_cmount    :vfs_cmount_t    ;
  vfs_unmount   :vfs_unmount_t   ;
  vfs_root      :vfs_root_t      ;
  vfs_quotactl  :vfs_quotactl_t  ;
  vfs_statfs    :vfs_statfs_t    ;
  vfs_sync      :vfs_sync_t      ;
  vfs_vget      :vfs_vget_t      ;
  vfs_fhtovp    :vfs_fhtovp_t    ;
  vfs_checkexp  :vfs_checkexp_t  ;
  vfs_init      :vfs_init_t      ;
  vfs_uninit    :vfs_uninit_t    ;
  vfs_extattrctl:vfs_extattrctl_t;
  vfs_sysctl    :vfs_sysctl_t    ;
  vfs_susp_clean:vfs_susp_clean_t;
 end;

 t_fsnamelen=array[0..MFSNAMELEN-1] of AnsiChar;
 t_mname    =array[0..MNAMELEN-1]   of AnsiChar;

{
  * Filesystem configuration information. One of these exists for each
  * type of filesystem supported by the kernel. These are searched at
  * mount time to identify the requested filesystem.
  *
  * XXX: Never change the first two arguments!
}
 p_vfsoptdecl=Pointer;

 vfsconf=record
  vfc_version :DWORD       ; // ABI version number
  vfc_name    :t_fsnamelen ; // filesystem type name
  vfc_vfsops  :p_vfsops    ; // filesystem operations vector
  vfc_typenum :Integer     ; // historic filesystem type number
  vfc_refcount:Integer     ; // number mounted of this type
  vfc_flags   :Integer     ; // permanent flags
  vfc_opts    :p_vfsoptdecl; // mount options
  vfc_list    :TAILQ_ENTRY ; // list of vfscons
 end;

 t_statfs=packed record
  f_version    :DWORD;  // structure version number
  f_type       :DWORD;  // type of filesystem
  f_flags      :QWORD;  // copy of mount exported flags
  f_bsize      :QWORD;  // filesystem fragment size
  f_iosize     :QWORD;  // optimal transfer block size
  f_blocks     :QWORD;  // total data blocks in filesystem
  f_bfree      :QWORD;  // free blocks in filesystem
  f_bavail     :Int64;  // free blocks avail to non-superuser
  f_files      :QWORD;  // total file nodes in filesystem
  f_ffree      :Int64;  // free nodes avail to non-superuser
  f_syncwrites :QWORD;  // count of sync writes since mount
  f_asyncwrites:QWORD;  // count of async writes since mount
  f_syncreads  :QWORD;  // count of sync reads since mount
  f_asyncreads :QWORD;  // count of async reads since mount
  f_spare:array[0..9] of QWORD;  // unused spare
  f_namemax    :DWORD;  // maximum filename length
  f_owner      :DWORD;  // user that mounted the filesystem
  f_fsid       :fsid_t; // filesystem id
  f_charspare  :array[0..79] of AnsiChar; // spare string space
  f_fstypename :t_fsnamelen; // filesystem type name
  f_mntfromname:t_mname;     // mounted filesystem
  f_mntonname  :t_mname;     // directory on which mounted
 end;
 {$IF sizeof(t_statfs)<>472}{$STOP sizeof(t_statfs)<>472}{$ENDIF}

 {
   * Structure per mounted filesystem.  Each mounted filesystem has an
   * array of operations and an instance record.  The filesystems are
   * put on a doubly linked list.
   *
   * Lock reference:
   * m - mountlist_mtx
   * i - interlock
   * v - vnode freelist mutex
   *
   * Unmarked fields are considered stable as long as a ref is held.
   *
 }
 t_mount=packed record
  mnt_mtx                :mtx         ;// mount structure interlock
  mnt_gen                :Integer     ;// mount generation
  mnt_list               :TAILQ_ENTRY ;// (m) mount list
  mnt_op                 :p_vfsops    ;// operations on fs
  mnt_vfc                :p_vfsconf   ;// configuration info
  mnt_vnodecovered       :p_vnode     ;// vnode we mounted on
  mnt_syncer             :p_vnode     ;// syncer vnode
  mnt_ref                :Integer     ;// (i) Reference count
  mnt_nvnodelist         :vnodelst    ;// (i) list of vnodes
  mnt_nvnodelistsize     :Integer     ;// (i) # of vnodes
  mnt_activevnodelist    :vnodelst    ;// (v) list of active vnodes
  mnt_activevnodelistsize:Integer     ;// (v) # of active vnodes
  mnt_writeopcount       :Integer     ;// (i) write syscalls pending
  mnt_kern_flag          :Integer     ;// (i) kernel only flags
  mnt_flag               :QWORD       ;// (i) flags shared with user
  mnt_pad_noasync        :DWORD       ;
  mnt_opt                :p_vfsoptlist;// current mount options
  mnt_optnew             :p_vfsoptlist;// new options passed to fs
  mnt_maxsymlinklen      :Integer     ;// max size of short symlink
  mnt_stat               :t_statfs    ;// cache of filesystem stats
  mnt_data               :Pointer     ;// private data
  mnt_time               :time_t      ;// last time written
  mnt_iosize_max         :Integer     ;// max size for clusters, etc
  //mnt_export             :p_netexport ;// export list
  //mnt_label              :p_label     ;// MAC label for the fs
  mnt_hashseed           :DWORD       ;// Random seed for vfs_hash
  mnt_lockref            :Integer     ;// (i) Lock reference count
  mnt_secondary_writes   :Integer     ;// (i) # of secondary writes
  mnt_secondary_accwrites:Integer     ;// (i) secondary wr. starts
  mnt_susp_owner         :Pointer     ;// (i) thread owning suspension
  mnt_explock            :mtx         ;// vfs_export walkers lock
  mnt_upper_link         :TAILQ_ENTRY ;// (m) we in the all uppers
  mnt_uppers             :TAILQ_HEAD  ;// (m) upper mounts over us
 end;

 //Generic file handle
 fhandle_t=packed record
  fh_fsid:fsid_t; // Filesystem id of mount point
  fh_fid :fid     // Filesys specific id
 end;

const
 VFS_NOTIFY_UPPER_RECLAIM=1;
 VFS_NOTIFY_UPPER_UNLINK =2;

 VFS_VERSION=$19660120;

var
 mountlist_mtx:mtx;
 mountlist:TAILQ_HEAD=(tqh_first:nil;tqh_last:@mountlist.tqh_first);

 VFS_Giant:mtx;

function  MNT_SHARED_WRITES(mp:p_mount):Boolean; inline;

procedure MNT_ILOCK(mp:p_mount); inline;
function  MNT_ITRYLOCK(mp:p_mount):Boolean; inline;
procedure MNT_IUNLOCK(mp:p_mount); inline;
function  MNT_MTX(mp:p_mount):p_mtx; inline;

procedure MNT_REF(mp:p_mount); inline;
procedure MNT_REL(mp:p_mount); inline;

function  VFS_NEEDSGIANT(mp:p_mount):Boolean;
function  VFS_LOCK_GIANT(mp:p_mount):Integer;
procedure VFS_UNLOCK_GIANT(locked:Integer);
procedure VFS_ASSERT_GIANT(mp:p_mount);

function  VFS_PROLOGUE(mp:p_mount):Boolean;
procedure VFS_EPILOGUE(_enable_stops:Boolean); inline;

function  VFS_MOUNT(mp:p_mount):Integer;
function  VFS_UNMOUNT(mp:p_mount;FORCE:Integer):Integer;
function  VFS_ROOT(mp:p_mount;flags:Integer;vpp:pp_vnode):Integer;
function  VFS_STATFS(mp:p_mount;sbp:p_statfs):Integer;
function  VFS_SYNC(mp:p_mount;WAIT:Integer):Integer;
function  VFS_VGET(mp:p_mount;ino:QWORD;flags:Integer;vpp:pp_vnode):Integer;
function  VFS_QUOTACTL(mp:p_mount;c,u:Integer;a:Pointer):Integer;
function  VFS_FHTOVP(mp:p_mount;fidp:p_fid;flags:Integer;vpp:pp_vnode):Integer;
function  VFS_EXTATTRCTL(mp:p_mount;c:Integer;fn:p_vnode;ns:Integer;n:PChar):Integer;
procedure VFS_KNOTE_LOCKED(vp:p_vnode;hint:Integer);
procedure VFS_KNOTE_UNLOCKED(vp:p_vnode;hint:Integer);

procedure vmountinit; //SYSINIT

implementation

//

procedure wakeup(ident:Pointer); external;

//

function MNT_SHARED_WRITES(mp:p_mount):Boolean; inline;
begin
 if (mp<>nil) then
 begin
  Result:=((mp^.mnt_kern_flag and MNTK_SHARED_WRITES)<>0);
 end else
 begin
  Result:=False;
 end;
end;

procedure MNT_ILOCK(mp:p_mount); inline;
begin
 mtx_lock(mp^.mnt_mtx);
end;

function MNT_ITRYLOCK(mp:p_mount):Boolean; inline;
begin
 Result:=mtx_trylock(mp^.mnt_mtx);
end;

procedure MNT_IUNLOCK(mp:p_mount); inline;
begin
 mtx_unlock(mp^.mnt_mtx);
end;

function MNT_MTX(mp:p_mount):p_mtx; inline;
begin
 Result:=@mp^.mnt_mtx;
end;

procedure MNT_REF(mp:p_mount); inline;
begin
 Assert(mp^.mnt_ref>=0);
 System.InterlockedIncrement(mp^.mnt_ref);
end;

procedure MNT_REL(mp:p_mount); inline;
begin
 Assert(mp^.mnt_ref>0);
 if (System.InterlockedDecrement(mp^.mnt_ref)=0) then
 begin
  wakeup(mp)
 end;
end;

function VFS_NEEDSGIANT(mp:p_mount):Boolean;
begin
 if (mp<>nil) then
 begin
  Result:=((mp^.mnt_kern_flag and MNTK_MPSAFE)=0);
 end else
 begin
  Result:=False;
 end;
end;

function VFS_LOCK_GIANT(mp:p_mount):Integer; public;
begin
 if VFS_NEEDSGIANT(mp) then
 begin
  mtx_lock(VFS_Giant);
  Result:=1;
 end else
  Result:=0;
end;

procedure VFS_UNLOCK_GIANT(locked:Integer);
begin
 if (locked<>0) then
  mtx_unlock(VFS_Giant);
end;

procedure VFS_ASSERT_GIANT(mp:p_mount);
begin
 if VFS_NEEDSGIANT(mp) then
  mtx_assert(VFS_Giant);
end;

function VFS_PROLOGUE(mp:p_mount):Boolean; public;
begin
 if (mp<>nil) then
 begin
  Result:=((mp^.mnt_vfc^.vfc_flags and VFCF_SBDRY)<>0) and (sigdeferstop<>0);
 end else
 begin
  Result:=False;
 end;
end;

procedure VFS_EPILOGUE(_enable_stops:Boolean); public;
begin
 if _enable_stops then sigallowstop;
end;

function VFS_MOUNT(mp:p_mount):Integer;
var
 _enable_stops:Boolean;
begin
 _enable_stops:=VFS_PROLOGUE(MP);
 Result:=mp^.mnt_op^.vfs_mount(mp);
 VFS_EPILOGUE(_enable_stops);
end;

function VFS_UNMOUNT(mp:p_mount;FORCE:Integer):Integer;
var
 _enable_stops:Boolean;
begin
 _enable_stops:=VFS_PROLOGUE(MP);
 Result:=mp^.mnt_op^.vfs_unmount(mp,FORCE);
 VFS_EPILOGUE(_enable_stops);
end;

function VFS_ROOT(mp:p_mount;flags:Integer;vpp:pp_vnode):Integer;
var
 _enable_stops:Boolean;
begin
 _enable_stops:=VFS_PROLOGUE(MP);
 Result:=mp^.mnt_op^.vfs_root(mp,flags,vpp);
 VFS_EPILOGUE(_enable_stops);
end;

function VFS_STATFS(mp:p_mount;sbp:p_statfs):Integer;
var
 _enable_stops:Boolean;
begin
 _enable_stops:=VFS_PROLOGUE(MP);
 Result:=mp^.mnt_op^.vfs_statfs(mp,@mp^.mnt_stat);
 if (sbp<>@mp^.mnt_stat) then
 begin
  sbp^:=mp^.mnt_stat;
 end;
 VFS_EPILOGUE(_enable_stops);
end;

function VFS_SYNC(mp:p_mount;WAIT:Integer):Integer;
var
 _enable_stops:Boolean;
begin
 _enable_stops:=VFS_PROLOGUE(MP);
 Result:=mp^.mnt_op^.vfs_sync(mp,WAIT);
 VFS_EPILOGUE(_enable_stops);
end;

function VFS_VGET(mp:p_mount;ino:QWORD;flags:Integer;vpp:pp_vnode):Integer;
var
 _enable_stops:Boolean;
begin
 _enable_stops:=VFS_PROLOGUE(MP);
 Result:=mp^.mnt_op^.vfs_vget(mp,ino,flags,vpp);
 VFS_EPILOGUE(_enable_stops);
end;

function VFS_QUOTACTL(mp:p_mount;c,u:Integer;a:Pointer):Integer;
var
 _enable_stops:Boolean;
begin
 _enable_stops:=VFS_PROLOGUE(MP);
 Result:=mp^.mnt_op^.vfs_quotactl(mp,c,u,a);
 VFS_EPILOGUE(_enable_stops);
end;

function VFS_FHTOVP(mp:p_mount;fidp:p_fid;flags:Integer;vpp:pp_vnode):Integer;
var
 _enable_stops:Boolean;
begin
 _enable_stops:=VFS_PROLOGUE(MP);
 Result:=mp^.mnt_op^.vfs_fhtovp(mp,fidp,flags,vpp);
 VFS_EPILOGUE(_enable_stops);
end;

function VFS_EXTATTRCTL(mp:p_mount;c:Integer;fn:p_vnode;ns:Integer;n:PChar):Integer;
var
 _enable_stops:Boolean;
begin
 _enable_stops:=VFS_PROLOGUE(MP);
 Result:=mp^.mnt_op^.vfs_extattrctl(mp,c,fn,ns,n);
 VFS_EPILOGUE(_enable_stops);
end;

procedure VFS_KNOTE_LOCKED(vp:p_vnode;hint:Integer);
begin
 if ((vp^.v_vflag and VV_NOKNOTE)=0) then
  VN_KNOTE(vp, hint, KNF_LISTLOCKED);
end;

procedure VFS_KNOTE_UNLOCKED(vp:p_vnode;hint:Integer);
begin
 if ((vp^.v_vflag and VV_NOKNOTE)=0) then
  VN_KNOTE(vp, hint, 0);
end;

procedure vmountinit;
begin
 mtx_init(mountlist_mtx,'mountlist');
 mtx_init(VFS_Giant    ,'VFS_Giant');
end;

end.

