1 /*
2 * Copyright 2013-2014, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Ingo Weinhold <ingo_weinhold@gmx.de>
7 */
8
9
10 #include "CommitTransactionHandler.h"
11
12 #include <errno.h>
13 #include <grp.h>
14 #include <pwd.h>
15
16 #include <File.h>
17 #include <Path.h>
18 #include <SymLink.h>
19
20 #include <AutoDeleter.h>
21 #include <CopyEngine.h>
22 #include <NotOwningEntryRef.h>
23 #include <package/CommitTransactionResult.h>
24 #include <package/DaemonDefs.h>
25 #include <RemoveEngine.h>
26
27 #include "Constants.h"
28 #include "DebugSupport.h"
29 #include "Exception.h"
30 #include "PackageFileManager.h"
31 #include "VolumeState.h"
32
33
34 using namespace BPackageKit::BPrivate;
35
36 using BPackageKit::BTransactionIssue;
37
38
39 // #pragma mark - TransactionIssueBuilder
40
41
42 struct CommitTransactionHandler::TransactionIssueBuilder {
TransactionIssueBuilderCommitTransactionHandler::TransactionIssueBuilder43 TransactionIssueBuilder(BTransactionIssue::BType type,
44 Package* package = NULL)
45 :
46 fType(type),
47 fPackageName(package != NULL ? package->FileName() : BString()),
48 fPath1(),
49 fPath2(),
50 fSystemError(B_OK),
51 fExitCode(0)
52 {
53 }
54
SetPath1CommitTransactionHandler::TransactionIssueBuilder55 TransactionIssueBuilder& SetPath1(const BString& path)
56 {
57 fPath1 = path;
58 return *this;
59 }
60
SetPath1CommitTransactionHandler::TransactionIssueBuilder61 TransactionIssueBuilder& SetPath1(const FSUtils::Entry& entry)
62 {
63 return SetPath1(entry.Path());
64 }
65
SetPath2CommitTransactionHandler::TransactionIssueBuilder66 TransactionIssueBuilder& SetPath2(const BString& path)
67 {
68 fPath2 = path;
69 return *this;
70 }
71
SetPath2CommitTransactionHandler::TransactionIssueBuilder72 TransactionIssueBuilder& SetPath2(const FSUtils::Entry& entry)
73 {
74 return SetPath2(entry.Path());
75 }
76
SetSystemErrorCommitTransactionHandler::TransactionIssueBuilder77 TransactionIssueBuilder& SetSystemError(status_t error)
78 {
79 fSystemError = error;
80 return *this;
81 }
82
SetExitCodeCommitTransactionHandler::TransactionIssueBuilder83 TransactionIssueBuilder& SetExitCode(int exitCode)
84 {
85 fExitCode = exitCode;
86 return *this;
87 }
88
BuildIssueCommitTransactionHandler::TransactionIssueBuilder89 BTransactionIssue BuildIssue(Package* package) const
90 {
91 BString packageName(fPackageName);
92 if (packageName.IsEmpty() && package != NULL)
93 packageName = package->FileName();
94
95 return BTransactionIssue(fType, packageName, fPath1, fPath2,
96 fSystemError, fExitCode);
97 }
98
99 private:
100 BTransactionIssue::BType fType;
101 BString fPackageName;
102 BString fPath1;
103 BString fPath2;
104 status_t fSystemError;
105 int fExitCode;
106 };
107
108
109 // #pragma mark - CommitTransactionHandler
110
111
CommitTransactionHandler(Volume * volume,PackageFileManager * packageFileManager,BCommitTransactionResult & result)112 CommitTransactionHandler::CommitTransactionHandler(Volume* volume,
113 PackageFileManager* packageFileManager, BCommitTransactionResult& result)
114 :
115 fVolume(volume),
116 fPackageFileManager(packageFileManager),
117 fVolumeState(NULL),
118 fVolumeStateIsActive(false),
119 fPackagesToActivate(),
120 fPackagesToDeactivate(),
121 fAddedPackages(),
122 fRemovedPackages(),
123 fPackagesAlreadyAdded(),
124 fPackagesAlreadyRemoved(),
125 fOldStateDirectory(),
126 fOldStateDirectoryRef(),
127 fOldStateDirectoryName(),
128 fTransactionDirectoryRef(),
129 fFirstBootProcessing(false),
130 fWritableFilesDirectory(),
131 fAddedGroups(),
132 fAddedUsers(),
133 fFSTransaction(),
134 fResult(result),
135 fCurrentPackage(NULL)
136 {
137 }
138
139
~CommitTransactionHandler()140 CommitTransactionHandler::~CommitTransactionHandler()
141 {
142 // Delete Package objects we created in case of error (on success
143 // fPackagesToActivate will be empty).
144 int32 count = fPackagesToActivate.CountItems();
145 for (int32 i = 0; i < count; i++) {
146 Package* package = fPackagesToActivate.ItemAt(i);
147 if (fPackagesAlreadyAdded.find(package)
148 == fPackagesAlreadyAdded.end()) {
149 delete package;
150 }
151 }
152
153 delete fVolumeState;
154 }
155
156
157 void
Init(VolumeState * volumeState,bool isActiveVolumeState,const PackageSet & packagesAlreadyAdded,const PackageSet & packagesAlreadyRemoved)158 CommitTransactionHandler::Init(VolumeState* volumeState,
159 bool isActiveVolumeState, const PackageSet& packagesAlreadyAdded,
160 const PackageSet& packagesAlreadyRemoved)
161 {
162 fVolumeState = volumeState->Clone();
163 if (fVolumeState == NULL)
164 throw std::bad_alloc();
165
166 fVolumeStateIsActive = isActiveVolumeState;
167
168 for (PackageSet::const_iterator it = packagesAlreadyAdded.begin();
169 it != packagesAlreadyAdded.end(); ++it) {
170 Package* package = fVolumeState->FindPackage((*it)->FileName());
171 fPackagesAlreadyAdded.insert(package);
172 }
173
174 for (PackageSet::const_iterator it = packagesAlreadyRemoved.begin();
175 it != packagesAlreadyRemoved.end(); ++it) {
176 Package* package = fVolumeState->FindPackage((*it)->FileName());
177 fPackagesAlreadyRemoved.insert(package);
178 }
179 }
180
181
182 void
HandleRequest(BMessage * request)183 CommitTransactionHandler::HandleRequest(BMessage* request)
184 {
185 status_t error;
186
187 BActivationTransaction transaction(request, &error);
188 if (error == B_OK)
189 error = transaction.InitCheck();
190 if (error != B_OK) {
191 if (error == B_NO_MEMORY)
192 throw Exception(B_TRANSACTION_NO_MEMORY);
193 throw Exception(B_TRANSACTION_BAD_REQUEST);
194 }
195
196 HandleRequest(transaction);
197 }
198
199
200 void
HandleRequest(const BActivationTransaction & transaction)201 CommitTransactionHandler::HandleRequest(
202 const BActivationTransaction& transaction)
203 {
204 // check the change count
205 if (transaction.ChangeCount() != fVolume->ChangeCount())
206 throw Exception(B_TRANSACTION_CHANGE_COUNT_MISMATCH);
207
208 fFirstBootProcessing = transaction.FirstBootProcessing();
209
210 // collect the packages to deactivate
211 _GetPackagesToDeactivate(transaction);
212
213 // read the packages to activate
214 _ReadPackagesToActivate(transaction);
215
216 // anything to do at all?
217 if (fPackagesToActivate.IsEmpty() && fPackagesToDeactivate.empty()) {
218 WARN("Bad package activation request: no packages to activate or"
219 " deactivate\n");
220 throw Exception(B_TRANSACTION_BAD_REQUEST);
221 }
222
223 _ApplyChanges();
224
225 // Clean up the unused empty transaction directory for first boot
226 // processing, since it's usually an internal to package_daemon
227 // operation and there is no external client to clean it up.
228 if (fFirstBootProcessing) {
229 RelativePath directoryPath(kAdminDirectoryName,
230 transaction.TransactionDirectoryName().String());
231 BDirectory transactionDir;
232 status_t error = _OpenPackagesSubDirectory(directoryPath, false,
233 transactionDir);
234 if (error == B_OK) {
235 BEntry transactionDirEntry;
236 error = transactionDir.GetEntry(&transactionDirEntry);
237 if (error == B_OK)
238 transactionDirEntry.Remove(); // Okay to fail when non-empty.
239 }
240 }
241 }
242
243
244 void
HandleRequest()245 CommitTransactionHandler::HandleRequest()
246 {
247 for (PackageSet::const_iterator it = fPackagesAlreadyAdded.begin();
248 it != fPackagesAlreadyAdded.end(); ++it) {
249 if (!fPackagesToActivate.AddItem(*it))
250 throw std::bad_alloc();
251 }
252
253 fPackagesToDeactivate = fPackagesAlreadyRemoved;
254
255 _ApplyChanges();
256 }
257
258
259 void
Revert()260 CommitTransactionHandler::Revert()
261 {
262 // move packages to activate back to transaction directory
263 _RevertAddPackagesToActivate();
264
265 // move packages to deactivate back to packages directory
266 _RevertRemovePackagesToDeactivate();
267
268 // revert user and group changes
269 _RevertUserGroupChanges();
270
271 // Revert all other FS operations, i.e. the writable files changes as
272 // well as the creation of the old state directory.
273 fFSTransaction.RollBack();
274 }
275
276
277 VolumeState*
DetachVolumeState()278 CommitTransactionHandler::DetachVolumeState()
279 {
280 VolumeState* result = fVolumeState;
281 fVolumeState = NULL;
282 return result;
283 }
284
285
286 void
_GetPackagesToDeactivate(const BActivationTransaction & transaction)287 CommitTransactionHandler::_GetPackagesToDeactivate(
288 const BActivationTransaction& transaction)
289 {
290 // get the number of packages to deactivate
291 const BStringList& packagesToDeactivate
292 = transaction.PackagesToDeactivate();
293 int32 packagesToDeactivateCount = packagesToDeactivate.CountStrings();
294 if (packagesToDeactivateCount == 0)
295 return;
296
297 for (int32 i = 0; i < packagesToDeactivateCount; i++) {
298 BString packageName = packagesToDeactivate.StringAt(i);
299 Package* package = fVolumeState->FindPackage(packageName);
300 if (package == NULL) {
301 throw Exception(B_TRANSACTION_NO_SUCH_PACKAGE)
302 .SetPackageName(packageName);
303 }
304
305 fPackagesToDeactivate.insert(package);
306 }
307 }
308
309
310 void
_ReadPackagesToActivate(const BActivationTransaction & transaction)311 CommitTransactionHandler::_ReadPackagesToActivate(
312 const BActivationTransaction& transaction)
313 {
314 // get the number of packages to activate
315 const BStringList& packagesToActivate
316 = transaction.PackagesToActivate();
317 int32 packagesToActivateCount = packagesToActivate.CountStrings();
318 if (packagesToActivateCount == 0)
319 return;
320
321 // check the transaction directory name -- we only allow a simple
322 // subdirectory of the admin directory
323 const BString& transactionDirectoryName
324 = transaction.TransactionDirectoryName();
325 if (transactionDirectoryName.IsEmpty()
326 || transactionDirectoryName.FindFirst('/') >= 0
327 || transactionDirectoryName == "."
328 || transactionDirectoryName == "..") {
329 WARN("Bad package activation request: malformed transaction"
330 " directory name: \"%s\"\n", transactionDirectoryName.String());
331 throw Exception(B_TRANSACTION_BAD_REQUEST);
332 }
333
334 // open the directory
335 RelativePath directoryPath(kAdminDirectoryName,
336 transactionDirectoryName);
337 BDirectory directory;
338 status_t error = _OpenPackagesSubDirectory(directoryPath, false, directory);
339 if (error == B_OK) {
340 error = directory.GetNodeRef(&fTransactionDirectoryRef);
341 if (error != B_OK) {
342 ERROR("Failed to get transaction directory node ref: %s\n",
343 strerror(error));
344 }
345 } else
346 ERROR("Failed to open transaction directory: %s\n", strerror(error));
347
348 if (error != B_OK) {
349 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY)
350 .SetPath1(_GetPath(
351 FSUtils::Entry(fVolume->PackagesDirectoryRef(),
352 directoryPath.ToString()),
353 directoryPath.ToString()))
354 .SetSystemError(error);
355 }
356
357 // read the packages
358 for (int32 i = 0; i < packagesToActivateCount; i++) {
359 BString packageName = packagesToActivate.StringAt(i);
360 // make sure it doesn't clash with an already existing package,
361 // except in first boot mode where it should always clash.
362 Package* package = fVolumeState->FindPackage(packageName);
363 if (fFirstBootProcessing) {
364 if (package == NULL) {
365 throw Exception(B_TRANSACTION_NO_SUCH_PACKAGE)
366 .SetPackageName(packageName);
367 }
368 if (!fPackagesToActivate.AddItem(package))
369 throw Exception(B_TRANSACTION_NO_MEMORY);
370 continue;
371 } else {
372 if (package != NULL) {
373 if (fPackagesAlreadyAdded.find(package)
374 != fPackagesAlreadyAdded.end()) {
375 if (!fPackagesToActivate.AddItem(package))
376 throw Exception(B_TRANSACTION_NO_MEMORY);
377 continue;
378 }
379
380 if (fPackagesToDeactivate.find(package)
381 == fPackagesToDeactivate.end()) {
382 throw Exception(B_TRANSACTION_PACKAGE_ALREADY_EXISTS)
383 .SetPackageName(packageName);
384 }
385 }
386 }
387
388 // read the package
389 error = fPackageFileManager->CreatePackage(
390 NotOwningEntryRef(fTransactionDirectoryRef, packageName),
391 package);
392 if (error != B_OK) {
393 if (error == B_NO_MEMORY)
394 throw Exception(B_TRANSACTION_NO_MEMORY);
395 throw Exception(B_TRANSACTION_FAILED_TO_READ_PACKAGE_FILE)
396 .SetPackageName(packageName)
397 .SetPath1(_GetPath(
398 FSUtils::Entry(
399 NotOwningEntryRef(fTransactionDirectoryRef,
400 packageName)),
401 packageName))
402 .SetSystemError(error);
403 }
404
405 if (!fPackagesToActivate.AddItem(package)) {
406 delete package;
407 throw Exception(B_TRANSACTION_NO_MEMORY);
408 }
409 }
410 }
411
412
413 void
_ApplyChanges()414 CommitTransactionHandler::_ApplyChanges()
415 {
416 if (!fFirstBootProcessing)
417 {
418 // create an old state directory
419 _CreateOldStateDirectory();
420
421 // move packages to deactivate to old state directory
422 _RemovePackagesToDeactivate();
423
424 // move packages to activate to packages directory
425 _AddPackagesToActivate();
426
427 // run pre-uninstall scripts, before their packages vanish.
428 _RunPreUninstallScripts();
429
430 // activate/deactivate packages and create users, groups, settings files.
431 _ChangePackageActivation(fAddedPackages, fRemovedPackages);
432 } else // FirstBootProcessing, skip several steps and just do package setup.
433 _PrepareFirstBootPackages();
434
435 // run post-install scripts now that the new packages are visible in the
436 // package file system.
437 if (fVolumeStateIsActive || fFirstBootProcessing) {
438 _RunPostInstallScripts();
439 } else {
440 // Do post-install scripts later after a reboot, for Haiku OS packages.
441 _QueuePostInstallScripts();
442 }
443
444 // removed packages have been deleted, new packages shall not be deleted
445 fAddedPackages.clear();
446 fRemovedPackages.clear();
447 fPackagesToActivate.MakeEmpty(false);
448 fPackagesToDeactivate.clear();
449 }
450
451
452 void
_CreateOldStateDirectory()453 CommitTransactionHandler::_CreateOldStateDirectory()
454 {
455 time_t stateTime = 0;
456 {
457 // use the modification time of the old activations file, if possible
458 BFile oldActivationFile;
459 BEntry oldActivationEntry;
460 if (_OpenPackagesFile(RelativePath(kAdminDirectoryName), kActivationFileName,
461 B_READ_ONLY, oldActivationFile, &oldActivationEntry) != B_OK
462 || oldActivationEntry.GetModificationTime(&stateTime) != B_OK) {
463 stateTime = time(NULL);
464 }
465 }
466
467 // construct a nice name from the date and time
468 struct tm now;
469 BString baseName;
470 if (localtime_r(&stateTime, &now) != NULL) {
471 baseName.SetToFormat("state_%d-%02d-%02d_%02d:%02d:%02d",
472 1900 + now.tm_year, now.tm_mon + 1, now.tm_mday, now.tm_hour,
473 now.tm_min, now.tm_sec);
474 } else
475 baseName = "state";
476
477 if (baseName.IsEmpty())
478 throw Exception(B_TRANSACTION_NO_MEMORY);
479
480 // make sure the directory doesn't exist yet
481 BDirectory adminDirectory;
482 status_t error = _OpenPackagesSubDirectory(
483 RelativePath(kAdminDirectoryName), true, adminDirectory);
484 if (error != B_OK) {
485 ERROR("Failed to open administrative directory: %s\n", strerror(error));
486 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY)
487 .SetPath1(_GetPath(
488 FSUtils::Entry(fVolume->PackagesDirectoryRef(),
489 kAdminDirectoryName),
490 kAdminDirectoryName))
491 .SetSystemError(error);
492 }
493
494 int uniqueId = 1;
495 BString directoryName = baseName;
496 while (BEntry(&adminDirectory, directoryName).Exists()) {
497 directoryName.SetToFormat("%s-%d", baseName.String(), uniqueId++);
498 if (directoryName.IsEmpty())
499 throw Exception(B_TRANSACTION_NO_MEMORY);
500 }
501
502 // create the directory
503 FSTransaction::CreateOperation createOldStateDirectoryOperation(
504 &fFSTransaction, FSUtils::Entry(adminDirectory, directoryName));
505
506 error = adminDirectory.CreateDirectory(directoryName,
507 &fOldStateDirectory);
508 if (error == B_OK) {
509 createOldStateDirectoryOperation.Finished();
510
511 fOldStateDirectoryName = directoryName;
512
513 error = fOldStateDirectory.GetNodeRef(&fOldStateDirectoryRef);
514 if (error != B_OK)
515 ERROR("Failed get old state directory ref: %s\n", strerror(error));
516 } else
517 ERROR("Failed to create old state directory: %s\n", strerror(error));
518
519 if (error != B_OK) {
520 throw Exception(B_TRANSACTION_FAILED_TO_CREATE_DIRECTORY)
521 .SetPath1(_GetPath(
522 FSUtils::Entry(adminDirectory, directoryName),
523 directoryName))
524 .SetSystemError(error);
525 }
526
527 // write the old activation file
528 BEntry activationFile;
529 _WriteActivationFile(RelativePath(kAdminDirectoryName, directoryName),
530 kActivationFileName, PackageSet(), PackageSet(), activationFile);
531
532 fResult.SetOldStateDirectory(fOldStateDirectoryName);
533 }
534
535
536 void
_RemovePackagesToDeactivate()537 CommitTransactionHandler::_RemovePackagesToDeactivate()
538 {
539 if (fPackagesToDeactivate.empty())
540 return;
541
542 for (PackageSet::const_iterator it = fPackagesToDeactivate.begin();
543 it != fPackagesToDeactivate.end(); ++it) {
544 Package* package = *it;
545
546 // When deactivating (or updating) a system package, don't do that live.
547 if (_IsSystemPackage(package))
548 fVolumeStateIsActive = false;
549
550 if (fPackagesAlreadyRemoved.find(package)
551 != fPackagesAlreadyRemoved.end()) {
552 fRemovedPackages.insert(package);
553 continue;
554 }
555
556 // get a BEntry for the package
557 NotOwningEntryRef entryRef(package->EntryRef());
558
559 BEntry entry;
560 status_t error = entry.SetTo(&entryRef);
561 if (error != B_OK) {
562 ERROR("Failed to get package entry for %s: %s\n",
563 package->FileName().String(), strerror(error));
564 throw Exception(B_TRANSACTION_FAILED_TO_GET_ENTRY_PATH)
565 .SetPath1(package->FileName())
566 .SetPackageName(package->FileName())
567 .SetSystemError(error);
568 }
569
570 // move entry
571 fRemovedPackages.insert(package);
572
573 error = entry.MoveTo(&fOldStateDirectory);
574 if (error != B_OK) {
575 fRemovedPackages.erase(package);
576 ERROR("Failed to move old package %s from packages directory: %s\n",
577 package->FileName().String(), strerror(error));
578 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE)
579 .SetPath1(
580 _GetPath(FSUtils::Entry(entryRef), package->FileName()))
581 .SetPath2(_GetPath(
582 FSUtils::Entry(fOldStateDirectory),
583 fOldStateDirectoryName))
584 .SetSystemError(error);
585 }
586
587 fPackageFileManager->PackageFileMoved(package->File(),
588 fOldStateDirectoryRef);
589 package->File()->IncrementEntryRemovedIgnoreLevel();
590 }
591 }
592
593
594 void
_AddPackagesToActivate()595 CommitTransactionHandler::_AddPackagesToActivate()
596 {
597 if (fPackagesToActivate.IsEmpty())
598 return;
599
600 // open packages directory
601 BDirectory packagesDirectory;
602 status_t error
603 = packagesDirectory.SetTo(&fVolume->PackagesDirectoryRef());
604 if (error != B_OK) {
605 ERROR("Failed to open packages directory: %s\n", strerror(error));
606 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY)
607 .SetPath1("<packages>")
608 .SetSystemError(error);
609 }
610
611 int32 count = fPackagesToActivate.CountItems();
612 for (int32 i = 0; i < count; i++) {
613 Package* package = fPackagesToActivate.ItemAt(i);
614 if (fPackagesAlreadyAdded.find(package)
615 != fPackagesAlreadyAdded.end()) {
616 fAddedPackages.insert(package);
617 _PreparePackageToActivate(package);
618 continue;
619 }
620
621 // get a BEntry for the package
622 NotOwningEntryRef entryRef(fTransactionDirectoryRef,
623 package->FileName());
624 BEntry entry;
625 error = entry.SetTo(&entryRef);
626 if (error != B_OK) {
627 ERROR("Failed to get package entry for %s: %s\n",
628 package->FileName().String(), strerror(error));
629 throw Exception(B_TRANSACTION_FAILED_TO_GET_ENTRY_PATH)
630 .SetPath1(package->FileName())
631 .SetPackageName(package->FileName())
632 .SetSystemError(error);
633 }
634
635 // move entry
636 fAddedPackages.insert(package);
637
638 error = entry.MoveTo(&packagesDirectory);
639 if (error == B_FILE_EXISTS) {
640 error = _AssertEntriesAreEqual(entry, &packagesDirectory);
641 if (error == B_OK) {
642 // Packages are identical, no need to move.
643 // If the entry is not removed however, it will prevent
644 // the transaction directory from being removed later.
645 // We ignore failure to Remove() here, though.
646 entry.Remove();
647 } else if (error != B_FILE_EXISTS) {
648 ERROR("Failed to compare new package %s to existing file in "
649 "packages directory: %s\n", package->FileName().String(),
650 strerror(error));
651 // Restore original error to avoid confusion
652 error = B_FILE_EXISTS;
653 }
654 }
655 if (error != B_OK) {
656 fAddedPackages.erase(package);
657 ERROR("Failed to move new package %s to packages directory: %s\n",
658 package->FileName().String(), strerror(error));
659 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE)
660 .SetPath1(
661 _GetPath(FSUtils::Entry(entryRef), package->FileName()))
662 .SetPath2(_GetPath(
663 FSUtils::Entry(packagesDirectory),
664 "packages"))
665 .SetSystemError(error);
666 }
667
668 fPackageFileManager->PackageFileMoved(package->File(),
669 fVolume->PackagesDirectoryRef());
670 package->File()->IncrementEntryCreatedIgnoreLevel();
671
672 // also add the package to the volume
673 fVolumeState->AddPackage(package);
674
675 _PreparePackageToActivate(package);
676 }
677 }
678
679
680 void
_PrepareFirstBootPackages()681 CommitTransactionHandler::_PrepareFirstBootPackages()
682 {
683 int32 count = fPackagesToActivate.CountItems();
684
685 BDirectory transactionDir(&fTransactionDirectoryRef);
686 BEntry transactionEntry;
687 BPath transactionPath;
688 if (transactionDir.InitCheck() == B_OK &&
689 transactionDir.GetEntry(&transactionEntry) == B_OK &&
690 transactionEntry.GetPath(&transactionPath) == B_OK) {
691 INFORM("Starting First Boot Processing for %d packages in %s.\n",
692 (int) count, transactionPath.Path());
693 }
694
695 for (int32 i = 0; i < count; i++) {
696 Package* package = fPackagesToActivate.ItemAt(i);
697 fAddedPackages.insert(package);
698 INFORM("Doing first boot processing #%d for package %s.\n",
699 (int) i, package->FileName().String());
700 _PreparePackageToActivate(package);
701 }
702 }
703
704
705 void
_PreparePackageToActivate(Package * package)706 CommitTransactionHandler::_PreparePackageToActivate(Package* package)
707 {
708 fCurrentPackage = package;
709
710 // add groups
711 const BStringList& groups = package->Info().Groups();
712 int32 count = groups.CountStrings();
713 for (int32 i = 0; i < count; i++)
714 _AddGroup(package, groups.StringAt(i));
715
716 // add users
717 const BObjectList<BUser>& users = package->Info().Users();
718 for (int32 i = 0; const BUser* user = users.ItemAt(i); i++)
719 _AddUser(package, *user);
720
721 // handle global writable files
722 _AddGlobalWritableFiles(package);
723
724 fCurrentPackage = NULL;
725 }
726
727
728 void
_AddGroup(Package * package,const BString & groupName)729 CommitTransactionHandler::_AddGroup(Package* package, const BString& groupName)
730 {
731 // Check whether the group already exists.
732 char buffer[256];
733 struct group groupBuffer;
734 struct group* groupFound;
735 int error = getgrnam_r(groupName, &groupBuffer, buffer, sizeof(buffer),
736 &groupFound);
737 if ((error == 0 && groupFound != NULL) || error == ERANGE)
738 return;
739
740 // add it
741 fAddedGroups.insert(groupName.String());
742
743 std::string commandLine("groupadd ");
744 commandLine += FSUtils::ShellEscapeString(groupName).String();
745
746 if (system(commandLine.c_str()) != 0) {
747 fAddedGroups.erase(groupName.String());
748 ERROR("Failed to add group \"%s\".\n", groupName.String());
749 throw Exception(B_TRANSACTION_FAILED_TO_ADD_GROUP)
750 .SetPackageName(package->FileName())
751 .SetString1(groupName);
752 }
753 }
754
755
756 void
_AddUser(Package * package,const BUser & user)757 CommitTransactionHandler::_AddUser(Package* package, const BUser& user)
758 {
759 // Check whether the user already exists.
760 char buffer[256];
761 struct passwd passwdBuffer;
762 struct passwd* passwdFound;
763 int error = getpwnam_r(user.Name(), &passwdBuffer, buffer,
764 sizeof(buffer), &passwdFound);
765 if ((error == 0 && passwdFound != NULL) || error == ERANGE)
766 return;
767
768 // add it
769 fAddedUsers.insert(user.Name().String());
770
771 std::string commandLine("useradd ");
772
773 if (!user.RealName().IsEmpty()) {
774 commandLine += std::string("-n ")
775 + FSUtils::ShellEscapeString(user.RealName()).String() + " ";
776 }
777
778 if (!user.Home().IsEmpty()) {
779 commandLine += std::string("-d ")
780 + FSUtils::ShellEscapeString(user.Home()).String() + " ";
781 }
782
783 if (!user.Shell().IsEmpty()) {
784 commandLine += std::string("-s ")
785 + FSUtils::ShellEscapeString(user.Shell()).String() + " ";
786 }
787
788 if (!user.Groups().IsEmpty()) {
789 commandLine += std::string("-g ")
790 + FSUtils::ShellEscapeString(user.Groups().First()).String()
791 + " ";
792 }
793
794 commandLine += FSUtils::ShellEscapeString(user.Name()).String();
795
796 if (system(commandLine.c_str()) != 0) {
797 fAddedUsers.erase(user.Name().String());
798 ERROR("Failed to add user \"%s\".\n", user.Name().String());
799 throw Exception(B_TRANSACTION_FAILED_TO_ADD_USER)
800 .SetPackageName(package->FileName())
801 .SetString1(user.Name());
802
803 }
804
805 // add the supplementary groups
806 int32 groupCount = user.Groups().CountStrings();
807 for (int32 i = 1; i < groupCount; i++) {
808 commandLine = std::string("groupmod -A ")
809 + FSUtils::ShellEscapeString(user.Name()).String()
810 + " "
811 + FSUtils::ShellEscapeString(user.Groups().StringAt(i))
812 .String();
813 if (system(commandLine.c_str()) != 0) {
814 fAddedUsers.erase(user.Name().String());
815 ERROR("Failed to add user \"%s\" to group \"%s\".\n",
816 user.Name().String(), user.Groups().StringAt(i).String());
817 throw Exception(B_TRANSACTION_FAILED_TO_ADD_USER_TO_GROUP)
818 .SetPackageName(package->FileName())
819 .SetString1(user.Name())
820 .SetString2(user.Groups().StringAt(i));
821 }
822 }
823 }
824
825
826 void
_AddGlobalWritableFiles(Package * package)827 CommitTransactionHandler::_AddGlobalWritableFiles(Package* package)
828 {
829 // get the list of included files
830 const BObjectList<BGlobalWritableFileInfo>& files
831 = package->Info().GlobalWritableFileInfos();
832 BStringList contentPaths;
833 for (int32 i = 0; const BGlobalWritableFileInfo* file = files.ItemAt(i);
834 i++) {
835 if (file->IsIncluded() && !contentPaths.Add(file->Path()))
836 throw std::bad_alloc();
837 }
838
839 if (contentPaths.IsEmpty())
840 return;
841
842 // Open the root directory of the installation location where we will
843 // extract the files -- that's the volume's root directory.
844 BDirectory rootDirectory;
845 status_t error = rootDirectory.SetTo(&fVolume->RootDirectoryRef());
846 if (error != B_OK) {
847 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY)
848 .SetPath1(_GetPath(
849 FSUtils::Entry(fVolume->RootDirectoryRef()),
850 "<packagefs root>"))
851 .SetSystemError(error);
852 }
853
854 // Open writable-files directory in the administrative directory.
855 if (fWritableFilesDirectory.InitCheck() != B_OK) {
856 RelativePath directoryPath(kAdminDirectoryName,
857 kWritableFilesDirectoryName);
858 error = _OpenPackagesSubDirectory(directoryPath, true,
859 fWritableFilesDirectory);
860
861 if (error != B_OK) {
862 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY)
863 .SetPath1(_GetPath(
864 FSUtils::Entry(fVolume->PackagesDirectoryRef(),
865 directoryPath.ToString()),
866 directoryPath.ToString()))
867 .SetPackageName(package->FileName())
868 .SetSystemError(error);
869 }
870 }
871
872 // extract files into a subdir of the writable-files directory
873 BDirectory extractedFilesDirectory;
874 _ExtractPackageContent(package, contentPaths,
875 fWritableFilesDirectory, extractedFilesDirectory);
876
877 for (int32 i = 0; const BGlobalWritableFileInfo* file = files.ItemAt(i);
878 i++) {
879 if (file->IsIncluded()) {
880 _AddGlobalWritableFile(package, *file, rootDirectory,
881 extractedFilesDirectory);
882 }
883 }
884 }
885
886
887 void
_AddGlobalWritableFile(Package * package,const BGlobalWritableFileInfo & file,const BDirectory & rootDirectory,const BDirectory & extractedFilesDirectory)888 CommitTransactionHandler::_AddGlobalWritableFile(Package* package,
889 const BGlobalWritableFileInfo& file, const BDirectory& rootDirectory,
890 const BDirectory& extractedFilesDirectory)
891 {
892 // open parent directory of the source entry
893 const char* lastSlash = strrchr(file.Path(), '/');
894 const BDirectory* sourceDirectory;
895 BDirectory stackSourceDirectory;
896 if (lastSlash != NULL) {
897 sourceDirectory = &stackSourceDirectory;
898 BString sourceParentPath(file.Path(),
899 lastSlash - file.Path().String());
900 if (sourceParentPath.Length() == 0)
901 throw std::bad_alloc();
902
903 status_t error = stackSourceDirectory.SetTo(
904 &extractedFilesDirectory, sourceParentPath);
905 if (error != B_OK) {
906 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY)
907 .SetPath1(_GetPath(
908 FSUtils::Entry(extractedFilesDirectory, sourceParentPath),
909 sourceParentPath))
910 .SetPackageName(package->FileName())
911 .SetSystemError(error);
912 }
913 } else {
914 sourceDirectory = &extractedFilesDirectory;
915 }
916
917 // open parent directory of the target entry -- create, if necessary
918 BString targetPath(file.Path());
919 FSUtils::Path relativeSourcePath(file.Path());
920 lastSlash = strrchr(targetPath, '/');
921 if (lastSlash != NULL) {
922 BString targetParentPath(targetPath,
923 lastSlash - targetPath.String());
924 if (targetParentPath.Length() == 0)
925 throw std::bad_alloc();
926
927 BDirectory targetDirectory;
928 status_t error = FSUtils::OpenSubDirectory(rootDirectory,
929 RelativePath(targetParentPath), true, targetDirectory);
930 if (error != B_OK) {
931 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY)
932 .SetPath1(_GetPath(
933 FSUtils::Entry(rootDirectory, targetParentPath),
934 targetParentPath))
935 .SetPackageName(package->FileName())
936 .SetSystemError(error);
937 }
938 _AddGlobalWritableFileRecurse(package, *sourceDirectory,
939 relativeSourcePath, targetDirectory, lastSlash + 1,
940 file.UpdateType());
941 } else {
942 _AddGlobalWritableFileRecurse(package, *sourceDirectory,
943 relativeSourcePath, rootDirectory, targetPath,
944 file.UpdateType());
945 }
946 }
947
948
949 void
_AddGlobalWritableFileRecurse(Package * package,const BDirectory & sourceDirectory,FSUtils::Path & relativeSourcePath,const BDirectory & targetDirectory,const char * targetName,BWritableFileUpdateType updateType)950 CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package,
951 const BDirectory& sourceDirectory, FSUtils::Path& relativeSourcePath,
952 const BDirectory& targetDirectory, const char* targetName,
953 BWritableFileUpdateType updateType)
954 {
955 // * If the file doesn't exist, just copy the extracted one.
956 // * If the file does exist, compare with the previous original version:
957 // * If unchanged, just overwrite it.
958 // * If changed, leave it to the user for now. When we support merging
959 // first back the file up, then try the merge.
960
961 // Check whether the target location exists and what type the entry at
962 // both locations are.
963 struct stat targetStat;
964 if (targetDirectory.GetStatFor(targetName, &targetStat) != B_OK) {
965 // target doesn't exist -- just copy
966 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
967 "couldn't get stat for writable file \"%s\", copying...\n",
968 targetName);
969 FSTransaction::CreateOperation copyOperation(&fFSTransaction,
970 FSUtils::Entry(targetDirectory, targetName));
971 status_t error = BCopyEngine(BCopyEngine::COPY_RECURSIVELY)
972 .CopyEntry(
973 FSUtils::Entry(sourceDirectory, relativeSourcePath.Leaf()),
974 FSUtils::Entry(targetDirectory, targetName));
975 if (error != B_OK) {
976 if (targetDirectory.GetStatFor(targetName, &targetStat) == B_OK)
977 copyOperation.Finished();
978
979 throw Exception(B_TRANSACTION_FAILED_TO_COPY_FILE)
980 .SetPath1(_GetPath(
981 FSUtils::Entry(sourceDirectory,
982 relativeSourcePath.Leaf()),
983 relativeSourcePath))
984 .SetPath2(_GetPath(
985 FSUtils::Entry(targetDirectory, targetName),
986 targetName))
987 .SetSystemError(error);
988 }
989 copyOperation.Finished();
990 return;
991 }
992
993 struct stat sourceStat;
994 status_t error = sourceDirectory.GetStatFor(relativeSourcePath.Leaf(),
995 &sourceStat);
996 if (error != B_OK) {
997 throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY)
998 .SetPath1(_GetPath(
999 FSUtils::Entry(sourceDirectory,
1000 relativeSourcePath.Leaf()),
1001 relativeSourcePath))
1002 .SetSystemError(error);
1003 }
1004
1005 if ((sourceStat.st_mode & S_IFMT) != (targetStat.st_mode & S_IFMT)
1006 || (!S_ISDIR(sourceStat.st_mode) && !S_ISREG(sourceStat.st_mode)
1007 && !S_ISLNK(sourceStat.st_mode))) {
1008 // Source and target entry types don't match or this is an entry
1009 // we cannot handle. The user must handle this manually.
1010 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
1011 "writable file \"%s\" exists, but type doesn't match previous "
1012 "type\n", targetName);
1013 _AddIssue(TransactionIssueBuilder(
1014 BTransactionIssue::B_WRITABLE_FILE_TYPE_MISMATCH)
1015 .SetPath1(FSUtils::Entry(targetDirectory, targetName))
1016 .SetPath2(FSUtils::Entry(sourceDirectory,
1017 relativeSourcePath.Leaf())));
1018 return;
1019 }
1020
1021 if (S_ISDIR(sourceStat.st_mode)) {
1022 // entry is a directory -- recurse
1023 BDirectory sourceSubDirectory;
1024 error = sourceSubDirectory.SetTo(&sourceDirectory,
1025 relativeSourcePath.Leaf());
1026 if (error != B_OK) {
1027 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY)
1028 .SetPath1(_GetPath(
1029 FSUtils::Entry(sourceDirectory,
1030 relativeSourcePath.Leaf()),
1031 relativeSourcePath))
1032 .SetPackageName(package->FileName())
1033 .SetSystemError(error);
1034 }
1035
1036 BDirectory targetSubDirectory;
1037 error = targetSubDirectory.SetTo(&targetDirectory, targetName);
1038 if (error != B_OK) {
1039 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY)
1040 .SetPath1(_GetPath(
1041 FSUtils::Entry(targetDirectory, targetName),
1042 targetName))
1043 .SetPackageName(package->FileName())
1044 .SetSystemError(error);
1045 }
1046
1047 entry_ref entry;
1048 while (sourceSubDirectory.GetNextRef(&entry) == B_OK) {
1049 relativeSourcePath.AppendComponent(entry.name);
1050 _AddGlobalWritableFileRecurse(package, sourceSubDirectory,
1051 relativeSourcePath, targetSubDirectory, entry.name,
1052 updateType);
1053 relativeSourcePath.RemoveLastComponent();
1054 }
1055
1056 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
1057 "writable directory, recursion done\n");
1058 return;
1059 }
1060
1061 // get the package the target file originated from
1062 BString originalPackage;
1063 if (BNode(&targetDirectory, targetName).ReadAttrString(
1064 kPackageFileAttribute, &originalPackage) != B_OK) {
1065 // Can't determine the original package. The user must handle this
1066 // manually.
1067 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
1068 "failed to get SYS:PACKAGE attribute for \"%s\", can't tell if "
1069 "file needs to be updated\n",
1070 targetName);
1071 if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) {
1072 _AddIssue(TransactionIssueBuilder(
1073 BTransactionIssue::B_WRITABLE_FILE_NO_PACKAGE_ATTRIBUTE)
1074 .SetPath1(FSUtils::Entry(targetDirectory, targetName)));
1075 }
1076 return;
1077 }
1078
1079 // If that's our package, we're happy.
1080 if (originalPackage == package->RevisionedNameThrows()) {
1081 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
1082 "file \"%s\" tagged with same package version we're activating\n",
1083 targetName);
1084 return;
1085 }
1086
1087 // Check, whether the writable-files directory for the original package
1088 // exists.
1089 BString originalRelativeSourcePath = BString().SetToFormat("%s/%s",
1090 originalPackage.String(), relativeSourcePath.ToCString());
1091 if (originalRelativeSourcePath.IsEmpty())
1092 throw std::bad_alloc();
1093
1094 struct stat originalPackageStat;
1095 error = fWritableFilesDirectory.GetStatFor(originalRelativeSourcePath,
1096 &originalPackageStat);
1097 if (error != B_OK
1098 || (sourceStat.st_mode & S_IFMT)
1099 != (originalPackageStat.st_mode & S_IFMT)) {
1100 // Original entry doesn't exist (either we don't have the data from
1101 // the original package or the entry really didn't exist) or its
1102 // type differs from the expected one. The user must handle this
1103 // manually.
1104 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
1105 "original \"%s\" doesn't exist or has other type\n",
1106 _GetPath(FSUtils::Entry(fWritableFilesDirectory,
1107 originalRelativeSourcePath),
1108 originalRelativeSourcePath).String());
1109 if (error != B_OK) {
1110 _AddIssue(TransactionIssueBuilder(
1111 BTransactionIssue
1112 ::B_WRITABLE_FILE_OLD_ORIGINAL_FILE_MISSING)
1113 .SetPath1(FSUtils::Entry(targetDirectory, targetName))
1114 .SetPath2(FSUtils::Entry(fWritableFilesDirectory,
1115 originalRelativeSourcePath)));
1116 } else {
1117 _AddIssue(TransactionIssueBuilder(
1118 BTransactionIssue
1119 ::B_WRITABLE_FILE_OLD_ORIGINAL_FILE_TYPE_MISMATCH)
1120 .SetPath1(FSUtils::Entry(targetDirectory, targetName))
1121 .SetPath2(FSUtils::Entry(fWritableFilesDirectory,
1122 originalRelativeSourcePath)));
1123 }
1124 return;
1125 }
1126
1127 if (S_ISREG(sourceStat.st_mode)) {
1128 // compare file content
1129 bool equal;
1130 error = FSUtils::CompareFileContent(
1131 FSUtils::Entry(fWritableFilesDirectory,
1132 originalRelativeSourcePath),
1133 FSUtils::Entry(targetDirectory, targetName),
1134 equal);
1135 // TODO: Merge support!
1136 if (error != B_OK || !equal) {
1137 // The comparison failed or the files differ. The user must
1138 // handle this manually.
1139 PRINT("Volume::CommitTransactionHandler::"
1140 "_AddGlobalWritableFile(): "
1141 "file comparison \"%s\" failed (%s) or files aren't equal\n",
1142 targetName, strerror(error));
1143 if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) {
1144 if (error != B_OK) {
1145 _AddIssue(TransactionIssueBuilder(
1146 BTransactionIssue
1147 ::B_WRITABLE_FILE_COMPARISON_FAILED)
1148 .SetPath1(FSUtils::Entry(targetDirectory, targetName))
1149 .SetPath2(FSUtils::Entry(fWritableFilesDirectory,
1150 originalRelativeSourcePath))
1151 .SetSystemError(error));
1152 } else {
1153 _AddIssue(TransactionIssueBuilder(
1154 BTransactionIssue
1155 ::B_WRITABLE_FILE_NOT_EQUAL)
1156 .SetPath1(FSUtils::Entry(targetDirectory, targetName))
1157 .SetPath2(FSUtils::Entry(fWritableFilesDirectory,
1158 originalRelativeSourcePath)));
1159 }
1160 }
1161 return;
1162 }
1163 } else {
1164 // compare symlinks
1165 bool equal;
1166 error = FSUtils::CompareSymLinks(
1167 FSUtils::Entry(fWritableFilesDirectory,
1168 originalRelativeSourcePath),
1169 FSUtils::Entry(targetDirectory, targetName),
1170 equal);
1171 if (error != B_OK || !equal) {
1172 // The comparison failed or the symlinks differ. The user must
1173 // handle this manually.
1174 PRINT("Volume::CommitTransactionHandler::"
1175 "_AddGlobalWritableFile(): "
1176 "symlink comparison \"%s\" failed (%s) or symlinks aren't "
1177 "equal\n", targetName, strerror(error));
1178 if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) {
1179 if (error != B_OK) {
1180 _AddIssue(TransactionIssueBuilder(
1181 BTransactionIssue
1182 ::B_WRITABLE_SYMLINK_COMPARISON_FAILED)
1183 .SetPath1(FSUtils::Entry(targetDirectory, targetName))
1184 .SetPath2(FSUtils::Entry(fWritableFilesDirectory,
1185 originalRelativeSourcePath))
1186 .SetSystemError(error));
1187 } else {
1188 _AddIssue(TransactionIssueBuilder(
1189 BTransactionIssue
1190 ::B_WRITABLE_SYMLINK_NOT_EQUAL)
1191 .SetPath1(FSUtils::Entry(targetDirectory, targetName))
1192 .SetPath2(FSUtils::Entry(fWritableFilesDirectory,
1193 originalRelativeSourcePath)));
1194 }
1195 }
1196 return;
1197 }
1198 }
1199
1200 // Replace the existing file/symlink. We do that in two steps: First
1201 // copy the new file to a neighoring location, then move-replace the
1202 // old file.
1203 BString tempTargetName;
1204 tempTargetName.SetToFormat("%s.%s", targetName,
1205 package->RevisionedNameThrows().String());
1206 if (tempTargetName.IsEmpty())
1207 throw std::bad_alloc();
1208
1209 // copy
1210 FSTransaction::CreateOperation copyOperation(&fFSTransaction,
1211 FSUtils::Entry(targetDirectory, tempTargetName));
1212
1213 error = BCopyEngine(BCopyEngine::UNLINK_DESTINATION).CopyEntry(
1214 FSUtils::Entry(sourceDirectory, relativeSourcePath.Leaf()),
1215 FSUtils::Entry(targetDirectory, tempTargetName));
1216 if (error != B_OK) {
1217 throw Exception(B_TRANSACTION_FAILED_TO_COPY_FILE)
1218 .SetPath1(_GetPath(
1219 FSUtils::Entry(sourceDirectory,
1220 relativeSourcePath.Leaf()),
1221 relativeSourcePath))
1222 .SetPath2(_GetPath(
1223 FSUtils::Entry(targetDirectory, tempTargetName),
1224 tempTargetName))
1225 .SetSystemError(error);
1226 }
1227
1228 copyOperation.Finished();
1229
1230 // rename
1231 FSTransaction::RemoveOperation renameOperation(&fFSTransaction,
1232 FSUtils::Entry(targetDirectory, targetName),
1233 FSUtils::Entry(fWritableFilesDirectory,
1234 originalRelativeSourcePath));
1235
1236 BEntry targetEntry;
1237 error = targetEntry.SetTo(&targetDirectory, tempTargetName);
1238 if (error == B_OK)
1239 error = targetEntry.Rename(targetName, true);
1240 if (error != B_OK) {
1241 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE)
1242 .SetPath1(_GetPath(
1243 FSUtils::Entry(targetDirectory, tempTargetName),
1244 tempTargetName))
1245 .SetPath2(targetName)
1246 .SetSystemError(error);
1247 }
1248
1249 renameOperation.Finished();
1250 copyOperation.Unregister();
1251 }
1252
1253
1254 void
_RevertAddPackagesToActivate()1255 CommitTransactionHandler::_RevertAddPackagesToActivate()
1256 {
1257 if (fAddedPackages.empty() || fFirstBootProcessing)
1258 return;
1259
1260 // open transaction directory
1261 BDirectory transactionDirectory;
1262 status_t error = transactionDirectory.SetTo(&fTransactionDirectoryRef);
1263 if (error != B_OK) {
1264 ERROR("failed to open transaction directory: %s\n",
1265 strerror(error));
1266 }
1267
1268 for (PackageSet::iterator it = fAddedPackages.begin();
1269 it != fAddedPackages.end(); ++it) {
1270 // remove package from the volume
1271 Package* package = *it;
1272
1273 if (fPackagesAlreadyAdded.find(package)
1274 != fPackagesAlreadyAdded.end()) {
1275 continue;
1276 }
1277
1278 fVolumeState->RemovePackage(package);
1279
1280 if (transactionDirectory.InitCheck() != B_OK)
1281 continue;
1282
1283 // get BEntry for the package
1284 NotOwningEntryRef entryRef(package->EntryRef());
1285 BEntry entry;
1286 error = entry.SetTo(&entryRef);
1287 if (error != B_OK) {
1288 ERROR("failed to get entry for package \"%s\": %s\n",
1289 package->FileName().String(), strerror(error));
1290 continue;
1291 }
1292
1293 // move entry
1294 error = entry.MoveTo(&transactionDirectory);
1295 if (error != B_OK) {
1296 ERROR("failed to move new package \"%s\" back to transaction "
1297 "directory: %s\n", package->FileName().String(),
1298 strerror(error));
1299 continue;
1300 }
1301
1302 fPackageFileManager->PackageFileMoved(package->File(),
1303 fTransactionDirectoryRef);
1304 package->File()->IncrementEntryRemovedIgnoreLevel();
1305 }
1306 }
1307
1308
1309 void
_RevertRemovePackagesToDeactivate()1310 CommitTransactionHandler::_RevertRemovePackagesToDeactivate()
1311 {
1312 if (fRemovedPackages.empty() || fFirstBootProcessing)
1313 return;
1314
1315 // open packages directory
1316 BDirectory packagesDirectory;
1317 status_t error
1318 = packagesDirectory.SetTo(&fVolume->PackagesDirectoryRef());
1319 if (error != B_OK) {
1320 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY)
1321 .SetPath1("<packages>")
1322 .SetSystemError(error);
1323 }
1324
1325 for (PackageSet::iterator it = fRemovedPackages.begin();
1326 it != fRemovedPackages.end(); ++it) {
1327 Package* package = *it;
1328 if (fPackagesAlreadyRemoved.find(package)
1329 != fPackagesAlreadyRemoved.end()) {
1330 continue;
1331 }
1332
1333 // get a BEntry for the package
1334 BEntry entry;
1335 status_t error = entry.SetTo(&fOldStateDirectory,
1336 package->FileName());
1337 if (error != B_OK) {
1338 ERROR("failed to get entry for package \"%s\": %s\n",
1339 package->FileName().String(), strerror(error));
1340 continue;
1341 }
1342
1343 // move entry
1344 error = entry.MoveTo(&packagesDirectory);
1345 if (error != B_OK) {
1346 ERROR("failed to move old package \"%s\" back to packages "
1347 "directory: %s\n", package->FileName().String(),
1348 strerror(error));
1349 continue;
1350 }
1351
1352 fPackageFileManager->PackageFileMoved(package->File(),
1353 fVolume->PackagesDirectoryRef());
1354 package->File()->IncrementEntryCreatedIgnoreLevel();
1355 }
1356 }
1357
1358
1359 void
_RevertUserGroupChanges()1360 CommitTransactionHandler::_RevertUserGroupChanges()
1361 {
1362 // delete users
1363 for (StringSet::const_iterator it = fAddedUsers.begin();
1364 it != fAddedUsers.end(); ++it) {
1365 std::string commandLine("userdel ");
1366 commandLine += FSUtils::ShellEscapeString(it->c_str()).String();
1367 if (system(commandLine.c_str()) != 0)
1368 ERROR("failed to remove user \"%s\"\n", it->c_str());
1369 }
1370
1371 // delete groups
1372 for (StringSet::const_iterator it = fAddedGroups.begin();
1373 it != fAddedGroups.end(); ++it) {
1374 std::string commandLine("groupdel ");
1375 commandLine += FSUtils::ShellEscapeString(it->c_str()).String();
1376 if (system(commandLine.c_str()) != 0)
1377 ERROR("failed to remove group \"%s\"\n", it->c_str());
1378 }
1379 }
1380
1381
1382 void
_RunPostInstallScripts()1383 CommitTransactionHandler::_RunPostInstallScripts()
1384 {
1385 for (PackageSet::iterator it = fAddedPackages.begin();
1386 it != fAddedPackages.end(); ++it) {
1387 Package* package = *it;
1388 fCurrentPackage = package;
1389 const BStringList& scripts = package->Info().PostInstallScripts();
1390 int32 count = scripts.CountStrings();
1391 for (int32 i = 0; i < count; i++)
1392 _RunPostOrPreScript(package, scripts.StringAt(i), true);
1393 }
1394
1395 fCurrentPackage = NULL;
1396 }
1397
1398
1399 void
_RunPreUninstallScripts()1400 CommitTransactionHandler::_RunPreUninstallScripts()
1401 {
1402 // Note this runs in the correct order, so dependents get uninstalled before
1403 // the packages they depend on. No need for a reversed loop.
1404 for (PackageSet::iterator it = fPackagesToDeactivate.begin();
1405 it != fPackagesToDeactivate.end(); ++it) {
1406 Package* package = *it;
1407 fCurrentPackage = package;
1408 const BStringList& scripts = package->Info().PreUninstallScripts();
1409 int32 count = scripts.CountStrings();
1410 for (int32 i = 0; i < count; i++)
1411 _RunPostOrPreScript(package, scripts.StringAt(i), false);
1412 }
1413
1414 fCurrentPackage = NULL;
1415 }
1416
1417
1418 void
_RunPostOrPreScript(Package * package,const BString & script,bool postNotPre)1419 CommitTransactionHandler::_RunPostOrPreScript(Package* package,
1420 const BString& script, bool postNotPre)
1421 {
1422 const char *postOrPreInstallWording = postNotPre
1423 ? "post-installation" : "pre-uninstall";
1424 BDirectory rootDir(&fVolume->RootDirectoryRef());
1425 BPath scriptPath(&rootDir, script);
1426 status_t error = scriptPath.InitCheck();
1427 if (error != B_OK) {
1428 ERROR("Volume::CommitTransactionHandler::_RunPostOrPreScript(): "
1429 "failed get path of %s script \"%s\" of package "
1430 "%s: %s\n",
1431 postOrPreInstallWording, script.String(),
1432 package->FileName().String(), strerror(error));
1433 _AddIssue(TransactionIssueBuilder(postNotPre
1434 ? BTransactionIssue::B_POST_INSTALL_SCRIPT_NOT_FOUND
1435 : BTransactionIssue::B_PRE_UNINSTALL_SCRIPT_NOT_FOUND)
1436 .SetPath1(script)
1437 .SetSystemError(error));
1438 return;
1439 }
1440
1441 errno = 0;
1442 int result = system(scriptPath.Path());
1443 if (result != 0) {
1444 ERROR("Volume::CommitTransactionHandler::_RunPostOrPreScript(): "
1445 "running %s script \"%s\" of package %s "
1446 "failed: %d (errno: %s)\n",
1447 postOrPreInstallWording, script.String(),
1448 package->FileName().String(), result, strerror(errno));
1449 if (result < 0 || result == 127) { // bash shell returns 127 on failure.
1450 _AddIssue(TransactionIssueBuilder(postNotPre
1451 ? BTransactionIssue::B_STARTING_POST_INSTALL_SCRIPT_FAILED
1452 : BTransactionIssue::B_STARTING_PRE_UNINSTALL_SCRIPT_FAILED)
1453 .SetPath1(BString(scriptPath.Path()))
1454 .SetSystemError(errno));
1455 } else { // positive is an exit code from the script itself.
1456 _AddIssue(TransactionIssueBuilder(postNotPre
1457 ? BTransactionIssue::B_POST_INSTALL_SCRIPT_FAILED
1458 : BTransactionIssue::B_PRE_UNINSTALL_SCRIPT_FAILED)
1459 .SetPath1(BString(scriptPath.Path()))
1460 .SetExitCode(result));
1461 }
1462 }
1463 }
1464
1465
1466 void
_QueuePostInstallScripts()1467 CommitTransactionHandler::_QueuePostInstallScripts()
1468 {
1469 BDirectory adminDirectory;
1470 status_t error = _OpenPackagesSubDirectory(
1471 RelativePath(kAdminDirectoryName), true, adminDirectory);
1472 if (error != B_OK) {
1473 ERROR("Failed to open administrative directory: %s\n", strerror(error));
1474 return;
1475 }
1476
1477 BDirectory scriptsDirectory;
1478 error = scriptsDirectory.SetTo(&adminDirectory, kQueuedScriptsDirectoryName);
1479 if (error == B_ENTRY_NOT_FOUND)
1480 error = adminDirectory.CreateDirectory(kQueuedScriptsDirectoryName, &scriptsDirectory);
1481 if (error != B_OK) {
1482 ERROR("Failed to open queued scripts directory: %s\n", strerror(error));
1483 return;
1484 }
1485
1486 BDirectory rootDir(&fVolume->RootDirectoryRef());
1487 for (PackageSet::iterator it = fAddedPackages.begin();
1488 it != fAddedPackages.end(); ++it) {
1489 Package* package = *it;
1490 const BStringList& scripts = package->Info().PostInstallScripts();
1491 for (int32 i = 0; i < scripts.CountStrings(); ++i) {
1492 BPath scriptPath(&rootDir, scripts.StringAt(i));
1493 status_t error = scriptPath.InitCheck();
1494 if (error != B_OK) {
1495 ERROR("Can't find script: %s\n", scripts.StringAt(i).String());
1496 continue;
1497 }
1498
1499 // symlink to the script
1500 BSymLink scriptLink;
1501 scriptsDirectory.CreateSymLink(scriptPath.Leaf(),
1502 scriptPath.Path(), &scriptLink);
1503 if (scriptLink.InitCheck() != B_OK) {
1504 ERROR("Creating symlink failed: %s\n", strerror(scriptLink.InitCheck()));
1505 continue;
1506 }
1507 }
1508 }
1509 }
1510
1511
1512 void
_ExtractPackageContent(Package * package,const BStringList & contentPaths,BDirectory & targetDirectory,BDirectory & _extractedFilesDirectory)1513 CommitTransactionHandler::_ExtractPackageContent(Package* package,
1514 const BStringList& contentPaths, BDirectory& targetDirectory,
1515 BDirectory& _extractedFilesDirectory)
1516 {
1517 // check whether the subdirectory already exists
1518 BString targetName(package->RevisionedNameThrows());
1519
1520 BEntry targetEntry;
1521 status_t error = targetEntry.SetTo(&targetDirectory, targetName);
1522 if (error != B_OK) {
1523 throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY)
1524 .SetPath1(_GetPath(
1525 FSUtils::Entry(targetDirectory, targetName),
1526 targetName))
1527 .SetPackageName(package->FileName())
1528 .SetSystemError(error);
1529 }
1530 if (targetEntry.Exists()) {
1531 // nothing to do -- the very same version of the package has already
1532 // been extracted
1533 error = _extractedFilesDirectory.SetTo(&targetDirectory,
1534 targetName);
1535 if (error != B_OK) {
1536 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY)
1537 .SetPath1(_GetPath(
1538 FSUtils::Entry(targetDirectory, targetName),
1539 targetName))
1540 .SetPackageName(package->FileName())
1541 .SetSystemError(error);
1542 }
1543 return;
1544 }
1545
1546 // create the subdirectory with a temporary name (remove, if it already
1547 // exists)
1548 BString temporaryTargetName = BString().SetToFormat("%s.tmp",
1549 targetName.String());
1550 if (temporaryTargetName.IsEmpty())
1551 throw std::bad_alloc();
1552
1553 error = targetEntry.SetTo(&targetDirectory, temporaryTargetName);
1554 if (error != B_OK) {
1555 throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY)
1556 .SetPath1(_GetPath(
1557 FSUtils::Entry(targetDirectory, temporaryTargetName),
1558 temporaryTargetName))
1559 .SetPackageName(package->FileName())
1560 .SetSystemError(error);
1561 }
1562
1563 if (targetEntry.Exists()) {
1564 // remove pre-existing
1565 error = BRemoveEngine().RemoveEntry(FSUtils::Entry(targetEntry));
1566 if (error != B_OK) {
1567 throw Exception(B_TRANSACTION_FAILED_TO_REMOVE_DIRECTORY)
1568 .SetPath1(_GetPath(
1569 FSUtils::Entry(targetDirectory, temporaryTargetName),
1570 temporaryTargetName))
1571 .SetPackageName(package->FileName())
1572 .SetSystemError(error);
1573 }
1574 }
1575
1576 BDirectory& subDirectory = _extractedFilesDirectory;
1577 FSTransaction::CreateOperation createSubDirectoryOperation(
1578 &fFSTransaction,
1579 FSUtils::Entry(targetDirectory, temporaryTargetName));
1580 error = targetDirectory.CreateDirectory(temporaryTargetName,
1581 &subDirectory);
1582 if (error != B_OK) {
1583 throw Exception(B_TRANSACTION_FAILED_TO_CREATE_DIRECTORY)
1584 .SetPath1(_GetPath(
1585 FSUtils::Entry(targetDirectory, temporaryTargetName),
1586 temporaryTargetName))
1587 .SetPackageName(package->FileName())
1588 .SetSystemError(error);
1589 }
1590
1591 createSubDirectoryOperation.Finished();
1592
1593 // extract
1594 NotOwningEntryRef packageRef(package->EntryRef());
1595
1596 int32 contentPathCount = contentPaths.CountStrings();
1597 for (int32 i = 0; i < contentPathCount; i++) {
1598 const char* contentPath = contentPaths.StringAt(i);
1599
1600 error = FSUtils::ExtractPackageContent(FSUtils::Entry(packageRef),
1601 contentPath, FSUtils::Entry(subDirectory));
1602 if (error != B_OK) {
1603 throw Exception(B_TRANSACTION_FAILED_TO_EXTRACT_PACKAGE_FILE)
1604 .SetPath1(contentPath)
1605 .SetPackageName(package->FileName())
1606 .SetSystemError(error);
1607 }
1608 }
1609
1610 // tag all entries with the package attribute
1611 _TagPackageEntriesRecursively(subDirectory, targetName, true);
1612
1613 // rename the subdirectory
1614 error = targetEntry.Rename(targetName);
1615 if (error != B_OK) {
1616 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE)
1617 .SetPath1(_GetPath(
1618 FSUtils::Entry(targetDirectory, temporaryTargetName),
1619 temporaryTargetName))
1620 .SetPath2(targetName)
1621 .SetPackageName(package->FileName())
1622 .SetSystemError(error);
1623 }
1624
1625 // keep the directory, regardless of whether the transaction is rolled
1626 // back
1627 createSubDirectoryOperation.Unregister();
1628 }
1629
1630
1631 status_t
_OpenPackagesSubDirectory(const RelativePath & path,bool create,BDirectory & _directory)1632 CommitTransactionHandler::_OpenPackagesSubDirectory(const RelativePath& path,
1633 bool create, BDirectory& _directory)
1634 {
1635 // open the packages directory
1636 BDirectory directory;
1637 status_t error = directory.SetTo(&fVolume->PackagesDirectoryRef());
1638 if (error != B_OK) {
1639 ERROR("CommitTransactionHandler::_OpenPackagesSubDirectory(): failed "
1640 "to open packages directory: %s\n", strerror(error));
1641 RETURN_ERROR(error);
1642 }
1643
1644 return FSUtils::OpenSubDirectory(directory, path, create, _directory);
1645 }
1646
1647
1648 status_t
_OpenPackagesFile(const RelativePath & subDirectoryPath,const char * fileName,uint32 openMode,BFile & _file,BEntry * _entry)1649 CommitTransactionHandler::_OpenPackagesFile(
1650 const RelativePath& subDirectoryPath, const char* fileName, uint32 openMode,
1651 BFile& _file, BEntry* _entry)
1652 {
1653 BDirectory directory;
1654 if (!subDirectoryPath.IsEmpty()) {
1655 status_t error = _OpenPackagesSubDirectory(subDirectoryPath,
1656 (openMode & B_CREATE_FILE) != 0, directory);
1657 if (error != B_OK) {
1658 ERROR("CommitTransactionHandler::_OpenPackagesFile(): failed to "
1659 "open packages subdirectory \"%s\": %s\n",
1660 subDirectoryPath.ToString().String(), strerror(error));
1661 RETURN_ERROR(error);
1662 }
1663 } else {
1664 status_t error = directory.SetTo(&fVolume->PackagesDirectoryRef());
1665 if (error != B_OK) {
1666 ERROR("CommitTransactionHandler::_OpenPackagesFile(): failed to "
1667 "open packages directory: %s\n", strerror(error));
1668 RETURN_ERROR(error);
1669 }
1670 }
1671
1672 BEntry stackEntry;
1673 BEntry& entry = _entry != NULL ? *_entry : stackEntry;
1674 status_t error = entry.SetTo(&directory, fileName);
1675 if (error != B_OK) {
1676 ERROR("CommitTransactionHandler::_OpenPackagesFile(): failed to get "
1677 "entry for file: %s", strerror(error));
1678 RETURN_ERROR(error);
1679 }
1680
1681 return _file.SetTo(&entry, openMode);
1682 }
1683
1684
1685 void
_WriteActivationFile(const RelativePath & directoryPath,const char * fileName,const PackageSet & toActivate,const PackageSet & toDeactivate,BEntry & _entry)1686 CommitTransactionHandler::_WriteActivationFile(
1687 const RelativePath& directoryPath, const char* fileName,
1688 const PackageSet& toActivate, const PackageSet& toDeactivate,
1689 BEntry& _entry)
1690 {
1691 // create the content
1692 BString activationFileContent;
1693 _CreateActivationFileContent(toActivate, toDeactivate,
1694 activationFileContent);
1695
1696 // write the file
1697 status_t error = _WriteTextFile(directoryPath, fileName,
1698 activationFileContent, _entry);
1699 if (error != B_OK) {
1700 BString filePath = directoryPath.ToString() << '/' << fileName;
1701 throw Exception(B_TRANSACTION_FAILED_TO_WRITE_ACTIVATION_FILE)
1702 .SetPath1(_GetPath(
1703 FSUtils::Entry(fVolume->PackagesDirectoryRef(), filePath),
1704 filePath))
1705 .SetSystemError(error);
1706 }
1707 }
1708
1709
1710 void
_CreateActivationFileContent(const PackageSet & toActivate,const PackageSet & toDeactivate,BString & _content)1711 CommitTransactionHandler::_CreateActivationFileContent(
1712 const PackageSet& toActivate, const PackageSet& toDeactivate,
1713 BString& _content)
1714 {
1715 BString activationFileContent;
1716 for (PackageFileNameHashTable::Iterator it
1717 = fVolumeState->ByFileNameIterator();
1718 Package* package = it.Next();) {
1719 if (package->IsActive()
1720 && toDeactivate.find(package) == toDeactivate.end()) {
1721 int32 length = activationFileContent.Length();
1722 activationFileContent << package->FileName() << '\n';
1723 if (activationFileContent.Length()
1724 < length + package->FileName().Length() + 1) {
1725 throw Exception(B_TRANSACTION_NO_MEMORY);
1726 }
1727 }
1728 }
1729
1730 for (PackageSet::const_iterator it = toActivate.begin();
1731 it != toActivate.end(); ++it) {
1732 Package* package = *it;
1733 int32 length = activationFileContent.Length();
1734 activationFileContent << package->FileName() << '\n';
1735 if (activationFileContent.Length()
1736 < length + package->FileName().Length() + 1) {
1737 throw Exception(B_TRANSACTION_NO_MEMORY);
1738 }
1739 }
1740
1741 _content = activationFileContent;
1742 }
1743
1744
1745 status_t
_WriteTextFile(const RelativePath & directoryPath,const char * fileName,const BString & content,BEntry & _entry)1746 CommitTransactionHandler::_WriteTextFile(const RelativePath& directoryPath,
1747 const char* fileName, const BString& content, BEntry& _entry)
1748 {
1749 BFile file;
1750 status_t error = _OpenPackagesFile(directoryPath,
1751 fileName, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE, file, &_entry);
1752 if (error != B_OK) {
1753 ERROR("CommitTransactionHandler::_WriteTextFile(): failed to create "
1754 "file \"%s/%s\": %s\n", directoryPath.ToString().String(), fileName,
1755 strerror(error));
1756 return error;
1757 }
1758
1759 ssize_t bytesWritten = file.Write(content.String(),
1760 content.Length());
1761 if (bytesWritten < 0) {
1762 ERROR("CommitTransactionHandler::_WriteTextFile(): failed to write "
1763 "file \"%s/%s\": %s\n", directoryPath.ToString().String(), fileName,
1764 strerror(bytesWritten));
1765 return bytesWritten;
1766 }
1767
1768 return B_OK;
1769 }
1770
1771
1772 void
_ChangePackageActivation(const PackageSet & packagesToActivate,const PackageSet & packagesToDeactivate)1773 CommitTransactionHandler::_ChangePackageActivation(
1774 const PackageSet& packagesToActivate,
1775 const PackageSet& packagesToDeactivate)
1776 {
1777 INFORM("CommitTransactionHandler::_ChangePackageActivation(): activating "
1778 "%zu, deactivating %zu packages\n", packagesToActivate.size(),
1779 packagesToDeactivate.size());
1780
1781 // write the temporary package activation file
1782 BEntry activationFileEntry;
1783 _WriteActivationFile(RelativePath(kAdminDirectoryName),
1784 kTemporaryActivationFileName, packagesToActivate, packagesToDeactivate,
1785 activationFileEntry);
1786
1787 // notify packagefs
1788 if (fVolumeStateIsActive) {
1789 _ChangePackageActivationIOCtl(packagesToActivate, packagesToDeactivate);
1790 } else {
1791 // TODO: Notify packagefs that active packages have been moved or do
1792 // node monitoring in packagefs!
1793 }
1794
1795 // rename the temporary activation file to the final file
1796 status_t error = activationFileEntry.Rename(kActivationFileName, true);
1797 if (error != B_OK) {
1798 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE)
1799 .SetPath1(_GetPath(
1800 FSUtils::Entry(activationFileEntry),
1801 activationFileEntry.Name()))
1802 .SetPath2(kActivationFileName)
1803 .SetSystemError(error);
1804
1805 // TODO: We should probably try to revert the activation changes, though that
1806 // will fail, if this method has been called in response to node monitoring
1807 // events. Alternatively moving the package activation file could be made part
1808 // of the ioctl(), since packagefs should be able to undo package changes until
1809 // the very end, unless running out of memory. In the end the situation would be
1810 // bad anyway, though, since the activation file may refer to removed packages
1811 // and things would be in an inconsistent state after rebooting.
1812 }
1813
1814 // Update our state, i.e. remove deactivated packages and mark activated
1815 // packages accordingly.
1816 fVolumeState->ActivationChanged(packagesToActivate, packagesToDeactivate);
1817 }
1818
1819
1820 void
_ChangePackageActivationIOCtl(const PackageSet & packagesToActivate,const PackageSet & packagesToDeactivate)1821 CommitTransactionHandler::_ChangePackageActivationIOCtl(
1822 const PackageSet& packagesToActivate,
1823 const PackageSet& packagesToDeactivate)
1824 {
1825 // compute the size of the allocation we need for the activation change
1826 // request
1827 int32 itemCount = packagesToActivate.size() + packagesToDeactivate.size();
1828 size_t requestSize = sizeof(PackageFSActivationChangeRequest)
1829 + itemCount * sizeof(PackageFSActivationChangeItem);
1830
1831 for (PackageSet::iterator it = packagesToActivate.begin();
1832 it != packagesToActivate.end(); ++it) {
1833 requestSize += (*it)->FileName().Length() + 1;
1834 }
1835
1836 for (PackageSet::iterator it = packagesToDeactivate.begin();
1837 it != packagesToDeactivate.end(); ++it) {
1838 requestSize += (*it)->FileName().Length() + 1;
1839 }
1840
1841 // allocate and prepare the request
1842 PackageFSActivationChangeRequest* request
1843 = (PackageFSActivationChangeRequest*)malloc(requestSize);
1844 if (request == NULL)
1845 throw Exception(B_TRANSACTION_NO_MEMORY);
1846 MemoryDeleter requestDeleter(request);
1847
1848 request->itemCount = itemCount;
1849
1850 PackageFSActivationChangeItem* item = &request->items[0];
1851 char* nameBuffer = (char*)(item + itemCount);
1852
1853 for (PackageSet::iterator it = packagesToActivate.begin();
1854 it != packagesToActivate.end(); ++it, item++) {
1855 _FillInActivationChangeItem(item, PACKAGE_FS_ACTIVATE_PACKAGE, *it,
1856 nameBuffer);
1857 }
1858
1859 for (PackageSet::iterator it = packagesToDeactivate.begin();
1860 it != packagesToDeactivate.end(); ++it, item++) {
1861 _FillInActivationChangeItem(item, PACKAGE_FS_DEACTIVATE_PACKAGE, *it,
1862 nameBuffer);
1863 }
1864
1865 // issue the request
1866 FileDescriptorCloser fd(fVolume->OpenRootDirectory());
1867 if (!fd.IsSet()) {
1868 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY)
1869 .SetPath1(_GetPath(
1870 FSUtils::Entry(fVolume->RootDirectoryRef()),
1871 "<packagefs root>"))
1872 .SetSystemError(fd.Get());
1873 }
1874
1875 if (ioctl(fd.Get(), PACKAGE_FS_OPERATION_CHANGE_ACTIVATION, request,
1876 requestSize) != 0) {
1877 // TODO: We need more error information and error handling!
1878 throw Exception(B_TRANSACTION_FAILED_TO_CHANGE_PACKAGE_ACTIVATION)
1879 .SetSystemError(errno);
1880 }
1881 }
1882
1883
1884 void
_FillInActivationChangeItem(PackageFSActivationChangeItem * item,PackageFSActivationChangeType type,Package * package,char * & nameBuffer)1885 CommitTransactionHandler::_FillInActivationChangeItem(
1886 PackageFSActivationChangeItem* item, PackageFSActivationChangeType type,
1887 Package* package, char*& nameBuffer)
1888 {
1889 item->type = type;
1890 item->packageDeviceID = package->NodeRef().device;
1891 item->packageNodeID = package->NodeRef().node;
1892 item->nameLength = package->FileName().Length();
1893 item->parentDeviceID = fVolume->PackagesDeviceID();
1894 item->parentDirectoryID = fVolume->PackagesDirectoryID();
1895 item->name = nameBuffer;
1896 strcpy(nameBuffer, package->FileName());
1897 nameBuffer += package->FileName().Length() + 1;
1898 }
1899
1900
1901 bool
_IsSystemPackage(Package * package)1902 CommitTransactionHandler::_IsSystemPackage(Package* package)
1903 {
1904 // package name should be "haiku[_<arch>]"
1905 const BString& name = package->Info().Name();
1906 if (!name.StartsWith("haiku"))
1907 return false;
1908 if (name.Length() == 5)
1909 return true;
1910 if (name[5] != '_')
1911 return false;
1912
1913 BPackageArchitecture architecture;
1914 return BPackageInfo::GetArchitectureByName(name.String() + 6, architecture)
1915 == B_OK;
1916 }
1917
1918
1919 void
_AddIssue(const TransactionIssueBuilder & builder)1920 CommitTransactionHandler::_AddIssue(const TransactionIssueBuilder& builder)
1921 {
1922 fResult.AddIssue(builder.BuildIssue(fCurrentPackage));
1923 }
1924
1925
1926 /*static*/ BString
_GetPath(const FSUtils::Entry & entry,const BString & fallback)1927 CommitTransactionHandler::_GetPath(const FSUtils::Entry& entry,
1928 const BString& fallback)
1929 {
1930 BString path = entry.Path();
1931 return path.IsEmpty() ? fallback : path;
1932 }
1933
1934
1935 /*static*/ void
_TagPackageEntriesRecursively(BDirectory & directory,const BString & value,bool nonDirectoriesOnly)1936 CommitTransactionHandler::_TagPackageEntriesRecursively(BDirectory& directory,
1937 const BString& value, bool nonDirectoriesOnly)
1938 {
1939 char buffer[offsetof(struct dirent, d_name) + B_FILE_NAME_LENGTH];
1940 dirent *entry = (dirent*)buffer;
1941 while (directory.GetNextDirents(entry, sizeof(buffer), 1) == 1) {
1942 if (strcmp(entry->d_name, ".") == 0
1943 || strcmp(entry->d_name, "..") == 0) {
1944 continue;
1945 }
1946
1947 // determine type
1948 struct stat st;
1949 status_t error = directory.GetStatFor(entry->d_name, &st);
1950 if (error != B_OK) {
1951 throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY)
1952 .SetPath1(_GetPath(
1953 FSUtils::Entry(directory, entry->d_name),
1954 entry->d_name))
1955 .SetSystemError(error);
1956 }
1957 bool isDirectory = S_ISDIR(st.st_mode);
1958
1959 // open the node and set the attribute
1960 BNode stackNode;
1961 BDirectory stackDirectory;
1962 BNode* node;
1963 if (isDirectory) {
1964 node = &stackDirectory;
1965 error = stackDirectory.SetTo(&directory, entry->d_name);
1966 } else {
1967 node = &stackNode;
1968 error = stackNode.SetTo(&directory, entry->d_name);
1969 }
1970
1971 if (error != B_OK) {
1972 throw Exception(isDirectory
1973 ? B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY
1974 : B_TRANSACTION_FAILED_TO_OPEN_FILE)
1975 .SetPath1(_GetPath(
1976 FSUtils::Entry(directory, entry->d_name),
1977 entry->d_name))
1978 .SetSystemError(error);
1979 }
1980
1981 if (!isDirectory || !nonDirectoriesOnly) {
1982 error = node->WriteAttrString(kPackageFileAttribute, &value);
1983 if (error != B_OK) {
1984 throw Exception(B_TRANSACTION_FAILED_TO_WRITE_FILE_ATTRIBUTE)
1985 .SetPath1(_GetPath(
1986 FSUtils::Entry(directory, entry->d_name),
1987 entry->d_name))
1988 .SetSystemError(error);
1989 }
1990 }
1991
1992 // recurse
1993 if (isDirectory) {
1994 _TagPackageEntriesRecursively(stackDirectory, value,
1995 nonDirectoriesOnly);
1996 }
1997 }
1998 }
1999
2000
2001 /*static*/ status_t
_AssertEntriesAreEqual(const BEntry & entry,const BDirectory * directory)2002 CommitTransactionHandler::_AssertEntriesAreEqual(const BEntry& entry,
2003 const BDirectory* directory)
2004 {
2005 BFile a;
2006 status_t status = a.SetTo(&entry, B_READ_ONLY);
2007 if (status != B_OK)
2008 return status;
2009
2010 BFile b;
2011 status = b.SetTo(directory, entry.Name(), B_READ_ONLY);
2012 if (status != B_OK)
2013 return status;
2014
2015 off_t aSize;
2016 status = a.GetSize(&aSize);
2017 if (status != B_OK)
2018 return status;
2019
2020 off_t bSize;
2021 status = b.GetSize(&bSize);
2022 if (status != B_OK)
2023 return status;
2024
2025 if (aSize != bSize)
2026 return B_FILE_EXISTS;
2027
2028 const size_t bufferSize = 4096;
2029 uint8 aBuffer[bufferSize];
2030 uint8 bBuffer[bufferSize];
2031
2032 while (aSize > 0) {
2033 ssize_t aRead = a.Read(aBuffer, bufferSize);
2034 ssize_t bRead = b.Read(bBuffer, bufferSize);
2035 if (aRead < 0 || aRead != bRead)
2036 return B_FILE_EXISTS;
2037 if (memcmp(aBuffer, bBuffer, aRead) != 0)
2038 return B_FILE_EXISTS;
2039 aSize -= aRead;
2040 }
2041
2042 INFORM("CommitTransactionHandler::_AssertEntriesAreEqual(): "
2043 "Package file '%s' already exists in target folder "
2044 "with equal contents\n", entry.Name());
2045 return B_OK;
2046 }
2047