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