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