1 /* This file is the counterpart of "read.c". It contains the code for writing
2 * insofar as this is not contained in fs_readwrite().
4 * The entry points into this file are
5 * write_map: write a new block into an inode
6 * new_block: acquire a new block
7 * zero_block: overwrite a block with zeroes
10 * February 2010 (Evgeniy Ivanov)
19 static void wr_indir(struct buf
*bp
, int index
, block_t block
);
20 static int empty_indir(struct buf
*, struct super_block
*);
22 /*===========================================================================*
24 *===========================================================================*/
25 int write_map(rip
, position
, new_block
, op
)
26 struct inode
*rip
; /* pointer to inode to be changed */
27 off_t position
; /* file address to be mapped */
28 block_t new_block
; /* block # to be inserted */
29 int op
; /* special actions */
31 /* Write a new block into an inode.
33 * If op includes WMAP_FREE, free the block corresponding to that position
34 * in the inode ('new_block' is ignored then). Also free the indirect block
35 * if that was the last entry in the indirect block.
36 * Also free the double/triple indirect block if that was the last entry in
37 * the double/triple indirect block.
38 * It's the only function which should take care about rip->i_blocks counter.
40 int index1
, index2
, index3
; /* indexes in single..triple indirect blocks */
41 long excess
, block_pos
;
42 char new_ind
= 0, new_dbl
= 0, new_triple
= 0;
43 int single
= 0, triple
= 0;
44 block_t old_block
= NO_BLOCK
, b1
= NO_BLOCK
, b2
= NO_BLOCK
, b3
= NO_BLOCK
;
45 struct buf
*bp
= NULL
,
48 static char first_time
= TRUE
;
49 static long addr_in_block
;
50 static long addr_in_block2
;
51 static long doub_ind_s
;
52 static long triple_ind_s
;
53 static long out_range_s
;
56 addr_in_block
= rip
->i_sp
->s_block_size
/ BLOCK_ADDRESS_BYTES
;
57 addr_in_block2
= addr_in_block
* addr_in_block
;
58 doub_ind_s
= EXT2_NDIR_BLOCKS
+ addr_in_block
;
59 triple_ind_s
= doub_ind_s
+ addr_in_block2
;
60 out_range_s
= triple_ind_s
+ addr_in_block2
* addr_in_block
;
64 block_pos
= position
/ rip
->i_sp
->s_block_size
; /* relative blk # in file */
65 rip
->i_dirt
= DIRTY
; /* inode will be changed */
67 /* Is 'position' to be found in the inode itself? */
68 if (block_pos
< EXT2_NDIR_BLOCKS
) {
69 if (rip
->i_block
[block_pos
] != NO_BLOCK
&& (op
& WMAP_FREE
)) {
70 free_block(rip
->i_sp
, rip
->i_block
[block_pos
]);
71 rip
->i_block
[block_pos
] = NO_BLOCK
;
72 rip
->i_blocks
-= rip
->i_sp
->s_sectors_in_block
;
74 rip
->i_block
[block_pos
] = new_block
;
75 rip
->i_blocks
+= rip
->i_sp
->s_sectors_in_block
;
80 /* It is not in the inode, so it must be single, double or triple indirect */
81 if (block_pos
< doub_ind_s
) {
82 b1
= rip
->i_block
[EXT2_NDIR_BLOCKS
]; /* addr of single indirect block */
83 index1
= block_pos
- EXT2_NDIR_BLOCKS
;
85 } else if (block_pos
>= out_range_s
) { /* TODO: do we need it? */
88 /* double or triple indirect block. At first if it's triple,
89 * find double indirect block.
91 excess
= block_pos
- doub_ind_s
;
92 b2
= rip
->i_block
[EXT2_DIND_BLOCK
];
93 if (block_pos
>= triple_ind_s
) {
94 b3
= rip
->i_block
[EXT2_TIND_BLOCK
];
95 if (b3
== NO_BLOCK
&& !(op
& WMAP_FREE
)) {
96 /* Create triple indirect block. */
97 if ( (b3
= alloc_block(rip
, rip
->i_bsearch
) ) == NO_BLOCK
) {
98 ext2_debug("failed to allocate tblock near %d\n", rip
->i_block
[0]);
101 rip
->i_block
[EXT2_TIND_BLOCK
] = b3
;
102 rip
->i_blocks
+= rip
->i_sp
->s_sectors_in_block
;
105 /* 'b3' is block number for triple indirect block, either old
107 * If there wasn't one and WMAP_FREE is set, 'b3' is NO_BLOCK.
109 if (b3
== NO_BLOCK
&& (op
& WMAP_FREE
)) {
110 /* WMAP_FREE and no triple indirect block - then no
111 * double and single indirect blocks either.
115 bp_tindir
= get_block(rip
->i_dev
, b3
, (new_triple
? NO_READ
: NORMAL
));
117 zero_block(bp_tindir
);
118 bp_tindir
->b_dirt
= DIRTY
;
120 excess
= block_pos
- triple_ind_s
;
121 index3
= excess
/ addr_in_block2
;
122 b2
= rd_indir(bp_tindir
, index3
);
123 excess
= excess
% addr_in_block2
;
128 if (b2
== NO_BLOCK
&& !(op
& WMAP_FREE
)) {
129 /* Create the double indirect block. */
130 if ( (b2
= alloc_block(rip
, rip
->i_bsearch
) ) == NO_BLOCK
) {
131 /* Release triple ind blk. */
132 put_block(bp_tindir
, INDIRECT_BLOCK
);
133 ext2_debug("failed to allocate dblock near %d\n", rip
->i_block
[0]);
137 wr_indir(bp_tindir
, index3
, b2
); /* update triple indir */
138 bp_tindir
->b_dirt
= DIRTY
;
140 rip
->i_block
[EXT2_DIND_BLOCK
] = b2
;
142 rip
->i_blocks
+= rip
->i_sp
->s_sectors_in_block
;
143 new_dbl
= TRUE
; /* set flag for later */
146 /* 'b2' is block number for double indirect block, either old
148 * If there wasn't one and WMAP_FREE is set, 'b2' is NO_BLOCK.
150 if (b2
== NO_BLOCK
&& (op
& WMAP_FREE
)) {
151 /* WMAP_FREE and no double indirect block - then no
152 * single indirect block either.
156 bp_dindir
= get_block(rip
->i_dev
, b2
, (new_dbl
? NO_READ
: NORMAL
));
158 zero_block(bp_dindir
);
159 bp_dindir
->b_dirt
= DIRTY
;
161 index2
= excess
/ addr_in_block
;
162 b1
= rd_indir(bp_dindir
, index2
);
163 index1
= excess
% addr_in_block
;
168 /* b1 is now single indirect block or NO_BLOCK; 'index' is index.
169 * We have to create the indirect block if it's NO_BLOCK. Unless
170 * we're freing (WMAP_FREE).
172 if (b1
== NO_BLOCK
&& !(op
& WMAP_FREE
)) {
173 if ( (b1
= alloc_block(rip
, rip
->i_bsearch
) ) == NO_BLOCK
) {
174 /* Release dbl and triple indirect blks. */
175 put_block(bp_dindir
, INDIRECT_BLOCK
);
176 put_block(bp_tindir
, INDIRECT_BLOCK
);
177 ext2_debug("failed to allocate dblock near %d\n", rip
->i_block
[0]);
181 rip
->i_block
[EXT2_NDIR_BLOCKS
] = b1
; /* update inode single indirect */
183 wr_indir(bp_dindir
, index2
, b1
); /* update dbl indir */
184 bp_dindir
->b_dirt
= DIRTY
;
186 rip
->i_blocks
+= rip
->i_sp
->s_sectors_in_block
;
190 /* b1 is indirect block's number (unless it's NO_BLOCK when we're
193 if (b1
!= NO_BLOCK
) {
194 bp
= get_block(rip
->i_dev
, b1
, (new_ind
? NO_READ
: NORMAL
) );
197 if (op
& WMAP_FREE
) {
198 if ((old_block
= rd_indir(bp
, index1
)) != NO_BLOCK
) {
199 free_block(rip
->i_sp
, old_block
);
200 rip
->i_blocks
-= rip
->i_sp
->s_sectors_in_block
;
201 wr_indir(bp
, index1
, NO_BLOCK
);
204 /* Last reference in the indirect block gone? Then
205 * free the indirect block.
207 if (empty_indir(bp
, rip
->i_sp
)) {
208 free_block(rip
->i_sp
, b1
);
209 rip
->i_blocks
-= rip
->i_sp
->s_sectors_in_block
;
211 /* Update the reference to the indirect block to
212 * NO_BLOCK - in the double indirect block if there
213 * is one, otherwise in the inode directly.
216 rip
->i_block
[EXT2_NDIR_BLOCKS
] = b1
;
218 wr_indir(bp_dindir
, index2
, b1
);
219 bp_dindir
->b_dirt
= DIRTY
;
223 wr_indir(bp
, index1
, new_block
);
224 rip
->i_blocks
+= rip
->i_sp
->s_sectors_in_block
;
226 /* b1 equals NO_BLOCK only when we are freeing up the indirect block. */
227 bp
->b_dirt
= (b1
== NO_BLOCK
) ? CLEAN
: DIRTY
;;
228 put_block(bp
, INDIRECT_BLOCK
);
231 /* If the single indirect block isn't there (or was just freed),
232 * see if we have to keep the double indirect block, if any.
233 * If we don't have to keep it, don't bother writing it out.
235 if (b1
== NO_BLOCK
&& !single
&& b2
!= NO_BLOCK
&&
236 empty_indir(bp_dindir
, rip
->i_sp
)) {
237 bp_dindir
->b_dirt
= CLEAN
;
238 free_block(rip
->i_sp
, b2
);
239 rip
->i_blocks
-= rip
->i_sp
->s_sectors_in_block
;
242 wr_indir(bp_tindir
, index3
, b2
); /* update triple indir */
243 bp_tindir
->b_dirt
= DIRTY
;
245 rip
->i_block
[EXT2_DIND_BLOCK
] = b2
;
248 /* If the double indirect block isn't there (or was just freed),
249 * see if we have to keep the triple indirect block, if any.
250 * If we don't have to keep it, don't bother writing it out.
252 if (b2
== NO_BLOCK
&& triple
&& b3
!= NO_BLOCK
&&
253 empty_indir(bp_tindir
, rip
->i_sp
)) {
254 bp_tindir
->b_dirt
= CLEAN
;
255 free_block(rip
->i_sp
, b3
);
256 rip
->i_blocks
-= rip
->i_sp
->s_sectors_in_block
;
257 rip
->i_block
[EXT2_TIND_BLOCK
] = NO_BLOCK
;
260 put_block(bp_dindir
, INDIRECT_BLOCK
); /* release double indirect blk */
261 put_block(bp_tindir
, INDIRECT_BLOCK
); /* release triple indirect blk */
267 /*===========================================================================*
269 *===========================================================================*/
270 static void wr_indir(bp
, index
, block
)
271 struct buf
*bp
; /* pointer to indirect block */
272 int index
; /* index into *bp */
273 block_t block
; /* block to write */
275 /* Given a pointer to an indirect block, write one entry. */
278 panic("wr_indir() on NULL");
280 /* write a block into an indirect block */
281 bp
->b_ind
[index
] = conv4(le_CPU
, block
);
285 /*===========================================================================*
287 *===========================================================================*/
288 static int empty_indir(bp
, sb
)
289 struct buf
*bp
; /* pointer to indirect block */
290 struct super_block
*sb
; /* superblock of device block resides on */
292 /* Return nonzero if the indirect block pointed to by bp contains
293 * only NO_BLOCK entries.
295 long addr_in_block
= sb
->s_block_size
/4; /* 4 bytes per addr */
297 for(i
= 0; i
< addr_in_block
; i
++)
298 if(bp
->b_ind
[i
] != NO_BLOCK
)
303 /*===========================================================================*
305 *===========================================================================*/
306 struct buf
*new_block(rip
, position
)
307 register struct inode
*rip
; /* pointer to inode */
308 off_t position
; /* file pointer */
310 /* Acquire a new block and return a pointer to it. */
311 register struct buf
*bp
;
315 /* Is another block available? */
316 if ( (b
= read_map(rip
, position
)) == NO_BLOCK
) {
317 /* Check if this position follows last allocated
320 block_t goal
= NO_BLOCK
;
321 if (rip
->i_last_pos_bl_alloc
!= 0) {
322 off_t position_diff
= position
- rip
->i_last_pos_bl_alloc
;
323 if (rip
->i_bsearch
== 0) {
324 /* Should never happen, but not critical */
325 ext2_debug("warning, i_bsearch is 0, while\
326 i_last_pos_bl_alloc is not!");
328 if (position_diff
<= rip
->i_sp
->s_block_size
) {
329 goal
= rip
->i_bsearch
+ 1;
331 /* Non-sequential write operation,
332 * disable preallocation
335 rip
->i_preallocation
= 0;
336 discard_preallocated_blocks(rip
);
340 if ( (b
= alloc_block(rip
, goal
) ) == NO_BLOCK
) {
344 if ( (r
= write_map(rip
, position
, b
, 0)) != OK
) {
345 free_block(rip
->i_sp
, b
);
347 ext2_debug("write_map failed\n");
350 rip
->i_last_pos_bl_alloc
= position
;
352 /* rip->i_last_pos_bl_alloc points to the block position,
353 * and zero indicates first usage, thus just increment.
355 rip
->i_last_pos_bl_alloc
++;
359 bp
= get_block(rip
->i_dev
, b
, NO_READ
);
364 /*===========================================================================*
366 *===========================================================================*/
368 register struct buf
*bp
; /* pointer to buffer to zero */
371 ASSERT(bp
->b_bytes
> 0);
373 memset(bp
->b_data
, 0, (size_t) bp
->b_bytes
);