/*
* Copyright (c) 1987,1997, Prentice Hall
* All rights reserved.
*
* Redistribution and use of the MINIX operating system in source and
* binary forms, with or without modification, are permitted provided
* that the following conditions are met:
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* - Neither the name of Prentice Hall nor the names of the software
* authors or contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS, AUTHORS, AND
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL PRENTICE HALL OR ANY AUTHORS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @addtogroup FileSystemImpl
* @{
*/
/**
* @file read.c
* @brief 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.
*/
#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);
/**
* Perform the READ system call
*/
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;
}
}
/**
* Given an inode and file position, look up its zone number
*/
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;
}
/**
* Read an entry in an indirect block
*/
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;
}
/**
* }
*/