git

Форк
0
/
csum-file.c 
253 строки · 5.8 Кб
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
 */
10

11
#define USE_THE_REPOSITORY_VARIABLE
12

13
#include "git-compat-util.h"
14
#include "progress.h"
15
#include "csum-file.h"
16
#include "hash.h"
17

18
static void verify_buffer_or_die(struct hashfile *f,
19
				 const void *buf,
20
				 unsigned int count)
21
{
22
	ssize_t ret = read_in_full(f->check_fd, f->check_buffer, count);
23

24
	if (ret < 0)
25
		die_errno("%s: sha1 file read error", f->name);
26
	if (ret != count)
27
		die("%s: sha1 file truncated", f->name);
28
	if (memcmp(buf, f->check_buffer, count))
29
		die("sha1 file '%s' validation error", f->name);
30
}
31

32
static void flush(struct hashfile *f, const void *buf, unsigned int count)
33
{
34
	if (0 <= f->check_fd && count)
35
		verify_buffer_or_die(f, buf, count);
36

37
	if (write_in_full(f->fd, buf, count) < 0) {
38
		if (errno == ENOSPC)
39
			die("sha1 file '%s' write error. Out of diskspace", f->name);
40
		die_errno("sha1 file '%s' write error", f->name);
41
	}
42

43
	f->total += count;
44
	display_throughput(f->tp, f->total);
45
}
46

47
void hashflush(struct hashfile *f)
48
{
49
	unsigned offset = f->offset;
50

51
	if (offset) {
52
		if (!f->skip_hash)
53
			the_hash_algo->update_fn(&f->ctx, f->buffer, offset);
54
		flush(f, f->buffer, offset);
55
		f->offset = 0;
56
	}
57
}
58

59
void free_hashfile(struct hashfile *f)
60
{
61
	free(f->buffer);
62
	free(f->check_buffer);
63
	free(f);
64
}
65

66
int finalize_hashfile(struct hashfile *f, unsigned char *result,
67
		      enum fsync_component component, unsigned int flags)
68
{
69
	int fd;
70

71
	hashflush(f);
72

73
	if (f->skip_hash)
74
		hashclr(f->buffer, the_repository->hash_algo);
75
	else
76
		the_hash_algo->final_fn(f->buffer, &f->ctx);
77

78
	if (result)
79
		hashcpy(result, f->buffer, the_repository->hash_algo);
80
	if (flags & CSUM_HASH_IN_STREAM)
81
		flush(f, f->buffer, the_hash_algo->rawsz);
82
	if (flags & CSUM_FSYNC)
83
		fsync_component_or_die(component, f->fd, f->name);
84
	if (flags & CSUM_CLOSE) {
85
		if (close(f->fd))
86
			die_errno("%s: sha1 file error on close", f->name);
87
		fd = 0;
88
	} else
89
		fd = f->fd;
90
	if (0 <= f->check_fd) {
91
		char discard;
92
		int cnt = read_in_full(f->check_fd, &discard, 1);
93
		if (cnt < 0)
94
			die_errno("%s: error when reading the tail of sha1 file",
95
				  f->name);
96
		if (cnt)
97
			die("%s: sha1 file has trailing garbage", f->name);
98
		if (close(f->check_fd))
99
			die_errno("%s: sha1 file error on close", f->name);
100
	}
101
	free_hashfile(f);
102
	return fd;
103
}
104

105
void discard_hashfile(struct hashfile *f)
106
{
107
	if (0 <= f->check_fd)
108
		close(f->check_fd);
109
	if (0 <= f->fd)
110
		close(f->fd);
111
	free_hashfile(f);
112
}
113

114
void hashwrite(struct hashfile *f, const void *buf, unsigned int count)
115
{
116
	while (count) {
117
		unsigned left = f->buffer_len - f->offset;
118
		unsigned nr = count > left ? left : count;
119

120
		if (f->do_crc)
121
			f->crc32 = crc32(f->crc32, buf, nr);
122

123
		if (nr == f->buffer_len) {
124
			/*
125
			 * Flush a full batch worth of data directly
126
			 * from the input, skipping the memcpy() to
127
			 * the hashfile's buffer. In this block,
128
			 * f->offset is necessarily zero.
129
			 */
130
			if (!f->skip_hash)
131
				the_hash_algo->update_fn(&f->ctx, buf, nr);
132
			flush(f, buf, nr);
133
		} else {
134
			/*
135
			 * Copy to the hashfile's buffer, flushing only
136
			 * if it became full.
137
			 */
138
			memcpy(f->buffer + f->offset, buf, nr);
139
			f->offset += nr;
140
			left -= nr;
141
			if (!left)
142
				hashflush(f);
143
		}
144

145
		count -= nr;
146
		buf = (char *) buf + nr;
147
	}
148
}
149

150
struct hashfile *hashfd_check(const char *name)
151
{
152
	int sink, check;
153
	struct hashfile *f;
154

155
	sink = xopen("/dev/null", O_WRONLY);
156
	check = xopen(name, O_RDONLY);
157
	f = hashfd(sink, name);
158
	f->check_fd = check;
159
	f->check_buffer = xmalloc(f->buffer_len);
160

161
	return f;
162
}
163

164
static struct hashfile *hashfd_internal(int fd, const char *name,
165
					struct progress *tp,
166
					size_t buffer_len)
167
{
168
	struct hashfile *f = xmalloc(sizeof(*f));
169
	f->fd = fd;
170
	f->check_fd = -1;
171
	f->offset = 0;
172
	f->total = 0;
173
	f->tp = tp;
174
	f->name = name;
175
	f->do_crc = 0;
176
	f->skip_hash = 0;
177
	the_hash_algo->init_fn(&f->ctx);
178

179
	f->buffer_len = buffer_len;
180
	f->buffer = xmalloc(buffer_len);
181
	f->check_buffer = NULL;
182

183
	return f;
184
}
185

186
struct hashfile *hashfd(int fd, const char *name)
187
{
188
	/*
189
	 * Since we are not going to use a progress meter to
190
	 * measure the rate of data passing through this hashfile,
191
	 * use a larger buffer size to reduce fsync() calls.
192
	 */
193
	return hashfd_internal(fd, name, NULL, 128 * 1024);
194
}
195

196
struct hashfile *hashfd_throughput(int fd, const char *name, struct progress *tp)
197
{
198
	/*
199
	 * Since we are expecting to report progress of the
200
	 * write into this hashfile, use a smaller buffer
201
	 * size so the progress indicators arrive at a more
202
	 * frequent rate.
203
	 */
204
	return hashfd_internal(fd, name, tp, 8 * 1024);
205
}
206

207
void hashfile_checkpoint(struct hashfile *f, struct hashfile_checkpoint *checkpoint)
208
{
209
	hashflush(f);
210
	checkpoint->offset = f->total;
211
	the_hash_algo->clone_fn(&checkpoint->ctx, &f->ctx);
212
}
213

214
int hashfile_truncate(struct hashfile *f, struct hashfile_checkpoint *checkpoint)
215
{
216
	off_t offset = checkpoint->offset;
217

218
	if (ftruncate(f->fd, offset) ||
219
	    lseek(f->fd, offset, SEEK_SET) != offset)
220
		return -1;
221
	f->total = offset;
222
	the_hash_algo->clone_fn(&f->ctx, &checkpoint->ctx);
223
	f->offset = 0; /* hashflush() was called in checkpoint */
224
	return 0;
225
}
226

227
void crc32_begin(struct hashfile *f)
228
{
229
	f->crc32 = crc32(0, NULL, 0);
230
	f->do_crc = 1;
231
}
232

233
uint32_t crc32_end(struct hashfile *f)
234
{
235
	f->do_crc = 0;
236
	return f->crc32;
237
}
238

239
int hashfile_checksum_valid(const unsigned char *data, size_t total_len)
240
{
241
	unsigned char got[GIT_MAX_RAWSZ];
242
	git_hash_ctx ctx;
243
	size_t data_len = total_len - the_hash_algo->rawsz;
244

245
	if (total_len < the_hash_algo->rawsz)
246
		return 0; /* say "too short"? */
247

248
	the_hash_algo->init_fn(&ctx);
249
	the_hash_algo->update_fn(&ctx, data, data_len);
250
	the_hash_algo->final_fn(got, &ctx);
251

252
	return hasheq(got, data + data_len, the_repository->hash_algo);
253
}
254

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.