1187 lines
28 KiB
C
1187 lines
28 KiB
C
#include "psxsrv.h"
|
||
#include <sys/stat.h>
|
||
#include "sesport.h"
|
||
|
||
NTSTATUS
|
||
DoAccess(
|
||
HANDLE FileHandle,
|
||
PPSX_PROCESS p,
|
||
mode_t Mode,
|
||
PULONG pError
|
||
);
|
||
|
||
BOOLEAN
|
||
DumpFileIfRequired(
|
||
IN PPSX_PROCESS p,
|
||
IN PPSX_API_MSG m,
|
||
IN HANDLE FileHandle,
|
||
IN PUNICODE_STRING Path_U
|
||
);
|
||
|
||
static BOOLEAN
|
||
IsUserInGroup(
|
||
PPSX_PROCESS p,
|
||
PSID Group
|
||
);
|
||
|
||
static PVOID pvSDMem[10]; // InitSecurityDescriptor -> DeInitSD
|
||
|
||
BOOLEAN
|
||
FilesAreIdentical(
|
||
HANDLE FileA,
|
||
HANDLE FileB
|
||
)
|
||
{
|
||
FILE_INTERNAL_INFORMATION infoA, infoB;
|
||
IO_STATUS_BLOCK Iosb;
|
||
NTSTATUS Status;
|
||
|
||
Status = NtQueryInformationFile(FileA, &Iosb, &infoA,
|
||
sizeof(infoA), FileInternalInformation);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = NtQueryInformationFile(FileB, &Iosb, &infoB,
|
||
sizeof(infoB), FileInternalInformation);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
return infoA.IndexNumber.QuadPart == infoB.IndexNumber.QuadPart;
|
||
}
|
||
|
||
BOOLEAN
|
||
PsxUnlink(
|
||
IN PPSX_PROCESS p,
|
||
IN OUT PPSX_API_MSG m
|
||
)
|
||
{
|
||
PPSX_UNLINK_MSG args;
|
||
HANDLE FileHandle,
|
||
DirHandle;
|
||
UNICODE_STRING
|
||
ParentDir_U; // path to the containing directory
|
||
OBJECT_ATTRIBUTES
|
||
DirObj, // names the parent directory
|
||
FileObj; // names the file to be unlinked
|
||
IO_STATUS_BLOCK Iosb;
|
||
NTSTATUS Status;
|
||
FILE_DISPOSITION_INFORMATION Disp;
|
||
ANSI_STRING Path_A;
|
||
PCHAR pch;
|
||
ULONG Error;
|
||
|
||
args = &m->u.Unlink;
|
||
|
||
if (!ISPOINTERVALID_CLIENT(p, args->Path_U.Buffer,
|
||
args->Path_U.Length)) {
|
||
KdPrint(("Invalid pointer to unlink: %lx, %d\n",
|
||
args->Path_U.Buffer, args->Path_U.Length));
|
||
m->Error = EINVAL;
|
||
return TRUE;
|
||
}
|
||
|
||
Status = RtlUnicodeStringToAnsiString(&Path_A, &args->Path_U, TRUE);
|
||
if (!NT_SUCCESS(Status)) {
|
||
m->Error = ENOMEM;
|
||
return TRUE;
|
||
}
|
||
|
||
if ('\\' == Path_A.Buffer[Path_A.Length - 1]) {
|
||
//
|
||
// Unlink must reject paths that end in slash.
|
||
//
|
||
RtlFreeAnsiString(&Path_A);
|
||
m->Error = EINVAL;
|
||
return TRUE;
|
||
}
|
||
RtlFreeAnsiString(&Path_A);
|
||
|
||
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
InitializeObjectAttributes(&FileObj, &args->Path_U, 0, NULL, NULL);
|
||
Status = NtOpenFile(&FileHandle, DELETE | SYNCHRONIZE, &FileObj,
|
||
&Iosb, SHARE_ALL,
|
||
FILE_SYNCHRONOUS_IO_ALERT | FILE_NON_DIRECTORY_FILE);
|
||
EndImpersonation();
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
if (STATUS_FILE_IS_A_DIRECTORY == Status) {
|
||
m->Error = EPERM;
|
||
return TRUE;
|
||
}
|
||
if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
|
||
m->Error = PsxStatusToErrnoPath(&args->Path_U);
|
||
return TRUE;
|
||
}
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// If some posix process has this file open, we can't delete
|
||
// it or that process would be unable to write to the file (the
|
||
// only operation allowed on a deleted file is "close").
|
||
//
|
||
|
||
if (DumpFileIfRequired(p, m, FileHandle, &args->Path_U)) {
|
||
NtClose(FileHandle);
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// If we didn't dump the file, we'll try to delete it in the
|
||
// obvious way. Should succeed unless some windows process has
|
||
// it open -- we don't worry about that possibility, you get
|
||
// what you get.
|
||
//
|
||
|
||
Disp.DeleteFile = TRUE;
|
||
Status = NtSetInformationFile(FileHandle, &Iosb, &Disp, sizeof(Disp),
|
||
FileDispositionInformation);
|
||
if (!NT_SUCCESS(Status)) {
|
||
KdPrint(("PSXSS: PsxUnlink: SetInfoFile: 0x%x\n", Status));
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return TRUE;
|
||
}
|
||
NtClose(FileHandle);
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
DoAccess(
|
||
HANDLE FileHandle,
|
||
PPSX_PROCESS p,
|
||
mode_t Mode,
|
||
PULONG pError
|
||
)
|
||
{
|
||
FILE_ACCESS_INFORMATION FileAccessInfo;
|
||
BOOLEAN RetVal;
|
||
SECURITY_INFORMATION SecurityInformation;
|
||
PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
|
||
PSID NtOwner, NtGroup;
|
||
BOOLEAN OwnerDefaulted, GroupDefaulted;
|
||
BOOLEAN DaclPresent, DaclDefaulted;
|
||
ACCESS_MASK UserAccess, GroupAccess, OtherAccess;
|
||
ULONG LengthNeeded;
|
||
mode_t filemode;
|
||
PSID UserSid;
|
||
PACL pDacl;
|
||
NTSTATUS Status;
|
||
HANDLE TokenHandle;
|
||
PSID_AND_ATTRIBUTES pSA;
|
||
|
||
//
|
||
// N.B.: this code kind of depends on F_OK being 0.
|
||
//
|
||
|
||
if (F_OK == Mode) {
|
||
//
|
||
// The file exists: succeed.
|
||
//
|
||
return TRUE;
|
||
}
|
||
|
||
Status = NtOpenProcessToken(p->Process, GENERIC_READ, &TokenHandle);
|
||
if (!NT_SUCCESS(Status)) {
|
||
// can fail due to lack of memory
|
||
*pError = ENOMEM;
|
||
return Status;
|
||
}
|
||
|
||
Status = NtQueryInformationToken(TokenHandle, TokenUser, NULL,
|
||
0, &LengthNeeded);
|
||
if (STATUS_BUFFER_TOO_SMALL != Status) {
|
||
NtClose(TokenHandle);
|
||
*pError = PsxStatusToErrno(Status);
|
||
return Status;
|
||
}
|
||
|
||
pSA = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
|
||
if (NULL == pSA) {
|
||
*pError = ENOMEM;
|
||
NtClose(TokenHandle);
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
Status = NtQueryInformationToken(TokenHandle, TokenUser, pSA,
|
||
LengthNeeded, &LengthNeeded);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
UserSid = pSA->Sid;
|
||
|
||
//
|
||
// Get the security descriptor for the file.
|
||
//
|
||
|
||
SecurityInformation = OWNER_SECURITY_INFORMATION |
|
||
GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
|
||
|
||
Status = NtQuerySecurityObject(FileHandle, SecurityInformation,
|
||
NULL, 0, &LengthNeeded);
|
||
if (!(STATUS_BUFFER_TOO_SMALL == Status)) {
|
||
KdPrint(("PSXSS: NtQSObject failed: 0x%x\n", Status));
|
||
}
|
||
ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
|
||
|
||
SecurityDescriptor = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
|
||
if (NULL == SecurityDescriptor) {
|
||
NtClose(FileHandle);
|
||
*pError = ENOMEM;
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
Status = NtQuerySecurityObject(FileHandle, SecurityInformation,
|
||
SecurityDescriptor, LengthNeeded, &LengthNeeded);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
//
|
||
// Get the owner and group from the security descriptor
|
||
//
|
||
|
||
Status = RtlGetOwnerSecurityDescriptor(SecurityDescriptor,
|
||
&NtOwner, &OwnerDefaulted);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = RtlGetGroupSecurityDescriptor(SecurityDescriptor,
|
||
&NtGroup, &GroupDefaulted);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
if (NULL == NtOwner || NULL == NtGroup) {
|
||
//
|
||
// Seems like this file doesn't have an owner or a
|
||
// group, which means that we can't change it's mode.
|
||
//
|
||
NtClose(FileHandle);
|
||
*pError = EPERM;
|
||
return TRUE;
|
||
}
|
||
|
||
Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor,
|
||
&DaclPresent, &pDacl, &DaclDefaulted);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
if (!DaclPresent || NULL == pDacl) {
|
||
//
|
||
// In this case, all access is granted; we don't even look
|
||
// to see what particular access the caller was interested
|
||
// in.
|
||
//
|
||
return TRUE;
|
||
}
|
||
|
||
Status = RtlInterpretPosixAcl(ACL_REVISION2, NtOwner, NtGroup,
|
||
pDacl, &UserAccess, &GroupAccess, &OtherAccess);
|
||
if (!NT_SUCCESS(Status)) {
|
||
//
|
||
// XXX.mjb: Should do something reasonable here.
|
||
//
|
||
return TRUE;
|
||
}
|
||
|
||
filemode = AccessMaskToMode(UserAccess, GroupAccess, OtherAccess);
|
||
|
||
if (RtlEqualSid(UserSid, NtOwner)) {
|
||
if (Mode & (filemode >> 6)) {
|
||
// access is granted.
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
if (IsUserInGroup(p, NtGroup)) {
|
||
if (Mode & (filemode >> 3)) {
|
||
// access is granted.
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
if (Mode & filemode) {
|
||
// access is granted.
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
*pError = EACCES;
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// See if the given group is one that the owner of this process belongs
|
||
// to.
|
||
//
|
||
static BOOLEAN
|
||
IsUserInGroup(
|
||
PPSX_PROCESS p,
|
||
PSID Group
|
||
)
|
||
{
|
||
HANDLE TokenHandle;
|
||
TOKEN_GROUPS *pGroups;
|
||
ULONG LengthNeeded;
|
||
NTSTATUS Status;
|
||
BOOLEAN RetVal = FALSE;
|
||
ULONG i;
|
||
|
||
Status = NtOpenProcessToken(p->Process, GENERIC_READ, &TokenHandle);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = NtQueryInformationToken(TokenHandle, TokenGroups, NULL,
|
||
0, &LengthNeeded);
|
||
ASSERT(STATUS_BUFFER_TOO_SMALL == Status);
|
||
|
||
pGroups = RtlAllocateHeap(PsxHeap, 0, LengthNeeded);
|
||
if (NULL == pGroups) {
|
||
NtClose(TokenHandle);
|
||
return FALSE;
|
||
}
|
||
|
||
Status = NtQueryInformationToken(TokenHandle, TokenGroups, pGroups,
|
||
LengthNeeded, &LengthNeeded);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
for (i = 0; i < pGroups->GroupCount; ++i) {
|
||
if (RtlEqualSid(pGroups->Groups[i].Sid, Group)) {
|
||
RetVal = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
RtlFreeHeap(PsxHeap, 0, pGroups);
|
||
NtClose(TokenHandle);
|
||
return RetVal;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
PsxRmDir(
|
||
IN PPSX_PROCESS p,
|
||
IN PPSX_API_MSG m
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure implements POSIX rmdir.
|
||
|
||
Arguments:
|
||
|
||
p - Supplies the address of the process making the call.
|
||
|
||
m - Supplies the address of the message associated with the rmdir request.
|
||
|
||
Return Value:
|
||
|
||
TRUE - The contents of *m should be used to generate a reply.
|
||
|
||
--*/
|
||
{
|
||
PPSX_RMDIR_MSG args;
|
||
HANDLE FileHandle, ParentHandle;
|
||
NTSTATUS Status;
|
||
IO_STATUS_BLOCK Iosb;
|
||
OBJECT_ATTRIBUTES
|
||
ParentObj,
|
||
DirObj;
|
||
UNICODE_STRING
|
||
ParentDir_U,
|
||
Path_U;
|
||
FILE_DISPOSITION_INFORMATION DispositionInfo;
|
||
FILE_INTERNAL_INFORMATION SerialNumber;
|
||
PIONODE IoNode;
|
||
UCHAR buf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) +
|
||
128 * sizeof(WCHAR)];
|
||
PFILE_FS_ATTRIBUTE_INFORMATION pFSInfo = (PVOID)buf;
|
||
ANSI_STRING Path_A;
|
||
PCHAR pch;
|
||
|
||
args = &m->u.RmDir;
|
||
|
||
if (!ISPOINTERVALID_CLIENT(p,args->Path_U.Buffer,args->Path_U.Length)) {
|
||
KdPrint(("Invalid pointer to rmdir %lx \n",
|
||
args->Path_U.Buffer));
|
||
m->Error = EINVAL;
|
||
return TRUE;
|
||
}
|
||
|
||
Path_U = args->Path_U;
|
||
|
||
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
InitializeObjectAttributes(&DirObj, &Path_U, 0, NULL, NULL);
|
||
|
||
Status = NtOpenFile(&FileHandle, SYNCHRONIZE | DELETE,
|
||
&DirObj, &Iosb,
|
||
SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
|
||
EndImpersonation();
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
|
||
m->Error = PsxStatusToErrnoPath(&Path_U);
|
||
return TRUE;
|
||
}
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// If some posix process has this dir open, we can't delete
|
||
// it or that process would be unable to read from the dir (the
|
||
// only operation allowed on a deleted dir is "close".
|
||
//
|
||
|
||
//
|
||
// XXX.mjb: must check to make sure the directory contains no files,
|
||
// and return ENOTEMPTY or EEXIST if so.
|
||
//
|
||
|
||
if (DumpFileIfRequired(p, m, FileHandle, &Path_U)) {
|
||
NtClose(FileHandle);
|
||
return TRUE;
|
||
}
|
||
|
||
m->Error = 0;
|
||
|
||
DispositionInfo.DeleteFile = TRUE;
|
||
|
||
Status = NtSetInformationFile(FileHandle, &Iosb,
|
||
&DispositionInfo, sizeof(DispositionInfo),
|
||
FileDispositionInformation);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
if (STATUS_CANNOT_DELETE == Status) {
|
||
|
||
//
|
||
// 1003.1-90 (5.5.2.2) : If the named directory is the root
|
||
// directory or the current working directory of any process, it
|
||
// is unspecified whether the function succeeds or whether it fails
|
||
// and sets /errno/ to [EBUSY].
|
||
//
|
||
// NT is going to fail with STATUS_CANNOT_DELETE here, and so we
|
||
// want posix to return EBUSY. (The standard posix mapping is to
|
||
// ETXTBUSY.)
|
||
//
|
||
|
||
m->Error = EBUSY;
|
||
|
||
} else {
|
||
m->Error = PsxStatusToErrno(Status);
|
||
}
|
||
|
||
}
|
||
|
||
Status = NtClose(FileHandle);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
PsxAccess(
|
||
IN PPSX_PROCESS p,
|
||
IN PPSX_API_MSG m
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure implements POSIX access.
|
||
|
||
Arguments:
|
||
|
||
p - Supplies the address of the process making the call.
|
||
|
||
m - Supplies the address of the message associated with the access request.
|
||
|
||
Return Value:
|
||
|
||
TRUE - The contents of *m should be used to generate a reply.
|
||
|
||
--*/
|
||
{
|
||
PPSX_ACCESS_MSG args;
|
||
HANDLE FileHandle;
|
||
NTSTATUS Status;
|
||
IO_STATUS_BLOCK Iosb;
|
||
OBJECT_ATTRIBUTES ObjA;
|
||
UNICODE_STRING Path_U;
|
||
|
||
args = &m->u.Access;
|
||
|
||
if (!ISPOINTERVALID_CLIENT(p,args->Path_U.Buffer,args->Path_U.Length)) {
|
||
KdPrint(("Invalid pointer to access: %lx\n",
|
||
args->Path_U.Buffer));
|
||
m->Error = EINVAL;
|
||
return TRUE;
|
||
}
|
||
|
||
Path_U = args->Path_U;
|
||
|
||
InitializeObjectAttributes(&ObjA, &Path_U, 0, NULL, NULL);
|
||
|
||
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
Status = NtOpenFile(&FileHandle,
|
||
SYNCHRONIZE | READ_CONTROL | FILE_READ_ATTRIBUTES,
|
||
&ObjA, &Iosb,
|
||
SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT);
|
||
EndImpersonation();
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
|
||
m->Error = PsxStatusToErrnoPath(&Path_U);
|
||
return TRUE;
|
||
}
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return TRUE;
|
||
}
|
||
|
||
(void)DoAccess(FileHandle, p, args->Amode, &m->Error);
|
||
|
||
NtClose(FileHandle);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
PsxRename(
|
||
IN PPSX_PROCESS p,
|
||
IN PPSX_API_MSG m
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure implements POSIX rename. The routine must
|
||
be implemented on the server because if we're renaming onto
|
||
an open file, we need to move it to the dump first.
|
||
|
||
Arguments:
|
||
|
||
p - Supplies the address of the process making the call.
|
||
|
||
m - Supplies the address of the message associated with the access request.
|
||
|
||
Return Value:
|
||
|
||
TRUE - The contents of *m should be used to generate a reply.
|
||
|
||
--*/
|
||
{
|
||
PPSX_RENAME_MSG args;
|
||
HANDLE FileHandle, NewFileHandle, RootDirHandle;
|
||
NTSTATUS Status;
|
||
IO_STATUS_BLOCK Iosb;
|
||
OBJECT_ATTRIBUTES Obj;
|
||
ANSI_STRING str_A, rel_A;
|
||
UNICODE_STRING str_U, new_U;
|
||
PFILE_RENAME_INFORMATION pRenameInfo;
|
||
char *pch, ch;
|
||
|
||
FILE_INTERNAL_INFORMATION SerialNumber;
|
||
unsigned char buf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) +
|
||
128*sizeof(WCHAR)];
|
||
PFILE_FS_ATTRIBUTE_INFORMATION pFSInfo = (PVOID)buf;
|
||
PIONODE IoNode;
|
||
|
||
args = &m->u.Rename;
|
||
|
||
//
|
||
// Open the old pathname
|
||
//
|
||
|
||
InitializeObjectAttributes(&Obj, &args->OldName, 0, NULL, NULL);
|
||
|
||
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
Status = NtOpenFile(&FileHandle, SYNCHRONIZE | DELETE,
|
||
&Obj, &Iosb, SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT);
|
||
|
||
EndImpersonation();
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
|
||
m->Error = PsxStatusToErrnoPath(&args->OldName);
|
||
return TRUE;
|
||
}
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Now we need to get a handle on the root directory of the 'new'
|
||
// path; we'll pass that in the rename information, and the rest
|
||
// of the path will be given relative to that root. We depend
|
||
// on paths looking like "\DosDevices\X:\path".
|
||
//
|
||
|
||
//
|
||
// Open the new filename for error checking, then close. We
|
||
// also take this opportunity to find the ENOTDIR error case.
|
||
//
|
||
|
||
InitializeObjectAttributes(&Obj, &args->NewName, 0, NULL, NULL);
|
||
|
||
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
Status = NtOpenFile(&NewFileHandle, SYNCHRONIZE,
|
||
&Obj, &Iosb, SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT);
|
||
|
||
EndImpersonation();
|
||
}
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
if (FilesAreIdentical(FileHandle, NewFileHandle)) {
|
||
//
|
||
// 1003.1-90 (5.5.3.1): If the old argument and the
|
||
// new argument both refer to links to the same
|
||
// existing file, the rename function shall return
|
||
// successfully and perform no other action.
|
||
//
|
||
|
||
NtClose(FileHandle);
|
||
NtClose(NewFileHandle);
|
||
m->ReturnValue = 0;
|
||
return TRUE;
|
||
}
|
||
|
||
DumpFileIfRequired(p, m, NewFileHandle, &args->NewName);
|
||
m->Error = 0;
|
||
NtClose(NewFileHandle);
|
||
} else if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
|
||
m->Error = PsxStatusToErrnoPath(&args->NewName);
|
||
if (ENOTDIR == m->Error) {
|
||
NtClose(FileHandle);
|
||
return TRUE;
|
||
}
|
||
m->Error = 0;
|
||
} else {
|
||
NtClose(FileHandle);
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return TRUE;
|
||
}
|
||
|
||
Status = RtlUnicodeStringToAnsiString(&str_A, &args->NewName, TRUE);
|
||
if (!NT_SUCCESS(Status)) {
|
||
NtClose(FileHandle);
|
||
m->Error = ENOMEM;
|
||
return TRUE;
|
||
}
|
||
|
||
// find the root directory
|
||
|
||
pch = strchr(str_A.Buffer + 1, '\\');
|
||
ASSERT(NULL != pch);
|
||
pch = strchr(pch + 1, '\\');
|
||
ASSERT(NULL != pch);
|
||
ch = pch[1];
|
||
pch[1] = '\000';
|
||
str_A.Length = (USHORT)strlen(str_A.Buffer);
|
||
|
||
Status = RtlAnsiStringToUnicodeString(&str_U, &str_A, TRUE);
|
||
if (!NT_SUCCESS(Status)) {
|
||
RtlFreeAnsiString(&str_A);
|
||
NtClose(FileHandle);
|
||
m->Error = ENOMEM;
|
||
return TRUE;
|
||
}
|
||
|
||
InitializeObjectAttributes(&Obj, &str_U, 0, NULL, NULL);
|
||
|
||
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
Status = NtOpenFile(&RootDirHandle, SYNCHRONIZE, &Obj, &Iosb,
|
||
SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
|
||
|
||
EndImpersonation();
|
||
}
|
||
|
||
RtlFreeUnicodeString(&str_U);
|
||
if (!NT_SUCCESS(Status)) {
|
||
RtlFreeAnsiString(&str_A);
|
||
NtClose(FileHandle);
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Now get the path relative to the root.
|
||
//
|
||
|
||
pch[1] = ch;
|
||
rel_A.Buffer = &pch[1];
|
||
rel_A.Length = rel_A.MaximumLength = (USHORT)strlen(rel_A.Buffer);
|
||
Status = RtlAnsiStringToUnicodeString(&str_U, &rel_A, TRUE);
|
||
RtlFreeAnsiString(&str_A);
|
||
if (!NT_SUCCESS(Status)) {
|
||
NtClose(RootDirHandle);
|
||
NtClose(FileHandle);
|
||
m->Error = ENOMEM;
|
||
return TRUE;
|
||
}
|
||
|
||
pRenameInfo = RtlAllocateHeap(PsxHeap, 0,
|
||
sizeof(*pRenameInfo) + str_U.Length);
|
||
if (NULL == pRenameInfo) {
|
||
RtlFreeUnicodeString(&str_U);
|
||
NtClose(RootDirHandle);
|
||
NtClose(FileHandle);
|
||
m->Error = ENOMEM;
|
||
return TRUE;
|
||
}
|
||
|
||
RtlMoveMemory(pRenameInfo->FileName, str_U.Buffer, str_U.Length);
|
||
pRenameInfo->FileNameLength = str_U.Length;
|
||
|
||
pRenameInfo->ReplaceIfExists = TRUE;
|
||
pRenameInfo->RootDirectory = RootDirHandle;
|
||
|
||
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
Status = NtSetInformationFile(FileHandle, &Iosb, pRenameInfo,
|
||
sizeof(*pRenameInfo) + str_U.Length,
|
||
FileRenameInformation);
|
||
EndImpersonation();
|
||
}
|
||
|
||
RtlFreeUnicodeString(&str_U);
|
||
|
||
NtClose(FileHandle);
|
||
NtClose(RootDirHandle);
|
||
RtlFreeHeap(PsxHeap, 0, pRenameInfo);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
m->Error = PsxStatusToErrno(Status);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOLEAN
|
||
PsxLink(
|
||
IN PPSX_PROCESS p,
|
||
IN PPSX_API_MSG m
|
||
)
|
||
{
|
||
PPSX_LINK_MSG args;
|
||
HANDLE FileHandle, NewFileHandle, RootDirHandle;
|
||
NTSTATUS Status;
|
||
IO_STATUS_BLOCK Iosb;
|
||
OBJECT_ATTRIBUTES Obj;
|
||
PFILE_LINK_INFORMATION pLinkInfo;
|
||
FILE_INTERNAL_INFORMATION SerialNumber;
|
||
LARGE_INTEGER Time;
|
||
ULONG PosixTime;
|
||
PIONODE IoNode;
|
||
UNICODE_STRING str_U, new_U;
|
||
ANSI_STRING str_A, rel_A;
|
||
char *pch, ch;
|
||
|
||
args = &m->u.Link;
|
||
|
||
//
|
||
// Open the existing pathname.
|
||
//
|
||
|
||
InitializeObjectAttributes(&Obj, &args->OldName, 0, NULL, NULL);
|
||
|
||
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
Status = NtOpenFile(&FileHandle, SYNCHRONIZE, &Obj, &Iosb,
|
||
SHARE_ALL,
|
||
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
||
EndImpersonation();
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
if (STATUS_FILE_IS_A_DIRECTORY == Status) {
|
||
m->Error = EPERM;
|
||
return TRUE;
|
||
}
|
||
if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
|
||
m->Error = PsxStatusToErrnoPath(&args->OldName);
|
||
return TRUE;
|
||
}
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Open new filename for error checking, then close. This
|
||
// is to find the ENOTDIR error case.
|
||
//
|
||
|
||
InitializeObjectAttributes(&Obj, &args->NewName, 0, NULL, NULL);
|
||
|
||
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
NtClose(FileHandle);
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return TRUE;
|
||
}
|
||
|
||
Status = NtOpenFile(&NewFileHandle, SYNCHRONIZE,
|
||
&Obj, &Iosb, SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT);
|
||
EndImpersonation();
|
||
|
||
if (STATUS_OBJECT_PATH_NOT_FOUND == Status) {
|
||
m->Error = PsxStatusToErrnoPath(&args->NewName);
|
||
if (ENOTDIR == m->Error) {
|
||
NtClose(FileHandle);
|
||
return TRUE;
|
||
}
|
||
m->Error = 0;
|
||
} else if (NT_SUCCESS(Status)) {
|
||
NtClose(NewFileHandle);
|
||
}
|
||
|
||
//
|
||
// Now we need to get a handle on the root directory of the 'new'
|
||
// pathname; we'll pass that in the link information, and the
|
||
// rest of the path will be given relative to the root. We
|
||
// depend on paths looking like "\DosDevices\X:\path".
|
||
//
|
||
|
||
Status = RtlUnicodeStringToAnsiString(&str_A, &args->NewName, TRUE);
|
||
if (!NT_SUCCESS(Status)) {
|
||
NtClose(FileHandle);
|
||
m->Error = ENOMEM;
|
||
return TRUE;
|
||
}
|
||
|
||
// find the root directory
|
||
|
||
pch = strchr(str_A.Buffer + 1, '\\');
|
||
ASSERT((NULL != pch) && ((ULONG)(pch-str_A.Buffer) < str_A.Length));
|
||
pch = strchr(pch + 1, '\\');
|
||
ASSERT((NULL != pch) && ((ULONG)(pch-str_A.Buffer) < str_A.Length));
|
||
ch = pch[1];
|
||
pch[1] = '\0';
|
||
str_A.Length = (USHORT)strlen(str_A.Buffer);
|
||
|
||
Status = RtlAnsiStringToUnicodeString(&str_U, &str_A, TRUE);
|
||
if (!NT_SUCCESS(Status)) {
|
||
RtlFreeAnsiString(&str_A);
|
||
NtClose(FileHandle);
|
||
m->Error = ENOMEM;
|
||
return TRUE;
|
||
}
|
||
|
||
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
InitializeObjectAttributes(&Obj, &str_U, 0, NULL, NULL);
|
||
Status = NtOpenFile(&RootDirHandle, SYNCHRONIZE, &Obj, &Iosb,
|
||
SHARE_ALL, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
|
||
EndImpersonation();
|
||
}
|
||
|
||
RtlFreeUnicodeString(&str_U);
|
||
if (!NT_SUCCESS(Status)) {
|
||
RtlFreeAnsiString(&str_A);
|
||
NtClose(FileHandle);
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Now get the path relative to the root.
|
||
//
|
||
|
||
pch[1] = ch;
|
||
rel_A.Buffer = &pch[1];
|
||
rel_A.Length = rel_A.MaximumLength = (USHORT)strlen(rel_A.Buffer);
|
||
Status = RtlAnsiStringToUnicodeString(&str_U, &rel_A, TRUE);
|
||
RtlFreeAnsiString(&str_A);
|
||
if (!NT_SUCCESS(Status)) {
|
||
NtClose(RootDirHandle);
|
||
NtClose(FileHandle);
|
||
m->Error = ENOMEM;
|
||
return TRUE;
|
||
}
|
||
|
||
pLinkInfo = RtlAllocateHeap(PsxHeap, 0, sizeof(*pLinkInfo) + str_U.Length);
|
||
if (NULL == pLinkInfo) {
|
||
RtlFreeUnicodeString(&str_U);
|
||
NtClose(RootDirHandle);
|
||
NtClose(FileHandle);
|
||
m->Error = ENOMEM;
|
||
return TRUE;
|
||
}
|
||
|
||
RtlMoveMemory(pLinkInfo->FileName, str_U.Buffer, str_U.Length);
|
||
pLinkInfo->FileNameLength = str_U.Length;
|
||
|
||
pLinkInfo->ReplaceIfExists = FALSE;
|
||
pLinkInfo->RootDirectory = RootDirHandle;
|
||
|
||
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
Status = NtSetInformationFile(FileHandle,
|
||
&Iosb,
|
||
pLinkInfo,
|
||
sizeof(*pLinkInfo) + str_U.Length,
|
||
FileLinkInformation);
|
||
EndImpersonation();
|
||
}
|
||
|
||
NtClose(RootDirHandle);
|
||
RtlFreeHeap(PsxHeap, 0, pLinkInfo);
|
||
RtlFreeUnicodeString(&str_U);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
NtClose(FileHandle);
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return TRUE;
|
||
}
|
||
|
||
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
Status = NtQueryInformationFile(FileHandle, &Iosb, &SerialNumber,
|
||
sizeof(SerialNumber), FileInternalInformation);
|
||
EndImpersonation();
|
||
}
|
||
|
||
NtClose(FileHandle);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return TRUE;
|
||
}
|
||
|
||
if (ReferenceOrCreateIoNode(GetFileDeviceNumber(&args->OldName),
|
||
(ino_t)SerialNumber.IndexNumber.LowPart, TRUE, &IoNode)) {
|
||
// File is open.
|
||
|
||
NtQuerySystemTime(&Time);
|
||
if (!RtlTimeToSecondsSince1970(&Time, &PosixTime)) {
|
||
PosixTime = 0;
|
||
}
|
||
|
||
IoNode->ModifyIoNodeTime = PosixTime;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOLEAN
|
||
DumpFileIfRequired(
|
||
IN PPSX_PROCESS p,
|
||
IN PPSX_API_MSG m,
|
||
IN HANDLE FileHandle,
|
||
IN PUNICODE_STRING Path_U
|
||
)
|
||
{
|
||
UNICODE_STRING U;
|
||
UNICODE_STRING Dir_U, New_U;
|
||
WCHAR wcBuf[100];
|
||
BOOLEAN Found;
|
||
OBJECT_ATTRIBUTES Obj;
|
||
HANDLE DirHandle;
|
||
NTSTATUS Status;
|
||
IO_STATUS_BLOCK Iosb;
|
||
PFILE_RENAME_INFORMATION pRenameInfo;
|
||
LARGE_INTEGER Time;
|
||
ULONG PosixTime;
|
||
CHAR sdbuf[SECURITY_DESCRIPTOR_MIN_LENGTH];
|
||
PSECURITY_DESCRIPTOR pSD = (PVOID)sdbuf;
|
||
FILE_INTERNAL_INFORMATION SerialNumber;
|
||
unsigned char buf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) +
|
||
128*sizeof(WCHAR)];
|
||
PFILE_FS_ATTRIBUTE_INFORMATION pFSInfo = (PVOID)buf;
|
||
PIONODE IoNode;
|
||
FILE_STANDARD_INFORMATION StandardInfo;
|
||
ULONG len;
|
||
|
||
Status = NtQueryInformationFile(FileHandle, &Iosb, &SerialNumber,
|
||
sizeof(SerialNumber), FileInternalInformation);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return FALSE;
|
||
}
|
||
|
||
if (!ReferenceOrCreateIoNode(GetFileDeviceNumber(Path_U),
|
||
(ino_t)SerialNumber.IndexNumber.LowPart, TRUE, &IoNode)) {
|
||
|
||
// File is not open.
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
// File is open.
|
||
|
||
// If the filesystem is not NTFS, we don't bother with
|
||
// this stuff.
|
||
|
||
Status = NtQueryVolumeInformationFile(FileHandle,
|
||
&Iosb, buf, sizeof(buf), FileFsAttributeInformation);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
pFSInfo->FileSystemName[pFSInfo->FileSystemNameLength/2] = 0;
|
||
if (0 != wcscmp(L"NTFS", pFSInfo->FileSystemName)) {
|
||
// Not NTFS.
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Create the dump on the root of the filesystem where the
|
||
// file is located.
|
||
//
|
||
|
||
U.MaximumLength = PATH_MAX * sizeof(WCHAR);
|
||
U.Buffer = RtlAllocateHeap(PsxHeap, HEAP_ZERO_MEMORY,
|
||
U.MaximumLength);
|
||
if (NULL == U.Buffer) {
|
||
m->Error = ENOMEM;
|
||
return FALSE;
|
||
}
|
||
|
||
PSX_GET_WCSLEN(L"/DosDevices/X:/",len);
|
||
|
||
wcsncpy(U.Buffer, Path_U->Buffer, len);
|
||
wcscat(U.Buffer, PSX_JUNK_DIR);
|
||
|
||
U.Length = wcslen(U.Buffer) * sizeof(WCHAR);
|
||
|
||
Status = InitSecurityDescriptor(pSD, &U, NtCurrentProcess(),
|
||
S_IRWXU, pvSDMem);
|
||
if (!NT_SUCCESS(Status)) {
|
||
KdPrint(("PSXSS: DumpFile: InitSD: 0x%x\n", Status));
|
||
m->Error = EPERM;
|
||
return FALSE;
|
||
}
|
||
|
||
InitializeObjectAttributes(&Obj, &U, 0, NULL, pSD);
|
||
|
||
Status = NtCreateFile(
|
||
&DirHandle,
|
||
SYNCHRONIZE,
|
||
&Obj,
|
||
&Iosb,
|
||
NULL,
|
||
FILE_ATTRIBUTE_SYSTEM,
|
||
SHARE_ALL,
|
||
FILE_OPEN_IF,
|
||
FILE_DIRECTORY_FILE,
|
||
NULL, 0
|
||
);
|
||
|
||
DeInitSecurityDescriptor(pSD, pvSDMem);
|
||
|
||
RtlFreeHeap(PsxHeap, 0, (PVOID)U.Buffer);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
KdPrint(("PSXSS: Can't create/open junk dir, 0x%x\n", Status));
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Get a handle on the file.
|
||
//
|
||
|
||
InitializeObjectAttributes(&Obj, Path_U, 0, NULL, NULL);
|
||
|
||
Status = NtImpersonateClientOfPort(p->ClientPort, (PPORT_MESSAGE)m);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
// NOTE - overwriting caller supplied file handle here!
|
||
Status = NtOpenFile(&FileHandle, SYNCHRONIZE | DELETE,
|
||
&Obj, &Iosb, SHARE_ALL,
|
||
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT);
|
||
EndImpersonation();
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
NtClose(DirHandle);
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// add the file to the directory, giving it a new name if there's
|
||
// already an existing file with this name.
|
||
//
|
||
|
||
New_U.Buffer = wcBuf;
|
||
New_U.MaximumLength = sizeof(wcBuf);
|
||
|
||
pRenameInfo = RtlAllocateHeap(PsxHeap, 0, sizeof(*pRenameInfo)
|
||
+ New_U.MaximumLength + 2);
|
||
|
||
if (NULL == pRenameInfo) {
|
||
NtClose(DirHandle);
|
||
NtClose(FileHandle);
|
||
m->Error = ENOMEM;
|
||
return FALSE;
|
||
}
|
||
|
||
NtQuerySystemTime(&Time);
|
||
if (!RtlTimeToSecondsSince1970(&Time, &PosixTime)) {
|
||
PosixTime = 0;
|
||
}
|
||
|
||
for (;;) {
|
||
Status = RtlIntegerToUnicodeString(PosixTime, 16, &New_U);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
RtlMoveMemory(pRenameInfo->FileName, New_U.Buffer,
|
||
New_U.Length);
|
||
pRenameInfo->FileNameLength = New_U.Length;
|
||
pRenameInfo->ReplaceIfExists = FALSE;
|
||
pRenameInfo->RootDirectory = DirHandle;
|
||
|
||
Status = NtSetInformationFile(FileHandle, &Iosb, pRenameInfo,
|
||
sizeof(*pRenameInfo) + New_U.Length,
|
||
FileRenameInformation);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
RtlFreeHeap(PsxHeap, 0, pRenameInfo);
|
||
NtClose(DirHandle);
|
||
NtClose(FileHandle);
|
||
|
||
IoNode->Junked = TRUE;
|
||
|
||
return TRUE;
|
||
}
|
||
if (!NT_SUCCESS(Status) &&
|
||
STATUS_OBJECT_NAME_COLLISION != Status) {
|
||
|
||
RtlFreeHeap(PsxHeap, 0, pRenameInfo);
|
||
NtClose(DirHandle);
|
||
NtClose(FileHandle);
|
||
m->Error = PsxStatusToErrno(Status);
|
||
return FALSE;
|
||
}
|
||
|
||
// change the filename and start again
|
||
|
||
++PosixTime;
|
||
}
|
||
|
||
// NOTREACHED
|
||
}
|