xref: /plan9/sys/src/cmd/disk/kfs/con.c (revision 8c41de8297cbc34de6620d918a2a8d9038d0b3d4)
1 #include	"all.h"
2 #include	"9p1.h"
3 
4 static	char	elem[NAMELEN];
5 static	Filsys*	cur_fs;
6 static	char	conline[100];
7 
8 void
consserve(void)9 consserve(void)
10 {
11 	con_session();
12 	cmd_exec("cfs");
13 	cmd_exec("user");
14 }
15 
16 int
cmd_exec(char * arg)17 cmd_exec(char *arg)
18 {
19 	char *s, *c;
20 	int i;
21 
22 	for(i=0; s = command[i].string; i++) {
23 		for(c=arg; *s; c++)
24 			if(*c != *s++)
25 				goto brk;
26 		if(*c == '\0' || *c == ' ' || *c == '\t'){
27 			cons.arg = c;
28 			(*command[i].func)();
29 			return 1;
30 		}
31 	brk:;
32 	}
33 	return 0;
34 }
35 
36 void
cmd_check(void)37 cmd_check(void)
38 {
39 	char *s;
40 	int flags;
41 
42 	flags = 0;
43 	for(s = cons.arg; *s; s++){
44 		while(*s == ' ' || *s == '\t')
45 			s++;
46 		if(*s == '\0')
47 			break;
48 		switch(*s){
49 		/* rebuild the free list */
50 		case 'f':	flags |= Cfree;			break;
51 		/* fix bad tags */
52 		case 't':	flags |= Ctag;			break;
53 		/* fix bad tags and clear the contents of the block */
54 		case 'c':	flags |= Cream;			break;
55 		/* delete all redundant references to a block */
56 		case 'd':	flags |= Cbad;			break;
57 		/* read and check tags on all blocks */
58 		case 'r':	flags |= Crdall;		break;
59 		/* write all of the blocks you touch */
60 		case 'w':	flags |= Ctouch;		break;
61 		/* print all directories as they are read */
62 		case 'p':	flags |= Cpdir;			break;
63 		/* print all files as they are read */
64 		case 'P':	flags |= Cpfile;		break;
65 		/* quiet, just report really nasty stuff */
66 		case 'q':	flags |= Cquiet;		break;
67 		}
68 	}
69 	check(cur_fs, flags);
70 }
71 
72 enum
73 {
74 	Sset	= (1<<0),
75 	Setc	= (1<<1),
76 };
77 void
cmd_stats(void)78 cmd_stats(void)
79 {
80 	cprint("work stats\n");
81 	cprint("	work = %A rps\n", (Filta){&cons.work, 1});
82 	cprint("	rate = %A tBps\n", (Filta){&cons.rate, 1000});
83 	cprint("	hits = %A iops\n", (Filta){&cons.bhit, 1});
84 	cprint("	read = %A iops\n", (Filta){&cons.bread, 1});
85 	cprint("	init = %A iops\n", (Filta){&cons.binit, 1});
86 /*	for(i = 0; i < MAXTAG; i++)
87 		cprint("	tag %G = %A iops\n", i, (Filta){&cons.tags[i], 1});
88 */
89 }
90 
91 void
cmd_sync(void)92 cmd_sync(void)
93 {
94 	rlock(&mainlock);
95 	syncall();
96 	runlock(&mainlock);
97 }
98 
99 void
cmd_halt(void)100 cmd_halt(void)
101 {
102 	wlock(&mainlock);
103 	syncall();
104 	superok(cur_fs->dev, superaddr(cur_fs->dev), 1);
105 	print("kfs: file system halted\n");
106 }
107 
108 void
cmd_start(void)109 cmd_start(void)
110 {
111 	superok(cur_fs->dev, superaddr(cur_fs->dev), 0);
112 	wunlock(&mainlock);
113 	print("kfs: file system started\n");
114 }
115 
116 void
cmd_help(void)117 cmd_help(void)
118 {
119 	int i;
120 
121 	for(i=0; command[i].string; i++)
122 		cprint("	%s %s\n", command[i].string, command[i].args);
123 	cprint("check options:\n"
124 		" r	read all blocks\n"
125 		" f	rebuild the free list\n"
126 		" t	fix all bad tags\n"
127 		" c	fix bad tags and zero the blocks\n"
128 		" d	delete all redundant references to blocks\n"
129 		" p	print directories as they are checked\n"
130 		" P	print all files as they are checked\n"
131 		" w	write all blocks that are read\n");
132 }
133 
134 void
cmd_create(void)135 cmd_create(void)
136 {
137 	int uid, gid, err;
138 	long perm;
139 	char oelem[NAMELEN];
140 	char name[NAMELEN];
141 
142 	if(err = con_clone(FID1, FID2)){
143 		cprint("clone failed: %s\n", errstring[err]);
144 		return;
145 	}
146 	if(skipbl(1)){
147 		cprint("skipbl\n");
148 		return;
149 	}
150 	oelem[0] = 0;
151 	while(nextelem()) {
152 		if(oelem[0])
153 			if(err = con_walk(FID2, oelem)){
154 				cprint("walk failed: %s\n", errstring[err]);
155 				return;
156 			}
157 		memmove(oelem, elem, NAMELEN);
158 	}
159 	if(skipbl(1))
160 		return;
161 	uid = strtouid(cname(name));
162 	if(uid == 0){
163 		cprint("unknown user %s\n", name);
164 		return;
165 	}
166 	gid = strtouid(cname(name));
167 	if(gid == 0){
168 		cprint("unknown group %s\n", name);
169 		return;
170 	}
171 	perm = number(0777, 8);
172 	skipbl(0);
173 	for(; *cons.arg; cons.arg++){
174 		if(*cons.arg == 'l')
175 			perm |= PLOCK;
176 		else
177 		if(*cons.arg == 'a')
178 			perm |= PAPND;
179 		else
180 		if(*cons.arg == 'd')
181 			perm |= PDIR;
182 		else
183 			break;
184 	}
185 	err = con_create(FID2, elem, uid, gid, perm, 0);
186 	if(err)
187 		cprint("can't create %s: %s\n", elem, errstring[err]);
188 }
189 
190 void
cmd_clri(void)191 cmd_clri(void)
192 {
193 	if(con_clone(FID1, FID2))
194 		return;
195 	if(skipbl(1))
196 		return;
197 	while(nextelem())
198 		if(con_walk(FID2, elem)){
199 			cprint("can't walk %s\n", elem);
200 			return;
201 		}
202 	con_clri(FID2);
203 }
204 
205 void
cmd_rename(void)206 cmd_rename(void)
207 {
208 	ulong perm;
209 	Dentry d;
210 	char stat[DIRREC];
211 	char oelem[NAMELEN], noelem[NAMELEN], nxelem[NAMELEN];
212 	int err;
213 
214 	if(con_clone(FID1, FID2))
215 		return;
216 	if(skipbl(1))
217 		return;
218 	oelem[0] = 0;
219 	while(nextelem()) {
220 		if(oelem[0])
221 			if(con_walk(FID2, oelem)){
222 				cprint("file does not exits");
223 				return;
224 			}
225 		memmove(oelem, elem, NAMELEN);
226 	}
227 	if(skipbl(1))
228 		return;
229 	if(cons.arg[0]=='/'){
230 		if(con_clone(FID1, FID3))
231 			return;
232 		noelem[0] = 0;
233 		while(nextelem()){
234 			if(noelem[0])
235 				if(con_walk(FID3, noelem)){
236 					cprint("target path %s does not exist", noelem);
237 					return;
238 				}
239 			memmove(noelem, elem, NAMELEN);
240 		}
241 		if(!con_walk(FID3, elem)){
242 			cprint("target %s already exists\n", elem);
243 			return;
244 		}
245 		if(con_walk(FID2, oelem)){
246 			cprint("src %s does not exist\n", oelem);
247 			return;
248 		}
249 		/*
250 		 * we know the target does not exist,
251 		 * the source does exist.
252 		 * to do the rename, create the target and then
253 		 * copy the directory entry directly.  then remove the source.
254 		 */
255 		if(err = con_stat(FID2, stat)){
256 			cprint("can't stat file: %s\n", errstring[err]);
257 			return;
258 		}
259 		convM2D9p1(stat, &d);
260 		perm = (d.mode&0777)|((d.mode&0x7000)<<17);
261 		if(err = con_create(FID3, elem, d.uid, d.gid, perm, (d.mode&DDIR)?OREAD:ORDWR)){
262 			cprint("can't create %s: %s\n", elem, errstring[err]);
263 			return;
264 		}
265 		if(err = con_swap(FID2, FID3)){
266 			cprint("can't swap data: %s\n", errstring[err]);
267 			return;
268 		}
269 		if(err = con_remove(FID2)){
270 			cprint("can't remove file: %s\n", errstring[err]);
271 			return;
272 		}
273 	}else{
274 		cname(nxelem);
275 		if(strchr(nxelem, '/')){
276 			cprint("bad rename target: not full path, but contains slashes\n");
277 			return;
278 		}
279 		if(!con_walk(FID2, nxelem))
280 			cprint("file %s already exists\n", nxelem);
281 		else if(con_walk(FID2, oelem))
282 			cprint("file does not already exist\n");
283 		else if(err = con_stat(FID2, stat))
284 			cprint("can't stat file: %s\n", errstring[err]);
285 		else{
286 			convM2D9p1(stat, &d);
287 			strncpy(d.name, nxelem, NAMELEN);
288 			convD2M9p1(&d, stat);
289 			if(err = con_wstat(FID2, stat))
290 				cprint("can't move file: %s\n", errstring[err]);
291 		}
292 	}
293 }
294 
295 void
cmd_remove(void)296 cmd_remove(void)
297 {
298 	if(con_clone(FID1, FID2))
299 		return;
300 	if(skipbl(1))
301 		return;
302 	while(nextelem())
303 		if(con_walk(FID2, elem)){
304 			cprint("can't walk %s\n", elem);
305 			return;
306 		}
307 	con_remove(FID2);
308 }
309 
310 void
cmd_cfs(void)311 cmd_cfs(void)
312 {
313 	Filsys *fs;
314 
315 	if(*cons.arg != ' ') {
316 		fs = &filesys[0];		/* default */
317 	} else {
318 		if(skipbl(1)){
319 			cprint("skipbl\n");
320 			return;
321 		}
322 		if(!nextelem())
323 			fs = &filesys[0];	/* default */
324 		else
325 			fs = fsstr(elem);
326 	}
327 	if(fs == 0) {
328 		cprint("unknown file system %s\n", elem);
329 		return;
330 	}
331 	if(con_attach(FID1, "adm", fs->name))
332 		panic("FID1 attach to root");
333 	cur_fs = fs;
334 }
335 
336 /*
337  * find out the length of a file
338  * given the mesg version of a stat buffer
339  * we call this because convM2D is different
340  * for the file system than in the os
341  */
342 static uvlong
statlen(char * ap)343 statlen(char *ap)
344 {
345 	uchar *p;
346 	ulong ll, hl;
347 
348 	p = (uchar*)ap;
349 	p += 3*28+5*4;
350 	ll = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
351 	hl = p[4] | (p[5]<<8) | (p[6]<<16) | (p[7]<<24);
352 	return ll | ((uvlong) hl << 32);
353 }
354 
355 int
adduser(char * user,int isgroup)356 adduser(char *user, int isgroup)
357 {
358 	char stat[DIRREC];
359 	char msg[100];
360 	Uid *u;
361 	int i, c, nu;
362 
363 	/*
364 	 * check uniq of name
365 	 * and get next uid
366 	 */
367 	cmd_exec("cfs");
368 	cmd_exec("user");
369 	if(isgroup)
370 		nu = 9000;
371 	else
372 		nu = 0;
373 	for(i=0, u=uid; i<conf.nuid; i++,u++) {
374 		c = u->uid;
375 		if(c == 0)
376 			break;
377 		if(strcmp(uidspace+u->offset, user) == 0)
378 			return 1;
379 		if(c >= 9000 && !isgroup)
380 			continue;
381 		if(c > nu)
382 			nu = c;
383 	}
384 	nu++;
385 	if(isgroup){
386 		if(nu >= 0x10000) {
387 			cprint("out of group ids\n");
388 			return 0;
389 		}
390 	} else {
391 		if(nu >= 9000) {
392 			cprint("out of user ids\n");
393 			return 0;
394 		}
395 	}
396 
397 	/*
398 	 * write onto adm/users
399 	 */
400 	if(con_clone(FID1, FID2)
401 	|| con_path(FID2, "/adm/users")
402 	|| con_open(FID2, 1)) {
403 		cprint("can't open /adm/users\n");
404 		return 0;
405 	}
406 
407 	sprint(msg, "%d:%s:%s:\n", nu, user, user);
408 	cprint("add user %s", msg);
409 	c = strlen(msg);
410 	i = con_stat(FID2, stat);
411 	if(i){
412 		cprint("can't stat /adm/users: %s\n", errstring[i]);
413 		return 0;
414 	}
415 	i = con_write(FID2, msg, statlen(stat), c);
416 	if(i != c){
417 		cprint("short write on /adm/users: %d %d\n", c, i);
418 		return 0;
419 	}
420 	return 1;
421 }
422 
423 void
cmd_newuser(void)424 cmd_newuser(void)
425 {
426 	char user[NAMELEN], param[NAMELEN], msg[100];
427 	int i, c;
428 
429 	/*
430 	 * get uid
431 	 */
432 	cname(user);
433 	cname(param);
434 	for(i=0; i<NAMELEN; i++) {
435 		c = user[i];
436 		if(c == 0)
437 			break;
438 		if(c >= '0' && c <= '9'
439 		|| c >= 'a' && c <= 'z'
440 		|| c >= 'A' && c <= 'Z')
441 			continue;
442 		cprint("bad character in name: 0x%x\n", c);
443 		return;
444 	}
445 	if(i < 2) {
446 		cprint("name too short: %s\n", user);
447 		return;
448 	}
449 	if(i >= NAMELEN) {
450 		cprint("name too long: %s\n", user);
451 		return;
452 	}
453 
454 	switch(param[0]){
455 	case 0:
456 		if(!adduser(user, 0))
457 			return;
458 		cmd_exec("user");
459 		break;
460 	case ':':
461 		adduser(user, 1);
462 		cmd_exec("user");
463 		return;
464 	case '#':
465 		adduser(user, 0);
466 		cmd_exec("user");
467 		return;
468 	}
469 
470 	/*
471 	 * create directories
472 	 */
473 	cmd_exec("user");
474 	sprint(msg, "create /usr/%s %s %s 775 d", user, user, user);
475 	cmd_exec(msg);
476 	sprint(msg, "create /usr/%s/tmp %s %s 775 d", user, user, user);
477 	cmd_exec(msg);
478 	sprint(msg, "create /usr/%s/lib %s %s 775 d", user, user, user);
479 	cmd_exec(msg);
480 	sprint(msg, "create /usr/%s/bin %s %s 775 d", user, user, user);
481 	cmd_exec(msg);
482 	sprint(msg, "create /usr/%s/bin/rc %s %s 775 d", user, user, user);
483 	cmd_exec(msg);
484 	sprint(msg, "create /usr/%s/bin/mips %s %s 775 d", user, user, user);
485 	cmd_exec(msg);
486 	sprint(msg, "create /usr/%s/bin/386 %s %s 775 d", user, user, user);
487 	cmd_exec(msg);
488 	sprint(msg, "create /usr/%s/bin/power %s %s 775 d", user, user, user);
489 	cmd_exec(msg);
490 	sprint(msg, "create /usr/%s/bin/alpha %s %s 775 d", user, user, user);
491 	cmd_exec(msg);
492 }
493 
494 void
cmd_checkuser(void)495 cmd_checkuser(void)
496 {
497 	uchar buf[DIRREC], *p;
498 	static char utime[4];
499 
500 	if(con_clone(FID1, FID2)
501 	|| con_path(FID2, "/adm/users")
502 	|| con_open(FID2, 0)
503 	|| con_stat(FID2, (char*)buf))
504 		return;
505 	p = buf + 3*NAMELEN + 4*4;
506 	if(memcmp(utime, p, 4) == 0)
507 		return;
508 	memmove(utime, p, 4);
509 	cmd_user();
510 }
511 
512 void
cmd_allow(void)513 cmd_allow(void)
514 {
515 	wstatallow = 1;
516 }
517 
518 void
cmd_disallow(void)519 cmd_disallow(void)
520 {
521 	wstatallow = 0;
522 }
523 
524 void
cmd_chat(void)525 cmd_chat(void)
526 {
527 	chat = 1 - chat;
528 }
529 
530 void
cmd_atime(void)531 cmd_atime(void)
532 {
533 	noatime = !noatime;
534 	if(noatime)
535 		cprint("atimes will not be updated\n");
536 	else
537 		cprint("atimes will be updated\n");
538 }
539 
540 void
cmd_noneattach(void)541 cmd_noneattach(void)
542 {
543 	allownone = !allownone;
544 	if(allownone)
545 		cprint("none can attach to new connections\n");
546 	else
547 		cprint("none can only attach on authenticated connections\n");
548 }
549 
550 void
cmd_listen(void)551 cmd_listen(void)
552 {
553 	char addr[NAMELEN];
554 
555 	if(skipbl(0))
556 		strcpy(addr, "tcp!*!564");	/* 9fs */
557 	else
558 		cname(addr);
559 
560 	if(netserve(addr))
561 		cprint("announce %s failed\n", addr);
562 	else
563 		cprint("announce %s\n", addr);
564 }
565 
566 void
cmd_nowritegroup(void)567 cmd_nowritegroup(void)
568 {
569 	writegroup = 0;
570 }
571 
572 Command	command[] =
573 {
574 	"allow",	cmd_allow,	"",
575 	"allowoff",	cmd_disallow,	"",
576 	"atime",		cmd_atime,	"",
577 	"cfs",		cmd_cfs,	"[filesys]",
578 	"chat",		cmd_chat,	"",
579 	"check",	cmd_check,	"[cdfpPqrtw]",
580 	"clri",		cmd_clri,	"filename",
581 	"create",	cmd_create,	"filename user group perm [ald]",
582 	"disallow",	cmd_disallow,	"",
583 	"halt",		cmd_halt,	"",
584 	"help",		cmd_help,	"",
585 	"listen",		cmd_listen,	"[address]",
586 	"newuser",	cmd_newuser,	"username",
587 	"noneattach",	cmd_noneattach,	"",
588 	"nowritegroup",	cmd_nowritegroup,	"",
589 	"remove",	cmd_remove,	"filename",
590 	"rename",	cmd_rename,	"file newname",
591 	"start",	cmd_start, "",
592 	"stats",	cmd_stats,	"[fw]",
593 	"sync",		cmd_sync,	"",
594 	"user",		cmd_user,	"",
595 	0
596 };
597 
598 int
skipbl(int err)599 skipbl(int err)
600 {
601 	if(*cons.arg != ' ') {
602 		if(err)
603 			cprint("syntax error\n");
604 		return 1;
605 	}
606 	while(*cons.arg == ' ')
607 		cons.arg++;
608 	return 0;
609 }
610 
611 char*
_cname(char * name)612 _cname(char *name)
613 {
614 	int i, c;
615 
616 	memset(name, 0, NAMELEN);
617 	for(i=0;; i++) {
618 		c = *cons.arg;
619 		switch(c) {
620 		case ' ':
621 		case '\t':
622 		case '\n':
623 		case '\0':
624 			return name;
625 		}
626 		if(i < NAMELEN-1)
627 			name[i] = c;
628 		cons.arg++;
629 	}
630 }
631 
632 char*
cname(char * name)633 cname(char *name)
634 {
635 	skipbl(0);
636 	return _cname(name);
637 }
638 
639 int
nextelem(void)640 nextelem(void)
641 {
642 	char *e;
643 	int i, c;
644 
645 	e = elem;
646 	while(*cons.arg == '/')
647 		cons.arg++;
648 	c = *cons.arg;
649 	if(c == 0 || c == ' ')
650 		return 0;
651 	for(i = 0; c = *cons.arg; i++) {
652 		if(c == ' ' || c == '/')
653 			break;
654 		if(i == NAMELEN) {
655 			cprint("path name component too long\n");
656 			return 0;
657 		}
658 		*e++ = c;
659 		cons.arg++;
660 	}
661 	*e = 0;
662 	return 1;
663 }
664 
665 long
number(int d,int base)666 number(int d, int base)
667 {
668 	int c, sign, any;
669 	long n;
670 
671 	sign = 0;
672 	any = 0;
673 	n = 0;
674 
675 	c = *cons.arg;
676 	while(c == ' ') {
677 		cons.arg++;
678 		c = *cons.arg;
679 	}
680 	if(c == '-') {
681 		sign = 1;
682 		cons.arg++;
683 		c = *cons.arg;
684 	}
685 	while((c >= '0' && c <= '9') ||
686 	      (base == 16 && c >= 'a' && c <= 'f') ||
687 	      (base == 16 && c >= 'A' && c <= 'F')) {
688 		n *= base;
689 		if(c >= 'a' && c <= 'f')
690 			n += c - 'a' + 10;
691 		else
692 		if(c >= 'A' && c <= 'F')
693 			n += c - 'A' + 10;
694 		else
695 			n += c - '0';
696 		cons.arg++;
697 		c = *cons.arg;
698 		any = 1;
699 	}
700 	if(!any)
701 		return d;
702 	if(sign)
703 		n = -n;
704 	return n;
705 }
706