diff options
-rw-r--r-- | cache.h | 58 | ||||
-rw-r--r-- | read-cache.c | 51 |
2 files changed, 95 insertions, 14 deletions
@@ -115,6 +115,26 @@ struct ondisk_cache_entry { char name[FLEX_ARRAY]; /* more */ }; +/* + * This struct is used when CE_EXTENDED bit is 1 + * The struct must match ondisk_cache_entry exactly from + * ctime till flags + */ +struct ondisk_cache_entry_extended { + struct cache_time ctime; + struct cache_time mtime; + unsigned int dev; + unsigned int ino; + unsigned int mode; + unsigned int uid; + unsigned int gid; + unsigned int size; + unsigned char sha1[20]; + unsigned short flags; + unsigned short flags2; + char name[FLEX_ARRAY]; /* more */ +}; + struct cache_entry { unsigned int ce_ctime; unsigned int ce_mtime; @@ -136,7 +156,15 @@ struct cache_entry { #define CE_VALID (0x8000) #define CE_STAGESHIFT 12 -/* In-memory only */ +/* + * Range 0xFFFF0000 in ce_flags is divided into + * two parts: in-memory flags and on-disk ones. + * Flags in CE_EXTENDED_FLAGS will get saved on-disk + * if you want to save a new flag, add it in + * CE_EXTENDED_FLAGS + * + * In-memory only flags + */ #define CE_UPDATE (0x10000) #define CE_REMOVE (0x20000) #define CE_UPTODATE (0x40000) @@ -146,6 +174,24 @@ struct cache_entry { #define CE_UNHASHED (0x200000) /* + * Extended on-disk flags + */ +/* CE_EXTENDED2 is for future extension */ +#define CE_EXTENDED2 0x80000000 + +#define CE_EXTENDED_FLAGS (0) + +/* + * Safeguard to avoid saving wrong flags: + * - CE_EXTENDED2 won't get saved until its semantic is known + * - Bits in 0x0000FFFF have been saved in ce_flags already + * - Bits in 0x003F0000 are currently in-memory flags + */ +#if CE_EXTENDED_FLAGS & 0x803FFFFF +#error "CE_EXTENDED_FLAGS out of range" +#endif + +/* * Copy the sha1 and stat state of a cache entry from one to * another. But we never change the name, or the hash state! */ @@ -177,7 +223,9 @@ static inline size_t ce_namelen(const struct cache_entry *ce) } #define ce_size(ce) cache_entry_size(ce_namelen(ce)) -#define ondisk_ce_size(ce) ondisk_cache_entry_size(ce_namelen(ce)) +#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \ + ondisk_cache_entry_extended_size(ce_namelen(ce)) : \ + ondisk_cache_entry_size(ce_namelen(ce))) #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT) #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE) #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE) @@ -220,8 +268,10 @@ static inline int ce_to_dtype(const struct cache_entry *ce) (S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \ S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK) -#define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7) -#define ondisk_cache_entry_size(len) ((offsetof(struct ondisk_cache_entry,name) + (len) + 8) & ~7) +#define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7) +#define cache_entry_size(len) flexible_size(cache_entry,len) +#define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len) +#define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len) struct index_state { struct cache_entry **cache; diff --git a/read-cache.c b/read-cache.c index 22a814311d..abc627ba0b 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1098,7 +1098,7 @@ static int verify_hdr(struct cache_header *hdr, unsigned long size) if (hdr->hdr_signature != htonl(CACHE_SIGNATURE)) return error("bad signature"); - if (hdr->hdr_version != htonl(2)) + if (hdr->hdr_version != htonl(2) && hdr->hdr_version != htonl(3)) return error("bad index version"); git_SHA1_Init(&c); git_SHA1_Update(&c, hdr, size - 20); @@ -1133,6 +1133,7 @@ int read_index(struct index_state *istate) static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_entry *ce) { size_t len; + const char *name; ce->ce_ctime = ntohl(ondisk->ctime.sec); ce->ce_mtime = ntohl(ondisk->mtime.sec); @@ -1145,19 +1146,31 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en /* On-disk flags are just 16 bits */ ce->ce_flags = ntohs(ondisk->flags); - /* For future extension: we do not understand this entry yet */ - if (ce->ce_flags & CE_EXTENDED) - die("Unknown index entry format"); hashcpy(ce->sha1, ondisk->sha1); len = ce->ce_flags & CE_NAMEMASK; + + if (ce->ce_flags & CE_EXTENDED) { + struct ondisk_cache_entry_extended *ondisk2; + int extended_flags; + ondisk2 = (struct ondisk_cache_entry_extended *)ondisk; + extended_flags = ntohs(ondisk2->flags2) << 16; + /* We do not yet understand any bit out of CE_EXTENDED_FLAGS */ + if (extended_flags & ~CE_EXTENDED_FLAGS) + die("Unknown index entry format %08x", extended_flags); + ce->ce_flags |= extended_flags; + name = ondisk2->name; + } + else + name = ondisk->name; + if (len == CE_NAMEMASK) - len = strlen(ondisk->name); + len = strlen(name); /* * NEEDSWORK: If the original index is crafted, this copy could * go unchecked. */ - memcpy(ce->name, ondisk->name, len + 1); + memcpy(ce->name, name, len + 1); } static inline size_t estimate_cache_size(size_t ondisk_size, unsigned int entries) @@ -1422,6 +1435,7 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce) { int size = ondisk_ce_size(ce); struct ondisk_cache_entry *ondisk = xcalloc(1, size); + char *name; ondisk->ctime.sec = htonl(ce->ce_ctime); ondisk->ctime.nsec = 0; @@ -1435,7 +1449,15 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce) ondisk->size = htonl(ce->ce_size); hashcpy(ondisk->sha1, ce->sha1); ondisk->flags = htons(ce->ce_flags); - memcpy(ondisk->name, ce->name, ce_namelen(ce)); + if (ce->ce_flags & CE_EXTENDED) { + struct ondisk_cache_entry_extended *ondisk2; + ondisk2 = (struct ondisk_cache_entry_extended *)ondisk; + ondisk2->flags2 = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16); + name = ondisk2->name; + } + else + name = ondisk->name; + memcpy(name, ce->name, ce_namelen(ce)); return ce_write(c, fd, ondisk, size); } @@ -1444,16 +1466,25 @@ int write_index(const struct index_state *istate, int newfd) { git_SHA_CTX c; struct cache_header hdr; - int i, err, removed; + int i, err, removed, extended; struct cache_entry **cache = istate->cache; int entries = istate->cache_nr; - for (i = removed = 0; i < entries; i++) + for (i = removed = extended = 0; i < entries; i++) { if (cache[i]->ce_flags & CE_REMOVE) removed++; + /* reduce extended entries if possible */ + cache[i]->ce_flags &= ~CE_EXTENDED; + if (cache[i]->ce_flags & CE_EXTENDED_FLAGS) { + extended++; + cache[i]->ce_flags |= CE_EXTENDED; + } + } + hdr.hdr_signature = htonl(CACHE_SIGNATURE); - hdr.hdr_version = htonl(2); + /* for extended format, increase version so older git won't try to read it */ + hdr.hdr_version = htonl(extended ? 3 : 2); hdr.hdr_entries = htonl(entries - removed); git_SHA1_Init(&c); |