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