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:
commit
89563ec379
@ -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;
|
||||
}
|
||||
|
@ -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."));
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
37
cache-tree.c
37
cache-tree.c
@ -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)
|
||||
|
2
config.c
2
config.c
@ -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;
|
||||
|
@ -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
15
diff.c
@ -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);
|
||||
|
@ -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
38
list.h
@ -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 */
|
||||
|
@ -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");
|
||||
|
58
lockfile.h
58
lockfile.h
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
32
read-cache.c
32
read-cache.c
@ -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);
|
||||
|
@ -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 @@ out:
|
||||
|
||||
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 @@ retry:
|
||||
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)",
|
||||
|
@ -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;
|
||||
|
16
shallow.c
16
shallow.c
@ -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,
|
||||
|
232
tempfile.c
232
tempfile.c
@ -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;
|
||||
}
|
||||
|
123
tempfile.h
123
tempfile.h
@ -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 */
|
||||
|
@ -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"));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user