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

Terminal I/O
45 Shells and UCL
46 UOS API, the Application Side
47 UOS API, the Executive Side
48 I/O Devices
49 Streams
50 Terminal Output Filters
51 The TTerminal Class
52 Handles
53 Putting it All Together
54 Getting Terminal Input
55 QIO
56 Cooking Terminal Input
57 Putting it all together, part 2
58 Quotas and I/O

UCL
59 UCL Basics
60 Symbol Substitution
61 Command execution
62 Command execution, part 2
63 Command Abbreviation
64 ASTs
65 Expressions, Part 1
66 Expressions, Part 2: Support code
67 Expressions, part 3: Parsing
68 SYS_GETJPIW and SYS_TRNLNM
69 Expressions, part 4: Evaluation

UCL Lexical Functions
70 PROCESS_SCAN
71 PROCESS_SCAN, Part 2
72 TProcess updates
73 Unicode revisted
74 Lexical functions: F$CONTEXT
75 Lexical functions: F$PID
76 Lexical Functions: F$CUNITS
77 Lexical Functions: F$CVSI and F$CVUI
78 UOS Date and Time Formatting
79 Lexical Functions: F$CVTIME
80 LIB_CVTIME
81 Date/Time Contexts
82 SYS_GETTIM, LIB_Get_Timestamp, SYS_ASCTIM, and LIB_SYS_ASCTIM
83 Lexical Functions: F$DELTA_TIME
84 Lexical functions: F$DEVICE
85 SYS_DEVICE_SCAN
86 Lexical functions: F$DIRECTORY
87 Lexical functions: F$EDIT and F$ELEMENT
88 Lexical functions: F$ENVIRONMENT
89 SYS_GETUAI
90 Lexical functions: F$EXTRACT and F$IDENTIFIER
91 LIB_FAO and LIB_FAOL
92 LIB_FAO and LIB_FAOL, part 2
93 Lexical functions: F$FAO
94 File Processing Structures
95 Lexical functions: F$FILE_ATTRIBUTES
96 SYS_DISPLAY
97 Lexical functions: F$GETDVI
98 Parse_GetDVI
99 GetDVI
100 GetDVI, part 2
101 GetDVI, part 3
102 Lexical functions: F$GETJPI
103 GETJPI
104 Lexical functions: F$GETSYI
105 GETSYI
106 Lexical functions: F$INTEGER, F$LENGTH, F$LOCATE, and F$MATCH_WILD
107 Lexical function: F$PARSE
108 FILESCAN
109 SYS_PARSE
110 Lexical Functions: F$MODE, F$PRIVILEGE, and F$PROCESS
111 File Lookup Service
112 Lexical Functions: F$SEARCH
113 SYS_SEARCH
114 F$SETPRV and SYS_SETPRV
115 Lexical Functions: F$STRING, F$TIME, and F$TYPE
116 More on symbols
117 Lexical Functions: F$TRNLNM
118 SYS_TRNLNM, Part 2
119 Lexical functions: F$UNIQUE, F$USER, and F$VERIFY
120 Lexical functions: F$MESSAGE
121 TUOS_File_Wrapper
122 OPEN, CLOSE, and READ system services

UCL Commands
123 WRITE
124 Symbol assignment
125 The @ command
126 @ and EXIT
127 CRELNT system service
128 DELLNT system service
129 IF...THEN...ELSE
130 Comments, labels, and GOTO
131 GOSUB and RETURN
132 CALL, SUBROUTINE, and ENDSUBROUTINE
133 ON, SET {NO}ON, and error handling
134 INQUIRE
135 SYS_WRITE Service
136 OPEN
137 CLOSE
138 DELLNM system service
139 READ
140 Command Recall
141 RECALL
142 RUN
143 LIB_RUN
144 The Data Stream Interface
145 Preparing for execution
146 EOJ and LOGOUT
147 SYS_DELPROC and LIB_GET_FOREIGN

CUSPs and utilities
148 The I/O Queue
149 Timers
150 Logging in, part one
151 Logging in, part 2
152 System configuration
153 SET NODE utility
154 UUI
155 SETTERM utility
156 SETTERM utility, part 2
157 SETTERM utility, part 3
158 AUTHORIZE utility
159 AUTHORIZE utility, UI
160 AUTHORIZE utility, Access Restrictions
161 AUTHORIZE utility, Part 4
162 AUTHORIZE utility, Reporting
163 AUTHORIZE utility, Part 6
164 Authentication
165 Hashlib
166 Authenticate, Part 7
167 Logging in, part 3
168 DAY_OF_WEEK, CVT_FROM_INTERNAL_TIME, and SPAWN
169 DAY_OF_WEEK and CVT_FROM_INTERNAL_TIME
170 LIB_SPAWN
171 CREPRC
172 CREPRC, Part 2
173 COPY
174 COPY, part 2
175 COPY, part 3
176 COPY, part 4
177 LIB_Get_Default_File_Protection and LIB_Substitute_Wildcards
178 CREATESTREAM, STREAMNAME, and Set_Contiguous
179 Help Files
180 LBR Services
181 LBR Services, Part 2
182 LIBRARY utility
183 LIBRARY utility, Part 2
184 FS Services
185 FS Services, Part 2
186 Implementing Help
187 HELP
188 HELP, Part 2
189 DMG_Get_Key and LIB_Put_Formatted_Output
190 LIBRARY utility, Part 3
191 Shutting Down UOS
192 SHUTDOWN
193 WAIT
194 SETIMR
195 WAITFR and Scheduling
196 REPLY, OPCOM, and Mailboxes
197 REPLY utility
198 Mailboxes
199 BRKTHRU
200 OPCOM
201 Mailbox Services
202 Mailboxes, Part 2
203 DEFINE
204 CRELNM
205 DISABLE
206 STOP
207 OPCCRASH and SHUTDOWN
208 APPEND
209 APPEND and CONTINUE
210 CREATE
211 CREATE/DIRECTORY and LIB_CREATE_DIR

Glossary/Index


Downloads

CREATE/DIRECTORY and LIB_CREATE_DIR

In this article we will look at extending the CREATE CUSP to handle the /DIRECTORY qualifier. CREATE/DIRECTORY is a unique command from CREATE on VMS. Most operating systems would have two separate commands, or CUSPS, for these different operations. VMS, instead, has different qualifiers for the CREATE command which perform the different operations. When using /DIRECTORY, there are a separate set of qualifiers than when not using /DIRECTORY. Granted, there are a few switches in common, but essentially CREATE and CREATE/DIRECTORY are separate commands with different qualifiers and different functions. So we must add support for this to the CREATE CUSP.

This is the opposite of the Unix/Linux approach of several small utilities that each do a single function. That is an approach I generally agree with, but we are trying to follow the VMS specification here. Fortunately, in this case, the CUSP is so simple that the size of the program won't be very large even after adding the /DIRECTORY support.

First the documentation and then the code.

CREATE/DIRECTORY
Creates one or more directories.

Format

CREATE/DIRECTORY directoryspec{,...}

Arguments

directoryspec
The specification for a directory to create. Multiple directories can be specified. If the device isn't specified, the user's default device is used. Wildcards are not permitted.

It is possible to create a series of nested directories with a single command. For example,
CREATE/DIRECTORY x\y\z
This command creates directory x, if that directory doesn't already exist. Then if subdirectory y doesn't exist under directory x, it is then created. And then so on to directory z.

Qualifiers

/ALLOCATION=n

Pre-extends the directory to have room for n files in the directory. This can be useful for directories that are expected to contain many files to increase system performance by allocating the space ahead of time instead of multiple directory extensions as files are added later.

The effect of this switch depends upon the file system on the device. On a native UOS file system, the space taken up by the directory will be roughly equal to 512 bytes times n. On some file systems, there may not be any effect.

/{NO}LOG
Displays the file specification as each directory is created.
/OWNER=owner
Assigns the specified user as owner of the directory. The owner may be a user name or a UIC. SYSPRV is required to specify another user. If no owner is specified, then if the user creating the directory has SYSPRV, the created directory has the same owner as its parent; otherwise the user creating the directory is marked as the owner.
/PROTECTION(ownership[:access][,...])
Specified protection for the directory. "ownership" is one of the following:
ValueMeaning
SSystem
OOwner
GGroup
WWorld
"access" is one or more of the following:
ValueMeaning
RRead
WWrite
EExecute
DDelete
If a value is not specified for each access category (or if /PROTECTION is not used), the protection is assigned from the parent directory (minus Delete access).

Example


$ CREATE/DIRECTORY MYDIR/PROTECTION(W:R)
MYDIR is created as a subdirectory under the default directory. It is created with the ability for anyone on the system to see the files in the directory.


const UI_File =  'object Main:rectangle' + CR +
            '  top = 2' + CR +
            '  object olabel:label' + CR +
            '    text = "Output file(s):"' + CR +
            '    top = $margin' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object output:string' + CR +
            '    hint = "Output filespec"' + CR +
            '    top = $margin' + CR +
            '    left = olabel.width + $margin' + CR +
            '    require = "_Name:"' + CR +
            '  end' + CR +
            '' +
            '  object Options:rectangle' + CR +
            '    object label3:label' + CR +
            '      text = "User:"' + CR +
            '    end' + CR +
            '    object Owner:string' + CR +
            '      text = ""' + CR +
            '      hint = "User name/UIC"' + CR +
            '      left = label3.left + label3.width' + CR +
            '    end' + CR +
            '    object label5:label' + CR +
            '      text = "File protections:"' + CR +
            '    end' + CR +
            '    object Protection:string' + CR +
            '      text = "false"' + CR +
            '      hint = "Protection for output file(s)"' + CR +
            '      left = label5.left + label5.wigth' + CR +
            '    end' + CR +
            '    object Log:boolean' + CR +
            '      text = "false"' + CR +
            '      falsehint = "Click to display create operations"' + CR +
            '      truehint = ' +
	        '"Click to disable display of create operations"' + CR +
            '    end' + CR +
            'end' ;

const UI_Directory =
            'object Main:rectangle' + CR +
            '  top = 2' + CR +
            '  object olabel:label' + CR +
            '    text = "Directory name:"' + CR +
            '    top = $margin' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object output:string' + CR +
            '    hint = "Directory specification"' + CR +
            '    top = $margin' + CR +
            '    left = olabel.width + $margin' + CR +
            '    require = "_Name:"' + CR +
            '  end' + CR +
            '' +
            '  object Options:rectangle' + CR +
            '    object label2:label' + CR +
            '      text = "Allocation:"' + CR +
            '    end' + CR +
            '    object Allocation:integer' + CR +
            '      text = ""' + CR +
            '      hint = "Initial allocation"' + CR +
            '      left = label2.left + label2.width' + CR +
            '    end' + CR +
            '    object label3:label' + CR +
            '      text = "User:"' + CR +
            '    end' + CR +
            '    object Owner:string' + CR +
            '      text = ""' + CR +
            '      hint = "User name/UIC"' + CR +
            '      left = label3.left + label3.width' + CR +
            '    end' + CR +
            '    object label5:label' + CR +
            '      text = "Directory protections:"' + CR +
            '    end' + CR +
            '    object Protection:string' + CR +
            '      text = "false"' + CR +
            '      hint = "Protection for directory"' + CR +
            '      left = label5.left + label5.wigth' + CR +
            '    end' + CR +
            '    object Log:boolean' + CR +
            '      text = "false"' + CR +
            '      falsehint = "Click to display create operations"' + CR +
            '      truehint = ' +
                '"Click to disable display of create operations"' + CR +
            '    end' + CR +
            'end' ;
We renamed the existing UI specification to UI_File in the CREATE CUSP and added a new UI specification (UI_Directory) for the /DIRECTORY form of the command.

    // Handle different forms of command...
    Create_Directory := False ;
    if( Parse_Switch( 'DIR|ECTORY', '', C, S ) = 1 ) then // CREATE/DIRECTORY
    begin
        if( S <> '' ) then
        begin
            Result := UUI_NOSWITCH ;
            exit ;
        end ;
        Create_Directory := True ;
    end ;

    // Processing...
    UUI := TUUI.Create ;
    if( Create_Directory ) then
    begin
        UUI.Definition := UI_Directory ;
    end else
    begin
        UUI.Definition := UI_File ;
    end ;
    UUI.Command_Line := PChar( C ) ;
    if( UUI.Execute( UUI.Definition, Standalone ) ) then
We modify the beginning of the _Create procedure to choose which UI definition to use depending on the presence of the /DIRECTORY qualifier (or lack thereof). The new Create_Directory boolean is set for later use.

        Outf := trim( Component.Text ) ;
        if( Outf = '' ) then
        begin
            if( Create_Directory ) then
            begin
                Outf := UUI.Get_Parameter( 'Create', 'Directory name' ) ;
            end else
            begin
                Outf := UUI.Get_Parameter( 'Create', 'Output file' ) ;
            end ;
        end ;
We update the handling of the case when the user must be prompted if no output file/directory specification is provided.

    if( Create_Directory ) then
    begin
        Component := UUI.Get_Component_By_Name( 'Allocation' ) ;
        S := Component.Text ;
        if( S = '' ) then
        begin
            Allocation := -1 ;
        end else
        begin
            Allocation := strtoint( Component.Text ) ;
        end ;
    end ;
At the end of the qualifier handling, we add handling for /ALLOCATION if Create_Directory is true.

            for I := 0 to SL.Count - 1 do // For each file in the list
            begin
                S := SL[ I ] ; // Get next filespec
                FileProt := DefProt ;

                if( Create_Directory ) then
                begin
                    C := '' ;
                    Enabled := 0 ;
                    if( Use_Protection ) then
                    begin
                        Enabled := -1 ;
                    end ;

                    // Iterate through all directories in path...
                    while( S <> '' ) do
                    begin
                        P := pos( '\', S ) ;
                        if( P = 0 ) then
                        begin
                            P := length( S ) + 1 ;
                        end ;
                        if( C <> '' ) then
                        begin
                            C := C + '\' ;
                        end ;
                        C := C + copy( S, 1, P - 1 ) ;
                        S := copy( S, P + 1, length( S ) ) ;
                        if( copy( C, length( C ), 1 ) <> ':' ) then
                        begin
                            if( not File_Exists( C ) ) then
                            begin
                                Result := Create_Dir( C, UIC, Enabled, 
                                    Protection, -1,
                                    0, Allocation ) ;
                                if( Result <> 0 ) then
                                begin
                                    SL.Free ;
                                    exit ;
                                end ;
                            end ;
                        end ;
                    end ; // while( S <> '' )
                end else
                begin
In the loop where we process each output specification, we add an if block for handling the directory creation. If /PROTECTION was specified, we set the Enabled to mask all bits, otherwise we set it to 0 to indicate that the default protection will be used. Next we iterate through the directories specified in this output specification. If the directory doesn't already exist, we call the Create_Dir function to create the directory. Essentially, we are moving one level of path from S to C in each loop iteration, making sure that the directories are created from topmost to bottom-most.

function Create_Dir( const Dir : string ; UIC : int64 = 0 ; 
    Enabled : int64 = 0 ; Protection : int64 = 0 ; Versions : int64 = 0 ;
    Volume : int64 = -1 ; Allocation : int64 = -1 ) : int64 ;

var P : string ;
    SRB : TSRB ;

begin
    P := Dir ;
    Set_String( P, SRB ) ;
    Result := LIB_CREATE_DIR( int64( @SRB ), int64( @UIC ), 
        int64( @Enabled ), int64( @Protection ), int64( @Versions ), 
        int64( @Volume ), int64( @Allocation ) ) ;
end ;
This function is added to PasStarlet which merely wraps the Pascal parameters and passes them onto the LIB_CREATE_DIR function.


LIB_CREATE_DIR

Creates a directory.

Format

LIB_CREATE_DIR name {, uic} {, enabled} {, protection} {, versions} {, volume} {, allocation}

Arguments

name
A pointer to an SRB which points to a directory specification string.

uic
An address pointing to a 64-bit integer containing the User Identification Code (UIC) to be used as the owner of the newly created directory. Ignored if 0 or if it points to a value of 0. The caller must have SYSPRV privilege to use a UIC that is different from his own. If a UIC isn't specified, the UIC of the parent directory is used if the caller has SYSPRV, or the UIC of the caller otherwise.

enabled
A pointer to a 64-bit integer containing a bitmask that defines which protection bits are specified in the protection argument.

protection
A pointer to a 64-bit integer containing a bitmask that defines the protection codes to assign to the newly created directory. If enabled is 0, this is ignored. Only the bits specified in enabled are applied to the protection code. The following bit values define protections: F000Mask for all world protections.
MnuemonicHex ValueDescription
PROTECTION_OWNER_READ01Allow owner to read.
PROTECTION_OWNER_WRITE02Allow owner to write to directory.
PROTECTION_OWNER_DELETE04Allow owner to delete.
PROTECTION_OWNER_MASK0FMask for all of owner protections.
PROTECTION_GROUP_READ10Allow group members to read.
PROTECTION_GROUP_WRITE20Allow group members to write to directory.
PROTECTION_GROUP_DELETE40Allow group members to delete.
PROTECTION_GROUP_MASKF0Mask for all group protections.
PROTECTION_SYSTEM_READ100Allow system to read.
PROTECTION_SYSTEM_WRITE200Allow system to write.
PROTECTION_SYSTEM_DELETE400Allow system to delete.
PROTECTION_SYSTEM_MASKF00Mask for all system protections.
PROTECTION_WORLD_READ1000Allow anyone to read.
PROTECTION_WORLD_WRITE2000Allow anyone to write to directory.
PROTECTION_WORLD_DELETE4000Allow anyone to delete.
PROTECTION_WORLD_MASK

versions
A pointer to a 64-bit integer containing the number of file versions for this directory. 0 indicated unlimited.

volume
A pointer to a 64-bit integer containing the volume number of a multivolume set (such as a public store) the directory is to be created in. If -1, the volume number is chosen by the operating system.

allocation
A pointer to a 64-bit integer containing the number of file entries to be preallocated in the directory, if 0, the initial size of the directory is defaulted by the file system.

Description

This service creates a new directory. The owner, protection, volume number, max versions, and initial size can be specified.

Condition Values Returned

SS_NORMAL Normal completion of service.
SS_NOPRIV The calling process does not have the privilege to create the directory on the specified device or under the parent directory, or a different owner than the caller was used but did not have SYSPRV privilege.
UOSErr_Device_Not_File_Structured The specified device is not a file structured device.
UOSErr_Device_Not_Found The specified device does not exist.
UOSErr_Device_Not_Mounted The specified device is not mounted.
UOSErr_Duplicate_Name The directory already exists.
UOSErr_Memory_Address_Error Could not access a parameter.
UOSErr_Missing_Name The directory specification was null.
UOSErr_No_Device_Specified A device specification was missing.


        UOS_FIP_Create_Dir:
            begin
                UE := Enter_System_Call( Request, SReq, PID, MMC, 
                   sizeof( TInteger_Request ) - sizeof( SReq ), Address ) ;
                if( UE <> nil ) then
                begin
                    Set_Last_Error( UE ) ;
                    exit ;
                end ;
                try
                    String1I6_Request := PString1I6_Request( Address ) ;

                    Status := Create_Directory( String1I6_Request.SRB,
                        String1I6_Request.Integer1, 
                        String1I6_Request.Integer2,
                        String1I6_Request.Integer3, 
                        String1I6_Request.Integer4,
                        String1I6_Request.Integer5, 
                        String1I6_Request.Integer6 ) ;
                    Write_User( Kernel, Kernel.PID, 
                        String1I6_Request.Request.Status, sizeof( Status ), 
                        Status ) ;
                finally
                    Exit_System_Call( integer( String1I6_Request ), PID, 
                        MMC, sizeof( TString1I6_Request ) - sizeof( SReq ) ) ;
                end ;
            end ;
This code is added to the TUOS_FiP.API method.

function TUOS_FiP.Create_Directory( Name : TSRB ; UIC, Enabled,
    Protection, Versions, Volume, Allocation : int64 ) : int64 ;

var Device : TDevice ;
    DirName : string ;
    Dummy : integer ;
    Info : TUOS_File_Info ;
    PID : TPID ;
    Prot : int64 ;
    S : TUOS_String ;
    Status : longint ;
    SN : string ;
    U : TUnified_Exception ;
    User_UIC : int64 ;

begin
    // Get parameters...
    PID := Kernel.PID ;
    S := Get_User_String( Kernel, PID, Name, status ) ;
    if( status = UE_Error ) then
    begin
        Result := 
            Generate_Exception( UOSErr_Memory_Address_Error ).Get_Error ;
        exit ;
    end ;
    Get_User_Data( Kernel, PID, UIC, sizeof( UIC ), UIC, Status ) ;
    if( Status <> 0 ) then
    begin
        Result := 
            Generate_Exception( UOSErr_Memory_Address_Error ).Get_Error ;
        exit ;
    end ;
    Get_User_Data( Kernel, PID, Enabled, sizeof( Enabled ), Enabled, 
        Status ) ;
    if( Status <> 0 ) then
    begin
        Result := 
            Generate_Exception( UOSErr_Memory_Address_Error ).Get_Error ;
        exit ;
    end ;
    Get_User_Data( Kernel, PID, Protection, sizeof( Protection ), 
        Protection, Status ) ;
    if( Status <> 0 ) then
    begin
        Result := 
            Generate_Exception( UOSErr_Memory_Address_Error ).Get_Error ;
        exit ;
    end ;
    Get_User_Data( Kernel, PID, Versions, sizeof( Versions ), Versions, 
        Status ) ;
    if( Status <> 0 ) then
    begin
        Result := 
            Generate_Exception( UOSErr_Memory_Address_Error ).Get_Error ;
        exit ;
    end ;
    Get_User_Data( Kernel, PID, Volume, sizeof( Volume ), Volume, Status ) ;
    if( Status <> 0 ) then
    begin
        Result := 
            Generate_Exception( UOSErr_Memory_Address_Error ).Get_Error ;
        exit ;
    end ;
    Get_User_Data( Kernel, PID, Allocation, sizeof( Allocation ), 
        Allocation, Status ) ;
    if( Status <> 0 ) then
    begin
        Result := 
            Generate_Exception( UOSErr_Memory_Address_Error ).Get_Error ;
        exit ;
    end ;

    // Validate parameters...
    DirName := As_UTF8( S )  ;
    if( DirName = '' ) then
    begin
        Result := Generate_Exception( UOSErr_Missing_Name ).Get_Error ;
        exit ;
    end ;
    Dummy := pos( ':', DirName ) ;
    if( Dummy < 2 ) then
    begin
        Result := 
            Generate_Exception( UOSErr_No_Device_Specified ).Get_Error ;
        exit ;
    end ;
    User_UIC := USC.Get_Process_Info( PID, JPI_UIC ) ;
This new method in the TUOS_FiP class does the work of creating a directory in response to the system call. First we obtain the various parameters from the passed addresses. All but one are integer values, so we simply replace the passed-in address with the value from that address. Then we validate that the directory specification isn't null and includes a device.

    // Get device and its file system...
    Device := Devices.TDevice( Get_Device( copy( DirName, 1, Dummy ) ) ) ;
    if( Device = nil ) then
    begin
        Result := Generate_Exception( UOSErr_Device_Not_Found ).Get_Error ;
        exit ;
    end ;
    if( not Device.Mounted ) then
    begin
        Result := 
            Generate_Exception( UOSErr_Device_Not_Mounted ).Get_Error ;
        exit ;
    end ;
    if( Device.FS = nil ) then
    begin
        Result := 
          Generate_Exception( UOSErr_Device_Not_File_Structured ).Get_Error;
        exit ;
    end ;

    // Check for existing file...
    if( File_Exists( Kernel.PID, DirName ) ) then // File already exists
    begin
        Result := Generate_Exception( UOSErr_Duplicate_Name ).Get_Error ;
        exit ;
    end ;

    // Strip device name and normalize filename...
    DirName := trim( copy( DirName, Dummy + 1, length( DirName ) ) ) ;
    if( copy( DirName, 1, 1 ) <> '\' ) then
    begin
        DirName := '\' + DirName ;
    end ;
Next we get the device instance for the specified device, returning errors if it is non-existant, not mounted, or non-file-structured. We then verify that the directory doesn't already exist, returning an error if it does. Next we trim off the device name from the directory specification and make sure the path begins with a separator.

    // Check parent directory protection...
    SN := Parent_Path( DirName ) ;
    Info := Device.FS.Get_File_Info( PChar( SN ), 0 ) ;
    if( not USC.Validate_Protection( Kernel.PID, FAB_V_PUT, Info.Owner, 
        Info.Flags, Info.ACL ) ) then
    begin
        Result := 
            Generate_Exception( UOSErr_Protection_Violation ).Get_Error ;
        exit ; // No permission to write to parent directory
    end ;

    // Determine owner for new directory...
    if( ( Kernel.USC.Get_Process_Info( Kernel.PID, JPI_CURPRIV ) 
        and SYSPRV ) = 0 
      ) then // No SYSPRV
    begin
        if( UIC = 0 ) then // Use default (user's UIC)
        begin
            UIC := User_UIC ;
        end ;
        if( UIC <> User_UIC ) then // Creating with a different owner
        begin
             Result := 
               Generate_Exception( UOSErr_Protection_Violation ).Get_Error;
             exit ; // No permission
        end ;
    end else
    begin
        if( UIC = 0 ) then // Use default (parent directory's UIC)
        begin
            UIC := Info.Owner ;
        end ;
    end ;
Next, we get information about the parent directory and validate that the caller isn't violating any protections by creating a directory in the parent, exiting with an error if so. Next we determine who the owner of the newly created directory will be, based on the rules described in the documentation.

    // Determine protection for new direcotry...
    Prot := ( Info.Flag shr 16 ) and ( not PROTECTION_OWNER_DELETE )
                                  and ( not PROTECTION_GROUP_DELETE )
                                  and ( not PROTECTION_SYSTEM_DELETE )
                                  and ( not PROTECTION_WORLD_DELETE ) ; 
                                  
                                  { Default is the parent dir protection, 
                                    minus delete }
    Enabled := Enabled and ( not PROTECTION_MASK ) ;
    Protection := Protection and ( not PROTECTION_MASK ) ;
    Prot := Prot and ( not Enabled ) ; // Clear explicitly set flags
    Prot := Prot or ( Protection and not Enabled ) ; // Set flags
Next we determine the protections to apply to the new directory. We start with a mask of the parent directory protections minus the deletes (note that since the protection bit field is located in the high bits of the file flags, we have to shift them into position to match up with the constants). We clear any bits in Enabled that are outside of the field for protection bits. Then we mask out any bits in the passed protection with what is in the Enabled parameter. Finally, we merge in the new set of protection flags.

    // Create directory file...
    Info.Size := 0 ;
    Info.EOF := 0 ;
    Info.Uncompressed_Size := 0 ;
    Info.Record_Size := 0 ;
    Info.Creation := HAL.Timestamp ;
    Info.Last_Modified := FAF_DIRECTORY ;
    Info.Last_Backup := 0 ;
    Info.Last_Access := FAF_DIRECTORY ;
    Info.Expiration := 0 ;
    Info.Creator := UIC ;
    Info.Owner := Info.Creator ;
    Info.Flags := ( Prot shl 16 ) or FAF_DIRECTORY ;
    Info.Version_Limit := Versions ;
    Info.Location := 0 ;
    Info.ACL := 0 ;
    Info.Privileges := 0 ;
    U := Device.FS.Create_File( PChar( DirName ), Info ) ;
    if( U <> nil ) then
    begin
        Set_Last_Error( U ) ;
        Result := U.Get_Error ;
        exit ;
    end ;

    // Now pre-extend directory
    Device.FS.Extend_Directory( PChar( DirName ), Allocation ) ;
end ; // TUOS_FiP.Create_Directory
Next, we create the new directory based on the passed-in parameters, exiting if there is an error. Then finally we tell the file system to extend the directory. We tell the file system to do this rather than us doing it because that is an implementation detail specific to whatever file system is on the device. In fact, the file system might not even do anything as a result of this call.

procedure TUOS_Native_File_System.Extend_Directory( Name : PAnsiChar ; 
    Entries : int64 ) ;

var F : TUOS_File ;
    Header : TUOS_File_Header ;
    Pos : integer ;
    N : string ;

begin
    if( Entries < 1 ) then
    begin
        exit ;
    end ;
    N := Name ;
    if( copy( N, length( N ), 1 ) <> '\' ) then
    begin
        N := N + '\' ; // Must end with separator for Get_File
    end ;
    F := Get_File( PChar( N ) ) ;
    if( F <> nil ) then
    begin
        fillchar( Header, sizeof( Header ), 0 ) ;
        Pos := F.File_Size ;
        while( Entries > 0 ) do
        begin
            dec( Entries ) ;
            F.Write( 0, Pos, sizeof( Header ), Header, 0 ) ;
            Pos := Pos + sizeof( Header ) ;
        end ;
        F.Detach ;
    end ;
end ;
This new method is added to the TUOS_File_System class and this code is the method in the TUOS_Native_File_System descendant class. For our native file system, we simply write out a number of null headers equal to the passed value.

In the next article, we will cover the DELETE CUSP.