|
1 /* |
|
2 * Copyright (c) 2008-2010 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::RegisterNewL(const TDesC& aFileName) |
|
135 { |
|
136 DEBUG_PRINTF3(_L("CIntegrityServices::RegisterNewL() - Session %X, File: %S."), iTransactionID, &aFileName); |
|
137 |
|
138 HBufC* localFilenameHeap = aFileName.AllocLC(); |
|
139 TPtr localFilename = localFilenameHeap->Des(); |
|
140 NormalizeDirectoryName(localFilename); // If it is a directory name, make sure to normalize it |
|
141 |
|
142 // Record the added file or directory in the journal |
|
143 SimulatePowerFailureL(EFailAddingNewFile, EBeforeJournal, aFileName); |
|
144 iJournal->AddL(localFilename); |
|
145 SimulatePowerFailureL(EFailAddingNewFile, EAfterJournal, aFileName); |
|
146 CleanupStack::PopAndDestroy(localFilenameHeap); |
|
147 } |
|
148 |
|
149 void VerifyMkDirErrorL(TInt err) |
|
150 { |
|
151 if(err != KErrNone && err != KErrAlreadyExists) |
|
152 { |
|
153 User::Leave(err); |
|
154 } |
|
155 } |
|
156 |
|
157 void ProcessNewFileRegistrationResultL(TInt err, RFs& aFs, const TDesC& aFileName, RFile& aFile) |
|
158 { |
|
159 if(err!= KErrNone) |
|
160 { |
|
161 //if we hit this point it means we successfully created the new file however registering with the transaction has failed |
|
162 //so we have to remove the new file to make the journal and the file system consistent |
|
163 aFile.Close(); |
|
164 aFs.Delete(aFileName); |
|
165 User::Leave(err); |
|
166 } |
|
167 } |
|
168 |
|
169 void CIntegrityServices::CreateNewL(const TDesC& aFileName, RFile &newFile, TUint aFileMode) |
|
170 { |
|
171 DEBUG_PRINTF3(_L("CIntegrityServices::CreateNewL() - Session %X, File: %S."), iTransactionID, &aFileName); |
|
172 TInt err = iFs.MkDirAll(aFileName); //first we have to create the full directory path to aFileName otherwise RFs::Create will fail |
|
173 VerifyMkDirErrorL(err); |
|
174 User::LeaveIfError(newFile.Create(iFs, aFileName, aFileMode)); |
|
175 TRAPD(regResult, RegisterNewL(aFileName)); |
|
176 ProcessNewFileRegistrationResultL(regResult, iFs, aFileName, newFile); //checks if the registration failed and cleans up the file in the filesystem if it did |
|
177 } |
|
178 |
|
179 void CIntegrityServices::RemoveL(const TDesC& aFileName) |
|
180 { |
|
181 DEBUG_PRINTF3(_L("CIntegrityServices::RemoveL() - Session %X, File: %S."), iTransactionID, &aFileName); |
|
182 |
|
183 // before doing anything check that the file or directory exists |
|
184 TEntry entry; |
|
185 |
|
186 TInt res = iFs.Entry(aFileName, entry); |
|
187 if (res == KErrNotFound || res == KErrPathNotFound) |
|
188 return; // If the file is not present, do nothing. Returning an error would require the user of the API to do additional checks |
|
189 User::LeaveIfError(res); |
|
190 |
|
191 // We might need to grow this buffer by one byte later |
|
192 HBufC* localFilenameHeap = HBufC::NewLC(aFileName.Length() + 1); |
|
193 TPtr localFilename = localFilenameHeap->Des(); |
|
194 localFilename.Copy(aFileName); |
|
195 |
|
196 TBool isFilenameDir = entry.IsDir(); |
|
197 // The "if" below is not functionally necessary, but it is a slight optimization - |
|
198 // so that we won't attempt to normalize directory name on files. The optimization is not |
|
199 // 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 |
|
200 if ( isFilenameDir ) |
|
201 { |
|
202 NormalizeDirectoryName(localFilename); |
|
203 } |
|
204 |
|
205 RBuf backupFileName; |
|
206 backupFileName.CreateL(KMaxFileName); |
|
207 CleanupClosePushL(backupFileName); |
|
208 SimulatePowerFailureL(EFailRemovingFile, EBeforeJournal, aFileName); |
|
209 iJournal->RemoveL(localFilename, backupFileName); |
|
210 |
|
211 if (backupFileName.Length()) |
|
212 { |
|
213 SimulatePowerFailureL(EFailRemovingFile, EAfterJournal, aFileName); |
|
214 |
|
215 TInt err = iFs.MkDirAll(backupFileName); |
|
216 VerifyMkDirErrorL(err); |
|
217 |
|
218 SimulatePowerFailureL(EFailRemovingFile, EBeforeAction, aFileName); |
|
219 err = iFs.Rename(localFilename, backupFileName); |
|
220 DEBUG_PRINTF4(_L("CIntegrityServices::RemoveL() - Renamed %S as %S error %d"), &localFilename, &backupFileName, err); |
|
221 User::LeaveIfError(err); |
|
222 SimulatePowerFailureL(EFailRemovingFile, EAfterAction, aFileName); |
|
223 } |
|
224 else |
|
225 { |
|
226 DEBUG_PRINTF2(_L("CIntegrityServices::RemoveL() - %S already backed up"), &aFileName); |
|
227 SimulatePowerFailureL(EFailRemovingFile, EBeforeAction, aFileName); |
|
228 // If backupFileName is zero-length, the file was added earlier |
|
229 // in the same journal and doesn't need to be backed up. |
|
230 if (isFilenameDir) |
|
231 { |
|
232 CFileMan* fileman = CFileMan::NewL(iFs); |
|
233 CleanupStack::PushL(fileman); |
|
234 // Make sure to append slash before calling RmDir - otherwise it deletes the parent directory |
|
235 if (localFilename[localFilename.Length()-1] != KPathDelimiter) |
|
236 { |
|
237 localFilename.Append(KPathDelimiter); |
|
238 } |
|
239 User::LeaveIfError(fileman->RmDir(localFilename)); // A directory cannot be a paged exec., so we don't have to use iLoader |
|
240 CleanupStack::PopAndDestroy(fileman); |
|
241 } |
|
242 else |
|
243 { |
|
244 User::LeaveIfError(iLoader.Delete(aFileName)); |
|
245 } |
|
246 SimulatePowerFailureL(EFailRemovingFile, EAfterAction, aFileName); |
|
247 } |
|
248 |
|
249 // Don't leave an empty directory structure, try pruning it |
|
250 RemoveDirectoryTreeL(iFs, aFileName); |
|
251 |
|
252 CleanupStack::PopAndDestroy(2, localFilenameHeap); // backupFileName |
|
253 } |
|
254 |
|
255 void CIntegrityServices::RegisterTemporaryL(const TDesC& aFileName) |
|
256 { |
|
257 DEBUG_PRINTF3(_L("CIntegrityServices::RegisterTemporaryL() - Session %X, File: %S."), iTransactionID, &aFileName); |
|
258 |
|
259 // record the temporary file or directory in the journal |
|
260 SimulatePowerFailureL(EFailAddingTempFile, EBeforeJournal, aFileName); |
|
261 iJournal->TemporaryL(aFileName); |
|
262 SimulatePowerFailureL(EFailAddingTempFile, EAfterJournal, aFileName); |
|
263 } |
|
264 |
|
265 void CIntegrityServices::CreateTemporaryL(const TDesC& aFileName, RFile &newFile, TUint aFileMode) |
|
266 { |
|
267 DEBUG_PRINTF3(_L("CIntegrityServices::CreateTemporaryL() - Session %X, File: %S."), iTransactionID, &aFileName); |
|
268 |
|
269 TInt err = iFs.MkDirAll(aFileName); //first we have to create the full directory path to aFileName otherwise RFs::Create will fail |
|
270 VerifyMkDirErrorL(err); |
|
271 User::LeaveIfError(newFile.Create(iFs, aFileName, aFileMode)); |
|
272 TRAPD(regResult, RegisterTemporaryL(aFileName)); |
|
273 ProcessNewFileRegistrationResultL(regResult, iFs, aFileName, newFile); |
|
274 } |
|
275 |
|
276 void CIntegrityServices::OverwriteL(const TDesC& aFileName, RFile &newFile, TUint aFileMode) |
|
277 { |
|
278 DEBUG_PRINTF3(_L("CIntegrityServices::OverwriteL() - Session %X, File: %S."), iTransactionID, &aFileName); |
|
279 |
|
280 TBool b; |
|
281 TInt err; |
|
282 if((err=iFs.IsFileOpen(aFileName, b))== KErrNone) //returned error code shows whether the file exists or not; the bool value is ignored |
|
283 { |
|
284 //file exists remove first |
|
285 RemoveL(aFileName); |
|
286 } |
|
287 else |
|
288 { |
|
289 if(err != KErrNotFound) |
|
290 { |
|
291 User::Leave(err); |
|
292 } |
|
293 } |
|
294 CreateNewL(aFileName, newFile, aFileMode); |
|
295 } |
|
296 |
|
297 void CIntegrityServices::CommitL() |
|
298 { |
|
299 DEBUG_PRINTF2(_L("CIntegrityServices::CommitL() - Session %X."), iTransactionID); |
|
300 iJournal->CommitL(); |
|
301 } |
|
302 |
|
303 void CIntegrityServices::RollBackL(TBool aRecordAllRollbackEvents /* = ETrue */) |
|
304 { |
|
305 DEBUG_PRINTF2(_L("CIntegrityServices::RollBackL() - transaction %X"), iTransactionID); |
|
306 iJournal->RollBackL(aRecordAllRollbackEvents); |
|
307 } |
|
308 |
|
309 /*static*/ void CIntegrityServices::GetListOfTransactionsL(RArray<TStsTransactionId>& aIdArray) |
|
310 { |
|
311 RFs fs; |
|
312 User::LeaveIfError(fs.Connect()); |
|
313 CleanupClosePushL(fs); |
|
314 TDriveUnit systemDrive(::RFs::GetSystemDrive()); |
|
315 RBuf fileSpec; |
|
316 fileSpec.CreateL(systemDrive.Name(), KMaxFileName); |
|
317 CleanupClosePushL(fileSpec); |
|
318 fileSpec.Append(KTransactionPath); |
|
319 fileSpec.Append(KMatchAny); |
|
320 fileSpec.Append(KExtDelimiter); |
|
321 fileSpec.Append(KDriveExt); |
|
322 |
|
323 RDir dir; |
|
324 TInt err = dir.Open(fs, fileSpec, KEntryAttNormal); |
|
325 CleanupStack::PopAndDestroy(&fileSpec); |
|
326 if (err == KErrPathNotFound || err == KErrNotFound) |
|
327 { |
|
328 CleanupStack::PopAndDestroy(&fs); |
|
329 return; // These errors are not considered fatal - there may be no journals present |
|
330 } |
|
331 User::LeaveIfError(err); |
|
332 CleanupClosePushL(dir); |
|
333 |
|
334 CEntryArray* entryArrayContainer = new (ELeave) CEntryArray; |
|
335 TEntryArray& entryArray = (*entryArrayContainer)(); |
|
336 err = dir.Read(entryArray); |
|
337 CleanupStack::PopAndDestroy(&dir); |
|
338 CleanupStack::PushL(entryArrayContainer); |
|
339 if (err != KErrNone && err != KErrEof) |
|
340 { |
|
341 User::Leave(err); |
|
342 } |
|
343 TInt entryCount(entryArray.Count()); |
|
344 for (TInt index = 0; index < entryCount; ++index) |
|
345 { |
|
346 TStsTransactionId transactionID; |
|
347 if (CJournal::RecoverTransactionIdFromDrvFileName( |
|
348 entryArray[index].iName, transactionID) == KErrNone) |
|
349 { |
|
350 aIdArray.AppendL(transactionID); |
|
351 } |
|
352 } |
|
353 CleanupStack::PopAndDestroy(entryArrayContainer); |
|
354 CleanupStack::PopAndDestroy(&fs); |
|
355 } |
|
356 |
|
357 /*static*/ void CIntegrityServices::RollbackAllL() |
|
358 { |
|
359 RArray<TStsTransactionId> transactionIDs; |
|
360 CleanupClosePushL(transactionIDs); |
|
361 CIntegrityServices::GetListOfTransactionsL(transactionIDs); |
|
362 TInt numberOfTransactions(transactionIDs.Count()); |
|
363 TInt lastError=KErrNone; |
|
364 DEBUG_PRINTF2(_L("CIntegrityServices::RollbackAllL() %d transactions have been found."), numberOfTransactions ); |
|
365 for(TInt i=0; i<numberOfTransactions; ++i) |
|
366 { |
|
367 DEBUG_PRINTF2(_L("CIntegrityServices::RollbackAllL() Trying to roll back transaction %X"), transactionIDs[i]); |
|
368 TRAPD(err, |
|
369 CIntegrityServices* transactionPtr = CIntegrityServices::NewLC(transactionIDs[i]); |
|
370 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 |
|
371 CleanupStack::PopAndDestroy(transactionPtr); |
|
372 ); //failing to roll back one transaction shouldn't affect the overall rollback all procedure |
|
373 if(err!=KErrNone) |
|
374 { |
|
375 lastError=err; //remember last error and leave with that error code indicating an error in the overall procedure |
|
376 } |
|
377 DEBUG_PRINTF2(_L("CIntegrityServices::RollbackAllL() Rolled back transaction %X"), transactionIDs[i]); |
|
378 } |
|
379 CleanupStack::PopAndDestroy(&transactionIDs); |
|
380 User::LeaveIfError(lastError); |
|
381 } |