xref: /plan9/sys/src/9/pcboot/dir.c (revision 25210b069a6ed8c047fa67220cf1dff32812f121)
1 /*
2  * directory reading
3  * from /sys/src/libc/9sys/dirread.c
4  */
5 #include	"u.h"
6 #include	"../port/lib.h"
7 #include	"mem.h"
8 #include	"dat.h"
9 #include	"fns.h"
10 #include	"../port/error.h"
11 #include	"ureg.h"
12 
13 enum
14 {
15 	DIRSIZE	= STATFIXLEN + 16 * 4		/* enough for encoded stat buf + some reasonable strings */
16 };
17 
18 Dir*
dirchstat(Chan * chan)19 dirchstat(Chan *chan)
20 {
21 	Dir *d;
22 	uchar *buf;
23 	int n, nd, i;
24 
25 	nd = DIRSIZE;
26 	for(i=0; i<2; i++){	/* should work by the second try */
27 		d = malloc(sizeof(Dir) + BIT16SZ + nd);
28 		if(d == nil)
29 			return nil;
30 		buf = (uchar*)&d[1];
31 		n = devtab[chan->type]->stat(chan, buf, BIT16SZ + nd);
32 		if(n < BIT16SZ){
33 			free(d);
34 			return nil;
35 		}
36 		nd = GBIT16((uchar*)buf);	/* upper bound on size of Dir + strings */
37 		if(nd <= n){
38 			convM2D(buf, n, d, (char*)&d[1]);
39 			return d;
40 		}
41 		/* else sizeof(Dir)+BIT16SZ+nd is plenty */
42 		free(d);
43 	}
44 	return nil;
45 }
46 
47 long
dirpackage(uchar * buf,long ts,Dir ** d)48 dirpackage(uchar *buf, long ts, Dir **d)
49 {
50 	char *s;
51 	long ss, i, n, nn, m;
52 
53 	*d = nil;
54 	if(ts <= 0)
55 		return 0;
56 
57 	/*
58 	 * first find number of all stats, check they look like stats, & size all associated strings
59 	 */
60 	ss = 0;
61 	n = 0;
62 	for(i = 0; i < ts; i += m){
63 		m = BIT16SZ + GBIT16(&buf[i]);
64 		if(statcheck(&buf[i], m) < 0)
65 			break;
66 		ss += m;
67 		n++;
68 	}
69 
70 	if(i != ts)
71 		return -1;
72 
73 	*d = malloc(n * sizeof(Dir) + ss);
74 	if(*d == nil)
75 		return -1;
76 
77 	/*
78 	 * then convert all buffers
79 	 */
80 	s = (char*)*d + n * sizeof(Dir);
81 	nn = 0;
82 	for(i = 0; i < ts; i += m){
83 		m = BIT16SZ + GBIT16((uchar*)&buf[i]);
84 		if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){
85 			free(*d);
86 			*d = nil;
87 			return -1;
88 		}
89 		nn++;
90 		s += m;
91 	}
92 
93 	return nn;
94 }
95 
96 /*
97  * directory reading, from sysfile.c
98  */
99 
100 long
unionread(Chan * c,void * va,long n)101 unionread(Chan *c, void *va, long n)
102 {
103 	int i;
104 	long nr;
105 	Mhead *m;
106 	Mount *mount;
107 
108 	qlock(&c->umqlock);
109 	m = c->umh;
110 	rlock(&m->lock);
111 	mount = m->mount;
112 	/* bring mount in sync with c->uri and c->umc */
113 	for(i = 0; mount != nil && i < c->uri; i++)
114 		mount = mount->next;
115 
116 	nr = 0;
117 	while(mount != nil){
118 		/* Error causes component of union to be skipped */
119 		if(mount->to && !waserror()){
120 			if(c->umc == nil){
121 				c->umc = cclone(mount->to);
122 				c->umc = devtab[c->umc->type]->open(c->umc, OREAD);
123 			}
124 
125 			nr = devtab[c->umc->type]->read(c->umc, va, n, c->umc->offset);
126 			c->umc->offset += nr;
127 			poperror();
128 		}
129 		if(nr > 0)
130 			break;
131 
132 		/* Advance to next element */
133 		c->uri++;
134 		if(c->umc){
135 			cclose(c->umc);
136 			c->umc = nil;
137 		}
138 		mount = mount->next;
139 	}
140 	runlock(&m->lock);
141 	qunlock(&c->umqlock);
142 	return nr;
143 }
144 
145 void
unionrewind(Chan * c)146 unionrewind(Chan *c)
147 {
148 	qlock(&c->umqlock);
149 	c->uri = 0;
150 	if(c->umc){
151 		cclose(c->umc);
152 		c->umc = nil;
153 	}
154 	qunlock(&c->umqlock);
155 }
156 
157 static int
dirfixed(uchar * p,uchar * e,Dir * d)158 dirfixed(uchar *p, uchar *e, Dir *d)
159 {
160 	int len;
161 
162 	len = GBIT16(p)+BIT16SZ;
163 	if(p + len > e)
164 		return -1;
165 
166 	p += BIT16SZ;	/* ignore size */
167 	d->type = devno(GBIT16(p), 1);
168 	p += BIT16SZ;
169 	d->dev = GBIT32(p);
170 	p += BIT32SZ;
171 	d->qid.type = GBIT8(p);
172 	p += BIT8SZ;
173 	d->qid.vers = GBIT32(p);
174 	p += BIT32SZ;
175 	d->qid.path = GBIT64(p);
176 	p += BIT64SZ;
177 	d->mode = GBIT32(p);
178 	p += BIT32SZ;
179 	d->atime = GBIT32(p);
180 	p += BIT32SZ;
181 	d->mtime = GBIT32(p);
182 	p += BIT32SZ;
183 	d->length = GBIT64(p);
184 
185 	return len;
186 }
187 
188 static char*
dirname(uchar * p,int * n)189 dirname(uchar *p, int *n)
190 {
191 	p += BIT16SZ+BIT16SZ+BIT32SZ+BIT8SZ+BIT32SZ+BIT64SZ
192 		+ BIT32SZ+BIT32SZ+BIT32SZ+BIT64SZ;
193 	*n = GBIT16(p);
194 	return (char*)p+BIT16SZ;
195 }
196 
197 static long
dirsetname(char * name,int len,uchar * p,long n,long maxn)198 dirsetname(char *name, int len, uchar *p, long n, long maxn)
199 {
200 	char *oname;
201 	int olen;
202 	long nn;
203 
204 	if(n == BIT16SZ)
205 		return BIT16SZ;
206 
207 	oname = dirname(p, &olen);
208 
209 	nn = n+len-olen;
210 	PBIT16(p, nn-BIT16SZ);
211 	if(nn > maxn)
212 		return BIT16SZ;
213 
214 	if(len != olen)
215 		memmove(oname+len, oname+olen, p+n-(uchar*)(oname+olen));
216 	PBIT16((uchar*)(oname-2), len);
217 	memmove(oname, name, len);
218 	return nn;
219 }
220 
221 /*
222  * Mountfix might have caused the fixed results of the directory read
223  * to overflow the buffer.  Catch the overflow in c->dirrock.
224  */
225 static void
mountrock(Chan * c,uchar * p,uchar ** pe)226 mountrock(Chan *c, uchar *p, uchar **pe)
227 {
228 	uchar *e, *r;
229 	int len, n;
230 
231 	e = *pe;
232 
233 	/* find last directory entry */
234 	for(;;){
235 		len = BIT16SZ+GBIT16(p);
236 		if(p+len >= e)
237 			break;
238 		p += len;
239 	}
240 
241 	/* save it away */
242 	qlock(&c->rockqlock);
243 	if(c->nrock+len > c->mrock){
244 		n = ROUND(c->nrock+len, 1024);
245 		r = smalloc(n);
246 		memmove(r, c->dirrock, c->nrock);
247 		free(c->dirrock);
248 		c->dirrock = r;
249 		c->mrock = n;
250 	}
251 	memmove(c->dirrock+c->nrock, p, len);
252 	c->nrock += len;
253 	qunlock(&c->rockqlock);
254 
255 	/* drop it */
256 	*pe = p;
257 }
258 
259 /*
260  * Satisfy a directory read with the results saved in c->dirrock.
261  */
262 int
mountrockread(Chan * c,uchar * op,long n,long * nn)263 mountrockread(Chan *c, uchar *op, long n, long *nn)
264 {
265 	long dirlen;
266 	uchar *rp, *erp, *ep, *p;
267 
268 	/* common case */
269 	if(c->nrock == 0)
270 		return 0;
271 
272 	/* copy out what we can */
273 	qlock(&c->rockqlock);
274 	rp = c->dirrock;
275 	erp = rp+c->nrock;
276 	p = op;
277 	ep = p+n;
278 	while(rp+BIT16SZ <= erp){
279 		dirlen = BIT16SZ+GBIT16(rp);
280 		if(p+dirlen > ep)
281 			break;
282 		memmove(p, rp, dirlen);
283 		p += dirlen;
284 		rp += dirlen;
285 	}
286 
287 	if(p == op){
288 		qunlock(&c->rockqlock);
289 		return 0;
290 	}
291 
292 	/* shift the rest */
293 	if(rp != erp)
294 		memmove(c->dirrock, rp, erp-rp);
295 	c->nrock = erp - rp;
296 
297 	*nn = p - op;
298 	qunlock(&c->rockqlock);
299 	return 1;
300 }
301 
302 void
mountrewind(Chan * c)303 mountrewind(Chan *c)
304 {
305 	c->nrock = 0;
306 }
307 
308 /*
309  * Rewrite the results of a directory read to reflect current
310  * name space bindings and mounts.  Specifically, replace
311  * directory entries for bind and mount points with the results
312  * of statting what is mounted there.  Except leave the old names.
313  */
314 long
mountfix(Chan * c,uchar * op,long n,long maxn)315 mountfix(Chan *c, uchar *op, long n, long maxn)
316 {
317 	char *name;
318 	int nbuf, nname;
319 	Chan *nc;
320 	Mhead *mh;
321 	Mount *m;
322 	uchar *p;
323 	int dirlen, rest;
324 	long l;
325 	uchar *buf, *e;
326 	Dir d;
327 
328 	p = op;
329 	buf = nil;
330 	nbuf = 0;
331 	for(e=&p[n]; p+BIT16SZ<e; p+=dirlen){
332 		dirlen = dirfixed(p, e, &d);
333 		if(dirlen < 0)
334 			break;
335 		nc = nil;
336 		mh = nil;
337 		if(findmount(&nc, &mh, d.type, d.dev, d.qid)){
338 			/*
339 			 * If it's a union directory and the original is
340 			 * in the union, don't rewrite anything.
341 			 */
342 			for(m=mh->mount; m; m=m->next)
343 				if(eqchantdqid(m->to, d.type, d.dev, d.qid, 1))
344 					goto Norewrite;
345 
346 			name = dirname(p, &nname);
347 			/*
348 			 * Do the stat but fix the name.  If it fails, leave old entry.
349 			 * BUG: If it fails because there isn't room for the entry,
350 			 * what can we do?  Nothing, really.  Might as well skip it.
351 			 */
352 			if(buf == nil){
353 				buf = smalloc(4096);
354 				nbuf = 4096;
355 			}
356 			if(waserror())
357 				goto Norewrite;
358 			l = devtab[nc->type]->stat(nc, buf, nbuf);
359 			l = dirsetname(name, nname, buf, l, nbuf);
360 			if(l == BIT16SZ)
361 				error("dirsetname");
362 			poperror();
363 
364 			/*
365 			 * Shift data in buffer to accomodate new entry,
366 			 * possibly overflowing into rock.
367 			 */
368 			rest = e - (p+dirlen);
369 			if(l > dirlen){
370 				while(p+l+rest > op+maxn){
371 					mountrock(c, p, &e);
372 					if(e == p){
373 						dirlen = 0;
374 						goto Norewrite;
375 					}
376 					rest = e - (p+dirlen);
377 				}
378 			}
379 			if(l != dirlen){
380 				memmove(p+l, p+dirlen, rest);
381 				dirlen = l;
382 				e = p+dirlen+rest;
383 			}
384 
385 			/*
386 			 * Rewrite directory entry.
387 			 */
388 			memmove(p, buf, l);
389 
390 		    Norewrite:
391 			cclose(nc);
392 			putmhead(mh);
393 		}
394 	}
395 	if(buf)
396 		free(buf);
397 
398 	if(p != e)
399 		error("oops in rockfix");
400 
401 	return e-op;
402 }
403