logo       
Google Custom Search
    AddThis Social Bookmark Button
-->

Auditing compat.c:maybe_relink() (was: Re: darcs patch: Fix off-by-one erro: msg#00163

Subject: Auditing compat.c:maybe_relink() (was: Re: darcs patch: Fix off-by-one error)
On Wed, 2005-01-26 15:47:00 +0100, Juliusz Chroboczek <jch@xxxxxxxxxxxxxx>
wrote in message <tpsm4ob63f.fsf@xxxxxxxxxxxxxxxxxxxxxxx>:
> If you've got time, could you please have a look at maybe_relink at
> the end of compat.c?  I've only added it last week, and this function,
> if it's wrong, could cause massive data loss.

Well, I think I've now basically understood what this function tries to
do. Here are some ad-hoc comments, maybe I'll send a patch at some time:
-----------------------------
    if(srcstat.st_dev != dststat.st_dev) {
        return 0;
    }
-----------------------------
This part looks clearly wrong to me. A return value of zero is ment to
mean "source and destination are already linked", but a hard link can
never ever spawn a st_dev (since st_dev does have filesystem meaning
here, not physical storage as in raid/lvm). So I'd return -2, "files
differ".

Another thing that we need to keep an eye on is the linking part:
-----------------------------
    rc = link(src, tempname);
    if(rc < 0) {
        free(tempname);
        return -1;
    }
-----------------------------
As stated, link() is atomic even on NFS, but that's only half of the
truth. Indeed, link() *is* atomic on the server [1], but what happens if
server's positive reply isn't received because of a shortish network
outage? You'll get a -1, probably with EIO. Because I don't yet know
enough about darcs internals, I need to ask how darcs will react if the
file shows up later on, because network is available again. (I've spent
lots of time doing transaction-like things over network filesystems that
don't have them at all, thus I'm hyper-paranoid on that :-)

And the least part I need to think hard about it:
-----------------------------
        while(i < tempstat.st_size) {
            rc1 = read(fd1, buf1, RELINK_BUFFER_SIZE);
            if(rc1 < 0) { close(fd1); close(fd2); goto fail; }
            rc2 = read(fd2, buf2, RELINK_BUFFER_SIZE);
            if(rc2 < 0) { close(fd1); close(fd2); goto fail; }
            if(rc1 == 0 || rc1 != rc2 || memcmp(buf1, buf2, rc1) != 0) {
                close(fd1); close(fd2);
                unlink(tempname);
                free(tempname);
                return -2;
            }
            i += rc1;
        }
-----------------------------
Comparing two files sounds easy, but really, it isn't :-)  This loop is
missing EINTR and short-read handling. I don't know if it's worth
implementing these (since they're quite rare), but if this is mission
critical, I guess giving up with -2 (files differ) only because one of
the reads were short reads isn't neccessary. (I'd figure out what
higher-level Haskall sources will do in this case, though...)


MfG, JBG

[1] I seem to recall that you may get into trouble if your server only
re-exports a different server's filesystem... Also, some network
filesystems may not provide an atomic link at all, SMB with Unix
extensions would be a good candidate, but I'd need to check...

-- 
Jan-Benedict Glaw       jbglaw@xxxxxxxxxx    . +49-172-7608481             _ O _
"Eine Freie Meinung in  einem Freien Kopf    | Gegen Zensur | Gegen Krieg  _ _ O
 fuer einen Freien Staat voll Freier Bürger" | im Internet! |   im Irak!   O O O
ret = do_actions((curr | FREE_SPEECH) & ~(NEW_COPYRIGHT_LAW | DRM | TCPA));

Attachment: signature.asc
Description: Digital signature

_______________________________________________
darcs-devel mailing list
darcs-devel@xxxxxxxxx
http://www.abridgegame.org/cgi-bin/mailman/listinfo/darcs-devel
<Prev in Thread] Current Thread [Next in Thread>