mirror of https://github.com/red-prig/fpPS4.git
320 lines
7.6 KiB
Plaintext
320 lines
7.6 KiB
Plaintext
unit vfs_init;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
sysutils,
|
|
murmurhash,
|
|
mqueue,
|
|
vmount;
|
|
|
|
function vfs_byname(name:PChar):p_vfsconf;
|
|
function vfs_byname_kld(fstype:PChar;error:PInteger):p_vfsconf;
|
|
function vfs_register(vfc:p_vfsconf):Integer;
|
|
function vfs_unregister(vfc:p_vfsconf):Integer;
|
|
|
|
implementation
|
|
|
|
uses
|
|
errno,
|
|
vfs_default;
|
|
|
|
var
|
|
maxvfsconf:Integer=VFS_GENERIC+1;
|
|
|
|
{
|
|
* Single-linked list of configured VFSes.
|
|
* New entries are added/deleted by vfs_register()/vfs_unregister()
|
|
}
|
|
vfsconf:TAILQ_HEAD=(tqh_first:nil;tqh_last:@vfsconf.tqh_first);
|
|
|
|
{
|
|
* Loader.conf variable vfs.typenumhash enables setting vfc_typenum using a hash
|
|
* calculation on vfc_name, so that it doesn't change when file systems are
|
|
* loaded in a different order. This will avoid the NFS server file handles from
|
|
* changing for file systems that use vfc_typenum in their fsid.
|
|
}
|
|
vfs_typenumhash:Integer=1;
|
|
|
|
{
|
|
* vfs_init.c
|
|
*
|
|
* Allocate and fill in operations vectors.
|
|
*
|
|
* An undocumented feature of this approach to defining operations is that
|
|
* there can be multiple entries in vfs_opv_descs for the same operations
|
|
* vector. This allows third parties to extend the set of operations
|
|
* supported by another layer in a binary compatibile way. For example,
|
|
* assume that NFS needed to be modified to support Ficus. NFS has an entry
|
|
* (probably nfs_vnopdeop_decls) declaring all the operations NFS supports by
|
|
* default. Ficus could add another entry (ficus_nfs_vnodeop_decl_entensions)
|
|
* listing those new operations Ficus adds to NFS, all without modifying the
|
|
* NFS code. (Of couse, the OTW NFS protocol still needs to be munged, but
|
|
* that is a(whole)nother story.) This is a feature.
|
|
}
|
|
|
|
{
|
|
* Routines having to do with the management of the vnode table.
|
|
}
|
|
function vfs_byname(name:PChar):p_vfsconf;
|
|
var
|
|
vfsp:p_vfsconf;
|
|
begin
|
|
Result:=nil;
|
|
vfsp:=TAILQ_FIRST(@vfsconf);
|
|
while (vfsp<>nil) do
|
|
begin
|
|
if (strcomp(name, vfsp^.vfc_name)=0) then Exit(vfsp);
|
|
vfsp:=TAILQ_NEXT(vfsp,@vfsp^.vfc_list);
|
|
end;
|
|
end;
|
|
|
|
function vfs_byname_kld(fstype:PChar;error:PInteger):p_vfsconf;
|
|
var
|
|
vfsp:p_vfsconf;
|
|
fileid,loaded:Integer;
|
|
begin
|
|
vfsp:=vfs_byname(fstype);
|
|
if (vfsp<>nil) then Exit(vfsp);
|
|
|
|
error^:=ENODEV;
|
|
|
|
{
|
|
{ Try to load the respective module. }
|
|
error^:=kern_kldload(td, fstype, @fileid);
|
|
|
|
loaded:=(error^=0);
|
|
|
|
if (error^=EEXIST)
|
|
error^:=0;
|
|
|
|
if (error^<>0) then
|
|
Exit(nil);
|
|
|
|
{ Look up again to see if the VFS was loaded. }
|
|
vfsp:=vfs_byname(fstype);
|
|
if (vfsp=nil)
|
|
begin
|
|
if (loaded<>0) then
|
|
kern_kldunload(td, fileid, LINKER_UNLOAD_FORCE);
|
|
|
|
error^:=ENODEV;
|
|
Exit(nil);
|
|
end;
|
|
}
|
|
|
|
Exit(vfsp);
|
|
end;
|
|
|
|
{ Register a new filesystem type in the global table }
|
|
function vfs_register(vfc:p_vfsconf):Integer;
|
|
var
|
|
vfsops:p_vfsops;
|
|
tvfc:p_vfsconf;
|
|
hashval:DWORD;
|
|
secondpass:Integer;
|
|
begin
|
|
//struct sysctl_oid *oidp;
|
|
|
|
if (vfc^.vfc_version<>VFS_VERSION) then
|
|
begin
|
|
Assert(false,'ERROR: filesystem %s, unsupported ABI version');
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
if (vfs_byname(vfc^.vfc_name)<>nil) then
|
|
Exit(EEXIST);
|
|
|
|
if (vfs_typenumhash<>0) then
|
|
begin
|
|
{
|
|
* Calculate a hash on vfc_name to use for vfc_typenum. Unless
|
|
* all of 1<^.255 are assigned, it is limited to 8bits since
|
|
* that is what ZFS uses from vfc_typenum and is also the
|
|
* preferred range for vfs_getnewfsid().
|
|
}
|
|
hashval:=MurmurHash64A(@vfc^.vfc_name,strlen(vfc^.vfc_name),0);
|
|
hashval:=hashval and $ff;
|
|
secondpass:=0;
|
|
repeat
|
|
{ Look for and fix any collision. }
|
|
|
|
tvfc:=TAILQ_FIRST(@vfsconf);
|
|
while (tvfc<>nil) do
|
|
begin
|
|
if (hashval=tvfc^.vfc_typenum) then
|
|
begin
|
|
if (hashval=255) and (secondpass=0) then
|
|
begin
|
|
hashval:=1;
|
|
secondpass:=1;
|
|
end else
|
|
Inc(hashval);
|
|
break;
|
|
end;
|
|
|
|
tvfc:=TAILQ_NEXT(tvfc,@tvfc^.vfc_list);
|
|
end;
|
|
until (tvfc=nil);
|
|
vfc^.vfc_typenum:=hashval;
|
|
if (vfc^.vfc_typenum >= maxvfsconf) then
|
|
maxvfsconf:=vfc^.vfc_typenum + 1;
|
|
end else
|
|
begin
|
|
Inc(maxvfsconf);
|
|
vfc^.vfc_typenum:=maxvfsconf;
|
|
end;
|
|
TAILQ_INSERT_TAIL(@vfsconf,vfc,@vfc^.vfc_list);
|
|
|
|
{
|
|
* If this filesystem has a sysctl node under vfs
|
|
* (i.e. vfs.xxfs), then change the oid number of that node to
|
|
* match the filesystem's type number. This allows user code
|
|
* which uses the type number to read sysctl variables defined
|
|
* by the filesystem to continue working. Since the oids are
|
|
* in a sorted list, we need to make sure the order is
|
|
* preserved by re-registering the oid after modifying its
|
|
* number.
|
|
}
|
|
//sysctl_lock();
|
|
//SLIST_FOREACH(oidp, @sysctl__vfs_children, oid_link)
|
|
// if (strcmp(oidp^.oid_name, vfc^.vfc_name)=0) begin
|
|
// sysctl_unregister_oid(oidp);
|
|
// oidp^.oid_number:=vfc^.vfc_typenum;
|
|
// sysctl_register_oid(oidp);
|
|
// break;
|
|
// end;
|
|
//sysctl_unlock();
|
|
|
|
{
|
|
* Initialise unused ``struct vfsops'' fields, to use
|
|
* the vfs_std*() functions. Note, we need the mount
|
|
* and unmount operations, at the least. The check
|
|
* for vfsops available is just a debugging aid.
|
|
}
|
|
Assert(vfc^.vfc_vfsops<>nil,'Filesystem %s has no vfsops');
|
|
{
|
|
* Check the mount and unmount operations.
|
|
}
|
|
vfsops:=vfc^.vfc_vfsops;
|
|
|
|
Assert(vfsops^.vfs_mount<>nil,'Filesystem %s has no mount op');
|
|
Assert(vfsops^.vfs_unmount<>nil,'Filesystem %s has no unmount op');
|
|
|
|
if (vfsops^.vfs_root=nil) then
|
|
{ Exit file system's root vnode }
|
|
vfsops^.vfs_root:=@vfs_stdroot;
|
|
if (vfsops^.vfs_quotactl=nil) then
|
|
{ quota control }
|
|
vfsops^.vfs_quotactl:=@vfs_stdquotactl;
|
|
if (vfsops^.vfs_statfs=nil) then
|
|
{ Exit file system's status }
|
|
vfsops^.vfs_statfs:=@vfs_stdstatfs;
|
|
if (vfsops^.vfs_sync=nil) then
|
|
{
|
|
* flush unwritten data (nosync)
|
|
* file systems can use vfs_stdsync
|
|
* explicitly by setting it in the
|
|
* vfsop vector.
|
|
}
|
|
vfsops^.vfs_sync:=@vfs_stdnosync;
|
|
if (vfsops^.vfs_vget=nil) then
|
|
{ convert an inode number to a vnode }
|
|
vfsops^.vfs_vget:=@vfs_stdvget;
|
|
if (vfsops^.vfs_fhtovp=nil) then
|
|
{ turn an NFS file handle into a vnode }
|
|
vfsops^.vfs_fhtovp:=@vfs_stdfhtovp;
|
|
//if (vfsops^.vfs_checkexp=nil) then
|
|
// { check if file system is exported }
|
|
// vfsops^.vfs_checkexp:=@vfs_stdcheckexp;
|
|
if (vfsops^.vfs_init=nil) then
|
|
{ file system specific initialisation }
|
|
vfsops^.vfs_init:=@vfs_stdinit;
|
|
if (vfsops^.vfs_uninit=nil) then
|
|
{ file system specific uninitialisation }
|
|
vfsops^.vfs_uninit:=@vfs_stduninit;
|
|
if (vfsops^.vfs_extattrctl=nil) then
|
|
{ extended attribute control }
|
|
vfsops^.vfs_extattrctl:=@vfs_stdextattrctl;
|
|
if (vfsops^.vfs_sysctl=nil) then
|
|
vfsops^.vfs_sysctl:=@vfs_stdsysctl;
|
|
|
|
{
|
|
* Call init function for this VFS...
|
|
}
|
|
vfc^.vfc_vfsops^.vfs_init(vfc);
|
|
|
|
Result:=0;
|
|
end;
|
|
|
|
|
|
{ Remove registration of a filesystem type }
|
|
function vfs_unregister(vfc:p_vfsconf):Integer;
|
|
var
|
|
vfsp:p_vfsconf;
|
|
error,maxtypenum:Integer;
|
|
begin
|
|
vfsp:=vfs_byname(vfc^.vfc_name);
|
|
if (vfsp=nil) then
|
|
Exit(EINVAL);
|
|
if (vfsp^.vfc_refcount<>0) then
|
|
Exit(EBUSY);
|
|
if (vfc^.vfc_vfsops^.vfs_uninit<>nil) then
|
|
begin
|
|
error:=vfc^.vfc_vfsops^.vfs_uninit(vfsp);
|
|
if (error<>0) then
|
|
Exit(error);
|
|
end;
|
|
TAILQ_REMOVE(@vfsconf,vfsp,@vfsp^.vfc_list);
|
|
maxtypenum:=VFS_GENERIC;
|
|
|
|
vfsp:=TAILQ_FIRST(@vfsconf);
|
|
while (vfsp<>nil) do
|
|
begin
|
|
if (maxtypenum<vfsp^.vfc_typenum) then
|
|
maxtypenum:=vfsp^.vfc_typenum;
|
|
|
|
vfsp:=TAILQ_NEXT(vfsp,@vfsp^.vfc_list);
|
|
end;
|
|
|
|
maxvfsconf:=maxtypenum + 1;
|
|
Result:=0;
|
|
end;
|
|
|
|
{
|
|
* Standard kernel module handling code for filesystem modules.
|
|
* Referenced from VFS_SET().
|
|
}
|
|
{
|
|
int
|
|
vfs_modevent(module_t mod, int type, void *data)
|
|
begin
|
|
p_vfsconf vfc;
|
|
int error:=0;
|
|
|
|
vfc:=(p_vfsconf )data;
|
|
|
|
switch (type) begin
|
|
case MOD_LOAD:
|
|
if (vfc)
|
|
error:=vfs_register(vfc);
|
|
break;
|
|
|
|
case MOD_UNLOAD:
|
|
if (vfc)
|
|
error:=vfs_unregister(vfc);
|
|
break;
|
|
default:
|
|
error:=EOPNOTSUPP;
|
|
break;
|
|
end;
|
|
Exit (error);
|
|
end;
|
|
}
|
|
|
|
end.
|
|
|