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 int loose_object_map_load(struct odb_source_loose *loose)
65 {
66 struct repository *repo = loose->base.odb->repo;
67 struct strbuf buf = STRBUF_INIT;
68 char *path;
69 FILE *fp;
70 int ret = -1;
71
72 if (!should_use_loose_object_map(repo))
73 return 0;
74
75 if (!loose->map)
76 loose_object_map_init(&loose->map);
77 if (!loose->cache) {
78 ALLOC_ARRAY(loose->cache, 1);
79 oidtree_init(loose->cache);
80 }
81
82 insert_loose_map(loose, repo->hash_algo->empty_tree, repo->compat_hash_algo->empty_tree);
83 insert_loose_map(loose, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob);
84 insert_loose_map(loose, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid);
85
86 path = xstrfmt("%s/loose-object-idx", loose->base.path);
87 fp = fopen(path, "rb");
88 if (!fp) {
89 free(path);
90 return 0;
91 }
92
93 if (strbuf_getwholeline(&buf, fp, '\n') || strcmp(buf.buf, loose_object_header))
94 goto err;
95 while (!strbuf_getline_lf(&buf, fp)) {
96 const char *p;
97 struct object_id oid, compat_oid;
98 if (parse_oid_hex_algop(buf.buf, &oid, &p, repo->hash_algo) ||
99 *p++ != ' ' ||
100 parse_oid_hex_algop(p, &compat_oid, &p, repo->compat_hash_algo) ||
101 p != buf.buf + buf.len)
102 goto err;
103 insert_loose_map(loose, &oid, &compat_oid);
104 }
105
106 ret = ferror(fp) ? -1 : 0;
107 err:
108 fclose(fp);
109 strbuf_release(&buf);
110 free(path);
111 return ret;
112 }
113
114 int repo_read_loose_object_map(struct repository *repo)
115 {
116 struct odb_source *source;
117
118 odb_prepare_alternates(repo->objects);
119 for (source = repo->objects->sources; source; source = source->next) {
120 struct odb_source_files *files = odb_source_files_downcast(source);
121 if (loose_object_map_load(files->loose) < 0)
122 return -1;
123 }
124
125 return 0;
126 }
127
128 int repo_write_loose_object_map(struct repository *repo)
129 {
130 struct odb_source_files *files = odb_source_files_downcast(repo->objects->sources);
131 kh_oid_map_t *map = files->loose->map->to_compat;
132 struct lock_file lock;
133 int fd;
134 khiter_t iter;
135 struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
136
137 if (!should_use_loose_object_map(repo))
138 return 0;
139
140 repo_common_path_replace(repo, &path, "objects/loose-object-idx");
141 fd = repo_hold_lock_file_for_update_timeout(repo, &lock, path.buf,
142 LOCK_DIE_ON_ERROR, -1);
143 iter = kh_begin(map);
144 if (write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
145 goto errout;
146
147 for (; iter != kh_end(map); iter++) {
148 if (kh_exist(map, iter)) {
149 if (oideq(&kh_key(map, iter), repo->hash_algo->empty_tree) ||
150 oideq(&kh_key(map, iter), repo->hash_algo->empty_blob))
151 continue;
152 strbuf_addf(&buf, "%s %s\n", oid_to_hex(&kh_key(map, iter)), oid_to_hex(kh_value(map, iter)));
153 if (write_in_full(fd, buf.buf, buf.len) < 0)
154 goto errout;
155 strbuf_reset(&buf);
156 }
157 }
158 strbuf_release(&buf);
159 if (commit_lock_file(&lock) < 0) {
160 error_errno(_("could not write loose object index %s"), path.buf);
161 strbuf_release(&path);
162 return -1;
163 }
164 strbuf_release(&path);
165 return 0;
166 errout:
167 rollback_lock_file(&lock);
168 strbuf_release(&buf);
169 error_errno(_("failed to write loose object index %s"), path.buf);
170 strbuf_release(&path);
171 return -1;
172 }
173
174 static int write_one_object(struct odb_source_loose *loose,
175 const struct object_id *oid,
176 const struct object_id *compat_oid)
177 {
178 struct lock_file lock;
179 int fd;
180 struct stat st;
181 struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
182
183 strbuf_addf(&path, "%s/loose-object-idx", loose->base.path);
184 repo_hold_lock_file_for_update_timeout(loose->base.odb->repo, &lock,
185 path.buf, LOCK_DIE_ON_ERROR, -1);
186
187 fd = open(path.buf, O_WRONLY | O_CREAT | O_APPEND, 0666);
188 if (fd < 0)
189 goto errout;
190 if (fstat(fd, &st) < 0)
191 goto errout;
192 if (!st.st_size && write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
193 goto errout;
194
195 strbuf_addf(&buf, "%s %s\n", oid_to_hex(oid), oid_to_hex(compat_oid));
196 if (write_in_full(fd, buf.buf, buf.len) < 0)
197 goto errout;
198 if (close(fd))
199 goto errout;
200 adjust_shared_perm(loose->base.odb->repo, path.buf);
201 rollback_lock_file(&lock);
202 strbuf_release(&buf);
203 strbuf_release(&path);
204 return 0;
205 errout:
206 error_errno(_("failed to write loose object index %s"), path.buf);
207 if (fd >= 0)
208 close(fd);
209 rollback_lock_file(&lock);
210 strbuf_release(&buf);
211 strbuf_release(&path);
212 return -1;
213 }
214
215 int repo_add_loose_object_map(struct odb_source_loose *loose,
216 const struct object_id *oid,
217 const struct object_id *compat_oid)
218 {
219 int inserted = 0;
220
221 if (!should_use_loose_object_map(loose->base.odb->repo))
222 return 0;
223
224 inserted = insert_loose_map(loose, oid, compat_oid);
225 if (inserted)
226 return write_one_object(loose, oid, compat_oid);
227 return 0;
228 }
229
230 int repo_loose_object_map_oid(struct repository *repo,
231 const struct object_id *src,
232 const struct git_hash_algo *to,
233 struct object_id *dest)
234 {
235 struct odb_source *source;
236 kh_oid_map_t *map;
237 khiter_t pos;
238
239 for (source = repo->objects->sources; source; source = source->next) {
240 struct odb_source_files *files = odb_source_files_downcast(source);
241 struct loose_object_map *loose_map = files->loose->map;
242 if (!loose_map)
243 continue;
244 map = (to == repo->compat_hash_algo) ?
245 loose_map->to_compat :
246 loose_map->to_storage;
247 pos = kh_get_oid_map(map, *src);
248 if (pos < kh_end(map)) {
249 oidcpy(dest, kh_value(map, pos));
250 return 0;
251 }
252 }
253 return -1;
254 }
255
256 void loose_object_map_clear(struct loose_object_map **map)
257 {
258 struct loose_object_map *m = *map;
259 struct object_id *oid;
260
261 if (!m)
262 return;
263
264 kh_foreach_value(m->to_compat, oid, free(oid));
265 kh_foreach_value(m->to_storage, oid, free(oid));
266 kh_destroy_oid_map(m->to_compat);
267 kh_destroy_oid_map(m->to_storage);
268 free(m);
269 *map = NULL;
270 }