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.