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 <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Michael Haggerty committed Aug 10, 2015 at 11:47 UTC 1a9d15db25487bb3fc009a88375cc206a60e0e3b
5 files changed +470 -270
Makefile
+1
@@ -786,6 +786,7 @@ LIB_OBJS += string-list.o
786 LIB_OBJS += submodule.o
787 LIB_OBJS += symlinks.o
788 LIB_OBJS += tag.o
789 +LIB_OBJS += tempfile.o
790 LIB_OBJS += trace.o
791 LIB_OBJS += trailer.o
792 LIB_OBJS += transport.o
lockfile.c
+16 -245
@@ -2,90 +2,8 @@
2 * Copyright (c) 2005, Junio C Hamano
3 */
4
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 -
5 #include "cache.h"
6 #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 -}
7
8 /*
9 * path = absolute or relative path name
@@ -154,60 +72,17 @@ static void resolve_symlink(struct strbuf *path)
72 /* Make sure errno contains a meaningful value on error */
73 static int lock_file(struct lock_file *lk, const char *path, int flags)
74 {
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;
77
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);
81
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;
86 }
87
88 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)
228 return fd;
229 }
230
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 -
231 char *get_locked_file_path(struct lock_file *lk)
232 {
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))
238 die("BUG: get_locked_file_path() called for malformed lock object");
239 /* 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);
242 }
243
244 int commit_lock_file(struct lock_file *lk)
@@ -471,15 +254,3 @@ int commit_lock_file(struct lock_file *lk)
254 free(result_path);
255 return 0;
256 }
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 -}
lockfile.h
+48 -25
@@ -29,6 +29,8 @@
29 * the file or the new contents of the file (assuming that the
30 * filesystem implements `rename(2)` atomically).
31 *
32 + * Most of the heavy lifting is done by the tempfile module (see
33 + * "tempfile.h").
34 *
35 * Calling sequence
36 * ----------------
@@ -74,19 +76,19 @@
76 * `hold_lock_file_for_update()` or `hold_lock_file_for_append()`.
77 *
78 * If the program exits before `commit_lock_file()`,
77 - * `commit_lock_file_to()`, or `rollback_lock_file()` is called, an
78 - * `atexit(3)` handler will close and remove the lockfile, thereby
79 - * rolling back any uncommitted changes.
79 + * `commit_lock_file_to()`, or `rollback_lock_file()` is called, the
80 + * tempfile module will close and remove the lockfile, thereby rolling
81 + * back any uncommitted changes.
82 *
83 * If you need to close the file descriptor you obtained from a
84 * `hold_lock_file_for_*()` function yourself, do so by calling
83 - * `close_lock_file()`. You should never call `close(2)` or
84 - * `fclose(3)` yourself, otherwise the `struct lock_file` structure
85 - * would still think that the file descriptor needs to be closed, and
86 - * a commit or rollback would result in duplicate calls to `close(2)`.
87 - * Worse yet, if you close and then later open another file descriptor
88 - * for a completely different purpose, then a commit or rollback might
89 - * close that unrelated file descriptor.
85 + * `close_lock_file()`. See "tempfile.h" for more information.
86 + *
87 + *
88 + * Under the covers, a lockfile is just a tempfile with a few helper
89 + * functions. In particular, the state diagram and the cleanup
90 + * machinery are all implemented in the tempfile module.
91 + *
92 *
93 * Error handling
94 * --------------
@@ -103,14 +105,10 @@
105 * -1.
106 */
107
108 +#include "tempfile.h"
109 +
110 struct lock_file {
107 - struct lock_file *volatile next;
108 - volatile sig_atomic_t active;
109 - volatile int fd;
110 - FILE *volatile fp;
111 - volatile pid_t owner;
112 - char on_list;
113 - struct strbuf filename;
111 + struct tempfile tempfile;
112 };
113
114 /* String appended to a filename to derive the lockfile name: */
@@ -201,16 +199,29 @@ extern NORETURN void unable_to_lock_die(const char *path, int err);
199 * error. The stream is closed automatically when `close_lock_file()`
200 * is called or when the file is committed or rolled back.
201 */
204 -extern FILE *fdopen_lock_file(struct lock_file *lk, const char *mode);
202 +static inline FILE *fdopen_lock_file(struct lock_file *lk, const char *mode)
203 +{
204 + return fdopen_tempfile(&lk->tempfile, mode);
205 +}
206
207 /*
208 * Return the path of the lockfile. The return value is a pointer to a
209 * field within the lock_file object and should not be freed.
210 */
210 -extern const char *get_lock_file_path(struct lock_file *lk);
211 +static inline const char *get_lock_file_path(struct lock_file *lk)
212 +{
213 + return get_tempfile_path(&lk->tempfile);
214 +}
215
212 -extern int get_lock_file_fd(struct lock_file *lk);
213 -extern FILE *get_lock_file_fp(struct lock_file *lk);
216 +static inline int get_lock_file_fd(struct lock_file *lk)
217 +{
218 + return get_tempfile_fd(&lk->tempfile);
219 +}
220 +
221 +static inline FILE *get_lock_file_fp(struct lock_file *lk)
222 +{
223 + return get_tempfile_fp(&lk->tempfile);
224 +}
225
226 /*
227 * Return the path of the file that is locked by the specified
@@ -227,7 +238,10 @@ extern char *get_locked_file_path(struct lock_file *lk);
238 * or `rollback_lock_file()` should eventually be called if
239 * `close_lock_file()` succeeds.
240 */
230 -extern int close_lock_file(struct lock_file *lk);
241 +static inline int close_lock_file(struct lock_file *lk)
242 +{
243 + return close_tempfile(&lk->tempfile);
244 +}
245
246 /*
247 * Re-open a lockfile that has been closed using `close_lock_file()`
@@ -248,7 +262,10 @@ extern int close_lock_file(struct lock_file *lk);
262 *
263 * * `commit_lock_file()` to make the final version permanent.
264 */
251 -extern int reopen_lock_file(struct lock_file *lk);
265 +static inline int reopen_lock_file(struct lock_file *lk)
266 +{
267 + return reopen_tempfile(&lk->tempfile);
268 +}
269
270 /*
271 * Commit the change represented by `lk`: close the file descriptor
@@ -265,7 +282,10 @@ extern int commit_lock_file(struct lock_file *lk);
282 * Like `commit_lock_file()`, but rename the lockfile to the provided
283 * `path`. `path` must be on the same filesystem as the lock file.
284 */
268 -extern int commit_lock_file_to(struct lock_file *lk, const char *path);
285 +static inline int commit_lock_file_to(struct lock_file *lk, const char *path)
286 +{
287 + return rename_tempfile(&lk->tempfile, path);
288 +}
289
290 /*
291 * Roll back `lk`: close the file descriptor and/or file pointer and
@@ -273,6 +293,9 @@ extern int commit_lock_file_to(struct lock_file *lk, const char *path);
293 * for a `lock_file` object that has already been committed or rolled
294 * back.
295 */
276 -extern void rollback_lock_file(struct lock_file *lk);
296 +static inline void rollback_lock_file(struct lock_file *lk)
297 +{
298 + delete_tempfile(&lk->tempfile);
299 +}
300
301 #endif /* LOCKFILE_H */
tempfile.c new
+238
@@ -0,0 +1,238 @@
1 +/*
2 + * State diagram and cleanup
3 + * -------------------------
4 + *
5 + * If the program exits while a temporary file is active, we want to
6 + * make sure that we remove it. This is done by remembering the active
7 + * temporary files in a linked list, `tempfile_list`. An `atexit(3)`
8 + * handler and a signal handler are registered, to clean up any active
9 + * temporary files.
10 + *
11 + * Because the signal handler can run at any time, `tempfile_list` and
12 + * the `tempfile` objects that comprise it must be kept in
13 + * self-consistent states at all times.
14 + *
15 + * The possible states of a `tempfile` object are as follows:
16 + *
17 + * - Uninitialized. In this state the object's `on_list` field must be
18 + * zero but the rest of its contents need not be initialized. As
19 + * soon as the object is used in any way, it is irrevocably
20 + * registered in `tempfile_list`, and `on_list` is set.
21 + *
22 + * - Active, file open (after `create_tempfile()` or
23 + * `reopen_tempfile()`). In this state:
24 + *
25 + * - the temporary file exists
26 + * - `active` is set
27 + * - `filename` holds the filename of the temporary file
28 + * - `fd` holds a file descriptor open for writing to it
29 + * - `fp` holds a pointer to an open `FILE` object if and only if
30 + * `fdopen_tempfile()` has been called on the object
31 + * - `owner` holds the PID of the process that created the file
32 + *
33 + * - Active, file closed (after successful `close_tempfile()`). Same
34 + * as the previous state, except that the temporary file is closed,
35 + * `fd` is -1, and `fp` is `NULL`.
36 + *
37 + * - Inactive (after `delete_tempfile()`, `rename_tempfile()`, a
38 + * failed attempt to create a temporary file, or a failed
39 + * `close_tempfile()`). In this state:
40 + *
41 + * - `active` is unset
42 + * - `filename` is empty (usually, though there are transitory
43 + * states in which this condition doesn't hold). Client code should
44 + * *not* rely on the filename being empty in this state.
45 + * - `fd` is -1 and `fp` is `NULL`
46 + * - the object is left registered in the `tempfile_list`, and
47 + * `on_list` is set.
48 + *
49 + * A temporary file is owned by the process that created it. The
50 + * `tempfile` has an `owner` field that records the owner's PID. This
51 + * field is used to prevent a forked process from deleting a temporary
52 + * file created by its parent.
53 + */
54 +
55 +#include "cache.h"
56 +#include "tempfile.h"
57 +#include "sigchain.h"
58 +
59 +static struct tempfile *volatile tempfile_list;
60 +
61 +static void remove_tempfiles(int skip_fclose)
62 +{
63 + pid_t me = getpid();
64 +
65 + while (tempfile_list) {
66 + if (tempfile_list->owner == me) {
67 + /* fclose() is not safe to call in a signal handler */
68 + if (skip_fclose)
69 + tempfile_list->fp = NULL;
70 + delete_tempfile(tempfile_list);
71 + }
72 + tempfile_list = tempfile_list->next;
73 + }
74 +}
75 +
76 +static void remove_tempfiles_on_exit(void)
77 +{
78 + remove_tempfiles(0);
79 +}
80 +
81 +static void remove_tempfiles_on_signal(int signo)
82 +{
83 + remove_tempfiles(1);
84 + sigchain_pop(signo);
85 + raise(signo);
86 +}
87 +
88 +/* Make sure errno contains a meaningful value on error */
89 +int create_tempfile(struct tempfile *tempfile, const char *path)
90 +{
91 + size_t pathlen = strlen(path);
92 +
93 + if (!tempfile_list) {
94 + /* One-time initialization */
95 + sigchain_push_common(remove_tempfiles_on_signal);
96 + atexit(remove_tempfiles_on_exit);
97 + }
98 +
99 + if (tempfile->active)
100 + die("BUG: create_tempfile called for active object");
101 + if (!tempfile->on_list) {
102 + /* Initialize *tempfile and add it to tempfile_list: */
103 + tempfile->fd = -1;
104 + tempfile->fp = NULL;
105 + tempfile->active = 0;
106 + tempfile->owner = 0;
107 + strbuf_init(&tempfile->filename, pathlen);
108 + tempfile->next = tempfile_list;
109 + tempfile_list = tempfile;
110 + tempfile->on_list = 1;
111 + } else if (tempfile->filename.len) {
112 + /* This shouldn't happen, but better safe than sorry. */
113 + die("BUG: create_tempfile called for improperly-reset object");
114 + }
115 +
116 + strbuf_add_absolute_path(&tempfile->filename, path);
117 + tempfile->fd = open(tempfile->filename.buf, O_RDWR | O_CREAT | O_EXCL, 0666);
118 + if (tempfile->fd < 0) {
119 + strbuf_reset(&tempfile->filename);
120 + return -1;
121 + }
122 + tempfile->owner = getpid();
123 + tempfile->active = 1;
124 + if (adjust_shared_perm(tempfile->filename.buf)) {
125 + int save_errno = errno;
126 + error("cannot fix permission bits on %s", tempfile->filename.buf);
127 + delete_tempfile(tempfile);
128 + errno = save_errno;
129 + return -1;
130 + }
131 + return tempfile->fd;
132 +}
133 +
134 +FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode)
135 +{
136 + if (!tempfile->active)
137 + die("BUG: fdopen_tempfile() called for inactive object");
138 + if (tempfile->fp)
139 + die("BUG: fdopen_tempfile() called for open object");
140 +
141 + tempfile->fp = fdopen(tempfile->fd, mode);
142 + return tempfile->fp;
143 +}
144 +
145 +const char *get_tempfile_path(struct tempfile *tempfile)
146 +{
147 + if (!tempfile->active)
148 + die("BUG: get_tempfile_path() called for inactive object");
149 + return tempfile->filename.buf;
150 +}
151 +
152 +int get_tempfile_fd(struct tempfile *tempfile)
153 +{
154 + if (!tempfile->active)
155 + die("BUG: get_tempfile_fd() called for inactive object");
156 + return tempfile->fd;
157 +}
158 +
159 +FILE *get_tempfile_fp(struct tempfile *tempfile)
160 +{
161 + if (!tempfile->active)
162 + die("BUG: get_tempfile_fp() called for inactive object");
163 + return tempfile->fp;
164 +}
165 +
166 +int close_tempfile(struct tempfile *tempfile)
167 +{
168 + int fd = tempfile->fd;
169 + FILE *fp = tempfile->fp;
170 + int err;
171 +
172 + if (fd < 0)
173 + return 0;
174 +
175 + tempfile->fd = -1;
176 + if (fp) {
177 + tempfile->fp = NULL;
178 +
179 + /*
180 + * Note: no short-circuiting here; we want to fclose()
181 + * in any case!
182 + */
183 + err = ferror(fp) | fclose(fp);
184 + } else {
185 + err = close(fd);
186 + }
187 +
188 + if (err) {
189 + int save_errno = errno;
190 + delete_tempfile(tempfile);
191 + errno = save_errno;
192 + return -1;
193 + }
194 +
195 + return 0;
196 +}
197 +
198 +int reopen_tempfile(struct tempfile *tempfile)
199 +{
200 + if (0 <= tempfile->fd)
201 + die("BUG: reopen_tempfile called for an open object");
202 + if (!tempfile->active)
203 + die("BUG: reopen_tempfile called for an inactive object");
204 + tempfile->fd = open(tempfile->filename.buf, O_WRONLY);
205 + return tempfile->fd;
206 +}
207 +
208 +int rename_tempfile(struct tempfile *tempfile, const char *path)
209 +{
210 + if (!tempfile->active)
211 + die("BUG: rename_tempfile called for inactive object");
212 +
213 + if (close_tempfile(tempfile))
214 + return -1;
215 +
216 + if (rename(tempfile->filename.buf, path)) {
217 + int save_errno = errno;
218 + delete_tempfile(tempfile);
219 + errno = save_errno;
220 + return -1;
221 + }
222 +
223 + tempfile->active = 0;
224 + strbuf_reset(&tempfile->filename);
225 + return 0;
226 +}
227 +
228 +void delete_tempfile(struct tempfile *tempfile)
229 +{
230 + if (!tempfile->active)
231 + return;
232 +
233 + if (!close_tempfile(tempfile)) {
234 + unlink_or_warn(tempfile->filename.buf);
235 + tempfile->active = 0;
236 + strbuf_reset(&tempfile->filename);
237 + }
238 +}
tempfile.h new
+167
@@ -0,0 +1,167 @@
1 +#ifndef TEMPFILE_H
2 +#define TEMPFILE_H
3 +
4 +/*
5 + * Handle temporary files.
6 + *
7 + * The tempfile API allows temporary files to be created, deleted, and
8 + * atomically renamed. Temporary files that are still active when the
9 + * program ends are cleaned up automatically. Lockfiles (see
10 + * "lockfile.h") are built on top of this API.
11 + *
12 + *
13 + * Calling sequence
14 + * ----------------
15 + *
16 + * The caller:
17 + *
18 + * * Allocates a `struct tempfile` either as a static variable or on
19 + * the heap, initialized to zeros. Once you use the structure to
20 + * call `create_tempfile()`, it belongs to the tempfile subsystem
21 + * and its storage must remain valid throughout the life of the
22 + * program (i.e. you cannot use an on-stack variable to hold this
23 + * structure).
24 + *
25 + * * Attempts to create a temporary file by calling
26 + * `create_tempfile()`.
27 + *
28 + * * Writes new content to the file by either:
29 + *
30 + * * writing to the file descriptor returned by `create_tempfile()`
31 + * (also available via `tempfile->fd`).
32 + *
33 + * * calling `fdopen_tempfile()` to get a `FILE` pointer for the
34 + * open file and writing to the file using stdio.
35 + *
36 + * When finished writing, the caller can:
37 + *
38 + * * Close the file descriptor and remove the temporary file by
39 + * calling `delete_tempfile()`.
40 + *
41 + * * Close the temporary file and rename it atomically to a specified
42 + * filename by calling `rename_tempfile()`. This relinquishes
43 + * control of the file.
44 + *
45 + * * Close the file descriptor without removing or renaming the
46 + * temporary file by calling `close_tempfile()`, and later call
47 + * `delete_tempfile()` or `rename_tempfile()`.
48 + *
49 + * Even after the temporary file is renamed or deleted, the `tempfile`
50 + * object must not be freed or altered by the caller. However, it may
51 + * be reused; just pass it to another call of `create_tempfile()`.
52 + *
53 + * If the program exits before `rename_tempfile()` or
54 + * `delete_tempfile()` is called, an `atexit(3)` handler will close
55 + * and remove the temporary file.
56 + *
57 + * If you need to close the file descriptor yourself, do so by calling
58 + * `close_tempfile()`. You should never call `close(2)` or `fclose(3)`
59 + * yourself, otherwise the `struct tempfile` structure would still
60 + * think that the file descriptor needs to be closed, and a later
61 + * cleanup would result in duplicate calls to `close(2)`. Worse yet,
62 + * if you close and then later open another file descriptor for a
63 + * completely different purpose, then the unrelated file descriptor
64 + * might get closed.
65 + *
66 + *
67 + * Error handling
68 + * --------------
69 + *
70 + * `create_tempfile()` returns a file descriptor on success or -1 on
71 + * failure. On errors, `errno` describes the reason for failure.
72 + *
73 + * `delete_tempfile()`, `rename_tempfile()`, and `close_tempfile()`
74 + * return 0 on success. On failure they set `errno` appropriately, do
75 + * their best to delete the temporary file, and return -1.
76 + */
77 +
78 +struct tempfile {
79 + struct tempfile *volatile next;
80 + volatile sig_atomic_t active;
81 + volatile int fd;
82 + FILE *volatile fp;
83 + volatile pid_t owner;
84 + char on_list;
85 + struct strbuf filename;
86 +};
87 +
88 +/*
89 + * Attempt to create a temporary file at the specified `path`. Return
90 + * a file descriptor for writing to it, or -1 on error. It is an error
91 + * if a file already exists at that path.
92 + */
93 +extern int create_tempfile(struct tempfile *tempfile, const char *path);
94 +
95 +/*
96 + * Associate a stdio stream with the temporary file (which must still
97 + * be open). Return `NULL` (*without* deleting the file) on error. The
98 + * stream is closed automatically when `close_tempfile()` is called or
99 + * when the file is deleted or renamed.
100 + */
101 +extern FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode);
102 +
103 +static inline int is_tempfile_active(struct tempfile *tempfile)
104 +{
105 + return tempfile->active;
106 +}
107 +
108 +/*
109 + * Return the path of the lockfile. The return value is a pointer to a
110 + * field within the lock_file object and should not be freed.
111 + */
112 +extern const char *get_tempfile_path(struct tempfile *tempfile);
113 +
114 +extern int get_tempfile_fd(struct tempfile *tempfile);
115 +extern FILE *get_tempfile_fp(struct tempfile *tempfile);
116 +
117 +/*
118 + * If the temporary file is still open, close it (and the file pointer
119 + * too, if it has been opened using `fdopen_tempfile()`) without
120 + * deleting the file. Return 0 upon success. On failure to `close(2)`,
121 + * return a negative value and delete the file. Usually
122 + * `delete_tempfile()` or `rename_tempfile()` should eventually be
123 + * called if `close_tempfile()` succeeds.
124 + */
125 +extern int close_tempfile(struct tempfile *tempfile);
126 +
127 +/*
128 + * Re-open a temporary file that has been closed using
129 + * `close_tempfile()` but not yet deleted or renamed. This can be used
130 + * to implement a sequence of operations like the following:
131 + *
132 + * * Create temporary file.
133 + *
134 + * * Write new contents to file, then `close_tempfile()` to cause the
135 + * contents to be written to disk.
136 + *
137 + * * Pass the name of the temporary file to another program to allow
138 + * it (and nobody else) to inspect or even modify the file's
139 + * contents.
140 + *
141 + * * `reopen_tempfile()` to reopen the temporary file. Make further
142 + * updates to the contents.
143 + *
144 + * * `rename_tempfile()` to move the file to its permanent location.
145 + */
146 +extern int reopen_tempfile(struct tempfile *tempfile);
147 +
148 +/*
149 + * Close the file descriptor and/or file pointer and remove the
150 + * temporary file associated with `tempfile`. It is a NOOP to call
151 + * `delete_tempfile()` for a `tempfile` object that has already been
152 + * deleted or renamed.
153 + */
154 +extern void delete_tempfile(struct tempfile *tempfile);
155 +
156 +/*
157 + * Close the file descriptor and/or file pointer if they are still
158 + * open, and atomically rename the temporary file to `path`. `path`
159 + * must be on the same filesystem as the lock file. Return 0 on
160 + * success. On failure, delete the temporary file and return -1, with
161 + * `errno` set to the value from the failing call to `close(2)` or
162 + * `rename(2)`. It is a bug to call `rename_tempfile()` for a
163 + * `tempfile` object that is not currently active.
164 + */
165 +extern int rename_tempfile(struct tempfile *tempfile, const char *path);
166 +
167 +#endif /* TEMPFILE_H */