/********************************************************************/
/**               Copyright(c) 1989 Microsoft Corporation.	   **/
/********************************************************************/

//***
//
// Filename:	finder.c
//
// Description: This module contains support routines for the finder
//		category API's for the AFP server service
//
// History:
//		Sept 30,1993.	NarenG		Created original version.
//
#include "afpsvcp.h"

BOOL
IsTargetNTFS(
	IN     LPWSTR lpwsPath
);

DWORD
CopyStream(
    	IN	HANDLE hSrc,
	IN	HANDLE hDst
);

#define	AFP_RESC_STREAM			TEXT(":AFP_Resource")

//**
//
// Call:	AfpAdminrFinderSetInfo
//
// Returns:	NO_ERROR
//		ERROR_ACCESS_DENIED
//		non-zero returns from AfpServerIOCtrl
//
// Description: This routine communicates with the AFP FSD to implement
//		the AfpAdminFinderSetInfo function.
//
DWORD
AfpAdminrFinderSetInfo(
	IN AFP_SERVER_HANDLE 	hServer,
	IN LPWSTR     		pType,
	IN LPWSTR     		pCreator,
	IN LPWSTR     		pData,
	IN LPWSTR     		pResource,
	IN LPWSTR     		pTarget,
	IN DWORD		dwParmNum
)
{
AFP_REQUEST_PACKET 	AfpSrp;
DWORD			dwRetCode = NO_ERROR, dwRetryCount = 0;
AFP_FINDER_INFO	AfpFinderInfo;
LPBYTE 			pAfpFinderInfoSR = NULL;
DWORD			cbAfpFinderInfoSRSize;
DWORD		    dwAccessStatus=0;
HANDLE			hTarget = INVALID_HANDLE_VALUE;
HANDLE		    hDataSrc = INVALID_HANDLE_VALUE;
HANDLE		    hResourceSrc = INVALID_HANDLE_VALUE;
LPWSTR			lpwsResourceFork;
BOOLEAN			fCreatedFile = FALSE;


    // Check if caller has access
    //
    if ( dwRetCode = AfpSecObjAccessCheck( AFPSVC_ALL_ACCESS, &dwAccessStatus))
    {
        AFP_PRINT(( "SFMSVC: AfpAdminrFinderSetInfo, AfpSecObjAccessCheck failed %ld\n",dwRetCode));
		AfpLogEvent( AFPLOG_CANT_CHECK_ACCESS, 0, NULL,
					 dwRetCode, EVENTLOG_ERROR_TYPE );
        return( ERROR_ACCESS_DENIED );
    }

    if ( dwAccessStatus )
    {
        AFP_PRINT(( "SFMSVC: AfpAdminrFinderSetInfo, AfpSecObjAccessCheck returned %ld\n",dwAccessStatus));
        return( ERROR_ACCESS_DENIED );
    }

    if ( wcsstr( pTarget, (LPWSTR)TEXT(":\\") ) == NULL )
		return( ERROR_INVALID_NAME );

    if ( !IsTargetNTFS( pTarget ) )
		return( (DWORD)AFPERR_UnsupportedFS );


	//
	// Impersonate the client while we read/write the fork data
	//
	dwRetCode = RpcImpersonateClient( NULL );
	if ( dwRetCode != RPC_S_OK )
	{
		return(I_RpcMapWin32Status( dwRetCode ));
	}

    // open the data source file if one was specified
    //
	if ( STRLEN( pData ) > 0 ){
		hDataSrc = CreateFile(pData, GENERIC_READ, FILE_SHARE_READ, NULL,
					  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	
		if (hDataSrc == INVALID_HANDLE_VALUE) {
			RpcRevertToSelf();
			return( GetLastError() );
		}
	
	
		// open the target file's data stream if the file exists,
		// otherwise create the file
		//
		hTarget = CreateFile(pTarget, GENERIC_WRITE, FILE_SHARE_READ, NULL,
					 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	
		if (hTarget == INVALID_HANDLE_VALUE) {
			dwRetCode = GetLastError();
			CloseHandle(hDataSrc);
			RpcRevertToSelf();
			return( dwRetCode );
			}

        // Figure out if we just created a new file
	    if (GetLastError() == 0)
		{
			fCreatedFile = TRUE;
		}

		SetFilePointer(hTarget,0,NULL,FILE_BEGIN);
		SetEndOfFile(hTarget);
	
		// Read the source data and write it to target data stream
		//
		SetLastError(NO_ERROR);
		dwRetCode = CopyStream(hDataSrc, hTarget);
	
		CloseHandle(hDataSrc);
		CloseHandle(hTarget);
	
		if (dwRetCode != NO_ERROR) {
			RpcRevertToSelf();
			return( dwRetCode );
		}
	}

    // open the resource source file if one was specified
    //
    if ( STRLEN( pResource ) > 0 ) {

		hResourceSrc = CreateFile( pResource, GENERIC_READ, FILE_SHARE_READ,
					   NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
					   NULL);
	
		if (hResourceSrc == INVALID_HANDLE_VALUE) {
			RpcRevertToSelf();
			return( GetLastError() );
		}
	
		lpwsResourceFork = LocalAlloc( LPTR,
						   (STRLEN(pTarget)+
							STRLEN(AFP_RESC_STREAM)+1)
						* sizeof( WCHAR ) );
	
		if ( lpwsResourceFork == NULL ) {
			CloseHandle(hResourceSrc);
			RpcRevertToSelf();
			return( ERROR_NOT_ENOUGH_MEMORY );
		}
	
		// Open the target resource fork
		//
		STRCPY(lpwsResourceFork, pTarget );
		STRCAT(lpwsResourceFork, AFP_RESC_STREAM);
	
		hTarget = CreateFile(lpwsResourceFork, GENERIC_WRITE, FILE_SHARE_READ,
					 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	
		if (hTarget == INVALID_HANDLE_VALUE) {
			dwRetCode = GetLastError();
			LocalFree( lpwsResourceFork );
			CloseHandle(hResourceSrc);
			RpcRevertToSelf();
			return( dwRetCode );
		}
	
		LocalFree( lpwsResourceFork );
	
		// Assume we created a new file (datafork) in the process, there is
		// no way to tell for sure since creating a new resource fork will
		// not tell us whether or not the datafork already existed or not
		fCreatedFile = TRUE;

		// Read the source resource and write it to target resource stream
		//
		SetLastError(NO_ERROR);
		dwRetCode = CopyStream(hResourceSrc, hTarget);
	
		CloseHandle(hResourceSrc);
		CloseHandle(hTarget);
	
		if (dwRetCode != NO_ERROR) {
			RpcRevertToSelf();
			return( dwRetCode );
		}
	
	}

	//
	// Revert back to LocalSystem context
	//
	RpcRevertToSelf();

    if ( dwParmNum & ( AFP_FD_PARMNUM_TYPE | AFP_FD_PARMNUM_CREATOR ) ){

		dwRetCode = NO_ERROR;
	
		AfpFinderInfo.afpfd_path = pTarget;
	
		if ( dwParmNum & AFP_FD_PARMNUM_TYPE )
			STRCPY( AfpFinderInfo.afpfd_type, pType );
			else
			AfpFinderInfo.afpfd_type[0] = TEXT( '\0' );
		
		if ( dwParmNum & AFP_FD_PARMNUM_CREATOR )
			STRCPY( AfpFinderInfo.afpfd_creator, pCreator );
		else
			AfpFinderInfo.afpfd_creator[0] = TEXT( '\0' );
	
		// Make this buffer self-relative.
		//
		if ( dwRetCode = AfpBufMakeFSDRequest((LPBYTE)&AfpFinderInfo,
						   sizeof(SETINFOREQPKT),
						   AFP_FINDER_STRUCT,
						   &pAfpFinderInfoSR,
						   &cbAfpFinderInfoSRSize ))
	        return( dwRetCode );

		// Make IOCTL to set info
		//
		AfpSrp.dwRequestCode 		    = OP_FINDER_SET;
		AfpSrp.dwApiType     		    = AFP_API_TYPE_SETINFO;
		AfpSrp.Type.SetInfo.pInputBuf       = pAfpFinderInfoSR;
		AfpSrp.Type.SetInfo.cbInputBufSize  = cbAfpFinderInfoSRSize;
		AfpSrp.Type.SetInfo.dwParmNum       = dwParmNum;


		// Since there will be a delay between the time the change
		// notify comes into the server for the new file, and the
		// time it is actually processed by the server, we need to
		// put in a delay and retry to give the server a chance to
		// cache the new file
		if (fCreatedFile)
		{
			Sleep( 2000 );
		}

		do
		{

			dwRetCode = AfpServerIOCtrl( &AfpSrp );

			if (dwRetCode != ERROR_PATH_NOT_FOUND)
			{
				break;
			}

			Sleep( 2000);

		} while ( ++dwRetryCount < 4 );

		LocalFree( pAfpFinderInfoSR );
	
    }

    return( dwRetCode );
}



DWORD
CopyStream(
    	IN	HANDLE hSrc,
	IN	HANDLE hDst
)
{
    DWORD bytesread, byteswritten, Status = NO_ERROR;
    BYTE		Buffer[1024 * 16];

    do
    {
	bytesread = byteswritten = 0;

	// read from src, write to dst
	//
	if (ReadFile(hSrc, Buffer, sizeof(Buffer), &bytesread, NULL))
	{
	    if (bytesread == 0)
	    {
		break;
	    }
	}
	else
	{
	    Status = GetLastError();
	    break;
	}

	if (!WriteFile(hDst, Buffer, bytesread, &byteswritten, NULL))
	{
	    Status = GetLastError();
	    break;
	}

    } while (TRUE);

    return(Status);
}

BOOL
IsTargetNTFS(
	IN     LPWSTR lpwsPath
)
{
WCHAR	wchDrive[5];
DWORD   dwMaxCompSize;
DWORD   dwFlags;
WCHAR   wchFileSystem[10];

    // Get the drive letter, : and backslash
    //
    ZeroMemory( wchDrive, sizeof( wchDrive ) );

    STRNCPY( wchDrive, lpwsPath, 3 );

    if ( !( GetVolumeInformation( (LPWSTR)wchDrive,
			          NULL,
			          0,
 			          NULL,
			          &dwMaxCompSize,
			          &dwFlags,
				  (LPWSTR)wchFileSystem,
				  sizeof( wchFileSystem ) ) ) ){
	return( FALSE );
    }

    if ( STRICMP( wchFileSystem, TEXT("NTFS") ) == 0 )
   	return( TRUE );
    else
	return( FALSE );
	
}