mirror of https://github.com/red-prig/fpPS4.git
445 lines
9.2 KiB
Plaintext
445 lines
9.2 KiB
Plaintext
unit null_vfsops;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
mqueue,
|
|
kern_param,
|
|
vnode,
|
|
vmount,
|
|
nullfs,
|
|
null_subr;
|
|
|
|
function nullfs_mount(mp:p_mount):Integer;
|
|
function nullfs_unmount(mp:p_mount;mntflags:Integer):Integer;
|
|
function nullfs_root(mp:p_mount;flags:Integer;vpp:pp_vnode):Integer;
|
|
function nullfs_quotactl(mp:p_mount;cmd,uid:Integer;arg:Pointer):Integer;
|
|
function nullfs_statfs(mp:p_mount;sbp:p_statfs):Integer;
|
|
function nullfs_sync(mp:p_mount;waitfor:Integer):Integer;
|
|
function nullfs_vget(mp:p_mount;ino:DWORD;flags:Integer;vpp:pp_vnode):Integer;
|
|
function nullfs_fhtovp(mp:p_mount;fidp:p_fid;flags:Integer;vpp:pp_vnode):Integer;
|
|
function nullfs_extattrctl(mp:p_mount;cmd:Integer;filename_vp:p_vnode;namespace:Integer;attrname:PChar):Integer;
|
|
|
|
const
|
|
_null_vfsops:vfsops=(
|
|
vfs_mount :@nullfs_mount;
|
|
vfs_cmount :nil;
|
|
vfs_unmount :@nullfs_unmount;
|
|
vfs_root :@nullfs_root;
|
|
vfs_quotactl :@nullfs_quotactl;
|
|
vfs_statfs :@nullfs_statfs;
|
|
vfs_sync :@nullfs_sync;
|
|
vfs_vget :@nullfs_vget;
|
|
vfs_fhtovp :@nullfs_fhtovp;
|
|
vfs_checkexp :nil;
|
|
vfs_init :@nullfs_init;
|
|
vfs_uninit :@nullfs_uninit;
|
|
vfs_extattrctl :@nullfs_extattrctl;
|
|
vfs_sysctl :nil;
|
|
vfs_susp_clean :nil;
|
|
);
|
|
|
|
//VFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK or VFCF_JAIL);
|
|
nullfs_vfsconf:vfsconf=(
|
|
vfc_version :VFS_VERSION;
|
|
vfc_name :'nullfs';
|
|
vfc_vfsops :@_null_vfsops;
|
|
vfc_typenum :-1;
|
|
vfc_refcount:0;
|
|
vfc_flags :VFCF_LOOPBACK or VFCF_JAIL;
|
|
vfc_opts :nil;
|
|
vfc_list :(tqe_next:nil;tqe_prev:nil)
|
|
);
|
|
|
|
implementation
|
|
|
|
uses
|
|
errno,
|
|
vuio,
|
|
vnamei,
|
|
vfs_mount,
|
|
vfs_vnops,
|
|
vfs_subr,
|
|
vnode_if,
|
|
null_vnops,
|
|
kern_thr,
|
|
kern_mtx,
|
|
systm;
|
|
|
|
function MOUNTTONULLMOUNT(mp:p_mount):p_null_mount; inline;
|
|
begin
|
|
Result:=mp^.mnt_data;
|
|
end;
|
|
|
|
function VTONULL(vp:p_vnode):p_null_node; inline;
|
|
begin
|
|
Result:=vp^.v_data;
|
|
end;
|
|
|
|
function NULLTOV(xp:p_null_node):p_vnode; inline;
|
|
begin
|
|
Result:=xp^.null_vnode;
|
|
end;
|
|
|
|
{
|
|
* Mount null layer
|
|
}
|
|
function nullfs_mount(mp:p_mount):Integer;
|
|
var
|
|
error:Integer;
|
|
lowerrootvp,vp,nullm_rootvp:p_vnode;
|
|
xmp:p_null_mount;
|
|
target:PChar;
|
|
isvnunlocked,len:Integer;
|
|
nd:t_nameidata;
|
|
ndp:p_nameidata;
|
|
lvp:p_vnode;
|
|
lmp:p_mount;
|
|
begin
|
|
error:=0;
|
|
isvnunlocked:=0;
|
|
ndp:=@nd;
|
|
|
|
//if (prison_allow(td^.td_ucred, PR_ALLOW_MOUNT_NULLFS)=0) then
|
|
// Exit(EPERM);
|
|
|
|
//if ((mp^.mnt_flag and MNT_ROOTFS)<>0) then
|
|
// Exit(EOPNOTSUPP);
|
|
|
|
{
|
|
* Update is a no-op
|
|
}
|
|
if ((mp^.mnt_flag and MNT_UPDATE)<>0) then
|
|
begin
|
|
{
|
|
* Only support update mounts for NFS export.
|
|
}
|
|
if (vfs_flagopt(mp^.mnt_optnew, 'export', nil, 0)<>0) then
|
|
Exit(0)
|
|
else
|
|
Exit(EOPNOTSUPP);
|
|
end;
|
|
|
|
{
|
|
* Get argument
|
|
}
|
|
error:=vfs_getopt(mp^.mnt_optnew, {'target'} 'from', @target, @len);
|
|
if (error<>0) or (target[len - 1]<>#0) then
|
|
Exit(EINVAL);
|
|
|
|
{
|
|
* Unlock lower node to avoid possible deadlock.
|
|
}
|
|
if (mp^.mnt_vnodecovered^.v_op=@null_vnodeops) and
|
|
(VOP_ISLOCKED(mp^.mnt_vnodecovered)=LK_EXCLUSIVE) then
|
|
begin
|
|
VOP_UNLOCK(mp^.mnt_vnodecovered, 0);
|
|
isvnunlocked:=1;
|
|
end;
|
|
|
|
{
|
|
* Find lower node
|
|
}
|
|
NDINIT(ndp, LOOKUP, FOLLOW or LOCKLEAF, UIO_SYSSPACE, target, curkthread);
|
|
error:=nd_namei(ndp);
|
|
|
|
{
|
|
* Re-lock vnode.
|
|
* XXXKIB This is deadlock-prone as well.
|
|
}
|
|
if (isvnunlocked<>0) then
|
|
begin
|
|
vn_lock(mp^.mnt_vnodecovered, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
end;
|
|
|
|
if (error<>0) then
|
|
Exit(error);
|
|
|
|
NDFREE(ndp, NDF_ONLY_PNBUF);
|
|
|
|
{
|
|
* Sanity check on lower vnode
|
|
}
|
|
lowerrootvp:=ndp^.ni_vp;
|
|
|
|
{
|
|
* Check multi null mount to avoid `lock against myself' panic.
|
|
}
|
|
if (lowerrootvp=VTONULL(mp^.mnt_vnodecovered)^.null_lowervp) then
|
|
begin
|
|
vput(lowerrootvp);
|
|
Exit(EDEADLK);
|
|
end;
|
|
|
|
xmp:=AllocMem(sizeof(t_null_mount));
|
|
|
|
if (lowerrootvp=nil) then
|
|
begin
|
|
xmp^.nullm_vfs:=nil;
|
|
|
|
vp:=nil;
|
|
error:=0;
|
|
end else
|
|
begin
|
|
{
|
|
* Save reference to underlying FS
|
|
}
|
|
xmp^.nullm_vfs:=lowerrootvp^.v_mount;
|
|
end;
|
|
|
|
{
|
|
* Save reference. Each mount also holds
|
|
* a reference on the root vnode.
|
|
}
|
|
error:=null_nodeget(mp, lowerrootvp, @vp);
|
|
|
|
{
|
|
* Make sure the node alias worked
|
|
}
|
|
if (error<>0) then
|
|
begin
|
|
FreeMem(xmp);
|
|
Exit(error);
|
|
end;
|
|
|
|
{
|
|
* Keep a held reference to the root vnode.
|
|
* It is vrele'd in nullfs_unmount.
|
|
}
|
|
nullm_rootvp:=vp;
|
|
nullm_rootvp^.v_vflag:=nullm_rootvp^.v_vflag or VV_ROOT;
|
|
xmp^.nullm_rootvp:=nullm_rootvp;
|
|
|
|
{
|
|
* Unlock the node (either the lower or the alias)
|
|
}
|
|
VOP_UNLOCK(vp, 0);
|
|
|
|
lvp:=NULLVPTOLOWERVP(nullm_rootvp);
|
|
if (lvp<>nil) then
|
|
begin
|
|
lmp:=lvp^.v_mount;
|
|
if ((lmp^.mnt_flag and MNT_LOCAL)<>0) then
|
|
begin
|
|
MNT_ILOCK(mp);
|
|
mp^.mnt_flag:=mp^.mnt_flag or MNT_LOCAL;
|
|
MNT_IUNLOCK(mp);
|
|
end;
|
|
end;
|
|
|
|
xmp^.nullm_flags:=xmp^.nullm_flags or NULLM_CACHE;
|
|
|
|
if (vfs_getopt(mp^.mnt_optnew, 'nocache', nil, nil)=0) then
|
|
xmp^.nullm_flags:=xmp^.nullm_flags and (not NULLM_CACHE);
|
|
|
|
MNT_ILOCK(mp);
|
|
if ((xmp^.nullm_flags and NULLM_CACHE)<>0) then
|
|
begin
|
|
if (lowerrootvp<>nil) then
|
|
begin
|
|
lmp:=lowerrootvp^.v_mount;
|
|
mp^.mnt_kern_flag:=mp^.mnt_kern_flag or (lmp^.mnt_kern_flag and (MNTK_MPSAFE or MNTK_SHARED_WRITES or MNTK_LOOKUP_SHARED or MNTK_EXTENDED_SHARED));
|
|
end;
|
|
end;
|
|
mp^.mnt_kern_flag:=mp^.mnt_kern_flag or MNTK_LOOKUP_EXCL_DOTDOT;
|
|
MNT_IUNLOCK(mp);
|
|
mp^.mnt_data:=xmp;
|
|
vfs_getnewfsid(mp);
|
|
if ((xmp^.nullm_flags and NULLM_CACHE)<>0) then
|
|
if (xmp^.nullm_vfs<>nil) then
|
|
begin
|
|
MNT_ILOCK(xmp^.nullm_vfs);
|
|
TAILQ_INSERT_TAIL(@xmp^.nullm_vfs^.mnt_uppers,mp,@mp^.mnt_upper_link);
|
|
MNT_IUNLOCK(xmp^.nullm_vfs);
|
|
end;
|
|
|
|
vfs_mountedfrom(mp, target);
|
|
|
|
Exit(0);
|
|
end;
|
|
|
|
{
|
|
* Free reference to null layer
|
|
}
|
|
function nullfs_unmount(mp:p_mount;mntflags:Integer):Integer;
|
|
var
|
|
mntdata:p_null_mount;
|
|
ump:p_mount;
|
|
error,flags:Integer;
|
|
begin
|
|
if ((mntflags and MNT_FORCE)<>0) then
|
|
flags:=FORCECLOSE
|
|
else
|
|
flags:=0;
|
|
|
|
{ There is 1 extra root vnode reference (nullm_rootvp). }
|
|
error:=vflush(mp, 1, flags);
|
|
if (error<>0) then
|
|
Exit(error);
|
|
|
|
{
|
|
* Finally, throw away the null_mount structure
|
|
}
|
|
mntdata:=mp^.mnt_data;
|
|
ump:=mntdata^.nullm_vfs;
|
|
|
|
if (ump<>nil) then
|
|
if ((mntdata^.nullm_flags and NULLM_CACHE)<>0) then
|
|
begin
|
|
MNT_ILOCK(ump);
|
|
while ((ump^.mnt_kern_flag and MNTK_VGONE_UPPER)<>0) do
|
|
begin
|
|
ump^.mnt_kern_flag:=ump^.mnt_kern_flag or MNTK_VGONE_WAITER;
|
|
msleep(@ump^.mnt_uppers, @ump^.mnt_mtx, 0, 'vgnupw', 0);
|
|
end;
|
|
TAILQ_REMOVE(@ump^.mnt_uppers,mp,@mp^.mnt_upper_link);
|
|
MNT_IUNLOCK(ump);
|
|
end;
|
|
|
|
mp^.mnt_data:=nil;
|
|
|
|
FreeMem(mntdata);
|
|
Exit(0);
|
|
end;
|
|
|
|
function nullfs_root(mp:p_mount;flags:Integer;vpp:pp_vnode):Integer;
|
|
var
|
|
vp:p_vnode;
|
|
xmp:p_null_mount;
|
|
begin
|
|
{
|
|
* Return locked reference to root.
|
|
}
|
|
xmp:=MOUNTTONULLMOUNT(mp);
|
|
if (xmp=nil) then Exit(EOPNOTSUPP);
|
|
|
|
vp:=xmp^.nullm_rootvp;
|
|
VREF(vp);
|
|
|
|
//ASSERT_VOP_UNLOCKED(vp, 'root vnode is locked');
|
|
vn_lock(vp, flags or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
vpp^:=vp;
|
|
Exit(0);
|
|
end;
|
|
|
|
function nullfs_quotactl(mp:p_mount;cmd,uid:Integer;arg:Pointer):Integer;
|
|
var
|
|
xmp:p_null_mount;
|
|
begin
|
|
xmp:=MOUNTTONULLMOUNT(mp);
|
|
if (xmp=nil) then Exit(EOPNOTSUPP);
|
|
|
|
Exit(VFS_QUOTACTL(xmp^.nullm_vfs, cmd, uid, arg));
|
|
end;
|
|
|
|
function nullfs_statfs(mp:p_mount;sbp:p_statfs):Integer;
|
|
var
|
|
error:Integer;
|
|
mstat:t_statfs;
|
|
xmp:p_null_mount;
|
|
begin
|
|
xmp:=MOUNTTONULLMOUNT(mp);
|
|
if (xmp=nil) then Exit(EOPNOTSUPP);
|
|
|
|
if (xmp^.nullm_vfs=nil) then
|
|
begin
|
|
sbp^.f_flags :=0;
|
|
sbp^.f_bsize :=DEV_BSIZE;
|
|
sbp^.f_iosize:=DEV_BSIZE;
|
|
sbp^.f_blocks:=2;
|
|
sbp^.f_bfree :=0;
|
|
sbp^.f_bavail:=0;
|
|
sbp^.f_files :=0;
|
|
sbp^.f_ffree :=0;
|
|
Exit(0);
|
|
end;
|
|
|
|
mstat:=Default(t_statfs);
|
|
error:=VFS_STATFS(xmp^.nullm_vfs, @mstat);
|
|
if (error<>0) then
|
|
Exit(error);
|
|
|
|
{ now copy across the 'interesting' information and fake the rest }
|
|
sbp^.f_type :=mstat.f_type;
|
|
sbp^.f_flags :=mstat.f_flags;
|
|
sbp^.f_bsize :=mstat.f_bsize;
|
|
sbp^.f_iosize:=mstat.f_iosize;
|
|
sbp^.f_blocks:=mstat.f_blocks;
|
|
sbp^.f_bfree :=mstat.f_bfree;
|
|
sbp^.f_bavail:=mstat.f_bavail;
|
|
sbp^.f_files :=mstat.f_files;
|
|
sbp^.f_ffree :=mstat.f_ffree;
|
|
|
|
Exit(0);
|
|
end;
|
|
|
|
function nullfs_sync(mp:p_mount;waitfor:Integer):Integer;
|
|
begin
|
|
{
|
|
* XXX - Assumes no data cached at null layer.
|
|
}
|
|
Exit(0);
|
|
end;
|
|
|
|
function nullfs_vget(mp:p_mount;ino:DWORD;flags:Integer;vpp:pp_vnode):Integer;
|
|
var
|
|
xmp:p_null_mount;
|
|
error:Integer;
|
|
begin
|
|
Assert((flags and LK_TYPE_MASK)<>0,'nullfs_vget: no lock requested');
|
|
|
|
xmp:=MOUNTTONULLMOUNT(mp);
|
|
if (xmp=nil) then Exit(EOPNOTSUPP);
|
|
|
|
if (xmp^.nullm_vfs=nil) then
|
|
begin
|
|
Exit(ENOENT);
|
|
end;
|
|
|
|
error:=VFS_VGET(xmp^.nullm_vfs, ino, flags, vpp);
|
|
if (error<>0) then
|
|
Exit(error);
|
|
|
|
Exit(null_nodeget(mp, vpp^, vpp));
|
|
end;
|
|
|
|
function nullfs_fhtovp(mp:p_mount;fidp:p_fid;flags:Integer;vpp:pp_vnode):Integer;
|
|
var
|
|
xmp:p_null_mount;
|
|
error:Integer;
|
|
begin
|
|
xmp:=MOUNTTONULLMOUNT(mp);
|
|
if (xmp=nil) then Exit(EOPNOTSUPP);
|
|
|
|
if (xmp^.nullm_vfs=nil) then
|
|
begin
|
|
Exit(ENOENT);
|
|
end;
|
|
|
|
error:=VFS_FHTOVP(xmp^.nullm_vfs, fidp, flags, vpp);
|
|
if (error<>0) then
|
|
Exit(error);
|
|
Exit(null_nodeget(mp, vpp^, vpp));
|
|
end;
|
|
|
|
function nullfs_extattrctl(mp:p_mount;cmd:Integer;filename_vp:p_vnode;namespace:Integer;attrname:PChar):Integer;
|
|
var
|
|
xmp:p_null_mount;
|
|
begin
|
|
xmp:=MOUNTTONULLMOUNT(mp);
|
|
if (xmp=nil) then Exit(EOPNOTSUPP);
|
|
|
|
if (xmp^.nullm_vfs=nil) then
|
|
begin
|
|
Exit(ENOENT);
|
|
end;
|
|
|
|
Exit(VFS_EXTATTRCTL(xmp^.nullm_vfs, cmd, filename_vp, namespace, attrname));
|
|
end;
|
|
|
|
end.
|
|
|