1 /*
2 * File rock.c - generate RRIP records for iso9660 filesystems.
3
4 Written by Eric Youngdale (1993).
5
6 Copyright 1993 Yggdrasil Computing, Incorporated
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
11 any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
21
22 #include <stdlib.h>
23
24 #include "config.h"
25
26 #ifndef VMS
27 #if defined(MAJOR_IN_SYSMACROS)
28 #include <sys/sysmacros.h>
29 #endif
30
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34
35 #endif
36 #if defined(MAJOR_IN_MKDEV)
37 #include <sys/types.h>
38 #include <sys/mkdev.h>
39 #endif
40
41 #include "mkisofs.h"
42 #include "iso9660.h"
43 #include <string.h>
44
45 #ifdef DOESNT_WORK
46
47 #ifdef NON_UNIXFS
48 #define S_ISLNK(m) (0)
49 #else
50 #ifndef S_ISLNK
51 #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
52 #endif
53 #endif
54
55 #else
56 #include <statdefs.h>
57 #endif
58
59 #define SU_VERSION 1
60
61 #define SL_ROOT 8
62 #define SL_PARENT 4
63 #define SL_CURRENT 2
64 #define SL_CONTINUE 1
65
66 #define CE_SIZE 28
67 #define CL_SIZE 12
68 #define ER_SIZE 8
69 #define NM_SIZE 5
70 #define PL_SIZE 12
71 #define PN_SIZE 20
72 #define PX_SIZE 36
73 #define RE_SIZE 4
74 #define SL_SIZE 20
75 #define ZZ_SIZE 15
76 #ifdef APPLE_HYB
77 #define AA_SIZE 14 /* size of Apple extension */
78 #endif /* APPLE_HYB */
79 #ifdef __QNX__
80 #define TF_SIZE (5 + 4 * 7)
81 #else
82 #define TF_SIZE (5 + 3 * 7)
83 #endif
84
85 /* If we need to store this number of bytes, make sure we
86 do not box ourselves in so that we do not have room for
87 a CE entry for the continuation record */
88
89 #define MAYBE_ADD_CE_ENTRY(BYTES) \
90 (BYTES + CE_SIZE + currlen + (ipnt - recstart) > reclimit ? 1 : 0)
91
92 /*
93 * Buffer to build RR attributes
94 */
95
96 static unsigned char Rock[16384];
97 static unsigned char symlink_buff[256];
98 static int ipnt = 0;
99 static int recstart = 0;
100 static int currlen = 0;
101 static int mainrec = 0;
102 static int reclimit;
103
104 #ifdef APPLE_HYB
105 /* if we are using the HFS name, we don't want the '/' character */
106 static void
rstrncpy(char * t,char * f,int c)107 rstrncpy(char *t, char *f, int c)
108 {
109 while (c-- && *f) {
110 switch (*f) {
111 case '/':
112 *t = '_';
113 break;
114 default:
115 *t = *f;
116 break;
117 }
118 t++; f++;
119 }
120 }
121 #endif /* APPLE HYB */
122
123 static void add_CE_entry __PR((void));
124
add_CE_entry()125 static void add_CE_entry(){
126 if(recstart)
127 set_733((char*)Rock + recstart - 8, ipnt + 28 - recstart);
128 Rock[ipnt++] ='C';
129 Rock[ipnt++] ='E';
130 Rock[ipnt++] = CE_SIZE;
131 Rock[ipnt++] = SU_VERSION;
132 set_733((char*)Rock + ipnt, 0);
133 ipnt += 8;
134 set_733((char*)Rock + ipnt, 0);
135 ipnt += 8;
136 set_733((char*)Rock + ipnt, 0);
137 ipnt += 8;
138 recstart = ipnt;
139 currlen = 0;
140 if(!mainrec) mainrec = ipnt;
141 reclimit = SECTOR_SIZE - 8; /* Limit to one sector */
142 }
143
144 #ifdef __STDC__
generate_rock_ridge_attributes(char * whole_name,char * name,struct directory_entry * s_entry,struct stat * statbuf,struct stat * lstatbuf,int deep_opt)145 int generate_rock_ridge_attributes (char * whole_name, char * name,
146 struct directory_entry * s_entry,
147 struct stat * statbuf,
148 struct stat * lstatbuf,
149 int deep_opt)
150 #else
151 int generate_rock_ridge_attributes (whole_name, name,
152 s_entry,
153 statbuf,
154 lstatbuf,
155 deep_opt)
156 char * whole_name; char * name; struct directory_entry * s_entry;
157 struct stat * statbuf, *lstatbuf;
158 int deep_opt;
159 #endif
160 {
161 int flagpos, flagval;
162 int need_ce;
163
164 statbuf = statbuf; /* this shuts up unreferenced compiler warnings */
165 mainrec = recstart = ipnt = 0;
166 reclimit = 0xf8;
167
168 /* no need to fill in the RR stuff if we won't see the file */
169 if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY)
170 return 0;
171
172 /* Obtain the amount of space that is currently used for the directory
173 record. Assume max for name, since name conflicts may cause us
174 to rename the file later on */
175 currlen = sizeof(s_entry->isorec);
176
177 #ifdef APPLE_HYB
178 /* if we have regular file, then add Apple extensions */
179 if (S_ISREG(lstatbuf->st_mode) && apple_ext && s_entry->hfs_ent) {
180 Rock[ipnt++] ='A'; /* AppleSignature */
181 Rock[ipnt++] ='A';
182 Rock[ipnt++] = AA_SIZE; /* includes AppleSignature bytes */
183 Rock[ipnt++] = 0x02; /* SystemUseID */
184 Rock[ipnt++] = s_entry->hfs_ent->type[0];
185 Rock[ipnt++] = s_entry->hfs_ent->type[1];
186 Rock[ipnt++] = s_entry->hfs_ent->type[2];
187 Rock[ipnt++] = s_entry->hfs_ent->type[3];
188 Rock[ipnt++] = s_entry->hfs_ent->creator[0];
189 Rock[ipnt++] = s_entry->hfs_ent->creator[1];
190 Rock[ipnt++] = s_entry->hfs_ent->creator[2];
191 Rock[ipnt++] = s_entry->hfs_ent->creator[3];
192 Rock[ipnt++] = (s_entry->hfs_ent->fdflags >> 8) & 0xff;
193 Rock[ipnt++] = s_entry->hfs_ent->fdflags & 0xff;
194 }
195 #endif /* APPLE_HYB */
196
197 /* Identify that we are using the SUSP protocol */
198 if(deep_opt & NEED_SP){
199 Rock[ipnt++] ='S';
200 Rock[ipnt++] ='P';
201 Rock[ipnt++] = 7;
202 Rock[ipnt++] = SU_VERSION;
203 Rock[ipnt++] = 0xbe;
204 Rock[ipnt++] = 0xef;
205 Rock[ipnt++] = 0;
206 };
207
208 /* First build the posix name field */
209 Rock[ipnt++] ='R';
210 Rock[ipnt++] ='R';
211 Rock[ipnt++] = 5;
212 Rock[ipnt++] = SU_VERSION;
213 flagpos = ipnt;
214 flagval = 0;
215 Rock[ipnt++] = 0; /* We go back and fix this later */
216
217 if(strcmp(name,".") && strcmp(name,"..")){
218 char * npnt;
219 int remain, use;
220
221 #ifdef APPLE_HYB
222 /* use the HFS name if it exists */
223 if (USE_MAC_NAME(mac_name, s_entry)) {
224 remain = strlen(s_entry->hfs_ent->name);
225 npnt = s_entry->hfs_ent->name;
226 }
227 else {
228 #endif
229
230 remain = strlen(name);
231 npnt = name;
232 #ifdef APPLE_HYB
233 }
234 #endif /* APPLE_HYB */
235
236 while(remain){
237 use = remain;
238 need_ce = 0;
239 /* Can we fit this SUSP and a CE entry? */
240 if(use + currlen + CE_SIZE + (ipnt - recstart) > reclimit) {
241 use = reclimit - currlen - CE_SIZE - (ipnt - recstart);
242 need_ce++;
243 }
244
245 /* Only room for 256 per SUSP field */
246 if(use > 0xf8) use = 0xf8;
247
248 /* First build the posix name field */
249 Rock[ipnt++] ='N';
250 Rock[ipnt++] ='M';
251 Rock[ipnt++] = NM_SIZE + use;
252 Rock[ipnt++] = SU_VERSION;
253 Rock[ipnt++] = (remain != use ? 1 : 0);
254 flagval |= (1<<3);
255 #ifdef APPLE_HYB
256 /* filter out any '/' character in HFS filename */
257 if (USE_MAC_NAME(mac_name, s_entry))
258 rstrncpy((char *)&Rock[ipnt], npnt, use);
259 else
260 #endif /* APPLE_HYB */
261 strncpy((char *)&Rock[ipnt], npnt, use);
262 npnt += use;
263 ipnt += use;
264 remain -= use;
265 if(remain && need_ce) add_CE_entry();
266 };
267 };
268
269 /*
270 * Add the posix modes
271 */
272 if(MAYBE_ADD_CE_ENTRY(PX_SIZE)) add_CE_entry();
273 Rock[ipnt++] ='P';
274 Rock[ipnt++] ='X';
275 Rock[ipnt++] = PX_SIZE;
276 Rock[ipnt++] = SU_VERSION;
277 flagval |= (1<<0);
278 set_733((char*)Rock + ipnt, lstatbuf->st_mode);
279 ipnt += 8;
280 set_733((char*)Rock + ipnt, lstatbuf->st_nlink);
281 ipnt += 8;
282 set_733((char*)Rock + ipnt, lstatbuf->st_uid);
283 ipnt += 8;
284 set_733((char*)Rock + ipnt, lstatbuf->st_gid);
285 ipnt += 8;
286
287 /*
288 * Check for special devices
289 */
290 #ifndef NON_UNIXFS
291 if (S_ISCHR(lstatbuf->st_mode) || S_ISBLK(lstatbuf->st_mode)) {
292 if(MAYBE_ADD_CE_ENTRY(PN_SIZE)) add_CE_entry();
293 Rock[ipnt++] ='P';
294 Rock[ipnt++] ='N';
295 Rock[ipnt++] = PN_SIZE;
296 Rock[ipnt++] = SU_VERSION;
297 flagval |= (1<<1);
298 #if defined(MAJOR_IN_SYSMACROS) || defined(MAJOR_IN_MKDEV)
299 set_733((char*)Rock + ipnt, major(lstatbuf->st_rdev ));
300 ipnt += 8;
301 set_733((char*)Rock + ipnt, minor(lstatbuf->st_rdev));
302 ipnt += 8;
303 #else
304 /*
305 * If we don't have sysmacros.h, then we have to guess as to how
306 * best to pick apart the device number for major/minor.
307 * Note: this may very well be wrong for many systems, so
308 * it is always best to use the major/minor macros if the
309 * system supports it.
310 */
311 if(sizeof(dev_t) <= 2) {
312 set_733((char*)Rock + ipnt, (lstatbuf->st_rdev >> 8));
313 ipnt += 8;
314 set_733((char*)Rock + ipnt, lstatbuf->st_rdev & 0xff);
315 ipnt += 8;
316 }
317 else if(sizeof(dev_t) <= 4) {
318 set_733((char*)Rock + ipnt, (lstatbuf->st_rdev >> 8) >> 8);
319 ipnt += 8;
320 set_733((char*)Rock + ipnt, lstatbuf->st_rdev & 0xffff);
321 ipnt += 8;
322 }
323 else {
324 set_733((char*)Rock + ipnt, (lstatbuf->st_rdev >> 16) >> 16);
325 ipnt += 8;
326 set_733((char*)Rock + ipnt, lstatbuf->st_rdev);
327 ipnt += 8;
328 }
329 #endif
330 };
331 #endif
332 /*
333 * Check for and symbolic links. VMS does not have these.
334 */
335 if (S_ISLNK(lstatbuf->st_mode)){
336 int lenpos, lenval, j0, j1;
337 int nchar;
338 unsigned char * cpnt, *cpnt1;
339 nchar = readlink(whole_name, (char *)symlink_buff, sizeof(symlink_buff)-1);
340 symlink_buff[nchar < 0 ? 0 : nchar] = 0;
341 nchar = strlen((char *) symlink_buff);
342 set_733(s_entry->isorec.size, 0);
343 cpnt = &symlink_buff[0];
344 flagval |= (1<<2);
345
346 if (! split_SL_field)
347 {
348 int sl_bytes = 0;
349 for (cpnt1 = cpnt; *cpnt1 != '\0'; cpnt1++)
350 {
351 if (*cpnt1 == '/')
352 {
353 sl_bytes += 4;
354 }
355 else
356 {
357 sl_bytes += 1;
358 }
359 }
360 if (sl_bytes > 250)
361 {
362 /*
363 * the symbolic link won't fit into one SL System Use Field
364 * print an error message and continue with splited one
365 */
366 fprintf(stderr,"symbolic link ``%s'' to long for one SL System Use Field, splitting", cpnt);
367 }
368 if(MAYBE_ADD_CE_ENTRY(SL_SIZE + sl_bytes)) add_CE_entry();
369 }
370
371 while(nchar){
372 if(MAYBE_ADD_CE_ENTRY(SL_SIZE)) add_CE_entry();
373 Rock[ipnt++] ='S';
374 Rock[ipnt++] ='L';
375 lenpos = ipnt;
376 Rock[ipnt++] = SL_SIZE;
377 Rock[ipnt++] = SU_VERSION;
378 Rock[ipnt++] = 0; /* Flags */
379 lenval = 5;
380 while(*cpnt){
381 cpnt1 = (unsigned char *) strchr((char *) cpnt, '/');
382 if(cpnt1) {
383 nchar--;
384 *cpnt1 = 0;
385 };
386
387 /* We treat certain components in a special way. */
388 if(cpnt[0] == '.' && cpnt[1] == '.' && cpnt[2] == 0){
389 if(MAYBE_ADD_CE_ENTRY(2)) add_CE_entry();
390 Rock[ipnt++] = SL_PARENT;
391 Rock[ipnt++] = 0; /* length is zero */
392 lenval += 2;
393 nchar -= 2;
394 } else if(cpnt[0] == '.' && cpnt[1] == 0){
395 if(MAYBE_ADD_CE_ENTRY(2)) add_CE_entry();
396 Rock[ipnt++] = SL_CURRENT;
397 Rock[ipnt++] = 0; /* length is zero */
398 lenval += 2;
399 nchar -= 1;
400 } else if(cpnt[0] == 0){
401 if(MAYBE_ADD_CE_ENTRY(2)) add_CE_entry();
402 Rock[ipnt++] = SL_ROOT;
403 Rock[ipnt++] = 0; /* length is zero */
404 lenval += 2;
405 } else {
406 /* If we do not have enough room for a component, start
407 a new continuations segment now */
408 if(split_SL_component ? MAYBE_ADD_CE_ENTRY(6) :
409 MAYBE_ADD_CE_ENTRY(6 + strlen ((char *) cpnt)))
410 {
411 add_CE_entry();
412 if(cpnt1)
413 {
414 *cpnt1 = '/';
415 nchar++;
416 cpnt1 = NULL; /* A kluge so that we can restart properly */
417 }
418 break;
419 }
420 j0 = strlen((char *) cpnt);
421 while(j0) {
422 j1 = j0;
423 if(j1 > 0xf8) j1 = 0xf8;
424 need_ce = 0;
425 if(j1 + currlen + CE_SIZE + (ipnt - recstart) > reclimit) {
426 j1 = reclimit - currlen - CE_SIZE - (ipnt - recstart);
427 need_ce++;
428 }
429 Rock[ipnt++] = (j1 != j0 ? SL_CONTINUE : 0);
430 Rock[ipnt++] = j1;
431 strncpy((char *) Rock + ipnt, (char *) cpnt, j1);
432 ipnt += j1;
433 lenval += j1 + 2;
434 cpnt += j1;
435 nchar -= j1; /* Number we processed this time */
436 j0 -= j1;
437 if(need_ce) {
438 add_CE_entry();
439 if(cpnt1) {
440 *cpnt1 = '/';
441 nchar++;
442 cpnt1 = NULL; /* A kluge so that we can restart properly */
443 }
444 break;
445 }
446 }
447 };
448 if(cpnt1) {
449 cpnt = cpnt1 + 1;
450 } else
451 break;
452 }
453 Rock[lenpos] = lenval;
454 if(nchar) Rock[lenpos + 2] = SL_CONTINUE; /* We need another SL entry */
455 } /* while nchar */
456 } /* Is a symbolic link */
457 /*
458 * Add in the Rock Ridge TF time field
459 */
460 if(MAYBE_ADD_CE_ENTRY(TF_SIZE)) add_CE_entry();
461 Rock[ipnt++] ='T';
462 Rock[ipnt++] ='F';
463 Rock[ipnt++] = TF_SIZE;
464 Rock[ipnt++] = SU_VERSION;
465 #ifdef __QNX__
466 Rock[ipnt++] = 0x0f;
467 #else
468 Rock[ipnt++] = 0x0e;
469 #endif
470 flagval |= (1<<7);
471 #ifdef __QNX__
472 iso9660_date((char *) &Rock[ipnt], lstatbuf->st_ftime);
473 ipnt += 7;
474 #endif
475 iso9660_date((char *) &Rock[ipnt], lstatbuf->st_mtime);
476 ipnt += 7;
477 iso9660_date((char *) &Rock[ipnt], lstatbuf->st_atime);
478 ipnt += 7;
479 iso9660_date((char *) &Rock[ipnt], lstatbuf->st_ctime);
480 ipnt += 7;
481
482 /*
483 * Add in the Rock Ridge RE time field
484 */
485 if(deep_opt & NEED_RE){
486 if(MAYBE_ADD_CE_ENTRY(RE_SIZE)) add_CE_entry();
487 Rock[ipnt++] ='R';
488 Rock[ipnt++] ='E';
489 Rock[ipnt++] = RE_SIZE;
490 Rock[ipnt++] = SU_VERSION;
491 flagval |= (1<<6);
492 };
493 /*
494 * Add in the Rock Ridge PL record, if required.
495 */
496 if(deep_opt & NEED_PL){
497 if(MAYBE_ADD_CE_ENTRY(PL_SIZE)) add_CE_entry();
498 Rock[ipnt++] ='P';
499 Rock[ipnt++] ='L';
500 Rock[ipnt++] = PL_SIZE;
501 Rock[ipnt++] = SU_VERSION;
502 set_733((char*)Rock + ipnt, 0);
503 ipnt += 8;
504 flagval |= (1<<5);
505 };
506
507 /*
508 * Add in the Rock Ridge CL field, if required.
509 */
510 if(deep_opt & NEED_CL){
511 if(MAYBE_ADD_CE_ENTRY(CL_SIZE)) add_CE_entry();
512 Rock[ipnt++] ='C';
513 Rock[ipnt++] ='L';
514 Rock[ipnt++] = CL_SIZE;
515 Rock[ipnt++] = SU_VERSION;
516 set_733((char*)Rock + ipnt, 0);
517 ipnt += 8;
518 flagval |= (1<<4);
519 };
520
521 #ifndef VMS
522 /* If transparent compression was requested, fill in the correct
523 field for this file */
524 if(transparent_compression &&
525 S_ISREG(lstatbuf->st_mode) &&
526 strlen(name) > 3 &&
527 strcmp(name + strlen(name) - 3,".gZ") == 0){
528 FILE * zipfile;
529 char * checkname;
530 unsigned int file_size;
531 unsigned char header[8];
532 int OK_flag;
533
534 /* First open file and verify that the correct algorithm was used */
535 file_size = 0;
536 OK_flag = 1;
537
538 zipfile = fopen(whole_name, "rb");
539 fread(header, 1, sizeof(header), zipfile);
540
541 /* Check some magic numbers from gzip. */
542 if(header[0] != 0x1f || header[1] != 0x8b || header[2] != 8) OK_flag = 0;
543 /* Make sure file was blocksized. */
544 if(((header[3] & 0x40) == 0)) OK_flag = 0;
545 /* OK, now go to the end of the file and get some more info */
546 if(OK_flag){
547 int status;
548 status = (long)lseek(fileno(zipfile), (off_t)(-8), SEEK_END);
549 if(status == -1) OK_flag = 0;
550 }
551 if(OK_flag){
552 if(read(fileno(zipfile), (char*)header, sizeof(header)) != sizeof(header))
553 OK_flag = 0;
554 else {
555 int blocksize;
556 blocksize = (header[3] << 8) | header[2];
557 file_size = ((unsigned int)header[7] << 24) |
558 ((unsigned int)header[6] << 16) |
559 ((unsigned int)header[5] << 8) | header[4];
560 #if 0
561 fprintf(stderr,"Blocksize = %d %d\n", blocksize, file_size);
562 #endif
563 if(blocksize != SECTOR_SIZE) OK_flag = 0;
564 }
565 }
566 fclose(zipfile);
567
568 checkname = strdup(whole_name);
569 checkname[strlen(whole_name)-3] = 0;
570 zipfile = fopen(checkname, "rb");
571 if(zipfile) {
572 OK_flag = 0;
573 fprintf(stderr,"Unable to insert transparent compressed file - name conflict\n");
574 fclose(zipfile);
575 }
576
577 free(checkname);
578
579 if(OK_flag){
580 if(MAYBE_ADD_CE_ENTRY(ZZ_SIZE)) add_CE_entry();
581 Rock[ipnt++] ='Z';
582 Rock[ipnt++] ='Z';
583 Rock[ipnt++] = ZZ_SIZE;
584 Rock[ipnt++] = SU_VERSION;
585 Rock[ipnt++] = 'g'; /* Identify compression technique used */
586 Rock[ipnt++] = 'z';
587 Rock[ipnt++] = 3;
588 set_733((char*)Rock + ipnt, file_size); /* Real file size */
589 ipnt += 8;
590 };
591 }
592 #endif
593 /*
594 * Add in the Rock Ridge CE field, if required. We use this for the
595 * extension record that is stored in the root directory.
596 */
597 if(deep_opt & NEED_CE) add_CE_entry();
598 /*
599 * Done filling in all of the fields. Now copy it back to a buffer for the
600 * file in question.
601 */
602
603 /* Now copy this back to the buffer for the file */
604 Rock[flagpos] = flagval;
605
606 /* If there was a CE, fill in the size field */
607 if(recstart)
608 set_733((char*)Rock + recstart - 8, ipnt - recstart);
609
610 s_entry->rr_attributes = (unsigned char *) e_malloc(ipnt);
611 s_entry->total_rr_attr_size = ipnt;
612 s_entry->rr_attr_size = (mainrec ? mainrec : ipnt);
613 memcpy(s_entry->rr_attributes, Rock, ipnt);
614 return ipnt;
615 }
616
617 /* Guaranteed to return a single sector with the relevant info */
618
FDECL4(generate_rr_extension_record,char *,id,char *,descriptor,char *,source,int *,size)619 char * FDECL4(generate_rr_extension_record, char *, id, char *, descriptor,
620 char *, source, int *, size){
621 int lipnt = 0;
622 char * pnt;
623 int len_id, len_des, len_src;
624
625 len_id = strlen(id);
626 len_des = strlen(descriptor);
627 len_src = strlen(source);
628 Rock[lipnt++] ='E';
629 Rock[lipnt++] ='R';
630 Rock[lipnt++] = ER_SIZE + len_id + len_des + len_src;
631 Rock[lipnt++] = 1;
632 Rock[lipnt++] = len_id;
633 Rock[lipnt++] = len_des;
634 Rock[lipnt++] = len_src;
635 Rock[lipnt++] = 1;
636
637 memcpy(Rock + lipnt, id, len_id);
638 lipnt += len_id;
639
640 memcpy(Rock + lipnt, descriptor, len_des);
641 lipnt += len_des;
642
643 memcpy(Rock + lipnt, source, len_src);
644 lipnt += len_src;
645
646 if(lipnt > SECTOR_SIZE) {
647 fprintf(stderr,"Extension record too long\n");
648 exit(1);
649 };
650 pnt = (char *) e_malloc(SECTOR_SIZE);
651 memset(pnt, 0, SECTOR_SIZE);
652 memcpy(pnt, Rock, lipnt);
653 *size = lipnt;
654 return pnt;
655 }
656