]>
Commit | Line | Data |
---|---|---|
c38138cd LT |
1 | /* |
2 | * csum-file.c | |
3 | * | |
4 | * Copyright (C) 2005 Linus Torvalds | |
5 | * | |
6 | * Simple file write infrastructure for writing SHA1-summed | |
7 | * files. Useful when you write a file that you want to be | |
8 | * able to verify hasn't been messed with afterwards. | |
9 | */ | |
e7da9385 | 10 | |
98750588 | 11 | #include "git-compat-util.h" |
c38138cd | 12 | #include "csum-file.h" |
41f1a843 | 13 | #include "git-zlib.h" |
d1cbe1e6 | 14 | #include "hash.h" |
41f1a843 | 15 | #include "progress.h" |
c38138cd | 16 | |
2ca245f8 DS |
17 | static void verify_buffer_or_die(struct hashfile *f, |
18 | const void *buf, | |
19 | unsigned int count) | |
20 | { | |
21 | ssize_t ret = read_in_full(f->check_fd, f->check_buffer, count); | |
22 | ||
23 | if (ret < 0) | |
24 | die_errno("%s: sha1 file read error", f->name); | |
ba8f6018 | 25 | if ((size_t)ret != count) |
2ca245f8 DS |
26 | die("%s: sha1 file truncated", f->name); |
27 | if (memcmp(buf, f->check_buffer, count)) | |
28 | die("sha1 file '%s' validation error", f->name); | |
29 | } | |
30 | ||
98a3beab | 31 | static void flush(struct hashfile *f, const void *buf, unsigned int count) |
c38138cd | 32 | { |
2ca245f8 DS |
33 | if (0 <= f->check_fd && count) |
34 | verify_buffer_or_die(f, buf, count); | |
e337a04d | 35 | |
68142e11 DS |
36 | if (write_in_full(f->fd, buf, count) < 0) { |
37 | if (errno == ENOSPC) | |
e1808845 | 38 | die("sha1 file '%s' write error. Out of diskspace", f->name); |
d824cbba | 39 | die_errno("sha1 file '%s' write error", f->name); |
c38138cd | 40 | } |
68142e11 DS |
41 | |
42 | f->total += count; | |
43 | display_throughput(f->tp, f->total); | |
c38138cd LT |
44 | } |
45 | ||
98a3beab | 46 | void hashflush(struct hashfile *f) |
c38138cd LT |
47 | { |
48 | unsigned offset = f->offset; | |
4c81b03e | 49 | |
c38138cd | 50 | if (offset) { |
1687150b | 51 | if (!f->skip_hash) |
0578f1e6 | 52 | git_hash_update(&f->ctx, f->buffer, offset); |
e782e12f | 53 | flush(f, f->buffer, offset); |
f0215369 | 54 | f->offset = 0; |
c38138cd | 55 | } |
838cd346 NP |
56 | } |
57 | ||
c81dcf63 | 58 | void free_hashfile(struct hashfile *f) |
2ca245f8 DS |
59 | { |
60 | free(f->buffer); | |
61 | free(f->check_buffer); | |
62 | free(f); | |
63 | } | |
64 | ||
020406ea NS |
65 | int finalize_hashfile(struct hashfile *f, unsigned char *result, |
66 | enum fsync_component component, unsigned int flags) | |
838cd346 NP |
67 | { |
68 | int fd; | |
69 | ||
98a3beab | 70 | hashflush(f); |
1687150b DS |
71 | |
72 | if (f->skip_hash) | |
48524fac | 73 | hashclr(f->buffer, f->algop); |
1687150b | 74 | else |
0578f1e6 | 75 | git_hash_final(f->buffer, &f->ctx); |
1687150b | 76 | |
ac0463ed | 77 | if (result) |
48524fac | 78 | hashcpy(result, f->buffer, f->algop); |
cfe83216 | 79 | if (flags & CSUM_HASH_IN_STREAM) |
48524fac | 80 | flush(f, f->buffer, f->algop->rawsz); |
cfe83216 | 81 | if (flags & CSUM_FSYNC) |
020406ea | 82 | fsync_component_or_die(component, f->fd, f->name); |
cfe83216 | 83 | if (flags & CSUM_CLOSE) { |
7ba502c4 | 84 | if (close(f->fd)) |
d824cbba | 85 | die_errno("%s: sha1 file error on close", f->name); |
7ba502c4 NP |
86 | fd = 0; |
87 | } else | |
88 | fd = f->fd; | |
e337a04d JH |
89 | if (0 <= f->check_fd) { |
90 | char discard; | |
91 | int cnt = read_in_full(f->check_fd, &discard, 1); | |
92 | if (cnt < 0) | |
93 | die_errno("%s: error when reading the tail of sha1 file", | |
94 | f->name); | |
95 | if (cnt) | |
96 | die("%s: sha1 file has trailing garbage", f->name); | |
97 | if (close(f->check_fd)) | |
98 | die_errno("%s: sha1 file error on close", f->name); | |
99 | } | |
2ca245f8 | 100 | free_hashfile(f); |
7ba502c4 | 101 | return fd; |
c38138cd LT |
102 | } |
103 | ||
c3d034df JH |
104 | void discard_hashfile(struct hashfile *f) |
105 | { | |
106 | if (0 <= f->check_fd) | |
107 | close(f->check_fd); | |
108 | if (0 <= f->fd) | |
109 | close(f->fd); | |
110 | free_hashfile(f); | |
111 | } | |
112 | ||
98a3beab | 113 | void hashwrite(struct hashfile *f, const void *buf, unsigned int count) |
c38138cd LT |
114 | { |
115 | while (count) { | |
2ca245f8 | 116 | unsigned left = f->buffer_len - f->offset; |
c38138cd | 117 | unsigned nr = count > left ? left : count; |
a8032d12 NP |
118 | |
119 | if (f->do_crc) | |
120 | f->crc32 = crc32(f->crc32, buf, nr); | |
121 | ||
2ca245f8 | 122 | if (nr == f->buffer_len) { |
ddaf1f62 DS |
123 | /* |
124 | * Flush a full batch worth of data directly | |
125 | * from the input, skipping the memcpy() to | |
126 | * the hashfile's buffer. In this block, | |
127 | * f->offset is necessarily zero. | |
128 | */ | |
1687150b | 129 | if (!f->skip_hash) |
0578f1e6 | 130 | git_hash_update(&f->ctx, buf, nr); |
ddaf1f62 | 131 | flush(f, buf, nr); |
a8032d12 | 132 | } else { |
ddaf1f62 DS |
133 | /* |
134 | * Copy to the hashfile's buffer, flushing only | |
135 | * if it became full. | |
136 | */ | |
137 | memcpy(f->buffer + f->offset, buf, nr); | |
138 | f->offset += nr; | |
139 | left -= nr; | |
140 | if (!left) | |
141 | hashflush(f); | |
a8032d12 | 142 | } |
c38138cd | 143 | |
c38138cd | 144 | count -= nr; |
1d7f171c | 145 | buf = (char *) buf + nr; |
c38138cd | 146 | } |
c38138cd LT |
147 | } |
148 | ||
228457c9 PS |
149 | struct hashfile *hashfd_check(const struct git_hash_algo *algop, |
150 | const char *name) | |
e337a04d JH |
151 | { |
152 | int sink, check; | |
98a3beab | 153 | struct hashfile *f; |
e337a04d | 154 | |
66e905b7 RS |
155 | sink = xopen("/dev/null", O_WRONLY); |
156 | check = xopen(name, O_RDONLY); | |
228457c9 | 157 | f = hashfd(algop, sink, name); |
e337a04d | 158 | f->check_fd = check; |
2ca245f8 DS |
159 | f->check_buffer = xmalloc(f->buffer_len); |
160 | ||
e337a04d JH |
161 | return f; |
162 | } | |
163 | ||
228457c9 PS |
164 | static struct hashfile *hashfd_internal(const struct git_hash_algo *algop, |
165 | int fd, const char *name, | |
2ca245f8 DS |
166 | struct progress *tp, |
167 | size_t buffer_len) | |
4397f014 | 168 | { |
98a3beab | 169 | struct hashfile *f = xmalloc(sizeof(*f)); |
4397f014 | 170 | f->fd = fd; |
e337a04d | 171 | f->check_fd = -1; |
4397f014 | 172 | f->offset = 0; |
218558af | 173 | f->total = 0; |
2a128d63 | 174 | f->tp = tp; |
ec640ed1 | 175 | f->name = name; |
78d1e84f | 176 | f->do_crc = 0; |
1687150b | 177 | f->skip_hash = 0; |
48524fac | 178 | |
228457c9 | 179 | f->algop = unsafe_hash_algo(algop); |
f0c266af | 180 | f->algop->init_fn(&f->ctx); |
2ca245f8 DS |
181 | |
182 | f->buffer_len = buffer_len; | |
183 | f->buffer = xmalloc(buffer_len); | |
184 | f->check_buffer = NULL; | |
185 | ||
4397f014 LT |
186 | return f; |
187 | } | |
188 | ||
228457c9 PS |
189 | struct hashfile *hashfd(const struct git_hash_algo *algop, |
190 | int fd, const char *name) | |
2ca245f8 DS |
191 | { |
192 | /* | |
193 | * Since we are not going to use a progress meter to | |
194 | * measure the rate of data passing through this hashfile, | |
195 | * use a larger buffer size to reduce fsync() calls. | |
196 | */ | |
228457c9 | 197 | return hashfd_internal(algop, fd, name, NULL, 128 * 1024); |
2ca245f8 DS |
198 | } |
199 | ||
228457c9 PS |
200 | struct hashfile *hashfd_throughput(const struct git_hash_algo *algop, |
201 | int fd, const char *name, struct progress *tp) | |
2ca245f8 DS |
202 | { |
203 | /* | |
204 | * Since we are expecting to report progress of the | |
205 | * write into this hashfile, use a smaller buffer | |
206 | * size so the progress indicators arrive at a more | |
207 | * frequent rate. | |
208 | */ | |
228457c9 | 209 | return hashfd_internal(algop, fd, name, tp, 8 * 1024); |
2ca245f8 DS |
210 | } |
211 | ||
a8dd3821 TB |
212 | void hashfile_checkpoint_init(struct hashfile *f, |
213 | struct hashfile_checkpoint *checkpoint) | |
214 | { | |
215 | memset(checkpoint, 0, sizeof(*checkpoint)); | |
216 | f->algop->init_fn(&checkpoint->ctx); | |
217 | } | |
218 | ||
98a3beab | 219 | void hashfile_checkpoint(struct hashfile *f, struct hashfile_checkpoint *checkpoint) |
6c526148 | 220 | { |
98a3beab | 221 | hashflush(f); |
6c526148 | 222 | checkpoint->offset = f->total; |
0578f1e6 | 223 | git_hash_clone(&checkpoint->ctx, &f->ctx); |
6c526148 JH |
224 | } |
225 | ||
98a3beab | 226 | int hashfile_truncate(struct hashfile *f, struct hashfile_checkpoint *checkpoint) |
6c526148 JH |
227 | { |
228 | off_t offset = checkpoint->offset; | |
229 | ||
230 | if (ftruncate(f->fd, offset) || | |
231 | lseek(f->fd, offset, SEEK_SET) != offset) | |
232 | return -1; | |
233 | f->total = offset; | |
0578f1e6 | 234 | git_hash_clone(&f->ctx, &checkpoint->ctx); |
98a3beab | 235 | f->offset = 0; /* hashflush() was called in checkpoint */ |
6c526148 JH |
236 | return 0; |
237 | } | |
238 | ||
98a3beab | 239 | void crc32_begin(struct hashfile *f) |
78d1e84f | 240 | { |
1e4cd68c | 241 | f->crc32 = crc32(0, NULL, 0); |
78d1e84f NP |
242 | f->do_crc = 1; |
243 | } | |
c38138cd | 244 | |
98a3beab | 245 | uint32_t crc32_end(struct hashfile *f) |
78d1e84f NP |
246 | { |
247 | f->do_crc = 0; | |
248 | return f->crc32; | |
249 | } | |
f9221e2c | 250 | |
228457c9 PS |
251 | int hashfile_checksum_valid(const struct git_hash_algo *algop, |
252 | const unsigned char *data, size_t total_len) | |
f9221e2c TB |
253 | { |
254 | unsigned char got[GIT_MAX_RAWSZ]; | |
7346e340 | 255 | struct git_hash_ctx ctx; |
5fcc6833 | 256 | size_t data_len = total_len - algop->rawsz; |
f9221e2c | 257 | |
228457c9 PS |
258 | algop = unsafe_hash_algo(algop); |
259 | ||
5fcc6833 | 260 | if (total_len < algop->rawsz) |
f9221e2c TB |
261 | return 0; /* say "too short"? */ |
262 | ||
f0c266af | 263 | algop->init_fn(&ctx); |
0578f1e6 PS |
264 | git_hash_update(&ctx, data, data_len); |
265 | git_hash_final(got, &ctx); | |
f9221e2c | 266 | |
5fcc6833 | 267 | return hasheq(got, data + data_len, algop); |
f9221e2c | 268 | } |