mirror of https://github.com/red-prig/fpPS4.git
645 lines
12 KiB
Plaintext
645 lines
12 KiB
Plaintext
unit fdesc_vnops;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
mqueue,
|
|
kern_param,
|
|
vmount,
|
|
vnode,
|
|
vfs_default,
|
|
vnode_if,
|
|
fdescfs;
|
|
|
|
{
|
|
* /dev/fd Filesystem
|
|
}
|
|
function fdesc_getattr(ap:p_vop_getattr_args):Integer;
|
|
function fdesc_lookup(ap:p_vop_lookup_args):Integer;
|
|
function fdesc_open(ap:p_vop_open_args):Integer;
|
|
function fdesc_readdir(ap:p_vop_readdir_args):Integer;
|
|
function fdesc_reclaim(ap:p_vop_reclaim_args):Integer;
|
|
function fdesc_setattr(ap:p_vop_setattr_args):Integer;
|
|
|
|
const
|
|
fdesc_vnodeops:vop_vector=(
|
|
vop_default :@default_vnodeops;
|
|
vop_bypass :nil;
|
|
|
|
vop_islocked :nil;
|
|
vop_lookup :@fdesc_lookup;
|
|
vop_create :nil;
|
|
vop_whiteout :nil;
|
|
vop_mknod :nil;
|
|
vop_open :@fdesc_open;
|
|
vop_close :nil;
|
|
vop_access :@VOP_NULL;
|
|
vop_accessx :nil;
|
|
vop_getattr :@fdesc_getattr;
|
|
vop_setattr :@fdesc_setattr;
|
|
vop_markatime :nil;
|
|
vop_read :nil;
|
|
vop_write :nil;
|
|
vop_ioctl :nil;
|
|
vop_poll :nil;
|
|
vop_kqfilter :nil;
|
|
vop_revoke :nil;
|
|
vop_fsync :nil;
|
|
vop_remove :nil;
|
|
vop_link :nil;
|
|
vop_rename :nil;
|
|
vop_mkdir :nil;
|
|
vop_rmdir :nil;
|
|
vop_symlink :nil;
|
|
vop_readdir :@fdesc_readdir;
|
|
vop_readlink :nil;
|
|
vop_inactive :nil;
|
|
vop_reclaim :@fdesc_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;
|
|
);
|
|
|
|
procedure fdesc_insmntque_dtr(vp:p_vnode;arg:Pointer);
|
|
procedure fdesc_remove_entry(fd:p_fdescnode);
|
|
|
|
function fdesc_allocvp(ftype:fdntype;fd_fd,ix:Integer;mp:p_mount;vpp:pp_vnode):Integer;
|
|
|
|
implementation
|
|
|
|
uses
|
|
errno,
|
|
time,
|
|
vfile,
|
|
vfiledesc,
|
|
vnamei,
|
|
vstat,
|
|
vdirent,
|
|
vcapability,
|
|
vuio,
|
|
subr_uio,
|
|
vfs_vnops,
|
|
vfs_subr,
|
|
vfs_syscalls,
|
|
kern_mtx,
|
|
kern_descrip,
|
|
kern_thr;
|
|
|
|
function VFSTOFDESC(mp:p_mount):p_fdescmount; inline;
|
|
begin
|
|
Result:=mp^.mnt_data;
|
|
end;
|
|
|
|
function VTOFDESC(vp:p_vnode):p_fdescnode; inline;
|
|
begin
|
|
Result:=vp^.v_data;
|
|
end;
|
|
|
|
{
|
|
* If allocating vnode fails, call this.
|
|
}
|
|
procedure fdesc_insmntque_dtr(vp:p_vnode;arg:Pointer);
|
|
begin
|
|
vgone(vp);
|
|
vput(vp);
|
|
end;
|
|
|
|
{
|
|
* Remove an entry from the hash if it exists.
|
|
}
|
|
procedure fdesc_remove_entry(fd:p_fdescnode);
|
|
var
|
|
fc:p_fdhashhead;
|
|
fd2:p_fdescnode;
|
|
begin
|
|
fc:=FD_NHASH(fd^.fd_ix);
|
|
mtx_lock(fdesc_hashmtx);
|
|
fd2:=LIST_FIRST(fc);
|
|
while (fd2<>nil) do
|
|
begin
|
|
if (fd=fd2) then
|
|
begin
|
|
LIST_REMOVE(fd,@fd^.fd_hash);
|
|
break;
|
|
end;
|
|
fd2:=LIST_NEXT(fd2,@fd2^.fd_hash);
|
|
end;
|
|
mtx_unlock(fdesc_hashmtx);
|
|
end;
|
|
|
|
function fdesc_allocvp(ftype:fdntype;fd_fd,ix:Integer;mp:p_mount;vpp:pp_vnode):Integer;
|
|
label
|
|
_or,
|
|
loop;
|
|
var
|
|
fmp:p_fdescmount;
|
|
fc:p_fdhashhead;
|
|
fd,fd2:p_fdescnode;
|
|
vp,vp2:p_vnode;
|
|
error:Integer;
|
|
begin
|
|
error:=0;
|
|
|
|
fc:=FD_NHASH(ix);
|
|
loop:
|
|
mtx_lock(fdesc_hashmtx);
|
|
{
|
|
* If a forced unmount is progressing, we need to drop it. The flags are
|
|
* protected by the hashmtx.
|
|
}
|
|
fmp:=p_fdescmount(mp^.mnt_data);
|
|
|
|
if (fmp=nil) then
|
|
begin
|
|
mtx_unlock(fdesc_hashmtx);
|
|
Exit(-1);
|
|
end;
|
|
|
|
if ((fmp^.flags and FMNT_UNMOUNTF)<>0) then
|
|
begin
|
|
mtx_unlock(fdesc_hashmtx);
|
|
Exit(-1);
|
|
end;
|
|
|
|
fd:=LIST_FIRST(fc);
|
|
|
|
while (fd<>nil) do
|
|
begin
|
|
if (fd^.fd_ix=ix) and (fd^.fd_vnode^.v_mount=mp) then
|
|
begin
|
|
{ Get reference to vnode in case it's being free'd }
|
|
vp:=fd^.fd_vnode;
|
|
VI_LOCK(vp);
|
|
mtx_unlock(fdesc_hashmtx);
|
|
if (vget(vp, LK_EXCLUSIVE or LK_INTERLOCK)<>0) then
|
|
begin
|
|
goto loop;
|
|
end;
|
|
vpp^:=vp;
|
|
Exit(0);
|
|
end;
|
|
fd:=LIST_NEXT(fd,@fd^.fd_hash);
|
|
end;
|
|
mtx_unlock(fdesc_hashmtx);
|
|
|
|
fd:=AllocMem(sizeof(t_fdescnode));
|
|
|
|
error:=getnewvnode('fdescfs', mp, @fdesc_vnodeops, @vp);
|
|
if (error<>0) then
|
|
begin
|
|
FreeMem(fd);
|
|
Exit(error);
|
|
end;
|
|
|
|
vn_lock(vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
|
|
vp^.v_data :=fd;
|
|
fd^.fd_vnode:=vp;
|
|
fd^.fd_type :=ftype;
|
|
fd^.fd_fd :=fd_fd;
|
|
fd^.fd_ix :=ix;
|
|
|
|
error:=insmntque1(vp, mp, @fdesc_insmntque_dtr, nil);
|
|
if (error<>0) then
|
|
begin
|
|
vpp^:=nil;
|
|
Exit(error);
|
|
end;
|
|
|
|
{ Make sure that someone didn't beat us when inserting the vnode. }
|
|
mtx_lock(fdesc_hashmtx);
|
|
{
|
|
* If a forced unmount is progressing, we need to drop it. The flags are
|
|
* protected by the hashmtx.
|
|
}
|
|
fmp:=p_fdescmount(mp^.mnt_data);
|
|
|
|
if (fmp=nil) then goto _or;
|
|
|
|
if ((fmp^.flags and FMNT_UNMOUNTF)<>0) then
|
|
begin
|
|
_or:
|
|
mtx_unlock(fdesc_hashmtx);
|
|
vgone(vp);
|
|
vput(vp);
|
|
vpp^:=nil;
|
|
Exit(-1);
|
|
end;
|
|
|
|
fd2:=LIST_FIRST(fc);
|
|
while (fd2<>nil) do
|
|
begin
|
|
if (fd2^.fd_ix=ix) and (fd2^.fd_vnode^.v_mount=mp) then
|
|
begin
|
|
{ Get reference to vnode in case it's being free'd }
|
|
vp2:=fd2^.fd_vnode;
|
|
VI_LOCK(vp2);
|
|
mtx_unlock(fdesc_hashmtx);
|
|
error:=vget(vp2, LK_EXCLUSIVE or LK_INTERLOCK);
|
|
{ Someone beat us, dec use count and wait for reclaim }
|
|
vgone(vp);
|
|
vput(vp);
|
|
{ If we didn't get it, return no vnode. }
|
|
if (error<>0) then
|
|
begin
|
|
vp2:=nil;
|
|
end;
|
|
vpp^:=vp2;
|
|
Exit(error);
|
|
end;
|
|
fd2:=LIST_NEXT(fd2,@fd2^.fd_hash);
|
|
end;
|
|
|
|
{ If we came here, we can insert it safely. }
|
|
LIST_INSERT_HEAD(fc,fd,@fd^.fd_hash);
|
|
mtx_unlock(fdesc_hashmtx);
|
|
vpp^:=vp;
|
|
Exit(0);
|
|
end;
|
|
|
|
{
|
|
* vp is the current namei directory
|
|
* ndp is the name to locate in that directory...
|
|
}
|
|
function fdesc_lookup(ap:p_vop_lookup_args):Integer;
|
|
label
|
|
bad;
|
|
var
|
|
vpp:pp_vnode;
|
|
dvp:p_vnode;
|
|
cnp:p_componentname;
|
|
pname:PChar;
|
|
fp:p_file;
|
|
nlen:Integer;
|
|
fd,fd1:Integer;
|
|
error:Integer;
|
|
fvp:p_vnode;
|
|
begin
|
|
vpp:=ap^.a_vpp;
|
|
dvp:=ap^.a_dvp;
|
|
cnp:=ap^.a_cnp;
|
|
pname:=cnp^.cn_nameptr;
|
|
nlen:=cnp^.cn_namelen;
|
|
|
|
if ((cnp^.cn_flags and ISLASTCN)<>0) and
|
|
((cnp^.cn_nameiop=DELETE) or (cnp^.cn_nameiop=RENAME)) then
|
|
begin
|
|
error:=EROFS;
|
|
goto bad;
|
|
end;
|
|
|
|
if (cnp^.cn_namelen=1) and (pname^='.') then
|
|
begin
|
|
vpp^:=dvp;
|
|
VREF(dvp);
|
|
Exit(0);
|
|
end;
|
|
|
|
if (VTOFDESC(dvp)^.fd_type<>_Froot) then
|
|
begin
|
|
error:=ENOTDIR;
|
|
goto bad;
|
|
end;
|
|
|
|
fd:=0;
|
|
{ the only time a leading 0 is acceptable is if it's '0' }
|
|
if (pname^='0') and (nlen<>1) then
|
|
begin
|
|
error:=ENOENT;
|
|
goto bad;
|
|
end;
|
|
|
|
while (nlen<>0) do
|
|
begin
|
|
Dec(nlen);
|
|
if (pname^ < '0') or (pname^ > '9') then
|
|
begin
|
|
error:=ENOENT;
|
|
goto bad;
|
|
end;
|
|
fd1:=10 * fd + ord(pname^) - ord('0');
|
|
Inc(pname);
|
|
if (fd1 < fd) then
|
|
begin
|
|
error:=ENOENT;
|
|
goto bad;
|
|
end;
|
|
fd:=fd1;
|
|
end;
|
|
|
|
{
|
|
* No rights to check since 'fp' isn't actually used.
|
|
}
|
|
error:=fget(fd, 0, @fp);
|
|
if (error<>0) then
|
|
begin
|
|
goto bad;
|
|
end;
|
|
|
|
{ Check if we're looking up ourselves. }
|
|
if (VTOFDESC(dvp)^.fd_ix=FD_DESC + fd) then
|
|
begin
|
|
{
|
|
* In case we're holding the last reference to the file, the dvp
|
|
* will be re-acquired.
|
|
}
|
|
vhold(dvp);
|
|
VOP_UNLOCK(dvp, 0);
|
|
fdrop(fp);
|
|
|
|
{ Re-aquire the lock afterwards. }
|
|
vn_lock(dvp, LK_RETRY or LK_EXCLUSIVE,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
vdrop(dvp);
|
|
fvp:=dvp;
|
|
end else
|
|
begin
|
|
{
|
|
* Unlock our root node (dvp) when doing this, since we might
|
|
* deadlock since the vnode might be locked by another thread
|
|
* and the root vnode lock will be obtained afterwards (in case
|
|
* we're looking up the fd of the root vnode), which will be the
|
|
* opposite lock order. Vhold the root vnode first so we don't
|
|
* lose it.
|
|
}
|
|
vhold(dvp);
|
|
VOP_UNLOCK(dvp, 0);
|
|
error:=fdesc_allocvp(_Fdesc, fd, FD_DESC + fd, dvp^.v_mount, @fvp);
|
|
fdrop(fp);
|
|
{
|
|
* The root vnode must be locked last to prevent deadlock condition.
|
|
}
|
|
vn_lock(dvp, LK_RETRY or LK_EXCLUSIVE,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
vdrop(dvp);
|
|
end;
|
|
|
|
if (error<>0) then
|
|
begin
|
|
goto bad;
|
|
end;
|
|
vpp^:=fvp;
|
|
Exit(0);
|
|
|
|
bad:
|
|
vpp^:=nil;
|
|
Exit(error);
|
|
end;
|
|
|
|
function fdesc_open(ap:p_vop_open_args):Integer;
|
|
var
|
|
vp:p_vnode;
|
|
td:p_kthread;
|
|
begin
|
|
vp:=ap^.a_vp;
|
|
|
|
if (VTOFDESC(vp)^.fd_type=_Froot) then
|
|
Exit(0);
|
|
|
|
{
|
|
* XXX Kludge: set td^.td_proc^.p_dupfd to contain the value of the file
|
|
* descriptor being sought for duplication. The error Exitensures
|
|
* that the vnode for this device will be released by vn_open. Open
|
|
* will detect this special error and take the actions in dupfdopen.
|
|
* Other callers of vn_open or VOP_OPEN will simply report the
|
|
* error.
|
|
}
|
|
td:=curkthread;
|
|
if (td<>nil) then
|
|
begin
|
|
td^.td_dupfd:=VTOFDESC(vp)^.fd_fd; { XXX }
|
|
end;
|
|
|
|
Exit(ENODEV);
|
|
end;
|
|
|
|
function fdesc_getattr(ap:p_vop_getattr_args):Integer;
|
|
var
|
|
vp:p_vnode;
|
|
vap:p_vattr;
|
|
begin
|
|
vp:=ap^.a_vp;
|
|
vap:=ap^.a_vap;
|
|
|
|
vap^.va_mode :=S_IRUSR or S_IXUSR or S_IRGRP or S_IXGRP or S_IROTH or S_IXOTH;
|
|
vap^.va_fileid :=VTOFDESC(vp)^.fd_ix;
|
|
vap^.va_uid :=0;
|
|
vap^.va_gid :=0;
|
|
vap^.va_blocksize:=DEV_BSIZE;
|
|
vap^.va_atime.tv_sec :=boottime.tv_sec;
|
|
vap^.va_atime.tv_nsec:=0;
|
|
vap^.va_mtime :=vap^.va_atime;
|
|
vap^.va_ctime :=vap^.va_mtime;
|
|
vap^.va_gen :=0;
|
|
vap^.va_flags :=0;
|
|
vap^.va_bytes :=0;
|
|
vap^.va_filerev :=0;
|
|
|
|
case VTOFDESC(vp)^.fd_type of
|
|
_Froot:
|
|
begin
|
|
vap^.va_type :=VDIR;
|
|
vap^.va_nlink:=2;
|
|
vap^.va_size :=DEV_BSIZE;
|
|
vap^.va_rdev :=NODEV;
|
|
end;
|
|
_Fdesc:
|
|
begin
|
|
vap^.va_type :=VCHR;
|
|
vap^.va_nlink:=1;
|
|
vap^.va_size :=0;
|
|
vap^.va_rdev :=makedev(0, vap^.va_fileid);
|
|
end;
|
|
end;
|
|
|
|
vp^.v_type:=vap^.va_type;
|
|
Exit(0);
|
|
end;
|
|
|
|
function fdesc_setattr(ap:p_vop_setattr_args):Integer;
|
|
var
|
|
vap:p_vattr;
|
|
vp:p_vnode;
|
|
mp:p_mount;
|
|
fp:p_file;
|
|
fd:Integer;
|
|
error:Integer;
|
|
begin
|
|
vap:=ap^.a_vap;
|
|
|
|
{
|
|
* Can't mess with the root vnode
|
|
}
|
|
if (VTOFDESC(ap^.a_vp)^.fd_type=_Froot) then
|
|
begin
|
|
Exit(EACCES);
|
|
end;
|
|
|
|
fd:=VTOFDESC(ap^.a_vp)^.fd_fd;
|
|
|
|
{
|
|
* Allow setattr where there is an underlying vnode.
|
|
}
|
|
error:=getvnode(fd, CAP_EXTATTR_SET, @fp);
|
|
if (error<>0) then
|
|
begin
|
|
{
|
|
* getvnode() Exits EINVAL if the file descriptor is not
|
|
* backed by a vnode. Silently drop all changes except
|
|
* chflags(2) in this case.
|
|
}
|
|
if (error=EINVAL) then
|
|
begin
|
|
if (vap^.va_flags<>VNOVAL) then
|
|
error:=EOPNOTSUPP
|
|
else
|
|
error:=0;
|
|
end;
|
|
Exit(error);
|
|
end;
|
|
vp:=fp^.f_vnode;
|
|
|
|
error:=vn_start_write(vp, @mp, V_WAIT or PCATCH);
|
|
if (error=0) then
|
|
begin
|
|
vn_lock(vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
error:=VOP_SETATTR(vp, ap^.a_vap);
|
|
VOP_UNLOCK(vp, 0);
|
|
vn_finished_write(mp);
|
|
end;
|
|
fdrop(fp);
|
|
Exit(error);
|
|
end;
|
|
|
|
const
|
|
UIO_MX=16;
|
|
|
|
function fdesc_readdir(ap:p_vop_readdir_args):Integer;
|
|
label
|
|
_break,
|
|
done;
|
|
var
|
|
uio:p_uio;
|
|
d:t_dirent;
|
|
dp:p_dirent;
|
|
error,i,off,fcnt:Integer;
|
|
tmp:p_file;
|
|
begin
|
|
uio:=ap^.a_uio;
|
|
dp:=@d;
|
|
|
|
if (VTOFDESC(ap^.a_vp)^.fd_type<>_Froot) then
|
|
begin
|
|
Assert(False,'fdesc_readdir: not dir');
|
|
end;
|
|
|
|
if (ap^.a_ncookies<>nil) then
|
|
ap^.a_ncookies^:=0;
|
|
|
|
off:=uio^.uio_offset;
|
|
|
|
if (off<>uio^.uio_offset) or
|
|
(off < 0) or
|
|
((off mod UIO_MX)<>0) or
|
|
(uio^.uio_resid < UIO_MX) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
i:=off div UIO_MX;
|
|
|
|
error:=0;
|
|
|
|
fcnt:=i - 2; { The first two nodes are `.' and `..' }
|
|
|
|
FILEDESC_SLOCK(@fd_table);
|
|
|
|
while (i < fd_table.fd_nfiles + 2) and (uio^.uio_resid >= UIO_MX) do
|
|
begin
|
|
FillChar(dp^,UIO_MX,0);
|
|
case i of
|
|
0, { `.' }
|
|
1: { `..' }
|
|
begin
|
|
dp^.d_fileno:=i + FD_ROOT;
|
|
dp^.d_namlen:=i + 1;
|
|
dp^.d_reclen:=UIO_MX;
|
|
Move(PChar('..')^, dp^.d_name, dp^.d_namlen);
|
|
dp^.d_name[i + 1]:=#0;
|
|
dp^.d_type:=DT_DIR;
|
|
end;
|
|
else
|
|
begin
|
|
tmp:=fget_unlocked(fcnt); //check exists
|
|
if (tmp=nil) then
|
|
begin
|
|
goto _break;
|
|
end;
|
|
fdrop(tmp);
|
|
|
|
Str(fcnt,dp^.d_name);
|
|
dp^.d_namlen:=strlen(dp^.d_name);
|
|
dp^.d_reclen:=UIO_MX;
|
|
dp^.d_type :=DT_CHR;
|
|
dp^.d_fileno:=i + FD_DESC;
|
|
end;
|
|
end;
|
|
|
|
_break:
|
|
if (dp^.d_namlen<>0) then
|
|
begin
|
|
{
|
|
* And ship to userland
|
|
}
|
|
FILEDESC_SUNLOCK(@fd_table);
|
|
error:=uiomove(dp, UIO_MX, uio);
|
|
if (error<>0) then
|
|
begin
|
|
goto done;
|
|
end;
|
|
FILEDESC_SLOCK(@fd_table);
|
|
end;
|
|
Inc(i);
|
|
Inc(fcnt);
|
|
end;
|
|
FILEDESC_SUNLOCK(@fd_table);
|
|
|
|
done:
|
|
uio^.uio_offset:=i * UIO_MX;
|
|
Exit(error);
|
|
end;
|
|
|
|
function fdesc_reclaim(ap:p_vop_reclaim_args):Integer;
|
|
var
|
|
vp:p_vnode;
|
|
fd:p_fdescnode;
|
|
begin
|
|
vp:=ap^.a_vp;
|
|
fd:=VTOFDESC(vp);
|
|
fdesc_remove_entry(fd);
|
|
FreeMem(vp^.v_data);
|
|
vp^.v_data:=nil;
|
|
Exit(0);
|
|
end;
|
|
|
|
|
|
end.
|
|
|