unit devfs_vfsops; {$mode ObjFPC}{$H+} {$CALLING SysV_ABI_CDecl} interface uses kern_param, vnode, vmount, devfs_int, 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; procedure devfs_unmount_final(fmp:p_devfs_mount); 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; ); 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; vfc_opts :nil; vfc_list :(tqe_next:nil;tqe_prev:nil) ); implementation uses errno, kern_mtx, kern_sx, vfs_mount, vfs_subr, vnode_if, devfs_devs; var unr_desc:t_id_desc=(free:nil;refs:0); //temp function VFSTODEVFS(mp:p_mount):p_devfs_mount; inline; begin Result:=mp^.mnt_data; end; function new_unrhdr(min,max:Integer):p_id_desc_table; begin Result:=AllocMem(SizeOf(t_id_desc_table)); id_table_init(Result,min,max); end; function alloc_unr(p:p_id_desc_table):Integer; begin if id_new(p,@unr_desc,@Result) then begin id_release(@unr_desc); //<-id_new 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; {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_xlock(@fmp^.dm_lock); devfs_purge(fmp,fmp^.dm_rootdir); sx_xunlock(@fmp^.dm_lock); 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); public; 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; Dec(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 begin Exit(error); end; 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.