FPPS4/sys/vfs/kern_descrip.pas

1677 lines
34 KiB
Plaintext

unit kern_descrip;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
kern_param,
kern_thr,
kern_proc,
kern_id,
sys_event,
vfile,
vstat,
vuio,
vcapability,
vfcntl,
vfilio,
vmount,
vnode,
vsocketvar;
const
FGET_GETCAP=$00000001;
function kern_close(fd:Integer):Integer;
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:Pointer):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);
function fdrop(fp:p_file):Integer;
function fget(fd:Integer;
rights:cap_rights_t;
fpp:pp_file):Integer;
function fget_mmap(fd:Integer;
rights:cap_rights_t;
maxprotp:PByte;
fpp:pp_file):Integer;
function fget_read(fd:Integer;
rights:cap_rights_t;
fpp:pp_file):Integer;
function fget_write(fd:Integer;
rights:cap_rights_t;
fpp:pp_file):Integer;
function fgetcap(fd:Integer;
fpp:pp_file):Integer;
function fgetvp(fd:Integer;
rights:cap_rights_t;
vpp:pp_vnode):Integer;
function fgetvp_rights(fd:Integer;
need:cap_rights_t;
have:p_cap_rights_t;
vpp:pp_vnode):Integer;
function fgetvp_read(fd:Integer;
rights:cap_rights_t;
vpp:pp_vnode):Integer;
function fgetvp_exec(fd:Integer;
rights:cap_rights_t;
vpp:pp_vnode):Integer;
function fgetvp_write(fd:Integer;
rights:cap_rights_t;
vpp:pp_vnode):Integer;
function fgetsock(fd:Integer;
rights:cap_rights_t;
spp:pp_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);
procedure fdcloseexec();
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:p_knote):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;
function invfo_chmod(fp:p_file;mode:mode_t):Integer;
function invfo_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
);
procedure fildesc_drvinit(); //SYSINIT(fildescdev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, fildesc_drvinit, NULL);
implementation
uses
atomic,
errno,
systm,
vfiledesc,
vfs_vnops,
vnode_if,
sys_resource,
kern_resource,
kern_mtx,
sys_conf;
//
function cap_funwrap(fp_cap:p_file;rights:cap_rights_t;fpp:pp_file):Integer; external;
function cap_rights(fp_cap:p_file):cap_rights_t; external;
function cap_funwrap_mmap(fp_cap:p_file;rights:cap_rights_t;maxprotp:PByte;fpp:pp_file):Integer; external;
//
function badfo_readwrite(fp:p_file;uio:p_uio;flags:Integer):Integer;
begin
Exit(EBADF);
end;
function badfo_truncate(fp:p_file;length:Int64):Integer;
begin
Exit(EINVAL);
end;
function badfo_ioctl(fp:p_file;com:QWORD;data:Pointer):Integer;
begin
Exit(EBADF);
end;
function badfo_poll(fp:p_file;events:Integer):Integer;
begin
Exit(0);
end;
function badfo_kqfilter(fp:p_file;kn:p_knote):Integer;
begin
Exit(EBADF);
end;
function badfo_stat(fp:p_file;sb:p_stat):Integer;
begin
Exit(EBADF);
end;
function badfo_close(fp:p_file):Integer;
begin
Exit(EBADF);
end;
function badfo_chmod(fp:p_file;mode:mode_t):Integer;
begin
Exit(EBADF);
end;
function badfo_chown(fp:p_file;uid:uid_t;gid:gid_t):Integer;
begin
Exit(EBADF);
end;
function invfo_chmod(fp:p_file;mode:mode_t):Integer;
begin
Exit(EINVAL);
end;
function invfo_chown(fp:p_file;uid:uid_t;gid:gid_t):Integer;
begin
Exit(EINVAL);
end;
{
* System calls on descriptors.
}
function sys_getdtablesize():Integer;
begin
//priv_check(td,683);
Exit(EPERM);
curkthread^.td_retval[0]:=lim_cur(RLIMIT_NOFILE);
Exit(0);
end;
// Flags for do_dup()
const
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
//priv_check(td,683);
Exit(EPERM);
Exit(do_dup(DUP_FIXED, from, _to, @curkthread^.td_retval));
end;
{
* Duplicate a file descriptor.
}
function sys_dup(u_fd:Integer):Integer;
begin
//priv_check(td,688);
Exit(EPERM);
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
//if (priv_check(td,683) <> 0) then
if (cmd > 13) or (($3818 shr (cmd and $1f) and 1)=0) then
begin
Exit(EINVAL);
end;
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
begin
Exit(error);
end;
error:=kern_fcntl(fd, cmd, arg);
if (error<>0) then
begin
Exit(error);
end;
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
fp_fromcap:p_file;
err:Integer;
begin
fpp^:=fget_unlocked(fd);
if (fpp^=nil) then
begin
Exit(EBADF);
end;
if (fpp^^.f_type=DTYPE_CAPABILITY) then
begin
fp_fromcap:=nil;
err:=cap_funwrap(fpp^, rights, @fp_fromcap);
if (err<>0) then
begin
fdrop(fpp^);
fpp^:=nil;
Exit(err);
end;
if (fpp^<>fp_fromcap) then
begin
fhold(fp_fromcap);
fdrop(fpp^);
fpp^:=fp_fromcap;
end;
end;
Exit(0);
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
atomic_set_int(@fp^.f_exclose,UF_EXCLOSE);
end else
begin
atomic_clear_int(@fp^.f_exclose,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_proc.p_flag:=p_proc.p_flag or P_ADVLOCK;
PROC_UNLOCK();
error:=VOP_ADVLOCK(vp, @p_proc, 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_proc.p_flag:=p_proc.p_flag or P_ADVLOCK;
PROC_UNLOCK();
error:=VOP_ADVLOCK(vp, @p_proc, F_SETLK, flp, flg);
end;
F_UNLCK:
begin
error:=VOP_ADVLOCK(vp, @p_proc, 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_proc, 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_proc, 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,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
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
* Return EINVAL when the new descriptor is out of bounds.
}
if (old < 0) then
begin
Exit(EBADF);
end;
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;
atomic_set_int(@fp^.f_exclose,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); //<-id_new
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(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; public;
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;
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_proc.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_proc, F_UNLCK, @lf, F_POSIX);
end;
VFS_UNLOCK_GIANT(vfslocked);
end;
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:Pointer):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
//priv_check(td,683);
Exit(EPERM);
td:=curkthread;
error:=fget(fd, CAP_FPATHCONF, @fp);
if (error<>0) then
begin
Exit(error);
end;
{ 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,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
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;
function falloc_noinstall(resultfp:pp_file):Integer;
var
fp:p_file;
begin
Assert(resultfp<>nil,'resultfp=nil');
System.InterlockedIncrement(openfiles);
fp:=AllocMem(SizeOf(t_file));
if (fp=nil) then Exit(ENOMEM);
fp^.desc.refs:=1;
fp^.desc.free:=@_fdrop;
fp^.f_ops :=@badfileops;
fp^.f_data :=nil;
fp^.f_vnode :=nil;
resultfp^:=fp;
Exit(0);
end;
function finstall(fp:p_file;fd:PInteger;flags:Integer):Integer;
begin
Assert(fd<>nil,'fd=nil');
Assert(fp<>nil,'fp=nil');
if not id_new(@fd_table.fd_ofiles,@fp^.desc,fd) then
begin
Exit(ENFILE);
end;
if ((flags and O_CLOEXEC)<>0) then
begin
atomic_set_int(@fp^.f_exclose,UF_EXCLOSE);
end;
Exit(0);
end;
function falloc(resultfp:pp_file;resultfd:PInteger;flags:Integer):Integer;
var
fp:p_file;
error,fd:Integer;
begin
error:=falloc_noinstall(@fp);
if (error<>0) then
begin
Exit(error); { no reference held on error }
end;
error:=finstall(fp,@fd,flags);
if (error<>0) then
begin
_fdrop(fp); { one reference (fp only) }
Exit(error);
end;
if (resultfp<>nil) then
resultfp^:=fp { copy out result }
else
fdrop(fp); { release local reference }
if (resultfd<>nil) then
begin
resultfd^:=fd;
end;
Exit(0);
end;
procedure _fdrop(data:pointer);
var
fp:p_file;
begin
fp:=data;
if (fp^.f_ops<>@badfileops) then
begin
fo_close(fp);
end;
System.InterlockedDecrement(openfiles);
FreeMem(fp^.f_advice);
FreeMem(fp);
end;
procedure fhold(fp:p_file);
begin
id_acqure(@fp^.desc);
end;
function fdrop(fp:p_file):Integer;
begin
id_release(@fp^.desc);
Result:=0;
end;
function fget_unlocked(fd:Integer):p_file;
begin
if (fd<0) then Exit(nil);
Result:=p_file(id_get(@fd_table.fd_ofiles,fd));
end;
function _fget(fd:Integer;
fpp:pp_file;
flags:Integer;
needrights:cap_rights_t;
haverightsp:p_cap_rights_t;
maxprotp:PByte;
fget_flags:Integer):Integer;
var
fp:p_file;
fp_fromcap:p_file;
error:Integer;
begin
fpp^:=nil;
fp:=fget_unlocked(fd);
if (fp=nil) then
Exit(EBADF);
if (fp^.f_ops=@badfileops) then
begin
fdrop(fp);
Exit(EBADF);
end;
{
* If this is a capability, what rights does it have?
}
if (haverightsp<>nil) then
begin
if (fp^.f_type=DTYPE_CAPABILITY) then
haverightsp^:=cap_rights(fp)
else
haverightsp^:=CAP_MASK_VALID;
end;
{
* If a capability has been requested, Exitthe capability directly.
* Otherwise, check capability rights, extract the underlying object,
* and check its access flags.
}
if ((fget_flags and FGET_GETCAP)<>0) then
begin
if (fp^.f_type<>DTYPE_CAPABILITY) then
begin
fdrop(fp);
Exit(EINVAL);
end;
end else
begin
if (maxprotp=nil) then
error:=cap_funwrap(fp, needrights,@fp_fromcap)
else
error:=cap_funwrap_mmap(fp, needrights, maxprotp,@fp_fromcap);
if (error<>0) then
begin
fdrop(fp);
Exit(error);
end;
{
* If we've unwrapped a file, drop the original capability
* and hold the new descriptor. fp after this point refers to
* the actual (unwrapped) object, not the capability.
}
if (fp<>fp_fromcap) then
begin
fhold(fp_fromcap);
fdrop(fp);
fp:=fp_fromcap;
end;
end;
{
* FREAD and FWRITE failure return EBADF as per POSIX.
}
error:=0;
case (flags) of
FREAD,
FWRITE:
begin
if ((fp^.f_flag and flags)=0) then
error:=EBADF;
end;
FEXEC:
begin
if ((fp^.f_flag and (FREAD or FEXEC))=0) or
((fp^.f_flag and FWRITE)<>0) then
error:=EBADF;
end;
0:;
else
Assert(false,'wrong flags');
end;
if (error<>0) then
begin
fdrop(fp);
Exit(error);
end;
fpp^:=fp;
Exit(0);
end;
function fget(fd:Integer;
rights:cap_rights_t;
fpp:pp_file):Integer;
begin
Exit(_fget(fd, fpp, 0, rights, nil, nil, 0));
end;
function fget_mmap(fd:Integer;
rights:cap_rights_t;
maxprotp:PByte;
fpp:pp_file):Integer;
begin
Exit(_fget(fd, fpp, 0, rights, nil, maxprotp, 0));
end;
function fget_read(fd:Integer;
rights:cap_rights_t;
fpp:pp_file):Integer;
begin
Exit(_fget(fd, fpp, FREAD, rights, nil, nil, 0));
end;
function fget_write(fd:Integer;
rights:cap_rights_t;
fpp:pp_file):Integer;
begin
Exit(_fget(fd, fpp, FWRITE, rights, nil, nil, 0));
end;
{
* Unlike the other fget() calls, which accept and check capability rights
* but never Exitcapabilities, fgetcap() Exits the capability but doesn't
* check capability rights.
}
function fgetcap(fd:Integer;
fpp:pp_file):Integer;
begin
Exit(_fget(fd, fpp, 0, 0, nil, nil, FGET_GETCAP));
end;
{
* Like fget() but loads the underlying vnode, or Exits an error if the
* descriptor does not represent a vnode. Note that pipes use vnodes but
* never have VM objects. The Exited vnode will be vref()'d.
*
* XXX: what about the unused flags ?
}
function _fgetvp(fd:Integer;
flags:Integer;
needrights:cap_rights_t;
haverightsp:p_cap_rights_t;
vpp:pp_vnode):Integer;
var
fp:p_file;
error:Integer;
begin
vpp^:=nil;
error:=_fget(fd, @fp, flags, needrights, haverightsp,nil, 0);
if (error<>0) then
Exit(error);
if (fp^.f_vnode=nil) then
begin
error:=EINVAL;
end else
begin
vpp^:=fp^.f_vnode;
vref(vpp^);
end;
fdrop(fp);
Exit(error);
end;
function fgetvp(fd:Integer;
rights:cap_rights_t;
vpp:pp_vnode):Integer;
begin
Exit(_fgetvp(fd, 0, rights, nil, vpp));
end;
function fgetvp_rights(fd:Integer;
need:cap_rights_t;
have:p_cap_rights_t;
vpp:pp_vnode):Integer;
begin
Exit(_fgetvp(fd, 0, need, have, vpp));
end;
function fgetvp_read(fd:Integer;
rights:cap_rights_t;
vpp:pp_vnode):Integer;
begin
Exit(_fgetvp(fd, FREAD, rights, nil, vpp));
end;
function fgetvp_exec(fd:Integer;
rights:cap_rights_t;
vpp:pp_vnode):Integer;
begin
Exit(_fgetvp(fd, FEXEC, rights, nil, vpp));
end;
function fgetvp_write(fd:Integer;
rights:cap_rights_t;
vpp:pp_vnode):Integer;
begin
Exit(_fgetvp(fd, FWRITE, rights, nil, vpp));
end;
{
* Like fget() but loads the underlying socket, or Exits an error if the
* descriptor does not represent a socket.
*
* We bump the ref count on the Exited socket. XXX Also obtain the SX lock
* in the future.
*
* Note: fgetsock() and fputsock() are deprecated, as consumers should rely
* on their file descriptor reference to prevent the socket from being free'd
* during use.
}
function fgetsock(fd:Integer;
rights:cap_rights_t;
spp:pp_socket;
fflagp:PDWORD):Integer;
var
fp:p_file;
error:Integer;
begin
spp^:=nil;
if (fflagp<>nil) then
fflagp^:=0;
error:=_fget(fd, @fp, 0, rights, nil, nil, 0);
if (error<>0) then
Exit(error);
if (fp^.f_type<>DTYPE_SOCKET) then
begin
error:=ENOTSOCK;
end else
begin
spp^:=fp^.f_data;
if (fflagp<>nil) then
fflagp^:=fp^.f_flag;
SOCK_LOCK(spp^);
soref(spp^);
SOCK_UNLOCK(spp^);
end;
fdrop(fp);
Exit(error);
end;
{
* Close any files on exec?
}
procedure fdcloseexec();
var
i,count:Integer;
fp:p_file;
begin
FILEDESC_XLOCK(@fd_table);
count:=fd_table.fd_lastfile;
if (count<>0) then
For i:=0 to count-1 do
begin
fp:=p_file(id_get(@fd_table.fd_ofiles,i));
if (fp<>nil) then
begin
if (fp^.f_type=DTYPE_MQUEUE) or
((fp^.f_exclose and UF_EXCLOSE)<>0) then
begin
fdrop(fp);
knote_fdclose(i);
fp:=nil;
if id_del(@fd_table.fd_ofiles,i,@fp) then
if (fp<>nil) then
begin
if (fp^.f_type=DTYPE_MQUEUE) then
begin
//mq_fdclose(td, i, fp);
end;
FILEDESC_XUNLOCK(@fd_table);
closef(fp);
FILEDESC_XLOCK(@fd_table);
end;
end;
end;
end;
FILEDESC_XUNLOCK(@fd_table);
end;
{
* File Descriptor pseudo-device driver (/dev/fd/).
*
* Opening minor device N dup()s the file (if any) connected to file
* descriptor N belonging to the calling process. Note that this driver
* consists of only the ``open()'' routine, because all subsequent
* references to this file will be direct to the other driver.
*
* XXX: we could give this one a cloning event handler if necessary.
}
{ ARGSUSED }
function fdopen(dev:p_cdev;mode,_type:Integer):Integer;
var
td:p_kthread;
begin
td:=curkthread;
{
* XXX Kludge: set curthread^.td_dupfd to contain the value of the
* the file descriptor being sought for duplication. The error
* Exitensures that the vnode for this device will be released
* by vn_open. Open will detect this special error and take the
* actions in dupfdopen below. Other callers of vn_open or VOP_OPEN
* will simply report the error.
}
td^.td_dupfd:=dev2unit(dev);
Exit(ENODEV);
end;
const
fildesc_cdevsw:t_cdevsw=(
d_version :D_VERSION;
d_flags :0;
d_name :'FD';
d_open :@fdopen;
);
procedure fildesc_drvinit();
var
dev:p_cdev;
begin
dev:=make_dev_credf(MAKEDEV_ETERNAL, @fildesc_cdevsw, 0, UID_ROOT, GID_WHEEL, &0666, 'fd/0',[]);
make_dev_alias(dev, 'stdin',[]);
dev:=make_dev_credf(MAKEDEV_ETERNAL, @fildesc_cdevsw, 1, UID_ROOT, GID_WHEEL, &0666, 'fd/1',[]);
make_dev_alias(dev, 'stdout',[]);
dev:=make_dev_credf(MAKEDEV_ETERNAL, @fildesc_cdevsw, 2, UID_ROOT, GID_WHEEL, &0666, 'fd/2',[]);
make_dev_alias(dev, 'stderr',[]);
end;
end.