1 /*
2  * DESCRIPTION: Object Manager Simple Explorer
3  * PROGRAMMER:  David Welch
4  * REVISIONS
5  * 	2000-04-30 (ea)
6  * 		Added directory enumeration.
7  * 		(tested under nt4sp4/x86)
8  * 	2000-08-11 (ea)
9  * 		Added symbolic link expansion.
10  * 		(tested under nt4sp4/x86)
11  * 	2001-05-01 (ea)
12  * 		Fixed entries counter. Added more
13  * 		error codes check. Removed wprintf,
14  * 		because it does not work in .17.
15  * 	2001-05-02 (ea)
16  * 		Added -r option.
17  */
18 
19 #define WIN32_NO_STATUS
20 #include <windows.h>
21 #include <stdlib.h>
22 #include <ntndk.h>
23 #include <stdio.h>
24 
25 #define MAX_DIR_ENTRY 256
26 
27 
28 static
29 PCHAR
30 WINAPI
31 RawUszAsz (
32 	PWCHAR	szU,
33 	PCHAR	szA
34 	)
35 {
36 	register PCHAR a = szA;
37 
38 	while (*szU) {*szA++ = (CHAR) (0x00ff & * szU++);}
39 	*szA = '\0';
40 	return a;
41 }
42 
43 
44 static
45 PWCHAR
46 WINAPI
47 RawAszUsz (
48 	PCHAR	szA,
49 	PWCHAR	szW
50 	)
51 {
52 	register PWCHAR w = szW;
53 
54 	while (*szA) {*szW++ = (WCHAR) *szA++;}
55 	*szW = L'\0';
56 	return w;
57 }
58 
59 
60 static
61 const char *
62 WINAPI
63 StatusToName (NTSTATUS Status)
64 {
65 	static char RawValue [16];
66 
67 	switch (Status)
68 	{
69 		case STATUS_BUFFER_TOO_SMALL:
70 			return "STATUS_BUFFER_TOO_SMALL";
71 		case STATUS_INVALID_PARAMETER:
72 			return "STATUS_INVALID_PARAMETER";
73 		case STATUS_OBJECT_NAME_INVALID:
74 			return "STATUS_OBJECT_NAME_INVALID";
75 		case STATUS_OBJECT_NAME_NOT_FOUND:
76 			return "STATUS_OBJECT_NAME_NOT_FOUND";
77 		case STATUS_OBJECT_PATH_SYNTAX_BAD:
78 			return "STATUS_PATH_SYNTAX_BAD";
79 		case STATUS_NO_MORE_ENTRIES:
80 			return "STATUS_NO_MORE_ENTRIES";
81 		case STATUS_MORE_ENTRIES:
82 			return "STATUS_MORE_ENTRIES";
83 		case STATUS_ACCESS_DENIED:
84 			return "STATUS_ACCESS_DENIED";
85 		case STATUS_UNSUCCESSFUL:
86 			return "STATUS_UNSUCCESSFUL";
87 		case STATUS_INVALID_HANDLE:
88 			return "STATUS_INVALID_HANDLE";
89 	}
90 	sprintf (RawValue, "0x%08lx", Status);
91 	return (const char *) RawValue;
92 }
93 
94 
95 BOOL
96 WINAPI
97 ExpandSymbolicLink (
98 	IN	PUNICODE_STRING	DirectoryName,
99 	IN	PUNICODE_STRING	SymbolicLinkName,
100 	IN OUT	PUNICODE_STRING	TargetObjectName
101 	)
102 {
103 	NTSTATUS		Status;
104 	HANDLE			hSymbolicLink;
105 	OBJECT_ATTRIBUTES	oa;
106 	UNICODE_STRING		Path;
107 	WCHAR			PathBuffer [MAX_PATH];
108 	ULONG			DataWritten = 0;
109 
110 
111 	Path.Buffer = PathBuffer;
112 	Path.Length = 0;
113 	Path.MaximumLength = sizeof PathBuffer;
114 
115 	RtlCopyUnicodeString (& Path, DirectoryName);
116 	if (L'\\' != Path.Buffer [(Path.Length / sizeof Path.Buffer[0]) - 1])
117 	{
118 		RtlAppendUnicodeToString (& Path, L"\\");
119 	}
120 	RtlAppendUnicodeStringToString (& Path, SymbolicLinkName);
121 
122 	oa.Length			= sizeof (OBJECT_ATTRIBUTES);
123 	oa.ObjectName			= & Path;
124 	oa.Attributes			= 0; /* OBJ_CASE_INSENSITIVE; */
125 	oa.RootDirectory		= NULL;
126 	oa.SecurityDescriptor		= NULL;
127 	oa.SecurityQualityOfService	= NULL;
128 
129 	Status = NtOpenSymbolicLinkObject(
130 			& hSymbolicLink,
131 			SYMBOLIC_LINK_QUERY,	/* 0x20001 */
132 			& oa
133 			);
134 
135 	if (!NT_SUCCESS(Status))
136 	{
137 		printf (
138 			"Failed to open SymbolicLink object (Status: %s)\n",
139 			StatusToName (Status)
140 			);
141 		return FALSE;
142 	}
143 	TargetObjectName->Length = TargetObjectName->MaximumLength;
144 	memset (
145 		TargetObjectName->Buffer,
146 		0,
147 		TargetObjectName->MaximumLength
148 		);
149 	Status = NtQuerySymbolicLinkObject(
150 			hSymbolicLink,
151 			TargetObjectName,
152 			& DataWritten
153 			);
154 	if (!NT_SUCCESS(Status))
155 	{
156 		printf (
157 			"Failed to query SymbolicLink object (Status: %s)\n",
158 			StatusToName (Status)
159 			);
160 		NtClose (hSymbolicLink);
161 		return FALSE;
162 	}
163 	NtClose (hSymbolicLink);
164 	return TRUE;
165 }
166 
167 
168 BOOL
169 WINAPI
170 ListDirectory (
171 	IN	PUNICODE_STRING	DirectoryNameW,
172 	IN	BOOL		Recurse
173 	)
174 {
175 	CHAR			DirectoryNameA [MAX_PATH];
176 	OBJECT_ATTRIBUTES	ObjectAttributes;
177 	NTSTATUS		Status;
178 	HANDLE			DirectoryHandle;
179 	BYTE			DirectoryEntry [512];
180 	POBJECT_DIRECTORY_INFORMATION pDirectoryEntry = (POBJECT_DIRECTORY_INFORMATION) DirectoryEntry;
181 	POBJECT_DIRECTORY_INFORMATION pDirectoryEntries = (POBJECT_DIRECTORY_INFORMATION) DirectoryEntry;
182 	ULONG			Context = 0;
183 	ULONG			ReturnLength = 0;
184 	ULONG			EntryCount = 0;
185 
186 	/* For expanding symbolic links */
187 	WCHAR			TargetName [2 * MAX_PATH];
188 	UNICODE_STRING		TargetObjectName = {
189 					sizeof TargetName,
190 					sizeof TargetName,
191 					TargetName
192 				};
193 
194 	/* Convert to ANSI the directory's name */
195 	RawUszAsz (DirectoryNameW->Buffer, DirectoryNameA);
196 	/*
197 	 * Prepare parameters for next call.
198 	 */
199 	InitializeObjectAttributes (
200 		& ObjectAttributes,
201 		DirectoryNameW,
202 		0,
203 		NULL,
204 		NULL
205 		);
206 	/*
207 	 * Try opening the directory.
208 	 */
209 	Status = NtOpenDirectoryObject (
210 			& DirectoryHandle,
211 			DIRECTORY_QUERY,
212 			& ObjectAttributes
213 			);
214 	if (!NT_SUCCESS(Status))
215 	{
216 		printf (
217 			"Failed to open directory object \"%s\" (Status: %s)\n",
218 			DirectoryNameA,
219 			StatusToName (Status)
220 			);
221 		return (FALSE);
222 	}
223 	printf ("\n Directory of %s\n\n", DirectoryNameA);
224 
225         for(;;)
226         {
227 	/*
228 	 * Enumerate each item in the directory.
229 	 */
230 	Status = NtQueryDirectoryObject (
231 			DirectoryHandle,
232 			pDirectoryEntries,
233 			sizeof DirectoryEntry,
234 			FALSE,/* ReturnSingleEntry */
235 			FALSE, /* RestartScan */
236 			& Context,
237 			& ReturnLength
238 			);
239 	if (!NT_SUCCESS(Status) && Status != STATUS_NO_MORE_ENTRIES)
240 	{
241 		printf (
242 			"Failed to query directory object (Status: %s)\n",
243 			StatusToName (Status)
244 			);
245 		NtClose (DirectoryHandle);
246 		return (FALSE);
247 	}
248 	if (Status == STATUS_NO_MORE_ENTRIES)
249 	{
250           break;
251         }
252 	pDirectoryEntry = pDirectoryEntries;
253 	while (EntryCount < Context)
254 	{
255 		CHAR ObjectNameA [MAX_PATH];
256 		CHAR TypeNameA [MAX_PATH];
257 		CHAR TargetNameA [MAX_PATH];
258 
259 		if (0 == wcscmp (L"SymbolicLink", pDirectoryEntry->TypeName.Buffer))
260 		{
261 			if (TRUE == ExpandSymbolicLink (
262 					DirectoryNameW,
263 					& pDirectoryEntry->Name,
264 					& TargetObjectName
265 					)
266 				)
267 			{
268 
269 				printf (
270 					"%-16s %s -> %s\n",
271 					RawUszAsz (pDirectoryEntry->TypeName.Buffer, TypeNameA),
272 					RawUszAsz (pDirectoryEntry->Name.Buffer, ObjectNameA),
273 					RawUszAsz (TargetObjectName.Buffer, TargetNameA)
274 					);
275 			}
276 			else
277 			{
278 				printf (
279 					"%-16s %s -> (error!)\n",
280 					RawUszAsz (pDirectoryEntry->TypeName.Buffer, TypeNameA),
281 					RawUszAsz (pDirectoryEntry->Name.Buffer, ObjectNameA)
282 					);
283 			}
284 		}
285 		else
286 		{
287 			printf (
288 				"%-16s %s\n",
289 				RawUszAsz (pDirectoryEntry->TypeName.Buffer, TypeNameA),
290 				RawUszAsz (pDirectoryEntry->Name.Buffer, ObjectNameA)
291 				);
292 		}
293 		++ pDirectoryEntry;
294 		++ EntryCount;
295 	}
296 	};
297 	printf ("\n\t%lu object(s)\n", EntryCount);
298 	/*
299 	 * Free any resource.
300 	 */
301 	NtClose (DirectoryHandle);
302 	/*
303 	 * Recurse into, if required so.
304 	 */
305 	if (FALSE != Recurse)
306 	{
307 		pDirectoryEntry = (POBJECT_DIRECTORY_INFORMATION) DirectoryEntry;
308 		while (0 != pDirectoryEntry->TypeName.Length)
309 		{
310 			if (0 == wcscmp (L"Directory", pDirectoryEntry->TypeName.Buffer))
311 			{
312 				WCHAR		CurrentName [MAX_PATH];
313 				UNICODE_STRING	CurrentDirectory;
314 
315 				CurrentName [0] = L'\0';
316 				wcscpy (CurrentName, DirectoryNameW->Buffer);
317 				if (wcslen (CurrentName) > 1)
318 				{
319 					wcscat (CurrentName, L"\\");
320 				}
321 				wcscat (CurrentName, pDirectoryEntry->Name.Buffer);
322 				RtlInitUnicodeString (& CurrentDirectory, CurrentName);
323 				ListDirectory (& CurrentDirectory, Recurse);
324 			}
325 			++ pDirectoryEntry;
326 		}
327 	}
328 	return (TRUE);
329 }
330 
331 
332 int main(int argc, char* argv[])
333 {
334 	WCHAR		DirectoryNameW [MAX_PATH];
335 	UNICODE_STRING	DirectoryName;
336 	BOOL		Recurse = FALSE;
337 
338 	/*
339 	 * Check user arguments.
340 	 */
341 	switch (argc)
342 	{
343 	case 2:
344 		RawAszUsz (argv[1], DirectoryNameW);
345 		break;
346 	case 3:
347 		if (strcmp (argv[1], "-r"))
348 		{
349 			fprintf (
350 				stderr,
351 				"%s: unknown option '%s'.\n",
352 				argv [0], argv[1]
353 				);
354 			return EXIT_FAILURE;
355 		}
356 		RawAszUsz (argv[2], DirectoryNameW);
357 		Recurse = TRUE;
358 		break;
359 	default:
360 		fprintf (
361 			stderr,
362 			"\nUsage: %s [-r] directory\n\n"
363 			"  -r          recurse\n"
364 			"  directory   a directory name in the system namespace\n\n",
365 			argv [0]
366 			);
367 		return EXIT_FAILURE;
368 	}
369 	/*
370 	 * List the directory.
371 	 */
372 	RtlInitUnicodeString (& DirectoryName, DirectoryNameW);
373 	return (FALSE == ListDirectory (& DirectoryName, Recurse))
374 		? EXIT_FAILURE
375 		: EXIT_SUCCESS;
376 }
377 
378 
379 /* EOF */
380