We've gone through the strace logs and thanks to some pointers to the dpkg
code, I think I can explain this.  Dpkg does something like this:

while( filename = readdir(/var/dpkg/info)) {
        err = rename(/var/lib/dpkg/tmp.ci/filename, /var/lib/dpkg/info);
        if (err) {
                unlink /var/lib/dpkg/info/filename
        }
}

The basic idea is that for each file in /var/lib/dpkg/info we overwrite it
with a file from /var/lib/dpkg/tmp.ci.  Btrfs implements readdir with a
per-directory sequence number.  Each time you make a new file in the
directory it gets a new sequence number, which is returned as the offset
for seekdir/telldir use.

The rename operation is creating a new file, so it can show up again in
the next readdir call.  The strace shows readdir finding a file name,
then renaming over it into /var/lib/dpkg/info and then finding the file
name again.

This could actually happen with any of the filesystems, but btrfs is
especially consistent at reproducing it.  A while ago, git hit a similar
problem:

http://www.mail-archive.com/btrfs-de...@oss.oracle.com/msg00370.html

Which we solved in btrfs by forcing a very high readdir offset when the
entire directory had been read, but this doesn't quite work for dpkg
because it doesn't read all the directory entries before it starts
processing them.  Git later added a commit to avoid the problem
internally as well.

I would suggest changing the loop to look like this:

while (filename = readdir(/var/dpkg/info)) {
        add filename to list of things that need processing
}

for each filename in list {
        rename (/var/lib/dpkg/tmp.ci/filename, /var/lib/dpkg/info)
}

In other words, buffer the readdir results.  Just let me know if you
have other questions about this, thanks for bringing us into the
debugging.

-chris




-- 
To UNSUBSCRIBE, email to debian-bugs-dist-requ...@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact listmas...@lists.debian.org

Reply via email to