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