/* 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 copy_file(const char *dst, const char *src, int mode) { int fdi, fdo, status; mode = (mode & 0111) ? 0777 : 0666; if ((fdi = open(src, O_RDONLY)) < 0) return fdi; if ((fdo = open(dst, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) { close(fdi); return fdo; } status = copy_fd(fdi, fdo); switch (status) { case COPY_READ_ERROR: error("copy-fd: read returned %s", strerror(errno)); break; case COPY_WRITE_ERROR: error("copy-fd: write returned %s", strerror(errno)); break; } close(fdi); if (close(fdo) != 0) return error("%s: close error: %s", dst, strerror(errno)); if (!status && adjust_shared_perm(dst)) return -1; return status; }
static int lock_file(struct lock_file *lk, const char *path) { if (strlen(path) >= sizeof(lk->filename)) return -1; strcpy(lk->filename, path); /* * subtract 5 from size to make sure there's room for adding * ".lock" for the lock file name */ resolve_symlink(lk->filename, sizeof(lk->filename)-5); strcat(lk->filename, ".lock"); lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666); if (0 <= lk->fd) { if (!lock_file_list) { signal(SIGINT, remove_lock_file_on_signal); signal(SIGHUP, remove_lock_file_on_signal); signal(SIGTERM, remove_lock_file_on_signal); signal(SIGQUIT, remove_lock_file_on_signal); atexit(remove_lock_file); } lk->owner = getpid(); if (!lk->on_list) { lk->next = lock_file_list; lock_file_list = lk; lk->on_list = 1; } if (adjust_shared_perm(lk->filename)) return error("cannot fix permission bits on %s", lk->filename); } else lk->filename[0] = 0; return lk->fd; }
static int log_ref_write(const char *ref_name, const unsigned char *old_sha1, const unsigned char *new_sha1, const char *msg) { int logfd, written, oflags = O_APPEND | O_WRONLY; unsigned maxlen, len; int msglen; char *log_file, *logrec; const char *committer; if (log_all_ref_updates < 0) log_all_ref_updates = !is_bare_repository(); log_file = git_path("logs/%s", ref_name); if (log_all_ref_updates && (!prefixcmp(ref_name, "refs/heads/") || !prefixcmp(ref_name, "refs/remotes/") || !strcmp(ref_name, "HEAD"))) { if (safe_create_leading_directories(log_file) < 0) return error("unable to create directory for %s", log_file); oflags |= O_CREAT; } logfd = open(log_file, oflags, 0666); if (logfd < 0) { if (!(oflags & O_CREAT) && errno == ENOENT) return 0; if ((oflags & O_CREAT) && errno == EISDIR) { if (remove_empty_directories(log_file)) { return error("There are still logs under '%s'", log_file); } logfd = open(log_file, oflags, 0666); } if (logfd < 0) return error("Unable to append to %s: %s", log_file, strerror(errno)); } adjust_shared_perm(log_file); msglen = msg ? strlen(msg) : 0; committer = git_committer_info(0); maxlen = strlen(committer) + msglen + 100; logrec = xmalloc(maxlen); len = sprintf(logrec, "%s %s %s\n", sha1_to_hex(old_sha1), sha1_to_hex(new_sha1), committer); if (msglen) len += copy_msg(logrec + len - 1, msg) - 1; written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1; free(logrec); if (close(logfd) != 0 || written != len) return error("Unable to append to %s", log_file); return 0; }
static int lock_file(struct lock_file *lk, const char *path, int flags) { /* * subtract 5 from size to make sure there's room for adding * ".lock" for the lock file name */ static const size_t max_path_len = sizeof(lk->filename) - 5; if (strlen(path) >= max_path_len) return -1; strcpy(lk->filename, path); if (!(flags & LOCK_NODEREF)) resolve_symlink(lk->filename, max_path_len); strcat(lk->filename, ".lock"); lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666); if (0 <= lk->fd) { if (!lock_file_list) { sigchain_push_common(remove_lock_file_on_signal); atexit(remove_lock_file); } lk->owner = getpid(); if (!lk->on_list) { lk->next = lock_file_list; lock_file_list = lk; lk->on_list = 1; } if (adjust_shared_perm(lk->filename)) return error("cannot fix permission bits on %s", lk->filename); } else lk->filename[0] = 0; return lk->fd; }
/* Make sure errno contains a meaningful value on error */ static int lock_file(struct lock_file *lk, const char *path, int flags) { size_t pathlen = strlen(path); if (!lock_file_list) { /* One-time initialization */ sigchain_push_common(remove_lock_files_on_signal); atexit(remove_lock_files_on_exit); } if (lk->active) die("BUG: cannot lock_file(\"%s\") using active struct lock_file", path); if (!lk->on_list) { /* Initialize *lk and add it to lock_file_list: */ lk->fd = -1; lk->fp = NULL; lk->active = 0; lk->owner = 0; strbuf_init(&lk->filename, pathlen + LOCK_SUFFIX_LEN); lk->next = lock_file_list; lock_file_list = lk; lk->on_list = 1; } else if (lk->filename.len) { /* This shouldn't happen, but better safe than sorry. */ die("BUG: lock_file(\"%s\") called with improperly-reset lock_file object", path); } if (flags & LOCK_NO_DEREF) { strbuf_add_absolute_path(&lk->filename, path); } else { struct strbuf resolved_path = STRBUF_INIT; strbuf_add(&resolved_path, path, pathlen); resolve_symlink(&resolved_path); strbuf_add_absolute_path(&lk->filename, resolved_path.buf); strbuf_release(&resolved_path); } strbuf_addstr(&lk->filename, LOCK_SUFFIX); lk->fd = open(lk->filename.buf, O_RDWR | O_CREAT | O_EXCL, 0666); if (lk->fd < 0) { strbuf_reset(&lk->filename); return -1; } lk->owner = getpid(); lk->active = 1; if (adjust_shared_perm(lk->filename.buf)) { int save_errno = errno; error("cannot fix permission bits on %s", lk->filename.buf); rollback_lock_file(lk); errno = save_errno; return -1; } return lk->fd; }
void safe_create_dir(const char *dir, int share) { if (mkdir(dir, 0777) < 0) { if (errno != EEXIST) { perror(dir); exit(1); } } else if (share && adjust_shared_perm(dir)) die(_("Could not make %s writable by group"), dir); }
static int is_rerere_enabled(void) { const char *rr_cache; int rr_cache_exists; if (!rerere_enabled) return 0; rr_cache = git_path("rr-cache"); rr_cache_exists = is_directory(rr_cache); if (rerere_enabled < 0) return rr_cache_exists; if (!rr_cache_exists && (mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache))) die("Could not create directory %s", rr_cache); return 1; }
void bitmap_writer_finish(struct pack_idx_entry **index, uint32_t index_nr, const char *filename, uint16_t options) { static char tmp_file[PATH_MAX]; static uint16_t default_version = 1; static uint16_t flags = BITMAP_OPT_FULL_DAG; struct sha1file *f; struct bitmap_disk_header header; int fd = odb_mkstemp(tmp_file, sizeof(tmp_file), "pack/tmp_bitmap_XXXXXX"); if (fd < 0) die_errno("unable to create '%s'", tmp_file); f = sha1fd(fd, tmp_file); memcpy(header.magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE)); header.version = htons(default_version); header.options = htons(flags | options); header.entry_count = htonl(writer.selected_nr); hashcpy(header.checksum, writer.pack_checksum); sha1write(f, &header, sizeof(header)); dump_bitmap(f, writer.commits); dump_bitmap(f, writer.trees); dump_bitmap(f, writer.blobs); dump_bitmap(f, writer.tags); write_selected_commits_v1(f, index, index_nr); if (options & BITMAP_OPT_HASH_CACHE) write_hash_cache(f, index, index_nr); sha1close(f, NULL, CSUM_FSYNC); if (adjust_shared_perm(tmp_file)) die_errno("unable to make temporary bitmap file readable"); if (rename(tmp_file, filename)) die_errno("unable to rename temporary bitmap file to '%s'", filename); }
void bitmap_writer_finish(struct pack_idx_entry **index, uint32_t index_nr, const char *filename, uint16_t options) { static uint16_t default_version = 1; static uint16_t flags = BITMAP_OPT_FULL_DAG; struct strbuf tmp_file = STRBUF_INIT; struct hashfile *f; struct bitmap_disk_header header; int fd = odb_mkstemp(&tmp_file, "pack/tmp_bitmap_XXXXXX"); f = hashfd(fd, tmp_file.buf); memcpy(header.magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE)); header.version = htons(default_version); header.options = htons(flags | options); header.entry_count = htonl(writer.selected_nr); hashcpy(header.checksum, writer.pack_checksum); hashwrite(f, &header, sizeof(header) - GIT_MAX_RAWSZ + the_hash_algo->rawsz); dump_bitmap(f, writer.commits); dump_bitmap(f, writer.trees); dump_bitmap(f, writer.blobs); dump_bitmap(f, writer.tags); write_selected_commits_v1(f, index, index_nr); if (options & BITMAP_OPT_HASH_CACHE) write_hash_cache(f, index, index_nr); finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE); if (adjust_shared_perm(tmp_file.buf)) die_errno("unable to make temporary bitmap file readable"); if (rename(tmp_file.buf, filename)) die_errno("unable to rename temporary bitmap file to '%s'", filename); strbuf_release(&tmp_file); }
/* 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; }
int log_ref_setup(const char *ref_name, char *logfile, int bufsize) { int logfd, oflags = O_APPEND | O_WRONLY; git_snpath(logfile, bufsize, "logs/%s", ref_name); if (log_all_ref_updates && (!prefixcmp(ref_name, "refs/heads/") || !prefixcmp(ref_name, "refs/remotes/") || !prefixcmp(ref_name, "refs/notes/") || !strcmp(ref_name, "HEAD"))) { if (safe_create_leading_directories(logfile) < 0) return error("unable to create directory for %s", logfile); oflags |= O_CREAT; } logfd = open(logfile, oflags, 0666); if (logfd < 0) { if (!(oflags & O_CREAT) && errno == ENOENT) return 0; if ((oflags & O_CREAT) && errno == EISDIR) { if (remove_empty_directories(logfile)) { return error("There are still logs under '%s'", logfile); } logfd = open(logfile, oflags, 0666); } if (logfd < 0) return error("Unable to append to %s: %s", logfile, strerror(errno)); } adjust_shared_perm(logfile); close(logfd); return 0; }
/* * Create the file "path" by writing to a temporary file and renaming * it into place. The contents of the file come from "generate", which * should return non-zero if it encounters an error. */ static int update_info_file(char *path, int (*generate)(FILE *)) { char *tmp = mkpathdup("%s_XXXXXX", path); int ret = -1; int fd = -1; FILE *fp = NULL; safe_create_leading_directories(path); fd = git_mkstemp_mode(tmp, 0666); if (fd < 0) goto out; fp = fdopen(fd, "w"); if (!fp) goto out; ret = generate(fp); if (ret) goto out; if (fclose(fp)) goto out; if (adjust_shared_perm(tmp) < 0) goto out; if (rename(tmp, path) < 0) goto out; ret = 0; out: if (ret) { error_errno("unable to update %s", path); if (fp) fclose(fp); else if (fd >= 0) close(fd); unlink(tmp); } free(tmp); return ret; }
int create_symref(const char *ref_target, const char *refs_heads_master, const char *logmsg) { const char *lockpath; char ref[1000]; int fd, len, written; char *git_HEAD = git_pathdup("%s", ref_target); unsigned char old_sha1[20], new_sha1[20]; if (logmsg && read_ref(ref_target, old_sha1)) hashclr(old_sha1); if (safe_create_leading_directories(git_HEAD) < 0) return error("unable to create directory for %s", git_HEAD); #ifndef NO_SYMLINK_HEAD if (prefer_symlink_refs) { unlink(git_HEAD); if (!symlink(refs_heads_master, git_HEAD)) goto done; fprintf(stderr, "no symlink - falling back to symbolic ref\n"); } #endif len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master); if (sizeof(ref) <= len) { error("refname too long: %s", refs_heads_master); goto error_free_return; } lockpath = mkpath("%s.lock", git_HEAD); fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666); if (fd < 0) { error("Unable to open %s for writing", lockpath); goto error_free_return; } written = write_in_full(fd, ref, len); if (close(fd) != 0 || written != len) { error("Unable to write to %s", lockpath); goto error_unlink_return; } if (rename(lockpath, git_HEAD) < 0) { error("Unable to create %s", git_HEAD); goto error_unlink_return; } if (adjust_shared_perm(git_HEAD)) { error("Unable to fix permissions on %s", lockpath); error_unlink_return: unlink_or_warn(lockpath); error_free_return: free(git_HEAD); return -1; } #ifndef NO_SYMLINK_HEAD done: #endif if (logmsg && !read_ref(refs_heads_master, new_sha1)) log_ref_write(ref_target, old_sha1, new_sha1, logmsg); free(git_HEAD); return 0; }
static int create_default_files(const char *template_path, const char *original_git_dir) { struct stat st1; struct strbuf buf = STRBUF_INIT; char *path; char repo_version_string[10]; char junk[2]; int reinit; int filemode; struct strbuf err = STRBUF_INIT; /* Just look for `init.templatedir` */ git_config(git_init_db_config, NULL); /* * First copy the templates -- we might have the default * config file there, in which case we would want to read * from it after installing. * * Before reading that config, we also need to clear out any cached * values (since we've just potentially changed what's available on * disk). */ copy_templates(template_path); git_config_clear(); reset_shared_repository(); git_config(git_default_config, NULL); /* * We must make sure command-line options continue to override any * values we might have just re-read from the config. */ is_bare_repository_cfg = init_is_bare_repository; if (init_shared_repository != -1) set_shared_repository(init_shared_repository); /* * We would have created the above under user's umask -- under * shared-repository settings, we would need to fix them up. */ if (get_shared_repository()) { adjust_shared_perm(get_git_dir()); } /* * We need to create a "refs" dir in any case so that older * versions of git can tell that this is a repository. */ safe_create_dir(git_path("refs"), 1); adjust_shared_perm(git_path("refs")); if (refs_init_db(&err)) die("failed to set up refs db: %s", err.buf); /* * Create the default symlink from ".git/HEAD" to the "master" * branch, if it does not exist yet. */ path = git_path_buf(&buf, "HEAD"); reinit = (!access(path, R_OK) || readlink(path, junk, sizeof(junk)-1) != -1); if (!reinit) { if (create_symref("HEAD", "refs/heads/master", NULL) < 0) exit(1); } /* This forces creation of new config file */ xsnprintf(repo_version_string, sizeof(repo_version_string), "%d", GIT_REPO_VERSION); git_config_set("core.repositoryformatversion", repo_version_string); /* Check filemode trustability */ path = git_path_buf(&buf, "config"); filemode = TEST_FILEMODE; if (TEST_FILEMODE && !lstat(path, &st1)) { struct stat st2; filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) && !lstat(path, &st2) && st1.st_mode != st2.st_mode && !chmod(path, st1.st_mode)); if (filemode && !reinit && (st1.st_mode & S_IXUSR)) filemode = 0; } git_config_set("core.filemode", filemode ? "true" : "false"); if (is_bare_repository()) git_config_set("core.bare", "true"); else { const char *work_tree = get_git_work_tree(); git_config_set("core.bare", "false"); /* allow template config file to override the default */ if (log_all_ref_updates == LOG_REFS_UNSET) git_config_set("core.logallrefupdates", "true"); if (needs_work_tree_config(original_git_dir, work_tree)) git_config_set("core.worktree", work_tree); } if (!reinit) { /* Check if symlink is supported in the work tree */ path = git_path_buf(&buf, "tXXXXXX"); if (!close(xmkstemp(path)) && !unlink(path) && !create_symlink(NULL, "testing", path) && !lstat(path, &st1) && S_ISLNK(st1.st_mode)) unlink(path); /* good */ else git_config_set("core.symlinks", "false"); /* Check if the filesystem is case-insensitive */ path = git_path_buf(&buf, "CoNfIg"); if (!access(path, F_OK)) git_config_set("core.ignorecase", "true"); probe_utf8_pathname_composition(); } strbuf_release(&buf); return reinit; }