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 zone into an inode 6 * clear_zone: erase a zone in the middle of a file 7 * new_block: acquire a new block 8 * zero_block: overwrite a block with zeroes 9 * 10 */ 11 12 #include "fs.h" 13 #include <string.h> 14 #include <assert.h> 15 #include <sys/param.h> 16 #include "buf.h" 17 #include "inode.h" 18 #include "super.h" 19 20 21 static void wr_indir(struct buf *bp, int index, zone_t zone); 22 static int empty_indir(struct buf *, struct super_block *); 23 24 25 /*===========================================================================* 26 * write_map * 27 *===========================================================================*/ 28 int write_map(rip, position, new_zone, op) 29 struct inode *rip; /* pointer to inode to be changed */ 30 off_t position; /* file address to be mapped */ 31 zone_t new_zone; /* zone # to be inserted */ 32 int op; /* special actions */ 33 { 34 /* Write a new zone into an inode. 35 * 36 * If op includes WMAP_FREE, free the data zone corresponding to that position 37 * in the inode ('new_zone' is ignored then). Also free the indirect block 38 * if that was the last entry in the indirect block. 39 * Also free the double indirect block if that was the last entry in the 40 * double indirect block. 41 */ 42 int scale, ind_ex = 0, new_ind, new_dbl, 43 zones, nr_indirects, single, zindex, ex; 44 zone_t z, z1, z2 = NO_ZONE, old_zone; 45 register block_t b; 46 long excess, zone; 47 struct buf *bp_dindir = NULL, *bp = NULL; 48 49 IN_MARKDIRTY(rip); 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 if(rip->i_zone[zindex] != NO_ZONE && (op & WMAP_FREE)) { 60 free_zone(rip->i_dev, rip->i_zone[zindex]); 61 rip->i_zone[zindex] = NO_ZONE; 62 } else { 63 rip->i_zone[zindex] = new_zone; 64 } 65 return(OK); 66 } 67 68 /* It is not in the inode, so it must be single or double indirect. */ 69 excess = zone - zones; /* first Vx_NR_DZONES don't count */ 70 new_ind = FALSE; 71 new_dbl = FALSE; 72 73 if (excess < nr_indirects) { 74 /* 'position' can be located via the single indirect block. */ 75 z1 = rip->i_zone[zones]; /* single indirect zone */ 76 single = TRUE; 77 } else { 78 /* 'position' can be located via the double indirect block. */ 79 if ( (z2 = z = rip->i_zone[zones+1]) == NO_ZONE && 80 !(op & WMAP_FREE)) { 81 /* Create the double indirect block. */ 82 if ( (z = alloc_zone(rip->i_dev, rip->i_zone[0])) == NO_ZONE) 83 return(err_code); 84 rip->i_zone[zones+1] = z; 85 new_dbl = TRUE; /* set flag for later */ 86 } 87 88 /* 'z' is zone number for double indirect block, either old 89 * or newly created. 90 * If there wasn't one and WMAP_FREE is set, 'z' is NO_ZONE. 91 */ 92 excess -= nr_indirects; /* single indirect doesn't count */ 93 ind_ex = (int) (excess / nr_indirects); 94 excess = excess % nr_indirects; 95 if (ind_ex >= nr_indirects) return(EFBIG); 96 97 if(z == NO_ZONE && (op & WMAP_FREE)) { 98 /* WMAP_FREE and no double indirect block - then no 99 * single indirect block either. 100 */ 101 z1 = NO_ZONE; 102 } else { 103 b = (block_t) z << scale; 104 bp_dindir = get_block(rip->i_dev, b, 105 (new_dbl?NO_READ:NORMAL)); 106 if (new_dbl) zero_block(bp_dindir); 107 z1 = rd_indir(bp_dindir, ind_ex); 108 } 109 single = FALSE; 110 } 111 112 /* z1 is now single indirect zone, or NO_ZONE; 'excess' is index. 113 * We have to create the indirect zone if it's NO_ZONE. Unless 114 * we're freeing (WMAP_FREE). 115 */ 116 if (z1 == NO_ZONE && !(op & WMAP_FREE)) { 117 z1 = alloc_zone(rip->i_dev, rip->i_zone[0]); 118 if (single) 119 rip->i_zone[zones] = z1; /* update inode w. single indirect */ 120 else 121 wr_indir(bp_dindir, ind_ex, z1); /* update dbl indir */ 122 123 new_ind = TRUE; 124 /* If double ind, it is dirty. */ 125 if (bp_dindir != NULL) MARKDIRTY(bp_dindir); 126 if (z1 == NO_ZONE) { 127 /* Release dbl indirect blk. */ 128 put_block(bp_dindir); 129 return(err_code); /* couldn't create single ind */ 130 } 131 } 132 133 /* z1 is indirect block's zone number (unless it's NO_ZONE when we're 134 * freeing). 135 */ 136 if(z1 != NO_ZONE) { 137 ex = (int) excess; /* we need an int here */ 138 b = (block_t) z1 << scale; 139 bp = get_block(rip->i_dev, b, (new_ind ? NO_READ : NORMAL) ); 140 if (new_ind) zero_block(bp); 141 if(op & WMAP_FREE) { 142 if((old_zone = rd_indir(bp, ex)) != NO_ZONE) { 143 free_zone(rip->i_dev, old_zone); 144 wr_indir(bp, ex, NO_ZONE); 145 } 146 147 /* Last reference in the indirect block gone? Then 148 * free the indirect block. 149 */ 150 if(empty_indir(bp, rip->i_sp)) { 151 free_zone(rip->i_dev, z1); 152 z1 = NO_ZONE; 153 /* Update the reference to the indirect block to 154 * NO_ZONE - in the double indirect block if there 155 * is one, otherwise in the inode directly. 156 */ 157 if(single) { 158 rip->i_zone[zones] = z1; 159 } else { 160 wr_indir(bp_dindir, ind_ex, z1); 161 MARKDIRTY(bp_dindir); 162 } 163 } 164 } else { 165 wr_indir(bp, ex, new_zone); 166 } 167 /* z1 equals NO_ZONE only when we are freeing up the indirect block. */ 168 if(z1 != NO_ZONE) MARKDIRTY(bp); 169 put_block(bp); 170 } 171 172 /* If the single indirect block isn't there (or was just freed), 173 * see if we have to keep the double indirect block, if any. 174 * If we don't have to keep it, don't bother writing it out. 175 */ 176 if(z1 == NO_ZONE && !single && z2 != NO_ZONE && 177 empty_indir(bp_dindir, rip->i_sp)) { 178 free_zone(rip->i_dev, z2); 179 rip->i_zone[zones+1] = NO_ZONE; 180 } 181 182 put_block(bp_dindir); /* release double indirect blk */ 183 184 return(OK); 185 } 186 187 188 /*===========================================================================* 189 * wr_indir * 190 *===========================================================================*/ 191 static void wr_indir(bp, index, zone) 192 struct buf *bp; /* pointer to indirect block */ 193 int index; /* index into *bp */ 194 zone_t zone; /* zone to write */ 195 { 196 /* Given a pointer to an indirect block, write one entry. */ 197 198 struct super_block *sp; 199 200 if(bp == NULL) 201 panic("wr_indir() on NULL"); 202 203 sp = &superblock; 204 205 /* write a zone into an indirect block */ 206 assert(sp->s_version == V3); 207 b_v2_ind(bp)[index] = (zone_t) conv4(sp->s_native, (long) zone); 208 } 209 210 211 /*===========================================================================* 212 * empty_indir * 213 *===========================================================================*/ 214 static int empty_indir(bp, sb) 215 struct buf *bp; /* pointer to indirect block */ 216 struct super_block *sb; /* superblock of device block resides on */ 217 { 218 /* Return nonzero if the indirect block pointed to by bp contains 219 * only NO_ZONE entries. 220 */ 221 unsigned int i; 222 for(i = 0; i < V2_INDIRECTS(sb->s_block_size); i++) 223 if( b_v2_ind(bp)[i] != NO_ZONE) 224 return(0); 225 226 return(1); 227 } 228 229 230 /*===========================================================================* 231 * clear_zone * 232 *===========================================================================*/ 233 void clear_zone(rip, pos, flag) 234 register struct inode *rip; /* inode to clear */ 235 off_t __unused pos; /* points to block to clear */ 236 int __unused flag; /* 1 if called by new_block, 0 otherwise */ 237 { 238 /* Zero a zone, possibly starting in the middle. The parameter 'pos' gives 239 * a byte in the first block to be zeroed. Clearzone() is called from 240 * fs_readwrite(), truncate_inode(), and new_block(). 241 */ 242 int scale; 243 244 /* If the block size and zone size are the same, clear_zone() not needed. */ 245 scale = rip->i_sp->s_log_zone_size; 246 assert(scale == 0); 247 return; 248 } 249 250 251 /*===========================================================================* 252 * new_block * 253 *===========================================================================*/ 254 struct buf *new_block(rip, position) 255 register struct inode *rip; /* pointer to inode */ 256 off_t position; /* file pointer */ 257 { 258 /* Acquire a new block and return a pointer to it. Doing so may require 259 * allocating a complete zone, and then returning the initial block. 260 * On the other hand, the current zone may still have some unused blocks. 261 */ 262 struct buf *bp; 263 block_t b, base_block; 264 zone_t z; 265 zone_t zone_size; 266 int scale, r; 267 268 /* Is another block available in the current zone? */ 269 if ( (b = read_map(rip, position, 0)) == NO_BLOCK) { 270 if (rip->i_zsearch == NO_ZONE) { 271 /* First search for this file. Start looking from 272 * the file's first data zone to prevent fragmentation 273 */ 274 if ( (z = rip->i_zone[0]) == NO_ZONE) { 275 /* No first zone for file either, let alloc_zone 276 * decide. */ 277 z = (zone_t) rip->i_sp->s_firstdatazone; 278 } 279 } else { 280 /* searched before, start from last find */ 281 z = rip->i_zsearch; 282 } 283 if ( (z = alloc_zone(rip->i_dev, z)) == NO_ZONE) return(NULL); 284 rip->i_zsearch = z; /* store for next lookup */ 285 if ( (r = write_map(rip, position, z, 0)) != OK) { 286 free_zone(rip->i_dev, z); 287 err_code = r; 288 return(NULL); 289 } 290 291 /* If we are not writing at EOF, clear the zone, just to be safe. */ 292 if ( position != rip->i_size) clear_zone(rip, position, 1); 293 scale = rip->i_sp->s_log_zone_size; 294 base_block = (block_t) z << scale; 295 zone_size = (zone_t) rip->i_sp->s_block_size << scale; 296 b = base_block + (block_t)((position % zone_size)/rip->i_sp->s_block_size); 297 } 298 299 r = lmfs_get_block_ino(&bp, rip->i_dev, b, NO_READ, rip->i_num, 300 rounddown(position, rip->i_sp->s_block_size)); 301 if (r != OK) 302 panic("MFS: error getting block (%llu,%u): %d", rip->i_dev, b, r); 303 zero_block(bp); 304 return(bp); 305 } 306 307 308 /*===========================================================================* 309 * zero_block * 310 *===========================================================================*/ 311 void zero_block(bp) 312 register struct buf *bp; /* pointer to buffer to zero */ 313 { 314 /* Zero a block. */ 315 ASSERT(bp->data); 316 memset(b_data(bp), 0, lmfs_fs_block_size()); 317 MARKDIRTY(bp); 318 } 319 320