1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * "find_lock.exe", for Windows only.
19 *
20 * References used:
21 *
22 * http://drdobbs.com/windows/184411099
23 * article by Sven B. Schreiber, November 01, 1999
24 *
25 * http://www.codeguru.com/Cpp/W-P/system/processesmodules/article.php/c2827/
26 * by Zoltan Csizmadia, November 14, 2000
27 *
28 * http://stackoverflow.com/questions/860656/
29 * (same technique, but written in unsafe C#)
30 *
31 * Starting with Vista, we can also use the Restart Manager API as
32 * explained here: (TODO for next version)
33 * http://msdn.microsoft.com/en-us/magazine/cc163450.aspx
34 */
35
36#ifdef _WIN32
37
38#include "utils.h"
39#include <ctype.h>
40#include <fcntl.h>
41#include <io.h>
42#include <process.h>
43
44
45// NtDll structures from the the Dr Dobbs article, adjusted for our needs:
46
47typedef void *POBJECT;
48typedef LONG KPRIORITY;
49typedef LARGE_INTEGER QWORD;
50
51typedef struct {
52    WORD  Length;
53    WORD  MaximumLength;
54    PWORD Buffer;
55} UNICODE_STRING;
56
57typedef struct {
58    DWORD       dIdProcess;
59    BYTE        bObjectType;    // OB_TYPE_*
60    BYTE        bFlags;         // bits 0..2 HANDLE_FLAG_*
61    WORD        wValue;         // multiple of 4
62    POBJECT     pObject;
63    ACCESS_MASK GrantedAccess;
64} SYSTEM_HANDLE;
65
66typedef struct {
67    DWORD         dCount;
68    SYSTEM_HANDLE ash[1];
69} SYSTEM_HANDLE_INFORMATION;
70
71typedef struct {
72    DWORD PeakVirtualSize;
73    DWORD VirtualSize;
74    DWORD PageFaultCount;
75    DWORD PeakWorkingSetSize;
76    DWORD WorkingSetSize;
77    DWORD QuotaPeakPagedPoolUsage;
78    DWORD QuotaPagedPoolUsage;
79    DWORD QuotaPeakNonPagedPoolUsage;
80    DWORD QuotaNonPagedPoolUsage;
81    DWORD PagefileUsage;
82    DWORD PeakPagefileUsage;
83} VM_COUNTERS;
84
85typedef struct {
86    HANDLE UniqueProcess;
87    HANDLE UniqueThread;
88} CLIENT_ID;
89
90typedef enum {
91    // Ignored. We don't actually use these values.
92    Unused
93} KWAIT_REASON;
94
95typedef struct {
96    QWORD        qKernelTime;       // 100 nsec units
97    QWORD        qUserTime;         // 100 nsec units
98    QWORD        qCreateTime;       // relative to 01-01-1601
99    DWORD        d18;
100    PVOID        pStartAddress;
101    CLIENT_ID    Cid;               // process/thread ids
102    DWORD        dPriority;
103    DWORD        dBasePriority;
104    DWORD        dContextSwitches;
105    DWORD        dThreadState;      // 2=running, 5=waiting
106    KWAIT_REASON WaitReason;
107    DWORD        dReserved01;
108} SYSTEM_THREAD;
109
110typedef struct {
111    DWORD          dNext;           // relative offset
112    DWORD          dThreadCount;
113    DWORD          dReserved01;
114    DWORD          dReserved02;
115    DWORD          dReserved03;
116    DWORD          dReserved04;
117    DWORD          dReserved05;
118    DWORD          dReserved06;
119    QWORD          qCreateTime;     // relative to 01-01-1601
120    QWORD          qUserTime;       // 100 nsec units
121    QWORD          qKernelTime;     // 100 nsec units
122    UNICODE_STRING usName;
123    KPRIORITY      BasePriority;
124    DWORD          dUniqueProcessId;
125    DWORD          dInheritedFromUniqueProcessId;
126    DWORD          dHandleCount;
127    DWORD          dReserved07;
128    DWORD          dReserved08;
129    VM_COUNTERS    VmCounters;
130    DWORD          dCommitCharge;   // bytes
131    SYSTEM_THREAD  ast[1];
132} SYSTEM_PROCESS_INFORMATION;
133
134// The sic opcode for NtQuerySystemInformation
135typedef enum {
136    SystemProcessInformation = 5,
137    SystemHandleInformation = 16,
138} SYSTEMINFOCLASS;
139
140
141#define STATUS_SUCCESS               0x00000000
142#define STATUS_UNSUCCESSFUL          0xC0000001
143#define STATUS_NOT_IMPLEMENTED       0xC0000002
144#define STATUS_INVALID_INFO_CLASS    0xC0000003
145#define STATUS_INFO_LENGTH_MISMATCH  0xC0000004
146#define STATUS_INVALID_PARAMETER     0xC000000D
147
148typedef DWORD (WINAPI *NtQuerySystemInformationFuncPtr)(
149                                      DWORD sic, VOID* pData, DWORD sSize, ULONG* pdSize);
150typedef DWORD (WINAPI *NtQueryInformationFileFuncPtr)(HANDLE, PVOID, PVOID, DWORD, DWORD);
151typedef DWORD (WINAPI *NtQueryObjectFuncPtr)(HANDLE, DWORD, VOID*, DWORD, VOID*);
152
153static NtQuerySystemInformationFuncPtr sNtQuerySystemInformationFunc;
154static NtQueryInformationFileFuncPtr   sNtQueryInformationFileFunc;
155static NtQueryObjectFuncPtr            sNtQueryObjectFunc;
156
157//------------
158
159// Get the NT DLL functions we need to use.
160static bool init() {
161
162    sNtQuerySystemInformationFunc =
163        (NtQuerySystemInformationFuncPtr) GetProcAddress(
164            GetModuleHandleA("ntdll.dll"), "NtQuerySystemInformation");
165
166    sNtQueryInformationFileFunc =
167        (NtQueryInformationFileFuncPtr) GetProcAddress(
168            GetModuleHandleA("ntdll.dll"), "NtQueryInformationFile");
169
170    sNtQueryObjectFunc =
171        (NtQueryObjectFuncPtr) GetProcAddress(
172            GetModuleHandleA("ntdll.dll"), "NtQueryObject");
173
174    return sNtQuerySystemInformationFunc != NULL &&
175           sNtQueryInformationFileFunc   != NULL &&
176           sNtQueryObjectFunc            != NULL;
177}
178
179static void terminate() {
180    sNtQuerySystemInformationFunc = NULL;
181    sNtQueryInformationFileFunc = NULL;
182    sNtQueryObjectFunc = NULL;
183}
184
185static bool adjustPrivileges() {
186    char *error = NULL;
187    HANDLE tokenH;
188
189    // Open a process token that lets us adjust privileges
190    BOOL ok = OpenProcessToken(GetCurrentProcess(),   // ProcessHandle
191                               TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, // DesiredAccess
192                               &tokenH);              // TokenHandle
193    if (!ok) {
194        error = "OpenProcessToken failed: ";
195        goto bail_out;
196    }
197
198    // Lookup the privilege by name and get its local LUID token.
199    // What we request:
200    // SE_DEBUG_NAME, aka "SeDebugPrivilege"
201    // MSDN: Required to debug and adjust the memory of a process owned by another account.
202    //       User Right: Debug programs.
203    TOKEN_PRIVILEGES priv;
204    priv.PrivilegeCount = 1;
205    priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
206    ok = LookupPrivilegeValueA(NULL,                        // lpSystemName
207                               SE_DEBUG_NAME,               // lpName
208                               &(priv.Privileges[0].Luid)); // lpLuid
209    if (!ok) {
210        error = "LookupPrivilegeValue failed: ";
211        goto bail_out;
212    }
213
214    ok = AdjustTokenPrivileges(tokenH,  // TokenHandle
215                               FALSE,   // DisableAllPrivileges
216                               &priv,   // NewState
217                               0,       // BufferLength
218                               NULL,    // PreviousState
219                               0);      // ReturnLength
220    if (!ok) {
221        error = "AdjustTokenPrivileges failed: ";
222        goto bail_out;
223    }
224
225bail_out:
226    if (error != NULL && gIsDebug) {
227        CString err;
228        err.setLastWin32Error(error);
229        fprintf(stderr, "%s", err.cstr());
230    }
231
232    if (tokenH != NULL) {
233        CloseHandle(tokenH);
234    }
235
236    return !!ok;
237}
238
239static bool getHandleType(HANDLE h, CString *type) {
240    bool result = false;
241    ULONG size = 0;
242    // Get the size of the type string
243    int status = sNtQueryObjectFunc(h, 2, NULL, 0, &size);
244    if (status == STATUS_INFO_LENGTH_MISMATCH && size > 0) {
245        // Get the type string itself
246        char *buf = new char[size];
247        status = sNtQueryObjectFunc(h, 2, buf, size, NULL);
248        if (status == 0 && size > 96) {
249            // The type string we want is a wide unicode (UTF16)
250            // zero-terminated string located at offset 96 in the
251            // buffer. In our case we want the string to be
252            // "Directory" or "File" so we know the max useful length
253            // is 9.
254            // Since we can only deal with ansi strings in this program,
255            // we'll make a crude copy of every other byte and just check
256            // that the other bytes are zero.
257            const char *c = buf + 96;
258            const char *e = buf + 96 + size;
259            // we'll write at the beginning of our buffer
260            char *dest = buf;
261            char *dend = dest + 9;
262            for (; c < e && dest < dend && c[0] != '\0' && c[1] == '\0'; c += 2, dest++) {
263                *dest = *c;
264            }
265            *(dest++) = '\0';
266            type->set(buf, dest - buf);
267            result = true;
268        }
269
270        free(buf);
271    }
272    return result;
273}
274
275// These is the wide unicode representations of the type we want to find.
276static const char kFileW[] = "File";
277
278static char isFileHandleType(HANDLE handle) {
279    char type = 0;
280    ULONG size = 0;
281    // Get the size of the type string
282    int status = sNtQueryObjectFunc(handle, 2, NULL, 0, &size);
283    if (status == STATUS_INFO_LENGTH_MISMATCH && size > 0) {
284        // Get the type string itself
285        char *buf = new char[size];
286        status = sNtQueryObjectFunc(handle, 2, buf, size, NULL);
287        if (status == 0 && size > 96) {
288            // The type string we want is a wide unicode (UTF16-LE)
289            // zero-terminated string located at offset 96 in the
290            // buffer. In our case we want the string to be "File".
291            //
292            // Since we're reading wide unicode, we want each character
293            // to be the one from our string followed by a zero byte.
294            // e.g. c should point to F \0 i \0 l \0 e \0 \0 \0.
295            const char *c = buf + 96;
296            type = c[0];
297
298            int len = sizeof(kFileW);
299            const char *d = kFileW;
300
301            for (; type != 0 && len > 0; c+=2, d++, len--) {
302                if (c[0] != *d || c[1] != 0) {
303                    type = 0;
304                    break;
305                }
306            }
307        }
308
309        free(buf);
310    }
311    return type;
312}
313
314typedef struct {
315    HANDLE handle;
316    CString *outStr;
317    bool result;
318} SFileNameInfo;
319
320static unsigned __stdcall FileNameThreadFunc(void *param) {
321    SFileNameInfo *info = (SFileNameInfo *)param;
322    if (info == NULL) {
323        return 1;
324    }
325
326    char buf[MAX_PATH*2 + 4];
327    DWORD iob[2] = { 0, 0 };
328
329    DWORD status = sNtQueryInformationFileFunc(info->handle, iob, buf, sizeof(buf), 9);
330    if (status == STATUS_SUCCESS) {
331        // The result is a buffer with:
332        // - DWORD (4 bytes) for the *byte* length (so twice the character length)
333        // - Actual string in Unicode
334        // Not sure of the actual type, but it does look like a UNICODE_STRING struct.
335
336        DWORD len = ((DWORD *)buf)[0];
337        if (len <= MAX_PATH * 2) {
338            // We can't handle wide Unicode. What we do is convert it into
339            // straight ansi by just retaining the first of each couple bytes.
340            // Bytes that cannot be mapped (e.g. 2nd byte is != 0) will be
341            // simply converted to 0xFF.
342
343            unsigned char *dest = (unsigned char *)buf + 4;
344            unsigned char *src  = (unsigned char *)buf + 4;
345            for (DWORD i = 0; i < len; dest++, src += 2, i += 2) {
346                if (src[1] == 0) {
347                    *dest = *src;
348                } else {
349                    *dest = 0xFF;
350                }
351            }
352            *dest = '\0';
353            info->outStr->set(buf + 4, len);
354            info->result = true;
355            return 0;
356        }
357    }
358    return 1;
359}
360
361static bool getFileName(HANDLE handle, CString *outStr) {
362    SFileNameInfo info;
363    info.handle = handle;
364    info.outStr = outStr;
365    info.result = false;
366
367    // sNtQueryInformationFileFunc might hang on some handles.
368    // A trick is to do it in a thread and if it takes too loog then
369    // just shutdown the thread, since it's deadlocked anyway.
370    unsigned threadId;
371    HANDLE th = (HANDLE)_beginthreadex(NULL,                    // security
372                                       0,                       // stack_size
373                                       &FileNameThreadFunc,     // address
374                                       &info,                   // arglist
375                                       0,                       // initflag
376                                       &threadId);              // thrdaddr
377
378    if (th == NULL) {
379        // Failed to create thread. Shouldn't really happen.
380        outStr->set("<failed to create thread>");
381        return false;
382    }
383
384    bool result = false;
385
386    // Wait for thread or kill it if it takes too long.
387    if (WaitForSingleObject(th /*handle*/, 200 /*ms*/) == WAIT_TIMEOUT) {
388        TerminateThread(th /*handle*/, 0 /*retCode*/);
389        outStr->set("<timeout>");
390    } else {
391        result = info.result;
392    }
393
394    CloseHandle(th);
395    return result;
396}
397
398// Find the name of the process (e.g. "java.exe") given its id.
399// processesPtr must be the list returned by getAllProcesses().
400// Special handling for javaw.exe: this isn't quite useful so
401// we also try to find and append the parent process name.
402static bool getProcessName(SYSTEM_PROCESS_INFORMATION *processesPtr,
403                           DWORD remoteProcessId,
404                           CString *outStr) {
405    SYSTEM_PROCESS_INFORMATION *ptr = processesPtr;
406    while (ptr != NULL) {
407        if (ptr->dUniqueProcessId == remoteProcessId) {
408            // This is the process we want.
409
410            UNICODE_STRING *uniStr = &(ptr->usName);
411            WORD len = uniStr->Length;
412
413            char buf[MAX_PATH];
414            if (len <= MAX_PATH * 2) {
415                // We can't handle wide Unicode. What we do is convert it into
416                // straight ansi by just retaining the first of each couple bytes.
417                // Bytes that cannot be mapped (e.g. 2nd byte is != 0) will be
418                // simply converted to 0xFF.
419
420                unsigned char *dest = (unsigned char *)buf;
421                unsigned char *src  = (unsigned char *)uniStr->Buffer;
422                for (WORD i = 0; i < len; dest++, src += 2, i += 2) {
423                    if (src[1] == 0) {
424                        *dest = *src;
425                    } else {
426                        *dest = 0xFF;
427                    }
428                }
429                *dest = '\0';
430                outStr->set(buf, len);
431
432                if (strcmp(buf, "javaw.exe") == 0) {
433                    // Heuristic: eclipse often shows up as javaw.exe
434                    // but what is useful is to report eclipse to the user
435                    // instead.
436                    // So in this case, look at the parent and report it too.
437                    DWORD parentId = ptr->dInheritedFromUniqueProcessId;
438                    if (parentId > 0) {
439                        CString name2;
440                        bool ok2 = getProcessName(processesPtr,
441                                                  parentId,
442                                                  &name2);
443                        if (ok2) {
444                            outStr->add(" (");
445                            outStr->add(name2.cstr());
446                            outStr->add(")");
447                        }
448                    }
449                }
450
451                return true;
452            }
453        }
454
455        // Look at the next process, if any.
456        if (ptr->dNext == NULL) {
457            break;
458        } else {
459            ptr = (SYSTEM_PROCESS_INFORMATION *)((char *)ptr + ptr->dNext);
460        }
461    }
462
463    outStr->setf("<process id %08x name not found>", remoteProcessId);
464    return false;
465}
466
467// Query system for all processes information.
468// Returns an error string in case of error.
469// Returns the virtual_alloc-allocated buffer on success or NULL on error.
470// It's up to the caller to do a VirtualFree on the returned buffer.
471static SYSTEM_PROCESS_INFORMATION *queryAllProcess(const char **error) {
472    // Allocate a buffer for the process information. We don't know the
473    // exact size. A normal system might typically have between 100-200 processes.
474    // We'll resize the buffer if not big enough.
475    DWORD infoSize = 4096;
476    SYSTEM_PROCESS_INFORMATION *infoPtr =
477        (SYSTEM_PROCESS_INFORMATION *) VirtualAlloc(NULL, infoSize, MEM_COMMIT, PAGE_READWRITE);
478
479    if (infoPtr != NULL) {
480        // Query the actual size needed (or the data if it fits in the buffer)
481        DWORD needed = 0;
482        if (sNtQuerySystemInformationFunc(
483                SystemProcessInformation, infoPtr, infoSize, &needed) != 0) {
484            if (needed == 0) {
485                // Shouldn't happen.
486                *error = "No processes found";
487                goto bail_out;
488            }
489
490            // Realloc
491            VirtualFree(infoPtr, 0, MEM_RELEASE);
492            infoSize += needed;
493            infoPtr = (SYSTEM_PROCESS_INFORMATION *) VirtualAlloc(
494                            NULL, infoSize, MEM_COMMIT, PAGE_READWRITE);
495
496            // Query all the processes objects again
497            if (sNtQuerySystemInformationFunc(
498                    SystemProcessInformation, infoPtr, infoSize, NULL) != 0) {
499                *error = "Failed to query system processes";
500                goto bail_out;
501            }
502        }
503    }
504
505    if (infoPtr == NULL) {
506        *error = "Failed to allocate system processes info buffer";
507        goto bail_out;
508    }
509
510bail_out:
511    if (*error != NULL) {
512        VirtualFree(infoPtr, 0, MEM_RELEASE);
513        infoPtr = NULL;
514    }
515    return infoPtr;
516}
517
518// Query system for all handle information.
519// Returns an error string in case of error.
520// Returns the virtual_alloc-allocated buffer on success or NULL on error.
521// It's up to the caller to do a VirtualFree on the returned buffer.
522static SYSTEM_HANDLE_INFORMATION *queryAllHandles(const char **error) {
523    // Allocate a buffer. It won't be large enough to get the handles
524    // (e.g. there might be 10k or 40k handles around). We'll resize
525    // it once we know the actual size.
526    DWORD infoSize = 4096;
527    SYSTEM_HANDLE_INFORMATION *infoPtr =
528        (SYSTEM_HANDLE_INFORMATION *) VirtualAlloc(NULL, infoSize, MEM_COMMIT, PAGE_READWRITE);
529
530    if (infoPtr != NULL) {
531        // Query the actual size needed
532        DWORD needed = 0;
533        if (sNtQuerySystemInformationFunc(
534                SystemHandleInformation, infoPtr, infoSize, &needed) != 0) {
535            if (needed == 0) {
536                // Shouldn't happen.
537                *error = "No handles found";
538                goto bail_out;
539            }
540
541            // Realloc
542            VirtualFree(infoPtr, 0, MEM_RELEASE);
543            infoSize += needed;
544            infoPtr = (SYSTEM_HANDLE_INFORMATION *) VirtualAlloc(
545                            NULL, infoSize, MEM_COMMIT, PAGE_READWRITE);
546        }
547    }
548
549    if (infoPtr == NULL) {
550        *error = "Failed to allocate system handle info buffer";
551        goto bail_out;
552    }
553
554    // Query all the handle objects
555    if (sNtQuerySystemInformationFunc(SystemHandleInformation, infoPtr, infoSize, NULL) != 0) {
556        *error = "Failed to query system handles";
557        goto bail_out;
558    }
559
560bail_out:
561    if (*error != NULL) {
562        VirtualFree(infoPtr, 0, MEM_RELEASE);
563        infoPtr = NULL;
564    }
565    return infoPtr;
566}
567
568bool findLock(CPath &path, CString *outModule) {
569    bool result = false;
570    const char *error = NULL;
571
572    SYSTEM_PROCESS_INFORMATION *processesPtr = NULL;
573    SYSTEM_HANDLE_INFORMATION  *handlesPtr   = NULL;
574
575    const HANDLE currProcessH = GetCurrentProcess();
576    const DWORD currProcessId = GetCurrentProcessId();
577    HANDLE remoteProcessH = NULL;
578    DWORD remoteProcessId = 0;
579    DWORD matchProcessId = 0;
580
581    int numHandleFound = 0;
582    int numHandleChecked = 0;
583    int numHandleDirs = 0;
584    int numHandleFiles = 0;
585    int numProcessMatch = 0;
586
587    BYTE ob_type_file = 0;
588
589    // Get the path to search, without the drive letter.
590    const char *searchPath = path.cstr();
591    if (isalpha(searchPath[0]) && searchPath[1] == ':') {
592        searchPath += 2;
593    }
594    int searchPathLen = strlen(searchPath);
595
596    if (gIsDebug) fprintf(stderr, "Search path: '%s'\n", searchPath);
597
598    if (!init()) {
599        error = "Failed to bind to ntdll.dll";
600        goto bail_out;
601    }
602
603    if (!adjustPrivileges()) {
604        // We can still continue even if the privilege escalation failed.
605        // The apparent effect is that we'll fail to query the name of
606        // some processes, yet it will work for some of them.
607        if (gIsDebug) fprintf(stderr, "Warning: adusting privileges failed. Continuing anyway.\n");
608    } else {
609        if (gIsDebug) fprintf(stderr, "Privileges adjusted.\n"); // DEBUG remove lter
610    }
611
612    processesPtr = queryAllProcess(&error);
613    if (processesPtr == NULL) goto bail_out;
614
615    handlesPtr = queryAllHandles(&error);
616    if (handlesPtr == NULL) goto bail_out;
617
618    numHandleFound = handlesPtr->dCount;
619
620    // Check all the handles
621    for (int n = handlesPtr->dCount, i = 0; i < n; i++) {
622        SYSTEM_HANDLE sysh = handlesPtr->ash[i];
623
624        if (ob_type_file != 0 && sysh.bObjectType != ob_type_file) {
625            continue;
626        }
627
628        HANDLE handle = (HANDLE) sysh.wValue;
629        DWORD remoteId = sysh.dIdProcess;
630        HANDLE remoteH = NULL;
631
632        if (remoteId == matchProcessId) {
633            // We already matched that process, we can skip its other entries.
634            continue;
635        }
636
637        if (remoteId == currProcessId) {
638            // We don't match ourselves
639            continue;
640        }
641
642        // Open a remote process.
643        // Most entries of a given process seem to be consecutive, so we
644        // only open the remote process handle if it's a different id.
645        if (remoteProcessH == NULL && remoteId == remoteProcessId) {
646            // We already tried to open this process and it failed.
647            // It's not going to be any better the next time so skip it.
648            continue;
649        }
650        if (remoteProcessH == NULL || remoteId != remoteProcessId) {
651            if (remoteProcessH != NULL) {
652                CloseHandle(remoteProcessH);
653            }
654
655            remoteProcessId = remoteId;
656            remoteProcessH = OpenProcess(PROCESS_DUP_HANDLE,
657                                         FALSE /*inheritHandle*/,
658                                         remoteProcessId);
659            if (remoteProcessH == NULL) {
660                continue;
661            }
662        }
663
664        if (remoteProcessH != NULL) {
665            // Duplicate the remote handle
666            if (DuplicateHandle(remoteProcessH,     // hSourceProcessHandle
667                                handle,             // hSourceHandle
668                                currProcessH,       // hTargetProcessHandle
669                                &remoteH,           // lpTargetHandle
670                                0,                  // dwDesiredAccess (ignored by same access)
671                                FALSE,              // bInheritHandle
672                                DUPLICATE_SAME_ACCESS) == 0) {
673                continue;
674            }
675        }
676
677        numHandleChecked++;
678
679        char type = isFileHandleType(remoteH);
680
681        if (type != 0) {
682            if (type == 'D') numHandleDirs++;
683            else if (type == 'F') numHandleFiles++;
684
685            // TODO simplify by not keeping directory handles
686            if (ob_type_file == 0 && type == 'F') {
687                // We found the first file handle. Remember it's system_handle object type
688                // and then use it to filter the following system_handle.
689                // For some reason OB_TYPE_FILE should be 0x1A but empirically I find it
690                // to be 0x1C, so we just make this test more dynamic.
691                ob_type_file = sysh.bObjectType;
692            }
693
694            // Try to get a filename out of that file or directory handle.
695            CString name("<unknown>");
696            bool ok = getFileName(remoteH, &name);
697
698            if (gIsDebug) {
699                fprintf(stderr, "P:%08x | t:%02x | f:%02x | v:%08x | %c | %s %s\n",
700                    sysh.dIdProcess, sysh.bObjectType, sysh.bFlags, sysh.wValue,
701                    type,
702                    ok ? "OK" : "FAIL",
703                    name.cstr()
704                    );
705            }
706
707            if (ok) {
708                // We got a file path. Let's check if it matches our target path.
709                if (_strnicmp(searchPath, name.cstr(), searchPathLen) == 0) {
710                    // Remember this process id so that we can ignore all its following entries.
711                    matchProcessId = remoteId;
712
713                    // Find out its process name
714                    CString procName("<unknown>");
715                    ok = getProcessName(processesPtr, remoteProcessId, &procName);
716                    if (ok) {
717                        numProcessMatch++;
718
719                        if (!outModule->isEmpty()) {
720                            outModule->add(";");
721                        }
722                        outModule->add(procName.cstr());
723                        result = true;
724                    }
725
726                    if (gIsDebug) {
727                        fprintf(stderr, "==> MATCH FOUND: %s  %s\n",
728                            ok ? "OK" : "FAIL",
729                            procName.cstr()
730                            );
731                    }
732                }
733            }
734
735        }
736
737        if (remoteH != NULL) {
738            CloseHandle(remoteH);
739            remoteH = NULL;
740        }
741    }
742
743bail_out:
744
745    if (gIsDebug) {
746        fprintf(stderr, "Processes matched: %d\n", numProcessMatch);
747        fprintf(stderr, "Handles: %d found, %d checked, %d dirs, %d files\n",
748               numHandleFound,
749               numHandleChecked,
750               numHandleDirs,
751               numHandleFiles);
752    }
753
754    if (error != NULL) {
755        CString msg;
756        msg.setLastWin32Error(NULL);
757        if (gIsDebug) fprintf(stderr, "[ERROR] %s: %s", error, msg.cstr());
758    }
759
760    if (remoteProcessH != NULL) {
761        CloseHandle(remoteProcessH);
762    }
763
764    if (currProcessH != NULL) {
765        CloseHandle(currProcessH);
766    }
767
768    if (handlesPtr != NULL) {
769        VirtualFree(handlesPtr, 0, MEM_RELEASE);
770        handlesPtr = NULL;
771    }
772
773    terminate();
774
775    return result;
776}
777
778#endif /* _WIN32 */
779