FPPS4/sys/fs/fdescfs/fdesc_vnops.pas

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.