git/csum-file.c
Nicolas Pitre 838cd34664 fix pread()'s short read in index-pack
Since v1.6.0.2~13^2~ the completion of a thin pack uses sha1write() for
its ability to compute a SHA1 on the written data.  This also provides
data buffering which, along with commit 92392b4a45, will confuse pread()
whenever an appended object is 1) freed due to memory pressure because
of the depth-first delta processing, and 2) needed again because it has
many delta children, and 3) its data is still buffered by sha1write().

Let's fix the issue by simply forcing cached data out when such an
object is written so it can be pread()'d at leisure.

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
2008-10-10 07:09:30 -07:00

121 lines
2.4 KiB
C

/*
* csum-file.c
*
* Copyright (C) 2005 Linus Torvalds
*
* Simple file write infrastructure for writing SHA1-summed
* files. Useful when you write a file that you want to be
* able to verify hasn't been messed with afterwards.
*/
#include "cache.h"
#include "progress.h"
#include "csum-file.h"
static void flush(struct sha1file *f, unsigned int count)
{
void *buf = f->buffer;
for (;;) {
int ret = xwrite(f->fd, buf, count);
if (ret > 0) {
f->total += ret;
display_throughput(f->tp, f->total);
buf = (char *) buf + ret;
count -= ret;
if (count)
continue;
return;
}
if (!ret)
die("sha1 file '%s' write error. Out of diskspace", f->name);
die("sha1 file '%s' write error (%s)", f->name, strerror(errno));
}
}
void sha1flush(struct sha1file *f)
{
unsigned offset = f->offset;
if (offset) {
SHA1_Update(&f->ctx, f->buffer, offset);
flush(f, offset);
f->offset = 0;
}
}
int sha1close(struct sha1file *f, unsigned char *result, unsigned int flags)
{
int fd;
sha1flush(f);
SHA1_Final(f->buffer, &f->ctx);
if (result)
hashcpy(result, f->buffer);
if (flags & (CSUM_CLOSE | CSUM_FSYNC)) {
/* write checksum and close fd */
flush(f, 20);
if (flags & CSUM_FSYNC)
fsync_or_die(f->fd, f->name);
if (close(f->fd))
die("%s: sha1 file error on close (%s)",
f->name, strerror(errno));
fd = 0;
} else
fd = f->fd;
free(f);
return fd;
}
int sha1write(struct sha1file *f, void *buf, unsigned int count)
{
if (f->do_crc)
f->crc32 = crc32(f->crc32, buf, count);
while (count) {
unsigned offset = f->offset;
unsigned left = sizeof(f->buffer) - offset;
unsigned nr = count > left ? left : count;
memcpy(f->buffer + offset, buf, nr);
count -= nr;
offset += nr;
buf = (char *) buf + nr;
left -= nr;
if (!left) {
SHA1_Update(&f->ctx, f->buffer, offset);
flush(f, offset);
offset = 0;
}
f->offset = offset;
}
return 0;
}
struct sha1file *sha1fd(int fd, const char *name)
{
return sha1fd_throughput(fd, name, NULL);
}
struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp)
{
struct sha1file *f = xmalloc(sizeof(*f));
f->fd = fd;
f->offset = 0;
f->total = 0;
f->tp = tp;
f->name = name;
f->do_crc = 0;
SHA1_Init(&f->ctx);
return f;
}
void crc32_begin(struct sha1file *f)
{
f->crc32 = crc32(0, Z_NULL, 0);
f->do_crc = 1;
}
uint32_t crc32_end(struct sha1file *f)
{
f->do_crc = 0;
return f->crc32;
}