Skip to content

Commit 06aaaa0

Browse files
pcloudsspearce
authored andcommitted
Extend index to save more flags
The on-disk format of index only saves 16 bit flags, nearly all have been used. The last bit (CE_EXTENDED) is used to for future extension. This patch extends index entry format to save more flags in future. The new entry format will be used when CE_EXTENDED bit is 1. Because older implementation may not understand CE_EXTENDED bit and misread the new format, if there is any extended entry in index, index header version will turn 3, which makes it incompatible for older git. If there is none, header version will return to 2 again. Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Shawn O. Pearce <[email protected]>
1 parent 5c283eb commit 06aaaa0

File tree

2 files changed

+95
-14
lines changed

2 files changed

+95
-14
lines changed

cache.h

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,26 @@ struct ondisk_cache_entry {
115115
char name[FLEX_ARRAY]; /* more */
116116
};
117117

118+
/*
119+
* This struct is used when CE_EXTENDED bit is 1
120+
* The struct must match ondisk_cache_entry exactly from
121+
* ctime till flags
122+
*/
123+
struct ondisk_cache_entry_extended {
124+
struct cache_time ctime;
125+
struct cache_time mtime;
126+
unsigned int dev;
127+
unsigned int ino;
128+
unsigned int mode;
129+
unsigned int uid;
130+
unsigned int gid;
131+
unsigned int size;
132+
unsigned char sha1[20];
133+
unsigned short flags;
134+
unsigned short flags2;
135+
char name[FLEX_ARRAY]; /* more */
136+
};
137+
118138
struct cache_entry {
119139
unsigned int ce_ctime;
120140
unsigned int ce_mtime;
@@ -136,7 +156,15 @@ struct cache_entry {
136156
#define CE_VALID (0x8000)
137157
#define CE_STAGESHIFT 12
138158

139-
/* In-memory only */
159+
/*
160+
* Range 0xFFFF0000 in ce_flags is divided into
161+
* two parts: in-memory flags and on-disk ones.
162+
* Flags in CE_EXTENDED_FLAGS will get saved on-disk
163+
* if you want to save a new flag, add it in
164+
* CE_EXTENDED_FLAGS
165+
*
166+
* In-memory only flags
167+
*/
140168
#define CE_UPDATE (0x10000)
141169
#define CE_REMOVE (0x20000)
142170
#define CE_UPTODATE (0x40000)
@@ -145,6 +173,24 @@ struct cache_entry {
145173
#define CE_HASHED (0x100000)
146174
#define CE_UNHASHED (0x200000)
147175

176+
/*
177+
* Extended on-disk flags
178+
*/
179+
/* CE_EXTENDED2 is for future extension */
180+
#define CE_EXTENDED2 0x80000000
181+
182+
#define CE_EXTENDED_FLAGS (0)
183+
184+
/*
185+
* Safeguard to avoid saving wrong flags:
186+
* - CE_EXTENDED2 won't get saved until its semantic is known
187+
* - Bits in 0x0000FFFF have been saved in ce_flags already
188+
* - Bits in 0x003F0000 are currently in-memory flags
189+
*/
190+
#if CE_EXTENDED_FLAGS & 0x803FFFFF
191+
#error "CE_EXTENDED_FLAGS out of range"
192+
#endif
193+
148194
/*
149195
* Copy the sha1 and stat state of a cache entry from one to
150196
* 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)
177223
}
178224

179225
#define ce_size(ce) cache_entry_size(ce_namelen(ce))
180-
#define ondisk_ce_size(ce) ondisk_cache_entry_size(ce_namelen(ce))
226+
#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \
227+
ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
228+
ondisk_cache_entry_size(ce_namelen(ce)))
181229
#define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
182230
#define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
183231
#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)
220268
(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
221269
S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
222270

223-
#define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
224-
#define ondisk_cache_entry_size(len) ((offsetof(struct ondisk_cache_entry,name) + (len) + 8) & ~7)
271+
#define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
272+
#define cache_entry_size(len) flexible_size(cache_entry,len)
273+
#define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len)
274+
#define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len)
225275

226276
struct index_state {
227277
struct cache_entry **cache;

read-cache.c

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,7 +1098,7 @@ static int verify_hdr(struct cache_header *hdr, unsigned long size)
10981098

10991099
if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
11001100
return error("bad signature");
1101-
if (hdr->hdr_version != htonl(2))
1101+
if (hdr->hdr_version != htonl(2) && hdr->hdr_version != htonl(3))
11021102
return error("bad index version");
11031103
git_SHA1_Init(&c);
11041104
git_SHA1_Update(&c, hdr, size - 20);
@@ -1133,6 +1133,7 @@ int read_index(struct index_state *istate)
11331133
static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_entry *ce)
11341134
{
11351135
size_t len;
1136+
const char *name;
11361137

11371138
ce->ce_ctime = ntohl(ondisk->ctime.sec);
11381139
ce->ce_mtime = ntohl(ondisk->mtime.sec);
@@ -1145,19 +1146,31 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en
11451146
/* On-disk flags are just 16 bits */
11461147
ce->ce_flags = ntohs(ondisk->flags);
11471148

1148-
/* For future extension: we do not understand this entry yet */
1149-
if (ce->ce_flags & CE_EXTENDED)
1150-
die("Unknown index entry format");
11511149
hashcpy(ce->sha1, ondisk->sha1);
11521150

11531151
len = ce->ce_flags & CE_NAMEMASK;
1152+
1153+
if (ce->ce_flags & CE_EXTENDED) {
1154+
struct ondisk_cache_entry_extended *ondisk2;
1155+
int extended_flags;
1156+
ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
1157+
extended_flags = ntohs(ondisk2->flags2) << 16;
1158+
/* We do not yet understand any bit out of CE_EXTENDED_FLAGS */
1159+
if (extended_flags & ~CE_EXTENDED_FLAGS)
1160+
die("Unknown index entry format %08x", extended_flags);
1161+
ce->ce_flags |= extended_flags;
1162+
name = ondisk2->name;
1163+
}
1164+
else
1165+
name = ondisk->name;
1166+
11541167
if (len == CE_NAMEMASK)
1155-
len = strlen(ondisk->name);
1168+
len = strlen(name);
11561169
/*
11571170
* NEEDSWORK: If the original index is crafted, this copy could
11581171
* go unchecked.
11591172
*/
1160-
memcpy(ce->name, ondisk->name, len + 1);
1173+
memcpy(ce->name, name, len + 1);
11611174
}
11621175

11631176
static inline size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
@@ -1417,6 +1430,7 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
14171430
{
14181431
int size = ondisk_ce_size(ce);
14191432
struct ondisk_cache_entry *ondisk = xcalloc(1, size);
1433+
char *name;
14201434

14211435
ondisk->ctime.sec = htonl(ce->ce_ctime);
14221436
ondisk->ctime.nsec = 0;
@@ -1430,7 +1444,15 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
14301444
ondisk->size = htonl(ce->ce_size);
14311445
hashcpy(ondisk->sha1, ce->sha1);
14321446
ondisk->flags = htons(ce->ce_flags);
1433-
memcpy(ondisk->name, ce->name, ce_namelen(ce));
1447+
if (ce->ce_flags & CE_EXTENDED) {
1448+
struct ondisk_cache_entry_extended *ondisk2;
1449+
ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
1450+
ondisk2->flags2 = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16);
1451+
name = ondisk2->name;
1452+
}
1453+
else
1454+
name = ondisk->name;
1455+
memcpy(name, ce->name, ce_namelen(ce));
14341456

14351457
return ce_write(c, fd, ondisk, size);
14361458
}
@@ -1439,16 +1461,25 @@ int write_index(const struct index_state *istate, int newfd)
14391461
{
14401462
git_SHA_CTX c;
14411463
struct cache_header hdr;
1442-
int i, err, removed;
1464+
int i, err, removed, extended;
14431465
struct cache_entry **cache = istate->cache;
14441466
int entries = istate->cache_nr;
14451467

1446-
for (i = removed = 0; i < entries; i++)
1468+
for (i = removed = extended = 0; i < entries; i++) {
14471469
if (cache[i]->ce_flags & CE_REMOVE)
14481470
removed++;
14491471

1472+
/* reduce extended entries if possible */
1473+
cache[i]->ce_flags &= ~CE_EXTENDED;
1474+
if (cache[i]->ce_flags & CE_EXTENDED_FLAGS) {
1475+
extended++;
1476+
cache[i]->ce_flags |= CE_EXTENDED;
1477+
}
1478+
}
1479+
14501480
hdr.hdr_signature = htonl(CACHE_SIGNATURE);
1451-
hdr.hdr_version = htonl(2);
1481+
/* for extended format, increase version so older git won't try to read it */
1482+
hdr.hdr_version = htonl(extended ? 3 : 2);
14521483
hdr.hdr_entries = htonl(entries - removed);
14531484

14541485
git_SHA1_Init(&c);

0 commit comments

Comments
 (0)