• J
    sha1_file.c:create_tmpfile(): Fix race when creating loose object dirs · b2476a60
    Johan Herland 提交于
    There are cases (e.g. when running concurrent fetches in a repo) where
    multiple Git processes concurrently attempt to create loose objects
    within the same objects/XX/ dir. The creation of the loose object files
    is (AFAICS) safe from races, but the creation of the objects/XX/ dir in
    which the loose objects reside is unsafe, for example:
    
    Two concurrent fetches - A and B. As part of its fetch, A needs to store
    12aaaaa as a loose object. B, on the other hand, needs to store 12bbbbb
    as a loose object. The objects/12 directory does not already exist.
    Concurrently, both A and B determine that they need to create the
    objects/12 directory (because their first call to git_mkstemp_mode()
    within create_tmpfile() fails witn ENOENT). One of them - let's say A -
    executes the following mkdir() call before the other. This first call
    returns success, and A moves on. When B gets around to calling mkdir(),
    it fails with EEXIST, because A won the race. The mkdir() error causes B
    to return -1 from create_tmpfile(), which propagates all the way,
    resulting in the fetch failing with:
    
      error: unable to create temporary file: File exists
      fatal: failed to write object
      fatal: unpack-objects failed
    
    Although it's hard to add a testcase reproducing this issue, it's easy
    to provoke if we insert a sleep after the
    
      if (mkdir(buffer, 0777) || adjust_shared_perm(buffer))
          return -1;
    
    block, and then run two concurrent "git fetch"es against the same repo.
    
    The fix is to simply handle mkdir() failing with EEXIST as a success.
    If EEXIST is somehow returned for the wrong reasons (because the relevant
    objects/XX is not a directory, or is otherwise unsuitable for object
    storage), the following call to adjust_shared_perm(), or ultimately the
    retried call to git_mkstemp_mode() will fail, and we end up returning
    error from create_tmpfile() in any case.
    
    Note that there are still cases where two users with unsuitable umasks
    in a shared repo can end up in two races where one user first wins the
    mkdir() race to create an objects/XX/ directory, and then the other user
    wins the adjust_shared_perms() race to chmod() that directory, but fails
    because it is (transiently, until the first users completes its chmod())
    unwriteable to the other user. However, (an equivalent of) this race also
    exists before this patch, and is made no worse by this patch.
    Signed-off-by: NJohan Herland <johan@herland.net>
    Signed-off-by: NJunio C Hamano <gitster@pobox.com>
    b2476a60
sha1_file.c 78.4 KB