Synchronize with trunk r58606.
[reactos.git] / subsystems / win / basesrv / dosdev.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS/Win32 Base enviroment Subsystem Server
4 * FILE: subsystems/win/basesrv/dosdev.c
5 * PURPOSE: DOS Devices Management
6 * PROGRAMMERS: Pierre Schweitzer (pierre.schweitzer@reactos.org)
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "basesrv.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS ********************************************************************/
17
18 typedef struct _BASE_DOS_DEVICE_HISTORY_ENTRY
19 {
20 LIST_ENTRY Entry;
21 UNICODE_STRING Device;
22 UNICODE_STRING Target;
23 } BASE_DOS_DEVICE_HISTORY_ENTRY, *PBASE_DOS_DEVICE_HISTORY_ENTRY;
24
25 static RTL_CRITICAL_SECTION BaseDefineDosDeviceCritSec;
26 static LIST_ENTRY DosDeviceHistory;
27
28 /* PRIVATE FUNCTIONS **********************************************************/
29
30 VOID BaseInitDefineDosDevice(VOID)
31 {
32 RtlInitializeCriticalSection(&BaseDefineDosDeviceCritSec);
33 InitializeListHead(&DosDeviceHistory);
34 }
35
36 VOID BaseCleanupDefineDosDevice(VOID)
37 {
38 PLIST_ENTRY Entry, ListHead;
39 PBASE_DOS_DEVICE_HISTORY_ENTRY HistoryEntry;
40
41 RtlDeleteCriticalSection(&BaseDefineDosDeviceCritSec);
42
43 ListHead = &DosDeviceHistory;
44 Entry = ListHead->Flink;
45 while (Entry != ListHead)
46 {
47 HistoryEntry = (PBASE_DOS_DEVICE_HISTORY_ENTRY)
48 CONTAINING_RECORD(Entry,
49 BASE_DOS_DEVICE_HISTORY_ENTRY,
50 Entry);
51 Entry = Entry->Flink;
52
53 if (HistoryEntry)
54 {
55 if (HistoryEntry->Target.Buffer)
56 {
57 RtlFreeHeap(BaseSrvHeap,
58 0,
59 HistoryEntry->Target.Buffer);
60 }
61 if (HistoryEntry->Device.Buffer)
62 {
63 RtlFreeHeap(BaseSrvHeap,
64 0,
65 HistoryEntry->Device.Buffer);
66 }
67 RtlFreeHeap(BaseSrvHeap,
68 0,
69 HistoryEntry);
70 }
71 }
72 }
73
74 /* PUBLIC SERVER APIS *********************************************************/
75
76 CSR_API(BaseSrvDefineDosDevice)
77 {
78 NTSTATUS Status;
79 PBASE_DEFINE_DOS_DEVICE DefineDosDeviceRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.DefineDosDeviceRequest;
80 OBJECT_ATTRIBUTES ObjectAttributes;
81 HANDLE LinkHandle = NULL;
82 UNICODE_STRING DeviceName = {0};
83 UNICODE_STRING RequestDeviceName = {0};
84 UNICODE_STRING LinkTarget = {0};
85 PUNICODE_STRING RequestLinkTarget;
86 ULONG Length;
87 SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY};
88 SID_IDENTIFIER_AUTHORITY SystemAuthority = {SECURITY_NT_AUTHORITY};
89 PSECURITY_DESCRIPTOR SecurityDescriptor;
90 PACL Dacl;
91 PSID AdminSid;
92 PSID SystemSid;
93 PSID WorldSid;
94 ULONG SidLength;
95 PBASE_DOS_DEVICE_HISTORY_ENTRY HistoryEntry;
96 PLIST_ENTRY Entry;
97 PLIST_ENTRY ListHead;
98 BOOLEAN Matched, AddHistory;
99 DWORD dwFlags;
100 PWSTR lpBuffer;
101
102 DPRINT("BaseSrvDefineDosDevice entered, Flags:%d, DeviceName:%wZ, TargetName:%wZ\n",
103 DefineDosDeviceRequest->dwFlags,
104 &DefineDosDeviceRequest->DeviceName,
105 &DefineDosDeviceRequest->TargetName);
106
107 Matched = AddHistory = FALSE;
108 HistoryEntry = NULL;
109 AdminSid = SystemSid = WorldSid = NULL;
110 SecurityDescriptor = NULL;
111 ListHead = &DosDeviceHistory;
112 dwFlags = DefineDosDeviceRequest->dwFlags;
113
114 /* Validate the flags */
115 if ( (dwFlags & 0xFFFFFFF0) ||
116 ((dwFlags & DDD_EXACT_MATCH_ON_REMOVE) &&
117 !(dwFlags & DDD_REMOVE_DEFINITION)) )
118 {
119 return STATUS_INVALID_PARAMETER;
120 }
121
122 Status = RtlEnterCriticalSection(&BaseDefineDosDeviceCritSec);
123 if (!NT_SUCCESS(Status))
124 {
125 DPRINT1("RtlEnterCriticalSection() failed (Status %lx)\n",
126 Status);
127 return Status;
128 }
129
130 _SEH2_TRY
131 {
132 Status =
133 RtlUpcaseUnicodeString(&RequestDeviceName,
134 &DefineDosDeviceRequest->DeviceName,
135 TRUE);
136 if (!NT_SUCCESS(Status))
137 _SEH2_LEAVE;
138
139 RequestLinkTarget = &DefineDosDeviceRequest->TargetName;
140 lpBuffer = (PWSTR) RtlAllocateHeap(BaseSrvHeap,
141 HEAP_ZERO_MEMORY,
142 RequestDeviceName.MaximumLength + 5 * sizeof(WCHAR));
143 if (!lpBuffer)
144 {
145 DPRINT1("Failed to allocate memory\n");
146 Status = STATUS_NO_MEMORY;
147 _SEH2_LEAVE;
148 }
149
150 swprintf(lpBuffer,
151 L"\\??\\%wZ",
152 &RequestDeviceName);
153 RtlInitUnicodeString(&DeviceName,
154 lpBuffer);
155 InitializeObjectAttributes(&ObjectAttributes,
156 &DeviceName,
157 OBJ_CASE_INSENSITIVE,
158 NULL,
159 NULL);
160 Status = NtOpenSymbolicLinkObject(&LinkHandle,
161 DELETE | 0x1,
162 &ObjectAttributes);
163 if (NT_SUCCESS(Status))
164 {
165 Status = NtQuerySymbolicLinkObject(LinkHandle,
166 &LinkTarget,
167 &Length);
168 if (!NT_SUCCESS(Status) &&
169 Status == STATUS_BUFFER_TOO_SMALL)
170 {
171 LinkTarget.Length = 0;
172 LinkTarget.MaximumLength = Length;
173 LinkTarget.Buffer = (PWSTR)
174 RtlAllocateHeap(BaseSrvHeap,
175 HEAP_ZERO_MEMORY,
176 Length);
177 if (!LinkTarget.Buffer)
178 {
179 DPRINT1("Failed to allocate memory\n");
180 Status = STATUS_NO_MEMORY;
181 _SEH2_LEAVE;
182 }
183
184 Status = NtQuerySymbolicLinkObject(LinkHandle,
185 &LinkTarget,
186 &Length);
187 }
188
189 if (!NT_SUCCESS(Status))
190 {
191 DPRINT1("NtQuerySymbolicLinkObject(%wZ) failed (Status %lx)\n",
192 &DeviceName, Status);
193 _SEH2_LEAVE;
194 }
195
196 if ((dwFlags & DDD_REMOVE_DEFINITION))
197 {
198 /* If no target name specified we remove the current symlink target */
199 if (RequestLinkTarget->Length == 0)
200 Matched = TRUE;
201 else
202 {
203 if (dwFlags & DDD_EXACT_MATCH_ON_REMOVE)
204 Matched = !RtlCompareUnicodeString(RequestLinkTarget,
205 &LinkTarget,
206 TRUE);
207 else
208 Matched = RtlPrefixUnicodeString(RequestLinkTarget,
209 &LinkTarget,
210 TRUE);
211 }
212
213 if (Matched && IsListEmpty(ListHead))
214 {
215 /* Current symlink target macthed and there is nothing to revert to */
216 RequestLinkTarget = NULL;
217 }
218 else if (Matched && !IsListEmpty(ListHead))
219 {
220 /*
221 * Fetch the first history entry we come across for the device name.
222 * This will become the current symlink target for the device name.
223 */
224 Matched = FALSE;
225 Entry = ListHead->Flink;
226 while (Entry != ListHead)
227 {
228 HistoryEntry = (PBASE_DOS_DEVICE_HISTORY_ENTRY)
229 CONTAINING_RECORD(Entry,
230 BASE_DOS_DEVICE_HISTORY_ENTRY,
231 Entry);
232 Matched =
233 !RtlCompareUnicodeString(&RequestDeviceName,
234 &HistoryEntry->Device,
235 FALSE);
236 if (Matched)
237 {
238 RemoveEntryList(&HistoryEntry->Entry);
239 RequestLinkTarget = &HistoryEntry->Target;
240 break;
241 }
242 Entry = Entry->Flink;
243 HistoryEntry = NULL;
244 }
245
246 /* Nothing to revert to so delete the symlink */
247 if (!Matched)
248 RequestLinkTarget = NULL;
249 }
250 else if (!Matched)
251 {
252 /*
253 * Locate a previous symlink target as we did not get
254 * a hit earlier. If we find one we need to remove it.
255 */
256 Entry = ListHead->Flink;
257 while (Entry != ListHead)
258 {
259 HistoryEntry = (PBASE_DOS_DEVICE_HISTORY_ENTRY)
260 CONTAINING_RECORD(Entry,
261 BASE_DOS_DEVICE_HISTORY_ENTRY,
262 Entry);
263 Matched =
264 !RtlCompareUnicodeString(&RequestDeviceName,
265 &HistoryEntry->Device,
266 FALSE);
267 if (!Matched)
268 {
269 HistoryEntry = NULL;
270 Entry = Entry->Flink;
271 continue;
272 }
273
274 Matched = FALSE;
275 if (dwFlags & DDD_EXACT_MATCH_ON_REMOVE)
276 {
277 if (!RtlCompareUnicodeString(RequestLinkTarget,
278 &HistoryEntry->Target,
279 TRUE))
280 {
281 Matched = TRUE;
282 }
283 }
284 else if (RtlPrefixUnicodeString(RequestLinkTarget,
285 &HistoryEntry->Target,
286 TRUE))
287 {
288 Matched = TRUE;
289 }
290
291 if (Matched)
292 {
293 RemoveEntryList(&HistoryEntry->Entry);
294 break;
295 }
296 Entry = Entry->Flink;
297 HistoryEntry = NULL;
298 }
299
300 /* Leave existing symlink as is */
301 if (!Matched)
302 Status = STATUS_OBJECT_NAME_NOT_FOUND;
303 else
304 Status = STATUS_SUCCESS;
305 _SEH2_LEAVE;
306 }
307 }
308 else
309 {
310 AddHistory = TRUE;
311 }
312
313 Status = NtMakeTemporaryObject(LinkHandle);
314 if (!NT_SUCCESS(Status))
315 {
316 DPRINT1("NtMakeTemporaryObject(%wZ) failed (Status %lx)\n",
317 &DeviceName, Status);
318 _SEH2_LEAVE;
319 }
320
321 Status = NtClose(LinkHandle);
322 LinkHandle = NULL;
323 if (!NT_SUCCESS(Status))
324 {
325 DPRINT1("NtClose(%wZ) failed (Status %lx)\n",
326 &DeviceName, Status);
327 _SEH2_LEAVE;
328 }
329 }
330
331 /* Don't create symlink if we don't have a target */
332 if (!RequestLinkTarget || RequestLinkTarget->Length == 0)
333 _SEH2_LEAVE;
334
335 if (AddHistory)
336 {
337 HistoryEntry = (PBASE_DOS_DEVICE_HISTORY_ENTRY)
338 RtlAllocateHeap(BaseSrvHeap,
339 HEAP_ZERO_MEMORY,
340 sizeof(BASE_DOS_DEVICE_HISTORY_ENTRY));
341 if (!HistoryEntry)
342 {
343 DPRINT1("Failed to allocate memory\n");
344 Status = STATUS_NO_MEMORY;
345 _SEH2_LEAVE;
346 }
347
348 HistoryEntry->Target.Buffer =
349 RtlAllocateHeap(BaseSrvHeap,
350 HEAP_ZERO_MEMORY,
351 LinkTarget.Length);
352 if (!HistoryEntry->Target.Buffer)
353 {
354 DPRINT1("Failed to allocate memory\n");
355 Status = STATUS_NO_MEMORY;
356 _SEH2_LEAVE;
357 }
358 HistoryEntry->Target.Length =
359 HistoryEntry->Target.MaximumLength =
360 LinkTarget.Length;
361 RtlCopyUnicodeString(&HistoryEntry->Target,
362 &LinkTarget);
363
364 HistoryEntry->Device.Buffer =
365 RtlAllocateHeap(BaseSrvHeap,
366 HEAP_ZERO_MEMORY,
367 RequestDeviceName.Length);
368 if (!HistoryEntry->Device.Buffer)
369 {
370 DPRINT1("Failed to allocate memory\n");
371 Status = STATUS_NO_MEMORY;
372 _SEH2_LEAVE;
373 }
374 HistoryEntry->Device.Length =
375 HistoryEntry->Device.MaximumLength =
376 RequestDeviceName.Length;
377 RtlCopyUnicodeString(&HistoryEntry->Device,
378 &RequestDeviceName);
379
380 /* Remember previous symlink target for this device */
381 InsertHeadList(ListHead,
382 &HistoryEntry->Entry);
383 HistoryEntry = NULL;
384 }
385
386 RtlAllocateAndInitializeSid(&WorldAuthority,
387 1,
388 SECURITY_WORLD_RID,
389 SECURITY_NULL_RID,
390 SECURITY_NULL_RID,
391 SECURITY_NULL_RID,
392 SECURITY_NULL_RID,
393 SECURITY_NULL_RID,
394 SECURITY_NULL_RID,
395 SECURITY_NULL_RID,
396 &WorldSid);
397
398 RtlAllocateAndInitializeSid(&SystemAuthority,
399 1,
400 SECURITY_LOCAL_SYSTEM_RID,
401 SECURITY_NULL_RID,
402 SECURITY_NULL_RID,
403 SECURITY_NULL_RID,
404 SECURITY_NULL_RID,
405 SECURITY_NULL_RID,
406 SECURITY_NULL_RID,
407 SECURITY_NULL_RID,
408 &SystemSid);
409
410 RtlAllocateAndInitializeSid(&SystemAuthority,
411 2,
412 SECURITY_BUILTIN_DOMAIN_RID,
413 DOMAIN_ALIAS_RID_ADMINS,
414 SECURITY_NULL_RID,
415 SECURITY_NULL_RID,
416 SECURITY_NULL_RID,
417 SECURITY_NULL_RID,
418 SECURITY_NULL_RID,
419 SECURITY_NULL_RID,
420 &AdminSid);
421
422 SidLength = RtlLengthSid(SystemSid) +
423 RtlLengthSid(AdminSid) +
424 RtlLengthSid(WorldSid);
425 Length = sizeof(ACL) + SidLength + 3 * sizeof(ACCESS_ALLOWED_ACE);
426
427 SecurityDescriptor = RtlAllocateHeap(BaseSrvHeap,
428 0,
429 SECURITY_DESCRIPTOR_MIN_LENGTH + Length);
430 if (!SecurityDescriptor)
431 {
432 DPRINT1("Failed to allocate memory\n");
433 Status = STATUS_NO_MEMORY;
434 _SEH2_LEAVE;
435 }
436
437 Dacl = (PACL)((ULONG_PTR)SecurityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH);
438 Status = RtlCreateSecurityDescriptor(SecurityDescriptor,
439 SECURITY_DESCRIPTOR_REVISION);
440 if (!NT_SUCCESS(Status))
441 {
442 DPRINT1("RtlCreateSecurityDescriptor() failed (Status %lx)\n", Status);
443 _SEH2_LEAVE;
444 }
445
446 Status = RtlCreateAcl(Dacl,
447 Length,
448 ACL_REVISION);
449 if (!NT_SUCCESS(Status))
450 {
451 DPRINT1("RtlCreateAcl() failed (Status %lx)\n", Status);
452 _SEH2_LEAVE;
453 }
454
455 RtlAddAccessAllowedAce(Dacl,
456 ACL_REVISION,
457 GENERIC_ALL,
458 SystemSid);
459 RtlAddAccessAllowedAce(Dacl,
460 ACL_REVISION,
461 GENERIC_ALL,
462 AdminSid);
463 RtlAddAccessAllowedAce(Dacl,
464 ACL_REVISION,
465 STANDARD_RIGHTS_READ,
466 WorldSid);
467
468 Status = RtlSetDaclSecurityDescriptor(SecurityDescriptor,
469 TRUE,
470 Dacl,
471 FALSE);
472 if (!NT_SUCCESS(Status))
473 {
474 DPRINT1("RtlSetDaclSecurityDescriptor() failed (Status %lx)\n", Status);
475 _SEH2_LEAVE;
476 }
477
478 InitializeObjectAttributes(&ObjectAttributes,
479 &DeviceName,
480 OBJ_CASE_INSENSITIVE,
481 NULL,
482 SecurityDescriptor);
483 Status = NtCreateSymbolicLinkObject(&LinkHandle,
484 SYMBOLIC_LINK_ALL_ACCESS,
485 &ObjectAttributes,
486 RequestLinkTarget);
487 if (NT_SUCCESS(Status))
488 {
489 Status = NtMakePermanentObject(LinkHandle);
490 if (!NT_SUCCESS(Status))
491 {
492 DPRINT1("NtMakePermanentObject(%wZ) failed (Status %lx)\n",
493 &DeviceName, Status);
494 }
495 }
496 else
497 {
498 DPRINT1("NtCreateSymbolicLinkObject(%wZ) failed (Status %lx)\n",
499 &DeviceName, Status);
500 }
501 }
502 _SEH2_FINALLY
503 {
504 RtlLeaveCriticalSection(&BaseDefineDosDeviceCritSec);
505 if (DeviceName.Buffer)
506 {
507 RtlFreeHeap(BaseSrvHeap,
508 0,
509 DeviceName.Buffer);
510 }
511 if (LinkTarget.Buffer)
512 {
513 RtlFreeHeap(BaseSrvHeap,
514 0,
515 LinkTarget.Buffer);
516 }
517 if (SecurityDescriptor)
518 {
519 RtlFreeHeap(BaseSrvHeap,
520 0,
521 SecurityDescriptor);
522 }
523
524 if (LinkHandle) NtClose(LinkHandle);
525 if (SystemSid) RtlFreeSid(SystemSid);
526 if (AdminSid) RtlFreeSid(AdminSid);
527 if (WorldSid) RtlFreeSid(WorldSid);
528
529 RtlFreeUnicodeString(&RequestDeviceName);
530
531 if (HistoryEntry)
532 {
533 if (HistoryEntry->Target.Buffer)
534 {
535 RtlFreeHeap(BaseSrvHeap,
536 0,
537 HistoryEntry->Target.Buffer);
538 }
539 if (HistoryEntry->Device.Buffer)
540 {
541 RtlFreeHeap(BaseSrvHeap,
542 0,
543 HistoryEntry->Device.Buffer);
544 }
545 RtlFreeHeap(BaseSrvHeap,
546 0,
547 HistoryEntry);
548 }
549 }
550 _SEH2_END
551
552 DPRINT("BaseSrvDefineDosDevice exit, Status: 0x%x\n", Status);
553 return Status;
554 }
555
556 /* EOF */