kern/57696: NFS client readdir terminates prematurely if renaming files

Brian Candler B.Candler at
Tue Oct 7 05:40:19 PDT 2003

>Number:         57696
>Category:       kern
>Synopsis:       NFS client readdir terminates prematurely if renaming files
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Oct 07 05:40:14 PDT 2003
>Originator:     B.Candler at
>Release:        FreeBSD 4.8p3 i386

FreeBSD-4.7-RELEASE, FreeBSD-4.8p3


If you have an opendir/readdir loop which rename()s files out of that
directory while the directory is being read, and the directory is mounted
over NFS, the readdir terminates prematurely - i.e. not all files are seen.

Problem verified with the following combinations:

   FreeBSD client - NetApp server:         problem [1]
   FreeBSD client - Solaris 2.8 server:    problem [1]
   FreeBSD client - FreeBSD server:        problem [2]
   FreeBSD client - Linux server:          problem [3]
   Linux client - Linux server:            no problem [3]
   Solaris 2.8 client - Netapp server:     no problem [4]

That seems to nail it as a FreeBSD NFS client issue. References:
[3] not yet appeared on archive, message from Stefan Kaltenbrunner

It's a problem in particular for Maildir messages, when moving files from
Maildir/new/* to Maildir/cur/*


Run the following program with an NFS-mounted directory as the command-line
argument. The failure mode is:

    bash-2.05a# ./testnfs /na0/testdir
    Transferred 169 out of 200 files

This program also posted at reference [1] above.

/* Demonstrate problem with NFS readdir */

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h> /* for mkdir */
#include <sys/stat.h>  /* for mkdir */
#include <dirent.h>    /* opendir/readdir etc */

#define TESTSIZE 200
int main(int argc, char *argv[])
  int i;
  char fnbuf[1024], fnbuf2[1024];
  char *dir = argv[1];
  int count;
  DIR *dp;
  struct dirent *de;
  if (argc < 2 || !dir || !dir[0])
    dir = ".";

  sprintf(fnbuf, "%s/new", dir);
  mkdir(fnbuf, 0777);
  sprintf(fnbuf, "%s/cur", dir);
  mkdir(fnbuf, 0777);
  for (i=0; i<TESTSIZE; i++) {
    FILE *f;
    sprintf(fnbuf, "%s/new/MYTESTFILE%d", dir, i);
    f = fopen(fnbuf, "w");
    if (!f) { perror("fopen"); exit(1); }
    fprintf(f, "Some dummy content\n");

  sprintf(fnbuf, "%s/new", dir);
  dp = opendir(fnbuf);
  if (!dp) { perror("opendir"); exit(1); }
  count = 0;
  while ((de = readdir(dp))) {
    if (de->d_name[0] == '.') continue;
    sprintf(fnbuf, "%s/new/%s", dir, de->d_name);
    sprintf(fnbuf2,"%s/cur/%s:2,S", dir, de->d_name);
    if (rename(fnbuf, fnbuf2) < 0) {
      fprintf(stderr, "(from %s to %s)\n", fnbuf, fnbuf2);

  fprintf(stderr, "Transferred %d out of %d files\n", count, TESTSIZE);
  return count != TESTSIZE;


No idea. Workaround implemented in courier-imap is to opendir, readdir 20
items into array, closedir, process the 20 items, rinse and repeat.


More information about the freebsd-bugs mailing list