1 #include "all.h"
2
3 Dentry*
getdir(Iobuf * p,int slot)4 getdir(Iobuf *p, int slot)
5 {
6 if(!p)
7 return 0;
8 return (Dentry*)p->iobuf + slot%DIRPERBUF;
9 }
10
11 void
accessdir(Iobuf * p,Dentry * d,int f,int uid)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
preread(Device * d,Off addr)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
rel2abs(Iobuf * p,Dentry * d,Off a,int tag,int putb,int uid)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
dbufread(Iobuf * p,Dentry * d,Off a,Off ra,int uid)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*
dnodebuf(Iobuf * p,Dentry * d,Off a,int tag,int uid)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*
dnodebuf1(Iobuf * p,Dentry * d,Off a,int tag,int uid)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
indfetch(Device * d,Off qpath,Off addr,Off a,int itag,int tag,int uid)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
ibbpow(int exp)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
ibbpowsum(int exp)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
trunczero(Truncstate * ts)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
dtrunclen(Iobuf * p,Dentry * d,Off newsize,int uid)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
dtrunc(Iobuf * p,Dentry * d,int uid)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