Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 2378 → Rev 2379

/branches/fs/uspace/fs/read.c
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;
}