mirror of https://github.com/red-prig/fpPS4.git
1090 lines
21 KiB
Plaintext
1090 lines
21 KiB
Plaintext
unit ufs_vnops;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
mqueue,
|
|
kern_param,
|
|
time,
|
|
vfile,
|
|
vmount,
|
|
vdirent,
|
|
vuio,
|
|
vnode,
|
|
vnamei,
|
|
vnode_if,
|
|
vfs_default,
|
|
vfs_vnops,
|
|
ufs,
|
|
kern_mtx,
|
|
kern_sx;
|
|
|
|
function ufs_vmkdir(dmp:p_ufs_mount;name:PChar;namelen:Integer;dotdot:p_ufs_dirent;inode:DWORD):p_ufs_dirent;
|
|
procedure ufs_purge(dm:p_ufs_mount;dd:p_ufs_dirent);
|
|
|
|
function ufs_lookup(ap:p_vop_lookup_args):Integer;
|
|
function ufs_access(ap:p_vop_access_args):Integer;
|
|
function ufs_getattr(ap:p_vop_getattr_args):Integer;
|
|
function ufs_setattr(ap:p_vop_setattr_args):Integer;
|
|
function ufs_readdir(ap:p_vop_readdir_args):Integer;
|
|
function ufs_rread(ap:p_vop_read_args):Integer;
|
|
function ufs_readlink(ap:p_vop_readlink_args):Integer;
|
|
function ufs_symlink(ap:p_vop_symlink_args):Integer;
|
|
function ufs_remove(ap:p_vop_remove_args):Integer;
|
|
function ufs_rmdir(ap:p_vop_rmdir_args):Integer;
|
|
function ufs_mkdir(ap:p_vop_mkdir_args):Integer;
|
|
function ufs_reclaim(ap:p_vop_reclaim_args):Integer;
|
|
|
|
const
|
|
ufs_vnodeops_root:vop_vector=(
|
|
vop_default :@default_vnodeops;
|
|
vop_bypass :nil;
|
|
|
|
vop_islocked :nil;
|
|
vop_lookup :@ufs_lookup;
|
|
vop_create :nil;
|
|
vop_whiteout :nil;
|
|
vop_mknod :nil;
|
|
vop_open :nil;
|
|
vop_close :nil;
|
|
vop_access :@ufs_access;
|
|
vop_accessx :nil;
|
|
vop_getattr :@ufs_getattr;
|
|
vop_setattr :@ufs_setattr;
|
|
vop_markatime :nil;
|
|
vop_read :@ufs_rread;
|
|
vop_write :nil;
|
|
vop_ioctl :nil;
|
|
vop_poll :nil;
|
|
vop_kqfilter :nil;
|
|
vop_revoke :nil;
|
|
vop_fsync :nil;
|
|
vop_remove :@ufs_remove;
|
|
vop_link :nil;
|
|
vop_rename :nil;
|
|
vop_mkdir :@ufs_mkdir;
|
|
vop_rmdir :@ufs_rmdir;
|
|
vop_symlink :@ufs_symlink;
|
|
vop_readdir :@ufs_readdir;
|
|
vop_readlink :@ufs_readlink;
|
|
vop_inactive :nil;
|
|
vop_reclaim :@ufs_reclaim;
|
|
vop_lock1 :nil;
|
|
vop_unlock :nil;
|
|
vop_bmap :nil;
|
|
vop_strategy :nil;
|
|
vop_getwritemount :nil;
|
|
vop_print :nil;
|
|
vop_pathconf :@vop_stdpathconf;
|
|
vop_advlock :nil;
|
|
vop_advlockasync :nil;
|
|
vop_advlockpurge :nil;
|
|
vop_reallocblks :nil;
|
|
vop_getpages :nil;
|
|
vop_putpages :nil;
|
|
vop_vptofh :nil;
|
|
vop_vptocnp :nil;
|
|
vop_allocate :nil;
|
|
vop_unp_bind :nil;
|
|
vop_unp_connect :nil;
|
|
vop_unp_detach :nil;
|
|
);
|
|
|
|
implementation
|
|
|
|
uses
|
|
sysutils,
|
|
errno,
|
|
kern_thr,
|
|
kern_proc,
|
|
vfs_subr,
|
|
subr_uio,
|
|
vnode_pager;
|
|
|
|
const
|
|
UFS_DEL_VNLOCKED =$01;
|
|
UFS_DEL_NORECURSE=$02;
|
|
|
|
function VFSTOUFS(mp:p_mount):p_ufs_mount; inline;
|
|
begin
|
|
Result:=mp^.mnt_data;
|
|
end;
|
|
|
|
function ufs_parent_dirent(de:p_ufs_dirent):p_ufs_dirent; inline;
|
|
begin
|
|
Exit(de^.ufs_dir);
|
|
end;
|
|
|
|
function ufs_newdirent(name:PChar;namelen:Integer):p_ufs_dirent;
|
|
var
|
|
i:Integer;
|
|
de:p_ufs_dirent;
|
|
d:t_dirent;
|
|
begin
|
|
d.d_namlen:=namelen;
|
|
i:=sizeof(t_ufs_dirent) + GENERIC_DIRSIZ(@d);
|
|
de:=AllocMem(i);
|
|
de^.ufs_dirent:=p_dirent(de + 1);
|
|
de^.ufs_dirent^.d_namlen:=namelen;
|
|
de^.ufs_dirent^.d_reclen:=GENERIC_DIRSIZ(@d);
|
|
|
|
Move(name^, de^.ufs_dirent^.d_name, namelen);
|
|
|
|
de^.ufs_dirent^.d_name[namelen]:=#0;
|
|
|
|
vfs_timestamp(@de^.ufs_ctime);
|
|
de^.ufs_mtime :=de^.ufs_ctime;
|
|
de^.ufs_atime :=de^.ufs_ctime;
|
|
de^.ufs_btime :=de^.ufs_ctime;
|
|
|
|
de^.ufs_links :=1;
|
|
de^.ufs_ref :=1;
|
|
|
|
TAILQ_INIT(@de^.ufs_dlist);
|
|
|
|
Exit(de);
|
|
end;
|
|
|
|
function _ufs_rmdir(dm:p_ufs_mount;de:p_ufs_dirent):Integer; forward;
|
|
|
|
{
|
|
* The caller needs to hold the dm for the duration of the call since
|
|
* dm^.dm_lock may be temporary dropped.
|
|
}
|
|
procedure ufs_delete(dm:p_ufs_mount;de:p_ufs_dirent;flags:Integer);
|
|
var
|
|
dd,dd2:p_ufs_dirent;
|
|
vp:p_vnode;
|
|
begin
|
|
if (dm=nil) or (de=nil) then Exit;
|
|
|
|
Assert((de^.ufs_flags and UFS_DOOMED)=0,'ufs_delete doomed dirent');
|
|
de^.ufs_flags:=de^.ufs_flags or UFS_DOOMED;
|
|
|
|
if ((flags and UFS_DEL_NORECURSE)=0) then
|
|
begin
|
|
dd:=ufs_parent_dirent(de);
|
|
end else
|
|
begin
|
|
dd:=nil;
|
|
end;
|
|
|
|
if (dd<>nil) then
|
|
begin
|
|
ufs_de_hold(dd);
|
|
end;
|
|
|
|
mtx_lock(ufs_interlock);
|
|
vp:=de^.ufs_vnode;
|
|
if (vp<>nil) then
|
|
begin
|
|
VI_LOCK(vp);
|
|
mtx_unlock(ufs_interlock);
|
|
vholdl(vp);
|
|
|
|
sx_unlock(@dm^.ufs_lock);
|
|
|
|
if ((flags and UFS_DEL_VNLOCKED)=0) then
|
|
vn_lock(vp, LK_EXCLUSIVE or LK_INTERLOCK or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%})
|
|
else
|
|
VI_UNLOCK(vp);
|
|
|
|
vgone(vp);
|
|
|
|
if ((flags and UFS_DEL_VNLOCKED)=0) then
|
|
begin
|
|
VOP_UNLOCK(vp, 0);
|
|
end;
|
|
|
|
vdrop(vp);
|
|
sx_xlock(@dm^.ufs_lock);
|
|
end else
|
|
begin
|
|
mtx_unlock(ufs_interlock);
|
|
end;
|
|
|
|
if (de^.ufs_symlink<>nil) then
|
|
begin
|
|
FreeMem(de^.ufs_symlink);
|
|
de^.ufs_symlink:=nil;
|
|
end;
|
|
|
|
if (de^.ufs_inode > UFS_ROOTINO) then
|
|
begin
|
|
ufs_free_cdp_inode(de^.ufs_inode);
|
|
de^.ufs_inode:=0;
|
|
end;
|
|
|
|
dd2:=de^.ufs_dir;
|
|
if (dd2<>nil) then
|
|
begin
|
|
de^.ufs_dir:=nil;
|
|
Dec(dd2^.ufs_links);
|
|
TAILQ_REMOVE(@dd2^.ufs_dlist,de,@de^.ufs_list);
|
|
end;
|
|
|
|
if (dd<>nil) then
|
|
begin
|
|
if ufs_de_drop(dd) then
|
|
begin
|
|
//
|
|
end else
|
|
if ((flags and UFS_DEL_NORECURSE)=0) then
|
|
begin
|
|
_ufs_rmdir(dm, de);
|
|
end;
|
|
end;
|
|
|
|
ufs_de_drop(de);
|
|
end;
|
|
|
|
function _ufs_dir_status(dm:p_ufs_mount;de:p_ufs_dirent):Integer;
|
|
var
|
|
de_dot,de_dotdot:p_ufs_dirent;
|
|
begin
|
|
Result:=0;
|
|
if (dm=nil) or (de=nil) then Exit;
|
|
|
|
sx_assert(@dm^.ufs_lock);
|
|
|
|
if (de^.ufs_dirent^.d_type<>DT_DIR) then Exit(ENOTDIR);
|
|
|
|
if ((de^.ufs_flags and UFS_DOOMED)<>0) or (de=dm^.ufs_rootdir) then
|
|
begin
|
|
Exit(EBUSY);
|
|
end;
|
|
|
|
de_dot:=TAILQ_FIRST(@de^.ufs_dlist);
|
|
Assert(de_dot<>nil, 'ufs_rmdir: . missing');
|
|
|
|
de_dotdot:=TAILQ_NEXT(de_dot,@de_dot^.ufs_list);
|
|
Assert(de_dotdot<>nil, 'ufs_rmdir: .. missing');
|
|
|
|
{ Exit if the directory is not empty. }
|
|
if (TAILQ_NEXT(de_dotdot,@de_dotdot^.ufs_list)<>nil) then
|
|
begin
|
|
Exit(ENOTEMPTY);
|
|
end;
|
|
end;
|
|
|
|
function _ufs_rmdir(dm:p_ufs_mount;de:p_ufs_dirent):Integer;
|
|
label
|
|
next;
|
|
var
|
|
dd,de_dot,de_dotdot:p_ufs_dirent;
|
|
begin
|
|
Result:=0;
|
|
if (dm=nil) or (de=nil) then Exit;
|
|
|
|
sx_assert(@dm^.ufs_lock);
|
|
|
|
Assert(de^.ufs_dirent^.d_type=DT_DIR,'ufs_rmdir: de is not a directory');
|
|
|
|
if ((de^.ufs_flags and UFS_DOOMED)<>0) or (de=dm^.ufs_rootdir) then
|
|
begin
|
|
Exit(EBUSY);
|
|
end;
|
|
|
|
dd :=nil;
|
|
de_dot :=nil;
|
|
de_dotdot:=nil;
|
|
|
|
de_dot:=TAILQ_FIRST(@de^.ufs_dlist);
|
|
if (de_dot=nil) then
|
|
begin
|
|
goto next;
|
|
end;
|
|
|
|
de_dotdot:=TAILQ_NEXT(de_dot,@de_dot^.ufs_list);
|
|
if (de_dotdot=nil) then
|
|
begin
|
|
goto next;
|
|
end;
|
|
|
|
{ Exit if the directory is not empty. }
|
|
if (TAILQ_NEXT(de_dotdot,@de_dotdot^.ufs_list)<>nil) then
|
|
begin
|
|
Exit(ENOTEMPTY);
|
|
end;
|
|
|
|
dd:=ufs_parent_dirent(de);
|
|
|
|
next:
|
|
|
|
Assert(de_dot^.ufs_dir=de);
|
|
Assert(de_dotdot^.ufs_dir=de);
|
|
Assert(de^.ufs_dir=dd);
|
|
|
|
ufs_de_hold(dd);
|
|
ufs_delete(dm, de_dot ,UFS_DEL_NORECURSE);
|
|
ufs_delete(dm, de_dotdot,UFS_DEL_NORECURSE);
|
|
ufs_delete(dm, de ,UFS_DEL_NORECURSE);
|
|
ufs_de_drop(dd);
|
|
end;
|
|
|
|
procedure ufs_purge(dm:p_ufs_mount;dd:p_ufs_dirent);
|
|
var
|
|
de:p_ufs_dirent;
|
|
begin
|
|
if (dm=nil) or (dd=nil) then Exit;
|
|
|
|
sx_assert(@dm^.ufs_lock);
|
|
|
|
ufs_de_hold(dd);
|
|
|
|
repeat
|
|
de:=TAILQ_LAST(@dd^.ufs_dlist);
|
|
if (de=nil) then break;
|
|
|
|
if ((de^.ufs_flags and (UFS_DOT or UFS_DOTDOT))<>0) then
|
|
ufs_delete(dm, de, UFS_DEL_NORECURSE)
|
|
else
|
|
if (de^.ufs_dirent^.d_type=DT_DIR) then
|
|
ufs_purge(dm, de)
|
|
else
|
|
ufs_delete(dm, de, UFS_DEL_NORECURSE);
|
|
|
|
until false;
|
|
|
|
if ((dd^.ufs_flags and UFS_DOOMED)=0) then
|
|
ufs_delete(dm, dd, UFS_DEL_NORECURSE);
|
|
|
|
ufs_de_drop(dd);
|
|
end;
|
|
|
|
function ufs_vmkdir(dmp:p_ufs_mount;name:PChar;namelen:Integer;dotdot:p_ufs_dirent;inode:DWORD):p_ufs_dirent;
|
|
var
|
|
nd,de:p_ufs_dirent;
|
|
begin
|
|
{ Create the new directory }
|
|
nd:=ufs_newdirent(name, namelen);
|
|
|
|
nd^.ufs_dirent^.d_type:=DT_DIR;
|
|
nd^.ufs_mode :=UFS_DEFAULT_MODE;
|
|
nd^.ufs_links:=2;
|
|
nd^.ufs_dir :=dotdot;
|
|
|
|
if (inode<>0) then
|
|
nd^.ufs_inode:=inode
|
|
else
|
|
nd^.ufs_inode:=ufs_alloc_cdp_inode;
|
|
|
|
if (dotdot=nil) then
|
|
begin
|
|
nd^.ufs_flags:=UFS_DROOT;
|
|
end else
|
|
begin
|
|
{
|
|
* '.' and '..' are always the two first entries in the
|
|
* de_dlist list.
|
|
*
|
|
* Create the '.' entry in the new directory.
|
|
}
|
|
de:=ufs_newdirent('.', 1);
|
|
de^.ufs_dirent^.d_type:=DT_DIR;
|
|
de^.ufs_flags:=de^.ufs_flags or UFS_DOT;
|
|
TAILQ_INSERT_TAIL(@nd^.ufs_dlist,de,@de^.ufs_list);
|
|
de^.ufs_dir:=nd;
|
|
|
|
{ Create the '..' entry in the new directory. }
|
|
de:=ufs_newdirent('..', 2);
|
|
de^.ufs_dirent^.d_type:=DT_DIR;
|
|
de^.ufs_flags:=de^.ufs_flags or UFS_DOTDOT;
|
|
TAILQ_INSERT_TAIL(@nd^.ufs_dlist,de,@de^.ufs_list);
|
|
de^.ufs_dir:=nd;
|
|
|
|
sx_assert(@dmp^.ufs_lock);
|
|
TAILQ_INSERT_TAIL(@dotdot^.ufs_dlist,nd,@nd^.ufs_list);
|
|
Inc(dotdot^.ufs_links);
|
|
end;
|
|
|
|
Exit(nd);
|
|
end;
|
|
|
|
function ufs_find(dd:p_ufs_dirent;name:PChar;namelen:Integer;_type:Integer):p_ufs_dirent;
|
|
var
|
|
de:p_ufs_dirent;
|
|
begin
|
|
de:=TAILQ_FIRST(@dd^.ufs_dlist);
|
|
while (de<>nil) do
|
|
begin
|
|
if (namelen<>de^.ufs_dirent^.d_namlen) then
|
|
begin
|
|
de:=TAILQ_NEXT(de,@de^.ufs_list);
|
|
continue;
|
|
end;
|
|
if (_type<>0) and (_type<>de^.ufs_dirent^.d_type) then
|
|
begin
|
|
de:=TAILQ_NEXT(de,@de^.ufs_list);
|
|
continue;
|
|
end;
|
|
|
|
if (CompareByte(name^, de^.ufs_dirent^.d_name, namelen)<>0) then
|
|
begin
|
|
de:=TAILQ_NEXT(de,@de^.ufs_list);
|
|
continue;
|
|
end;
|
|
break;
|
|
end;
|
|
|
|
Exit(de);
|
|
end;
|
|
|
|
function ufs_lookupx(ap:p_vop_lookup_args;dm_unlock:PBoolean):Integer;
|
|
var
|
|
cnp:p_componentname;
|
|
dvp:p_vnode;
|
|
vpp:pp_vnode;
|
|
de,dd:p_ufs_dirent;
|
|
error,flags,nameiop,dvplocked:Integer;
|
|
pname:PChar;
|
|
begin
|
|
cnp:=ap^.a_cnp;
|
|
vpp:=ap^.a_vpp;
|
|
dvp:=ap^.a_dvp;
|
|
pname:=cnp^.cn_nameptr;
|
|
flags:=cnp^.cn_flags;
|
|
nameiop:=cnp^.cn_nameiop;
|
|
dd:=dvp^.v_data;
|
|
vpp^:=nil;
|
|
|
|
if ((flags and ISLASTCN)<>0) and (nameiop=RENAME) then
|
|
begin
|
|
Exit(EOPNOTSUPP);
|
|
end;
|
|
|
|
if (dvp^.v_type<>VDIR) then
|
|
begin
|
|
Exit(ENOTDIR);
|
|
end;
|
|
|
|
//If read-only and op is not CREATE|LOOKUP, will return EROFS.
|
|
if ((flags and ISLASTCN)<>0) and
|
|
((p_mount(dvp^.v_mount)^.mnt_flag and MNT_RDONLY)<>0) and
|
|
(nameiop <> CREATE) and
|
|
(nameiop <> LOOKUP) then
|
|
begin
|
|
Exit(EROFS);
|
|
end;
|
|
|
|
if (((flags and ISDOTDOT)<>0) and ((dvp^.v_vflag and VV_ROOT)<>0)) then
|
|
begin
|
|
Exit(EIO);
|
|
end;
|
|
|
|
error:=VOP_ACCESS(dvp, VEXEC);
|
|
if (error<>0) then
|
|
begin
|
|
Exit(error);
|
|
end;
|
|
|
|
if (cnp^.cn_namelen=1) and (pname^='.') then
|
|
begin
|
|
if ((flags and ISLASTCN)<>0) and (nameiop<>LOOKUP) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
vpp^:=dvp;
|
|
VREF(dvp);
|
|
|
|
Exit(0);
|
|
end;
|
|
|
|
if ((flags and ISDOTDOT)<>0) then
|
|
begin
|
|
if ((flags and ISLASTCN)<>0) and (nameiop<>LOOKUP) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
de:=ufs_parent_dirent(dd);
|
|
|
|
if (de=nil) then Exit(ENOENT);
|
|
|
|
dvplocked:=VOP_ISLOCKED(dvp);
|
|
VOP_UNLOCK(dvp, 0);
|
|
|
|
error:=ufs_allocv(de, dvp^.v_mount, cnp^.cn_lkflags and LK_TYPE_MASK, vpp); //sx_xunlock
|
|
dm_unlock^:=false;
|
|
|
|
vn_lock(dvp, dvplocked or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
|
|
Exit(error);
|
|
end;
|
|
|
|
de:=ufs_find(dd, cnp^.cn_nameptr, cnp^.cn_namelen, 0);
|
|
|
|
if (de=nil) then
|
|
begin
|
|
//not found
|
|
|
|
Case nameiop of
|
|
CREATE,
|
|
RENAME:
|
|
begin
|
|
|
|
//If read-only and op is CREATE|RENAME, will return EROFS.
|
|
if ((flags and ISLASTCN)<>0) and
|
|
((p_mount(dvp^.v_mount)^.mnt_flag and MNT_RDONLY)<>0) then
|
|
begin
|
|
Exit(EROFS);
|
|
end;
|
|
|
|
if ((flags and (LOCKPARENT or WANTPARENT))<>0) and
|
|
((flags and ISLASTCN)<>0) then
|
|
begin
|
|
cnp^.cn_flags:=cnp^.cn_flags or SAVENAME;
|
|
Exit(EJUSTRETURN);
|
|
end;
|
|
|
|
end;
|
|
else;
|
|
end;
|
|
|
|
Exit(ENOENT);
|
|
end;
|
|
|
|
if (cnp^.cn_nameiop=DELETE) and ((flags and ISLASTCN)<>0) then
|
|
begin
|
|
error:=VOP_ACCESS(dvp, VWRITE);
|
|
if (error<>0) then
|
|
begin
|
|
Exit(error);
|
|
end;
|
|
|
|
if (vpp^=dvp) then
|
|
begin
|
|
VREF(dvp);
|
|
vpp^:=dvp;
|
|
Exit(0);
|
|
end;
|
|
end;
|
|
|
|
error:=ufs_allocv(de, dvp^.v_mount, cnp^.cn_lkflags and LK_TYPE_MASK, vpp); //sx_xunlock
|
|
dm_unlock^:=false;
|
|
|
|
Exit(error);
|
|
end;
|
|
|
|
function ufs_lookup(ap:p_vop_lookup_args):Integer;
|
|
var
|
|
dmp:p_ufs_mount;
|
|
dm_unlock:Boolean;
|
|
begin
|
|
dmp:=VFSTOUFS(ap^.a_dvp^.v_mount);
|
|
|
|
dm_unlock:=True;
|
|
sx_xlock(@dmp^.ufs_lock);
|
|
|
|
Result:=ufs_lookupx(ap, @dm_unlock);
|
|
|
|
if (dm_unlock) then
|
|
begin
|
|
sx_xunlock(@dmp^.ufs_lock);
|
|
end;
|
|
end;
|
|
|
|
function ufs_access(ap:p_vop_access_args):Integer;
|
|
var
|
|
vp:p_vnode;
|
|
de:p_ufs_dirent;
|
|
accmode:accmode_t;
|
|
error:Integer;
|
|
begin
|
|
vp:=ap^.a_vp;
|
|
|
|
de:=vp^.v_data;
|
|
|
|
accmode:=ap^.a_accmode;
|
|
|
|
if ((accmode and VWRITE)<>0) and
|
|
((p_mount(vp^.v_mount)^.mnt_flag and MNT_RDONLY)<>0) then
|
|
begin
|
|
case vp^.v_type of
|
|
VREG,
|
|
VDIR,
|
|
VLNK:
|
|
Exit(EROFS);
|
|
else;
|
|
end;
|
|
end;
|
|
|
|
error:=vaccess(vp^.v_type, de^.ufs_mode, de^.ufs_uid, de^.ufs_gid, accmode, nil);
|
|
|
|
if (error=0) then
|
|
begin
|
|
Exit(0);
|
|
end;
|
|
|
|
if (error<>EACCES) then
|
|
begin
|
|
Exit(error);
|
|
end;
|
|
|
|
if ((p_proc.p_flag and P_CONTROLT)=0) then
|
|
begin
|
|
Exit(error);
|
|
end;
|
|
|
|
Exit(error);
|
|
end;
|
|
|
|
function ufs_getattr(ap:p_vop_getattr_args):Integer;
|
|
var
|
|
vp:p_vnode;
|
|
vap:p_vattr;
|
|
de:p_ufs_dirent;
|
|
|
|
begin
|
|
vp:=ap^.a_vp;
|
|
vap:=ap^.a_vap;
|
|
de:=vp^.v_data;
|
|
|
|
vap^.va_uid :=de^.ufs_uid;
|
|
vap^.va_gid :=de^.ufs_gid;
|
|
vap^.va_mode:=de^.ufs_mode;
|
|
|
|
case vp^.v_type of
|
|
VLNK:
|
|
begin
|
|
vap^.va_size :=strlen(de^.ufs_symlink);
|
|
vap^.va_bytes:=0;
|
|
end;
|
|
VDIR:
|
|
begin
|
|
vap^.va_size :=DEV_BSIZE;
|
|
vap^.va_bytes:=DEV_BSIZE;
|
|
end;
|
|
else
|
|
begin
|
|
vap^.va_size :=de^.ufs_size;
|
|
vap^.va_bytes:=de^.ufs_bytes;
|
|
end;
|
|
end;
|
|
|
|
vap^.va_blocksize:=DEV_BSIZE;
|
|
vap^.va_type:=vp^.v_type;
|
|
|
|
vap^.va_atime :=de^.ufs_atime;
|
|
vap^.va_mtime :=de^.ufs_mtime;
|
|
vap^.va_ctime :=de^.ufs_ctime;
|
|
vap^.va_birthtime:=de^.ufs_btime;
|
|
|
|
vap^.va_gen :=0;
|
|
vap^.va_flags :=0;
|
|
vap^.va_filerev:=0;
|
|
vap^.va_nlink :=de^.ufs_links;
|
|
vap^.va_fileid :=de^.ufs_inode;
|
|
|
|
Exit(0);
|
|
end;
|
|
|
|
function ufs_setattr(ap:p_vop_setattr_args):Integer;
|
|
var
|
|
de:p_ufs_dirent;
|
|
vap:p_vattr;
|
|
vp:p_vnode;
|
|
c,error:Integer;
|
|
uid:uid_t;
|
|
gid:gid_t;
|
|
begin
|
|
vap:=ap^.a_vap;
|
|
vp:=ap^.a_vp;
|
|
|
|
if ((p_mount(vp^.v_mount)^.mnt_flag and MNT_RDONLY)<>0) and
|
|
(
|
|
(vap^.va_flags <>VNOVAL) or
|
|
(vap^.va_uid <>VNOVAL) or
|
|
(vap^.va_gid <>VNOVAL) or
|
|
(vap^.va_atime.tv_sec<>VNOVAL) or
|
|
(vap^.va_mtime.tv_sec<>VNOVAL) or
|
|
(vap^.va_mode <>VNOVAL)
|
|
) then
|
|
begin
|
|
Exit(EROFS);
|
|
end;
|
|
|
|
if (vap^.va_type <>VNON) or
|
|
(vap^.va_nlink <>VNOVAL) or
|
|
(vap^.va_fsid <>VNOVAL) or
|
|
(vap^.va_fileid <>VNOVAL) or
|
|
(vap^.va_blocksize<>VNOVAL) or
|
|
((vap^.va_flags <>VNOVAL) and (vap^.va_flags<>0)) or
|
|
(vap^.va_rdev <>VNOVAL) or
|
|
(vap^.va_bytes <>VNOVAL) or
|
|
(vap^.va_gen <>VNOVAL) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
de:=vp^.v_data;
|
|
|
|
error:=0;
|
|
c:=0;
|
|
|
|
if (vap^.va_uid=VNOVAL) then
|
|
uid:=de^.ufs_uid
|
|
else
|
|
uid:=vap^.va_uid;
|
|
|
|
if (vap^.va_gid=VNOVAL) then
|
|
gid:=de^.ufs_gid
|
|
else
|
|
gid:=vap^.va_gid;
|
|
|
|
if (uid<>de^.ufs_uid) or (gid<>de^.ufs_gid) then
|
|
begin
|
|
//if ((ap^.a_cred^.cr_uid<>de^.de_uid) or uid<>de^.de_uid or
|
|
// (gid<>de^.de_gid and !groupmember(gid, ap^.a_cred))) then
|
|
//begin
|
|
// error:=priv_check(td, PRIV_VFS_CHOWN);
|
|
// if (error<>) then
|
|
// Exit(error);
|
|
//end;
|
|
de^.ufs_uid:=uid;
|
|
de^.ufs_gid:=gid;
|
|
c:=1;
|
|
end;
|
|
|
|
if (vap^.va_mode<>VNOVAL) then
|
|
begin
|
|
//if (ap^.a_cred^.cr_uid<>de^.de_uid) then
|
|
//begin
|
|
// error:=priv_check(td, PRIV_VFS_ADMIN);
|
|
// if (error<>0) then
|
|
// Exit(error);
|
|
//end;
|
|
de^.ufs_mode:=vap^.va_mode;
|
|
c:=1;
|
|
end;
|
|
|
|
if (vap^.va_atime.tv_sec<>VNOVAL) or
|
|
(vap^.va_mtime.tv_sec<>VNOVAL) or
|
|
(vap^.va_birthtime.tv_sec<>VNOVAL) then
|
|
begin
|
|
{ See the comment in ufs_vnops::ufs_setattr(). }
|
|
|
|
error:=VOP_ACCESS(vp, VADMIN);
|
|
if (error<>0) then
|
|
begin
|
|
if ((vap^.va_vaflags and VA_UTIMES_NULL)=0) then Exit(error);
|
|
error:=VOP_ACCESS(vp, VWRITE);
|
|
if (error<>0) then Exit(error);
|
|
end;
|
|
|
|
if (vap^.va_atime.tv_sec<>VNOVAL) then
|
|
begin
|
|
de^.ufs_atime:=vap^.va_atime;
|
|
end;
|
|
if (vap^.va_mtime.tv_sec<>VNOVAL) then
|
|
begin
|
|
de^.ufs_mtime:=vap^.va_mtime;
|
|
end;
|
|
if (vap^.va_birthtime.tv_sec<>VNOVAL) then
|
|
begin
|
|
de^.ufs_btime:=vap^.va_birthtime;
|
|
end;
|
|
c:=1;
|
|
end;
|
|
|
|
if (c<>0) then
|
|
begin
|
|
vfs_timestamp(@de^.ufs_mtime);
|
|
end;
|
|
Exit(0);
|
|
end;
|
|
|
|
function ufs_readdir(ap:p_vop_readdir_args):Integer;
|
|
var
|
|
error:Integer;
|
|
uio:p_uio;
|
|
dp:p_dirent;
|
|
dd:p_ufs_dirent;
|
|
de:p_ufs_dirent;
|
|
dmp:p_ufs_mount;
|
|
mp:p_mount;
|
|
off:Int64;
|
|
tmp_ncookies:PInteger;
|
|
begin
|
|
tmp_ncookies:=nil;
|
|
|
|
if (ap^.a_vp^.v_type<>VDIR) then
|
|
begin
|
|
Exit(ENOTDIR);
|
|
end;
|
|
|
|
uio:=ap^.a_uio;
|
|
if (uio^.uio_offset < 0) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
if (ap^.a_ncookies<>nil) then
|
|
begin
|
|
tmp_ncookies:=ap^.a_ncookies;
|
|
ap^.a_ncookies^:=0;
|
|
ap^.a_ncookies:=nil;
|
|
end;
|
|
|
|
mp:=ap^.a_vp^.v_mount;
|
|
dmp:=VFSTOUFS(mp);
|
|
|
|
error:=0;
|
|
de:=ap^.a_vp^.v_data;
|
|
off:=0;
|
|
|
|
sx_xlock(@dmp^.ufs_lock);
|
|
|
|
dd:=TAILQ_FIRST(@de^.ufs_dlist);
|
|
while (dd<>nil) do
|
|
begin
|
|
dp:=dd^.ufs_dirent;
|
|
|
|
if (dp^.d_reclen > uio^.uio_resid) then break;
|
|
|
|
dp^.d_fileno:=dd^.ufs_inode;
|
|
|
|
if (off >= uio^.uio_offset) then
|
|
begin
|
|
error:=vfs_read_dirent(ap, dp, off);
|
|
if (error<>0) then break;
|
|
end;
|
|
|
|
Inc(off,dp^.d_reclen);
|
|
dd:=TAILQ_NEXT(dd,@dd^.ufs_list);
|
|
end;
|
|
|
|
sx_xunlock(@dmp^.ufs_lock);
|
|
uio^.uio_offset:=off;
|
|
|
|
{
|
|
* Restore ap^.a_ncookies if it wasn't originally nil in the first
|
|
* place.
|
|
}
|
|
if (tmp_ncookies<>nil) then
|
|
ap^.a_ncookies:=tmp_ncookies;
|
|
|
|
Exit(error);
|
|
end;
|
|
|
|
function ufs_rread(ap:p_vop_read_args):Integer;
|
|
begin
|
|
if (ap^.a_vp^.v_type<>VDIR) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
Exit(VOP_READDIR(ap^.a_vp, ap^.a_uio, nil, nil, nil));
|
|
end;
|
|
|
|
function ufs_readlink(ap:p_vop_readlink_args):Integer;
|
|
var
|
|
de:p_ufs_dirent;
|
|
begin
|
|
de:=ap^.a_vp^.v_data;
|
|
if (de^.ufs_dirent^.d_type<>DT_LNK) then Exit(EINVAL);
|
|
|
|
Exit(uiomove(de^.ufs_symlink, strlen(de^.ufs_symlink), ap^.a_uio));
|
|
end;
|
|
|
|
function ufs_symlink(ap:p_vop_symlink_args):Integer;
|
|
var
|
|
i,error:Integer;
|
|
dd:p_ufs_dirent;
|
|
de:p_ufs_dirent;
|
|
dmp:p_ufs_mount;
|
|
begin
|
|
error:=0;
|
|
//error:=priv_check(curkthread, PRIV_DEVFS_SYMLINK);
|
|
if (error<>0) then Exit(error);
|
|
|
|
dmp:=VFSTOUFS(ap^.a_dvp^.v_mount);
|
|
|
|
sx_xlock(@dmp^.ufs_lock);
|
|
|
|
dd:=ap^.a_dvp^.v_data;
|
|
de:=ufs_find(dd, ap^.a_cnp^.cn_nameptr, ap^.a_cnp^.cn_namelen, 0);
|
|
|
|
if (de<>nil) then
|
|
begin
|
|
sx_xunlock(@dmp^.ufs_lock);
|
|
Exit(EEXIST);
|
|
end;
|
|
|
|
de:=ufs_newdirent(ap^.a_cnp^.cn_nameptr, ap^.a_cnp^.cn_namelen);
|
|
de^.ufs_flags:=0;
|
|
de^.ufs_uid :=0;
|
|
de^.ufs_gid :=0;
|
|
de^.ufs_mode :=UFS_DEFAULT_MODE;
|
|
de^.ufs_inode:=ufs_alloc_cdp_inode;
|
|
de^.ufs_dir :=dd;
|
|
de^.ufs_dirent^.d_type:=DT_LNK;
|
|
|
|
i:=strlen(ap^.a_target) + 1;
|
|
de^.ufs_symlink:=AllocMem(i);
|
|
Move(ap^.a_target^, de^.ufs_symlink^, i);
|
|
|
|
TAILQ_INSERT_TAIL(@dd^.ufs_dlist,de,@de^.ufs_list);
|
|
|
|
Exit(ufs_allocv(de, ap^.a_dvp^.v_mount, LK_EXCLUSIVE, ap^.a_vpp)); //sx_xunlock
|
|
end;
|
|
|
|
function ufs_remove(ap:p_vop_remove_args):Integer;
|
|
var
|
|
dvp,vp:p_vnode;
|
|
de:p_ufs_dirent;
|
|
dmp:p_ufs_mount;
|
|
begin
|
|
dvp:=ap^.a_dvp;
|
|
vp:=ap^.a_vp;
|
|
dmp:=VFSTOUFS(vp^.v_mount);
|
|
|
|
ASSERT_VOP_ELOCKED(dvp, 'ufs_remove');
|
|
ASSERT_VOP_ELOCKED(vp, 'ufs_remove');
|
|
|
|
sx_xlock(@dmp^.ufs_lock);
|
|
de:=vp^.v_data;
|
|
|
|
if (de^.ufs_dirent^.d_type=DT_DIR) then
|
|
begin
|
|
Result:=_ufs_dir_status(dmp, de);
|
|
if (Result<>0) then
|
|
begin
|
|
sx_xunlock(@dmp^.ufs_lock);
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
VOP_UNLOCK(vp, 0);
|
|
|
|
if (dvp<>vp) then
|
|
begin
|
|
VOP_UNLOCK(dvp, 0);
|
|
end;
|
|
|
|
if (de^.ufs_dirent^.d_type=DT_DIR) then
|
|
begin
|
|
_ufs_rmdir(dmp, de);
|
|
end else
|
|
begin
|
|
ufs_delete(dmp, de, UFS_DEL_NORECURSE);
|
|
end;
|
|
|
|
sx_xunlock(@dmp^.ufs_lock);
|
|
|
|
if (dvp<>vp) then
|
|
begin
|
|
vn_lock(dvp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
end;
|
|
|
|
vn_lock(vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
|
|
Exit(0);
|
|
end;
|
|
|
|
function ufs_rmdir(ap:p_vop_rmdir_args):Integer;
|
|
var
|
|
dvp,vp:p_vnode;
|
|
de:p_ufs_dirent;
|
|
dmp:p_ufs_mount;
|
|
begin
|
|
dvp:=ap^.a_dvp;
|
|
vp:=ap^.a_vp;
|
|
dmp:=VFSTOUFS(vp^.v_mount);
|
|
|
|
ASSERT_VOP_ELOCKED(dvp, 'ufs_remove');
|
|
ASSERT_VOP_ELOCKED(vp, 'ufs_remove');
|
|
|
|
sx_xlock(@dmp^.ufs_lock);
|
|
|
|
de:=vp^.v_data;
|
|
|
|
Result:=_ufs_dir_status(dmp, de);
|
|
if (Result<>0) then
|
|
begin
|
|
sx_xunlock(@dmp^.ufs_lock);
|
|
Exit;
|
|
end;
|
|
|
|
VOP_UNLOCK(vp, 0);
|
|
|
|
if (dvp<>vp) then
|
|
begin
|
|
VOP_UNLOCK(dvp, 0);
|
|
end;
|
|
|
|
_ufs_rmdir(dmp, de);
|
|
|
|
sx_xunlock(@dmp^.ufs_lock);
|
|
|
|
if (dvp<>vp) then
|
|
begin
|
|
vn_lock(dvp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
end;
|
|
|
|
vn_lock(vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
|
|
Exit(0);
|
|
end;
|
|
|
|
function ufs_mkdir(ap:p_vop_mkdir_args):Integer;
|
|
var
|
|
dvp:p_vnode;
|
|
cnp:p_componentname;
|
|
vap:p_vattr;
|
|
dmp:p_ufs_mount;
|
|
dd:p_ufs_dirent;
|
|
de:p_ufs_dirent;
|
|
begin
|
|
dvp:=ap^.a_dvp;
|
|
cnp:=ap^.a_cnp;
|
|
vap:=ap^.a_vap;
|
|
dmp:=VFSTOUFS(dvp^.v_mount);
|
|
|
|
//priv_check
|
|
|
|
sx_xlock(@dmp^.ufs_lock);
|
|
|
|
dd:=dvp^.v_data;
|
|
|
|
de:=ufs_find(dd, cnp^.cn_nameptr, cnp^.cn_namelen, 0);
|
|
|
|
if (de<>nil) then
|
|
begin
|
|
sx_xunlock(@dmp^.ufs_lock);
|
|
Exit(EEXIST);
|
|
end;
|
|
|
|
de:=ufs_vmkdir(dmp,ap^.a_cnp^.cn_nameptr,ap^.a_cnp^.cn_namelen,dd,0);
|
|
|
|
if (de=nil) then
|
|
begin
|
|
sx_xunlock(@dmp^.ufs_lock);
|
|
Exit(ENOMEM);
|
|
end;
|
|
|
|
de^.ufs_mode :=vap^.va_mode;
|
|
|
|
Exit(ufs_allocv(de, ap^.a_dvp^.v_mount, LK_EXCLUSIVE, ap^.a_vpp)); //sx_xunlock
|
|
end;
|
|
|
|
function ufs_reclaim(ap:p_vop_reclaim_args):Integer;
|
|
var
|
|
vp:p_vnode;
|
|
begin
|
|
vp:=ap^.a_vp;
|
|
|
|
ufs_relv(vp);
|
|
|
|
vnode_destroy_vobject(vp);
|
|
|
|
Exit(0);
|
|
end;
|
|
|
|
end.
|
|
|