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(). 3 * 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 8 * 9 * Created (MFS based): 10 * February 2010 (Evgeniy Ivanov) 11 */ 12 13 #include "fs.h" 14 #include <string.h> 15 #include <assert.h> 16 #include <sys/param.h> 17 #include "buf.h" 18 #include "inode.h" 19 #include "super.h" 20 21 static void wr_indir(struct buf *bp, int index, block_t block); 22 static int empty_indir(struct buf *, struct super_block *); 23 24 /*===========================================================================* 25 * write_map * 26 *===========================================================================*/ 27 int write_map(rip, position, new_wblock, op) 28 struct inode *rip; /* pointer to inode to be changed */ 29 off_t position; /* file address to be mapped */ 30 block_t new_wblock; /* block # to be inserted */ 31 int op; /* special actions */ 32 { 33 /* Write a new block into an inode. 34 * 35 * If op includes WMAP_FREE, free the block corresponding to that position 36 * in the inode ('new_wblock' 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. 41 */ 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, 48 *bp_dindir = NULL, 49 *bp_tindir = 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; 56 57 if (first_time) { 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; 63 first_time = FALSE; 64 } 65 66 block_pos = position / rip->i_sp->s_block_size; /* relative blk # in file */ 67 rip->i_dirt = IN_DIRTY; /* inode will be changed */ 68 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; 75 } else { 76 rip->i_block[block_pos] = new_wblock; 77 rip->i_blocks += rip->i_sp->s_sectors_in_block; 78 } 79 return(OK); 80 } 81 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; 86 single = TRUE; 87 } else if (block_pos >= out_range_s) { /* TODO: do we need it? */ 88 return(EFBIG); 89 } else { 90 /* double or triple indirect block. At first if it's triple, 91 * find double indirect block. 92 */ 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]); 101 return(ENOSPC); 102 } 103 rip->i_block[EXT2_TIND_BLOCK] = b3; 104 rip->i_blocks += rip->i_sp->s_sectors_in_block; 105 new_triple = TRUE; 106 } 107 /* 'b3' is block number for triple indirect block, either old 108 * or newly created. 109 * If there wasn't one and WMAP_FREE is set, 'b3' is NO_BLOCK. 110 */ 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. 114 */ 115 b1 = b2 = NO_BLOCK; 116 } else { 117 bp_tindir = get_block(rip->i_dev, b3, (new_triple ? NO_READ : NORMAL)); 118 if (new_triple) { 119 zero_block(bp_tindir); 120 lmfs_markdirty(bp_tindir); 121 } 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; 126 } 127 triple = TRUE; 128 } 129 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); 135 ext2_debug("failed to allocate dblock near %d\n", rip->i_block[0]); 136 return(ENOSPC); 137 } 138 if (triple) { 139 wr_indir(bp_tindir, index3, b2); /* update triple indir */ 140 lmfs_markdirty(bp_tindir); 141 } else { 142 rip->i_block[EXT2_DIND_BLOCK] = b2; 143 } 144 rip->i_blocks += rip->i_sp->s_sectors_in_block; 145 new_dbl = TRUE; /* set flag for later */ 146 } 147 148 /* 'b2' is block number for double indirect block, either old 149 * or newly created. 150 * If there wasn't one and WMAP_FREE is set, 'b2' is NO_BLOCK. 151 */ 152 if (b2 == NO_BLOCK && (op & WMAP_FREE)) { 153 /* WMAP_FREE and no double indirect block - then no 154 * single indirect block either. 155 */ 156 b1 = NO_BLOCK; 157 } else { 158 bp_dindir = get_block(rip->i_dev, b2, (new_dbl ? NO_READ : NORMAL)); 159 if (new_dbl) { 160 zero_block(bp_dindir); 161 lmfs_markdirty(bp_dindir); 162 } 163 index2 = excess / addr_in_block; 164 b1 = rd_indir(bp_dindir, index2); 165 index1 = excess % addr_in_block; 166 } 167 single = FALSE; 168 } 169 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). 173 */ 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); 178 put_block(bp_tindir); 179 ext2_debug("failed to allocate dblock near %d\n", rip->i_block[0]); 180 return(ENOSPC); 181 } 182 if (single) { 183 rip->i_block[EXT2_NDIR_BLOCKS] = b1; /* update inode single indirect */ 184 } else { 185 wr_indir(bp_dindir, index2, b1); /* update dbl indir */ 186 lmfs_markdirty(bp_dindir); 187 } 188 rip->i_blocks += rip->i_sp->s_sectors_in_block; 189 new_ind = TRUE; 190 } 191 192 /* b1 is indirect block's number (unless it's NO_BLOCK when we're 193 * freeing). 194 */ 195 if (b1 != NO_BLOCK) { 196 bp = get_block(rip->i_dev, b1, (new_ind ? NO_READ : NORMAL) ); 197 if (new_ind) 198 zero_block(bp); 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); 204 } 205 206 /* Last reference in the indirect block gone? Then 207 * free the indirect block. 208 */ 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; 212 b1 = NO_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. 216 */ 217 if (single) { 218 rip->i_block[EXT2_NDIR_BLOCKS] = b1; 219 } else { 220 wr_indir(bp_dindir, index2, b1); 221 lmfs_markdirty(bp_dindir); 222 } 223 } 224 } else { 225 wr_indir(bp, index1, new_wblock); 226 rip->i_blocks += rip->i_sp->s_sectors_in_block; 227 } 228 /* b1 equals NO_BLOCK only when we are freeing up the indirect block. */ 229 if(b1 != NO_BLOCK) 230 lmfs_markdirty(bp); 231 put_block(bp); 232 } 233 234 /* If the single indirect block isn't there (or was just freed), 235 * see if we have to keep the double indirect block, if any. 236 */ 237 if (b1 == NO_BLOCK && !single && b2 != NO_BLOCK && 238 empty_indir(bp_dindir, rip->i_sp)) { 239 free_block(rip->i_sp, b2); 240 rip->i_blocks -= rip->i_sp->s_sectors_in_block; 241 b2 = NO_BLOCK; 242 if (triple) { 243 wr_indir(bp_tindir, index3, b2); /* update triple indir */ 244 lmfs_markdirty(bp_tindir); 245 } else { 246 rip->i_block[EXT2_DIND_BLOCK] = b2; 247 } 248 } 249 /* If the double indirect block isn't there (or was just freed), 250 * see if we have to keep the triple indirect block, if any. 251 */ 252 if (b2 == NO_BLOCK && triple && b3 != NO_BLOCK && 253 empty_indir(bp_tindir, rip->i_sp)) { 254 free_block(rip->i_sp, b3); 255 rip->i_blocks -= rip->i_sp->s_sectors_in_block; 256 rip->i_block[EXT2_TIND_BLOCK] = NO_BLOCK; 257 } 258 259 put_block(bp_dindir); /* release double indirect blk */ 260 put_block(bp_tindir); /* release triple indirect blk */ 261 262 return(OK); 263 } 264 265 266 /*===========================================================================* 267 * wr_indir * 268 *===========================================================================*/ 269 static void wr_indir(bp, wrindex, block) 270 struct buf *bp; /* pointer to indirect block */ 271 int wrindex; /* index into *bp */ 272 block_t block; /* block to write */ 273 { 274 /* Given a pointer to an indirect block, write one entry. */ 275 276 if(bp == NULL) 277 panic("wr_indir() on NULL"); 278 279 /* write a block into an indirect block */ 280 b_ind(bp)[wrindex] = conv4(le_CPU, block); 281 } 282 283 284 /*===========================================================================* 285 * empty_indir * 286 *===========================================================================*/ 287 static int empty_indir(bp, sb) 288 struct buf *bp; /* pointer to indirect block */ 289 struct super_block *sb; /* superblock of device block resides on */ 290 { 291 /* Return nonzero if the indirect block pointed to by bp contains 292 * only NO_BLOCK entries. 293 */ 294 long addr_in_block = sb->s_block_size/4; /* 4 bytes per addr */ 295 int i; 296 for(i = 0; i < addr_in_block; i++) 297 if(b_ind(bp)[i] != NO_BLOCK) 298 return(0); 299 return(1); 300 } 301 302 /*===========================================================================* 303 * new_block * 304 *===========================================================================*/ 305 struct buf *new_block(rip, position) 306 register struct inode *rip; /* pointer to inode */ 307 off_t position; /* file pointer */ 308 { 309 /* Acquire a new block and return a pointer to it. */ 310 struct buf *bp; 311 int r; 312 block_t b; 313 314 /* Is another block available? */ 315 if ( (b = read_map(rip, position, 0)) == NO_BLOCK) { 316 /* Check if this position follows last allocated 317 * block. 318 */ 319 block_t goal = NO_BLOCK; 320 if (rip->i_last_pos_bl_alloc != 0) { 321 off_t position_diff = position - rip->i_last_pos_bl_alloc; 322 if (rip->i_bsearch == 0) { 323 /* Should never happen, but not critical */ 324 ext2_debug("warning, i_bsearch is 0, while\ 325 i_last_pos_bl_alloc is not!"); 326 } 327 if (position_diff <= rip->i_sp->s_block_size) { 328 goal = rip->i_bsearch + 1; 329 } else { 330 /* Non-sequential write operation, 331 * disable preallocation 332 * for this inode. 333 */ 334 rip->i_preallocation = 0; 335 discard_preallocated_blocks(rip); 336 } 337 } 338 339 if ( (b = alloc_block(rip, goal) ) == NO_BLOCK) { 340 err_code = ENOSPC; 341 return(NULL); 342 } 343 if ( (r = write_map(rip, position, b, 0)) != OK) { 344 free_block(rip->i_sp, b); 345 err_code = r; 346 ext2_debug("write_map failed\n"); 347 return(NULL); 348 } 349 rip->i_last_pos_bl_alloc = position; 350 if (position == 0) { 351 /* rip->i_last_pos_bl_alloc points to the block position, 352 * and zero indicates first usage, thus just increment. 353 */ 354 rip->i_last_pos_bl_alloc++; 355 } 356 } 357 358 r = lmfs_get_block_ino(&bp, rip->i_dev, b, NO_READ, rip->i_num, 359 rounddown(position, rip->i_sp->s_block_size)); 360 if (r != OK) 361 panic("ext2: error getting block (%llu,%u): %d", rip->i_dev, b, r); 362 zero_block(bp); 363 return(bp); 364 } 365 366 /*===========================================================================* 367 * zero_block * 368 *===========================================================================*/ 369 void zero_block(bp) 370 register struct buf *bp; /* pointer to buffer to zero */ 371 { 372 /* Zero a block. */ 373 ASSERT(bp->data); 374 memset(b_data(bp), 0, lmfs_fs_block_size()); 375 lmfs_markdirty(bp); 376 } 377