diff --git a/Makefile b/Makefile index ff4790e..922ba1a 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ platform := k210 +# platform := qemu K=kernel U=xv6-user T=target @@ -179,14 +180,14 @@ UPROGS=\ $U/_init\ $U/_sh\ $U/_cat\ + $U/_echo\ + $U/_grep\ + $U/_ls\ $U/_test - # $U/_echo\ # $U/_forktest\ - # $U/_grep\ # $U/_kill\ # $U/_ln\ - # $U/_ls\ # $U/_mkdir\ # $U/_rm\ # $U/_stressfs\ diff --git a/README.md b/README.md index ece3e13..f1837f7 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Besides, file system and uesr programs are available on qemu. More details [here ```bash $ dd if=/dev/zero of=fs.img bs=512k count=2048 $ mkfs.vfat -F 32 fs.img -$ make fs +$ make build $ (sudo)mount fs.img /mnt $ (sudo)cp xv6-user/_init /mnt/init $ (sudo)cp xv6-user/_sh /mnt @@ -73,7 +73,12 @@ $ make run platform=qemu ``` After entering `qemu`, type `_cat init.c`, and it will read the contents of `init.c` in `fs.img`, output to the terminal. -The `init.c` can be any text file. +The `init.c` can be any text file. In addition, `shell` supports some shortcut keys as below: + +- Ctrl-H -- backspace +- Ctrl-U -- kill a line +- Ctrl-D -- end of file (EOF) +- Ctrl-P -- print process list ## Progress - [x] Multicore boot diff --git a/README_cn.md b/README_cn.md index 4ca6ac8..e335370 100644 --- a/README_cn.md +++ b/README_cn.md @@ -62,7 +62,7 @@ Ps: 按 `Ctrl + A` 然后 `X` 退出 `qemu`。 ```bash $ dd if=/dev/zero of=fs.img bs=512k count=2048 $ mkfs.vfat -F 32 fs.img -$ make fs +$ make build $ (sudo)mount fs.img /mnt $ (sudo)cp xv6-user/_init /mnt/init $ (sudo)cp xv6-user/_sh /mnt @@ -73,7 +73,11 @@ $ make run platform=qemu ``` 进入 `qemu` 后输入命令 `_cat init.c`,将会读取 `fs.img` 中的 `init.c` 文件内容并输出到终端。 -这里的 `init.c` 文件可以是任意文本文件。 +这里的 `init.c` 文件可以是任意文本文件。此外,`shell`支持下列快捷键: +- Ctrl-H -- 退格 +- Ctrl-U -- 删除行 +- Ctrl-D -- 文件尾(EOF) +- Ctrl-P -- 打印进程列表 ## 进度 - [x] 多核启动 diff --git a/doc/fs.md b/doc/fs.md index 894ba08..2865f80 100644 --- a/doc/fs.md +++ b/doc/fs.md @@ -18,7 +18,6 @@ + 目前只实现了文件读,文件写有初步实现但不完善,主要有新建文件无法生成时间戳、文件名校验和等问题; + 由于FAT32目录结构与xv6目录结构的不同,还未实现相关的接口为`ls`等程序提供支持; -+ 对`"."`和`".."`两个特殊文件的支持还不完善,尤其是在根目录下,因为FAT32的根目录中没有这两个文件; + FAT32的文件属性不支持设备类型,而原xv6文件系统中有`mknod`系统调用新建一个设备,并可以使用`open`打开。控制台输入输出就是这样打开的。为打开控制台标准输入输出,我们改用了另一种实现。 ## 如何在qemu上挂载文件镜像 @@ -35,10 +34,10 @@ $ mkfs.vfat -F 32 fs.img 首先通过`mount`命令,将镜像挂载到你指定的目录(这里以/mnt为例),然后将build之后xv6-user目录下文件名以"\_"为前缀的用户程序拷贝至镜像中,再取消挂载以保存写入结果即可。也可以在镜像中建立一些文本文件进行读测试。 -注意:必须将xv6-user目录下的"\_init"拷贝至镜像中,并且重命名为"init"(这是xv6原本的设定,目前我们暂未更改),还需拷贝"\_sh"至镜像中,才能启动shell。编写的用户程序也需要放在xv6-user目录下,同时需要添加至Makefile的`UPROGS`目标中,才能连同内核一起编译链接。 +注意:必须将xv6-user目录下的"\_init"拷贝至镜像根目录中,并且重命名为"init"(这是xv6原本的设定,目前我们暂未更改),还需拷贝"\_sh"至镜像中,才能启动shell。编写的用户程序也需要放在xv6-user目录下,同时需要添加至Makefile的`UPROGS`目标中,才能连同内核一起编译链接。 ```bash -$ make fs +$ make build $ (sudo)mount fs.img /mnt $ (sudo)cp xv6-user/_init /mnt/init $ (sudo)cp xv6-user/_sh /mnt diff --git a/kernel/exec.c b/kernel/exec.c index 67de0df..fc4b651 100644 --- a/kernel/exec.c +++ b/kernel/exec.c @@ -50,12 +50,8 @@ int exec(char *path, char **argv) pagetable_t pagetable = 0, oldpagetable; struct proc *p = myproc(); - // begin_op(); - if((ep = ename(path)) == 0){ - // printf("exec: can't find %s\n", path); - // end_op(); + if((ep = ename(path)) == 0) return -1; - } elock(ep); // Check ELF header @@ -88,7 +84,6 @@ int exec(char *path, char **argv) } eunlock(ep); eput(ep); - // end_op(); ep = 0; p = myproc(); @@ -154,8 +149,6 @@ int exec(char *path, char **argv) if(ep){ eunlock(ep); eput(ep); - // end_op(); } - // printf("[exec]bad, pid = %d\n", p->pid); return -1; } diff --git a/kernel/fat32.c b/kernel/fat32.c index c0ebe19..b8a53f0 100644 --- a/kernel/fat32.c +++ b/kernel/fat32.c @@ -72,12 +72,17 @@ int fat32_init() initlock(&ecache.lock, "ecache"); memset(&root, 0, sizeof(root)); initsleeplock(&root.lock, "entry"); - root.attribute = ATTR_DIRECTORY; - root.first_clus = fat.bpb.root_clus; + root.attribute = (ATTR_DIRECTORY | ATTR_SYSTEM); + root.first_clus = root.cur_clus = fat.bpb.root_clus; + root.valid = 1; root.prev = &root; root.next = &root; for(struct dirent *de = ecache.entries; de < ecache.entries + ENTRY_CACHE_NUM; de++) { + de->dev = 0; de->valid = 0; + de->ref = 0; + de->dirty = 0; + de->parent = 0; de->next = root.next; de->prev = &root; initsleeplock(&de->lock, "entry"); @@ -166,14 +171,14 @@ static void zero_clus(uint32 cluster) } } -static uint32 alloc_clus() +static uint32 alloc_clus(uint8 dev) { // should we keep a free cluster list? instead of searching fat every time. struct buf *b; uint32 sec = fat.bpb.rsvd_sec_cnt; uint32 const ent_per_sec = fat.bpb.byts_per_sec / sizeof(uint32); for (uint32 i = 0; i < fat.bpb.fat_sz; i++, sec++) { - b = bread(0, sec); + b = bread(dev, sec); for (uint32 j = 0; j < ent_per_sec; j++) { if (((uint32 *)(b->data))[j] == 0) { ((uint32 *)(b->data))[j] = FAT32_EOC + 7; @@ -225,32 +230,51 @@ static uint rw_clus(uint32 cluster, int write, int user, uint64 data, uint off, return tot; } +/** + * for the given entry, relocate the cur_clus field based on the off + * @param entry modify its cur_clus field + * @param off the offset from the beginning of the relative file + * @return the offset from the new cur_clus + */ +static uint reloc_clus(struct dirent *entry, uint off) +{ + int clus_num = off / fat.byts_per_clus; + while (clus_num > entry->clus_cnt) { + entry->cur_clus = read_fat(entry->cur_clus); + entry->clus_cnt++; + } + if (clus_num < entry->clus_cnt) { + entry->cur_clus = entry->first_clus; + entry->clus_cnt = 0; + while (entry->clus_cnt < clus_num) { + entry->cur_clus = read_fat(entry->cur_clus); + entry->clus_cnt++; + } + } + return off % fat.byts_per_clus; +} + /* like the original readi, but "reade" is odd, let alone "writee" */ +// Caller must hold entry->lock. int eread(struct dirent *entry, int user_dst, uint64 dst, uint off, uint n) { - if (off > entry->file_size || off + n < off) { + if (off > entry->file_size || off + n < off || (entry->attribute & ATTR_DIRECTORY)) { return 0; } if (off + n > entry->file_size) { n = entry->file_size - off; } - int clus_num = off / fat.byts_per_clus; - off = off % fat.byts_per_clus; - uint32 cluster = entry->first_clus; - while (clus_num-- > 0 && cluster < FAT32_EOC) { - cluster = read_fat(cluster); - } uint tot, m; - for (tot = 0; cluster < FAT32_EOC && tot < n; tot += m, off += m, dst += m) { + for (tot = 0; entry->cur_clus < FAT32_EOC && tot < n; tot += m, off += m, dst += m) { + reloc_clus(entry, off); m = fat.byts_per_clus - off % fat.byts_per_clus; if (n - tot < m) { m = n - tot; } - if (rw_clus(cluster, 0, user_dst, dst, off % fat.byts_per_clus, m) != m) { + if (rw_clus(entry->cur_clus, 0, user_dst, dst, off % fat.byts_per_clus, m) != m) { break; } - cluster = read_fat(cluster); } return tot; } @@ -258,38 +282,34 @@ int eread(struct dirent *entry, int user_dst, uint64 dst, uint off, uint n) // Caller must hold entry->lock. int ewrite(struct dirent *entry, int user_src, uint64 src, uint off, uint n) { - if (off > entry->file_size || off + n < off) { + if (off > entry->file_size || off + n < off || (entry->attribute & ATTR_READ_ONLY)) { return -1; } - int clus_num = off / fat.byts_per_clus; - off = off % fat.byts_per_clus; - if (entry->first_clus == 0) { - entry->first_clus = alloc_clus(); - eupdate(entry); - } - uint32 cluster = entry->first_clus; - while (clus_num-- > 0) { - cluster = read_fat(cluster); + if (entry->first_clus == 0) { // so file_size if 0 too, which requests off == 0 + entry->cur_clus = entry->first_clus = alloc_clus(entry->dev); + entry->clus_cnt = 0; + entry->dirty = 1; } uint tot, m; for (tot = 0; tot < n; tot += m, off += m, src += m) { - if (cluster >= FAT32_EOC) { + reloc_clus(entry, off); + if (entry->cur_clus >= FAT32_EOC) { uint32 new_clus = alloc_clus(entry->dev); - write_fat(cluster, new_clus); + write_fat(entry->cur_clus, new_clus); } m = fat.byts_per_clus - off % fat.byts_per_clus; if (n - tot < m) { m = n - tot; } - if (rw_clus(cluster, 1, user_src, src, off % fat.byts_per_clus, m) != m) { + if (rw_clus(entry->cur_clus, 1, user_src, src, off % fat.byts_per_clus, m) != m) { break; } - cluster = read_fat(cluster); } if(n > 0) { if(off > entry->file_size) { entry->file_size = off; + entry->dirty = 1; eupdate(entry); } } @@ -297,23 +317,25 @@ int ewrite(struct dirent *entry, int user_src, uint64 src, uint off, uint n) } // should never get root by eget -static struct dirent *eget(uint dev, uint32 parent, char *name) +static struct dirent *eget(struct dirent *parent, char *name) { struct dirent *ep; acquire(&ecache.lock); - for (ep = root.next; ep != &root; ep = ep->next) { - if (ep->dev == dev && ep->parent == parent - && strncmp(ep->filename, name, FAT32_MAX_FILENAME) == 0) { - ep->ref++; - ep->valid = 1; - release(&ecache.lock); - return ep; + if (name) { + for (ep = root.next; ep != &root; ep = ep->next) { + if (ep->parent == parent && strncmp(ep->filename, name, FAT32_MAX_FILENAME) == 0) { + ep->ref++; + ep->valid = 1; + release(&ecache.lock); + edup(ep->parent); + return ep; + } } } for (ep = root.prev; ep != &root; ep = ep->prev) { if (ep->ref == 0) { ep->ref = 1; - ep->dev = dev; + ep->dev = parent->dev; ep->off = 0; ep->valid = 0; release(&ecache.lock); @@ -333,14 +355,15 @@ static void wnstr_lazy(wchar *dst, char const *src, int len) { struct dirent *ealloc(struct dirent *dp, char *name, int dir) { - if (dp->attribute != ATTR_DIRECTORY) { return 0; } + if (!(dp->attribute & ATTR_DIRECTORY)) { return 0; } struct dirent *ep; uint off = 0; ep = dirlookup(dp, name, &off); if (ep != 0) { // entry exists + eput(ep); return 0; } - ep = eget(dp->dev, dp->parent, name); + ep = eget(dp, name); if (ep->valid) { // shouldn't be valid panic("ealloc"); } @@ -349,39 +372,36 @@ struct dirent *ealloc(struct dirent *dp, char *name, int dir) ep->attribute = 0; ep->file_size = 0; ep->first_clus = 0; - ep->parent = dp->first_clus; + ep->parent = edup(dp); ep->off = off; + ep->clus_cnt = 0; + ep->cur_clus = 0; strncpy(ep->filename, name, FAT32_MAX_FILENAME); int len = strlen(name); int entcnt = (len + CHAR_LONG_NAME - 1) / CHAR_LONG_NAME; // count of l-n-entries, rounds up - uint clus = dp->first_clus; - int clus_cnt = off / fat.byts_per_clus; - off %= fat.byts_per_clus; - while (clus_cnt-- != 0) { - clus = read_fat(clus); - } uint8 ebuf[32] = {0}; if(dir){ // generate "." and ".." for ep ep->attribute |= ATTR_DIRECTORY; - ep->first_clus = alloc_clus(); + ep->cur_clus = ep->first_clus = alloc_clus(dp->dev); strncpy((char *)ebuf, ".", 11); ebuf[11] = ATTR_DIRECTORY; *(uint16 *)(ebuf + 20) = (uint16)(ep->first_clus >> 16); *(uint16 *)(ebuf + 26) = (uint16)(ep->first_clus & 0xff); *(uint32 *)(ebuf + 28) = 0; - rw_clus(clus, 1, 0, (uint64)ebuf, 0, sizeof(ebuf)); + rw_clus(ep->cur_clus, 1, 0, (uint64)ebuf, 0, sizeof(ebuf)); strncpy((char *)ebuf, "..", 11); ebuf[11] = ATTR_DIRECTORY; *(uint16 *)(ebuf + 20) = (uint16)(dp->first_clus >> 16); *(uint16 *)(ebuf + 26) = (uint16)(dp->first_clus & 0xff); *(uint32 *)(ebuf + 28) = 0; - rw_clus(clus, 1, 0, (uint64)ebuf, 32, sizeof(ebuf)); + rw_clus(ep->cur_clus, 1, 0, (uint64)ebuf, 32, sizeof(ebuf)); } else { ep->attribute |= ATTR_ARCHIVE; } memset(ebuf, 0, sizeof(ebuf)); + off = reloc_clus(dp, off); for (uint8 i = entcnt; i > 0; i--) { // ignore checksum ebuf[0] = i; if (i == entcnt) { @@ -394,13 +414,10 @@ struct dirent *ealloc(struct dirent *dp, char *name, int dir) wnstr_lazy((wchar *) (ebuf + 1), ep->filename + i * CHAR_LONG_NAME, 5); wnstr_lazy((wchar *) (ebuf + 14), ep->filename + i * CHAR_LONG_NAME + 5, 6); wnstr_lazy((wchar *) (ebuf + 28), ep->filename + i * CHAR_LONG_NAME + 11, 2); - rw_clus(clus, 1, 0, (uint64)ebuf, off, sizeof(ebuf)); - off += 32; - if (off >= fat.byts_per_clus) { - clus = read_fat(clus); - off -= fat.byts_per_clus; - } + rw_clus(dp->cur_clus, 1, 0, (uint64)ebuf, off, sizeof(ebuf)); + off = reloc_clus(dp, off + 32); } + ep->dirty = 1; eupdate(ep); ep->valid = 1; eunlock(ep); @@ -409,17 +426,20 @@ struct dirent *ealloc(struct dirent *dp, char *name, int dir) struct dirent *edup(struct dirent *entry) { - acquire(&ecache.lock); - entry->ref++; - release(&ecache.lock); + if (entry != 0) { + acquire(&ecache.lock); + entry->ref++; + release(&ecache.lock); + } return entry; } -// only update file size void eupdate(struct dirent *entry) { + if (!entry->dirty) { return; } + printf("[eupdate] %s\n", entry->filename); uint entcnt; - uint32 clus = entry->parent; + uint32 clus = entry->parent->first_clus; uint32 off = entry->off % fat.byts_per_clus; for (uint clus_cnt = entry->off / fat.byts_per_clus; clus_cnt > 0; clus_cnt--) { clus = read_fat(clus); @@ -437,12 +457,13 @@ void eupdate(struct dirent *entry) rw_clus(clus, 1, 0, (uint64) &clus_high, off + 20, sizeof(clus_high)); rw_clus(clus, 1, 0, (uint64) &clus_low, off + 26, sizeof(clus_low)); rw_clus(clus, 1, 0, (uint64) &entry->file_size, off + 28, sizeof(entry->file_size)); + entry->dirty = 0; } void etrunc(struct dirent *entry) { uint entcnt; - uint32 clus = entry->parent; + uint32 clus = entry->parent->first_clus; uint32 off = entry->off % fat.byts_per_clus; for (uint clus_cnt = entry->off / fat.byts_per_clus; clus_cnt > 0; clus_cnt--) { clus = read_fat(clus); @@ -496,6 +517,7 @@ void eput(struct dirent *entry) root.next->prev = entry; root.next = entry; eupdate(entry); + eput(entry->parent); } releasesleep(&entry->lock); acquire(&ecache.lock); @@ -506,7 +528,8 @@ void eput(struct dirent *entry) void estat(struct dirent *entry, struct stat *st) { - st->attribute = entry->attribute; + strncpy(st->name, entry->filename, STAT_MAX_NAME); + st->type = (entry->attribute & ATTR_DIRECTORY) ? T_DIR : T_FILE; st->dev = entry->dev; st->size = entry->file_size; } @@ -558,6 +581,54 @@ static void read_entry_info(struct dirent *entry, uint8 *raw_entry) entry->first_clus = ((uint32) *(uint16 *)(raw_entry + 20)) << 16; entry->first_clus += *(uint16 *)(raw_entry + 26); entry->file_size = *(uint32 *)(raw_entry + 28); + entry->cur_clus = entry->first_clus; + entry->clus_cnt = 0; +} +/** + * Read a directory from off + * + * + */ +int enext(struct dirent *dp, struct dirent *ep, uint off, int *count) +{ + if (!(dp->attribute & ATTR_DIRECTORY)) + panic("enext not dir"); + if (ep->valid) + panic("enext ep valid"); + if (off % 32) + panic("enext not align"); + + uint8 ebuf[32]; + int cnt = 0; + for (uint off2 = reloc_clus(dp, off); dp->cur_clus < FAT32_EOC; off2 = reloc_clus(dp, off2 + 32)) { + if (rw_clus(dp->cur_clus, 0, 0, (uint64)ebuf, off2, 32) != 32 || ebuf[0] == END_OF_ENTRY) { + return -1; + } + if (ebuf[0] == EMPTY_ENTRY) { + cnt++; + continue; + } else if (cnt) { + *count = cnt; + return 0; + } + if (ebuf[11] == ATTR_LONG_NAME) { + int lcnt = ebuf[0] & ~LAST_LONG_ENTRY; + if (ebuf[0] & LAST_LONG_ENTRY) { + *count = lcnt + 1; // plus the s-n-e; + count = 0; + *(ep->filename + lcnt * CHAR_LONG_NAME) = '\0'; // if filename aligns CHAR_LONG_NAME, then no '\0' is stored in the raw entry. + } + read_entry_name(ep->filename + (lcnt - 1) * CHAR_LONG_NAME, ebuf, 1); + } else { + if (count) { + *count = 1; + read_entry_name(ep->filename, ebuf, 0); + } + read_entry_info(ep, ebuf); + return 1; + } + } + return -1; } /** @@ -568,82 +639,38 @@ static void read_entry_info(struct dirent *entry, uint8 *raw_entry) */ struct dirent *dirlookup(struct dirent *entry, char *filename, uint *poff) { - if (entry->attribute != ATTR_DIRECTORY) + if (!(entry->attribute & ATTR_DIRECTORY)) panic("dirlookup not DIR"); - - struct dirent *de = eget(entry->dev, entry->first_clus, filename); + if (strncmp(filename, ".", FAT32_MAX_FILENAME) == 0) { + return edup(entry); + } else if (strncmp(filename, "..", FAT32_MAX_FILENAME) == 0) { + return edup(entry->parent); + } + struct dirent *de = eget(entry, filename); if (de->valid) { return de; } // ecache hits - uint8 ebuf[32]; - char name[CHAR_LONG_NAME]; int len = strlen(filename); - int entcnt = (len + CHAR_LONG_NAME - 1) / CHAR_LONG_NAME; // count of l-n-entries, rounds up - uint32 cluster = entry->first_clus; - uint clus_cnt = 0; - uint off = 0; - int match = 0; - int first = 1; - int empty_cnt = entcnt; - - while (cluster < FAT32_EOC) { - while (off < fat.byts_per_clus) { - if (rw_clus(cluster, 0, 0, (uint64)ebuf, off, 32) != 32 || ebuf[0] == END_OF_ENTRY) { - goto miss; - } - if (ebuf[0] == EMPTY_ENTRY) { - if (poff) { - if (first) { - empty_cnt = entcnt; - first = 0; - } else if (--empty_cnt == 0) { - *poff = clus_cnt * fat.byts_per_clus + off - (entcnt << 5); - poff = 0; - } - } - off += 32; - continue; - } else if (poff) { - first = 1; - } - if (ebuf[11] == ATTR_LONG_NAME) { - int count = ebuf[0] & ~LAST_LONG_ENTRY; - if ((ebuf[0] & LAST_LONG_ENTRY) && count != entcnt) { // meet first l-n-e, and if count is unequal - off += (count + 1) << 5; // skip over, plus corresponding s-n-e - continue; - } - read_entry_name(name, ebuf, 1); - int offset = (count - 1) * CHAR_LONG_NAME; - if (strncmp(name, filename + offset, CHAR_LONG_NAME) != 0) { - off += (count + 1) << 5; - continue; - } - if (count == 1) { - match = 1; - } - off += 32; - } else { - if (!match) { - read_entry_name(name, ebuf, 0); - if (strncmp(name, filename, 11) != 0) { - off += 32; - continue; - } - } - strncpy(de->filename, filename, len); - read_entry_info(de, ebuf); - de->parent = entry->first_clus; - de->off = clus_cnt * fat.byts_per_clus + off - (entcnt << 5); - de->valid = 1; - return de; + int entcnt = (len + CHAR_LONG_NAME - 1) / CHAR_LONG_NAME + 1; // count of l-n-entries, rounds up + int count = 0; + int type; + uint off = reloc_clus(entry, 0); + + while ((type = enext(entry, de, off, &count) != -1)) { + if (type == 0) { + if (poff && count >= entcnt) { + *poff = off; + poff = 0; } + } else if (strncmp(filename, de->filename, FAT32_MAX_FILENAME) == 0) { + de->parent = edup(entry); + de->off = off; + de->valid = 1; + return de; } - off -= fat.byts_per_clus; - cluster = read_fat(cluster); - clus_cnt++; + off = reloc_clus(entry, off + count * 32); } -miss: if (poff) { - *poff = clus_cnt * fat.byts_per_clus + off; + *poff = off; } eput(de); return 0; @@ -683,7 +710,7 @@ static struct dirent *lookup_path(char *path, int parent, char *name) } while ((path = skipelem(path, name)) != 0) { elock(entry); - if (entry->attribute != ATTR_DIRECTORY) { + if (!(entry->attribute & ATTR_DIRECTORY)) { eunlock(entry); eput(entry); return 0; diff --git a/kernel/file.c b/kernel/file.c index 97943e3..b6002d8 100644 --- a/kernel/file.c +++ b/kernel/file.c @@ -77,8 +77,10 @@ fileclose(struct file *f) if(ff.type == FD_PIPE){ pipeclose(ff.pipe, ff.writable); - } else if(ff.type == FD_ENTRY || ff.type == FD_DEVICE){ + } else if(ff.type == FD_ENTRY){ eput(ff.ep); + } else if (ff.type == FD_DEVICE) { + } } @@ -90,7 +92,7 @@ filestat(struct file *f, uint64 addr) struct proc *p = myproc(); struct stat st; - if(f->type == FD_ENTRY || f->type == FD_DEVICE){ + if(f->type == FD_ENTRY){ elock(f->ep); estat(f->ep, &st); eunlock(f->ep); @@ -111,19 +113,23 @@ fileread(struct file *f, uint64 addr, int n) if(f->readable == 0) return -1; - if(f->type == FD_PIPE){ - r = piperead(f->pipe, addr, n); - } else if(f->type == FD_DEVICE){ - if(f->major < 0 || f->major >= NDEV || !devsw[f->major].read) - return -1; - r = devsw[f->major].read(1, addr, n); - } else if(f->type == FD_ENTRY){ - elock(f->ep); - if((r = eread(f->ep, 1, addr, f->off, n)) > 0) - f->off += r; - eunlock(f->ep); - } else { - panic("fileread"); + switch (f->type) { + case FD_PIPE: + r = piperead(f->pipe, addr, n); + break; + case FD_DEVICE: + if(f->major < 0 || f->major >= NDEV || !devsw[f->major].read) + return -1; + r = devsw[f->major].read(1, addr, n); + break; + case FD_ENTRY: + elock(f->ep); + if((r = eread(f->ep, 1, addr, f->off, n)) > 0) + f->off += r; + eunlock(f->ep); + break; + default: + panic("fileread"); } return r; @@ -161,3 +167,33 @@ filewrite(struct file *f, uint64 addr, int n) return ret; } +// Read from dir f. +// addr is a user virtual address. +int +dirnext(struct file *f, uint64 addr) +{ + struct proc *p = myproc(); + + if(f->readable == 0 || !(f->ep->attribute & ATTR_DIRECTORY)) + return -1; + + struct dirent de; + struct stat st; + int count = 0; + int ret; + memset(&de, 0, sizeof(de)); + elock(f->ep); + while ((ret = enext(f->ep, &de, f->off, &count)) == 0) { // skip empty entry + f->off += count * 32; + } + eunlock(f->ep); + if (ret == -1) + return 0; + + f->off += count * 32; + estat(&de, &st); + if(copyout(p->pagetable, addr, (char *)&st, sizeof(st)) < 0) + return -1; + + return 1; +} \ No newline at end of file diff --git a/kernel/include/defs.h b/kernel/include/defs.h index 28272fd..eb8fa19 100644 --- a/kernel/include/defs.h +++ b/kernel/include/defs.h @@ -50,6 +50,7 @@ void eput(struct dirent *entry); void estat(struct dirent *ep, struct stat *st); void elock(struct dirent *entry); void eunlock(struct dirent *entry); +int enext(struct dirent *dp, struct dirent *ep, uint off, int *count); struct dirent* ename(char *path); struct dirent* enameparent(char *path, char *name); int eread(struct dirent *entry, int user_dst, uint64 dst, uint off, uint n); @@ -63,6 +64,7 @@ void fileinit(void); int fileread(struct file*, uint64, int n); int filestat(struct file*, uint64 addr); int filewrite(struct file*, uint64, int n); +int dirnext(struct file *f, uint64 addr); // fs.c // void fsinit(int); diff --git a/kernel/include/fat32.h b/kernel/include/fat32.h index 18843a2..cc625db 100644 --- a/kernel/include/fat32.h +++ b/kernel/include/fat32.h @@ -28,12 +28,16 @@ struct dirent { // uint16 last_write_date; uint32 file_size; + uint32 cur_clus; + uint clus_cnt; + /* for OS */ - uint dev; - int valid; + uint8 dev; + uint8 dirty; + uint8 valid; int ref; - uint32 parent; // because FAT32 doesn't have such thing like inum, use this for cache trick - uint32 off; // offset in the parent dir entry, for writing convenience + uint32 off; // offset in the parent dir entry, for writing convenience + struct dirent *parent; // because FAT32 doesn't have such thing like inum, use this for cache trick struct dirent *next; struct dirent *prev; struct sleeplock lock; diff --git a/kernel/include/stat.h b/kernel/include/stat.h index aee2983..25abe15 100644 --- a/kernel/include/stat.h +++ b/kernel/include/stat.h @@ -2,11 +2,12 @@ #define T_FILE 2 // File #define T_DEVICE 3 // Device +#define STAT_MAX_NAME 32 + struct stat { + char name[STAT_MAX_NAME + 1]; int dev; // File system's disk device - uint8 attribute; - // short type; // Type of file - // short nlink; // Number of links to file + short type; // Type of file uint64 size; // Size of file in bytes }; diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h index 84726de..e9e8208 100644 --- a/kernel/include/syscall.h +++ b/kernel/include/syscall.h @@ -22,3 +22,5 @@ #define SYS_close 21 #define SYS_test_proc 22 #define SYS_dev 23 +#define SYS_dir 24 +#define SYS_getcwd 25 diff --git a/kernel/proc.c b/kernel/proc.c index d631400..06c85c2 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -402,9 +402,7 @@ exit(int status) } } - // begin_op(); eput(p->cwd); - // end_op(); p->cwd = 0; // we might re-parent a child to init. we can't be precise about diff --git a/kernel/string.c b/kernel/string.c index ca337c9..6df8e18 100644 --- a/kernel/string.c +++ b/kernel/string.c @@ -118,8 +118,8 @@ void snstr(char *dst, wchar const *src, int len) { *dst++ = (uchar)(*src & 0xff); src ++; } - - *dst = 0; + while(len-- > 0) + *dst++ = 0; } int wcsncmp(wchar const *s1, wchar const *s2, int len) { diff --git a/kernel/syscall.c b/kernel/syscall.c index deb940c..ac99d33 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -108,6 +108,8 @@ extern uint64 sys_write(void); extern uint64 sys_uptime(void); extern uint64 sys_test_proc(void); extern uint64 sys_dev(void); +extern uint64 sys_dir(void); +extern uint64 sys_getcwd(void); static uint64 (*syscalls[])(void) = { [SYS_fork] sys_fork, @@ -133,6 +135,8 @@ static uint64 (*syscalls[])(void) = { [SYS_close] sys_close, [SYS_test_proc] sys_test_proc, [SYS_dev] sys_dev, +[SYS_dir] sys_dir, +[SYS_getcwd] sys_getcwd, }; void diff --git a/kernel/sysfile.c b/kernel/sysfile.c index 2ebd829..78f396b 100644 --- a/kernel/sysfile.c +++ b/kernel/sysfile.c @@ -144,16 +144,8 @@ create(char *path, short type) return 0; elock(dp); - - if((ep = dirlookup(dp, name, 0)) != 0){ - eunlock(dp); - eput(dp); - elock(ep); - return ep; - } - if((ep = ealloc(dp, name, type == T_DIR)) == 0) - panic("create: ialloc"); + return 0; elock(ep); @@ -186,7 +178,7 @@ sys_open(void) return -1; } elock(ep); - if(ep->attribute == ATTR_DIRECTORY && omode != O_RDONLY){ + if((ep->attribute & ATTR_DIRECTORY) && omode != O_RDONLY){ eunlock(ep); eput(ep); return -1; @@ -265,7 +257,7 @@ sys_chdir(void) return -1; } elock(ep); - if(ep->attribute != ATTR_DIRECTORY){ + if(!(ep->attribute & ATTR_DIRECTORY)){ eunlock(ep); eput(ep); return -1; @@ -349,6 +341,7 @@ sys_pipe(void) return 0; } +// To open console device. uint64 sys_dev(void) { @@ -381,4 +374,52 @@ sys_dev(void) f->writable = (omode & O_WRONLY) || (omode & O_RDWR); return fd; +} + +// To support ls command +uint64 +sys_dir(void) +{ + struct file *f; + uint64 p; + + if(argfd(0, 0, &f) < 0 || argaddr(1, &p) < 0) + return -1; + return dirnext(f, p); +} + +// get absolute cwd string +uint64 +sys_getcwd(void) +{ + uint64 addr; + if (argaddr(0, &addr) < 0) + return -1; + + struct dirent *de = myproc()->cwd; + char path[MAXPATH]; + char *s; + int len; + + if (de->parent == 0) { + s = "/"; + } else { + s = path + MAXPATH - 1; + *s-- = '\0'; + while (de->parent) { + len = strlen(de->filename); + s -= len; + if (s <= path) // can't reach root "/" + return -1; + strncpy(s, de->filename, len); + *--s = '/'; + de = de->parent; + } + } + + if (copyout(myproc()->pagetable, addr, s, strlen(s) + 1) < 0) + return -1; + + return 0; + } \ No newline at end of file diff --git a/xv6-user/ls.c b/xv6-user/ls.c index 3e03f21..c0a6e1a 100644 --- a/xv6-user/ls.c +++ b/xv6-user/ls.c @@ -1,37 +1,31 @@ #include "kernel/include/types.h" #include "kernel/include/stat.h" #include "xv6-user/user.h" -// #include "kernel/include/fs.h" -#include "kernel/include/fat32.h" - -#define FILENAME_LENGTH 32 char* -fmtname(char *path) +fmtname(char *name) { - static char buf[FILENAME_LENGTH+1]; - char *p; - - // Find first character after last slash. - for(p=path+strlen(path); p >= path && *p != '/'; p--) - ; - p++; + static char buf[STAT_MAX_NAME+1]; + int len = strlen(name); // Return blank-padded name. - if(strlen(p) >= FILENAME_LENGTH) - return p; - memmove(buf, p, strlen(p)); - memset(buf+strlen(p), ' ', FILENAME_LENGTH-strlen(p)); + if(len >= STAT_MAX_NAME) + return name; + memmove(buf, name, len); + memset(buf + len, ' ', STAT_MAX_NAME - len); + buf[STAT_MAX_NAME] = '\0'; return buf; } void ls(char *path) { - char buf[512], *p; int fd; - // struct dirent de; struct stat st; + char *types[] = { + [T_DIR] "DIR ", + [T_FILE] "FILE", + }; if((fd = open(path, 0)) < 0){ fprintf(2, "ls: cannot open %s\n", path); @@ -44,30 +38,12 @@ ls(char *path) return; } - if (st.attribute == ATTR_DIRECTORY){ - - if(strlen(path) + 1 + FILENAME_LENGTH + 1 > sizeof buf){ - printf("ls: path too long\n"); - } - strcpy(buf, path); - p = buf+strlen(buf); - *p++ = '/'; - -// CAN'T WORK NOW - - while(read(fd, &de, sizeof(de)) == sizeof(de)){ - // if(de.inum == 0) - // continue; - // memmove(p, de.name, FILENAME_LENGTH); - // p[DIRSIZ] = 0; - if(stat(buf, &st) < 0){ - printf("ls: cannot stat %s\n", buf); - continue; - } - printf("%s %d %d\n", fmtname(buf), st.attribute, st.size); + if (st.type == T_DIR){ + while(dir(fd, &st) == 1){ + printf("%s %s\t%d\n", fmtname(st.name), types[st.type], st.size); } } else { - printf("%s %d %l\n", fmtname(path), st.attribute, st.size); + printf("%s %s\t%l\n", fmtname(st.name), types[st.type], st.size); } close(fd); } diff --git a/xv6-user/sh.c b/xv6-user/sh.c index 227ae82..c362de9 100644 --- a/xv6-user/sh.c +++ b/xv6-user/sh.c @@ -133,7 +133,14 @@ runcmd(struct cmd *cmd) int getcmd(char *buf, int nbuf) { - fprintf(2, "$ "); + #ifndef QEMU + static char pf[] = "xv6-k210"; + #else + static char pf[] = "xv6-qemu"; + #endif + char cur[128]; + getcwd(cur); + fprintf(2, "%s@%s$ ", pf, cur); memset(buf, 0, nbuf); gets(buf, nbuf); if(buf[0] == 0) // EOF @@ -148,7 +155,7 @@ main(void) int fd; // Ensure that three file descriptors are open. - while((fd = open("console", O_RDWR)) >= 0){ + while((fd = dev(O_RDWR, 1, 0)) >= 0){ if(fd >= 3){ close(fd); break; diff --git a/xv6-user/user.h b/xv6-user/user.h index 8f9e568..e1bea06 100644 --- a/xv6-user/user.h +++ b/xv6-user/user.h @@ -25,6 +25,8 @@ int sleep(int); int uptime(void); int test_proc(void); int dev(int, short, short); +int dir(int fd, struct stat*); +int getcwd(char *); // ulib.c int stat(const char*, struct stat*); diff --git a/xv6-user/usys.pl b/xv6-user/usys.pl index ad6b1ec..64c7d4b 100755 --- a/xv6-user/usys.pl +++ b/xv6-user/usys.pl @@ -38,3 +38,5 @@ entry("sleep"); entry("uptime"); entry("test_proc"); entry("dev"); +entry("dir"); +entry("getcwd");