1 /*
2 * ReactOS Control ACLs Program
3 * Copyright (C) 2006 Thomas Weidenmueller
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include "precomp.h"
21
22 /* command line options */
23 BOOL OptionT = FALSE, OptionE = FALSE, OptionC = FALSE;
24 BOOL OptionG = FALSE, OptionR = FALSE, OptionP = FALSE, OptionD = FALSE;
25 LPCTSTR GUser, GPerm, RUser, PUser, PPerm, DUser;
26
27 static GENERIC_MAPPING FileGenericMapping =
28 {
29 FILE_GENERIC_READ,
30 FILE_GENERIC_WRITE,
31 FILE_GENERIC_EXECUTE,
32 FILE_ALL_ACCESS
33 };
34
35
36 static VOID
PrintError(DWORD dwError)37 PrintError(DWORD dwError)
38 {
39 if (dwError == ERROR_SUCCESS)
40 return;
41
42 ConMsgPuts(StdErr, FORMAT_MESSAGE_FROM_SYSTEM,
43 NULL, dwError, LANG_USER_DEFAULT);
44 }
45
46 static BOOL
PrintFileDacl(IN LPTSTR FilePath,IN LPTSTR FileName)47 PrintFileDacl(IN LPTSTR FilePath,
48 IN LPTSTR FileName)
49 {
50 SIZE_T Length;
51 PSECURITY_DESCRIPTOR SecurityDescriptor;
52 DWORD SDSize = 0;
53 TCHAR FullFileName[MAX_PATH + 1];
54 BOOL Error = FALSE, Ret = FALSE;
55
56 Length = _tcslen(FilePath) + _tcslen(FileName);
57 if (Length > MAX_PATH)
58 {
59 /* file name too long */
60 SetLastError(ERROR_FILE_NOT_FOUND);
61 return FALSE;
62 }
63
64 _tcscpy(FullFileName, FilePath);
65 _tcscat(FullFileName, FileName);
66
67 /* find out how much memory we need */
68 if (!GetFileSecurity(FullFileName,
69 DACL_SECURITY_INFORMATION,
70 NULL,
71 0,
72 &SDSize) &&
73 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
74 {
75 return FALSE;
76 }
77
78 SecurityDescriptor = (PSECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(),
79 0,
80 SDSize);
81 if (SecurityDescriptor != NULL)
82 {
83 if (GetFileSecurity(FullFileName,
84 DACL_SECURITY_INFORMATION,
85 SecurityDescriptor,
86 SDSize,
87 &SDSize))
88 {
89 PACL Dacl;
90 BOOL DaclPresent;
91 BOOL DaclDefaulted;
92
93 if (GetSecurityDescriptorDacl(SecurityDescriptor,
94 &DaclPresent,
95 &Dacl,
96 &DaclDefaulted))
97 {
98 if (DaclPresent)
99 {
100 PACCESS_ALLOWED_ACE Ace;
101 DWORD AceIndex = 0;
102
103 /* dump the ACL */
104 while (GetAce(Dacl,
105 AceIndex,
106 (PVOID*)&Ace))
107 {
108 SID_NAME_USE Use;
109 DWORD NameSize = 0;
110 DWORD DomainSize = 0;
111 LPTSTR Name = NULL;
112 LPTSTR Domain = NULL;
113 LPTSTR SidString = NULL;
114 DWORD IndentAccess;
115 DWORD AccessMask = Ace->Mask;
116 PSID Sid = (PSID)&Ace->SidStart;
117
118 /* attempt to translate the SID into a readable string */
119 if (!LookupAccountSid(NULL,
120 Sid,
121 Name,
122 &NameSize,
123 Domain,
124 &DomainSize,
125 &Use))
126 {
127 if (GetLastError() == ERROR_NONE_MAPPED || NameSize == 0)
128 {
129 goto BuildSidString;
130 }
131 else
132 {
133 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
134 {
135 Error = TRUE;
136 break;
137 }
138
139 Name = (LPTSTR)HeapAlloc(GetProcessHeap(),
140 0,
141 (NameSize + DomainSize) * sizeof(TCHAR));
142 if (Name == NULL)
143 {
144 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
145 Error = TRUE;
146 break;
147 }
148
149 Domain = Name + NameSize;
150 Name[0] = _T('\0');
151 if (DomainSize != 0)
152 Domain[0] = _T('\0');
153 if (!LookupAccountSid(NULL,
154 Sid,
155 Name,
156 &NameSize,
157 Domain,
158 &DomainSize,
159 &Use))
160 {
161 HeapFree(GetProcessHeap(),
162 0,
163 Name);
164 Name = NULL;
165 goto BuildSidString;
166 }
167 }
168 }
169 else
170 {
171 BuildSidString:
172 if (!ConvertSidToStringSid(Sid,
173 &SidString))
174 {
175 Error = TRUE;
176 break;
177 }
178 }
179
180 /* print the file name or space */
181 ConPrintf(StdOut, L"%s ", FullFileName);
182
183 /* attempt to map the SID to a user name */
184 if (AceIndex == 0)
185 {
186 DWORD i = 0;
187
188 /* overwrite the full file name with spaces so we
189 only print the file name once */
190 while (FullFileName[i] != _T('\0'))
191 FullFileName[i++] = _T(' ');
192 }
193
194 /* print the domain and/or user if possible, or the SID string */
195 if (Name != NULL && Domain[0] != _T('\0'))
196 {
197 ConPrintf(StdOut, L"%s\\%s:", Domain, Name);
198 IndentAccess = (DWORD)_tcslen(Domain) + _tcslen(Name);
199 }
200 else
201 {
202 LPTSTR DisplayString = (Name != NULL ? Name : SidString);
203
204 ConPrintf(StdOut, L"%s:", DisplayString);
205 IndentAccess = (DWORD)_tcslen(DisplayString);
206 }
207
208 /* print the ACE Flags */
209 if (Ace->Header.AceFlags & CONTAINER_INHERIT_ACE)
210 {
211 IndentAccess += ConResPuts(StdOut, IDS_ABBR_CI);
212 }
213 if (Ace->Header.AceFlags & OBJECT_INHERIT_ACE)
214 {
215 IndentAccess += ConResPuts(StdOut, IDS_ABBR_OI);
216 }
217 if (Ace->Header.AceFlags & INHERIT_ONLY_ACE)
218 {
219 IndentAccess += ConResPuts(StdOut, IDS_ABBR_IO);
220 }
221
222 IndentAccess += 2;
223
224 /* print the access rights */
225 MapGenericMask(&AccessMask,
226 &FileGenericMapping);
227 if (Ace->Header.AceType & ACCESS_DENIED_ACE_TYPE)
228 {
229 if (AccessMask == FILE_ALL_ACCESS)
230 {
231 ConResPuts(StdOut, IDS_ABBR_NONE);
232 }
233 else
234 {
235 ConResPuts(StdOut, IDS_DENY);
236 goto PrintSpecialAccess;
237 }
238 }
239 else
240 {
241 if (AccessMask == FILE_ALL_ACCESS)
242 {
243 ConResPuts(StdOut, IDS_ABBR_FULL);
244 }
245 else if (!(Ace->Mask & (GENERIC_READ | GENERIC_EXECUTE)) &&
246 AccessMask == (FILE_GENERIC_READ | FILE_EXECUTE))
247 {
248 ConResPuts(StdOut, IDS_ABBR_READ);
249 }
250 else if (AccessMask == (FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_EXECUTE | DELETE))
251 {
252 ConResPuts(StdOut, IDS_ABBR_CHANGE);
253 }
254 else if (AccessMask == FILE_GENERIC_WRITE)
255 {
256 ConResPuts(StdOut, IDS_ABBR_WRITE);
257 }
258 else
259 {
260 DWORD x, x2;
261 static const struct
262 {
263 DWORD Access;
264 UINT uID;
265 }
266 AccessRights[] =
267 {
268 {FILE_WRITE_ATTRIBUTES, IDS_FILE_WRITE_ATTRIBUTES},
269 {FILE_READ_ATTRIBUTES, IDS_FILE_READ_ATTRIBUTES},
270 {FILE_DELETE_CHILD, IDS_FILE_DELETE_CHILD},
271 {FILE_EXECUTE, IDS_FILE_EXECUTE},
272 {FILE_WRITE_EA, IDS_FILE_WRITE_EA},
273 {FILE_READ_EA, IDS_FILE_READ_EA},
274 {FILE_APPEND_DATA, IDS_FILE_APPEND_DATA},
275 {FILE_WRITE_DATA, IDS_FILE_WRITE_DATA},
276 {FILE_READ_DATA, IDS_FILE_READ_DATA},
277 {FILE_GENERIC_EXECUTE, IDS_FILE_GENERIC_EXECUTE},
278 {FILE_GENERIC_WRITE, IDS_FILE_GENERIC_WRITE},
279 {FILE_GENERIC_READ, IDS_FILE_GENERIC_READ},
280 {GENERIC_ALL, IDS_GENERIC_ALL},
281 {GENERIC_EXECUTE, IDS_GENERIC_EXECUTE},
282 {GENERIC_WRITE, IDS_GENERIC_WRITE},
283 {GENERIC_READ, IDS_GENERIC_READ},
284 {MAXIMUM_ALLOWED, IDS_MAXIMUM_ALLOWED},
285 {ACCESS_SYSTEM_SECURITY, IDS_ACCESS_SYSTEM_SECURITY},
286 {SPECIFIC_RIGHTS_ALL, IDS_SPECIFIC_RIGHTS_ALL},
287 {STANDARD_RIGHTS_REQUIRED, IDS_STANDARD_RIGHTS_REQUIRED},
288 {SYNCHRONIZE, IDS_SYNCHRONIZE},
289 {WRITE_OWNER, IDS_WRITE_OWNER},
290 {WRITE_DAC, IDS_WRITE_DAC},
291 {READ_CONTROL, IDS_READ_CONTROL},
292 {DELETE, IDS_DELETE},
293 {STANDARD_RIGHTS_ALL, IDS_STANDARD_RIGHTS_ALL},
294 };
295
296 ConResPuts(StdOut, IDS_ALLOW);
297
298 PrintSpecialAccess:
299 ConResPuts(StdOut, IDS_SPECIAL_ACCESS);
300
301 /* print the special access rights */
302 x = ARRAYSIZE(AccessRights);
303 while (x-- != 0)
304 {
305 if ((Ace->Mask & AccessRights[x].Access) == AccessRights[x].Access)
306 {
307 ConPrintf(StdOut, L"\n%s ", FullFileName);
308 for (x2 = 0; x2 < IndentAccess; x2++)
309 {
310 ConPuts(StdOut, L" ");
311 }
312
313 ConResPuts(StdOut, AccessRights[x].uID);
314 }
315 }
316
317 ConPuts(StdOut, L"\n");
318 }
319 }
320
321 ConPuts(StdOut, L"\n");
322
323 /* free up all resources */
324 if (Name != NULL)
325 {
326 HeapFree(GetProcessHeap(),
327 0,
328 Name);
329 }
330
331 if (SidString != NULL)
332 {
333 LocalFree((HLOCAL)SidString);
334 }
335
336 AceIndex++;
337 }
338
339 if (!Error)
340 Ret = TRUE;
341 }
342 else
343 {
344 SetLastError(ERROR_NO_SECURITY_ON_OBJECT);
345 }
346 }
347 }
348
349 HeapFree(GetProcessHeap(),
350 0,
351 SecurityDescriptor);
352 }
353 else
354 {
355 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
356 }
357
358 return Ret;
359 }
360
361 /* add a backslash at end to a path string if necessary */
362 static VOID
AddBackslash(LPTSTR FilePath)363 AddBackslash(LPTSTR FilePath)
364 {
365 INT len = lstrlen(FilePath);
366 LPTSTR pch = CharPrev(FilePath, FilePath + len);
367 if (*pch != _T('\\'))
368 lstrcat(pch, _T("\\"));
369 }
370
371 static BOOL
GetPathOfFile(LPTSTR FilePath,LPCTSTR pszFiles)372 GetPathOfFile(LPTSTR FilePath, LPCTSTR pszFiles)
373 {
374 TCHAR FullPath[MAX_PATH];
375 LPTSTR pch;
376 DWORD attrs;
377
378 lstrcpyn(FilePath, pszFiles, MAX_PATH);
379 pch = _tcsrchr(FilePath, _T('\\'));
380 if (pch != NULL)
381 {
382 *pch = 0;
383 if (!GetFullPathName(FilePath, MAX_PATH, FullPath, NULL))
384 {
385 PrintError(GetLastError());
386 return FALSE;
387 }
388 lstrcpyn(FilePath, FullPath, MAX_PATH);
389
390 attrs = GetFileAttributes(FilePath);
391 if (attrs == 0xFFFFFFFF || !(attrs & FILE_ATTRIBUTE_DIRECTORY))
392 {
393 PrintError(ERROR_DIRECTORY);
394 return FALSE;
395 }
396 }
397 else
398 GetCurrentDirectory(MAX_PATH, FilePath);
399
400 AddBackslash(FilePath);
401 return TRUE;
402 }
403
404 static BOOL
PrintDaclsOfFiles(LPCTSTR pszFiles)405 PrintDaclsOfFiles(LPCTSTR pszFiles)
406 {
407 TCHAR FilePath[MAX_PATH];
408 WIN32_FIND_DATA FindData;
409 HANDLE hFind;
410 DWORD LastError;
411
412 /*
413 * get the file path
414 */
415 if (!GetPathOfFile(FilePath, pszFiles))
416 return FALSE;
417
418 /*
419 * search for the files
420 */
421 hFind = FindFirstFile(pszFiles, &FindData);
422 if (hFind == INVALID_HANDLE_VALUE)
423 return FALSE;
424
425 do
426 {
427 if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
428 continue;
429
430 if (!PrintFileDacl(FilePath, FindData.cFileName))
431 {
432 LastError = GetLastError();
433 if (LastError == ERROR_ACCESS_DENIED)
434 {
435 PrintError(LastError);
436 if (!OptionC)
437 {
438 FindClose(hFind);
439 return FALSE;
440 }
441 }
442 else
443 {
444 break;
445 }
446 }
447 else
448 {
449 ConPuts(StdOut, L"\n");
450 }
451 } while(FindNextFile(hFind, &FindData));
452 LastError = GetLastError();
453 FindClose(hFind);
454
455 if (LastError != ERROR_NO_MORE_FILES)
456 {
457 PrintError(LastError);
458 return FALSE;
459 }
460
461 return TRUE;
462 }
463
464 static BOOL
GrantUserAccessRights(LPCTSTR FilePath,LPCTSTR File,LPCTSTR User,TCHAR Perm)465 GrantUserAccessRights(LPCTSTR FilePath, LPCTSTR File, LPCTSTR User, TCHAR Perm)
466 {
467 /* TODO & FIXME */
468 switch(Perm)
469 {
470 case _T('R'): // Read
471 break;
472 case _T('W'): // Write
473 break;
474 case _T('C'): // Change (write)
475 break;
476 case _T('F'): // Full control
477 break;
478 default:
479 break;
480 }
481 return FALSE;
482 }
483
484 static BOOL
ReplaceUserAccessRights(LPCTSTR FilePath,LPCTSTR File,LPCTSTR User,TCHAR Perm)485 ReplaceUserAccessRights(
486 LPCTSTR FilePath,
487 LPCTSTR File,
488 LPCTSTR User,
489 TCHAR Perm)
490 {
491 /* TODO & FIXME */
492 switch(Perm)
493 {
494 case _T('N'): // None
495 break;
496 case _T('R'): // Read
497 break;
498 case _T('W'): // Write
499 break;
500 case _T('C'): // Change (write)
501 break;
502 case _T('F'): // Full control
503 break;
504 default:
505 break;
506 }
507 return FALSE;
508 }
509
510 static BOOL
EditUserAccessRights(LPCTSTR FilePath,LPCTSTR File,LPCTSTR User,TCHAR Perm)511 EditUserAccessRights(
512 LPCTSTR FilePath,
513 LPCTSTR File,
514 LPCTSTR User,
515 TCHAR Perm)
516 {
517 /* TODO & FIXME */
518 switch(Perm)
519 {
520 case _T('N'): // None
521 break;
522 case _T('R'): // Read
523 break;
524 case _T('W'): // Write
525 break;
526 case _T('C'): // Change (write)
527 break;
528 case _T('F'): // Full control
529 break;
530 default:
531 break;
532 }
533 return FALSE;
534 }
535
536 static BOOL
DenyUserAccess(LPCTSTR FilePath,LPCTSTR File,LPCTSTR User)537 DenyUserAccess(LPCTSTR FilePath, LPCTSTR File, LPCTSTR User)
538 {
539 /* TODO & FIXME */
540 return FALSE;
541 }
542
543 static BOOL
RevokeUserAccessRights(LPCTSTR FilePath,LPCTSTR File,LPCTSTR User)544 RevokeUserAccessRights(LPCTSTR FilePath, LPCTSTR File, LPCTSTR User)
545 {
546 /* TODO & FIXME */
547 return FALSE;
548 }
549
550 static BOOL
ChangeFileACL(LPCTSTR FilePath,LPCTSTR File)551 ChangeFileACL(LPCTSTR FilePath, LPCTSTR File)
552 {
553 if (OptionG)
554 {
555 /* Grant specified user access rights. */
556 GrantUserAccessRights(FilePath, File, GUser, *GPerm);
557 }
558
559 if (OptionP)
560 {
561 if (!OptionE)
562 {
563 /* Replace specified user's access rights. */
564 ReplaceUserAccessRights(FilePath, File, PUser, *PPerm);
565 }
566 else
567 {
568 /* Edit ACL instead of replacing it. */
569 EditUserAccessRights(FilePath, File, PUser, *PPerm);
570 }
571 }
572
573 if (OptionD)
574 {
575 /* Deny specified user access. */
576 DenyUserAccess(FilePath, File, DUser);
577 }
578
579 if (OptionR)
580 {
581 /* Revoke specified user's access rights. */
582 RevokeUserAccessRights(FilePath, File, RUser);
583 }
584
585 return TRUE;
586 }
587
588 static BOOL
ChangeACLsOfFiles(LPCTSTR pszFiles)589 ChangeACLsOfFiles(LPCTSTR pszFiles)
590 {
591 TCHAR FilePath[MAX_PATH];
592 HANDLE hFind;
593 WIN32_FIND_DATA FindData;
594 DWORD LastError;
595
596 /*
597 * get the file path
598 */
599 if (!GetPathOfFile(FilePath, pszFiles))
600 return FALSE;
601
602 /*
603 * search for files in current directory
604 */
605 hFind = FindFirstFile(pszFiles, &FindData);
606 if (hFind == INVALID_HANDLE_VALUE)
607 return FALSE;
608
609 do
610 {
611 if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
612 continue;
613
614 if (!ChangeFileACL(FilePath, FindData.cFileName))
615 {
616 LastError = GetLastError();
617 if (LastError == ERROR_ACCESS_DENIED)
618 {
619 PrintError(LastError);
620 if (!OptionC)
621 {
622 FindClose(hFind);
623 return FALSE;
624 }
625 }
626 else
627 break;
628 }
629 } while(FindNextFile(hFind, &FindData));
630
631 LastError = GetLastError();
632 FindClose(hFind);
633
634 if (LastError != ERROR_NO_MORE_FILES)
635 {
636 PrintError(LastError);
637 return FALSE;
638 }
639
640 return TRUE;
641 }
642
643 static BOOL
ChangeACLsOfFilesInCurDir(LPCTSTR pszFiles)644 ChangeACLsOfFilesInCurDir(LPCTSTR pszFiles)
645 {
646 HANDLE hFind;
647 WIN32_FIND_DATA FindData;
648 TCHAR szCurDir[MAX_PATH];
649 DWORD LastError;
650
651 /*
652 * get the file path (current directory)
653 */
654 GetCurrentDirectory(MAX_PATH, szCurDir);
655 AddBackslash(szCurDir);
656
657 /*
658 * search for files in current directory
659 */
660 hFind = FindFirstFile(pszFiles, &FindData);
661 if (hFind == INVALID_HANDLE_VALUE)
662 return FALSE;
663
664 do
665 {
666 if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
667 continue;
668
669 if (!ChangeFileACL(szCurDir, FindData.cFileName))
670 {
671 LastError = GetLastError();
672 if (LastError == ERROR_ACCESS_DENIED)
673 {
674 PrintError(LastError);
675 if (!OptionC)
676 {
677 FindClose(hFind);
678 return FALSE;
679 }
680 }
681 else
682 break;
683 }
684 } while(FindNextFile(hFind, &FindData));
685
686 LastError = GetLastError();
687 FindClose(hFind);
688
689 if (LastError != ERROR_NO_MORE_FILES)
690 {
691 PrintError(LastError);
692 return FALSE;
693 }
694
695 /*
696 * search for subdirectory in current directory
697 */
698 hFind = FindFirstFile(_T("*"), &FindData);
699 if (hFind == INVALID_HANDLE_VALUE)
700 return FALSE;
701 do
702 {
703 if (_tcscmp(FindData.cFileName, _T(".")) == 0 ||
704 _tcscmp(FindData.cFileName, _T("..")) == 0)
705 continue;
706
707 if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
708 {
709 GetCurrentDirectory(MAX_PATH, szCurDir);
710 if (SetCurrentDirectory(FindData.cFileName))
711 {
712 ChangeACLsOfFilesInCurDir(pszFiles);
713 SetCurrentDirectory(szCurDir);
714 }
715 else
716 {
717 LastError = GetLastError();
718 if (LastError == ERROR_ACCESS_DENIED)
719 {
720 PrintError(LastError);
721 if (!OptionC)
722 {
723 FindClose(hFind);
724 return FALSE;
725 }
726 }
727 else
728 break;
729 }
730 }
731 } while(FindNextFile(hFind, &FindData));
732 LastError = GetLastError();
733 FindClose(hFind);
734
735 if (LastError != ERROR_NO_MORE_FILES)
736 {
737 PrintError(LastError);
738 return FALSE;
739 }
740 return TRUE;
741 }
742
_tmain(int argc,const TCHAR * argv[])743 int _tmain(int argc, const TCHAR *argv[])
744 {
745 INT i;
746 LPTSTR pch;
747 BOOL InvalidParameter = FALSE;
748
749 /* Initialize the Console Standard Streams */
750 ConInitStdStreams();
751
752 if (argc <= 1)
753 {
754 ConResPuts(StdOut, IDS_HELP);
755 return 0;
756 }
757
758 // FIXME: Convert to proper parsing, with support for /?
759
760 /*
761 * parse command line options
762 */
763 for (i = 2; i < argc; i++)
764 {
765 if (lstrcmpi(argv[i], _T("/T")) == 0)
766 {
767 OptionT = TRUE;
768 }
769 else if (lstrcmpi(argv[i], _T("/E")) == 0)
770 {
771 OptionE = TRUE;
772 }
773 else if (lstrcmpi(argv[i], _T("/C")) == 0)
774 {
775 OptionC = TRUE;
776 }
777 else if (lstrcmpi(argv[i], _T("/G")) == 0)
778 {
779 if (i + 1 < argc)
780 {
781 pch = _tcschr(argv[++i], _T(':'));
782 if (pch != NULL)
783 {
784 OptionG = TRUE;
785 *pch = 0;
786 GUser = argv[i];
787 GPerm = pch + 1;
788 continue;
789 }
790 }
791 InvalidParameter = TRUE;
792 break;
793 }
794 else if (lstrcmpi(argv[i], _T("/R")) == 0)
795 {
796 if (i + 1 < argc)
797 {
798 RUser = argv[++i];
799 OptionR = TRUE;
800 continue;
801 }
802 InvalidParameter = TRUE;
803 break;
804 }
805 else if (lstrcmpi(argv[i], _T("/P")) == 0)
806 {
807 if (i + 1 < argc)
808 {
809 pch = _tcschr(argv[++i], _T(':'));
810 if (pch != NULL)
811 {
812 OptionP = TRUE;
813 *pch = 0;
814 PUser = argv[i];
815 PPerm = pch + 1;
816 continue;
817 }
818 }
819 InvalidParameter = TRUE;
820 break;
821 }
822 else if (lstrcmpi(argv[i], _T("/D")) == 0)
823 {
824 if (i + 1 < argc)
825 {
826 OptionD = TRUE;
827 DUser = argv[++i];
828 continue;
829 }
830 InvalidParameter = TRUE;
831 break;
832 }
833 else
834 {
835 InvalidParameter = TRUE;
836 break;
837 }
838 }
839
840 if (InvalidParameter)
841 {
842 PrintError(ERROR_INVALID_PARAMETER);
843 ConResPuts(StdOut, IDS_HELP);
844 return 1;
845 }
846
847 /* /R is only valid with /E */
848 if (OptionR && !OptionE)
849 {
850 OptionR = FALSE;
851 }
852
853 PrintDaclsOfFiles(argv[1]);
854
855 if (OptionT)
856 {
857 ChangeACLsOfFilesInCurDir(argv[1]);
858 }
859 else
860 {
861 ChangeACLsOfFiles(argv[1]);
862 }
863
864 return 0;
865 }
866