mirror of https://github.com/red-prig/fpPS4.git
2118 lines
45 KiB
Plaintext
2118 lines
45 KiB
Plaintext
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.
|
|
|