unit fdesc_vfsops;

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

interface

uses
 vmount,
 kern_param,
 vfs_mount,
 vnode,
 fdescfs;

{
 * /dev/fd Filesystem
 }
function fdesc_cmount(ma,data:Pointer;flags:QWORD):Integer;
function fdesc_mount(mp:p_mount):Integer;
function fdesc_unmount(mp:p_mount;mntflags:Integer):Integer;
function fdesc_root(mp:p_mount;flags:Integer;vpp:pp_vnode):Integer;
function fdesc_statfs(mp:p_mount;sbp:p_statfs):Integer;

const
 _fdesc_vfsops:vfsops=(
  vfs_mount          :@fdesc_mount;
  vfs_cmount         :@fdesc_cmount;
  vfs_unmount        :@fdesc_unmount;
  vfs_root           :@fdesc_root;
  vfs_quotactl       :nil;
  vfs_statfs         :@fdesc_statfs;
  vfs_sync           :nil;
  vfs_vget           :nil;
  vfs_fhtovp         :nil;
  vfs_checkexp       :nil;
  vfs_init           :@fdesc_init;
  vfs_uninit         :@fdesc_uninit;
  vfs_extattrctl     :nil;
  vfs_sysctl         :nil;
  vfs_susp_clean     :nil;
 );

 //VFS_SET(fdesc_vfsops, fdescfs, VFCF_SYNTHETIC);
 fdescfs_vfsconf:vfsconf=(
  vfc_version :VFS_VERSION;
  vfc_name    :'fdescfs';
  vfc_vfsops  :@_fdesc_vfsops;
  vfc_typenum :-1;
  vfc_refcount:0;
  vfc_flags   :VFCF_SYNTHETIC;
  vfc_opts    :nil;
  vfc_list    :(tqe_next:nil;tqe_prev:nil)
 );

implementation

uses
 errno,
 vfiledesc,
 vfs_subr,
 vnode_if,
 kern_mtx,
 kern_id,
 fdesc_vnops;

function VFSTOFDESC(mp:p_mount):p_fdescmount; inline;
begin
 Result:=mp^.mnt_data;
end;

function VTOFDESC(vp:p_vnode):p_fdescnode; inline;
begin
 Result:=vp^.v_data;
end;

{
 * Compatibility shim for old mount(2) system call.
 }
function fdesc_cmount(ma,data:Pointer;flags:QWORD):Integer;
begin
 Exit(kernel_nmount(ma, flags));
end;

{
 * Mount the per-process file descriptors (/dev/fd)
 }
function fdesc_mount(mp:p_mount):Integer;
var
 error:Integer;
 fmp:p_fdescmount;
 rvp:p_vnode;
begin
 error:=0;
 rvp:=nil;
 {
  * Update is a no-op
  }
 if ((mp^.mnt_flag and (MNT_UPDATE or MNT_ROOTFS))<>0) then
  Exit(EOPNOTSUPP);

 fmp:=AllocMem(sizeof(t_fdescmount)); { XXX }

 {
  * We need to initialize a few bits of our local mount point struct to
  * avoid confusion in allocvp.
  }
 mp^.mnt_data:=fmp;
 fmp^.flags:=0;

 error:=fdesc_allocvp(_Froot, -1, FD_ROOT, mp, @rvp);

 if (error<>0) then
 begin
  FreeMem(fmp);
  mp^.mnt_data:=nil;
  Exit(error);
 end;

 rvp^.v_type:=VDIR;
 rvp^.v_vflag:=rvp^.v_vflag or VV_ROOT;
 fmp^.f_root:=rvp;

 VOP_UNLOCK(rvp, 0);
 { XXX -- don't mark as local to work around fts() problems }
 {mp^.mnt_flag:=mp^.mnt_flag or MNT_LOCAL;}
 MNT_ILOCK(mp);
 mp^.mnt_kern_flag:=mp^.mnt_kern_flag or MNTK_MPSAFE;
 MNT_IUNLOCK(mp);
 vfs_getnewfsid(mp);

 vfs_mountedfrom(mp, 'fdescfs');
 Exit(0);
end;

function fdesc_unmount(mp:p_mount;mntflags:Integer):Integer;
var
 fmp:p_fdescmount;
 data:Pointer;
 error:Integer;
 flags:Integer;
begin
 flags:=0;

 fmp:=p_fdescmount(mp^.mnt_data);

 if ((mntflags and MNT_FORCE)<>0) then
 begin
  { The hash mutex protects the private mount flags. }
  mtx_lock(fdesc_hashmtx);
  fmp^.flags:=fmp^.flags or FMNT_UNMOUNTF;
  mtx_unlock(fdesc_hashmtx);
  flags:=flags or FORCECLOSE;
 end;

 {
  * Clear out buffer cache.  I don't think we
  * ever get anything cached at this level at the
  * moment, but who knows...
  *
  * There is 1 extra root vnode reference corresponding
  * to f_root.
  }
  error:=vflush(mp, 1, flags);
 if (error<>0) then
  Exit(error);

 {
  * Finally, throw away the fdescmount structure. Hold the hashmtx to
  * protect the fdescmount structure.
  }
 mtx_lock(fdesc_hashmtx);
 data:=mp^.mnt_data;
 mp^.mnt_data:=nil;
 mtx_unlock(fdesc_hashmtx);
 FreeMem(data); { XXX }

 Exit(0);
end;

function fdesc_root(mp:p_mount;flags:Integer;vpp:pp_vnode):Integer;
var
 vp:p_vnode;
begin
 {
  * Exitlocked reference to root.
  }
 vp:=VFSTOFDESC(mp)^.f_root;
 vget(vp, LK_EXCLUSIVE or LK_RETRY);
 vpp^:=vp;
 Exit(0);
end;

function fdesc_statfs(mp:p_mount;sbp:p_statfs):Integer;
var
 lim:Integer;
 freefd:Integer;
begin
 lim   :=fd_table.fd_nfiles;
 freefd:=fd_table.fd_freefd;

 sbp^.f_flags :=0;
 sbp^.f_bsize :=DEV_BSIZE;
 sbp^.f_iosize:=DEV_BSIZE;
 sbp^.f_blocks:=2;  { 1K to keep df happy }
 sbp^.f_bfree :=0;
 sbp^.f_bavail:=0;
 sbp^.f_files :=lim + 1;  { Allow for '.' }
 sbp^.f_ffree :=freefd;
 Exit(0);
end;



end.

