source: branches/minix3-book/servers/fs/write.c

Last change on this file was 4, checked in by Mattia Monga, 14 years ago

Importazione sorgenti libro

File size: 8.1 KB
Line 
1/* This file is the counterpart of "read.c". It contains the code for writing
2 * insofar as this is not contained in read_write().
3 *
4 * The entry points into this file are
5 * do_write: call read_write to perform the WRITE system call
6 * clear_zone: erase a zone in the middle of a file
7 * new_block: acquire a new block
8 */
9
10#include "fs.h"
11#include <string.h>
12#include "buf.h"
13#include "file.h"
14#include "fproc.h"
15#include "inode.h"
16#include "super.h"
17
18FORWARD _PROTOTYPE( int write_map, (struct inode *rip, off_t position,
19 zone_t new_zone) );
20
21FORWARD _PROTOTYPE( void wr_indir, (struct buf *bp, int index, zone_t zone) );
22
23/*===========================================================================*
24 * do_write *
25 *===========================================================================*/
26PUBLIC int do_write()
27{
28/* Perform the write(fd, buffer, nbytes) system call. */
29
30 return(read_write(WRITING));
31}
32
33/*===========================================================================*
34 * write_map *
35 *===========================================================================*/
36PRIVATE int write_map(rip, position, new_zone)
37register struct inode *rip; /* pointer to inode to be changed */
38off_t position; /* file address to be mapped */
39zone_t new_zone; /* zone # to be inserted */
40{
41/* Write a new zone into an inode. */
42 int scale, ind_ex, new_ind, new_dbl, zones, nr_indirects, single, zindex, ex;
43 zone_t z, z1;
44 register block_t b;
45 long excess, zone;
46 struct buf *bp;
47
48 rip->i_dirt = DIRTY; /* inode will be changed */
49 bp = NIL_BUF;
50 scale = rip->i_sp->s_log_zone_size; /* for zone-block conversion */
51 /* relative zone # to insert */
52 zone = (position/rip->i_sp->s_block_size) >> scale;
53 zones = rip->i_ndzones; /* # direct zones in the inode */
54 nr_indirects = rip->i_nindirs;/* # indirect zones per indirect block */
55
56 /* Is 'position' to be found in the inode itself? */
57 if (zone < zones) {
58 zindex = (int) zone; /* we need an integer here */
59 rip->i_zone[zindex] = new_zone;
60 return(OK);
61 }
62
63 /* It is not in the inode, so it must be single or double indirect. */
64 excess = zone - zones; /* first Vx_NR_DZONES don't count */
65 new_ind = FALSE;
66 new_dbl = FALSE;
67
68 if (excess < nr_indirects) {
69 /* 'position' can be located via the single indirect block. */
70 z1 = rip->i_zone[zones]; /* single indirect zone */
71 single = TRUE;
72 } else {
73 /* 'position' can be located via the double indirect block. */
74 if ( (z = rip->i_zone[zones+1]) == NO_ZONE) {
75 /* Create the double indirect block. */
76 if ( (z = alloc_zone(rip->i_dev, rip->i_zone[0])) == NO_ZONE)
77 return(err_code);
78 rip->i_zone[zones+1] = z;
79 new_dbl = TRUE; /* set flag for later */
80 }
81
82 /* Either way, 'z' is zone number for double indirect block. */
83 excess -= nr_indirects; /* single indirect doesn't count */
84 ind_ex = (int) (excess / nr_indirects);
85 excess = excess % nr_indirects;
86 if (ind_ex >= nr_indirects) return(EFBIG);
87 b = (block_t) z << scale;
88 bp = get_block(rip->i_dev, b, (new_dbl ? NO_READ : NORMAL));
89 if (new_dbl) zero_block(bp);
90 z1 = rd_indir(bp, ind_ex);
91 single = FALSE;
92 }
93
94 /* z1 is now single indirect zone; 'excess' is index. */
95 if (z1 == NO_ZONE) {
96 /* Create indirect block and store zone # in inode or dbl indir blk. */
97 z1 = alloc_zone(rip->i_dev, rip->i_zone[0]);
98 if (single)
99 rip->i_zone[zones] = z1; /* update inode */
100 else
101 wr_indir(bp, ind_ex, z1); /* update dbl indir */
102
103 new_ind = TRUE;
104 if (bp != NIL_BUF) bp->b_dirt = DIRTY; /* if double ind, it is dirty*/
105 if (z1 == NO_ZONE) {
106 put_block(bp, INDIRECT_BLOCK); /* release dbl indirect blk */
107 return(err_code); /* couldn't create single ind */
108 }
109 }
110 put_block(bp, INDIRECT_BLOCK); /* release double indirect blk */
111
112 /* z1 is indirect block's zone number. */
113 b = (block_t) z1 << scale;
114 bp = get_block(rip->i_dev, b, (new_ind ? NO_READ : NORMAL) );
115 if (new_ind) zero_block(bp);
116 ex = (int) excess; /* we need an int here */
117 wr_indir(bp, ex, new_zone);
118 bp->b_dirt = DIRTY;
119 put_block(bp, INDIRECT_BLOCK);
120
121 return(OK);
122}
123
124/*===========================================================================*
125 * wr_indir *
126 *===========================================================================*/
127PRIVATE void wr_indir(bp, index, zone)
128struct buf *bp; /* pointer to indirect block */
129int index; /* index into *bp */
130zone_t zone; /* zone to write */
131{
132/* Given a pointer to an indirect block, write one entry. */
133
134 struct super_block *sp;
135
136 sp = get_super(bp->b_dev); /* need super block to find file sys type */
137
138 /* write a zone into an indirect block */
139 if (sp->s_version == V1)
140 bp->b_v1_ind[index] = (zone1_t) conv2(sp->s_native, (int) zone);
141 else
142 bp->b_v2_ind[index] = (zone_t) conv4(sp->s_native, (long) zone);
143}
144
145/*===========================================================================*
146 * clear_zone *
147 *===========================================================================*/
148PUBLIC void clear_zone(rip, pos, flag)
149register struct inode *rip; /* inode to clear */
150off_t pos; /* points to block to clear */
151int flag; /* 0 if called by read_write, 1 by new_block */
152{
153/* Zero a zone, possibly starting in the middle. The parameter 'pos' gives
154 * a byte in the first block to be zeroed. Clearzone() is called from
155 * read_write and new_block().
156 */
157
158 register struct buf *bp;
159 register block_t b, blo, bhi;
160 register off_t next;
161 register int scale;
162 register zone_t zone_size;
163
164 /* If the block size and zone size are the same, clear_zone() not needed. */
165 scale = rip->i_sp->s_log_zone_size;
166 if (scale == 0) return;
167
168 zone_size = (zone_t) rip->i_sp->s_block_size << scale;
169 if (flag == 1) pos = (pos/zone_size) * zone_size;
170 next = pos + rip->i_sp->s_block_size - 1;
171
172 /* If 'pos' is in the last block of a zone, do not clear the zone. */
173 if (next/zone_size != pos/zone_size) return;
174 if ( (blo = read_map(rip, next)) == NO_BLOCK) return;
175 bhi = ( ((blo>>scale)+1) << scale) - 1;
176
177 /* Clear all the blocks between 'blo' and 'bhi'. */
178 for (b = blo; b <= bhi; b++) {
179 bp = get_block(rip->i_dev, b, NO_READ);
180 zero_block(bp);
181 put_block(bp, FULL_DATA_BLOCK);
182 }
183}
184
185/*===========================================================================*
186 * new_block *
187 *===========================================================================*/
188PUBLIC struct buf *new_block(rip, position)
189register struct inode *rip; /* pointer to inode */
190off_t position; /* file pointer */
191{
192/* Acquire a new block and return a pointer to it. Doing so may require
193 * allocating a complete zone, and then returning the initial block.
194 * On the other hand, the current zone may still have some unused blocks.
195 */
196
197 register struct buf *bp;
198 block_t b, base_block;
199 zone_t z;
200 zone_t zone_size;
201 int scale, r;
202 struct super_block *sp;
203
204 /* Is another block available in the current zone? */
205 if ( (b = read_map(rip, position)) == NO_BLOCK) {
206 /* Choose first zone if possible. */
207 /* Lose if the file is nonempty but the first zone number is NO_ZONE
208 * corresponding to a zone full of zeros. It would be better to
209 * search near the last real zone.
210 */
211 if (rip->i_zone[0] == NO_ZONE) {
212 sp = rip->i_sp;
213 z = sp->s_firstdatazone;
214 } else {
215 z = rip->i_zone[0]; /* hunt near first zone */
216 }
217 if ( (z = alloc_zone(rip->i_dev, z)) == NO_ZONE) return(NIL_BUF);
218 if ( (r = write_map(rip, position, z)) != OK) {
219 free_zone(rip->i_dev, z);
220 err_code = r;
221 return(NIL_BUF);
222 }
223
224 /* If we are not writing at EOF, clear the zone, just to be safe. */
225 if ( position != rip->i_size) clear_zone(rip, position, 1);
226 scale = rip->i_sp->s_log_zone_size;
227 base_block = (block_t) z << scale;
228 zone_size = (zone_t) rip->i_sp->s_block_size << scale;
229 b = base_block + (block_t)((position % zone_size)/rip->i_sp->s_block_size);
230 }
231
232 bp = get_block(rip->i_dev, b, NO_READ);
233 zero_block(bp);
234 return(bp);
235}
236
237/*===========================================================================*
238 * zero_block *
239 *===========================================================================*/
240PUBLIC void zero_block(bp)
241register struct buf *bp; /* pointer to buffer to zero */
242{
243/* Zero a block. */
244 memset(bp->b_data, 0, MAX_BLOCK_SIZE);
245 bp->b_dirt = DIRTY;
246}
Note: See TracBrowser for help on using the repository browser.