You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1312 lines
33 KiB
1312 lines
33 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
svcshare.c
|
|
|
|
Abstract:
|
|
|
|
This module contains routines for supporting the share APIs in the
|
|
server service, NetShareAdd, NetShareCheck, NetShareDel,
|
|
NetShareEnum, NetShareGetInfo, and NetShareSetInfo.
|
|
|
|
Author:
|
|
|
|
David Treadwell (davidtr) 15-Jan-1991
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "svcshare.tmh"
|
|
#pragma hdrstop
|
|
|
|
#define BugCheckFileId SRV_FILE_SVCSHARE
|
|
|
|
#define DISK_ROOT_NAME_TEMPLATE L"\\DosDevices\\X:\\"
|
|
|
|
//
|
|
// Forward declarations.
|
|
//
|
|
|
|
STATIC
|
|
VOID
|
|
FillShareInfoBuffer (
|
|
IN PSERVER_REQUEST_PACKET Srp,
|
|
IN PVOID Block,
|
|
IN OUT PVOID *FixedStructure,
|
|
IN LPWSTR *EndOfVariableData
|
|
);
|
|
|
|
STATIC
|
|
BOOLEAN
|
|
FilterShares (
|
|
IN PSERVER_REQUEST_PACKET Srp,
|
|
IN PVOID Block
|
|
);
|
|
|
|
STATIC
|
|
ULONG
|
|
SizeShares (
|
|
IN PSERVER_REQUEST_PACKET Srp,
|
|
IN PVOID Block
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, SrvNetShareAdd )
|
|
#pragma alloc_text( PAGE, SrvNetShareDel )
|
|
#pragma alloc_text( PAGE, SrvNetShareEnum )
|
|
#pragma alloc_text( PAGE, SrvNetShareSetInfo )
|
|
#pragma alloc_text( PAGE, FillShareInfoBuffer )
|
|
#pragma alloc_text( PAGE, FilterShares )
|
|
#pragma alloc_text( PAGE, SizeShares )
|
|
#endif
|
|
|
|
|
|
#define FIXED_SIZE_OF_SHARE(level) \
|
|
( (level) == 0 ? sizeof(SHARE_INFO_0) : \
|
|
(level) == 1 ? sizeof(SHARE_INFO_1) : \
|
|
(level) == 2 ? sizeof(SHARE_INFO_2) : \
|
|
(level) == 501 ? sizeof(SHARE_INFO_501) : \
|
|
(level) == 502 ? sizeof(SHARE_INFO_502) : \
|
|
sizeof(SHARE_INFO_1005) )
|
|
|
|
|
|
NTSTATUS
|
|
SrvNetShareAdd (
|
|
IN PSERVER_REQUEST_PACKET Srp,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the NetShareAdd API in the server.
|
|
|
|
Arguments:
|
|
|
|
Srp - a pointer to the server request packet that contains all
|
|
the information necessary to satisfy the request. This includes:
|
|
|
|
INPUT:
|
|
|
|
Name1 - the NT path name of the share.
|
|
|
|
OUTPUT:
|
|
|
|
Parameters.Set.ErrorParameter - if STATUS_INVALID_PARAMETER is
|
|
returned, this contains the index of the parameter in error.
|
|
|
|
Buffer - a pointer to a SHARE_INFO2 structure for the new share.
|
|
|
|
BufferLength - total length of this buffer.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - result of operation to return to the server service.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PSHARE share;
|
|
SHARE_TYPE shareType;
|
|
BOOLEAN isSpecial;
|
|
BOOLEAN isRemovable;
|
|
BOOLEAN isCdrom;
|
|
UNICODE_STRING shareName;
|
|
UNICODE_STRING ntPath;
|
|
UNICODE_STRING dosPath;
|
|
UNICODE_STRING remark;
|
|
PSHARE_INFO_502 shi502;
|
|
PSECURITY_DESCRIPTOR securityDescriptor = NULL;
|
|
PSECURITY_DESCRIPTOR fileSecurityDescriptor = NULL;
|
|
|
|
PAGED_CODE( );
|
|
|
|
//
|
|
// We usually don't return any information about the parameter in
|
|
// error.
|
|
//
|
|
|
|
Srp->Parameters.Set.ErrorParameter = 0;
|
|
|
|
//
|
|
// Convert the offsets in the share data structure to pointers. Also
|
|
// make sure that all the pointers are within the specified buffer.
|
|
//
|
|
|
|
shi502 = Buffer;
|
|
|
|
OFFSET_TO_POINTER( shi502->shi502_netname, shi502 );
|
|
OFFSET_TO_POINTER( shi502->shi502_remark, shi502 );
|
|
OFFSET_TO_POINTER( shi502->shi502_path, shi502 );
|
|
OFFSET_TO_POINTER( shi502->shi502_security_descriptor, shi502 );
|
|
|
|
//
|
|
// Construct the security descriptor pointer by hand, because
|
|
// shi502_permissions is only 32 bits in width.
|
|
//
|
|
|
|
if( shi502->shi502_permissions ) {
|
|
securityDescriptor =
|
|
(PSECURITY_DESCRIPTOR)((PCHAR)shi502 + shi502->shi502_permissions);
|
|
}
|
|
else
|
|
{
|
|
// Connect securityDescriptor is REQUIRED!
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ( !POINTER_IS_VALID( shi502->shi502_netname, shi502, BufferLength ) ||
|
|
!POINTER_IS_VALID( shi502->shi502_remark, shi502, BufferLength ) ||
|
|
!POINTER_IS_VALID( shi502->shi502_path, shi502, BufferLength ) ||
|
|
!POINTER_IS_VALID( securityDescriptor, shi502, BufferLength ) ||
|
|
!POINTER_IS_VALID( shi502->shi502_security_descriptor, shi502, BufferLength ) ) {
|
|
|
|
return STATUS_ACCESS_VIOLATION;
|
|
}
|
|
|
|
|
|
//
|
|
// Check the share type
|
|
//
|
|
|
|
isSpecial = (BOOLEAN)((shi502->shi502_type & STYPE_SPECIAL) != 0);
|
|
|
|
isRemovable = FALSE;
|
|
isCdrom = FALSE;
|
|
|
|
switch ( shi502->shi502_type & ~(STYPE_TEMPORARY|STYPE_SPECIAL) ) {
|
|
case STYPE_CDROM:
|
|
|
|
isCdrom = TRUE; // lack of break is intentional
|
|
|
|
case STYPE_REMOVABLE:
|
|
|
|
isRemovable = TRUE; // lack of break is intentional
|
|
|
|
case STYPE_DISKTREE:
|
|
|
|
shareType = ShareTypeDisk;
|
|
break;
|
|
|
|
case STYPE_PRINTQ:
|
|
|
|
shareType = ShareTypePrint;
|
|
break;
|
|
|
|
case STYPE_IPC:
|
|
|
|
shareType = ShareTypePipe;
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// An illegal share type was passed in.
|
|
//
|
|
|
|
IF_DEBUG(SMB_ERRORS) {
|
|
KdPrint(( "SrvNetShareAdd: illegal share type: %ld\n",
|
|
shi502->shi502_type ));
|
|
}
|
|
|
|
Srp->Parameters.Set.ErrorParameter = SHARE_TYPE_PARMNUM;
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Get pointers to the share name, path, remark, and security
|
|
// descriptor.
|
|
//
|
|
|
|
RtlInitUnicodeString( &shareName, (PWCH)shi502->shi502_netname );
|
|
ntPath = Srp->Name1;
|
|
RtlInitUnicodeString( &dosPath, (PWCH)shi502->shi502_path );
|
|
RtlInitUnicodeString( &remark, (PWCH)shi502->shi502_remark );
|
|
|
|
//
|
|
// If this is level 502, get the file security descriptor
|
|
//
|
|
|
|
if ( Srp->Level == 502 ) {
|
|
|
|
fileSecurityDescriptor = shi502->shi502_security_descriptor;
|
|
|
|
//
|
|
// if the sd is invalid, quit.
|
|
//
|
|
|
|
if ( fileSecurityDescriptor != NULL &&
|
|
!RtlValidSecurityDescriptor( fileSecurityDescriptor) ) {
|
|
|
|
Srp->Parameters.Set.ErrorParameter = SHARE_FILE_SD_PARMNUM;
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Allocate a share block.
|
|
//
|
|
|
|
SrvAllocateShare(
|
|
&share,
|
|
&shareName,
|
|
&ntPath,
|
|
&dosPath,
|
|
&remark,
|
|
securityDescriptor,
|
|
fileSecurityDescriptor,
|
|
shareType
|
|
);
|
|
|
|
if ( share == NULL ) {
|
|
DEBUG KdPrint(( "SrvNetShareAdd: unable to allocate share block\n" ));
|
|
return STATUS_INSUFF_SERVER_RESOURCES;
|
|
}
|
|
|
|
share->SpecialShare = isSpecial;
|
|
share->Removable = isRemovable;
|
|
|
|
//
|
|
// Set the MaxUses field in the share. The CurrentUses field was
|
|
// zeroed by SrvAllocateShare.
|
|
//
|
|
|
|
share->MaxUses = shi502->shi502_max_uses;
|
|
|
|
if ( shareType == ShareTypePrint ) {
|
|
|
|
status = SrvOpenPrinter((PWCH)shi502->shi502_path,
|
|
&share->Type.hPrinter,
|
|
&Srp->ErrorCode
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
SrvFreeShare( share );
|
|
return status;
|
|
}
|
|
|
|
if ( Srp->ErrorCode != NO_ERROR ) {
|
|
SrvFreeShare( share );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mark the share if it is in the DFS
|
|
//
|
|
SrvIsShareInDfs( share, &share->IsDfs, &share->IsDfsRoot );
|
|
|
|
//
|
|
// Ensure that another share with the same name doesn't already
|
|
// exist. Insert the share block in the global share list.
|
|
//
|
|
|
|
ACQUIRE_LOCK( &SrvShareLock );
|
|
|
|
if ( SrvFindShare( &share->ShareName ) != NULL ) {
|
|
|
|
//
|
|
// A share with the same name exists. Clean up and return an
|
|
// error.
|
|
//
|
|
// *** Note that SrvFindShare ignores existing shares that are
|
|
// closing. This allows a new share to be created even if
|
|
// and old share with the same name is in the "twilight
|
|
// zone" between existence and nonexistence because of a
|
|
// stray reference.
|
|
//
|
|
|
|
RELEASE_LOCK( &SrvShareLock );
|
|
|
|
SrvFreeShare( share );
|
|
|
|
Srp->ErrorCode = NERR_DuplicateShare;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Insert the share on the global ordered list.
|
|
//
|
|
|
|
SrvAddShare( share );
|
|
|
|
RELEASE_LOCK( &SrvShareLock );
|
|
|
|
//
|
|
// Is this is a removable type e.g. Floppy or CDROM, fill up the
|
|
// file system name.
|
|
//
|
|
|
|
if ( isRemovable ) {
|
|
|
|
PWSTR fileSystemName;
|
|
ULONG fileSystemNameLength;
|
|
|
|
if ( isCdrom ) {
|
|
|
|
//
|
|
// uses cdfs
|
|
//
|
|
|
|
fileSystemName = StrFsCdfs;
|
|
fileSystemNameLength = sizeof( FS_CDFS ) - sizeof(WCHAR);
|
|
|
|
} else {
|
|
|
|
//
|
|
// assume it's fat
|
|
//
|
|
|
|
fileSystemName = StrFsFat;
|
|
fileSystemNameLength = sizeof( FS_FAT ) - sizeof(WCHAR);
|
|
}
|
|
|
|
|
|
SrvFillInFileSystemName(
|
|
share,
|
|
fileSystemName,
|
|
fileSystemNameLength
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// If this is an administrative disk share, update SrvDiskConfiguration
|
|
// to cause the scavenger thread to check the disk free space. The server
|
|
// service has already verified that the format of the pathname is valid
|
|
// before it allowed the ShareAdd to get this far.
|
|
//
|
|
// We want to skip this if its a \\?\ name
|
|
//
|
|
if( share->SpecialShare && share->ShareType == ShareTypeDisk &&
|
|
share->ShareName.Buffer[1] == L'$' &&
|
|
share->DosPathName.Buffer[0] != L'\\' ) {
|
|
|
|
ACQUIRE_LOCK( &SrvConfigurationLock );
|
|
SrvDiskConfiguration |= (0x80000000 >> (share->DosPathName.Buffer[0] - L'A'));
|
|
RELEASE_LOCK( &SrvConfigurationLock );
|
|
}
|
|
|
|
//
|
|
// Dereference the share block, because we're going to forget
|
|
// its address. (The initial reference count is 2.)
|
|
//
|
|
|
|
SrvDereferenceShare( share );
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // SrvNetShareAdd
|
|
|
|
|
|
NTSTATUS
|
|
SrvNetShareDel (
|
|
IN PSERVER_REQUEST_PACKET Srp,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the NetShareDel API in the server.
|
|
|
|
Arguments:
|
|
|
|
Srp - a pointer to the server request packet that contains all
|
|
the information necessary to satisfy the request. This includes:
|
|
|
|
INPUT:
|
|
|
|
Name1 - name of the share to delete.
|
|
|
|
OUTPUT:
|
|
|
|
None.
|
|
|
|
Buffer - unused.
|
|
|
|
BufferLength - unused.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - result of operation to return to the server service.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSHARE share;
|
|
DWORD AdministrativeDiskBit = 0;
|
|
BOOLEAN isPrintShare = FALSE;
|
|
|
|
PAGED_CODE( );
|
|
|
|
Buffer, BufferLength;
|
|
|
|
//
|
|
// Find the share with the specified name. Note that if a share
|
|
// with the specified name exists but is closing, it will not be
|
|
// found.
|
|
//
|
|
|
|
ACQUIRE_LOCK( &SrvShareLock );
|
|
|
|
share = SrvFindShare( &Srp->Name1 );
|
|
|
|
if ( share == NULL ) {
|
|
|
|
//
|
|
// No share with the specified name exists. Return an error.
|
|
//
|
|
|
|
RELEASE_LOCK( &SrvShareLock );
|
|
|
|
Srp->ErrorCode = NERR_NetNameNotFound;
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure the DFS state for this share is accurate
|
|
//
|
|
SrvIsShareInDfs( share, &share->IsDfs, &share->IsDfsRoot );
|
|
|
|
//
|
|
// If the share really is in the DFS, then do not allow it to be deleted
|
|
//
|
|
if( share->IsDfs == TRUE ) {
|
|
|
|
RELEASE_LOCK( &SrvShareLock );
|
|
|
|
IF_DEBUG( DFS ) {
|
|
KdPrint(("NetShareDel attempted on share in DFS!\n" ));
|
|
}
|
|
|
|
Srp->ErrorCode = NERR_IsDfsShare;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// Don't allow the deletion of IPC$, as behavior is very bad with it gone
|
|
// (Named-pipe traffic doesn't work, so NetAPI's and RPC don't work..)
|
|
if( share->SpecialShare )
|
|
{
|
|
UNICODE_STRING Ipc = { 8, 8, L"IPC$" };
|
|
|
|
if( RtlCompareUnicodeString( &Ipc, &share->ShareName, TRUE ) == 0 )
|
|
{
|
|
RELEASE_LOCK( &SrvShareLock );
|
|
|
|
Srp->ErrorCode = ERROR_ACCESS_DENIED;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
switch( share->ShareType ) {
|
|
case ShareTypePrint:
|
|
//
|
|
// This is a print share
|
|
// Don't close the printer here because we have the ShareLock acquired,
|
|
// and it can cause some strange deadlocks because it involves calling up
|
|
// to usermode, and then calling over to Spooler, etc. This can take a long time
|
|
//
|
|
isPrintShare = TRUE;
|
|
|
|
// Reference the share so it doesn't go away at SrvCloseShare time
|
|
SrvReferenceShare( share );
|
|
break;
|
|
|
|
case ShareTypeDisk:
|
|
//
|
|
// See if this was an administrative disk share
|
|
//
|
|
if( share->SpecialShare && share->DosPathName.Buffer[1] == L'$' ) {
|
|
AdministrativeDiskBit = (0x80000000 >> (share->DosPathName.Buffer[0] - L'A'));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
SrvCloseShare( share );
|
|
|
|
RELEASE_LOCK( &SrvShareLock );
|
|
|
|
// If it was a print share, we need to close the printer now that we've released the lock
|
|
if( isPrintShare )
|
|
{
|
|
SrvClosePrinter( share->Type.hPrinter );
|
|
SrvDereferenceShare( share );
|
|
}
|
|
|
|
//
|
|
// If this was an administrative disk share, update SrvDiskConfiguration
|
|
// to cause the scavenger thread to ignore this disk.
|
|
//
|
|
if( AdministrativeDiskBit ) {
|
|
ACQUIRE_LOCK( &SrvConfigurationLock );
|
|
SrvDiskConfiguration &= ~AdministrativeDiskBit;
|
|
RELEASE_LOCK( &SrvConfigurationLock );
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // SrvNetShareDel
|
|
|
|
|
|
NTSTATUS
|
|
SrvNetShareEnum (
|
|
IN PSERVER_REQUEST_PACKET Srp,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the NetShareEnum API in the server.
|
|
|
|
Arguments:
|
|
|
|
Srp - a pointer to the server request packet that contains all
|
|
the information necessary to satisfy the request. This includes:
|
|
|
|
INPUT:
|
|
|
|
Level - level of information to return, 0, 1, or 2.
|
|
|
|
Parameters.Get.ResumeHandle - share ID to determine where to
|
|
start returning info. We start with the first share with an
|
|
ID greater than this value.
|
|
|
|
OUTPUT:
|
|
|
|
Parameters.Get.EntriesRead - the number of entries that fit in
|
|
the output buffer.
|
|
|
|
Parameters.Get.TotalEntries - the total number of entries that
|
|
would be returned with a large enough buffer.
|
|
|
|
Parameters.Get.TotalBytesNeeded - the buffer size that would be
|
|
required to hold all the entries.
|
|
|
|
Parameters.Get.ResumeHandle - share ID of last share returned.
|
|
|
|
Buffer - a pointer to the buffer for results.
|
|
|
|
BufferLength - the length of this buffer.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - result of operation to return to the server service.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE( );
|
|
|
|
return SrvShareEnumApiHandler(
|
|
Srp,
|
|
Buffer,
|
|
BufferLength,
|
|
FilterShares,
|
|
SizeShares,
|
|
FillShareInfoBuffer
|
|
);
|
|
|
|
} // SrvNetShareEnum
|
|
|
|
|
|
NTSTATUS
|
|
SrvNetShareSetInfo (
|
|
IN PSERVER_REQUEST_PACKET Srp,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the NetShareSetInfo API in the server.
|
|
|
|
Arguments:
|
|
|
|
Srp - a pointer to the server request packet that contains all
|
|
the information necessary to satisfy the request. This includes:
|
|
|
|
INPUT:
|
|
|
|
Name1 - name of the share to set information on.
|
|
|
|
Parameters.Set.Api.ShareInfo.MaxUses - if not 0, a new maximum
|
|
user count. If the current count of users on the share
|
|
exceeds the new value, no check is made, but no new
|
|
tree connects are allowed.
|
|
|
|
OUTPUT:
|
|
|
|
Parameters.Set.ErrorParameter - if ERROR_INVALID_PARAMETER is
|
|
returned, this contains the index of the parameter in error.
|
|
|
|
Buffer - a pointer to a SHARE_INFO_502 structure.
|
|
|
|
BufferLength - length of this buffer.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - result of operation to return to the user.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSHARE share;
|
|
UNICODE_STRING remark;
|
|
PWCH newRemarkBuffer = NULL;
|
|
ULONG maxUses;
|
|
ULONG level;
|
|
PSHARE_INFO_502 shi502;
|
|
PSECURITY_DESCRIPTOR fileSD;
|
|
PSECURITY_DESCRIPTOR securityDescriptor = NULL;
|
|
|
|
PAGED_CODE( );
|
|
|
|
//
|
|
// Convert the offsets in the share data structure to pointers. Also
|
|
// make sure that all the pointers are within the specified buffer.
|
|
//
|
|
|
|
level = Srp->Level;
|
|
|
|
switch( level ) {
|
|
default:
|
|
|
|
shi502 = Buffer;
|
|
|
|
if( shi502->shi502_permissions ) {
|
|
securityDescriptor =
|
|
(PSECURITY_DESCRIPTOR)((PCHAR)shi502 + shi502->shi502_permissions);
|
|
}
|
|
|
|
OFFSET_TO_POINTER( shi502->shi502_netname, shi502 );
|
|
OFFSET_TO_POINTER( shi502->shi502_remark, shi502 );
|
|
OFFSET_TO_POINTER( shi502->shi502_path, shi502 );
|
|
OFFSET_TO_POINTER( shi502->shi502_security_descriptor, shi502 );
|
|
|
|
if ( !POINTER_IS_VALID( shi502->shi502_netname, shi502, BufferLength ) ||
|
|
!POINTER_IS_VALID( shi502->shi502_remark, shi502, BufferLength ) ||
|
|
!POINTER_IS_VALID( shi502->shi502_path, shi502, BufferLength ) ||
|
|
!POINTER_IS_VALID( securityDescriptor, shi502, BufferLength ) ||
|
|
!POINTER_IS_VALID( shi502->shi502_security_descriptor, shi502, BufferLength ) ) {
|
|
|
|
return STATUS_ACCESS_VIOLATION;
|
|
}
|
|
|
|
break;
|
|
|
|
case 1005:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Acquire the lock that protects the share list and attempt to find
|
|
// the correct share.
|
|
//
|
|
|
|
ACQUIRE_LOCK( &SrvShareLock );
|
|
|
|
share = SrvFindShare( &Srp->Name1 );
|
|
|
|
if ( share == NULL ) {
|
|
IF_DEBUG(API_ERRORS) {
|
|
KdPrint(( "SrvNetShareSetInfo: share %wZ not found.\n",
|
|
&Srp->Name1 ));
|
|
}
|
|
RELEASE_LOCK( &SrvShareLock );
|
|
Srp->ErrorCode = NERR_NetNameNotFound;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if( level == 1005 ) {
|
|
|
|
if( share->ShareType != ShareTypeDisk ) {
|
|
Srp->Parameters.Set.ErrorParameter = 0;
|
|
Srp->ErrorCode = ERROR_BAD_DEV_TYPE;
|
|
} else {
|
|
PSHARE_INFO_1005 shi1005 = Buffer;
|
|
|
|
share->CSCState = shi1005->shi1005_flags & CSC_MASK;
|
|
share->ShareProperties = (shi1005->shi1005_flags & ~CSC_MASK);
|
|
|
|
Srp->ErrorCode = 0;
|
|
}
|
|
|
|
RELEASE_LOCK( &SrvShareLock );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Set up local variables.
|
|
//
|
|
|
|
maxUses = Srp->Parameters.Set.Api.ShareInfo.MaxUses;
|
|
|
|
//
|
|
// If a remark was specified, allocate space for a new remark and
|
|
// copy over the remark.
|
|
//
|
|
|
|
if ( ARGUMENT_PRESENT( shi502->shi502_remark ) ) {
|
|
|
|
RtlInitUnicodeString( &remark, shi502->shi502_remark );
|
|
|
|
newRemarkBuffer = ALLOCATE_HEAP_COLD(
|
|
remark.MaximumLength,
|
|
BlockTypeDataBuffer
|
|
);
|
|
|
|
if ( newRemarkBuffer == NULL ) {
|
|
|
|
RELEASE_LOCK( &SrvShareLock );
|
|
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_EXPECTED,
|
|
"SrvNetShareSetInfo: unable to allocate %ld bytes of heap.\n",
|
|
remark.MaximumLength,
|
|
NULL
|
|
);
|
|
|
|
Srp->Parameters.Set.ErrorParameter = SHARE_REMARK_PARMNUM;
|
|
return STATUS_INSUFF_SERVER_RESOURCES;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If a file security descriptor was specified, allocate space for a
|
|
// new SD and copy over the new SD. We do this before setting the
|
|
// MaxUses in case the allocation fails and we have to back out.
|
|
//
|
|
// Don't let a file ACL be specified for admin shares.
|
|
//
|
|
|
|
fileSD = shi502->shi502_security_descriptor;
|
|
|
|
if ( ((level == 502) || (level == SHARE_FILE_SD_INFOLEVEL)) &&
|
|
ARGUMENT_PRESENT( fileSD ) ) {
|
|
|
|
PSECURITY_DESCRIPTOR newFileSD;
|
|
ULONG newFileSDLength;
|
|
|
|
if ( share->SpecialShare || !RtlValidSecurityDescriptor( fileSD ) ) {
|
|
RELEASE_LOCK( &SrvShareLock );
|
|
if ( newRemarkBuffer != NULL) {
|
|
FREE_HEAP( newRemarkBuffer );
|
|
}
|
|
Srp->Parameters.Set.ErrorParameter = SHARE_FILE_SD_PARMNUM;
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
newFileSDLength = RtlLengthSecurityDescriptor( fileSD );
|
|
|
|
newFileSD = ALLOCATE_HEAP_COLD(
|
|
newFileSDLength,
|
|
BlockTypeDataBuffer
|
|
);
|
|
|
|
if ( newFileSD == NULL ) {
|
|
|
|
RELEASE_LOCK( &SrvShareLock );
|
|
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_EXPECTED,
|
|
"SrvNetShareSetInfo: unable to allocate %ld bytes of heap.\n",
|
|
newFileSDLength,
|
|
NULL
|
|
);
|
|
|
|
Srp->Parameters.Set.ErrorParameter = SHARE_FILE_SD_PARMNUM;
|
|
|
|
//
|
|
// Free the remarks buffer allocated
|
|
//
|
|
|
|
if ( newRemarkBuffer != NULL) {
|
|
FREE_HEAP( newRemarkBuffer );
|
|
}
|
|
|
|
return STATUS_INSUFF_SERVER_RESOURCES;
|
|
}
|
|
|
|
ACQUIRE_LOCK( share->SecurityDescriptorLock );
|
|
|
|
//
|
|
// Free the old security descriptor
|
|
//
|
|
|
|
if ( share->FileSecurityDescriptor != NULL ) {
|
|
FREE_HEAP( share->FileSecurityDescriptor );
|
|
}
|
|
|
|
//
|
|
// And set up the new one.
|
|
//
|
|
|
|
share->FileSecurityDescriptor = newFileSD;
|
|
RtlCopyMemory(
|
|
share->FileSecurityDescriptor,
|
|
fileSD,
|
|
newFileSDLength
|
|
);
|
|
|
|
RELEASE_LOCK( share->SecurityDescriptorLock );
|
|
}
|
|
|
|
//
|
|
// Replace the old remark if a new one was specified.
|
|
//
|
|
|
|
if ( newRemarkBuffer != NULL ) {
|
|
|
|
//
|
|
// Free the old remark buffer.
|
|
//
|
|
|
|
if ( share->Remark.Buffer != NULL ) {
|
|
FREE_HEAP( share->Remark.Buffer );
|
|
}
|
|
|
|
//
|
|
// And set up the new one.
|
|
//
|
|
|
|
share->Remark.Buffer = newRemarkBuffer;
|
|
share->Remark.MaximumLength = remark.MaximumLength;
|
|
RtlCopyUnicodeString( &share->Remark, &remark );
|
|
|
|
}
|
|
|
|
//
|
|
// If MaxUses was specified, set the new value.
|
|
//
|
|
|
|
if ( maxUses != 0 ) {
|
|
share->MaxUses = maxUses;
|
|
}
|
|
|
|
//
|
|
// Release the share lock.
|
|
//
|
|
|
|
RELEASE_LOCK( &SrvShareLock );
|
|
|
|
//
|
|
// Set up the error parameter to 0 (no error) and return.
|
|
//
|
|
|
|
Srp->Parameters.Set.ErrorParameter = 0;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // SrvNetShareSetInfo
|
|
|
|
|
|
VOID
|
|
FillShareInfoBuffer (
|
|
IN PSERVER_REQUEST_PACKET Srp,
|
|
IN PVOID Block,
|
|
IN OUT PVOID *FixedStructure,
|
|
IN LPWSTR *EndOfVariableData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine puts a single fixed share structure and, if it fits,
|
|
associated variable data, into a buffer. Fixed data goes at the
|
|
beginning of the buffer, variable data at the end.
|
|
|
|
Arguments:
|
|
|
|
Level - the level of information to copy from the share.
|
|
|
|
Block - the share from which to get information.
|
|
|
|
FixedStructure - where the ine buffer to place the fixed structure.
|
|
This pointer is updated to point to the next available
|
|
position for a fixed structure.
|
|
|
|
EndOfVariableData - the last position on the buffer that variable
|
|
data for this structure can occupy. The actual variable data
|
|
is written before this position as long as it won't overwrite
|
|
fixed structures. It is would overwrite fixed structures, it
|
|
is not written.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PSHARE share = Block;
|
|
PSHARE_INFO_501 shi501 = *FixedStructure;
|
|
PSHARE_INFO_502 shi502 = *FixedStructure;
|
|
PSHARE_INFO_1005 shi1005 = *FixedStructure;
|
|
|
|
PAGED_CODE( );
|
|
|
|
//
|
|
// Update FixedStructure to point to the next structure
|
|
// location.
|
|
//
|
|
|
|
*FixedStructure = (PCHAR)*FixedStructure + FIXED_SIZE_OF_SHARE( Srp->Level );
|
|
ASSERT( (ULONG_PTR)*EndOfVariableData >= (ULONG_PTR)*FixedStructure );
|
|
|
|
//
|
|
// Case on the level to fill in the fixed structure appropriately.
|
|
// We fill in actual pointers in the output structure. This is
|
|
// possible because we are in the server FSD, hence the server
|
|
// service's process and address space.
|
|
//
|
|
// *** This routine assumes that the fixed structure will fit in the
|
|
// buffer!
|
|
//
|
|
// *** Using the switch statement in this fashion relies on the fact
|
|
// that the first fields on the different share structures are
|
|
// identical.
|
|
//
|
|
|
|
switch( Srp->Level ) {
|
|
case 1005:
|
|
shi1005->shi1005_flags = 0;
|
|
|
|
if( share->IsDfs ) {
|
|
shi1005->shi1005_flags |= SHI1005_FLAGS_DFS;
|
|
}
|
|
if( share->IsDfsRoot ) {
|
|
shi1005->shi1005_flags |= SHI1005_FLAGS_DFS_ROOT;
|
|
}
|
|
|
|
shi1005->shi1005_flags |= share->ShareProperties;
|
|
shi1005->shi1005_flags |= share->CSCState;
|
|
break;
|
|
|
|
case 502:
|
|
|
|
ACQUIRE_LOCK_SHARED( share->SecurityDescriptorLock );
|
|
|
|
if ( share->FileSecurityDescriptor != NULL ) {
|
|
|
|
ULONG fileSDLength;
|
|
fileSDLength =
|
|
RtlLengthSecurityDescriptor( share->FileSecurityDescriptor );
|
|
|
|
|
|
//
|
|
// DWord Align
|
|
//
|
|
|
|
*EndOfVariableData = (LPWSTR) ( (ULONG_PTR) ((PCHAR) *EndOfVariableData -
|
|
fileSDLength ) & ~3 );
|
|
|
|
shi502->shi502_security_descriptor = *EndOfVariableData;
|
|
shi502->shi502_reserved = fileSDLength;
|
|
|
|
RtlCopyMemory(
|
|
shi502->shi502_security_descriptor,
|
|
share->FileSecurityDescriptor,
|
|
fileSDLength
|
|
);
|
|
|
|
} else {
|
|
shi502->shi502_security_descriptor = NULL;
|
|
shi502->shi502_reserved = 0;
|
|
}
|
|
|
|
RELEASE_LOCK( share->SecurityDescriptorLock );
|
|
|
|
case 2:
|
|
|
|
//
|
|
// Set level 2 specific fields in the buffer. Since this server
|
|
// can only have user-level security, share permissions are
|
|
// meaningless.
|
|
//
|
|
|
|
shi502->shi502_permissions = 0;
|
|
shi502->shi502_max_uses = share->MaxUses;
|
|
shi502->shi502_current_uses = share->CurrentUses;
|
|
|
|
//
|
|
// Copy the DOS path name to the buffer.
|
|
//
|
|
|
|
SrvCopyUnicodeStringToBuffer(
|
|
&share->DosPathName,
|
|
*FixedStructure,
|
|
EndOfVariableData,
|
|
&shi502->shi502_path
|
|
);
|
|
|
|
//
|
|
// We don't have per-share passwords (share-level security)
|
|
// so set the password pointer to NULL.
|
|
//
|
|
|
|
shi502->shi502_passwd = NULL;
|
|
|
|
// *** Lack of break is intentional!
|
|
|
|
case 501:
|
|
|
|
if( Srp->Level == 501 ) {
|
|
shi501->shi501_flags = share->CSCState;
|
|
}
|
|
|
|
// *** Lack of break is intentional!
|
|
|
|
case 1:
|
|
|
|
//
|
|
// Convert the server's internal representation of share types
|
|
// to the expected format.
|
|
//
|
|
|
|
switch ( share->ShareType ) {
|
|
|
|
case ShareTypeDisk:
|
|
|
|
shi502->shi502_type = STYPE_DISKTREE;
|
|
break;
|
|
|
|
case ShareTypePrint:
|
|
|
|
shi502->shi502_type = STYPE_PRINTQ;
|
|
break;
|
|
|
|
case ShareTypePipe:
|
|
|
|
shi502->shi502_type = STYPE_IPC;
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// This should never happen. It means that somebody
|
|
// stomped on the share block.
|
|
//
|
|
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_UNEXPECTED,
|
|
"FillShareInfoBuffer: invalid share type in share: %ld",
|
|
share->ShareType,
|
|
NULL
|
|
);
|
|
|
|
shi502->shi502_type = 0;
|
|
|
|
}
|
|
|
|
if ( share->SpecialShare ) {
|
|
shi502->shi502_type |= STYPE_SPECIAL;
|
|
}
|
|
//
|
|
// Copy the remark to the buffer. The routine will handle the
|
|
// case where there is no remark on the share and put a pointer
|
|
// to a zero terminator in the buffer.
|
|
//
|
|
// *** We hold the share lock to keep SrvNetShareSetInfo from
|
|
// changing the remark during the copy. (Changing the
|
|
// remark can result in different storage being allocated.)
|
|
//
|
|
|
|
SrvCopyUnicodeStringToBuffer(
|
|
&share->Remark,
|
|
*FixedStructure,
|
|
EndOfVariableData,
|
|
&shi502->shi502_remark
|
|
);
|
|
|
|
// *** Lack of break is intentional!
|
|
|
|
case 0:
|
|
|
|
//
|
|
// Copy the share name to the buffer.
|
|
//
|
|
|
|
SrvCopyUnicodeStringToBuffer(
|
|
&share->ShareName,
|
|
*FixedStructure,
|
|
EndOfVariableData,
|
|
&shi502->shi502_netname
|
|
);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// This should never happen. The server service should have
|
|
// checked for an invalid level.
|
|
//
|
|
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_UNEXPECTED,
|
|
"FillShareInfoBuffer: invalid level number: %ld",
|
|
Srp->Level,
|
|
NULL
|
|
);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} // FillShareInfoBuffer
|
|
|
|
|
|
BOOLEAN
|
|
FilterShares (
|
|
IN PSERVER_REQUEST_PACKET Srp,
|
|
IN PVOID Block
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is intended to be called by SrvEnumApiHandler to check
|
|
whether a particular share should be returned.
|
|
|
|
Arguments:
|
|
|
|
Srp - a pointer to the SRP for the operation. Name1 ("netname"
|
|
on NetShareGetInfo) is used to do the filtering.
|
|
|
|
Block - a pointer to the share to check.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the block should be placed in the output buffer, FALSE
|
|
if it should be passed over.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSHARE share = Block;
|
|
|
|
PAGED_CODE( );
|
|
|
|
//
|
|
// If this share is being deleted, than we should not let it be enumerated
|
|
//
|
|
if( GET_BLOCK_STATE(share) == BlockStateClosing )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If this is an Enum, then we definitely want the share. An Enum
|
|
// leaves the net name blank; a get info sets the name to the share
|
|
// name on which to return info.
|
|
//
|
|
|
|
if ( Srp->Name1.Length == 0 ) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// This is a get info; use the share only if the share name matches
|
|
// the Name1 field of the SRP.
|
|
//
|
|
|
|
return RtlEqualUnicodeString(
|
|
&Srp->Name1,
|
|
&share->ShareName,
|
|
TRUE
|
|
);
|
|
|
|
} // FilterShares
|
|
|
|
|
|
ULONG
|
|
SizeShares (
|
|
IN PSERVER_REQUEST_PACKET Srp,
|
|
IN PVOID Block
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the size the passed-in share would take up in
|
|
an API output buffer.
|
|
|
|
Arguments:
|
|
|
|
Srp - a pointer to the SRP for the operation. Only the Level
|
|
parameter is used.
|
|
|
|
Block - a pointer to the share to size.
|
|
|
|
Return Value:
|
|
|
|
ULONG - The number of bytes the share would take up in the
|
|
output buffer.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSHARE share = Block;
|
|
ULONG shareSize = 0;
|
|
|
|
PAGED_CODE( );
|
|
|
|
switch ( Srp->Level ) {
|
|
case 502:
|
|
ACQUIRE_LOCK_SHARED( share->SecurityDescriptorLock );
|
|
|
|
if ( share->FileSecurityDescriptor != NULL ) {
|
|
|
|
//
|
|
// add 4 bytes for possible padding
|
|
//
|
|
|
|
shareSize = sizeof( ULONG ) +
|
|
RtlLengthSecurityDescriptor( share->FileSecurityDescriptor );
|
|
}
|
|
|
|
RELEASE_LOCK( share->SecurityDescriptorLock );
|
|
|
|
case 2:
|
|
shareSize += SrvLengthOfStringInApiBuffer(&share->DosPathName);
|
|
|
|
case 501:
|
|
case 1:
|
|
shareSize += SrvLengthOfStringInApiBuffer(&share->Remark);
|
|
|
|
case 0:
|
|
shareSize += SrvLengthOfStringInApiBuffer(&share->ShareName);
|
|
|
|
}
|
|
|
|
return ( shareSize + FIXED_SIZE_OF_SHARE( Srp->Level ) );
|
|
|
|
} // SizeShares
|
|
|