Subversion Repositories HelenOS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2379 konopa 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