mirror of https://github.com/red-prig/fpPS4.git
3520 lines
73 KiB
Plaintext
3520 lines
73 KiB
Plaintext
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.
|
|
|