1 Introduction
2 Ground Rules

Building a File System
3 File Systems
4 File Content Data Structure
5 Allocation Cluster Manager
6 Exceptions and Emancipation
7 Base Classes, Testing, and More
8 File Meta Data
9 Native File Class
10 Our File System
11 Allocation Table
12 File System Support Code
13 Initializing the File System
14 Contiguous Files
15 Rebuilding the File System
16 Native File System Support Methods
17 Lookups, Wildcards, and Unicode, Oh My
18 Finishing the File System Class

The Init Program
19 Hardware Abstraction and UOS Architecture
20 Init Command Mode
21 Using Our File System
22 Hardware and Device Lists
23 Fun with Stores: Partitions
24 Fun with Stores: RAID
25 Fun with Stores: RAM Disks
26 Init wrap-up

The Executive
27 Overview of The Executive
28 Starting the Kernel
29 The Kernel
30 Making a Store Bootable
31 The MMC
32 The HMC
33 Loading the components
34 Using the File Processor
35 Symbols and the SSC
36 The File Processor and Device Management
37 The File Processor and File System Management
38 Finishing Executive Startup

Users and Security
39 Introduction to Users and Security
40 More Fun With Stores: File Heaps
41 File Heaps, part 2
42 SysUAF
43 TUser
44 SysUAF API

UCL
45 Shells and UCL

Glossary/Index


Download sources
Download binaries

Native File System Support Methods

In this article, we continue to examine the methods of the UOS Native File system. You may notice that the source code is formatted slightly differently than in the past. Comments are in italic fuschia, keywords are bolded, string literals are blue, and numeric literals are red. I find it a little easier to read now that I'm accustomed to viewing it this way.

The constructor and destructor for our class are as follows:

// Constructors and destructors...

constructor TUOS_Native_File_System.Create ;

begin
    inherited Create ;

    Open_Files := TList.Create ;
end ;


destructor TUOS_Native_File_System.Destroy ;

begin
    Open_Files.Free ;
    Open_Files := nil ;
    if( _Root <> nil ) then
    begin
        _Root.Free ;
        _Root := nil ;
    end ;
    if( _Store_Folder <> nil ) then
    begin
        _Store_Folder.Free ;
        _Store_Folder := nil ;
    end ;

    inherited Destroy ;
end ;

Open_Files is a list of any files that are currently open. We don't want to have two separate UOS Native File class instances for the same class, because the class caches the file header and ACM. Have multiple classes potentially updating the same file header and allocate chains could result in file corruption, although it wouldn't likely corrupt the file system itself. So, our constructor creates the list and the destructor frees it. We'll talk about the _Root and _Store_Folder instance data in a bit.

Next, some internal utility methods that we will be calling from other methods:

// Internal utility methods...

function TUOS_Native_File_System.Buffer : PChar ;

begin
    if( _Buffer = nil ) then
    begin
        _Buffer := allocmem( Store.Min_Storage ) ;
    end ;
    Result := _Buffer ;
end ;

This method defers creation of our buffer until it is needed. When called, if the buffer isn't allocated, the method allocates it.

procedure TUOS_Native_File_System.Get_Header ;

var P : TStore_Address64 ;

begin
    if( _Header.ID = 0 ) then
    begin
        Store.Get_Root( Buffer^ ) ;
        move( Buffer[ 16 ], P, sizeof( P ) ) ;
        Store.Read( P, Store.Min_Storage, Buffer^ ) ;
        move( Buffer[ 0 ], _Header, sizeof( _Header ) ) ;
        reallocmem( _Buffer, _Header.Folder_Clustersize ) ;
    end ;
end ;

This method reads the fils system store header into an instance header variable, if this is the first time it is accessed. It makes sure our internal buffer matches the folder clustersize for this store, which is always the same size or larger than the store clustersize.

function TUOS_Native_File_System.Root : TUOS_Native_File ;

begin
    if( _Root = nil ) then
    begin
        _Root := TUOS_Native_File.Create ;
        _Root.Store := _Store ;
        Get_Header ;
        _Store.Read( _Header.Root, sizeof( _Root.Header ), Buffer^ ) ;
        move( Buffer^, _Root.Header, sizeof( _Root.Header ) ) ;
    end ;
    Result := _Root ;
end ;


function TUOS_Native_File_System.Store_Folder : TUOS_Native_File ;

begin
    if( _Store_Folder = nil ) then
    begin
        _Store_Folder := Create_File_Instance( Root, 0 ) ;
        Root.Read( 0, 0, sizeof( _Store_Folder.Header ), Buffer^ ) ;
        move( Buffer^, _Store_Folder.Header, sizeof( _Store_Folder.Header ) ) ;
    end ;
    Result := _Store_Folder ;
end ;

These methods return a UOS native file instance of the root folder and the store folder, respectively. Since these two file folders are used so often, once we open them we keep them open fot the rest of the file system instance's existence.

function TUOS_Native_File_System.Create_File_Instance( Parent : TUOS_Native_File ;
    Index : cardinal ) : TUOS_Native_File ;

begin
    Result := TUOS_Native_File.Create ;
    Result.Store := _Store ;
    Result.Directory_Index := Index ;
    Result.Directory := Parent ;
    if( Parent <> nil ) then
    begin
        Parent.Attach ;
    end ;
end ;

This method creates an instance of a UOS Native file given a parent folder and an index of the file's header within that folder.

function TUOS_Native_File_System.Get_Store_File_Index( Index : integer ) : TUOS_Native_File ;

begin
    Result := Create_File_Instance( _Store_Folder, Index ) ;
    Store_Folder.Read( 0, Index * sizeof( Result.Header ), sizeof( Result.Header ), Buffer^ ) ;
    move( Buffer^, Result.Header, sizeof( Result.Header ) ) ;
end ;


function TUOS_Native_File_System.Get_String( Index : cardinal ) : string ;

begin
    // Setup...
    Result := '' ;
    if( ( Index = 0 ) or odd( Index ) ) then
    begin
        exit ;
    end ;

    if( _String_Table = nil ) then
    begin
        _String_Table := TUOS_String_Table.Create ;
        _String_Table.Set_AT_File( Get_Store_File_Index( 0 ) ) ;
        _String_Table.Set_Index_File( Get_Store_File_Index( 1 ) ) ;
        _String_Table.Set_Data_File( Get_Store_File_Index( 2 ) ) ;
    end ;

    Result := _String_Table.Get_String( Index ) ;
end ;

Besides the root and \Store folders, the string table files are the only other files that our class always has open. We know from the Init method that the string AT file is the first header in the Store folder, followed by the string Index and then the string data files. So when we need a string, we call this method which sets up the string table instance, if it hasn't been. It does this by loading the three string table files and passing them to the string table object.

The next two methods handle the mounting and dismounting of the file system on the store. A store must be mounted before we can access the files on the store.

function TUOS_Native_File_System.Mount( _Label, Password : PChar ;
    Flags : longint ) : TUnified_Exception ;

var P : TStore_Address64 ;
    UEC : TUnified_Exception ;

begin
    if( Mounted ) then
    begin
        Set_Last_Error( Create_Exception( UOS_File_System_Already_Mounted, nil ) ) ;
        Result := Last_Error ;
        exit ;
    end ;
    Set_Last_Error( nil ) ;
    if( not Valid_File_System ) then
    begin
        if( Last_Error <> nil ) then
        begin
            Set_Last_Error( Create_Exception( UOS_File_System_Invalid_File_System_Structure, nil ) ) ;
        end ;
        Result := Last_Error ;
        exit ;
    end ;

    // Mark the store as dirty and mounted...
    Store.Read_Data( Buffer[ 0 ], P, sizeof( _Header ), UEC ) ;
    if( UEC <> nil ) then
    begin
        Set_Last_Error( Create_Exception( UOS_File_System_Read_Error, UEC ) ) ;
        Result := Last_Error ;
        exit ;
    end ;
    _Header.Flags := _Header.Flags or UOSFSF_Dirty ;
    move( _Header, Buffer[ 0 ], sizeof( _Header ) ) ;
    Store.Write_Data( Buffer[ 0 ], P, sizeof( _Header ), UEC ) ;
    if( UEC <> nil ) then
    begin
        Set_Last_Error( Create_Exception( UOS_File_System_Write_Error, UEC ) ) ;
        Result := Last_Error ;
        exit ;
    end ;

    Mounted := True ;
    Result := Last_Error ;
end ; // TUOS_Native_File_System.Mount

First, we make sure we aren't already mounted. Second, we verify that we have a valid file system on the store. Third, we verify that the store isn't dirty. We exit with an exception if any of these checks fail. Next we set the UOSFS_Dirty flag in the fils system header. Finally we set our internal mounted flag for quick checks in other methods.

function TUOS_Native_File_System.Dismount : TUnified_Exception ;

var P : TStore_Address64 ;
    UEC : TUnified_Exception ;

begin
    // Sanity check...
    if( not Mounted ) then
    begin
        Set_Last_Error( Create_Exception( UOS_File_System_Not_Mounted, nil ) ) ;
        Result := Last_Error ;
        exit ;
    end ;

    // Close out our persistent file objects...
    if( _Root <> nil ) then
    begin
        _Root.Free ;
        _Root := nil ;
    end ;
    if( _Store_Folder <> nil ) then
    begin
        _Store_Folder.Free ;
        _Store_Folder := nil ;
    end ;
    if( _String_Table <> nil ) then
    begin
        _String_Table.Free ;
        _String_Table := nil ;
    end ;

    // Write up-to-date allocated table
    Store.Flush_Allocation_Table ;

    // Mark us as clean and dismounted...
    Mounted := False ;
    Store.Read_Data( Buffer[ 0 ], P, sizeof( _Header ), UEC ) ;
    if( UEC <> nil ) then
    begin
        Set_Last_Error( Create_Exception( UOS_File_System_Read_Error, UEC ) ) ;
        Result := Last_Error ;
        exit ;
    end ;
    if( ( _Header.Flags and UOSFSF_Dirty ) <> 0 ) then
    begin
        Set_Last_Error( Create_Exception( UOS_File_System_Is_Dirty, UEC ) ) ;
        Result := Last_Error ;
        exit ;
    end ;
    _Header.Flags := _Header.Flags and ( not UOSFSF_Dirty ) ;
    move( _Header, Buffer[ 0 ], sizeof( _Header ) ) ;
    Store.Write_Data( Buffer[ 0 ], P, sizeof( _Header ), UEC ) ;
    if( UEC <> nil ) then
    begin
        Set_Last_Error( Create_Exception( UOS_File_System_Write_Error, UEC ) ) ;
        Result := Last_Error ;
        exit ;
    end ;

    Set_Last_Error( nil ) ;
    Result := nil ;
end ; // TUOS_Native_File_System.Dismount

The first thing is to make sure we are actually mounted. Then we close our special-case files. Next we flush the allocation table out to the store. Then we clear our internal Mounted flag. Finally, we clear the UOSFS_Dirty flag in the file system header.

We will stop here since the next article will be somewhat lengthy - we will examine the file lookup process, and related issues.