Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

584 lines
15 KiB

  1. /*
  2. * Copyright (c) 1998 Microsoft Corporation
  3. *
  4. * Module Name:
  5. *
  6. * dir.cpp
  7. *
  8. * Abstract:
  9. *
  10. * This file contains code to recursively create directories.
  11. *
  12. * Author:
  13. *
  14. * Breen Hagan (BreenH) Oct-02-98
  15. *
  16. * Environment:
  17. *
  18. * User Mode
  19. */
  20. #include "stdafx.h"
  21. #include "logfile.h"
  22. #include <sddl.h>
  23. #include "Aclapi.h"
  24. #include "Accctrl.h"
  25. /*
  26. * Global variables.
  27. */
  28. TCHAR gszDatabaseDirectory[MAX_PATH + 1] =
  29. _T("%SystemRoot%\\System32\\LServer");
  30. /*
  31. * Helper Functions.
  32. */
  33. DWORD
  34. CreateDirectoryRecursively(
  35. IN LPCTSTR pszDirectory
  36. )
  37. {
  38. TCHAR Buffer[MAX_PATH + 1];
  39. PTCHAR p,q;
  40. BOOL fDone, br;
  41. DWORD dwErr;
  42. SECURITY_ATTRIBUTES SA;
  43. SECURITY_INFORMATION securityInfo;
  44. PSECURITY_DESCRIPTOR pSD = NULL;
  45. PACL pOldACL = NULL;
  46. ACL_SIZE_INFORMATION asiAclSize;
  47. DWORD dwBufLength=sizeof(asiAclSize);
  48. ACCESS_ALLOWED_ACE *paaAllowedAce;
  49. DWORD dwAcl_i;
  50. if (_tcslen(pszDirectory) > (MAX_PATH)) {
  51. return(ERROR_BAD_PATHNAME);
  52. }
  53. if (ExpandEnvironmentStrings(pszDirectory, Buffer, MAX_PATH) > MAX_PATH) {
  54. return(ERROR_BAD_PATHNAME);
  55. }
  56. //
  57. // This is a string security descriptor. Look up "Security Descriptor
  58. // Definition Language" in MSDN for more details.
  59. //
  60. // This one says:
  61. //
  62. // D: <we are creating a DACL>
  63. // (A; <Allow ACE>
  64. // OICI; <Perform object and container inheritance, i.e., let files and
  65. // directories under this one have these attributes>
  66. // GA <Generic All Access--Full Control>
  67. // ;;;SY) <SYSTEM>
  68. // (A;OICI;GA;;;BA) <same for Builtin Administrators group>
  69. // (A;OICI;GA;;;CO) <same for creator/owner>
  70. // (A;OICI;GRGWGXDTSDCCLC;;;PU) <read access for Power Users>
  71. //
  72. // We'll use it below to create our directory with the right permissions.
  73. TCHAR* pwszSD = _TEXT("D:(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GA;;;CO)(A;OICI;GRGWGXDTSDCCLC;;;PU)");
  74. SA.nLength = sizeof(SECURITY_ATTRIBUTES);
  75. SA.bInheritHandle = FALSE;
  76. SA.lpSecurityDescriptor = NULL;
  77. br = ConvertStringSecurityDescriptorToSecurityDescriptor(pwszSD,
  78. SDDL_REVISION_1, &(SA.lpSecurityDescriptor), NULL);
  79. if (br == 0) {
  80. dwErr = GetLastError();
  81. goto cleanup;
  82. }
  83. q = Buffer;
  84. if (q[1] == _T(':')) {
  85. //
  86. // This is a "C:" style path. Put p past the colon and first
  87. // backslash, if it exists.
  88. //
  89. if (q[2] == _T('\\')) {
  90. p = &(q[3]);
  91. } else {
  92. p = &(q[2]);
  93. }
  94. } else if (q[0] == _T('\\')) {
  95. //
  96. // This path begins with a backslash. If the second character is
  97. // also a backslash, this is a UNC path, which is not accepted.
  98. //
  99. if (q[1] == _T('\\')) {
  100. return(ERROR_BAD_PATHNAME);
  101. } else {
  102. p = &(q[1]);
  103. }
  104. } else {
  105. //
  106. // This path is a relative path from the current directory.
  107. //
  108. p = q;
  109. }
  110. q = p;
  111. fDone = FALSE;
  112. do {
  113. //
  114. // Locate the next path sep char. If there is none then
  115. // this is the deepest level of the path.
  116. //
  117. p = _tcschr(q, _T('\\'));
  118. if (p) {
  119. *p = (TCHAR)NULL;
  120. } else {
  121. fDone = TRUE;
  122. }
  123. if(fDone == TRUE)
  124. {
  125. BOOL bInherit = TRUE;
  126. //
  127. // Create this portion of the path.
  128. //
  129. if(CreateDirectory(Buffer,&SA)) {
  130. dwErr = NO_ERROR;
  131. }
  132. else
  133. {
  134. dwErr = GetLastError();
  135. if(dwErr == ERROR_ALREADY_EXISTS) {
  136. dwErr = NO_ERROR;
  137. if( GetNamedSecurityInfoW( (LPWSTR)Buffer,
  138. SE_FILE_OBJECT,
  139. DACL_SECURITY_INFORMATION,
  140. NULL, // psidOwner
  141. NULL, // psidGroup
  142. &pOldACL, // pDacl
  143. NULL, // pSacl
  144. &pSD ) != ERROR_SUCCESS)
  145. {
  146. dwErr = GetLastError();
  147. goto cleanup;
  148. }
  149. if(pOldACL == NULL)
  150. {
  151. goto cleanup;
  152. }
  153. if (GetAclInformation(pOldACL,
  154. (LPVOID)&asiAclSize,
  155. (DWORD)dwBufLength,
  156. (ACL_INFORMATION_CLASS)AclSizeInformation))
  157. {
  158. for (dwAcl_i = 0; dwAcl_i < asiAclSize.AceCount; dwAcl_i++)
  159. {
  160. if(GetAce( pOldACL, dwAcl_i, (LPVOID *)&paaAllowedAce))
  161. {
  162. if(!(paaAllowedAce->Header.AceFlags & INHERITED_ACE))
  163. {
  164. //some permission already exist, we don't need to
  165. //do anything (even if it is a different permission!)
  166. bInherit = FALSE;
  167. break;
  168. }
  169. }
  170. }
  171. }
  172. if (bInherit)
  173. {
  174. // If the database had default security => inheritance from parent only then, set the security.
  175. // No changes in case of custom security.
  176. securityInfo = DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION;
  177. SetFileSecurity(Buffer, securityInfo, SA.lpSecurityDescriptor);
  178. }
  179. }
  180. }
  181. }
  182. else
  183. {
  184. if(CreateDirectory(Buffer, NULL)) {
  185. dwErr = NO_ERROR;
  186. }
  187. else {
  188. dwErr = GetLastError();
  189. if(dwErr == ERROR_ALREADY_EXISTS) {
  190. dwErr = NO_ERROR;
  191. }
  192. }
  193. }
  194. if(dwErr == NO_ERROR) {
  195. //
  196. // Put back the path sep and move to the next component.
  197. //
  198. if (!fDone) {
  199. *p = TEXT('\\');
  200. q = p + sizeof(TCHAR);
  201. }
  202. } else {
  203. fDone = TRUE;
  204. }
  205. } while(!fDone);
  206. cleanup:
  207. LocalFree(SA.lpSecurityDescriptor);
  208. SA.lpSecurityDescriptor = NULL;
  209. return(dwErr);
  210. }
  211. BOOL
  212. ConcatenatePaths(
  213. IN OUT LPTSTR Target,
  214. IN LPCTSTR Path,
  215. IN UINT TargetBufferSize,
  216. OUT LPUINT RequiredSize OPTIONAL
  217. )
  218. {
  219. UINT TargetLength,PathLength;
  220. BOOL TrailingBackslash,LeadingBackslash;
  221. UINT EndingLength;
  222. TargetLength = lstrlen(Target);
  223. PathLength = lstrlen(Path);
  224. //
  225. // See whether the target has a trailing backslash.
  226. //
  227. if(TargetLength && (Target[TargetLength-1] == TEXT('\\'))) {
  228. TrailingBackslash = TRUE;
  229. TargetLength--;
  230. } else {
  231. TrailingBackslash = FALSE;
  232. }
  233. //
  234. // See whether the path has a leading backshash.
  235. //
  236. if(Path[0] == TEXT('\\')) {
  237. LeadingBackslash = TRUE;
  238. PathLength--;
  239. } else {
  240. LeadingBackslash = FALSE;
  241. }
  242. //
  243. // Calculate the ending length, which is equal to the sum of
  244. // the length of the two strings modulo leading/trailing
  245. // backslashes, plus one path separator, plus a nul.
  246. //
  247. EndingLength = TargetLength + PathLength + 2;
  248. if(RequiredSize) {
  249. *RequiredSize = EndingLength;
  250. }
  251. if(!LeadingBackslash && (TargetLength < TargetBufferSize)) {
  252. Target[TargetLength++] = TEXT('\\');
  253. }
  254. if(TargetBufferSize > TargetLength) {
  255. lstrcpyn(Target+TargetLength,Path,TargetBufferSize-TargetLength);
  256. }
  257. //
  258. // Make sure the buffer is nul terminated in all cases.
  259. //
  260. if (TargetBufferSize) {
  261. Target[TargetBufferSize-1] = 0;
  262. }
  263. return(EndingLength <= TargetBufferSize);
  264. }
  265. VOID
  266. Delnode(
  267. IN LPCTSTR Directory
  268. )
  269. {
  270. TCHAR pszDirectory[MAX_PATH + 1];
  271. TCHAR pszPattern[MAX_PATH + 1];
  272. WIN32_FIND_DATA FindData;
  273. HANDLE FindHandle;
  274. LOGMESSAGE(_T("Delnode: Entered"));
  275. //
  276. // Delete each file in the given directory, then remove the directory
  277. // itself. If any directories are encountered along the way recurse to
  278. // delete them as they are encountered.
  279. //
  280. // Start by forming the search pattern, which is <currentdir>\*.
  281. //
  282. ExpandEnvironmentStrings(Directory, pszDirectory, MAX_PATH);
  283. LOGMESSAGE(_T("Delnode: Deleting %s"), pszDirectory);
  284. lstrcpyn(pszPattern, pszDirectory, MAX_PATH);
  285. ConcatenatePaths(pszPattern, _T("*"), MAX_PATH, NULL);
  286. //
  287. // Start the search.
  288. //
  289. FindHandle = FindFirstFile(pszPattern, &FindData);
  290. if(FindHandle != INVALID_HANDLE_VALUE) {
  291. do {
  292. //
  293. // Form the full name of the file or directory we just found.
  294. //
  295. lstrcpyn(pszPattern, pszDirectory, MAX_PATH);
  296. ConcatenatePaths(pszPattern, FindData.cFileName, MAX_PATH, NULL);
  297. //
  298. // Remove read-only atttribute if it's there.
  299. //
  300. if (FindData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
  301. SetFileAttributes(pszPattern, FILE_ATTRIBUTE_NORMAL);
  302. }
  303. if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  304. //
  305. // The current match is a directory. Recurse into it unless
  306. // it's . or ...
  307. //
  308. if ((lstrcmp(FindData.cFileName,_T("."))) &&
  309. (lstrcmp(FindData.cFileName,_T("..")))) {
  310. Delnode(pszPattern);
  311. }
  312. } else {
  313. //
  314. // The current match is not a directory -- so delete it.
  315. //
  316. if (!DeleteFile(pszPattern)) {
  317. LOGMESSAGE(_T("Delnode: %s not deleted: %d"), pszPattern,
  318. GetLastError());
  319. }
  320. }
  321. } while(FindNextFile(FindHandle, &FindData));
  322. FindClose(FindHandle);
  323. }
  324. //
  325. // Remove the directory we just emptied out. Ignore errors.
  326. //
  327. RemoveDirectory(pszDirectory);
  328. }
  329. /*
  330. * Exported Functions.
  331. */
  332. /*
  333. * CheckDatabaseDirectory()
  334. *
  335. * CheckDatabaseDirectory is very hardcore about which paths it will accept.
  336. *
  337. * Good Paths:
  338. * <DriveLetter>:\AbsolutePathToDirectory
  339. *
  340. * Bad Paths:
  341. * Any path that is not like above, AND any path in the form above that
  342. * is not on a fixed disk (e.g. no path to a floppy, CD-ROM, network
  343. * share).
  344. */
  345. DWORD
  346. CheckDatabaseDirectory(
  347. IN LPCTSTR pszDatabaseDir
  348. )
  349. {
  350. BOOL fBadChars;
  351. BOOL fBadPath;
  352. UINT DriveType;
  353. TCHAR pszExpandedDir[MAX_PATH + 1];
  354. LOGMESSAGE(_T("CheckDatabaseDirectory: Entered"));
  355. LOGMESSAGE(_T("CheckDatabaseDirectory: Checking %s"), pszDatabaseDir);
  356. //
  357. // NULL is not accepted.
  358. //
  359. if (pszDatabaseDir == NULL) {
  360. return(ERROR_INVALID_PARAMETER);
  361. }
  362. //
  363. // A path greater than MAX_PATH will cause problems somewhere. This
  364. // will also catch pathnames with no environment variables that are
  365. // still too long.
  366. //
  367. if (ExpandEnvironmentStrings(pszDatabaseDir, pszExpandedDir, MAX_PATH) >
  368. MAX_PATH) {
  369. LOGMESSAGE(_T("CheckDatabaseDirectory: Path too long"));
  370. return(ERROR_BAD_PATHNAME);
  371. }
  372. //
  373. // A path of less than three characters can't contain "<DriveLetter>:\".
  374. // Also, don't allow anything but a letter, a colon, and a backslash.
  375. //
  376. fBadPath = FALSE;
  377. if (!fBadPath) {
  378. fBadPath = (_tcslen(pszExpandedDir) < 3);
  379. }
  380. if (!fBadPath) {
  381. fBadPath = !(_istalpha(pszExpandedDir[0]));
  382. }
  383. if (!fBadPath) {
  384. fBadPath = (pszExpandedDir[1] != _T(':'));
  385. }
  386. if (!fBadPath) {
  387. fBadPath = (pszExpandedDir[2] != _T('\\'));
  388. }
  389. if (fBadPath) {
  390. LOGMESSAGE(_T("CheckDatabaseDirectory: Not a C:\\ style directory"));
  391. return(ERROR_BAD_PATHNAME);
  392. }
  393. //
  394. // Characters like < > * ? and , won't work. Check for that now.
  395. // Also, check for additional colons after the first C:\....
  396. //
  397. fBadChars = FALSE;
  398. if (!fBadChars) {
  399. fBadChars = (_tcschr(pszExpandedDir, _T('<')) != NULL);
  400. }
  401. if (!fBadChars) {
  402. fBadChars = (_tcschr(pszExpandedDir, _T('>')) != NULL);
  403. }
  404. if (!fBadChars) {
  405. fBadChars = (_tcschr(pszExpandedDir, _T('*')) != NULL);
  406. }
  407. if (!fBadChars) {
  408. fBadChars = (_tcschr(pszExpandedDir, _T('?')) != NULL);
  409. }
  410. if (!fBadChars) {
  411. fBadChars = (_tcschr(&(pszExpandedDir[3]), _T(':')) != NULL);
  412. }
  413. if (fBadChars) {
  414. LOGMESSAGE(_T("CheckDatabaseDirectory: Invalid characters"));
  415. return(ERROR_BAD_PATHNAME);
  416. }
  417. //
  418. // GetDriveType only works for paths in the form "C:\" or
  419. // "C:\ExistingDir". As pszDatabaseDir probably doesn't exist, it can't
  420. // be passed to GetDriveType. Set a NULL character passed the "C:\" to
  421. // pass in only the drive letter.
  422. //
  423. pszExpandedDir[3] = (TCHAR)NULL;
  424. DriveType = GetDriveType(pszExpandedDir);
  425. if (DriveType == DRIVE_FIXED) {
  426. return(NO_ERROR);
  427. } else {
  428. LOGMESSAGE(_T("CheckDatabaseDirectory: Bad DriveType %d"), DriveType);
  429. return(ERROR_BAD_PATHNAME);
  430. }
  431. }
  432. /*
  433. * CreateDatabaseDirectory()
  434. *
  435. * Creates the specified database directory.
  436. */
  437. DWORD
  438. CreateDatabaseDirectory(
  439. VOID
  440. )
  441. {
  442. return(CreateDirectoryRecursively(gszDatabaseDirectory));
  443. }
  444. /*
  445. * GetDatabaseDirectory()
  446. *
  447. * Returns the current database directory.
  448. */
  449. LPCTSTR
  450. GetDatabaseDirectory(
  451. VOID
  452. )
  453. {
  454. return(gszDatabaseDirectory);
  455. }
  456. /*
  457. * RemoveDatabaseDirectory()
  458. *
  459. * Removes the entire database directory.
  460. */
  461. VOID
  462. RemoveDatabaseDirectory(
  463. VOID
  464. )
  465. {
  466. Delnode(gszDatabaseDirectory);
  467. }
  468. /*
  469. * SetDatabaseDirectory()
  470. *
  471. * This function assumes pszDatabaseDir has been verified by a call to
  472. * CheckDatabaseDir(), which verifies not NULL, within MAX_PATH, and on a
  473. * fixed hard drive.
  474. */
  475. VOID
  476. SetDatabaseDirectory(
  477. IN LPCTSTR pszDatabaseDir
  478. )
  479. {
  480. if(pszDatabaseDir && (_tcslen(pszDatabaseDir) <= MAX_PATH ))
  481. {
  482. _tcscpy(gszDatabaseDirectory, pszDatabaseDir);
  483. }
  484. }