1#include"cache.h" 2 3const char*prefix_path(const char*prefix,int len,const char*path) 4{ 5const char*orig = path; 6for(;;) { 7char c; 8if(*path !='.') 9break; 10 c = path[1]; 11/* "." */ 12if(!c) { 13 path++; 14break; 15} 16/* "./" */ 17if(c =='/') { 18 path +=2; 19continue; 20} 21if(c !='.') 22break; 23 c = path[2]; 24if(!c) 25 path +=2; 26else if(c =='/') 27 path +=3; 28else 29break; 30/* ".." and "../" */ 31/* Remove last component of the prefix */ 32do{ 33if(!len) 34die("'%s' is outside repository", orig); 35 len--; 36}while(len && prefix[len-1] !='/'); 37continue; 38} 39if(len) { 40int speclen =strlen(path); 41char*n =xmalloc(speclen + len +1); 42 43memcpy(n, prefix, len); 44memcpy(n + len, path, speclen+1); 45 path = n; 46} 47return 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{ 57static char path[PATH_MAX]; 58if(!pfx || !*pfx || arg[0] =='/') 59return arg; 60memcpy(path, pfx, pfx_len); 61strcpy(path + pfx_len, arg); 62return 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 */ 72voidverify_filename(const char*prefix,const char*arg) 73{ 74const char*name; 75struct stat st; 76 77if(*arg =='-') 78die("bad flag '%s' used after filename", arg); 79 name = prefix ?prefix_filename(prefix,strlen(prefix), arg) : arg; 80if(!lstat(name, &st)) 81return; 82if(errno == ENOENT) 83die("ambiguous argument '%s': unknown revision or path not in the working tree.\n" 84"Use '--' to separate paths from revisions", arg); 85die("'%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 */ 93voidverify_non_filename(const char*prefix,const char*arg) 94{ 95const char*name; 96struct stat st; 97 98if(*arg =='-') 99return;/* flag */ 100 name = prefix ?prefix_filename(prefix,strlen(prefix), arg) : arg; 101if(!lstat(name, &st)) 102die("ambiguous argument '%s': both revision and filename\n" 103"Use '--' to separate filenames from revisions", arg); 104if(errno != ENOENT) 105die("'%s':%s", arg,strerror(errno)); 106} 107 108const char**get_pathspec(const char*prefix,const char**pathspec) 109{ 110const char*entry = *pathspec; 111const char**p; 112int prefixlen; 113 114if(!prefix && !entry) 115return NULL; 116 117if(!entry) { 118static const char*spec[2]; 119 spec[0] = prefix; 120 spec[1] = NULL; 121return spec; 122} 123 124/* Otherwise we have to re-write the entries.. */ 125 p = pathspec; 126 prefixlen = prefix ?strlen(prefix) :0; 127do{ 128*p =prefix_path(prefix, prefixlen, entry); 129}while((entry = *++p) != NULL); 130return(const char**) pathspec; 131} 132 133/* 134 * Test if it looks like we're at a git directory. 135 * We want to see: 136 * 137 * - either a objects/ directory _or_ the proper 138 * GIT_OBJECT_DIRECTORY environment variable 139 * - a refs/ directory 140 * - either a HEAD symlink or a HEAD file that is formatted as 141 * a proper "ref:", or a regular file HEAD that has a properly 142 * formatted sha1 object name. 143 */ 144static intis_git_directory(const char*suspect) 145{ 146char path[PATH_MAX]; 147size_t len =strlen(suspect); 148 149strcpy(path, suspect); 150if(getenv(DB_ENVIRONMENT)) { 151if(access(getenv(DB_ENVIRONMENT), X_OK)) 152return0; 153} 154else{ 155strcpy(path + len,"/objects"); 156if(access(path, X_OK)) 157return0; 158} 159 160strcpy(path + len,"/refs"); 161if(access(path, X_OK)) 162return0; 163 164strcpy(path + len,"/HEAD"); 165if(validate_headref(path)) 166return0; 167 168return1; 169} 170 171const char*setup_git_directory_gently(int*nongit_ok) 172{ 173static char cwd[PATH_MAX+1]; 174const char*gitdirenv; 175int len, offset; 176 177/* 178 * If GIT_DIR is set explicitly, we're not going 179 * to do any discovery, but we still do repository 180 * validation. 181 */ 182 gitdirenv =getenv(GIT_DIR_ENVIRONMENT); 183if(gitdirenv) { 184if(PATH_MAX -40<strlen(gitdirenv)) 185die("'$%s' too big", GIT_DIR_ENVIRONMENT); 186if(is_git_directory(gitdirenv)) 187return NULL; 188if(nongit_ok) { 189*nongit_ok =1; 190return NULL; 191} 192die("Not a git repository: '%s'", gitdirenv); 193} 194 195if(!getcwd(cwd,sizeof(cwd)) || cwd[0] !='/') 196die("Unable to read current working directory"); 197 198 offset = len =strlen(cwd); 199for(;;) { 200if(is_git_directory(".git")) 201break; 202chdir(".."); 203do{ 204if(!offset) { 205if(is_git_directory(cwd)) { 206if(chdir(cwd)) 207die("Cannot come back to cwd"); 208setenv(GIT_DIR_ENVIRONMENT, cwd,1); 209return NULL; 210} 211if(nongit_ok) { 212if(chdir(cwd)) 213die("Cannot come back to cwd"); 214*nongit_ok =1; 215return NULL; 216} 217die("Not a git repository"); 218} 219}while(cwd[--offset] !='/'); 220} 221 222if(offset == len) 223return NULL; 224 225/* Make "offset" point to past the '/', and add a '/' at the end */ 226 offset++; 227 cwd[len++] ='/'; 228 cwd[len] =0; 229return cwd + offset; 230} 231 232intgit_config_perm(const char*var,const char*value) 233{ 234if(value) { 235if(!strcmp(value,"umask")) 236return PERM_UMASK; 237if(!strcmp(value,"group")) 238return PERM_GROUP; 239if(!strcmp(value,"all") || 240!strcmp(value,"world") || 241!strcmp(value,"everybody")) 242return PERM_EVERYBODY; 243} 244returngit_config_bool(var, value); 245} 246 247intcheck_repository_format_version(const char*var,const char*value) 248{ 249if(strcmp(var,"core.repositoryformatversion") ==0) 250 repository_format_version =git_config_int(var, value); 251else if(strcmp(var,"core.sharedrepository") ==0) 252 shared_repository =git_config_perm(var, value); 253return0; 254} 255 256intcheck_repository_format(void) 257{ 258git_config(check_repository_format_version); 259if(GIT_REPO_VERSION < repository_format_version) 260die("Expected git repo version <=%d, found%d", 261 GIT_REPO_VERSION, repository_format_version); 262return0; 263} 264 265const char*setup_git_directory(void) 266{ 267const char*retval =setup_git_directory_gently(NULL); 268check_repository_format(); 269return retval; 270}