gzlog.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:11k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * gzlog.c
  3.  * Copyright (C) 2004 Mark Adler
  4.  * For conditions of distribution and use, see copyright notice in gzlog.h
  5.  * version 1.0, 26 Nov 2004
  6.  *
  7.  */
  8. #include <string.h>             /* memcmp() */
  9. #include <stdlib.h>             /* malloc(), free(), NULL */
  10. #include <sys/types.h>          /* size_t, off_t */
  11. #include <unistd.h>             /* read(), close(), sleep(), ftruncate(), */
  12.                                 /* lseek() */
  13. #include <fcntl.h>              /* open() */
  14. #include <sys/file.h>           /* flock() */
  15. #include "zlib.h"               /* deflateInit2(), deflate(), deflateEnd() */
  16. #include "gzlog.h"              /* interface */
  17. #define local static
  18. /* log object structure */
  19. typedef struct {
  20.     int id;                 /* object identifier */
  21.     int fd;                 /* log file descriptor */
  22.     off_t extra;            /* offset of extra "ap" subfield */
  23.     off_t mark_off;         /* offset of marked data */
  24.     off_t last_off;         /* offset of last block */
  25.     unsigned long crc;      /* uncompressed crc */
  26.     unsigned long len;      /* uncompressed length (modulo 2^32) */
  27.     unsigned stored;        /* length of current stored block */
  28. } gz_log;
  29. #define GZLOGID 19334       /* gz_log object identifier */
  30. #define LOCK_RETRY 1            /* retry lock once a second */
  31. #define LOCK_PATIENCE 1200      /* try about twenty minutes before forcing */
  32. /* acquire a lock on a file */
  33. local int lock(int fd)
  34. {
  35.     int patience;
  36.     /* try to lock every LOCK_RETRY seconds for LOCK_PATIENCE seconds */
  37.     patience = LOCK_PATIENCE;
  38.     do {
  39.         if (flock(fd, LOCK_EX + LOCK_NB) == 0)
  40.             return 0;
  41.         (void)sleep(LOCK_RETRY);
  42.         patience -= LOCK_RETRY;
  43.     } while (patience > 0);
  44.     /* we've run out of patience -- give up */
  45.     return -1;
  46. }
  47. /* release lock */
  48. local void unlock(int fd)
  49. {
  50.     (void)flock(fd, LOCK_UN);
  51. }
  52. /* release a log object */
  53. local void log_clean(gz_log *log)
  54. {
  55.     unlock(log->fd);
  56.     (void)close(log->fd);
  57.     free(log);
  58. }
  59. /* read an unsigned long from a byte buffer little-endian */
  60. local unsigned long make_ulg(unsigned char *buf)
  61. {
  62.     int n;
  63.     unsigned long val;
  64.     val = (unsigned long)(*buf++);
  65.     for (n = 8; n < 32; n += 8)
  66.         val += (unsigned long)(*buf++) << n;
  67.     return val;
  68. }
  69. /* read an off_t from a byte buffer little-endian */
  70. local off_t make_off(unsigned char *buf)
  71. {
  72.     int n;
  73.     off_t val;
  74.     val = (off_t)(*buf++);
  75.     for (n = 8; n < 64; n += 8)
  76.         val += (off_t)(*buf++) << n;
  77.     return val;
  78. }
  79. /* write an unsigned long little-endian to byte buffer */
  80. local void dice_ulg(unsigned long val, unsigned char *buf)
  81. {
  82.     int n;
  83.     for (n = 0; n < 4; n++) {
  84.         *buf++ = val & 0xff;
  85.         val >>= 8;
  86.     }
  87. }
  88. /* write an off_t little-endian to byte buffer */
  89. local void dice_off(off_t val, unsigned char *buf)
  90. {
  91.     int n;
  92.     for (n = 0; n < 8; n++) {
  93.         *buf++ = val & 0xff;
  94.         val >>= 8;
  95.     }
  96. }
  97. /* initial, empty gzip file for appending */
  98. local char empty_gz[] = {
  99.     0x1f, 0x8b,                 /* magic gzip id */
  100.     8,                          /* compression method is deflate */
  101.     4,                          /* there is an extra field */
  102.     0, 0, 0, 0,                 /* no modification time provided */
  103.     0, 0xff,                    /* no extra flags, no OS */
  104.     20, 0, 'a', 'p', 16, 0,     /* extra field with "ap" subfield */
  105.     32, 0, 0, 0, 0, 0, 0, 0,    /* offset of uncompressed data */
  106.     32, 0, 0, 0, 0, 0, 0, 0,    /* offset of last block */
  107.     1, 0, 0, 0xff, 0xff,        /* empty stored block (last) */
  108.     0, 0, 0, 0,                 /* crc */
  109.     0, 0, 0, 0                  /* uncompressed length */
  110. };
  111. /* initialize a log object with locking */
  112. void *gzlog_open(char *path)
  113. {
  114.     unsigned xlen;
  115.     unsigned char temp[20];
  116.     unsigned sub_len;
  117.     int good;
  118.     gz_log *log;
  119.     /* allocate log structure */
  120.     log = malloc(sizeof(gz_log));
  121.     if (log == NULL)
  122.         return NULL;
  123.     log->id = GZLOGID;
  124.     /* open file, creating it if necessary, and locking it */
  125.     log->fd = open(path, O_RDWR | O_CREAT, 0600);
  126.     if (log->fd < 0) {
  127.         free(log);
  128.         return NULL;
  129.     }
  130.     if (lock(log->fd)) {
  131.         close(log->fd);
  132.         free(log);
  133.         return NULL;
  134.     }
  135.     /* if file is empty, write new gzip stream */
  136.     if (lseek(log->fd, 0, SEEK_END) == 0) {
  137.         if (write(log->fd, empty_gz, sizeof(empty_gz)) != sizeof(empty_gz)) {
  138.             log_clean(log);
  139.             return NULL;
  140.         }
  141.     }
  142.     /* check gzip header */
  143.     (void)lseek(log->fd, 0, SEEK_SET);
  144.     if (read(log->fd, temp, 12) != 12 || temp[0] != 0x1f ||
  145.         temp[1] != 0x8b || temp[2] != 8 || (temp[3] & 4) == 0) {
  146.         log_clean(log);
  147.         return NULL;
  148.     }
  149.     /* process extra field to find "ap" sub-field */
  150.     xlen = temp[10] + (temp[11] << 8);
  151.     good = 0;
  152.     while (xlen) {
  153.         if (xlen < 4 || read(log->fd, temp, 4) != 4)
  154.             break;
  155.         sub_len = temp[2];
  156.         sub_len += temp[3] << 8;
  157.         xlen -= 4;
  158.         if (memcmp(temp, "ap", 2) == 0 && sub_len == 16) {
  159.             good = 1;
  160.             break;
  161.         }
  162.         if (xlen < sub_len)
  163.             break;
  164.         (void)lseek(log->fd, sub_len, SEEK_CUR);
  165.         xlen -= sub_len;
  166.     }
  167.     if (!good) {
  168.         log_clean(log);
  169.         return NULL;
  170.     }
  171.     /* read in "ap" sub-field */
  172.     log->extra = lseek(log->fd, 0, SEEK_CUR);
  173.     if (read(log->fd, temp, 16) != 16) {
  174.         log_clean(log);
  175.         return NULL;
  176.     }
  177.     log->mark_off = make_off(temp);
  178.     log->last_off = make_off(temp + 8);
  179.     /* get crc, length of gzip file */
  180.     (void)lseek(log->fd, log->last_off, SEEK_SET);
  181.     if (read(log->fd, temp, 13) != 13 ||
  182.         memcmp(temp, "010000377377", 5) != 0) {
  183.         log_clean(log);
  184.         return NULL;
  185.     }
  186.     log->crc = make_ulg(temp + 5);
  187.     log->len = make_ulg(temp + 9);
  188.     /* set up to write over empty last block */
  189.     (void)lseek(log->fd, log->last_off + 5, SEEK_SET);
  190.     log->stored = 0;
  191.     return (void *)log;
  192. }
  193. /* maximum amount to put in a stored block before starting a new one */
  194. #define MAX_BLOCK 16384
  195. /* write a block to a log object */
  196. int gzlog_write(void *obj, char *data, size_t len)
  197. {
  198.     size_t some;
  199.     unsigned char temp[5];
  200.     gz_log *log;
  201.     /* check object */
  202.     log = (gz_log *)obj;
  203.     if (log == NULL || log->id != GZLOGID)
  204.         return 1;
  205.     /* write stored blocks until all of the input is written */
  206.     do {
  207.         some = MAX_BLOCK - log->stored;
  208.         if (some > len)
  209.             some = len;
  210.         if (write(log->fd, data, some) != some)
  211.             return 1;
  212.         log->crc = crc32(log->crc, data, some);
  213.         log->len += some;
  214.         len -= some;
  215.         data += some;
  216.         log->stored += some;
  217.         /* if the stored block is full, end it and start another */
  218.         if (log->stored == MAX_BLOCK) {
  219.             (void)lseek(log->fd, log->last_off, SEEK_SET);
  220.             temp[0] = 0;
  221.             dice_ulg(log->stored + ((unsigned long)(~log->stored) << 16),
  222.                      temp + 1);
  223.             if (write(log->fd, temp, 5) != 5)
  224.                 return 1;
  225.             log->last_off = lseek(log->fd, log->stored, SEEK_CUR);
  226.             (void)lseek(log->fd, 5, SEEK_CUR);
  227.             log->stored = 0;
  228.         }
  229.     } while (len);
  230.     return 0;
  231. }
  232. /* recompress the remaining stored deflate data in place */
  233. local int recomp(gz_log *log)
  234. {
  235.     z_stream strm;
  236.     size_t len, max;
  237.     unsigned char *in;
  238.     unsigned char *out;
  239.     unsigned char temp[16];
  240.     /* allocate space and read it all in (it's around 1 MB) */
  241.     len = log->last_off - log->mark_off;
  242.     max = len + (len >> 12) + (len >> 14) + 11;
  243.     out = malloc(max);
  244.     if (out == NULL)
  245.         return 1;
  246.     in = malloc(len);
  247.     if (in == NULL) {
  248.         free(out);
  249.         return 1;
  250.     }
  251.     (void)lseek(log->fd, log->mark_off, SEEK_SET);
  252.     if (read(log->fd, in, len) != len) {
  253.         free(in);
  254.         free(out);
  255.         return 1;
  256.     }
  257.     /* recompress in memory, decoding stored data as we go */
  258.     /* note: this assumes that unsigned is four bytes or more */
  259.     /*       consider not making that assumption */
  260.     strm.zalloc = Z_NULL;
  261.     strm.zfree = Z_NULL;
  262.     strm.opaque = Z_NULL;
  263.     if (deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, -15, 8,
  264.         Z_DEFAULT_STRATEGY) != Z_OK) {
  265.         free(in);
  266.         free(out);
  267.         return 1;
  268.     }
  269.     strm.next_in = in;
  270.     strm.avail_out = max;
  271.     strm.next_out = out;
  272.     while (len >= 5) {
  273.         if (strm.next_in[0] != 0)
  274.             break;
  275.         strm.avail_in = strm.next_in[1] + (strm.next_in[2] << 8);
  276.         strm.next_in += 5;
  277.         len -= 5;
  278.         if (strm.avail_in != 0) {
  279.             if (len < strm.avail_in)
  280.                 break;
  281.             len -= strm.avail_in;
  282.             (void)deflate(&strm, Z_NO_FLUSH);
  283.             if (strm.avail_in != 0 || strm.avail_out == 0)
  284.                 break;
  285.         }
  286.     }
  287.     (void)deflate(&strm, Z_SYNC_FLUSH);
  288.     (void)deflateEnd(&strm);
  289.     free(in);
  290.     if (len != 0 || strm.avail_out == 0) {
  291.         free(out);
  292.         return 1;
  293.     }
  294.     /* overwrite stored data with compressed data */
  295.     (void)lseek(log->fd, log->mark_off, SEEK_SET);
  296.     len = max - strm.avail_out;
  297.     if (write(log->fd, out, len) != len) {
  298.         free(out);
  299.         return 1;
  300.     }
  301.     free(out);
  302.     /* write last empty block, crc, and length */
  303.     log->mark_off = log->last_off = lseek(log->fd, 0, SEEK_CUR);
  304.     temp[0] = 1;
  305.     dice_ulg(0xffffL << 16, temp + 1);
  306.     dice_ulg(log->crc, temp + 5);
  307.     dice_ulg(log->len, temp + 9);
  308.     if (write(log->fd, temp, 13) != 13)
  309.         return 1;
  310.     /* truncate file to discard remaining stored data and old trailer */
  311.     ftruncate(log->fd, lseek(log->fd, 0, SEEK_CUR));
  312.     /* update extra field to point to new last empty block */
  313.     (void)lseek(log->fd, log->extra, SEEK_SET);
  314.     dice_off(log->mark_off, temp);
  315.     dice_off(log->last_off, temp + 8);
  316.     if (write(log->fd, temp, 16) != 16)
  317.         return 1;
  318.     return 0;
  319. }
  320. /* maximum accumulation of stored blocks before compressing */
  321. #define MAX_STORED 1048576
  322. /* close log object */
  323. int gzlog_close(void *obj)
  324. {
  325.     unsigned char temp[8];
  326.     gz_log *log;
  327.     /* check object */
  328.     log = (gz_log *)obj;
  329.     if (log == NULL || log->id != GZLOGID)
  330.         return 1;
  331.     /* go to start of most recent block being written */
  332.     (void)lseek(log->fd, log->last_off, SEEK_SET);
  333.     /* if some stuff was put there, update block */
  334.     if (log->stored) {
  335.         temp[0] = 0;
  336.         dice_ulg(log->stored + ((unsigned long)(~log->stored) << 16),
  337.                  temp + 1);
  338.         if (write(log->fd, temp, 5) != 5)
  339.             return 1;
  340.         log->last_off = lseek(log->fd, log->stored, SEEK_CUR);
  341.     }
  342.     /* write last block (empty) */
  343.     if (write(log->fd, "010000377377", 5) != 5)
  344.         return 1;
  345.     /* write updated crc and uncompressed length */
  346.     dice_ulg(log->crc, temp);
  347.     dice_ulg(log->len, temp + 4);
  348.     if (write(log->fd, temp, 8) != 8)
  349.         return 1;
  350.     /* put offset of that last block in gzip extra block */
  351.     (void)lseek(log->fd, log->extra + 8, SEEK_SET);
  352.     dice_off(log->last_off, temp);
  353.     if (write(log->fd, temp, 8) != 8)
  354.         return 1;
  355.     /* if more than 1 MB stored, then time to compress it */
  356.     if (log->last_off - log->mark_off > MAX_STORED) {
  357.         if (recomp(log))
  358.             return 1;
  359.     }
  360.     /* unlock and close file */
  361.     log_clean(log);
  362.     return 0;
  363. }