1 #include "all.h" 2 3 Dentry* 4 getdir(Iobuf *p, int slot) 5 { 6 if(!p) 7 return 0; 8 return (Dentry*)p->iobuf + slot%DIRPERBUF; 9 } 10 11 void 12 accessdir(Iobuf *p, Dentry *d, int f, int uid) 13 { 14 Timet t; 15 16 if(p && p->dev->type != Devro) { 17 p->flags |= Bmod; 18 t = time(nil); 19 if(f & (FREAD|FWRITE)) 20 d->atime = t; 21 if(f & FWRITE) { 22 d->mtime = t; 23 d->muid = uid; 24 d->qid.version++; 25 } 26 } 27 } 28 29 void 30 preread(Device *d, Off addr) 31 { 32 Rabuf *rb; 33 34 if(addr == 0) 35 return; 36 if(raheadq->count+10 >= raheadq->size) /* ugly knowing layout */ 37 return; 38 lock(&rabuflock); 39 rb = rabuffree; 40 if(rb == 0) { 41 unlock(&rabuflock); 42 return; 43 } 44 rabuffree = rb->link; 45 unlock(&rabuflock); 46 rb->dev = d; 47 rb->addr = addr; 48 fs_send(raheadq, rb); 49 } 50 51 Off 52 rel2abs(Iobuf *p, Dentry *d, Off a, int tag, int putb, int uid) 53 { 54 int i; 55 Off addr, qpath, indaddrs = 1, div; 56 Device *dev; 57 58 if(a < 0) { 59 print("rel2abs: neg offset\n"); 60 if(putb) 61 putbuf(p); 62 return 0; 63 } 64 dev = p->dev; 65 qpath = d->qid.path; 66 67 /* is `a' a direct block? */ 68 if(a < NDBLOCK) { 69 addr = d->dblock[a]; 70 if(!addr && tag) { 71 addr = bufalloc(dev, tag, qpath, uid); 72 d->dblock[a] = addr; 73 p->flags |= Bmod|Bimm; 74 } 75 if(putb) 76 putbuf(p); 77 return addr; 78 } 79 a -= NDBLOCK; 80 81 /* 82 * loop through indirect block depths. 83 */ 84 for (i = 0; i < NIBLOCK; i++) { 85 indaddrs *= INDPERBUF; 86 /* is a's disk addr in this indir block or one of its kids? */ 87 if (a < indaddrs) { 88 addr = d->iblocks[i]; 89 if(!addr && tag) { 90 addr = bufalloc(dev, Tind1+i, qpath, uid); 91 d->iblocks[i] = addr; 92 p->flags |= Bmod|Bimm; 93 } 94 if(putb) 95 putbuf(p); 96 97 div = indaddrs; 98 for (; i >= 0; i--) { 99 div /= INDPERBUF; 100 if (div <= 0) 101 panic("rel2abs: non-positive divisor"); 102 addr = indfetch(dev, qpath, addr, 103 (a/div)%INDPERBUF, Tind1+i, 104 (i == 0? tag: Tind1+i-1), uid); 105 } 106 return addr; 107 } 108 a -= indaddrs; 109 } 110 if(putb) 111 putbuf(p); 112 113 /* quintuple-indirect blocks not implemented. */ 114 print("rel2abs: no %d-deep indirect\n", NIBLOCK+1); 115 return 0; 116 } 117 118 /* 119 * read-ahead strategy 120 * on second block, read RAGAP blocks, 121 * thereafter, read RAGAP ahead of current pos 122 */ 123 Off 124 dbufread(Iobuf *p, Dentry *d, Off a, Off ra, int uid) 125 { 126 Off addr; 127 128 if(a == 0) 129 return 1; 130 if(a == 1 && ra == 1) { 131 while(ra < a+RAGAP) { 132 ra++; 133 addr = rel2abs(p, d, ra, 0, 0, uid); 134 if(!addr) 135 return 0; 136 preread(p->dev, addr); 137 } 138 return ra+1; 139 } 140 if(ra == a+RAGAP) { 141 addr = rel2abs(p, d, ra, 0, 0, uid); 142 if(!addr) 143 return 0; 144 preread(p->dev, addr); 145 return ra+1; 146 } 147 return ra; 148 } 149 150 Iobuf* 151 dnodebuf(Iobuf *p, Dentry *d, Off a, int tag, int uid) 152 { 153 Off addr; 154 155 addr = rel2abs(p, d, a, tag, 0, uid); 156 if(addr) 157 return getbuf(p->dev, addr, Brd); 158 return 0; 159 } 160 161 /* 162 * same as dnodebuf but it calls putbuf(p) 163 * to reduce interference. 164 */ 165 Iobuf* 166 dnodebuf1(Iobuf *p, Dentry *d, Off a, int tag, int uid) 167 { 168 Off addr; 169 Device *dev; 170 171 dev = p->dev; 172 addr = rel2abs(p, d, a, tag, 1, uid); 173 if(addr) 174 return getbuf(dev, addr, Brd); 175 return 0; 176 177 } 178 179 Off 180 indfetch(Device* d, Off qpath, Off addr, Off a, int itag, int tag, int uid) 181 { 182 Iobuf *bp; 183 184 if(!addr) 185 return 0; 186 bp = getbuf(d, addr, Brd); 187 if(!bp || checktag(bp, itag, qpath)) { 188 if(!bp) { 189 print("ind fetch bp = 0\n"); 190 return 0; 191 } 192 print("ind fetch tag\n"); 193 putbuf(bp); 194 return 0; 195 } 196 addr = ((Off *)bp->iobuf)[a]; 197 if(!addr && tag) { 198 addr = bufalloc(d, tag, qpath, uid); 199 if(addr) { 200 ((Off *)bp->iobuf)[a] = addr; 201 bp->flags |= Bmod; 202 if(tag == Tdir) 203 bp->flags |= Bimm; 204 settag(bp, itag, qpath); 205 } 206 } 207 putbuf(bp); 208 return addr; 209 } 210 211 /* return INDPERBUF^exp */ 212 Off 213 ibbpow(int exp) 214 { 215 static Off pows[] = { 216 1, 217 INDPERBUF, 218 (Off)INDPERBUF*INDPERBUF, 219 (Off)INDPERBUF*(Off)INDPERBUF*INDPERBUF, 220 (Off)INDPERBUF*(Off)INDPERBUF*(Off)INDPERBUF*INDPERBUF, 221 }; 222 223 if (exp < 0) 224 return 0; 225 else if (exp >= nelem(pows)) { /* not in table? do it long-hand */ 226 Off indpow = 1; 227 228 while (exp-- > 0 && indpow > 0) 229 indpow *= INDPERBUF; 230 return indpow; 231 } else 232 return pows[exp]; 233 } 234 235 /* return sum of INDPERBUF^n for 1 ≤ n ≤ exp */ 236 Off 237 ibbpowsum(int exp) 238 { 239 Off indsum = 0; 240 241 for (; exp > 0; exp--) 242 indsum += ibbpow(exp); 243 return indsum; 244 } 245 246 /* zero bytes past new file length; return an error code */ 247 int 248 trunczero(Truncstate *ts) 249 { 250 int blkoff = ts->newsize % BUFSIZE; 251 Iobuf *pd; 252 253 pd = dnodebuf(ts->p, ts->d, ts->lastblk, Tfile, ts->uid); 254 if (pd == nil || checktag(pd, Tfile, QPNONE)) { 255 if (pd != nil) 256 putbuf(pd); 257 ts->err = Ephase; 258 return Ephase; 259 } 260 memset(pd->iobuf+blkoff, 0, BUFSIZE - blkoff); 261 putbuf(pd); 262 return 0; 263 } 264 265 /* 266 * truncate d (in p) to length `newsize'. 267 * if larger, just increase size. 268 * if smaller, deallocate blocks after last one 269 * still in file at new size. last byte to keep 270 * is newsize-1, due to zero origin. 271 * we free in forward order because it's simpler to get right. 272 * if the final block at the new size is partially-filled, 273 * zero the remainder. 274 */ 275 int 276 dtrunclen(Iobuf *p, Dentry *d, Off newsize, int uid) 277 { 278 int i, pastlast; 279 Truncstate trunc; 280 281 if (newsize <= 0) { 282 dtrunc(p, d, uid); 283 return 0; 284 } 285 memset(&trunc, 0, sizeof trunc); 286 trunc.d = d; 287 trunc.p = p; 288 trunc.uid = uid; 289 trunc.newsize = newsize; 290 trunc.lastblk = newsize/BUFSIZE; 291 if (newsize % BUFSIZE == 0) 292 trunc.lastblk--; 293 else 294 trunczero(&trunc); 295 for (i = 0; i < NDBLOCK; i++) 296 if (trunc.pastlast) { 297 trunc.relblk = i; 298 buffree(p->dev, d->dblock[i], 0, &trunc); 299 d->dblock[i] = 0; 300 } else if (i == trunc.lastblk) 301 trunc.pastlast = 1; 302 trunc.relblk = NDBLOCK; 303 for (i = 0; i < NIBLOCK; i++) { 304 pastlast = trunc.pastlast; 305 buffree(p->dev, d->iblocks[i], i+1, &trunc); 306 if (pastlast) 307 d->iblocks[i] = 0; 308 } 309 310 d->size = newsize; 311 p->flags |= Bmod|Bimm; 312 accessdir(p, d, FWRITE, uid); 313 return trunc.err; 314 } 315 316 /* 317 * truncate d (in p) to zero length. 318 * freeing blocks in reverse order is traditional, from Unix, 319 * in an attempt to keep the free list contiguous. 320 */ 321 void 322 dtrunc(Iobuf *p, Dentry *d, int uid) 323 { 324 int i; 325 326 for (i = NIBLOCK-1; i >= 0; i--) { 327 buffree(p->dev, d->iblocks[i], i+1, nil); 328 d->iblocks[i] = 0; 329 } 330 for (i = NDBLOCK-1; i >= 0; i--) { 331 buffree(p->dev, d->dblock[i], 0, nil); 332 d->dblock[i] = 0; 333 } 334 d->size = 0; 335 p->flags |= Bmod|Bimm; 336 accessdir(p, d, FWRITE, uid); 337 } 338