FPPS4/sys/fs/ufs/ufs.pas

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.