Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> --- cache.h | 29 +++++++++-- fast-import.c | 2 +- pack-check.c | 2 +- sha1_file.c | 165 +++++++++++++++++++++++++++++++++++++++------------------- streaming.c | 4 +- 5 files changed, 141 insertions(+), 61 deletions(-) diff --git a/cache.h b/cache.h index 94ca1ac..bed403a 100644 --- a/cache.h +++ b/cache.h @@ -744,12 +744,33 @@ char *strip_path_suffix(const char *path, const char *suffix); int daemon_avoid_alias(const char *path); int offset_1st_component(const char *path); +/* + * odb origin bit mask: + * - 8 low bits are for requested objects + * - 8 high bits are for their base objects + */ +#define ODB_LOCAL 1 +#define ODB_ALT 2 +#define ODB_CACHED 4 +/* + * This flag is used to track if the origin selection is from + * "odb_default" below. Not a real source, not to be passed to any + * function cals explicitly. + */ +#define ODB_DEFAULT 8 + +extern unsigned int odb_default; + /* object replacement */ #define READ_SHA1_FILE_REPLACE 1 -extern void *read_sha1_file_extended(const unsigned char *sha1, enum object_type *type, unsigned long *size, unsigned flag); +extern void *read_sha1_file_extended(const unsigned char *sha1, + unsigned int origin, + enum object_type *type, + unsigned long *size, + unsigned flag); static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size) { - return read_sha1_file_extended(sha1, type, size, READ_SHA1_FILE_REPLACE); + return read_sha1_file_extended(sha1, odb_default, type, size, READ_SHA1_FILE_REPLACE); } extern const unsigned char *do_lookup_replace_object(const unsigned char *sha1); static inline const unsigned char *lookup_replace_object(const unsigned char *sha1) @@ -765,7 +786,7 @@ extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1); extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *); extern int force_object_loose(const unsigned char *sha1, time_t mtime); -extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size); +extern void *map_sha1_file(const unsigned char *sha1, unsigned int, unsigned long *size); extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz); extern int parse_sha1_header(const char *hdr, unsigned long *sizep); @@ -1091,7 +1112,7 @@ extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t); extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *); extern int is_pack_valid(struct packed_git *); -extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *); +extern void *unpack_entry(struct packed_git *, unsigned int, off_t, enum object_type *, unsigned long *); extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep); extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t); extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *); diff --git a/fast-import.c b/fast-import.c index 5f539d7..8542786 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1303,7 +1303,7 @@ static void *gfi_unpack_entry( */ p->pack_size = pack_size + 20; } - return unpack_entry(p, oe->idx.offset, &type, sizep); + return unpack_entry(p, odb_default, oe->idx.offset, &type, sizep); } static const char *get_mode(const char *str, uint16_t *modep) diff --git a/pack-check.c b/pack-check.c index 63a595c..981dc98 100644 --- a/pack-check.c +++ b/pack-check.c @@ -116,7 +116,7 @@ static int verify_packfile(struct packed_git *p, sha1_to_hex(entries[i].sha1), p->pack_name, (uintmax_t)offset); } - data = unpack_entry(p, entries[i].offset, &type, &size); + data = unpack_entry(p, odb_default, entries[i].offset, &type, &size); if (!data) err = error("cannot unpack %s from %s at offset %"PRIuMAX"", sha1_to_hex(entries[i].sha1), p->pack_name, diff --git a/sha1_file.c b/sha1_file.c index 99ead7c..d1f44c9 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -37,6 +37,12 @@ static inline uintmax_t sz_fmt(size_t s) { return s; } const unsigned char null_sha1[20]; /* + * clear_delta_base_cache() may be needed if odb_default is changed to + * a narrower origin set. + */ +unsigned int odb_default = ODB_DEFAULT | ODB_LOCAL | ODB_ALT | ODB_CACHED; + +/* * This is meant to hold a *small* number of objects that you would * want read_sha1_file() to be able to return, but yet you do not want * to write them into the object store (e.g. a browse-only @@ -59,11 +65,15 @@ static struct cached_object empty_tree = { static struct packed_git *last_found_pack; -static struct cached_object *find_cached_object(const unsigned char *sha1) +static struct cached_object *find_cached_object(const unsigned char *sha1, + unsigned int origin) { int i; struct cached_object *co = cached_objects; + if (!(origin & ODB_CACHED)) + return NULL; + for (i = 0; i < cached_object_nr; i++, co++) { if (!hashcmp(co->sha1, sha1)) return co; @@ -404,6 +414,12 @@ void foreach_alt_odb(alt_odb_fn fn, void *cb) return; } +static inline int match_origin(unsigned int origin, int pack_local) +{ + return (pack_local && (origin & ODB_LOCAL)) || + (!pack_local && (origin & ODB_ALT)); +} + void prepare_alt_odb(void) { const char *alt; @@ -420,28 +436,31 @@ void prepare_alt_odb(void) read_info_alternates(get_object_directory(), 0); } -static int has_loose_object_local(const unsigned char *sha1) +static int has_loose_object_extended(const unsigned char *sha1, + unsigned int origin) { - char *name = sha1_file_name(sha1); - return !access(name, F_OK); -} - -int has_loose_object_nonlocal(const unsigned char *sha1) -{ - struct alternate_object_database *alt; - prepare_alt_odb(); - for (alt = alt_odb_list; alt; alt = alt->next) { - fill_sha1_path(alt->name, sha1); - if (!access(alt->base, F_OK)) + if (origin & ODB_LOCAL) { + char *name = sha1_file_name(sha1); + if (!access(name, F_OK)) return 1; } + + if (origin & ODB_ALT) { + struct alternate_object_database *alt; + prepare_alt_odb(); + for (alt = alt_odb_list; alt; alt = alt->next) { + fill_sha1_path(alt->name, sha1); + if (!access(alt->base, F_OK)) + return 1; + } + } return 0; } -static int has_loose_object(const unsigned char *sha1) +int has_loose_object_nonlocal(const unsigned char *sha1) { - return has_loose_object_local(sha1) || - has_loose_object_nonlocal(sha1); + unsigned int origin = ODB_ALT; + return has_loose_object_extended(sha1, origin); } static unsigned int pack_used_ctr; @@ -1303,16 +1322,21 @@ static int git_open_noatime(const char *name) } } -static int open_sha1_file(const unsigned char *sha1) +static int open_sha1_file(const unsigned char *sha1, unsigned int origin) { int fd; char *name = sha1_file_name(sha1); struct alternate_object_database *alt; - fd = git_open_noatime(name); - if (fd >= 0) + if ((origin & ODB_LOCAL) && + (fd = git_open_noatime(name)) >= 0) return fd; + if (!(origin & ODB_ALT)) { + errno = ENOENT; + return -1; + } + prepare_alt_odb(); errno = ENOENT; for (alt = alt_odb_list; alt; alt = alt->next) { @@ -1325,12 +1349,14 @@ static int open_sha1_file(const unsigned char *sha1) return -1; } -void *map_sha1_file(const unsigned char *sha1, unsigned long *size) +void *map_sha1_file(const unsigned char *sha1, + unsigned int origin, + unsigned long *size) { void *map; int fd; - fd = open_sha1_file(sha1); + fd = open_sha1_file(sha1, origin); map = NULL; if (fd >= 0) { struct stat st; @@ -1872,7 +1898,8 @@ static void clear_delta_base_cache_entry(struct delta_base_cache_entry *ent) delta_base_cached -= ent->size; } -static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset, +static void *cache_or_unpack_entry(struct packed_git *p, + unsigned int origin, off_t base_offset, unsigned long *base_size, enum object_type *type, int keep_cache) { struct delta_base_cache_entry *ent; @@ -1880,8 +1907,9 @@ static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset, ent = get_delta_base_cache_entry(p, base_offset); - if (!eq_delta_base_cache_entry(ent, p, base_offset)) - return unpack_entry(p, base_offset, type, base_size); + if (!(origin & ODB_DEFAULT) || /* only cache the default case */ + !eq_delta_base_cache_entry(ent, p, base_offset)) + return unpack_entry(p, origin, base_offset, type, base_size); ret = ent->data; @@ -1951,7 +1979,9 @@ add_delta_base_cache(struct packed_git *p, off_t base_offset, return ent; } -static void *read_object(const unsigned char *sha1, enum object_type *type, +static void *read_object(const unsigned char *sha1, + unsigned int origin, + enum object_type *type, unsigned long *size); static void write_pack_access_log(struct packed_git *p, off_t obj_offset) @@ -1981,7 +2011,8 @@ struct unpack_entry_stack_ent { unsigned long size; }; -void *unpack_entry(struct packed_git *p, off_t obj_offset, +void *unpack_entry(struct packed_git *p, + unsigned int origin, off_t obj_offset, enum object_type *final_type, unsigned long *final_size) { struct pack_window *w_curs = NULL; @@ -2093,7 +2124,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, data = NULL; - if (base) + if (base && (origin & ODB_DEFAULT)) ent = add_delta_base_cache(p, obj_offset, base, base_size, type); if (!base) { @@ -2113,7 +2144,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, sha1_to_hex(base_sha1), (uintmax_t)obj_offset, p->pack_name); mark_bad_packed_object(p, base_sha1); - base = read_object(base_sha1, &type, &base_size); + base = read_object(base_sha1, origin, &type, &base_size); } } @@ -2277,11 +2308,15 @@ int is_pack_valid(struct packed_git *p) } static int fill_pack_entry(const unsigned char *sha1, + unsigned int origin, struct pack_entry *e, struct packed_git *p) { off_t offset; + if (!match_origin(origin, p->pack_local)) + return 0; + if (p->num_bad_objects) { unsigned i; for (i = 0; i < p->num_bad_objects; i++) @@ -2310,7 +2345,9 @@ static int fill_pack_entry(const unsigned char *sha1, return 1; } -static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e) +static int find_pack_entry(const unsigned char *sha1, + unsigned int origin, + struct pack_entry *e) { struct packed_git *p; @@ -2318,11 +2355,12 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e) if (!packed_git) return 0; - if (last_found_pack && fill_pack_entry(sha1, e, last_found_pack)) + if (last_found_pack && + fill_pack_entry(sha1, origin, e, last_found_pack)) return 1; for (p = packed_git; p; p = p->next) { - if (p == last_found_pack || !fill_pack_entry(sha1, e, p)) + if (p == last_found_pack || !fill_pack_entry(sha1, origin, e, p)) continue; last_found_pack = p; @@ -2352,7 +2390,7 @@ static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *size git_zstream stream; char hdr[32]; - map = map_sha1_file(sha1, &mapsize); + map = map_sha1_file(sha1, odb_default, &mapsize); if (!map) return error("unable to find %s", sha1_to_hex(sha1)); if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) @@ -2373,8 +2411,9 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi) struct cached_object *co; struct pack_entry e; int status, rtype; + unsigned int origin = odb_default; - co = find_cached_object(sha1); + co = find_cached_object(sha1, origin); if (co) { if (oi->sizep) *(oi->sizep) = co->size; @@ -2382,7 +2421,7 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi) return co->type; } - if (!find_pack_entry(sha1, &e)) { + if (!find_pack_entry(sha1, origin, &e)) { /* Most likely it's a loose object. */ status = sha1_loose_object_info(sha1, oi->sizep); if (status >= 0) { @@ -2392,7 +2431,7 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi) /* Not a loose object; someone else may have just packed it. */ reprepare_packed_git(); - if (!find_pack_entry(sha1, &e)) + if (!find_pack_entry(sha1, origin, &e)) return status; } @@ -2422,15 +2461,26 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep) } static void *read_packed_sha1(const unsigned char *sha1, + unsigned int origin, enum object_type *type, unsigned long *size) { struct pack_entry e; void *data; - if (!find_pack_entry(sha1, &e)) + if (!find_pack_entry(sha1, origin, &e)) return NULL; - data = cache_or_unpack_entry(e.p, e.offset, size, type, 1); - if (!data) { + data = cache_or_unpack_entry(e.p, origin, e.offset, size, type, 1); + if (!data && + /* + * If a user attempts to read from a single source via + * read_sha1_file_extended and the object has base in + * another source, it'll come here. It's not as bad as the + * corrupted pack we're handling in the below block + * because the user asked to do so and must be able to + * deal with the consequences. Just return NULL in this + * case without marking the sha-1 "bad". + */ + !(origin & ODB_DEFAULT)) { /* * We're probably in deep shit, but let's try to fetch * the required object anyway from another pack or loose. @@ -2440,7 +2490,7 @@ static void *read_packed_sha1(const unsigned char *sha1, error("failed to read object %s at offset %"PRIuMAX" from %s", sha1_to_hex(sha1), (uintmax_t)e.offset, e.p->pack_name); mark_bad_packed_object(e.p, sha1); - data = read_object(sha1, type, size); + data = read_object(sha1, origin, type, size); } return data; } @@ -2451,7 +2501,7 @@ int pretend_sha1_file(void *buf, unsigned long len, enum object_type type, struct cached_object *co; hash_sha1_file(buf, len, typename(type), sha1); - if (has_sha1_file(sha1) || find_cached_object(sha1)) + if (has_sha1_file(sha1) || find_cached_object(sha1, odb_default)) return 0; if (cached_object_alloc <= cached_object_nr) { cached_object_alloc = alloc_nr(cached_object_alloc); @@ -2468,31 +2518,33 @@ int pretend_sha1_file(void *buf, unsigned long len, enum object_type type, return 0; } -static void *read_object(const unsigned char *sha1, enum object_type *type, +static void *read_object(const unsigned char *sha1, + unsigned int origin, + enum object_type *type, unsigned long *size) { unsigned long mapsize; void *map, *buf; struct cached_object *co; - co = find_cached_object(sha1); + co = find_cached_object(sha1, origin); if (co) { *type = co->type; *size = co->size; return xmemdupz(co->buf, co->size); } - buf = read_packed_sha1(sha1, type, size); + buf = read_packed_sha1(sha1, origin, type, size); if (buf) return buf; - map = map_sha1_file(sha1, &mapsize); + map = map_sha1_file(sha1, origin, &mapsize); if (map) { buf = unpack_sha1_file(map, mapsize, type, size, sha1); munmap(map, mapsize); return buf; } reprepare_packed_git(); - return read_packed_sha1(sha1, type, size); + return read_packed_sha1(sha1, origin, type, size); } /* @@ -2501,6 +2553,7 @@ static void *read_object(const unsigned char *sha1, enum object_type *type, * messages themselves. */ void *read_sha1_file_extended(const unsigned char *sha1, + unsigned int origin, enum object_type *type, unsigned long *size, unsigned flag) @@ -2512,7 +2565,7 @@ void *read_sha1_file_extended(const unsigned char *sha1, ? lookup_replace_object(sha1) : sha1; errno = 0; - data = read_object(repl, type, size); + data = read_object(repl, origin, type, size); if (data) return data; @@ -2524,7 +2577,7 @@ void *read_sha1_file_extended(const unsigned char *sha1, die("replacement %s not found for %s", sha1_to_hex(repl), sha1_to_hex(sha1)); - if (has_loose_object(repl)) { + if (has_loose_object_extended(repl, origin)) { path = sha1_file_name(sha1); die("loose object %s (stored in %s) is corrupt", sha1_to_hex(repl), path); @@ -2809,9 +2862,9 @@ int force_object_loose(const unsigned char *sha1, time_t mtime) int hdrlen; int ret; - if (has_loose_object(sha1)) + if (has_loose_object_extended(sha1, odb_default)) return 0; - buf = read_packed_sha1(sha1, &type, &len); + buf = read_packed_sha1(sha1, odb_default, &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; @@ -2832,16 +2885,22 @@ int has_pack_index(const unsigned char *sha1) int has_sha1_pack(const unsigned char *sha1) { struct pack_entry e; - return find_pack_entry(sha1, &e); + return find_pack_entry(sha1, odb_default, &e); } -int has_sha1_file(const unsigned char *sha1) +static int has_sha1_file_extended(const unsigned char *sha1, + unsigned origin) { struct pack_entry e; - if (find_pack_entry(sha1, &e)) + if (find_pack_entry(sha1, origin, &e)) return 1; - return has_loose_object(sha1); + return has_loose_object_extended(sha1, origin); +} + +int has_sha1_file(const unsigned char *sha1) +{ + return has_sha1_file_extended(sha1, odb_default); } static void check_tree(const void *buf, size_t size) diff --git a/streaming.c b/streaming.c index cabcd9d..975002c 100644 --- a/streaming.c +++ b/streaming.c @@ -332,7 +332,7 @@ static struct stream_vtbl loose_vtbl = { static open_method_decl(loose) { - st->u.loose.mapped = map_sha1_file(sha1, &st->u.loose.mapsize); + st->u.loose.mapped = map_sha1_file(sha1, odb_default, &st->u.loose.mapsize); if (!st->u.loose.mapped) return -1; if (unpack_sha1_header(&st->z, @@ -483,7 +483,7 @@ static struct stream_vtbl incore_vtbl = { static open_method_decl(incore) { - st->u.incore.buf = read_sha1_file_extended(sha1, type, &st->size, 0); + st->u.incore.buf = read_sha1_file_extended(sha1, odb_default, type, &st->size, 0); st->u.incore.read_ptr = 0; st->vtbl = &incore_vtbl; -- 1.8.2.83.gc99314b -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html