]> git.ipfire.org Git - thirdparty/git.git/blame - csum-file.c
The sixth batch
[thirdparty/git.git] / csum-file.c
CommitLineData
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
17static 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 31static 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 46void 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 58void free_hashfile(struct hashfile *f)
2ca245f8
DS
59{
60 free(f->buffer);
61 free(f->check_buffer);
62 free(f);
63}
64
020406ea
NS
65int 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
104void 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 113void 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
149struct 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
164static 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
189struct 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
200struct 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
212void 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 219void 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 226int 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 239void 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 245uint32_t crc32_end(struct hashfile *f)
78d1e84f
NP
246{
247 f->do_crc = 0;
248 return f->crc32;
249}
f9221e2c 250
228457c9
PS
251int 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}