X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=init-db.c;h=bd88291b0efd3eb9c46195d413db1aba820db2dd;hb=b17e659dd4007cb1d3eb5ac32b524c0c5ab59601;hp=bc65086368b44f37a8c7e47734e5b0dffed8873c;hpb=8bc9a0c769ac1df7820f2dbf8f7b7d64835e3c68;p=git.git diff --git a/init-db.c b/init-db.c index bc650863..bd88291b 100644 --- a/init-db.c +++ b/init-db.c @@ -5,52 +5,264 @@ */ #include "cache.h" -int main(int argc, char **argv) +#ifndef DEFAULT_GIT_TEMPLATE_DIR +#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates/" +#endif + +static void safe_create_dir(const char *dir) +{ + if (mkdir(dir, 0777) < 0) { + if (errno != EEXIST) { + perror(dir); + exit(1); + } + } +} + +static int copy_file(const char *dst, const char *src, int mode) { - char *sha1_dir = getenv(DB_ENVIRONMENT), *path; - int len, i, fd; + int fdi, fdo, status; - if (mkdir(".dircache", 0700) < 0) { - perror("unable to create .dircache"); - exit(1); + 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); + close(fdo); + return status; +} - /* - * If you want to, you can share the DB area with any number of branches. - * That has advantages: you can save space by sharing all the SHA1 objects. - * On the other hand, it might just make lookup slower and messier. You - * be the judge. +static void copy_templates_1(char *path, int baselen, + char *template, int template_baselen, + DIR *dir) +{ + struct dirent *de; + + /* Note: if ".git/hooks" file exists in the repository being + * re-initialized, /etc/core-git/templates/hooks/update would + * cause git-init-db to fail here. I think this is sane but + * it means that the set of templates we ship by default, along + * with the way the namespace under .git/ is organized, should + * be really carefully chosen. */ - sha1_dir = getenv(DB_ENVIRONMENT); - if (sha1_dir) { - struct stat st; - if (!stat(sha1_dir, &st) < 0 && S_ISDIR(st.st_mode)) - return; - fprintf(stderr, "DB_ENVIRONMENT set to bad directory %s: ", sha1_dir); + safe_create_dir(path); + while ((de = readdir(dir)) != NULL) { + struct stat st_git, st_template; + int namelen; + int exists = 0; + + if (de->d_name[0] == '.') + continue; + namelen = strlen(de->d_name); + if ((PATH_MAX <= baselen + namelen) || + (PATH_MAX <= template_baselen + namelen)) + die("insanely long template name %s", de->d_name); + memcpy(path + baselen, de->d_name, namelen+1); + memcpy(template + template_baselen, de->d_name, namelen+1); + if (lstat(path, &st_git)) { + if (errno != ENOENT) + die("cannot stat %s", path); + } + else + exists = 1; + + if (lstat(template, &st_template)) + die("cannot stat template %s", template); + + if (S_ISDIR(st_template.st_mode)) { + DIR *subdir = opendir(template); + int baselen_sub = baselen + namelen; + int template_baselen_sub = template_baselen + namelen; + if (!subdir) + die("cannot opendir %s", template); + path[baselen_sub++] = + template[template_baselen_sub++] = '/'; + path[baselen_sub] = + template[template_baselen_sub] = 0; + copy_templates_1(path, baselen_sub, + template, template_baselen_sub, + subdir); + closedir(subdir); + } + else if (exists) + continue; + else if (S_ISLNK(st_template.st_mode)) { + char lnk[256]; + int len; + len = readlink(template, lnk, sizeof(lnk)); + if (len < 0) + die("cannot readlink %s", template); + if (sizeof(lnk) <= len) + die("insanely long symlink %s", template); + lnk[len] = 0; + if (symlink(lnk, path)) + die("cannot symlink %s %s", lnk, path); + } + else if (S_ISREG(st_template.st_mode)) { + if (copy_file(path, template, st_template.st_mode)) + die("cannot copy %s to %s", template, path); + } + else + error("ignoring template %s", template); } +} + +static void copy_templates(const char *git_dir, int len, char *template_dir) +{ + char path[PATH_MAX]; + char template_path[PATH_MAX]; + int template_len; + DIR *dir; + + if (!template_dir) + template_dir = DEFAULT_GIT_TEMPLATE_DIR; + strcpy(template_path, template_dir); + template_len = strlen(template_path); + if (template_path[template_len-1] != '/') { + template_path[template_len++] = '/'; + template_path[template_len] = 0; + } + dir = opendir(template_path); + if (!dir) { + fprintf(stderr, "warning: templates not found %s\n", + template_dir); + return; + } + + memcpy(path, git_dir, len); + path[len] = 0; + copy_templates_1(path, len, + template_path, template_len, + dir); + closedir(dir); +} + +static void create_default_files(const char *git_dir, + char *template_path) +{ + unsigned len = strlen(git_dir); + static char path[PATH_MAX]; + unsigned char sha1[20]; + + if (len > sizeof(path)-50) + die("insane git directory %s", git_dir); + memcpy(path, git_dir, len); + + if (len && path[len-1] != '/') + path[len++] = '/'; /* - * The default case is to have a DB per managed directory. + * Create .git/refs/{heads,tags} */ - sha1_dir = DEFAULT_DB_ENVIRONMENT; - fprintf(stderr, "defaulting to private storage area\n"); - len = strlen(sha1_dir); - if (mkdir(sha1_dir, 0700) < 0) { - if (errno != EEXIST) { - perror(sha1_dir); + strcpy(path + len, "refs"); + safe_create_dir(path); + strcpy(path + len, "refs/heads"); + safe_create_dir(path); + strcpy(path + len, "refs/tags"); + safe_create_dir(path); + + /* + * Create the default symlink from ".git/HEAD" to the "master" + * branch, if it does not exist yet. + */ + strcpy(path + len, "HEAD"); + if (read_ref(path, sha1) < 0) { + if (create_symref(path, "refs/heads/master") < 0) exit(1); - } } - path = malloc(len + 40); - memcpy(path, sha1_dir, len); - for (i = 0; i < 256; i++) { - sprintf(path+len, "/%02x", i); - if (mkdir(path, 0700) < 0) { - if (errno != EEXIST) { - perror(path); - exit(1); - } + path[len] = 0; + copy_templates(path, len, template_path); + + /* + * Find out if we can trust the executable bit. + */ + safe_create_dir(path); + strcpy(path + len, "config"); + if (access(path, R_OK) < 0) { + static const char contents[] = + "#\n" + "# This is the config file\n" + "#\n" + "\n" + "; core variables\n" + "[core]\n" + " ; Don't trust file modes\n" + " filemode = false\n" + "\n"; + FILE *config = fopen(path, "w"); + struct stat st; + + if (!config) + die("Can not write to %s?", path); + + fwrite(contents, sizeof(contents)-1, 1, config); + + fclose(config); + + if (!lstat(path, &st)) { + struct stat st2; + if (!chmod(path, st.st_mode ^ S_IXUSR) && + !lstat(path, &st2) && + st.st_mode != st2.st_mode) + unlink(path); + else + fprintf(stderr, "Ignoring file modes\n"); } } +} + +static const char init_db_usage[] = +"git-init-db [--template=]"; + +/* + * If you want to, you can share the DB area with any number of branches. + * That has advantages: you can save space by sharing all the SHA1 objects. + * On the other hand, it might just make lookup slower and messier. You + * be the judge. The default case is to have one DB per managed directory. + */ +int main(int argc, char **argv) +{ + const char *git_dir; + const char *sha1_dir; + char *path, *template_dir = NULL; + int len, i; + + for (i = 1; i < argc; i++, argv++) { + char *arg = argv[1]; + if (arg[0] != '-') + break; + else if (!strncmp(arg, "--template=", 11)) + template_dir = arg+11; + else + die(init_db_usage); + } + + /* + * Set up the default .git directory contents + */ + git_dir = getenv(GIT_DIR_ENVIRONMENT); + if (!git_dir) { + git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; + fprintf(stderr, "defaulting to local storage area\n"); + } + safe_create_dir(git_dir); + create_default_files(git_dir, template_dir); + + /* + * And set up the object store. + */ + sha1_dir = get_object_directory(); + len = strlen(sha1_dir); + path = xmalloc(len + 40); + memcpy(path, sha1_dir, len); + + safe_create_dir(sha1_dir); + strcpy(path+len, "/pack"); + safe_create_dir(path); + strcpy(path+len, "/info"); + safe_create_dir(path); return 0; }