【xv6】Lab: file system

Lab: Large files

Overview

  • Support a “doubly-indirect” block in each inode

My solution:

  • Definations:
    - Add defination NINDIRECT_2: the number of double-indirect block, equals to (NINDIRECT * BSIZE) / sizeof(uint)
    - Modify NDIRECT from 12 to 11, otherwise mkfs will be failed because of (BSIZE % sizeof(struct dinode)) != 0
  • Modify block array size: inode.addrs[NDIRECT+2], dinode.addrs[NDIRECT+2] to store the double-indirect block infomation
  • Modify bmap():
    - If NINDIRECT <= bn < NINDIRECT_2, then perform double-indirect block index

Patch

Date: Fri, 4 Apr 2025 17:20:01 +0800
Subject: [PATCH] lab: large files

---
 kernel/file.h |  2 +-
 kernel/fs.c   | 24 ++++++++++++++++++++++++
 kernel/fs.h   | 12 ++++++++----
 3 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/kernel/file.h b/kernel/file.h
index b076d1d..5c4eb3a 100755
--- a/kernel/file.h
+++ b/kernel/file.h
@@ -26,7 +26,7 @@ struct inode {
   short minor;
   short nlink;
   uint size;
-  uint addrs[NDIRECT+1];
+  uint addrs[NDIRECT+2];
 };
 
 // map major device number to device functions.
diff --git a/kernel/fs.c b/kernel/fs.c
index f33553a..1f0dc89 100755
--- a/kernel/fs.c
+++ b/kernel/fs.c
@@ -400,6 +400,30 @@ bmap(struct inode *ip, uint bn)
     brelse(bp);
     return addr;
   }
+  bn -= NINDIRECT;
+
+  if (bn < NINDIRECT_2) {
+    // Load double indirect block, allocating if necessary.
+    if((addr = ip->addrs[IXINDIRECT_2]) == 0)
+      ip->addrs[IXINDIRECT_2] = addr = balloc(ip->dev);
+    bp = bread(ip->dev, addr);
+    a = (uint*)bp->data;
+    if((addr = a[bn / NINDIRECT_1]) == 0) {
+      a[bn / NINDIRECT_1] = addr = balloc(ip->dev);
+      log_write(bp);
+    }
+    brelse(bp);
+
+    // Load indirect block, allocating if necessary.
+    bp = bread(ip->dev, addr);
+    a = (uint*)bp->data;
+    if((addr = a[bn % NINDIRECT_1]) == 0) {
+      a[bn % NINDIRECT_1] = addr = balloc(ip->dev);
+      log_write(bp);
+    }
+    brelse(bp);
+    return addr;
+  }
 
   panic("bmap: out of range");
 }
diff --git a/kernel/fs.h b/kernel/fs.h
index 139dcc9..1568866 100755
--- a/kernel/fs.h
+++ b/kernel/fs.h
@@ -24,9 +24,13 @@ struct superblock {
 
 #define FSMAGIC 0x10203040
 
-#define NDIRECT 12
-#define NINDIRECT (BSIZE / sizeof(uint))
-#define MAXFILE (NDIRECT + NINDIRECT)
+#define NDIRECT 11
+#define IXINDIRECT_1 (NDIRECT)
+#define IXINDIRECT_2 (IXINDIRECT_1 + 1)
+#define NINDIRECT_1 (BSIZE / sizeof(uint))
+#define NINDIRECT (NINDIRECT_1)
+#define NINDIRECT_2 ((NINDIRECT_1*BSIZE) / sizeof(uint))
+#define MAXFILE (NDIRECT + NINDIRECT_1 + NINDIRECT_2)
 
 // On-disk inode structure
 struct dinode {
@@ -35,7 +39,7 @@ struct dinode {
   short minor;          // Minor device number (T_DEVICE only)
   short nlink;          // Number of links to inode in file system
   uint size;            // Size of file (bytes)
-  uint addrs[NDIRECT+1];   // Data block addresses
+  uint addrs[NDIRECT+2];   // Data block addresses
 };
 
 // Inodes per block.
-- 
2.25.1

Lab: Symbolic links

Overview

  • implement the symlink(char *target, char *path) system call

My solution:

  • Implement the symlink(target, path) system call
    - Add T_SYMLINK to kernel/stat.h
    - Store the target path of a symbolic link in the inode’s data blocks
    - I choose to store the target path to inode->addrs field directly, so I have to modify iput().
    - We shouldn’t perform itrunc() when inode->type == T_SYMLINK, otherwise we will get an panic(“virtio_disk_intr status”) because of itrunc() will treat symbolic link which stored in inode->addrs as directly or indirectly blocks then will free it, and then it will cause panic.
    - Add O_NOFOLLOW to kernel/fcntl.h, when a process specifies O_NOFOLLOW in the flags to open, open should open the symlink (and not follow the symbolic link).
    - Follow the symbolic link to reach the file
    - If the linked file is also a symbolic link, we should recursively follow it until a non-link file is reached
    - If the links form a cycle, or , if the depth of links reaches some threshold (e.g., 10), you must return an error code

Patch

Subject: [PATCH] add symlink syscall

---
 Makefile         |  1 +
 kernel/fcntl.h   |  1 +
 kernel/fs.c      |  3 +-
 kernel/stat.h    |  1 +
 kernel/syscall.c |  2 ++
 kernel/syscall.h |  1 +
 kernel/sysfile.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++
 user/user.h      |  1 +
 user/usys.pl     |  1 +
 9 files changed, 101 insertions(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index d8509b1..7c258af 100755
--- a/Makefile
+++ b/Makefile
@@ -175,6 +175,7 @@ UPROGS=\
 	$U/_grind\
 	$U/_wc\
 	$U/_zombie\
+	$U/_symlinktest\
 
 
 
diff --git a/kernel/fcntl.h b/kernel/fcntl.h
index 44861b9..b42df18 100755
--- a/kernel/fcntl.h
+++ b/kernel/fcntl.h
@@ -3,3 +3,4 @@
 #define O_RDWR    0x002
 #define O_CREATE  0x200
 #define O_TRUNC   0x400
+#define O_NOFOLLOW 0x800
\ No newline at end of file
diff --git a/kernel/fs.c b/kernel/fs.c
index 1f0dc89..ff0848c 100755
--- a/kernel/fs.c
+++ b/kernel/fs.c
@@ -343,7 +343,8 @@ iput(struct inode *ip)
 
     release(&icache.lock);
 
-    itrunc(ip);
+    if (ip->type != T_SYMLINK)
+      itrunc(ip);
     ip->type = 0;
     iupdate(ip);
     ip->valid = 0;
diff --git a/kernel/stat.h b/kernel/stat.h
index 19543af..9554d30 100755
--- a/kernel/stat.h
+++ b/kernel/stat.h
@@ -1,6 +1,7 @@
 #define T_DIR     1   // Directory
 #define T_FILE    2   // File
 #define T_DEVICE  3   // Device
+#define T_SYMLINK 4   // Symbolic link
 
 struct stat {
   int dev;     // File system's disk device
diff --git a/kernel/syscall.c b/kernel/syscall.c
index c1b3670..1697b62 100755
--- a/kernel/syscall.c
+++ b/kernel/syscall.c
@@ -104,6 +104,7 @@ extern uint64 sys_unlink(void);
 extern uint64 sys_wait(void);
 extern uint64 sys_write(void);
 extern uint64 sys_uptime(void);
+extern uint64 sys_symlink(void);
 
 static uint64 (*syscalls[])(void) = {
 [SYS_fork]    sys_fork,
@@ -127,6 +128,7 @@ static uint64 (*syscalls[])(void) = {
 [SYS_link]    sys_link,
 [SYS_mkdir]   sys_mkdir,
 [SYS_close]   sys_close,
+[SYS_symlink] sys_symlink,
 };
 
 void
diff --git a/kernel/syscall.h b/kernel/syscall.h
index bc5f356..0fbf6ed 100755
--- a/kernel/syscall.h
+++ b/kernel/syscall.h
@@ -20,3 +20,4 @@
 #define SYS_link   19
 #define SYS_mkdir  20
 #define SYS_close  21
+#define SYS_symlink 22
\ No newline at end of file
diff --git a/kernel/sysfile.c b/kernel/sysfile.c
index 5dc453b..a47ffd3 100755
--- a/kernel/sysfile.c
+++ b/kernel/sysfile.c
@@ -283,6 +283,53 @@ create(char *path, short type, short major, short minor)
   return ip;
 }
 
+#define FOLLOW_DEPTH_MAX 10
+// Follow a symbolic link to its target.
+// Returns the target inode with ilock(), or 0 if the target is not found.
+struct inode*
+_follow_symlink(struct inode *ip, int depth, int *symlink_stack)
+{
+  char *target;
+  struct inode *new_ip, *target_ip;
+  target = (char *)ip->addrs;
+  if(!target)
+    panic("follow_symlink(): no target");
+
+  symlink_stack[depth] = ip->inum;
+
+  // Find the inode for the target path.
+  new_ip = namei(target);
+  if (!new_ip)
+    return 0;
+
+  for (uint i = 0; i < depth+1; i++) {
+    // circular symlink detected
+    if (symlink_stack[i] == new_ip->inum)
+      return 0;
+  }
+
+  ilock(new_ip);
+  if (!(new_ip->type & T_SYMLINK)) {
+    target_ip = new_ip;
+  } else {
+    // maximum depth exceeded
+    if (depth+1 >= FOLLOW_DEPTH_MAX)
+      return 0;
+
+    target_ip = _follow_symlink(new_ip, depth+1, symlink_stack);
+    iunlockput(new_ip);
+  }
+
+  return target_ip;
+}
+
+static struct inode*
+follow_symlink(struct inode *ip)
+{
+  int symlink_stack[FOLLOW_DEPTH_MAX];
+  return _follow_symlink(ip, 0, symlink_stack);
+}
+
 uint64
 sys_open(void)
 {
@@ -316,6 +363,16 @@ sys_open(void)
     }
   }
 
+  if(!(omode & O_NOFOLLOW) && ip->type == T_SYMLINK){
+    struct inode *target_ip = follow_symlink(ip);
+    iunlockput(ip);
+    if (target_ip == 0) {
+      end_op();
+      return -1;
+    }
+    ip = target_ip;
+  }
+
   if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){
     iunlockput(ip);
     end_op();
@@ -484,3 +541,37 @@ sys_pipe(void)
   }
   return 0;
 }
+
+uint64
+sys_symlink(void)
+{
+  const char target[MAXPATH], linkpath[MAXPATH];
+  uint ntarget;
+
+  if(argstr(0, (char *)target, sizeof target) < 0 ||
+    argstr(1, (char *)linkpath, sizeof linkpath) < 0)
+    return -1;
+
+  ntarget = strlen(target);
+  if (ntarget >= sizeof(((struct inode *)0)->addrs)-1){
+    printf("sys_symlink(): target path too long\n");
+    return -1;
+  }
+
+  begin_op();
+  struct inode *ip = create((char *)linkpath, T_SYMLINK, 0, 0);
+  if(ip == 0){
+    end_op();
+    return -1;
+  }
+
+  memmove(ip->addrs, target, ntarget);
+  ip->addrs[ntarget] = 0; // null-terminate the string
+  ip->type = T_SYMLINK;
+  ip->size = ntarget;
+  iupdate(ip);
+  iunlockput(ip);
+  end_op();
+
+  return 0;
+}
\ No newline at end of file
diff --git a/user/user.h b/user/user.h
index b71ecda..5ce5b1b 100755
--- a/user/user.h
+++ b/user/user.h
@@ -16,6 +16,7 @@ int mknod(const char*, short, short);
 int unlink(const char*);
 int fstat(int fd, struct stat*);
 int link(const char*, const char*);
+int symlink(const char*, const char*);
 int mkdir(const char*);
 int chdir(const char*);
 int dup(int);
diff --git a/user/usys.pl b/user/usys.pl
index 01e426e..65a8d6b 100755
--- a/user/usys.pl
+++ b/user/usys.pl
@@ -36,3 +36,4 @@ entry("getpid");
 entry("sbrk");
 entry("sleep");
 entry("uptime");
+entry("symlink");
\ No newline at end of file
-- 
2.25.1

Verify

make[1]: Leaving directory '/home/code'
== Test running bigfile == 
$ make qemu-gdb
running bigfile: OK (47.6s) 
== Test running symlinktest == 
$ make qemu-gdb
(0.4s) 
== Test   symlinktest: symlinks == 
  symlinktest: symlinks: OK 
== Test   symlinktest: concurrent symlinks == 
  symlinktest: concurrent symlinks: OK 
== Test usertests == 
$ make qemu-gdb
usertests: OK (96.2s) 
== Test time == 
time: OK 
Score: 100/100

sysfile.c

#define FOLLOW_DEPTH_MAX 10
// Follow a symbolic link to its target.
// Returns the target inode with ilock(), or 0 if the target is not found.
struct inode*
_follow_symlink(struct inode *ip, int depth, int *symlink_stack)
{
  char *target;
  struct inode *new_ip, *target_ip;
  target = (char *)ip->addrs;
  if(!target)
    panic("follow_symlink(): no target");

  symlink_stack[depth] = ip->inum;

  // Find the inode for the target path.
  new_ip = namei(target);
  if (!new_ip)
    return 0;

  for (uint i = 0; i < depth+1; i++) {
    // circular symlink detected
    if (symlink_stack[i] == new_ip->inum)
      return 0;
  }

  ilock(new_ip);
  if (!(new_ip->type & T_SYMLINK)) {
    target_ip = new_ip;
  } else {
    // maximum depth exceeded
    if (depth+1 >= FOLLOW_DEPTH_MAX)
      return 0;

    target_ip = _follow_symlink(new_ip, depth+1, symlink_stack);
    iunlockput(new_ip);
  }

  return target_ip;
}

static struct inode*
follow_symlink(struct inode *ip)
{
  int symlink_stack[FOLLOW_DEPTH_MAX];
  return _follow_symlink(ip, 0, symlink_stack);
}

uint64
sys_open(void)
{
  char path[MAXPATH];
  int fd, omode;
  struct file *f;
  struct inode *ip;
  int n;

  if((n = argstr(0, path, MAXPATH)) < 0 || argint(1, &omode) < 0)
    return -1;

  begin_op();

  if(omode & O_CREATE){
    ip = create(path, T_FILE, 0, 0);
    if(ip == 0){
      end_op();
      return -1;
    }
  } else {
    if((ip = namei(path)) == 0){
      end_op();
      return -1;
    }
    ilock(ip);
    if(ip->type == T_DIR && omode != O_RDONLY){
      iunlockput(ip);
      end_op();
      return -1;
    }
  }

  if(!(omode & O_NOFOLLOW) && ip->type == T_SYMLINK){
    struct inode *target_ip = follow_symlink(ip);
    iunlockput(ip);
    if (target_ip == 0) {
      end_op();
      return -1;
    }
    ip = target_ip;
  }

  if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){
    iunlockput(ip);
    end_op();
    return -1;
  }

  if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
    if(f)
      fileclose(f);
    iunlockput(ip);
    end_op();
    return -1;
  }

  if(ip->type == T_DEVICE){
    f->type = FD_DEVICE;
    f->major = ip->major;
  } else {
    f->type = FD_INODE;
    f->off = 0;
  }
  f->ip = ip;
  f->readable = !(omode & O_WRONLY);
  f->writable = (omode & O_WRONLY) || (omode & O_RDWR);

  if((omode & O_TRUNC) && ip->type == T_FILE){
    itrunc(ip);
  }

  iunlock(ip);
  end_op();

  return fd;
}

uint64
sys_symlink(void)
{
  const char target[MAXPATH], linkpath[MAXPATH];
  uint ntarget;

  if(argstr(0, (char *)target, sizeof target) < 0 ||
    argstr(1, (char *)linkpath, sizeof linkpath) < 0)
    return -1;

  ntarget = strlen(target);
  if (ntarget >= sizeof(((struct inode *)0)->addrs)-1){
    printf("sys_symlink(): target path too long\n");
    return -1;
  }

  begin_op();
  struct inode *ip = create((char *)linkpath, T_SYMLINK, 0, 0);
  if(ip == 0){
    end_op();
    return -1;
  }

  memmove(ip->addrs, target, ntarget);
  ip->addrs[ntarget] = 0; // null-terminate the string
  ip->type = T_SYMLINK;
  ip->size = ntarget;
  iupdate(ip);
  iunlockput(ip);
  end_op();

  return 0;
}

fs.c

void
iput(struct inode *ip)
{
  acquire(&icache.lock);

  if(ip->ref == 1 && ip->valid && ip->nlink == 0){
    // inode has no links and no other references: truncate and free.

    // ip->ref == 1 means no other process can have ip locked,
    // so this acquiresleep() won't block (or deadlock).
    acquiresleep(&ip->lock);

    release(&icache.lock);

    if (ip->type != T_SYMLINK)
      itrunc(ip);
    ip->type = 0;
    iupdate(ip);
    ip->valid = 0;

    releasesleep(&ip->lock);

    acquire(&icache.lock);
  }

  ip->ref--;
  release(&icache.lock);
}
### MIT xv6操作系统实验中Lab9文件系统的文档和指南 在MIT的xv6操作系统课程中,Lab 9专注于实现一个简单的日志结构化文件系统(log-structured file system, LFS)。这个实验室的目标是让学生理解并实践如何设计和实现一种高效的文件系统,这种文件系统通过将所有的写操作顺序记录在一个日志中来提高性能。 #### 实验目标 学生需要修改`fs.c`中的函数以支持LFS的功能。这涉及到重写磁盘块分配逻辑以及引入新的机制来处理垃圾回收等问题[^1]。 #### 关键概念 - **Log Structured File System (LFS)**: 日志结构化的文件系统不覆盖已有的数据块而是追加新版本到日志末端。 - **Segment**: 文件系统被划分为多个段(segment),每个段包含若干个连续的数据块。 - **Checkpointing**: 定期保存当前状态的一个快照以便于恢复。 - **Garbage Collection**: 清理不再使用的旧版本数据块以释放空间给未来的写入操作。 #### 主要任务 为了完成此实验,参与者通常会经历以下几个方面的工作: - 修改`balloc()` 和 `bread()` 函数使得它们能够管理基于日志的空间而不是传统的位图(bitmap)方式。 - 更新`iwrite()` 来确保每次更改都先记入日志再提交至最终位置。 - 设计并实施有效的垃圾收集算法用于清理过时的日志条目。 ```c // Example of modifying balloc to support log-based allocation. struct superblock { int size; // Number of blocks. int nblocks; // Total number of data blocks. int ninodes; // Number of inodes. }; int lfs_balloc(struct superblock *sb){ static uint next_block = 0; if(next_block >= sb->nblocks) panic("out of blocks"); struct buf* bp = bread(sb->size + next_block); next_block++; return bp->blockno; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值