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
 * partfs - serve an underlying file, with devsd-style partitions
3
 */
4
#include <u.h>
5
#include <libc.h>
6
#include <auth.h>
7
#include <fcall.h>
8
#include <thread.h>
9
#include <9p.h>
10
 
11
typedef struct Part Part;
12
struct Part
13
{
14
	int	inuse;
15
	int	vers;
16
	ulong	mode;
17
	char	*name;
18
	vlong	offset;		/* in sectors */
19
	vlong	length;		/* in sectors */
20
};
21
 
22
enum
23
{
24
	Qroot = 0,
25
	Qdir,
26
	Qctl,
27
	Qpart,
28
};
29
 
30
int fd = -1, ctlfd = -1;
31
int rdonly;
32
ulong ctlmode = 0666;
33
ulong time0;
34
vlong nsect, sectsize;
35
 
36
char *inquiry = "partfs hard drive";
37
char *sdname = "sdXX";
38
Part tab[64];
39
 
40
char*
41
ctlstring(void)
42
{
43
	Part *p;
44
	Fmt fmt;
45
 
46
	fmtstrinit(&fmt);
47
	fmtprint(&fmt, "inquiry %s\n", inquiry);
48
	fmtprint(&fmt, "geometry %lld %lld\n", nsect, sectsize);
49
	for (p = tab; p < tab + nelem(tab); p++)
50
		if (p->inuse)
51
			fmtprint(&fmt, "part %s %lld %lld\n",
52
				p->name, p->offset, p->length);
53
	return fmtstrflush(&fmt);
54
}
55
 
56
int
57
addpart(char *name, vlong start, vlong end)
58
{
59
	Part *p;
60
 
61
	if(start < 0 || start > end || end > nsect){
62
		werrstr("bad partition boundaries");
63
		return -1;
64
	}
65
 
66
	if (strcmp(name, "ctl") == 0 || strcmp(name, "data") == 0) {
67
		werrstr("partition name already in use");
68
		return -1;
69
	}
70
	for (p = tab; p < tab + nelem(tab) && p->inuse; p++)
71
		if (strcmp(p->name, name) == 0) {
72
			werrstr("partition name already in use");
73
			return -1;
74
		}
75
	if(p == tab + nelem(tab)){
76
		werrstr("no free partition slots");
77
		return -1;
78
	}
79
 
80
	p->inuse = 1;
81
	free(p->name);
82
	p->name = estrdup9p(name);
83
	p->offset = start;
84
	p->length = end - start;
85
	p->mode = ctlmode;
86
	p->vers++;
87
	return 0;
88
}
89
 
90
int
91
delpart(char *s)
92
{
93
	Part *p;
94
 
95
	for (p = tab; p < tab + nelem(tab); p++)
96
		if(p->inuse && strcmp(p->name, s) == 0)
97
			break;
98
	if(p == tab + nelem(tab)){
99
		werrstr("partition not found");
100
		return -1;
101
	}
102
 
103
	p->inuse = 0;
104
	free(p->name);
105
	p->name = nil;
106
	return 0;
107
}
108
 
109
static void
110
addparts(char *buf)
111
{
112
	char *f[4], *p, *q;
113
 
114
	/*
115
	 * Use partitions passed from boot program,
116
	 * e.g.
117
	 *	sdC0part=dos 63 123123/plan9 123123 456456
118
	 * This happens before /boot sets hostname so the
119
	 * partitions will have the null-string for user.
120
	 */
121
	for(p = buf; p != nil; p = q){
122
		if(q = strchr(p, '/'))
123
			*q++ = '\0';
124
		if(tokenize(p, f, nelem(f)) >= 3 &&
125
		    addpart(f[0], strtoull(f[1], 0, 0), strtoull(f[2], 0, 0)) < 0)
126
			fprint(2, "%s: addpart %s: %r\n", argv0, f[0]);
127
	}
128
}
129
 
130
static void
131
ctlwrite0(Req *r, char *msg, Cmdbuf *cb)
132
{
133
	vlong start, end;
134
	Part *p;
135
 
136
	r->ofcall.count = r->ifcall.count;
137
 
138
	if(cb->nf < 1){
139
		respond(r, "empty control message");
140
		return;
141
	}
142
 
143
	if(strcmp(cb->f[0], "part") == 0){
144
		if(cb->nf != 4){
145
			respondcmderror(r, cb, "part takes 3 args");
146
			return;
147
		}
148
		start = strtoll(cb->f[2], 0, 0);
149
		end = strtoll(cb->f[3], 0, 0);
150
		if(addpart(cb->f[1], start, end) < 0){
151
			respondcmderror(r, cb, "%r");
152
			return;
153
		}
154
	}
155
	else if(strcmp(cb->f[0], "delpart") == 0){
156
		if(cb->nf != 2){
157
			respondcmderror(r, cb, "delpart takes 1 arg");
158
			return;
159
		}
160
		if(delpart(cb->f[1]) < 0){
161
			respondcmderror(r, cb, "%r");
162
			return;
163
		}
164
	}
165
	else if(strcmp(cb->f[0], "inquiry") == 0){
166
		if(cb->nf != 2){
167
			respondcmderror(r, cb, "inquiry takes 1 arg");
168
			return;
169
		}
170
		free(inquiry);
171
		inquiry = estrdup9p(cb->f[1]);
172
	}
173
	else if(strcmp(cb->f[0], "geometry") == 0){
174
		if(cb->nf != 3){
175
			respondcmderror(r, cb, "geometry takes 2 args");
176
			return;
177
		}
178
		nsect = strtoll(cb->f[1], 0, 0);
179
		sectsize = strtoll(cb->f[2], 0, 0);
180
		if(tab[0].inuse && strcmp(tab[0].name, "data") == 0 &&
181
		    tab[0].vers == 0){
182
			tab[0].offset = 0;
183
			tab[0].length = nsect;
184
		}
185
		for(p = tab; p < tab + nelem(tab); p++)
186
			if(p->inuse && p->offset + p->length > nsect){
187
				p->inuse = 0;
188
				free(p->name);
189
				p->name = nil;
190
			}
191
	} else
192
		/* pass through to underlying ctl file, if any */
193
		if (write(ctlfd, msg, r->ifcall.count) != r->ifcall.count) {
194
			respondcmderror(r, cb, "%r");
195
			return;
196
		}
197
	respond(r, nil);
198
}
199
 
200
void
201
ctlwrite(Req *r)
202
{
203
	char *msg;
204
	Cmdbuf *cb;
205
 
206
	r->ofcall.count = r->ifcall.count;
207
 
208
	msg = emalloc9p(r->ifcall.count+1);
209
	memmove(msg, r->ifcall.data, r->ifcall.count);
210
	msg[r->ifcall.count] = '\0';
211
 
212
	cb = parsecmd(r->ifcall.data, r->ifcall.count);
213
	ctlwrite0(r, msg, cb);
214
 
215
	free(cb);
216
	free(msg);
217
}
218
 
219
int
220
rootgen(int off, Dir *d, void*)
221
{
222
	memset(d, 0, sizeof *d);
223
	d->atime = time0;
224
	d->mtime = time0;
225
	if(off == 0){
226
		d->name = estrdup9p(sdname);
227
		d->mode = DMDIR|0777;
228
		d->qid.path = Qdir;
229
		d->qid.type = QTDIR;
230
		d->uid = estrdup9p("partfs");
231
		d->gid = estrdup9p("partfs");
232
		d->muid = estrdup9p("");
233
		return 0;
234
	}
235
	return -1;
236
}
237
 
238
int
239
dirgen(int off, Dir *d, void*)
240
{
241
	int n;
242
	Part *p;
243
 
244
	memset(d, 0, sizeof *d);
245
	d->atime = time0;
246
	d->mtime = time0;
247
	if(off == 0){
248
		d->name = estrdup9p("ctl");
249
		d->mode = ctlmode;
250
		d->qid.path = Qctl;
251
		goto Have;
252
	}
253
 
254
	off--;
255
	n = 0;
256
	for(p = tab; p < tab + nelem(tab); p++, n++){
257
		if(!p->inuse)
258
			continue;
259
		if(n == off){
260
			d->name = estrdup9p(p->name);
261
			d->length = p->length*sectsize;
262
			d->mode = p->mode;
263
			d->qid.path = Qpart + p - tab;
264
			d->qid.vers = p->vers;
265
			goto Have;
266
		}
267
	}
268
	return -1;
269
 
270
Have:
271
	d->uid = estrdup9p("partfs");
272
	d->gid = estrdup9p("partfs");
273
	d->muid = estrdup9p("");
274
	return 0;
275
}
276
 
277
int
278
rdwrpart(Req *r)
279
{
280
	int q;
281
	long count, tot;
282
	vlong offset;
283
	uchar *dat;
284
	Part *p;
285
 
286
	q = r->fid->qid.path - Qpart;
287
	if(q < 0 || q > nelem(tab) || !tab[q].inuse ||
288
	    tab[q].vers != r->fid->qid.vers){
289
		respond(r, "unknown partition");
290
		return -1;
291
	}
292
	p = &tab[q];
293
 
294
	offset = r->ifcall.offset;
295
	count = r->ifcall.count;
296
	if(offset < 0){
297
		respond(r, "negative offset");
298
		return -1;
299
	}
300
	if(count < 0){
301
		respond(r, "negative count");
302
		return -1;
303
	}
304
	if(offset > p->length*sectsize){
305
		respond(r, "offset past end of partition");
306
		return -1;
307
	}
308
	if(offset+count > p->length*sectsize)
309
		count = p->length*sectsize - offset;
310
	offset += p->offset*sectsize;
311
 
312
	if(r->ifcall.type == Tread)
313
		dat = (uchar*)r->ofcall.data;
314
	else
315
		dat = (uchar*)r->ifcall.data;
316
 
317
	/* pass i/o through to underlying file */
318
	seek(fd, offset, 0);
319
	if (r->ifcall.type == Twrite) {
320
		tot = write(fd, dat, count);
321
		if (tot != count) {
322
			respond(r, "%r");
323
			return -1;
324
		}
325
	} else {
326
		tot = read(fd, dat, count);
327
		if (tot < 0) {
328
			respond(r, "%r");
329
			return -1;
330
		}
331
	}
332
	r->ofcall.count = tot;
333
	respond(r, nil);
334
	return 0;
335
}
336
 
337
void
338
fsread(Req *r)
339
{
340
	char *s;
341
 
342
	switch((int)r->fid->qid.path){
343
	case Qroot:
344
		dirread9p(r, rootgen, nil);
345
		break;
346
	case Qdir:
347
		dirread9p(r, dirgen, nil);
348
		break;
349
	case Qctl:
350
		s = ctlstring();
351
		readstr(r, s);
352
		free(s);
353
		break;
354
	default:
355
		rdwrpart(r);
356
		return;
357
	}
358
	respond(r, nil);
359
}
360
 
361
void
362
fswrite(Req *r)
363
{
364
	switch((int)r->fid->qid.path){
365
	case Qroot:
366
	case Qdir:
367
		respond(r, "write to a directory?");
368
		break;
369
	case Qctl:
370
		ctlwrite(r);
371
		break;
372
	default:
373
		rdwrpart(r);
374
		break;
375
	}
376
}
377
 
378
void
379
fsopen(Req *r)
380
{
381
	if(r->ifcall.mode&ORCLOSE)
382
		respond(r, "cannot open ORCLOSE");
383
 
384
	switch((int)r->fid->qid.path){
385
	case Qroot:
386
	case Qdir:
387
		if(r->ifcall.mode != OREAD){
388
			respond(r, "bad mode for directory open");
389
			return;
390
		}
391
	}
392
 
393
	respond(r, nil);
394
}
395
 
396
void
397
fsstat(Req *r)
398
{
399
	int q;
400
	Dir *d;
401
	Part *p;
402
 
403
	d = &r->d;
404
	memset(d, 0, sizeof *d);
405
	d->qid = r->fid->qid;
406
	d->atime = d->mtime = time0;
407
	q = r->fid->qid.path;
408
	switch(q){
409
	case Qroot:
410
		d->name = estrdup9p("/");
411
		d->mode = DMDIR|0777;
412
		break;
413
 
414
	case Qdir:
415
		d->name = estrdup9p(sdname);
416
		d->mode = DMDIR|0777;
417
		break;
418
 
419
	case Qctl:
420
		d->name = estrdup9p("ctl");
421
		d->mode = 0666;
422
		break;
423
 
424
	default:
425
		q -= Qpart;
426
		if(q < 0 || q > nelem(tab) || tab[q].inuse == 0 ||
427
		    r->fid->qid.vers != tab[q].vers){
428
			respond(r, "partition no longer exists");
429
			return;
430
		}
431
		p = &tab[q];
432
		d->name = estrdup9p(p->name);
433
		d->length = p->length * sectsize;
434
		d->mode = p->mode;
435
		break;
436
	}
437
 
438
	d->uid = estrdup9p("partfs");
439
	d->gid = estrdup9p("partfs");
440
	d->muid = estrdup9p("");
441
	respond(r, nil);
442
}
443
 
444
void
445
fsattach(Req *r)
446
{
447
	char *spec;
448
 
449
	spec = r->ifcall.aname;
450
	if(spec && spec[0]){
451
		respond(r, "invalid attach specifier");
452
		return;
453
	}
454
	r->ofcall.qid = (Qid){Qroot, 0, QTDIR};
455
	r->fid->qid = r->ofcall.qid;
456
	respond(r, nil);
457
}
458
 
459
char*
460
fswalk1(Fid *fid, char *name, Qid *qid)
461
{
462
	Part *p;
463
 
464
	switch((int)fid->qid.path){
465
	case Qroot:
466
		if(strcmp(name, sdname) == 0){
467
			fid->qid.path = Qdir;
468
			fid->qid.type = QTDIR;
469
			*qid = fid->qid;
470
			return nil;
471
		}
472
		break;
473
	case Qdir:
474
		if(strcmp(name, "ctl") == 0){
475
			fid->qid.path = Qctl;
476
			fid->qid.vers = 0;
477
			fid->qid.type = 0;
478
			*qid = fid->qid;
479
			return nil;
480
		}
481
		for(p = tab; p < tab + nelem(tab); p++)
482
			if(p->inuse && strcmp(p->name, name) == 0){
483
				fid->qid.path = p - tab + Qpart;
484
				fid->qid.vers = p->vers;
485
				fid->qid.type = 0;
486
				*qid = fid->qid;
487
				return nil;
488
			}
489
		break;
490
	}
491
	return "file not found";
492
}
493
 
494
Srv fs = {
495
	.attach=fsattach,
496
	.open=	fsopen,
497
	.read=	fsread,
498
	.write=	fswrite,
499
	.stat=	fsstat,
500
	.walk1=	fswalk1,
501
};
502
 
503
char *mtpt = "/dev";
504
char *srvname;
505
 
506
void
507
usage(void)
508
{
509
	fprint(2, "usage: %s [-Dr] [-d sdname] [-m mtpt] [-p 9parts] "
510
		"[-s srvname] diskimage\n", argv0);
511
	fprint(2, "\tdefault mtpt is /dev\n");
512
	exits("usage");
513
}
514
 
515
void
516
main(int argc, char **argv)
517
{
518
	int isdir;
519
	char *file, *cname, *parts;
520
	Dir *dir;
521
 
522
	quotefmtinstall();
523
	time0 = time(0);
524
	parts = nil;
525
 
526
	ARGBEGIN{
527
	case 'D':
528
		chatty9p++;
529
		break;
530
	case 'd':
531
		sdname = EARGF(usage());
532
		break;
533
	case 'm':
534
		mtpt = EARGF(usage());
535
		break;
536
	case 'p':
537
		parts = EARGF(usage());
538
		break;
539
	case 'r':
540
		rdonly = 1;
541
		break;
542
	case 's':
543
		srvname = EARGF(usage());
544
		break;
545
	default:
546
		usage();
547
	}ARGEND
548
 
549
	if(argc != 1)
550
		usage();
551
	file = argv[0];
552
	dir = dirstat(file);
553
	if(!dir)
554
		sysfatal("%s: %r", file);
555
	isdir = (dir->mode & DMDIR) != 0;
556
	free(dir);
557
 
558
	if (isdir) {
559
		cname = smprint("%s/ctl", file);
560
		if ((ctlfd = open(cname, ORDWR)) < 0)
561
			sysfatal("open %s: %r", cname);
562
		file = smprint("%s/data", file);
563
	}
564
	if((fd = open(file, rdonly? OREAD: ORDWR)) < 0)
565
		sysfatal("open %s: %r", file);
566
 
567
	sectsize = 512;			/* conventional */
568
	dir = dirfstat(fd);
569
	if (dir)
570
		nsect = dir->length / sectsize;
571
	free(dir);
572
 
573
	inquiry = estrdup9p(inquiry);
574
	tab[0].inuse = 1;
575
	tab[0].name = estrdup9p("data");
576
	tab[0].mode = 0666;
577
	tab[0].length = nsect;
578
 
579
	/*
580
	 * hack for booting from usb: add 9load partitions.
581
	 */
582
	if(parts != nil)
583
		addparts(parts);
584
 
585
	postmountsrv(&fs, srvname, mtpt, MBEFORE);
586
	exits(nil);
587
}