xref: /xv6-public/fs.c (revision e4bcd2a3)
1 #include "types.h"
2 #include "stat.h"
3 #include "param.h"
4 #include "x86.h"
5 #include "mmu.h"
6 #include "proc.h"
7 #include "defs.h"
8 #include "spinlock.h"
9 #include "buf.h"
10 #include "fs.h"
11 #include "fsvar.h"
12 #include "dev.h"
13 
14 // these are inodes currently in use
15 // an entry is free if count == 0
16 struct inode inode[NINODE];
17 struct spinlock inode_table_lock;
18 
19 uint rootdev = 1;
20 
21 void
22 iinit(void)
23 {
24   initlock(&inode_table_lock, "inode_table");
25 }
26 
27 static uint
28 balloc(uint dev)
29 {
30   int b;
31   struct buf *bp;
32   struct superblock *sb;
33   int bi = 0;
34   int size;
35   int ninodes;
36   uchar m;
37 
38   bp = bread(dev, 1);
39   sb = (struct superblock *) bp->data;
40   size = sb->size;
41   ninodes = sb->ninodes;
42 
43   for (b = 0; b < size; b++) {
44     if (b % BPB == 0) {
45       brelse(bp);
46       bp = bread(dev, BBLOCK(b, ninodes));
47     }
48     bi = b % BPB;
49     m = 0x1 << (bi % 8);
50     if ((bp->data[bi/8] & m) == 0) {  // is block free?
51       break;
52     }
53   }
54   if (b >= size)
55     panic("balloc: out of blocks\n");
56 
57   bp->data[bi/8] |= 0x1 << (bi % 8);
58   bwrite (bp, BBLOCK(b, ninodes));  // mark it allocated on disk
59   brelse(bp);
60   return b;
61 }
62 
63 static void
64 bfree(int dev, uint b)
65 {
66   struct buf *bp;
67   struct superblock *sb;
68   int bi;
69   int ninodes;
70   uchar m;
71 
72   bp = bread(dev, 1);
73   sb = (struct superblock *) bp->data;
74   ninodes = sb->ninodes;
75   brelse(bp);
76 
77   bp = bread(dev, b);
78   memset(bp->data, 0, BSIZE);
79   bwrite(bp, b);
80   brelse(bp);
81 
82   bp = bread(dev, BBLOCK(b, ninodes));
83   bi = b % BPB;
84   m = ~(0x1 << (bi %8));
85   bp->data[bi/8] &= m;
86   bwrite (bp, BBLOCK(b, ninodes));  // mark it free on disk
87   brelse(bp);
88 }
89 
90 // returns an inode with busy set and incremented reference count.
91 struct inode *
92 iget(uint dev, uint inum)
93 {
94   struct inode *ip, *nip;
95   struct dinode *dip;
96   struct buf *bp;
97 
98   acquire(&inode_table_lock);
99 
100  loop:
101   nip = 0;
102   for(ip = &inode[0]; ip < &inode[NINODE]; ip++){
103     if(ip->count > 0 && ip->dev == dev && ip->inum == inum){
104       if(ip->busy){
105         sleep(ip, &inode_table_lock);
106         goto loop;
107       }
108       ip->count++;
109       ip->busy = 1;
110       release(&inode_table_lock);
111       return ip;
112     }
113     if(nip == 0 && ip->count == 0)
114       nip = ip;
115   }
116 
117   if(nip == 0)
118     panic("out of inodes");
119 
120   nip->dev = dev;
121   nip->inum = inum;
122   nip->count = 1;
123   nip->busy = 1;
124 
125   release(&inode_table_lock);
126 
127   bp = bread(dev, IBLOCK(inum));
128   dip = &((struct dinode *)(bp->data))[inum % IPB];
129   nip->type = dip->type;
130   nip->major = dip->major;
131   nip->minor = dip->minor;
132   nip->nlink = dip->nlink;
133   nip->size = dip->size;
134   memmove(nip->addrs, dip->addrs, sizeof(nip->addrs));
135   brelse(bp);
136 
137   return nip;
138 }
139 
140 void
141 iupdate (struct inode *ip)
142 {
143   struct buf *bp;
144   struct dinode *dip;
145 
146   bp = bread(ip->dev, IBLOCK(ip->inum));
147   dip = &((struct dinode *)(bp->data))[ip->inum % IPB];
148   dip->type = ip->type;
149   dip->major = ip->major;
150   dip->minor = ip->minor;
151   dip->nlink = ip->nlink;
152   dip->size = ip->size;
153   memmove(dip->addrs, ip->addrs, sizeof(ip->addrs));
154   bwrite (bp, IBLOCK(ip->inum));   // mark it allocated on the disk
155   brelse(bp);
156 }
157 
158 struct inode *
159 ialloc(uint dev, short type)
160 {
161   struct inode *ip;
162   struct dinode *dip = 0;
163   struct superblock *sb;
164   int ninodes;
165   int inum;
166   struct buf *bp;
167 
168   bp = bread(dev, 1);
169   sb = (struct superblock *) bp->data;
170   ninodes = sb->ninodes;
171   brelse(bp);
172 
173   for (inum = 1; inum < ninodes; inum++) {  // loop over inode blocks
174     bp = bread(dev, IBLOCK(inum));
175     dip = &((struct dinode *)(bp->data))[inum % IPB];
176     if (dip->type == 0) {  // a free inode
177       break;
178     }
179     brelse(bp);
180   }
181 
182   if (inum >= ninodes)
183     panic ("ialloc: no inodes left\n");
184 
185   dip->type = type;
186   bwrite (bp, IBLOCK(inum));   // mark it allocated on the disk
187   brelse(bp);
188   ip = iget (dev, inum);
189   return ip;
190 }
191 
192 static void
193 ifree(struct inode *ip)
194 {
195   ip->type = 0;
196   iupdate(ip);
197 }
198 
199 void
200 ilock(struct inode *ip)
201 {
202   if(ip->count < 1)
203     panic("ilock");
204 
205   acquire(&inode_table_lock);
206 
207   while(ip->busy)
208     sleep(ip, &inode_table_lock);
209   ip->busy = 1;
210 
211   release(&inode_table_lock);
212 }
213 
214 // caller is holding onto a reference to this inode, but no
215 // longer needs to examine or change it, so clear ip->busy.
216 void
217 iunlock(struct inode *ip)
218 {
219   if(ip->busy != 1 || ip->count < 1)
220     panic("iunlock");
221 
222   acquire(&inode_table_lock);
223 
224   ip->busy = 0;
225   wakeup(ip);
226 
227   release(&inode_table_lock);
228 }
229 
230 uint
231 bmap(struct inode *ip, uint bn)
232 {
233   unsigned x;
234 
235   if(bn >= NDIRECT)
236     panic("bmap 1");
237   x = ip->addrs[bn];
238   if(x == 0)
239     panic("bmap 2");
240   return x;
241 }
242 
243 void
244 iunlink(struct inode *ip)
245 {
246   int i;
247 
248   // free inode, its blocks, and remove dir entry
249   for (i = 0; i < NDIRECT; i++) {
250     if (ip->addrs[i] != 0) {
251       bfree(ip->dev, ip->addrs[i]);
252       ip->addrs[i] = 0;
253     }
254   }
255   ip->size = 0;
256   ip->major = 0;
257   ip->minor = 0;
258   iupdate(ip);
259   ifree(ip);  // is this the right order?
260 }
261 
262 // caller is releasing a reference to this inode.
263 // you must have the inode lock.
264 void
265 iput(struct inode *ip)
266 {
267   if(ip->count < 1 || ip->busy != 1)
268     panic("iput");
269 
270   if ((ip->count == 1) && (ip->nlink == 0))
271     iunlink(ip);
272 
273   acquire(&inode_table_lock);
274 
275   ip->count -= 1;
276   ip->busy = 0;
277   wakeup(ip);
278 
279   release(&inode_table_lock);
280 }
281 
282 void
283 idecref(struct inode *ip)
284 {
285   ilock(ip);
286   iput(ip);
287 }
288 
289 void
290 stati(struct inode *ip, struct stat *st)
291 {
292   st->st_dev = ip->dev;
293   st->st_ino = ip->inum;
294   st->st_type = ip->type;
295   st->st_nlink = ip->nlink;
296   st->st_size = ip->size;
297 }
298 
299 #define min(a, b) ((a) < (b) ? (a) : (b))
300 
301 int
302 readi(struct inode *ip, char *dst, uint off, uint n)
303 {
304   uint target = n, n1;
305   struct buf *bp;
306 
307   if (ip->type == T_DEV) {
308     if (ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].d_read)
309       return -1;
310     return devsw[ip->major].d_read (ip->minor, dst, n);
311   }
312 
313   while(n > 0 && off < ip->size){
314     bp = bread(ip->dev, bmap(ip, off / BSIZE));
315     n1 = min(n, ip->size - off);
316     n1 = min(n1, BSIZE - (off % BSIZE));
317     memmove(dst, bp->data + (off % BSIZE), n1);
318     n -= n1;
319     off += n1;
320     dst += n1;
321     brelse(bp);
322   }
323 
324   return target - n;
325 }
326 
327 int
328 writei(struct inode *ip, char *addr, uint off, uint n)
329 {
330   if (ip->type == T_DEV) {
331     if (ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].d_write)
332       return -1;
333     return devsw[ip->major].d_write (ip->minor, addr, n);
334   } else if (ip->type == T_FILE || ip->type == T_DIR) { // XXX dir here too?
335     struct buf *bp;
336     int r = 0;
337     int m;
338     int lbn;
339     uint b;
340     while (r < n) {
341       lbn = off / BSIZE;
342       if (lbn >= NDIRECT) return r;
343       if (ip->addrs[lbn] == 0) {
344 	b = balloc(ip->dev);
345 	if (b <= 0) return r;
346 	ip->addrs[lbn] = b;
347       }
348       m = min(BSIZE - off % BSIZE, n-r);
349       bp = bread(ip->dev, bmap(ip, off / BSIZE));
350       memmove (bp->data + off % BSIZE, addr, m);
351       bwrite (bp, bmap(ip, off/BSIZE));
352       brelse (bp);
353       r += m;
354       off += m;
355     }
356     if (r > 0) {
357       if (off > ip->size) {
358 	ip->size = off;
359       }
360       iupdate(ip);
361     }
362     return r;
363   } else {
364     panic ("writei: unknown type\n");
365     return 0;
366   }
367 }
368 
369 // look up a path name, in one of three modes.
370 // NAMEI_LOOKUP: return locked target inode.
371 // NAMEI_CREATE: return locked parent inode.
372 //   but return 0 if name does exist.
373 // NAMEI_DELETE: return locked parent inode, offset of dirent in *ret_off.
374 //   return 0 if name doesn't exist.
375 struct inode *
376 namei(char *path, int mode, uint *ret_off)
377 {
378   struct inode *dp;
379   char *cp = path, *cp1;
380   uint off, dev;
381   struct buf *bp;
382   struct dirent *ep;
383   int i, atend;
384   unsigned ninum;
385 
386   dp = iget(rootdev, 1);
387 
388   while(*cp == '/')
389     cp++;
390 
391   while(1){
392     if(*cp == '\0'){
393       if(mode == NAMEI_LOOKUP)
394         return dp;
395       iput(dp);
396       return 0;
397     }
398 
399     if(dp->type != T_DIR){
400       iput(dp);
401       return 0;
402     }
403 
404     for(off = 0; off < dp->size; off += BSIZE){
405       bp = bread(dp->dev, bmap(dp, off / BSIZE));
406       for(ep = (struct dirent *) bp->data;
407           ep < (struct dirent *) (bp->data + BSIZE);
408           ep++){
409         if(ep->inum == 0)
410           continue;
411         for(i = 0; i < DIRSIZ && cp[i] != '/' && cp[i]; i++)
412           if(cp[i] != ep->name[i])
413             break;
414         if((cp[i] == '\0' || cp[i] == '/') && (i >= DIRSIZ || ep->name[i] == '\0')){
415           off += (uchar*)ep - bp->data;
416           ninum = ep->inum;
417           brelse(bp);
418           cp += i;
419           goto found;
420         }
421       }
422       brelse(bp);
423     }
424     atend = 1;
425     for(cp1 = cp; *cp1; cp1++)
426       if(*cp1 == '/')
427         atend = 0;
428     if(mode == NAMEI_CREATE && atend)
429       return dp;
430 
431     iput(dp);
432     return 0;
433 
434   found:
435     if(mode == NAMEI_DELETE && *cp == '\0'){
436       *ret_off = off;
437       return dp;
438     }
439     dev = dp->dev;
440     iput(dp);
441     dp = iget(dev, ninum);
442     if(dp->type == 0 || dp->nlink < 1)
443       panic("namei");
444     while(*cp == '/')
445       cp++;
446   }
447 }
448 
449 void
450 wdir(struct inode *dp, char *name, uint ino)
451 {
452   uint off;
453   struct dirent de;
454   int i;
455 
456   for(off = 0; off < dp->size; off += sizeof(de)){
457     if(readi(dp, (char *) &de, off, sizeof(de)) != sizeof(de))
458       panic("wdir read");
459     if(de.inum == 0)
460       break;
461   }
462 
463   de.inum = ino;
464   for(i = 0; i < DIRSIZ && name[i]; i++)
465     de.name[i] = name[i];
466   for( ; i < DIRSIZ; i++)
467     de.name[i] = '\0';
468 
469   if(writei(dp, (char *) &de, off, sizeof(de)) != sizeof(de))
470     panic("wdir write");
471 }
472 
473 struct inode *
474 mknod(char *cp, short type, short major, short minor)
475 {
476   struct inode *ip, *dp;
477 
478   if ((dp = namei(cp, NAMEI_CREATE, 0)) == 0)
479     return 0;
480 
481   ip = ialloc(dp->dev, type);
482   if (ip == 0) {
483     iput(dp);
484     return 0;
485   }
486   ip->major = major;
487   ip->minor = minor;
488   ip->size = 0;
489   ip->nlink = 1;
490 
491   iupdate (ip);  // write new inode to disk
492 
493   wdir(dp, cp, ip->inum);
494   iput(dp);
495   return ip;
496 }
497 
498 int
499 unlink(char *cp)
500 {
501   struct inode *ip, *dp;
502   struct dirent de;
503   uint off, inum, dev;
504 
505   if ((dp = namei(cp, NAMEI_DELETE, &off)) == 0)
506     return -1;
507 
508   dev = dp->dev;
509 
510   if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de) || de.inum == 0)
511     panic("unlink no entry");
512 
513   inum = de.inum;
514 
515   memset(&de, 0, sizeof(de));
516   if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
517     panic("unlink dir write");
518 
519   iupdate(dp);
520   iput(dp);
521 
522   ip = iget(dev, inum);
523 
524   ip->nlink--;
525 
526   iupdate(ip);
527   iput(ip);
528 
529   return 0;
530 }
531 
532 int
533 link(char *name1, char *name2)
534 {
535   struct inode *ip, *dp;
536 
537   if ((ip = namei(name1, NAMEI_LOOKUP, 0)) == 0)
538     return -1;
539   if(ip->type == T_DIR){
540     iput(ip);
541     return -1;
542   }
543 
544   iunlock(ip);
545 
546   if ((dp = namei(name2, NAMEI_CREATE, 0)) == 0) {
547     idecref(ip);
548     return -1;
549   }
550   if(dp->dev != ip->dev){
551     idecref(ip);
552     iput(dp);
553     return -1;
554   }
555 
556   ilock(ip);
557   ip->nlink += 1;
558   iupdate (ip);
559 
560   wdir(dp, name2, ip->inum);
561   iput(dp);
562   iput(ip);
563 
564   return 0;
565 }
566