Source code of Windows XP (NT5)
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.

1186 lines
28 KiB

  1. #include "psxsrv.h"
  2. #include <sys/stat.h>
  3. #include "sesport.h"
  4. NTSTATUS
  5. DoAccess(
  6. HANDLE FileHandle,
  7. PPSX_PROCESS p,
  8. mode_t Mode,
  9. PULONG pError
  10. );
  11. BOOLEAN
  12. DumpFileIfRequired(
  13. IN PPSX_PROCESS p,
  14. IN PPSX_API_MSG m,
  15. IN HANDLE FileHandle,
  16. IN PUNICODE_STRING Path_U
  17. );
  18. static BOOLEAN
  19. IsUserInGroup(
  20. PPSX_PROCESS p,
  21. PSID Group
  22. );
  23. static PVOID pvSDMem[10]; // InitSecurityDescriptor -> DeInitSD
  24. BOOLEAN
  25. FilesAreIdentical(
  26. HANDLE FileA,
  27. HANDLE FileB
  28. )
  29. {
  30. FILE_INTERNAL_INFORMATION infoA, infoB;
  31. IO_STATUS_BLOCK Iosb;
  32. NTSTATUS Status;
  33. Status = NtQueryInformationFile(FileA, &Iosb, &infoA,
  34. sizeof(infoA), FileInternalInformation);
  35. ASSERT(NT_SUCCESS(Status));
  36. Status = NtQueryInformationFile(FileB, &Iosb, &infoB,
  37. sizeof(infoB), FileInternalInformation);
  38. ASSERT(NT_SUCCESS(Status));
  39. return infoA.IndexNumber.QuadPart == infoB.IndexNumber.QuadPart;
  40. }
  41. BOOLEAN
  42. PsxUnlink(
  43. IN PPSX_PROCESS p,
  44. IN OUT PPSX_API_MSG m
  45. )
  46. {
  47. PPSX_UNLINK_MSG args;
  48. HANDLE FileHandle,
  49. DirHandle;
  50. UNICODE_STRING
  51. ParentDir_U; // path to the containing directory
  52. OBJECT_ATTRIBUTES
  53. DirObj, // names the parent directory
  54. FileObj; // names the file to be unlinked
  55. IO_STATUS_BLOCK Iosb;
  56. NTSTATUS Status;
  57. FILE_DISPOSITION_INFORMATION Disp;
  58. ANSI_STRING Path_A;
  59. PCHAR pch;
  60. ULONG Error;
  61. args = &m->u.Unlink;
  62. if (!ISPOINTERVALID_CLIENT(p, args->Path_U.Buffer,
  63. args->Path_U.Length)) {
  64. KdPrint(("Invalid pointer to unlink: %lx, %d\n",
  65. args->Path_U.Buffer, args->Path_U.Length));
  66. m->Error = EINVAL;
  67. return TRUE;
  68. }
  69. Status = RtlUnicodeStringToAnsiString(&Path_A, &args->Path_U, TRUE);
  70. if (!NT_SUCCESS(Status)) {
  71. m->Error = ENOMEM;
  72. return TRUE;
  73. }
  74. if ('\\' == Path_A.Buffer[Path_A.Length - 1]) {
  75. //
  76. // Unlink must reject paths that end in slash.
  77. //
  78. RtlFreeAnsiString(&Path_A);
  79. m->Error = EINVAL;
  80. return TRUE;
  81. }
  82. RtlFreeAnsiString(&Path_A);
  83. Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
  84. if (NT_SUCCESS(Status)) {
  85. InitializeObjectAttributes(&FileObj, &args->Path_U, 0, NULL, NULL);
  86. Status = NtOpenFile(&FileHandle, DELETE | SYNCHRONIZE, &FileObj,
  87. &Iosb, SHARE_ALL,
  88. FILE_SYNCHRONOUS_IO_ALERT | FILE_NON_DIRECTORY_FILE);
  89. EndImpersonation();
  90. }
  91. if (!NT_SUCCESS(Status)) {
  92. if (STATUS_FILE_IS_A_DIRECTORY == Status) {
  93. m->Error = EPERM;
  94. return TRUE;
  95. }
  96. if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
  97. m->Error = PsxStatusToErrnoPath(&args->Path_U);
  98. return TRUE;
  99. }
  100. m->Error = PsxStatusToErrno(Status);
  101. return TRUE;
  102. }
  103. //
  104. // If some posix process has this file open, we can't delete
  105. // it or that process would be unable to write to the file (the
  106. // only operation allowed on a deleted file is "close").
  107. //
  108. if (DumpFileIfRequired(p, m, FileHandle, &args->Path_U)) {
  109. NtClose(FileHandle);
  110. return TRUE;
  111. }
  112. //
  113. // If we didn't dump the file, we'll try to delete it in the
  114. // obvious way. Should succeed unless some windows process has
  115. // it open -- we don't worry about that possibility, you get
  116. // what you get.
  117. //
  118. Disp.DeleteFile = TRUE;
  119. Status = NtSetInformationFile(FileHandle, &Iosb, &Disp, sizeof(Disp),
  120. FileDispositionInformation);
  121. if (!NT_SUCCESS(Status)) {
  122. KdPrint(("PSXSS: PsxUnlink: SetInfoFile: 0x%x\n", Status));
  123. m->Error = PsxStatusToErrno(Status);
  124. return TRUE;
  125. }
  126. NtClose(FileHandle);
  127. return TRUE;
  128. }
  129. NTSTATUS
  130. DoAccess(
  131. HANDLE FileHandle,
  132. PPSX_PROCESS p,
  133. mode_t Mode,
  134. PULONG pError
  135. )
  136. {
  137. FILE_ACCESS_INFORMATION FileAccessInfo;
  138. BOOLEAN RetVal;
  139. SECURITY_INFORMATION SecurityInformation;
  140. PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
  141. PSID NtOwner, NtGroup;
  142. BOOLEAN OwnerDefaulted, GroupDefaulted;
  143. BOOLEAN DaclPresent, DaclDefaulted;
  144. ACCESS_MASK UserAccess, GroupAccess, OtherAccess;
  145. ULONG LengthNeeded;
  146. mode_t filemode;
  147. PSID UserSid;
  148. PACL pDacl;
  149. NTSTATUS Status;
  150. HANDLE TokenHandle;
  151. PSID_AND_ATTRIBUTES pSA;
  152. //
  153. // N.B.: this code kind of depends on F_OK being 0.
  154. //
  155. if (F_OK == Mode) {
  156. //
  157. // The file exists: succeed.
  158. //
  159. return TRUE;
  160. }
  161. Status = NtOpenProcessToken(p->Process, GENERIC_READ, &TokenHandle);
  162. if (!NT_SUCCESS(Status)) {
  163. // can fail due to lack of memory
  164. *pError = ENOMEM;
  165. return Status;
  166. }
  167. Status = NtQueryInformationToken(TokenHandle, TokenUser, NULL,
  168. 0, &LengthNeeded);
  169. if (STATUS_BUFFER_TOO_SMALL != Status) {
  170. NtClose(TokenHandle);
  171. *pError = PsxStatusToErrno(Status);
  172. return Status;
  173. }
  174. pSA = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
  175. if (NULL == pSA) {
  176. *pError = ENOMEM;
  177. NtClose(TokenHandle);
  178. return STATUS_NO_MEMORY;
  179. }
  180. Status = NtQueryInformationToken(TokenHandle, TokenUser, pSA,
  181. LengthNeeded, &LengthNeeded);
  182. ASSERT(NT_SUCCESS(Status));
  183. UserSid = pSA->Sid;
  184. //
  185. // Get the security descriptor for the file.
  186. //
  187. SecurityInformation = OWNER_SECURITY_INFORMATION |
  188. GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
  189. Status = NtQuerySecurityObject(FileHandle, SecurityInformation,
  190. NULL, 0, &LengthNeeded);
  191. if (!(STATUS_BUFFER_TOO_SMALL == Status)) {
  192. KdPrint(("PSXSS: NtQSObject failed: 0x%x\n", Status));
  193. }
  194. ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
  195. SecurityDescriptor = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
  196. if (NULL == SecurityDescriptor) {
  197. NtClose(FileHandle);
  198. *pError = ENOMEM;
  199. return STATUS_NO_MEMORY;
  200. }
  201. Status = NtQuerySecurityObject(FileHandle, SecurityInformation,
  202. SecurityDescriptor, LengthNeeded, &LengthNeeded);
  203. ASSERT(NT_SUCCESS(Status));
  204. //
  205. // Get the owner and group from the security descriptor
  206. //
  207. Status = RtlGetOwnerSecurityDescriptor(SecurityDescriptor,
  208. &NtOwner, &OwnerDefaulted);
  209. ASSERT(NT_SUCCESS(Status));
  210. Status = RtlGetGroupSecurityDescriptor(SecurityDescriptor,
  211. &NtGroup, &GroupDefaulted);
  212. ASSERT(NT_SUCCESS(Status));
  213. if (NULL == NtOwner || NULL == NtGroup) {
  214. //
  215. // Seems like this file doesn't have an owner or a
  216. // group, which means that we can't change it's mode.
  217. //
  218. NtClose(FileHandle);
  219. *pError = EPERM;
  220. return TRUE;
  221. }
  222. Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor,
  223. &DaclPresent, &pDacl, &DaclDefaulted);
  224. ASSERT(NT_SUCCESS(Status));
  225. if (!DaclPresent || NULL == pDacl) {
  226. //
  227. // In this case, all access is granted; we don't even look
  228. // to see what particular access the caller was interested
  229. // in.
  230. //
  231. return TRUE;
  232. }
  233. Status = RtlInterpretPosixAcl(ACL_REVISION2, NtOwner, NtGroup,
  234. pDacl, &UserAccess, &GroupAccess, &OtherAccess);
  235. if (!NT_SUCCESS(Status)) {
  236. //
  237. // XXX.mjb: Should do something reasonable here.
  238. //
  239. return TRUE;
  240. }
  241. filemode = AccessMaskToMode(UserAccess, GroupAccess, OtherAccess);
  242. if (RtlEqualSid(UserSid, NtOwner)) {
  243. if (Mode & (filemode >> 6)) {
  244. // access is granted.
  245. return STATUS_SUCCESS;
  246. }
  247. }
  248. if (IsUserInGroup(p, NtGroup)) {
  249. if (Mode & (filemode >> 3)) {
  250. // access is granted.
  251. return STATUS_SUCCESS;
  252. }
  253. }
  254. if (Mode & filemode) {
  255. // access is granted.
  256. return STATUS_SUCCESS;
  257. }
  258. *pError = EACCES;
  259. return STATUS_UNSUCCESSFUL;
  260. }
  261. //
  262. // See if the given group is one that the owner of this process belongs
  263. // to.
  264. //
  265. static BOOLEAN
  266. IsUserInGroup(
  267. PPSX_PROCESS p,
  268. PSID Group
  269. )
  270. {
  271. HANDLE TokenHandle;
  272. TOKEN_GROUPS *pGroups;
  273. ULONG LengthNeeded;
  274. NTSTATUS Status;
  275. BOOLEAN RetVal = FALSE;
  276. ULONG i;
  277. Status = NtOpenProcessToken(p->Process, GENERIC_READ, &TokenHandle);
  278. ASSERT(NT_SUCCESS(Status));
  279. Status = NtQueryInformationToken(TokenHandle, TokenGroups, NULL,
  280. 0, &LengthNeeded);
  281. ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
  282. pGroups = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
  283. if (NULL == pGroups) {
  284. NtClose(TokenHandle);
  285. return FALSE;
  286. }
  287. Status = NtQueryInformationToken(TokenHandle, TokenGroups, pGroups,
  288. LengthNeeded, &LengthNeeded);
  289. ASSERT(NT_SUCCESS(Status));
  290. for (i = 0; i < pGroups->GroupCount; ++i) {
  291. if (RtlEqualSid(pGroups->Groups[i].Sid, Group)) {
  292. RetVal = TRUE;
  293. break;
  294. }
  295. }
  296. RtlFreeHeap(PsxHeap, 0, pGroups);
  297. NtClose(TokenHandle);
  298. return RetVal;
  299. }
  300. BOOLEAN
  301. PsxRmDir(
  302. IN PPSX_PROCESS p,
  303. IN PPSX_API_MSG m
  304. )
  305. /*++
  306. Routine Description:
  307. This procedure implements POSIX rmdir.
  308. Arguments:
  309. p - Supplies the address of the process making the call.
  310. m - Supplies the address of the message associated with the rmdir request.
  311. Return Value:
  312. TRUE - The contents of *m should be used to generate a reply.
  313. --*/
  314. {
  315. PPSX_RMDIR_MSG args;
  316. HANDLE FileHandle, ParentHandle;
  317. NTSTATUS Status;
  318. IO_STATUS_BLOCK Iosb;
  319. OBJECT_ATTRIBUTES
  320. ParentObj,
  321. DirObj;
  322. UNICODE_STRING
  323. ParentDir_U,
  324. Path_U;
  325. FILE_DISPOSITION_INFORMATION DispositionInfo;
  326. FILE_INTERNAL_INFORMATION SerialNumber;
  327. PIONODE IoNode;
  328. UCHAR buf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) +
  329. 128 * sizeof(WCHAR)];
  330. PFILE_FS_ATTRIBUTE_INFORMATION pFSInfo = (PVOID)buf;
  331. ANSI_STRING Path_A;
  332. PCHAR pch;
  333. args = &m->u.RmDir;
  334. if (!ISPOINTERVALID_CLIENT(p,args->Path_U.Buffer,args->Path_U.Length)) {
  335. KdPrint(("Invalid pointer to rmdir %lx \n",
  336. args->Path_U.Buffer));
  337. m->Error = EINVAL;
  338. return TRUE;
  339. }
  340. Path_U = args->Path_U;
  341. Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
  342. if (NT_SUCCESS(Status)) {
  343. InitializeObjectAttributes(&DirObj, &Path_U, 0, NULL, NULL);
  344. Status = NtOpenFile(&FileHandle, SYNCHRONIZE | DELETE,
  345. &DirObj, &Iosb,
  346. SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
  347. EndImpersonation();
  348. }
  349. if (!NT_SUCCESS(Status)) {
  350. if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
  351. m->Error = PsxStatusToErrnoPath(&Path_U);
  352. return TRUE;
  353. }
  354. m->Error = PsxStatusToErrno(Status);
  355. return TRUE;
  356. }
  357. //
  358. // If some posix process has this dir open, we can't delete
  359. // it or that process would be unable to read from the dir (the
  360. // only operation allowed on a deleted dir is "close".
  361. //
  362. //
  363. // XXX.mjb: must check to make sure the directory contains no files,
  364. // and return ENOTEMPTY or EEXIST if so.
  365. //
  366. if (DumpFileIfRequired(p, m, FileHandle, &Path_U)) {
  367. NtClose(FileHandle);
  368. return TRUE;
  369. }
  370. m->Error = 0;
  371. DispositionInfo.DeleteFile = TRUE;
  372. Status = NtSetInformationFile(FileHandle, &Iosb,
  373. &DispositionInfo, sizeof(DispositionInfo),
  374. FileDispositionInformation);
  375. if (!NT_SUCCESS(Status)) {
  376. if (STATUS_CANNOT_DELETE == Status) {
  377. //
  378. // 1003.1-90 (5.5.2.2) : If the named directory is the root
  379. // directory or the current working directory of any process, it
  380. // is unspecified whether the function succeeds or whether it fails
  381. // and sets /errno/ to [EBUSY].
  382. //
  383. // NT is going to fail with STATUS_CANNOT_DELETE here, and so we
  384. // want posix to return EBUSY. (The standard posix mapping is to
  385. // ETXTBUSY.)
  386. //
  387. m->Error = EBUSY;
  388. } else {
  389. m->Error = PsxStatusToErrno(Status);
  390. }
  391. }
  392. Status = NtClose(FileHandle);
  393. ASSERT(NT_SUCCESS(Status));
  394. return TRUE;
  395. }
  396. BOOLEAN
  397. PsxAccess(
  398. IN PPSX_PROCESS p,
  399. IN PPSX_API_MSG m
  400. )
  401. /*++
  402. Routine Description:
  403. This procedure implements POSIX access.
  404. Arguments:
  405. p - Supplies the address of the process making the call.
  406. m - Supplies the address of the message associated with the access request.
  407. Return Value:
  408. TRUE - The contents of *m should be used to generate a reply.
  409. --*/
  410. {
  411. PPSX_ACCESS_MSG args;
  412. HANDLE FileHandle;
  413. NTSTATUS Status;
  414. IO_STATUS_BLOCK Iosb;
  415. OBJECT_ATTRIBUTES ObjA;
  416. UNICODE_STRING Path_U;
  417. args = &m->u.Access;
  418. if (!ISPOINTERVALID_CLIENT(p,args->Path_U.Buffer,args->Path_U.Length)) {
  419. KdPrint(("Invalid pointer to access: %lx\n",
  420. args->Path_U.Buffer));
  421. m->Error = EINVAL;
  422. return TRUE;
  423. }
  424. Path_U = args->Path_U;
  425. InitializeObjectAttributes(&ObjA, &Path_U, 0, NULL, NULL);
  426. Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
  427. if (NT_SUCCESS(Status)) {
  428. Status = NtOpenFile(&FileHandle,
  429. SYNCHRONIZE | READ_CONTROL | FILE_READ_ATTRIBUTES,
  430. &ObjA, &Iosb,
  431. SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT);
  432. EndImpersonation();
  433. }
  434. if (!NT_SUCCESS(Status)) {
  435. if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
  436. m->Error = PsxStatusToErrnoPath(&Path_U);
  437. return TRUE;
  438. }
  439. m->Error = PsxStatusToErrno(Status);
  440. return TRUE;
  441. }
  442. (void)DoAccess(FileHandle, p, args->Amode, &m->Error);
  443. NtClose(FileHandle);
  444. return TRUE;
  445. }
  446. BOOLEAN
  447. PsxRename(
  448. IN PPSX_PROCESS p,
  449. IN PPSX_API_MSG m
  450. )
  451. /*++
  452. Routine Description:
  453. This procedure implements POSIX rename. The routine must
  454. be implemented on the server because if we're renaming onto
  455. an open file, we need to move it to the dump first.
  456. Arguments:
  457. p - Supplies the address of the process making the call.
  458. m - Supplies the address of the message associated with the access request.
  459. Return Value:
  460. TRUE - The contents of *m should be used to generate a reply.
  461. --*/
  462. {
  463. PPSX_RENAME_MSG args;
  464. HANDLE FileHandle, NewFileHandle, RootDirHandle;
  465. NTSTATUS Status;
  466. IO_STATUS_BLOCK Iosb;
  467. OBJECT_ATTRIBUTES Obj;
  468. ANSI_STRING str_A, rel_A;
  469. UNICODE_STRING str_U, new_U;
  470. PFILE_RENAME_INFORMATION pRenameInfo;
  471. char *pch, ch;
  472. FILE_INTERNAL_INFORMATION SerialNumber;
  473. unsigned char buf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) +
  474. 128*sizeof(WCHAR)];
  475. PFILE_FS_ATTRIBUTE_INFORMATION pFSInfo = (PVOID)buf;
  476. PIONODE IoNode;
  477. args = &m->u.Rename;
  478. //
  479. // Open the old pathname
  480. //
  481. InitializeObjectAttributes(&Obj, &args->OldName, 0, NULL, NULL);
  482. Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
  483. if (NT_SUCCESS(Status)) {
  484. Status = NtOpenFile(&FileHandle, SYNCHRONIZE | DELETE,
  485. &Obj, &Iosb, SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT);
  486. EndImpersonation();
  487. }
  488. if (!NT_SUCCESS(Status)) {
  489. if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
  490. m->Error = PsxStatusToErrnoPath(&args->OldName);
  491. return TRUE;
  492. }
  493. m->Error = PsxStatusToErrno(Status);
  494. return TRUE;
  495. }
  496. //
  497. // Now we need to get a handle on the root directory of the 'new'
  498. // path; we'll pass that in the rename information, and the rest
  499. // of the path will be given relative to that root. We depend
  500. // on paths looking like "\DosDevices\X:\path".
  501. //
  502. //
  503. // Open the new filename for error checking, then close. We
  504. // also take this opportunity to find the ENOTDIR error case.
  505. //
  506. InitializeObjectAttributes(&Obj, &args->NewName, 0, NULL, NULL);
  507. Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
  508. if (NT_SUCCESS(Status)) {
  509. Status = NtOpenFile(&NewFileHandle, SYNCHRONIZE,
  510. &Obj, &Iosb, SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT);
  511. EndImpersonation();
  512. }
  513. if (NT_SUCCESS(Status)) {
  514. if (FilesAreIdentical(FileHandle, NewFileHandle)) {
  515. //
  516. // 1003.1-90 (5.5.3.1): If the old argument and the
  517. // new argument both refer to links to the same
  518. // existing file, the rename function shall return
  519. // successfully and perform no other action.
  520. //
  521. NtClose(FileHandle);
  522. NtClose(NewFileHandle);
  523. m->ReturnValue = 0;
  524. return TRUE;
  525. }
  526. DumpFileIfRequired(p, m, NewFileHandle, &args->NewName);
  527. m->Error = 0;
  528. NtClose(NewFileHandle);
  529. } else if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
  530. m->Error = PsxStatusToErrnoPath(&args->NewName);
  531. if (ENOTDIR == m->Error) {
  532. NtClose(FileHandle);
  533. return TRUE;
  534. }
  535. m->Error = 0;
  536. } else {
  537. NtClose(FileHandle);
  538. m->Error = PsxStatusToErrno(Status);
  539. return TRUE;
  540. }
  541. Status = RtlUnicodeStringToAnsiString(&str_A, &args->NewName, TRUE);
  542. if (!NT_SUCCESS(Status)) {
  543. NtClose(FileHandle);
  544. m->Error = ENOMEM;
  545. return TRUE;
  546. }
  547. // find the root directory
  548. pch = strchr(str_A.Buffer + 1, '\\');
  549. ASSERT(NULL != pch);
  550. pch = strchr(pch + 1, '\\');
  551. ASSERT(NULL != pch);
  552. ch = pch[1];
  553. pch[1] = '\000';
  554. str_A.Length = (USHORT)strlen(str_A.Buffer);
  555. Status = RtlAnsiStringToUnicodeString(&str_U, &str_A, TRUE);
  556. if (!NT_SUCCESS(Status)) {
  557. RtlFreeAnsiString(&str_A);
  558. NtClose(FileHandle);
  559. m->Error = ENOMEM;
  560. return TRUE;
  561. }
  562. InitializeObjectAttributes(&Obj, &str_U, 0, NULL, NULL);
  563. Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
  564. if (NT_SUCCESS(Status)) {
  565. Status = NtOpenFile(&RootDirHandle, SYNCHRONIZE, &Obj, &Iosb,
  566. SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
  567. EndImpersonation();
  568. }
  569. RtlFreeUnicodeString(&str_U);
  570. if (!NT_SUCCESS(Status)) {
  571. RtlFreeAnsiString(&str_A);
  572. NtClose(FileHandle);
  573. m->Error = PsxStatusToErrno(Status);
  574. return TRUE;
  575. }
  576. //
  577. // Now get the path relative to the root.
  578. //
  579. pch[1] = ch;
  580. rel_A.Buffer = &pch[1];
  581. rel_A.Length = rel_A.MaximumLength = (USHORT)strlen(rel_A.Buffer);
  582. Status = RtlAnsiStringToUnicodeString(&str_U, &rel_A, TRUE);
  583. RtlFreeAnsiString(&str_A);
  584. if (!NT_SUCCESS(Status)) {
  585. NtClose(RootDirHandle);
  586. NtClose(FileHandle);
  587. m->Error = ENOMEM;
  588. return TRUE;
  589. }
  590. pRenameInfo = RtlAllocateHeap(PsxHeap, 0,
  591. sizeof(*pRenameInfo) + str_U.Length);
  592. if (NULL == pRenameInfo) {
  593. RtlFreeUnicodeString(&str_U);
  594. NtClose(RootDirHandle);
  595. NtClose(FileHandle);
  596. m->Error = ENOMEM;
  597. return TRUE;
  598. }
  599. RtlMoveMemory(pRenameInfo->FileName, str_U.Buffer, str_U.Length);
  600. pRenameInfo->FileNameLength = str_U.Length;
  601. pRenameInfo->ReplaceIfExists = TRUE;
  602. pRenameInfo->RootDirectory = RootDirHandle;
  603. Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
  604. if (NT_SUCCESS(Status)) {
  605. Status = NtSetInformationFile(FileHandle, &Iosb, pRenameInfo,
  606. sizeof(*pRenameInfo) + str_U.Length,
  607. FileRenameInformation);
  608. EndImpersonation();
  609. }
  610. RtlFreeUnicodeString(&str_U);
  611. NtClose(FileHandle);
  612. NtClose(RootDirHandle);
  613. RtlFreeHeap(PsxHeap, 0, pRenameInfo);
  614. if (!NT_SUCCESS(Status)) {
  615. m->Error = PsxStatusToErrno(Status);
  616. }
  617. return TRUE;
  618. }
  619. BOOLEAN
  620. PsxLink(
  621. IN PPSX_PROCESS p,
  622. IN PPSX_API_MSG m
  623. )
  624. {
  625. PPSX_LINK_MSG args;
  626. HANDLE FileHandle, NewFileHandle, RootDirHandle;
  627. NTSTATUS Status;
  628. IO_STATUS_BLOCK Iosb;
  629. OBJECT_ATTRIBUTES Obj;
  630. PFILE_LINK_INFORMATION pLinkInfo;
  631. FILE_INTERNAL_INFORMATION SerialNumber;
  632. LARGE_INTEGER Time;
  633. ULONG PosixTime;
  634. PIONODE IoNode;
  635. UNICODE_STRING str_U, new_U;
  636. ANSI_STRING str_A, rel_A;
  637. char *pch, ch;
  638. args = &m->u.Link;
  639. //
  640. // Open the existing pathname.
  641. //
  642. InitializeObjectAttributes(&Obj, &args->OldName, 0, NULL, NULL);
  643. Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
  644. if (NT_SUCCESS(Status)) {
  645. Status = NtOpenFile(&FileHandle, SYNCHRONIZE, &Obj, &Iosb,
  646. SHARE_ALL,
  647. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
  648. EndImpersonation();
  649. }
  650. if (!NT_SUCCESS(Status)) {
  651. if (STATUS_FILE_IS_A_DIRECTORY == Status) {
  652. m->Error = EPERM;
  653. return TRUE;
  654. }
  655. if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
  656. m->Error = PsxStatusToErrnoPath(&args->OldName);
  657. return TRUE;
  658. }
  659. m->Error = PsxStatusToErrno(Status);
  660. return TRUE;
  661. }
  662. //
  663. // Open new filename for error checking, then close. This
  664. // is to find the ENOTDIR error case.
  665. //
  666. InitializeObjectAttributes(&Obj, &args->NewName, 0, NULL, NULL);
  667. Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
  668. if (!NT_SUCCESS(Status)) {
  669. NtClose(FileHandle);
  670. m->Error = PsxStatusToErrno(Status);
  671. return TRUE;
  672. }
  673. Status = NtOpenFile(&NewFileHandle, SYNCHRONIZE,
  674. &Obj, &Iosb, SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT);
  675. EndImpersonation();
  676. if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
  677. m->Error = PsxStatusToErrnoPath(&args->NewName);
  678. if (ENOTDIR == m->Error) {
  679. NtClose(FileHandle);
  680. return TRUE;
  681. }
  682. m->Error = 0;
  683. } else if (NT_SUCCESS(Status)) {
  684. NtClose(NewFileHandle);
  685. }
  686. //
  687. // Now we need to get a handle on the root directory of the 'new'
  688. // pathname; we'll pass that in the link information, and the
  689. // rest of the path will be given relative to the root. We
  690. // depend on paths looking like "\DosDevices\X:\path".
  691. //
  692. Status = RtlUnicodeStringToAnsiString(&str_A, &args->NewName, TRUE);
  693. if (!NT_SUCCESS(Status)) {
  694. NtClose(FileHandle);
  695. m->Error = ENOMEM;
  696. return TRUE;
  697. }
  698. // find the root directory
  699. pch = strchr(str_A.Buffer + 1, '\\');
  700. ASSERT((NULL != pch) && ((ULONG)(pch-str_A.Buffer) < str_A.Length));
  701. pch = strchr(pch + 1, '\\');
  702. ASSERT((NULL != pch) && ((ULONG)(pch-str_A.Buffer) < str_A.Length));
  703. ch = pch[1];
  704. pch[1] = '\0';
  705. str_A.Length = (USHORT)strlen(str_A.Buffer);
  706. Status = RtlAnsiStringToUnicodeString(&str_U, &str_A, TRUE);
  707. if (!NT_SUCCESS(Status)) {
  708. RtlFreeAnsiString(&str_A);
  709. NtClose(FileHandle);
  710. m->Error = ENOMEM;
  711. return TRUE;
  712. }
  713. Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
  714. if (NT_SUCCESS(Status)) {
  715. InitializeObjectAttributes(&Obj, &str_U, 0, NULL, NULL);
  716. Status = NtOpenFile(&RootDirHandle, SYNCHRONIZE, &Obj, &Iosb,
  717. SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
  718. EndImpersonation();
  719. }
  720. RtlFreeUnicodeString(&str_U);
  721. if (!NT_SUCCESS(Status)) {
  722. RtlFreeAnsiString(&str_A);
  723. NtClose(FileHandle);
  724. m->Error = PsxStatusToErrno(Status);
  725. return TRUE;
  726. }
  727. //
  728. // Now get the path relative to the root.
  729. //
  730. pch[1] = ch;
  731. rel_A.Buffer = &pch[1];
  732. rel_A.Length = rel_A.MaximumLength = (USHORT)strlen(rel_A.Buffer);
  733. Status = RtlAnsiStringToUnicodeString(&str_U, &rel_A, TRUE);
  734. RtlFreeAnsiString(&str_A);
  735. if (!NT_SUCCESS(Status)) {
  736. NtClose(RootDirHandle);
  737. NtClose(FileHandle);
  738. m->Error = ENOMEM;
  739. return TRUE;
  740. }
  741. pLinkInfo = RtlAllocateHeap(PsxHeap, 0, sizeof(*pLinkInfo) + str_U.Length);
  742. if (NULL == pLinkInfo) {
  743. RtlFreeUnicodeString(&str_U);
  744. NtClose(RootDirHandle);
  745. NtClose(FileHandle);
  746. m->Error = ENOMEM;
  747. return TRUE;
  748. }
  749. RtlMoveMemory(pLinkInfo->FileName, str_U.Buffer, str_U.Length);
  750. pLinkInfo->FileNameLength = str_U.Length;
  751. pLinkInfo->ReplaceIfExists = FALSE;
  752. pLinkInfo->RootDirectory = RootDirHandle;
  753. Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
  754. if (NT_SUCCESS(Status)) {
  755. Status = NtSetInformationFile(FileHandle,
  756. &Iosb,
  757. pLinkInfo,
  758. sizeof(*pLinkInfo) + str_U.Length,
  759. FileLinkInformation);
  760. EndImpersonation();
  761. }
  762. NtClose(RootDirHandle);
  763. RtlFreeHeap(PsxHeap, 0, pLinkInfo);
  764. RtlFreeUnicodeString(&str_U);
  765. if (!NT_SUCCESS(Status)) {
  766. NtClose(FileHandle);
  767. m->Error = PsxStatusToErrno(Status);
  768. return TRUE;
  769. }
  770. Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
  771. if (NT_SUCCESS(Status)) {
  772. Status = NtQueryInformationFile(FileHandle, &Iosb, &SerialNumber,
  773. sizeof(SerialNumber), FileInternalInformation);
  774. EndImpersonation();
  775. }
  776. NtClose(FileHandle);
  777. if (!NT_SUCCESS(Status)) {
  778. m->Error = PsxStatusToErrno(Status);
  779. return TRUE;
  780. }
  781. if (ReferenceOrCreateIoNode(GetFileDeviceNumber(&args->OldName),
  782. (ino_t)SerialNumber.IndexNumber.LowPart, TRUE, &IoNode)) {
  783. // File is open.
  784. NtQuerySystemTime(&Time);
  785. if (!RtlTimeToSecondsSince1970(&Time, &PosixTime)) {
  786. PosixTime = 0;
  787. }
  788. IoNode->ModifyIoNodeTime = PosixTime;
  789. return TRUE;
  790. }
  791. return TRUE;
  792. }
  793. BOOLEAN
  794. DumpFileIfRequired(
  795. IN PPSX_PROCESS p,
  796. IN PPSX_API_MSG m,
  797. IN HANDLE FileHandle,
  798. IN PUNICODE_STRING Path_U
  799. )
  800. {
  801. UNICODE_STRING U;
  802. UNICODE_STRING Dir_U, New_U;
  803. WCHAR wcBuf[100];
  804. BOOLEAN Found;
  805. OBJECT_ATTRIBUTES Obj;
  806. HANDLE DirHandle;
  807. NTSTATUS Status;
  808. IO_STATUS_BLOCK Iosb;
  809. PFILE_RENAME_INFORMATION pRenameInfo;
  810. LARGE_INTEGER Time;
  811. ULONG PosixTime;
  812. CHAR sdbuf[SECURITY_DESCRIPTOR_MIN_LENGTH];
  813. PSECURITY_DESCRIPTOR pSD = (PVOID)sdbuf;
  814. FILE_INTERNAL_INFORMATION SerialNumber;
  815. unsigned char buf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) +
  816. 128*sizeof(WCHAR)];
  817. PFILE_FS_ATTRIBUTE_INFORMATION pFSInfo = (PVOID)buf;
  818. PIONODE IoNode;
  819. FILE_STANDARD_INFORMATION StandardInfo;
  820. ULONG len;
  821. Status = NtQueryInformationFile(FileHandle, &Iosb, &SerialNumber,
  822. sizeof(SerialNumber), FileInternalInformation);
  823. if (!NT_SUCCESS(Status)) {
  824. m->Error = PsxStatusToErrno(Status);
  825. return FALSE;
  826. }
  827. if (!ReferenceOrCreateIoNode(GetFileDeviceNumber(Path_U),
  828. (ino_t)SerialNumber.IndexNumber.LowPart, TRUE, &IoNode)) {
  829. // File is not open.
  830. return FALSE;
  831. }
  832. // File is open.
  833. // If the filesystem is not NTFS, we don't bother with
  834. // this stuff.
  835. Status = NtQueryVolumeInformationFile(FileHandle,
  836. &Iosb, buf, sizeof(buf), FileFsAttributeInformation);
  837. if (!NT_SUCCESS(Status)) {
  838. m->Error = PsxStatusToErrno(Status);
  839. return FALSE;
  840. }
  841. pFSInfo->FileSystemName[pFSInfo->FileSystemNameLength/2] = 0;
  842. if (0 != wcscmp(L"NTFS", pFSInfo->FileSystemName)) {
  843. // Not NTFS.
  844. return FALSE;
  845. }
  846. //
  847. // Create the dump on the root of the filesystem where the
  848. // file is located.
  849. //
  850. U.MaximumLength = PATH_MAX * sizeof(WCHAR);
  851. U.Buffer = RtlAllocateHeap(PsxHeap, HEAP_ZERO_MEMORY,
  852. U.MaximumLength);
  853. if (NULL == U.Buffer) {
  854. m->Error = ENOMEM;
  855. return FALSE;
  856. }
  857. PSX_GET_WCSLEN(L"/DosDevices/X:/",len);
  858. wcsncpy(U.Buffer, Path_U->Buffer, len);
  859. wcscat(U.Buffer, PSX_JUNK_DIR);
  860. U.Length = wcslen(U.Buffer) * sizeof(WCHAR);
  861. Status = InitSecurityDescriptor(pSD, &U, NtCurrentProcess(),
  862. S_IRWXU, pvSDMem);
  863. if (!NT_SUCCESS(Status)) {
  864. KdPrint(("PSXSS: DumpFile: InitSD: 0x%x\n", Status));
  865. m->Error = EPERM;
  866. return FALSE;
  867. }
  868. InitializeObjectAttributes(&Obj, &U, 0, NULL, pSD);
  869. Status = NtCreateFile(
  870. &DirHandle,
  871. SYNCHRONIZE,
  872. &Obj,
  873. &Iosb,
  874. NULL,
  875. FILE_ATTRIBUTE_SYSTEM,
  876. SHARE_ALL,
  877. FILE_OPEN_IF,
  878. FILE_DIRECTORY_FILE,
  879. NULL, 0
  880. );
  881. DeInitSecurityDescriptor(pSD, pvSDMem);
  882. RtlFreeHeap(PsxHeap, 0, (PVOID)U.Buffer);
  883. if (!NT_SUCCESS(Status)) {
  884. KdPrint(("PSXSS: Can't create/open junk dir, 0x%x\n", Status));
  885. m->Error = PsxStatusToErrno(Status);
  886. return FALSE;
  887. }
  888. //
  889. // Get a handle on the file.
  890. //
  891. InitializeObjectAttributes(&Obj, Path_U, 0, NULL, NULL);
  892. Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
  893. if (NT_SUCCESS(Status)) {
  894. // NOTE - overwriting caller supplied file handle here!
  895. Status = NtOpenFile(&FileHandle, SYNCHRONIZE | DELETE,
  896. &Obj, &Iosb, SHARE_ALL,
  897. FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT);
  898. EndImpersonation();
  899. }
  900. if (!NT_SUCCESS(Status)) {
  901. NtClose(DirHandle);
  902. m->Error = PsxStatusToErrno(Status);
  903. return FALSE;
  904. }
  905. //
  906. // add the file to the directory, giving it a new name if there's
  907. // already an existing file with this name.
  908. //
  909. New_U.Buffer = wcBuf;
  910. New_U.MaximumLength = sizeof(wcBuf);
  911. pRenameInfo = RtlAllocateHeap(PsxHeap, 0, sizeof(*pRenameInfo)
  912. + New_U.MaximumLength + 2);
  913. if (NULL == pRenameInfo) {
  914. NtClose(DirHandle);
  915. NtClose(FileHandle);
  916. m->Error = ENOMEM;
  917. return FALSE;
  918. }
  919. NtQuerySystemTime(&Time);
  920. if (!RtlTimeToSecondsSince1970(&Time, &PosixTime)) {
  921. PosixTime = 0;
  922. }
  923. for (;;) {
  924. Status = RtlIntegerToUnicodeString(PosixTime, 16, &New_U);
  925. ASSERT(NT_SUCCESS(Status));
  926. RtlMoveMemory(pRenameInfo->FileName, New_U.Buffer,
  927. New_U.Length);
  928. pRenameInfo->FileNameLength = New_U.Length;
  929. pRenameInfo->ReplaceIfExists = FALSE;
  930. pRenameInfo->RootDirectory = DirHandle;
  931. Status = NtSetInformationFile(FileHandle, &Iosb, pRenameInfo,
  932. sizeof(*pRenameInfo) + New_U.Length,
  933. FileRenameInformation);
  934. if (NT_SUCCESS(Status)) {
  935. RtlFreeHeap(PsxHeap, 0, pRenameInfo);
  936. NtClose(DirHandle);
  937. NtClose(FileHandle);
  938. IoNode->Junked = TRUE;
  939. return TRUE;
  940. }
  941. if (!NT_SUCCESS(Status) &&
  942. STATUS_OBJECT_NAME_COLLISION != Status) {
  943. RtlFreeHeap(PsxHeap, 0, pRenameInfo);
  944. NtClose(DirHandle);
  945. NtClose(FileHandle);
  946. m->Error = PsxStatusToErrno(Status);
  947. return FALSE;
  948. }
  949. // change the filename and start again
  950. ++PosixTime;
  951. }
  952. // NOTREACHED
  953. }