/* Make sure errno contains a meaningful value on error */ int create_tempfile(struct tempfile *tempfile, const char *path) { prepare_tempfile_object(tempfile); strbuf_add_absolute_path(&tempfile->filename, path); tempfile->fd = open(tempfile->filename.buf, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0666); if (O_CLOEXEC && tempfile->fd < 0 && errno == EINVAL) /* Try again w/o O_CLOEXEC: the kernel might not support it */ tempfile->fd = open(tempfile->filename.buf, O_RDWR | O_CREAT | O_EXCL, 0666); if (tempfile->fd < 0) { strbuf_reset(&tempfile->filename); return -1; } tempfile->owner = getpid(); tempfile->active = 1; if (adjust_shared_perm(tempfile->filename.buf)) { int save_errno = errno; error("cannot fix permission bits on %s", tempfile->filename.buf); delete_tempfile(tempfile); errno = save_errno; return -1; } return tempfile->fd; }
int main(int argc, const char **argv) { const char *socket_path; static const char *usage[] = { "git-credential-cache--daemon [opts] <socket_path>", NULL }; int debug = 0; const struct option options[] = { OPT_BOOL(0, "debug", &debug, N_("print debugging messages to stderr")), OPT_END() }; argc = parse_options(argc, argv, NULL, options, usage, 0); socket_path = argv[0]; if (!socket_path) usage_with_options(usage, options); check_socket_directory(socket_path); register_tempfile(&socket_file, socket_path); serve_cache(socket_path, debug); delete_tempfile(&socket_file); return 0; }
int verify_signed_buffer(const char *payload, size_t payload_size, const char *signature, size_t signature_size, struct strbuf *gpg_output, struct strbuf *gpg_status) { struct child_process gpg = CHILD_PROCESS_INIT; struct tempfile *temp; int ret; struct strbuf buf = STRBUF_INIT; temp = mks_tempfile_t(".git_vtag_tmpXXXXXX"); if (!temp) return error_errno(_("could not create temporary file")); if (write_in_full(temp->fd, signature, signature_size) < 0 || close_tempfile_gently(temp) < 0) { error_errno(_("failed writing detached signature to '%s'"), temp->filename.buf); delete_tempfile(&temp); return -1; } argv_array_pushl(&gpg.args, gpg_program, "--status-fd=1", "--keyid-format=long", "--verify", temp->filename.buf, "-", NULL); if (!gpg_status) gpg_status = &buf; sigchain_push(SIGPIPE, SIG_IGN); ret = pipe_command(&gpg, payload, payload_size, gpg_status, 0, gpg_output, 0); sigchain_pop(SIGPIPE); delete_tempfile(&temp); ret |= !strstr(gpg_status->buf, "\n[GNUPG:] GOODSIG "); strbuf_release(&buf); /* no matter it was used or not */ return ret; }
static void remove_tempfiles(int skip_fclose) { pid_t me = getpid(); while (tempfile_list) { if (tempfile_list->owner == me) { /* fclose() is not safe to call in a signal handler */ if (skip_fclose) tempfile_list->fp = NULL; delete_tempfile(tempfile_list); } tempfile_list = tempfile_list->next; } }
/* Make sure errno contains a meaningful value on error */ int create_tempfile(struct tempfile *tempfile, const char *path) { prepare_tempfile_object(tempfile); strbuf_add_absolute_path(&tempfile->filename, path); tempfile->fd = open(tempfile->filename.buf, O_RDWR | O_CREAT | O_EXCL, 0666); if (tempfile->fd < 0) { strbuf_reset(&tempfile->filename); return -1; } tempfile->owner = getpid(); tempfile->active = 1; if (adjust_shared_perm(tempfile->filename.buf)) { int save_errno = errno; error("cannot fix permission bits on %s", tempfile->filename.buf); delete_tempfile(tempfile); errno = save_errno; return -1; } return tempfile->fd; }
static void packed_transaction_cleanup(struct packed_ref_store *refs, struct ref_transaction *transaction) { struct packed_transaction_backend_data *data = transaction->backend_data; if (data) { string_list_clear(&data->updates, 0); if (is_tempfile_active(refs->tempfile)) delete_tempfile(&refs->tempfile); if (data->own_lock && is_lock_file_locked(&refs->lock)) { packed_refs_unlock(&refs->base); data->own_lock = 0; } free(data); transaction->backend_data = NULL; } transaction->state = REF_TRANSACTION_CLOSED; }
int cmd_main(int argc, const char **argv) { const char *socket_path; int ignore_sighup = 0; static const char *usage[] = { "git-credential-cache--daemon [opts] <socket_path>", NULL }; int debug = 0; const struct option options[] = { OPT_BOOL(0, "debug", &debug, N_("print debugging messages to stderr")), OPT_END() }; git_config_get_bool("credentialcache.ignoresighup", &ignore_sighup); argc = parse_options(argc, argv, NULL, options, usage, 0); socket_path = argv[0]; if (!socket_path) usage_with_options(usage, options); if (!is_absolute_path(socket_path)) die("socket directory must be an absolute path"); init_socket_directory(socket_path); register_tempfile(&socket_file, socket_path); if (ignore_sighup) signal(SIGHUP, SIG_IGN); serve_cache(socket_path, debug); delete_tempfile(&socket_file); return 0; }
/* * Write the packed refs from the current snapshot to the packed-refs * tempfile, incorporating any changes from `updates`. `updates` must * be a sorted string list whose keys are the refnames and whose util * values are `struct ref_update *`. On error, rollback the tempfile, * write an error message to `err`, and return a nonzero value. * * The packfile must be locked before calling this function and will * remain locked when it is done. */ static int write_with_updates(struct packed_ref_store *refs, struct string_list *updates, struct strbuf *err) { struct ref_iterator *iter = NULL; size_t i; int ok; FILE *out; struct strbuf sb = STRBUF_INIT; char *packed_refs_path; if (!is_lock_file_locked(&refs->lock)) die("BUG: write_with_updates() called while unlocked"); /* * If packed-refs is a symlink, we want to overwrite the * symlinked-to file, not the symlink itself. Also, put the * staging file next to it: */ packed_refs_path = get_locked_file_path(&refs->lock); strbuf_addf(&sb, "%s.new", packed_refs_path); free(packed_refs_path); refs->tempfile = create_tempfile(sb.buf); if (!refs->tempfile) { strbuf_addf(err, "unable to create file %s: %s", sb.buf, strerror(errno)); strbuf_release(&sb); return -1; } strbuf_release(&sb); out = fdopen_tempfile(refs->tempfile, "w"); if (!out) { strbuf_addf(err, "unable to fdopen packed-refs tempfile: %s", strerror(errno)); goto error; } if (fprintf(out, "%s", PACKED_REFS_HEADER) < 0) goto write_error; /* * We iterate in parallel through the current list of refs and * the list of updates, processing an entry from at least one * of the lists each time through the loop. When the current * list of refs is exhausted, set iter to NULL. When the list * of updates is exhausted, leave i set to updates->nr. */ iter = packed_ref_iterator_begin(&refs->base, "", DO_FOR_EACH_INCLUDE_BROKEN); if ((ok = ref_iterator_advance(iter)) != ITER_OK) iter = NULL; i = 0; while (iter || i < updates->nr) { struct ref_update *update = NULL; int cmp; if (i >= updates->nr) { cmp = -1; } else { update = updates->items[i].util; if (!iter) cmp = +1; else cmp = strcmp(iter->refname, update->refname); } if (!cmp) { /* * There is both an old value and an update * for this reference. Check the old value if * necessary: */ if ((update->flags & REF_HAVE_OLD)) { if (is_null_oid(&update->old_oid)) { strbuf_addf(err, "cannot update ref '%s': " "reference already exists", update->refname); goto error; } else if (oidcmp(&update->old_oid, iter->oid)) { strbuf_addf(err, "cannot update ref '%s': " "is at %s but expected %s", update->refname, oid_to_hex(iter->oid), oid_to_hex(&update->old_oid)); goto error; } } /* Now figure out what to use for the new value: */ if ((update->flags & REF_HAVE_NEW)) { /* * The update takes precedence. Skip * the iterator over the unneeded * value. */ if ((ok = ref_iterator_advance(iter)) != ITER_OK) iter = NULL; cmp = +1; } else { /* * The update doesn't actually want to * change anything. We're done with it. */ i++; cmp = -1; } } else if (cmp > 0) { /* * There is no old value but there is an * update for this reference. Make sure that * the update didn't expect an existing value: */ if ((update->flags & REF_HAVE_OLD) && !is_null_oid(&update->old_oid)) { strbuf_addf(err, "cannot update ref '%s': " "reference is missing but expected %s", update->refname, oid_to_hex(&update->old_oid)); goto error; } } if (cmp < 0) { /* Pass the old reference through. */ struct object_id peeled; int peel_error = ref_iterator_peel(iter, &peeled); if (write_packed_entry(out, iter->refname, iter->oid, peel_error ? NULL : &peeled)) goto write_error; if ((ok = ref_iterator_advance(iter)) != ITER_OK) iter = NULL; } else if (is_null_oid(&update->new_oid)) { /* * The update wants to delete the reference, * and the reference either didn't exist or we * have already skipped it. So we're done with * the update (and don't have to write * anything). */ i++; } else { struct object_id peeled; int peel_error = peel_object(&update->new_oid, &peeled); if (write_packed_entry(out, update->refname, &update->new_oid, peel_error ? NULL : &peeled)) goto write_error; i++; } } if (ok != ITER_DONE) { strbuf_addstr(err, "unable to write packed-refs file: " "error iterating over old contents"); goto error; } if (close_tempfile_gently(refs->tempfile)) { strbuf_addf(err, "error closing file %s: %s", get_tempfile_path(refs->tempfile), strerror(errno)); strbuf_release(&sb); delete_tempfile(&refs->tempfile); return -1; } return 0; write_error: strbuf_addf(err, "error writing to %s: %s", get_tempfile_path(refs->tempfile), strerror(errno)); error: if (iter) ref_iterator_abort(iter); delete_tempfile(&refs->tempfile); return -1; }
int cmd_gc(int argc, const char **argv, const char *prefix) { int aggressive = 0; int auto_gc = 0; int quiet = 0; int force = 0; const char *name; pid_t pid; int daemonized = 0; int keep_base_pack = -1; timestamp_t dummy; struct option builtin_gc_options[] = { OPT__QUIET(&quiet, N_("suppress progress reporting")), { OPTION_STRING, 0, "prune", &prune_expire, N_("date"), N_("prune unreferenced objects"), PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire }, OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")), OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"), PARSE_OPT_NOCOMPLETE), OPT_BOOL_F(0, "force", &force, N_("force running gc even if there may be another gc running"), PARSE_OPT_NOCOMPLETE), OPT_BOOL(0, "keep-largest-pack", &keep_base_pack, N_("repack all other packs except the largest pack")), OPT_END() }; if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_gc_usage, builtin_gc_options); argv_array_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL); argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL); argv_array_pushl(&repack, "repack", "-d", "-l", NULL); argv_array_pushl(&prune, "prune", "--expire", NULL); argv_array_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL); argv_array_pushl(&rerere, "rerere", "gc", NULL); /* default expiry time, overwritten in gc_config */ gc_config(); if (parse_expiry_date(gc_log_expire, &gc_log_expire_time)) die(_("failed to parse gc.logexpiry value %s"), gc_log_expire); if (pack_refs < 0) pack_refs = !is_bare_repository(); argc = parse_options(argc, argv, prefix, builtin_gc_options, builtin_gc_usage, 0); if (argc > 0) usage_with_options(builtin_gc_usage, builtin_gc_options); if (prune_expire && parse_expiry_date(prune_expire, &dummy)) die(_("failed to parse prune expiry value %s"), prune_expire); if (aggressive) { argv_array_push(&repack, "-f"); if (aggressive_depth > 0) argv_array_pushf(&repack, "--depth=%d", aggressive_depth); if (aggressive_window > 0) argv_array_pushf(&repack, "--window=%d", aggressive_window); } if (quiet) argv_array_push(&repack, "-q"); if (auto_gc) { /* * Auto-gc should be least intrusive as possible. */ if (!need_to_gc()) return 0; if (!quiet) { if (detach_auto) fprintf(stderr, _("Auto packing the repository in background for optimum performance.\n")); else fprintf(stderr, _("Auto packing the repository for optimum performance.\n")); fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n")); } if (detach_auto) { int ret = report_last_gc_error(); if (ret < 0) /* an I/O error occured, already reported */ exit(128); if (ret == 1) /* Last gc --auto failed. Skip this one. */ return 0; if (lock_repo_for_gc(force, &pid)) return 0; gc_before_repack(); /* dies on failure */ delete_tempfile(&pidfile); /* * failure to daemonize is ok, we'll continue * in foreground */ daemonized = !daemonize(); } } else { struct string_list keep_pack = STRING_LIST_INIT_NODUP; if (keep_base_pack != -1) { if (keep_base_pack) find_base_packs(&keep_pack, 0); } else if (big_pack_threshold) { find_base_packs(&keep_pack, big_pack_threshold); } add_repack_all_option(&keep_pack); string_list_clear(&keep_pack, 0); } name = lock_repo_for_gc(force, &pid); if (name) { if (auto_gc) return 0; /* be quiet on --auto */ die(_("gc is already running on machine '%s' pid %"PRIuMAX" (use --force if not)"), name, (uintmax_t)pid); } if (daemonized) { hold_lock_file_for_update(&log_lock, git_path("gc.log"), LOCK_DIE_ON_ERROR); dup2(get_lock_file_fd(&log_lock), 2); sigchain_push_common(process_log_file_on_signal); atexit(process_log_file_at_exit); } gc_before_repack(); if (!repository_format_precious_objects) { close_all_packs(the_repository->objects); if (run_command_v_opt(repack.argv, RUN_GIT_CMD)) die(FAILED_RUN, repack.argv[0]); if (prune_expire) { argv_array_push(&prune, prune_expire); if (quiet) argv_array_push(&prune, "--no-progress"); if (repository_format_partial_clone) argv_array_push(&prune, "--exclude-promisor-objects"); if (run_command_v_opt(prune.argv, RUN_GIT_CMD)) die(FAILED_RUN, prune.argv[0]); } } if (prune_worktrees_expire) { argv_array_push(&prune_worktrees, prune_worktrees_expire); if (run_command_v_opt(prune_worktrees.argv, RUN_GIT_CMD)) die(FAILED_RUN, prune_worktrees.argv[0]); } if (run_command_v_opt(rerere.argv, RUN_GIT_CMD)) die(FAILED_RUN, rerere.argv[0]); report_garbage = report_pack_garbage; reprepare_packed_git(the_repository); if (pack_garbage.nr > 0) clean_pack_garbage(); if (gc_write_commit_graph) write_commit_graph_reachable(get_object_directory(), 0, !quiet && !daemonized); if (auto_gc && too_many_loose_objects()) warning(_("There are too many unreachable loose objects; " "run 'git prune' to remove them.")); if (!daemonized) unlink(git_path("gc.log")); return 0; }