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;