1#include "cache.h"2#include "blob.h"3#include "dir.h"45static void create_directories(const char *path, const struct checkout *state)6{7int len = strlen(path);8char *buf = xmalloc(len + 1);9const char *slash = path;1011while ((slash = strchr(slash+1, '/')) != NULL) {12struct stat st;13int stat_status;1415len = slash - path;16memcpy(buf, path, len);17buf[len] = 0;1819if (len <= state->base_dir_len)20/*21* checkout-index --prefix=<dir>; <dir> is22* allowed to be a symlink to an existing23* directory.24*/25stat_status = stat(buf, &st);26else27/*28* if there currently is a symlink, we would29* want to replace it with a real directory.30*/31stat_status = lstat(buf, &st);3233if (!stat_status && S_ISDIR(st.st_mode))34continue; /* ok, it is already a directory. */3536/*37* We know stat_status == 0 means something exists38* there and this mkdir would fail, but that is an39* error codepath; we do not care, as we unlink and40* mkdir again in such a case.41*/42if (mkdir(buf, 0777)) {43if (errno == EEXIST && state->force &&44!unlink(buf) && !mkdir(buf, 0777))45continue;46die("cannot create directory at %s", buf);47}48}49free(buf);50}5152static void remove_subtree(const char *path)53{54DIR *dir = opendir(path);55struct dirent *de;56char pathbuf[PATH_MAX];57char *name;5859if (!dir)60die("cannot opendir %s (%s)", path, strerror(errno));61strcpy(pathbuf, path);62name = pathbuf + strlen(path);63*name++ = '/';64while ((de = readdir(dir)) != NULL) {65struct stat st;66if (is_dot_or_dotdot(de->d_name))67continue;68strcpy(name, de->d_name);69if (lstat(pathbuf, &st))70die("cannot lstat %s (%s)", pathbuf, strerror(errno));71if (S_ISDIR(st.st_mode))72remove_subtree(pathbuf);73else if (unlink(pathbuf))74die("cannot unlink %s (%s)", pathbuf, strerror(errno));75}76closedir(dir);77if (rmdir(path))78die("cannot rmdir %s (%s)", path, strerror(errno));79}8081static int create_file(const char *path, unsigned int mode)82{83mode = (mode & 0100) ? 0777 : 0666;84return open(path, O_WRONLY | O_CREAT | O_EXCL, mode);85}8687static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned long *size)88{89enum object_type type;90void *new = read_sha1_file(ce->sha1, &type, size);9192if (new) {93if (type == OBJ_BLOB)94return new;95free(new);96}97return NULL;98}99100static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile)101{102int fd;103long wrote;104105switch (ce->ce_mode & S_IFMT) {106char *new;107struct strbuf buf;108unsigned long size;109110case S_IFREG:111new = read_blob_entry(ce, path, &size);112if (!new)113return error("git checkout-index: unable to read sha1 file of %s (%s)",114path, sha1_to_hex(ce->sha1));115116/*117* Convert from git internal format to working tree format118*/119strbuf_init(&buf, 0);120if (convert_to_working_tree(ce->name, new, size, &buf)) {121size_t newsize = 0;122free(new);123new = strbuf_detach(&buf, &newsize);124size = newsize;125}126127if (to_tempfile) {128strcpy(path, ".merge_file_XXXXXX");129fd = mkstemp(path);130} else131fd = create_file(path, ce->ce_mode);132if (fd < 0) {133free(new);134return error("git checkout-index: unable to create file %s (%s)",135path, strerror(errno));136}137138wrote = write_in_full(fd, new, size);139close(fd);140free(new);141if (wrote != size)142return error("git checkout-index: unable to write file %s", path);143break;144case S_IFLNK:145new = read_blob_entry(ce, path, &size);146if (!new)147return error("git checkout-index: unable to read sha1 file of %s (%s)",148path, sha1_to_hex(ce->sha1));149if (to_tempfile || !has_symlinks) {150if (to_tempfile) {151strcpy(path, ".merge_link_XXXXXX");152fd = mkstemp(path);153} else154fd = create_file(path, 0666);155if (fd < 0) {156free(new);157return error("git checkout-index: unable to create "158"file %s (%s)", path, strerror(errno));159}160wrote = write_in_full(fd, new, size);161close(fd);162free(new);163if (wrote != size)164return error("git checkout-index: unable to write file %s",165path);166} else {167wrote = symlink(new, path);168free(new);169if (wrote)170return error("git checkout-index: unable to create "171"symlink %s (%s)", path, strerror(errno));172}173break;174case S_IFGITLINK:175if (to_tempfile)176return error("git checkout-index: cannot create temporary subproject %s", path);177if (mkdir(path, 0777) < 0)178return error("git checkout-index: cannot create subproject directory %s", path);179break;180default:181return error("git checkout-index: unknown file mode for %s", path);182}183184if (state->refresh_cache) {185struct stat st;186lstat(ce->name, &st);187fill_stat_cache_info(ce, &st);188}189return 0;190}191192int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath)193{194static char path[PATH_MAX + 1];195struct stat st;196int len = state->base_dir_len;197198if (topath)199return write_entry(ce, topath, state, 1);200201memcpy(path, state->base_dir, len);202strcpy(path + len, ce->name);203204if (!lstat(path, &st)) {205unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);206if (!changed)207return 0;208if (!state->force) {209if (!state->quiet)210fprintf(stderr, "git-checkout-index: %s already exists\n", path);211return -1;212}213214/*215* We unlink the old file, to get the new one with the216* right permissions (including umask, which is nasty217* to emulate by hand - much easier to let the system218* just do the right thing)219*/220if (S_ISDIR(st.st_mode)) {221/* If it is a gitlink, leave it alone! */222if (S_ISGITLINK(ce->ce_mode))223return 0;224if (!state->force)225return error("%s is a directory", path);226remove_subtree(path);227} else if (unlink(path))228return error("unable to unlink old '%s' (%s)", path, strerror(errno));229} else if (state->not_new)230return 0;231create_directories(path, state);232return write_entry(ce, path, state, 0);233}