os/security/authorisation/userpromptservice/database/source/upsdbw.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /*
     2 * Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
     3 * All rights reserved.
     4 * This component and the accompanying materials are made available
     5 * under the terms of the License "Eclipse Public License v1.0"
     6 * which accompanies this distribution, and is available
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
     8 *
     9 * Initial Contributors:
    10 * Nokia Corporation - initial contribution.
    11 *
    12 * Contributors:
    13 *
    14 * Description: 
    15 * Implements a writable interface for UPS Database.
    16 *
    17 */
    18 
    19 
    20 /**
    21  @file
    22  @internalTechnology
    23  @released
    24 */
    25 
    26 #include "upsdbw.h"
    27 #include "upscommon.h"
    28 
    29 using namespace UserPromptService;
    30 
    31 CDecisionDbW::CDecisionDbW()
    32 /**
    33 	Constructor for writable decision database object
    34  */
    35 	{
    36 	
    37 	}
    38 
    39 	
    40 CDecisionDbW::~CDecisionDbW()
    41 /**
    42 	Destructor for writable decision database object
    43  */
    44 	{
    45 	iDatabase.Close();
    46 	delete iDbName;
    47 	delete iStore;
    48 	}
    49 
    50 
    51 EXPORT_C CDecisionDbW* CDecisionDbW::NewL(const TDesC& aDbName, RFs& aFs)
    52 /**
    53 	Creates a writable decision database object and connects to the database.
    54 	If the database does not exist or is corrupted, a new decision database is created.
    55 	The function leaves, if creation of the object or connection to the database fail.
    56 		
    57 	@param	 aDbName The path of the decision database
    58 	@param   aFs     Handle to the file server session
    59 	
    60 	@return  A pointer to the newly allocated database object, if creation and connection are successful. 
    61  */
    62 	{
    63 	CDecisionDbW* self = CDecisionDbW::NewLC(aDbName, aFs);
    64 	CleanupStack::Pop(self);
    65 	return self;
    66 	}
    67 
    68 	
    69 EXPORT_C CDecisionDbW* CDecisionDbW::NewLC(const TDesC& aDbName, RFs& aFs)
    70 /**
    71 	Creates a writable decision database object and connects to the database.
    72 	If the database does not exist or is corrupted, a new decision database is created.
    73 	The function leaves, if creation of the object or connection to the database fail.
    74 		
    75 	@param	aDbName The path of the decision database
    76 	param   aFs		Handle to the file server session
    77 	
    78 	@return A pointer to the newly allocated database object, if creation and connection 
    79 			are successful. The pointer is also put onto the cleanup stack.
    80  */
    81 	{
    82 	CDecisionDbW* self = new (ELeave) CDecisionDbW();
    83 	CleanupStack::PushL(self);
    84 	self->ConstructL(aDbName, aFs);
    85 	return self;
    86 	}
    87 
    88 	
    89 void CDecisionDbW::ConstructL(const TDesC& aDbName, RFs& aFs)
    90 /**
    91 	Second phase constructor for the decision database object. Connects to the decision database.
    92 	If the database does not existed or is corrupted, creates a new one. The function leaves,
    93 	if both database connection and creation fail.
    94 	
    95 	@param aDbName The path of the decision database
    96 	@param aFs		Handle to the file server session
    97  */
    98 	{
    99 	//Construct the name of UPS decision database
   100 	iDbName = aDbName.AllocL();
   101 	
   102 	//First try to open the decision database
   103 	TRAPD(error,OpenDatabaseL(aFs));
   104 	
   105 	if((error == KErrNoMemory) || (error == KErrDiskFull))
   106 		{
   107 		User::Leave(error);
   108 		}
   109 	
   110 	// Makes sure that DB file is writable. (DEF122590)
   111 	aFs.SetAtt(iDbName->Des(),0, KEntryAttReadOnly);
   112 		
   113 	if(error != KErrNone)
   114 		{
   115 		DEBUG_PRINTF2(_L("%S database file does not exist or is corrupted."), iDbName);
   116 		//The decision database does not exist or is corrupted, create a new one
   117 		CreateDatabaseL(aFs);
   118 		
   119 		//Create a decision table in the decision database
   120 		CreateTableL();
   121 		
   122 		//Create an index on the decision table
   123 		CreateIndexL();
   124 		}
   125 	}
   126 
   127 	
   128 void CDecisionDbW::OpenDatabaseL(RFs& aFs)
   129 /**
   130 	Opens the decision database.
   131 	@param aFs	Handle to the file server session
   132  */
   133 	{
   134 	DEBUG_PRINTF2(_L("%S database file is being opened."), iDbName);
   135 	
   136 	iStore = CPermanentFileStore::OpenL(aFs, *iDbName, EFileRead|EFileWrite);
   137 	iDatabase.OpenL(iStore, iStore->Root());
   138 	
   139 	//Database does exist. However, make sure that the decision table also exists
   140 	CDbTableNames *tables = iDatabase.TableNamesL();
   141 	CleanupStack::PushL(tables);
   142 	if(1 != tables->Count())
   143 		{
   144 		DEBUG_PRINTF(_L("The decision table could not be found in the database file!"));
   145 		User::Leave(KErrNotFound);
   146 		}
   147 	CleanupStack::PopAndDestroy(tables);
   148 	
   149 	//OK. The decision table does exist. What about the decision index?
   150 	CDbIndexNames *indexes = iDatabase.IndexNamesL(KDecisionTable);
   151 	CleanupStack::PushL(indexes);
   152 	if(2 != indexes->Count())
   153 		{
   154 		DEBUG_PRINTF(_L("The index on the decision table is missing!"));
   155 		User::Leave(KErrNotFound);
   156 		}
   157 	CleanupStack::PopAndDestroy(indexes);
   158 
   159 	if(iDatabase.IsDamaged())
   160 		{
   161 		User::LeaveIfError(iDatabase.Recover());
   162 		}
   163 	
   164 	DEBUG_PRINTF2(_L("%S database file has been opened successfully."), iDbName);
   165 	}
   166 
   167 
   168 void CDecisionDbW::CreateDatabaseL(RFs& aFs)
   169 /**
   170 	Creates a new, empty store database.
   171 	@param aFs	Handle to the file server session
   172  */
   173 	{
   174 	DEBUG_PRINTF2(_L("%S database is being created."), iDbName);
   175 	
   176 	if(iStore)
   177 		{//if database file exists but the table does not, iStore has already been allocated. 
   178 		delete iStore;
   179 		iStore = 0;
   180 		iDatabase.Close();
   181 		}
   182 
   183 	//Create a file store
   184 	iStore = CPermanentFileStore::ReplaceL(aFs, *iDbName, EFileRead|EFileWrite);
   185 	iStore->SetTypeL(iStore->Layout());
   186 	
   187 	//Create UPS Decision Database in the file store
   188 	TStreamId sId = iDatabase.CreateL(iStore);
   189 	iStore->SetRootL(sId);
   190 	
   191 	//Commit the database creation
   192 	iStore->CommitL();
   193 	
   194 	DEBUG_PRINTF2(_L("%S database has been created successfully."), iDbName);
   195 	}
   196 
   197 
   198 void CDecisionDbW::CreateTableL()
   199 /**
   200 	Creates the decision table in the decision database.
   201  */
   202 	{
   203 	DEBUG_PRINTF(_L("The Ups decision table is being created."));
   204 	
   205 	// Create a table definition
   206 	CDbColSet* columns=CDbColSet::NewLC();
   207 	
   208 	// add the columns to the definition
   209 	TDbCol clientSid(KColClientSid,EDbColUint32);
   210 	clientSid.iAttributes = TDbCol::ENotNull;				
   211 	columns->AddL(clientSid);
   212 	
   213 	TDbCol evaluatorId(KColEvaluatorId,EDbColUint32);
   214 	evaluatorId.iAttributes = TDbCol::ENotNull;				
   215 	columns->AddL(evaluatorId);
   216 	
   217 	TDbCol serviceId(KColServiceId,EDbColUint32);
   218 	serviceId.iAttributes=TDbCol::ENotNull;
   219 	columns->AddL(serviceId);
   220 	
   221 	TDbCol serverSid(KColServerSid,EDbColUint32);
   222 	serverSid.iAttributes=TDbCol::ENotNull;
   223 	columns->AddL(serverSid);
   224 	
   225 	TDbCol fingerprint(KColFingerprint,EDbColText8,KUpsMaxFingerprintLength );
   226 	columns->AddL(fingerprint);
   227 	
   228 	TDbCol clientEntity(KColClientEntity,EDbColText8,KUpsMaxClientEntityLength);
   229 	columns->AddL(clientEntity);
   230 	
   231 	TDbCol description(KColDescription,EDbColLongText);
   232 	columns->AddL(description);
   233 	
   234 	TDbCol result(KColResult,EDbColInt8);
   235 	result.iAttributes=TDbCol::ENotNull;
   236 	columns->AddL(result);
   237 	
   238 	TDbCol evaluatorInfo(KColEvaluatorInfo,EDbColUint32);
   239 	columns->AddL(evaluatorInfo);
   240 	
   241 	TDbCol policyVersion(KColMajorPolicyVersion,EDbColUint16);
   242 	policyVersion.iAttributes=TDbCol::ENotNull;
   243 	columns->AddL(policyVersion);
   244 	
   245 	TDbCol recordId(KColRecordId,EDbColUint32);
   246 	recordId.iAttributes=TDbCol::ENotNull|TDbCol::EAutoIncrement;
   247 	columns->AddL(recordId);
   248 	
   249 	// Create a table with the table definition
   250 	User::LeaveIfError(iDatabase.CreateTable(KDecisionTable,*columns));
   251 				
   252 	// cleanup the column set
   253 	CleanupStack::PopAndDestroy(columns);
   254 	
   255 	DEBUG_PRINTF(_L("The Ups decision table has been created successfully."));
   256 	}
   257 
   258 
   259 void CDecisionDbW::CreateIndexL()
   260 /**
   261 	Creates an index on the decision table and makes it unique.
   262  */
   263 	{
   264 	DEBUG_PRINTF(_L("The Ups decision index is being created on the decision table."));
   265 	
   266 	// create the index key
   267 	CDbKey* key=CDbKey::NewLC();
   268 
   269 	// add the key columns
   270 	key->AddL(TDbKeyCol(KColClientSid));
   271 	key->AddL(TDbKeyCol(KColEvaluatorId));
   272 	key->AddL(TDbKeyCol(KColServiceId));
   273 	key->AddL(TDbKeyCol(KColServerSid));
   274 	key->AddL(TDbKeyCol(KColFingerprint));
   275 	key->AddL(TDbKeyCol(KColClientEntity));
   276 	key->AddL(TDbKeyCol(KColMajorPolicyVersion));
   277 	
   278 	//Make the index key unique
   279 	key->MakeUnique();
   280 	
   281 	// create the primary index
   282 	User::LeaveIfError(iDatabase.CreateIndex(KPrimaryIndex,KDecisionTable,*key));
   283 	
   284 	//Now create the second index on the record id
   285 	key->Clear();
   286 	// add the record id column
   287 	key->AddL(TDbKeyCol(KColRecordId));
   288 	key->MakeUnique();
   289 	
   290 	// create the record id index
   291 	User::LeaveIfError(iDatabase.CreateIndex(KRecordIdIndex,KDecisionTable,*key));
   292 	
   293 	// cleanup the key
   294 	CleanupStack::PopAndDestroy(key);
   295 	
   296 	DEBUG_PRINTF(_L("The Ups decision index has been created successfully."));
   297 	}
   298 
   299 
   300 EXPORT_C void CDecisionDbW::DeleteDatabaseL(RFs& aFs)
   301 /**
   302 	Delete the decision database completely.
   303 	@param aFs	Handle to the file server session
   304  */
   305 	{
   306 	DEBUG_PRINTF2(_L("%S database is being deleted."),iDbName);
   307 	
   308 	iDatabase.Close();
   309 	delete iStore;
   310 	iStore = 0;
   311 	User::LeaveIfError(aFs.Delete(*iDbName));
   312 	
   313 	DEBUG_PRINTF2(_L("%S database has been deleted successfully."),iDbName);
   314 	}
   315 
   316 
   317 void CDecisionDbW::UpdateCurrentRowL(RDbTable& aTable, CDecisionRecord& aRecord)
   318 /**
   319 	Updates the current row of the rowset by using the values the provided decision record.
   320 	
   321 	@param aTable  A table object which provides access to table data as a rowset.
   322 	@param aRecord A decision record object used to update the current row.
   323  */
   324 	{
   325 	//Use CDbColSet to set fields
   326 	CDbColSet* colSet = aTable.ColSetL();
   327 	CleanupStack::PushL(colSet);
   328 	
   329 	//Set the fields of the empty row
   330 	aTable.SetColL(colSet->ColNo(KColClientSid)	 ,(TUint32)aRecord.iClientSid.iId);
   331 	aTable.SetColL(colSet->ColNo(KColEvaluatorId),(TUint32)aRecord.iEvaluatorId.iUid);
   332 	aTable.SetColL(colSet->ColNo(KColServiceId)  ,(TUint32)aRecord.iServiceId.iUid);
   333 	aTable.SetColL(colSet->ColNo(KColServerSid)	 ,(TUint32)aRecord.iServerSid.iId);
   334 	
   335 	//Fingerprint may be null
   336 	if(aRecord.iFingerprint.Length() != 0)
   337 		{
   338 		aTable.SetColL(colSet->ColNo(KColFingerprint),aRecord.iFingerprint);
   339 		}
   340 	
   341 	//ClientEntity may be null
   342 	if(aRecord.iClientEntity.Length() != 0)
   343 		{
   344 		aTable.SetColL(colSet->ColNo(KColClientEntity),aRecord.iClientEntity);
   345 		}
   346 	
   347 	//Description may be null	
   348 	if(aRecord.iDescription.Length() != 0)
   349 		{
   350 		//A long column is written by using an RDbColStream
   351 		RDbColWriteStream str;
   352 		TDbColNo col = colSet->ColNo(KColDescription);
   353 		
   354 		str.OpenLC(aTable,col);
   355 		str.WriteL(aRecord.iDescription);
   356 		str.Close();
   357 		
   358 		CleanupStack::PopAndDestroy(&str);
   359 		}
   360 		
   361 	aTable.SetColL(colSet->ColNo(KColResult),aRecord.iResult);
   362 	aTable.SetColL(colSet->ColNo(KColEvaluatorInfo),(TUint32)aRecord.iEvaluatorInfo);
   363 	aTable.SetColL(colSet->ColNo(KColMajorPolicyVersion),aRecord.iMajorPolicyVersion);
   364 	
   365 	CleanupStack::PopAndDestroy(colSet);
   366 	}
   367 
   368 
   369 EXPORT_C void CDecisionDbW::CreateDecisionL(CDecisionRecord& aRecord)
   370 /**
   371 	Inserts a new decision record into the decision database.
   372 	
   373 	@param aRecord A new decision record
   374  */
   375 	{
   376 	//Create a database table object
   377 	RDbTable dbTable;
   378 	User::LeaveIfError(dbTable.Open(iDatabase,KDecisionTable,dbTable.EInsertOnly));
   379 	CleanupClosePushL(dbTable);
   380 	
   381 	//Set the rowset cursor to the beginning position
   382 	dbTable.Reset();
   383 	
   384 	//Insert an empty row into the table
   385 	dbTable.InsertL();
   386 	
   387 	//Set the fields of newly inserted row
   388 	UpdateCurrentRowL(dbTable, aRecord);
   389 	
   390 	//Complete insertion
   391 	dbTable.PutL();
   392 
   393 	CleanupStack::Pop(&dbTable);
   394 	//Close the rowset
   395 	dbTable.Close();	 
   396 	}
   397 
   398 
   399 void CDecisionDbW::PrepareForSearchL(RDbTable& aTable, CDecisionFilter& aFilter, TDbSeekMultiKey<KIndexColumnNumber>& aSeekKey)
   400 /**
   401 	Opens the provided table object on the decision database and sets the decision index 
   402 	as the active index for this table. If successful, the rowset is reset to the beginning.
   403 
   404 	@param aTable  A table object which provides access to table data as a rowset.
   405 	@param aFilter A filter object which is used to set the decision index.
   406 	@return 	   The key value to lookup in the index.
   407  */
   408 	{
   409 	User::LeaveIfError(aTable.Open(iDatabase, KDecisionTable, aTable.EUpdatable));
   410 	
   411 	TUint16 flags = KSetClientSid|KSetEvaluatorId|KSetServiceId|KSetServerSid|KSetFingerprint|KSetClientEntity|KSetMajorPolicyVersion;
   412 	
   413 	TUint16 combinedFlags = (0x00FF) & (aFilter.iSetFlag[KPosClientSid]   | aFilter.iSetFlag[KPosEvaluatorId] |
   414 						     			aFilter.iSetFlag[KPosServiceId]   | aFilter.iSetFlag[KPosServerSid]   |
   415 						     			aFilter.iSetFlag[KPosFingerprint] | aFilter.iSetFlag[KPosClientEntity]|
   416 						     			aFilter.iSetFlag[KPosMajorPolicyVersion]);
   417 
   418 	//If any of these flags is not set, do not continue
   419 	if ((combinedFlags & flags) != flags)
   420 		{
   421 		DEBUG_PRINTF(_L("The provided filter does not have all required keys to look up a decision."));
   422 		User::Leave(KErrUpsMissingArgument);
   423 		}
   424 	
   425 	aSeekKey.Add((TUint)aFilter.iClientSid.iId);
   426 	aSeekKey.Add((TUint)aFilter.iEvaluatorId.iUid);
   427 	aSeekKey.Add((TUint)aFilter.iServiceId.iUid);
   428 	aSeekKey.Add((TUint)aFilter.iServerSid.iId);
   429 	aSeekKey.Add(*aFilter.iFingerprint);
   430 	aSeekKey.Add(*aFilter.iClientEntity);
   431 	aSeekKey.Add(aFilter.iMajorPolicyVersion);
   432 			
   433 	//Set the primary index
   434 	User::LeaveIfError(aTable.SetIndex(KPrimaryIndex));
   435 
   436 	}
   437 
   438 
   439 EXPORT_C CDecisionRecord* CDecisionDbW::GetDecisionL(CDecisionFilter& aFilter)
   440 /**
   441 	Gets a decision record from the decision database. The functions returns the first matched
   442 	record. All the methods of the filter object except the client entity should be supplied to 
   443 	get the intended decision record. If any other attribute of the filter object is missing, 
   444 	either no decision might be found or a wrong decision may be retrieved
   445 	
   446 	@param aFilter A filter object whose client id, policy evaluator id, service id,
   447 	 			   server id, fingerprint (and client entity) attributes are set.
   448  */
   449 	{
   450 	//Define a database table object handle
   451 	RDbTable dbTable;
   452 	CleanupClosePushL(dbTable);
   453 	
   454 	//Open the database table object and prepares it for searching
   455 	TDbSeekMultiKey<KIndexColumnNumber> seekKey;
   456 	PrepareForSearchL(dbTable,aFilter,seekKey);
   457 	
   458 	//Define the decision record that will be returned
   459 	CDecisionRecord* retRecord(0);
   460 	
   461 	if(dbTable.SeekL(seekKey))
   462 		{
   463 		//Result found
   464 		CDbColSet* colSet = dbTable.ColSetL();
   465 		CleanupStack::PushL(colSet);
   466 		
   467 		dbTable.GetL();
   468 		
   469 		retRecord = CDecisionView::GenerateRecordL(dbTable,colSet);
   470 		
   471 		CleanupStack::PopAndDestroy(colSet);		
   472 		}
   473 
   474 	CleanupStack::PopAndDestroy(&dbTable);
   475 	
   476 	return retRecord;
   477 	}
   478 
   479 
   480 EXPORT_C TBool CDecisionDbW::UpdateDecisionL(CDecisionFilter& aFilter, CDecisionRecord& aNewRecord)
   481 	{
   482 	//Define a database table object handle
   483 	RDbTable dbTable;
   484 	CleanupClosePushL(dbTable);
   485 	
   486 	//Open the database table object and prepares it for searching
   487 	TDbSeekMultiKey<KIndexColumnNumber> seekKey;
   488 	PrepareForSearchL(dbTable,aFilter,seekKey);
   489 	
   490 	TBool retValue = ETrue; //return value
   491 	
   492 	if(dbTable.SeekL(seekKey))
   493 		{
   494 		//Get the current row
   495 		dbTable.GetL();
   496 		//Prepare it for update
   497 		dbTable.UpdateL();
   498 		//Set the new values
   499 		UpdateCurrentRowL(dbTable, aNewRecord);
   500 		//Commit the update
   501 		dbTable.PutL();
   502 		}
   503 	else
   504 		{
   505 		retValue = EFalse;
   506 		}
   507 	
   508 	CleanupStack::PopAndDestroy(&dbTable);
   509 	
   510 	return retValue;
   511 	}
   512 	
   513 
   514 void CDecisionDbW::DoRemoveDecisionL(CDecisionFilter& aFilter)
   515 /**
   516 	Deletes a set of records from the decision database. This function is called 
   517 	by RemoveDecisionsL in a loop to be able to catch and repair index corruptions.
   518 	
   519 	@param aFilter A filter object
   520  */
   521 	{
   522 	//Create the SQL statement
   523 	RBuf sqlStatement;
   524 	CreateSqlStatementLC(aFilter, sqlStatement);
   525 						
   526 	RDbView dbView;
   527 	CleanupClosePushL(dbView);
   528 	
   529 	User::LeaveIfError(dbView.Prepare(iDatabase,TDbQuery(sqlStatement),TDbWindow::EUnlimited));
   530 	User::LeaveIfError(dbView.EvaluateAll());
   531 
   532 	//Begin the delete transaction and lock the database
   533 	User::LeaveIfError(iDatabase.Begin());	
   534 		
   535 	while(dbView.NextL())
   536 		{
   537 		dbView.DeleteL();
   538 		}
   539 			
   540 	CleanupStack::PopAndDestroy(&dbView);		
   541 	CleanupStack::PopAndDestroy(&sqlStatement);
   542 	
   543 	//Release the lock
   544 	TInt result = iDatabase.Commit();
   545 	if(KErrNone != result)
   546 		{
   547 		DEBUG_PRINTF2(_L("Removing a decision has returned with %d. Rollback is now in proggress."),result);
   548 		iDatabase.Rollback();
   549 		User::Leave(result);
   550 		}
   551 	}
   552 
   553 EXPORT_C void CDecisionDbW::RemoveDecisionsL(CDecisionFilter& aFilter)
   554 /**
   555 	Deletes a set of records from the decision database. It is possible to delete a specific
   556 	decision record or a set of records based on a given filter. While this function is in
   557 	progress, the database is locked not to lead to an inconsistent data retrieving. If there
   558 	is any index corruption in the decision able, it is tried to be repaired automatically.
   559 	
   560 	@param aFilter A filter object
   561  */
   562 	{
   563 	TInt loopCount=1;
   564 	TInt error;
   565 	do
   566 		{
   567 		++loopCount;
   568 		TRAP(error,DoRemoveDecisionL(aFilter));
   569 		if(error == KErrCorrupt)
   570 			{
   571 			DEBUG_PRINTF2(_L("The database is corrupted and being recovered. Try %d"),loopCount);
   572 			error = iDatabase.Recover(); //Recover() may fail, so update error
   573 			}
   574 		}while(error==KErrCorrupt && loopCount<2);
   575 		
   576 	User::LeaveIfError(error);
   577 	}
   578 
   579 
   580 EXPORT_C CDecisionDbCompactor *CDecisionDbW::PrepareCompactionLC()
   581 /**
   582 	Creates a database compaction object to perform asynchronous compaction on the database.
   583 	
   584 	@return A pointer to the newly created database compaction object
   585 			The pointer is also put onto the cleanup stack.
   586  */
   587 	{
   588 	DEBUG_PRINTF(_L("The Ups database is being compacted."));
   589 	CDecisionDbCompactor *compactor = CDecisionDbCompactor::NewLC();
   590 	User::LeaveIfError(compactor->iDbIncremental.Compact(iDatabase,compactor->iStep()));
   591 	return compactor;
   592 	}
   593