Dana L. How | 8b0eca7 | 2007-05-02 12:13:14 -0400 | [diff] [blame] | 1 | #include "cache.h" |
| 2 | #include "pack.h" |
Geert Bosch | aa7e44b | 2007-06-01 15:18:05 -0400 | [diff] [blame] | 3 | #include "csum-file.h" |
| 4 | |
Nicolas Pitre | c0a5e2d | 2008-06-25 00:25:53 -0400 | [diff] [blame] | 5 | uint32_t pack_idx_default_version = 2; |
Geert Bosch | aa7e44b | 2007-06-01 15:18:05 -0400 | [diff] [blame] | 6 | uint32_t pack_idx_off32_limit = 0x7fffffff; |
| 7 | |
| 8 | static int sha1_compare(const void *_a, const void *_b) |
| 9 | { |
| 10 | struct pack_idx_entry *a = *(struct pack_idx_entry **)_a; |
| 11 | struct pack_idx_entry *b = *(struct pack_idx_entry **)_b; |
| 12 | return hashcmp(a->sha1, b->sha1); |
| 13 | } |
| 14 | |
| 15 | /* |
| 16 | * On entry *sha1 contains the pack content SHA1 hash, on exit it is |
| 17 | * the SHA1 hash of sorted object names. The objects array passed in |
| 18 | * will be sorted by SHA1 on exit. |
| 19 | */ |
Nicolas Pitre | 4049b9c | 2007-10-16 21:55:49 -0400 | [diff] [blame] | 20 | char *write_idx_file(char *index_name, struct pack_idx_entry **objects, |
| 21 | int nr_objects, unsigned char *sha1) |
Geert Bosch | aa7e44b | 2007-06-01 15:18:05 -0400 | [diff] [blame] | 22 | { |
| 23 | struct sha1file *f; |
| 24 | struct pack_idx_entry **sorted_by_sha, **list, **last; |
| 25 | off_t last_obj_offset = 0; |
| 26 | uint32_t array[256]; |
| 27 | int i, fd; |
Nicolas Pitre | 9126f00 | 2008-10-01 14:05:20 -0400 | [diff] [blame] | 28 | git_SHA_CTX ctx; |
Geert Bosch | aa7e44b | 2007-06-01 15:18:05 -0400 | [diff] [blame] | 29 | uint32_t index_version; |
| 30 | |
| 31 | if (nr_objects) { |
| 32 | sorted_by_sha = objects; |
| 33 | list = sorted_by_sha; |
| 34 | last = sorted_by_sha + nr_objects; |
| 35 | for (i = 0; i < nr_objects; ++i) { |
| 36 | if (objects[i]->offset > last_obj_offset) |
| 37 | last_obj_offset = objects[i]->offset; |
| 38 | } |
| 39 | qsort(sorted_by_sha, nr_objects, sizeof(sorted_by_sha[0]), |
| 40 | sha1_compare); |
| 41 | } |
| 42 | else |
| 43 | sorted_by_sha = list = last = NULL; |
| 44 | |
| 45 | if (!index_name) { |
| 46 | static char tmpfile[PATH_MAX]; |
Junio C Hamano | 6e180cd | 2009-02-24 23:11:29 -0800 | [diff] [blame] | 47 | fd = odb_mkstemp(tmpfile, sizeof(tmpfile), "pack/tmp_idx_XXXXXX"); |
Geert Bosch | aa7e44b | 2007-06-01 15:18:05 -0400 | [diff] [blame] | 48 | index_name = xstrdup(tmpfile); |
| 49 | } else { |
| 50 | unlink(index_name); |
| 51 | fd = open(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600); |
| 52 | } |
| 53 | if (fd < 0) |
| 54 | die("unable to create %s: %s", index_name, strerror(errno)); |
| 55 | f = sha1fd(fd, index_name); |
| 56 | |
| 57 | /* if last object's offset is >= 2^31 we should use index V2 */ |
| 58 | index_version = (last_obj_offset >> 31) ? 2 : pack_idx_default_version; |
| 59 | |
| 60 | /* index versions 2 and above need a header */ |
| 61 | if (index_version >= 2) { |
| 62 | struct pack_idx_header hdr; |
| 63 | hdr.idx_signature = htonl(PACK_IDX_SIGNATURE); |
| 64 | hdr.idx_version = htonl(index_version); |
| 65 | sha1write(f, &hdr, sizeof(hdr)); |
| 66 | } |
| 67 | |
| 68 | /* |
| 69 | * Write the first-level table (the list is sorted, |
| 70 | * but we use a 256-entry lookup to be able to avoid |
| 71 | * having to do eight extra binary search iterations). |
| 72 | */ |
| 73 | for (i = 0; i < 256; i++) { |
| 74 | struct pack_idx_entry **next = list; |
| 75 | while (next < last) { |
| 76 | struct pack_idx_entry *obj = *next; |
| 77 | if (obj->sha1[0] != i) |
| 78 | break; |
| 79 | next++; |
| 80 | } |
| 81 | array[i] = htonl(next - sorted_by_sha); |
| 82 | list = next; |
| 83 | } |
| 84 | sha1write(f, array, 256 * 4); |
| 85 | |
| 86 | /* compute the SHA1 hash of sorted object names. */ |
Nicolas Pitre | 9126f00 | 2008-10-01 14:05:20 -0400 | [diff] [blame] | 87 | git_SHA1_Init(&ctx); |
Geert Bosch | aa7e44b | 2007-06-01 15:18:05 -0400 | [diff] [blame] | 88 | |
| 89 | /* |
| 90 | * Write the actual SHA1 entries.. |
| 91 | */ |
| 92 | list = sorted_by_sha; |
| 93 | for (i = 0; i < nr_objects; i++) { |
| 94 | struct pack_idx_entry *obj = *list++; |
| 95 | if (index_version < 2) { |
| 96 | uint32_t offset = htonl(obj->offset); |
| 97 | sha1write(f, &offset, 4); |
| 98 | } |
| 99 | sha1write(f, obj->sha1, 20); |
Nicolas Pitre | 9126f00 | 2008-10-01 14:05:20 -0400 | [diff] [blame] | 100 | git_SHA1_Update(&ctx, obj->sha1, 20); |
Geert Bosch | aa7e44b | 2007-06-01 15:18:05 -0400 | [diff] [blame] | 101 | } |
| 102 | |
| 103 | if (index_version >= 2) { |
| 104 | unsigned int nr_large_offset = 0; |
| 105 | |
| 106 | /* write the crc32 table */ |
| 107 | list = sorted_by_sha; |
| 108 | for (i = 0; i < nr_objects; i++) { |
| 109 | struct pack_idx_entry *obj = *list++; |
| 110 | uint32_t crc32_val = htonl(obj->crc32); |
| 111 | sha1write(f, &crc32_val, 4); |
| 112 | } |
| 113 | |
| 114 | /* write the 32-bit offset table */ |
| 115 | list = sorted_by_sha; |
| 116 | for (i = 0; i < nr_objects; i++) { |
| 117 | struct pack_idx_entry *obj = *list++; |
| 118 | uint32_t offset = (obj->offset <= pack_idx_off32_limit) ? |
| 119 | obj->offset : (0x80000000 | nr_large_offset++); |
| 120 | offset = htonl(offset); |
| 121 | sha1write(f, &offset, 4); |
| 122 | } |
| 123 | |
| 124 | /* write the large offset table */ |
| 125 | list = sorted_by_sha; |
| 126 | while (nr_large_offset) { |
| 127 | struct pack_idx_entry *obj = *list++; |
| 128 | uint64_t offset = obj->offset; |
| 129 | if (offset > pack_idx_off32_limit) { |
| 130 | uint32_t split[2]; |
| 131 | split[0] = htonl(offset >> 32); |
| 132 | split[1] = htonl(offset & 0xffffffff); |
| 133 | sha1write(f, split, 8); |
| 134 | nr_large_offset--; |
| 135 | } |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | sha1write(f, sha1, 20); |
Linus Torvalds | 4c81b03 | 2008-05-30 08:42:16 -0700 | [diff] [blame] | 140 | sha1close(f, NULL, CSUM_FSYNC); |
Nicolas Pitre | 9126f00 | 2008-10-01 14:05:20 -0400 | [diff] [blame] | 141 | git_SHA1_Final(sha1, &ctx); |
Geert Bosch | aa7e44b | 2007-06-01 15:18:05 -0400 | [diff] [blame] | 142 | return index_name; |
| 143 | } |
Dana L. How | 8b0eca7 | 2007-05-02 12:13:14 -0400 | [diff] [blame] | 144 | |
Nicolas Pitre | abeb40e | 2008-08-29 16:07:59 -0400 | [diff] [blame] | 145 | /* |
| 146 | * Update pack header with object_count and compute new SHA1 for pack data |
| 147 | * associated to pack_fd, and write that SHA1 at the end. That new SHA1 |
| 148 | * is also returned in new_pack_sha1. |
| 149 | * |
| 150 | * If partial_pack_sha1 is non null, then the SHA1 of the existing pack |
| 151 | * (without the header update) is computed and validated against the |
| 152 | * one provided in partial_pack_sha1. The validation is performed at |
| 153 | * partial_pack_offset bytes in the pack file. The SHA1 of the remaining |
| 154 | * data (i.e. from partial_pack_offset to the end) is then computed and |
| 155 | * returned in partial_pack_sha1. |
| 156 | * |
| 157 | * Note that new_pack_sha1 is updated last, so both new_pack_sha1 and |
| 158 | * partial_pack_sha1 can refer to the same buffer if the caller is not |
| 159 | * interested in the resulting SHA1 of pack data above partial_pack_offset. |
| 160 | */ |
Dana L. How | 8b0eca7 | 2007-05-02 12:13:14 -0400 | [diff] [blame] | 161 | void fixup_pack_header_footer(int pack_fd, |
Nicolas Pitre | abeb40e | 2008-08-29 16:07:59 -0400 | [diff] [blame] | 162 | unsigned char *new_pack_sha1, |
Dana L. How | 8b0eca7 | 2007-05-02 12:13:14 -0400 | [diff] [blame] | 163 | const char *pack_name, |
Nicolas Pitre | abeb40e | 2008-08-29 16:07:59 -0400 | [diff] [blame] | 164 | uint32_t object_count, |
| 165 | unsigned char *partial_pack_sha1, |
| 166 | off_t partial_pack_offset) |
Dana L. How | 8b0eca7 | 2007-05-02 12:13:14 -0400 | [diff] [blame] | 167 | { |
Nicolas Pitre | d35825d | 2008-08-29 16:08:02 -0400 | [diff] [blame] | 168 | int aligned_sz, buf_sz = 8 * 1024; |
Nicolas Pitre | 9126f00 | 2008-10-01 14:05:20 -0400 | [diff] [blame] | 169 | git_SHA_CTX old_sha1_ctx, new_sha1_ctx; |
Dana L. How | 8b0eca7 | 2007-05-02 12:13:14 -0400 | [diff] [blame] | 170 | struct pack_header hdr; |
| 171 | char *buf; |
| 172 | |
Nicolas Pitre | 9126f00 | 2008-10-01 14:05:20 -0400 | [diff] [blame] | 173 | git_SHA1_Init(&old_sha1_ctx); |
| 174 | git_SHA1_Init(&new_sha1_ctx); |
Nicolas Pitre | abeb40e | 2008-08-29 16:07:59 -0400 | [diff] [blame] | 175 | |
Dana L. How | 8b0eca7 | 2007-05-02 12:13:14 -0400 | [diff] [blame] | 176 | if (lseek(pack_fd, 0, SEEK_SET) != 0) |
Nicolas Pitre | abeb40e | 2008-08-29 16:07:59 -0400 | [diff] [blame] | 177 | die("Failed seeking to start of %s: %s", pack_name, strerror(errno)); |
Dana L. How | 8b0eca7 | 2007-05-02 12:13:14 -0400 | [diff] [blame] | 178 | if (read_in_full(pack_fd, &hdr, sizeof(hdr)) != sizeof(hdr)) |
| 179 | die("Unable to reread header of %s: %s", pack_name, strerror(errno)); |
| 180 | if (lseek(pack_fd, 0, SEEK_SET) != 0) |
Nicolas Pitre | abeb40e | 2008-08-29 16:07:59 -0400 | [diff] [blame] | 181 | die("Failed seeking to start of %s: %s", pack_name, strerror(errno)); |
Nicolas Pitre | 9126f00 | 2008-10-01 14:05:20 -0400 | [diff] [blame] | 182 | git_SHA1_Update(&old_sha1_ctx, &hdr, sizeof(hdr)); |
Dana L. How | 8b0eca7 | 2007-05-02 12:13:14 -0400 | [diff] [blame] | 183 | hdr.hdr_entries = htonl(object_count); |
Nicolas Pitre | 9126f00 | 2008-10-01 14:05:20 -0400 | [diff] [blame] | 184 | git_SHA1_Update(&new_sha1_ctx, &hdr, sizeof(hdr)); |
Dana L. How | 8b0eca7 | 2007-05-02 12:13:14 -0400 | [diff] [blame] | 185 | write_or_die(pack_fd, &hdr, sizeof(hdr)); |
Nicolas Pitre | abeb40e | 2008-08-29 16:07:59 -0400 | [diff] [blame] | 186 | partial_pack_offset -= sizeof(hdr); |
Dana L. How | 8b0eca7 | 2007-05-02 12:13:14 -0400 | [diff] [blame] | 187 | |
| 188 | buf = xmalloc(buf_sz); |
Nicolas Pitre | d35825d | 2008-08-29 16:08:02 -0400 | [diff] [blame] | 189 | aligned_sz = buf_sz - sizeof(hdr); |
Dana L. How | 8b0eca7 | 2007-05-02 12:13:14 -0400 | [diff] [blame] | 190 | for (;;) { |
Nicolas Pitre | abeb40e | 2008-08-29 16:07:59 -0400 | [diff] [blame] | 191 | ssize_t m, n; |
Nicolas Pitre | d35825d | 2008-08-29 16:08:02 -0400 | [diff] [blame] | 192 | m = (partial_pack_sha1 && partial_pack_offset < aligned_sz) ? |
| 193 | partial_pack_offset : aligned_sz; |
Nicolas Pitre | abeb40e | 2008-08-29 16:07:59 -0400 | [diff] [blame] | 194 | n = xread(pack_fd, buf, m); |
Dana L. How | 8b0eca7 | 2007-05-02 12:13:14 -0400 | [diff] [blame] | 195 | if (!n) |
| 196 | break; |
| 197 | if (n < 0) |
| 198 | die("Failed to checksum %s: %s", pack_name, strerror(errno)); |
Nicolas Pitre | 9126f00 | 2008-10-01 14:05:20 -0400 | [diff] [blame] | 199 | git_SHA1_Update(&new_sha1_ctx, buf, n); |
Nicolas Pitre | abeb40e | 2008-08-29 16:07:59 -0400 | [diff] [blame] | 200 | |
Nicolas Pitre | d35825d | 2008-08-29 16:08:02 -0400 | [diff] [blame] | 201 | aligned_sz -= n; |
| 202 | if (!aligned_sz) |
| 203 | aligned_sz = buf_sz; |
| 204 | |
Nicolas Pitre | abeb40e | 2008-08-29 16:07:59 -0400 | [diff] [blame] | 205 | if (!partial_pack_sha1) |
| 206 | continue; |
| 207 | |
Nicolas Pitre | 9126f00 | 2008-10-01 14:05:20 -0400 | [diff] [blame] | 208 | git_SHA1_Update(&old_sha1_ctx, buf, n); |
Nicolas Pitre | abeb40e | 2008-08-29 16:07:59 -0400 | [diff] [blame] | 209 | partial_pack_offset -= n; |
| 210 | if (partial_pack_offset == 0) { |
| 211 | unsigned char sha1[20]; |
Nicolas Pitre | 9126f00 | 2008-10-01 14:05:20 -0400 | [diff] [blame] | 212 | git_SHA1_Final(sha1, &old_sha1_ctx); |
Nicolas Pitre | abeb40e | 2008-08-29 16:07:59 -0400 | [diff] [blame] | 213 | if (hashcmp(sha1, partial_pack_sha1) != 0) |
| 214 | die("Unexpected checksum for %s " |
| 215 | "(disk corruption?)", pack_name); |
| 216 | /* |
| 217 | * Now let's compute the SHA1 of the remainder of the |
| 218 | * pack, which also means making partial_pack_offset |
| 219 | * big enough not to matter anymore. |
| 220 | */ |
Nicolas Pitre | 9126f00 | 2008-10-01 14:05:20 -0400 | [diff] [blame] | 221 | git_SHA1_Init(&old_sha1_ctx); |
Nicolas Pitre | abeb40e | 2008-08-29 16:07:59 -0400 | [diff] [blame] | 222 | partial_pack_offset = ~partial_pack_offset; |
| 223 | partial_pack_offset -= MSB(partial_pack_offset, 1); |
| 224 | } |
Dana L. How | 8b0eca7 | 2007-05-02 12:13:14 -0400 | [diff] [blame] | 225 | } |
| 226 | free(buf); |
| 227 | |
Nicolas Pitre | abeb40e | 2008-08-29 16:07:59 -0400 | [diff] [blame] | 228 | if (partial_pack_sha1) |
Nicolas Pitre | 9126f00 | 2008-10-01 14:05:20 -0400 | [diff] [blame] | 229 | git_SHA1_Final(partial_pack_sha1, &old_sha1_ctx); |
| 230 | git_SHA1_Final(new_pack_sha1, &new_sha1_ctx); |
Nicolas Pitre | abeb40e | 2008-08-29 16:07:59 -0400 | [diff] [blame] | 231 | write_or_die(pack_fd, new_pack_sha1, 20); |
Linus Torvalds | 0c68d38 | 2008-08-27 12:48:00 -0700 | [diff] [blame] | 232 | fsync_or_die(pack_fd, pack_name); |
Dana L. How | 8b0eca7 | 2007-05-02 12:13:14 -0400 | [diff] [blame] | 233 | } |
Shawn O. Pearce | 106764e | 2007-09-14 03:31:16 -0400 | [diff] [blame] | 234 | |
| 235 | char *index_pack_lockfile(int ip_out) |
| 236 | { |
Shawn O. Pearce | 106764e | 2007-09-14 03:31:16 -0400 | [diff] [blame] | 237 | char packname[46]; |
| 238 | |
| 239 | /* |
Junio C Hamano | 6e180cd | 2009-02-24 23:11:29 -0800 | [diff] [blame] | 240 | * The first thing we expect from index-pack's output |
Shawn O. Pearce | 106764e | 2007-09-14 03:31:16 -0400 | [diff] [blame] | 241 | * is "pack\t%40s\n" or "keep\t%40s\n" (46 bytes) where |
| 242 | * %40s is the newly created pack SHA1 name. In the "keep" |
| 243 | * case, we need it to remove the corresponding .keep file |
| 244 | * later on. If we don't get that then tough luck with it. |
| 245 | */ |
Heikki Orsila | c697ad1 | 2008-05-03 16:27:26 +0300 | [diff] [blame] | 246 | if (read_in_full(ip_out, packname, 46) == 46 && packname[45] == '\n' && |
| 247 | memcmp(packname, "keep\t", 5) == 0) { |
Shawn O. Pearce | 106764e | 2007-09-14 03:31:16 -0400 | [diff] [blame] | 248 | char path[PATH_MAX]; |
| 249 | packname[45] = 0; |
| 250 | snprintf(path, sizeof(path), "%s/pack/pack-%s.keep", |
| 251 | get_object_directory(), packname + 5); |
| 252 | return xstrdup(path); |
| 253 | } |
| 254 | return NULL; |
| 255 | } |