summaryrefslogtreecommitdiff
path: root/wrapper.c
diff options
context:
space:
mode:
Diffstat (limited to 'wrapper.c')
-rw-r--r--wrapper.c192
1 files changed, 174 insertions, 18 deletions
diff --git a/wrapper.c b/wrapper.c
index 25074d71b6..6fcaa4dc62 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -11,19 +11,20 @@ static void (*try_to_free_routine)(size_t size) = do_nothing;
static int memory_limit_check(size_t size, int gentle)
{
- static int limit = -1;
- if (limit == -1) {
- const char *env = getenv("GIT_ALLOC_LIMIT");
- limit = env ? atoi(env) * 1024 : 0;
+ static size_t limit = 0;
+ if (!limit) {
+ limit = git_env_ulong("GIT_ALLOC_LIMIT", 0);
+ if (!limit)
+ limit = SIZE_MAX;
}
- if (limit && size > limit) {
+ if (size > limit) {
if (gentle) {
- error("attempting to allocate %"PRIuMAX" over limit %d",
- (intmax_t)size, limit);
+ error("attempting to allocate %"PRIuMAX" over limit %"PRIuMAX,
+ (uintmax_t)size, (uintmax_t)limit);
return -1;
} else
- die("attempting to allocate %"PRIuMAX" over limit %d",
- (intmax_t)size, limit);
+ die("attempting to allocate %"PRIuMAX" over limit %"PRIuMAX,
+ (uintmax_t)size, (uintmax_t)limit);
}
return 0;
}
@@ -171,8 +172,57 @@ void *xcalloc(size_t nmemb, size_t size)
* 64-bit is buggy, returning EINVAL if len >= INT_MAX; and even in
* the absence of bugs, large chunks can result in bad latencies when
* you decide to kill the process.
+ *
+ * We pick 8 MiB as our default, but if the platform defines SSIZE_MAX
+ * that is smaller than that, clip it to SSIZE_MAX, as a call to
+ * read(2) or write(2) larger than that is allowed to fail. As the last
+ * resort, we allow a port to pass via CFLAGS e.g. "-DMAX_IO_SIZE=value"
+ * to override this, if the definition of SSIZE_MAX given by the platform
+ * is broken.
*/
-#define MAX_IO_SIZE (8*1024*1024)
+#ifndef MAX_IO_SIZE
+# define MAX_IO_SIZE_DEFAULT (8*1024*1024)
+# if defined(SSIZE_MAX) && (SSIZE_MAX < MAX_IO_SIZE_DEFAULT)
+# define MAX_IO_SIZE SSIZE_MAX
+# else
+# define MAX_IO_SIZE MAX_IO_SIZE_DEFAULT
+# endif
+#endif
+
+/**
+ * xopen() is the same as open(), but it die()s if the open() fails.
+ */
+int xopen(const char *path, int oflag, ...)
+{
+ mode_t mode = 0;
+ va_list ap;
+
+ /*
+ * va_arg() will have undefined behavior if the specified type is not
+ * compatible with the argument type. Since integers are promoted to
+ * ints, we fetch the next argument as an int, and then cast it to a
+ * mode_t to avoid undefined behavior.
+ */
+ va_start(ap, oflag);
+ if (oflag & O_CREAT)
+ mode = va_arg(ap, int);
+ va_end(ap);
+
+ for (;;) {
+ int fd = open(path, oflag, mode);
+ if (fd >= 0)
+ return fd;
+ if (errno == EINTR)
+ continue;
+
+ if ((oflag & O_RDWR) == O_RDWR)
+ die_errno(_("could not open '%s' for reading and writing"), path);
+ else if ((oflag & O_WRONLY) == O_WRONLY)
+ die_errno(_("could not open '%s' for writing"), path);
+ else
+ die_errno(_("could not open '%s' for reading"), path);
+ }
+}
/*
* xread() is the same a read(), but it automatically restarts read()
@@ -296,6 +346,27 @@ int xdup(int fd)
return ret;
}
+/**
+ * xfopen() is the same as fopen(), but it die()s if the fopen() fails.
+ */
+FILE *xfopen(const char *path, const char *mode)
+{
+ for (;;) {
+ FILE *fp = fopen(path, mode);
+ if (fp)
+ return fp;
+ if (errno == EINTR)
+ continue;
+
+ if (*mode && mode[1] == '+')
+ die_errno(_("could not open '%s' for reading and writing"), path);
+ else if (*mode == 'w' || *mode == 'a')
+ die_errno(_("could not open '%s' for writing"), path);
+ else
+ die_errno(_("could not open '%s' for reading"), path);
+ }
+}
+
FILE *xfdopen(int fd, const char *mode)
{
FILE *stream = fdopen(fd, mode);
@@ -465,17 +536,29 @@ int xmkstemp_mode(char *template, int mode)
static int warn_if_unremovable(const char *op, const char *file, int rc)
{
- if (rc < 0) {
- int err = errno;
- if (ENOENT != err) {
- warning("unable to %s %s: %s",
- op, file, strerror(errno));
- errno = err;
- }
- }
+ int err;
+ if (!rc || errno == ENOENT)
+ return 0;
+ err = errno;
+ warning("unable to %s %s: %s", op, file, strerror(errno));
+ errno = err;
return rc;
}
+int unlink_or_msg(const char *file, struct strbuf *err)
+{
+ int rc = unlink(file);
+
+ assert(err);
+
+ if (!rc || errno == ENOENT)
+ return 0;
+
+ strbuf_addf(err, "unable to unlink %s: %s",
+ file, strerror(errno));
+ return -1;
+}
+
int unlink_or_warn(const char *file)
{
return warn_if_unremovable("unlink", file, unlink(file));
@@ -537,3 +620,76 @@ char *xgetcwd(void)
die_errno(_("unable to get current working directory"));
return strbuf_detach(&sb, NULL);
}
+
+int xsnprintf(char *dst, size_t max, const char *fmt, ...)
+{
+ va_list ap;
+ int len;
+
+ va_start(ap, fmt);
+ len = vsnprintf(dst, max, fmt, ap);
+ va_end(ap);
+
+ if (len < 0)
+ die("BUG: your snprintf is broken");
+ if (len >= max)
+ die("BUG: attempt to snprintf into too-small buffer");
+ return len;
+}
+
+static int write_file_v(const char *path, int fatal,
+ const char *fmt, va_list params)
+{
+ struct strbuf sb = STRBUF_INIT;
+ int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0) {
+ if (fatal)
+ die_errno(_("could not open %s for writing"), path);
+ return -1;
+ }
+ strbuf_vaddf(&sb, fmt, params);
+ strbuf_complete_line(&sb);
+ if (write_in_full(fd, sb.buf, sb.len) != sb.len) {
+ int err = errno;
+ close(fd);
+ strbuf_release(&sb);
+ errno = err;
+ if (fatal)
+ die_errno(_("could not write to %s"), path);
+ return -1;
+ }
+ strbuf_release(&sb);
+ if (close(fd)) {
+ if (fatal)
+ die_errno(_("could not close %s"), path);
+ return -1;
+ }
+ return 0;
+}
+
+int write_file(const char *path, const char *fmt, ...)
+{
+ int status;
+ va_list params;
+
+ va_start(params, fmt);
+ status = write_file_v(path, 1, fmt, params);
+ va_end(params);
+ return status;
+}
+
+int write_file_gently(const char *path, const char *fmt, ...)
+{
+ int status;
+ va_list params;
+
+ va_start(params, fmt);
+ status = write_file_v(path, 0, fmt, params);
+ va_end(params);
+ return status;
+}
+
+void sleep_millisec(int millisec)
+{
+ poll(NULL, 0, millisec);
+}