2 * This file is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2,
4 * as published by the Free Software Foundation.
6 * In addition to the permissions in the GNU General Public License,
7 * the authors give you unlimited permission to link the compiled
8 * version of this file into combinations with other programs,
9 * and to distribute those combinations without any restriction
10 * coming from the use of this file. (The General Public License
11 * restrictions do apply in other respects; for example, they cover
12 * modification of the file, and distribution when not linked into
13 * a combined executable.)
15 * This file is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; see the file COPYING. If not, write to
22 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
31 static const size_t WRITE_BUFFER_SIZE
= (4096 * 2);
33 static int lock_file(git_filebuf
*file
, int flags
)
35 if (gitfo_exists(file
->path_lock
) == 0) {
36 if (flags
& GIT_FILEBUF_FORCE
)
37 gitfo_unlink(file
->path_lock
);
39 return git__throw(GIT_EOSERR
, "Failed to lock file");
42 /* create path to the file buffer is required */
43 if (flags
& GIT_FILEBUF_FORCE
) {
44 file
->fd
= gitfo_creat_locked_force(file
->path_lock
, 0644);
46 file
->fd
= gitfo_creat_locked(file
->path_lock
, 0644);
50 return git__throw(GIT_EOSERR
, "Failed to create lock");
52 if ((flags
& GIT_FILEBUF_APPEND
) && gitfo_exists(file
->path_original
) == 0) {
57 source
= gitfo_open(file
->path_original
, O_RDONLY
);
59 return git__throw(GIT_EOSERR
, "Failed to lock file. Could not open %s", file
->path_original
);
61 while ((read_bytes
= gitfo_read(source
, buffer
, 2048)) > 0) {
62 gitfo_write(file
->fd
, buffer
, read_bytes
);
64 git_hash_update(file
->digest
, buffer
, read_bytes
);
73 void git_filebuf_cleanup(git_filebuf
*file
)
76 gitfo_close(file
->fd
);
78 if (file
->fd
>= 0 && file
->path_lock
&& gitfo_exists(file
->path_lock
) == GIT_SUCCESS
)
79 gitfo_unlink(file
->path_lock
);
82 git_hash_free_ctx(file
->digest
);
87 deflateEnd(&file
->zs
);
89 free(file
->path_original
);
90 free(file
->path_lock
);
93 GIT_INLINE(int) flush_buffer(git_filebuf
*file
)
95 int result
= file
->write(file
, file
->buffer
, file
->buf_pos
);
100 static int write_normal(git_filebuf
*file
, const void *source
, size_t len
)
105 result
= gitfo_write(file
->fd
, (void *)source
, len
);
107 git_hash_update(file
->digest
, source
, len
);
113 static int write_deflate(git_filebuf
*file
, const void *source
, size_t len
)
116 z_stream
*zs
= &file
->zs
;
118 if (len
> 0 || file
->flush_mode
== Z_FINISH
) {
119 zs
->next_in
= (void *)source
;
125 zs
->next_out
= file
->z_buf
;
126 zs
->avail_out
= file
->buf_size
;
128 result
= deflate(zs
, file
->flush_mode
);
129 assert(result
!= Z_STREAM_ERROR
);
131 have
= file
->buf_size
- zs
->avail_out
;
133 if (gitfo_write(file
->fd
, file
->z_buf
, have
) < GIT_SUCCESS
)
134 return git__throw(GIT_EOSERR
, "Failed to write to file");
136 } while (zs
->avail_out
== 0);
138 assert(zs
->avail_in
== 0);
141 git_hash_update(file
->digest
, source
, len
);
147 int git_filebuf_open(git_filebuf
*file
, const char *path
, int flags
)
152 assert(file
&& path
);
154 memset(file
, 0x0, sizeof(git_filebuf
));
156 file
->buf_size
= WRITE_BUFFER_SIZE
;
160 /* Allocate the main cache buffer */
161 file
->buffer
= git__malloc(file
->buf_size
);
162 if (file
->buffer
== NULL
){
167 /* If we are hashing on-write, allocate a new hash context */
168 if (flags
& GIT_FILEBUF_HASH_CONTENTS
) {
169 if ((file
->digest
= git_hash_new_ctx()) == NULL
) {
175 /* If we are deflating on-write, */
176 if (flags
& GIT_FILEBUF_DEFLATE_CONTENTS
) {
178 /* Initialize the ZLib stream */
179 if (deflateInit(&file
->zs
, Z_BEST_SPEED
) != Z_OK
) {
180 error
= git__throw(GIT_EZLIB
, "Failed to initialize zlib");
184 /* Allocate the Zlib cache buffer */
185 file
->z_buf
= git__malloc(file
->buf_size
);
186 if (file
->z_buf
== NULL
){
192 file
->flush_mode
= Z_NO_FLUSH
;
193 file
->write
= &write_deflate
;
195 file
->write
= &write_normal
;
198 /* If we are writing to a temp file */
199 if (flags
& GIT_FILEBUF_TEMPORARY
) {
200 char tmp_path
[GIT_PATH_MAX
];
202 /* Open the file as temporary for locking */
203 file
->fd
= gitfo_mktemp(tmp_path
, path
);
209 /* No original path */
210 file
->path_original
= NULL
;
211 file
->path_lock
= git__strdup(tmp_path
);
213 if (file
->path_lock
== NULL
) {
218 path_len
= strlen(path
);
220 /* Save the original path of the file */
221 file
->path_original
= git__strdup(path
);
222 if (file
->path_original
== NULL
) {
227 /* create the locking path by appending ".lock" to the original */
228 file
->path_lock
= git__malloc(path_len
+ GIT_FILELOCK_EXTLENGTH
);
229 if (file
->path_lock
== NULL
) {
234 memcpy(file
->path_lock
, file
->path_original
, path_len
);
235 memcpy(file
->path_lock
+ path_len
, GIT_FILELOCK_EXTENSION
, GIT_FILELOCK_EXTLENGTH
);
237 /* open the file for locking */
238 if ((error
= lock_file(file
, flags
)) < GIT_SUCCESS
)
245 git_filebuf_cleanup(file
);
246 return git__rethrow(error
, "Failed to open file buffer for '%s'", path
);
249 int git_filebuf_hash(git_oid
*oid
, git_filebuf
*file
)
253 assert(oid
&& file
&& file
->digest
);
255 if ((error
= flush_buffer(file
)) < GIT_SUCCESS
)
256 return git__rethrow(error
, "Failed to get hash for file");
258 git_hash_final(oid
, file
->digest
);
259 git_hash_free_ctx(file
->digest
);
265 int git_filebuf_commit_at(git_filebuf
*file
, const char *path
)
267 free(file
->path_original
);
268 file
->path_original
= git__strdup(path
);
269 if (file
->path_original
== NULL
)
272 return git_filebuf_commit(file
);
275 int git_filebuf_commit(git_filebuf
*file
)
279 /* temporary files cannot be committed */
280 assert(file
&& file
->path_original
);
282 file
->flush_mode
= Z_FINISH
;
283 if ((error
= flush_buffer(file
)) < GIT_SUCCESS
)
286 gitfo_close(file
->fd
);
289 error
= gitfo_mv(file
->path_lock
, file
->path_original
);
292 git_filebuf_cleanup(file
);
293 if (error
< GIT_SUCCESS
)
294 return git__rethrow(error
, "Failed to commit locked file from buffer");
298 GIT_INLINE(void) add_to_cache(git_filebuf
*file
, const void *buf
, size_t len
)
300 memcpy(file
->buffer
+ file
->buf_pos
, buf
, len
);
301 file
->buf_pos
+= len
;
304 int git_filebuf_write(git_filebuf
*file
, const void *buff
, size_t len
)
307 const unsigned char *buf
= buff
;
310 size_t space_left
= file
->buf_size
- file
->buf_pos
;
312 /* cache if it's small */
313 if (space_left
> len
) {
314 add_to_cache(file
, buf
, len
);
318 /* flush the cache if it doesn't fit */
319 if (file
->buf_pos
> 0) {
320 add_to_cache(file
, buf
, space_left
);
322 if ((error
= flush_buffer(file
)) < GIT_SUCCESS
)
323 return git__rethrow(error
, "Failed to write to buffer");
329 /* write too-large chunks immediately */
330 if (len
> file
->buf_size
) {
331 error
= file
->write(file
, buf
, len
);
332 if (error
< GIT_SUCCESS
)
333 return git__rethrow(error
, "Failed to write to buffer");
338 int git_filebuf_reserve(git_filebuf
*file
, void **buffer
, size_t len
)
341 size_t space_left
= file
->buf_size
- file
->buf_pos
;
345 if (len
> file
->buf_size
)
348 if (space_left
<= len
) {
349 if ((error
= flush_buffer(file
)) < GIT_SUCCESS
)
350 return git__rethrow(error
, "Failed to reserve buffer");
353 *buffer
= (file
->buffer
+ file
->buf_pos
);
354 file
->buf_pos
+= len
;
359 int git_filebuf_printf(git_filebuf
*file
, const char *format
, ...)
362 size_t space_left
= file
->buf_size
- file
->buf_pos
;
365 va_start(arglist
, format
);
366 len
= vsnprintf((char *)file
->buffer
+ file
->buf_pos
, space_left
, format
, arglist
);
369 if (len
< 0 || (size_t)len
>= space_left
) {
370 if ((error
= flush_buffer(file
)) < GIT_SUCCESS
)
371 return git__rethrow(error
, "Failed to output to buffer");
373 space_left
= file
->buf_size
- file
->buf_pos
;
375 va_start(arglist
, format
);
376 len
= vsnprintf((char *)file
->buffer
+ file
->buf_pos
, space_left
, format
, arglist
);
379 if (len
< 0 || (size_t)len
> file
->buf_size
)
383 file
->buf_pos
+= len
;