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