Subversion Repositories planix.SVN

Rev

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

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <bio.h>
4
#include <auth.h>
5
#include <fcall.h>
6
#include <String.h>
7
#include <libsec.h>
8
 
9
enum
10
{
11
	OPERM	= 0x3,		/* mask of all permission types in open mode */
12
	Maxsize	= 512*1024*1024,
13
	Maxfdata	= 8192,
14
	NAMELEN = 28,
15
};
16
 
17
typedef struct Fid Fid;
18
struct Fid
19
{
20
	short	busy;
21
	int	fid;
22
	Fid	*next;
23
	char	*user;
24
	String	*path;		/* complete path */
25
 
26
	int	fd;		/* set on open or create */
27
	Qid	qid;		/* set on open or create */
28
	int	attach;		/* this is an attach fd */
29
 
30
	ulong	diroff;		/* directory offset */
31
	Dir	*dir;		/* directory entries */
32
	int	ndir;		/* number of directory entries */
33
};
34
 
35
Fid	*fids;
36
int	mfd[2];
37
char	*user;
38
uchar	mdata[IOHDRSZ+Maxfdata];
39
uchar	rdata[Maxfdata];	/* buffer for data in reply */
40
uchar	statbuf[STATMAX];
41
Fcall	thdr;
42
Fcall	rhdr;
43
int	messagesize = sizeof mdata;
44
int	readonly;
45
char	*srvname;
46
int	debug;
47
 
48
Fid *	newfid(int);
49
void	io(void);
50
void	*erealloc(void*, ulong);
51
void	*emalloc(ulong);
52
char	*estrdup(char*);
53
void	usage(void);
54
void	fidqid(Fid*, Qid*);
55
char*	short2long(char*);
56
char*	long2short(char*, int);
57
void	readnames(void);
58
void	post(char*, int);
59
 
60
char	*rflush(Fid*), *rversion(Fid*), *rauth(Fid*),
61
	*rattach(Fid*), *rwalk(Fid*),
62
	*ropen(Fid*), *rcreate(Fid*),
63
	*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
64
	*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
65
 
66
char 	*(*fcalls[])(Fid*) = {
67
	[Tversion]	rversion,
68
	[Tflush]	rflush,
69
	[Tauth]	rauth,
70
	[Tattach]	rattach,
71
	[Twalk]		rwalk,
72
	[Topen]		ropen,
73
	[Tcreate]	rcreate,
74
	[Tread]		rread,
75
	[Twrite]	rwrite,
76
	[Tclunk]	rclunk,
77
	[Tremove]	rremove,
78
	[Tstat]		rstat,
79
	[Twstat]	rwstat,
80
};
81
 
82
char	Eperm[] =	"permission denied";
83
char	Enotdir[] =	"not a directory";
84
char	Enoauth[] =	"lnfs: authentication not required";
85
char	Enotexist[] =	"file does not exist";
86
char	Einuse[] =	"file in use";
87
char	Eexist[] =	"file exists";
88
char	Eisdir[] =	"file is a directory";
89
char	Enotowner[] =	"not owner";
90
char	Eisopen[] = 	"file already open for I/O";
91
char	Excl[] = 	"exclusive use file already open";
92
char	Ename[] = 	"illegal name";
93
char	Eversion[] =	"unknown 9P version";
94
 
95
void
96
usage(void)
97
{
98
	fprint(2, "usage: %s [-r] [-s srvname] mountpoint\n", argv0);
99
	exits("usage");
100
}
101
 
102
void
103
notifyf(void *a, char *s)
104
{
105
	USED(a);
106
	if(strncmp(s, "interrupt", 9) == 0)
107
		noted(NCONT);
108
	noted(NDFLT);
109
}
110
 
111
void
112
main(int argc, char *argv[])
113
{
114
	char *defmnt;
115
	int p[2];
116
	Dir *d;
117
 
118
	ARGBEGIN{
119
	case 'r':
120
		readonly = 1;
121
		break;
122
	case 'd':
123
		debug = 1;
124
		break;
125
	case 's':
126
		srvname = ARGF();
127
		if(srvname == nil)
128
			usage();
129
		break;
130
	default:
131
		usage();
132
	}ARGEND
133
 
134
	if(argc < 1)
135
		usage();
136
	defmnt = argv[0];
137
	d = dirstat(defmnt);
138
	if(d == nil || !(d->qid.type & QTDIR))
139
		sysfatal("mountpoint must be an accessible directory");
140
	free(d);
141
 
142
	if(pipe(p) < 0)
143
		sysfatal("pipe failed");
144
	mfd[0] = p[0];
145
	mfd[1] = p[0];
146
 
147
	user = getuser();
148
	notify(notifyf);
149
 
150
	if(srvname != nil)
151
		post(srvname, p[1]);
152
 
153
	if(debug)
154
		fmtinstall('F', fcallfmt);
155
	switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
156
	case -1:
157
		sysfatal("fork: %r");
158
	case 0:
159
		close(p[1]);
160
		chdir(defmnt);
161
		io();
162
		break;
163
	default:
164
		close(p[0]);	/* don't deadlock if child fails */
165
		if(mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0)
166
			sysfatal("mount failed: %r");
167
	}
168
	exits(0);
169
}
170
 
171
void
172
post(char *srvname, int pfd)
173
{
174
	char name[128];
175
	int fd;
176
 
177
	snprint(name, sizeof name, "#s/%s", srvname);
178
	fd = create(name, OWRITE, 0666);
179
	if(fd < 0)
180
		sysfatal("create of %s failed: %r", srvname);
181
	sprint(name, "%d", pfd);
182
	if(write(fd, name, strlen(name)) < 0)
183
		sysfatal("writing %s: %r", srvname);
184
}
185
char*
186
rversion(Fid*)
187
{
188
	Fid *f;
189
 
190
	for(f = fids; f; f = f->next)
191
		if(f->busy)
192
			rclunk(f);
193
	if(thdr.msize > sizeof mdata)
194
		rhdr.msize = sizeof mdata;
195
	else
196
		rhdr.msize = thdr.msize;
197
	messagesize = rhdr.msize;
198
	if(strncmp(thdr.version, "9P2000", 6) != 0)
199
		return Eversion;
200
	rhdr.version = "9P2000";
201
	return nil;
202
}
203
 
204
char*
205
rauth(Fid*)
206
{
207
	return Enoauth;
208
}
209
 
210
char*
211
rflush(Fid *f)
212
{
213
	USED(f);
214
	return nil;
215
}
216
 
217
char*
218
rattach(Fid *f)
219
{
220
	/* no authentication! */
221
	f->busy = 1;
222
	if(thdr.uname[0])
223
		f->user = estrdup(thdr.uname);
224
	else
225
		f->user = "none";
226
	if(strcmp(user, "none") == 0)
227
		user = f->user;
228
	if(f->path)
229
		s_free(f->path);
230
	f->path = s_copy(".");
231
	fidqid(f, &rhdr.qid);
232
	f->attach = 1;
233
	return nil;
234
}
235
 
236
char*
237
clone(Fid *f, Fid **nf)
238
{
239
	if(f->fd >= 0)
240
		return Eisopen;
241
	*nf = newfid(thdr.newfid);
242
	(*nf)->busy = 1;
243
	if((*nf)->path)
244
		s_free((*nf)->path);
245
	(*nf)->path = s_clone(f->path);
246
	(*nf)->fd = -1;
247
	(*nf)->user = f->user;
248
	(*nf)->attach = 0;
249
	return nil;
250
}
251
 
252
char*
253
rwalk(Fid *f)
254
{
255
	char *name;
256
	Fid *nf;
257
	char *err;
258
	int i;
259
	String *npath;
260
	Dir *d;
261
	char *cp;
262
	Qid qid;
263
 
264
	err = nil;
265
	nf = nil;
266
	rhdr.nwqid = 0;
267
	if(rhdr.newfid != rhdr.fid){
268
		err = clone(f, &nf);
269
		if(err)
270
			return err;
271
		f = nf;	/* walk the new fid */
272
	}
273
	readnames();
274
	npath = s_clone(f->path);
275
	if(thdr.nwname > 0){
276
		for(i=0; i<thdr.nwname && i<MAXWELEM; i++){
277
			name = long2short(thdr.wname[i], 0);
278
			if(strcmp(name, ".") == 0){
279
				;
280
			} else if(strcmp(name, "..") == 0){
281
				cp = strrchr(s_to_c(npath), '/');
282
				if(cp != nil){
283
					*cp = 0;
284
					npath->ptr = cp;
285
				}
286
			} else {
287
				s_append(npath, "/");
288
				s_append(npath, name);
289
			}
290
			d = dirstat(s_to_c(npath));
291
			if(d == nil)
292
				break;
293
			rhdr.nwqid++;
294
			qid = d->qid;
295
			rhdr.wqid[i] = qid;
296
			free(d);
297
		}
298
		if(i==0 && err == nil)
299
			err = Enotexist;
300
	}
301
 
302
	/* if there was an error and we cloned, get rid of the new fid */
303
	if(nf != nil && (err!=nil || rhdr.nwqid<thdr.nwname)){
304
		f->busy = 0;
305
		s_free(npath);
306
	}
307
 
308
	/* update the fid after a successful walk */
309
	if(rhdr.nwqid == thdr.nwname){
310
		s_free(f->path);
311
		f->path = npath;
312
	}
313
	return err;
314
}
315
 
316
static char*
317
passerror(void)
318
{
319
	static char err[256];
320
 
321
	rerrstr(err, sizeof err);
322
	return err;
323
}
324
 
325
char*
326
ropen(Fid *f)
327
{
328
	if(readonly && (thdr.mode & 3))
329
		return Eperm;
330
	if(f->fd >= 0)
331
		return Eisopen;
332
	f->fd = open(s_to_c(f->path), thdr.mode);
333
	if(f->fd < 0)
334
		return passerror();
335
	fidqid(f, &rhdr.qid);
336
	f->qid = rhdr.qid;
337
	rhdr.iounit = messagesize-IOHDRSZ;
338
	return nil;
339
}
340
 
341
char*
342
rcreate(Fid *f)
343
{
344
	char *name;
345
 
346
	if(readonly)
347
		return Eperm;
348
	readnames();
349
	name = long2short(thdr.name, 1);
350
	if(f->fd >= 0)
351
		return Eisopen;
352
	s_append(f->path, "/");
353
	s_append(f->path, name);
354
	f->fd = create(s_to_c(f->path), thdr.mode, thdr.perm);
355
	if(f->fd < 0)
356
		return passerror();
357
	fidqid(f, &rhdr.qid);
358
	f->qid = rhdr.qid;
359
	rhdr.iounit = messagesize-IOHDRSZ;
360
	return nil;
361
}
362
 
363
char*
364
rreaddir(Fid *f)
365
{
366
	int i;
367
	int n;
368
 
369
	/* reread the directory */
370
	if(thdr.offset == 0){
371
		if(f->dir)
372
			free(f->dir);
373
		f->dir = nil;
374
		if(f->diroff != 0)
375
			seek(f->fd, 0, 0);
376
		f->ndir = dirreadall(f->fd, &f->dir);
377
		f->diroff = 0;
378
		if(f->ndir < 0)
379
			return passerror();
380
		readnames();
381
		for(i = 0; i < f->ndir; i++)
382
			f->dir[i].name = short2long(f->dir[i].name);
383
	}
384
 
385
	/* copy in as many directory entries as possible */
386
	for(n = 0; f->diroff < f->ndir; n += i){
387
		i = convD2M(&f->dir[f->diroff], rdata+n, thdr.count - n);
388
		if(i <= BIT16SZ)
389
			break;
390
		f->diroff++;
391
	}
392
	rhdr.data = (char*)rdata;
393
	rhdr.count = n;
394
	return nil;
395
}
396
 
397
char*
398
rread(Fid *f)
399
{
400
	long n;
401
 
402
	if(f->fd < 0)
403
		return Enotexist;
404
	if(thdr.count > messagesize-IOHDRSZ)
405
		thdr.count = messagesize-IOHDRSZ;
406
	if(f->qid.type & QTDIR)
407
		return rreaddir(f);
408
	n = pread(f->fd, rdata, thdr.count, thdr.offset);
409
	if(n < 0)
410
		return passerror();
411
	rhdr.data = (char*)rdata;
412
	rhdr.count = n;
413
	return nil;
414
}
415
 
416
char*
417
rwrite(Fid *f)
418
{
419
	long n;
420
 
421
	if(readonly || (f->qid.type & QTDIR))
422
		return Eperm;
423
	if(f->fd < 0)
424
		return Enotexist;
425
	if(thdr.count > messagesize-IOHDRSZ)	/* shouldn't happen, anyway */
426
		thdr.count = messagesize-IOHDRSZ;
427
	n = pwrite(f->fd, thdr.data, thdr.count, thdr.offset);
428
	if(n < 0)
429
		return passerror();
430
	rhdr.count = n;
431
	return nil;
432
}
433
 
434
char*
435
rclunk(Fid *f)
436
{
437
	f->busy = 0;
438
	close(f->fd);
439
	f->fd = -1;
440
	f->path = s_reset(f->path);
441
	if(f->attach){
442
		free(f->user);
443
		f->user = nil;
444
	}
445
	f->attach = 0;
446
	if(f->dir != nil){
447
		free(f->dir);
448
		f->dir = nil;
449
	}
450
	f->diroff = f->ndir = 0;
451
	return nil;
452
}
453
 
454
char*
455
rremove(Fid *f)
456
{
457
	if(remove(s_to_c(f->path)) < 0)
458
		return passerror();
459
	return nil;
460
}
461
 
462
char*
463
rstat(Fid *f)
464
{
465
	int n;
466
	Dir *d;
467
 
468
	d = dirstat(s_to_c(f->path));
469
	if(d == nil)
470
		return passerror();
471
	d->name = short2long(d->name);
472
	n = convD2M(d, statbuf, sizeof statbuf);
473
	free(d);
474
	if(n <= BIT16SZ)
475
		return passerror();
476
	rhdr.nstat = n;
477
	rhdr.stat = statbuf;
478
	return nil;
479
}
480
 
481
char*
482
rwstat(Fid *f)
483
{
484
	int n;
485
	Dir d;
486
 
487
	if(readonly)
488
		return Eperm;
489
	convM2D(thdr.stat, thdr.nstat, &d, (char*)rdata);
490
	d.name = long2short(d.name, 1);
491
	n = dirwstat(s_to_c(f->path), &d);
492
	if(n < 0)
493
		return passerror();
494
	return nil;
495
}
496
 
497
Fid *
498
newfid(int fid)
499
{
500
	Fid *f, *ff;
501
 
502
	ff = 0;
503
	for(f = fids; f; f = f->next)
504
		if(f->fid == fid)
505
			return f;
506
		else if(!ff && !f->busy)
507
			ff = f;
508
	if(ff){
509
		ff->fid = fid;
510
		return ff;
511
	}
512
	f = emalloc(sizeof *f);
513
	f->path = s_reset(f->path);
514
	f->fd = -1;
515
	f->fid = fid;
516
	f->next = fids;
517
	fids = f;
518
	return f;
519
}
520
 
521
void
522
io(void)
523
{
524
	char *err;
525
	int n, pid;
526
 
527
	pid = getpid();
528
 
529
	for(;;){
530
		/*
531
		 * reading from a pipe or a network device
532
		 * will give an error after a few eof reads.
533
		 * however, we cannot tell the difference
534
		 * between a zero-length read and an interrupt
535
		 * on the processes writing to us,
536
		 * so we wait for the error.
537
		 */
538
		n = read9pmsg(mfd[0], mdata, messagesize);
539
		if(n < 0)
540
			sysfatal("mount read");
541
		if(n == 0)
542
			continue;
543
		if(convM2S(mdata, n, &thdr) == 0)
544
			continue;
545
 
546
		if(debug)
547
			fprint(2, "%s %d:<-%F\n", argv0, pid, &thdr);
548
 
549
		if(!fcalls[thdr.type])
550
			err = "bad fcall type";
551
		else
552
			err = (*fcalls[thdr.type])(newfid(thdr.fid));
553
		if(err){
554
			rhdr.type = Rerror;
555
			rhdr.ename = err;
556
		}else{
557
			rhdr.type = thdr.type + 1;
558
			rhdr.fid = thdr.fid;
559
		}
560
		rhdr.tag = thdr.tag;
561
		if(debug)
562
			fprint(2, "%s %d:->%F\n", argv0, pid, &rhdr);/**/
563
		n = convS2M(&rhdr, mdata, messagesize);
564
		if(n == 0)
565
			sysfatal("convS2M error on write");
566
		if(write(mfd[1], mdata, n) != n)
567
			sysfatal("mount write");
568
	}
569
}
570
 
571
void *
572
emalloc(ulong n)
573
{
574
	void *p;
575
 
576
	p = malloc(n);
577
	if(!p)
578
		sysfatal("out of memory");
579
	memset(p, 0, n);
580
	return p;
581
}
582
 
583
void *
584
erealloc(void *p, ulong n)
585
{
586
	p = realloc(p, n);
587
	if(!p)
588
		sysfatal("out of memory");
589
	return p;
590
}
591
 
592
char *
593
estrdup(char *q)
594
{
595
	char *p;
596
	int n;
597
 
598
	n = strlen(q)+1;
599
	p = malloc(n);
600
	if(!p)
601
		sysfatal("out of memory");
602
	memmove(p, q, n);
603
	return p;
604
}
605
 
606
void
607
fidqid(Fid *f, Qid *q)
608
{
609
	Dir *d;
610
 
611
	d = dirstat(s_to_c(f->path));
612
	if(d == nil)
613
		*q = (Qid){0, 0, QTFILE};
614
	else {
615
		*q = d->qid;
616
		free(d);
617
	}
618
}
619
 
620
/*
621
 *  table of name translations
622
 *
623
 *  the file ./.longnames contains all the known long names.
624
 *  the short name is the first NAMELEN-1 bytes of the base64
625
 *  encoding of the MD5 hash of the longname.
626
 */
627
 
628
typedef struct Name Name;
629
struct Name
630
{
631
	Name	*next;
632
	char	shortname[NAMELEN];
633
	char	*longname;
634
};
635
 
636
Dir *dbstat;	/* last stat of the name file */
637
char *namefile = "./.longnames";
638
char *namebuf;
639
Name *names;
640
 
641
Name*
642
newname(char *longname, int writeflag)
643
{
644
	Name *np;
645
	int n;
646
	uchar digest[MD5dlen];
647
	int fd;
648
 
649
	/* chain in new name */
650
	n = strlen(longname);
651
	np = emalloc(sizeof(*np)+n+1);
652
	np->longname = (char*)&np[1];
653
	strcpy(np->longname, longname);
654
	md5((uchar*)longname, n, digest, nil);
655
	enc32(np->shortname, sizeof(np->shortname), digest, MD5dlen);
656
	np->next = names;
657
	names = np;
658
 
659
	/* don't change namefile if we're read only */
660
	if(!writeflag)
661
		return np;
662
 
663
	/* add to namefile */
664
	fd = open(namefile, OWRITE);
665
	if(fd >= 0){
666
		seek(fd, 0, 2);
667
		fprint(fd, "%s\n", longname);
668
		close(fd);
669
	}
670
	return np;
671
}
672
 
673
void
674
freenames(void)
675
{
676
	Name *np, *next;
677
 
678
	for(np = names; np != nil; np = next){
679
		next = np->next;
680
		free(np);
681
	}
682
	names = nil;
683
}
684
 
685
/*
686
 *  reread the file if the qid.path has changed.
687
 *
688
 *  read any new entries if length has changed.
689
 */
690
void
691
readnames(void)
692
{
693
	Dir *d;
694
	int fd;
695
	vlong offset;
696
	Biobuf *b;
697
	char *p;
698
 
699
	d = dirstat(namefile);
700
	if(d == nil){
701
		if(readonly)
702
			return;
703
 
704
		/* create file if it doesn't exist */
705
		fd = create(namefile, OREAD, DMAPPEND|0666);
706
		if(fd < 0)
707
			return;
708
		if(dbstat != nil)
709
			free(dbstat);
710
		dbstat = nil;
711
		close(fd);
712
		return;
713
	}
714
 
715
	/* up to date? */
716
	offset = 0;
717
	if(dbstat != nil){
718
		if(d->qid.path == dbstat->qid.path){
719
			if(d->length <= dbstat->length){
720
				free(d);
721
				return;
722
			}
723
			offset = dbstat->length;
724
		} else {
725
			freenames();
726
		}
727
		free(dbstat);
728
		dbstat = nil;
729
	}
730
 
731
	/* read file */
732
	b = Bopen(namefile, OREAD);
733
	if(b == nil){
734
		free(d);
735
		return;
736
	}
737
	Bseek(b, offset, 0);
738
	while((p = Brdline(b, '\n')) != nil){
739
		p[Blinelen(b)-1] = 0;
740
		newname(p, 0);
741
	}
742
	Bterm(b);
743
	dbstat = d;
744
}
745
 
746
/*
747
 *  look up a long name,  if it doesn't exist in the
748
 *  file, add an entry to the file if the writeflag is
749
 *  non-zero.  Return a pointer to the short name.
750
 */
751
char*
752
long2short(char *longname, int writeflag)
753
{
754
	Name *np;
755
 
756
	if(strlen(longname) < NAMELEN-1 && strpbrk(longname, " ")==nil)
757
		return longname;
758
 
759
	for(np = names; np != nil; np = np->next)
760
		if(strcmp(longname, np->longname) == 0)
761
			return np->shortname;
762
	if(!writeflag)
763
		return longname;
764
	np = newname(longname, !readonly);
765
	return np->shortname;
766
}
767
 
768
/*
769
 *  look up a short name, if it doesn't exist, return the
770
 *  longname.
771
 */
772
char*
773
short2long(char *shortname)
774
{
775
	Name *np;
776
 
777
	for(np = names; np != nil; np = np->next)
778
		if(strcmp(shortname, np->shortname) == 0)
779
			return np->longname;
780
	return shortname;
781
}