mirror of https://github.com/red-prig/fpPS4.git
715 lines
16 KiB
Plaintext
715 lines
16 KiB
Plaintext
unit devfs_devs;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
mqueue,
|
|
kern_id,
|
|
sys_conf,
|
|
devfs_int,
|
|
devfs;
|
|
|
|
function devfs_alloc(flags:Integer):p_cdev;
|
|
function devfs_dev_exists(name:PChar):Integer;
|
|
procedure devfs_free(cdev:p_cdev);
|
|
function devfs_find(dd:p_devfs_dirent;name:PChar;namelen:Integer;_type:Integer):p_devfs_dirent;
|
|
function devfs_newdirent(name:PChar;namelen:Integer):p_devfs_dirent;
|
|
function devfs_parent_dirent(de:p_devfs_dirent):p_devfs_dirent;
|
|
function devfs_vmkdir(dmp:p_devfs_mount;name:PChar;namelen:Integer;dotdot:p_devfs_dirent;inode:DWORD):p_devfs_dirent;
|
|
procedure devfs_dirent_free(de:p_devfs_dirent);
|
|
procedure devfs_rmdir_empty(dm:p_devfs_mount;de:p_devfs_dirent);
|
|
procedure devfs_delete(dm:p_devfs_mount;de:p_devfs_dirent;flags:Integer);
|
|
procedure devfs_purge(dm:p_devfs_mount;dd:p_devfs_dirent);
|
|
procedure devfs_metoo(cdp:p_cdev_priv;dm:p_devfs_mount);
|
|
function devfs_populate_loop(dm:p_devfs_mount;cleanup:Integer):Integer;
|
|
procedure devfs_populate(dm:p_devfs_mount);
|
|
procedure devfs_cleanup(dm:p_devfs_mount);
|
|
procedure devfs_create(dev:p_cdev);
|
|
procedure devfs_destroy(dev:p_cdev);
|
|
function devfs_alloc_cdp_inode():ino_t;
|
|
procedure devfs_free_cdp_inode(ino:ino_t);
|
|
procedure devfs_devs_init(); //SYSINIT(devfs_devs, SI_SUB_DEVFS, SI_ORDER_FIRST, devfs_devs_init, NULL);
|
|
|
|
implementation
|
|
|
|
uses
|
|
time,
|
|
vdirent,
|
|
vnode,
|
|
kern_mtx,
|
|
kern_sx,
|
|
vfs_vnops,
|
|
vfs_subr,
|
|
vnode_if;
|
|
|
|
var
|
|
devfs_desc:t_id_desc=(free:nil;refs:0);
|
|
devfs_inos:t_id_desc_table;
|
|
|
|
//
|
|
|
|
function cdev2priv(c:Pointer):p_cdev_priv; inline;
|
|
begin
|
|
Result:=c-ptruint(@p_cdev_priv(nil)^.cdp_c);
|
|
end;
|
|
|
|
function devfs_alloc(flags:Integer):p_cdev; public;
|
|
var
|
|
cdp:p_cdev_priv;
|
|
cdev:p_cdev;
|
|
ts:timespec;
|
|
begin
|
|
cdp:=AllocMem(SizeOf(t_cdev_priv));
|
|
|
|
if (cdp=nil) then
|
|
Exit(nil);
|
|
|
|
cdp^.cdp_dirents:=@cdp^.cdp_dirent0;
|
|
cdp^.cdp_dirent0:=nil;
|
|
cdp^.cdp_maxdirent:=0;
|
|
cdp^.cdp_inode:=0;
|
|
|
|
cdev:=@cdp^.cdp_c;
|
|
|
|
cdev^.si_name:=cdev^.__si_namebuf;
|
|
LIST_INIT(@cdev^.si_children);
|
|
vfs_timestamp(@ts);
|
|
cdev^.si_atime:=ts;
|
|
cdev^.si_mtime:=ts;
|
|
cdev^.si_ctime:=ts;
|
|
|
|
Exit(cdev);
|
|
end;
|
|
|
|
function devfs_dev_exists(name:PChar):Integer; public;
|
|
var
|
|
cdp:p_cdev_priv;
|
|
begin
|
|
mtx_assert(devmtx);
|
|
|
|
cdp:=TAILQ_FIRST(@cdevp_list);
|
|
while (cdp<>nil) do
|
|
begin
|
|
if ((cdp^.cdp_flags and CDP_ACTIVE)=0) then
|
|
begin
|
|
cdp:=TAILQ_NEXT(cdp,@cdp^.cdp_list);
|
|
continue;
|
|
end;
|
|
if (devfs_pathpath(cdp^.cdp_c.si_name, name)<>0) then
|
|
Exit(1);
|
|
if (devfs_pathpath(name, cdp^.cdp_c.si_name)<>0) then
|
|
Exit(1);
|
|
//
|
|
cdp:=TAILQ_NEXT(cdp,@cdp^.cdp_list);
|
|
end;
|
|
if (devfs_dir_find(name)<>0) then
|
|
Exit(1);
|
|
|
|
Exit(0);
|
|
end;
|
|
|
|
procedure devfs_free(cdev:p_cdev); public;
|
|
var
|
|
cdp:p_cdev_priv;
|
|
begin
|
|
cdp:=cdev2priv(cdev);
|
|
devfs_free_cdp_inode(cdp^.cdp_inode);
|
|
if (cdp^.cdp_maxdirent > 0) then
|
|
FreeMem(cdp^.cdp_dirents);
|
|
FreeMem(cdp);
|
|
end;
|
|
|
|
function devfs_find(dd:p_devfs_dirent;name:PChar;namelen:Integer;_type:Integer):p_devfs_dirent; public;
|
|
var
|
|
de:p_devfs_dirent;
|
|
begin
|
|
de:=TAILQ_FIRST(@dd^.de_dlist);
|
|
while (de<>nil) do
|
|
begin
|
|
if (namelen<>de^.de_dirent^.d_namlen) then
|
|
begin
|
|
de:=TAILQ_NEXT(de,@de^.de_list);
|
|
continue;
|
|
end;
|
|
if (_type<>0) and (_type<>de^.de_dirent^.d_type) then
|
|
begin
|
|
de:=TAILQ_NEXT(de,@de^.de_list);
|
|
continue;
|
|
end;
|
|
|
|
if (CompareByte(name^, de^.de_dirent^.d_name, namelen)<>0) then
|
|
begin
|
|
de:=TAILQ_NEXT(de,@de^.de_list);
|
|
continue;
|
|
end;
|
|
break;
|
|
end;
|
|
|
|
if (de<>nil) then
|
|
begin
|
|
Assert((de^.de_flags and DE_DOOMED)=0,'devfs_find: Exiting a doomed entry');
|
|
end;
|
|
Exit(de);
|
|
end;
|
|
|
|
function devfs_newdirent(name:PChar;namelen:Integer):p_devfs_dirent; public;
|
|
var
|
|
i:Integer;
|
|
de:p_devfs_dirent;
|
|
d:t_dirent;
|
|
begin
|
|
d.d_namlen:=namelen;
|
|
i:=sizeof(t_devfs_dirent) + GENERIC_DIRSIZ(@d);
|
|
de:=AllocMem(i);
|
|
de^.de_dirent:=p_dirent(de + 1);
|
|
de^.de_dirent^.d_namlen:=namelen;
|
|
de^.de_dirent^.d_reclen:=GENERIC_DIRSIZ(@d);
|
|
Move(name^, de^.de_dirent^.d_name, namelen);
|
|
de^.de_dirent^.d_name[namelen]:=#0;
|
|
vfs_timestamp(@de^.de_ctime);
|
|
de^.de_mtime :=de^.de_ctime;
|
|
de^.de_atime :=de^.de_ctime;
|
|
de^.de_links :=1;
|
|
de^.de_holdcnt:=1;
|
|
|
|
//mac_devfs_init(de);
|
|
|
|
Exit(de);
|
|
end;
|
|
|
|
function devfs_parent_dirent(de:p_devfs_dirent):p_devfs_dirent; public;
|
|
begin
|
|
if (de^.de_dirent^.d_type<>DT_DIR) then
|
|
begin
|
|
Exit(de^.de_dir);
|
|
end;
|
|
|
|
if ((de^.de_flags and (DE_DOT or DE_DOTDOT))<>0) then
|
|
begin
|
|
Exit(nil);
|
|
end;
|
|
|
|
de:=TAILQ_FIRST(@de^.de_dlist); { '.' }
|
|
if (de=nil) then Exit(nil);
|
|
|
|
de:=TAILQ_NEXT(de,@de^.de_list); { '..' }
|
|
if (de=nil) then Exit(nil);
|
|
|
|
Exit(de^.de_dir);
|
|
end;
|
|
|
|
function devfs_vmkdir(dmp:p_devfs_mount;name:PChar;namelen:Integer;dotdot:p_devfs_dirent;inode:DWORD):p_devfs_dirent; public;
|
|
var
|
|
dd,de:p_devfs_dirent;
|
|
begin
|
|
{ Create the new directory }
|
|
dd:=devfs_newdirent(name, namelen);
|
|
TAILQ_INIT(@dd^.de_dlist);
|
|
dd^.de_dirent^.d_type:=DT_DIR;
|
|
dd^.de_mode :=&0555;
|
|
dd^.de_links:=2;
|
|
dd^.de_dir :=dd;
|
|
|
|
if (inode<>0) then
|
|
dd^.de_inode:=inode
|
|
else
|
|
dd^.de_inode:=devfs_alloc_cdp_inode;
|
|
|
|
{
|
|
* '.' and '..' are always the two first entries in the
|
|
* de_dlist list.
|
|
*
|
|
* Create the '.' entry in the new directory.
|
|
}
|
|
de:=devfs_newdirent('.', 1);
|
|
de^.de_dirent^.d_type:=DT_DIR;
|
|
de^.de_flags:=de^.de_flags or DE_DOT;
|
|
TAILQ_INSERT_TAIL(@dd^.de_dlist,de,@de^.de_list);
|
|
de^.de_dir:=dd;
|
|
|
|
{ Create the '..' entry in the new directory. }
|
|
de:=devfs_newdirent('..', 2);
|
|
de^.de_dirent^.d_type:=DT_DIR;
|
|
de^.de_flags:=de^.de_flags or DE_DOTDOT;
|
|
TAILQ_INSERT_TAIL(@dd^.de_dlist,de,@de^.de_list);
|
|
if (dotdot=nil) then
|
|
begin
|
|
de^.de_dir:=dd;
|
|
end else
|
|
begin
|
|
de^.de_dir:=dotdot;
|
|
sx_assert(@dmp^.dm_lock);
|
|
TAILQ_INSERT_TAIL(@dotdot^.de_dlist,dd,@dd^.de_list);
|
|
Inc(dotdot^.de_links);
|
|
devfs_rules_apply(dmp, dd);
|
|
end;
|
|
|
|
//mac_devfs_create_directory(dmp^.dm_mount, name, namelen, dd);
|
|
|
|
Exit(dd);
|
|
end;
|
|
|
|
procedure devfs_dirent_free(de:p_devfs_dirent); public;
|
|
begin
|
|
FreeMem(de);
|
|
end;
|
|
|
|
{
|
|
* Removes a directory if it is empty. Also empty parent directories are
|
|
* removed recursively.
|
|
}
|
|
procedure devfs_rmdir_empty(dm:p_devfs_mount;de:p_devfs_dirent); public;
|
|
var
|
|
dd,de_dot,de_dotdot:p_devfs_dirent;
|
|
begin
|
|
sx_assert(@dm^.dm_lock);
|
|
|
|
repeat
|
|
Assert(de^.de_dirent^.d_type=DT_DIR,'devfs_rmdir_empty: de is not a directory');
|
|
|
|
if ((de^.de_flags and DE_DOOMED)<>0) or (de=dm^.dm_rootdir) then
|
|
Exit;
|
|
|
|
de_dot:=TAILQ_FIRST(@de^.de_dlist);
|
|
Assert(de_dot<>nil, 'devfs_rmdir_empty: . missing');
|
|
|
|
de_dotdot:=TAILQ_NEXT(de_dot,@de_dot^.de_list);
|
|
Assert(de_dotdot<>nil, 'devfs_rmdir_empty: .. missing');
|
|
|
|
{ Exit if the directory is not empty. }
|
|
if (TAILQ_NEXT(de_dotdot,@de_dotdot^.de_list)<>nil) then
|
|
begin
|
|
Exit;
|
|
end;
|
|
|
|
dd:=devfs_parent_dirent(de);
|
|
Assert(dd<>nil, 'devfs_rmdir_empty: nil dd');
|
|
|
|
TAILQ_REMOVE(@de^.de_dlist,de_dot,@de_dot^.de_list);
|
|
TAILQ_REMOVE(@de^.de_dlist,de_dotdot,@de_dotdot^.de_list);
|
|
TAILQ_REMOVE(@dd^.de_dlist,de,@de^.de_list);
|
|
|
|
DEVFS_DE_HOLD(dd);
|
|
|
|
devfs_delete(dm, de ,DEVFS_DEL_NORECURSE);
|
|
devfs_delete(dm, de_dot ,DEVFS_DEL_NORECURSE);
|
|
devfs_delete(dm, de_dotdot,DEVFS_DEL_NORECURSE);
|
|
|
|
if (DEVFS_DE_DROP(dd)) then
|
|
begin
|
|
devfs_dirent_free(dd);
|
|
Exit;
|
|
end;
|
|
|
|
de:=dd;
|
|
until false;
|
|
end;
|
|
|
|
{
|
|
* The caller needs to hold the dm for the duration of the call since
|
|
* dm^.dm_lock may be temporary dropped.
|
|
}
|
|
procedure devfs_delete(dm:p_devfs_mount;de:p_devfs_dirent;flags:Integer); public;
|
|
var
|
|
dd:p_devfs_dirent;
|
|
vp:p_vnode;
|
|
begin
|
|
Assert((de^.de_flags and DE_DOOMED)=0,'devfs_delete doomed dirent');
|
|
de^.de_flags:=de^.de_flags or DE_DOOMED;
|
|
|
|
if ((flags and DEVFS_DEL_NORECURSE)=0) then
|
|
begin
|
|
dd:=devfs_parent_dirent(de);
|
|
if (dd<>nil) then
|
|
begin
|
|
DEVFS_DE_HOLD(dd);
|
|
end;
|
|
if (de^.de_flags and DE_USER)<>0 then
|
|
begin
|
|
Assert(dd<>nil,'devfs_delete: nil dd');
|
|
devfs_dir_unref_de(dm, dd);
|
|
end;
|
|
end else
|
|
begin
|
|
dd:=nil;
|
|
end;
|
|
|
|
mtx_lock(devfs_de_interlock);
|
|
vp:=de^.de_vnode;
|
|
if (vp<>nil) then
|
|
begin
|
|
VI_LOCK(vp);
|
|
mtx_unlock(devfs_de_interlock);
|
|
vholdl(vp);
|
|
sx_unlock(@dm^.dm_lock);
|
|
|
|
if ((flags and DEVFS_DEL_VNLOCKED)=0) then
|
|
vn_lock(vp, LK_EXCLUSIVE or LK_INTERLOCK or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%})
|
|
else
|
|
VI_UNLOCK(vp);
|
|
|
|
vgone(vp);
|
|
|
|
if ((flags and DEVFS_DEL_VNLOCKED)=0) then
|
|
begin
|
|
VOP_UNLOCK(vp, 0);
|
|
end;
|
|
|
|
vdrop(vp);
|
|
sx_xlock(@dm^.dm_lock);
|
|
end else
|
|
begin
|
|
mtx_unlock(devfs_de_interlock);
|
|
end;
|
|
|
|
if (de^.de_symlink<>nil) then
|
|
begin
|
|
FreeMem(de^.de_symlink);
|
|
de^.de_symlink:=nil;
|
|
end;
|
|
|
|
//mac_devfs_destroy(de);
|
|
|
|
if (de^.de_inode > DEVFS_ROOTINO) then
|
|
begin
|
|
devfs_free_cdp_inode(de^.de_inode);
|
|
de^.de_inode:=0;
|
|
end;
|
|
|
|
if DEVFS_DE_DROP(de) then
|
|
begin
|
|
devfs_dirent_free(de);
|
|
end;
|
|
|
|
if (dd<>nil) then
|
|
begin
|
|
if DEVFS_DE_DROP(dd) then
|
|
devfs_dirent_free(dd)
|
|
else
|
|
devfs_rmdir_empty(dm, dd);
|
|
end;
|
|
end;
|
|
|
|
{
|
|
* Called on unmount.
|
|
* Recursively removes the entire tree.
|
|
* The caller needs to hold the dm for the duration of the call.
|
|
}
|
|
procedure devfs_purge(dm:p_devfs_mount;dd:p_devfs_dirent); public;
|
|
var
|
|
de:p_devfs_dirent;
|
|
begin
|
|
if (dm=nil) or (dd=nil) then Exit;
|
|
|
|
sx_assert(@dm^.dm_lock);
|
|
|
|
DEVFS_DE_HOLD(dd);
|
|
repeat
|
|
{
|
|
* Use TAILQ_LAST() to remove '.' and '..' last.
|
|
* We might need '..' to resolve a path in
|
|
* devfs_dir_unref_de().
|
|
}
|
|
de:=TAILQ_LAST(@dd^.de_dlist);
|
|
if (de=nil) then break;
|
|
|
|
TAILQ_REMOVE(@dd^.de_dlist,de,@de^.de_list);
|
|
if ((de^.de_flags and DE_USER)<>0) then
|
|
devfs_dir_unref_de(dm, dd);
|
|
|
|
if ((de^.de_flags and (DE_DOT or DE_DOTDOT))<>0) then
|
|
devfs_delete(dm, de, DEVFS_DEL_NORECURSE)
|
|
else
|
|
if (de^.de_dirent^.d_type=DT_DIR) then
|
|
devfs_purge(dm, de)
|
|
else
|
|
devfs_delete(dm, de, DEVFS_DEL_NORECURSE);
|
|
|
|
until false;
|
|
|
|
if DEVFS_DE_DROP(dd) then
|
|
devfs_dirent_free(dd)
|
|
else
|
|
if ((dd^.de_flags and DE_DOOMED)=0) then
|
|
devfs_delete(dm, dd, DEVFS_DEL_NORECURSE);
|
|
end;
|
|
|
|
{
|
|
* Each cdev_priv has an array of pointers to devfs_dirent which is indexed
|
|
* by the mount points dm_idx.
|
|
* This function extends the array when necessary, taking into account that
|
|
* the default array is 1 element and not malloc'ed.
|
|
}
|
|
procedure devfs_metoo(cdp:p_cdev_priv;dm:p_devfs_mount); public;
|
|
var
|
|
dep:pp_devfs_dirent;
|
|
siz:Integer;
|
|
begin
|
|
siz:=(dm^.dm_idx + 1) * sizeof(Pointer);
|
|
dep:=AllocMem(siz);
|
|
dev_lock();
|
|
if (dm^.dm_idx <= cdp^.cdp_maxdirent) then
|
|
begin
|
|
{ We got raced }
|
|
dev_unlock();
|
|
FreeMem(dep);
|
|
Exit;
|
|
end;
|
|
Move(cdp^.cdp_dirents^,dep^,(cdp^.cdp_maxdirent + 1)*SizeOf(Pointer));
|
|
|
|
if (cdp^.cdp_maxdirent > 0) then
|
|
begin
|
|
FreeMem(cdp^.cdp_dirents);
|
|
end;
|
|
|
|
cdp^.cdp_dirents:=dep;
|
|
{
|
|
* XXX: if malloc told us how much we actually got this could
|
|
* XXX: be optimized.
|
|
}
|
|
cdp^.cdp_maxdirent:=dm^.dm_idx;
|
|
dev_unlock();
|
|
end;
|
|
|
|
{
|
|
* The caller needs to hold the dm for the duration of the call.
|
|
}
|
|
function devfs_populate_loop(dm:p_devfs_mount;cleanup:Integer):Integer; public;
|
|
var
|
|
cdp:p_cdev_priv;
|
|
de:p_devfs_dirent;
|
|
dd:p_devfs_dirent;
|
|
pdev:p_cdev;
|
|
de_flags,j:Integer;
|
|
q,s:PChar;
|
|
begin
|
|
sx_assert(@dm^.dm_lock);
|
|
dev_lock();
|
|
|
|
cdp:=TAILQ_FIRST(@cdevp_list);
|
|
while (cdp<>nil) do
|
|
begin
|
|
Assert(cdp^.cdp_dirents<>nil,'nil cdp_dirents');
|
|
|
|
{
|
|
* If we are unmounting, or the device has been destroyed,
|
|
* clean up our dirent.
|
|
}
|
|
if ((cleanup<>0) or
|
|
((cdp^.cdp_flags and CDP_ACTIVE)=0)) and
|
|
(dm^.dm_idx <= cdp^.cdp_maxdirent) and
|
|
(cdp^.cdp_dirents[dm^.dm_idx]<>nil) then
|
|
begin
|
|
de:=cdp^.cdp_dirents[dm^.dm_idx];
|
|
cdp^.cdp_dirents[dm^.dm_idx]:=nil;
|
|
|
|
Assert(cdp=de^.de_cdp,cdp^.cdp_c.si_name);
|
|
Assert(de^.de_dir<>nil,'nil de^.de_dir');
|
|
dev_unlock();
|
|
|
|
TAILQ_REMOVE(@de^.de_dir^.de_dlist,de,@de^.de_list);
|
|
de^.de_cdp :=nil;
|
|
de^.de_inode:=0;
|
|
devfs_delete(dm, de, 0);
|
|
dev_lock();
|
|
Dec(cdp^.cdp_inuse);
|
|
dev_unlock();
|
|
Exit(1);
|
|
end;
|
|
{
|
|
* GC any lingering devices
|
|
}
|
|
if ((cdp^.cdp_flags and CDP_ACTIVE)=0) then
|
|
begin
|
|
if (cdp^.cdp_inuse > 0) then
|
|
begin
|
|
cdp:=TAILQ_NEXT(cdp,@cdp^.cdp_list);
|
|
continue;
|
|
end;
|
|
TAILQ_REMOVE(@cdevp_list,cdp,@cdp^.cdp_list);
|
|
dev_unlock();
|
|
dev_rel(@cdp^.cdp_c);
|
|
Exit(1);
|
|
end;
|
|
{
|
|
* Don't create any new dirents if we are unmounting
|
|
}
|
|
if (cleanup<>0) then
|
|
begin
|
|
cdp:=TAILQ_NEXT(cdp,@cdp^.cdp_list);
|
|
continue;
|
|
end;
|
|
Assert((cdp^.cdp_flags and CDP_ACTIVE)<>0,'Bogons, I tell ya!');
|
|
|
|
if (dm^.dm_idx <= cdp^.cdp_maxdirent) and
|
|
(cdp^.cdp_dirents[dm^.dm_idx]<>nil) then
|
|
begin
|
|
de:=cdp^.cdp_dirents[dm^.dm_idx];
|
|
Assert(cdp=de^.de_cdp, 'inconsistent cdp');
|
|
cdp:=TAILQ_NEXT(cdp,@cdp^.cdp_list);
|
|
continue;
|
|
end;
|
|
|
|
Inc(cdp^.cdp_inuse);
|
|
dev_unlock();
|
|
|
|
if (dm^.dm_idx > cdp^.cdp_maxdirent) then
|
|
begin
|
|
devfs_metoo(cdp, dm);
|
|
end;
|
|
|
|
dd:=dm^.dm_rootdir;
|
|
s:=cdp^.cdp_c.si_name;
|
|
repeat
|
|
q:=s;
|
|
while (q^<>'/') and (q^<>#0) do Inc(q);
|
|
if (q^<>'/') then break;
|
|
|
|
de:=devfs_find(dd, s, q - s, 0);
|
|
if (de=nil) then
|
|
begin
|
|
de:=devfs_vmkdir(dm, s, q - s, dd, 0);
|
|
end else
|
|
if (de^.de_dirent^.d_type=DT_LNK) then
|
|
begin
|
|
de:=devfs_find(dd, s, q - s, DT_DIR);
|
|
if (de=nil) then
|
|
begin
|
|
de:=devfs_vmkdir(dm, s, q - s, dd, 0);
|
|
end;
|
|
de^.de_flags:=de^.de_flags or DE_COVERED;
|
|
end;
|
|
s:=q + 1;
|
|
dd:=de;
|
|
Assert((dd^.de_dirent^.d_type=DT_DIR) and ((dd^.de_flags and (DE_DOT or DE_DOTDOT))=0),'invalid directory');
|
|
until false;
|
|
|
|
de_flags:=0;
|
|
de:=devfs_find(dd, s, q - s, DT_LNK);
|
|
if (de<>nil) then
|
|
de_flags:=de_flags or DE_COVERED;
|
|
|
|
de:=devfs_newdirent(s, q - s);
|
|
if ((cdp^.cdp_c.si_flags and SI_ALIAS)<>0) then
|
|
begin
|
|
de^.de_uid :=0;
|
|
de^.de_gid :=0;
|
|
de^.de_mode:=&0755;
|
|
de^.de_dirent^.d_type:=DT_LNK;
|
|
pdev:=cdp^.cdp_c.si_parent;
|
|
j:=strlen(pdev^.si_name) + 1;
|
|
de^.de_symlink:=AllocMem(j);
|
|
Move(pdev^.si_name^,de^.de_symlink^,j);
|
|
end else
|
|
begin
|
|
de^.de_uid :=cdp^.cdp_c.si_uid;
|
|
de^.de_gid :=cdp^.cdp_c.si_gid;
|
|
de^.de_mode:=cdp^.cdp_c.si_mode;
|
|
de^.de_dirent^.d_type:=DT_CHR;
|
|
end;
|
|
de^.de_flags:=de^.de_flags or de_flags;
|
|
de^.de_inode:=cdp^.cdp_inode;
|
|
de^.de_cdp :=cdp;
|
|
|
|
//mac_devfs_create_device(cdp^.cdp_c.si_cred, dm^.dm_mount, @cdp^.cdp_c, de);
|
|
|
|
de^.de_dir:=dd;
|
|
TAILQ_INSERT_TAIL(@dd^.de_dlist,de,@de^.de_list);
|
|
devfs_rules_apply(dm, de);
|
|
dev_lock();
|
|
{ XXX: could check that cdp is still active here }
|
|
Assert(cdp^.cdp_dirents[dm^.dm_idx]=nil);
|
|
cdp^.cdp_dirents[dm^.dm_idx]:=de;
|
|
Assert(de^.de_cdp<>Pointer(ptruint($deadc0de)));
|
|
dev_unlock();
|
|
Exit(1);
|
|
end;
|
|
dev_unlock();
|
|
Exit(0);
|
|
end;
|
|
|
|
{
|
|
* The caller needs to hold the dm for the duration of the call.
|
|
}
|
|
procedure devfs_populate(dm:p_devfs_mount); public;
|
|
var
|
|
gen:DWORD;
|
|
begin
|
|
sx_assert(@dm^.dm_lock);
|
|
gen:=devfs_generation;
|
|
if (dm^.dm_generation=gen) then
|
|
begin
|
|
Exit;
|
|
end;
|
|
while (devfs_populate_loop(dm, 0)<>0) do;
|
|
dm^.dm_generation:=gen;
|
|
end;
|
|
|
|
{
|
|
* The caller needs to hold the dm for the duration of the call.
|
|
}
|
|
procedure devfs_cleanup(dm:p_devfs_mount); public;
|
|
begin
|
|
sx_assert(@dm^.dm_lock);
|
|
while (devfs_populate_loop(dm, 1)<>0) do;
|
|
devfs_purge(dm, dm^.dm_rootdir);
|
|
end;
|
|
|
|
{
|
|
* devfs_create() and devfs_destroy() are called from kern_conf.c and
|
|
* in both cases the devlock() mutex is held, so no further locking
|
|
* is necesary and no sleeping allowed.
|
|
}
|
|
procedure devfs_create(dev:p_cdev); public;
|
|
var
|
|
cdp:p_cdev_priv;
|
|
begin
|
|
mtx_assert(devmtx);
|
|
cdp:=cdev2priv(dev);
|
|
cdp^.cdp_flags:=cdp^.cdp_flags or CDP_ACTIVE;
|
|
cdp^.cdp_inode:=devfs_alloc_cdp_inode;
|
|
dev_refl(dev);
|
|
TAILQ_INSERT_TAIL(@cdevp_list,cdp,@cdp^.cdp_list);
|
|
Inc(devfs_generation);
|
|
end;
|
|
|
|
procedure devfs_destroy(dev:p_cdev); public;
|
|
var
|
|
cdp:p_cdev_priv;
|
|
begin
|
|
mtx_assert(devmtx);
|
|
cdp:=cdev2priv(dev);
|
|
cdp^.cdp_flags:=cdp^.cdp_flags and (not CDP_ACTIVE);
|
|
Inc(devfs_generation);
|
|
end;
|
|
|
|
function devfs_alloc_cdp_inode():ino_t; public;
|
|
begin
|
|
if id_new(@devfs_inos,@devfs_desc,@Result) then
|
|
begin
|
|
id_release(@devfs_desc); //<-id_new
|
|
end else
|
|
begin
|
|
Result:=-1;
|
|
end;
|
|
end;
|
|
|
|
procedure devfs_free_cdp_inode(ino:ino_t); public;
|
|
begin
|
|
if (ino>0) then
|
|
begin
|
|
id_del(@devfs_inos,ino,nil);
|
|
end;
|
|
end;
|
|
|
|
procedure devfs_devs_init();
|
|
begin
|
|
id_table_init(@devfs_inos,DEVFS_ROOTINO + 1);
|
|
end;
|
|
|
|
end.
|
|
|