[ Ccing the fdm-users mailing list. Please reply to all addresses in ]
[ To: and Cc: in order to keep all involved parties and the debian   ]
[ BTS in the loop.                                                   ]

A new bug report via the debian BTS, which includes a suggestion for a
fix.

The referenced bug #352589 can be found here:
    http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=352589

Regards, Frank

Louis Opter <opte...@epitech.net>:
> Package: fdm
> Version: 1.5-3
> Severity: important
> Tags: patch
> 
> Dear maintainer,
> 
> link() fails with EXDEV when you try to link files accross file systems.
> 
> On the AFS file system, this also occurs between directories :
> http://docs.openafs.org/AdminGuide/ch02.html#HDRWQ32
> 
> This special case breaks fdm when using maildirs on OpenAFS.
> 
> I suggest a patch which workaround EXDEV by using stat() and rename().
> 
> I haven't run fdm's regressions tests on it. I have just checked it on
> AFS and ext3.
> 
> A similar issue has been fixed in openssh (see #352589).
> 
> The same bug certainly exists in other parts of fdm.
> 
> Here is the code :
> 
--- fdm-1.5.orig/deliver-maildir.c      2008-03-06 10:25:32.000000000 +0100
+++ fdm-1.5-3.debian.louis/deliver-maildir.c    2009-07-23 21:28:34.418851829 
+0200
@@ -146,6 +146,7 @@
        char                             src[MAXPATHLEN], dst[MAXPATHLEN];
        int                              fd;
        ssize_t                          n;
+       struct stat                      sb;
 
        name = NULL;
        fd = -1;
@@ -195,28 +196,52 @@
        fd = -1;
 
        /*
-        * Create the new path and attempt to link it. A failed link jumps
-        * back to find another name in the tmp directory.
+        * Create the new path and attempt to link it. A failed link on EEXIST
+        * jumps back to find another name in the tmp directory.
         */
        if (ppath(dst, sizeof dst, "%s/new/%s", path, name) != 0)
                goto error_unlink;
        log_debug2(
            "%s: linking .../tmp/%s to .../new/%s", a->name, name, name);
        if (link(src, dst) != 0) {
-               if (errno == EEXIST) {
-                       log_debug2("%s: %s: link failed", a->name, src);
+               log_debug2("%s: %s: link failed", a->name, src);
+               /*
+                * EXDEV must also be handled, especially for the AFS filesystem
+                * where hardlinks can't traverse directories :
+                * http://docs.openafs.org/AdminGuide/ch02.html#HDRWQ32
+                */
+               if (errno == EXDEV) {
+                       log_debug2("%s: renaming .../tmp/%s to .../new/%s",
+                               a->name, name, name);
+                       /*
+                        * But stat + rename can be racy.
+                        */
+                       if (stat(dst, &sb) == -1) {
+                               if (errno != ENOENT || rename(src, dst) != 0) {
+                                       log_debug2("%s: %s: rename failed",
+                                               a->name, src);
+                                       goto error_cleanup;
+                               }
+                       } else { /* EEXIST */
+                               if (unlink(src) != 0)
+                                       fatal("unlink failed");
+                               cleanup_deregister(src);
+                               goto restart;
+                       }
+               } else if (errno == EEXIST) {
                        if (unlink(src) != 0)
                                fatal("unlink failed");
                        cleanup_deregister(src);
                        goto restart;
-               }
-               goto error_unlink;
+               } else /* Dot it only if we are not on EXDEV */
+                       goto error_unlink;
+       } else {
+               /* Unlink the original tmp file. */
+               log_debug2("%s: unlinking .../tmp/%s", a->name, name);
+               if (unlink(src) != 0)
+                       goto error_unlink;
        }
 
-       /* Unlink the original tmp file. */
-       log_debug2("%s: unlinking .../tmp/%s", a->name, name);
-       if (unlink(src) != 0)
-               goto error_unlink;
        cleanup_deregister(src);
 
        /* Save the mail file as a tag. */
@@ -229,6 +254,7 @@
 error_unlink:
        if (unlink(src) != 0)
                fatal("unlink failed");
+error_cleanup:
        cleanup_deregister(src);
 
 error_log:




-- 
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