Subversion Repositories HelenOS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /* This file contains the heart of the mechanism used to read files.
  2.  * Read requests are split up into chunks that do not cross block
  3.  * boundaries. Each chunk is then processed in turn.
  4.  */
  5.  
  6. /* Methods:
  7.  * do_read:    perform the READ system call
  8.  * read_map:   given an inode and file position, look up its zone number
  9.  * rd_indir:   read an entry in an indirect block
  10.  */
  11.  
  12. #include <stdio.h>
  13. #include "fs.h"
  14. #include "block.h"
  15. #include "file.h"
  16. #include "fproc.h"
  17. #include "inode.h"
  18. #include "super.h"
  19. #include "param.h"
  20.  
  21.  
  22. static int read_chunk(inode_t *rip, offset_t position,
  23.         unsigned off, int chunk, char *buff);
  24.  
  25. int do_read()
  26. {
  27.    
  28.     /* Perform read(fd, buffer, nbytes) call. */
  29.    
  30.     register inode_t *rip;
  31.     register filp_t *f;
  32.     offset_t bytes_left, f_size, position;
  33.     unsigned int off, cum_io;
  34.     int oflags, r, chunk;
  35.     int regular;
  36.     mode_t mode_word;
  37.     void *old_addr;
  38.    
  39.  
  40.     /* If the file descriptor is valid, get the inode, size and mode. */
  41.     if (nbytes < 0)
  42.         return FS_EINVAL;
  43.  
  44.     if ((f = get_filp(fd)) == NIL_FILP)
  45.         return err_code;
  46.  
  47.     if (f->filp_mode == FILP_CLOSED)
  48.         return FS_EIO;
  49.      
  50.     if (nbytes == 0)
  51.         return 0;    
  52.      
  53.     position = f->filp_pos;
  54.     if (position > MAX_FILE_POS)
  55.         return FS_EINVAL;
  56.  
  57.     if (position + nbytes < position)
  58.         return FS_EINVAL;   /* unsigned overflow */
  59.      
  60.     oflags = f->filp_flags;
  61.     rip = f->filp_ino;
  62.     f_size = rip->i_size;
  63.     r = OK;  
  64.     cum_io = 0;
  65.     mode_word = rip->i_mode & I_TYPE;
  66.     regular = (mode_word == I_REGULAR);
  67.     status = OK;                /* set to EIO if disk error occurs */
  68.    
  69.     old_addr = fp->buffer; 
  70.    
  71.     /* Split the transfer into chunks that don't span two blocks. */
  72.     while (nbytes != 0) {
  73.         off = (unsigned int)(position % BLOCK_SIZE); /* offset in blk*/
  74.         chunk = MIN(nbytes, BLOCK_SIZE - off);
  75.         if (chunk < 0) {
  76.             chunk = BLOCK_SIZE - off;
  77.         }
  78.        
  79.         bytes_left = f_size - position;
  80.  
  81.         if (position >= f_size) {
  82.             break;          /* we are beyond EOF */
  83.         }
  84.         if (chunk > bytes_left)
  85.             chunk = (int)bytes_left;
  86.        
  87.  
  88.         /* Read 'chunk' bytes. */
  89.         r = read_chunk(rip, position, off, chunk, fp->buffer);
  90.    
  91.         if (status < 0)
  92.             break;
  93.    
  94.         /* Update counters and pointers. */
  95.         fp->buffer += chunk * sizeof(char);         /* user buffer address */
  96.            
  97.         nbytes -= chunk;        /* bytes yet to be read */
  98.         cum_io += chunk;        /* bytes read so far */
  99.             position += chunk;      /* position within the file */
  100.        
  101.     }
  102.    
  103.     if (bytes_left <= 0 || nbytes == 0)
  104.         status = END_OF_FILE;
  105.  
  106.     fp->buffer = old_addr;
  107.    
  108.     f->filp_pos = position;
  109.     rip->i_seek = NO_SEEK;
  110.    
  111.     if (status != OK) {
  112.         r = status;     /* check for disk error */
  113.     }
  114.     if (status == END_OF_FILE) {
  115.         r = OK;
  116.     }
  117.     if (r == OK) {
  118.         fp->fp_cum_io_partial = 0;
  119.         print_console("File was successfully read into buffer\n");
  120.  
  121.             return cum_io;
  122.     }
  123.     else {
  124.         print_console("Some error occured during reading the file\n");
  125.         return r;
  126.     }
  127. }
  128.    
  129. static int read_chunk(register inode_t *rip, offset_t position, unsigned off,
  130.     int chunk, char *buff)
  131. {
  132.    
  133.     /*
  134.      * rip: pointer to inode for file to be read
  135.      * position:    position within file to read or write
  136.      * off:     off within the current block
  137.      * chunk:   number of bytes to read or write
  138.      * buff:    virtual address of the user buffer
  139.      */
  140.  
  141.     /* Read (part of) a block. */
  142.    
  143.     register block_t *bp;
  144.     block_num_t b;
  145.    
  146.     b = read_map(rip, position);
  147.      
  148.     /* Reading from a nonexistent block.  Must read as all zeros.*/
  149.     if (b == NO_BLOCK) {
  150.          bp = get_zero_block();    /* get a zero block */  
  151.     }
  152.     else {
  153.         bp = get_block(b);
  154.     }
  155.  
  156.     /* Copy a chunk from the block buffer to user space. */
  157.     memcpy((void *)buff, (void *)(bp->b.b__data+off), chunk);
  158.      
  159.     return OK;
  160. }
  161.    
  162. block_num_t read_map(register inode_t *rip, offset_t position)
  163. {
  164.    
  165.     /* Given an inode and a position within the corresponding file, locate the
  166.      * block (not zone) number in which that position is to be found and return it.
  167.      */
  168.    
  169.     register block_t *bp;
  170.     register zone_t z;
  171.     int scale, boff, dzones, nr_indirects, index, zind, ex;
  172.     block_num_t b;
  173.     long excess, zone, block_pos;
  174.      
  175.     scale = rip->i_sp->s_log_zone_size;   /* for block-zone conversion */
  176.     block_pos = position/BLOCK_SIZE;      /* relative blk # in file */
  177.     zone = block_pos >> scale;    /* position's zone */
  178.     boff = (int) (block_pos - (zone << scale) ); /* relative blk # within zone */
  179.     dzones = rip->i_ndzones;
  180.     nr_indirects = rip->i_nindirs;
  181.    
  182.     /* Is 'position' to be found in the inode itself? */
  183.     if (zone < dzones) {
  184.         zind = (int)zone;      /* index should be an int */
  185.         z = rip->i_zone[zind];
  186.         if (z == NO_ZONE)
  187.            return NO_BLOCK;
  188.         b = ((block_num_t) z << scale) + boff;
  189.         return b;
  190.     }
  191.    
  192.     /* It is not in the inode, so it must be single or double indirect. */
  193.     excess = zone - dzones;       /* first Vx_NR_DZONES don't count */
  194.    
  195.     if (excess < nr_indirects) {
  196.     /* 'position' can be located via the single indirect block. */
  197.     z = rip->i_zone[dzones];
  198.     }
  199.     else {
  200.     /* 'position' can be located via the double indirect block. */
  201.         if ( (z = rip->i_zone[dzones+1]) == NO_ZONE)
  202.             return NO_BLOCK;
  203.  
  204.         excess -= nr_indirects;         /* single indir doesn't count*/
  205.         b = (block_num_t) z << scale;
  206.         bp = get_block(b);              /* get double indirect block */
  207.         index = (int) (excess/nr_indirects);
  208.         z = rd_indir(bp, index);        /* z= zone for single*/
  209.         excess = excess % nr_indirects;     /* index into single ind blk */
  210.     }
  211.    
  212.     /* 'z' is zone num for single indirect block; 'excess' is index into it. */
  213.     if (z == NO_ZONE)
  214.         return NO_BLOCK;
  215.  
  216.     b = (block_num_t) z << scale;       /* b is blk # for single ind */
  217.     bp = get_block(b);              /* get single indirect block */
  218.     ex = (int) excess;          /* need an integer */
  219.     z = rd_indir(bp, ex);           /* get block pointed to */
  220.      
  221.     if (z == NO_ZONE)
  222.         return NO_BLOCK;
  223.  
  224.     b = ((block_num_t) z << scale) + boff;
  225.  
  226.     return b;
  227. }
  228.    
  229. zone_t rd_indir(block_t *bp, int index)
  230. {
  231.  
  232.     super_block_t *sp;
  233.     zone_t zone;   
  234.    
  235.     sp = get_super();   /* need super block to find file sys type */
  236.    
  237.     /* read a zone from an indirect block */
  238.     if (sp->s_version == V1) {
  239.         zone = (zone_t)bp->b.b__v1_ind[index];
  240.     }
  241.     else {
  242.         zone = (zone_t)bp->b.b__v2_ind[index];
  243.     }
  244.     if (zone != NO_ZONE && (zone < (zone_t)sp->s_firstdatazone || zone >= sp->s_zones)) {
  245.         print_console_int("Illegal zone number %d ", zone);
  246.         print_console_int("in indirect block, index %d\n", index);
  247.     }
  248.  
  249.     return zone;
  250. }
  251.    
  252.