diff options
Diffstat (limited to 'sha1_file.c')
| -rw-r--r-- | sha1_file.c | 481 | 
1 files changed, 309 insertions, 172 deletions
diff --git a/sha1_file.c b/sha1_file.c index 99155c0d6b..d5e11217f5 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -36,6 +36,7 @@  static inline uintmax_t sz_fmt(size_t s) { return s; }  const unsigned char null_sha1[20]; +const struct object_id null_oid;  /*   * This is meant to hold a *small* number of objects that you would @@ -208,44 +209,25 @@ const char *sha1_file_name(const unsigned char *sha1)   * provided by the caller.  which should be "pack" or "idx".   */  static char *sha1_get_pack_name(const unsigned char *sha1, -				char **name, char **base, const char *which) +				struct strbuf *buf, +				const char *which)  { -	static const char hex[] = "0123456789abcdef"; -	char *buf; -	int i; - -	if (!*base) { -		const char *sha1_file_directory = get_object_directory(); -		int len = strlen(sha1_file_directory); -		*base = xmalloc(len + 60); -		sprintf(*base, "%s/pack/pack-1234567890123456789012345678901234567890.%s", -			sha1_file_directory, which); -		*name = *base + len + 11; -	} - -	buf = *name; - -	for (i = 0; i < 20; i++) { -		unsigned int val = *sha1++; -		*buf++ = hex[val >> 4]; -		*buf++ = hex[val & 0xf]; -	} - -	return *base; +	strbuf_reset(buf); +	strbuf_addf(buf, "%s/pack/pack-%s.%s", get_object_directory(), +		    sha1_to_hex(sha1), which); +	return buf->buf;  }  char *sha1_pack_name(const unsigned char *sha1)  { -	static char *name, *base; - -	return sha1_get_pack_name(sha1, &name, &base, "pack"); +	static struct strbuf buf = STRBUF_INIT; +	return sha1_get_pack_name(sha1, &buf, "pack");  }  char *sha1_pack_index_name(const unsigned char *sha1)  { -	static char *name, *base; - -	return sha1_get_pack_name(sha1, &name, &base, "idx"); +	static struct strbuf buf = STRBUF_INIT; +	return sha1_get_pack_name(sha1, &buf, "idx");  }  struct alternate_object_database *alt_odb_list; @@ -271,7 +253,7 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,  {  	struct alternate_object_database *ent;  	struct alternate_object_database *alt; -	int pfxlen, entlen; +	size_t pfxlen, entlen;  	struct strbuf pathbuf = STRBUF_INIT;  	if (!is_absolute_path(entry) && relative_base) { @@ -291,8 +273,8 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,  	while (pfxlen && pathbuf.buf[pfxlen-1] == '/')  		pfxlen -= 1; -	entlen = pfxlen + 43; /* '/' + 2 hex + '/' + 38 hex + NUL */ -	ent = xmalloc(sizeof(*ent) + entlen); +	entlen = st_add(pfxlen, 43); /* '/' + 2 hex + '/' + 38 hex + NUL */ +	ent = xmalloc(st_add(sizeof(*ent), entlen));  	memcpy(ent->base, pathbuf.buf, pfxlen);  	strbuf_release(&pathbuf); @@ -319,7 +301,7 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,  			return -1;  		}  	} -	if (!strcmp_icase(ent->base, normalized_objdir)) { +	if (!fspathcmp(ent->base, normalized_objdir)) {  		free(ent);  		return -1;  	} @@ -401,13 +383,46 @@ void read_info_alternates(const char * relative_base, int depth)  void add_to_alternates_file(const char *reference)  {  	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); -	int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), LOCK_DIE_ON_ERROR); -	char *alt = mkpath("%s\n", reference); -	write_or_die(fd, alt, strlen(alt)); -	if (commit_lock_file(lock)) -		die("could not close alternates file"); -	if (alt_odb_tail) -		link_alt_odb_entries(alt, strlen(alt), '\n', NULL, 0); +	char *alts = git_pathdup("objects/info/alternates"); +	FILE *in, *out; + +	hold_lock_file_for_update(lock, alts, LOCK_DIE_ON_ERROR); +	out = fdopen_lock_file(lock, "w"); +	if (!out) +		die_errno("unable to fdopen alternates lockfile"); + +	in = fopen(alts, "r"); +	if (in) { +		struct strbuf line = STRBUF_INIT; +		int found = 0; + +		while (strbuf_getline(&line, in) != EOF) { +			if (!strcmp(reference, line.buf)) { +				found = 1; +				break; +			} +			fprintf_or_die(out, "%s\n", line.buf); +		} + +		strbuf_release(&line); +		fclose(in); + +		if (found) { +			rollback_lock_file(lock); +			lock = NULL; +		} +	} +	else if (errno != ENOENT) +		die_errno("unable to read alternates file"); + +	if (lock) { +		fprintf_or_die(out, "%s\n", reference); +		if (commit_lock_file(lock)) +			die_errno("unable to move new alternates file into place"); +		if (alt_odb_tail) +			link_alt_odb_entries(reference, strlen(reference), '\n', NULL, 0); +	} +	free(alts);  }  int foreach_alt_odb(alt_odb_fn fn, void *cb) @@ -638,13 +653,15 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)  int open_pack_index(struct packed_git *p)  {  	char *idx_name; +	size_t len;  	int ret;  	if (p->index_data)  		return 0; -	idx_name = xstrdup(p->pack_name); -	strcpy(idx_name + strlen(idx_name) - strlen(".pack"), ".idx"); +	if (!strip_suffix(p->pack_name, ".pack", &len)) +		die("BUG: pack_name does not end in .pack"); +	idx_name = xstrfmt("%.*s.idx", (int)len, p->pack_name);  	ret = check_packed_git_idx(idx_name, p);  	free(idx_name);  	return ret; @@ -753,6 +770,37 @@ void close_pack_windows(struct packed_git *p)  	}  } +static int close_pack_fd(struct packed_git *p) +{ +	if (p->pack_fd < 0) +		return 0; + +	close(p->pack_fd); +	pack_open_fds--; +	p->pack_fd = -1; + +	return 1; +} + +static void close_pack(struct packed_git *p) +{ +	close_pack_windows(p); +	close_pack_fd(p); +	close_pack_index(p); +} + +void close_all_packs(void) +{ +	struct packed_git *p; + +	for (p = packed_git; p; p = p->next) +		if (p->do_not_close) +			die("BUG! Want to close pack marked 'do-not-close'"); +		else +			close_pack(p); +} + +  /*   * The LRU pack is the one with the oldest MRU window, preferring packs   * with no used windows, or the oldest mtime if it has no windows allocated. @@ -820,12 +868,8 @@ static int close_one_pack(void)  		find_lru_pack(p, &lru_p, &mru_w, &accept_windows_inuse);  	} -	if (lru_p) { -		close(lru_p->pack_fd); -		pack_open_fds--; -		lru_p->pack_fd = -1; -		return 1; -	} +	if (lru_p) +		return close_pack_fd(lru_p);  	return 0;  } @@ -865,12 +909,7 @@ void free_pack_by_name(const char *pack_name)  		p = *pp;  		if (strcmp(pack_name, p->pack_name) == 0) {  			clear_delta_base_cache(); -			close_pack_windows(p); -			if (p->pack_fd != -1) { -				close(p->pack_fd); -				pack_open_fds--; -			} -			close_pack_index(p); +			close_pack(p);  			free(p->bad_object_sha1);  			*pp = p->next;  			if (last_found_pack == p) @@ -1004,11 +1043,7 @@ static int open_packed_git(struct packed_git *p)  {  	if (!open_packed_git_1(p))  		return 0; -	if (p->pack_fd != -1) { -		close(p->pack_fd); -		pack_open_fds--; -		p->pack_fd = -1; -	} +	close_pack_fd(p);  	return -1;  } @@ -1041,6 +1076,8 @@ unsigned char *use_pack(struct packed_git *p,  		die("packfile %s cannot be accessed", p->pack_name);  	if (offset > (p->pack_size - 20))  		die("offset beyond end of packfile (truncated pack?)"); +	if (offset < 0) +		die(_("offset before end of packfile (broken .idx?)"));  	if (!win || !in_window(win, offset)) {  		if (win) @@ -1070,15 +1107,11 @@ unsigned char *use_pack(struct packed_git *p,  				PROT_READ, MAP_PRIVATE,  				p->pack_fd, win->offset);  			if (win->base == MAP_FAILED) -				die("packfile %s cannot be mapped: %s", -					p->pack_name, -					strerror(errno)); +				die_errno("packfile %s cannot be mapped", +					  p->pack_name);  			if (!win->offset && win->len == p->pack_size -				&& !p->do_not_close) { -				close(p->pack_fd); -				pack_open_fds--; -				p->pack_fd = -1; -			} +				&& !p->do_not_close) +				close_pack_fd(p);  			pack_mmap_calls++;  			pack_open_windows++;  			if (pack_mapped > peak_pack_mapped) @@ -1102,7 +1135,7 @@ unsigned char *use_pack(struct packed_git *p,  static struct packed_git *alloc_packed_git(int extra)  { -	struct packed_git *p = xmalloc(sizeof(*p) + extra); +	struct packed_git *p = xmalloc(st_add(sizeof(*p), extra));  	memset(p, 0, sizeof(*p));  	p->pack_fd = -1;  	return p; @@ -1113,11 +1146,12 @@ static void try_to_free_pack_memory(size_t size)  	release_pack_memory(size);  } -struct packed_git *add_packed_git(const char *path, int path_len, int local) +struct packed_git *add_packed_git(const char *path, size_t path_len, int local)  {  	static int have_set_try_to_free_routine;  	struct stat st; -	struct packed_git *p = alloc_packed_git(path_len + 2); +	size_t alloc; +	struct packed_git *p;  	if (!have_set_try_to_free_routine) {  		have_set_try_to_free_routine = 1; @@ -1128,18 +1162,22 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local)  	 * Make sure a corresponding .pack file exists and that  	 * the index looks sane.  	 */ -	path_len -= strlen(".idx"); -	if (path_len < 1) { -		free(p); +	if (!strip_suffix_mem(path, &path_len, ".idx"))  		return NULL; -	} + +	/* +	 * ".pack" is long enough to hold any suffix we're adding (and +	 * the use xsnprintf double-checks that) +	 */ +	alloc = st_add3(path_len, strlen(".pack"), 1); +	p = alloc_packed_git(alloc);  	memcpy(p->pack_name, path, path_len); -	strcpy(p->pack_name + path_len, ".keep"); +	xsnprintf(p->pack_name + path_len, alloc - path_len, ".keep");  	if (!access(p->pack_name, F_OK))  		p->pack_keep = 1; -	strcpy(p->pack_name + path_len, ".pack"); +	xsnprintf(p->pack_name + path_len, alloc - path_len, ".pack");  	if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {  		free(p);  		return NULL; @@ -1159,9 +1197,10 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local)  struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)  {  	const char *path = sha1_pack_name(sha1); -	struct packed_git *p = alloc_packed_git(strlen(path) + 1); +	size_t alloc = st_add(strlen(path), 1); +	struct packed_git *p = alloc_packed_git(alloc); -	strcpy(p->pack_name, path); +	memcpy(p->pack_name, path, alloc); /* includes NUL */  	hashcpy(p->sha1, sha1);  	if (check_packed_git_idx(idx_path, p)) {  		free(p); @@ -1180,27 +1219,16 @@ void install_packed_git(struct packed_git *pack)  	packed_git = pack;  } -void (*report_garbage)(const char *desc, const char *path); +void (*report_garbage)(unsigned seen_bits, const char *path);  static void report_helper(const struct string_list *list,  			  int seen_bits, int first, int last)  { -	const char *msg; -	switch (seen_bits) { -	case 0: -		msg = "no corresponding .idx or .pack"; -		break; -	case 1: -		msg = "no corresponding .idx"; -		break; -	case 2: -		msg = "no corresponding .pack"; -		break; -	default: +	if (seen_bits == (PACKDIR_FILE_PACK|PACKDIR_FILE_IDX))  		return; -	} +  	for (; first < last; first++) -		report_garbage(msg, list->items[first].string); +		report_garbage(seen_bits, list->items[first].string);  }  static void report_pack_garbage(struct string_list *list) @@ -1223,7 +1251,7 @@ static void report_pack_garbage(struct string_list *list)  		if (baselen == -1) {  			const char *dot = strrchr(path, '.');  			if (!dot) { -				report_garbage("garbage found", path); +				report_garbage(PACKDIR_FILE_GARBAGE, path);  				continue;  			}  			baselen = dot - path + 1; @@ -1250,8 +1278,8 @@ static void prepare_packed_git_one(char *objdir, int local)  	dir = opendir(path.buf);  	if (!dir) {  		if (errno != ENOENT) -			error("unable to open object pack directory: %s: %s", -			      path.buf, strerror(errno)); +			error_errno("unable to open object pack directory: %s", +				    path.buf);  		strbuf_release(&path);  		return;  	} @@ -1295,7 +1323,7 @@ static void prepare_packed_git_one(char *objdir, int local)  		    ends_with(de->d_name, ".keep"))  			string_list_append(&garbage, path.buf);  		else -			report_garbage("garbage found", path.buf); +			report_garbage(PACKDIR_FILE_GARBAGE, path.buf);  	}  	closedir(dir);  	report_pack_garbage(&garbage); @@ -1386,10 +1414,12 @@ static void mark_bad_packed_object(struct packed_git *p,  {  	unsigned i;  	for (i = 0; i < p->num_bad_objects; i++) -		if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i)) +		if (!hashcmp(sha1, p->bad_object_sha1 + GIT_SHA1_RAWSZ * i))  			return; -	p->bad_object_sha1 = xrealloc(p->bad_object_sha1, 20 * (p->num_bad_objects + 1)); -	hashcpy(p->bad_object_sha1 + 20 * p->num_bad_objects, sha1); +	p->bad_object_sha1 = xrealloc(p->bad_object_sha1, +				      st_mult(GIT_SHA1_RAWSZ, +					      st_add(p->num_bad_objects, 1))); +	hashcpy(p->bad_object_sha1 + GIT_SHA1_RAWSZ * p->num_bad_objects, sha1);  	p->num_bad_objects++;  } @@ -1431,7 +1461,7 @@ int check_sha1_signature(const unsigned char *sha1, void *map,  		return -1;  	/* Generate the header */ -	hdrlen = sprintf(hdr, "%s %lu", typename(obj_type), size) + 1; +	hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(obj_type), size) + 1;  	/* Sha1.. */  	git_SHA1_Init(&c); @@ -1458,7 +1488,10 @@ int git_open_noatime(const char *name)  	static int sha1_file_open_flag = O_NOATIME;  	for (;;) { -		int fd = open(name, O_RDONLY | sha1_file_open_flag); +		int fd; + +		errno = 0; +		fd = open(name, O_RDONLY | sha1_file_open_flag);  		if (fd >= 0)  			return fd; @@ -1576,6 +1609,40 @@ int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long ma  	return git_inflate(stream, 0);  } +static int unpack_sha1_header_to_strbuf(git_zstream *stream, unsigned char *map, +					unsigned long mapsize, void *buffer, +					unsigned long bufsiz, struct strbuf *header) +{ +	int status; + +	status = unpack_sha1_header(stream, map, mapsize, buffer, bufsiz); + +	/* +	 * Check if entire header is unpacked in the first iteration. +	 */ +	if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer)) +		return 0; + +	/* +	 * buffer[0..bufsiz] was not large enough.  Copy the partial +	 * result out to header, and then append the result of further +	 * reading the stream. +	 */ +	strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer); +	stream->next_out = buffer; +	stream->avail_out = bufsiz; + +	do { +		status = git_inflate(stream, 0); +		strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer); +		if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer)) +			return 0; +		stream->next_out = buffer; +		stream->avail_out = bufsiz; +	} while (status != Z_STREAM_END); +	return -1; +} +  static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long size, const unsigned char *sha1)  {  	int bytes = strlen(buffer) + 1; @@ -1626,27 +1693,38 @@ static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long s   * too permissive for what we want to check. So do an anal   * object header parse by hand.   */ -int parse_sha1_header(const char *hdr, unsigned long *sizep) +static int parse_sha1_header_extended(const char *hdr, struct object_info *oi, +			       unsigned int flags)  { -	char type[10]; -	int i; +	const char *type_buf = hdr;  	unsigned long size; +	int type, type_len = 0;  	/* -	 * The type can be at most ten bytes (including the -	 * terminating '\0' that we add), and is followed by +	 * The type can be of any size but is followed by  	 * a space.  	 */ -	i = 0;  	for (;;) {  		char c = *hdr++;  		if (c == ' ')  			break; -		type[i++] = c; -		if (i >= sizeof(type)) -			return -1; +		type_len++;  	} -	type[i] = 0; + +	type = type_from_string_gently(type_buf, type_len, 1); +	if (oi->typename) +		strbuf_add(oi->typename, type_buf, type_len); +	/* +	 * Set type to 0 if its an unknown object and +	 * we're obtaining the type using '--allow-unkown-type' +	 * option. +	 */ +	if ((flags & LOOKUP_UNKNOWN_OBJECT) && (type < 0)) +		type = 0; +	else if (type < 0) +		die("invalid object type"); +	if (oi->typep) +		*oi->typep = type;  	/*  	 * The length must follow immediately, and be in canonical @@ -1664,12 +1742,24 @@ int parse_sha1_header(const char *hdr, unsigned long *sizep)  			size = size * 10 + c;  		}  	} -	*sizep = size; + +	if (oi->sizep) +		*oi->sizep = size;  	/*  	 * The length must be followed by a zero byte  	 */ -	return *hdr ? -1 : type_from_string(type); +	return *hdr ? -1 : type; +} + +int parse_sha1_header(const char *hdr, unsigned long *sizep) +{ +	struct object_info oi; + +	oi.sizep = sizep; +	oi.typename = NULL; +	oi.typep = NULL; +	return parse_sha1_header_extended(hdr, &oi, LOOKUP_REPLACE_OBJECT);  }  static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size, const unsigned char *sha1) @@ -1855,7 +1945,7 @@ static enum object_type packed_to_object_type(struct packed_git *p,  		/* Push the object we're going to leave behind */  		if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {  			poi_stack_alloc = alloc_nr(poi_stack_nr); -			poi_stack = xmalloc(sizeof(off_t)*poi_stack_alloc); +			ALLOC_ARRAY(poi_stack, poi_stack_alloc);  			memcpy(poi_stack, small_poi_stack, sizeof(off_t)*poi_stack_nr);  		} else {  			ALLOC_GROW(poi_stack, poi_stack_nr+1, poi_stack_alloc); @@ -2029,7 +2119,7 @@ static unsigned long pack_entry_hash(struct packed_git *p, off_t base_offset)  {  	unsigned long hash; -	hash = (unsigned long)p + (unsigned long)base_offset; +	hash = (unsigned long)(intptr_t)p + (unsigned long)base_offset;  	hash += (hash >> 8) + (hash >> 16);  	return hash % MAX_DELTA_CACHE;  } @@ -2221,7 +2311,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,  		if (delta_stack_nr >= delta_stack_alloc  		    && delta_stack == small_delta_stack) {  			delta_stack_alloc = alloc_nr(delta_stack_nr); -			delta_stack = xmalloc(sizeof(*delta_stack)*delta_stack_alloc); +			ALLOC_ARRAY(delta_stack, delta_stack_alloc);  			memcpy(delta_stack, small_delta_stack,  			       sizeof(*delta_stack)*delta_stack_nr);  		} else { @@ -2359,6 +2449,20 @@ const unsigned char *nth_packed_object_sha1(struct packed_git *p,  	}  } +void check_pack_index_ptr(const struct packed_git *p, const void *vptr) +{ +	const unsigned char *ptr = vptr; +	const unsigned char *start = p->index_data; +	const unsigned char *end = start + p->index_size; +	if (ptr < start) +		die(_("offset before start of pack index for %s (corrupt index?)"), +		    p->pack_name); +	/* No need to check for underflow; .idx files must be at least 8 bytes */ +	if (ptr >= end - 8) +		die(_("offset beyond end of pack index for %s (truncated index?)"), +		    p->pack_name); +} +  off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)  {  	const unsigned char *index = p->index_data; @@ -2372,6 +2476,7 @@ off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)  		if (!(off & 0x80000000))  			return off;  		index += p->num_objects * 4 + (off & 0x7fffffff) * 8; +		check_pack_index_ptr(p, index);  		return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |  				   ntohl(*((uint32_t *)(index + 4)));  	} @@ -2534,13 +2639,15 @@ struct packed_git *find_sha1_pack(const unsigned char *sha1,  }  static int sha1_loose_object_info(const unsigned char *sha1, -				  struct object_info *oi) +				  struct object_info *oi, +				  int flags)  { -	int status; -	unsigned long mapsize, size; +	int status = 0; +	unsigned long mapsize;  	void *map;  	git_zstream stream;  	char hdr[32]; +	struct strbuf hdrbuf = STRBUF_INIT;  	if (oi->delta_base_sha1)  		hashclr(oi->delta_base_sha1); @@ -2553,7 +2660,7 @@ static int sha1_loose_object_info(const unsigned char *sha1,  	 * return value implicitly indicates whether the  	 * object even exists.  	 */ -	if (!oi->typep && !oi->sizep) { +	if (!oi->typep && !oi->typename && !oi->sizep) {  		struct stat st;  		if (stat_sha1_file(sha1, &st) < 0)  			return -1; @@ -2567,17 +2674,26 @@ static int sha1_loose_object_info(const unsigned char *sha1,  		return -1;  	if (oi->disk_sizep)  		*oi->disk_sizep = mapsize; -	if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) +	if ((flags & LOOKUP_UNKNOWN_OBJECT)) { +		if (unpack_sha1_header_to_strbuf(&stream, map, mapsize, hdr, sizeof(hdr), &hdrbuf) < 0) +			status = error("unable to unpack %s header with --allow-unknown-type", +				       sha1_to_hex(sha1)); +	} else if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)  		status = error("unable to unpack %s header",  			       sha1_to_hex(sha1)); -	else if ((status = parse_sha1_header(hdr, &size)) < 0) +	if (status < 0) +		; /* Do nothing */ +	else if (hdrbuf.len) { +		if ((status = parse_sha1_header_extended(hdrbuf.buf, oi, flags)) < 0) +			status = error("unable to parse %s header with --allow-unknown-type", +				       sha1_to_hex(sha1)); +	} else if ((status = parse_sha1_header_extended(hdr, oi, flags)) < 0)  		status = error("unable to parse %s header", sha1_to_hex(sha1)); -	else if (oi->sizep) -		*oi->sizep = size;  	git_inflate_end(&stream);  	munmap(map, mapsize); -	if (oi->typep) +	if (status && oi->typep)  		*oi->typep = status; +	strbuf_release(&hdrbuf);  	return 0;  } @@ -2586,6 +2702,7 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,  	struct cached_object *co;  	struct pack_entry e;  	int rtype; +	enum object_type real_type;  	const unsigned char *real = lookup_replace_object_extended(sha1, flags);  	co = find_cached_object(real); @@ -2598,13 +2715,15 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,  			*(oi->disk_sizep) = 0;  		if (oi->delta_base_sha1)  			hashclr(oi->delta_base_sha1); +		if (oi->typename) +			strbuf_addstr(oi->typename, typename(co->type));  		oi->whence = OI_CACHED;  		return 0;  	}  	if (!find_pack_entry(real, &e)) {  		/* Most likely it's a loose object. */ -		if (!sha1_loose_object_info(real, oi)) { +		if (!sha1_loose_object_info(real, oi, flags)) {  			oi->whence = OI_LOOSE;  			return 0;  		} @@ -2615,9 +2734,18 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,  			return -1;  	} +	/* +	 * packed_object_info() does not follow the delta chain to +	 * find out the real type, unless it is given oi->typep. +	 */ +	if (oi->typename && !oi->typep) +		oi->typep = &real_type; +  	rtype = packed_object_info(e.p, e.offset, oi);  	if (rtype < 0) {  		mark_bad_packed_object(e.p, real); +		if (oi->typep == &real_type) +			oi->typep = NULL;  		return sha1_object_info_extended(real, oi, 0);  	} else if (in_delta_base_cache(e.p, e.offset)) {  		oi->whence = OI_DBCACHED; @@ -2628,6 +2756,10 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,  		oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||  					 rtype == OBJ_OFS_DELTA);  	} +	if (oi->typename) +		strbuf_addstr(oi->typename, typename(*oi->typep)); +	if (oi->typep == &real_type) +		oi->typep = NULL;  	return 0;  } @@ -2810,7 +2942,7 @@ static void write_sha1_file_prepare(const void *buf, unsigned long len,  	git_SHA_CTX c;  	/* Generate the header */ -	*hdrlen = sprintf(hdr, "%s %lu", type, len)+1; +	*hdrlen = xsnprintf(hdr, *hdrlen, "%s %lu", type, len)+1;  	/* Sha1.. */  	git_SHA1_Init(&c); @@ -2821,11 +2953,8 @@ static void write_sha1_file_prepare(const void *buf, unsigned long len,  /*   * Move the just written object into its final resting place. - * NEEDSWORK: this should be renamed to finalize_temp_file() as - * "moving" is only a part of what it does, when no patch between - * master to pu changes the call sites of this function.   */ -int move_temp_to_file(const char *tmpfile, const char *filename) +int finalize_object_file(const char *tmpfile, const char *filename)  {  	int ret = 0; @@ -2854,7 +2983,7 @@ int move_temp_to_file(const char *tmpfile, const char *filename)  	unlink_or_warn(tmpfile);  	if (ret) {  		if (ret != EEXIST) { -			return error("unable to write sha1 filename %s: %s", filename, strerror(ret)); +			return error_errno("unable to write sha1 filename %s", filename);  		}  		/* FIXME!!! Collision check here ? */  	} @@ -2868,7 +2997,7 @@ out:  static int write_buffer(int fd, const void *buf, size_t len)  {  	if (write_in_full(fd, buf, len) < 0) -		return error("file write error (%s)", strerror(errno)); +		return error_errno("file write error");  	return 0;  } @@ -2876,7 +3005,7 @@ int hash_sha1_file(const void *buf, unsigned long len, const char *type,                     unsigned char *sha1)  {  	char hdr[32]; -	int hdrlen; +	int hdrlen = sizeof(hdr);  	write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);  	return 0;  } @@ -2906,29 +3035,31 @@ static inline int directory_size(const char *filename)   * We want to avoid cross-directory filename renames, because those   * can have problems on various filesystems (FAT, NFS, Coda).   */ -static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename) +static int create_tmpfile(struct strbuf *tmp, const char *filename)  {  	int fd, dirlen = directory_size(filename); -	if (dirlen + 20 > bufsiz) { -		errno = ENAMETOOLONG; -		return -1; -	} -	memcpy(buffer, filename, dirlen); -	strcpy(buffer + dirlen, "tmp_obj_XXXXXX"); -	fd = git_mkstemp_mode(buffer, 0444); +	strbuf_reset(tmp); +	strbuf_add(tmp, filename, dirlen); +	strbuf_addstr(tmp, "tmp_obj_XXXXXX"); +	fd = git_mkstemp_mode(tmp->buf, 0444);  	if (fd < 0 && dirlen && errno == ENOENT) { -		/* Make sure the directory exists */ -		memcpy(buffer, filename, dirlen); -		buffer[dirlen-1] = 0; -		if (mkdir(buffer, 0777) && errno != EEXIST) +		/* +		 * Make sure the directory exists; note that the contents +		 * of the buffer are undefined after mkstemp returns an +		 * error, so we have to rewrite the whole buffer from +		 * scratch. +		 */ +		strbuf_reset(tmp); +		strbuf_add(tmp, filename, dirlen - 1); +		if (mkdir(tmp->buf, 0777) && errno != EEXIST)  			return -1; -		if (adjust_shared_perm(buffer)) +		if (adjust_shared_perm(tmp->buf))  			return -1;  		/* Try again */ -		strcpy(buffer + dirlen - 1, "/tmp_obj_XXXXXX"); -		fd = git_mkstemp_mode(buffer, 0444); +		strbuf_addstr(tmp, "/tmp_obj_XXXXXX"); +		fd = git_mkstemp_mode(tmp->buf, 0444);  	}  	return fd;  } @@ -2941,15 +3072,15 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,  	git_zstream stream;  	git_SHA_CTX c;  	unsigned char parano_sha1[20]; -	static char tmp_file[PATH_MAX]; +	static struct strbuf tmp_file = STRBUF_INIT;  	const char *filename = sha1_file_name(sha1); -	fd = create_tmpfile(tmp_file, sizeof(tmp_file), filename); +	fd = create_tmpfile(&tmp_file, filename);  	if (fd < 0) {  		if (errno == EACCES)  			return error("insufficient permission for adding an object to repository database %s", get_object_directory());  		else -			return error("unable to create temporary file: %s", strerror(errno)); +			return error_errno("unable to create temporary file");  	}  	/* Set it up */ @@ -2993,12 +3124,11 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,  		struct utimbuf utb;  		utb.actime = mtime;  		utb.modtime = mtime; -		if (utime(tmp_file, &utb) < 0) -			warning("failed utime() on %s: %s", -				tmp_file, strerror(errno)); +		if (utime(tmp_file.buf, &utb) < 0) +			warning_errno("failed utime() on %s", tmp_file.buf);  	} -	return move_temp_to_file(tmp_file, filename); +	return finalize_object_file(tmp_file.buf, filename);  }  static int freshen_loose_object(const unsigned char *sha1) @@ -3022,7 +3152,7 @@ static int freshen_packed_object(const unsigned char *sha1)  int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1)  {  	char hdr[32]; -	int hdrlen; +	int hdrlen = sizeof(hdr);  	/* Normally if we have it in the pack then we do not bother writing  	 * it out into .git/objects/??/?{38} file. @@ -3040,7 +3170,8 @@ int hash_sha1_file_literally(const void *buf, unsigned long len, const char *typ  	int hdrlen, status = 0;  	/* type string, SP, %lu of the length plus NUL must fit this */ -	header = xmalloc(strlen(type) + 32); +	hdrlen = strlen(type) + 32; +	header = xmalloc(hdrlen);  	write_sha1_file_prepare(buf, len, type, sha1, header, &hdrlen);  	if (!(flags & HASH_WRITE_OBJECT)) @@ -3068,7 +3199,7 @@ int force_object_loose(const unsigned char *sha1, time_t mtime)  	buf = read_packed_sha1(sha1, &type, &len);  	if (!buf)  		return error("cannot read sha1_file for %s", sha1_to_hex(sha1)); -	hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1; +	hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), len) + 1;  	ret = write_loose_object(sha1, hdr, hdrlen, buf, len, mtime);  	free(buf); @@ -3103,6 +3234,11 @@ int has_sha1_file_with_flags(const unsigned char *sha1, int flags)  	return find_pack_entry(sha1, &e);  } +int has_object_file(const struct object_id *oid) +{ +	return has_sha1_file(oid->hash); +} +  static void check_tree(const void *buf, size_t size)  {  	struct tree_desc desc; @@ -3222,7 +3358,7 @@ static int index_core(unsigned char *sha1, int fd, size_t size,  		if (size == read_in_full(fd, buf, size))  			ret = index_mem(sha1, buf, size, type, path, flags);  		else -			ret = error("short read %s", strerror(errno)); +			ret = error_errno("short read");  		free(buf);  	} else {  		void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); @@ -3287,18 +3423,14 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned  	case S_IFREG:  		fd = open(path, O_RDONLY);  		if (fd < 0) -			return error("open(\"%s\"): %s", path, -				     strerror(errno)); +			return error_errno("open(\"%s\")", path);  		if (index_fd(sha1, fd, st, OBJ_BLOB, path, flags) < 0)  			return error("%s: failed to insert into database",  				     path);  		break;  	case S_IFLNK: -		if (strbuf_readlink(&sb, path, st->st_size)) { -			char *errstr = strerror(errno); -			return error("readlink(\"%s\"): %s", path, -			             errstr); -		} +		if (strbuf_readlink(&sb, path, st->st_size)) +			return error_errno("readlink(\"%s\")", path);  		if (!(flags & HASH_WRITE_OBJECT))  			hash_sha1_file(sb.buf, sb.len, blob_type, sha1);  		else if (write_sha1_file(sb.buf, sb.len, blob_type, sha1)) @@ -3354,7 +3486,7 @@ static int for_each_file_in_obj_subdir(int subdir_nr,  	if (!dir) {  		if (errno == ENOENT)  			return 0; -		return error("unable to open %s: %s", path->buf, strerror(errno)); +		return error_errno("unable to open %s", path->buf);  	}  	while ((de = readdir(dir))) { @@ -3386,12 +3518,12 @@ static int for_each_file_in_obj_subdir(int subdir_nr,  				break;  		}  	} -	strbuf_setlen(path, baselen); +	closedir(dir); +	strbuf_setlen(path, baselen);  	if (!r && subdir_cb)  		r = subdir_cb(subdir_nr, path->buf, data); -	closedir(dir);  	return r;  } @@ -3496,14 +3628,19 @@ int for_each_packed_object(each_packed_object_fn cb, void *data, unsigned flags)  {  	struct packed_git *p;  	int r = 0; +	int pack_errors = 0;  	prepare_packed_git();  	for (p = packed_git; p; p = p->next) {  		if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)  			continue; +		if (open_pack_index(p)) { +			pack_errors = 1; +			continue; +		}  		r = for_each_object_in_pack(p, cb, data);  		if (r)  			break;  	} -	return r; +	return r ? r : pack_errors;  }  | 
