File access

From BeebWiki
Revision as of 00:24, 31 August 2024 by Jgharston (talk | contribs) (Tried different table layout.)
Jump to: navigation, search

Files have access attributes, returned and set in XY+14 in OSFILE calls. Each bit indicates different access rights to the file. However, care needs to be taken in interpreting these bits, particularly as errors have been propagated in most documentation on the matter.

Definition

Each access bit has the following definition:

   b0 - R - file can be read by owner
   b1 - W - file can be written to by owner
   b2 - E - file can be executed by owner
   b3 - L - file cannot be deleted, renamed or overwritten by owner
   b4 - R - file can be read by public
   b5 - W - file can be written to by public
   b6 - E - file can be executed by public
   b7 - L - file cannot be deleted, renamed or overwritten by public

Note that lots of documentation (including the Advanced User Guides) list these as cannot be read or written. This is wrong. Each bit is set if the corresponding access right is present.

In access strings, the owner access is on the left of any '/' character, and the public access is on the right, sometimes in lower case. For example:

   WR/R - owner read and write access, public read access only

The normal access string of WR/WR gives the access byte &33.

Read Access

Having read access means that a file can be read with LOAD, BGET, OSGBPB, *RUN and */.

The owner of a file can determine if they are allowed to read or load a file by examining the 'R' bit with:

   readable%=(access% AND 1)<>0

Write Access

Having write access means that a file can be written to with BPUT and OSGBB. Not having write access does not mean that you cannot overwrite the file with SAVE. That is controlled by the Locked access.

The owner of a file can determine if they are allowed to write to a file by examining the 'W' bit with:

   writable%=(access% AND 2)<>0

Execution Access

Having execute access means the file can be run with *RUN and */.

Most filing systems do not maintain a seperate execution access setting. If a file is readable, then it is also executable. Consequently, most filing systems either never return the 'E' bit, or always return the 'E' bit as a copy of the relevant 'R' bit.

The owner of a file can determine if they are allowed to run a file by examining both the 'R' and the 'E' bits with:

   runnable%=(access% AND 5)<>0

If the 'E' bit is set and the 'R' bit is not set, then the file cannot be loaded or read, and can only be accessed with *RUN. The owner of a file can determine if a file is run-only with:

   runonly%=(access% AND 5)=4

HADFS and HDFS maintain a seperate 'E' bit (HDFS calls it the 'X' bit). ADFS allows you to set 'E' access with the *ACCESS command to create a run-only file, but it does not reflect this in the returned file access byte. Also, ADFS does not let you set the 'E' bit with OSFILE, you can only do it with the *ACCESS command.

Locked Access

Being locked means that the file cannot be deleted, cannot be renamed, and cannot be overwritten with SAVE. Being locked does not mean the file cannot be written to with BPUT and OSGBPB. That is controlled by the Write access.

It may not be obvious, but a non-owner can never be able to delete a file. To a non-owner, a file is always locked, bit 7 is always implicitly set. However, most filing systems actually return bit 7 as an exact copy of bit 3, implying that a non-owner may be able to delete the file. You should never examine bit 7 to decide whether you can delete, rename or overwrite a file. If you do not own the file, you cannot delete it.

The owner of a file can determine if they are allowed to delete, rename or overwrite a file with:

   deletable%=(access% AND 8)<>0

Owner vs Public

The access byte holds the owner's access in the bottom four bits and the public's access in the top four bits. However, there is no way of knowing whether you actually own the file you are examining and so which bits you should look at to determine your own access. OSGBPB 6 will tell you if you own the currently selected directory, but that does not have to be the directory that the file you are examining is in.

DFS

DFS must be considered as a special case, as it completely mangles the file access byte. Also, some DFS extensions or replacements have been written using the flawed documentation propagated by likes of the Advanced User Guide.

Reading the access byte

DFS only ever returns the 'Locked' bit in bit 3. It never returns anything for the owner 'R', 'W' or 'E' bits and never returns anything for the public access bits.

   *ACCESS file   returns an access byte of &00, ie: ----
   *ACCESS file L returns an access byte of &08, ie: L---

Consequently, if reading the file access byte from DFS you must modify it before using it:

   IF dfs% THEN access%=(access% OR 3)EOR(access% DIV 4)
 or
   IF dfs% THEN access%=access% DIV 4 * 3 + 3

This changes:

   ---- to --WR    and    L--- to L--R

Writing the access byte

Even though DFS only ever returns the owner's Locked bit, when writing the access byte both the Locked bit and the Write bit set the Locked bit.

   Writing &00 (----) results in Unlocked
   Writing &02 (--W-) results in Locked
   Writing &08 (L---) results in Locked

Consequently, if writing the file access byte in DFS you must translate it before using it, and avoid writing to the 'W' bit:

   IF dfs% THEN access%=access% AND 8

This changes all access bytes to just Locked off or Locked on.

HDFS

Hierarchial DFS by Andrew Duggan implements the full Locked, Execute, Write and Read access bits, but implements them according to the flawed documentation that says cannot read/etc instead of read/etc access present. Consequently, if reading or writing access bytes on HDFS, they must be translated:

   IF FNhdfs THEN access%=(access% EOR 7)

HDFS and DFS both report themselves as filing system 4, but they use a different range of file handles. Calling FSCV with A=7 to request the handle range can be used to distinguish between them:

   DEFFNhdfs
   A%=7:A%=USR !&21E
   =(A% AND &FF00)=&12

Note that this can only be done from the I/O processor.

Directories

Conventionally, the only access that directories can have is Locked or not Locked. If any other bit is written with OSFILE, they are either ignored entirely, or they are stored, but have no effect.

ADFS returns the 'R' bits set when reading a directory's access byte. RISC OS ADFS allows all the owner access bits to be set and read with OSFILE calls.

Some filing systems and programs[1] use the 'R' bit to indicate that the directory can be read - ie, catalogued - and the 'E' bit to indicate that the directory can be searched.

Private Access

HADFS and the SJ MDFS implement a 'P'rivate bit where an object is invisible to a non-owner. HADFS returns the 'P' bit in bit 7 of the access byte, whereas the NFS cannot see the MDFS's 'P' bit. On NFS bit 7 is a copy of the owner's Locked bit.

Unless copying within the same filing system, bit 7 should be ignored with:

   access%=access% AND &7F

On RISC OS, the MDFS 'P' bit is returned in the owner's 'E' bit, and the 'P' bit is always returned set reflecting the fact that a non-owner can never delete an object.

Access permission on other systems

DOS

|              |                  |                  | RISC OS  | RISC OS  |
| Petrov DOSFS |   Sprow DOSFS    |  LanManFS[2]      | DOSFS[3] |Win95FS[4]|   OSFILE  
+--------------+------------------+------------------+----------+----------+------------+
| 1            | 1                | 1                | 1        | 1        | ->  Owner R
| NOT ReadOnly | NOT ReadOnly     | NOT ReadOnly     | 1        | 1        | ->  Owner W
| 0            | 0                | 0                | 0        | 0        | ->  Owner E
| System       | System OR Hidden | System OR Hidden | ReadOnly | ReadOnly | ->  Owner L
| 1            | 0                | 1                | 0        | 1        | -> Public R
| NOT ReadOnly | 0                | NOT ReadOnly     | 0        | 1        | -> Public W
| 0            | 0                | 0                | 0        | 0        | -> Public E
| 0            | 0                | 0                | 0        | 0        | -> Public L
|              |                  |                  | System   | System   | -> attr b8
|              |                  |                  | Hidden   | Hidden   | -> attr b9  
|              |                  |                  | Archive  | Archive  | -> attr b10

this can be done with the following code:

   acc%=&33-&22*(dosacc% AND 1)+2*(dosacc% AND 4)     :REM Petrov
   acc%=&0B-&02*(dosacc% AND 1)-8*((dosacc% AND 6)=0) :REM Sprow
   acc%=&3B-&22*(dosacc% AND 1)-8*((dosacc% AND 6)=0) :REM LanManFS
   acc%=&0B-8*(dosacc% AND 1)                         :REM DOSFS
   acc%-&3B-8*(dosacc% AND 1)                         :REM Win95FS
|              |                  |                  | RISC OS  | RISC OS  |           
| Petrov DOSFS |   Sprow DOSFS    |    LanManFS      |  DOSFS   | Win95FS  |    DOS    
+--------------+------------------+------------------+----------+----------+-------------
| ignore       | NOT Owner W      | NOT Owner W      | Owner L  | Owner L  | -> ReadOnly  
| ignore       | Owner L          | Owner L          | 0        | 0        | -> Hidden    
| ignore       | Owner L          | Owner L          | 0        | 0        | -> System    
| ignore       | 0                | 0                | 0        | 0        | -> Volume    
| ignore       | ignore           | ignore           | ignore   | ignore   | -> Directory 
| ignore       | 0                | ignore           | 0        | 0        | -> Archive   

this can be done with the following code:

   dosacc%=1-(acc% AND 2)/2 + (acc% AND 8)*0.75 :REM Sprow, LanManFS
   dosacc%=8-(acc% AND 8)/8                     :REM DOSFS, Win95FS

CP/M

|    CPMFS     |    OSFILE  
+--------------+--------------
| 1            | ->  Owner R  
| NOT ReadOnly | ->  Owner W  
| 0            | ->  Owner E  
| System       | ->  Owner L  
| 1            | -> Public R  
| NOT ReadOnly | -> Public W  
| 0            | -> Public E  
| 0            | -> Public L  

this can be done with the following code:

   acc%=&33-&22*(cpmacc% AND 1)+4*(cpmacc% AND 2)
|    OSFILE    |    CP/M   
+--------------+-------------
| NOT Owner W  | -> ReadOnly  
| Owner L      | -> System    
| ignore       | -> Archive   
| ignore       | -> f1-f8     

this can be done with the following code:

   cpmacc%=1-(acc% AND 2)/2+(acc% AND 8)/4

UNIX

| UnixFS[5] |   OSFILE  
+------------------+-----------------------------------
| Owner r          | ->   Owner R
| Owner w          | ->   Owner W
|       0          | ->   Owner E
| file: 0          | ->   Owner L
| dir: NOT World E | ->   Owner L
| World r          | ->  Public R
| World w          | ->  Public W
|       0          | ->  Public E
|       0          | ->  Public L

this can be done with the following code (probably easier with a lookup table):

   acc%=0
   acc%=acc% OR (uxacc%AND01)*4 OR (uxacc%AND002)*1 OR (uxacc%AND004)/4
   acc%=acc% OR (uxacc%AND08)*8 OR (uxacc%AND016)*2 OR (uxacc%AND032)/2
   acc%=acc% OR (uxacc%AND64)*1 OR (uxacc%AND128)/4 OR (uxacc%AND256)/16
| UnixFS file                  | UnixFS directory  | Unix    
+------------------------------+-------------------+---------------
| (Owner R AND filetype=&FE6)  |     1             | ->   Owner x  
|  Owner W                     | unchanged         | ->   Owner w  
|  Owner R                     | unchanged         | ->   Owner r  
| (Public R AND filetype=&FE6) | NOT Locked        | ->   Group x  
| Public W                     | Public W          | ->   Group w  
| Public R                     | Public R          | ->   Group r  
|        0                     | NOT Locked        | ->   World x  
| Public W                     | Public W          | ->   World w  
| Public R                     | Public R          | ->   World r  

this can be done with the following code (probably easier with a lookup table):

   uxacc%=0
   uxacc%=uxacc% OR (acc%AND01)*04 OR (acc%AND02)*1 OR (acc%AND04)/4
   uxacc%=uxacc% OR (acc%AND16)*02 OR (acc%AND32)/2 OR (acc%AND64)/8
   uxacc%=uxacc% OR (acc%AND16)*16 OR (acc%AND32)*4 OR (acc%AND64)*1
   IF type%=1:IF ftype%=&FE6:uxacc%=uxacc% OR (acc%AND01)*1 OR (acc%AND16)/2
   IF type%=2:uxacc%=((uxacc% AND &1B6) OR &49)-&48*(acc% AND 8)


References

  1. The Level 4 File Server hides directories from non-owners if the 'R' bit is unset
  2. Sprow Ethernet Module https://chrisacorns.computinghistory.org.uk/docs/Sprow/masternet.pdf
  3. From investigation with RISC OS 3.11
  4. From investigation with RISC OS 3.11
  5. TCP Protocol Suite User Guide

Jgharston 23:57, 30 October 2011 (UTC)