1 /*-
2  ***********************************************************************
3  *
4  * $Id: map.c,v 1.110 2014/07/30 08:13:33 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 static int giDirectories;
15 static int giFiles;
16 static int giSpecial;
17 #ifdef WINNT
18 static int giStreams;
19 #endif
20 
21 static int giRecords;
22 static int giIncompleteRecords;
23 
24 #ifdef USE_EMBEDDED_PYTHON
25 /*-
26  ***********************************************************************
27  *
28  * MapConvertPythonArguments
29  *
30  ***********************************************************************
31  */
32 wchar_t **
MapConvertPythonArguments(size_t szArgumentCount,char ** ppcArgumentVector)33 MapConvertPythonArguments(size_t szArgumentCount, char **ppcArgumentVector)
34 {
35   size_t              szArgument = 0;
36   size_t              szLength = 0;
37   wchar_t            *pwcArgument = NULL;
38   wchar_t           **ppwcArgumentVector = NULL;
39 
40   /*-
41    *********************************************************************
42    *
43    * Make sure the caller has provided valid arguments.
44    *
45    *********************************************************************
46    */
47   if (szArgumentCount < 1 || ppcArgumentVector == NULL)
48   {
49     return NULL;
50   }
51 
52   /*-
53    *********************************************************************
54    *
55    * Allocate and initialize memory for a new argument vector.
56    *
57    *********************************************************************
58    */
59   ppwcArgumentVector = calloc(szArgumentCount, sizeof(wchar_t *));
60   if (ppwcArgumentVector == NULL)
61   {
62     return NULL;
63   }
64 
65   /*-
66    *********************************************************************
67    *
68    * Convert each multibyte argument to a wide-character argument.
69    *
70    *********************************************************************
71    */
72   for (szArgument = 0; szArgument < szArgumentCount; szArgument++)
73   {
74     szLength = mbstowcs(NULL, ppcArgumentVector[szArgument], 0);
75     if (szLength < 0)
76     {
77       MapFreePythonArguments(szArgumentCount, ppwcArgumentVector);
78       return NULL;
79     }
80     pwcArgument = calloc(szLength + 1, sizeof(wchar_t));
81     if (pwcArgument == NULL)
82     {
83       MapFreePythonArguments(szArgumentCount, ppwcArgumentVector);
84       return NULL;
85     }
86     szLength = mbstowcs(pwcArgument, ppcArgumentVector[szArgument], szLength);
87     if (szLength < 0)
88     {
89       MapFreePythonArguments(szArgumentCount, ppwcArgumentVector);
90       return NULL;
91     }
92     ppwcArgumentVector[szArgument] = pwcArgument;
93   }
94 
95   return ppwcArgumentVector;
96 }
97 #endif
98 
99 
100 /*-
101  ***********************************************************************
102  *
103  * MapDirHashAlpha
104  *
105  ***********************************************************************
106  */
107 void
MapDirHashAlpha(FTIMES_PROPERTIES * psProperties,FTIMES_HASH_DATA * psFTHashData)108 MapDirHashAlpha(FTIMES_PROPERTIES *psProperties, FTIMES_HASH_DATA *psFTHashData)
109 {
110   /*-
111    *********************************************************************
112    *
113    * Conditionally start directory hashes.
114    *
115    *********************************************************************
116    */
117   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MD5))
118   {
119     MD5Alpha(&psFTHashData->sMd5Context);
120   }
121   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA1))
122   {
123     SHA1Alpha(&psFTHashData->sSha1Context);
124   }
125   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA256))
126   {
127     SHA256Alpha(&psFTHashData->sSha256Context);
128   }
129 }
130 
131 
132 /*-
133  ***********************************************************************
134  *
135  * MapDirHashCycle
136  *
137  ***********************************************************************
138  */
139 void
MapDirHashCycle(FTIMES_PROPERTIES * psProperties,FTIMES_HASH_DATA * psFTHashData,FTIMES_FILE_DATA * psFTFileData)140 MapDirHashCycle(FTIMES_PROPERTIES *psProperties, FTIMES_HASH_DATA *psFTHashData, FTIMES_FILE_DATA *psFTFileData)
141 {
142   /*-
143    *********************************************************************
144    *
145    * Conditionally update directory hashes. If the current file was
146    * not hashed (e.g., because it's a special file or it could not be
147    * opened), then its default hash value (all zeros) is folded into
148    * the aggregate directory hash.
149    *
150    *********************************************************************
151    */
152   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MD5))
153   {
154     MD5Cycle(&psFTHashData->sMd5Context, psFTFileData->aucFileMd5, MD5_HASH_SIZE);
155   }
156   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA1))
157   {
158     SHA1Cycle(&psFTHashData->sSha1Context, psFTFileData->aucFileSha1, SHA1_HASH_SIZE);
159   }
160   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA256))
161   {
162     SHA256Cycle(&psFTHashData->sSha256Context, psFTFileData->aucFileSha256, SHA256_HASH_SIZE);
163   }
164 }
165 
166 
167 /*-
168  ***********************************************************************
169  *
170  * MapDirHashOmega
171  *
172  ***********************************************************************
173  */
174 void
MapDirHashOmega(FTIMES_PROPERTIES * psProperties,FTIMES_HASH_DATA * psFTHashData,FTIMES_FILE_DATA * psFTFileData)175 MapDirHashOmega(FTIMES_PROPERTIES *psProperties, FTIMES_HASH_DATA *psFTHashData, FTIMES_FILE_DATA *psFTFileData)
176 {
177   /*-
178    *********************************************************************
179    *
180    * Conditionally complete directory hashes.
181    *
182    *********************************************************************
183    */
184   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MD5))
185   {
186     MD5Omega(&psFTHashData->sMd5Context, psFTFileData->aucFileMd5);
187   }
188   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA1))
189   {
190     SHA1Omega(&psFTHashData->sSha1Context, psFTFileData->aucFileSha1);
191   }
192   if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA256))
193   {
194     SHA256Omega(&psFTHashData->sSha256Context, psFTFileData->aucFileSha256);
195   }
196 }
197 
198 
199 /*-
200  ***********************************************************************
201  *
202  * MapDirname
203  *
204  ***********************************************************************
205  */
206 char *
MapDirname(char * pcPath)207 MapDirname(char *pcPath)
208 {
209   static char         acDirname[FTIMES_MAX_PATH] = "";
210   int                 n = 0;
211   int                 iLength = 0;
212   int                 iIndex = 0;
213 
214   /*-
215    *********************************************************************
216    *
217    * Return "." for NULL or empty paths.
218    *
219    *********************************************************************
220    */
221   if (pcPath == NULL || pcPath[0] == 0 || iLength > FTIMES_MAX_PATH)
222   {
223     acDirname[n++] = '.';
224     acDirname[n] = 0;
225     return acDirname;
226   }
227 
228   /*-
229    *********************************************************************
230    *
231    * Set errno and return NULL for paths that are too long.
232    *
233    *********************************************************************
234    */
235   iLength = iIndex = strlen(pcPath);
236   if (iLength > FTIMES_MAX_PATH)
237   {
238     errno = ENAMETOOLONG;
239     return NULL;
240   }
241   iIndex--;
242 
243   /*-
244    *********************************************************************
245    *
246    * Backup over trailing slashes.
247    *
248    *********************************************************************
249    */
250   while (iIndex > 0 && pcPath[iIndex] == FTIMES_SLASHCHAR)
251   {
252     iIndex--;
253   }
254 
255   /*-
256    *********************************************************************
257    *
258    * Backup until the next slash is found or nothing is left.
259    *
260    *********************************************************************
261    */
262   while (iIndex > 0 && pcPath[iIndex] != FTIMES_SLASHCHAR)
263   {
264     iIndex--;
265   }
266 
267   /*-
268    *********************************************************************
269    *
270    * Return "." if the index is zero and the path does not start with
271    * a drive letter or a slash. Otherwise, return the drive letter or
272    * slash. If the index is greater than zero, keep backing up until
273    * there are no more trailing slashes.
274    *
275    *********************************************************************
276    */
277   if (iIndex == 0)
278   {
279 #ifdef WIN32
280     if (iLength >= 2 && isalpha(pcPath[0]) && pcPath[1] == ':')
281     {
282       acDirname[n++] = pcPath[0];
283       acDirname[n++] = pcPath[1];
284       acDirname[n++] = FTIMES_SLASHCHAR;
285     }
286     else
287 #endif
288     if (pcPath[iIndex] == FTIMES_SLASHCHAR)
289     {
290       acDirname[n++] = FTIMES_SLASHCHAR;
291     }
292     else
293     {
294       acDirname[n++] = '.';
295     }
296     acDirname[n] = 0;
297     return acDirname;
298   }
299   else
300   {
301     while (--iIndex > 0 && pcPath[iIndex] == FTIMES_SLASHCHAR);
302   }
303   iLength = iIndex + 1;
304 
305   /*-
306    *********************************************************************
307    *
308    * Return anything that's left.
309    *
310    *********************************************************************
311    */
312   strncpy(acDirname, pcPath, iLength);
313 #ifdef WIN32
314   if (iLength == 2)
315   {
316     acDirname[iLength++] = FTIMES_SLASHCHAR;
317   }
318 #endif
319   acDirname[iLength] = 0;
320 
321   return acDirname;
322 }
323 
324 
325 #ifdef USE_FILE_HOOKS
326 /*-
327  ***********************************************************************
328  *
329  * MapExecuteHook
330  *
331  ***********************************************************************
332  */
333 int
MapExecuteHook(FTIMES_PROPERTIES * psProperties,FTIMES_FILE_DATA * psFTFileData,char * pcError)334 MapExecuteHook(FTIMES_PROPERTIES *psProperties, FTIMES_FILE_DATA *psFTFileData, char *pcError)
335 {
336   const char          acRoutine[] = "MapExecuteHook()";
337 #define PIPE_READ_SIZE 8192
338   char                acData[PIPE_READ_SIZE] = "";
339   char                acMessage[MESSAGE_SIZE] = "";
340   fd_set              sFdReadSet;
341   fd_set              sFdSaveSet;
342   HOOK_LIST          *psHook = NULL;
343   int                 i = 0;
344   int                 iFd = 0;
345   int                 iError = 0;
346   int                 iKidPid = 0;
347   int                 iKidReturn = 0;
348   int                 iKidStatus = 0;
349 //int                 iKidSignal = 0;
350 //int                 iKidDumped = 0;
351   int                 iNRead = 0;
352   int                 iNReady = 0;
353   int                 iNToWatch = 0;
354   int                 iNWritten = 0;
355 #define PIPE_STDIN_INDEX 0
356 #define PIPE_STDOUT_INDEX 1
357 #define PIPE_STDERR_INDEX 2
358 #define PIPE_READER_INDEX 0
359 #define PIPE_WRITER_INDEX 1
360   int                 aaiPipes[3][2];
361   KLEL_COMMAND       *psCommand = NULL;
362 #ifdef USE_EMBEDDED_PERL
363   SV                 *psScalarValue = NULL;
364 #endif
365 
366   /*-
367    *********************************************************************
368    *
369    * See if there's a hook that needs to be executed.
370    *
371    *********************************************************************
372    */
373   psHook = HookMatchHook(psProperties->psFileHookList, psFTFileData);
374   if (!psHook)
375   {
376     return ER_OK;
377   }
378 
379   /*-
380    *********************************************************************
381    *
382    * Let them know we have a match.
383    *
384    *********************************************************************
385    */
386   if (psProperties->iLogLevel <= MESSAGE_DEBUGGER)
387   {
388     snprintf(acMessage, MESSAGE_SIZE, "FileHook=%s RawPath=%s", psHook->pcExpression, psFTFileData->pcRawPath);
389     MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_DEBUGGER, MESSAGE_DEBUGGER_STRING, acMessage);
390   }
391 
392   /*-
393    *********************************************************************
394    *
395    * Create stdin/stdout/stderr pipes.
396    *
397    *********************************************************************
398    */
399   for (i = 0; i < 3; i++)
400   {
401     iError = pipe(aaiPipes[i]);
402     if (iError == -1)
403     {
404       snprintf(pcError, MESSAGE_SIZE, "%s: pipe(): %s", acRoutine, strerror(errno));
405       return ER;
406     }
407   }
408 
409   /*-
410    *********************************************************************
411    *
412    * Fork off a kid to run the specified command.
413    *
414    *********************************************************************
415    */
416   iKidPid = fork();
417   if (iKidPid == -1)
418   {
419     snprintf(pcError, MESSAGE_SIZE, "%s: fork(): %s", acRoutine, strerror(errno));
420     return ER;
421   }
422   else if (iKidPid == 0)
423   {
424     close(aaiPipes[PIPE_STDIN_INDEX][PIPE_WRITER_INDEX]);
425     close(aaiPipes[PIPE_STDOUT_INDEX][PIPE_READER_INDEX]);
426     close(aaiPipes[PIPE_STDERR_INDEX][PIPE_READER_INDEX]);
427     dup2(aaiPipes[PIPE_STDIN_INDEX][PIPE_READER_INDEX], 0);
428     dup2(aaiPipes[PIPE_STDOUT_INDEX][PIPE_WRITER_INDEX], 1);
429     dup2(aaiPipes[PIPE_STDERR_INDEX][PIPE_WRITER_INDEX], 2);
430 
431 /* NOTE: Perhaps this code perhaps should be placed above the fork(), but having it here eliminates the need to have KlelFreeCommand() before each return in the parent. */
432     psHook->psContext->pvData = (void *)psFTFileData;
433     psCommand = KlelGetCommand(psHook->psContext);
434     if (psCommand == NULL)
435     {
436       snprintf(acMessage, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: KlelGetCommand(): Hook (%s) failed to produce a valid command (%s).", acRoutine, psFTFileData->pcNeuteredPath, psHook->pcName, KlelGetError(psHook->psContext));
437       MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_INFORMATION, MESSAGE_HOOK_STRING, acMessage);
438       exit(-2);
439     }
440 
441     if (strcmp(psCommand->acInterpreter, "exec") == 0)
442     {
443       execv(psCommand->acProgram, psCommand->ppcArgumentVector);
444       snprintf(acMessage, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Interpreter = [%s]: Hook (%s) failed to execute \"%s\" (%s).", acRoutine, psFTFileData->pcNeuteredPath, psCommand->acInterpreter, psHook->pcName, psCommand->acProgram, strerror(errno));
445       MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_INFORMATION, MESSAGE_HOOK_STRING, acMessage);
446       KlelFreeCommand(psCommand);
447       exit(-1);
448     }
449 #ifdef USE_EMBEDDED_PERL
450     else if (strcmp(psCommand->acInterpreter, "perl") == 0)
451     {
452       dSP;
453       ENTER;
454 //    SAVETMPS; /* This should not be needed since mortal variables are not created/used. */
455       call_argv("Embed::Persistent::EvalScript", G_EVAL | G_KEEPERR | G_SCALAR, psCommand->ppcArgumentVector); /* Do not use G_DISCARD here so that Perl stack items are preserved. */
456       SPAGAIN;
457       psScalarValue = POPs;
458       PUTBACK;
459       if (SvTRUE(ERRSV))
460       {
461         iError = -1;
462         snprintf(acMessage, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Interpreter = [%s]: Hook (%s) failed to execute \"%s\" (%s).", acRoutine, psFTFileData->pcNeuteredPath, psCommand->acInterpreter, psHook->pcName, psCommand->acProgram, SvPV_nolen(ERRSV));
463         MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_INFORMATION, MESSAGE_HOOK_STRING, acMessage);
464       }
465       else
466       {
467         if (SvOK(psScalarValue) && SvIOK(psScalarValue)) /* Expect the Perl stack to contain exactly one defined integer value. */
468         {
469           iError = SvIV(psScalarValue);
470         }
471         else
472         {
473           iError = -1;
474           snprintf(acMessage, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Interpreter = [%s]: Hook (%s) failed to execute \"%s\" (No $@).", acRoutine, psFTFileData->pcNeuteredPath, psCommand->acInterpreter, psHook->pcName, psCommand->acProgram);
475           MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_INFORMATION, MESSAGE_HOOK_STRING, acMessage);
476         }
477       }
478 //    FREETMPS; /* This should not be needed since mortal variables are not created/used. */
479       LEAVE;
480       PL_perl_destruct_level = 1; /* This must be set to 1 since perl_construct() reset it to 0 according to perlembed. */
481       perl_destruct(psProperties->psMyPerl);
482       perl_free(psProperties->psMyPerl);
483       PERL_SYS_TERM();
484       KlelFreeCommand(psCommand);
485       exit(iError);
486     }
487 #endif
488 #ifdef USE_EMBEDDED_PYTHON
489     else if (strcmp(psCommand->acInterpreter, "python") == 0)
490     {
491       iError = MapExecutePythonScript(psProperties, psHook, psCommand, psFTFileData, acMessage);
492       KlelFreeCommand(psCommand);
493       Py_Finalize();
494       exit(iError);
495     }
496 #endif
497     else if (strcmp(psCommand->acInterpreter, "system") == 0)
498     {
499       if
500       (
501            psCommand->ppcArgumentVector[0] != NULL
502         && psCommand->ppcArgumentVector[1] != NULL
503         && strcmp(psCommand->ppcArgumentVector[0], "") == 0
504         && strcmp(psCommand->ppcArgumentVector[1], "") != 0
505       )
506       {
507         iError = system(psCommand->ppcArgumentVector[1]);
508         if (iError == -1)
509         {
510           snprintf(acMessage, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Interpreter = [%s]: Hook (%s) failed to execute \"%s\" (%s).", acRoutine, psFTFileData->pcNeuteredPath, psCommand->acInterpreter, psHook->pcName, psCommand->ppcArgumentVector[1], strerror(errno));
511           MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_INFORMATION, MESSAGE_HOOK_STRING, acMessage);
512         }
513       }
514       else
515       {
516         iError = -1;
517         snprintf(acMessage, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Interpreter = [%s]: Hook (%s) failed to execute (Usage: eval(\"system\", \"\", \"<command>\")).", acRoutine, psFTFileData->pcNeuteredPath, psCommand->acInterpreter, psHook->pcName);
518         MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_INFORMATION, MESSAGE_HOOK_STRING, acMessage);
519       }
520       KlelFreeCommand(psCommand);
521       exit(iError);
522     }
523     else
524     {
525       snprintf(acMessage, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Interpreter = [%s]: Hook (%s) requires an unsupported interpreter.", acRoutine, psFTFileData->pcNeuteredPath, psCommand->acInterpreter, psHook->pcName);
526       MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_INFORMATION, MESSAGE_HOOK_STRING, acMessage);
527       KlelFreeCommand(psCommand);
528       exit(-1);
529     }
530   }
531   else
532   {
533     close(aaiPipes[PIPE_STDIN_INDEX][PIPE_READER_INDEX]);
534     close(aaiPipes[PIPE_STDOUT_INDEX][PIPE_WRITER_INDEX]);
535     close(aaiPipes[PIPE_STDERR_INDEX][PIPE_WRITER_INDEX]);
536 
537     close(aaiPipes[PIPE_STDIN_INDEX][PIPE_WRITER_INDEX]); /* There's nothing to send to the kid on stdin. */
538 
539     FD_ZERO(&sFdSaveSet);
540     FD_SET(aaiPipes[PIPE_STDOUT_INDEX][PIPE_READER_INDEX], &sFdSaveSet);
541     iNToWatch++;
542     FD_SET(aaiPipes[PIPE_STDERR_INDEX][PIPE_READER_INDEX], &sFdSaveSet);
543     iNToWatch++;
544 
545     while (iNToWatch > 0)
546     {
547       struct timeval sTvTimeout = { 1, 0 }; /* The Linux implementation of select() modifies the timeout value, so it must be initialized before each call. */
548       sFdReadSet = sFdSaveSet;
549       iNReady = select(FD_SETSIZE, &sFdReadSet, NULL, NULL, &sTvTimeout);
550       if (iNReady < 0)
551       {
552         snprintf(pcError, MESSAGE_SIZE, "%s: select(): Hook (%s) failed to select a file descriptor (%s).", acRoutine, psHook->pcName, strerror(errno));
553         return ER;
554       }
555       else if (iNReady == 0)
556       {
557         /* Select timeout. */
558       }
559       else
560       {
561         for(iFd = 0; iFd < FD_SETSIZE; iFd++)
562         {
563           if (FD_ISSET(iFd, &sFdReadSet))
564           {
565             iNRead = read(iFd, acData, PIPE_READ_SIZE);
566             if (iNRead < 0)
567             {
568               snprintf(pcError, MESSAGE_SIZE, "%s: read(): Hook (%s) failed to read file descriptor %d (%s)", acRoutine, psHook->pcName, iFd, strerror(errno));
569               return ER;
570             }
571             else if (iNRead == 0)
572             {
573               FD_CLR(iFd, &sFdSaveSet);
574               iNToWatch--;
575               close(iFd);
576             }
577             else
578             {
579               if (iFd == aaiPipes[PIPE_STDOUT_INDEX][PIPE_READER_INDEX])
580               {
581                 iNWritten = fwrite(acData, 1, iNRead, psProperties->pFileOut);
582                 if (iNWritten != iNRead)
583                 {
584                   snprintf(pcError, MESSAGE_SIZE, "%s: fwrite(): Hook (%s) failed to write on file descriptor %d (%s)", acRoutine, psHook->pcName, iFd, strerror(errno));
585                   return ER;
586                 }
587                 MD5Cycle(&psProperties->sOutFileHashContext, (unsigned char *) acData, iNWritten);
588               }
589               else if (iFd == aaiPipes[PIPE_STDERR_INDEX][PIPE_READER_INDEX])
590               {
591                 iNWritten = fwrite(acData, 1, iNRead, psProperties->pFileLog);
592                 if (iNWritten != iNRead)
593                 {
594                   snprintf(pcError, MESSAGE_SIZE, "%s: fwrite(): Hook (%s) failed to write on file descriptor %d (%s)", acRoutine, psHook->pcName, iFd, strerror(errno));
595                   return ER;
596                 }
597               }
598               else
599               {
600                 /* Empty */
601               }
602             }
603           }
604         }
605       }
606     }
607 
608     iKidPid = wait(&iKidStatus);
609     if (iKidPid == -1)
610     {
611       snprintf(pcError, MESSAGE_SIZE, "%s: wait(): %s", acRoutine, strerror(errno));
612       return ER;
613     }
614     iKidReturn = WEXITSTATUS(iKidStatus);
615 //  iKidSignal = WTERMSIG(iKidStatus);
616 //  iKidDumped = WCOREDUMP(iKidStatus);
617     if (!KlelIsSuccessReturnCode(psHook->psContext, iKidReturn))
618     {
619       snprintf(acMessage, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: wait(): Hook (%s) returned an unexpected exit code (%d).", acRoutine, psFTFileData->pcNeuteredPath, psHook->pcName, iKidReturn);
620       ErrorHandler(ER_Failure, acMessage, ERROR_FAILURE);
621     }
622   }
623 
624   return ER_OK;
625 }
626 #endif
627 
628 
629 #ifdef USE_EMBEDDED_PYTHON
630 /*-
631  ***********************************************************************
632  *
633  * MapExecutePythonScript
634  *
635  ***********************************************************************
636  */
637 int
MapExecutePythonScript(FTIMES_PROPERTIES * psProperties,HOOK_LIST * psHook,KLEL_COMMAND * psCommand,FTIMES_FILE_DATA * psFTFileData,char * pcMessage)638 MapExecutePythonScript(FTIMES_PROPERTIES *psProperties, HOOK_LIST *psHook, KLEL_COMMAND *psCommand, FTIMES_FILE_DATA *psFTFileData, char *pcMessage)
639 {
640   int                 iError = -1;
641   PyObject           *psPyLocals = NULL;
642   PyObject           *psPyResult = NULL;
643   PyObject           *psPyException = NULL;
644   PyObject           *psPyExceptionType = NULL;
645   wchar_t           **ppwcArgumentVector = NULL;
646 
647   /*-
648    *********************************************************************
649    *
650    * Obtain references to a new dictionary to store local variables.
651    *
652    *********************************************************************
653    */
654   psPyLocals = PyDict_New();
655   if (psPyLocals == NULL)
656   {
657     snprintf(pcMessage, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Interpreter = [%s]: Hook (%s) failed to execute \"%s\" (could not allocate locals).", "MapExecutePythonScript()", psFTFileData->pcNeuteredPath, psCommand->acInterpreter, psHook->pcName, psCommand->acProgram);
658     MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_INFORMATION, MESSAGE_HOOK_STRING, pcMessage);
659     return iError;
660   }
661 
662   /*-
663    *********************************************************************
664    *
665    * Convert and marshall the script's arguments.
666    *
667    *********************************************************************
668    */
669   ppwcArgumentVector = MapConvertPythonArguments(psCommand->szArgumentCount, psCommand->ppcArgumentVector);
670   if (ppwcArgumentVector == NULL)
671   {
672     snprintf(pcMessage, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Interpreter = [%s]: Hook (%s) failed to execute \"%s\" (could not convert arguments).", "MapExecutePythonScript()", psFTFileData->pcNeuteredPath, psCommand->acInterpreter, psHook->pcName, psCommand->acProgram);
673     MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_INFORMATION, MESSAGE_HOOK_STRING, pcMessage);
674     Py_XDECREF(psPyLocals);
675     return iError;
676   }
677   PySys_SetArgvEx(psCommand->szArgumentCount, ppwcArgumentVector, 0);
678 
679   /*-
680    *********************************************************************
681    *
682    * Execute the script. Note that we don't care about the result of
683    * the module evaluation since it is generally None. However, we do
684    * care if a SystemExit exception was raised, meaning a return code
685    * was provided.
686    *
687    *********************************************************************
688    */
689   psPyResult = PyEval_EvalCode(psHook->psPyScript, psProperties->psPyGlobals, psPyLocals);
690   psPyExceptionType = PyErr_Occurred();
691   if (psPyExceptionType != NULL)
692   {
693     if (PyErr_ExceptionMatches(PyExc_SystemExit))
694     {
695       PyErr_Fetch(&psPyExceptionType, &psPyException, NULL);
696       if (PyLong_Check(psPyException))
697       {
698         iError = (PyLong_AsLong(psPyException) > INT_MAX) ? -1 : ((int)PyLong_AsLong(psPyException));
699       }
700     }
701     Py_XDECREF(psPyException);
702     PyErr_Clear();
703   }
704   else
705   {
706     iError = 0;
707   }
708 
709   /*-
710    *********************************************************************
711    *
712    * Release any remaining local resources.
713    *
714    *********************************************************************
715    */
716   Py_XDECREF(psPyResult);
717   Py_XDECREF(psPyLocals);
718   MapFreePythonArguments(psCommand->szArgumentCount, ppwcArgumentVector);
719 
720   return iError;
721 }
722 #endif
723 
724 
725 /*-
726  ***********************************************************************
727  *
728  * MapGetDirectoryCount
729  *
730  ***********************************************************************
731  */
732 int
MapGetDirectoryCount()733 MapGetDirectoryCount()
734 {
735   return giDirectories;
736 }
737 
738 
739 /*-
740  ***********************************************************************
741  *
742  * MapGetFileCount
743  *
744  ***********************************************************************
745  */
746 int
MapGetFileCount()747 MapGetFileCount()
748 {
749   return giFiles;
750 }
751 
752 
753 /*-
754  ***********************************************************************
755  *
756  * MapGetSpecialCount
757  *
758  ***********************************************************************
759  */
760 int
MapGetSpecialCount()761 MapGetSpecialCount()
762 {
763   return giSpecial;
764 }
765 
766 
767 #ifdef WINNT
768 /*-
769  ***********************************************************************
770  *
771  * MapGetStreamCount
772  *
773  ***********************************************************************
774  */
775 int
MapGetStreamCount()776 MapGetStreamCount()
777 {
778   return giStreams;
779 }
780 #endif
781 
782 
783 /*-
784  ***********************************************************************
785  *
786  * MapGetRecordCount
787  *
788  ***********************************************************************
789  */
790 int
MapGetRecordCount()791 MapGetRecordCount()
792 {
793   return giRecords;
794 }
795 
796 
797 /*-
798  ***********************************************************************
799  *
800  * MapGetIncompleteRecordCount
801  *
802  ***********************************************************************
803  */
804 int
MapGetIncompleteRecordCount()805 MapGetIncompleteRecordCount()
806 {
807   return giIncompleteRecords;
808 }
809 
810 
811 #ifdef UNIX
812 /*-
813  ***********************************************************************
814  *
815  * MapTree
816  *
817  ***********************************************************************
818  */
819 int
MapTree(FTIMES_PROPERTIES * psProperties,FTIMES_FILE_DATA * psFTTreeData,char * pcError)820 MapTree(FTIMES_PROPERTIES *psProperties, FTIMES_FILE_DATA *psFTTreeData, char *pcError)
821 {
822   const char          acRoutine[] = "MapTree()";
823   char                acLocalError[MESSAGE_SIZE] = "";
824   char                acLinkData[FTIMES_MAX_PATH] = "";
825   char                acMessage[MESSAGE_SIZE] = "";
826   char               *pcParentPath = NULL;
827   DIR                *psDir = NULL;
828   FTIMES_FILE_DATA   *psFTFileData = NULL;
829   FTIMES_HASH_DATA    sFTHashData;
830   int                 iError = 0;
831   int                 iNewFSType = 0;
832   struct dirent      *psDirEntry = NULL;
833   struct stat         sStatPDirectory;
834   struct stat        *psStatPDirectory = NULL;
835   struct stat        *psStatCDirectory = NULL;
836 
837   /*-
838    *********************************************************************
839    *
840    * Let them know where we're at.
841    *
842    *********************************************************************
843    */
844   if (psProperties->iLogLevel <= MESSAGE_WAYPOINT)
845   {
846     snprintf(acMessage, MESSAGE_SIZE, "FS=%s Directory=%s", gaacFSType[psFTTreeData->iFSType], psFTTreeData->pcRawPath);
847     MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_WAYPOINT, MESSAGE_WAYPOINT_STRING, acMessage);
848   }
849 
850   /*-
851    *********************************************************************
852    *
853    * Conditionally start directory hashes.
854    *
855    *********************************************************************
856    */
857   if (psProperties->bHashDirectories)
858   {
859     MapDirHashAlpha(psProperties, &sFTHashData);
860   }
861 
862   /*-
863    *********************************************************************
864    *
865    * Determine current and parent path attributes. These are used later
866    * to check that "." and ".." are really hard links to the current
867    * and parent paths, respectively. This is done to ensure that "."
868    * or ".." don't point to a hidden file or directory. The attacker
869    * would necessarily have to modify the directory structure for this
870    * to be the case.
871    *
872    *********************************************************************
873    */
874   if (psFTTreeData->psParent == NULL)
875   {
876     pcParentPath = MapDirname(psFTTreeData->pcRawPath);
877     if (lstat((pcParentPath == NULL) ? "" : pcParentPath, &sStatPDirectory) == ER)
878     {
879       psStatPDirectory = NULL;
880     }
881     else
882     {
883       psStatPDirectory = &sStatPDirectory;
884     }
885   }
886   else
887   {
888     if (psFTTreeData->psParent->ulAttributeMask == 0)
889     {
890       psStatPDirectory = NULL;
891     }
892     else
893     {
894       psStatPDirectory = &psFTTreeData->psParent->sStatEntry;
895     }
896   }
897 
898   if (psFTTreeData->ulAttributeMask == 0)
899   {
900     psStatCDirectory = NULL;
901   }
902   else
903   {
904     psStatCDirectory = &psFTTreeData->sStatEntry;
905   }
906 
907   /*-
908    *********************************************************************
909    *
910    * Open up the directory to be scanned.
911    *
912    *********************************************************************
913    */
914   if ((psDir = opendir(psFTTreeData->pcRawPath)) == NULL)
915   {
916     snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, psFTTreeData->pcNeuteredPath, strerror(errno));
917     ErrorHandler(ER_opendir, pcError, ERROR_FAILURE);
918     return ER_opendir;
919   }
920 
921   /*-
922    *********************************************************************
923    *
924    * Loop through the list of directory entries. Note that errno must
925    * be cleared before each readdir() call so that its value can be
926    * checked after the function returns. Read the comment that follows
927    * this loop for more details.
928    *
929    *********************************************************************
930    */
931   for (errno = 0; (psDirEntry = readdir(psDir)) != NULL; errno = 0, MapFreeFTFileData(psFTFileData))
932   {
933     /*-
934      *******************************************************************
935      *
936      * Create and initialize a new file data structure.
937      *
938      *******************************************************************
939      */
940     psFTFileData = MapNewFTFileData(psFTTreeData, psDirEntry->d_name, acLocalError);
941     if (psFTFileData == NULL)
942     {
943 /* FIXME Need to prevent truncation in the case where the new path is larger than MESSAGE_SIZE. */
944       char  acTempError[MESSAGE_SIZE] = "";
945       char *pcNeuteredName = SupportNeuterString(psDirEntry->d_name, strlen(psDirEntry->d_name), acTempError);
946       if (pcNeuteredName)
947       {
948         snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s%s%s]: %s", acRoutine, psFTTreeData->pcNeuteredPath, FTIMES_SLASH, pcNeuteredName, acLocalError);
949         ErrorHandler(ER_Failure, pcError, ERROR_FAILURE);
950         free(pcNeuteredName);
951       }
952       else
953       {
954         snprintf(pcError, MESSAGE_SIZE, "%s: FallbackPath = [%s%s%s]: %s", acRoutine, psFTTreeData->pcRawPath, FTIMES_SLASH, psDirEntry->d_name, acLocalError);
955         ErrorHandler(ER_Failure, pcError, ERROR_FAILURE);
956       }
957       continue;
958     }
959 
960     /*-
961      *******************************************************************
962      *
963      * Get file attributes. This fills in several structure members.
964      *
965      *******************************************************************
966      */
967     MapGetAttributes(psFTFileData);
968     if (!psFTFileData->iFileExists)
969     {
970       continue;
971     }
972 
973     /*-
974      *******************************************************************
975      *
976      * If the new path is in the exclude list, skip it.
977      *
978      *******************************************************************
979      */
980     if (SupportMatchExclude(psProperties->psExcludeList, psFTFileData->pcRawPath) != NULL)
981     {
982       continue;
983     }
984 
985 #ifdef USE_PCRE
986     /*-
987      *******************************************************************
988      *
989      * If the new path is matched by an exclude filter, continue with
990      * the next entry. If the new path is matched by an include
991      * filter, set a flag, but keep going. Include filters do not get
992      * applied until the file's type is known. This is because
993      * directories must be traversed before they can be filtered.
994      *
995      *******************************************************************
996      */
997     if (psProperties->psExcludeFilterList)
998     {
999       FILTER_LIST *psFilter = SupportMatchFilter(psProperties->psExcludeFilterList, psFTFileData->pcRawPath);
1000       if (psFilter != NULL)
1001       {
1002         if (psProperties->iLogLevel <= MESSAGE_DEBUGGER)
1003         {
1004           snprintf(acMessage, MESSAGE_SIZE, "ExcludeFilter=%s RawPath=%s", psFilter->pcFilter, psFTFileData->pcRawPath);
1005           MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_DEBUGGER, MESSAGE_DEBUGGER_STRING, acMessage);
1006         }
1007         continue;
1008       }
1009     }
1010 
1011     if (psProperties->psIncludeFilterList)
1012     {
1013       FILTER_LIST *psFilter = SupportMatchFilter(psProperties->psIncludeFilterList, psFTFileData->pcRawPath);
1014       if (psFilter == NULL)
1015       {
1016         psFTFileData->iFiltered = 1;
1017       }
1018       else
1019       {
1020         if (psProperties->iLogLevel <= MESSAGE_DEBUGGER)
1021         {
1022           snprintf(acMessage, MESSAGE_SIZE, "IncludeFilter=%s RawPath=%s", psFilter->pcFilter, psFTFileData->pcRawPath);
1023           MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_DEBUGGER, MESSAGE_DEBUGGER_STRING, acMessage);
1024         }
1025       }
1026     }
1027 #endif
1028 
1029     /*-
1030      *******************************************************************
1031      *
1032      * No attributes means no file type, so we have to stop short.
1033      *
1034      *******************************************************************
1035      */
1036     if (psFTFileData->ulAttributeMask == 0)
1037     {
1038 #ifdef USE_PCRE
1039       /*-
1040        *****************************************************************
1041        *
1042        * If this path has been filtered, we're done.
1043        *
1044        *****************************************************************
1045        */
1046       if (psFTFileData->iFiltered)
1047       {
1048         continue;
1049       }
1050 #endif
1051 
1052       /*-
1053        *****************************************************************
1054        *
1055        * If this is "." or "..", we're done.
1056        *
1057        *****************************************************************
1058        */
1059       if (strcmp(psDirEntry->d_name, FTIMES_DOT) == 0 || strcmp(psDirEntry->d_name, FTIMES_DOTDOT) == 0)
1060       {
1061         continue;
1062       }
1063 
1064       /*-
1065        *****************************************************************
1066        *
1067        * Conditionally update directory hashes.
1068        *
1069        *****************************************************************
1070        */
1071       if (psProperties->bHashDirectories)
1072       {
1073         MapDirHashCycle(psProperties, &sFTHashData, psFTFileData);
1074       }
1075 
1076       /*-
1077        *****************************************************************
1078        *
1079        * Record the collected data. In this case we only have a name.
1080        *
1081        *****************************************************************
1082        */
1083       iError = MapWriteRecord(psProperties, psFTFileData, acLocalError);
1084       if (iError != ER_OK)
1085       {
1086         snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
1087         ErrorHandler(iError, pcError, ERROR_CRITICAL);
1088       }
1089 
1090       continue;
1091     }
1092 
1093     /*-
1094      *******************************************************************
1095      *
1096      * If this is "." and it has the same inode as the current path,
1097      * fall through to the bottom of the loop.
1098      *
1099      *******************************************************************
1100      *
1101      * If this is ".." and it has the same inode as the parent path,
1102      * fall through to the bottom of the loop.
1103      *
1104      *******************************************************************
1105      *
1106      * Otherwise, attempt to process the record.
1107      *
1108      *******************************************************************
1109      */
1110     if (strcmp(psDirEntry->d_name, FTIMES_DOT) == 0)
1111     {
1112       if (psStatCDirectory != NULL)
1113       {
1114         if (psFTFileData->sStatEntry.st_ino != psStatCDirectory->st_ino)
1115         {
1116           snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Inode mismatch between '.' and current directory.", acRoutine, psFTFileData->pcNeuteredPath);
1117           ErrorHandler(ER_BadValue, pcError, ERROR_WARNING);
1118         }
1119       }
1120       else
1121       {
1122         snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Not enough information on current directory to compare it to '.'.", acRoutine, psFTFileData->pcNeuteredPath);
1123         ErrorHandler(ER_Warning, pcError, ERROR_WARNING);
1124       }
1125     }
1126     else if (strcmp(psDirEntry->d_name, FTIMES_DOTDOT) == 0)
1127     {
1128       if (psStatPDirectory != NULL)
1129       {
1130         if (psFTFileData->sStatEntry.st_ino != psStatPDirectory->st_ino)
1131         {
1132           snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Inode mismatch between '..' and parent directory.", acRoutine, psFTFileData->pcNeuteredPath);
1133           ErrorHandler(ER_BadValue, pcError, ERROR_WARNING);
1134         }
1135       }
1136       else
1137       {
1138         snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Not enough information on parent directory to compare it to '..'.", acRoutine, psFTFileData->pcNeuteredPath);
1139         ErrorHandler(ER_Warning, pcError, ERROR_WARNING);
1140       }
1141     }
1142     else
1143     {
1144       /*-
1145        *****************************************************************
1146        *
1147        * Map directories, files, and links.
1148        *
1149        *****************************************************************
1150        */
1151       if (S_ISDIR(psFTFileData->sStatEntry.st_mode))
1152       {
1153         giDirectories++;
1154 #ifdef USE_XMAGIC
1155         if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MAGIC))
1156         {
1157           snprintf(psFTFileData->acType, FTIMES_FILETYPE_BUFSIZE, "special/directory");
1158         }
1159 #endif
1160         /*-
1161          ***************************************************************
1162          *
1163          * Check for a device crossing. Process new file systems if
1164          * they are supported, and process remote file systems when
1165          * AnalyzeRemoteFiles is enabled.
1166          *
1167          ***************************************************************
1168          */
1169         if (psStatCDirectory != NULL)
1170         {
1171           if (psFTFileData->sStatEntry.st_dev != psStatCDirectory->st_dev)
1172           {
1173             snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Crossing a device boundary.", acRoutine, psFTFileData->pcNeuteredPath);
1174             ErrorHandler(ER_Warning, pcError, ERROR_WARNING);
1175             /*-
1176              *************************************************************
1177              *
1178              * WHEN AN ERROR OCCURS OR THE FILE SYSTEM IS UNSUPPORTED,
1179              * WARN THE USER AND CONTINUE. IF THE FILE SYSTEM IS NFS AND
1180              * REMOTE SCANNING IS NOT ENABLED, WARN THE USER AND CONTINUE.
1181              * IF ONE OF THESE CASES SHOULD ARISE, DO NOT WRITE A ENTRY
1182              * IN THE OUTPUT FILE, AND DO NOT UPDATE THE DIRECTORY HASH.
1183              *
1184              *************************************************************
1185              */
1186             iNewFSType = GetFileSystemType(psFTFileData->pcRawPath, acLocalError);
1187             if (iNewFSType == ER || iNewFSType == FSTYPE_UNSUPPORTED)
1188             {
1189               snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, psFTFileData->pcNeuteredPath, acLocalError);
1190               ErrorHandler(ER_Warning, pcError, ERROR_WARNING);
1191               continue;
1192             }
1193             if (!psProperties->bAnalyzeRemoteFiles && (iNewFSType == FSTYPE_NFS || iNewFSType == FSTYPE_NFS3 || iNewFSType == FSTYPE_SMB))
1194             {
1195               snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Excluding remote file system.", acRoutine, psFTFileData->pcNeuteredPath);
1196               ErrorHandler(ER_Warning, pcError, ERROR_WARNING);
1197               continue;
1198             }
1199             psFTFileData->iFSType = iNewFSType;
1200           }
1201         }
1202         else
1203         {
1204           snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Not enough information on current directory to determine a device boundary crossing.", acRoutine, psFTFileData->pcNeuteredPath);
1205           ErrorHandler(ER_Warning, pcError, ERROR_WARNING);
1206           psFTFileData->iFSType = FSTYPE_NA;
1207         }
1208         if (psProperties->bEnableRecursion)
1209         {
1210           MapTree(psProperties, psFTFileData, acLocalError);
1211         }
1212 #ifdef USE_PCRE
1213         if (psFTFileData->iFiltered) /* We're done. */
1214         {
1215           continue;
1216         }
1217 #endif
1218       }
1219       else if (S_ISREG(psFTFileData->sStatEntry.st_mode))
1220       {
1221 #ifdef USE_PCRE
1222         if (psFTFileData->iFiltered) /* We're done. */
1223         {
1224           continue;
1225         }
1226 #endif
1227         giFiles++;
1228         if (psProperties->iLastAnalysisStage > 0)
1229         {
1230           iError = AnalyzeFile(psProperties, psFTFileData, acLocalError);
1231           if (iError != ER_OK)
1232           {
1233             snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, psFTFileData->pcNeuteredPath, acLocalError);
1234             ErrorHandler(iError, pcError, ERROR_FAILURE);
1235           }
1236         }
1237       }
1238       else if (S_ISLNK(psFTFileData->sStatEntry.st_mode))
1239       {
1240 #ifdef USE_PCRE
1241         if (psFTFileData->iFiltered) /* We're done. */
1242         {
1243           continue;
1244         }
1245 #endif
1246         giSpecial++;
1247         if (psProperties->bHashSymbolicLinks)
1248         {
1249           iError = readlink(psFTFileData->pcRawPath, acLinkData, FTIMES_MAX_PATH - 1);
1250           if (iError == ER)
1251           {
1252             snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Unreadable Symbolic Link: %s", acRoutine, psFTFileData->pcNeuteredPath, strerror(errno));
1253             ErrorHandler(ER_readlink, pcError, ERROR_FAILURE);
1254           }
1255           else
1256           {
1257             acLinkData[iError] = 0; /* Readlink does not append a NULL. */
1258             if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MD5))
1259             {
1260               MD5HashString((unsigned char *) acLinkData, strlen(acLinkData), psFTFileData->aucFileMd5);
1261             }
1262             if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA1))
1263             {
1264               SHA1HashString((unsigned char *) acLinkData, strlen(acLinkData), psFTFileData->aucFileSha1);
1265             }
1266             if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA256))
1267             {
1268               SHA256HashString((unsigned char *) acLinkData, strlen(acLinkData), psFTFileData->aucFileSha256);
1269             }
1270           }
1271         }
1272 #ifdef USE_XMAGIC
1273         if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MAGIC))
1274         {
1275           iError = XMagicTestSpecial(psFTFileData->pcRawPath, &psFTFileData->sStatEntry, psFTFileData->acType, FTIMES_FILETYPE_BUFSIZE, acLocalError);
1276           if (iError != ER_OK)
1277           {
1278             snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, psFTFileData->pcNeuteredPath, acLocalError);
1279             ErrorHandler(iError, pcError, ERROR_FAILURE);
1280           }
1281         }
1282 #endif
1283       }
1284       else
1285       {
1286 #ifdef USE_PCRE
1287         if (psFTFileData->iFiltered) /* We're done. */
1288         {
1289           continue;
1290         }
1291 #endif
1292         giSpecial++;
1293 #ifdef USE_XMAGIC
1294         if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MAGIC))
1295         {
1296           iError = XMagicTestSpecial(psFTFileData->pcRawPath, &psFTFileData->sStatEntry, psFTFileData->acType, FTIMES_FILETYPE_BUFSIZE, acLocalError);
1297           if (iError != ER_OK)
1298           {
1299             snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, psFTFileData->pcNeuteredPath, acLocalError);
1300             ErrorHandler(iError, pcError, ERROR_FAILURE);
1301           }
1302         }
1303 #endif
1304       }
1305 
1306       /*-
1307        *****************************************************************
1308        *
1309        * Conditionally update directory hashes.
1310        *
1311        *****************************************************************
1312        */
1313       if (psProperties->bHashDirectories)
1314       {
1315         MapDirHashCycle(psProperties, &sFTHashData, psFTFileData);
1316       }
1317 
1318       /*-
1319        *****************************************************************
1320        *
1321        * Record the collected data.
1322        *
1323        *****************************************************************
1324        */
1325       iError = MapWriteRecord(psProperties, psFTFileData, acLocalError);
1326       if (iError != ER_OK)
1327       {
1328         snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
1329         ErrorHandler(iError, pcError, ERROR_CRITICAL);
1330       }
1331 
1332 #ifdef USE_FILE_HOOKS
1333       /*-
1334        *****************************************************************
1335        *
1336        * Conditionally execute hooks for regular files.
1337        *
1338        *****************************************************************
1339        */
1340       if (psProperties->psFileHookList && S_ISREG(psFTFileData->sStatEntry.st_mode))
1341       {
1342         iError = MapExecuteHook(psProperties, psFTFileData, acLocalError);
1343         if (iError != ER_OK)
1344         {
1345           snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, psFTFileData->pcNeuteredPath, acLocalError);
1346           ErrorHandler(iError, pcError, ERROR_CRITICAL);
1347         }
1348       }
1349 #endif
1350     }
1351   }
1352 
1353   /*-
1354    *********************************************************************
1355    *
1356    * A NULL could mean EOD or error. The question is whether or not
1357    * errno is set by readdir(). We explicitly set errno to 0 before
1358    * each call to readdir(). So, in theory if readdir() actually
1359    * fails, then errno should be something other than 0. Unfortunately,
1360    * linux and freebsd man pages aren't explicit about their return
1361    * values for readdir().
1362    *
1363    *********************************************************************
1364    */
1365   if (psDirEntry == NULL && errno != ER_OK)
1366   {
1367     snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, psFTTreeData->pcNeuteredPath, strerror(errno));
1368     ErrorHandler(ER_readdir, pcError, ERROR_FAILURE);
1369   }
1370 
1371   /*-
1372    *********************************************************************
1373    *
1374    * Conditionally complete directory hashes.
1375    *
1376    *********************************************************************
1377    */
1378   if (psProperties->bHashDirectories)
1379   {
1380     MapDirHashOmega(psProperties, &sFTHashData, psFTTreeData);
1381   }
1382 
1383   closedir(psDir);
1384   return ER_OK;
1385 }
1386 #endif
1387 
1388 
1389 #ifdef WIN32
1390 /*-
1391  ***********************************************************************
1392  *
1393  * MapGetFileHandleW
1394  *
1395  ***********************************************************************
1396  */
1397 HANDLE
MapGetFileHandleW(wchar_t * pwcPath)1398 MapGetFileHandleW(wchar_t *pwcPath)
1399 {
1400   /*-
1401    *********************************************************************
1402    *
1403    * This is just a wrapper for CreateFile(). The caller must check
1404    * the return value to ensure that no error has occurred. Directories
1405    * specifically require the FILE_FLAG_BACKUP_SEMANTICS flag. This
1406    * does not seem to affect the opening of regular files. In the
1407    * past, the desired access flag was set to 0, which means device
1408    * query access. However, the flag has been changed to GENERIC_READ,
1409    * which includes READ_CONTROL, so that the information in security
1410    * descriptors (e.g., owner/group SIDs and DACL) can be read as well.
1411    *
1412    * Update 1: GENERIC_READ was causing this function to fail on files
1413    * where it used to work. However, reverting to the previous value
1414    * (i.e., 0) won't work because that would cause GetSecurityInfo()
1415    * to fail. The current value of READ_CONTROL seems to produce the
1416    * best results.
1417    *
1418    *********************************************************************
1419    */
1420   return CreateFileW(
1421     pwcPath,
1422     READ_CONTROL,
1423     FILE_SHARE_READ,
1424     NULL,
1425     OPEN_EXISTING,
1426     FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN,
1427     NULL
1428     );
1429 }
1430 
1431 
1432 /*-
1433  ***********************************************************************
1434  *
1435  * MapTree
1436  *
1437  ***********************************************************************
1438  */
1439 int
MapTree(FTIMES_PROPERTIES * psProperties,FTIMES_FILE_DATA * psFTTreeData,char * pcError)1440 MapTree(FTIMES_PROPERTIES *psProperties, FTIMES_FILE_DATA *psFTTreeData, char *pcError)
1441 {
1442   const char          acRoutine[] = "MapTree()";
1443   BOOL                bResult;
1444 //THIS CHANGE IS PART OF EXTENDED PREFIX SUPPORT (\\?\)
1445 //  BY_HANDLE_FILE_INFORMATION sFileInfoCurrent;
1446 //  BY_HANDLE_FILE_INFORMATION sFileInfoParent;
1447 //END (\\?\)
1448   char                acLocalError[MESSAGE_SIZE] = "";
1449   char                acMessage[MESSAGE_SIZE] = "";
1450 //THIS CHANGE IS PART OF EXTENDED PREFIX SUPPORT (\\?\)
1451 //  char               *pcParentPath = NULL;
1452 //END (\\?\)
1453   char               *pcMessage = NULL;
1454   FTIMES_FILE_DATA   *psFTFileData = NULL;
1455   FTIMES_HASH_DATA    sFTHashData;
1456 //THIS CHANGE IS PART OF EXTENDED PREFIX SUPPORT (\\?\)
1457 //  HANDLE              hFileCurrent;
1458 //  HANDLE              hFileParent;
1459 //END (\\?\)
1460   HANDLE              hSearch;
1461   int                 iError = 0;
1462   wchar_t             awcSearchPath[FTIMES_MAX_PATH] = L"";
1463 //THIS CHANGE IS PART OF EXTENDED PREFIX SUPPORT (\\?\)
1464 //  wchar_t            *pwcParentPath = NULL;
1465 //END (\\?\)
1466   WIN32_FIND_DATAW    sFindDataW;
1467 
1468   /*-
1469    *********************************************************************
1470    *
1471    * Let them know where we're at.
1472    *
1473    *********************************************************************
1474    */
1475   if (psProperties->iLogLevel <= MESSAGE_WAYPOINT)
1476   {
1477     snprintf(acMessage, MESSAGE_SIZE, "FS=%s Directory=%s", gaacFSType[psFTTreeData->iFSType], psFTTreeData->pcRawPath);
1478     MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_WAYPOINT, MESSAGE_WAYPOINT_STRING, acMessage);
1479   }
1480 
1481   /*-
1482    *********************************************************************
1483    *
1484    * Conditionally start directory hashes.
1485    *
1486    *********************************************************************
1487    */
1488   if (psProperties->bHashDirectories)
1489   {
1490     MapDirHashAlpha(psProperties, &sFTHashData);
1491   }
1492 
1493   /*-
1494    *********************************************************************
1495    *
1496    * Create a search path to match all files (i.e., append "\*").
1497    *
1498    *********************************************************************
1499    */
1500   if (psFTTreeData->iUtf8RawPathLength > FTIMES_MAX_PATH - 3)
1501   {
1502     snprintf(pcError, MESSAGE_SIZE, "%s: SearchPath = [%s%s*]: Length (%d) exceeds %d bytes.", acRoutine, psFTTreeData->pcRawPath, FTIMES_SLASH, psFTTreeData->iUtf8RawPathLength, FTIMES_MAX_PATH - 3);
1503     ErrorHandler(ER_Length, pcError, ERROR_FAILURE);
1504     return ER_Length;
1505   }
1506   _snwprintf(awcSearchPath, FTIMES_MAX_PATH, L"%s%s*", psFTTreeData->pwcRawPath, FTIMES_SLASH_W);
1507 
1508   /*-
1509    *********************************************************************
1510    *
1511    * Begin the search.
1512    *
1513    *********************************************************************
1514    */
1515   hSearch = FindFirstFileW(awcSearchPath, &sFindDataW);
1516   if (hSearch == INVALID_HANDLE_VALUE)
1517   {
1518     ErrorFormatWinxError(GetLastError(), &pcMessage);
1519     snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, psFTTreeData->pcNeuteredPath, pcMessage);
1520     ErrorHandler(ER_FindFirstFile, pcError, ERROR_FAILURE);
1521     return ER_FindFirstFile;
1522   }
1523 
1524   /*-
1525    *********************************************************************
1526    *
1527    * Loop through the list of directory entries.
1528    *
1529    *********************************************************************
1530    */
1531   for (bResult = TRUE; bResult == TRUE; MapFreeFTFileData(psFTFileData), bResult = FindNextFileW(hSearch, &sFindDataW))
1532   {
1533 //THIS CHANGE IS PART OF EXTENDED PREFIX SUPPORT (\\?\)
1534     /*-
1535      *******************************************************************
1536      *
1537      * If this is "." or "..", we're done.
1538      *
1539      *******************************************************************
1540      */
1541     if (wcscmp(sFindDataW.cFileName, FTIMES_DOT_W) == 0 || wcscmp(sFindDataW.cFileName, FTIMES_DOTDOT_W) == 0)
1542     {
1543       continue;
1544     }
1545 //END (\\?\)
1546 
1547     /*-
1548      *******************************************************************
1549      *
1550      * Create and initialize a new file data structure.
1551      *
1552      *******************************************************************
1553      */
1554     psFTFileData = MapNewFTFileDataW(psFTTreeData, sFindDataW.cFileName, acLocalError);
1555     if (psFTFileData == NULL)
1556     {
1557 /* FIXME Need to prevent truncation in the case where the new path is larger than MESSAGE_SIZE. */
1558       char  acTempError[MESSAGE_SIZE] = "";
1559       char *pcUtf8Name = MapWideToUtf8(sFindDataW.cFileName, -1, acTempError);
1560       if (pcUtf8Name)
1561       {
1562         char *pcNeuteredName = SupportNeuterString(pcUtf8Name, strlen(pcUtf8Name), acTempError);
1563         if (pcNeuteredName)
1564         {
1565           snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s%s%s]: %s", acRoutine, psFTTreeData->pcNeuteredPath, FTIMES_SLASH, pcNeuteredName, acLocalError);
1566           ErrorHandler(ER_Failure, pcError, ERROR_FAILURE);
1567           free(pcNeuteredName);
1568         }
1569         else
1570         {
1571           snprintf(pcError, MESSAGE_SIZE, "%s: FallbackPath = [%s%s%s]: %s", acRoutine, psFTTreeData->pcRawPath, FTIMES_SLASH, pcUtf8Name, acLocalError);
1572           ErrorHandler(ER_Failure, pcError, ERROR_FAILURE);
1573         }
1574         free(pcUtf8Name);
1575       }
1576       else
1577       {
1578         snprintf(pcError, MESSAGE_SIZE, "%s: FallbackTree = [%s]: %s", acRoutine, psFTTreeData->pcRawPath, acLocalError);
1579         ErrorHandler(ER_Failure, pcError, ERROR_FAILURE);
1580       }
1581       continue;
1582     }
1583 
1584     /*-
1585      *******************************************************************
1586      *
1587      * Get file attributes. This fills in several structure members.
1588      *
1589      *******************************************************************
1590      */
1591     MapGetAttributes(psFTFileData);
1592     if (!psFTFileData->iFileExists)
1593     {
1594       continue;
1595     }
1596 
1597     /*-
1598      *******************************************************************
1599      *
1600      * If the new path is in the exclude list, skip it.
1601      *
1602      *******************************************************************
1603      */
1604     if (SupportMatchExclude(psProperties->psExcludeList, psFTFileData->pcRawPath) != NULL)
1605     {
1606       continue;
1607     }
1608 
1609 #ifdef USE_PCRE
1610     /*-
1611      *******************************************************************
1612      *
1613      * If the new path is matched by an exclude filter, continue with
1614      * the next entry. If the new path is matched by an include
1615      * filter, set a flag, but keep going. Include filters do not get
1616      * applied until the file's type is known. This is because
1617      * directories must be traversed before they can be filtered.
1618      *
1619      *******************************************************************
1620      */
1621     if (psProperties->psExcludeFilterList)
1622     {
1623       FILTER_LIST *psFilter = SupportMatchFilter(psProperties->psExcludeFilterList, psFTFileData->pcRawPath);
1624       if (psFilter != NULL)
1625       {
1626         if (psProperties->iLogLevel <= MESSAGE_DEBUGGER)
1627         {
1628           snprintf(acMessage, MESSAGE_SIZE, "ExcludeFilter=%s RawPath=%s", psFilter->pcFilter, psFTFileData->pcRawPath);
1629           MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_DEBUGGER, MESSAGE_DEBUGGER_STRING, acMessage);
1630         }
1631         continue;
1632       }
1633     }
1634 
1635     if (psProperties->psIncludeFilterList)
1636     {
1637       FILTER_LIST *psFilter = SupportMatchFilter(psProperties->psIncludeFilterList, psFTFileData->pcRawPath);
1638       if (psFilter == NULL)
1639       {
1640         psFTFileData->iFiltered = 1;
1641       }
1642       else
1643       {
1644         if (psProperties->iLogLevel <= MESSAGE_DEBUGGER)
1645         {
1646           snprintf(acMessage, MESSAGE_SIZE, "IncludeFilter=%s RawPath=%s", psFilter->pcFilter, psFTFileData->pcRawPath);
1647           MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_DEBUGGER, MESSAGE_DEBUGGER_STRING, acMessage);
1648         }
1649       }
1650     }
1651 #endif
1652 
1653     /*-
1654      *******************************************************************
1655      *
1656      * No attributes means no file type, so we have to stop short.
1657      *
1658      *******************************************************************
1659      */
1660     if (psFTFileData->ulAttributeMask == 0)
1661     {
1662 #ifdef USE_PCRE
1663       /*-
1664        *****************************************************************
1665        *
1666        * If this path has been filtered, we're done.
1667        *
1668        *****************************************************************
1669        */
1670       if (psFTFileData->iFiltered)
1671       {
1672         continue;
1673       }
1674 #endif
1675 
1676 //THIS CHANGE IS PART OF EXTENDED PREFIX SUPPORT (\\?\)
1677 //      /*-
1678 //       *****************************************************************
1679 //       *
1680 //       * If this is "." or "..", we're done.
1681 //       *
1682 //       *****************************************************************
1683 //       */
1684 //      if (wcscmp(sFindDataW.cFileName, FTIMES_DOT_W) == 0 || wcscmp(sFindDataW.cFileName, FTIMES_DOTDOT_W) == 0)
1685 //      {
1686 //        continue;
1687 //      }
1688 //END (\\?\)
1689 
1690       /*-
1691        *****************************************************************
1692        *
1693        * Conditionally update directory hashes.
1694        *
1695        *****************************************************************
1696        */
1697       if (psProperties->bHashDirectories)
1698       {
1699         MapDirHashCycle(psProperties, &sFTHashData, psFTFileData);
1700       }
1701 
1702       /*-
1703        *****************************************************************
1704        *
1705        * Record the collected data. In this case we only have a name.
1706        *
1707        *****************************************************************
1708        */
1709       iError = MapWriteRecord(psProperties, psFTFileData, acLocalError);
1710       if (iError != ER_OK)
1711       {
1712         snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
1713         ErrorHandler(iError, pcError, ERROR_CRITICAL);
1714       }
1715 
1716       continue;
1717     }
1718 
1719     /*-
1720      *******************************************************************
1721      *
1722      * If this is "." and it has the same volume and file index as the
1723      * current path, fall through to the bottom of the loop.
1724      *
1725      *******************************************************************
1726      *
1727      * If this is ".." and it has the same volume and file index as the
1728      * parent path, fall through to the bottom of the loop.
1729      *
1730      *******************************************************************
1731      *
1732      * Otherwise, attempt to process the record.
1733      *
1734      *******************************************************************
1735      */
1736 //THIS CHANGE IS PART OF EXTENDED PREFIX SUPPORT (\\?\)
1737 //    if (wcscmp(sFindDataW.cFileName, FTIMES_DOT_W) == 0)
1738 //    {
1739 //      if (MASK_BIT_IS_SET(psFTFileData->ulAttributeMask, (MAP_VOLUME | MAP_FINDEX)))
1740 //      {
1741 //        hFileCurrent = MapGetFileHandleW(psFTTreeData->pwcRawPath);
1742 //        if (hFileCurrent != INVALID_HANDLE_VALUE && GetFileInformationByHandle(hFileCurrent, &sFileInfoCurrent))
1743 //        {
1744 //          if (sFileInfoCurrent.dwVolumeSerialNumber != psFTFileData->dwVolumeSerialNumber ||
1745 //              sFileInfoCurrent.nFileIndexHigh != psFTFileData->dwFileIndexHigh ||
1746 //              sFileInfoCurrent.nFileIndexLow != psFTFileData->dwFileIndexLow)
1747 //          {
1748 //            snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Volume/FileIndex mismatch between '.' and current directory.", acRoutine, psFTFileData->pcNeuteredPath);
1749 //            ErrorHandler(ER_BadValue, pcError, ERROR_WARNING);
1750 //          }
1751 //          CloseHandle(hFileCurrent);
1752 //        }
1753 //        else
1754 //        {
1755 //          snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Not enough information on current directory to compare it to '.'.", acRoutine, psFTFileData->pcNeuteredPath);
1756 //          ErrorHandler(ER_Warning, pcError, ERROR_WARNING);
1757 //        }
1758 //      }
1759 //      else
1760 //      {
1761 //        snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Not enough information on '.' to compare it to current directory.", acRoutine, psFTFileData->pcNeuteredPath);
1762 //        ErrorHandler(ER_Warning, pcError, ERROR_WARNING);
1763 //      }
1764 //    }
1765 //    else if (wcscmp(sFindDataW.cFileName, FTIMES_DOTDOT_W) == 0)
1766 //    {
1767 //      /*-
1768 //       *****************************************************************
1769 //       *
1770 //       * If the file system is remote, skip this check. This is done
1771 //       * because, in testing, the file index for '..' is different than
1772 //       * the parent directory. This was found to be true with NTFS and
1773 //       * Samba shares which, by-the-way, show up as NTFS_REMOTE. For
1774 //       * now, these quirks remain unexplained. NWFS_REMOTE was added
1775 //       * here to follow suit with the other file systems -- i.e., it
1776 //       * has not been tested to see if the '..' issue exists.
1777 //       *
1778 //       *****************************************************************
1779 //       */
1780 //      if (psFTFileData->iFSType != FSTYPE_NTFS_REMOTE && psFTFileData->iFSType != FSTYPE_FAT_REMOTE && psFTFileData->iFSType != FSTYPE_NWFS_REMOTE)
1781 //      {
1782 //        if (MASK_BIT_IS_SET(psFTFileData->ulAttributeMask, (MAP_VOLUME | MAP_FINDEX)))
1783 //        {
1784 //          pcParentPath = MapDirname(psFTTreeData->pcRawPath);
1785 //          pwcParentPath = MapUtf8ToWide((pcParentPath) ? pcParentPath : "", -1, acLocalError);
1786 //          hFileParent = MapGetFileHandleW((pwcParentPath) ? pwcParentPath : L"");
1787 //          if (hFileParent != INVALID_HANDLE_VALUE && GetFileInformationByHandle(hFileParent, &sFileInfoParent))
1788 //          {
1789 //            if (sFileInfoParent.dwVolumeSerialNumber != psFTFileData->dwVolumeSerialNumber ||
1790 //                sFileInfoParent.nFileIndexHigh != psFTFileData->dwFileIndexHigh ||
1791 //                sFileInfoParent.nFileIndexLow != psFTFileData->dwFileIndexLow)
1792 //            {
1793 //              snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Volume/FileIndex mismatch between '..' and parent directory.", acRoutine, psFTFileData->pcNeuteredPath);
1794 //              ErrorHandler(ER_BadValue, pcError, ERROR_WARNING);
1795 //            }
1796 //            CloseHandle(hFileParent);
1797 //          }
1798 //          else
1799 //          {
1800 //            snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Not enough information on parent directory to compare it to '..'.", acRoutine, psFTFileData->pcNeuteredPath);
1801 //            ErrorHandler(ER_Warning, pcError, ERROR_WARNING);
1802 //          }
1803 //        }
1804 //        else
1805 //        {
1806 //          snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Not enough information on '..' to compare it to parent directory.", acRoutine, psFTFileData->pcNeuteredPath);
1807 //          ErrorHandler(ER_Warning, pcError, ERROR_WARNING);
1808 //        }
1809 //      }
1810 //    }
1811 //    else
1812 //END (\\?\)
1813     {
1814       /*-
1815        *****************************************************************
1816        *
1817        * Map directories and files.
1818        *
1819        *****************************************************************
1820        */
1821       if ((psFTFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
1822       {
1823         giDirectories++;
1824 #ifdef USE_XMAGIC
1825         if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MAGIC))
1826         {
1827           snprintf(psFTFileData->acType, FTIMES_FILETYPE_BUFSIZE, "special/directory");
1828         }
1829 #endif
1830         if (psProperties->bEnableRecursion)
1831         {
1832           MapTree(psProperties, psFTFileData, acLocalError);
1833         }
1834 #ifdef USE_PCRE
1835         if (psFTFileData->iFiltered) /* We're done. */
1836         {
1837           continue;
1838         }
1839 #endif
1840       }
1841       else
1842       {
1843 #ifdef USE_PCRE
1844         if (psFTFileData->iFiltered) /* We're done. */
1845         {
1846           continue;
1847         }
1848 #endif
1849         giFiles++;
1850         if (psProperties->iLastAnalysisStage > 0)
1851         {
1852           iError = AnalyzeFile(psProperties, psFTFileData, acLocalError);
1853           if (iError != ER_OK)
1854           {
1855             snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, psFTFileData->pcNeuteredPath, acLocalError);
1856             ErrorHandler(iError, pcError, ERROR_FAILURE);
1857           }
1858         }
1859       }
1860 
1861       /*-
1862        *****************************************************************
1863        *
1864        * Conditionally update directory hashes.
1865        *
1866        *****************************************************************
1867        */
1868       if (psProperties->bHashDirectories)
1869       {
1870         MapDirHashCycle(psProperties, &sFTHashData, psFTFileData);
1871       }
1872 
1873       /*-
1874        *****************************************************************
1875        *
1876        * Record the collected data.
1877        *
1878        *****************************************************************
1879        */
1880       iError = MapWriteRecord(psProperties, psFTFileData, acLocalError);
1881       if (iError != ER_OK)
1882       {
1883         snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
1884         ErrorHandler(iError, pcError, ERROR_CRITICAL);
1885       }
1886 
1887 #ifdef WINNT
1888       /*-
1889        *****************************************************************
1890        *
1891        * Process alternate streams. This applies only to NTFS.
1892        *
1893        *****************************************************************
1894        */
1895       if (psFTFileData->iStreamCount > 0)
1896       {
1897         MapStream(psProperties, psFTFileData, &sFTHashData, acLocalError);
1898       }
1899 #endif
1900     }
1901   }
1902 
1903   /*-
1904    *********************************************************************
1905    *
1906    * Conditionally complete directory hashes.
1907    *
1908    *********************************************************************
1909    */
1910   if (psProperties->bHashDirectories)
1911   {
1912     MapDirHashOmega(psProperties, &sFTHashData, psFTTreeData);
1913   }
1914 
1915   FindClose(hSearch);
1916   return ER_OK;
1917 }
1918 
1919 
1920 #ifdef WINNT
1921 /*-
1922  ***********************************************************************
1923  *
1924  * MapStream
1925  *
1926  ***********************************************************************
1927  */
1928 void
MapStream(FTIMES_PROPERTIES * psProperties,FTIMES_FILE_DATA * psFTFileData,FTIMES_HASH_DATA * psFTHashData,char * pcError)1929 MapStream(FTIMES_PROPERTIES *psProperties, FTIMES_FILE_DATA *psFTFileData, FTIMES_HASH_DATA *psFTHashData, char *pcError)
1930 {
1931   const char          acRoutine[] = "MapStream()";
1932   char                acLocalError[MESSAGE_SIZE] = "";
1933   char                acRawPath[FTIMES_MAX_PATH] = "";
1934   char               *pcNeuteredPath = NULL;
1935   char               *pcStreamName = NULL;
1936   FTIMES_FILE_DATA    sFTFileData;
1937   FILE_STREAM_INFORMATION *psFSI = (FILE_STREAM_INFORMATION *) psFTFileData->pucStreamInfo;
1938   int                 iDone = 0;
1939   int                 iError = 0;
1940   int                 iLength = 0;
1941   int                 iNameLength = 0;
1942   int                 iNextEntryOffset = 0;
1943   wchar_t             awcRawPath[FTIMES_MAX_PATH] = L"";
1944   wchar_t             awcStreamName[FTIMES_MAX_PATH] = L"";
1945 
1946   giStreams += psFTFileData->iStreamCount;
1947 
1948   /*-
1949    *********************************************************************
1950    *
1951    * Make a local copy of the file data. If the stream belongs to
1952    * a directory, clear the attributes field. If this isn't done,
1953    * the output routine, will overwrite the hash field with "DIRECTORY"
1954    * or "D" respectively.
1955    *
1956    *********************************************************************
1957    */
1958 /* FIXME Look into using MapNewFTFileData() here. */
1959   memcpy(&sFTFileData, psFTFileData, sizeof(FTIMES_FILE_DATA));
1960 
1961   sFTFileData.pcRawPath = acRawPath;
1962 
1963   sFTFileData.iStreamCount = 0;
1964 
1965   if ((psFTFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
1966   {
1967     sFTFileData.dwFileAttributes = 0;
1968   }
1969 
1970   /*-
1971    *********************************************************************
1972    *
1973    * Process each stream, but skip the default stream.
1974    *
1975    *********************************************************************
1976    */
1977   for (iDone = iNextEntryOffset = 0; !iDone; iNextEntryOffset = psFSI->NextEntryOffset)
1978   {
1979     psFSI = (FILE_STREAM_INFORMATION *) ((byte *) psFSI + iNextEntryOffset);
1980     if (psFSI->NextEntryOffset == 0)
1981     {
1982       iDone = 1; /* Instruct the loop to terminate after this pass. */
1983     }
1984 
1985     /*-
1986      *******************************************************************
1987      *
1988      * Skip unnamed streams. Remove the ":$DATA" suffix since it's not
1989      * part of the stream name as stored on disk in the MFT and it can
1990      * result in paths that exceed MAX_PATH. Convert the result to
1991      * UTF-8.
1992      *
1993      *******************************************************************
1994      */
1995     iLength = psFSI->StreamNameLength / sizeof(wchar_t);
1996     if (wcscmp(&psFSI->StreamName[iLength - 6], L":$DATA") == 0)
1997     {
1998       if (psFSI->StreamName[iLength - 7] == L':')
1999       {
2000         continue;
2001       }
2002       iLength -= 6;
2003     }
2004     wcsncpy(awcStreamName, psFSI->StreamName, iLength);
2005     awcStreamName[iLength] = 0;
2006     pcStreamName = MapWideToUtf8(awcStreamName, iLength + 1, acLocalError);
2007     if (pcStreamName == NULL)
2008     {
2009       snprintf(pcError, MESSAGE_SIZE, "%s: RawPath = [%s]: UTF-8 conversion failed for a stream associated with this file.", acRoutine, psFTFileData->pcRawPath);
2010       ErrorHandler(ER_Failure, pcError, ERROR_FAILURE);
2011       continue;
2012     }
2013 
2014     /*-
2015      *******************************************************************
2016      *
2017      * Figure out if the new path length will be too long. If yes, warn
2018      * the user, and continue with the next stream.
2019      *
2020      *******************************************************************
2021      */
2022     iNameLength = strlen(psFTFileData->pcRawPath) + iLength;
2023     if (iNameLength > FTIMES_MAX_PATH - 1) /* Subtract one for the NULL. */
2024     {
2025       snprintf(pcError, MESSAGE_SIZE, "%s: RawPath = [%s%s]: Length (%d) exceeds %d bytes.",
2026         acRoutine,
2027         psFTFileData->pcRawPath,
2028         pcStreamName,
2029         iNameLength,
2030         FTIMES_MAX_PATH - 1
2031         );
2032       ErrorHandler(ER_Length, pcError, ERROR_FAILURE);
2033       MEMORY_FREE(pcStreamName);
2034       continue;
2035     }
2036     snprintf(acRawPath, FTIMES_MAX_PATH, "%s%s", psFTFileData->pcRawPath, pcStreamName);
2037     sFTFileData.pcRawPath = acRawPath;
2038 
2039     _snwprintf(awcRawPath, FTIMES_MAX_PATH, L"%s%s", psFTFileData->pwcRawPath, awcStreamName);
2040     sFTFileData.pwcRawPath = awcRawPath;
2041 
2042     /*-
2043      *******************************************************************
2044      *
2045      * Neuter the new given path.
2046      *
2047      *******************************************************************
2048      */
2049     pcNeuteredPath = SupportNeuterString(acRawPath, iNameLength, acLocalError);
2050     if (pcNeuteredPath == NULL)
2051     {
2052       snprintf(pcError, MESSAGE_SIZE, "%s: RawPath = [%s]: %s", acRoutine, acRawPath, acLocalError);
2053       ErrorHandler(ER_NeuterPathname, pcError, ERROR_FAILURE);
2054       MEMORY_FREE(pcStreamName);
2055       continue;
2056     }
2057     sFTFileData.pcNeuteredPath = pcNeuteredPath;
2058 
2059     /*-
2060      *******************************************************************
2061      *
2062      * Set the file attributes that are unique to this stream.
2063      *
2064      *******************************************************************
2065      */
2066     sFTFileData.dwFileSizeHigh = (DWORD) (psFSI->StreamSize.QuadPart >> 32);
2067     sFTFileData.dwFileSizeLow = (DWORD) psFSI->StreamSize.QuadPart;
2068 
2069     /*-
2070      *******************************************************************
2071      *
2072      * Analyze the stream's content.
2073      *
2074      *******************************************************************
2075      */
2076     if (psProperties->iLastAnalysisStage > 0)
2077     {
2078       iError = AnalyzeFile(psProperties, &sFTFileData, acLocalError);
2079       if (iError != ER_OK)
2080       {
2081         snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, pcNeuteredPath, acLocalError);
2082         ErrorHandler(iError, pcError, ERROR_FAILURE);
2083       }
2084     }
2085 
2086     /*-
2087      *******************************************************************
2088      *
2089      * Conditionally update directory hashes. If psFTHashData is NULL,
2090      * assume the caller was MapFile() and skip this step -- directory
2091      * hashes are not computed for includes that are individual files.
2092      *
2093      *******************************************************************
2094      */
2095     if (psProperties->bHashDirectories && psFTHashData != NULL)
2096     {
2097       MapDirHashCycle(psProperties, psFTHashData, &sFTFileData);
2098     }
2099 
2100     /*-
2101      *******************************************************************
2102      *
2103      * Record the collected data.
2104      *
2105      *******************************************************************
2106      */
2107     iError = MapWriteRecord(psProperties, &sFTFileData, acLocalError);
2108     if (iError != ER_OK)
2109     {
2110       snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
2111       ErrorHandler(iError, pcError, ERROR_CRITICAL);
2112     }
2113 
2114     /*-
2115      *******************************************************************
2116      *
2117      * Free the neutered path and stream name.
2118      *
2119      *******************************************************************
2120      */
2121     MEMORY_FREE(pcNeuteredPath);
2122     MEMORY_FREE(pcStreamName);
2123   }
2124 }
2125 
2126 
2127 /*-
2128  ***********************************************************************
2129  *
2130  * MapCountNamedStreams
2131  *
2132  ***********************************************************************
2133  */
2134 int
MapCountNamedStreams(HANDLE hFile,int * piStreamCount,unsigned char ** ppucStreamInfo,char * pcError)2135 MapCountNamedStreams(HANDLE hFile, int *piStreamCount, unsigned char **ppucStreamInfo, char *pcError)
2136 {
2137   const char          acRoutine[] = "MapCountNamedStreams()";
2138   char               *pcMessage = NULL;
2139   DWORD               dwStatus = 0;
2140   FILE_STREAM_INFORMATION *psFSI = NULL;
2141   FILE_STREAM_INFORMATION *psTempFSI = NULL;
2142   int                 i = 0;
2143   int                 iDone = 0;
2144   int                 iNextEntryOffset = 0;
2145   IO_STATUS_BLOCK     sIOStatusBlock;
2146   unsigned long       ulSize = 0;
2147 
2148 #ifndef STATUS_SUCCESS
2149 #define STATUS_SUCCESS 0x00000000
2150 #endif
2151 #ifndef STATUS_BUFFER_OVERFLOW
2152 #define STATUS_BUFFER_OVERFLOW 0x80000005
2153 #endif
2154 
2155   /*-
2156    *********************************************************************
2157    *
2158    * Make sure the provided file handle is valid.
2159    *
2160    *********************************************************************
2161    */
2162   if (hFile == INVALID_HANDLE_VALUE)
2163   {
2164     snprintf(pcError, MESSAGE_SIZE, "%s: Invalid File Handle", acRoutine);
2165     return ER;
2166   }
2167 
2168   /*-
2169    *********************************************************************
2170    *
2171    * Request the file's stream information. Loop until enough memory
2172    * has been allocated to hold the data. However, do not exceed the
2173    * maximum size limit.
2174    *
2175    *********************************************************************
2176    */
2177   i = 0; psFSI = psTempFSI = NULL;
2178   do
2179   {
2180     ulSize = FTIMES_STREAM_INFO_SIZE << i;
2181     if (ulSize > FTIMES_MAX_STREAM_INFO_SIZE)
2182     {
2183       snprintf(pcError, MESSAGE_SIZE, "%s: Requested buffer size would exceed the maximum size limit (%lu bytes).", acRoutine, FTIMES_MAX_STREAM_INFO_SIZE);
2184       MEMORY_FREE(psFSI);
2185       return ER;
2186     }
2187     psTempFSI = (FILE_STREAM_INFORMATION *) realloc(psFSI, ulSize);
2188     if (psTempFSI == NULL)
2189     {
2190       snprintf(pcError, MESSAGE_SIZE, "%s: realloc(): %s", acRoutine, strerror(errno));
2191       MEMORY_FREE(psFSI);
2192       return ER;
2193     }
2194     memset(psTempFSI, 0, ulSize);
2195     psFSI = psTempFSI;
2196     dwStatus = NtdllNQIF(hFile, &sIOStatusBlock, psFSI, ulSize, FileStreamInformation);
2197     if (dwStatus != STATUS_SUCCESS && dwStatus != STATUS_BUFFER_OVERFLOW)
2198     {
2199       SetLastError(LsaNtStatusToWinError(dwStatus));
2200       ErrorFormatWinxError(GetLastError(), &pcMessage);
2201       snprintf(pcError, MESSAGE_SIZE, "%s: NtQueryInformationFile(): %s", acRoutine, pcMessage);
2202       MEMORY_FREE(psFSI);
2203       return ER;
2204     }
2205     i++;
2206   } while (dwStatus == STATUS_BUFFER_OVERFLOW);
2207 
2208   /*-
2209    *********************************************************************
2210    *
2211    * Record the final FSI pointer.
2212    *
2213    *********************************************************************
2214    */
2215   *ppucStreamInfo = (unsigned char *) psFSI;
2216 
2217   /*-
2218    *********************************************************************
2219    *
2220    * Count all but the default stream. This logic is supposed to work
2221    * even if NtQueryInformationFile() returns no data. This is due to
2222    * the fact that psFSI should be pointing to a zero-initialized
2223    * block of memory that is large enough to contain at least one FSI
2224    * struct. In other words, NextEntryOffset will be set to zero, and
2225    * the loop will terminate after one pass. Note: At least one pass
2226    * through the loop is required to catch directories that have only
2227    * one named stream. This is necessary because directories do not
2228    * have an unnamed (i.e., default) stream -- see Data attribute in
2229    * Table 9-1 of Inside Windows NT Second Edition, page 412.
2230    *
2231    *********************************************************************
2232    */
2233   for (*piStreamCount = iDone = iNextEntryOffset = 0; !iDone; iNextEntryOffset = psFSI->NextEntryOffset)
2234   {
2235     psFSI = (FILE_STREAM_INFORMATION *) ((byte *) psFSI + iNextEntryOffset);
2236     if (psFSI->NextEntryOffset == 0)
2237     {
2238       iDone = 1; /* Instruct the loop to terminate after this pass. */
2239     }
2240     if (psFSI->StreamNameLength && wcscmp(psFSI->StreamName, DEFAULT_STREAM_NAME_W) != 0)
2241     {
2242       (*piStreamCount)++;
2243     }
2244   }
2245 
2246   return ER_OK;
2247 }
2248 #endif /* WINNT */
2249 #endif /* WIN32 */
2250 
2251 
2252 #ifdef UNIX
2253 int
MapFile(FTIMES_PROPERTIES * psProperties,char * pcPath,char * pcError)2254 MapFile(FTIMES_PROPERTIES *psProperties, char *pcPath, char *pcError)
2255 {
2256   const char          acRoutine[] = "MapFile()";
2257   char                acLocalError[MESSAGE_SIZE] = "";
2258   char                acLinkData[FTIMES_MAX_PATH] = "";
2259   FTIMES_FILE_DATA   *psFTFileData = NULL;
2260   int                 iError = 0;
2261   int                 iFSType = 0;
2262   int                 iLength = strlen(pcPath);
2263 #ifdef USE_PCRE
2264   char                acMessage[MESSAGE_SIZE] = "";
2265 #endif
2266 
2267   /*-
2268    *********************************************************************
2269    *
2270    * Create and initialize a new file data structure.
2271    *
2272    *********************************************************************
2273    */
2274   psFTFileData = MapNewFTFileData(NULL, pcPath, acLocalError);
2275   if (psFTFileData == NULL)
2276   {
2277 /* FIXME Need to prevent truncation in the case where the new path is larger than MESSAGE_SIZE. */
2278     char  acTempError[MESSAGE_SIZE] = "";
2279     char *pcNeuteredPath = SupportNeuterString(pcPath, iLength, acTempError);
2280     if (pcNeuteredPath)
2281     {
2282       snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, pcNeuteredPath, acLocalError);
2283       ErrorHandler(ER_Failure, pcError, ERROR_FAILURE);
2284       free(pcNeuteredPath);
2285     }
2286     else
2287     {
2288       snprintf(pcError, MESSAGE_SIZE, "%s: FallbackPath = [%s]: %s", acRoutine, pcPath, acLocalError);
2289       ErrorHandler(ER_Failure, pcError, ERROR_FAILURE);
2290     }
2291     return ER;
2292   }
2293 
2294   /*-
2295    *********************************************************************
2296    *
2297    * Get file attributes. This fills in several structure members.
2298    *
2299    *********************************************************************
2300    */
2301   MapGetAttributes(psFTFileData);
2302   if (!psFTFileData->iFileExists)
2303   {
2304     return ER_OK;
2305   }
2306 
2307 #ifdef USE_PCRE
2308   /*-
2309    *********************************************************************
2310    *
2311    * If the path is matched by an exclude filter, just return. If the
2312    * path is matched by an include filter, set a flag, but keep going.
2313    * Include filters do not get applied until the file's type is
2314    * known. This is because directories must be traversed before they
2315    * can be filtered.
2316    *
2317    *********************************************************************
2318    */
2319   if (psProperties->psExcludeFilterList)
2320   {
2321     FILTER_LIST *psFilter = SupportMatchFilter(psProperties->psExcludeFilterList, psFTFileData->pcRawPath);
2322     if (psFilter != NULL)
2323     {
2324       if (psProperties->iLogLevel <= MESSAGE_DEBUGGER)
2325       {
2326         snprintf(acMessage, MESSAGE_SIZE, "ExcludeFilter=%s RawPath=%s", psFilter->pcFilter, psFTFileData->pcRawPath);
2327         MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_DEBUGGER, MESSAGE_DEBUGGER_STRING, acMessage);
2328       }
2329       return ER_OK;
2330     }
2331   }
2332 
2333   if (psProperties->psIncludeFilterList)
2334   {
2335     FILTER_LIST *psFilter = SupportMatchFilter(psProperties->psIncludeFilterList, psFTFileData->pcRawPath);
2336     if (psFilter == NULL)
2337     {
2338       psFTFileData->iFiltered = 1;
2339     }
2340     else
2341     {
2342       if (psProperties->iLogLevel <= MESSAGE_DEBUGGER)
2343       {
2344         snprintf(acMessage, MESSAGE_SIZE, "IncludeFilter=%s RawPath=%s", psFilter->pcFilter, psFTFileData->pcRawPath);
2345         MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_DEBUGGER, MESSAGE_DEBUGGER_STRING, acMessage);
2346       }
2347     }
2348   }
2349 #endif
2350 
2351   /*-
2352    *********************************************************************
2353    *
2354    * If the file system is remote and remote scanning is disabled, we're done.
2355    *
2356    *********************************************************************
2357    */
2358   iFSType = psFTFileData->iFSType;
2359   if (!psProperties->bAnalyzeRemoteFiles && (iFSType == FSTYPE_NFS || iFSType == FSTYPE_NFS3 || iFSType == FSTYPE_SMB))
2360   {
2361     snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Excluding remote file system.", acRoutine, psFTFileData->pcNeuteredPath);
2362     ErrorHandler(ER_Warning, pcError, ERROR_WARNING);
2363     MapFreeFTFileData(psFTFileData);
2364     return ER;
2365   }
2366 
2367   /*-
2368    *********************************************************************
2369    *
2370    * No attributes means no file type, so we have to stop short.
2371    *
2372    *********************************************************************
2373    */
2374   if (psFTFileData->ulAttributeMask == 0)
2375   {
2376 #ifdef USE_PCRE
2377     /*-
2378      *******************************************************************
2379      *
2380      * If this path has been filtered, we're done.
2381      *
2382      *******************************************************************
2383      */
2384     if (psFTFileData->iFiltered)
2385     {
2386       MapFreeFTFileData(psFTFileData);
2387       return ER_OK;
2388     }
2389 #endif
2390 
2391     /*-
2392      *******************************************************************
2393      *
2394      * Record the collected data. In this case we only have a name.
2395      *
2396      *******************************************************************
2397      */
2398     iError = MapWriteRecord(psProperties, psFTFileData, acLocalError);
2399     if (iError != ER_OK)
2400     {
2401       snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
2402       ErrorHandler(iError, pcError, ERROR_CRITICAL);
2403     }
2404 
2405     /*-
2406      *******************************************************************
2407      *
2408      * Free the file data structure.
2409      *
2410      *******************************************************************
2411      */
2412     MapFreeFTFileData(psFTFileData);
2413     return ER;
2414   }
2415 
2416 
2417   /*-
2418    *********************************************************************
2419    *
2420    * Map directories, files, and links. Everything else is considered
2421    * a special file. In that case write out any attributes have been
2422    * collected.
2423    *
2424    *********************************************************************
2425    */
2426   if (S_ISDIR(psFTFileData->sStatEntry.st_mode))
2427   {
2428     giDirectories++;
2429 #ifdef USE_XMAGIC
2430     if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MAGIC))
2431     {
2432       snprintf(psFTFileData->acType, FTIMES_FILETYPE_BUFSIZE, "special/directory");
2433     }
2434 #endif
2435     MapTree(psProperties, psFTFileData, acLocalError);
2436 #ifdef USE_PCRE
2437     if (psFTFileData->iFiltered) /* We're done. */
2438     {
2439       MapFreeFTFileData(psFTFileData);
2440       return ER_OK;
2441     }
2442 #endif
2443   }
2444   else if (S_ISREG(psFTFileData->sStatEntry.st_mode) || ((S_ISBLK(psFTFileData->sStatEntry.st_mode) || S_ISCHR(psFTFileData->sStatEntry.st_mode)) && psProperties->bAnalyzeDeviceFiles))
2445   {
2446 #ifdef USE_PCRE
2447     if (psFTFileData->iFiltered) /* We're done. */
2448     {
2449       MapFreeFTFileData(psFTFileData);
2450       return ER_OK;
2451     }
2452 #endif
2453     giFiles++;
2454     if (psProperties->iLastAnalysisStage > 0)
2455     {
2456       iError = AnalyzeFile(psProperties, psFTFileData, acLocalError);
2457       if (iError != ER_OK)
2458       {
2459         snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, psFTFileData->pcNeuteredPath, acLocalError);
2460         ErrorHandler(iError, pcError, ERROR_FAILURE);
2461       }
2462     }
2463   }
2464   else if (S_ISLNK(psFTFileData->sStatEntry.st_mode))
2465   {
2466 #ifdef USE_PCRE
2467     if (psFTFileData->iFiltered) /* We're done. */
2468     {
2469       MapFreeFTFileData(psFTFileData);
2470       return ER_OK;
2471     }
2472 #endif
2473     giSpecial++;
2474     if (psProperties->bHashSymbolicLinks)
2475     {
2476       iError = readlink(psFTFileData->pcRawPath, acLinkData, FTIMES_MAX_PATH - 1);
2477       if (iError == ER)
2478       {
2479         snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Unreadable Symbolic Link: %s", acRoutine, psFTFileData->pcNeuteredPath, strerror(errno));
2480         ErrorHandler(ER_readlink, pcError, ERROR_FAILURE);
2481       }
2482       else
2483       {
2484         acLinkData[iError] = 0; /* Readlink does not append a NULL. */
2485         if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MD5))
2486         {
2487           MD5HashString((unsigned char *) acLinkData, strlen(acLinkData), psFTFileData->aucFileMd5);
2488         }
2489         if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA1))
2490         {
2491           SHA1HashString((unsigned char *) acLinkData, strlen(acLinkData), psFTFileData->aucFileSha1);
2492         }
2493         if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_SHA256))
2494         {
2495           SHA256HashString((unsigned char *) acLinkData, strlen(acLinkData), psFTFileData->aucFileSha256);
2496         }
2497       }
2498     }
2499 #ifdef USE_XMAGIC
2500     if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MAGIC))
2501     {
2502       iError = XMagicTestSpecial(psFTFileData->pcRawPath, &psFTFileData->sStatEntry, psFTFileData->acType, FTIMES_FILETYPE_BUFSIZE, acLocalError);
2503       if (iError != ER_OK)
2504       {
2505         snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, psFTFileData->pcNeuteredPath, acLocalError);
2506         ErrorHandler(iError, pcError, ERROR_FAILURE);
2507       }
2508     }
2509 #endif
2510   }
2511   else
2512   {
2513 #ifdef USE_PCRE
2514     if (psFTFileData->iFiltered) /* We're done. */
2515     {
2516       MapFreeFTFileData(psFTFileData);
2517       return ER_OK;
2518     }
2519 #endif
2520     giSpecial++;
2521 #ifdef USE_XMAGIC
2522     if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MAGIC))
2523     {
2524       iError = XMagicTestSpecial(psFTFileData->pcRawPath, &psFTFileData->sStatEntry, psFTFileData->acType, FTIMES_FILETYPE_BUFSIZE, acLocalError);
2525       if (iError != ER_OK)
2526       {
2527         snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, psFTFileData->pcNeuteredPath, acLocalError);
2528         ErrorHandler(iError, pcError, ERROR_FAILURE);
2529       }
2530     }
2531 #endif
2532   }
2533 
2534   /*-
2535    *********************************************************************
2536    *
2537    * Record the collected data.
2538    *
2539    *********************************************************************
2540    */
2541   iError = MapWriteRecord(psProperties, psFTFileData, acLocalError);
2542   if (iError != ER_OK)
2543   {
2544     snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, psFTFileData->pcNeuteredPath, acLocalError);
2545     ErrorHandler(iError, pcError, ERROR_CRITICAL);
2546   }
2547 
2548 #ifdef USE_FILE_HOOKS
2549   /*-
2550    *********************************************************************
2551    *
2552    * Conditionally execute hooks for regular files.
2553    *
2554    *********************************************************************
2555    */
2556   if (psProperties->psFileHookList && S_ISREG(psFTFileData->sStatEntry.st_mode))
2557   {
2558     iError = MapExecuteHook(psProperties, psFTFileData, acLocalError);
2559     if (iError != ER_OK)
2560     {
2561       snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, psFTFileData->pcNeuteredPath, acLocalError);
2562       ErrorHandler(iError, pcError, ERROR_CRITICAL);
2563     }
2564   }
2565 #endif
2566 
2567   /*-
2568    *********************************************************************
2569    *
2570    * Free the file data structure.
2571    *
2572    *********************************************************************
2573    */
2574   MapFreeFTFileData(psFTFileData);
2575 
2576   return ER_OK;
2577 }
2578 #endif
2579 
2580 
2581 #ifdef WIN32
2582 /*-
2583  ***********************************************************************
2584  *
2585  * MapFile
2586  *
2587  ***********************************************************************
2588  */
2589 int
MapFile(FTIMES_PROPERTIES * psProperties,char * pcPath,char * pcError)2590 MapFile(FTIMES_PROPERTIES *psProperties, char *pcPath, char *pcError)
2591 {
2592   const char          acRoutine[] = "MapFile()";
2593   char                acLocalError[MESSAGE_SIZE] = "";
2594   FTIMES_FILE_DATA   *psFTFileData = NULL;
2595   int                 iError = 0;
2596   int                 iFSType = 0;
2597   int                 iLength = strlen(pcPath);
2598 #ifdef USE_PCRE
2599   char                acMessage[MESSAGE_SIZE] = "";
2600 #endif
2601   wchar_t            *pwcPath = NULL;
2602 
2603   /*-
2604    *********************************************************************
2605    *
2606    * Internally, paths are handled as wide character strings, so the
2607    * initial conversion is done here.
2608    *
2609    *********************************************************************
2610    */
2611   pwcPath = MapUtf8ToWide(pcPath, iLength + 1, acLocalError);
2612   if (pwcPath == NULL)
2613   {
2614 /* FIXME Need to prevent truncation in the case where the new path is larger than MESSAGE_SIZE. */
2615     char  acTempError[MESSAGE_SIZE] = "";
2616     char *pcNeuteredPath = SupportNeuterString(pcPath, iLength, acTempError);
2617     if (pcNeuteredPath)
2618     {
2619       snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, pcNeuteredPath, acLocalError);
2620       ErrorHandler(ER_Failure, pcError, ERROR_FAILURE);
2621       free(pcNeuteredPath);
2622     }
2623     else
2624     {
2625       snprintf(pcError, MESSAGE_SIZE, "%s: FallbackPath = [%s]: %s", acRoutine, pcPath, acLocalError);
2626       ErrorHandler(ER_Failure, pcError, ERROR_FAILURE);
2627     }
2628     return ER;
2629   }
2630 
2631   /*-
2632    *********************************************************************
2633    *
2634    * Create and initialize a new file data structure.
2635    *
2636    *********************************************************************
2637    */
2638   psFTFileData = MapNewFTFileDataW(NULL, pwcPath, acLocalError);
2639   if (psFTFileData == NULL)
2640   {
2641 /* FIXME Need to prevent truncation in the case where the new path is larger than MESSAGE_SIZE. */
2642     char  acTempError[MESSAGE_SIZE] = "";
2643     char *pcNeuteredPath = SupportNeuterString(pcPath, iLength, acTempError);
2644     if (pcNeuteredPath)
2645     {
2646       snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, pcNeuteredPath, acLocalError);
2647       ErrorHandler(ER_Failure, pcError, ERROR_FAILURE);
2648       free(pcNeuteredPath);
2649     }
2650     else
2651     {
2652       snprintf(pcError, MESSAGE_SIZE, "%s: FallbackPath = [%s]: %s", acRoutine, pcPath, acLocalError);
2653       ErrorHandler(ER_Failure, pcError, ERROR_FAILURE);
2654     }
2655     return ER;
2656   }
2657   MEMORY_FREE(pwcPath);
2658 
2659   /*-
2660    *********************************************************************
2661    *
2662    * Get file attributes. This fills in several structure members.
2663    *
2664    *********************************************************************
2665    */
2666   MapGetAttributes(psFTFileData);
2667   if (!psFTFileData->iFileExists)
2668   {
2669     return ER_OK;
2670   }
2671 
2672 #ifdef USE_PCRE
2673   /*-
2674    *********************************************************************
2675    *
2676    * If the path is matched by an exclude filter, just return. If the
2677    * path is matched by an include filter, set a flag, but keep going.
2678    * Include filters do not get applied until the file's type is
2679    * known. This is because directories must be traversed before they
2680    * can be filtered.
2681    *
2682    *********************************************************************
2683    */
2684   if (psProperties->psExcludeFilterList)
2685   {
2686     FILTER_LIST *psFilter = SupportMatchFilter(psProperties->psExcludeFilterList, psFTFileData->pcRawPath);
2687     if (psFilter != NULL)
2688     {
2689       if (psProperties->iLogLevel <= MESSAGE_DEBUGGER)
2690       {
2691         snprintf(acMessage, MESSAGE_SIZE, "ExcludeFilter=%s RawPath=%s", psFilter->pcFilter, psFTFileData->pcRawPath);
2692         MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_DEBUGGER, MESSAGE_DEBUGGER_STRING, acMessage);
2693       }
2694       return ER_OK;
2695     }
2696   }
2697 
2698   if (psProperties->psIncludeFilterList)
2699   {
2700     FILTER_LIST *psFilter = SupportMatchFilter(psProperties->psIncludeFilterList, psFTFileData->pcRawPath);
2701     if (psFilter == NULL)
2702     {
2703       psFTFileData->iFiltered = 1;
2704     }
2705     else
2706     {
2707       if (psProperties->iLogLevel <= MESSAGE_DEBUGGER)
2708       {
2709         snprintf(acMessage, MESSAGE_SIZE, "IncludeFilter=%s RawPath=%s", psFilter->pcFilter, psFTFileData->pcRawPath);
2710         MessageHandler(MESSAGE_FLUSH_IT, MESSAGE_DEBUGGER, MESSAGE_DEBUGGER_STRING, acMessage);
2711       }
2712     }
2713   }
2714 #endif
2715 
2716   /*-
2717    *********************************************************************
2718    *
2719    * If the file system is remote and remote scanning is disabled, we're done.
2720    *
2721    *********************************************************************
2722    */
2723   iFSType = psFTFileData->iFSType;
2724   if (!psProperties->bAnalyzeRemoteFiles && (iFSType == FSTYPE_NTFS_REMOTE || iFSType == FSTYPE_FAT_REMOTE || iFSType == FSTYPE_NWFS_REMOTE))
2725   {
2726     snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Excluding remote file system.", acRoutine, psFTFileData->pcNeuteredPath);
2727     ErrorHandler(ER_Warning, pcError, ERROR_WARNING);
2728     MapFreeFTFileData(psFTFileData);
2729     return ER;
2730   }
2731 
2732   /*-
2733    *********************************************************************
2734    *
2735    * No attributes means no file type, so we have to stop short.
2736    *
2737    *********************************************************************
2738    */
2739   if (psFTFileData->ulAttributeMask == 0)
2740   {
2741 #ifdef USE_PCRE
2742     /*-
2743      *******************************************************************
2744      *
2745      * If this path has been filtered, we're done.
2746      *
2747      *******************************************************************
2748      */
2749     if (psFTFileData->iFiltered)
2750     {
2751       MapFreeFTFileData(psFTFileData);
2752       return ER_OK;
2753     }
2754 #endif
2755 
2756     /*-
2757      *******************************************************************
2758      *
2759      * Record the collected data. In this case we only have a name.
2760      *
2761      *******************************************************************
2762      */
2763     iError = MapWriteRecord(psProperties, psFTFileData, acLocalError);
2764     if (iError != ER_OK)
2765     {
2766       snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
2767       ErrorHandler(iError, pcError, ERROR_CRITICAL);
2768     }
2769 
2770     /*-
2771      *******************************************************************
2772      *
2773      * Free the file data structure.
2774      *
2775      *******************************************************************
2776      */
2777     MapFreeFTFileData(psFTFileData);
2778     return ER;
2779   }
2780 
2781   /*-
2782    *********************************************************************
2783    *
2784    * Map directories and files.
2785    *
2786    *********************************************************************
2787    */
2788   if ((psFTFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
2789   {
2790     giDirectories++;
2791 #ifdef USE_XMAGIC
2792     if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, MAP_MAGIC))
2793     {
2794       snprintf(psFTFileData->acType, FTIMES_FILETYPE_BUFSIZE, "special/directory");
2795     }
2796 #endif
2797     MapTree(psProperties, psFTFileData, acLocalError);
2798 #ifdef USE_PCRE
2799     if (psFTFileData->iFiltered) /* We're done. */
2800     {
2801       MapFreeFTFileData(psFTFileData);
2802       return ER_OK;
2803     }
2804 #endif
2805   }
2806   else
2807   {
2808 #ifdef USE_PCRE
2809     if (psFTFileData->iFiltered) /* We're done. */
2810     {
2811       MapFreeFTFileData(psFTFileData);
2812       return ER_OK;
2813     }
2814 #endif
2815     giFiles++;
2816     if (psProperties->iLastAnalysisStage > 0)
2817     {
2818       iError = AnalyzeFile(psProperties, psFTFileData, acLocalError);
2819       if (iError != ER_OK)
2820       {
2821         snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, psFTFileData->pcNeuteredPath, acLocalError);
2822         ErrorHandler(iError, pcError, ERROR_FAILURE);
2823       }
2824     }
2825   }
2826 
2827   /*-
2828    *********************************************************************
2829    *
2830    * Record the collected data.
2831    *
2832    *********************************************************************
2833    */
2834   iError = MapWriteRecord(psProperties, psFTFileData, acLocalError);
2835   if (iError != ER_OK)
2836   {
2837     snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: %s", acRoutine, psFTFileData->pcNeuteredPath, acLocalError);
2838     ErrorHandler(iError, pcError, ERROR_CRITICAL);
2839   }
2840 
2841 #ifdef WINNT
2842   /*-
2843    *********************************************************************
2844    *
2845    * Process alternate streams. This applies only to NTFS. The NULL
2846    * argument causes MapStream to skip directory hashing -- even if
2847    * the user has enabled HashDirectories.
2848    *
2849    *********************************************************************
2850    */
2851   if (psFTFileData->iStreamCount > 0)
2852   {
2853     MapStream(psProperties, psFTFileData, NULL, acLocalError);
2854   }
2855 #endif
2856 
2857   /*-
2858    *********************************************************************
2859    *
2860    * Free the file data structure.
2861    *
2862    *********************************************************************
2863    */
2864   MapFreeFTFileData(psFTFileData);
2865 
2866   return ER_OK;
2867 }
2868 #endif
2869 
2870 
2871 /*-
2872  ***********************************************************************
2873  *
2874  * MapWriteRecord
2875  *
2876  ***********************************************************************
2877  */
2878 int
MapWriteRecord(FTIMES_PROPERTIES * psProperties,FTIMES_FILE_DATA * psFTFileData,char * pcError)2879 MapWriteRecord(FTIMES_PROPERTIES *psProperties, FTIMES_FILE_DATA *psFTFileData, char *pcError)
2880 {
2881   const char          acRoutine[] = "MapWriteRecord()";
2882   char                acLocalError[MESSAGE_SIZE] = "";
2883   int                 iError;
2884   int                 iIndex = 0;
2885   int                 iWriteCount = 0;
2886 
2887 #ifdef UNIX
2888   /*-
2889    *********************************************************************
2890    *
2891    * prefix        4
2892    * name          1 (for quote) + (3 * FTIMES_MAX_PATH) + 1 (for quote)
2893    * dev           FTIMES_MAX_32BIT_SIZE
2894    * inode         FTIMES_MAX_32BIT_SIZE
2895    * mode          FTIMES_MAX_32BIT_SIZE
2896    * nlink         FTIMES_MAX_32BIT_SIZE
2897    * uid           FTIMES_MAX_32BIT_SIZE
2898    * gid           FTIMES_MAX_32BIT_SIZE
2899    * rdev          FTIMES_MAX_32BIT_SIZE
2900    * atime         FTIMES_TIME_FORMAT_SIZE
2901    * mtime         FTIMES_TIME_FORMAT_SIZE
2902    * ctime         FTIMES_TIME_FORMAT_SIZE
2903    * size          FTIMES_MAX_64BIT_SIZE
2904    * md5           FTIMES_MAX_MD5_LENGTH
2905    * sha1          FTIMES_MAX_SHA1_LENGTH
2906    * sha256        FTIMES_MAX_SHA256_LENGTH
2907 #ifdef USE_XMAGIC
2908    * magic         XMAGIC_DESCRIPTION_BUFSIZE
2909 #endif
2910    * |'s           13
2911    * newline       2
2912    *
2913    *********************************************************************
2914    */
2915   char acOutput[4 +
2916                 (3 * FTIMES_MAX_PATH) +
2917                 (7 * FTIMES_MAX_32BIT_SIZE) +
2918                 (3 * FTIMES_TIME_FORMAT_SIZE) +
2919                 (1 * FTIMES_MAX_64BIT_SIZE) +
2920                 (1 * FTIMES_MAX_MD5_LENGTH) +
2921                 (1 * FTIMES_MAX_SHA1_LENGTH) +
2922                 (1 * FTIMES_MAX_SHA256_LENGTH) +
2923 #ifdef USE_XMAGIC
2924                 (1 * XMAGIC_DESCRIPTION_BUFSIZE) +
2925 #endif
2926                 17
2927                 ];
2928 #endif
2929 
2930 #ifdef WIN32
2931   /*-
2932    *********************************************************************
2933    *
2934    * prefix        4
2935    * name          1 (for quote) + (3 * FTIMES_MAX_PATH) + 1 (for quote)
2936    * volume        FTIMES_MAX_32BIT_SIZE
2937    * findex        FTIMES_MAX_64BIT_SIZE
2938    * attributes    FTIMES_MAX_32BIT_SIZE
2939    * atime|ams     FTIMES_TIME_FORMAT_SIZE
2940    * mtime|mms     FTIMES_TIME_FORMAT_SIZE
2941    * ctime|cms     FTIMES_TIME_FORMAT_SIZE
2942    * chtime|chms   FTIMES_TIME_FORMAT_SIZE
2943    * size          FTIMES_MAX_64BIT_SIZE
2944    * altstreams    FTIMES_MAX_32BIT_SIZE
2945    * md5           FTIMES_MAX_MD5_LENGTH
2946    * sha1          FTIMES_MAX_SHA1_LENGTH
2947    * sha256        FTIMES_MAX_SHA256_LENGTH
2948 #ifdef USE_XMAGIC
2949    * magic         XMAGIC_DESCRIPTION_BUFSIZE
2950 #endif
2951    * osid          FTIMES_MAX_SID_SIZE
2952    * gsid          FTIMES_MAX_SID_SIZE
2953    * dacl          FTIMES_MAX_ACL_SIZE
2954    * |'s           14 (not counting those embedded in time)
2955    * newline       2
2956    *
2957    *********************************************************************
2958    */
2959   char acOutput[4 +
2960                 (3 * FTIMES_MAX_PATH) +
2961                 (3 * FTIMES_MAX_32BIT_SIZE) +
2962                 (4 * FTIMES_TIME_FORMAT_SIZE) +
2963                 (2 * FTIMES_MAX_64BIT_SIZE) +
2964                 (1 * FTIMES_MAX_MD5_LENGTH) +
2965                 (1 * FTIMES_MAX_SHA1_LENGTH) +
2966                 (1 * FTIMES_MAX_SHA256_LENGTH) +
2967 #ifdef USE_XMAGIC
2968                 (1 * XMAGIC_DESCRIPTION_BUFSIZE) +
2969 #endif
2970                 (2 * FTIMES_MAX_SID_SIZE) +
2971                 (1 * FTIMES_MAX_ACL_SIZE) +
2972                 18
2973                 ];
2974 #endif
2975 
2976   /*-
2977    *********************************************************************
2978    *
2979    * Conditionally add a record prefix.
2980    *
2981    *********************************************************************
2982    */
2983   if (psProperties->acMapRecordPrefix[0])
2984   {
2985     iIndex = sprintf(acOutput, "%s", psProperties->acMapRecordPrefix);
2986   }
2987 
2988   /*-
2989    *********************************************************************
2990    *
2991    * Develop the output. Warn the user if a record has null fields.
2992    *
2993    *********************************************************************
2994    */
2995   iError = psProperties->piDevelopMapOutput(psProperties, &acOutput[iIndex], &iWriteCount, psFTFileData, acLocalError);
2996   if (iError == ER_NullFields)
2997   {
2998     giIncompleteRecords++;
2999     snprintf(pcError, MESSAGE_SIZE, "%s: NeuteredPath = [%s], NullFields = [%s]", acRoutine, psFTFileData->pcNeuteredPath, acLocalError);
3000     ErrorHandler(ER_Warning, pcError, ERROR_WARNING);
3001   }
3002 #ifdef USE_PCRE
3003   else if (iError == ER_Filtered)
3004   {
3005     return ER_OK;
3006   }
3007 #endif
3008   giRecords++;
3009   iWriteCount += iIndex;
3010 
3011   /*-
3012    *********************************************************************
3013    *
3014    * Write the output data.
3015    *
3016    *********************************************************************
3017    */
3018   iError = SupportWriteData(psProperties->pFileOut, acOutput, iWriteCount, acLocalError);
3019   if (iError != ER_OK)
3020   {
3021     snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
3022     return iError;
3023   }
3024 
3025   /*-
3026    *********************************************************************
3027    *
3028    * Update the output file hash.
3029    *
3030    *********************************************************************
3031    */
3032   MD5Cycle(&psProperties->sOutFileHashContext, (unsigned char *) acOutput, iWriteCount);
3033 
3034   return ER_OK;
3035 }
3036 
3037 
3038 /*-
3039  ***********************************************************************
3040  *
3041  * MapWriteHeader
3042  *
3043  ***********************************************************************
3044  */
3045 int
MapWriteHeader(FTIMES_PROPERTIES * psProperties,char * pcError)3046 MapWriteHeader(FTIMES_PROPERTIES *psProperties, char *pcError)
3047 {
3048   const char          acRoutine[] = "MapWriteHeader()";
3049   char                acLocalError[MESSAGE_SIZE] = "";
3050   char                acHeaderData[FTIMES_MAX_LINE] = "";
3051   int                 i = 0;
3052   int                 iError = 0;
3053   int                 iIndex = 0;
3054   int                 iMaskTableLength = MaskGetTableLength(MASK_RUNMODE_TYPE_MAP);
3055   MASK_B2S_TABLE     *psMaskTable = MaskGetTableReference(MASK_RUNMODE_TYPE_MAP);
3056   unsigned long       ul = 0;
3057 
3058   /*-
3059    *********************************************************************
3060    *
3061    * Build the output's header. Conditionally add a header prefix.
3062    *
3063    *********************************************************************
3064    */
3065   if (psProperties->bCompress)
3066   {
3067     iIndex = sprintf(acHeaderData, "%sz_name", (psProperties->acMapRecordPrefix[0]) ? psProperties->acMapRecordPrefix : "");
3068     for (i = 0; i < iMaskTableLength; i++)
3069     {
3070       ul = (1 << i);
3071       if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, ul))
3072       {
3073 #ifdef WIN32
3074         switch (ul)
3075         {
3076         case MAP_ATIME:
3077           iIndex += sprintf(&acHeaderData[iIndex], "|z_atime|z_ams");
3078           break;
3079         case MAP_MTIME:
3080           iIndex += sprintf(&acHeaderData[iIndex], "|z_mtime|z_mms");
3081           break;
3082         case MAP_CTIME:
3083           iIndex += sprintf(&acHeaderData[iIndex], "|z_ctime|z_cms");
3084           break;
3085         case MAP_CHTIME:
3086           iIndex += sprintf(&acHeaderData[iIndex], "|z_chtime|z_chms");
3087           break;
3088         default:
3089           iIndex += sprintf(&acHeaderData[iIndex], "|z_%s", (char *) psMaskTable[i].acName);
3090           break;
3091         }
3092 #else
3093         iIndex += sprintf(&acHeaderData[iIndex], "|z_%s", (char *) psMaskTable[i].acName);
3094 #endif
3095       }
3096     }
3097   }
3098   else
3099   {
3100     iIndex = sprintf(acHeaderData, "%sname", (psProperties->acMapRecordPrefix[0]) ? psProperties->acMapRecordPrefix : "");
3101     for (i = 0; i < iMaskTableLength; i++)
3102     {
3103       ul = (1 << i);
3104       if (MASK_BIT_IS_SET(psProperties->psFieldMask->ulMask, ul))
3105       {
3106 #ifdef WIN32
3107         switch (ul)
3108         {
3109         case MAP_ATIME:
3110           iIndex += sprintf(&acHeaderData[iIndex], "|atime|ams");
3111           break;
3112         case MAP_MTIME:
3113           iIndex += sprintf(&acHeaderData[iIndex], "|mtime|mms");
3114           break;
3115         case MAP_CTIME:
3116           iIndex += sprintf(&acHeaderData[iIndex], "|ctime|cms");
3117           break;
3118         case MAP_CHTIME:
3119           iIndex += sprintf(&acHeaderData[iIndex], "|chtime|chms");
3120           break;
3121         default:
3122           iIndex += sprintf(&acHeaderData[iIndex], "|%s", (char *) psMaskTable[i].acName);
3123           break;
3124         }
3125 #else
3126         iIndex += sprintf(&acHeaderData[iIndex], "|%s", (char *) psMaskTable[i].acName);
3127 #endif
3128       }
3129     }
3130   }
3131   iIndex += sprintf(&acHeaderData[iIndex], "%s", psProperties->acNewLine);
3132 
3133   /*-
3134    *********************************************************************
3135    *
3136    * Write the output's header.
3137    *
3138    *********************************************************************
3139    */
3140   iError = SupportWriteData(psProperties->pFileOut, acHeaderData, iIndex, acLocalError);
3141   if (iError != ER_OK)
3142   {
3143     snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
3144     return iError;
3145   }
3146 
3147   /*-
3148    *********************************************************************
3149    *
3150    * Update the output's MD5 hash.
3151    *
3152    *********************************************************************
3153    */
3154   MD5Cycle(&psProperties->sOutFileHashContext, (unsigned char *) acHeaderData, iIndex);
3155 
3156   return ER_OK;
3157 }
3158 
3159 
3160 /*-
3161  ***********************************************************************
3162  *
3163  * MapGetAttributes
3164  *
3165  ***********************************************************************
3166  */
3167 unsigned long
MapGetAttributes(FTIMES_FILE_DATA * psFTFileData)3168 MapGetAttributes(FTIMES_FILE_DATA *psFTFileData)
3169 {
3170   const char          acRoutine[] = "MapGetAttributes()";
3171   char                acLocalError[MESSAGE_SIZE] = "";
3172 #ifdef WINNT
3173   BOOL                bResult;
3174   BY_HANDLE_FILE_INFORMATION sFileInfo;
3175   char               *pcMessage;
3176   DWORD               dwLastError;
3177   DWORD               dwSize;
3178   DWORD               dwStatus;
3179   FILE_BASIC_INFORMATION sFileBasicInfo;
3180   HANDLE              hFile;
3181   IO_STATUS_BLOCK     sIOStatusBlock;
3182   int                 iStatus;
3183   WIN32_FILE_ATTRIBUTE_DATA sFileAttributeData;
3184 #endif
3185 
3186   psFTFileData->ulAttributeMask = 0;
3187   psFTFileData->iFileExists = 1; /* Be optimistic. */
3188 
3189 #ifdef WINNT
3190   /*-
3191    *********************************************************************
3192    *
3193    * Collect attributes. Use GetFileInformationByHandle() if the file
3194    * handle is valid. Otherwise, fall back to GetFileAttributesEx().
3195    * If this is NT and the file handle is valid, additionally invoke
3196    * the native call NTQueryInformationFile() to get ChangeTime.
3197    *
3198    *********************************************************************
3199    */
3200   hFile = MapGetFileHandleW(psFTFileData->pwcRawPath);
3201   if (hFile != INVALID_HANDLE_VALUE)
3202   {
3203     if (GetFileInformationByHandle(hFile, &sFileInfo))
3204     {
3205       psFTFileData->ulAttributeMask |= MAP_VOLUME | MAP_FINDEX | MAP_ATTRIBUTES | MAP_ATIME | MAP_MTIME | MAP_CTIME | MAP_CHTIME | MAP_SIZE;
3206       psFTFileData->dwVolumeSerialNumber     = sFileInfo.dwVolumeSerialNumber;
3207       psFTFileData->dwFileIndexHigh          = sFileInfo.nFileIndexHigh;
3208       psFTFileData->dwFileIndexLow           = sFileInfo.nFileIndexLow;
3209       psFTFileData->dwFileAttributes         = sFileInfo.dwFileAttributes;
3210       psFTFileData->sFTATime.dwLowDateTime   = sFileInfo.ftLastAccessTime.dwLowDateTime;
3211       psFTFileData->sFTATime.dwHighDateTime  = sFileInfo.ftLastAccessTime.dwHighDateTime;
3212       psFTFileData->sFTMTime.dwLowDateTime   = sFileInfo.ftLastWriteTime.dwLowDateTime;
3213       psFTFileData->sFTMTime.dwHighDateTime  = sFileInfo.ftLastWriteTime.dwHighDateTime;
3214       psFTFileData->sFTCTime.dwLowDateTime   = sFileInfo.ftCreationTime.dwLowDateTime;
3215       psFTFileData->sFTCTime.dwHighDateTime  = sFileInfo.ftCreationTime.dwHighDateTime;
3216       psFTFileData->sFTChTime.dwLowDateTime  = 0;
3217       psFTFileData->sFTChTime.dwHighDateTime = 0;
3218       psFTFileData->dwFileSizeHigh           = sFileInfo.nFileSizeHigh;
3219       psFTFileData->dwFileSizeLow            = sFileInfo.nFileSizeLow;
3220     }
3221     else
3222     {
3223       ErrorFormatWinxError(GetLastError(), &pcMessage);
3224       snprintf(acLocalError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: GetFileInformationByHandle(): %s", acRoutine, psFTFileData->pcNeuteredPath, pcMessage);
3225       ErrorHandler(ER_Failure, acLocalError, ERROR_FAILURE);
3226     }
3227 
3228     memset(&sFileBasicInfo, 0, sizeof(FILE_BASIC_INFORMATION));
3229     dwStatus = NtdllNQIF(hFile, &sIOStatusBlock, &sFileBasicInfo, sizeof(FILE_BASIC_INFORMATION), FileBasicInformation);
3230     if (dwStatus == 0)
3231     {
3232       psFTFileData->ulAttributeMask |= MAP_ATTRIBUTES | MAP_ATIME | MAP_MTIME | MAP_CTIME | MAP_CHTIME;
3233       psFTFileData->dwFileAttributes         = sFileBasicInfo.FileAttributes;
3234       psFTFileData->sFTATime.dwLowDateTime   = sFileBasicInfo.LastAccessTime.LowPart;
3235       psFTFileData->sFTATime.dwHighDateTime  = sFileBasicInfo.LastAccessTime.HighPart;
3236       psFTFileData->sFTMTime.dwLowDateTime   = sFileBasicInfo.LastWriteTime.LowPart;
3237       psFTFileData->sFTMTime.dwHighDateTime  = sFileBasicInfo.LastWriteTime.HighPart;
3238       psFTFileData->sFTCTime.dwLowDateTime   = sFileBasicInfo.CreationTime.LowPart;
3239       psFTFileData->sFTCTime.dwHighDateTime  = sFileBasicInfo.CreationTime.HighPart;
3240       psFTFileData->sFTChTime.dwLowDateTime  = sFileBasicInfo.ChangeTime.LowPart;
3241       psFTFileData->sFTChTime.dwHighDateTime = sFileBasicInfo.ChangeTime.HighPart;
3242     }
3243     else
3244     {
3245       snprintf(acLocalError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: NtdllNQIF(): %08x", acRoutine, psFTFileData->pcNeuteredPath, dwStatus);
3246       ErrorHandler(ER_Failure, acLocalError, ERROR_FAILURE);
3247     }
3248 
3249     /*-
3250      *********************************************************************
3251      *
3252      * Harvest security information (owner/group SIDs and DACL).
3253      *
3254      *********************************************************************
3255      */
3256     dwStatus = GetSecurityInfo(
3257       hFile,
3258       SE_FILE_OBJECT,
3259       OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
3260       (PSID) &psFTFileData->psSidOwner,
3261       (PSID) &psFTFileData->psSidGroup,
3262       NULL, /* This pointer is not required to obtain DACL information. */
3263       NULL,
3264 #if (WINVER <= 0x500)
3265       &psFTFileData->psSd
3266 #else
3267       (PSECURITY_DESCRIPTOR) &psFTFileData->psSd
3268 #endif
3269       );
3270     if (dwStatus != ERROR_SUCCESS)
3271     {
3272       dwLastError = dwStatus; /* According to MSDN, GetSecurityInfo() returns a nonzero error code defined in WinError.h when it fails. */
3273       ErrorFormatWinxError(dwLastError, &pcMessage);
3274       snprintf(acLocalError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: GetSecurityInfo(): %s", acRoutine, psFTFileData->pcNeuteredPath, pcMessage);
3275       ErrorHandler(ER_Failure, acLocalError, ERROR_FAILURE);
3276     }
3277     if (!IsValidSecurityDescriptor(psFTFileData->psSd))
3278     {
3279       snprintf(acLocalError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: IsValidSecurityDescriptor(): One or more components of the security descriptor are not valid.", acRoutine, psFTFileData->pcNeuteredPath);
3280       ErrorHandler(ER_Failure, acLocalError, ERROR_FAILURE);
3281     }
3282     else
3283     {
3284       psFTFileData->ulAttributeMask |= MAP_OWNER | MAP_GROUP | MAP_DACL;
3285     }
3286 
3287     /*-
3288      *********************************************************************
3289      *
3290      * Determine the number of alternate streams. This check applies to
3291      * files and directories, and it is NTFS specific. A valid handle
3292      * is required to perform the check.
3293      *
3294      *********************************************************************
3295      */
3296     if (psFTFileData->iFSType == FSTYPE_NTFS)
3297     {
3298       iStatus = MapCountNamedStreams(hFile, &psFTFileData->iStreamCount, &psFTFileData->pucStreamInfo, acLocalError);
3299       if (iStatus == ER_OK)
3300       {
3301         psFTFileData->ulAttributeMask |= MAP_ALTSTREAMS;
3302       }
3303       else
3304       {
3305         snprintf(acLocalError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: Stream Count Failed: %s", acRoutine, psFTFileData->pcNeuteredPath, acLocalError);
3306         ErrorHandler(ER_Failure, acLocalError, ERROR_FAILURE);
3307       }
3308     }
3309     CloseHandle(hFile);
3310   }
3311   else
3312   {
3313     dwLastError = GetLastError();
3314     ErrorFormatWinxError(dwLastError, &pcMessage);
3315     snprintf(acLocalError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: MapGetFileHandleW(): %s", acRoutine, psFTFileData->pcNeuteredPath, pcMessage);
3316     ErrorHandler(ER_Failure, acLocalError, ERROR_FAILURE);
3317     if (dwLastError == ERROR_FILE_NOT_FOUND)
3318     {
3319       psFTFileData->iFileExists = 0;
3320       return psFTFileData->ulAttributeMask;
3321     }
3322 
3323     bResult = GetFileAttributesExW(psFTFileData->pwcRawPath, GetFileExInfoStandard, &sFileAttributeData);
3324     if (bResult)
3325     {
3326       psFTFileData->ulAttributeMask |= MAP_ATTRIBUTES | MAP_ATIME | MAP_MTIME | MAP_CTIME | MAP_SIZE;
3327       psFTFileData->dwFileAttributes        = sFileAttributeData.dwFileAttributes;
3328       psFTFileData->sFTATime.dwLowDateTime  = sFileAttributeData.ftLastAccessTime.dwLowDateTime;
3329       psFTFileData->sFTATime.dwHighDateTime = sFileAttributeData.ftLastAccessTime.dwHighDateTime;
3330       psFTFileData->sFTMTime.dwLowDateTime  = sFileAttributeData.ftLastWriteTime.dwLowDateTime;
3331       psFTFileData->sFTMTime.dwHighDateTime = sFileAttributeData.ftLastWriteTime.dwHighDateTime;
3332       psFTFileData->sFTCTime.dwLowDateTime  = sFileAttributeData.ftCreationTime.dwLowDateTime;
3333       psFTFileData->sFTCTime.dwHighDateTime = sFileAttributeData.ftCreationTime.dwHighDateTime;
3334       psFTFileData->dwFileSizeHigh          = sFileAttributeData.nFileSizeHigh;
3335       psFTFileData->dwFileSizeLow           = sFileAttributeData.nFileSizeLow;
3336     }
3337     else
3338     {
3339       ErrorFormatWinxError(GetLastError(), &pcMessage);
3340       snprintf(acLocalError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: GetFileAttributesExW(): %s", acRoutine, psFTFileData->pcNeuteredPath, pcMessage);
3341       ErrorHandler(ER_Failure, acLocalError, ERROR_FAILURE);
3342     }
3343 
3344     /*-
3345      *********************************************************************
3346      *
3347      * Harvest security information (owner/group SIDs and DACL).
3348      *
3349      *********************************************************************
3350      */
3351     bResult = GetFileSecurityW(
3352       psFTFileData->pwcRawPath,
3353       OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
3354       NULL,
3355       0,
3356       &dwSize
3357       );
3358     if (!bResult && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
3359     {
3360       psFTFileData->psSd = (SECURITY_DESCRIPTOR *) LocalAlloc(LPTR, dwSize);
3361       if (psFTFileData->psSd)
3362       {
3363         bResult = GetFileSecurityW(
3364           psFTFileData->pwcRawPath,
3365           OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
3366           psFTFileData->psSd,
3367           dwSize,
3368           &dwSize
3369           );
3370         if (bResult)
3371         {
3372           if (!IsValidSecurityDescriptor(psFTFileData->psSd))
3373           {
3374             snprintf(acLocalError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: IsValidSecurityDescriptor(): One or more components of the security descriptor are not valid.", acRoutine, psFTFileData->pcNeuteredPath);
3375             ErrorHandler(ER_Failure, acLocalError, ERROR_FAILURE);
3376           }
3377           else
3378           {
3379             BOOL bDefaulted;
3380             GetSecurityDescriptorOwner(psFTFileData->psSd, (PSID) &psFTFileData->psSidOwner, &bDefaulted);
3381             GetSecurityDescriptorGroup(psFTFileData->psSd, (PSID) &psFTFileData->psSidGroup, &bDefaulted);
3382             psFTFileData->ulAttributeMask |= MAP_OWNER | MAP_GROUP | MAP_DACL;
3383           }
3384         }
3385         else
3386         {
3387           ErrorFormatWinxError(GetLastError(), &pcMessage);
3388           snprintf(acLocalError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: GetFileSecurityW(): %s", acRoutine, psFTFileData->pcNeuteredPath, pcMessage);
3389           ErrorHandler(ER_Failure, acLocalError, ERROR_FAILURE);
3390         }
3391       }
3392       else
3393       {
3394         ErrorFormatWinxError(GetLastError(), &pcMessage);
3395         snprintf(acLocalError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: LocalAlloc(): %s", acRoutine, psFTFileData->pcNeuteredPath, pcMessage);
3396         ErrorHandler(ER_Failure, acLocalError, ERROR_FAILURE);
3397       }
3398     }
3399     else
3400     {
3401       ErrorFormatWinxError(GetLastError(), &pcMessage);
3402       snprintf(acLocalError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: GetFileSecurityW(): %s", acRoutine, psFTFileData->pcNeuteredPath, pcMessage);
3403       ErrorHandler(ER_Failure, acLocalError, ERROR_FAILURE);
3404     }
3405   }
3406 #else
3407   /*-
3408    *********************************************************************
3409    *
3410    * Collect attributes. Use lstat() so links aren't followed.
3411    *
3412    *********************************************************************
3413    */
3414   if (lstat(psFTFileData->pcRawPath, &psFTFileData->sStatEntry) != ER)
3415   {
3416     psFTFileData->ulAttributeMask |= MAP_LSTAT_MASK;
3417   }
3418   else
3419   {
3420     int iLastErrno = errno;
3421     snprintf(acLocalError, MESSAGE_SIZE, "%s: NeuteredPath = [%s]: lstat(): %s", acRoutine, psFTFileData->pcNeuteredPath, strerror(errno));
3422     ErrorHandler(ER_Failure, acLocalError, ERROR_FAILURE);
3423     if (iLastErrno == ENOENT || iLastErrno == ENOTDIR)
3424     {
3425       psFTFileData->iFileExists = 0;
3426     }
3427   }
3428 #endif
3429 
3430   return psFTFileData->ulAttributeMask;
3431 }
3432 
3433 
3434 /*-
3435  ***********************************************************************
3436  *
3437  * MapFreeFTFileData
3438  *
3439  ***********************************************************************
3440  */
3441 void
MapFreeFTFileData(FTIMES_FILE_DATA * psFTFileData)3442 MapFreeFTFileData(FTIMES_FILE_DATA *psFTFileData)
3443 {
3444   if (psFTFileData != NULL)
3445   {
3446     if (psFTFileData->pcNeuteredPath != NULL)
3447     {
3448       free(psFTFileData->pcNeuteredPath);
3449     }
3450     if (psFTFileData->pcRawPath != NULL)
3451     {
3452       free(psFTFileData->pcRawPath);
3453     }
3454 #ifdef WINNT
3455     if (psFTFileData->pwcRawPath != NULL)
3456     {
3457       free(psFTFileData->pwcRawPath);
3458     }
3459     if (psFTFileData->psSd != NULL)
3460     {
3461       LocalFree(psFTFileData->psSd);
3462     }
3463     if (psFTFileData->pucStreamInfo != NULL)
3464     {
3465       free(psFTFileData->pucStreamInfo);
3466     }
3467 #endif
3468     free(psFTFileData);
3469   }
3470 }
3471 
3472 
3473 /*-
3474  ***********************************************************************
3475  *
3476  * MapFreePythonArguments
3477  *
3478  ***********************************************************************
3479  */
3480 void
MapFreePythonArguments(size_t szArgumentCount,wchar_t ** ppwcArgumentVector)3481 MapFreePythonArguments(size_t szArgumentCount, wchar_t **ppwcArgumentVector)
3482 {
3483   size_t              szArgument = 0;
3484 
3485   if (ppwcArgumentVector != NULL)
3486   {
3487     for (szArgument = 0; szArgument < szArgumentCount; szArgument++)
3488     {
3489       if (ppwcArgumentVector[szArgument] != NULL)
3490       {
3491         free(ppwcArgumentVector[szArgument]);
3492       }
3493     }
3494     free(ppwcArgumentVector);
3495   }
3496 
3497   return;
3498 }
3499 
3500 
3501 #ifndef WINNT
3502 /*-
3503  ***********************************************************************
3504  *
3505  * MapNewFTFileData
3506  *
3507  ***********************************************************************
3508  */
3509 FTIMES_FILE_DATA *
MapNewFTFileData(FTIMES_FILE_DATA * psParentFTFileData,char * pcName,char * pcError)3510 MapNewFTFileData(FTIMES_FILE_DATA *psParentFTFileData, char *pcName, char *pcError)
3511 {
3512   const char          acRoutine[] = "MapNewFTFileData()";
3513   char                acLocalError[MESSAGE_SIZE] = "";
3514   char                acSeparator[2] = "";
3515   int                 iFSType = FSTYPE_UNSUPPORTED;
3516   FTIMES_FILE_DATA   *psFTFileData = NULL;
3517 
3518   /*
3519    *********************************************************************
3520    *
3521    * Allocate and clear memory for the file data structure.
3522    *
3523    *********************************************************************
3524    */
3525   psFTFileData = (FTIMES_FILE_DATA *) calloc(sizeof(FTIMES_FILE_DATA), 1);
3526   if (psFTFileData == NULL)
3527   {
3528     snprintf(pcError, MESSAGE_SIZE, "%s: calloc(): %s", acRoutine, strerror(errno));
3529     return NULL;
3530   }
3531 
3532   /*
3533    *********************************************************************
3534    *
3535    * Initialize variables that require a nonzero value. Also note that
3536    * subsequent logic relies on the assertion that each hash value has
3537    * been initialized to all zeros.
3538    *
3539    *********************************************************************
3540    */
3541   psFTFileData->psParent = psParentFTFileData;
3542 
3543   /*-
3544    *********************************************************************
3545    *
3546    * Create the new path. Impose a limit to keep things under control.
3547    *
3548    *********************************************************************
3549    */
3550   if (psParentFTFileData)
3551   {
3552     psFTFileData->iRawPathLength = psParentFTFileData->iRawPathLength + strlen(pcName);
3553     if (psParentFTFileData->pcRawPath[psParentFTFileData->iRawPathLength - 1] != FTIMES_SLASHCHAR)
3554     {
3555       acSeparator[0] = FTIMES_SLASHCHAR;
3556       acSeparator[1] = 0;
3557       psFTFileData->iRawPathLength++;
3558     }
3559   }
3560   else
3561   {
3562     psFTFileData->iRawPathLength = strlen(pcName);
3563   }
3564   if (psFTFileData->iRawPathLength > FTIMES_MAX_PATH - 1) /* Subtract one for the NULL. */
3565   {
3566     snprintf(pcError, MESSAGE_SIZE, "%s: Length (%d) exceeds %d bytes.", acRoutine, psFTFileData->iRawPathLength, FTIMES_MAX_PATH - 1);
3567     MapFreeFTFileData(psFTFileData);
3568     return NULL;
3569   }
3570   psFTFileData->pcRawPath = malloc(psFTFileData->iRawPathLength + 1);
3571   if (psFTFileData->pcRawPath == NULL)
3572   {
3573     snprintf(pcError, MESSAGE_SIZE, "%s: malloc(): %s", acRoutine, strerror(errno));
3574     MapFreeFTFileData(psFTFileData);
3575     return NULL;
3576   }
3577   if (psParentFTFileData)
3578   {
3579     snprintf(psFTFileData->pcRawPath, FTIMES_MAX_PATH, "%s%s%s", psParentFTFileData->pcRawPath, acSeparator, pcName);
3580   }
3581   else
3582   {
3583     snprintf(psFTFileData->pcRawPath, FTIMES_MAX_PATH, "%s", pcName);
3584   }
3585 
3586   /*-
3587    *********************************************************************
3588    *
3589    * Neuter the new path.
3590    *
3591    *********************************************************************
3592    */
3593   psFTFileData->pcNeuteredPath = SupportNeuterString(psFTFileData->pcRawPath, psFTFileData->iRawPathLength, acLocalError);
3594   if (psFTFileData->pcNeuteredPath == NULL)
3595   {
3596     snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
3597     MapFreeFTFileData(psFTFileData);
3598     return NULL;
3599   }
3600 
3601   /*-
3602    *********************************************************************
3603    *
3604    * Conditionally determine file system type. This value is required
3605    * by MapGetAttributes() under WINX, so it is set here.
3606    *
3607    *********************************************************************
3608    */
3609   if (psParentFTFileData)
3610   {
3611     psFTFileData->iFSType = psParentFTFileData->iFSType; /* Inherit file system type. */
3612   }
3613   else
3614   {
3615     iFSType = GetFileSystemType(psFTFileData->pcRawPath, acLocalError);
3616     if (iFSType == ER || iFSType == FSTYPE_UNSUPPORTED)
3617     {
3618       snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
3619       MapFreeFTFileData(psFTFileData);
3620       return NULL;
3621     }
3622     psFTFileData->iFSType = iFSType;
3623   }
3624 
3625   return psFTFileData;
3626 }
3627 #endif
3628 
3629 
3630 #ifdef WINNT
3631 /*-
3632  ***********************************************************************
3633  *
3634  * MapNewFTFileData
3635  *
3636  ***********************************************************************
3637  */
3638 FTIMES_FILE_DATA *
MapNewFTFileDataW(FTIMES_FILE_DATA * psParentFTFileData,wchar_t * pwcName,char * pcError)3639 MapNewFTFileDataW(FTIMES_FILE_DATA *psParentFTFileData, wchar_t *pwcName, char *pcError)
3640 {
3641   const char          acRoutine[] = "MapNewFTFileDataW()";
3642   char                acLocalError[MESSAGE_SIZE] = "";
3643   wchar_t             awcSeparator[2] = L"";
3644   int                 iFSType = FSTYPE_UNSUPPORTED;
3645   FTIMES_FILE_DATA   *psFTFileData = NULL;
3646 
3647   /*
3648    *********************************************************************
3649    *
3650    * Allocate and clear memory for the file data structure.
3651    *
3652    *********************************************************************
3653    */
3654   psFTFileData = (FTIMES_FILE_DATA *) calloc(sizeof(FTIMES_FILE_DATA), 1);
3655   if (psFTFileData == NULL)
3656   {
3657     snprintf(pcError, MESSAGE_SIZE, "%s: calloc(): %s", acRoutine, strerror(errno));
3658     return NULL;
3659   }
3660 
3661   /*
3662    *********************************************************************
3663    *
3664    * Initialize variables that require a nonzero value. Also note that
3665    * subsequent logic relies on the assertion that each hash value has
3666    * been initialized to all zeros.
3667    *
3668    *********************************************************************
3669    */
3670   psFTFileData->dwVolumeSerialNumber = -1;
3671   psFTFileData->dwFileIndexHigh = -1;
3672   psFTFileData->dwFileIndexLow = -1;
3673   psFTFileData->iStreamCount = FTIMES_INVALID_STREAM_COUNT; /* The Develop{Compressed,Normal}Output routines check for this value. */
3674   psFTFileData->psParent = psParentFTFileData;
3675 
3676   /*-
3677    *********************************************************************
3678    *
3679    * Create the new path. Impose a limit to keep things under control.
3680    *
3681    *********************************************************************
3682    */
3683   if (psParentFTFileData)
3684   {
3685     psFTFileData->iWideRawPathLength = psParentFTFileData->iWideRawPathLength + wcslen(pwcName);
3686     if (psParentFTFileData->pwcRawPath[psParentFTFileData->iWideRawPathLength - 1] != FTIMES_SLASHCHAR_W)
3687     {
3688       awcSeparator[0] = FTIMES_SLASHCHAR_W;
3689       awcSeparator[1] = 0;
3690       psFTFileData->iWideRawPathLength++;
3691     }
3692   }
3693   else
3694   {
3695 //THIS CHANGE IS PART OF EXTENDED PREFIX SUPPORT (\\?\)
3696 //    psFTFileData->iWideRawPathLength = wcslen(pwcName);
3697     psFTFileData->iWideRawPathLength = FTIMES_EXTENDED_PREFIX_SIZE + wcslen(pwcName);
3698 //END (\\?\)
3699   }
3700 //THIS CHANGE IS PART OF EXTENDED PREFIX SUPPORT (\\?\)
3701 //  if (psFTFileData->iWideRawPathLength > FTIMES_MAX_PATH - 1) /* Subtract one for the NULL. */
3702   if (psFTFileData->iWideRawPathLength - FTIMES_EXTENDED_PREFIX_SIZE > FTIMES_MAX_PATH - 1) /* Subtract one for the NULL. */
3703 //END (\\?\)
3704   {
3705     snprintf(pcError, MESSAGE_SIZE, "%s: Length (%d) exceeds %d bytes.", acRoutine, psFTFileData->iWideRawPathLength, FTIMES_MAX_PATH - 1);
3706     MapFreeFTFileData(psFTFileData);
3707     return NULL;
3708   }
3709   psFTFileData->pwcRawPath = malloc((psFTFileData->iWideRawPathLength + 1) * sizeof(wchar_t));
3710   if (psFTFileData->pwcRawPath == NULL)
3711   {
3712     snprintf(pcError, MESSAGE_SIZE, "%s: malloc(): %s", acRoutine, strerror(errno));
3713     MapFreeFTFileData(psFTFileData);
3714     return NULL;
3715   }
3716   if (psParentFTFileData)
3717   {
3718 //THIS CHANGE IS PART OF EXTENDED PREFIX SUPPORT (\\?\)
3719 //    _snwprintf(psFTFileData->pwcRawPath, psFTFileData->iWideRawPathLength + 1, L"%s%s%s", psParentFTFileData->pwcRawPath, awcSeparator, pwcName);
3720     _snwprintf(psFTFileData->pwcRawPath, psFTFileData->iWideRawPathLength + 1, L"%s%s%s", psParentFTFileData->pwcRawPath, awcSeparator, pwcName); /* The extended path prefix should already be included. */
3721 //END (\\?\)
3722   }
3723   else
3724   {
3725 //THIS CHANGE IS PART OF EXTENDED PREFIX SUPPORT (\\?\)
3726 //    _snwprintf(psFTFileData->pwcRawPath, psFTFileData->iWideRawPathLength + 1, L"%s", pwcName);
3727     _snwprintf(psFTFileData->pwcRawPath, psFTFileData->iWideRawPathLength + 1, L"\\\\?\\%s", pwcName); /* Include the extended path prefix since there is no parent. */
3728 //END (\\?\)
3729   }
3730 
3731   /*-
3732    *********************************************************************
3733    *
3734    * Convert the new path to UTF-8.
3735    *
3736    *********************************************************************
3737    */
3738 //THIS CHANGE IS PART OF EXTENDED PREFIX SUPPORT (\\?\)
3739 //  psFTFileData->pcRawPath = MapWideToUtf8(psFTFileData->pwcRawPath, psFTFileData->iWideRawPathLength + 1, acLocalError);
3740   psFTFileData->pcRawPath = MapWideToUtf8(&psFTFileData->pwcRawPath[FTIMES_EXTENDED_PREFIX_SIZE], psFTFileData->iWideRawPathLength - FTIMES_EXTENDED_PREFIX_SIZE + 1, acLocalError);
3741 //END (\\?\)
3742   if (psFTFileData->pcRawPath == NULL)
3743   {
3744     snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
3745     MapFreeFTFileData(psFTFileData);
3746     return NULL;
3747   }
3748   psFTFileData->iUtf8RawPathLength = strlen(psFTFileData->pcRawPath);
3749 
3750   /*-
3751    *********************************************************************
3752    *
3753    * Neuter the new path.
3754    *
3755    *********************************************************************
3756    */
3757   psFTFileData->pcNeuteredPath = SupportNeuterString(psFTFileData->pcRawPath, psFTFileData->iUtf8RawPathLength, acLocalError);
3758   if (psFTFileData->pcNeuteredPath == NULL)
3759   {
3760     snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
3761     MapFreeFTFileData(psFTFileData);
3762     return NULL;
3763   }
3764 
3765   /*-
3766    *********************************************************************
3767    *
3768    * Conditionally determine file system type. This value is required
3769    * by MapGetAttributes() under WINX, so it is set here.
3770    *
3771    *********************************************************************
3772    */
3773   if (psParentFTFileData)
3774   {
3775     psFTFileData->iFSType = psParentFTFileData->iFSType; /* Inherit file system type. */
3776   }
3777   else
3778   {
3779     iFSType = GetFileSystemType(psFTFileData->pcRawPath, acLocalError);
3780     if (iFSType == ER || iFSType == FSTYPE_UNSUPPORTED)
3781     {
3782       snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
3783       MapFreeFTFileData(psFTFileData);
3784       return NULL;
3785     }
3786     psFTFileData->iFSType = iFSType;
3787   }
3788 
3789   return psFTFileData;
3790 }
3791 
3792 
3793 /*-
3794  ***********************************************************************
3795  *
3796  * MapUtf8ToWide
3797  *
3798  ***********************************************************************
3799  */
3800 wchar_t *
MapUtf8ToWide(char * pcString,int iUtf8Size,char * pcError)3801 MapUtf8ToWide(char *pcString, int iUtf8Size, char *pcError)
3802 {
3803   const char          acRoutine[] = "MapUtf8ToWide()";
3804   char               *pcMessage = NULL;
3805   int                 iWideSize = 0;
3806   wchar_t            *pwcString = NULL;
3807 
3808   iWideSize = MultiByteToWideChar(CP_UTF8, 0, pcString, iUtf8Size, NULL, 0); /* The byte count returned includes the NULL terminator. */
3809   if (iWideSize)
3810   {
3811     pwcString = malloc(iWideSize * sizeof(wchar_t));
3812     if (pwcString == NULL)
3813     {
3814       snprintf(pcError, MESSAGE_SIZE, "%s: malloc(): %s", acRoutine, strerror(errno));
3815       return NULL;
3816     }
3817     iWideSize = MultiByteToWideChar(CP_UTF8, 0, pcString, iUtf8Size, pwcString, iWideSize);
3818     if (!iWideSize)
3819     {
3820       ErrorFormatWinxError(GetLastError(), &pcMessage);
3821       snprintf(pcError, MESSAGE_SIZE, "%s: MultiByteToWideChar(): %s", acRoutine, pcMessage);
3822       free(pwcString);
3823       return NULL;
3824     }
3825   }
3826   else
3827   {
3828     ErrorFormatWinxError(GetLastError(), &pcMessage);
3829     snprintf(pcError, MESSAGE_SIZE, "%s: MultiByteToWideChar(): %s", acRoutine, pcMessage);
3830     return NULL;
3831   }
3832 
3833   return pwcString;
3834 }
3835 
3836 
3837 /*-
3838  ***********************************************************************
3839  *
3840  * MapWideToUtf8
3841  *
3842  ***********************************************************************
3843  */
3844 char *
MapWideToUtf8(wchar_t * pwcString,int iWideSize,char * pcError)3845 MapWideToUtf8(wchar_t *pwcString, int iWideSize, char *pcError)
3846 {
3847   const char          acRoutine[] = "MapWideToUtf8()";
3848   char               *pcMessage = NULL;
3849   char               *pcString = NULL;
3850   int                 iUtf8Size = 0;
3851 
3852   iUtf8Size = WideCharToMultiByte(CP_UTF8, 0, pwcString, iWideSize, NULL, 0, NULL, NULL); /* The byte count returned includes the NULL terminator. */
3853   if (iUtf8Size)
3854   {
3855     pcString = malloc(iUtf8Size);
3856     if (pcString == NULL)
3857     {
3858       snprintf(pcError, MESSAGE_SIZE, "%s: malloc(): %s", acRoutine, strerror(errno));
3859       return NULL;
3860     }
3861     iUtf8Size = WideCharToMultiByte(CP_UTF8, 0, pwcString, iWideSize, pcString, iUtf8Size, NULL, NULL);
3862     if (!iUtf8Size)
3863     {
3864       ErrorFormatWinxError(GetLastError(), &pcMessage);
3865       snprintf(pcError, MESSAGE_SIZE, "%s: WideCharToMultiByte(): %s", acRoutine, pcMessage);
3866       free(pcString);
3867       return NULL;
3868     }
3869   }
3870   else
3871   {
3872     ErrorFormatWinxError(GetLastError(), &pcMessage);
3873     snprintf(pcError, MESSAGE_SIZE, "%s: WideCharToMultiByte(): %s", acRoutine, pcMessage);
3874     return NULL;
3875   }
3876 
3877   return pcString;
3878 }
3879 #endif
3880