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 */