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:". 142 */ 143static intis_git_directory(const char*suspect) 144{ 145char path[PATH_MAX]; 146size_t len =strlen(suspect); 147 148strcpy(path, suspect); 149if(getenv(DB_ENVIRONMENT)) { 150if(access(getenv(DB_ENVIRONMENT), X_OK)) 151return0; 152} 153else{ 154strcpy(path + len,"/objects"); 155if(access(path, X_OK)) 156return0; 157} 158 159strcpy(path + len,"/refs"); 160if(access(path, X_OK)) 161return0; 162 163strcpy(path + len,"/HEAD"); 164if(validate_symref(path)) 165return0; 166 167return1; 168} 169 170const char*setup_git_directory_gently(int*nongit_ok) 171{ 172static char cwd[PATH_MAX+1]; 173const char*gitdirenv; 174int len, offset; 175 176/* 177 * If GIT_DIR is set explicitly, we're not going 178 * to do any discovery, but we still do repository 179 * validation. 180 */ 181 gitdirenv =getenv(GIT_DIR_ENVIRONMENT); 182if(gitdirenv) { 183if(PATH_MAX -40<strlen(gitdirenv)) 184die("'$%s' too big", GIT_DIR_ENVIRONMENT); 185if(is_git_directory(gitdirenv)) 186return NULL; 187if(nongit_ok) { 188*nongit_ok =1; 189return NULL; 190} 191die("Not a git repository: '%s'", gitdirenv); 192} 193 194if(!getcwd(cwd,sizeof(cwd)) || cwd[0] !='/') 195die("Unable to read current working directory"); 196 197 offset = len =strlen(cwd); 198for(;;) { 199if(is_git_directory(".git")) 200break; 201chdir(".."); 202do{ 203if(!offset) { 204if(is_git_directory(cwd)) { 205if(chdir(cwd)) 206die("Cannot come back to cwd"); 207setenv(GIT_DIR_ENVIRONMENT, cwd,1); 208return NULL; 209} 210if(nongit_ok) { 211if(chdir(cwd)) 212die("Cannot come back to cwd"); 213*nongit_ok =1; 214return NULL; 215} 216die("Not a git repository"); 217} 218}while(cwd[--offset] !='/'); 219} 220 221if(offset == len) 222return NULL; 223 224/* Make "offset" point to past the '/', and add a '/' at the end */ 225 offset++; 226 cwd[len++] ='/'; 227 cwd[len] =0; 228return cwd + offset; 229} 230 231intgit_config_perm(const char*var,const char*value) 232{ 233if(value) { 234if(!strcmp(value,"umask")) 235return PERM_UMASK; 236if(!strcmp(value,"group")) 237return PERM_GROUP; 238if(!strcmp(value,"all") || 239!strcmp(value,"world") || 240!strcmp(value,"everybody")) 241return PERM_EVERYBODY; 242} 243returngit_config_bool(var, value); 244} 245 246intcheck_repository_format_version(const char*var,const char*value) 247{ 248if(strcmp(var,"core.repositoryformatversion") ==0) 249 repository_format_version =git_config_int(var, value); 250else if(strcmp(var,"core.sharedrepository") ==0) 251 shared_repository =git_config_perm(var, value); 252return0; 253} 254 255intcheck_repository_format(void) 256{ 257git_config(check_repository_format_version); 258if(GIT_REPO_VERSION < repository_format_version) 259die("Expected git repo version <=%d, found%d", 260 GIT_REPO_VERSION, repository_format_version); 261return0; 262} 263 264const char*setup_git_directory(void) 265{ 266const char*retval =setup_git_directory_gently(NULL); 267check_repository_format(); 268return retval; 269}