unit vfs_mount; {$mode ObjFPC}{$H+} {$CALLING SysV_ABI_CDecl} interface uses sysutils, mqueue, kern_param, vmount, vuio, vnamei, kern_mtx, kern_thr, vnode, vfs_init, vnode_if; const VFS_MOUNTARG_SIZE_MAX=(1024 * 64); global_opts:array[0..7] of PChar=( 'errmsg', 'fstype', 'fspath', 'ro', 'rw', 'nosuid', 'noexec', nil ); type { A memory allocation which must be freed when we are done } p_mntaarg=^t_mntaarg; t_mntaarg=packed record next:SLIST_ENTRY; //mntaarg end; { The header for the mount arguments } p_mntarg=^t_mntarg; t_mntarg=packed record v:p_iovec; len:Integer; error:Integer; list:SLIST_HEAD; //mntaarg end; procedure vfs_freeopt(opts:p_vfsoptlist;opt:p_vfsopt); procedure vfs_freeopts(opts:p_vfsoptlist); procedure vfs_deleteopt(opts:p_vfsoptlist;name:PChar); function vfs_isopt_ro(opt:PChar):Integer; function vfs_isopt_rw(opt:PChar):Integer; function vfs_equalopts(opt1,opt2:PChar):Integer; procedure vfs_sanitizeopts(opts:p_vfsoptlist); function vfs_buildopts(auio:p_uio;options:pp_vfsoptlist):Integer; procedure vfs_mergeopts(toopts,oldopts:p_vfsoptlist); procedure vfs_mount_error(mp:p_mount;fmt:PChar;const Args:Array of const); register; procedure vfs_opterror(opts:p_vfsoptlist;fmt:PChar;const Args:Array of const); register; function vfs_filteropt(opts:p_vfsoptlist;legal:ppchar):Integer; function vfs_getopt(opts:p_vfsoptlist;name:PChar;buf:PPointer;len:PInteger):Integer; function vfs_getopt_pos(opts:p_vfsoptlist;name:PChar):Integer; function vfs_getopts(opts:p_vfsoptlist;name:PChar;error:PInteger):PChar; function vfs_flagopt(opts:p_vfsoptlist;name:PChar;w:PQWORD;val:QWORD):Integer; function vfs_scanopt(opts:p_vfsoptlist;name,fmt:PChar;const Args:Array of const):Integer; register; function vfs_setopt(opts:p_vfsoptlist;name,value:PChar;len:Integer):Integer; function vfs_setopt_part(opts:p_vfsoptlist;name,value:PChar;len:Integer):Integer; function vfs_setopts(opts:p_vfsoptlist;name,value:PChar):Integer; procedure vfs_mountedfrom(mp:p_mount;from:PChar); procedure vfs_ref(mp:p_mount); inline; procedure vfs_rel(mp:p_mount); inline; procedure mount_init(mp:p_mount); procedure mount_fini(mp:p_mount); function vfs_mount_alloc(vp :p_vnode; vfsp :p_vfsconf; fspath:PChar):p_mount; procedure vfs_mount_destroy(mp:p_mount); function vfs_domount(fstype:PChar; { Filesystem type. } fspath:PChar; { Mount path. } fsflags:QWORD; { Flags common to all filesystems. } optlist:pp_vfsoptlist { Options local to the filesystem. } ):Integer; function vfs_donmount(fsflags:QWORD;fsoptions:p_uio):Integer; function dounmount(mp:p_mount;flags:Integer):Integer; function mount_argb(ma:p_mntarg;flag:Integer;name:PChar):p_mntarg; function mount_argf(ma:p_mntarg;name,fmt:PChar;const Args:Array of const):p_mntarg; register; function mount_argsu(ma:p_mntarg;name:PChar;val:Pointer;len:Integer):p_mntarg; function mount_arg(ma:p_mntarg;name:PChar;val:Pointer;len:Integer):p_mntarg; procedure free_mntarg(ma:p_mntarg); function kernel_nmount(ma:p_mntarg;flags:QWORD):Integer; function kern_unmount(path:PChar;flags:Integer):Integer; function sys_nmount(iovp:Pointer;iovcnt:DWORD;flags:QWORD):Integer; function sys_mount(ftype,fpath:PChar;flags:QWORD;data:Pointer):Integer; function sys_unmount(path:PChar;flags:Integer):Integer; implementation uses murmurhash, errno, systm, subr_uio, vfs_vnops, vfs_subr, vfs_cache; { * --------------------------------------------------------------------- * Functions for building and sanitizing the mount options } { Remove one mount option. } procedure vfs_freeopt(opts:p_vfsoptlist;opt:p_vfsopt); begin TAILQ_REMOVE(opts,opt,@opt^.link); FreeMem(opt^.name); if (opt^.value<>nil) then begin FreeMem(opt^.value); end; FreeMem(opt); end; { Release all resources related to the mount options. } procedure vfs_freeopts(opts:p_vfsoptlist); var opt:p_vfsopt; begin while (not TAILQ_EMPTY(opts)) do begin opt:=TAILQ_FIRST(opts); vfs_freeopt(opts, opt); end; FreeMem(opts); end; procedure vfs_deleteopt(opts:p_vfsoptlist;name:PChar); var opt,temp:p_vfsopt; begin if (opts=nil) then Exit; opt:=TAILQ_FIRST(opts); while (opt<>nil) do begin temp:=TAILQ_NEXT(opt,@opt^.link); // if (strcomp(opt^.name, name)=0) then begin vfs_freeopt(opts, opt); end; // opt:=temp; end; end; function vfs_isopt_ro(opt:PChar):Integer; begin if (strcomp(opt, 'ro')=0) or (strcomp(opt, 'rdonly')=0) or (strcomp(opt, 'norw')=0) then begin Exit(1); end; Exit(0); end; function vfs_isopt_rw(opt:PChar):Integer; begin if (strcomp(opt, 'rw')=0) or (strcomp(opt, 'noro')=0) then begin Exit(1); end; Exit(0); end; { * Check if options are equal (with or without the 'no' prefix). } function vfs_equalopts(opt1,opt2:PChar):Integer; var p:PChar; begin { 'opt' vs. 'opt' or 'noopt' vs. 'noopt' } if (strcomp(opt1, opt2)=0) then begin Exit(1); end; { 'noopt' vs. 'opt' } if (strlcomp(opt1, 'no', 2)=0) and (strcomp(opt1 + 2, opt2)=0) then begin Exit(1); end; { 'opt' vs. 'noopt' } if (strlcomp(opt2, 'no', 2)=0) and (strcomp(opt1, opt2 + 2)=0) then begin Exit(1); end; p:=strscan(opt1, '.'); while (p<>nil) and (strlcomp(opt1,opt2,(p+1)-opt1)=0) do begin Inc(p); Inc(opt2,p-opt1); opt1:=p; { 'foo.noopt' vs. 'foo.opt' } if (strlcomp(opt1, 'no', 2)=0) and (strcomp(opt1 + 2, opt2)=0) then Exit(1); { 'foo.opt' vs. 'foo.noopt' } if (strlcomp(opt2, 'no', 2)=0) and (strcomp(opt1, opt2 + 2)=0) then Exit(1); // p:=strscan(opt1, '.'); end; { 'ro' / 'rdonly' / 'norw' / 'rw' / 'noro' } if ((vfs_isopt_ro(opt1)<>0) or (vfs_isopt_rw(opt1)<>0)) and ((vfs_isopt_ro(opt2)<>0) or (vfs_isopt_rw(opt2)<>0)) then begin Exit(1); end; Exit(0); end; { * If a mount option is specified several times, * (with or without the 'no' prefix) only keep * the last occurence of it. } procedure vfs_sanitizeopts(opts:p_vfsoptlist); var opt,opt2,tmp:p_vfsopt; begin opt:=TAILQ_LAST(opts); while (opt<>nil) do begin opt2:=TAILQ_PREV(opt,@opt^.link); while (opt2<>nil) do begin if (vfs_equalopts(opt^.name, opt2^.name)<>0) then begin tmp:=TAILQ_PREV(opt2,@opt2^.link); vfs_freeopt(opts, opt2); opt2:=tmp; end else begin opt2:=TAILQ_PREV(opt2,@opt2^.link); end; end; // opt:=TAILQ_PREV(opt,@opt^.link); end; end; { * Build a linked list of mount options from a struct uio. } function vfs_buildopts(auio:p_uio;options:pp_vfsoptlist):Integer; label bad; var opts:p_vfsoptlist; opt:p_vfsopt; memused,namelen,optlen:QWORD; i,iovcnt:DWORD; error:Integer; begin opts:=AllocMem(sizeof(vfsoptlist)); TAILQ_INIT(opts); memused:=0; iovcnt:=auio^.uio_iovcnt; i:=0; while (i < iovcnt) do begin namelen:=auio^.uio_iov[i].iov_len; optlen :=auio^.uio_iov[i + 1].iov_len; Inc(memused,sizeof(vfsopt)+optlen+namelen); { * Avoid consuming too much memory, and attempts to overflow * memused. } if (memused > VFS_MOUNTARG_SIZE_MAX) or (optlen > VFS_MOUNTARG_SIZE_MAX) or (namelen > VFS_MOUNTARG_SIZE_MAX) then begin error:=EINVAL; goto bad; end; opt:=AllocMem(sizeof(vfsopt)); opt^.name :=AllocMem(namelen); opt^.value:=nil; opt^.len :=0; opt^.pos :=i div 2; opt^.seen :=0; { * Do this early, so jumps to 'bad' will free the current * option. } TAILQ_INSERT_TAIL(opts,opt,@opt^.link); if (auio^.uio_segflg=UIO_SYSSPACE) then begin Move(auio^.uio_iov[i].iov_base^, opt^.name^, namelen); end else begin error:=copyin(auio^.uio_iov[i].iov_base, opt^.name, namelen); if (error<>0) then begin goto bad; end; end; { Ensure names are nil-terminated strings. } if (namelen=0) or (opt^.name[namelen - 1]<>#0) then begin error:=EINVAL; goto bad; end; if (optlen<>0) then begin opt^.len:=optlen; opt^.value:=AllocMem(optlen); if (auio^.uio_segflg=UIO_SYSSPACE) then begin Move(auio^.uio_iov[i + 1].iov_base^, opt^.value^, optlen); end else begin error:=copyin(auio^.uio_iov[i + 1].iov_base, opt^.value, optlen); if (error<>0) then begin goto bad; end; end; end; // Inc(i,2); end; vfs_sanitizeopts(opts); options^:=opts; Exit(0); bad: vfs_freeopts(opts); Exit(error); end; function strdup(src:PChar):PChar; inline; var i:ptrint; begin i:=strlen(src); Result:=AllocMem(i+1); Move(src^,Result^,i); end; { * Merge the old mount options with the new ones passed * in the MNT_UPDATE case. * * XXX: This function will keep a 'nofoo' option in the new * options. E.g, if the option's canonical name is 'foo', * 'nofoo' ends up in the mount point's active options. } procedure vfs_mergeopts(toopts,oldopts:p_vfsoptlist); var opt,new:p_vfsopt; begin opt:=TAILQ_FIRST(oldopts); while (opt<>nil) do begin new:=AllocMem(sizeof(vfsopt)); new^.name:=strdup(opt^.name); if (opt^.len<>0) then begin new^.value:=AllocMem(opt^.len); Move(opt^.value^, new^.value^, opt^.len); end else begin new^.value:=nil; end; new^.len :=opt^.len; new^.seen:=opt^.seen; TAILQ_INSERT_HEAD(toopts,new,@new^.link); // opt:=TAILQ_NEXT(opt,@opt^.link); end; vfs_sanitizeopts(toopts); end; { * Report errors during filesystem mounting. } procedure vfs_mount_error(mp:p_mount;fmt:PChar;const Args:Array of const); register; var moptlist:p_vfsoptlist; error,len:Integer; errmsg:PChar; S:RawByteString; begin moptlist:=mp^.mnt_optnew; error:=vfs_getopt(moptlist, 'errmsg', @errmsg, @len); if (error<>0) or (errmsg=nil) or (len<=0) then begin Exit; end; S:=Format(fmt,Args); if (len>(Length(S)+1)) then len:=Length(S)+1; Move(PChar(S)^,errmsg^,len); end; procedure vfs_opterror(opts:p_vfsoptlist;fmt:PChar;const Args:Array of const); register; var error,len:Integer; errmsg:PChar; S:RawByteString; begin error:=vfs_getopt(opts, 'errmsg', @errmsg, @len); if (error<>0) or (errmsg=nil) or (len<=0) then begin Exit; end; S:=Format(fmt,Args); if (len>(Length(S)+1)) then len:=Length(S)+1; Move(PChar(S)^,errmsg^,len); end; { * Check that no unknown options are given } function vfs_filteropt(opts:p_vfsoptlist;legal:ppchar):Integer; var opt:p_vfsopt; errmsg:array[0..254] of Char; t:ppchar; p,q:pchar; begin Result:=0; opt:=TAILQ_FIRST(opts); while (opt<>nil) do begin p:=opt^.name; q:=nil; if (p[0]='n') and (p[1]='o') then begin q:=p + 2; end; t:=@global_opts; while (t^<>nil) do begin if (strcomp(t^, p)=0) then break; if (q<>nil) then begin if (strcomp(t^, q)=0) then break; end; Inc(t); end; if (t^<>nil) then begin opt:=TAILQ_NEXT(opt,@opt^.link); // continue; end; t:=legal; while (t^<>nil) do begin if (strcomp(t^, p)=0) then break; if (q<>nil) then begin if (strcomp(t^, q)=0) then break; end; Inc(t); end; if (t^<>nil) then begin opt:=TAILQ_NEXT(opt,@opt^.link); // continue; end; errmsg:='mount option is unknown'; Result:=EINVAL; // opt:=TAILQ_NEXT(opt,@opt^.link); end; if (Result<>0) then begin opt:=TAILQ_FIRST(opts); while (opt<>nil) do begin if (strcomp(opt^.name, 'errmsg')=0) then begin strlcopy(opt^.value, errmsg, opt^.len); break; end; opt:=TAILQ_NEXT(opt,@opt^.link); end; if (opt=nil) then begin Writeln(errmsg); end; end; end; { * Get a mount option by its name. * * return 0 if the option was found, ENOENT otherwise. * If len is non-nil it will be filled with the length * of the option. If buf is non-nil, it will be filled * with the address of the option. } function vfs_getopt(opts:p_vfsoptlist;name:PChar;buf:PPointer;len:PInteger):Integer; var opt:p_vfsopt; begin Assert(opts<>nil,'vfs_getopt: caller passed opts as nil'); opt:=TAILQ_FIRST(opts); while (opt<>nil) do begin if (strcomp(name, opt^.name)=0) then begin opt^.seen:=1; if (len<>nil) then begin len^:=opt^.len; end; if (buf<>nil) then begin buf^:=opt^.value; end; Exit(0); end; // opt:=TAILQ_NEXT(opt,@opt^.link); end; Exit(ENOENT); end; function vfs_getopt_pos(opts:p_vfsoptlist;name:PChar):Integer; var opt:p_vfsopt; begin if (opts=nil) then begin Exit(-1); end; opt:=TAILQ_FIRST(opts); while (opt<>nil) do begin if (strcomp(name, opt^.name)=0) then begin opt^.seen:=1; Exit(opt^.pos); end; // opt:=TAILQ_NEXT(opt,@opt^.link); end; Exit(-1); end; function vfs_getopts(opts:p_vfsoptlist;name:PChar;error:PInteger):PChar; var opt:p_vfsopt; begin error^:=0; opt:=TAILQ_FIRST(opts); while (opt<>nil) do begin if (strcomp(name, opt^.name)<>0) then begin opt:=TAILQ_NEXT(opt,@opt^.link); continue; end; opt^.seen:=1; if (opt^.len=0) or (PChar(opt^.value)[opt^.len - 1]<>#0) then begin error^:=EINVAL; Exit(nil); end; Exit(opt^.value); end; error^:=ENOENT; Exit(nil); end; function vfs_flagopt(opts:p_vfsoptlist;name:PChar;w:PQWORD;val:QWORD):Integer; var opt:p_vfsopt; begin opt:=TAILQ_FIRST(opts); while (opt<>nil) do begin if (strcomp(name, opt^.name)=0) then begin opt^.seen:=1; if (w<>nil) then begin w^:=w^ or val; end; Exit(1); end; // opt:=TAILQ_NEXT(opt,@opt^.link); end; if (w<>nil) then begin w^:=w^ and (not val); end; Exit(0); end; function vfs_scanopt(opts:p_vfsoptlist;name,fmt:PChar;const Args:Array of const):Integer; register; var opt:p_vfsopt; S:RawByteString; begin Assert(opts<>nil, 'vfs_getopt: caller passed opts as nil'); opt:=TAILQ_FIRST(opts); while (opt<>nil) do begin if (strcomp(name, opt^.name)<>0) then begin opt:=TAILQ_NEXT(opt,@opt^.link); continue; end; opt^.seen:=1; if (opt^.len=0) or (opt^.value=nil) then begin Exit(0); end; if (PChar(opt^.value)[opt^.len - 1]<>#0) then begin Exit(0); end; S:=Format(fmt,Args); Move(PChar(S)^,opt^.value^,Length(S)+1); Exit(0); end; Exit(0); end; function vfs_setopt(opts:p_vfsoptlist;name,value:PChar;len:Integer):Integer; var opt:p_vfsopt; begin opt:=TAILQ_FIRST(opts); while (opt<>nil) do begin if (strcomp(name, opt^.name)<>0) then begin opt:=TAILQ_NEXT(opt,@opt^.link); continue; end; opt^.seen:=1; if (opt^.value=nil) then begin opt^.len:=len; end else begin if (opt^.len<>len) then begin Exit(EINVAL); end; Move(value^, opt^.value^, len); end; Exit(0); end; Exit(ENOENT); end; function vfs_setopt_part(opts:p_vfsoptlist;name,value:PChar;len:Integer):Integer; var opt:p_vfsopt; begin opt:=TAILQ_FIRST(opts); while (opt<>nil) do begin if (strcomp(name, opt^.name)<>0) then begin opt:=TAILQ_NEXT(opt,@opt^.link); continue; end; opt^.seen:=1; if (opt^.value=nil) then begin opt^.len:=len; end else begin if (opt^.len < len) then begin Exit(EINVAL); end; opt^.len:=len; Move(value^, opt^.value^, len); end; Exit(0); end; Exit(ENOENT); end; function strlcpy(dst,src:PChar;size:ptrint):ptrint; inline; begin strlcopy(dst,src,size); Result:=strlen(dst); end; function vfs_setopts(opts:p_vfsoptlist;name,value:PChar):Integer; var opt:p_vfsopt; begin opt:=TAILQ_FIRST(opts); while (opt<>nil) do begin if (strcomp(name, opt^.name)<>0) then begin opt:=TAILQ_NEXT(opt,@opt^.link); continue; end; opt^.seen:=1; if (opt^.value=nil) then begin opt^.len:=strlen(value) + 1; end else if (strlcpy(opt^.value, value, opt^.len) >= opt^.len) then begin Exit(EINVAL); end; Exit(0); end; Exit(ENOENT); end; procedure vfs_mountedfrom(mp:p_mount;from:PChar); begin mp^.mnt_stat.f_mntfromname:=Default(t_mname); strlcopy(@mp^.mnt_stat.f_mntfromname,from,sizeof(mp^.mnt_stat.f_mntfromname)); end; /// procedure vfs_ref(mp:p_mount); inline; begin MNT_REF(mp); end; procedure vfs_rel(mp:p_mount); inline; begin MNT_REL(mp); end; procedure mount_init(mp:p_mount); begin mtx_init(mp^.mnt_mtx ,'struct mount mtx'); mtx_init(mp^.mnt_explock,'explock'); end; procedure mount_fini(mp:p_mount); begin mtx_destroy(mp^.mnt_explock); mtx_destroy(mp^.mnt_mtx); end; var mnt_hashseed:QWORD=QWORD($FEEDBABEFEEDBABE); function get_mnt_hashseed:DWORD; var i:QWORD; begin i:=MurmurHash64A(@mnt_hashseed,SizeOf(mnt_hashseed),mnt_hashseed); mnt_hashseed:=i; Result:=DWORD(i); end; function vfs_mount_alloc(vp :p_vnode; vfsp :p_vfsconf; fspath:PChar):p_mount; var mp:p_mount; begin mp:=AllocMem(SizeOf(t_mount)); mount_init(mp); TAILQ_INIT(@mp^.mnt_nvnodelist); mp^.mnt_nvnodelistsize:=0; TAILQ_INIT(@mp^.mnt_activevnodelist); mp^.mnt_activevnodelistsize:=0; mp^.mnt_ref:=0; vfs_busy(mp, MBF_NOWAIT); mp^.mnt_op:=vfsp^.vfc_vfsops; mp^.mnt_vfc:=vfsp; Inc(vfsp^.vfc_refcount); // XXX Unlocked mp^.mnt_stat.f_type:=vfsp^.vfc_typenum; Inc(mp^.mnt_gen); strlcopy(@mp^.mnt_stat.f_fstypename, @vfsp^.vfc_name, MFSNAMELEN); mp^.mnt_vnodecovered:=vp; strlcopy(@mp^.mnt_stat.f_mntonname, fspath, MNAMELEN); mp^.mnt_iosize_max:=DFLTPHYS; //mac_mount_init(mp); //mac_mount_create(cred, mp); //arc4rand(&mp->mnt_hashseed, sizeof mp->mnt_hashseed, 0); mp^.mnt_hashseed:=get_mnt_hashseed; TAILQ_INIT(@mp^.mnt_uppers); Result:=mp; end; procedure vfs_mount_destroy(mp:p_mount); begin MNT_ILOCK(mp); mp^.mnt_kern_flag:=mp^.mnt_kern_flag or MNTK_REFEXPIRE; if ((mp^.mnt_kern_flag and MNTK_MWAIT)<>0) then begin mp^.mnt_kern_flag:=mp^.mnt_kern_flag and (not MNTK_MWAIT); wakeup(mp); end; while (mp^.mnt_ref<>0) do begin msleep(mp, MNT_MTX(mp), PVFS, 'mntref', 0); end; Assert(mp^.mnt_ref=0,'invalid refcount in the drain path'); Assert(mp^.mnt_writeopcount=0,'vfs_mount_destroy: nonzero writeopcount'); Assert(mp^.mnt_secondary_writes=0,'vfs_mount_destroy: nonzero secondary_writes'); Dec(mp^.mnt_vfc^.vfc_refcount); if (not TAILQ_EMPTY(@mp^.mnt_nvnodelist)) then begin Assert(false,'unmount: dangling vnode'); end; Assert(TAILQ_EMPTY(@mp^.mnt_uppers),'mnt_uppers'); Assert(mp^.mnt_nvnodelistsize=0,'vfs_mount_destroy: nonzero nvnodelistsize'); Assert(mp^.mnt_activevnodelistsize=0,'vfs_mount_destroy: nonzero activevnodelistsize'); Assert(mp^.mnt_lockref=0,'vfs_mount_destroy: nonzero lock refcount'); MNT_IUNLOCK(mp); //mac_mount_destroy(mp); if (mp^.mnt_opt<>nil) then begin vfs_freeopts(mp^.mnt_opt); end; mount_fini(mp); FreeMem(mp); end; { * vfs_domount_first(): first file system mount (not update) } function vfs_domount_first(vfsp:p_vfsconf; { File system type. } fspath:PChar; { Mount path. } vp:p_vnode; { Vnode to be covered. } fsflags:QWORD; { Flags common to all filesystems. } optlist:pp_vfsoptlist { Options local to the filesystem. } ):Integer; var //va:t_vattr; mp:p_mount; newdp:p_vnode; error:Integer; begin mtx_assert(VFS_Giant); Assert((fsflags and MNT_UPDATE)=0,'MNT_UPDATE shouldnt be here'); error:=0; //error:=vinvalbuf(vp, V_SAVE, 0, 0); //if (error=0) and (vp^.v_type<>VDIR) then // error:=ENOTDIR; if (error=0) then begin VI_LOCK(vp); if ((vp^.v_iflag and VI_MOUNT)=0) and (vp^.v_mountedhere=nil) then vp^.v_iflag:=vp^.v_iflag or VI_MOUNT else error:=EBUSY; VI_UNLOCK(vp); end; if (error<>0) then begin vput(vp); Exit(error); end; VOP_UNLOCK(vp, 0); { Allocate and initialize the filesystem. } mp:=vfs_mount_alloc(vp, vfsp, fspath); { XXXMAC: pass to vfs_mount_alloc? } mp^.mnt_optnew:=optlist^; { Set the mount level flags. } mp^.mnt_flag:=(fsflags and (MNT_UPDATEMASK or MNT_ROOTFS or MNT_RDONLY)); { * Mount the filesystem. * XXX The final recipients of VFS_MOUNT just overwrite the ndp they * get. No freeing of cn_pnbuf. } error:=vmount.VFS_MOUNT(mp); if (error<>0) then begin vfs_unbusy(mp); vfs_mount_destroy(mp); VI_LOCK(vp); vp^.v_iflag:=vp^.v_iflag and (not VI_MOUNT); VI_UNLOCK(vp); vrele(vp); Exit(error); end; if (mp^.mnt_opt<>nil) then begin vfs_freeopts(mp^.mnt_opt); end; mp^.mnt_opt:=mp^.mnt_optnew; optlist^:=nil; VFS_STATFS(mp,@mp^.mnt_stat); { * Prevent external consumers of mount options from reading mnt_optnew. } mp^.mnt_optnew:=nil; MNT_ILOCK(mp); if ((mp^.mnt_flag and MNT_ASYNC)<>0) and ((mp^.mnt_kern_flag and MNTK_NOASYNC)=0) then mp^.mnt_kern_flag:=mp^.mnt_kern_flag or MNTK_ASYNC else mp^.mnt_kern_flag:=mp^.mnt_kern_flag and (not MNTK_ASYNC); MNT_IUNLOCK(mp); vn_lock(vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%}); //cache_purge(vp); VI_LOCK(vp); vp^.v_iflag:=vp^.v_iflag and (not VI_MOUNT); VI_UNLOCK(vp); vp^.v_mountedhere:=mp; { Place the new filesystem at the end of the mount list. } mtx_lock(mountlist_mtx); TAILQ_INSERT_TAIL(@mountlist, mp,@mp^.mnt_list); mtx_unlock(mountlist_mtx); vfs_event_signal(nil, VQ_MOUNT, 0); if (VFS_ROOT(mp,LK_EXCLUSIVE,@newdp)<>0) then begin Assert(false,'mount: lost mount'); end; VOP_UNLOCK(newdp, 0); VOP_UNLOCK(vp, 0); //mountcheckdirs(vp, newdp); vrele(newdp); //if ((mp^.mnt_flag and MNT_RDONLY)=0) // vfs_allocate_syncvnode(mp); vfs_unbusy(mp); Exit (0); end; { * vfs_domount_update(): update of mounted file system } function vfs_domount_update(vp:p_vnode; { Mount point vnode. } fsflags:QWORD; { Flags common to all filesystems. } optlist:pp_vfsoptlist { Options local to the filesystem. } ):Integer; label _end; var //struct oexport_args oexport; //struct export_args export; mp:p_mount; error,export_error:Integer; flag:QWORD; begin mtx_assert(VFS_Giant); ASSERT_VOP_ELOCKED(vp, 'vfs_domount_update'); Assert((fsflags and MNT_UPDATE)<>0, 'MNT_UPDATE should be here'); if ((vp^.v_vflag and VV_ROOT)=0) then begin vput(vp); Exit(EINVAL); end; mp:=vp^.v_mount; { * We only allow the filesystem to be reloaded if it * is currently mounted read-only. } flag:=mp^.mnt_flag; if ((fsflags and MNT_RELOAD)<>0) and ((flag and MNT_RDONLY)=0) then begin vput(vp); Exit(EOPNOTSUPP); { Needs translation } end; { * Only privileged root, or (if MNT_USER is set) the user that * did the original mount is permitted to update it. } error:=0; //error:=vfs_suser(mp, td); if (error<>0) then begin vput(vp); Exit(error); end; if (vfs_busy(mp, MBF_NOWAIT)<>0) then begin vput(vp); Exit(EBUSY); end; VI_LOCK(vp); if ((vp^.v_iflag and VI_MOUNT)<>0) or (vp^.v_mountedhere<>nil) then begin VI_UNLOCK(vp); vfs_unbusy(mp); vput(vp); Exit(EBUSY); end; vp^.v_iflag:=vp^.v_iflag or VI_MOUNT; VI_UNLOCK(vp); VOP_UNLOCK(vp, 0); MNT_ILOCK(mp); mp^.mnt_flag:=mp^.mnt_flag and (not MNT_UPDATEMASK); mp^.mnt_flag:=(mp^.mnt_flag or fsflags) and (MNT_RELOAD or MNT_FORCE or MNT_UPDATE or MNT_SNAPSHOT or MNT_ROOTFS or MNT_UPDATEMASK or MNT_RDONLY); if ((mp^.mnt_flag and MNT_ASYNC)=0) then begin mp^.mnt_kern_flag:=mp^.mnt_kern_flag and (not MNTK_ASYNC); end; MNT_IUNLOCK(mp); mp^.mnt_optnew:=optlist^; vfs_mergeopts(mp^.mnt_optnew, mp^.mnt_opt); { * Mount the filesystem. * XXX The final recipients of VFS_MOUNT just overwrite the ndp they * get. No freeing of cn_pnbuf. } error:=vmount.VFS_MOUNT(mp); export_error:=0; if (error=0) then begin { Process the export option. } //if (vfs_copyopt(mp^.mnt_optnew, 'export', @export, sizeof(export))=0) then //begin // export_error:=vfs_export(mp, @export); //end else //if (vfs_copyopt(mp^.mnt_optnew, 'export', @oexport, sizeof(oexport))=0) then //begin // export.ex_flags :=oexport.ex_flags; // export.ex_root :=oexport.ex_root; // export.ex_anon :=oexport.ex_anon; // export.ex_addr :=oexport.ex_addr; // export.ex_addrlen :=oexport.ex_addrlen; // export.ex_mask :=oexport.ex_mask; // export.ex_masklen :=oexport.ex_masklen; // export.ex_indexfile :=oexport.ex_indexfile; // export.ex_numsecflavors:=0; // export_error:=vfs_export(mp, @export); //end; end; MNT_ILOCK(mp); if (error=0) then begin mp^.mnt_flag:=mp^.mnt_flag and (not (MNT_UPDATE or MNT_RELOAD or MNT_FORCE or MNT_SNAPSHOT)); end else begin { * If we fail, restore old mount flags. MNT_QUOTA is special, * because it is not part of MNT_UPDATEMASK, but it could have * changed in the meantime if quotactl(2) was called. * All in all we want current value of MNT_QUOTA, not the old * one. } mp^.mnt_flag:=(mp^.mnt_flag and MNT_QUOTA) or (flag and (not MNT_QUOTA)); end; if ((mp^.mnt_flag and MNT_ASYNC)<>0) and ((mp^.mnt_kern_flag and MNTK_NOASYNC)=0) then mp^.mnt_kern_flag:=mp^.mnt_kern_flag or MNTK_ASYNC else mp^.mnt_kern_flag:=mp^.mnt_kern_flag and (not MNTK_ASYNC); MNT_IUNLOCK(mp); if (error<>0) then begin goto _end; end; if (mp^.mnt_opt<>nil) then begin vfs_freeopts(mp^.mnt_opt); end; mp^.mnt_opt:=mp^.mnt_optnew; optlist^:=nil; VFS_STATFS(mp, @mp^.mnt_stat); { * Prevent external consumers of mount options from reading * mnt_optnew. } mp^.mnt_optnew:=nil; //if ((mp^.mnt_flag and MNT_RDONLY)=0) then // vfs_allocate_syncvnode(mp) //else // vfs_deallocate_syncvnode(mp); _end: vfs_unbusy(mp); VI_LOCK(vp); vp^.v_iflag:=vp^.v_iflag and (not VI_MOUNT); VI_UNLOCK(vp); vrele(vp); if (error<>0) then Exit(error) else Exit(export_error); end; { * vfs_domount(): actually attempt a filesystem mount. } function vfs_domount(fstype:PChar; { Filesystem type. } fspath:PChar; { Mount path. } fsflags:QWORD; { Flags common to all filesystems. } optlist:pp_vfsoptlist { Options local to the filesystem. } ):Integer; var vfsp:p_vfsconf; nd:t_nameidata; vp:p_vnode; pathbuf:t_mname; error:Integer; begin { * Be ultra-paranoid about making sure the type and fspath * variables will fit in our mp buffers, including the * terminating NUL. } if (strlen(fstype) >= MFSNAMELEN) or (strlen(fspath) >= MNAMELEN) then begin Exit(ENAMETOOLONG); end; { Load KLDs before we lock the covered vnode to avoid reversals. } vfsp:=nil; if ((fsflags and MNT_UPDATE)=0) then begin vfsp:=vfs_byname(fstype); if (vfsp=nil) then begin Exit(ENODEV); end; end; { * Get vnode to be covered or mount point's vnode in case of MNT_UPDATE. } NDINIT(@nd, LOOKUP, FOLLOW or LOCKLEAF or MPSAFE or AUDITVNODE1, UIO_SYSSPACE, fspath, curkthread); error:=nd_namei(@nd); if (error<>0) then begin Exit(error); end; if (NDHASGIANT(@nd)=0) then begin mtx_lock(VFS_Giant); end; NDFREE(@nd, NDF_ONLY_PNBUF); vp:=nd.ni_vp; if ((fsflags and MNT_UPDATE)=0) then begin pathbuf:=Default(t_mname); strcopy(@pathbuf, fspath); error:=vn_path_to_global_path(vp, @pathbuf, MNAMELEN); { debug.disablefullpath=1 results in ENODEV } if (error=0) or (error=ENODEV) then begin error:=vfs_domount_first(vfsp, @pathbuf, vp, fsflags, optlist); end; end else begin error:=vfs_domount_update(vp, fsflags, optlist); end; mtx_unlock(VFS_Giant); ASSERT_VI_UNLOCKED (vp, {$I %LINE%}); ASSERT_VOP_UNLOCKED(vp, {$I %LINE%}); Exit(error); end; function vfs_donmount(fsflags:QWORD;fsoptions:p_uio):Integer; label bail; var optlist:p_vfsoptlist; opt,tmp_opt:p_vfsopt; fstype,fspath,errmsg:PChar; error,fstypelen,fspathlen,errmsg_len,errmsg_pos:Integer; begin errmsg:=nil; fspath:=nil; errmsg_len:=0; fspathlen :=0; errmsg_pos:=-1; error:=vfs_buildopts(fsoptions, @optlist); if (error<>0) then begin Exit(error); end; if (vfs_getopt(optlist, 'errmsg', @errmsg, @errmsg_len)=0) then begin errmsg_pos:=vfs_getopt_pos(optlist, 'errmsg'); end; { * We need these two options before the others, * and they are mandatory for any filesystem. * Ensure they are NUL terminated as well. } fstypelen:=0; error:=vfs_getopt(optlist, 'fstype', @fstype, @fstypelen); if (error<>0) or (fstype[fstypelen - 1]<>#0) then begin error:=EINVAL; if (errmsg<>nil) then begin strlcopy(errmsg, 'Invalid fstype', errmsg_len); end; goto bail; end; fspathlen:=0; error:=vfs_getopt(optlist, 'fspath', @fspath, @fspathlen); if (error<>0) or (fspath[fspathlen - 1]<>#0) then begin error:=EINVAL; if (errmsg<>nil) then begin strlcopy(errmsg, 'Invalid fspath', errmsg_len); end; goto bail; end; { * We need to see if we have the 'update' option * before we call vfs_domount(), since vfs_domount() has special * logic based on MNT_UPDATE. This is very important * when we want to update the root filesystem. } opt:=TAILQ_FIRST(optlist); while (opt<>nil) do begin tmp_opt:=TAILQ_NEXT(opt,@opt^.link); // case RawByteString(opt^.name) of 'update': begin fsflags:=fsflags or MNT_UPDATE; vfs_freeopt(optlist, opt); end; 'async': begin fsflags:=fsflags or MNT_ASYNC; end; 'force': begin fsflags:=fsflags or MNT_FORCE; vfs_freeopt(optlist, opt); end; 'reload': begin fsflags:=fsflags or MNT_RELOAD; vfs_freeopt(optlist, opt); end; 'multilabel': begin fsflags:=fsflags or MNT_MULTILABEL; end; 'noasync': begin fsflags:=fsflags and (not MNT_ASYNC) end; 'noatime': begin fsflags:=fsflags or MNT_NOATIME; end; 'atime': begin FreeMem(opt^.name); opt^.name:=strdup('nonoatime'); end; 'noclusterr': begin fsflags:=fsflags or MNT_NOCLUSTERR; end; 'clusterr': begin FreeMem(opt^.name); opt^.name:=strdup('nonoclusterr'); end; 'noclusterw': begin fsflags:=fsflags or MNT_NOCLUSTERW; end; 'clusterw': begin FreeMem(opt^.name); opt^.name:=strdup('nonoclusterw'); end; 'noexec': begin fsflags:=fsflags or MNT_NOEXEC; end; 'exec': begin FreeMem(opt^.name); opt^.name:=strdup('nonoexec'); end; 'nosuid': begin fsflags:=fsflags or MNT_NOSUID; end; 'suid': begin FreeMem(opt^.name); opt^.name:=strdup('nonosuid'); end; 'nosymfollow': begin fsflags:=fsflags or MNT_NOSYMFOLLOW; end; 'symfollow': begin FreeMem(opt^.name); opt^.name:=strdup('nonosymfollow'); end; 'noro': begin fsflags:=fsflags and (not MNT_RDONLY); end; 'rw': begin fsflags:=fsflags and (not MNT_RDONLY); end; 'ro': begin fsflags:=fsflags or MNT_RDONLY; end; 'rdonly': begin FreeMem(opt^.name); opt^.name:=strdup('ro'); fsflags:=fsflags or MNT_RDONLY; end; 'suiddir': begin fsflags:=fsflags or MNT_SUIDDIR; end; 'sync': begin fsflags:=fsflags or MNT_SYNCHRONOUS; end; 'union': begin fsflags:=fsflags or MNT_UNION; end; else; end; // opt:=tmp_opt; end; { * Be ultra-paranoid about making sure the type and fspath * variables will fit in our mp buffers, including the * terminating NUL. } if (fstypelen >= MFSNAMELEN - 1) or (fspathlen >= MNAMELEN - 1) then begin error:=ENAMETOOLONG; goto bail; end; error:=vfs_domount(fstype, fspath, fsflags, @optlist); bail: { copyout the errmsg } if (errmsg_pos<>-1) and ((2 * errmsg_pos + 1) < fsoptions^.uio_iovcnt) and (errmsg_len > 0) and (errmsg<>nil) then begin if (fsoptions^.uio_segflg=UIO_SYSSPACE) then begin Move(errmsg^, fsoptions^.uio_iov[2 * errmsg_pos + 1].iov_base^, fsoptions^.uio_iov[2 * errmsg_pos + 1].iov_len); end else begin copyout(errmsg, fsoptions^.uio_iov[2 * errmsg_pos + 1].iov_base, fsoptions^.uio_iov[2 * errmsg_pos + 1].iov_len); end; end; if (optlist<>nil) then begin vfs_freeopts(optlist); end; Exit(error); end; { * Do the actual filesystem unmount. } function dounmount(mp:p_mount;flags:Integer):Integer; var coveredvp,fsrootvp:p_vnode; error:Integer; async_flag:QWORD; mnt_gen_r:Integer; begin mtx_assert(VFS_Giant); coveredvp:=mp^.mnt_vnodecovered; if (coveredvp<>nil) then begin mnt_gen_r:=mp^.mnt_gen; VI_LOCK(coveredvp); vholdl(coveredvp); vn_lock(coveredvp, LK_EXCLUSIVE or LK_INTERLOCK or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%}); vdrop(coveredvp); { * Check for mp being unmounted while waiting for the * covered vnode lock. } if (coveredvp^.v_mountedhere<>mp) or (p_mount(coveredvp^.v_mountedhere)^.mnt_gen<>mnt_gen_r) then begin VOP_UNLOCK(coveredvp, 0); Exit(EBUSY); end; end; { * Only privileged root, or (if MNT_USER is set) the user that did the * original mount is permitted to unmount this filesystem. } error:=0; //error:=vfs_suser(mp, td); if (error<>0) then begin if (coveredvp<>nil) then begin VOP_UNLOCK(coveredvp, 0); end; Exit(error); end; vn_start_write(nil, @mp, V_WAIT); MNT_ILOCK(mp); if ((mp^.mnt_kern_flag and MNTK_UNMOUNT)<>0) or (not TAILQ_EMPTY(@mp^.mnt_uppers)) then begin MNT_IUNLOCK(mp); if (coveredvp<>nil) then begin VOP_UNLOCK(coveredvp, 0); end; vn_finished_write(mp); Exit(EBUSY); end; mp^.mnt_kern_flag:=mp^.mnt_kern_flag or MNTK_UNMOUNT or MNTK_NOINSMNTQ; { Allow filesystems to detect that a forced unmount is in progress. } if ((flags and MNT_FORCE)<>0) then begin mp^.mnt_kern_flag:=mp^.mnt_kern_flag or MNTK_UNMOUNTF; end; error:=0; if (mp^.mnt_lockref<>0) then begin mp^.mnt_kern_flag:=mp^.mnt_kern_flag or MNTK_DRAINING; error:=msleep(@mp^.mnt_lockref, MNT_MTX(mp), PVFS, 'mount drain', 0); end; MNT_IUNLOCK(mp); Assert(mp^.mnt_lockref=0,'%s: invalid lock refcount in the drain path @ %s:%d'); Assert(error=0,'%s: invalid Exitvalue for msleep in the drain path @ %s:%d'); //if (mp^.mnt_flag and MNT_EXPUBLIC) then // vfs_setpublicfs(nil, nil, nil); vfs_msync(mp, MNT_WAIT); MNT_ILOCK(mp); async_flag:=mp^.mnt_flag and MNT_ASYNC; mp^.mnt_flag :=mp^.mnt_flag and (not MNT_ASYNC); mp^.mnt_kern_flag:=mp^.mnt_kern_flag and (not MNTK_ASYNC); MNT_IUNLOCK(mp); //cache_purgevfs(mp); { remove cache entries for this file sys } //vfs_deallocate_syncvnode(mp); { * For forced unmounts, move process cdir/rdir refs on the fs root * vnode to the covered vnode. For non-forced unmounts we want * such references to cause an EBUSY error. } if ((flags and MNT_FORCE)<>0) and (VFS_ROOT(mp, LK_EXCLUSIVE, @fsrootvp)=0) then begin //if (mp^.mnt_vnodecovered<>nil) and // ((mp^.mnt_flag and MNT_IGNORE)=0) then // mountcheckdirs(fsrootvp, mp^.mnt_vnodecovered); if (fsrootvp=rootvnode) then begin vrele(rootvnode); rootvnode:=nil; end; vput(fsrootvp); end; error:=VFS_SYNC(mp, MNT_WAIT); if ((mp^.mnt_flag and MNT_RDONLY)<>0) or (error=0) or ((flags and MNT_FORCE)<>0) then begin error:=VFS_UNMOUNT(mp, flags); end; vn_finished_write(mp); { * If we failed to flush the dirty blocks for this mount point, * undo all the cdir/rdir and rootvnode changes we made above. * Unless we failed to do so because the device is reporting that * it doesn't exist anymore. } if (error<>0) and (error<>ENXIO) then begin if ((flags and MNT_FORCE)<>0) and (VFS_ROOT(mp, LK_EXCLUSIVE, @fsrootvp)=0) then begin //if (mp^.mnt_vnodecovered<>nil) and // ((mp^.mnt_flag and MNT_IGNORE)=0) then // mountcheckdirs(mp^.mnt_vnodecovered, fsrootvp); if (rootvnode=nil) then begin rootvnode:=fsrootvp; vref(rootvnode); end; vput(fsrootvp); end; MNT_ILOCK(mp); mp^.mnt_kern_flag:=mp^.mnt_kern_flag and (not MNTK_NOINSMNTQ); if ((mp^.mnt_flag and MNT_RDONLY)=0) then begin //MNT_IUNLOCK(mp); //vfs_allocate_syncvnode(mp); //MNT_ILOCK(mp); end; mp^.mnt_kern_flag:=mp^.mnt_kern_flag and (not (MNTK_UNMOUNT or MNTK_UNMOUNTF)); mp^.mnt_flag:=mp^.mnt_flag or async_flag; if ((mp^.mnt_flag and MNT_ASYNC)<>0) and ((mp^.mnt_kern_flag and MNTK_NOASYNC)=0) then begin mp^.mnt_kern_flag:=mp^.mnt_kern_flag or MNTK_ASYNC; end; if ((mp^.mnt_kern_flag and MNTK_MWAIT)<>0) then begin mp^.mnt_kern_flag:=mp^.mnt_kern_flag and (not MNTK_MWAIT); wakeup(mp); end; MNT_IUNLOCK(mp); if (coveredvp<>nil) then begin VOP_UNLOCK(coveredvp, 0); end; Exit(error); end; mtx_lock(mountlist_mtx); TAILQ_REMOVE(@mountlist,mp,@mp^.mnt_list); mtx_unlock(mountlist_mtx); if (coveredvp<>nil) then begin coveredvp^.v_mountedhere:=nil; vput(coveredvp); end; vfs_event_signal(nil, VQ_UNMOUNT, 0); vfs_mount_destroy(mp); Exit(0); end; { * Add a boolean argument. * * flag is the boolean value. * name must start with 'no'. } function mount_argb(ma:p_mntarg;flag:Integer;name:PChar):p_mntarg; begin Assert((name[0]='n') and (name[1]='o'),'mount_argb(...,%s): name must start with no'); Exit(mount_arg(ma, name + (ord(flag<>0)*2), nil, 0)); end; { * Add an argument printf style } function mount_argf(ma:p_mntarg;name,fmt:PChar;const Args:Array of const):p_mntarg; register; var maa:p_mntaarg; len:Integer; sb:RawByteString; begin if (ma=nil) then begin ma:=AllocMem(sizeof(t_mntarg)); SLIST_INIT(@ma^.list); end; if (ma^.error<>0) then begin Exit(ma); end; ma^.v:=ReAllocMem(ma^.v, sizeof(iovec) * (ma^.len + 2)); ma^.v[ma^.len].iov_base:=name; ma^.v[ma^.len].iov_len :=strlen(name) + 1; Inc(ma^.len); sb:=Format(fmt,Args); len:=Length(sb) + 1; maa:=AllocMem(sizeof(t_mntaarg) + len); SLIST_INSERT_HEAD(@ma^.list,maa,@maa^.next); Move(PChar(sb)^, (maa + 1)^, len); ma^.v[ma^.len].iov_base:=maa + 1; ma^.v[ma^.len].iov_len :=len; Inc(ma^.len); Exit(ma); end; { * Add an argument which is a userland string. } function mount_argsu(ma:p_mntarg;name:PChar;val:Pointer;len:Integer):p_mntarg; var maa:p_mntaarg; tbuf:Pointer; begin if (val=nil) then begin Exit(ma); end; if (ma=nil) then begin ma:=AllocMem(sizeof(t_mntarg)); SLIST_INIT(@ma^.list); end; if (ma^.error<>0) then begin Exit(ma); end; maa:=AllocMem(sizeof(t_mntaarg) + len); SLIST_INSERT_HEAD(@ma^.list,maa,@maa^.next); tbuf:=Pointer(maa + 1); ma^.error:=copyinstr(val, tbuf, len, nil); Exit(mount_arg(ma, name, tbuf, -1)); end; { * Plain argument. * * If length is -1, treat value as a C string. } function mount_arg(ma:p_mntarg;name:PChar;val:Pointer;len:Integer):p_mntarg; begin if (ma=nil) then begin ma:=AllocMem(sizeof(t_mntarg)); SLIST_INIT(@ma^.list); end; if (ma^.error<>0) then begin Exit(ma); end; if (val=nil) or (len=0) then Exit(ma); ma^.v:=ReAllocMem(ma^.v, sizeof(iovec) * (ma^.len + 2)); ma^.v[ma^.len].iov_base:=name; ma^.v[ma^.len].iov_len :=strlen(name) + 1; Inc(ma^.len); ma^.v[ma^.len].iov_base:=val; if (len < 0) then ma^.v[ma^.len].iov_len:=strlen(val) + 1 else ma^.v[ma^.len].iov_len:=len; Inc(ma^.len); Exit(ma); end; { * Free a mntarg structure } procedure free_mntarg(ma:p_mntarg); var maa:p_mntaarg; begin while (not SLIST_EMPTY(@ma^.list)) do begin maa:=SLIST_FIRST(@ma^.list); SLIST_REMOVE_HEAD(@ma^.list,@p_mntaarg(nil)^.next); FreeMem(maa); end; FreeMem(ma^.v); FreeMem(ma); end; { * Mount a filesystem } function kernel_nmount(ma:p_mntarg;flags:QWORD):Integer; var auio:t_uio; error:Integer; begin Assert(ma<>nil, 'kernel_mount nil ma'); Assert(ma^.v<>nil, 'kernel_mount nil ma^.v'); Assert((ma^.len and 1)=0, 'kernel_mount odd ma^.len (%d)'); auio.uio_iov :=ma^.v; auio.uio_iovcnt:=ma^.len; auio.uio_offset:=0; auio.uio_resid :=0; auio.uio_segflg:=UIO_SYSSPACE; auio.uio_rw :=UIO_READ; auio.uio_td :=nil; error:=ma^.error; if (error=0) then begin error:=vfs_donmount(flags, @auio); end; free_mntarg(ma); Exit(error); end; function kern_unmount(path:PChar;flags:Integer):Integer; var nd:t_nameidata; mp:p_mount; pathbuf:t_mname; error,vfslocked:Integer; begin pathbuf:=Default(t_mname); strlcopy(@pathbuf,path,MNAMELEN); mtx_lock(VFS_Giant); { * Try to find global path for path argument. } NDINIT(@nd, LOOKUP, FOLLOW or LOCKLEAF or MPSAFE or AUDITVNODE1, UIO_SYSSPACE, path, curkthread); if (nd_namei(@nd)=0) then begin vfslocked:=NDHASGIANT(@nd); NDFREE(@nd, NDF_ONLY_PNBUF); error:=vn_path_to_global_path(nd.ni_vp,@pathbuf,MNAMELEN); if (error=0) or (error=ENODEV) then begin vput(nd.ni_vp); end; VFS_UNLOCK_GIANT(vfslocked); end; mtx_lock(mountlist_mtx); mp:=TAILQ_LAST(@mountlist); while (mp<>nil) do begin if (strcomp(@mp^.mnt_stat.f_mntonname, @pathbuf)=0) then begin break; end; mp:=TAILQ_PREV(mp,@mp^.mnt_list); end; mtx_unlock(mountlist_mtx); if (mp=nil) then begin mtx_unlock(VFS_Giant); Exit(EINVAL); end; error:=dounmount(mp, flags); mtx_unlock(VFS_Giant); Exit(error); end; /// { * Mount a filesystem. } function sys_nmount(iovp:Pointer;iovcnt:DWORD;flags:QWORD):Integer; var auio:p_uio; error:Integer; begin //priv_check(td,683); Exit(EPERM); { * Filter out MNT_ROOTFS. We do not want clients of nmount() in * userspace to set this flag, but we must filter it out if we want * MNT_UPDATE on the root file system to work. * MNT_ROOTFS should only be set by the kernel when mounting its * root file system. } flags:=flags and (not MNT_ROOTFS); { * Check that we have an even number of iovec's * and that we have at least two options. } if ((iovcnt and 1)<>0) or (iovcnt < 4) then begin Exit(EINVAL); end; error:=copyinuio(iovp, iovcnt, @auio); if (error<>0) then begin Exit(error); end; error:=vfs_donmount(flags, auio); FreeMem(auio); Exit(error); end; { * Old mount API. } function sys_mount(ftype,fpath:PChar;flags:QWORD;data:Pointer):Integer; var fstype:PChar; vfsp:p_vfsconf; ma:p_mntarg; error:Integer; begin //priv_check(td,683); Exit(EPERM); vfsp:=nil; ma:=nil; { * Filter out MNT_ROOTFS. We do not want clients of mount() in * userspace to set this flag, but we must filter it out if we want * MNT_UPDATE on the root file system to work. * MNT_ROOTFS should only be set by the kernel when mounting its * root file system. } flags:=flags and (not MNT_ROOTFS); fstype:=AllocMem(MFSNAMELEN); error:=copyinstr(ftype, fstype, MFSNAMELEN, nil); if (error<>0) then begin FreeMem(fstype); Exit(error); end; mtx_lock(VFS_Giant); vfsp:=vfs_byname_kld(fstype, @error); FreeMem(fstype); if (vfsp=nil) then begin mtx_unlock(VFS_Giant); Exit(ENOENT); end; if (vfsp^.vfc_vfsops^.vfs_cmount=nil) then begin mtx_unlock(VFS_Giant); Exit(EOPNOTSUPP); end; ma:=mount_argsu(ma, 'fstype', ftype, MFSNAMELEN); ma:=mount_argsu(ma, 'fspath', fpath, MNAMELEN); ma:=mount_argb (ma, flags and MNT_RDONLY, 'noro'); ma:=mount_argb (ma, ord((flags and MNT_NOSUID)=0), 'nosuid'); ma:=mount_argb (ma, ord((flags and MNT_NOEXEC)=0), 'noexec'); error:=vfsp^.vfc_vfsops^.vfs_cmount(ma, data, flags); mtx_unlock(VFS_Giant); Exit(error); end; { * Unmount a filesystem. * * Note: unmount takes a path to the vnode mounted on as argument, not * special file (as before). } function sys_unmount(path:PChar;flags:Integer):Integer; var nd:t_nameidata; mp:p_mount; pathbuf:t_mname; error,id0,id1,vfslocked:Integer; begin //priv_check(td,683); Exit(EPERM); //if (jailed(td^.td_ucred)) or (usermount=0) then //begin // error:=priv_check(td, PRIV_VFS_UNMOUNT); // if (error<>0) then // Exit(error); //end; pathbuf:=Default(t_mname); error:=copyinstr(path, @pathbuf, MNAMELEN, nil); if (error<>0) then begin Exit(error); end; mtx_lock(VFS_Giant); if ((flags and MNT_BYFSID)<>0) then begin { Decode the filesystem ID. } if (sscanf(pathbuf, 'FSID:%d:%d', [@id0, @id1])<>2) then begin mtx_unlock(VFS_Giant); Exit(EINVAL); end; mtx_lock(mountlist_mtx); mp:=TAILQ_LAST(@mountlist); while (mp<>nil) do begin if (mp^.mnt_stat.f_fsid.val[0]=id0) and (mp^.mnt_stat.f_fsid.val[1]=id1) then begin break; end; mp:=TAILQ_PREV(mp,@mp^.mnt_list); end; mtx_unlock(mountlist_mtx); end else begin { * Try to find global path for path argument. } NDINIT(@nd, LOOKUP, FOLLOW or LOCKLEAF or MPSAFE or AUDITVNODE1, UIO_SYSSPACE, @pathbuf, curkthread); if (nd_namei(@nd)=0) then begin vfslocked:=NDHASGIANT(@nd); NDFREE(@nd, NDF_ONLY_PNBUF); error:=vn_path_to_global_path(nd.ni_vp,@pathbuf,MNAMELEN); if (error=0) or (error=ENODEV) then begin vput(nd.ni_vp); end; VFS_UNLOCK_GIANT(vfslocked); end; mtx_lock(mountlist_mtx); mp:=TAILQ_LAST(@mountlist); while (mp<>nil) do begin if (strcomp(@mp^.mnt_stat.f_mntonname, @pathbuf)=0) then begin break; end; mp:=TAILQ_PREV(mp,@mp^.mnt_list); end; mtx_unlock(mountlist_mtx); end; if (mp=nil) then begin { * Previously we returned ENOENT for a nonexistent path and * EINVAL for a non-mountpoint. We cannot tell these apart * now, so in the !MNT_BYFSID case return the more likely * EINVAL for compatibility. } mtx_unlock(VFS_Giant); if ((flags and MNT_BYFSID)<>0) then Exit(ENOENT) else Exit(EINVAL); end; { * Don't allow unmounting the root filesystem. } if ((mp^.mnt_flag and MNT_ROOTFS)<>0) then begin mtx_unlock(VFS_Giant); Exit(EINVAL); end; error:=dounmount(mp, flags); mtx_unlock(VFS_Giant); Exit(error); end; end.