1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS pending moves operations Information tool
4  * FILE:            cmdutils/pendmoves/pendmoves.c
5  * PURPOSE:         Query information from registry about pending moves
6  * PROGRAMMERS:     Pierre Schweitzer <pierre@reactos.org>
7  */
8 
9 #include <windows.h>
10 #include <tchar.h>
11 #include <stdio.h>
12 
13 static
14 TCHAR *
15 BeautifyPath(TCHAR * Path, DWORD * Len)
16 {
17     DWORD LocalLen = *Len;
18 
19     /* If there's a ! marking that existing file can be overwritten,
20      * drop it
21      */
22     if (LocalLen > 1)
23     {
24         if (Path[0] == _T('!'))
25         {
26             ++Path;
27             --LocalLen;
28         }
29     }
30 
31     /* Remove namespace if prefixed */
32     if (LocalLen > 4)
33     {
34         if (Path[0] == _T('\\') && Path[1] == _T('?') &&
35             Path[2] == _T('?') && Path[3] == _T('\\'))
36         {
37             Path += 4;
38             LocalLen -= 4;
39         }
40     }
41 
42     /* Return modified string + len */
43     *Len = LocalLen;
44     return Path;
45 }
46 
47 static
48 DWORD
49 DisplayPendingOps(TCHAR * Value, DWORD Len)
50 {
51     DWORD Chars, i, j, Count, SrcLen, TgtLen;
52     TCHAR * SrcFile, * Target, * Current;
53 
54     /* Compute the amount of chars
55      * NULL char isn't relaible EOF (MULTI_SZ)
56      */
57     Chars = Len / sizeof(TCHAR);
58 
59     i = 0;
60     Count = 0;
61     Current = Value;
62     /* Browse the whole string */
63     while (i < Chars)
64     {
65         /* Jump to the next NULL (end of source) */
66         for (j = i; j < Chars && Value[j] != 0; ++j);
67         /* Get len & clean path */
68         SrcLen = _tcslen(Current);
69         SrcFile = BeautifyPath(Current, &SrcLen);
70         /* Source file is null - likely the end of the MULTI_SZ, quit */
71         if (SrcLen == 0)
72         {
73             break;
74         }
75 
76         /* Remember position, jump to the begin of the target */
77         i = j;
78         ++i;
79         /* Update position in MULTI_SZ */
80         Current = Value + i;
81 
82         /* Jump to the next NULL (end of target) */
83         for (j = i; j < Chars && Value[j] != 0; ++j);
84         /* Get len & clean path */
85         TgtLen = _tcslen(Current);
86         Target = BeautifyPath(Current, &TgtLen);
87         /* Remember position, jump to the begin of the next source */
88         i = j;
89         ++i;
90         Current = Value + i;
91 
92         /* Display source */
93         _ftprintf(stdout, _T("Source: %s\n"), SrcFile);
94         /* If is accessible? Warn if not */
95         if (GetFileAttributes(SrcFile) == INVALID_FILE_ATTRIBUTES)
96         {
97             _ftprintf(stdout, _T("\t *** Source file lookup error: %d\n"), GetLastError());
98         }
99         /* And display target - if empty, it's for deletion, mark as it */
100         _ftprintf(stdout, _T("Target: %s\n\n"), (_tcslen(Target) != 0 ? Target: _T("DELETE")));
101 
102         /* Remember position and number of entries */
103         Current = Value + i;
104         ++Count;
105     }
106 
107     return Count;
108 }
109 
110 int
111 __cdecl
112 _tmain(int argc, const TCHAR *argv[])
113 {
114     HKEY hKey;
115     LONG Ret;
116     DWORD MaxLen, Len, Count, Type;
117     PVOID Buffer;
118     FILETIME LastModified;
119     TCHAR RegistryPath[] = _T("System\\CurrentControlSet\\Control\\Session Manager");
120 
121     /* Open the SMSS registry key */
122     Ret = RegOpenKey(HKEY_LOCAL_MACHINE, RegistryPath, &hKey);
123     if (Ret != ERROR_SUCCESS)
124     {
125         _ftprintf(stderr, _T("Failed opening the registry key '%s' (%lx)\n"), RegistryPath, Ret);
126         return 1;
127     }
128 
129     /* Get last modified date + buffer length we need to allocate */
130     Ret = RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &MaxLen, NULL, &LastModified);
131     if (Ret != ERROR_SUCCESS)
132     {
133         RegCloseKey(hKey);
134         _ftprintf(stderr, _T("Failed querying information for '%s' (%lx)\n"), RegistryPath, Ret);
135         return 1;
136     }
137 
138     /* No value, so no operations */
139     if (MaxLen == 0)
140     {
141         RegCloseKey(hKey);
142         _ftprintf(stdout, _T("No pending file rename operations registered.\n\n"));
143         return 0;
144     }
145 
146     /* Allocate memory */
147     Buffer = HeapAlloc(GetProcessHeap(), 0, MaxLen);
148     if (Buffer == NULL)
149     {
150         RegCloseKey(hKey);
151         _ftprintf(stderr, _T("Failed allocating %d bytes\n"), MaxLen);
152         return 1;
153     }
154 
155     /* Start with PendingFileRenameOperations */
156     Count = 0;
157     Len = MaxLen;
158     Ret = RegQueryValueEx(hKey, _T("PendingFileRenameOperations"), NULL, &Type, Buffer, &Len);
159     if (Ret == ERROR_SUCCESS && Type == REG_MULTI_SZ)
160     {
161         Count += DisplayPendingOps(Buffer, Len);
162     }
163 
164     /* Continue with PendingFileRenameOperations2 - used if PendingFileRenameOperations is too big */
165     Len = MaxLen;
166     Ret = RegQueryValueEx(hKey, _T("PendingFileRenameOperations2"), NULL, &Type, Buffer, &Len);
167     if (Ret == ERROR_SUCCESS && Type == REG_MULTI_SZ)
168     {
169         Count += DisplayPendingOps(Buffer, Len);
170     }
171 
172     /* Release everything */
173     HeapFree(GetProcessHeap(), 0, Buffer);
174     RegCloseKey(hKey);
175 
176     /* If we found entries, display modification date */
177     if (Count != 0)
178     {
179         FILETIME LocalTime;
180         SYSTEMTIME SysTime;
181 
182         /* Convert our UTC time to local time, and then to system time to allow easy display */
183         if (FileTimeToLocalFileTime(&LastModified, &LocalTime) && FileTimeToSystemTime(&LocalTime, &SysTime))
184         {
185             _ftprintf(stdout, _T("Time of last update to pending moves key: %02d/%02d/%04d %02d:%02d\n\n"),
186                       SysTime.wDay, SysTime.wMonth, SysTime.wYear, SysTime.wHour, SysTime.wMinute);
187         }
188     }
189     /* No operations found */
190     else
191     {
192         _ftprintf(stdout, _T("No pending file rename operations registered.\n\n"));
193     }
194 
195     return 0;
196 }
197