This commit is contained in:
Pavel 2023-05-01 23:02:16 +03:00
parent ed32b44e8d
commit 5818cc3403
9 changed files with 522 additions and 72 deletions

View File

@ -24,6 +24,7 @@ procedure sx_xlock(p:p_sx);
procedure sx_sunlock(p:p_sx);
procedure sx_xunlock(p:p_sx);
procedure sx_unlock(p:p_sx);
procedure sx_destroy(p:p_sx);
implementation
@ -77,5 +78,10 @@ begin
end;
end;
procedure sx_destroy(p:p_sx);
begin
//
end;
end.

View File

@ -33,16 +33,10 @@ implementation
uses
sysutils;
//#include <sys/cdefs.h>
//#include <sys/param.h>
//#include <sys/ctype.h>
//#include <sys/libkern.h>
{
* 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;

View File

@ -26,8 +26,10 @@ uses
vsys_generic,
vfs_subr,
vfs_lookup,
vfs_init,
devfs,
devfs_devs;
devfs_devs,
devfs_vfsops;
//Daemon for a separate thread
procedure sys_update;
@ -35,6 +37,11 @@ begin
vnlru_proc;
end;
procedure module_init;
begin
vfs_register(@devfs_vfsconf);
end;
//Manual order of lazy initialization
procedure sys_init;
begin
@ -55,6 +62,7 @@ begin
nameiinit;
devfs_mtx_init;
devfs_devs_init;
module_init;
end;
end.

View File

@ -425,6 +425,10 @@
<Filename Value="..\sys_fnmatch.pas"/>
<IsPartOfProject Value="True"/>
</Unit>
<Unit>
<Filename Value="..\vfs\devfs\devfs_vfsops.pas"/>
<IsPartOfProject Value="True"/>
</Unit>
</Units>
</ProjectOptions>
<CompilerOptions>

View File

@ -51,7 +51,8 @@ uses
vnode_if,
sys_sysinit,
sys_fnmatch,
devfs;
devfs,
devfs_vfsops;
var
mtx:umutex;

View File

@ -10,20 +10,6 @@ uses
devfs,
kern_sx;
//#include <sys/param.h>
//#include <sys/systm.h>
//#include <sys/conf.h>
//#include <sys/kernel.h>
//#include <sys/malloc.h>
//#include <sys/priv.h>
//#include <sys/dirent.h>
//#include <sys/ioccom.h>
//#include <sys/lock.h>
//#include <sys/sx.h>
//
//#include <fs/devfs/devfs.h>
//#include <fs/devfs/devfs_int.h>
{
* Kernel version of devfs_rule.
}

View File

@ -0,0 +1,275 @@
unit devfs_vfsops;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
vfs_vnode,
vmount,
devfs,
kern_id;
var
devfs_unr:p_id_desc_table;
function devfs_mount(mp:p_mount):Integer;
function devfs_unmount(mp:p_mount;mntflags:Integer):Integer;
function devfs_root(mp:p_mount;flags:Integer;vpp:pp_vnode):Integer;
function devfs_statfs(mp:p_mount;sbp:p_statfs):Integer;
const
devfs_opts:array[0..3] of PChar=(
'from','export','ruleset',nil
);
_devfs_vfsops:vfsops=(
vfs_mount :@devfs_mount;
vfs_cmount :nil;
vfs_unmount :@devfs_unmount;
vfs_root :@devfs_root;
vfs_quotactl :nil;
vfs_statfs :@devfs_statfs;
vfs_sync :nil;
vfs_vget :nil;
vfs_fhtovp :nil;
vfs_checkexp :nil;
vfs_init :nil;
vfs_uninit :nil;
vfs_extattrctl :nil;
vfs_sysctl :nil;
vfs_susp_clean :nil;
vfs_reclaim_lowervp:nil;
vfs_unlink_lowervp :nil;
);
var
//VFS_SET(devfs_vfsops, devfs, VFCF_SYNTHETIC or VFCF_JAIL);
devfs_vfsconf:vfsconf=(
vfc_version :VFS_VERSION;
vfc_name :'devfs';
vfc_vfsops :@_devfs_vfsops;
vfc_typenum :-1;
vfc_refcount:0;
vfc_flags :VFCF_SYNTHETIC or VFCF_JAIL;
);
implementation
uses
errno,
kern_mtx,
kern_sx,
devfs_devs,
devfs_rule,
vfs_mount,
vfs_subr,
vnode_if;
function new_unrhdr(min,max:Integer):p_id_desc_table;
begin
Result:=AllocMem(SizeOf(t_id_desc_table));
id_table_init(Result,min);
Result^.max_key:=max;
end;
function alloc_unr(p:p_id_desc_table):Integer;
begin
if id_new(p,nil,@Result) then
begin
//
end else
begin
Result:=-1;
end;
end;
procedure free_unr(p:p_id_desc_table;i:Integer);
begin
id_del(p,i,nil);
end;
{
* Mount the filesystem
}
function devfs_mount(mp:p_mount):Integer;
var
error:Integer;
fmp:p_devfs_mount;
rvp:p_vnode;
//struct thread *td:=curthread;
{injail,}rsnum:Integer;
begin
if (devfs_unr=nil) then
devfs_unr:=new_unrhdr(0, High(Integer));
error:=0;
if ((mp^.mnt_flag and MNT_ROOTFS)<>0) then
Exit(EOPNOTSUPP);
//if (!prison_allow(td^.td_ucred, PR_ALLOW_MOUNT_DEVFS))
// Exit(EPERM);
rsnum:=0;
//injail:=jailed(td^.td_ucred);
if (mp^.mnt_optnew<>nil) then
begin
if (vfs_filteropt(mp^.mnt_optnew, devfs_opts)<>0) then
Exit(EINVAL);
if (vfs_flagopt(mp^.mnt_optnew, 'export', nil, 0)<>0) then
Exit(EOPNOTSUPP);
if (vfs_getopt(mp^.mnt_optnew, 'ruleset', nil, nil)=0) and
((vfs_scanopt(mp^.mnt_optnew, 'ruleset', '%d',[rsnum])<>1) or
(rsnum < 0) or
(rsnum > 65535)) then
begin
vfs_mount_error(mp, '%s', ['invalid ruleset specification']);
Exit(EINVAL);
end;
//if (injail) and
// (rsnum<>0) and
// (rsnum<>td^.td_ucred^.cr_prison^.pr_devfs_rsnum) then
// Exit(EPERM);
end;
{ jails enforce their ruleset }
//if (injail<>0) then
// rsnum:=td^.td_ucred^.cr_prison^.pr_devfs_rsnum;
if ((mp^.mnt_flag and MNT_UPDATE)<>0) then
begin
if (rsnum<>0) then
begin
fmp:=mp^.mnt_data;
if (fmp<>nil) then
begin
sx_xlock(@fmp^.dm_lock);
devfs_ruleset_set(devfs_rsnum(rsnum), fmp);
devfs_ruleset_apply(fmp);
sx_xunlock(@fmp^.dm_lock);
end;
end;
Exit(0);
end;
fmp:=AllocMem(sizeof(t_devfs_mount));
fmp^.dm_idx:=alloc_unr(devfs_unr);
sx_init(@fmp^.dm_lock, 'devfsmount');
fmp^.dm_holdcnt:=1;
MNT_ILOCK(mp);
mp^.mnt_flag:=mp^.mnt_flag or MNT_LOCAL;
mp^.mnt_kern_flag:=mp^.mnt_kern_flag or MNTK_MPSAFE or MNTK_LOOKUP_SHARED or MNTK_EXTENDED_SHARED;
//MAC
//mp^.mnt_flag:=mp^.mnt_flag or MNT_MULTILABEL;
MNT_IUNLOCK(mp);
fmp^.dm_mount:=mp;
mp^.mnt_data:=fmp;
vfs_getnewfsid(mp);
fmp^.dm_rootdir:=devfs_vmkdir(fmp, nil, 0, nil, DEVFS_ROOTINO);
error:=devfs_root(mp, LK_EXCLUSIVE, @rvp);
if (error<>0) then
begin
sx_destroy(@fmp^.dm_lock);
free_unr(devfs_unr, fmp^.dm_idx);
FreeMem(fmp);
Exit(error);
end;
if (rsnum<>0) then
begin
sx_xlock(@fmp^.dm_lock);
devfs_ruleset_set(devfs_rsnum(rsnum), fmp);
sx_xunlock(@fmp^.dm_lock);
end;
VOP_UNLOCK(rvp, 0);
vfs_mountedfrom(mp, 'devfs');
Exit(0);
end;
procedure devfs_unmount_final(fmp:p_devfs_mount);
begin
sx_destroy(@fmp^.dm_lock);
FreeMem(fmp);
end;
function devfs_unmount(mp:p_mount;mntflags:Integer):Integer;
var
error:Integer;
flags:Integer;
fmp:p_devfs_mount;
hold:Integer;
idx:DWORD;
begin
flags:=0;
fmp:=VFSTODEVFS(mp);
Assert(fmp^.dm_mount<>nil,
('devfs_unmount unmounted devfs_mount'));
{ There is 1 extra root vnode reference from devfs_mount(). }
error:=vflush(mp, 1, flags);
if (error<>0) then
Exit(error);
sx_xlock(@fmp^.dm_lock);
devfs_cleanup(fmp);
devfs_rules_cleanup(fmp);
fmp^.dm_mount:=nil;
Inc(fmp^.dm_holdcnt);
hold:=fmp^.dm_holdcnt;
mp^.mnt_data:=nil;
idx:=fmp^.dm_idx;
sx_xunlock(@fmp^.dm_lock);
free_unr(devfs_unr, idx);
if (hold=0) then
devfs_unmount_final(fmp);
Exit(0);
end;
{ Exitlocked reference to root. }
function devfs_root(mp:p_mount;flags:Integer;vpp:pp_vnode):Integer;
var
error:Integer;
vp:p_vnode;
dmp:p_devfs_mount;
begin
dmp:=VFSTODEVFS(mp);
sx_xlock(@dmp^.dm_lock);
//error:=devfs_allocv(dmp^.dm_rootdir, mp, LK_EXCLUSIVE, @vp);
if (error<>0) then
Exit(error);
vp^.v_vflag:=vp^.v_vflag or VV_ROOT;
vpp^:=vp;
Exit(0);
end;
function devfs_statfs(mp:p_mount;sbp:p_statfs):Integer;
begin
sbp^.f_flags :=0;
sbp^.f_bsize :=DEV_BSIZE;
sbp^.f_iosize:=DEV_BSIZE;
sbp^.f_blocks:=2; { 1K to keep df happy }
sbp^.f_bfree :=0;
sbp^.f_bavail:=0;
sbp^.f_files :=0;
sbp^.f_ffree :=0;
Exit(0);
end;
end.

View File

@ -22,6 +22,17 @@ uses
const
VFS_MOUNTARG_SIZE_MAX=(1024 * 64);
global_opts:array[0..7] of PChar=(
'errmsg',
'fstype',
'fspath',
'ro',
'rw',
'nosuid',
'noexec',
nil
);
procedure vfs_freeopt(opts:p_vfsoptlist;opt:p_vfsopt);
procedure vfs_freeopts(opts:p_vfsoptlist);
procedure vfs_deleteopt(opts:p_vfsoptlist;name:PChar);
@ -32,13 +43,18 @@ procedure vfs_sanitizeopts(opts:p_vfsoptlist);
function vfs_buildopts(auio:p_uio;options:pp_vfsoptlist):Integer;
procedure vfs_mergeopts(toopts,oldopts:p_vfsoptlist);
procedure vfs_mount_error(mp:p_mount;fmt:PChar;const Args:Array of const); register;
procedure vfs_opterror(opts:p_vfsoptlist;fmt:PChar;const Args:Array of const); register;
function vfs_filteropt(opts:p_vfsoptlist;legal:ppchar):Integer;
function vfs_getopt(opts:p_vfsoptlist;name:PChar;buf:PPointer;len:PInteger):Integer;
function vfs_getopt_pos(opts:p_vfsoptlist;name:PChar):Integer;
function vfs_getopts(opts:p_vfsoptlist;name:PChar;error:PInteger):PChar;
function vfs_flagopt(opts:p_vfsoptlist;name:PChar;w:PQWORD;val:QWORD):Integer;
function vfs_scanopt(opts:p_vfsoptlist;name,fmt:PChar;const Args:Array of const):Integer; register;
function vfs_setopt(opts:p_vfsoptlist;name,value:PChar;len:Integer):Integer;
function vfs_setopt_part(opts:p_vfsoptlist;name,value:PChar;len:Integer):Integer;
function vfs_setopts(opts:p_vfsoptlist;name,value:PChar):Integer;
procedure vfs_mountedfrom(mp:p_mount;from:PChar);
procedure vfs_ref(mp:p_mount); inline;
procedure vfs_rel(mp:p_mount); inline;
@ -337,11 +353,116 @@ begin
vfs_sanitizeopts(toopts);
end;
{
* Report errors during filesystem mounting.
}
procedure vfs_mount_error(mp:p_mount;fmt:PChar;const Args:Array of const); register;
var
moptlist:p_vfsoptlist;
error,len:Integer;
errmsg:PChar;
S:RawByteString;
begin
moptlist:=mp^.mnt_optnew;
error:=vfs_getopt(moptlist, 'errmsg', @errmsg, @len);
if (error<>0) or
(errmsg=nil) or
(len<=0) then
Exit;
S:=Format(fmt,Args);
if (len>(Length(S)+1)) then len:=Length(S)+1;
Move(PChar(S)^,errmsg^,len);
end;
procedure vfs_opterror(opts:p_vfsoptlist;fmt:PChar;const Args:Array of const); register;
var
error,len:Integer;
errmsg:PChar;
S:RawByteString;
begin
error:=vfs_getopt(opts, 'errmsg', @errmsg, @len);
if (error<>0) or
(errmsg=nil) or
(len<=0) then
Exit;
S:=Format(fmt,Args);
if (len>(Length(S)+1)) then len:=Length(S)+1;
Move(PChar(S)^,errmsg^,len);
end;
{
* Check that no unknown options are given
}
function vfs_filteropt(opts:p_vfsoptlist;legal:ppchar):Integer;
var
opt:p_vfsopt;
errmsg:array[0..254] of Char;
t:ppchar;
p,q:pchar;
begin
opt:=TAILQ_FIRST(opts);
while (opt<>nil) do
begin
p:=opt^.name;
q:=nil;
if (p[0]='n') and (p[1]='o') then
q:=p + 2;
t:=@global_opts;
while (t^<>nil) do
begin
if (strcomp(t^, p)=0) then
break;
if (q<>nil) then
begin
if (strcomp(t^, q)=0) then
break;
end;
Inc(t);
end;
if (t^<>nil) then
continue;
t:=legal;
while (t^<>nil) do
begin
if (strcomp(t^, p)=0) then
break;
if (q<>nil) then
begin
if (strcomp(t^, q)=0) then
break;
end;
Inc(t);
end;
if (t^<>nil) then
continue;
errmsg:='mount option is unknown';
Result:=EINVAL;
opt:=TAILQ_NEXT(opt,@opt^.link);
end;
if (Result<>0) then
begin
opt:=TAILQ_FIRST(opts);
while (opt<>nil) do
begin
if (strcomp(opt^.name, 'errmsg')=0) then
begin
strlcopy(opt^.value, errmsg, opt^.len);
break;
end;
opt:=TAILQ_NEXT(opt,@opt^.link);
end;
if (opt=nil) then
Writeln(errmsg);
end;
end;
{
* Get a mount option by its name.
*
* Exit0 if the option was found, ENOENT otherwise.
* return 0 if the option was found, ENOENT otherwise.
* If len is non-nil it will be filled with the length
* of the option. If buf is non-nil, it will be filled
* with the address of the option.
@ -439,6 +560,34 @@ begin
Exit(0);
end;
function vfs_scanopt(opts:p_vfsoptlist;name,fmt:PChar;const Args:Array of const):Integer; register;
var
opt:p_vfsopt;
S:RawByteString;
begin
Assert(opts<>nil, 'vfs_getopt: caller passed opts as nil');
opt:=TAILQ_FIRST(opts);
while (opt<>nil) do
begin
if (strcomp(name, opt^.name)<>0) then
begin
opt:=TAILQ_NEXT(opt,@opt^.link);
continue;
end;
opt^.seen:=1;
if (opt^.len=0) or (opt^.value=nil) then
Exit(0);
if (PChar(opt^.value)[opt^.len - 1]<>#0) then
Exit(0);
S:=Format(fmt,Args);
Move(PChar(S)^,opt^.value^,Length(S)+1);
Exit(0);
end;
Exit(0);
end;
function vfs_setopt(opts:p_vfsoptlist;name,value:PChar;len:Integer):Integer;
var
opt:p_vfsopt;
@ -520,6 +669,12 @@ begin
Exit(ENOENT);
end;
procedure vfs_mountedfrom(mp:p_mount;from:PChar);
begin
FillChar(mp^.mnt_stat.f_mntfromname,sizeof(mp^.mnt_stat.f_mntfromname),0);
strlcopy(@mp^.mnt_stat.f_mntfromname, from, sizeof(mp^.mnt_stat.f_mntfromname));
end;
///
procedure vfs_ref(mp:p_mount); inline;

View File

@ -42,6 +42,7 @@ procedure vrele(vp:p_vnode);
procedure vput(vp:p_vnode);
procedure vinactive(vp:p_vnode);
function vflush(mp:p_mount;rootrefs,flags:Integer):Integer;
procedure vfs_notify_upper(vp:p_vnode;event:Integer);
@ -2248,44 +2249,55 @@ end;
* If the SKIPSYSTEM or WRITECLOSE flags are specified, rootrefs must
* be zero.
}
{
int
vflush(mp:p_mount, int rootrefs, int flags, struct thread *td)
function vflush(mp:p_mount;rootrefs,flags:Integer):Integer;
label
loop;
var
vp,mvp,rootvp:p_vnode;
vattr:t_vattr;
busy,error:Integer;
begin
vp:p_vnode, *mvp, *rootvp:=nil;
struct vattr vattr;
int busy:=0, error;
rootvp:=nil;
busy:=0;
if (rootrefs > 0) begin
Assert((flags and (SKIPSYSTEM or WRITECLOSE))=0,
'vflush: bad args');
if (rootrefs > 0) then
begin
Assert((flags and (SKIPSYSTEM or WRITECLOSE))=0,'vflush: bad args');
{
* Get the filesystem root vnode. We can vput() it
* immediately, since with rootrefs > 0, it won't go away.
}
if ((error:=VFS_ROOT(mp, LK_EXCLUSIVE, &rootvp))<>0) then
error:=VFS_ROOT(mp, LK_EXCLUSIVE, @rootvp);
if (error<>0) then
begin
Exit(error);
end;
vput(rootvp);
end;
loop:
MNT_VNODE_FOREACH_ALL(vp, mp, mvp) begin
vp:=__mnt_vnode_first_all(@mvp,mp);
while (vp<>nil) do
begin
vholdl(vp);
error:=vn_lock(vp, LK_INTERLOCK or LK_EXCLUSIVE);
if (error) begin
if (error<>0) then
begin
vdrop(vp);
MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
//MNT_VNODE_FOREACH_ALL_ABORT
MNT_ILOCK(mp);
__mnt_vnode_markerfree_all(@mvp,mp);
//MNT_VNODE_FOREACH_ALL_ABORT
goto loop;
end;
{
* Skip over a vnodes marked VV_SYSTEM.
}
if ((flags and SKIPSYSTEM) and (vp^.v_vflag and VV_SYSTEM)) begin
if (((flags and SKIPSYSTEM)<>0) and ((vp^.v_vflag and VV_SYSTEM)<>0)) then
begin
VOP_UNLOCK(vp, 0);
vdrop(vp);
//
vp:=__mnt_vnode_next_all(@mvp,mp);
continue;
end;
{
@ -2293,30 +2305,39 @@ loop:
* files (even if open only for reading) and regular file
* vnodes open for writing.
}
if (flags and WRITECLOSE) begin
if (vp^.v_object<>nil) begin
VM_OBJECT_LOCK(vp^.v_object);
vm_object_page_clean(vp^.v_object, 0, 0, 0);
VM_OBJECT_UNLOCK(vp^.v_object);
end;
error:=VOP_FSYNC(vp, MNT_WAIT, td);
if (error<>0) begin
if ((flags and WRITECLOSE)<>0) then
begin
//if (vp^.v_object<>nil) then
//begin
// VM_OBJECT_LOCK(vp^.v_object);
// vm_object_page_clean(vp^.v_object, 0, 0, 0);
// VM_OBJECT_UNLOCK(vp^.v_object);
//end;
error:=VOP_FSYNC(vp, MNT_WAIT);
if (error<>0) then
begin
VOP_UNLOCK(vp, 0);
vdrop(vp);
MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
//MNT_VNODE_FOREACH_ALL_ABORT
MNT_ILOCK(mp);
__mnt_vnode_markerfree_all(@mvp,mp);
//MNT_VNODE_FOREACH_ALL_ABORT
Exit(error);
end;
error:=VOP_GETATTR(vp, &vattr, td^.td_ucred);
error:=VOP_GETATTR(vp, @vattr);
VI_LOCK(vp);
if ((vp^.v_type=VNON or
(error=0 and vattr.va_nlink > 0)) and
(vp^.v_writecount=0 or vp^.v_type<>VREG)) begin
if ((vp^.v_type=VNON) or
((error=0) and (vattr.va_nlink > 0))) and
((vp^.v_writecount=0) or (vp^.v_type<>VREG)) then
begin
VOP_UNLOCK(vp, 0);
vdropl(vp);
//
vp:=__mnt_vnode_next_all(@mvp,mp);
continue;
end;
end; else
end else
VI_LOCK(vp);
{
* With v_usecount=0, all we need to do is clear out the
@ -2324,46 +2345,46 @@ loop:
*
* If FORCECLOSE is set, forcibly close the vnode.
}
if (vp^.v_usecount=0 or (flags and FORCECLOSE)) begin
Assert(vp^.v_usecount=0 or
(vp^.v_type<>VCHR and vp^.v_type<>VBLK), vp,
'device VNODE %p is FORCECLOSED", vp));
if (vp^.v_usecount=0 or (flags and FORCECLOSE)) then
begin
Assert((vp^.v_usecount=0) or
((vp^.v_type<>VCHR) and (vp^.v_type<>VBLK)),'device VNODE %p is FORCECLOSED');
vgonel(vp);
end; else begin
busy++;
end else
begin
Inc(busy);
end;
VOP_UNLOCK(vp, 0);
vdropl(vp);
//
vp:=__mnt_vnode_next_all(@mvp,mp);
end;
if (rootrefs > 0 and (flags and FORCECLOSE)=0) begin
if (rootrefs > 0) and ((flags and FORCECLOSE)=0) then
begin
{
* If just the root vnode is busy, and if its refcount
* is equal to `rootrefs', then go ahead and kill it.
}
VI_LOCK(rootvp);
Assert(busy > 0, 'vflush: not busy');
Assert(rootvp^.v_usecount >= rootrefs, rootvp,
'vflush: usecount %d < rootrefs %d",
rootvp^.v_usecount, rootrefs));
if (busy=1 and rootvp^.v_usecount=rootrefs) begin
VOP_LOCK(rootvp, LK_EXCLUSIVE|LK_INTERLOCK);
Assert(rootvp^.v_usecount >= rootrefs,'vflush: usecount %d < rootrefs %d');
if (busy=1) and (rootvp^.v_usecount=rootrefs) then
begin
VOP_LOCK(rootvp, LK_EXCLUSIVE or LK_INTERLOCK,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
vgone(rootvp);
VOP_UNLOCK(rootvp, 0);
busy:=0;
end; else
end else
VI_UNLOCK(rootvp);
end;
if (busy) begin
CTR2(KTR_VFS, "%s: failing as %d vnodes are busy", {$I %LINE%},
busy);
if (busy<>0) then
begin
Exit(EBUSY);
end;
for (; rootrefs > 0; rootrefs--)
For rootrefs:=rootrefs downto 0 do
vrele(rootvp);
Exit(0);
end;
}
{
* Recycle an unused vnode to the front of the free list.