Merge branch 'jk/incore-lockfile-removal'

The long-standing rule that an in-core lockfile instance, once it
is used, must not be freed, has been lifted and the lockfile and
tempfile APIs have been updated to reduce the chance of programming
errors.

* jk/incore-lockfile-removal:
  stop leaking lock structs in some simple cases
  ref_lock: stop leaking lock_files
  lockfile: update lifetime requirements in documentation
  tempfile: auto-allocate tempfiles on heap
  tempfile: remove deactivated list entries
  tempfile: use list.h for linked list
  tempfile: release deactivated strbufs instead of resetting
  tempfile: robustify cleanup handler
  tempfile: factor out deactivation
  tempfile: factor out activation
  tempfile: replace die("BUG") with BUG()
  tempfile: handle NULL tempfile pointers gracefully
  tempfile: prefer is_tempfile_active to bare access
  lockfile: do not rollback lock on failed close
  tempfile: do not delete tempfile on failed close
  always check return value of close_tempfile
  verify_signed_buffer: prefer close_tempfile() to close()
  setup_temporary_shallow: move tempfile struct into function
  setup_temporary_shallow: avoid using inactive tempfile
  write_index_as_tree: cleanup tempfile on error
This commit is contained in:
Junio C Hamano 2017-09-19 10:47:53 +09:00
commit 89563ec379
18 changed files with 352 additions and 324 deletions

View file

@ -47,7 +47,7 @@ static struct argv_array prune = ARGV_ARRAY_INIT;
static struct argv_array prune_worktrees = ARGV_ARRAY_INIT;
static struct argv_array rerere = ARGV_ARRAY_INIT;
static struct tempfile pidfile;
static struct tempfile *pidfile;
static struct lock_file log_lock;
static struct string_list pack_garbage = STRING_LIST_INIT_DUP;
@ -78,7 +78,7 @@ static void process_log_file(void)
*/
int saved_errno = errno;
fprintf(stderr, _("Failed to fstat %s: %s"),
get_tempfile_path(&log_lock.tempfile),
get_tempfile_path(log_lock.tempfile),
strerror(saved_errno));
fflush(stderr);
commit_lock_file(&log_lock);
@ -242,7 +242,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
int fd;
char *pidfile_path;
if (is_tempfile_active(&pidfile))
if (is_tempfile_active(pidfile))
/* already locked */
return NULL;
@ -293,7 +293,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
write_in_full(fd, sb.buf, sb.len);
strbuf_release(&sb);
commit_lock_file(&lock);
register_tempfile(&pidfile, pidfile_path);
pidfile = register_tempfile(pidfile_path);
free(pidfile_path);
return NULL;
}

View file

@ -367,8 +367,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
die_if_unmerged_cache(reset_type);
if (reset_type != SOFT) {
struct lock_file *lock = xcalloc(1, sizeof(*lock));
hold_locked_index(lock, LOCK_DIE_ON_ERROR);
struct lock_file lock = LOCK_INIT;
hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
if (reset_type == MIXED) {
int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
if (read_from_tree(&pathspec, &oid, intent_to_add))
@ -384,7 +384,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
die(_("Could not reset index file to revision '%s'."), rev);
}
if (write_locked_index(&the_index, lock, COMMIT_LOCK))
if (write_locked_index(&the_index, &lock, COMMIT_LOCK))
die(_("Could not write new index file."));
}

View file

@ -915,7 +915,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
struct refresh_params refresh_args = {0, &has_errors};
int lock_error = 0;
int split_index = -1;
struct lock_file *lock_file;
struct lock_file lock_file = LOCK_INIT;
struct parse_opt_ctx_t ctx;
strbuf_getline_fn getline_fn;
int parseopt_state = PARSE_OPT_UNKNOWN;
@ -1014,11 +1014,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
git_config(git_default_config, NULL);
/* We can't free this memory, it becomes part of a linked list parsed atexit() */
lock_file = xcalloc(1, sizeof(struct lock_file));
/* we will diagnose later if it turns out that we need to update it */
newfd = hold_locked_index(lock_file, 0);
newfd = hold_locked_index(&lock_file, 0);
if (newfd < 0)
lock_error = errno;
@ -1153,11 +1150,11 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
exit(128);
unable_to_lock_die(get_index_file(), lock_error);
}
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die("Unable to write new index file");
}
rollback_lock_file(lock_file);
rollback_lock_file(&lock_file);
return has_errors ? 1 : 0;
}

View file

@ -603,19 +603,16 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat
int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, const char *index_path, int flags, const char *prefix)
{
int entries, was_valid, newfd;
struct lock_file *lock_file;
struct lock_file lock_file = LOCK_INIT;
int ret = 0;
/*
* We can't free this memory, it becomes part of a linked list
* parsed atexit()
*/
lock_file = xcalloc(1, sizeof(struct lock_file));
newfd = hold_lock_file_for_update(lock_file, index_path, LOCK_DIE_ON_ERROR);
newfd = hold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR);
entries = read_index_from(index_state, index_path);
if (entries < 0)
return WRITE_TREE_UNREADABLE_INDEX;
if (entries < 0) {
ret = WRITE_TREE_UNREADABLE_INDEX;
goto out;
}
if (flags & WRITE_TREE_IGNORE_CACHE_TREE)
cache_tree_free(&index_state->cache_tree);
@ -624,10 +621,12 @@ int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, co
was_valid = cache_tree_fully_valid(index_state->cache_tree);
if (!was_valid) {
if (cache_tree_update(index_state, flags) < 0)
return WRITE_TREE_UNMERGED_INDEX;
if (cache_tree_update(index_state, flags) < 0) {
ret = WRITE_TREE_UNMERGED_INDEX;
goto out;
}
if (0 <= newfd) {
if (!write_locked_index(index_state, lock_file, COMMIT_LOCK))
if (!write_locked_index(index_state, &lock_file, COMMIT_LOCK))
newfd = -1;
}
/* Not being able to write is fine -- we are only interested
@ -641,17 +640,19 @@ int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, co
if (prefix) {
struct cache_tree *subtree;
subtree = cache_tree_find(index_state->cache_tree, prefix);
if (!subtree)
return WRITE_TREE_PREFIX_ERROR;
if (!subtree) {
ret = WRITE_TREE_PREFIX_ERROR;
goto out;
}
hashcpy(sha1, subtree->oid.hash);
}
else
hashcpy(sha1, index_state->cache_tree->oid.hash);
out:
if (0 <= newfd)
rollback_lock_file(lock_file);
return 0;
rollback_lock_file(&lock_file);
return ret;
}
int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)

View file

@ -2450,7 +2450,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
{
int fd = -1, in_fd = -1;
int ret;
static struct lock_file lock;
struct lock_file lock = LOCK_INIT;
char *filename_buf = NULL;
char *contents = NULL;
size_t contents_sz;

View file

@ -5,8 +5,6 @@
#include "unix-socket.h"
#include "parse-options.h"
static struct tempfile socket_file;
struct credential_cache_entry {
struct credential item;
timestamp_t expiration;
@ -260,6 +258,7 @@ static void init_socket_directory(const char *path)
int cmd_main(int argc, const char **argv)
{
struct tempfile *socket_file;
const char *socket_path;
int ignore_sighup = 0;
static const char *usage[] = {
@ -285,7 +284,7 @@ int cmd_main(int argc, const char **argv)
die("socket directory must be an absolute path");
init_socket_directory(socket_path);
register_tempfile(&socket_file, socket_path);
socket_file = register_tempfile(socket_path);
if (ignore_sighup)
signal(SIGHUP, SIG_IGN);

15
diff.c
View file

@ -459,7 +459,7 @@ static struct diff_tempfile {
* If this diff_tempfile instance refers to a temporary file,
* this tempfile object is used to manage its lifetime.
*/
struct tempfile tempfile;
struct tempfile *tempfile;
} diff_temp[2];
struct emit_callback {
@ -1414,7 +1414,7 @@ static void remove_tempfile(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(diff_temp); i++) {
if (is_tempfile_active(&diff_temp[i].tempfile))
if (is_tempfile_active(diff_temp[i].tempfile))
delete_tempfile(&diff_temp[i].tempfile);
diff_temp[i].name = NULL;
}
@ -3720,7 +3720,6 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
const struct object_id *oid,
int mode)
{
int fd;
struct strbuf buf = STRBUF_INIT;
struct strbuf template = STRBUF_INIT;
char *path_dup = xstrdup(path);
@ -3730,18 +3729,18 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
strbuf_addstr(&template, "XXXXXX_");
strbuf_addstr(&template, base);
fd = mks_tempfile_ts(&temp->tempfile, template.buf, strlen(base) + 1);
if (fd < 0)
temp->tempfile = mks_tempfile_ts(template.buf, strlen(base) + 1);
if (!temp->tempfile)
die_errno("unable to create temp-file");
if (convert_to_working_tree(path,
(const char *)blob, (size_t)size, &buf)) {
blob = buf.buf;
size = buf.len;
}
if (write_in_full(fd, blob, size) != size)
if (write_in_full(temp->tempfile->fd, blob, size) != size ||
close_tempfile_gently(temp->tempfile))
die_errno("unable to write temp-file");
close_tempfile(&temp->tempfile);
temp->name = get_tempfile_path(&temp->tempfile);
temp->name = get_tempfile_path(temp->tempfile);
oid_to_hex_r(temp->hex, oid);
xsnprintf(temp->mode, sizeof(temp->mode), "%06o", mode);
strbuf_release(&buf);

View file

@ -202,26 +202,26 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
struct strbuf *gpg_output, struct strbuf *gpg_status)
{
struct child_process gpg = CHILD_PROCESS_INIT;
static struct tempfile temp;
int fd, ret;
struct tempfile *temp;
int ret;
struct strbuf buf = STRBUF_INIT;
fd = mks_tempfile_t(&temp, ".git_vtag_tmpXXXXXX");
if (fd < 0)
temp = mks_tempfile_t(".git_vtag_tmpXXXXXX");
if (!temp)
return error_errno(_("could not create temporary file"));
if (write_in_full(fd, signature, signature_size) < 0) {
if (write_in_full(temp->fd, signature, signature_size) < 0 ||
close_tempfile_gently(temp) < 0) {
error_errno(_("failed writing detached signature to '%s'"),
temp.filename.buf);
temp->filename.buf);
delete_tempfile(&temp);
return -1;
}
close(fd);
argv_array_pushl(&gpg.args,
gpg_program,
"--status-fd=1",
"--keyid-format=long",
"--verify", temp.filename.buf, "-",
"--verify", temp->filename.buf, "-",
NULL);
if (!gpg_status)

38
list.h
View file

@ -163,4 +163,42 @@ static inline void list_replace_init(struct list_head *old,
INIT_LIST_HEAD(old);
}
/*
* This is exactly the same as a normal list_head, except that it can be
* declared volatile (e.g., if you have a list that may be accessed from signal
* handlers).
*/
struct volatile_list_head {
volatile struct volatile_list_head *next, *prev;
};
#define VOLATILE_LIST_HEAD(name) \
volatile struct volatile_list_head name = { &(name), &(name) }
static inline void __volatile_list_del(volatile struct volatile_list_head *prev,
volatile struct volatile_list_head *next)
{
next->prev = prev;
prev->next = next;
}
static inline void volatile_list_del(volatile struct volatile_list_head *elem)
{
__volatile_list_del(elem->prev, elem->next);
}
static inline int volatile_list_empty(volatile struct volatile_list_head *head)
{
return head == head->next;
}
static inline void volatile_list_add(volatile struct volatile_list_head *newp,
volatile struct volatile_list_head *head)
{
head->next->prev = newp;
newp->next = head->next;
newp->prev = head;
head->next = newp;
}
#endif /* LIST_H */

View file

@ -72,7 +72,6 @@ static void resolve_symlink(struct strbuf *path)
/* Make sure errno contains a meaningful value on error */
static int lock_file(struct lock_file *lk, const char *path, int flags)
{
int fd;
struct strbuf filename = STRBUF_INIT;
strbuf_addstr(&filename, path);
@ -80,9 +79,9 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
resolve_symlink(&filename);
strbuf_addstr(&filename, LOCK_SUFFIX);
fd = create_tempfile(&lk->tempfile, filename.buf);
lk->tempfile = create_tempfile(filename.buf);
strbuf_release(&filename);
return fd;
return lk->tempfile ? lk->tempfile->fd : -1;
}
/*
@ -191,7 +190,7 @@ char *get_locked_file_path(struct lock_file *lk)
{
struct strbuf ret = STRBUF_INIT;
strbuf_addstr(&ret, get_tempfile_path(&lk->tempfile));
strbuf_addstr(&ret, get_tempfile_path(lk->tempfile));
if (ret.len <= LOCK_SUFFIX_LEN ||
strcmp(ret.buf + ret.len - LOCK_SUFFIX_LEN, LOCK_SUFFIX))
die("BUG: get_locked_file_path() called for malformed lock object");

View file

@ -37,12 +37,12 @@
*
* The caller:
*
* * Allocates a `struct lock_file` either as a static variable or on
* the heap, initialized to zeros. Once you use the structure to
* call the `hold_lock_file_for_*()` family of functions, it belongs
* to the lockfile subsystem and its storage must remain valid
* throughout the life of the program (i.e. you cannot use an
* on-stack variable to hold this structure).
* * Allocates a `struct lock_file` with whatever storage duration you
* desire. The struct does not have to be initialized before being
* used, but it is good practice to do so using by setting it to
* all-zeros (or using the LOCK_INIT macro). This puts the object in a
* consistent state that allows you to call rollback_lock_file() even
* if the lock was never taken (in which case it is a noop).
*
* * Attempts to create a lockfile by calling `hold_lock_file_for_update()`.
*
@ -69,14 +69,12 @@
* `rollback_lock_file()`.
*
* * Close the file descriptor without removing or renaming the
* lockfile by calling `close_lock_file()`, and later call
* lockfile by calling `close_lock_file_gently()`, and later call
* `commit_lock_file()`, `commit_lock_file_to()`,
* `rollback_lock_file()`, or `reopen_lock_file()`.
*
* Even after the lockfile is committed or rolled back, the
* `lock_file` object must not be freed or altered by the caller.
* However, it may be reused; just pass it to another call of
* `hold_lock_file_for_update()`.
* After the lockfile is committed or rolled back, the `lock_file`
* object can be discarded or reused.
*
* If the program exits before `commit_lock_file()`,
* `commit_lock_file_to()`, or `rollback_lock_file()` is called, the
@ -85,7 +83,7 @@
*
* If you need to close the file descriptor you obtained from a
* `hold_lock_file_for_*()` function yourself, do so by calling
* `close_lock_file()`. See "tempfile.h" for more information.
* `close_lock_file_gently()`. See "tempfile.h" for more information.
*
*
* Under the covers, a lockfile is just a tempfile with a few helper
@ -104,16 +102,18 @@
*
* Similarly, `commit_lock_file`, `commit_lock_file_to`, and
* `close_lock_file` return 0 on success. On failure they set `errno`
* appropriately, do their best to roll back the lockfile, and return
* -1.
* appropriately and return -1. The `commit` variants (but not `close`)
* do their best to delete the temporary file before returning.
*/
#include "tempfile.h"
struct lock_file {
struct tempfile tempfile;
struct tempfile *tempfile;
};
#define LOCK_INIT { NULL }
/* String appended to a filename to derive the lockfile name: */
#define LOCK_SUFFIX ".lock"
#define LOCK_SUFFIX_LEN 5
@ -180,7 +180,7 @@ static inline int hold_lock_file_for_update(
*/
static inline int is_lock_file_locked(struct lock_file *lk)
{
return is_tempfile_active(&lk->tempfile);
return is_tempfile_active(lk->tempfile);
}
/*
@ -202,12 +202,13 @@ extern NORETURN void unable_to_lock_die(const char *path, int err);
/*
* Associate a stdio stream with the lockfile (which must still be
* open). Return `NULL` (*without* rolling back the lockfile) on
* error. The stream is closed automatically when `close_lock_file()`
* is called or when the file is committed or rolled back.
* error. The stream is closed automatically when
* `close_lock_file_gently()` is called or when the file is committed or
* rolled back.
*/
static inline FILE *fdopen_lock_file(struct lock_file *lk, const char *mode)
{
return fdopen_tempfile(&lk->tempfile, mode);
return fdopen_tempfile(lk->tempfile, mode);
}
/*
@ -216,17 +217,17 @@ static inline FILE *fdopen_lock_file(struct lock_file *lk, const char *mode)
*/
static inline const char *get_lock_file_path(struct lock_file *lk)
{
return get_tempfile_path(&lk->tempfile);
return get_tempfile_path(lk->tempfile);
}
static inline int get_lock_file_fd(struct lock_file *lk)
{
return get_tempfile_fd(&lk->tempfile);
return get_tempfile_fd(lk->tempfile);
}
static inline FILE *get_lock_file_fp(struct lock_file *lk)
{
return get_tempfile_fp(&lk->tempfile);
return get_tempfile_fp(lk->tempfile);
}
/*
@ -241,22 +242,21 @@ extern char *get_locked_file_path(struct lock_file *lk);
* lockfile over the file being locked. Return 0 upon success. On
* failure to `close(2)`, return a negative value and roll back the
* lock file. Usually `commit_lock_file()`, `commit_lock_file_to()`,
* or `rollback_lock_file()` should eventually be called if
* `close_lock_file()` succeeds.
* or `rollback_lock_file()` should eventually be called.
*/
static inline int close_lock_file(struct lock_file *lk)
static inline int close_lock_file_gently(struct lock_file *lk)
{
return close_tempfile(&lk->tempfile);
return close_tempfile_gently(lk->tempfile);
}
/*
* Re-open a lockfile that has been closed using `close_lock_file()`
* Re-open a lockfile that has been closed using `close_lock_file_gently()`
* but not yet committed or rolled back. This can be used to implement
* a sequence of operations like the following:
*
* * Lock file.
*
* * Write new contents to lockfile, then `close_lock_file()` to
* * Write new contents to lockfile, then `close_lock_file_gently()` to
* cause the contents to be written to disk.
*
* * Pass the name of the lockfile to another program to allow it (and
@ -270,7 +270,7 @@ static inline int close_lock_file(struct lock_file *lk)
*/
static inline int reopen_lock_file(struct lock_file *lk)
{
return reopen_tempfile(&lk->tempfile);
return reopen_tempfile(lk->tempfile);
}
/*

View file

@ -2309,8 +2309,11 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
if (ce_flush(&c, newfd, istate->sha1))
return -1;
if (close_tempfile(tempfile))
return error(_("could not close '%s'"), tempfile->filename.buf);
if (close_tempfile_gently(tempfile)) {
error(_("could not close '%s'"), tempfile->filename.buf);
delete_tempfile(&tempfile);
return -1;
}
if (stat(tempfile->filename.buf, &st))
return -1;
istate->timestamp.sec = (unsigned int)st.st_mtime;
@ -2334,7 +2337,7 @@ static int commit_locked_index(struct lock_file *lk)
static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
unsigned flags)
{
int ret = do_write_index(istate, &lock->tempfile, 0);
int ret = do_write_index(istate, lock->tempfile, 0);
if (ret)
return ret;
assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) !=
@ -2342,7 +2345,7 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l
if (flags & COMMIT_LOCK)
return commit_locked_index(lock);
else if (flags & CLOSE_LOCK)
return close_lock_file(lock);
return close_lock_file_gently(lock);
else
return ret;
}
@ -2417,34 +2420,33 @@ static int clean_shared_index_files(const char *current_hex)
return 0;
}
static struct tempfile temporary_sharedindex;
static int write_shared_index(struct index_state *istate,
struct lock_file *lock, unsigned flags)
{
struct tempfile *temp;
struct split_index *si = istate->split_index;
int fd, ret;
int ret;
fd = mks_tempfile(&temporary_sharedindex, git_path("sharedindex_XXXXXX"));
if (fd < 0) {
temp = mks_tempfile(git_path("sharedindex_XXXXXX"));
if (!temp) {
hashclr(si->base_sha1);
return do_write_locked_index(istate, lock, flags);
}
move_cache_to_base_index(istate);
ret = do_write_index(si->base, &temporary_sharedindex, 1);
ret = do_write_index(si->base, temp, 1);
if (ret) {
delete_tempfile(&temporary_sharedindex);
delete_tempfile(&temp);
return ret;
}
ret = adjust_shared_perm(get_tempfile_path(&temporary_sharedindex));
ret = adjust_shared_perm(get_tempfile_path(temp));
if (ret) {
int save_errno = errno;
error("cannot fix permission bits on %s", get_tempfile_path(&temporary_sharedindex));
delete_tempfile(&temporary_sharedindex);
error("cannot fix permission bits on %s", get_tempfile_path(temp));
delete_tempfile(&temp);
errno = save_errno;
return ret;
}
ret = rename_tempfile(&temporary_sharedindex,
ret = rename_tempfile(&temp,
git_path("sharedindex.%s", sha1_to_hex(si->base->sha1)));
if (!ret) {
hashcpy(si->base_sha1, si->base->sha1);

View file

@ -12,7 +12,7 @@
struct ref_lock {
char *ref_name;
struct lock_file *lk;
struct lock_file lk;
struct object_id old_oid;
};
@ -409,9 +409,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,
static void unlock_ref(struct ref_lock *lock)
{
/* Do not free lock->lk -- atexit() still looks at them */
if (lock->lk)
rollback_lock_file(lock->lk);
rollback_lock_file(&lock->lk);
free(lock->ref_name);
free(lock);
}
@ -525,11 +523,8 @@ static int lock_raw_ref(struct files_ref_store *refs,
goto error_return;
}
if (!lock->lk)
lock->lk = xcalloc(1, sizeof(struct lock_file));
if (hold_lock_file_for_update_timeout(
lock->lk, ref_file.buf, LOCK_NO_DEREF,
&lock->lk, ref_file.buf, LOCK_NO_DEREF,
get_files_ref_lock_timeout_ms()) < 0) {
if (errno == ENOENT && --attempts_remaining > 0) {
/*
@ -940,11 +935,9 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
goto error_return;
}
lock->lk = xcalloc(1, sizeof(struct lock_file));
lock->ref_name = xstrdup(refname);
if (raceproof_create_file(ref_file.buf, create_reflock, lock->lk)) {
if (raceproof_create_file(ref_file.buf, create_reflock, &lock->lk)) {
last_errno = errno;
unable_to_lock_message(ref_file.buf, errno, err);
goto error_return;
@ -1393,16 +1386,16 @@ static int files_rename_ref(struct ref_store *ref_store,
return ret;
}
static int close_ref(struct ref_lock *lock)
static int close_ref_gently(struct ref_lock *lock)
{
if (close_lock_file(lock->lk))
if (close_lock_file_gently(&lock->lk))
return -1;
return 0;
}
static int commit_ref(struct ref_lock *lock)
{
char *path = get_locked_file_path(lock->lk);
char *path = get_locked_file_path(&lock->lk);
struct stat st;
if (!lstat(path, &st) && S_ISDIR(st.st_mode)) {
@ -1426,7 +1419,7 @@ static int commit_ref(struct ref_lock *lock)
free(path);
}
if (commit_lock_file(lock->lk))
if (commit_lock_file(&lock->lk))
return -1;
return 0;
}
@ -1618,12 +1611,12 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
unlock_ref(lock);
return -1;
}
fd = get_lock_file_fd(lock->lk);
fd = get_lock_file_fd(&lock->lk);
if (write_in_full(fd, oid_to_hex(oid), GIT_SHA1_HEXSZ) != GIT_SHA1_HEXSZ ||
write_in_full(fd, &term, 1) != 1 ||
close_ref(lock) < 0) {
close_ref_gently(lock) < 0) {
strbuf_addf(err,
"couldn't write '%s'", get_lock_file_path(lock->lk));
"couldn't write '%s'", get_lock_file_path(&lock->lk));
unlock_ref(lock);
return -1;
}
@ -1700,7 +1693,7 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)
{
int ret = -1;
#ifndef NO_SYMLINK_HEAD
char *ref_path = get_locked_file_path(lock->lk);
char *ref_path = get_locked_file_path(&lock->lk);
unlink(ref_path);
ret = symlink(target, ref_path);
free(ref_path);
@ -1736,14 +1729,14 @@ static int create_symref_locked(struct files_ref_store *refs,
return 0;
}
if (!fdopen_lock_file(lock->lk, "w"))
if (!fdopen_lock_file(&lock->lk, "w"))
return error("unable to fdopen %s: %s",
lock->lk->tempfile.filename.buf, strerror(errno));
lock->lk.tempfile->filename.buf, strerror(errno));
update_symref_reflog(refs, lock, refname, target, logmsg);
/* no error check; commit_ref will check ferror */
fprintf(lock->lk->tempfile.fp, "ref: %s\n", target);
fprintf(lock->lk.tempfile->fp, "ref: %s\n", target);
if (commit_ref(lock) < 0)
return error("unable to write symref for %s: %s", refname,
strerror(errno));
@ -2425,7 +2418,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
* the lockfile is still open. Close it to
* free up the file descriptor:
*/
if (close_ref(lock)) {
if (close_ref_gently(lock)) {
strbuf_addf(err, "couldn't close '%s.lock'",
update->refname);
ret = TRANSACTION_GENERIC_ERROR;
@ -2905,16 +2898,17 @@ static int files_reflog_expire(struct ref_store *ref_store,
!(type & REF_ISSYMREF) &&
!is_null_oid(&cb.last_kept_oid);
if (close_lock_file(&reflog_lock)) {
if (close_lock_file_gently(&reflog_lock)) {
status |= error("couldn't write %s: %s", log_file,
strerror(errno));
rollback_lock_file(&reflog_lock);
} else if (update &&
(write_in_full(get_lock_file_fd(lock->lk),
(write_in_full(get_lock_file_fd(&lock->lk),
oid_to_hex(&cb.last_kept_oid), GIT_SHA1_HEXSZ) != GIT_SHA1_HEXSZ ||
write_str_in_full(get_lock_file_fd(lock->lk), "\n") != 1 ||
close_ref(lock) < 0)) {
write_str_in_full(get_lock_file_fd(&lock->lk), "\n") != 1 ||
close_ref_gently(lock) < 0)) {
status |= error("couldn't write %s",
get_lock_file_path(lock->lk));
get_lock_file_path(&lock->lk));
rollback_lock_file(&reflog_lock);
} else if (commit_lock_file(&reflog_lock)) {
status |= error("unable to write reflog '%s' (%s)",

View file

@ -75,7 +75,7 @@ struct packed_ref_store {
* "packed-refs" file. Note that this (and thus the enclosing
* `packed_ref_store`) must not be freed.
*/
struct tempfile tempfile;
struct tempfile *tempfile;
};
struct ref_store *packed_ref_store_create(const char *path,
@ -545,8 +545,9 @@ int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err)
return -1;
}
if (close_lock_file(&refs->lock)) {
if (close_lock_file_gently(&refs->lock)) {
strbuf_addf(err, "unable to close %s: %s", refs->path, strerror(errno));
rollback_lock_file(&refs->lock);
return -1;
}
@ -627,7 +628,8 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
*/
packed_refs_path = get_locked_file_path(&refs->lock);
strbuf_addf(&sb, "%s.new", packed_refs_path);
if (create_tempfile(&refs->tempfile, sb.buf) < 0) {
refs->tempfile = create_tempfile(sb.buf);
if (!refs->tempfile) {
strbuf_addf(err, "unable to create file %s: %s",
sb.buf, strerror(errno));
strbuf_release(&sb);
@ -635,7 +637,7 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
}
strbuf_release(&sb);
out = fdopen_tempfile(&refs->tempfile, "w");
out = fdopen_tempfile(refs->tempfile, "w");
if (!out) {
strbuf_addf(err, "unable to fdopen packed-refs tempfile: %s",
strerror(errno));
@ -644,7 +646,7 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
if (fprintf(out, "%s", PACKED_REFS_HEADER) < 0) {
strbuf_addf(err, "error writing to %s: %s",
get_tempfile_path(&refs->tempfile), strerror(errno));
get_tempfile_path(refs->tempfile), strerror(errno));
goto error;
}
@ -656,7 +658,7 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
if (write_packed_entry(out, iter->refname, iter->oid->hash,
peel_error ? NULL : peeled.hash)) {
strbuf_addf(err, "error writing to %s: %s",
get_tempfile_path(&refs->tempfile),
get_tempfile_path(refs->tempfile),
strerror(errno));
ref_iterator_abort(iter);
goto error;

View file

@ -286,28 +286,26 @@ int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
return write_shallow_commits_1(out, use_pack_protocol, extra, 0);
}
static struct tempfile temporary_shallow;
const char *setup_temporary_shallow(const struct oid_array *extra)
{
struct tempfile *temp;
struct strbuf sb = STRBUF_INIT;
int fd;
if (write_shallow_commits(&sb, 0, extra)) {
fd = xmks_tempfile(&temporary_shallow, git_path("shallow_XXXXXX"));
temp = xmks_tempfile(git_path("shallow_XXXXXX"));
if (write_in_full(fd, sb.buf, sb.len) != sb.len)
if (write_in_full(temp->fd, sb.buf, sb.len) != sb.len ||
close_tempfile_gently(temp) < 0)
die_errno("failed to write to %s",
get_tempfile_path(&temporary_shallow));
close_tempfile(&temporary_shallow);
get_tempfile_path(temp));
strbuf_release(&sb);
return get_tempfile_path(&temporary_shallow);
return get_tempfile_path(temp);
}
/*
* is_repository_shallow() sees empty string as "no shallow
* file".
*/
return get_tempfile_path(&temporary_shallow);
return "";
}
void setup_alternate_shallow(struct lock_file *shallow_lock,

View file

@ -30,21 +30,19 @@
* `fdopen_tempfile()` has been called on the object
* - `owner` holds the PID of the process that created the file
*
* - Active, file closed (after successful `close_tempfile()`). Same
* - Active, file closed (after `close_tempfile_gently()`). Same
* as the previous state, except that the temporary file is closed,
* `fd` is -1, and `fp` is `NULL`.
*
* - Inactive (after `delete_tempfile()`, `rename_tempfile()`, a
* failed attempt to create a temporary file, or a failed
* `close_tempfile()`). In this state:
* - Inactive (after `delete_tempfile()`, `rename_tempfile()`, or a
* failed attempt to create a temporary file). In this state:
*
* - `active` is unset
* - `filename` is empty (usually, though there are transitory
* states in which this condition doesn't hold). Client code should
* *not* rely on the filename being empty in this state.
* - `fd` is -1 and `fp` is `NULL`
* - the object is left registered in the `tempfile_list`, and
* `on_list` is set.
* - the object is removed from `tempfile_list` (but could be used again)
*
* A temporary file is owned by the process that created it. The
* `tempfile` has an `owner` field that records the owner's PID. This
@ -56,20 +54,28 @@
#include "tempfile.h"
#include "sigchain.h"
static struct tempfile *volatile tempfile_list;
static VOLATILE_LIST_HEAD(tempfile_list);
static void remove_tempfiles(int skip_fclose)
static void remove_tempfiles(int in_signal_handler)
{
pid_t me = getpid();
volatile struct volatile_list_head *pos;
while (tempfile_list) {
if (tempfile_list->owner == me) {
/* fclose() is not safe to call in a signal handler */
if (skip_fclose)
tempfile_list->fp = NULL;
delete_tempfile(tempfile_list);
}
tempfile_list = tempfile_list->next;
list_for_each(pos, &tempfile_list) {
struct tempfile *p = list_entry(pos, struct tempfile, list);
if (!is_tempfile_active(p) || p->owner != me)
continue;
if (p->fd >= 0)
close(p->fd);
if (in_signal_handler)
unlink(p->filename.buf);
else
unlink_or_warn(p->filename.buf);
p->active = 0;
}
}
@ -85,39 +91,48 @@ static void remove_tempfiles_on_signal(int signo)
raise(signo);
}
/*
* Initialize *tempfile if necessary and add it to tempfile_list.
*/
static void prepare_tempfile_object(struct tempfile *tempfile)
static struct tempfile *new_tempfile(void)
{
if (!tempfile_list) {
/* One-time initialization */
struct tempfile *tempfile = xmalloc(sizeof(*tempfile));
tempfile->fd = -1;
tempfile->fp = NULL;
tempfile->active = 0;
tempfile->owner = 0;
INIT_LIST_HEAD(&tempfile->list);
strbuf_init(&tempfile->filename, 0);
return tempfile;
}
static void activate_tempfile(struct tempfile *tempfile)
{
static int initialized;
if (is_tempfile_active(tempfile))
BUG("activate_tempfile called for active object");
if (!initialized) {
sigchain_push_common(remove_tempfiles_on_signal);
atexit(remove_tempfiles_on_exit);
initialized = 1;
}
if (tempfile->active)
die("BUG: prepare_tempfile_object called for active object");
if (!tempfile->on_list) {
/* Initialize *tempfile and add it to tempfile_list: */
tempfile->fd = -1;
tempfile->fp = NULL;
tempfile->active = 0;
tempfile->owner = 0;
strbuf_init(&tempfile->filename, 0);
tempfile->next = tempfile_list;
tempfile_list = tempfile;
tempfile->on_list = 1;
} else if (tempfile->filename.len) {
/* This shouldn't happen, but better safe than sorry. */
die("BUG: prepare_tempfile_object called for improperly-reset object");
}
volatile_list_add(&tempfile->list, &tempfile_list);
tempfile->owner = getpid();
tempfile->active = 1;
}
static void deactivate_tempfile(struct tempfile *tempfile)
{
tempfile->active = 0;
strbuf_release(&tempfile->filename);
volatile_list_del(&tempfile->list);
free(tempfile);
}
/* Make sure errno contains a meaningful value on error */
int create_tempfile(struct tempfile *tempfile, const char *path)
struct tempfile *create_tempfile(const char *path)
{
prepare_tempfile_object(tempfile);
struct tempfile *tempfile = new_tempfile();
strbuf_add_absolute_path(&tempfile->filename, path);
tempfile->fd = open(tempfile->filename.buf,
@ -127,52 +142,48 @@ int create_tempfile(struct tempfile *tempfile, const char *path)
tempfile->fd = open(tempfile->filename.buf,
O_RDWR | O_CREAT | O_EXCL, 0666);
if (tempfile->fd < 0) {
strbuf_reset(&tempfile->filename);
return -1;
deactivate_tempfile(tempfile);
return NULL;
}
tempfile->owner = getpid();
tempfile->active = 1;
activate_tempfile(tempfile);
if (adjust_shared_perm(tempfile->filename.buf)) {
int save_errno = errno;
error("cannot fix permission bits on %s", tempfile->filename.buf);
delete_tempfile(tempfile);
delete_tempfile(&tempfile);
errno = save_errno;
return -1;
return NULL;
}
return tempfile->fd;
return tempfile;
}
void register_tempfile(struct tempfile *tempfile, const char *path)
struct tempfile *register_tempfile(const char *path)
{
prepare_tempfile_object(tempfile);
struct tempfile *tempfile = new_tempfile();
strbuf_add_absolute_path(&tempfile->filename, path);
tempfile->owner = getpid();
tempfile->active = 1;
activate_tempfile(tempfile);
return tempfile;
}
int mks_tempfile_sm(struct tempfile *tempfile,
const char *template, int suffixlen, int mode)
struct tempfile *mks_tempfile_sm(const char *template, int suffixlen, int mode)
{
prepare_tempfile_object(tempfile);
struct tempfile *tempfile = new_tempfile();
strbuf_add_absolute_path(&tempfile->filename, template);
tempfile->fd = git_mkstemps_mode(tempfile->filename.buf, suffixlen, mode);
if (tempfile->fd < 0) {
strbuf_reset(&tempfile->filename);
return -1;
deactivate_tempfile(tempfile);
return NULL;
}
tempfile->owner = getpid();
tempfile->active = 1;
return tempfile->fd;
activate_tempfile(tempfile);
return tempfile;
}
int mks_tempfile_tsm(struct tempfile *tempfile,
const char *template, int suffixlen, int mode)
struct tempfile *mks_tempfile_tsm(const char *template, int suffixlen, int mode)
{
struct tempfile *tempfile = new_tempfile();
const char *tmpdir;
prepare_tempfile_object(tempfile);
tmpdir = getenv("TMPDIR");
if (!tmpdir)
tmpdir = "/tmp";
@ -180,35 +191,34 @@ int mks_tempfile_tsm(struct tempfile *tempfile,
strbuf_addf(&tempfile->filename, "%s/%s", tmpdir, template);
tempfile->fd = git_mkstemps_mode(tempfile->filename.buf, suffixlen, mode);
if (tempfile->fd < 0) {
strbuf_reset(&tempfile->filename);
return -1;
deactivate_tempfile(tempfile);
return NULL;
}
tempfile->owner = getpid();
tempfile->active = 1;
return tempfile->fd;
activate_tempfile(tempfile);
return tempfile;
}
int xmks_tempfile_m(struct tempfile *tempfile, const char *template, int mode)
struct tempfile *xmks_tempfile_m(const char *template, int mode)
{
int fd;
struct tempfile *tempfile;
struct strbuf full_template = STRBUF_INIT;
strbuf_add_absolute_path(&full_template, template);
fd = mks_tempfile_m(tempfile, full_template.buf, mode);
if (fd < 0)
tempfile = mks_tempfile_m(full_template.buf, mode);
if (!tempfile)
die_errno("Unable to create temporary file '%s'",
full_template.buf);
strbuf_release(&full_template);
return fd;
return tempfile;
}
FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode)
{
if (!tempfile->active)
die("BUG: fdopen_tempfile() called for inactive object");
if (!is_tempfile_active(tempfile))
BUG("fdopen_tempfile() called for inactive object");
if (tempfile->fp)
die("BUG: fdopen_tempfile() called for open object");
BUG("fdopen_tempfile() called for open object");
tempfile->fp = fdopen(tempfile->fd, mode);
return tempfile->fp;
@ -216,34 +226,36 @@ FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode)
const char *get_tempfile_path(struct tempfile *tempfile)
{
if (!tempfile->active)
die("BUG: get_tempfile_path() called for inactive object");
if (!is_tempfile_active(tempfile))
BUG("get_tempfile_path() called for inactive object");
return tempfile->filename.buf;
}
int get_tempfile_fd(struct tempfile *tempfile)
{
if (!tempfile->active)
die("BUG: get_tempfile_fd() called for inactive object");
if (!is_tempfile_active(tempfile))
BUG("get_tempfile_fd() called for inactive object");
return tempfile->fd;
}
FILE *get_tempfile_fp(struct tempfile *tempfile)
{
if (!tempfile->active)
die("BUG: get_tempfile_fp() called for inactive object");
if (!is_tempfile_active(tempfile))
BUG("get_tempfile_fp() called for inactive object");
return tempfile->fp;
}
int close_tempfile(struct tempfile *tempfile)
int close_tempfile_gently(struct tempfile *tempfile)
{
int fd = tempfile->fd;
FILE *fp = tempfile->fp;
int fd;
FILE *fp;
int err;
if (fd < 0)
if (!is_tempfile_active(tempfile) || tempfile->fd < 0)
return 0;
fd = tempfile->fd;
fp = tempfile->fp;
tempfile->fd = -1;
if (fp) {
tempfile->fp = NULL;
@ -258,54 +270,52 @@ int close_tempfile(struct tempfile *tempfile)
err = close(fd);
}
if (err) {
int save_errno = errno;
delete_tempfile(tempfile);
errno = save_errno;
return -1;
}
return 0;
return err ? -1 : 0;
}
int reopen_tempfile(struct tempfile *tempfile)
{
if (!is_tempfile_active(tempfile))
BUG("reopen_tempfile called for an inactive object");
if (0 <= tempfile->fd)
die("BUG: reopen_tempfile called for an open object");
if (!tempfile->active)
die("BUG: reopen_tempfile called for an inactive object");
BUG("reopen_tempfile called for an open object");
tempfile->fd = open(tempfile->filename.buf, O_WRONLY);
return tempfile->fd;
}
int rename_tempfile(struct tempfile *tempfile, const char *path)
int rename_tempfile(struct tempfile **tempfile_p, const char *path)
{
if (!tempfile->active)
die("BUG: rename_tempfile called for inactive object");
struct tempfile *tempfile = *tempfile_p;
if (close_tempfile(tempfile))
if (!is_tempfile_active(tempfile))
BUG("rename_tempfile called for inactive object");
if (close_tempfile_gently(tempfile)) {
delete_tempfile(tempfile_p);
return -1;
}
if (rename(tempfile->filename.buf, path)) {
int save_errno = errno;
delete_tempfile(tempfile);
delete_tempfile(tempfile_p);
errno = save_errno;
return -1;
}
tempfile->active = 0;
strbuf_reset(&tempfile->filename);
deactivate_tempfile(tempfile);
*tempfile_p = NULL;
return 0;
}
void delete_tempfile(struct tempfile *tempfile)
void delete_tempfile(struct tempfile **tempfile_p)
{
if (!tempfile->active)
struct tempfile *tempfile = *tempfile_p;
if (!is_tempfile_active(tempfile))
return;
if (!close_tempfile(tempfile)) {
unlink_or_warn(tempfile->filename.buf);
tempfile->active = 0;
strbuf_reset(&tempfile->filename);
}
close_tempfile_gently(tempfile);
unlink_or_warn(tempfile->filename.buf);
deactivate_tempfile(tempfile);
*tempfile_p = NULL;
}

View file

@ -1,6 +1,8 @@
#ifndef TEMPFILE_H
#define TEMPFILE_H
#include "list.h"
/*
* Handle temporary files.
*
@ -15,25 +17,18 @@
*
* The caller:
*
* * Allocates a `struct tempfile` either as a static variable or on
* the heap, initialized to zeros. Once you use the structure to
* call `create_tempfile()`, it belongs to the tempfile subsystem
* and its storage must remain valid throughout the life of the
* program (i.e. you cannot use an on-stack variable to hold this
* structure).
*
* * Attempts to create a temporary file by calling
* `create_tempfile()`.
* `create_tempfile()`. The resources used for the temporary file are
* managed by the tempfile API.
*
* * Writes new content to the file by either:
*
* * writing to the file descriptor returned by `create_tempfile()`
* (also available via `tempfile->fd`).
* * writing to the `tempfile->fd` file descriptor
*
* * calling `fdopen_tempfile()` to get a `FILE` pointer for the
* open file and writing to the file using stdio.
*
* Note that the file descriptor returned by create_tempfile()
* Note that the file descriptor created by create_tempfile()
* is marked O_CLOEXEC, so the new contents must be written by
* the current process, not any spawned one.
*
@ -47,19 +42,18 @@
* control of the file.
*
* * Close the file descriptor without removing or renaming the
* temporary file by calling `close_tempfile()`, and later call
* temporary file by calling `close_tempfile_gently()`, and later call
* `delete_tempfile()` or `rename_tempfile()`.
*
* Even after the temporary file is renamed or deleted, the `tempfile`
* object must not be freed or altered by the caller. However, it may
* be reused; just pass it to another call of `create_tempfile()`.
* After the temporary file is renamed or deleted, the `tempfile`
* object is no longer valid and should not be reused.
*
* If the program exits before `rename_tempfile()` or
* `delete_tempfile()` is called, an `atexit(3)` handler will close
* and remove the temporary file.
*
* If you need to close the file descriptor yourself, do so by calling
* `close_tempfile()`. You should never call `close(2)` or `fclose(3)`
* `close_tempfile_gently()`. You should never call `close(2)` or `fclose(3)`
* yourself, otherwise the `struct tempfile` structure would still
* think that the file descriptor needs to be closed, and a later
* cleanup would result in duplicate calls to `close(2)`. Worse yet,
@ -71,30 +65,30 @@
* Error handling
* --------------
*
* `create_tempfile()` returns a file descriptor on success or -1 on
* failure. On errors, `errno` describes the reason for failure.
* `create_tempfile()` returns an allocated tempfile on success or NULL
* on failure. On errors, `errno` describes the reason for failure.
*
* `delete_tempfile()`, `rename_tempfile()`, and `close_tempfile()`
* return 0 on success. On failure they set `errno` appropriately, do
* their best to delete the temporary file, and return -1.
* `delete_tempfile()`, `rename_tempfile()`, and `close_tempfile_gently()`
* return 0 on success. On failure they set `errno` appropriately and return
* -1. `delete` and `rename` (but not `close`) do their best to delete the
* temporary file before returning.
*/
struct tempfile {
struct tempfile *volatile next;
volatile struct volatile_list_head list;
volatile sig_atomic_t active;
volatile int fd;
FILE *volatile fp;
volatile pid_t owner;
char on_list;
struct strbuf filename;
};
/*
* Attempt to create a temporary file at the specified `path`. Return
* a file descriptor for writing to it, or -1 on error. It is an error
* if a file already exists at that path.
* a tempfile (whose "fd" member can be used for writing to it), or
* NULL on error. It is an error if a file already exists at that path.
*/
extern int create_tempfile(struct tempfile *tempfile, const char *path);
extern struct tempfile *create_tempfile(const char *path);
/*
* Register an existing file as a tempfile, meaning that it will be
@ -102,7 +96,7 @@ extern int create_tempfile(struct tempfile *tempfile, const char *path);
* but it can be worked with like any other closed tempfile (for
* example, it can be opened using reopen_tempfile()).
*/
extern void register_tempfile(struct tempfile *tempfile, const char *path);
extern struct tempfile *register_tempfile(const char *path);
/*
@ -134,83 +128,78 @@ extern void register_tempfile(struct tempfile *tempfile, const char *path);
* know the (absolute) path of the file that was created, it can be
* read from tempfile->filename.
*
* On success, the functions return a file descriptor that is open for
* writing the temporary file. On errors, they return -1 and set errno
* appropriately (except for the "x" variants, which die() on errors).
* On success, the functions return a tempfile whose "fd" member is open
* for writing the temporary file. On errors, they return NULL and set
* errno appropriately (except for the "x" variants, which die() on
* errors).
*/
/* See "mks_tempfile functions" above. */
extern int mks_tempfile_sm(struct tempfile *tempfile,
const char *template, int suffixlen, int mode);
extern struct tempfile *mks_tempfile_sm(const char *template,
int suffixlen, int mode);
/* See "mks_tempfile functions" above. */
static inline int mks_tempfile_s(struct tempfile *tempfile,
const char *template, int suffixlen)
static inline struct tempfile *mks_tempfile_s(const char *template,
int suffixlen)
{
return mks_tempfile_sm(tempfile, template, suffixlen, 0600);
return mks_tempfile_sm(template, suffixlen, 0600);
}
/* See "mks_tempfile functions" above. */
static inline int mks_tempfile_m(struct tempfile *tempfile,
const char *template, int mode)
static inline struct tempfile *mks_tempfile_m(const char *template, int mode)
{
return mks_tempfile_sm(tempfile, template, 0, mode);
return mks_tempfile_sm(template, 0, mode);
}
/* See "mks_tempfile functions" above. */
static inline int mks_tempfile(struct tempfile *tempfile,
const char *template)
static inline struct tempfile *mks_tempfile(const char *template)
{
return mks_tempfile_sm(tempfile, template, 0, 0600);
return mks_tempfile_sm(template, 0, 0600);
}
/* See "mks_tempfile functions" above. */
extern int mks_tempfile_tsm(struct tempfile *tempfile,
const char *template, int suffixlen, int mode);
extern struct tempfile *mks_tempfile_tsm(const char *template,
int suffixlen, int mode);
/* See "mks_tempfile functions" above. */
static inline int mks_tempfile_ts(struct tempfile *tempfile,
const char *template, int suffixlen)
static inline struct tempfile *mks_tempfile_ts(const char *template,
int suffixlen)
{
return mks_tempfile_tsm(tempfile, template, suffixlen, 0600);
return mks_tempfile_tsm(template, suffixlen, 0600);
}
/* See "mks_tempfile functions" above. */
static inline int mks_tempfile_tm(struct tempfile *tempfile,
const char *template, int mode)
static inline struct tempfile *mks_tempfile_tm(const char *template, int mode)
{
return mks_tempfile_tsm(tempfile, template, 0, mode);
return mks_tempfile_tsm(template, 0, mode);
}
/* See "mks_tempfile functions" above. */
static inline int mks_tempfile_t(struct tempfile *tempfile,
const char *template)
static inline struct tempfile *mks_tempfile_t(const char *template)
{
return mks_tempfile_tsm(tempfile, template, 0, 0600);
return mks_tempfile_tsm(template, 0, 0600);
}
/* See "mks_tempfile functions" above. */
extern int xmks_tempfile_m(struct tempfile *tempfile,
const char *template, int mode);
extern struct tempfile *xmks_tempfile_m(const char *template, int mode);
/* See "mks_tempfile functions" above. */
static inline int xmks_tempfile(struct tempfile *tempfile,
const char *template)
static inline struct tempfile *xmks_tempfile(const char *template)
{
return xmks_tempfile_m(tempfile, template, 0600);
return xmks_tempfile_m(template, 0600);
}
/*
* Associate a stdio stream with the temporary file (which must still
* be open). Return `NULL` (*without* deleting the file) on error. The
* stream is closed automatically when `close_tempfile()` is called or
* stream is closed automatically when `close_tempfile_gently()` is called or
* when the file is deleted or renamed.
*/
extern FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode);
static inline int is_tempfile_active(struct tempfile *tempfile)
{
return tempfile->active;
return tempfile && tempfile->active;
}
/*
@ -226,20 +215,20 @@ extern FILE *get_tempfile_fp(struct tempfile *tempfile);
* If the temporary file is still open, close it (and the file pointer
* too, if it has been opened using `fdopen_tempfile()`) without
* deleting the file. Return 0 upon success. On failure to `close(2)`,
* return a negative value and delete the file. Usually
* `delete_tempfile()` or `rename_tempfile()` should eventually be
* called if `close_tempfile()` succeeds.
* return a negative value. Usually `delete_tempfile()` or `rename_tempfile()`
* should eventually be called regardless of whether `close_tempfile_gently()`
* succeeds.
*/
extern int close_tempfile(struct tempfile *tempfile);
extern int close_tempfile_gently(struct tempfile *tempfile);
/*
* Re-open a temporary file that has been closed using
* `close_tempfile()` but not yet deleted or renamed. This can be used
* `close_tempfile_gently()` but not yet deleted or renamed. This can be used
* to implement a sequence of operations like the following:
*
* * Create temporary file.
*
* * Write new contents to file, then `close_tempfile()` to cause the
* * Write new contents to file, then `close_tempfile_gently()` to cause the
* contents to be written to disk.
*
* * Pass the name of the temporary file to another program to allow
@ -259,7 +248,7 @@ extern int reopen_tempfile(struct tempfile *tempfile);
* `delete_tempfile()` for a `tempfile` object that has already been
* deleted or renamed.
*/
extern void delete_tempfile(struct tempfile *tempfile);
extern void delete_tempfile(struct tempfile **tempfile_p);
/*
* Close the file descriptor and/or file pointer if they are still
@ -270,6 +259,6 @@ extern void delete_tempfile(struct tempfile *tempfile);
* `rename(2)`. It is a bug to call `rename_tempfile()` for a
* `tempfile` object that is not currently active.
*/
extern int rename_tempfile(struct tempfile *tempfile, const char *path);
extern int rename_tempfile(struct tempfile **tempfile_p, const char *path);
#endif /* TEMPFILE_H */

View file

@ -995,7 +995,7 @@ static void free_all(struct list_head *head)
}
}
static struct tempfile trailers_tempfile;
static struct tempfile *trailers_tempfile;
static FILE *create_in_place_tempfile(const char *file)
{
@ -1017,9 +1017,9 @@ static FILE *create_in_place_tempfile(const char *file)
strbuf_add(&template, file, tail - file + 1);
strbuf_addstr(&template, "git-interpret-trailers-XXXXXX");
xmks_tempfile_m(&trailers_tempfile, template.buf, st.st_mode);
trailers_tempfile = xmks_tempfile_m(template.buf, st.st_mode);
strbuf_release(&template);
outfile = fdopen_tempfile(&trailers_tempfile, "w");
outfile = fdopen_tempfile(trailers_tempfile, "w");
if (!outfile)
die_errno(_("could not open temporary file"));