~ubuntu-branches/ubuntu/edgy/rpm/edgy

« back to all changes in this revision

Viewing changes to rpmio/rpmio.c

  • Committer: Bazaar Package Importer
  • Author(s): Joey Hess
  • Date: 2002-01-22 20:56:57 UTC
  • Revision ID: james.westby@ubuntu.com-20020122205657-l74j50mr9z8ofcl5
Tags: upstream-4.0.3
ImportĀ upstreamĀ versionĀ 4.0.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** \ingroup rpmio
 
2
 * \file rpmio/rpmio.c
 
3
 */
 
4
 
 
5
#include "system.h"
 
6
#include <stdarg.h>
 
7
 
 
8
#ifdef  __LCLINT__
 
9
/*@-incondefs@*/
 
10
typedef unsigned int            uint32_t;
 
11
/*@=incondefs@*/
 
12
#define INADDR_ANY              ((uint32_t) 0x00000000)
 
13
#define IPPROTO_IP              0
 
14
 
 
15
#else   /* __LCLINT__ */
 
16
 
 
17
#if HAVE_MACHINE_TYPES_H
 
18
# include <machine/types.h>
 
19
#endif
 
20
 
 
21
#include <netinet/in.h>
 
22
#include <arpa/inet.h>          /* XXX for inet_aton and HP-UX */
 
23
 
 
24
#if HAVE_NETINET_IN_SYSTM_H
 
25
# include <sys/types.h>
 
26
# include <netinet/in_systm.h>
 
27
#endif
 
28
 
 
29
#if HAVE_LIBIO_H && defined(_G_IO_IO_FILE_VERSION)
 
30
#define _USE_LIBIO      1
 
31
#endif
 
32
 
 
33
#endif  /* __LCLINT__ */
 
34
 
 
35
#if !defined(HAVE_HERRNO) && defined(__hpux) /* XXX HP-UX w/o -D_XOPEN_SOURCE needs */
 
36
extern int h_errno;
 
37
#endif
 
38
 
 
39
#ifndef IPPORT_FTP
 
40
#define IPPORT_FTP      21
 
41
#endif
 
42
#ifndef IPPORT_HTTP
 
43
#define IPPORT_HTTP     80
 
44
#endif
 
45
 
 
46
#if !defined(HAVE_INET_ATON)
 
47
static int inet_aton(const char *cp, struct in_addr *inp)
 
48
{
 
49
    long addr;
 
50
 
 
51
    addr = inet_addr(cp);
 
52
    if (addr == ((long) -1)) return 0;
 
53
 
 
54
    memcpy(inp, &addr, sizeof(addr));
 
55
    return 1;
 
56
}
 
57
#endif
 
58
 
 
59
#if defined(USE_ALT_DNS) && USE_ALT_DNS
 
60
#include "dns.h"
 
61
#endif
 
62
 
 
63
#include <rpmio_internal.h>
 
64
#undef  fdFileno
 
65
#undef  fdOpen
 
66
#undef  fdRead
 
67
#undef  fdWrite
 
68
#undef  fdClose
 
69
 
 
70
#include "ugid.h"
 
71
#include "rpmmessages.h"
 
72
 
 
73
#include "debug.h"
 
74
 
 
75
/*@access urlinfo @*/
 
76
/*@access FDSTAT_t @*/
 
77
 
 
78
#define FDNREFS(fd)     (fd ? ((FD_t)fd)->nrefs : -9)
 
79
#define FDTO(fd)        (fd ? ((FD_t)fd)->rd_timeoutsecs : -99)
 
80
#define FDCPIOPOS(fd)   (fd ? ((FD_t)fd)->fd_cpioPos : -99)
 
81
 
 
82
#define FDONLY(fd)      assert(fdGetIo(fd) == fdio)
 
83
#define GZDONLY(fd)     assert(fdGetIo(fd) == gzdio)
 
84
#define BZDONLY(fd)     assert(fdGetIo(fd) == bzdio)
 
85
 
 
86
#define UFDONLY(fd)     /* assert(fdGetIo(fd) == ufdio) */
 
87
 
 
88
#define fdGetFILE(_fd)  ((FILE *)fdGetFp(_fd))
 
89
 
 
90
#if _USE_LIBIO
 
91
int noLibio = 0;
 
92
#else
 
93
int noLibio = 1;
 
94
#endif
 
95
 
 
96
#define TIMEOUT_SECS 60
 
97
static int ftpTimeoutSecs = TIMEOUT_SECS;
 
98
static int httpTimeoutSecs = TIMEOUT_SECS;
 
99
 
 
100
int _ftp_debug = 0;
 
101
int _rpmio_debug = 0;
 
102
 
 
103
/**
 
104
 * Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
 
105
 * @param p             memory to free
 
106
 * @retval              NULL always
 
107
 */
 
108
/*@unused@*/ static inline /*@null@*/ void *
 
109
_free(/*@only@*/ /*@null@*/ const void * p)
 
110
        /*@modifies p@*/
 
111
{
 
112
    if (p != NULL)      free((void *)p);
 
113
    return NULL;
 
114
}
 
115
 
 
116
/* =============================================================== */
 
117
 
 
118
static /*@observer@*/ const char * fdbg(/*@null@*/ FD_t fd)
 
119
        /*@modifies fileSystem @*/
 
120
{
 
121
    static char buf[BUFSIZ];
 
122
    char *be = buf;
 
123
    int i;
 
124
 
 
125
    buf[0] = '\0';
 
126
    if (fd == NULL)
 
127
        return buf;
 
128
 
 
129
#if DYING
 
130
    sprintf(be, "fd %p", fd);   be += strlen(be);
 
131
    if (fd->rd_timeoutsecs >= 0) {
 
132
        sprintf(be, " secs %d", fd->rd_timeoutsecs);
 
133
        be += strlen(be);
 
134
    }
 
135
#endif
 
136
    if (fd->bytesRemain != -1) {
 
137
        sprintf(be, " clen %d", (int)fd->bytesRemain);
 
138
        be += strlen(be);
 
139
     }
 
140
    if (fd->wr_chunked) {
 
141
        strcpy(be, " chunked");
 
142
        be += strlen(be);
 
143
     }
 
144
    *be++ = '\t';
 
145
    for (i = fd->nfps; i >= 0; i--) {
 
146
        FDSTACK_t * fps = &fd->fps[i];
 
147
        if (i != fd->nfps)
 
148
            *be++ = ' ';
 
149
        *be++ = '|';
 
150
        *be++ = ' ';
 
151
        if (fps->io == fdio) {
 
152
            sprintf(be, "FD %d fp %p", fps->fdno, fps->fp);
 
153
        } else if (fps->io == ufdio) {
 
154
            sprintf(be, "UFD %d fp %p", fps->fdno, fps->fp);
 
155
        } else if (fps->io == fadio) {
 
156
            sprintf(be, "FAD %d fp %p", fps->fdno, fps->fp);
 
157
        } else if (fps->io == gzdio) {
 
158
            sprintf(be, "GZD %p fdno %d", fps->fp, fps->fdno);
 
159
#if HAVE_BZLIB_H
 
160
        } else if (fps->io == bzdio) {
 
161
            sprintf(be, "BZD %p fdno %d", fps->fp, fps->fdno);
 
162
#endif
 
163
        } else if (fps->io == fpio) {
 
164
            /*@+voidabstract@*/
 
165
            sprintf(be, "%s %p(%d) fdno %d",
 
166
                (fps->fdno < 0 ? "LIBIO" : "FP"),
 
167
                fps->fp, fileno(((FILE *)fps->fp)), fps->fdno);
 
168
            /*@=voidabstract@*/
 
169
        } else {
 
170
            sprintf(be, "??? io %p fp %p fdno %d ???",
 
171
                fps->io, fps->fp, fps->fdno);
 
172
        }
 
173
        be += strlen(be);
 
174
        *be = '\0';
 
175
    }
 
176
    return buf;
 
177
}
 
178
 
 
179
/* =============================================================== */
 
180
off_t fdSize(FD_t fd)
 
181
{
 
182
    struct stat sb;
 
183
    off_t rc = -1; 
 
184
 
 
185
#ifdef  NOISY
 
186
DBGIO(0, (stderr, "==>\tfdSize(%p) rc %ld\n", fd, (long)rc));
 
187
#endif
 
188
    FDSANE(fd);
 
189
    if (fd->contentLength >= 0)
 
190
        rc = fd->contentLength;
 
191
    else switch (fd->urlType) {
 
192
    case URL_IS_PATH:
 
193
    case URL_IS_UNKNOWN:
 
194
        if (fstat(Fileno(fd), &sb) == 0)
 
195
            rc = sb.st_size;
 
196
        /*@fallthrough@*/
 
197
    case URL_IS_FTP:
 
198
    case URL_IS_HTTP:
 
199
    case URL_IS_DASH:
 
200
        break;
 
201
    }
 
202
    return rc;
 
203
}
 
204
 
 
205
FD_t fdDup(int fdno)
 
206
{
 
207
    FD_t fd;
 
208
    int nfdno;
 
209
 
 
210
    if ((nfdno = dup(fdno)) < 0)
 
211
        return NULL;
 
212
    fd = fdNew("open (fdDup)");
 
213
    fdSetFdno(fd, nfdno);
 
214
DBGIO(fd, (stderr, "==> fdDup(%d) fd %p %s\n", fdno, (fd ? fd : NULL), fdbg(fd)));
 
215
    /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
 
216
}
 
217
 
 
218
static inline /*@unused@*/ int fdSeekNot(void * cookie,
 
219
                /*@unused@*/ _libio_pos_t pos,  /*@unused@*/ int whence)
 
220
        /*@*/
 
221
{
 
222
    FD_t fd = c2f(cookie);
 
223
    FDSANE(fd);         /* XXX keep gcc quiet */
 
224
    return -2;
 
225
}
 
226
 
 
227
#ifdef UNUSED
 
228
FILE *fdFdopen(void * cookie, const char *fmode)
 
229
{
 
230
    FD_t fd = c2f(cookie);
 
231
    int fdno;
 
232
    FILE * fp;
 
233
 
 
234
    if (fmode == NULL) return NULL;
 
235
    fdno = fdFileno(fd);
 
236
    if (fdno < 0) return NULL;
 
237
    fp = fdopen(fdno, fmode);
 
238
DBGIO(fd, (stderr, "==> fdFdopen(%p,\"%s\") fdno %d -> fp %p fdno %d\n", cookie, fmode, fdno, fp, fileno(fp)));
 
239
    fd = fdFree(fd, "open (fdFdopen)");
 
240
    return fp;
 
241
}
 
242
#endif
 
243
 
 
244
#if 0
 
245
#undef  fdLink
 
246
#undef  fdFree
 
247
#undef  fdNew
 
248
#endif
 
249
 
 
250
/* =============================================================== */
 
251
static inline /*@null@*/ FD_t XfdLink(void * cookie, const char * msg,
 
252
                const char * file, unsigned line)
 
253
        /*@modifies internalState @*/
 
254
{
 
255
    FD_t fd;
 
256
if (cookie == NULL)
 
257
    /*@-castexpose@*/
 
258
DBGREFS(0, (stderr, "--> fd  %p ++ %d %s at %s:%u\n", cookie, FDNREFS(cookie)+1, msg, file, line));
 
259
    /*@=castexpose@*/
 
260
    fd = c2f(cookie);
 
261
    if (fd) {
 
262
        fd->nrefs++;
 
263
DBGREFS(fd, (stderr, "--> fd  %p ++ %d %s at %s:%u %s\n", fd, fd->nrefs, msg, file, line, fdbg(fd)));
 
264
    }
 
265
    return fd;
 
266
}
 
267
 
 
268
static inline /*@null@*/ FD_t XfdFree( /*@killref@*/ FD_t fd, const char *msg,
 
269
                const char *file, unsigned line)
 
270
        /*@modifies fd @*/
 
271
{
 
272
if (fd == NULL)
 
273
DBGREFS(0, (stderr, "--> fd  %p -- %d %s at %s:%u\n", fd, FDNREFS(fd), msg, file, line));
 
274
    FDSANE(fd);
 
275
    if (fd) {
 
276
DBGREFS(fd, (stderr, "--> fd  %p -- %d %s at %s:%u %s\n", fd, fd->nrefs, msg, file, line, fdbg(fd)));
 
277
        if (--fd->nrefs > 0)
 
278
            /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
 
279
        fd->stats = _free(fd->stats);
 
280
        fd->digest = _free(fd->digest);
 
281
        /*@-refcounttrans@*/ free(fd); /*@=refcounttrans@*/
 
282
    }
 
283
    return NULL;
 
284
}
 
285
 
 
286
static inline /*@null@*/ FD_t XfdNew(const char * msg,
 
287
                const char * file, unsigned line)
 
288
        /*@*/
 
289
{
 
290
    FD_t fd = (FD_t) xmalloc(sizeof(struct _FD_s));
 
291
    if (fd == NULL) /* XXX xmalloc never returns NULL */
 
292
        return NULL;
 
293
    fd->nrefs = 0;
 
294
    fd->flags = 0;
 
295
    fd->magic = FDMAGIC;
 
296
    fd->urlType = URL_IS_UNKNOWN;
 
297
 
 
298
    fd->nfps = 0;
 
299
    memset(fd->fps, 0, sizeof(fd->fps));
 
300
 
 
301
    /*@-assignexpose@*/
 
302
    fd->fps[0].io = fdio;
 
303
    /*@=assignexpose@*/
 
304
    fd->fps[0].fp = NULL;
 
305
    fd->fps[0].fdno = -1;
 
306
 
 
307
    fd->url = NULL;
 
308
    fd->rd_timeoutsecs = 1;     /* XXX default value used to be -1 */
 
309
    fd->contentLength = fd->bytesRemain = -1;
 
310
    fd->wr_chunked = 0;
 
311
    fd->syserrno = 0;
 
312
    fd->errcookie = NULL;
 
313
    fd->stats = xcalloc(1, sizeof(*fd->stats));
 
314
    fd->digest = NULL;
 
315
    (void) gettimeofday(&fd->stats->create, NULL);
 
316
    fd->stats->begin = fd->stats->create;       /* structure assignment */
 
317
 
 
318
    fd->ftpFileDoneNeeded = 0;
 
319
    fd->firstFree = 0;
 
320
    fd->fileSize = 0;
 
321
    fd->fd_cpioPos = 0;
 
322
 
 
323
    return XfdLink(fd, msg, file, line);
 
324
}
 
325
 
 
326
/*@-redef@*/    /* FIX: legacy API should be made static */
 
327
ssize_t fdRead(void * cookie, /*@out@*/ char * buf, size_t count)
 
328
/*@=redef@*/
 
329
{
 
330
    FD_t fd = c2f(cookie);
 
331
    ssize_t rc;
 
332
 
 
333
    if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
 
334
 
 
335
    fdstat_enter(fd, FDSTAT_READ);
 
336
    rc = read(fdFileno(fd), buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
 
337
    fdstat_exit(fd, FDSTAT_READ, rc);
 
338
 
 
339
    if (fd->digest && rc > 0) rpmDigestUpdate(fd->digest, buf, rc);
 
340
 
 
341
DBGIO(fd, (stderr, "==>\tfdRead(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
 
342
 
 
343
    return rc;
 
344
}
 
345
 
 
346
/*@-redef@*/    /* FIX: legacy API should be made static */
 
347
ssize_t fdWrite(void * cookie, const char * buf, size_t count)
 
348
/*@=redef@*/
 
349
{
 
350
    FD_t fd = c2f(cookie);
 
351
    int fdno = fdFileno(fd);
 
352
    ssize_t rc;
 
353
 
 
354
    if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
 
355
 
 
356
    if (fd->digest && count > 0) rpmDigestUpdate(fd->digest, buf, count);
 
357
 
 
358
    if (fd->wr_chunked) {
 
359
        char chunksize[20];
 
360
        sprintf(chunksize, "%x\r\n", (unsigned)count);
 
361
        rc = write(fdno, chunksize, strlen(chunksize));
 
362
        if (rc == -1)   fd->syserrno = errno;
 
363
    }
 
364
    if (count == 0) return 0;
 
365
 
 
366
    fdstat_enter(fd, FDSTAT_WRITE);
 
367
    rc = write(fdno, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
 
368
    fdstat_exit(fd, FDSTAT_WRITE, rc);
 
369
 
 
370
    if (fd->wr_chunked) {
 
371
        int ec;
 
372
        ec = write(fdno, "\r\n", sizeof("\r\n")-1);
 
373
        if (ec == -1)   fd->syserrno = errno;
 
374
    }
 
375
 
 
376
DBGIO(fd, (stderr, "==>\tfdWrite(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
 
377
 
 
378
    return rc;
 
379
}
 
380
 
 
381
static inline int fdSeek(void * cookie, _libio_pos_t pos, int whence)
 
382
        /*@modifies internalState, fileSystem @*/
 
383
{
 
384
#ifdef USE_COOKIE_SEEK_POINTER
 
385
    _IO_off64_t p = *pos;
 
386
#else
 
387
    off_t p = pos;
 
388
#endif
 
389
    FD_t fd = c2f(cookie);
 
390
    off_t rc;
 
391
 
 
392
    assert(fd->bytesRemain == -1);      /* XXX FIXME fadio only for now */
 
393
    fdstat_enter(fd, FDSTAT_SEEK);
 
394
    rc = lseek(fdFileno(fd), p, whence);
 
395
    fdstat_exit(fd, FDSTAT_SEEK, rc);
 
396
 
 
397
DBGIO(fd, (stderr, "==>\tfdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
 
398
 
 
399
    return rc;
 
400
}
 
401
 
 
402
/*@-redef@*/    /* FIX: legacy API should be made static */
 
403
int fdClose( /*@only@*/ void * cookie)
 
404
/*@=redef@*/
 
405
{
 
406
    FD_t fd;
 
407
    int fdno;
 
408
    int rc;
 
409
 
 
410
    if (cookie == NULL) return -2;
 
411
    fd = c2f(cookie);
 
412
    fdno = fdFileno(fd);
 
413
 
 
414
    fdSetFdno(fd, -1);
 
415
 
 
416
    fdstat_enter(fd, FDSTAT_CLOSE);
 
417
    rc = ((fdno >= 0) ? close(fdno) : -2);
 
418
    fdstat_exit(fd, FDSTAT_CLOSE, rc);
 
419
 
 
420
DBGIO(fd, (stderr, "==>\tfdClose(%p) rc %lx %s\n", (fd ? fd : NULL), (unsigned long)rc, fdbg(fd)));
 
421
 
 
422
    fd = fdFree(fd, "open (fdClose)");
 
423
    return rc;
 
424
}
 
425
 
 
426
/*@-redef@*/    /* FIX: legacy API should be made static */
 
427
/*@null@*/ FD_t fdOpen(const char *path, int flags, mode_t mode)
 
428
/*@=redef@*/
 
429
{
 
430
    FD_t fd;
 
431
    int fdno;
 
432
 
 
433
    fdno = open(path, flags, mode);
 
434
    if (fdno < 0) return NULL;
 
435
    fd = fdNew("open (fdOpen)");
 
436
    fdSetFdno(fd, fdno);
 
437
    fd->flags = flags;
 
438
DBGIO(fd, (stderr, "==>\tfdOpen(\"%s\",%x,0%o) %s\n", path, (unsigned)flags, (unsigned)mode, fdbg(fd)));
 
439
    /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
 
440
}
 
441
 
 
442
static struct FDIO_s fdio_s = {
 
443
  fdRead, fdWrite, fdSeek, fdClose, XfdLink, XfdFree, XfdNew, fdFileno,
 
444
  fdOpen, NULL, fdGetFp, NULL,  mkdir, chdir, rmdir, rename, unlink
 
445
};
 
446
FDIO_t fdio = /*@-compmempass@*/ &fdio_s /*@=compmempass@*/ ;
 
447
 
 
448
/*@-redef@*/    /* see lib/falloc.c */
 
449
FDIO_t fadio;   /* XXX usually NULL, filled in when linked with rpm */
 
450
/*@=redef@*/
 
451
 
 
452
int fdWritable(FD_t fd, int secs)
 
453
{
 
454
    int fdno;
 
455
    fd_set wrfds;
 
456
    struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
 
457
    int rc;
 
458
        
 
459
    if ((fdno = fdFileno(fd)) < 0)
 
460
        return -1;      /* XXX W2DO? */
 
461
        
 
462
    FD_ZERO(&wrfds);
 
463
    do {
 
464
        FD_SET(fdno, &wrfds);
 
465
 
 
466
        if (tvp) {
 
467
            tvp->tv_sec = secs;
 
468
            tvp->tv_usec = 0;
 
469
        }
 
470
        errno = 0;
 
471
        /*@-compdef -nullpass@*/
 
472
        rc = select(fdno + 1, NULL, &wrfds, NULL, tvp);
 
473
        /*@=compdef =nullpass@*/
 
474
 
 
475
if (_rpmio_debug && !(rc == 1 && errno == 0))
 
476
fprintf(stderr, "*** fdWritable fdno %d rc %d %s\n", fdno, rc, strerror(errno));
 
477
        if (rc < 0) {
 
478
            switch (errno) {
 
479
            case EINTR:
 
480
                continue;
 
481
                /*@notreached@*/ break;
 
482
            default:
 
483
                return rc;
 
484
                /*@notreached@*/ break;
 
485
            }
 
486
        }
 
487
        return rc;
 
488
    } while (1);
 
489
    /*@notreached@*/
 
490
}
 
491
 
 
492
int fdReadable(FD_t fd, int secs)
 
493
{
 
494
    int fdno;
 
495
    fd_set rdfds;
 
496
    struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
 
497
    int rc;
 
498
 
 
499
    if ((fdno = fdFileno(fd)) < 0)
 
500
        return -1;      /* XXX W2DO? */
 
501
        
 
502
    FD_ZERO(&rdfds);
 
503
    do {
 
504
        FD_SET(fdno, &rdfds);
 
505
 
 
506
        if (tvp) {
 
507
            tvp->tv_sec = secs;
 
508
            tvp->tv_usec = 0;
 
509
        }
 
510
        errno = 0;
 
511
        /*@-compdef -nullpass@*/
 
512
        rc = select(fdno + 1, &rdfds, NULL, NULL, tvp);
 
513
        /*@=compdef =nullpass@*/
 
514
 
 
515
        if (rc < 0) {
 
516
            switch (errno) {
 
517
            case EINTR:
 
518
                continue;
 
519
                /*@notreached@*/ break;
 
520
            default:
 
521
                return rc;
 
522
                /*@notreached@*/ break;
 
523
            }
 
524
        }
 
525
        return rc;
 
526
    } while (1);
 
527
    /*@notreached@*/
 
528
}
 
529
 
 
530
int fdFgets(FD_t fd, char * buf, size_t len)
 
531
{
 
532
    int fdno;
 
533
    int secs = fd->rd_timeoutsecs;
 
534
    size_t nb = 0;
 
535
    int ec = 0;
 
536
    char lastchar = '\0';
 
537
 
 
538
    if ((fdno = fdFileno(fd)) < 0)
 
539
        return 0;       /* XXX W2DO? */
 
540
        
 
541
    do {
 
542
        int rc;
 
543
 
 
544
        /* Is there data to read? */
 
545
        rc = fdReadable(fd, secs);
 
546
 
 
547
        switch (rc) {
 
548
        case -1:        /* error */
 
549
            ec = -1;
 
550
            continue;
 
551
            /*@notreached@*/ break;
 
552
        case  0:        /* timeout */
 
553
            ec = -1;
 
554
            continue;
 
555
            /*@notreached@*/ break;
 
556
        default:        /* data to read */
 
557
            break;
 
558
        }
 
559
 
 
560
        errno = 0;
 
561
#ifdef  NOISY
 
562
        rc = fdRead(fd, buf + nb, 1);
 
563
#else
 
564
        rc = read(fdFileno(fd), buf + nb, 1);
 
565
#endif
 
566
        if (rc < 0) {
 
567
            fd->syserrno = errno;
 
568
            switch (errno) {
 
569
            case EWOULDBLOCK:
 
570
                continue;
 
571
                /*@notreached@*/ break;
 
572
            default:
 
573
                break;
 
574
            }
 
575
if (_rpmio_debug)
 
576
fprintf(stderr, "*** read: fd %p rc %d errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
 
577
            ec = -1;
 
578
            break;
 
579
        } else if (rc == 0) {
 
580
if (_rpmio_debug)
 
581
fprintf(stderr, "*** read: fd %p rc %d EOF errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
 
582
            break;
 
583
        } else {
 
584
            nb += rc;
 
585
            buf[nb] = '\0';
 
586
            lastchar = buf[nb - 1];
 
587
        }
 
588
    } while (ec == 0 && nb < len && lastchar != '\n');
 
589
 
 
590
    return (ec >= 0 ? nb : ec);
 
591
}
 
592
 
 
593
/* =============================================================== */
 
594
/* Support for FTP/HTTP I/O.
 
595
 */
 
596
const char *const ftpStrerror(int errorNumber) {
 
597
  switch (errorNumber) {
 
598
    case 0:
 
599
        return _("Success");
 
600
 
 
601
    case FTPERR_BAD_SERVER_RESPONSE:
 
602
        return _("Bad server response");
 
603
 
 
604
    case FTPERR_SERVER_IO_ERROR:
 
605
        return _("Server I/O error");
 
606
 
 
607
    case FTPERR_SERVER_TIMEOUT:
 
608
        return _("Server timeout");
 
609
 
 
610
    case FTPERR_BAD_HOST_ADDR:
 
611
        return _("Unable to lookup server host address");
 
612
 
 
613
    case FTPERR_BAD_HOSTNAME:
 
614
        return _("Unable to lookup server host name");
 
615
 
 
616
    case FTPERR_FAILED_CONNECT:
 
617
        return _("Failed to connect to server");
 
618
 
 
619
    case FTPERR_FAILED_DATA_CONNECT:
 
620
        return _("Failed to establish data connection to server");
 
621
 
 
622
    case FTPERR_FILE_IO_ERROR:
 
623
        return _("I/O error to local file");
 
624
 
 
625
    case FTPERR_PASSIVE_ERROR:
 
626
        return _("Error setting remote server to passive mode");
 
627
 
 
628
    case FTPERR_FILE_NOT_FOUND:
 
629
        return _("File not found on server");
 
630
 
 
631
    case FTPERR_NIC_ABORT_IN_PROGRESS:
 
632
        return _("Abort in progress");
 
633
 
 
634
    case FTPERR_UNKNOWN:
 
635
    default:
 
636
        return _("Unknown or unexpected error");
 
637
  }
 
638
}
 
639
 
 
640
const char *urlStrerror(const char *url)
 
641
{
 
642
    const char *retstr;
 
643
    switch (urlIsURL(url)) {
 
644
    case URL_IS_FTP:
 
645
    case URL_IS_HTTP:
 
646
    {   urlinfo u;
 
647
/* XXX This only works for httpReq/ftpLogin/ftpReq failures */
 
648
        if (urlSplit(url, &u) == 0) {
 
649
            retstr = ftpStrerror(u->openError);
 
650
        } else
 
651
            retstr = "Malformed URL";
 
652
    }   break;
 
653
    default:
 
654
        retstr = strerror(errno);
 
655
        break;
 
656
    }
 
657
    return retstr;
 
658
}
 
659
 
 
660
#if !defined(USE_ALT_DNS) || !USE_ALT_DNS 
 
661
static int mygethostbyname(const char * host,
 
662
                /*@out@*/ struct in_addr * address)
 
663
        /*@modifies *address, fileSystem @*/
 
664
{
 
665
    struct hostent * hostinfo;
 
666
 
 
667
    /*@-unrecog -multithreaded @*/
 
668
    hostinfo = gethostbyname(host);
 
669
    /*@=unrecog =multithreaded @*/
 
670
    if (!hostinfo) return 1;
 
671
 
 
672
    /*@-nullderef@*/
 
673
    memcpy(address, hostinfo->h_addr_list[0], sizeof(*address));
 
674
    /*@=nullderef@*/
 
675
    return 0;
 
676
}
 
677
#endif
 
678
 
 
679
static int getHostAddress(const char * host, /*@out@*/ struct in_addr * address)
 
680
        /*@modifies *address, fileSystem @*/
 
681
{
 
682
    if (xisdigit(host[0])) {
 
683
        if (! /*@-unrecog@*/ inet_aton(host, address) /*@=unrecog@*/ )
 
684
            return FTPERR_BAD_HOST_ADDR;
 
685
    } else {
 
686
        if (mygethostbyname(host, address)) {
 
687
            errno = /*@-unrecog@*/ h_errno /*@=unrecog@*/;
 
688
            return FTPERR_BAD_HOSTNAME;
 
689
        }
 
690
    }
 
691
    
 
692
    return 0;
 
693
}
 
694
 
 
695
static int tcpConnect(FD_t ctrl, const char * host, int port)
 
696
        /*@modifies ctrl, fileSystem @*/
 
697
{
 
698
    struct sockaddr_in sin;
 
699
    int fdno = -1;
 
700
    int rc;
 
701
 
 
702
    memset(&sin, 0, sizeof(sin));
 
703
    sin.sin_family = AF_INET;
 
704
    sin.sin_port = htons(port);
 
705
    sin.sin_addr.s_addr = INADDR_ANY;
 
706
    
 
707
  do {
 
708
    if ((rc = getHostAddress(host, &sin.sin_addr)) < 0)
 
709
        break;
 
710
 
 
711
    if ((fdno = socket(sin.sin_family, SOCK_STREAM, IPPROTO_IP)) < 0) {
 
712
        rc = FTPERR_FAILED_CONNECT;
 
713
        break;
 
714
    }
 
715
 
 
716
    if (connect(fdno, (struct sockaddr *) &sin, sizeof(sin))) {
 
717
        rc = FTPERR_FAILED_CONNECT;
 
718
        break;
 
719
    }
 
720
  } while (0);
 
721
 
 
722
    if (rc < 0)
 
723
        goto errxit;
 
724
 
 
725
if (_ftp_debug)
 
726
fprintf(stderr,"++ connect %s:%d on fdno %d\n",
 
727
/*@-unrecog@*/ inet_ntoa(sin.sin_addr) /*@=unrecog@*/ ,
 
728
(int)ntohs(sin.sin_port), fdno);
 
729
 
 
730
    fdSetFdno(ctrl, (fdno >= 0 ? fdno : -1));
 
731
    return 0;
 
732
 
 
733
errxit:
 
734
    /*@-observertrans@*/
 
735
    fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
 
736
    /*@=observertrans@*/
 
737
    if (fdno >= 0)
 
738
        (void) close(fdno);
 
739
    return rc;
 
740
}
 
741
 
 
742
static int checkResponse(void * uu, FD_t ctrl,
 
743
                /*@out@*/ int *ecp, /*@out@*/ char ** str)
 
744
        /*@modifies ctrl, *ecp, *str, fileSystem @*/
 
745
{
 
746
    urlinfo u = uu;
 
747
    char *buf;
 
748
    size_t bufAlloced;
 
749
    int bufLength = 0; 
 
750
    const char *s;
 
751
    char *se;
 
752
    int ec = 0;
 
753
    int moretodo = 1;
 
754
    char errorCode[4];
 
755
 
 
756
    URLSANE(u);
 
757
    if (u->bufAlloced == 0 || u->buf == NULL) {
 
758
        u->bufAlloced = url_iobuf_size;
 
759
        u->buf = xcalloc(u->bufAlloced, sizeof(char));
 
760
    }
 
761
    buf = u->buf;
 
762
    bufAlloced = u->bufAlloced;
 
763
    *buf = '\0';
 
764
 
 
765
    errorCode[0] = '\0';
 
766
    
 
767
    do {
 
768
        int rc;
 
769
 
 
770
        /*
 
771
         * Read next line from server.
 
772
         */
 
773
        se = buf + bufLength;
 
774
        *se = '\0';
 
775
        rc = fdFgets(ctrl, se, (bufAlloced - bufLength));
 
776
        if (rc < 0) {
 
777
            ec = FTPERR_BAD_SERVER_RESPONSE;
 
778
            continue;
 
779
        } else if (rc == 0 || fdWritable(ctrl, 0) < 1)
 
780
            moretodo = 0;
 
781
 
 
782
        /*
 
783
         * Process next line from server.
 
784
         */
 
785
        for (s = se; *s != '\0'; s = se) {
 
786
                const char *e;
 
787
 
 
788
                while (*se && *se != '\n') se++;
 
789
 
 
790
                if (se > s && se[-1] == '\r')
 
791
                   se[-1] = '\0';
 
792
                if (*se == '\0')
 
793
                    /*@innerbreak@*/ break;
 
794
 
 
795
if (_ftp_debug)
 
796
fprintf(stderr, "<- %s\n", s);
 
797
 
 
798
                /* HTTP: header termination on empty line */
 
799
                if (*s == '\0') {
 
800
                    moretodo = 0;
 
801
                    /*@innerbreak@*/ break;
 
802
                }
 
803
                *se++ = '\0';
 
804
 
 
805
                /* HTTP: look for "HTTP/1.1 123 ..." */
 
806
                if (!strncmp(s, "HTTP", sizeof("HTTP")-1)) {
 
807
                    ctrl->contentLength = -1;
 
808
                    if ((e = strchr(s, '.')) != NULL) {
 
809
                        e++;
 
810
                        u->httpVersion = *e - '0';
 
811
                        if (u->httpVersion < 1 || u->httpVersion > 2)
 
812
                            ctrl->persist = u->httpVersion = 0;
 
813
                        else
 
814
                            ctrl->persist = 1;
 
815
                    }
 
816
                    if ((e = strchr(s, ' ')) != NULL) {
 
817
                        e++;
 
818
                        if (strchr("0123456789", *e))
 
819
                            strncpy(errorCode, e, 3);
 
820
                        errorCode[3] = '\0';
 
821
                    }
 
822
                    continue;
 
823
                }
 
824
 
 
825
                /* HTTP: look for "token: ..." */
 
826
                for (e = s; *e && !(*e == ' ' || *e == ':'); e++)
 
827
                    {};
 
828
                if (e > s && *e++ == ':') {
 
829
                    size_t ne = (e - s);
 
830
                    while (*e && *e == ' ') e++;
 
831
#if 0
 
832
                    if (!strncmp(s, "Date:", ne)) {
 
833
                    } else
 
834
                    if (!strncmp(s, "Server:", ne)) {
 
835
                    } else
 
836
                    if (!strncmp(s, "Last-Modified:", ne)) {
 
837
                    } else
 
838
                    if (!strncmp(s, "ETag:", ne)) {
 
839
                    } else
 
840
#endif
 
841
                    if (!strncmp(s, "Accept-Ranges:", ne)) {
 
842
                        if (!strcmp(e, "bytes"))
 
843
                            u->httpHasRange = 1;
 
844
                        if (!strcmp(e, "none"))
 
845
                            u->httpHasRange = 0;
 
846
                    } else
 
847
                    if (!strncmp(s, "Content-Length:", ne)) {
 
848
                        if (strchr("0123456789", *e))
 
849
                            ctrl->contentLength = atoi(e);
 
850
                    } else
 
851
                    if (!strncmp(s, "Connection:", ne)) {
 
852
                        if (!strcmp(e, "close"))
 
853
                            ctrl->persist = 0;
 
854
                    }
 
855
#if 0
 
856
                    else
 
857
                    if (!strncmp(s, "Content-Type:", ne)) {
 
858
                    } else
 
859
                    if (!strncmp(s, "Transfer-Encoding:", ne)) {
 
860
                        if (!strcmp(e, "chunked"))
 
861
                            ctrl->wr_chunked = 1;
 
862
                        else
 
863
                            ctrl->wr_chunked = 0;
 
864
                    } else
 
865
                    if (!strncmp(s, "Allow:", ne)) {
 
866
                    }
 
867
#endif
 
868
                    continue;
 
869
                }
 
870
 
 
871
                /* HTTP: look for "<TITLE>501 ... </TITLE>" */
 
872
                if (!strncmp(s, "<TITLE>", sizeof("<TITLE>")-1))
 
873
                    s += sizeof("<TITLE>") - 1;
 
874
 
 
875
                /* FTP: look for "123-" and/or "123 " */
 
876
                if (strchr("0123456789", *s)) {
 
877
                    if (errorCode[0] != '\0') {
 
878
                        if (!strncmp(s, errorCode, sizeof("123")-1) && s[3] == ' ')
 
879
                            moretodo = 0;
 
880
                    } else {
 
881
                        strncpy(errorCode, s, sizeof("123")-1);
 
882
                        errorCode[3] = '\0';
 
883
                        if (s[3] != '-')
 
884
                            moretodo = 0;
 
885
                    }
 
886
                }
 
887
        }
 
888
 
 
889
        if (moretodo && se > s) {
 
890
            bufLength = se - s - 1;
 
891
            if (s != buf)
 
892
                memmove(buf, s, bufLength);
 
893
        } else {
 
894
            bufLength = 0;
 
895
        }
 
896
    } while (moretodo && ec == 0);
 
897
 
 
898
    if (str)    *str = buf;
 
899
    if (ecp)    *ecp = atoi(errorCode);
 
900
 
 
901
    return ec;
 
902
}
 
903
 
 
904
static int ftpCheckResponse(urlinfo u, /*@out@*/ char ** str)
 
905
        /*@modifies u, *str, fileSystem @*/
 
906
{
 
907
    int ec = 0;
 
908
    int rc;
 
909
 
 
910
    URLSANE(u);
 
911
    rc = checkResponse(u, u->ctrl, &ec, str);
 
912
 
 
913
    switch (ec) {
 
914
    case 550:
 
915
        return FTPERR_FILE_NOT_FOUND;
 
916
        /*@notreached@*/ break;
 
917
    case 552:
 
918
        return FTPERR_NIC_ABORT_IN_PROGRESS;
 
919
        /*@notreached@*/ break;
 
920
    default:
 
921
        if (ec >= 400 && ec <= 599) {
 
922
            return FTPERR_BAD_SERVER_RESPONSE;
 
923
        }
 
924
        break;
 
925
    }
 
926
    return rc;
 
927
}
 
928
 
 
929
static int ftpCommand(urlinfo u, char ** str, ...)
 
930
        /*@modifies u, *str, fileSystem @*/
 
931
{
 
932
    va_list ap;
 
933
    int len = 0;
 
934
    const char * s, * t;
 
935
    char * te;
 
936
    int rc;
 
937
 
 
938
    URLSANE(u);
 
939
    va_start(ap, str);
 
940
    while ((s = va_arg(ap, const char *)) != NULL) {
 
941
        if (len) len++;
 
942
        len += strlen(s);
 
943
    }
 
944
    len += sizeof("\r\n")-1;
 
945
    va_end(ap);
 
946
 
 
947
    t = te = alloca(len + 1);
 
948
 
 
949
    va_start(ap, str);
 
950
    while ((s = va_arg(ap, const char *)) != NULL) {
 
951
        if (te > t) *te++ = ' ';
 
952
        te = stpcpy(te, s);
 
953
    }
 
954
    te = stpcpy(te, "\r\n");
 
955
    va_end(ap);
 
956
 
 
957
if (_ftp_debug)
 
958
fprintf(stderr, "-> %s", t);
 
959
    if (fdWrite(u->ctrl, t, (te-t)) != (te-t))
 
960
        return FTPERR_SERVER_IO_ERROR;
 
961
 
 
962
    rc = ftpCheckResponse(u, str);
 
963
    return rc;
 
964
}
 
965
 
 
966
static int ftpLogin(urlinfo u)
 
967
        /*@modifies u, fileSystem @*/
 
968
{
 
969
    const char * host;
 
970
    const char * user;
 
971
    const char * password;
 
972
    int port;
 
973
    int rc;
 
974
 
 
975
    URLSANE(u);
 
976
    u->ctrl = fdLink(u->ctrl, "open ctrl");
 
977
 
 
978
    if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL)) {
 
979
        rc = FTPERR_BAD_HOSTNAME;
 
980
        goto errxit;
 
981
    }
 
982
 
 
983
    if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = IPPORT_FTP;
 
984
 
 
985
    if ((user = (u->proxyu ? u->proxyu : u->user)) == NULL)
 
986
        user = "anonymous";
 
987
 
 
988
    if ((password = u->password) == NULL) {
 
989
        uid_t uid = getuid();
 
990
        struct passwd * pw;
 
991
        if (uid && (pw = getpwuid(uid)) != NULL) {
 
992
            char *myp = alloca(strlen(pw->pw_name) + sizeof("@"));
 
993
            strcpy(myp, pw->pw_name);
 
994
            strcat(myp, "@");
 
995
            password = myp;
 
996
        } else {
 
997
            password = "root@";
 
998
        }
 
999
    }
 
1000
 
 
1001
    if (fdFileno(u->ctrl) >= 0 && fdWritable(u->ctrl, 0) < 1)
 
1002
        /*@-refcounttrans@*/ (void) fdClose(u->ctrl); /*@=refcounttrans@*/
 
1003
 
 
1004
/*@-usereleased@*/
 
1005
    if (fdFileno(u->ctrl) < 0) {
 
1006
        rc = tcpConnect(u->ctrl, host, port);
 
1007
        if (rc < 0)
 
1008
            goto errxit2;
 
1009
    }
 
1010
 
 
1011
    if ((rc = ftpCheckResponse(u, NULL)))
 
1012
        goto errxit;
 
1013
 
 
1014
    if ((rc = ftpCommand(u, NULL, "USER", user, NULL)))
 
1015
        goto errxit;
 
1016
 
 
1017
    if ((rc = ftpCommand(u, NULL, "PASS", password, NULL)))
 
1018
        goto errxit;
 
1019
 
 
1020
    if ((rc = ftpCommand(u, NULL, "TYPE", "I", NULL)))
 
1021
        goto errxit;
 
1022
 
 
1023
    /*@-compdef@*/
 
1024
    return 0;
 
1025
    /*@=compdef@*/
 
1026
 
 
1027
errxit:
 
1028
    /*@-observertrans@*/
 
1029
    fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
 
1030
    /*@=observertrans@*/
 
1031
errxit2:
 
1032
    if (fdFileno(u->ctrl) >= 0)
 
1033
        /*@-refcounttrans@*/ (void) fdClose(u->ctrl); /*@=refcounttrans@*/
 
1034
    /*@-compdef@*/
 
1035
    return rc;
 
1036
    /*@=compdef@*/
 
1037
/*@=usereleased@*/
 
1038
}
 
1039
 
 
1040
int ftpReq(FD_t data, const char * ftpCmd, const char * ftpArg)
 
1041
{
 
1042
    urlinfo u = data->url;
 
1043
    struct sockaddr_in dataAddress;
 
1044
    char * cmd;
 
1045
    int cmdlen;
 
1046
    char * passReply;
 
1047
    char * chptr;
 
1048
    int rc;
 
1049
 
 
1050
    URLSANE(u);
 
1051
    if (ftpCmd == NULL)
 
1052
        return FTPERR_UNKNOWN;  /* XXX W2DO? */
 
1053
 
 
1054
    cmdlen = strlen(ftpCmd) + (ftpArg ? 1+strlen(ftpArg) : 0) + sizeof("\r\n");
 
1055
    chptr = cmd = alloca(cmdlen);
 
1056
    chptr = stpcpy(chptr, ftpCmd);
 
1057
    if (ftpArg) {
 
1058
        *chptr++ = ' ';
 
1059
        chptr = stpcpy(chptr, ftpArg);
 
1060
    }
 
1061
    chptr = stpcpy(chptr, "\r\n");
 
1062
    cmdlen = chptr - cmd;
 
1063
 
 
1064
/*
 
1065
 * Get the ftp version of the Content-Length.
 
1066
 */
 
1067
    if (!strncmp(cmd, "RETR", 4)) {
 
1068
        unsigned cl;
 
1069
 
 
1070
        passReply = NULL;
 
1071
        rc = ftpCommand(u, &passReply, "SIZE", ftpArg, NULL);
 
1072
        if (rc)
 
1073
            goto errxit;
 
1074
        if (sscanf(passReply, "%d %u", &rc, &cl) != 2) {
 
1075
            rc = FTPERR_BAD_SERVER_RESPONSE;
 
1076
            goto errxit;
 
1077
        }
 
1078
        rc = 0;
 
1079
        data->contentLength = cl;
 
1080
    }
 
1081
 
 
1082
    passReply = NULL;
 
1083
    rc = ftpCommand(u, &passReply, "PASV", NULL);
 
1084
    if (rc) {
 
1085
        rc = FTPERR_PASSIVE_ERROR;
 
1086
        goto errxit;
 
1087
    }
 
1088
 
 
1089
    chptr = passReply;
 
1090
    while (*chptr && *chptr != '(') chptr++;
 
1091
    if (*chptr != '(') return FTPERR_PASSIVE_ERROR; 
 
1092
    chptr++;
 
1093
    passReply = chptr;
 
1094
    while (*chptr && *chptr != ')') chptr++;
 
1095
    if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
 
1096
    *chptr-- = '\0';
 
1097
 
 
1098
    while (*chptr && *chptr != ',') chptr--;
 
1099
    if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
 
1100
    chptr--;
 
1101
    while (*chptr && *chptr != ',') chptr--;
 
1102
    if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
 
1103
    *chptr++ = '\0';
 
1104
    
 
1105
    /* now passReply points to the IP portion, and chptr points to the
 
1106
       port number portion */
 
1107
 
 
1108
    {   int i, j;
 
1109
        memset(&dataAddress, 0, sizeof(dataAddress));
 
1110
        dataAddress.sin_family = AF_INET;
 
1111
        if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
 
1112
            rc = FTPERR_PASSIVE_ERROR;
 
1113
            goto errxit;
 
1114
        }
 
1115
        dataAddress.sin_port = htons((((unsigned)i) << 8) + j);
 
1116
    }
 
1117
 
 
1118
    chptr = passReply;
 
1119
    while (*chptr++ != '\0') {
 
1120
        if (*chptr == ',') *chptr = '.';
 
1121
    }
 
1122
 
 
1123
    if (!inet_aton(passReply, &dataAddress.sin_addr)) {
 
1124
        rc = FTPERR_PASSIVE_ERROR;
 
1125
        goto errxit;
 
1126
    }
 
1127
 
 
1128
    rc = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
 
1129
    fdSetFdno(data, (rc >= 0 ? rc : -1));
 
1130
    if (rc < 0) {
 
1131
        rc = FTPERR_FAILED_CONNECT;
 
1132
        goto errxit;
 
1133
    }
 
1134
    data = fdLink(data, "open data (ftpReq)");
 
1135
 
 
1136
    /* XXX setsockopt SO_LINGER */
 
1137
    /* XXX setsockopt SO_KEEPALIVE */
 
1138
    /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
 
1139
 
 
1140
    while (connect(fdFileno(data), (struct sockaddr *) &dataAddress, 
 
1141
                sizeof(dataAddress)) < 0) {
 
1142
        if (errno == EINTR)
 
1143
            continue;
 
1144
        rc = FTPERR_FAILED_DATA_CONNECT;
 
1145
        goto errxit;
 
1146
    }
 
1147
 
 
1148
if (_ftp_debug)
 
1149
fprintf(stderr, "-> %s", cmd);
 
1150
    if (fdWrite(u->ctrl, cmd, cmdlen) != cmdlen) {
 
1151
        rc = FTPERR_SERVER_IO_ERROR;
 
1152
        goto errxit;
 
1153
    }
 
1154
 
 
1155
    if ((rc = ftpCheckResponse(u, NULL))) {
 
1156
        goto errxit;
 
1157
    }
 
1158
 
 
1159
    data->ftpFileDoneNeeded = 1;
 
1160
    u->ctrl = fdLink(u->ctrl, "grab data (ftpReq)");
 
1161
    u->ctrl = fdLink(u->ctrl, "open data (ftpReq)");
 
1162
    return 0;
 
1163
 
 
1164
errxit:
 
1165
    /*@-observertrans@*/
 
1166
    fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
 
1167
    /*@=observertrans@*/
 
1168
    if (fdFileno(data) >= 0)
 
1169
        /*@-refcounttrans@*/ (void) fdClose(data); /*@=refcounttrans@*/
 
1170
    return rc;
 
1171
}
 
1172
 
 
1173
/*@null@*/ static rpmCallbackFunction   urlNotify = NULL;
 
1174
/*@null@*/ static void *        urlNotifyData = NULL;
 
1175
static int                      urlNotifyCount = -1;
 
1176
 
 
1177
void urlSetCallback(rpmCallbackFunction notify, void *notifyData, int notifyCount) {
 
1178
    urlNotify = notify;
 
1179
    urlNotifyData = notifyData;
 
1180
    urlNotifyCount = (notifyCount >= 0) ? notifyCount : 4096;
 
1181
}
 
1182
 
 
1183
int ufdCopy(FD_t sfd, FD_t tfd)
 
1184
{
 
1185
    char buf[BUFSIZ];
 
1186
    int itemsRead;
 
1187
    int itemsCopied = 0;
 
1188
    int rc = 0;
 
1189
    int notifier = -1;
 
1190
 
 
1191
    if (urlNotify) {
 
1192
        (void)(*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
 
1193
                0, 0, NULL, urlNotifyData);
 
1194
    }
 
1195
    
 
1196
    while (1) {
 
1197
        rc = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
 
1198
        if (rc < 0)
 
1199
            break;
 
1200
        else if (rc == 0) {
 
1201
            rc = itemsCopied;
 
1202
            break;
 
1203
        }
 
1204
        itemsRead = rc;
 
1205
        rc = Fwrite(buf, sizeof(buf[0]), itemsRead, tfd);
 
1206
        if (rc < 0)
 
1207
            break;
 
1208
        if (rc != itemsRead) {
 
1209
            rc = FTPERR_FILE_IO_ERROR;
 
1210
            break;
 
1211
        }
 
1212
 
 
1213
        itemsCopied += itemsRead;
 
1214
        if (urlNotify && urlNotifyCount > 0) {
 
1215
            int n = itemsCopied/urlNotifyCount;
 
1216
            if (n != notifier) {
 
1217
                (void)(*urlNotify) (NULL, RPMCALLBACK_INST_PROGRESS,
 
1218
                        itemsCopied, 0, NULL, urlNotifyData);
 
1219
                notifier = n;
 
1220
            }
 
1221
        }
 
1222
    }
 
1223
 
 
1224
    DBGIO(sfd, (stderr, "++ copied %d bytes: %s\n", itemsCopied,
 
1225
        ftpStrerror(rc)));
 
1226
 
 
1227
    if (urlNotify) {
 
1228
        (void)(*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
 
1229
                itemsCopied, itemsCopied, NULL, urlNotifyData);
 
1230
    }
 
1231
    
 
1232
    return rc;
 
1233
}
 
1234
 
 
1235
static int urlConnect(const char * url, /*@out@*/ urlinfo * uret)
 
1236
        /*@modifies *uret, fileSystem @*/
 
1237
{
 
1238
    urlinfo u;
 
1239
    int rc = 0;
 
1240
 
 
1241
    if (urlSplit(url, &u) < 0)
 
1242
        return -1;
 
1243
 
 
1244
    if (u->urltype == URL_IS_FTP) {
 
1245
        FD_t fd;
 
1246
 
 
1247
        if ((fd = u->ctrl) == NULL) {
 
1248
            fd = u->ctrl = fdNew("persist ctrl (urlConnect FTP)");
 
1249
            fdSetIo(u->ctrl, ufdio);
 
1250
        }
 
1251
        
 
1252
        fd->rd_timeoutsecs = ftpTimeoutSecs;
 
1253
        fd->contentLength = fd->bytesRemain = -1;
 
1254
        fd->url = NULL;         /* XXX FTP ctrl has not */
 
1255
        fd->ftpFileDoneNeeded = 0;
 
1256
        fd = fdLink(fd, "grab ctrl (urlConnect FTP)");
 
1257
 
 
1258
        if (fdFileno(u->ctrl) < 0) {
 
1259
            rpmMessage(RPMMESS_DEBUG, _("logging into %s as %s, pw %s\n"),
 
1260
                        u->host ? u->host : "???",
 
1261
                        u->user ? u->user : "ftp",
 
1262
                        u->password ? u->password : "(username)");
 
1263
 
 
1264
            if ((rc = ftpLogin(u)) < 0) {       /* XXX save ftpLogin error */
 
1265
                u->ctrl = fdFree(fd, "grab ctrl (urlConnect FTP)");
 
1266
                u->openError = rc;
 
1267
            }
 
1268
        }
 
1269
    }
 
1270
 
 
1271
    if (uret != NULL)
 
1272
        *uret = urlLink(u, "urlConnect");
 
1273
    u = urlFree(u, "urlSplit (urlConnect)");    
 
1274
 
 
1275
    return rc;
 
1276
}
 
1277
 
 
1278
int ufdGetFile(FD_t sfd, FD_t tfd)
 
1279
{
 
1280
    int rc;
 
1281
 
 
1282
    FDSANE(sfd);
 
1283
    FDSANE(tfd);
 
1284
    rc = ufdCopy(sfd, tfd);
 
1285
    (void) Fclose(sfd);
 
1286
    if (rc > 0)         /* XXX ufdCopy now returns no. bytes copied */
 
1287
        rc = 0;
 
1288
    return rc;
 
1289
}
 
1290
 
 
1291
int ftpCmd(const char * cmd, const char * url, const char * arg2)
 
1292
{
 
1293
    urlinfo u;
 
1294
    int rc;
 
1295
    const char * path;
 
1296
 
 
1297
    if (urlConnect(url, &u) < 0)
 
1298
        return -1;
 
1299
 
 
1300
    (void) urlPath(url, &path);
 
1301
 
 
1302
    rc = ftpCommand(u, NULL, cmd, path, arg2, NULL);
 
1303
    u->ctrl = fdFree(u->ctrl, "grab ctrl (ftpCmd)");
 
1304
    return rc;
 
1305
}
 
1306
 
 
1307
/* XXX these aren't worth the pain of including correctly */
 
1308
#if !defined(IAC)
 
1309
#define IAC     255             /* interpret as command: */
 
1310
#endif
 
1311
#if !defined(IP)
 
1312
#define IP      244             /* interrupt process--permanently */
 
1313
#endif
 
1314
#if !defined(DM)
 
1315
#define DM      242             /* data mark--for connect. cleaning */
 
1316
#endif
 
1317
#if !defined(SHUT_RDWR)
 
1318
#define SHUT_RDWR       1+1
 
1319
#endif
 
1320
 
 
1321
static int ftpAbort(urlinfo u, FD_t data)
 
1322
        /*@modifies u, data, fileSystem @*/
 
1323
{
 
1324
    static unsigned char ipbuf[3] = { IAC, IP, IAC };
 
1325
    FD_t ctrl;
 
1326
    int rc;
 
1327
    int tosecs;
 
1328
 
 
1329
    URLSANE(u);
 
1330
 
 
1331
    if (data != NULL) {
 
1332
        data->ftpFileDoneNeeded = 0;
 
1333
        if (fdFileno(data) >= 0)
 
1334
            u->ctrl = fdFree(u->ctrl, "open data (ftpAbort)");
 
1335
        u->ctrl = fdFree(u->ctrl, "grab data (ftpAbort)");
 
1336
    }
 
1337
    ctrl = u->ctrl;
 
1338
 
 
1339
    DBGIO(0, (stderr, "-> ABOR\n"));
 
1340
 
 
1341
/*@-usereleased -compdef@*/
 
1342
    if (send(fdFileno(ctrl), ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
 
1343
        /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
 
1344
        return FTPERR_SERVER_IO_ERROR;
 
1345
    }
 
1346
 
 
1347
    sprintf(u->buf, "%cABOR\r\n",(char) DM);
 
1348
    if (fdWrite(ctrl, u->buf, 7) != 7) {
 
1349
        /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
 
1350
        return FTPERR_SERVER_IO_ERROR;
 
1351
    }
 
1352
 
 
1353
    if (data && fdFileno(data) >= 0) {
 
1354
        /* XXX shorten data drain time wait */
 
1355
        tosecs = data->rd_timeoutsecs;
 
1356
        data->rd_timeoutsecs = 10;
 
1357
        if (fdReadable(data, data->rd_timeoutsecs) > 0) {
 
1358
            while (timedRead(data, u->buf, u->bufAlloced) > 0)
 
1359
                u->buf[0] = '\0';
 
1360
        }
 
1361
        data->rd_timeoutsecs = tosecs;
 
1362
        /* XXX ftp abort needs to close the data channel to receive status */
 
1363
        (void) shutdown(fdFileno(data), SHUT_RDWR);
 
1364
        (void) close(fdFileno(data));
 
1365
        data->fps[0].fdno = -1; /* XXX WRONG but expedient */
 
1366
    }
 
1367
 
 
1368
    /* XXX shorten ctrl drain time wait */
 
1369
    tosecs = u->ctrl->rd_timeoutsecs;
 
1370
    u->ctrl->rd_timeoutsecs = 10;
 
1371
    if ((rc = ftpCheckResponse(u, NULL)) == FTPERR_NIC_ABORT_IN_PROGRESS) {
 
1372
        rc = ftpCheckResponse(u, NULL);
 
1373
    }
 
1374
    rc = ftpCheckResponse(u, NULL);
 
1375
    u->ctrl->rd_timeoutsecs = tosecs;
 
1376
 
 
1377
    return rc;
 
1378
/*@=usereleased =compdef@*/
 
1379
}
 
1380
 
 
1381
static int ftpFileDone(urlinfo u, FD_t data)
 
1382
        /*@modifies u, data, fileSystem @*/
 
1383
{
 
1384
    int rc = 0;
 
1385
 
 
1386
    URLSANE(u);
 
1387
    assert(data->ftpFileDoneNeeded);
 
1388
 
 
1389
    if (data->ftpFileDoneNeeded) {
 
1390
        data->ftpFileDoneNeeded = 0;
 
1391
        u->ctrl = fdFree(u->ctrl, "open data (ftpFileDone)");
 
1392
        u->ctrl = fdFree(u->ctrl, "grab data (ftpFileDone)");
 
1393
        rc = ftpCheckResponse(u, NULL);
 
1394
    }
 
1395
    return rc;
 
1396
}
 
1397
 
 
1398
static int httpResp(urlinfo u, FD_t ctrl, /*@out@*/ char ** str)
 
1399
        /*@modifies ctrl, *str, fileSystem @*/
 
1400
{
 
1401
    int ec = 0;
 
1402
    int rc;
 
1403
 
 
1404
    URLSANE(u);
 
1405
    rc = checkResponse(u, ctrl, &ec, str);
 
1406
 
 
1407
if (_ftp_debug && !(rc == 0 && ec == 200))
 
1408
fprintf(stderr, "*** httpResp: rc %d ec %d\n", rc, ec);
 
1409
 
 
1410
    switch (ec) {
 
1411
    case 200:
 
1412
        break;
 
1413
    default:
 
1414
        rc = FTPERR_FILE_NOT_FOUND;
 
1415
        break;
 
1416
    }
 
1417
 
 
1418
    return rc;
 
1419
}
 
1420
 
 
1421
static int httpReq(FD_t ctrl, const char * httpCmd, const char * httpArg)
 
1422
        /*@modifies ctrl, fileSystem @*/
 
1423
{
 
1424
    urlinfo u = ctrl->url;
 
1425
    const char * host;
 
1426
    const char * path;
 
1427
    int port;
 
1428
    int rc;
 
1429
    char * req;
 
1430
    size_t len;
 
1431
    int retrying = 0;
 
1432
 
 
1433
    URLSANE(u);
 
1434
    assert(ctrl != NULL);
 
1435
 
 
1436
    if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL))
 
1437
        return FTPERR_BAD_HOSTNAME;
 
1438
 
 
1439
    if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = 80;
 
1440
    path = (u->proxyh || u->proxyp > 0) ? u->url : httpArg;
 
1441
    if (path == NULL) path = "";
 
1442
 
 
1443
reopen:
 
1444
    if (fdFileno(ctrl) >= 0 && (rc = fdWritable(ctrl, 0)) < 1) {
 
1445
        /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
 
1446
    }
 
1447
 
 
1448
/*@-usereleased@*/
 
1449
    if (fdFileno(ctrl) < 0) {
 
1450
        rc = tcpConnect(ctrl, host, port);
 
1451
        if (rc < 0)
 
1452
            goto errxit2;
 
1453
        ctrl = fdLink(ctrl, "open ctrl (httpReq)");
 
1454
    }
 
1455
 
 
1456
    len = sizeof("\
 
1457
req x HTTP/1.0\r\n\
 
1458
User-Agent: rpm/3.0.4\r\n\
 
1459
Host: y:z\r\n\
 
1460
Accept: text/plain\r\n\
 
1461
Transfer-Encoding: chunked\r\n\
 
1462
\r\n\
 
1463
") + strlen(httpCmd) + strlen(path) + sizeof(VERSION) + strlen(host) + 20;
 
1464
 
 
1465
    req = alloca(len);
 
1466
    *req = '\0';
 
1467
 
 
1468
  if (!strcmp(httpCmd, "PUT")) {
 
1469
    sprintf(req, "\
 
1470
%s %s HTTP/1.%d\r\n\
 
1471
User-Agent: rpm/%s\r\n\
 
1472
Host: %s:%d\r\n\
 
1473
Accept: text/plain\r\n\
 
1474
Transfer-Encoding: chunked\r\n\
 
1475
\r\n\
 
1476
",      httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, host, port);
 
1477
} else {
 
1478
    sprintf(req, "\
 
1479
%s %s HTTP/1.%d\r\n\
 
1480
User-Agent: rpm/%s\r\n\
 
1481
Host: %s:%d\r\n\
 
1482
Accept: text/plain\r\n\
 
1483
\r\n\
 
1484
",      httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, host, port);
 
1485
}
 
1486
 
 
1487
if (_ftp_debug)
 
1488
fprintf(stderr, "-> %s", req);
 
1489
 
 
1490
    len = strlen(req);
 
1491
    if (fdWrite(ctrl, req, len) != len) {
 
1492
        rc = FTPERR_SERVER_IO_ERROR;
 
1493
        goto errxit;
 
1494
    }
 
1495
 
 
1496
    if (!strcmp(httpCmd, "PUT")) {
 
1497
        ctrl->wr_chunked = 1;
 
1498
    } else {
 
1499
 
 
1500
        rc = httpResp(u, ctrl, NULL);
 
1501
 
 
1502
        if (rc) {
 
1503
            if (!retrying) {    /* not HTTP_OK */
 
1504
                retrying = 1;
 
1505
                /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
 
1506
                goto reopen;
 
1507
            }
 
1508
            goto errxit;
 
1509
        }
 
1510
    }
 
1511
 
 
1512
    ctrl = fdLink(ctrl, "open data (httpReq)");
 
1513
    return 0;
 
1514
 
 
1515
errxit:
 
1516
    /*@-observertrans@*/
 
1517
    fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
 
1518
    /*@=observertrans@*/
 
1519
errxit2:
 
1520
    if (fdFileno(ctrl) >= 0)
 
1521
        /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
 
1522
    return rc;
 
1523
/*@=usereleased@*/
 
1524
}
 
1525
 
 
1526
/* XXX DYING: unused */
 
1527
void * ufdGetUrlinfo(FD_t fd)
 
1528
{
 
1529
    FDSANE(fd);
 
1530
    if (fd->url == NULL)
 
1531
        return NULL;
 
1532
    return urlLink(fd->url, "ufdGetUrlinfo");
 
1533
}
 
1534
 
 
1535
/* =============================================================== */
 
1536
static ssize_t ufdRead(void * cookie, /*@out@*/ char * buf, size_t count)
 
1537
        /*@modifies internalState, *buf, fileSystem @*/
 
1538
{
 
1539
    FD_t fd = c2f(cookie);
 
1540
    int bytesRead;
 
1541
    int total;
 
1542
 
 
1543
    *buf = '\0';        /* LCL: insistent bugger. */
 
1544
    /* XXX preserve timedRead() behavior */
 
1545
    if (fdGetIo(fd) == fdio) {
 
1546
        struct stat sb;
 
1547
        int fdno = fdFileno(fd);
 
1548
        (void) fstat(fdno, &sb);
 
1549
        if (S_ISREG(sb.st_mode))
 
1550
            return fdRead(fd, buf, count);
 
1551
    }
 
1552
 
 
1553
    UFDONLY(fd);
 
1554
    assert(fd->rd_timeoutsecs >= 0);
 
1555
 
 
1556
    for (total = 0; total < count; total += bytesRead) {
 
1557
 
 
1558
        int rc;
 
1559
 
 
1560
        bytesRead = 0;
 
1561
 
 
1562
        /* Is there data to read? */
 
1563
        if (fd->bytesRemain == 0) return total; /* XXX simulate EOF */
 
1564
        rc = fdReadable(fd, fd->rd_timeoutsecs);
 
1565
 
 
1566
        switch (rc) {
 
1567
        case -1:        /* error */
 
1568
        case  0:        /* timeout */
 
1569
            return total;
 
1570
            /*@notreached@*/ break;
 
1571
        default:        /* data to read */
 
1572
            break;
 
1573
        }
 
1574
 
 
1575
        rc = fdRead(fd, buf + total, count - total);
 
1576
 
 
1577
        if (rc < 0) {
 
1578
            switch (errno) {
 
1579
            case EWOULDBLOCK:
 
1580
                continue;
 
1581
                /*@notreached@*/ break;
 
1582
            default:
 
1583
                break;
 
1584
            }
 
1585
if (_rpmio_debug)
 
1586
fprintf(stderr, "*** read: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
 
1587
            return rc;
 
1588
            /*@notreached@*/ break;
 
1589
        } else if (rc == 0) {
 
1590
            return total;
 
1591
            /*@notreached@*/ break;
 
1592
        }
 
1593
        bytesRead = rc;
 
1594
    }
 
1595
 
 
1596
    return count;
 
1597
}
 
1598
 
 
1599
static ssize_t ufdWrite(void * cookie, const char * buf, size_t count)
 
1600
        /*@modifies internalState, fileSystem @*/
 
1601
{
 
1602
    FD_t fd = c2f(cookie);
 
1603
    int bytesWritten;
 
1604
    int total = 0;
 
1605
 
 
1606
#ifdef  NOTYET
 
1607
    if (fdGetIo(fd) == fdio) {
 
1608
        struct stat sb;
 
1609
        (void) fstat(fdGetFdno(fd), &sb);
 
1610
        if (S_ISREG(sb.st_mode))
 
1611
            return fdWrite(fd, buf, count);
 
1612
    }
 
1613
#endif
 
1614
 
 
1615
    UFDONLY(fd);
 
1616
 
 
1617
    for (total = 0; total < count; total += bytesWritten) {
 
1618
 
 
1619
        int rc;
 
1620
 
 
1621
        bytesWritten = 0;
 
1622
 
 
1623
        /* Is there room to write data? */
 
1624
        if (fd->bytesRemain == 0) {
 
1625
fprintf(stderr, "*** ufdWrite fd %p WRITE PAST END OF CONTENT\n", fd);
 
1626
            return total;       /* XXX simulate EOF */
 
1627
        }
 
1628
        rc = fdWritable(fd, 2);         /* XXX configurable? */
 
1629
 
 
1630
        switch (rc) {
 
1631
        case -1:        /* error */
 
1632
        case  0:        /* timeout */
 
1633
            return total;
 
1634
            /*@notreached@*/ break;
 
1635
        default:        /* data to write */
 
1636
            break;
 
1637
        }
 
1638
 
 
1639
        rc = fdWrite(fd, buf + total, count - total);
 
1640
 
 
1641
        if (rc < 0) {
 
1642
            switch (errno) {
 
1643
            case EWOULDBLOCK:
 
1644
                continue;
 
1645
                /*@notreached@*/ break;
 
1646
            default:
 
1647
                break;
 
1648
            }
 
1649
if (_rpmio_debug)
 
1650
fprintf(stderr, "*** write: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
 
1651
            return rc;
 
1652
            /*@notreached@*/ break;
 
1653
        } else if (rc == 0) {
 
1654
            return total;
 
1655
            /*@notreached@*/ break;
 
1656
        }
 
1657
        bytesWritten = rc;
 
1658
    }
 
1659
 
 
1660
    return count;
 
1661
}
 
1662
 
 
1663
static inline int ufdSeek(void * cookie, _libio_pos_t pos, int whence)
 
1664
        /*@modifies internalState, fileSystem @*/
 
1665
{
 
1666
    FD_t fd = c2f(cookie);
 
1667
 
 
1668
    switch (fd->urlType) {
 
1669
    case URL_IS_UNKNOWN:
 
1670
    case URL_IS_PATH:
 
1671
        break;
 
1672
    case URL_IS_DASH:
 
1673
    case URL_IS_FTP:
 
1674
    case URL_IS_HTTP:
 
1675
    default:
 
1676
        return -2;
 
1677
        /*@notreached@*/ break;
 
1678
    }
 
1679
    return fdSeek(cookie, pos, whence);
 
1680
}
 
1681
 
 
1682
/*@-usereleased@*/      /* LCL: fd handling is tricky here. */
 
1683
int ufdClose( /*@only@*/ void * cookie)
 
1684
{
 
1685
    FD_t fd = c2f(cookie);
 
1686
 
 
1687
    UFDONLY(fd);
 
1688
 
 
1689
    if (fd->url) {
 
1690
        urlinfo u = fd->url;
 
1691
 
 
1692
        if (fd == u->data)
 
1693
                fd = u->data = fdFree(fd, "grab data (ufdClose persist)");
 
1694
        else
 
1695
                fd = fdFree(fd, "grab data (ufdClose)");
 
1696
        (void) urlFree(fd->url, "url (ufdClose)");
 
1697
        fd->url = NULL;
 
1698
        u->ctrl = fdFree(u->ctrl, "grab ctrl (ufdClose)");
 
1699
 
 
1700
        if (u->urltype == URL_IS_FTP) {
 
1701
 
 
1702
            /* XXX if not using libio, lose the fp from fpio */
 
1703
            {   FILE * fp;
 
1704
                /*@+voidabstract -nullpass@*/
 
1705
                fp = fdGetFILE(fd);
 
1706
                if (noLibio && fp)
 
1707
                    fdSetFp(fd, NULL);
 
1708
                /*@=voidabstract =nullpass@*/
 
1709
            }
 
1710
 
 
1711
            /*
 
1712
             * Normal FTP has 4 refs on the data fd:
 
1713
             *  "persist data (ufdOpen FTP)"            rpmio.c:888
 
1714
             *  "grab data (ufdOpen FTP)"               rpmio.c:892
 
1715
             *  "open data (ftpReq)"                    ftp.c:633
 
1716
             *  "fopencookie"                           rpmio.c:1507
 
1717
             *
 
1718
             * Normal FTP has 5 refs on the ctrl fd:
 
1719
             *  "persist ctrl"                          url.c:176
 
1720
             *  "grab ctrl (urlConnect FTP)"            rpmio.c:404
 
1721
             *  "open ctrl"                             ftp.c:504
 
1722
             *  "grab data (ftpReq)"                    ftp.c:661
 
1723
             *  "open data (ftpReq)"                    ftp.c:662
 
1724
             */
 
1725
            if (fd->bytesRemain > 0) {
 
1726
                if (fd->ftpFileDoneNeeded) {
 
1727
                    if (fdReadable(u->ctrl, 0) > 0)
 
1728
                        (void) ftpFileDone(u, fd);
 
1729
                    else
 
1730
                        (void) ftpAbort(u, fd);
 
1731
                }
 
1732
            } else {
 
1733
                int rc;
 
1734
                /* XXX STOR et al require close before ftpFileDone */
 
1735
                rc = fdClose(fd);
 
1736
#if 0   /* XXX error exit from ufdOpen does not have this set */
 
1737
                assert(fd->ftpFileDoneNeeded != 0);
 
1738
#endif
 
1739
                if (fd->ftpFileDoneNeeded)
 
1740
                    (void) ftpFileDone(u, fd);
 
1741
                return rc;
 
1742
            }
 
1743
        }
 
1744
 
 
1745
        /* XXX Why not (u->urltype == URL_IS_HTTP) ??? */
 
1746
        if (u->service != NULL && !strcmp(u->service, "http")) {
 
1747
            if (fd->wr_chunked) {
 
1748
                int rc;
 
1749
            /* XXX HTTP PUT requires terminating 0 length chunk. */
 
1750
                (void) fdWrite(fd, NULL, 0);
 
1751
                fd->wr_chunked = 0;
 
1752
            /* XXX HTTP PUT requires terminating entity-header. */
 
1753
if (_ftp_debug)
 
1754
fprintf(stderr, "-> \r\n");
 
1755
                (void) fdWrite(fd, "\r\n", sizeof("\r\n")-1);
 
1756
                rc = httpResp(u, fd, NULL);
 
1757
            }
 
1758
 
 
1759
            if (fd == u->ctrl)
 
1760
                fd = u->ctrl = fdFree(fd, "open data (ufdClose HTTP persist ctrl)");
 
1761
            else if (fd == u->data)
 
1762
                fd = u->data = fdFree(fd, "open data (ufdClose HTTP persist data)");
 
1763
            else
 
1764
                fd = fdFree(fd, "open data (ufdClose HTTP)");
 
1765
 
 
1766
            /*
 
1767
             * HTTP has 4 (or 5 if persistent malloc) refs on the fd:
 
1768
             *  "persist ctrl"                          url.c:177
 
1769
             *  "grab ctrl (ufdOpen HTTP)"              rpmio.c:924
 
1770
             *  "grab data (ufdOpen HTTP)"              rpmio.c:928
 
1771
             *  "open ctrl (httpReq)"                   ftp.c:382
 
1772
             *  "open data (httpReq)"                   ftp.c:435
 
1773
             */
 
1774
 
 
1775
            /* XXX if not using libio, lose the fp from fpio */
 
1776
            {   FILE * fp;
 
1777
                /*@+voidabstract -nullpass@*/
 
1778
                fp = fdGetFILE(fd);
 
1779
                if (noLibio && fp)
 
1780
                    fdSetFp(fd, NULL);
 
1781
                /*@=voidabstract =nullpass@*/
 
1782
            }
 
1783
 
 
1784
            if (fd->persist && u->httpVersion &&
 
1785
                (fd == u->ctrl || fd == u->data) && fd->bytesRemain == 0) {
 
1786
                fd->contentLength = fd->bytesRemain = -1;
 
1787
                return 0;
 
1788
            } else {
 
1789
                fd->contentLength = fd->bytesRemain = -1;
 
1790
            }
 
1791
        }
 
1792
    }
 
1793
    return fdClose(fd);
 
1794
}
 
1795
/*@=usereleased@*/
 
1796
 
 
1797
/*@-nullstate@*/        /* FIX: u->{ctrl,data}->url undef after XurlLink. */
 
1798
/*@null@*/ FD_t ftpOpen(const char *url, /*@unused@*/ int flags,
 
1799
                /*@unused@*/ mode_t mode, /*@out@*/ urlinfo *uret)
 
1800
        /*@modifies *uret, fileSystem @*/
 
1801
{
 
1802
    urlinfo u = NULL;
 
1803
    FD_t fd = NULL;
 
1804
 
 
1805
#if 0   /* XXX makeTempFile() heartburn */
 
1806
    assert(!(flags & O_RDWR));
 
1807
#endif
 
1808
    if (urlConnect(url, &u) < 0)
 
1809
        goto exit;
 
1810
 
 
1811
    if (u->data == NULL)
 
1812
        u->data = fdNew("persist data (ftpOpen)");
 
1813
 
 
1814
    if (u->data->url == NULL)
 
1815
        fd = fdLink(u->data, "grab data (ftpOpen persist data)");
 
1816
    else
 
1817
        fd = fdNew("grab data (ftpOpen)");
 
1818
 
 
1819
    if (fd) {
 
1820
        fdSetIo(fd, ufdio);
 
1821
        fd->ftpFileDoneNeeded = 0;
 
1822
        fd->rd_timeoutsecs = ftpTimeoutSecs;
 
1823
        fd->contentLength = fd->bytesRemain = -1;
 
1824
        fd->url = urlLink(u, "url (ufdOpen FTP)");
 
1825
        fd->urlType = URL_IS_FTP;
 
1826
    }
 
1827
 
 
1828
exit:
 
1829
    if (uret)
 
1830
        *uret = u;
 
1831
    return fd;
 
1832
}
 
1833
/*@=nullstate@*/
 
1834
 
 
1835
/*@-nullstate@*/        /* FIX: u->{ctrl,data}->url undef after XurlLink. */
 
1836
static /*@null@*/ FD_t httpOpen(const char * url, /*@unused@*/ int flags,
 
1837
                /*@unused@*/ mode_t mode, /*@out@*/ urlinfo * uret)
 
1838
        /*@modifies *uret, fileSystem @*/
 
1839
{
 
1840
    urlinfo u = NULL;
 
1841
    FD_t fd = NULL;
 
1842
 
 
1843
#if 0   /* XXX makeTempFile() heartburn */
 
1844
    assert(!(flags & O_RDWR));
 
1845
#endif
 
1846
    if (urlSplit(url, &u))
 
1847
        goto exit;
 
1848
 
 
1849
    if (u->ctrl == NULL)
 
1850
        u->ctrl = fdNew("persist ctrl (httpOpen)");
 
1851
    if (u->ctrl->nrefs > 2 && u->data == NULL)
 
1852
        u->data = fdNew("persist data (httpOpen)");
 
1853
 
 
1854
    if (u->ctrl->url == NULL)
 
1855
        fd = fdLink(u->ctrl, "grab ctrl (httpOpen persist ctrl)");
 
1856
    else if (u->data->url == NULL)
 
1857
        fd = fdLink(u->data, "grab ctrl (httpOpen persist data)");
 
1858
    else
 
1859
        fd = fdNew("grab ctrl (httpOpen)");
 
1860
 
 
1861
    if (fd) {
 
1862
        fdSetIo(fd, ufdio);
 
1863
        fd->ftpFileDoneNeeded = 0;
 
1864
        fd->rd_timeoutsecs = httpTimeoutSecs;
 
1865
        fd->contentLength = fd->bytesRemain = -1;
 
1866
        fd->url = urlLink(u, "url (httpOpen)");
 
1867
        fd = fdLink(fd, "grab data (httpOpen)");
 
1868
        fd->urlType = URL_IS_HTTP;
 
1869
    }
 
1870
 
 
1871
exit:
 
1872
    if (uret)
 
1873
        *uret = u;
 
1874
    return fd;
 
1875
}
 
1876
/*@=nullstate@*/
 
1877
 
 
1878
static /*@null@*/ FD_t ufdOpen(const char * url, int flags, mode_t mode)
 
1879
        /*@modifies fileSystem @*/
 
1880
{
 
1881
    FD_t fd = NULL;
 
1882
    const char * cmd;
 
1883
    urlinfo u;
 
1884
    const char * path;
 
1885
    urltype urlType = urlPath(url, &path);
 
1886
 
 
1887
if (_rpmio_debug)
 
1888
fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, (unsigned)flags, (unsigned)mode);
 
1889
 
 
1890
    switch (urlType) {
 
1891
    case URL_IS_FTP:
 
1892
        fd = ftpOpen(url, flags, mode, &u);
 
1893
        if (fd == NULL || u == NULL)
 
1894
            break;
 
1895
 
 
1896
        /* XXX W2DO? use STOU rather than STOR to prevent clobbering */
 
1897
        cmd = ((flags & O_WRONLY) 
 
1898
                ?  ((flags & O_APPEND) ? "APPE" :
 
1899
                   ((flags & O_CREAT) ? "STOR" : "STOR"))
 
1900
                :  ((flags & O_CREAT) ? "STOR" : "RETR"));
 
1901
        u->openError = ftpReq(fd, cmd, path);
 
1902
        if (u->openError < 0) {
 
1903
            /* XXX make sure that we can exit through ufdClose */
 
1904
            fd = fdLink(fd, "error data (ufdOpen FTP)");
 
1905
        } else {
 
1906
            fd->bytesRemain = ((!strcmp(cmd, "RETR"))
 
1907
                ?  fd->contentLength : -1);
 
1908
            fd->wr_chunked = 0;
 
1909
        }
 
1910
        break;
 
1911
    case URL_IS_HTTP:
 
1912
        fd = httpOpen(url, flags, mode, &u);
 
1913
        if (fd == NULL || u == NULL)
 
1914
            break;
 
1915
 
 
1916
        cmd = ((flags & O_WRONLY)
 
1917
                ?  ((flags & O_APPEND) ? "PUT" :
 
1918
                   ((flags & O_CREAT) ? "PUT" : "PUT"))
 
1919
                : "GET");
 
1920
        u->openError = httpReq(fd, cmd, path);
 
1921
        if (u->openError < 0) {
 
1922
            /* XXX make sure that we can exit through ufdClose */
 
1923
            fd = fdLink(fd, "error ctrl (ufdOpen HTTP)");
 
1924
            fd = fdLink(fd, "error data (ufdOpen HTTP)");
 
1925
        } else {
 
1926
            fd->bytesRemain = ((!strcmp(cmd, "GET"))
 
1927
                ?  fd->contentLength : -1);
 
1928
            fd->wr_chunked = ((!strcmp(cmd, "PUT"))
 
1929
                ?  fd->wr_chunked : 0);
 
1930
        }
 
1931
        break;
 
1932
    case URL_IS_DASH:
 
1933
        assert(!(flags & O_RDWR));
 
1934
        fd = fdDup( ((flags & O_WRONLY) ? STDOUT_FILENO : STDIN_FILENO) );
 
1935
        if (fd) {
 
1936
            fdSetIo(fd, ufdio);
 
1937
            fd->rd_timeoutsecs = 600;   /* XXX W2DO? 10 mins? */
 
1938
            fd->contentLength = fd->bytesRemain = -1;
 
1939
        }
 
1940
        break;
 
1941
    case URL_IS_PATH:
 
1942
    case URL_IS_UNKNOWN:
 
1943
    default:
 
1944
        fd = fdOpen(path, flags, mode);
 
1945
        if (fd) {
 
1946
            fdSetIo(fd, ufdio);
 
1947
            fd->rd_timeoutsecs = 1;
 
1948
            fd->contentLength = fd->bytesRemain = -1;
 
1949
        }
 
1950
        break;
 
1951
    }
 
1952
 
 
1953
    if (fd == NULL) return NULL;
 
1954
    fd->urlType = urlType;
 
1955
    if (Fileno(fd) < 0) {
 
1956
        (void) ufdClose(fd);
 
1957
        return NULL;
 
1958
    }
 
1959
DBGIO(fd, (stderr, "==>\tufdOpen(\"%s\",%x,0%o) %s\n", url, (unsigned)flags, (unsigned)mode, fdbg(fd)));
 
1960
    return fd;
 
1961
}
 
1962
 
 
1963
static struct FDIO_s ufdio_s = {
 
1964
  ufdRead, ufdWrite, ufdSeek, ufdClose, XfdLink, XfdFree, XfdNew, fdFileno,
 
1965
  ufdOpen, NULL, fdGetFp, NULL, Mkdir, Chdir, Rmdir, Rename, Unlink
 
1966
};
 
1967
FDIO_t ufdio = /*@-compmempass@*/ &ufdio_s /*@=compmempass@*/ ;
 
1968
 
 
1969
/* =============================================================== */
 
1970
/* Support for GZIP library.
 
1971
 */
 
1972
#ifdef  HAVE_ZLIB_H
 
1973
 
 
1974
#include <zlib.h>
 
1975
 
 
1976
static inline /*@dependent@*/ /*@null@*/ void * gzdFileno(FD_t fd)
 
1977
        /*@*/
 
1978
{
 
1979
    void * rc = NULL;
 
1980
    int i;
 
1981
 
 
1982
    FDSANE(fd);
 
1983
    for (i = fd->nfps; i >= 0; i--) {
 
1984
        FDSTACK_t * fps = &fd->fps[i];
 
1985
        if (fps->io != gzdio)
 
1986
            continue;
 
1987
        rc = fps->fp;
 
1988
        break;
 
1989
    }
 
1990
    
 
1991
    return rc;
 
1992
}
 
1993
 
 
1994
static /*@null@*/ FD_t gzdOpen(const char * path, const char * fmode)
 
1995
        /*@modifies fileSystem @*/
 
1996
{
 
1997
    FD_t fd;
 
1998
    gzFile *gzfile;
 
1999
    if ((gzfile = gzopen(path, fmode)) == NULL)
 
2000
        return NULL;
 
2001
    fd = fdNew("open (gzdOpen)");
 
2002
    fdPop(fd); fdPush(fd, gzdio, gzfile, -1);
 
2003
    
 
2004
DBGIO(fd, (stderr, "==>\tgzdOpen(\"%s\", \"%s\") fd %p %s\n", path, fmode, (fd ? fd : NULL), fdbg(fd)));
 
2005
    return fdLink(fd, "gzdOpen");
 
2006
}
 
2007
 
 
2008
static /*@null@*/ FD_t gzdFdopen(void * cookie, const char *fmode)
 
2009
        /*@modifies internalState, fileSystem @*/
 
2010
{
 
2011
    FD_t fd = c2f(cookie);
 
2012
    int fdno;
 
2013
    gzFile *gzfile;
 
2014
 
 
2015
    if (fmode == NULL) return NULL;
 
2016
    fdno = fdFileno(fd);
 
2017
    fdSetFdno(fd, -1);          /* XXX skip the fdio close */
 
2018
    if (fdno < 0) return NULL;
 
2019
    gzfile = gzdopen(fdno, fmode);
 
2020
    if (gzfile == NULL) return NULL;
 
2021
 
 
2022
    fdPush(fd, gzdio, gzfile, fdno);            /* Push gzdio onto stack */
 
2023
 
 
2024
    return fdLink(fd, "gzdFdopen");
 
2025
}
 
2026
 
 
2027
static int gzdFlush(FD_t fd)
 
2028
        /*@modifies fileSystem @*/
 
2029
{
 
2030
    return gzflush(gzdFileno(fd), Z_SYNC_FLUSH);        /* XXX W2DO? */
 
2031
}
 
2032
 
 
2033
/* =============================================================== */
 
2034
/*@-mustmod@*/          /* LCL: *buf is modified */
 
2035
static ssize_t gzdRead(void * cookie, /*@out@*/ char * buf, size_t count)
 
2036
        /*@modifies internalState, *buf, fileSystem @*/
 
2037
{
 
2038
    FD_t fd = c2f(cookie);
 
2039
    gzFile *gzfile;
 
2040
    ssize_t rc;
 
2041
 
 
2042
    if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
 
2043
    gzfile = gzdFileno(fd);
 
2044
    fdstat_enter(fd, FDSTAT_READ);
 
2045
    rc = gzread(gzfile, buf, count);
 
2046
    /*@-compdef@*/
 
2047
DBGIO(fd, (stderr, "==>\tgzdRead(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
 
2048
    /*@=compdef@*/
 
2049
    if (rc < 0) {
 
2050
        int zerror = 0;
 
2051
        fd->errcookie = gzerror(gzfile, &zerror);
 
2052
        if (zerror == Z_ERRNO) {
 
2053
            fd->syserrno = errno;
 
2054
            fd->errcookie = strerror(fd->syserrno);
 
2055
        }
 
2056
    } else if (rc >= 0) {
 
2057
        fdstat_exit(fd, FDSTAT_READ, rc);
 
2058
        /*@-compdef@*/
 
2059
        if (fd->digest && rc > 0) rpmDigestUpdate(fd->digest, buf, rc);
 
2060
        /*@=compdef@*/
 
2061
    }
 
2062
    return rc;
 
2063
}
 
2064
/*@=mustmod@*/
 
2065
 
 
2066
static ssize_t gzdWrite(void * cookie, const char * buf, size_t count)
 
2067
        /*@modifies internalState, fileSystem @*/
 
2068
{
 
2069
    FD_t fd = c2f(cookie);
 
2070
    gzFile *gzfile;
 
2071
    ssize_t rc;
 
2072
 
 
2073
    if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
 
2074
 
 
2075
    if (fd->digest && count > 0) rpmDigestUpdate(fd->digest, buf, count);
 
2076
 
 
2077
    gzfile = gzdFileno(fd);
 
2078
    fdstat_enter(fd, FDSTAT_WRITE);
 
2079
    rc = gzwrite(gzfile, (void *)buf, count);
 
2080
DBGIO(fd, (stderr, "==>\tgzdWrite(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
 
2081
    if (rc < 0) {
 
2082
        int zerror = 0;
 
2083
        fd->errcookie = gzerror(gzfile, &zerror);
 
2084
        if (zerror == Z_ERRNO) {
 
2085
            fd->syserrno = errno;
 
2086
            fd->errcookie = strerror(fd->syserrno);
 
2087
        }
 
2088
    } else if (rc > 0) {
 
2089
        fdstat_exit(fd, FDSTAT_WRITE, rc);
 
2090
    }
 
2091
    return rc;
 
2092
}
 
2093
 
 
2094
/* XXX zlib-1.0.4 has not */
 
2095
static inline int gzdSeek(void * cookie, _libio_pos_t pos, int whence)
 
2096
        /*@modifies internalState, fileSystem @*/
 
2097
{
 
2098
#ifdef USE_COOKIE_SEEK_POINTER
 
2099
    _IO_off64_t p = *pos;
 
2100
#else
 
2101
    off_t p = pos;
 
2102
#endif
 
2103
    int rc;
 
2104
#if HAVE_GZSEEK
 
2105
    FD_t fd = c2f(cookie);
 
2106
    gzFile *gzfile;
 
2107
 
 
2108
    if (fd == NULL) return -2;
 
2109
    assert(fd->bytesRemain == -1);      /* XXX FIXME */
 
2110
    gzfile = gzdFileno(fd);
 
2111
    fdstat_enter(fd, FDSTAT_SEEK);
 
2112
    rc = gzseek(gzfile, p, whence);
 
2113
DBGIO(fd, (stderr, "==>\tgzdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
 
2114
    if (rc < 0) {
 
2115
        int zerror = 0;
 
2116
        fd->errcookie = gzerror(gzfile, &zerror);
 
2117
        if (zerror == Z_ERRNO) {
 
2118
            fd->syserrno = errno;
 
2119
            fd->errcookie = strerror(fd->syserrno);
 
2120
        }
 
2121
    } else if (rc >= 0) {
 
2122
        fdstat_exit(fd, FDSTAT_SEEK, rc);
 
2123
    }
 
2124
#else
 
2125
    rc = -2;
 
2126
#endif
 
2127
    return rc;
 
2128
}
 
2129
 
 
2130
static int gzdClose( /*@only@*/ void * cookie)
 
2131
        /*@modifies internalState, fileSystem @*/
 
2132
{
 
2133
    FD_t fd = c2f(cookie);
 
2134
    gzFile *gzfile;
 
2135
    int rc;
 
2136
 
 
2137
    gzfile = gzdFileno(fd);
 
2138
 
 
2139
    if (gzfile == NULL) return -2;
 
2140
    fdstat_enter(fd, FDSTAT_CLOSE);
 
2141
    rc = gzclose(gzfile);
 
2142
 
 
2143
    /* XXX TODO: preserve fd if errors */
 
2144
 
 
2145
    if (fd) {
 
2146
DBGIO(fd, (stderr, "==>\tgzdClose(%p) zerror %d %s\n", cookie, rc, fdbg(fd)));
 
2147
        if (rc < 0) {
 
2148
            fd->errcookie = gzerror(gzfile, &rc);
 
2149
            if (rc == Z_ERRNO) {
 
2150
                fd->syserrno = errno;
 
2151
                fd->errcookie = strerror(fd->syserrno);
 
2152
            }
 
2153
        } else if (rc >= 0) {
 
2154
            fdstat_exit(fd, FDSTAT_CLOSE, rc);
 
2155
        }
 
2156
    }
 
2157
 
 
2158
DBGIO(fd, (stderr, "==>\tgzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
 
2159
 
 
2160
    if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "GZDIO", stderr);
 
2161
    if (rc == 0)
 
2162
        fd = fdFree(fd, "open (gzdClose)");
 
2163
    return rc;
 
2164
}
 
2165
 
 
2166
static struct FDIO_s gzdio_s = {
 
2167
  gzdRead, gzdWrite, gzdSeek, gzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
 
2168
  NULL, gzdOpen, gzdFileno, gzdFlush,   NULL, NULL, NULL, NULL, NULL
 
2169
};
 
2170
FDIO_t gzdio = /*@-compmempass@*/ &gzdio_s /*@=compmempass@*/ ;
 
2171
 
 
2172
#endif  /* HAVE_ZLIB_H */
 
2173
 
 
2174
/* =============================================================== */
 
2175
/* Support for BZIP2 library.
 
2176
 */
 
2177
#if HAVE_BZLIB_H
 
2178
 
 
2179
#include <bzlib.h>
 
2180
 
 
2181
#ifdef HAVE_BZ2_1_0
 
2182
# define bzopen  BZ2_bzopen
 
2183
# define bzclose BZ2_bzclose
 
2184
# define bzdopen BZ2_bzdopen
 
2185
# define bzerror BZ2_bzerror
 
2186
# define bzflush BZ2_bzflush
 
2187
# define bzread  BZ2_bzread
 
2188
# define bzwrite BZ2_bzwrite
 
2189
#endif /* HAVE_BZ2_1_0 */
 
2190
 
 
2191
static inline /*@dependent@*/ void * bzdFileno(FD_t fd)
 
2192
        /*@*/
 
2193
{
 
2194
    void * rc = NULL;
 
2195
    int i;
 
2196
 
 
2197
    FDSANE(fd);
 
2198
    for (i = fd->nfps; i >= 0; i--) {
 
2199
        FDSTACK_t * fps = &fd->fps[i];
 
2200
        if (fps->io != bzdio)
 
2201
            continue;
 
2202
        rc = fps->fp;
 
2203
        break;
 
2204
    }
 
2205
    
 
2206
    return rc;
 
2207
}
 
2208
 
 
2209
static /*@null@*/ FD_t bzdOpen(const char * path, const char * mode)
 
2210
        /*@modifies fileSystem @*/
 
2211
{
 
2212
    FD_t fd;
 
2213
    BZFILE *bzfile;;
 
2214
    if ((bzfile = bzopen(path, mode)) == NULL)
 
2215
        return NULL;
 
2216
    fd = fdNew("open (bzdOpen)");
 
2217
    fdPop(fd); fdPush(fd, bzdio, bzfile, -1);
 
2218
    return fdLink(fd, "bzdOpen");
 
2219
}
 
2220
 
 
2221
static /*@null@*/ FD_t bzdFdopen(void * cookie, const char * fmode)
 
2222
        /*@modifies internalState, fileSystem @*/
 
2223
{
 
2224
    FD_t fd = c2f(cookie);
 
2225
    int fdno;
 
2226
    BZFILE *bzfile;
 
2227
 
 
2228
    if (fmode == NULL) return NULL;
 
2229
    fdno = fdFileno(fd);
 
2230
    fdSetFdno(fd, -1);          /* XXX skip the fdio close */
 
2231
    if (fdno < 0) return NULL;
 
2232
    bzfile = bzdopen(fdno, fmode);
 
2233
    if (bzfile == NULL) return NULL;
 
2234
 
 
2235
    fdPush(fd, bzdio, bzfile, fdno);            /* Push bzdio onto stack */
 
2236
 
 
2237
    return fdLink(fd, "bzdFdopen");
 
2238
}
 
2239
 
 
2240
static int bzdFlush(FD_t fd)
 
2241
        /*@modifies fileSystem @*/
 
2242
{
 
2243
    return bzflush(bzdFileno(fd));
 
2244
}
 
2245
 
 
2246
/* =============================================================== */
 
2247
/*@-mustmod@*/          /* LCL: *buf is modified */
 
2248
static ssize_t bzdRead(void * cookie, /*@out@*/ char * buf, size_t count)
 
2249
        /*@modifies internalState, *buf, fileSystem @*/
 
2250
{
 
2251
    FD_t fd = c2f(cookie);
 
2252
    BZFILE *bzfile;
 
2253
    ssize_t rc = 0;
 
2254
 
 
2255
    if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
 
2256
    bzfile = bzdFileno(fd);
 
2257
    fdstat_enter(fd, FDSTAT_READ);
 
2258
    if (bzfile)
 
2259
        /*@-compdef@*/
 
2260
        rc = bzread(bzfile, buf, count);
 
2261
        /*@=compdef@*/
 
2262
    if (rc == -1) {
 
2263
        int zerror = 0;
 
2264
        if (bzfile)
 
2265
            fd->errcookie = bzerror(bzfile, &zerror);
 
2266
    } else if (rc >= 0) {
 
2267
        fdstat_exit(fd, FDSTAT_READ, rc);
 
2268
        /*@-compdef@*/
 
2269
        if (fd->digest && rc > 0) rpmDigestUpdate(fd->digest, buf, rc);
 
2270
        /*@=compdef@*/
 
2271
    }
 
2272
    return rc;
 
2273
}
 
2274
/*@=mustmod@*/
 
2275
 
 
2276
static ssize_t bzdWrite(void * cookie, const char * buf, size_t count)
 
2277
        /*@modifies internalState, fileSystem @*/
 
2278
{
 
2279
    FD_t fd = c2f(cookie);
 
2280
    BZFILE *bzfile;
 
2281
    ssize_t rc;
 
2282
 
 
2283
    if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
 
2284
 
 
2285
    if (fd->digest && count > 0) rpmDigestUpdate(fd->digest, buf, count);
 
2286
 
 
2287
    bzfile = bzdFileno(fd);
 
2288
    fdstat_enter(fd, FDSTAT_WRITE);
 
2289
    rc = bzwrite(bzfile, (void *)buf, count);
 
2290
    if (rc == -1) {
 
2291
        int zerror = 0;
 
2292
        fd->errcookie = bzerror(bzfile, &zerror);
 
2293
    } else if (rc > 0) {
 
2294
        fdstat_exit(fd, FDSTAT_WRITE, rc);
 
2295
    }
 
2296
    return rc;
 
2297
}
 
2298
 
 
2299
static inline int bzdSeek(void * cookie, /*@unused@*/ _libio_pos_t pos,
 
2300
                        /*@unused@*/ int whence)
 
2301
        /*@*/
 
2302
{
 
2303
    FD_t fd = c2f(cookie);
 
2304
 
 
2305
    BZDONLY(fd);
 
2306
    return -2;
 
2307
}
 
2308
 
 
2309
static int bzdClose( /*@only@*/ void * cookie)
 
2310
        /*@modifies internalState, fileSystem @*/
 
2311
{
 
2312
    FD_t fd = c2f(cookie);
 
2313
    BZFILE *bzfile;
 
2314
    int rc;
 
2315
 
 
2316
    bzfile = bzdFileno(fd);
 
2317
 
 
2318
    if (bzfile == NULL) return -2;
 
2319
    fdstat_enter(fd, FDSTAT_CLOSE);
 
2320
    bzclose(bzfile);
 
2321
    rc = 0;     /* XXX FIXME */
 
2322
 
 
2323
    /* XXX TODO: preserve fd if errors */
 
2324
 
 
2325
    if (fd) {
 
2326
        if (rc == -1) {
 
2327
            int zerror = 0;
 
2328
            fd->errcookie = bzerror(bzfile, &zerror);
 
2329
        } else if (rc >= 0) {
 
2330
            fdstat_exit(fd, FDSTAT_CLOSE, rc);
 
2331
        }
 
2332
    }
 
2333
 
 
2334
DBGIO(fd, (stderr, "==>\tbzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
 
2335
 
 
2336
    if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "BZDIO", stderr);
 
2337
    if (rc == 0)
 
2338
        fd = fdFree(fd, "open (bzdClose)");
 
2339
    return rc;
 
2340
}
 
2341
 
 
2342
static struct FDIO_s bzdio_s = {
 
2343
  bzdRead, bzdWrite, bzdSeek, bzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
 
2344
  NULL, bzdOpen, bzdFileno, bzdFlush,   NULL, NULL, NULL, NULL, NULL
 
2345
};
 
2346
FDIO_t bzdio = /*@-compmempass@*/ &bzdio_s /*@=compmempass@*/ ;
 
2347
 
 
2348
#endif  /* HAVE_BZLIB_H */
 
2349
 
 
2350
/* =============================================================== */
 
2351
/*@observer@*/ static const char * getFdErrstr (FD_t fd)
 
2352
        /*@*/
 
2353
{
 
2354
    const char *errstr = NULL;
 
2355
 
 
2356
#ifdef  HAVE_ZLIB_H
 
2357
    if (fdGetIo(fd) == gzdio) {
 
2358
        errstr = fd->errcookie;
 
2359
    } else
 
2360
#endif  /* HAVE_ZLIB_H */
 
2361
 
 
2362
#ifdef  HAVE_BZLIB_H
 
2363
    if (fdGetIo(fd) == bzdio) {
 
2364
        errstr = fd->errcookie;
 
2365
    } else
 
2366
#endif  /* HAVE_BZLIB_H */
 
2367
 
 
2368
    {
 
2369
        errstr = strerror(fd->syserrno);
 
2370
    }
 
2371
 
 
2372
    return errstr;
 
2373
}
 
2374
 
 
2375
/* =============================================================== */
 
2376
 
 
2377
const char *Fstrerror(FD_t fd)
 
2378
{
 
2379
    if (fd == NULL)
 
2380
        return strerror(errno);
 
2381
    FDSANE(fd);
 
2382
    return getFdErrstr(fd);
 
2383
}
 
2384
 
 
2385
#define FDIOVEC(_fd, _vec)      \
 
2386
  ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL)
 
2387
 
 
2388
size_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd) {
 
2389
    fdio_read_function_t *_read;
 
2390
    int rc;
 
2391
 
 
2392
    FDSANE(fd);
 
2393
#ifdef __LCLINT__
 
2394
    *(char *)buf = '\0';
 
2395
#endif
 
2396
DBGIO(fd, (stderr, "==> Fread(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
 
2397
 
 
2398
    if (fdGetIo(fd) == fpio) {
 
2399
        /*@+voidabstract -nullpass@*/
 
2400
        rc = fread(buf, size, nmemb, fdGetFILE(fd));
 
2401
        /*@=voidabstract =nullpass@*/
 
2402
        return rc;
 
2403
    }
 
2404
 
 
2405
    /*@-nullderef@*/
 
2406
    _read = FDIOVEC(fd, read);
 
2407
    /*@=nullderef@*/
 
2408
 
 
2409
    rc = (_read ? (*_read) (fd, buf, size * nmemb) : -2);
 
2410
    return rc;
 
2411
}
 
2412
 
 
2413
size_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd)
 
2414
{
 
2415
    fdio_write_function_t *_write;
 
2416
    int rc;
 
2417
 
 
2418
    FDSANE(fd);
 
2419
DBGIO(fd, (stderr, "==> Fwrite(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
 
2420
 
 
2421
    if (fdGetIo(fd) == fpio) {
 
2422
        /*@+voidabstract -nullpass@*/
 
2423
        rc = fwrite(buf, size, nmemb, fdGetFILE(fd));
 
2424
        /*@=voidabstract =nullpass@*/
 
2425
        return rc;
 
2426
    }
 
2427
 
 
2428
    /*@-nullderef@*/
 
2429
    _write = FDIOVEC(fd, write);
 
2430
    /*@=nullderef@*/
 
2431
 
 
2432
    rc = (_write ? _write(fd, buf, size * nmemb) : -2);
 
2433
    return rc;
 
2434
}
 
2435
 
 
2436
int Fseek(FD_t fd, _libio_off_t offset, int whence) {
 
2437
    fdio_seek_function_t *_seek;
 
2438
#ifdef USE_COOKIE_SEEK_POINTER
 
2439
    _IO_off64_t o64 = offset;
 
2440
    _libio_pos_t pos = &o64;
 
2441
#else
 
2442
    _libio_pos_t pos = offset;
 
2443
#endif
 
2444
 
 
2445
    long int rc;
 
2446
 
 
2447
    FDSANE(fd);
 
2448
DBGIO(fd, (stderr, "==> Fseek(%p,%ld,%d) %s\n", fd, (long)offset, whence, fdbg(fd)));
 
2449
 
 
2450
    if (fdGetIo(fd) == fpio) {
 
2451
        FILE *fp;
 
2452
 
 
2453
        /*@+voidabstract -nullpass@*/
 
2454
        fp = fdGetFILE(fd);
 
2455
        rc = fseek(fp, offset, whence);
 
2456
        /*@=voidabstract =nullpass@*/
 
2457
        return rc;
 
2458
    }
 
2459
 
 
2460
    /*@-nullderef@*/
 
2461
    _seek = FDIOVEC(fd, seek);
 
2462
    /*@=nullderef@*/
 
2463
 
 
2464
    rc = (_seek ? _seek(fd, pos, whence) : -2);
 
2465
    return rc;
 
2466
}
 
2467
 
 
2468
int Fclose(FD_t fd)
 
2469
{
 
2470
    int rc = 0, ec = 0;
 
2471
 
 
2472
    FDSANE(fd);
 
2473
DBGIO(fd, (stderr, "==> Fclose(%p) %s\n", (fd ? fd : NULL), fdbg(fd)));
 
2474
 
 
2475
    fd = fdLink(fd, "Fclose");
 
2476
    while (fd->nfps >= 0) {
 
2477
        FDSTACK_t * fps = &fd->fps[fd->nfps];
 
2478
        
 
2479
        if (fps->io == fpio) {
 
2480
            FILE *fp;
 
2481
            int fpno;
 
2482
 
 
2483
            /*@+voidabstract -nullpass@*/
 
2484
            fp = fdGetFILE(fd);
 
2485
            fpno = fileno(fp);
 
2486
            /*@=voidabstract =nullpass@*/
 
2487
        /* XXX persistent HTTP/1.1 returns the previously opened fp */
 
2488
            if (fd->nfps > 0 && fpno == -1 &&
 
2489
                fd->fps[fd->nfps-1].io == ufdio &&
 
2490
                fd->fps[fd->nfps-1].fp == fp &&
 
2491
                fd->fps[fd->nfps-1].fdno >= 0)
 
2492
            {
 
2493
                if (fp)
 
2494
                    rc = fflush(fp);
 
2495
                fd->nfps--;
 
2496
                /*@-refcounttrans@*/
 
2497
                rc = ufdClose(fd);
 
2498
                /*@=refcounttrans@*/
 
2499
/*@-usereleased@*/
 
2500
                if (fdGetFdno(fd) >= 0)
 
2501
                    break;
 
2502
                fdSetFp(fd, NULL);
 
2503
                fd->nfps++;
 
2504
                if (fp)
 
2505
                    rc = fclose(fp);
 
2506
                fdPop(fd);
 
2507
                if (noLibio)
 
2508
                    fdSetFp(fd, NULL);
 
2509
            } else {
 
2510
                if (fp)
 
2511
                    rc = fclose(fp);
 
2512
                if (fpno == -1) {
 
2513
                    fd = fdFree(fd, "fopencookie (Fclose)");
 
2514
                    fdPop(fd);
 
2515
                }
 
2516
            }
 
2517
        } else {
 
2518
            /*@-nullderef@*/
 
2519
            fdio_close_function_t * _close = FDIOVEC(fd, close);
 
2520
            /*@=nullderef@*/
 
2521
            rc = _close(fd);
 
2522
        }
 
2523
        if (fd->nfps == 0)
 
2524
            break;
 
2525
        if (ec == 0 && rc)
 
2526
            ec = rc;
 
2527
        fdPop(fd);
 
2528
    }
 
2529
    fd = fdFree(fd, "Fclose");
 
2530
    return ec;
 
2531
/*@=usereleased@*/
 
2532
}
 
2533
 
 
2534
/*
 
2535
 * Convert stdio fmode to open(2) mode, filtering out zlib/bzlib flags.
 
2536
 *      returns stdio[0] = '\0' on error.
 
2537
 *
 
2538
 * gzopen:      [0-9] is compession level
 
2539
 * gzopen:      'f' is filtered (Z_FILTERED)
 
2540
 * gzopen:      'h' is Huffman encoding (Z_HUFFMAN_ONLY)
 
2541
 * bzopen:      [1-9] is block size (modulo 100K)
 
2542
 * bzopen:      's' is smallmode
 
2543
 * HACK:        '.' terminates, rest is type of I/O
 
2544
 */
 
2545
static inline void cvtfmode (const char *m,
 
2546
                                /*@out@*/ char *stdio, size_t nstdio,
 
2547
                                /*@out@*/ char *other, size_t nother,
 
2548
                                /*@out@*/ const char **end, /*@out@*/ int * f)
 
2549
        /*@modifies *stdio, *other, *end, *f @*/
 
2550
{
 
2551
    int flags = 0;
 
2552
    char c;
 
2553
 
 
2554
    switch (*m) {
 
2555
    case 'a':
 
2556
        flags |= O_WRONLY | O_CREAT | O_APPEND;
 
2557
        if (--nstdio > 0) *stdio++ = *m;
 
2558
        break;
 
2559
    case 'w':
 
2560
        flags |= O_WRONLY | O_CREAT | O_TRUNC;
 
2561
        if (--nstdio > 0) *stdio++ = *m;
 
2562
        break;
 
2563
    case 'r':
 
2564
        flags |= O_RDONLY;
 
2565
        if (--nstdio > 0) *stdio++ = *m;
 
2566
        break;
 
2567
    default:
 
2568
        *stdio = '\0';
 
2569
        return;
 
2570
        /*@notreached@*/ break;
 
2571
    }
 
2572
    m++;
 
2573
 
 
2574
    while ((c = *m++) != '\0') {
 
2575
        switch (c) {
 
2576
        case '.':
 
2577
            break;
 
2578
        case '+':
 
2579
            flags &= ~(O_RDONLY|O_WRONLY);
 
2580
            flags |= O_RDWR;
 
2581
            if (--nstdio > 0) *stdio++ = c;
 
2582
            continue;
 
2583
        case 'b':
 
2584
            if (--nstdio > 0) *stdio++ = c;
 
2585
            continue;
 
2586
        case 'x':
 
2587
            flags |= O_EXCL;
 
2588
            if (--nstdio > 0) *stdio++ = c;
 
2589
            continue;
 
2590
        default:
 
2591
            if (--nother > 0) *other++ = c;
 
2592
            continue;
 
2593
        }
 
2594
        break;
 
2595
    }
 
2596
 
 
2597
    *stdio = *other = '\0';
 
2598
    if (end != NULL)
 
2599
        *end = (*m != '\0' ? m : NULL);
 
2600
    if (f != NULL)
 
2601
        *f = flags;
 
2602
}
 
2603
 
 
2604
#if _USE_LIBIO
 
2605
#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
 
2606
/* XXX retrofit glibc-2.1.x typedef on glibc-2.0.x systems */
 
2607
typedef _IO_cookie_io_functions_t cookie_io_functions_t;
 
2608
#endif
 
2609
#endif
 
2610
 
 
2611
FD_t Fdopen(FD_t ofd, const char *fmode)
 
2612
{
 
2613
    char stdio[20], other[20], zstdio[20];
 
2614
    const char *end = NULL;
 
2615
    FDIO_t iof = NULL;
 
2616
    FD_t fd = ofd;
 
2617
 
 
2618
if (_rpmio_debug)
 
2619
fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
 
2620
    FDSANE(fd);
 
2621
 
 
2622
    if (fmode == NULL)
 
2623
        return NULL;
 
2624
 
 
2625
    cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
 
2626
    if (stdio[0] == '\0')
 
2627
        return NULL;
 
2628
    zstdio[0] = '\0';
 
2629
    strncat(zstdio, stdio, sizeof(zstdio) - strlen(zstdio));
 
2630
    strncat(zstdio, other, sizeof(zstdio) - strlen(zstdio));
 
2631
 
 
2632
    if (end == NULL && other[0] == '\0')
 
2633
        /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
 
2634
 
 
2635
    if (end && *end) {
 
2636
        if (!strcmp(end, "fdio")) {
 
2637
            iof = fdio;
 
2638
        } else if (!strcmp(end, "gzdio")) {
 
2639
            iof = gzdio;
 
2640
            fd = gzdFdopen(fd, zstdio);
 
2641
#if HAVE_BZLIB_H
 
2642
        } else if (!strcmp(end, "bzdio")) {
 
2643
            iof = bzdio;
 
2644
            fd = bzdFdopen(fd, zstdio);
 
2645
#endif
 
2646
        } else if (!strcmp(end, "ufdio")) {
 
2647
            iof = ufdio;
 
2648
        } else if (!strcmp(end, "fadio")) {
 
2649
            iof = fadio;
 
2650
        } else if (!strcmp(end, "fpio")) {
 
2651
            iof = fpio;
 
2652
            if (noLibio) {
 
2653
                int fdno = Fileno(fd);
 
2654
                FILE * fp = fdopen(fdno, stdio);
 
2655
/*@+voidabstract -nullpass@*/
 
2656
if (_rpmio_debug)
 
2657
fprintf(stderr, "*** Fdopen fpio fp %p\n", (void *)fp);
 
2658
/*@=voidabstract =nullpass@*/
 
2659
                if (fp == NULL)
 
2660
                    return NULL;
 
2661
                /* XXX gzdio/bzdio use fp for private data */
 
2662
                /*@+voidabstract@*/
 
2663
                if (fdGetFp(fd) == NULL)
 
2664
                    fdSetFp(fd, fp);
 
2665
                fdPush(fd, fpio, fp, fdno);     /* Push fpio onto stack */
 
2666
                /*@=voidabstract@*/
 
2667
            }
 
2668
        }
 
2669
    } else if (other[0] != '\0') {
 
2670
        for (end = other; *end && strchr("0123456789fh", *end); end++)
 
2671
            {};
 
2672
        if (*end == '\0') {
 
2673
            iof = gzdio;
 
2674
            fd = gzdFdopen(fd, zstdio);
 
2675
        }
 
2676
    }
 
2677
    if (iof == NULL)
 
2678
        /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
 
2679
 
 
2680
    if (!noLibio) {
 
2681
        FILE * fp = NULL;
 
2682
 
 
2683
#if _USE_LIBIO
 
2684
        {   cookie_io_functions_t ciof;
 
2685
            ciof.read = iof->read;
 
2686
            ciof.write = iof->write;
 
2687
            ciof.seek = iof->seek;
 
2688
            ciof.close = iof->close;
 
2689
            fp = fopencookie(fd, stdio, ciof);
 
2690
DBGIO(fd, (stderr, "==> fopencookie(%p,\"%s\",*%p) returns fp %p\n", fd, stdio, iof, fp));
 
2691
        }
 
2692
#endif
 
2693
 
 
2694
        if (fp) {
 
2695
            /* XXX gzdio/bzdio use fp for private data */
 
2696
            /*@+voidabstract -nullpass@*/
 
2697
            if (fdGetFp(fd) == NULL)
 
2698
                fdSetFp(fd, fp);
 
2699
            fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
 
2700
            /*@=voidabstract =nullpass@*/
 
2701
            fd = fdLink(fd, "fopencookie");
 
2702
        }
 
2703
    }
 
2704
 
 
2705
DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd)));
 
2706
    /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
 
2707
}
 
2708
 
 
2709
FD_t Fopen(const char *path, const char *fmode)
 
2710
{
 
2711
    char stdio[20], other[20];
 
2712
    const char *end = NULL;
 
2713
    mode_t perms = 0666;
 
2714
    int flags;
 
2715
    FD_t fd;
 
2716
 
 
2717
    if (path == NULL || fmode == NULL)
 
2718
        return NULL;
 
2719
 
 
2720
    cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
 
2721
    if (stdio[0] == '\0')
 
2722
        return NULL;
 
2723
 
 
2724
    if (end == NULL || !strcmp(end, "fdio")) {
 
2725
if (_rpmio_debug)
 
2726
fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode);
 
2727
        fd = fdOpen(path, flags, perms);
 
2728
        if (fdFileno(fd) < 0) {
 
2729
            if (fd) (void) fdClose(fd);
 
2730
            return NULL;
 
2731
        }
 
2732
    } else if (!strcmp(end, "fadio")) {
 
2733
if (_rpmio_debug)
 
2734
fprintf(stderr, "*** Fopen fadio path %s fmode %s\n", path, fmode);
 
2735
        fd = fadio->_open(path, flags, perms);
 
2736
        if (fdFileno(fd) < 0) {
 
2737
            /*@-refcounttrans@*/ (void) fdClose(fd); /*@=refcounttrans@*/
 
2738
            return NULL;
 
2739
        }
 
2740
    } else {
 
2741
        FILE *fp;
 
2742
        int fdno;
 
2743
        int isHTTP = 0;
 
2744
 
 
2745
        /* XXX gzdio and bzdio here too */
 
2746
 
 
2747
        switch (urlIsURL(path)) {
 
2748
        case URL_IS_HTTP:
 
2749
            isHTTP = 1;
 
2750
            /*@fallthrough@*/
 
2751
        case URL_IS_PATH:
 
2752
        case URL_IS_DASH:
 
2753
        case URL_IS_FTP:
 
2754
        case URL_IS_UNKNOWN:
 
2755
if (_rpmio_debug)
 
2756
fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode);
 
2757
            fd = ufdOpen(path, flags, perms);
 
2758
            if (fd == NULL || fdFileno(fd) < 0)
 
2759
                return fd;
 
2760
            break;
 
2761
        default:
 
2762
if (_rpmio_debug)
 
2763
fprintf(stderr, "*** Fopen WTFO path %s fmode %s\n", path, fmode);
 
2764
            return NULL;
 
2765
            /*@notreached@*/ break;
 
2766
        }
 
2767
 
 
2768
        /* XXX persistent HTTP/1.1 returns the previously opened fp */
 
2769
        if (isHTTP && ((fp = fdGetFp(fd)) != NULL) && ((fdno = fdGetFdno(fd)) >= 0)) {
 
2770
            /*@+voidabstract@*/
 
2771
            fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
 
2772
            /*@=voidabstract@*/
 
2773
            return fd;
 
2774
        }
 
2775
    }
 
2776
 
 
2777
    if (fd)
 
2778
        fd = Fdopen(fd, fmode);
 
2779
    return fd;
 
2780
}
 
2781
 
 
2782
int Fflush(FD_t fd)
 
2783
{
 
2784
    void * vh;
 
2785
    if (fd == NULL) return -1;
 
2786
    if (fdGetIo(fd) == fpio)
 
2787
        /*@+voidabstract -nullpass@*/
 
2788
        return fflush(fdGetFILE(fd));
 
2789
        /*@=voidabstract =nullpass@*/
 
2790
 
 
2791
    vh = fdGetFp(fd);
 
2792
    if (vh && fdGetIo(fd) == gzdio)
 
2793
        return gzdFlush(vh);
 
2794
#if HAVE_BZLIB_H
 
2795
    if (vh && fdGetIo(fd) == bzdio)
 
2796
        return bzdFlush(vh);
 
2797
#endif
 
2798
 
 
2799
    return 0;
 
2800
}
 
2801
 
 
2802
int Ferror(FD_t fd)
 
2803
{
 
2804
    int i, rc = 0;
 
2805
 
 
2806
    if (fd == NULL) return -1;
 
2807
    for (i = fd->nfps; rc == 0 && i >= 0; i--) {
 
2808
        FDSTACK_t * fps = &fd->fps[i];
 
2809
        int ec;
 
2810
        
 
2811
        if (fps->io == fpio) {
 
2812
            /*@+voidabstract -nullpass@*/
 
2813
            ec = ferror(fdGetFILE(fd));
 
2814
            /*@=voidabstract =nullpass@*/
 
2815
        } else if (fps->io == gzdio) {
 
2816
            ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
 
2817
            i--;        /* XXX fdio under gzdio always has fdno == -1 */
 
2818
#if HAVE_BZLIB_H
 
2819
        } else if (fps->io == bzdio) {
 
2820
            ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
 
2821
            i--;        /* XXX fdio under bzdio always has fdno == -1 */
 
2822
#endif
 
2823
        } else {
 
2824
        /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */
 
2825
            ec = (fdFileno(fd) < 0 ? -1 : 0);
 
2826
        }
 
2827
 
 
2828
        if (rc == 0 && ec)
 
2829
            rc = ec;
 
2830
    }
 
2831
DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
 
2832
    return rc;
 
2833
}
 
2834
 
 
2835
int Fileno(FD_t fd)
 
2836
{
 
2837
    int i, rc = -1;
 
2838
 
 
2839
    for (i = fd->nfps ; rc == -1 && i >= 0; i--) {
 
2840
        rc = fd->fps[i].fdno;
 
2841
    }
 
2842
DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd)));
 
2843
    return rc;
 
2844
}
 
2845
 
 
2846
/* XXX this is naive */
 
2847
int Fcntl(FD_t fd, int op, void *lip)
 
2848
{
 
2849
    return fcntl(Fileno(fd), op, lip);
 
2850
}
 
2851
 
 
2852
/* =============================================================== */
 
2853
/* Helper routines that may be generally useful.
 
2854
 */
 
2855
 
 
2856
/* XXX falloc.c: analogues to pread(3)/pwrite(3). */
 
2857
ssize_t Pread(FD_t fd, void * buf, size_t count, _libio_off_t offset)
 
2858
{
 
2859
    if (Fseek(fd, offset, SEEK_SET) < 0)
 
2860
        return -1;
 
2861
    return Fread(buf, sizeof(char), count, fd);
 
2862
}
 
2863
 
 
2864
ssize_t Pwrite(FD_t fd, const void * buf, size_t count, _libio_off_t offset)
 
2865
{
 
2866
    if (Fseek(fd, offset, SEEK_SET) < 0)
 
2867
        return -1;
 
2868
    return Fwrite(buf, sizeof(char), count, fd);
 
2869
}
 
2870
 
 
2871
static struct FDIO_s fpio_s = {
 
2872
  ufdRead, ufdWrite, fdSeek, ufdClose, XfdLink, XfdFree, XfdNew, fdFileno,
 
2873
  ufdOpen, NULL, fdGetFp, NULL, Mkdir, Chdir, Rmdir, Rename, Unlink
 
2874
};
 
2875
FDIO_t fpio = /*@-compmempass@*/ &fpio_s /*@=compmempass@*/ ;