|
1 /* |
|
2 * Copyright (c) 2004-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 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include "restoreprocessor.h" |
|
20 |
|
21 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
22 #include <usif/sts/sts.h> |
|
23 #include "swtypereginfo.h" |
|
24 #include "installswtypehelper.h" |
|
25 #else |
|
26 #include "integrityservices.h" |
|
27 #endif |
|
28 |
|
29 #include "application.h" |
|
30 #include "hashcontainer.h" |
|
31 #include "secutils.h" |
|
32 #include "sisregistryentry.h" |
|
33 #include "sisregistrypackage.h" |
|
34 #include "sisinfo.h" |
|
35 #include "sisuid.h" |
|
36 #include "sistruststatus.h" |
|
37 #include "log.h" |
|
38 #include "securitycheckutil.h" |
|
39 #include "sidcache.h" |
|
40 #include <f32file.h> |
|
41 |
|
42 using namespace Swi; |
|
43 |
|
44 |
|
45 // |
|
46 // CRestoreProcessor |
|
47 // |
|
48 |
|
49 CRestoreProcessor::CRestoreProcessor(const CPlan& aPlan, const TDesC8& aControllerBuffer, |
|
50 CSecurityManager& aSecurityManager, |
|
51 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
52 Usif::RStsSession& aStsSession, RSisRegistryWritableSession& aRegistrySession, |
|
53 #else |
|
54 CIntegrityServices& aIntegrityServices, |
|
55 #endif |
|
56 const RPointerArray<CRestoreController::CSisCertificateVerifier>& aVerifiers,RSwiObserverSession& aObserver) |
|
57 : CActive(CActive::EPriorityStandard), |
|
58 iVerifiers(aVerifiers), |
|
59 iSecurityManager(aSecurityManager), |
|
60 iControllerBuffer(aControllerBuffer), |
|
61 iPlan(aPlan), |
|
62 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
63 iStsSession(aStsSession), |
|
64 iRegistrySession(aRegistrySession), |
|
65 #else |
|
66 iIntegrityServices(aIntegrityServices), |
|
67 #endif |
|
68 iSystemDriveChar(RFs::GetSystemDriveChar()), |
|
69 iObserver(aObserver) |
|
70 { |
|
71 CActiveScheduler::Add(this); |
|
72 } |
|
73 |
|
74 CRestoreProcessor* CRestoreProcessor::NewL(const CPlan& aPlan, const TDesC8& aControllerBuffer, CSecurityManager& aSecurityManager, |
|
75 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
76 Usif::RStsSession& aStsSession, RSisRegistryWritableSession& aRegistrySession, |
|
77 #else |
|
78 CIntegrityServices& aIntegrityServices, |
|
79 #endif |
|
80 const RPointerArray<CRestoreController::CSisCertificateVerifier>& aVerifiers, |
|
81 RArray<TUid>& aSids, RSwiObserverSession& aObserver) |
|
82 { |
|
83 CRestoreProcessor* self = CRestoreProcessor::NewLC(aPlan, aControllerBuffer, aSecurityManager, |
|
84 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
85 aStsSession, aRegistrySession, |
|
86 #else |
|
87 aIntegrityServices, |
|
88 #endif |
|
89 aVerifiers, aSids, aObserver); |
|
90 CleanupStack::Pop(self); |
|
91 return self; |
|
92 } |
|
93 |
|
94 CRestoreProcessor* CRestoreProcessor::NewLC(const CPlan& aPlan, const TDesC8& aControllerBuffer, CSecurityManager& aSecurityManager, |
|
95 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
96 Usif::RStsSession& aStsSession, RSisRegistryWritableSession& aRegistrySession, |
|
97 #else |
|
98 CIntegrityServices& aIntegrityServices, |
|
99 #endif |
|
100 const RPointerArray<CRestoreController::CSisCertificateVerifier>& aVerifiers, |
|
101 RArray<TUid>& aSids, RSwiObserverSession& aObserver) |
|
102 { |
|
103 CRestoreProcessor* self = new (ELeave) CRestoreProcessor(aPlan, aControllerBuffer, aSecurityManager, |
|
104 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
105 aStsSession, aRegistrySession, |
|
106 #else |
|
107 aIntegrityServices, |
|
108 #endif |
|
109 aVerifiers, aObserver); |
|
110 CleanupStack::PushL(self); |
|
111 self->ConstructL(aSids); |
|
112 return self; |
|
113 } |
|
114 |
|
115 void CRestoreProcessor::InstallFileL(const CSisRegistryFileDescription& aRegistryFileDescription) |
|
116 { |
|
117 TParsePtrC parse(aRegistryFileDescription.Target()); |
|
118 |
|
119 TFileName tempFileName; |
|
120 _LIT(KTemporaryFileFormat, "%c:%Stfile-%d-%d-%d-"); |
|
121 TUint driveCh(iSystemDriveChar); |
|
122 tempFileName.Format(KTemporaryFileFormat, driveCh, &KSysInstallTempPath, |
|
123 iApplication->ControllerL().Info().Uid().Uid().iUid, iApplication->AbsoluteDataIndex(), aRegistryFileDescription.Index()); |
|
124 |
|
125 // Append a hex representation of a hash of the filename. We need the |
|
126 // temporary filename to be effectively unique, but the filename itself |
|
127 // may be too long. |
|
128 // Use the entire path minus the drive letter because the drive letter can change. |
|
129 // For consistency, the target name must be the name from controller not the target |
|
130 // after the path substitution code. |
|
131 HBufC* hashBuf = SecUtils::HexHashL(parse.FullName().Right(parse.FullName().Length() - 1)); |
|
132 tempFileName.Append(*hashBuf); |
|
133 delete hashBuf; |
|
134 |
|
135 DEBUG_PRINTF3(_L("Restore - Installing File '%S' from '%S'"), &aRegistryFileDescription.Target(), &tempFileName); |
|
136 |
|
137 TEntry entry; |
|
138 TInt err = iFs.Entry(tempFileName, entry); |
|
139 if (err == KErrNone) |
|
140 { |
|
141 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
142 TRAP(err, iStsSession.RemoveL(aRegistryFileDescription.Target())); |
|
143 #else |
|
144 TRAP(err, iIntegrityServices.RemoveL(aRegistryFileDescription.Target())); |
|
145 #endif |
|
146 if (err != KErrNone && err != KErrPathNotFound && err != KErrNotFound) |
|
147 { |
|
148 User::Leave(err); |
|
149 } |
|
150 |
|
151 TInt err = iFs.MkDirAll(aRegistryFileDescription.Target()); |
|
152 if (err != KErrNone && err != KErrAlreadyExists) |
|
153 { |
|
154 User::LeaveIfError(err); |
|
155 } |
|
156 |
|
157 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
158 iStsSession.RegisterNewL(aRegistryFileDescription.Target()); |
|
159 #else |
|
160 iIntegrityServices.AddL(aRegistryFileDescription.Target()); |
|
161 #endif |
|
162 User::LeaveIfError(iFileMan->Move(tempFileName, aRegistryFileDescription.Target())); |
|
163 } |
|
164 else |
|
165 { |
|
166 DEBUG_PRINTF3(_L("Cannot find '%S' assuming SBE will restore public data err = %d"), &tempFileName, err); |
|
167 } |
|
168 |
|
169 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
170 // Parse the file if it carries software type registration info |
|
171 if (InstallSoftwareTypeHelper::IsValidSwRegFileL(aRegistryFileDescription.Target(), |
|
172 iApplication->ControllerL().Info().Uid().Uid().iUid)) |
|
173 { |
|
174 InstallSoftwareTypeHelper::ParseRegFileL(iFs, |
|
175 aRegistryFileDescription.Target(), |
|
176 iSoftwareTypeRegInfoArray); |
|
177 } |
|
178 #endif |
|
179 |
|
180 } |
|
181 |
|
182 void CRestoreProcessor::ConstructL(RArray<TUid>& aSids) |
|
183 { |
|
184 User::LeaveIfError(iFs.Connect()); |
|
185 User::LeaveIfError(iFs.ShareProtected()); |
|
186 iFileMan = CFileMan::NewL(iFs); |
|
187 for (TInt i = 0; i < aSids.Count(); ++i) |
|
188 { |
|
189 iSids.AppendL(aSids[i]); |
|
190 } |
|
191 } |
|
192 |
|
193 void CRestoreProcessor::ExtractHashL(const CSisRegistryFileDescription& aFileToProcess) |
|
194 { |
|
195 TBuf<32> hashPath; |
|
196 TUint driveCh(iSystemDriveChar); // can't pass TChar to Format |
|
197 hashPath.Format(KHashPathFormat, driveCh, &KHashPath); |
|
198 TParse hashFileName; |
|
199 hashFileName.Set(hashPath, &aFileToProcess.Target(), NULL); |
|
200 |
|
201 TEntry hashEntry; |
|
202 TInt err = iFs.Entry(hashFileName.FullName(), hashEntry); |
|
203 |
|
204 if (err == KErrNone) |
|
205 { |
|
206 // Hash file exists, need to remove the old one. |
|
207 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
208 iStsSession.RemoveL(hashFileName.FullName()); |
|
209 #else |
|
210 iIntegrityServices.RemoveL(hashFileName.FullName()); |
|
211 #endif |
|
212 } |
|
213 |
|
214 err = iFs.MkDirAll(hashFileName.DriveAndPath()); |
|
215 if (err != KErrNone && err != KErrAlreadyExists) |
|
216 { |
|
217 User::Leave(err); |
|
218 } |
|
219 |
|
220 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
221 iStsSession.RegisterNewL(hashFileName.FullName()); |
|
222 #else |
|
223 iIntegrityServices.AddL(hashFileName.FullName()); |
|
224 #endif |
|
225 |
|
226 DEBUG_PRINTF2(_L("Restore - Extracting hash to file '%S'"), |
|
227 &hashFileName.FullName()); |
|
228 |
|
229 RFile file; |
|
230 User::LeaveIfError(file.Create(iFs, hashFileName.FullName(), |
|
231 EFileWrite | EFileShareExclusive | EFileStream)); |
|
232 |
|
233 CleanupClosePushL(file); |
|
234 const TDesC8& hashData = aFileToProcess.Hash().Data(); |
|
235 User::LeaveIfError(file.Write(hashData)); |
|
236 CleanupStack::PopAndDestroy(&file); |
|
237 } |
|
238 |
|
239 TBool CRestoreProcessor::DoStateInitializeL() |
|
240 { |
|
241 iCurrent = 0; |
|
242 return ETrue; |
|
243 } |
|
244 |
|
245 |
|
246 TBool CRestoreProcessor::DoStateInstallFilesL() |
|
247 { |
|
248 if (iCurrent < iApplication->FilesToAdd().Count()) |
|
249 { |
|
250 if(0 == iCurrent) |
|
251 {//Write package header before logging the first install event |
|
252 CObservationHeader *header = CObservationHeader::NewLC(iApplication->ControllerL().Info().Uid().Uid(), |
|
253 static_cast<TPackageType>(iApplication->ControllerL().Info().InstallType()), Swi::EOpInstall); |
|
254 iObserver.AddHeaderL(*header); |
|
255 CleanupStack::PopAndDestroy(header); |
|
256 } |
|
257 |
|
258 //Get file description |
|
259 CSisRegistryFileDescription* regFileDes = iApplication->FilesToAdd()[iCurrent++]; |
|
260 //Complete actual file installation |
|
261 InstallFileL(*regFileDes); |
|
262 TUint8 fileFlag(EFileAdded); |
|
263 if(regFileDes->Sid().iUid) |
|
264 {//if the file has a SID, it can be exe or dll |
|
265 if(SecUtils::IsExeL(regFileDes->Target())) |
|
266 {//Set file exe flag. |
|
267 fileFlag |= Swi::EFileExe; |
|
268 } |
|
269 else if(SecUtils::IsDllL(regFileDes->Target())) |
|
270 {//Set file dll flag. |
|
271 fileFlag |= Swi::EFileDll; |
|
272 } |
|
273 } |
|
274 |
|
275 //Write the name of the file into the observation file |
|
276 CObservationData *event = CObservationData::NewLC(regFileDes->Target(),regFileDes->Sid(),fileFlag); |
|
277 iObserver.AddEventL(*event); |
|
278 CleanupStack::PopAndDestroy(event); |
|
279 SwitchState(ECurrentState); |
|
280 return EFalse; |
|
281 } |
|
282 else |
|
283 { |
|
284 iCurrent = 0; |
|
285 return ETrue; |
|
286 } |
|
287 } |
|
288 |
|
289 TBool CRestoreProcessor::DoStateUpdateRegistryL() |
|
290 { |
|
291 #ifndef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
292 RSisRegistryWritableSession session; |
|
293 User::LeaveIfError(session.Connect()); |
|
294 CleanupClosePushL(session); |
|
295 #endif |
|
296 |
|
297 Swi::RSisRegistryEntry entry; |
|
298 TBool entryInROM(EFalse); |
|
299 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
300 TInt err = entry.Open(iRegistrySession, iApplication->ControllerL().Info().Uid().Uid()); |
|
301 #else |
|
302 TInt err = entry.Open(session, iApplication->ControllerL().Info().Uid().Uid()); |
|
303 #endif |
|
304 if (err == KErrNone) |
|
305 { |
|
306 CleanupClosePushL(entry); |
|
307 entryInROM = entry.IsInRomL(); |
|
308 CleanupStack::PopAndDestroy(&entry); |
|
309 } |
|
310 |
|
311 if (iApplication->IsUpgrade() || iApplication->IsPartialUpgrade() || (iApplication->IsInstall() && entryInROM)) |
|
312 { |
|
313 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
314 if (iSoftwareTypeRegInfoArray.Count() > 0) |
|
315 { |
|
316 iRegistrySession.UpdateEntryL(*iApplication, iControllerBuffer, iSoftwareTypeRegInfoArray, iStsSession.TransactionIdL()); |
|
317 } |
|
318 else |
|
319 { |
|
320 iRegistrySession.UpdateEntryL(*iApplication, iControllerBuffer, iStsSession.TransactionIdL()); |
|
321 } |
|
322 #else |
|
323 session.UpdateEntryL(*iApplication, iControllerBuffer, iIntegrityServices.TransactionId()); |
|
324 #endif |
|
325 } |
|
326 else |
|
327 { |
|
328 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
329 if (iSoftwareTypeRegInfoArray.Count() > 0) |
|
330 { |
|
331 iRegistrySession.AddEntryL(*iApplication, iControllerBuffer, iSoftwareTypeRegInfoArray, iStsSession.TransactionIdL()); |
|
332 } |
|
333 else |
|
334 { |
|
335 iRegistrySession.AddEntryL(*iApplication, iControllerBuffer, iStsSession.TransactionIdL()); |
|
336 } |
|
337 #else |
|
338 session.AddEntryL(*iApplication, iControllerBuffer, iIntegrityServices.TransactionId()); |
|
339 #endif |
|
340 } |
|
341 |
|
342 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
343 // Registration of MIME types of the software types being installed to AppArc |
|
344 InstallSoftwareTypeHelper::RegisterMimeTypesL(iSoftwareTypeRegInfoArray); |
|
345 #else |
|
346 CleanupStack::PopAndDestroy(&session); |
|
347 #endif |
|
348 return ETrue; |
|
349 } |
|
350 |
|
351 TBool CRestoreProcessor::DoStateProcessFilesL() |
|
352 { |
|
353 if (iCurrent < iApplication->FilesToAdd().Count()) |
|
354 { |
|
355 |
|
356 // determine the temporary file name from the registry description |
|
357 // and controller |
|
358 CSisRegistryFileDescription* aFileToProcess = iApplication->FilesToAdd()[iCurrent++]; |
|
359 TParsePtrC parse(aFileToProcess->Target()); |
|
360 |
|
361 _LIT(KTemporaryFileFormat, "%c:%Stfile-%d-%d-%d-"); |
|
362 TFileName tempFileName; |
|
363 TUint driveCh(iSystemDriveChar); // Can't pass TChar to Format. |
|
364 tempFileName.Format(KTemporaryFileFormat, driveCh, &KSysInstallTempPath, |
|
365 iApplication->ControllerL().Info().Uid().Uid().iUid, iApplication->AbsoluteDataIndex(), aFileToProcess->Index()); |
|
366 |
|
367 // Append a hex representation of a hash of the filename. We need the |
|
368 // temporary filename to be effectively unique, but the filename itself |
|
369 // may be too long. |
|
370 // Use the entire path minus the drive letter because the drive letter can change. |
|
371 // For consistency, the target name must be the name from controller not the target |
|
372 // after the path substitution code. |
|
373 HBufC* hashBuf = SecUtils::HexHashL(parse.FullName().Right(parse.FullName().Length() - 1)); |
|
374 tempFileName.Append(*hashBuf); |
|
375 delete hashBuf; |
|
376 |
|
377 //if the file is an exe or dll, which in restore machine |
|
378 //is more or less guarantee'd, then we need to add the hash |
|
379 // to /sys/hash |
|
380 |
|
381 TEntry entry; |
|
382 TInt err = iFs.Entry(tempFileName, entry); |
|
383 if (err == KErrNone) |
|
384 { |
|
385 // Process the file if the temporary actually exists |
|
386 TInt err = SecurityCheckUtil::ProcessFileL(*iApplication, iFs, iSids, |
|
387 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
388 iStsSession, |
|
389 #else |
|
390 iIntegrityServices, |
|
391 #endif |
|
392 *aFileToProcess, tempFileName); |
|
393 if (err != KErrNone) |
|
394 { |
|
395 User::Leave(KErrCorrupt); |
|
396 } |
|
397 |
|
398 // extract a hash file for this, if the target is /sys/bin |
|
399 if (parse.Path().CompareF(KBinPath) == 0) |
|
400 { |
|
401 ExtractHashL(*aFileToProcess); |
|
402 } |
|
403 } |
|
404 SwitchState(ECurrentState); |
|
405 return EFalse; |
|
406 } |
|
407 else |
|
408 { |
|
409 iCurrent = 0; |
|
410 return ETrue; |
|
411 } |
|
412 } |
|
413 |
|
414 TBool CRestoreProcessor::DoStateVerifyPathsL() |
|
415 { |
|
416 if (iCurrent < iApplication->FilesToAdd().Count()) |
|
417 { |
|
418 CSisRegistryFileDescription* fileToProcess = iApplication->FilesToAdd()[iCurrent++]; |
|
419 |
|
420 SecurityCheckUtil::TProtectedDirectoryCheckError dummyErrorCode; |
|
421 // sis file signed by Su Cert are allowed to restore files in private dir |
|
422 // without corresponding executable in the package. |
|
423 TBool pathValid = SecurityCheckUtil::CheckProtectedDirectoriesL( |
|
424 fileToProcess->Target(), |
|
425 fileToProcess->Operation(), |
|
426 iSids, |
|
427 dummyErrorCode); |
|
428 |
|
429 if (!pathValid && !iApplication->IsInstallSuCertBased()) |
|
430 { |
|
431 User::Leave(KErrAccessDenied); |
|
432 } |
|
433 |
|
434 SwitchState(ECurrentState); |
|
435 return EFalse; |
|
436 } |
|
437 else |
|
438 { |
|
439 iCurrent = 0; |
|
440 return ETrue; |
|
441 } |
|
442 } |
|
443 |
|
444 void CRestoreProcessor::RunL() |
|
445 { |
|
446 DEBUG_PRINTF3(_L8("Restore - Restore processing state machine, State: %d, Status: %d"), |
|
447 iState, iStatus.Int()); |
|
448 |
|
449 // Leave if there has been an error |
|
450 User::LeaveIfError(iStatus.Int()); |
|
451 |
|
452 switch(iState) |
|
453 { |
|
454 case EInitialize: |
|
455 if (DoStateInitializeL()) |
|
456 { |
|
457 SwitchState(EProcessFiles); |
|
458 } |
|
459 break; |
|
460 |
|
461 case EProcessFiles: |
|
462 if (DoStateProcessFilesL()) |
|
463 { |
|
464 SwitchState(EVerifyPaths); |
|
465 } |
|
466 break; |
|
467 |
|
468 case EVerifyPaths: |
|
469 if (DoStateVerifyPathsL()) |
|
470 { |
|
471 SwitchState(EInstallFiles); |
|
472 } |
|
473 break; |
|
474 |
|
475 case EInstallFiles: |
|
476 if (DoStateInstallFilesL()) |
|
477 { |
|
478 SwitchState(EUpdateRegistry); |
|
479 } |
|
480 break; |
|
481 |
|
482 case EUpdateRegistry: |
|
483 if (DoStateUpdateRegistryL()) |
|
484 { |
|
485 SwitchState(EFinished); |
|
486 } |
|
487 break; |
|
488 |
|
489 case EFinished: |
|
490 DoStateFinishedL(); |
|
491 break; |
|
492 |
|
493 default: |
|
494 User::Leave(KErrGeneral); |
|
495 break; |
|
496 } |
|
497 } |
|
498 |
|
499 void CRestoreProcessor::SwitchState(TProcessingState aNextState) |
|
500 { |
|
501 if (aNextState!=ECurrentState) |
|
502 { |
|
503 iState=aNextState; |
|
504 } |
|
505 TRequestStatus* status = &iStatus; |
|
506 User::RequestComplete(status, KErrNone); |
|
507 SetActive(); |
|
508 } |
|
509 |
|
510 |
|
511 void CRestoreProcessor::DoStateFinishedL() |
|
512 { |
|
513 User::RequestComplete(iClientStatus, KErrNone); |
|
514 } |
|
515 |
|
516 TInt CRestoreProcessor::RunError(TInt aError) |
|
517 { |
|
518 |
|
519 User::RequestComplete(iClientStatus, aError); |
|
520 return KErrNone; |
|
521 |
|
522 } |
|
523 |
|
524 void CRestoreProcessor::ProcessApplicationL(const CApplication& aApplication, TRequestStatus& aClientStatus) |
|
525 { |
|
526 iApplication = &aApplication; |
|
527 iClientStatus = &aClientStatus; |
|
528 aClientStatus = KRequestPending; |
|
529 |
|
530 iState = EInitialize; |
|
531 |
|
532 TRequestStatus* status = &iStatus; |
|
533 User::RequestComplete(status, KErrNone); |
|
534 SetActive(); |
|
535 } |
|
536 |
|
537 void CRestoreProcessor::ProcessPlanL(TRequestStatus& aClientStatus) |
|
538 { |
|
539 ProcessApplicationL(iPlan.ApplicationL(), aClientStatus); |
|
540 } |
|
541 |
|
542 void CRestoreProcessor::DoCancel() |
|
543 { |
|
544 } |
|
545 |
|
546 CRestoreProcessor::~CRestoreProcessor() |
|
547 { |
|
548 delete iFileMan; |
|
549 iFs.Close(); |
|
550 iSids.Close(); |
|
551 |
|
552 #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK |
|
553 iSoftwareTypeRegInfoArray.Close(); |
|
554 #endif |
|
555 } |