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)
16 #include <sys/param.h>
21 static void wr_indir(struct buf
*bp
, int index
, block_t block
);
22 static int empty_indir(struct buf
*, struct super_block
*);
24 /*===========================================================================*
26 *===========================================================================*/
27 int write_map(rip
, position
, new_block
, op
)
28 struct inode
*rip
; /* pointer to inode to be changed */
29 off_t position
; /* file address to be mapped */
30 block_t new_block
; /* block # to be inserted */
31 int op
; /* special actions */
33 /* Write a new block into an inode.
35 * If op includes WMAP_FREE, free the block corresponding to that position
36 * in the inode ('new_block' is ignored then). Also free the indirect block
37 * if that was the last entry in the indirect block.
38 * Also free the double/triple indirect block if that was the last entry in
39 * the double/triple indirect block.
40 * It's the only function which should take care about rip->i_blocks counter.
42 int index1
= 0, index2
= 0, index3
= 0; /* indexes in single..triple indirect blocks */
43 long excess
, block_pos
;
44 char new_ind
= 0, new_dbl
= 0, new_triple
= 0;
45 int single
= 0, triple
= 0;
46 block_t old_block
= NO_BLOCK
, b1
= NO_BLOCK
, b2
= NO_BLOCK
, b3
= NO_BLOCK
;
47 struct buf
*bp
= NULL
,
50 static char first_time
= TRUE
;
51 static long addr_in_block
;
52 static long addr_in_block2
;
53 static long doub_ind_s
;
54 static long triple_ind_s
;
55 static long out_range_s
;
58 addr_in_block
= rip
->i_sp
->s_block_size
/ BLOCK_ADDRESS_BYTES
;
59 addr_in_block2
= addr_in_block
* addr_in_block
;
60 doub_ind_s
= EXT2_NDIR_BLOCKS
+ addr_in_block
;
61 triple_ind_s
= doub_ind_s
+ addr_in_block2
;
62 out_range_s
= triple_ind_s
+ addr_in_block2
* addr_in_block
;
66 block_pos
= position
/ rip
->i_sp
->s_block_size
; /* relative blk # in file */
67 rip
->i_dirt
= IN_DIRTY
; /* inode will be changed */
69 /* Is 'position' to be found in the inode itself? */
70 if (block_pos
< EXT2_NDIR_BLOCKS
) {
71 if (rip
->i_block
[block_pos
] != NO_BLOCK
&& (op
& WMAP_FREE
)) {
72 free_block(rip
->i_sp
, rip
->i_block
[block_pos
]);
73 rip
->i_block
[block_pos
] = NO_BLOCK
;
74 rip
->i_blocks
-= rip
->i_sp
->s_sectors_in_block
;
76 rip
->i_block
[block_pos
] = new_block
;
77 rip
->i_blocks
+= rip
->i_sp
->s_sectors_in_block
;
82 /* It is not in the inode, so it must be single, double or triple indirect */
83 if (block_pos
< doub_ind_s
) {
84 b1
= rip
->i_block
[EXT2_NDIR_BLOCKS
]; /* addr of single indirect block */
85 index1
= block_pos
- EXT2_NDIR_BLOCKS
;
87 } else if (block_pos
>= out_range_s
) { /* TODO: do we need it? */
90 /* double or triple indirect block. At first if it's triple,
91 * find double indirect block.
93 excess
= block_pos
- doub_ind_s
;
94 b2
= rip
->i_block
[EXT2_DIND_BLOCK
];
95 if (block_pos
>= triple_ind_s
) {
96 b3
= rip
->i_block
[EXT2_TIND_BLOCK
];
97 if (b3
== NO_BLOCK
&& !(op
& WMAP_FREE
)) {
98 /* Create triple indirect block. */
99 if ( (b3
= alloc_block(rip
, rip
->i_bsearch
) ) == NO_BLOCK
) {
100 ext2_debug("failed to allocate tblock near %d\n", rip
->i_block
[0]);
103 rip
->i_block
[EXT2_TIND_BLOCK
] = b3
;
104 rip
->i_blocks
+= rip
->i_sp
->s_sectors_in_block
;
107 /* 'b3' is block number for triple indirect block, either old
109 * If there wasn't one and WMAP_FREE is set, 'b3' is NO_BLOCK.
111 if (b3
== NO_BLOCK
&& (op
& WMAP_FREE
)) {
112 /* WMAP_FREE and no triple indirect block - then no
113 * double and single indirect blocks either.
117 bp_tindir
= get_block(rip
->i_dev
, b3
, (new_triple
? NO_READ
: NORMAL
));
119 zero_block(bp_tindir
);
120 lmfs_markdirty(bp_tindir
);
122 excess
= block_pos
- triple_ind_s
;
123 index3
= excess
/ addr_in_block2
;
124 b2
= rd_indir(bp_tindir
, index3
);
125 excess
= excess
% addr_in_block2
;
130 if (b2
== NO_BLOCK
&& !(op
& WMAP_FREE
)) {
131 /* Create the double indirect block. */
132 if ( (b2
= alloc_block(rip
, rip
->i_bsearch
) ) == NO_BLOCK
) {
133 /* Release triple ind blk. */
134 put_block(bp_tindir
, INDIRECT_BLOCK
);
135 ext2_debug("failed to allocate dblock near %d\n", rip
->i_block
[0]);
139 wr_indir(bp_tindir
, index3
, b2
); /* update triple indir */
140 lmfs_markdirty(bp_tindir
);
142 rip
->i_block
[EXT2_DIND_BLOCK
] = b2
;
144 rip
->i_blocks
+= rip
->i_sp
->s_sectors_in_block
;
145 new_dbl
= TRUE
; /* set flag for later */
148 /* 'b2' is block number for double indirect block, either old
150 * If there wasn't one and WMAP_FREE is set, 'b2' is NO_BLOCK.
152 if (b2
== NO_BLOCK
&& (op
& WMAP_FREE
)) {
153 /* WMAP_FREE and no double indirect block - then no
154 * single indirect block either.
158 bp_dindir
= get_block(rip
->i_dev
, b2
, (new_dbl
? NO_READ
: NORMAL
));
160 zero_block(bp_dindir
);
161 lmfs_markdirty(bp_dindir
);
163 index2
= excess
/ addr_in_block
;
164 b1
= rd_indir(bp_dindir
, index2
);
165 index1
= excess
% addr_in_block
;
170 /* b1 is now single indirect block or NO_BLOCK; 'index' is index.
171 * We have to create the indirect block if it's NO_BLOCK. Unless
172 * we're freing (WMAP_FREE).
174 if (b1
== NO_BLOCK
&& !(op
& WMAP_FREE
)) {
175 if ( (b1
= alloc_block(rip
, rip
->i_bsearch
) ) == NO_BLOCK
) {
176 /* Release dbl and triple indirect blks. */
177 put_block(bp_dindir
, INDIRECT_BLOCK
);
178 put_block(bp_tindir
, INDIRECT_BLOCK
);
179 ext2_debug("failed to allocate dblock near %d\n", rip
->i_block
[0]);
183 rip
->i_block
[EXT2_NDIR_BLOCKS
] = b1
; /* update inode single indirect */
185 wr_indir(bp_dindir
, index2
, b1
); /* update dbl indir */
186 lmfs_markdirty(bp_dindir
);
188 rip
->i_blocks
+= rip
->i_sp
->s_sectors_in_block
;
192 /* b1 is indirect block's number (unless it's NO_BLOCK when we're
195 if (b1
!= NO_BLOCK
) {
196 bp
= get_block(rip
->i_dev
, b1
, (new_ind
? NO_READ
: NORMAL
) );
199 if (op
& WMAP_FREE
) {
200 if ((old_block
= rd_indir(bp
, index1
)) != NO_BLOCK
) {
201 free_block(rip
->i_sp
, old_block
);
202 rip
->i_blocks
-= rip
->i_sp
->s_sectors_in_block
;
203 wr_indir(bp
, index1
, NO_BLOCK
);
206 /* Last reference in the indirect block gone? Then
207 * free the indirect block.
209 if (empty_indir(bp
, rip
->i_sp
)) {
210 free_block(rip
->i_sp
, b1
);
211 rip
->i_blocks
-= rip
->i_sp
->s_sectors_in_block
;
213 /* Update the reference to the indirect block to
214 * NO_BLOCK - in the double indirect block if there
215 * is one, otherwise in the inode directly.
218 rip
->i_block
[EXT2_NDIR_BLOCKS
] = b1
;
220 wr_indir(bp_dindir
, index2
, b1
);
221 lmfs_markdirty(bp_dindir
);
225 wr_indir(bp
, index1
, new_block
);
226 rip
->i_blocks
+= rip
->i_sp
->s_sectors_in_block
;
228 /* b1 equals NO_BLOCK only when we are freeing up the indirect block. */
233 put_block(bp
, INDIRECT_BLOCK
);
236 /* If the single indirect block isn't there (or was just freed),
237 * see if we have to keep the double indirect block, if any.
238 * If we don't have to keep it, don't bother writing it out.
240 if (b1
== NO_BLOCK
&& !single
&& b2
!= NO_BLOCK
&&
241 empty_indir(bp_dindir
, rip
->i_sp
)) {
242 lmfs_markclean(bp_dindir
);
243 free_block(rip
->i_sp
, b2
);
244 rip
->i_blocks
-= rip
->i_sp
->s_sectors_in_block
;
247 wr_indir(bp_tindir
, index3
, b2
); /* update triple indir */
248 lmfs_markdirty(bp_tindir
);
250 rip
->i_block
[EXT2_DIND_BLOCK
] = b2
;
253 /* If the double indirect block isn't there (or was just freed),
254 * see if we have to keep the triple indirect block, if any.
255 * If we don't have to keep it, don't bother writing it out.
257 if (b2
== NO_BLOCK
&& triple
&& b3
!= NO_BLOCK
&&
258 empty_indir(bp_tindir
, rip
->i_sp
)) {
259 lmfs_markclean(bp_tindir
);
260 free_block(rip
->i_sp
, b3
);
261 rip
->i_blocks
-= rip
->i_sp
->s_sectors_in_block
;
262 rip
->i_block
[EXT2_TIND_BLOCK
] = NO_BLOCK
;
265 put_block(bp_dindir
, INDIRECT_BLOCK
); /* release double indirect blk */
266 put_block(bp_tindir
, INDIRECT_BLOCK
); /* release triple indirect blk */
272 /*===========================================================================*
274 *===========================================================================*/
275 static void wr_indir(bp
, index
, block
)
276 struct buf
*bp
; /* pointer to indirect block */
277 int index
; /* index into *bp */
278 block_t block
; /* block to write */
280 /* Given a pointer to an indirect block, write one entry. */
283 panic("wr_indir() on NULL");
285 /* write a block into an indirect block */
286 b_ind(bp
)[index
] = conv4(le_CPU
, block
);
290 /*===========================================================================*
292 *===========================================================================*/
293 static int empty_indir(bp
, sb
)
294 struct buf
*bp
; /* pointer to indirect block */
295 struct super_block
*sb
; /* superblock of device block resides on */
297 /* Return nonzero if the indirect block pointed to by bp contains
298 * only NO_BLOCK entries.
300 long addr_in_block
= sb
->s_block_size
/4; /* 4 bytes per addr */
302 for(i
= 0; i
< addr_in_block
; i
++)
303 if(b_ind(bp
)[i
] != NO_BLOCK
)
308 /*===========================================================================*
310 *===========================================================================*/
311 struct buf
*new_block(rip
, position
)
312 register struct inode
*rip
; /* pointer to inode */
313 off_t position
; /* file pointer */
315 /* Acquire a new block and return a pointer to it. */
316 register struct buf
*bp
;
320 /* Is another block available? */
321 if ( (b
= read_map(rip
, position
, 0)) == NO_BLOCK
) {
322 /* Check if this position follows last allocated
325 block_t goal
= NO_BLOCK
;
326 if (rip
->i_last_pos_bl_alloc
!= 0) {
327 off_t position_diff
= position
- rip
->i_last_pos_bl_alloc
;
328 if (rip
->i_bsearch
== 0) {
329 /* Should never happen, but not critical */
330 ext2_debug("warning, i_bsearch is 0, while\
331 i_last_pos_bl_alloc is not!");
333 if (position_diff
<= rip
->i_sp
->s_block_size
) {
334 goal
= rip
->i_bsearch
+ 1;
336 /* Non-sequential write operation,
337 * disable preallocation
340 rip
->i_preallocation
= 0;
341 discard_preallocated_blocks(rip
);
345 if ( (b
= alloc_block(rip
, goal
) ) == NO_BLOCK
) {
349 if ( (r
= write_map(rip
, position
, b
, 0)) != OK
) {
350 free_block(rip
->i_sp
, b
);
352 ext2_debug("write_map failed\n");
355 rip
->i_last_pos_bl_alloc
= position
;
357 /* rip->i_last_pos_bl_alloc points to the block position,
358 * and zero indicates first usage, thus just increment.
360 rip
->i_last_pos_bl_alloc
++;
364 bp
= lmfs_get_block_ino(rip
->i_dev
, b
, NO_READ
, rip
->i_num
,
365 rounddown(position
, rip
->i_sp
->s_block_size
));
370 /*===========================================================================*
372 *===========================================================================*/
374 register struct buf
*bp
; /* pointer to buffer to zero */
377 ASSERT(lmfs_bytes(bp
) > 0);
379 memset(b_data(bp
), 0, (size_t) lmfs_bytes(bp
));