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 
94 static 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 */
110 typedef 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: */
126 ioctx_t *vms_open(char *file);
127 unsigned int  vms_read(register ioctx_t *ctx,
128                        register char *buf, register unsigned int size);
129 int  vms_error(ioctx_t *ctx);
130 int  vms_rewind(ioctx_t *ctx);
131 int  vms_get_attributes(ioctx_t *ctx, struct zlist far *z,
132                         iztimes *z_utim);
133 int  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 
vms_open(file)146 ioctx_t *vms_open(file)
147 char *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 
vms_read(ctx,buf,size)335 size_t vms_read( ctx, buf, size)
336 ioctx_t *ctx;
337 char *buf;
338 size_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 
vms_error(ctx)439 int vms_error(ctx)
440 ioctx_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 
vms_rewind(ctx)451 int vms_rewind(ctx)
452 ioctx_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 
vms_get_attributes(ctx,z,z_utim)474 int vms_get_attributes(ctx, z, z_utim)
475 ioctx_t *ctx;           /* Internal file control structure. */
476 struct zlist far *z;    /* Zip entry to compress. */
477 iztimes *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 
vms_close(ctx)588 int vms_close(ctx)
589 ioctx_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