mirror of
https://github.com/git/git.git
synced 2024-11-15 05:33:04 +01:00
8e86c155d2
If closing an open lockfile fails, then we cannot be sure of the contents of the lockfile, so there is nothing sensible to do but delete it. This change also insures that the lock_file object is left in a defined state in this error path (namely, unlocked). The only caller that is ultimately affected by this change is try_merge_strategy() -> write_locked_index(), which can call close_lock_file() via various execution paths. This caller uses a static lock_file object which previously could have been reused after a failed close_lock_file() even though it was still in locked state. This change causes the lock_file object to be unlocked on failure, thus fixing this error-handling path. Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>
189 lines
6.8 KiB
Text
189 lines
6.8 KiB
Text
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 writing to the file
|
|
descriptor returned by those functions (also available via
|
|
`lock->fd`).
|
|
|
|
When finished writing, the caller can:
|
|
|
|
* Close the file descriptor and rename the lockfile to its final
|
|
destination by calling `commit_lock_file`.
|
|
|
|
* 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`,
|
|
`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`,
|
|
`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)` yourself!
|
|
Otherwise the `struct lock_file` structure would still think that the
|
|
file descriptor needs to be closed, and a later call to
|
|
`commit_lock_file` or `rollback_lock_file` or program exit would
|
|
result in duplicate calls to `close(2)`. Worse yet, if you `close(2)`
|
|
and then later open another file descriptor for a completely different
|
|
purpose, then a call to `commit_lock_file` or `rollback_lock_file`
|
|
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()`.
|
|
|
|
|
|
Flags
|
|
-----
|
|
|
|
The following flags can be passed to `hold_lock_file_for_update` or
|
|
`hold_lock_file_for_append`:
|
|
|
|
LOCK_NODEREF::
|
|
|
|
Usually symbolic links in the destination path are resolved
|
|
and the lockfile is created by adding ".lock" to the resolved
|
|
path. If `LOCK_NODEREF` 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.
|
|
|
|
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 or a negative value on failure 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.
|
|
|
|
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.
|
|
|
|
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`, and close the file descriptor.
|
|
Return 0 upon success. On failure to `close(2)`, return a
|
|
negative value and rollback the lock file. Usually
|
|
`commit_lock_file` 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.
|