Skip to content

Commit 5723fe7

Browse files
torvaldsgitster
authored andcommitted
Avoid cross-directory renames and linking on object creation
Instead of creating new temporary objects in the top-level git object directory, create them in the same directory they will finally end up in anyway. This avoids making the final atomic "rename to stable name" operation be a cross-directory event, which makes it a lot easier for various filesystems. Several filesystems do things like change the inode number when moving files across directories (or refuse to do it entirely). In particular, it can also cause problems for NFS implementations that change the filehandle of a file when it moves to a different directory, like the old user-space NFS server did, and like the Linux knfsd still does if you don't export your filesystems with 'no_subtree_check' or if you export a filesystem that doesn't have stable inode numbers across renames). This change also obviously implies creating the object fan-out subdirectory at tempfile creation time, rather than at the final move_temp_to_file() time. Which actually accounts for most of the size of the patch. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 9adefee commit 5723fe7

File tree

1 file changed

+42
-41
lines changed

1 file changed

+42
-41
lines changed

sha1_file.c

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2018,49 +2018,12 @@ static void write_sha1_file_prepare(const void *buf, unsigned long len,
20182018
SHA1_Final(sha1, &c);
20192019
}
20202020

2021-
/*
2022-
* Link the tempfile to the final place, possibly creating the
2023-
* last directory level as you do so.
2024-
*
2025-
* Returns the errno on failure, 0 on success.
2026-
*/
2027-
static int link_temp_to_file(const char *tmpfile, const char *filename)
2028-
{
2029-
int ret;
2030-
char *dir;
2031-
2032-
if (!link(tmpfile, filename))
2033-
return 0;
2034-
2035-
/*
2036-
* Try to mkdir the last path component if that failed.
2037-
*
2038-
* Re-try the "link()" regardless of whether the mkdir
2039-
* succeeds, since a race might mean that somebody
2040-
* else succeeded.
2041-
*/
2042-
ret = errno;
2043-
dir = strrchr(filename, '/');
2044-
if (dir) {
2045-
*dir = 0;
2046-
if (!mkdir(filename, 0777) && adjust_shared_perm(filename)) {
2047-
*dir = '/';
2048-
return -2;
2049-
}
2050-
*dir = '/';
2051-
if (!link(tmpfile, filename))
2052-
return 0;
2053-
ret = errno;
2054-
}
2055-
return ret;
2056-
}
2057-
20582021
/*
20592022
* Move the just written object into its final resting place
20602023
*/
20612024
int move_temp_to_file(const char *tmpfile, const char *filename)
20622025
{
2063-
int ret = link_temp_to_file(tmpfile, filename);
2026+
int ret = link(tmpfile, filename);
20642027

20652028
/*
20662029
* Coda hack - coda doesn't like cross-directory links,
@@ -2114,6 +2077,46 @@ static void close_sha1_file(int fd)
21142077
die("unable to write sha1 file");
21152078
}
21162079

2080+
/* Size of directory component, including the ending '/' */
2081+
static inline int directory_size(const char *filename)
2082+
{
2083+
const char *s = strrchr(filename, '/');
2084+
if (!s)
2085+
return 0;
2086+
return s - filename + 1;
2087+
}
2088+
2089+
/*
2090+
* This creates a temporary file in the same directory as the final
2091+
* 'filename'
2092+
*
2093+
* We want to avoid cross-directory filename renames, because those
2094+
* can have problems on various filesystems (FAT, NFS, Coda).
2095+
*/
2096+
static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename)
2097+
{
2098+
int fd, dirlen = directory_size(filename);
2099+
2100+
if (dirlen + 20 > bufsiz) {
2101+
errno = ENAMETOOLONG;
2102+
return -1;
2103+
}
2104+
memcpy(buffer, filename, dirlen);
2105+
strcpy(buffer + dirlen, "tmp_obj_XXXXXX");
2106+
fd = mkstemp(buffer);
2107+
if (fd < 0 && dirlen) {
2108+
/* Make sure the directory exists */
2109+
buffer[dirlen-1] = 0;
2110+
if (mkdir(buffer, 0777) && adjust_shared_perm(buffer))
2111+
return -1;
2112+
2113+
/* Try again */
2114+
strcpy(buffer + dirlen - 1, "/tmp_obj_XXXXXX");
2115+
fd = mkstemp(buffer);
2116+
}
2117+
return fd;
2118+
}
2119+
21172120
static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
21182121
void *buf, unsigned long len, time_t mtime)
21192122
{
@@ -2138,9 +2141,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
21382141
return error("sha1 file %s: %s\n", filename, strerror(errno));
21392142
}
21402143

2141-
snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
2142-
2143-
fd = mkstemp(tmpfile);
2144+
fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
21442145
if (fd < 0) {
21452146
if (errno == EPERM)
21462147
return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());

0 commit comments

Comments
 (0)