Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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*
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
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
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
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
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*
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
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
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
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
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
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
}