FPPS4/sys/vfs/vfs_default.pas

1168 lines
26 KiB
Plaintext

unit vfs_default;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
sysutils,
kern_param,
vnode,
vnode_if,
vdirent,
vuio,
vfile,
vmount,
vfs_mount,
vnamei,
vfcntl,
vpoll,
kern_thr,
kern_mtx;
function vop_eopnotsupp(ap:Pointer):Integer;
function vop_ebadf(ap:Pointer):Integer;
function vop_enotty(ap:Pointer):Integer;
function vop_einval(ap:Pointer):Integer;
function vop_enoent(ap:Pointer):Integer;
function VOP_NULL(ap:Pointer):Integer;
function vop_panic(ap:Pointer):Integer;
function vop_nolookup(ap:p_vop_lookup_args):Integer;
function vop_norename(ap:p_vop_rename_args):Integer;
function vop_nostrategy(ap:p_vop_strategy_args):Integer;
function get_next_dirent(vp:p_vnode;
dpp:PPointer;
dirbuf:PByte;
dirbuflen:Integer;
off:PQWORD;
cpos:PPByte;
len:PInteger;
eofflag:PInteger):Integer;
function dirent_exists(vp:p_vnode;dirname:PChar):Integer;
function vop_stdaccess(ap:p_vop_access_args):Integer;
function vop_stdaccessx(ap:p_vop_accessx_args):Integer;
function vop_stdadvlock(ap:p_vop_advlock_args):Integer;
function vop_stdadvlockasync(ap:p_vop_advlockasync_args):Integer;
function vop_stdadvlockpurge(ap:p_vop_advlockpurge_args):Integer;
function vop_stdpathconf(ap:p_vop_pathconf_args):Integer;
function lockmgr(lk:p_mtx;flags:Integer;ilk:p_mtx):Integer;
function vop_stdlock(ap:p_vop_lock1_args):Integer;
function vop_stdunlock(ap:p_vop_unlock_args):Integer;
function vop_stdislocked(ap:p_vop_islocked_args):Integer;
function vop_nopoll(ap:p_vop_poll_args):Integer;
function vop_stdpoll(ap:p_vop_poll_args):Integer;
function vop_stdgetwritemount(ap:p_vop_getwritemount_args):Integer;
function vop_stdbmap(ap:p_vop_bmap_args):Integer;
function vop_stdfsync(ap:p_vop_fsync_args):Integer;
function vop_stdgetpages(ap:p_vop_getpages_args):Integer;
function vop_stdkqfilter(ap:p_vop_kqfilter_args):Integer;
function vop_stdputpages(ap:p_vop_putpages_args):Integer;
function vop_stdvptofh(ap:p_vop_vptofh_args):Integer;
function vop_stdvptocnp(ap:p_vop_vptocnp_args):Integer;
function vop_stdallocate(ap:p_vop_allocate_args):Integer;
function vop_stdunp_bind(ap:p_vop_unp_bind_args):Integer;
function vop_stdunp_connect(ap:p_vop_unp_connect_args):Integer;
function vop_stdunp_detach(ap:p_vop_unp_detach_args):Integer;
function vfs_stdroot(mp:p_mount;flags:Integer;vpp:pp_vnode):Integer;
function vfs_stdstatfs(mp:p_mount;sbp:p_statfs):Integer;
function vfs_stdquotactl(mp:p_mount;cmds:Integer;uid:uid_t;arg:Pointer):Integer;
function vfs_stdsync(mp:p_mount;waitfor:Integer):Integer;
function vfs_stdnosync(mp:p_mount;waitfor:Integer):Integer;
function vfs_stdvget(mp:p_mount;ino:DWORD;flags:Integer;vpp:pp_vnode):Integer;
function vfs_stdfhtovp(mp:p_mount;fhp:p_fid;flags:Integer;vpp:pp_vnode) :Integer;
function vfs_stdinit(vfsp:p_vfsconf):Integer;
function vfs_stduninit(vfsp:p_vfsconf):Integer;
function vfs_stdextattrctl(mp:p_mount;cmd:Integer;filename_vp:p_vnode;attrnamespace:Integer;attrname:PChar):Integer;
function vfs_stdsysctl(mp:p_mount;op:Integer;req:Pointer):Integer;
const
DIRENT_MINSIZE=SizeOf(t_dirent)-(t_dirent.MAXNAMLEN+1)+4;
{
* This vnode table stores what we want to do if the filesystem doesn't
* implement a particular VOP.
*
* If there is no specific entry here, we will return EOPNOTSUPP.
*
* Note that every filesystem has to implement either vop_access
* or vop_accessx; failing to do so will result in immediate crash
* due to stack overflow, as vop_stdaccess() calls vop_stdaccessx(),
* which calls vop_stdaccess() etc.
}
const
default_vnodeops:vop_vector=(
vop_default :nil ;
vop_bypass :@VOP_EOPNOTSUPP ;
vop_islocked :@vop_stdislocked ;
vop_lookup :@vop_nolookup ;
vop_create :nil ;
vop_whiteout :nil ;
vop_mknod :nil ;
vop_open :@VOP_NULL ;
vop_close :@VOP_NULL ;
vop_access :@vop_stdaccess ;
vop_accessx :@vop_stdaccessx ;
vop_getattr :nil ;
vop_setattr :nil ;
vop_markatime :nil ;
vop_read :nil ;
vop_write :nil ;
vop_ioctl :@VOP_ENOTTY ;
vop_poll :@vop_nopoll ;
vop_kqfilter :@vop_stdkqfilter ;
vop_revoke :@VOP_PANIC ;
vop_fsync :@VOP_NULL ;
vop_remove :nil ;
vop_link :nil ;
vop_rename :@vop_norename ;
vop_mkdir :nil ;
vop_rmdir :nil ;
vop_symlink :nil ;
vop_readdir :nil ;
vop_readlink :@VOP_EINVAL ;
vop_inactive :@VOP_NULL ;
vop_reclaim :nil ;
vop_lock1 :@vop_stdlock ;
vop_unlock :@vop_stdunlock ;
vop_bmap :@vop_stdbmap ;
vop_strategy :@vop_nostrategy ;
vop_getwritemount :@vop_stdgetwritemount ;
vop_print :nil ;
vop_pathconf :@VOP_EINVAL ;
vop_advlock :@vop_stdadvlock ;
vop_advlockasync :@vop_stdadvlockasync ;
vop_advlockpurge :@vop_stdadvlockpurge ;
vop_reallocblks :nil ;
vop_getpages :@vop_stdgetpages ;
vop_putpages :@vop_stdputpages ;
vop_vptofh :@vop_stdvptofh ;
vop_vptocnp :@vop_stdvptocnp ;
vop_allocate :@vop_stdallocate ;
vop_unp_bind :@vop_stdunp_bind ;
vop_unp_connect :@vop_stdunp_connect ;
vop_unp_detach :@vop_stdunp_detach ;
);
implementation
uses
errno,
vfs_subr,
vfs_vnops,
vsys_generic;
{
* Series of placeholder functions for various error returns for
* VOPs.
}
function vop_eopnotsupp(ap:Pointer):Integer;
begin
Exit(EOPNOTSUPP);
end;
function vop_ebadf(ap:Pointer):Integer;
begin
Exit(EBADF);
end;
function vop_enotty(ap:Pointer):Integer;
begin
Exit(ENOTTY);
end;
function vop_einval(ap:Pointer):Integer;
begin
Exit(EINVAL);
end;
function vop_enoent(ap:Pointer):Integer;
begin
Exit(ENOENT);
end;
function VOP_NULL(ap:Pointer):Integer;
begin
Exit(0);
end;
{
* Helper function to panic on some bad VOPs in some filesystems.
}
function vop_panic(ap:Pointer):Integer;
begin
Assert(false,'filesystem goof: vop_panic[%s]');
Exit(ENOENT);
end;
{
* vop_std<something> and vop_no<something> are default functions for use by
* filesystems that need the 'default reasonable' implementation for a
* particular operation.
*
* The documentation for the operations they implement exists (if it exists)
* in the VOP_<SOMETHING>(9) manpage (all uppercase).
}
{
* Default vop for filesystems that do not support name lookup
}
function vop_nolookup(ap:p_vop_lookup_args):Integer;
begin
ap^.a_vpp^:=nil;
Exit(ENOTDIR);
end;
{
* vop_norename:
*
* Handle unlock and reference counting for arguments of vop_rename
* for filesystems that do not implement rename operation.
}
function vop_norename(ap:p_vop_rename_args):Integer;
begin
vop_rename_fail(ap);
Exit(EOPNOTSUPP);
end;
{
* vop_nostrategy:
*
* Strategy routine for VFS devices that have none.
*
* BIO_ERROR and B_INVAL must be cleared prior to calling any strategy
* routine. Typically this is done for a BIO_READ strategy call.
* Typically B_INVAL is assumed to already be clear prior to a write
* and should not be cleared manually unless you just made the buffer
* invalid. BIO_ERROR should be cleared either way.
}
function vop_nostrategy(ap:p_vop_strategy_args):Integer;
begin
Writeln('No strategy for buffer at %p');
//ap^.a_bp^.b_ioflags:=ap^.a_bp^.b_ioflags or BIO_ERROR;
//ap^.a_bp^.b_error:=EOPNOTSUPP;
//bufdone(ap^.a_bp);
Exit(EOPNOTSUPP);
end;
function get_next_dirent(vp:p_vnode;
dpp:PPointer;
dirbuf:PByte;
dirbuflen:Integer;
off:PQWORD;
cpos:PPByte;
len:PInteger;
eofflag:PInteger):Integer;
var
error, reclen:Integer;
uio:t_uio;
iov:iovec;
dp:p_dirent;
begin
Assert(VOP_ISLOCKED(vp)<>0,'vp %p is not locked');
Assert(vp^.v_type=VDIR,'vp %p is not a directory');
if (len^=0) then
begin
iov.iov_base:=dirbuf;
iov.iov_len:=dirbuflen;
uio.uio_iov :=@iov;
uio.uio_iovcnt:=1;
uio.uio_offset:=off^;
uio.uio_resid :=dirbuflen;
uio.uio_segflg:=UIO_SYSSPACE;
uio.uio_rw :=UIO_READ;
uio.uio_td :=curkthread;
eofflag^:=0;
//error:=mac_vnode_check_readdir(td^.td_ucred, vp);
//if (error=0) then
error:=VOP_READDIR(vp, @uio, eofflag, nil, nil);
if (error<>0) then Exit(error);
off^:=uio.uio_offset;
cpos^:=dirbuf;
len^:=(dirbuflen - uio.uio_resid);
if (len^=0) then
Exit(ENOENT);
end;
dp:=p_dirent(cpos^);
reclen:=dp^.d_reclen;
dpp^:=dp;
{ check for malformed directory.. }
if (reclen < DIRENT_MINSIZE) then
Exit(EINVAL);
cpos^:=cpos^+reclen;
len^:=len^-reclen;
Exit(0);
end;
{
* Check if a named file exists in a given directory vnode.
}
function dirent_exists(vp:p_vnode;dirname:PChar):Integer;
label
_out;
var
dirbuf,cpos:PByte;
error, eofflag, dirbuflen, len, found:Integer;
off:QWORD;
dp:p_dirent;
va:t_vattr;
begin
Assert(VOP_ISLOCKED(vp)<>0, 'vp %p is not locked');
Assert(vp^.v_type=VDIR, 'vp %p is not a directory');
found:=0;
error:=VOP_GETATTR(vp, @va);
if (error<>0) then
Exit(found);
dirbuflen:=DEV_BSIZE;
if (dirbuflen < va.va_blocksize) then
dirbuflen:=va.va_blocksize;
dirbuf:=AllocMem(dirbuflen);
off:=0;
len:=0;
repeat
error:=get_next_dirent(vp, @dp, dirbuf, dirbuflen, @off, @cpos, @len, @eofflag);
if (error<>0) then
goto _out;
if (dp^.d_type<>DT_WHT) and
(Strcomp(PChar(@dp^.d_name),PChar(dirname))=0) then
begin
found:=1;
goto _out;
end;
until not ((len>0) or (eofflag=0));
_out:
FreeMem(dirbuf);
Exit(found);
end;
function vop_stdaccess(ap:p_vop_access_args):Integer;
begin
Assert((ap^.a_accmode and (not (VEXEC or VWRITE or VREAD or VADMIN or VAPPEND)))=0, ('invalid bit in accmode'));
Exit(VOP_ACCESSX(ap^.a_vp, ap^.a_accmode));
end;
function vop_stdaccessx(ap:p_vop_accessx_args):Integer;
var
error:Integer;
accmode:accmode_t;
begin
accmode:=ap^.a_accmode;
error:=vfs_unixify_accmode(@accmode);
if (error<>0) then
Exit(error);
if (accmode=0) then
Exit(0);
Exit(VOP_ACCESS(ap^.a_vp, accmode));
end;
{
* Advisory record locking support
}
function vop_stdadvlock(ap:p_vop_advlock_args):Integer;
var
vp:p_vnode;
vattr:t_vattr;
error:Integer;
begin
vp:=ap^.a_vp;
if (ap^.a_fl^.l_whence=SEEK_END) then
begin
{
* The NFSv4 server must avoid doing a vn_lock() here, since it
* can deadlock the nfsd threads, due to a LOR. Fortunately
* the NFSv4 server always uses SEEK_SET and this code is
* only required for the SEEK_END case.
}
vn_lock(vp, LK_SHARED or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
error:=VOP_GETATTR(vp, @vattr);
VOP_UNLOCK(vp, 0);
if (error<>0) then
Exit(error);
end else
vattr.va_size:=0;
Exit(EOPNOTSUPP);
//Exit(lf_advlock(ap, @(vp^.v_lockf), vattr.va_size));
end;
function vop_stdadvlockasync(ap:p_vop_advlockasync_args):Integer;
var
vp:p_vnode;
vattr:t_vattr;
error:Integer;
begin
vp:=ap^.a_vp;
if (ap^.a_fl^.l_whence=SEEK_END) then
begin
{ The size argument is only needed for SEEK_END. }
vn_lock(vp, LK_SHARED or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
error:=VOP_GETATTR(vp, @vattr);
VOP_UNLOCK(vp, 0);
if (error<>0) then
begin
Exit(error);
end;
end else
begin
vattr.va_size:=0;
end;
Exit(EOPNOTSUPP);
//Exit(lf_advlockasync(ap, @(vp^.v_lockf), vattr.va_size));
end;
function vop_stdadvlockpurge(ap:p_vop_advlockpurge_args):Integer;
//var
// vp:p_vnode;
begin
//vp:=ap^.a_vp;
Exit(EOPNOTSUPP);
//lf_purgelocks(vp, @vp^.v_lockf);
//Exit(0);
end;
{
* vop_stdpathconf:
*
* Standard implementation of POSIX pathconf, to get information about limits
* for a filesystem.
* Override per filesystem for the case where the filesystem has smaller
* limits.
}
function vop_stdpathconf(ap:p_vop_pathconf_args):Integer;
begin
case (ap^.a_name) of
_PC_NAME_MAX:
begin
ap^.a_retval^:=NAME_MAX;
Exit(0);
end;
_PC_PATH_MAX:
begin
ap^.a_retval^:=PATH_MAX;
Exit(0);
end;
_PC_LINK_MAX:
begin
ap^.a_retval^:=LINK_MAX;
Exit(0);
end;
_PC_MAX_CANON:
begin
ap^.a_retval^:=MAX_CANON;
Exit(0);
end;
_PC_MAX_INPUT:
begin
ap^.a_retval^:=MAX_INPUT;
Exit(0);
end;
_PC_PIPE_BUF:
begin
ap^.a_retval^:=PIPE_BUF;
Exit(0);
end;
_PC_CHOWN_RESTRICTED:
begin
ap^.a_retval^:=1;
Exit(0);
end;
_PC_VDISABLE:
begin
ap^.a_retval^:=_POSIX_VDISABLE;
Exit(0);
end;
else
Exit(EINVAL);
end;
{ NOTREACHED }
end;
function lockmgr(lk:p_mtx;flags:Integer;ilk:p_mtx):Integer;
var
op:Integer;
begin
Result:=0;
if ((flags and LK_INTERLOCK)<>0) then
begin
mtx_unlock(ilk^);
end;
op:=(flags and LK_TYPE_MASK);
case op of
LK_SHARED, //read lock
LK_EXCLUSIVE: //write lock
begin
if ((flags and LK_NOWAIT)<>0) then //trylock
begin
if not mtx_trylock(lk^) then
begin
Result:=1;
end;
end else
begin
mtx_lock(lk^);
end;
end;
LK_RELEASE: //unlock
begin
mtx_unlock(lk^);
end;
LK_UPGRADE :;//read ->write
LK_DOWNGRADE:;//write->read
else
Assert(false,'TODO');
end;
end;
{
* Standard lock, unlock and islocked functions.
}
function vop_stdlock(ap:p_vop_lock1_args):Integer;
var
vp:p_vnode;
begin
vp:=ap^.a_vp;
//Writeln('vop_std lock:',HexStr(ap^.a_vp^.v_vnlock));
Result:=lockmgr(vp^.v_vnlock,ap^.a_flags,VI_MTX(vp));
//Exit(_lockmgr_args(vp^.v_vnlock, ap^.a_flags, VI_MTX(vp),
// LK_WMESG_DEFAULT, LK_PRIO_DEFAULT, LK_TIMO_DEFAULT, ap^.a_file,
// ap^.a_line));
end;
{ See above. }
function vop_stdunlock(ap:p_vop_unlock_args):Integer;
var
vp:p_vnode;
begin
vp:=ap^.a_vp;
//Writeln('vop_stdunlock:',HexStr(ap^.a_vp^.v_vnlock));
Result:=lockmgr(vp^.v_vnlock,ap^.a_flags or LK_RELEASE,VI_MTX(vp));
//Exit(lockmgr(vp^.v_vnlock, ap^.a_flags or LK_RELEASE, VI_MTX(vp)));
end;
{ See above. }
function vop_stdislocked(ap:p_vop_islocked_args):Integer;
begin
//Writeln('vop_stdislocked:',HexStr(ap^.a_vp^.v_vnlock),':',mtx_owned(ap^.a_vp^.v_vnlock^));
if mtx_owned(ap^.a_vp^.v_vnlock^) then
Exit(LK_EXCLUSIVE)
else
Exit(0);
//Exit(lockstatus(ap^.a_vp^.v_vnlock));
end;
{
* Exittrue for select/poll.
}
function vop_nopoll(ap:p_vop_poll_args):Integer;
begin
Exit(poll_no_poll(ap^.a_events));
end;
{
* Implement poll for local filesystems that support it.
}
function vop_stdpoll(ap:p_vop_poll_args):Integer;
begin
if ((ap^.a_events and (not POLLSTANDARD))<>0) then
Exit(vn_pollrecord(ap^.a_vp, ap^.a_events));
Exit(ap^.a_events and (POLLIN or POLLOUT or POLLRDNORM or POLLWRNORM));
end;
{
* Exit our mount point, as we will take charge of the writes.
}
function vop_stdgetwritemount(ap:p_vop_getwritemount_args):Integer;
var
mp:p_mount;
begin
{
* XXX Since this is called unlocked we may be recycled while
* attempting to ref the mount. If this is the case or mountpoint
* will be set to nil. We only have to prevent this call from
* returning with a ref to an incorrect mountpoint. It is not
* harmful to Exitwith a ref to our previous mountpoint.
}
mp:=ap^.a_vp^.v_mount;
if (mp<>nil) then
begin
vfs_ref(mp);
if (mp<>ap^.a_vp^.v_mount) then
begin
vfs_rel(mp);
mp:=nil;
end;
end;
(ap^.a_mpp)^:=mp;
Exit(0);
end;
{ XXX Needs good comment and VOP_BMAP(9) manpage }
function vop_stdbmap(ap:p_vop_bmap_args):Integer;
begin
//if (ap^.a_bop<>nil) then
// ap^.a_bop^:=@ap^.a_vp^.v_bufobj;
//if (ap^.a_bnp<>nil) then
// ap^.a_bnp^:=ap^.a_bn * btodb(ap^.a_vp^.v_mount^.mnt_stat.f_iosize);
//if (ap^.a_runp<>nil) then
// ap^.a_runp^:=0;
//if (ap^.a_runb<>nil) then
// ap^.a_runb^:=0;
Exit(0);
end;
function vop_stdfsync(ap:p_vop_fsync_args):Integer;
var
vp:p_vnode;
//struct buf *bp;
//struct bufobj *bo;
//struct buf *nbp;
error:Integer;
maxretry:Integer;
begin
vp:=ap^.a_vp;
error:=0;
maxretry:=1000; { large, arbitrarily chosen }
{
bo:=@vp^.v_bufobj;
BO_LOCK(bo);
loop1:
{
* MARK/SCAN initialization to avoid infinite loops.
}
TAILQ_FOREACH(bp, @bo^.bo_dirty.bv_hd, b_bobufs)
begin
bp^.b_vflags:=bp^.b_vflags and (not BV_SCANNED);
bp^.b_error:=0;
end;
{
* Flush all dirty buffers associated with a vnode.
}
loop2:
TAILQ_FOREACH_SAFE(bp, @bo^.bo_dirty.bv_hd, b_bobufs, nbp)
begin
if ((bp^.b_vflags and BV_SCANNED)<>0)
continue;
bp^.b_vflags:=bp^.b_vflags or BV_SCANNED;
if (BUF_LOCK(bp, LK_EXCLUSIVE or LK_NOWAIT, nil)) then
begin
if (ap^.a_waitfor<>MNT_WAIT)
continue;
if (BUF_LOCK(bp,
LK_EXCLUSIVE or LK_INTERLOCK or LK_SLEEPFAIL,
BO_MTX(bo))<>0) then
begin
BO_LOCK(bo);
goto loop1;
end;
BO_LOCK(bo);
end;
BO_UNLOCK(bo);
Assert(bp^.b_bufobj=bo,
('bp %p wrong b_bufobj %p should be %p',
bp, bp^.b_bufobj, bo));
if ((bp^.b_flags and B_DELWRI)=0) then
panic('fsync: not dirty');
if ((vp^.v_object<>nil) and (bp^.b_flags and B_CLUSTEROK)) then
begin
vfs_bio_awrite(bp);
end else
begin
bremfree(bp);
bawrite(bp);
end;
BO_LOCK(bo);
goto loop2;
end;
{
* If synchronous the caller expects us to completely resolve all
* dirty buffers in the system. Wait for in-progress I/O to
* complete (which could include background bitmap writes), then
* retry if dirty blocks still exist.
}
if (ap^.a_waitfor=MNT_WAIT) then
begin
bufobj_wwait(bo, 0, 0);
if (bo^.bo_dirty.bv_cnt > 0) then
begin
{
* If we are unable to write any of these buffers
* then we fail now rather than trying endlessly
* to write them out.
}
TAILQ_FOREACH(bp, @bo^.bo_dirty.bv_hd, b_bobufs)
if ((error:=bp^.b_error)=0) then
continue;
if (error=0) and (--maxretry >= 0) then
goto loop1;
error:=EAGAIN;
end;
end;
BO_UNLOCK(bo);
if (error=EAGAIN) then
vprint('fsync: giving up on dirty', vp);
}
Exit(error);
end;
{ XXX Needs good comment and more info in the manpage (VOP_GETPAGES(9)). }
function vop_stdgetpages(ap:p_vop_getpages_args):Integer;
begin
Exit(EOPNOTSUPP);
//Exit(vnode_pager_generic_getpages(ap^.a_vp, ap^.a_m, ap^.a_count, ap^.a_reqpage));
end;
function vop_stdkqfilter(ap:p_vop_kqfilter_args):Integer;
begin
Exit(vfs_kqfilter(ap));
end;
{ XXX Needs good comment and more info in the manpage (VOP_PUTPAGES(9)). }
function vop_stdputpages(ap:p_vop_putpages_args):Integer;
begin
Exit(EOPNOTSUPP);
//Exit(vnode_pager_generic_putpages(ap^.a_vp, ap^.a_m, ap^.a_count, ap^.a_sync, ap^.a_rtvals));
end;
function vop_stdvptofh(ap:p_vop_vptofh_args):Integer;
begin
Exit(EOPNOTSUPP);
end;
function vop_stdvptocnp(ap:p_vop_vptocnp_args):Integer;
label
_out;
var
vp:p_vnode;
dvp:pp_vnode;
buf:PChar;
buflen:PInteger;
dirbuf, cpos:PByte;
i, error, eofflag, dirbuflen, flags, locked, len, covered:Integer;
off:QWORD;
fileno:DWORD;
va:t_vattr;
nd:t_nameidata;
td:p_kthread;
dp:p_dirent;
mvp:p_vnode;
begin
vp:=ap^.a_vp;
dvp:=ap^.a_vpp;
buf:=ap^.a_buf;
buflen:=ap^.a_buflen;
i:=buflen^;
error:=0;
covered:=0;
td:=curkthread;
if (vp^.v_type<>VDIR) then
Exit(ENOENT);
error:=VOP_GETATTR(vp, @va);
if (error<>0) then
Exit(error);
VREF(vp);
locked:=VOP_ISLOCKED(vp);
VOP_UNLOCK(vp, 0);
NDINIT_ATVP(@nd, LOOKUP, FOLLOW or LOCKLEAF, UIO_SYSSPACE, '..', vp, td);
flags:=FREAD;
error:=vn_open_cred(@nd, @flags, 0, VN_OPEN_NOAUDIT, nil);
if (error<>0) then
begin
vn_lock(vp, locked or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
Exit(error);
end;
NDFREE(@nd, NDF_ONLY_PNBUF);
mvp:=nd.ni_vp;
dvp^:=mvp;
if (vp^.v_mount<>dvp^^.v_mount) and
((dvp^^.v_vflag and VV_ROOT)<>0) and
((p_mount(dvp^^.v_mount)^.mnt_flag and MNT_UNION)<>0) then
begin
dvp^:=p_mount(dvp^^.v_mount)^.mnt_vnodecovered;
VREF(mvp);
VOP_UNLOCK(mvp, 0);
vn_close(mvp, FREAD);
VREF(dvp^);
vn_lock(dvp^, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
covered:=1;
end;
fileno:=va.va_fileid;
dirbuflen:=DEV_BSIZE;
if (dirbuflen < va.va_blocksize) then
dirbuflen:=va.va_blocksize;
dirbuf:=AllocMem(dirbuflen);
if (dvp^^.v_type<>VDIR) then
begin
error:=ENOENT;
goto _out;
end;
off:=0;
len:=0;
repeat
{ call VOP_READDIR of parent }
error:=get_next_dirent(dvp^, @dp, dirbuf, dirbuflen, @off, @cpos, @len, @eofflag);
if (error<>0) then
goto _out;
if (dp^.d_type<>DT_WHT) and
(dp^.d_fileno=fileno) then
begin
if (covered<>0) then
begin
VOP_UNLOCK(dvp^, 0);
vn_lock(mvp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
if (dirent_exists(mvp, dp^.d_name)=0) then
begin
error:=ENOENT;
VOP_UNLOCK(mvp, 0);
vn_lock(dvp^, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
goto _out;
end;
VOP_UNLOCK(mvp, 0);
vn_lock(dvp^, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
end;
Dec(i,dp^.d_namlen);
if (i < 0) then
begin
error:=ENOMEM;
goto _out;
end;
if (dp^.d_namlen=1) and (dp^.d_name[0]='.') then
begin
error:=ENOENT;
end else
begin
Move(dp^.d_name, (buf + i)^, dp^.d_namlen);
error:=0;
end;
goto _out;
end;
until not ((len>0) or (eofflag=0));
error:=ENOENT;
_out:
FreeMem(dirbuf);
if (error=0) then
begin
buflen^:=i;
vref(dvp^);
end;
if (covered<>0) then
begin
vput(dvp^);
vrele(mvp);
end else
begin
VOP_UNLOCK(mvp, 0);
vn_close(mvp, FREAD);
end;
vn_lock(vp, locked or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
Exit(error);
end;
function vop_stdallocate(ap:p_vop_allocate_args):Integer;
label
_out;
var
aiov:iovec;
vattr:t_vattr;
vap:p_vattr;
auio:t_uio;
fsize, len, cur, offset:QWORD;
buf:PByte;
td:p_kthread;
vp:p_vnode;
iosize:QWORD;
error:Integer;
begin
buf:=nil;
error:=0;
td:=curkthread;
vap:=@vattr;
vp:=ap^.a_vp;
len:=ap^.a_len^;
offset:=ap^.a_offset^;
error:=VOP_GETATTR(vp, vap);
if (error<>0) then
goto _out;
fsize:=vap^.va_size;
iosize:=vap^.va_blocksize;
if (iosize=0) then
iosize:=BLKDEV_IOSIZE;
if (iosize > MAXPHYS) then
iosize:=MAXPHYS;
buf:=AllocMem(iosize);
if (offset + len > vap^.va_size) then
begin
{
* Test offset + len against the filesystem's maxfilesize.
}
VATTR_null(vap);
vap^.va_size:=offset + len;
error:=VOP_SETATTR(vp, vap);
if (error<>0) then
goto _out;
VATTR_null(vap);
vap^.va_size:=fsize;
error:=VOP_SETATTR(vp, vap);
if (error<>0) then
goto _out;
end;
repeat
{
* Read and write back anything below the nominal file
* size. There's currently no way outside the filesystem
* to know whether this area is sparse or not.
}
cur:=iosize;
if ((offset mod iosize)<>0) then
Dec(cur,(offset mod iosize));
if (cur > len) then
cur:=len;
if (offset < fsize) then
begin
aiov.iov_base :=buf;
aiov.iov_len :=cur;
auio.uio_iov :=@aiov;
auio.uio_iovcnt:=1;
auio.uio_offset:=offset;
auio.uio_resid :=cur;
auio.uio_segflg:=UIO_SYSSPACE;
auio.uio_rw :=UIO_READ;
auio.uio_td :=td;
error:=VOP_READ(vp, @auio, 0);
if (error<>0) then
break;
if (auio.uio_resid > 0) then
begin
FillChar((buf + cur - auio.uio_resid)^,auio.uio_resid,0);
end;
end else
begin
FillChar(buf^,cur,0);
end;
aiov.iov_base :=buf;
aiov.iov_len :=cur;
auio.uio_iov :=@aiov;
auio.uio_iovcnt:=1;
auio.uio_offset:=offset;
auio.uio_resid :=cur;
auio.uio_segflg:=UIO_SYSSPACE;
auio.uio_rw :=UIO_WRITE;
auio.uio_td :=td;
error:=VOP_WRITE(vp, @auio, 0);
if (error<>0) then
break;
Dec(len,cur);
Inc(offset,cur);
if (len=0) then
break;
//if (should_yield())
// break;
until false;
_out:
ap^.a_len^:=len;
ap^.a_offset^:=offset;
FreeMem(buf);
Exit(error);
end;
function vop_stdunp_bind(ap:p_vop_unp_bind_args):Integer;
begin
ap^.a_vp^.v_socket:=ap^.a_socket;
Exit(0);
end;
function vop_stdunp_connect(ap:p_vop_unp_connect_args):Integer;
begin
ap^.a_socket^:=ap^.a_vp^.v_socket;
Exit(0);
end;
function vop_stdunp_detach(ap:p_vop_unp_detach_args):Integer;
begin
ap^.a_vp^.v_un:=nil;
Exit(0);
end;
{
* vfs default ops
* used to fill the vfs function table to get reasonable default Exitvalues.
}
function vfs_stdroot(mp:p_mount;flags:Integer;vpp:pp_vnode):Integer;
begin
Exit(EOPNOTSUPP);
end;
function vfs_stdstatfs(mp:p_mount;sbp:p_statfs):Integer;
begin
Exit(EOPNOTSUPP);
end;
function vfs_stdquotactl(mp:p_mount;cmds:Integer;uid:uid_t;arg:Pointer):Integer;
begin
Exit(EOPNOTSUPP);
end;
function vfs_stdsync(mp:p_mount;waitfor:Integer):Integer;
label
loop;
var
vp,mvp:p_vnode;
error,lockreq,allerror:Integer;
begin
error:=0;
lockreq:=0;
allerror:=0;
lockreq:=LK_EXCLUSIVE or LK_INTERLOCK;
if (waitfor<>MNT_WAIT) then
begin
lockreq:=lockreq or LK_NOWAIT;
end;
{
* Force stale buffer cache information to be flushed.
}
loop:
vp:=__mnt_vnode_first_all(@mvp,mp);
while (vp<>nil) do
begin
//if (vp^.v_bufobj.bo_dirty.bv_cnt=0) then
//begin
// VI_UNLOCK(vp);
// continue;
//end;
error:=vget(vp, lockreq);
if (error<>0) then
begin
if (error=ENOENT) then
begin
//MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
MNT_ILOCK(mp);
__mnt_vnode_markerfree_all(@mvp,mp);
//MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
goto loop;
end;
continue;
end;
error:=VOP_FSYNC(vp, waitfor);
if (error<>0) then
allerror:=error;
vput(vp);
vp:=__mnt_vnode_next_all(@mvp,mp)
end;
Exit(allerror);
end;
function vfs_stdnosync(mp:p_mount;waitfor:Integer):Integer;
begin
Exit(0);
end;
function vfs_stdvget(mp:p_mount;ino:DWORD;flags:Integer;vpp:pp_vnode):Integer;
begin
Exit(EOPNOTSUPP);
end;
function vfs_stdfhtovp(mp:p_mount;fhp:p_fid;flags:Integer;vpp:pp_vnode) :Integer;
begin
Exit(EOPNOTSUPP);
end;
function vfs_stdinit(vfsp:p_vfsconf):Integer;
begin
Exit(0);
end;
function vfs_stduninit(vfsp:p_vfsconf):Integer;
begin
Exit(0);
end;
function vfs_stdextattrctl(mp:p_mount;cmd:Integer;filename_vp:p_vnode;attrnamespace:Integer;attrname:PChar):Integer;
begin
if (filename_vp<>nil) then
VOP_UNLOCK(filename_vp, 0);
Exit(EOPNOTSUPP);
end;
function vfs_stdsysctl(mp:p_mount;op:Integer;req:Pointer):Integer;
begin
Exit(EOPNOTSUPP);
end;
{ end of vfs default ops }
end.