Xv6 with picoc & Linkage editor
v1.0
The project delineate mutual cohesion between c library, linkage editor ( linker), interpreter and operating system by porting the same on xv6 kernel
|
00001 // File system implementation. Five layers: 00002 // + Blocks: allocator for raw disk blocks. 00003 // + Log: crash recovery for multi-step updates. 00004 // + Files: inode allocator, reading, writing, metadata. 00005 // + Directories: inode with special contents (list of other inodes!) 00006 // + Names: paths like /usr/rtm/xv6/fs.c for convenient naming. 00007 // 00008 // This file contains the low-level file system manipulation 00009 // routines. The (higher-level) system call implementations 00010 // are in sysfile.c. 00011 00012 #include "types.h" 00013 #include "defs.h" 00014 #include "param.h" 00015 #include "stat.h" 00016 #include "mmu.h" 00017 #include "proc.h" 00018 #include "spinlock.h" 00019 #include "buf.h" 00020 #include "fs.h" 00021 #include "file.h" 00022 00023 #define min(a, b) ((a) < (b) ? (a) : (b)) 00024 static void itrunc(struct inode*); 00025 00026 // Read the super block. 00027 void 00028 readsb(int dev, struct superblock *sb) 00029 { 00030 struct buf *bp; 00031 00032 bp = bread(dev, 1); 00033 memmove(sb, bp->data, sizeof(*sb)); 00034 brelse(bp); 00035 } 00036 00037 // Zero a block. 00038 static void 00039 bzero(int dev, int bno) 00040 { 00041 struct buf *bp; 00042 00043 bp = bread(dev, bno); 00044 memset(bp->data, 0, BSIZE); 00045 log_write(bp); 00046 brelse(bp); 00047 } 00048 00049 // Blocks. 00050 00051 // Allocate a zeroed disk block. 00052 static uint 00053 balloc(uint dev) 00054 { 00055 int b, bi, m; 00056 struct buf *bp; 00057 struct superblock sb; 00058 00059 bp = 0; 00060 readsb(dev, &sb); 00061 for(b = 0; b < sb.size; b += BPB){ 00062 bp = bread(dev, BBLOCK(b, sb.ninodes)); 00063 for(bi = 0; bi < BPB && b + bi < sb.size; bi++){ 00064 m = 1 << (bi % 8); 00065 if((bp->data[bi/8] & m) == 0){ // Is block free? 00066 bp->data[bi/8] |= m; // Mark block in use. 00067 log_write(bp); 00068 brelse(bp); 00069 bzero(dev, b + bi); 00070 return b + bi; 00071 } 00072 } 00073 brelse(bp); 00074 } 00075 cprintf("problem is there"); 00076 panic("balloc: out of blocks"); 00077 } 00078 00079 // Free a disk block. 00080 static void 00081 bfree(int dev, uint b) 00082 { 00083 struct buf *bp; 00084 struct superblock sb; 00085 int bi, m; 00086 00087 readsb(dev, &sb); 00088 bp = bread(dev, BBLOCK(b, sb.ninodes)); 00089 bi = b % BPB; 00090 m = 1 << (bi % 8); 00091 if((bp->data[bi/8] & m) == 0){ 00092 cprintf("while freeing value of b is %d\n",b); 00093 panic("freeing free block"); 00094 } 00095 bp->data[bi/8] &= ~m; 00096 log_write(bp); 00097 brelse(bp); 00098 } 00099 00100 // Inodes. 00101 // 00102 // An inode describes a single unnamed file. 00103 // The inode disk structure holds metadata: the file's type, 00104 // its size, the number of links referring to it, and the 00105 // list of blocks holding the file's content. 00106 // 00107 // The inodes are laid out sequentially on disk immediately after 00108 // the superblock. Each inode has a number, indicating its 00109 // position on the disk. 00110 // 00111 // The kernel keeps a cache of in-use inodes in memory 00112 // to provide a place for synchronizing access 00113 // to inodes used by multiple processes. The cached 00114 // inodes include book-keeping information that is 00115 // not stored on disk: ip->ref and ip->flags. 00116 // 00117 // An inode and its in-memory represtative go through a 00118 // sequence of states before they can be used by the 00119 // rest of the file system code. 00120 // 00121 // * Allocation: an inode is allocated if its type (on disk) 00122 // is non-zero. ialloc() allocates, iput() frees if 00123 // the link count has fallen to zero. 00124 // 00125 // * Referencing in cache: an entry in the inode cache 00126 // is free if ip->ref is zero. Otherwise ip->ref tracks 00127 // the number of in-memory pointers to the entry (open 00128 // files and current directories). iget() to find or 00129 // create a cache entry and increment its ref, iput() 00130 // to decrement ref. 00131 // 00132 // * Valid: the information (type, size, &c) in an inode 00133 // cache entry is only correct when the I_VALID bit 00134 // is set in ip->flags. ilock() reads the inode from 00135 // the disk and sets I_VALID, while iput() clears 00136 // I_VALID if ip->ref has fallen to zero. 00137 // 00138 // * Locked: file system code may only examine and modify 00139 // the information in an inode and its content if it 00140 // has first locked the inode. The I_BUSY flag indicates 00141 // that the inode is locked. ilock() sets I_BUSY, 00142 // while iunlock clears it. 00143 // 00144 // Thus a typical sequence is: 00145 // ip = iget(dev, inum) 00146 // ilock(ip) 00147 // ... examine and modify ip->xxx ... 00148 // iunlock(ip) 00149 // iput(ip) 00150 // 00151 // ilock() is separate from iget() so that system calls can 00152 // get a long-term reference to an inode (as for an open file) 00153 // and only lock it for short periods (e.g., in read()). 00154 // The separation also helps avoid deadlock and races during 00155 // pathname lookup. iget() increments ip->ref so that the inode 00156 // stays cached and pointers to it remain valid. 00157 // 00158 // Many internal file system functions expect the caller to 00159 // have locked the inodes involved; this lets callers create 00160 // multi-step atomic operations. 00161 00162 struct { 00163 struct spinlock lock; 00164 struct inode inode[NINODE]; 00165 } icache; 00166 00167 void 00168 iinit(void) 00169 { 00170 initlock(&icache.lock, "icache"); 00171 } 00172 00173 static struct inode* iget(uint dev, uint inum); 00174 00175 //PAGEBREAK! 00176 // Allocate a new inode with the given type on device dev. 00177 // A free inode has a type of zero. 00178 struct inode* 00179 ialloc(uint dev, short type) 00180 { 00181 int inum; 00182 struct buf *bp; 00183 struct dinode *dip; 00184 struct superblock sb; 00185 00186 readsb(dev, &sb); 00187 00188 for(inum = 1; inum < sb.ninodes; inum++){ 00189 bp = bread(dev, IBLOCK(inum)); 00190 dip = (struct dinode*)bp->data + inum%IPB; 00191 if(dip->type == 0){ // a free inode 00192 memset(dip, 0, sizeof(*dip)); 00193 dip->type = type; 00194 log_write(bp); // mark it allocated on the disk 00195 brelse(bp); 00196 return iget(dev, inum); 00197 } 00198 brelse(bp); 00199 } 00200 panic("ialloc: no inodes"); 00201 } 00202 00203 // Copy a modified in-memory inode to disk. 00204 void 00205 iupdate(struct inode *ip) 00206 { 00207 struct buf *bp; 00208 struct dinode *dip; 00209 00210 bp = bread(ip->dev, IBLOCK(ip->inum)); 00211 dip = (struct dinode*)bp->data + ip->inum%IPB; 00212 dip->type = ip->type; 00213 dip->major = ip->major; 00214 dip->minor = ip->minor; 00215 dip->nlink = ip->nlink; 00216 dip->size = ip->size; 00217 memmove(dip->addrs, ip->addrs, sizeof(ip->addrs)); 00218 log_write(bp); 00219 brelse(bp); 00220 } 00221 00222 // Find the inode with number inum on device dev 00223 // and return the in-memory copy. Does not lock 00224 // the inode and does not read it from disk. 00225 static struct inode* 00226 iget(uint dev, uint inum) 00227 { 00228 struct inode *ip, *empty; 00229 00230 acquire(&icache.lock); 00231 00232 // Is the inode already cached? 00233 empty = 0; 00234 for(ip = &icache.inode[0]; ip < &icache.inode[NINODE]; ip++){ 00235 if(ip->ref > 0 && ip->dev == dev && ip->inum == inum){ 00236 ip->ref++; 00237 release(&icache.lock); 00238 return ip; 00239 } 00240 if(empty == 0 && ip->ref == 0) // Remember empty slot. 00241 empty = ip; 00242 } 00243 00244 // Recycle an inode cache entry. 00245 if(empty == 0) 00246 panic("iget: no inodes"); 00247 00248 ip = empty; 00249 ip->dev = dev; 00250 ip->inum = inum; 00251 ip->ref = 1; 00252 ip->flags = 0; 00253 release(&icache.lock); 00254 00255 return ip; 00256 } 00257 00258 // Increment reference count for ip. 00259 // Returns ip to enable ip = idup(ip1) idiom. 00260 struct inode* 00261 idup(struct inode *ip) 00262 { 00263 acquire(&icache.lock); 00264 ip->ref++; 00265 release(&icache.lock); 00266 return ip; 00267 } 00268 00269 // Lock the given inode. 00270 // Reads the inode from disk if necessary. 00271 void 00272 ilock(struct inode *ip) 00273 { 00274 struct buf *bp; 00275 struct dinode *dip; 00276 00277 if(ip == 0 || ip->ref < 1) 00278 panic("ilock"); 00279 00280 acquire(&icache.lock); 00281 while(ip->flags & I_BUSY) 00282 sleep(ip, &icache.lock); 00283 ip->flags |= I_BUSY; 00284 release(&icache.lock); 00285 00286 if(!(ip->flags & I_VALID)){ 00287 bp = bread(ip->dev, IBLOCK(ip->inum)); 00288 dip = (struct dinode*)bp->data + ip->inum%IPB; 00289 ip->type = dip->type; 00290 ip->major = dip->major; 00291 ip->minor = dip->minor; 00292 ip->nlink = dip->nlink; 00293 ip->size = dip->size; 00294 memmove(ip->addrs, dip->addrs, sizeof(ip->addrs)); 00295 brelse(bp); 00296 ip->flags |= I_VALID; 00297 if(ip->type == 0) 00298 panic("ilock: no type"); 00299 } 00300 } 00301 00302 // Unlock the given inode. 00303 void 00304 iunlock(struct inode *ip) 00305 { 00306 if(ip == 0 || !(ip->flags & I_BUSY) || ip->ref < 1) 00307 panic("iunlock"); 00308 00309 acquire(&icache.lock); 00310 ip->flags &= ~I_BUSY; 00311 wakeup(ip); 00312 release(&icache.lock); 00313 } 00314 00315 // Drop a reference to an in-memory inode. 00316 // If that was the last reference, the inode cache entry can 00317 // be recycled. 00318 // If that was the last reference and the inode has no links 00319 // to it, free the inode (and its content) on disk. 00320 void 00321 iput(struct inode *ip) 00322 { 00323 acquire(&icache.lock); 00324 if(ip->ref == 1 && (ip->flags & I_VALID) && ip->nlink == 0){ 00325 // inode has no links: truncate and free inode. 00326 if(ip->flags & I_BUSY) 00327 panic("iput busy"); 00328 ip->flags |= I_BUSY; 00329 release(&icache.lock); 00330 itrunc(ip); 00331 ip->type = 0; 00332 iupdate(ip); 00333 acquire(&icache.lock); 00334 ip->flags = 0; 00335 wakeup(ip); 00336 } 00337 ip->ref--; 00338 release(&icache.lock); 00339 } 00340 00341 // Common idiom: unlock, then put. 00342 void 00343 iunlockput(struct inode *ip) 00344 { 00345 iunlock(ip); 00346 iput(ip); 00347 } 00348 00349 //PAGEBREAK! 00350 // Inode content 00351 // 00352 // The content (data) associated with each inode is stored 00353 // in blocks on the disk. The first NDIRECT block numbers 00354 // are listed in ip->addrs[]. The next NINDIRECT blocks are 00355 // listed in block ip->addrs[NDIRECT]. 00356 00357 // Return the disk block address of the nth block in inode ip. 00358 // If there is no such block, bmap allocates one. 00359 static uint 00360 bmap(struct inode *ip, uint bn) 00361 { 00362 // cprintf("In bmap function value of bn is %d\n", bn); 00363 uint addr, *a, *b; 00364 struct buf *bp; 00365 00366 if(bn < NDIRECT - 1){ 00367 if((addr = ip->addrs[bn]) == 0) 00368 ip->addrs[bn] = addr = balloc(ip->dev); 00369 return addr; 00370 } 00371 bn -= (NDIRECT - 1) ; 00372 00373 if(bn < NINDIRECT){ 00374 // Load indirect block, allocating if necessary. 00375 if((addr = ip->addrs[NDIRECT - 1]) == 0){ 00376 ip->addrs[NDIRECT - 1] = addr = balloc(ip->dev); 00377 // cprintf("allocating block for 1st single indirect %d\n",addr); 00378 } 00379 bp = bread(ip->dev, addr); 00380 a = (uint*)bp->data; 00381 if((addr = a[bn]) == 0){ 00382 a[bn] = addr = balloc(ip->dev); 00383 // cprintf("allocating block for 2nd single indirect %d\n",addr); 00384 log_write(bp); 00385 } 00386 brelse(bp); 00387 return addr; 00388 } 00389 //*part being added 00390 // this is for double indirect 00391 00392 00393 bn -= NINDIRECT; 00394 // cprintf("printnig inode\n"); 00395 // for (i = 0 ; i < NDIRECT+2 && flagforme;i++) 00396 // cprintf("a[%d] = %d\n",i,ip->addrs[i]); 00397 00398 if(bn < NDINDIRECT){ 00399 if((addr = ip->addrs[NDIRECT]) == 0){ 00400 ip->addrs[NDIRECT] = addr = balloc(ip->dev); 00401 } 00402 00403 // cprintf("1st addr is %d\n", addr); 00404 bp = bread(ip->dev, addr); 00405 a = (uint*)bp->data; 00406 // cprintf("printing value of a \n"); 00407 // for (i = 0 ; i < NDIRECT+2;i++) 00408 // cprintf("a[%2d] = %d",a[i]); 00409 00410 if((addr = a[bn/NINDIRECT]) == 0){ 00411 a[bn/NINDIRECT] = addr = balloc(ip->dev); 00412 log_write(bp); 00413 } 00414 brelse(bp); 00415 bp = bread(ip->dev,addr); 00416 b = (uint*)bp->data; 00417 if((addr = b[bn % NINDIRECT]) == 0){ 00418 b[bn % NINDIRECT] = addr = balloc(ip->dev); 00419 // cprintf("allocating block for 3rd double indirect %d \n", addr); 00420 log_write(bp); 00421 } 00422 brelse(bp); 00423 return addr; 00424 } 00425 00426 panic("bmap: out of range"); 00427 } 00428 00429 // Truncate inode (discard contents). 00430 // Only called when the inode has no links 00431 // to it (no directory entries referring to it) 00432 // and has no in-memory reference to it (is 00433 // not an open file or current directory). 00434 static void 00435 itrunc(struct inode *ip) 00436 { 00437 int i, j,k; 00438 struct buf *bp,*bp1; 00439 uint *a, *b; 00440 00441 for(i = 0; i < (NDIRECT-1); i++){ 00442 if(ip->addrs[i]){ 00443 bfree(ip->dev, ip->addrs[i]); 00444 ip->addrs[i] = 0; 00445 } 00446 } 00447 00448 if(ip->addrs[NDIRECT-1]){ 00449 bp = bread(ip->dev, ip->addrs[NDIRECT-1]); 00450 a = (uint*)bp->data; 00451 for(j = 0; j < NINDIRECT; j++){ 00452 if(a[j]) 00453 bfree(ip->dev,a[j]); 00454 } 00455 brelse(bp); 00456 bfree(ip->dev, ip->addrs[NDIRECT-1]); 00457 ip->addrs[NDIRECT-1] = 0; 00458 } 00459 00460 if(ip->addrs[NDIRECT]){ 00461 bp = bread(ip->dev, ip->addrs[NDIRECT]); 00462 a = (uint*)bp->data; 00463 for(j = 0; j < NINDIRECT; j++){ 00464 if(a[j]){ 00465 bp1 = bread(ip->dev, a[j]); 00466 b = (uint*)bp1->data; 00467 for(k = 0; k < NINDIRECT; k++){ 00468 if(b[k]) 00469 bfree(ip->dev, b[k]); 00470 } 00471 brelse(bp1); 00472 bfree(ip->dev, a[j]); 00473 } 00474 } 00475 brelse(bp); 00476 bfree(ip->dev, ip->addrs[NDIRECT]); 00477 ip->addrs[NDIRECT] = 0; 00478 } 00479 00480 ip->size = 0; 00481 iupdate(ip); 00482 } 00483 00484 // Copy stat information from inode. 00485 void 00486 stati(struct inode *ip, struct stat *st) 00487 { 00488 st->dev = ip->dev; 00489 st->ino = ip->inum; 00490 st->type = ip->type; 00491 st->nlink = ip->nlink; 00492 st->size = ip->size; 00493 } 00494 00495 //PAGEBREAK! 00496 // Read data from inode. 00497 int 00498 readi(struct inode *ip, char *dst, uint off, uint n) 00499 { 00500 // cprintf("In readi function\n"); 00501 uint tot, m; 00502 struct buf *bp; 00503 00504 if(ip->type == T_DEV){ 00505 if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].read) 00506 return -1; 00507 return devsw[ip->major].read(ip, dst, n); 00508 } 00509 00510 if(off > ip->size || off + n < off) 00511 return -1; 00512 if(off + n > ip->size) 00513 n = ip->size - off; 00514 00515 for(tot=0; tot<n; tot+=m, off+=m, dst+=m){ 00516 // cprintf("value of readi is %d\n",bmap(ip,off/BSIZE)); 00517 bp = bread(ip->dev, bmap(ip, off/BSIZE)); 00518 m = min(n - tot, BSIZE - off%BSIZE); 00519 memmove(dst, bp->data + off%BSIZE, m); 00520 brelse(bp); 00521 } 00522 return n; 00523 } 00524 00525 // PAGEBREAK! 00526 // Write data to inode. 00527 int 00528 writei(struct inode *ip, char *src, uint off, uint n) 00529 { 00530 uint tot, m; 00531 struct buf *bp; 00532 if(ip->type == T_DEV){ 00533 if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].write) 00534 return -1; 00535 return devsw[ip->major].write(ip, src, n); 00536 } 00537 00538 if(off > ip->size || off + n < off) 00539 return -1; 00540 if(off + n > MAXFILE*BSIZE) 00541 return -1; 00542 00543 for(tot=0; tot<n; tot+=m, off+=m, src+=m){ 00544 cprintf("value of writei is %d\n",bmap(ip, off/BSIZE)); 00545 bp = bread(ip->dev, bmap(ip, off/BSIZE)); 00546 m = min(n - tot, BSIZE - off%BSIZE); 00547 memmove(bp->data + off%BSIZE, src, m); 00548 log_write(bp); 00549 brelse(bp); 00550 } 00551 00552 if(n > 0 && off > ip->size){ 00553 ip->size = off; 00554 iupdate(ip); 00555 } 00556 return n; 00557 } 00558 00559 //PAGEBREAK! 00560 // Directories 00561 00562 int 00563 namecmp(const char *s, const char *t) 00564 { 00565 return strncmp(s, t, DIRSIZ); 00566 } 00567 00568 // Look for a directory entry in a directory. 00569 // If found, set *poff to byte offset of entry. 00570 struct inode* 00571 dirlookup(struct inode *dp, char *name, uint *poff) 00572 { 00573 uint off, inum; 00574 struct dirent de; 00575 00576 if(dp->type != T_DIR) 00577 panic("dirlookup not DIR"); 00578 00579 for(off = 0; off < dp->size; off += sizeof(de)){ 00580 if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) 00581 panic("dirlink read"); 00582 if(de.inum == 0) 00583 continue; 00584 if(namecmp(name, de.name) == 0){ 00585 // entry matches path element 00586 if(poff) 00587 *poff = off; 00588 inum = de.inum; 00589 return iget(dp->dev, inum); 00590 } 00591 } 00592 00593 return 0; 00594 } 00595 00596 // Write a new directory entry (name, inum) into the directory dp. 00597 int 00598 dirlink(struct inode *dp, char *name, uint inum) 00599 { 00600 int off; 00601 struct dirent de; 00602 struct inode *ip; 00603 00604 // Check that name is not present. 00605 if((ip = dirlookup(dp, name, 0)) != 0){ 00606 iput(ip); 00607 return -1; 00608 } 00609 00610 // Look for an empty dirent. 00611 for(off = 0; off < dp->size; off += sizeof(de)){ 00612 if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) 00613 panic("dirlink read"); 00614 if(de.inum == 0) 00615 break; 00616 } 00617 00618 strncpy(de.name, name, DIRSIZ); 00619 de.inum = inum; 00620 if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) 00621 panic("dirlink"); 00622 00623 return 0; 00624 } 00625 00626 //PAGEBREAK! 00627 // Paths 00628 00629 // Copy the next path element from path into name. 00630 // Return a pointer to the element following the copied one. 00631 // The returned path has no leading slashes, 00632 // so the caller can check *path=='\0' to see if the name is the last one. 00633 // If no name to remove, return 0. 00634 // 00635 // Examples: 00636 // skipelem("a/bb/c", name) = "bb/c", setting name = "a" 00637 // skipelem("///a//bb", name) = "bb", setting name = "a" 00638 // skipelem("a", name) = "", setting name = "a" 00639 // skipelem("", name) = skipelem("////", name) = 0 00640 // 00641 static char* 00642 skipelem(char *path, char *name) 00643 { 00644 char *s; 00645 int len; 00646 00647 while(*path == '/') 00648 path++; 00649 if(*path == 0) 00650 return 0; 00651 s = path; 00652 while(*path != '/' && *path != 0) 00653 path++; 00654 len = path - s; 00655 if(len >= DIRSIZ) 00656 memmove(name, s, DIRSIZ); 00657 else { 00658 memmove(name, s, len); 00659 name[len] = 0; 00660 } 00661 while(*path == '/') 00662 path++; 00663 return path; 00664 } 00665 00666 // Look up and return the inode for a path name. 00667 // If parent != 0, return the inode for the parent and copy the final 00668 // path element into name, which must have room for DIRSIZ bytes. 00669 static struct inode* 00670 namex(char *path, int nameiparent, char *name) 00671 { 00672 struct inode *ip, *next; 00673 if(*path == '/') 00674 ip = iget(ROOTDEV, ROOTINO); 00675 else 00676 ip = idup(proc->cwd); 00677 00678 while((path = skipelem(path, name)) != 0){ 00679 ilock(ip); 00680 if(ip->type != T_DIR){ 00681 iunlockput(ip); 00682 return 0; 00683 } 00684 if(nameiparent && *path == '\0'){ 00685 // Stop one level early. 00686 iunlock(ip); 00687 return ip; 00688 } 00689 if((next = dirlookup(ip, name, 0)) == 0){ 00690 iunlockput(ip); 00691 return 0; 00692 } 00693 iunlockput(ip); 00694 ip = next; 00695 } 00696 if(nameiparent){ 00697 iput(ip); 00698 return 0; 00699 } 00700 return ip; 00701 } 00702 00703 struct inode* 00704 namei(char *path) 00705 { 00706 char name[DIRSIZ]; 00707 return namex(path, 0, name); 00708 } 00709 00710 struct inode* 00711 nameiparent(char *path, char *name) 00712 { 00713 return namex(path, 1, name); 00714 }