diff --git a/chapter_8.unix_interface/8_5.finfo/Makefile b/chapter_8.unix_interface/8_5.finfo/Makefile
new file mode 100644
index 0000000..f1c13e2
--- /dev/null
+++ b/chapter_8.unix_interface/8_5.finfo/Makefile
@@ -0,0 +1,3 @@
+BINARY=finfo
+
+include ../../Exercise.mk
diff --git a/chapter_8.unix_interface/8_5.finfo/main.c b/chapter_8.unix_interface/8_5.finfo/main.c
new file mode 100644
index 0000000..29bdb74
--- /dev/null
+++ b/chapter_8.unix_interface/8_5.finfo/main.c
@@ -0,0 +1,92 @@
+# define _DEFAULT_SOURCE
+
+# include <stdio.h>
+# include <string.h>
+# include <sys/types.h> /* typedefs */
+# include <sys/stat.h>  /* structure returned by stat */
+# include <dirent.h>
+# include <time.h>
+
+/*
+  you can't read a directory like a file in modern system. so no manual "dirent.h"
+ */
+
+# define MAX_PATH 1024
+
+/* dirwalk: apply fcn to all files in dir */
+void dirwalk (char *dir, void (*fcn)(char *))
+{
+  char name[MAX_PATH];
+  struct dirent *dp;
+  DIR *dfd;
+
+  if ((dfd = opendir(dir)) == NULL) {
+    fprintf(stderr, "dirwalk: can't open %s\n", dir);
+    return;
+  }
+  while ((dp = readdir(dfd)) != NULL) {
+    if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
+      continue;    /* skip self and parent */
+    if (strlen(dir)+strlen(dp->d_name)+2 > sizeof(name))
+      fprintf(stderr, "dirwalk: name %s %s too long\n", dir, dp->d_name);
+    else {
+      sprintf(name, "%s/%s", dir, dp->d_name);
+      (*fcn)(name);
+    }
+  }
+  closedir(dfd);
+}
+
+/* finfo: print the name of file "name" */
+void finfo (char *name)
+{
+  struct stat stbuf;
+  char *ftype, fbits[4] = "---", fmode[10] = "---------", mtime[64];
+
+  if (stat(name, &stbuf) == -1) {
+    fprintf(stderr, "finfo: can't access %s\n", name);
+    return;
+  }
+  if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
+    dirwalk(name, finfo);
+
+  if      (S_ISREG (stbuf.st_mode)) ftype = "FILE";
+  else if (S_ISDIR (stbuf.st_mode)) ftype = " DIR";
+  else if (S_ISCHR (stbuf.st_mode)) ftype = "CDEV";
+  else if (S_ISBLK (stbuf.st_mode)) ftype = "BDEV";
+  else if (S_ISFIFO(stbuf.st_mode)) ftype = "PIPE";
+  else if (S_ISLNK (stbuf.st_mode)) ftype = "LINK";
+  else if (S_ISSOCK(stbuf.st_mode)) ftype = "SOCK";
+  else                              ftype = "(\?\?)";
+
+  if (stbuf.st_mode & S_ISUID) fbits[0] = 'u';
+  if (stbuf.st_mode & S_ISGID) fbits[1] = 'g';
+  if (stbuf.st_mode & S_ISVTX) fbits[2] = 's';
+
+  if (stbuf.st_mode & S_IRUSR) fmode[0] = 'r';
+  if (stbuf.st_mode & S_IWUSR) fmode[1] = 'w';
+  if (stbuf.st_mode & S_IXUSR) fmode[2] = 'x';
+  if (stbuf.st_mode & S_IRGRP) fmode[3] = 'r';
+  if (stbuf.st_mode & S_IWGRP) fmode[4] = 'w';
+  if (stbuf.st_mode & S_IXGRP) fmode[5] = 'x';
+  if (stbuf.st_mode & S_IROTH) fmode[6] = 'r';
+  if (stbuf.st_mode & S_IWOTH) fmode[7] = 'w';
+  if (stbuf.st_mode & S_IXOTH) fmode[8] = 'x';
+
+  strftime(mtime, sizeof(mtime), "%Y/%m/%d %H:%M:%S", localtime(&stbuf.st_mtim.tv_sec));
+    
+  printf("%4s %4d:%4d %3s %9s %s %8ld %2lu %s\n",
+         ftype, stbuf.st_uid, stbuf.st_gid, fbits, fmode, mtime,
+         stbuf.st_size, stbuf.st_nlink, name);
+}
+
+/* print file name */
+int main (int argc, char **argv)
+{
+  if (argc == 1) /* default: current directory */
+    finfo(".");
+  else
+    while (--argc > 0)
+      finfo(*++argv);
+  return 0;
+}
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.targs b/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.targs
new file mode 100644
index 0000000..77fbafa
--- /dev/null
+++ b/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.targs
@@ -0,0 +1 @@
+no_file.txt
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.terr b/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.terr
new file mode 100644
index 0000000..cd74c2a
--- /dev/null
+++ b/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.terr
@@ -0,0 +1 @@
+finfo: can't access no_file.txt
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.tin b/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.tin
new file mode 100644
index 0000000..e69de29
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.tout b/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.tout
new file mode 100644
index 0000000..e69de29
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/1_file.targs b/chapter_8.unix_interface/8_5.finfo/tests/1_file.targs
new file mode 100644
index 0000000..af03a6e
--- /dev/null
+++ b/chapter_8.unix_interface/8_5.finfo/tests/1_file.targs
@@ -0,0 +1 @@
+tests/testdir/Makefile
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/1_file.terr b/chapter_8.unix_interface/8_5.finfo/tests/1_file.terr
new file mode 100644
index 0000000..e69de29
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/1_file.tin b/chapter_8.unix_interface/8_5.finfo/tests/1_file.tin
new file mode 100644
index 0000000..e69de29
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/1_file.tout b/chapter_8.unix_interface/8_5.finfo/tests/1_file.tout
new file mode 100644
index 0000000..99e81e5
--- /dev/null
+++ b/chapter_8.unix_interface/8_5.finfo/tests/1_file.tout
@@ -0,0 +1 @@
+FILE 1000:1000 --- rw-r--r-- 2024/08/28 15:39:07       40  1 tests/testdir/Makefile
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/2_directory.targs b/chapter_8.unix_interface/8_5.finfo/tests/2_directory.targs
new file mode 100644
index 0000000..0b8db3c
--- /dev/null
+++ b/chapter_8.unix_interface/8_5.finfo/tests/2_directory.targs
@@ -0,0 +1 @@
+tests/testdir/
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/2_directory.terr b/chapter_8.unix_interface/8_5.finfo/tests/2_directory.terr
new file mode 100644
index 0000000..e69de29
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/2_directory.tin b/chapter_8.unix_interface/8_5.finfo/tests/2_directory.tin
new file mode 100644
index 0000000..e69de29
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/2_directory.tout b/chapter_8.unix_interface/8_5.finfo/tests/2_directory.tout
new file mode 100644
index 0000000..15a1b39
--- /dev/null
+++ b/chapter_8.unix_interface/8_5.finfo/tests/2_directory.tout
@@ -0,0 +1,7 @@
+FILE 1000:1000 --- rw-r--r-- 2024/08/28 15:39:07     1506  1 tests/testdir//dirent.c
+FILE 1000:1000 --- rw-r--r-- 2024/08/28 15:48:28     2741  1 tests/testdir//main.c
+FILE 1000:1000 --- rw-r--r-- 2024/08/28 15:39:07       40  1 tests/testdir//Makefile
+FILE 1000:1000 --- rw-r--r-- 2024/08/28 15:48:28     2741  1 tests/testdir//mainlink.c
+FILE 1000:1000 --- rw-r--r-- 2024/08/28 15:45:37        0  1 tests/testdir//maindir/another_file
+ DIR 1000:1000 --- rwxr-xr-x 2024/08/28 15:45:37       24  1 tests/testdir//maindir
+ DIR 1000:1000 --- rwxr-xr-x 2024/08/28 15:40:17       78  1 tests/testdir/
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/testdir/Makefile b/chapter_8.unix_interface/8_5.finfo/tests/testdir/Makefile
new file mode 100644
index 0000000..f1c13e2
--- /dev/null
+++ b/chapter_8.unix_interface/8_5.finfo/tests/testdir/Makefile
@@ -0,0 +1,3 @@
+BINARY=finfo
+
+include ../../Exercise.mk
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/testdir/dirent.c b/chapter_8.unix_interface/8_5.finfo/tests/testdir/dirent.c
new file mode 100644
index 0000000..7c40845
--- /dev/null
+++ b/chapter_8.unix_interface/8_5.finfo/tests/testdir/dirent.c
@@ -0,0 +1,64 @@
+# define _XOPEN_SOURCE
+
+# include <error.h>
+# include <errno.h>
+
+# include <stdlib.h>
+# include <unistd.h>
+# include <string.h>
+# include <fcntl.h>     /* flags for read and write */
+# include <sys/types.h> /* typedefs */
+# include <sys/stat.h>  /* structure returned by stat */
+# include <sys/dir.h>   /* local directory structure */
+# include "dirent.h"
+
+
+# ifndef DIRSIZ
+# define DIRSIZ 14
+# endif
+
+/* opendir: open a directory for readdir calls */
+KNR_DIR *knr_opendir (char *dirname)
+{
+  int fd;
+  struct stat stbuf;
+  KNR_DIR *dp;
+  if ((fd = open(dirname, O_RDONLY, 0)) == -1
+      || fstat(fd, &stbuf) == -1
+      || (stbuf.st_mode & S_IFMT) != S_IFDIR
+      || (dp = (KNR_DIR *) malloc(sizeof(KNR_DIR))) == NULL)
+    return NULL;
+  dp->fd = fd;
+  return dp;
+}
+
+/* closedir: close directory opened by opendir */
+void knr_closedir (KNR_DIR *dp)
+{
+  if (dp) {
+    close(dp->fd);
+    free(dp);
+  }
+}
+
+/* readdir: read directory entries in sequence */
+KNR_Dirent *knr_readdir (KNR_DIR *dp)
+{
+  struct direct dirbuf; /* local directory structure */
+  static KNR_Dirent d;      /* return: portable structure */
+
+  ssize_t r;
+
+  while ((r = read(dp->fd, (char *) &dirbuf, sizeof(dirbuf))) == sizeof(dirbuf)) {
+    if (dirbuf.d_ino == 0) /* slot not in use */
+      continue;
+    d.ino = dirbuf.d_ino;
+    strncpy(d.name, dirbuf.d_name, DIRSIZ);
+    d.name[DIRSIZ] = '\0'; /* ensure termination */
+    return &d;
+  }
+  if (r == -1)
+    error(-1, errno, "reading dir \"%s\"", dp->d.name);
+  return NULL;
+}
+
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/testdir/main.c b/chapter_8.unix_interface/8_5.finfo/tests/testdir/main.c
new file mode 100644
index 0000000..29bdb74
--- /dev/null
+++ b/chapter_8.unix_interface/8_5.finfo/tests/testdir/main.c
@@ -0,0 +1,92 @@
+# define _DEFAULT_SOURCE
+
+# include <stdio.h>
+# include <string.h>
+# include <sys/types.h> /* typedefs */
+# include <sys/stat.h>  /* structure returned by stat */
+# include <dirent.h>
+# include <time.h>
+
+/*
+  you can't read a directory like a file in modern system. so no manual "dirent.h"
+ */
+
+# define MAX_PATH 1024
+
+/* dirwalk: apply fcn to all files in dir */
+void dirwalk (char *dir, void (*fcn)(char *))
+{
+  char name[MAX_PATH];
+  struct dirent *dp;
+  DIR *dfd;
+
+  if ((dfd = opendir(dir)) == NULL) {
+    fprintf(stderr, "dirwalk: can't open %s\n", dir);
+    return;
+  }
+  while ((dp = readdir(dfd)) != NULL) {
+    if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
+      continue;    /* skip self and parent */
+    if (strlen(dir)+strlen(dp->d_name)+2 > sizeof(name))
+      fprintf(stderr, "dirwalk: name %s %s too long\n", dir, dp->d_name);
+    else {
+      sprintf(name, "%s/%s", dir, dp->d_name);
+      (*fcn)(name);
+    }
+  }
+  closedir(dfd);
+}
+
+/* finfo: print the name of file "name" */
+void finfo (char *name)
+{
+  struct stat stbuf;
+  char *ftype, fbits[4] = "---", fmode[10] = "---------", mtime[64];
+
+  if (stat(name, &stbuf) == -1) {
+    fprintf(stderr, "finfo: can't access %s\n", name);
+    return;
+  }
+  if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
+    dirwalk(name, finfo);
+
+  if      (S_ISREG (stbuf.st_mode)) ftype = "FILE";
+  else if (S_ISDIR (stbuf.st_mode)) ftype = " DIR";
+  else if (S_ISCHR (stbuf.st_mode)) ftype = "CDEV";
+  else if (S_ISBLK (stbuf.st_mode)) ftype = "BDEV";
+  else if (S_ISFIFO(stbuf.st_mode)) ftype = "PIPE";
+  else if (S_ISLNK (stbuf.st_mode)) ftype = "LINK";
+  else if (S_ISSOCK(stbuf.st_mode)) ftype = "SOCK";
+  else                              ftype = "(\?\?)";
+
+  if (stbuf.st_mode & S_ISUID) fbits[0] = 'u';
+  if (stbuf.st_mode & S_ISGID) fbits[1] = 'g';
+  if (stbuf.st_mode & S_ISVTX) fbits[2] = 's';
+
+  if (stbuf.st_mode & S_IRUSR) fmode[0] = 'r';
+  if (stbuf.st_mode & S_IWUSR) fmode[1] = 'w';
+  if (stbuf.st_mode & S_IXUSR) fmode[2] = 'x';
+  if (stbuf.st_mode & S_IRGRP) fmode[3] = 'r';
+  if (stbuf.st_mode & S_IWGRP) fmode[4] = 'w';
+  if (stbuf.st_mode & S_IXGRP) fmode[5] = 'x';
+  if (stbuf.st_mode & S_IROTH) fmode[6] = 'r';
+  if (stbuf.st_mode & S_IWOTH) fmode[7] = 'w';
+  if (stbuf.st_mode & S_IXOTH) fmode[8] = 'x';
+
+  strftime(mtime, sizeof(mtime), "%Y/%m/%d %H:%M:%S", localtime(&stbuf.st_mtim.tv_sec));
+    
+  printf("%4s %4d:%4d %3s %9s %s %8ld %2lu %s\n",
+         ftype, stbuf.st_uid, stbuf.st_gid, fbits, fmode, mtime,
+         stbuf.st_size, stbuf.st_nlink, name);
+}
+
+/* print file name */
+int main (int argc, char **argv)
+{
+  if (argc == 1) /* default: current directory */
+    finfo(".");
+  else
+    while (--argc > 0)
+      finfo(*++argv);
+  return 0;
+}
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/testdir/maindir/another_file b/chapter_8.unix_interface/8_5.finfo/tests/testdir/maindir/another_file
new file mode 100644
index 0000000..e69de29
diff --git a/chapter_8.unix_interface/8_5.finfo/tests/testdir/mainlink.c b/chapter_8.unix_interface/8_5.finfo/tests/testdir/mainlink.c
new file mode 120000
index 0000000..8a03e94
--- /dev/null
+++ b/chapter_8.unix_interface/8_5.finfo/tests/testdir/mainlink.c
@@ -0,0 +1 @@
+main.c
\ No newline at end of file