FPPS4/sys/fs/ufs/ufs_vnops.pas

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.