/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
 * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
 * Editor Settings: expandtabs and use 4 spaces for indentation */

/*
 * 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
 * <http://www.gnu.org/licenses/>.
 *
 * 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:
 *
 *        shareapi.c
 *
 * Abstract:
 *
 *        Likewise IO (LWIO) Server subsystem
 *
 *        Server share list handling interface
 *
 * Authors: Sriram Nambakam (snambakam@likewise.com)
 *          Rafal Szczesniak (rafal@likewise.com)
 */

#include "includes.h"

typedef struct _SRV_SHARE_ENTRY
{
    PSRV_SHARE_INFO pInfo;

    struct _SRV_SHARE_ENTRY *pNext;

} SRV_SHARE_ENTRY, *PSRV_SHARE_ENTRY;


typedef struct _LWIO_SRV_SHARE_ENTRY_LIST
{
    pthread_rwlock_t RwLock;
    pthread_rwlock_t* pRwLock;

    PSRV_SHARE_ENTRY pShareEntry;

    PLWRTL_RB_TREE pShareCollection;

} LWIO_SRV_SHARE_ENTRY_LIST, *PLWIO_SRV_SHARE_ENTRY_LIST;


struct SRV_SHARES_HANDLE_DATA
{
    LWIO_SRV_SHARE_ENTRY_LIST ShareList;
    SRV_SHARE_REPOSITORY_HANDLE hRepository;
};


static
NTSTATUS
SrvShareFindByName_inlock(
    IN PLWIO_SRV_SHARE_ENTRY_LIST pShareList,
    IN PWSTR pwszShareName,
    IN OPTIONAL PCWSTR pDomain,
    IN OPTIONAL PCWSTR pUser,
    OUT PSRV_SHARE_INFO* ppShareInfo
    );

static
NTSTATUS
SrvShareRemoveFromList_inlock(
    IN OUT PLWIO_SRV_SHARE_ENTRY_LIST pShareList,
    IN PWSTR pwszShareName
    );

static
int
SrvShareCompareInfo(
    PVOID pKey1,
    PVOID pKey2
    );

static
VOID
SrvShareReleaseInfoHandle(
    PVOID hShareInfo
    );

static
VOID
SrvShareFreeInfo(
    PSRV_SHARE_INFO pShareInfo
    );

static
NTSTATUS
SrvShareCollectionDuplicate(
    IN PLWRTL_RB_TREE pShareCollection,
    OUT PLWRTL_RB_TREE* ppShareCollectionCopy
    );

static
NTSTATUS
SrvShareCollectionDuplicateVisit(
    IN PVOID pKey,
    IN PVOID pData,
    IN PVOID pUserData,
    OUT PBOOLEAN pContinue
    );

static
NTSTATUS
SrvShareReloadConfigurationVisitDeleteShare_inlock(
    IN PVOID pKey,
    IN PVOID hShareInfo,
    IN PVOID hShareList,
    OUT PBOOLEAN pContinue
    );

static
NTSTATUS
SrvShareAddShareInfoInMemory_inlock(
    IN PLWIO_SRV_SHARE_ENTRY_LIST pShareList,
    IN PSRV_SHARE_REPOSITORY_SHARE pRepositoryShare
    );

static
VOID
SrvShareDeleteShareInfoInMemory_inlock(
    IN PLWIO_SRV_SHARE_ENTRY_LIST pShareList,
    IN PSRV_SHARE_INFO pShareInfo
    );

static
NTSTATUS
SrvShareUpdateShareInfoInMemory_inlock(
    IN OUT PSRV_SHARE_INFO pShareInfoDst,
    IN PSRV_SHARE_REPOSITORY_SHARE pRepositoryShareSrc
    );

static
NTSTATUS
SrvShareDuplicateInfo_inlock(
    IN PSRV_SHARE_INFO pShareInfo,
    OUT PSRV_SHARE_INFO* ppShareInfo
    );

static
VOID
SrvShareDisconnectClients(
    IN PSRV_SHARE_INFO pShareInfo
    );

static
VOID
SrvShareFreeEntry(
    IN PSRV_SHARE_ENTRY pShareEntry
    );

static
NTSTATUS
SrvShareInfoCreate(
    IN OPTIONAL PWSTR pwszShareName,
    IN OPTIONAL PWSTR pwszSharePath,
    IN OPTIONAL PWSTR pwszShareComment,
    IN OPTIONAL PSECURITY_DESCRIPTOR_RELATIVE pSecDesc,
    IN ULONG SecDescLength,
    IN SHARE_STYPE Service,
    IN ULONG Flags,
    OUT PSRV_SHARE_INFO* ppShareInfo
    );

static
VOID
SrvShareFreeListContents(
    IN OUT PLWIO_SRV_SHARE_ENTRY_LIST pShareList
    );

static
NTSTATUS
SrvShareReloadCallback(
    PVOID pContext
    );

static
VOID
SrvShareFreeInfoList(
    IN PSRV_SHARE_INFO* ppInfoList,
    IN ULONG NumInfos
    );

static
BOOLEAN
SrvShareValidateShareName(
    PWSTR pwszShareName
    );

static
NTSTATUS
SrvShareReloadIsValidRepositoryShare(
    IN PSRV_SHARE_REPOSITORY_SHARE pRepositoryShare,
    OUT PBOOLEAN pIsValid
    );

static
NTSTATUS
SrvShareBootstrapIpc_inlock(
    IN PLWIO_SRV_SHARE_ENTRY_LIST pShareList
    );

static const WCHAR homeShareName[] = LWIO_SRV_SHARE_STRING_ID_HOME_SHARE_W;

VOID
SrvShareShutdown(
    IN OUT PSRV_SHARES_HANDLE pHandle
    )
{
    if (*pHandle)
    {
        SRV_SHARES_HANDLE handle = *pHandle;

        SrvShareFreeListContents(&handle->ShareList);
        SrvShareRepositoryCleanup(handle->hRepository);
        LwRtlWC16StringFree(&gSharesApiGlobals.pwszFileSystemRoot);

        SrvFreeMemory(handle);
        handle = NULL;

        *pHandle = handle;
    }
}

NTSTATUS
SrvShareInit(
    IN PSTR pszFileSystemRoot,
    OUT PSRV_SHARES_HANDLE pHandle
    )
{
    NTSTATUS ntStatus = 0;
    SRV_SHARES_HANDLE handle = NULL;

    ntStatus = SrvAllocateMemory(sizeof(*handle), OUT_PPVOID(&handle));
    BAIL_ON_NT_STATUS(ntStatus);

    pthread_rwlock_init(&handle->ShareList.RwLock, NULL);
    handle->ShareList.pRwLock = &handle->ShareList.RwLock;

    handle->ShareList.pShareEntry = NULL;

    ntStatus = LwRtlWC16StringAllocateFromCString(
                    &gSharesApiGlobals.pwszFileSystemRoot,
                    pszFileSystemRoot);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = LwRtlRBTreeCreate(
                    &SrvShareCompareInfo,
                    NULL,
                    &SrvShareReleaseInfoHandle,
                    &handle->ShareList.pShareCollection);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SrvShareRepositoryInitialize(
                    &handle->hRepository,
                    SrvShareReloadCallback,
                    handle);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SrvShareReloadConfiguration(handle);
    BAIL_ON_NT_STATUS(ntStatus);

cleanup:

    *pHandle = handle;

    return ntStatus;

error:

    SrvShareShutdown(&handle);

    goto cleanup;
}

/*
 * The flow is as following:
 *
 * 1) Duplicate in-memory share list
 * 2) Enum shares from DB
 *   a) If share does not exist in in-memory list, add it to the in-memory list
 *   b) Else remove it from the copy (created in 1) and update the in-memory
 *      info
 * 3) Remove any shares from the in-memory list that are still in the copy
 */
NTSTATUS
SrvShareReloadConfiguration(
    IN SRV_SHARES_HANDLE Handle
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    BOOLEAN inLock = FALSE;
    ULONG numSharesFound = 0;
    PSRV_SHARE_REPOSITORY_SHARE* ppShareRepositoryList = NULL;
    PLWRTL_RB_TREE pShareCollectionCopy = NULL;
    PSRV_SHARE_INFO pMemoryShareInfo = NULL;
    BOOLEAN inMemoryShareInfoLock = FALSE;
    ULONG shareIndex = 0;

    LWIO_LOCK_RWMUTEX_EXCLUSIVE(inLock, &Handle->ShareList.RwLock);

    ntStatus = SrvShareBootstrapIpc_inlock(&Handle->ShareList);
    BAIL_ON_NT_STATUS(ntStatus);

    // In the end of the enumeration this copy will hold a list of deleted
    // shares
    ntStatus = SrvShareCollectionDuplicate(Handle->ShareList.pShareCollection,
                                           &pShareCollectionCopy);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SrvShareRepositoryGetShareList(
                    Handle->hRepository,
                    &ppShareRepositoryList,
                    &numSharesFound);
    BAIL_ON_NT_STATUS(ntStatus);

    for (; shareIndex < numSharesFound; shareIndex++)
    {
        PSRV_SHARE_REPOSITORY_SHARE pRepositoryShare =
                                            ppShareRepositoryList[shareIndex];
        BOOLEAN disconnectClients = FALSE;
        BOOLEAN isValidRepositoryShare = FALSE;

        pMemoryShareInfo = NULL;

        ntStatus = SrvShareReloadIsValidRepositoryShare(
                        pRepositoryShare,
                        &isValidRepositoryShare);
        BAIL_ON_NT_STATUS(ntStatus);

        if (!isValidRepositoryShare)
        {
            continue;
        }

        ntStatus = LwRtlRBTreeFind(
                        Handle->ShareList.pShareCollection,
                        pRepositoryShare->pwszShareName,
                        OUT_PPVOID(&pMemoryShareInfo));
        if (ntStatus == STATUS_NOT_FOUND)
        {
            ntStatus = STATUS_SUCCESS;

            // new entry case - add repository share to the in-memory list
            ntStatus = SrvShareAddShareInfoInMemory_inlock(
                            &Handle->ShareList,
                            pRepositoryShare);
            BAIL_ON_NT_STATUS(ntStatus);

            continue;
        }
        BAIL_ON_NT_STATUS(ntStatus);


        ntStatus = LwRtlRBTreeRemove(pShareCollectionCopy,
                                     pRepositoryShare->pwszShareName);
        BAIL_ON_NT_STATUS(ntStatus);


        LWIO_LOCK_RWMUTEX_EXCLUSIVE(inMemoryShareInfoLock,
                                    &pMemoryShareInfo->Mutex);

        if (LwRtlWC16StringIsEqual(pRepositoryShare->pwszPath,
                           pMemoryShareInfo->pwszPath, FALSE) == FALSE ||
            pRepositoryShare->Service != pMemoryShareInfo->Service)
        {
            disconnectClients = TRUE;
        }
        else
        {
            ntStatus = SrvShareUpdateShareInfoInMemory_inlock(
                            pMemoryShareInfo,
                            pRepositoryShare);
            BAIL_ON_NT_STATUS(ntStatus);
        }

        LWIO_UNLOCK_RWMUTEX(inMemoryShareInfoLock,
                            &pMemoryShareInfo->Mutex);

        if (disconnectClients)
        {
            SrvShareDeleteShareInfoInMemory_inlock(&Handle->ShareList,
                                                   pMemoryShareInfo);

            ntStatus = SrvShareAddShareInfoInMemory_inlock(
                            &Handle->ShareList,
                            pRepositoryShare);
            BAIL_ON_NT_STATUS(ntStatus);
        }
    }

    // Take care of all the deleted shares
    ntStatus = LwRtlRBTreeTraverse(
                    pShareCollectionCopy,
                    LWRTL_TREE_TRAVERSAL_TYPE_IN_ORDER,
                    SrvShareReloadConfigurationVisitDeleteShare_inlock,
                    &Handle->ShareList);
    BAIL_ON_NT_STATUS(ntStatus);

cleanup:

    if (pMemoryShareInfo)
    {
        LWIO_UNLOCK_RWMUTEX(inMemoryShareInfoLock, &pMemoryShareInfo->Mutex);
    }

    if (ppShareRepositoryList)
    {
        SrvShareRepositoryFreeShareList(ppShareRepositoryList, numSharesFound);
    }

    if (pShareCollectionCopy)
    {
        LwRtlRBTreeFree(pShareCollectionCopy);
        pShareCollectionCopy = NULL;
    }

    LWIO_UNLOCK_RWMUTEX(inLock, &Handle->ShareList.RwLock);

    return ntStatus;

error:

    goto cleanup;
}

NTSTATUS
SrvShareFindByName(
    IN SRV_SHARES_HANDLE Handle,
    IN PWSTR pwszShareName,
    IN OPTIONAL PCWSTR pDomain,
    IN OPTIONAL PCWSTR pUser,
    OUT PSRV_SHARE_INFO* ppShareInfo
    )
{
    NTSTATUS ntStatus = 0;
    BOOLEAN inLock = FALSE;

    LWIO_LOCK_RWMUTEX_SHARED(inLock, &Handle->ShareList.RwLock);

    ntStatus = SrvShareFindByName_inlock(
                        &Handle->ShareList,
                        pwszShareName,
                        pDomain,
                        pUser,
                        ppShareInfo);

    LWIO_UNLOCK_RWMUTEX(inLock, &Handle->ShareList.RwLock);

    return ntStatus;
}

static
NTSTATUS
SrvShareFindByName_inlock(
    IN PLWIO_SRV_SHARE_ENTRY_LIST pShareList,
    IN PWSTR pwszShareName,
    IN OPTIONAL PCWSTR pDomain,
    IN OPTIONAL PCWSTR pUser,
    OUT PSRV_SHARE_INFO* ppShareInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSRV_SHARE_INFO pShareInfo = NULL;

    ntStatus = LwRtlRBTreeFind(
                    pShareList->pShareCollection,
                    pwszShareName,
                    (PVOID*)&pShareInfo);
    if (ntStatus == STATUS_NOT_FOUND && pUser &&
        LwRtlWC16StringIsEqual(pUser, pwszShareName, FALSE))
    {
        // Look for home share entry in database
        ntStatus = LwRtlRBTreeFind(
                    pShareList->pShareCollection,
                    (PVOID) homeShareName,
                    (PVOID*)&pShareInfo);
    }
    BAIL_ON_NT_STATUS(ntStatus);

    *ppShareInfo = SrvShareAcquireInfo(pShareInfo);

cleanup:

    return ntStatus;

error:

    *ppShareInfo = NULL;

    goto cleanup;
}

NTSTATUS
SrvShareGetName(
    IN PSRV_SHARE_INFO pInfo,
    IN OPTIONAL PCWSTR pDomain,
    IN OPTIONAL PCWSTR pUser,
    OUT PWSTR* ppName
    )
{
    if (pDomain && pUser && LwRtlWC16StringIsEqual(pInfo->pwszName, homeShareName, FALSE))
    {
        return LwRtlWC16StringDuplicate(ppName, pUser);
    }
    else
    {
        return LwRtlWC16StringDuplicate(ppName, pInfo->pwszName);
    }
}

NTSTATUS
SrvShareGetPath(
    IN PSRV_SHARE_INFO pInfo,
    IN OPTIONAL PCWSTR pDomain,
    IN OPTIONAL PCWSTR pUser,
    OUT PWSTR* ppPath
    )
{
    NTSTATUS status = STATUS_SUCCESS;
    PWSTR pPath = NULL;
    ULONG in = 0;
    ULONG out = 0;
    ULONG len = 0;

    if (pDomain && pUser && LwRtlWC16StringIsEqual(pInfo->pwszName, homeShareName, FALSE))
    {
        for (in = 0; pInfo->pwszPath[in]; in++)
        {
            if (pInfo->pwszPath[in] == '%' && pInfo->pwszPath[in+1] == 'D')
            {
                len += LwRtlWC16StringNumChars(pDomain);
                in++;
            }
            else if (pInfo->pwszPath[in] == '%' && pInfo->pwszPath[in+1] == 'U')
            {
                len += LwRtlWC16StringNumChars(pUser);
                in++;
            }
            else
            {
                len++;
            }
        }

        status = LW_RTL_ALLOCATE_ARRAY_AUTO(&pPath, len + 1);
        BAIL_ON_NT_STATUS(status);

        for (in = 0; pInfo->pwszPath[in]; in++)
        {
            if (pInfo->pwszPath[in] == '%' && pInfo->pwszPath[in+1] == 'D')
            {
                len = LwRtlWC16StringNumChars(pDomain);
                memcpy(&pPath[out], pDomain, len * sizeof(WCHAR));
                out += len;
                in++;
            }
            else if (pInfo->pwszPath[in] == '%' && pInfo->pwszPath[in+1] == 'U')
            {
                len = LwRtlWC16StringNumChars(pUser);
                memcpy(&pPath[out], pUser, len * sizeof(WCHAR));
                out += len;
                in++;
            }
            else
            {
                pPath[out++] = pInfo->pwszPath[in];
            }
        }
    }
    else
    {
        status = LwRtlWC16StringDuplicate(&pPath, pInfo->pwszPath);
        BAIL_ON_NT_STATUS(status);
    }

error:

    if (!NT_SUCCESS(status))
    {
        RTL_FREE(&pPath);
    }

    *ppPath = pPath;

    return status;
}

NTSTATUS
SrvShareAdd(
    IN SRV_SHARES_HANDLE Handle,
    IN PWSTR pwszShareName,
    IN PWSTR pwszSharePath,
    IN PWSTR pwszShareComment,
    IN PBYTE pSecDesc,
    IN ULONG SecDescLen,
    IN SHARE_STYPE ShareType,
    IN SHARE_INFO_FLAGS ShareFlags
    )
{
    NTSTATUS ntStatus = 0;
    BOOLEAN inLock = FALSE;
    PSRV_SHARE_ENTRY pShareEntry = NULL;
    PSRV_SHARE_INFO pShareInfo = NULL;
    wchar16_t wszShareComment[] = {0};
    PWSTR pwszShareCommentRef =
                    (pwszShareComment ? pwszShareComment : &wszShareComment[0]);
    SRV_SHARE_REPOSITORY_SHARE repositoryShare = { 0 };

    if (IsNullOrEmptyString(pwszShareName))
    {
        ntStatus = STATUS_INVALID_PARAMETER_2;
    }
    if (IsNullOrEmptyString(pwszSharePath))
    {
        ntStatus = STATUS_INVALID_PARAMETER_3;
    }
    if (ShareType != SHARE_STYPE_DISKTREE)
    {
        ntStatus = STATUS_INVALID_PARAMETER_7;
    }
    if (ShareFlags & (~SHARE_INFO_FLAGS_VALID_MASK))
    {
        ntStatus = STATUS_INVALID_PARAMETER_8;
    }
    BAIL_ON_NT_STATUS(ntStatus);

    LWIO_LOCK_RWMUTEX_EXCLUSIVE(inLock, &Handle->ShareList.RwLock);

    ntStatus = SrvShareFindByName_inlock(
                        &Handle->ShareList,
                        pwszShareName,
                        NULL,
                        NULL,
                        &pShareInfo);
    if (!ntStatus)
    {
        ntStatus = STATUS_OBJECT_NAME_INVALID;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    ntStatus = SrvShareInfoCreate(
                    pwszShareName,
                    pwszSharePath,
                    pwszShareCommentRef,
                    (PSECURITY_DESCRIPTOR_RELATIVE)pSecDesc,
                    SecDescLen,
                    ShareType,
                    ShareFlags,
                    &pShareInfo);
    BAIL_ON_NT_STATUS(ntStatus);

    if (!SecDescLen)
    {
        ntStatus = SrvShareSetDefaultSecurity(pShareInfo);
        BAIL_ON_NT_STATUS(ntStatus);
    }

    ntStatus = SrvAllocateMemory(
                    sizeof(SRV_SHARE_ENTRY),
                    (PVOID*)&pShareEntry);
    BAIL_ON_NT_STATUS(ntStatus);

    pShareEntry->pInfo = SrvShareAcquireInfo(pShareInfo);

    // TODO: We only allow adding disk sharess
    repositoryShare.pwszShareName = pShareInfo->pwszName;
    repositoryShare.pwszPath = pShareInfo->pwszPath;
    repositoryShare.pwszComment = pShareInfo->pwszComment;
    repositoryShare.pSecurityDescriptor = pShareInfo->pSecDesc;
    repositoryShare.SecurityDescriptorLength = pShareInfo->SecDescLen;
    repositoryShare.Service = ShareType;
    repositoryShare.Flags = ShareFlags;
    repositoryShare.MaxUses = SRV_SHARE_MAX_USES_UNLIMITED;

    ntStatus = SrvShareRepositoryAddShare(
                    Handle->hRepository,
                    &repositoryShare);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = LwRtlRBTreeAdd(
                    Handle->ShareList.pShareCollection,
                    pShareInfo->pwszName,
                    pShareInfo);
    BAIL_ON_NT_STATUS(ntStatus);

    pShareInfo = NULL;

    pShareEntry->pNext = Handle->ShareList.pShareEntry;
    Handle->ShareList.pShareEntry = pShareEntry;

cleanup:

    LWIO_UNLOCK_RWMUTEX(inLock, &Handle->ShareList.RwLock);

    if (pShareInfo)
    {
        SrvShareReleaseInfo(pShareInfo);
    }

    return ntStatus;

error:

    if (pShareEntry)
    {
        SrvShareFreeEntry(pShareEntry);
    }

    goto cleanup;
}

NTSTATUS
SrvShareUpdate(
    IN SRV_SHARES_HANDLE Handle,
    IN PSRV_SHARE_INFO pShareInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    BOOLEAN inLock = FALSE;
    SRV_SHARE_REPOSITORY_SHARE repositoryShare = { 0 };

    if (!pShareInfo || IsNullOrEmptyString(pShareInfo->pwszName))
    {
        ntStatus = STATUS_INVALID_PARAMETER;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    LWIO_LOCK_RWMUTEX_SHARED(inLock, &pShareInfo->Mutex);

    if (pShareInfo->Service != SHARE_STYPE_DISKTREE)
    {
        ntStatus = STATUS_ACCESS_DENIED;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    repositoryShare.pwszShareName = pShareInfo->pwszName;
    repositoryShare.pwszPath = pShareInfo->pwszPath;
    repositoryShare.pwszComment = pShareInfo->pwszComment;
    repositoryShare.pSecurityDescriptor = pShareInfo->pSecDesc;
    repositoryShare.SecurityDescriptorLength = pShareInfo->SecDescLen;
    repositoryShare.Service = pShareInfo->Service;
    repositoryShare.Flags = pShareInfo->Flags;
    repositoryShare.MaxUses = SRV_SHARE_MAX_USES_UNLIMITED;

    ntStatus = SrvShareRepositoryUpdateShare(
                    Handle->hRepository,
                    &repositoryShare);
    BAIL_ON_NT_STATUS(ntStatus);

cleanup:

    LWIO_UNLOCK_RWMUTEX(inLock, &pShareInfo->Mutex);

    return ntStatus;

error:

    goto cleanup;
}

NTSTATUS
SrvShareDelete(
    IN SRV_SHARES_HANDLE Handle,
    IN PWSTR pwszShareName
    )
{
    NTSTATUS ntStatus = 0;
    BOOLEAN inListLock = FALSE;
    PSRV_SHARE_INFO pShareInfo = NULL;

    if (IsNullOrEmptyString(pwszShareName))
    {
        ntStatus = STATUS_INVALID_PARAMETER;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    ntStatus = SrvShareFindByName(Handle, pwszShareName, NULL, NULL, &pShareInfo);
    BAIL_ON_NT_STATUS(ntStatus);

    if (pShareInfo->Service != SHARE_STYPE_DISKTREE)
    {
        ntStatus = STATUS_ACCESS_DENIED;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    LWIO_LOCK_RWMUTEX_EXCLUSIVE(inListLock, &Handle->ShareList.RwLock);

    ntStatus = SrvShareRepositoryDeleteShare(Handle->hRepository, pwszShareName);
    BAIL_ON_NT_STATUS(ntStatus);

    SrvShareDeleteShareInfoInMemory_inlock(&Handle->ShareList, pShareInfo);

cleanup:

    LWIO_UNLOCK_RWMUTEX(inListLock, &Handle->ShareList.RwLock);

    if (pShareInfo)
    {
        SrvShareReleaseInfo(pShareInfo);
    }

    return ntStatus;

error:

    goto cleanup;
}

static
NTSTATUS
SrvShareRemoveFromList_inlock(
    IN OUT PLWIO_SRV_SHARE_ENTRY_LIST pShareList,
    IN PWSTR pwszShareName
    )
{
    PSRV_SHARE_ENTRY pShareEntry = NULL;
    PSRV_SHARE_ENTRY pPrevShareEntry = NULL;
    ULONG sharesRemoved = 0;

    pShareEntry = pShareList->pShareEntry;

    while (pShareEntry)
    {
        if (SMBWc16sCaseCmp(pwszShareName, pShareEntry->pInfo->pwszName) == 0)
        {
            if (pPrevShareEntry)
            {
                pPrevShareEntry->pNext = pShareEntry->pNext;
            }
            else
            {
                pShareList->pShareEntry = pShareEntry->pNext;
            }

            pShareEntry->pNext = NULL;
            SrvShareFreeEntry(pShareEntry);

            sharesRemoved++;

            break;
        }

        pPrevShareEntry = pShareEntry;
        pShareEntry     = pShareEntry->pNext;
    }

    return (sharesRemoved ? STATUS_SUCCESS : STATUS_NOT_FOUND);
}

static
BOOLEAN
SrvShareShouldFilter(
    IN PSRV_SHARE_INFO pInfo,
    IN OPTIONAL PWSTR pDomain,
    IN OPTIONAL PWSTR pUser
    )
{
    if (LwRtlWC16StringIsEqual(pInfo->pwszName, homeShareName, FALSE))
    {
        // Filter home share if there is no NT4 name for the calling user
        return pUser == NULL;
    }
    else
    {
        return FALSE;
    }
}

NTSTATUS
SrvShareEnum(
    IN SRV_SHARES_HANDLE Handle,
    IN PWSTR pDomain,
    IN PWSTR pUser,
    OUT PSRV_SHARE_INFO** pppShareInfo,
    IN OUT PULONG pNumEntries
    )
{
    NTSTATUS ntStatus = 0;
    ULONG count = 0;
    BOOLEAN inLock = FALSE;
    PSRV_SHARE_ENTRY pShareEntry = NULL;
    PSRV_SHARE_INFO* ppShares = NULL;

    LWIO_LOCK_RWMUTEX_SHARED(inLock, &Handle->ShareList.RwLock);

    /* Count the number of share entries */
    for (pShareEntry = Handle->ShareList.pShareEntry; pShareEntry; pShareEntry = pShareEntry->pNext)
    {
        if (SrvShareShouldFilter(pShareEntry->pInfo, pDomain, pUser))
        {
            continue;
        }

        count++;
    }

    if (count)
    {
        ULONG i = 0;

        ntStatus = SrvAllocateMemory(
                        count * sizeof(PSRV_SHARE_INFO),
                        (PVOID*)&ppShares);
        BAIL_ON_NT_STATUS(ntStatus);

        for (pShareEntry = Handle->ShareList.pShareEntry; pShareEntry; pShareEntry = pShareEntry->pNext)
        {
            if (SrvShareShouldFilter(pShareEntry->pInfo, pDomain, pUser))
            {
                continue;
            }

            ntStatus = SrvShareDuplicateInfo(pShareEntry->pInfo, &ppShares[i]);
            BAIL_ON_NT_STATUS(ntStatus);

            i++;
        }
    }

    *pppShareInfo   = ppShares;
    *pNumEntries  = count;

cleanup:

    LWIO_UNLOCK_RWMUTEX(inLock, &Handle->ShareList.RwLock);

    return ntStatus;

error:

    if (ppShares)
    {
        SrvShareFreeInfoList(ppShares, count);
    }

    *pppShareInfo   = NULL;
    *pNumEntries = 0;

    goto cleanup;
}

NTSTATUS
SrvShareDuplicateInfo(
    IN PSRV_SHARE_INFO pShareInfo,
    OUT PSRV_SHARE_INFO* ppShareInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    BOOLEAN  inLock = FALSE;

    LWIO_LOCK_RWMUTEX_SHARED(inLock, &pShareInfo->Mutex);

    ntStatus = SrvShareDuplicateInfo_inlock(pShareInfo, ppShareInfo);
    BAIL_ON_NT_STATUS(ntStatus);

cleanup:

    LWIO_UNLOCK_RWMUTEX(inLock, &pShareInfo->Mutex);

    return ntStatus;

error:

    goto cleanup;
}

static
int
SrvShareCompareInfo(
    PVOID pKey1,
    PVOID pKey2
    )
{
    PWSTR pwszShareName1 = (PWSTR)pKey1;
    PWSTR pwszShareName2 = (PWSTR)pKey2;

    return SMBWc16sCaseCmp(pwszShareName1, pwszShareName2);
}

static
VOID
SrvShareReleaseInfoHandle(
    PVOID hShareInfo
    )
{
    SrvShareReleaseInfo((PSRV_SHARE_INFO)hShareInfo);
}

static
VOID
SrvShareFreeListContents(
    IN OUT PLWIO_SRV_SHARE_ENTRY_LIST pShareList
    )
{
    if (pShareList->pShareEntry)
    {
        SrvShareFreeEntry(pShareList->pShareEntry);
        pShareList->pShareEntry = NULL;
    }

    if (pShareList->pShareCollection)
    {
        LwRtlRBTreeFree(pShareList->pShareCollection);
        pShareList->pShareCollection = NULL;
    }

    if (pShareList->pRwLock)
    {
        pthread_rwlock_destroy(&pShareList->RwLock);
        pShareList->pRwLock = NULL;
    }
}

VOID
SrvShareFreeEntry(
    IN PSRV_SHARE_ENTRY pShareEntry
    )
{
    while (pShareEntry)
    {
        PSRV_SHARE_ENTRY pTmpShareEntry = pShareEntry;

        pShareEntry = pShareEntry->pNext;

        if (pTmpShareEntry->pInfo)
        {
            SrvShareReleaseInfo(pTmpShareEntry->pInfo);
        }

        SrvFreeMemory(pTmpShareEntry);
    }
}

VOID
SrvShareFreeInfoList(
    IN PSRV_SHARE_INFO* ppInfoList,
    IN ULONG NumInfos
    )
{
    ULONG iInfo = 0;

    for (; iInfo < NumInfos; iInfo++)
    {
        PSRV_SHARE_INFO pShareInfo = ppInfoList[iInfo];

        if (pShareInfo)
        {
            SrvShareReleaseInfo(pShareInfo);
        }
    }

    SrvFreeMemory(ppInfoList);
}

PSRV_SHARE_INFO
SrvShareAcquireInfo(
    IN PSRV_SHARE_INFO pShareInfo
    )
{
    InterlockedIncrement(&pShareInfo->RefCount);

    return pShareInfo;
}

VOID
SrvShareReleaseInfo(
    IN PSRV_SHARE_INFO pShareInfo
    )
{
    if (InterlockedDecrement(&pShareInfo->RefCount) == 0)
    {
        SrvShareFreeInfo(pShareInfo);
    }
}

VOID
SrvShareInfoRegistrationInit(
    IN OUT PSRV_SHARE_INFO_REGISTRATION pRegistration
    )
{
    LwListInit(&pRegistration->Link);
    LwListInit(&pRegistration->RundownLink);
}

NTSTATUS
SrvShareInfoTreeRegister(
    IN PSRV_SHARE_INFO pShareInfo,
    IN PLWIO_SRV_TREE pTree
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    BOOLEAN inLock = FALSE;

    LWIO_LOCK_RWMUTEX_EXCLUSIVE(inLock, &pShareInfo->Mutex);

    if (pShareInfo->DeleteInProgress)
    {
        ntStatus = STATUS_INVALID_HANDLE;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    LWIO_ASSERT(LwListIsEmpty(&pTree->ShareInfoRegistration.Link));
    LWIO_ASSERT(LwListIsEmpty(&pTree->ShareInfoRegistration.RundownLink));

    LwListInsertTail(&pShareInfo->TreeList, &pTree->ShareInfoRegistration.Link);

cleanup:

    LWIO_UNLOCK_RWMUTEX(inLock, &pShareInfo->Mutex);

    return ntStatus;

error:

    goto cleanup;
}

NTSTATUS
SrvShareInfoTree2Register(
    IN PSRV_SHARE_INFO pShareInfo,
    IN PLWIO_SRV_TREE_2 pTree
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    BOOLEAN inLock = FALSE;

    LWIO_LOCK_RWMUTEX_EXCLUSIVE(inLock, &pShareInfo->Mutex);

    if (pShareInfo->DeleteInProgress)
    {
        ntStatus = STATUS_INVALID_HANDLE;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    LWIO_ASSERT(LwListIsEmpty(&pTree->ShareInfoRegistration.Link));
    LWIO_ASSERT(LwListIsEmpty(&pTree->ShareInfoRegistration.RundownLink));

    LwListInsertTail(&pShareInfo->Tree2List, &pTree->ShareInfoRegistration.Link);

cleanup:

    LWIO_UNLOCK_RWMUTEX(inLock, &pShareInfo->Mutex);

    return ntStatus;

error:

    goto cleanup;
}

BOOLEAN
SrvShareInfoTreeIsRegistered(
    IN PSRV_SHARE_INFO pShareInfo,
    IN PLWIO_SRV_TREE pTree
    )
{
    BOOLEAN inLock = FALSE;
    BOOLEAN isRegistered = FALSE;

    LWIO_LOCK_RWMUTEX_SHARED(inLock, &pShareInfo->Mutex);

    if (LwListIsEmpty(&pTree->ShareInfoRegistration.Link))
    {
        isRegistered = FALSE;
    }
    else
    {
        isRegistered = TRUE;
    }

    LWIO_UNLOCK_RWMUTEX(inLock, &pShareInfo->Mutex);

    return isRegistered;
}

BOOLEAN
SrvShareInfoTree2IsRegistered(
    IN PSRV_SHARE_INFO pShareInfo,
    IN PLWIO_SRV_TREE_2 pTree
    )
{
    BOOLEAN inLock = FALSE;
    BOOLEAN isRegistered = FALSE;

    LWIO_LOCK_RWMUTEX_SHARED(inLock, &pShareInfo->Mutex);

    if (LwListIsEmpty(&pTree->ShareInfoRegistration.Link))
    {
        isRegistered = FALSE;
    }
    else
    {
        isRegistered = TRUE;
    }

    LWIO_UNLOCK_RWMUTEX(inLock, &pShareInfo->Mutex);

    return isRegistered;
}

VOID
SrvShareInfoTreeUnregister(
    IN PSRV_SHARE_INFO pShareInfo,
    IN PLWIO_SRV_TREE pTree
    )
{
    BOOLEAN inLock = FALSE;

    LWIO_LOCK_RWMUTEX_EXCLUSIVE(inLock, &pShareInfo->Mutex);

    LwListRemove(&pTree->ShareInfoRegistration.Link);

    LWIO_UNLOCK_RWMUTEX(inLock, &pShareInfo->Mutex);
}

VOID
SrvShareInfoTree2Unregister(
    IN PSRV_SHARE_INFO pShareInfo,
    IN PLWIO_SRV_TREE_2 pTree
    )
{
    BOOLEAN inLock = FALSE;

    LWIO_LOCK_RWMUTEX_EXCLUSIVE(inLock, &pShareInfo->Mutex);

    LwListRemove(&pTree->ShareInfoRegistration.Link);

    LWIO_UNLOCK_RWMUTEX(inLock, &pShareInfo->Mutex);
}


static
VOID
SrvShareFreeInfo(
    IN PSRV_SHARE_INFO pShareInfo
    )
{
    LWIO_ASSERT(LwListIsEmpty(&pShareInfo->TreeList));
    LWIO_ASSERT(LwListIsEmpty(&pShareInfo->Tree2List));

    if (pShareInfo->pMutex)
    {
        pthread_rwlock_destroy(&pShareInfo->Mutex);
    }

    if (pShareInfo->pwszName)
    {
        SrvFreeMemory(pShareInfo->pwszName);
    }
    if (pShareInfo->pwszPath)
    {
        SrvFreeMemory(pShareInfo->pwszPath);
    }
    if (pShareInfo->pwszComment)
    {
        SrvFreeMemory(pShareInfo->pwszComment);
    }

    SrvShareFreeSecurity(pShareInfo);

    SrvFreeMemory(pShareInfo);
}

static
NTSTATUS
SrvShareCollectionDuplicate(
    IN PLWRTL_RB_TREE pShareCollection,
    OUT PLWRTL_RB_TREE* ppShareCollectionCopy
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PLWRTL_RB_TREE pShareCollectionCopy = NULL;

    ntStatus = LwRtlRBTreeCreate(
                    &SrvShareCompareInfo,
                    NULL,
                    &SrvShareReleaseInfoHandle,
                    &pShareCollectionCopy);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = LwRtlRBTreeTraverse(
                    pShareCollection,
                    LWRTL_TREE_TRAVERSAL_TYPE_IN_ORDER,
                    SrvShareCollectionDuplicateVisit,
                    pShareCollectionCopy);
    BAIL_ON_NT_STATUS(ntStatus);

cleanup:

    *ppShareCollectionCopy = pShareCollectionCopy;

    return ntStatus;

error:

    if (pShareCollectionCopy)
    {
        LwRtlRBTreeFree(pShareCollectionCopy);
        pShareCollectionCopy = NULL;
    }

    goto cleanup;
}

static
NTSTATUS
SrvShareCollectionDuplicateVisit(
    IN PVOID pKey,
    IN PVOID pData,
    IN PVOID pUserData,
    OUT PBOOLEAN pContinue
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PLWRTL_RB_TREE pShareCollectionCopy = (PLWRTL_RB_TREE)pUserData;
    PSRV_SHARE_INFO pShareInfo = (PSRV_SHARE_INFO)pData;

    SrvShareAcquireInfo(pShareInfo);

    ntStatus = LwRtlRBTreeAdd(pShareCollectionCopy, pKey, pShareInfo);
    BAIL_ON_NT_STATUS(ntStatus);

    *pContinue = TRUE;

cleanup:

    return ntStatus;

error:

    SrvShareReleaseInfo(pShareInfo);

    *pContinue = FALSE;

    goto cleanup;
}

static
NTSTATUS
SrvShareReloadConfigurationVisitDeleteShare_inlock(
    IN PVOID pKey,
    IN PVOID hShareInfo,
    IN PVOID hShareList,
    OUT PBOOLEAN pContinue
    )
{
    PLWIO_SRV_SHARE_ENTRY_LIST pShareList =
                                        (PLWIO_SRV_SHARE_ENTRY_LIST)hShareList;
    PSRV_SHARE_INFO pShareInfo = (PSRV_SHARE_INFO)hShareInfo;

    // Do not remove IPC$ from in-memory db
    if (pShareInfo->Service != SHARE_STYPE_IPC)
    {
        SrvShareDeleteShareInfoInMemory_inlock(pShareList, pShareInfo);
    }

    *pContinue = TRUE;

    return STATUS_SUCCESS;
}

static
NTSTATUS
SrvShareAddShareInfoInMemory_inlock(
    IN PLWIO_SRV_SHARE_ENTRY_LIST pShareList,
    IN PSRV_SHARE_REPOSITORY_SHARE pRepositoryShare
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSRV_SHARE_ENTRY pShareEntry = NULL;
    PSRV_SHARE_INFO pShareInfo = NULL;

    ntStatus = SrvShareInfoCreate(
                    pRepositoryShare->pwszShareName,
                    pRepositoryShare->pwszPath,
                    pRepositoryShare->pwszComment,
                    pRepositoryShare->pSecurityDescriptor,
                    pRepositoryShare->SecurityDescriptorLength,
                    pRepositoryShare->Service,
                    pRepositoryShare->Flags,
                    &pShareInfo);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SrvAllocateMemory(sizeof(*pShareEntry),
                               OUT_PPVOID(&pShareEntry));
    BAIL_ON_NT_STATUS(ntStatus);

    pShareEntry->pInfo = SrvShareAcquireInfo(pShareInfo);
    pShareEntry->pNext = pShareList->pShareEntry;
    pShareList->pShareEntry = pShareEntry;

    ntStatus = LwRtlRBTreeAdd(
                pShareList->pShareCollection,
                pShareInfo->pwszName,
                pShareInfo);
    BAIL_ON_NT_STATUS(ntStatus);

cleanup:

    return ntStatus;

error:

    if (pShareEntry)
    {
        SrvShareRemoveFromList_inlock(pShareList, pShareInfo->pwszName);
        pShareEntry = NULL;
    }
    else if (pShareInfo)
    {
        SrvShareReleaseInfo(pShareInfo);
    }

    goto cleanup;
}

static
VOID
SrvShareDeleteShareInfoInMemory_inlock(
    IN PLWIO_SRV_SHARE_ENTRY_LIST pShareList,
    IN PSRV_SHARE_INFO pShareInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    BOOLEAN inLock = FALSE;

    LWIO_LOCK_RWMUTEX_EXCLUSIVE(inLock, &pShareInfo->Mutex);
    pShareInfo->DeleteInProgress = TRUE;
    LWIO_UNLOCK_RWMUTEX(inLock, &pShareInfo->Mutex);

    ntStatus = SrvShareRemoveFromList_inlock(pShareList, pShareInfo->pwszName);
    LWIO_ASSERT(!ntStatus);

    ntStatus = LwRtlRBTreeRemove(pShareList->pShareCollection,
                                 pShareInfo->pwszName);
    LWIO_ASSERT(!ntStatus);

    SrvShareDisconnectClients(pShareInfo);
}

static
NTSTATUS
SrvShareUpdateShareInfoInMemory_inlock(
    IN OUT PSRV_SHARE_INFO pShareInfoDst,
    IN PSRV_SHARE_REPOSITORY_SHARE pRepositoryShareSrc
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSRV_SHARE_INFO pShareInfoSrcCopy = NULL;

    LWIO_ASSERT(LwRtlWC16StringIsEqual(pRepositoryShareSrc->pwszShareName,
                                       pShareInfoDst->pwszName, FALSE));

    ntStatus = SrvShareInfoCreate(
                    pRepositoryShareSrc->pwszShareName,
                    pRepositoryShareSrc->pwszPath,
                    pRepositoryShareSrc->pwszComment,
                    pRepositoryShareSrc->pSecurityDescriptor,
                    pRepositoryShareSrc->SecurityDescriptorLength,
                    pRepositoryShareSrc->Service,
                    pRepositoryShareSrc->Flags,
                    &pShareInfoSrcCopy);
    BAIL_ON_NT_STATUS(ntStatus);

    SrvFreeMemory(pShareInfoDst->pwszPath);
    pShareInfoDst->pwszPath = pShareInfoSrcCopy->pwszPath;
    pShareInfoSrcCopy->pwszPath = NULL;

    SrvFreeMemory(pShareInfoDst->pwszComment);
    pShareInfoDst->pwszComment = pShareInfoSrcCopy->pwszComment;
    pShareInfoSrcCopy->pwszComment = NULL;


    SrvShareFreeSecurity(pShareInfoDst);

    pShareInfoDst->pSecDesc = pShareInfoSrcCopy->pSecDesc;
    pShareInfoDst->SecDescLen = pShareInfoSrcCopy->SecDescLen;
    pShareInfoSrcCopy->pSecDesc = NULL;
    pShareInfoSrcCopy->SecDescLen = 0;

    pShareInfoDst->pAbsSecDesc = pShareInfoSrcCopy->pAbsSecDesc;
    pShareInfoSrcCopy->pAbsSecDesc = NULL;


    pShareInfoDst->Service = pShareInfoSrcCopy->Service;
    pShareInfoDst->Flags = pShareInfoSrcCopy->Flags;

cleanup:

    if (pShareInfoSrcCopy)
    {
        SrvShareReleaseInfo(pShareInfoSrcCopy);
    }

    return ntStatus;

error:

    goto cleanup;
}

static
NTSTATUS
SrvShareDuplicateInfo_inlock(
    IN PSRV_SHARE_INFO pShareInfo,
    OUT PSRV_SHARE_INFO* ppShareInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSRV_SHARE_INFO pShareInfoCopy = NULL;

    ntStatus = SrvShareInfoCreate(
                    pShareInfo->pwszName,
                    pShareInfo->pwszPath,
                    pShareInfo->pwszComment,
                    pShareInfo->pSecDesc,
                    pShareInfo->SecDescLen,
                    pShareInfo->Service,
                    pShareInfo->Flags,
                    &pShareInfoCopy);
    BAIL_ON_NT_STATUS(ntStatus);

    *ppShareInfo = pShareInfoCopy;

cleanup:

    return ntStatus;

error:

    *ppShareInfo = NULL;

    if (pShareInfoCopy)
    {
        SrvShareFreeInfo(pShareInfoCopy);
    }

    goto cleanup;
}

static
VOID
SrvShareDisconnectClients(
    IN PSRV_SHARE_INFO pShareInfo
    )
{
    BOOLEAN inLock = FALSE;
    LW_LIST_LINKS treeListCopy = { 0 };
    LW_LIST_LINKS tree2ListCopy = { 0 };

    LwListInit(&treeListCopy);
    LwListInit(&tree2ListCopy);

    LWIO_LOCK_RWMUTEX_EXCLUSIVE(inLock, &pShareInfo->Mutex);

    // Move the lists to the local ones by moving trees from Link to RundownLink
    while (!LwListIsEmpty(&pShareInfo->TreeList))
    {
        PSRV_SHARE_INFO_REGISTRATION pRegistration = LW_STRUCT_FROM_FIELD(
                pShareInfo->TreeList.Next, SRV_SHARE_INFO_REGISTRATION, Link);
        PLWIO_SRV_TREE pTree = LW_STRUCT_FROM_FIELD(
                pRegistration, LWIO_SRV_TREE, ShareInfoRegistration);

        SrvTreeAcquire(pTree);

        LwListRemove(pShareInfo->TreeList.Next);
        LwListInsertTail(&treeListCopy,
                         &pTree->ShareInfoRegistration.RundownLink);
    }

    while (!LwListIsEmpty(&pShareInfo->Tree2List))
    {
        PSRV_SHARE_INFO_REGISTRATION pRegistration = LW_STRUCT_FROM_FIELD(
                pShareInfo->Tree2List.Next, SRV_SHARE_INFO_REGISTRATION, Link);
        PLWIO_SRV_TREE_2 pTree = LW_STRUCT_FROM_FIELD(
                pRegistration, LWIO_SRV_TREE_2, ShareInfoRegistration);

        SrvTree2Acquire(pTree);

        LwListRemove(pShareInfo->Tree2List.Next);
        LwListInsertTail(&tree2ListCopy,
                         &pTree->ShareInfoRegistration.RundownLink);
    }

    LWIO_UNLOCK_RWMUTEX(inLock, &pShareInfo->Mutex);


    // For each tree in the tree lists call SrvTree{2}Rundown
    while (!LwListIsEmpty(&treeListCopy))
    {
        PSRV_SHARE_INFO_REGISTRATION pRegistration = LW_STRUCT_FROM_FIELD(
                treeListCopy.Next, SRV_SHARE_INFO_REGISTRATION, RundownLink);
        PLWIO_SRV_TREE pTree = LW_STRUCT_FROM_FIELD(
                pRegistration, LWIO_SRV_TREE, ShareInfoRegistration);

        SrvTreeRundown(pTree);
        LwListRemove(treeListCopy.Next);
        SrvTreeRelease(pTree);
    }

    while (!LwListIsEmpty(&tree2ListCopy))
    {
        PSRV_SHARE_INFO_REGISTRATION pRegistration = LW_STRUCT_FROM_FIELD(
                tree2ListCopy.Next, SRV_SHARE_INFO_REGISTRATION, RundownLink);
        PLWIO_SRV_TREE_2 pTree = LW_STRUCT_FROM_FIELD(
                pRegistration, LWIO_SRV_TREE_2, ShareInfoRegistration);

        SrvTree2Rundown(pTree);
        LwListRemove(tree2ListCopy.Next);
        SrvTree2Release(pTree);
    }

    LWIO_ASSERT(LwListIsEmpty(&pShareInfo->TreeList));
    LWIO_ASSERT(LwListIsEmpty(&pShareInfo->Tree2List));
}

static
NTSTATUS
SrvShareInfoCreate(
    IN OPTIONAL PWSTR pwszShareName,
    IN OPTIONAL PWSTR pwszSharePath,
    IN OPTIONAL PWSTR pwszShareComment,
    IN OPTIONAL PSECURITY_DESCRIPTOR_RELATIVE pSecDesc,
    IN ULONG SecDescLength,
    IN SHARE_STYPE Service,
    IN ULONG Flags,
    OUT PSRV_SHARE_INFO* ppShareInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSRV_SHARE_INFO pShareInfo = NULL;

    ntStatus = SrvAllocateMemory(sizeof(*pShareInfo), OUT_PPVOID(&pShareInfo));
    BAIL_ON_NT_STATUS(ntStatus);

    pShareInfo->RefCount = 1;

    pthread_rwlock_init(&pShareInfo->Mutex, NULL);
    pShareInfo->pMutex = &pShareInfo->Mutex;

    if (pwszShareName)
    {
        ntStatus = SrvAllocateStringW(pwszShareName, &pShareInfo->pwszName);
        BAIL_ON_NT_STATUS(ntStatus);
    }

    if (pwszSharePath)
    {
        ntStatus = SrvAllocateStringW(pwszSharePath, &pShareInfo->pwszPath);
        BAIL_ON_NT_STATUS(ntStatus);
    }

    if (pwszShareComment)
    {
        ntStatus = SrvAllocateStringW(
                        pwszShareComment,
                        &pShareInfo->pwszComment);
        BAIL_ON_NT_STATUS(ntStatus);
    }

    if (SecDescLength)
    {
        ntStatus = SrvShareSetSecurity(
                        pShareInfo,
                        pSecDesc,
                        SecDescLength);
        BAIL_ON_NT_STATUS(ntStatus);
    }

    pShareInfo->Service = Service;
    pShareInfo->Flags = Flags;
    pShareInfo->DeleteInProgress = FALSE;

    LwListInit(&pShareInfo->TreeList);
    LwListInit(&pShareInfo->Tree2List);

cleanup:

    *ppShareInfo = pShareInfo;

    return ntStatus;

error:

    if (pShareInfo)
    {
        SrvShareFreeInfo(pShareInfo);
        pShareInfo = NULL;
    }

    goto cleanup;
}

static
NTSTATUS
SrvShareReloadCallback(
    PVOID pContext
    )
{
    return SrvShareReloadConfiguration((SRV_SHARES_HANDLE)pContext);
}

static
BOOLEAN
SrvShareValidateShareName(
    PWSTR pwszShareName
    )
{
    PWSTR pCur = NULL;
    BOOLEAN bIsValidShareName = TRUE;
    size_t sShareNameLen = 0;

    sShareNameLen = RtlWC16StringNumChars(pwszShareName);
    if (sShareNameLen > SRV_MAX_SHARE_NAME_LENGTH)
    {
        bIsValidShareName = FALSE;
        goto error;
    }

    for (pCur = pwszShareName; *pCur; pCur++)
    {
        switch (*pCur)
        {
            case '*':
            case '/':
            case '\\':
            case '|':
            case '<':
            case '>':
            case ':':
            case '"':
                bIsValidShareName = FALSE;
                goto error;
            // Home share allows a leading '?'
            case '?':
                if (pCur != pwszShareName)
                {
                    bIsValidShareName = FALSE;
                    goto error;
                }
                break;
            default:
                break;
        }

        if (*pCur<=0x1F)
        {
            bIsValidShareName = FALSE;
            goto error;
        }
    }

error:

   return bIsValidShareName;
}

static
NTSTATUS
SrvShareReloadIsValidRepositoryShare(
    IN PSRV_SHARE_REPOSITORY_SHARE pRepositoryShare,
    OUT PBOOLEAN pIsValid
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    BOOLEAN isValid = FALSE;
    PSTR pszShareName = NULL;

    ntStatus = LwRtlCStringAllocateFromWC16String(
                    &pszShareName,
                    pRepositoryShare->pwszShareName);
    BAIL_ON_NT_STATUS(ntStatus);

    if (!SrvShareValidateShareName(pRepositoryShare->pwszShareName))
    {
        LWIO_LOG_ERROR("Error reloading share %s: the share name is invalid",
                                              pszShareName);
        goto cleanup;
    }

    if (pRepositoryShare->Service != SHARE_STYPE_DISKTREE)
    {
        LWIO_LOG_ERROR("Error reloading share %s: the share type is "
                       "invalid(%d)", pszShareName, pRepositoryShare->Service);
        goto cleanup;
    }

    if (pRepositoryShare->Flags & (~SHARE_INFO_FLAGS_VALID_MASK))
    {
        LWIO_LOG_ERROR("Error reloading share %s: the flags are invalid"
                       "(0x%08x)", pszShareName, pRepositoryShare->Flags);
        goto cleanup;
    }

    isValid = TRUE;

cleanup:

    SRV_SAFE_FREE_MEMORY_AND_RESET(pszShareName);

    *pIsValid = isValid;

    return ntStatus;

error:

    isValid = FALSE;

    goto cleanup;
}

static
NTSTATUS
SrvShareBootstrapIpc_inlock(
    IN PLWIO_SRV_SHARE_ENTRY_LIST pShareList
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    wchar16_t wszIpcShareName[] = {'I', 'P', 'C', '$', 0};
    wchar16_t wszIpcSharePath[] = LWIO_SRV_PIPE_SYSTEM_ROOT_W;
    wchar16_t wszIpcShareComment[] = {'R', 'e', 'm', 'o', 't', 'e', ' ',
                                      'I', 'P', 'C', 0};
    PSRV_SHARE_INFO pShareInfo = NULL;
    PSRV_SHARE_ENTRY pShareEntry = NULL;

    ntStatus = SrvShareFindByName_inlock(
                    pShareList,
                    wszIpcShareName,
                    NULL,
                    NULL,
                    &pShareInfo);
    if (ntStatus == STATUS_SUCCESS)
    {
        SrvShareReleaseInfo(pShareInfo);
        pShareInfo = NULL;

        goto cleanup;
    }
    if (ntStatus != STATUS_NOT_FOUND)
    {
        BAIL_ON_NT_STATUS(ntStatus);
    }
    ntStatus = STATUS_SUCCESS;

    ntStatus = SrvShareInfoCreate(
                    &wszIpcShareName[0],
                    &wszIpcSharePath[0],
                    &wszIpcShareComment[0],
                    NULL,
                    0,
                    SHARE_STYPE_IPC,
                    0,
                    &pShareInfo);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SrvShareSetDefaultSecurity(pShareInfo);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SrvAllocateMemory(sizeof(*pShareEntry),
                               OUT_PPVOID(&pShareEntry));
    BAIL_ON_NT_STATUS(ntStatus);

    pShareEntry->pInfo = SrvShareAcquireInfo(pShareInfo);
    pShareEntry->pNext = pShareList->pShareEntry;
    pShareList->pShareEntry = pShareEntry;

    ntStatus = LwRtlRBTreeAdd(
                pShareList->pShareCollection,
                pShareInfo->pwszName,
                pShareInfo);
    BAIL_ON_NT_STATUS(ntStatus);

cleanup:

    return ntStatus;

error:

    LWIO_LOG_ERROR("Failed to bootstrap default IPC$ shares. [error code: %d]",
                   ntStatus);

    if (pShareEntry)
    {
        SrvShareRemoveFromList_inlock(pShareList, pShareInfo->pwszName);
        pShareEntry = NULL;
    }
    else if (pShareInfo)
    {
        SrvShareReleaseInfo(pShareInfo);
    }

    goto cleanup;
}
