0,0 → 1,251 |
/* This file contains the heart of the mechanism used to read files. |
* Read requests are split up into chunks that do not cross block |
* boundaries. Each chunk is then processed in turn. |
*/ |
|
/* Methods: |
* do_read: perform the READ system call |
* read_map: given an inode and file position, look up its zone number |
* rd_indir: read an entry in an indirect block |
*/ |
|
#include <stdio.h> |
#include "fs.h" |
#include "block.h" |
#include "file.h" |
#include "fproc.h" |
#include "inode.h" |
#include "super.h" |
#include "param.h" |
|
|
static int read_chunk(inode_t *rip, offset_t position, |
unsigned off, int chunk, char *buff); |
|
int do_read() |
{ |
|
/* Perform read(fd, buffer, nbytes) call. */ |
|
register inode_t *rip; |
register filp_t *f; |
offset_t bytes_left, f_size, position; |
unsigned int off, cum_io; |
int oflags, r, chunk; |
int regular; |
mode_t mode_word; |
void *old_addr; |
|
|
/* If the file descriptor is valid, get the inode, size and mode. */ |
if (nbytes < 0) |
return FS_EINVAL; |
|
if ((f = get_filp(fd)) == NIL_FILP) |
return err_code; |
|
if (f->filp_mode == FILP_CLOSED) |
return FS_EIO; |
|
if (nbytes == 0) |
return 0; |
|
position = f->filp_pos; |
if (position > MAX_FILE_POS) |
return FS_EINVAL; |
|
if (position + nbytes < position) |
return FS_EINVAL; /* unsigned overflow */ |
|
oflags = f->filp_flags; |
rip = f->filp_ino; |
f_size = rip->i_size; |
r = OK; |
cum_io = 0; |
mode_word = rip->i_mode & I_TYPE; |
regular = (mode_word == I_REGULAR); |
status = OK; /* set to EIO if disk error occurs */ |
|
old_addr = fp->buffer; |
|
/* Split the transfer into chunks that don't span two blocks. */ |
while (nbytes != 0) { |
off = (unsigned int)(position % BLOCK_SIZE); /* offset in blk*/ |
chunk = MIN(nbytes, BLOCK_SIZE - off); |
if (chunk < 0) { |
chunk = BLOCK_SIZE - off; |
} |
|
bytes_left = f_size - position; |
|
if (position >= f_size) { |
break; /* we are beyond EOF */ |
} |
if (chunk > bytes_left) |
chunk = (int)bytes_left; |
|
|
/* Read 'chunk' bytes. */ |
r = read_chunk(rip, position, off, chunk, fp->buffer); |
|
if (status < 0) |
break; |
|
/* Update counters and pointers. */ |
fp->buffer += chunk * sizeof(char); /* user buffer address */ |
|
nbytes -= chunk; /* bytes yet to be read */ |
cum_io += chunk; /* bytes read so far */ |
position += chunk; /* position within the file */ |
|
} |
|
if (bytes_left <= 0 || nbytes == 0) |
status = END_OF_FILE; |
|
fp->buffer = old_addr; |
|
f->filp_pos = position; |
rip->i_seek = NO_SEEK; |
|
if (status != OK) { |
r = status; /* check for disk error */ |
} |
if (status == END_OF_FILE) { |
r = OK; |
} |
if (r == OK) { |
fp->fp_cum_io_partial = 0; |
print_console("File was successfully read into buffer\n"); |
|
return cum_io; |
} |
else { |
print_console("Some error occured during reading the file\n"); |
return r; |
} |
} |
|
static int read_chunk(register inode_t *rip, offset_t position, unsigned off, |
int chunk, char *buff) |
{ |
|
/* |
* rip: pointer to inode for file to be read |
* position: position within file to read or write |
* off: off within the current block |
* chunk: number of bytes to read or write |
* buff: virtual address of the user buffer |
*/ |
|
/* Read (part of) a block. */ |
|
register block_t *bp; |
block_num_t b; |
|
b = read_map(rip, position); |
|
/* Reading from a nonexistent block. Must read as all zeros.*/ |
if (b == NO_BLOCK) { |
bp = get_zero_block(); /* get a zero block */ |
} |
else { |
bp = get_block(b); |
} |
|
/* Copy a chunk from the block buffer to user space. */ |
memcpy((void *)buff, (void *)(bp->b.b__data+off), chunk); |
|
return OK; |
} |
|
block_num_t read_map(register inode_t *rip, offset_t position) |
{ |
|
/* Given an inode and a position within the corresponding file, locate the |
* block (not zone) number in which that position is to be found and return it. |
*/ |
|
register block_t *bp; |
register zone_t z; |
int scale, boff, dzones, nr_indirects, index, zind, ex; |
block_num_t b; |
long excess, zone, block_pos; |
|
scale = rip->i_sp->s_log_zone_size; /* for block-zone conversion */ |
block_pos = position/BLOCK_SIZE; /* relative blk # in file */ |
zone = block_pos >> scale; /* position's zone */ |
boff = (int) (block_pos - (zone << scale) ); /* relative blk # within zone */ |
dzones = rip->i_ndzones; |
nr_indirects = rip->i_nindirs; |
|
/* Is 'position' to be found in the inode itself? */ |
if (zone < dzones) { |
zind = (int)zone; /* index should be an int */ |
z = rip->i_zone[zind]; |
if (z == NO_ZONE) |
return NO_BLOCK; |
b = ((block_num_t) z << scale) + boff; |
return b; |
} |
|
/* It is not in the inode, so it must be single or double indirect. */ |
excess = zone - dzones; /* first Vx_NR_DZONES don't count */ |
|
if (excess < nr_indirects) { |
/* 'position' can be located via the single indirect block. */ |
z = rip->i_zone[dzones]; |
} |
else { |
/* 'position' can be located via the double indirect block. */ |
if ( (z = rip->i_zone[dzones+1]) == NO_ZONE) |
return NO_BLOCK; |
|
excess -= nr_indirects; /* single indir doesn't count*/ |
b = (block_num_t) z << scale; |
bp = get_block(b); /* get double indirect block */ |
index = (int) (excess/nr_indirects); |
z = rd_indir(bp, index); /* z= zone for single*/ |
excess = excess % nr_indirects; /* index into single ind blk */ |
} |
|
/* 'z' is zone num for single indirect block; 'excess' is index into it. */ |
if (z == NO_ZONE) |
return NO_BLOCK; |
|
b = (block_num_t) z << scale; /* b is blk # for single ind */ |
bp = get_block(b); /* get single indirect block */ |
ex = (int) excess; /* need an integer */ |
z = rd_indir(bp, ex); /* get block pointed to */ |
|
if (z == NO_ZONE) |
return NO_BLOCK; |
|
b = ((block_num_t) z << scale) + boff; |
|
return b; |
} |
|
zone_t rd_indir(block_t *bp, int index) |
{ |
|
super_block_t *sp; |
zone_t zone; |
|
sp = get_super(); /* need super block to find file sys type */ |
|
/* read a zone from an indirect block */ |
if (sp->s_version == V1) { |
zone = (zone_t)bp->b.b__v1_ind[index]; |
} |
else { |
zone = (zone_t)bp->b.b__v2_ind[index]; |
} |
if (zone != NO_ZONE && (zone < (zone_t)sp->s_firstdatazone || zone >= sp->s_zones)) { |
print_console_int("Illegal zone number %d ", zone); |
print_console_int("in indirect block, index %d\n", index); |
} |
|
return zone; |
} |
|