xref: /plan9/sys/src/cmd/unix/drawterm/kern/devfs-posix.c (revision 58da3067adcdccaaa043d0bfde28ba83b7ced07d)
1 #include	"u.h"
2 #include	<sys/types.h>
3 #include	<sys/stat.h>
4 #include	<dirent.h>
5 #include	<fcntl.h>
6 #include	<errno.h>
7 #include	<stdio.h> /* for remove, rename */
8 #include	<limits.h>
9 
10 #ifndef NAME_MAX
11 #	define NAME_MAX 256
12 #endif
13 #include	"lib.h"
14 #include	"dat.h"
15 #include	"fns.h"
16 #include	"error.h"
17 
18 
19 typedef	struct Ufsinfo	Ufsinfo;
20 
21 enum
22 {
23 	NUID	= 256,
24 	NGID	= 256,
25 	MAXPATH	= 1024,
26 	MAXCOMP	= 128
27 };
28 
29 struct Ufsinfo
30 {
31 	int	mode;
32 	int	fd;
33 	int	uid;
34 	int	gid;
35 	DIR*	dir;
36 	vlong	offset;
37 	QLock	oq;
38 	char nextname[NAME_MAX];
39 };
40 
41 char	*base = "/";
42 
43 static	Qid	fsqid(char*, struct stat *);
44 static	void	fspath(Chan*, char*, char*);
45 static	ulong	fsdirread(Chan*, uchar*, int, ulong);
46 static	int	fsomode(int);
47 
48 /* clumsy hack, but not worse than the Path stuff in the last one */
49 static char*
uc2name(Chan * c)50 uc2name(Chan *c)
51 {
52 	char *s;
53 
54 	if(c->name == nil)
55 		return "/";
56 	s = c2name(c);
57 	if(s[0]=='#' && s[1]=='U')
58 		return s+2;
59 	return s;
60 }
61 
62 static char*
lastelem(Chan * c)63 lastelem(Chan *c)
64 {
65 	char *s, *t;
66 
67 	s = uc2name(c);
68 	if((t = strrchr(s, '/')) == nil)
69 		return s;
70 	if(t[1] == 0)
71 		return t;
72 	return t+1;
73 }
74 
75 static Chan*
fsattach(char * spec)76 fsattach(char *spec)
77 {
78 	Chan *c;
79 	struct stat stbuf;
80 	static int devno;
81 	Ufsinfo *uif;
82 
83 	if(stat(base, &stbuf) < 0)
84 		error(strerror(errno));
85 
86 	c = devattach('U', spec);
87 
88 	uif = mallocz(sizeof(Ufsinfo), 1);
89 	uif->mode = stbuf.st_mode;
90 	uif->uid = stbuf.st_uid;
91 	uif->gid = stbuf.st_gid;
92 
93 	c->aux = uif;
94 	c->dev = devno++;
95 	c->qid.type = QTDIR;
96 /*print("fsattach %s\n", c2name(c));*/
97 
98 	return c;
99 }
100 
101 static Chan*
fsclone(Chan * c,Chan * nc)102 fsclone(Chan *c, Chan *nc)
103 {
104 	Ufsinfo *uif;
105 
106 	uif = mallocz(sizeof(Ufsinfo), 1);
107 	*uif = *(Ufsinfo*)c->aux;
108 	nc->aux = uif;
109 
110 	return nc;
111 }
112 
113 static int
fswalk1(Chan * c,char * name)114 fswalk1(Chan *c, char *name)
115 {
116 	struct stat stbuf;
117 	char path[MAXPATH];
118 	Ufsinfo *uif;
119 
120 	fspath(c, name, path);
121 
122 	/*print("** fs walk '%s' -> %s\n", path, name);  */
123 
124 	if(stat(path, &stbuf) < 0)
125 		return 0;
126 
127 	uif = c->aux;
128 
129 	uif->mode = stbuf.st_mode;
130 	uif->uid = stbuf.st_uid;
131 	uif->gid = stbuf.st_gid;
132 
133 	c->qid = fsqid(path, &stbuf);
134 
135 	return 1;
136 }
137 
138 extern Cname* addelem(Cname*, char*);
139 
140 static Walkqid*
fswalk(Chan * c,Chan * nc,char ** name,int nname)141 fswalk(Chan *c, Chan *nc, char **name, int nname)
142 {
143 	int i;
144 	Cname *cname;
145 	Walkqid *wq;
146 
147 	if(nc != nil)
148 		panic("fswalk: nc != nil");
149 	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
150 	nc = devclone(c);
151 	cname = c->name;
152 	incref(&cname->ref);
153 
154 	fsclone(c, nc);
155 	wq->clone = nc;
156 	for(i=0; i<nname; i++){
157 		nc->name = cname;
158 		if(fswalk1(nc, name[i]) == 0)
159 			break;
160 		cname = addelem(cname, name[i]);
161 		wq->qid[i] = nc->qid;
162 	}
163 	nc->name = cname;
164 	if(i != nname){
165 		cclose(nc);
166 		wq->clone = nil;
167 	}
168 	wq->nqid = i;
169 	return wq;
170 }
171 
172 static int
fsstat(Chan * c,uchar * buf,int n)173 fsstat(Chan *c, uchar *buf, int n)
174 {
175 	Dir d;
176 	struct stat stbuf;
177 	char path[MAXPATH];
178 
179 	if(n < BIT16SZ)
180 		error(Eshortstat);
181 
182 	fspath(c, 0, path);
183 	if(stat(path, &stbuf) < 0)
184 		error(strerror(errno));
185 
186 	d.name = lastelem(c);
187 	d.uid = "unknown";
188 	d.gid = "unknown";
189 	d.muid = "unknown";
190 	d.qid = c->qid;
191 	d.mode = (c->qid.type<<24)|(stbuf.st_mode&0777);
192 	d.atime = stbuf.st_atime;
193 	d.mtime = stbuf.st_mtime;
194 	d.length = stbuf.st_size;
195 	d.type = 'U';
196 	d.dev = c->dev;
197 	return convD2M(&d, buf, n);
198 }
199 
200 static Chan*
fsopen(Chan * c,int mode)201 fsopen(Chan *c, int mode)
202 {
203 	char path[MAXPATH];
204 	int m, isdir;
205 	Ufsinfo *uif;
206 
207 /*print("fsopen %s\n", c2name(c));*/
208 	m = mode & (OTRUNC|3);
209 	switch(m) {
210 	case 0:
211 		break;
212 	case 1:
213 	case 1|16:
214 		break;
215 	case 2:
216 	case 0|16:
217 	case 2|16:
218 		break;
219 	case 3:
220 		break;
221 	default:
222 		error(Ebadarg);
223 	}
224 
225 	isdir = c->qid.type & QTDIR;
226 
227 	if(isdir && mode != OREAD)
228 		error(Eperm);
229 
230 	m = fsomode(m & 3);
231 	c->mode = openmode(mode);
232 
233 	uif = c->aux;
234 
235 	fspath(c, 0, path);
236 	if(isdir) {
237 		uif->dir = opendir(path);
238 		if(uif->dir == 0)
239 			error(strerror(errno));
240 	}
241 	else {
242 		if(mode & OTRUNC)
243 			m |= O_TRUNC;
244 		uif->fd = open(path, m, 0666);
245 
246 		if(uif->fd < 0)
247 			error(strerror(errno));
248 	}
249 	uif->offset = 0;
250 
251 	c->offset = 0;
252 	c->flag |= COPEN;
253 	return c;
254 }
255 
256 static void
fscreate(Chan * c,char * name,int mode,ulong perm)257 fscreate(Chan *c, char *name, int mode, ulong perm)
258 {
259 	int fd, m;
260 	char path[MAXPATH];
261 	struct stat stbuf;
262 	Ufsinfo *uif;
263 
264 	m = fsomode(mode&3);
265 
266 	fspath(c, name, path);
267 
268 	uif = c->aux;
269 
270 	if(perm & DMDIR) {
271 		if(m)
272 			error(Eperm);
273 
274 		if(mkdir(path, perm & 0777) < 0)
275 			error(strerror(errno));
276 
277 		fd = open(path, 0);
278 		if(fd >= 0) {
279 			chmod(path, perm & 0777);
280 			chown(path, uif->uid, uif->uid);
281 		}
282 		close(fd);
283 
284 		uif->dir = opendir(path);
285 		if(uif->dir == 0)
286 			error(strerror(errno));
287 	}
288 	else {
289 		fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666);
290 		if(fd >= 0) {
291 			if(m != 1) {
292 				close(fd);
293 				fd = open(path, m);
294 			}
295 			chmod(path, perm & 0777);
296 			chown(path, uif->uid, uif->gid);
297 		}
298 		if(fd < 0)
299 			error(strerror(errno));
300 		uif->fd = fd;
301 	}
302 
303 	if(stat(path, &stbuf) < 0)
304 		error(strerror(errno));
305 	c->qid = fsqid(path, &stbuf);
306 	c->offset = 0;
307 	c->flag |= COPEN;
308 	c->mode = openmode(mode);
309 }
310 
311 static void
fsclose(Chan * c)312 fsclose(Chan *c)
313 {
314 	Ufsinfo *uif;
315 
316 	uif = c->aux;
317 
318 	if(c->flag & COPEN) {
319 		if(c->qid.type & QTDIR)
320 			closedir(uif->dir);
321 		else
322 			close(uif->fd);
323 	}
324 
325 	free(uif);
326 }
327 
328 static long
fsread(Chan * c,void * va,long n,vlong offset)329 fsread(Chan *c, void *va, long n, vlong offset)
330 {
331 	int fd, r;
332 	Ufsinfo *uif;
333 
334 /*print("fsread %s\n", c2name(c));*/
335 	if(c->qid.type & QTDIR)
336 		return fsdirread(c, va, n, offset);
337 
338 	uif = c->aux;
339 	qlock(&uif->oq);
340 	if(waserror()) {
341 		qunlock(&uif->oq);
342 		nexterror();
343 	}
344 	fd = uif->fd;
345 	if(uif->offset != offset) {
346 		r = lseek(fd, offset, 0);
347 		if(r < 0)
348 			error(strerror(errno));
349 		uif->offset = offset;
350 	}
351 
352 	n = read(fd, va, n);
353 	if(n < 0)
354 		error(strerror(errno));
355 
356 	uif->offset += n;
357 	qunlock(&uif->oq);
358 	poperror();
359 
360 	return n;
361 }
362 
363 static long
fswrite(Chan * c,void * va,long n,vlong offset)364 fswrite(Chan *c, void *va, long n, vlong offset)
365 {
366 	int fd, r;
367 	Ufsinfo *uif;
368 
369 	uif = c->aux;
370 
371 	qlock(&uif->oq);
372 	if(waserror()) {
373 		qunlock(&uif->oq);
374 		nexterror();
375 	}
376 	fd = uif->fd;
377 	if(uif->offset != offset) {
378 		r = lseek(fd, offset, 0);
379 		if(r < 0)
380 			error(strerror(errno));
381 		uif->offset = offset;
382 	}
383 
384 	n = write(fd, va, n);
385 	if(n < 0)
386 		error(strerror(errno));
387 
388 	uif->offset += n;
389 	qunlock(&uif->oq);
390 	poperror();
391 
392 	return n;
393 }
394 
395 static void
fsremove(Chan * c)396 fsremove(Chan *c)
397 {
398 	int n;
399 	char path[MAXPATH];
400 
401 	fspath(c, 0, path);
402 	if(c->qid.type & QTDIR)
403 		n = rmdir(path);
404 	else
405 		n = remove(path);
406 	if(n < 0)
407 		error(strerror(errno));
408 }
409 
410 int
fswstat(Chan * c,uchar * buf,int n)411 fswstat(Chan *c, uchar *buf, int n)
412 {
413 	Dir d;
414 	struct stat stbuf;
415 	char old[MAXPATH], new[MAXPATH];
416 	char strs[MAXPATH*3], *p;
417 	Ufsinfo *uif;
418 
419 	if(convM2D(buf, n, &d, strs) != n)
420 		error(Ebadstat);
421 
422 	fspath(c, 0, old);
423 	if(stat(old, &stbuf) < 0)
424 		error(strerror(errno));
425 
426 	uif = c->aux;
427 
428 	fspath(c, 0, old);
429 	if(~d.mode != 0 && (int)(d.mode&0777) != (int)(stbuf.st_mode&0777)) {
430 		if(chmod(old, d.mode&0777) < 0)
431 			error(strerror(errno));
432 		uif->mode &= ~0777;
433 		uif->mode |= d.mode&0777;
434 	}
435 
436 	if(d.name[0] && strcmp(d.name, lastelem(c)) != 0) {
437 		fspath(c, 0, old);
438 		strcpy(new, old);
439 		p = strrchr(new, '/');
440 		strcpy(p+1, d.name);
441 		if(rename(old, new) < 0)
442 			error(strerror(errno));
443 	}
444 
445 /*
446 	p = name2pass(gid, d.gid);
447 	if(p == 0)
448 		error(Eunknown);
449 
450 	if(p->id != stbuf.st_gid) {
451 		if(chown(old, stbuf.st_uid, p->id) < 0)
452 			error(strerror(errno));
453 
454 		uif->gid = p->id;
455 	}
456 */
457 	return n;
458 }
459 
460 static Qid
fsqid(char * p,struct stat * st)461 fsqid(char *p, struct stat *st)
462 {
463 	Qid q;
464 	int dev;
465 	ulong h;
466 	static int nqdev;
467 	static uchar *qdev;
468 
469 	if(qdev == 0)
470 		qdev = mallocz(65536U, 1);
471 
472 	q.type = 0;
473 	if((st->st_mode&S_IFMT) ==  S_IFDIR)
474 		q.type = QTDIR;
475 
476 	dev = st->st_dev & 0xFFFFUL;
477 	if(qdev[dev] == 0)
478 		qdev[dev] = ++nqdev;
479 
480 	h = 0;
481 	while(*p != '\0')
482 		h += *p++ * 13;
483 
484 	q.path = (vlong)qdev[dev]<<32;
485 	q.path |= h;
486 	q.vers = st->st_mtime;
487 
488 	return q;
489 }
490 
491 static void
fspath(Chan * c,char * ext,char * path)492 fspath(Chan *c, char *ext, char *path)
493 {
494 	strcpy(path, base);
495 	strcat(path, "/");
496 	strcat(path, uc2name(c));
497 	if(ext){
498 		strcat(path, "/");
499 		strcat(path, ext);
500 	}
501 	cleanname(path);
502 }
503 
504 static int
isdots(char * name)505 isdots(char *name)
506 {
507 	if(name[0] != '.')
508 		return 0;
509 	if(name[1] == '\0')
510 		return 1;
511 	if(name[1] != '.')
512 		return 0;
513 	if(name[2] == '\0')
514 		return 1;
515 	return 0;
516 }
517 
518 static int
p9readdir(char * name,Ufsinfo * uif)519 p9readdir(char *name, Ufsinfo *uif)
520 {
521 	struct dirent *de;
522 
523 	if(uif->nextname[0]){
524 		strcpy(name, uif->nextname);
525 		uif->nextname[0] = 0;
526 		return 1;
527 	}
528 
529 	de = readdir(uif->dir);
530 	if(de == NULL)
531 		return 0;
532 
533 	strcpy(name, de->d_name);
534 	return 1;
535 }
536 
537 static ulong
fsdirread(Chan * c,uchar * va,int count,ulong offset)538 fsdirread(Chan *c, uchar *va, int count, ulong offset)
539 {
540 	int i;
541 	Dir d;
542 	long n;
543 	char de[NAME_MAX];
544 	struct stat stbuf;
545 	char path[MAXPATH], dirpath[MAXPATH];
546 	Ufsinfo *uif;
547 
548 /*print("fsdirread %s\n", c2name(c));*/
549 	i = 0;
550 	uif = c->aux;
551 
552 	errno = 0;
553 	if(uif->offset != offset) {
554 		if(offset != 0)
555 			error("bad offset in fsdirread");
556 		uif->offset = offset;  /* sync offset */
557 		uif->nextname[0] = 0;
558 		rewinddir(uif->dir);
559 	}
560 
561 	fspath(c, 0, dirpath);
562 
563 	while(i+BIT16SZ < count) {
564 		if(!p9readdir(de, uif))
565 			break;
566 
567 		if(de[0]==0 || isdots(de))
568 			continue;
569 
570 		d.name = de;
571 		sprint(path, "%s/%s", dirpath, de);
572 		memset(&stbuf, 0, sizeof stbuf);
573 
574 		if(stat(path, &stbuf) < 0) {
575 			/* fprint(2, "dir: bad path %s\n", path); */
576 			/* but continue... probably a bad symlink */
577 		}
578 
579 		d.uid = "unknown";
580 		d.gid = "unknown";
581 		d.muid = "unknown";
582 		d.qid = fsqid(path, &stbuf);
583 		d.mode = (d.qid.type<<24)|(stbuf.st_mode&0777);
584 		d.atime = stbuf.st_atime;
585 		d.mtime = stbuf.st_mtime;
586 		d.length = stbuf.st_size;
587 		d.type = 'U';
588 		d.dev = c->dev;
589 		n = convD2M(&d, (uchar*)va+i, count-i);
590 		if(n == BIT16SZ){
591 			strcpy(uif->nextname, de);
592 			break;
593 		}
594 		i += n;
595 	}
596 /*print("got %d\n", i);*/
597 	uif->offset += i;
598 	return i;
599 }
600 
601 static int
fsomode(int m)602 fsomode(int m)
603 {
604 	switch(m) {
605 	case 0:			/* OREAD */
606 	case 3:			/* OEXEC */
607 		return 0;
608 	case 1:			/* OWRITE */
609 		return 1;
610 	case 2:			/* ORDWR */
611 		return 2;
612 	}
613 	error(Ebadarg);
614 	return 0;
615 }
616 
617 Dev fsdevtab = {
618 	'U',
619 	"fs",
620 
621 	devreset,
622 	devinit,
623 	devshutdown,
624 	fsattach,
625 	fswalk,
626 	fsstat,
627 	fsopen,
628 	fscreate,
629 	fsclose,
630 	fsread,
631 	devbread,
632 	fswrite,
633 	devbwrite,
634 	fsremove,
635 	fswstat,
636 };
637