1#include "cache.h" 2 3const char *prefix_path(const char *prefix, int len, const char *path) 4{ 5 const char *orig = path; 6 for (;;) { 7 char c; 8 if (*path != '.') 9 break; 10 c = path[1]; 11 /* "." */ 12 if (!c) { 13 path++; 14 break; 15 } 16 /* "./" */ 17 if (c == '/') { 18 path += 2; 19 continue; 20 } 21 if (c != '.') 22 break; 23 c = path[2]; 24 if (!c) 25 path += 2; 26 else if (c == '/') 27 path += 3; 28 else 29 break; 30 /* ".." and "../" */ 31 /* Remove last component of the prefix */ 32 do { 33 if (!len) 34 die("'%s' is outside repository", orig); 35 len--; 36 } while (len && prefix[len-1] != '/'); 37 continue; 38 } 39 if (len) { 40 int speclen = strlen(path); 41 char *n = xmalloc(speclen + len + 1); 42 43 memcpy(n, prefix, len); 44 memcpy(n + len, path, speclen+1); 45 path = n; 46 } 47 return path; 48} 49 50/* 51 * Unlike prefix_path, this should be used if the named file does 52 * not have to interact with index entry; i.e. name of a random file 53 * on the filesystem. 54 */ 55const char *prefix_filename(const char *pfx, int pfx_len, const char *arg) 56{ 57 static char path[PATH_MAX]; 58 if (!pfx || !*pfx || arg[0] == '/') 59 return arg; 60 memcpy(path, pfx, pfx_len); 61 strcpy(path + pfx_len, arg); 62 return path; 63} 64 65/* 66 * Verify a filename that we got as an argument for a pathspec 67 * entry. Note that a filename that begins with "-" never verifies 68 * as true, because even if such a filename were to exist, we want 69 * it to be preceded by the "--" marker (or we want the user to 70 * use a format like "./-filename") 71 */ 72void verify_filename(const char *prefix, const char *arg) 73{ 74 const char *name; 75 struct stat st; 76 77 if (*arg == '-') 78 die("bad flag '%s' used after filename", arg); 79 name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg; 80 if (!lstat(name, &st)) 81 return; 82 if (errno == ENOENT) 83 die("ambiguous argument '%s': unknown revision or path not in the working tree.\n" 84 "Use '--' to separate paths from revisions", arg); 85 die("'%s': %s", arg, strerror(errno)); 86} 87 88/* 89 * Opposite of the above: the command line did not have -- marker 90 * and we parsed the arg as a refname. It should not be interpretable 91 * as a filename. 92 */ 93void verify_non_filename(const char *prefix, const char *arg) 94{ 95 const char *name; 96 struct stat st; 97 98 if (*arg == '-') 99 return; /* flag */ 100 name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg; 101 if (!lstat(name, &st)) 102 die("ambiguous argument '%s': both revision and filename\n" 103 "Use '--' to separate filenames from revisions", arg); 104 if (errno != ENOENT) 105 die("'%s': %s", arg, strerror(errno)); 106} 107 108const char **get_pathspec(const char *prefix, const char **pathspec) 109{ 110 const char *entry = *pathspec; 111 const char **p; 112 int prefixlen; 113 114 if (!prefix && !entry) 115 return NULL; 116 117 if (!entry) { 118 static const char *spec[2]; 119 spec[0] = prefix; 120 spec[1] = NULL; 121 return spec; 122 } 123 124 /* Otherwise we have to re-write the entries.. */ 125 p = pathspec; 126 prefixlen = prefix ? strlen(prefix) : 0; 127 do { 128 *p = prefix_path(prefix, prefixlen, entry); 129 } while ((entry = *++p) != NULL); 130 return (const char **) pathspec; 131} 132 133/* 134 * Test if it looks like we're at the top level git directory. 135 * We want to see: 136 * 137 * - either a .git/objects/ directory _or_ the proper 138 * GIT_OBJECT_DIRECTORY environment variable 139 * - a refs/ directory under ".git" 140 * - either a HEAD symlink or a HEAD file that is formatted as 141 * a proper "ref:". 142 */ 143static int is_toplevel_directory(void) 144{ 145 if (access(".git/refs/", X_OK) || 146 access(getenv(DB_ENVIRONMENT) ? 147 getenv(DB_ENVIRONMENT) : ".git/objects/", X_OK) || 148 validate_symref(".git/HEAD")) 149 return 0; 150 return 1; 151} 152 153const char *setup_git_directory_gently(int *nongit_ok) 154{ 155 static char cwd[PATH_MAX+1]; 156 int len, offset; 157 158 /* 159 * If GIT_DIR is set explicitly, we're not going 160 * to do any discovery, but we still do repository 161 * validation. 162 */ 163 if (getenv(GIT_DIR_ENVIRONMENT)) { 164 char path[PATH_MAX]; 165 int len = strlen(getenv(GIT_DIR_ENVIRONMENT)); 166 if (sizeof(path) - 40 < len) 167 die("'$%s' too big", GIT_DIR_ENVIRONMENT); 168 memcpy(path, getenv(GIT_DIR_ENVIRONMENT), len); 169 170 strcpy(path + len, "/refs"); 171 if (access(path, X_OK)) 172 goto bad_dir_environ; 173 strcpy(path + len, "/HEAD"); 174 if (validate_symref(path)) 175 goto bad_dir_environ; 176 if (getenv(DB_ENVIRONMENT)) { 177 if (access(getenv(DB_ENVIRONMENT), X_OK)) 178 goto bad_dir_environ; 179 } 180 else { 181 strcpy(path + len, "/objects"); 182 if (access(path, X_OK)) 183 goto bad_dir_environ; 184 } 185 return NULL; 186 bad_dir_environ: 187 path[len] = 0; 188 die("Not a git repository: '%s'", path); 189 } 190 191 if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/') 192 die("Unable to read current working directory"); 193 194 offset = len = strlen(cwd); 195 for (;;) { 196 if (is_toplevel_directory()) 197 break; 198 chdir(".."); 199 do { 200 if (!offset) { 201 if (nongit_ok) { 202 if (chdir(cwd)) 203 die("Cannot come back to cwd"); 204 *nongit_ok = 1; 205 return NULL; 206 } 207 die("Not a git repository"); 208 } 209 } while (cwd[--offset] != '/'); 210 } 211 212 if (offset == len) 213 return NULL; 214 215 /* Make "offset" point to past the '/', and add a '/' at the end */ 216 offset++; 217 cwd[len++] = '/'; 218 cwd[len] = 0; 219 return cwd + offset; 220} 221 222int check_repository_format_version(const char *var, const char *value) 223{ 224 if (strcmp(var, "core.repositoryformatversion") == 0) 225 repository_format_version = git_config_int(var, value); 226 else if (strcmp(var, "core.sharedrepository") == 0) 227 shared_repository = git_config_bool(var, value); 228 return 0; 229} 230 231int check_repository_format(void) 232{ 233 git_config(check_repository_format_version); 234 if (GIT_REPO_VERSION < repository_format_version) 235 die ("Expected git repo version <= %d, found %d", 236 GIT_REPO_VERSION, repository_format_version); 237 return 0; 238} 239 240const char *setup_git_directory(void) 241{ 242 const char *retval = setup_git_directory_gently(NULL); 243 check_repository_format(); 244 return retval; 245}