*/
#include "cache.h"
-static void try_to_free_builtin(size_t size)
+static void do_nothing(size_t size)
{
- release_pack_memory(size, -1);
}
-static void (*try_to_free_routine)(size_t size) = try_to_free_builtin;
+static void (*try_to_free_routine)(size_t size) = do_nothing;
-void set_try_to_free_routine(void (*routine)(size_t))
+try_to_free_t set_try_to_free_routine(try_to_free_t routine)
{
- try_to_free_routine = (routine) ? routine : try_to_free_builtin;
+ try_to_free_t old = try_to_free_routine;
+ if (!routine)
+ routine = do_nothing;
+ try_to_free_routine = routine;
+ return old;
}
char *xstrdup(const char *str)
if (!ret && !size)
ret = malloc(1);
if (!ret)
- die("Out of memory, malloc failed");
+ die("Out of memory, malloc failed (tried to allocate %lu bytes)",
+ (unsigned long)size);
}
#ifdef XMALLOC_POISON
memset(ret, 0xA5, size);
void *xmallocz(size_t size)
{
void *ret;
- if (size + 1 < size)
+ if (unsigned_add_overflows(size, 1))
die("Data too large to fit into virtual memory space.");
ret = xmalloc(size + 1);
((char*)ret)[size] = 0;
return ret;
}
-void *xmmap(void *start, size_t length,
- int prot, int flags, int fd, off_t offset)
-{
- void *ret = mmap(start, length, prot, flags, fd, offset);
- if (ret == MAP_FAILED) {
- if (!length)
- return NULL;
- release_pack_memory(length, fd);
- ret = mmap(start, length, prot, flags, fd, offset);
- if (ret == MAP_FAILED)
- die_errno("Out of memory? mmap failed");
- }
- return ret;
-}
-
/*
* xread() is the same a read(), but it automatically restarts read()
* operations with a recoverable error (EAGAIN and EINTR). xread()
int xmkstemp(char *template)
{
int fd;
+ char origtemplate[PATH_MAX];
+ strlcpy(origtemplate, template, sizeof(origtemplate));
fd = mkstemp(template);
- if (fd < 0)
- die_errno("Unable to create temporary file");
- return fd;
-}
+ if (fd < 0) {
+ int saved_errno = errno;
+ const char *nonrelative_template;
-int xmkstemp_mode(char *template, int mode)
-{
- int fd;
+ if (!template[0])
+ template = origtemplate;
- fd = git_mkstemp_mode(template, mode);
- if (fd < 0)
- die_errno("Unable to create temporary file");
+ nonrelative_template = make_nonrelative_path(template);
+ errno = saved_errno;
+ die_errno("Unable to create temporary file '%s'",
+ nonrelative_template);
+ }
return fd;
}
-/*
- * zlib wrappers to make sure we don't silently miss errors
- * at init time.
- */
-void git_inflate_init(z_streamp strm)
+/* git_mkstemp() - create tmp file honoring TMPDIR variable */
+int git_mkstemp(char *path, size_t len, const char *template)
{
- const char *err;
-
- switch (inflateInit(strm)) {
- case Z_OK:
- return;
-
- case Z_MEM_ERROR:
- err = "out of memory";
- break;
- case Z_VERSION_ERROR:
- err = "wrong version";
- break;
- default:
- err = "error";
+ const char *tmp;
+ size_t n;
+
+ tmp = getenv("TMPDIR");
+ if (!tmp)
+ tmp = "/tmp";
+ n = snprintf(path, len, "%s/%s", tmp, template);
+ if (len <= n) {
+ errno = ENAMETOOLONG;
+ return -1;
}
- die("inflateInit: %s (%s)", err, strm->msg ? strm->msg : "no message");
+ return mkstemp(path);
}
-void git_inflate_end(z_streamp strm)
+/* git_mkstemps() - create tmp file with suffix honoring TMPDIR variable. */
+int git_mkstemps(char *path, size_t len, const char *template, int suffix_len)
{
- if (inflateEnd(strm) != Z_OK)
- error("inflateEnd: %s", strm->msg ? strm->msg : "failed");
+ const char *tmp;
+ size_t n;
+
+ tmp = getenv("TMPDIR");
+ if (!tmp)
+ tmp = "/tmp";
+ n = snprintf(path, len, "%s/%s", tmp, template);
+ if (len <= n) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ return mkstemps(path, suffix_len);
}
-int git_inflate(z_streamp strm, int flush)
+/* Adapted from libiberty's mkstemp.c. */
+
+#undef TMP_MAX
+#define TMP_MAX 16384
+
+int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
{
- int ret = inflate(strm, flush);
- const char *err;
-
- switch (ret) {
- /* Out of memory is fatal. */
- case Z_MEM_ERROR:
- die("inflate: out of memory");
-
- /* Data corruption errors: we may want to recover from them (fsck) */
- case Z_NEED_DICT:
- err = "needs dictionary"; break;
- case Z_DATA_ERROR:
- err = "data stream error"; break;
- case Z_STREAM_ERROR:
- err = "stream consistency error"; break;
- default:
- err = "unknown error"; break;
-
- /* Z_BUF_ERROR: normal, needs more space in the output buffer */
- case Z_BUF_ERROR:
- case Z_OK:
- case Z_STREAM_END:
- return ret;
+ static const char letters[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789";
+ static const int num_letters = 62;
+ uint64_t value;
+ struct timeval tv;
+ char *template;
+ size_t len;
+ int fd, count;
+
+ len = strlen(pattern);
+
+ if (len < 6 + suffix_len) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (strncmp(&pattern[len - 6 - suffix_len], "XXXXXX", 6)) {
+ errno = EINVAL;
+ return -1;
}
- error("inflate: %s (%s)", err, strm->msg ? strm->msg : "no message");
- return ret;
-}
-int odb_mkstemp(char *template, size_t limit, const char *pattern)
-{
- int fd;
/*
- * we let the umask do its job, don't try to be more
- * restrictive except to remove write permission.
+ * Replace pattern's XXXXXX characters with randomness.
+ * Try TMP_MAX different filenames.
*/
- int mode = 0444;
- snprintf(template, limit, "%s/%s",
- get_object_directory(), pattern);
- fd = git_mkstemp_mode(template, mode);
- if (0 <= fd)
- return fd;
-
- /* slow path */
- /* some mkstemp implementations erase template on failure */
- snprintf(template, limit, "%s/%s",
- get_object_directory(), pattern);
- safe_create_leading_directories(template);
- return xmkstemp_mode(template, mode);
+ gettimeofday(&tv, NULL);
+ value = ((size_t)(tv.tv_usec << 16)) ^ tv.tv_sec ^ getpid();
+ template = &pattern[len - 6 - suffix_len];
+ for (count = 0; count < TMP_MAX; ++count) {
+ uint64_t v = value;
+ /* Fill in the random bits. */
+ template[0] = letters[v % num_letters]; v /= num_letters;
+ template[1] = letters[v % num_letters]; v /= num_letters;
+ template[2] = letters[v % num_letters]; v /= num_letters;
+ template[3] = letters[v % num_letters]; v /= num_letters;
+ template[4] = letters[v % num_letters]; v /= num_letters;
+ template[5] = letters[v % num_letters]; v /= num_letters;
+
+ fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, mode);
+ if (fd > 0)
+ return fd;
+ /*
+ * Fatal error (EPERM, ENOSPC etc).
+ * It doesn't make sense to loop.
+ */
+ if (errno != EEXIST)
+ break;
+ /*
+ * This is a random value. It is only necessary that
+ * the next TMP_MAX values generated by adding 7777 to
+ * VALUE are different with (module 2^32).
+ */
+ value += 7777;
+ }
+ /* We return the null string if we can't find a unique file name. */
+ pattern[0] = '\0';
+ return -1;
+}
+
+int git_mkstemp_mode(char *pattern, int mode)
+{
+ /* mkstemp is just mkstemps with no suffix */
+ return git_mkstemps_mode(pattern, 0, mode);
}
-int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1)
+int gitmkstemps(char *pattern, int suffix_len)
+{
+ return git_mkstemps_mode(pattern, suffix_len, 0600);
+}
+
+int xmkstemp_mode(char *template, int mode)
{
int fd;
+ char origtemplate[PATH_MAX];
+ strlcpy(origtemplate, template, sizeof(origtemplate));
- snprintf(name, namesz, "%s/pack/pack-%s.keep",
- get_object_directory(), sha1_to_hex(sha1));
- fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
- if (0 <= fd)
- return fd;
+ fd = git_mkstemp_mode(template, mode);
+ if (fd < 0) {
+ int saved_errno = errno;
+ const char *nonrelative_template;
+
+ if (!template[0])
+ template = origtemplate;
- /* slow path */
- safe_create_leading_directories(name);
- return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
+ nonrelative_template = make_nonrelative_path(template);
+ errno = saved_errno;
+ die_errno("Unable to create temporary file '%s'",
+ nonrelative_template);
+ }
+ return fd;
}
static int warn_if_unremovable(const char *op, const char *file, int rc)