/*++

Copyright (c) 1991-1992 Microsoft Corporation

Module Name:

    Share.c

Abstract:

    This module contains support for the Share catagory of APIs for the
    NT server service.

Author:

    David Treadwell (davidtr)    10-Jan-1991

Revision History:

--*/

#include "srvsvcp.h"
#include "ssreg.h"

#include <lmaccess.h>
#include <lmerr.h>
#include <ntddnfs.h>
#include <tstr.h>
#include <netevent.h>
#include <icanon.h>

#include <seopaque.h>
#include <sertlp.h>
#include <sddl.h>

#define SET_ERROR_PARAMETER(a) \
    if ( ARGUMENT_PRESENT( ErrorParameter ) ) { *ErrorParameter = a; }  

//
// Use the same directory separator as the object system uses.
//

#define IS_SLASH_SLASH_NAME( _x )   \
    ( IS_PATH_SEPARATOR( _x[0] ) && \
      IS_PATH_SEPARATOR( _x[1] ) && \
      _x[2] == L'.'              && \
      IS_PATH_SEPARATOR( _x[3] ) )

#define IS_NTPATH_NAME( _x )   \
    ( _x && \
      IS_PATH_SEPARATOR( _x[0] ) && \
      IS_PATH_SEPARATOR( _x[1] ) && \
      _x[2] == L'?'              && \
      IS_PATH_SEPARATOR( _x[3] ) )


GENERIC_MAPPING SrvShareFileGenericMapping = GENERIC_SHARE_FILE_ACCESS_MAPPING;

//
// Local types.
//

PSHARE_DEL_CONTEXT SrvShareDelContextHead = NULL;
CRITICAL_SECTION ShareDelContextMutex;

//
// Forward declarations.
//

PVOID
CaptureShareInfo (
    IN DWORD Level,
    IN PSHARE_INFO_2 Shi2,
    IN DWORD ShareType,
    IN LPWSTR Path,
    IN LPWSTR Remark,
    IN PSECURITY_DESCRIPTOR ConnectSecurityDescriptor,
    IN PSECURITY_DESCRIPTOR FileSecurityDescriptor OPTIONAL,
    OUT PULONG CapturedBufferLength
    );

NET_API_STATUS
DisallowSharedLanmanNetDrives(
    IN PUNICODE_STRING NtSharePath
    );

NET_API_STATUS
ShareAssignSecurityDescriptor(
            IN PSECURITY_DESCRIPTOR PassedSecurityDescriptor,
            OUT PSECURITY_DESCRIPTOR *NewSecurityDescriptor
            );

NET_API_STATUS
ShareEnumCommon (
    IN DWORD Level,
    OUT LPBYTE *Buffer,
    IN DWORD PreferredMaximumLength,
    OUT LPDWORD EntriesRead,
    OUT LPDWORD TotalEntries,
    IN OUT LPDWORD ResumeHandle OPTIONAL,
    IN LPWSTR NetName OPTIONAL
    );

NET_API_STATUS
ShareEnumSticky (
    IN DWORD Level,
    OUT LPBYTE *Buffer,
    IN DWORD PreferredMaximumLength,
    OUT LPDWORD EntriesRead,
    OUT LPDWORD TotalEntries,
    IN OUT LPDWORD ResumeHandle OPTIONAL
    );

ULONG
SizeShares (
    IN ULONG Level,
    IN PSHARE_INFO_502 Shi502
    );

BOOLEAN
ValidSharePath(
    IN LPWSTR SharePath,
    IN BOOL   IsNtPath
    );


NET_API_STATUS NET_API_FUNCTION
NetrShareAdd (
    IN LPWSTR ServerName,
    IN DWORD Level,
    IN LPSHARE_INFO Buffer,
    OUT LPDWORD ErrorParameter
    )
{
    return I_NetrShareAdd( ServerName, Level, Buffer, ErrorParameter, FALSE );
}

NET_API_STATUS NET_API_FUNCTION
I_NetrShareAdd (
    IN LPWSTR ServerName,
    IN DWORD Level,
    IN LPSHARE_INFO Buffer,
    OUT LPDWORD ErrorParameter,
    IN BOOLEAN BypassSecurity
    )

/*++

Routine Description:

    This routine communicates with the server FSD to implement the
    NetShareAdd function.  Only levels 2 and 502 are valid.

Arguments:

    ServerName - name of the server.
    Level - Request level.
    Buffer - Contains the information about the share.  If this is a level
        502 request, will also contain a valid security descriptor in
        self-relative form.
    ErrorParameter - status of the FsControl call.
    BypassSecurity - skip security checks

Return Value:

    NET_API_STATUS - NO_ERROR or reason for failure.

--*/

{
    NET_API_STATUS error;
    NTSTATUS status;
    PSERVER_REQUEST_PACKET srp;
    PVOID capturedBuffer;
    LPWSTR path;
    LPWSTR netName;
    LPWSTR remark;
    ULONG bufferLength;
    UNICODE_STRING dosSharePath;
    UNICODE_STRING ntSharePath;

    PSRVSVC_SECURITY_OBJECT securityObject;
    PSECURITY_DESCRIPTOR connectSecurityDescriptor;
    PSECURITY_DESCRIPTOR fileSecurityDescriptor = NULL;
    PSECURITY_DESCRIPTOR newFileSecurityDescriptor = NULL;

    UINT driveType = DRIVE_FIXED;
    DWORD shareType;

    BOOL isIpc;
    BOOL isAdmin;
    BOOL isDiskAdmin;
    BOOL isPrintShare;
    BOOL isSpecial;
    BOOL isNtPath;
    BOOL isTemporary;
    BOOL FreeFileSecurityDescriptor;

    PSHARE_INFO_2 shi2;
    PSHARE_INFO_502 shi502;

    ServerName;

    //
    // Check that user input buffer is not NULL
    //
    if ( !ARGUMENT_PRESENT( Buffer ) || Buffer->ShareInfo2 == NULL) {
        SET_ERROR_PARAMETER(PARM_ERROR_UNKNOWN);
        return ERROR_INVALID_PARAMETER;
    }

    //
    // Set up for error cleanup.
    //

    srp = NULL;
    dosSharePath.Buffer = NULL;
    ntSharePath.Buffer = NULL;
    capturedBuffer = NULL;
    FreeFileSecurityDescriptor = FALSE;

    //
    // Extract Internal buffer information.
    //

    shi2 = Buffer->ShareInfo2;

    //
    // 502 may contain a security descriptor.
    //

    if ( Level == 502 ) {

        shi502 = (LPSHARE_INFO_502) Buffer->ShareInfo502;
        fileSecurityDescriptor = shi502->shi502_security_descriptor;

        //
        // check the reserved field.  If it is zero, this was called from
        // inside the srvsvc. If not, it was through rpc.
        //

        if ( fileSecurityDescriptor != NULL ) {

            if ( shi502->shi502_reserved != 0 ) {

				if ( !RtlValidRelativeSecurityDescriptor( fileSecurityDescriptor,
														  shi502->shi502_reserved,
														  0 ) ) {
					SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
					error = ERROR_INVALID_PARAMETER;
					goto exit;
				}

                error = ShareAssignSecurityDescriptor(
                                        fileSecurityDescriptor,
                                        &newFileSecurityDescriptor
                                        );

                if ( error != NO_ERROR ) {

                    SS_PRINT(( "NetrShareAdd: ShareAssignSecurityDescriptor "
                                "error: %d\n", error ));

                    SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
                    error = ERROR_INVALID_PARAMETER;
                    goto exit;
                }

                FreeFileSecurityDescriptor = TRUE;

            } else {
				
				if ( !RtlValidSecurityDescriptor( fileSecurityDescriptor ) ) {
					SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
					error = ERROR_INVALID_PARAMETER;
					goto exit;
				}

                newFileSecurityDescriptor = fileSecurityDescriptor;
            }
        } else {
            fileSecurityDescriptor = SsDefaultShareSecurityObject.SecurityDescriptor;
            newFileSecurityDescriptor = fileSecurityDescriptor;
        }

    } else if ( Level != 2 ) {

        //
        // The only valid levels are 2 and 502. 2 is a subset of 502.
        //

        error = ERROR_INVALID_LEVEL;
        goto exit;
    }
    else
    {
        // For level 2, default to the default security descriptor
        fileSecurityDescriptor = SsDefaultShareSecurityObject.SecurityDescriptor;
        newFileSecurityDescriptor = fileSecurityDescriptor;
    }

    //
    // A share name must be specified.
    //

    netName = shi2->shi2_netname;

    if ( (netName == NULL) || (*netName == '\0') ) {
        SET_ERROR_PARAMETER( SHARE_NETNAME_PARMNUM );
        error = ERROR_INVALID_PARAMETER;
        goto exit;
    }   

    //
    // Limit it to NNLEN
    //

    if ( wcslen(netName) > NNLEN ) {
        SET_ERROR_PARAMETER( SHARE_NETNAME_PARMNUM );
        error = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    //
    // Don't allow the creation of shares named "pipe" or "mailslot"
    //
    if( !_wcsnicmp( netName, L"pipe", NNLEN ) || !_wcsnicmp( netName, L"mailslot", NNLEN ) )
    {
        SET_ERROR_PARAMETER( SHARE_NETNAME_PARMNUM );
        error = ERROR_ACCESS_DENIED;
        goto exit;
    }

    //
    // If this is the IPC$ share, or the ADMIN$ share, no path
    // may be specified.  No path is needed for the IPC$ share, while a path
    // is supplied internally for the ADMIN$ share.
    //

    path = shi2->shi2_path;
    remark = shi2->shi2_remark;
    shareType = (shi2->shi2_type & ~(STYPE_TEMPORARY));


    //
    // Figure out which kind of share this is.
    //

    isIpc = (BOOL)(STRICMP( netName, IPC_SHARE_NAME ) == 0);
    isAdmin = (BOOL)(STRICMP( netName, ADMIN_SHARE_NAME ) == 0);
    isTemporary = (BOOL)(shi2->shi2_type & STYPE_TEMPORARY);
    isNtPath = IS_NTPATH_NAME( path );

    // For NTPaths, we only allow disk-style shares
    if( isNtPath && (shareType != STYPE_DISKTREE) )
    {
        SET_ERROR_PARAMETER( SHARE_TYPE_PARMNUM );
        error = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    //
    // We have an administrative disk share if the share name is a drive letter
    // followed by $, and if the path name is the root of that same drive.
    //
    if( wcslen( netName ) == 2 && netName[1] == L'$' &&
        TOUPPER( netName[0] ) >= L'A' && TOUPPER( netName[0]) <= L'Z' &&
        path != NULL && wcslen( path ) == 3 &&
        TOUPPER( path[0] ) == TOUPPER( netName[0] ) &&
        path[1] == L':' && path[2] == L'\\' ) {
        //
        // The share name and path look to be an administrative disk share.
        // If the path refers to a fixed drive, then it really is one.
        //
        // Note that we only autoshare fixed drives now, so any other share setup
        // to a non-fixed drive should not be considered a "DiskAdmin" share.
        isDiskAdmin = (SsGetDriveType( path ) == DRIVE_FIXED);
    } else {
        isDiskAdmin = FALSE;
    }

    isPrintShare = (BOOL)(shareType == STYPE_PRINTQ);

    isSpecial = isIpc || isAdmin || isDiskAdmin;

    if ( isIpc ) {

        if ( path != NULL ) {
            SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM );
            error = ERROR_INVALID_PARAMETER;
            goto exit;
        }
        path = NULL;

        //
        // Let the caller specify a remark if they want.  If they don't,
        // supply a default remark.
        //

        if ( remark == NULL ) {
            remark = SsIPCShareRemark;
        }

        shareType = STYPE_IPC;

    } else if ( isAdmin ) {

        if ( path != NULL ) {
            SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM );
            error = ERROR_INVALID_PARAMETER;
            goto exit;
        }

        //
        // Let the caller specify a remark if they want.  If they don't,
        // supply a default remark.
        //

        if ( remark == NULL ) {
            remark = SsAdminShareRemark;
        }

        shareType = STYPE_DISKTREE;

        //
        // For the ADMIN$ share, we set the path to the system root
        // directory.  We get this from from the kernel via the
        // read-only shared page (USER_SHARED_DATA)
        //

        path = USER_SHARED_DATA->NtSystemRoot;

    } else {

        //
        // For all shares other than IPC$ and ADMIN$, a path must be
        // specified and must not have .. and . as directory names.
        //

        if ( (path == NULL) || (*path == '\0') || !ValidSharePath( path, isNtPath ) ) {
            SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM );
            error = ERROR_INVALID_NAME;
            goto exit;
        }

        //
        // If we've got a disk admin share and they didn't supply a
        //  comment, use the built in one
        //
        if( isDiskAdmin && remark == NULL ) {
            remark = SsDiskAdminShareRemark;
        }
    }

    //
    // The remark must be no longer than MAXCOMMENTSZ.
    //

    if ( (remark != NULL) && (STRLEN(remark) > MAXCOMMENTSZ) ) {
        SET_ERROR_PARAMETER( SHARE_REMARK_PARMNUM );
        error = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    //
    // If the server service is fully started, make sure that the caller
    // is allowed to set share information in the server.  We only do
    // this if the service is started--the default share and configured
    // share creations done during initialization do not need any
    // special access.
    //

    if ( SsData.SsInitialized && BypassSecurity == FALSE ) {

        if ( isSpecial ) {
            securityObject = &SsShareAdminSecurityObject;
        } else if ( isPrintShare ) {
            securityObject = &SsSharePrintSecurityObject;
        } else {
            securityObject = &SsShareFileSecurityObject;
        }

        error = SsCheckAccess( securityObject, SRVSVC_SHARE_INFO_SET );

        if ( error != NO_ERROR ) {
            SET_ERROR_PARAMETER( 0 );
            goto exit;
        }

    }

    //
    // If this is a disk share, make sure that the drive is a type that
    // can be shared.
    //

    if ( (shareType == STYPE_DISKTREE) && !isAdmin ) {

        DWORD pathType;

        //
        // Check the path type.  It should be an absolute directory path.
        // We do not check the path type for Nt Paths
        //

        if( !isNtPath )
        {
            error = NetpPathType(
                               NULL,
                               path,
                               &pathType,
                               0
                               );

            if ( (error != NO_ERROR) || (pathType != ITYPE_PATH_ABSD) ) {
                error = ERROR_INVALID_NAME;
                SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM );
                goto exit;
            }
        }

        driveType = SsGetDriveType( path );

        if ( driveType == DRIVE_REMOVABLE ) {

            shareType = STYPE_REMOVABLE;

        } else if ( driveType == DRIVE_CDROM ) {

            shareType = STYPE_CDROM;

        } else if ( !(driveType == DRIVE_REMOTE &&
                     SsData.ServerInfo599.sv599_enablesharednetdrives) &&
                    driveType != DRIVE_FIXED &&
                    driveType != DRIVE_RAMDISK ) {

            if ( driveType == DRIVE_REMOTE ) {
                error = NERR_RedirectedPath;
            } else {
                error = NERR_UnknownDevDir;
            }
            SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM );
            goto exit;
        }
    }

    //
    // Set up the request packet.
    //

    srp = SsAllocateSrp( );
    if ( srp == NULL ) {
        error = ERROR_NOT_ENOUGH_MEMORY;
        goto exit;
    }
    srp->Level = Level;

    //
    // Get the path name in NT format and put it in the SRP.
    //

    if ( path != NULL ) {

        RtlInitUnicodeString( &dosSharePath, path );

        if ( !RtlDosPathNameToNtPathName_U(
                  dosSharePath.Buffer,
                  &ntSharePath,
                  NULL,
                  NULL ) ) {
            SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM );
            error = ERROR_INVALID_PARAMETER;
            goto exit;
        }

        //
        // If this is a redirected drive, make sure the redir is not
        // LanMan.
        //

        if ( driveType == DRIVE_REMOTE ) {

            error = DisallowSharedLanmanNetDrives( &ntSharePath );

            if ( error != NERR_Success ) {
                SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM );
                goto exit;
            }

        } // if remote drive

        srp->Name1 = ntSharePath;
    }

    //
    // Determine whether this is an admin share and use the appropriate
    // security descriptor.
    //

    if ( isAdmin || isDiskAdmin ) {
        connectSecurityDescriptor = SsShareAdmConnectSecurityObject.SecurityDescriptor;
    } else {
        connectSecurityDescriptor = SsShareConnectSecurityObject.SecurityDescriptor;
    }

    //
    // If this is a disk share, verify that the directory to be shared
    // exists and that the caller has access.  (Don't do the access
    // check during server startup.) Don't check the admin$ share -- we
    // know it exists.  Skip removable type disks.
    //

    if ( !isAdmin &&
         (shareType == STYPE_DISKTREE) &&
         (shi2->shi2_path != NULL) ) {

        OBJECT_ATTRIBUTES objectAttributes;
        IO_STATUS_BLOCK iosb;
        HANDLE handle = INVALID_HANDLE_VALUE;
        NTSTATUS status;

        if ( SsData.SsInitialized && BypassSecurity == FALSE &&
           (error = RpcImpersonateClient(NULL)) != NO_ERROR ) {
                goto exit;
        }

        InitializeObjectAttributes(
            &objectAttributes,
            &ntSharePath,
            OBJ_CASE_INSENSITIVE,
            0,
            NULL
            );

        status = NtOpenFile(
                    &handle,
                    FILE_LIST_DIRECTORY,
                    &objectAttributes,
                    &iosb,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT
                    );

        if( status == STATUS_INVALID_PARAMETER ) {
            status = NtOpenFile(
                    &handle,
                    FILE_LIST_DIRECTORY,
                    &objectAttributes,
                    &iosb,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    FILE_DIRECTORY_FILE
                    );
        }

        if ( SsData.SsInitialized && BypassSecurity == FALSE ) {
            (VOID)RpcRevertToSelf( );
        }

        if ( !NT_SUCCESS(status) ) {

            if ( SsData.SsInitialized || (status != STATUS_ACCESS_DENIED) ) {

                //
                // During startup, if the directory does not
                // exist (renamed/deleted), log an event.
                //

                if ( !SsData.SsInitialized &&
                     ((status == STATUS_OBJECT_NAME_NOT_FOUND) ||
                      (status == STATUS_OBJECT_PATH_NOT_FOUND)) ) {

                    LPWSTR subStrings[2];
                    subStrings[0] = netName;
                    subStrings[1] = shi2->shi2_path;

                    SsLogEvent(
                        EVENT_SRV_CANT_RECREATE_SHARE,
                        2,
                        subStrings,
                        NO_ERROR
                        );

                }

                SET_ERROR_PARAMETER( 0 );
                error = RtlNtStatusToDosError( status );
                goto exit;
            }

        } else if( !IS_SLASH_SLASH_NAME( path ) ) {

            if ( SsData.SsInitialized ) {

                FILE_FS_ATTRIBUTE_INFORMATION fileFsAttributeInformation;

                RtlZeroMemory( &fileFsAttributeInformation, sizeof( fileFsAttributeInformation ));

                status = NtQueryVolumeInformationFile( handle,
                                                       &iosb,
                                                       &fileFsAttributeInformation,
                                                       sizeof( fileFsAttributeInformation ),
                                                       FileFsAttributeInformation
                                                     );

                if( (status == STATUS_SUCCESS || status == STATUS_BUFFER_OVERFLOW) &&
                    !(fileFsAttributeInformation.FileSystemAttributes &
                      FILE_SUPPORTS_REPARSE_POINTS) ) {

                    //
                    // Query the name from the file system.  This is because some
                    // fs like fat uses only upper case oem names.  This can cause
                    // a problem because some oem characters do not have upper case
                    // equivalents and thus get mapped to something funny.
                    //
                    // We do not do this if the filesystem supports reparse points, since
                    //  we will come up with the wrong name!
                    //

                    PFILE_NAME_INFORMATION fileNameInformation;
                    ULONG fileInfoSize;
                    ULONG fileNameLength;

                    fileInfoSize =  sizeof(FILE_NAME_INFORMATION) + SIZE_WSTR( path );

                    fileNameInformation = MIDL_user_allocate( fileInfoSize );

                    if ( fileNameInformation == NULL ) {
                        error = ERROR_NOT_ENOUGH_MEMORY;
                        NtClose( handle );
                        goto exit;
                    }


                    status = NtQueryInformationFile(
                                                handle,
                                                &iosb,
                                                fileNameInformation,
                                                fileInfoSize,
                                                FileNameInformation
                                                );


                    if ( status == STATUS_SUCCESS ) {

                        //
                        // The file name returned is expected to be
                        // 3 characters shorter than the share path length.
                        // These 3 characters are "X", ":", "\0".
                        //
                        // If the lengths do not match, than this could be a mounted
                        // FAT volume on an NTFS drive, so we only copy the necessary data
                        //

                        fileNameLength = fileNameInformation->FileNameLength;

                        if ((fileNameLength+3*sizeof(WCHAR)) <= SIZE_WSTR(path)) {

                            //
                            // Copy the path name
                            //

                            RtlCopyMemory(
                                    (LPBYTE) path + 2*sizeof(WCHAR) + (SIZE_WSTR(path) - (fileNameLength+3*sizeof(WCHAR))),
                                    fileNameInformation->FileName,
                                    fileNameLength
                                    );

                            path[fileNameLength/sizeof(WCHAR)+2+(SIZE_WSTR(path) - (fileNameLength+3*sizeof(WCHAR)))/sizeof(WCHAR)] = L'\0';
                        }
                    }

                    MIDL_user_free( fileNameInformation );
                }
            }

            NtClose( handle );

        } else {

            NtClose( handle );
        }
    }

    //
    // Capture the share data structure passed in.
    //

    if ( isSpecial ) {
        shareType |= STYPE_SPECIAL;
    }
    if ( isTemporary ) {
        shareType |= STYPE_TEMPORARY;
    }

    capturedBuffer = CaptureShareInfo(
                        Level,
                        shi2,
                        shareType,
                        path,
                        remark,
                        connectSecurityDescriptor,
                        newFileSecurityDescriptor,
                        &bufferLength
                        );

    if ( capturedBuffer == NULL ) {
        SET_ERROR_PARAMETER( 0 );
        error = ERROR_NOT_ENOUGH_MEMORY;
        goto exit;
    }

    //
    // Send the request on to the server.
    //

    error = SsServerFsControl(
                FSCTL_SRV_NET_SHARE_ADD,
                srp,
                capturedBuffer,
                bufferLength
                );

    SET_ERROR_PARAMETER( srp->Parameters.Set.ErrorParameter );

    //
    // If the request succeeded, add a value to the Shares key, thus
    // effecting a sticky share.  We only do this if the server is fully
    // started -- the default share and configured share creations done
    // during initialization should not be added to the registry.
    //
    // Don't do this if this is an admin share being [re]created.
    //

    if ( SsData.SsInitialized &&
         (error == NO_ERROR) &&
         !isSpecial &&
         !isTemporary ) {
        SsAddShareToRegistry( shi2, newFileSecurityDescriptor, 0 );
    }

    //
    // If a print share was successfully added, increment the number
    // of print shares and update the exported (announced) server type.
    //

    if ( isPrintShare ) {
        InterlockedIncrement( &SsData.NumberOfPrintShares );
        SsSetExportedServerType( NULL, FALSE, TRUE );
    }

exit:

    //
    // Clean up.  Free the share data structure that was allocated by
    // CaptureShareInfo2.  Free the server request packet.  If there was
    // an NT path name allocated by RtlDosPathNameToNtPathName, free it.
    // If we created ADMIN$, free the system path string and the system
    // path information buffer.
    //

    if (FreeFileSecurityDescriptor) {

        (VOID) RtlDeleteSecurityObject ( &newFileSecurityDescriptor );
    }

    if ( capturedBuffer != NULL ) {
        MIDL_user_free( capturedBuffer );
    }

    if ( srp != NULL ) {
        SsFreeSrp( srp );
    }

    if ( ntSharePath.Buffer != NULL ) {
        RtlFreeUnicodeString( &ntSharePath );
    }
    return error;

} // NetrShareAdd


NET_API_STATUS
NetrShareCheck (
    IN LPWSTR ServerName,
    IN LPWSTR Device,
    OUT LPDWORD Type
    )

/*++

Routine Description:

    This routine implements NetShareCheck by calling NetrShareEnum.

Arguments:

    None.

Return Value:

    NET_API_STATUS - NO_ERROR or reason for failure.

--*/

{
    DWORD totalEntries;
    DWORD entriesRead;
    ULONG i;
    PSHARE_INFO_2 shi2;
    NET_API_STATUS error;
    LPBYTE buffer = NULL;

    ServerName;

    //
    // Call ShareEnumCommon to actually get the information about what
    // the shares are on the server.  We use this routine rather than
    // calling NetrShareEnum because NetShareCheck requires no privilege
    // to execute, and we don't want the security checks in
    // NetrShareEnum.
    //

    error = ShareEnumCommon(
                2,
                &buffer,
                (DWORD)-1,
                &entriesRead,
                &totalEntries,
                NULL,
                NULL
                );

    if ( error != NO_ERROR ) {
        if( buffer ) {
            MIDL_user_free( buffer );
        }
        return error;
    }

    SS_ASSERT( totalEntries == entriesRead );

    //
    // Attempt to find the drive letter in a share's path name.
    //

    for ( shi2 = (PSHARE_INFO_2)buffer, i = 0; i < totalEntries; shi2++, i++ ) {

        if ( shi2->shi2_path != NULL && Device && *Device == *shi2->shi2_path ) {

            //
            // Something on the specified disk is shared--free the buffer
            // and return the type of share.
            //

            *Type = shi2->shi2_type & ~STYPE_SPECIAL;
            MIDL_user_free( buffer );

            return NO_ERROR;
        }
    }

    //
    // Nothing on the specified disk is shared.  Return an error.
    //

    MIDL_user_free( buffer );
    return NERR_DeviceNotShared;

} // NetrShareCheck


NET_API_STATUS NET_API_FUNCTION
NetrShareDel (
    IN LPWSTR ServerName,
    IN LPWSTR NetName,
    IN DWORD Reserved
    )

/*++

Routine Description:

    This routine communicates with the server FSD to implement the
    NetShareDel function.

Arguments:

    None.

Return Value:

    NET_API_STATUS - NO_ERROR or reason for failure.

--*/

{
    NET_API_STATUS error;
    SHARE_DEL_HANDLE handle;

    error = I_NetrShareDelStart( ServerName, NetName, Reserved, &handle, TRUE );

    if ( error == NO_ERROR ) {
        error = NetrShareDelCommit( &handle );
    }

    return error;

} // NetrShareDel

NET_API_STATUS NET_API_FUNCTION
NetrShareDelStart (
    IN LPWSTR ServerName,
    IN LPWSTR NetName,
    IN DWORD Reserved,
    IN PSHARE_DEL_HANDLE ContextHandle
    )
{
    return I_NetrShareDelStart( ServerName, NetName, Reserved, ContextHandle, TRUE );
}


NET_API_STATUS NET_API_FUNCTION
I_NetrShareDelStart (
    IN LPWSTR ServerName,
    IN LPWSTR NetName,
    IN DWORD Reserved,
    IN PSHARE_DEL_HANDLE ContextHandle,
    IN BOOLEAN CheckAccess
    )

/*++

Routine Description:

    This routine implements the first phase of the share deletion
    function, which simply remembers that the specified share is to be
    deleted.  The NetrShareDelCommit function actually deletes the
    share.  This two-phase deletion is used to delete IPC$, which is
    the share used for named pipes, so that RPC can be used to delete
    the IPC$ share without receiving RPC errors.

Arguments:

    None.

Return Value:

    NET_API_STATUS - NO_ERROR or reason for failure.

--*/

{
    NET_API_STATUS error;
    PSHARE_INFO_2 shareInfo = NULL;
    DWORD entriesRead;
    DWORD totalEntries;
    DWORD shareType;
    BOOL isPrintShare;
    BOOL isSpecial;
    PSRVSVC_SECURITY_OBJECT securityObject;
    PSHARE_DEL_CONTEXT context;

    ServerName, Reserved;

    //
    // A share name must be specified.
    //

    if ( (NetName == NULL) || (*NetName == '\0') ) {
        return ERROR_INVALID_PARAMETER;
    }

    //
    // First determine what kind of share is being deleted.
    //

    error = ShareEnumCommon(
                2,
                (LPBYTE *)&shareInfo,
                (DWORD)-1,
                &entriesRead,
                &totalEntries,
                NULL,
                NetName
                );

    if ( error != NO_ERROR ) {
        if( shareInfo ) {
            MIDL_user_free( shareInfo );
        }
        return error;
    }

    if ( entriesRead == 0 ) {
        if( shareInfo ) {
            MIDL_user_free( shareInfo );
        }
        return NERR_NetNameNotFound;
    }

    shareType = shareInfo->shi2_type & ~STYPE_SPECIAL;
    isSpecial = (BOOL)((shareInfo->shi2_type & STYPE_SPECIAL) != 0);

    isPrintShare = (BOOL)(shareType == STYPE_PRINTQ);

    MIDL_user_free( shareInfo );

    //
    // Make sure that the caller is allowed to delete this share.
    //

    if( CheckAccess ) {
        if ( isSpecial ) {
            securityObject = &SsShareAdminSecurityObject;
        } else if ( isPrintShare ) {
            securityObject = &SsSharePrintSecurityObject;
        } else {
            securityObject = &SsShareFileSecurityObject;
        }

        error = SsCheckAccess( securityObject, SRVSVC_SHARE_INFO_SET );

        if ( error != NO_ERROR ) {
            return error;
        }
    }

    //
    // Set up context for the commit phase.
    //

    context = MIDL_user_allocate(
                sizeof(SHARE_DEL_CONTEXT) +
                    wcslen(NetName)*sizeof(WCHAR) + sizeof(WCHAR)
                );

    if ( context == NULL ) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    RtlZeroMemory(
        context,
        sizeof(SHARE_DEL_CONTEXT) +
            wcslen(NetName)*sizeof(WCHAR) + sizeof(WCHAR)
        );

    context->IsPrintShare = isPrintShare;
    context->IsSpecial = isSpecial;

    wcscpy( (LPWSTR)(context + 1), NetName );

    RtlInitUnicodeString( &context->Srp.Name1, (LPWSTR)(context + 1) );

    // Insert it into the context list
    EnterCriticalSection( &ShareDelContextMutex );

    context->Next = SrvShareDelContextHead;
    SrvShareDelContextHead = context;

    LeaveCriticalSection( &ShareDelContextMutex );

    //
    // Return the context pointer as an RPC context handle.
    //

    *ContextHandle = context;

    return NO_ERROR;

} // I_NetrShareDelStart


NET_API_STATUS NET_API_FUNCTION
NetrShareDelCommit (
    IN PSHARE_DEL_HANDLE ContextHandle
    )

/*++

Routine Description:

    This routine implements the second phase of the share deletion
    function, which actually deletes the share.  The first phase,
    NetrShareDelStart simply remembers that the share is to be deleted.
    This two-phase deletion is used to delete IPC$, which is the share
    used for named pipes, so that RPC can be used to delete the IPC$
    share without receiving RPC errors.

Arguments:

    None.

Return Value:

    NET_API_STATUS - NO_ERROR or reason for failure.

--*/

{
    NET_API_STATUS error;
    PSHARE_DEL_CONTEXT context;
    PSHARE_DEL_CONTEXT pSearch;

    //
    // The context handle is a pointer to allocated storage containing
    // the name of the share being deleted and other useful information.
    // Copy the pointer, then clear the context handle.
    //

    if( (ContextHandle == NULL) || (*ContextHandle == NULL) )
    {
        return ERROR_INVALID_PARAMETER;
    }

    context = *ContextHandle;
    *ContextHandle = NULL;

    //
    // Look for this context to validate that its on the list
    //
    EnterCriticalSection( &ShareDelContextMutex );

    pSearch = SrvShareDelContextHead;
    if( pSearch == context )
    {
        SrvShareDelContextHead = pSearch->Next;
        context->Next = NULL;
    }
    else
    {
        while( (pSearch != NULL) && (pSearch->Next != context) )
        {
            pSearch = pSearch->Next;
        }

        if( (pSearch != NULL) && (pSearch->Next == context) )
        {
            pSearch->Next = pSearch->Next->Next;
            context->Next = NULL;
        }
        else
        {
            pSearch = NULL;
        }
    }

    LeaveCriticalSection( &ShareDelContextMutex );

    if( pSearch == NULL )
    {
        return ERROR_INVALID_PARAMETER;
    }


    //
    // Send the request on to the server.
    //

    error =  SsServerFsControl(
                FSCTL_SRV_NET_SHARE_DEL,
                &context->Srp,
                NULL,
                0
                );

    //
    // If the request succeeded, remove the value corresponding to the
    // share from the Shares key, thus effecting a sticky share
    // deletion.
    //
    // We don't do this if this is an admin share being deleted.  No
    // registry information is kept for these shares.
    //

    if ( (error == NO_ERROR) && !context->IsSpecial ) {
        SsRemoveShareFromRegistry( (LPWSTR)(context + 1) );
    }

    //
    // If a print share was successfully deleted, decrement the number
    // of print shares and update the exported (announced) server type.
    //

    if ( context->IsPrintShare ) {
        InterlockedDecrement( &SsData.NumberOfPrintShares );
        SsSetExportedServerType( NULL, FALSE, TRUE );
    }

    //
    // Free the context.
    //

    MIDL_user_free( context );

    return error;

} // NetrShareDelCommit


NET_API_STATUS NET_API_FUNCTION
NetrShareDelSticky (
    IN LPWSTR ServerName,
    IN LPWSTR NetName,
    IN DWORD Reserved
    )

/*++

Routine Description:

    This routine implements the NetShareDelSticky function.  It removes
    the named share from the sticky share list in the registry.  The
    primary use of this function is to delete a sticky share whose
    root directory has been deleted, thus preventing actual recreation
    of the share, but whose entry still exists in the registry.  It can
    also be used to remove the persistence of a share without deleting
    the current incarnation of the share.

Arguments:

    None.

Return Value:

    NET_API_STATUS - NO_ERROR or reason for failure.

--*/

{
    NET_API_STATUS error;
    PSHARE_INFO_2 shareInfo, shi2;
    DWORD entriesRead;
    DWORD totalEntries;
    DWORD shareType;
    ULONG i;
    BOOL isPrintShare;
    PSRVSVC_SECURITY_OBJECT securityObject;

    ServerName, Reserved;


    //
    // A share name must be specified.
    //

    if ( (NetName == NULL) || (*NetName == '\0') ) {
        return ERROR_INVALID_PARAMETER;
    }

    //
    // First determine what kind of share is being deleted.
    //

    error = ShareEnumSticky(
                2,
                (LPBYTE *)&shareInfo,
                (DWORD)-1,
                &entriesRead,
                &totalEntries,
                NULL
                );

    if ( error != NO_ERROR ) {

        return error;

    } else if ( entriesRead == 0 ) {

        return NERR_NetNameNotFound;
    }

    for ( shi2 = shareInfo, i = 0 ; i < entriesRead; i++, shi2++ ) {

        if ( _wcsicmp( shi2->shi2_netname, NetName ) == 0 ) {
            break;
        }
    }

    //
    // Does it exist?
    //

    if ( i == entriesRead ) {
        MIDL_user_free( shareInfo );
        return NERR_NetNameNotFound;
    }

    //
    // Use appropriate security object based on whether it is a print
    // share or not.  Admin shares are not sticky.
    //

    shareType = shi2->shi2_type & ~STYPE_SPECIAL;
    isPrintShare = (BOOL)(shareType == STYPE_PRINTQ);

    MIDL_user_free( shareInfo );

    //
    // Make sure that the caller is allowed to delete this share.
    //

    if ( isPrintShare ) {
        securityObject = &SsSharePrintSecurityObject;
    } else {
        securityObject = &SsShareFileSecurityObject;
    }

    error = SsCheckAccess(
                securityObject,
                SRVSVC_SHARE_INFO_SET
                );

    if ( error != NO_ERROR ) {
        return error;
    }

    //
    // Remove the value corresponding to the share from the Shares key,
    // thus effecting a sticky share deletion.
    //

    error = SsRemoveShareFromRegistry( NetName );

    if ( error == ERROR_FILE_NOT_FOUND ) {
        error = NERR_NetNameNotFound;
    }

    return error;

} // NetrShareDelSticky


NET_API_STATUS NET_API_FUNCTION
NetrShareEnum (
    SRVSVC_HANDLE ServerName,
    LPSHARE_ENUM_STRUCT InfoStruct,
    DWORD PreferredMaximumLength,
    LPDWORD TotalEntries,
    LPDWORD ResumeHandle
    )

/*++

Routine Description:

    This routine communicates with the server FSD to implement the
    NetShareEnum function.

Arguments:

    None.

Return Value:

    NET_API_STATUS - NO_ERROR or reason for failure.

--*/

{
    NET_API_STATUS error;
    ACCESS_MASK desiredAccess;

    ServerName;

    if( !ARGUMENT_PRESENT( InfoStruct ) ) {
        return ERROR_INVALID_PARAMETER;
    }

    //
    // Determine the desired access.
    //

    switch ( InfoStruct->Level ) {

    case 0:
    case 1:
    case 501:
        desiredAccess = SRVSVC_SHARE_USER_INFO_GET;
        break;

    case 2:
    case 502:
        desiredAccess = SRVSVC_SHARE_ADMIN_INFO_GET;
        break;

    default:

        return ERROR_INVALID_LEVEL;
    }

    //
    // Make sure that the caller has the access necessary for this
    // operation.
    //

    error = SsCheckAccess(
                &SsSharePrintSecurityObject,
                desiredAccess
                );

    if ( error != NO_ERROR ) {
        return error;
    }

    //
    // Use the common routine to get the information.
    //

    if( InfoStruct->ShareInfo.Level2 == NULL ||
        InfoStruct->ShareInfo.Level2->Buffer != NULL ) {
        return ERROR_INVALID_PARAMETER;
    }

    return ShareEnumCommon(
               InfoStruct->Level,
               (LPBYTE *)&InfoStruct->ShareInfo.Level2->Buffer,
               PreferredMaximumLength,
               &InfoStruct->ShareInfo.Level2->EntriesRead,
               TotalEntries,
               ResumeHandle,
               NULL
               );

} // NetrShareEnum


NET_API_STATUS NET_API_FUNCTION
NetrShareEnumSticky (
    SRVSVC_HANDLE ServerName,
    LPSHARE_ENUM_STRUCT InfoStruct,
    DWORD PreferredMaximumLength,
    LPDWORD TotalEntries,
    LPDWORD ResumeHandle OPTIONAL
    )

/*++

Routine Description:

    This routine communicates with the server FSD to implement the
    NetShareEnumSticky function.

Arguments:

    ServerName - the name of the server whose shares we want to enumerate.
    InfoStruct - pointer to a PSHARE_ENUM_STRUCT that will contain the
        output buffer upon completion.
    PreferredMaximumLength - an advisory value that specifies the maximum
        number of bytes the client is expecting to be returned. If -1, the
        client expects the whole list to be returned.
    TotalEntries - Upon return, will contain the number of entries that
        were available.
    ResumeHandle - is not NULL, will contain the resume handle that can be
        used to continue a search.

Return Value:

    NET_API_STATUS - NO_ERROR or reason for failure.

--*/

{
    NET_API_STATUS error;
    ACCESS_MASK desiredAccess;

    ServerName;

    if( !ARGUMENT_PRESENT( InfoStruct ) ) {
        return ERROR_INVALID_PARAMETER;
    }

    if( InfoStruct->ShareInfo.Level2 == NULL ||
        InfoStruct->ShareInfo.Level2->Buffer != NULL ) {
        return ERROR_INVALID_PARAMETER;
    }

    //
    // Determine the desired access.
    //

    switch ( InfoStruct->Level ) {

    case 0:
    case 1:
        desiredAccess = SRVSVC_SHARE_USER_INFO_GET;
        break;

    case 2:
    case 502:
        desiredAccess = SRVSVC_SHARE_ADMIN_INFO_GET;
        break;

    default:

        return ERROR_INVALID_LEVEL;
    }

    //
    // Make sure that the caller has the access necessary for this
    // operation.
    //

    error = SsCheckAccess(
                &SsSharePrintSecurityObject,
                desiredAccess
                );

    if ( error != NO_ERROR ) {
        return error;
    }

    //
    // Use the common routine to get the information.
    //

    return ShareEnumSticky(
               InfoStruct->Level,
               (LPBYTE *)&InfoStruct->ShareInfo.Level2->Buffer,
               PreferredMaximumLength,
               &InfoStruct->ShareInfo.Level2->EntriesRead,
               TotalEntries,
               ResumeHandle
               );

} // NetrShareEnumSticky


NET_API_STATUS NET_API_FUNCTION
NetrShareGetInfo (
    IN LPWSTR ServerName,
    IN LPWSTR NetName,
    IN  DWORD Level,
    OUT LPSHARE_INFO Buffer
    )

/*++

Routine Description:

    This routine communicates with the server FSD to implement the
    NetShareGetInfo function.

Arguments:

    None.

Return Value:

    NET_API_STATUS - NO_ERROR or reason for failure.

--*/

{
    NET_API_STATUS error;
    PSHARE_INFO_2 shareInfo = NULL;
    ULONG entriesRead;
    ULONG totalEntries;
    ACCESS_MASK desiredAccess;

    ServerName;

    //
    // A share name must be specified.
    //

    if ( (NetName == NULL) || (*NetName == '\0') ) {
        return ERROR_INVALID_PARAMETER;
    }

    //
    // Determine the desired access.
    //

    switch ( Level ) {

    case 0:
    case 1:
    case 501:
    case 1005:
        desiredAccess = SRVSVC_SHARE_USER_INFO_GET;
        break;

    case 2:
    case 502:
        desiredAccess = SRVSVC_SHARE_ADMIN_INFO_GET;
        break;

    default:

        return ERROR_INVALID_LEVEL;
    }

    //
    // Make sure that the caller has the access necessary for this
    // operation.
    //

    error = SsCheckAccess(
                &SsSharePrintSecurityObject,
                desiredAccess
                );

    if ( error != NO_ERROR ) {
        return error;
    }

    //
    // Use the common routine to get the information.
    //

    error = ShareEnumCommon(
                Level,
                (LPBYTE *)&shareInfo,
                (DWORD)-1,
                &entriesRead,
                &totalEntries,
                NULL,
                NetName
                );

    if ( error != NO_ERROR ) {
        if( shareInfo ) {
            MIDL_user_free( shareInfo );
        }
        return error;
    }
    if ( entriesRead == 0 ) {
        if( shareInfo ) {
            MIDL_user_free( shareInfo );
        }
        return NERR_NetNameNotFound;
    }
    SS_ASSERT( entriesRead == 1 );

    //
    // Make sure that the caller is allowed to get share information on
    // this share.
    //

    if ( Level == 502 ) {
        Buffer->ShareInfo502 = (LPSHARE_INFO_502_I)shareInfo;
    } else {
        Buffer->ShareInfo2 = (LPSHARE_INFO_2)shareInfo;
    }
    return NO_ERROR;

} // NetrShareGetInfo


NET_API_STATUS NET_API_FUNCTION
NetrShareSetInfo (
    IN LPWSTR ServerName,
    IN LPWSTR NetName,
    IN DWORD Level,
    IN LPSHARE_INFO Buffer,
    OUT LPDWORD ErrorParameter OPTIONAL
    )

/*++

Routine Description:

    This routine communicates with the server FSD to implement the
    NetShareSetInfo function.

Arguments:

    None.

Return Value:

    NET_API_STATUS - NO_ERROR or reason for failure.

--*/

{
    NET_API_STATUS error;
    PSERVER_REQUEST_PACKET srp = NULL;
    DWORD entriesRead;
    DWORD totalEntries;
    DWORD shareType;
    BOOL isPrintShare;
    BOOL isSpecial;
    PSRVSVC_SECURITY_OBJECT securityObject;
    PVOID capturedBuffer = NULL;
    ULONG bufferLength;

    LPWSTR remark = NULL;
    ULONG maxUses = 0;
    PSECURITY_DESCRIPTOR fileSd = NULL;
    PSECURITY_DESCRIPTOR newFileSd = NULL;
	ULONG sdLength = 0;

    BOOL setRemark;
    BOOL setFileSd;

    PSHARE_INFO_2 shi2 = NULL;
    SHARE_INFO_2 localShi2;

    ServerName;

    //
    // Check that user input buffer is not NULL
    //
    if (Buffer->ShareInfo2 == NULL) {
        SET_ERROR_PARAMETER(PARM_ERROR_UNKNOWN);
        return ERROR_INVALID_PARAMETER;
    }

    //
    // Determine what the caller is trying to set.
    //

    switch ( Level ) {

    case 502:

        fileSd = Buffer->ShareInfo502->shi502_security_descriptor;
		sdLength = Buffer->ShareInfo502->shi502_reserved;

        // *** lack of break is intentional!

    case 2:

        maxUses = Buffer->ShareInfo2->shi2_max_uses;

        // *** lack of break is intentional!

    case 1:

        remark = Buffer->ShareInfo2->shi2_remark;

        break;

    case SHARE_REMARK_INFOLEVEL:

        remark = Buffer->ShareInfo1004->shi1004_remark;

        break;

    case SHARE_MAX_USES_INFOLEVEL:

        maxUses = Buffer->ShareInfo1006->shi1006_max_uses;

        break;

    case SHARE_FILE_SD_INFOLEVEL:

        fileSd = Buffer->ShareInfo1501->shi1501_security_descriptor;
		sdLength = Buffer->ShareInfo1501->shi1501_reserved;

        break;

    case 1005:
        Buffer->ShareInfo1005->shi1005_flags &= SHI1005_VALID_FLAGS_SET;
        break;

    default:

        SS_PRINT(( "NetrShareSetInfo: invalid level: %ld\n", Level ));
        SET_ERROR_PARAMETER( 0 );
        return ERROR_INVALID_LEVEL;
    }

    setRemark = (BOOLEAN)( remark != NULL );
    setFileSd = (BOOLEAN)( fileSd != NULL );

    //
    // A share name must be specified.
    //

    if ( (NetName == NULL) || (*NetName == '\0') ) {
        return ERROR_INVALID_PARAMETER;
    }

    //
    // Determine what kind of share is being modified.
    //

    error = ShareEnumCommon(
                2,
                (LPBYTE *)&shi2,
                (DWORD)-1,
                &entriesRead,
                &totalEntries,
                NULL,
                NetName
                );
    if ( error != NO_ERROR ) {
        if( shi2 ) {
            MIDL_user_free( shi2 );
        }
        return error;
    }
    if ( entriesRead == 0 ) {
        if( shi2 ) {
            MIDL_user_free( shi2 );
        }
        return NERR_NetNameNotFound;
    }

    shareType = shi2->shi2_type & ~(STYPE_SPECIAL|STYPE_TEMPORARY);
    isSpecial = (BOOL)((shi2->shi2_type & STYPE_SPECIAL) != 0);

    MIDL_user_free( shi2 );

    //
    // The share ACL cannot be changed on admin shares.
    //
    if ( isSpecial && setFileSd ) {
        SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
        error = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    //
    // Figure out which kind of share this is.
    //

    isPrintShare = (BOOL)(shareType == STYPE_PRINTQ);

    //
    // Only disk shares can be affected by 1005
    //
    if( Level == 1005 && shareType != STYPE_DISKTREE ) {
        error = ERROR_BAD_DEV_TYPE;
        goto exit;
    }

    if( SsData.SsInitialized ) {

        //
        // Make sure that the caller is allowed to set share information on
        // this share.
        //

        if ( isSpecial ) {
            securityObject = &SsShareAdminSecurityObject;
        } else if ( isPrintShare ) {
            securityObject = &SsSharePrintSecurityObject;
        } else {
            securityObject = &SsShareFileSecurityObject;
        }

        error = SsCheckAccess( securityObject, SRVSVC_SHARE_INFO_SET );

        if ( error != NO_ERROR ) {
            return error;
        }
    }

    //
    // Just return success if not trying to set anything.
    //

    if ( !setRemark && (maxUses == 0) && !setFileSd && Level != 1005 ) {
        return NO_ERROR;
    }

    //
    // The remark must be no longer than MAXCOMMENTSZ.
    //

    if ( setRemark ) {
        if ( STRLEN(remark) > MAXCOMMENTSZ ) {
            SET_ERROR_PARAMETER( SHARE_REMARK_PARMNUM );
            return ERROR_INVALID_PARAMETER;
        }
    }

    //
    // Mapped the security descriptor to remove the generic permissions
    //

    if ( setFileSd ) {

        if ( !RtlValidRelativeSecurityDescriptor( fileSd,
												  sdLength,
												  0 ) ) {
            SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
            return ERROR_INVALID_PARAMETER;
        }

        error = ShareAssignSecurityDescriptor(
                                fileSd,
                                &newFileSd
                                );

        if ( error != NO_ERROR ) {
            SS_PRINT(( "NetrShareSetInfo: ShareAssignSecurityDescriptor "
                        "error: %d\n", error ));
            SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
            return ERROR_INVALID_PARAMETER;
        }
    }

    //
    // Allocate a request packet.
    //

    srp = SsAllocateSrp( );
    if ( srp == NULL ) {
        error = ERROR_NOT_ENOUGH_MEMORY;
        goto exit;
    }

    srp->Level = Level;

    //
    // Set up the share name.
    //

    RtlInitUnicodeString( &srp->Name1, NetName );

    //
    // Set up the MaxUses field.  If equal to 0, then it won't be changed
    // by the server.
    //

    srp->Parameters.Set.Api.ShareInfo.MaxUses = maxUses;

    //
    // Capture the share data structure passed in.
    //

    localShi2.shi2_netname = NetName;

    switch( Level ) {
    case 1005:
        bufferLength = sizeof(SHARE_INFO_1005);

        capturedBuffer = MIDL_user_allocate( bufferLength );
        if( capturedBuffer == NULL ) {
            SET_ERROR_PARAMETER( 0 );
            error = ERROR_NOT_ENOUGH_MEMORY;
            goto exit;
        }

        RtlZeroMemory( capturedBuffer, bufferLength );

        *((PSHARE_INFO_1005) capturedBuffer) = *Buffer->ShareInfo1005;
        break;

    default:
        capturedBuffer = CaptureShareInfo(
                            Level,
                            &localShi2,
                            0,      // ShareType, unused for SHARE_SET_INFO
                            NULL,
                            remark,
                            NULL,
                            newFileSd,
                            &bufferLength
                            );

        if ( capturedBuffer == NULL ) {
            SET_ERROR_PARAMETER( 0 );
            error = ERROR_NOT_ENOUGH_MEMORY;
            goto exit;
        }

        break;
    }

    //
    // Send the request to the server.
    //

    error = SsServerFsControl(
                FSCTL_SRV_NET_SHARE_SET_INFO,
                srp,
                capturedBuffer,
                bufferLength
                );

    //
    // If the request succeeded, modify the share's value in the Shares
    // key, thus effecting a sticky change.
    //
    // We don't do this if this is an admin share being modified.  No
    // registry information is kept for these shares.
    //

    if ( (error == NO_ERROR) && !isSpecial ) {

        DWORD entriesRead;
        DWORD totalEntries;
        NET_API_STATUS error2;

        shi2 = NULL;

        error2 = ShareEnumCommon(
                    2,
                    (LPBYTE *)&shi2,
                    (DWORD)-1,
                    &entriesRead,
                    &totalEntries,
                    NULL,
                    NetName
                    );

        if ( error2 == NO_ERROR ) {

            DWORD CSCFlags = 0;

            if( Level != 1005 ) {
                PSHARE_INFO_501 shi501 = NULL;

                if( ShareEnumCommon(
                            501,
                            (LPBYTE *)&shi501,
                            (DWORD)-1,
                            &entriesRead,
                            &totalEntries,
                            NULL,
                            NetName
                            ) == NO_ERROR && shi501 != NULL ) {

                    CSCFlags = shi501->shi501_flags & SHI1005_VALID_FLAGS_SET;
                }

                if( shi501 ) {
                    MIDL_user_free( shi501 );
                }

            } else {

                CSCFlags = Buffer->ShareInfo1005->shi1005_flags & SHI1005_VALID_FLAGS_SET;
            }

            SsAddShareToRegistry( shi2,
                                  newFileSd,
                                  CSCFlags
                                );

        }

        if( shi2 ) {
            MIDL_user_free( shi2 );
        }

    }

    //
    // Set up the error parameter if requested and return.
    //

    SET_ERROR_PARAMETER( srp->Parameters.Set.ErrorParameter );


exit:

    if (srp != NULL) {
        SsFreeSrp( srp );
    }

    if ( newFileSd != NULL ) {
        (VOID)RtlDeleteSecurityObject( &newFileSd );
    }

    if( capturedBuffer != NULL ) {
        MIDL_user_free( capturedBuffer );
    }

    return error;

} // NetrShareSetInfo


PVOID
CaptureShareInfo (
    IN DWORD Level,
    IN PSHARE_INFO_2 Shi2,
    IN DWORD ShareType OPTIONAL,
    IN LPWSTR Path OPTIONAL,
    IN LPWSTR Remark OPTIONAL,
    IN PSECURITY_DESCRIPTOR ConnectSecurityDescriptor OPTIONAL,
    IN PSECURITY_DESCRIPTOR FileSecurityDescriptor OPTIONAL,
    OUT PULONG CapturedBufferLength
    )

{
    PSHARE_INFO_502 capturedShi502;
    ULONG capturedBufferLength;
    PCHAR variableData;
    ULONG pathNameLength;
    ULONG shareNameLength;
    ULONG remarkLength;
    ULONG connectSDLength = 0;
    ULONG fileSdLength = 0;

    //
    // Determine the lengths of the strings in the buffer and the total
    // length of the buffer.
    //

    if ( Shi2->shi2_netname == NULL ) {
        shareNameLength = 0;
    } else {
        shareNameLength = SIZE_WSTR( Shi2->shi2_netname );
    }

    if ( Path == NULL ) {
        pathNameLength = 0;
    } else {
        pathNameLength = SIZE_WSTR( Path );
    }

    if ( Remark == NULL ) {
        remarkLength = 0;
    } else {
        remarkLength = SIZE_WSTR( Remark );
    }

    if ( ARGUMENT_PRESENT( ConnectSecurityDescriptor ) ) {

        //
        // Allocate extra space for the security descriptor since it needs
        // to be longword-aligned and there may be padding in front of it.
        //

        connectSDLength =
            RtlLengthSecurityDescriptor( ConnectSecurityDescriptor ) +
            sizeof(ULONG);
    }

    if ( ARGUMENT_PRESENT( FileSecurityDescriptor ) ) {
        //
        //  ULONG added for alignment.
        //

        fileSdLength = RtlLengthSecurityDescriptor( FileSecurityDescriptor ) +
                       sizeof(ULONG);
    }

    //
    // Allocate a buffer in which to capture the share information.
    //

    capturedBufferLength = sizeof(SHARE_INFO_502) +
                               shareNameLength +
                               remarkLength +
                               pathNameLength +
                               connectSDLength +
                               fileSdLength;

    //
    // Allocate a buffer to hold the input information.
    //

    capturedShi502 = MIDL_user_allocate( capturedBufferLength );

    if ( capturedShi502 == NULL ) {
        *CapturedBufferLength = 0;
        return NULL;
    }

    //
    // Copy over the share info structure.
    //

    *((PSHARE_INFO_2) capturedShi502) = *Shi2;

    //
    // Optionally override the share type.
    //

    if ( ShareType != 0 ) {
        capturedShi502->shi502_type = ShareType;
    }

    //
    // Capture the share name.
    //

    variableData = (PCHAR)( capturedShi502 + 1 );

    if ( shareNameLength != 0 ) {
        capturedShi502->shi502_netname = (LPWSTR)variableData;
        RtlCopyMemory( variableData, Shi2->shi2_netname, shareNameLength );
        variableData += shareNameLength;
    } else {
        capturedShi502->shi502_netname = NULL;
    }

    //
    // Capture the remark.
    //

    if ( remarkLength != 0 ) {
        capturedShi502->shi502_remark = (LPWSTR)variableData;
        RtlCopyMemory( variableData, Remark, remarkLength );
        variableData += remarkLength;
    } else {
        capturedShi502->shi502_remark = NULL;
    }

    //
    // Capture the path.
    //

    if ( pathNameLength > 0 ) {
        capturedShi502->shi502_path = (LPWSTR)variableData;
        RtlCopyMemory( variableData, Path, pathNameLength );
        variableData += pathNameLength;
    } else {
        capturedShi502->shi502_path = NULL;
    }

    //
    // Capture the security descriptor.  Use the shi502_permissions field
    // to contain the offset to the security descriptor in the buffer.
    //

    if ( ARGUMENT_PRESENT( ConnectSecurityDescriptor ) ) {

        variableData = (PCHAR)( ((ULONG_PTR)variableData + 3) & ~3 );

        //
        // Store the offset directly into shi502_permissions now.  The
        // reason is that shi502_permissions is a 32-bit field, insufficient
        // to contain a pointer under Sundown.
        //

        capturedShi502->shi502_permissions = (ULONG)((ULONG_PTR)variableData -
                                                     (ULONG_PTR)capturedShi502);

        RtlCopyMemory(
            variableData,
            ConnectSecurityDescriptor,
            connectSDLength - sizeof(ULONG)
            );

        variableData += (connectSDLength - sizeof(ULONG));

    } else {
        capturedShi502->shi502_permissions = 0;
    }

    //
    // Capture the self relative form of the file security descriptor.
    //

    if ( ARGUMENT_PRESENT( FileSecurityDescriptor ) ) {

        variableData = (PCHAR)( ((ULONG_PTR)variableData + 3) & ~3 );
        capturedShi502->shi502_security_descriptor = (LPBYTE) variableData;
        variableData += ( fileSdLength - sizeof(ULONG)) ;

        RtlCopyMemory(
            (PVOID)capturedShi502->shi502_security_descriptor,
            FileSecurityDescriptor,
            fileSdLength - sizeof(ULONG)
            );

    } else {
        capturedShi502->shi502_security_descriptor = (LPBYTE) NULL;
    }


    //
    // Convert all the pointers in the structure to offsets from the
    // beginning of the structure.
    //

    POINTER_TO_OFFSET( capturedShi502->shi502_netname, capturedShi502 );
    POINTER_TO_OFFSET( capturedShi502->shi502_remark, capturedShi502 );
    POINTER_TO_OFFSET( capturedShi502->shi502_path, capturedShi502 );
    POINTER_TO_OFFSET( (PCHAR)capturedShi502->shi502_security_descriptor, capturedShi502 );

    //
    // Set up the length of the captured buffer to return to the caller
    // and return the captures structure.
    //

    *CapturedBufferLength = capturedBufferLength;

    return capturedShi502;

} // CaptureShareInfo

NET_API_STATUS
DisallowSharedLanmanNetDrives(
    IN PUNICODE_STRING NtSharePath
    )
{
    NET_API_STATUS error = NERR_Success;
    NTSTATUS status;
    HANDLE linkHandle;
    OBJECT_ATTRIBUTES objAttr;
    UNICODE_STRING linkTarget;
    ULONG returnedLength = 0;
    UNICODE_STRING tempNtPath;

    linkTarget.Buffer = NULL;
    linkTarget.MaximumLength = 0;
    linkTarget.Length = 0;
    tempNtPath = *NtSharePath;

    //
    // Remove the trailing '\\'
    //

    tempNtPath.Length -= 2;

    InitializeObjectAttributes(
        &objAttr,
        &tempNtPath,
        OBJ_CASE_INSENSITIVE,
        0,
        NULL
        );

    status = NtOpenSymbolicLinkObject(
                                &linkHandle,
                                SYMBOLIC_LINK_QUERY,
                                &objAttr
                                );

    if ( !NT_SUCCESS(status) ) {
        return NERR_Success;
    }

    //
    // Get the size of the buffer needed.
    //

    status = NtQuerySymbolicLinkObject(
                                linkHandle,
                                &linkTarget,
                                &returnedLength
                                );

    if ( !NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL ) {
        NtClose( linkHandle );
        return NERR_Success;
    }

    //
    // Allocate our buffer
    //

    linkTarget.Length = (USHORT)returnedLength;
    linkTarget.MaximumLength = (USHORT)(returnedLength + sizeof(WCHAR));
    linkTarget.Buffer = MIDL_user_allocate( linkTarget.MaximumLength );

    if ( linkTarget.Buffer == NULL ) {
        NtClose( linkHandle );
        return NERR_Success;
    }

    status = NtQuerySymbolicLinkObject(
                                linkHandle,
                                &linkTarget,
                                &returnedLength
                                );

    NtClose( linkHandle );

    if ( NT_SUCCESS(status) ) {

        //
        // See if this is a lanman drive
        //

        if (_wcsnicmp(
                linkTarget.Buffer,
                DD_NFS_DEVICE_NAME_U,
                wcslen(DD_NFS_DEVICE_NAME_U)) == 0) {

            error = NERR_RedirectedPath;
        }
    }

    MIDL_user_free( linkTarget.Buffer );

    return(error);

} // DisallowSharedLanmanNetDrives

NET_API_STATUS
FillStickyShareInfo(
        IN PSRVSVC_SHARE_ENUM_INFO ShareEnumInfo,
        IN PSHARE_INFO_502 Shi502
        )

/*++

Routine Description:

    This routine fills in the output buffer with data from the shi502
    structure.

Arguments:

    ShareEnumInfo - contains the parameters passed in through the
        NetShareEnumSticky api.
    Shi502 - pointer to a shi502 structure

Return Value:

    status of operation.

--*/

{

    PSHARE_INFO_502 newShi502;
    PCHAR endOfVariableData;

    ShareEnumInfo->TotalBytesNeeded += SizeShares(
                                            ShareEnumInfo->Level,
                                            Shi502
                                            );


    //
    // If we have more data but ran out of space, return ERROR_MORE_DATA
    //

    if ( ShareEnumInfo->TotalBytesNeeded >
            ShareEnumInfo->OutputBufferLength ) {
        return(ERROR_MORE_DATA);
    }

    //
    // Transfer data from the share info 502 structure to the output
    // buffer.
    //

    newShi502 = (PSHARE_INFO_502)ShareEnumInfo->StartOfFixedData;
    ShareEnumInfo->StartOfFixedData += FIXED_SIZE_OF_SHARE(ShareEnumInfo->Level);

    endOfVariableData = ShareEnumInfo->EndOfVariableData;

    //
    // 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( ShareEnumInfo->Level ) {

    case 502:

        if ( Shi502->shi502_security_descriptor != NULL ) {

            ULONG fileSDLength;
            fileSDLength = RtlLengthSecurityDescriptor(
                                Shi502->shi502_security_descriptor
                                );

            //
            // DWord Align
            //

            endOfVariableData = (PCHAR) ( (ULONG_PTR) ( endOfVariableData -
                            fileSDLength ) & ~3 );

            newShi502->shi502_security_descriptor = endOfVariableData;
            newShi502->shi502_reserved  = fileSDLength;

            RtlMoveMemory(
                    newShi502->shi502_security_descriptor,
                    Shi502->shi502_security_descriptor,
                    fileSDLength
                    );

        } else {
            newShi502->shi502_security_descriptor = NULL;
            newShi502->shi502_reserved = 0;
        }

    case 2:

        //
        // Set level 2 specific fields in the buffer.  Since this server
        // can only have user-level security, share permissions are
        // meaningless.
        //

        newShi502->shi502_permissions = 0;
        newShi502->shi502_max_uses = Shi502->shi502_max_uses;

        //
        // To get the current uses, we need to query the server for this
        //

        {
            PSHARE_INFO_2 shareInfo = NULL;
            NET_API_STATUS error;
            DWORD entriesRead;
            DWORD totalEntries;

            error = ShareEnumCommon(
                        2,
                        (LPBYTE *)&shareInfo,
                        (DWORD)-1,
                        &entriesRead,
                        &totalEntries,
                        NULL,
                        Shi502->shi502_netname
                        );

            if ( error != NO_ERROR || entriesRead == 0 ) {
                newShi502->shi502_current_uses = 0;
            } else {
                newShi502->shi502_current_uses = shareInfo->shi2_current_uses;
            }

            if( shareInfo ) {
                MIDL_user_free( shareInfo );
            }
        }

        //
        // Copy the DOS path name to the buffer.
        //

        if ( Shi502->shi502_path != NULL ) {
            endOfVariableData -= SIZE_WSTR( Shi502->shi502_path );
            newShi502->shi502_path = (LPTSTR) endOfVariableData;
            wcscpy( newShi502->shi502_path, Shi502->shi502_path );
        } else {
            newShi502->shi502_path = NULL;
        }

        //
        // We don't have per-share passwords (share-level security)
        // so set the password pointer to NULL.
        //

        newShi502->shi502_passwd = NULL;

        // *** Lack of break is intentional!

    case 1:

        newShi502->shi502_type = Shi502->shi502_type;

        //
        // 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.
        //

        if ( Shi502->shi502_remark != NULL ) {
            endOfVariableData -= SIZE_WSTR( Shi502->shi502_remark );
            newShi502->shi502_remark = (LPTSTR) endOfVariableData;
            wcscpy( newShi502->shi502_remark, Shi502->shi502_remark );
        } else {
            newShi502->shi502_remark = NULL;
        }

        // *** Lack of break is intentional!

    case 0:

        //
        // Copy the share name to the buffer.
        //

        if ( Shi502->shi502_netname != NULL ) {
            endOfVariableData -= SIZE_WSTR( Shi502->shi502_netname );
            newShi502->shi502_netname = (LPTSTR) endOfVariableData;
            wcscpy( newShi502->shi502_netname, Shi502->shi502_netname );
        } else {
            newShi502->shi502_remark = NULL;
        }
        break;
    }

    ShareEnumInfo->EndOfVariableData = endOfVariableData;

    return NO_ERROR;

} // FillStickyShareInfo


NET_API_STATUS
ShareAssignSecurityDescriptor(
    IN PSECURITY_DESCRIPTOR PassedSecurityDescriptor,
    OUT PSECURITY_DESCRIPTOR *NewSecurityDescriptor
    )

/*++

Routine Description:

    This routine converts a the generic mappings in an sd to
    standards and specifics.

Arguments:

    PassedSecurityDescriptor - Security descriptor passed from the client.
    NewSecurityDescriptor - Pointer to a buffer to receive the new sd.

Return Value:

    NET_API_STATUS - NO_ERROR or reason for failure.

--*/

{

    NTSTATUS status;
    HANDLE token;
    PISECURITY_DESCRIPTOR_RELATIVE trustedSecurityDescriptor = NULL;
    NET_API_STATUS error;
    ULONG secLen;

    //
    // We don't necessarily trust the security descriptor passed in from the client.
    //  And, since we are going to write to it, we better make sure it is in some
    //  memory that we understand.
    //
    try {
        // We only work with Self-Relative SD's, reject any others
        if( !RtlpAreControlBitsSet((PISECURITY_DESCRIPTOR)PassedSecurityDescriptor, SE_SELF_RELATIVE) )
        {
            return ERROR_INVALID_PARAMETER;
        }

        secLen = RtlLengthSecurityDescriptor( PassedSecurityDescriptor );

        if( secLen < sizeof( *trustedSecurityDescriptor ) ) {
            RaiseException( STATUS_INVALID_PARAMETER, 0, 0, NULL );
        }

        trustedSecurityDescriptor = (PISECURITY_DESCRIPTOR_RELATIVE)MIDL_user_allocate( secLen );
        if( trustedSecurityDescriptor == NULL ) {
            RaiseException( STATUS_INSUFFICIENT_RESOURCES, 0, 0, NULL );
        }

        RtlCopyMemory( trustedSecurityDescriptor, PassedSecurityDescriptor, secLen );

        trustedSecurityDescriptor->Owner = 0;
        trustedSecurityDescriptor->Group = 0;
        trustedSecurityDescriptor->Sacl = 0;
        trustedSecurityDescriptor->Control &=
                (SE_DACL_DEFAULTED | SE_DACL_PRESENT | SE_SELF_RELATIVE | SE_DACL_PROTECTED);

        //
        // Impersonate client
        //

        status = RpcImpersonateClient( NULL );

        if( !NT_SUCCESS( status ) ) {
            RaiseException( status, 0, 0, NULL );
        }

        status = NtOpenThreadToken(
                        NtCurrentThread(),
                        TOKEN_QUERY,
                        TRUE,
                        &token
                        );

        (VOID)RpcRevertToSelf( );

        if( !NT_SUCCESS( status ) ) {
            RaiseException( status, 0, 0, NULL );
        }

        //
        // Get a new sd which has the generics mapped to specifics.
        // the returned sd is in self-relative form.
        //

        status = RtlNewSecurityObject(
                                    NULL,
                                    trustedSecurityDescriptor,
                                    NewSecurityDescriptor,
                                    FALSE,
                                    token,
                                    &SrvShareFileGenericMapping
                                    );

        ASSERT( RtlValidSecurityDescriptor( *NewSecurityDescriptor ) );

        NtClose( token );

    } except ( EXCEPTION_EXECUTE_HANDLER ) {

        status = GetExceptionCode();
    }

    if( trustedSecurityDescriptor != NULL ) {
        MIDL_user_free( trustedSecurityDescriptor );
    }

    return RtlNtStatusToDosError( status );

} // ShareAssignSecurityDescriptor


void
SHARE_DEL_HANDLE_rundown (
    SHARE_DEL_HANDLE ContextHandle
    )
{
    (VOID)NetrShareDelCommit( &ContextHandle );

    return;

} // SHARE_DEL_HANDLE_rundown


NET_API_STATUS
ShareEnumCommon (
    IN DWORD Level,
    OUT LPBYTE *Buffer,
    IN DWORD PreferredMaximumLength,
    OUT LPDWORD EntriesRead,
    OUT LPDWORD TotalEntries,
    IN OUT LPDWORD ResumeHandle OPTIONAL,
    IN LPWSTR NetName OPTIONAL
    )

{
    NET_API_STATUS error;
    PSERVER_REQUEST_PACKET srp;

    if( !ARGUMENT_PRESENT( EntriesRead ) || !ARGUMENT_PRESENT( TotalEntries ) ) {
        return ERROR_INVALID_PARAMETER;
    }

    //
    // Make sure that the level is valid.  Since it is an unsigned
    // value, it can never be less than 0.
    //

    if ( (Level > 2) && (Level != 501 ) && (Level != 502) && (Level != 1005) ) {
        return ERROR_INVALID_LEVEL;
    }

    //
    // Set up the input parameters in the request buffer.
    //

    srp = SsAllocateSrp( );
    if ( srp == NULL ) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }
    srp->Level = Level;
    if ( ARGUMENT_PRESENT( NetName ) ) {
        srp->Flags = SRP_RETURN_SINGLE_ENTRY;
    }

    if ( ARGUMENT_PRESENT( ResumeHandle ) ) {
        srp->Parameters.Get.ResumeHandle = *ResumeHandle;
    } else {
        srp->Parameters.Get.ResumeHandle = 0;
    }

    RtlInitUnicodeString( &srp->Name1, NetName );

    //
    // Get the data from the server.  This routine will allocate the
    // return buffer and handle the case where PreferredMaximumLength ==
    // -1.
    //

    error = SsServerFsControlGetInfo(
                FSCTL_SRV_NET_SHARE_ENUM,
                srp,
                (PVOID *)Buffer,
                PreferredMaximumLength
                );

    //
    // Set up return information.
    //

    *EntriesRead = srp->Parameters.Get.EntriesRead;
    *TotalEntries = srp->Parameters.Get.TotalEntries;
    if ( *EntriesRead > 0 && ARGUMENT_PRESENT( ResumeHandle ) ) {
        *ResumeHandle = srp->Parameters.Get.ResumeHandle;
    }

    SsFreeSrp( srp );

    /*
    This code is not used because it will result in an invalid SD that cannot
    be used by Apps.  The Owner and Group fields are significant, and should be
    included.

    //
    // We need to null out the owner, group, and sacl.
    //

    if ( Level == 502 && *Buffer != NULL ) {

        PSHARE_INFO_502 shi502 = (PSHARE_INFO_502) *Buffer;
        PSECURITY_DESCRIPTOR fileSD;
        ULONG i;

        for ( i = 0 ; i < *EntriesRead; i++, shi502++ ) {

            fileSD = shi502->shi502_security_descriptor;
            if ( fileSD != NULL ) {

                PISECURITY_DESCRIPTOR SD = fileSD;

                if (SD->Control & SE_SELF_RELATIVE) {
                    PISECURITY_DESCRIPTOR_RELATIVE SDR = fileSD;

                    SDR->Owner = 0;
                    SDR->Group = 0;
                    SDR->Sacl = 0;

                } else {

                    SD->Owner = NULL;
                    SD->Group = NULL;
                    SD->Sacl  = NULL;

                }

                SD->Control &=
                    (SE_DACL_DEFAULTED | SE_DACL_PROTECTED | SE_DACL_PRESENT | SE_SELF_RELATIVE);

                ASSERT( RtlValidSecurityDescriptor( fileSD ) );
            }

        } // for
    }

    */

    return error;

} // ShareEnumCommon


NET_API_STATUS
ShareEnumSticky (
    IN DWORD Level,
    OUT LPBYTE *Buffer,
    IN DWORD PreferredMaximumLength,
    OUT LPDWORD EntriesRead,
    OUT LPDWORD TotalEntries,
    IN OUT LPDWORD ResumeHandle OPTIONAL
    )

/*++

Routine Description:

    This routine enumerates all the shares kept in the registry.

Arguments:

    Same as NetShareEnumSticky api.

Return Value:

    status of request.

--*/

{
    NET_API_STATUS error;
    BOOLEAN getEverything;
    ULONG oldResumeHandle;
    SRVSVC_SHARE_ENUM_INFO enumInfo;

    //
    // Set up the input parameters in the request buffer.
    //

    enumInfo.Level = Level;
    if ( ARGUMENT_PRESENT( ResumeHandle ) ) {
        enumInfo.ResumeHandle = *ResumeHandle;
    } else {
        enumInfo.ResumeHandle = 0;
    }

    oldResumeHandle = enumInfo.ResumeHandle;

    //
    // If the length of the second buffer is specified as -1, then we
    // are supposed to get all the information, regardless of size.
    // Allocate space for the output buffer and try to use it.  If this
    // fails, the SsEnumerateStickyShares will tell us just how much we
    // really need to allocate.
    //

    if ( PreferredMaximumLength == 0xFFFFFFFF ) {

        enumInfo.OutputBufferLength = INITIAL_BUFFER_SIZE;
        getEverything = TRUE;

    } else {

        enumInfo.OutputBufferLength = PreferredMaximumLength;
        getEverything = FALSE;
    }

    enumInfo.OutputBuffer = MIDL_user_allocate( enumInfo.OutputBufferLength );

    if ( enumInfo.OutputBuffer == NULL ) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    //
    // Make the request
    //

    error = SsEnumerateStickyShares( &enumInfo );

    //
    // If the call was successful, or there was an error other than
    // ERROR_MORE_DATA (which indicates that the buffer wasn't large
    // enough), or the passed in buffer size was all we're allowed to
    // allocate, return to the caller.
    //

    if ( (error != ERROR_MORE_DATA && error != NERR_BufTooSmall) ||
             !getEverything ) {

        //
        // If no entries were found, free the buffer and set the pointer
        // to NULL.
        //

        if ( enumInfo.EntriesRead == 0 ) {
            MIDL_user_free( enumInfo.OutputBuffer );
            enumInfo.OutputBuffer = NULL;
        }

        goto exit;
    }

    //
    // The initial buffer wasn't large enough, and we're allowed to
    // allocate more.  Free the first buffer.
    //

    MIDL_user_free( enumInfo.OutputBuffer );

    //
    // Allocate a buffer large enough to hold all the information, plus
    // a fudge factor in case the amount of information has increased.
    // If the amount of information increased more than the fudge factor,
    // then we give up.  This should almost never happen.
    //

    enumInfo.OutputBufferLength = enumInfo.TotalBytesNeeded + EXTRA_ALLOCATION;

    enumInfo.OutputBuffer = MIDL_user_allocate( enumInfo.OutputBufferLength );

    if ( enumInfo.OutputBuffer == NULL ) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    //
    // Reset the resume handle in the SRP.  It was altered by the first
    // Enum attempt.
    //

    enumInfo.ResumeHandle = oldResumeHandle;

    //
    // Try again to get the information from the server, this time with the
    // larger buffer.
    //

    error = SsEnumerateStickyShares( &enumInfo );

exit:
    //
    // Set up return information.
    //

    *Buffer = enumInfo.OutputBuffer;
    *EntriesRead = enumInfo.EntriesRead;
    *TotalEntries = enumInfo.TotalEntries;
    if ( *EntriesRead > 0 && ARGUMENT_PRESENT( ResumeHandle ) ) {
        *ResumeHandle = enumInfo.ResumeHandle;
    }

    return error;

} // ShareEnumSticky


ULONG
SizeShares (
    IN ULONG Level,
    IN PSHARE_INFO_502 Shi502
    )

/*++

Routine Description:

    This routine returns the size the passed-in share would take up in
    an API output buffer.

Arguments:

    Level - level of request
    Shi502 - pointer to a shi502 structure

Return Value:

    ULONG - The number of bytes the share would take up in the
        output buffer.

--*/

{
    ULONG shareSize = 0;

    switch ( Level ) {
    case 502:
        if ( Shi502->shi502_security_descriptor != NULL ) {

            //
            // add 4 bytes for possible padding
            //

            shareSize = sizeof( ULONG ) +
                RtlLengthSecurityDescriptor( Shi502->shi502_security_descriptor );
        }

    case 2:
        shareSize += SIZE_WSTR( Shi502->shi502_path );

    case 501:
    case 1:
        shareSize += SIZE_WSTR( Shi502->shi502_remark );

    case 0:
        shareSize += SIZE_WSTR( Shi502->shi502_netname );

    }

    return ( shareSize + FIXED_SIZE_OF_SHARE( Level ) );

} // SizeShares


BOOLEAN
ValidSharePath(
    IN LPWSTR SharePath,
    IN BOOL   IsNtPath
    )
/*++

Routine Description:

    This routine checks to see if .. and . exists on the path.  If they do,
    then we reject this path name.

Arguments:

    SharePath - The share path to be checked.

Return Value:

    TRUE, if path is ok.

--*/

{

    LPWCH source = SharePath;

    if( IsNtPath )
    {
        // The NT Path is validated by the OPEN call
        return TRUE;
    }

    //
    // Walk through the pathname until we reach the zero terminator.  At
    // the start of this loop, source points to the first charaecter
    // after a directory separator or the first character of the
    // pathname.
    //

    //
    // Allow the NT naming convention of slash slash . slash through here
    //
    if( IS_SLASH_SLASH_NAME( source ) ) {

        //
        // We have a path which starts with slash slash
        //  Set the buffer ptr so we start checking the pathname after
        //  the slash slash dot

        source += 3;
    }

    while ( *source != L'\0' ) {

        if ( *source == L'.') {
            source++;
            if ( ( IS_PATH_SEPARATOR(*source) ) ||
                 ( (*source++ == L'.') &&
                    IS_PATH_SEPARATOR(*source) ) ) {

                //
                // '.' and '..' appear as a directory names. Reject.
                //

                return(FALSE);
            }
        }

        //
        // source does not point to a dot, so continue until we see
        // another directory separator
        //

        while ( *source != L'\0' ) {
            if ( *source++ == L'\\' ) {
                break;
            }
        }
    }

    return TRUE;

} // ValidSharePath
