mirror of https://github.com/red-prig/fpPS4.git
563 lines
10 KiB
Plaintext
563 lines
10 KiB
Plaintext
unit ufs;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
mqueue,
|
|
kern_param,
|
|
vnode,
|
|
vmount,
|
|
vdirent,
|
|
vfile,
|
|
time,
|
|
kern_mtx,
|
|
kern_sx;
|
|
|
|
type
|
|
pp_ufs_dirent=^p_ufs_dirent;
|
|
p_ufs_dirent=^t_ufs_dirent;
|
|
|
|
t_ufs_dirent=record
|
|
ufs_inode :Integer;
|
|
ufs_flags :Integer; //UFS_*
|
|
ufs_ref :Integer;
|
|
ufs_vref :Integer;
|
|
ufs_dirent :p_dirent;
|
|
ufs_list :TAILQ_ENTRY;
|
|
ufs_dlist :TAILQ_HEAD; //dir list
|
|
ufs_dir :p_ufs_dirent; //parent
|
|
ufs_links :Integer;
|
|
ufs_mode :mode_t; //S_IFMT
|
|
ufs_uid :uid_t;
|
|
ufs_gid :gid_t;
|
|
ufs_size :Int64; // file size in bytes
|
|
ufs_bytes :Int64; // bytes of disk space held by file
|
|
ufs_atime :timespec; // time of last access
|
|
ufs_mtime :timespec; // time of last data modification
|
|
ufs_ctime :timespec; // time of last file status change
|
|
ufs_btime :timespec; // time of file creation
|
|
ufs_vnode :p_vnode;
|
|
ufs_symlink:PChar;
|
|
ufs_md_lock:t_sx;
|
|
ufs_md_fp :Pointer; //host data
|
|
end;
|
|
|
|
p_ufs_mount=^t_ufs_mount;
|
|
t_ufs_mount=record
|
|
//ufs_idx :DWORD;
|
|
ufs_mount :p_mount;
|
|
ufs_rootdir :p_ufs_dirent;
|
|
ufs_generation:DWORD;
|
|
ufs_ref :Integer;
|
|
ufs_lock :t_sx;
|
|
ufs_vnops :p_vop_vector;
|
|
ufs_path :PChar; //host path
|
|
ufs_md_fp :Pointer; //host data
|
|
end;
|
|
|
|
function ufs_init(cf:p_vfsconf):Integer;
|
|
function ufs_uinit(cf:p_vfsconf):Integer;
|
|
|
|
function ufs_mount(mp:p_mount):Integer;
|
|
function ufs_root(mp:p_mount;flags:Integer;vpp:pp_vnode):Integer;
|
|
function ufs_unmount(mp:p_mount;mntflags:Integer):Integer;
|
|
function ufs_statfs(mp:p_mount;sbp:p_statfs):Integer;
|
|
|
|
const
|
|
_ufs_vfsops:vfsops=(
|
|
vfs_mount :@ufs_mount;
|
|
vfs_cmount :nil;
|
|
vfs_unmount :@ufs_unmount;
|
|
vfs_root :@ufs_root;
|
|
vfs_quotactl :nil;
|
|
vfs_statfs :@ufs_statfs;
|
|
vfs_sync :nil;
|
|
vfs_vget :nil;
|
|
vfs_fhtovp :nil;
|
|
vfs_checkexp :nil;
|
|
vfs_init :@ufs_init;
|
|
vfs_uninit :@ufs_uinit;
|
|
vfs_extattrctl :nil;
|
|
vfs_sysctl :nil;
|
|
vfs_susp_clean :nil;
|
|
);
|
|
|
|
var
|
|
//VFS_SET(ufs_vfsops, ufs, 0);
|
|
ufs_vfsconf:vfsconf=(
|
|
vfc_version :VFS_VERSION;
|
|
vfc_name :'ufs';
|
|
vfc_vfsops :@_ufs_vfsops;
|
|
vfc_typenum :-1;
|
|
vfc_refcount:0;
|
|
vfc_flags :0;
|
|
vfc_opts :nil;
|
|
vfc_list :(tqe_next:nil;tqe_prev:nil)
|
|
);
|
|
|
|
function ufs_alloc_cdp_inode():Integer;
|
|
procedure ufs_free_cdp_inode(ino:Integer);
|
|
|
|
procedure ufs_de_hold(p:p_ufs_dirent);
|
|
function ufs_de_drop(p:p_ufs_dirent):Boolean;
|
|
|
|
procedure ufs_mp_hold(p:p_ufs_mount);
|
|
function ufs_mp_drop(p:p_ufs_mount):Boolean;
|
|
|
|
function ufs_relv(vp:p_vnode):p_ufs_dirent;
|
|
function ufs_allocv(de:p_ufs_dirent;mp:p_mount;lockmode:Integer;vpp:pp_vnode):Integer;
|
|
|
|
var
|
|
ufs_interlock:mtx;
|
|
|
|
const
|
|
UFS_ROOTINO =1;
|
|
|
|
UFS_DROOT =$01;
|
|
UFS_DOT =$02;
|
|
UFS_DOTDOT =$04;
|
|
UFS_DOOMED =$08;
|
|
UFS_CREATE =$10;
|
|
|
|
UFS_DEFAULT_MODE=&0755;
|
|
|
|
implementation
|
|
|
|
uses
|
|
errno,
|
|
vfs_mount,
|
|
vfs_subr,
|
|
vfs_vnops,
|
|
vnode_if,
|
|
ufs_vnops,
|
|
md_vnops,
|
|
kern_id;
|
|
|
|
var
|
|
ufs_desc:t_id_desc=(free:nil;refs:0);
|
|
ufs_inos:t_id_desc_table;
|
|
|
|
function ufs_alloc_cdp_inode():Integer;
|
|
begin
|
|
if id_new(@ufs_inos,@ufs_desc,@Result) then
|
|
begin
|
|
id_release(@ufs_desc); //<-id_new
|
|
end else
|
|
begin
|
|
Result:=-1;
|
|
end;
|
|
end;
|
|
|
|
procedure ufs_free_cdp_inode(ino:Integer);
|
|
begin
|
|
if (ino>0) then
|
|
begin
|
|
id_del(@ufs_inos,ino,nil);
|
|
end;
|
|
end;
|
|
|
|
function ufs_init(cf:p_vfsconf):Integer;
|
|
begin
|
|
Result:=0;
|
|
id_table_init(@ufs_inos,UFS_ROOTINO+1);
|
|
mtx_init(ufs_interlock,'ufs_interlock');
|
|
end;
|
|
|
|
function ufs_uinit(cf:p_vfsconf):Integer;
|
|
begin
|
|
Result:=0;
|
|
id_table_fini(@ufs_inos);
|
|
mtx_destroy(ufs_interlock);
|
|
end;
|
|
|
|
function VFSTOUFS(mp:p_mount):p_ufs_mount; inline;
|
|
begin
|
|
Result:=mp^.mnt_data;
|
|
end;
|
|
|
|
procedure ufs_de_hold(p:p_ufs_dirent);
|
|
begin
|
|
System.InterlockedIncrement(p^.ufs_ref);
|
|
end;
|
|
|
|
function ufs_de_drop(p:p_ufs_dirent):Boolean;
|
|
begin
|
|
Result:=False;
|
|
if (System.InterlockedDecrement(p^.ufs_ref)=0) then
|
|
begin
|
|
md_free_dirent(p);
|
|
sx_destroy(@p^.ufs_md_lock);
|
|
FreeMem(p);
|
|
Result:=True;
|
|
end;
|
|
end;
|
|
|
|
procedure ufs_mp_hold(p:p_ufs_mount);
|
|
begin
|
|
System.InterlockedIncrement(p^.ufs_ref);
|
|
end;
|
|
|
|
function ufs_mp_drop(p:p_ufs_mount):Boolean;
|
|
begin
|
|
Result:=False;
|
|
if (System.InterlockedDecrement(p^.ufs_ref)=0) then
|
|
begin
|
|
md_unmount(p);
|
|
sx_destroy(@p^.ufs_lock);
|
|
FreeMem(p^.ufs_path);
|
|
FreeMem(p);
|
|
Result:=True;
|
|
end;
|
|
end;
|
|
|
|
function ufs_allocv_drop_refs(drop_lock:Integer;dmp:p_ufs_mount;de:p_ufs_dirent):Integer;
|
|
var
|
|
not_found:Integer;
|
|
begin
|
|
not_found:=0;
|
|
if ((de^.ufs_flags and UFS_DOOMED)<>0) then
|
|
begin
|
|
not_found:=1;
|
|
end;
|
|
|
|
ufs_de_drop(de);
|
|
|
|
if ufs_mp_drop(dmp) then
|
|
begin
|
|
not_found:=2;
|
|
sx_xunlock(@dmp^.ufs_lock);
|
|
end;
|
|
|
|
if (not_found=1) or ((drop_lock<>0) and (not_found<>2)) then
|
|
begin
|
|
sx_unlock(@dmp^.ufs_lock);
|
|
end;
|
|
|
|
Exit(not_found);
|
|
end;
|
|
|
|
procedure ufs_insmntque_dtr(vp:p_vnode;arg:Pointer);
|
|
begin
|
|
mtx_lock(ufs_interlock);
|
|
ufs_relv(vp);
|
|
mtx_unlock(ufs_interlock);
|
|
vgone(vp);
|
|
vput(vp);
|
|
end;
|
|
|
|
function ufs_relv(vp:p_vnode):p_ufs_dirent;
|
|
var
|
|
de:p_ufs_dirent;
|
|
begin
|
|
Result:=nil;
|
|
if (vp=nil) then Exit;
|
|
VI_LOCK(vp);
|
|
de:=System.InterlockedExchange(vp^.v_data,nil);
|
|
if (de<>nil) then
|
|
begin
|
|
de^.ufs_vnode:=nil;
|
|
vp^.v_data:=nil;
|
|
if (System.InterlockedDecrement(de^.ufs_vref)=0) then
|
|
begin
|
|
Result:=de;
|
|
end;
|
|
end;
|
|
VI_UNLOCK(vp);
|
|
end;
|
|
|
|
function ufs_allocv(de:p_ufs_dirent;mp:p_mount;lockmode:Integer;vpp:pp_vnode):Integer;
|
|
label
|
|
loop;
|
|
var
|
|
error:Integer;
|
|
vp:p_vnode;
|
|
dmp:p_ufs_mount;
|
|
begin
|
|
dmp:=VFSTOUFS(mp);
|
|
|
|
if ((de^.ufs_flags and UFS_DOOMED)<>0) then
|
|
begin
|
|
sx_xunlock(@dmp^.ufs_lock);
|
|
Exit(ENOENT);
|
|
end;
|
|
|
|
loop:
|
|
ufs_de_hold(de);
|
|
ufs_mp_hold(dmp);
|
|
|
|
mtx_lock(ufs_interlock);
|
|
|
|
vp:=nil;
|
|
//cache only dir
|
|
if (de^.ufs_dirent^.d_type=DT_DIR) then
|
|
begin
|
|
vp:=de^.ufs_vnode;
|
|
end;
|
|
|
|
if (vp<>nil) then
|
|
begin
|
|
VI_LOCK(vp);
|
|
mtx_unlock(ufs_interlock);
|
|
|
|
sx_xunlock(@dmp^.ufs_lock);
|
|
vget(vp, lockmode or LK_INTERLOCK or LK_RETRY);
|
|
sx_xlock(@dmp^.ufs_lock);
|
|
|
|
if (ufs_allocv_drop_refs(0, dmp, de)<>0) then
|
|
begin
|
|
vput(vp);
|
|
Exit(ENOENT);
|
|
end else
|
|
if ((vp^.v_iflag and VI_DOOMED)<>0) then
|
|
begin
|
|
mtx_lock(ufs_interlock);
|
|
if (de^.ufs_vnode=vp) then
|
|
begin
|
|
de^.ufs_vnode:=nil;
|
|
vp^.v_data:=nil;
|
|
end;
|
|
mtx_unlock(ufs_interlock);
|
|
vput(vp);
|
|
goto loop;
|
|
end;
|
|
|
|
sx_xunlock(@dmp^.ufs_lock);
|
|
vpp^:=vp;
|
|
Exit(0);
|
|
end;
|
|
|
|
mtx_unlock(ufs_interlock);
|
|
|
|
error:=getnewvnode('ufs', mp, dmp^.ufs_vnops, @vp);
|
|
if (error<>0) then
|
|
begin
|
|
ufs_allocv_drop_refs(1, dmp, de);
|
|
Exit(error);
|
|
end;
|
|
|
|
vp^.v_type:=iftovt_tab[de^.ufs_dirent^.d_type];
|
|
|
|
vn_lock(vp, LK_EXCLUSIVE or LK_RETRY or LK_NOWITNESS,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
//VN_LOCK_ASHARE(vp);
|
|
|
|
mtx_lock(ufs_interlock);
|
|
|
|
System.InterlockedIncrement(de^.ufs_vref);
|
|
vp^.v_data:=de;
|
|
|
|
//cache only dir
|
|
if (de^.ufs_dirent^.d_type=DT_DIR) then
|
|
begin
|
|
de^.ufs_vnode:=vp;
|
|
end;
|
|
|
|
mtx_unlock(ufs_interlock);
|
|
|
|
error:=insmntque1(vp, mp, @ufs_insmntque_dtr, de);
|
|
if (error<>0) then
|
|
begin
|
|
ufs_allocv_drop_refs(1, dmp, de);
|
|
Exit(error);
|
|
end;
|
|
|
|
if (ufs_allocv_drop_refs(0, dmp, de)<>0) then
|
|
begin
|
|
vput(vp);
|
|
Exit(ENOENT);
|
|
end;
|
|
|
|
sx_xunlock(@dmp^.ufs_lock);
|
|
vpp^:=vp;
|
|
Exit(0);
|
|
end;
|
|
|
|
function ufs_root(mp:p_mount;flags:Integer;vpp:pp_vnode):Integer;
|
|
var
|
|
error:Integer;
|
|
vp:p_vnode;
|
|
dmp:p_ufs_mount;
|
|
begin
|
|
dmp:=VFSTOUFS(mp);
|
|
sx_xlock(@dmp^.ufs_lock);
|
|
|
|
error:=ufs_allocv(dmp^.ufs_rootdir, mp, LK_EXCLUSIVE, @vp); //sx_xunlock
|
|
if (error<>0) then
|
|
begin
|
|
Exit(error);
|
|
end;
|
|
|
|
vp^.v_vflag:=vp^.v_vflag or VV_ROOT;
|
|
vpp^:=vp;
|
|
Exit(0);
|
|
end;
|
|
|
|
function ufs_mount(mp:p_mount):Integer;
|
|
label
|
|
_err,
|
|
_mount_err;
|
|
var
|
|
error:Integer;
|
|
fmp:p_ufs_mount;
|
|
rvp:p_vnode;
|
|
path:PChar;
|
|
plen:Integer;
|
|
begin
|
|
|
|
//if (ufs_unr=nil) then
|
|
// ufs_unr:=new_unrhdr(0, High(Integer));
|
|
|
|
error:=0;
|
|
|
|
if ((mp^.mnt_flag and MNT_UPDATE)<>0) then
|
|
begin
|
|
Exit(0);
|
|
end;
|
|
|
|
if (mp^.mnt_optnew<>nil) then
|
|
begin
|
|
//if (vfs_filteropt(mp^.mnt_optnew, ufs_opts)<>0) then
|
|
// Exit(EINVAL);
|
|
end;
|
|
|
|
if ((mp^.mnt_flag and MNT_ROOTFS)<>0) then
|
|
begin
|
|
path:=nil;
|
|
plen:=0;
|
|
end else
|
|
begin
|
|
if (mp^.mnt_optnew=nil) then Exit(EINVAL);
|
|
if (vfs_getopt(mp^.mnt_optnew, 'from', @path, @plen)<>0) then Exit(EINVAL);
|
|
if (path=nil) then Exit(EINVAL);
|
|
|
|
while (plen>0) and ((path[0]='/') or (path[0]='\')) do
|
|
begin
|
|
Inc(path);
|
|
Dec(plen);
|
|
end;
|
|
end;
|
|
|
|
fmp:=AllocMem(sizeof(t_ufs_mount));
|
|
|
|
if (path<>nil) then
|
|
begin
|
|
fmp^.ufs_path:=AllocMem(plen);
|
|
Move(path^,fmp^.ufs_path^,plen);
|
|
end;
|
|
|
|
//fmp^.ufs_idx:=alloc_ufs_unr);
|
|
sx_init(@fmp^.ufs_lock, 'ufsmount');
|
|
fmp^.ufs_ref:=1;
|
|
|
|
MNT_ILOCK(mp);
|
|
mp^.mnt_kern_flag:=mp^.mnt_kern_flag or MNTK_MPSAFE or MNTK_LOOKUP_SHARED or MNTK_EXTENDED_SHARED;
|
|
MNT_IUNLOCK(mp);
|
|
|
|
fmp^.ufs_mount:=mp;
|
|
mp^.mnt_data:=fmp;
|
|
vfs_getnewfsid(mp);
|
|
|
|
if ((mp^.mnt_flag and MNT_ROOTFS)<>0) then
|
|
begin
|
|
fmp^.ufs_vnops:=@ufs_vnodeops_root;
|
|
|
|
fmp^.ufs_rootdir:=ufs_vmkdir(fmp, nil, 0, nil, UFS_ROOTINO);
|
|
|
|
sx_xlock(@fmp^.ufs_lock);
|
|
ufs_vmkdir(fmp,'dev',3,fmp^.ufs_rootdir,0);
|
|
sx_xunlock(@fmp^.ufs_lock);
|
|
end else
|
|
begin
|
|
fmp^.ufs_vnops:=@md_vnodeops_host;
|
|
|
|
error:=md_mount(fmp);
|
|
if (error<>0) then goto _mount_err;
|
|
|
|
fmp^.ufs_rootdir:=md_vmkdir(fmp, nil, 0, nil);
|
|
end;
|
|
|
|
if (fmp^.ufs_rootdir=nil) then
|
|
begin
|
|
error:=EINVAL; //??
|
|
goto _err;
|
|
end;
|
|
|
|
error:=ufs_root(mp, LK_EXCLUSIVE, @rvp);
|
|
if (error<>0) then
|
|
begin
|
|
_err:
|
|
md_unmount(fmp);
|
|
_mount_err:
|
|
sx_xlock(@fmp^.ufs_lock);
|
|
ufs_purge(fmp,fmp^.ufs_rootdir);
|
|
sx_xunlock(@fmp^.ufs_lock);
|
|
sx_destroy(@fmp^.ufs_lock);
|
|
//free_unr(ufs_unr, fmp^.ufs_idx);
|
|
FreeMem(fmp);
|
|
Exit(error);
|
|
end;
|
|
|
|
VOP_UNLOCK(rvp, 0);
|
|
|
|
if (path=nil) then
|
|
begin
|
|
path:='ufs';
|
|
end;
|
|
|
|
vfs_mountedfrom(mp,path);
|
|
|
|
Exit(0);
|
|
end;
|
|
|
|
function ufs_unmount(mp:p_mount;mntflags:Integer):Integer;
|
|
var
|
|
error:Integer;
|
|
flags:Integer;
|
|
fmp:p_ufs_mount;
|
|
//idx:DWORD;
|
|
begin
|
|
flags:=0;
|
|
|
|
fmp:=VFSTOUFS(mp);
|
|
Assert(fmp^.ufs_mount<>nil,'devfs_unmount unmounted devfs_mount');
|
|
|
|
error:=vflush(mp, 1, flags);
|
|
if (error<>0) then
|
|
begin
|
|
Exit(error);
|
|
end;
|
|
|
|
sx_xlock(@fmp^.ufs_lock);
|
|
|
|
fmp^.ufs_mount:=nil;
|
|
mp^.mnt_data:=nil;
|
|
|
|
//idx:=fmp^.ufs_idx;
|
|
|
|
sx_xunlock(@fmp^.ufs_lock);
|
|
|
|
//free_unr(ufs_unr, idx);
|
|
|
|
ufs_mp_drop(fmp);
|
|
|
|
Exit(0);
|
|
end;
|
|
|
|
function ufs_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;
|
|
sbp^.f_bfree :=0;
|
|
sbp^.f_bavail:=0;
|
|
sbp^.f_files :=0;
|
|
sbp^.f_ffree :=0;
|
|
Exit(md_statfs(mp,sbp));
|
|
end;
|
|
|
|
end.
|
|
|