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 ( &params, & args, argc, argv );
2195     if ( rc == 0 )
2196     {
2197         rc = run ( &params );
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