xref: /plan9/sys/src/cmd/unix/drawterm/kern/devfs-posix.c (revision 96cbc34f1b36a29efdcfd47b10e70703a690febc)
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*
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 = 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
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*
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
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
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
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
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
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
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 	if(d.name[0] && strcmp(d.name, lastelem(c)) != 0) {
429 		fspath(c, 0, old);
430 		strcpy(new, old);
431 		p = strrchr(new, '/');
432 		strcpy(p+1, d.name);
433 		if(rename(old, new) < 0)
434 			error(strerror(errno));
435 	}
436 
437 	fspath(c, 0, old);
438 	if(~d.mode != 0 && (int)(d.mode&0777) != (int)(stbuf.st_mode&0777)) {
439 		if(chmod(old, d.mode&0777) < 0)
440 			error(strerror(errno));
441 		uif->mode &= ~0777;
442 		uif->mode |= d.mode&0777;
443 	}
444 /*
445 	p = name2pass(gid, d.gid);
446 	if(p == 0)
447 		error(Eunknown);
448 
449 	if(p->id != stbuf.st_gid) {
450 		if(chown(old, stbuf.st_uid, p->id) < 0)
451 			error(strerror(errno));
452 
453 		uif->gid = p->id;
454 	}
455 */
456 	return n;
457 }
458 
459 static Qid
460 fsqid(char *p, struct stat *st)
461 {
462 	Qid q;
463 	int dev;
464 	ulong h;
465 	static int nqdev;
466 	static uchar *qdev;
467 
468 	if(qdev == 0)
469 		qdev = mallocz(65536U, 1);
470 
471 	q.type = 0;
472 	if((st->st_mode&S_IFMT) ==  S_IFDIR)
473 		q.type = QTDIR;
474 
475 	dev = st->st_dev & 0xFFFFUL;
476 	if(qdev[dev] == 0)
477 		qdev[dev] = ++nqdev;
478 
479 	h = 0;
480 	while(*p != '\0')
481 		h += *p++ * 13;
482 
483 	q.path = (vlong)qdev[dev]<<32;
484 	q.path |= h;
485 	q.vers = st->st_mtime;
486 
487 	return q;
488 }
489 
490 static void
491 fspath(Chan *c, char *ext, char *path)
492 {
493 	strcpy(path, base);
494 	strcat(path, "/");
495 	strcat(path, uc2name(c));
496 	if(ext){
497 		strcat(path, "/");
498 		strcat(path, ext);
499 	}
500 	cleanname(path);
501 }
502 
503 static int
504 isdots(char *name)
505 {
506 	if(name[0] != '.')
507 		return 0;
508 	if(name[1] == '\0')
509 		return 1;
510 	if(name[1] != '.')
511 		return 0;
512 	if(name[2] == '\0')
513 		return 1;
514 	return 0;
515 }
516 
517 static int
518 p9readdir(char *name, Ufsinfo *uif)
519 {
520 	struct dirent *de;
521 
522 	if(uif->nextname[0]){
523 		strcpy(name, uif->nextname);
524 		uif->nextname[0] = 0;
525 		return 1;
526 	}
527 
528 	de = readdir(uif->dir);
529 	if(de == NULL)
530 		return 0;
531 
532 	strcpy(name, de->d_name);
533 	return 1;
534 }
535 
536 static ulong
537 fsdirread(Chan *c, uchar *va, int count, ulong offset)
538 {
539 	int i;
540 	Dir d;
541 	long n;
542 	char de[NAME_MAX];
543 	struct stat stbuf;
544 	char path[MAXPATH], dirpath[MAXPATH];
545 	Ufsinfo *uif;
546 
547 /*print("fsdirread %s\n", c2name(c));*/
548 	i = 0;
549 	uif = c->aux;
550 
551 	errno = 0;
552 	if(uif->offset != offset) {
553 		if(offset != 0)
554 			error("bad offset in fsdirread");
555 		uif->offset = offset;  /* sync offset */
556 		uif->nextname[0] = 0;
557 		rewinddir(uif->dir);
558 	}
559 
560 	fspath(c, 0, dirpath);
561 
562 	while(i+BIT16SZ < count) {
563 		if(!p9readdir(de, uif))
564 			break;
565 
566 		if(de[0]==0 || isdots(de))
567 			continue;
568 
569 		d.name = de;
570 		sprint(path, "%s/%s", dirpath, de);
571 		memset(&stbuf, 0, sizeof stbuf);
572 
573 		if(stat(path, &stbuf) < 0) {
574 			/* fprint(2, "dir: bad path %s\n", path); */
575 			/* but continue... probably a bad symlink */
576 		}
577 
578 		d.uid = "unknown";
579 		d.gid = "unknown";
580 		d.muid = "unknown";
581 		d.qid = fsqid(path, &stbuf);
582 		d.mode = (d.qid.type<<24)|(stbuf.st_mode&0777);
583 		d.atime = stbuf.st_atime;
584 		d.mtime = stbuf.st_mtime;
585 		d.length = stbuf.st_size;
586 		d.type = 'U';
587 		d.dev = c->dev;
588 		n = convD2M(&d, (uchar*)va+i, count-i);
589 		if(n == BIT16SZ){
590 			strcpy(uif->nextname, de);
591 			break;
592 		}
593 		i += n;
594 	}
595 /*print("got %d\n", i);*/
596 	uif->offset += i;
597 	return i;
598 }
599 
600 static int
601 fsomode(int m)
602 {
603 	switch(m) {
604 	case 0:			/* OREAD */
605 	case 3:			/* OEXEC */
606 		return 0;
607 	case 1:			/* OWRITE */
608 		return 1;
609 	case 2:			/* ORDWR */
610 		return 2;
611 	}
612 	error(Ebadarg);
613 	return 0;
614 }
615 
616 Dev fsdevtab = {
617 	'U',
618 	"fs",
619 
620 	devreset,
621 	devinit,
622 	devshutdown,
623 	fsattach,
624 	fswalk,
625 	fsstat,
626 	fsopen,
627 	fscreate,
628 	fsclose,
629 	fsread,
630 	devbread,
631 	fswrite,
632 	devbwrite,
633 	fsremove,
634 	fswstat,
635 };
636