Added README, LICENSE, Makefile for future PDP-11 Unix FUSE fs driver project.
[pdp11-unix-fusefs] / v6.c
CommitLineData
845dfc67
AT
1#include "fs.h"
2
3/* NB: not portable! assumes little endian (and probably other things) */
4
5#define NIPB (512/sizeof(DInode))
6#define NDPB (512/sizeof(Dirent))
7
8/* v6 superblock */
9typedef struct Filsys Filsys;
10struct Filsys
11{
12 int16 s_isize; /* size in blocks of I list */
13 int16 s_fsize; /* size in blocks of entire volume */
14 int16 s_nfree; /* number of in core free blocks (0-100) */
15 int16 s_free[100]; /* in core free blocks */
16 int16 s_ninode; /* number of in core I nodes (0-100) */
17 int16 s_inode[100]; /* in core free I nodes */
18 uint8 s_flock; /* lock during free list manipulation */
19 uint8 s_ilock; /* lock during I list manipulation */
20 uint8 s_fmod; /* super block modified flag */
21 uint8 s_ronly; /* mounted read-only flag */
22 int16 s_time[2]; /* current date of last update */
23 int16 pad[48];
24};
25
26/* v6 and PWB/1.0 disk inode */
27struct DInode
28{
29 uint16 i_mode;
30 int8 i_nlink;
31 uint8 i_uid;
32 uint8 i_gid;
33 uint8 i_size0;
34 uint16 i_size1;
35 uint16 i_addr[8];
36 uint16 i_atime[2];
37 uint16 i_mtime[2];
38};
39
40/* modes */
41#define IALLOC 0100000
42#define IFMT 060000
43#define IFDIR 040000
44#define IFCHR 020000
45#define IFBLK 060000
46#define ILARG 010000
47#define ISUID 04000
48#define ISGID 02000
49#define ISVTX 01000
50#define IREAD 0400
51#define IWRITE 0200
52#define IEXEC 0100
53
54typedef struct Dirent Dirent;
55struct Dirent
56{
57 uint16 inode;
58 char name[14];
59};
60
61Filsys *filesys;
62DInode *dinodes;
63Inode *inodes;
64
65#define ISIZE(ip) ((ip)->i_size0<<16 | (ip)->i_size1)
66int32
67pdplong(void *p)
68{
69 uint8 *up;
70 uint32 ui;
71 up = p;
72 ui = up[1]<<24 | up[0]<<16 | up[3]<<8 | up[2];
73 return *(int32*)&ui;
74}
75
76void
77setpdplong(void *p, int32 i)
78{
79 uint8 *up;
80 uint32 ui;
81 up = p;
82 ui = *(uint32*)&i;
83 up[1] = ui>>24;
84 up[0] = ui>>16;
85 up[3] = ui>>8;
86 up[2] = ui;
87}
88
89void
90setint24(void *p, int32 i)
91{
92 uint8 *up;
93 uint32 ui;
94 up = p;
95 ui = *(uint32*)&i;
96 up[0] = ui>>16;
97 up[2] = ui>>8;
98 up[1] = ui;
99}
100
101void *bget(int n) { return &fsdata[n*512]; }
102
103/* returns a free block or 0 */
104int
105balloc(void)
106{
107 int i, bno;
108 uint16 *b;
109 filesys->s_nfree--;
110 if(filesys->s_nfree < 0 || filesys->s_nfree >= 100){
111 printf("bad free count\n");
112 return 0;
113 }
114 bno = filesys->s_free[filesys->s_nfree];
115 filesys->s_free[filesys->s_nfree] = 0;
116 if(bno == 0)
117 return 0;
118 if(bno < filesys->s_isize+2 || bno >= filesys->s_fsize){
119 printf("bad free block (%d)\n", bno);
120 return 0;
121 }
122 if(filesys->s_nfree <= 0){
123 b = bget(bno);
124 filesys->s_nfree = b[0];
125 for(i = 0; i < 100; i++)
126 filesys->s_free[i] = b[i+1];
127 }
128 return bno;
129}
130
131void
132bfree(int bno)
133{
134 int i;
135 uint16 *b;
136
137printf("freeing block\n");
138 if(filesys->s_nfree >= 100){
139 b = bget(bno);
140 b[0] = filesys->s_nfree;
141 for(i = 0; i < 100; i++)
142 b[i+1] = filesys->s_free[i];
143 filesys->s_nfree = 0;
144 }
145 filesys->s_free[filesys->s_nfree] = bno;
146 filesys->s_nfree++;
147}
148
149int
150zalloc(void)
151{
152 int bn;
153 bn = balloc();
154 if(bn)
155 memset(bget(bn), 0, 512);
156 return bn;
157}
158
159/* returns a free inode or 0 */
160int
161ialloc(void)
162{
163 int ino;
164 DInode *ip;
165 int i, j;
166
167 if(filesys->s_ninode <= 0){
168 ino = 0;
169 for(i = 0; i < filesys->s_isize; i++)
170 for(j = 0; j < NIPB; j++){
171 ino++;
172 ip = &dinodes[ino];
173 if(ip->i_mode)
174 continue;
175 filesys->s_inode[filesys->s_ninode++] = ino;
176 if(filesys->s_ninode >= 100)
177 goto brk;
178 }
179 }
180brk:
181 if(filesys->s_ninode > 0){
182 ino = filesys->s_inode[--filesys->s_ninode];
183 return ino;
184 }
185 return 0;
186}
187
188void
189ifree(int ino)
190{
191printf("freeing inode\n");
192 if(filesys->s_ninode >= 100)
193 return;
194 filesys->s_inode[filesys->s_ninode++] = ino;
195}
196
197void
198itrunc(DInode *ip)
199{
200 int i;
201 uint16 *bp, *cp, *dp, *ep;
202
203 if(ip->i_mode & (IFCHR|IFBLK))
204 return;
205 for(i = 7; i >= 0; i--)
206 if(ip->i_addr[i]){
207 if(ip->i_mode & ILARG){
208 bp = bget(ip->i_addr[i]);
209 for(cp = &bp[255]; cp >= bp; cp--)
210 if(*cp){
211 if(i == 7){
212 dp = bget(*cp);
213 for(ep = &dp[255]; ep >= dp; ep--)
214 if(*ep)
215 bfree(*ep);
216 }
217 bfree(*cp);
218 }
219 }
220 bfree(ip->i_addr[i]);
221 ip->i_addr[i] = 0;
222 }
223 ip->i_mode &= ~ILARG;
224 setint24(&ip->i_size0, 0);
225}
226
227void
228dcheck(void)
229{
230 Filsys fs;
231 int i;
232
233 fs = *filesys;
234 i = 0;
235 while(balloc())
236 i++;
237 printf("free: %d\n", i);
238 *filesys = fs;
239}
240
241int
242bmap(DInode *ip, uint bn)
243{
244 uint i;
245 uint nb;
246 uint16 *b;
247
248 if(bn & ~077777)
249 return 0;
250
251 if((ip->i_mode & ILARG) == 0){
252
253 /* small file, direct fetch */
254
255 if(bn & ~7){
256 /* convert to large */
257 nb = zalloc();
258 if(nb == 0)
259 return 0;
260 b = bget(nb);
261 for(i = 0; i < 8; i++){
262 b[i] = ip->i_addr[i];
263 ip->i_addr[i] = 0;
264 }
265 ip->i_addr[0] = nb;
266 ip->i_mode |= ILARG;
267 goto large;
268 }
269
270 nb = ip->i_addr[bn];
271 if(nb == 0){
272 nb = zalloc();
273 if(nb == 0)
274 return 0;
275 ip->i_addr[bn] = nb;
276 }
277 return nb;
278 }
279
280 /* large file, 7 indirect blocks */
281
282large:
283 i = bn>>8;
284 if(i > 7)
285 i = 7;
286 nb = ip->i_addr[i];
287 if(nb == 0){
288 nb = zalloc();
289 if(nb == 0)
290 return 0;
291 ip->i_addr[bn] = nb;
292 }
293 b = bget(nb);
294
295 if(i == 7){
296
297 /* huge file, double indirect last block */
298
299 i = (bn>>8) - 7;
300 nb = b[i&0377];
301 if(nb == 0){
302 nb = zalloc();
303 if(nb == 0)
304 return 0;
305 b[i&0377] = nb;
306 }
307 b = bget(nb);
308 }
309
310 nb = b[bn&0377];
311 return nb;
312};
313
314uint8*
315getblock(DInode *ip, uint bn)
316{
317 bn = bmap(ip, bn);
318 if(bn == 0)
319 return nil;
320 return bget(bn);
321}
322
323void
324fs_init(void)
325{
326 int i, ni;
327
328 filesys = bget(1);
329 dinodes = (DInode*)bget(2) - 1;
330 ni = filesys->s_isize*NIPB + 1;
331 inodes = malloc(ni*sizeof(Inode));
332 for(i = 0; i < ni; i++){
333 inodes[i].ino = i;
334 inodes[i].count = 0;
335 inodes[i].i = &dinodes[i];
336 }
337}
338
339int
340fs_open(uint ino, int flags)
341{
342 DInode *ip;
343
344 ip = &dinodes[ino];
345 if((ip->i_mode & IFMT) == IFDIR)
346 return EISDIR;
347// if((flags & 3) != O_RDONLY)
348// return EACCES;
349 return 0;
350}
351
352int
353fs_stat(uint ino, struct stat *stbuf)
354{
355 DInode *ip;
356 ip = &dinodes[ino];
357
358 stbuf->st_ino = ino;
359 if((ip->i_mode & IFMT) == IFDIR)
360 stbuf->st_mode = S_IFDIR;
361 else if((ip->i_mode & IFMT) == IFCHR)
362 stbuf->st_mode = S_IFCHR;
363 else if((ip->i_mode & IFMT) == IFBLK)
364 stbuf->st_mode = S_IFBLK;
365 else
366 stbuf->st_mode = S_IFREG;
367 stbuf->st_mode |= ip->i_mode & 0777;
368 stbuf->st_nlink = ip->i_nlink;
369 stbuf->st_size = ISIZE(ip);
370 if((ip->i_mode & IFMT) == IFCHR ||
371 (ip->i_mode & IFMT) == IFBLK)
372 stbuf->st_rdev = makedev(ip->i_addr[0]>>8 & 0xFF, ip->i_addr[0]&0xFF);
373 stbuf->st_uid = ip->i_uid;
374 stbuf->st_gid = ip->i_gid;
375 stbuf->st_atime = pdplong(ip->i_atime);
376 stbuf->st_mtime = pdplong(ip->i_mtime);
377
378 return 0;
379}
380
381int
382fs_atime(uint ino, int32 time)
383{
384 DInode *ip;
385 ip = &dinodes[ino];
386 setpdplong(ip->i_atime, time);
387 return 0;
388}
389
390int
391fs_mtime(uint ino, int32 time)
392{
393 DInode *ip;
394 ip = &dinodes[ino];
395 setpdplong(ip->i_mtime, time);
396 return 0;
397}
398
399int
400fs_uid(uint ino, int32 uid)
401{
402 DInode *ip;
403 ip = &dinodes[ino];
404 ip->i_uid = uid;
405 return 0;
406}
407
408int
409fs_gid(uint ino, int32 gid)
410{
411 DInode *ip;
412 ip = &dinodes[ino];
413 ip->i_gid = gid;
414 return 0;
415}
416
417int
418fs_read(uint ino, void *vdst, int offset, int len)
419{
420 DInode *ip;
421 int bn;
422 uint8 *b;
423 int isize;
424 int n;
425 uint8 *dst;
426
427 ip = &dinodes[ino];
428 dst = vdst;
429 isize = ISIZE(ip);
430 if(offset + len > isize)
431 len = isize - offset;
432 if(len <= 0)
433 return 0;
434
435 bn = offset/512;
436 offset %= 512;
437
438 b = getblock(ip, bn);
439 if(b == nil)
440 /* TODO: handle gracefully */
441 panic("no block");
442
443 if(len < 512-offset){
444 /* smaller than block */
445 memcpy(dst, b+offset, len);
446 return len;
447 }
448
449 /* at least one block */
450 memcpy(dst, b+offset, 512-offset);
451 n = 512-offset;
452 len -= n;
453 dst += n;
454 while(len > 0){
455 bn++;
456 b = getblock(ip, bn);
457 if(b == nil)
458 /* TODO: handle gracefully */
459 panic("no block");
460 memcpy(dst, b, len > 512 ? 512 : len);
461 n += len > 512 ? 512 : len;
462 len -= 512;
463 dst += 512;
464 }
465 return n;
466}
467
468int
469fs_write(uint ino, void *vsrc, int offset, int len)
470{
471 DInode *ip;
472 int bn;
473 uint8 *b;
474 int n;
475 int32 size;
476 uint8 *src;
477
478 ip = &dinodes[ino];
479 src = vsrc;
480 size = ISIZE(ip);
481 // TODO: make this better:
482 if(offset+len > size)
483 setint24(&ip->i_size0, offset+len);
484 if(len <= 0)
485 return 0;
486
487 bn = offset/512;
488 offset %= 512;
489
490 b = getblock(ip, bn);
491 if(b == nil)
492 /* TODO: handle gracefully */
493 panic("no block");
494
495 if(len < 512-offset){
496 /* smaller than block */
497 memcpy(b+offset, src, len);
498 return len;
499 }
500
501 /* at least one block */
502 memcpy(b+offset, src, 512-offset);
503 n = 512-offset;
504 len -= n;
505 src += n;
506 while(len > 0){
507 bn++;
508 b = getblock(ip, bn);
509 if(b == nil)
510 /* TODO: handle gracefully */
511 panic("no block");
512 memcpy(b, src, len > 512 ? 512 : len);
513 n += len > 512 ? 512 : len;
514 len -= 512;
515 src += 512;
516 }
517 return n;
518}
519
520/* return a malloc'd array of dirents for a given inode */
521struct dirent*
522fs_readdir(uint ino)
523{
524 DInode *ip;
525 struct dirent *des, *dp;
526 int size;
527 int ndes;
528 Dirent de;
529 int offset;
530
531 ip = &dinodes[ino];
532 if((ip->i_mode & IFDIR) == 0)
533 return nil;
534
535 size = ISIZE(ip);
536 ndes = size/sizeof(Dirent);
537 des = malloc((ndes+1)*sizeof(struct dirent));
538
539 offset = 0;
540 dp = des;
541 while(fs_read(ino, &de, offset, sizeof(Dirent)) == sizeof(Dirent)){
542 if(de.inode){
543 dp->d_ino = de.inode;
544 memcpy(dp->d_name, de.name, 14);
545 dp->d_name[14] = '\0';
546 dp++;
547 }
548 offset += sizeof(Dirent);
549 }
550 dp->d_ino = 0;
551 dp->d_name[0] = '\0';
552
553 return des;
554}
555
556static int
557lookup(uint ino, const char *name, Dirent *de)
558{
559 int offset;
560
561 assert(de);
562 offset = 0;
563 while(fs_read(ino, de, offset, sizeof(Dirent)) == sizeof(Dirent)){
564 if(de->inode)
565 if(strcmp(de->name, name) == 0)
566 return offset;
567 offset += sizeof(Dirent);
568 }
569 memset(de, 0, sizeof(Dirent));
570 return -1;
571}
572
573static int
574allocdirent(uint ino)
575{
576 Dirent de;
577 int offset;
578
579 offset = 0;
580 while(fs_read(ino, &de, offset, sizeof(Dirent)) == sizeof(Dirent)){
581 if(de.inode == 0)
582 return offset;
583 offset += sizeof(Dirent);
584 }
585 memset(&de, 0, sizeof(Dirent));
586 fs_write(ino, &de, offset, sizeof(Dirent));
587 return offset;
588}
589
590int
591fs_link(uint ino, uint parent, const char *name)
592{
593 Dirent de;
594 int offset;
595
596 offset = allocdirent(parent);
597 /* TODO: can't happen right now */
598 if(offset < 0)
599 return ENOSPC;
600 memset(&de, 0, sizeof(Dirent));
601 de.inode = ino;
602 strncpy(de.name, name, 14);
603 fs_write(parent, &de, offset, sizeof(de));
604 fs_mtime(ino, time(nil));
605 fs_atime(ino, time(nil));
606 dinodes[ino].i_nlink++;
607
608 return 0;
609}
610
611int
612fs_mknod(uint parent, const char *name, mode_t mode, dev_t rdev, uint *newino)
613{
614 uint ino;
615 DInode *ip, *ipp;
616 int offset;
617 Dirent de;
618
619 USED(rdev);
620 // TODO: support more files
621 if((mode & S_IFMT) != S_IFREG &&
622 (mode & S_IFMT) != S_IFDIR)
623 return EACCES;
624
625 ino = ialloc();
626 if(ino == 0)
627 return ENOSPC;
628 ip = &dinodes[ino];
629 memset(ip, 0, sizeof(DInode));
630 ip->i_mode = (mode&0777) | IALLOC;
631 if((mode & S_IFMT) == S_IFDIR)
632 ip->i_mode |= IFDIR;
633 else if((mode & S_IFMT) == S_IFCHR)
634 ip->i_mode |= IFCHR;
635 else if((mode & S_IFMT) == S_IFBLK)
636 ip->i_mode |= IFBLK;
637 if((ip->i_mode & IFMT) == IFCHR ||
638 (ip->i_mode & IFMT) == IFBLK)
639 ip->i_addr[0] =
640 major(rdev)<<8 & 0xFF00 | minor(rdev) & 0xFF;
641
642 ipp = &dinodes[parent];
643 assert((ipp->i_mode & IFMT) == IFDIR);
644
645 offset = allocdirent(parent);
646 /* TODO: can't happen right now */
647 if(offset < 0){
648 ip->i_mode = 0;
649 return ENOSPC;
650 }
651
652 ip->i_nlink = 1;
653 memset(&de, 0, sizeof(Dirent));
654 de.inode = ino;
655 strncpy(de.name, name, 14);
656 fs_write(parent, &de, offset, sizeof(de));
657 fs_mtime(ino, time(nil));
658 fs_atime(ino, time(nil));
659
660 if(newino)
661 *newino = ino;
662 printf("making node %s %o\n", name, mode);
663 return 0;
664}
665
666int
667fs_mkdir(uint parent, const char *name, mode_t mode)
668{
669 int bn;
670 int ino;
671 int ret;
672 DInode *ip;
673 Dirent des[2];
674
675 /* Make sure we have space */
676 bn = balloc();
677 if(bn == 0)
678 return ENOSPC;
679 ret = fs_mknod(parent, name, mode | S_IFDIR, 0, &ino);
680 if(ret){
681 bfree(bn);
682 return ret;
683 }
684 ip = &dinodes[ino];
685 ip->i_addr[0] = bn;
686
687 memset(des, 0, 2*sizeof(Dirent));
688 des[0].inode = ino;
689 strcpy(des[0].name, ".");
690 ip->i_nlink++;
691 des[1].inode = parent;
692 strcpy(des[1].name, "..");
693 dinodes[parent].i_nlink++;
694 fs_write(ino, des, 0, 2*sizeof(Dirent));
695 return 0;
696}
697
698Inode*
699fs_iget(uint ino)
700{
701 Inode *ip;
702 ip = &inodes[ino];
703 ip->count++;
704 return ip;
705}
706
707void
708fs_iput(Inode *ip)
709{
710 assert(ip->count > 0);
711 ip->count--;
712 if(ip->count == 0)
713 if(ip->i->i_nlink <= 0){
714 printf("truncating inode %d (%d)\n", ip->ino, ip->i->i_nlink);
715 itrunc(ip->i);
716 ip->i->i_mode = 0;
717 ifree(ip->ino);
718 }
719}
720
721int
722fs_unlink(uint parent, const char *name)
723{
724 Dirent de;
725 int offset;
726
727 offset = lookup(parent, name, &de);
728 if(offset < 0)
729 /* shouldn't happen */
730 return 1;
731 dinodes[de.inode].i_nlink--;
732
733 de.inode = 0;
734 fs_write(parent, &de, offset, sizeof(Dirent));
735 return 0;
736}