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
18 FORWARD _PROTOTYPE( int write_map, (struct inode *rip, off_t position,
19 zone_t new_zone) );
20
21 FORWARD _PROTOTYPE( void wr_indir, (struct buf *bp, int index, zone_t zone) );
22
23 /*===========================================================================*
24 * do_write *
25 *===========================================================================*/
26 PUBLIC int do_write()
27 {
28 /* Perform the write(fd, buffer, nbytes) system call. */
29
30 return(read_write(WRITING));
31 }
32
33
34 /*===========================================================================*
35 * write_map *
36 *===========================================================================*/
37 PRIVATE int write_map(rip, position, new_zone)
38 register struct inode *rip; /* pointer to inode to be changed */
39 off_t position; /* file address to be mapped */
40 zone_t new_zone; /* zone # to be inserted */
41 {
42 /* Write a new zone into an inode. */
43 int scale, ind_ex, new_ind, new_dbl, zones, nr_indirects, single, zindex, ex;
44 zone_t z, z1;
45 register block_t b;
46 long excess, zone;
47 struct buf *bp;
48
49 rip->i_dirt = DIRTY; /* inode will be changed */
50 bp = NIL_BUF;
51 scale = rip->i_sp->s_log_zone_size; /* for zone-block conversion */
52 zone = (position/BLOCK_SIZE) >> scale; /* relative zone # to insert */
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 /*===========================================================================*
126 * wr_indir *
127 *===========================================================================*/
128 PRIVATE void wr_indir(bp, index, zone)
129 struct buf *bp; /* pointer to indirect block */
130 int index; /* index into *bp */
131 zone_t zone; /* zone to write */
132 {
133 /* Given a pointer to an indirect block, write one entry. */
134
135 struct super_block *sp;
136
137 sp = get_super(bp->b_dev); /* need super block to find file sys type */
138
139 /* write a zone into an indirect block */
140 if (sp->s_version == V1)
141 bp->b_v1_ind[index] = (zone1_t) conv2(sp->s_native, (int) zone);
142 else
143 bp->b_v2_ind[index] = (zone_t) conv4(sp->s_native, (long) zone);
144 }
145
146
147 /*===========================================================================*
148 * clear_zone *
149 *===========================================================================*/
150 PUBLIC void clear_zone(rip, pos, flag)
151 register struct inode *rip; /* inode to clear */
152 off_t pos; /* points to block to clear */
153 int flag; /* 0 if called by read_write, 1 by new_block */
154 {
155 /* Zero a zone, possibly starting in the middle. The parameter 'pos' gives
156 * a byte in the first block to be zeroed. Clearzone() is called from
157 * read_write and new_block().
158 */
159
160 register struct buf *bp;
161 register block_t b, blo, bhi;
162 register off_t next;
163 register int scale;
164 register zone_t zone_size;
165
166 /* If the block size and zone size are the same, clear_zone() not needed. */
167 scale = rip->i_sp->s_log_zone_size;
168 if (scale == 0) return;
169
170 zone_size = (zone_t) BLOCK_SIZE << scale;
171 if (flag == 1) pos = (pos/zone_size) * zone_size;
172 next = pos + BLOCK_SIZE - 1;
173
174 /* If 'pos' is in the last block of a zone, do not clear the zone. */
175 if (next/zone_size != pos/zone_size) return;
176 if ( (blo = read_map(rip, next)) == NO_BLOCK) return;
177 bhi = ( ((blo>>scale)+1) << scale) - 1;
178
179 /* Clear all the blocks between 'blo' and 'bhi'. */
180 for (b = blo; b <= bhi; b++) {
181 bp = get_block(rip->i_dev, b, NO_READ);
182 zero_block(bp);
183 put_block(bp, FULL_DATA_BLOCK);
184 }
185 }
186
187
188 /*===========================================================================*
189 * new_block *
190 *===========================================================================*/
191 PUBLIC struct buf *new_block(rip, position)
192 register struct inode *rip; /* pointer to inode */
193 off_t position; /* file pointer */
194 {
195 /* Acquire a new block and return a pointer to it. Doing so may require
196 * allocating a complete zone, and then returning the initial block.
197 * On the other hand, the current zone may still have some unused blocks.
198 */
199
200 register struct buf *bp;
201 block_t b, base_block;
202 zone_t z;
203 zone_t zone_size;
204 int scale, r;
205 struct super_block *sp;
206
207 /* Is another block available in the current zone? */
208 if ( (b = read_map(rip, position)) == NO_BLOCK) {
209 /* Choose first zone if possible. */
210 /* Lose if the file is nonempty but the first zone number is NO_ZONE
211 * corresponding to a zone full of zeros. It would be better to
212 * search near the last real zone.
213 */
214 if (rip->i_zone[0] == NO_ZONE) {
215 sp = rip->i_sp;
216 z = sp->s_firstdatazone;
217 } else {
218 z = rip->i_zone[0]; /* hunt near first zone */
219 }
220 if ( (z = alloc_zone(rip->i_dev, z)) == NO_ZONE) return(NIL_BUF);
221 if ( (r = write_map(rip, position, z)) != OK) {
222 free_zone(rip->i_dev, z);
223 err_code = r;
224 return(NIL_BUF);
225 }
226
227 /* If we are not writing at EOF, clear the zone, just to be safe. */
228 if ( position != rip->i_size) clear_zone(rip, position, 1);
229 scale = rip->i_sp->s_log_zone_size;
230 base_block = (block_t) z << scale;
231 zone_size = (zone_t) BLOCK_SIZE << scale;
232 b = base_block + (block_t)((position % zone_size)/BLOCK_SIZE);
233 }
234
235 bp = get_block(rip->i_dev, b, NO_READ);
236 zero_block(bp);
237 return(bp);
238 }
239
240
241 /*===========================================================================*
242 * zero_block *
243 *===========================================================================*/
244 PUBLIC void zero_block(bp)
245 register struct buf *bp; /* pointer to buffer to zero */
246 {
247 /* Zero a block. */
248
249 memset(bp->b_data, 0, BLOCK_SIZE);
250 bp->b_dirt = DIRTY;
251 }
252
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.