Add a new function, fdopen_lock_file(), which returns a FILE pointer open to the lockfile. If a stream is open on a lock_file object, it is closed using fclose() on commit, rollback, or close_lock_file(). This change will allow callers to use stdio to write to a lockfile without having to muck around in the internal representation of the lock_file object (callers will be rewritten in upcoming commits). Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>
		
			
				
	
	
		
			221 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			221 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
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.
 |