1/*
2  Copyright (c) 1990-2007 Info-ZIP.  All rights reserved.
3
4  See the accompanying file LICENSE, version 2007-Mar-4 or later
5  (the contents of which are also included in zip.h) for terms of use.
6  If, for some reason, all these files are missing, the Info-ZIP license
7  also may be found at:  ftp://ftp.info-zip.org/pub/infozip/license.html
8*/
9/*
10 *    vms_pk.c  by Igor Mandrichenko
11 *
12 *    version 2.0       20-Mar-1993
13 *                      Generates PKWARE version of VMS attributes
14 *                      extra field according to appnote 2.0.
15 *                      Uses low level QIO-ACP interface.
16 *    version 2.0-1     10-Apr-1993
17 *                      Save ACLs
18 *    version 2.1       24-Aug-1993
19 *                      By default produce 0x010C extra record ID instead of
20 *                      PKWARE's 0x000C. The format is mostly compatible with
21 *                      PKWARE.
22 *                      Incompatibility (?): zip produces multiple ACE
23 *                      fields.
24 *    version 2.1-1     Clean extra fields in vms_get_attributes().
25 *                      Fixed bug with EOF.
26 *    version 2.1-2     15-Sep-1995, Chr. Spieler
27 *                      Removed extra fields cleanup from vms_get_attributes().
28 *                      This is now done in zipup.c
29 *                      Modified (according to UnZip's vms.[ch]) the fib stuff
30 *                      for DEC C (AXP,VAX) support.
31 *    version 2.2       28-Sep-1995, Chr. Spieler
32 *                      Reorganized code for easier maintance of the two
33 *                      incompatible flavours (IM style and PK style) VMS
34 *                      attribute support.  Generic functions (common to
35 *                      both flavours) are now collected in a `wrapper'
36 *                      source file that includes one of the VMS attribute
37 *                      handlers.
38 *                      Made extra block header conforming to PKware's
39 *                      specification (extra block header has a length
40 *                      of four bytes, two bytes for a signature, and two
41 *                      bytes for the length of the block excluding this
42 *                      header.
43 *    version 2.2-1     19-Oct-1995, Chr. Spieler
44 *                      Fixed bug in CRC calculation.
45 *                      Use official PK VMS extra field id.
46 *    version 2.2-2     21-Nov-1997, Chr. Spieler
47 *                      Fixed bug in vms_get_attributes() for directory
48 *                      entries (access to uninitialized ioctx record).
49 *                      Removed unused second arg for vms_open().
50 *    version 2.2-3     04-Apr-1999, Chr. Spieler
51 *                      Changed calling interface of vms_get_attributes()
52 *                      to accept a void pointer as first argument.
53 *    version 2.2-4     26-Jan-2002, Chr. Spieler
54 *                      Modified vms_read() to handle files larger than 2GByte
55 *                      (up to size limit of "unsigned long", resp. 4GByte).
56 *    version 3.0       20-Oct-2004, Steven Schweda.
57 *                      Changed vms_read() to read all the allocated
58 *                      blocks in a file, for sure.  Changed the default
59 *                      chunk size from 16K to 32K.  Changed to use the
60 *                      new typedef for the ioctx structure.  Moved the
61 *                      VMS_PK_EXTRA test into here from VMS.C to allow
62 *                      more general automatic dependency generation.
63 *                      08-Feb-2005, SMS.
64 *                      Changed to accomodate ODS5 extended file names:
65 *                      NAM structure -> NAM[L], and so on.  (VMS.H.)
66 *                      Added some should-never-appear error messages in
67 *                      vms_open().
68 */
69
70#ifdef VMS                      /* For VMS only ! */
71
72#ifdef VMS_PK_EXTRA
73
74#include <ssdef.h>
75
76#ifndef VMS_ZIP
77#define VMS_ZIP
78#endif
79
80#include "crc32.h"
81#include "vms.h"
82#include "vmsdefs.h"
83
84#ifndef ERR
85#define ERR(x) (((x)&1)==0)
86#endif
87
88#ifndef NULL
89#define NULL (void*)(0L)
90#endif
91
92#ifndef UTIL
93
94static PK_info_t PK_def_info =
95{
96        ATR$C_RECATTR,  ATR$S_RECATTR,  {0},
97        ATR$C_UCHAR,    ATR$S_UCHAR,    {0},
98        ATR$C_CREDATE,  ATR$S_CREDATE,  {0},
99        ATR$C_REVDATE,  ATR$S_REVDATE,  {0},
100        ATR$C_EXPDATE,  ATR$S_EXPDATE,  {0},
101        ATR$C_BAKDATE,  ATR$S_BAKDATE,  {0},
102        ATR$C_ASCDATES, sizeof(ush),    0,
103        ATR$C_UIC,      ATR$S_UIC,      {0},
104        ATR$C_FPRO,     ATR$S_FPRO,     {0},
105        ATR$C_RPRO,     ATR$S_RPRO,     {0},
106        ATR$C_JOURNAL,  ATR$S_JOURNAL,  {0}
107};
108
109/* File description structure for Zip low level I/O */
110typedef struct
111{
112    struct iosb         iosb;
113    long                vbn;
114    uzoff_t             size;
115    uzoff_t             rest;
116    int                 status;
117    ush                 chan;
118    ush                 chan_pad;       /* alignment member */
119    long                acllen;
120    uch                 aclbuf[ATR$S_READACL];
121    PK_info_t           PKi;
122} ioctx_t;
123
124
125/* Forward declarations of public functions: */
126ioctx_t *vms_open(char *file);
127unsigned int  vms_read(register ioctx_t *ctx,
128                       register char *buf, register unsigned int size);
129int  vms_error(ioctx_t *ctx);
130int  vms_rewind(ioctx_t *ctx);
131int  vms_get_attributes(ioctx_t *ctx, struct zlist far *z,
132                        iztimes *z_utim);
133int  vms_close(ioctx_t *ctx);
134
135
136#define BLOCK_BYTES 512
137
138
139/*---------------*
140 |  vms_open()   |
141 *---------------*
142 |  This routine opens file for reading fetching its attributes.
143 |  Returns pointer to file description structure.
144 */
145
146ioctx_t *vms_open(file)
147char *file;
148{
149    static struct atrdef        Atr[VMS_MAX_ATRCNT+1];
150    static struct NAM_STRUCT    Nam;
151    static struct fibdef        Fib;
152    static struct dsc$descriptor FibDesc =
153        {sizeof(Fib),DSC$K_DTYPE_Z,DSC$K_CLASS_S,(char *)&Fib};
154    static struct dsc$descriptor_s DevDesc =
155        {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,&Nam.NAM_DVI[1]};
156    static char EName[NAM_MAXRSS];
157    static char RName[NAM_MAXRSS];
158
159    struct FAB Fab;
160    register ioctx_t *ctx;
161    register struct fatdef *fat;
162    int status;
163    int i;
164    ulg efblk;
165    ulg hiblk;
166
167    if ( (ctx=(ioctx_t *)malloc(sizeof(ioctx_t))) == NULL )
168        return NULL;
169    ctx -> PKi = PK_def_info;
170
171#define FILL_REQ(ix,id,b)   {       \
172    Atr[ix].atr$l_addr = GVTC &(b);      \
173    Atr[ix].atr$w_type = (id);      \
174    Atr[ix].atr$w_size = sizeof(b); \
175}
176
177    FILL_REQ(0, ATR$C_RECATTR,  ctx->PKi.ra);
178    FILL_REQ(1, ATR$C_UCHAR,    ctx->PKi.uc);
179    FILL_REQ(2, ATR$C_REVDATE,  ctx->PKi.rd);
180    FILL_REQ(3, ATR$C_EXPDATE,  ctx->PKi.ed);
181    FILL_REQ(4, ATR$C_CREDATE,  ctx->PKi.cd);
182    FILL_REQ(5, ATR$C_BAKDATE,  ctx->PKi.bd);
183    FILL_REQ(6, ATR$C_ASCDATES, ctx->PKi.rn);
184    FILL_REQ(7, ATR$C_JOURNAL,  ctx->PKi.jr);
185    FILL_REQ(8, ATR$C_RPRO,     ctx->PKi.rp);
186    FILL_REQ(9, ATR$C_FPRO,     ctx->PKi.fp);
187    FILL_REQ(10,ATR$C_UIC,      ctx->PKi.ui);
188    FILL_REQ(11,ATR$C_ACLLENGTH,ctx->acllen);
189    FILL_REQ(12,ATR$C_READACL,  ctx->aclbuf);
190
191    Atr[13].atr$w_type = 0;     /* End of ATR list */
192    Atr[13].atr$w_size = 0;
193    Atr[13].atr$l_addr = GVTC NULL;
194
195    /* Initialize RMS structures.  We need a NAM[L] to retrieve the FID. */
196    Fab = cc$rms_fab;
197    Nam = CC_RMS_NAM;
198    Fab.FAB_NAM = &Nam; /* FAB has an associated NAM[L]. */
199
200#ifdef NAML$C_MAXRSS
201
202    Fab.fab$l_dna =(char *) -1;         /* Using NAML for default name. */
203    Fab.fab$l_fna = (char *) -1;        /* Using NAML for file name. */
204
205#endif /* def NAML$C_MAXRSS */
206
207    FAB_OR_NAML( Fab, Nam).FAB_OR_NAML_FNA = file ;     /* File name. */
208    FAB_OR_NAML( Fab, Nam).FAB_OR_NAML_FNS = strlen(file);
209    Nam.NAM_ESA = EName; /* expanded filename */
210    Nam.NAM_ESS = sizeof(EName);
211    Nam.NAM_RSA = RName; /* resultant filename */
212    Nam.NAM_RSS = sizeof(RName);
213
214    /* Do $PARSE and $SEARCH here. */
215    status = sys$parse(&Fab);
216
217    if (!(status & 1))
218    {
219        fprintf( stderr,
220         " vms_open(): $parse sts = %%x%08x.\n", status);
221        return NULL;
222    }
223
224#ifdef NAML$M_OPEN_SPECIAL
225    /* 2007-02-28 SMS.
226     * If processing symlinks as symlinks ("-y"), then $SEARCH for the
227     * link, not the target file.
228     */
229    if (linkput)
230    {
231        Nam.naml$v_open_special = 1;
232    }
233#endif /* def NAML$M_OPEN_SPECIAL */
234
235    /* Search for the first file.  If none, signal error. */
236    status = sys$search(&Fab);
237
238    if (!(status & 1))
239    {
240        fprintf( stderr,
241         " vms_open(): $search sts = %%x%08x.\n", status);
242        return NULL;
243    }
244
245    /* Initialize Device name length.  Note that this points into the
246       NAM[L] to get the device name filled in by the $PARSE, $SEARCH
247       services.
248    */
249    DevDesc.dsc$w_length = Nam.NAM_DVI[0];
250
251    status = sys$assign(&DevDesc,&ctx->chan,0,0);
252
253    if (!(status & 1))
254    {
255        fprintf( stderr,
256         " vms_open(): $assign sts = %%x%08x.\n", status);
257        return NULL;
258    }
259
260    /* Move the FID (and not the DID) into the FIB.
261       2005=02-08 SMS.
262       Note that only the FID is needed, not the DID, and not the file
263       name.  Setting these other items causes failures on ODS5.
264    */
265    Fib.FIB$L_ACCTL = FIB$M_NOWRITE;
266
267    for (i = 0; i < 3; i++)
268    {
269        Fib.FIB$W_FID[ i] = Nam.NAM_FID[ i];
270        Fib.FIB$W_DID[ i] = 0;
271    }
272
273    /* Use the IO$_ACCESS function to return info about the file. */
274    status = sys$qiow( 0, ctx->chan,
275     (IO$_ACCESS| IO$M_ACCESS), &ctx->iosb, 0, 0,
276     &FibDesc, 0, 0, 0, Atr, 0);
277
278    if (ERR(status) || ERR(status = ctx->iosb.status))
279    {
280        vms_close(ctx);
281        fprintf( stderr,
282         " vms_open(): $qiow (access) sts = %%x%08x, iosb sts = %%x%08x.\n",
283         status, ctx->iosb.status);
284        return NULL;
285    }
286
287    fat = (struct fatdef *)&(ctx -> PKi.ra);
288
289#define SWAPW(x)        ( (((x)>>16)&0xFFFF) + ((x)<<16) )
290
291    efblk = SWAPW(fat->fat$l_efblk);
292    hiblk = SWAPW(fat->fat$l_hiblk);
293
294    if (efblk == 0)
295    {
296        /* Only known size is all allocated blocks.
297           (This occurs with a zero-length file, for example.)
298        */
299        ctx -> size =
300        ctx -> rest = ((uzoff_t) hiblk)* BLOCK_BYTES;
301    }
302    else
303    {
304        /* Store normal (used) size in ->size.
305           If only one -V, store normal (used) size in ->rest.
306           If multiple -V, store allocated-blocks size in ->rest.
307        */
308        ctx -> size =
309         (((uzoff_t) efblk)- 1)* BLOCK_BYTES+ fat -> fat$w_ffbyte;
310
311        if (vms_native < 2)
312            ctx -> rest = ctx -> size;
313        else
314            ctx -> rest = ((uzoff_t) hiblk)* BLOCK_BYTES;
315    }
316
317    ctx -> status = SS$_NORMAL;
318    ctx -> vbn = 1;
319    return ctx;
320}
321
322
323#define KByte (2* BLOCK_BYTES)
324#define MAX_READ_BYTES (32* KByte)
325
326/*----------------*
327 |   vms_read()   |
328 *----------------*
329 |   Reads file in (multi-)block-sized chunks into the buffer.
330 |   Stops on EOF. Returns number of bytes actually read.
331 |   Note: This function makes no sense (and will error) if the buffer
332 |   size ("size") is not a multiple of the disk block size (512).
333 */
334
335size_t vms_read( ctx, buf, size)
336ioctx_t *ctx;
337char *buf;
338size_t size;
339{
340    int act_cnt;
341    uzoff_t rest_rndup;
342    int status;
343    size_t bytes_read = 0;
344
345    /* If previous read hit EOF, fail early. */
346    if (ctx -> status == SS$_ENDOFFILE)
347        return 0;               /* EOF. */
348
349    /* If no more expected to be read, fail early. */
350    if (ctx -> rest == 0)
351        return 0;               /* Effective EOF. */
352
353    /* If request is smaller than a whole block, fail.
354       This really should never happen.  (assert()?)
355    */
356    if (size < BLOCK_BYTES)
357        return 0;
358
359    /* Note that on old VMS VAX versions (like V5.5-2), QIO[W] may fail
360       with status %x0000034c (= %SYSTEM-F-IVBUFLEN, invalid buffer
361       length) when size is not a multiple of 512.  Thus the requested
362       size is boosted as needed, but the IOSB byte count returned is
363       reduced when it exceeds the actual bytes remaining (->rest).
364    */
365
366    /* Adjust request size as appropriate. */
367    if (size > MAX_READ_BYTES)
368    {
369        /* Restrict request to MAX_READ_BYTES. */
370        size = MAX_READ_BYTES;
371    }
372    else
373    {
374        /* Round odd-ball request up to the next whole block.
375           This really should never happen.  (assert()?)
376        */
377        size = (size+ BLOCK_BYTES- 1)& ~(BLOCK_BYTES- 1);
378    }
379    rest_rndup = (ctx -> rest+ BLOCK_BYTES- 1)& ~(BLOCK_BYTES- 1);
380
381    /* Read (QIOW) until error or "size" bytes have been read. */
382    do
383    {
384        /* Reduce "size" when next (last) read would overrun the EOF,
385           but never below one block (so we'll always get a nice EOF).
386        */
387        if (size > rest_rndup)
388            size = rest_rndup;
389
390        status = sys$qiow( 0, ctx->chan, IO$_READVBLK,
391            &ctx->iosb, 0, 0,
392            buf, size, ctx->vbn, 0, 0, 0);
393
394        /* If initial status was good, use final status. */
395        if ( !ERR(status) )
396                status = ctx->iosb.status;
397
398        if ( !ERR(status) || status == SS$_ENDOFFILE )
399        {
400            act_cnt = ctx->iosb.count;
401            /* Ignore whole-block boost when remainder is smaller. */
402            if (act_cnt > ctx->rest)
403            {
404                act_cnt = ctx->rest;
405                status = SS$_ENDOFFILE;
406            }
407            /* Adjust counters/pointers according to delivered bytes. */
408            size -= act_cnt;
409            buf += act_cnt;
410            bytes_read += act_cnt;
411            ctx->vbn += ctx->iosb.count/ BLOCK_BYTES;
412        }
413
414    } while ( !ERR(status) && (size > 0) );
415
416    if (!ERR(status))
417    {
418        /* Record any successful status as SS$_NORMAL. */
419        ctx -> status = SS$_NORMAL;
420    }
421    else if (status == SS$_ENDOFFILE)
422    {
423        /* Record EOF as SS$_ENDOFFILE.  (Ignore error status codes?) */
424        ctx -> status = SS$_ENDOFFILE;
425    }
426
427    /* Decrement bytes-to-read.  Return the total bytes read. */
428    ctx -> rest -= bytes_read;
429
430    return bytes_read;
431}
432
433/*-----------------*
434 |   vms_error()   |
435 *-----------------*
436 |   Returns whether last operation on the file caused an error
437 */
438
439int vms_error(ctx)
440ioctx_t *ctx;
441{   /* EOF is not actual error */
442    return ERR(ctx->status) && (ctx->status != SS$_ENDOFFILE);
443}
444
445/*------------------*
446 |   vms_rewind()   |
447 *------------------*
448 |   Rewinds file to the beginning for the next vms_read().
449 */
450
451int vms_rewind(ctx)
452ioctx_t *ctx;
453{
454    ctx -> vbn = 1;
455    ctx -> rest = ctx -> size;
456    return 0;
457}
458
459/*--------------------------*
460 |   vms_get_attributes()   |
461 *--------------------------*
462 |   Malloc a PKWARE extra field and fill with file attributes. Returns
463 |   error number of the ZE_??? class.
464 |   If the passed ioctx record "FILE *" pointer is NULL, vms_open() is
465 |   called to fetch the file attributes.
466 |   When `vms_native' is not set, a generic "UT" type timestamp extra
467 |   field is generated instead.
468 |
469 |   2004-11-11 SMS.
470 |   Changed to use separate storage for ->extra and ->cextra.  Zip64
471 |   processing may move (reallocate) one and not the other.
472 */
473
474int vms_get_attributes(ctx, z, z_utim)
475ioctx_t *ctx;           /* Internal file control structure. */
476struct zlist far *z;    /* Zip entry to compress. */
477iztimes *z_utim;
478{
479    byte    *p;
480    byte    *xtra;
481    byte    *cxtra;
482    struct  PK_header    *h;
483    extent  l;
484    int     notopened;
485
486    if ( !vms_native )
487    {
488#ifdef USE_EF_UT_TIME
489        /*
490         *  A `portable' zipfile entry is created. Create an "UT" extra block
491         *  containing UNIX style modification time stamp in UTC, which helps
492         *  maintaining the `real' "last modified" time when the archive is
493         *  transfered across time zone boundaries.
494         */
495#  ifdef IZ_CHECK_TZ
496        if (!zp_tz_is_valid)
497            return ZE_OK;       /* skip silently if no valid TZ info */
498#  endif
499
500        if ((xtra = (uch *) malloc( EB_HEADSIZE+ EB_UT_LEN( 1))) == NULL)
501            return ZE_MEM;
502
503        if ((cxtra = (uch *) malloc( EB_HEADSIZE+ EB_UT_LEN( 1))) == NULL)
504            return ZE_MEM;
505
506        /* Fill xtra[] with data. */
507        xtra[ 0] = 'U';
508        xtra[ 1] = 'T';
509        xtra[ 2] = EB_UT_LEN(1);        /* length of data part of e.f. */
510        xtra[ 3] = 0;
511        xtra[ 4] = EB_UT_FL_MTIME;
512        xtra[ 5] = (byte) (z_utim->mtime);
513        xtra[ 6] = (byte) (z_utim->mtime >> 8);
514        xtra[ 7] = (byte) (z_utim->mtime >> 16);
515        xtra[ 8] = (byte) (z_utim->mtime >> 24);
516
517        /* Copy xtra[] data into cxtra[]. */
518        memcpy( cxtra, xtra, (EB_HEADSIZE+ EB_UT_LEN( 1)));
519
520        /* Set sizes and pointers. */
521        z->cext = z->ext = (EB_HEADSIZE+ EB_UT_LEN( 1));
522        z->extra = (char*) xtra;
523        z->cextra = (char*) cxtra;
524
525#endif /* USE_EF_UT_TIME */
526
527        return ZE_OK;
528    }
529
530    notopened = (ctx == NULL);
531    if ( notopened && ((ctx = vms_open(z->name)) == NULL) )
532        return ZE_OPEN;
533
534    l = PK_HEADER_SIZE + sizeof(ctx->PKi);
535    if (ctx->acllen > 0)
536        l += PK_FLDHDR_SIZE + ctx->acllen;
537
538    if ((xtra = (uch *) malloc( l)) == NULL)
539        return ZE_MEM;
540
541    if ((cxtra = (uch *) malloc( l)) == NULL)
542        return ZE_MEM;
543
544    /* Fill xtra[] with data. */
545
546    h = (struct PK_header *) xtra;
547    h->tag = PK_SIGNATURE;
548    h->size = l - EB_HEADSIZE;
549    p = (h->data);
550
551    /* Copy default set of attributes */
552    memcpy(h->data, (char*)&(ctx->PKi), sizeof(ctx->PKi));
553    p += sizeof(ctx->PKi);
554
555    if ( ctx->acllen > 0 )
556    {
557        struct PK_field *f;
558
559        if (dosify)
560            zipwarn("file has ACL, may be incompatible with PKUNZIP","");
561
562        f = (struct PK_field *)p;
563        f->tag = ATR$C_ADDACLENT;
564        f->size = ctx->acllen;
565        memcpy((char *)&(f->value[0]), ctx->aclbuf, ctx->acllen);
566        p += PK_FLDHDR_SIZE + ctx->acllen;
567    }
568
569
570    h->crc32 = CRCVAL_INITIAL;                  /* Init CRC register */
571    h->crc32 = crc32(h->crc32, (uch *)(h->data), l - PK_HEADER_SIZE);
572
573    /* Copy xtra[] data into cxtra[]. */
574    memcpy( cxtra, xtra, l);
575
576    /* Set sizes and pointers. */
577    z->ext = z->cext = l;
578    z->extra = (char *) xtra;
579    z->cextra = (char *) cxtra;
580
581    if (notopened)              /* close "ctx", if we have opened it here */
582        vms_close(ctx);
583
584    return ZE_OK;
585}
586
587
588int vms_close(ctx)
589ioctx_t *ctx;
590{
591        sys$dassgn(ctx->chan);
592        free(ctx);
593        return 0;
594}
595
596#endif /* !_UTIL */
597
598#endif /* def VMS_PK_EXTRA */
599
600#endif /* VMS */
601