Subject: None
To: None <netbsd-help@netbsd.org>
From: John Refling <johnr@imageworks.com>
List: netbsd-help
Date: 07/05/2000 18:46:49
I've been using getdents() to scan the directory
structure on my filesystem, and have noticed a few
strange things:

1)  getdents() fails when used on /kern, with an errno
    of 22, which from the man page indicates that:
    "A directory was being read on NFS, but it was
    modified on the server while it was being read."
    While I can see that it is possible for the /kern
    file system to be constantly changing, the find
    command works on kern.  Note however that "cat /kern"
    fails with an "Operation not supported" error,
    while other directories can be cat'ed.  Why?


2)  it appears as though the contents of the directory
    file can contain references to deleted files.
    When I use getdents(), I will occasionally get
    a deleted file name printed out when scanning the
    chain of structures.  This deleted file entry will
    disappear if I touch a new file in that directory
    (it seems as though the directory chain is updated
    at that point).  Out of 65,000 files on my partition,
    there were 5 like this.  What's going on?  Am I
    using getdents incorrectly?  Is the file system
    corrupt?  Is getdents broken?  Is the file system
    broken?  Is it required to do a 'stat' on each
    directory entry from getdents to ensure that it exists?

???????


Below I show that myfind finds a deleted file in the dir
chain, where find and ls do not.  After creating several new
files in that dir, myfind does not find the deleted file
anymore.

> myfind /tmp/nbsd/distfiles | fgrep eso
x /tmp/nbsd/distfiles/esound-0.2.18.tar.gz

> ls /tmp/nbsd/distfiles/esound-0.2.18.tar.gz
ls: /tmp/nbsd/distfiles/esound-0.2.18.tar.gz: No such file or directory

> find /tmp/nbsd/distfiles | fgrep eso

> touch /tmp/nbsd/distfiles/delete{1,2,3,4,5}

> myfind /tmp/nbsd/distfiles | fgrep eso
x /tmp/nbsd/distfiles/esound-0.2.18.tar.gz

> touch /tmp/nbsd/distfiles/delete{6,7,8,9,10,11,12,13,14,15,16,17,18,19}

> myfind /tmp/nbsd/distfiles | fgrep eso

> fsck
** /dev/rsd0a
** Last Mounted on /
** Root file system
** Phase 1 - Check Blocks and Sizes
** Phase 2 - Check Pathnames
** Phase 3 - Check Connectivity
** Phase 4 - Check Reference Counts
** Phase 5 - Check Cyl groups
62748 files, 1279710 used, 2764329 free (11825 frags, 344063 blocks, 0.3% fragmentation)


myfind.c:

#include <dirent.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/param.h>
#include <errno.h>

main(int argc, char **argv) {

   int i, j, fileno, quant, catpos;
   int64_t max;
   char pathfilename[MAXPATHLEN];
   struct stat statd;
   char *cp, *buff;

   // prepend the dir path for stat
   strcpy(pathfilename, argv[1]);
   if (pathfilename[strlen(pathfilename)-1] != '/') strcat(pathfilename, "/");
   catpos = strlen(pathfilename);


   if (0 > (fileno = open(argv[1], O_RDONLY, 555))) {
      printf("no such dir: %s\n", argv[1]);
      exit(1);
   }

   if (0 > fstat(fileno, &statd)) {
      printf("stat on %s failed\n", argv[1]);
      exit(1);
   }

   max = statd.st_blocks * 512;

   if (NULL == (buff = (char *)malloc((size_t)max))) {
      printf("malloc failed\n");
      exit(1);
    }

   if (0 > (quant = getdents(fileno, buff, max)))
      printf("getdents errno is %d on %s\n", errno, argv[1]);

   close(fileno);

   // hop down chain of structures printing out when stat fails
   for (i = 0; i < quant; i += *(unsigned short *)&buff[4+i]) {
      cp = (char *)&buff[8+i];
      strcpy(&pathfilename[catpos], cp);
      if (0 > lstat(pathfilename, &statd))
         printf("x %s\n", pathfilename);
   }
}