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