chengchun.hsu
  • NEW!
    NEW!  Connect Ideas Across Notes
    Save time and share insights. With Paragraph Citation, you can quote others’ work with source info built in. If someone cites your note, you’ll see a card showing where it’s used—bringing notes closer together.
    Got it
      • Create new note
      • Create a note from template
        • Sharing URL Link copied
        • /edit
        • View mode
          • Edit mode
          • View mode
          • Book mode
          • Slide mode
          Edit mode View mode Book mode Slide mode
        • Customize slides
        • Note Permission
        • Read
          • Only me
          • Signed-in users
          • Everyone
          Only me Signed-in users Everyone
        • Write
          • Only me
          • Signed-in users
          • Everyone
          Only me Signed-in users Everyone
        • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invite by email
        Invitee

        This note has no invitees

      • Publish Note

        Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

        Your note will be visible on your profile and discoverable by anyone.
        Your note is now live.
        This note is visible on your profile and discoverable online.
        Everyone on the web can find and read all notes of this public team.

        Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

        Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

        Explore these features while you wait
        Complete general settings
        Bookmark and like published notes
        Write a few more notes
        Complete general settings
        Write a few more notes
        See published notes
        Unpublish note
        Please check the box to agree to the Community Guidelines.
        View profile
      • Commenting
        Permission
        Disabled Forbidden Owners Signed-in users Everyone
      • Enable
      • Permission
        • Forbidden
        • Owners
        • Signed-in users
        • Everyone
      • Suggest edit
        Permission
        Disabled Forbidden Owners Signed-in users Everyone
      • Enable
      • Permission
        • Forbidden
        • Owners
        • Signed-in users
      • Emoji Reply
      • Enable
      • Versions and GitHub Sync
      • Note settings
      • Note Insights New
      • Engagement control
      • Make a copy
      • Transfer ownership
      • Delete this note
      • Save as template
      • Insert from template
      • Import from
        • Dropbox
        • Google Drive
        • Gist
        • Clipboard
      • Export to
        • Dropbox
        • Google Drive
        • Gist
      • Download
        • Markdown
        • HTML
        • Raw HTML
    Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
    Create Create new note Create a note from template
    Menu
    Options
    Engagement control Make a copy Transfer ownership Delete this note
    Import from
    Dropbox Google Drive Gist Clipboard
    Export to
    Dropbox Google Drive Gist
    Download
    Markdown HTML Raw HTML
    Back
    Sharing URL Link copied
    /edit
    View mode
    • Edit mode
    • View mode
    • Book mode
    • Slide mode
    Edit mode View mode Book mode Slide mode
    Customize slides
    Note Permission
    Read
    Only me
    • Only me
    • Signed-in users
    • Everyone
    Only me Signed-in users Everyone
    Write
    Only me
    • Only me
    • Signed-in users
    • Everyone
    Only me Signed-in users Everyone
    Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    106 OS homework 2-2 成果報告書 === > 組長:B10415009、徐晟鈞 > 組員:B10415014、湯志超 > 組員:B10415042、蔡奇霖 > github: https://github.com/chengchunhsu/xv6-public --- # 🙂 使用情境說明(包含流程圖) 我們打算增加保護文件系統(file system protection)的功能。 參考ZFS,我們將為文件系統添加一個重複區塊(ditto blocks)的概念,所謂ditto block如同字面意思,即為資料區塊的備份。實際上來說就是一個原文件inode的鏡像(mirror),並讓指標(pointer)指向一個複製原始內容新的資料區(data blocks)。 ![](https://i.imgur.com/wxf9yoE.jpg) (圖1 資料結構) 新增一個`checksum()`的函式,使其以inode作為argument產生一個數值。讓所有data block都以這個函式產生一個數值並儲存於自身資料區。 接著以後每次存取file的時候(call `ilock()`),系統都會重新計算一次並與儲存資料內數值比較來校驗資料的正確性,若是不同則代表數據的損壞。 偵測到文件損壞後,則試圖在ditto block尋找之前備份完好的資料。 若是找到,則用它取代損壞的資料以及其他ditto block。若是找不到,則中止`ilock()`函式並返回錯誤訊息。 ![](https://i.imgur.com/9NH0pGx.png) (圖二 救援inode流程圖) --- # 😇 成功畫面 先使用`mkdir`創建一個路徑在xv6裡面。 再以`ls`確認是否有出現在root裡。 `ls`輸出內容依序為: | FileName | DevNumber | InodeNumber | Child1 | Child2 | Checksum | | :------: | :-------: | :---------: | :----: | :----: | :------: | ![](https://i.imgur.com/qcbrhnZ.png) 使用`idesignate`,將test標記為重要,將其內容備份至兩ditto blocks(`child1`、`child2`)中。 再以`ls`確認test的`child1`、`child2`是否已更新。 ![](https://i.imgur.com/VYeJfdC.png) 以`pchecksum`、`pinode`、`pcat`確認 test (inodeNumber=26)與其`child1 ` (inodeNumber=27)、`child2` (inodeNumber=28) checksum值、內容無異。 ![](https://i.imgur.com/4pD8FUR.png) --- # 🏃 實作過程(修改哪些檔案[含圖片]) ## Kernal ### stat.h * 新增`T_DITTO`。 ```clike= #define T_DIR 1 // Directory #define T_FILE 2 // File #define T_DEV 3 // Device #define T_DITTO 4 // Ditto inode ``` --- ### param.h - 將`NINODE`值由`50`改為`100`。 - 將`LOGSIZE`值由`MAXOPBLOCKS*3 (30)`改為`MAXOPBLOCKS*6 (60)`。 - 將`NBUF`值由`MAXOPBLOCKS*3 (30)`改為`MAXOPBLOCKS*6 (60)`。 - 將`FSSIZE`值由`1000`改為`2048`(=2mb)。 ```clike= #define NPROC 64 // maximum number of processes #define KSTACKSIZE 4096 // size of per-process kernel stack #define NCPU 8 // maximum number of CPUs #define NOFILE 16 // open files per process #define NFILE 100 // open files per system #define NINODE 100 // maximum number of active i-nodes #define NDEV 10 // maximum major device number #define ROOTDEV 1 // device number of file system root disk #define MAXARG 32 // max exec arguments #define MAXOPBLOCKS 10 // max # of blocks any FS op writes #define LOGSIZE (MAXOPBLOCKS*6) // max data blocks in on-disk log #define NBUF (MAXOPBLOCKS*6) // size of disk block cache #define FSSIZE 2048 // size of file system in blocks ``` --- ### file.h * 修改`inode`的定義,新增`child1`、`child2`、`checksum`。 ```clike= // in-memory copy of an inode struct inode { uint dev; // Device number uint inum; // Inode number int ref; // Reference count struct sleeplock lock; // protects everything below here int valid; // inode has been read from disk? short type; // copy of disk inode short major; short minor; short nlink; uint size; uint addrs[NDIRECT+1]; short child1; short child2; uint checksum; }; ``` --- ### fs.h [![](https://i.imgur.com/qcFhyZW.jpg)](https://i.imgur.com/qcFhyZW.jpg) * `NDIRECT`值由12改為10 ```clike= #define NDIRECT 10 ``` * 修改dinode的定義,新增`child1`、`child2`、`checksum`。 ```clike= // On-disk inode structure struct dinode { short type; // File type short major; // Major device number (T_DEV only) short minor; // Minor device number (T_DEV only) short nlink; // Number of links to inode in file system uint size; // Size of file (bytes) uint addrs[NDIRECT+1]; // Data block addresses short child1; short child2; uint checksum; }; ``` * 於最後新增以下片段 ```clike= #define DITTO_LOWER 3 // 離root的level數 < 3,建立2個Ditto block #define DITTO_HIGHER 6 // 3 < 離root的level數 < 6,建立1個Ditto blocks enum { REPLICA_SELF, REPLICA_CHILD_1, REPLICA_CHILD_2 } ditto_replica; // ditto副本類型 #define E_CORRUPTED -10 // 傳回此值代表文件損壞 ``` ---- ### fs.c * 修改`ilock`、`writei`、`iupdate`等函式來處理子節點的更新及寫入。 * 新增`ilock_trans`、`ilock_ext`、`writei_ext`、`cupdate`等函式來更新子節點、`checksums`值,並使kernal在recovery階段時不需去計算`checksum`值且傳回error。 * 新增`iduplicate`、`irescue`等函式來執行文件備份及救援。 * 新增`ichecksum`函式來計算`inode`的`checksums`值 #### ichecksum() ```clike= //Compute inode checksum uint ichecksum(struct inode *ip) { // We do not want to checksum files like console if (ip->type == T_DEV) // 註1 return 0; unsigned int buf[512]; char* cbuf = (char*) buf; // sets cbuf to point to buf's pointee uint n = sizeof(buf); uint off = 0; uint r, i; unsigned int checksum = 0; memset((void*) cbuf, 0, n); // 將cbuf全部值設為0 unsigned int * bp; // readi不停將ip值寫入cbuf中(一次大小為n): // for迴圈遍歷buf(cbuf)中所有uint值: // 一一取出buf中的值,checksum ^= *bp (XOR運算) // bp指針隨for迴圈持續前進,直到迴圈跳出 while ((r = readi(ip, cbuf, off, n)) > 0) { // 將ip中的資料依序讀入cbuf中(註2) off += r; // r == n bp = (unsigned int *)buf; // sets bp to point to buf's pointee // 讀取存在buf中的每個unsigned int for (i = 0; i < sizeof(buf) / sizeof(uint); i++){ // buf == char* cbuf // *bp == (unsigned int *)buf checksum ^= *bp; bp++; } memset((void *) cbuf, 0, n); // 將cbuf全部值重設為0 } return checksum; } ``` > 註1 > ```clike= > # define T_DIR 1 // Directory > # define T_FILE 2 // File > # define T_DEV 3 // Device > # define T_DITTO 4 // Ditto inode > ``` > > > 註2 > readi(struct inode *ip, char *dst, uint off, uint n) > reads **n** blocks in inode **ip** starting from **off** into **dst**, return **n** or **-1**(failed) --- * 修改`iupdate`函式。 * 新增`iupdate_ext`函式,並將`iupdate`實作移至此函式內。 * 新增`cupdate`,用來更新`inode`的`child1`、`child2`值。 #### iupdate() ```clike= void iupdate(struct inode *ip) { iupdate_ext(ip, 0); } ``` #### iupdate_ext() 將`inode`值寫入`dinode` (mermory → disk) 中。 `skip`值為`0`時須更新`inode`的`child`值,`1`則否。 ```clike= void iupdate_ext(struct inode *ip, uint skip) { struct buf *bp; struct dinode *dip; bp = bread(ip->dev, IBLOCK(ip->inum, sb)); dip = (struct dinode *)bp->data + ip->inum % IPB; dip->type = ip->type; dip->major = ip->major; dip->minor = ip->minor; dip->nlink = ip->nlink; dip->size = ip->size; dip->child1 = ip->child1; // 將inode的child1值寫入dinode dip->child2 = ip->child2; // 將inode的child2值寫入dinode ip->checksum = ichecksum(ip); // 更新inode的checksum值 dip->checksum = ip->checksum; memmove(dip->addrs, ip->addrs, sizeof(ip->addrs)); log_write(bp); brelse(bp); // 如果skip值為0,則須更新child值 if (skip == 0) { if (ip->child1) cupdate(ip, iget(ip->dev, ip->child1)); if (ip->child2) cupdate(ip, iget(ip->dev, ip->child2)); } } ``` #### cupdate() 更新`inode* ip`的`child1`、`child2`值。 ```clike= void cupdate(struct inode *ip, struct inode *ic) { ilock_ext(ic, 0); // 檢查ic是否為ditto block if (ic->type != T_DITTO) panic("trying to update a \"child\" that is not a ditto block!\n"); struct buf *bp; struct dinode *dic; bp = bread(ip->dev, IBLOCK(ip->inum, sb)); dic = (struct dinode *)bp->data + ic->inum % IPB; dic->type = ic->type; dic->major = ic->major; dic->minor = ic->minor; dic->nlink = 1; ic->size = ip->size; // 直接沿用 parent size dic->size = ic->size; dic->child1 = ic->child1; dic->child2 = ic->child2; ic->checksum = ip->checksum; // 直接沿用 parent checksum dic->checksum = ic->checksum; memmove(dic->addrs, ic->addrs, sizeof(ic->addrs)); log_write(bp); brelse(bp); iunlock(ic); } ``` --- - 修改`ilock`函式。 - 新增`ilock_ext`、`ilock_trans`函式。 - 將`ilock`實作移至`ilock_ext`函式內。 #### ilock() ```clike= int ilock (struct inode *ip) { return ilock_ext(ip, 1); } ``` #### ilock_ext() lock指定的`inode`,確保其在讀取或寫入過程中不會產生 deadlock 或 races 現象。 大部分是原本`ilock`內容,加入了`child1`、`child2`、`checksum`值的同步。 此外還需要檢查`inode`的`checksum`值是否正確,若否則須嘗試修正。 ```clike= int ilock_ext(struct inode *ip, int checksum) { struct buf *bp; struct dinode *dip; if (ip == 0 || ip->ref < 1) panic("ilock"); acquiresleep(&ip->lock); if (ip->valid == 0) { bp = bread(ip->dev, IBLOCK(ip->inum, sb)); dip = (struct dinode *)bp->data + ip->inum % IPB; ip->type = dip->type; ip->major = dip->major; ip->minor = dip->minor; ip->nlink = dip->nlink; ip->size = dip->size; ip->child1 = dip->child1; // 同步child1值 ip->child2 = dip->child2; // 同步child2值 ip->checksum = dip->checksum; // 同步checksum值 memmove(ip->addrs, dip->addrs, sizeof(ip->addrs)); brelse(bp); // Initialize some checking variables uint replica = REPLICA_SELF; ushort rinum; struct inode *rinode; if (checksum == 0) goto zc_success; zc_verify: if (ichecksum(ip) == ip->checksum) { // 檢查ip之checksum值是否正確 goto zc_success; // 符合 } else { // 不符合 -> 試著從child中獲取備份 replica++; // REPLICA_SELF -> REPLICA_CHILD_1 -> REPLICA_CHILD_2 // 檢查備份是否存在 (child1、child2) if (replica == REPLICA_CHILD_1) rinum = ip->child1; else if (replica == REPLICA_CHILD_2) rinum = ip->child2; else goto zc_failure; // 獲取備份之inode number是否為有效值 if (!rinum) goto zc_failure; // 透過iget取得該備份inode的內容,並lock它 rinode = iget(ip->dev, rinum); if (ilock(rinode) == 0) { iunlock(rinode); // 解鎖rinode iunlock(ip); // 解鎖ip return rinum; // 傳回rinode的inum } goto zc_verify; // verify again } zc_failure: // 失敗 iunlock(ip); // 解鎖ip return E_CORRUPTED; // 告知caller檔案已損壞 zc_success: // 同步成功 ip->valid = 1; // ip已與對應dinode完成同步 if (ip->type == 0) // 檢查inode type是否為正常 panic("ilock: no type"); } return 0; } ``` #### ilock_trans() 在傳輸(transaction)前鎖定`inode *ip`,成功後傳回`0`。 若文件損壞則從child取得備份,嘗試救援。 若依然失敗,則呼叫panic錯誤訊息。 ```clike= int ilock_trans(struct inode *ip) { int r = ilock(ip); struct inode *ic; if (r == 0) return 0; // ilock(ip) 執行成功,傳回0 if (r == E_CORRUPTED) return E_CORRUPTED; // ip已損壞,傳回E_CORRUPTED if (r > 0) { // 獲得備份inode之inum ic = iget(ip->dev, r); // 取得該備份inode之內容,並存入ic中 irescue(ip, ic); // 利用ic修正ip } r = ilock(ip); // 救援後再次嘗試ilock(ip) if (r == 0) return 0; // ilock(ip)執行成功,傳回0 // 救援後依然失敗,呼叫panic panic("ilock_trans attempted to recover but still failed to unlock"); } ``` --- #### irescue() 從`replica inode`複製`size`、`checksum`、和`inode data`至`parent inode`中。 以修復`parent inode`。 ```clike= void irescue(struct inode *ip, struct inode *rinode) { int max = ((LOGSIZE-1-1-2) / 4) * 512; int i = 0; uint off = 0; ilock_ext(rinode, 0); // 鎖定rinode ilock_ext(ip, 0); // 鎖定ip int n = ip->size; while(i < n){ int n1 = n - i; if(n1 > max) n1 = max; // 一個循環最多遍歷max大小的資料 begin_trans(); // begin transaction iduplicate(rinode, ip, off, n1); // 從rinode的off位址複製n1大小的data至ip中 commit_trans(); // end transaction off += n1; i += n1; } iunlock(rinode); // 解鎖rinode iunlock(ip); // 解鎖ip } ``` #### iduplicate() 從`inode *src`複製`size`、`checksum`、和`inode data`至`inode *dst`中。 ```clike= void iduplicate(struct inode *src, struct inode *dst, uint off, uint ntotal) { char buf[512]; uint n = sizeof(buf); memset((void*) buf, 0, n); uint r; uint _off = off; dst->checksum = src->checksum; // 同步checksum值 dst->size = src->size; // 同步size值 // 檢查讀取成敗及是否out of range後,將buf內容寫入dst中 while (((r = readi(src, buf, off, n)) > 0) && (off < (ntotal+_off)) ) { writei_ext(dst, buf, off, r, 1); off += r; memset((void *) buf, 0, n); } } ``` --- * 修改`writei()`函式。 * 新增`write_ext()`函式。 * 將`writei()`實做移至`write_ext()`函式。 #### **write_ext()** * 以`writei()`為範本的更改,新增了parameter `skip`,並根據`skip`來決定是否要複寫`child1`與`child2`。 ```clike= // 依據skip來判斷是否要連child都去做複寫 if (skip == 0) { if (ip->child1) { ci = iget(ip->dev, ip->child1); ilock_ext(ci, 0); // 注意呼叫的是writei()而非write_ext(),亦即不會再有複寫,防止無限循環 writei(ci, _src, _off, n); iunlock(ci); } if (ip->child2) { ci = iget(ip->dev, ip->child2); ilock_ext(ci, 0); // 注意呼叫的是writei()而非write_ext(),亦即不會再有複寫,防止無限循環 writei(ci, _src, _off, n); iunlock(ci); } } ``` * 寫入前先檢查是否會寫入到不合法的區域,寫入後檢查是否需要更新檔案資訊。 ```clike= // 如果是device則另外處理 if(ip->type == T_DEV){ // 如果是非法的裝置,則回傳-1代表失敗 if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].write) return -1; // 如果是合法的裝置,則就由負責的去寫入 return devsw[ip->major].write(ip, src, n); } // 如果被寫入的起始位址超過檔案範圍,或者寫入大小為負數,則傳回-1代表失敗 if(off > ip->size || off + n < off) return -1; // 如果寫入後的大小超越最大單檔限制,則傳回-1代表失敗 if(off + n > MAXFILE*BSIZE) return -1; ``` ```clike= // ditto block的資訊會由parent更新,所以提早返回 if (ip->type == T_DITTO) return n; // 若寫入後超過原本大小,則更新大小資訊 if (n > 0 && off > ip->size) ip->size = off; // 將修改過後inode寫入disk if (ip->type != T_DEV) iupdate_ext(ip, skip); ``` [![](https://i.imgur.com/cCCr1Ku.jpg)](https://i.imgur.com/cCCr1Ku.jpg) --- #### **writei()** * 修改使其成為呼叫`write_ext()`的介面,新增的`skip`參數預設為`0`,這樣便與呼叫`writei()`的結果一樣。 [![](https://i.imgur.com/sfVZTwN.jpg)](https://i.imgur.com/sfVZTwN.jpg) --- * 修改`namex()`、`namei()`、`nameiparent()`函式。 * 新增`namei_ext()`、`nameiparent_ext()`、`namei_trans()`、` nameiparent_trans()`函式。 * 將`namei()`、`nameiparent()`實做移至`namei_ext()`、`nameiparent_ext()`函式。 #### **namex()** * 為name系列函式的最底層,`namei()`等就是呼叫不同參數的`namex()`的介面,實際上根據路徑將`inode`找出來就是在這邊。 * namei 系列和 nameiparent 系列差別即為 parameter `int nameiparent`的差別,此參數為`1`時會提早一層跳出迴圈,藉此得到傳入`path`的母目錄。 * 這邊因為recover的關係,需要使用不同的`ilock`。 ```clike= // 若nameiparent為1,則在結束前一個迴圈unlock ip並跳出 if(nameiparent && *path == '\0'){ // 提早離開 iunlock(ip); return ip; } ``` ```clike= // 依據trans來判斷要用ilock_trans()還是ilock() if (trans) { if (ilock_trans(ip) != 0) return 0; // recover和lock失敗 } else { if (ilock(ip) != 0) return 0; // lock失敗 } ``` [![](https://i.imgur.com/NED5zQk.jpg)](https://i.imgur.com/NED5zQk.jpg) --- #### **namei_ext()** * `namei_ext()`為因應`namex()`修改而增加的介面(`原為namei()`),預設需要多傳入`namex()`多了的參數`trans`。 [![](https://i.imgur.com/GPBtkfI.jpg)](https://i.imgur.com/GPBtkfI.jpg) --- #### **nameiparent_ext()** * `nameiparent_ext()`為因應`namex()`修改而增加的介面(原為`nameiparent()`),預設需要多傳入`namex()`多了的參數`trans`。 [![](https://i.imgur.com/DiPVJEq.jpg)](https://i.imgur.com/DiPVJEq.jpg) --- #### **namei()** * 因`namei()`需提供與原本相符的功能,所以必須使`namex()`的`trans=0`,也就是呼叫`namei_ext()`時`trans`傳入`0`。 [![](https://i.imgur.com/NEaZcqA.jpg)](https://i.imgur.com/NEaZcqA.jpg) --- #### **nameiparent()** * 因`nameiparent()`需提供與原本相符的功能,所以必須使`namex()`的`trans=0`,也就是呼叫nameiparent_ext時`trans`傳入`0`。 [![](https://i.imgur.com/C2jdq5N.jpg)](https://i.imgur.com/C2jdq5N.jpg) --- #### **namei_trans()** * `namei_trans()`則與`namei()`相反,為`trans`傳入`1`的版本。 [![](https://i.imgur.com/dmhKXck.jpg)](https://i.imgur.com/dmhKXck.jpg) --- #### **nameiparent_trans()** * `nameiparent_trans()`則與`nameiparent()`相反,為`trans`傳入`1`的版本。 [![](https://i.imgur.com/3hTpZTk.jpg)](https://i.imgur.com/3hTpZTk.jpg) --- #### **distance_to_root()** * `distance_to_root()`為新增的函式,傳入一個合法的`path`,並利用傳進來的`path`計算其距離`root`有多少層,藉以實做3層內2個,或者6層內1個`ditto block`等功能。 ```clike= // 利用..不斷向上一層爬 while((ip = dirlookup(dp,"..", &off) ) != 0){ inum1 = dp->inum; iunlockput(dp); // 得到父目錄ip,所以紀錄完inum後,dp就不需要,可以解鎖並釋放了 dp = ip; ilock_trans(dp); inum2 = dp->inum; // 找到root的話 if(inum1 == inum2){ iunlockput(dp); // 解鎖inode並釋放 break; // 已經找到,中止迴圈 } counter++; // 沒找到root則counter+1,繼續往上一層找 off = 0; } ``` [![](https://i.imgur.com/BgKLchQ.jpg)](https://i.imgur.com/BgKLchQ.jpg) --- ### exec.c * 將`namei(path)`改為`namei_trans(path)`。 ```clike= ... if((ip = namei_trans(path)) == 0){ end_op(); cprintf("exec: fail\n"); return -1; } ... ``` * 將`ilock(ip)`改為`ilock_trans(ip)`,並在錯誤時return`E_CORRUPTED`值。 ```clike= if (ilock_trans(ip)) return E_CORRUPTED; ``` --- ### file.c #### filestat() * 將`ilock(f->ip)`改為`ilock_trans(f->ip)`,並在錯誤時return`E_CORRUPTED`值。 ```clike= ... if (ilock_trans(f->ip) == E_CORRUPTED) return E_CORRUPTED; ... ``` #### fileread() * 將`ilock(f->ip)`改為`ilock_trans(f->ip)` #### filewrite() * 將`ilock(ip)`改為`ilock_trans(ip)`,並在錯誤時return`E_CORRUPTED`值。 ```clike= ... if (ilock_trans(f->ip) == E_CORRUPTED) return E_CORRUPTED; ... ``` --- ### mkfs.c - 修改`main`函式,加入計算`checksum`值,及建置`ditto blocks`的片段。 - 新增`ichecksum`、`readi`、`rblock`函式,用來計算checksum值。 #### main() * 初始化file system。 * 初始化`root inode`的`checksum`值及`ditto blocks`。 ```clike= int main(int argc, char *argv[]) { int i, cc, fd; uint rootino, inum, off; struct dirent de; char buf[BSIZE]; struct dinode din, din2; unsigned int checksum; static_assert(sizeof(int) == 4, "Integers must be 4 bytes!"); if (argc < 2) { fprintf(stderr, "Usage: mkfs fs.img files...\n"); exit(1); } assert((BSIZE % sizeof(struct dinode)) == 0); assert((BSIZE % sizeof(struct dirent)) == 0); fsfd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0666); if (fsfd < 0) { perror(argv[1]); exit(1); } // 1 fs block = 1 disk sector nmeta = 2 + nlog + ninodeblocks + nbitmap; nblocks = FSSIZE - nmeta; sb.size = xint(FSSIZE); sb.nblocks = xint(nblocks); sb.ninodes = xint(NINODES); sb.nlog = xint(nlog); sb.logstart = xint(2); sb.inodestart = xint(2 + nlog); sb.bmapstart = xint(2 + nlog + ninodeblocks); freeblock = nmeta; // the first free block that we can allocate for (i = 0; i < FSSIZE; i++) wsect(i, zeroes); memset(buf, 0, sizeof(buf)); memmove(buf, &sb, sizeof(sb)); wsect(1, buf); rootino = ialloc(T_DIR); assert(rootino == ROOTINO); bzero(&de, sizeof(de)); de.inum = xshort(rootino); strcpy(de.name, "."); iappend(rootino, &de, sizeof(de)); bzero(&de, sizeof(de)); de.inum = xshort(rootino); strcpy(de.name, ".."); iappend(rootino, &de, sizeof(de)); for (i = 2; i < argc; i++) { assert(index(argv[i], '/') == 0); if ((fd = open(argv[i], 0)) < 0) { perror(argv[i]); exit(1); } // Skip leading _ in name when writing to file system. // The binaries are named _rm, _cat, etc. to keep the // build operating system from trying to execute them // in place of system binaries like rm and cat. if (argv[i][0] == '_') ++argv[i]; inum = ialloc(T_FILE); bzero(&de, sizeof(de)); de.inum = xshort(inum); strncpy(de.name, argv[i], DIRSIZ); iappend(rootino, &de, sizeof(de)); // 計算fd的checksum值 checksum = 0; int counter2 = 0; char *cbuf = (char *)buf; memset((void *)cbuf, 0, sizeof(buf)); while ((cc = read(fd, buf, sizeof(buf))) > 0) { counter2 += cc; uint i; unsigned int *bp = (unsigned int *)buf; char *cbuf = (char *)buf; memset((void *)cbuf + cc, 0, sizeof(buf) - cc); for (i = 0; i < sizeof(buf) / sizeof(uint); i++) { checksum ^= *bp; bp++; } iappend(inum, buf, cc); } // 讀取剛剛寫入的inode並將其存入din2中 // 計算並更新din2的checksum值 // 將din2的值寫回inode rinode(inum, &din2); char temp_buf[BSIZE]; readi(&din2, (char *)temp_buf, 0, BSIZE); unsigned int checksum2 = 0; checksum2 = ichecksum(&din2); din2.checksum = xint(checksum2); winode(inum, &din2); close(fd); } // 讀取root inode並將其存入din中 // 計算並更新din的checksum值 // 將din的值寫回root inode rinode(rootino, &din); // 讀取root inode並將其存入din中 off = xint(din.size); off = ((off / BSIZE) + 1) * BSIZE; din.size = xint(off); // 更新din.size值 checksum = ichecksum(&din); din.checksum = xint(checksum); // 更新din.checksum值 winode(rootino, &din); // 將din內容寫回至root inode中 // 建立root inode的ditto blocks rinode(rootino, &din); // 讀取root inode並將其存入din中 uint ditto_inum1, ditto_inum2; struct dinode ditto_din1, ditto_din2; ditto_inum1 = ialloc(T_DITTO); // alloc ditto block1 ditto_inum2 = ialloc(T_DITTO); // alloc ditto block2 copy_dinode_content(&din, ditto_inum1); // 將din內容拷貝至ditto block1中 rinode(ditto_inum1, &ditto_din1); // 讀取ditto block1並將其存入ditto_din1中 ditto_din1.checksum = din.checksum; // 完成checksum值同步 (din -> ditto_din1) ditto_din1.size = din.size; // 完成size值同步 (din -> ditto_din1) winode(ditto_inum1, &ditto_din1); // 將ditto_din1內容寫回至ditto block1中 copy_dinode_content(&din, ditto_inum2); // 同上 rinode(ditto_inum2, &ditto_din2); ditto_din2.checksum = din.checksum; ditto_din2.size = din.size; winode(ditto_inum2, &ditto_din2); rinode(rootino, &din); // 讀取root inode並將其存入din中 din.child1 = xshort(ditto_inum1); // 更新din.child1 din.child2 = xshort(ditto_inum2); // 更新din.child2 winode(rootino, &din); // 將din內容寫回至root inode中 //writes the bitmap to fs.img balloc(freeblock); exit(0); } ``` ---- #### ichecksum() 同[上](https://hackmd.io/EYRgLAhgxsDsCmBaAnMiATRYAMYkA4IBmZRbZeMdCfAVmzliA===?view#ichecksum),不再贅述。 ```clike= uint ichecksum(struct dinode *din) { unsigned int buf[512]; char *cbuf = (char *)buf; uint n = sizeof(buf); uint off = 0; uint r, i; unsigned int checksum = 0; memset((void *)cbuf, 0, n); unsigned int *bp; while ((r = readi(din, cbuf, off, n)) > 0) { off += r; bp = (unsigned int *)buf; for (i = 0; i < sizeof(buf) / sizeof(uint); i++) { checksum ^= *bp; bp++; } memset((void *)cbuf, 0, n); } return checksum; } ``` ---- #### readi() 將`dinode *din`內 data 存入`*dst`中。 ```clike= int readi(struct dinode *din, char *dst, uint off, uint n) { uint tot, m, fbn; char data[BSIZE]; char *cdata = (char *)data; // 檢查din是否為device,是則報錯 if (xint(din->type) == T_DEV) { fprintf(stderr, "Reading DEV file. Not implemented in readi in mkfs\n"); return -1; } // 檢查是否out of range,是則報錯 if (off > xint(din->size) || off + n < off) { return -1; } // 檢查 off + n 是否out of range,是則重置n值 if (off + n > xint(din->size)) { n = xint(din->size) - off; } // 將din內data存入dst中 for (tot = 0; tot < n; tot += m, off += m, dst += m) { fbn = off / BSIZE; rblock(din, fbn, (char *)data); m = min(n - tot, BSIZE - off % BSIZE); memmove(dst, cdata + off % BSIZE, m); } return n; } ``` ---- #### rblock() 將`dinode *din`的第 bn 個 block 內容存入`char *dst`中。 ```clike= void rblock(struct dinode *din, uint bn, char *dst) { uint indirect[NINDIRECT]; uint addr; // 若bn < NDIRECT,開始寫入dst if (bn < NDIRECT) { rsect(xint(din->addrs[bn]), dst); return; } // 將bn轉為indirect block之編號 bn -= NDIRECT; // 若bn < NINDIRECT,開始寫入dst if (bn < NINDIRECT) { rsect(xint(din->addrs[NDIRECT]), (char *)indirect); addr = xint(indirect[bn]); rsect(addr, dst); return; } } ``` ---- #### copy_dinode_content() 複製`dinode *src`內容至`dst`。 ```clike= void copy_dinode_content(struct dinode *src, uint dst) { char buf[512]; char *cbuf = (char *)buf; uint n = sizeof(buf); uint off = 0; uint r; memset((void *)cbuf, 0, n); // 清空cbuf // 檢查讀取是否成功 while ((r = readi(src, cbuf, off, n)) > 0) { off += r; // 更新off值 iappend(dst, cbuf, r); // 寫入dst memset((void *)cbuf, 0, n); // 清空cbuf } } ``` --- ## Syscall Handler ### usys.s * 新增syscall: * iopen * cps * ichecksum * duplicate * forceopen ![](https://i.imgur.com/WAcCimg.png) --- ### syscall.c * 將`iopen` `cps`等syscall加進去。 ![](https://i.imgur.com/MIjmY7h.png) --- ### sysfile.c #### sys_ichecksum() * 確認傳入參數無誤後會call `ichecksum`(實作在fs.c),計算檢驗值大小,並更新回file。 ```clike= // 抓取該dev與inum,若錯誤會回傳-1。 if (argint(0, &dev) < 0 || argint(1, &inum) < 0) return -1; // 用dev.inum去找該inode,若沒找到會return 0,當iget==0,則會回傳-2。 if(ip=iget((uint)dev,inum)==0) return -2; // 判斷是否已損毀,判斷無誤後更新ip的ichecksum。 if (ilock_trans(ip) == E_CORRUPTED) return E_CORRUPTED; ``` ![](https://i.imgur.com/FswrNFZ.png) --- #### sys_duplicate() * 確認傳入參數無誤後會call `duplicate`(實作在fs.c),備份此檔案到`child1`、`child2`。備份成功會return 0。 ```clike= // 抓取該path與ndittos,並直接duplicate child給ip,如果已存在child會直接回傳-1。 if (argstr(0, &path) < 0 || argint(1, &ndittos) < 0 || (ip = duplicate(path, ndittos)) == 0) { return -1; } ``` ![](https://i.imgur.com/uXGY8i2.png) --- #### sys_forceopen() * 開啟檔案並回傳此檔案inode。 * 相較於`sys_open`,`sys_forceopen`不會檢查file是否 CORRUPTED。 ```clike= // 抓取該dev與inum。 if (argint(0, &path) < 0 || argint(1, &&omode) < 0) return -1; ``` ![](https://i.imgur.com/ge7ezuf.png) --- #### sys_iopen() * 打開對應`devNumber`、`inodeNumber`的file。 ```clike= // 抓取該dev與inum。 if (argint(0, &dev) < 0 || argint(1, &inum) < 0) return -1; // 用dev.inum去找該inode 若沒找到會return 0。 if(ip=iget((uint)dev,inum)==0) return -2; ``` ![](https://i.imgur.com/eOnFGIt.png) --- #### duplicate() * 先確認`path`輸入正確,獲得`ip`且此`ip`不是`CORRUPTED`後建立`ditto block`。 * 依據`ndittos`的值,建立1至2個`child`,若已存在`child`則return 0。 * 最後call `ipropagate`更新ip的值 ![](https://i.imgur.com/kdWWDOj.png) --- #### ipropagate() * `max`為`ditto block`的最大size * 如果`ip->child`不是0,會call `iduplicate`來複製`ip` `inode`的資訊到`ic`。 ![](https://i.imgur.com/pw1iGvb.png) --- #### create() * `create`將新創的`inode`新增在`path`下。 * call `nameiparent_trans`來找到device中的`path`路徑。 * call `dirlook up`來找到memory中的`path`路徑。 * call `ialloc` 來為`inode` allocate一段空間,並更新其值。 * call `iupdate` 來同步`inode`和`dinode`內容。 ![](https://i.imgur.com/sJq1lvt.png) --- ### proc.c * 新增`cps`函式,印出正在執行的process。 ![](https://i.imgur.com/1xqbSvL.png) --- ## C Library ### cat.c * 新增`-f`的功能,在argv[1]打`-f`,可以`forceopen`的方式來打開file。 ![](https://i.imgur.com/HxPaRJ6.png) --- --- ### user.h 宣告syscall。 ![](https://i.imgur.com/4kZjkjP.png) --- --- ### ulib.c #### hasdittos() * 檢驗此path的file是否已經有ditto block(備份`child1`、`child2`)。 ``` clike= // 如果已存在一個child以上則直接return 1。 if(st.child) return 1 ``` ![](https://i.imgur.com/XoZQIYZ.png) --- --- ## Application ### sh.c * 在`runcmd`新增file CORRUPTED時的錯誤訊息。 ```clike= if(r==E_CORRUPTED) //確認該檔案是否已經損毀。 printf(2,": %s is corrupted",ecmd->argv[0]); ``` ![](https://i.imgur.com/bfufXrS.png) --- --- ### ls.c * 在原有檔案無法開啟的錯誤訊息中,新增CORRUPTED的提示。 * 開啟檔案輸出訊息新增child1、child2、checksum值。 ![](https://i.imgur.com/EuXju47.png) --- --- ### 新增 User Command 實作及使用 #### 1. idesignate.c * Usage:idesignate [path] [improtance] * `improtance`:可為 1 or 2。 * 功用:允許使用者以透過此指令將檔案標記為 "重要",並選擇使用 1 或 2 個 ditto blocks 保護文件。 ```clike= //確認該file是否有ditto block。 if(hasdittos(argv[1]) == 0) { //依據argv[2]之值建立child1,2。 r = duplicate(argv[1], atoi(argv[2])); } ``` ![](https://i.imgur.com/1yHLHY9.png) --- #### 2. pchecksum.c * Usage:pchecksum [devNumber] [iNodeNumber]。 * `devNumber`:通常為1。 * `iNodeNumber`:可使用ls得知。 * 功用:可顯示指定文件的 device number 及 inode number。 ```clike= //將devnum inum 從字串轉數字 dev = atoi(devnum) iint = atoi(inum) //計算並確認其ichecksum執行過程中沒有錯誤 if ((checksum = ichecksum(dev, iint)) < 0) //輸出該inode的ichecksum值 printf(1, "%d %d: %x\n", dev, iint, checksum) ``` ![](https://i.imgur.com/fdW4uNF.png) --- #### 3. pinode.c * Usage:pinode [devNumber] [iNodeNumber]。 * `devNumber`:通常為1。 * `iNodeNumber`:可使用ls得知。 * 功用:可允許使用者藉由指定文件的 device number 或 inode number 來執行ls功能。 ```clike= // 用iopen抓到此inode的值。 if ((fd = iopen(dev, iint)) < 0 { printf(2, "pinode: cannot open inode %d on dev %d\n", iint, dev); return; } // 用fstat取得指定inode的資訊。 if (fstat(fd, &st) < 0) { printf(2, "pinode: cannot stat inode %d on dev %d (fd=%d)\n", iint, dev, fd); close(fd); return; } ``` ![](https://i.imgur.com/gIOQcI6.png) --- #### 4. pcat.c * Usage:pcat [devNumber][iNodeNumber]。 * `devNumber`:通常為1。 * `iNodeNumber`:可使用ls得知。 * 功用:可允許使用者藉由指定文件的 device number或inode number來執行cat功能,從buffer寫至標準輸出,可用來cat ditto blocks。 ```clike= // 從fd讀檔案到buf 每次讀 sizeof(buf)。 while((n = read(fd, buf, sizeof(buf))) > 0 { // 將buf輸出到標準輸出。 write(1, buf, n); printf(1,"%s",buf); } ``` ![](https://i.imgur.com/c4e8ciR.png) --- #### 5. ps.c * Usage:ps - 功用:印出現正執行的process及其狀態。 ![](https://i.imgur.com/ARARfGO.png) ---- # 😎 結論 本來這次的實做宗旨是為了增加 file system 的安全機制,結果卻多次被 xv6 code 中原有的安全檢查給提示自己的修改錯誤,讓我明白 file system 牽一髮而動全身,實做起來相當不易,也讓我認識到了作業系統之精妙與其心細。 透過這次實做 file system 的安全備份功能 ditto block,我明白了為了提高安全性,勢必得有一些犧牲,除了備份需要犧牲額外的空間外,也需要為了 dittoblock 而減少原本的最大定址數量。真的是做了才知道,讓我學習到了很多,也進一步明白現今的作業系統是多少人智慧的結晶。 --- # 📅 組員分工 :::success | 組員\工作 | kernal | Syacall | C libary | Applicaion | | ----- | ------ | ------- | -------- | ---------- | | 蔡奇霖 | ✓ | | | | | 徐晟鈞 | ✓ | | ✓ | | | 湯智超 | | ✓ | ✓ | ✓ | ::: ---

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully