Skip to content

Commit 1a9d15d

Browse files
mhaggergitster
authored andcommitted
tempfile: a new module for handling temporary files
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. Subsequent commits will add more users of the new module. Signed-off-by: Michael Haggerty <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 9c77381 commit 1a9d15d

File tree

5 files changed

+470
-270
lines changed

5 files changed

+470
-270
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,7 @@ LIB_OBJS += string-list.o
786786
LIB_OBJS += submodule.o
787787
LIB_OBJS += symlinks.o
788788
LIB_OBJS += tag.o
789+
LIB_OBJS += tempfile.o
789790
LIB_OBJS += trace.o
790791
LIB_OBJS += trailer.o
791792
LIB_OBJS += transport.o

lockfile.c

Lines changed: 16 additions & 245 deletions
Original file line numberDiff line numberDiff line change
@@ -2,90 +2,8 @@
22
* Copyright (c) 2005, Junio C Hamano
33
*/
44

5-
/*
6-
* State diagram and cleanup
7-
* -------------------------
8-
*
9-
* This module keeps track of all locked files in `lock_file_list` for
10-
* use at cleanup. This list and the `lock_file` objects that comprise
11-
* it must be kept in self-consistent states at all time, because the
12-
* program can be interrupted any time by a signal, in which case the
13-
* signal handler will walk through the list attempting to clean up
14-
* any open lock files.
15-
*
16-
* The possible states of a `lock_file` object are as follows:
17-
*
18-
* - Uninitialized. In this state the object's `on_list` field must be
19-
* zero but the rest of its contents need not be initialized. As
20-
* soon as the object is used in any way, it is irrevocably
21-
* registered in `lock_file_list`, and `on_list` is set.
22-
*
23-
* - Locked, lockfile open (after `hold_lock_file_for_update()`,
24-
* `hold_lock_file_for_append()`, or `reopen_lock_file()`). In this
25-
* state:
26-
*
27-
* - the lockfile exists
28-
* - `active` is set
29-
* - `filename` holds the filename of the lockfile
30-
* - `fd` holds a file descriptor open for writing to the lockfile
31-
* - `fp` holds a pointer to an open `FILE` object if and only if
32-
* `fdopen_lock_file()` has been called on the object
33-
* - `owner` holds the PID of the process that locked the file
34-
*
35-
* - Locked, lockfile closed (after successful `close_lock_file()`).
36-
* Same as the previous state, except that the lockfile is closed
37-
* and `fd` is -1.
38-
*
39-
* - Unlocked (after `commit_lock_file()`, `commit_lock_file_to()`,
40-
* `rollback_lock_file()`, a failed attempt to lock, or a failed
41-
* `close_lock_file()`). In this state:
42-
*
43-
* - `active` is unset
44-
* - `filename` is empty (usually, though there are transitory
45-
* states in which this condition doesn't hold). Client code should
46-
* *not* rely on the filename being empty in this state.
47-
* - `fd` is -1
48-
* - the object is left registered in the `lock_file_list`, and
49-
* `on_list` is set.
50-
*
51-
* A lockfile is owned by the process that created it. The `lock_file`
52-
* has an `owner` field that records the owner's PID. This field is
53-
* used to prevent a forked process from closing a lockfile created by
54-
* its parent.
55-
*/
56-
575
#include "cache.h"
586
#include "lockfile.h"
59-
#include "sigchain.h"
60-
61-
static struct lock_file *volatile lock_file_list;
62-
63-
static void remove_lock_files(int skip_fclose)
64-
{
65-
pid_t me = getpid();
66-
67-
while (lock_file_list) {
68-
if (lock_file_list->owner == me) {
69-
/* fclose() is not safe to call in a signal handler */
70-
if (skip_fclose)
71-
lock_file_list->fp = NULL;
72-
rollback_lock_file(lock_file_list);
73-
}
74-
lock_file_list = lock_file_list->next;
75-
}
76-
}
77-
78-
static void remove_lock_files_on_exit(void)
79-
{
80-
remove_lock_files(0);
81-
}
82-
83-
static void remove_lock_files_on_signal(int signo)
84-
{
85-
remove_lock_files(1);
86-
sigchain_pop(signo);
87-
raise(signo);
88-
}
897

908
/*
919
* path = absolute or relative path name
@@ -154,60 +72,17 @@ static void resolve_symlink(struct strbuf *path)
15472
/* Make sure errno contains a meaningful value on error */
15573
static int lock_file(struct lock_file *lk, const char *path, int flags)
15674
{
157-
size_t pathlen = strlen(path);
158-
159-
if (!lock_file_list) {
160-
/* One-time initialization */
161-
sigchain_push_common(remove_lock_files_on_signal);
162-
atexit(remove_lock_files_on_exit);
163-
}
75+
int fd;
76+
struct strbuf filename = STRBUF_INIT;
16477

165-
if (lk->active)
166-
die("BUG: cannot lock_file(\"%s\") using active struct lock_file",
167-
path);
168-
if (!lk->on_list) {
169-
/* Initialize *lk and add it to lock_file_list: */
170-
lk->fd = -1;
171-
lk->fp = NULL;
172-
lk->active = 0;
173-
lk->owner = 0;
174-
strbuf_init(&lk->filename, pathlen + LOCK_SUFFIX_LEN);
175-
lk->next = lock_file_list;
176-
lock_file_list = lk;
177-
lk->on_list = 1;
178-
} else if (lk->filename.len) {
179-
/* This shouldn't happen, but better safe than sorry. */
180-
die("BUG: lock_file(\"%s\") called with improperly-reset lock_file object",
181-
path);
182-
}
78+
strbuf_addstr(&filename, path);
79+
if (!(flags & LOCK_NO_DEREF))
80+
resolve_symlink(&filename);
18381

184-
if (flags & LOCK_NO_DEREF) {
185-
strbuf_add_absolute_path(&lk->filename, path);
186-
} else {
187-
struct strbuf resolved_path = STRBUF_INIT;
188-
189-
strbuf_add(&resolved_path, path, pathlen);
190-
resolve_symlink(&resolved_path);
191-
strbuf_add_absolute_path(&lk->filename, resolved_path.buf);
192-
strbuf_release(&resolved_path);
193-
}
194-
195-
strbuf_addstr(&lk->filename, LOCK_SUFFIX);
196-
lk->fd = open(lk->filename.buf, O_RDWR | O_CREAT | O_EXCL, 0666);
197-
if (lk->fd < 0) {
198-
strbuf_reset(&lk->filename);
199-
return -1;
200-
}
201-
lk->owner = getpid();
202-
lk->active = 1;
203-
if (adjust_shared_perm(lk->filename.buf)) {
204-
int save_errno = errno;
205-
error("cannot fix permission bits on %s", lk->filename.buf);
206-
rollback_lock_file(lk);
207-
errno = save_errno;
208-
return -1;
209-
}
210-
return lk->fd;
82+
strbuf_addstr(&filename, LOCK_SUFFIX);
83+
fd = create_tempfile(&lk->tempfile, filename.buf);
84+
strbuf_release(&filename);
85+
return fd;
21186
}
21287

21388
static int sleep_microseconds(long us)
@@ -353,109 +228,17 @@ int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags)
353228
return fd;
354229
}
355230

356-
FILE *fdopen_lock_file(struct lock_file *lk, const char *mode)
357-
{
358-
if (!lk->active)
359-
die("BUG: fdopen_lock_file() called for unlocked object");
360-
if (lk->fp)
361-
die("BUG: fdopen_lock_file() called twice for file '%s'", lk->filename.buf);
362-
363-
lk->fp = fdopen(lk->fd, mode);
364-
return lk->fp;
365-
}
366-
367-
const char *get_lock_file_path(struct lock_file *lk)
368-
{
369-
if (!lk->active)
370-
die("BUG: get_lock_file_path() called for unlocked object");
371-
return lk->filename.buf;
372-
}
373-
374-
int get_lock_file_fd(struct lock_file *lk)
375-
{
376-
if (!lk->active)
377-
die("BUG: get_lock_file_fd() called for unlocked object");
378-
return lk->fd;
379-
}
380-
381-
FILE *get_lock_file_fp(struct lock_file *lk)
382-
{
383-
if (!lk->active)
384-
die("BUG: get_lock_file_fp() called for unlocked object");
385-
return lk->fp;
386-
}
387-
388231
char *get_locked_file_path(struct lock_file *lk)
389232
{
390-
if (!lk->active)
391-
die("BUG: get_locked_file_path() called for unlocked object");
392-
if (lk->filename.len <= LOCK_SUFFIX_LEN ||
393-
strcmp(lk->filename.buf + lk->filename.len - LOCK_SUFFIX_LEN, LOCK_SUFFIX))
233+
struct strbuf ret = STRBUF_INIT;
234+
235+
strbuf_addstr(&ret, get_tempfile_path(&lk->tempfile));
236+
if (ret.len <= LOCK_SUFFIX_LEN ||
237+
strcmp(ret.buf + ret.len - LOCK_SUFFIX_LEN, LOCK_SUFFIX))
394238
die("BUG: get_locked_file_path() called for malformed lock object");
395239
/* remove ".lock": */
396-
return xmemdupz(lk->filename.buf, lk->filename.len - LOCK_SUFFIX_LEN);
397-
}
398-
399-
int close_lock_file(struct lock_file *lk)
400-
{
401-
int fd = lk->fd;
402-
FILE *fp = lk->fp;
403-
int err;
404-
405-
if (fd < 0)
406-
return 0;
407-
408-
lk->fd = -1;
409-
if (fp) {
410-
lk->fp = NULL;
411-
412-
/*
413-
* Note: no short-circuiting here; we want to fclose()
414-
* in any case!
415-
*/
416-
err = ferror(fp) | fclose(fp);
417-
} else {
418-
err = close(fd);
419-
}
420-
421-
if (err) {
422-
int save_errno = errno;
423-
rollback_lock_file(lk);
424-
errno = save_errno;
425-
return -1;
426-
}
427-
428-
return 0;
429-
}
430-
431-
int reopen_lock_file(struct lock_file *lk)
432-
{
433-
if (0 <= lk->fd)
434-
die(_("BUG: reopen a lockfile that is still open"));
435-
if (!lk->active)
436-
die(_("BUG: reopen a lockfile that has been committed"));
437-
lk->fd = open(lk->filename.buf, O_WRONLY);
438-
return lk->fd;
439-
}
440-
441-
int commit_lock_file_to(struct lock_file *lk, const char *path)
442-
{
443-
if (!lk->active)
444-
die("BUG: attempt to commit unlocked object to \"%s\"", path);
445-
446-
if (close_lock_file(lk))
447-
return -1;
448-
449-
if (rename(lk->filename.buf, path)) {
450-
int save_errno = errno;
451-
rollback_lock_file(lk);
452-
errno = save_errno;
453-
return -1;
454-
}
455-
456-
lk->active = 0;
457-
strbuf_reset(&lk->filename);
458-
return 0;
240+
strbuf_setlen(&ret, ret.len - LOCK_SUFFIX_LEN);
241+
return strbuf_detach(&ret, NULL);
459242
}
460243

461244
int commit_lock_file(struct lock_file *lk)
@@ -471,15 +254,3 @@ int commit_lock_file(struct lock_file *lk)
471254
free(result_path);
472255
return 0;
473256
}
474-
475-
void rollback_lock_file(struct lock_file *lk)
476-
{
477-
if (!lk->active)
478-
return;
479-
480-
if (!close_lock_file(lk)) {
481-
unlink_or_warn(lk->filename.buf);
482-
lk->active = 0;
483-
strbuf_reset(&lk->filename);
484-
}
485-
}

0 commit comments

Comments
 (0)