xref: /plan9/sys/src/cmd/cwfs/dentry.c (revision 01a344a29f2ff35133953eaef092a50fc8c3163b)
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