diff --git a/sys/kern/kern_evf.pas b/sys/kern/kern_evf.pas
index 3cdb2aea..33a89972 100644
--- a/sys/kern/kern_evf.pas
+++ b/sys/kern/kern_evf.pas
@@ -575,14 +575,11 @@ var
begin
Result:=ESRCH;
- evf:=p_evf(id_get(@evf_table,key));
- if (evf=nil) then Exit;
+ if not id_del(@evf_table,key,@evf) then Exit;
evf_delete(evf);
id_release(@evf^.desc);
- if not id_del(@evf_table,key) then Exit;
-
Result:=0;
end;
diff --git a/sys/kern/kern_id.pas b/sys/kern/kern_id.pas
index cd82effb..e403664b 100644
--- a/sys/kern/kern_id.pas
+++ b/sys/kern/kern_id.pas
@@ -36,9 +36,9 @@ procedure id_table_init(t:p_id_desc_table;min:Integer);
procedure id_table_fini(t:p_id_desc_table);
function id_new(t:p_id_desc_table;d:p_id_desc;pKey:PInteger):Boolean;
-function id_set(t:p_id_desc_table;d:p_id_desc;Key:Integer):Boolean;
+function id_set(t:p_id_desc_table;d:p_id_desc;Key:Integer;old:PPointer):Boolean;
function id_get(t:p_id_desc_table;Key:Integer):p_id_desc;
-function id_del(t:p_id_desc_table;Key:Integer):Boolean;
+function id_del(t:p_id_desc_table;Key:Integer;old:PPointer):Boolean;
implementation
@@ -140,7 +140,7 @@ begin
rw_wunlock(t^.FLock);
end;
-function id_set(t:p_id_desc_table;d:p_id_desc;Key:Integer):Boolean;
+function id_set(t:p_id_desc_table;d:p_id_desc;Key:Integer;old:PPointer):Boolean;
Label
_exit;
Var
@@ -154,10 +154,23 @@ begin
data:=HAMT_insert32(@t^.FHAMT,Key,Pointer(d));
if (data=nil) then goto _exit;
- if (data^<>Pointer(d)) then goto _exit;
- Inc(t^.FCount);
- id_acqure(d);
+ if (data^<>Pointer(d)) then
+ begin
+ if (old<>nil) then
+ begin
+ old^:=data^;
+ data^:=Pointer(d);
+ end else
+ begin
+ goto _exit;
+ end;
+ end else
+ if (old<>nil) then
+ begin
+ old^:=nil;
+ end;
+
id_acqure(d);
Result:=True;
@@ -190,7 +203,7 @@ begin
rw_runlock(t^.FLock);
end;
-function id_del(t:p_id_desc_table;Key:Integer):Boolean;
+function id_del(t:p_id_desc_table;Key:Integer;old:PPointer):Boolean;
Var
d:p_id_desc;
begin
@@ -203,9 +216,19 @@ begin
if (d<>nil) then
begin
- id_release(d);
+ if (old<>nil) then
+ begin
+ old^:=d;
+ end else
+ begin
+ id_release(d);
+ end;
Dec(t^.FCount);
Result:=True;
+ end else
+ if (old<>nil) then
+ begin
+ old^:=nil;
end;
rw_wunlock(t^.FLock);
diff --git a/sys/kern/kern_osem.pas b/sys/kern/kern_osem.pas
index 5bca05f3..7619de8c 100644
--- a/sys/kern/kern_osem.pas
+++ b/sys/kern/kern_osem.pas
@@ -427,14 +427,11 @@ var
begin
Result:=ESRCH;
- sem:=p_osem(id_get(@osem_table,key));
- if (sem=nil) then Exit;
+ if not id_del(@osem_table,key,@sem) then Exit;
osem_delete(sem);
id_release(@sem^.desc);
- if not id_del(@osem_table,key) then Exit;
-
Result:=0;
end;
diff --git a/sys/kern/kern_thr.pas b/sys/kern/kern_thr.pas
index 84ec6a4e..76bd9593 100644
--- a/sys/kern/kern_thr.pas
+++ b/sys/kern/kern_thr.pas
@@ -92,6 +92,9 @@ const
THR_SUSPENDED =$0001;
+ // These flags are kept in p_flag.
+ P_ADVLOCK =$00001; // Process may hold a POSIX advisory lock.
+
type
p_teb=^teb;
teb=packed record
@@ -143,6 +146,7 @@ type
td_sqqueue :Integer;
td_intrval :Integer;
td_inhibitors :Integer;
+ td_dupfd :Integer;
td_timeo :Int64;
//
td_map_def_user :Pointer;
diff --git a/sys/kern/systm.pas b/sys/kern/systm.pas
index ddf6e66a..91dc4da4 100644
--- a/sys/kern/systm.pas
+++ b/sys/kern/systm.pas
@@ -8,6 +8,10 @@ interface
uses
ntapi;
+const
+ IOSIZE_MAX =High(Int64);
+ DEVFS_IOSIZE_MAX=High(Int64);
+
function copyin(udaddr,kaddr:Pointer;len:ptruint):Integer; inline;
function copyinstr(udaddr,kaddr:Pointer;len:ptruint;lencopied:pptruint):Integer;
function copyout(kaddr,udaddr:Pointer;len:ptruint):Integer; inline;
diff --git a/sys/test/project1.lpi b/sys/test/project1.lpi
index 44abd629..b193b911 100644
--- a/sys/test/project1.lpi
+++ b/sys/test/project1.lpi
@@ -337,6 +337,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sys/test/project1.lpr b/sys/test/project1.lpr
index 9db93a10..f5c03ccb 100644
--- a/sys/test/project1.lpr
+++ b/sys/test/project1.lpr
@@ -43,7 +43,8 @@ uses
vfs_subr,
vfs_mount,
vfs_default,
- sysent;
+ sysent,
+ vfs_syscalls;
var
mtx:umutex;
diff --git a/sys/time.pas b/sys/time.pas
index 1d33a2a2..9b29a15c 100644
--- a/sys/time.pas
+++ b/sys/time.pas
@@ -54,6 +54,10 @@ type
tz_dstsec :DWORD;
end;
+function timespeccmp_lt(tvp,uvp:ptimespec):Integer; inline;
+
+procedure TIMEVAL_TO_TIMESPEC(tv:ptimeval;ts:ptimespec); inline;
+procedure TIMESPEC_TO_TIMEVAL(tv:ptimeval;ts:ptimespec); inline;
function TIMESPEC_TO_UNIT(ts:ptimespec):Int64; inline; //Unit
function tvtohz(time:Int64):Int64; inline;
procedure usec2timespec(ts:ptimespec;timeo:DWORD);
@@ -71,6 +75,29 @@ uses
thr_error,
kern_time;
+function timespeccmp_lt(tvp,uvp:ptimespec):Integer; inline;
+begin
+ if (tvp^.tv_sec=uvp^.tv_sec) then
+ begin
+ Result:=ord(tvp^.tv_nsec < uvp^.tv_nsec);
+ end else
+ begin
+ Result:=ord(tvp^.tv_sec < uvp^.tv_sec);
+ end;
+end;
+
+procedure TIMEVAL_TO_TIMESPEC(tv:ptimeval;ts:ptimespec); inline;
+begin
+ ts^.tv_sec :=tv^.tv_sec;
+ ts^.tv_nsec:=tv^.tv_usec * 1000;
+end;
+
+procedure TIMESPEC_TO_TIMEVAL(tv:ptimeval;ts:ptimespec); inline;
+begin
+ tv^.tv_sec :=ts^.tv_sec;
+ tv^.tv_usec:=ts^.tv_nsec div 1000;
+end;
+
function TIMESPEC_TO_UNIT(ts:ptimespec):Int64; inline; //Unit
begin
Result:=(QWORD(ts^.tv_sec)*10000000)+(QWORD(ts^.tv_nsec) div 100);
diff --git a/sys/vfs/kern_descrip.pas b/sys/vfs/kern_descrip.pas
index 4534979b..f3ab2e76 100644
--- a/sys/vfs/kern_descrip.pas
+++ b/sys/vfs/kern_descrip.pas
@@ -13,15 +13,29 @@ uses
vuio,
vcapability,
vfcntl,
+ vfilio,
+ vmount,
vfs_vnode;
const
FGET_GETCAP=$00000001;
+function sys_getdtablesize():Integer;
+function sys_dup2(from,_to:Integer):Integer;
+function sys_dup(u_fd:Integer):Integer;
+function sys_fcntl(fd,cmd:Integer;arg:QWORD):Integer;
+function sys_close(fd:Integer):Integer;
+function sys_fstat(fd:Integer;sb:p_stat):Integer;
+function sys_fpathconf(fd,name:Integer):Integer;
+function sys_flock(fd,how:Integer):Integer;
+
+function fget_unlocked(fd:Integer):p_file;
+function falloc_noinstall(resultfp:pp_file):Integer;
+function finstall(fp:p_file;fd:PInteger;flags:Integer):Integer;
function falloc(resultfp:pp_file;resultfd:PInteger;flags:Integer):Integer;
procedure fhold(fp:p_file);
-procedure fdrop(fp:p_file);
+function fdrop(fp:p_file):Integer;
function fget(fd:Integer;
rights:cap_rights_t;
@@ -69,13 +83,55 @@ function fgetsock(fd:Integer;
spp:PPointer; //socket **
fflagp:PDWORD):Integer;
+procedure fdclose(fp:p_file;idx:Integer);
+function dupfdopen(indx,dfd,mode,error:Integer):Integer;
+procedure finit(fp:p_file;flag:DWORD;_type:Word;data:Pointer;ops:p_fileops);
+
+function badfo_readwrite(fp:p_file;uio:p_uio;flags:Integer):Integer;
+function badfo_truncate(fp:p_file;length:Int64):Integer;
+function badfo_ioctl(fp:p_file;com:QWORD;data:Pointer):Integer;
+function badfo_poll(fp:p_file;events:Integer):Integer;
+function badfo_kqfilter(fp:p_file;kn:Pointer):Integer;
+function badfo_stat(fp:p_file;sb:p_stat):Integer;
+function badfo_close(fp:p_file):Integer;
+function badfo_chmod(fp:p_file;mode:mode_t):Integer;
+function badfo_chown(fp:p_file;uid:uid_t;gid:gid_t):Integer;
+
+const
+ badfileops:fileops=(
+ fo_read :@badfo_readwrite;
+ fo_write :@badfo_readwrite;
+ fo_truncate:@badfo_truncate;
+ fo_ioctl :@badfo_ioctl;
+ fo_poll :@badfo_poll;
+ fo_kqfilter:@badfo_kqfilter;
+ fo_stat :@badfo_stat;
+ fo_close :@badfo_close;
+ fo_chmod :@badfo_chmod;
+ fo_chown :@badfo_chown;
+ fo_flags :0
+ );
+
+var
+ p_leader:packed record
+ p_flag:Integer;
+ end;
+
+procedure atomic_set_int(addr:PInteger;val:Integer); sysv_abi_default;
+procedure atomic_clear_int(addr:PInteger;val:Integer); sysv_abi_default;
+
implementation
uses
errno,
+ systm,
vfiledesc,
sys_capability,
- vfs_subr;
+ vfs_subr,
+ vfs_vnops,
+ vnode_if,
+ kern_resource,
+ kern_mtx;
function badfo_readwrite(fp:p_file;uio:p_uio;flags:Integer):Integer;
begin
@@ -122,20 +178,987 @@ begin
Exit(EBADF);
end;
+{
+ * System calls on descriptors.
+ }
+function sys_getdtablesize():Integer;
+begin
+ curkthread^.td_retval[0]:=lim_cur(RLIMIT_NOFILE);
+ Exit(0);
+end;
+
+// Flags for do_dup()
const
- badfileops:fileops=(
- fo_read :@badfo_readwrite;
- fo_write :@badfo_readwrite;
- fo_truncate:@badfo_truncate;
- fo_ioctl :@badfo_ioctl;
- fo_poll :@badfo_poll;
- fo_kqfilter:@badfo_kqfilter;
- fo_stat :@badfo_stat;
- fo_close :@badfo_close;
- fo_chmod :@badfo_chmod;
- fo_chown :@badfo_chown;
- fo_flags :0
- );
+ DUP_FIXED =$1; // Force fixed allocation
+ DUP_FCNTL =$2; // fcntl()-style errors
+ DUP_CLOEXEC=$4; // Atomically set FD_CLOEXEC
+
+function do_dup(flags,old,new:Integer;retval:PQWORD):Integer; forward;
+
+{
+ * Duplicate a file descriptor to a particular value.
+ *
+ * Note: keep in mind that a potential race condition exists when closing
+ * descriptors from a shared descriptor table (via rfork).
+ }
+function sys_dup2(from,_to:Integer):Integer;
+begin
+ Exit(do_dup(DUP_FIXED, from, _to, @curkthread^.td_retval));
+end;
+
+{
+ * Duplicate a file descriptor.
+ }
+function sys_dup(u_fd:Integer):Integer;
+begin
+ Exit(do_dup(0, u_fd, 0, @curkthread^.td_retval));
+end;
+
+function kern_fcntl(fd,cmd:Integer;arg:QWORD):Integer; forward;
+
+{
+ * The file control system call.
+ }
+function sys_fcntl(fd,cmd:Integer;arg:QWORD):Integer;
+var
+ fl:t_flock;
+ ofl:__oflock;
+ error:Integer;
+begin
+ error:=0;
+ case cmd of
+ F_OGETLK,
+ F_OSETLK,
+ F_OSETLKW:
+ begin
+ {
+ * Convert old flock structure to new.
+ }
+ error:=copyin(Pointer(arg), @ofl, sizeof(ofl));
+ fl.l_start :=ofl.l_start;
+ fl.l_len :=ofl.l_len;
+ fl.l_pid :=ofl.l_pid;
+ fl.l_type :=ofl.l_type;
+ fl.l_whence:=ofl.l_whence;
+ fl.l_sysid :=0;
+
+ case cmd of
+ F_OGETLK :cmd:=F_GETLK;
+ F_OSETLK :cmd:=F_SETLK;
+ F_OSETLKW:cmd:=F_SETLKW;
+ end;
+ arg:=QWORD(@fl);
+ end;
+
+ F_GETLK,
+ F_SETLK,
+ F_SETLKW,
+ F_SETLK_REMOTE:
+ begin
+ error:=copyin(Pointer(arg), @fl, sizeof(fl));
+ arg:=QWORD(@fl);
+ end;
+ else;
+ end;
+ if (error<>0) then
+ Exit(error);
+ error:=kern_fcntl(fd, cmd, arg);
+ if (error<>0) then
+ Exit(error);
+ if (cmd=F_OGETLK) then
+ begin
+ ofl.l_start :=fl.l_start;
+ ofl.l_len :=fl.l_len;
+ ofl.l_pid :=fl.l_pid;
+ ofl.l_type :=fl.l_type;
+ ofl.l_whence:=fl.l_whence;
+ error:=copyout(@ofl, Pointer(arg), sizeof(ofl));
+ end else
+ if (cmd=F_GETLK) then
+ begin
+ error:=copyout(@fl, Pointer(arg), sizeof(fl));
+ end;
+ Exit(error);
+end;
+
+function fdunwrap(fd:Integer;rights:cap_rights_t;fpp:pp_file):Integer;
+var
+ err:Integer;
+begin
+
+ fpp^:=fget_unlocked(fd);
+ if (fpp^=nil) then
+ Exit(EBADF);
+
+ if (fpp^^.f_type=DTYPE_CAPABILITY) then
+ begin
+ err:=cap_funwrap(fpp^, rights, fpp);
+ if (err<>0) then
+ begin
+ fdrop(fpp^);
+ fpp^:=nil;
+ Exit(err);
+ end;
+ end;
+
+ Exit(0);
+end;
+
+procedure atomic_set_int(addr:PInteger;val:Integer); assembler; nostackframe; sysv_abi_default;
+asm
+ lock orl %esi,(%rdi)
+end;
+
+procedure atomic_clear_int(addr:PInteger;val:Integer); assembler; nostackframe; sysv_abi_default;
+asm
+ not %esi
+ lock andl %esi,(%rdi)
+end;
+
+function kern_fcntl(fd,cmd:Integer;arg:QWORD):Integer;
+label
+ _break,
+ do_setlk,
+ _break2,
+ do_readahead,
+ readahead_vnlock_fail;
+var
+ td:p_kthread;
+ flp:p_flock;
+ fp:p_file;
+ vp:p_vnode;
+ error,flg,tmp:Integer;
+ vfslocked:Integer;
+ old,new:DWORD;
+ bsize:QWORD;
+ foffset:Int64;
+begin
+ td:=curkthread;
+ vfslocked:=0;
+ error:=0;
+ flg:=F_POSIX;
+ fp:=nil;
+
+ case cmd of
+ F_DUPFD:
+ begin
+ tmp:=arg;
+ error:=do_dup(DUP_FCNTL, fd, tmp,@td^.td_retval);
+ end;
+
+ F_DUPFD_CLOEXEC:
+ begin
+ tmp:=arg;
+ error:=do_dup(DUP_FCNTL or DUP_CLOEXEC, fd, tmp,@td^.td_retval);
+ end;
+
+ F_DUP2FD:
+ begin
+ tmp:=arg;
+ error:=do_dup(DUP_FIXED, fd, tmp,@td^.td_retval);
+ end;
+
+ F_DUP2FD_CLOEXEC:
+ begin
+ tmp:=arg;
+ error:=do_dup(DUP_FIXED or DUP_CLOEXEC, fd, tmp,@td^.td_retval);
+ end;
+
+ F_GETFD:
+ begin
+ fp:=fget_unlocked(fd);
+ if (fp=nil) then
+ begin
+ error:=EBADF;
+ goto _break;
+ end;
+ if ((fp^.f_exclose and UF_EXCLOSE)<>0) then
+ begin
+ td^.td_retval[0]:=FD_CLOEXEC;
+ end else
+ begin
+ td^.td_retval[0]:=0;
+ end;
+ end;
+
+ F_SETFD:
+ begin
+ fp:=fget_unlocked(fd);
+ if (fp=nil) then
+ begin
+ error:=EBADF;
+ goto _break;
+ end;
+ if ((arg and FD_CLOEXEC)<>0) then
+ begin
+ fp^.f_exclose:=fp^.f_exclose or UF_EXCLOSE;
+ end else
+ begin
+ fp^.f_exclose:=(fp^.f_exclose and (not UF_EXCLOSE));
+ end;
+ end;
+
+ F_GETFL:
+ begin
+ error:=fdunwrap(fd, CAP_FCNTL, @fp);
+ if (error<>0) then
+ begin
+ goto _break;
+ end;
+ td^.td_retval[0]:=OFLAGS(fp^.f_flag);
+ end;
+
+ F_SETFL:
+ begin
+ error:=fdunwrap(fd, CAP_FCNTL, @fp);
+ if (error<>0) then
+ begin
+ goto _break;
+ end;
+ repeat
+ flg:=fp^.f_flag;
+ tmp:=flg;
+ tmp:=tmp and (not FCNTLFLAGS);
+ tmp:=tmp or FFLAGS(arg and (not O_ACCMODE)) and FCNTLFLAGS;
+ until (System.InterlockedCompareExchange(fp^.f_flag,tmp,flg)=flg);
+ tmp:=fp^.f_flag and FNONBLOCK;
+ error:=fo_ioctl(fp, FIONBIO, @tmp);
+ if (error<>0) then
+ begin
+ goto _break;
+ end;
+ tmp:=fp^.f_flag and FASYNC;
+ error:=fo_ioctl(fp, FIOASYNC, @tmp);
+ if (error=0) then
+ begin
+ goto _break;
+ end;
+ atomic_clear_int(@fp^.f_flag, FNONBLOCK);
+ tmp:=0;
+ fo_ioctl(fp, FIONBIO, @tmp);
+ end;
+
+ F_GETOWN:
+ begin
+ error:=fdunwrap(fd, CAP_FCNTL, @fp);
+ if (error<>0) then
+ begin
+ goto _break;
+ end;
+ error:=fo_ioctl(fp, FIOGETOWN, @tmp);
+ if (error=0) then
+ begin
+ td^.td_retval[0]:=tmp;
+ end;
+ end;
+
+ F_SETOWN:
+ begin
+ error:=fdunwrap(fd, CAP_FCNTL, @fp);
+ if (error<>0) then
+ begin
+ goto _break;
+ end;
+ tmp:=arg;
+ error:=fo_ioctl(fp, FIOSETOWN, @tmp);
+ end;
+
+ F_SETLK_REMOTE:
+ begin
+ error:=EPERM;
+ //error:=priv_check(td, PRIV_NFS_LOCKD);
+ if (error<>0) then
+ Exit(error);
+ flg:=F_REMOTE;
+ goto do_setlk;
+ end;
+
+ F_SETLKW:
+ begin
+ flg:=flg or F_WAIT;
+ goto do_setlk;
+ end;
+
+ F_SETLK:
+ begin
+ do_setlk:
+ error:=fdunwrap(fd, CAP_FLOCK, @fp);
+ if (error<>0) then
+ begin
+ goto _break;
+ end;
+ if (fp^.f_type<>DTYPE_VNODE) then
+ begin
+ error:=EBADF;
+ goto _break;
+ end;
+ flp:=p_flock(arg);
+ if (flp^.l_whence=SEEK_CUR) then
+ begin
+ FILEDESC_SLOCK(@fd_table);
+ foffset:=foffset_get(fp);
+ if (foffset < 0) or
+ ((flp^.l_start > 0) and
+ (foffset > High(Int64) - flp^.l_start)) then
+ begin
+ FILEDESC_SUNLOCK(@fd_table);
+ error:=EOVERFLOW;
+ goto _break;
+ end;
+ Inc(flp^.l_start,foffset);
+ FILEDESC_SUNLOCK(@fd_table);
+ end;
+
+ {
+ * VOP_ADVLOCK() may block.
+ }
+ vp:=fp^.f_vnode;
+ vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
+ case flp^.l_type of
+ F_RDLCK:
+ begin
+ if ((fp^.f_flag and FREAD)=0) then
+ begin
+ error:=EBADF;
+ goto _break2;
+ end;
+ PROC_LOCK();
+ p_leader.p_flag:=p_leader.p_flag or P_ADVLOCK;
+ PROC_UNLOCK();
+ error:=VOP_ADVLOCK(vp, @p_leader, F_SETLK, flp, flg);
+ end;
+ F_WRLCK:
+ begin
+ if ((fp^.f_flag and FWRITE)=0) then
+ begin
+ error:=EBADF;
+ goto _break2;
+ end;
+ PROC_LOCK();
+ p_leader.p_flag:=p_leader.p_flag or P_ADVLOCK;
+ PROC_UNLOCK();
+ error:=VOP_ADVLOCK(vp, @p_leader, F_SETLK, flp, flg);
+ end;
+ F_UNLCK:
+ begin
+ error:=VOP_ADVLOCK(vp, @p_leader, F_UNLCK, flp, flg);
+ end;
+ F_UNLCKSYS:
+ begin
+ {
+ * Temporary api for testing remote lock
+ * infrastructure.
+ }
+ if (flg<>F_REMOTE) then
+ begin
+ error:=EINVAL;
+ goto _break2;
+ end;
+ error:=VOP_ADVLOCK(vp, @p_leader, F_UNLCKSYS, flp, flg);
+ end;
+ else
+ error:=EINVAL;
+ end;
+ _break2:
+ VFS_UNLOCK_GIANT(vfslocked);
+ vfslocked:=0;
+ end;
+
+ F_GETLK:
+ begin
+ error:=fdunwrap(fd, CAP_FLOCK, @fp);
+ if (error<>0) then
+ begin
+ goto _break;
+ end;
+ if (fp^.f_type<>DTYPE_VNODE) then
+ begin
+ error:=EBADF;
+ goto _break;
+ end;
+ flp:=p_flock(arg);
+ if (flp^.l_type<>F_RDLCK) and
+ (flp^.l_type<>F_WRLCK) and
+ (flp^.l_type<>F_UNLCK) then
+ begin
+ error:=EINVAL;
+ goto _break;
+ end;
+ if (flp^.l_whence=SEEK_CUR) then
+ begin
+ FILEDESC_SLOCK(@fd_table);
+ foffset:=foffset_get(fp);
+ if ((flp^.l_start > 0) and
+ (foffset > High(Int64) - flp^.l_start)) or
+ ((flp^.l_start < 0) and
+ (foffset < Low(Int64) - flp^.l_start)) then
+ begin
+ FILEDESC_SUNLOCK(@fd_table);
+ error:=EOVERFLOW;
+ goto _break;
+ end;
+ Inc(flp^.l_start,foffset);
+ FILEDESC_SUNLOCK(@fd_table);
+ end;
+ {
+ * VOP_ADVLOCK() may block.
+ }
+ vp:=fp^.f_vnode;
+ vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
+ error:=VOP_ADVLOCK(vp, @p_leader, F_GETLK, flp, F_POSIX);
+ VFS_UNLOCK_GIANT(vfslocked);
+ vfslocked:=0;
+ end;
+
+ F_RDAHEAD:
+ begin
+ if (arg<>0) then
+ arg:=128 * 1024
+ else
+ arg:=0;
+ goto do_readahead;
+ end;
+ F_READAHEAD:
+ begin
+ do_readahead:
+ fp:=fget_unlocked(fd);
+ if (fp=nil) then
+ begin
+ error:=EBADF;
+ goto _break;
+ end;
+ if (fp^.f_type<>DTYPE_VNODE) then
+ begin
+ error:=EBADF;
+ goto _break;
+ end;
+ if (arg<>0) then
+ begin
+ vp:=fp^.f_vnode;
+ vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
+ error:=vn_lock(vp, LK_SHARED);
+ if (error<>0) then
+ goto readahead_vnlock_fail;
+ bsize:=p_mount(fp^.f_vnode^.v_mount)^.mnt_stat.f_iosize;
+ VOP_UNLOCK(vp, 0);
+ fp^.f_seqcount:=(arg + bsize - 1) div bsize;
+ repeat
+ old:=fp^.f_flag;
+ new:=old;
+ new:=new or FRDAHEAD;
+ until (System.InterlockedCompareExchange(fp^.f_flag,new,old)=old);
+ readahead_vnlock_fail:
+ VFS_UNLOCK_GIANT(vfslocked);
+ vfslocked:=0;
+ end else
+ begin
+ repeat
+ old:=fp^.f_flag;
+ new:=old;
+ new:=new and (not FRDAHEAD);
+ until (System.InterlockedCompareExchange(fp^.f_flag,new,old)=old);
+ end;
+ end;
+
+ else
+ error:=EINVAL;
+ end;
+ _break:
+
+ if (fp<>nil) then
+ begin
+ fdrop(fp);
+ end;
+
+ VFS_UNLOCK_GIANT(vfslocked);
+ Exit(error);
+end;
+
+function getmaxfd():Integer; inline;
+begin
+ Result:=lim_cur(RLIMIT_NOFILE);
+end;
+
+function closef(fp:p_file):Integer; forward;
+
+{
+ * Common code for dup, dup2, fcntl(F_DUPFD) and fcntl(F_DUP2FD).
+ }
+function do_dup(flags,old,new:Integer;retval:PQWORD):Integer;
+var
+ fp,delfp:p_file;
+ maxfd:Integer;
+begin
+ {
+ * Verify we have a valid descriptor to dup from and possibly to
+ * dup to. Unlike dup() and dup2(), fcntl()'s F_DUPFD should
+ * ExitEINVAL when the new descriptor is out of bounds.
+ }
+ if (old < 0) then
+ Exit(EBADF);
+
+ if (new < 0) then
+ begin
+ if (flags and DUP_FCNTL)<>0 then
+ Exit(EINVAL)
+ else
+ Exit(EBADF);
+ end;
+
+ maxfd:=getmaxfd();
+ if (new >= maxfd) then
+ begin
+ if (flags and DUP_FCNTL)<>0 then
+ Exit(EINVAL)
+ else
+ Exit(EBADF);
+ end;
+
+ if ((flags and DUP_FIXED)<>0) and (old=new) then
+ begin
+ retval^:=new;
+ if ((flags and DUP_CLOEXEC)<>0) then
+ begin
+ fp:=fget_unlocked(new);
+ if (fp=nil) then
+ begin
+ Exit(EBADF);
+ end;
+ fp^.f_exclose:=fp^.f_exclose or UF_EXCLOSE;
+ fdrop(fp);
+ end;
+ Exit(0);
+ end;
+
+ fp:=fget_unlocked(old);
+ if (fp=nil) then
+ begin
+ Exit(EBADF);
+ end;
+
+ Assert(old<>new,'new fd is same as old');
+
+ {
+ * If the caller specified a file descriptor, make sure the file
+ * table is large enough to hold it, and grab it. Otherwise, just
+ * allocate a new descriptor the usual way. Since the filedesc
+ * lock may be temporarily dropped in the process, we have to look
+ * out for a race.
+ }
+ delfp:=nil;
+ if ((flags and DUP_FIXED)<>0) then
+ begin
+ if not id_set(@fd_table.fd_ofiles,@fp^.desc,new,@delfp) then
+ begin
+ fdrop(fp);
+ Exit(EBADF);
+ end;
+ end else
+ begin
+ if not id_new(@fd_table.fd_ofiles,@fp^.desc,@new) then
+ begin
+ fdrop(fp);
+ Exit(ENFILE);
+ end;
+ fdrop(fp);
+ end;
+ fdrop(fp);
+
+
+ retval^:=new;
+
+ {
+ * If we dup'd over a valid file, we now own the reference to it
+ * and must dispose of it using closef() semantics (as if a
+ * close() were performed on it).
+ *
+ * XXX this duplicates parts of close().
+ }
+ if (delfp<>nil) then
+ begin
+ //knote_fdclose(td, new);
+ if (delfp^.f_type=DTYPE_MQUEUE) then
+ begin
+ //mq_fdclose(td, new, delfp);
+ end;
+ closef(delfp);
+ end;
+ Exit(0);
+end;
+
+function kern_close(fd:Integer):Integer;
+var
+ fp,fp_object:p_file;
+begin
+ fp:=nil;
+ if not id_del(@fd_table.fd_ofiles,fd,@fp) then
+ begin
+ Exit(EBADF);
+ end;
+
+ {
+ * We now hold the fp reference that used to be owned by the
+ * descriptor array. We have to unlock the FILEDESC *AFTER*
+ * knote_fdclose to prevent a race of the fd getting opened, a knote
+ * added, and deleteing a knote for the new fd.
+ }
+ //knote_fdclose(fd);
+
+ {
+ * When we're closing an fd with a capability, we need to notify
+ * mqueue if the underlying object is of type mqueue.
+ }
+ if (cap_funwrap(fp, 0, @fp_object)=0) then
+ begin
+ if (fp_object^.f_type=DTYPE_MQUEUE) then
+ begin
+ //mq_fdclose(td, fd, fp_object);
+ end;
+ fdrop(fp_object);
+ end;
+
+ Exit(closef(fp));
+end;
+
+{
+ * Close a file descriptor.
+ }
+function sys_close(fd:Integer):Integer;
+begin
+ Exit(kern_close(fd));
+end;
+
+{
+ * Internal form of close. Decrement reference count on file structure.
+ * Note: td may be nil when closing a file that was being passed in a
+ * message.
+ *
+ * XXXRW: Giant is not required for the caller, but often will be held; this
+ * makes it moderately likely the Giant will be recursed in the VFS case.
+ }
+function closef(fp:p_file):Integer;
+var
+ vp:p_vnode;
+ lf:t_flock;
+ fp_object:p_file;
+ vfslocked:Integer;
+begin
+ {
+ * POSIX record locking dictates that any close releases ALL
+ * locks owned by this process. This is handled by setting
+ * a flag in the unlock to free ONLY locks obeying POSIX
+ * semantics, and not to free BSD-style file locks.
+ * If the descriptor was in a message, POSIX-style locks
+ * aren't passed with the descriptor, and the thread pointer
+ * will be nil. Callers should be careful only to pass a
+ * nil thread pointer when there really is no owning
+ * context that might have locks, or the locks will be
+ * leaked.
+ *
+ * If this is a capability, we do lock processing under the underlying
+ * node, not the capability itself.
+ }
+ if (cap_funwrap(fp, 0, @fp_object)=0) then
+ begin
+ if (fp_object^.f_type=DTYPE_VNODE) then
+ begin
+ vp:=fp_object^.f_vnode;
+ vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
+ if ((p_leader.p_flag and P_ADVLOCK)<>0) then
+ begin
+ lf.l_whence:=SEEK_SET;
+ lf.l_start :=0;
+ lf.l_len :=0;
+ lf.l_type :=F_UNLCK;
+ VOP_ADVLOCK(vp, @p_leader, F_UNLCK, @lf, F_POSIX);
+ end;
+
+ VFS_UNLOCK_GIANT(vfslocked);
+ end;
+ fdrop(fp_object);
+ end;
+ Exit(fdrop(fp));
+end;
+
+{
+ * If a specific file object occupies a specific file descriptor, close the
+ * file descriptor entry and drop a reference on the file object. This is a
+ * convenience function to handle a subsequent error in a function that calls
+ * falloc() that handles the race that another thread might have closed the
+ * file descriptor out from under the thread creating the file object.
+}
+procedure fdclose(fp:p_file;idx:Integer);
+var
+ tmp:p_file;
+begin
+ tmp:=p_file(id_get(@fd_table.fd_ofiles,idx));
+ if (tmp=fp) then
+ begin
+ fdrop(tmp);
+ fdrop(tmp);
+ end else
+ if (tmp<>nil) then
+ begin
+ fdrop(tmp);
+ end;
+end;
+
+function kern_fstat(fd:Integer;sbp:p_stat):Integer;
+var
+ fp:p_file;
+ error:Integer;
+begin
+ error:=fget(fd, CAP_FSTAT, @fp);
+ if (error<>0) then
+ Exit(error);
+
+ error:=fo_stat(fp, sbp);
+ fdrop(fp);
+
+ Exit(error);
+end;
+
+{
+ * Exitstatus information about a file descriptor.
+ }
+function sys_fstat(fd:Integer;sb:p_stat):Integer;
+var
+ ub:t_stat;
+ error:Integer;
+begin
+ error:=kern_fstat(fd, @ub);
+ if (error=0) then
+ begin
+ error:=copyout(@ub, sb, sizeof(ub));
+ end;
+ Exit(error);
+end;
+
+{
+ * Exitpathconf information about a file descriptor.
+ }
+function sys_fpathconf(fd,name:Integer):Integer;
+label
+ _out;
+var
+ td:p_kthread;
+ fp:p_file;
+ vp:p_vnode;
+ error:Integer;
+ vfslocked:Integer;
+begin
+ td:=curkthread;
+
+ error:=fget(fd, CAP_FPATHCONF, @fp);
+ if (error<>0) then
+ Exit(error);
+
+ { If asynchronous I/O is available, it works for all descriptors. }
+ if (name=_PC_ASYNC_IO) then
+ begin
+ td^.td_retval[0]:=async_io_version;
+ goto _out;
+ end;
+ vp:=fp^.f_vnode;
+ if (vp<>nil) then
+ begin
+ vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
+ vn_lock(vp, LK_SHARED or LK_RETRY);
+ error:=VOP_PATHCONF(vp, name, td^.td_retval);
+ VOP_UNLOCK(vp, 0);
+ VFS_UNLOCK_GIANT(vfslocked);
+ end else
+ if (fp^.f_type=DTYPE_PIPE) or (fp^.f_type=DTYPE_SOCKET) then
+ begin
+ if (name<>_PC_PIPE_BUF) then
+ begin
+ error:=EINVAL;
+ end else
+ begin
+ td^.td_retval[0]:=PIPE_BUF;
+ error:=0;
+ end;
+ end else
+ begin
+ error:=EOPNOTSUPP;
+ end;
+_out:
+ fdrop(fp);
+ Exit(error);
+end;
+
+{
+ * Apply an advisory lock on a file descriptor.
+ *
+ * Just attempt to get a record lock of the requested type on the entire file
+ * (l_whence:=SEEK_SET, l_start:=0, l_len:=0).
+ }
+function sys_flock(fd,how:Integer):Integer;
+label
+ done2;
+var
+ fp:p_file;
+ vp:p_vnode;
+ lf:t_flock;
+ vfslocked:Integer;
+ error:Integer;
+begin
+
+ error:=fget(fd, CAP_FLOCK, @fp);
+ if (error<>0) then
+ Exit(error);
+ if (fp^.f_type<>DTYPE_VNODE) then
+ begin
+ fdrop(fp);
+ Exit(EOPNOTSUPP);
+ end;
+
+ vp:=fp^.f_vnode;
+ vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
+ lf.l_whence:=SEEK_SET;
+ lf.l_start:=0;
+ lf.l_len:=0;
+ if ((how and LOCK_UN)<>0) then
+ begin
+ lf.l_type:=F_UNLCK;
+ atomic_clear_int(@fp^.f_flag, FHASLOCK);
+ error:=VOP_ADVLOCK(vp, fp, F_UNLCK, @lf, F_FLOCK);
+ goto done2;
+ end;
+ if ((how and LOCK_EX)<>0) then
+ lf.l_type:=F_WRLCK
+ else
+ if ((how and LOCK_SH)<>0) then
+ lf.l_type:=F_RDLCK
+ else
+ begin
+ error:=EBADF;
+ goto done2;
+ end;
+ atomic_set_int(@fp^.f_flag, FHASLOCK);
+
+ if ((how and LOCK_NB)<>0) then
+ begin
+ error:=VOP_ADVLOCK(vp, fp, F_SETLK, @lf, F_FLOCK);
+ end else
+ begin
+ error:=VOP_ADVLOCK(vp, fp, F_SETLK, @lf, F_FLOCK or F_WAIT);
+ end;
+
+done2:
+ fdrop(fp);
+ VFS_UNLOCK_GIANT(vfslocked);
+ Exit(error);
+end;
+
+{
+ * Duplicate the specified descriptor to a free descriptor.
+ }
+function dupfdopen(indx,dfd,mode,error:Integer):Integer;
+var
+ wfp,fp:p_file;
+begin
+ {
+ * If the to-be-dup'd fd number is greater than the allowed number
+ * of file descriptors, or the fd to be dup'd has already been
+ * closed, then reject.
+ }
+
+ wfp:=fget_unlocked(dfd);
+ if (wfp=nil) then
+ begin
+ Exit(EBADF);
+ end;
+
+ {
+ * There are two cases of interest here.
+ *
+ * For ENODEV simply dup (dfd) to file descriptor (indx) and return.
+ *
+ * For ENXIO steal away the file structure from (dfd) and store it in
+ * (indx). (dfd) is effectively closed by this operation.
+ *
+ * Any other error code is just returned.
+ }
+ case error of
+ ENODEV:
+ begin
+ {
+ * Check that the mode the file is being opened for is a
+ * subset of the mode of the existing descriptor.
+ }
+ if (((mode and (FREAD or FWRITE)) or wfp^.f_flag)<>wfp^.f_flag) then
+ begin
+ fdrop(wfp);
+ Exit(EACCES);
+ end;
+
+ fp:=nil;
+ if not id_set(@fd_table.fd_ofiles,@wfp^.desc,indx,@fp) then
+ begin
+ fdrop(wfp);
+ Exit(EBADF);
+ end;
+
+ if (fp<>nil) then
+ begin
+ fdrop(fp);
+ end;
+
+ fdrop(wfp);
+ Exit(0);
+ end;
+
+ ENXIO:
+ begin
+ {
+ * Steal away the file pointer from dfd and stuff it into indx.
+ }
+ fp:=nil;
+ if not id_set(@fd_table.fd_ofiles,@wfp^.desc,indx,@fp) then
+ begin
+ fdrop(wfp);
+ Exit(EBADF);
+ end;
+
+ if not id_del(@fd_table.fd_ofiles,dfd,nil) then
+ begin
+ fdrop(wfp);
+ Exit(EBADF);
+ end;
+
+ if (fp<>nil) then
+ begin
+ fdrop(fp);
+ end;
+
+ fdrop(wfp);
+ Exit(0);
+ end;
+
+ else
+ begin
+ fdrop(wfp);
+ Exit(error);
+ end;
+ end;
+ { NOTREACHED }
+end;
+
+{
+ * Initialize the file pointer with the specified properties.
+ *
+ * The ops are set with release semantics to be certain that the flags, type,
+ * and data are visible when ops is. This is to prevent ops methods from being
+ * called with bad data.
+}
+procedure finit(fp:p_file;flag:DWORD;_type:Word;data:Pointer;ops:p_fileops);
+begin
+ fp^.f_data:=data;
+ fp^.f_flag:=flag;
+ fp^.f_type:=_type;
+ System.InterlockedExchange(fp^.f_ops,ops);
+end;
procedure _fdrop(data:pointer); forward;
@@ -169,7 +1192,10 @@ begin
Exit(ENFILE);
end;
- //if ((flags and O_CLOEXEC)<>0)
+ if ((flags and O_CLOEXEC)<>0) then
+ begin
+ fp^.f_exclose:=fp^.f_exclose or UF_EXCLOSE;
+ end;
Exit(0);
end;
@@ -223,9 +1249,10 @@ begin
id_acqure(@fp^.desc);
end;
-procedure fdrop(fp:p_file);
+function fdrop(fp:p_file):Integer;
begin
id_release(@fp^.desc);
+ Result:=0;
end;
function fget_unlocked(fd:Integer):p_file;
@@ -270,7 +1297,7 @@ begin
end;
{
- * If a capability has been requested, return the capability directly.
+ * If a capability has been requested, Exitthe capability directly.
* Otherwise, check capability rights, extract the underlying object,
* and check its access flags.
}
diff --git a/sys/vfs/sys_capability.pas b/sys/vfs/sys_capability.pas
index 77d6922a..b35d844f 100644
--- a/sys/vfs/sys_capability.pas
+++ b/sys/vfs/sys_capability.pas
@@ -6,11 +6,17 @@ unit sys_capability;
interface
uses
- errno,
vcapability,
vfile,
+ vuio,
+ vstat,
vm;
+function sys_cap_new():Integer;
+function sys_cap_getrights():Integer;
+function sys_cap_enter():Integer;
+function sys_cap_getmode():Integer;
+
{
* struct capability describes a capability, and is hung off of its struct
* file f_data field. cap_file and cap_rightss are static once hooked up, as
@@ -30,8 +36,78 @@ function cap_rights(fp_cap:p_file):cap_rights_t;
function cap_funwrap(fp_cap:p_file;rights:cap_rights_t;fpp:pp_file):Integer;
function cap_funwrap_mmap(fp_cap:p_file;rights:cap_rights_t;maxprotp:PByte;fpp:pp_file):Integer;
+function kern_capwrap(fp:p_file;rights:cap_rights_t;capfdp:PInteger):Integer;
+
+function capability_close(fp:p_file):Integer;
+function capability_read(fp:p_file;uio:p_uio;flags:Integer):Integer;
+function capability_write(fp:p_file;uio:p_uio;flags:Integer):Integer;
+function capability_truncate(fp:p_file;length:Int64):Integer;
+function capability_ioctl(fp:p_file;com:QWORD;data:Pointer):Integer;
+function capability_poll(fp:p_file;events:Integer):Integer;
+function capability_kqfilter(fp:p_file;kn:Pointer):Integer;
+function capability_stat(fp:p_file;sb:p_stat):Integer;
+function capability_chmod(fp:p_file;mode:mode_t):Integer;
+function capability_chown(fp:p_file;uid:uid_t;gid:gid_t):Integer;
+
+const
+ capability_ops:fileops=(
+ fo_read :@capability_read;
+ fo_write :@capability_write;
+ fo_truncate:@capability_truncate;
+ fo_ioctl :@capability_ioctl;
+ fo_poll :@capability_poll;
+ fo_kqfilter:@capability_kqfilter;
+ fo_stat :@capability_stat;
+ fo_close :@capability_close;
+ fo_chmod :@capability_chmod;
+ fo_chown :@capability_chown;
+ fo_flags :DFLAG_PASSABLE
+ );
+
+ capability_ops_unpassable:fileops=(
+ fo_read :@capability_read;
+ fo_write :@capability_write;
+ fo_truncate:@capability_truncate;
+ fo_ioctl :@capability_ioctl;
+ fo_poll :@capability_poll;
+ fo_kqfilter:@capability_kqfilter;
+ fo_stat :@capability_stat;
+ fo_close :@capability_close;
+ fo_chmod :@capability_chmod;
+ fo_chown :@capability_chown;
+ fo_flags :0
+ );
+
implementation
+uses
+ errno,
+ kern_descrip;
+
+{
+ * Stub Capability functions for when options CAPABILITIES isn't compiled
+ * into the kernel.
+ }
+function sys_cap_new():Integer;
+begin
+ Exit(ENOSYS);
+end;
+
+function sys_cap_getrights():Integer;
+begin
+ Exit(ENOSYS);
+end;
+
+function sys_cap_enter():Integer;
+begin
+ Exit(ENOSYS);
+end;
+
+function sys_cap_getmode():Integer;
+begin
+ Exit(ENOSYS);
+end;
+
function cap_check(c:p_capability;rights:cap_rights_t):Integer;
begin
if ((c^.cap_rights or rights)<>c^.cap_rights) then
@@ -105,5 +181,148 @@ begin
Exit(0);
end;
+{
+ * Create a capability to wrap around an existing file.
+ }
+function kern_capwrap(fp:p_file;rights:cap_rights_t;capfdp:PInteger):Integer;
+var
+ cp,cp_old:p_capability;
+ fp_object,fcapp:p_file;
+ error:Integer;
+begin
+ if ((rights or CAP_MASK_VALID)<>CAP_MASK_VALID) then
+ Exit(EINVAL);
+
+ {
+ * If a new capability is being derived from an existing capability,
+ * then the new capability rights must be a subset of the existing
+ * rights.
+ }
+ if (fp^.f_type=DTYPE_CAPABILITY) then
+ begin
+ cp_old:=fp^.f_data;
+ if ((cp_old^.cap_rights or rights)<>cp_old^.cap_rights) then
+ Exit(ENOTCAPABLE);
+ end;
+
+ {
+ * Allocate a new file descriptor to hang the capability off of.
+ }
+ error:=falloc(@fcapp, capfdp, fp^.f_flag);
+ if (error<>0) then
+ Exit(error);
+
+ {
+ * Rather than nesting capabilities, directly reference the object an
+ * existing capability references. There's nothing else interesting
+ * to preserve for future use, as we've incorporated the previous
+ * rights mask into the new one. This prevents us from having to
+ * deal with capability chains.
+ }
+ if (fp^.f_type=DTYPE_CAPABILITY) then
+ fp_object:=p_capability(fp^.f_data)^.cap_object
+ else
+ fp_object:=fp;
+ fhold(fp_object);
+ cp:=AllocMem(SizeOf(t_capability));
+ cp^.cap_rights:=rights;
+ cp^.cap_object:=fp_object;
+ cp^.cap_file:=fcapp;
+
+ if ((fp^.f_flag and DFLAG_PASSABLE)<>0) then
+ finit(fcapp, fp^.f_flag, DTYPE_CAPABILITY, cp, @capability_ops)
+ else
+ finit(fcapp, fp^.f_flag, DTYPE_CAPABILITY, cp, @capability_ops_unpassable);
+
+ {
+ * Release our private reference (the proc filedesc still has one).
+ }
+ fdrop(fcapp);
+ Exit(0);
+end;
+
+{
+ * When a capability is closed, simply drop the reference on the underlying
+ * object and free the capability. fdrop() will handle the case where the
+ * underlying object also needs to close, and the caller will have already
+ * performed any object-specific lock or mqueue handling.
+ }
+function capability_close(fp:p_file):Integer;
+var
+ c:p_capability;
+ fp_object:p_file;
+begin
+ Assert(fp^.f_type=DTYPE_CAPABILITY,('capability_close: !capability'));
+
+ c:=fp^.f_data;
+ fp^.f_ops:=@badfileops;
+ fp^.f_data:=nil;
+ fp_object:=c^.cap_object;
+ FreeMem(c);
+
+ Exit(fdrop(fp_object));
+end;
+
+{
+ * In general, file descriptor operations should never make it to the
+ * capability, only the underlying file descriptor operation vector, so panic
+ * if we do turn up here.
+ }
+function capability_read(fp:p_file;uio:p_uio;flags:Integer):Integer;
+begin
+ Assert(False,'capability_read');
+ Exit(ENOSYS);
+end;
+
+function capability_write(fp:p_file;uio:p_uio;flags:Integer):Integer;
+begin
+ Assert(False,'capability_write');
+ Exit(ENOSYS);
+end;
+
+function capability_truncate(fp:p_file;length:Int64):Integer;
+begin
+ Assert(False,'capability_truncate');
+ Exit(ENOSYS);
+end;
+
+function capability_ioctl(fp:p_file;com:QWORD;data:Pointer):Integer;
+begin
+ Assert(False,'capability_ioctl');
+ Exit(ENOSYS);
+end;
+
+function capability_poll(fp:p_file;events:Integer):Integer;
+begin
+ Assert(False,'capability_poll');
+ Exit(ENOSYS);
+end;
+
+function capability_kqfilter(fp:p_file;kn:Pointer):Integer;
+begin
+ Assert(False,'capability_kqfilter');
+ Exit(ENOSYS);
+end;
+
+function capability_stat(fp:p_file;sb:p_stat):Integer;
+begin
+ Assert(False,'capability_stat');
+ Exit(ENOSYS);
+end;
+
+function capability_chmod(fp:p_file;mode:mode_t):Integer;
+begin
+ Assert(False,'capability_chmod');
+ Exit(ENOSYS);
+end;
+
+function capability_chown(fp:p_file;uid:uid_t;gid:gid_t):Integer;
+begin
+ Assert(False,'capability_chown');
+ Exit(ENOSYS);
+end;
+
+
+
end.
diff --git a/sys/vfs/vdisk.pas b/sys/vfs/vdisk.pas
new file mode 100644
index 00000000..716224fe
--- /dev/null
+++ b/sys/vfs/vdisk.pas
@@ -0,0 +1,27 @@
+unit vdisk;
+
+{$mode ObjFPC}{$H+}
+{$CALLING SysV_ABI_CDecl}
+
+interface
+
+const
+ DIOCGSECTORSIZE =$40046480;
+ DIOCGMEDIASIZE =$40046481;
+ DIOCGFWSECTORS =$40046482;
+ DIOCGFWHEADS =$40046483;
+ DIOCSKERNELDUMP =$80046485;
+ DIOCGFRONTSTUFF =$40046486;
+ DIOCGFLUSH =$20006487;
+ DIOCGDELETE =$80086488;
+ DISK_IDENT_SIZE =256 ;
+ DIOCGIDENT =$41006489;
+ DIOCGPROVIDERNAME=$4400648A;
+ DIOCGSTRIPESIZE =$4004648B;
+ DIOCGSTRIPEOFFSET=$4004648C;
+ DIOCGPHYSPATH =$4400648D;
+
+implementation
+
+end.
+
diff --git a/sys/vfs/vfcntl.pas b/sys/vfs/vfcntl.pas
index d3fb2cc0..907c4fa1 100644
--- a/sys/vfs/vfcntl.pas
+++ b/sys/vfs/vfcntl.pas
@@ -100,13 +100,34 @@ const
{
* Constants used for fcntl(2)
}
-
{ command values }
- F_GETFL = 3; { get file status flags }
- F_SETFL = 4; { set file status flags }
- F_GETLK =11; { get record locking information }
- F_SETLK =12; { set record locking information }
- F_SETLKW=13; { F_SETLK; wait if blocked }
+ F_DUPFD = 0; { duplicate file descriptor }
+ F_GETFD = 1; { get file descriptor flags }
+ F_SETFD = 2; { set file descriptor flags }
+
+ F_GETFL = 3; { get file status flags }
+ F_SETFL = 4; { set file status flags }
+
+ F_GETOWN = 5; { get SIGIO/SIGURG proc/pgrp }
+ F_SETOWN = 6; { set SIGIO/SIGURG proc/pgrp }
+
+ F_OGETLK = 7; { get record locking information }
+ F_OSETLK = 8; { set record locking information }
+ F_OSETLKW= 9; { F_SETLK; wait if blocked }
+ F_DUP2FD =10; { duplicate file descriptor to arg }
+
+ F_GETLK =11; { get record locking information }
+ F_SETLK =12; { set record locking information }
+ F_SETLKW =13; { F_SETLK; wait if blocked }
+
+ F_SETLK_REMOTE =14; { debugging support for remote locks }
+ F_READAHEAD =15; { read ahead }
+ F_RDAHEAD =16; { Darwin compatible read ahead }
+ F_DUPFD_CLOEXEC =17; { Like F_DUPFD, but FD_CLOEXEC is set }
+ F_DUP2FD_CLOEXEC=18; { Like F_DUP2FD, but FD_CLOEXEC is set }
+
+{ file descriptor flags (F_GETFD, F_SETFD) }
+ FD_CLOEXEC=1; { close-on-exec flag }
{ record locking flags (F_GETLK, F_SETLK, F_SETLKW) }
F_RDLCK =1; { shared or read lock }
@@ -121,6 +142,12 @@ const
F_REMOTE=$080; { Lock owner is remote NFS client }
F_NOINTR=$100; { Ignore signals when waiting }
+ // lock operations for flock(2)
+ LOCK_SH =$01; // shared file lock
+ LOCK_EX =$02; // exclusive file lock
+ LOCK_NB =$04; // don't block when locking
+ LOCK_UN =$08; // unlock file
+
//Advice to posix_fadvise
POSIX_FADV_NORMAL =0; // no special treatment
POSIX_FADV_RANDOM =1; // expect random page references
@@ -136,14 +163,26 @@ const
type
p_flock=^t_flock;
t_flock=packed record
- l_start :QWORD ; // starting offset
- l_len :QWORD ; // len = 0 means until end of file
+ l_start :Int64 ; // starting offset
+ l_len :Int64 ; // len = 0 means until end of file
l_pid :DWORD ; // lock owner
l_type :WORD ; // lock type: read/write, etc.
l_whence:WORD ; // type of l_start
l_sysid :Integer; // remote system id or zero for local
end;
+{
+ * Old advisory file segment locking data type,
+ * before adding l_sysid.
+}
+ __oflock=packed record
+ l_start :Int64; // starting offset
+ l_len :Int64; // len = 0 means until end of file
+ l_pid :DWORD; // lock owner
+ l_type :WORD ; // lock type: read/write, etc.
+ l_whence:WORD ; // type of l_start
+ end;
+
{ convert from open() flags to/from fflags; convert O_RD/WR to FREAD/FWRITE }
function FFLAGS(oflags:Integer):Integer; inline;
function OFLAGS(fflags:Integer):Integer; inline;
diff --git a/sys/vfs/vfile.pas b/sys/vfs/vfile.pas
index 657fcc24..5323e602 100644
--- a/sys/vfs/vfile.pas
+++ b/sys/vfs/vfile.pas
@@ -27,6 +27,8 @@ const
MAXPATHLEN =PATH_MAX;
MAXSYMLINKS=32;
+ maxfilesperproc = 44236;
+
// configurable pathname variables
_PC_LINK_MAX =1;
_PC_MAX_CANON =2;
@@ -39,6 +41,25 @@ const
_PC_VDISABLE =9;
_POSIX_VDISABLE =$ff;
+ _PC_ASYNC_IO =53;
+ _PC_PRIO_IO =54;
+ _PC_SYNC_IO =55;
+
+ _PC_ALLOC_SIZE_MIN =10;
+ _PC_FILESIZEBITS =12;
+ _PC_REC_INCR_XFER_SIZE=14;
+ _PC_REC_MAX_XFER_SIZE =15;
+ _PC_REC_MIN_XFER_SIZE =16;
+ _PC_REC_XFER_ALIGN =17;
+ _PC_SYMLINK_MAX =18;
+
+ _PC_ACL_EXTENDED=59;
+ _PC_ACL_PATH_MAX=60;
+ _PC_CAP_PRESENT =61;
+ _PC_INF_PRESENT =62;
+ _PC_MAC_PRESENT =63;
+ _PC_ACL_NFS4 =64;
+
// access function
F_OK=0; // test for existence of file
X_OK=$01; // test for execute or search permission
@@ -46,9 +67,16 @@ const
R_OK=$04; // test for read permission
// whence values for lseek(2)
- SEEK_SET=0; // set file offset to offset
- SEEK_CUR=1; // set file offset to current plus offset
- SEEK_END=2; // set file offset to EOF plus offset
+ SEEK_SET =0; // set file offset to offset
+ SEEK_CUR =1; // set file offset to current plus offset
+ SEEK_END =2; // set file offset to EOF plus offset
+ SEEK_DATA=3; // set file offset to next data past offset
+ SEEK_HOLE=4; // set file offset to next hole past offset
+
+ // whence values for lseek(2); renamed by POSIX 1003.1
+ L_SET =SEEK_SET;
+ L_INCR=SEEK_CUR;
+ L_XTND=SEEK_END;
DTYPE_VNODE = 1; { file }
DTYPE_SOCKET = 2; { communications endpoint }
@@ -131,6 +159,7 @@ type
* DTYPE_VNODE specific fields.
}
f_seqcount:Integer; { Count of sequential accesses. }
+ f_exclose :Integer;
f_nextoff :Int64; { next expected read/write offset. }
f_vnun :Pointer;
{
@@ -157,6 +186,14 @@ const
var
openfiles:Integer=0; { actual number of open files }
+ {
+ * The module initialization routine for POSIX asynchronous I/O will
+ * set this to the version of AIO that it implements. (Zero means
+ * that it is not implemented.) This value is used here by pathconf()
+ * and in kern_descrip.c by fpathconf().
+ }
+ async_io_version:Integer=0;
+
function fo_read(fp:p_file;uio:p_uio;flags:Integer):Integer;
function fo_write(fp:p_file;uio:p_uio;flags:Integer):Integer;
function fo_truncate(fp:p_file;length:Int64):Integer;
@@ -170,11 +207,6 @@ function fo_chown(fp:p_file;uid:uid_t;gid:gid_t):Integer;
implementation
-//function foffset_get(fp:p_file):Int64; inline;
-//begin
-// Result:=(foffset_lock(fp, FOF_NOLOCK));
-//end;
-
{
fhold(fp) (refcount_acquire(&(fp)^.f_count))
fdrop(fp, td) (refcount_release(&(fp)^.f_count) ? _fdrop((fp), (td)) : _fnoop())
diff --git a/sys/vfs/vfiledesc.pas b/sys/vfs/vfiledesc.pas
index 8f163ee7..c7fe73f5 100644
--- a/sys/vfs/vfiledesc.pas
+++ b/sys/vfs/vfiledesc.pas
@@ -20,7 +20,7 @@ type
NDSLOTTYPE=QWORD;
p_filedesc=^t_filedesc;
- t_filedesc=packed record
+ t_filedesc=packed object
fd_ofiles:t_id_desc_table;
//fd_ofiles :pp_file ; { file structures for open files }
//fd_ofileflags :PChar ; { per-process open file flags }
@@ -38,6 +38,8 @@ type
//fd_kqlist :kqlist ; { list of kqueues on this filedesc }
fd_holdleaderscount :Integer ; { block fdfree() for shared close() }
fd_holdleaderswakeup:Integer ; { fdfree() needs wakeup }
+ //
+ property fd_nfiles:Integer read fd_ofiles.FCount;
end;
{
diff --git a/sys/vfs/vfilio.pas b/sys/vfs/vfilio.pas
new file mode 100644
index 00000000..41bb11f6
--- /dev/null
+++ b/sys/vfs/vfilio.pas
@@ -0,0 +1,28 @@
+unit vfilio;
+
+
+{$mode ObjFPC}{$H+}
+{$CALLING SysV_ABI_CDecl}
+
+interface
+
+const
+ FIOCLEX =$20006601;
+ FIONCLEX =$20006602;
+ FIONREAD =$4004667F;
+ FIONBIO =$8004667E;
+ FIOASYNC =$8004667D;
+ FIOSETOWN =$8004667C;
+ FIOGETOWN =$4004667B;
+ FIODTYPE =$4004667A;
+ FIOGETLBA =$40046679;
+ FIODGNAME =$80106678;
+ FIONWRITE =$40046677;
+ FIONSPACE =$40046676;
+ FIOSEEKDATA=$C0046661;
+ FIOSEEKHOLE=$C0046662;
+
+implementation
+
+end.
+
diff --git a/sys/vfs/vfs_default.pas b/sys/vfs/vfs_default.pas
index ed7fe3df..9c5c8d67 100644
--- a/sys/vfs/vfs_default.pas
+++ b/sys/vfs/vfs_default.pas
@@ -219,6 +219,7 @@ end;
function vop_panic(ap:Pointer):Integer;
begin
Assert(false,'filesystem goof: vop_panic[%s]');
+ Exit(ENOENT);
end;
{
@@ -1036,16 +1037,14 @@ begin
Exit(error);
end;
//vinvalbuf(vp, V_CLEANONLY, 0, 0);
- {
- if (vp^.v_object<>nil) then
- begin
- start:=trunc_page(ap^.a_start);
- __end:=round_page(ap^.a_end);
- VM_OBJECT_LOCK(vp^.v_object);
- vm_object_page_cache(vp^.v_object, OFF_TO_IDX(start), OFF_TO_IDX(__end));
- VM_OBJECT_UNLOCK(vp^.v_object);
- end;
- }
+ //if (vp^.v_object<>nil) then
+ //begin
+ // start:=trunc_page(ap^.a_start);
+ // __end:=round_page(ap^.a_end);
+ // VM_OBJECT_LOCK(vp^.v_object);
+ // vm_object_page_cache(vp^.v_object, OFF_TO_IDX(start), OFF_TO_IDX(__end));
+ // VM_OBJECT_UNLOCK(vp^.v_object);
+ //end;
VOP_UNLOCK(vp, 0);
VFS_UNLOCK_GIANT(vfslocked);
end;
diff --git a/sys/vfs/vfs_mount.pas b/sys/vfs/vfs_mount.pas
index c0ca39b3..0ae151ce 100644
--- a/sys/vfs/vfs_mount.pas
+++ b/sys/vfs/vfs_mount.pas
@@ -425,7 +425,7 @@ begin
error:=_namei(@nd);
if (error<>0) then
Exit (error);
- if (not NDHASGIANT(@nd)) then
+ if (NDHASGIANT(@nd)=0) then
mtx_lock(VFS_Giant);
NDFREE(@nd, NDF_ONLY_PNBUF);
vp:=nd.ni_vp;
diff --git a/sys/vfs/vfs_subr.pas b/sys/vfs/vfs_subr.pas
index d2e2ce74..63752791 100644
--- a/sys/vfs/vfs_subr.pas
+++ b/sys/vfs/vfs_subr.pas
@@ -36,6 +36,8 @@ procedure vput(vp:p_vnode);
procedure vinactive(vp:p_vnode);
+procedure vfs_notify_upper(vp:p_vnode;event:Integer);
+
procedure assert_vi_locked (vp:p_vnode;str:PChar);
procedure assert_vi_unlocked (vp:p_vnode;str:PChar);
procedure assert_vop_locked (vp:p_vnode;str:PChar);
@@ -49,6 +51,11 @@ function vfs_read_dirent(ap:p_vop_readdir_args;dp:p_dirent;off:QWORD):Integer;
procedure vfs_mark_atime(vp:p_vnode);
function vfs_unixify_accmode(accmode:p_accmode_t):Integer;
+function vcount(vp:p_vnode):Integer;
+function count_dev(dev:Pointer):Integer; //cdev
+
+procedure vfs_msync(mp:p_mount;flags:Integer);
+
function __mnt_vnode_next_all(mvp:pp_vnode;mp:p_mount):p_vnode;
function __mnt_vnode_first_all(mvp:pp_vnode;mp:p_mount):p_vnode;
procedure __mnt_vnode_markerfree_all(mvp:pp_vnode;mp:p_mount);
@@ -58,7 +65,8 @@ implementation
uses
errno,
vfs_vnops,
- subr_uio;
+ subr_uio,
+ vm_object;
{
* List of vnodes that are ready for recycling.
@@ -282,7 +290,6 @@ end;
}
procedure vattr_null(vap:p_vattr);
begin
-
vap^.va_type:=VNON;
vap^.va_size:=VNOVAL;
vap^.va_bytes:=VNOVAL;
@@ -2152,9 +2159,9 @@ end;
* failed lock upgrade.
}
procedure vinactive(vp:p_vnode);
+var
+ obj:vm_object_t;
begin
- //struct vm_object *obj;
-
ASSERT_VOP_ELOCKED(vp,'vinactive');
ASSERT_VI_LOCKED(vp,'vinactive');
Assert((vp^.v_iflag and VI_DOINGINACT)=0,'vinactive: recursed on VI_DOINGINACT');
@@ -2170,13 +2177,14 @@ begin
* if there is at least one resident non-cached page, the vnode
* cannot leave the active list without the page cleanup done.
}
+ obj:=nil;
//obj:=vp^.v_object;
- //if (obj<>nil) and (obj^.flags and OBJ_MIGHTBEDIRTY)<>0) then
- //begin
- // VM_OBJECT_LOCK(obj);
- // vm_object_page_clean(obj, 0, 0, OBJPC_NOSYNC);
- // VM_OBJECT_UNLOCK(obj);
- //end;
+ if (obj<>nil) and ((obj^.flags and OBJ_MIGHTBEDIRTY)<>0) then
+ begin
+ VM_OBJECT_LOCK(obj);
+ //vm_object_page_clean(obj, 0, 0, OBJPC_NOSYNC);
+ VM_OBJECT_UNLOCK(obj);
+ end;
VOP_INACTIVE(vp);
VI_LOCK(vp);
Assert((vp^.v_iflag and VI_DOINGINACT)<>0,'vinactive: lost VI_DOINGINACT');
@@ -2350,70 +2358,90 @@ begin
vgonel(vp);
VI_UNLOCK(vp);
end;
+}
-static void
-notify_lowervp_vfs_dummy(mp:p_mount __unused,
- struct vnode *lowervp __unused)
+procedure notify_lowervp_vfs_dummy(mp:p_mount;lowervp:p_vnode);
begin
end;
+const
+ vgonel_vfsops:vfsops=(
+ vfs_mount :nil;
+ vfs_cmount :nil;
+ vfs_unmount :nil;
+ vfs_root :nil;
+ vfs_quotactl :nil;
+ vfs_statfs :nil;
+ vfs_sync :nil;
+ vfs_vget :nil;
+ vfs_fhtovp :nil;
+ vfs_checkexp :nil;
+ vfs_init :nil;
+ vfs_uninit :nil;
+ vfs_extattrctl :nil;
+ vfs_sysctl :nil;
+ vfs_susp_clean :nil;
+ vfs_reclaim_lowervp:@notify_lowervp_vfs_dummy;
+ vfs_unlink_lowervp :@notify_lowervp_vfs_dummy
+ );
+
{
* Notify upper mounts about reclaimed or unlinked vnode.
}
-void
-vfs_notify_upper(vp:p_vnode, int event)
+procedure vfs_notify_upper(vp:p_vnode;event:Integer);
+label
+ unlock;
+var
+ mp,ump,mmp:p_mount;
begin
- static struct vfsops vgonel_vfsops:=begin
- .vfs_reclaim_lowervp:=notify_lowervp_vfs_dummy,
- .vfs_unlink_lowervp:=notify_lowervp_vfs_dummy,
- end;;
- mp:p_mount, *ump, *mmp;
-
mp:=vp^.v_mount;
- if (mp=nil)
+ if (mp=nil) then
Exit;
MNT_ILOCK(mp);
- if (TAILQ_EMPTY(@mp^.mnt_uppers))
+ if TAILQ_EMPTY(@mp^.mnt_uppers) then
goto unlock;
MNT_IUNLOCK(mp);
- mmp:=malloc(sizeof(struct mount), M_TEMP, M_WAITOK or M_ZERO);
+ mmp:=AllocMem(SizeOf(mount));
mmp^.mnt_op:=@vgonel_vfsops;
- mmp^.mnt_kern_flag:= or MNTK_MARKER;
+ mmp^.mnt_kern_flag:=mmp^.mnt_kern_flag or MNTK_MARKER;
MNT_ILOCK(mp);
- mp^.mnt_kern_flag:= or MNTK_VGONE_UPPER;
- for (ump:=TAILQ_FIRST(@mp^.mnt_uppers); ump<>nil;) begin
- if ((ump^.mnt_kern_flag and MNTK_MARKER)<>0) begin
- ump:=TAILQ_NEXT(ump, mnt_upper_link);
+ mp^.mnt_kern_flag:=mmp^.mnt_kern_flag or MNTK_VGONE_UPPER;
+
+ ump:=TAILQ_FIRST(@mp^.mnt_uppers);
+ while (ump<>nil) do
+ begin
+ if ((ump^.mnt_kern_flag and MNTK_MARKER)<>0) then
+ begin
+ ump:=TAILQ_NEXT(ump,@ump^.mnt_upper_link);
continue;
end;
- TAILQ_INSERT_AFTER(@mp^.mnt_uppers, ump, mmp, mnt_upper_link);
+ TAILQ_INSERT_AFTER(@mp^.mnt_uppers, ump, mmp,@mmp^.mnt_upper_link);
MNT_IUNLOCK(mp);
- switch (event) begin
- case VFS_NOTIFY_UPPER_RECLAIM:
- VFS_RECLAIM_LOWERVP(ump, vp);
- break;
- case VFS_NOTIFY_UPPER_UNLINK:
- VFS_UNLINK_LOWERVP(ump, vp);
- break;
- default:
- Assert(0, 'invalid event %d", event));
- break;
+ case event of
+ VFS_NOTIFY_UPPER_RECLAIM:
+ VFS_RECLAIM_LOWERVP(ump, vp);
+ VFS_NOTIFY_UPPER_UNLINK:
+ VFS_UNLINK_LOWERVP(ump, vp);
+ else
+ Assert(false, 'invalid event');
end;
MNT_ILOCK(mp);
- ump:=TAILQ_NEXT(mmp, mnt_upper_link);
- TAILQ_REMOVE(@mp^.mnt_uppers, mmp, mnt_upper_link);
+ ump:=TAILQ_NEXT(mmp,@mmp^.mnt_upper_link);
+ TAILQ_REMOVE(@mp^.mnt_uppers, mmp,@mmp^.mnt_upper_link);
end;
- free(mmp, M_TEMP);
- mp^.mnt_kern_flag:= and ~MNTK_VGONE_UPPER;
- if ((mp^.mnt_kern_flag and MNTK_VGONE_WAITER)<>0) begin
- mp^.mnt_kern_flag:= and ~MNTK_VGONE_WAITER;
+ FreeMem(mmp);
+ mp^.mnt_kern_flag:=mp^.mnt_kern_flag and (not MNTK_VGONE_UPPER);
+ if ((mp^.mnt_kern_flag and MNTK_VGONE_WAITER)<>0) then
+ begin
+ mp^.mnt_kern_flag:=mp^.mnt_kern_flag and (not MNTK_VGONE_WAITER);
wakeup(@mp^.mnt_uppers);
end;
unlock:
MNT_IUNLOCK(mp);
end;
+{
{
* vgone, with the vp interlock held.
}
@@ -2508,6 +2536,7 @@ end;
}
function vcount(vp:p_vnode):Integer;
begin
+ Result:=0;
//dev_lock();
//count:=vp^.v_rdev^.si_usecount;
//dev_unlock();
@@ -2519,6 +2548,7 @@ end;
}
function count_dev(dev:Pointer):Integer; //cdev
begin
+ Result:=0;
//dev_lock();
//count:=dev^.si_usecount;
//dev_unlock();
@@ -2529,15 +2559,14 @@ end;
* perform msync on all vnodes under a mount point
* the mount point must be locked.
}
-
-{
-void
-vfs_msync(mp:p_mount, int flags)
+procedure vfs_msync(mp:p_mount;flags:Integer);
+var
+ vp,mvp:p_vnode;
+ obj:Pointer; //vm_object
begin
- vp:p_vnode, *mvp;
- struct vm_object *obj;
-
- MNT_VNODE_FOREACH_ACTIVE(vp, mp, mvp) begin
+{
+ MNT_VNODE_FOREACH_ACTIVE(vp, mp, mvp)
+ begin
obj:=vp^.v_object;
if (obj<>nil and (obj^.flags and OBJ_MIGHTBEDIRTY)<>0 and
(flags=MNT_WAIT or VOP_ISLOCKED(vp)=0)) begin
@@ -2562,8 +2591,8 @@ begin
end; else
VI_UNLOCK(vp);
end;
-end;
}
+end;
{
static void
diff --git a/sys/vfs/vfs_syscalls.pas b/sys/vfs/vfs_syscalls.pas
new file mode 100644
index 00000000..33006a35
--- /dev/null
+++ b/sys/vfs/vfs_syscalls.pas
@@ -0,0 +1,2942 @@
+unit vfs_syscalls;
+
+{$mode ObjFPC}{$H+}
+{$CALLING SysV_ABI_CDecl}
+
+interface
+
+uses
+ time,
+ vmount,
+ vuio,
+ vnamei,
+ vstat,
+ vfile,
+ vfiledesc,
+ vcapability,
+ vfcntl,
+ vfilio,
+ vdisk,
+ vfs_mount,
+ vfs_vnode,
+ vfs_vnops,
+ vfs_subr,
+ vfs_lookup;
+
+function chroot_refuse_vdir_fds():Integer;
+function getutimes(usrtvp:ptimeval;tvpseg:uio_seg;tsp:ptimespec):Integer;
+function setfflags(vp:p_vnode;flags:Integer):Integer;
+function setutimes(vp:p_vnode;ts:ptimespec;numtimes,nilflag:Integer):Integer;
+function vn_access(vp:p_vnode;user_flags:Integer):Integer;
+
+function sys_sync():Integer;
+function sys_statfs(path:PChar;buf:p_statfs):Integer;
+function sys_fstatfs(fd:Integer;buf:p_statfs):Integer;
+function sys_getfsstat(buf:p_statfs;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(path,link: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:p_stat):Integer;
+function sys_fstatat(fd:Integer;path:PChar;buf:p_stat;flag:Integer):Integer;
+function sys_lstat(path:PChar;ub:p_stat):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:ptimeval):Integer;
+function sys_futimesat(fd:Integer;path:PChar;times:ptimeval):Integer;
+function sys_lutimes(path:PChar;tptr:ptimeval):Integer;
+function sys_futimes(fd:Integer;tptr:ptimeval):Integer;
+function sys_truncate(path:PChar;length:Int64):Integer;
+function sys_fsync(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:PChar;count:DWORD;basep:PInt64):Integer;
+function sys_getdents(fd:Integer;buf:PChar;count:DWORD):Integer;
+function sys_umask(newmask:Integer):Integer;
+function sys_revoke(path:PChar):Integer;
+
+implementation
+
+uses
+ mqueue,
+ systm,
+ errno,
+ kern_mtx,
+ kern_thr,
+ kern_synch,
+ kern_descrip,
+ vnode_if,
+ sys_capability,
+ 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:nameidata;
+begin
+ NDINIT(@nd, LOOKUP, FOLLOW or LOCKSHARED or LOCKLEAF or MPSAFE or AUDITVNODE1, pathseg, path, curkthread);
+ error:=_namei(@nd);
+ if (error<>0) then
+ Exit(error);
+ 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
+ Move(sp^, sb, sizeof(sb));
+ 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:p_statfs):Integer;
+var
+ sf:t_statfs;
+ error:Integer;
+begin
+ error:=kern_statfs(path, UIO_USERSPACE, @sf);
+ if (error=0) then
+ begin
+ error:=copyout(@sf, buf, sizeof(sf));
+ end;
+ Exit(error);
+end;
+
+function getvnode(fd:Integer;rights:cap_rights_t;fpp:pp_file):Integer; forward;
+
+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
+ Exit(error);
+ vp:=fp^.f_vnode;
+ vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
+ vn_lock(vp, LK_SHARED or LK_RETRY);
+ mp:=vp^.v_mount;
+ if (mp<>nil) then
+ vfs_ref(mp);
+ 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
+ goto _out;
+ if {(priv_check(td, PRIV_VFS_GENERATION))} True then
+ begin
+ Move(sp^, sb, sizeof(sb));
+ 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
+ vfs_unbusy(mp);
+ VFS_UNLOCK_GIANT(vfslocked);
+ Exit(error);
+end;
+
+{
+ * Get filesystem statistics.
+ }
+function sys_fstatfs(fd:Integer;buf:p_statfs):Integer;
+var
+ sf:t_statfs;
+ error:Integer;
+begin
+ 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
+ mp,nmp:p_mount;
+ sfsp,sp:p_statfs;
+ sb:t_statfs;
+ count,maxcount:QWORD;
+ vfslocked:Integer;
+ error:Integer;
+begin
+ 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
+ maxcount:=count;
+ 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 (count0) 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
+ Move(sp^, sb, sizeof(sb));
+ 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
+ Move(sp^, sfsp^, sizeof(sb));
+ 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
+ curkthread^.td_retval[0]:=maxcount
+ else
+ curkthread^.td_retval[0]:=count;
+ Exit(0);
+end;
+
+{
+ * Get statistics on all filesystems.
+ }
+function sys_getfsstat(buf:p_statfs;bufsize:QWORD;flags:Integer):Integer;
+begin
+ 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
+ error:=getvnode(fd, CAP_FCHDIR, @fp);
+ if (error<>0) then
+ Exit(error);
+ vp:=fp^.f_vnode;
+ VREF(vp);
+ fdrop(fp);
+ vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
+ vn_lock(vp, LK_SHARED or LK_RETRY);
+
+ error:=change_dir(vp);
+ mp:=vp^.v_mountedhere;
+ while (error=0) and (mp<>nil) do
+ begin
+ if (vfs_busy(mp, 0)<>0) then
+ continue;
+ 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:nameidata;
+ vp:p_vnode;
+ vfslocked:Integer;
+begin
+ NDINIT(@nd, LOOKUP, FOLLOW or LOCKSHARED or LOCKLEAF or AUDITVNODE1 or MPSAFE, pathseg, path, curkthread);
+ error:=_namei(@nd);
+ if (error<>0) then
+ Exit(error);
+ 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
+ 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
+ continue;
+ if (fp^.f_type=DTYPE_VNODE) then
+ begin
+ vp:=fp^.f_vnode;
+ if (vp^.v_type=VDIR) then
+ Exit(EPERM);
+ 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:nameidata;
+ vfslocked:Integer;
+begin
+ error:=EPERM;
+ //error:=priv_check(td, PRIV_VFS_CHROOT);
+ if (error<>0) then
+ Exit(error);
+ NDINIT(@nd, LOOKUP, FOLLOW or LOCKSHARED or LOCKLEAF or MPSAFE or AUDITVNODE1, UIO_USERSPACE, path, curkthread);
+ error:=_namei(@nd);
+ if (error<>0) then
+ goto _error;
+ vfslocked:=NDHASGIANT(@nd);
+ error:=change_dir(nd.ni_vp);
+ if (error<>0) then
+ goto e_vunlock;
+
+ //if ((error:=mac_vnode_check_chroot(td^.td_ucred, nd.ni_vp)))
+ // goto e_vunlock;
+
+ 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
+ Exit(ENOTDIR);
+
+ //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
+ rights:=rights or CAP_CREATE;
+
+ if ((flags and O_TRUNC)<>0) then
+ rights:=rights or CAP_FTRUNCATE;
+
+ if ((flags and O_EXLOCK)<>0) or ((flags and O_SHLOCK)<>0) then
+ rights:=rights or CAP_FLOCK;
+
+ 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;
+ _type,indx,error,error_open:Integer;
+ lf:t_flock;
+ nd:nameidata;
+ vfslocked:Integer;
+ rights_needed:cap_rights_t;
+begin
+ td:=curkthread;
+ 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
+ Exit(EINVAL);
+ end else
+ if ((flags and O_ACCMODE)=O_ACCMODE) then
+ Exit(EINVAL)
+ else
+ flags:=FFLAGS(flags);
+
+ {
+ * allocate the file descriptor, but don't install a descriptor yet
+ }
+ error:=falloc_noinstall(@nfp);
+ if (error<>0) then
+ Exit(error);
+ { 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
+ goto success;
+
+ {
+ * 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
+ goto bad_unlocked;
+ error:=dupfdopen(indx, td^.td_dupfd, flags, error_open);
+ if (error=0) then
+ goto success;
+ 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;
+ _type:=F_FLOCK;
+ if ((flags and FNONBLOCK)=0) then
+ _type:=_type or F_WAIT;
+ error:=VOP_ADVLOCK(vp, fp, F_SETLK, @lf, _type);
+ if (error<>0) then
+ goto bad;
+ 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
+ goto bad;
+ 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
+ goto bad_unlocked;
+ end else
+ begin
+ error:=finstall(fp, @indx, flags);
+ if (error<>0) then
+ goto bad_unlocked;
+ 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
+ fdclose(fp, indx);
+ 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
+ Exit(kern_open(path, UIO_USERSPACE, flags, mode));
+end;
+
+function sys_openat(fd:Integer;path:PChar;flags,mode:Integer):Integer;
+begin
+ Exit(kern_openat(fd, path, UIO_USERSPACE, flags, mode));
+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:nameidata;
+ vfslocked:Integer;
+begin
+restart:
+ //bwillwrite();
+ NDINIT_AT(@nd, CREATE, LOCKPARENT or SAVENAME or MPSAFE or AUDITVNODE1, pathseg, path, fd, curkthread);
+ error:=_namei(@nd);
+ if (error<>0) then
+ Exit(error);
+ 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
+ Exit(error);
+ 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
+ vput(nd.ni_vp);
+
+_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: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
+ Exit(error);
+
+restart:
+ //bwillwrite();
+ NDINIT_ATRIGHTS(@nd, CREATE, LOCKPARENT or SAVENAME or MPSAFE or AUDITVNODE1, pathseg, path, fd, CAP_MKFIFO, curkthread);
+ error:=_namei(@nd);
+ if (error<>0) then
+ Exit(error);
+ 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
+ Exit(error);
+ 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
+ vput(nd.ni_vp);
+ 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
+ Exit(kern_mknod(path, UIO_USERSPACE, mode, dev));
+end;
+
+function sys_mknodat(fd:Integer;path:PChar;mode,dev:Integer):Integer;
+begin
+ Exit(kern_mknodat(fd, path, UIO_USERSPACE, mode, dev));
+end;
+
+function kern_mkfifo(path:PChar;pathseg:uio_seg;mode:Integer):Integer;
+begin
+ Exit(kern_mkfifoat(AT_FDCWD, path, pathseg, mode));
+end;
+
+{
+ * Create a named pipe.
+ }
+function sys_mkfifo(path:PChar;mode:Integer):Integer;
+begin
+ Exit(kern_mkfifo(path, UIO_USERSPACE, mode));
+end;
+
+function sys_mkfifoat(fd:Integer;path:PChar;mode:Integer):Integer;
+begin
+ Exit(kern_mkfifoat(fd, path, UIO_USERSPACE, mode));
+end;
+
+function can_hardlink(vp:p_vnode):Integer;
+begin
+ Exit(EPERM);
+end;
+
+function kern_linkat(fd1,fd2:Integer;path1,path2:PChar;segflg:uio_seg;follow:Integer):Integer;
+var
+ vp:p_vnode;
+ mp:p_mount;
+ nd:nameidata;
+ vfslocked:Integer;
+ lvfslocked:Integer;
+ error:Integer;
+begin
+ //bwillwrite();
+ NDINIT_AT(@nd, LOOKUP, follow or MPSAFE or AUDITVNODE1, segflg, path1, fd1, curkthread);
+
+ error:=_namei(@nd);
+ if (error<>0) then
+ Exit(error);
+ 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:=_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);
+ 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
+
+ error:=VOP_LINK(nd.ni_dvp, vp, @nd.ni_cnd);
+ 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(path,link:PChar):Integer;
+begin
+ Exit(kern_link(path, link, UIO_USERSPACE));
+end;
+
+function sys_linkat(fd1:Integer;path1:PChar;fd2:Integer;path2:PChar;flag:Integer):Integer;
+begin
+ if ((flag and (not AT_SYMLINK_FOLLOW))<>0) then
+ Exit(EINVAL);
+
+ 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:nameidata;
+ vfslocked:Integer;
+begin
+ if (segflg=UIO_SYSSPACE) then
+ begin
+ syspath:=path1;
+ end else
+ begin
+ syspath:=AllocMem(SizeOf(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:=_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
+ vput(nd.ni_vp);
+out2:
+ NDFREE(@nd, NDF_ONLY_PNBUF);
+ vput(nd.ni_dvp);
+ vn_finished_write(mp);
+ VFS_UNLOCK_GIANT(vfslocked);
+_out:
+ if (segflg<>UIO_SYSSPACE) then
+ FreeMem(syspath);
+ 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
+ Exit(kern_symlink(path, link, UIO_USERSPACE));
+end;
+
+function sys_symlinkat(path1:PChar;fd:Integer;path2:PChar):Integer;
+begin
+ 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: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:=_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
+ error:=EBUSY;
+ 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
+ Exit(error);
+ goto restart;
+ end;
+
+ //error:=mac_vnode_check_unlink(td^.td_ucred, nd.ni_dvp, vp, @nd.ni_cnd);
+ //if (error)
+ // goto _out;
+
+ vfs_notify_upper(vp, VFS_NOTIFY_UPPER_UNLINK);
+ 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 kern_rmdirat(fd:Integer;path:PChar;pathseg:uio_seg):Integer; forward;
+
+function sys_unlinkat(fd:Integer;path:PChar;flag:Integer):Integer;
+begin
+ if ((flag and (not AT_REMOVEDIR))<>0) then
+ Exit(EINVAL);
+
+ 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
+ fp:p_file;
+ vp:p_vnode;
+ vattr:t_vattr;
+ foffset,size:Int64;
+ error,noneg:Integer;
+ vfslocked:Integer;
+begin
+ error:=fget(fd, CAP_SEEK, @fp);
+ if (error<>0) then
+ Exit(error);
+ 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:
+ 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:
+ begin
+ vn_lock(vp, LK_SHARED or LK_RETRY);
+ error:=VOP_GETATTR(vp, @vattr);
+ VOP_UNLOCK(vp, 0);
+ if (error<>0) then
+ goto _break;
+
+ {
+ * 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
+ vattr.va_size:=size;
+
+ if (noneg<>0) and
+ ((vattr.va_size > High(Int64)) 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,
+ 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
+ error:=EINVAL;
+ if (error<>0) then
+ goto drop;
+ VFS_KNOTE_UNLOCKED(vp, 0);
+ curkthread^.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: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:=_namei(@nd);
+ if (error<>0) then
+ goto out1;
+ 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
+ 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:nameidata;
+ sb:t_stat;
+ error, vfslocked:Integer;
+begin
+ if (flag and (not AT_SYMLINK_NOFOLLOW))<>0 then
+ Exit(EINVAL);
+
+ 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:=_namei(@nd);
+ if (error<>0) then
+ Exit(error);
+ vfslocked:=NDHASGIANT(@nd);
+ error:=vn_stat(nd.ni_vp, @sb);
+ if (error=0) then
+ begin
+ if (hook<>nil) then
+ hook(nd.ni_vp, @sb);
+ end;
+ NDFREE(@nd, NDF_ONLY_PNBUF);
+ vput(nd.ni_vp);
+ VFS_UNLOCK_GIANT(vfslocked);
+ if (error<>0) then
+ Exit(error);
+ sbp^:=sb;
+
+ Exit(0);
+end;
+
+function kern_statat(flag,fd:Integer;path:PChar;
+ pathseg:uio_seg;sbp:p_stat):Integer;
+begin
+
+ Exit(kern_statat_vnhook(flag, fd, path, pathseg, sbp, nil));
+end;
+
+function kern_stat(path:PChar;pathseg:uio_seg;sbp:p_stat):Integer;
+begin
+ Exit(kern_statat(0, AT_FDCWD, path, pathseg, sbp));
+end;
+
+{
+ * Get file status; this version follows links.
+ }
+function sys_stat(path:PChar;ub:p_stat):Integer;
+var
+ sb:t_stat;
+ error:Integer;
+begin
+ error:=kern_stat(path, UIO_USERSPACE, @sb);
+ if (error=0) then
+ error:=copyout(@sb, ub, sizeof(sb));
+ Exit(error);
+end;
+
+function sys_fstatat(fd:Integer;path:PChar;buf:p_stat;flag:Integer):Integer;
+var
+ sb:p_stat;
+ error:Integer;
+begin
+ error:=kern_statat(flag, fd, path, UIO_USERSPACE, @sb);
+ if (error=0) then
+ error:=copyout(@sb, buf, sizeof(sb));
+ 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:p_stat):Integer;
+var
+ sb:t_stat;
+ error:Integer;
+begin
+ error:=kern_lstat(path, UIO_USERSPACE, @sb);
+ if (error=0) then
+ error:=copyout(@sb, ub, sizeof(sb));
+ Exit(error);
+end;
+
+function kern_pathconf(path:PChar;pathseg:uio_seg;name:Integer;flags:QWORD):Integer;
+var
+ td:p_kthread;
+ nd:nameidata;
+ error,vfslocked:Integer;
+begin
+ td:=curkthread;
+ NDINIT(@nd, LOOKUP, LOCKSHARED or LOCKLEAF or MPSAFE or AUDITVNODE1 or flags, pathseg, path, td);
+ error:=_namei(@nd);
+ if (error<>0) then
+ Exit(error);
+ 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
+ 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:nameidata;
+ vfslocked:Integer;
+begin
+ if (count > IOSIZE_MAX) then
+ Exit(EINVAL);
+
+ td:=curkthread;
+ NDINIT_AT(@nd, LOOKUP, NOFOLLOW or LOCKSHARED or LOCKLEAF or MPSAFE or AUDITVNODE1, pathseg, path, fd, td);
+
+ error:=_namei(@nd);
+ if (error<>0) then
+ Exit(error);
+ 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
+ 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
+ Exit(error);
+ 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);
+ VATTR_NULL(@vattr);
+ vattr.va_flags:=flags;
+
+ //error:=mac_vnode_check_setflags(td^.td_ucred, vp, vattr.va_flags);
+ //if (error=0) then
+
+ error:=VOP_SETATTR(vp, @vattr);
+ 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:nameidata;
+ vfslocked:Integer;
+begin
+ NDINIT(@nd, LOOKUP, FOLLOW or MPSAFE or AUDITVNODE1, UIO_USERSPACE, path, curkthread);
+ error:=_namei(@nd);
+ if (error<>0) then
+ Exit(error);
+ 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:nameidata;
+ vfslocked:Integer;
+begin
+ NDINIT(@nd, LOOKUP, NOFOLLOW or MPSAFE or AUDITVNODE1, UIO_USERSPACE, path, curkthread);
+ error:=_namei(@nd);
+ if (error<>0) then
+ Exit(error);
+ 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
+ Exit(error);
+
+ 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
+ Exit(error);
+ vn_lock(vp, LK_EXCLUSIVE or LK_RETRY);
+ VATTR_NULL(@vattr);
+ vattr.va_mode:=mode and ALLPERMS;
+
+ //error:=mac_vnode_check_setmode(cred, vp, vattr.va_mode);
+ //if (error=0) then
+
+ error:=VOP_SETATTR(vp, @vattr);
+ 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: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:=_namei(@nd);
+ if (error<>0) then
+ Exit(error);
+ 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
+ Exit(kern_chmod(path, UIO_USERSPACE, mode));
+end;
+
+function sys_fchmodat(fd:Integer;path:PChar;mode,flag:Integer):Integer;
+begin
+ if ((flag and (not AT_SYMLINK_NOFOLLOW))<>0) then
+ Exit(EINVAL);
+
+ 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
+ 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
+ error:=fget(fd, CAP_FCHMOD, @fp);
+ if (error<>0) then
+ Exit(error);
+ 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
+ Exit(error);
+ vn_lock(vp, LK_EXCLUSIVE or LK_RETRY);
+ 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
+
+ error:=VOP_SETATTR(vp, @vattr);
+ 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: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:=_namei(@nd);
+ if (error<>0) then
+ Exit(error);
+ 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
+ Exit(kern_chown(path, UIO_USERSPACE, uid, gid));
+end;
+
+function sys_fchownat(fd:Integer;path:PChar;uid,gid,flag:Integer):Integer;
+begin
+ if ((flag and (not AT_SYMLINK_NOFOLLOW))<>0) then
+ Exit(EINVAL);
+
+ 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
+ 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
+ error:=fget(fd, CAP_FCHOWN, @fp);
+ if (error<>0) then
+ Exit(error);
+ error:=fo_chown(fp, uid, gid);
+ fdrop(fp);
+ Exit(error);
+end;
+
+{
+ * Common implementation code for utimes(), lutimes(), and futimes().
+ }
+function getutimes(usrtvp:ptimeval;tvpseg:uio_seg;tsp:ptimespec):Integer;
+var
+ tv:array[0..1] of timeval;
+ tvp:ptimeval;
+ 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
+ Exit(error);
+ 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
+ Exit(EINVAL);
+
+ 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:ptimespec;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);
+ setbirthtime:=0;
+ if (numtimes < 3) and
+ (VOP_GETATTR(vp, @vattr)=0) and
+ (timespeccmp_lt(@ts[1], @vattr.va_birthtime)<>0) then //<
+ setbirthtime:=1;
+
+ VATTR_NULL(@vattr);
+ vattr.va_atime:=ts[0];
+ vattr.va_mtime:=ts[1];
+ if (setbirthtime<>0) then
+ vattr.va_birthtime:=ts[1];
+ if (numtimes > 2) then
+ vattr.va_birthtime:=ts[2];
+ if (nilflag<>0) then
+ 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);
+
+ if (error=0) then
+ error:=VOP_SETATTR(vp, @vattr);
+ VOP_UNLOCK(vp, 0);
+ vn_finished_write(mp);
+ Exit(error);
+end;
+
+function kern_utimesat(fd:Integer;path:PChar;pathseg:uio_seg;
+ tptr:ptimeval;tptrseg:uio_seg):Integer;
+var
+ nd: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:=_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:ptimeval;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:ptimeval):Integer;
+begin
+ Exit(kern_utimes(path, UIO_USERSPACE, tptr, UIO_USERSPACE));
+end;
+
+function sys_futimesat(fd:Integer;path:PChar;times:ptimeval):Integer;
+begin
+ Exit(kern_utimesat(fd, path, UIO_USERSPACE, times, UIO_USERSPACE));
+end;
+
+function kern_lutimes(path:PChar;pathseg:uio_seg;
+ tptr:ptimeval;tptrseg:uio_seg):Integer;
+var
+ ts:array[0..1] of timespec;
+ error:Integer;
+ nd: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:=_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:ptimeval):Integer;
+begin
+ Exit(kern_lutimes(path, UIO_USERSPACE, tptr, UIO_USERSPACE));
+end;
+
+function kern_futimes(fd:Integer;tptr:ptimeval;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
+ Exit(error);
+
+ error:=getvnode(fd, CAP_FUTIMES, @fp);
+ if (error<>0) then
+ Exit(error);
+ 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:ptimeval):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:nameidata;
+ error,vfslocked:Integer;
+begin
+ if (length < 0) then
+ Exit(EINVAL);
+ NDINIT(@nd, LOOKUP, FOLLOW or MPSAFE or AUDITVNODE1, pathseg, path, curkthread);
+ error:=_namei(@nd);
+ if (error<>0) then
+ Exit(error);
+ 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);
+ 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 sys_fsync(fd:Integer):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);
+
+ //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);
+
+ VOP_UNLOCK(vp, 0);
+ vn_finished_write(mp);
+drop:
+ VFS_UNLOCK_GIANT(vfslocked);
+ fdrop(fp);
+ Exit(error);
+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: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:=_namei(@fromnd);
+ if (error<>0) then
+ Exit(error);
+ 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
+ VOP_UNLOCK(fromnd.ni_vp, 0);
+
+ fvp:=fromnd.ni_vp;
+ if (error=0) then
+ error:=vn_start_write(fvp, @mp, V_WAIT or PCATCH);
+ 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
+ tond.ni_cnd.cn_flags:=tond.ni_cnd.cn_flags or WILLBEDIR;
+
+ error:=_namei(@tond);
+ if (error<>0) then
+ begin
+ { Translate error code for rename('dir1', 'dir2/.'). }
+ if (error=EISDIR) and (fvp^.v_type=VDIR) then
+ error:=EINVAL;
+ 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
+ error:=-1;
+
+ //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
+ vput(tvp);
+ 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
+ vrele(fromnd.ni_startdir);
+ 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
+ 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: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:=_namei(@nd);
+ if (error<>0) then
+ Exit(error);
+ 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
+ Exit(error);
+ 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
+ vput(nd.ni_vp);
+ 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
+ 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:nameidata;
+ vfslocked:Integer;
+begin
+restart:
+ //bwillwrite();
+ NDINIT_ATRIGHTS(@nd, DELETE, LOCKPARENT or LOCKLEAF or MPSAFE or
+ AUDITVNODE1, pathseg, path, fd, CAP_RMDIR, curkthread);
+ error:=_namei(@nd);
+ if (error<>0) then
+ Exit(error);
+ 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
+ Exit(error);
+ goto restart;
+ end;
+ vfs_notify_upper(vp, VFS_NOTIFY_UPPER_UNLINK);
+ 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:PChar;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;
+ auio.uio_resid:=count;
+ if (auio.uio_resid > IOSIZE_MAX) then
+ Exit(EINVAL);
+ error:=getvnode(fd, CAP_READ or CAP_SEEK, @fp);
+ if (error<>0) then
+ Exit(error);
+ 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);
+
+ loff:=foffset;
+ auio.uio_offset:=foffset;
+
+ //error:=mac_vnode_check_readdir(td^.td_ucred, vp);
+ //if (error=0) then
+
+ error:=VOP_READDIR(vp, @auio, @eofflag, nil, nil);
+ 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) and
+ (p_mount(vp^.v_mount)^.mnt_flag and MNT_UNION)) 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:PChar;count:DWORD;basep:PInt64):Integer;
+var
+ base:Int64;
+ error:Integer;
+begin
+ error:=kern_getdirentries(fd, buf, count, @base);
+ if (error<>0) then
+ Exit(error);
+ if (basep<>nil) then
+ error:=copyout(@base, basep, sizeof(Int64));
+ Exit(error);
+end;
+
+function sys_getdents(fd:Integer;buf:PChar;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;
+begin
+ FILEDESC_XLOCK(@fd_table);
+ curkthread^.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:nameidata;
+ vfslocked:Integer;
+begin
+ NDINIT(@nd, LOOKUP, FOLLOW or LOCKLEAF or MPSAFE or AUDITVNODE1,
+ UIO_USERSPACE, path, curkthread);
+ error:=_namei(@nd);
+ if (error<>0) then
+ Exit(error);
+ 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
+ goto _out;
+ //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
+ VOP_REVOKE(vp, REVOKEALL);
+_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
+ Exit(EBADF);
+
+ {
+ * 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;
+
+end.
+
diff --git a/sys/vfs/vfs_vnode.pas b/sys/vfs/vfs_vnode.pas
index 0e3c204b..01ced3e0 100644
--- a/sys/vfs/vfs_vnode.pas
+++ b/sys/vfs/vfs_vnode.pas
@@ -10,7 +10,6 @@ uses
kern_thr,
kern_mtx,
time,
- vuio,
vmparam;
const
@@ -210,7 +209,7 @@ type
va_nlink :Word;
va_uid :Integer;
va_gid :Integer;
- va_fsid :DWORD;
+ va_fsid :Integer;
va_fileid :QWORD;
va_size :QWORD;
va_blocksize:QWORD;
@@ -220,7 +219,7 @@ type
va_birthtime:timespec;
va_gen :QWORD;
va_flags :QWORD;
- va_rdev :DWORD;
+ va_rdev :Integer;
va_bytes :QWORD;
va_filerev :QWORD;
va_vaflags :DWORD;
@@ -233,22 +232,16 @@ function VI_MTX(vp:p_vnode):p_mtx; inline;
function IGNORE_LOCK(vp:p_vnode):Boolean; inline;
+procedure vn_rangelock_unlock(vp:p_vnode;cookie:Pointer);
+procedure vn_rangelock_unlock_range(vp:p_vnode;cookie:Pointer;start,__end:Int64);
+function vn_rangelock_rlock(vp:p_vnode;start,__end:Int64):Pointer;
+function vn_rangelock_wlock(vp:p_vnode;start,__end:Int64):Pointer;
+
var
rootvnode:p_vnode;
implementation
-uses
- errno,
- vnode_if,
- vnamei,
- vfile,
- vmount,
- vfcntl,
- vfs_lookup,
- vfs_subr,
- kern_synch;
-
procedure VI_LOCK(vp:p_vnode);
begin
mtx_lock(vp^.v_interlock);
@@ -274,6 +267,27 @@ begin
Result:=(vp=nil) or (vp^.v_type=VCHR) or (vp^.v_type=VBAD);
end;
+procedure vn_rangelock_unlock(vp:p_vnode;cookie:Pointer);
+begin
+ //rangelock_unlock(@vp^.v_rl, (cookie), VI_MTX(vp))
+end;
+
+procedure vn_rangelock_unlock_range(vp:p_vnode;cookie:Pointer;start,__end:Int64);
+begin
+ //rangelock_unlock_range(@vp^.v_rl, (cookie), start, __end, VI_MTX(vp))
+end;
+
+function vn_rangelock_rlock(vp:p_vnode;start,__end:Int64):Pointer;
+begin
+ Result:=nil;
+ //Result:=rangelock_rlock(@vp^.v_rl, start, __end, VI_MTX(vp))
+end;
+
+function vn_rangelock_wlock(vp:p_vnode;start,__end:Int64):Pointer;
+begin
+ Result:=nil;
+ //Result:=rangelock_wlock(@vp^.v_rl, start, __end, VI_MTX(vp))
+end;
end.
diff --git a/sys/vfs/vfs_vnops.pas b/sys/vfs/vfs_vnops.pas
index b075a0ee..e67cc659 100644
--- a/sys/vfs/vfs_vnops.pas
+++ b/sys/vfs/vfs_vnops.pas
@@ -9,11 +9,18 @@ uses
vmount,
vnamei,
vfile,
+ vstat,
+ vmparam,
vfs_vnode,
kern_mtx;
function vn_lock(vp:p_vnode;flags:Integer):Integer;
+function vn_open(ndp:p_nameidata;
+ flagp:PInteger;
+ cmode:Integer;
+ fp:p_file):Integer;
+
function vn_open_cred(ndp:p_nameidata;
flagp:PInteger;
cmode:Integer;
@@ -23,8 +30,28 @@ function vn_open_cred(ndp:p_nameidata;
function vn_writechk(vp:p_vnode):Integer;
function vn_start_write(vp:p_vnode;mpp:pp_mount;flags:Integer):Integer;
procedure vn_finished_write(mp:p_mount);
-
function vn_close(vp:p_vnode;flags:Integer):Integer;
+function vn_stat(vp:p_vnode;sb:p_stat):Integer;
+
+const
+ vnops:fileops=(
+ fo_read :nil;//@vn_io_fault;
+ fo_write :nil;//@vn_io_fault;
+ fo_truncate:nil;//@vn_truncate;
+ fo_ioctl :nil;//@vn_ioctl;
+ fo_poll :nil;//@vn_poll;
+ fo_kqfilter:nil;//@vn_kqfilter;
+ fo_stat :nil;//@vn_statfile;
+ fo_close :nil;//@vn_closefile;
+ fo_chmod :nil;//@vn_chmod;
+ fo_chown :nil;//@vn_chown;
+ fo_flags :DFLAG_PASSABLE or DFLAG_SEEKABLE
+ );
+
+function foffset_get(fp:p_file):Int64; inline;
+function foffset_lock(fp:p_file;flags:Integer):Int64;
+procedure foffset_lock(fp:p_file;val:Int64;flags:Integer);
+procedure foffset_unlock(fp:p_file;val:Int64;flags:Integer);
implementation
@@ -58,6 +85,14 @@ begin
until ((flags and LK_RETRY)=0) or (Result=0);
end;
+function vn_open(ndp:p_nameidata;
+ flagp:PInteger;
+ cmode:Integer;
+ fp:p_file):Integer;
+begin
+ Result:=vn_open_cred(ndp, flagp, cmode, 0, fp);
+end;
+
{
* Common code for vnode open operations.
* Check permissions, and call the VOP_OPEN or VOP_CREATE routine.
@@ -65,7 +100,6 @@ end;
* Note that this does NOT free nameidata for the successful case,
* due to the NDINIT being done elsewhere.
}
-
function vn_open_cred(ndp:p_nameidata;
flagp:PInteger;
cmode:Integer;
@@ -82,14 +116,14 @@ var
fmode,error:Integer;
accmode:accmode_t;
mps:Integer;
- vfslocked:Boolean;
+ vfslocked:Integer;
begin
vap:=@vat;
mps:=ndp^.ni_cnd.cn_flags and MPSAFE;
restart:
- vfslocked:=False;
+ vfslocked:=0;
fmode:=flagp^;
if ((fmode and O_CREAT)<>0) then
begin
@@ -383,5 +417,143 @@ begin
Exit(error);
end;
+function vn_stat(vp:p_vnode;sb:p_stat):Integer;
+var
+ vattr:t_vattr;
+ vap:p_vattr;
+ error:Integer;
+ mode:WORD;
+begin
+ //error:=mac_vnode_check_stat(active_cred, file_cred, vp);
+ //if (error<>0) then
+ // Exit(error);
+
+ vap:=@vattr;
+
+ {
+ * Initialize defaults for new and unusual fields, so that file
+ * systems which don't support these fields don't need to know
+ * about them.
+ }
+ vap^.va_birthtime.tv_sec:=-1;
+ vap^.va_birthtime.tv_nsec:=0;
+ vap^.va_fsid:=VNOVAL;
+ vap^.va_rdev:=NODEV;
+
+ error:=VOP_GETATTR(vp, vap);
+ if (error<>0) then
+ Exit(error);
+
+ {
+ * Zero the spare stat fields
+ }
+ FillChar(sb^,SizeOf(t_stat),0);
+
+ {
+ * Copy from vattr table
+ }
+ if (vap^.va_fsid<>VNOVAL) then
+ sb^.st_dev:=vap^.va_fsid
+ else
+ sb^.st_dev:=p_mount(vp^.v_mount)^.mnt_stat.f_fsid.val[0];
+ sb^.st_ino:=vap^.va_fileid;
+ mode:=vap^.va_mode;
+ case vap^.va_type of
+ VREG:
+ mode:=mode or S_IFREG;
+ VDIR:
+ mode:=mode or S_IFDIR;
+ VBLK:
+ mode:=mode or S_IFBLK;
+ VCHR:
+ mode:=mode or S_IFCHR;
+ VLNK:
+ mode:=mode or S_IFLNK;
+ VSOCK:
+ mode:=mode or S_IFSOCK;
+ VFIFO:
+ mode:=mode or S_IFIFO;
+ else
+ Exit(EBADF);
+ end;;
+ sb^.st_mode:=mode;
+ sb^.st_nlink:=vap^.va_nlink;
+ sb^.st_uid:=vap^.va_uid;
+ sb^.st_gid:=vap^.va_gid;
+ sb^.st_rdev:=vap^.va_rdev;
+ if (vap^.va_size > High(Int64)) then
+ Exit(EOVERFLOW);
+ sb^.st_size:=vap^.va_size;
+ sb^.st_atim:=vap^.va_atime;
+ sb^.st_mtim:=vap^.va_mtime;
+ sb^.st_ctim:=vap^.va_ctime;
+ sb^.st_birthtim:=vap^.va_birthtime;
+
+ {
+ * According to www.opengroup.org, the meaning of st_blksize is
+ * "a filesystem-specific preferred I/O block size for this
+ * object. In some filesystem types, this may vary from file
+ * to file"
+ * Use miminum/default of PAGE_SIZE (e.g. for VCHR).
+ }
+
+ if (PAGE_SIZE>vap^.va_blocksize) then
+ begin
+ sb^.st_blksize:=PAGE_SIZE;
+ end else
+ begin
+ sb^.st_blksize:=vap^.va_blocksize;
+ end;
+
+ sb^.st_flags:=vap^.va_flags;
+ //if (priv_check(td, PRIV_VFS_GENERATION)) then
+ // sb^.st_gen:=0;
+ //else
+ sb^.st_gen:=vap^.va_gen;
+
+ sb^.st_blocks:=vap^.va_bytes div S_BLKSIZE;
+ Exit(0);
+end;
+
+
+function foffset_get(fp:p_file):Int64; inline;
+begin
+ Result:=(foffset_lock(fp, FOF_NOLOCK));
+end;
+
+function foffset_lock(fp:p_file;flags:Integer):Int64;
+begin
+ Assert((flags and FOF_OFFSET)=0, 'FOF_OFFSET passed');
+
+ if ((flags and FOF_NOLOCK)<>0) then
+ Exit(fp^.f_offset);
+end;
+
+procedure foffset_lock(fp:p_file;val:Int64;flags:Integer);
+begin
+ Assert((flags and FOF_OFFSET)=0, ('FOF_OFFSET passed'));
+
+ if ((flags and FOF_NOLOCK)<>0) then
+ begin
+ if ((flags and FOF_NOUPDATE)=0) then
+ fp^.f_offset:=val;
+ if ((flags and FOF_NEXTOFF)<>0) then
+ fp^.f_nextoff:=val;
+ end;
+end;
+
+procedure foffset_unlock(fp:p_file;val:Int64;flags:Integer);
+begin
+ Assert((flags and FOF_OFFSET)=0, ('FOF_OFFSET passed'));
+
+ if ((flags and FOF_NOLOCK)<>0) then
+ begin
+ if ((flags and FOF_NOUPDATE)=0) then
+ fp^.f_offset:=val;
+ if ((flags and FOF_NEXTOFF)<>0) then
+ fp^.f_nextoff:=val;
+ end;
+end;
+
end.
diff --git a/sys/vfs/vmount.pas b/sys/vfs/vmount.pas
index 1ab1df1f..713936ab 100644
--- a/sys/vfs/vmount.pas
+++ b/sys/vfs/vmount.pas
@@ -21,6 +21,8 @@ const
MFSNAMELEN=16; // length of type name including null
MNAMELEN =88; // size of on/from name bufs
+ STATFS_VERSION=$20030518; // current version number
+
//User specifiable flags, stored in mnt_flag.
MNT_RDONLY =$0000000000000001; // read only filesystem
MNT_SYNCHRONOUS=$0000000000000002; // fs written synchronously
@@ -40,6 +42,14 @@ const
MNT_NOCLUSTERW =$0000000080000000; // disable cluster write
MNT_SUJ =$0000000100000000; // using journaled soft updates
+ //NFS export related mount flags.
+ MNT_EXRDONLY =$0000000000000080; // exported read only
+ MNT_EXPORTED =$0000000000000100; // filesystem is exported
+ MNT_DEFEXPORTED=$0000000000000200; // exported to the world
+ MNT_EXPORTANON =$0000000000000400; // anon uid mapping for all
+ MNT_EXKERB =$0000000000000800; // exported with Kerberos
+ MNT_EXPUBLIC =$0000000020000000; // public export (WebNFS)
+
{
* Flags set by internal operations,
* but visible to the user.
@@ -65,6 +75,17 @@ const
MNT_SNAPSHOT =$0000000001000000; // snapshot the filesystem
MNT_BYFSID =$0000000008000000; // specify filesystem by ID.
+ MNT_VISFLAGMASK=(MNT_RDONLY or MNT_SYNCHRONOUS or MNT_NOEXEC or
+ MNT_NOSUID or MNT_UNION or MNT_SUJ or
+ MNT_ASYNC or MNT_EXRDONLY or MNT_EXPORTED or
+ MNT_DEFEXPORTED or MNT_EXPORTANON or MNT_EXKERB or
+ MNT_LOCAL or MNT_USER or MNT_QUOTA or
+ MNT_ROOTFS or MNT_NOATIME or MNT_NOCLUSTERR or
+ MNT_NOCLUSTERW or MNT_SUIDDIR or MNT_SOFTDEP or
+ MNT_IGNORE or MNT_EXPUBLIC or MNT_NOSYMFOLLOW or
+ MNT_GJOURNAL or MNT_MULTILABEL or MNT_ACLS or
+ MNT_NFS4ACLS);
+
MNT_UPDATEMASK=(MNT_NOSUID or MNT_NOEXEC or
MNT_SYNCHRONOUS or MNT_UNION or MNT_ASYNC or
MNT_NOATIME or
@@ -202,7 +223,8 @@ type
pp_mount=^p_mount;
p_mount=^mount;
- p_statfs=^statfs;
+ pp_statfs=^p_statfs;
+ p_statfs=^t_statfs;
p_vfsconf=^vfsconf;
vfs_cmount_t =function (ma,data:Pointer;flags:QWORD):Integer;
@@ -263,7 +285,7 @@ type
vfc_list :TAILQ_ENTRY ; // list of vfscons
end;
- statfs=packed record
+ t_statfs=packed record
f_version :DWORD; // structure version number
f_type :DWORD; // type of filesystem
f_flags :QWORD; // copy of mount exported flags
@@ -321,7 +343,7 @@ type
mnt_opt :p_vfsoptlist;// current mount options
mnt_optnew :p_vfsoptlist;// new options passed to fs
mnt_maxsymlinklen :Integer ;// max size of short symlink
- mnt_stat :statfs ;// cache of filesystem stats
+ mnt_stat :t_statfs ;// cache of filesystem stats
mnt_data :Pointer ;// private data
mnt_time :time_t ;// last time written
mnt_iosize_max :Integer ;// max size for clusters, etc
@@ -344,6 +366,9 @@ type
end;
const
+ VFS_NOTIFY_UPPER_RECLAIM=1;
+ VFS_NOTIFY_UPPER_UNLINK =2;
+
VFS_VERSION=$19660120;
var
@@ -352,6 +377,8 @@ var
VFS_Giant:mtx;
+function MNT_SHARED_WRITES(mp:p_mount):Boolean; inline;
+
procedure MNT_ILOCK(mp:p_mount); inline;
function MNT_ITRYLOCK(mp:p_mount):Boolean; inline;
procedure MNT_IUNLOCK(mp:p_mount); inline;
@@ -370,10 +397,26 @@ function VFS_MOUNT(mp:p_mount):Integer;
function VFS_UNMOUNT(mp:p_mount;FORCE:Integer):Integer;
function VFS_ROOT(mp:p_mount;flags:Integer;vpp:pp_vnode):Integer;
function VFS_STATFS(mp:p_mount;sbp:p_statfs):Integer;
+function VFS_SYNC(mp:p_mount;WAIT:Integer):Integer;
function VFS_VGET(mp:p_mount;ino:QWORD;flags:Integer;vpp:pp_vnode):Integer;
+procedure VFS_RECLAIM_LOWERVP(mp:p_mount;vp:p_vnode);
+procedure VFS_UNLINK_LOWERVP(mp:p_mount;vp:p_vnode);
+procedure VFS_KNOTE_LOCKED(vp:p_vnode;hint:Integer);
+procedure VFS_KNOTE_UNLOCKED(vp:p_vnode;hint:Integer);
implementation
+function MNT_SHARED_WRITES(mp:p_mount):Boolean; inline;
+begin
+ if (mp<>nil) then
+ begin
+ Result:=((mp^.mnt_kern_flag and MNTK_SHARED_WRITES)<>0);
+ end else
+ begin
+ Result:=False;
+ end;
+end;
+
procedure MNT_ILOCK(mp:p_mount); inline;
begin
mtx_lock(mp^.mnt_mtx);
@@ -484,6 +527,15 @@ begin
VFS_EPILOGUE(_enable_stops);
end;
+function VFS_SYNC(mp:p_mount;WAIT:Integer):Integer;
+var
+ _enable_stops:Boolean;
+begin
+ _enable_stops:=VFS_PROLOGUE(MP);
+ Result:=mp^.mnt_op^.vfs_sync(mp,WAIT);
+ VFS_EPILOGUE(_enable_stops);
+end;
+
function VFS_VGET(mp:p_mount;ino:QWORD;flags:Integer;vpp:pp_vnode):Integer;
var
_enable_stops:Boolean;
@@ -493,6 +545,41 @@ begin
VFS_EPILOGUE(_enable_stops);
end;
+procedure VFS_RECLAIM_LOWERVP(mp:p_mount;vp:p_vnode);
+var
+ _enable_stops:Boolean;
+begin
+ if (mp^.mnt_op^.vfs_reclaim_lowervp<>nil) then
+ begin
+ _enable_stops:=VFS_PROLOGUE(MP);
+ mp^.mnt_op^.vfs_reclaim_lowervp(mp,vp);
+ VFS_EPILOGUE(_enable_stops);
+ end;
+end;
+
+procedure VFS_UNLINK_LOWERVP(mp:p_mount;vp:p_vnode);
+var
+ _enable_stops:Boolean;
+begin
+ if (mp^.mnt_op^.vfs_unlink_lowervp<>nil) then
+ begin
+ _enable_stops:=VFS_PROLOGUE(MP);
+ mp^.mnt_op^.vfs_unlink_lowervp(mp,vp);
+ VFS_EPILOGUE(_enable_stops);
+ end;
+end;
+
+procedure VFS_KNOTE_LOCKED(vp:p_vnode;hint:Integer);
+begin
+ //if ((vp^.v_vflag and VV_NOKNOTE)=0) then
+ // VN_KNOTE(vp, hint, KNF_LISTLOCKED);
+end;
+
+procedure VFS_KNOTE_UNLOCKED(vp:p_vnode;hint:Integer);
+begin
+ //if ((vp^.v_vflag and VV_NOKNOTE)=0) then
+ // VN_KNOTE(vp, hint, 0);
+end;
initialization
mtx_init(mountlist_mtx);
diff --git a/sys/vfs/vnamei.pas b/sys/vfs/vnamei.pas
index a5abf529..0785c81f 100644
--- a/sys/vfs/vnamei.pas
+++ b/sys/vfs/vnamei.pas
@@ -140,7 +140,7 @@ type
ni_cnd:componentname;
end;
-function NDHASGIANT(ndp:p_nameidata):Boolean; inline;
+function NDHASGIANT(ndp:p_nameidata):Integer; inline;
procedure NDINIT_ALL(
ndp:p_nameidata;
@@ -191,9 +191,9 @@ procedure NDINIT_ATVP(
implementation
-function NDHASGIANT(ndp:p_nameidata):Boolean; inline;
+function NDHASGIANT(ndp:p_nameidata):Integer; inline;
begin
- Result:=(ndp^.ni_cnd.cn_flags and GIANTHELD)<>0;
+ Result:=ord((ndp^.ni_cnd.cn_flags and GIANTHELD)<>0);
end;
procedure NDINIT_ALL(
@@ -216,7 +216,7 @@ begin
ndp^.ni_strictrelative:=0;
ndp^.ni_rightsneeded :=rights;
ndp^.ni_baserights :=0;
- ndp^.ni_cnd.cn_thread:=td;
+ ndp^.ni_cnd.cn_thread :=td;
end;
procedure NDINIT(
diff --git a/sys/vfs/vstat.pas b/sys/vfs/vstat.pas
index df040786..d366344d 100644
--- a/sys/vfs/vstat.pas
+++ b/sys/vfs/vstat.pas
@@ -68,8 +68,20 @@ const
S_IFSOCK =&0140000; // socket
S_ISVTX =&0001000; // save swapped text even after use
+ S_ISUID =&0004000; // set user id on execution
+ S_ISGID =&0002000; // set group id on execution
+ S_ISTXT =&0001000; // sticky bit
+
+ S_IFWHT =&0160000; // whiteout
+
+ ACCESSPERMS=(S_IRWXU or S_IRWXG or S_IRWXO); // 0777
+ ALLPERMS =(S_ISUID or S_ISGID or S_ISTXT or S_IRWXU or S_IRWXG or S_IRWXO); // 7777
+ DEFFILEMODE=(S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH or S_IWOTH); // 0666
+
S_BLKSIZE=512; // block size used in the stat struct
+ NODEV=-1; // non-existent device
+
{
* Definitions of flags stored in file flags word.
*
diff --git a/sys/vm/kern_resource.pas b/sys/vm/kern_resource.pas
index cf691c52..db485c9a 100644
--- a/sys/vm/kern_resource.pas
+++ b/sys/vm/kern_resource.pas
@@ -6,7 +6,8 @@ unit kern_resource;
interface
uses
- vmparam;
+ vmparam,
+ vfile;
const
RLIMIT_CPU = 0; // maximum cpu time in seconds
@@ -37,6 +38,7 @@ begin
RLIMIT_STACK :Result:=MAXSSIZ;
RLIMIT_MEMLOCK:Result:=pageablemem;
RLIMIT_VMEM :Result:=pageablemem;
+ RLIMIT_NOFILE :Result:=maxfilesperproc;
else;
end;
end;
@@ -49,6 +51,7 @@ begin
RLIMIT_STACK :Result:=MAXSSIZ;
RLIMIT_MEMLOCK:Result:=pageablemem;
RLIMIT_VMEM :Result:=pageablemem;
+ RLIMIT_NOFILE :Result:=maxfilesperproc;
else;
end;
end;