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