The sixteenth batch
[git/gitster.git] / tempfile.c
blob82dfa3d82f2ac9c842088d6833181cd6214b56a9
1 /*
2 * State diagram and cleanup
3 * -------------------------
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.
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.
15 * The possible states of a `tempfile` object are as follows:
17 * - Inactive/unallocated. The only way to get a tempfile is via a creation
18 * function like create_tempfile(). Once allocated, the tempfile is on the
19 * global tempfile_list and considered active.
21 * - Active, file open (after `create_tempfile()` or
22 * `reopen_tempfile()`). In this state:
24 * - the temporary file exists
25 * - `filename` holds the filename of the temporary file
26 * - `fd` holds a file descriptor open for writing to it
27 * - `fp` holds a pointer to an open `FILE` object if and only if
28 * `fdopen_tempfile()` has been called on the object
29 * - `owner` holds the PID of the process that created the file
31 * - Active, file closed (after `close_tempfile_gently()`). Same
32 * as the previous state, except that the temporary file is closed,
33 * `fd` is -1, and `fp` is `NULL`.
35 * - Inactive (after `delete_tempfile()`, `rename_tempfile()`, or a
36 * failed attempt to create a temporary file). The struct is removed from
37 * the global tempfile_list and deallocated.
39 * A temporary file is owned by the process that created it. The
40 * `tempfile` has an `owner` field that records the owner's PID. This
41 * field is used to prevent a forked process from deleting a temporary
42 * file created by its parent.
45 #define USE_THE_REPOSITORY_VARIABLE
47 #include "git-compat-util.h"
48 #include "abspath.h"
49 #include "path.h"
50 #include "tempfile.h"
51 #include "sigchain.h"
53 static VOLATILE_LIST_HEAD(tempfile_list);
55 static int remove_template_directory(struct tempfile *tempfile,
56 int in_signal_handler)
58 if (tempfile->directory) {
59 if (in_signal_handler)
60 return rmdir(tempfile->directory);
61 else
62 return rmdir_or_warn(tempfile->directory);
65 return 0;
68 static void remove_tempfiles(int in_signal_handler)
70 pid_t me = getpid();
71 volatile struct volatile_list_head *pos;
73 list_for_each(pos, &tempfile_list) {
74 struct tempfile *p = list_entry(pos, struct tempfile, list);
76 if (!is_tempfile_active(p) || p->owner != me)
77 continue;
79 if (p->fd >= 0)
80 close(p->fd);
82 if (in_signal_handler)
83 unlink(p->filename.buf);
84 else
85 unlink_or_warn(p->filename.buf);
86 remove_template_directory(p, in_signal_handler);
90 static void remove_tempfiles_on_exit(void)
92 remove_tempfiles(0);
95 static void remove_tempfiles_on_signal(int signo)
97 remove_tempfiles(1);
98 sigchain_pop(signo);
99 raise(signo);
102 static struct tempfile *new_tempfile(void)
104 struct tempfile *tempfile = xmalloc(sizeof(*tempfile));
105 tempfile->fd = -1;
106 tempfile->fp = NULL;
107 tempfile->owner = 0;
108 INIT_LIST_HEAD(&tempfile->list);
109 strbuf_init(&tempfile->filename, 0);
110 tempfile->directory = NULL;
111 return tempfile;
114 static void activate_tempfile(struct tempfile *tempfile)
116 static int initialized;
118 if (!initialized) {
119 sigchain_push_common(remove_tempfiles_on_signal);
120 atexit(remove_tempfiles_on_exit);
121 initialized = 1;
124 volatile_list_add(&tempfile->list, &tempfile_list);
125 tempfile->owner = getpid();
128 static void deactivate_tempfile(struct tempfile *tempfile)
130 volatile_list_del(&tempfile->list);
131 strbuf_release(&tempfile->filename);
132 free(tempfile->directory);
133 free(tempfile);
136 /* Make sure errno contains a meaningful value on error */
137 struct tempfile *create_tempfile_mode(const char *path, int mode)
139 struct tempfile *tempfile = new_tempfile();
141 strbuf_add_absolute_path(&tempfile->filename, path);
142 tempfile->fd = open(tempfile->filename.buf,
143 O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, mode);
144 if (O_CLOEXEC && tempfile->fd < 0 && errno == EINVAL)
145 /* Try again w/o O_CLOEXEC: the kernel might not support it */
146 tempfile->fd = open(tempfile->filename.buf,
147 O_RDWR | O_CREAT | O_EXCL, mode);
148 if (tempfile->fd < 0) {
149 deactivate_tempfile(tempfile);
150 return NULL;
152 activate_tempfile(tempfile);
153 if (adjust_shared_perm(the_repository, tempfile->filename.buf)) {
154 int save_errno = errno;
155 error("cannot fix permission bits on %s", tempfile->filename.buf);
156 delete_tempfile(&tempfile);
157 errno = save_errno;
158 return NULL;
161 return tempfile;
164 struct tempfile *register_tempfile(const char *path)
166 struct tempfile *tempfile = new_tempfile();
167 strbuf_add_absolute_path(&tempfile->filename, path);
168 activate_tempfile(tempfile);
169 return tempfile;
172 struct tempfile *mks_tempfile_sm(const char *filename_template, int suffixlen, int mode)
174 struct tempfile *tempfile = new_tempfile();
176 strbuf_add_absolute_path(&tempfile->filename, filename_template);
177 tempfile->fd = git_mkstemps_mode(tempfile->filename.buf, suffixlen, mode);
178 if (tempfile->fd < 0) {
179 deactivate_tempfile(tempfile);
180 return NULL;
182 activate_tempfile(tempfile);
183 return tempfile;
186 struct tempfile *mks_tempfile_tsm(const char *filename_template, int suffixlen, int mode)
188 struct tempfile *tempfile = new_tempfile();
189 const char *tmpdir;
191 tmpdir = getenv("TMPDIR");
192 if (!tmpdir)
193 tmpdir = "/tmp";
195 strbuf_addf(&tempfile->filename, "%s/%s", tmpdir, filename_template);
196 tempfile->fd = git_mkstemps_mode(tempfile->filename.buf, suffixlen, mode);
197 if (tempfile->fd < 0) {
198 deactivate_tempfile(tempfile);
199 return NULL;
201 activate_tempfile(tempfile);
202 return tempfile;
205 struct tempfile *mks_tempfile_dt(const char *directory_template,
206 const char *filename)
208 struct tempfile *tempfile;
209 const char *tmpdir;
210 struct strbuf sb = STRBUF_INIT;
211 int fd;
212 size_t directorylen;
214 if (!ends_with(directory_template, "XXXXXX")) {
215 errno = EINVAL;
216 return NULL;
219 tmpdir = getenv("TMPDIR");
220 if (!tmpdir)
221 tmpdir = "/tmp";
223 strbuf_addf(&sb, "%s/%s", tmpdir, directory_template);
224 directorylen = sb.len;
225 if (!mkdtemp(sb.buf)) {
226 int orig_errno = errno;
227 strbuf_release(&sb);
228 errno = orig_errno;
229 return NULL;
232 strbuf_addf(&sb, "/%s", filename);
233 fd = open(sb.buf, O_CREAT | O_EXCL | O_RDWR, 0600);
234 if (fd < 0) {
235 int orig_errno = errno;
236 strbuf_setlen(&sb, directorylen);
237 rmdir(sb.buf);
238 strbuf_release(&sb);
239 errno = orig_errno;
240 return NULL;
243 tempfile = new_tempfile();
244 strbuf_swap(&tempfile->filename, &sb);
245 tempfile->directory = xmemdupz(tempfile->filename.buf, directorylen);
246 tempfile->fd = fd;
247 activate_tempfile(tempfile);
248 return tempfile;
251 struct tempfile *xmks_tempfile_m(const char *filename_template, int mode)
253 struct tempfile *tempfile;
254 struct strbuf full_template = STRBUF_INIT;
256 strbuf_add_absolute_path(&full_template, filename_template);
257 tempfile = mks_tempfile_m(full_template.buf, mode);
258 if (!tempfile)
259 die_errno("Unable to create temporary file '%s'",
260 full_template.buf);
262 strbuf_release(&full_template);
263 return tempfile;
266 FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode)
268 if (!is_tempfile_active(tempfile))
269 BUG("fdopen_tempfile() called for inactive object");
270 if (tempfile->fp)
271 BUG("fdopen_tempfile() called for open object");
273 tempfile->fp = fdopen(tempfile->fd, mode);
274 return tempfile->fp;
277 const char *get_tempfile_path(struct tempfile *tempfile)
279 if (!is_tempfile_active(tempfile))
280 BUG("get_tempfile_path() called for inactive object");
281 return tempfile->filename.buf;
284 int get_tempfile_fd(struct tempfile *tempfile)
286 if (!is_tempfile_active(tempfile))
287 BUG("get_tempfile_fd() called for inactive object");
288 return tempfile->fd;
291 FILE *get_tempfile_fp(struct tempfile *tempfile)
293 if (!is_tempfile_active(tempfile))
294 BUG("get_tempfile_fp() called for inactive object");
295 return tempfile->fp;
298 int close_tempfile_gently(struct tempfile *tempfile)
300 int fd;
301 FILE *fp;
302 int err;
304 if (!is_tempfile_active(tempfile) || tempfile->fd < 0)
305 return 0;
307 fd = tempfile->fd;
308 fp = tempfile->fp;
309 tempfile->fd = -1;
310 if (fp) {
311 tempfile->fp = NULL;
312 if (ferror(fp)) {
313 err = -1;
314 if (!fclose(fp))
315 errno = EIO;
316 } else {
317 err = fclose(fp);
319 } else {
320 err = close(fd);
323 return err ? -1 : 0;
326 int reopen_tempfile(struct tempfile *tempfile)
328 if (!is_tempfile_active(tempfile))
329 BUG("reopen_tempfile called for an inactive object");
330 if (0 <= tempfile->fd)
331 BUG("reopen_tempfile called for an open object");
332 tempfile->fd = open(tempfile->filename.buf, O_WRONLY|O_TRUNC);
333 return tempfile->fd;
336 int rename_tempfile(struct tempfile **tempfile_p, const char *path)
338 struct tempfile *tempfile = *tempfile_p;
340 if (!is_tempfile_active(tempfile))
341 BUG("rename_tempfile called for inactive object");
343 if (close_tempfile_gently(tempfile)) {
344 delete_tempfile(tempfile_p);
345 return -1;
348 if (rename(tempfile->filename.buf, path)) {
349 int save_errno = errno;
350 delete_tempfile(tempfile_p);
351 errno = save_errno;
352 return -1;
355 deactivate_tempfile(tempfile);
356 *tempfile_p = NULL;
357 return 0;
360 int delete_tempfile(struct tempfile **tempfile_p)
362 struct tempfile *tempfile = *tempfile_p;
363 int err = 0;
365 if (!is_tempfile_active(tempfile))
366 return 0;
368 err |= close_tempfile_gently(tempfile);
369 err |= unlink_or_warn(tempfile->filename.buf);
370 err |= remove_template_directory(tempfile, 0);
371 deactivate_tempfile(tempfile);
372 *tempfile_p = NULL;
374 return err ? -1 : 0;