본문 바로가기
TIL/OS & Linux

MIT 6.828 - 1. Lab 01: Xv6 and Unix utilities - find 코드 및 해석

by 왁왁s 2022. 11. 5.

MIT 6.828 - 1. Lab 01: Xv6 and Unix utilities - find 

 

 

문제 (Problems)

find (moderate)

Write a simple version of the UNIX find program: find all the files in a directory tree with a specific name. Your solution should be in the file user/find.c.

Some hints:

  • Look at user/ls.c to see how to read directories.
  • Use recursion to allow find to descend into sub-directories.
  • Don't recurse into "." and "..".
  • Changes to the file system persist across runs of qemu; to get a clean file system run make clean and then make qemu.
  • You'll need to use C strings. Have a look at K&R (the C book), for example Section 5.5.
  • Note that == does not compare strings like in Python. Use strcmp() instead.
  • Add the program to UPROGS in Makefile.

Your solution is correct if produces the following output (when the file system contains the files b, a/b and a/aa/b):

    $ make qemu
    ...
    init: starting sh
    $ echo > b
    $ mkdir a
    $ echo > a/b
    $ mkdir a/aa
    $ echo > a/aa/b
    $ find . b
    ./b
    ./a/b
    ./a/aa/b
    $

 


 

코드(Code) - find.c 파일

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"

char*
fmtname(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++;

  // Return blank-padded name.
  if(strlen(p) >= DIRSIZ)
     return p;
    memmove(buf, p, strlen(p));
    memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));
    buf[strlen(p)] = 0;
    return buf;
}

void
find(char *path, char *fileName) // Add *fileName parameter
{
  char buf[512], *p;
  int fd;
  struct dirent de;
  struct stat st;

  if((fd = open(path, 0)) < 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;
  }

  switch(st.type){
	  case T_FILE:
		  if (strcmp(fmtname(path), fileName) == 0){ 
			  printf("%s\n", path);
		  }
		  break;
	  case T_DIR:
		  if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
			  printf("ls: 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)
				continue;
			  if(strcmp(".", de.name) == 0  || strcmp("..", de.name) == 0)
				continue;
			  memmove(p, de.name, DIRSIZ); 

			  p[DIRSIZ] = 0;
			  find(buf, fileName); 

			  if(stat(buf, &st) < 0){
				  printf("ls: cannot stat %s\n", buf);				
				  continue;
			  }

		  }
		  break;
					
  }
  close(fd);
}

int
main(int argc, char *argv[])
{
     	if(argc < 3){ 
  		fprintf(2, "find: %s is not a directory\n", argv[1]);	      
	     	exit();	
	}	
    	find(argv[1], argv[2]); 	 
   	exit();
}

로직(logic) 설명

 

기존 ls.c 와 원리는 동일하며 ls.c에서 조금의 수정을 거치면 과제에서의 find를 구현할 수 있다. ls.c 에 작성되어 있던 fmtname( ) 함수를 그대로 사용해, 파일 이름에서 ‘/’ 이후의 첫 character을 찾고, 긴 파일의 이름을 줄여 반환한다.

기존 ls부분을 find 함수로 수정하며, 매개변수 *fileName를 추가하여 사용자가 찾고자 하는 파일 이름을 받는다

 

.

de.name과 같은 카탈로그 관련 정보를 저장하는 dirent de와 파일형식이나 open 여부 등의 파일 상태를 저장한 stat st를 선언한다.

T_FILE 부분에 찾고자 하는 파일 이름과 현재 파일(target)이 일치하면 출력한 후 break를 한다.

While(read(fd, &de, sizeof(de)) == sizeof(de)) 문을 통해 현재 디렉터리에 있는 모든 파일을 읽는다. 파일 이름을 읽으며 if(strcmp(“.”, de.name) == 0 || strcmp(“..”, de.name) == 0) 조건문을 통해 “.”과 “..”, 즉 현재 디렉터리와 상위 디렉터리는 건너뛰도록 설정한다.

 

 

memmove(p, de.name, DIRSIZ)를 통해 파일명을 buf에 이어 붙여 전체 경로를 얻는다. find(buf, fileName)으로 while문이 실행되는 동안 디렉터리를 계속적으로 찾는다. stat를 사용해 문서 상태를 가져오는데, if문을 두어 오류를 방지한다.

 

과제의 입력 값 find . b는 3개의 argv를 입력 받는다. 그렇다면 argc의 개수가 3보다 작으면 오류를 출력해야 한다. if문을 3보다 작으면 오류를 발생하도록 한다. 위에서 만든 find( ) 함수를 활용해 찾고자 하는 파일을 출력한다.

 

 


구현 결과(Result)

댓글