summaryrefslogtreecommitdiff
path: root/vms/vms_pk.c
blob: 9aa203d8a1b51c725ebc53c2d9522f3a014dfd46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
/*
  Copyright (c) 1990-2007 Info-ZIP.  All rights reserved.

  See the accompanying file LICENSE, version 2007-Mar-4 or later
  (the contents of which are also included in zip.h) for terms of use.
  If, for some reason, all these files are missing, the Info-ZIP license
  also may be found at:  ftp://ftp.info-zip.org/pub/infozip/license.html
*/
/*
 *    vms_pk.c  by Igor Mandrichenko
 *
 *    version 2.0       20-Mar-1993
 *                      Generates PKWARE version of VMS attributes
 *                      extra field according to appnote 2.0.
 *                      Uses low level QIO-ACP interface.
 *    version 2.0-1     10-Apr-1993
 *                      Save ACLs
 *    version 2.1       24-Aug-1993
 *                      By default produce 0x010C extra record ID instead of
 *                      PKWARE's 0x000C. The format is mostly compatible with
 *                      PKWARE.
 *                      Incompatibility (?): zip produces multiple ACE
 *                      fields.
 *    version 2.1-1     Clean extra fields in vms_get_attributes().
 *                      Fixed bug with EOF.
 *    version 2.1-2     15-Sep-1995, Chr. Spieler
 *                      Removed extra fields cleanup from vms_get_attributes().
 *                      This is now done in zipup.c
 *                      Modified (according to UnZip's vms.[ch]) the fib stuff
 *                      for DEC C (AXP,VAX) support.
 *    version 2.2       28-Sep-1995, Chr. Spieler
 *                      Reorganized code for easier maintance of the two
 *                      incompatible flavours (IM style and PK style) VMS
 *                      attribute support.  Generic functions (common to
 *                      both flavours) are now collected in a `wrapper'
 *                      source file that includes one of the VMS attribute
 *                      handlers.
 *                      Made extra block header conforming to PKware's
 *                      specification (extra block header has a length
 *                      of four bytes, two bytes for a signature, and two
 *                      bytes for the length of the block excluding this
 *                      header.
 *    version 2.2-1     19-Oct-1995, Chr. Spieler
 *                      Fixed bug in CRC calculation.
 *                      Use official PK VMS extra field id.
 *    version 2.2-2     21-Nov-1997, Chr. Spieler
 *                      Fixed bug in vms_get_attributes() for directory
 *                      entries (access to uninitialized ioctx record).
 *                      Removed unused second arg for vms_open().
 *    version 2.2-3     04-Apr-1999, Chr. Spieler
 *                      Changed calling interface of vms_get_attributes()
 *                      to accept a void pointer as first argument.
 *    version 2.2-4     26-Jan-2002, Chr. Spieler
 *                      Modified vms_read() to handle files larger than 2GByte
 *                      (up to size limit of "unsigned long", resp. 4GByte).
 *    version 3.0       20-Oct-2004, Steven Schweda.
 *                      Changed vms_read() to read all the allocated
 *                      blocks in a file, for sure.  Changed the default
 *                      chunk size from 16K to 32K.  Changed to use the
 *                      new typedef for the ioctx structure.  Moved the
 *                      VMS_PK_EXTRA test into here from VMS.C to allow
 *                      more general automatic dependency generation.
 *                      08-Feb-2005, SMS.
 *                      Changed to accomodate ODS5 extended file names:
 *                      NAM structure -> NAM[L], and so on.  (VMS.H.)
 *                      Added some should-never-appear error messages in
 *                      vms_open().
 */

#ifdef VMS                      /* For VMS only ! */

#ifdef VMS_PK_EXTRA

#include <ssdef.h>

#ifndef VMS_ZIP
#define VMS_ZIP
#endif

#include "crc32.h"
#include "vms.h"
#include "vmsdefs.h"

#ifndef ERR
#define ERR(x) (((x)&1)==0)
#endif

#ifndef NULL
#define NULL (void*)(0L)
#endif

#ifndef UTIL

static PK_info_t PK_def_info =
{
        ATR$C_RECATTR,  ATR$S_RECATTR,  {0},
        ATR$C_UCHAR,    ATR$S_UCHAR,    {0},
        ATR$C_CREDATE,  ATR$S_CREDATE,  {0},
        ATR$C_REVDATE,  ATR$S_REVDATE,  {0},
        ATR$C_EXPDATE,  ATR$S_EXPDATE,  {0},
        ATR$C_BAKDATE,  ATR$S_BAKDATE,  {0},
        ATR$C_ASCDATES, sizeof(ush),    0,
        ATR$C_UIC,      ATR$S_UIC,      {0},
        ATR$C_FPRO,     ATR$S_FPRO,     {0},
        ATR$C_RPRO,     ATR$S_RPRO,     {0},
        ATR$C_JOURNAL,  ATR$S_JOURNAL,  {0}
};

/* File description structure for Zip low level I/O */
typedef struct
{
    struct iosb         iosb;
    long                vbn;
    uzoff_t             size;
    uzoff_t             rest;
    int                 status;
    ush                 chan;
    ush                 chan_pad;       /* alignment member */
    long                acllen;
    uch                 aclbuf[ATR$S_READACL];
    PK_info_t           PKi;
} ioctx_t;


/* Forward declarations of public functions: */
ioctx_t *vms_open(char *file);
unsigned int  vms_read(register ioctx_t *ctx,
                       register char *buf, register unsigned int size);
int  vms_error(ioctx_t *ctx);
int  vms_rewind(ioctx_t *ctx);
int  vms_get_attributes(ioctx_t *ctx, struct zlist far *z,
                        iztimes *z_utim);
int  vms_close(ioctx_t *ctx);


#define BLOCK_BYTES 512


/*---------------*
 |  vms_open()   |
 *---------------*
 |  This routine opens file for reading fetching its attributes.
 |  Returns pointer to file description structure.
 */

ioctx_t *vms_open(file)
char *file;
{
    static struct atrdef        Atr[VMS_MAX_ATRCNT+1];
    static struct NAM_STRUCT    Nam;
    static struct fibdef        Fib;
    static struct dsc$descriptor FibDesc =
        {sizeof(Fib),DSC$K_DTYPE_Z,DSC$K_CLASS_S,(char *)&Fib};
    static struct dsc$descriptor_s DevDesc =
        {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,&Nam.NAM_DVI[1]};
    static char EName[NAM_MAXRSS];
    static char RName[NAM_MAXRSS];

    struct FAB Fab;
    register ioctx_t *ctx;
    register struct fatdef *fat;
    int status;
    int i;
    ulg efblk;
    ulg hiblk;

    if ( (ctx=(ioctx_t *)malloc(sizeof(ioctx_t))) == NULL )
        return NULL;
    ctx -> PKi = PK_def_info;

#define FILL_REQ(ix,id,b)   {       \
    Atr[ix].atr$l_addr = GVTC &(b);      \
    Atr[ix].atr$w_type = (id);      \
    Atr[ix].atr$w_size = sizeof(b); \
}

    FILL_REQ(0, ATR$C_RECATTR,  ctx->PKi.ra);
    FILL_REQ(1, ATR$C_UCHAR,    ctx->PKi.uc);
    FILL_REQ(2, ATR$C_REVDATE,  ctx->PKi.rd);
    FILL_REQ(3, ATR$C_EXPDATE,  ctx->PKi.ed);
    FILL_REQ(4, ATR$C_CREDATE,  ctx->PKi.cd);
    FILL_REQ(5, ATR$C_BAKDATE,  ctx->PKi.bd);
    FILL_REQ(6, ATR$C_ASCDATES, ctx->PKi.rn);
    FILL_REQ(7, ATR$C_JOURNAL,  ctx->PKi.jr);
    FILL_REQ(8, ATR$C_RPRO,     ctx->PKi.rp);
    FILL_REQ(9, ATR$C_FPRO,     ctx->PKi.fp);
    FILL_REQ(10,ATR$C_UIC,      ctx->PKi.ui);
    FILL_REQ(11,ATR$C_ACLLENGTH,ctx->acllen);
    FILL_REQ(12,ATR$C_READACL,  ctx->aclbuf);

    Atr[13].atr$w_type = 0;     /* End of ATR list */
    Atr[13].atr$w_size = 0;
    Atr[13].atr$l_addr = GVTC NULL;

    /* Initialize RMS structures.  We need a NAM[L] to retrieve the FID. */
    Fab = cc$rms_fab;
    Nam = CC_RMS_NAM;
    Fab.FAB_NAM = &Nam; /* FAB has an associated NAM[L]. */

#ifdef NAML$C_MAXRSS

    Fab.fab$l_dna =(char *) -1;         /* Using NAML for default name. */
    Fab.fab$l_fna = (char *) -1;        /* Using NAML for file name. */

#endif /* def NAML$C_MAXRSS */

    FAB_OR_NAML( Fab, Nam).FAB_OR_NAML_FNA = file ;     /* File name. */
    FAB_OR_NAML( Fab, Nam).FAB_OR_NAML_FNS = strlen(file);
    Nam.NAM_ESA = EName; /* expanded filename */
    Nam.NAM_ESS = sizeof(EName);
    Nam.NAM_RSA = RName; /* resultant filename */
    Nam.NAM_RSS = sizeof(RName);

    /* Do $PARSE and $SEARCH here. */
    status = sys$parse(&Fab);

    if (!(status & 1))
    {
        fprintf( stderr,
         " vms_open(): $parse sts = %%x%08x.\n", status);
        return NULL;
    }

#ifdef NAML$M_OPEN_SPECIAL
    /* 2007-02-28 SMS.
     * If processing symlinks as symlinks ("-y"), then $SEARCH for the
     * link, not the target file.
     */
    if (linkput)
    {
        Nam.naml$v_open_special = 1;
    }
#endif /* def NAML$M_OPEN_SPECIAL */

    /* Search for the first file.  If none, signal error. */
    status = sys$search(&Fab);

    if (!(status & 1))
    {
        fprintf( stderr,
         " vms_open(): $search sts = %%x%08x.\n", status);
        return NULL;
    }

    /* Initialize Device name length.  Note that this points into the
       NAM[L] to get the device name filled in by the $PARSE, $SEARCH
       services.
    */
    DevDesc.dsc$w_length = Nam.NAM_DVI[0];

    status = sys$assign(&DevDesc,&ctx->chan,0,0);

    if (!(status & 1))
    {
        fprintf( stderr,
         " vms_open(): $assign sts = %%x%08x.\n", status);
        return NULL;
    }

    /* Move the FID (and not the DID) into the FIB.
       2005=02-08 SMS.
       Note that only the FID is needed, not the DID, and not the file
       name.  Setting these other items causes failures on ODS5.
    */
    Fib.FIB$L_ACCTL = FIB$M_NOWRITE;

    for (i = 0; i < 3; i++)
    {
        Fib.FIB$W_FID[ i] = Nam.NAM_FID[ i];
        Fib.FIB$W_DID[ i] = 0;
    }

    /* Use the IO$_ACCESS function to return info about the file. */
    status = sys$qiow( 0, ctx->chan,
     (IO$_ACCESS| IO$M_ACCESS), &ctx->iosb, 0, 0,
     &FibDesc, 0, 0, 0, Atr, 0);

    if (ERR(status) || ERR(status = ctx->iosb.status))
    {
        vms_close(ctx);
        fprintf( stderr,
         " vms_open(): $qiow (access) sts = %%x%08x, iosb sts = %%x%08x.\n",
         status, ctx->iosb.status);
        return NULL;
    }

    fat = (struct fatdef *)&(ctx -> PKi.ra);

#define SWAPW(x)        ( (((x)>>16)&0xFFFF) + ((x)<<16) )

    efblk = SWAPW(fat->fat$l_efblk);
    hiblk = SWAPW(fat->fat$l_hiblk);

    if (efblk == 0)
    {
        /* Only known size is all allocated blocks.
           (This occurs with a zero-length file, for example.)
        */
        ctx -> size =
        ctx -> rest = ((uzoff_t) hiblk)* BLOCK_BYTES;
    }
    else
    {
        /* Store normal (used) size in ->size.
           If only one -V, store normal (used) size in ->rest.
           If multiple -V, store allocated-blocks size in ->rest.
        */
        ctx -> size =
         (((uzoff_t) efblk)- 1)* BLOCK_BYTES+ fat -> fat$w_ffbyte;

        if (vms_native < 2)
            ctx -> rest = ctx -> size;
        else
            ctx -> rest = ((uzoff_t) hiblk)* BLOCK_BYTES;
    }

    ctx -> status = SS$_NORMAL;
    ctx -> vbn = 1;
    return ctx;
}


#define KByte (2* BLOCK_BYTES)
#define MAX_READ_BYTES (32* KByte)

/*----------------*
 |   vms_read()   |
 *----------------*
 |   Reads file in (multi-)block-sized chunks into the buffer.
 |   Stops on EOF. Returns number of bytes actually read.
 |   Note: This function makes no sense (and will error) if the buffer
 |   size ("size") is not a multiple of the disk block size (512).
 */

size_t vms_read( ctx, buf, size)
ioctx_t *ctx;
char *buf;
size_t size;
{
    int act_cnt;
    uzoff_t rest_rndup;
    int status;
    size_t bytes_read = 0;

    /* If previous read hit EOF, fail early. */
    if (ctx -> status == SS$_ENDOFFILE)
        return 0;               /* EOF. */

    /* If no more expected to be read, fail early. */
    if (ctx -> rest == 0)
        return 0;               /* Effective EOF. */

    /* If request is smaller than a whole block, fail.
       This really should never happen.  (assert()?)
    */
    if (size < BLOCK_BYTES)
        return 0;

    /* Note that on old VMS VAX versions (like V5.5-2), QIO[W] may fail
       with status %x0000034c (= %SYSTEM-F-IVBUFLEN, invalid buffer
       length) when size is not a multiple of 512.  Thus the requested
       size is boosted as needed, but the IOSB byte count returned is
       reduced when it exceeds the actual bytes remaining (->rest).
    */

    /* Adjust request size as appropriate. */
    if (size > MAX_READ_BYTES)
    {
        /* Restrict request to MAX_READ_BYTES. */
        size = MAX_READ_BYTES;
    }
    else
    {
        /* Round odd-ball request up to the next whole block.
           This really should never happen.  (assert()?)
        */
        size = (size+ BLOCK_BYTES- 1)& ~(BLOCK_BYTES- 1);
    }
    rest_rndup = (ctx -> rest+ BLOCK_BYTES- 1)& ~(BLOCK_BYTES- 1);

    /* Read (QIOW) until error or "size" bytes have been read. */
    do
    {
        /* Reduce "size" when next (last) read would overrun the EOF,
           but never below one block (so we'll always get a nice EOF).
        */
        if (size > rest_rndup)
            size = rest_rndup;

        status = sys$qiow( 0, ctx->chan, IO$_READVBLK,
            &ctx->iosb, 0, 0,
            buf, size, ctx->vbn, 0, 0, 0);

        /* If initial status was good, use final status. */
        if ( !ERR(status) )
                status = ctx->iosb.status;

        if ( !ERR(status) || status == SS$_ENDOFFILE )
        {
            act_cnt = ctx->iosb.count;
            /* Ignore whole-block boost when remainder is smaller. */
            if (act_cnt > ctx->rest)
            {
                act_cnt = ctx->rest;
                status = SS$_ENDOFFILE;
            }
            /* Adjust counters/pointers according to delivered bytes. */
            size -= act_cnt;
            buf += act_cnt;
            bytes_read += act_cnt;
            ctx->vbn += ctx->iosb.count/ BLOCK_BYTES;
        }

    } while ( !ERR(status) && (size > 0) );

    if (!ERR(status))
    {
        /* Record any successful status as SS$_NORMAL. */
        ctx -> status = SS$_NORMAL;
    }
    else if (status == SS$_ENDOFFILE)
    {
        /* Record EOF as SS$_ENDOFFILE.  (Ignore error status codes?) */
        ctx -> status = SS$_ENDOFFILE;
    }

    /* Decrement bytes-to-read.  Return the total bytes read. */
    ctx -> rest -= bytes_read;

    return bytes_read;
}

/*-----------------*
 |   vms_error()   |
 *-----------------*
 |   Returns whether last operation on the file caused an error
 */

int vms_error(ctx)
ioctx_t *ctx;
{   /* EOF is not actual error */
    return ERR(ctx->status) && (ctx->status != SS$_ENDOFFILE);
}

/*------------------*
 |   vms_rewind()   |
 *------------------*
 |   Rewinds file to the beginning for the next vms_read().
 */

int vms_rewind(ctx)
ioctx_t *ctx;
{
    ctx -> vbn = 1;
    ctx -> rest = ctx -> size;
    return 0;
}

/*--------------------------*
 |   vms_get_attributes()   |
 *--------------------------*
 |   Malloc a PKWARE extra field and fill with file attributes. Returns
 |   error number of the ZE_??? class.
 |   If the passed ioctx record "FILE *" pointer is NULL, vms_open() is
 |   called to fetch the file attributes.
 |   When `vms_native' is not set, a generic "UT" type timestamp extra
 |   field is generated instead.
 |
 |   2004-11-11 SMS.
 |   Changed to use separate storage for ->extra and ->cextra.  Zip64
 |   processing may move (reallocate) one and not the other.
 */

int vms_get_attributes(ctx, z, z_utim)
ioctx_t *ctx;           /* Internal file control structure. */
struct zlist far *z;    /* Zip entry to compress. */
iztimes *z_utim;
{
    byte    *p;
    byte    *xtra;
    byte    *cxtra;
    struct  PK_header    *h;
    extent  l;
    int     notopened;

    if ( !vms_native )
    {
#ifdef USE_EF_UT_TIME
        /*
         *  A `portable' zipfile entry is created. Create an "UT" extra block
         *  containing UNIX style modification time stamp in UTC, which helps
         *  maintaining the `real' "last modified" time when the archive is
         *  transfered across time zone boundaries.
         */
#  ifdef IZ_CHECK_TZ
        if (!zp_tz_is_valid)
            return ZE_OK;       /* skip silently if no valid TZ info */
#  endif

        if ((xtra = (uch *) malloc( EB_HEADSIZE+ EB_UT_LEN( 1))) == NULL)
            return ZE_MEM;

        if ((cxtra = (uch *) malloc( EB_HEADSIZE+ EB_UT_LEN( 1))) == NULL)
            return ZE_MEM;

        /* Fill xtra[] with data. */
        xtra[ 0] = 'U';
        xtra[ 1] = 'T';
        xtra[ 2] = EB_UT_LEN(1);        /* length of data part of e.f. */
        xtra[ 3] = 0;
        xtra[ 4] = EB_UT_FL_MTIME;
        xtra[ 5] = (byte) (z_utim->mtime);
        xtra[ 6] = (byte) (z_utim->mtime >> 8);
        xtra[ 7] = (byte) (z_utim->mtime >> 16);
        xtra[ 8] = (byte) (z_utim->mtime >> 24);

        /* Copy xtra[] data into cxtra[]. */
        memcpy( cxtra, xtra, (EB_HEADSIZE+ EB_UT_LEN( 1)));

        /* Set sizes and pointers. */
        z->cext = z->ext = (EB_HEADSIZE+ EB_UT_LEN( 1));
        z->extra = (char*) xtra;
        z->cextra = (char*) cxtra;

#endif /* USE_EF_UT_TIME */

        return ZE_OK;
    }

    notopened = (ctx == NULL);
    if ( notopened && ((ctx = vms_open(z->name)) == NULL) )
        return ZE_OPEN;

    l = PK_HEADER_SIZE + sizeof(ctx->PKi);
    if (ctx->acllen > 0)
        l += PK_FLDHDR_SIZE + ctx->acllen;

    if ((xtra = (uch *) malloc( l)) == NULL)
        return ZE_MEM;

    if ((cxtra = (uch *) malloc( l)) == NULL)
        return ZE_MEM;

    /* Fill xtra[] with data. */

    h = (struct PK_header *) xtra;
    h->tag = PK_SIGNATURE;
    h->size = l - EB_HEADSIZE;
    p = (h->data);

    /* Copy default set of attributes */
    memcpy(h->data, (char*)&(ctx->PKi), sizeof(ctx->PKi));
    p += sizeof(ctx->PKi);

    if ( ctx->acllen > 0 )
    {
        struct PK_field *f;

        if (dosify)
            zipwarn("file has ACL, may be incompatible with PKUNZIP","");

        f = (struct PK_field *)p;
        f->tag = ATR$C_ADDACLENT;
        f->size = ctx->acllen;
        memcpy((char *)&(f->value[0]), ctx->aclbuf, ctx->acllen);
        p += PK_FLDHDR_SIZE + ctx->acllen;
    }


    h->crc32 = CRCVAL_INITIAL;                  /* Init CRC register */
    h->crc32 = crc32(h->crc32, (uch *)(h->data), l - PK_HEADER_SIZE);

    /* Copy xtra[] data into cxtra[]. */
    memcpy( cxtra, xtra, l);

    /* Set sizes and pointers. */
    z->ext = z->cext = l;
    z->extra = (char *) xtra;
    z->cextra = (char *) cxtra;

    if (notopened)              /* close "ctx", if we have opened it here */
        vms_close(ctx);

    return ZE_OK;
}


int vms_close(ctx)
ioctx_t *ctx;
{
        sys$dassgn(ctx->chan);
        free(ctx);
        return 0;
}

#endif /* !_UTIL */

#endif /* def VMS_PK_EXTRA */

#endif /* VMS */