1 /*===========================================================================
2 *
3 * PUBLIC DOMAIN NOTICE
4 * National Center for Biotechnology Information
5 *
6 * This software/database is a "United States Government Work" under the
7 * terms of the United States Copyright Act. It was written as part of
8 * the author's official duties as a United States Government employee and
9 * thus cannot be copyrighted. This software/database is freely available
10 * to the public for use. The National Library of Medicine and the U.S.
11 * Government have not placed any restriction on its use or reproduction.
12 *
13 * Although all reasonable efforts have been taken to ensure the accuracy
14 * and reliability of the software and data, the NLM and the U.S.
15 * Government do not and cannot warrant the performance or results that
16 * may be obtained by using this software or data. The NLM and the U.S.
17 * Government disclaim all warranties, express or implied, including
18 * warranties of performance, merchantability or fitness for any particular
19 * purpose.
20 *
21 * Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26
27 #include <klib/rc.h>
28 #include <klib/namelist.h>
29 #include <klib/vector.h>
30 #include <klib/container.h>
31 #include <klib/sort.h>
32 #include <klib/log.h>
33 #include <klib/out.h>
34 #include <klib/status.h>
35 #include <klib/text.h>
36 #include <klib/printf.h>
37 #include <klib/time.h>
38 #include <sysalloc.h>
39 #include <kfs/directory.h>
40 #include <kfs/file.h>
41 #include <kfs/toc.h>
42 #include <kfs/sra.h>
43 #include <kfs/md5.h>
44
45 #include <kapp/main.h>
46
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <ctype.h>
50 #include <string.h>
51 #include <assert.h>
52 #include <time.h>
53 #include <endian.h>
54 #include <byteswap.h>
55
56 #include "kar+.h"
57 #include "kar+args.h"
58
59
60 /*******************************************************************************
61 * Globals + Forwards + Declarations + Definitions
62 */
63
64 static uint64_t max_size = 0;
65 static uint64_t max_offset = 0;
66
67
68 static rc_t kar_scan_directory ( const KDirectory *dir, KARDir *kar_dir, const char *path );
69
70
71 /*******************************************************************************
72 * Create
73 */
74
75
76 /********** KAREntry */
77
78 static
kar_entry_whack(BSTNode * node,void * data)79 void kar_entry_whack ( BSTNode *node, void *data )
80 {
81 KAREntry *entry = ( KAREntry * ) node;
82
83 if ( entry -> type == kptDir ) {
84 BSTreeWhack (
85 & ( ( KARDir * ) entry ) -> contents,
86 kar_entry_whack,
87 NULL
88 );
89 }
90
91 /* do the cleanup */
92 switch ( entry -> type )
93 {
94 case kptAlias:
95 case kptFile | kptAlias:
96 case kptDir | kptAlias:
97 free ( ( void * ) ( ( KARAlias * ) entry ) -> link );
98 break;
99 }
100
101 free ( entry );
102 }
103
104 static
kar_entry_create(KAREntry ** rtn,size_t entry_size,const KDirectory * dir,const char * name,uint32_t type,uint8_t eff_type)105 rc_t kar_entry_create ( KAREntry ** rtn, size_t entry_size,
106 const KDirectory * dir, const char * name, uint32_t type,
107 uint8_t eff_type
108 )
109 {
110 rc_t rc;
111
112 size_t name_len = strlen ( name ) + 1;
113 KAREntry * entry = calloc ( 1, entry_size + name_len );
114 if ( entry == NULL )
115 {
116 rc = RC (rcExe, rcNode, rcAllocating, rcMemory, rcExhausted);
117 pLogErr ( klogErr, rc, "Failed to allocated memory for entry '$(name)'",
118 "name=%s", name );
119 }
120 else
121 {
122 /* location for string copy */
123 char * dst = & ( ( char * ) entry ) [ entry_size ];
124
125 /* sanity check */
126 assert ( entry_size >= sizeof * entry );
127
128 /* populate the name by copying to the end of structure */
129 memmove ( dst, name, name_len );
130 entry -> name = dst;
131
132 entry -> type = type;
133 entry -> eff_type = eff_type;
134
135 rc = KDirectoryAccess ( dir, & entry -> access_mode, "%s", entry -> name );
136 if ( rc != 0 )
137 {
138 pLogErr ( klogErr, rc, "Failed to get access mode for entry '$(name)'",
139 "name=%s", entry -> name );
140 }
141 else
142 {
143 rc = KDirectoryDate ( dir, &entry -> mod_time, "%s", entry -> name );
144 if ( rc != 0 )
145 {
146 pLogErr ( klogErr, rc, "Failed to get modification for entry '$(name)'",
147 "name=%s", entry -> name );
148 }
149 else
150 {
151 * rtn = entry;
152 return 0;
153 }
154 }
155
156 free ( entry );
157 }
158
159 * rtn = NULL;
160 return rc;
161 }
162
163 static
kar_entry_inflate(KAREntry ** rtn,size_t entry_size,const char * name,size_t name_len,uint64_t mod_time,uint32_t access_mode,uint8_t type,uint8_t eff_type)164 rc_t kar_entry_inflate ( KAREntry **rtn, size_t entry_size, const char *name, size_t name_len,
165 uint64_t mod_time, uint32_t access_mode, uint8_t type, uint8_t eff_type )
166 {
167 rc_t rc;
168 KAREntry * entry;
169
170 STATUS ( STAT_QA, "inflating entry for '%s' with name_len: %u + entry_size: %u",
171 name, ( uint32_t ) name_len, ( uint32_t ) entry_size );
172 entry = calloc ( 1, entry_size + name_len + 1 );
173 if ( entry == NULL )
174 {
175 rc = RC (rcExe, rcNode, rcAllocating, rcMemory, rcExhausted);
176 pLogErr ( klogErr, rc, "Failed to allocated memory for entry '$(name)'",
177 "name=%s", name );
178 }
179 else
180 {
181 /* location for string copy */
182 char * dst = & ( ( char * ) entry ) [ entry_size ];
183
184 /* sanity check */
185 assert ( entry_size >= sizeof * entry );
186
187 /* populate the name by copying to the end of structure */
188 memmove ( dst, name, name_len + 1 );
189
190 entry -> name = dst;
191 entry -> mod_time = mod_time;
192 entry -> access_mode = access_mode;
193 entry -> type = type;
194 entry -> eff_type = eff_type;
195
196 *rtn = entry;
197 STATUS ( STAT_QA, "finished inflating entry for '%s'", entry -> name );
198 return 0;
199 }
200
201 free ( entry );
202
203 * rtn = NULL;
204 return rc;
205 }
206
207 static
kar_entry_cmp(const BSTNode * item,const BSTNode * n)208 int64_t CC kar_entry_cmp ( const BSTNode *item, const BSTNode *n )
209 {
210 /* TODO - ensure that this is consistent with the ordering in kfs/toc */
211 const KAREntry *a = ( const KAREntry * ) item;
212 const KAREntry *b = ( const KAREntry * ) n;
213
214 return strcmp ( a -> name, b -> name );
215 }
216
217 static
kar_entry_sort_size(const void * a,const void * b,void * ignore)218 int64_t CC kar_entry_sort_size ( const void *a, const void *b, void *ignore )
219 {
220 /* Only KARFiles should make it here */
221 const KARFile *f1 = * ( const KARFile ** ) a;
222 const KARFile *f2 = * ( const KARFile ** ) b;
223
224 return ( int64_t ) f1 -> byte_size - ( int64_t ) f2 -> byte_size;
225 }
226
227 /********** BSTree population */
228
229 static
kar_add_file(const KDirectory * dir,const char * name,void * data)230 rc_t kar_add_file ( const KDirectory *dir, const char *name, void *data )
231 {
232 KARDir * parent = ( KARDir * ) data;
233 KARFile *file;
234
235 rc_t rc = kar_entry_create ( ( KAREntry ** ) & file, sizeof * file, dir, name, kptFile, ktocentrytype_file );
236 if ( rc == 0 )
237 {
238 rc = KDirectoryFileSize ( dir, &file -> byte_size, "%s", file -> dad . name );
239 if ( rc != 0 )
240 {
241 pLogErr ( klogErr, rc, "Failed to get file size for file '$(name)'",
242 "name=%s", file -> dad . name );
243 }
244 else
245 {
246 if ( file -> byte_size == 0 ) {
247 file -> dad . eff_type = ktocentrytype_emptyfile;
248 }
249
250 rc = BSTreeInsert ( & ( parent -> contents ), &file -> dad . dad, kar_entry_cmp );
251 if ( rc == 0 )
252 {
253 file -> dad . parentDir = parent;
254
255 return 0;
256 }
257
258
259 pLogErr ( klogErr, rc, "Failed to insert file '$(name)' into tree",
260 "name=%s", file -> dad . name );
261
262 }
263
264 free ( file );
265 }
266
267 return rc;
268 }
269
270
271 static
kar_add_dir(const KDirectory * parent_dir,const char * name,void * data)272 rc_t kar_add_dir ( const KDirectory *parent_dir, const char *name, void *data )
273 {
274 KARDir * parent = ( KARDir * ) data;
275 KARDir * dir;
276
277 rc_t rc = kar_entry_create ( ( KAREntry ** ) & dir, sizeof * dir, parent_dir, name, kptDir, ktocentrytype_dir );
278 if ( rc == 0 )
279 {
280 /* BSTree is already initialized, but it doesn't hurt to be thorough */
281 BSTreeInit ( & dir -> contents );
282
283 rc = BSTreeInsert ( & ( parent -> contents ), &dir -> dad . dad, kar_entry_cmp );
284 if ( rc != 0 )
285 {
286 pLogErr ( klogErr, rc, "Failed to insert directory '$(name)' into tree",
287 "name=%s", dir -> dad . name );
288 }
289 else
290 {
291 dir -> dad . parentDir = parent;
292
293 /* the name passed to us was that of a child directory.
294 we need to recursively process it - so open up the child directory. */
295 const KDirectory * child_dir;
296 rc = KDirectoryOpenDirRead ( parent_dir, & child_dir, false, "%s", name );
297 if ( rc != 0 )
298 {
299 pLogErr ( klogErr, rc, "Failed to open directory '$(name)' for scanning",
300 "name=%s", dir -> dad . name );
301 }
302 else
303 {
304 /* recursively scan this directory to populate tree */
305 rc = kar_scan_directory ( child_dir, dir, "." );
306
307 KDirectoryRelease ( child_dir );
308
309 if ( rc == 0 )
310 return 0;
311
312 pLogErr ( klogErr, rc, "Failed to scan directory '$(name)'",
313 "name=%s", dir -> dad . name );
314 }
315 }
316
317 free ( dir );
318 }
319
320 return rc;
321 }
322
323
324 static
kar_add_alias(const KDirectory * dir,const char * name,void * data,uint32_t type)325 rc_t kar_add_alias ( const KDirectory *dir, const char *name, void *data, uint32_t type )
326 {
327 KARDir * parent = ( KARDir * ) data;
328 KARAlias *alias;
329
330 rc_t rc = kar_entry_create ( ( KAREntry ** ) & alias, sizeof * alias, dir, name, type, ktocentrytype_softlink );
331 if ( rc == 0 )
332 {
333 char resolved [ 4096 ];
334 rc = KDirectoryResolveAlias ( dir, false, resolved, sizeof resolved, "%s", name );
335 if ( rc == 0 )
336 {
337 size_t rsize = string_size ( resolved );
338 char * copy = malloc ( rsize + 1 );
339 if ( copy == NULL )
340 {
341 rc = RC (rcExe, rcNode, rcAllocating, rcMemory, rcExhausted);
342 pLogErr ( klogErr, rc, "Failed to allocated memory for entry '$(name)'",
343 "name=%s", name );
344 }
345 else
346 {
347 string_copy ( copy, rsize + 1, resolved, rsize );
348 alias -> link = copy;
349
350 rc = BSTreeInsert ( & ( parent -> contents ), &alias -> dad . dad, kar_entry_cmp );
351 if ( rc == 0 ) {
352 alias -> dad . parentDir = parent;
353
354 return 0;
355 }
356
357
358 pLogErr ( klogErr, rc, "Failed to insert file '$(name)' into tree",
359 "name=%s", alias -> dad . name );
360
361 }
362 }
363 }
364 return rc;
365 }
366
367 static
kar_populate_tree(const KDirectory * dir,uint32_t type,const char * name,void * data)368 rc_t CC kar_populate_tree ( const KDirectory *dir, uint32_t type, const char *name, void *data )
369 {
370 /* We have a KDirectory* to the directory being visited, plus an entry description,
371 giving the type and name of the entry. In addition, we have the data pointer
372 we sent in, which is either a BSTree* or a custom structure holding more data. */
373
374 switch ( type )
375 {
376 case kptFile:
377 return kar_add_file ( dir, name, data );
378
379 case kptDir:
380 return kar_add_dir ( dir, name, data );
381
382 case kptAlias:
383 case kptFile | kptAlias:
384 case kptDir | kptAlias:
385 return kar_add_alias ( dir, name, data, type );
386
387 default:
388 LogMsg ( klogWarn, "Unsupported file type" );
389 }
390
391 return 0;
392 }
393
394 /********** File searching */
395
396 static
kar_scan_directory(const KDirectory * dir,KARDir * kar_dir,const char * path)397 rc_t kar_scan_directory ( const KDirectory *dir, KARDir *kar_dir, const char *path )
398 {
399 /* In this case, the directory itself is NOT added to the tree,
400 but only its contents. Use a shallow (non-recursive) "Visit()"
401 to list contents and process each in turn. */
402
403 rc_t rc = KDirectoryVisit ( dir, false, kar_populate_tree, kar_dir, "%s", path );
404 if ( rc != 0 )
405 {
406 pLogErr ( klogErr, rc, "Failed to scan directory $(directory)",
407 "directory=%s", path );
408 }
409
410 return rc;
411 }
412
413 /********** md5 */
414
415 static
kar_md5(KDirectory * wd,KFile ** archive,const char * path,KCreateMode mode)416 rc_t kar_md5 ( KDirectory *wd, KFile **archive, const char *path, KCreateMode mode )
417 {
418 rc_t rc = 0;
419 KFile *md5_f;
420
421 /* create the *.md5 file to hold md5sum-compatible checksum */
422 rc = KDirectoryCreateFile ( wd, &md5_f, false, 0664, mode, "%s.md5", path );
423 if ( rc )
424 PLOGERR (klogFatal, (klogFatal, rc, "unable to create md5 file [$(A).md5]", PLOG_S(A), path));
425 else
426 {
427 KMD5SumFmt *fmt;
428
429 /* create md5 formatter to write to md5_f */
430 rc = KMD5SumFmtMakeUpdate ( &fmt, md5_f );
431 if ( rc )
432 LOGERR (klogErr, rc, "failed to make KMD5SumFmt");
433 else
434 {
435 KMD5File *kmd5_f;
436
437 size_t size = string_size ( path );
438 const char *fname = string_rchr ( path, size, '/' );
439 if ( fname ++ == NULL )
440 fname = path;
441
442 /* KMD5SumFmtMakeUpdate() took over ownership of "md5_f" */
443 md5_f = NULL;
444
445 /* create a file that knows how to calculate md5 as data
446 are written-through to archive, and then write digest
447 result to fmt, using "fname" as description. */
448 rc = KMD5FileMakeWrite ( &kmd5_f, * archive, fmt, fname );
449 KMD5SumFmtRelease ( fmt );
450 if ( rc )
451 LOGERR (klogErr, rc, "failed to make KMD5File");
452 else
453 {
454 /* success */
455 *archive = KMD5FileToKFile ( kmd5_f );
456 return 0;
457 }
458 }
459
460 /* error cleanup */
461 KFileRelease ( md5_f );
462 }
463
464 return rc;
465 }
466
467 /********** write to toc and archive */
468
469 static
align_offset(uint64_t offset,uint64_t alignment)470 uint64_t align_offset ( uint64_t offset, uint64_t alignment )
471 {
472 uint64_t mask = alignment - 1;
473 return ( offset + mask ) & ~ mask;
474 }
475
476 static
kar_write_header_v1(KARArchiveFile * af,uint64_t toc_size)477 void kar_write_header_v1 ( KARArchiveFile * af, uint64_t toc_size )
478 {
479 rc_t rc;
480 size_t num_writ, hdr_size;
481
482 KSraHeader hdr;
483 memmove ( hdr.ncbi, "NCBI", sizeof hdr.ncbi );
484 memmove ( hdr.sra, ".sra", sizeof hdr.sra );
485
486 hdr.byte_order = eSraByteOrderTag;
487
488 hdr.version = 1;
489
490 /* calculate header size based upon version */
491 hdr_size = sizeof hdr - sizeof hdr . u + sizeof hdr . u . v1;
492
493 /* TBD - don't use hard-coded alignment - get it from cmdline */
494 hdr.u.v1.file_offset = align_offset ( hdr_size + toc_size, 4 );
495 af -> starting_pos = hdr . u . v1 . file_offset;
496
497 rc = KFileWriteAll ( af -> archive, af -> pos, &hdr, hdr_size, &num_writ );
498 if ( rc != 0 || num_writ != hdr_size )
499 {
500 if ( rc == 0 )
501 rc = RC ( rcExe, rcFile, rcWriting, rcTransfer, rcIncomplete );
502
503 LogErr ( klogInt, rc, "Failed to write header" );
504 exit(5);
505 }
506
507 af -> pos += num_writ;
508 }
509
510
511 static
kar_write_archive(void * param,const void * buffer,size_t bytes,size_t * num_writ)512 rc_t CC kar_write_archive ( void *param, const void *buffer,
513 size_t bytes, size_t *num_writ )
514 {
515 rc_t rc = 0;
516
517 KARArchiveFile * self = param;
518
519 rc = KFileWriteAll ( self -> archive, self -> pos, buffer, bytes, num_writ );
520 self -> pos += * num_writ;
521
522 return rc;
523 }
524
525 static
526 rc_t CC kar_persist ( void *param, const void *node,
527 size_t *num_writ, PTWriteFunc write, void *write_param );
528
529 static
kar_persist_karentry(const KAREntry * entry,int type_code,size_t * num_writ,PTWriteFunc write,void * write_param)530 rc_t kar_persist_karentry ( const KAREntry * entry, int type_code,
531 size_t * num_writ, PTWriteFunc write, void *write_param )
532 {
533 rc_t rc = 0;
534 size_t total_written, total_expected;
535
536 uint16_t legacy_name_len;
537 uint8_t legacy_type_code = ( uint8_t ) type_code;
538
539 /* actual length of the string in bytes */
540 size_t name_len = strlen ( entry -> name );
541
542 STATUS ( STAT_QA, "%s: '%.*s'"
543 , __func__
544 , ( uint32_t ) name_len, entry -> name
545 );
546
547 if ( name_len > UINT16_MAX )
548 return RC (rcExe, rcNode, rcWriting, rcPath, rcExcessive);
549
550 legacy_name_len = ( uint16_t ) name_len;
551
552 /* determine size */
553 total_expected
554 = sizeof legacy_name_len
555 + name_len
556 + sizeof entry -> mod_time
557 + sizeof entry -> access_mode
558 + sizeof legacy_type_code
559 ;
560
561 /* if just determining toc size - return */
562 if ( write == NULL )
563 {
564 * num_writ = total_expected;
565
566 return 0;
567 }
568
569 total_written = 0;
570 rc = ( * write ) ( write_param, &legacy_name_len, sizeof legacy_name_len, num_writ );
571 if ( rc == 0 )
572 {
573 total_written = * num_writ;
574 rc = ( * write ) ( write_param, entry -> name, name_len, num_writ );
575 if ( rc == 0 )
576 {
577 total_written += * num_writ;
578 rc = ( * write ) ( write_param, &entry -> mod_time, sizeof entry -> mod_time, num_writ );
579 if ( rc == 0 )
580 {
581 total_written += * num_writ;
582 rc = ( * write ) ( write_param, &entry -> access_mode, sizeof entry -> access_mode, num_writ );
583 if ( rc == 0 )
584 {
585 total_written += * num_writ;
586 rc = ( * write ) ( write_param, &legacy_type_code, sizeof legacy_type_code, num_writ );
587 if ( rc == 0 )
588 total_written += * num_writ;
589 }
590 }
591 }
592 }
593
594 if ( rc == 0 && total_written != total_expected )
595 rc = RC ( rcExe, rcFile, rcWriting, rcTransfer, rcIncorrect );
596
597 * num_writ = total_written;
598
599 /* check */
600
601 return rc;
602 }
603
604 static
kar_persist_karfile(const KARFile * entry,size_t * num_writ,PTWriteFunc write,void * write_param)605 rc_t kar_persist_karfile ( const KARFile * entry, size_t *num_writ, PTWriteFunc write, void *write_param )
606 {
607 size_t total_expected, total_written;
608 rc_t rc = kar_persist_karentry ( & entry -> dad,
609 entry -> byte_size == 0 ? ktocentrytype_emptyfile : ktocentrytype_file,
610 num_writ, write, write_param );
611 if ( rc == 0 )
612 {
613 total_written = * num_writ;
614
615 /* empty files are given a special type in the toc */
616 if ( entry -> byte_size == 0 )
617 total_expected = total_written;
618 else
619 {
620 /* determine size */
621 total_expected
622 = total_written /* from KAREntry */
623 + sizeof entry -> byte_offset /* specific to KARFile */
624 + sizeof entry -> byte_size
625 ;
626
627 /* if determining size of toc - return */
628 if ( write == NULL )
629 {
630 * num_writ = total_expected;
631 return 0;
632 }
633
634 /* actually write the toc file entry */
635 rc = ( * write ) ( write_param, &entry -> byte_offset, sizeof entry -> byte_offset, num_writ );
636 if ( rc == 0 )
637 {
638 total_written += * num_writ;
639 rc = ( * write ) ( write_param, &entry -> byte_size, sizeof entry -> byte_size, num_writ );
640 if ( rc == 0 )
641 total_written += *num_writ;
642 }
643 }
644 }
645
646 if ( rc == 0 && total_written != total_expected )
647 {
648 STATUS ( STAT_QA, "total_written ( %zu ) != total_expected ( %zu )", total_written, total_expected );
649 rc = RC ( rcExe, rcFile, rcWriting, rcTransfer, rcIncorrect );
650 }
651
652 *num_writ = total_written;
653
654 return rc;
655 }
656
657
658 static
kar_persist_kardir(const KARDir * entry,size_t * num_writ,PTWriteFunc write,void * write_param)659 rc_t kar_persist_kardir ( const KARDir * entry, size_t *num_writ, PTWriteFunc write, void *write_param )
660 {
661 rc_t rc = kar_persist_karentry ( & entry -> dad, ktocentrytype_dir, num_writ, write, write_param );
662 if ( rc == 0 )
663 {
664 size_t entry_writ = * num_writ;
665 rc = BSTreePersist ( &entry -> contents, num_writ, write, write_param, kar_persist, NULL );
666 if ( rc == 0 )
667 * num_writ += entry_writ;
668 }
669
670 return rc;
671 }
672
673 static
kar_persist_karalias(const KARAlias * entry,size_t * num_writ,PTWriteFunc write,void * write_param)674 rc_t kar_persist_karalias ( const KARAlias *entry, size_t *num_writ, PTWriteFunc write, void *write_param )
675 {
676 size_t total_expected, total_written;
677 rc_t rc = kar_persist_karentry ( & entry -> dad, ktocentrytype_softlink, num_writ, write, write_param );
678 if ( rc == 0 )
679 {
680 size_t link_size = string_size ( entry -> link );
681 uint16_t legacy_link_len = ( uint16_t ) link_size;
682
683 if ( link_size > UINT16_MAX )
684 return RC (rcExe, rcNode, rcWriting, rcPath, rcExcessive);
685
686 total_written = * num_writ;
687
688 /* determine size */
689 total_expected
690 = total_written /* from KAREntry */
691 + sizeof legacy_link_len
692 + link_size /* specific to KARAlias */
693 ;
694
695 /* if determining size of toc - return */
696 if ( write == NULL )
697 {
698 * num_writ = total_expected;
699 return 0;
700 }
701
702 /* actually write the toc file entry */
703 rc = ( * write ) ( write_param, &legacy_link_len, sizeof legacy_link_len, num_writ );
704 if ( rc == 0 )
705 {
706 total_written += * num_writ;
707
708 rc = ( * write ) ( write_param, entry -> link, link_size, num_writ );
709 if ( rc == 0 )
710 total_written += * num_writ;
711 }
712 }
713
714 if ( rc == 0 && total_written != total_expected )
715 {
716 STATUS ( STAT_QA, "total_written ( %zu ) != total_expected ( %zu )", total_written, total_expected );
717 rc = RC ( rcExe, rcFile, rcWriting, rcTransfer, rcIncorrect );
718 }
719
720 *num_writ = total_written;
721
722 return rc;
723 }
724
725 static
kar_persist(void * param,const void * node,size_t * num_writ,PTWriteFunc write,void * write_param)726 rc_t CC kar_persist ( void *param, const void *node,
727 size_t *num_writ, PTWriteFunc write, void *write_param )
728 {
729 rc_t rc = Quitting ();
730 const KAREntry * entry = ( const KAREntry * ) node;
731
732 if ( rc != 0 )
733 return rc;
734
735 if ( entry -> the_flag ) {
736 /* JOJOBA : that could not be here.
737 */
738 return RC ( rcExe, rcTocEntry, rcProcessing, rcParam, rcInvalid );
739 }
740
741 STATUS ( STAT_QA, "%s called", __func__ );
742
743 switch ( entry -> type )
744 {
745 case kptFile:
746 {
747 STATUS ( STAT_QA, "file entry" );
748 rc = kar_persist_karfile ( ( const KARFile* ) entry, num_writ, write, write_param );
749 return rc;
750 }
751 case kptDir:
752 {
753 STATUS ( STAT_QA, "directory entry" );
754 rc = kar_persist_kardir ( ( const KARDir* ) entry, num_writ, write, write_param );
755 break;
756 }
757 case kptAlias:
758 case kptFile | kptAlias:
759 case kptDir | kptAlias:
760 STATUS ( STAT_USR, "alias entry" );
761 rc = kar_persist_karalias ( ( const KARAlias * ) entry, num_writ, write, write_param );
762 break;
763 default:
764 STATUS ( 0, "unknown entry type: id %u", entry -> type );
765 break;
766 }
767
768 return rc;
769 }
770
771 static
kar_eval_toc_size(KARDir * kar_dir)772 uint64_t kar_eval_toc_size ( KARDir *kar_dir )
773 {
774 rc_t rc;
775 size_t toc_size = 0;
776 STATUS ( STAT_QA, "evaluating toc size" );
777 rc = BSTreePersist ( & ( kar_dir -> contents ), & toc_size, NULL, NULL, kar_persist, NULL );
778 if ( rc != 0 )
779 {
780 LogErr ( klogInt, rc, "Failed to determine TOC size" );
781 exit(5);
782 }
783 STATUS ( STAT_QA, "toc size reported as %lu bytes", toc_size );
784
785 return toc_size;
786 }
787
788 static
kar_write_toc(KARArchiveFile * af,KARDir * kar_dir)789 void kar_write_toc ( KARArchiveFile * af, KARDir *kar_dir )
790 {
791 rc_t rc;
792 size_t toc_size = 0;
793
794 STATUS ( STAT_QA, "writing toc" );
795 rc = BSTreePersist ( & ( kar_dir -> contents ), & toc_size, kar_write_archive, af, kar_persist, NULL );
796 if ( rc != 0 )
797 {
798 LogErr ( klogInt, rc, "Failed to determine TOC size" );
799 exit(5);
800 }
801
802 if ( af -> starting_pos > af -> pos ) {
803 /* It would be better to use KFileSetSize,
804 * however, md5 file can only shrunk files.
805 */
806 uint32_t BF = 0;
807 rc = KFileWriteAll (
808 af -> archive,
809 af -> pos,
810 & BF,
811 af -> starting_pos - af -> pos,
812 NULL
813 );
814 if ( rc != 0 ) {
815 LogErr ( klogInt, rc, "Failed to write TOC" );
816 exit(5);
817 }
818
819 af -> pos = af -> starting_pos;
820 }
821
822 STATUS ( STAT_QA, "toc written" );
823 }
824
825 static
kar_purge_entries(KAREntry * Root,bool FlagValue)826 rc_t CC kar_purge_entries ( KAREntry * Root, bool FlagValue )
827 {
828 rc_t rc;
829 KARWek * Wek;
830
831 rc = 0;
832 Wek = NULL;
833
834 rc = kar_entry_filter_flag ( Root, & Wek, FlagValue );
835 if ( rc == 0 ) {
836 for ( size_t llp = 0; llp < kar_wek_size ( Wek ); llp ++ ) {
837 KAREntry * Entry = ( KAREntry * ) kar_wek_get ( Wek, llp );
838
839 if ( Entry != NULL ) {
840 if ( Entry -> parentDir != NULL ) {
841 bool good = BSTreeUnlink (
842 & ( Entry -> parentDir -> contents ),
843 & ( Entry -> dad )
844 );
845 if ( good ) {
846 kar_entry_whack ( & ( Entry -> dad ), NULL );
847 }
848 }
849 }
850 }
851
852 kar_wek_dispose ( Wek );
853 }
854
855 return rc;
856 } /* kar_purge_entries () */
857
858 static
kar_entry_mark_to_keep(KAREntry * self)859 rc_t CC kar_entry_mark_to_keep ( KAREntry * self )
860 {
861 rc_t rc = kar_entry_set_flag ( self, true );
862 if ( rc == 0 ) {
863 KAREntry * Ent = & ( self -> parentDir -> dad );
864 while ( Ent != NULL ) {
865 Ent -> the_flag = true;
866
867 Ent = & ( Ent -> parentDir -> dad );
868 }
869 }
870
871 return rc;
872 } /* kar_entry_mark_to_keep () */
873
874 static
kar_keep_keep_entries(KARDir * kar_dir,const Params * params)875 rc_t CC kar_keep_keep_entries ( KARDir * kar_dir, const Params * params )
876 {
877 rc_t rc;
878 uint32_t Count;
879 BSTree p2e;
880
881 rc = 0;
882 Count = 0;
883
884 if ( params == NULL ) {
885 return 0;
886 }
887
888 if ( params -> keep == NULL ) {
889 return 0;
890 }
891
892 rc = VNameListCount ( params -> keep, & Count );
893 if ( rc == 0 ) {
894 if ( Count == 0 ) {
895 return 0;
896 }
897
898 rc = kar_p2e_init ( & p2e, & ( kar_dir -> dad ) );
899 if ( rc == 0 ) {
900 rc = kar_entry_set_flag ( & ( kar_dir -> dad ), false );
901 if ( rc == 0 ) {
902 for ( uint32_t llp = 0; llp < Count; llp ++ ) {
903 const char * Name = NULL;
904 KAREntry * Entry = NULL;
905
906 rc = VNameListGet ( params -> keep, llp, & Name );
907 if ( rc != 0 ) {
908 break;
909 }
910
911 Entry = kar_p2e_find ( & p2e, Name );
912 if ( Entry != NULL ) {
913 rc = kar_entry_mark_to_keep ( Entry );
914 if ( rc != 0 ) {
915 break;
916 }
917 }
918 }
919 }
920 kar_p2e_whack ( & p2e );
921
922 if ( rc == 0 ) {
923 rc = kar_purge_entries ( & ( kar_dir -> dad ), false );
924
925 if ( rc == 0 ) {
926 rc = kar_entry_set_flag (
927 & ( kar_dir -> dad ),
928 false
929 );
930 }
931 }
932 }
933 }
934
935 return rc;
936 } /* kar_keep_keep_entries () */
937
938 static
kar_drop_drop_entries(KARDir * kar_dir,const Params * params)939 rc_t CC kar_drop_drop_entries ( KARDir * kar_dir, const Params * params )
940 {
941 rc_t rc;
942 uint32_t Count;
943 BSTree p2e;
944
945 rc = 0;
946 Count = 0;
947
948 if ( params == NULL ) {
949 return 0;
950 }
951
952 if ( params -> drop == NULL ) {
953 return 0;
954 }
955
956 rc = VNameListCount ( params -> drop, & Count );
957 if ( rc == 0 ) {
958 if ( Count == 0 ) {
959 return 0;
960 }
961
962 rc = kar_p2e_init ( & p2e, & ( kar_dir -> dad ) );
963 if ( rc == 0 ) {
964 rc = kar_entry_set_flag ( & ( kar_dir -> dad ), true );
965 if ( rc == 0 ) {
966 for ( uint32_t llp = 0; llp < Count; llp ++ ) {
967 const char * Name = NULL;
968 KAREntry * Entry = NULL;
969
970 rc = VNameListGet ( params -> drop, llp, & Name );
971 if ( rc != 0 ) {
972 break;
973 }
974
975 Entry = kar_p2e_find ( & p2e, Name );
976 if ( Entry != NULL ) {
977 rc = kar_entry_set_flag ( Entry, false );
978 if ( rc != 0 ) {
979 break;
980 }
981 }
982 }
983 }
984 kar_p2e_whack ( & p2e );
985
986 if ( rc == 0 ) {
987 rc = kar_purge_entries ( & ( kar_dir -> dad ), false );
988
989 if ( rc == 0 ) {
990 rc = kar_entry_set_flag (
991 & ( kar_dir -> dad ),
992 false
993 );
994 }
995 }
996 }
997 }
998
999 return rc;
1000 } /* kar_drop_drop_entries () */
1001
1002 static
kar_transform_tok(KARDir * kar_dir,const Params * params)1003 rc_t CC kar_transform_tok ( KARDir * kar_dir, const Params * params )
1004 {
1005 rc_t rc;
1006
1007 rc = 0;
1008
1009 if ( kar_dir == NULL ) {
1010 return RC ( rcExe, rcTocEntry, rcProcessing, rcParam, rcNull );
1011 }
1012
1013 if ( rc == 0 ) {
1014 /* First we should to leave all 'keep' nodes
1015 */
1016 rc = kar_keep_keep_entries ( kar_dir, params );
1017 if ( rc == 0 ) {
1018 /* Second we should remove all 'drop' nodes
1019 */
1020 rc = kar_drop_drop_entries ( kar_dir, params );
1021 }
1022 }
1023
1024 return rc;
1025 } /* kar_transform_tok () */
1026
1027 static
kar_prepare_toc(KARDir * kar_dir,KARWek ** Wek,const Params * params)1028 rc_t kar_prepare_toc ( KARDir *kar_dir, KARWek ** Wek, const Params * params )
1029 {
1030 rc_t rc = 0;
1031
1032 if ( kar_dir == NULL ) {
1033 return RC ( rcExe, rcTocEntry, rcProcessing, rcParam, rcNull );
1034 }
1035
1036 rc = kar_transform_tok ( kar_dir, params );
1037 if ( rc == 0 ) {
1038 rc = kar_entry_list_files ( & ( kar_dir -> dad ), Wek );
1039 if ( rc == 0 ) {
1040 uint64_t offset;
1041 size_t i;
1042 size_t num_files = kar_wek_size ( * Wek );
1043 KARFile ** Files = ( KARFile ** ) kar_wek_data ( * Wek );
1044
1045 /* now, sort the array based upon size - use <klib/sort.h> */
1046 ksort (
1047 Files,
1048 num_files,
1049 sizeof ( KARFile * ),
1050 kar_entry_sort_size,
1051 NULL
1052 );
1053
1054 /* now you can assign offsets to the files in the array */
1055 for ( i = 0, offset = 0; i < num_files; ++ i )
1056 {
1057 KARFile *f = Files [ i ];
1058 f -> byte_offset = offset;
1059
1060 offset += f -> byte_size;
1061
1062 /* perform aligning to boundary */
1063 offset = align_offset ( offset, 4 );
1064 }
1065
1066 }
1067 }
1068
1069 return rc;
1070 }
1071
1072 static
kar_write_file(KARArchiveFile * af,const KDirectory * wd,const KARFile * file,const char * root_dir)1073 void kar_write_file ( KARArchiveFile *af, const KDirectory *wd, const KARFile *file, const char * root_dir )
1074 {
1075 rc_t rc;
1076 char *buffer;
1077 size_t num_read, align_size;
1078 uint64_t pos = 0;
1079 char align_buffer [ 4 ] = "0000";
1080 size_t bsize = 128 * 1024 * 1024;
1081
1082 const KFile *f;
1083
1084 char filename [ 4096 ];
1085 size_t path_size;
1086
1087 if ( file -> byte_size == 0 )
1088 return;
1089
1090 if ( bsize > file -> byte_size )
1091 bsize = file -> byte_size;
1092
1093 STATUS ( STAT_QA, "writing file '%s'", file -> dad . name );
1094
1095 path_size = kar_entry_full_path ( & file -> dad, root_dir, filename, sizeof filename );
1096 if ( path_size == sizeof filename )
1097 {
1098 /* path name was somehow too long */
1099 rc = RC ( rcExe, rcFile, rcWriting, rcMemory, rcExhausted );
1100 LogErr ( klogInt, rc, "File path was too long" );
1101 exit (5);
1102 }
1103
1104 STATUS ( STAT_QA, "opening: full path is '%s'", filename );
1105 rc = KDirectoryOpenFileRead ( wd, &f, "%s", filename );
1106 if ( rc != 0 )
1107 {
1108 pLogErr ( klogInt, rc, "Failed to open file $(fname)", "fname=%s", file -> dad . name );
1109 exit (6);
1110 }
1111
1112 STATUS ( STAT_QA, "allocating buffer of %,zu bytes", bsize );
1113 buffer = malloc ( bsize );
1114 if ( buffer == NULL )
1115 {
1116 rc = RC ( rcExe, rcFile, rcWriting, rcMemory, rcExhausted );
1117 pLogErr ( klogInt, rc, "Failed to open file $(fname)", "fname=%s", file -> dad . name );
1118 exit ( 7 );
1119 }
1120
1121 /* establish current position */
1122 align_size = align_offset ( af -> pos, 4 ) - af -> pos;
1123 if ( align_size != 0 )
1124 rc = KFileWriteAll ( af -> archive, af -> pos, align_buffer, align_size, NULL );
1125
1126 af -> pos = af -> starting_pos + file -> byte_offset;
1127
1128 while ( rc == 0 && pos < file -> byte_size )
1129 {
1130 size_t num_writ, to_read = bsize;
1131
1132 if ( pos + to_read > file -> byte_size )
1133 to_read = ( size_t ) ( file -> byte_size - pos );
1134
1135 STATUS ( STAT_QA, "about to read at offset %lu from input file '%s'", pos, filename );
1136 rc = KFileReadAll ( f, pos, buffer, to_read, & num_read );
1137 if ( rc != 0 || num_read == 0 )
1138 break;
1139
1140 STATUS ( STAT_QA, "about to write %zu bytes to archive", num_read );
1141 rc = KFileWriteAll ( af -> archive, af -> pos, buffer, num_read, & num_writ );
1142 if ( rc != 0 || num_writ != num_read )
1143 {
1144 /* error */
1145 break;
1146 }
1147
1148 af -> pos += num_writ;
1149 pos += num_read;
1150 }
1151
1152 STATUS ( STAT_PRG, "freeing memory" );
1153 free ( buffer );
1154
1155 STATUS ( STAT_QA, "closing '%s'", filename );
1156 KFileRelease ( f );
1157 }
1158
1159 static
kar_make(const KDirectory * wd,KFile * archive,KARDir * kar_dir,const char * root_dir,const Params * params)1160 rc_t kar_make ( const KDirectory * wd, KFile *archive, KARDir *kar_dir, const char * root_dir, const Params * params )
1161 {
1162 rc_t rc = 0;
1163 KARWek * Files = 0;
1164
1165 rc = kar_prepare_toc ( kar_dir, & Files, params );
1166 if ( rc == 0 )
1167 {
1168 uint64_t i, toc_size;
1169 KARArchiveFile af;
1170 /* evaluate toc size */
1171 toc_size = kar_eval_toc_size ( kar_dir );
1172
1173 af . starting_pos = 0;
1174 af . pos = 0;
1175 af . archive = archive;
1176
1177 /*write header */
1178 kar_write_header_v1 ( & af, toc_size );
1179
1180 /* write toc */
1181 kar_write_toc ( & af, kar_dir );
1182
1183 /* write each of the files in order */
1184 STATUS ( STAT_QA, "about to write %u files", kar_wek_size ( Files ) );
1185 for ( i = 0; i < kar_wek_size ( Files ); ++ i )
1186 {
1187 KARFile * File = ( KARFile * ) kar_wek_get ( Files, i );
1188 STATUS ( STAT_QA, "writing file %u: '%s'", i, File -> dad . name );
1189 kar_write_file ( & af, wd, File, root_dir );
1190 }
1191
1192 kar_wek_dispose ( Files );
1193 }
1194
1195 return rc;
1196 }
1197
1198
1199 /********** main create execution */
1200
1201
1202 static
kar_create(const Params * p)1203 rc_t kar_create ( const Params *p )
1204 {
1205 rc_t rc;
1206
1207 KDirectory *wd;
1208
1209 rc = KDirectoryNativeDir ( &wd );
1210 if ( rc != 0 )
1211 LogErr ( klogInt, rc, "Failed to create working directory" );
1212 else
1213 {
1214 KFile *archive;
1215 KCreateMode mode = ( p -> force ? kcmInit : kcmCreate ) | kcmParents;
1216 rc = KDirectoryCreateFile ( wd, &archive, false, 0666, mode,
1217 "%s", p -> archive_path );
1218 if ( rc != 0 )
1219 {
1220 pLogErr ( klogErr, rc, "Failed to create archive $(archive)",
1221 "archive=%s", p -> archive_path );
1222 }
1223 else
1224 {
1225 if ( p -> md5sum )
1226 rc = kar_md5 ( wd, &archive, p -> archive_path, mode );
1227
1228 if ( rc == 0 )
1229 {
1230 KARDir the_dir;
1231 memset ( & the_dir, 0, sizeof ( the_dir ) );
1232 BSTreeInit ( & ( the_dir . contents ) );
1233 the_dir . dad . type = kptDir;
1234 the_dir . dad . eff_type = ktocentrytype_dir;
1235
1236 /* build contents by walking input directory if given,
1237 and adding the individual members if given */
1238 if ( p -> dir_count != 0 )
1239 {
1240 rc = kar_scan_directory ( wd, & the_dir, p -> directory_path );
1241 if ( rc == 0 )
1242 {
1243 rc = kar_make ( wd, archive, & the_dir, p -> directory_path, p );
1244 if ( rc != 0 )
1245 LogErr ( klogInt, rc, "Failed to build archive" );
1246 }
1247 }
1248
1249 BSTreeWhack ( & ( the_dir . contents ), kar_entry_whack, NULL );
1250 }
1251
1252 KFileRelease ( archive );
1253 }
1254
1255 KDirectoryRelease ( wd );
1256 }
1257
1258 return rc;
1259 }
1260
1261 /*******************************************************************************
1262 * Test / Extract
1263 */
1264
1265 static
kar_verify_header(const KFile * archive,KSraHeader * hdr)1266 uint64_t kar_verify_header ( const KFile *archive, KSraHeader *hdr )
1267 {
1268 rc_t rc;
1269
1270 size_t num_read;
1271
1272
1273 STSMSG (1, ("Verifying header\n"));
1274
1275 rc = KFileReadAll ( archive, 0, hdr, sizeof * hdr, &num_read );
1276 if ( rc != 0 )
1277 {
1278 LOGERR (klogErr, rc, "failed to access archive file");
1279 exit ( 1 );
1280 }
1281
1282 if ( num_read < sizeof * hdr - sizeof hdr -> u )
1283 {
1284 rc = RC ( rcExe, rcFile, rcValidating, rcOffset, rcInvalid );
1285 LOGERR (klogErr, rc, "corrupt archive file - invalid header");
1286 exit ( 1 );
1287 }
1288
1289 /* verify "ncbi" and "sra" members */
1290 if ( memcmp ( hdr -> ncbi, "NCBI", sizeof hdr -> ncbi ) != 0 ||
1291 memcmp ( hdr -> sra, ".sra", sizeof hdr -> sra ) != 0 )
1292 {
1293 rc = RC ( rcExe, rcFile, rcValidating, rcFormat, rcInvalid );
1294 LOGERR (klogErr, rc, "invalid file format");
1295 exit ( 1 );
1296 }
1297
1298 /* test "byte_order".
1299 this is allowed to be either eSraByteOrderTag or eSraByteOrderReverse.
1300 anything else, is garbage */
1301 if ( hdr -> byte_order != eSraByteOrderTag && hdr -> byte_order != eSraByteOrderReverse )
1302 {
1303 rc = RC ( rcExe, rcFile, rcValidating, rcByteOrder, rcInvalid );
1304 LOGERR (klogErr, rc, "failed to access archive file - invalid byte order");
1305 exit ( 1 );
1306 }
1307
1308 if ( hdr -> byte_order == eSraByteOrderReverse )
1309 {
1310 hdr -> version = bswap_32 ( hdr -> version );
1311 }
1312
1313 /* test version */
1314 if ( hdr -> version == 0 )
1315 {
1316 rc = RC ( rcExe, rcFile, rcValidating, rcInterface, rcInvalid );
1317 LOGERR (klogErr, rc, "invalid version");
1318 exit ( 1 );
1319 }
1320
1321 if ( hdr -> version > 1 )
1322 {
1323 rc = RC ( rcExe, rcFile, rcValidating, rcInterface, rcUnsupported );
1324 LOGERR (klogErr, rc, "version not supported");
1325 exit ( 1 );
1326 }
1327
1328 /* test actual size against specific header version */
1329 if ( num_read < sizeof * hdr - sizeof hdr -> u + sizeof hdr -> u . v1 )
1330 {
1331 rc = RC ( rcExe, rcFile, rcValidating, rcOffset, rcIncorrect );
1332 LOGERR (klogErr, rc, "failed to read header");
1333 exit ( 1 );
1334 }
1335
1336 return num_read;
1337 }
1338
1339 static
toc_data_copy(void * dst,size_t dst_size,const uint8_t * toc_data,size_t toc_data_size,size_t offset)1340 size_t toc_data_copy ( void * dst, size_t dst_size, const uint8_t * toc_data, size_t toc_data_size, size_t offset )
1341 {
1342 if ( offset + dst_size > toc_data_size )
1343 {
1344 rc_t rc = RC ( rcExe, rcFile, rcValidating, rcOffset, rcInvalid );
1345 LOGERR (klogErr, rc, "toc offset out of bounds");
1346 exit ( 3 );
1347 }
1348
1349 memmove ( dst, & toc_data [ offset ], dst_size );
1350 return offset + dst_size;
1351 }
1352
1353 static
kar_alias_find_link(const void * item,const BSTNode * node)1354 int64_t kar_alias_find_link ( const void *item, const BSTNode *node )
1355 {
1356 const char *link = ( const char * ) item;
1357 KAREntry *entry = ( KAREntry * ) node;
1358
1359 uint64_t link_size = string_size ( link );
1360 uint64_t name_size = string_size ( entry -> name );
1361
1362 return string_cmp ( item, link_size, entry -> name, name_size, link_size );
1363 }
1364
1365
1366 static
kar_alias_link_type(BSTNode * node,void * data)1367 void kar_alias_link_type ( BSTNode *node, void *data )
1368 {
1369 /* archive fake root directory node */
1370 const KARDir * root = ( const KARDir * ) data;
1371
1372 const KARDir *dir;
1373 KAREntry *entry = ( KAREntry * ) node;
1374
1375 if ( entry -> type == kptDir )
1376 {
1377 /* need to go recursive on contents */
1378 dir = ( const KARDir * ) entry;
1379 BSTreeForEach ( &dir -> contents, false, kar_alias_link_type, ( void * ) root );
1380 }
1381 else if ( entry -> type == kptAlias )
1382 {
1383 const KAREntry *e;
1384
1385 size_t lsize;
1386 const char *link, *end;
1387 KARAlias *alias = ( KARAlias * ) entry;
1388
1389 link = alias -> link;
1390 lsize = string_size ( link );
1391 end = link + lsize;
1392
1393 /* if the link is an absolute path, it's outside of archive */
1394 if ( link [ 0 ] == '/' )
1395 return;
1396
1397 /* establish root for search */
1398 dir = entry -> parentDir;
1399 if ( dir == NULL )
1400 dir = root;
1401 e = & dir -> dad;
1402
1403 /* walk the path */
1404 while ( e != NULL && link < end )
1405 {
1406 /* get the segment */
1407 const char *seg = link;
1408 char *sep = string_chr ( link, lsize, '/' );
1409 if ( sep == NULL )
1410 link = end;
1411 else
1412 {
1413 *sep = 0;
1414 link = sep + 1;
1415 }
1416
1417 /* if the segment is empty, then we saw '/'.
1418 if the segment is a single '.', then it means same thing */
1419 if ( seg [ 0 ] == 0 ||
1420 ( seg [ 0 ] == '.' && seg [ 1 ] == 0 ) )
1421 {
1422 /* do nothing */
1423 }
1424 else if ( seg [ 0 ] == '.' && seg [ 1 ] == '.' && seg [ 2 ] == 0 )
1425 {
1426 /* move up to parent */
1427 if ( e == & root -> dad )
1428 e = NULL;
1429 else
1430 {
1431 e = & e -> parentDir -> dad;
1432 if ( e == NULL )
1433 e = & root -> dad;
1434 }
1435 }
1436 else
1437 {
1438 rc_t rc;
1439
1440 while ( e != NULL && e -> type == kptAlias )
1441 {
1442 assert ( ( ( KARAlias * ) e ) -> resolved == NULL );
1443 kar_alias_link_type ( ( BSTNode * ) & e -> dad, ( void * ) root );
1444 e = ( ( KARAlias * ) e ) -> resolved;
1445 }
1446
1447 if ( e -> type == ( kptDir | kptAlias ) )
1448 {
1449 assert ( ( ( KARAlias * ) e ) -> resolved != NULL );
1450 e = ( ( KARAlias * ) e ) -> resolved;
1451 }
1452
1453 /* move down */
1454 if ( e -> type == kptDir )
1455 {
1456 dir = ( KARDir * ) e;
1457 e = ( KAREntry * ) BSTreeFind ( & dir -> contents, seg, kar_alias_find_link );
1458
1459 while ( e != NULL && ( e -> type & kptAlias ) != 0 )
1460 {
1461 if ( ( ( const KARAlias * ) e ) -> resolved == NULL )
1462 break;
1463 e = ( ( const KARAlias * ) e ) -> resolved;
1464 }
1465 }
1466 else
1467 {
1468 e = NULL;
1469 rc = RC ( rcExe, rcPath, rcValidating, rcPath, rcInvalid );
1470 LOGERR (klogErr, rc, "unable to locate symlink reference");
1471 }
1472 }
1473
1474 if ( sep != NULL )
1475 *sep = '/';
1476 }
1477
1478 if ( e != NULL )
1479 {
1480 assert ( link == end );
1481 alias -> dad . type = e -> type | kptAlias;
1482 alias -> resolved = ( KAREntry * ) e;
1483 }
1484 }
1485 }
1486
1487 static
kar_inflate_toc(PBSTNode * node,void * data)1488 void kar_inflate_toc ( PBSTNode *node, void *data )
1489 {
1490 rc_t rc = 0;
1491
1492 size_t offset = 0;
1493 const uint8_t * toc_data = node -> data . addr;
1494 char buffer [ 4096 ], * name = buffer;
1495 uint16_t name_len = 0;
1496 uint64_t mod_time = 0;
1497 uint32_t access_mode = 0;
1498 uint8_t type_code = 0;
1499
1500 offset = toc_data_copy ( & name_len, sizeof name_len, toc_data, node -> data . size, offset );
1501 if ( name_len >= sizeof buffer )
1502 {
1503 name = malloc ( name_len + 1 );
1504 if ( name == NULL )
1505 exit ( 10 );
1506 }
1507 offset = toc_data_copy ( name, name_len, toc_data, node -> data . size, offset );
1508 name [ name_len ] = 0;
1509 STATUS ( STAT_QA, "inflating '%s'", name );
1510 offset = toc_data_copy ( & mod_time, sizeof mod_time, toc_data, node -> data . size, offset );
1511 offset = toc_data_copy ( & access_mode, sizeof access_mode, toc_data, node -> data . size, offset );
1512 offset = toc_data_copy ( & type_code, sizeof type_code, toc_data, node -> data . size, offset );
1513
1514 switch ( type_code )
1515 {
1516 case ktocentrytype_file:
1517 {
1518 KARFile *file;
1519
1520 rc = kar_entry_inflate ( ( KAREntry ** ) &file, sizeof *file, name, name_len,
1521 mod_time, access_mode, kptFile, ktocentrytype_file );
1522 if ( rc != 0 )
1523 {
1524 LOGERR (klogErr, rc, "failed inflate KARFile");
1525 exit ( 3 );
1526 }
1527
1528 offset = toc_data_copy ( & file -> byte_offset, sizeof file -> byte_offset, toc_data, node -> data . size, offset );
1529 toc_data_copy ( & file -> byte_size, sizeof file -> byte_size, toc_data, node -> data . size, offset );
1530
1531 if ( file -> byte_size > max_size )
1532 max_size = file -> byte_size;
1533 if ( file -> byte_offset > max_offset )
1534 max_offset = file -> byte_offset;
1535
1536 STATUS ( STAT_QA, "inserting '%s'", file -> dad . name );
1537 rc = BSTreeInsert ( ( BSTree * ) data, &file -> dad . dad, kar_entry_cmp );
1538 if ( rc != 0 )
1539 {
1540 LOGERR (klogErr, rc, "failed insert KARFile into tree");
1541 exit ( 3 );
1542 }
1543
1544 break;
1545 }
1546 case ktocentrytype_emptyfile:
1547 {
1548 KARFile *file;
1549
1550 rc = kar_entry_inflate ( ( KAREntry ** ) &file, sizeof *file, name, name_len,
1551 mod_time, access_mode, kptFile, ktocentrytype_emptyfile );
1552 if ( rc != 0 )
1553 {
1554 LOGERR (klogErr, rc, "failed inflate KARFile");
1555 exit ( 3 );
1556 }
1557
1558 file -> byte_offset = 0;
1559 file -> byte_size = 0;
1560
1561 STATUS ( STAT_QA, "inserting '%s'", file -> dad . name );
1562 rc = BSTreeInsert ( ( BSTree * ) data, &file -> dad . dad, kar_entry_cmp );
1563 if ( rc != 0 )
1564 {
1565 LOGERR (klogErr, rc, "failed insert KARFile into tree");
1566 exit ( 3 );
1567 }
1568
1569 break;
1570 }
1571 case ktocentrytype_dir:
1572 {
1573 KARDir *dir;
1574 PBSTree *ptree;
1575
1576 rc = kar_entry_inflate ( ( KAREntry ** ) &dir, sizeof *dir, name, name_len, mod_time, access_mode, kptDir, ktocentrytype_dir );
1577 if ( rc != 0 )
1578 {
1579 LOGERR (klogErr, rc, "failed inflate KARDir");
1580 exit ( 3 );
1581 }
1582
1583 BSTreeInit ( &dir -> contents );
1584
1585
1586 rc = PBSTreeMake ( & ptree, & toc_data [ offset ], node -> data . size - offset, false );
1587 if ( rc != 0 )
1588 LOGERR (klogErr, rc, "failed make PBSTree");
1589 else
1590 {
1591 PBSTreeForEach ( ptree, false, kar_inflate_toc, &dir -> contents );
1592
1593 PBSTreeWhack ( ptree );
1594 }
1595
1596 rc = BSTreeInsert ( ( BSTree * ) data, &dir -> dad . dad, kar_entry_cmp );
1597 if ( rc != 0 )
1598 {
1599 LOGERR (klogErr, rc, "failed insert KARDir into tree");
1600 exit ( 3 );
1601 }
1602
1603 break;
1604 }
1605 case ktocentrytype_softlink:
1606 {
1607 KARAlias *alias;
1608
1609 rc = kar_entry_inflate ( ( KAREntry ** ) &alias, sizeof *alias, name, name_len, mod_time, access_mode, kptAlias, ktocentrytype_softlink );
1610 if ( rc != 0 )
1611 {
1612 LOGERR (klogErr, rc, "failed inflate KARAlias");
1613 exit ( 3 );
1614 }
1615
1616 /* need to reuse name* for soft-link string */
1617 if ( name != buffer )
1618 free ( name );
1619
1620 offset = toc_data_copy ( & name_len, sizeof name_len, toc_data, node -> data . size, offset );
1621 name = malloc ( name_len + 1 );
1622 if ( name == NULL )
1623 exit ( 10 );
1624 offset = toc_data_copy ( name, name_len, toc_data, node -> data . size, offset );
1625 name [ name_len ] = 0;
1626
1627 alias -> link = name;
1628 name = buffer;
1629
1630 rc = BSTreeInsert ( ( BSTree * ) data, &alias -> dad . dad, kar_entry_cmp );
1631 if ( rc != 0 )
1632 {
1633 LOGERR (klogErr, rc, "failed insert KARAlias into tree");
1634 exit ( 3 );
1635 }
1636 break;
1637 }
1638 default:
1639 STATUS ( 0, "unknown entry type: id %u", type_code );
1640 break;
1641 }
1642
1643 if ( name != buffer )
1644 free ( name );
1645 }
1646
1647 static
kar_extract_toc(const KFile * archive,BSTree * tree,uint64_t * toc_pos,const size_t toc_size)1648 rc_t kar_extract_toc ( const KFile *archive, BSTree *tree, uint64_t *toc_pos, const size_t toc_size )
1649 {
1650 rc_t rc = 0;
1651
1652 char *buffer;
1653 buffer = malloc ( toc_size );
1654 if ( buffer == NULL )
1655 {
1656 rc = RC ( rcExe, rcFile, rcAllocating, rcMemory, rcExhausted );
1657 LOGERR (klogErr, rc, "failed allocate memory");
1658 }
1659 else
1660 {
1661 size_t num_read;
1662
1663 rc = KFileReadAll ( archive, *toc_pos, buffer, toc_size, &num_read );
1664 if ( rc != 0 )
1665 {
1666 LOGERR (klogErr, rc, "failed to access archive file");
1667 exit ( 2 );
1668 }
1669
1670 if ( num_read < toc_size )
1671 {
1672 rc = RC ( rcExe, rcFile, rcValidating, rcOffset, rcInsufficient );
1673 LOGERR (klogErr, rc, "failed to read header");
1674 }
1675 else
1676 {
1677 PBSTree *ptree;
1678
1679 rc = PBSTreeMake ( &ptree, buffer, num_read, false );
1680 if ( rc != 0 )
1681 LOGERR (klogErr, rc, "failed make PBSTree");
1682 else
1683 {
1684 PBSTreeForEach ( ptree, false, kar_inflate_toc, tree );
1685
1686 PBSTreeWhack ( ptree );
1687 }
1688 }
1689
1690 free ( buffer );
1691 }
1692
1693 return rc;
1694 }
1695
1696 /*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*
1697 * kar_extract part ... things optimizing reading order from archive
1698 *_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*/
1699 typedef struct stored_file stored_file;
1700 struct stored_file
1701 {
1702 const KARFile * file;
1703
1704 KDirectory * cdir; /* Note, we add ref to it */
1705 }; /* stored_file */
1706
1707 #define SF_SE(SF,SH) SF -> file -> dad . SH
1708 #define SF_SF(SF,SH) SF -> file -> SH
1709
1710 static
stored_file_make(stored_file ** ret,const KARFile * file,KDirectory * cdir)1711 rc_t stored_file_make ( stored_file ** ret,
1712 const KARFile * file,
1713 KDirectory * cdir
1714 )
1715 {
1716 rc_t rc = 0;
1717 stored_file * stf = NULL;
1718
1719 if ( ret != NULL ) {
1720 * ret = NULL;
1721 }
1722
1723 if ( ret == NULL ) {
1724 return RC ( rcExe, rcFile, rcConstructing, rcSelf, rcNull );
1725 }
1726
1727 if ( file == NULL || cdir == NULL ) {
1728 return RC ( rcExe, rcFile, rcConstructing, rcParam, rcNull );
1729 }
1730
1731 stf = calloc ( 1, sizeof ( stored_file ) );
1732 if ( stf == NULL ) {
1733 rc = RC ( rcExe, rcFile, rcConstructing, rcMemory, rcExhausted );
1734 }
1735 else {
1736 rc = KDirectoryAddRef ( cdir );
1737 if ( rc == 0 ) {
1738 stf -> file = file;
1739 stf -> cdir = cdir;
1740
1741 * ret = stf;
1742 }
1743 }
1744
1745 return rc;
1746 } /* stored_file_make () */
1747
1748 static
stored_file_dispose(stored_file * self)1749 rc_t stored_file_dispose ( stored_file * self )
1750 {
1751 if ( self != NULL ) {
1752 if ( self -> cdir != NULL ) {
1753 KDirectoryRelease ( self -> cdir );
1754 self -> cdir = NULL;
1755 }
1756
1757 free ( self );
1758 }
1759
1760 return 0;
1761 } /* stored_file_whack () */
1762
1763 static
stored_file_add(KARWek * wek,const KARFile * file,KDirectory * cdir)1764 rc_t stored_file_add (
1765 KARWek * wek,
1766 const KARFile * file,
1767 KDirectory * cdir
1768 )
1769 {
1770 rc_t rc = 0;
1771 stored_file * SF;
1772
1773 if ( wek == NULL ) {
1774 rc = RC ( rcExe, rcFile, rcAllocating, rcParam, rcNull );
1775 }
1776
1777 rc = stored_file_make ( & SF, file, cdir );
1778 if ( rc == 0 ) {
1779 rc = kar_wek_append ( wek, ( void * ) SF );
1780 }
1781
1782 return rc;
1783 } /* stored_file_add () */
1784
1785 /****************************************************************
1786 * Optimized retrieval data from remote
1787 ****************************************************************/
1788
1789 typedef struct extract_block extract_block;
1790 struct extract_block
1791 {
1792 uint64_t extract_pos;
1793
1794 KDirectory *cdir;
1795 const KFile *archive;
1796
1797 KARWek * wek;
1798
1799 rc_t rc;
1800
1801 };
1802
1803 static bool CC kar_extract ( BSTNode *node, void *data );
1804
1805 static
extract_file(const KARFile * src,const extract_block * eb)1806 rc_t extract_file ( const KARFile *src, const extract_block *eb )
1807 {
1808 /* We will extract files later, after sotrint
1809 */
1810 rc_t rc = stored_file_add ( eb -> wek, src, eb -> cdir );
1811 if ( rc != 0 ) {
1812 pLogErr (klogErr, rc, "something is wrong '$(fname)'", "fname=%s", src -> dad . name );
1813 exit ( 4 );
1814 }
1815
1816 return 0;
1817 }
1818
1819 static
store_extracted_file(stored_file * sf,const extract_block * eb)1820 rc_t store_extracted_file ( stored_file * sf, const extract_block * eb )
1821 {
1822 KFile *dst;
1823 char *buffer;
1824 size_t num_writ = 0, num_read = 0, total = 0;
1825 size_t bsize = 256 * 1024 *1024;
1826
1827 rc_t rc = KDirectoryCreateFile ( sf -> cdir, &dst, false, 0200,
1828 kcmCreate, "%s", SF_SE(sf,name) );
1829 if ( rc != 0 )
1830 {
1831 pLogErr (klogErr, rc, "failed extract to file '$(fname)'", "fname=%s", SF_SE(sf,name) );
1832 exit ( 4 );
1833 }
1834
1835
1836 buffer = malloc ( bsize );
1837 if ( buffer == NULL )
1838 {
1839 rc = RC ( rcExe, rcFile, rcAllocating, rcMemory, rcExhausted );
1840 pLogErr (klogErr, rc, "failed to allocate '$(mem)'", "mem=%zu", bsize );
1841 exit ( 4 );
1842 }
1843
1844 for ( total = 0; total < SF_SF(sf,byte_size); total += num_read )
1845 {
1846 size_t to_read = SF_SF(sf,byte_size) - total;
1847 if ( to_read > bsize )
1848 to_read = bsize;
1849
1850 rc = KFileReadAll ( eb -> archive, SF_SF(sf,byte_offset) + eb -> extract_pos + total,
1851 buffer, to_read, &num_read );
1852 if ( rc != 0 )
1853 {
1854 pLogErr (klogErr, rc, "failed to read from archive '$(fname)'", "fname=%s", SF_SE(sf,name) );
1855 exit ( 4 );
1856 }
1857
1858 if ( num_read == 0 && to_read != 0 ) {
1859 /* we reached end of file, and we still need more data
1860 */
1861 pLogErr (klogErr, rc, "end of file reached while reading from archive '$(fname)'", "fname=%s", SF_SE(sf,name) );
1862 exit ( 4 );
1863 }
1864
1865 rc = KFileWriteAll ( dst, total, buffer, num_read, &num_writ );
1866 if ( rc != 0 )
1867 {
1868 pLogErr (klogErr, rc, "failed to write to file '$(fname)'", "fname=%s", SF_SE(sf,name) );
1869 exit ( 4 );
1870 }
1871
1872 if ( num_writ < num_read )
1873 {
1874 rc = RC ( rcExe, rcFile, rcWriting, rcTransfer, rcIncomplete );
1875 pLogErr (klogErr, rc, "failed to write to file '$(fname)'", "fname=%s", SF_SE(sf,name) );
1876 exit ( 4 );
1877 }
1878 }
1879
1880 KFileRelease ( dst );
1881
1882 free ( buffer );
1883
1884 return rc;
1885 } /* store_extracted_file () */
1886
1887 int64_t CC
store_extracted_files_comparator(const void * l,const void * r,void * data)1888 store_extracted_files_comparator (
1889 const void * l,
1890 const void * r,
1891 void * data
1892 )
1893 {
1894 stored_file * sl = * ( stored_file ** ) l;
1895 stored_file * sr = * ( stored_file ** ) r;
1896
1897 return SF_SF(sl,byte_offset) - SF_SF(sr,byte_offset);
1898 } /* store_extracted_files_comparator () */
1899
1900 static
store_extracted_files(const extract_block * eb)1901 rc_t store_extracted_files ( const extract_block * eb )
1902 {
1903 rc_t rc = 0;
1904
1905 KARWek * wek = eb -> wek;
1906
1907 ksort (
1908 kar_wek_data ( wek ),
1909 kar_wek_size ( wek ),
1910 sizeof ( stored_file * ),
1911 store_extracted_files_comparator,
1912 NULL
1913 );
1914
1915 for ( size_t llp = 0; llp < kar_wek_size ( wek ); llp ++ ) {
1916 stored_file * sf = ( stored_file * ) kar_wek_get ( wek, llp );
1917 rc = store_extracted_file ( sf, eb );
1918 if ( rc != 0 ) {
1919 pLogErr (klogErr, rc, "failed to store extracted files", "" );
1920 exit ( 4 );
1921 }
1922 }
1923
1924 return rc;
1925 } /* store_extracted_files () */
1926
1927 static
extract_dir(const KARDir * src,const extract_block * eb)1928 rc_t extract_dir ( const KARDir *src, const extract_block *eb )
1929 {
1930 rc_t rc;
1931
1932 STATUS ( STAT_QA, "extracting dir: %s", src -> dad . name );
1933 rc = KDirectoryCreateDir ( eb -> cdir, 0700, kcmCreate, "%s", src -> dad . name );
1934 if ( rc == 0 )
1935 {
1936 extract_block c_eb = *eb;
1937 rc = KDirectoryOpenDirUpdate ( eb -> cdir, &c_eb . cdir, false, "%s", src -> dad . name );
1938 if ( rc == 0 )
1939 {
1940 BSTreeDoUntil ( &src -> contents, false, kar_extract, &c_eb );
1941
1942 rc = c_eb . rc;
1943
1944 KDirectoryRelease ( c_eb . cdir );
1945 }
1946 }
1947 return rc;
1948 }
1949
1950 static
extract_alias(const KARAlias * src,const extract_block * eb)1951 rc_t extract_alias ( const KARAlias *src, const extract_block *eb )
1952 {
1953 return KDirectoryCreateAlias ( eb -> cdir, 0777, kcmCreate, src -> link, src -> dad . name );
1954 }
1955
1956 static
kar_extract(BSTNode * node,void * data)1957 bool CC kar_extract ( BSTNode *node, void *data )
1958 {
1959 const KAREntry *entry = ( KAREntry * ) node;
1960 extract_block *eb = ( extract_block * ) data;
1961 eb -> rc = 0;
1962 STATUS ( STAT_QA, "Entry to extract: %s", entry -> name );
1963
1964 switch ( entry -> type )
1965 {
1966 case kptFile:
1967 eb -> rc = extract_file ( ( const KARFile * ) entry, eb );
1968 break;
1969 case kptDir:
1970 eb -> rc = extract_dir ( ( const KARDir * ) entry, eb );
1971 break;
1972 case kptAlias:
1973 case kptFile | kptAlias:
1974 case kptDir | kptAlias:
1975 eb -> rc = extract_alias ( ( const KARAlias * ) entry, eb );
1976 if ( eb -> rc != 0 )
1977 return true;
1978 else
1979 return false;
1980 /* TBD - need to mdify the timestamp of the symlink without dereferencing using lutimes - requires library code handling*/
1981 /* should not get down below to setaccess or setdate */
1982 break;
1983 default:
1984 break;
1985 }
1986
1987 if ( eb -> rc != 0 )
1988 return true;
1989
1990 return false;
1991 }
1992
1993 static bool CC kar_set_attributes ( BSTNode *node, void *data );
1994
1995 static
set_attributes_dir(const KARDir * src,const extract_block * eb)1996 rc_t set_attributes_dir ( const KARDir *src, const extract_block *eb )
1997 {
1998 rc_t rc;
1999
2000 STATUS ( STAT_QA, "set attributes dir: %s", src -> dad . name );
2001 extract_block c_eb = *eb;
2002 rc = KDirectoryOpenDirUpdate ( eb -> cdir, &c_eb . cdir, false, "%s", src -> dad . name );
2003 if ( rc == 0 )
2004 {
2005 BSTreeDoUntil ( &src -> contents, false, kar_set_attributes, &c_eb );
2006
2007 rc = c_eb . rc;
2008
2009 KDirectoryRelease ( c_eb . cdir );
2010 }
2011 return rc;
2012 }
2013
2014 static
kar_set_attributes(BSTNode * node,void * data)2015 bool CC kar_set_attributes ( BSTNode *node, void *data )
2016 {
2017 const KAREntry *entry = ( KAREntry * ) node;
2018 extract_block *eb = ( extract_block * ) data;
2019 eb -> rc = 0;
2020
2021 STATUS ( STAT_QA, "Entry to set attributes: %s", entry -> name );
2022
2023 if ( entry -> type == kptDir ) {
2024 eb -> rc = set_attributes_dir ( ( const KARDir * ) entry, eb );
2025 }
2026
2027 if ( eb -> rc == 0 )
2028 eb -> rc = KDirectorySetAccess ( eb -> cdir, false, entry -> access_mode, 0777, "%s", entry -> name );
2029 if ( eb -> rc == 0 )
2030 eb -> rc = KDirectorySetDate ( eb -> cdir, false, entry -> mod_time, "%s", entry -> name );
2031
2032 if ( eb -> rc != 0 )
2033 return true;
2034
2035 return false;
2036 } /* kar_set_attributes () */
2037
2038
2039 rc_t kar_open_file_read (
2040 struct KDirectory * Dir,
2041 const struct KFile ** File,
2042 const char * PathOrAccession
2043 );
2044
2045 static
kar_test_extract(const Params * p)2046 rc_t kar_test_extract ( const Params *p )
2047 {
2048 rc_t rc;
2049
2050 KDirectory *wd;
2051
2052 STSMSG (1, ("Extracting kar\n"));
2053
2054 rc = KDirectoryNativeDir ( &wd );
2055 if ( rc != 0 )
2056 LogErr ( klogInt, rc, "Failed to create working directory" );
2057 else
2058 {
2059 const KFile *archive;
2060
2061 rc = kar_open_file_read ( wd, &archive, p -> archive_path );
2062 if ( rc != 0 )
2063 LogErr ( klogInt, rc, "Failed to open archive" );
2064 else
2065 {
2066 KARDir root;
2067 BSTree *tree;
2068 KSraHeader hdr;
2069 uint64_t toc_pos, toc_size, file_offset;
2070
2071 toc_pos = kar_verify_header ( archive, &hdr );
2072 file_offset = hdr . u . v1 . file_offset;
2073 toc_size = file_offset - toc_pos;
2074
2075 memset ( & root, 0, sizeof root );
2076 root . dad . type = kptDir;
2077
2078 tree = & root . contents;
2079 BSTreeInit ( tree );
2080
2081 STATUS ( STAT_QA, "extracting toc" );
2082 rc = kar_extract_toc ( archive, tree, &toc_pos, toc_size );
2083 if ( rc == 0 )
2084 {
2085 /* find what the alias points to */
2086 BSTreeForEach ( tree, false, kar_alias_link_type, &root );
2087
2088 /* Finish test */
2089 if ( p -> x_count == 0 )
2090 {
2091 KARPrintMode kpm;
2092 STATUS ( STAT_QA, "Test Mode" );
2093
2094 kar_print_set_max_size_fw ( max_size );
2095 kar_print_set_max_offset_fw ( max_offset );
2096
2097 kpm . indent = 0;
2098
2099 if ( p -> long_list )
2100 {
2101 KOutMsg ( "TypeAccess Size Offset ModDateTime Path Name\n" );
2102 kpm . pm = pm_longlist;
2103 }
2104 else
2105 kpm . pm = pm_normal;
2106
2107 BSTreeForEach ( tree, false, kar_print, &kpm );
2108 }
2109 else
2110 {
2111 extract_block eb;
2112 /* begin extracting */
2113 STATUS ( STAT_QA, "Extract Mode" );
2114 eb . archive = archive;
2115 eb . extract_pos = file_offset;
2116 eb . rc = 0;
2117
2118 rc = kar_wek_make (
2119 & ( eb . wek ),
2120 256,
2121 16,
2122 ( void (*)(void *) ) stored_file_dispose
2123 );
2124 if ( rc == 0 )
2125 {
2126
2127 STATUS ( STAT_QA, "creating directory from path: %s", p -> directory_path );
2128 rc = KDirectoryCreateDir ( wd, 0777, kcmInit, "%s", p -> directory_path );
2129 if ( rc == 0 )
2130 {
2131 STATUS ( STAT_QA, "opening directory" );
2132 rc = KDirectoryOpenDirUpdate ( wd, &eb . cdir, false, "%s", p -> directory_path );
2133 if ( rc == 0 )
2134 {
2135 /* Extracting directories
2136 */
2137 BSTreeDoUntil ( tree, false, kar_extract, &eb );
2138 rc = eb . rc;
2139
2140 if ( rc == 0 ) {
2141 /* Writing files
2142 */
2143 rc = store_extracted_files ( & eb );
2144
2145 if ( rc == 0 ) {
2146 /* Setting attributes
2147 */
2148 BSTreeDoUntil ( tree, false, kar_set_attributes, &eb );
2149 rc = eb . rc;
2150 }
2151 }
2152 }
2153
2154 KDirectoryRelease ( eb . cdir );
2155 }
2156
2157 kar_wek_dispose ( eb . wek );
2158 }
2159 }
2160 }
2161
2162 BSTreeWhack ( tree, kar_entry_whack, NULL );
2163 KFileRelease ( archive );
2164 }
2165
2166 KDirectoryRelease ( wd );
2167 }
2168
2169 return rc;
2170 }
2171
2172 /*******************************************************************************
2173 * Startup
2174 */
2175
2176 static
run(const Params * p)2177 rc_t run ( const Params *p )
2178 {
2179 if ( p -> c_count != 0 )
2180 return kar_create ( p );
2181
2182 if ( p -> x_count != 0 )
2183 return kar_test_extract ( p );
2184
2185 assert ( p -> t_count != 0 );
2186 return kar_test_extract ( p );
2187 }
2188
KMain(int argc,char * argv[])2189 rc_t CC KMain ( int argc, char *argv [] )
2190 {
2191 Params params;
2192 struct Args * args;
2193
2194 rc_t rc = parse_params ( ¶ms, & args, argc, argv );
2195 if ( rc == 0 )
2196 {
2197 rc = run ( ¶ms );
2198
2199 whack_params ( & params );
2200
2201 ArgsWhack ( args );
2202 }
2203
2204 if ( rc == 0 )
2205 STSMSG (1, ("Success: Exiting kar\n"));
2206
2207 return rc;
2208 }
2209
2210