File Coverage

bpc_fileZIO.c
Criterion Covered Total %
statement 0 261 0.0
branch 0 208 0.0
condition n/a
subroutine n/a
pod n/a
total 0 469 0.0


line stmt bran cond sub pod time code
1             /*
2             * Routines for reading and writing compressed files using zlib
3             *
4             * Copyright (C) 2013 Craig Barratt.
5             *
6             * This program is free software; you can redistribute it and/or modify
7             * it under the terms of the GNU General Public License as published by
8             * the Free Software Foundation; either version 3 of the License, or
9             * (at your option) any later version.
10             *
11             * This program is distributed in the hope that it will be useful,
12             * but WITHOUT ANY WARRANTY; without even the implied warranty of
13             * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14             * GNU General Public License for more details.
15             *
16             * You should have received a copy of the GNU General Public License along
17             * with this program; if not, visit the http://fsf.org website.
18             */
19              
20             #include "backuppc.h"
21              
22             /*
23             * A freelist of unused data buffers.
24             * We use the first sizeof(void*) bytes of the buffer as a single-linked
25             * list, with a NULL at the end.
26             */
27             static void *DataBufferFreeList = (void*)NULL;
28              
29             /*
30             * Open a regular or compressed file for reading or writing/create
31             */
32 0           int bpc_fileZIO_open(bpc_fileZIO_fd *fd, char *fileName, int writeFile, int compressLevel)
33             {
34 0           fd->strm.next_out = NULL;
35 0           fd->strm.zalloc = NULL;
36 0           fd->strm.zfree = NULL;
37 0           fd->strm.opaque = NULL;
38              
39 0           fd->compressLevel = compressLevel;
40 0           fd->first = 1;
41 0           fd->write = writeFile;
42 0           fd->eof = 0;
43 0           fd->error = 0;
44 0           fd->writeTeeStderr = 0;
45              
46 0           fd->lineBuf = NULL;
47 0           fd->lineBufSize = 0;
48 0           fd->lineBufLen = 0;
49 0           fd->lineBufIdx = 0;
50 0           fd->lineBufEof = 0;
51              
52 0           fd->bufSize = 1 << 20; /* 1MB */
53 0 0         if ( writeFile ) {
54 0           fd->fd = open(fileName, O_WRONLY | O_CREAT | O_TRUNC, 0660);
55 0 0         if ( fd->fd < 0 ) {
56             /*
57             * try removing first
58             */
59 0           unlink(fileName);
60 0           fd->fd = open(fileName, O_WRONLY | O_CREAT | O_TRUNC, 0660);
61             }
62 0 0         if ( fd->fd < 0 ) return -1;
63 0 0         if ( fd->compressLevel ) {
64 0 0         if (deflateInit2(&fd->strm, compressLevel, Z_DEFLATED, MAX_WBITS, 8,
65             Z_DEFAULT_STRATEGY) != Z_OK) {
66 0           bpc_logErrf("bpc_fileZIO_open: compression init failed\n");
67 0           return -1;
68             }
69 0           fd->strm.next_out = (Bytef*)fd->buf;
70 0           fd->strm.avail_out = fd->bufSize;
71             }
72             } else {
73 0           fd->fd = open(fileName, O_RDONLY);
74 0 0         if ( fd->fd < 0 ) return -1;
75 0 0         if ( fd->compressLevel ) {
76 0 0         if ( inflateInit(&fd->strm) != Z_OK ) {
77 0           bpc_logErrf("bpc_fileZIO_open: compression init failed\n");
78 0           return -1;
79             }
80 0           fd->strm.avail_in = 0;
81             }
82             }
83 0 0         if ( DataBufferFreeList ) {
84 0           fd->buf = DataBufferFreeList;
85 0           DataBufferFreeList = *(void**)DataBufferFreeList;
86             } else {
87 0           fd->buf = malloc(fd->bufSize);
88             }
89 0 0         if ( !fd->buf ) {
90 0           bpc_logErrf("bpc_fileZIO_open: fatal error: can't allocate %u bytes\n", (unsigned)fd->bufSize);
91 0           return -1;
92             }
93 0 0         if ( BPC_LogLevel >= 8 ) bpc_logMsgf("bpc_fileZIO_open(%s, %d, %d) -> %d\n", fileName, writeFile, compressLevel, fd->fd);
94 0           return 0;
95             }
96              
97             /*
98             * Open an existing FILE stream for reading/writing.
99             * Note: we used unbuffered integer fds here, and we simply grab the underlying integer fd. That will
100             * mess up the FILE stream buffering if anything has been read/written from/to the FILE.
101             *
102             * This function is only used to support the legacy BackupPC::FileZIO feature that allows you to
103             * pass STDIN in as an argument to open().
104             */
105 0           int bpc_fileZIO_fdopen(bpc_fileZIO_fd *fd, FILE *stream, int writeFile, int compressLevel)
106             {
107 0           fd->strm.next_out = NULL;
108 0           fd->strm.zalloc = NULL;
109 0           fd->strm.zfree = NULL;
110 0           fd->strm.opaque = NULL;
111              
112 0           fd->compressLevel = compressLevel;
113 0           fd->first = 1;
114 0           fd->write = writeFile;
115 0           fd->eof = 0;
116 0           fd->error = 0;
117 0           fd->writeTeeStderr = 0;
118              
119 0           fd->lineBuf = NULL;
120 0           fd->lineBufSize = 0;
121 0           fd->lineBufLen = 0;
122 0           fd->lineBufIdx = 0;
123 0           fd->lineBufEof = 0;
124              
125 0           fd->fd = fileno(stream);
126 0 0         if ( fd->fd < 0 ) return -1;
127              
128 0           fd->bufSize = 1 << 20; /* 1MB */
129 0 0         if ( !(fd->buf = malloc(fd->bufSize)) ) {
130 0           bpc_logErrf("bpc_fileZIO_fdopen: can't allocate %u bytes\n", (unsigned)fd->bufSize);
131 0           return -1;
132             }
133              
134 0 0         if ( fd->compressLevel ) {
135 0 0         if ( writeFile ) {
136 0 0         if (deflateInit2(&fd->strm, compressLevel, Z_DEFLATED, MAX_WBITS, 8,
137             Z_DEFAULT_STRATEGY) != Z_OK) {
138 0           bpc_logErrf("bpc_fileZIO_open: compression init failed\n");
139 0           return -1;
140             }
141 0           fd->strm.next_out = (Bytef*)fd->buf;
142 0           fd->strm.avail_out = fd->bufSize;
143             } else {
144 0 0         if ( inflateInit(&fd->strm) != Z_OK ) {
145 0           bpc_logErrf("bpc_fileZIO_open: compression init failed\n");
146 0           return -1;
147             }
148 0           fd->strm.avail_in = 0;
149             }
150             }
151 0 0         if ( BPC_LogLevel >= 8 ) bpc_logMsgf("bpc_fileZIO_fdopen(%d, %d) -> %d\n", writeFile, compressLevel, fd->fd);
152 0           return 0;
153             }
154              
155 0           void bpc_fileZIO_writeTeeStderr(bpc_fileZIO_fd *fd, int tee)
156             {
157 0           fd->writeTeeStderr = tee;
158 0           }
159              
160             /*
161             * Read from a compressed or regular file.
162             */
163 0           ssize_t bpc_fileZIO_read(bpc_fileZIO_fd *fd, uchar *buf, size_t nRead)
164             {
165 0           size_t totalRead = 0;
166              
167 0 0         if ( fd->write || fd->fd < 0 ) return -1;
    0          
168 0 0         if ( fd->compressLevel == 0 ) {
169             ssize_t thisRead;
170 0 0         while ( nRead > 0 ) {
171             do {
172 0           thisRead = read(fd->fd, buf, nRead);
173 0 0         } while ( thisRead < 0 && errno == EINTR );
    0          
174 0 0         if ( thisRead < 0 ) return thisRead;
175 0 0         if ( thisRead == 0 ) return totalRead;
176 0           buf += thisRead;
177 0           nRead -= thisRead;
178 0           totalRead += thisRead;
179             }
180 0           return totalRead;
181             }
182 0 0         if ( fd->error ) return fd->error;
183 0 0         while ( nRead > 0 ) {
184             /*
185             * Start by trying to read more of the compressed input file
186             */
187 0           int maxRead, thisRead = -1;
188              
189 0 0         if ( fd->strm.avail_in == 0 ) {
190 0           fd->strm.next_in = (Bytef*)fd->buf;
191             }
192 0           maxRead = fd->bufSize - ((fd->strm.next_in - (Bytef*)fd->buf) + fd->strm.avail_in);
193              
194 0 0         if ( !fd->eof && maxRead > 0 ) {
    0          
195             do {
196 0           thisRead = read(fd->fd, fd->strm.next_in + fd->strm.avail_in, maxRead);
197 0 0         } while ( thisRead < 0 && errno == EINTR );
    0          
198 0 0         if ( thisRead < 0 ) {
199 0           fd->error = thisRead;
200 0           return fd->error;
201             }
202 0           fd->strm.avail_in += thisRead;
203 0 0         if ( thisRead == 0 ) {
204 0           fd->eof = 1;
205             }
206             }
207              
208 0 0         while ( nRead > 0 ) {
209             int status, numOut;
210              
211 0           fd->strm.next_out = (Bytef*)buf;
212 0           fd->strm.avail_out = nRead;
213              
214 0 0         if ( fd->first && fd->strm.avail_in > 0 ) {
    0          
215             /*
216             * we are at the very start of a new zlib block (or it could be cached checksums)
217             */
218 0           fd->first = 0;
219 0 0         if ( fd->strm.next_in[0] == 0xd6 || fd->strm.next_in[0] == 0xd7 ) {
    0          
220             /*
221             * Flag 0xd6 or 0xd7 means this is a compressed file with
222             * appended md4 block checksums for rsync. Change
223             * the first byte back to 0x78 and proceed.
224             */
225 0           fd->strm.next_in[0] = 0x78;
226 0 0         } else if ( fd->strm.next_in[0] == 0xb3 ) {
227             /*
228             * Flag 0xb3 means this is the start of the rsync
229             * block checksums, so consider this as EOF for
230             * the compressed file. Also seek the file so
231             * it is positioned at the 0xb3.
232             */
233 0           fd->eof = 1;
234             /* TODO: check return status */
235 0           lseek(fd->fd, -fd->strm.avail_in, SEEK_CUR);
236 0           fd->strm.avail_in = 0;
237             }
238             }
239 0 0         status = inflate(&fd->strm, fd->eof ? Z_SYNC_FLUSH : Z_NO_FLUSH);
240 0           numOut = fd->strm.next_out - (Bytef*)buf;
241 0           nRead -= numOut;
242 0           buf += numOut;
243 0           totalRead += numOut;
244              
245 0 0         if ( BPC_LogLevel >= 10 ) bpc_logMsgf("inflate returns %d; thisRead = %d, avail_in = %d, numOut = %d\n", status, thisRead, fd->strm.avail_in, numOut);
246              
247 0 0         if ( fd->eof && fd->strm.avail_in == 0 && numOut == 0 ) return totalRead;
    0          
    0          
248 0 0         if ( status == Z_OK && fd->strm.avail_in == 0 ) break;
    0          
249 0 0         if ( status == Z_BUF_ERROR && fd->strm.avail_in == 0 && numOut == 0 ) break;
    0          
    0          
250 0 0         if ( status == Z_STREAM_END ) {
251 0           inflateReset(&fd->strm);
252 0           fd->first = 1;
253             }
254 0 0         if ( status < 0 ) {
255             /*
256             * return error immediately if there are no bytes to return
257             */
258 0 0         if ( totalRead <= 0 ) return status;
259             /*
260             * save error return for next call and return remaining buffer
261             */
262 0           fd->error = status;
263 0           return totalRead;
264             }
265             }
266             }
267 0           return totalRead;
268             }
269              
270             /*
271             * Write to a compressed or regular file.
272             * Write flush and eof is indicated with nWrite == 0.
273             */
274 0           ssize_t bpc_fileZIO_write(bpc_fileZIO_fd *fd, uchar *buf, size_t nWrite)
275             {
276 0 0         if ( !fd->write || fd->fd < 0 ) return -1;
    0          
277 0 0         if ( fd->eof ) return 0;
278 0 0         if ( fd->writeTeeStderr && nWrite > 0 ) (void)fwrite((char*)buf, nWrite, 1, stderr);
    0          
279 0 0         if ( fd->compressLevel == 0 ) {
280 0           int thisWrite, totalWrite = 0;
281 0 0         while ( nWrite > 0 ) {
282             do {
283 0           thisWrite = write(fd->fd, buf, nWrite);
284 0 0         } while ( thisWrite < 0 && errno == EINTR );
    0          
285 0 0         if ( thisWrite < 0 ) return thisWrite;
286 0           buf += thisWrite;
287 0           nWrite -= thisWrite;
288 0           totalWrite += thisWrite;
289             }
290 0           return totalWrite;
291             }
292 0 0         if ( fd->error ) return fd->error;
293              
294 0 0         if ( nWrite == 0 || (fd->strm.total_in > (1 << 23) && fd->strm.total_out < (1 << 18)) ) {
    0          
    0          
295             /*
296             * final or intermediate flush (if the compression ratio is too high, since the
297             * perl Compress::Zlib implementation allocates the output buffer for inflate
298             * and it could grow to be very large).
299             */
300 0 0         if ( BPC_LogLevel >= 10 ) bpc_logMsgf("Flushing (nWrite = %d)\n", nWrite);
301             while ( 1 ) {
302             int status, numOut, thisWrite;
303 0           char *writePtr = fd->buf;
304            
305 0           fd->strm.next_in = NULL;
306 0           fd->strm.avail_in = 0;
307 0           fd->strm.next_out = (Bytef*)fd->buf;
308 0           fd->strm.avail_out = fd->bufSize;
309 0           status = deflate(&fd->strm, Z_FINISH);
310 0           numOut = fd->strm.next_out - (Bytef*)fd->buf;
311            
312 0 0         while ( numOut > 0 ) {
313             do {
314 0           thisWrite = write(fd->fd, writePtr, numOut);
315 0 0         } while ( thisWrite < 0 && errno == EINTR );
    0          
316 0 0         if ( thisWrite < 0 ) return thisWrite;
317 0           numOut -= thisWrite;
318 0           writePtr += thisWrite;
319             }
320 0 0         if ( status != Z_OK ) break;
321 0           }
322 0           deflateReset(&fd->strm);
323             }
324 0 0         if ( nWrite == 0 ) {
325 0           fd->eof = 1;
326 0           return nWrite;
327             }
328              
329 0           fd->strm.next_in = (Bytef*)buf;
330 0           fd->strm.avail_in = nWrite;
331 0 0         while ( fd->strm.avail_in > 0 ) {
332             int numOut, thisWrite;
333 0           char *writePtr = fd->buf;
334              
335 0           fd->strm.next_out = (Bytef*)fd->buf;
336 0           fd->strm.avail_out = fd->bufSize;
337 0           deflate(&fd->strm, Z_NO_FLUSH);
338 0           numOut = fd->strm.next_out - (Bytef*)fd->buf;
339              
340 0 0         while ( numOut > 0 ) {
341             do {
342 0           thisWrite = write(fd->fd, writePtr, numOut);
343 0 0         } while ( thisWrite < 0 && errno == EINTR );
    0          
344 0 0         if ( thisWrite < 0 ) return thisWrite;
345 0           numOut -= thisWrite;
346 0           writePtr += thisWrite;
347             }
348             }
349 0           return nWrite;
350             }
351              
352 0           int bpc_fileZIO_close(bpc_fileZIO_fd *fd)
353             {
354 0 0         if ( fd->fd < 0 ) return -1;
355              
356 0 0         if ( fd->compressLevel ) {
357 0 0         if ( fd->write ) {
358             /*
359             * Flush the output file
360             */
361 0           bpc_fileZIO_write(fd, NULL, 0);
362 0           deflateEnd(&fd->strm);
363             } else {
364 0           inflateEnd(&fd->strm);
365             }
366             }
367 0 0         if ( BPC_LogLevel >= 8 ) bpc_logMsgf("bpc_fileZIO_close(%d)\n", fd->fd);
368 0           close(fd->fd);
369 0 0         if ( fd->lineBuf ) free(fd->lineBuf);
370 0           fd->lineBuf = NULL;
371 0 0         if ( fd->buf ) {
372 0           *(void**)fd->buf = DataBufferFreeList;
373 0           DataBufferFreeList = fd->buf;
374 0           fd->buf = NULL;
375             }
376 0           fd->fd = -1;
377 0           return 0;
378             }
379              
380 0           int bpc_fileZIO_rewind(bpc_fileZIO_fd *fd)
381             {
382 0 0         if ( fd->write ) return -1;
383              
384 0 0         if ( fd->compressLevel ) {
385 0           inflateReset(&fd->strm);
386 0           fd->first = 1;
387 0           fd->eof = 0;
388 0           fd->error = 0;
389 0           fd->strm.avail_in = 0;
390             }
391 0 0         return lseek(fd->fd, 0, SEEK_SET) == 0 ? 0 : -1;
392             }
393              
394             /*
395             * Returns \n terminated lines, one at a time, from the opened read stream.
396             * The returned string is not '\0' terminated. At EOF sets *str = NULL;
397             */
398 0           int bpc_fileZIO_readLine(bpc_fileZIO_fd *fd, char **str, size_t *strLen)
399             {
400 0 0         if ( !fd->lineBuf ) {
401             /*
402             * allocate initial read buffer
403             */
404 0           fd->lineBufSize = 65536;
405 0 0         if ( !(fd->lineBuf = malloc(fd->lineBufSize)) ) {
406 0           bpc_logErrf("bpc_fileZIO_readLine: can't allocate %u bytes\n", (unsigned)fd->lineBufSize);
407 0           return -1;
408             }
409 0           fd->lineBufLen = 0;
410 0           fd->lineBufIdx = 0;
411 0           fd->lineBufEof = 0;
412             }
413             while ( 1 ) {
414             char *p;
415              
416 0 0         if ( fd->lineBufIdx < fd->lineBufLen ) {
417 0 0         if ( (p = memchr(fd->lineBuf + fd->lineBufIdx, '\n', fd->lineBufLen - fd->lineBufIdx)) ) {
418             /*
419             * found next complete line
420             */
421 0           p++;
422 0           *str = fd->lineBuf + fd->lineBufIdx;
423 0           *strLen = p - (fd->lineBuf + fd->lineBufIdx);
424 0           fd->lineBufIdx += p - (fd->lineBuf + fd->lineBufIdx);
425 0           return 0;
426 0 0         } else if ( fd->lineBufEof ) {
427             /*
428             * return last string - not \n terminated
429             */
430 0           *str = fd->lineBuf + fd->lineBufIdx;
431 0           *strLen = fd->lineBufLen - fd->lineBufIdx;
432 0           fd->lineBufIdx += fd->lineBufLen - fd->lineBufIdx;
433 0           return 0;
434 0 0         } else if ( fd->lineBufLen >= fd->lineBufSize ) {
435             /*
436             * No complete lines left, and buffer is full. Either move the unused buffer down to make
437             * more room for reading, or make the buffer bigger.
438             */
439 0 0         if ( fd->lineBufIdx > 0 ) {
440 0           memmove(fd->lineBuf, fd->lineBuf + fd->lineBufIdx, fd->lineBufLen - fd->lineBufIdx);
441 0           fd->lineBufLen -= fd->lineBufIdx;
442 0           fd->lineBufIdx = 0;
443             } else {
444 0           fd->lineBufSize *= 2;
445 0 0         if ( !(fd->lineBuf = realloc(fd->lineBuf, fd->lineBufSize)) ) {
446 0           bpc_logErrf("bpc_fileZIO_readLine: can't reallocate %u bytes\n", (unsigned)fd->lineBufSize);
447 0           return -1;
448             }
449             }
450             }
451             }
452 0 0         if ( fd->lineBufIdx >= fd->lineBufLen && fd->lineBufEof ) {
    0          
453             /*
454             * at EOF
455             */
456 0           *str = NULL;
457 0           *strLen = 0;
458 0           return 0;
459             }
460 0 0         if ( fd->lineBufIdx >= fd->lineBufLen ) {
461 0           fd->lineBufLen = 0;
462 0           fd->lineBufIdx = 0;
463             }
464 0 0         if ( fd->lineBufLen < fd->lineBufSize && !fd->lineBufEof ) {
    0          
465 0           int nread = bpc_fileZIO_read(fd, (uchar*)fd->lineBuf + fd->lineBufLen, fd->lineBufSize - fd->lineBufLen);
466 0 0         if ( nread < 0 ) {
467 0           bpc_logErrf("bpc_fileZIO_readLine: reading %u returned %d\n", (unsigned)(fd->lineBufSize - fd->lineBufLen), nread);
468 0           return nread;
469             }
470 0 0         if ( nread == 0 ) fd->lineBufEof = 1;
471 0           fd->lineBufLen += nread;
472             }
473 0           }
474             }