FPPS4/sys/fs/devfs/devfs_vnops.pas

2077 lines
43 KiB
Plaintext

unit devfs_vnops;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
mqueue,
kern_param,
time,
sys_event,
vfile,
vfilio,
vttycom,
vmount,
vdirent,
vstat,
vuio,
vfcntl,
vnode,
vnamei,
vnode_if,
vfs_default,
vfs_vnops,
devfs_int,
devfs,
kern_mtx,
kern_sx,
sys_conf;
function devfs_fp_check(fp:p_file;devp:pp_cdev;dswp:pp_cdevsw;ref:PInteger):Integer;
function devfs_get_cdevpriv(datap:PPointer):Integer;
function devfs_set_cdevpriv(priv:Pointer;priv_dtr:cdevpriv_dtr_t):Integer;
procedure devfs_destroy_cdevpriv(p:p_cdev_privdata);
procedure devfs_fpdrop(fp:p_file);
procedure devfs_clear_cdevpriv();
function devfs_populate_vp(vp:p_vnode):Integer;
function devfs_vptocnp(ap:p_vop_vptocnp_args):Integer;
function devfs_fqpn(buf:PChar;dmp:p_devfs_mount;dd:p_devfs_dirent;cnp:p_componentname):PChar;
function devfs_allocv_drop_refs(drop_dm_lock:Integer;dmp:p_devfs_mount;de:p_devfs_dirent):Integer;
function devfs_allocv(de:p_devfs_dirent;mp:p_mount;lockmode:Integer;vpp:pp_vnode):Integer;
function devfs_access(ap:p_vop_access_args):Integer;
function devfs_close(ap:p_vop_close_args):Integer;
function devfs_close_f(fp:p_file):Integer;
function devfs_fsync(ap:p_vop_fsync_args):Integer;
function devfs_getattr(ap:p_vop_getattr_args):Integer;
function devfs_ioctl_f(fp:p_file;com:QWORD;data:Pointer):Integer;
function devfs_kqfilter_f(fp:p_file;kn:p_knote):Integer;
function devfs_prison_check(de:p_devfs_dirent):Integer;
function devfs_lookup(ap:p_vop_lookup_args):Integer;
function devfs_mknod(ap:p_vop_mknod_args):Integer;
function devfs_open(ap:p_vop_open_args):Integer;
function devfs_pathconf(ap:p_vop_pathconf_args):Integer;
function devfs_poll_f(fp:p_file;events:Integer):Integer;
function devfs_print(ap:p_vop_print_args):Integer;
function devfs_read_f(fp:p_file;uio:p_uio;flags:Integer):Integer;
function devfs_readdir(ap:p_vop_readdir_args):Integer;
function devfs_readlink(ap:p_vop_readlink_args):Integer;
function devfs_reclaim(ap:p_vop_reclaim_args):Integer;
function devfs_remove(ap:p_vop_remove_args):Integer;
function devfs_revoke(ap:p_vop_revoke_args):Integer;
function devfs_rioctl(ap:p_vop_ioctl_args):Integer;
function devfs_rread(ap:p_vop_read_args):Integer;
function devfs_setattr(ap:p_vop_setattr_args):Integer;
function devfs_stat_f(fp:p_file;sb:p_stat):Integer;
function devfs_symlink(ap:p_vop_symlink_args):Integer;
function devfs_truncate_f(fp:p_file;length:Int64):Integer;
function devfs_write_f(fp:p_file;uio:p_uio;flags:Integer):Integer;
function dev2udev(x:p_cdev):Integer;
const
devfs_vnodeops:vop_vector=(
vop_default :@default_vnodeops;
vop_bypass :nil;
vop_islocked :nil;
vop_lookup :@devfs_lookup;
vop_create :nil;
vop_whiteout :nil;
vop_mknod :@devfs_mknod;
vop_open :nil;
vop_close :nil;
vop_access :@devfs_access;
vop_accessx :nil;
vop_getattr :@devfs_getattr;
vop_setattr :@devfs_setattr;
vop_markatime :nil;
vop_read :@devfs_rread;
vop_write :nil;
vop_ioctl :@devfs_rioctl;
vop_poll :nil;
vop_kqfilter :nil;
vop_revoke :@devfs_revoke;
vop_fsync :nil;
vop_remove :@devfs_remove;
vop_link :nil;
vop_rename :nil;
vop_mkdir :nil;
vop_rmdir :nil;
vop_symlink :@devfs_symlink;
vop_readdir :@devfs_readdir;
vop_readlink :@devfs_readlink;
vop_inactive :nil;
vop_reclaim :@devfs_reclaim;
vop_lock1 :nil;
vop_unlock :nil;
vop_bmap :nil;
vop_strategy :nil;
vop_getwritemount :nil;
vop_print :nil;
vop_pathconf :@devfs_pathconf;
vop_advlock :nil;
vop_advlockasync :nil;
vop_advlockpurge :nil;
vop_reallocblks :nil;
vop_getpages :nil;
vop_putpages :nil;
vop_vptofh :nil;
vop_vptocnp :@devfs_vptocnp;
vop_allocate :nil;
vop_unp_bind :nil;
vop_unp_connect :nil;
vop_unp_detach :nil;
);
devfs_specops:vop_vector=(
vop_default :@default_vnodeops;
vop_bypass :nil;
vop_islocked :nil;
vop_lookup :nil;
vop_create :@VOP_PANIC;
vop_whiteout :nil;
vop_mknod :@VOP_PANIC;
vop_open :@devfs_open;
vop_close :@devfs_close;
vop_access :@devfs_access;
vop_accessx :nil;
vop_getattr :@devfs_getattr;
vop_setattr :@devfs_setattr;
vop_markatime :nil;
vop_read :@VOP_PANIC;
vop_write :@VOP_PANIC;
vop_ioctl :nil;
vop_poll :nil;
vop_kqfilter :nil;
vop_revoke :@devfs_revoke;
vop_fsync :@devfs_fsync;
vop_remove :@devfs_remove;
vop_link :@VOP_PANIC;
vop_rename :@VOP_PANIC;
vop_mkdir :@VOP_PANIC;
vop_rmdir :@VOP_PANIC;
vop_symlink :@VOP_PANIC;
vop_readdir :@VOP_PANIC;
vop_readlink :@VOP_PANIC;
vop_inactive :nil;
vop_reclaim :@devfs_reclaim;
vop_lock1 :nil;
vop_unlock :nil;
vop_bmap :@VOP_PANIC;
vop_strategy :@VOP_PANIC;
vop_getwritemount :nil;
vop_print :@devfs_print;
vop_pathconf :@devfs_pathconf;
vop_advlock :nil;
vop_advlockasync :nil;
vop_advlockpurge :nil;
vop_reallocblks :@VOP_PANIC;
vop_getpages :nil;
vop_putpages :nil;
vop_vptofh :nil;
vop_vptocnp :@devfs_vptocnp;
vop_allocate :nil;
vop_unp_bind :nil;
vop_unp_connect :nil;
vop_unp_detach :nil;
);
devfs_ops_f:fileops=(
fo_read :@devfs_read_f;
fo_write :@devfs_write_f;
fo_truncate:@devfs_truncate_f;
fo_ioctl :@devfs_ioctl_f;
fo_poll :@devfs_poll_f;
fo_kqfilter:@devfs_kqfilter_f;
fo_stat :@devfs_stat_f;
fo_close :@devfs_close_f;
fo_chmod :@vn_chmod;
fo_chown :@vn_chown;
fo_flags :DFLAG_PASSABLE or DFLAG_SEEKABLE
);
implementation
uses
sysutils,
errno,
systm,
kern_thr,
vfs_subr,
vsys_generic,
//devfs_vfsops,
kern_proc,
kern_descrip,
kern_mtxpool,
subr_uio,
vnode_pager;
function VFSTODEVFS(mp:p_mount):p_devfs_mount; inline;
begin
Result:=mp^.mnt_data;
end;
function cdev2priv(c:Pointer):p_cdev_priv; inline;
begin
Result:=c-ptruint(@p_cdev_priv(nil)^.cdp_c);
end;
function devfs_fp_check(fp:p_file;devp:pp_cdev;dswp:pp_cdevsw;ref:PInteger):Integer;
begin
dswp^:=devvn_refthread(fp^.f_vnode, devp, ref);
if (devp^<>fp^.f_data) then
begin
if (dswp^<>nil) then
begin
dev_relthread(devp^, ref^);
end;
Exit(ENXIO);
end;
Assert(devp^^.si_refcount > 0,'devfs: un-referenced struct cdev');
if (dswp^=nil) then Exit(ENXIO);
curkthread^.td_fpop:=fp;
Exit(0);
end;
function devfs_get_cdevpriv(datap:PPointer):Integer; public;
var
fp:p_file;
p:p_cdev_privdata;
error:Integer;
begin
fp:=curkthread^.td_fpop;
if (fp=nil) then Exit(EBADF);
p:=fp^.f_cdevpriv;
if (p<>nil) then
begin
error:=0;
datap^:=p^.cdpd_data;
end else
begin
error:=ENOENT;
end;
Exit(error);
end;
function devfs_set_cdevpriv(priv:Pointer;priv_dtr:cdevpriv_dtr_t):Integer; public;
var
fp:p_file;
cdp:p_cdev_priv;
p:p_cdev_privdata;
error:Integer;
begin
fp:=curkthread^.td_fpop;
if (fp=nil) then Exit(ENOENT);
cdp:=cdev2priv(fp^.f_data);
p:=AllocMem(sizeof(t_cdev_privdata));
p^.cdpd_data:=priv;
p^.cdpd_dtr :=priv_dtr;
p^.cdpd_fp :=fp;
mtx_lock(cdevpriv_mtx);
if (fp^.f_cdevpriv=nil) then
begin
LIST_INSERT_HEAD(@cdp^.cdp_fdpriv,p,@p^.cdpd_list);
fp^.f_cdevpriv:=p;
mtx_unlock(cdevpriv_mtx);
error:=0;
end else
begin
mtx_unlock(cdevpriv_mtx);
FreeMem(p);
error:=EBUSY;
end;
Exit(error);
end;
procedure devfs_destroy_cdevpriv(p:p_cdev_privdata); public;
begin
mtx_assert(cdevpriv_mtx);
p^.cdpd_fp^.f_cdevpriv:=nil;
LIST_REMOVE(p,@p^.cdpd_list);
mtx_unlock(cdevpriv_mtx);
p^.cdpd_dtr(p^.cdpd_data);
FreeMem(p);
end;
procedure devfs_fpdrop(fp:p_file); public;
var
p:p_cdev_privdata;
begin
mtx_lock(cdevpriv_mtx);
p:=fp^.f_cdevpriv;
if (p=nil) then
begin
mtx_unlock(cdevpriv_mtx);
Exit;
end;
devfs_destroy_cdevpriv(p);
end;
procedure devfs_clear_cdevpriv(); public;
var
fp:p_file;
begin
fp:=curkthread^.td_fpop;
if (fp=nil) then Exit;
devfs_fpdrop(fp);
end;
{
* On success devfs_populate_vp() Exits with dmp^.dm_lock held.
}
function devfs_populate_vp(vp:p_vnode):Integer;
var
de:p_devfs_dirent;
dmp:p_devfs_mount;
locked:Integer;
begin
ASSERT_VOP_LOCKED(vp, 'devfs_populate_vp');
dmp:=VFSTODEVFS(vp^.v_mount);
locked:=VOP_ISLOCKED(vp);
sx_xlock(@dmp^.dm_lock);
DEVFS_DMP_HOLD(dmp);
{ Can't call devfs_populate() with the vnode lock held. }
VOP_UNLOCK(vp, 0);
devfs_populate(dmp);
sx_xunlock(@dmp^.dm_lock);
vn_lock(vp, locked or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
sx_xlock(@dmp^.dm_lock);
if DEVFS_DMP_DROP(dmp) then
begin
sx_xunlock(@dmp^.dm_lock);
devfs_unmount_final(dmp);
Exit(EBADF);
end;
if ((vp^.v_iflag and VI_DOOMED)<>0) then
begin
sx_xunlock(@dmp^.dm_lock);
Exit(EBADF);
end;
de:=vp^.v_data;
Assert(de<>nil,'devfs_populate_vp: vp^.v_data=nil but vnode not doomed');
if ((de^.de_flags and DE_DOOMED)<>0) then
begin
sx_xunlock(@dmp^.dm_lock);
Exit(EBADF);
end;
Exit(0);
end;
function devfs_vptocnp(ap:p_vop_vptocnp_args):Integer;
label
finished;
var
vp:p_vnode;
dvp:pp_vnode;
dmp:p_devfs_mount;
buf:PChar;
buflen:PInteger;
dd,de:p_devfs_dirent;
i,error:Integer;
begin
vp :=ap^.a_vp;
dvp :=ap^.a_vpp;
buf :=ap^.a_buf;
buflen:=ap^.a_buflen;
dmp:=VFSTODEVFS(vp^.v_mount);
error:=devfs_populate_vp(vp);
if (error<>0) then
begin
Exit(error);
end;
i:=buflen^;
dd:=vp^.v_data;
if (vp^.v_type=VCHR) then
begin
Dec(i,strlen(dd^.de_cdp^.cdp_c.si_name));
if (i < 0) then
begin
error:=ENOMEM;
goto finished;
end;
Move(dd^.de_cdp^.cdp_c.si_name^, (buf + i)^,strlen(dd^.de_cdp^.cdp_c.si_name));
de:=dd^.de_dir;
end else
if (vp^.v_type=VDIR) then
begin
if (dd=dmp^.dm_rootdir) then
begin
dvp^:=vp;
vref(dvp^);
goto finished;
end;
Dec(i,dd^.de_dirent^.d_namlen);
if (i < 0) then
begin
error:=ENOMEM;
goto finished;
end;
Move(dd^.de_dirent^.d_name, (buf + i)^, dd^.de_dirent^.d_namlen);
de:=dd;
end else
begin
error:=ENOENT;
goto finished;
end;
buflen^:=i;
de:=devfs_parent_dirent(de);
if (de=nil) then
begin
error:=ENOENT;
goto finished;
end;
mtx_lock(devfs_de_interlock);
dvp^:=de^.de_vnode;
if (dvp^<>nil) then
begin
VI_LOCK(dvp^);
mtx_unlock(devfs_de_interlock);
vholdl(dvp^);
VI_UNLOCK(dvp^);
vref(dvp^);
vdrop(dvp^);
end else
begin
mtx_unlock(devfs_de_interlock);
error:=ENOENT;
end;
finished:
sx_xunlock(@dmp^.dm_lock);
Exit(error);
end;
{
* Construct the fully qualified path name relative to the mountpoint.
* If a nil cnp is provided, no '/' is appended to the resulting path.
}
function devfs_fqpn(buf:PChar;dmp:p_devfs_mount;dd:p_devfs_dirent;cnp:p_componentname):PChar; public;
var
i:Integer;
de:p_devfs_dirent;
begin
sx_assert(@dmp^.dm_lock);
i:=SPECNAMELEN;
buf[i]:=#0;
if (cnp<>nil) then
Dec(i,cnp^.cn_namelen);
if (i < 0) then
Exit(nil);
if (cnp<>nil) then
Move(cnp^.cn_nameptr^, (buf + i)^, cnp^.cn_namelen);
de:=dd;
while (de<>dmp^.dm_rootdir) do
begin
if (cnp<>nil) or (i < SPECNAMELEN) then
begin
Dec(i);
if (i < 0) then
Exit(nil);
buf[i]:='/';
end;
Dec(i,de^.de_dirent^.d_namlen);
if (i < 0) then
Exit(nil);
Move(de^.de_dirent^.d_name, (buf + i)^, de^.de_dirent^.d_namlen);
de:=devfs_parent_dirent(de);
if (de=nil) then
Exit(nil);
end;
Exit(buf + i);
end;
function devfs_allocv_drop_refs(drop_dm_lock:Integer;dmp:p_devfs_mount;de:p_devfs_dirent):Integer;
var
not_found:Integer;
begin
not_found:=0;
if ((de^.de_flags and DE_DOOMED)<>0) then
begin
not_found:=1;
end;
if DEVFS_DE_DROP(de) then
begin
Assert(not_found=1,'DEVFS de dropped but not doomed');
devfs_dirent_free(de);
end;
if DEVFS_DMP_DROP(dmp) then
begin
Assert(not_found=1,'DEVFS mount struct freed before dirent');
not_found:=2;
sx_xunlock(@dmp^.dm_lock);
devfs_unmount_final(dmp);
end;
if (not_found=1) or ((drop_dm_lock<>0) and (not_found<>2)) then
sx_unlock(@dmp^.dm_lock);
Exit(not_found);
end;
procedure devfs_insmntque_dtr(vp:p_vnode;arg:Pointer);
var
de:p_devfs_dirent;
begin
de:=p_devfs_dirent(arg);
mtx_lock(devfs_de_interlock);
vp^.v_data:=nil;
de^.de_vnode:=nil;
mtx_unlock(devfs_de_interlock);
vgone(vp);
vput(vp);
end;
{
* devfs_allocv shall be entered with dmp^.dm_lock held, and it drops
* it on Exit.
}
function devfs_allocv(de:p_devfs_dirent;mp:p_mount;lockmode:Integer;vpp:pp_vnode):Integer; public;
label
loop;
var
error:Integer;
vp:p_vnode;
dev:p_cdev;
dmp:p_devfs_mount;
dsw:p_cdevsw;
begin
dmp:=VFSTODEVFS(mp);
if ((de^.de_flags and DE_DOOMED)<>0) then
begin
sx_xunlock(@dmp^.dm_lock);
Exit(ENOENT);
end;
loop:
DEVFS_DE_HOLD(de);
DEVFS_DMP_HOLD(dmp);
mtx_lock(devfs_de_interlock);
vp:=de^.de_vnode;
if (vp<>nil) then
begin
VI_LOCK(vp);
mtx_unlock(devfs_de_interlock);
sx_xunlock(@dmp^.dm_lock);
vget(vp, lockmode or LK_INTERLOCK or LK_RETRY);
sx_xlock(@dmp^.dm_lock);
if (devfs_allocv_drop_refs(0, dmp, de)<>0) then
begin
vput(vp);
Exit(ENOENT);
end else
if ((vp^.v_iflag and VI_DOOMED)<>0) then
begin
mtx_lock(devfs_de_interlock);
if (de^.de_vnode=vp) then
begin
de^.de_vnode:=nil;
vp^.v_data:=nil;
end;
mtx_unlock(devfs_de_interlock);
vput(vp);
goto loop;
end;
sx_xunlock(@dmp^.dm_lock);
vpp^:=vp;
Exit(0);
end;
mtx_unlock(devfs_de_interlock);
if (de^.de_dirent^.d_type=DT_CHR) then
begin
if ((de^.de_cdp^.cdp_flags and CDP_ACTIVE)=0) then
begin
devfs_allocv_drop_refs(1, dmp, de);
Exit(ENOENT);
end;
dev:=@de^.de_cdp^.cdp_c;
end else
begin
dev:=nil;
end;
error:=getnewvnode('devfs', mp, @devfs_vnodeops, @vp);
if (error<>0) then
begin
devfs_allocv_drop_refs(1, dmp, de);
Writeln('devfs_allocv: failed to allocate new vnode');
Exit(error);
end;
if (de^.de_dirent^.d_type=DT_CHR) then
begin
vp^.v_type:=VCHR;
VI_LOCK(vp);
dev_lock();
dev_refl(dev);
{ XXX: v_rdev should be protect by vnode lock }
vp^.v_rdev:=dev;
Assert(vp^.v_usecount=1);
Inc(dev^.si_usecount,vp^.v_usecount);
{ Special casing of ttys for deadfs. Probably redundant. }
dsw:=dev^.si_devsw;
if (dsw<>nil) then
if ((dsw^.d_flags and D_TTY)<>0) then
begin
vp^.v_vflag:=vp^.v_vflag or VV_ISTTY;
end;
dev_unlock();
VI_UNLOCK(vp);
if ((dev^.si_flags and SI_ETERNAL)<>0) then
begin
vp^.v_vflag:=vp^.v_vflag or VV_ETERNALDEV;
end;
vp^.v_op:=@devfs_specops;
end else
if (de^.de_dirent^.d_type=DT_DIR) then
begin
vp^.v_type:=VDIR;
end else
if (de^.de_dirent^.d_type=DT_LNK) then
begin
vp^.v_type:=VLNK;
end else
begin
vp^.v_type:=VBAD;
end;
vn_lock(vp, LK_EXCLUSIVE or LK_RETRY or LK_NOWITNESS,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
//VN_LOCK_ASHARE(vp);
mtx_lock(devfs_de_interlock);
vp^.v_data:=de;
de^.de_vnode:=vp;
mtx_unlock(devfs_de_interlock);
error:=insmntque1(vp, mp, @devfs_insmntque_dtr, de);
if (error<>0) then
begin
devfs_allocv_drop_refs(1, dmp, de);
Exit(error);
end;
if (devfs_allocv_drop_refs(0, dmp, de)<>0) then
begin
vput(vp);
Exit(ENOENT);
end;
//mac_devfs_vnode_associate(mp, de, vp);
sx_xunlock(@dmp^.dm_lock);
vpp^:=vp;
Exit(0);
end;
function devfs_access(ap:p_vop_access_args):Integer;
var
vp:p_vnode;
de:p_devfs_dirent;
error:Integer;
begin
vp:=ap^.a_vp;
de:=vp^.v_data;
if (vp^.v_type=VDIR) then
begin
de:=de^.de_dir;
end;
error:=vaccess(vp^.v_type, de^.de_mode, de^.de_uid, de^.de_gid, ap^.a_accmode, nil);
if (error=0) then
begin
Exit(0);
end;
if (error<>EACCES) then
Exit(error);
{ We do, however, allow access to the controlling terminal }
if ((p_proc.p_flag and P_CONTROLT)=0) then
begin
Exit(error);
end;
//if (ap^.a_td^.td_proc^.p_session^.s_ttydp=de^.de_cdp) then
// Exit(0);
Exit(error);
end;
{ ARGSUSED }
function devfs_close(ap:p_vop_close_args):Integer;
var
vp,oldvp:p_vnode;
dev:p_cdev;
dsw:p_cdevsw;
vp_locked,error,ref:Integer;
begin
vp:=ap^.a_vp;
//struct thread *td:=ap^.a_td;
dev:=vp^.v_rdev;
{
* XXX: Don't call d_close() if we were called because of
* XXX: insmntque1() failure.
}
if (vp^.v_data=nil) then
begin
Exit(0);
end;
{
* Hack: a tty device that is a controlling terminal
* has a reference from the session structure.
* We cannot easily tell that a character device is
* a controlling terminal, unless it is the closing
* process' controlling terminal. In that case,
* if the reference count is 2 (this last descriptor
* plus the session), release the reference from the session.
}
oldvp:=nil;
//sx_xlock(@proctree_lock);
//if (td<>nil) then
//if (vp=td^.td_proc^.p_session^.s_ttyvp) then
//begin
// SESS_LOCK(td^.td_proc^.p_session);
// VI_LOCK(vp);
// if (count_dev(dev)=2) and ((vp^.v_iflag and VI_DOOMED)=0) then
// begin
// td^.td_proc^.p_session^.s_ttyvp:=nil;
// td^.td_proc^.p_session^.s_ttydp:=nil;
// oldvp:=vp;
// end;
// VI_UNLOCK(vp);
// SESS_UNLOCK(td^.td_proc^.p_session);
//end;
//sx_xunlock(@proctree_lock);
if (oldvp<>nil) then
begin
vrele(oldvp);
end;
{
* We do not want to really close the device if it
* is still in use unless we are trying to close it
* forcibly. Since every use (buffer, vnode, swap, cmap)
* holds a reference to the vnode, and because we mark
* any other vnodes that alias this device, when the
* sum of the reference counts on all the aliased
* vnodes descends to one, we are on last close.
}
dsw:=dev_refthread(dev, @ref);
if (dsw=nil) then Exit(ENXIO);
VI_LOCK(vp);
if ((vp^.v_iflag and VI_DOOMED)<>0) then
begin
{ Forced close. }
end else
if ((dsw^.d_flags and D_TRACKCLOSE)<>0) then
begin
{ Keep device updated on status. }
end else
if (count_dev(dev) > 1) then
begin
VI_UNLOCK(vp);
dev_relthread(dev, ref);
Exit(0);
end;
vholdl(vp);
VI_UNLOCK(vp);
vp_locked:=VOP_ISLOCKED(vp);
VOP_UNLOCK(vp, 0);
Assert(dev^.si_refcount > 0,'devfs_close() on un-referenced struct cdev');
error:=dsw^.d_close(dev, ap^.a_fflag, S_IFCHR);
dev_relthread(dev, ref);
vn_lock(vp, vp_locked or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
vdrop(vp);
Exit(error);
end;
function devfs_close_f(fp:p_file):Integer;
var
td:p_kthread;
fpop:p_file;
begin
td:=curkthread;
{
* NB: td may be nil if this descriptor is closed due to
* garbage collection from a closed UNIX domain socket.
}
fpop:=td^.td_fpop;
td^.td_fpop:=fp;
Result:=vnops.fo_close(fp);
td^.td_fpop:=fpop;
{
* The f_cdevpriv cannot be assigned non-nil value while we
* are destroying the file.
}
if (fp^.f_cdevpriv<>nil) then
devfs_fpdrop(fp);
end;
function devfs_fsync(ap:p_vop_fsync_args):Integer;
var
error:Integer;
//struct bufobj *bo;
de:p_devfs_dirent;
begin
if vn_isdisk(ap^.a_vp, @error) then
begin
//bo:=@ap^.a_vp^.v_bufobj;
de:=ap^.a_vp^.v_data;
if (error=ENXIO) {and (bo^.bo_dirty.bv_cnt > 0)} then
begin
Writeln('Device %s went missing before all of the data ',
'could be written to it; expect data loss.',
de^.de_dirent^.d_name);
error:=vop_stdfsync(ap);
if {(bo^.bo_dirty.bv_cnt<>0) or} (error<>0) then
Assert(False,'devfs_fsync: vop_stdfsync failed.');
end;
Exit(0);
end;
Exit(vop_stdfsync(ap));
end;
function devfs_getattr(ap:p_vop_getattr_args):Integer;
var
vp:p_vnode;
vap:p_vattr;
error:Integer;
de:p_devfs_dirent;
dmp:p_devfs_mount;
dev:p_cdev;
procedure fix(var src,dst:timespec); inline;
begin
if (src.tv_sec <= 3600) then
begin
src.tv_sec :=boottime.tv_sec;
src.tv_nsec:=boottime.tv_usec * 1000;
end;
dst:=src;
end;
begin
vp:=ap^.a_vp;
vap:=ap^.a_vap;
error:=devfs_populate_vp(vp);
if (error<>0) then
begin
Exit(error);
end;
dmp:=VFSTODEVFS(vp^.v_mount);
sx_xunlock(@dmp^.dm_lock);
de:=vp^.v_data;
Assert(de<>nil,'nil dirent in devfs_getattr vp=%p');
if (vp^.v_type=VDIR) then
begin
de:=de^.de_dir;
Assert(de<>nil,'nil dir dirent in devfs_getattr vp=%p');
end;
vap^.va_uid :=de^.de_uid;
vap^.va_gid :=de^.de_gid;
vap^.va_mode:=de^.de_mode;
case vp^.v_type of
VLNK:
begin
vap^.va_size :=strlen(de^.de_symlink);
vap^.va_bytes:=0;
end;
VDIR:
begin
vap^.va_size :=DEV_BSIZE;
vap^.va_bytes:=DEV_BSIZE;
end;
else
begin
vap^.va_size :=0;
vap^.va_bytes:=0;
end;
end;
vap^.va_blocksize:=DEV_BSIZE;
vap^.va_type:=vp^.v_type;
if (vp^.v_type<>VCHR) then
begin
fix(de^.de_atime,vap^.va_atime);
fix(de^.de_mtime,vap^.va_mtime);
fix(de^.de_ctime,vap^.va_ctime);
end else
begin
dev:=vp^.v_rdev;
fix(dev^.si_atime,vap^.va_atime);
fix(dev^.si_mtime,vap^.va_mtime);
fix(dev^.si_ctime,vap^.va_ctime);
vap^.va_rdev:=cdev2priv(dev)^.cdp_inode;
end;
vap^.va_gen :=0;
vap^.va_flags :=0;
vap^.va_filerev:=0;
vap^.va_nlink :=de^.de_links;
vap^.va_fileid :=de^.de_inode;
Exit(error);
end;
{ ARGSUSED }
function devfs_ioctl_f(fp:p_file;com:QWORD;data:Pointer):Integer;
var
td:p_kthread;
dev:p_cdev;
dsw:p_cdevsw;
vp:p_vnode;
vpold:p_vnode;
error,i,ref:Integer;
p:PChar;
fgn:p_fiodgname_arg;
fpop:p_file;
begin
td:=curkthread;
fpop:=td^.td_fpop;
error:=devfs_fp_check(fp, @dev, @dsw, @ref);
if (error<>0) then Exit(error);
if (com=FIODTYPE) then
begin
PInteger(data)^:=dsw^.d_flags and D_TYPEMASK;
td^.td_fpop:=fpop;
dev_relthread(dev, ref);
Exit(0);
end else
if (com=FIODGNAME) then
begin
fgn:=data;
p:=devtoname(dev);
i:=strlen(p) + 1;
if (i > fgn^.len) then
error:=EINVAL
else
error:=copyout(p, fgn^.buf, i);
td^.td_fpop:=fpop;
dev_relthread(dev, ref);
Exit(error);
end;
error:=dsw^.d_ioctl(dev, com, data, fp^.f_flag);
td^.td_fpop:=nil;
dev_relthread(dev, ref);
if (error=ENOIOCTL) then
begin
error:=ENOTTY;
end;
if (error=0) and (com=TIOCSCTTY) then
begin
vp:=fp^.f_vnode;
vpold:=nil;
{ Do nothing if reassigning same control tty }
//sx_slock(@proctree_lock);
//if (td^.td_proc^.p_session^.s_ttyvp=vp) then
//begin
// sx_sunlock(@proctree_lock);
// Exit(0);
//end;
//
//vpold:=td^.td_proc^.p_session^.s_ttyvp;
//VREF(vp);
//SESS_LOCK(td^.td_proc^.p_session);
//td^.td_proc^.p_session^.s_ttyvp:=vp;
//td^.td_proc^.p_session^.s_ttydp:=cdev2priv(dev);
//SESS_UNLOCK(td^.td_proc^.p_session);
//
//sx_sunlock(@proctree_lock);
{ Get rid of reference to old control tty }
if (vpold<>nil) then
begin
vrele(vpold);
end;
end;
Exit(error);
end;
{ ARGSUSED }
function devfs_kqfilter_f(fp:p_file;kn:p_knote):Integer;
var
td:p_kthread;
dev:p_cdev;
dsw:p_cdevsw;
error,ref:Integer;
fpop:p_file;
begin
td:=curkthread;
fpop:=td^.td_fpop;
error:=devfs_fp_check(fp, @dev, @dsw, @ref);
if (error<>0) then Exit(error);
error:=dsw^.d_kqfilter(dev, kn);
td^.td_fpop:=fpop;
dev_relthread(dev, ref);
Exit(error);
end;
function devfs_prison_check(de:p_devfs_dirent):Integer;
var
cdp:p_cdev_priv;
error:Integer;
begin
cdp:=de^.de_cdp;
if (cdp=nil) then
Exit(0);
error:=0;
//error:=prison_check(td^.td_ucred, dcr);
if (error=0) then
begin
Exit(0);
end;
{ We do, however, allow access to the controlling terminal }
if ((p_proc.p_flag and P_CONTROLT)=0) then
begin
Exit(error);
end;
//if (td^.td_proc^.p_session^.s_ttydp=cdp) then
// Exit(0);
Exit(error);
end;
function devfs_lookupx(ap:p_vop_lookup_args;dm_unlock:PBoolean):Integer;
label
_or;
var
cnp:p_componentname;
dvp:p_vnode;
vpp:pp_vnode;
de,dd:p_devfs_dirent;
dde:pp_devfs_dirent;
dmp:p_devfs_mount;
cdev:p_cdev;
error,flags,nameiop,dvplocked:Integer;
specname:array[0..SPECNAMELEN] of Char;
pname:PChar;
begin
cnp:=ap^.a_cnp;
vpp:=ap^.a_vpp;
dvp:=ap^.a_dvp;
pname:=cnp^.cn_nameptr;
flags:=cnp^.cn_flags;
nameiop:=cnp^.cn_nameiop;
dmp:=VFSTODEVFS(dvp^.v_mount);
dd:=dvp^.v_data;
vpp^:=nil;
if ((flags and ISLASTCN)<>0) and (nameiop=RENAME) then
begin
Exit(EOPNOTSUPP);
end;
if (dvp^.v_type<>VDIR) then
begin
Exit(ENOTDIR);
end;
if (((flags and ISDOTDOT)<>0) and ((dvp^.v_vflag and VV_ROOT)<>0)) then
begin
Exit(EIO);
end;
error:=VOP_ACCESS(dvp, VEXEC);
if (error<>0) then
begin
Exit(error);
end;
if (cnp^.cn_namelen=1) and (pname^='.') then
begin
if ((flags and ISLASTCN)<>0) and (nameiop<>LOOKUP) then
begin
Exit(EINVAL);
end;
vpp^:=dvp;
VREF(dvp);
Exit(0);
end;
if ((flags and ISDOTDOT)<>0) then
begin
if ((flags and ISLASTCN)<>0) and (nameiop<>LOOKUP) then
begin
Exit(EINVAL);
end;
de:=devfs_parent_dirent(dd);
if (de=nil) then Exit(ENOENT);
dvplocked:=VOP_ISLOCKED(dvp);
VOP_UNLOCK(dvp, 0);
error:=devfs_allocv(de, dvp^.v_mount, cnp^.cn_lkflags and LK_TYPE_MASK, vpp);
dm_unlock^:=False;
vn_lock(dvp, dvplocked or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
Exit(error);
end;
dd:=dvp^.v_data;
de:=devfs_find(dd, cnp^.cn_nameptr, cnp^.cn_namelen, 0);
while (de=nil) do
begin { While(...) so we can use break }
if (nameiop=DELETE) then Exit(ENOENT);
{
* OK, we didn't have an entry for the name we were asked for
* so we try to see if anybody can create it on demand.
}
pname:=devfs_fqpn(specname, dmp, dd, cnp);
if (pname=nil) then break;
cdev:=nil;
DEVFS_DMP_HOLD(dmp);
sx_xunlock(@dmp^.dm_lock);
//sx_slock(@clone_drain_lock);
//EVENTHANDLER_INVOKE(dev_clone, td^.td_ucred, pname, strlen(pname), @cdev);
//sx_sunlock(@clone_drain_lock);
if (cdev=nil) then
sx_xlock(@dmp^.dm_lock)
else
if (devfs_populate_vp(dvp)<>0) then
begin
dm_unlock^:=false;
sx_xlock(@dmp^.dm_lock);
if (DEVFS_DMP_DROP(dmp)) then
begin
sx_xunlock(@dmp^.dm_lock);
devfs_unmount_final(dmp);
end else
sx_xunlock(@dmp^.dm_lock);
dev_rel(cdev);
Exit(ENOENT);
end;
if DEVFS_DMP_DROP(dmp) then
begin
dm_unlock^:=false;
sx_xunlock(@dmp^.dm_lock);
devfs_unmount_final(dmp);
if (cdev<>nil) then
begin
dev_rel(cdev);
end;
Exit(ENOENT);
end;
if (cdev=nil) then
break;
dev_lock();
dde:=@cdev2priv(cdev)^.cdp_dirents[dmp^.dm_idx];
if (dde<>nil) then
if (dde^<>nil) then
begin
de:=dde^;
end;
dev_unlock();
dev_rel(cdev);
break;
end;
if (de=nil) then goto _or;
if ((de^.de_flags and DE_WHITEOUT)<>0) then
begin
_or:
if ((nameiop=CREATE) or (nameiop=RENAME)) and
((flags and (LOCKPARENT or WANTPARENT))<>0) and
((flags and ISLASTCN)<>0) then
begin
cnp^.cn_flags:=cnp^.cn_flags or SAVENAME;
Exit(EJUSTRETURN);
end;
Exit(ENOENT);
end;
if (devfs_prison_check(de)<>0) then
begin
Exit(ENOENT);
end;
if (cnp^.cn_nameiop=DELETE) and ((flags and ISLASTCN)<>0) then
begin
error:=VOP_ACCESS(dvp, VWRITE);
if (error<>0) then
begin
Exit(error);
end;
if (vpp^=dvp) then
begin
VREF(dvp);
vpp^:=dvp;
Exit(0);
end;
end;
error:=devfs_allocv(de, dvp^.v_mount, cnp^.cn_lkflags and LK_TYPE_MASK, vpp);
dm_unlock^:=false;
Exit(error);
end;
function devfs_lookup(ap:p_vop_lookup_args):Integer;
var
dmp:p_devfs_mount;
dm_unlock:Boolean;
begin
if (devfs_populate_vp(ap^.a_dvp)<>0) then
begin
Exit(ENOTDIR);
end;
dmp:=VFSTODEVFS(ap^.a_dvp^.v_mount);
dm_unlock:=True;
Result:=devfs_lookupx(ap, @dm_unlock);
if (dm_unlock) then
begin
sx_xunlock(@dmp^.dm_lock);
end;
end;
function devfs_mknod(ap:p_vop_mknod_args):Integer;
label
notfound;
var
cnp:p_componentname;
dvp:p_vnode;
vpp:pp_vnode;
dd,de:p_devfs_dirent;
dmp:p_devfs_mount;
error:Integer;
begin
{
* The only type of node we should be creating here is a
* character device, for anything else return EOPNOTSUPP.
}
if (ap^.a_vap^.va_type<>VCHR) then
Exit(EOPNOTSUPP);
dvp:=ap^.a_dvp;
dmp:=VFSTODEVFS(dvp^.v_mount);
cnp:=ap^.a_cnp;
vpp:=ap^.a_vpp;
dd:=dvp^.v_data;
error:=ENOENT;
sx_xlock(@dmp^.dm_lock);
de:=TAILQ_FIRST(@dd^.de_dlist);
while (de<>nil) do
begin
if (cnp^.cn_namelen<>de^.de_dirent^.d_namlen) then
begin
de:=TAILQ_NEXT(de,@de^.de_list);
continue;
end;
if (CompareByte(cnp^.cn_nameptr^, de^.de_dirent^.d_name, de^.de_dirent^.d_namlen)<>0) then
begin
de:=TAILQ_NEXT(de,@de^.de_list);
continue;
end;
if ((de^.de_flags and DE_WHITEOUT)<>0) then
break;
goto notfound;
end;
if (de=nil) then
goto notfound;
de^.de_flags:=de^.de_flags and (not DE_WHITEOUT);
error:=devfs_allocv(de, dvp^.v_mount, LK_EXCLUSIVE, vpp);
Exit(error);
notfound:
sx_xunlock(@dmp^.dm_lock);
Exit(error);
end;
{ ARGSUSED }
function devfs_open(ap:p_vop_open_args):Integer;
var
td:p_kthread;
vp:p_vnode;
dev:p_cdev;
fp:p_file;
error,ref,vlocked:Integer;
dsw:p_cdevsw;
fpop:p_file;
mtxp:p_mtx;
begin
td:=curkthread;
vp:=ap^.a_vp;
dev:=vp^.v_rdev;
fp:=ap^.a_fp;
if (vp^.v_type=VBLK) then Exit(ENXIO);
if (dev=nil) then Exit(ENXIO);
{ Make this field valid before any I/O in d_open. }
if (dev^.si_iosize_max=0) then
begin
dev^.si_iosize_max:=DFLTPHYS;
end;
dsw:=dev_refthread(dev, @ref);
if (dsw=nil) then Exit(ENXIO);
if (fp=nil) and (dsw^.d_fdopen<>nil) then
begin
dev_relthread(dev, ref);
Exit(ENXIO);
end;
vlocked:=VOP_ISLOCKED(vp);
VOP_UNLOCK(vp, 0);
fpop:=td^.td_fpop;
td^.td_fpop:=fp;
if (fp<>nil) then
begin
fp^.f_data:=dev;
fp^.f_vnode:=vp;
end;
if (dsw^.d_fdopen<>nil) then
error:=dsw^.d_fdopen(dev, ap^.a_mode, fp)
else
error:=dsw^.d_open(dev, ap^.a_mode, S_IFCHR);
{ cleanup any cdevpriv upon error }
if (error<>0) then
begin
devfs_clear_cdevpriv();
end;
td^.td_fpop:=fpop;
vn_lock(vp, vlocked or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
dev_relthread(dev, ref);
if (error<>0) then
begin
if (error=ERESTART) then error:=EINTR;
Exit(error);
end;
if (fp=nil) then Exit(error);
if (fp^.f_ops=@badfileops) then
begin
finit(fp, fp^.f_flag, DTYPE_VNODE, dev, @devfs_ops_f);
end;
mtxp:=mtx_pool_find(mtxpool_sleep, fp);
{
* Hint to the dofilewrite() to not force the buffer draining
* on the writer to the file. Most likely, the write would
* not need normal buffers.
}
mtx_lock(mtxp^);
fp^.f_vnread_flags:=fp^.f_vnread_flags or FDEVFS_VNODE;
mtx_unlock(mtxp^);
Exit(error);
end;
function devfs_pathconf(ap:p_vop_pathconf_args):Integer;
begin
case ap^.a_name of
_PC_MAC_PRESENT:
begin
{
* If MAC is enabled, devfs automatically supports
* trivial non-persistant label storage.
}
ap^.a_retval^:=1;
Exit(0);
end
else
Exit(vop_stdpathconf(ap));
end;
{ NOTREACHED }
end;
{ ARGSUSED }
function devfs_poll_f(fp:p_file;events:Integer):Integer;
var
td:p_kthread;
dev:p_cdev;
dsw:p_cdevsw;
error,ref:Integer;
fpop:p_file;
begin
td:=curkthread;
fpop:=td^.td_fpop;
error:=devfs_fp_check(fp, @dev, @dsw, @ref);
if (error<>0) then Exit(poll_no_poll(events));
error:=dsw^.d_poll(dev, events);
td^.td_fpop:=fpop;
dev_relthread(dev, ref);
Exit(error);
end;
{
* Print out the contents of a special device vnode.
}
function devfs_print(ap:p_vop_print_args):Integer;
begin
Writeln(Format('dev %s',[devtoname(ap^.a_vp^.v_rdev)]));
Exit(0);
end;
function devfs_read_f(fp:p_file;uio:p_uio;flags:Integer):Integer;
var
td:p_kthread;
dev:p_cdev;
ioflag,error,ref:Integer;
resid:Int64;
dsw:p_cdevsw;
fpop:p_file;
begin
if (uio^.uio_resid > DEVFS_IOSIZE_MAX) then
begin
Exit(EINVAL);
end;
td:=curkthread;
fpop:=td^.td_fpop;
error:=devfs_fp_check(fp, @dev, @dsw, @ref);
if (error<>0) then Exit(error);
resid:=uio^.uio_resid;
ioflag:=fp^.f_flag and (O_NONBLOCK or O_DIRECT);
if ((ioflag and O_DIRECT)<>0) then
begin
ioflag:=ioflag or IO_DIRECT;
end;
foffset_lock_uio(fp, uio, flags or FOF_NOLOCK);
error:=dsw^.d_read(dev, uio, ioflag);
if (uio^.uio_resid<>resid) or ((error=0) and (resid<>0)) then
begin
vfs_timestamp(@dev^.si_atime);
end;
td^.td_fpop:=fpop;
dev_relthread(dev, ref);
foffset_unlock_uio(fp, uio, flags or FOF_NOLOCK or FOF_NEXTOFF);
Exit(error);
end;
function devfs_readdir(ap:p_vop_readdir_args):Integer;
var
error:Integer;
uio:p_uio;
dp:p_dirent;
dd:p_devfs_dirent;
de:p_devfs_dirent;
dmp:p_devfs_mount;
off:Int64;
tmp_ncookies:PInteger;
begin
tmp_ncookies:=nil;
if (ap^.a_vp^.v_type<>VDIR) then
begin
Exit(ENOTDIR);
end;
uio:=ap^.a_uio;
if (uio^.uio_offset < 0) then
begin
Exit(EINVAL);
end;
{
* XXX: This is a temporary hack to get around this filesystem not
* supporting cookies. We store the location of the ncookies pointer
* in a temporary variable before calling vfs_subr.c:vfs_read_dirent()
* and set the number of cookies to 0. We then set the pointer to
* nil so that vfs_read_dirent doesn't try to call realloc() on
* ap^.a_cookies. Later in this function, we restore the ap^.a_ncookies
* pointer to its original location before Exiting to the caller.
}
if (ap^.a_ncookies<>nil) then
begin
tmp_ncookies:=ap^.a_ncookies;
ap^.a_ncookies^:=0;
ap^.a_ncookies:=nil;
end;
dmp:=VFSTODEVFS(ap^.a_vp^.v_mount);
if (devfs_populate_vp(ap^.a_vp)<>0) then
begin
if (tmp_ncookies<>nil) then
begin
ap^.a_ncookies:=tmp_ncookies;
end;
Exit(EIO);
end;
error:=0;
de:=ap^.a_vp^.v_data;
off:=0;
dd:=TAILQ_FIRST(@de^.de_dlist);
while (dd<>nil) do
begin
Assert(ptruint(dd^.de_cdp)<>$deadc0de);
if ((dd^.de_flags and (DE_COVERED or DE_WHITEOUT))<>0) then
begin
dd:=TAILQ_NEXT(dd,@dd^.de_list);
continue;
end;
if (devfs_prison_check(dd)<>0) then
begin
dd:=TAILQ_NEXT(dd,@dd^.de_list);
continue;
end;
if (dd^.de_dirent^.d_type=DT_DIR) then
de:=dd^.de_dir
else
de:=dd;
dp:=dd^.de_dirent;
if (dp^.d_reclen > uio^.uio_resid) then
begin
break;
end;
dp^.d_fileno:=de^.de_inode;
if (off >= uio^.uio_offset) then
begin
error:=vfs_read_dirent(ap, dp, off);
if (error<>0) then
begin
break;
end;
end;
Inc(off,dp^.d_reclen);
dd:=TAILQ_NEXT(dd,@dd^.de_list);
end;
sx_xunlock(@dmp^.dm_lock);
uio^.uio_offset:=off;
{
* Restore ap^.a_ncookies if it wasn't originally nil in the first
* place.
}
if (tmp_ncookies<>nil) then
begin
ap^.a_ncookies:=tmp_ncookies;
end;
Exit(error);
end;
function devfs_readlink(ap:p_vop_readlink_args):Integer;
var
de:p_devfs_dirent;
begin
de:=ap^.a_vp^.v_data;
if (de^.de_dirent^.d_type<>DT_LNK) then Exit(EINVAL);
Exit(uiomove(de^.de_symlink, strlen(de^.de_symlink), ap^.a_uio));
end;
function devfs_reclaim(ap:p_vop_reclaim_args):Integer;
var
vp:p_vnode;
de:p_devfs_dirent;
dev:p_cdev;
begin
vp:=ap^.a_vp;
mtx_lock(devfs_de_interlock);
de:=vp^.v_data;
if (de<>nil) then
begin
de^.de_vnode:=nil;
vp^.v_data:=nil;
end;
mtx_unlock(devfs_de_interlock);
vnode_destroy_vobject(vp);
VI_LOCK(vp);
dev_lock();
dev:=vp^.v_rdev;
vp^.v_rdev:=nil;
if (dev=nil) then
begin
dev_unlock();
VI_UNLOCK(vp);
Exit(0);
end;
Dec(dev^.si_usecount,vp^.v_usecount);
dev_unlock();
VI_UNLOCK(vp);
dev_rel(dev);
Exit(0);
end;
function devfs_remove(ap:p_vop_remove_args):Integer;
var
dvp,vp:p_vnode;
dd:p_devfs_dirent;
de,de_cov:p_devfs_dirent;
dmp:p_devfs_mount;
begin
dvp:=ap^.a_dvp;
vp:=ap^.a_vp;
dmp:=VFSTODEVFS(vp^.v_mount);
ASSERT_VOP_ELOCKED(dvp, 'devfs_remove');
ASSERT_VOP_ELOCKED(vp, 'devfs_remove');
sx_xlock(@dmp^.dm_lock);
dd:=ap^.a_dvp^.v_data;
de:=vp^.v_data;
if (de^.de_cdp=nil) then
begin
TAILQ_REMOVE(@dd^.de_dlist,de,@de^.de_list);
if (de^.de_dirent^.d_type=DT_LNK) then
begin
de_cov:=devfs_find(dd, de^.de_dirent^.d_name, de^.de_dirent^.d_namlen, 0);
if (de_cov<>nil) then
begin
de_cov^.de_flags:=de_cov^.de_flags and (not DE_COVERED);
end;
end;
{ We need to unlock dvp because devfs_delete() may lock it. }
VOP_UNLOCK(vp, 0);
if (dvp<>vp) then
begin
VOP_UNLOCK(dvp, 0);
end;
devfs_delete(dmp, de, 0);
sx_xunlock(@dmp^.dm_lock);
if (dvp<>vp) then
begin
vn_lock(dvp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
end;
vn_lock(vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
end else
begin
de^.de_flags:=de^.de_flags or DE_WHITEOUT;
sx_xunlock(@dmp^.dm_lock);
end;
Exit(0);
end;
{
* Revoke is called on a tty when a terminal session ends. The vnode
* is orphaned by setting v_op to deadfs so we need to let go of it
* as well so that we create a new one next time around.
*
}
function devfs_revoke(ap:p_vop_revoke_args):Integer;
label
loop;
var
vp,vp2:p_vnode;
dev:p_cdev;
cdp:p_cdev_priv;
de:p_devfs_dirent;
i:DWORD;
begin
vp:=ap^.a_vp;
Assert((ap^.a_flags and REVOKEALL)<>0,'devfs_revoke !REVOKEALL');
dev:=vp^.v_rdev;
cdp:=cdev2priv(dev);
dev_lock();
Inc(cdp^.cdp_inuse);
dev_unlock();
vhold(vp);
vgone(vp);
vdrop(vp);
VOP_UNLOCK(vp,0);
loop:
repeat
mtx_lock(devfs_de_interlock);
dev_lock();
vp2:=nil;
For i:=0 to cdp^.cdp_maxdirent do
begin
de:=cdp^.cdp_dirents[i];
if (de=nil) then
begin
continue;
end;
vp2:=de^.de_vnode;
if (vp2<>nil) then
begin
dev_unlock();
VI_LOCK(vp2);
mtx_unlock(devfs_de_interlock);
if (vget(vp2, LK_EXCLUSIVE or LK_INTERLOCK)<>0) then
begin
goto loop;
end;
vhold(vp2);
vgone(vp2);
vdrop(vp2);
vput(vp2);
break;
end;
end;
if (vp2<>nil) then
begin
continue;
end;
dev_unlock();
mtx_unlock(devfs_de_interlock);
break;
until false;
dev_lock();
Dec(cdp^.cdp_inuse);
if ((cdp^.cdp_flags and CDP_ACTIVE)=0) and (cdp^.cdp_inuse=0) then
begin
TAILQ_REMOVE(@cdevp_list,cdp,@cdp^.cdp_list);
dev_unlock();
dev_rel(@cdp^.cdp_c);
end else
begin
dev_unlock();
end;
vn_lock(vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
Exit(0);
end;
function devfs_rioctl(ap:p_vop_ioctl_args):Integer;
var
vp:p_vnode;
dmp:p_devfs_mount;
error:Integer;
begin
vp:=ap^.a_vp;
vn_lock(vp, LK_SHARED or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
if ((vp^.v_iflag and VI_DOOMED)<>0) then
begin
VOP_UNLOCK(vp, 0);
Exit(EBADF);
end;
dmp:=VFSTODEVFS(vp^.v_mount);
sx_xlock(@dmp^.dm_lock);
VOP_UNLOCK(vp, 0);
DEVFS_DMP_HOLD(dmp);
devfs_populate(dmp);
if DEVFS_DMP_DROP(dmp) then
begin
sx_xunlock(@dmp^.dm_lock);
devfs_unmount_final(dmp);
Exit(ENOENT);
end;
error:=devfs_rules_ioctl(dmp, ap^.a_command, ap^.a_data);
sx_xunlock(@dmp^.dm_lock);
Exit(error);
end;
function devfs_rread(ap:p_vop_read_args):Integer;
begin
if (ap^.a_vp^.v_type<>VDIR) then
Exit(EINVAL);
Exit(VOP_READDIR(ap^.a_vp, ap^.a_uio, nil, nil, nil));
end;
function devfs_setattr(ap:p_vop_setattr_args):Integer;
var
de:p_devfs_dirent;
vap:p_vattr;
vp:p_vnode;
c,error:Integer;
uid:uid_t;
gid:gid_t;
begin
vap:=ap^.a_vap;
vp:=ap^.a_vp;
if (vap^.va_type <>VNON) or
(vap^.va_nlink <>VNOVAL) or
(vap^.va_fsid <>VNOVAL) or
(vap^.va_fileid <>VNOVAL) or
(vap^.va_blocksize<>VNOVAL) or
((vap^.va_flags <>VNOVAL) and (vap^.va_flags<>0)) or
(vap^.va_rdev <>VNOVAL) or
(vap^.va_bytes <>VNOVAL) or
(vap^.va_gen <>VNOVAL) then
begin
Exit(EINVAL);
end;
de:=vp^.v_data;
if (vp^.v_type=VDIR) then
begin
de:=de^.de_dir;
end;
error:=0;
c:=0;
if (vap^.va_uid=VNOVAL) then
uid:=de^.de_uid
else
uid:=vap^.va_uid;
if (vap^.va_gid=VNOVAL) then
gid:=de^.de_gid
else
gid:=vap^.va_gid;
if (uid<>de^.de_uid) or (gid<>de^.de_gid) then
begin
//if ((ap^.a_cred^.cr_uid<>de^.de_uid) or uid<>de^.de_uid or
// (gid<>de^.de_gid and !groupmember(gid, ap^.a_cred))) then
//begin
// error:=priv_check(td, PRIV_VFS_CHOWN);
// if (error<>) then
// Exit(error);
//end;
de^.de_uid:=uid;
de^.de_gid:=gid;
c:=1;
end;
if (vap^.va_mode<>VNOVAL) then
begin
//if (ap^.a_cred^.cr_uid<>de^.de_uid) then
//begin
// error:=priv_check(td, PRIV_VFS_ADMIN);
// if (error<>0) then
// Exit(error);
//end;
de^.de_mode:=vap^.va_mode;
c:=1;
end;
if (vap^.va_atime.tv_sec<>VNOVAL) or (vap^.va_mtime.tv_sec<>VNOVAL) then
begin
{ See the comment in ufs_vnops::ufs_setattr(). }
error:=VOP_ACCESS(vp, VADMIN);
if (error<>0) then
begin
if ((vap^.va_vaflags and VA_UTIMES_NULL)=0) then Exit(error);
error:=VOP_ACCESS(vp, VWRITE);
if (error<>0) then Exit(error);
end;
if (vap^.va_atime.tv_sec<>VNOVAL) then
begin
if (vp^.v_type=VCHR) then
p_cdev(vp^.v_rdev)^.si_atime:=vap^.va_atime
else
de^.de_atime:=vap^.va_atime;
end;
if (vap^.va_mtime.tv_sec<>VNOVAL) then
begin
if (vp^.v_type=VCHR) then
p_cdev(vp^.v_rdev)^.si_mtime:=vap^.va_mtime
else
de^.de_mtime:=vap^.va_mtime;
end;
c:=1;
end;
if (c<>0) then
begin
if (vp^.v_type=VCHR) then
vfs_timestamp(@p_cdev(vp^.v_rdev)^.si_ctime)
else
vfs_timestamp(@de^.de_mtime);
end;
Exit(0);
end;
function devfs_stat_f(fp:p_file;sb:p_stat):Integer;
begin
Exit(vnops.fo_stat(fp, sb));
end;
function devfs_symlink(ap:p_vop_symlink_args):Integer;
var
i, error:Integer;
dd:p_devfs_dirent;
de,de_cov,de_dotdot:p_devfs_dirent;
dmp:p_devfs_mount;
begin
error:=0;
//error:=priv_check(curkthread, PRIV_DEVFS_SYMLINK);
if (error<>0) then
begin
Exit(error);
end;
dmp:=VFSTODEVFS(ap^.a_dvp^.v_mount);
if (devfs_populate_vp(ap^.a_dvp)<>0) then
begin
Exit(ENOENT);
end;
dd:=ap^.a_dvp^.v_data;
de_cov:=devfs_find(dd, ap^.a_cnp^.cn_nameptr, ap^.a_cnp^.cn_namelen, 0);
if (de_cov<>nil) then
begin
if ((de_cov^.de_flags and DE_USER)<>0) then
begin
sx_xunlock(@dmp^.dm_lock);
Exit(EEXIST);
end;
Assert((de_cov^.de_flags and DE_COVERED)=0,'devfs_symlink: entry %p already covered');
de_cov^.de_flags:=de_cov^.de_flags or DE_COVERED;
end;
de:=devfs_newdirent(ap^.a_cnp^.cn_nameptr, ap^.a_cnp^.cn_namelen);
de^.de_flags:=DE_USER;
de^.de_uid :=0;
de^.de_gid :=0;
de^.de_mode :=&0755;
de^.de_inode:=devfs_alloc_cdp_inode;
de^.de_dir :=dd;
de^.de_dirent^.d_type:=DT_LNK;
i:=strlen(ap^.a_target) + 1;
de^.de_symlink:=AllocMem(i);
Move(ap^.a_target^, de^.de_symlink^, i);
//mac_devfs_create_symlink(ap^.a_cnp^.cn_cred, dmp^.dm_mount, dd, de);
de_dotdot:=TAILQ_FIRST(@dd^.de_dlist); { '.' }
de_dotdot:=TAILQ_NEXT(de_dotdot,@de_dotdot^.de_list); { '..' }
TAILQ_INSERT_AFTER(@dd^.de_dlist,de_dotdot,de,@de^.de_list);
devfs_dir_ref_de(dmp, dd);
devfs_rules_apply(dmp, de);
Exit(devfs_allocv(de, ap^.a_dvp^.v_mount, LK_EXCLUSIVE, ap^.a_vpp));
end;
function devfs_truncate_f(fp:p_file;length:Int64):Integer;
begin
Exit(vnops.fo_truncate(fp, length));
end;
function devfs_write_f(fp:p_file;uio:p_uio;flags:Integer):Integer;
var
td:p_kthread;
dev:p_cdev;
error,ioflag,ref:Integer;
resid:Int64;
dsw:p_cdevsw;
fpop:p_file;
begin
td:=curkthread;
if (uio^.uio_resid > DEVFS_IOSIZE_MAX) then
begin
Exit(EINVAL);
end;
fpop:=td^.td_fpop;
error:=devfs_fp_check(fp, @dev, @dsw, @ref);
if (error<>0) then Exit(error);
Assert(uio^.uio_td=td, 'uio_td %p is not td %p');
ioflag:=fp^.f_flag and (O_NONBLOCK or O_DIRECT or O_FSYNC);
if ((ioflag and O_DIRECT)<>0) then
begin
ioflag:=ioflag or IO_DIRECT;
end;
foffset_lock_uio(fp, uio, flags or FOF_NOLOCK);
resid:=uio^.uio_resid;
error:=dsw^.d_write(dev, uio, ioflag);
if (uio^.uio_resid<>resid) or ((error=0) and (resid<>0)) then
begin
vfs_timestamp(@dev^.si_ctime);
dev^.si_mtime:=dev^.si_ctime;
end;
td^.td_fpop:=fpop;
dev_relthread(dev, ref);
foffset_unlock_uio(fp, uio, flags or FOF_NOLOCK or FOF_NEXTOFF);
Exit(error);
end;
function dev2udev(x:p_cdev):Integer;
begin
if (x=nil) then Exit(NODEV);
Exit(cdev2priv(x)^.cdp_inode);
end;
end.