|
1 /* |
|
2 * Copyright (c) 2008-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 * CIntegrityServices implementation |
|
16 * |
|
17 */ |
|
18 |
|
19 |
|
20 /** |
|
21 @file |
|
22 @released |
|
23 @internalTechnology |
|
24 */ |
|
25 |
|
26 #include "integrityservices.h" |
|
27 #include "journal.h" |
|
28 #include "operationfunctions.h" |
|
29 #include "usiflog.h" |
|
30 |
|
31 #include <f32file.h> |
|
32 |
|
33 using namespace Usif; |
|
34 |
|
35 _LIT(KTransactionPath, "\\sys\\install\\integrityservices\\"); |
|
36 |
|
37 CIntegrityServices::TFailType CIntegrityServices::iFailType = EFailNone; |
|
38 CIntegrityServices::TFailPosition CIntegrityServices::iFailPosition = EBeforeJournal; |
|
39 TFileName CIntegrityServices::iFailFileName; |
|
40 TBool CIntegrityServices::iIsFailureTestingEnabled = EFalse; |
|
41 |
|
42 |
|
43 /** |
|
44 * This is a trivial C class that just encapsulates a TEntryArray object in order to |
|
45 * facilitate its storage on the heap. |
|
46 * |
|
47 * @released |
|
48 * @internalComponent |
|
49 */ |
|
50 class CEntryArray : public CBase |
|
51 { |
|
52 public: |
|
53 inline TEntryArray& operator()(); |
|
54 |
|
55 private: |
|
56 /** |
|
57 * Container to hold file entries |
|
58 */ |
|
59 TEntryArray iEntryArray; |
|
60 }; |
|
61 |
|
62 inline TEntryArray& CEntryArray::operator()() |
|
63 { |
|
64 return iEntryArray; |
|
65 } |
|
66 |
|
67 CIntegrityServices* CIntegrityServices::NewL(TStsTransactionId aTransactionID) |
|
68 { |
|
69 CIntegrityServices* self = CIntegrityServices::NewLC(aTransactionID); |
|
70 CleanupStack::Pop(self); |
|
71 return self; |
|
72 } |
|
73 |
|
74 CIntegrityServices* CIntegrityServices::NewLC(TStsTransactionId aTransactionID) |
|
75 { |
|
76 CIntegrityServices* self = new(ELeave) CIntegrityServices(aTransactionID); |
|
77 CleanupStack::PushL(self); |
|
78 self->ConstructL(); |
|
79 return self; |
|
80 } |
|
81 |
|
82 CIntegrityServices::CIntegrityServices(TStsTransactionId aTransactionID) : iTransactionID(aTransactionID) |
|
83 { |
|
84 } |
|
85 |
|
86 CIntegrityServices::~CIntegrityServices() |
|
87 { |
|
88 delete iJournal; |
|
89 iFs.Close(); |
|
90 |
|
91 iLoader.Close(); |
|
92 } |
|
93 |
|
94 void CIntegrityServices::ConstructL() |
|
95 { |
|
96 DEBUG_PRINTF2(_L("CIntegrityServices::ConstructL() - Opening session with Session ID %X."), iTransactionID); |
|
97 |
|
98 User::LeaveIfError(iFs.Connect()); |
|
99 User::LeaveIfError(iFs.ShareProtected()); //needed as new RFiles are to be passed back to client's side |
|
100 User::LeaveIfError(iLoader.Connect()); |
|
101 |
|
102 // store the journal path and create the journal |
|
103 TParsePtrC pathPtr(KTransactionPath); |
|
104 iJournalPath = pathPtr.Path(); |
|
105 iJournal = CJournal::NewL(iFs, iLoader, iTransactionID, iJournalPath); |
|
106 iSystemDrive = ::RFs::GetSystemDrive(); |
|
107 } |
|
108 |
|
109 const TInt KIntegrityServicesSimulatedBatteryFailure=-10205; |
|
110 |
|
111 /*static*/ void CIntegrityServices::SimulatePowerFailureL(TFailType aFailType, TFailPosition aFailPosition, const TDesC& aFailFileName) |
|
112 { |
|
113 if (!iIsFailureTestingEnabled) |
|
114 return; |
|
115 |
|
116 if(iFailType == aFailType && iFailPosition == aFailPosition && iFailFileName == aFailFileName) |
|
117 { |
|
118 User::Leave(KIntegrityServicesSimulatedBatteryFailure); |
|
119 } |
|
120 } |
|
121 |
|
122 /*static*/ void CIntegrityServices::NormalizeDirectoryName(TDes& aFileName) |
|
123 { |
|
124 // Directories are represented in the integrity tree and integrity journal exactly as files, |
|
125 // without the trailing slash |
|
126 TInt lastCharPos = aFileName.Length() - 1; |
|
127 if ( lastCharPos >= 0 && aFileName[lastCharPos] == KPathDelimiter && |
|
128 aFileName.Locate(KPathDelimiter) != lastCharPos) // Take care not to remove slash from "c:\" and the like |
|
129 { |
|
130 aFileName.Delete(lastCharPos, 1); |
|
131 } |
|
132 } |
|
133 |
|
134 void CIntegrityServices::CopyToBackupL(const TDesC& aSource, const TDesC& aBackup) |
|
135 { |
|
136 // Copying a file isn't atomic so we create a temporary backup file first |
|
137 RBuf backupTmpName; |
|
138 backupTmpName.Create(aBackup.Length() + 4); |
|
139 CleanupClosePushL(backupTmpName); |
|
140 backupTmpName.Copy(aBackup); |
|
141 _LIT(KTmpExt, ".tmp"); |
|
142 backupTmpName.Append(KTmpExt); |
|
143 |
|
144 // Copying a file is not an atomic operation so add the temporary |
|
145 // file to the journal to enable cleanup if a power failure occurs before |
|
146 // the rename |
|
147 SimulatePowerFailureL(EFailAddingTempFile, EBeforeJournal, backupTmpName); |
|
148 RegisterTemporaryL(backupTmpName); |
|
149 SimulatePowerFailureL(EFailAddingTempFile, EAfterJournal, backupTmpName); |
|
150 |
|
151 CFileMan* fileMan = CFileMan::NewL(iFs); |
|
152 CleanupStack::PushL(fileMan); |
|
153 |
|
154 TInt err = fileMan->Copy(aSource, backupTmpName); |
|
155 DEBUG_PRINTF4(_L("CIntegrityServices::CopyToBackupL() - Copying %S to %S, err %d"), &aSource, &backupTmpName, err); |
|
156 User::LeaveIfError(err); |
|
157 |
|
158 // Backup is complete, use RFs::Rename as atomic 'commit' of backup |
|
159 err = iFs.Rename(backupTmpName, aBackup); |
|
160 DEBUG_PRINTF2(_L("CIntegrityServices::CopyToBackupL() - Commit backup returned error %d"), err); |
|
161 User::LeaveIfError(err); |
|
162 CleanupStack::PopAndDestroy(2, &backupTmpName); // backupTmpName, fileMan |
|
163 |
|
164 // Now the backup is safe the original can be deleted |
|
165 err = iLoader.Delete(aSource); |
|
166 DEBUG_PRINTF3(_L("CIntegrityServices::CopyToBackupL() - RLoader::Delete %S returned error %d"), &aSource, err); |
|
167 User::LeaveIfError(err); |
|
168 } |
|
169 |
|
170 |
|
171 void CIntegrityServices::RegisterNewL(const TDesC& aFileName) |
|
172 { |
|
173 DEBUG_PRINTF3(_L("CIntegrityServices::RegisterNewL() - Session %X, File: %S."), iTransactionID, &aFileName); |
|
174 |
|
175 HBufC* localFilenameHeap = aFileName.AllocLC(); |
|
176 TPtr localFilename = localFilenameHeap->Des(); |
|
177 NormalizeDirectoryName(localFilename); // If it is a directory name, make sure to normalize it |
|
178 |
|
179 // Record the added file or directory in the journal |
|
180 SimulatePowerFailureL(EFailAddingNewFile, EBeforeJournal, aFileName); |
|
181 iJournal->AddL(localFilename); |
|
182 SimulatePowerFailureL(EFailAddingNewFile, EAfterJournal, aFileName); |
|
183 CleanupStack::PopAndDestroy(localFilenameHeap); |
|
184 } |
|
185 |
|
186 void VerifyMkDirErrorL(TInt err) |
|
187 { |
|
188 if(err != KErrNone && err != KErrAlreadyExists) |
|
189 { |
|
190 User::Leave(err); |
|
191 } |
|
192 } |
|
193 |
|
194 void ProcessNewFileRegistrationResultL(TInt err, RFs& aFs, const TDesC& aFileName, RFile& aFile) |
|
195 { |
|
196 if(err!= KErrNone) |
|
197 { |
|
198 //if we hit this point it means we successfully created the new file however registering with the transaction has failed |
|
199 //so we have to remove the new file to make the journal and the file system consistent |
|
200 aFile.Close(); |
|
201 aFs.Delete(aFileName); |
|
202 User::Leave(err); |
|
203 } |
|
204 } |
|
205 |
|
206 void CIntegrityServices::CreateNewL(const TDesC& aFileName, RFile &newFile, TUint aFileMode) |
|
207 { |
|
208 DEBUG_PRINTF3(_L("CIntegrityServices::CreateNewL() - Session %X, File: %S."), iTransactionID, &aFileName); |
|
209 TInt err = iFs.MkDirAll(aFileName); //first we have to create the full directory path to aFileName otherwise RFs::Create will fail |
|
210 VerifyMkDirErrorL(err); |
|
211 User::LeaveIfError(newFile.Create(iFs, aFileName, aFileMode)); |
|
212 TRAPD(regResult, RegisterNewL(aFileName)); |
|
213 ProcessNewFileRegistrationResultL(regResult, iFs, aFileName, newFile); //checks if the registration failed and cleans up the file in the filesystem if it did |
|
214 } |
|
215 |
|
216 void CIntegrityServices::RemoveL(const TDesC& aFileName) |
|
217 { |
|
218 DEBUG_PRINTF3(_L("CIntegrityServices::RemoveL() - Session %X, File: %S."), iTransactionID, &aFileName); |
|
219 |
|
220 // before doing anything check that the file or directory exists |
|
221 TEntry entry; |
|
222 |
|
223 TInt res = iFs.Entry(aFileName, entry); |
|
224 if (res == KErrNotFound || res == KErrPathNotFound) |
|
225 return; // If the file is not present, do nothing. Returning an error would require the user of the API to do additional checks |
|
226 User::LeaveIfError(res); |
|
227 |
|
228 // We might need to grow this buffer by one byte later |
|
229 HBufC* localFilenameHeap = HBufC::NewLC(aFileName.Length() + 1); |
|
230 TPtr localFilename = localFilenameHeap->Des(); |
|
231 localFilename.Copy(aFileName); |
|
232 |
|
233 TBool isFilenameDir = entry.IsDir(); |
|
234 // The "if" below is not functionally necessary, but it is a slight optimization - |
|
235 // so that we won't attempt to normalize directory name on files. The optimization is not |
|
236 // done in AddL or NormalizeDirectoryName itself, since we don't have future use for TEntry there, and the cost for RFs::Entry overweighs the one for NormalizeDirectoryName |
|
237 if ( isFilenameDir ) |
|
238 { |
|
239 NormalizeDirectoryName(localFilename); |
|
240 } |
|
241 |
|
242 RBuf backupFileName; |
|
243 backupFileName.CreateL(KMaxFileName); |
|
244 CleanupClosePushL(backupFileName); |
|
245 SimulatePowerFailureL(EFailRemovingFile, EBeforeJournal, aFileName); |
|
246 iJournal->RemoveL(localFilename, backupFileName); |
|
247 |
|
248 if (backupFileName.Length()) |
|
249 { |
|
250 SimulatePowerFailureL(EFailRemovingFile, EAfterJournal, aFileName); |
|
251 |
|
252 TInt err = iFs.MkDirAll(backupFileName); |
|
253 VerifyMkDirErrorL(err); |
|
254 |
|
255 SimulatePowerFailureL(EFailRemovingFile, EBeforeAction, aFileName); |
|
256 _LIT(KSysBinMatch, "?:\\sys\\bin\\*"); |
|
257 if (localFilename.MatchF(KSysBinMatch) == 0) |
|
258 { |
|
259 // A copy is slower than a rename to only use the |
|
260 // demand paging safe API for files in sys\bin |
|
261 CopyToBackupL(localFilename, backupFileName); |
|
262 } |
|
263 else |
|
264 { |
|
265 err = iFs.Rename(localFilename, backupFileName); |
|
266 DEBUG_PRINTF4(_L("CIntegrityServices::RemoveL() - Renamed %S as %S error %d"), &localFilename, &backupFileName, err); |
|
267 User::LeaveIfError(err); |
|
268 } |
|
269 SimulatePowerFailureL(EFailRemovingFile, EAfterAction, aFileName); |
|
270 } |
|
271 else |
|
272 { |
|
273 DEBUG_PRINTF2(_L("CIntegrityServices::RemoveL() - %S already backed up"), &aFileName); |
|
274 SimulatePowerFailureL(EFailRemovingFile, EBeforeAction, aFileName); |
|
275 // If backupFileName is zero-length, the file was added earlier |
|
276 // in the same journal and doesn't need to be backed up. |
|
277 if (isFilenameDir) |
|
278 { |
|
279 CFileMan* fileman = CFileMan::NewL(iFs); |
|
280 CleanupStack::PushL(fileman); |
|
281 // Make sure to append slash before calling RmDir - otherwise it deletes the parent directory |
|
282 if (localFilename[localFilename.Length()-1] != KPathDelimiter) |
|
283 { |
|
284 localFilename.Append(KPathDelimiter); |
|
285 } |
|
286 User::LeaveIfError(fileman->RmDir(localFilename)); // A directory cannot be a paged exec., so we don't have to use iLoader |
|
287 CleanupStack::PopAndDestroy(fileman); |
|
288 } |
|
289 else |
|
290 { |
|
291 User::LeaveIfError(iLoader.Delete(aFileName)); |
|
292 } |
|
293 SimulatePowerFailureL(EFailRemovingFile, EAfterAction, aFileName); |
|
294 } |
|
295 |
|
296 // Don't leave an empty directory structure, try pruning it |
|
297 RemoveDirectoryTreeL(iFs, aFileName); |
|
298 |
|
299 CleanupStack::PopAndDestroy(2, localFilenameHeap); // backupFileName |
|
300 } |
|
301 |
|
302 void CIntegrityServices::RegisterTemporaryL(const TDesC& aFileName) |
|
303 { |
|
304 DEBUG_PRINTF3(_L("CIntegrityServices::RegisterTemporaryL() - Session %X, File: %S."), iTransactionID, &aFileName); |
|
305 |
|
306 // record the temporary file or directory in the journal |
|
307 SimulatePowerFailureL(EFailAddingTempFile, EBeforeJournal, aFileName); |
|
308 iJournal->TemporaryL(aFileName); |
|
309 SimulatePowerFailureL(EFailAddingTempFile, EAfterJournal, aFileName); |
|
310 } |
|
311 |
|
312 void CIntegrityServices::CreateTemporaryL(const TDesC& aFileName, RFile &newFile, TUint aFileMode) |
|
313 { |
|
314 DEBUG_PRINTF3(_L("CIntegrityServices::CreateTemporaryL() - Session %X, File: %S."), iTransactionID, &aFileName); |
|
315 |
|
316 TInt err = iFs.MkDirAll(aFileName); //first we have to create the full directory path to aFileName otherwise RFs::Create will fail |
|
317 VerifyMkDirErrorL(err); |
|
318 User::LeaveIfError(newFile.Create(iFs, aFileName, aFileMode)); |
|
319 TRAPD(regResult, RegisterTemporaryL(aFileName)); |
|
320 ProcessNewFileRegistrationResultL(regResult, iFs, aFileName, newFile); |
|
321 } |
|
322 |
|
323 void CIntegrityServices::OverwriteL(const TDesC& aFileName, RFile &newFile, TUint aFileMode) |
|
324 { |
|
325 DEBUG_PRINTF3(_L("CIntegrityServices::OverwriteL() - Session %X, File: %S."), iTransactionID, &aFileName); |
|
326 |
|
327 TBool b; |
|
328 TInt err; |
|
329 if((err=iFs.IsFileOpen(aFileName, b))== KErrNone) //returned error code shows whether the file exists or not; the bool value is ignored |
|
330 { |
|
331 //file exists remove first |
|
332 RemoveL(aFileName); |
|
333 } |
|
334 else |
|
335 { |
|
336 if(err != KErrNotFound) |
|
337 { |
|
338 User::Leave(err); |
|
339 } |
|
340 } |
|
341 CreateNewL(aFileName, newFile, aFileMode); |
|
342 } |
|
343 |
|
344 void CIntegrityServices::CommitL() |
|
345 { |
|
346 DEBUG_PRINTF2(_L("CIntegrityServices::CommitL() - Session %X."), iTransactionID); |
|
347 iJournal->CommitL(); |
|
348 } |
|
349 |
|
350 void CIntegrityServices::RollBackL(TBool aRecordAllRollbackEvents /* = ETrue */) |
|
351 { |
|
352 DEBUG_PRINTF2(_L("CIntegrityServices::RollBackL() - transaction %X"), iTransactionID); |
|
353 iJournal->RollBackL(aRecordAllRollbackEvents); |
|
354 } |
|
355 |
|
356 /*static*/ void CIntegrityServices::GetListOfTransactionsL(RArray<TStsTransactionId>& aIdArray) |
|
357 { |
|
358 RFs fs; |
|
359 User::LeaveIfError(fs.Connect()); |
|
360 CleanupClosePushL(fs); |
|
361 TDriveUnit systemDrive(::RFs::GetSystemDrive()); |
|
362 RBuf fileSpec; |
|
363 fileSpec.CreateL(systemDrive.Name(), KMaxFileName); |
|
364 CleanupClosePushL(fileSpec); |
|
365 fileSpec.Append(KTransactionPath); |
|
366 fileSpec.Append(KMatchAny); |
|
367 fileSpec.Append(KExtDelimiter); |
|
368 fileSpec.Append(KDriveExt); |
|
369 |
|
370 RDir dir; |
|
371 TInt err = dir.Open(fs, fileSpec, KEntryAttNormal); |
|
372 CleanupStack::PopAndDestroy(&fileSpec); |
|
373 if (err == KErrPathNotFound || err == KErrNotFound) |
|
374 { |
|
375 CleanupStack::PopAndDestroy(&fs); |
|
376 return; // These errors are not considered fatal - there may be no journals present |
|
377 } |
|
378 User::LeaveIfError(err); |
|
379 CleanupClosePushL(dir); |
|
380 |
|
381 CEntryArray* entryArrayContainer = new (ELeave) CEntryArray; |
|
382 TEntryArray& entryArray = (*entryArrayContainer)(); |
|
383 err = dir.Read(entryArray); |
|
384 CleanupStack::PopAndDestroy(&dir); |
|
385 CleanupStack::PushL(entryArrayContainer); |
|
386 if (err != KErrNone && err != KErrEof) |
|
387 { |
|
388 User::Leave(err); |
|
389 } |
|
390 TInt entryCount(entryArray.Count()); |
|
391 for (TInt index = 0; index < entryCount; ++index) |
|
392 { |
|
393 TStsTransactionId transactionID; |
|
394 if (CJournal::RecoverTransactionIdFromDrvFileName( |
|
395 entryArray[index].iName, transactionID) == KErrNone) |
|
396 { |
|
397 aIdArray.AppendL(transactionID); |
|
398 } |
|
399 } |
|
400 CleanupStack::PopAndDestroy(entryArrayContainer); |
|
401 CleanupStack::PopAndDestroy(&fs); |
|
402 } |
|
403 |
|
404 /*static*/ void CIntegrityServices::RollbackAllL() |
|
405 { |
|
406 RArray<TStsTransactionId> transactionIDs; |
|
407 CleanupClosePushL(transactionIDs); |
|
408 CIntegrityServices::GetListOfTransactionsL(transactionIDs); |
|
409 TInt numberOfTransactions(transactionIDs.Count()); |
|
410 TInt lastError=KErrNone; |
|
411 DEBUG_PRINTF2(_L("CIntegrityServices::RollbackAllL() %d transactions have been found."), numberOfTransactions ); |
|
412 for(TInt i=0; i<numberOfTransactions; ++i) |
|
413 { |
|
414 DEBUG_PRINTF2(_L("CIntegrityServices::RollbackAllL() Trying to roll back transaction %X"), transactionIDs[i]); |
|
415 TRAPD(err, |
|
416 CIntegrityServices* transactionPtr = CIntegrityServices::NewLC(transactionIDs[i]); |
|
417 transactionPtr->RollBackL(EFalse); // Specify not to record roll back events. If we failed in the middle of a previous roll back, due to lack of resources we shouldn't be trying to record more events |
|
418 CleanupStack::PopAndDestroy(transactionPtr); |
|
419 ); //failing to roll back one transaction shouldn't affect the overall rollback all procedure |
|
420 if(err!=KErrNone) |
|
421 { |
|
422 lastError=err; //remember last error and leave with that error code indicating an error in the overall procedure |
|
423 } |
|
424 DEBUG_PRINTF2(_L("CIntegrityServices::RollbackAllL() Rolled back transaction %X"), transactionIDs[i]); |
|
425 } |
|
426 CleanupStack::PopAndDestroy(&transactionIDs); |
|
427 User::LeaveIfError(lastError); |
|
428 } |