git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/14] Introduce a tempfile module
@ 2015-06-08  9:07 Michael Haggerty
  2015-06-08  9:07 ` [PATCH 01/14] Move lockfile API documentation to lockfile.h Michael Haggerty
                   ` (14 more replies)
  0 siblings, 15 replies; 26+ messages in thread
From: Michael Haggerty @ 2015-06-08  9:07 UTC (permalink / raw)
  To: Junio C Hamano, Jeff King; +Cc: git, Michael Haggerty

We have spent a lot of effort defining the state diagram for lockfiles
and ensuring correct, race-resistant cleanup in all circumstances. Now
let's abstract out part of the lockfile module so that it can be used
to clean up arbitrary temporary files.

This patch series

* implements a new "tempfile" module

* re-implements the "lockfile" module on top of tempfile

* changes a number of places in the code that manage temporary files
  to use the new module.

This project was suggested by Peff as a 2014 GSoC project [1], but
nobody took it up. It was not suggested again as a project for GSoC
2015.

There are still a number of other call sites that could be rewritten
to use the new module. But I think it's better to get the new module
out there and rewrite the other call sites as time allows rather than
for me to keep sitting on these patches in the naive hope that I will
get around to rewriting all possible users.

Patch 06/14 adds a number of mkstemp()-like functions that work with
tempfile objects rather than just returning paths. Since wrapper.c
already contains many variants of mkstemp()-like functions, the new
module does as well. These functions basically have four switches that
can be turned on/off independently:

* Can the caller specify the file mode of the new file?

* Does the filename template include a suffix?

* Does the filename template include the full path to the file, or is
  the file created in a temporary directory?

* Does the function die on failure?

Hopefully the new module will be easier to use, not only because it
takes care of cleaning the temporary file up automatically, but also
because its functions are named more systematically. The following
table might help people trying to make sense of things:

| wrapper function  | die? | location | suffix? | mode? | tempfile function |
| ----------------- | ---- | -------- | ------- | ----- | ----------------- |
| mkstemp           |      |          |         |       | mks_tempfile      |
| git_mkstemp_mode  |      |          |         | yes   | mks_tempfile_m    |
| mkstemps          |      |          | yes     |       | mks_tempfile_s    |
| gitmkstemps (†)   |      |          | yes     |       | mks_tempfile_s    |
| git_mkstemps_mode |      |          | yes     | yes   | mks_tempfile_sm   |
| git_mkstemp       |      | $TMPDIR  |         |       | mks_tempfile_t    |
| N/A               |      | $TMPDIR  |         | yes   | mks_tempfile_tm   |
| git_mkstemps      |      | $TMPDIR  | yes     |       | mks_tempfile_ts   |
| N/A               |      | $TMPDIR  | yes     | yes   | mks_tempfile_tsm  |
| xmkstemp          | yes  |          |         |       | xmks_tempfile     |
| xmkstemp_mode     | yes  |          |         | yes   | xmks_tempfile_m   |

If the large number of new functions is too intimidating (even though
most of the functions are inline), it would be possible to decrease
the number. For example, we could add a "flags" argument that covers
"location" and "die?". We could also get rid of the no-suffix
variants, requiring all callers to use the suffix variant, setting
suffixlen to 0 if no suffix is desired.

These patches are also available from my GitHub repo [2], branch
"tempfile".

[1] http://git.github.io/SoC-2014-Ideas.html
[2] https://github.com/mhagger/git

Michael Haggerty (14):
  Move lockfile API documentation to lockfile.h
  tempfile: a new module for handling temporary files
  lockfile: remove some redundant functions
  commit_lock_file(): use get_locked_file_path()
  register_tempfile_object(): new function, extracted from
    create_tempfile()
  tempfile: add several functions for creating temporary files
  register_tempfile(): new function to handle an existing temporary file
  write_shared_index(): use tempfile module
  setup_temporary_shallow(): use tempfile module
  diff: use tempfile module
  lock_repo_for_gc(): compute the path to "gc.pid" only once
  gc: use tempfile module to handle gc.pid file
  credential-cache--daemon: delete socket from main()
  credential-cache--daemon: use tempfile module

 Documentation/technical/api-lockfile.txt | 220 ----------------------
 Makefile                                 |   1 +
 builtin/add.c                            |   1 +
 builtin/apply.c                          |   1 +
 builtin/checkout-index.c                 |   1 +
 builtin/checkout.c                       |   1 +
 builtin/clone.c                          |   1 +
 builtin/commit.c                         |  15 +-
 builtin/describe.c                       |   1 +
 builtin/diff.c                           |   1 +
 builtin/gc.c                             |  32 +---
 builtin/merge.c                          |   1 +
 builtin/mv.c                             |   1 +
 builtin/read-tree.c                      |   1 +
 builtin/receive-pack.c                   |   1 +
 builtin/reflog.c                         |   1 +
 builtin/reset.c                          |   1 +
 builtin/rm.c                             |   1 +
 builtin/update-index.c                   |   1 +
 bundle.c                                 |   3 +-
 cache-tree.c                             |   1 +
 config.c                                 |  15 +-
 credential-cache--daemon.c               |  25 +--
 credential-store.c                       |   3 +-
 diff.c                                   |  29 +--
 fast-import.c                            |   3 +-
 fetch-pack.c                             |   1 +
 lockfile.c                               | 198 +++-----------------
 lockfile.h                               | 260 +++++++++++++++++++-------
 merge-recursive.c                        |   1 +
 merge.c                                  |   1 +
 read-cache.c                             |  42 +----
 refs.c                                   |  23 +--
 rerere.c                                 |   1 +
 sequencer.c                              |   1 +
 sha1_file.c                              |   1 +
 shallow.c                                |  41 +---
 tempfile.c                               | 231 +++++++++++++++++++++++
 tempfile.h                               | 310 +++++++++++++++++++++++++++++++
 test-scrap-cache-tree.c                  |   1 +
 40 files changed, 857 insertions(+), 617 deletions(-)
 delete mode 100644 Documentation/technical/api-lockfile.txt
 create mode 100644 tempfile.c
 create mode 100644 tempfile.h

-- 
2.1.4

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 01/14] Move lockfile API documentation to lockfile.h
  2015-06-08  9:07 [PATCH 00/14] Introduce a tempfile module Michael Haggerty
@ 2015-06-08  9:07 ` Michael Haggerty
  2015-06-08  9:07 ` [PATCH 02/14] tempfile: a new module for handling temporary files Michael Haggerty
                   ` (13 subsequent siblings)
  14 siblings, 0 replies; 26+ messages in thread
From: Michael Haggerty @ 2015-06-08  9:07 UTC (permalink / raw)
  To: Junio C Hamano, Jeff King; +Cc: git, Michael Haggerty

Rearrange/rewrite it somewhat to fit its new environment.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 Documentation/technical/api-lockfile.txt | 220 ----------------------
 lockfile.h                               | 310 ++++++++++++++++++++++++++-----
 2 files changed, 266 insertions(+), 264 deletions(-)
 delete mode 100644 Documentation/technical/api-lockfile.txt

diff --git a/Documentation/technical/api-lockfile.txt b/Documentation/technical/api-lockfile.txt
deleted file mode 100644
index 93b5f23..0000000
--- a/Documentation/technical/api-lockfile.txt
+++ /dev/null
@@ -1,220 +0,0 @@
-lockfile API
-============
-
-The lockfile API serves two purposes:
-
-* Mutual exclusion and atomic file updates. When we want to change a
-  file, we create a lockfile `<filename>.lock`, write the new file
-  contents into it, and then rename the lockfile to its final
-  destination `<filename>`. We create the `<filename>.lock` file with
-  `O_CREAT|O_EXCL` so that we can notice and fail if somebody else has
-  already locked the file, then atomically rename the lockfile to its
-  final destination to commit the changes and unlock the file.
-
-* Automatic cruft removal. If the program exits after we lock a file
-  but before the changes have been committed, we want to make sure
-  that we remove the lockfile. This is done by remembering the
-  lockfiles we have created in a linked list and setting up an
-  `atexit(3)` handler and a signal handler that clean up the
-  lockfiles. This mechanism ensures that outstanding lockfiles are
-  cleaned up if the program exits (including when `die()` is called)
-  or if the program dies on a signal.
-
-Please note that lockfiles only block other writers. Readers do not
-block, but they are guaranteed to see either the old contents of the
-file or the new contents of the file (assuming that the filesystem
-implements `rename(2)` atomically).
-
-
-Calling sequence
-----------------
-
-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_*` 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).
-
-* Attempts to create a lockfile by passing that variable and the path
-  of the final destination (e.g. `$GIT_DIR/index`) to
-  `hold_lock_file_for_update` or `hold_lock_file_for_append`.
-
-* Writes new content for the destination file by either:
-
-  * writing to the file descriptor returned by the `hold_lock_file_*`
-    functions (also available via `lock->fd`).
-
-  * calling `fdopen_lock_file` to get a `FILE` pointer for the open
-    file and writing to the file using stdio.
-
-When finished writing, the caller can:
-
-* Close the file descriptor and rename the lockfile to its final
-  destination by calling `commit_lock_file` or `commit_lock_file_to`.
-
-* Close the file descriptor and remove the lockfile by calling
-  `rollback_lock_file`.
-
-* Close the file descriptor without removing or renaming the lockfile
-  by calling `close_lock_file`, 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` or
-`hold_lock_file_for_append`.
-
-If the program exits before you have called one of `commit_lock_file`,
-`commit_lock_file_to`, `rollback_lock_file`, or `close_lock_file`, an
-`atexit(3)` handler will close and remove the lockfile, rolling back
-any uncommitted changes.
-
-If you need to close the file descriptor you obtained from a
-`hold_lock_file_*` function yourself, do so by calling
-`close_lock_file`. You should never call `close(2)` or `fclose(3)`
-yourself! Otherwise the `struct lock_file` structure would still think
-that the file descriptor needs to be closed, and a commit or rollback
-would result in duplicate calls to `close(2)`. Worse yet, if you close
-and then later open another file descriptor for a completely different
-purpose, then a commit or rollback might close that unrelated file
-descriptor.
-
-
-Error handling
---------------
-
-The `hold_lock_file_*` functions return a file descriptor on success
-or -1 on failure (unless `LOCK_DIE_ON_ERROR` is used; see below). On
-errors, `errno` describes the reason for failure. Errors can be
-reported by passing `errno` to one of the following helper functions:
-
-unable_to_lock_message::
-
-	Append an appropriate error message to a `strbuf`.
-
-unable_to_lock_error::
-
-	Emit an appropriate error message using `error()`.
-
-unable_to_lock_die::
-
-	Emit an appropriate error message and `die()`.
-
-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.
-
-
-Flags
------
-
-The following flags can be passed to `hold_lock_file_for_update` or
-`hold_lock_file_for_append`:
-
-LOCK_NO_DEREF::
-
-	Usually symbolic links in the destination path are resolved
-	and the lockfile is created by adding ".lock" to the resolved
-	path. If `LOCK_NO_DEREF` is set, then the lockfile is created
-	by adding ".lock" to the path argument itself. This option is
-	used, for example, when locking a symbolic reference, which
-	for backwards-compatibility reasons can be a symbolic link
-	containing the name of the referred-to-reference.
-
-LOCK_DIE_ON_ERROR::
-
-	If a lock is already taken for the file, `die()` with an error
-	message. If this option is not specified, trying to lock a
-	file that is already locked returns -1 to the caller.
-
-
-The functions
--------------
-
-hold_lock_file_for_update::
-
-	Take a pointer to `struct lock_file`, the path of the file to
-	be locked (e.g. `$GIT_DIR/index`) and a flags argument (see
-	above). Attempt to create a lockfile for the destination and
-	return the file descriptor for writing to the file.
-
-hold_lock_file_for_append::
-
-	Like `hold_lock_file_for_update`, but before returning copy
-	the existing contents of the file (if any) to the lockfile and
-	position its write pointer at the end of the file.
-
-fdopen_lock_file::
-
-	Associate a stdio stream with the lockfile. 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.
-
-get_locked_file_path::
-
-	Return the path of the file that is locked by the specified
-	lock_file object. The caller must free the memory.
-
-commit_lock_file::
-
-	Take a pointer to the `struct lock_file` initialized with an
-	earlier call to `hold_lock_file_for_update` or
-	`hold_lock_file_for_append`, close the file descriptor, and
-	rename the lockfile to its final destination. Return 0 upon
-	success. On failure, roll back the lock file and return -1,
-	with `errno` set to the value from the failing call to
-	`close(2)` or `rename(2)`. It is a bug to call
-	`commit_lock_file` for a `lock_file` object that is not
-	currently locked.
-
-commit_lock_file_to::
-
-	Like `commit_lock_file()`, except that it takes an explicit
-	`path` argument to which the lockfile should be renamed. The
-	`path` must be on the same filesystem as the lock file.
-
-rollback_lock_file::
-
-	Take a pointer to the `struct lock_file` initialized with an
-	earlier call to `hold_lock_file_for_update` or
-	`hold_lock_file_for_append`, close the file descriptor and
-	remove the lockfile. It is a NOOP to call
-	`rollback_lock_file()` for a `lock_file` object that has
-	already been committed or rolled back.
-
-close_lock_file::
-
-	Take a pointer to the `struct lock_file` initialized with an
-	earlier call to `hold_lock_file_for_update` or
-	`hold_lock_file_for_append`. Close the file descriptor (and
-	the file pointer if it has been opened using
-	`fdopen_lock_file`). 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.
-
-reopen_lock_file::
-
-	Re-open a lockfile that has been closed (using
-	`close_lock_file`) 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
-	  cause the contents to be written to disk.
-
-	* Pass the name of the lockfile to another program to allow it
-	  (and nobody else) to inspect the contents you wrote, while
-	  still holding the lock yourself.
-
-	* `reopen_lock_file` to reopen the lockfile. Make further
-	  updates to the contents.
-
-	* `commit_lock_file` to make the final version permanent.
diff --git a/lockfile.h b/lockfile.h
index b4abc61..4cd03c2 100644
--- a/lockfile.h
+++ b/lockfile.h
@@ -4,54 +4,155 @@
 /*
  * File write-locks as used by Git.
  *
- * For an overview of how to use the lockfile API, please see
+ * The lockfile API serves two purposes:
  *
- *     Documentation/technical/api-lockfile.txt
+ * * Mutual exclusion and atomic file updates. When we want to change
+ *   a file, we create a lockfile `<filename>.lock`, write the new
+ *   file contents into it, and then rename the lockfile to its final
+ *   destination `<filename>`. We create the `<filename>.lock` file
+ *   with `O_CREAT|O_EXCL` so that we can notice and fail if somebody
+ *   else has already locked the file, then atomically rename the
+ *   lockfile to its final destination to commit the changes and
+ *   unlock the file.
  *
- * This module keeps track of all locked files in lock_file_list for
- * use at cleanup. This list and the lock_file objects that comprise
+ * * Automatic cruft removal. If the program exits after we lock a
+ *   file but before the changes have been committed, we want to make
+ *   sure that we remove the lockfile. This is done by remembering the
+ *   lockfiles we have created in a linked list and setting up an
+ *   `atexit(3)` handler and a signal handler that clean up the
+ *   lockfiles. This mechanism ensures that outstanding lockfiles are
+ *   cleaned up if the program exits (including when `die()` is
+ *   called) or if the program is terminated by a signal.
+ *
+ * Please note that lockfiles only block other writers. Readers do not
+ * block, but they are guaranteed to see either the old contents of
+ * the file or the new contents of the file (assuming that the
+ * filesystem implements `rename(2)` atomically).
+ *
+ *
+ * Calling sequence
+ * ----------------
+ *
+ * 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).
+ *
+ * * Attempts to create a lockfile by calling
+ *   `hold_lock_file_for_update()` or `hold_lock_file_for_append()`.
+ *
+ * * Writes new content for the destination file by either:
+ *
+ *   * writing to the file descriptor returned by the
+ *     `hold_lock_file_for_*()` functions (also available via
+ *     `lock->fd`).
+ *
+ *   * calling `fdopen_lock_file()` to get a `FILE` pointer for the
+ *     open file and writing to the file using stdio.
+ *
+ * When finished writing, the caller can:
+ *
+ * * Close the file descriptor and rename the lockfile to its final
+ *   destination by calling `commit_lock_file()` or
+ *   `commit_lock_file_to()`.
+ *
+ * * Close the file descriptor and remove the lockfile by calling
+ *   `rollback_lock_file()`.
+ *
+ * * Close the file descriptor without removing or renaming the
+ *   lockfile by calling `close_lock_file()`, 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()` or `hold_lock_file_for_append()`.
+ *
+ * If the program exits before `commit_lock_file()`,
+ * `commit_lock_file_to()`, or `rollback_lock_file()` is called, an
+ * `atexit(3)` handler will close and remove the lockfile, thereby
+ * rolling back any uncommitted changes.
+ *
+ * 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()`. You should never call `close(2)` or
+ * `fclose(3)` yourself, otherwise the `struct lock_file` structure
+ * would still think that the file descriptor needs to be closed, and
+ * a commit or rollback would result in duplicate calls to `close(2)`.
+ * Worse yet, if you close and then later open another file descriptor
+ * for a completely different purpose, then a commit or rollback might
+ * close that unrelated file descriptor.
+ *
+ *
+ * State diagram and cleanup
+ * -------------------------
+ *
+ * This module keeps track of all locked files in `lock_file_list` for
+ * use at cleanup. This list and the `lock_file` objects that comprise
  * it must be kept in self-consistent states at all time, because the
  * program can be interrupted any time by a signal, in which case the
  * signal handler will walk through the list attempting to clean up
  * any open lock files.
  *
- * A lockfile is owned by the process that created it. The lock_file
- * object has an "owner" field that records its owner. This field is
- * used to prevent a forked process from closing a lockfile created by
- * its parent.
- *
- * The possible states of a lock_file object are as follows:
+ * The possible states of a `lock_file` object are as follows:
  *
- * - Uninitialized.  In this state the object's on_list field must be
- *   zero but the rest of its contents need not be initialized.  As
+ * - Uninitialized. In this state the object's `on_list` field must be
+ *   zero but the rest of its contents need not be initialized. As
  *   soon as the object is used in any way, it is irrevocably
- *   registered in the lock_file_list, and on_list is set.
+ *   registered in `lock_file_list`, and `on_list` is set.
  *
- * - Locked, lockfile open (after hold_lock_file_for_update(),
- *   hold_lock_file_for_append(), or reopen_lock_file()). In this
+ * - Locked, lockfile open (after `hold_lock_file_for_update()`,
+ *   `hold_lock_file_for_append()`, or `reopen_lock_file()`). In this
  *   state:
+ *
  *   - the lockfile exists
- *   - active is set
- *   - filename holds the filename of the lockfile
- *   - fd holds a file descriptor open for writing to the lockfile
- *   - fp holds a pointer to an open FILE object if and only if
- *     fdopen_lock_file() has been called on the object
- *   - owner holds the PID of the process that locked the file
- *
- * - Locked, lockfile closed (after successful close_lock_file()).
+ *   - `active` is set
+ *   - `filename` holds the filename of the lockfile
+ *   - `fd` holds a file descriptor open for writing to the lockfile
+ *   - `fp` holds a pointer to an open `FILE` object if and only if
+ *     `fdopen_lock_file()` has been called on the object
+ *   - `owner` holds the PID of the process that locked the file
+ *
+ * - Locked, lockfile closed (after successful `close_lock_file()`).
  *   Same as the previous state, except that the lockfile is closed
- *   and fd is -1.
+ *   and `fd` is -1.
+ *
+ * - Unlocked (after `commit_lock_file()`, `commit_lock_file_to()`,
+ *   `rollback_lock_file()`, a failed attempt to lock, or a failed
+ *   `close_lock_file()`).  In this state:
  *
- * - Unlocked (after commit_lock_file(), commit_lock_file_to(),
- *   rollback_lock_file(), a failed attempt to lock, or a failed
- *   close_lock_file()).  In this state:
- *   - active is unset
- *   - filename is empty (usually, though there are transitory
+ *   - `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
- *   - the object is left registered in the lock_file_list, and
- *     on_list is set.
+ *   - `fd` is -1
+ *   - the object is left registered in the `lock_file_list`, and
+ *     `on_list` is set.
+ *
+ * A lockfile is owned by the process that created it. The `lock_file`
+ * has an `owner` field that records the owner's PID. This field is
+ * used to prevent a forked process from closing a lockfile created by
+ * its parent.
+ *
+ *
+ * Error handling
+ * --------------
+ *
+ * The `hold_lock_file_for_*()` functions return a file descriptor on
+ * success or -1 on failure (unless `LOCK_DIE_ON_ERROR` is used; see
+ * "flags" below). On errors, `errno` describes the reason for
+ * failure. Errors can be reported by passing `errno` to
+ * `unable_to_lock_message()` or `unable_to_lock_die()`.
+ *
+ * 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.
  */
 
 struct lock_file {
@@ -68,16 +169,51 @@ struct lock_file {
 #define LOCK_SUFFIX ".lock"
 #define LOCK_SUFFIX_LEN 5
 
+
+/*
+ * Flags
+ * -----
+ *
+ * The following flags can be passed to `hold_lock_file_for_update()`
+ * or `hold_lock_file_for_append()`.
+ */
+
+/*
+ * If a lock is already taken for the file, `die()` with an error
+ * message. If this flag is not specified, trying to lock a file that
+ * is already locked returns -1 to the caller.
+ */
 #define LOCK_DIE_ON_ERROR 1
+
+/*
+ * Usually symbolic links in the destination path are resolved. This
+ * means that (1) the lockfile is created by adding ".lock" to the
+ * resolved path, and (2) upon commit, the resolved path is
+ * overwritten. However, if `LOCK_NO_DEREF` is set, then the lockfile
+ * is created by adding ".lock" to the path argument itself. This
+ * option is used, for example, when detaching a symbolic reference,
+ * which for backwards-compatibility reasons, can be a symbolic link
+ * containing the name of the referred-to-reference.
+ */
 #define LOCK_NO_DEREF 2
 
-extern void unable_to_lock_message(const char *path, int err,
-				   struct strbuf *buf);
-extern NORETURN void unable_to_lock_die(const char *path, int err);
+/*
+ * Attempt to create a lockfile for the file at `path` and return a
+ * file descriptor for writing to it, or -1 on error. If the file is
+ * currently locked, retry with quadratic backoff for at least
+ * timeout_ms milliseconds. If timeout_ms is 0, try exactly once; if
+ * timeout_ms is -1, retry indefinitely. The flags argument and error
+ * handling are described above.
+ */
 extern int hold_lock_file_for_update_timeout(
 		struct lock_file *lk, const char *path,
 		int flags, long timeout_ms);
 
+/*
+ * Attempt to create a lockfile for the file at `path` and return a
+ * file descriptor for writing to it, or -1 on error. The flags
+ * argument and error handling are described above.
+ */
 static inline int hold_lock_file_for_update(
 		struct lock_file *lk, const char *path,
 		int flags)
@@ -85,15 +221,101 @@ static inline int hold_lock_file_for_update(
 	return hold_lock_file_for_update_timeout(lk, path, flags, 0);
 }
 
-extern int hold_lock_file_for_append(struct lock_file *lk, const char *path,
-				     int flags);
+/*
+ * Like `hold_lock_file_for_update()`, but before returning copy the
+ * existing contents of the file (if any) to the lockfile and position
+ * its write pointer at the end of the file. The flags argument and
+ * error handling are described above.
+ */
+extern int hold_lock_file_for_append(struct lock_file *lk,
+				     const char *path, int flags);
+
+/*
+ * Append an appropriate error message to `buf` following the failure
+ * of `hold_lock_file_for_update()` or `hold_lock_file_for_append()`
+ * to lock `path`. `err` should be the `errno` set by the failing
+ * call.
+ */
+extern void unable_to_lock_message(const char *path, int err,
+				   struct strbuf *buf);
+
+/*
+ * Emit an appropriate error message and `die()` following the failure
+ * of `hold_lock_file_for_update()` or `hold_lock_file_for_append()`
+ * to lock `path`. `err` should be the `errno` set by the failing
+ * call.
+ */
+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.
+ */
+extern FILE *fdopen_lock_file(struct lock_file *lk, const char *mode);
+
+/*
+ * Return the path of the file that is locked by the specified
+ * lock_file object. The caller must free the memory.
+ */
+extern char *get_locked_file_path(struct lock_file *lk);
+
+/*
+ * If the lockfile is still open, close it (and the file pointer if it
+ * has been opened using `fdopen_lock_file()`) without renaming the
+ * 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.
+ */
+extern int close_lock_file(struct lock_file *lk);
 
-extern FILE *fdopen_lock_file(struct lock_file *, const char *mode);
-extern char *get_locked_file_path(struct lock_file *);
-extern int commit_lock_file_to(struct lock_file *, const char *path);
-extern int commit_lock_file(struct lock_file *);
-extern int reopen_lock_file(struct lock_file *);
-extern int close_lock_file(struct lock_file *);
-extern void rollback_lock_file(struct lock_file *);
+/*
+ * Re-open a lockfile that has been closed using `close_lock_file()`
+ * 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
+ *   cause the contents to be written to disk.
+ *
+ * * Pass the name of the lockfile to another program to allow it (and
+ *   nobody else) to inspect the contents you wrote, while still
+ *   holding the lock yourself.
+ *
+ * * `reopen_lock_file()` to reopen the lockfile. Make further updates
+ *   to the contents.
+ *
+ * * `commit_lock_file()` to make the final version permanent.
+ */
+extern int reopen_lock_file(struct lock_file *lk);
+
+/*
+ * Commit the change represented by `lk`: close the file descriptor
+ * and/or file pointer if they are still open and rename the lockfile
+ * to its final destination. Return 0 upon success. On failure, roll
+ * back the lock file and return -1, with `errno` set to the value
+ * from the failing call to `close(2)` or `rename(2)`. It is a bug to
+ * call `commit_lock_file()` for a `lock_file` object that is not
+ * currently locked.
+ */
+extern int commit_lock_file(struct lock_file *lk);
+
+/*
+ * Like `commit_lock_file()`, but rename the lockfile to the provided
+ * `path`. `path` must be on the same filesystem as the lock file.
+ */
+extern int commit_lock_file_to(struct lock_file *lk, const char *path);
+
+/*
+ * Roll back `lk`: close the file descriptor and/or file pointer and
+ * remove the lockfile. It is a NOOP to call `rollback_lock_file()`
+ * for a `lock_file` object that has already been committed or rolled
+ * back.
+ */
+extern void rollback_lock_file(struct lock_file *lk);
 
 #endif /* LOCKFILE_H */
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 02/14] tempfile: a new module for handling temporary files
  2015-06-08  9:07 [PATCH 00/14] Introduce a tempfile module Michael Haggerty
  2015-06-08  9:07 ` [PATCH 01/14] Move lockfile API documentation to lockfile.h Michael Haggerty
@ 2015-06-08  9:07 ` Michael Haggerty
  2015-06-10 17:36   ` Junio C Hamano
  2015-06-08  9:07 ` [PATCH 03/14] lockfile: remove some redundant functions Michael Haggerty
                   ` (12 subsequent siblings)
  14 siblings, 1 reply; 26+ messages in thread
From: Michael Haggerty @ 2015-06-08  9:07 UTC (permalink / raw)
  To: Junio C Hamano, Jeff King; +Cc: git, Michael Haggerty

A lot of work went into defining the state diagram for lockfiles and
ensuring correct, race-resistant cleanup in all circumstances.

Most of that infrastructure can be applied directly to *any* temporary
file. So extract a new "tempfile" module from the "lockfile" module.
Reimplement lockfile on top of tempfile.

This first step is a pretty direct transplant of code. Subsequent
commits will improve the boundary between the two modules and add
users of the new module.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 Makefile                 |   1 +
 builtin/add.c            |   1 +
 builtin/apply.c          |   1 +
 builtin/checkout-index.c |   1 +
 builtin/checkout.c       |   1 +
 builtin/clone.c          |   1 +
 builtin/commit.c         |  13 +--
 builtin/describe.c       |   1 +
 builtin/diff.c           |   1 +
 builtin/gc.c             |   1 +
 builtin/merge.c          |   1 +
 builtin/mv.c             |   1 +
 builtin/read-tree.c      |   1 +
 builtin/receive-pack.c   |   1 +
 builtin/reflog.c         |   1 +
 builtin/reset.c          |   1 +
 builtin/rm.c             |   1 +
 builtin/update-index.c   |   1 +
 bundle.c                 |   3 +-
 cache-tree.c             |   1 +
 config.c                 |  15 ++--
 credential-store.c       |   3 +-
 fast-import.c            |   1 +
 fetch-pack.c             |   1 +
 lockfile.c               | 179 ++++++----------------------------------
 lockfile.h               |  76 +++--------------
 merge-recursive.c        |   1 +
 merge.c                  |   1 +
 read-cache.c             |   3 +-
 refs.c                   |  15 ++--
 rerere.c                 |   1 +
 sequencer.c              |   1 +
 sha1_file.c              |   1 +
 shallow.c                |   7 +-
 tempfile.c               | 166 ++++++++++++++++++++++++++++++++++++++
 tempfile.h               | 206 +++++++++++++++++++++++++++++++++++++++++++++++
 test-scrap-cache-tree.c  |   1 +
 37 files changed, 465 insertions(+), 247 deletions(-)
 create mode 100644 tempfile.c
 create mode 100644 tempfile.h

diff --git a/Makefile b/Makefile
index 54ec511..2573f89 100644
--- a/Makefile
+++ b/Makefile
@@ -786,6 +786,7 @@ LIB_OBJS += string-list.o
 LIB_OBJS += submodule.o
 LIB_OBJS += symlinks.o
 LIB_OBJS += tag.o
+LIB_OBJS += tempfile.o
 LIB_OBJS += trace.o
 LIB_OBJS += trailer.o
 LIB_OBJS += transport.o
diff --git a/builtin/add.c b/builtin/add.c
index df5135b..aaa9ce4 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -5,6 +5,7 @@
  */
 #include "cache.h"
 #include "builtin.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "dir.h"
 #include "pathspec.h"
diff --git a/builtin/apply.c b/builtin/apply.c
index 146be97..ea34626 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -7,6 +7,7 @@
  *
  */
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "cache-tree.h"
 #include "quote.h"
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 8028c37..ad5ac18 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -5,6 +5,7 @@
  *
  */
 #include "builtin.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "quote.h"
 #include "cache-tree.h"
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 9b49f0e..82c179d 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,4 +1,5 @@
 #include "builtin.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "parse-options.h"
 #include "refs.h"
diff --git a/builtin/clone.c b/builtin/clone.c
index b878252..494ebd2 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -9,6 +9,7 @@
  */
 
 #include "builtin.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "parse-options.h"
 #include "fetch-pack.h"
diff --git a/builtin/commit.c b/builtin/commit.c
index 254477f..c9fbe42 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -6,6 +6,7 @@
  */
 
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "cache-tree.h"
 #include "color.h"
@@ -344,7 +345,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
 			die(_("unable to create temporary index"));
 
 		old_index_env = getenv(INDEX_ENVIRONMENT);
-		setenv(INDEX_ENVIRONMENT, index_lock.filename.buf, 1);
+		setenv(INDEX_ENVIRONMENT, index_lock.tempfile.filename.buf, 1);
 
 		if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
 			die(_("interactive add failed"));
@@ -355,7 +356,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
 			unsetenv(INDEX_ENVIRONMENT);
 
 		discard_cache();
-		read_cache_from(index_lock.filename.buf);
+		read_cache_from(index_lock.tempfile.filename.buf);
 		if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
 			if (reopen_lock_file(&index_lock) < 0)
 				die(_("unable to write index file"));
@@ -365,7 +366,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
 			warning(_("Failed to update main cache tree"));
 
 		commit_style = COMMIT_NORMAL;
-		return index_lock.filename.buf;
+		return index_lock.tempfile.filename.buf;
 	}
 
 	/*
@@ -388,7 +389,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
 		if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
 			die(_("unable to write new_index file"));
 		commit_style = COMMIT_NORMAL;
-		return index_lock.filename.buf;
+		return index_lock.tempfile.filename.buf;
 	}
 
 	/*
@@ -475,9 +476,9 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
 		die(_("unable to write temporary index file"));
 
 	discard_cache();
-	read_cache_from(false_lock.filename.buf);
+	read_cache_from(false_lock.tempfile.filename.buf);
 
-	return false_lock.filename.buf;
+	return false_lock.tempfile.filename.buf;
 }
 
 static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
diff --git a/builtin/describe.c b/builtin/describe.c
index a36c829..2b54eb5 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "commit.h"
 #include "tag.h"
diff --git a/builtin/diff.c b/builtin/diff.c
index 4326fa5..e815ec7 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -4,6 +4,7 @@
  * Copyright (c) 2006 Junio C Hamano
  */
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "color.h"
 #include "commit.h"
diff --git a/builtin/gc.c b/builtin/gc.c
index 36fe333..6e18d35 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -11,6 +11,7 @@
  */
 
 #include "builtin.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "parse-options.h"
 #include "run-command.h"
diff --git a/builtin/merge.c b/builtin/merge.c
index 85c54dc..584f18c 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -9,6 +9,7 @@
 #include "cache.h"
 #include "parse-options.h"
 #include "builtin.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "run-command.h"
 #include "diff.h"
diff --git a/builtin/mv.c b/builtin/mv.c
index d1d4316..9752126 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -4,6 +4,7 @@
  * Copyright (C) 2006 Johannes Schindelin
  */
 #include "builtin.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "dir.h"
 #include "cache-tree.h"
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 43b47f7..7317e6f 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -5,6 +5,7 @@
  */
 
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "object.h"
 #include "tree.h"
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 94d0571..13edfac 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1,4 +1,5 @@
 #include "builtin.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "pack.h"
 #include "refs.h"
diff --git a/builtin/reflog.c b/builtin/reflog.c
index c2eb8ff..a1aa7e6 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -1,4 +1,5 @@
 #include "builtin.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "commit.h"
 #include "refs.h"
diff --git a/builtin/reset.c b/builtin/reset.c
index 4c08ddc..9271200 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -8,6 +8,7 @@
  * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
  */
 #include "builtin.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "tag.h"
 #include "object.h"
diff --git a/builtin/rm.c b/builtin/rm.c
index 80b972f..bc3c132 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -4,6 +4,7 @@
  * Copyright (C) Linus Torvalds 2006
  */
 #include "builtin.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "dir.h"
 #include "cache-tree.h"
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 7431938..aa81943 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -4,6 +4,7 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "quote.h"
 #include "cache-tree.h"
diff --git a/bundle.c b/bundle.c
index f732c92..d5985a1 100644
--- a/bundle.c
+++ b/bundle.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "bundle.h"
 #include "object.h"
@@ -255,7 +256,7 @@ static int write_pack_data(int bundle_fd, struct lock_file *lock, struct rev_inf
 	 * so set the lock fd to -1 so commit_lock_file()
 	 * won't fail trying to close it.
 	 */
-	lock->fd = -1;
+	lock->tempfile.fd = -1;
 
 	for (i = 0; i < revs->pending.nr; i++) {
 		struct object *object = revs->pending.objects[i].item;
diff --git a/cache-tree.c b/cache-tree.c
index 32772b9..014a7e9 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "tree.h"
 #include "tree-walk.h"
diff --git a/config.c b/config.c
index ab46462..2d79ba1 100644
--- a/config.c
+++ b/config.c
@@ -6,6 +6,7 @@
  *
  */
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "exec_cmd.h"
 #include "strbuf.h"
@@ -2056,9 +2057,9 @@ int git_config_set_multivar_in_file(const char *config_filename,
 			MAP_PRIVATE, in_fd, 0);
 		close(in_fd);
 
-		if (chmod(lock->filename.buf, st.st_mode & 07777) < 0) {
+		if (chmod(lock->tempfile.filename.buf, st.st_mode & 07777) < 0) {
 			error("chmod on %s failed: %s",
-				lock->filename.buf, strerror(errno));
+				lock->tempfile.filename.buf, strerror(errno));
 			ret = CONFIG_NO_WRITE;
 			goto out_free;
 		}
@@ -2138,7 +2139,7 @@ out_free:
 	return ret;
 
 write_err_out:
-	ret = write_error(lock->filename.buf);
+	ret = write_error(lock->tempfile.filename.buf);
 	goto out_free;
 
 }
@@ -2239,9 +2240,9 @@ int git_config_rename_section_in_file(const char *config_filename,
 
 	fstat(fileno(config_file), &st);
 
-	if (chmod(lock->filename.buf, st.st_mode & 07777) < 0) {
+	if (chmod(lock->tempfile.filename.buf, st.st_mode & 07777) < 0) {
 		ret = error("chmod on %s failed: %s",
-				lock->filename.buf, strerror(errno));
+				lock->tempfile.filename.buf, strerror(errno));
 		goto out;
 	}
 
@@ -2262,7 +2263,7 @@ int git_config_rename_section_in_file(const char *config_filename,
 				}
 				store.baselen = strlen(new_name);
 				if (!store_write_section(out_fd, new_name)) {
-					ret = write_error(lock->filename.buf);
+					ret = write_error(lock->tempfile.filename.buf);
 					goto out;
 				}
 				/*
@@ -2288,7 +2289,7 @@ int git_config_rename_section_in_file(const char *config_filename,
 			continue;
 		length = strlen(output);
 		if (write_in_full(out_fd, output, length) != length) {
-			ret = write_error(lock->filename.buf);
+			ret = write_error(lock->tempfile.filename.buf);
 			goto out;
 		}
 	}
diff --git a/credential-store.c b/credential-store.c
index f692509..96f258f 100644
--- a/credential-store.c
+++ b/credential-store.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "credential.h"
 #include "string-list.h"
@@ -52,7 +53,7 @@ static void print_entry(struct credential *c)
 static void print_line(struct strbuf *buf)
 {
 	strbuf_addch(buf, '\n');
-	write_or_die(credential_lock.fd, buf->buf, buf->len);
+	write_or_die(credential_lock.tempfile.fd, buf->buf, buf->len);
 }
 
 static void rewrite_credential_file(const char *fn, struct credential *c,
diff --git a/fast-import.c b/fast-import.c
index 6378726..32a3e21 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -153,6 +153,7 @@ Format of STDIN stream:
 
 #include "builtin.h"
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "object.h"
 #include "blob.h"
diff --git a/fetch-pack.c b/fetch-pack.c
index a912935..ec668c8 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "refs.h"
 #include "pkt-line.h"
diff --git a/lockfile.c b/lockfile.c
index 5a93bc7..b453016 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -2,37 +2,8 @@
  * Copyright (c) 2005, Junio C Hamano
  */
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
-#include "sigchain.h"
-
-static struct lock_file *volatile lock_file_list;
-
-static void remove_lock_files(int skip_fclose)
-{
-	pid_t me = getpid();
-
-	while (lock_file_list) {
-		if (lock_file_list->owner == me) {
-			/* fclose() is not safe to call in a signal handler */
-			if (skip_fclose)
-				lock_file_list->fp = NULL;
-			rollback_lock_file(lock_file_list);
-		}
-		lock_file_list = lock_file_list->next;
-	}
-}
-
-static void remove_lock_files_on_exit(void)
-{
-	remove_lock_files(0);
-}
-
-static void remove_lock_files_on_signal(int signo)
-{
-	remove_lock_files(1);
-	sigchain_pop(signo);
-	raise(signo);
-}
 
 /*
  * path = absolute or relative path name
@@ -101,60 +72,17 @@ 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)
 {
-	size_t pathlen = strlen(path);
-
-	if (!lock_file_list) {
-		/* One-time initialization */
-		sigchain_push_common(remove_lock_files_on_signal);
-		atexit(remove_lock_files_on_exit);
-	}
-
-	if (lk->active)
-		die("BUG: cannot lock_file(\"%s\") using active struct lock_file",
-		    path);
-	if (!lk->on_list) {
-		/* Initialize *lk and add it to lock_file_list: */
-		lk->fd = -1;
-		lk->fp = NULL;
-		lk->active = 0;
-		lk->owner = 0;
-		strbuf_init(&lk->filename, pathlen + LOCK_SUFFIX_LEN);
-		lk->next = lock_file_list;
-		lock_file_list = lk;
-		lk->on_list = 1;
-	} else if (lk->filename.len) {
-		/* This shouldn't happen, but better safe than sorry. */
-		die("BUG: lock_file(\"%s\") called with improperly-reset lock_file object",
-		    path);
-	}
+	int fd;
+	struct strbuf filename = STRBUF_INIT;
 
-	if (flags & LOCK_NO_DEREF) {
-		strbuf_add_absolute_path(&lk->filename, path);
-	} else {
-		struct strbuf resolved_path = STRBUF_INIT;
+	strbuf_addstr(&filename, path);
+	if (!(flags & LOCK_NO_DEREF))
+		resolve_symlink(&filename);
 
-		strbuf_add(&resolved_path, path, pathlen);
-		resolve_symlink(&resolved_path);
-		strbuf_add_absolute_path(&lk->filename, resolved_path.buf);
-		strbuf_release(&resolved_path);
-	}
-
-	strbuf_addstr(&lk->filename, LOCK_SUFFIX);
-	lk->fd = open(lk->filename.buf, O_RDWR | O_CREAT | O_EXCL, 0666);
-	if (lk->fd < 0) {
-		strbuf_reset(&lk->filename);
-		return -1;
-	}
-	lk->owner = getpid();
-	lk->active = 1;
-	if (adjust_shared_perm(lk->filename.buf)) {
-		int save_errno = errno;
-		error("cannot fix permission bits on %s", lk->filename.buf);
-		rollback_lock_file(lk);
-		errno = save_errno;
-		return -1;
-	}
-	return lk->fd;
+	strbuf_addstr(&filename, LOCK_SUFFIX);
+	fd = create_tempfile(&lk->tempfile, filename.buf);
+	strbuf_release(&filename);
+	return fd;
 }
 
 static int sleep_microseconds(long us)
@@ -302,84 +230,31 @@ int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags)
 
 FILE *fdopen_lock_file(struct lock_file *lk, const char *mode)
 {
-	if (!lk->active)
-		die("BUG: fdopen_lock_file() called for unlocked object");
-	if (lk->fp)
-		die("BUG: fdopen_lock_file() called twice for file '%s'", lk->filename.buf);
-
-	lk->fp = fdopen(lk->fd, mode);
-	return lk->fp;
+	return fdopen_tempfile(&lk->tempfile, mode);
 }
 
 char *get_locked_file_path(struct lock_file *lk)
 {
-	if (!lk->active)
+	if (!lk->tempfile.active)
 		die("BUG: get_locked_file_path() called for unlocked object");
-	if (lk->filename.len <= LOCK_SUFFIX_LEN)
+	if (lk->tempfile.filename.len <= LOCK_SUFFIX_LEN)
 		die("BUG: get_locked_file_path() called for malformed lock object");
-	return xmemdupz(lk->filename.buf, lk->filename.len - LOCK_SUFFIX_LEN);
+	return xmemdupz(lk->tempfile.filename.buf, lk->tempfile.filename.len - LOCK_SUFFIX_LEN);
 }
 
 int close_lock_file(struct lock_file *lk)
 {
-	int fd = lk->fd;
-	FILE *fp = lk->fp;
-	int err;
-
-	if (fd < 0)
-		return 0;
-
-	lk->fd = -1;
-	if (fp) {
-		lk->fp = NULL;
-
-		/*
-		 * Note: no short-circuiting here; we want to fclose()
-		 * in any case!
-		 */
-		err = ferror(fp) | fclose(fp);
-	} else {
-		err = close(fd);
-	}
-
-	if (err) {
-		int save_errno = errno;
-		rollback_lock_file(lk);
-		errno = save_errno;
-		return -1;
-	}
-
-	return 0;
+	return close_tempfile(&lk->tempfile);
 }
 
 int reopen_lock_file(struct lock_file *lk)
 {
-	if (0 <= lk->fd)
-		die(_("BUG: reopen a lockfile that is still open"));
-	if (!lk->active)
-		die(_("BUG: reopen a lockfile that has been committed"));
-	lk->fd = open(lk->filename.buf, O_WRONLY);
-	return lk->fd;
+	return reopen_tempfile(&lk->tempfile);
 }
 
 int commit_lock_file_to(struct lock_file *lk, const char *path)
 {
-	if (!lk->active)
-		die("BUG: attempt to commit unlocked object to \"%s\"", path);
-
-	if (close_lock_file(lk))
-		return -1;
-
-	if (rename(lk->filename.buf, path)) {
-		int save_errno = errno;
-		rollback_lock_file(lk);
-		errno = save_errno;
-		return -1;
-	}
-
-	lk->active = 0;
-	strbuf_reset(&lk->filename);
-	return 0;
+	return rename_tempfile(&lk->tempfile, path);
 }
 
 int commit_lock_file(struct lock_file *lk)
@@ -387,16 +262,17 @@ int commit_lock_file(struct lock_file *lk)
 	static struct strbuf result_file = STRBUF_INIT;
 	int err;
 
-	if (!lk->active)
+	if (!lk->tempfile.active)
 		die("BUG: attempt to commit unlocked object");
 
-	if (lk->filename.len <= LOCK_SUFFIX_LEN ||
-	    strcmp(lk->filename.buf + lk->filename.len - LOCK_SUFFIX_LEN, LOCK_SUFFIX))
+	if (lk->tempfile.filename.len <= LOCK_SUFFIX_LEN ||
+	    strcmp(lk->tempfile.filename.buf + lk->tempfile.filename.len - LOCK_SUFFIX_LEN,
+		   LOCK_SUFFIX))
 		die("BUG: lockfile filename corrupt");
 
 	/* remove ".lock": */
-	strbuf_add(&result_file, lk->filename.buf,
-		   lk->filename.len - LOCK_SUFFIX_LEN);
+	strbuf_add(&result_file, lk->tempfile.filename.buf,
+		   lk->tempfile.filename.len - LOCK_SUFFIX_LEN);
 	err = commit_lock_file_to(lk, result_file.buf);
 	strbuf_reset(&result_file);
 	return err;
@@ -404,12 +280,5 @@ int commit_lock_file(struct lock_file *lk)
 
 void rollback_lock_file(struct lock_file *lk)
 {
-	if (!lk->active)
-		return;
-
-	if (!close_lock_file(lk)) {
-		unlink_or_warn(lk->filename.buf);
-		lk->active = 0;
-		strbuf_reset(&lk->filename);
-	}
+	delete_tempfile(&lk->tempfile);
 }
diff --git a/lockfile.h b/lockfile.h
index 4cd03c2..5b313ef 100644
--- a/lockfile.h
+++ b/lockfile.h
@@ -29,6 +29,8 @@
  * the file or the new contents of the file (assuming that the
  * filesystem implements `rename(2)` atomically).
  *
+ * Most of the heavy lifting is done by the tempfile module (see
+ * "tempfile.h").
  *
  * Calling sequence
  * ----------------
@@ -74,70 +76,18 @@
  * `hold_lock_file_for_update()` or `hold_lock_file_for_append()`.
  *
  * If the program exits before `commit_lock_file()`,
- * `commit_lock_file_to()`, or `rollback_lock_file()` is called, an
- * `atexit(3)` handler will close and remove the lockfile, thereby
- * rolling back any uncommitted changes.
+ * `commit_lock_file_to()`, or `rollback_lock_file()` is called, the
+ * tempfile module will close and remove the lockfile, thereby rolling
+ * back any uncommitted changes.
  *
  * 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()`. You should never call `close(2)` or
- * `fclose(3)` yourself, otherwise the `struct lock_file` structure
- * would still think that the file descriptor needs to be closed, and
- * a commit or rollback would result in duplicate calls to `close(2)`.
- * Worse yet, if you close and then later open another file descriptor
- * for a completely different purpose, then a commit or rollback might
- * close that unrelated file descriptor.
+ * `close_lock_file()`. See "tempfile.h" for more information.
  *
  *
- * State diagram and cleanup
- * -------------------------
- *
- * This module keeps track of all locked files in `lock_file_list` for
- * use at cleanup. This list and the `lock_file` objects that comprise
- * it must be kept in self-consistent states at all time, because the
- * program can be interrupted any time by a signal, in which case the
- * signal handler will walk through the list attempting to clean up
- * any open lock files.
- *
- * The possible states of a `lock_file` object are as follows:
- *
- * - Uninitialized. In this state the object's `on_list` field must be
- *   zero but the rest of its contents need not be initialized. As
- *   soon as the object is used in any way, it is irrevocably
- *   registered in `lock_file_list`, and `on_list` is set.
- *
- * - Locked, lockfile open (after `hold_lock_file_for_update()`,
- *   `hold_lock_file_for_append()`, or `reopen_lock_file()`). In this
- *   state:
- *
- *   - the lockfile exists
- *   - `active` is set
- *   - `filename` holds the filename of the lockfile
- *   - `fd` holds a file descriptor open for writing to the lockfile
- *   - `fp` holds a pointer to an open `FILE` object if and only if
- *     `fdopen_lock_file()` has been called on the object
- *   - `owner` holds the PID of the process that locked the file
- *
- * - Locked, lockfile closed (after successful `close_lock_file()`).
- *   Same as the previous state, except that the lockfile is closed
- *   and `fd` is -1.
- *
- * - Unlocked (after `commit_lock_file()`, `commit_lock_file_to()`,
- *   `rollback_lock_file()`, a failed attempt to lock, or a failed
- *   `close_lock_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
- *   - the object is left registered in the `lock_file_list`, and
- *     `on_list` is set.
- *
- * A lockfile is owned by the process that created it. The `lock_file`
- * has an `owner` field that records the owner's PID. This field is
- * used to prevent a forked process from closing a lockfile created by
- * its parent.
+ * Under the covers, a lockfile is just a tempfile with a few helper
+ * functions. In particular, the state diagram and the cleanup
+ * machinery are all implemented in the tempfile module.
  *
  *
  * Error handling
@@ -156,13 +106,7 @@
  */
 
 struct lock_file {
-	struct lock_file *volatile next;
-	volatile sig_atomic_t active;
-	volatile int fd;
-	FILE *volatile fp;
-	volatile pid_t owner;
-	char on_list;
-	struct strbuf filename;
+	struct tempfile tempfile;
 };
 
 /* String appended to a filename to derive the lockfile name: */
diff --git a/merge-recursive.c b/merge-recursive.c
index 44d85be..2768124 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -5,6 +5,7 @@
  */
 #include "cache.h"
 #include "advice.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "cache-tree.h"
 #include "commit.h"
diff --git a/merge.c b/merge.c
index fcff632..f23e3e7 100644
--- a/merge.c
+++ b/merge.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "commit.h"
 #include "run-command.h"
diff --git a/read-cache.c b/read-cache.c
index 723d48d..e20e003 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -5,6 +5,7 @@
  */
 #define NO_THE_INDEX_COMPATIBILITY_MACROS
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "cache-tree.h"
 #include "refs.h"
@@ -2112,7 +2113,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->fd, 0);
+	int ret = do_write_index(istate, lock->tempfile.fd, 0);
 	if (ret)
 		return ret;
 	assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) !=
diff --git a/refs.c b/refs.c
index a742d79..d98b0f5 100644
--- a/refs.c
+++ b/refs.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "refs.h"
 #include "object.h"
@@ -3178,11 +3179,11 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
 		errno = EINVAL;
 		return -1;
 	}
-	if (write_in_full(lock->lk->fd, sha1_to_hex(sha1), 40) != 40 ||
-	    write_in_full(lock->lk->fd, &term, 1) != 1 ||
+	if (write_in_full(lock->lk->tempfile.fd, sha1_to_hex(sha1), 40) != 40 ||
+	    write_in_full(lock->lk->tempfile.fd, &term, 1) != 1 ||
 	    close_ref(lock) < 0) {
 		int save_errno = errno;
-		error("Couldn't write %s", lock->lk->filename.buf);
+		error("Couldn't write %s", lock->lk->tempfile.filename.buf);
 		unlock_ref(lock);
 		errno = save_errno;
 		return -1;
@@ -4239,7 +4240,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
 		cb.newlog = fdopen_lock_file(&reflog_lock, "w");
 		if (!cb.newlog) {
 			error("cannot fdopen %s (%s)",
-			      reflog_lock.filename.buf, strerror(errno));
+			      reflog_lock.tempfile.filename.buf, strerror(errno));
 			goto failure;
 		}
 	}
@@ -4264,12 +4265,12 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
 			status |= error("couldn't write %s: %s", log_file,
 					strerror(errno));
 		} else if (update &&
-			   (write_in_full(lock->lk->fd,
+			   (write_in_full(lock->lk->tempfile.fd,
 				sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
-			 write_str_in_full(lock->lk->fd, "\n") != 1 ||
+			 write_str_in_full(lock->lk->tempfile.fd, "\n") != 1 ||
 			 close_ref(lock) < 0)) {
 			status |= error("couldn't write %s",
-					lock->lk->filename.buf);
+					lock->lk->tempfile.filename.buf);
 			rollback_lock_file(&reflog_lock);
 		} else if (commit_lock_file(&reflog_lock)) {
 			status |= error("unable to commit reflog '%s' (%s)",
diff --git a/rerere.c b/rerere.c
index 94aea9a..c435668 100644
--- a/rerere.c
+++ b/rerere.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "string-list.h"
 #include "rerere.h"
diff --git a/sequencer.c b/sequencer.c
index c4f4b7d..d85765f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "sequencer.h"
 #include "dir.h"
diff --git a/sha1_file.c b/sha1_file.c
index 7e38148..1e67e16 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -8,6 +8,7 @@
  */
 #include "cache.h"
 #include "string-list.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "delta.h"
 #include "pack.h"
diff --git a/shallow.c b/shallow.c
index 257d811..59ee321 100644
--- a/shallow.c
+++ b/shallow.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "commit.h"
 #include "tag.h"
@@ -267,8 +268,8 @@ void setup_alternate_shallow(struct lock_file *shallow_lock,
 	if (write_shallow_commits(&sb, 0, extra)) {
 		if (write_in_full(fd, sb.buf, sb.len) != sb.len)
 			die_errno("failed to write to %s",
-				  shallow_lock->filename.buf);
-		*alternate_shallow_file = shallow_lock->filename.buf;
+				  shallow_lock->tempfile.filename.buf);
+		*alternate_shallow_file = shallow_lock->tempfile.filename.buf;
 	} else
 		/*
 		 * is_repository_shallow() sees empty string as "no
@@ -314,7 +315,7 @@ void prune_shallow(int show_only)
 	if (write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY)) {
 		if (write_in_full(fd, sb.buf, sb.len) != sb.len)
 			die_errno("failed to write to %s",
-				  shallow_lock.filename.buf);
+				  shallow_lock.tempfile.filename.buf);
 		commit_lock_file(&shallow_lock);
 	} else {
 		unlink(git_path("shallow"));
diff --git a/tempfile.c b/tempfile.c
new file mode 100644
index 0000000..bde7fd3
--- /dev/null
+++ b/tempfile.c
@@ -0,0 +1,166 @@
+#include "cache.h"
+#include "tempfile.h"
+#include "sigchain.h"
+
+static struct tempfile *volatile tempfile_list;
+
+static void remove_tempfiles(int skip_fclose)
+{
+	pid_t me = getpid();
+
+	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;
+	}
+}
+
+static void remove_tempfiles_on_exit(void)
+{
+	remove_tempfiles(0);
+}
+
+static void remove_tempfiles_on_signal(int signo)
+{
+	remove_tempfiles(1);
+	sigchain_pop(signo);
+	raise(signo);
+}
+
+/* Make sure errno contains a meaningful value on error */
+int create_tempfile(struct tempfile *tempfile, const char *path)
+{
+	size_t pathlen = strlen(path);
+
+	if (!tempfile_list) {
+		/* One-time initialization */
+		sigchain_push_common(remove_tempfiles_on_signal);
+		atexit(remove_tempfiles_on_exit);
+	}
+
+	if (tempfile->active)
+		die("BUG: cannot tempfile(\"%s\") using active struct tempfile",
+		    path);
+	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, pathlen);
+		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: tempfile(\"%s\") called with improperly-reset tempfile object",
+		    path);
+	}
+
+	strbuf_add_absolute_path(&tempfile->filename, path);
+	tempfile->fd = open(tempfile->filename.buf, O_RDWR | O_CREAT | O_EXCL, 0666);
+	if (tempfile->fd < 0) {
+		strbuf_reset(&tempfile->filename);
+		return -1;
+	}
+	tempfile->owner = getpid();
+	tempfile->active = 1;
+	if (adjust_shared_perm(tempfile->filename.buf)) {
+		int save_errno = errno;
+		error("cannot fix permission bits on %s", tempfile->filename.buf);
+		delete_tempfile(tempfile);
+		errno = save_errno;
+		return -1;
+	}
+	return tempfile->fd;
+}
+
+FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode)
+{
+	if (!tempfile->active)
+		die("BUG: fdopen_tempfile() called for unlocked object");
+	if (tempfile->fp)
+		die("BUG: fdopen_tempfile() called twice for file '%s'",
+		    tempfile->filename.buf);
+
+	tempfile->fp = fdopen(tempfile->fd, mode);
+	return tempfile->fp;
+}
+
+int close_tempfile(struct tempfile *tempfile)
+{
+	int fd = tempfile->fd;
+	FILE *fp = tempfile->fp;
+	int err;
+
+	if (fd < 0)
+		return 0;
+
+	tempfile->fd = -1;
+	if (fp) {
+		tempfile->fp = NULL;
+
+		/*
+		 * Note: no short-circuiting here; we want to fclose()
+		 * in any case!
+		 */
+		err = ferror(fp) | fclose(fp);
+	} else {
+		err = close(fd);
+	}
+
+	if (err) {
+		int save_errno = errno;
+		delete_tempfile(tempfile);
+		errno = save_errno;
+		return -1;
+	}
+
+	return 0;
+}
+
+int reopen_tempfile(struct tempfile *tempfile)
+{
+	if (0 <= tempfile->fd)
+		die(_("BUG: reopen a temporary file that is still open"));
+	if (!tempfile->active)
+		die(_("BUG: reopen a temporary file that has been removed"));
+	tempfile->fd = open(tempfile->filename.buf, O_WRONLY);
+	return tempfile->fd;
+}
+
+int rename_tempfile(struct tempfile *tempfile, const char *path)
+{
+	if (!tempfile->active)
+		die("BUG: attempt to rename inactive temporary file to \"%s\"", path);
+
+	if (close_tempfile(tempfile))
+		return -1;
+
+	if (rename(tempfile->filename.buf, path)) {
+		int save_errno = errno;
+		delete_tempfile(tempfile);
+		errno = save_errno;
+		return -1;
+	}
+
+	tempfile->active = 0;
+	strbuf_reset(&tempfile->filename);
+	return 0;
+}
+
+void delete_tempfile(struct tempfile *tempfile)
+{
+	if (!tempfile->active)
+		return;
+
+	if (!close_tempfile(tempfile)) {
+		unlink_or_warn(tempfile->filename.buf);
+		tempfile->active = 0;
+		strbuf_reset(&tempfile->filename);
+	}
+}
diff --git a/tempfile.h b/tempfile.h
new file mode 100644
index 0000000..f83deac
--- /dev/null
+++ b/tempfile.h
@@ -0,0 +1,206 @@
+#ifndef TEMPFILE_H
+#define TEMPFILE_H
+
+/*
+ * Handle temporary files.
+ *
+ * The tempfile API allows temporary files to be created, deleted, and
+ * atomically renamed. Temporary files that are still active when the
+ * program ends are cleaned up automatically. Lockfiles (see
+ * "lockfile.h") are built on top of this API.
+ *
+ *
+ * Calling sequence
+ * ----------------
+ *
+ * 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()`.
+ *
+ * * Writes new content to the file by either:
+ *
+ *   * writing to the file descriptor returned by `create_tempfile()`
+ *     (also available via `tempfile->fd`).
+ *
+ *   * calling `fdopen_tempfile()` to get a `FILE` pointer for the
+ *     open file and writing to the file using stdio.
+ *
+ * When finished writing, the caller can:
+ *
+ * * Close the file descriptor and remove the temporary file by
+ *   calling `delete_tempfile()`.
+ *
+ * * Close the temporary file and rename it atomically to a specified
+ *   filename by calling `rename_tempfile()`. This relinquishes
+ *   control of the file.
+ *
+ * * Close the file descriptor without removing or renaming the
+ *   temporary file by calling `close_tempfile()`, 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()`.
+ *
+ * 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)`
+ * 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,
+ * if you close and then later open another file descriptor for a
+ * completely different purpose, then the unrelated file descriptor
+ * might get closed.
+ *
+ *
+ * State diagram and cleanup
+ * -------------------------
+ *
+ * If the program exits while a temporary file is active, we want to
+ * make sure that we remove it. This is done by remembering the active
+ * temporary files in a linked list, `tempfile_list`. An `atexit(3)`
+ * handler and a signal handler are registered, to clean up any active
+ * temporary files.
+ *
+ * Because the signal handler can run at any time, `tempfile_list` and
+ * the `tempfile` objects that comprise it must be kept in
+ * self-consistent states at all times.
+ *
+ * The possible states of a `tempfile` object are as follows:
+ *
+ * - Uninitialized. In this state the object's `on_list` field must be
+ *   zero but the rest of its contents need not be initialized. As
+ *   soon as the object is used in any way, it is irrevocably
+ *   registered in `tempfile_list`, and `on_list` is set.
+ *
+ * - Active, file open (after `create_tempfile()` or
+ *   `reopen_tempfile()`). In this state:
+ *
+ *   - the temporary file exists
+ *   - `active` is set
+ *   - `filename` holds the filename of the temporary file
+ *   - `fd` holds a file descriptor open for writing to it
+ *   - `fp` holds a pointer to an open `FILE` object if and only if
+ *     `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
+ *   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:
+ *
+ *   - `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.
+ *
+ * A temporary file is owned by the process that created it. The
+ * `tempfile` has an `owner` field that records the owner's PID. This
+ * field is used to prevent a forked process from deleting a temporary
+ * file created by its parent.
+ *
+ *
+ * Error handling
+ * --------------
+ *
+ * `create_tempfile()` returns a file descriptor on success or -1 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.
+ */
+
+struct tempfile {
+	struct tempfile *volatile next;
+	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.
+ */
+extern int create_tempfile(struct tempfile *tempfile, const char *path);
+
+/*
+ * 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
+ * when the file is deleted or renamed.
+ */
+extern FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode);
+
+/*
+ * 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.
+ */
+extern int close_tempfile(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
+ * to implement a sequence of operations like the following:
+ *
+ * * Create temporary file.
+ *
+ * * Write new contents to file, then `close_tempfile()` to cause the
+ *   contents to be written to disk.
+ *
+ * * Pass the name of the temporary file to another program to allow
+ *   it (and nobody else) to inspect or even modify the file's
+ *   contents.
+ *
+ * * `reopen_tempfile()` to reopen the temporary file. Make further
+ *   updates to the contents.
+ *
+ * * `rename_tempfile()` to move the file to its permanent location.
+ */
+extern int reopen_tempfile(struct tempfile *tempfile);
+
+/*
+ * Close the file descriptor and/or file pointer and remove the
+ * temporary file associated with `tempfile`. It is a NOOP to call
+ * `delete_tempfile()` for a `tempfile` object that has already been
+ * deleted or renamed.
+ */
+extern void delete_tempfile(struct tempfile *tempfile);
+
+/*
+ * Close the file descriptor and/or file pointer if they are still
+ * open, and atomically rename the temporary file to `path`. `path`
+ * must be on the same filesystem as the lock file. Return 0 on
+ * success. On failure, delete the temporary file and return -1, with
+ * `errno` set to the value from the failing call to `close(2)` or
+ * `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);
+
+#endif /* TEMPFILE_H */
diff --git a/test-scrap-cache-tree.c b/test-scrap-cache-tree.c
index 6efee31..c21e575 100644
--- a/test-scrap-cache-tree.c
+++ b/test-scrap-cache-tree.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "tempfile.h"
 #include "lockfile.h"
 #include "tree.h"
 #include "cache-tree.h"
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 03/14] lockfile: remove some redundant functions
  2015-06-08  9:07 [PATCH 00/14] Introduce a tempfile module Michael Haggerty
  2015-06-08  9:07 ` [PATCH 01/14] Move lockfile API documentation to lockfile.h Michael Haggerty
  2015-06-08  9:07 ` [PATCH 02/14] tempfile: a new module for handling temporary files Michael Haggerty
@ 2015-06-08  9:07 ` Michael Haggerty
  2015-06-10 17:40   ` Junio C Hamano
  2015-06-08  9:07 ` [PATCH 04/14] commit_lock_file(): use get_locked_file_path() Michael Haggerty
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 26+ messages in thread
From: Michael Haggerty @ 2015-06-08  9:07 UTC (permalink / raw)
  To: Junio C Hamano, Jeff King; +Cc: git, Michael Haggerty

Remove the following functions and rewrite their callers to use the
equivalent tempfile functions directly:

* fdopen_lock_file() -> fdopen_tempfile()
* reopen_lock_file() -> reopen_tempfile()
* close_lock_file() -> close_tempfile()

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/commit.c |  2 +-
 fast-import.c    |  2 +-
 lockfile.c       | 15 ------------
 lockfile.h       | 70 ++++++++++++--------------------------------------------
 read-cache.c     |  2 +-
 refs.c           |  8 +++----
 6 files changed, 22 insertions(+), 77 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index c9fbe42..970565c 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -358,7 +358,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
 		discard_cache();
 		read_cache_from(index_lock.tempfile.filename.buf);
 		if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
-			if (reopen_lock_file(&index_lock) < 0)
+			if (reopen_tempfile(&index_lock.tempfile) < 0)
 				die(_("unable to write index file"));
 			if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
 				die(_("unable to update temporary index"));
diff --git a/fast-import.c b/fast-import.c
index 32a3e21..ca30fe9 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -1807,7 +1807,7 @@ static void dump_marks(void)
 		return;
 	}
 
-	f = fdopen_lock_file(&mark_lock, "w");
+	f = fdopen_tempfile(&mark_lock.tempfile, "w");
 	if (!f) {
 		int saved_errno = errno;
 		rollback_lock_file(&mark_lock);
diff --git a/lockfile.c b/lockfile.c
index b453016..c2d6ad1 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -228,11 +228,6 @@ int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags)
 	return fd;
 }
 
-FILE *fdopen_lock_file(struct lock_file *lk, const char *mode)
-{
-	return fdopen_tempfile(&lk->tempfile, mode);
-}
-
 char *get_locked_file_path(struct lock_file *lk)
 {
 	if (!lk->tempfile.active)
@@ -242,16 +237,6 @@ char *get_locked_file_path(struct lock_file *lk)
 	return xmemdupz(lk->tempfile.filename.buf, lk->tempfile.filename.len - LOCK_SUFFIX_LEN);
 }
 
-int close_lock_file(struct lock_file *lk)
-{
-	return close_tempfile(&lk->tempfile);
-}
-
-int reopen_lock_file(struct lock_file *lk)
-{
-	return reopen_tempfile(&lk->tempfile);
-}
-
 int commit_lock_file_to(struct lock_file *lk, const char *path)
 {
 	return rename_tempfile(&lk->tempfile, path);
diff --git a/lockfile.h b/lockfile.h
index 5b313ef..3a212e9 100644
--- a/lockfile.h
+++ b/lockfile.h
@@ -53,8 +53,8 @@
  *     `hold_lock_file_for_*()` functions (also available via
  *     `lock->fd`).
  *
- *   * calling `fdopen_lock_file()` to get a `FILE` pointer for the
- *     open file and writing to the file using stdio.
+ *   * calling `fdopen_tempfile(lk->tempfile)` to get a `FILE` pointer
+ *     for the open file and writing to the file using stdio.
  *
  * When finished writing, the caller can:
  *
@@ -65,10 +65,16 @@
  * * Close the file descriptor and remove the lockfile by calling
  *   `rollback_lock_file()`.
  *
- * * Close the file descriptor without removing or renaming the
- *   lockfile by calling `close_lock_file()`, and later call
- *   `commit_lock_file()`, `commit_lock_file_to()`,
- *   `rollback_lock_file()`, or `reopen_lock_file()`.
+ * It is also permissable to call the following functions on the
+ * underlying tempfile object:
+ *
+ * * close_tempfile(lk->tempfile)
+ *
+ * * reopen_tempfile(lk->tempfile)
+ *
+ * * fdopen_tempfile(lk->tempfile, mode)
+ *
+ * See "tempfile.h" for more information.
  *
  * Even after the lockfile is committed or rolled back, the
  * `lock_file` object must not be freed or altered by the caller.
@@ -80,11 +86,6 @@
  * tempfile module will close and remove the lockfile, thereby rolling
  * back any uncommitted changes.
  *
- * 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.
- *
- *
  * Under the covers, a lockfile is just a tempfile with a few helper
  * functions. In particular, the state diagram and the cleanup
  * machinery are all implemented in the tempfile module.
@@ -99,10 +100,9 @@
  * failure. Errors can be reported by passing `errno` to
  * `unable_to_lock_message()` or `unable_to_lock_die()`.
  *
- * 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.
+ * Similarly, `commit_lock_file` and `commit_lock_file_to` return 0 on
+ * success. On failure they set `errno` appropriately, do their best
+ * to roll back the lockfile, and return -1.
  */
 
 struct lock_file {
@@ -192,52 +192,12 @@ extern void unable_to_lock_message(const char *path, int err,
 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.
- */
-extern FILE *fdopen_lock_file(struct lock_file *lk, const char *mode);
-
-/*
  * Return the path of the file that is locked by the specified
  * lock_file object. The caller must free the memory.
  */
 extern char *get_locked_file_path(struct lock_file *lk);
 
 /*
- * If the lockfile is still open, close it (and the file pointer if it
- * has been opened using `fdopen_lock_file()`) without renaming the
- * 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.
- */
-extern int close_lock_file(struct lock_file *lk);
-
-/*
- * Re-open a lockfile that has been closed using `close_lock_file()`
- * 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
- *   cause the contents to be written to disk.
- *
- * * Pass the name of the lockfile to another program to allow it (and
- *   nobody else) to inspect the contents you wrote, while still
- *   holding the lock yourself.
- *
- * * `reopen_lock_file()` to reopen the lockfile. Make further updates
- *   to the contents.
- *
- * * `commit_lock_file()` to make the final version permanent.
- */
-extern int reopen_lock_file(struct lock_file *lk);
-
-/*
  * Commit the change represented by `lk`: close the file descriptor
  * and/or file pointer if they are still open and rename the lockfile
  * to its final destination. Return 0 upon success. On failure, roll
diff --git a/read-cache.c b/read-cache.c
index e20e003..3e49c49 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -2121,7 +2121,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_tempfile(&lock->tempfile);
 	else
 		return ret;
 }
diff --git a/refs.c b/refs.c
index d98b0f5..b46deba 100644
--- a/refs.c
+++ b/refs.c
@@ -2549,7 +2549,7 @@ int commit_packed_refs(void)
 	if (!packed_ref_cache->lock)
 		die("internal error: packed-refs not locked");
 
-	out = fdopen_lock_file(packed_ref_cache->lock, "w");
+	out = fdopen_tempfile(&packed_ref_cache->lock->tempfile, "w");
 	if (!out)
 		die_errno("unable to fdopen packed-refs descriptor");
 
@@ -2984,7 +2984,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
 
 static int close_ref(struct ref_lock *lock)
 {
-	if (close_lock_file(lock->lk))
+	if (close_tempfile(&lock->lk->tempfile))
 		return -1;
 	return 0;
 }
@@ -4237,7 +4237,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
 			strbuf_release(&err);
 			goto failure;
 		}
-		cb.newlog = fdopen_lock_file(&reflog_lock, "w");
+		cb.newlog = fdopen_tempfile(&reflog_lock.tempfile, "w");
 		if (!cb.newlog) {
 			error("cannot fdopen %s (%s)",
 			      reflog_lock.tempfile.filename.buf, strerror(errno));
@@ -4261,7 +4261,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
 			!(type & REF_ISSYMREF) &&
 			!is_null_sha1(cb.last_kept_sha1);
 
-		if (close_lock_file(&reflog_lock)) {
+		if (close_tempfile(&reflog_lock.tempfile)) {
 			status |= error("couldn't write %s: %s", log_file,
 					strerror(errno));
 		} else if (update &&
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 04/14] commit_lock_file(): use get_locked_file_path()
  2015-06-08  9:07 [PATCH 00/14] Introduce a tempfile module Michael Haggerty
                   ` (2 preceding siblings ...)
  2015-06-08  9:07 ` [PATCH 03/14] lockfile: remove some redundant functions Michael Haggerty
@ 2015-06-08  9:07 ` Michael Haggerty
  2015-06-08  9:07 ` [PATCH 05/14] register_tempfile_object(): new function, extracted from create_tempfile() Michael Haggerty
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 26+ messages in thread
From: Michael Haggerty @ 2015-06-08  9:07 UTC (permalink / raw)
  To: Junio C Hamano, Jeff King; +Cc: git, Michael Haggerty

First beef up the sanity checking in get_locked_file_path() to match
that in commit_lock_file(). Then rewrite commit_lock_file() to use
get_locked_file_path() for its pathname computation.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 lockfile.c | 30 +++++++++++++-----------------
 1 file changed, 13 insertions(+), 17 deletions(-)

diff --git a/lockfile.c b/lockfile.c
index c2d6ad1..7d04ed1 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -232,8 +232,11 @@ char *get_locked_file_path(struct lock_file *lk)
 {
 	if (!lk->tempfile.active)
 		die("BUG: get_locked_file_path() called for unlocked object");
-	if (lk->tempfile.filename.len <= LOCK_SUFFIX_LEN)
+	if (lk->tempfile.filename.len <= LOCK_SUFFIX_LEN ||
+	    strcmp(lk->tempfile.filename.buf + lk->tempfile.filename.len - LOCK_SUFFIX_LEN,
+		   LOCK_SUFFIX))
 		die("BUG: get_locked_file_path() called for malformed lock object");
+	/* remove ".lock": */
 	return xmemdupz(lk->tempfile.filename.buf, lk->tempfile.filename.len - LOCK_SUFFIX_LEN);
 }
 
@@ -244,23 +247,16 @@ int commit_lock_file_to(struct lock_file *lk, const char *path)
 
 int commit_lock_file(struct lock_file *lk)
 {
-	static struct strbuf result_file = STRBUF_INIT;
-	int err;
+	char *result_path = get_locked_file_path(lk);
 
-	if (!lk->tempfile.active)
-		die("BUG: attempt to commit unlocked object");
-
-	if (lk->tempfile.filename.len <= LOCK_SUFFIX_LEN ||
-	    strcmp(lk->tempfile.filename.buf + lk->tempfile.filename.len - LOCK_SUFFIX_LEN,
-		   LOCK_SUFFIX))
-		die("BUG: lockfile filename corrupt");
-
-	/* remove ".lock": */
-	strbuf_add(&result_file, lk->tempfile.filename.buf,
-		   lk->tempfile.filename.len - LOCK_SUFFIX_LEN);
-	err = commit_lock_file_to(lk, result_file.buf);
-	strbuf_reset(&result_file);
-	return err;
+	if (commit_lock_file_to(lk, result_path)) {
+		int save_errno = errno;
+		free(result_path);
+		errno = save_errno;
+		return -1;
+	}
+	free(result_path);
+	return 0;
 }
 
 void rollback_lock_file(struct lock_file *lk)
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 05/14] register_tempfile_object(): new function, extracted from create_tempfile()
  2015-06-08  9:07 [PATCH 00/14] Introduce a tempfile module Michael Haggerty
                   ` (3 preceding siblings ...)
  2015-06-08  9:07 ` [PATCH 04/14] commit_lock_file(): use get_locked_file_path() Michael Haggerty
@ 2015-06-08  9:07 ` Michael Haggerty
  2015-06-08  9:07 ` [PATCH 06/14] tempfile: add several functions for creating temporary files Michael Haggerty
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 26+ messages in thread
From: Michael Haggerty @ 2015-06-08  9:07 UTC (permalink / raw)
  To: Junio C Hamano, Jeff King; +Cc: git, Michael Haggerty

This makes the next step easier.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 tempfile.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/tempfile.c b/tempfile.c
index bde7fd3..f76bc07 100644
--- a/tempfile.c
+++ b/tempfile.c
@@ -31,11 +31,8 @@ static void remove_tempfiles_on_signal(int signo)
 	raise(signo);
 }
 
-/* Make sure errno contains a meaningful value on error */
-int create_tempfile(struct tempfile *tempfile, const char *path)
+static void register_tempfile_object(struct tempfile *tempfile, const char *path)
 {
-	size_t pathlen = strlen(path);
-
 	if (!tempfile_list) {
 		/* One-time initialization */
 		sigchain_push_common(remove_tempfiles_on_signal);
@@ -51,7 +48,7 @@ int create_tempfile(struct tempfile *tempfile, const char *path)
 		tempfile->fp = NULL;
 		tempfile->active = 0;
 		tempfile->owner = 0;
-		strbuf_init(&tempfile->filename, pathlen);
+		strbuf_init(&tempfile->filename, strlen(path));
 		tempfile->next = tempfile_list;
 		tempfile_list = tempfile;
 		tempfile->on_list = 1;
@@ -60,6 +57,12 @@ int create_tempfile(struct tempfile *tempfile, const char *path)
 		die("BUG: tempfile(\"%s\") called with improperly-reset tempfile object",
 		    path);
 	}
+}
+
+/* Make sure errno contains a meaningful value on error */
+int create_tempfile(struct tempfile *tempfile, const char *path)
+{
+	register_tempfile_object(tempfile, path);
 
 	strbuf_add_absolute_path(&tempfile->filename, path);
 	tempfile->fd = open(tempfile->filename.buf, O_RDWR | O_CREAT | O_EXCL, 0666);
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 06/14] tempfile: add several functions for creating temporary files
  2015-06-08  9:07 [PATCH 00/14] Introduce a tempfile module Michael Haggerty
                   ` (4 preceding siblings ...)
  2015-06-08  9:07 ` [PATCH 05/14] register_tempfile_object(): new function, extracted from create_tempfile() Michael Haggerty
@ 2015-06-08  9:07 ` Michael Haggerty
  2015-06-10 17:48   ` Junio C Hamano
  2015-06-08  9:07 ` [PATCH 07/14] register_tempfile(): new function to handle an existing temporary file Michael Haggerty
                   ` (8 subsequent siblings)
  14 siblings, 1 reply; 26+ messages in thread
From: Michael Haggerty @ 2015-06-08  9:07 UTC (permalink / raw)
  To: Junio C Hamano, Jeff King; +Cc: git, Michael Haggerty

Add several functions for creating temporary files with
automatically-generated names, analogous to mkstemps(), but also
arranging for the files to be deleted on program exit.

The functions are named according to a pattern depending how they
operate. They will be used to replace many places in the code where
temporary files are created and cleaned up ad-hoc.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 tempfile.c | 55 ++++++++++++++++++++++++++++++++++-
 tempfile.h | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 150 insertions(+), 1 deletion(-)

diff --git a/tempfile.c b/tempfile.c
index f76bc07..890075f 100644
--- a/tempfile.c
+++ b/tempfile.c
@@ -48,7 +48,7 @@ static void register_tempfile_object(struct tempfile *tempfile, const char *path
 		tempfile->fp = NULL;
 		tempfile->active = 0;
 		tempfile->owner = 0;
-		strbuf_init(&tempfile->filename, strlen(path));
+		strbuf_init(&tempfile->filename, 0);
 		tempfile->next = tempfile_list;
 		tempfile_list = tempfile;
 		tempfile->on_list = 1;
@@ -82,6 +82,59 @@ int create_tempfile(struct tempfile *tempfile, const char *path)
 	return tempfile->fd;
 }
 
+int mks_tempfile_sm(struct tempfile *tempfile,
+		    const char *template, int suffixlen, int mode)
+{
+	register_tempfile_object(tempfile, template);
+
+	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;
+	}
+	tempfile->owner = getpid();
+	tempfile->active = 1;
+	return tempfile->fd;
+}
+
+int mks_tempfile_tsm(struct tempfile *tempfile,
+		     const char *template, int suffixlen, int mode)
+{
+	const char *tmpdir;
+
+	register_tempfile_object(tempfile, template);
+
+	tmpdir = getenv("TMPDIR");
+	if (!tmpdir)
+		tmpdir = "/tmp";
+
+	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;
+	}
+	tempfile->owner = getpid();
+	tempfile->active = 1;
+	return tempfile->fd;
+}
+
+int xmks_tempfile_m(struct tempfile *tempfile, const char *template, int mode)
+{
+	int fd;
+	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)
+		die_errno("Unable to create temporary file '%s'",
+			  full_template.buf);
+
+	strbuf_release(&full_template);
+	return fd;
+}
+
 FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode)
 {
 	if (!tempfile->active)
diff --git a/tempfile.h b/tempfile.h
index f83deac..6276156 100644
--- a/tempfile.h
+++ b/tempfile.h
@@ -145,6 +145,102 @@ struct tempfile {
  */
 extern int create_tempfile(struct tempfile *tempfile, const char *path);
 
+
+/*
+ * mks_tempfile functions
+ *
+ * The following functions attempt to create and open temporary files
+ * with names derived automatically from a template, in the manner of
+ * mkstemps(), and arrange for them to be deleted if the program ends
+ * before they are deleted explicitly. There is a whole family of such
+ * functions, named according to the following pattern:
+ *
+ *     x?mks_tempfile_t?s?m?()
+ *
+ * The optional letters have the following meanings:
+ *
+ *   x - die if the temporary file cannot be created.
+ *
+ *   t - create the temporary file under $TMPDIR (as opposed to
+ *       relative to the current directory). When these variants are
+ *       used, template should be the pattern for the filename alone,
+ *       without a path.
+ *
+ *   s - template includes a suffix that is suffixlen characters long.
+ *
+ *   m - the temporary file should be created with the specified mode
+ *       (otherwise, the mode is set to 0600).
+ *
+ * None of these functions modify template. If the caller wants to
+ * 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).
+ */
+
+/* See "mks_tempfile functions" above. */
+extern int mks_tempfile_sm(struct tempfile *tempfile,
+			   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)
+{
+	return mks_tempfile_sm(tempfile, template, suffixlen, 0600);
+}
+
+/* See "mks_tempfile functions" above. */
+static inline int mks_tempfile_m(struct tempfile *tempfile,
+				 const char *template, int mode)
+{
+	return mks_tempfile_sm(tempfile, template, 0, mode);
+}
+
+/* See "mks_tempfile functions" above. */
+static inline int mks_tempfile(struct tempfile *tempfile,
+			       const char *template)
+{
+	return mks_tempfile_sm(tempfile, template, 0, 0600);
+}
+
+/* See "mks_tempfile functions" above. */
+extern int mks_tempfile_tsm(struct tempfile *tempfile,
+			    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)
+{
+	return mks_tempfile_tsm(tempfile, template, suffixlen, 0600);
+}
+
+/* See "mks_tempfile functions" above. */
+static inline int mks_tempfile_tm(struct tempfile *tempfile,
+				  const char *template, int mode)
+{
+	return mks_tempfile_tsm(tempfile, template, 0, mode);
+}
+
+/* See "mks_tempfile functions" above. */
+static inline int mks_tempfile_t(struct tempfile *tempfile,
+				 const char *template)
+{
+	return mks_tempfile_tsm(tempfile, template, 0, 0600);
+}
+
+/* See "mks_tempfile functions" above. */
+extern int xmks_tempfile_m(struct tempfile *tempfile,
+			   const char *template, int mode);
+
+/* See "mks_tempfile functions" above. */
+static inline int xmks_tempfile(struct tempfile *tempfile,
+				const char *template)
+{
+	return xmks_tempfile_m(tempfile, template, 0600);
+}
+
 /*
  * Associate a stdio stream with the temporary file (which must still
  * be open). Return `NULL` (*without* deleting the file) on error. The
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 07/14] register_tempfile(): new function to handle an existing temporary file
  2015-06-08  9:07 [PATCH 00/14] Introduce a tempfile module Michael Haggerty
                   ` (5 preceding siblings ...)
  2015-06-08  9:07 ` [PATCH 06/14] tempfile: add several functions for creating temporary files Michael Haggerty
@ 2015-06-08  9:07 ` Michael Haggerty
  2015-06-10 17:55   ` Junio C Hamano
  2015-06-08  9:07 ` [PATCH 08/14] write_shared_index(): use tempfile module Michael Haggerty
                   ` (7 subsequent siblings)
  14 siblings, 1 reply; 26+ messages in thread
From: Michael Haggerty @ 2015-06-08  9:07 UTC (permalink / raw)
  To: Junio C Hamano, Jeff King; +Cc: git, Michael Haggerty

Allow an existing file to be registered with the tempfile-handling
infrastructure; in particular, arrange for it to be deleted on program
exit.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 tempfile.c | 9 +++++++++
 tempfile.h | 8 ++++++++
 2 files changed, 17 insertions(+)

diff --git a/tempfile.c b/tempfile.c
index 890075f..235fc85 100644
--- a/tempfile.c
+++ b/tempfile.c
@@ -82,6 +82,15 @@ int create_tempfile(struct tempfile *tempfile, const char *path)
 	return tempfile->fd;
 }
 
+void register_tempfile(struct tempfile *tempfile, const char *path)
+{
+	register_tempfile_object(tempfile, path);
+
+	strbuf_add_absolute_path(&tempfile->filename, path);
+	tempfile->owner = getpid();
+	tempfile->active = 1;
+}
+
 int mks_tempfile_sm(struct tempfile *tempfile,
 		    const char *template, int suffixlen, int mode)
 {
diff --git a/tempfile.h b/tempfile.h
index 6276156..18ff963 100644
--- a/tempfile.h
+++ b/tempfile.h
@@ -145,6 +145,14 @@ struct tempfile {
  */
 extern int create_tempfile(struct tempfile *tempfile, const char *path);
 
+/*
+ * Register an existing file as a tempfile, meaning that it will be
+ * deleted when the program exits. The tempfile is considered closed,
+ * 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);
+
 
 /*
  * mks_tempfile functions
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 08/14] write_shared_index(): use tempfile module
  2015-06-08  9:07 [PATCH 00/14] Introduce a tempfile module Michael Haggerty
                   ` (6 preceding siblings ...)
  2015-06-08  9:07 ` [PATCH 07/14] register_tempfile(): new function to handle an existing temporary file Michael Haggerty
@ 2015-06-08  9:07 ` Michael Haggerty
  2015-06-10 17:56   ` Junio C Hamano
  2015-06-08  9:07 ` [PATCH 09/14] setup_temporary_shallow(): " Michael Haggerty
                   ` (6 subsequent siblings)
  14 siblings, 1 reply; 26+ messages in thread
From: Michael Haggerty @ 2015-06-08  9:07 UTC (permalink / raw)
  To: Junio C Hamano, Jeff King; +Cc: git, Michael Haggerty

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 read-cache.c | 37 +++++--------------------------------
 1 file changed, 5 insertions(+), 32 deletions(-)

diff --git a/read-cache.c b/read-cache.c
index 3e49c49..4f7b70f 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -2137,54 +2137,27 @@ static int write_split_index(struct index_state *istate,
 	return ret;
 }
 
-static char *temporary_sharedindex;
-
-static void remove_temporary_sharedindex(void)
-{
-	if (temporary_sharedindex) {
-		unlink_or_warn(temporary_sharedindex);
-		free(temporary_sharedindex);
-		temporary_sharedindex = NULL;
-	}
-}
-
-static void remove_temporary_sharedindex_on_signal(int signo)
-{
-	remove_temporary_sharedindex();
-	sigchain_pop(signo);
-	raise(signo);
-}
+static struct tempfile temporary_sharedindex;
 
 static int write_shared_index(struct index_state *istate,
 			      struct lock_file *lock, unsigned flags)
 {
 	struct split_index *si = istate->split_index;
-	static int installed_handler;
 	int fd, ret;
 
-	temporary_sharedindex = git_pathdup("sharedindex_XXXXXX");
-	fd = mkstemp(temporary_sharedindex);
+	fd = mks_tempfile(&temporary_sharedindex, git_path("sharedindex_XXXXXX"));
 	if (fd < 0) {
-		free(temporary_sharedindex);
-		temporary_sharedindex = NULL;
 		hashclr(si->base_sha1);
 		return do_write_locked_index(istate, lock, flags);
 	}
-	if (!installed_handler) {
-		atexit(remove_temporary_sharedindex);
-		sigchain_push_common(remove_temporary_sharedindex_on_signal);
-	}
 	move_cache_to_base_index(istate);
 	ret = do_write_index(si->base, fd, 1);
-	close(fd);
 	if (ret) {
-		remove_temporary_sharedindex();
+		delete_tempfile(&temporary_sharedindex);
 		return ret;
 	}
-	ret = rename(temporary_sharedindex,
-		     git_path("sharedindex.%s", sha1_to_hex(si->base->sha1)));
-	free(temporary_sharedindex);
-	temporary_sharedindex = NULL;
+	ret = rename_tempfile(&temporary_sharedindex,
+			      git_path("sharedindex.%s", sha1_to_hex(si->base->sha1)));
 	if (!ret)
 		hashcpy(si->base_sha1, si->base->sha1);
 	return ret;
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 09/14] setup_temporary_shallow(): use tempfile module
  2015-06-08  9:07 [PATCH 00/14] Introduce a tempfile module Michael Haggerty
                   ` (7 preceding siblings ...)
  2015-06-08  9:07 ` [PATCH 08/14] write_shared_index(): use tempfile module Michael Haggerty
@ 2015-06-08  9:07 ` Michael Haggerty
  2015-06-08  9:07 ` [PATCH 10/14] diff: " Michael Haggerty
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 26+ messages in thread
From: Michael Haggerty @ 2015-06-08  9:07 UTC (permalink / raw)
  To: Junio C Hamano, Jeff King; +Cc: git, Michael Haggerty

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 shallow.c | 34 ++++++----------------------------
 1 file changed, 6 insertions(+), 28 deletions(-)

diff --git a/shallow.c b/shallow.c
index 59ee321..311ba9b 100644
--- a/shallow.c
+++ b/shallow.c
@@ -209,50 +209,28 @@ int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
 	return write_shallow_commits_1(out, use_pack_protocol, extra, 0);
 }
 
-static struct strbuf temporary_shallow = STRBUF_INIT;
-
-static void remove_temporary_shallow(void)
-{
-	if (temporary_shallow.len) {
-		unlink_or_warn(temporary_shallow.buf);
-		strbuf_reset(&temporary_shallow);
-	}
-}
-
-static void remove_temporary_shallow_on_signal(int signo)
-{
-	remove_temporary_shallow();
-	sigchain_pop(signo);
-	raise(signo);
-}
+static struct tempfile temporary_shallow;
 
 const char *setup_temporary_shallow(const struct sha1_array *extra)
 {
 	struct strbuf sb = STRBUF_INIT;
 	int fd;
 
-	if (temporary_shallow.len)
-		die("BUG: attempt to create two temporary shallow files");
-
 	if (write_shallow_commits(&sb, 0, extra)) {
-		strbuf_addstr(&temporary_shallow, git_path("shallow_XXXXXX"));
-		fd = xmkstemp(temporary_shallow.buf);
-
-		atexit(remove_temporary_shallow);
-		sigchain_push_common(remove_temporary_shallow_on_signal);
+		fd = xmks_tempfile(&temporary_shallow, git_path("shallow_XXXXXX"));
 
 		if (write_in_full(fd, sb.buf, sb.len) != sb.len)
 			die_errno("failed to write to %s",
-				  temporary_shallow.buf);
-		close(fd);
+				  temporary_shallow.filename.buf);
+		close_tempfile(&temporary_shallow);
 		strbuf_release(&sb);
-		return temporary_shallow.buf;
+		return temporary_shallow.filename.buf;
 	}
 	/*
 	 * is_repository_shallow() sees empty string as "no shallow
 	 * file".
 	 */
-	return temporary_shallow.buf;
+	return temporary_shallow.filename.buf;
 }
 
 void setup_alternate_shallow(struct lock_file *shallow_lock,
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 10/14] diff: use tempfile module
  2015-06-08  9:07 [PATCH 00/14] Introduce a tempfile module Michael Haggerty
                   ` (8 preceding siblings ...)
  2015-06-08  9:07 ` [PATCH 09/14] setup_temporary_shallow(): " Michael Haggerty
@ 2015-06-08  9:07 ` Michael Haggerty
  2015-06-08  9:07 ` [PATCH 11/14] lock_repo_for_gc(): compute the path to "gc.pid" only once Michael Haggerty
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 26+ messages in thread
From: Michael Haggerty @ 2015-06-08  9:07 UTC (permalink / raw)
  To: Junio C Hamano, Jeff King; +Cc: git, Michael Haggerty

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 diff.c | 29 +++++++----------------------
 1 file changed, 7 insertions(+), 22 deletions(-)

diff --git a/diff.c b/diff.c
index 7500c55..742a842 100644
--- a/diff.c
+++ b/diff.c
@@ -2,6 +2,7 @@
  * Copyright (C) 2005 Junio C Hamano
  */
 #include "cache.h"
+#include "tempfile.h"
 #include "quote.h"
 #include "diff.h"
 #include "diffcore.h"
@@ -312,7 +313,7 @@ static struct diff_tempfile {
 	const char *name; /* filename external diff should read from */
 	char hex[41];
 	char mode[10];
-	char tmp_path[PATH_MAX];
+	struct tempfile tempfile;
 } diff_temp[2];
 
 typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
@@ -564,25 +565,16 @@ static struct diff_tempfile *claim_diff_tempfile(void) {
 	die("BUG: diff is failing to clean up its tempfiles");
 }
 
-static int remove_tempfile_installed;
-
 static void remove_tempfile(void)
 {
 	int i;
 	for (i = 0; i < ARRAY_SIZE(diff_temp); i++) {
-		if (diff_temp[i].name == diff_temp[i].tmp_path)
-			unlink_or_warn(diff_temp[i].name);
+		if (diff_temp[i].tempfile.active)
+			delete_tempfile(&diff_temp[i].tempfile);
 		diff_temp[i].name = NULL;
 	}
 }
 
-static void remove_tempfile_on_signal(int signo)
-{
-	remove_tempfile();
-	sigchain_pop(signo);
-	raise(signo);
-}
-
 static void print_line_count(FILE *file, int count)
 {
 	switch (count) {
@@ -2817,8 +2809,7 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
 	strbuf_addstr(&template, "XXXXXX_");
 	strbuf_addstr(&template, base);
 
-	fd = git_mkstemps(temp->tmp_path, PATH_MAX, template.buf,
-			strlen(base) + 1);
+	fd = mks_tempfile_ts(&temp->tempfile, template.buf, strlen(base) + 1);
 	if (fd < 0)
 		die_errno("unable to create temp-file");
 	if (convert_to_working_tree(path,
@@ -2828,8 +2819,8 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
 	}
 	if (write_in_full(fd, blob, size) != size)
 		die_errno("unable to write temp-file");
-	close(fd);
-	temp->name = temp->tmp_path;
+	close_tempfile(&temp->tempfile);
+	temp->name = temp->tempfile.filename.buf;
 	strcpy(temp->hex, sha1_to_hex(sha1));
 	temp->hex[40] = 0;
 	sprintf(temp->mode, "%06o", mode);
@@ -2854,12 +2845,6 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
 		return temp;
 	}
 
-	if (!remove_tempfile_installed) {
-		atexit(remove_tempfile);
-		sigchain_push_common(remove_tempfile_on_signal);
-		remove_tempfile_installed = 1;
-	}
-
 	if (!S_ISGITLINK(one->mode) &&
 	    (!one->sha1_valid ||
 	     reuse_worktree_file(name, one->sha1, 1))) {
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 11/14] lock_repo_for_gc(): compute the path to "gc.pid" only once
  2015-06-08  9:07 [PATCH 00/14] Introduce a tempfile module Michael Haggerty
                   ` (9 preceding siblings ...)
  2015-06-08  9:07 ` [PATCH 10/14] diff: " Michael Haggerty
@ 2015-06-08  9:07 ` Michael Haggerty
  2015-06-08  9:07 ` [PATCH 12/14] gc: use tempfile module to handle gc.pid file Michael Haggerty
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 26+ messages in thread
From: Michael Haggerty @ 2015-06-08  9:07 UTC (permalink / raw)
  To: Junio C Hamano, Jeff King; +Cc: git, Michael Haggerty

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/gc.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/builtin/gc.c b/builtin/gc.c
index 6e18d35..4dc21b2 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -200,6 +200,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
 	uintmax_t pid;
 	FILE *fp;
 	int fd;
+	char *pidfile_path;
 
 	if (pidfile)
 		/* already locked */
@@ -208,12 +209,13 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
 	if (gethostname(my_host, sizeof(my_host)))
 		strcpy(my_host, "unknown");
 
-	fd = hold_lock_file_for_update(&lock, git_path("gc.pid"),
+	pidfile_path = git_pathdup("gc.pid");
+	fd = hold_lock_file_for_update(&lock, pidfile_path,
 				       LOCK_DIE_ON_ERROR);
 	if (!force) {
 		static char locking_host[128];
 		int should_exit;
-		fp = fopen(git_path("gc.pid"), "r");
+		fp = fopen(pidfile_path, "r");
 		memset(locking_host, 0, sizeof(locking_host));
 		should_exit =
 			fp != NULL &&
@@ -237,6 +239,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
 			if (fd >= 0)
 				rollback_lock_file(&lock);
 			*ret_pid = pid;
+			free(pidfile_path);
 			return locking_host;
 		}
 	}
@@ -247,7 +250,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
 	strbuf_release(&sb);
 	commit_lock_file(&lock);
 
-	pidfile = git_pathdup("gc.pid");
+	pidfile = pidfile_path;
 	sigchain_push_common(remove_pidfile_on_signal);
 	atexit(remove_pidfile);
 
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 12/14] gc: use tempfile module to handle gc.pid file
  2015-06-08  9:07 [PATCH 00/14] Introduce a tempfile module Michael Haggerty
                   ` (10 preceding siblings ...)
  2015-06-08  9:07 ` [PATCH 11/14] lock_repo_for_gc(): compute the path to "gc.pid" only once Michael Haggerty
@ 2015-06-08  9:07 ` Michael Haggerty
  2015-06-08  9:07 ` [PATCH 13/14] credential-cache--daemon: delete socket from main() Michael Haggerty
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 26+ messages in thread
From: Michael Haggerty @ 2015-06-08  9:07 UTC (permalink / raw)
  To: Junio C Hamano, Jeff King; +Cc: git, Michael Haggerty

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/gc.c | 24 ++++--------------------
 1 file changed, 4 insertions(+), 20 deletions(-)

diff --git a/builtin/gc.c b/builtin/gc.c
index 4dc21b2..a340e89 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -43,20 +43,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 char *pidfile;
-
-static void remove_pidfile(void)
-{
-	if (pidfile)
-		unlink(pidfile);
-}
-
-static void remove_pidfile_on_signal(int signo)
-{
-	remove_pidfile();
-	sigchain_pop(signo);
-	raise(signo);
-}
+static struct tempfile pidfile;
 
 static void git_config_date_string(const char *key, const char **output)
 {
@@ -202,7 +189,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
 	int fd;
 	char *pidfile_path;
 
-	if (pidfile)
+	if (pidfile.active)
 		/* already locked */
 		return NULL;
 
@@ -249,11 +236,8 @@ 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);
-
-	pidfile = pidfile_path;
-	sigchain_push_common(remove_pidfile_on_signal);
-	atexit(remove_pidfile);
-
+	register_tempfile(&pidfile, pidfile_path);
+	free(pidfile_path);
 	return NULL;
 }
 
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 13/14] credential-cache--daemon: delete socket from main()
  2015-06-08  9:07 [PATCH 00/14] Introduce a tempfile module Michael Haggerty
                   ` (11 preceding siblings ...)
  2015-06-08  9:07 ` [PATCH 12/14] gc: use tempfile module to handle gc.pid file Michael Haggerty
@ 2015-06-08  9:07 ` Michael Haggerty
  2015-06-08  9:07 ` [PATCH 14/14] credential-cache--daemon: use tempfile module Michael Haggerty
  2015-06-10 18:34 ` [PATCH 00/14] Introduce a " Junio C Hamano
  14 siblings, 0 replies; 26+ messages in thread
From: Michael Haggerty @ 2015-06-08  9:07 UTC (permalink / raw)
  To: Junio C Hamano, Jeff King; +Cc: git, Michael Haggerty

main() is responsible for cleaning up the socket in the case of
errors, so it is reasonable to also make it responsible for cleaning
it up when there are no errors. This change also makes the next step
easier.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 credential-cache--daemon.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
index c2f0049..a671b2b 100644
--- a/credential-cache--daemon.c
+++ b/credential-cache--daemon.c
@@ -221,7 +221,6 @@ static void serve_cache(const char *socket_path, int debug)
 		; /* nothing */
 
 	close(fd);
-	unlink(socket_path);
 }
 
 static const char permissions_advice[] =
@@ -280,5 +279,7 @@ int main(int argc, const char **argv)
 
 	serve_cache(socket_path, debug);
 
+	unlink(socket_path);
+
 	return 0;
 }
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 14/14] credential-cache--daemon: use tempfile module
  2015-06-08  9:07 [PATCH 00/14] Introduce a tempfile module Michael Haggerty
                   ` (12 preceding siblings ...)
  2015-06-08  9:07 ` [PATCH 13/14] credential-cache--daemon: delete socket from main() Michael Haggerty
@ 2015-06-08  9:07 ` Michael Haggerty
  2015-06-10 18:34 ` [PATCH 00/14] Introduce a " Junio C Hamano
  14 siblings, 0 replies; 26+ messages in thread
From: Michael Haggerty @ 2015-06-08  9:07 UTC (permalink / raw)
  To: Junio C Hamano, Jeff King; +Cc: git, Michael Haggerty

Use the tempfile module to ensure that the socket file gets deleted on
program exit.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 credential-cache--daemon.c | 26 ++++++--------------------
 1 file changed, 6 insertions(+), 20 deletions(-)

diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
index a671b2b..eef6fce 100644
--- a/credential-cache--daemon.c
+++ b/credential-cache--daemon.c
@@ -1,23 +1,11 @@
 #include "cache.h"
+#include "tempfile.h"
 #include "credential.h"
 #include "unix-socket.h"
 #include "sigchain.h"
 #include "parse-options.h"
 
-static const char *socket_path;
-
-static void cleanup_socket(void)
-{
-	if (socket_path)
-		unlink(socket_path);
-}
-
-static void cleanup_socket_on_signal(int sig)
-{
-	cleanup_socket();
-	sigchain_pop(sig);
-	raise(sig);
-}
+static struct tempfile socket_file;
 
 struct credential_cache_entry {
 	struct credential item;
@@ -256,6 +244,7 @@ static void check_socket_directory(const char *path)
 
 int main(int argc, const char **argv)
 {
+	const char *socket_path;
 	static const char *usage[] = {
 		"git-credential-cache--daemon [opts] <socket_path>",
 		NULL
@@ -272,14 +261,11 @@ int main(int argc, const char **argv)
 
 	if (!socket_path)
 		usage_with_options(usage, options);
-	check_socket_directory(socket_path);
-
-	atexit(cleanup_socket);
-	sigchain_push_common(cleanup_socket_on_signal);
 
+	check_socket_directory(socket_path);
+	register_tempfile(&socket_file, socket_path);
 	serve_cache(socket_path, debug);
-
-	unlink(socket_path);
+	delete_tempfile(&socket_file);
 
 	return 0;
 }
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH 02/14] tempfile: a new module for handling temporary files
  2015-06-08  9:07 ` [PATCH 02/14] tempfile: a new module for handling temporary files Michael Haggerty
@ 2015-06-10 17:36   ` Junio C Hamano
  2015-06-10 20:56     ` Michael Haggerty
  0 siblings, 1 reply; 26+ messages in thread
From: Junio C Hamano @ 2015-06-10 17:36 UTC (permalink / raw)
  To: Michael Haggerty; +Cc: Jeff King, git

Michael Haggerty <mhagger@alum.mit.edu> writes:

> diff --git a/builtin/add.c b/builtin/add.c
> index df5135b..aaa9ce4 100644
> --- a/builtin/add.c
> +++ b/builtin/add.c
> @@ -5,6 +5,7 @@
>   */
>  #include "cache.h"
>  #include "builtin.h"
> +#include "tempfile.h"
>  #include "lockfile.h"
>  #include "dir.h"
>  #include "pathspec.h"

It is a bit sad that all users of lockfile.h has to include
tempfile.h; even when trying to find out something as basic as the
name of the file on which the lock is held, they need to look at
lk->tempfile.filename and that requires inclusion of tempfile.h

It is a good idea to have tempfile as a separate module, as it
allows new callers to use the same "clean-on-exit" infrastructure on
things that are not locks, i.e. they can include tempfile.h without
having to include lockfile.h, but I have to wonder if it is better
to include tempfile.h from inside lockfile.h (which is alrady done)
and allow users of lockfile API to assume that inclusion will always
stay there.  After all, if they are taking locks, they already know
lk->tempfile is the mechanism through which they need to learn about
various aspects of the underlying files.

> @@ -101,60 +72,17 @@ 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;
>  
> -	if (flags & LOCK_NO_DEREF) {
> -		strbuf_add_absolute_path(&lk->filename, path);
> -	} else {
> -		struct strbuf resolved_path = STRBUF_INIT;
> +	strbuf_addstr(&filename, path);
> +	if (!(flags & LOCK_NO_DEREF))
> +		resolve_symlink(&filename);
>  
> -		strbuf_add(&resolved_path, path, pathlen);
> -		resolve_symlink(&resolved_path);
> -		strbuf_add_absolute_path(&lk->filename, resolved_path.buf);
> -		strbuf_release(&resolved_path);
> -	}
> ...
> -	return lk->fd;
> +	strbuf_addstr(&filename, LOCK_SUFFIX);
> +	fd = create_tempfile(&lk->tempfile, filename.buf);
> +	strbuf_release(&filename);
> +	return fd;
>  }

This was the only part of this entire patch that needed more than
cursory reading ;-) and it looks correct.

Thanks.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 03/14] lockfile: remove some redundant functions
  2015-06-08  9:07 ` [PATCH 03/14] lockfile: remove some redundant functions Michael Haggerty
@ 2015-06-10 17:40   ` Junio C Hamano
  2015-06-10 18:27     ` Johannes Sixt
  0 siblings, 1 reply; 26+ messages in thread
From: Junio C Hamano @ 2015-06-10 17:40 UTC (permalink / raw)
  To: Michael Haggerty; +Cc: Jeff King, git

Michael Haggerty <mhagger@alum.mit.edu> writes:

> Remove the following functions and rewrite their callers to use the
> equivalent tempfile functions directly:
>
> * fdopen_lock_file() -> fdopen_tempfile()
> * reopen_lock_file() -> reopen_tempfile()
> * close_lock_file() -> close_tempfile()

Hmph, 

My knee-jerk reaction was "I thought lockfile abstraction was
fine---why do we expose the implementation detail of the lockfile,
which is now happen to be implemented on top of the tempfile API, to
the callers?"  I guess that was also where my comments on 02/14 "why
do callers have to include tempfile.h separately?" came from.

I'm undecided but would trust your judgement until I read thru to
the end of the series ;-).

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 06/14] tempfile: add several functions for creating temporary files
  2015-06-08  9:07 ` [PATCH 06/14] tempfile: add several functions for creating temporary files Michael Haggerty
@ 2015-06-10 17:48   ` Junio C Hamano
  2015-08-10  3:08     ` Michael Haggerty
  0 siblings, 1 reply; 26+ messages in thread
From: Junio C Hamano @ 2015-06-10 17:48 UTC (permalink / raw)
  To: Michael Haggerty; +Cc: Jeff King, git

Michael Haggerty <mhagger@alum.mit.edu> writes:

> Add several functions for creating temporary files with
> automatically-generated names, analogous to mkstemps(), but also
> arranging for the files to be deleted on program exit.
>
> The functions are named according to a pattern depending how they
> operate. They will be used to replace many places in the code where
> temporary files are created and cleaned up ad-hoc.
>
> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
> ---
>  tempfile.c | 55 ++++++++++++++++++++++++++++++++++-
>  tempfile.h | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 150 insertions(+), 1 deletion(-)
>
> diff --git a/tempfile.c b/tempfile.c
> index f76bc07..890075f 100644
> --- a/tempfile.c
> +++ b/tempfile.c
> @@ -48,7 +48,7 @@ static void register_tempfile_object(struct tempfile *tempfile, const char *path
>  		tempfile->fp = NULL;
>  		tempfile->active = 0;
>  		tempfile->owner = 0;
> -		strbuf_init(&tempfile->filename, strlen(path));
> +		strbuf_init(&tempfile->filename, 0);
>  		tempfile->next = tempfile_list;
>  		tempfile_list = tempfile;
>  		tempfile->on_list = 1;

This probably could have been part of the previous step.  Regardless
of where in the patch series this change is done, I think it makes
sense, as this function does not even know how long the final filename
would be, and strlen(path) is almost always wrong as path is likely to
be relative.

I notice this change makes "path" almost unused in this function,
and the only remaining use is for assert(!tempfile->filename.len).
Perhaps it is not worth passing the "path" parameter?

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 07/14] register_tempfile(): new function to handle an existing temporary file
  2015-06-08  9:07 ` [PATCH 07/14] register_tempfile(): new function to handle an existing temporary file Michael Haggerty
@ 2015-06-10 17:55   ` Junio C Hamano
  2015-08-10  3:40     ` Michael Haggerty
  0 siblings, 1 reply; 26+ messages in thread
From: Junio C Hamano @ 2015-06-10 17:55 UTC (permalink / raw)
  To: Michael Haggerty; +Cc: Jeff King, git

Michael Haggerty <mhagger@alum.mit.edu> writes:

> Allow an existing file to be registered with the tempfile-handling
> infrastructure; in particular, arrange for it to be deleted on program
> exit.
>
> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
> ---

Hmph.  Where does such a tempfile that is not on list come from?

Puzzled, but let's read on---this could for example become an
internal implementation detail for create_tempfile().  Also I cannot
tell which one of register_tempfile() and register_tempfile_object()
I should be calling when updating the implementation of this API
from their names.

> diff --git a/tempfile.c b/tempfile.c
> index 890075f..235fc85 100644
> --- a/tempfile.c
> +++ b/tempfile.c
> @@ -82,6 +82,15 @@ int create_tempfile(struct tempfile *tempfile, const char *path)
>  	return tempfile->fd;
>  }
>  
> +void register_tempfile(struct tempfile *tempfile, const char *path)
> +{
> +	register_tempfile_object(tempfile, path);
> +
> +	strbuf_add_absolute_path(&tempfile->filename, path);
> +	tempfile->owner = getpid();
> +	tempfile->active = 1;
> +}
> +
>  int mks_tempfile_sm(struct tempfile *tempfile,
>  		    const char *template, int suffixlen, int mode)
>  {
> diff --git a/tempfile.h b/tempfile.h
> index 6276156..18ff963 100644
> --- a/tempfile.h
> +++ b/tempfile.h
> @@ -145,6 +145,14 @@ struct tempfile {
>   */
>  extern int create_tempfile(struct tempfile *tempfile, const char *path);
>  
> +/*
> + * Register an existing file as a tempfile, meaning that it will be
> + * deleted when the program exits. The tempfile is considered closed,
> + * 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);
> +
>  
>  /*
>   * mks_tempfile functions

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 08/14] write_shared_index(): use tempfile module
  2015-06-08  9:07 ` [PATCH 08/14] write_shared_index(): use tempfile module Michael Haggerty
@ 2015-06-10 17:56   ` Junio C Hamano
  0 siblings, 0 replies; 26+ messages in thread
From: Junio C Hamano @ 2015-06-10 17:56 UTC (permalink / raw)
  To: Michael Haggerty; +Cc: Jeff King, git

Michael Haggerty <mhagger@alum.mit.edu> writes:

> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
> ---
>  read-cache.c | 37 +++++--------------------------------
>  1 file changed, 5 insertions(+), 32 deletions(-)

Nicely done.

>
> diff --git a/read-cache.c b/read-cache.c
> index 3e49c49..4f7b70f 100644
> --- a/read-cache.c
> +++ b/read-cache.c
> @@ -2137,54 +2137,27 @@ static int write_split_index(struct index_state *istate,
>  	return ret;
>  }
>  
> -static char *temporary_sharedindex;
> -
> -static void remove_temporary_sharedindex(void)
> -{
> -	if (temporary_sharedindex) {
> -		unlink_or_warn(temporary_sharedindex);
> -		free(temporary_sharedindex);
> -		temporary_sharedindex = NULL;
> -	}
> -}
> -
> -static void remove_temporary_sharedindex_on_signal(int signo)
> -{
> -	remove_temporary_sharedindex();
> -	sigchain_pop(signo);
> -	raise(signo);
> -}
> +static struct tempfile temporary_sharedindex;
>  
>  static int write_shared_index(struct index_state *istate,
>  			      struct lock_file *lock, unsigned flags)
>  {
>  	struct split_index *si = istate->split_index;
> -	static int installed_handler;
>  	int fd, ret;
>  
> -	temporary_sharedindex = git_pathdup("sharedindex_XXXXXX");
> -	fd = mkstemp(temporary_sharedindex);
> +	fd = mks_tempfile(&temporary_sharedindex, git_path("sharedindex_XXXXXX"));
>  	if (fd < 0) {
> -		free(temporary_sharedindex);
> -		temporary_sharedindex = NULL;
>  		hashclr(si->base_sha1);
>  		return do_write_locked_index(istate, lock, flags);
>  	}
> -	if (!installed_handler) {
> -		atexit(remove_temporary_sharedindex);
> -		sigchain_push_common(remove_temporary_sharedindex_on_signal);
> -	}
>  	move_cache_to_base_index(istate);
>  	ret = do_write_index(si->base, fd, 1);
> -	close(fd);
>  	if (ret) {
> -		remove_temporary_sharedindex();
> +		delete_tempfile(&temporary_sharedindex);
>  		return ret;
>  	}
> -	ret = rename(temporary_sharedindex,
> -		     git_path("sharedindex.%s", sha1_to_hex(si->base->sha1)));
> -	free(temporary_sharedindex);
> -	temporary_sharedindex = NULL;
> +	ret = rename_tempfile(&temporary_sharedindex,
> +			      git_path("sharedindex.%s", sha1_to_hex(si->base->sha1)));
>  	if (!ret)
>  		hashcpy(si->base_sha1, si->base->sha1);
>  	return ret;

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 03/14] lockfile: remove some redundant functions
  2015-06-10 17:40   ` Junio C Hamano
@ 2015-06-10 18:27     ` Johannes Sixt
  0 siblings, 0 replies; 26+ messages in thread
From: Johannes Sixt @ 2015-06-10 18:27 UTC (permalink / raw)
  To: Junio C Hamano, Michael Haggerty; +Cc: Jeff King, git

Am 10.06.2015 um 19:40 schrieb Junio C Hamano:
> Michael Haggerty <mhagger@alum.mit.edu> writes:
>
>> Remove the following functions and rewrite their callers to use the
>> equivalent tempfile functions directly:
>>
>> * fdopen_lock_file() -> fdopen_tempfile()
>> * reopen_lock_file() -> reopen_tempfile()
>> * close_lock_file() -> close_tempfile()
>
> Hmph,
>
> My knee-jerk reaction was "I thought lockfile abstraction was
> fine---why do we expose the implementation detail of the lockfile,
> which is now happen to be implemented on top of the tempfile API, to
> the callers?"

Just for the record, I had exactly the same reaction, and I find this 
transition against the spirit of a self-contained lockfile API.

-- Hannes

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 00/14] Introduce a tempfile module
  2015-06-08  9:07 [PATCH 00/14] Introduce a tempfile module Michael Haggerty
                   ` (13 preceding siblings ...)
  2015-06-08  9:07 ` [PATCH 14/14] credential-cache--daemon: use tempfile module Michael Haggerty
@ 2015-06-10 18:34 ` Junio C Hamano
  14 siblings, 0 replies; 26+ messages in thread
From: Junio C Hamano @ 2015-06-10 18:34 UTC (permalink / raw)
  To: Michael Haggerty; +Cc: Jeff King, git

Michael Haggerty <mhagger@alum.mit.edu> writes:

> These patches are also available from my GitHub repo [2], branch
> "tempfile".

Overall the series made a lot of sense.

On a few points I raised:

 - I still think that exposing the implementation detail of the
   lockfile API that it builds on tempfile API is going backwards.
   I wonder if a few helper functions added to the lockfile
   API to hide it, e.g. instead of letting the caller say
   "lk->tempfile.fd", have them call "lockfile_fd(lk)", would make
   things even more clean.

 - The tempfile API could be built on yet another layer of
   clean_on_exit_file API to unconfuse me on "register_tempfile()",
   but I do not think it is worth it only for two existing callers
   (gc and credential).

Thanks.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 02/14] tempfile: a new module for handling temporary files
  2015-06-10 17:36   ` Junio C Hamano
@ 2015-06-10 20:56     ` Michael Haggerty
  2015-06-10 21:35       ` Junio C Hamano
  0 siblings, 1 reply; 26+ messages in thread
From: Michael Haggerty @ 2015-06-10 20:56 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jeff King, git

On 06/10/2015 07:36 PM, Junio C Hamano wrote:
> Michael Haggerty <mhagger@alum.mit.edu> writes:
> 
>> diff --git a/builtin/add.c b/builtin/add.c
>> index df5135b..aaa9ce4 100644
>> --- a/builtin/add.c
>> +++ b/builtin/add.c
>> @@ -5,6 +5,7 @@
>>   */
>>  #include "cache.h"
>>  #include "builtin.h"
>> +#include "tempfile.h"
>>  #include "lockfile.h"
>>  #include "dir.h"
>>  #include "pathspec.h"
> 
> It is a bit sad that all users of lockfile.h has to include
> tempfile.h; even when trying to find out something as basic as the
> name of the file on which the lock is held, they need to look at
> lk->tempfile.filename and that requires inclusion of tempfile.h
> 
> It is a good idea to have tempfile as a separate module, as it
> allows new callers to use the same "clean-on-exit" infrastructure on
> things that are not locks, i.e. they can include tempfile.h without
> having to include lockfile.h, but I have to wonder if it is better
> to include tempfile.h from inside lockfile.h (which is alrady done)
> and allow users of lockfile API to assume that inclusion will always
> stay there.  After all, if they are taking locks, they already know
> lk->tempfile is the mechanism through which they need to learn about
> various aspects of the underlying files.

Hmmm, currently lockfile.h doesn't include tempfile.h. But I think it is
a good idea for it to do so. (I would have done it already but I thought
it was against project policy.)

I will make this change in v2.

> [...]

Michael

-- 
Michael Haggerty
mhagger@alum.mit.edu

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 02/14] tempfile: a new module for handling temporary files
  2015-06-10 20:56     ` Michael Haggerty
@ 2015-06-10 21:35       ` Junio C Hamano
  0 siblings, 0 replies; 26+ messages in thread
From: Junio C Hamano @ 2015-06-10 21:35 UTC (permalink / raw)
  To: Michael Haggerty; +Cc: Jeff King, git

Michael Haggerty <mhagger@alum.mit.edu> writes:

> On 06/10/2015 07:36 PM, Junio C Hamano wrote:
>> Michael Haggerty <mhagger@alum.mit.edu> writes:
>> 
>>> diff --git a/builtin/add.c b/builtin/add.c
>>> index df5135b..aaa9ce4 100644
>>> --- a/builtin/add.c
>>> +++ b/builtin/add.c
>>> @@ -5,6 +5,7 @@
>>>   */
>>>  #include "cache.h"
>>>  #include "builtin.h"
>>> +#include "tempfile.h"
>>>  #include "lockfile.h"
>>>  #include "dir.h"
>>>  #include "pathspec.h"
>> 
>> It is a bit sad that all users of lockfile.h has to include
>> tempfile.h; even when trying to find out something as basic as the
>> name of the file on which the lock is held, they need to look at
>> lk->tempfile.filename and that requires inclusion of tempfile.h
> ...
> Hmmm, currently lockfile.h doesn't include tempfile.h. But I think it is
> a good idea for it to do so. (I would have done it already but I thought
> it was against project policy.)

The project policy is "include what you use, do not rely on others
that happen include what you use" with a minor exception for the
"must be the first" headers git-compat-util.h (which cache.h and
friends include), I think.

If it does not include tempfile.h itself, lockfile.h would be at the
mercy of the *.c file that includes it to be able to see the struct
it uses; if *.c does not include tempfile.h before lockfile.h, it
would break.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 06/14] tempfile: add several functions for creating temporary files
  2015-06-10 17:48   ` Junio C Hamano
@ 2015-08-10  3:08     ` Michael Haggerty
  0 siblings, 0 replies; 26+ messages in thread
From: Michael Haggerty @ 2015-08-10  3:08 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jeff King, git

On 06/10/2015 07:48 PM, Junio C Hamano wrote:
> Michael Haggerty <mhagger@alum.mit.edu> writes:
> 
>> Add several functions for creating temporary files with
>> automatically-generated names, analogous to mkstemps(), but also
>> arranging for the files to be deleted on program exit.
>>
>> The functions are named according to a pattern depending how they
>> operate. They will be used to replace many places in the code where
>> temporary files are created and cleaned up ad-hoc.
>>
>> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
>> ---
>>  tempfile.c | 55 ++++++++++++++++++++++++++++++++++-
>>  tempfile.h | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 150 insertions(+), 1 deletion(-)
>>
>> diff --git a/tempfile.c b/tempfile.c
>> index f76bc07..890075f 100644
>> --- a/tempfile.c
>> +++ b/tempfile.c
>> @@ -48,7 +48,7 @@ static void register_tempfile_object(struct tempfile *tempfile, const char *path
>>  		tempfile->fp = NULL;
>>  		tempfile->active = 0;
>>  		tempfile->owner = 0;
>> -		strbuf_init(&tempfile->filename, strlen(path));
>> +		strbuf_init(&tempfile->filename, 0);
>>  		tempfile->next = tempfile_list;
>>  		tempfile_list = tempfile;
>>  		tempfile->on_list = 1;
> 
> This probably could have been part of the previous step.  Regardless
> of where in the patch series this change is done, I think it makes
> sense, as this function does not even know how long the final filename
> would be, and strlen(path) is almost always wrong as path is likely to
> be relative.
> 
> I notice this change makes "path" almost unused in this function,
> and the only remaining use is for assert(!tempfile->filename.len).
> Perhaps it is not worth passing the "path" parameter?

These are both good points. I will implement them in the upcoming v2.

Thanks,
Michael

-- 
Michael Haggerty
mhagger@alum.mit.edu

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 07/14] register_tempfile(): new function to handle an existing temporary file
  2015-06-10 17:55   ` Junio C Hamano
@ 2015-08-10  3:40     ` Michael Haggerty
  0 siblings, 0 replies; 26+ messages in thread
From: Michael Haggerty @ 2015-08-10  3:40 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jeff King, git

On 06/10/2015 07:55 PM, Junio C Hamano wrote:
> Michael Haggerty <mhagger@alum.mit.edu> writes:
> 
>> Allow an existing file to be registered with the tempfile-handling
>> infrastructure; in particular, arrange for it to be deleted on program
>> exit.
>>
>> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
>> ---
> 
> Hmph.  Where does such a tempfile that is not on list come from?

You saw the answer to your question later in the patch series, but for
the benefit of other readers:

This function will be useful to manage the lifetime of a file whose
creation is not as simple as open(); for example, if the file has to
itself be created using the lockfile API, or if it is not a regular file
(e.g., a socket). I will explain this better in the commit message in v2.

> [...] Also I cannot
> tell which one of register_tempfile() and register_tempfile_object()
> I should be calling when updating the implementation of this API
> from their names.

Good point. I will rename the latter to prepare_tempfile_object() and
add a docstring.

Michael

-- 
Michael Haggerty
mhagger@alum.mit.edu

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2015-08-10  3:40 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-06-08  9:07 [PATCH 00/14] Introduce a tempfile module Michael Haggerty
2015-06-08  9:07 ` [PATCH 01/14] Move lockfile API documentation to lockfile.h Michael Haggerty
2015-06-08  9:07 ` [PATCH 02/14] tempfile: a new module for handling temporary files Michael Haggerty
2015-06-10 17:36   ` Junio C Hamano
2015-06-10 20:56     ` Michael Haggerty
2015-06-10 21:35       ` Junio C Hamano
2015-06-08  9:07 ` [PATCH 03/14] lockfile: remove some redundant functions Michael Haggerty
2015-06-10 17:40   ` Junio C Hamano
2015-06-10 18:27     ` Johannes Sixt
2015-06-08  9:07 ` [PATCH 04/14] commit_lock_file(): use get_locked_file_path() Michael Haggerty
2015-06-08  9:07 ` [PATCH 05/14] register_tempfile_object(): new function, extracted from create_tempfile() Michael Haggerty
2015-06-08  9:07 ` [PATCH 06/14] tempfile: add several functions for creating temporary files Michael Haggerty
2015-06-10 17:48   ` Junio C Hamano
2015-08-10  3:08     ` Michael Haggerty
2015-06-08  9:07 ` [PATCH 07/14] register_tempfile(): new function to handle an existing temporary file Michael Haggerty
2015-06-10 17:55   ` Junio C Hamano
2015-08-10  3:40     ` Michael Haggerty
2015-06-08  9:07 ` [PATCH 08/14] write_shared_index(): use tempfile module Michael Haggerty
2015-06-10 17:56   ` Junio C Hamano
2015-06-08  9:07 ` [PATCH 09/14] setup_temporary_shallow(): " Michael Haggerty
2015-06-08  9:07 ` [PATCH 10/14] diff: " Michael Haggerty
2015-06-08  9:07 ` [PATCH 11/14] lock_repo_for_gc(): compute the path to "gc.pid" only once Michael Haggerty
2015-06-08  9:07 ` [PATCH 12/14] gc: use tempfile module to handle gc.pid file Michael Haggerty
2015-06-08  9:07 ` [PATCH 13/14] credential-cache--daemon: delete socket from main() Michael Haggerty
2015-06-08  9:07 ` [PATCH 14/14] credential-cache--daemon: use tempfile module Michael Haggerty
2015-06-10 18:34 ` [PATCH 00/14] Introduce a " Junio C Hamano

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).