Git fork
at reftables-rust 375 lines 9.6 kB view raw
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 * - 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. 20 * 21 * - Active, file open (after `create_tempfile()` or 22 * `reopen_tempfile()`). In this state: 23 * 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 30 * 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`. 34 * 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. 38 * 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. 43 */ 44 45#define USE_THE_REPOSITORY_VARIABLE 46 47#include "git-compat-util.h" 48#include "abspath.h" 49#include "path.h" 50#include "tempfile.h" 51#include "sigchain.h" 52 53static VOLATILE_LIST_HEAD(tempfile_list); 54 55static int remove_template_directory(struct tempfile *tempfile, 56 int in_signal_handler) 57{ 58 if (tempfile->directory) { 59 if (in_signal_handler) 60 return rmdir(tempfile->directory); 61 else 62 return rmdir_or_warn(tempfile->directory); 63 } 64 65 return 0; 66} 67 68static void remove_tempfiles(int in_signal_handler) 69{ 70 pid_t me = getpid(); 71 volatile struct volatile_list_head *pos; 72 73 list_for_each(pos, &tempfile_list) { 74 struct tempfile *p = list_entry(pos, struct tempfile, list); 75 76 if (!is_tempfile_active(p) || p->owner != me) 77 continue; 78 79 if (p->fd >= 0) 80 close(p->fd); 81 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); 87 } 88} 89 90static void remove_tempfiles_on_exit(void) 91{ 92 remove_tempfiles(0); 93} 94 95static void remove_tempfiles_on_signal(int signo) 96{ 97 remove_tempfiles(1); 98 sigchain_pop(signo); 99 raise(signo); 100} 101 102static struct tempfile *new_tempfile(void) 103{ 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; 112} 113 114static void activate_tempfile(struct tempfile *tempfile) 115{ 116 static int initialized; 117 118 if (!initialized) { 119 sigchain_push_common(remove_tempfiles_on_signal); 120 atexit(remove_tempfiles_on_exit); 121 initialized = 1; 122 } 123 124 volatile_list_add(&tempfile->list, &tempfile_list); 125 tempfile->owner = getpid(); 126} 127 128static void deactivate_tempfile(struct tempfile *tempfile) 129{ 130 volatile_list_del(&tempfile->list); 131 strbuf_release(&tempfile->filename); 132 free(tempfile->directory); 133 free(tempfile); 134} 135 136/* Make sure errno contains a meaningful value on error */ 137struct tempfile *create_tempfile_mode(const char *path, int mode) 138{ 139 struct tempfile *tempfile = new_tempfile(); 140 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; 151 } 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; 159 } 160 161 return tempfile; 162} 163 164struct tempfile *register_tempfile(const char *path) 165{ 166 struct tempfile *tempfile = new_tempfile(); 167 strbuf_add_absolute_path(&tempfile->filename, path); 168 activate_tempfile(tempfile); 169 return tempfile; 170} 171 172struct tempfile *mks_tempfile_sm(const char *filename_template, int suffixlen, int mode) 173{ 174 struct tempfile *tempfile = new_tempfile(); 175 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; 181 } 182 activate_tempfile(tempfile); 183 return tempfile; 184} 185 186struct tempfile *mks_tempfile_tsm(const char *filename_template, int suffixlen, int mode) 187{ 188 struct tempfile *tempfile = new_tempfile(); 189 const char *tmpdir; 190 191 tmpdir = getenv("TMPDIR"); 192 if (!tmpdir) 193 tmpdir = "/tmp"; 194 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; 200 } 201 activate_tempfile(tempfile); 202 return tempfile; 203} 204 205struct tempfile *mks_tempfile_dt(const char *directory_template, 206 const char *filename) 207{ 208 struct tempfile *tempfile; 209 const char *tmpdir; 210 struct strbuf sb = STRBUF_INIT; 211 int fd; 212 size_t directorylen; 213 214 if (!ends_with(directory_template, "XXXXXX")) { 215 errno = EINVAL; 216 return NULL; 217 } 218 219 tmpdir = getenv("TMPDIR"); 220 if (!tmpdir) 221 tmpdir = "/tmp"; 222 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; 230 } 231 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; 241 } 242 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; 249} 250 251struct tempfile *xmks_tempfile_m(const char *filename_template, int mode) 252{ 253 struct tempfile *tempfile; 254 struct strbuf full_template = STRBUF_INIT; 255 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); 261 262 strbuf_release(&full_template); 263 return tempfile; 264} 265 266FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode) 267{ 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"); 272 273 tempfile->fp = fdopen(tempfile->fd, mode); 274 return tempfile->fp; 275} 276 277const char *get_tempfile_path(struct tempfile *tempfile) 278{ 279 if (!is_tempfile_active(tempfile)) 280 BUG("get_tempfile_path() called for inactive object"); 281 return tempfile->filename.buf; 282} 283 284int get_tempfile_fd(struct tempfile *tempfile) 285{ 286 if (!is_tempfile_active(tempfile)) 287 BUG("get_tempfile_fd() called for inactive object"); 288 return tempfile->fd; 289} 290 291FILE *get_tempfile_fp(struct tempfile *tempfile) 292{ 293 if (!is_tempfile_active(tempfile)) 294 BUG("get_tempfile_fp() called for inactive object"); 295 return tempfile->fp; 296} 297 298int close_tempfile_gently(struct tempfile *tempfile) 299{ 300 int fd; 301 FILE *fp; 302 int err; 303 304 if (!is_tempfile_active(tempfile) || tempfile->fd < 0) 305 return 0; 306 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); 318 } 319 } else { 320 err = close(fd); 321 } 322 323 return err ? -1 : 0; 324} 325 326int reopen_tempfile(struct tempfile *tempfile) 327{ 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; 334} 335 336int rename_tempfile(struct tempfile **tempfile_p, const char *path) 337{ 338 struct tempfile *tempfile = *tempfile_p; 339 340 if (!is_tempfile_active(tempfile)) 341 BUG("rename_tempfile called for inactive object"); 342 343 if (close_tempfile_gently(tempfile)) { 344 delete_tempfile(tempfile_p); 345 return -1; 346 } 347 348 if (rename(tempfile->filename.buf, path)) { 349 int save_errno = errno; 350 delete_tempfile(tempfile_p); 351 errno = save_errno; 352 return -1; 353 } 354 355 deactivate_tempfile(tempfile); 356 *tempfile_p = NULL; 357 return 0; 358} 359 360int delete_tempfile(struct tempfile **tempfile_p) 361{ 362 struct tempfile *tempfile = *tempfile_p; 363 int err = 0; 364 365 if (!is_tempfile_active(tempfile)) 366 return 0; 367 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; 373 374 return err ? -1 : 0; 375}