xref: /plan9-contrib/sys/src/cmd/unix/drawterm/kern/devfs-posix.c (revision b85a83648eec38fe82b6f00adfd7828ceec5ee8d)
1 #include	<sys/types.h>
2 #include	<sys/stat.h>
3 #include	<dirent.h>
4 #include	<fcntl.h>
5 #include	<errno.h>
6 #include	<stdio.h> /* for remove, rename */
7 #include	<limits.h>
8 
9 #ifndef NAME_MAX
10 #	define NAME_MAX 256
11 #endif
12 #include	"u.h"
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 	ulong	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*
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*
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*
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*
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
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*
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 = nil;
164 	cnameclose(cname);
165 	if(i != nname){
166 		cclose(nc);
167 		wq->clone = nil;
168 	}
169 	wq->nqid = i;
170 	return wq;
171 }
172 
173 static int
174 fsstat(Chan *c, uchar *buf, int n)
175 {
176 	Dir d;
177 	struct stat stbuf;
178 	char path[MAXPATH];
179 
180 	if(n < BIT16SZ)
181 		error(Eshortstat);
182 
183 	fspath(c, 0, path);
184 	if(stat(path, &stbuf) < 0)
185 		error(strerror(errno));
186 
187 	d.name = lastelem(c);
188 	d.uid = "unknown";
189 	d.gid = "unknown";
190 	d.muid = "unknown";
191 	d.qid = c->qid;
192 	d.mode = (c->qid.type<<24)|(stbuf.st_mode&0777);
193 	d.atime = stbuf.st_atime;
194 	d.mtime = stbuf.st_mtime;
195 	d.length = stbuf.st_size;
196 	d.type = 'U';
197 	d.dev = c->dev;
198 	return convD2M(&d, buf, n);
199 }
200 
201 static Chan*
202 fsopen(Chan *c, int mode)
203 {
204 	char path[MAXPATH];
205 	int m, isdir;
206 	Ufsinfo *uif;
207 
208 /*print("fsopen %s\n", c2name(c));*/
209 	m = mode & (OTRUNC|3);
210 	switch(m) {
211 	case 0:
212 		break;
213 	case 1:
214 	case 1|16:
215 		break;
216 	case 2:
217 	case 0|16:
218 	case 2|16:
219 		break;
220 	case 3:
221 		break;
222 	default:
223 		error(Ebadarg);
224 	}
225 
226 	isdir = c->qid.type & QTDIR;
227 
228 	if(isdir && mode != OREAD)
229 		error(Eperm);
230 
231 	m = fsomode(m & 3);
232 	c->mode = openmode(mode);
233 
234 	uif = c->aux;
235 
236 	fspath(c, 0, path);
237 	if(isdir) {
238 		uif->dir = opendir(path);
239 		if(uif->dir == 0)
240 			error(strerror(errno));
241 	}
242 	else {
243 		if(mode & OTRUNC)
244 			m |= O_TRUNC;
245 		uif->fd = open(path, m, 0666);
246 
247 		if(uif->fd < 0)
248 			error(strerror(errno));
249 	}
250 	uif->offset = 0;
251 
252 	c->offset = 0;
253 	c->flag |= COPEN;
254 	return c;
255 }
256 
257 static void
258 fscreate(Chan *c, char *name, int mode, ulong perm)
259 {
260 	int fd, m;
261 	char path[MAXPATH];
262 	struct stat stbuf;
263 	Ufsinfo *uif;
264 
265 	m = fsomode(mode&3);
266 
267 	fspath(c, name, path);
268 
269 	uif = c->aux;
270 
271 	if(perm & DMDIR) {
272 		if(m)
273 			error(Eperm);
274 
275 		if(mkdir(path, perm & 0777) < 0)
276 			error(strerror(errno));
277 
278 		fd = open(path, 0);
279 		if(fd >= 0) {
280 			chmod(path, perm & 0777);
281 			chown(path, uif->uid, uif->uid);
282 		}
283 		close(fd);
284 
285 		uif->dir = opendir(path);
286 		if(uif->dir == 0)
287 			error(strerror(errno));
288 	}
289 	else {
290 		fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666);
291 		if(fd >= 0) {
292 			if(m != 1) {
293 				close(fd);
294 				fd = open(path, m);
295 			}
296 			chmod(path, perm & 0777);
297 			chown(path, uif->uid, uif->gid);
298 		}
299 		if(fd < 0)
300 			error(strerror(errno));
301 		uif->fd = fd;
302 	}
303 
304 	if(stat(path, &stbuf) < 0)
305 		error(strerror(errno));
306 	c->qid = fsqid(path, &stbuf);
307 	c->offset = 0;
308 	c->flag |= COPEN;
309 	c->mode = openmode(mode);
310 }
311 
312 static void
313 fsclose(Chan *c)
314 {
315 	Ufsinfo *uif;
316 
317 	uif = c->aux;
318 
319 	if(c->flag & COPEN) {
320 		if(c->qid.type & QTDIR)
321 			closedir(uif->dir);
322 		else
323 			close(uif->fd);
324 	}
325 
326 	free(uif);
327 }
328 
329 static long
330 fsread(Chan *c, void *va, long n, vlong offset)
331 {
332 	int fd, r;
333 	Ufsinfo *uif;
334 
335 /*print("fsread %s\n", c2name(c));*/
336 	if(c->qid.type & QTDIR)
337 		return fsdirread(c, va, n, offset);
338 
339 	uif = c->aux;
340 	qlock(&uif->oq);
341 	if(waserror()) {
342 		qunlock(&uif->oq);
343 		nexterror();
344 	}
345 	fd = uif->fd;
346 	if(uif->offset != offset) {
347 		r = lseek(fd, offset, 0);
348 		if(r < 0)
349 			error(strerror(errno));
350 		uif->offset = offset;
351 	}
352 
353 	n = read(fd, va, n);
354 	if(n < 0)
355 		error(strerror(errno));
356 
357 	uif->offset += n;
358 	qunlock(&uif->oq);
359 	poperror();
360 
361 	return n;
362 }
363 
364 static long
365 fswrite(Chan *c, void *va, long n, vlong offset)
366 {
367 	int fd, r;
368 	Ufsinfo *uif;
369 
370 	uif = c->aux;
371 
372 	qlock(&uif->oq);
373 	if(waserror()) {
374 		qunlock(&uif->oq);
375 		nexterror();
376 	}
377 	fd = uif->fd;
378 	if(uif->offset != offset) {
379 		r = lseek(fd, offset, 0);
380 		if(r < 0)
381 			error(strerror(errno));
382 		uif->offset = offset;
383 	}
384 
385 	n = write(fd, va, n);
386 	if(n < 0)
387 		error(strerror(errno));
388 
389 	uif->offset += n;
390 	qunlock(&uif->oq);
391 	poperror();
392 
393 	return n;
394 }
395 
396 static void
397 fsremove(Chan *c)
398 {
399 	int n;
400 	char path[MAXPATH];
401 
402 	fspath(c, 0, path);
403 	if(c->qid.type & QTDIR)
404 		n = rmdir(path);
405 	else
406 		n = remove(path);
407 	if(n < 0)
408 		error(strerror(errno));
409 }
410 
411 int
412 fswstat(Chan *c, uchar *buf, int n)
413 {
414 	Dir d;
415 	struct stat stbuf;
416 	char old[MAXPATH], new[MAXPATH];
417 	char strs[MAXPATH*3], *p;
418 	Ufsinfo *uif;
419 
420 	if(convM2D(buf, n, &d, strs) != n)
421 		error(Ebadstat);
422 
423 	fspath(c, 0, old);
424 	if(stat(old, &stbuf) < 0)
425 		error(strerror(errno));
426 
427 	uif = c->aux;
428 
429 	if(d.name[0] && strcmp(d.name, lastelem(c)) != 0) {
430 		fspath(c, 0, old);
431 		strcpy(new, old);
432 		p = strrchr(new, '/');
433 		strcpy(p+1, d.name);
434 		if(rename(old, new) < 0)
435 			error(strerror(errno));
436 	}
437 
438 	fspath(c, 0, old);
439 	if(~d.mode != 0 && (int)(d.mode&0777) != (int)(stbuf.st_mode&0777)) {
440 		if(chmod(old, d.mode&0777) < 0)
441 			error(strerror(errno));
442 		uif->mode &= ~0777;
443 		uif->mode |= d.mode&0777;
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
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
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
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
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
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
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