FPPS4/sys/vfs/devfs/devfs.pas

550 lines
11 KiB
Plaintext

unit devfs;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
sysutils,
mqueue,
vfile,
vdirent,
vfs_vnode,
vmount,
time,
kern_mtx,
kern_sx;
const
DEVFS_MAGIC=$db0a087a;
{
* Identifiers. The ruleset and rule numbers are 16-bit values. The
* "rule ID" is a combination of the ruleset and rule number; it
* should be able to univocally describe a rule in the system. In
* this implementation, the upper 16 bits of the rule ID is the
* ruleset number; the lower 16 bits, the rule number within the
* aforementioned ruleset.
}
type
p_devfs_rnum=^devfs_rnum;
devfs_rnum=WORD;
p_devfs_rsnum=^devfs_rsnum;
devfs_rsnum=WORD;
p_devfs_rid=^devfs_rid;
devfs_rid=DWORD;
ino_t=Integer;
const
DRC_DSWFLAGS =$001;
DRC_PATHPTRN =$002;
DEVFS_MAXPTRNLEN=200;
DRA_BACTS =$001;
DRA_UID =$002;
DRA_GID =$004;
DRA_MODE =$008;
DRA_INCSET=$010;
DRB_HIDE =$001; { Hide entry (DE_WHITEOUT). }
DRB_UNHIDE=$002; { Unhide entry. }
{
* Plain DEVFS rule. This gets shared between kernel and userland
* verbatim, so it shouldn't contain any pointers or other kernel- or
* userland-specific values.
}
type
p_devfs_rule=^t_devfs_rule;
t_devfs_rule=packed record
dr_magic:DWORD; { Magic number. }
dr_id :devfs_rid; { Identifier. }
{
* Conditions under which this rule should be applied. These
* are ANDed together since OR can be simulated by using
* multiple rules. dr_icond determines which of the other
* variables we should process.
}
dr_icond:Integer;
dr_dswflags:Integer; { cdevsw flags to match. }
dr_pathptrn:array[0..DEVFS_MAXPTRNLEN-1] of AnsiChar; { Pattern to match path. }
{
* Things to change. dr_iacts determines which of the other
* variables we should process.
}
dr_iacts:Integer;
dr_bacts:Integer; { Boolean (on/off) action. }
dr_uid :uid_t;
dr_gid :gid_t;
dr_mode :mode_t;
dr_incset:devfs_rsnum; { Included ruleset. }
end;
const
{
* Rule-related ioctls.
}
DEVFSIO_RADD =$C0F04400;
DEVFSIO_RDEL =$80044401;
DEVFSIO_RAPPLY =$80F04402;
DEVFSIO_RAPPLYID=$80044403;
DEVFSIO_RGETNEXT=$C0F04404;
DEVFSIO_SUSE =$8002440A;
DEVFSIO_SAPPLY =$8002440B;
DEVFSIO_SGETNEXT=$C002440C;
{ XXX: DEVFSIO_RS_GET_INFO for refcount, active if any, etc. }
//struct componentname;
DE_WHITEOUT=$01;
DE_DOT =$02;
DE_DOTDOT =$04;
DE_DOOMED =$08;
DE_COVERED =$10;
DE_USER =$20;
CDP_ACTIVE =(1 shl 0);
CDP_SCHED_DTR=(1 shl 1);
CDP_UNREF_DTR=(1 shl 2);
SI_ETERNAL =$0001; { never destroyed }
SI_ALIAS =$0002; { carrier of alias name }
SI_NAMED =$0004; { make_dev _alias has been called }
SI_CHEAPCLONE=$0008; { can be removed_dev'ed when vnode reclaims }
SI_CHILD =$0010; { child of another struct cdev *}
SI_DEVOPEN =$0020; { opened by device }
SI_CONSOPEN =$0040; { opened by console }
SI_DUMPDEV =$0080; { is kernel dumpdev }
SI_CANDELETE =$0100; { can do BIO_DELETE }
SI_CLONELIST =$0200; { on a clone list }
SI_UNMAPPED =$0400; { can handle unmapped I/O }
SPECNAMELEN =63; { max length of devicename }
type
p_cdev=^t_cdev;
t_cdev=packed record
si_mountpt :p_mount;
si_flags :DWORD;
_align1 :Integer;
si_atime :timespec;
si_ctime :timespec;
si_mtime :timespec;
si_uid :uid_t;
si_gid :gid_t;
si_mode :mode_t;
si_drv0 :Integer;
si_refcount :Integer;
_align2 :Integer;
si_list :LIST_ENTRY; //(cdev)
si_clone :LIST_ENTRY; //(cdev)
si_children :Pointer ; //(cdev)
si_siblings :LIST_ENTRY; //(cdev)
si_parent :p_cdev;
si_name :PChar;
si_drv1 :Pointer;
si_drv2 :Pointer;
si_devsw :Pointer; //cdevsw
si_iosize_max :Integer; { maximum I/O size (for physio &al) }
_align3 :Integer;
si_usecount :QWORD;
si_threadcount:QWORD;
__si_namebuf :array[0..SPECNAMELEN] of AnsiChar;
end;
t_cdpd_dtr=procedure(P:Pointer);
p_cdev_privdata=^t_cdev_privdata;
t_cdev_privdata=packed record
cdpd_fp :p_file;
cdpd_data:Pointer;
cdpd_dtr :t_cdpd_dtr;
cdpd_list:LIST_ENTRY; //cdev_privdata
end;
pp_devfs_dirent=^p_devfs_dirent;
p_devfs_dirent=^t_devfs_dirent;
p_cdev_priv=^t_cdev_priv;
t_cdev_priv=packed record
cdp_c:t_cdev;
cdp_list:TAILQ_ENTRY; //cdev_priv
cdp_inode:DWORD;
cdp_flags:DWORD;
cdp_inuse :DWORD;
cdp_maxdirent:DWORD;
cdp_dirents:pp_devfs_dirent;
cdp_dirent0:p_devfs_dirent;
cdp_dtr_list:TAILQ_ENTRY; //cdev_priv
cdp_dtr_cb :t_cdpd_dtr;
cdp_dtr_cb_arg:Pointer;
cdp_fdpriv:Pointer; //cdev_privdata
end;
devfs_dlist_head=TAILQ_HEAD; //devfs_dirent
t_devfs_dirent=packed record
de_cdp :p_cdev_priv;
de_inode :Integer;
de_flags :Integer;
de_holdcnt:Integer;
_align :Integer;
de_dirent :p_dirent;
de_list :TAILQ_ENTRY;
de_dlist :devfs_dlist_head;
de_dir :p_devfs_dirent;
de_links :Integer;
de_mode :mode_t;
de_uid :uid_t;
de_gid :gid_t;
//label *de_label;
de_atime :timespec;
de_mtime :timespec;
de_ctime :timespec;
de_vnode :p_vnode;
de_symlink:PChar;
end;
p_devfs_mount=^t_devfs_mount;
t_devfs_mount=packed record
dm_idx :DWORD ;
dm_mount :p_mount ;
dm_rootdir :p_devfs_dirent;
dm_generation:DWORD ;
dm_holdcnt :Integer ;
dm_lock :t_sx ;
dm_ruleset :devfs_rsnum ;
end;
const
DEVFS_ROOTINO=2;
DEVFS_DEL_VNLOCKED =$01;
DEVFS_DEL_NORECURSE=$02;
var
devfs_rule_depth:DWORD=0;
function rid2rsn(rid:devfs_rid):devfs_rsnum;
function rid2rn (rid:devfs_rid):devfs_rnum;
function mkrid (rsn:devfs_rsnum;rn:devfs_rnum):devfs_rid;
function VFSTODEVFS(mp:p_mount):p_devfs_mount; inline;
procedure DEVFS_DE_HOLD(de:p_devfs_dirent); inline;
function DEVFS_DE_DROP(de:p_devfs_dirent):Boolean; inline;
procedure DEVFS_DMP_HOLD(dmp:p_devfs_mount); inline;
function DEVFS_DMP_DROP(dmp:p_devfs_mount):Boolean; inline;
function cdev2priv(c:Pointer):p_cdev_priv; inline;
var
devmtx:mtx;
devfs_de_interlock:mtx;
//
type
p_dirlistent=^t_dirlistent;
t_dirlistent=packed record
dir :PChar;
refcnt:Integer;
_align:Integer;
link :LIST_ENTRY; //dirlistent
end;
var
devfs_dirlist:Pointer=nil; //dirlistent
dirlist_mtx :mtx;
devfs_generation:DWORD=0;
procedure dev_lock();
procedure dev_unlock();
procedure dev_ref(dev:p_cdev);
procedure dev_refl(dev:p_cdev);
procedure dev_rel(dev:p_cdev);
function devfs_dir_find(path:PChar):Integer;
function devfs_dir_findent_locked(dir:PChar):p_dirlistent;
procedure devfs_dir_ref(dir:PChar);
procedure devfs_dir_ref_de(dm:p_devfs_mount;de:p_devfs_dirent);
procedure devfs_dir_unref(dir:PChar);
procedure devfs_dir_unref_de(dm:p_devfs_mount;de:p_devfs_dirent);
function devfs_pathpath(p1,p2:PChar):Integer;
procedure devfs_mtx_init; //MTX_SYSINIT(dirlist_mtx, &dirlist_mtx, "devfs dirlist lock", MTX_DEF);
//MTX_SYSINIT(devfs_de_interlock, &devfs_de_interlock, "devfs interlock", MTX_DEF);
implementation
uses
devfs_devs;
{
* Identifier manipulators.
}
function rid2rsn(rid:devfs_rid):devfs_rsnum;
begin
Result:=rid shr 16;
end;
function rid2rn(rid:devfs_rid):devfs_rnum;
begin
Result:=rid and $ffff;
end;
function mkrid(rsn:devfs_rsnum;rn:devfs_rnum):devfs_rid;
begin
Result:=rn or (rsn shl 16);
end;
function VFSTODEVFS(mp:p_mount):p_devfs_mount; inline;
begin
Result:=mp^.mnt_data;
end;
procedure DEVFS_DE_HOLD(de:p_devfs_dirent); inline;
begin
Inc(de^.de_holdcnt);
end;
function DEVFS_DE_DROP(de:p_devfs_dirent):Boolean; inline;
begin
Dec(de^.de_holdcnt);
Result:=(de^.de_holdcnt=0);
end;
procedure DEVFS_DMP_HOLD(dmp:p_devfs_mount); inline;
begin
Inc(dmp^.dm_holdcnt);
end;
function DEVFS_DMP_DROP(dmp:p_devfs_mount):Boolean; inline;
begin
Dec(dmp^.dm_holdcnt);
Result:=(dmp^.dm_holdcnt=0);
end;
function cdev2priv(c:Pointer):p_cdev_priv; inline;
begin
Result:=c-ptruint(@p_cdev_priv(nil)^.cdp_c);
end;
//
procedure dev_lock();
begin
mtx_lock(devmtx);
end;
procedure dev_unlock();
begin
mtx_unlock(devmtx);
end;
procedure dev_ref(dev:p_cdev);
begin
mtx_lock(devmtx);
Inc(dev^.si_refcount);
mtx_unlock(devmtx);
end;
procedure dev_refl(dev:p_cdev);
begin
mtx_assert(devmtx);
Inc(dev^.si_refcount);
end;
procedure dev_rel(dev:p_cdev);
var
flag:Integer;
begin
flag:=0;
dev_lock();
Dec(dev^.si_refcount);
Assert(dev^.si_refcount >= 0,'dev_rel(%s) gave negative count');
if (dev^.si_devsw=nil) and
(dev^.si_refcount=0) then
begin
LIST_REMOVE(dev,@dev^.si_list);
flag:=1;
end;
dev_unlock();
if (flag<>0) then
devfs_free(dev);
end;
//
{ Exits 1 if the path is in the directory list. }
function devfs_dir_find(path:PChar):Integer;
var
dle:p_dirlistent;
begin
mtx_lock(dirlist_mtx);
dle:=LIST_FIRST(@devfs_dirlist);
while (dle<>nil) do
begin
if (devfs_pathpath(dle^.dir, path)<>0) then
begin
mtx_unlock(dirlist_mtx);
Exit(1);
end;
//
dle:=LIST_NEXT(dle,@dle^.link);
end;
mtx_unlock(dirlist_mtx);
Exit(0);
end;
function devfs_dir_findent_locked(dir:PChar):p_dirlistent;
var
dle:p_dirlistent;
begin
mtx_assert(dirlist_mtx);
dle:=LIST_FIRST(@devfs_dirlist);
while (dle<>nil) do
begin
if (strcomp(dir, dle^.dir)=0) then
Exit(dle);
//
dle:=LIST_NEXT(dle,@dle^.link);
end;
Exit(nil);
end;
function strdup(src:PChar):PChar; inline;
var
i:ptrint;
begin
i:=strlen(src);
Result:=AllocMem(i+1);
Move(src^,Result^,i);
end;
procedure devfs_dir_ref(dir:PChar);
var
dle,dle_new:p_dirlistent;
begin
if (dir^=#0) then
Exit;
dle_new:=AllocMem(sizeof(t_dirlistent));
dle_new^.dir:=strdup(dir);
dle_new^.refcnt:=1;
mtx_lock(dirlist_mtx);
dle:=devfs_dir_findent_locked(dir);
if (dle<>nil) then
begin
Inc(dle^.refcnt);
mtx_unlock(dirlist_mtx);
FreeMem(dle_new^.dir);
FreeMem(dle_new);
Exit;
end;
LIST_INSERT_HEAD(@devfs_dirlist,dle_new,@dle_new^.link);
mtx_unlock(dirlist_mtx);
end;
procedure devfs_dir_ref_de(dm:p_devfs_mount;de:p_devfs_dirent);
var
dirname:array[0..SPECNAMELEN] of AnsiChar;
namep:PChar;
begin
namep:=nil;
//devfs_vnops
//namep:=devfs_fqpn(dirname, dm, de, nil);
Assert(namep<>nil,'devfs_ref_dir_de: nil namep');
devfs_dir_ref(namep);
end;
procedure devfs_dir_unref(dir:PChar);
var
dle:p_dirlistent;
begin
if (dir^=#0) then
Exit;
mtx_lock(dirlist_mtx);
dle:=devfs_dir_findent_locked(dir);
Assert(dle<>nil, 'devfs_dir_unref: dir %s not referenced');
Dec(dle^.refcnt);
Assert(dle^.refcnt >= 0, 'devfs_dir_unref: negative refcnt');
if (dle^.refcnt=0) then
begin
LIST_REMOVE(dle,@dle^.link);
mtx_unlock(dirlist_mtx);
FreeMem(dle^.dir);
FreeMem(dle);
end else
mtx_unlock(dirlist_mtx);
end;
procedure devfs_dir_unref_de(dm:p_devfs_mount;de:p_devfs_dirent);
var
dirname:array[0..SPECNAMELEN] of AnsiChar;
namep:PChar;
begin
namep:=nil;
//devfs_vnops
//namep:=devfs_fqpn(dirname, dm, de, nil);
Assert(namep<>nil, 'devfs_unref_dir_de: nil namep');
devfs_dir_unref(namep);
end;
{ Exits 1 if the path p1 contains the path p2. }
function devfs_pathpath(p1,p2:PChar):Integer;
begin
repeat
if (p1^<>p2^) then
begin
if (p1^='/') and (p2^=#0) then
Exit(1)
else
Exit(0);
end else if (p1^=#0) then
Exit(1);
until false;
{ NOTREACHED }
end;
procedure devfs_mtx_init;
begin
mtx_init(devmtx,'cdev');
mtx_init(dirlist_mtx,'devfs dirlist lock');
mtx_init(devfs_de_interlock,'devfs interlock');
end;
end.