diff --git a/sys/kern/kern_sx.pas b/sys/kern/kern_sx.pas new file mode 100644 index 00000000..a9f6a49e --- /dev/null +++ b/sys/kern/kern_sx.pas @@ -0,0 +1,81 @@ +unit kern_sx; + +{$mode ObjFPC}{$H+} +{$CALLING SysV_ABI_CDecl} + +interface + +uses + kern_rwlock; + +type + p_sx=^t_sx; + t_sx=packed record + n:PChar; + c:Pointer; + m:QWORD; + end; + +procedure sx_init(p:p_sx;name:PChar); +function sx_xlocked(p:p_sx):Boolean; +procedure sx_assert(p:p_sx); +procedure sx_slock(p:p_sx); +procedure sx_xlock(p:p_sx); +procedure sx_sunlock(p:p_sx); +procedure sx_xunlock(p:p_sx); +procedure sx_unlock(p:p_sx); + +implementation + +procedure sx_init(p:p_sx;name:PChar); +begin + p^.n:=name; + p^.c:=nil; + p^.m:=0; +end; + +function sx_xlocked(p:p_sx):Boolean; +begin + Result:=(PDWORD(@p^.m)[0]=System.GetCurrentThreadId) and + (PDWORD(@p^.m)[1]=2); +end; + +procedure sx_assert(p:p_sx); +begin + Assert(sx_xlocked(p),'sx_assert'); +end; + +procedure sx_slock(p:p_sx); +begin + rw_rlock(p^.c); + PDWORD(@p^.m)[0]:=0; + PDWORD(@p^.m)[1]:=1; +end; + +procedure sx_xlock(p:p_sx); +begin + rw_wlock(p^.c); + PDWORD(@p^.m)[0]:=System.GetCurrentThreadId; + PDWORD(@p^.m)[1]:=2; +end; + +procedure sx_sunlock(p:p_sx); +begin + rw_runlock(p^.c); +end; + +procedure sx_xunlock(p:p_sx); +begin + rw_wunlock(p^.c); +end; + +procedure sx_unlock(p:p_sx); +begin + case PDWORD(@p^.m)[1] of + 1:rw_runlock(p^.c); + 2:rw_wunlock(p^.c); + end; +end; + +end. + diff --git a/sys/sys_fnmatch.pas b/sys/sys_fnmatch.pas new file mode 100644 index 00000000..e1ab0fdd --- /dev/null +++ b/sys/sys_fnmatch.pas @@ -0,0 +1,273 @@ +unit sys_fnmatch; + +{$mode ObjFPC}{$H+} +{$CALLING SysV_ABI_CDecl} + +interface + +const + FNM_NOMATCH =1; { Match failed. } + + FNM_NOESCAPE=$01; { Disable backslash escaping. } + FNM_PATHNAME=$02; { Slash must be matched by slash. } + FNM_PERIOD =$04; { Period must be matched by period. } + + FNM_NOSYS =(-1); { Reserved. } + + FNM_LEADING_DIR=$08; { Ignore / after Imatch. } + FNM_CASEFOLD =$10; { Case insensitive search. } + FNM_IGNORECASE =FNM_CASEFOLD; + FNM_FILE_NAME =FNM_PATHNAME; + + EOS=#0; + + RANGE_MATCH =1; + RANGE_NOMATCH=0; + RANGE_ERROR =(-1); + +function rangematch(pattern:pchar;test:char;flags:Integer;newp:ppchar):Integer; +function fnmatch(pattern,str:pchar;flags:Integer):Integer; + +implementation + +uses + sysutils; + +//#include +//#include +//#include +//#include + +{ + * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. + * Compares a filename or pathname to a pattern. + } + +function fnmatch(pattern,str:pchar;flags:Integer):Integer; +label + norm; +var + stringstart:pchar; + newp:pchar; + c,test:char; +begin + stringstart:=str; + while true do + begin + c:=pattern^; + Inc(pattern); + case (c) of + EOS: + begin + if ((flags and FNM_LEADING_DIR)<>0) and (str^='/') then + Exit(0); + if (str^=EOS) then + Exit(0) + else + Exit(FNM_NOMATCH); + end; + '?': + begin + if (str^=EOS) then + Exit(FNM_NOMATCH); + if (str^='/') and ((flags and FNM_PATHNAME)<>0) then + Exit(FNM_NOMATCH); + if (str^='.') and + ((flags and FNM_PERIOD)<>0) and + ((str=stringstart) or + (((flags and FNM_PATHNAME)<>0) and + ((str - 1)^='/'))) then + Exit(FNM_NOMATCH); + Inc(str); + end; + '*': + begin + c:=pattern^; + { Collapse multiple stars. } + while (c='*') do + begin + Inc(pattern); + c:=pattern^; + end; + + if (str^='.') and + ((flags and FNM_PERIOD)<>0) and + ((str=stringstart) or + (((flags and FNM_PATHNAME)<>0) and + ((str - 1)^='/'))) then + Exit(FNM_NOMATCH); + + { Optimize for pattern with * at end or before /. } + if (c=EOS) then + begin + if ((flags and FNM_PATHNAME)<>0) then + begin + if ((flags and FNM_LEADING_DIR)<>0) or (strscan(str, '/')=nil) then + Exit(0) + else + Exit(FNM_NOMATCH); + end else + Exit(0); + end else + if (c='/') and ((flags and FNM_PATHNAME)<>0) then + begin + str:=strscan(str, '/'); + if (str=nil) then + Exit(FNM_NOMATCH); + continue; + end; + + { General case, use recursion. } + test:=str^; + while (test<>EOS) do + begin + if (fnmatch(pattern, str, flags and (not FNM_PERIOD))=0) then + Exit(0); + if (test='/') and ((flags and FNM_PATHNAME)<>0) then + break; + Inc(str); + test:=str^; + end; + Exit(FNM_NOMATCH); + end; + '[': + begin + if (str^=EOS) then + Exit(FNM_NOMATCH); + if (str^='/') and ((flags and FNM_PATHNAME)<>0) then + Exit(FNM_NOMATCH); + if (str^='.') and + ((flags and FNM_PERIOD)<>0) and + ((str=stringstart) or + (((flags and FNM_PATHNAME)<>0) and + ((str - 1)^='/'))) then + Exit(FNM_NOMATCH); + + case (rangematch(pattern, str^, flags, @newp)) of + RANGE_ERROR: + goto norm; + RANGE_MATCH: + pattern:=newp; + RANGE_NOMATCH: + Exit(FNM_NOMATCH); + end; + Inc(str); + end; + '\': + begin + if ((flags and FNM_NOESCAPE)=0) then + begin + c:=pattern^; + Inc(pattern); + if (c=EOS) then + begin + c:='\'; + Dec(pattern); + end; + end; + goto norm; + end; + { FALLTHROUGH } + else + begin + norm: + if (c=str^) then + begin + // + end else + if ((flags and FNM_CASEFOLD)<>0) and + (lowercase(c)=lowercase(str^)) then + begin + // + end else + Exit(FNM_NOMATCH); + Inc(str); + end; + end; + end; + { NOTREACHED } +end; + +function rangematch(pattern:pchar;test:char;flags:Integer;newp:ppchar):Integer; +var + negate,ok:Integer; + c,c2:char; + + function _cond:Boolean; inline; + begin + c2:=(pattern+1)^; + Result:=(c2<>EOS) and (c2<>']'); + end; + +begin + { + * A bracket expression starting with an unquoted circumflex + * character produces unspecified results (IEEE 1003.2-1992, + * 3.13.2). This implementation treats it like '!', for + * consistency with the regular expression syntax. + * J.T. Conklin (conklin@ngai.kaleida.com) + } + negate:=ord((pattern^='!') or (pattern^='^')); + if (negate<>0) then + Inc(pattern); + + if ((flags and FNM_CASEFOLD)<>0) then + test:=lowercase(test); + + { + * A right bracket shall lose its special meaning and represent + * itself in a bracket expression if it occurs first in the list. + * -- POSIX.2 2.8.3.2 + } + ok:=0; + c:=pattern^; + Inc(pattern); + repeat + if (c='\') and ((flags and FNM_NOESCAPE)=0) then + begin + c:=pattern^; + Inc(pattern); + end; + if (c=EOS) then + Exit(RANGE_ERROR); + + if (c='/') and ((flags and FNM_PATHNAME)<>0) then + Exit(RANGE_NOMATCH); + + if ((flags and FNM_CASEFOLD)<>0) then + c:=lowercase(c); + + if (pattern^='-') and _cond then + begin + Inc(pattern,2); + if (c2='\') and ((flags and FNM_NOESCAPE)=0) then + begin + c2:=pattern^; + Inc(pattern); + end; + if (c2=EOS) then + Exit(RANGE_ERROR); + + if ((flags and FNM_CASEFOLD)<>0) then + c2:=lowercase(c2); + + if (c <= test) and (test <= c2) then + ok:=1; + end else + if (c=test) then + ok:=1; + c:=pattern^; + Inc(pattern); + until (c=']'); + + newp^:=pattern; + + if (ok=negate) then + Exit(RANGE_NOMATCH) + else + Exit(RANGE_MATCH); +end; + + +end. + diff --git a/sys/sys_sysinit.pas b/sys/sys_sysinit.pas index e1a4ed0f..88d1278f 100644 --- a/sys/sys_sysinit.pas +++ b/sys/sys_sysinit.pas @@ -26,7 +26,8 @@ uses vsys_generic, vfs_subr, vfs_lookup, - devfs; + devfs, + devfs_devs; //Daemon for a separate thread procedure sys_update; @@ -52,7 +53,7 @@ begin selectinit; vntblinit; nameiinit; - dirlist_mtx_init; + devfs_mtx_init; devfs_devs_init; end; diff --git a/sys/test/project1.lpi b/sys/test/project1.lpi index e4992848..b631379e 100644 --- a/sys/test/project1.lpi +++ b/sys/test/project1.lpi @@ -409,6 +409,22 @@ + + + + + + + + + + + + + + + + diff --git a/sys/test/project1.lpr b/sys/test/project1.lpr index 43e0ff2b..c0371268 100644 --- a/sys/test/project1.lpr +++ b/sys/test/project1.lpr @@ -50,6 +50,7 @@ uses vsocketvar, vnode_if, sys_sysinit, + sys_fnmatch, devfs; var diff --git a/sys/vfs/devfs/devfs.pas b/sys/vfs/devfs/devfs.pas index 98da83a1..a052c369 100644 --- a/sys/vfs/devfs/devfs.pas +++ b/sys/vfs/devfs/devfs.pas @@ -14,7 +14,7 @@ uses vmount, time, kern_mtx, - kern_id; + kern_sx; const DEVFS_MAGIC=$db0a087a; @@ -28,9 +28,14 @@ const * aforementioned ruleset. } type - devfs_rnum =WORD; + p_devfs_rnum=^devfs_rnum; + devfs_rnum=WORD; + + p_devfs_rsnum=^devfs_rsnum; devfs_rsnum=WORD; - devfs_rid =DWORD; + + p_devfs_rid=^devfs_rid; + devfs_rid=DWORD; ino_t=Integer; @@ -54,8 +59,8 @@ const * userland-specific values. } type - p_devfs_rule=^devfs_rule; - devfs_rule=packed record + p_devfs_rule=^t_devfs_rule; + t_devfs_rule=packed record dr_magic:DWORD; { Magic number. } dr_id :devfs_rid; { Identifier. } @@ -218,14 +223,14 @@ type de_symlink:PChar; end; - p_devfs_mount=^devfs_mount; - devfs_mount=packed record + 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 :Pointer ; + dm_holdcnt :Integer ; + dm_lock :t_sx ; dm_ruleset :devfs_rsnum ; end; @@ -235,11 +240,12 @@ const DEVFS_DEL_VNLOCKED =$01; DEVFS_DEL_NORECURSE=$02; -//extern unsigned devfs_rule_depth; +var + devfs_rule_depth:DWORD=0; -function rid2rsn(rid:devfs_rid):devfs_rsnum; inline; -function rid2rn (rid:devfs_rid):devfs_rnum; inline; -function mkrid (rsn:devfs_rsnum;rn:devfs_rnum):devfs_rid; inline; +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; @@ -252,8 +258,6 @@ function DEVFS_DMP_DROP(dmp:p_devfs_mount):Boolean; inline; function cdev2priv(c:Pointer):p_cdev_priv; inline; var - cdevp_list:TAILQ_HEAD=(tqh_first:nil;tqh_last:@cdevp_list.tqh_first); - devfs_inos:t_id_desc_table; devmtx:mtx; devfs_de_interlock:mtx; @@ -274,9 +278,10 @@ var devfs_generation:DWORD=0; -procedure dev_lock(); inline; -procedure dev_unlock(); inline; +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; @@ -286,52 +291,28 @@ 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 dirlist_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); - -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); +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 - kern_rwlock, - vfs_vnops, - vfs_subr, - vnode_if; + devfs_devs; { * Identifier manipulators. } -function rid2rsn(rid:devfs_rid):devfs_rsnum; inline; +function rid2rsn(rid:devfs_rid):devfs_rsnum; begin Result:=rid shr 16; end; -function rid2rn(rid:devfs_rid):devfs_rnum; inline; +function rid2rn(rid:devfs_rid):devfs_rnum; begin Result:=rid and $ffff; end; -function mkrid(rsn:devfs_rsnum;rn:devfs_rnum):devfs_rid; inline; +function mkrid(rsn:devfs_rsnum;rn:devfs_rnum):devfs_rid; begin Result:=rn or (rsn shl 16); end; @@ -370,29 +351,12 @@ end; // -procedure sx_assert(p:PPointer); inline; -begin - // -end; - -procedure sx_xlock(p:PPointer); inline; -begin - rw_wlock(p^); -end; - -procedure sx_unlock(p:PPointer); inline; -begin - rw_wunlock(p^); -end; - -// - -procedure dev_lock(); inline; +procedure dev_lock(); begin mtx_lock(devmtx); end; -procedure dev_unlock(); inline; +procedure dev_unlock(); begin mtx_unlock(devmtx); end; @@ -404,7 +368,7 @@ begin mtx_unlock(devmtx); end; -procedure dev_refl(dev:p_cdev); inline; +procedure dev_refl(dev:p_cdev); begin mtx_assert(devmtx); Inc(dev^.si_refcount); @@ -572,623 +536,13 @@ begin { NOTREACHED } end; -procedure dirlist_mtx_init; +procedure devfs_mtx_init; begin + mtx_init(devmtx,'cdev'); mtx_init(dirlist_mtx,'devfs dirlist lock'); mtx_init(devfs_de_interlock,'devfs interlock'); end; -// - -function devfs_alloc(flags:Integer):p_cdev; -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; -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); -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; -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; - Assert((de=nil) or ((de^.de_flags and DE_DOOMED)=0),'devfs_find: Exiting a doomed entry'); - Exit(de); -end; - -function devfs_newdirent(name:PChar;namelen:Integer):p_devfs_dirent; -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; -begin - - if (de^.de_dirent^.d_type<>DT_DIR) then - Exit(de^.de_dir); - - if ((de^.de_flags and (DE_DOT or DE_DOTDOT))<>0) then - Exit(nil); - - 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; -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); -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); -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')); - { Exitif the directory is not empty. } - if (TAILQ_NEXT(de_dotdot,@de_dotdot^.de_list)<>nil) then - Exit; - - 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); -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 - DEVFS_DE_HOLD(dd); - 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 - dd:=nil; - - 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) - else - VI_UNLOCK(vp); - vgone(vp); - if ((flags and DEVFS_DEL_VNLOCKED)=0) then - VOP_UNLOCK(vp, 0); - vdrop(vp); - sx_xlock(@dm^.dm_lock); - end else - mtx_unlock(devfs_de_interlock); - 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 - devfs_dirent_free(de); - - 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); -var - de:p_devfs_dirent; -begin - 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); -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 - FreeMem(cdp^.cdp_dirents); - 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; -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 - devfs_metoo(cdp, dm); - - 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 - de:=devfs_vmkdir(dm, s, q - s, dd, 0) - else - if (de^.de_dirent^.d_type=DT_LNK) then - begin - de:=devfs_find(dd, s, q - s, DT_DIR); - if (de=nil) then - de:=devfs_vmkdir(dm, s, q - s, dd, 0); - 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); -var - gen:DWORD; -begin - sx_assert(@dm^.dm_lock); - gen:=devfs_generation; - if (dm^.dm_generation=gen) then - Exit; - 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); -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); -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); -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; -begin - if id_new(@devfs_inos,nil,@Result) then - begin - // - end else - begin - Result:=-1; - end; -end; - -procedure devfs_free_cdp_inode(ino:ino_t); -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. diff --git a/sys/vfs/devfs/devfs_devs.pas b/sys/vfs/devfs/devfs_devs.pas new file mode 100644 index 00000000..8b00e388 --- /dev/null +++ b/sys/vfs/devfs/devfs_devs.pas @@ -0,0 +1,663 @@ +unit devfs_devs; + +{$mode ObjFPC}{$H+} +{$CALLING SysV_ABI_CDecl} + +interface + +uses + mqueue, + kern_id, + devfs, + devfs_rule; + +var + cdevp_list:TAILQ_HEAD=(tqh_first:nil;tqh_last:@cdevp_list.tqh_first); + devfs_inos:t_id_desc_table; + +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, + vfs_vnode, + kern_mtx, + kern_sx, + vfs_vnops, + vfs_subr, + vnode_if; + +// + +function devfs_alloc(flags:Integer):p_cdev; +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; +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); +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; +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; + Assert((de=nil) or ((de^.de_flags and DE_DOOMED)=0),'devfs_find: Exiting a doomed entry'); + Exit(de); +end; + +function devfs_newdirent(name:PChar;namelen:Integer):p_devfs_dirent; +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; +begin + + if (de^.de_dirent^.d_type<>DT_DIR) then + Exit(de^.de_dir); + + if ((de^.de_flags and (DE_DOT or DE_DOTDOT))<>0) then + Exit(nil); + + 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; +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); +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); +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')); + { Exitif the directory is not empty. } + if (TAILQ_NEXT(de_dotdot,@de_dotdot^.de_list)<>nil) then + Exit; + + 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); +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 + DEVFS_DE_HOLD(dd); + 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 + dd:=nil; + + 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) + else + VI_UNLOCK(vp); + vgone(vp); + if ((flags and DEVFS_DEL_VNLOCKED)=0) then + VOP_UNLOCK(vp, 0); + vdrop(vp); + sx_xlock(@dm^.dm_lock); + end else + mtx_unlock(devfs_de_interlock); + 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 + devfs_dirent_free(de); + + 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); +var + de:p_devfs_dirent; +begin + 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); +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 + FreeMem(cdp^.cdp_dirents); + 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; +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 + devfs_metoo(cdp, dm); + + 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 + de:=devfs_vmkdir(dm, s, q - s, dd, 0) + else + if (de^.de_dirent^.d_type=DT_LNK) then + begin + de:=devfs_find(dd, s, q - s, DT_DIR); + if (de=nil) then + de:=devfs_vmkdir(dm, s, q - s, dd, 0); + 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); +var + gen:DWORD; +begin + sx_assert(@dm^.dm_lock); + gen:=devfs_generation; + if (dm^.dm_generation=gen) then + Exit; + 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); +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); +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); +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; +begin + if id_new(@devfs_inos,nil,@Result) then + begin + // + end else + begin + Result:=-1; + end; +end; + +procedure devfs_free_cdp_inode(ino:ino_t); +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. + diff --git a/sys/vfs/devfs/devfs_rule.pas b/sys/vfs/devfs/devfs_rule.pas new file mode 100644 index 00000000..76d0364f --- /dev/null +++ b/sys/vfs/devfs/devfs_rule.pas @@ -0,0 +1,824 @@ +unit devfs_rule; + +{$mode ObjFPC}{$H+} +{$CALLING SysV_ABI_CDecl} + +interface + +uses + mqueue, + devfs, + kern_sx; + +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +// +//#include +//#include + +{ + * Kernel version of devfs_rule. + } +type + p_devfs_ruleset=^t_devfs_ruleset; + + p_devfs_krule=^t_devfs_krule; + t_devfs_krule=packed record + dk_list :TAILQ_ENTRY; //devfs_krule + dk_ruleset:p_devfs_ruleset; + dk_rule :t_devfs_rule; + end; + + rulehead=TAILQ_HEAD; //devfs_krule + +{ + * Structure to describe a ruleset. + } + t_devfs_ruleset=packed record + ds_list :TAILQ_ENTRY; //devfs_ruleset + ds_rules :rulehead; + ds_number :devfs_rsnum; + ds_refcount:Integer; + end; + +function devfs_rid_input(rid:devfs_rid;dm:p_devfs_mount):devfs_rid; +// +procedure devfs_rule_applyde_recursive(dk:p_devfs_krule;dm:p_devfs_mount;de:p_devfs_dirent); +procedure devfs_rule_applydm(dk:p_devfs_krule;dm:p_devfs_mount); +function devfs_rule_autonumber(ds:p_devfs_ruleset;rnump:p_devfs_rnum):Integer; +function devfs_rule_byid(rid:devfs_rid):p_devfs_krule; +function devfs_rule_delete(dk:p_devfs_krule):Integer; +function devfs_rule_getdev(de:p_devfs_dirent):p_cdev; +function devfs_rule_input(dr:p_devfs_rule;dm:p_devfs_mount):Integer; +function devfs_rule_insert(dr:p_devfs_rule):Integer; +function devfs_rule_match(dk:p_devfs_krule;dm:p_devfs_mount;de:p_devfs_dirent):Integer; +function devfs_rule_matchpath(dk:p_devfs_krule;dm:p_devfs_mount;de:p_devfs_dirent):Integer; +procedure devfs_rule_run(dk:p_devfs_krule;dm:p_devfs_mount;de:p_devfs_dirent;depth:DWORD); +// +procedure devfs_ruleset_applyde(ds:p_devfs_ruleset;dm:p_devfs_mount;de:p_devfs_dirent;depth:DWORD); +procedure devfs_ruleset_applydm(ds:p_devfs_ruleset;dm:p_devfs_mount); +function devfs_ruleset_bynum(rsnum:devfs_rsnum):p_devfs_ruleset; +function devfs_ruleset_create(rsnum:devfs_rsnum):p_devfs_ruleset; +procedure devfs_ruleset_reap(ds:p_devfs_ruleset); +function devfs_ruleset_use(rsnum:devfs_rsnum;dm:p_devfs_mount):Integer; + +procedure devfs_rules_apply(dm:p_devfs_mount;de:p_devfs_dirent); +function devfs_rules_ioctl(dm:p_devfs_mount;cmd:QWORD;data:Pointer):Integer; +procedure devfs_rules_cleanup(dm:p_devfs_mount); +procedure devfs_ruleset_set(rsnum:devfs_rsnum;dm:p_devfs_mount); +procedure devfs_ruleset_apply(dm:p_devfs_mount); + +var + //SX_SYSINIT(sx_rules, @sx_rules, 'DEVFS ruleset lock'); + sx_rules:t_sx=(n:'DEVFS ruleset lock';c:nil;m:0); + + devfs_rulesets:TAILQ_HEAD=(tqh_first:nil;tqh_last:@devfs_rulesets.tqh_first); + +implementation + +uses + errno, + vdirent, + sys_fnmatch; + +// + +{ + * Called to apply the proper rules for 'de' before it can be + * exposed to the userland. This should be called with an exclusive + * lock on dm in case we need to run anything. + } +procedure devfs_rules_apply(dm:p_devfs_mount;de:p_devfs_dirent); +var + ds:p_devfs_ruleset; +begin + sx_assert(@dm^.dm_lock); + + if (dm^.dm_ruleset=0) then + Exit; + sx_slock(@sx_rules); + ds:=devfs_ruleset_bynum(dm^.dm_ruleset); + Assert(ds<>nil, ('mount-point has nil ruleset')); + devfs_ruleset_applyde(ds, dm, de, devfs_rule_depth); + sx_sunlock(@sx_rules); +end; + +{ + * Rule subsystem ioctl hook. + } +function devfs_rules_ioctl(dm:p_devfs_mount;cmd:QWORD;data:Pointer):Integer; +label + _break; +var + ds:p_devfs_ruleset; + dk:p_devfs_krule; + dr:p_devfs_rule; + rsnum:devfs_rsnum; + rnum:devfs_rnum; + rid:devfs_rid; + error:Integer; +begin + sx_assert(@dm^.dm_lock); + + { + * XXX: This Exits an error regardless of whether we actually + * support the cmd or not. + * + * We could make this privileges finer grained if desired. + } + error:=EPERM; + //error:=priv_check(td, PRIV_DEVFS_RULE); + if (error<>0) then + Exit(error); + + sx_xlock(@sx_rules); + + case cmd of + DEVFSIO_RADD: + begin + dr:=p_devfs_rule(data); + error:=devfs_rule_input(dr, dm); + if (error<>0) then + goto _break; + dk:=devfs_rule_byid(dr^.dr_id); + if (dk<>nil) then + begin + error:=EEXIST; + goto _break; + end; + if (rid2rsn(dr^.dr_id)=0) then + begin + error:=EIO; + goto _break; + end; + error:=devfs_rule_insert(dr); + end; + DEVFSIO_RAPPLY: + begin + dr:=p_devfs_rule(data); + error:=devfs_rule_input(dr, dm); + if (error<>0) then + goto _break; + + { + * This is one of many possible hackish + * implementations. The primary contender is an + * implementation where the rule we read in is + * temporarily inserted into some ruleset, perhaps + * with a hypothetical DRO_NOAUTO flag so that it + * doesn't get used where it isn't intended, and + * applied in the normal way. This can be done in the + * userland (DEVFSIO_ADD, DEVFSIO_APPLYID, + * DEVFSIO_DEL) or in the kernel; either way it breaks + * some corner case assumptions in other parts of the + * code (not that this implementation doesn't do + * that). + } + if ((dr^.dr_iacts and DRA_INCSET)<>0) and + (devfs_ruleset_bynum(dr^.dr_incset)=nil) then + begin + error:=ESRCH; + goto _break; + end; + dk:=AllocMem(sizeof(t_devfs_krule)); + Move(dr^,dk^.dk_rule,sizeof(t_devfs_rule)); + devfs_rule_applydm(dk, dm); + FreeMem(dk); + end; + DEVFSIO_RAPPLYID: + begin + rid:=p_devfs_rid(data)^; + rid:=devfs_rid_input(rid, dm); + dk:=devfs_rule_byid(rid); + if (dk=nil) then + begin + error:=ENOENT; + goto _break; + end; + devfs_rule_applydm(dk, dm); + end; + DEVFSIO_RDEL: + begin + rid:=p_devfs_rid(data)^; + rid:=devfs_rid_input(rid, dm); + dk:=devfs_rule_byid(rid); + if (dk=nil) then + begin + error:=ENOENT; + goto _break + end; + ds:=dk^.dk_ruleset; + error:=devfs_rule_delete(dk); + end; + DEVFSIO_RGETNEXT: + begin + dr:=p_devfs_rule(data); + error:=devfs_rule_input(dr, dm); + if (error<>0) then + goto _break; + { + * We can't use devfs_rule_byid() here since that + * requires the rule specified to exist, but we want + * getnext(N) to work whether there is a rule N or not + * (specifically, getnext(0) must work, but we should + * never have a rule 0 since the add command + * interprets 0 to mean 'auto-number'). + } + ds:=devfs_ruleset_bynum(rid2rsn(dr^.dr_id)); + if (ds=nil) then + begin + error:=ENOENT; + goto _break; + end; + rnum:=rid2rn(dr^.dr_id); + dk:=TAILQ_FIRST(@ds^.ds_rules); + while (dk<>nil) do + begin + if (rid2rn(dk^.dk_rule.dr_id) > rnum) then + break; + dk:=TAILQ_NEXT(dk,@dk^.dk_list); + end; + if (dk=nil) then + begin + error:=ENOENT; + goto _break; + end; + Move(dk^.dk_rule,dr^,sizeof(t_devfs_rule)); + end; + DEVFSIO_SUSE: + begin + rsnum:=p_devfs_rsnum(data)^; + error:=devfs_ruleset_use(rsnum, dm); + end; + DEVFSIO_SAPPLY: + begin + rsnum:=p_devfs_rsnum(data)^; + rsnum:=rid2rsn(devfs_rid_input(mkrid(rsnum, 0), dm)); + ds:=devfs_ruleset_bynum(rsnum); + if (ds=nil) then + begin + error:=ESRCH; + goto _break; + end; + devfs_ruleset_applydm(ds, dm); + end; + DEVFSIO_SGETNEXT: + begin + rsnum:=p_devfs_rsnum(data)^; + ds:=TAILQ_FIRST(@devfs_rulesets); + while (ds<>nil) do + begin + if (ds^.ds_number > rsnum) then + break; + ds:=TAILQ_NEXT(ds,@ds^.ds_list); + end; + if (ds=nil) then + begin + error:=ENOENT; + goto _break; + end; + p_devfs_rsnum(data)^:=ds^.ds_number; + end; + else + error:=ENOIOCTL; + end; +_break: + + sx_xunlock(@sx_rules); + Exit(error); +end; + +{ + * Adjust the rule identifier to use the ruleset of dm if one isn't + * explicitly specified. + * + * Note that after this operation, rid2rsn(rid) might still be 0, and + * that's okay; ruleset 0 is a valid ruleset, but when it's read in + * from the userland, it means 'current ruleset for this mount-point'. + } +function devfs_rid_input(rid:devfs_rid;dm:p_devfs_mount):devfs_rid; +begin + if (rid2rsn(rid)=0) then + Exit(mkrid(dm^.dm_ruleset, rid2rn(rid))) + else + Exit(rid); +end; + +{ + * Apply dk to de and everything under de. + * + * XXX: This method needs a function call for every nested + * subdirectory in a devfs mount. If we plan to have many of these, + * we might eventually run out of kernel stack space. + * XXX: a linear search could be done through the cdev list instead. + } +procedure devfs_rule_applyde_recursive(dk:p_devfs_krule;dm:p_devfs_mount;de:p_devfs_dirent); +var + de2:p_devfs_dirent; +begin + de2:=TAILQ_FIRST(@de^.de_dlist); + while (de2<>nil) do + begin + devfs_rule_applyde_recursive(dk, dm, de2); + de2:=TAILQ_NEXT(de2,@de2^.de_list); + end; + devfs_rule_run(dk, dm, de, devfs_rule_depth); +end; + +{ + * Apply dk to all entires in dm. + } +procedure devfs_rule_applydm(dk:p_devfs_krule;dm:p_devfs_mount); +begin + devfs_rule_applyde_recursive(dk, dm, dm^.dm_rootdir); +end; + +{ + * Automatically select a number for a new rule in ds, and write the + * result into rnump. + } +function devfs_rule_autonumber(ds:p_devfs_ruleset;rnump:p_devfs_rnum):Integer; +var + dk:p_devfs_krule; +begin + { Find the last rule. } + dk:=TAILQ_LAST(@ds^.ds_rules); + if (dk=nil) then + rnump^:=100 + else + begin + rnump^:=rid2rn(dk^.dk_rule.dr_id) + 100; + { Detect overflow. } + if (rnump^ < rid2rn(dk^.dk_rule.dr_id)) then + Exit(ERANGE); + end; + Assert(devfs_rule_byid(mkrid(ds^.ds_number, rnump^))=nil,'autonumbering resulted in an already existing rule'); + Exit(0); +end; + +{ + * Find a krule by id. + } +function devfs_rule_byid(rid:devfs_rid):p_devfs_krule; +var + ds:p_devfs_ruleset; + dk:p_devfs_krule; + rn:devfs_rnum; +begin + rn:=rid2rn(rid); + ds:=devfs_ruleset_bynum(rid2rsn(rid)); + if (ds=nil) then + Exit(nil); + dk:=TAILQ_FIRST(@ds^.ds_rules); + while (dk<>nil) do + begin + if (rid2rn(dk^.dk_rule.dr_id)=rn) then + Exit(dk) + else + if (rid2rn(dk^.dk_rule.dr_id) > rn) then + break; + dk:=TAILQ_NEXT(dk,@dk^.dk_list); + end; + Exit(nil); +end; + +{ + * Remove dkp from any lists it may be on and remove memory associated + * with it. + } +function devfs_rule_delete(dk:p_devfs_krule):Integer; +var + ds:p_devfs_ruleset; +begin + if ((dk^.dk_rule.dr_iacts and DRA_INCSET)<>0) then + begin + ds:=devfs_ruleset_bynum(dk^.dk_rule.dr_incset); + Assert(ds<>nil,'DRA_INCSET but bad dr_incset'); + Dec(ds^.ds_refcount); + devfs_ruleset_reap(ds); + end; + ds:=dk^.dk_ruleset; + TAILQ_REMOVE(@ds^.ds_rules,dk,@dk^.dk_list); + devfs_ruleset_reap(ds); + FreeMem(dk); + Exit(0); +end; + +{ + * Get a struct cdev *corresponding to de so we can try to match rules based + * on it. If this routine Exits nil, there is no struct cdev *associated + * with the dirent (symlinks and directories don't have dev_ts), and + * the caller should assume that any critera dependent on a dev_t + * don't match. + } +function devfs_rule_getdev(de:p_devfs_dirent):p_cdev; +begin + if (de^.de_cdp=nil) then + Exit(nil); + if ((de^.de_cdp^.cdp_flags and CDP_ACTIVE)<>0) then + Exit(@de^.de_cdp^.cdp_c) + else + Exit(nil); +end; + +{ + * Do what we need to do to a rule that we just loaded from the + * userland. In particular, we need to check the magic, and adjust + * the ruleset appropriate if desired. + } +function devfs_rule_input(dr:p_devfs_rule;dm:p_devfs_mount):Integer; +begin + if (dr^.dr_magic<>DEVFS_MAGIC) then + Exit(ERPCMISMATCH); + dr^.dr_id:=devfs_rid_input(dr^.dr_id, dm); + Exit(0); +end; + +{ + * Import dr into the appropriate place in the kernel (i.e., make a + * krule). The value of dr is copied, so the pointer may be destroyed + * after this call completes. + } +function devfs_rule_insert(dr:p_devfs_rule):Integer; +var + ds,dsi:p_devfs_ruleset; + k1:p_devfs_krule; + dk:p_devfs_krule; + rsnum:devfs_rsnum; + dkrn:devfs_rnum; + error:Integer; +begin + { + * This stuff seems out of place here, but we want to do it as + * soon as possible so that if it fails, we don't have to roll + * back any changes we already made (e.g., ruleset creation). + } + if ((dr^.dr_iacts and DRA_INCSET)<>0) then + begin + dsi:=devfs_ruleset_bynum(dr^.dr_incset); + if (dsi=nil) then + Exit(ESRCH); + end else + dsi:=nil; + + rsnum:=rid2rsn(dr^.dr_id); + Assert(rsnum<>0, ('Inserting into ruleset zero')); + + ds:=devfs_ruleset_bynum(rsnum); + if (ds=nil) then + ds:=devfs_ruleset_create(rsnum); + dkrn:=rid2rn(dr^.dr_id); + if (dkrn=0) then + begin + error:=devfs_rule_autonumber(ds, @dkrn); + if (error<>0) then + begin + devfs_ruleset_reap(ds); + Exit(error); + end; + end; + + dk:=AllocMem(sizeof(t_devfs_krule)); + dk^.dk_ruleset:=ds; + if (dsi<>nil) then + Inc(dsi^.ds_refcount); + { XXX: Inspect dr? } + Move(dr^,dk^.dk_rule,sizeof(t_devfs_rule)); + dk^.dk_rule.dr_id:=mkrid(rid2rsn(dk^.dk_rule.dr_id), dkrn); + k1:=TAILQ_FIRST(@ds^.ds_rules); + while (k1<>nil) do + begin + if (rid2rn(k1^.dk_rule.dr_id) > dkrn) then + begin + TAILQ_INSERT_BEFORE(k1,dk,@dk^.dk_list); + break; + end; + k1:=TAILQ_NEXT(k1,@k1^.dk_list); + end; + if (k1=nil) then + TAILQ_INSERT_TAIL(@ds^.ds_rules,dk,@dk^.dk_list); + Exit(0); +end; + +{ + * Determine whether dk matches de. Exits 1 if dk should be run on + * de; 0, otherwise. + } +function devfs_rule_match(dk:p_devfs_krule;dm:p_devfs_mount;de:p_devfs_dirent):Integer; +var + dr:p_devfs_rule; + dev:p_cdev; + //dsw:p_cdevsw; + dsw:Pointer; + ref:Integer; +begin + dr:=@dk^.dk_rule; + + dev:=devfs_rule_getdev(de); + { + * At this point, if dev is nil, we should assume that any + * criteria that depend on it don't match. We should *not* + * just ignore them (i.e., act like they weren't specified), + * since that makes a rule that only has criteria dependent on + * the struct cdev *match all symlinks and directories. + * + * Note also that the following tests are somewhat reversed: + * They're actually testing to see whether the condition does + * *not* match, since the default is to assume the rule should + * be run (such as if there are no conditions). + } + if ((dr^.dr_icond and DRC_DSWFLAGS)<>0) then + begin + if (dev=nil) then + Exit(0); + dsw:=nil; + //dsw:=dev_refthread(dev, @ref); + if (dsw=nil) then + Exit(0); + //if ((dsw^.d_flags and dr^.dr_dswflags)=0) then + //begin + // dev_relthread(dev, ref); + // Exit(0); + //end; + //dev_relthread(dev, ref); + end; + if ((dr^.dr_icond and DRC_PATHPTRN)<>0) then + if (devfs_rule_matchpath(dk, dm, de)=0) then + Exit(0); + + Exit(1); +end; + +{ + * Determine whether dk matches de on account of dr_pathptrn. + } +function devfs_rule_matchpath(dk:p_devfs_krule;dm:p_devfs_mount;de:p_devfs_dirent):Integer; +var + dr:p_devfs_rule; + dev:p_cdev; + pname,specname:PChar; +begin + dr:=@dk^.dk_rule; + + specname:=nil; + dev:=devfs_rule_getdev(de); + if (dev<>nil) then + pname:=dev^.si_name + else + if (de^.de_dirent^.d_type=DT_LNK) or + ((de^.de_dirent^.d_type=DT_DIR) and + (de<>dm^.dm_rootdir) and + ((de^.de_flags and (DE_DOT or DE_DOTDOT))=0)) then + begin + specname:=AllocMem(SPECNAMELEN + 1); + //pname:=devfs_fqpn(specname, dm, de, nil); + end else + Exit(0); + + Assert(pname<>nil, ('devfs_rule_matchpath: nil pname')); + Result:=ord(fnmatch(dr^.dr_pathptrn, pname, FNM_PATHNAME)=0); + FreeMem(specname); + Exit; +end; + +{ + * Run dk on de. + } +procedure devfs_rule_run(dk:p_devfs_krule;dm:p_devfs_mount;de:p_devfs_dirent;depth:DWORD); +var + dr:p_devfs_rule; + ds:p_devfs_ruleset; +begin + dr:=@dk^.dk_rule; + + if (devfs_rule_match(dk, dm, de)=0) then + Exit; + if ((dr^.dr_iacts and DRA_BACTS)<>0) then + begin + if ((dr^.dr_bacts and DRB_HIDE)<>0) then + de^.de_flags:=de^.de_flags or DE_WHITEOUT; + if ((dr^.dr_bacts and DRB_UNHIDE)<>0) then + de^.de_flags:=de^.de_flags and (not DE_WHITEOUT); + end; + if ((dr^.dr_iacts and DRA_UID)<>0) then + de^.de_uid:=dr^.dr_uid; + if ((dr^.dr_iacts and DRA_GID)<>0) then + de^.de_gid:=dr^.dr_gid; + if ((dr^.dr_iacts and DRA_MODE)<>0) then + de^.de_mode:=dr^.dr_mode; + if ((dr^.dr_iacts and DRA_INCSET)<>0) then + begin + { + * XXX: we should tell the user if the depth is exceeded here + * XXX: but it is not obvious how to. A Exitvalue will + * XXX: not work as this is called when devices are created + * XXX: long time after the rules were instantiated. + * XXX: a printf() would probably give too much noise, or + * XXX: DoS the machine. I guess a rate-limited message + * XXX: might work. + } + if (depth > 0) then + begin + ds:=devfs_ruleset_bynum(dk^.dk_rule.dr_incset); + Assert(ds<>nil,'DRA_INCSET but bad dr_incset'); + devfs_ruleset_applyde(ds, dm, de, depth - 1); + end; + end; +end; + +{ + * Apply all the rules in ds to de. + } +procedure devfs_ruleset_applyde(ds:p_devfs_ruleset;dm:p_devfs_mount;de:p_devfs_dirent;depth:DWORD); +var + dk:p_devfs_krule; +begin + dk:=TAILQ_FIRST(@ds^.ds_rules); + while (dk<>nil) do + begin + devfs_rule_run(dk, dm, de, depth); + dk:=TAILQ_NEXT(dk,@dk^.dk_list); + end; +end; + +{ + * Apply all the rules in ds to all the entires in dm. + } +procedure devfs_ruleset_applydm(ds:p_devfs_ruleset;dm:p_devfs_mount); +var + dk:p_devfs_krule; +begin + { + * XXX: Does it matter whether we do + * + * foreach(dk in ds) + * foreach(de in dm) + * apply(dk to de) + * + * as opposed to + * + * foreach(de in dm) + * foreach(dk in ds) + * apply(dk to de) + * + * The end result is obviously the same, but does the order + * matter? + } + dk:=TAILQ_FIRST(@ds^.ds_rules); + while (dk<>nil) do + begin + devfs_rule_applydm(dk, dm); + dk:=TAILQ_NEXT(dk,@dk^.dk_list); + end; +end; + +{ + * Find a ruleset by number. + } +function devfs_ruleset_bynum(rsnum:devfs_rsnum):p_devfs_ruleset; +var + ds:p_devfs_ruleset; +begin + ds:=TAILQ_FIRST(@devfs_rulesets); + while (ds<>nil) do + begin + if (ds^.ds_number=rsnum) then + Exit(ds); + ds:=TAILQ_NEXT(ds,@ds^.ds_list); + end; + Exit(nil); +end; + +{ + * Create a new ruleset. + } +function devfs_ruleset_create(rsnum:devfs_rsnum):p_devfs_ruleset; +var + s1:p_devfs_ruleset; + ds:p_devfs_ruleset; +begin + Assert(rsnum<>0,'creating ruleset zero'); + + Assert(devfs_ruleset_bynum(rsnum)=nil,'creating already existent ruleset'); + + ds:=AllocMem(sizeof(t_devfs_ruleset)); + ds^.ds_number:=rsnum; + TAILQ_INIT(@ds^.ds_rules); + + s1:=TAILQ_FIRST(@devfs_rulesets); + while (s1<>nil) do + begin + if (s1^.ds_number > rsnum) then + begin + TAILQ_INSERT_BEFORE(s1,ds,@ds^.ds_list); + break; + end; + s1:=TAILQ_NEXT(s1,@s1^.ds_list); + end; + if (s1=nil) then + TAILQ_INSERT_TAIL(@devfs_rulesets,ds,@ds^.ds_list); + Exit(ds); +end; + +{ + * Remove a ruleset from the system if it's empty and not used + * anywhere. This should be called after every time a rule is deleted + * from this ruleset or the reference count is decremented. + } +procedure devfs_ruleset_reap(ds:p_devfs_ruleset); +begin + Assert(ds^.ds_number<>0,'reaping ruleset zero'); + + if (not TAILQ_EMPTY(@ds^.ds_rules)) or (ds^.ds_refcount<>0) then + Exit; + + TAILQ_REMOVE(@devfs_rulesets,ds,@ds^.ds_list); + FreeMem(ds); +end; + +{ + * Make rsnum the active ruleset for dm. + } +function devfs_ruleset_use(rsnum:devfs_rsnum;dm:p_devfs_mount):Integer; +var + cds,ds:p_devfs_ruleset; +begin + if (dm^.dm_ruleset<>0) then + begin + cds:=devfs_ruleset_bynum(dm^.dm_ruleset); + Dec(cds^.ds_refcount); + devfs_ruleset_reap(cds); + end; + + if (rsnum=0) then + begin + dm^.dm_ruleset:=0; + Exit(0); + end; + + ds:=devfs_ruleset_bynum(rsnum); + if (ds=nil) then + ds:=devfs_ruleset_create(rsnum); + { These should probably be made atomic somehow. } + Inc(ds^.ds_refcount); + dm^.dm_ruleset:=rsnum; + + Exit(0); +end; + +procedure devfs_rules_cleanup(dm:p_devfs_mount); +var + ds:p_devfs_ruleset; +begin + sx_assert(@dm^.dm_lock); + if (dm^.dm_ruleset<>0) then + begin + ds:=devfs_ruleset_bynum(dm^.dm_ruleset); + Dec(ds^.ds_refcount); + devfs_ruleset_reap(ds); + end; +end; + +{ + * Make rsnum the active ruleset for dm (locked) + } +procedure devfs_ruleset_set(rsnum:devfs_rsnum;dm:p_devfs_mount); +begin + sx_assert(@dm^.dm_lock); + sx_xlock(@sx_rules); + devfs_ruleset_use(rsnum, dm); + sx_xunlock(@sx_rules); +end; + +{ + * Apply the current active ruleset on a mount + } +procedure devfs_ruleset_apply(dm:p_devfs_mount); +var + ds:p_devfs_ruleset; +begin + sx_assert(@dm^.dm_lock); + + sx_xlock(@sx_rules); + if (dm^.dm_ruleset=0) then + begin + sx_xunlock(@sx_rules); + Exit; + end; + ds:=devfs_ruleset_bynum(dm^.dm_ruleset); + if (ds<>nil) then + devfs_ruleset_applydm(ds, dm); + sx_xunlock(@sx_rules); +end; + + +end. + diff --git a/sys/vfs/vfs_subr.pas b/sys/vfs/vfs_subr.pas index 947d3041..e176118e 100644 --- a/sys/vfs/vfs_subr.pas +++ b/sys/vfs/vfs_subr.pas @@ -110,7 +110,8 @@ uses vm_object, vsys_generic, dead_vnops, - rtprio; + rtprio, + devfs; { * List of vnodes that are ready for recycling. @@ -1798,11 +1799,11 @@ end; procedure v_incr_usecount(vp:p_vnode); begin Inc(vp^.v_usecount); - if (vp^.v_type=VCHR) {and (vp^.v_rdev<>nil)} then + if (vp^.v_type=VCHR) and (vp^.v_rdev<>nil) then begin - //dev_lock(); - //vp^.v_rdev^.si_usecount++; - //dev_unlock(); + dev_lock(); + Inc(p_cdev(vp^.v_rdev)^.si_usecount); + dev_unlock(); end; vholdl(vp); end; @@ -1814,11 +1815,11 @@ end; procedure v_upgrade_usecount(vp:p_vnode); begin Inc(vp^.v_usecount); - if (vp^.v_type=VCHR) {and (vp^.v_rdev<>nil)} then + if (vp^.v_type=VCHR) and (vp^.v_rdev<>nil) then begin - //dev_lock(); - //vp^.v_rdev^.si_usecount++; - //dev_unlock(); + dev_lock(); + Inc(p_cdev(vp^.v_rdev)^.si_usecount); + dev_unlock(); end; end; @@ -1832,11 +1833,11 @@ begin ASSERT_VI_LOCKED(vp,{$I %LINE%}); Assert(vp^.v_usecount>0,'v_decr_usecount: negative usecount'); Dec(vp^.v_usecount); - if (vp^.v_type=VCHR) {and (vp^.v_rdev<>nil)} then + if (vp^.v_type=VCHR) and (vp^.v_rdev<>nil) then begin - //dev_lock(); - //vp^.v_rdev^.si_usecount--; - //dev_unlock(); + dev_lock(); + Inc(p_cdev(vp^.v_rdev)^.si_usecount); + dev_unlock(); end; vdropl(vp); end; @@ -1852,11 +1853,11 @@ begin ASSERT_VI_LOCKED(vp,{$I %LINE%}); Assert(vp^.v_usecount>0,'v_decr_useonly: negative usecount'); Dec(vp^.v_usecount); - if (vp^.v_type=VCHR) {and (vp^.v_rdev<>nil)} then + if (vp^.v_type=VCHR) and (vp^.v_rdev<>nil) then begin - //dev_lock(); - //vp^.v_rdev^.si_usecount--; - //dev_unlock(); + dev_lock(); + Dec(p_cdev(vp^.v_rdev)^.si_usecount); + dev_unlock(); end; end; @@ -2562,11 +2563,9 @@ end; } function vcount(vp:p_vnode):Integer; begin - Result:=0; - //dev_lock(); - //count:=vp^.v_rdev^.si_usecount; - //dev_unlock(); - //Exit(count); + dev_lock(); + Result:=p_cdev(vp^.v_rdev)^.si_usecount; + dev_unlock(); end; { @@ -2574,11 +2573,9 @@ end; } function count_dev(dev:Pointer):Integer; //cdev begin - Result:=0; - //dev_lock(); - //count:=dev^.si_usecount; - //dev_unlock(); - //Exit(count); + dev_lock(); + Result:=p_cdev(dev)^.si_usecount; + dev_unlock(); end; { @@ -2881,19 +2878,19 @@ var error:Integer; begin error:=0; - //dev_lock(); + dev_lock(); if (vp^.v_type<>VCHR) then error:=ENOTBLK - ;//else - //if (vp^.v_rdev=nil) then - // error:=ENXIO - //else - //if (vp^.v_rdev^.si_devsw=nil) then - // error:=ENXIO - //else - //if ((vp^.v_rdev^.si_devsw^.d_flags and D_DISK)=0) then - // error:=ENOTBLK; - //dev_unlock(); + else + if (vp^.v_rdev=nil) then + error:=ENXIO + else + if (p_cdev(vp^.v_rdev)^.si_devsw=nil) then + error:=ENXIO + else{ + if ((p_cdev(vp^.v_rdev)^.si_devsw^.d_flags and D_DISK)=0) then + error:=ENOTBLK}; + dev_unlock(); error:=ENOTBLK; if (errp<>nil) then errp^:=error; diff --git a/sys/vfs/vfs_vnode.pas b/sys/vfs/vfs_vnode.pas index 4a518ceb..9d33db93 100644 --- a/sys/vfs/vfs_vnode.pas +++ b/sys/vfs/vfs_vnode.pas @@ -232,10 +232,10 @@ type v_pollinfo:p_vpollinfo; // i Poll events, p for *v_pi - property v_mountedhere:Pointer read v_un{.vu_mount } write v_un; - property v_socket :Pointer read v_un{.vu_socket } write v_un; - property v_rdev :Pointer read v_un{.vu_cdev } write v_un; - property v_fifoinfo :Pointer read v_un{.vu_fifoinfo} write v_un; + property v_mountedhere:Pointer read v_un{.vu_mount } write v_un; //mount + property v_socket :Pointer read v_un{.vu_socket } write v_un; //socket + property v_rdev :Pointer read v_un{.vu_cdev } write v_un; //cdev + property v_fifoinfo :Pointer read v_un{.vu_fifoinfo} write v_un; //fifoinfo end; p_vattr=^t_vattr;