Locating a file
When you read from a file
When you write to a file
When you delete a file

For concreteness, we will suppose that files are implemented with inodes; that file attributes including length and protection are kept in the inode; and that free blocks are kept in a free list; and that memory management uses paging. Other implementations are not very different in terms of what follows.

Note: In all the routines below, when a block is loaded into the cache, then, if the cache is full, the cache replacement algorithm must be called to remove a block from cache, and if it's dirty to write it to disk.

Process Q locates file F in directory D0

This subroutine is used whenever a file is opened.
{  Expand the relative path name to the absolute path name;
   If (the inode for F is not in memory) then {
      P = the path for D0;
      D = D0;
      loop until ((D is in cache) or (D is the root))
           D = the parent of D;     
      if (D == the root and D is not in cache) then
         read D from its known disk address to cache;
      check that Q has execute permission on D
           (compare the user of Q to the owner of D and 
            check associated permission) else exit with failure;
      loop { C = the child of D on P;
           look up the disk address of C in D;
           read the inode for C into cache;
           if (C == F) then exitloop;
           check that the user has execute permission for C;
           read C into cache;
           D = C;
         }     /* At this point the inode for F is in cache*/
    }  /* End if statement */

When you read from a file

Reading from a file has three parts: (1) Opening the file; (2) Reading; (3) Closing the file.

Process Q opens file F in directory D for reading.

{ Locate file F in directory D;
  Check that Q has read permission for F;
  Record the inode for F in Q's file table;
}       

Reading K bytes from position P in F

(We assume all K bytes are in the same block.)
 
B,O = the block and offset for position P in F;
if (B is not in the chache) {
   B0 = the inode of F;
   if (the address of B is not in B0)
    { for each indirect block BI on the path to B  {
           /* this can be directly computed from P, given the
                  structure of the inodes */
         find the address of BI in B0;
        load BI into the cache;
        B0 = BI;
      }  /* At this point B0 is the block with the address of B */
   get the address of B from B0;
   load B into the cache;
  }  /* At this point B is in the cache
read the K bytes from the cache copy of B;
}

Closing file F

 
{ Remove F from Q's file table;
  Mark all the blocks of Q as available for release from the cache;
}

When you write to a new file

Writing to a file has three parts: (1) Opening; (2) Writing; (3) Closing.

Process Q opens file F in directoy D0 for writing;

{ Locate F in D0;
  Check that Q has write permission as well as execute permission on D0.
  Get a free block from free list. Modify free list and save changes on disk.
  Create a block for inode in cache.
  Initialize attributes of F in cache copy of inode as specified 
     (or according to default).
  Write out inode to disk.
  Create entry for file F in directory D0;
  Write out modified directory to disk;
  Record inode for F in Q's file table.
}

Writing K bytes to position P in F

We assume all K bytes are in the same block, and that the file is written sequentially. Assume that if you are in the middle of writing to block B (either a data block or an indirect block) then B will be kept in the cache until you have filled B.

For simplicity, we will assume that there is at most one indirect block between the i-node and B.

 
B,O = the block and offset for position P in F;
if (B is not in the chache) {
   B0 = the inode for F;
   B1 = the block (i-node or indirect block) that should point to B;
   if (B1 is not in the cache) 
     { get a free block for B1 from free list; 
       save modified free list to disk;
       modify B0 to point to B1:
       save B0 on disk;
       choose a block in cache for B1;
     }
   get a free block for B from free list;
   save modified free list to disk;
   modify B1 to point to B;
   save B1 on disk;
   choose a block in cache for B;
  }
write K bytes at offset O in cache copy of B;
if  B is  full, or depending on write-out policy, 
    { write out B to disk;
      modify length of F as recorded in B0;
      save B0 on disk;
    }
}

Closing a new file F in directory D

{ Write out all dirty cache blocks of F; 
  Write out directory D;
  Remove F from file table;
  Mark all blocks of F as available for release from the cache
}

Deleting file F from directory D

{ Locate directory D as in opening for reading;
  Check that you have write permission on D (Note: at least in UNIX,
    you do not need write permission on F.)
  Read in the i-node of F and all indirect blocks;
  Delete F from D;
  Write out D to disk;
  Add all data blocks, all indirect blocks, and the i-node of F to
     the free list;
}