4 * Copyright (C) 2005 Linus Torvalds
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.
11 #include "git-compat-util.h"
12 #include "csum-file.h"
17 static void verify_buffer_or_die(struct hashfile
*f
,
21 ssize_t ret
= read_in_full(f
->check_fd
, f
->check_buffer
, count
);
24 die_errno("%s: sha1 file read error", f
->name
);
25 if ((size_t)ret
!= count
)
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
);
31 static void flush(struct hashfile
*f
, const void *buf
, unsigned int count
)
33 if (0 <= f
->check_fd
&& count
)
34 verify_buffer_or_die(f
, buf
, count
);
36 if (write_in_full(f
->fd
, buf
, count
) < 0) {
38 die("sha1 file '%s' write error. Out of diskspace", f
->name
);
39 die_errno("sha1 file '%s' write error", f
->name
);
43 display_throughput(f
->tp
, f
->total
);
46 void hashflush(struct hashfile
*f
)
48 unsigned offset
= f
->offset
;
52 git_hash_update(&f
->ctx
, f
->buffer
, offset
);
53 flush(f
, f
->buffer
, offset
);
58 void free_hashfile(struct hashfile
*f
)
61 free(f
->check_buffer
);
65 int finalize_hashfile(struct hashfile
*f
, unsigned char *result
,
66 enum fsync_component component
, unsigned int flags
)
73 hashclr(f
->buffer
, f
->algop
);
75 git_hash_final(f
->buffer
, &f
->ctx
);
78 hashcpy(result
, f
->buffer
, f
->algop
);
79 if (flags
& CSUM_HASH_IN_STREAM
)
80 flush(f
, f
->buffer
, f
->algop
->rawsz
);
81 if (flags
& CSUM_FSYNC
)
82 fsync_component_or_die(component
, f
->fd
, f
->name
);
83 if (flags
& CSUM_CLOSE
) {
85 die_errno("%s: sha1 file error on close", f
->name
);
89 if (0 <= f
->check_fd
) {
91 int cnt
= read_in_full(f
->check_fd
, &discard
, 1);
93 die_errno("%s: error when reading the tail of sha1 file",
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
);
104 void discard_hashfile(struct hashfile
*f
)
106 if (0 <= f
->check_fd
)
113 void hashwrite(struct hashfile
*f
, const void *buf
, unsigned int count
)
116 unsigned left
= f
->buffer_len
- f
->offset
;
117 unsigned nr
= count
> left
? left
: count
;
120 f
->crc32
= crc32(f
->crc32
, buf
, nr
);
122 if (nr
== f
->buffer_len
) {
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.
130 git_hash_update(&f
->ctx
, buf
, nr
);
134 * Copy to the hashfile's buffer, flushing only
137 memcpy(f
->buffer
+ f
->offset
, buf
, nr
);
145 buf
= (char *) buf
+ nr
;
149 struct hashfile
*hashfd_check(const struct git_hash_algo
*algop
,
155 sink
= xopen("/dev/null", O_WRONLY
);
156 check
= xopen(name
, O_RDONLY
);
157 f
= hashfd(algop
, sink
, name
);
159 f
->check_buffer
= xmalloc(f
->buffer_len
);
164 static struct hashfile
*hashfd_internal(const struct git_hash_algo
*algop
,
165 int fd
, const char *name
,
169 struct hashfile
*f
= xmalloc(sizeof(*f
));
179 f
->algop
= unsafe_hash_algo(algop
);
180 f
->algop
->init_fn(&f
->ctx
);
182 f
->buffer_len
= buffer_len
;
183 f
->buffer
= xmalloc(buffer_len
);
184 f
->check_buffer
= NULL
;
189 struct hashfile
*hashfd(const struct git_hash_algo
*algop
,
190 int fd
, const char *name
)
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.
197 return hashfd_internal(algop
, fd
, name
, NULL
, 128 * 1024);
200 struct hashfile
*hashfd_throughput(const struct git_hash_algo
*algop
,
201 int fd
, const char *name
, struct progress
*tp
)
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
209 return hashfd_internal(algop
, fd
, name
, tp
, 8 * 1024);
212 void hashfile_checkpoint_init(struct hashfile
*f
,
213 struct hashfile_checkpoint
*checkpoint
)
215 memset(checkpoint
, 0, sizeof(*checkpoint
));
216 f
->algop
->init_fn(&checkpoint
->ctx
);
219 void hashfile_checkpoint(struct hashfile
*f
, struct hashfile_checkpoint
*checkpoint
)
222 checkpoint
->offset
= f
->total
;
223 git_hash_clone(&checkpoint
->ctx
, &f
->ctx
);
226 int hashfile_truncate(struct hashfile
*f
, struct hashfile_checkpoint
*checkpoint
)
228 off_t offset
= checkpoint
->offset
;
230 if (ftruncate(f
->fd
, offset
) ||
231 lseek(f
->fd
, offset
, SEEK_SET
) != offset
)
234 git_hash_clone(&f
->ctx
, &checkpoint
->ctx
);
235 f
->offset
= 0; /* hashflush() was called in checkpoint */
239 void crc32_begin(struct hashfile
*f
)
241 f
->crc32
= crc32(0, NULL
, 0);
245 uint32_t crc32_end(struct hashfile
*f
)
251 int hashfile_checksum_valid(const struct git_hash_algo
*algop
,
252 const unsigned char *data
, size_t total_len
)
254 unsigned char got
[GIT_MAX_RAWSZ
];
255 struct git_hash_ctx ctx
;
256 size_t data_len
= total_len
- algop
->rawsz
;
258 algop
= unsafe_hash_algo(algop
);
260 if (total_len
< algop
->rawsz
)
261 return 0; /* say "too short"? */
263 algop
->init_fn(&ctx
);
264 git_hash_update(&ctx
, data
, data_len
);
265 git_hash_final(got
, &ctx
);
267 return hasheq(got
, data
+ data_len
, algop
);