Raw
1 #include "git-compat-util.h"
2 #include "hash.h"
3 #include "path.h"
4 #include "object-file.h"
5 #include "odb.h"
6 #include "odb/source-files.h"
7 #include "hex.h"
8 #include "repository.h"
9 #include "wrapper.h"
10 #include "gettext.h"
11 #include "loose.h"
12 #include "lockfile.h"
13 #include "oidtree.h"
14
15 static const char *loose_object_header = "# loose-object-idx\n";
16
17 static inline int should_use_loose_object_map(struct repository *repo)
18 {
19 return repo->compat_hash_algo && repo->gitdir;
20 }
21
22 void loose_object_map_init(struct loose_object_map **map)
23 {
24 struct loose_object_map *m;
25 m = xmalloc(sizeof(**map));
26 m->to_compat = kh_init_oid_map();
27 m->to_storage = kh_init_oid_map();
28 *map = m;
29 }
30
31 static int insert_oid_pair(kh_oid_map_t *map, const struct object_id *key, const struct object_id *value)
32 {
33 khiter_t pos;
34 int ret;
35 struct object_id *stored;
36
37 pos = kh_put_oid_map(map, *key, &ret);
38
39 /* This item already exists in the map. */
40 if (ret == 0)
41 return 0;
42
43 stored = xmalloc(sizeof(*stored));
44 oidcpy(stored, value);
45 kh_value(map, pos) = stored;
46 return 1;
47 }
48
49 static int insert_loose_map(struct odb_source_loose *loose,
50 const struct object_id *oid,
51 const struct object_id *compat_oid)
52 {
53 struct loose_object_map *map = loose->map;
54 int inserted = 0;
55
56 inserted |= insert_oid_pair(map->to_compat, oid, compat_oid);
57 inserted |= insert_oid_pair(map->to_storage, compat_oid, oid);
58 if (inserted)
59 oidtree_insert(loose->cache, compat_oid, NULL);
60
61 return inserted;
62 }
63
64 static int load_one_loose_object_map(struct repository *repo, struct odb_source_loose *loose)
65 {
66 struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
67 FILE *fp;
68 int ret = -1;
69
70 if (!loose->map)
71 loose_object_map_init(&loose->map);
72 if (!loose->cache) {
73 ALLOC_ARRAY(loose->cache, 1);
74 oidtree_init(loose->cache);
75 }
76
77 insert_loose_map(loose, repo->hash_algo->empty_tree, repo->compat_hash_algo->empty_tree);
78 insert_loose_map(loose, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob);
79 insert_loose_map(loose, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid);
80
81 repo_common_path_replace(repo, &path, "objects/loose-object-idx");
82 fp = fopen(path.buf, "rb");
83 if (!fp) {
84 strbuf_release(&path);
85 return 0;
86 }
87
88 if (strbuf_getwholeline(&buf, fp, '\n') || strcmp(buf.buf, loose_object_header))
89 goto err;
90 while (!strbuf_getline_lf(&buf, fp)) {
91 const char *p;
92 struct object_id oid, compat_oid;
93 if (parse_oid_hex_algop(buf.buf, &oid, &p, repo->hash_algo) ||
94 *p++ != ' ' ||
95 parse_oid_hex_algop(p, &compat_oid, &p, repo->compat_hash_algo) ||
96 p != buf.buf + buf.len)
97 goto err;
98 insert_loose_map(loose, &oid, &compat_oid);
99 }
100
101 ret = ferror(fp) ? -1 : 0;
102 err:
103 fclose(fp);
104 strbuf_release(&buf);
105 strbuf_release(&path);
106 return ret;
107 }
108
109 int repo_read_loose_object_map(struct repository *repo)
110 {
111 struct odb_source *source;
112
113 if (!should_use_loose_object_map(repo))
114 return 0;
115
116 odb_prepare_alternates(repo->objects);
117
118 for (source = repo->objects->sources; source; source = source->next) {
119 struct odb_source_files *files = odb_source_files_downcast(source);
120 if (load_one_loose_object_map(repo, files->loose) < 0) {
121 return -1;
122 }
123 }
124 return 0;
125 }
126
127 int repo_write_loose_object_map(struct repository *repo)
128 {
129 struct odb_source_files *files = odb_source_files_downcast(repo->objects->sources);
130 kh_oid_map_t *map = files->loose->map->to_compat;
131 struct lock_file lock;
132 int fd;
133 khiter_t iter;
134 struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
135
136 if (!should_use_loose_object_map(repo))
137 return 0;
138
139 repo_common_path_replace(repo, &path, "objects/loose-object-idx");
140 fd = repo_hold_lock_file_for_update_timeout(repo, &lock, path.buf,
141 LOCK_DIE_ON_ERROR, -1);
142 iter = kh_begin(map);
143 if (write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
144 goto errout;
145
146 for (; iter != kh_end(map); iter++) {
147 if (kh_exist(map, iter)) {
148 if (oideq(&kh_key(map, iter), repo->hash_algo->empty_tree) ||
149 oideq(&kh_key(map, iter), repo->hash_algo->empty_blob))
150 continue;
151 strbuf_addf(&buf, "%s %s\n", oid_to_hex(&kh_key(map, iter)), oid_to_hex(kh_value(map, iter)));
152 if (write_in_full(fd, buf.buf, buf.len) < 0)
153 goto errout;
154 strbuf_reset(&buf);
155 }
156 }
157 strbuf_release(&buf);
158 if (commit_lock_file(&lock) < 0) {
159 error_errno(_("could not write loose object index %s"), path.buf);
160 strbuf_release(&path);
161 return -1;
162 }
163 strbuf_release(&path);
164 return 0;
165 errout:
166 rollback_lock_file(&lock);
167 strbuf_release(&buf);
168 error_errno(_("failed to write loose object index %s"), path.buf);
169 strbuf_release(&path);
170 return -1;
171 }
172
173 static int write_one_object(struct odb_source_loose *loose,
174 const struct object_id *oid,
175 const struct object_id *compat_oid)
176 {
177 struct lock_file lock;
178 int fd;
179 struct stat st;
180 struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
181
182 strbuf_addf(&path, "%s/loose-object-idx", loose->base.path);
183 repo_hold_lock_file_for_update_timeout(loose->base.odb->repo, &lock,
184 path.buf, LOCK_DIE_ON_ERROR, -1);
185
186 fd = open(path.buf, O_WRONLY | O_CREAT | O_APPEND, 0666);
187 if (fd < 0)
188 goto errout;
189 if (fstat(fd, &st) < 0)
190 goto errout;
191 if (!st.st_size && write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
192 goto errout;
193
194 strbuf_addf(&buf, "%s %s\n", oid_to_hex(oid), oid_to_hex(compat_oid));
195 if (write_in_full(fd, buf.buf, buf.len) < 0)
196 goto errout;
197 if (close(fd))
198 goto errout;
199 adjust_shared_perm(loose->base.odb->repo, path.buf);
200 rollback_lock_file(&lock);
201 strbuf_release(&buf);
202 strbuf_release(&path);
203 return 0;
204 errout:
205 error_errno(_("failed to write loose object index %s"), path.buf);
206 if (fd >= 0)
207 close(fd);
208 rollback_lock_file(&lock);
209 strbuf_release(&buf);
210 strbuf_release(&path);
211 return -1;
212 }
213
214 int repo_add_loose_object_map(struct odb_source_loose *loose,
215 const struct object_id *oid,
216 const struct object_id *compat_oid)
217 {
218 int inserted = 0;
219
220 if (!should_use_loose_object_map(loose->base.odb->repo))
221 return 0;
222
223 inserted = insert_loose_map(loose, oid, compat_oid);
224 if (inserted)
225 return write_one_object(loose, oid, compat_oid);
226 return 0;
227 }
228
229 int repo_loose_object_map_oid(struct repository *repo,
230 const struct object_id *src,
231 const struct git_hash_algo *to,
232 struct object_id *dest)
233 {
234 struct odb_source *source;
235 kh_oid_map_t *map;
236 khiter_t pos;
237
238 for (source = repo->objects->sources; source; source = source->next) {
239 struct odb_source_files *files = odb_source_files_downcast(source);
240 struct loose_object_map *loose_map = files->loose->map;
241 if (!loose_map)
242 continue;
243 map = (to == repo->compat_hash_algo) ?
244 loose_map->to_compat :
245 loose_map->to_storage;
246 pos = kh_get_oid_map(map, *src);
247 if (pos < kh_end(map)) {
248 oidcpy(dest, kh_value(map, pos));
249 return 0;
250 }
251 }
252 return -1;
253 }
254
255 void loose_object_map_clear(struct loose_object_map **map)
256 {
257 struct loose_object_map *m = *map;
258 struct object_id *oid;
259
260 if (!m)
261 return;
262
263 kh_foreach_value(m->to_compat, oid, free(oid));
264 kh_foreach_value(m->to_storage, oid, free(oid));
265 kh_destroy_oid_map(m->to_compat);
266 kh_destroy_oid_map(m->to_storage);
267 free(m);
268 *map = NULL;
269 }