难度:moderate
Write a simple version of the UNIX find program for xv6: find all the files in a directory tree with a specific name. Your solution should be in the file user/find.c.
题目要求:实现find ,即在某个路径中,找出某个文件
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
#include "kernel/fcntl.h"
char* fmt_name(char *path){
static char buf[DIRSIZ+1];
char *p;
//find first character after last slash
for(p=path+strlen(path);p>=path&&*p!='/';p--);
p++;
memmove(buf,p,strlen(p)+1);
return buf;
}
void eq_print(char *fileName,char *findName){
if(strcmp(fmt_name(fileName),findName)==0){
printf("%s\n",fileName);
}
}
void find(char *path,char *findName){
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;
if((fd = open(path, O_RDONLY)) < 0){
fprintf(2, "ls: cannot open %s\n", path);
return;
}
if(fstat(fd, &st) < 0){
fprintf(2, "ls: cannot stat %s\n", path);
close(fd);
return;
}
/*
int fd;声明一个文件描述符变量 fd,用于在程序中表示打开的文件或目录。
struct dirent de;定义了一个结构体 dirent 变量 de,通常用于存储读取目录时的目录项信息。
struct stat st;:定义了一个结构体 stat 变量 st,用于存储文件或目录的状态信息。
if((fd = open(path, O_RDONLY)) < 0){ ... }:尝试以只读模式打开指定路径的文件或目录。如果 open() 函数返回的文件描述符小于 0(表示打开失败),则输出错误信息,并返回。
if(fstat(fd, &st) < 0){ ... }:如果文件或目录成功打开,则使用 fstat() 函数获取文件描述符 fd 对应文件或目录的状态信息,并将结果存储在 st 结构体中。如果获取状态信息失败(fstat() 返回值小于 0),则输出错误信息,并关闭文件描述符后返回。
*/
switch(st.type){
case T_DEVICE:
case T_FILE:
eq_print(path,findName);
break;
case T_DIR:
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
printf("find: path too long\n");
break;
}
strcpy(buf, path);
p = buf+strlen(buf);
*p++ = '/';
while(read(fd, &de, sizeof(de)) == sizeof(de)){
if(de.inum ==0||de.inum==1||strcmp(de.name,".")==0 || strcmp(de.name,"..")==0)
continue;
memmove(p, de.name, strlen(de.name));
p[strlen(de.name)] = 0;
find(buf,findName);
}
break;
/*
循环:遍历目录项
read(fd, &de, sizeof(de)):使用read函数从文件描述符 fd中读取一个目录项的内容,并将读取的内容存储在 de 变量中。
只要 read 函数成功读取一个目录项的大小(sizeof(de)),就会继续执行循环。
循环内部:
首先,通过检查 de.inum(目录项的inode号码)是否等于0或1,以及检查目录项的名称是否是.或..,来过滤掉特殊的目录项。
.(点)表示当前目录,..(点点)表示父目录,它们在此处被跳过,不做进一步处理。
如果目录项既不是特殊目录项.(点)或 ..(点点),也不是 inode 号码为 0 或 1,则会执行以下操作:
memmove(p, de.name, strlen(de.name));:将目录项的文件名 de.name 复制到一个指定的缓冲区 buf 中,从指针 p 指向的位置开始存储。
这里使用 memmove 函数是为了确保将文件名正确复制到指定的位置。
p[strlen(de.name)] = 0;:在复制文件名后,将字符串末尾添加一个空字符 \0,以确保字符串以空字符结尾,形成一个 C 风格的字符串。
最后,调用 find(buf, findName);,以递归方式在当前路径下的子目录中继续查找名为 findName 的文件或目录。
在当前目录下的每个子目录中进行深度优先的查找操作。
*/
}
close(fd);
}
/*
void find(char *path, char *findName):
递归函数,接收两个参数,path 表示要搜索的路径,findName 表示要查找的文件或目录名。
代码开始通过 open() 函数尝试打开指定的路径,如果失败则会输出错误信息并返回。
使用 fstat() 函数获取文件的状态信息,如果失败也会输出相应的错误信息并关闭文件描述符后返回。
接下来 定义了一个缓冲区buf和指针p,用于构建要查找的文件或目录的完整路径。
使用 switch 语句检查文件类型:
T_FILE:表示当前路径是一个文件,则调用 eq_print() 函数
T_DIR:表示当前路径是一个目录,则进行目录的遍历和递归查找。
首先检查路径长度是否过长,如果是,则输出相应错误信息。然后将当前路径复制到 buf 中,并在其末尾添加 /。
接着使用 read() 函数读取目录项,并在循环中遍历目录下的所有文件和子目录。
在目录遍历的循环中,会检查每个目录项的 inum 值是否为0或1(通常表示未使用的或损坏的inode),以及是否是.或..目录(表示当前目录和父目录).
如果是则跳过不处理。
将当前目录项的名字添加到 buf 中,并递归调用 find() 函数,以此在当前目录中查找与 findName 匹配的文件或目录。
最后关闭打开的文件描述符 fd。
*/
int main(int argc, char *argv[])
{
if(argc>3){
printf("find: find <path> <fileName>\n");
exit(0);
}
find(argv[1],argv[2]);
exit(0);
}
fmtname函数示意图
find函数示意图:
实验结果: