/* Editor Settings: expandtabs and use 4 spaces for indentation
* ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
* -*- mode: c, c-basic-offset: 4 -*- */
/*
* Copyright Likewise Software
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details. You should have received a copy of the GNU General
* Public License along with this program. If not, see
* .
*
* LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
* TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
* WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
* TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
* GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU
* HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
* TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
* license@likewisesoftware.com
*/
/*
* Copyright (C) Likewise Software. All rights reserved.
*
* Module Name:
*
* create.c
*
* Abstract:
*
* Likewise Posix File System Driver (PVFS)
*
* Create Dispatch Routine
*
* Authors: Gerald Carter
*/
#include "pvfs.h"
/*****************************************************************************
Main entry to the Create() routine for driver. Splits work based on the
CreateOptions.
****************************************************************************/
NTSTATUS
PvfsCreate(
PPVFS_IRP_CONTEXT pIrpContext
)
{
NTSTATUS ntError = STATUS_UNSUCCESSFUL;
FILE_CREATE_OPTIONS CreateOptions = 0;
BOOLEAN bIsDirectory = FALSE;
PIRP pIrp = pIrpContext->pIrp;
PSTR pszFilename = NULL;
PSTR pszDiskFilename = NULL;
PVFS_STAT Stat = {0};
FILE_CREATE_OPTIONS FileDirCombo = (FILE_DIRECTORY_FILE|
FILE_NON_DIRECTORY_FILE);
/* Check to see if this is a Device Create() */
if (*pIrp->Args.Create.FileName.FileName == '\0')
{
ntError = PvfsCreateDevice(pIrpContext);
BAIL_ON_NT_STATUS(ntError);
goto cleanup;
}
/* Regular File/Directory Create() */
CreateOptions = pIrp->Args.Create.CreateOptions;
if ((CreateOptions & FileDirCombo) == FileDirCombo)
{
ntError = STATUS_INVALID_PARAMETER;
BAIL_ON_NT_STATUS(ntError);
}
if (CreateOptions & FILE_DIRECTORY_FILE)
{
bIsDirectory = TRUE;
}
else if (CreateOptions & FILE_NON_DIRECTORY_FILE)
{
bIsDirectory = FALSE;
}
else
{
/* stat() the path and find out if this is a file or directory */
ntError = PvfsCanonicalPathName(
&pszFilename,
pIrp->Args.Create.FileName);
BAIL_ON_NT_STATUS(ntError);
ntError = PvfsLookupPath(&pszDiskFilename, pszFilename, FALSE);
/* The path lookup may fail which is ok. We'll catch whether
or not this is a real error later on */
if (ntError == STATUS_SUCCESS)
{
ntError = PvfsSysStat(pszDiskFilename, &Stat);
bIsDirectory = (ntError == STATUS_SUCCESS) ?
S_ISDIR(Stat.s_mode) : FALSE;
}
}
if (bIsDirectory)
{
pIrp->Args.Create.CreateOptions |= FILE_DIRECTORY_FILE;
ntError = PvfsCreateDirectory(pIrpContext);
}
/* File branch */
else
{
pIrp->Args.Create.CreateOptions |= FILE_NON_DIRECTORY_FILE;
ntError = PvfsCreateFile(pIrpContext);
}
BAIL_ON_NT_STATUS(ntError);
cleanup:
RtlCStringFree(&pszFilename);
RtlCStringFree(&pszDiskFilename);
return ntError;
error:
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
NTSTATUS
PvfsCreateFileDoSysOpen(
IN PVOID pContext
)
{
NTSTATUS ntError = STATUS_UNSUCCESSFUL;
PPVFS_PENDING_CREATE pCreateContext = (PPVFS_PENDING_CREATE)pContext;
PIRP pIrp = pCreateContext->pIrpContext->pIrp;
IRP_ARGS_CREATE Args = pIrp->Args.Create;
int fd = -1;
int unixFlags = 0;
PIO_CREATE_SECURITY_CONTEXT pSecCtx = Args.SecurityContext;
FILE_CREATE_RESULT CreateResult = 0;
PIO_SECURITY_CONTEXT_PROCESS_INFORMATION pProcess = NULL;
ntError = PvfsEnforceShareMode(
pCreateContext->pFcb,
Args.ShareAccess,
pCreateContext->GrantedAccess);
BAIL_ON_NT_STATUS(ntError);
/* Do the open() */
ntError = MapPosixOpenFlags(&unixFlags, pCreateContext->GrantedAccess, Args);
BAIL_ON_NT_STATUS(ntError);
ntError = PvfsSysOpen(&fd, pCreateContext->pszDiskFilename, unixFlags, 0700);
BAIL_ON_NT_STATUS(ntError);
/* Perform preallocation is requested */
if (Args.AllocationSize > 0)
{
BOOLEAN bAllocate = FALSE;
switch (Args.CreateDisposition)
{
case FILE_SUPERSEDE:
case FILE_CREATE:
case FILE_OVERWRITE:
case FILE_OVERWRITE_IF:
bAllocate = TRUE;
break;
case FILE_OPEN_IF:
if (!pCreateContext->bFileExisted)
{
bAllocate = TRUE;
}
break;
}
if (bAllocate)
{
ntError = PvfsSysFtruncate(fd, (off_t)Args.AllocationSize);
BAIL_ON_NT_STATUS(ntError);
}
}
/* Save our state */
pCreateContext->pCcb->fd = fd;
pCreateContext->pCcb->ShareFlags = Args.ShareAccess;
pCreateContext->pCcb->AccessGranted = pCreateContext->GrantedAccess;
pCreateContext->pCcb->CreateOptions = Args.CreateOptions;
pCreateContext->pCcb->pszFilename = pCreateContext->pszDiskFilename;
pCreateContext->pszDiskFilename = NULL;
ntError = PvfsAddCCBToFCB(pCreateContext->pFcb, pCreateContext->pCcb);
BAIL_ON_NT_STATUS(ntError);
ntError = PvfsSaveFileDeviceInfo(pCreateContext->pCcb);
BAIL_ON_NT_STATUS(ntError);
/* CCB is now complete */
if ((pCreateContext->SetPropertyFlags & PVFS_SET_PROP_SECURITY) && pSecCtx)
{
/* Unix Security */
pProcess = IoSecurityGetProcessInfo(pSecCtx);
ntError = PvfsSysChown(
pCreateContext->pCcb,
pProcess->Uid,
pProcess->Gid);
BAIL_ON_NT_STATUS(ntError);
/* Security Descriptor */
ntError = PvfsCreateFileSecurity(
pCreateContext->pCcb->pUserToken,
pCreateContext->pCcb,
Args.SecurityDescriptor,
FALSE);
BAIL_ON_NT_STATUS(ntError);
}
if ((pCreateContext->SetPropertyFlags & PVFS_SET_PROP_ATTRIB) &&
(Args.FileAttributes != 0))
{
ntError = PvfsSetFileAttributes(
pCreateContext->pCcb,
Args.FileAttributes);
BAIL_ON_NT_STATUS(ntError);
}
/* Save the delete-on-close flag to the FCB */
if (Args.CreateOptions & FILE_DELETE_ON_CLOSE)
{
pCreateContext->pCcb->bPendingDeleteHandle = TRUE;
}
ntError = PvfsStoreCCB(pIrp->FileHandle, pCreateContext->pCcb);
BAIL_ON_NT_STATUS(ntError);
CreateResult = PvfsSetCreateResult(
Args.CreateDisposition,
pCreateContext->bFileExisted,
STATUS_SUCCESS);
if (CreateResult == FILE_CREATED)
{
PvfsNotifyScheduleFullReport(
pCreateContext->pCcb->pFcb,
FILE_NOTIFY_CHANGE_FILE_NAME,
FILE_ACTION_ADDED,
pCreateContext->pCcb->pszFilename);
}
/* The CCB has been handled off to the FileHandle so make sure
we don't think we still own it */
pCreateContext->pCcb = NULL;
cleanup:
pIrp->IoStatusBlock.CreateResult = CreateResult;
return ntError;
error:
CreateResult = PvfsSetCreateResult(
Args.CreateDisposition,
pCreateContext->bFileExisted,
ntError);
if (fd != -1)
{
close(fd);
}
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
NTSTATUS
PvfsCreateDirDoSysOpen(
IN PVOID pContext
)
{
NTSTATUS ntError = STATUS_UNSUCCESSFUL;
PPVFS_PENDING_CREATE pCreateContext = (PPVFS_PENDING_CREATE)pContext;
PIRP pIrp = pCreateContext->pIrpContext->pIrp;
IRP_ARGS_CREATE Args = pIrp->Args.Create;
int fd = -1;
int unixFlags = 0;
PIO_CREATE_SECURITY_CONTEXT pSecCtx = Args.SecurityContext;
FILE_CREATE_RESULT CreateResult = 0;
IO_MATCH_FILE_SPEC FileSpec = {0};
WCHAR wszPattern[2] = {L'*', 0x0 };
PIO_SECURITY_CONTEXT_PROCESS_INFORMATION pProcess = NULL;
/* Do the open() */
ntError = MapPosixOpenFlags(
&unixFlags,
pCreateContext->GrantedAccess,
Args);
BAIL_ON_NT_STATUS(ntError);
if (!pCreateContext->bFileExisted)
{
ntError = PvfsSysMkDir(pCreateContext->pszDiskFilename, 0700);
BAIL_ON_NT_STATUS(ntError);
}
/* Open the DIR* and then open a fd based on that */
ntError = PvfsAllocateMemory(
(PVOID)&pCreateContext->pCcb->pDirContext,
sizeof(PVFS_DIRECTORY_CONTEXT));
BAIL_ON_NT_STATUS(ntError);
pCreateContext->pCcb->pszFilename = pCreateContext->pszDiskFilename;
pCreateContext->pszDiskFilename = NULL;
ntError = PvfsSysOpen(&fd, pCreateContext->pCcb->pszFilename, 0, 0);
BAIL_ON_NT_STATUS(ntError);
/* Save our state */
pCreateContext->pCcb->fd = fd;
pCreateContext->pCcb->ShareFlags = Args.ShareAccess;
pCreateContext->pCcb->AccessGranted = pCreateContext->GrantedAccess;
pCreateContext->pCcb->CreateOptions = Args.CreateOptions;
ntError = PvfsAddCCBToFCB(pCreateContext->pFcb, pCreateContext->pCcb);
BAIL_ON_NT_STATUS(ntError);
ntError = PvfsSaveFileDeviceInfo(pCreateContext->pCcb);
BAIL_ON_NT_STATUS(ntError);
if ((pCreateContext->SetPropertyFlags & PVFS_SET_PROP_SECURITY) && pSecCtx)
{
/* Unix Security */
pProcess = IoSecurityGetProcessInfo(pSecCtx);
ntError = PvfsSysChown(
pCreateContext->pCcb,
pProcess->Uid,
pProcess->Gid);
BAIL_ON_NT_STATUS(ntError);
/* Security Descriptor */
ntError = PvfsCreateFileSecurity(
pCreateContext->pCcb->pUserToken,
pCreateContext->pCcb,
Args.SecurityDescriptor,
TRUE);
BAIL_ON_NT_STATUS(ntError);
}
if ((pCreateContext->SetPropertyFlags & PVFS_SET_PROP_ATTRIB) &&
(Args.FileAttributes != 0))
{
ntError = PvfsSetFileAttributes(
pCreateContext->pCcb,
Args.FileAttributes);
BAIL_ON_NT_STATUS(ntError);
}
/* Save the delete-on-close flag to the FCB */
if (Args.CreateOptions & FILE_DELETE_ON_CLOSE)
{
LwRtlUnicodeStringInit(&FileSpec.Pattern, wszPattern);
ntError = PvfsEnumerateDirectory(pCreateContext->pCcb, &FileSpec, 1, FALSE);
if (ntError == STATUS_SUCCESS)
{
ntError = STATUS_DIRECTORY_NOT_EMPTY;
BAIL_ON_NT_STATUS(ntError);
}
pCreateContext->pCcb->bPendingDeleteHandle = TRUE;
}
ntError = PvfsStoreCCB(pIrp->FileHandle, pCreateContext->pCcb);
BAIL_ON_NT_STATUS(ntError);
CreateResult = PvfsSetCreateResult(
Args.CreateDisposition,
pCreateContext->bFileExisted,
STATUS_SUCCESS);
if (CreateResult == FILE_CREATED)
{
PvfsNotifyScheduleFullReport(
pCreateContext->pCcb->pFcb,
FILE_NOTIFY_CHANGE_FILE_NAME,
FILE_ACTION_ADDED,
pCreateContext->pCcb->pszFilename);
}
/* The CCB has been handled off to the FileHandle so make sure
we don't think we still own it */
pCreateContext->pCcb = NULL;
cleanup:
pIrp->IoStatusBlock.CreateResult = CreateResult;
return ntError;
error:
CreateResult = PvfsSetCreateResult(
Args.CreateDisposition,
pCreateContext->bFileExisted,
ntError);
if (fd != -1)
{
close(fd);
}
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
FILE_CREATE_RESULT
PvfsSetCreateResult(
IN FILE_CREATE_DISPOSITION Disposition,
IN BOOLEAN bFileExisted,
IN NTSTATUS ntError
)
{
FILE_CREATE_RESULT CreateResult = 0;
/* Handle the error case in the "error" label */
BAIL_ON_NT_STATUS(ntError);
switch (Disposition)
{
case FILE_CREATE:
case FILE_OPEN:
case FILE_OPEN_IF:
CreateResult = bFileExisted ? FILE_OPENED: FILE_CREATED;
break;
case FILE_OVERWRITE:
case FILE_OVERWRITE_IF:
CreateResult = bFileExisted ? FILE_OVERWRITTEN: FILE_CREATED;
break;
case FILE_SUPERSEDE:
CreateResult = bFileExisted ? FILE_SUPERSEDED: FILE_CREATED;
break;
}
cleanup:
return CreateResult;
error:
switch (Disposition)
{
case FILE_CREATE:
CreateResult = (ntError == STATUS_OBJECT_NAME_COLLISION) ?
FILE_EXISTS : FILE_DOES_NOT_EXIST;
break;
case FILE_OPEN:
case FILE_OVERWRITE:
CreateResult = (ntError == STATUS_OBJECT_PATH_NOT_FOUND) ?
FILE_DOES_NOT_EXIST : FILE_EXISTS;
break;
case FILE_OPEN_IF:
case FILE_OVERWRITE_IF:
CreateResult = (ntError == STATUS_OBJECT_NAME_COLLISION) ?
FILE_EXISTS : FILE_DOES_NOT_EXIST;
break;
case FILE_SUPERSEDE:
CreateResult = bFileExisted ? FILE_EXISTS : FILE_DOES_NOT_EXIST;
break;
}
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
NTSTATUS
PvfsCheckDeleteOnClose(
IN IRP_ARGS_CREATE CreateArgs,
IN PSTR pszFilename,
IN ACCESS_MASK GrantedAccess
)
{
NTSTATUS ntError = STATUS_SUCCESS;
FILE_ATTRIBUTES Attributes = 0;
if (!(CreateArgs.CreateOptions & FILE_DELETE_ON_CLOSE)) {
goto cleanup;
}
if (!(GrantedAccess & DELETE))
{
ntError = STATUS_ACCESS_DENIED;
BAIL_ON_NT_STATUS(ntError);
}
/* ReadOnly is largely ignored by the file system on directories.
It is used by the Windows explorer.exe to mark folders as "special" */
if (CreateArgs.CreateOptions & FILE_DIRECTORY_FILE)
{
ntError = STATUS_SUCCESS;
goto cleanup;
}
/* Dealing with files from here down */
if (pszFilename)
{
ntError = PvfsGetFilenameAttributes(
pszFilename,
&Attributes);
BAIL_ON_NT_STATUS(ntError);
}
switch (CreateArgs.CreateDisposition)
{
case FILE_OPEN:
case FILE_OPEN_IF:
case FILE_SUPERSEDE:
case FILE_CREATE:
if (pszFilename)
{
if (Attributes & FILE_ATTRIBUTE_READONLY)
{
ntError = STATUS_CANNOT_DELETE;
BAIL_ON_NT_STATUS(ntError);
}
}
else
{
if (CreateArgs.FileAttributes & FILE_ATTRIBUTE_READONLY)
{
ntError = STATUS_CANNOT_DELETE;
BAIL_ON_NT_STATUS(ntError);
}
}
break;
case FILE_OVERWRITE:
case FILE_OVERWRITE_IF:
if (pszFilename && (CreateArgs.FileAttributes == 0))
{
if (Attributes & FILE_ATTRIBUTE_READONLY)
{
ntError = STATUS_CANNOT_DELETE;
BAIL_ON_NT_STATUS(ntError);
}
}
else
{
if (CreateArgs.FileAttributes & FILE_ATTRIBUTE_READONLY)
{
ntError = STATUS_CANNOT_DELETE;
BAIL_ON_NT_STATUS(ntError);
}
}
break;
}
ntError = STATUS_SUCCESS;
cleanup:
return ntError;
error:
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
VOID
PvfsFreeCreateContext(
IN OUT PVOID *ppContext
)
{
PPVFS_PENDING_CREATE pCreateCtx = NULL;
if (ppContext && *ppContext)
{
pCreateCtx = (PPVFS_PENDING_CREATE)*ppContext;
if (pCreateCtx->pIrpContext)
{
PvfsReleaseIrpContext(&pCreateCtx->pIrpContext);
}
if (pCreateCtx->pCcb)
{
PvfsReleaseCCB(pCreateCtx->pCcb);
}
if (pCreateCtx->pFcb)
{
PvfsReleaseFCB(&pCreateCtx->pFcb);
}
RtlCStringFree(&pCreateCtx->pszDiskFilename);
RtlCStringFree(&pCreateCtx->pszOriginalFilename);
PVFS_FREE(ppContext);
}
return;
}
/*****************************************************************************
****************************************************************************/
NTSTATUS
PvfsAllocateCreateContext(
OUT PPVFS_PENDING_CREATE *ppCreate,
IN PPVFS_IRP_CONTEXT pIrpContext
)
{
NTSTATUS ntError = STATUS_UNSUCCESSFUL;
PPVFS_PENDING_CREATE pCreateCtx = NULL;
IRP_ARGS_CREATE Args = pIrpContext->pIrp->Args.Create;
PIO_CREATE_SECURITY_CONTEXT pSecCtx = Args.SecurityContext;
ntError = PvfsAllocateMemory(
(PVOID*)&pCreateCtx,
sizeof(PVFS_PENDING_CREATE));
BAIL_ON_NT_STATUS(ntError);
ntError = PvfsCanonicalPathName(
&pCreateCtx->pszOriginalFilename,
Args.FileName);
BAIL_ON_NT_STATUS(ntError);
ntError = PvfsAllocateCCB(&pCreateCtx->pCcb);
BAIL_ON_NT_STATUS(ntError);
ntError = PvfsAcquireAccessToken(pCreateCtx->pCcb, pSecCtx);
BAIL_ON_NT_STATUS(ntError);
pCreateCtx->pIrpContext = PvfsReferenceIrpContext(pIrpContext);
*ppCreate = pCreateCtx;
pCreateCtx = NULL;
cleanup:
return ntError;
error:
PvfsFreeCreateContext((PVOID*)&pCreateCtx);
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
NTSTATUS
PvfsCreateFileCheckPendingDelete(
PPVFS_FCB pFcb
)
{
NTSTATUS ntError = STATUS_SUCCESS;
PPVFS_FCB pParentFcb = NULL;
if (PvfsFcbIsPendingDelete(pFcb))
{
ntError = STATUS_DELETE_PENDING;
BAIL_ON_NT_STATUS(ntError);
}
pParentFcb = PvfsGetParentFCB(pFcb);
if (pParentFcb && PvfsFcbIsPendingDelete(pParentFcb))
{
ntError = STATUS_DELETE_PENDING;
BAIL_ON_NT_STATUS(ntError);
}
ntError = STATUS_SUCCESS;
cleanup:
if (pParentFcb)
{
PvfsReleaseFCB(&pParentFcb);
}
return ntError;
error:
goto cleanup;
}
/*
local variables:
mode: c
c-basic-offset: 4
indent-tabs-mode: nil
tab-width: 4
end:
*/