File: makegz.c

package info (click to toggle)
zsync 0.6.2-2
  • links: PTS
  • area: main
  • in suites: stretch
  • size: 1,396 kB
  • ctags: 1,052
  • sloc: ansic: 9,015; sh: 3,800; makefile: 42
file content (173 lines) | stat: -rw-r--r-- 5,256 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173

/*
 *   zsync - client side rsync over http
 *   Copyright (C) 2004,2005,2007,2009 Colin Phipps <cph@moria.org.uk>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the Artistic License v2 (see the accompanying
 *   file COPYING for the full license terms), or, at your option, any later
 *   version of the same license.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   COPYING file for details.
 */

#include "zsglobal.h"

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#include "zlib/zlib.h"

#include <sys/types.h>
#include <sys/stat.h>

#ifdef WITH_DMALLOC
# include <dmalloc.h>
#endif

/* fputlong(filehandle, long)
 * Writes a 32bit int as raw bytes in little-endian to the given filehandle.
 * Returns 0 if successful; otherwise ferror(filehandle) to see the error */
static int fputlong(FILE * f, unsigned long x) {
    int n;
    for (n = 0; n < 4; n++) {
        if (fputc((int)(x & 0xff), f) == EOF)
            return -1;
        x >>= 8;
    }
    return 0;
}

/* time = get_mtime(filehandle)
 * Get the mtime of a file from an open filehandle; or 0 if unavailable */
time_t get_mtime(FILE * f) {
    struct stat s;

    if (fstat(fileno(f), &s) == 0)
        return s.st_mtime;
    else
        return 0;
}

/* filehandle = optimal_gzip(filehandle, out_filename_str, blocksize)
 * Constructs a compressed version of the data in the file referenced by the
 * supplied filehandle; this is saved in a file with the filename supplied as
 * the second parameter. The compressed version is optimised for the zsync
 * algorithm with the supplied blocksize. The function returns a handle to the
 * compressed file (opened for r+w but rewound to the start of the file,
 * ready for reading.
 */

/* Algorithm: simple really. We construct a gzip (have to write header with
 * mtime, footer with length and crc; we do a standard zlib compress on the
 * content _except_ that we supply one block (of the size that zsync will be
 * using) and a Z_PARTIAL_FLUSH at the end of each one, so zlib breaks the
 * compression runs at exactly the places zsync will start/stop retrieving data.
 */
FILE *optimal_gzip(FILE * ffin, const char *fout, size_t blocksize) {
    time_t mtime = get_mtime(ffin);

    /* Open output file (for writing, but also reading so we can return the
     * handle for reading by the caller. */
    FILE *ffout = fopen(fout, "wb+");
    if (!ffout) {
        perror("open");
        return NULL;
    }

    /* Write gzip header */
    if (fwrite("\x1f\x8b\x08\x00", 4, 1, ffout) != 1) {
        perror("write");
        return NULL;
    }
    if (fputlong(ffout, mtime) == -1) {
        perror("write");
        return NULL;
    }
    if (fwrite("\x00\x03", 2, 1, ffout) != 1) {
        perror("write");
        return NULL;
    }

    {   /* Now write compressed content */
        z_stream zs;
        unsigned char *inbuf = malloc(blocksize);
        unsigned char *outbuf = malloc(blocksize + 500);
        int err, r;
        unsigned long crc = crc32(0L, Z_NULL, 0);

        /* Set up zlib object */
        zs.zalloc = Z_NULL;
        zs.zfree = Z_NULL;
        zs.opaque = NULL;
        zs.total_in = 0;
        zs.total_out = 0;

        /* windowBits is passed < 0 to suppress zlib header */
        err = deflateInit2(&zs, 9,
                           Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);

        /* Until end of file or error */
        for (r = 1; r > 0;) {
            r = fread(inbuf, 1, blocksize, ffin);
            if (r < 0)
                break;

            /* Maintain running crc (for footer) */
            crc = crc32(crc, inbuf, r);

            /* Set up compressor for this block */
            zs.next_in = inbuf;
            zs.avail_in = r;
            zs.next_out = outbuf;
            zs.avail_out = blocksize + 500;

            /* Compress with partial flush at the end of this block 
             * unless EOF, in which case finish */
            err = deflate(&zs, r ? Z_PARTIAL_FLUSH : Z_FINISH);
            switch (err) {
            case Z_STREAM_END:
            case Z_OK:
                {
                    size_t w = zs.next_out - outbuf;

                    if (w != fwrite(outbuf, 1, w, ffout)) {
                        perror("write");
                        r = -1;
                    }
                }
                break;
            default:
                fprintf(stderr, "zlib error: %s (%d)\n", zs.msg, err);
                r = -1;
            }
        }

        /* Write gzip footer */
        if (fputlong(ffout, crc) == -1) {
            perror("write");
            return NULL;
        }
        if (fputlong(ffout, zs.total_in) == -1) {
            perror("write");
            return NULL;
        }

        /* Clean up */
        fflush(ffout);
        free(outbuf);
        free(inbuf);
        if (fclose(ffin) != 0 || r != 0) {
            fclose(ffout);
            return NULL;
        }
    }

    /* Return rewound handle on the compressed data to the caller */
    rewind(ffout);
    return ffout;
}