1 /*-
2  ***********************************************************************
3  *
4  * $Id: develop.c,v 1.52 2014/07/30 07:24:15 mavrik Exp $
5  *
6  ***********************************************************************
7  *
8  * Copyright 2000-2014 The FTimes Project, All Rights Reserved.
9  *
10  ***********************************************************************
11  */
12 #include "all-includes.h"
13 
14 /*-
15  ***********************************************************************
16  *
17  * Defines
18  *
19  ***********************************************************************
20  */
21 #define COMPRESS_RECOVERY_RATE 100
22 
23 static unsigned char  gaucMd5ZeroHash[MD5_HASH_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
24 static unsigned char  gaucSha1ZeroHash[SHA1_HASH_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
25 static unsigned char  gaucSha256ZeroHash[SHA256_HASH_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
26 
27 /*-
28  ***********************************************************************
29  *
30  * DevelopHaveNothingOutput
31  *
32  ***********************************************************************
33  */
34 int
DevelopHaveNothingOutput(FTIMES_PROPERTIES * psProperties,char * pcOutData,int * iWriteCount,FTIMES_FILE_DATA * psFTFileData,char * pcError)35 DevelopHaveNothingOutput(FTIMES_PROPERTIES *psProperties, char *pcOutData, int *iWriteCount, FTIMES_FILE_DATA *psFTFileData, char *pcError)
36 {
37   int                 i = 0;
38   int                 n = 0;
39   int                 m = 0;
40   int                 iMaskTableLength = MaskGetTableLength(MASK_RUNMODE_TYPE_MAP);
41   MASK_B2S_TABLE     *psMaskTable = MaskGetTableReference(MASK_RUNMODE_TYPE_MAP);
42   unsigned long       ul = 0;
43 
44   /*-
45    *********************************************************************
46    *
47    * Loop over the mask table, and output a NULL value for each field
48    * that is set in the mask. Then, update the write count and return.
49    *
50    *********************************************************************
51    */
52   for (i = 0; i < iMaskTableLength; i++)
53   {
54     ul = (1 << i);
55     if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, ul))
56     {
57 #ifdef WIN32
58       switch (ul)
59       {
60       case MAP_ATIME:
61       case MAP_MTIME:
62       case MAP_CTIME:
63       case MAP_CHTIME:
64         n += sprintf(&pcOutData[n], "||");
65         break;
66       default:
67         n += sprintf(&pcOutData[n], "|");
68         break;
69       }
70 #else
71       n += sprintf(&pcOutData[n], "|");
72 #endif
73       m += sprintf(&pcError[m], "%s%s", (i == 0) ? "" : ",", (char *) psMaskTable[i].acName);
74     }
75   }
76   n += sprintf(&pcOutData[n], "%s", psProperties->acNewLine);
77   *iWriteCount += n;
78 
79   return ER_NullFields;
80 }
81 
82 
83 /*-
84  ***********************************************************************
85  *
86  * DevelopNoOutput
87  *
88  ***********************************************************************
89  */
90 int
DevelopNoOutput(FTIMES_PROPERTIES * psProperties,char * pcOutData,int * iWriteCount,FTIMES_FILE_DATA * psFTFileData,char * pcError)91 DevelopNoOutput(FTIMES_PROPERTIES *psProperties, char *pcOutData, int *iWriteCount, FTIMES_FILE_DATA *psFTFileData, char *pcError)
92 {
93   /*-
94    *********************************************************************
95    *
96    * Set the write count for the caller.
97    *
98    *********************************************************************
99    */
100   *iWriteCount = 0;
101 
102   return ER_OK;
103 }
104 
105 
106 #ifdef UNIX
107 /*-
108  ***********************************************************************
109  *
110  * DevelopNormalOutput
111  *
112  ***********************************************************************
113  */
114 int
DevelopNormalOutput(FTIMES_PROPERTIES * psProperties,char * pcOutData,int * iWriteCount,FTIMES_FILE_DATA * psFTFileData,char * pcError)115 DevelopNormalOutput(FTIMES_PROPERTIES *psProperties, char *pcOutData, int *iWriteCount, FTIMES_FILE_DATA *psFTFileData, char *pcError)
116 {
117   char                acTime[FTIMES_TIME_FORMAT_SIZE];
118   int                 n;
119   int                 iError;
120   int                 iStatus = ER_OK;
121 
122   /*-
123    *********************************************************************
124    *
125    * This is required since only strcats are used below.
126    *
127    *********************************************************************
128    */
129   pcError[0] = 0;
130 
131   /*-
132    *********************************************************************
133    *
134    * File Name = name
135    *
136    *********************************************************************
137    */
138   n = sprintf(pcOutData, "\"%s\"", psFTFileData->pcNeuteredPath);
139 
140   /*-
141    *********************************************************************
142    *
143    * If there are no attributes to develop, just generate a series of
144    * NULL fields and return.
145    *
146    *********************************************************************
147    */
148   if (psFTFileData->ulAttributeMask == 0)
149   {
150     *iWriteCount = n;
151     return DevelopHaveNothingOutput(psProperties, &pcOutData[n], iWriteCount, psFTFileData, pcError);
152   }
153 
154   /*-
155    *********************************************************************
156    *
157    * Device = dev
158    *
159    *********************************************************************
160    */
161   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_DEV))
162   {
163     n += sprintf(&pcOutData[n], "|%u", (unsigned) psFTFileData->sStatEntry.st_dev);
164   }
165 
166   /*-
167    *********************************************************************
168    *
169    * Inode = inode
170    *
171    *********************************************************************
172    */
173   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_INODE))
174   {
175     n += sprintf(&pcOutData[n], "|%u", (unsigned) psFTFileData->sStatEntry.st_ino);
176   }
177 
178   /*-
179    *********************************************************************
180    *
181    * Permissions and Mode = mode
182    *
183    *********************************************************************
184    */
185   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MODE))
186   {
187     n += sprintf(&pcOutData[n], "|%o", (unsigned) psFTFileData->sStatEntry.st_mode);
188   }
189 
190   /*-
191    *********************************************************************
192    *
193    * Number of Links = nlink
194    *
195    *********************************************************************
196    */
197   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_NLINK))
198   {
199     n += sprintf(&pcOutData[n], "|%u", (unsigned) psFTFileData->sStatEntry.st_nlink);
200   }
201 
202   /*-
203    *********************************************************************
204    *
205    * User ID = uid
206    *
207    *********************************************************************
208    */
209   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_UID))
210   {
211     n += sprintf(&pcOutData[n], "|%u", (unsigned) psFTFileData->sStatEntry.st_uid);
212   }
213 
214   /*-
215    *********************************************************************
216    *
217    * Group ID = gid
218    *
219    *********************************************************************
220    */
221   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_GID))
222   {
223     n += sprintf(&pcOutData[n], "|%u", (unsigned) psFTFileData->sStatEntry.st_gid);
224   }
225 
226   /*-
227    *********************************************************************
228    *
229    * Special Device Type = rdev
230    *
231    *********************************************************************
232    */
233   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_RDEV))
234   {
235     n += sprintf(&pcOutData[n], "|%u", (unsigned) psFTFileData->sStatEntry.st_rdev);
236   }
237 
238   /*-
239    *********************************************************************
240    *
241    * Last Access Time = atime
242    *
243    *********************************************************************
244    */
245   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_ATIME))
246   {
247     iError = TimeFormatTime(&psFTFileData->sStatEntry.st_atime, acTime);
248     if (iError == ER_OK)
249     {
250       n += sprintf(&pcOutData[n], "|%s", acTime);
251     }
252     else
253     {
254       n += sprintf(&pcOutData[n], "|");
255       strcat(pcError, (pcError[0]) ? ",atime" : "atime");
256       iStatus = ER_NullFields;
257     }
258   }
259 
260   /*-
261    *********************************************************************
262    *
263    * Last Modification Time = mtime
264    *
265    *********************************************************************
266    */
267   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MTIME))
268   {
269     iError = TimeFormatTime(&psFTFileData->sStatEntry.st_mtime, acTime);
270     if (iError == ER_OK)
271     {
272       n += sprintf(&pcOutData[n], "|%s", acTime);
273     }
274     else
275     {
276       n += sprintf(&pcOutData[n], "|");
277       strcat(pcError, (pcError[0]) ? ",mtime" : "mtime");
278       iStatus = ER_NullFields;
279     }
280   }
281 
282   /*-
283    *********************************************************************
284    *
285    * Last Status Change Time = ctime
286    *
287    *********************************************************************
288    */
289   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_CTIME))
290   {
291     iError = TimeFormatTime(&psFTFileData->sStatEntry.st_ctime, acTime);
292     if (iError == ER_OK)
293     {
294       n += sprintf(&pcOutData[n], "|%s", acTime);
295     }
296     else
297     {
298       n += sprintf(&pcOutData[n], "|");
299       strcat(pcError, (pcError[0]) ? ",ctime" : "ctime");
300       iStatus = ER_NullFields;
301     }
302   }
303 
304   /*-
305    *********************************************************************
306    *
307    * File Size = size
308    *
309    *********************************************************************
310    */
311   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SIZE))
312   {
313 #ifdef USE_AP_SNPRINTF
314     n += snprintf(&pcOutData[n], FTIMES_MAX_64BIT_SIZE, "|%qu", (unsigned long long) psFTFileData->sStatEntry.st_size);
315 #else
316     n += snprintf(&pcOutData[n], FTIMES_MAX_64BIT_SIZE, "|%llu", (unsigned long long) psFTFileData->sStatEntry.st_size);
317 #endif
318   }
319 
320   /*-
321    *********************************************************************
322    *
323    * File MD5 = md5
324    *
325    *********************************************************************
326    */
327   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MD5))
328   {
329 #ifdef USE_PCRE
330     int iFilterIndex = n + 1;
331 #endif
332     pcOutData[n++] = '|';
333     if (S_ISDIR(psFTFileData->sStatEntry.st_mode))
334     {
335       if (psProperties->bHashDirectories)
336       {
337         if (memcmp(psFTFileData->aucFileMd5, gaucMd5ZeroHash, MD5_HASH_SIZE) != 0)
338         {
339           n += MD5HashToHex(psFTFileData->aucFileMd5, &pcOutData[n]);
340         }
341       }
342       else
343       {
344         n += sprintf(&pcOutData[n], "DIRECTORY");
345       }
346     }
347     else if (S_ISREG(psFTFileData->sStatEntry.st_mode))
348     {
349       if (memcmp(psFTFileData->aucFileMd5, gaucMd5ZeroHash, MD5_HASH_SIZE) != 0)
350       {
351         n += MD5HashToHex(psFTFileData->aucFileMd5, &pcOutData[n]);
352       }
353       else
354       {
355         strcat(pcError, (pcError[0]) ? ",md5" : "md5");
356         iStatus = ER_NullFields;
357       }
358     }
359     else if (S_ISLNK(psFTFileData->sStatEntry.st_mode))
360     {
361       if (psProperties->bHashSymbolicLinks)
362       {
363         if (memcmp(psFTFileData->aucFileMd5, gaucMd5ZeroHash, MD5_HASH_SIZE) != 0)
364         {
365           n += MD5HashToHex(psFTFileData->aucFileMd5, &pcOutData[n]);
366         }
367         else
368         {
369           strcat(pcError, (pcError[0]) ? ",md5" : "md5");
370           iStatus = ER_NullFields;
371         }
372       }
373       else
374       {
375         n += sprintf(&pcOutData[n], "SYMLINK");
376       }
377     }
378     else
379     {
380       if (psProperties->bAnalyzeDeviceFiles && memcmp(psFTFileData->aucFileMd5, gaucMd5ZeroHash, MD5_HASH_SIZE) != 0)
381       {
382         n += MD5HashToHex(psFTFileData->aucFileMd5, &pcOutData[n]);
383       }
384       else
385       {
386         n += sprintf(&pcOutData[n], "SPECIAL");
387       }
388     }
389 #ifdef USE_PCRE
390     /*-
391      *******************************************************************
392      *
393      * Conditionally filter this record based on its MD5 value.
394      *
395      *******************************************************************
396      */
397     if
398     (
399       (psProperties->psExcludeFilterMd5List && SupportMatchFilter(psProperties->psExcludeFilterMd5List, &pcOutData[iFilterIndex]) != NULL) ||
400       (psProperties->psIncludeFilterMd5List && SupportMatchFilter(psProperties->psIncludeFilterMd5List, &pcOutData[iFilterIndex]) == NULL)
401     )
402     {
403       return ER_Filtered;
404     }
405 #endif
406   }
407 
408   /*-
409    *********************************************************************
410    *
411    * File SHA1 = sha1
412    *
413    *********************************************************************
414    */
415   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA1))
416   {
417 #ifdef USE_PCRE
418     int iFilterIndex = n + 1;
419 #endif
420     pcOutData[n++] = '|';
421     if (S_ISDIR(psFTFileData->sStatEntry.st_mode))
422     {
423       if (psProperties->bHashDirectories)
424       {
425         if (memcmp(psFTFileData->aucFileSha1, gaucSha1ZeroHash, SHA1_HASH_SIZE) != 0)
426         {
427           n += SHA1HashToHex(psFTFileData->aucFileSha1, &pcOutData[n]);
428         }
429       }
430       else
431       {
432         n += sprintf(&pcOutData[n], "DIRECTORY");
433       }
434     }
435     else if (S_ISREG(psFTFileData->sStatEntry.st_mode))
436     {
437       if (memcmp(psFTFileData->aucFileSha1, gaucSha1ZeroHash, SHA1_HASH_SIZE) != 0)
438       {
439         n += SHA1HashToHex(psFTFileData->aucFileSha1, &pcOutData[n]);
440       }
441       else
442       {
443         strcat(pcError, (pcError[0]) ? ",sha1" : "sha1");
444         iStatus = ER_NullFields;
445       }
446     }
447     else if (S_ISLNK(psFTFileData->sStatEntry.st_mode))
448     {
449       if (psProperties->bHashSymbolicLinks)
450       {
451         if (memcmp(psFTFileData->aucFileSha1, gaucSha1ZeroHash, SHA1_HASH_SIZE) != 0)
452         {
453           n += SHA1HashToHex(psFTFileData->aucFileSha1, &pcOutData[n]);
454         }
455         else
456         {
457           strcat(pcError, (pcError[0]) ? ",sha1" : "sha1");
458           iStatus = ER_NullFields;
459         }
460       }
461       else
462       {
463         n += sprintf(&pcOutData[n], "SYMLINK");
464       }
465     }
466     else
467     {
468       if (psProperties->bAnalyzeDeviceFiles && memcmp(psFTFileData->aucFileSha1, gaucSha1ZeroHash, MD5_HASH_SIZE) != 0)
469       {
470         n += MD5HashToHex(psFTFileData->aucFileSha1, &pcOutData[n]);
471       }
472       else
473       {
474         n += sprintf(&pcOutData[n], "SPECIAL");
475       }
476     }
477 #ifdef USE_PCRE
478     /*-
479      *******************************************************************
480      *
481      * Conditionally filter this record based on its SHA1 value.
482      *
483      *******************************************************************
484      */
485     if
486     (
487       (psProperties->psExcludeFilterSha1List && SupportMatchFilter(psProperties->psExcludeFilterSha1List, &pcOutData[iFilterIndex]) != NULL) ||
488       (psProperties->psIncludeFilterSha1List && SupportMatchFilter(psProperties->psIncludeFilterSha1List, &pcOutData[iFilterIndex]) == NULL)
489     )
490     {
491       return ER_Filtered;
492     }
493 #endif
494   }
495 
496   /*-
497    *********************************************************************
498    *
499    * File SHA256 = sha256
500    *
501    *********************************************************************
502    */
503   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA256))
504   {
505 #ifdef USE_PCRE
506     int iFilterIndex = n + 1;
507 #endif
508     pcOutData[n++] = '|';
509     if (S_ISDIR(psFTFileData->sStatEntry.st_mode))
510     {
511       if (psProperties->bHashDirectories)
512       {
513         if (memcmp(psFTFileData->aucFileSha256, gaucSha256ZeroHash, SHA256_HASH_SIZE) != 0)
514         {
515           n += SHA256HashToHex(psFTFileData->aucFileSha256, &pcOutData[n]);
516         }
517       }
518       else
519       {
520         n += sprintf(&pcOutData[n], "DIRECTORY");
521       }
522     }
523     else if (S_ISREG(psFTFileData->sStatEntry.st_mode))
524     {
525       if (memcmp(psFTFileData->aucFileSha256, gaucSha256ZeroHash, SHA256_HASH_SIZE) != 0)
526       {
527         n += SHA256HashToHex(psFTFileData->aucFileSha256, &pcOutData[n]);
528       }
529       else
530       {
531         strcat(pcError, (pcError[0]) ? ",sha256" : "sha256");
532         iStatus = ER_NullFields;
533       }
534     }
535     else if (S_ISLNK(psFTFileData->sStatEntry.st_mode))
536     {
537       if (psProperties->bHashSymbolicLinks)
538       {
539         if (memcmp(psFTFileData->aucFileSha256, gaucSha256ZeroHash, SHA256_HASH_SIZE) != 0)
540         {
541           n += SHA256HashToHex(psFTFileData->aucFileSha256, &pcOutData[n]);
542         }
543         else
544         {
545           strcat(pcError, (pcError[0]) ? ",sha256" : "sha256");
546           iStatus = ER_NullFields;
547         }
548       }
549       else
550       {
551         n += sprintf(&pcOutData[n], "SYMLINK");
552       }
553     }
554     else
555     {
556       if (psProperties->bAnalyzeDeviceFiles && memcmp(psFTFileData->aucFileSha256, gaucSha256ZeroHash, MD5_HASH_SIZE) != 0)
557       {
558         n += MD5HashToHex(psFTFileData->aucFileSha256, &pcOutData[n]);
559       }
560       else
561       {
562         n += sprintf(&pcOutData[n], "SPECIAL");
563       }
564     }
565 #ifdef USE_PCRE
566     /*-
567      *******************************************************************
568      *
569      * Conditionally filter this record based on its SHA256 value.
570      *
571      *******************************************************************
572      */
573     if
574     (
575       (psProperties->psExcludeFilterSha256List && SupportMatchFilter(psProperties->psExcludeFilterSha256List, &pcOutData[iFilterIndex]) != NULL) ||
576       (psProperties->psIncludeFilterSha256List && SupportMatchFilter(psProperties->psIncludeFilterSha256List, &pcOutData[iFilterIndex]) == NULL)
577     )
578     {
579       return ER_Filtered;
580     }
581 #endif
582   }
583 
584   /*-
585    *********************************************************************
586    *
587    * File Magic = magic
588    *
589    *********************************************************************
590    */
591 #ifdef USE_XMAGIC
592   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MAGIC))
593   {
594     pcOutData[n++] = '|';
595     if (psFTFileData->acType[0])
596     {
597       n += sprintf(&pcOutData[n], "%s", psFTFileData->acType);
598     }
599     else
600     {
601       strcat(pcError, (pcError[0]) ? ",magic" : "magic");
602       iStatus = ER_NullFields;
603     }
604   }
605 #else
606   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MAGIC))
607   {
608     pcOutData[n++] = '|';
609   }
610 #endif
611 
612   /*-
613    *********************************************************************
614    *
615    * EOL
616    *
617    *********************************************************************
618    */
619   n += sprintf(&pcOutData[n], "%s", psProperties->acNewLine);
620 
621   /*-
622    *********************************************************************
623    *
624    * Set the write count for the caller.
625    *
626    *********************************************************************
627    */
628   *iWriteCount = n;
629 
630   return iStatus;
631 }
632 #endif
633 
634 
635 #ifdef WIN32
636 /*-
637  ***********************************************************************
638  *
639  * DevelopNormalOutput
640  *
641  ***********************************************************************
642  */
643 int
DevelopNormalOutput(FTIMES_PROPERTIES * psProperties,char * pcOutData,int * iWriteCount,FTIMES_FILE_DATA * psFTFileData,char * pcError)644 DevelopNormalOutput(FTIMES_PROPERTIES *psProperties, char *pcOutData, int *iWriteCount, FTIMES_FILE_DATA *psFTFileData, char *pcError)
645 {
646   char                acTime[FTIMES_TIME_FORMAT_SIZE];
647   int                 iError;
648   int                 n;
649   int                 iStatus = ER_OK;
650   unsigned __int64    ui64FileIndex;
651   unsigned __int64    ui64FileSize;
652 
653   /*-
654    *********************************************************************
655    *
656    * This is required since only strcats are used below.
657    *
658    *********************************************************************
659    */
660   pcError[0] = 0;
661 
662   /*-
663    *********************************************************************
664    *
665    * File Name = name
666    *
667    *********************************************************************
668    */
669   n = sprintf(pcOutData, "\"%s\"", psFTFileData->pcNeuteredPath);
670 
671   /*-
672    *********************************************************************
673    *
674    * If there are no attributes to develop, just generate a series of
675    * NULL fields and return.
676    *
677    *********************************************************************
678    */
679   if (psFTFileData->ulAttributeMask == 0)
680   {
681     *iWriteCount = n;
682     return DevelopHaveNothingOutput(psProperties, &pcOutData[n], iWriteCount, psFTFileData, pcError);
683   }
684 
685   /*-
686    *********************************************************************
687    *
688    * Volume Number = volume
689    *
690    *********************************************************************
691    */
692   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_VOLUME))
693   {
694     pcOutData[n++] = '|';
695     if (psFTFileData->dwVolumeSerialNumber != 0xffffffff)
696     {
697       n += sprintf(&pcOutData[n], "%u", (unsigned int) psFTFileData->dwVolumeSerialNumber);
698     }
699     else
700     {
701       strcat(pcError, (pcError[0]) ? ",volume" : "volume");
702       iStatus = ER_NullFields;
703     }
704   }
705 
706   /*-
707    *********************************************************************
708    *
709    * File Index = findex
710    *
711    *********************************************************************
712    */
713   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_FINDEX))
714   {
715     pcOutData[n++] = '|';
716     if (psFTFileData->dwFileIndexHigh != 0xffffffff && psFTFileData->dwFileIndexLow != 0xffffffff)
717     {
718       ui64FileIndex = (((unsigned __int64) psFTFileData->dwFileIndexHigh) << 32) | psFTFileData->dwFileIndexLow;
719       n += sprintf(&pcOutData[n], "%I64u", (APP_UI64) ui64FileIndex);
720     }
721     else
722     {
723       strcat(pcError, (pcError[0]) ? ",findex" : "findex");
724       iStatus = ER_NullFields;
725     }
726   }
727 
728   /*-
729    *********************************************************************
730    *
731    * Attributes = attributes
732    *
733    *********************************************************************
734    */
735   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_ATTRIBUTES))
736   {
737     n += sprintf(&pcOutData[n], "|%u", (unsigned int) psFTFileData->dwFileAttributes);
738   }
739 
740   /*-
741    *********************************************************************
742    *
743    * Last Access Time = atime|ams
744    *
745    *********************************************************************
746    */
747   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_ATIME))
748   {
749     if (psFTFileData->sFTATime.dwLowDateTime == 0 && psFTFileData->sFTATime.dwHighDateTime == 0)
750     {
751       n += sprintf(&pcOutData[n], "||");
752       strcat(pcError, (pcError[0]) ? ",atime" : "atime");
753       iStatus = ER_NullFields;
754     }
755     else
756     {
757       iError = TimeFormatTime((FILETIME *) &psFTFileData->sFTATime, acTime);
758       if (iError == ER_OK)
759       {
760         n += sprintf(&pcOutData[n], "|%s", acTime);
761       }
762       else
763       {
764         n += sprintf(&pcOutData[n], "||");
765         strcat(pcError, (pcError[0]) ? ",atime" : "atime");
766         iStatus = ER_NullFields;
767       }
768     }
769   }
770 
771   /*-
772    *********************************************************************
773    *
774    * Last Write Time = mtime|mms
775    *
776    *********************************************************************
777    */
778   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MTIME))
779   {
780     if (psFTFileData->sFTMTime.dwLowDateTime == 0 && psFTFileData->sFTMTime.dwHighDateTime == 0)
781     {
782       n += sprintf(&pcOutData[n], "||");
783       strcat(pcError, (pcError[0]) ? ",mtime" : "mtime");
784       iStatus = ER_NullFields;
785     }
786     else
787     {
788       iError = TimeFormatTime((FILETIME *) &psFTFileData->sFTMTime, acTime);
789       if (iError == ER_OK)
790       {
791         n += sprintf(&pcOutData[n], "|%s", acTime);
792       }
793       else
794       {
795         n += sprintf(&pcOutData[n], "||");
796         strcat(pcError, (pcError[0]) ? ",mtime" : "mtime");
797         iStatus = ER_NullFields;
798       }
799     }
800   }
801 
802   /*-
803    *********************************************************************
804    *
805    * Creation Time = ctime|cms
806    *
807    *********************************************************************
808    */
809   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_CTIME))
810   {
811     if (psFTFileData->sFTCTime.dwLowDateTime == 0 && psFTFileData->sFTCTime.dwHighDateTime == 0)
812     {
813       n += sprintf(&pcOutData[n], "||");
814       strcat(pcError, (pcError[0]) ? ",ctime" : "ctime");
815       iStatus = ER_NullFields;
816     }
817     else
818     {
819       iError = TimeFormatTime((FILETIME *) &psFTFileData->sFTCTime, acTime);
820       if (iError == ER_OK)
821       {
822         n += sprintf(&pcOutData[n], "|%s", acTime);
823       }
824       else
825       {
826         n += sprintf(&pcOutData[n], "||");
827         strcat(pcError, (pcError[0]) ? ",ctime" : "ctime");
828         iStatus = ER_NullFields;
829       }
830     }
831   }
832 
833   /*-
834    *********************************************************************
835    *
836    * Last Change Time = chtime|chms
837    *
838    *********************************************************************
839    */
840   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_CHTIME))
841   {
842     if (psFTFileData->sFTChTime.dwLowDateTime == 0 && psFTFileData->sFTChTime.dwHighDateTime == 0)
843     {
844       n += sprintf(&pcOutData[n], "||");
845       if (psFTFileData->iFSType == FSTYPE_NTFS)
846       {
847         strcat(pcError, (pcError[0]) ? ",chtime" : "chtime");
848         iStatus = ER_NullFields;
849       }
850     }
851     else
852     {
853       iError = TimeFormatTime((FILETIME *) &psFTFileData->sFTChTime, acTime);
854       if (iError == ER_OK)
855       {
856         n += sprintf(&pcOutData[n], "|%s", acTime);
857       }
858       else
859       {
860         n += sprintf(&pcOutData[n], "||");
861         if (psFTFileData->iFSType == FSTYPE_NTFS)
862         {
863           strcat(pcError, (pcError[0]) ? ",chtime" : "chtime");
864           iStatus = ER_NullFields;
865         }
866       }
867     }
868   }
869 
870   /*-
871    *********************************************************************
872    *
873    * File Size = size
874    *
875    *********************************************************************
876    */
877   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SIZE))
878   {
879     ui64FileSize = (((unsigned __int64) psFTFileData->dwFileSizeHigh) << 32) | psFTFileData->dwFileSizeLow;
880     n += sprintf(&pcOutData[n], "|%I64u", (APP_UI64) ui64FileSize);
881   }
882 
883   /*-
884    *********************************************************************
885    *
886    * Number of Alternate Streams = altstreams
887    *
888    *********************************************************************
889    */
890   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_ALTSTREAMS))
891   {
892     pcOutData[n++] = '|';
893     if (psFTFileData->iStreamCount != FTIMES_INVALID_STREAM_COUNT)
894     {
895       n += sprintf(&pcOutData[n], "%u", psFTFileData->iStreamCount);
896     }
897     else
898     {
899       if (psFTFileData->iFSType == FSTYPE_NTFS)
900       {
901         strcat(pcError, (pcError[0]) ? ",altstreams" : "altstreams");
902         iStatus = ER_NullFields;
903       }
904     }
905   }
906 
907   /*-
908    *********************************************************************
909    *
910    * File MD5 = md5
911    *
912    *********************************************************************
913    */
914   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MD5))
915   {
916 #ifdef USE_PCRE
917     int iFilterIndex = n + 1;
918 #endif
919     pcOutData[n++] = '|';
920     if ((psFTFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
921     {
922       if (psProperties->bHashDirectories)
923       {
924         if (memcmp(psFTFileData->aucFileMd5, gaucMd5ZeroHash, MD5_HASH_SIZE) != 0)
925         {
926           n += MD5HashToHex(psFTFileData->aucFileMd5, &pcOutData[n]);
927         }
928       }
929       else
930       {
931         n += sprintf(&pcOutData[n], "DIRECTORY");
932       }
933     }
934     else
935     {
936       if (memcmp(psFTFileData->aucFileMd5, gaucMd5ZeroHash, MD5_HASH_SIZE) != 0)
937       {
938         n += MD5HashToHex(psFTFileData->aucFileMd5, &pcOutData[n]);
939       }
940       else
941       {
942         strcat(pcError, (pcError[0]) ? ",md5" : "md5");
943         iStatus = ER_NullFields;
944       }
945     }
946 #ifdef USE_PCRE
947     /*-
948      *******************************************************************
949      *
950      * Conditionally filter this record based on its MD5 value.
951      *
952      *******************************************************************
953      */
954     if
955     (
956       (psProperties->psExcludeFilterMd5List && SupportMatchFilter(psProperties->psExcludeFilterMd5List, &pcOutData[iFilterIndex]) != NULL) ||
957       (psProperties->psIncludeFilterMd5List && SupportMatchFilter(psProperties->psIncludeFilterMd5List, &pcOutData[iFilterIndex]) == NULL)
958     )
959     {
960       return ER_Filtered;
961     }
962 #endif
963   }
964 
965   /*-
966    *********************************************************************
967    *
968    * File SHA1 = sha1
969    *
970    *********************************************************************
971    */
972   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA1))
973   {
974 #ifdef USE_PCRE
975     int iFilterIndex = n + 1;
976 #endif
977     pcOutData[n++] = '|';
978     if ((psFTFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
979     {
980       if (psProperties->bHashDirectories)
981       {
982         if (memcmp(psFTFileData->aucFileSha1, gaucSha1ZeroHash, SHA1_HASH_SIZE) != 0)
983         {
984           n += SHA1HashToHex(psFTFileData->aucFileSha1, &pcOutData[n]);
985         }
986       }
987       else
988       {
989         n += sprintf(&pcOutData[n], "DIRECTORY");
990       }
991     }
992     else
993     {
994       if (memcmp(psFTFileData->aucFileSha1, gaucSha1ZeroHash, SHA1_HASH_SIZE) != 0)
995       {
996         n += SHA1HashToHex(psFTFileData->aucFileSha1, &pcOutData[n]);
997       }
998       else
999       {
1000         strcat(pcError, (pcError[0]) ? ",sha1" : "sha1");
1001         iStatus = ER_NullFields;
1002       }
1003     }
1004 #ifdef USE_PCRE
1005     /*-
1006      *******************************************************************
1007      *
1008      * Conditionally filter this record based on its SHA1 value.
1009      *
1010      *******************************************************************
1011      */
1012     if
1013     (
1014       (psProperties->psExcludeFilterSha1List && SupportMatchFilter(psProperties->psExcludeFilterSha1List, &pcOutData[iFilterIndex]) != NULL) ||
1015       (psProperties->psIncludeFilterSha1List && SupportMatchFilter(psProperties->psIncludeFilterSha1List, &pcOutData[iFilterIndex]) == NULL)
1016     )
1017     {
1018       return ER_Filtered;
1019     }
1020 #endif
1021   }
1022 
1023   /*-
1024    *********************************************************************
1025    *
1026    * File SHA256 = sha256
1027    *
1028    *********************************************************************
1029    */
1030   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA256))
1031   {
1032 #ifdef USE_PCRE
1033     int iFilterIndex = n + 1;
1034 #endif
1035     pcOutData[n++] = '|';
1036     if ((psFTFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
1037     {
1038       if (psProperties->bHashDirectories)
1039       {
1040         if (memcmp(psFTFileData->aucFileSha256, gaucSha256ZeroHash, SHA256_HASH_SIZE) != 0)
1041         {
1042           n += SHA256HashToHex(psFTFileData->aucFileSha256, &pcOutData[n]);
1043         }
1044       }
1045       else
1046       {
1047         n += sprintf(&pcOutData[n], "DIRECTORY");
1048       }
1049     }
1050     else
1051     {
1052       if (memcmp(psFTFileData->aucFileSha256, gaucSha256ZeroHash, SHA256_HASH_SIZE) != 0)
1053       {
1054         n += SHA256HashToHex(psFTFileData->aucFileSha256, &pcOutData[n]);
1055       }
1056       else
1057       {
1058         strcat(pcError, (pcError[0]) ? ",sha256" : "sha256");
1059         iStatus = ER_NullFields;
1060       }
1061     }
1062 #ifdef USE_PCRE
1063     /*-
1064      *******************************************************************
1065      *
1066      * Conditionally filter this record based on its SHA256 value.
1067      *
1068      *******************************************************************
1069      */
1070     if
1071     (
1072       (psProperties->psExcludeFilterSha256List && SupportMatchFilter(psProperties->psExcludeFilterSha256List, &pcOutData[iFilterIndex]) != NULL) ||
1073       (psProperties->psIncludeFilterSha256List && SupportMatchFilter(psProperties->psIncludeFilterSha256List, &pcOutData[iFilterIndex]) == NULL)
1074     )
1075     {
1076       return ER_Filtered;
1077     }
1078 #endif
1079   }
1080 
1081   /*-
1082    *********************************************************************
1083    *
1084    * File Magic = magic
1085    *
1086    *********************************************************************
1087    */
1088 #ifdef USE_XMAGIC
1089   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MAGIC))
1090   {
1091     pcOutData[n++] = '|';
1092     if (psFTFileData->acType[0])
1093     {
1094       n += sprintf(&pcOutData[n], "%s", psFTFileData->acType);
1095     }
1096     else
1097     {
1098       strcat(pcError, (pcError[0]) ? ",magic" : "magic");
1099       iStatus = ER_NullFields;
1100     }
1101   }
1102 #else
1103   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MAGIC))
1104   {
1105     pcOutData[n++] = '|';
1106   }
1107 #endif
1108 
1109   /*-
1110    *********************************************************************
1111    *
1112    * Owner SID = osid
1113    *
1114    *********************************************************************
1115    */
1116 #ifdef USE_SDDL
1117   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_OWNER))
1118   {
1119     char *pcSidOwner = NULL;
1120     pcOutData[n++] = '|';
1121     ConvertSidToStringSidA(psFTFileData->psSidOwner, &pcSidOwner);
1122     if (pcSidOwner)
1123     {
1124       n += sprintf(&pcOutData[n], "%s", pcSidOwner);
1125       LocalFree(pcSidOwner);
1126     }
1127     else
1128     {
1129       strcat(pcError, (pcError[0]) ? ",osid" : "osid");
1130       iStatus = ER_NullFields;
1131     }
1132   }
1133 #else
1134   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_OWNER))
1135   {
1136     pcOutData[n++] = '|';
1137   }
1138 #endif
1139 
1140   /*-
1141    *********************************************************************
1142    *
1143    * Group SID = gsid
1144    *
1145    *********************************************************************
1146    */
1147 #ifdef USE_SDDL
1148   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_GROUP))
1149   {
1150     char *pcSidGroup = NULL;
1151     pcOutData[n++] = '|';
1152     ConvertSidToStringSidA(psFTFileData->psSidGroup, &pcSidGroup);
1153     if (pcSidGroup)
1154     {
1155       n += sprintf(&pcOutData[n], "%s", pcSidGroup);
1156       LocalFree(pcSidGroup);
1157     }
1158     else
1159     {
1160       strcat(pcError, (pcError[0]) ? ",gsid" : "gsid");
1161       iStatus = ER_NullFields;
1162     }
1163   }
1164 #else
1165   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_GROUP))
1166   {
1167     pcOutData[n++] = '|';
1168   }
1169 #endif
1170 
1171   /*-
1172    *********************************************************************
1173    *
1174    * DACL = dacl
1175    *
1176    *********************************************************************
1177    */
1178 #ifdef USE_SDDL
1179   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_DACL))
1180   {
1181     char *pcAclDacl = NULL;
1182     DWORD dwLength = 0;
1183     pcOutData[n++] = '|';
1184     ConvertSecurityDescriptorToStringSecurityDescriptorA(psFTFileData->psSd, SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &pcAclDacl, &dwLength);
1185     if (pcAclDacl && dwLength < FTIMES_MAX_ACL_SIZE)
1186     {
1187       n += sprintf(&pcOutData[n], "%s", pcAclDacl);
1188       LocalFree(pcAclDacl);
1189     }
1190     else
1191     {
1192       if (dwLength >= FTIMES_MAX_ACL_SIZE)
1193       {
1194         snprintf(pcError, MESSAGE_SIZE, "DevelopNormalOutput(): NeuteredPath = [%s], DaclLength = [%d]: Length exceeds %d bytes.", psFTFileData->pcNeuteredPath, (int) dwLength, FTIMES_MAX_ACL_SIZE - 1);
1195         ErrorHandler(ER_Failure, pcError, ERROR_FAILURE);
1196         pcError[0] = 0;
1197       }
1198       strcat(pcError, (pcError[0]) ? ",dacl" : "dacl");
1199       iStatus = ER_NullFields;
1200     }
1201   }
1202 #else
1203   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_DACL))
1204   {
1205     pcOutData[n++] = '|';
1206   }
1207 #endif
1208 
1209   /*-
1210    *********************************************************************
1211    *
1212    * EOL
1213    *
1214    *********************************************************************
1215    */
1216   n += sprintf(&pcOutData[n], "%s", psProperties->acNewLine);
1217 
1218   /*-
1219    *********************************************************************
1220    *
1221    * Set the write count for the caller.
1222    *
1223    *********************************************************************
1224    */
1225   *iWriteCount = n;
1226 
1227   return iStatus;
1228 }
1229 #endif
1230 
1231 
1232 #ifdef UNIX
1233 /*-
1234  ***********************************************************************
1235  *
1236  * DevelopCompressedOutput
1237  *
1238  ***********************************************************************
1239  */
1240 int
DevelopCompressedOutput(FTIMES_PROPERTIES * psProperties,char * pcOutData,int * iWriteCount,FTIMES_FILE_DATA * psFTFileData,char * pcError)1241 DevelopCompressedOutput(FTIMES_PROPERTIES *psProperties, char *pcOutData, int *iWriteCount, FTIMES_FILE_DATA *psFTFileData, char *pcError)
1242 {
1243   int                 i;
1244   int                 n;
1245   int                 iStatus = ER_OK;
1246   static char         acLastName[4 * FTIMES_MAX_PATH]; /* This is an encoded name. */
1247   static long         lRecoveryCounter = 0;
1248   static struct stat  sStatLastEntry;
1249 
1250   /*-
1251    *********************************************************************
1252    *
1253    * This is required since only strcats are used below.
1254    *
1255    *********************************************************************
1256    */
1257   pcError[0] = 0;
1258 
1259   /*-
1260    *********************************************************************
1261    *
1262    * File Name = name
1263    *
1264    *********************************************************************
1265    */
1266   if (lRecoveryCounter == 0)
1267   {
1268     n = sprintf(pcOutData, "00\"%s\"", psFTFileData->pcNeuteredPath);
1269     strncpy(acLastName, psFTFileData->pcNeuteredPath, (4 * FTIMES_MAX_PATH));
1270   }
1271   else
1272   {
1273     /*-
1274      *******************************************************************
1275      *
1276      * Compress name by appending repeat count and deleting letters in
1277      * common with previous name.
1278      *
1279      *******************************************************************
1280      */
1281     i = 0;
1282     if (acLastName[0] == '\0')
1283     {
1284       n = sprintf(pcOutData, "00\"%s\"", psFTFileData->pcNeuteredPath);
1285     }
1286     else
1287     {
1288       while ((i < 254) &&
1289              (acLastName[i] != '\0') &&
1290              (psFTFileData->pcNeuteredPath[i] != '\0') &&
1291              (acLastName[i] == psFTFileData->pcNeuteredPath[i]))
1292       {
1293         i++;
1294       }
1295       n = sprintf(pcOutData, "%02x%s\"", i + 1 /* Add 1 for the leading quote. */, &psFTFileData->pcNeuteredPath[i]);
1296     }
1297     strncpy(&acLastName[i], &psFTFileData->pcNeuteredPath[i], ((4 * FTIMES_MAX_PATH) - i) /* Must subtract i here to prevent overruns. */);
1298   }
1299 
1300   /*-
1301    *********************************************************************
1302    *
1303    * If there are no attributes to develop, reset the recovery counter,
1304    * zero out state variables, generate a series of NULL fields, and
1305    * return.
1306    *
1307    *********************************************************************
1308    */
1309   if (psFTFileData->ulAttributeMask == 0)
1310   {
1311     *iWriteCount = n;
1312     lRecoveryCounter = 0;
1313     memset(acLastName, 0, (4 * FTIMES_MAX_PATH));
1314     memset(&sStatLastEntry, 0, sizeof(struct stat));
1315     return DevelopHaveNothingOutput(psProperties, &pcOutData[n], iWriteCount, psFTFileData, pcError);
1316   }
1317 
1318   /*-
1319    *********************************************************************
1320    *
1321    * Device = dev
1322    *
1323    *********************************************************************
1324    */
1325   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_DEV))
1326   {
1327     pcOutData[n++] = '|';
1328     if (lRecoveryCounter == 0)
1329     {
1330       n += sprintf(&pcOutData[n], "%x", (unsigned) psFTFileData->sStatEntry.st_dev);
1331     }
1332     else
1333     {
1334       if (psFTFileData->sStatEntry.st_dev == sStatLastEntry.st_dev)
1335       {
1336         pcOutData[n++] = '#';
1337       }
1338       else
1339       {
1340         n += sprintf(&pcOutData[n], "%x", (unsigned) psFTFileData->sStatEntry.st_dev);
1341       }
1342     }
1343   }
1344 
1345   /*-
1346    *********************************************************************
1347    *
1348    * Inode = inode
1349    *
1350    *********************************************************************
1351    */
1352   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_INODE))
1353   {
1354     pcOutData[n++] = '|';
1355     if (lRecoveryCounter == 0)
1356     {
1357       n += sprintf(&pcOutData[n], "%x", (unsigned) psFTFileData->sStatEntry.st_ino);
1358     }
1359     else
1360     {
1361       n += DevelopCompressHex(&pcOutData[n], psFTFileData->sStatEntry.st_ino, sStatLastEntry.st_ino);
1362     }
1363   }
1364 
1365   /*-
1366    *********************************************************************
1367    *
1368    * Permissions and Mode = mode
1369    *
1370    *********************************************************************
1371    */
1372   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MODE))
1373   {
1374     pcOutData[n++] = '|';
1375     if (lRecoveryCounter == 0)
1376     {
1377       n += sprintf(&pcOutData[n], "%x", (unsigned) psFTFileData->sStatEntry.st_mode);
1378     }
1379     else
1380     {
1381       if (psFTFileData->sStatEntry.st_mode == sStatLastEntry.st_mode)
1382       {
1383         pcOutData[n++] = '#';
1384       }
1385       else
1386       {
1387         n += sprintf(&pcOutData[n], "%x", (unsigned) psFTFileData->sStatEntry.st_mode);
1388       }
1389     }
1390   }
1391 
1392   /*-
1393    *********************************************************************
1394    *
1395    * Number of Links = nlink
1396    *
1397    *********************************************************************
1398    */
1399   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_NLINK))
1400   {
1401     pcOutData[n++] = '|';
1402     if (lRecoveryCounter == 0)
1403     {
1404       n += sprintf(&pcOutData[n], "%x", (unsigned) psFTFileData->sStatEntry.st_nlink);
1405     }
1406     else
1407     {
1408       if (psFTFileData->sStatEntry.st_nlink == sStatLastEntry.st_nlink)
1409       {
1410         pcOutData[n++] = '#';
1411       }
1412       else
1413       {
1414         n += sprintf(&pcOutData[n], "%x", (unsigned) psFTFileData->sStatEntry.st_nlink);
1415       }
1416     }
1417   }
1418 
1419   /*-
1420    *********************************************************************
1421    *
1422    * User ID = uid
1423    *
1424    *********************************************************************
1425    */
1426   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_UID))
1427   {
1428     pcOutData[n++] = '|';
1429     if (lRecoveryCounter == 0)
1430     {
1431       n += sprintf(&pcOutData[n], "%x", (unsigned) psFTFileData->sStatEntry.st_uid);
1432     }
1433     else
1434     {
1435       if (psFTFileData->sStatEntry.st_uid == sStatLastEntry.st_uid)
1436       {
1437         pcOutData[n++] = '#';
1438       }
1439       else
1440       {
1441         n += sprintf(&pcOutData[n], "%x", (unsigned) psFTFileData->sStatEntry.st_uid);
1442       }
1443     }
1444   }
1445 
1446   /*-
1447    *********************************************************************
1448    *
1449    * Group ID = gid
1450    *
1451    *********************************************************************
1452    */
1453   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_GID))
1454   {
1455     pcOutData[n++] = '|';
1456     if (lRecoveryCounter == 0)
1457     {
1458       n += sprintf(&pcOutData[n], "%x", (unsigned) psFTFileData->sStatEntry.st_gid);
1459     }
1460     else
1461     {
1462       if (psFTFileData->sStatEntry.st_gid == sStatLastEntry.st_gid)
1463       {
1464         pcOutData[n++] = '#';
1465       }
1466       else
1467       {
1468         n += sprintf(&pcOutData[n], "%x", (unsigned) psFTFileData->sStatEntry.st_gid);
1469       }
1470     }
1471   }
1472 
1473   /*-
1474    *********************************************************************
1475    *
1476    * Special Device Type = rdev
1477    *
1478    *********************************************************************
1479    */
1480   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_RDEV))
1481   {
1482     pcOutData[n++] = '|';
1483     if (lRecoveryCounter == 0)
1484     {
1485       n += sprintf(&pcOutData[n], "%x", (unsigned) psFTFileData->sStatEntry.st_rdev);
1486     }
1487     else
1488     {
1489       if (psFTFileData->sStatEntry.st_rdev == sStatLastEntry.st_rdev)
1490       {
1491         pcOutData[n++] = '#';
1492       }
1493       else
1494       {
1495         n += sprintf(&pcOutData[n], "%x", (unsigned) psFTFileData->sStatEntry.st_rdev);
1496       }
1497     }
1498   }
1499 
1500   /*-
1501    *********************************************************************
1502    *
1503    * Last Access Time = atime
1504    *
1505    *********************************************************************
1506    */
1507   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_ATIME))
1508   {
1509     pcOutData[n++] = '|';
1510     if (lRecoveryCounter == 0)
1511     {
1512       n += sprintf(&pcOutData[n], "%x", (unsigned) psFTFileData->sStatEntry.st_atime);
1513     }
1514     else if (psFTFileData->sStatEntry.st_atime == sStatLastEntry.st_atime)
1515     {
1516       pcOutData[n++] = '#';
1517     }
1518     else
1519     {
1520       n += DevelopCompressHex(&pcOutData[n], psFTFileData->sStatEntry.st_atime, sStatLastEntry.st_atime);
1521     }
1522   }
1523 
1524   /*-
1525    *********************************************************************
1526    *
1527    * Last Modification Time = mtime
1528    *
1529    *********************************************************************
1530    */
1531   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MTIME))
1532   {
1533     pcOutData[n++] = '|';
1534     if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_ATIME) && psFTFileData->sStatEntry.st_mtime == psFTFileData->sStatEntry.st_atime)
1535     {
1536       pcOutData[n++] = 'X';
1537     }
1538     else if (lRecoveryCounter == 0)
1539     {
1540       n += sprintf(&pcOutData[n], "%x", (unsigned) psFTFileData->sStatEntry.st_mtime);
1541     }
1542     else
1543     {
1544       if (psFTFileData->sStatEntry.st_mtime == sStatLastEntry.st_mtime)
1545       {
1546         pcOutData[n++] = '#';
1547       }
1548       else
1549       {
1550         n += DevelopCompressHex(&pcOutData[n], psFTFileData->sStatEntry.st_mtime, sStatLastEntry.st_mtime);
1551       }
1552     }
1553   }
1554 
1555   /*-
1556    *********************************************************************
1557    *
1558    * Last Status Change Time = ctime
1559    *
1560    *********************************************************************
1561    */
1562   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_CTIME))
1563   {
1564     pcOutData[n++] = '|';
1565     if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_ATIME) && psFTFileData->sStatEntry.st_ctime == psFTFileData->sStatEntry.st_atime)
1566     {
1567       pcOutData[n++] = 'X';
1568     }
1569     else if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MTIME) && psFTFileData->sStatEntry.st_ctime == psFTFileData->sStatEntry.st_mtime)
1570     {
1571       pcOutData[n++] = 'Y';
1572     }
1573     else if (lRecoveryCounter == 0)
1574     {
1575       n += sprintf(&pcOutData[n], "%x", (unsigned) psFTFileData->sStatEntry.st_ctime);
1576     }
1577     else
1578     {
1579       if (psFTFileData->sStatEntry.st_ctime == sStatLastEntry.st_ctime)
1580       {
1581         pcOutData[n++] = '#';
1582       }
1583       else
1584       {
1585         n += DevelopCompressHex(&pcOutData[n], psFTFileData->sStatEntry.st_ctime, sStatLastEntry.st_ctime);
1586       }
1587     }
1588   }
1589 
1590   /*-
1591    *********************************************************************
1592    *
1593    * File Size = size
1594    *
1595    *********************************************************************
1596    */
1597   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SIZE))
1598   {
1599     pcOutData[n++] = '|';
1600 #ifdef USE_AP_SNPRINTF
1601     n += snprintf(&pcOutData[n], FTIMES_MAX_64BIT_SIZE, "%qx", (unsigned long long) psFTFileData->sStatEntry.st_size);
1602 #else
1603     n += snprintf(&pcOutData[n], FTIMES_MAX_64BIT_SIZE, "%llx", (unsigned long long) psFTFileData->sStatEntry.st_size);
1604 #endif
1605   }
1606 
1607   /*-
1608    *********************************************************************
1609    *
1610    * File MD5 = md5
1611    *
1612    *********************************************************************
1613    */
1614   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MD5))
1615   {
1616     pcOutData[n++] = '|';
1617     if (S_ISDIR(psFTFileData->sStatEntry.st_mode))
1618     {
1619       if (psProperties->bHashDirectories)
1620       {
1621         if (memcmp(psFTFileData->aucFileMd5, gaucMd5ZeroHash, MD5_HASH_SIZE) != 0)
1622         {
1623           n += MD5HashToBase64(psFTFileData->aucFileMd5, &pcOutData[n]);
1624         }
1625         else
1626         {
1627           strcat(pcError, (pcError[0]) ? ",md5" : "md5");
1628           iStatus = ER_NullFields;
1629         }
1630       }
1631       else
1632       {
1633         pcOutData[n++] = 'D';
1634       }
1635     }
1636     else if (S_ISREG(psFTFileData->sStatEntry.st_mode))
1637     {
1638       if (memcmp(psFTFileData->aucFileMd5, gaucMd5ZeroHash, MD5_HASH_SIZE) != 0)
1639       {
1640         n += MD5HashToBase64(psFTFileData->aucFileMd5, &pcOutData[n]);
1641       }
1642       else
1643       {
1644         strcat(pcError, (pcError[0]) ? ",md5" : "md5");
1645         iStatus = ER_NullFields;
1646       }
1647     }
1648     else if (S_ISLNK(psFTFileData->sStatEntry.st_mode))
1649     {
1650       if (psProperties->bHashSymbolicLinks)
1651       {
1652         if (memcmp(psFTFileData->aucFileMd5, gaucMd5ZeroHash, MD5_HASH_SIZE) != 0)
1653         {
1654           n += MD5HashToBase64(psFTFileData->aucFileMd5, &pcOutData[n]);
1655         }
1656         else
1657         {
1658           strcat(pcError, (pcError[0]) ? ",md5" : "md5");
1659           iStatus = ER_NullFields;
1660         }
1661       }
1662       else
1663       {
1664         pcOutData[n++] = 'L';
1665       }
1666     }
1667     else
1668     {
1669       if (psProperties->bAnalyzeDeviceFiles && memcmp(psFTFileData->aucFileMd5, gaucMd5ZeroHash, MD5_HASH_SIZE) != 0)
1670       {
1671         n += MD5HashToBase64(psFTFileData->aucFileMd5, &pcOutData[n]);
1672       }
1673       else
1674       {
1675         pcOutData[n++] = 'S';
1676       }
1677     }
1678   }
1679 
1680   /*-
1681    *********************************************************************
1682    *
1683    * File SHA1 = sha1
1684    *
1685    *********************************************************************
1686    */
1687   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA1))
1688   {
1689     pcOutData[n++] = '|';
1690     if (S_ISDIR(psFTFileData->sStatEntry.st_mode))
1691     {
1692       if (psProperties->bHashDirectories)
1693       {
1694         if (memcmp(psFTFileData->aucFileSha1, gaucSha1ZeroHash, SHA1_HASH_SIZE) != 0)
1695         {
1696           n += SHA1HashToBase64(psFTFileData->aucFileSha1, &pcOutData[n]);
1697         }
1698         else
1699         {
1700           strcat(pcError, (pcError[0]) ? ",sha1" : "sha1");
1701           iStatus = ER_NullFields;
1702         }
1703       }
1704       else
1705       {
1706         pcOutData[n++] = 'D';
1707       }
1708     }
1709     else if (S_ISREG(psFTFileData->sStatEntry.st_mode))
1710     {
1711       if (memcmp(psFTFileData->aucFileSha1, gaucSha1ZeroHash, SHA1_HASH_SIZE) != 0)
1712       {
1713         n += SHA1HashToBase64(psFTFileData->aucFileSha1, &pcOutData[n]);
1714       }
1715       else
1716       {
1717         strcat(pcError, (pcError[0]) ? ",sha1" : "sha1");
1718         iStatus = ER_NullFields;
1719       }
1720     }
1721     else if (S_ISLNK(psFTFileData->sStatEntry.st_mode))
1722     {
1723       if (psProperties->bHashSymbolicLinks)
1724       {
1725         if (memcmp(psFTFileData->aucFileSha1, gaucSha1ZeroHash, SHA1_HASH_SIZE) != 0)
1726         {
1727           n += SHA1HashToBase64(psFTFileData->aucFileSha1, &pcOutData[n]);
1728         }
1729         else
1730         {
1731           strcat(pcError, (pcError[0]) ? ",sha1" : "sha1");
1732           iStatus = ER_NullFields;
1733         }
1734       }
1735       else
1736       {
1737         pcOutData[n++] = 'L';
1738       }
1739     }
1740     else
1741     {
1742       if (psProperties->bAnalyzeDeviceFiles && memcmp(psFTFileData->aucFileSha1, gaucSha1ZeroHash, MD5_HASH_SIZE) != 0)
1743       {
1744         n += MD5HashToBase64(psFTFileData->aucFileSha1, &pcOutData[n]);
1745       }
1746       else
1747       {
1748         pcOutData[n++] = 'S';
1749       }
1750     }
1751   }
1752 
1753   /*-
1754    *********************************************************************
1755    *
1756    * File SHA256 = sha256
1757    *
1758    *********************************************************************
1759    */
1760   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA256))
1761   {
1762     pcOutData[n++] = '|';
1763     if (S_ISDIR(psFTFileData->sStatEntry.st_mode))
1764     {
1765       if (psProperties->bHashDirectories)
1766       {
1767         if (memcmp(psFTFileData->aucFileSha256, gaucSha256ZeroHash, SHA256_HASH_SIZE) != 0)
1768         {
1769           n += SHA256HashToBase64(psFTFileData->aucFileSha256, &pcOutData[n]);
1770         }
1771         else
1772         {
1773           strcat(pcError, (pcError[0]) ? ",sha256" : "sha256");
1774           iStatus = ER_NullFields;
1775         }
1776       }
1777       else
1778       {
1779         pcOutData[n++] = 'D';
1780       }
1781     }
1782     else if (S_ISREG(psFTFileData->sStatEntry.st_mode))
1783     {
1784       if (memcmp(psFTFileData->aucFileSha256, gaucSha256ZeroHash, SHA256_HASH_SIZE) != 0)
1785       {
1786         n += SHA256HashToBase64(psFTFileData->aucFileSha256, &pcOutData[n]);
1787       }
1788       else
1789       {
1790         strcat(pcError, (pcError[0]) ? ",sha256" : "sha256");
1791         iStatus = ER_NullFields;
1792       }
1793     }
1794     else if (S_ISLNK(psFTFileData->sStatEntry.st_mode))
1795     {
1796       if (psProperties->bHashSymbolicLinks)
1797       {
1798         if (memcmp(psFTFileData->aucFileSha256, gaucSha256ZeroHash, SHA256_HASH_SIZE) != 0)
1799         {
1800           n += SHA256HashToBase64(psFTFileData->aucFileSha256, &pcOutData[n]);
1801         }
1802         else
1803         {
1804           strcat(pcError, (pcError[0]) ? ",sha256" : "sha256");
1805           iStatus = ER_NullFields;
1806         }
1807       }
1808       else
1809       {
1810         pcOutData[n++] = 'L';
1811       }
1812     }
1813     else
1814     {
1815       if (psProperties->bAnalyzeDeviceFiles && memcmp(psFTFileData->aucFileSha256, gaucSha256ZeroHash, MD5_HASH_SIZE) != 0)
1816       {
1817         n += MD5HashToBase64(psFTFileData->aucFileSha256, &pcOutData[n]);
1818       }
1819       else
1820       {
1821         pcOutData[n++] = 'S';
1822       }
1823     }
1824   }
1825 
1826   /*-
1827    *********************************************************************
1828    *
1829    * File Magic = magic
1830    *
1831    *********************************************************************
1832    */
1833   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MAGIC))
1834   {
1835     pcOutData[n++] = '|';
1836   }
1837 
1838   /*-
1839    *********************************************************************
1840    *
1841    * EOL
1842    *
1843    *********************************************************************
1844    */
1845   n += sprintf(&pcOutData[n], "%s", psProperties->acNewLine);
1846 
1847   /*-
1848    *********************************************************************
1849    *
1850    * Copy psFTFileData->sStatEntry to sStatLastEntry for next time around.
1851    *
1852    *********************************************************************
1853    */
1854   memcpy(&sStatLastEntry, &psFTFileData->sStatEntry, sizeof(struct stat));
1855 
1856   if (++lRecoveryCounter >= COMPRESS_RECOVERY_RATE)
1857   {
1858     lRecoveryCounter = 0;
1859   }
1860 
1861   /*-
1862    *********************************************************************
1863    *
1864    * Set the write count for the caller.
1865    *
1866    *********************************************************************
1867    */
1868   *iWriteCount = n;
1869 
1870   return iStatus;
1871 }
1872 #endif
1873 
1874 
1875 #ifdef WIN32
1876 /*-
1877  ***********************************************************************
1878  *
1879  * DevelopCompressedOutput
1880  *
1881  ***********************************************************************
1882  */
1883 int
DevelopCompressedOutput(FTIMES_PROPERTIES * psProperties,char * pcOutData,int * iWriteCount,FTIMES_FILE_DATA * psFTFileData,char * pcError)1884 DevelopCompressedOutput(FTIMES_PROPERTIES *psProperties, char *pcOutData, int *iWriteCount, FTIMES_FILE_DATA *psFTFileData, char *pcError)
1885 {
1886   char                acTime[FTIMES_TIME_FORMAT_SIZE];
1887   int                 i;
1888   int                 n;
1889   int                 iError;
1890   int                 iStatus = ER_OK;
1891   unsigned long       ulATimeSeconds;
1892   unsigned long       ulMTimeSeconds;
1893   unsigned long       ulCTimeSeconds;
1894   unsigned long       ulChTimeSeconds;
1895   unsigned long       ulATimeMilliseconds;
1896   unsigned long       ulMTimeMilliseconds;
1897   unsigned long       ulCTimeMilliseconds;
1898   unsigned long       ulChTimeMilliseconds;
1899   unsigned long       ulTempATimeSeconds;
1900   unsigned long       ulTempMTimeSeconds;
1901   unsigned long       ulTempCTimeSeconds;
1902   unsigned long       ulTempChTimeSeconds;
1903   unsigned __int64    ui64ATime = 0;
1904   unsigned __int64    ui64MTime = 0;
1905   unsigned __int64    ui64CTime = 0;
1906   unsigned __int64    ui64ChTime = 0;
1907   unsigned __int64    ui64FileIndex = 0;
1908   unsigned __int64    ui64FileSize = 0;
1909   static char         acLastName[4 * FTIMES_MAX_PATH]; /* This is an encoded name. */
1910   static DWORD        dwLastVolumeSerialNumber;
1911   static DWORD        dwLastFileIndexHigh;
1912   static DWORD        dwLastFileIndexLow;
1913   static DWORD        dwLastFileAttributes;
1914   static long         lRecoveryCounter = 0;
1915   static unsigned __int64 ui64LastATime;
1916   static unsigned __int64 ui64LastMTime;
1917   static unsigned __int64 ui64LastCTime;
1918   static unsigned __int64 ui64LastChTime;
1919 
1920   /*-
1921    *********************************************************************
1922    *
1923    * This is required since only strcats are used below.
1924    *
1925    *********************************************************************
1926    */
1927   pcError[0] = 0;
1928 
1929   /*-
1930    *********************************************************************
1931    *
1932    * File Name = name
1933    *
1934    *********************************************************************
1935    */
1936   if (lRecoveryCounter == 0)
1937   {
1938     n = sprintf(pcOutData, "00\"%s\"", psFTFileData->pcNeuteredPath);
1939     strncpy(acLastName, psFTFileData->pcNeuteredPath, (4 * FTIMES_MAX_PATH));
1940   }
1941   else
1942   {
1943     /*-
1944      *******************************************************************
1945      *
1946      * Compress name by appending repeat count and deleting letters in
1947      * common with previous name.
1948      *
1949      *******************************************************************
1950      */
1951     i = 0;
1952     if (acLastName[0] == '\0')
1953     {
1954       n = sprintf(pcOutData, "00\"%s\"", psFTFileData->pcNeuteredPath);
1955     }
1956     else
1957     {
1958       while ((i < 254) &&
1959              (acLastName[i] != '\0') &&
1960              (psFTFileData->pcNeuteredPath[i] != '\0') &&
1961              (acLastName[i] == psFTFileData->pcNeuteredPath[i]))
1962       {
1963         i++;
1964       }
1965       n = sprintf(pcOutData, "%02x%s\"", i + 1 /* Add 1 for the leading quote. */, &psFTFileData->pcNeuteredPath[i]);
1966     }
1967     strncpy(&acLastName[i], &psFTFileData->pcNeuteredPath[i], ((4 * FTIMES_MAX_PATH) - i) /* Must subtract i here to prevent overruns. */);
1968   }
1969 
1970   /*-
1971    *********************************************************************
1972    *
1973    * If there are no attributes to develop, reset the recovery counter,
1974    * zero out state variables, generate a series of NULL fields, and
1975    * return.
1976    *
1977    *********************************************************************
1978    */
1979   if (psFTFileData->ulAttributeMask == 0)
1980   {
1981     *iWriteCount = n;
1982     lRecoveryCounter = 0;
1983     memset(acLastName, 0, (4 * FTIMES_MAX_PATH));
1984     dwLastVolumeSerialNumber = 0;
1985     dwLastFileIndexHigh = 0;
1986     dwLastFileIndexLow = 0;
1987     dwLastFileAttributes = 0;
1988     ui64LastATime = 0;
1989     ui64LastMTime = 0;
1990     ui64LastCTime = 0;
1991     ui64LastChTime = 0;
1992     return DevelopHaveNothingOutput(psProperties, &pcOutData[n], iWriteCount, psFTFileData, pcError);
1993   }
1994 
1995   /*-
1996    *********************************************************************
1997    *
1998    * Volume Number = volume
1999    *
2000    *********************************************************************
2001    */
2002   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_VOLUME))
2003   {
2004     pcOutData[n++] = '|';
2005     if (lRecoveryCounter == 0)
2006     {
2007       n += sprintf(&pcOutData[n], "%x", (unsigned int) psFTFileData->dwVolumeSerialNumber);
2008     }
2009     else
2010     {
2011       if (psFTFileData->dwVolumeSerialNumber != 0xffffffff)
2012       {
2013         if (psFTFileData->dwVolumeSerialNumber == dwLastVolumeSerialNumber)
2014         {
2015           pcOutData[n++] = '#';
2016         }
2017         else
2018         {
2019           n += sprintf(&pcOutData[n], "%x", (unsigned int) psFTFileData->dwVolumeSerialNumber);
2020         }
2021       }
2022       else
2023       {
2024         strcat(pcError, (pcError[0]) ? ",volume" : "volume");
2025         iStatus = ER_NullFields;
2026       }
2027     }
2028   }
2029 
2030   /*-
2031    *********************************************************************
2032    *
2033    * File Index = findex
2034    *
2035    *********************************************************************
2036    */
2037   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_FINDEX))
2038   {
2039     pcOutData[n++] = '|';
2040     if (lRecoveryCounter == 0)
2041     {
2042       ui64FileIndex = (((unsigned __int64) psFTFileData->dwFileIndexHigh) << 32) | psFTFileData->dwFileIndexLow;
2043       n += sprintf(&pcOutData[n], "%I64x", (APP_UI64) ui64FileIndex);
2044     }
2045     else
2046     {
2047       if (psFTFileData->dwFileIndexHigh != 0xffffffff && psFTFileData->dwFileIndexLow != 0xffffffff)
2048       {
2049         if (psFTFileData->dwFileIndexHigh == dwLastFileIndexHigh)
2050         {
2051           pcOutData[n++] = '#';
2052           n += DevelopCompressHex(&pcOutData[n], psFTFileData->dwFileIndexLow, dwLastFileIndexLow);
2053         }
2054         else
2055         {
2056           ui64FileIndex = (((unsigned __int64) psFTFileData->dwFileIndexHigh) << 32) | psFTFileData->dwFileIndexLow;
2057           n += sprintf(&pcOutData[n], "%I64x", (APP_UI64) ui64FileIndex);
2058         }
2059       }
2060       else
2061       {
2062         strcat(pcError, (pcError[0]) ? ",findex" : "findex");
2063         iStatus = ER_NullFields;
2064       }
2065     }
2066   }
2067 
2068   /*-
2069    *********************************************************************
2070    *
2071    * Attributes = attributes
2072    *
2073    *********************************************************************
2074    */
2075   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_ATTRIBUTES))
2076   {
2077     pcOutData[n++] = '|';
2078     if (lRecoveryCounter == 0)
2079     {
2080       n += sprintf(&pcOutData[n], "%x", (unsigned int) psFTFileData->dwFileAttributes);
2081     }
2082     else
2083     {
2084       if (psFTFileData->dwFileAttributes == dwLastFileAttributes)
2085       {
2086         pcOutData[n++] = '#';
2087       }
2088       else
2089       {
2090         n += sprintf(&pcOutData[n], "%x", (unsigned int) psFTFileData->dwFileAttributes);
2091       }
2092     }
2093   }
2094 
2095   /*-
2096    *********************************************************************
2097    *
2098    * Last Access Time = atime|ams
2099    *
2100    *********************************************************************
2101    */
2102   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_ATIME))
2103   {
2104     pcOutData[n++] = '|';
2105     if (psFTFileData->sFTATime.dwLowDateTime == 0 && psFTFileData->sFTATime.dwHighDateTime == 0)
2106     {
2107       pcOutData[n++] = '|';
2108       strcat(pcError, (pcError[0]) ? ",atime" : "atime");
2109       iStatus = ER_NullFields;
2110       ui64ATime = 0; /* Ensure that ui64LastATime will be properly initialized. */
2111     }
2112     else
2113     {
2114       ui64ATime = (((unsigned __int64) psFTFileData->sFTATime.dwHighDateTime) << 32) | psFTFileData->sFTATime.dwLowDateTime;
2115       if (ui64ATime < UNIX_EPOCH_IN_NT_TIME || ui64ATime > UNIX_LIMIT_IN_NT_TIME)
2116       {
2117         iError = TimeFormatOutOfBandTime((FILETIME *) &psFTFileData->sFTATime, acTime);
2118         if (iError == ER_OK)
2119         {
2120           n += sprintf(&pcOutData[n], "~%s", acTime);
2121         }
2122         else
2123         {
2124           pcOutData[n++] = '|';
2125           strcat(pcError, (pcError[0]) ? ",atime" : "atime");
2126           iStatus = ER_NullFields;
2127         }
2128       }
2129       else
2130       {
2131         ulATimeSeconds = (unsigned long) ((ui64ATime - UNIX_EPOCH_IN_NT_TIME) / 10000000);
2132         ulATimeMilliseconds = (unsigned long) (((ui64ATime - UNIX_EPOCH_IN_NT_TIME) % 10000000) / 10000);
2133         if (lRecoveryCounter == 0)
2134         {
2135           n += sprintf(&pcOutData[n], "%lx|%lx", ulATimeSeconds, ulATimeMilliseconds);
2136         }
2137         else
2138         {
2139           if (ui64ATime == ui64LastATime)
2140           {
2141             pcOutData[n++] = '#';
2142             pcOutData[n++] = '|';
2143             pcOutData[n++] = '#';
2144           }
2145           else
2146           {
2147             ulTempATimeSeconds = (unsigned long) ((ui64LastATime - UNIX_EPOCH_IN_NT_TIME) / 10000000);
2148             n += DevelopCompressHex(&pcOutData[n], ulATimeSeconds, ulTempATimeSeconds);
2149             n += sprintf(&pcOutData[n], "|%lx", ulATimeMilliseconds);
2150           }
2151         }
2152       }
2153     }
2154   }
2155 
2156   /*-
2157    *********************************************************************
2158    *
2159    * Last Write Time = mtime|mms
2160    *
2161    *********************************************************************
2162    */
2163   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MTIME))
2164   {
2165     pcOutData[n++] = '|';
2166     if (psFTFileData->sFTMTime.dwLowDateTime == 0 && psFTFileData->sFTMTime.dwHighDateTime == 0)
2167     {
2168       pcOutData[n++] = '|';
2169       strcat(pcError, (pcError[0]) ? ",mtime" : "mtime");
2170       iStatus = ER_NullFields;
2171       ui64MTime = 0; /* Ensure that ui64LastMTime will be properly initialized. */
2172     }
2173     else
2174     {
2175       ui64MTime = (((unsigned __int64) psFTFileData->sFTMTime.dwHighDateTime) << 32) | psFTFileData->sFTMTime.dwLowDateTime;
2176       if (ui64MTime < UNIX_EPOCH_IN_NT_TIME || ui64MTime > UNIX_LIMIT_IN_NT_TIME)
2177       {
2178         iError = TimeFormatOutOfBandTime((FILETIME *) &psFTFileData->sFTMTime, acTime);
2179         if (iError == ER_OK)
2180         {
2181           n += sprintf(&pcOutData[n], "~%s", acTime);
2182         }
2183         else
2184         {
2185           pcOutData[n++] = '|';
2186           strcat(pcError, (pcError[0]) ? ",mtime" : "mtime");
2187           iStatus = ER_NullFields;
2188         }
2189       }
2190       else
2191       {
2192         ulMTimeSeconds = (unsigned long) ((ui64MTime - UNIX_EPOCH_IN_NT_TIME) / 10000000);
2193         ulMTimeMilliseconds = (unsigned long) (((ui64MTime - UNIX_EPOCH_IN_NT_TIME) % 10000000) / 10000);
2194         if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_ATIME) && ui64MTime == ui64ATime)
2195         {
2196           pcOutData[n++] = 'X';
2197           pcOutData[n++] = '|';
2198           pcOutData[n++] = 'X';
2199         }
2200         else if (lRecoveryCounter == 0)
2201         {
2202           n += sprintf(&pcOutData[n], "%lx|%lx", ulMTimeSeconds, ulMTimeMilliseconds);
2203         }
2204         else if (ui64MTime == ui64LastMTime)
2205         {
2206           pcOutData[n++] = '#';
2207           pcOutData[n++] = '|';
2208           pcOutData[n++] = '#';
2209         }
2210         else
2211         {
2212           ulTempMTimeSeconds = (unsigned long) ((ui64LastMTime - UNIX_EPOCH_IN_NT_TIME) / 10000000);
2213           n += DevelopCompressHex(&pcOutData[n], ulMTimeSeconds, ulTempMTimeSeconds);
2214           n += sprintf(&pcOutData[n], "|%lx", ulMTimeMilliseconds);
2215         }
2216       }
2217     }
2218   }
2219 
2220   /*-
2221    *********************************************************************
2222    *
2223    * Creation Time = ctime|cms
2224    *
2225    *********************************************************************
2226    */
2227   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_CTIME))
2228   {
2229     pcOutData[n++] = '|';
2230     if (psFTFileData->sFTCTime.dwLowDateTime == 0 && psFTFileData->sFTCTime.dwHighDateTime == 0)
2231     {
2232       pcOutData[n++] = '|';
2233       strcat(pcError, (pcError[0]) ? ",ctime" : "ctime");
2234       iStatus = ER_NullFields;
2235       ui64CTime = 0; /* Ensure that ui64LastCTime will be properly initialized. */
2236     }
2237     else
2238     {
2239       ui64CTime = (((unsigned __int64) psFTFileData->sFTCTime.dwHighDateTime) << 32) | psFTFileData->sFTCTime.dwLowDateTime;
2240       if (ui64CTime < UNIX_EPOCH_IN_NT_TIME || ui64CTime > UNIX_LIMIT_IN_NT_TIME)
2241       {
2242         iError = TimeFormatOutOfBandTime((FILETIME *) &psFTFileData->sFTCTime, acTime);
2243         if (iError == ER_OK)
2244         {
2245           n += sprintf(&pcOutData[n], "~%s", acTime);
2246         }
2247         else
2248         {
2249           pcOutData[n++] = '|';
2250           strcat(pcError, (pcError[0]) ? ",ctime" : "ctime");
2251           iStatus = ER_NullFields;
2252         }
2253       }
2254       else
2255       {
2256         ulCTimeSeconds = (unsigned long) ((ui64CTime - UNIX_EPOCH_IN_NT_TIME) / 10000000);
2257         ulCTimeMilliseconds = (unsigned long) (((ui64CTime - UNIX_EPOCH_IN_NT_TIME) % 10000000) / 10000);
2258         if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_ATIME) && ui64CTime == ui64ATime)
2259         {
2260           pcOutData[n++] = 'X';
2261           pcOutData[n++] = '|';
2262           pcOutData[n++] = 'X';
2263         }
2264         else if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MTIME) && ui64CTime == ui64MTime)
2265         {
2266           pcOutData[n++] = 'Y';
2267           pcOutData[n++] = '|';
2268           pcOutData[n++] = 'Y';
2269         }
2270         else if (lRecoveryCounter == 0)
2271         {
2272           n += sprintf(&pcOutData[n], "%lx|%lx", ulCTimeSeconds, ulCTimeMilliseconds);
2273         }
2274         else if (ui64CTime == ui64LastCTime)
2275         {
2276           pcOutData[n++] = '#';
2277           pcOutData[n++] = '|';
2278           pcOutData[n++] = '#';
2279         }
2280         else
2281         {
2282           ulTempCTimeSeconds = (unsigned long) ((ui64LastCTime - UNIX_EPOCH_IN_NT_TIME) / 10000000);
2283           n += DevelopCompressHex(&pcOutData[n], ulCTimeSeconds, ulTempCTimeSeconds);
2284           n += sprintf(&pcOutData[n], "|%lx", ulCTimeMilliseconds);
2285         }
2286       }
2287     }
2288   }
2289 
2290   /*-
2291    *********************************************************************
2292    *
2293    * Last Change Time = chtime|chms
2294    *
2295    *********************************************************************
2296    */
2297   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_CHTIME))
2298   {
2299     pcOutData[n++] = '|';
2300     if (psFTFileData->sFTChTime.dwLowDateTime == 0 && psFTFileData->sFTChTime.dwHighDateTime == 0)
2301     {
2302       pcOutData[n++] = '|';
2303       if (psFTFileData->iFSType == FSTYPE_NTFS)
2304       {
2305         strcat(pcError, (pcError[0]) ? ",chtime" : "chtime");
2306         iStatus = ER_NullFields;
2307       }
2308       ui64ChTime = 0; /* Ensure that ui64LastChTime will be properly initialized. */
2309     }
2310     else
2311     {
2312       ui64ChTime = (((unsigned __int64) psFTFileData->sFTChTime.dwHighDateTime) << 32) | psFTFileData->sFTChTime.dwLowDateTime;
2313       if (ui64ChTime < UNIX_EPOCH_IN_NT_TIME || ui64ChTime > UNIX_LIMIT_IN_NT_TIME)
2314       {
2315         iError = TimeFormatOutOfBandTime((FILETIME *) &psFTFileData->sFTChTime, acTime);
2316         if (iError == ER_OK)
2317         {
2318           n += sprintf(&pcOutData[n], "~%s", acTime);
2319         }
2320         else
2321         {
2322           pcOutData[n++] = '|';
2323           if (psFTFileData->iFSType == FSTYPE_NTFS)
2324           {
2325             strcat(pcError, (pcError[0]) ? ",chtime" : "chtime");
2326             iStatus = ER_NullFields;
2327           }
2328         }
2329       }
2330       else
2331       {
2332         ulChTimeSeconds = (unsigned long) ((ui64ChTime - UNIX_EPOCH_IN_NT_TIME) / 10000000);
2333         ulChTimeMilliseconds = (unsigned long) (((ui64ChTime - UNIX_EPOCH_IN_NT_TIME) % 10000000) / 10000);
2334         if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_ATIME) && ui64ChTime == ui64ATime)
2335         {
2336           pcOutData[n++] = 'X';
2337           pcOutData[n++] = '|';
2338           pcOutData[n++] = 'X';
2339         }
2340         else if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MTIME) && ui64ChTime == ui64MTime)
2341         {
2342           pcOutData[n++] = 'Y';
2343           pcOutData[n++] = '|';
2344           pcOutData[n++] = 'Y';
2345         }
2346         else if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_CTIME) && ui64ChTime == ui64CTime)
2347         {
2348           pcOutData[n++] = 'Z';
2349           pcOutData[n++] = '|';
2350           pcOutData[n++] = 'Z';
2351         }
2352         else if (lRecoveryCounter == 0)
2353         {
2354           n += sprintf(&pcOutData[n], "%lx|%lx", ulChTimeSeconds, ulChTimeMilliseconds);
2355         }
2356         else if (ui64ChTime == ui64LastChTime)
2357         {
2358           pcOutData[n++] = '#';
2359           pcOutData[n++] = '|';
2360           pcOutData[n++] = '#';
2361         }
2362         else
2363         {
2364           ulTempChTimeSeconds = (unsigned long) ((ui64LastChTime - UNIX_EPOCH_IN_NT_TIME) / 10000000);
2365           n += DevelopCompressHex(&pcOutData[n], ulChTimeSeconds, ulTempChTimeSeconds);
2366           n += sprintf(&pcOutData[n], "|%lx", ulChTimeMilliseconds);
2367         }
2368       }
2369     }
2370   }
2371 
2372   /*-
2373    *********************************************************************
2374    *
2375    * File Size = size
2376    *
2377    *********************************************************************
2378    */
2379   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SIZE))
2380   {
2381     pcOutData[n++] = '|';
2382     ui64FileSize = (((unsigned __int64) psFTFileData->dwFileSizeHigh) << 32) | psFTFileData->dwFileSizeLow;
2383     n += sprintf(&pcOutData[n], "%I64x", (APP_UI64) ui64FileSize);
2384   }
2385 
2386   /*-
2387    *********************************************************************
2388    *
2389    * Number of Alternate Streams = altstreams
2390    *
2391    *********************************************************************
2392    */
2393   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_ALTSTREAMS))
2394   {
2395     pcOutData[n++] = '|';
2396     if (psFTFileData->iStreamCount != FTIMES_INVALID_STREAM_COUNT)
2397     {
2398       n += sprintf(&pcOutData[n], "%x", psFTFileData->iStreamCount);
2399     }
2400     else
2401     {
2402       if (psFTFileData->iFSType == FSTYPE_NTFS)
2403       {
2404         strcat(pcError, (pcError[0]) ? ",altstreams" : "altstreams");
2405         iStatus = ER_NullFields;
2406       }
2407     }
2408   }
2409 
2410   /*-
2411    *********************************************************************
2412    *
2413    * File MD5 = md5
2414    *
2415    *********************************************************************
2416    */
2417   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MD5))
2418   {
2419     pcOutData[n++] = '|';
2420     if ((psFTFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
2421     {
2422       if (psProperties->bHashDirectories)
2423       {
2424         if (memcmp(psFTFileData->aucFileMd5, gaucMd5ZeroHash, MD5_HASH_SIZE) != 0)
2425         {
2426           n += MD5HashToBase64(psFTFileData->aucFileMd5, &pcOutData[n]);
2427         }
2428         else
2429         {
2430           strcat(pcError, (pcError[0]) ? ",md5" : "md5");
2431           iStatus = ER_NullFields;
2432         }
2433       }
2434       else
2435       {
2436         pcOutData[n++] = 'D';
2437       }
2438     }
2439     else
2440     {
2441       if (memcmp(psFTFileData->aucFileMd5, gaucMd5ZeroHash, MD5_HASH_SIZE) != 0)
2442       {
2443         n += MD5HashToBase64(psFTFileData->aucFileMd5, &pcOutData[n]);
2444       }
2445       else
2446       {
2447         strcat(pcError, (pcError[0]) ? ",md5" : "md5");
2448         iStatus = ER_NullFields;
2449       }
2450     }
2451   }
2452 
2453   /*-
2454    *********************************************************************
2455    *
2456    * File SHA1 = sha1
2457    *
2458    *********************************************************************
2459    */
2460   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA1))
2461   {
2462     pcOutData[n++] = '|';
2463     if ((psFTFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
2464     {
2465       if (psProperties->bHashDirectories)
2466       {
2467         if (memcmp(psFTFileData->aucFileSha1, gaucSha1ZeroHash, SHA1_HASH_SIZE) != 0)
2468         {
2469           n += SHA1HashToBase64(psFTFileData->aucFileSha1, &pcOutData[n]);
2470         }
2471         else
2472         {
2473           strcat(pcError, (pcError[0]) ? ",sha1" : "sha1");
2474           iStatus = ER_NullFields;
2475         }
2476       }
2477       else
2478       {
2479         pcOutData[n++] = 'D';
2480       }
2481     }
2482     else
2483     {
2484       if (memcmp(psFTFileData->aucFileSha1, gaucSha1ZeroHash, SHA1_HASH_SIZE) != 0)
2485       {
2486         n += SHA1HashToBase64(psFTFileData->aucFileSha1, &pcOutData[n]);
2487       }
2488       else
2489       {
2490         strcat(pcError, (pcError[0]) ? ",sha1" : "sha1");
2491         iStatus = ER_NullFields;
2492       }
2493     }
2494   }
2495 
2496   /*-
2497    *********************************************************************
2498    *
2499    * File SHA256 = sha256
2500    *
2501    *********************************************************************
2502    */
2503   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA256))
2504   {
2505     pcOutData[n++] = '|';
2506     if ((psFTFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
2507     {
2508       if (psProperties->bHashDirectories)
2509       {
2510         if (memcmp(psFTFileData->aucFileSha256, gaucSha256ZeroHash, SHA256_HASH_SIZE) != 0)
2511         {
2512           n += SHA256HashToBase64(psFTFileData->aucFileSha256, &pcOutData[n]);
2513         }
2514         else
2515         {
2516           strcat(pcError, (pcError[0]) ? ",sha256" : "sha256");
2517           iStatus = ER_NullFields;
2518         }
2519       }
2520       else
2521       {
2522         pcOutData[n++] = 'D';
2523       }
2524     }
2525     else
2526     {
2527       if (memcmp(psFTFileData->aucFileSha256, gaucSha256ZeroHash, SHA256_HASH_SIZE) != 0)
2528       {
2529         n += SHA256HashToBase64(psFTFileData->aucFileSha256, &pcOutData[n]);
2530       }
2531       else
2532       {
2533         strcat(pcError, (pcError[0]) ? ",sha256" : "sha256");
2534         iStatus = ER_NullFields;
2535       }
2536     }
2537   }
2538 
2539   /*-
2540    *********************************************************************
2541    *
2542    * File Magic = magic
2543    *
2544    *********************************************************************
2545    */
2546   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MAGIC))
2547   {
2548     pcOutData[n++] = '|';
2549   }
2550 
2551   /*-
2552    *********************************************************************
2553    *
2554    * Owner SID = osid (Compression for this field is not yet supported.)
2555    *
2556    *********************************************************************
2557    */
2558   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_OWNER))
2559   {
2560     pcOutData[n++] = '|';
2561   }
2562 
2563   /*-
2564    *********************************************************************
2565    *
2566    * Group SID = gsid (Compression for this field is not yet supported.)
2567    *
2568    *********************************************************************
2569    */
2570   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_GROUP))
2571   {
2572     pcOutData[n++] = '|';
2573   }
2574 
2575   /*-
2576    *********************************************************************
2577    *
2578    * DACL = dacl (Compression for this field is not yet supported.)
2579    *
2580    *********************************************************************
2581    */
2582   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_OWNER))
2583   {
2584     pcOutData[n++] = '|';
2585   }
2586 
2587   /*-
2588    *********************************************************************
2589    *
2590    * EOL
2591    *
2592    *********************************************************************
2593    */
2594   n += sprintf(&pcOutData[n], "%s", psProperties->acNewLine);
2595 
2596   /*-
2597    *********************************************************************
2598    *
2599    * Save pertinent fields for next time around.
2600    *
2601    *********************************************************************
2602    */
2603   dwLastVolumeSerialNumber = psFTFileData->dwVolumeSerialNumber;
2604   dwLastFileIndexHigh = psFTFileData->dwFileIndexHigh;
2605   dwLastFileIndexLow = psFTFileData->dwFileIndexLow;
2606   dwLastFileAttributes = psFTFileData->dwFileAttributes;
2607   ui64LastATime = ui64ATime;
2608   ui64LastMTime = ui64MTime;
2609   ui64LastCTime = ui64CTime;
2610   ui64LastChTime = ui64ChTime;
2611 
2612   if (++lRecoveryCounter >= COMPRESS_RECOVERY_RATE)
2613   {
2614     lRecoveryCounter = 0;
2615   }
2616 
2617   /*-
2618    *********************************************************************
2619    *
2620    * Set the write count for the caller.
2621    *
2622    *********************************************************************
2623    */
2624   *iWriteCount = n;
2625 
2626   return iStatus;
2627 }
2628 #endif
2629 
2630 
2631 /*-
2632  ***********************************************************************
2633  *
2634  * DevelopCompressHex
2635  *
2636  ***********************************************************************
2637  */
2638 int
DevelopCompressHex(char * pcData,unsigned long ulHex,unsigned long ulOldHex)2639 DevelopCompressHex(char *pcData, unsigned long ulHex, unsigned long ulOldHex)
2640 {
2641   int                 iHexDigitCount;
2642   int                 iHexDigitDeltaCount;
2643   unsigned long       ulDelta;
2644 
2645   /*-
2646    *********************************************************************
2647    *
2648    * We normally print ulHex in hex, taking up to 8 hex digits; maybe
2649    * fewer because we don't print leading zeros. However, if ulHex is
2650    * 'close to' ulOldHex, it may take less space to print the difference.
2651    * We have to be careful, because simply subtracting ulHex - ulOldHex
2652    * may overflow a signed number.
2653    *
2654    *********************************************************************
2655    */
2656   iHexDigitCount = DevelopCountHexDigits(ulHex);
2657 
2658   if (ulHex >= ulOldHex)
2659   {
2660     ulDelta = ulHex - ulOldHex;
2661     iHexDigitDeltaCount = 1 + DevelopCountHexDigits(ulDelta);
2662     if (iHexDigitDeltaCount < iHexDigitCount)
2663     {
2664       return sprintf(pcData, "+%lx", ulDelta);
2665     }
2666   }
2667   else
2668   {
2669     ulDelta = ulOldHex - ulHex;
2670     iHexDigitDeltaCount = 1 + DevelopCountHexDigits(ulDelta);
2671     if (iHexDigitDeltaCount < iHexDigitCount)
2672     {
2673       return sprintf(pcData, "-%lx", ulDelta);
2674     }
2675   }
2676 
2677   return sprintf(pcData, "%lx", ulHex);
2678 }
2679 
2680 
2681 /*-
2682  ***********************************************************************
2683  *
2684  * DevelopCountHexDigits
2685  *
2686  ***********************************************************************
2687  */
2688 int
DevelopCountHexDigits(unsigned long ulHex)2689 DevelopCountHexDigits(unsigned long ulHex)
2690 {
2691   int                 i = 8;
2692 
2693   if (ulHex == 0)
2694   {
2695     return 1;
2696   }
2697   while ((ulHex & 0xf0000000) == 0)
2698   {
2699     ulHex <<= 4;
2700     i--;
2701   }
2702   return i;
2703 }
2704