mirror of https://github.com/red-prig/fpPS4.git
433 lines
8.4 KiB
Plaintext
433 lines
8.4 KiB
Plaintext
unit devfs;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
sysutils,
|
|
mqueue,
|
|
vfile,
|
|
vdirent,
|
|
vnode,
|
|
vmount,
|
|
time,
|
|
kern_conf,
|
|
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. }
|
|
|
|
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);
|
|
|
|
type
|
|
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:LIST_HEAD; //cdev_privdata
|
|
end;
|
|
|
|
devfs_dlist_head=TAILQ_HEAD; //devfs_dirent
|
|
|
|
t_devfs_dirent=record
|
|
de_cdp :p_cdev_priv;
|
|
de_inode :Integer;
|
|
de_flags :Integer;
|
|
de_holdcnt: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=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;
|
|
|
|
procedure DEVFS_DE_HOLD(de:p_devfs_dirent);
|
|
function DEVFS_DE_DROP(de:p_devfs_dirent):Boolean;
|
|
|
|
procedure DEVFS_DMP_HOLD(dmp:p_devfs_mount);
|
|
function DEVFS_DMP_DROP(dmp:p_devfs_mount):Boolean;
|
|
|
|
//
|
|
|
|
type
|
|
p_dirlistent=^t_dirlistent;
|
|
t_dirlistent=record
|
|
dir :PChar;
|
|
refcnt:Integer;
|
|
link :LIST_ENTRY; //dirlistent
|
|
end;
|
|
|
|
var
|
|
devfs_dirlist:LIST_HEAD=(lh_first:nil); //dirlistent
|
|
dirlist_mtx :mtx; //MTX_SYSINIT(dirlist_mtx, &dirlist_mtx, "devfs dirlist lock", MTX_DEF);
|
|
|
|
devfs_generation:DWORD=0;
|
|
|
|
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;
|
|
|
|
implementation
|
|
|
|
uses
|
|
devfs_devs,
|
|
devfs_vnops;
|
|
|
|
{
|
|
* 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;
|
|
|
|
procedure DEVFS_DE_HOLD(de:p_devfs_dirent);
|
|
begin
|
|
Inc(de^.de_holdcnt);
|
|
end;
|
|
|
|
function DEVFS_DE_DROP(de:p_devfs_dirent):Boolean;
|
|
begin
|
|
Dec(de^.de_holdcnt);
|
|
Result:=(de^.de_holdcnt=0);
|
|
end;
|
|
|
|
procedure DEVFS_DMP_HOLD(dmp:p_devfs_mount);
|
|
begin
|
|
Inc(dmp^.dm_holdcnt);
|
|
end;
|
|
|
|
function DEVFS_DMP_DROP(dmp:p_devfs_mount):Boolean;
|
|
begin
|
|
Dec(dmp^.dm_holdcnt);
|
|
Result:=(dmp^.dm_holdcnt=0);
|
|
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:=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:=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);
|
|
Inc(p1);
|
|
Inc(p2);
|
|
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');
|
|
mtx_init(cdevpriv_mtx,'cdevpriv lock');
|
|
end;
|
|
|
|
|
|
end.
|
|
|
|
|