rpm  5.2.1
gzdio.c
Go to the documentation of this file.
1 
6 #include "system.h"
7 
8 #if defined(NOTYET) || defined(__LCLINT__)
9 #include <stdbool.h>
10 #else
11 typedef enum { true = 1, false = 0 } bool;
12 #endif
13 
14 #include "rpmio_internal.h"
15 #include <rpmmacro.h>
16 #include <rpmcb.h>
17 
18 #if defined(WITH_ZLIB)
19 
20 /*@-noparams@*/
21 #include <zlib.h>
22 /*@=noparams@*/
23 
24 #include "debug.h"
25 
26 /*@access FD_t @*/
27 
28 #define GZDONLY(fd) assert(fdGetIo(fd) == gzdio)
29 
30 typedef struct cpio_state_s {
31  rpmuint32_t n; /* byte progress in cpio header */
32  rpmuint32_t mode; /* file attributes */
33  rpmuint32_t nlnk;
34  rpmuint32_t size;
35 } * cpio_state;
36 
37 #define RSYNC_WIN 4096
38 
39 typedef struct rsync_state_s {
40  rpmuint32_t n; /* number of elements in the window */
41  rpmuint32_t sum; /* current sum */
42  unsigned char win[RSYNC_WIN]; /* window elements */
43 } * rsync_state;
44 
45 typedef struct rpmGZFILE_s {
46  gzFile gz; /* gzFile is a pointer */
47  struct rsync_state_s rs;
48  struct cpio_state_s cs;
49  rpmuint32_t nb; /* bytes pending for sync */
50 } * rpmGZFILE; /* like FILE, to use with star */
51 
52 /* Should gzflush be called only after RSYNC_WIN boundaries? */
53 /*@unchecked@*/
54 static int enable_rsync = 1;
55 
56 /* =============================================================== */
57 /* from ../lib/cpio.h */
58 #define CPIO_NEWC_MAGIC "070701"
59 #define PHYS_HDR_SIZE 110
60 
61 #define OFFSET_MODE (sizeof(CPIO_NEWC_MAGIC)-1 + 1*8)
62 #define OFFSET_NLNK (sizeof(CPIO_NEWC_MAGIC)-1 + 4*8)
63 #define OFFSET_SIZE (sizeof(CPIO_NEWC_MAGIC)-1 + 6*8)
64 
65 static inline
66 int hex(char c)
67  /*@*/
68 {
69  if (c >= '0' && c <= '9')
70  return (int)(c - '0');
71  else if (c >= 'a' && c <= 'f')
72  return (int)(c - 'a') + 10;
73  else if (c >= 'A' && c <= 'F')
74  return (int)(c - 'A') + 10;
75  return -1;
76 }
77 
78 static inline
79 bool cpio_next(cpio_state s, unsigned char c)
80  /*@modifies s @*/
81 {
82  if (s->n >= sizeof(CPIO_NEWC_MAGIC)-1) {
83  int d = hex(c);
84  if (d < 0) {
85  s->n = 0;
86  return false;
87  }
88  if (0){} /* indent */
89  else if (s->n >= OFFSET_MODE && s->n < OFFSET_MODE+8) {
90  if (s->n == OFFSET_MODE)
91  s->mode = 0;
92  else
93  s->mode <<= 4;
94  s->mode |= d;
95  }
96  else if (s->n >= OFFSET_NLNK && s->n < OFFSET_NLNK+8) {
97  if (s->n == OFFSET_NLNK)
98  s->nlnk = 0;
99  else
100  s->nlnk <<= 4;
101  s->nlnk |= d;
102  }
103  else if (s->n >= OFFSET_SIZE && s->n < OFFSET_SIZE+8) {
104  if (s->n == OFFSET_SIZE)
105  s->size = 0;
106  else
107  s->size <<= 4;
108  s->size |= d;
109  }
110  s->n++;
111  if (s->n >= PHYS_HDR_SIZE) {
112  s->n = 0;
113  if (!S_ISREG(s->mode) || s->nlnk != 1)
114  /* no file data */
115  s->size = 0;
116  return true;
117  }
118  }
119  else if (CPIO_NEWC_MAGIC[s->n] == c) {
120  s->n++;
121  }
122  else {
123  s->n = 0;
124  }
125  return false;
126 }
127 
128 /* =============================================================== */
129 static inline
130 bool rsync_next(rsync_state s, unsigned char c)
131  /*@modifies s @*/
132 {
133  rpmuint32_t i;
134 
135  if (s->n < RSYNC_WIN) { /* not enough elements */
136  s->sum += (rpmuint32_t)c; /* update the sum */
137  s->win[s->n++] = c; /* remember the element */
138  return false; /* no match */
139  }
140  i = s->n++ % RSYNC_WIN; /* wrap up */
141  s->sum -= (rpmuint32_t)s->win[i]; /* move the window on */
142  s->sum += (rpmuint32_t)c;
143  s->win[i] = c;
144  if (s->sum % RSYNC_WIN == 0) { /* match */
145  s->n = 0; /* reset */
146  s->sum = 0;
147  return true;
148  }
149  return false;
150 }
151 
152 #define CHUNK 4096
153 
154 static inline
155 bool sync_hint(rpmGZFILE rpmgz, unsigned char c)
156  /*@modifies rpmgz @*/
157 {
158  bool cpio_hint;
159  bool rsync_hint;
160 
161  rpmgz->nb++;
162  cpio_hint = cpio_next(&rpmgz->cs, c);
163  if (cpio_hint) {
164  /* cpio header/data boundary */
165  rpmgz->rs.n = rpmgz->rs.sum = 0;
166  if (rpmgz->nb >= 2*CHUNK)
167  /* better sync here */
168  goto cpio_sync;
169  if (rpmgz->cs.size < CHUNK)
170  /* file is too small */
171  return false;
172  if (rpmgz->nb < CHUNK/2)
173  /* not enough pending bytes */
174  return false;
175  cpio_sync:
176  rpmgz->nb = 0;
177  return true;
178  }
179  rsync_hint = rsync_next(&rpmgz->rs, c);
180  if (rsync_hint) {
181  /* rolling checksum match */
182  assert(rpmgz->nb >= RSYNC_WIN);
183  rpmgz->nb = 0;
184  return true;
185  }
186  return false;
187 }
188 
189 static ssize_t
190 rsyncable_gzwrite(rpmGZFILE rpmgz, const unsigned char *const buf, const size_t len)
191  /*@modifies rpmgz @*/
192 {
193  ssize_t rc;
194  size_t n;
195  ssize_t n_written = 0;
196  const unsigned char *begin = buf;
197  size_t i;
198 
199  for (i = 0; i < len; i++) {
200  if (!sync_hint(rpmgz, buf[i]))
201  continue;
202  n = i + 1 - (begin - buf);
203  rc = gzwrite(rpmgz->gz, begin, (unsigned)n);
204  if (rc < 0)
205  return (n_written ? n_written : rc);
206  n_written += rc;
207  if (rc < (ssize_t)n)
208  return n_written;
209  begin += n;
210  rc = gzflush(rpmgz->gz, Z_SYNC_FLUSH);
211  if (rc < 0)
212  return (n_written ? n_written : rc);
213  }
214  if (begin < buf + len) {
215  n = len - (begin - buf);
216  rc = gzwrite(rpmgz->gz, begin, (unsigned)n);
217  if (rc < 0)
218  return (n_written ? n_written : rc);
219  n_written += rc;
220  }
221  return n_written;
222 }
223 
224 /* =============================================================== */
225 /*@-moduncon@*/
226 
227 static inline /*@dependent@*/ /*@null@*/ void * gzdFileno(FD_t fd)
228  /*@*/
229 {
230  void * rc = NULL;
231  int i;
232 
233  FDSANE(fd);
234  for (i = fd->nfps; i >= 0; i--) {
235  FDSTACK_t * fps = &fd->fps[i];
236  if (fps->io != gzdio)
237  continue;
238  rc = fps->fp;
239  break;
240  }
241 
242  return rc;
243 }
244 
245 static /*@null@*/
246 FD_t gzdOpen(const char * path, const char * fmode)
247  /*@globals fileSystem, internalState @*/
248  /*@modifies fileSystem, internalState @*/
249 {
250  FD_t fd;
251  rpmGZFILE rpmgz;
252  mode_t mode = (fmode && fmode[0] == 'w' ? O_WRONLY : O_RDONLY);
253 
254  rpmgz = xcalloc(1, sizeof(*rpmgz));
255  rpmgz->gz = gzopen(path, fmode);
256  if (rpmgz->gz == NULL) {
257  rpmgz = _free(rpmgz);
258  return NULL;
259  }
260  fd = fdNew("open (gzdOpen)");
261  fdPop(fd); fdPush(fd, gzdio, rpmgz, -1);
262  fdSetOpen(fd, path, -1, mode);
263 
264 DBGIO(fd, (stderr, "==>\tgzdOpen(\"%s\", \"%s\") fd %p %s\n", path, fmode, (fd ? fd : NULL), fdbg(fd)));
265  return fdLink(fd, "gzdOpen");
266 }
267 
268 static /*@null@*/ FD_t gzdFdopen(void * cookie, const char *fmode)
269  /*@globals fileSystem, internalState @*/
270  /*@modifies fileSystem, internalState @*/
271 {
272  FD_t fd = c2f(cookie);
273  int fdno;
274  rpmGZFILE rpmgz;
275 
276  if (fmode == NULL) return NULL;
277  fdno = fdFileno(fd);
278  fdSetFdno(fd, -1); /* XXX skip the fdio close */
279  if (fdno < 0) return NULL;
280  rpmgz = xcalloc(1, sizeof(*rpmgz));
281  rpmgz->gz = gzdopen(fdno, fmode);
282  if (rpmgz->gz == NULL) {
283  rpmgz = _free(rpmgz);
284  return NULL;
285  }
286 
287  fdPush(fd, gzdio, rpmgz, fdno); /* Push gzdio onto stack */
288 
289  return fdLink(fd, "gzdFdopen");
290 }
291 
292 static int gzdFlush(void * cookie)
293  /*@*/
294 {
295  FD_t fd = c2f(cookie);
296  rpmGZFILE rpmgz;
297  rpmgz = gzdFileno(fd);
298  if (rpmgz == NULL) return -2;
299  return gzflush(rpmgz->gz, Z_SYNC_FLUSH); /* XXX W2DO? */
300 }
301 
302 /* =============================================================== */
303 /*@-mustmod@*/
304 static ssize_t gzdRead(void * cookie, /*@out@*/ char * buf, size_t count)
305  /*@globals fileSystem, internalState @*/
306  /*@modifies buf, fileSystem, internalState @*/
307 {
308  FD_t fd = c2f(cookie);
309  rpmGZFILE rpmgz;
310  ssize_t rc;
311 
312  if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
313 
314  rpmgz = gzdFileno(fd);
315  if (rpmgz == NULL) return -2; /* XXX can't happen */
316 
318  rc = gzread(rpmgz->gz, buf, (unsigned)count);
319 DBGIO(fd, (stderr, "==>\tgzdRead(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
320  if (rc < 0) {
321  int zerror = 0;
322  fd->errcookie = gzerror(rpmgz->gz, &zerror);
323  if (zerror == Z_ERRNO) {
324  fd->syserrno = errno;
325  fd->errcookie = strerror(fd->syserrno);
326  }
327  } else {
328  fdstat_exit(fd, FDSTAT_READ, (rc > 0 ? rc : 0));
329  if (fd->ndigests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
330  }
331  return rc;
332 }
333 /*@=mustmod@*/
334 
335 static ssize_t gzdWrite(void * cookie, const char * buf, size_t count)
336  /*@globals fileSystem, internalState @*/
337  /*@modifies fileSystem, internalState @*/
338 {
339  FD_t fd = c2f(cookie);
340  rpmGZFILE rpmgz;
341  ssize_t rc;
342 
343  if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
344 
345  if (fd->ndigests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
346 
347  rpmgz = gzdFileno(fd);
348  if (rpmgz == NULL) return -2; /* XXX can't happen */
349 
351  if (enable_rsync)
352  rc = rsyncable_gzwrite(rpmgz, (void *)buf, (unsigned)count);
353  else
354  rc = gzwrite(rpmgz->gz, (void *)buf, (unsigned)count);
355 DBGIO(fd, (stderr, "==>\tgzdWrite(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
356  if (rc < (ssize_t)count) {
357  int zerror = 0;
358  fd->errcookie = gzerror(rpmgz->gz, &zerror);
359  if (zerror == Z_ERRNO) {
360  fd->syserrno = errno;
361  fd->errcookie = strerror(fd->syserrno);
362  }
363  }
364  if (rc > 0)
365  fdstat_exit(fd, FDSTAT_WRITE, rc);
366  return rc;
367 }
368 
369 /* XXX zlib-1.0.4 has not */
370 #define HAVE_GZSEEK /* XXX autoFu doesn't set this anymore. */
371 static inline int gzdSeek(void * cookie, _libio_pos_t pos, int whence)
372  /*@globals fileSystem, internalState @*/
373  /*@modifies fileSystem, internalState @*/
374 {
375  int rc;
376 #if defined(HAVE_GZSEEK)
377 #ifdef USE_COOKIE_SEEK_POINTER
378  _IO_off64_t p = *pos;
379 #else
380  off_t p = pos;
381 #endif
382  FD_t fd = c2f(cookie);
383  rpmGZFILE rpmgz;
384 
385  if (fd == NULL) return -2;
386  assert(fd->bytesRemain == -1); /* XXX FIXME */
387 
388  rpmgz = gzdFileno(fd);
389  if (rpmgz == NULL) return -2; /* XXX can't happen */
390 
392  rc = gzseek(rpmgz->gz, (long)p, whence);
393 DBGIO(fd, (stderr, "==>\tgzdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
394  if (rc < 0) {
395  int zerror = 0;
396  fd->errcookie = gzerror(rpmgz->gz, &zerror);
397  if (zerror == Z_ERRNO) {
398  fd->syserrno = errno;
399  fd->errcookie = strerror(fd->syserrno);
400  }
401  }
402  if (rc > 0)
403  fdstat_exit(fd, FDSTAT_SEEK, rc);
404 #else
405  rc = -2;
406 #endif
407  return rc;
408 }
409 
410 static int gzdClose( /*@only@*/ void * cookie)
411  /*@globals fileSystem, internalState @*/
412  /*@modifies fileSystem, internalState @*/
413 {
414  FD_t fd = c2f(cookie);
415  rpmGZFILE rpmgz;
416  int rc;
417 
418  rpmgz = gzdFileno(fd);
419  if (rpmgz == NULL) return -2; /* XXX can't happen */
420 
422  /*@-dependenttrans@*/
423  rc = gzclose(rpmgz->gz);
424  /*@=dependenttrans@*/
425  rpmgz->gz = NULL;
426 /*@-dependenttrans@*/
427  rpmgz = _free(rpmgz);
428 /*@=dependenttrans@*/
429 
430  /* XXX TODO: preserve fd if errors */
431 
432  if (fd) {
433 DBGIO(fd, (stderr, "==>\tgzdClose(%p) zerror %d %s\n", cookie, rc, fdbg(fd)));
434  if (rc < 0) {
435  fd->errcookie = "gzclose error";
436  if (rc == Z_ERRNO) {
437  fd->syserrno = errno;
438  fd->errcookie = strerror(fd->syserrno);
439  }
440  } else if (rc >= 0) {
441  fdstat_exit(fd, FDSTAT_CLOSE, rc);
442  }
443  }
444 
445 DBGIO(fd, (stderr, "==>\tgzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
446 
447  if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "GZDIO", stderr);
448  if (rc == 0)
449  fd = fdFree(fd, "open (gzdClose)");
450  return rc;
451 }
452 
453 /*@-type@*/ /* LCL: function typedefs */
454 static struct FDIO_s gzdio_s = {
455  gzdRead, gzdWrite, gzdSeek, gzdClose, gzdOpen, gzdFdopen, gzdFlush,
456 };
457 /*@=type@*/
458 
459 FDIO_t gzdio = /*@-compmempass@*/ &gzdio_s /*@=compmempass@*/ ;
460 
461 /*@=moduncon@*/
462 #endif /* WITH_ZLIB */