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 "shared.h"
28 
29 #include <klib/defs.h>
30 #include <klib/callconv.h>
31 
32 #include <klib/rc.h>
33 #include <klib/out.h>
34 #include <klib/status.h>
35 #include <klib/log.h>
36 #include <klib/debug.h> /* DBGMSG */
37 #include <klib/status.h>
38 #include <klib/text.h>
39 #include <klib/printf.h>
40 #include <klib/namelist.h>
41 
42 #include <kfs/defs.h>
43 #include <kfs/file.h>
44 #include <kfs/directory.h>
45 #include <kfs/sra.h>
46 #include <kfs/lockfile.h>
47 #include <kfs/cacheteefile.h>
48 #include <kfs/buffile.h>
49 #include <vfs/manager.h>
50 
51 #include <krypto/key.h>
52 #include <krypto/encfile.h>
53 #include <krypto/wgaencrypt.h>
54 
55 #include <kapp/args.h>
56 #include <kapp/main.h>
57 
58 #include <kfg/config.h> /* KConfigSetNgcFile */
59 
60 #include <assert.h>
61 #include <string.h>
62 #include <stdint.h>
63 
64 #ifndef RIGOROUS_SRA_CHECK
65 #define RIGOROUS_SRA_CHECK 0
66 #endif
67 
68 #define OPTION_FORCE   "force"
69 #define OPTION_SRA     "decrypt-sra-files"
70 #define ALIAS_FORCE    "f"
71 #define ALIAS_SRA      NULL
72 
73 #define MY_MAX_PATH        4096
74 
75 
76 bool ForceFlag = false;
77 bool TmpFoundFlag = false;
78 bool UseStdin = false;
79 bool UseStdout = false;
80 bool IsArchive = false;
81 
82 /* for wga decrypt */
83 char Password [4096 + 2];
84 size_t PasswordSize;
85 
86 /* for encfile encrypt/decrypt */
87 KKey Key;
88 
89 const char * ForceUsage[] =
90 { "Force overwrite of existing files", NULL };
91 
92 const char * NgcUsage[] = { "PATH to ngc file", NULL };
93 
94 /*
95  * option  control flags
96  */
97 
98 const char EncExt[] = ".ncbi_enc";
99 static const char TmpExt[] = ".vdb-decrypt-tmp";
100 static const char TmpLockExt[] = ".vdb-decrypt-tmp.lock";
101 #if 0
102 static const char CacheExt[] = ".cache";
103 static const char CacheLockExt[] = ".cache.lock";
104 #endif
105 
106 /* Usage
107  */
UsageSummary(const char * progname)108 rc_t CC UsageSummary (const char * progname)
109 {
110     rc_t rc;
111     {
112         rc = KOutMsg (
113             /*345679012345678901234567890123456789012345678901234567890123456789012345678*/
114             "\n"
115             "Usage:\n"
116             "  %s [options] <source-file>\n"
117             "  %s [options] <source-file> <destination-file>\n"
118             "  %s [options] <source-file> <destination-directory>\n"
119             "  %s [options] <directory>\n",
120             progname, progname, progname, progname);
121     }
122 #if DIRECTORY_TO_DIRECTORY_SUPPORTED
123     if (rc == 0)
124         rc = KOutMsg (
125             "  %s [options] <source-directory> <destination-directory>\n",
126             progname);
127 #endif
128         if (rc == 0)
129     {
130         rc = KOutMsg (
131             "\n"
132             "Summary:\n"
133             "  %scrypt a file or all the files (recursively) in a directory\n\n",
134             De);
135     }
136     return rc;
137 }
138 
Usage(const Args * args)139 rc_t CC Usage (const Args * args)
140 {
141     const char * progname = UsageDefaultName;
142     const char * fullpath = UsageDefaultName;
143     const char * pline[] = {
144         "file to encrypt", NULL,
145         "name of resulting file", NULL,
146         "directory of resulting file", NULL,
147         "directory to encrypt", NULL
148     };
149 
150     rc_t rc, orc;
151 
152     /* super-fragilistic molti-hacki-docious
153        let's find a better way to reuse things. */
154     if ( de [ 0 ] == 'd' )
155     {
156         pline [ 0 ] = "file to decrypt";
157         pline [ 6 ] = "directory to decrypt";
158     }
159 
160     if (args == NULL)
161         rc = RC (rcApp, rcArgv, rcAccessing, rcSelf, rcNull);
162     else
163         rc = ArgsProgram (args, &fullpath, &progname);
164 
165     orc = UsageSummary (progname);
166     if (rc == 0)
167         rc = orc;
168 
169     KOutMsg ("Parameters:\n");
170     HelpParamLine ("source-file"          , pline);
171     HelpParamLine ("destination-file"     , pline + 2);
172     HelpParamLine ("destination-directory", pline + 4);
173     HelpParamLine ("directory"            , pline + 6);
174     KOutMsg ("\nOptions:\n");
175     HelpOptionLine (ALIAS_FORCE, OPTION_FORCE, NULL, ForceUsage);
176     CryptOptionLines ();
177     HelpOptionLine(ALIAS_NGC, OPTION_NGC, "PATH", NgcUsage);
178     KOutMsg("\n");
179     HelpOptionsStandard ();
180 
181     /* forcing editor alignment */
182     /*   12345678901234567890123456789012345678901234567890123456789012345678901234567890*/
183     KOutMsg (
184         "\n"
185         "Details:\n"
186         "  All %scryptions are non-destructive until successful. No files are deleted or\n"
187         "  replaced until the %scryptions are complete.\n"
188         "\n", de, de);
189 
190     KOutMsg (
191         "  The extension '.ncbi_enc' will be %s when a file is %scrypted.\n"
192         "\n", Decrypting ? "removed" : "added", de);
193 
194     if (Decrypting) KOutMsg (
195         "  NCBI Archive files that contain NCBI database objects will not be decrypted\n"
196         "  unless the %s option is used. As these objects can be used without\n"
197         "  decryption it is recommended they remain encrypted.\n"
198         "\n", OPTION_SRA);
199     else KOutMsg (
200         "  NCBI Archive files that contain NCBI database objects will not have the\n"
201         "  .ncbi_enc extension added.\n\n");
202 
203 
204     KOutMsg (
205         "  If the only parameter is a file name then it will be replaced by a file that\n"
206         "  is %scrypted with a possible changed extension.\n"
207         "  \n", de);
208 
209     KOutMsg (
210         "  If the only parameter is a directory, all files in that directory including\n"
211         "  all files in subdirectories will be replaced with a possible change\n"
212         "  in the extension.\n"
213         "\n");
214 
215     KOutMsg (
216         "  If there are two parameters  a copy is made but the copy will be %scrypted.\n"
217         "  If the second parameter is a directory the new file might have a different\n"
218         "  extension. If it is not a directory, the extension will be as given in the\n"
219         "  the parameter.\n"
220         "\n", de);
221 
222     KOutMsg (
223         "  Missing directories in the destination path will be created.\n"
224         "\n");
225 
226     KOutMsg (
227         "  Already existing destination files will cause the program to end with\n"
228         "  an error and will be left unchanged unless the --%s option is used to\n"
229         "  force the files to be overwritten.\n"
230         "\n", OPTION_FORCE);
231 
232     KOutMsg (
233         "Encryption key (file password):\n"
234         "  The encryption key or file password is handled by configuration. If not yet\n"
235         "  set, this program will fail.\n\n"
236         "  Please consult configuration page at\n"
237         "  https://trace.ncbi.nlm.nih.gov/Traces/sra/sra.cgi?view=toolkit_doc&f=std or\n"
238         "  https://github.com/ncbi/sra-tools/wiki/Toolkit-Configuration\n"
239         );
240 
241     HelpVersion (fullpath, KAppVersion());
242 
243     return rc;
244 }
245 
246 
247 
248 
249 
250 /*
251  * determine the archive type for KFile f with pathname name
252  *
253  * This could be extended to handle tar files with a larger head size and
254  * and some less simple checks for a tar header block at the start of
255  * the file.
256  */
ArchiveTypeCheck(const KFile * f)257 ArcScheme ArchiveTypeCheck (const KFile * f)
258 {
259     size_t num_read;
260     rc_t rc;
261     char head [128];
262 
263     IsArchive = false;
264     rc = KFileReadAll (f, 0, head, sizeof head, &num_read);
265     if (rc)
266     {
267         LOGERR (klogErr, rc, "Unable to read head of decrypted file");
268         return arcError;
269     }
270 
271     rc = KFileIsSRA (head, num_read);
272     if (rc == 0)
273     {
274 
275 /*         OUTMSG (("+++++ ARCHIVE\n")); */
276 
277 
278         /* a hack... */
279 
280         IsArchive = true;
281         return arcSRAFile;
282     }
283 /*     OUTMSG (("----- not an archive\n")); */
284     return arcNone;
285 }
286 
287 
288 /*
289  * Copy a file from a const KFile * to a KFile * with the paths for the two
290  * for logging purposes
291  *
292  * return rc_t = 0 for success
293  * return rc_t != 0 for failure
294  */
CopyKFile(const KFile * src,KFile * dst,const char * source,const char * dest)295 rc_t CopyKFile (const KFile * src, KFile * dst, const char * source, const char * dest)
296 {
297     rc_t rc;
298     uint8_t	buff	[256 * 1024];
299     size_t	num_read;
300     size_t      num_writ;
301     uint64_t	pos;
302 
303     for (pos = 0; ; pos += num_read)
304     {
305         rc = Quitting ();
306         if (rc)
307         {
308             LOGMSG (klogFatal, "Received quit");
309             break;
310         }
311 
312         rc = KFileReadAll (src, pos, buff, sizeof (buff), &num_read);
313         if (rc)
314         {
315             PLOGERR (klogErr,
316                      (klogErr, rc,
317                       "Failed to read from file $(F) at $(P)",
318                       "F=%s,P=%lu", source, pos));
319             break;
320         }
321 
322         if (num_read == 0)
323             break;
324 
325         rc = KFileWriteAll (dst, pos, buff, num_read, &num_writ);
326         if (rc)
327         {
328             PLOGERR (klogErr,
329                      (klogErr, rc,
330                       "Failed to write to file $(F) at $(P)",
331                       "F=%s,P=%lu", dest, pos));
332             break;
333         }
334 
335         if (num_writ != num_read)
336         {
337             rc = RC (rcExe, rcFile, rcWriting, rcFile, rcInsufficient);
338             PLOGERR (klogErr,
339                      (klogErr, rc,
340                       "Failed to write all to file $(F) at $(P)",
341                       "F=%s,P=%lu", dest, pos));
342             break;
343         }
344     }
345     return rc;
346 }
347 
348 
349 /*
350  * determine the encryption type for KFile f with pathname name
351  */
EncryptionTypeCheck(const KFile * f,const char * name,EncScheme * scheme)352 rc_t EncryptionTypeCheck (const KFile * f, const char * name, EncScheme * scheme)
353 {
354     size_t num_read;
355     rc_t rc;
356     char head [128];
357 
358     assert (f != NULL);
359     assert (name != NULL);
360 
361     rc = KFileReadAll (f, 0, head, sizeof head, &num_read);
362     if (rc)
363     {
364         PLOGERR (klogErr, (klogErr, rc, "Unable to read head of "
365                            "'$(F)'", "F=%s", name));
366         *scheme = encError;
367         return rc;
368     }
369 
370     /* looks for files with NCBInenc or NCBIsenc signatures */
371     rc = KFileIsEnc (head, num_read);
372     if (rc == 0)
373     {
374             /* looks for files with just NCBIsenc signatures */
375         rc = KFileIsSraEnc (head, num_read);
376 
377         *scheme = (rc == 0) ? encSraEncFile : encEncFile;
378     }
379     else
380     {
381         rc = KFileIsWGAEnc (head, num_read);
382         if (rc == 0)
383             *scheme = encWGAEncFile;
384         else
385             *scheme = encNone;
386     }
387     return 0;
388 }
389 
390 
391 /*
392  * Check a file path name for ending in the extension used by this program
393  *
394  * return true if it ends with the extension and false if it does not
395  */
396 static
IsTmpFile(const char * path)397 bool IsTmpFile (const char * path)
398 {
399     const char * pc;
400 
401     pc = strrchr (path, '.');
402     if (pc == NULL)
403         return false;
404 
405     if (strcmp (pc, TmpExt) == 0)
406         return true;
407 
408     pc = string_chr (path, pc - path, '.');
409     if (pc == NULL)
410         return false;
411 
412     return (strcmp (pc, TmpLockExt) == 0);
413 }
414 
415 #if 0
416 static
417 bool IsCacheFile (const char * path)
418 {
419     const char * pc;
420 
421     pc = strrchr (path, '.');
422     if (pc == NULL)
423         return false;
424 
425     if (strcmp (pc, CacheExt) == 0)
426         return true;
427 
428     pc = string_chr (path, pc - path, '.');
429     if (pc == NULL)
430         return false;
431 
432     return (strcmp (pc, CacheLockExt) == 0);
433 }
434 #endif
435 
436 static
FileInPlace(KDirectory * cwd,const char * leaf,bool try_rename)437 rc_t FileInPlace (KDirectory * cwd, const char * leaf, bool try_rename)
438 {
439     rc_t rc;
440     bool is_tmp;
441 
442     STSMSG (1, ("%scrypting file in place %s",De,leaf));
443 
444     rc = 0;
445     is_tmp = IsTmpFile (leaf);
446     if (is_tmp)
447     {
448         STSMSG (1, ("%s is a vdb-decrypt/vdb-encrypt temporary file and will "
449                     "be ignored", leaf));
450         TmpFoundFlag = true;
451         if (ForceFlag)
452             ; /* LOG OVERWRITE */
453         else
454             ; /* LOG TMP */
455     }
456     if (!is_tmp || ForceFlag)
457     {
458         char temp [MY_MAX_PATH];
459 
460 
461         rc = KDirectoryResolvePath (cwd, false, temp, sizeof temp, ".%s%s",
462                                     leaf, TmpExt);
463 
464         if (rc)
465             PLOGERR (klogErr, (klogErr, rc, "unable to resolve '.$(S)$(E)'",
466                                "S=%s,E=%s",leaf,TmpExt));
467         else
468         {
469             KPathType kpt;
470             uint32_t kcm;
471 
472             kcm = kcmCreate|kcmParents;
473             kpt = KDirectoryPathType (cwd, "%s", temp);
474             if (kpt != kptNotFound)
475             {
476                 /* log busy */
477                 if (ForceFlag)
478                 {
479                     kcm = kcmInit|kcmParents;
480                     /* log force */
481                     kpt = kptNotFound;
482                 }
483             }
484 
485             if (kpt == kptNotFound)
486             {
487                 const KFile * infile;
488 
489                 rc = KDirectoryOpenFileRead (cwd, &infile, "%s", leaf);
490                 if (rc)
491                     PLOGERR (klogErr, (klogErr, rc, "Unable to resolve '$(F)'",
492                                        "F=%s",leaf));
493                 else
494                 {
495                     uint64_t fz;
496                     size_t z;
497                     rc_t irc;
498                     uint64_t ignored;
499 
500                     rc = KFileSize (infile, &fz);
501                     /* ignore rc for now? yes hack */
502                     z = string_size (leaf);
503 
504                     /* vdb-decrypt and vdb-encrypt both ignore repository cache files. */
505                     irc = GetCacheTruncatedSize (infile, &ignored);
506                     if (irc == 0)
507                         STSMSG (1, ("skipping cache download file %s", leaf));
508                     else if ((fz == 0) &&
509                              (string_cmp (leaf + (z - (sizeof (".lock")-1)), sizeof (".lock"),
510                                           ".lock", sizeof (".lock") , sizeof (".lock")) == 0))
511                         STSMSG (1, ("skipping cache lock download file %s", leaf));
512                     else
513                     {
514                         EncScheme scheme;
515 
516                         rc = EncryptionTypeCheck (infile, leaf, &scheme);
517                         if (rc == 0)
518                         {
519                             ArcScheme ascheme;
520                             bool changed;
521                             bool do_this_file;
522                             char new_name [MY_MAX_PATH + sizeof EncExt];
523 
524                             do_this_file = DoThisFile (infile, scheme, &ascheme);
525                             strcpy (new_name, leaf);
526                             if (try_rename && do_this_file)
527                                 changed = NameFixUp (new_name);
528                             else
529                                 changed = false;
530 /*                         KOutMsg ("### %d \n", changed); */
531 
532                             if (!do_this_file)
533                             {
534                                 if (changed)
535                                 {
536                                     STSMSG (1, ("renaming %s to %s", leaf, new_name));
537                                     rc = KDirectoryRename (cwd, false, leaf, new_name);
538                                 }
539                                 else
540                                     STSMSG (1, ("skipping %s",leaf));
541                             }
542                             else
543                             {
544                                 KFile * outfile;
545 
546                                 rc = KDirectoryCreateExclusiveAccessFile (cwd, &outfile,
547                                                                           false, 0600, kcm,
548                                                                           "%s", temp);
549                                 if (rc == 0)
550                                 {
551                                     const KFile * Infile;
552                                     KFile * Outfile;
553 
554                                     rc = CryptFile (infile, &Infile, outfile, &Outfile, scheme);
555 
556                                     if (rc == 0)
557                                     {
558                                         STSMSG (1, ("copying %s to %s", leaf, temp));
559 
560                                         rc = CopyKFile (Infile, Outfile, leaf, temp);
561 
562                                         if (rc == 0)
563                                         {
564                                             uint32_t access;
565                                             KTime_t date;
566 
567                                             rc = KDirectoryAccess (cwd, &access, "%s", leaf);
568                                             if (rc == 0)
569                                                 rc = KDirectoryDate (cwd, &date, "%s", leaf);
570 
571                                             KFileRelease (infile);
572                                             KFileRelease (outfile);
573                                             KFileRelease (Infile);
574                                             KFileRelease (Outfile);
575 
576                                             if (rc == 0)
577                                             {
578                                                 STSMSG (1, ("renaming %s to %s", temp, new_name));
579 
580                                                 rc = KDirectoryRename (cwd, true, temp, new_name);
581                                                 if (rc)
582                                                     LOGERR (klogErr, rc, "error renaming");
583                                                 else
584                                                 {
585                                                     if (changed)
586                                                         KDirectoryRemove (cwd, false, "%s", leaf);
587 
588                                                     /*rc =*/
589                                                     KDirectorySetAccess (cwd, false, access,
590                                                                          0777, "%s", new_name);
591                                                     KDirectorySetDate (cwd, false, date, "%s", new_name);
592                                                     /* gonna ignore an error here I think */
593                                                     return rc;
594                                                 }
595                                             }
596                                         }
597                                     }
598                                     KFileRelease (outfile);
599                                 }
600                             }
601                         }
602                     }
603                     KFileRelease (infile);
604                 }
605             }
606         }
607     }
608     return rc;
609 }
610 
611 
612 static
FileToFile(const KDirectory * sd,const char * source,KDirectory * dd,const char * dest_,bool try_rename,char * base)613 rc_t FileToFile (const KDirectory * sd, const char * source,
614                  KDirectory *dd, const char * dest_, bool try_rename,
615                  char * base)
616 {
617     const KFile * infile;
618     rc_t rc;
619     uint32_t access;
620     KTime_t date;
621     bool is_tmp;
622     char dest [MY_MAX_PATH + sizeof EncExt];
623 
624     strcpy (dest, dest_);
625     if (try_rename)
626         NameFixUp (dest);
627 
628     if ((sd == dd) && (strcmp (source, dest) == 0))
629         return FileInPlace (dd, dest, try_rename);
630 
631     if (base == NULL)
632         STSMSG (1, ("%scrypting file %s to %s", De, source, dest));
633     else
634         STSMSG (1, ("%scrypting file %s to %s/%s", De, source, base, dest));
635 
636     /*
637      * A Hack to make stdin/stout work within KFS
638      */
639     if (UseStdin)
640     {
641         const KFile * iinfile;
642         rc = KFileMakeStdIn (&iinfile);
643         if (rc == 0)
644         {
645             rc = KBufReadFileMakeRead (&infile, iinfile, 64 * 1024);
646             KFileRelease (iinfile);
647             if (rc == 0)
648             {
649                 access = 0640;
650                 date = 0;
651                 goto stdin_shortcut;
652             }
653             LOGERR (klogErr, rc, "error wrapping stdin");
654             return rc;
655         }
656     }
657     rc = 0;
658     is_tmp = IsTmpFile (source);
659 
660     if (is_tmp)
661     {
662         TmpFoundFlag = true;
663         if (ForceFlag)
664             ; /* LOG OVERWRITE */
665         else
666                 ; /* LOG TMP */
667     }
668     if (!is_tmp || ForceFlag)
669     {
670         rc = KDirectoryAccess (sd, &access, "%s", source);
671         if (rc)
672             LOGERR (klogErr, rc, "Error check permission of source");
673 
674         else
675         {
676             rc = KDirectoryDate (sd, &date, "%s", source);
677             if (rc)
678                 LOGERR (klogErr, rc, "Error check date of source");
679 
680             else
681             {
682                 rc = KDirectoryOpenFileRead (sd, &infile, "%s", source);
683                 if (rc)
684                     PLOGERR (klogErr, (klogErr, rc,
685                                        "Error opening source file '$(S)'",
686                                        "S=%s", source));
687                 else
688                 {
689                     EncScheme scheme;
690 
691                 stdin_shortcut:
692                     rc = EncryptionTypeCheck (infile, source, &scheme);
693                     if (rc == 0)
694                     {
695                         KFile * outfile;
696                         uint32_t kcm;
697 
698                         /*
699                          * Hack to support stdout before VFS is complete enough to use here
700                          */
701                         if (UseStdout)
702                         {
703                             rc = KFileMakeStdOut (&outfile);
704                             if (rc)
705                                 LOGERR (klogErr, rc, "error wrapping stdout");
706                         }
707                         else
708                         {
709                             kcm = ForceFlag ? kcmInit|kcmParents : kcmCreate|kcmParents;
710 
711                             rc = KDirectoryCreateFile (dd, &outfile, false, 0600, kcm, "%s", dest);
712                             if (rc)
713                                 PLOGERR (klogErr,(klogErr, rc, "error opening output '$(O)'",
714                                                   "O=%s", dest));
715                         }
716                         if (rc == 0)
717                         {
718                             const KFile * Infile;
719                             KFile * Outfile;
720 
721                             rc = CryptFile (infile, &Infile, outfile, &Outfile, scheme);
722                             if (rc == 0)
723                             {
724                                 rc = CopyKFile (Infile, Outfile, source, dest);
725                                 if (rc == 0)
726                                 {
727                                     if (UseStdin || UseStdout)
728                                         ;
729                                     else
730                                     {
731                                         rc = KDirectorySetAccess (dd, false, access, 0777,
732                                                                   "%s", dest);
733 
734                                         if (rc == 0 && date != 0)
735                                             rc = KDirectorySetDate (dd, false, date, "%s", dest);
736                                     }
737                                 }
738                                 KFileRelease (Infile);
739                                 KFileRelease (Outfile);
740                             }
741                             KFileRelease (outfile);
742                         }
743                     }
744                     KFileRelease (infile);
745                 }
746             }
747         }
748     }
749     return rc;
750 }
751 
752 
753 static
DoDir(const KDirectory * sd,KDirectory * dd)754 rc_t DoDir (const KDirectory * sd, KDirectory * dd)
755 {
756     KNamelist * names;
757     rc_t rc;
758 
759     rc = KDirectoryList (sd, &names, NULL, NULL, ".");
760     if (rc)
761         ;
762     else
763     {
764         uint32_t count;
765 
766         rc = KNamelistCount (names, &count);
767         if (rc)
768             ;
769         else
770         {
771             uint32_t idx;
772 
773             for (idx = 0; idx < count; ++idx)
774             {
775                 const char * name;
776 
777                 rc = KNamelistGet (names, idx, &name);
778                 if (rc)
779                     ;
780                 else
781                 {
782                     const KDirectory * nsd;
783                     KDirectory * ndd;
784                     KPathType kpt;
785 
786                     kpt = KDirectoryPathType (sd, "%s", name);
787 
788                     switch (kpt)
789                     {
790                     default:
791                         break;
792 
793                     case kptFile:
794                         if (sd == dd)
795                             rc = FileInPlace (dd, name, true);
796                         else
797                             rc = FileToFile (sd, name, dd, name, true, NULL);
798                         break;
799 
800                     case kptDir:
801                         if (sd == dd)
802                         {
803                             rc = KDirectoryOpenDirUpdate (dd, &ndd, false, "%s", name);
804                             if (rc)
805                                 ;
806                             else
807                             {
808                                 /* RECURSION */
809                                 STSMSG (1, ("%scrypting directory %s", De, name));
810                                 rc = DoDir (ndd, ndd);
811                                 STSMSG (1, ("done with directory %s", name));
812                                 KDirectoryRelease (ndd);
813                             }
814                         }
815                         else
816                         {
817                             rc = KDirectoryOpenDirRead (sd, &nsd, false, "%s", name);
818                             if (rc)
819                                 ;
820                             else
821                             {
822                                 rc = KDirectoryCreateDir (dd, 0600, kcmOpen, "%s", name);
823                                 if (rc)
824                                     ;
825                                 else
826                                 {
827                                     rc = KDirectoryOpenDirUpdate (dd, &ndd, false, "%s", name);
828                                     if (rc)
829                                         ;
830                                     else
831                                     {
832                                         /* RECURSION */
833                                         STSMSG (1, ("%scrypting directory %s", De, name));
834                                         rc = DoDir (nsd, ndd);
835                                         STSMSG (1, ("done with directory %s", name));
836 
837                                         KDirectoryRelease (ndd);
838                                     }
839                                 }
840                                 KDirectoryRelease (nsd);
841                             }
842                         }
843                         break;
844                     }
845                 }
846             }
847         }
848         KNamelistRelease (names);
849     }
850     return rc;
851 }
852 
853 
854 static
Start(KDirectory * cwd,const char * src,const char * dst)855 rc_t Start (KDirectory * cwd, const char * src, const char * dst)
856 {
857     KPathType dtype;
858     KPathType stype;
859     char dpath [MY_MAX_PATH];
860     char spath [MY_MAX_PATH];
861     rc_t rc;
862     bool using_stdin, using_stdout, try_rename;
863 
864     /* limited anti oops checks */
865     try_rename = (dst == NULL);
866     if (!try_rename)
867     {
868         /* try to prevent file to file clash */
869         if (strcmp (src,dst) == 0)
870             dst = NULL;
871 
872         /* try to prevent file to dir clash */
873         else
874         {
875             size_t s,d;
876 
877             s = string_size (src);
878             d = string_size (dst);
879 
880             if (s > d)
881             {
882                 if (string_cmp (src, s, dst, d, d) == 0)
883                 {
884                     if ((strchr (src+d+1, '/') == NULL) &&
885                         ((src[d] == '/') ||
886                          (src[d-1] == '/')))
887                     {
888                         try_rename = true;
889                         dst = NULL;
890                     }
891                 }
892             }
893         }
894     }
895 
896     /*
897      * This is a quick fix "hack"
898      * A fully built out VFS should replace the KFS in use and eliminate this
899      */
900     using_stdin = (strcmp (src, "/dev/stdin") == 0);
901 
902     if (using_stdin)
903     {
904         if (dst == NULL)
905         {
906             rc = RC (rcExe, rcArgv, rcParsing, rcParam, rcNull);
907             LOGERR (klogErr, rc, "Unable to handle stdin in place");
908             return rc;
909         }
910         stype = kptFile;
911         strcpy (spath, src);
912         UseStdin = true;
913         STSMSG (1, ("reading console / stdin as input"));
914         goto stdin_shortcut;
915     }
916 
917     rc = KDirectoryResolvePath (cwd, false, spath, sizeof spath, "%s", src);
918     if (rc)
919     {
920         LOGERR (klogErr, rc, "can't resolve source");
921         return rc;
922     }
923 
924     stype = KDirectoryPathType (cwd, "%s", spath) & ~ kptAlias;
925 
926     switch (stype)
927     {
928     case kptNotFound:
929         rc = RC (rcExe, rcArgv, rcResolving, rcPath, rcNotFound);
930         break;
931 
932     default:
933     case kptBadPath:
934         rc = RC (rcExe, rcArgv, rcResolving, rcPath, rcInvalid);
935         break;
936 
937     case kptCharDev:
938     case kptBlockDev:
939     case kptFIFO:
940     case kptZombieFile:
941     case kptDataset:
942     case kptDatatype:
943         rc = RC (rcExe, rcArgv, rcResolving, rcPath, rcIncorrect);
944         break;
945 
946     case kptFile:
947     case kptDir:
948         break;
949     }
950     if (rc)
951     {
952         PLOGERR (klogErr, (klogErr, rc, "can not use source '$(S)'", "S=%s", src));
953         return rc;
954     }
955 
956     /*
957      * In Place Operation
958      */
959     if (dst == NULL)
960     {
961 
962         /*
963          * Input is a file
964          */
965         if (stype == kptFile)
966         {
967             KDirectory * ndir;
968             char * pc;
969 
970             pc = strrchr (spath, '/');
971             if (pc == NULL)
972             {
973                 pc = spath;
974                 ndir = cwd;
975                 rc = KDirectoryAddRef (cwd);
976             }
977             else if (pc == spath)
978             {
979                 ++pc;
980                 ndir = cwd;
981                 rc = KDirectoryAddRef (cwd);
982             }
983             else
984             {
985                 *pc++ = '\0';
986                 rc = KDirectoryOpenDirUpdate (cwd, &ndir, false, "%s", spath);
987             }
988 
989             if (rc == 0)
990             {
991                 rc = FileInPlace (ndir, pc, try_rename);
992                 KDirectoryRelease (ndir);
993             }
994         }
995         /*
996          * Input is a directory
997          */
998         else
999         {
1000             KDirectory * ndir;
1001 
1002             rc = KDirectoryOpenDirUpdate (cwd, &ndir, false, "%s", spath);
1003             if (rc)
1004                 ;
1005             else
1006             {
1007                 STSMSG (1, ("%scrypting directory %s", De, spath));
1008                 rc = DoDir (ndir, ndir);
1009                 STSMSG (1, ("done with directory %s", spath));
1010                 KDirectoryRelease (ndir);
1011             }
1012         }
1013     }
1014 
1015     /*
1016      * 'Copy' Operation
1017      */
1018     else
1019     {
1020     stdin_shortcut:
1021         using_stdout = (strcmp (dst, "/dev/stdout") == 0);
1022         if (using_stdout == true)
1023         {
1024             dtype = kptFile;
1025             strcpy (dpath, dst);
1026             UseStdout = true;
1027             STSMSG (1, ("writing console / stdout as output"));
1028             goto do_file;
1029         }
1030         rc = KDirectoryResolvePath (cwd, false, dpath, sizeof dpath, "%s", dst);
1031         if (rc)
1032         {
1033             LOGERR (klogErr, rc, "can't resolve destination");
1034             return rc;
1035         }
1036         dtype = KDirectoryPathType (cwd, "%s", dpath) & ~ kptAlias;
1037         switch (dtype)
1038         {
1039         default:
1040         case kptBadPath:
1041             rc = RC (rcExe, rcArgv, rcResolving, rcPath, rcInvalid);
1042             PLOGERR (klogErr, (klogErr, rc, "can not use destination  '$(S)'", "S=%s", dst));
1043             break;
1044 
1045         case kptCharDev:
1046         case kptBlockDev:
1047         case kptFIFO:
1048         case kptZombieFile:
1049         case kptDataset:
1050         case kptDatatype:
1051             rc = RC (rcExe, rcArgv, rcResolving, rcPath, rcIncorrect);
1052             PLOGERR (klogErr, (klogErr, rc, "can not use destination parameter '$(S)'", "S=%s", dst));
1053             break;
1054 
1055         case kptNotFound:
1056         {
1057             size_t z;
1058 
1059             z = strlen (dst) - 1;
1060             if ((dst[z] == '/') || (stype == kptDir))
1061                 goto do_dir;
1062             else
1063                 goto do_file;
1064         }
1065 
1066         case kptFile:
1067             if (!ForceFlag)
1068             {
1069                 rc = RC (rcExe, rcArgv, rcParsing, rcFile, rcExists);
1070                 PLOGERR (klogErr, (klogErr, rc, "can not over-write '$(F)' without --force",
1071                                    "F=%s", dpath));
1072                 break;
1073             }
1074         do_file:
1075             if (stype == kptFile)
1076             {
1077                 rc = FileToFile (cwd, spath, cwd, dpath, try_rename, NULL);
1078             }
1079             else
1080             {
1081                 rc = RC (rcExe, rcArgv, rcResolving, rcPath, rcIncorrect);
1082                 LOGERR (klogErr, rc, "Can't do directory to file");
1083             }
1084             break;
1085 
1086         do_dir:
1087         case kptDir:
1088             /*
1089              * Input is a directory
1090              */
1091             if (stype == kptDir)
1092             {
1093 #if DIRECTORY_TO_DIRECTORY_SUPPORTED
1094                 const KDirectory * sdir;
1095                 KDirectory * ddir;
1096 
1097                 rc = KDirectoryOpenDirRead (cwd, &sdir, false, "%s", spath);
1098                 if (rc)
1099                     ;
1100                 else
1101                 {
1102                     if (dtype == kptNotFound)
1103                     {
1104                         STSMSG (1, ("creating output directory %s", dpath));
1105                         rc = KDirectoryCreateDir (cwd, 0775, kcmCreate|kcmParents,
1106                                                   "%s", dpath);
1107                     }
1108                     if (rc == 0)
1109                     {
1110                         rc = KDirectoryOpenDirUpdate (cwd, &ddir, false, "%s", dpath);
1111                         if (rc)
1112                             ;
1113                         else
1114                         {
1115                             STSMSG (1, ("%scrypting directory %s to %s", De, spath, dpath));
1116                             rc = DoDir (sdir, ddir);
1117                             STSMSG (1, ("done with directory %s to %s", spath, dpath));
1118                             KDirectoryRelease (ddir);
1119                         }
1120                     }
1121                     KDirectoryRelease (sdir);
1122                 }
1123 #else
1124                 rc = RC (rcExe, rcArgv, rcResolving, rcPath, rcIncorrect);
1125                 LOGERR (klogErr, rc, "Can't do directory to directory");
1126 #endif
1127             }
1128             /*
1129              * Input is a file
1130              */
1131             else
1132             {
1133                 KDirectory * ndir;
1134                 const char * pc;
1135 
1136                 if (dtype == kptNotFound)
1137                 {
1138                     STSMSG (1, ("creating output directory %s", dpath));
1139                     rc = KDirectoryCreateDir (cwd, 0775, kcmCreate|kcmParents,
1140                                               "%s", dpath);
1141                 }
1142                 if (rc == 0)
1143                 {
1144 
1145                     STSMSG (1, ("opening output directory %s", dpath));
1146                     rc = KDirectoryOpenDirUpdate (cwd, &ndir, false, "%s", dpath);
1147                     if (rc)
1148                         ;
1149                     else
1150                     {
1151                         pc = strrchr (spath, '/');
1152                         if (pc == NULL)
1153                             pc = spath;
1154                         else
1155                             ++pc;
1156 
1157                         rc = FileToFile (cwd, spath, ndir, pc, true, dpath);
1158 
1159                         KDirectoryRelease (ndir);
1160                     }
1161                 }
1162             }
1163             break;
1164         }
1165     }
1166     return rc;
1167 }
1168 
1169 
1170 static
StartFileSystem(const char * src,const char * dst)1171 rc_t StartFileSystem (const char * src, const char * dst)
1172 {
1173     VFSManager * vmanager;
1174     rc_t rc;
1175 
1176     rc = VFSManagerMake (&vmanager);
1177     if (rc)
1178         LOGERR (klogErr, rc, "Failed to open file system");
1179 
1180     else
1181     {
1182         rc = VFSManagerGetKryptoPassword (vmanager, Password, sizeof Password,
1183                                           &PasswordSize);
1184         if (rc != 0)
1185             LOGERR (klogErr, rc, "unable to obtain a password");
1186 
1187         else
1188         {
1189             rc = KKeyInitRead (&Key, kkeyAES128, Password, PasswordSize);
1190             if (rc)
1191                 LOGERR (klogErr, rc, "Unable to make encryption/decryption key");
1192 
1193             else
1194             {
1195                 KDirectory * cwd;
1196 
1197                 rc = VFSManagerGetCWD (vmanager, &cwd);
1198                 if (rc)
1199                     LOGERR (klogInt, rc, "unable to access current directory");
1200 
1201                 else
1202                 {
1203                     rc = Start (cwd, src, dst);
1204 
1205                     KDirectoryRelease (cwd);
1206                 }
1207             }
1208         }
1209         VFSManagerRelease (vmanager);
1210     }
1211     return rc;
1212 }
1213 
1214 
CommonMain(Args * args)1215 rc_t CommonMain (Args * args)
1216 {
1217     rc_t rc;
1218     uint32_t ocount; /* we take the address of ocount but not pcount. */
1219     uint32_t pcount; /* does that help the compiler optimize? */
1220 
1221     rc = ArgsParamCount (args, &ocount);
1222     if (rc)
1223         LOGERR (klogInt, rc, "failed to count parameters");
1224 
1225     else if ((pcount = ocount) == 0)
1226         MiniUsage (args);
1227 
1228     else if (pcount > 2)
1229     {
1230         LOGERR (klogErr, rc, "too many parameters");
1231         MiniUsage(args);
1232     }
1233 
1234     else
1235     {
1236         const char * dst; /* we only take the address of one of these */
1237         const char * src;
1238 
1239         rc = ArgsOptionCount (args, OPTION_FORCE, &ocount);
1240         if (rc)
1241             LOGERR (klogInt, rc, "failed to examine force option");
1242 
1243         else
1244         {
1245             ForceFlag = (ocount > 0);
1246 
1247             /* -----
1248              * letting comp put src in register
1249              * only if it wants
1250              */
1251             rc = ArgsParamValue (args, 0, (const void **)&dst);
1252             if (rc)
1253                 LOGERR (klogInt, rc, "Failure to fetch "
1254                         "source parameter");
1255 
1256             else
1257             {
1258                 src = dst;
1259 
1260                 if (pcount == 1)
1261                     dst = NULL;
1262 
1263                 else
1264                 {
1265                     rc = ArgsParamValue (args, 1, (const void **)&dst);
1266                     if (rc)
1267                         LOGERR (klogInt, rc, "Failure to fetch "
1268                                 "destination parameter");
1269                 }
1270 
1271 /* OPTION_NGC */
1272                 if (rc == 0) {
1273                     const char * dummy = NULL;
1274                     rc = ArgsOptionCount(args, OPTION_NGC, &pcount);
1275                     if (rc != 0)
1276                         LOGERR(klogErr, rc,
1277                             "Failure to get '" OPTION_NGC "' argument");
1278                     else if (pcount != 0) {
1279                         rc = ArgsOptionValue(args, OPTION_NGC, 0,
1280                             (const void **)&dummy);
1281                         if (rc != 0)
1282                             LOGERR(klogErr, rc,
1283                                 "Failure to get '" OPTION_NGC "' argument");
1284                         else
1285                             KConfigSetNgcFile(dummy);
1286                     }
1287                 }
1288 
1289                 if (rc == 0)
1290                     rc = StartFileSystem (src, dst);
1291             }
1292         }
1293     }
1294     return rc;
1295 }
1296 
1297 /* EOF */
1298