mirror of https://github.com/red-prig/fpPS4.git
2077 lines
43 KiB
Plaintext
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.
|
|
|