sl@0: // Copyright (c) 2006-2010 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // sl@0: sl@0: #include "SqlSrvFileData.h" sl@0: #include "SqlSrvUtil.h" sl@0: #include "SqlAssert.h" sl@0: #include "SqlSrvStrings.h" sl@0: #include "SqlSrvResourceProfiler.h" sl@0: sl@0: _LIT(KPrivateFmtStr, "\\private\\%08X\\"); sl@0: sl@0: /** sl@0: Creates SQL server private data path on the specified drive. sl@0: sl@0: The idea for calling it is to make sure that the server's private data path exists before making any other sl@0: operation - attempting to create a database file there for example. sl@0: sl@0: @param aFs File session instance sl@0: @param aDriveNumber Drive number on which the private path has to be created sl@0: sl@0: @internalComponent sl@0: */ sl@0: static void CreatePrivateDataPathL(RFs& aFs, TDriveNumber aDriveNumber) sl@0: { sl@0: TDriveInfo driveInfo; sl@0: __SQLLEAVE_IF_ERROR2(aFs.Drive(driveInfo, aDriveNumber)); sl@0: if(!(driveInfo.iDriveAtt & KDriveAttRom)) sl@0: { sl@0: TInt err = aFs.CreatePrivatePath(aDriveNumber); sl@0: if(err != KErrNone && err != KErrAlreadyExists) sl@0: { sl@0: __SQLLEAVE2(err); sl@0: } sl@0: } sl@0: } sl@0: sl@0: /** sl@0: @return Zero if aDbFileName is a non-secure file name (contains the path), non-zero otherwise. sl@0: sl@0: @internalComponent sl@0: */ sl@0: static TBool IsSecureFileNameFmt(const TDesC& aDbFileName) sl@0: { sl@0: TParsePtrC parse(aDbFileName);//this call may panic if aDbFileName cannot be parsed, but SetL() already parsed it sl@0: return !parse.PathPresent(); sl@0: } sl@0: sl@0: /** sl@0: The function parses thr database file name argument and extracts the SID from it (if the name contains SID). sl@0: The SID is expected to be found at position 0 of the file name and must have 8 hex digits. sl@0: sl@0: @param aDbFileName Database file name sl@0: sl@0: @return Database security UID or KNullUid if the database name does not contain SID. sl@0: sl@0: @internalComponent sl@0: */ sl@0: static TUid ExtractSID(const TDesC& aDbFileName) sl@0: { sl@0: TParsePtrC parse(aDbFileName);//this call may panic if aDbFileName cannot be parsed, but SetL() already parsed it sl@0: TPtrC dbName = parse.Name(); sl@0: TInt pos1 = dbName.Locate(TChar('[')); sl@0: TInt pos2 = dbName.Locate(TChar(']')); sl@0: if(pos1 == 0 && pos2 == 9) //position 0 for '[', 8 digits SID, position 9 for ']' sl@0: { sl@0: TLex lex(dbName.Mid(pos1 + 1, pos2 - pos1 - 1)); sl@0: TUid securityUid; sl@0: TInt err = lex.Val(*(TUint32*)&securityUid, EHex); sl@0: if(err == KErrNone) sl@0: { sl@0: return securityUid; sl@0: } sl@0: } sl@0: return KNullUid; sl@0: } sl@0: sl@0: /** sl@0: @return ETrue if the aDbFileName argument contains aPrivatePath as a first directory in the file path, EFalse otherwise. sl@0: sl@0: @internalComponent sl@0: */ sl@0: static TBool IsPrivatePathInFileName(const TDesC& aDbFileName, const TDesC& aPrivatePath) sl@0: { sl@0: TInt pos = aDbFileName.FindF(aPrivatePath); sl@0: return (TUint)pos <= (TUint)KMaxDriveName; sl@0: } sl@0: sl@0: /** sl@0: The method parses aFileName argument and constructs the full database file name (including the path) there. sl@0: The full file name will be constructed in aFileName input/output argument. sl@0: sl@0: @param aDbFileName Input/Output. Database file name will be constructed there. sl@0: @param aSysDrivePrivatePath SQL server private path on the system drive. sl@0: @param aDrive Output parameter. The drive number. sl@0: sl@0: @leave KErrBadName Missing file name. sl@0: sl@0: @panic SqlDb 7 In _DEBUG mode - no drive in the final file path. sl@0: sl@0: @internalComponent sl@0: */ sl@0: static void DoFullFileNameL(TDes& aDbFileName, const TDesC& aSysDrivePrivatePath, TDriveNumber& aDrive) sl@0: { sl@0: TParse parse; sl@0: __SQLLEAVE_IF_ERROR2(parse.Set(aDbFileName, &aSysDrivePrivatePath, NULL)); sl@0: if(!parse.NamePresent()) sl@0: { sl@0: __SQLLEAVE2(KErrBadName); sl@0: } sl@0: aDbFileName.Copy(parse.FullName()); sl@0: TPtrC driveName = parse.Drive(); sl@0: __ASSERT_DEBUG(driveName.Length() > 0, __SQLPANIC2(ESqlPanicInternalError)); sl@0: TInt driveNumber = -1; sl@0: __SQLLEAVE_IF_ERROR2(RFs::CharToDrive(driveName[0], driveNumber)); sl@0: aDrive = static_cast (driveNumber); sl@0: } sl@0: sl@0: /** sl@0: Extracts file name properties, such as secure/non-secure file name, secure UID (SID). sl@0: sl@0: @param aDbFileName Database file name sl@0: @param aServerPrivatePath SQL ser ver private path sl@0: @param aIsSecureFileNameFmt Output. Initialized with non-zero if aDbFileName format is "[drive:]name" sl@0: @param aSecureUid Output. Database secure UID. KNullUid for non-secure databases. sl@0: sl@0: @internalComponent sl@0: */ sl@0: static void GetFileNamePropertiesL(const TDesC& aDbFileName, const TDesC& aServerPrivatePath, sl@0: TBool& aIsSecureFileNameFmt, TUid& aSecureUid) sl@0: { sl@0: //If SQL server private path is in the file name - leave sl@0: if(::IsPrivatePathInFileName(aDbFileName, aServerPrivatePath)) sl@0: { sl@0: __SQLLEAVE2(KErrArgument); sl@0: } sl@0: //Extract database SID from the name sl@0: aIsSecureFileNameFmt = ::IsSecureFileNameFmt(aDbFileName); sl@0: aSecureUid = KNullUid; sl@0: if(aIsSecureFileNameFmt) sl@0: { sl@0: aSecureUid = ::ExtractSID(aDbFileName); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Extracts configuration parameters from client's config string. sl@0: For the rules how decision is made which parameter has to be used - from the config file or from the config string, sl@0: please check the TSqlSrvConfig class' comments. sl@0: If the client config string (aConfigStr argument) is NULL, then the config file parameters will be used (if there is a config file) sl@0: or the build-time partameters. Again, check the TSqlSrvConfig class' comments. sl@0: sl@0: @see TSqlSrvConfig sl@0: @see TSqlSrvConfigParams sl@0: sl@0: @param aConfigStr Client configuration string, can be NULL sl@0: @param aConfigParams Output parameter, the place where config parameters will be stored sl@0: @param aConfig TSqlSrvConfig object used for the production of the config parameters sl@0: sl@0: @see TSqlSrvConfig sl@0: sl@0: @internalComponent sl@0: */ sl@0: static void ExtractConfigParamsL(const TDesC8* aConfigStr, TSqlSrvConfigParams& aConfigParams, const TSqlSrvConfig& aConfig) sl@0: { sl@0: TPtrC8 ptr(aConfigStr ? *aConfigStr : KNullDesC8()); sl@0: aConfig.GetConfigParamsL(ptr, aConfigParams); sl@0: } sl@0: sl@0: /** sl@0: 1. Reads the database file name which is in "aFileNameArgNum" argument of aMessage and sl@0: initializes with it iFileName. sl@0: 2. Parses the file name and initializes iIsSecureFileNameFmt and iSecureUid. sl@0: 3. Creates the full file name in iFileName. sl@0: 4. Creates the server private directory on the related drive. sl@0: sl@0: @leave KErrBadName, the database file name length is invalid (not in [1..KMaxFileName] range); sl@0: KErrArgument, the database file name contains the server private path; sl@0: KErrArgument, the database file name format is secure but the name does not contain SID. sl@0: sl@0: @panic SqlDb 4 In _DEBUG mode. Invalid aFileNameArgNum value. sl@0: @panic SqlDb 7 In _DEBUG mode. Invalid TSqlSrvFileData object. Not initialized system drive and path. sl@0: */ sl@0: void TSqlSrvFileData::SetL(const RMessage2& aMessage, TInt aFileNameLen, TInt aFileNameArgNum, sl@0: #ifdef SQLSRV_STARTUP_TEST sl@0: const TDesC& aDbFileName, sl@0: #endif sl@0: const TDesC8* aConfigStr) sl@0: { sl@0: __ASSERT_DEBUG((TUint)aFileNameArgNum < KMaxMessageArguments, __SQLPANIC(ESqlPanicBadArgument)); sl@0: __ASSERT_DEBUG(iSysDrivePrivatePath.DriveAndPath().Length() > 0, __SQLPANIC(ESqlPanicInternalError)); sl@0: sl@0: if(aFileNameLen < 1 || aFileNameLen > KMaxFileName) sl@0: { sl@0: __SQLLEAVE(KErrBadName); sl@0: } sl@0: #ifdef SQLSRV_STARTUP_TEST sl@0: //To prevent compiler warning sl@0: aMessage.Int0(); sl@0: aFileNameArgNum = aFileNameArgNum; sl@0: // sl@0: iFileName.Copy(aDbFileName); sl@0: #else sl@0: aMessage.ReadL(aFileNameArgNum, iFileName); sl@0: #endif sl@0: SQLPROFILER_REPORT_IPC(ESqlIpcRead, (aFileNameLen * sizeof(TText))); sl@0: TParse parsedFileName; sl@0: __SQLLEAVE_IF_ERROR(parsedFileName.Set(iFileName, 0, 0));//prophylactic check, leave if the file name cannot be parsed sl@0: ::GetFileNamePropertiesL(iFileName, iSysDrivePrivatePath.Path(), iIsSecureFileNameFmt, iSecureUid); sl@0: ::DoFullFileNameL(iFileName, iSysDrivePrivatePath.DriveAndPath(), iDrive); sl@0: iFileName.Append(TChar(0)); sl@0: if(iIsSecureFileNameFmt) sl@0: { sl@0: if(iSecureUid == KNullUid) sl@0: { sl@0: __SQLLEAVE(KErrArgument); sl@0: } sl@0: ::CreatePrivateDataPathL(iFs, iDrive); sl@0: } sl@0: iReadOnly = ::IsReadOnlyFileL(iFs, FileName()); sl@0: ::ExtractConfigParamsL(aConfigStr, iConfigParams, iConfig); sl@0: } sl@0: sl@0: /** sl@0: 1. Initializes iFileName with the database file name. sl@0: 2. Initializes iDrive. sl@0: 3. Checks that iFileName really refers to a file belonging to application's private data cage. sl@0: sl@0: Since the file to be created/opened is a file which belongs to the client application's private data cage sl@0: and the file has been created/opened already on the client side, iFileName is formatted to contain useful sl@0: information for the OS layer, such as file handle, file session handle, etc. The information is passed sl@0: to the OS layer in this strange way (formatted string treted as a file name), because the infomation goes sl@0: through the SQLITE library first. sl@0: The format of iFileName is: sl@0: @code sl@0: Bytes:01 2 10 11 20 Last byte (before the terminating 0) sl@0: || sl@0: @endcode sl@0: sl@0: '|' is a symbol which cannot be placed in normal file names, so here it is used as an indication that the sl@0: string is not a file name (the string contains other information - handles message pointers, etc). sl@0: sl@0: @leave KErrBadName, the database file name length is invalid (not in [1..KMaxFileName] range); sl@0: KErrPermissionDenied, the database file name is not in the application's private data cage. sl@0: */ sl@0: void TSqlSrvFileData::SetFromHandleL(const RMessage2& aMessage, const TDesC& aDbFileName, TBool aCreated, TBool aReadOnly, sl@0: const TDesC8* aConfigStr) sl@0: { sl@0: TParse parsedFileName; sl@0: __SQLLEAVE_IF_ERROR(parsedFileName.Set(aDbFileName, 0, 0));//prophylactic check, leave if the file name cannot be parsed sl@0: iCreated = aCreated; sl@0: iReadOnly = aReadOnly; sl@0: iIsSecureFileNameFmt = EFalse; sl@0: iSecureUid = KNullUid; sl@0: iFileName.Copy(aDbFileName); sl@0: TParsePtrC parse(iFileName); sl@0: if(!parse.DrivePresent() || !parse.PathPresent()) sl@0: { sl@0: __SQLLEAVE(KErrBadName); sl@0: } sl@0: //Get the drive number sl@0: TPtrC driveName = parse.Drive(); sl@0: TInt driveNumber = -1; sl@0: __SQLLEAVE_IF_ERROR(RFs::CharToDrive(driveName[0], driveNumber)); sl@0: iDrive = static_cast (driveNumber); sl@0: ::CreatePrivateDataPathL(iFs, iDrive); sl@0: //Create in "buf" variable calling application's private data path sl@0: TBuf buf; sl@0: buf.Format(KPrivateFmtStr(), aMessage.SecureId().iId); sl@0: //Check that the file name refers to a file which is in the application's private data cage sl@0: TInt pos = iFileName.FindF(buf); sl@0: if((TUint)pos > (TUint)KMaxDriveName) sl@0: { sl@0: __SQLLEAVE(KErrPermissionDenied); sl@0: } sl@0: //Form a new unique name for the database. It will be used when creating transaction rollback files, etc. sl@0: TPtrC nameAndExt = parse.NameAndExt(); sl@0: buf.Format(KFileHandleFmt(), iReadOnly ? 1 : 0, &aMessage, &driveName, aMessage.SecureId().iId, &nameAndExt); sl@0: iFileName.Copy(buf); sl@0: ::ExtractConfigParamsL(aConfigStr, iConfigParams, iConfig); sl@0: } sl@0: