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 and vop_no 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_(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.