diff options
Diffstat (limited to 'wrapper.c')
-rw-r--r-- | wrapper.c | 338 |
1 files changed, 281 insertions, 57 deletions
@@ -9,16 +9,24 @@ static void do_nothing(size_t size) static void (*try_to_free_routine)(size_t size) = do_nothing; -static void memory_limit_check(size_t size) +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) - die("attempting to allocate %"PRIuMAX" over limit %d", - (intmax_t)size, limit); + if (size > limit) { + if (gentle) { + error("attempting to allocate %"PRIuMAX" over limit %"PRIuMAX, + (uintmax_t)size, (uintmax_t)limit); + return -1; + } else + die("attempting to allocate %"PRIuMAX" over limit %"PRIuMAX, + (uintmax_t)size, (uintmax_t)limit); + } + return 0; } try_to_free_t set_try_to_free_routine(try_to_free_t routine) @@ -42,11 +50,12 @@ char *xstrdup(const char *str) return ret; } -void *xmalloc(size_t size) +static void *do_xmalloc(size_t size, int gentle) { void *ret; - memory_limit_check(size); + if (memory_limit_check(size, gentle)) + return NULL; ret = malloc(size); if (!ret && !size) ret = malloc(1); @@ -55,9 +64,16 @@ void *xmalloc(size_t size) ret = malloc(size); if (!ret && !size) ret = malloc(1); - if (!ret) - die("Out of memory, malloc failed (tried to allocate %lu bytes)", - (unsigned long)size); + if (!ret) { + if (!gentle) + die("Out of memory, malloc failed (tried to allocate %lu bytes)", + (unsigned long)size); + else { + error("Out of memory, malloc failed (tried to allocate %lu bytes)", + (unsigned long)size); + return NULL; + } + } } #ifdef XMALLOC_POISON memset(ret, 0xA5, size); @@ -65,16 +81,37 @@ void *xmalloc(size_t size) return ret; } -void *xmallocz(size_t size) +void *xmalloc(size_t size) +{ + return do_xmalloc(size, 0); +} + +static void *do_xmallocz(size_t size, int gentle) { void *ret; - if (unsigned_add_overflows(size, 1)) - die("Data too large to fit into virtual memory space."); - ret = xmalloc(size + 1); - ((char*)ret)[size] = 0; + if (unsigned_add_overflows(size, 1)) { + if (gentle) { + error("Data too large to fit into virtual memory space."); + return NULL; + } else + die("Data too large to fit into virtual memory space."); + } + ret = do_xmalloc(size + 1, gentle); + if (ret) + ((char*)ret)[size] = 0; return ret; } +void *xmallocz(size_t size) +{ + return do_xmallocz(size, 0); +} + +void *xmallocz_gently(size_t size) +{ + return do_xmallocz(size, 1); +} + /* * xmemdupz() allocates (len + 1) bytes of memory, duplicates "len" bytes of * "data" to the allocated memory, zero terminates the allocated memory, @@ -96,7 +133,7 @@ void *xrealloc(void *ptr, size_t size) { void *ret; - memory_limit_check(size); + memory_limit_check(size, 0); ret = realloc(ptr, size); if (!ret && !size) ret = realloc(ptr, 1); @@ -115,7 +152,10 @@ void *xcalloc(size_t nmemb, size_t size) { void *ret; - memory_limit_check(size * nmemb); + if (unsigned_mult_overflows(nmemb, size)) + die("data too large to fit into virtual memory space"); + + memory_limit_check(size * nmemb, 0); ret = calloc(nmemb, size); if (!ret && (!nmemb || !size)) ret = calloc(1, 1); @@ -135,8 +175,75 @@ 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. + */ +#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. */ -#define MAX_IO_SIZE (8*1024*1024) +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); + } +} + +static int handle_nonblock(int fd, short poll_events, int err) +{ + struct pollfd pfd; + + if (err != EAGAIN && err != EWOULDBLOCK) + return 0; + + pfd.fd = fd; + pfd.events = poll_events; + + /* + * no need to check for errors, here; + * a subsequent read/write will detect unrecoverable errors + */ + poll(&pfd, 1, -1); + return 1; +} /* * xread() is the same a read(), but it automatically restarts read() @@ -150,8 +257,12 @@ ssize_t xread(int fd, void *buf, size_t len) len = MAX_IO_SIZE; while (1) { nr = read(fd, buf, len); - if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) - continue; + if (nr < 0) { + if (errno == EINTR) + continue; + if (handle_nonblock(fd, POLLIN, errno)) + continue; + } return nr; } } @@ -168,6 +279,29 @@ ssize_t xwrite(int fd, const void *buf, size_t len) len = MAX_IO_SIZE; while (1) { nr = write(fd, buf, len); + if (nr < 0) { + if (errno == EINTR) + continue; + if (handle_nonblock(fd, POLLOUT, errno)) + continue; + } + + return nr; + } +} + +/* + * xpread() is the same as pread(), but it automatically restarts pread() + * operations with a recoverable error (EAGAIN and EINTR). xpread() DOES + * NOT GUARANTEE that "len" bytes is read even if the data is available. + */ +ssize_t xpread(int fd, void *buf, size_t len, off_t offset) +{ + ssize_t nr; + if (len > MAX_IO_SIZE) + len = MAX_IO_SIZE; + while (1) { + nr = pread(fd, buf, len, offset); if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) continue; return nr; @@ -214,6 +348,26 @@ ssize_t write_in_full(int fd, const void *buf, size_t count) return total; } +ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset) +{ + char *p = buf; + ssize_t total = 0; + + while (count > 0) { + ssize_t loaded = xpread(fd, p, count, offset); + if (loaded < 0) + return -1; + if (loaded == 0) + return total; + count -= loaded; + p += loaded; + total += loaded; + offset += loaded; + } + + return total; +} + int xdup(int fd) { int ret = dup(fd); @@ -222,6 +376,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); @@ -230,6 +405,19 @@ FILE *xfdopen(int fd, const char *mode) return stream; } +FILE *fopen_for_writing(const char *path) +{ + FILE *ret = fopen(path, "w"); + + if (!ret && errno == EPERM) { + if (!unlink(path)) + ret = fopen(path, "w"); + else + errno = EPERM; + } + return ret; +} + int xmkstemp(char *template) { int fd; @@ -269,23 +457,6 @@ int git_mkstemp(char *path, size_t len, const char *template) return mkstemp(path); } -/* git_mkstemps() - create tmp file with suffix honoring TMPDIR variable. */ -int git_mkstemps(char *path, size_t len, const char *template, int suffix_len) -{ - 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); -} - /* Adapted from libiberty's mkstemp.c. */ #undef TMP_MAX @@ -391,17 +562,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_errno("unable to %s %s", op, file); + 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)); @@ -419,7 +602,7 @@ int remove_or_warn(unsigned int mode, const char *file) void warn_on_inaccessible(const char *path) { - warning(_("unable to access '%s': %s"), path, strerror(errno)); + warning_errno(_("unable to access '%s'"), path); } static int access_error_is_ok(int err, unsigned flag) @@ -444,14 +627,55 @@ int access_or_die(const char *path, int mode, unsigned flag) return ret; } -struct passwd *xgetpwuid_self(void) +char *xgetcwd(void) { - struct passwd *pw; + struct strbuf sb = STRBUF_INIT; + if (strbuf_getcwd(&sb)) + die_errno(_("unable to get current working directory")); + return strbuf_detach(&sb, NULL); +} - errno = 0; - pw = getpwuid(getuid()); - if (!pw) - die(_("unable to look up current user in the passwd file: %s"), - errno ? strerror(errno) : _("no such user")); - return pw; +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; +} + +void write_file_buf(const char *path, const char *buf, size_t len) +{ + int fd = xopen(path, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (write_in_full(fd, buf, len) != len) + die_errno(_("could not write to %s"), path); + if (close(fd)) + die_errno(_("could not close %s"), path); +} + +void write_file(const char *path, const char *fmt, ...) +{ + va_list params; + struct strbuf sb = STRBUF_INIT; + + va_start(params, fmt); + strbuf_vaddf(&sb, fmt, params); + va_end(params); + + strbuf_complete_line(&sb); + + write_file_buf(path, sb.buf, sb.len); + strbuf_release(&sb); +} + +void sleep_millisec(int millisec) +{ + poll(NULL, 0, millisec); } |