unit vfs_syscalls;

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

interface

uses
 kern_param,
 kern_proc,
 time,
 vmount,
 vuio,
 vnamei,
 vstat,
 vfile,
 vfiledesc,
 vcapability,
 vfcntl,
 vfilio,
 vdisk,
 vfs_mount,
 vnode,
 vfs_vnops,
 vfs_subr;

function chroot_refuse_vdir_fds():Integer;
function getutimes(usrtvp:p_timeval;tvpseg:uio_seg;tsp:p_timespec):Integer;
function setfflags(vp:p_vnode;flags:Integer):Integer;
function setutimes(vp:p_vnode;ts:p_timespec;numtimes,nilflag:Integer):Integer;
function vn_access(vp:p_vnode;user_flags:Integer):Integer;
function setfown(vp:p_vnode;uid:uid_t;gid:gid_t):Integer;
function setfmode(vp:p_vnode;mode:Integer):Integer;
function getvnode(fd:Integer;rights:cap_rights_t;fpp:pp_file):Integer;

function sys_sync():Integer;
function sys_statfs(path:PChar;buf:Pointer):Integer;
function sys_fstatfs(fd:Integer;buf:Pointer):Integer;
function sys_getfsstat(buf:Pointer;bufsize:QWORD;flags:Integer):Integer;
function sys_fchdir(fd:Integer):Integer;
function sys_chdir(path:PChar):Integer;
function sys_chroot(path:PChar):Integer;
function sys_open(path:PChar;flags,mode:Integer):Integer;
function sys_openat(fd:Integer;path:PChar;flags,mode:Integer):Integer;
function sys_mknod(path:PChar;mode,dev:Integer):Integer;
function sys_mknodat(fd:Integer;path:PChar;mode,dev:Integer):Integer;
function sys_mkfifo(path:PChar;mode:Integer):Integer;
function sys_mkfifoat(fd:Integer;path:PChar;mode:Integer):Integer;
function sys_link(name1,name2:PChar):Integer;
function sys_linkat(fd1:Integer;path1:PChar;fd2:Integer;path2:PChar;flag:Integer):Integer;
function sys_symlink(path,link:PChar):Integer;
function sys_symlinkat(path1:PChar;fd:Integer;path2:PChar):Integer;
function sys_unlinkat(fd:Integer;path:PChar;flag:Integer):Integer;
function sys_unlink(path:PChar):Integer;
function sys_lseek(fd:Integer;offset:Int64;whence:Integer):Integer;
function sys_access(path:PChar;flags:Integer):Integer;
function sys_stat(path:PChar;ub:Pointer):Integer;
function sys_fstatat(fd:Integer;path:PChar;buf:Pointer;flag:Integer):Integer;
function sys_lstat(path:PChar;ub:Pointer):Integer;
function sys_pathconf(path:PChar;name:Integer):Integer;
function sys_readlink(path,buf:PChar;count:QWORD):Integer;
function sys_chflags(path:PChar;flags:Integer):Integer;
function sys_lchflags(path:PChar;flags:Integer):Integer;
function sys_fchflags(fd,flags:Integer):Integer;
function sys_chmod(path:PChar;mode:Integer):Integer;
function sys_fchmodat(fd:Integer;path:PChar;mode,flag:Integer):Integer;
function sys_lchmod(path:PChar;mode:Integer):Integer;
function sys_fchmod(fd,mode:Integer):Integer;
function sys_chown(path:PChar;uid,gid:Integer):Integer;
function sys_fchownat(fd:Integer;path:PChar;uid,gid,flag:Integer):Integer;
function sys_lchown(path:PChar;uid,gid:Integer):Integer;
function sys_fchown(fd,uid,gid:Integer):Integer;
function sys_utimes(path:PChar;tptr:Pointer):Integer;
function sys_futimesat(fd:Integer;path:PChar;times:Pointer):Integer;
function sys_lutimes(path:PChar;tptr:Pointer):Integer;
function sys_futimes(fd:Integer;tptr:Pointer):Integer;
function sys_truncate(path:PChar;length:Int64):Integer;
function sys_fsync(fd:Integer):Integer;
function sys_fdatasync(fd:Integer):Integer;
function sys_rename(from,_to:PChar):Integer;
function sys_renameat(oldfd:Integer;old:PChar;newfd:Integer;new:PChar):Integer;
function sys_mkdir(path:PChar;mode:Integer):Integer;
function sys_mkdirat(fd:Integer;path:PChar;mode:Integer):Integer;
function sys_rmdir(path:PChar):Integer;
function sys_getdirentries(fd:Integer;buf:Pointer;count:DWORD;basep:PInt64):Integer;
function sys_getdents(fd:Integer;buf:Pointer;count:DWORD):Integer;
function sys_umask(newmask:Integer):Integer;
function sys_revoke(path:PChar):Integer;

function kern_statfs(path:PChar;pathseg:uio_seg;buf:p_statfs):Integer;
function kern_fstatfs(fd:Integer;buf:p_statfs):Integer;
function kern_getfsstat(buf:pp_statfs;bufsize:QWORD;bufseg:uio_seg;flags:Integer):Integer;
function kern_chdir(path:PChar;pathseg:uio_seg):Integer;
function kern_openat(fd:Integer;path:PChar;pathseg:uio_seg;flags,mode:Integer):Integer;
function kern_open(path:PChar;pathseg:uio_seg;flags,mode:Integer):Integer;
function kern_mkfifoat(fd:Integer;path:PChar;pathseg:uio_seg;mode:Integer):Integer;
function kern_mknodat(fd:Integer;path:PChar;pathseg:uio_seg;mode,dev:Integer):Integer;
function kern_mknod(path:PChar;pathseg:uio_seg;mode,dev:Integer):Integer;
function kern_linkat(fd1,fd2:Integer;path1,path2:PChar;segflg:uio_seg;follow:Integer):Integer;
function kern_link(path,link:PChar;segflg:uio_seg):Integer;
function kern_symlinkat(path1:PChar;fd:Integer;path2:PChar;segflg:uio_seg):Integer;
function kern_symlink(path,link:PChar;segflg:uio_seg):Integer;
function kern_unlinkat(fd:Integer;path:PChar;pathseg:uio_seg;oldinum:Integer):Integer;
function kern_unlink(path:PChar;pathseg:uio_seg):Integer;
function kern_accessat(fd:Integer;path:PChar;pathseg:uio_seg;flags,mode:Integer):Integer;
function kern_access(path:PChar;pathseg:uio_seg;mode:Integer):Integer;
function kern_statat(flag,fd:Integer;path:PChar;pathseg:uio_seg;sbp:p_stat):Integer;
function kern_stat(path:PChar;pathseg:uio_seg;sbp:p_stat):Integer;
function kern_lstat(path:PChar;pathseg:uio_seg;sbp:p_stat):Integer;
function kern_pathconf(path:PChar;pathseg:uio_seg;name:Integer;flags:QWORD):Integer;
function kern_readlinkat(fd:Integer;path:PChar;pathseg:uio_seg;buf:PChar;bufseg:uio_seg;count:QWORD):Integer;
function kern_readlink(path:PChar;pathseg:uio_seg;buf:PChar;bufseg:uio_seg;count:QWORD):Integer;
function kern_fchmodat(fd:Integer;path:PChar;pathseg:uio_seg;mode,flag:Integer):Integer;
function kern_chmod(path:PChar;pathseg:uio_seg;mode:Integer):Integer;
function kern_chown(path:PChar;pathseg:uio_seg;uid,gid:Integer):Integer;
function kern_lchown(path:PChar;pathseg:uio_seg;uid,gid:Integer):Integer;
function kern_utimesat(fd:Integer;path:PChar;pathseg:uio_seg;tptr:p_timeval;tptrseg:uio_seg):Integer;
function kern_utimes(path:PChar;pathseg:uio_seg;tptr:p_timeval;tptrseg:uio_seg):Integer;
function kern_lutimes(path:PChar;pathseg:uio_seg;tptr:p_timeval;tptrseg:uio_seg):Integer;
function kern_futimes(fd:Integer;tptr:p_timeval;tptrseg:uio_seg):Integer;
function kern_truncate(path:PChar;pathseg:uio_seg;length:Int64):Integer;
function kern_fsync(fd:Integer;fullsync:Boolean):Integer;
function kern_renameat(oldfd:Integer;old:PChar;newfd:Integer;new:PChar;pathseg:uio_seg):Integer;
function kern_rename(from,_to:PChar;pathseg:uio_seg):Integer;
function kern_mkdirat(fd:Integer;path:PChar;segflg:uio_seg;mode:Integer):Integer;
function kern_mkdir(path:PChar;segflg:uio_seg;mode:Integer):Integer;
function kern_rmdirat(fd:Integer;path:PChar;pathseg:uio_seg):Integer;
function kern_rmdir(path:PChar;pathseg:uio_seg):Integer;
function kern_getdirentries(fd:Integer;buf:Pointer;count:DWORD;basep:PInt64):Integer;

/////

function sys_is_in_sandbox():Integer;
function sys_randomized_path(src,dst:pchar;plen:PQWORD):Integer;

implementation

uses
 atomic,
 mqueue,
 systm,
 errno,
 kern_mtx,
 kern_thr,
 kern_descrip,
 vnode_if,
 sys_capability,
 vmparam,
 sys_vm_object,
 vm_object;

{
 * Sync each mounted filesystem.
 }
function sys_sync():Integer;
var
 mp,nmp:p_mount;
 save, vfslocked:Integer;
begin
 mtx_lock(mountlist_mtx);
 mp:=TAILQ_FIRST(@mountlist);
 while (mp<>nil) do
 begin
  if (vfs_busy(mp, MBF_NOWAIT or MBF_MNTLSTLOCK)<>0) then
  begin
   nmp:=TAILQ_NEXT(mp,@mp^.mnt_list);
   continue;
  end;
  vfslocked:=VFS_LOCK_GIANT(mp);
  if ((mp^.mnt_flag and MNT_RDONLY)=0) and
     (vn_start_write(nil, @mp, V_NOWAIT)=0) then
  begin
   save:=curthread_pflags_set(TDP_SYNCIO);
   vfs_msync(mp, MNT_NOWAIT);
   VFS_SYNC(mp, MNT_NOWAIT);
   curthread_pflags_restore(save);
   vn_finished_write(mp);
  end;
  VFS_UNLOCK_GIANT(vfslocked);
  mtx_lock(mountlist_mtx);
  nmp:=TAILQ_NEXT(mp,@mp^.mnt_list);
  vfs_unbusy(mp);

  mp:=nmp;
 end;
 mtx_unlock(mountlist_mtx);
 Exit(0);
end;

function kern_statfs(path:PChar;pathseg:uio_seg;buf:p_statfs):Integer;
label
 _out;
var
 mp:p_mount;
 sp:p_statfs;
 sb:t_statfs;
 vfslocked:Integer;
 error:Integer;
 nd:t_nameidata;
begin
 NDINIT(@nd, LOOKUP, FOLLOW or LOCKSHARED or LOCKLEAF or MPSAFE or AUDITVNODE1, pathseg, path, curkthread);
 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  Exit(error);
 end;

 vfslocked:=NDHASGIANT(@nd);
 mp:=nd.ni_vp^.v_mount;
 vfs_ref(mp);
 NDFREE(@nd, NDF_ONLY_PNBUF);
 vput(nd.ni_vp);
 error:=vfs_busy(mp, 0);
 vfs_rel(mp);
 if (error<>0) then
 begin
  VFS_UNLOCK_GIANT(vfslocked);
  Exit(error);
 end;

 //error:=mac_mount_check_stat(td^.td_ucred, mp);
 //if (error<>0) then
 // goto _out;

 {
  * Set these in case the underlying filesystem fails to do so.
  }
 sp:=@mp^.mnt_stat;
 sp^.f_version:=STATFS_VERSION;
 sp^.f_namemax:=NAME_MAX;
 sp^.f_flags:=mp^.mnt_flag and MNT_VISFLAGMASK;
 error:=VFS_STATFS(mp, sp);
 if (error<>0) then
  goto _out;
 if {(priv_check(td, PRIV_VFS_GENERATION))} True then
 begin
  sb:=sp^;
  sb.f_fsid.val[0]:=0;
  sb.f_fsid.val[1]:=0;
  //prison_enforce_statfs(td^.td_ucred, mp, @sb);
  sp:=@sb;
 end;
 buf^:=sp^;
_out:
 vfs_unbusy(mp);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

{
 * Get filesystem statistics.
 }
function sys_statfs(path:PChar;buf:Pointer):Integer;
var
 sf:t_statfs;
 error:Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 error:=kern_statfs(path, UIO_USERSPACE, @sf);
 if (error=0) then
 begin
  error:=copyout(@sf, buf, sizeof(sf));
 end;
 Exit(error);
end;

function kern_fstatfs(fd:Integer;buf:p_statfs):Integer;
label
 _out;
var
 fp:p_file;
 mp:p_mount;
 sp:p_statfs;
 sb:t_statfs;
 vfslocked:Integer;
 vp:p_vnode;
 error:Integer;
begin
 error:=getvnode(fd, CAP_FSTATFS, @fp);
 if (error<>0) then
 begin
  Exit(error);
 end;

 vp:=fp^.f_vnode;
 vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
 vn_lock(vp, LK_SHARED or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
 mp:=vp^.v_mount;
 if (mp<>nil) then
 begin
  vfs_ref(mp);
 end;

 VOP_UNLOCK(vp, 0);
 fdrop(fp);
 if (mp=nil) then
 begin
  error:=EBADF;
  goto _out;
 end;
 error:=vfs_busy(mp, 0);
 vfs_rel(mp);
 if (error<>0) then
 begin
  VFS_UNLOCK_GIANT(vfslocked);
  Exit(error);
 end;

 //error:=mac_mount_check_stat(td^.td_ucred, mp);
 //if (error<>0) then
 // goto _out;

 {
  * Set these in case the underlying filesystem fails to do so.
  }
 sp:=@mp^.mnt_stat;
 sp^.f_version:=STATFS_VERSION;
 sp^.f_namemax:=NAME_MAX;
 sp^.f_flags:=mp^.mnt_flag and MNT_VISFLAGMASK;
 error:=VFS_STATFS(mp, sp);
 if (error<>0) then
 begin
  goto _out;
 end;
 if {(priv_check(td, PRIV_VFS_GENERATION))} True then
 begin
  sb:=sp^;
  sb.f_fsid.val[0]:=0;
  sb.f_fsid.val[1]:=0;
  //prison_enforce_statfs(td^.td_ucred, mp, @sb);
  sp:=@sb;
 end;
 buf^:=sp^;
_out:
 if (mp<>nil) then
 begin
  vfs_unbusy(mp);
 end;

 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

{
 * Get filesystem statistics.
 }
function sys_fstatfs(fd:Integer;buf:Pointer):Integer;
var
 sf:t_statfs;
 error:Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 error:=kern_fstatfs(fd, @sf);
 if (error=0) then
 begin
  error:=copyout(@sf, buf, sizeof(sf));
 end;
 Exit(error);
end;

{
 * If (bufsize > 0 and bufseg=UIO_SYSSPACE)
 *  The caller is responsible for freeing memory which will be allocated
 * in '*buf'.
 }

function kern_getfsstat(buf:pp_statfs;bufsize:QWORD;bufseg:uio_seg;flags:Integer):Integer;
var
 td:p_kthread;
 mp,nmp:p_mount;
 sfsp,sp:p_statfs;
 sb:t_statfs;
 count,maxcount:QWORD;
 vfslocked:Integer;
 error:Integer;
begin
 td:=curkthread;
 if (td=nil) then Exit(-1);

 maxcount:=bufsize div sizeof(t_statfs);
 if (bufsize=0) then
 begin
  sfsp:=nil;
 end else
 if (bufseg=UIO_USERSPACE) then
 begin
  sfsp:=buf^;
 end else
 begin
  count:=0;
  mtx_lock(mountlist_mtx);
  mp:=TAILQ_FIRST(@mountlist);
  while (mp<>nil) do
  begin
   Inc(count);
   mp:=TAILQ_NEXT(mp,@mp^.mnt_list);
  end;
  mtx_unlock(mountlist_mtx);
  if (maxcount > count) then
  begin
   maxcount:=count;
  end;
  sfsp:=AllocMem(maxcount*sizeof(t_statfs));
  buf^:=sfsp;
 end;
 count:=0;
 mtx_lock(mountlist_mtx);
 mp:=TAILQ_FIRST(@mountlist);
 while (mp<>nil) do
 begin
  if {(prison_canseemount(td^.td_ucred, mp)<>0)} True then
  begin
   nmp:=TAILQ_NEXT(mp,@mp^.mnt_list);
   continue;
  end;

  //if (mac_mount_check_stat(td^.td_ucred, mp)<>0) then
  //begin
  // nmp:=TAILQ_NEXT(mp, mnt_list);
  // continue;
  //end;

  if (vfs_busy(mp, MBF_NOWAIT or MBF_MNTLSTLOCK)<>0) then
  begin
   nmp:=TAILQ_NEXT(mp,@mp^.mnt_list);
   continue;
  end;
  vfslocked:=VFS_LOCK_GIANT(mp);
  if (sfsp<>nil) and (count<maxcount) then
  begin
   sp:=@mp^.mnt_stat;
   {
    * Set these in case the underlying filesystem
    * fails to do so.
    }
   sp^.f_version:=STATFS_VERSION;
   sp^.f_namemax:=NAME_MAX;
   sp^.f_flags:=mp^.mnt_flag and MNT_VISFLAGMASK;
   {
    * If MNT_NOWAIT or MNT_LAZY is specified, do not
    * refresh the fsstat cache. MNT_NOWAIT or MNT_LAZY
    * overrides MNT_WAIT.
    }
   if ((flags and (MNT_LAZY or MNT_NOWAIT))=0) or
      ((flags and MNT_WAIT)<>0) then
   begin
    error:=VFS_STATFS(mp, sp);
    if (error<>0) then
    begin
     VFS_UNLOCK_GIANT(vfslocked);
     mtx_lock(mountlist_mtx);
     nmp:=TAILQ_NEXT(mp,@mp^.mnt_list);
     vfs_unbusy(mp);
     continue;
    end;
   end;
   if {(priv_check(td, PRIV_VFS_GENERATION))} True then
   begin
    sb:=sp^;
    sb.f_fsid.val[0]:=0;
    sb.f_fsid.val[1]:=0;
    //prison_enforce_statfs(td^.td_ucred, mp, @sb);
    sp:=@sb;
   end;
   if (bufseg=UIO_SYSSPACE) then
   begin
    sfsp^:=sp^;
   end else
   begin
    error:=copyout(sp, sfsp, sizeof(sb));
    if (error<>0) then
    begin
     vfs_unbusy(mp);
     VFS_UNLOCK_GIANT(vfslocked);
     Exit(error);
    end;
   end;
   Inc(sfsp);
  end;
  VFS_UNLOCK_GIANT(vfslocked);
  Inc(count);
  mtx_lock(mountlist_mtx);
  nmp:=TAILQ_NEXT(mp,@mp^.mnt_list);
  vfs_unbusy(mp);

  mp:=nmp;
 end;
 mtx_unlock(mountlist_mtx);

 if (sfsp<>nil) and (count>maxcount) then
  td^.td_retval[0]:=maxcount
 else
  td^.td_retval[0]:=count;

 Exit(0);
end;

{
 * Get statistics on all filesystems.
 }
function sys_getfsstat(buf:Pointer;bufsize:QWORD;flags:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_getfsstat(@buf, bufsize, UIO_USERSPACE, flags));
end;

function change_dir(vp:p_vnode):Integer; forward;

{
 * Change current working directory to a given file descriptor.
 }
function sys_fchdir(fd:Integer):Integer;
var
 vp,tdp,vpold:p_vnode;
 mp:p_mount;
 fp:p_file;
 vfslocked:Integer;
 error:Integer;
 tvfslocked:Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 error:=getvnode(fd, CAP_FCHDIR, @fp);
 if (error<>0) then
 begin
  Exit(error);
 end;
 vp:=fp^.f_vnode;
 VREF(vp);
 fdrop(fp);
 vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
 vn_lock(vp, LK_SHARED or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});

 error:=change_dir(vp);
 mp:=vp^.v_mountedhere;
 while (error=0) and (mp<>nil) do
 begin
  if (vfs_busy(mp, 0)<>0) then
  begin
   continue;
  end;
  tvfslocked:=VFS_LOCK_GIANT(mp);
  error:=VFS_ROOT(mp, LK_SHARED, @tdp);
  vfs_unbusy(mp);
  if (error<>0) then
  begin
   VFS_UNLOCK_GIANT(tvfslocked);
   break;
  end;
  vput(vp);
  VFS_UNLOCK_GIANT(vfslocked);
  vp:=tdp;
  vfslocked:=tvfslocked;

  mp:=vp^.v_mountedhere;
 end;
 if (error<>0) then
 begin
  vput(vp);
  VFS_UNLOCK_GIANT(vfslocked);
  Exit(error);
 end;
 VOP_UNLOCK(vp, 0);
 VFS_UNLOCK_GIANT(vfslocked);
 FILEDESC_XLOCK(@fd_table);
 vpold:=fd_table.fd_cdir;
 fd_table.fd_cdir:=vp;
 FILEDESC_XUNLOCK(@fd_table);
 vfslocked:=VFS_LOCK_GIANT(vpold^.v_mount);
 vrele(vpold);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(0);
end;

function kern_chdir(path:PChar;pathseg:uio_seg):Integer;
var
 error:Integer;
 nd:t_nameidata;
 vp:p_vnode;
 vfslocked:Integer;
begin
 NDINIT(@nd, LOOKUP, FOLLOW or LOCKSHARED or LOCKLEAF or AUDITVNODE1 or MPSAFE, pathseg, path, curkthread);
 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  Exit(error);
 end;
 vfslocked:=NDHASGIANT(@nd);
 error:=change_dir(nd.ni_vp);
 if (error<>0) then
 begin
  vput(nd.ni_vp);
  VFS_UNLOCK_GIANT(vfslocked);
  NDFREE(@nd, NDF_ONLY_PNBUF);
  Exit(error);
 end;
 VOP_UNLOCK(nd.ni_vp, 0);
 VFS_UNLOCK_GIANT(vfslocked);
 NDFREE(@nd, NDF_ONLY_PNBUF);
 FILEDESC_XLOCK(@fd_table);
 vp:=fd_table.fd_cdir;
 fd_table.fd_cdir:=nd.ni_vp;
 FILEDESC_XUNLOCK(@fd_table);
 vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
 vrele(vp);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(0);
end;

{
 * Change current working directory (``.'').
 }
function sys_chdir(path:PChar):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_chdir(path, UIO_USERSPACE));
end;

{
 * Helper function for raised chroot(2) security function:  Refuse if
 * any filedescriptors are open directories.
 }
function chroot_refuse_vdir_fds():Integer;
var
 vp:p_vnode;
 fp:p_file;
 fd:Integer;
begin
 //FILEDESC_LOCK_ASSERT(@fd_table);

 For fd:=0 to fd_table.fd_nfiles-1 do
 begin
  fp:=fget_locked(@fd_table, fd);
  if (fp=nil) then
  begin
   continue;
  end;
  if (fp^.f_type=DTYPE_VNODE) then
  begin
   vp:=fp^.f_vnode;
   if (vp^.v_type=VDIR) then
   begin
    Exit(EPERM);
   end;
  end;
 end;
 Exit(0);
end;

{
 * This sysctl determines if we will allow a process to chroot(2) if it
 * has a directory open:
 * 0: disallowed for all processes.
 * 1: allowed for processes that were not already chroot(2)'ed.
 * 2: allowed for all processes.
 }
const
 chroot_allow_open_directories=0;

function change_root(vp:p_vnode):Integer; forward;

{
 * Change notion of root (``/'') directory.
 }
function sys_chroot(path:PChar):Integer;
label
 _error,
 e_vunlock;
var
 error:Integer;
 nd:t_nameidata;
 vfslocked:Integer;
begin
 error:=EPERM;
 //error:=priv_check(td, PRIV_VFS_CHROOT);
 if (error<>0) then
 begin
  Exit(error);
 end;

 NDINIT(@nd, LOOKUP, FOLLOW or LOCKSHARED or LOCKLEAF or MPSAFE or AUDITVNODE1, UIO_USERSPACE, path, curkthread);
 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  goto _error;
 end;
 vfslocked:=NDHASGIANT(@nd);
 error:=change_dir(nd.ni_vp);
 if (error<>0) then
 begin
  goto e_vunlock;

 //if ((error:=mac_vnode_check_chroot(td^.td_ucred, nd.ni_vp)))
 // goto e_vunlock;
 end;

 VOP_UNLOCK(nd.ni_vp, 0);
 error:=change_root(nd.ni_vp);
 vrele(nd.ni_vp);
 VFS_UNLOCK_GIANT(vfslocked);
 NDFREE(@nd, NDF_ONLY_PNBUF);
 Exit(error);
e_vunlock:
 vput(nd.ni_vp);
 VFS_UNLOCK_GIANT(vfslocked);
_error:
 NDFREE(@nd, NDF_ONLY_PNBUF);
 Exit(error);
end;

{
 * Common routine for chroot and chdir.  Callers must provide a locked vnode
 * instance.
 }

function change_dir(vp:p_vnode):Integer;
var
 error:Integer;
begin
 ASSERT_VOP_LOCKED(vp, 'change_dir(): vp not locked');
 if (vp^.v_type<>VDIR) then
 begin
  Exit(ENOTDIR);
 end;

 //error:=mac_vnode_check_chdir(td^.td_ucred, vp);
 //if (error<>0) then
 // Exit(error);

 error:=VOP_ACCESS(vp, VEXEC);
 Exit(error);
end;

{
 * Common routine for kern_chroot() and jail_attach().  The caller is
 * responsible for invoking priv_check() and mac_vnode_check_chroot() to
 * authorize this operation.
 }

function change_root(vp:p_vnode):Integer;
var
 oldvp:p_vnode;
 vfslocked:Integer;
 error:Integer;
begin
 VFS_ASSERT_GIANT(vp^.v_mount);
 FILEDESC_XLOCK(@fd_table);

 if (chroot_allow_open_directories=0) or
    ((chroot_allow_open_directories=1) and (fd_table.fd_rdir<>rootvnode)) then
 begin
  error:=chroot_refuse_vdir_fds();
  if (error<>0) then
  begin
   FILEDESC_XUNLOCK(@fd_table);
   Exit(error);
  end;
 end;

 oldvp:=fd_table.fd_rdir;
 fd_table.fd_rdir:=vp;
 VREF(fd_table.fd_rdir);

 if (fd_table.fd_jdir=nil) then
 begin
  fd_table.fd_jdir:=vp;
  VREF(fd_table.fd_jdir);
 end;

 FILEDESC_XUNLOCK(@fd_table);
 vfslocked:=VFS_LOCK_GIANT(oldvp^.v_mount);
 vrele(oldvp);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(0);
end;

function flags_to_rights(flags:Integer):cap_rights_t;
var
 rights:cap_rights_t;
begin
 rights:=0;

 case (flags and O_ACCMODE) of
  O_RDONLY:
   begin
    rights:=rights or CAP_READ;
   end;

  O_RDWR:
   begin
    rights:=rights or CAP_READ or CAP_WRITE;
   end;

  O_WRONLY:
   begin
    rights:=rights or CAP_WRITE;
   end;

  O_EXEC:
   begin
    rights:=rights or CAP_FEXECVE;
   end;
 end;

 if ((flags and O_CREAT)<>0) then
 begin
  rights:=rights or CAP_CREATE;
 end;

 if ((flags and O_TRUNC)<>0) then
 begin
  rights:=rights or CAP_FTRUNCATE;
 end;

 if ((flags and O_EXLOCK)<>0) or ((flags and O_SHLOCK)<>0) then
 begin
  rights:=rights or CAP_FLOCK;
 end;

 Exit(rights);
end;

function kern_openat(fd:Integer;path:PChar;pathseg:uio_seg;flags,mode:Integer):Integer;
label
 success,
 bad_unlocked,
 bad;
var
 td:p_kthread;
 fp:p_file;
 vp:p_vnode;
 cmode:Integer;
 nfp:p_file;
 l_type,indx,error,error_open:Integer;
 lf:t_flock;
 nd:t_nameidata;
 vfslocked:Integer;
 rights_needed:cap_rights_t;
begin
 td:=curkthread;
 if (td=nil) then Exit(-1);

 indx:=-1;
 rights_needed:=CAP_LOOKUP;

 { XXX: audit dirfd }
 rights_needed:=rights_needed or flags_to_rights(flags);
 {
  * Only one of the O_EXEC, O_RDONLY, O_WRONLY and O_RDWR flags
  * may be specified.
  }
 if ((flags and O_EXEC)<>0) then
 begin
  if ((flags and O_ACCMODE)<>0) then
  begin
   Exit(EINVAL);
  end;
 end else
 if ((flags and O_ACCMODE)=O_ACCMODE) then
 begin
  Exit(EINVAL)
 end else
 begin
  flags:=FFLAGS(flags);
 end;

 {
  * allocate the file descriptor, but don't install a descriptor yet
  }
 error:=falloc_noinstall(@nfp);
 if (error<>0) then
 begin
  Exit(error);
 end;
 { An extra reference on `nfp' has been held for us by falloc_noinstall(). }
 fp:=nfp;
 { Set the flags early so the finit in devfs can pick them up. }
 fp^.f_flag:=flags and FMASK;
 cmode:=((mode and (not fd_table.fd_cmask)) and ALLPERMS) and (not S_ISTXT);
 NDINIT_ATRIGHTS(@nd, LOOKUP, FOLLOW or AUDITVNODE1 or MPSAFE, pathseg, path, fd, rights_needed, td);
 td^.td_dupfd:=-1;  { XXX check for fdopen }
 error:=vn_open(@nd, @flags, cmode, fp);
 if (error<>0) then
 begin
  {
   * If the vn_open replaced the method vector, something
   * wonderous happened deep below and we just pass it up
   * pretending we know what we do.
   }
  if (error=ENXIO) and (fp^.f_ops<>@badfileops) then
  begin
   goto success;
  end;

  {
   * handle special fdopen() case.  bleh.  dupfdopen() is
   * responsible for dropping the old contents of ofiles[indx]
   * if it succeeds.
   *
   * Don't do this for relative (capability) lookups; we don't
   * understand exactly what would happen, and we don't think
   * that it ever should.
   }
  if (nd.ni_strictrelative=0) and
     ((error=ENODEV) or (error=ENXIO)) and
     (td^.td_dupfd >= 0) then
  begin
   { XXX from fdopen }
   error_open:=error;
   error:=finstall(fp, @indx, flags);
   if (error<>0) then
   begin
    goto bad_unlocked;
   end;

   error:=dupfdopen(indx, td^.td_dupfd, flags, error_open);
   if (error=0) then
   begin
    goto success;
   end;
  end;
  {
   * Clean up the descriptor, but only if another thread hadn't
   * replaced or closed it.
   }
  if (indx<>-1) then
  begin
   fdclose(fp, indx);
  end;
  fdrop(fp);

  Exit(error);
 end;

 td^.td_dupfd:=0;
 vfslocked:=NDHASGIANT(@nd);
 NDFREE(@nd, NDF_ONLY_PNBUF);
 vp:=nd.ni_vp;

 {
  * Store the vnode, for any f_type. Typically, the vnode use
  * count is decremented by direct call to vn_closefile() for
  * files that switched _type in the cdevsw fdopen() method.
  }
 fp^.f_vnode:=vp;
 {
  * If the file wasn't claimed by devfs bind it to the normal
  * vnode operations here.
  }
 if (fp^.f_ops=@badfileops) then
 begin
  Assert(vp^.v_type<>VFIFO,'Unexpected fifo.');
  fp^.f_seqcount:=1;
  finit(fp, flags and FMASK, DTYPE_VNODE, vp, @vnops);
 end;

 VOP_UNLOCK(vp, 0);
 if (fp^.f_type=DTYPE_VNODE) and ((flags and (O_EXLOCK or O_SHLOCK))<>0) then
 begin
  lf.l_whence:=SEEK_SET;
  lf.l_start:=0;
  lf.l_len:=0;

  if ((flags and O_EXLOCK)<>0) then
   lf.l_type:=F_WRLCK
  else
   lf.l_type:=F_RDLCK;

  l_type:=F_FLOCK;

  if ((flags and FNONBLOCK)=0) then
  begin
   l_type:=l_type or F_WAIT;
  end;

  error:=VOP_ADVLOCK(vp, fp, F_SETLK, @lf, l_type);
  if (error<>0) then
  begin
   goto bad;
  end;

  atomic_set_int(@fp^.f_flag, FHASLOCK);
 end;
 if ((flags and O_TRUNC)<>0) then
 begin
  error:=fo_truncate(fp, 0);
  if (error<>0) then
  begin
   goto bad;
  end;
 end;
 VFS_UNLOCK_GIANT(vfslocked);
success:
 {
  * If we haven't already installed the FD (for dupfdopen), do so now.
  }
 if (indx=-1) then
 begin

  if (nd.ni_strictrelative=1) then
  begin
   {
    * We are doing a strict relative lookup; wrap the
    * result in a capability.
    }
   error:=kern_capwrap(fp, nd.ni_baserights, @indx);
   if (error<>0) then
   begin
    goto bad_unlocked;
   end;
  end else
  begin
   error:=finstall(fp, @indx, flags);
   if (error<>0) then
   begin
    goto bad_unlocked;
   end;
  end;
 end;

 {
  * Release our private reference, leaving the one associated with
  * the descriptor table intact.
  }
 fdrop(fp);
 td^.td_retval[0]:=indx;
 Exit(0);

bad:
 VFS_UNLOCK_GIANT(vfslocked);

bad_unlocked:
 if (indx<>-1) then
 begin
  fdclose(fp, indx);
 end;

 fdrop(fp);
 td^.td_retval[0]:=QWORD(-1);
 Exit(error);
end;

function kern_open(path:PChar;pathseg:uio_seg;flags,mode:Integer):Integer;
begin
 Exit(kern_openat(AT_FDCWD, path, pathseg, flags, mode));
end;

{
 * Check permissions, allocate an open file structure, and call the device
 * open routine if any.
 }
function sys_open(path:PChar;flags,mode:Integer):Integer;
begin
 Result:=kern_open(path, UIO_USERSPACE, flags, mode);
 //
 if (curkthread<>nil) then
 if is_guest_addr(curkthread^.td_frame.tf_rip) then
 begin
  Writeln('sys_open("',path,'",0x',HexStr(flags,4),',0',OctStr(mode,3),'):',Result);
 end;
end;

function sys_openat(fd:Integer;path:PChar;flags,mode:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Result:=kern_openat(fd, path, UIO_USERSPACE, flags, mode);
 //
 if (curkthread<>nil) then
 if is_guest_addr(curkthread^.td_frame.tf_rip) then
 begin
  Writeln('sys_openat(',fd,',"',path,'",0x',HexStr(flags,4),',0',OctStr(mode,3),'):',Result);
 end;
end;

function kern_mkfifoat(fd:Integer;path:PChar;pathseg:uio_seg;mode:Integer):Integer;
label
 restart,
 _out;
var
 mp:p_mount;
 vattr:t_vattr;
 error:Integer;
 nd:t_nameidata;
 vfslocked:Integer;
begin
restart:
 //bwillwrite();
 NDINIT_AT(@nd, CREATE, LOCKPARENT or SAVENAME or MPSAFE or AUDITVNODE1, pathseg, path, fd, curkthread);
 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  Exit(error);
 end;

 vfslocked:=NDHASGIANT(@nd);
 if (nd.ni_vp<>nil) then
 begin
  NDFREE(@nd, NDF_ONLY_PNBUF);

  if (nd.ni_vp=nd.ni_dvp) then
   vrele(nd.ni_dvp)
  else
   vput(nd.ni_dvp);

  vrele(nd.ni_vp);
  VFS_UNLOCK_GIANT(vfslocked);
  Exit(EEXIST);
 end;
 if (vn_start_write(nd.ni_dvp, @mp, V_NOWAIT)<>0) then
 begin
  NDFREE(@nd, NDF_ONLY_PNBUF);
  vput(nd.ni_dvp);
  VFS_UNLOCK_GIANT(vfslocked);
  error:=vn_start_write(nil, @mp, V_XSLEEP or PCATCH);
  if (error<>0) then
  begin
   Exit(error);
  end;
  goto restart;
 end;
 VATTR_NULL(@vattr);
 vattr.va_type:=VFIFO;
 vattr.va_mode:=(mode and ALLPERMS) and (not fd_table.fd_cmask);

 //error:=mac_vnode_check_create(td^.td_ucred, nd.ni_dvp, @nd.ni_cnd, @vattr);
 //if (error<>0) then
 // goto _out;

 error:=VOP_MKNOD(nd.ni_dvp, @nd.ni_vp, @nd.ni_cnd, @vattr);
 if (error=0) then
 begin
  vput(nd.ni_vp);
 end;

_out:
 vput(nd.ni_dvp);
 vn_finished_write(mp);
 VFS_UNLOCK_GIANT(vfslocked);
 NDFREE(@nd, NDF_ONLY_PNBUF);
 Exit(error);
end;

function kern_mknodat(fd:Integer;path:PChar;pathseg:uio_seg;mode,dev:Integer):Integer;
label
 restart;
var
 vp:p_vnode;
 mp:p_mount;
 vattr:t_vattr;
 error:Integer;
 whiteout:Integer;
 nd:t_nameidata;
 vfslocked:Integer;
begin
 whiteout:=0;

 case (mode and S_IFMT) of
  S_IFCHR,
  S_IFBLK:
   begin
    error:=EPERM;
    //error:=priv_check(td, PRIV_VFS_MKNOD_DEV);
   end;
  S_IFMT:
   begin
    error:=EPERM;
    //error:=priv_check(td, PRIV_VFS_MKNOD_BAD);
   end;
  S_IFWHT:
   begin
    error:=EPERM;
    //error:=priv_check(td, PRIV_VFS_MKNOD_WHT);
   end;
  S_IFIFO:
   begin
    if (dev=0) then
     Exit(kern_mkfifoat(fd, path, pathseg, mode));
    error:=EINVAL;
   end
  else
   error:=EINVAL;
 end;

 if (error<>0) then
 begin
  Exit(error);
 end;

restart:
 //bwillwrite();
 NDINIT_ATRIGHTS(@nd, CREATE, LOCKPARENT or SAVENAME or MPSAFE or AUDITVNODE1, pathseg, path, fd, CAP_MKFIFO, curkthread);
 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  Exit(error);
 end;
 vfslocked:=NDHASGIANT(@nd);
 vp:=nd.ni_vp;
 if (vp<>nil) then
 begin
  NDFREE(@nd, NDF_ONLY_PNBUF);

  if (vp=nd.ni_dvp) then
   vrele(nd.ni_dvp)
  else
   vput(nd.ni_dvp);

  vrele(vp);
  VFS_UNLOCK_GIANT(vfslocked);
  Exit(EEXIST);
 end else
 begin
  VATTR_NULL(@vattr);
  vattr.va_mode:=(mode and ALLPERMS) and (not fd_table.fd_cmask);
  vattr.va_rdev:=dev;
  whiteout:=0;

  case (mode and S_IFMT) of
   S_IFMT: { used by badsect to flag bad sectors }
    vattr.va_type:=VBAD;
   S_IFCHR:
    vattr.va_type:=VCHR;
   S_IFBLK:
    vattr.va_type:=VBLK;
   S_IFWHT:
    whiteout:=1;
   else
    Assert(False,'kern_mknod: invalid mode');
  end;
 end;
 if (vn_start_write(nd.ni_dvp, @mp, V_NOWAIT)<>0) then
 begin
  NDFREE(@nd, NDF_ONLY_PNBUF);
  vput(nd.ni_dvp);
  VFS_UNLOCK_GIANT(vfslocked);
  error:=vn_start_write(nil, @mp, V_XSLEEP or PCATCH);
  if (error<>0) then
  begin
   Exit(error);
  end;
  goto restart;
 end;

 //if (error=0) and (whiteout=0) then
 // error:=mac_vnode_check_create(td^.td_ucred, nd.ni_dvp, @nd.ni_cnd, @vattr);

 if (error=0) then
 begin
  if (whiteout<>0) then
   error:=VOP_WHITEOUT(nd.ni_dvp, @nd.ni_cnd, CREATE)
  else
  begin
   error:=VOP_MKNOD(nd.ni_dvp, @nd.ni_vp, @nd.ni_cnd, @vattr);

   if (error=0) then
   begin
    vput(nd.ni_vp);
   end;
  end;
 end;
 NDFREE(@nd, NDF_ONLY_PNBUF);
 vput(nd.ni_dvp);
 vn_finished_write(mp);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

function kern_mknod(path:PChar;pathseg:uio_seg;mode,dev:Integer):Integer;
begin
 Exit(kern_mknodat(AT_FDCWD, path, pathseg, mode, dev));
end;

{
 * Create a special file.
 }
function sys_mknod(path:PChar;mode,dev:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_mknod(path, UIO_USERSPACE, mode, dev));
end;

function sys_mknodat(fd:Integer;path:PChar;mode,dev:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_mknodat(fd, path, UIO_USERSPACE, mode, dev));
end;

function kern_mkfifo(path:PChar;pathseg:uio_seg;mode:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_mkfifoat(AT_FDCWD, path, pathseg, mode));
end;

{
 * Create a named pipe.
 }
function sys_mkfifo(path:PChar;mode:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_mkfifo(path, UIO_USERSPACE, mode));
end;

function sys_mkfifoat(fd:Integer;path:PChar;mode:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_mkfifoat(fd, path, UIO_USERSPACE, mode));
end;

function can_hardlink(vp:p_vnode):Integer; inline;
begin
 //error = priv_check_cred(cred, PRIV_VFS_LINK, 0);
 //Exit(EPERM);
 Exit(0);
end;

function kern_linkat(fd1,fd2:Integer;path1,path2:PChar;segflg:uio_seg;follow:Integer):Integer;
var
 vp:p_vnode;
 mp:p_mount;
 nd:t_nameidata;
 vfslocked:Integer;
 lvfslocked:Integer;
 error:Integer;
begin
 //bwillwrite();
 NDINIT_AT(@nd, LOOKUP, follow or MPSAFE or AUDITVNODE1, segflg, path1, fd1, curkthread);

 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  Exit(error);
 end;

 vfslocked:=NDHASGIANT(@nd);
 NDFREE(@nd, NDF_ONLY_PNBUF);
 vp:=nd.ni_vp;
 if (vp^.v_type=VDIR) then
 begin
  vrele(vp);
  VFS_UNLOCK_GIANT(vfslocked);
  Exit(EPERM);  { POSIX }
 end;
 error:=vn_start_write(vp, @mp, V_WAIT or PCATCH);
 if (error<>0) then
 begin
  vrele(vp);
  VFS_UNLOCK_GIANT(vfslocked);
  Exit(error);
 end;
 NDINIT_AT(@nd, CREATE, LOCKPARENT or SAVENAME or MPSAFE or AUDITVNODE2, segflg, path2, fd2, curkthread);
 error:=nd_namei(@nd);
 if (error=0) then
 begin
  lvfslocked:=NDHASGIANT(@nd);
  if (nd.ni_vp<>nil) then
  begin
   if (nd.ni_dvp=nd.ni_vp) then
    vrele(nd.ni_dvp)
   else
    vput(nd.ni_dvp);

   vrele(nd.ni_vp);
   error:=EEXIST;
  end else
  begin
   error:=vn_lock(vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
   if (error=0) then
   begin
    error:=can_hardlink(vp);
    if (error=0) then

     // error:=mac_vnode_check_link(td^.td_ucred, nd.ni_dvp, vp, @nd.ni_cnd);
     //if (error=0) then
    begin
     error:=VOP_LINK(nd.ni_dvp, vp, @nd.ni_cnd);
    end;
    VOP_UNLOCK(vp, 0);
    vput(nd.ni_dvp);
   end;
  end;
  NDFREE(@nd, NDF_ONLY_PNBUF);
  VFS_UNLOCK_GIANT(lvfslocked);
 end;
 vrele(vp);
 vn_finished_write(mp);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

function kern_link(path,link:PChar;segflg:uio_seg):Integer;
begin
 Exit(kern_linkat(AT_FDCWD, AT_FDCWD, path,link, segflg, FOLLOW));
end;

{
 * Make a hard file link.
 }
function sys_link(name1,name2:PChar):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_link(name1,name2,UIO_USERSPACE));
end;

function sys_linkat(fd1:Integer;path1:PChar;fd2:Integer;path2:PChar;flag:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 if ((flag and (not AT_SYMLINK_FOLLOW))<>0) then
 begin
  Exit(EINVAL);
 end;

 if ((flag and AT_SYMLINK_FOLLOW)<>0) then
 begin
  Exit(kern_linkat(fd1, fd2, path1, path2, UIO_USERSPACE, FOLLOW));
 end else
 begin
  Exit(kern_linkat(fd1, fd2, path1, path2, UIO_USERSPACE, NOFOLLOW));
 end;
end;

function kern_symlinkat(path1:PChar;fd:Integer;path2:PChar;segflg:uio_seg):Integer;
label
 _out,
 restart,
 out2;
var
 mp:p_mount;
 vattr:t_vattr;
 syspath:PChar;
 error:Integer;
 nd:t_nameidata;
 vfslocked:Integer;
begin
 if (segflg=UIO_SYSSPACE) then
 begin
  syspath:=path1;
 end else
 begin
  syspath:=AllocMem(MAXPATHLEN);
  error:=copyinstr(path1, syspath, MAXPATHLEN, nil);
  if (error<>0) then goto _out;
 end;
 restart:
 //bwillwrite();
 NDINIT_AT(@nd, CREATE, LOCKPARENT or SAVENAME or MPSAFE or AUDITVNODE1, segflg, path2, fd, curkthread);
 error:=nd_namei(@nd);
 if (error<>0) then goto _out;

 vfslocked:=NDHASGIANT(@nd);
 if (nd.ni_vp<>nil) then
 begin
  NDFREE(@nd, NDF_ONLY_PNBUF);

  if (nd.ni_vp=nd.ni_dvp) then
   vrele(nd.ni_dvp)
  else
   vput(nd.ni_dvp);

  vrele(nd.ni_vp);
  VFS_UNLOCK_GIANT(vfslocked);
  error:=EEXIST;
  goto _out;
 end;
 if (vn_start_write(nd.ni_dvp, @mp, V_NOWAIT)<>0) then
 begin
  NDFREE(@nd, NDF_ONLY_PNBUF);
  vput(nd.ni_dvp);
  VFS_UNLOCK_GIANT(vfslocked);
  error:=vn_start_write(nil, @mp, V_XSLEEP or PCATCH);
  if (error<>0) then goto _out;

  goto restart;
 end;
 VATTR_NULL(@vattr);
 vattr.va_mode:=ACCESSPERMS and (not fd_table.fd_cmask);

 //vattr.va_type:=VLNK;
 //error:=mac_vnode_check_create(td^.td_ucred, nd.ni_dvp, @nd.ni_cnd, @vattr);
 //if (error)
 // goto out2;

 error:=VOP_SYMLINK(nd.ni_dvp, @nd.ni_vp, @nd.ni_cnd, @vattr, syspath);
 if (error=0) then
 begin
  vput(nd.ni_vp);
 end;

out2:
 NDFREE(@nd, NDF_ONLY_PNBUF);
 vput(nd.ni_dvp);
 vn_finished_write(mp);
 VFS_UNLOCK_GIANT(vfslocked);
_out:
 if (segflg<>UIO_SYSSPACE) then
 begin
  FreeMem(syspath);
 end;

 Exit(error);
end;

function kern_symlink(path,link:PChar;segflg:uio_seg):Integer;
begin
 Exit(kern_symlinkat(path, AT_FDCWD, link, segflg));
end;

{
 * Make a symbolic link.
 }
function sys_symlink(path,link:PChar):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_symlink(path, link, UIO_USERSPACE));
end;

function sys_symlinkat(path1:PChar;fd:Integer;path2:PChar):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_symlinkat(path1, fd, path2, UIO_USERSPACE));
end;

function kern_unlinkat(fd:Integer;path:PChar;pathseg:uio_seg;oldinum:Integer):Integer;
label
 restart,
 _out;
var
 mp:p_mount;
 vp:p_vnode;
 error:Integer;
 nd:t_nameidata;
 sb:t_stat;
 vfslocked:Integer;

 function _vn_stat:Integer; inline;
 begin
  error:=vn_stat(vp, @sb);
  Result:=error;
 end;

begin
 restart:
 //bwillwrite();
 NDINIT_AT(@nd, DELETE, LOCKPARENT or LOCKLEAF or MPSAFE or AUDITVNODE1, pathseg, path, fd, curkthread);
 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  if (error=EINVAL) then
  begin
   Exit(EPERM)
  end else
  begin
   Exit(error);
  end;
 end;
 vfslocked:=NDHASGIANT(@nd);
 vp:=nd.ni_vp;
 if (vp^.v_type=VDIR) and (oldinum=0) then
 begin
  error:=EPERM;  { POSIX }
 end else
 if (oldinum<>0) and
    (_vn_stat=0) and
    (sb.st_ino<>oldinum) then
 begin
  error:=EIDRM; { Identifier removed }
 end else
 begin
  {
   * The root of a mounted filesystem cannot be deleted.
   *
   * XXX: can this only be a VDIR case?
   }
  if ((vp^.v_vflag and VV_ROOT)<>0) then
  begin
   error:=EBUSY;
  end;
 end;
 if (error=0) then
 begin
  if (vn_start_write(nd.ni_dvp, @mp, V_NOWAIT)<>0) then
  begin
   NDFREE(@nd, NDF_ONLY_PNBUF);
   vput(nd.ni_dvp);

   if (vp=nd.ni_dvp) then
    vrele(vp)
   else
    vput(vp);

   VFS_UNLOCK_GIANT(vfslocked);
   error:=vn_start_write(nil, @mp, V_XSLEEP or PCATCH);
   if (error<>0) then
   begin
    Exit(error);
   end;
   goto restart;
  end;

  //error:=mac_vnode_check_unlink(td^.td_ucred, nd.ni_dvp, vp, @nd.ni_cnd);
  //if (error)
  // goto _out;

  error:=VOP_REMOVE(nd.ni_dvp, vp, @nd.ni_cnd);

  _out:
  vn_finished_write(mp);
 end;
 NDFREE(@nd, NDF_ONLY_PNBUF);
 vput(nd.ni_dvp);

 if (vp=nd.ni_dvp) then
  vrele(vp)
 else
  vput(vp);

 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

function kern_unlink(path:PChar;pathseg:uio_seg):Integer;
begin
 Exit(kern_unlinkat(AT_FDCWD, path, pathseg, 0));
end;

function sys_unlinkat(fd:Integer;path:PChar;flag:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 if ((flag and (not AT_REMOVEDIR))<>0) then
 begin
  Exit(EINVAL);
 end;

 if ((flag and AT_REMOVEDIR)<>0) then
  Exit(kern_rmdirat(fd, path, UIO_USERSPACE))
 else
  Exit(kern_unlinkat(fd, path, UIO_USERSPACE, 0));
end;

{
 * Delete a name from the filesystem.
 }
function sys_unlink(path:PChar):Integer;
begin
 Exit(kern_unlink(path, UIO_USERSPACE));
end;

{
 * Reposition read/write file offset.
 }
function sys_lseek(fd:Integer;offset:Int64;whence:Integer):Integer;
label
 drop,
 _break;
var
 td:p_kthread;
 fp:p_file;
 vp:p_vnode;
 vattr:t_vattr;
 foffset,size:Int64;
 error,noneg:Integer;
 vfslocked:Integer;
begin
 td:=curkthread;
 if (td=nil) then Exit(-1);

 error:=fget(fd, CAP_SEEK, @fp);
 if (error<>0) then
 begin
  Exit(error);
 end;

 if ((fp^.f_ops^.fo_flags and DFLAG_SEEKABLE)=0) then
 begin
  fdrop(fp);
  Exit(ESPIPE);
 end;

 vp:=fp^.f_vnode;
 foffset:=foffset_lock(fp, 0);
 vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
 noneg:=ord(vp^.v_type<>VCHR);

 case whence of
  L_INCR: //current
   begin
    if (noneg<>0) and
       ((foffset < 0) or
        ((offset > 0) and (foffset > (High(Int64) - offset)))) then
    begin
     error:=EOVERFLOW;
     goto _break;
    end;
    Inc(offset,foffset);
   end;
  L_XTND: //end
   begin
    vn_lock(vp, LK_SHARED or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
    error:=VOP_GETATTR(vp, @vattr);
    VOP_UNLOCK(vp, 0);
    if (error<>0) then
    begin
     goto _break;
    end;

    {
     * If the file references a disk device, then fetch
     * the media size and use that to determine the ending
     * offset.
     }
    if (vattr.va_size=0) and
       (vp^.v_type=VCHR) and
       (fo_ioctl(fp, DIOCGMEDIASIZE, @size)=0) then
    begin
     vattr.va_size:=size;
    end;

    if (noneg<>0) and
       ((vattr.va_size < 0) or
        ((offset > 0) and (vattr.va_size > High(Int64) - offset))) then
    begin
     error:=EOVERFLOW;
     goto _break;
    end;
    Inc(offset,vattr.va_size);
   end;
  L_SET, //absolute
  SEEK_DATA:
   error:=fo_ioctl(fp, FIOSEEKDATA, @offset);
  SEEK_HOLE:
   error:=fo_ioctl(fp, FIOSEEKHOLE, @offset);
  else
   error:=EINVAL;
 end;
 _break:
 if (error=0) and (noneg<>0) and (offset < 0) then
 begin
  error:=EINVAL;
 end;

 if (error<>0) then
 begin
  goto drop;
 end;

 VFS_KNOTE_UNLOCKED(vp, 0);
 td^.td_retval[0]:=offset;

drop:
 fdrop(fp);
 VFS_UNLOCK_GIANT(vfslocked);

 if (error<>0) then
 begin
  foffset_unlock(fp, offset, FOF_NOUPDATE);
 end else
 begin
  foffset_unlock(fp, offset, 0);
 end;

 Exit(error);
end;

{
 * Check access permissions using passed credentials.
 }
function vn_access(vp:p_vnode;user_flags:Integer):Integer;
var
 error:Integer;
 accmode:accmode_t;

 function _vn_writechk:Integer; inline;
 begin
  error:=vn_writechk(vp);
  Result:=error;
 end;

begin
 { Flags=0 means only check for existence. }
 error:=0;
 if (user_flags<>0) then
 begin
  accmode:=0;
  if ((user_flags and R_OK)<>0) then
   accmode:=accmode or VREAD;
  if ((user_flags and W_OK)<>0) then
   accmode:=accmode or VWRITE;
  if ((user_flags and X_OK)<>0) then
   accmode:=accmode or VEXEC;

  //error:=mac_vnode_check_access(cred, vp, accmode);
  //if (error<>0) then
  // Exit(error);

  if ((accmode and VWRITE)=0) or (_vn_writechk=0) then
  begin
   error:=VOP_ACCESS(vp, accmode);
  end;
 end;
 Exit(error);
end;

function kern_accessat(fd:Integer;path:PChar;pathseg:uio_seg;flags,mode:Integer):Integer;
label
 out1;
var
 vp:p_vnode;
 nd:t_nameidata;
 vfslocked:Integer;
 error:Integer;
begin
 NDINIT_ATRIGHTS(@nd, LOOKUP, FOLLOW or LOCKSHARED or LOCKLEAF or MPSAFE or AUDITVNODE1, pathseg, path, fd, CAP_FSTAT, curkthread);
 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  goto out1;
 end;
 vfslocked:=NDHASGIANT(@nd);
 vp:=nd.ni_vp;

 error:=vn_access(vp, mode);
 NDFREE(@nd, NDF_ONLY_PNBUF);
 vput(vp);
 VFS_UNLOCK_GIANT(vfslocked);
out1:
 Exit(error);
end;

function kern_access(path:PChar;pathseg:uio_seg;mode:Integer):Integer;
begin
 Exit(kern_accessat(AT_FDCWD, path, pathseg, 0, mode));
end;

{
 * Check access permissions using 'real' credentials.
 }
function sys_access(path:PChar;flags:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_access(path, UIO_USERSPACE, flags));
end;

type
 t_statat_hook_cb=procedure(vp:p_vnode;sbp:p_stat);

function kern_statat_vnhook(flag,fd:Integer;
                            path:PChar;
                            pathseg:uio_seg;
                            sbp:p_stat;
                            hook:t_statat_hook_cb):Integer;
var
 nd:t_nameidata;
 sb:t_stat;
 error, vfslocked:Integer;
begin
 if (flag and (not AT_SYMLINK_NOFOLLOW))<>0 then
 begin
  Exit(EINVAL);
 end;

 if ((flag and AT_SYMLINK_NOFOLLOW)<>0) then
 begin
  NDINIT_ATRIGHTS(@nd, LOOKUP, NOFOLLOW or LOCKSHARED or LOCKLEAF or AUDITVNODE1 or MPSAFE, pathseg,
      path, fd, CAP_FSTAT, curkthread);
 end else
 begin
  NDINIT_ATRIGHTS(@nd, LOOKUP, FOLLOW or LOCKSHARED or LOCKLEAF or AUDITVNODE1 or MPSAFE, pathseg,
      path, fd, CAP_FSTAT, curkthread);
 end;

 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  Exit(error);
 end;

 vfslocked:=NDHASGIANT(@nd);
 error:=vn_stat(nd.ni_vp, @sb);
 if (error=0) then
 begin
  if (hook<>nil) then
  begin
   hook(nd.ni_vp, @sb);
  end;
 end;

 NDFREE(@nd, NDF_ONLY_PNBUF);
 vput(nd.ni_vp);
 VFS_UNLOCK_GIANT(vfslocked);

 if (error<>0) then
 begin
  Exit(error);
 end;

 //error:=priv_check(td,683);
 error:=EPERM;
 if (error<>0) then
 begin
  sb.st_dev   :=0;
  sb.st_ino   :=0;
  sb.st_nlink :=0;
  sb.st_uid   :=0;
  sb.st_gid   :=0;
  sb.st_rdev  :=0;
  sb.st_flags :=0;
  sb.st_gen   :=0;
  sb.st_lspare:=0;
 end;

 sbp^:=sb;
 Exit(0);
end;

function kern_statat(flag,fd:Integer;path:PChar;
                     pathseg:uio_seg;sbp:p_stat):Integer; inline;
begin
 Exit(kern_statat_vnhook(flag, fd, path, pathseg, sbp, nil));
end;

function kern_stat(path:PChar;pathseg:uio_seg;sbp:p_stat):Integer; inline;
begin
 Exit(kern_statat(0, AT_FDCWD, path, pathseg, sbp));
end;

{
 * Get file status; this version follows links.
 }
function sys_stat(path:PChar;ub:Pointer):Integer;
var
 sb:t_stat;
 error:Integer;
begin

 begin
  Writeln('sys_stat("',path,'")');
 end;

 error:=kern_stat(path, UIO_USERSPACE, @sb);
 if (error=0) then
 begin
  error:=copyout(@sb, ub, sizeof(sb));
 end;
 Exit(error);
end;

function sys_fstatat(fd:Integer;path:PChar;buf:Pointer;flag:Integer):Integer;
var
 sb:p_stat;
 error:Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 error:=kern_statat(flag, fd, path, UIO_USERSPACE, @sb);
 if (error=0) then
 begin
  error:=copyout(@sb, buf, sizeof(sb));
 end;
 Exit(error);
end;

function kern_lstat(path:PChar;pathseg:uio_seg;sbp:p_stat):Integer;
begin
 Exit(kern_statat(AT_SYMLINK_NOFOLLOW, AT_FDCWD, path, pathseg, sbp));
end;

{
 * Get file status; this version does not follow links.
 }
function sys_lstat(path:PChar;ub:Pointer):Integer;
var
 sb:t_stat;
 error:Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 error:=kern_lstat(path, UIO_USERSPACE, @sb);
 if (error=0) then
 begin
  error:=copyout(@sb, ub, sizeof(sb));
 end;
 Exit(error);
end;

function kern_pathconf(path:PChar;pathseg:uio_seg;name:Integer;flags:QWORD):Integer;
var
 td:p_kthread;
 nd:t_nameidata;
 error,vfslocked:Integer;
begin
 td:=curkthread;
 if (td=nil) then Exit(-1);

 NDINIT(@nd, LOOKUP, LOCKSHARED or LOCKLEAF or MPSAFE or AUDITVNODE1 or flags, pathseg, path, td);
 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  Exit(error);
 end;
 vfslocked:=NDHASGIANT(@nd);
 NDFREE(@nd, NDF_ONLY_PNBUF);

 { If asynchronous I/O is available, it works for all files. }
 if (name=_PC_ASYNC_IO) then
  td^.td_retval[0]:=async_io_version
 else
  error:=VOP_PATHCONF(nd.ni_vp, name, @td^.td_retval);

 vput(nd.ni_vp);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

{
 * Get configurable pathname variables.
 }
function sys_pathconf(path:PChar;name:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_pathconf(path, UIO_USERSPACE, name, FOLLOW));
end;

function kern_readlinkat(fd:Integer;path:PChar;pathseg:uio_seg;
                         buf:PChar;bufseg:uio_seg;count:QWORD):Integer;
var
 td:p_kthread;
 vp:p_vnode;
 aiov:iovec;
 auio:t_uio;
 error:Integer;
 nd:t_nameidata;
 vfslocked:Integer;
begin
 if (count > IOSIZE_MAX) then
 begin
  Exit(EINVAL);
 end;

 td:=curkthread;
 if (td=nil) then Exit(-1);

 NDINIT_AT(@nd, LOOKUP, NOFOLLOW or LOCKSHARED or LOCKLEAF or MPSAFE or AUDITVNODE1, pathseg, path, fd, td);

 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  Exit(error);
 end;

 NDFREE(@nd, NDF_ONLY_PNBUF);
 vfslocked:=NDHASGIANT(@nd);
 vp:=nd.ni_vp;

 //error:=mac_vnode_check_readlink(td^.td_ucred, vp);
 //if (error<>0) then
 //begin
 // vput(vp);
 // VFS_UNLOCK_GIANT(vfslocked);
 // Exit(error);
 //end;

 if (vp^.v_type<>VLNK) then
  error:=EINVAL
 else
 begin
  aiov.iov_base  :=buf;
  aiov.iov_len   :=count;
  auio.uio_iov   :=@aiov;
  auio.uio_iovcnt:=1;
  auio.uio_offset:=0;
  auio.uio_rw    :=UIO_READ;
  auio.uio_segflg:=bufseg;
  auio.uio_td    :=td;
  auio.uio_resid :=count;
  error:=VOP_READLINK(vp, @auio);
  td^.td_retval[0]:=count - auio.uio_resid;
 end;
 vput(vp);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

function kern_readlink(path:PChar;pathseg:uio_seg;buf:PChar;
                       bufseg:uio_seg;count:QWORD):Integer;
begin
 Exit(kern_readlinkat(AT_FDCWD, path, pathseg, buf, bufseg, count));
end;

{
 * Exittarget name of a symbolic link.
 }
function sys_readlink(path,buf:PChar;count:QWORD):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_readlink(path, UIO_USERSPACE, buf, UIO_USERSPACE, count));
end;

{
 * Common implementation code for chflags() and fchflags().
 }
function setfflags(vp:p_vnode;flags:Integer):Integer;
var
 error:Integer;
 mp:p_mount;
 vattr:t_vattr;
begin
 { We can't support the value matching VNOVAL. }
 if (flags=VNOVAL) then Exit(EOPNOTSUPP);

 {
  * Prevent non-root users from setting flags on devices.  When
  * a device is reused, users can retain ownership of the device
  * if they are allowed to set flags and programs assume that
  * chown can't fail when done as root.
  }
 if (vp^.v_type=VCHR) or (vp^.v_type=VBLK) then
 begin
  error:=EPERM;
  //error:=priv_check(td, PRIV_VFS_CHFLAGS_DEV);
  if (error<>0) then
  begin
   Exit(error);
  end;
 end;

 error:=vn_start_write(vp, @mp, V_WAIT or PCATCH);
 if (error<>0) then Exit(error);

 vn_lock(vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});

 VATTR_NULL(@vattr);
 vattr.va_flags:=flags;

 //error:=mac_vnode_check_setflags(td^.td_ucred, vp, vattr.va_flags);
 //if (error=0) then
 begin
  error:=VOP_SETATTR(vp, @vattr);
 end;
 VOP_UNLOCK(vp, 0);
 vn_finished_write(mp);
 Exit(error);
end;

{
 * Change flags of a file given a path name.
 }
function sys_chflags(path:PChar;flags:Integer):Integer;
var
 error:Integer;
 nd:t_nameidata;
 vfslocked:Integer;
begin
 NDINIT(@nd, LOOKUP, FOLLOW or MPSAFE or AUDITVNODE1, UIO_USERSPACE, path, curkthread);
 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  Exit(error);
 end;
 NDFREE(@nd, NDF_ONLY_PNBUF);
 vfslocked:=NDHASGIANT(@nd);
 error:=setfflags(nd.ni_vp, flags);
 vrele(nd.ni_vp);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

{
 * Same as chflags() but doesn't follow symlinks.
 }
function sys_lchflags(path:PChar;flags:Integer):Integer;
var
 error:Integer;
 nd:t_nameidata;
 vfslocked:Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 NDINIT(@nd, LOOKUP, NOFOLLOW or MPSAFE or AUDITVNODE1, UIO_USERSPACE, path, curkthread);
 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  Exit(error);
 end;
 vfslocked:=NDHASGIANT(@nd);
 NDFREE(@nd, NDF_ONLY_PNBUF);
 error:=setfflags(nd.ni_vp, flags);
 vrele(nd.ni_vp);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

{
 * Change flags of a file given a file descriptor.
 }
function sys_fchflags(fd,flags:Integer):Integer;
var
 fp:p_file;
 vfslocked:Integer;
 error:Integer;
begin
 error:=getvnode(fd, CAP_FCHFLAGS, @fp);
 if (error<>0) then
 begin
  Exit(error);
 end;

 vfslocked:=VFS_LOCK_GIANT(fp^.f_vnode^.v_mount);

 error:=setfflags(fp^.f_vnode, flags);
 VFS_UNLOCK_GIANT(vfslocked);
 fdrop(fp);
 Exit(error);
end;

{
 * Common implementation code for chmod(), lchmod() and fchmod().
 }
function setfmode(vp:p_vnode;mode:Integer):Integer;
var
 error:Integer;
 mp:p_mount;
 vattr:t_vattr;
begin
 error:=vn_start_write(vp, @mp, V_WAIT or PCATCH);
 if (error<>0) then
 begin
  Exit(error);
 end;
 vn_lock(vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
 VATTR_NULL(@vattr);
 vattr.va_mode:=mode and ALLPERMS;

 //error:=mac_vnode_check_setmode(cred, vp, vattr.va_mode);
 //if (error=0) then
 begin
  error:=VOP_SETATTR(vp, @vattr);
 end;
 VOP_UNLOCK(vp, 0);
 vn_finished_write(mp);
 Exit(error);
end;

function kern_fchmodat(fd:Integer;path:PChar;pathseg:uio_seg;mode,flag:Integer):Integer;
var
 error:Integer;
 nd:t_nameidata;
 vfslocked:Integer;
 follow:Integer;
begin
 if (flag and AT_SYMLINK_NOFOLLOW)<>0 then
 begin
  follow:=NOFOLLOW;
 end else
 begin
  follow:=FOLLOW;
 end;

 NDINIT_ATRIGHTS(@nd, LOOKUP,  follow or MPSAFE or AUDITVNODE1, pathseg, path, fd, CAP_FCHMOD, curkthread);
 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  Exit(error);
 end;
 vfslocked:=NDHASGIANT(@nd);
 NDFREE(@nd, NDF_ONLY_PNBUF);
 error:=setfmode(nd.ni_vp, mode);
 vrele(nd.ni_vp);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

function kern_chmod(path:PChar;pathseg:uio_seg;mode:Integer):Integer;
begin
 Exit(kern_fchmodat(AT_FDCWD, path, pathseg, mode, 0));
end;

{
 * Change mode of a file given path name.
 }
function sys_chmod(path:PChar;mode:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_chmod(path, UIO_USERSPACE, mode));
end;

function sys_fchmodat(fd:Integer;path:PChar;mode,flag:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 if ((flag and (not AT_SYMLINK_NOFOLLOW))<>0) then
 begin
  Exit(EINVAL);
 end;

 Exit(kern_fchmodat(fd, path, UIO_USERSPACE, mode, flag));
end;

{
 * Change mode of a file given path name (don't follow links.)
 }
function sys_lchmod(path:PChar;mode:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_fchmodat(AT_FDCWD, path, UIO_USERSPACE, mode, AT_SYMLINK_NOFOLLOW));
end;

{
 * Change mode of a file given a file descriptor.
 }
function sys_fchmod(fd,mode:Integer):Integer;
var
 fp:p_file;
 error:Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 error:=fget(fd, CAP_FCHMOD, @fp);
 if (error<>0) then
 begin
  Exit(error);
 end;
 error:=fo_chmod(fp, mode);
 fdrop(fp);
 Exit(error);
end;

{
 * Common implementation for chown(), lchown(), and fchown()
 }
function setfown(vp:p_vnode;uid:uid_t;gid:gid_t):Integer;
var
 error:Integer;
 mp:p_mount;
 vattr:t_vattr;
begin
 error:=vn_start_write(vp, @mp, V_WAIT or PCATCH);

 if (error<>0) then
 begin
  Exit(error);
 end;
 vn_lock(vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
 VATTR_NULL(@vattr);
 vattr.va_uid:=uid;
 vattr.va_gid:=gid;

 //error:=mac_vnode_check_setowner(cred, vp, vattr.va_uid, vattr.va_gid);
 //if (error=0) then
 begin
  error:=VOP_SETATTR(vp, @vattr);
 end;
 VOP_UNLOCK(vp, 0);
 vn_finished_write(mp);
 Exit(error);
end;

function kern_fchownat(fd:Integer;path:PChar;pathseg:uio_seg;uid,gid,flag:Integer):Integer;
var
 nd:t_nameidata;
 error,vfslocked,follow:Integer;
begin
 if (flag and AT_SYMLINK_NOFOLLOW)<>0 then
 begin
  follow:=NOFOLLOW;
 end else
 begin
  follow:=FOLLOW;
 end;

 NDINIT_ATRIGHTS(@nd, LOOKUP, follow or MPSAFE or AUDITVNODE1, pathseg, path, fd, CAP_FCHOWN, curkthread);

 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  Exit(error);
 end;
 vfslocked:=NDHASGIANT(@nd);
 NDFREE(@nd, NDF_ONLY_PNBUF);
 error:=setfown(nd.ni_vp, uid, gid);
 vrele(nd.ni_vp);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

function kern_chown(path:PChar;pathseg:uio_seg;uid,gid:Integer):Integer;
begin
 Exit(kern_fchownat(AT_FDCWD, path, pathseg, uid, gid, 0));
end;

{
 * Set ownership given a path name.
 }
function sys_chown(path:PChar;uid,gid:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_chown(path, UIO_USERSPACE, uid, gid));
end;

function sys_fchownat(fd:Integer;path:PChar;uid,gid,flag:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 if ((flag and (not AT_SYMLINK_NOFOLLOW))<>0) then
 begin
  Exit(EINVAL);
 end;

 Exit(kern_fchownat(fd, path, UIO_USERSPACE, uid, gid, flag));
end;

function kern_lchown(path:PChar;pathseg:uio_seg;uid,gid:Integer):Integer;
begin
 Exit(kern_fchownat(AT_FDCWD, path, pathseg, uid, gid,AT_SYMLINK_NOFOLLOW));
end;

{
 * Set ownership given a path name, do not cross symlinks.
 }
function sys_lchown(path:PChar;uid,gid:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_lchown(path, UIO_USERSPACE, uid, gid));
end;

{
 * Set ownership given a file descriptor.
 }
function sys_fchown(fd,uid,gid:Integer):Integer;
var
 fp:p_file;
 error:Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 error:=fget(fd, CAP_FCHOWN, @fp);
 if (error<>0) then
 begin
  Exit(error);
 end;
 error:=fo_chown(fp, uid, gid);
 fdrop(fp);
 Exit(error);
end;

{
 * Common implementation code for utimes(), lutimes(), and futimes().
 }
function getutimes(usrtvp:p_timeval;tvpseg:uio_seg;tsp:p_timespec):Integer;
var
 tv:array[0..1] of timeval;
 tvp:p_timeval;
 error:Integer;
begin
 if (usrtvp=nil) then
 begin
  vfs_timestamp(@tsp[0]);
  tsp[1]:=tsp[0];
 end else
 begin
  if (tvpseg=UIO_SYSSPACE) then
  begin
   tvp:=usrtvp;
  end else
  begin
   error:=copyin(usrtvp, @tv, sizeof(tv));
   if (error<>0) then
   begin
    Exit(error);
   end;
   tvp:=tv;
  end;

  if (tvp[0].tv_usec < 0) or (tvp[0].tv_usec >= 1000000) or
     (tvp[1].tv_usec < 0) or (tvp[1].tv_usec >= 1000000) then
  begin
   Exit(EINVAL);
  end;

  TIMEVAL_TO_TIMESPEC(@tvp[0], @tsp[0]);
  TIMEVAL_TO_TIMESPEC(@tvp[1], @tsp[1]);
 end;
 Exit(0);
end;

{
 * Common implementation code for utimes(), lutimes(), and futimes().
 }
function setutimes(vp:p_vnode;ts:p_timespec;numtimes,nilflag:Integer):Integer;
var
 error,setbirthtime:Integer;
 mp:p_mount;
 vattr:t_vattr;
begin
 error:=vn_start_write(vp, @mp, V_WAIT or PCATCH);
 if (error<>0) then Exit(error);

 vn_lock(vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});

 setbirthtime:=0;
 if (numtimes < 3) and
    (VOP_GETATTR(vp, @vattr)=0) and
    (timespeccmp_lt(@ts[1], @vattr.va_birthtime)<>0) then //<
 begin
  setbirthtime:=1;
 end;

 VATTR_NULL(@vattr);
 vattr.va_atime:=ts[0];
 vattr.va_mtime:=ts[1];

 if (setbirthtime<>0) then
 begin
  vattr.va_birthtime:=ts[1];
 end;

 if (numtimes > 2) then
 begin
  vattr.va_birthtime:=ts[2];
 end;

 if (nilflag<>0) then
 begin
  vattr.va_vaflags:=vattr.va_vaflags or VA_UTIMES_NULL;

 //error:=mac_vnode_check_setutimes(td^.td_ucred, vp, vattr.va_atime, vattr.va_mtime);
 end;

 if (error=0) then
 begin
  error:=VOP_SETATTR(vp, @vattr);
 end;

 VOP_UNLOCK(vp, 0);
 vn_finished_write(mp);
 Exit(error);
end;

function kern_utimesat(fd:Integer;path:PChar;pathseg:uio_seg;
                       tptr:p_timeval;tptrseg:uio_seg):Integer;
var
 nd:t_nameidata;
 ts:array[0..1] of timespec;
 error,vfslocked:Integer;
begin
 error:=getutimes(tptr, tptrseg, ts);
 if (error<>0) then Exit(error);

 NDINIT_ATRIGHTS(@nd, LOOKUP, FOLLOW or MPSAFE or AUDITVNODE1, pathseg, path, fd, CAP_FUTIMES, curkthread);

 error:=nd_namei(@nd);
 if (error<>0) then Exit(error);

 vfslocked:=NDHASGIANT(@nd);
 NDFREE(@nd, NDF_ONLY_PNBUF);
 error:=setutimes(nd.ni_vp, ts, 2, ord(tptr=nil));
 vrele(nd.ni_vp);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

function kern_utimes(path:PChar;pathseg:uio_seg;
                     tptr:p_timeval;tptrseg:uio_seg):Integer;
begin
 Exit(kern_utimesat(AT_FDCWD, path, pathseg, tptr, tptrseg));
end;

{
 * Set the access and modification times of a file.
 }
function sys_utimes(path:PChar;tptr:Pointer):Integer;
begin
 Exit(kern_utimes(path, UIO_USERSPACE, tptr, UIO_USERSPACE));
end;

function sys_futimesat(fd:Integer;path:PChar;times:Pointer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_utimesat(fd, path, UIO_USERSPACE, times, UIO_USERSPACE));
end;

function kern_lutimes(path:PChar;pathseg:uio_seg;
                      tptr:p_timeval;tptrseg:uio_seg):Integer;
var
 ts:array[0..1] of timespec;
 error:Integer;
 nd:t_nameidata;
 vfslocked:Integer;
begin
 error:=getutimes(tptr, tptrseg, ts);
 if (error<>0) then Exit(error);

 NDINIT(@nd, LOOKUP, NOFOLLOW or MPSAFE or AUDITVNODE1, pathseg, path, curkthread);
 error:=nd_namei(@nd);
 if (error<>0) then Exit(error);

 vfslocked:=NDHASGIANT(@nd);
 NDFREE(@nd, NDF_ONLY_PNBUF);
 error:=setutimes(nd.ni_vp, ts, 2, ord(tptr=nil));
 vrele(nd.ni_vp);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

{
 * Set the access and modification times of a file.
 }
function sys_lutimes(path:PChar;tptr:Pointer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_lutimes(path, UIO_USERSPACE, tptr, UIO_USERSPACE));
end;

function kern_futimes(fd:Integer;tptr:p_timeval;tptrseg:uio_seg):Integer;
var
 ts:array[0..1] of timespec;
 fp:p_file;
 vfslocked:Integer;
 error:Integer;
begin
 error:=getutimes(tptr, tptrseg, ts);
 if (error<>0) then
 begin
  Exit(error);
 end;

 error:=getvnode(fd, CAP_FUTIMES, @fp);
 if (error<>0) then
 begin
  Exit(error);
 end;
 vfslocked:=VFS_LOCK_GIANT(fp^.f_vnode^.v_mount);

 error:=setutimes(fp^.f_vnode, ts, 2, ord(tptr=nil));
 VFS_UNLOCK_GIANT(vfslocked);
 fdrop(fp);
 Exit(error);
end;

{
 * Set the access and modification times of a file.
 }
function sys_futimes(fd:Integer;tptr:Pointer):Integer;
begin
 Exit(kern_futimes(fd, tptr, UIO_USERSPACE));
end;

function kern_truncate(path:PChar;pathseg:uio_seg;length:Int64):Integer;
var
 mp:p_mount;
 vp:p_vnode;
 rl_cookie:Pointer;
 vattr:t_vattr;
 nd:t_nameidata;
 error,vfslocked:Integer;
begin
 if (length < 0) then
 begin
  Exit(EINVAL);
 end;
 NDINIT(@nd, LOOKUP, FOLLOW or MPSAFE or AUDITVNODE1, pathseg, path, curkthread);
 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  Exit(error);
 end;
 vfslocked:=NDHASGIANT(@nd);
 vp:=nd.ni_vp;
 rl_cookie:=vn_rangelock_wlock(vp, 0, High(Int64));
 error:=vn_start_write(vp, @mp, V_WAIT or PCATCH);
 if (error<>0) then
 begin
  vn_rangelock_unlock(vp, rl_cookie);
  vrele(vp);
  VFS_UNLOCK_GIANT(vfslocked);
  Exit(error);
 end;
 NDFREE(@nd, NDF_ONLY_PNBUF);
 vn_lock(vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
 if (vp^.v_type=VDIR) then
  error:=EISDIR

 //else if ((error:=mac_vnode_check_write(td^.td_ucred, NOCRED, vp)))
 //begin
 //end

 else
 begin
  error:=vn_writechk(vp);
  if (error=0) then
  begin
   error:=VOP_ACCESS(vp, VWRITE);
   if (error=0) then
   begin
    VATTR_NULL(@vattr);
    vattr.va_size:=length;
    error:=VOP_SETATTR(vp, @vattr);
   end;
  end;
 end;
 VOP_UNLOCK(vp, 0);
 vn_finished_write(mp);
 vn_rangelock_unlock(vp, rl_cookie);
 vrele(vp);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

{
 * Truncate a file given its path name.
 }
function sys_truncate(path:PChar;length:Int64):Integer;
begin
 Exit(kern_truncate(path, UIO_USERSPACE, length));
end;

{
 * Sync an open file.
 }
function kern_fsync(fd:Integer;fullsync:Boolean):Integer;
label
 drop;
var
 vp:p_vnode;
 mp:p_mount;
 fp:p_file;
 vfslocked:Integer;
 error,lock_flags:Integer;
begin
 error:=getvnode(fd, CAP_FSYNC, @fp);
 if (error<>0) then Exit(error);

 vp:=fp^.f_vnode;
 vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
 error:=vn_start_write(vp, @mp, V_WAIT or PCATCH);
 if (error<>0) then goto drop;

 if MNT_SHARED_WRITES(mp) or
    ((mp=nil) and MNT_SHARED_WRITES(vp^.v_mount)) then
 begin
  lock_flags:=LK_SHARED;
 end else
 begin
  lock_flags:=LK_EXCLUSIVE;
 end;
 vn_lock(vp, lock_flags or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});

 if (vp^.v_object<>nil) then
 begin
  VM_OBJECT_LOCK(vp^.v_object);
  vm_object_page_clean(vp^.v_object, 0, 0, 0);
  VM_OBJECT_UNLOCK(vp^.v_object);
 end;

 error:=VOP_FSYNC(vp, MNT_WAIT or ((ord(fullsync) and 1) shl 1));

 VOP_UNLOCK(vp, 0);
 vn_finished_write(mp);
drop:
 VFS_UNLOCK_GIANT(vfslocked);
 fdrop(fp);
 Exit(error);
end;

function sys_fsync(fd:Integer):Integer;
begin
 Exit(kern_fsync(fd, true));
end;

function sys_fdatasync(fd:Integer):Integer;
begin
 Exit(kern_fsync(fd, false));
end;

function kern_renameat(oldfd:Integer;old:PChar;newfd:Integer;new:PChar;pathseg:uio_seg):Integer;
label
 out1,
 _out;
var
 mp:p_mount;
 tvp,fvp,tdvp:p_vnode;
 fromnd,tond:t_nameidata;
 tvfslocked:Integer;
 fvfslocked:Integer;
 error:Integer;
begin
 mp:=nil;

 //bwillwrite();
 NDINIT_ATRIGHTS(@fromnd, DELETE, LOCKPARENT or LOCKLEAF or SAVESTART or
     MPSAFE or AUDITVNODE1, pathseg, old, oldfd, CAP_DELETE, curkthread);

 error:=nd_namei(@fromnd);
 if (error<>0) then
 begin
  Exit(error);
 end;

 fvfslocked:=NDHASGIANT(@fromnd);
 tvfslocked:=0;

 //error:=mac_vnode_check_rename_from(td^.td_ucred, fromnd.ni_dvp,
 //    fromnd.ni_vp, @fromnd.ni_cnd);

 VOP_UNLOCK(fromnd.ni_dvp, 0);
 if (fromnd.ni_dvp<>fromnd.ni_vp) then
 begin
  VOP_UNLOCK(fromnd.ni_vp, 0);
 end;

 fvp:=fromnd.ni_vp;
 if (error=0) then
 begin
  error:=vn_start_write(fvp, @mp, V_WAIT or PCATCH);
 end;

 if (error<>0) then
 begin
  NDFREE(@fromnd, NDF_ONLY_PNBUF);
  vrele(fromnd.ni_dvp);
  vrele(fvp);
  goto out1;
 end;

 NDINIT_ATRIGHTS(@tond, RENAME, LOCKPARENT or LOCKLEAF or NOCACHE or
     SAVESTART or MPSAFE or AUDITVNODE2, pathseg, new, newfd, CAP_CREATE,
     curkthread);

 if (fromnd.ni_vp^.v_type=VDIR) then
 begin
  tond.ni_cnd.cn_flags:=tond.ni_cnd.cn_flags or WILLBEDIR;
 end;

 error:=nd_namei(@tond);
 if (error<>0) then
 begin
  { Translate error code for rename('dir1', 'dir2/.'). }
  if (error=EISDIR) and (fvp^.v_type=VDIR) then
  begin
   error:=EINVAL;
  end;

  NDFREE(@fromnd, NDF_ONLY_PNBUF);
  vrele(fromnd.ni_dvp);
  vrele(fvp);
  vn_finished_write(mp);
  goto out1;
 end;

 tvfslocked:=NDHASGIANT(@tond);
 tdvp:=tond.ni_dvp;
 tvp:=tond.ni_vp;
 if (tvp<>nil) then
 begin
  if (fvp^.v_type=VDIR) and (tvp^.v_type<>VDIR) then
  begin
   error:=ENOTDIR;
   goto _out;
  end else
  if (fvp^.v_type<>VDIR) and (tvp^.v_type=VDIR) then
  begin
   error:=EISDIR;
   goto _out;
  end;
 end;
 if (fvp=tdvp) then
 begin
  error:=EINVAL;
  goto _out;
 end;
 {
  * If the source is the same as the destination (that is, if they
  * are links to the same vnode), then there is nothing to do.
  }
 if (fvp=tvp) then
 begin
  error:=-1;
 end;

 //else
 // error:=mac_vnode_check_rename_to(td^.td_ucred, tdvp, tond.ni_vp, fromnd.ni_dvp=tdvp, @tond.ni_cnd);

 _out:
 if (error=0) then
 begin
  error:=VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, @fromnd.ni_cnd, tond.ni_dvp, tond.ni_vp, @tond.ni_cnd);
  NDFREE(@fromnd, NDF_ONLY_PNBUF);
  NDFREE(@tond, NDF_ONLY_PNBUF);
 end else
 begin
  NDFREE(@fromnd, NDF_ONLY_PNBUF);
  NDFREE(@tond, NDF_ONLY_PNBUF);

  if (tvp<>nil) then
  begin
   vput(tvp);
  end;

  if (tdvp=tvp) then
   vrele(tdvp)
  else
   vput(tdvp);

  vrele(fromnd.ni_dvp);
  vrele(fvp);
 end;
 vrele(tond.ni_startdir);
 vn_finished_write(mp);
out1:
 if (fromnd.ni_startdir<>nil) then
 begin
  vrele(fromnd.ni_startdir);
 end;

 VFS_UNLOCK_GIANT(fvfslocked);
 VFS_UNLOCK_GIANT(tvfslocked);
 if (error=-1) then Exit(0);

 Exit(error);
end;

function kern_rename(from,_to:PChar;pathseg:uio_seg):Integer;
begin
 Exit(kern_renameat(AT_FDCWD, from, AT_FDCWD, _to, pathseg));
end;

{
 * Rename files.  Source and destination must either both be directories, or
 * both not be directories.  If target is a directory, it must be empty.
 }
function sys_rename(from,_to:PChar):Integer;
begin
 Exit(kern_rename(from, _to, UIO_USERSPACE));
end;

function sys_renameat(oldfd:Integer;old:PChar;newfd:Integer;new:PChar):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_renameat(oldfd, old, newfd, new, UIO_USERSPACE));
end;

function kern_mkdirat(fd:Integer;path:PChar;segflg:uio_seg;mode:Integer):Integer;
label
 restart,
 _out;
var
 mp:p_mount;
 vp:p_vnode;
 vattr:t_vattr;
 error:Integer;
 nd:t_nameidata;
 vfslocked:Integer;
begin
restart:
 //bwillwrite();
 NDINIT_ATRIGHTS(@nd, CREATE, LOCKPARENT or SAVENAME or MPSAFE or
     AUDITVNODE1, segflg, path, fd, CAP_MKDIR, curkthread);
 nd.ni_cnd.cn_flags:=nd.ni_cnd.cn_flags or WILLBEDIR;

 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  Exit(error);
 end;

 vfslocked:=NDHASGIANT(@nd);
 vp:=nd.ni_vp;
 if (vp<>nil) then
 begin
  NDFREE(@nd, NDF_ONLY_PNBUF);
  {
   * XXX namei called with LOCKPARENT but not LOCKLEAF has
   * the strange behaviour of leaving the vnode unlocked
   * if the target is the same vnode as the parent.
   }
  if (vp=nd.ni_dvp) then
   vrele(nd.ni_dvp)
  else
   vput(nd.ni_dvp);

  vrele(vp);
  VFS_UNLOCK_GIANT(vfslocked);
  Exit(EEXIST);
 end;
 if (vn_start_write(nd.ni_dvp, @mp, V_NOWAIT)<>0) then
 begin
  NDFREE(@nd, NDF_ONLY_PNBUF);
  vput(nd.ni_dvp);
  VFS_UNLOCK_GIANT(vfslocked);
  error:=vn_start_write(nil, @mp, V_XSLEEP or PCATCH);
  if (error<>0) then
  begin
   Exit(error);
  end;

  goto restart;
 end;
 VATTR_NULL(@vattr);
 vattr.va_type:=VDIR;
 vattr.va_mode:=(mode and ACCESSPERMS) and (not fd_table.fd_cmask);

 //error:=mac_vnode_check_create(td^.td_ucred, nd.ni_dvp, @nd.ni_cnd, @vattr);
 //if (error)
 // goto out;

 error:=VOP_MKDIR(nd.ni_dvp, @nd.ni_vp, @nd.ni_cnd, @vattr);
_out:
 NDFREE(@nd, NDF_ONLY_PNBUF);
 vput(nd.ni_dvp);
 if (error=0) then
 begin
  vput(nd.ni_vp);
 end;

 vn_finished_write(mp);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

function kern_mkdir(path:PChar;segflg:uio_seg;mode:Integer):Integer;
begin
 Exit(kern_mkdirat(AT_FDCWD, path, segflg, mode));
end;

{
 * Make a directory file.
 }
function sys_mkdir(path:PChar;mode:Integer):Integer;
begin
 Exit(kern_mkdir(path, UIO_USERSPACE, mode));
end;

function sys_mkdirat(fd:Integer;path:PChar;mode:Integer):Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 Exit(kern_mkdirat(fd, path, UIO_USERSPACE, mode));
end;

function kern_rmdirat(fd:Integer;path:PChar;pathseg:uio_seg):Integer;
label
 restart,
 _out;
var
 mp:p_mount;
 vp:p_vnode;
 error:Integer;
 nd:t_nameidata;
 vfslocked:Integer;
begin
restart:
 //bwillwrite();
 NDINIT_ATRIGHTS(@nd, DELETE, LOCKPARENT or LOCKLEAF or MPSAFE or
     AUDITVNODE1, pathseg, path, fd, CAP_RMDIR, curkthread);

 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  Exit(error);
 end;

 vfslocked:=NDHASGIANT(@nd);
 vp:=nd.ni_vp;
 if (vp^.v_type<>VDIR) then
 begin
  error:=ENOTDIR;
  goto _out;
 end;
 {
  * No rmdir '.' please.
  }
 if (nd.ni_dvp=vp) then
 begin
  error:=EINVAL;
  goto _out;
 end;
 {
  * The root of a mounted filesystem cannot be deleted.
  }
 if ((vp^.v_vflag and VV_ROOT)<>0) then
 begin
  error:=EBUSY;
  goto _out;
 end;

 //error:=mac_vnode_check_unlink(td^.td_ucred, nd.ni_dvp, vp, @nd.ni_cnd);
 //if (error<>0) then
 // goto _out;

 if (vn_start_write(nd.ni_dvp, @mp, V_NOWAIT)<>0) then
 begin
  NDFREE(@nd, NDF_ONLY_PNBUF);
  vput(vp);

  if (nd.ni_dvp=vp) then
   vrele(nd.ni_dvp)
  else
   vput(nd.ni_dvp);

  VFS_UNLOCK_GIANT(vfslocked);
  error:=vn_start_write(nil, @mp, V_XSLEEP or PCATCH);
  if (error<>0) then
  begin
   Exit(error);
  end;

  goto restart;
 end;
 error:=VOP_RMDIR(nd.ni_dvp, nd.ni_vp, @nd.ni_cnd);
 vn_finished_write(mp);
 _out:
 NDFREE(@nd, NDF_ONLY_PNBUF);
 vput(vp);

 if (nd.ni_dvp=vp) then
  vrele(nd.ni_dvp)
 else
  vput(nd.ni_dvp);

 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

function kern_rmdir(path:PChar;pathseg:uio_seg):Integer;
begin
 Exit(kern_rmdirat(AT_FDCWD, path, pathseg));
end;

{
 * Remove a directory file.
 }
function sys_rmdir(path:PChar):Integer;
begin
 Exit(kern_rmdir(path, UIO_USERSPACE));
end;

function kern_getdirentries(fd:Integer;buf:Pointer;count:DWORD;basep:PInt64):Integer;
label
 unionread,
 fail;
var
 td:p_kthread;
 vp:p_vnode;
 fp:p_file;
 auio:t_uio;
 aiov:iovec;
 vfslocked:Integer;
 loff:Int64;
 error,eofflag:Integer;
 foffset:Int64;
 tvp:p_vnode;
begin
 td:=curkthread;
 if (td=nil) then Exit(-1);

 auio.uio_resid:=count;

 if (auio.uio_resid > IOSIZE_MAX) then
 begin
  Exit(EINVAL);
 end;

 error:=getvnode(fd, CAP_READ or CAP_SEEK, @fp);
 if (error<>0) then
 begin
  Exit(error);
 end;

 if ((fp^.f_flag and FREAD)=0) then
 begin
  fdrop(fp);
  Exit(EBADF);
 end;
 vp:=fp^.f_vnode;
 foffset:=foffset_lock(fp, 0);
unionread:
 vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
 if (vp^.v_type<>VDIR) then
 begin
  VFS_UNLOCK_GIANT(vfslocked);
  error:=EINVAL;
  goto fail;
 end;
 aiov.iov_base  :=buf;
 aiov.iov_len   :=count;
 auio.uio_iov   :=@aiov;
 auio.uio_iovcnt:=1;
 auio.uio_rw    :=UIO_READ;
 auio.uio_segflg:=UIO_USERSPACE;
 auio.uio_td    :=td;
 vn_lock(vp, LK_SHARED or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});

 loff:=foffset;
 auio.uio_offset:=foffset;

 //error:=mac_vnode_check_readdir(td^.td_ucred, vp);
 //if (error=0) then
 begin
  error:=VOP_READDIR(vp, @auio, @eofflag, nil, nil);
 end;

 foffset:=auio.uio_offset;
 if (error<>0) then
 begin
  VOP_UNLOCK(vp, 0);
  VFS_UNLOCK_GIANT(vfslocked);
  goto fail;
 end;
 if (count=auio.uio_resid) and
    ((vp^.v_vflag and VV_ROOT)<>0) and
    ((p_mount(vp^.v_mount)^.mnt_flag and MNT_UNION)<>0) then
 begin
  tvp:=vp;
  vp:=p_mount(vp^.v_mount)^.mnt_vnodecovered;
  VREF(vp);
  fp^.f_vnode:=vp;
  fp^.f_data:=vp;
  foffset:=0;
  vput(tvp);
  VFS_UNLOCK_GIANT(vfslocked);
  goto unionread;
 end;
 VOP_UNLOCK(vp, 0);
 VFS_UNLOCK_GIANT(vfslocked);
 basep^:=loff;
 td^.td_retval[0]:=count - auio.uio_resid;
fail:
 foffset_unlock(fp, foffset, 0);
 fdrop(fp);
 Exit(error);
end;

{
 * Read a block of directory entries in a filesystem independent format.
 }
function sys_getdirentries(fd:Integer;buf:Pointer;count:DWORD;basep:PInt64):Integer;
var
 base:Int64;
 error:Integer;
begin
 error:=kern_getdirentries(fd, buf, count, @base);
 if (error<>0) then
 begin
  Exit(error);
 end;

 if (basep<>nil) then
 begin
  error:=copyout(@base, basep, sizeof(Int64));
 end;

 Exit(error);
end;

function sys_getdents(fd:Integer;buf:Pointer;count:DWORD):Integer;
begin
 Exit(sys_getdirentries(fd,buf,count,nil));
end;

{
 * Set the mode mask for creation of filesystem nodes.
 }
function sys_umask(newmask:Integer):Integer;
var
 td:p_kthread;
begin
 //priv_check(td,683);
 Exit(EPERM);

 td:=curkthread;
 if (td=nil) then Exit(-1);

 FILEDESC_XLOCK(@fd_table);
 td^.td_retval[0]:=fd_table.fd_cmask;
 fd_table.fd_cmask:=newmask and ALLPERMS;
 FILEDESC_XUNLOCK(@fd_table);
 Exit(0);
end;

{
 * Void all references to file by ripping underlying filesystem away from
 * vnode.
 }
function sys_revoke(path:PChar):Integer;
label
 _out;
var
 vp:p_vnode;
 vattr:t_vattr;
 error:Integer;
 nd:t_nameidata;
 vfslocked:Integer;
begin
 //priv_check(td,683);
 Exit(EPERM);

 NDINIT(@nd, LOOKUP, FOLLOW or LOCKLEAF or MPSAFE or AUDITVNODE1,
     UIO_USERSPACE, path, curkthread);

 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  Exit(error);
 end;

 vfslocked:=NDHASGIANT(@nd);
 vp:=nd.ni_vp;
 NDFREE(@nd, NDF_ONLY_PNBUF);

 if (vp^.v_type<>VCHR) or (vp^.v_rdev=nil) then
 begin
  error:=EINVAL;
  goto _out;
 end;

 //error:=mac_vnode_check_revoke(td^.td_ucred, vp);
 //if (error<>0) then
 // goto _out;

 error:=VOP_GETATTR(vp, @vattr);
 if (error<>0) then
 begin
  goto _out;
 end;
 //if (td^.td_ucred^.cr_uid<>vattr.va_uid) then
 //begin
 // error:=priv_check(td, PRIV_VFS_ADMIN);
 // if (error<>0) then
 //  goto _out;
 //end;
 if (vcount(vp) > 1) then
 begin
  VOP_REVOKE(vp, REVOKEALL);
 end;
_out:
 vput(vp);
 VFS_UNLOCK_GIANT(vfslocked);
 Exit(error);
end;

{
 * Convert a user file descriptor to a kernel file entry and check that, if it
 * is a capability, the correct rights are present. A reference on the file
 * entry is held upon Exiting.
 }
function getvnode(fd:Integer;rights:cap_rights_t;fpp:pp_file):Integer;
var
 fp:p_file;
 fp_fromcap:p_file;
 error:Integer;
begin
 error:=0;
 fp:=fget_unlocked(fd);
 if (fp=nil) then
 begin
  Exit(EBADF);
 end;

 {
  * If the file descriptor is for a capability, test rights and use the
  * file descriptor referenced by the capability.
  }
 error:=cap_funwrap(fp, rights, @fp_fromcap);
 if (error<>0) then
 begin
  fdrop(fp);
  Exit(error);
 end;
 if (fp<>fp_fromcap) then
 begin
  fhold(fp_fromcap);
  fdrop(fp);
  fp:=fp_fromcap;
 end;

 {
  * The file could be not of the vnode type, or it may be not
  * yet fully initialized, in which case the f_vnode pointer
  * may be set, but f_ops is still badfileops.  E.g.,
  * devfs_open() transiently create such situation to
  * facilitate csw d_fdopen().
  *
  * Dupfdopen() handling in kern_openat() installs the
  * half-baked file into the process descriptor table, allowing
  * other thread to dereference it. Guard against the race by
  * checking f_ops.
  }
 if (fp^.f_vnode=nil) or (fp^.f_ops=@badfileops) then
 begin
  fdrop(fp);
  Exit(EINVAL);
 end;
 fpp^:=fp;
 Exit(0);
end;

//////////

function sys_is_in_sandbox():Integer;
var
 td:p_kthread;
begin
 td:=curkthread;
 if (td=nil) then Exit(-1);

 td^.td_retval[0]:=1; //only in sandbox
 Result:=0;
end;

function sys_randomized_path(src,dst:pchar;plen:PQWORD):Integer;
type
 t_data=array[0..255] of AnsiChar;
var
 ran_len:Integer;
 dst_len:QWORD;
 data:t_data;
begin
 Result:=0;
 data:=Default(t_data);
 dst_len:=0;

 if (src=nil) then
 begin
  if (dst=nil) or (plen=nil) then
  begin
   //
  end else
  begin
   dst_len:=fuword64(plen^);
  end;
 end else
 begin
  //priv_check(td,0x2af);
  Exit(EPERM);
 end;

 MoveChar0(p_proc.p_randomized_path,data,Length(p_proc.p_randomized_path));
 ran_len:=StrLen(@data);

 if (0 < dst_len) and (0 < ran_len) then
 begin
  if (dst_len < ran_len) then
  begin
   Result:=copyout(@data,dst,dst_len);
  end else
  begin
   Result:=copyout(@data,dst,ran_len);
  end;
  if (Result<>0) then Exit;
 end;

 if (plen<>nil) then
 begin
  dst_len:=ran_len;
  Result:=suword64(plen^,dst_len);
 end;
end;



end.

