2 * Copyright 2013-2014, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Ingo Weinhold <ingo_weinhold@gmx.de>
10 #include "CommitTransactionHandler.h"
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>
27 #include "Constants.h"
28 #include "DebugSupport.h"
29 #include "Exception.h"
30 #include "PackageFileManager.h"
31 #include "VolumeState.h"
34 using namespace BPackageKit::BPrivate
;
36 using BPackageKit::BTransactionIssue
;
39 // #pragma mark - TransactionIssueBuilder
42 struct CommitTransactionHandler::TransactionIssueBuilder
{
43 TransactionIssueBuilder(BTransactionIssue::BType type
,
44 Package
* package
= NULL
)
47 fPackageName(package
!= NULL
? package
->FileName() : BString()),
55 TransactionIssueBuilder
& SetPath1(const BString
& path
)
61 TransactionIssueBuilder
& SetPath1(const FSUtils::Entry
& entry
)
63 return SetPath1(entry
.Path());
66 TransactionIssueBuilder
& SetPath2(const BString
& path
)
72 TransactionIssueBuilder
& SetPath2(const FSUtils::Entry
& entry
)
74 return SetPath2(entry
.Path());
77 TransactionIssueBuilder
& SetSystemError(status_t error
)
83 TransactionIssueBuilder
& SetExitCode(int exitCode
)
89 BTransactionIssue
BuildIssue(Package
* package
) const
91 BString
packageName(fPackageName
);
92 if (packageName
.IsEmpty() && package
!= NULL
)
93 packageName
= package
->FileName();
95 return BTransactionIssue(fType
, packageName
, fPath1
, fPath2
,
96 fSystemError
, fExitCode
);
100 BTransactionIssue::BType fType
;
101 BString fPackageName
;
104 status_t fSystemError
;
109 // #pragma mark - CommitTransactionHandler
112 CommitTransactionHandler::CommitTransactionHandler(Volume
* volume
,
113 PackageFileManager
* packageFileManager
, BCommitTransactionResult
& result
)
116 fPackageFileManager(packageFileManager
),
118 fVolumeStateIsActive(false),
119 fPackagesToActivate(),
120 fPackagesToDeactivate(),
123 fPackagesAlreadyAdded(),
124 fPackagesAlreadyRemoved(),
125 fOldStateDirectory(),
126 fOldStateDirectoryRef(),
127 fOldStateDirectoryName(),
128 fTransactionDirectoryRef(),
129 fWritableFilesDirectory(),
134 fCurrentPackage(NULL
)
139 CommitTransactionHandler::~CommitTransactionHandler()
141 // Delete Package objects we created in case of error (on success
142 // fPackagesToActivate will be empty).
143 int32 count
= fPackagesToActivate
.CountItems();
144 for (int32 i
= 0; i
< count
; i
++) {
145 Package
* package
= fPackagesToActivate
.ItemAt(i
);
146 if (fPackagesAlreadyAdded
.find(package
)
147 == fPackagesAlreadyAdded
.end()) {
157 CommitTransactionHandler::Init(VolumeState
* volumeState
,
158 bool isActiveVolumeState
, const PackageSet
& packagesAlreadyAdded
,
159 const PackageSet
& packagesAlreadyRemoved
)
161 fVolumeState
= volumeState
->Clone();
162 if (fVolumeState
== NULL
)
163 throw std::bad_alloc();
165 fVolumeStateIsActive
= isActiveVolumeState
;
167 for (PackageSet::const_iterator it
= packagesAlreadyAdded
.begin();
168 it
!= packagesAlreadyAdded
.end(); ++it
) {
169 Package
* package
= fVolumeState
->FindPackage((*it
)->FileName());
170 fPackagesAlreadyAdded
.insert(package
);
173 for (PackageSet::const_iterator it
= packagesAlreadyRemoved
.begin();
174 it
!= packagesAlreadyRemoved
.end(); ++it
) {
175 Package
* package
= fVolumeState
->FindPackage((*it
)->FileName());
176 fPackagesAlreadyRemoved
.insert(package
);
182 CommitTransactionHandler::HandleRequest(BMessage
* request
)
185 BActivationTransaction
transaction(request
, &error
);
187 error
= transaction
.InitCheck();
189 if (error
== B_NO_MEMORY
)
190 throw Exception(B_TRANSACTION_NO_MEMORY
);
191 throw Exception(B_TRANSACTION_BAD_REQUEST
);
194 HandleRequest(transaction
);
199 CommitTransactionHandler::HandleRequest(
200 const BActivationTransaction
& transaction
)
202 // check the change count
203 if (transaction
.ChangeCount() != fVolume
->ChangeCount())
204 throw Exception(B_TRANSACTION_CHANGE_COUNT_MISMATCH
);
206 // collect the packages to deactivate
207 _GetPackagesToDeactivate(transaction
);
209 // read the packages to activate
210 _ReadPackagesToActivate(transaction
);
212 // anything to do at all?
213 if (fPackagesToActivate
.IsEmpty() && fPackagesToDeactivate
.empty()) {
214 WARN("Bad package activation request: no packages to activate or"
216 throw Exception(B_TRANSACTION_BAD_REQUEST
);
224 CommitTransactionHandler::HandleRequest()
226 for (PackageSet::const_iterator it
= fPackagesAlreadyAdded
.begin();
227 it
!= fPackagesAlreadyAdded
.end(); ++it
) {
228 if (!fPackagesToActivate
.AddItem(*it
))
229 throw std::bad_alloc();
232 fPackagesToDeactivate
= fPackagesAlreadyRemoved
;
239 CommitTransactionHandler::Revert()
241 // move packages to activate back to transaction directory
242 _RevertAddPackagesToActivate();
244 // move packages to deactivate back to packages directory
245 _RevertRemovePackagesToDeactivate();
247 // revert user and group changes
248 _RevertUserGroupChanges();
250 // Revert all other FS operations, i.e. the writable files changes as
251 // well as the creation of the old state directory.
252 fFSTransaction
.RollBack();
257 CommitTransactionHandler::DetachVolumeState()
259 VolumeState
* result
= fVolumeState
;
266 CommitTransactionHandler::_GetPackagesToDeactivate(
267 const BActivationTransaction
& transaction
)
269 // get the number of packages to deactivate
270 const BStringList
& packagesToDeactivate
271 = transaction
.PackagesToDeactivate();
272 int32 packagesToDeactivateCount
= packagesToDeactivate
.CountStrings();
273 if (packagesToDeactivateCount
== 0)
276 for (int32 i
= 0; i
< packagesToDeactivateCount
; i
++) {
277 BString packageName
= packagesToDeactivate
.StringAt(i
);
278 Package
* package
= fVolumeState
->FindPackage(packageName
);
279 if (package
== NULL
) {
280 throw Exception(B_TRANSACTION_NO_SUCH_PACKAGE
)
281 .SetPackageName(packageName
);
284 fPackagesToDeactivate
.insert(package
);
290 CommitTransactionHandler::_ReadPackagesToActivate(
291 const BActivationTransaction
& transaction
)
293 // get the number of packages to activate
294 const BStringList
& packagesToActivate
295 = transaction
.PackagesToActivate();
296 int32 packagesToActivateCount
= packagesToActivate
.CountStrings();
297 if (packagesToActivateCount
== 0)
300 // check the transaction directory name -- we only allow a simple
301 // subdirectory of the admin directory
302 const BString
& transactionDirectoryName
303 = transaction
.TransactionDirectoryName();
304 if (transactionDirectoryName
.IsEmpty()
305 || transactionDirectoryName
.FindFirst('/') >= 0
306 || transactionDirectoryName
== "."
307 || transactionDirectoryName
== "..") {
308 WARN("Bad package activation request: malformed transaction"
309 " directory name: \"%s\"\n", transactionDirectoryName
.String());
310 throw Exception(B_TRANSACTION_BAD_REQUEST
);
313 // open the directory
314 RelativePath
directoryPath(kAdminDirectoryName
,
315 transactionDirectoryName
);
316 BDirectory directory
;
317 status_t error
= _OpenPackagesSubDirectory(directoryPath
, false, directory
);
319 error
= directory
.GetNodeRef(&fTransactionDirectoryRef
);
321 ERROR("Failed to get transaction directory node ref: %s\n",
325 ERROR("Failed to open transaction directory: %s\n", strerror(error
));
328 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY
)
330 FSUtils::Entry(fVolume
->PackagesDirectoryRef(),
331 directoryPath
.ToString()),
332 directoryPath
.ToString()))
333 .SetSystemError(error
);
337 for (int32 i
= 0; i
< packagesToActivateCount
; i
++) {
338 BString packageName
= packagesToActivate
.StringAt(i
);
340 // make sure it doesn't clash with an already existing package
341 Package
* package
= fVolumeState
->FindPackage(packageName
);
342 if (package
!= NULL
) {
343 if (fPackagesAlreadyAdded
.find(package
)
344 != fPackagesAlreadyAdded
.end()) {
345 if (!fPackagesToActivate
.AddItem(package
))
346 throw Exception(B_TRANSACTION_NO_MEMORY
);
350 if (fPackagesToDeactivate
.find(package
)
351 == fPackagesToDeactivate
.end()) {
352 throw Exception(B_TRANSACTION_PACKAGE_ALREADY_EXISTS
)
353 .SetPackageName(packageName
);
358 error
= fPackageFileManager
->CreatePackage(
359 NotOwningEntryRef(fTransactionDirectoryRef
, packageName
),
362 if (error
== B_NO_MEMORY
)
363 throw Exception(B_TRANSACTION_NO_MEMORY
);
364 throw Exception(B_TRANSACTION_FAILED_TO_READ_PACKAGE_FILE
)
365 .SetPackageName(packageName
)
368 NotOwningEntryRef(fTransactionDirectoryRef
,
371 .SetSystemError(error
);
374 if (!fPackagesToActivate
.AddItem(package
)) {
376 throw Exception(B_TRANSACTION_NO_MEMORY
);
383 CommitTransactionHandler::_ApplyChanges()
385 // create an old state directory
386 _CreateOldStateDirectory();
388 // move packages to deactivate to old state directory
389 _RemovePackagesToDeactivate();
391 // move packages to activate to packages directory
392 _AddPackagesToActivate();
394 // activate/deactivate packages
395 _ChangePackageActivation(fAddedPackages
, fRemovedPackages
);
397 if (fVolumeStateIsActive
) {
398 // run post-installation scripts
399 _RunPostInstallScripts();
401 _QueuePostInstallScripts();
404 // removed packages have been deleted, new packages shall not be deleted
405 fAddedPackages
.clear();
406 fRemovedPackages
.clear();
407 fPackagesToActivate
.MakeEmpty(false);
408 fPackagesToDeactivate
.clear();
413 CommitTransactionHandler::_CreateOldStateDirectory()
415 // construct a nice name from the current date and time
416 time_t nowSeconds
= time(NULL
);
419 if (localtime_r(&nowSeconds
, &now
) != NULL
) {
420 baseName
.SetToFormat("state_%d-%02d-%02d_%02d:%02d:%02d",
421 1900 + now
.tm_year
, now
.tm_mon
+ 1, now
.tm_mday
, now
.tm_hour
,
422 now
.tm_min
, now
.tm_sec
);
426 if (baseName
.IsEmpty())
427 throw Exception(B_TRANSACTION_NO_MEMORY
);
429 // make sure the directory doesn't exist yet
430 BDirectory adminDirectory
;
431 status_t error
= _OpenPackagesSubDirectory(
432 RelativePath(kAdminDirectoryName
), true, adminDirectory
);
434 ERROR("Failed to open administrative directory: %s\n", strerror(error
));
435 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY
)
437 FSUtils::Entry(fVolume
->PackagesDirectoryRef(),
438 kAdminDirectoryName
),
439 kAdminDirectoryName
))
440 .SetSystemError(error
);
444 BString directoryName
= baseName
;
445 while (BEntry(&adminDirectory
, directoryName
).Exists()) {
446 directoryName
.SetToFormat("%s-%d", baseName
.String(), uniqueId
++);
447 if (directoryName
.IsEmpty())
448 throw Exception(B_TRANSACTION_NO_MEMORY
);
451 // create the directory
452 FSTransaction::CreateOperation
createOldStateDirectoryOperation(
453 &fFSTransaction
, FSUtils::Entry(adminDirectory
, directoryName
));
455 error
= adminDirectory
.CreateDirectory(directoryName
,
456 &fOldStateDirectory
);
458 createOldStateDirectoryOperation
.Finished();
460 fOldStateDirectoryName
= directoryName
;
462 error
= fOldStateDirectory
.GetNodeRef(&fOldStateDirectoryRef
);
464 ERROR("Failed get old state directory ref: %s\n", strerror(error
));
466 ERROR("Failed to create old state directory: %s\n", strerror(error
));
469 throw Exception(B_TRANSACTION_FAILED_TO_CREATE_DIRECTORY
)
471 FSUtils::Entry(adminDirectory
, directoryName
),
473 .SetSystemError(error
);
476 // write the old activation file
477 BEntry activationFile
;
478 _WriteActivationFile(RelativePath(kAdminDirectoryName
, directoryName
),
479 kActivationFileName
, PackageSet(), PackageSet(), activationFile
);
481 fResult
.SetOldStateDirectory(fOldStateDirectoryName
);
486 CommitTransactionHandler::_RemovePackagesToDeactivate()
488 if (fPackagesToDeactivate
.empty())
491 for (PackageSet::const_iterator it
= fPackagesToDeactivate
.begin();
492 it
!= fPackagesToDeactivate
.end(); ++it
) {
493 Package
* package
= *it
;
495 // When deactivating (or updating) a system package, don't do that live.
496 if (_IsSystemPackage(package
))
497 fVolumeStateIsActive
= false;
499 if (fPackagesAlreadyRemoved
.find(package
)
500 != fPackagesAlreadyRemoved
.end()) {
501 fRemovedPackages
.insert(package
);
505 // get a BEntry for the package
506 NotOwningEntryRef
entryRef(package
->EntryRef());
509 status_t error
= entry
.SetTo(&entryRef
);
511 ERROR("Failed to get package entry for %s: %s\n",
512 package
->FileName().String(), strerror(error
));
513 throw Exception(B_TRANSACTION_FAILED_TO_GET_ENTRY_PATH
)
514 .SetPath1(package
->FileName())
515 .SetPackageName(package
->FileName())
516 .SetSystemError(error
);
520 fRemovedPackages
.insert(package
);
522 error
= entry
.MoveTo(&fOldStateDirectory
);
524 fRemovedPackages
.erase(package
);
525 ERROR("Failed to move old package %s from packages directory: %s\n",
526 package
->FileName().String(), strerror(error
));
527 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE
)
529 _GetPath(FSUtils::Entry(entryRef
), package
->FileName()))
531 FSUtils::Entry(fOldStateDirectory
),
532 fOldStateDirectoryName
))
533 .SetSystemError(error
);
536 fPackageFileManager
->PackageFileMoved(package
->File(),
537 fOldStateDirectoryRef
);
538 package
->File()->IncrementEntryRemovedIgnoreLevel();
544 CommitTransactionHandler::_AddPackagesToActivate()
546 if (fPackagesToActivate
.IsEmpty())
549 // open packages directory
550 BDirectory packagesDirectory
;
552 = packagesDirectory
.SetTo(&fVolume
->PackagesDirectoryRef());
554 ERROR("Failed to open packages directory: %s\n", strerror(error
));
555 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY
)
556 .SetPath1("<packages>")
557 .SetSystemError(error
);
560 int32 count
= fPackagesToActivate
.CountItems();
561 for (int32 i
= 0; i
< count
; i
++) {
562 Package
* package
= fPackagesToActivate
.ItemAt(i
);
563 if (fPackagesAlreadyAdded
.find(package
)
564 != fPackagesAlreadyAdded
.end()) {
565 fAddedPackages
.insert(package
);
566 _PreparePackageToActivate(package
);
570 // get a BEntry for the package
571 NotOwningEntryRef
entryRef(fTransactionDirectoryRef
,
572 package
->FileName());
574 error
= entry
.SetTo(&entryRef
);
576 ERROR("Failed to get package entry for %s: %s\n",
577 package
->FileName().String(), strerror(error
));
578 throw Exception(B_TRANSACTION_FAILED_TO_GET_ENTRY_PATH
)
579 .SetPath1(package
->FileName())
580 .SetPackageName(package
->FileName())
581 .SetSystemError(error
);
585 fAddedPackages
.insert(package
);
587 error
= entry
.MoveTo(&packagesDirectory
);
589 fAddedPackages
.erase(package
);
590 ERROR("Failed to move new package %s to packages directory: %s\n",
591 package
->FileName().String(), strerror(error
));
592 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE
)
594 _GetPath(FSUtils::Entry(entryRef
), package
->FileName()))
596 FSUtils::Entry(packagesDirectory
),
598 .SetSystemError(error
);
601 fPackageFileManager
->PackageFileMoved(package
->File(),
602 fVolume
->PackagesDirectoryRef());
603 package
->File()->IncrementEntryCreatedIgnoreLevel();
605 // also add the package to the volume
606 fVolumeState
->AddPackage(package
);
608 _PreparePackageToActivate(package
);
614 CommitTransactionHandler::_PreparePackageToActivate(Package
* package
)
616 fCurrentPackage
= package
;
619 const BStringList
& groups
= package
->Info().Groups();
620 int32 count
= groups
.CountStrings();
621 for (int32 i
= 0; i
< count
; i
++)
622 _AddGroup(package
, groups
.StringAt(i
));
625 const BObjectList
<BUser
>& users
= package
->Info().Users();
626 for (int32 i
= 0; const BUser
* user
= users
.ItemAt(i
); i
++)
627 _AddUser(package
, *user
);
629 // handle global writable files
630 _AddGlobalWritableFiles(package
);
632 fCurrentPackage
= NULL
;
637 CommitTransactionHandler::_AddGroup(Package
* package
, const BString
& groupName
)
639 // Check whether the group already exists.
641 struct group groupBuffer
;
642 struct group
* groupFound
;
643 int error
= getgrnam_r(groupName
, &groupBuffer
, buffer
, sizeof(buffer
),
645 if ((error
== 0 && groupFound
!= NULL
) || error
== ERANGE
)
649 fAddedGroups
.insert(groupName
.String());
651 std::string
commandLine("groupadd ");
652 commandLine
+= FSUtils::ShellEscapeString(groupName
).String();
654 if (system(commandLine
.c_str()) != 0) {
655 fAddedGroups
.erase(groupName
.String());
656 ERROR("Failed to add group \"%s\".\n", groupName
.String());
657 throw Exception(B_TRANSACTION_FAILED_TO_ADD_GROUP
)
658 .SetPackageName(package
->FileName())
659 .SetString1(groupName
);
665 CommitTransactionHandler::_AddUser(Package
* package
, const BUser
& user
)
667 // Check whether the user already exists.
669 struct passwd passwdBuffer
;
670 struct passwd
* passwdFound
;
671 int error
= getpwnam_r(user
.Name(), &passwdBuffer
, buffer
,
672 sizeof(buffer
), &passwdFound
);
673 if ((error
== 0 && passwdFound
!= NULL
) || error
== ERANGE
)
677 fAddedUsers
.insert(user
.Name().String());
679 std::string
commandLine("useradd ");
681 if (!user
.RealName().IsEmpty()) {
682 commandLine
+= std::string("-n ")
683 + FSUtils::ShellEscapeString(user
.RealName()).String() + " ";
686 if (!user
.Home().IsEmpty()) {
687 commandLine
+= std::string("-d ")
688 + FSUtils::ShellEscapeString(user
.Home()).String() + " ";
691 if (!user
.Shell().IsEmpty()) {
692 commandLine
+= std::string("-s ")
693 + FSUtils::ShellEscapeString(user
.Shell()).String() + " ";
696 if (!user
.Groups().IsEmpty()) {
697 commandLine
+= std::string("-g ")
698 + FSUtils::ShellEscapeString(user
.Groups().First()).String()
702 commandLine
+= FSUtils::ShellEscapeString(user
.Name()).String();
704 if (system(commandLine
.c_str()) != 0) {
705 fAddedUsers
.erase(user
.Name().String());
706 ERROR("Failed to add user \"%s\".\n", user
.Name().String());
707 throw Exception(B_TRANSACTION_FAILED_TO_ADD_USER
)
708 .SetPackageName(package
->FileName())
709 .SetString1(user
.Name());
713 // add the supplementary groups
714 int32 groupCount
= user
.Groups().CountStrings();
715 for (int32 i
= 1; i
< groupCount
; i
++) {
716 commandLine
= std::string("groupmod -A ")
717 + FSUtils::ShellEscapeString(user
.Name()).String()
719 + FSUtils::ShellEscapeString(user
.Groups().StringAt(i
))
721 if (system(commandLine
.c_str()) != 0) {
722 fAddedUsers
.erase(user
.Name().String());
723 ERROR("Failed to add user \"%s\" to group \"%s\".\n",
724 user
.Name().String(), user
.Groups().StringAt(i
).String());
725 throw Exception(B_TRANSACTION_FAILED_TO_ADD_USER_TO_GROUP
)
726 .SetPackageName(package
->FileName())
727 .SetString1(user
.Name())
728 .SetString2(user
.Groups().StringAt(i
));
735 CommitTransactionHandler::_AddGlobalWritableFiles(Package
* package
)
737 // get the list of included files
738 const BObjectList
<BGlobalWritableFileInfo
>& files
739 = package
->Info().GlobalWritableFileInfos();
740 BStringList contentPaths
;
741 for (int32 i
= 0; const BGlobalWritableFileInfo
* file
= files
.ItemAt(i
);
743 if (file
->IsIncluded() && !contentPaths
.Add(file
->Path()))
744 throw std::bad_alloc();
747 if (contentPaths
.IsEmpty())
750 // Open the root directory of the installation location where we will
751 // extract the files -- that's the volume's root directory.
752 BDirectory rootDirectory
;
753 status_t error
= rootDirectory
.SetTo(&fVolume
->RootDirectoryRef());
755 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY
)
757 FSUtils::Entry(fVolume
->RootDirectoryRef()),
759 .SetSystemError(error
);
762 // Open writable-files directory in the administrative directory.
763 if (fWritableFilesDirectory
.InitCheck() != B_OK
) {
764 RelativePath
directoryPath(kAdminDirectoryName
,
765 kWritableFilesDirectoryName
);
766 error
= _OpenPackagesSubDirectory(directoryPath
, true,
767 fWritableFilesDirectory
);
770 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY
)
772 FSUtils::Entry(fVolume
->PackagesDirectoryRef(),
773 directoryPath
.ToString()),
774 directoryPath
.ToString()))
775 .SetPackageName(package
->FileName())
776 .SetSystemError(error
);
780 // extract files into a subdir of the writable-files directory
781 BDirectory extractedFilesDirectory
;
782 _ExtractPackageContent(package
, contentPaths
,
783 fWritableFilesDirectory
, extractedFilesDirectory
);
785 for (int32 i
= 0; const BGlobalWritableFileInfo
* file
= files
.ItemAt(i
);
787 if (file
->IsIncluded()) {
788 _AddGlobalWritableFile(package
, *file
, rootDirectory
,
789 extractedFilesDirectory
);
796 CommitTransactionHandler::_AddGlobalWritableFile(Package
* package
,
797 const BGlobalWritableFileInfo
& file
, const BDirectory
& rootDirectory
,
798 const BDirectory
& extractedFilesDirectory
)
800 // Map the path name to the actual target location. Currently this only
801 // concerns "settings/", which is mapped to "settings/global/".
802 BString
targetPath(file
.Path());
803 if (fVolume
->MountType() == PACKAGE_FS_MOUNT_TYPE_HOME
) {
804 if (targetPath
== "settings"
805 || targetPath
.StartsWith("settings/")) {
806 targetPath
.Insert("/global", 8);
807 if (targetPath
.Length() == file
.Path().Length())
808 throw std::bad_alloc();
812 // open parent directory of the source entry
813 const char* lastSlash
= strrchr(file
.Path(), '/');
814 const BDirectory
* sourceDirectory
;
815 BDirectory stackSourceDirectory
;
816 if (lastSlash
!= NULL
) {
817 sourceDirectory
= &stackSourceDirectory
;
818 BString
sourceParentPath(file
.Path(),
819 lastSlash
- file
.Path().String());
820 if (sourceParentPath
.Length() == 0)
821 throw std::bad_alloc();
823 status_t error
= stackSourceDirectory
.SetTo(
824 &extractedFilesDirectory
, sourceParentPath
);
826 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY
)
828 FSUtils::Entry(extractedFilesDirectory
, sourceParentPath
),
830 .SetPackageName(package
->FileName())
831 .SetSystemError(error
);
834 sourceDirectory
= &extractedFilesDirectory
;
837 // open parent directory of the target entry -- create, if necessary
838 FSUtils::Path
relativeSourcePath(file
.Path());
839 lastSlash
= strrchr(targetPath
, '/');
840 if (lastSlash
!= NULL
) {
841 BString
targetParentPath(targetPath
,
842 lastSlash
- targetPath
.String());
843 if (targetParentPath
.Length() == 0)
844 throw std::bad_alloc();
846 BDirectory targetDirectory
;
847 status_t error
= FSUtils::OpenSubDirectory(rootDirectory
,
848 RelativePath(targetParentPath
), true, targetDirectory
);
850 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY
)
852 FSUtils::Entry(rootDirectory
, targetParentPath
),
854 .SetPackageName(package
->FileName())
855 .SetSystemError(error
);
857 _AddGlobalWritableFileRecurse(package
, *sourceDirectory
,
858 relativeSourcePath
, targetDirectory
, lastSlash
+ 1,
861 _AddGlobalWritableFileRecurse(package
, *sourceDirectory
,
862 relativeSourcePath
, rootDirectory
, targetPath
,
869 CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package
* package
,
870 const BDirectory
& sourceDirectory
, FSUtils::Path
& relativeSourcePath
,
871 const BDirectory
& targetDirectory
, const char* targetName
,
872 BWritableFileUpdateType updateType
)
874 // * If the file doesn't exist, just copy the extracted one.
875 // * If the file does exist, compare with the previous original version:
876 // * If unchanged, just overwrite it.
877 // * If changed, leave it to the user for now. When we support merging
878 // first back the file up, then try the merge.
880 // Check whether the target location exists and what type the entry at
881 // both locations are.
882 struct stat targetStat
;
883 if (targetDirectory
.GetStatFor(targetName
, &targetStat
) != B_OK
) {
884 // target doesn't exist -- just copy
885 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
886 "couldn't get stat for writable file, copying...\n");
887 FSTransaction::CreateOperation
copyOperation(&fFSTransaction
,
888 FSUtils::Entry(targetDirectory
, targetName
));
889 status_t error
= BCopyEngine(BCopyEngine::COPY_RECURSIVELY
)
891 FSUtils::Entry(sourceDirectory
, relativeSourcePath
.Leaf()),
892 FSUtils::Entry(targetDirectory
, targetName
));
894 if (targetDirectory
.GetStatFor(targetName
, &targetStat
) == B_OK
)
895 copyOperation
.Finished();
897 throw Exception(B_TRANSACTION_FAILED_TO_COPY_FILE
)
899 FSUtils::Entry(sourceDirectory
,
900 relativeSourcePath
.Leaf()),
903 FSUtils::Entry(targetDirectory
, targetName
),
905 .SetSystemError(error
);
907 copyOperation
.Finished();
911 struct stat sourceStat
;
912 status_t error
= sourceDirectory
.GetStatFor(relativeSourcePath
.Leaf(),
915 throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY
)
917 FSUtils::Entry(sourceDirectory
,
918 relativeSourcePath
.Leaf()),
920 .SetSystemError(error
);
923 if ((sourceStat
.st_mode
& S_IFMT
) != (targetStat
.st_mode
& S_IFMT
)
924 || (!S_ISDIR(sourceStat
.st_mode
) && !S_ISREG(sourceStat
.st_mode
)
925 && !S_ISLNK(sourceStat
.st_mode
))) {
926 // Source and target entry types don't match or this is an entry
927 // we cannot handle. The user must handle this manually.
928 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
929 "writable file exists, but type doesn't match previous type\n");
930 _AddIssue(TransactionIssueBuilder(
931 BTransactionIssue::B_WRITABLE_FILE_TYPE_MISMATCH
)
932 .SetPath1(FSUtils::Entry(targetDirectory
, targetName
))
933 .SetPath2(FSUtils::Entry(sourceDirectory
,
934 relativeSourcePath
.Leaf())));
938 if (S_ISDIR(sourceStat
.st_mode
)) {
939 // entry is a directory -- recurse
940 BDirectory sourceSubDirectory
;
941 error
= sourceSubDirectory
.SetTo(&sourceDirectory
,
942 relativeSourcePath
.Leaf());
944 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY
)
946 FSUtils::Entry(sourceDirectory
,
947 relativeSourcePath
.Leaf()),
949 .SetPackageName(package
->FileName())
950 .SetSystemError(error
);
953 BDirectory targetSubDirectory
;
954 error
= targetSubDirectory
.SetTo(&targetDirectory
, targetName
);
956 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY
)
958 FSUtils::Entry(targetDirectory
, targetName
),
960 .SetPackageName(package
->FileName())
961 .SetSystemError(error
);
965 while (sourceSubDirectory
.GetNextRef(&entry
) == B_OK
) {
966 relativeSourcePath
.AppendComponent(entry
.name
);
967 _AddGlobalWritableFileRecurse(package
, sourceSubDirectory
,
968 relativeSourcePath
, targetSubDirectory
, entry
.name
,
970 relativeSourcePath
.RemoveLastComponent();
973 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
974 "writable directory, recursion done\n");
978 // get the package the target file originated from
979 BString originalPackage
;
980 if (BNode(&targetDirectory
, targetName
).ReadAttrString(
981 kPackageFileAttribute
, &originalPackage
) != B_OK
) {
982 // Can't determine the original package. The user must handle this
984 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
985 "failed to get SYS:PACKAGE attribute\n");
986 if (updateType
!= B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD
) {
987 _AddIssue(TransactionIssueBuilder(
988 BTransactionIssue::B_WRITABLE_FILE_NO_PACKAGE_ATTRIBUTE
)
989 .SetPath1(FSUtils::Entry(targetDirectory
, targetName
)));
994 // If that's our package, we're happy.
995 if (originalPackage
== package
->RevisionedNameThrows()) {
996 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
997 "file tagged with same package version we're activating\n");
1001 // Check, whether the writable-files directory for the original package
1003 BString originalRelativeSourcePath
= BString().SetToFormat("%s/%s",
1004 originalPackage
.String(), relativeSourcePath
.ToCString());
1005 if (originalRelativeSourcePath
.IsEmpty())
1006 throw std::bad_alloc();
1008 struct stat originalPackageStat
;
1009 error
= fWritableFilesDirectory
.GetStatFor(originalRelativeSourcePath
,
1010 &originalPackageStat
);
1012 || (sourceStat
.st_mode
& S_IFMT
)
1013 != (originalPackageStat
.st_mode
& S_IFMT
)) {
1014 // Original entry doesn't exist (either we don't have the data from
1015 // the original package or the entry really didn't exist) or its
1016 // type differs from the expected one. The user must handle this
1018 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
1019 "original \"%s\" doesn't exist or has other type\n",
1020 _GetPath(FSUtils::Entry(fWritableFilesDirectory
,
1021 originalRelativeSourcePath
),
1022 originalRelativeSourcePath
).String());
1023 if (error
!= B_OK
) {
1024 _AddIssue(TransactionIssueBuilder(
1026 ::B_WRITABLE_FILE_OLD_ORIGINAL_FILE_MISSING
)
1027 .SetPath1(FSUtils::Entry(targetDirectory
, targetName
))
1028 .SetPath2(FSUtils::Entry(fWritableFilesDirectory
,
1029 originalRelativeSourcePath
)));
1031 _AddIssue(TransactionIssueBuilder(
1033 ::B_WRITABLE_FILE_OLD_ORIGINAL_FILE_TYPE_MISMATCH
)
1034 .SetPath1(FSUtils::Entry(targetDirectory
, targetName
))
1035 .SetPath2(FSUtils::Entry(fWritableFilesDirectory
,
1036 originalRelativeSourcePath
)));
1041 if (S_ISREG(sourceStat
.st_mode
)) {
1042 // compare file content
1044 error
= FSUtils::CompareFileContent(
1045 FSUtils::Entry(fWritableFilesDirectory
,
1046 originalRelativeSourcePath
),
1047 FSUtils::Entry(targetDirectory
, targetName
),
1049 // TODO: Merge support!
1050 if (error
!= B_OK
|| !equal
) {
1051 // The comparison failed or the files differ. The user must
1052 // handle this manually.
1053 PRINT("Volume::CommitTransactionHandler::"
1054 "_AddGlobalWritableFile(): "
1055 "file comparison failed (%s) or files aren't equal\n",
1057 if (updateType
!= B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD
) {
1058 if (error
!= B_OK
) {
1059 _AddIssue(TransactionIssueBuilder(
1061 ::B_WRITABLE_FILE_COMPARISON_FAILED
)
1062 .SetPath1(FSUtils::Entry(targetDirectory
, targetName
))
1063 .SetPath2(FSUtils::Entry(fWritableFilesDirectory
,
1064 originalRelativeSourcePath
))
1065 .SetSystemError(error
));
1067 _AddIssue(TransactionIssueBuilder(
1069 ::B_WRITABLE_FILE_NOT_EQUAL
)
1070 .SetPath1(FSUtils::Entry(targetDirectory
, targetName
))
1071 .SetPath2(FSUtils::Entry(fWritableFilesDirectory
,
1072 originalRelativeSourcePath
)));
1080 error
= FSUtils::CompareSymLinks(
1081 FSUtils::Entry(fWritableFilesDirectory
,
1082 originalRelativeSourcePath
),
1083 FSUtils::Entry(targetDirectory
, targetName
),
1085 if (error
!= B_OK
|| !equal
) {
1086 // The comparison failed or the symlinks differ. The user must
1087 // handle this manually.
1088 PRINT("Volume::CommitTransactionHandler::"
1089 "_AddGlobalWritableFile(): "
1090 "symlink comparison failed (%s) or symlinks aren't equal\n",
1092 if (updateType
!= B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD
) {
1093 if (error
!= B_OK
) {
1094 _AddIssue(TransactionIssueBuilder(
1096 ::B_WRITABLE_SYMLINK_COMPARISON_FAILED
)
1097 .SetPath1(FSUtils::Entry(targetDirectory
, targetName
))
1098 .SetPath2(FSUtils::Entry(fWritableFilesDirectory
,
1099 originalRelativeSourcePath
))
1100 .SetSystemError(error
));
1102 _AddIssue(TransactionIssueBuilder(
1104 ::B_WRITABLE_SYMLINK_NOT_EQUAL
)
1105 .SetPath1(FSUtils::Entry(targetDirectory
, targetName
))
1106 .SetPath2(FSUtils::Entry(fWritableFilesDirectory
,
1107 originalRelativeSourcePath
)));
1114 // Replace the existing file/symlink. We do that in two steps: First
1115 // copy the new file to a neighoring location, then move-replace the
1117 BString tempTargetName
;
1118 tempTargetName
.SetToFormat("%s.%s", targetName
,
1119 package
->RevisionedNameThrows().String());
1120 if (tempTargetName
.IsEmpty())
1121 throw std::bad_alloc();
1124 FSTransaction::CreateOperation
copyOperation(&fFSTransaction
,
1125 FSUtils::Entry(targetDirectory
, tempTargetName
));
1127 error
= BCopyEngine(BCopyEngine::UNLINK_DESTINATION
).CopyEntry(
1128 FSUtils::Entry(sourceDirectory
, relativeSourcePath
.Leaf()),
1129 FSUtils::Entry(targetDirectory
, tempTargetName
));
1130 if (error
!= B_OK
) {
1131 throw Exception(B_TRANSACTION_FAILED_TO_COPY_FILE
)
1133 FSUtils::Entry(sourceDirectory
,
1134 relativeSourcePath
.Leaf()),
1135 relativeSourcePath
))
1137 FSUtils::Entry(targetDirectory
, tempTargetName
),
1139 .SetSystemError(error
);
1142 copyOperation
.Finished();
1145 FSTransaction::RemoveOperation
renameOperation(&fFSTransaction
,
1146 FSUtils::Entry(targetDirectory
, targetName
),
1147 FSUtils::Entry(fWritableFilesDirectory
,
1148 originalRelativeSourcePath
));
1151 error
= targetEntry
.SetTo(&targetDirectory
, tempTargetName
);
1153 error
= targetEntry
.Rename(targetName
, true);
1154 if (error
!= B_OK
) {
1155 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE
)
1157 FSUtils::Entry(targetDirectory
, tempTargetName
),
1159 .SetPath2(targetName
)
1160 .SetSystemError(error
);
1163 renameOperation
.Finished();
1164 copyOperation
.Unregister();
1169 CommitTransactionHandler::_RevertAddPackagesToActivate()
1171 if (fAddedPackages
.empty())
1174 // open transaction directory
1175 BDirectory transactionDirectory
;
1176 status_t error
= transactionDirectory
.SetTo(&fTransactionDirectoryRef
);
1177 if (error
!= B_OK
) {
1178 ERROR("failed to open transaction directory: %s\n",
1182 for (PackageSet::iterator it
= fAddedPackages
.begin();
1183 it
!= fAddedPackages
.end(); ++it
) {
1184 // remove package from the volume
1185 Package
* package
= *it
;
1187 if (fPackagesAlreadyAdded
.find(package
)
1188 != fPackagesAlreadyAdded
.end()) {
1192 fVolumeState
->RemovePackage(package
);
1194 if (transactionDirectory
.InitCheck() != B_OK
)
1197 // get BEntry for the package
1198 NotOwningEntryRef
entryRef(package
->EntryRef());
1200 error
= entry
.SetTo(&entryRef
);
1201 if (error
!= B_OK
) {
1202 ERROR("failed to get entry for package \"%s\": %s\n",
1203 package
->FileName().String(), strerror(error
));
1208 error
= entry
.MoveTo(&transactionDirectory
);
1209 if (error
!= B_OK
) {
1210 ERROR("failed to move new package \"%s\" back to transaction "
1211 "directory: %s\n", package
->FileName().String(),
1216 fPackageFileManager
->PackageFileMoved(package
->File(),
1217 fTransactionDirectoryRef
);
1218 package
->File()->IncrementEntryRemovedIgnoreLevel();
1224 CommitTransactionHandler::_RevertRemovePackagesToDeactivate()
1226 if (fRemovedPackages
.empty())
1229 // open packages directory
1230 BDirectory packagesDirectory
;
1232 = packagesDirectory
.SetTo(&fVolume
->PackagesDirectoryRef());
1233 if (error
!= B_OK
) {
1234 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY
)
1235 .SetPath1("<packages>")
1236 .SetSystemError(error
);
1239 for (PackageSet::iterator it
= fRemovedPackages
.begin();
1240 it
!= fRemovedPackages
.end(); ++it
) {
1241 Package
* package
= *it
;
1242 if (fPackagesAlreadyRemoved
.find(package
)
1243 != fPackagesAlreadyRemoved
.end()) {
1247 // get a BEntry for the package
1249 status_t error
= entry
.SetTo(&fOldStateDirectory
,
1250 package
->FileName());
1251 if (error
!= B_OK
) {
1252 ERROR("failed to get entry for package \"%s\": %s\n",
1253 package
->FileName().String(), strerror(error
));
1258 error
= entry
.MoveTo(&packagesDirectory
);
1259 if (error
!= B_OK
) {
1260 ERROR("failed to move old package \"%s\" back to packages "
1261 "directory: %s\n", package
->FileName().String(),
1266 fPackageFileManager
->PackageFileMoved(package
->File(),
1267 fVolume
->PackagesDirectoryRef());
1268 package
->File()->IncrementEntryCreatedIgnoreLevel();
1274 CommitTransactionHandler::_RevertUserGroupChanges()
1277 for (StringSet::const_iterator it
= fAddedUsers
.begin();
1278 it
!= fAddedUsers
.end(); ++it
) {
1279 std::string
commandLine("userdel ");
1280 commandLine
+= FSUtils::ShellEscapeString(it
->c_str()).String();
1281 if (system(commandLine
.c_str()) != 0)
1282 ERROR("failed to remove user \"%s\"\n", it
->c_str());
1286 for (StringSet::const_iterator it
= fAddedGroups
.begin();
1287 it
!= fAddedGroups
.end(); ++it
) {
1288 std::string
commandLine("groupdel ");
1289 commandLine
+= FSUtils::ShellEscapeString(it
->c_str()).String();
1290 if (system(commandLine
.c_str()) != 0)
1291 ERROR("failed to remove group \"%s\"\n", it
->c_str());
1297 CommitTransactionHandler::_RunPostInstallScripts()
1299 for (PackageSet::iterator it
= fAddedPackages
.begin();
1300 it
!= fAddedPackages
.end(); ++it
) {
1301 Package
* package
= *it
;
1302 fCurrentPackage
= package
;
1303 const BStringList
& scripts
= package
->Info().PostInstallScripts();
1304 int32 count
= scripts
.CountStrings();
1305 for (int32 i
= 0; i
< count
; i
++)
1306 _RunPostInstallScript(package
, scripts
.StringAt(i
));
1309 fCurrentPackage
= NULL
;
1314 CommitTransactionHandler::_RunPostInstallScript(Package
* package
,
1315 const BString
& script
)
1317 BDirectory
rootDir(&fVolume
->RootDirectoryRef());
1318 BPath
scriptPath(&rootDir
, script
);
1319 status_t error
= scriptPath
.InitCheck();
1320 if (error
!= B_OK
) {
1321 ERROR("Volume::CommitTransactionHandler::_RunPostInstallScript(): "
1322 "failed get path of post-installation script \"%s\" of package "
1323 "%s: %s\n", script
.String(), package
->FileName().String(),
1325 _AddIssue(TransactionIssueBuilder(
1326 BTransactionIssue::B_POST_INSTALL_SCRIPT_NOT_FOUND
)
1328 .SetSystemError(error
));
1333 int result
= system(scriptPath
.Path());
1335 ERROR("Volume::CommitTransactionHandler::_RunPostInstallScript(): "
1336 "running post-installation script \"%s\" of package %s "
1337 "failed: %d (errno: %s)\n", script
.String(),
1338 package
->FileName().String(), result
,
1340 if (result
< 0 && errno
!= 0) {
1341 _AddIssue(TransactionIssueBuilder(
1342 BTransactionIssue::B_POST_INSTALL_SCRIPT_FAILED
)
1343 .SetPath1(BString(scriptPath
.Path()))
1344 .SetSystemError(errno
));
1346 _AddIssue(TransactionIssueBuilder(
1347 BTransactionIssue::B_STARTING_POST_INSTALL_SCRIPT_FAILED
)
1348 .SetPath1(BString(scriptPath
.Path()))
1349 .SetExitCode(result
));
1356 CommitTransactionHandler::_QueuePostInstallScripts()
1358 BDirectory adminDirectory
;
1359 status_t error
= _OpenPackagesSubDirectory(
1360 RelativePath(kAdminDirectoryName
), true, adminDirectory
);
1361 if (error
!= B_OK
) {
1362 ERROR("Failed to open administrative directory: %s\n", strerror(error
));
1366 BDirectory scriptsDirectory
;
1367 error
= scriptsDirectory
.SetTo(&adminDirectory
, kQueuedScriptsDirectoryName
);
1368 if (error
== B_ENTRY_NOT_FOUND
)
1369 error
= adminDirectory
.CreateDirectory(kQueuedScriptsDirectoryName
, &scriptsDirectory
);
1370 if (error
!= B_OK
) {
1371 ERROR("Failed to open queued scripts directory: %s\n", strerror(error
));
1375 BDirectory
rootDir(&fVolume
->RootDirectoryRef());
1376 for (PackageSet::iterator it
= fAddedPackages
.begin();
1377 it
!= fAddedPackages
.end(); ++it
) {
1378 Package
* package
= *it
;
1379 const BStringList
& scripts
= package
->Info().PostInstallScripts();
1380 for (int32 i
= 0; i
< scripts
.CountStrings(); ++i
) {
1381 BPath
scriptPath(&rootDir
, scripts
.StringAt(i
));
1382 status_t error
= scriptPath
.InitCheck();
1383 if (error
!= B_OK
) {
1384 ERROR("Can't find script: %s\n", scripts
.StringAt(i
).String());
1388 // symlink to the script
1389 BSymLink scriptLink
;
1390 scriptsDirectory
.CreateSymLink(scriptPath
.Leaf(),
1391 scriptPath
.Path(), &scriptLink
);
1392 if (scriptLink
.InitCheck() != B_OK
) {
1393 ERROR("Creating symlink failed: %s\n", strerror(scriptLink
.InitCheck()));
1402 CommitTransactionHandler::_ExtractPackageContent(Package
* package
,
1403 const BStringList
& contentPaths
, BDirectory
& targetDirectory
,
1404 BDirectory
& _extractedFilesDirectory
)
1406 // check whether the subdirectory already exists
1407 BString
targetName(package
->RevisionedNameThrows());
1410 status_t error
= targetEntry
.SetTo(&targetDirectory
, targetName
);
1411 if (error
!= B_OK
) {
1412 throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY
)
1414 FSUtils::Entry(targetDirectory
, targetName
),
1416 .SetPackageName(package
->FileName())
1417 .SetSystemError(error
);
1419 if (targetEntry
.Exists()) {
1420 // nothing to do -- the very same version of the package has already
1422 error
= _extractedFilesDirectory
.SetTo(&targetDirectory
,
1424 if (error
!= B_OK
) {
1425 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY
)
1427 FSUtils::Entry(targetDirectory
, targetName
),
1429 .SetPackageName(package
->FileName())
1430 .SetSystemError(error
);
1435 // create the subdirectory with a temporary name (remove, if it already
1437 BString temporaryTargetName
= BString().SetToFormat("%s.tmp",
1438 targetName
.String());
1439 if (temporaryTargetName
.IsEmpty())
1440 throw std::bad_alloc();
1442 error
= targetEntry
.SetTo(&targetDirectory
, temporaryTargetName
);
1443 if (error
!= B_OK
) {
1444 throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY
)
1446 FSUtils::Entry(targetDirectory
, temporaryTargetName
),
1447 temporaryTargetName
))
1448 .SetPackageName(package
->FileName())
1449 .SetSystemError(error
);
1452 if (targetEntry
.Exists()) {
1453 // remove pre-existing
1454 error
= BRemoveEngine().RemoveEntry(FSUtils::Entry(targetEntry
));
1455 if (error
!= B_OK
) {
1456 throw Exception(B_TRANSACTION_FAILED_TO_REMOVE_DIRECTORY
)
1458 FSUtils::Entry(targetDirectory
, temporaryTargetName
),
1459 temporaryTargetName
))
1460 .SetPackageName(package
->FileName())
1461 .SetSystemError(error
);
1465 BDirectory
& subDirectory
= _extractedFilesDirectory
;
1466 FSTransaction::CreateOperation
createSubDirectoryOperation(
1468 FSUtils::Entry(targetDirectory
, temporaryTargetName
));
1469 error
= targetDirectory
.CreateDirectory(temporaryTargetName
,
1471 if (error
!= B_OK
) {
1472 throw Exception(B_TRANSACTION_FAILED_TO_CREATE_DIRECTORY
)
1474 FSUtils::Entry(targetDirectory
, temporaryTargetName
),
1475 temporaryTargetName
))
1476 .SetPackageName(package
->FileName())
1477 .SetSystemError(error
);
1480 createSubDirectoryOperation
.Finished();
1483 NotOwningEntryRef
packageRef(package
->EntryRef());
1485 int32 contentPathCount
= contentPaths
.CountStrings();
1486 for (int32 i
= 0; i
< contentPathCount
; i
++) {
1487 const char* contentPath
= contentPaths
.StringAt(i
);
1489 error
= FSUtils::ExtractPackageContent(FSUtils::Entry(packageRef
),
1490 contentPath
, FSUtils::Entry(subDirectory
));
1491 if (error
!= B_OK
) {
1492 throw Exception(B_TRANSACTION_FAILED_TO_EXTRACT_PACKAGE_FILE
)
1493 .SetPath1(contentPath
)
1494 .SetPackageName(package
->FileName())
1495 .SetSystemError(error
);
1499 // tag all entries with the package attribute
1500 _TagPackageEntriesRecursively(subDirectory
, targetName
, true);
1502 // rename the subdirectory
1503 error
= targetEntry
.Rename(targetName
);
1504 if (error
!= B_OK
) {
1505 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE
)
1507 FSUtils::Entry(targetDirectory
, temporaryTargetName
),
1508 temporaryTargetName
))
1509 .SetPath2(targetName
)
1510 .SetPackageName(package
->FileName())
1511 .SetSystemError(error
);
1514 // keep the directory, regardless of whether the transaction is rolled
1516 createSubDirectoryOperation
.Unregister();
1521 CommitTransactionHandler::_OpenPackagesSubDirectory(const RelativePath
& path
,
1522 bool create
, BDirectory
& _directory
)
1524 // open the packages directory
1525 BDirectory directory
;
1526 status_t error
= directory
.SetTo(&fVolume
->PackagesDirectoryRef());
1527 if (error
!= B_OK
) {
1528 ERROR("CommitTransactionHandler::_OpenPackagesSubDirectory(): failed "
1529 "to open packages directory: %s\n", strerror(error
));
1530 RETURN_ERROR(error
);
1533 return FSUtils::OpenSubDirectory(directory
, path
, create
, _directory
);
1538 CommitTransactionHandler::_OpenPackagesFile(
1539 const RelativePath
& subDirectoryPath
, const char* fileName
, uint32 openMode
,
1540 BFile
& _file
, BEntry
* _entry
)
1542 BDirectory directory
;
1543 if (!subDirectoryPath
.IsEmpty()) {
1544 status_t error
= _OpenPackagesSubDirectory(subDirectoryPath
,
1545 (openMode
& B_CREATE_FILE
) != 0, directory
);
1546 if (error
!= B_OK
) {
1547 ERROR("CommitTransactionHandler::_OpenPackagesFile(): failed to "
1548 "open packages subdirectory \"%s\": %s\n",
1549 subDirectoryPath
.ToString().String(), strerror(error
));
1550 RETURN_ERROR(error
);
1553 status_t error
= directory
.SetTo(&fVolume
->PackagesDirectoryRef());
1554 if (error
!= B_OK
) {
1555 ERROR("CommitTransactionHandler::_OpenPackagesFile(): failed to "
1556 "open packages directory: %s\n", strerror(error
));
1557 RETURN_ERROR(error
);
1562 BEntry
& entry
= _entry
!= NULL
? *_entry
: stackEntry
;
1563 status_t error
= entry
.SetTo(&directory
, fileName
);
1564 if (error
!= B_OK
) {
1565 ERROR("CommitTransactionHandler::_OpenPackagesFile(): failed to get "
1566 "entry for file: %s", strerror(error
));
1567 RETURN_ERROR(error
);
1570 return _file
.SetTo(&entry
, openMode
);
1575 CommitTransactionHandler::_WriteActivationFile(
1576 const RelativePath
& directoryPath
, const char* fileName
,
1577 const PackageSet
& toActivate
, const PackageSet
& toDeactivate
,
1580 // create the content
1581 BString activationFileContent
;
1582 _CreateActivationFileContent(toActivate
, toDeactivate
,
1583 activationFileContent
);
1586 status_t error
= _WriteTextFile(directoryPath
, fileName
,
1587 activationFileContent
, _entry
);
1588 if (error
!= B_OK
) {
1589 BString filePath
= directoryPath
.ToString() << '/' << fileName
;
1590 throw Exception(B_TRANSACTION_FAILED_TO_WRITE_ACTIVATION_FILE
)
1592 FSUtils::Entry(fVolume
->PackagesDirectoryRef(), filePath
),
1594 .SetSystemError(error
);
1600 CommitTransactionHandler::_CreateActivationFileContent(
1601 const PackageSet
& toActivate
, const PackageSet
& toDeactivate
,
1604 BString activationFileContent
;
1605 for (PackageFileNameHashTable::Iterator it
1606 = fVolumeState
->ByFileNameIterator();
1607 Package
* package
= it
.Next();) {
1608 if (package
->IsActive()
1609 && toDeactivate
.find(package
) == toDeactivate
.end()) {
1610 int32 length
= activationFileContent
.Length();
1611 activationFileContent
<< package
->FileName() << '\n';
1612 if (activationFileContent
.Length()
1613 < length
+ package
->FileName().Length() + 1) {
1614 throw Exception(B_TRANSACTION_NO_MEMORY
);
1619 for (PackageSet::const_iterator it
= toActivate
.begin();
1620 it
!= toActivate
.end(); ++it
) {
1621 Package
* package
= *it
;
1622 int32 length
= activationFileContent
.Length();
1623 activationFileContent
<< package
->FileName() << '\n';
1624 if (activationFileContent
.Length()
1625 < length
+ package
->FileName().Length() + 1) {
1626 throw Exception(B_TRANSACTION_NO_MEMORY
);
1630 _content
= activationFileContent
;
1635 CommitTransactionHandler::_WriteTextFile(const RelativePath
& directoryPath
,
1636 const char* fileName
, const BString
& content
, BEntry
& _entry
)
1639 status_t error
= _OpenPackagesFile(directoryPath
,
1640 fileName
, B_READ_WRITE
| B_CREATE_FILE
| B_ERASE_FILE
, file
, &_entry
);
1641 if (error
!= B_OK
) {
1642 ERROR("CommitTransactionHandler::_WriteTextFile(): failed to create "
1643 "file \"%s/%s\": %s\n", directoryPath
.ToString().String(), fileName
,
1648 ssize_t bytesWritten
= file
.Write(content
.String(),
1650 if (bytesWritten
< 0) {
1651 ERROR("CommitTransactionHandler::_WriteTextFile(): failed to write "
1652 "file \"%s/%s\": %s\n", directoryPath
.ToString().String(), fileName
,
1653 strerror(bytesWritten
));
1654 return bytesWritten
;
1662 CommitTransactionHandler::_ChangePackageActivation(
1663 const PackageSet
& packagesToActivate
,
1664 const PackageSet
& packagesToDeactivate
)
1666 INFORM("CommitTransactionHandler::_ChangePackageActivation(): activating "
1667 "%zu, deactivating %zu packages\n", packagesToActivate
.size(),
1668 packagesToDeactivate
.size());
1670 // write the temporary package activation file
1671 BEntry activationFileEntry
;
1672 _WriteActivationFile(RelativePath(kAdminDirectoryName
),
1673 kTemporaryActivationFileName
, packagesToActivate
, packagesToDeactivate
,
1674 activationFileEntry
);
1677 if (fVolumeStateIsActive
) {
1678 _ChangePackageActivationIOCtl(packagesToActivate
, packagesToDeactivate
);
1680 // TODO: Notify packagefs that active packages have been moved or do
1681 // node monitoring in packagefs!
1684 // rename the temporary activation file to the final file
1685 status_t error
= activationFileEntry
.Rename(kActivationFileName
, true);
1686 if (error
!= B_OK
) {
1687 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE
)
1689 FSUtils::Entry(activationFileEntry
),
1690 activationFileEntry
.Name()))
1691 .SetPath2(kActivationFileName
)
1692 .SetSystemError(error
);
1694 // TODO: We should probably try to revert the activation changes, though that
1695 // will fail, if this method has been called in response to node monitoring
1696 // events. Alternatively moving the package activation file could be made part
1697 // of the ioctl(), since packagefs should be able to undo package changes until
1698 // the very end, unless running out of memory. In the end the situation would be
1699 // bad anyway, though, since the activation file may refer to removed packages
1700 // and things would be in an inconsistent state after rebooting.
1703 // Update our state, i.e. remove deactivated packages and mark activated
1704 // packages accordingly.
1705 fVolumeState
->ActivationChanged(packagesToActivate
, packagesToDeactivate
);
1710 CommitTransactionHandler::_ChangePackageActivationIOCtl(
1711 const PackageSet
& packagesToActivate
,
1712 const PackageSet
& packagesToDeactivate
)
1714 // compute the size of the allocation we need for the activation change
1716 int32 itemCount
= packagesToActivate
.size() + packagesToDeactivate
.size();
1717 size_t requestSize
= sizeof(PackageFSActivationChangeRequest
)
1718 + itemCount
* sizeof(PackageFSActivationChangeItem
);
1720 for (PackageSet::iterator it
= packagesToActivate
.begin();
1721 it
!= packagesToActivate
.end(); ++it
) {
1722 requestSize
+= (*it
)->FileName().Length() + 1;
1725 for (PackageSet::iterator it
= packagesToDeactivate
.begin();
1726 it
!= packagesToDeactivate
.end(); ++it
) {
1727 requestSize
+= (*it
)->FileName().Length() + 1;
1730 // allocate and prepare the request
1731 PackageFSActivationChangeRequest
* request
1732 = (PackageFSActivationChangeRequest
*)malloc(requestSize
);
1733 if (request
== NULL
)
1734 throw Exception(B_TRANSACTION_NO_MEMORY
);
1735 MemoryDeleter
requestDeleter(request
);
1737 request
->itemCount
= itemCount
;
1739 PackageFSActivationChangeItem
* item
= &request
->items
[0];
1740 char* nameBuffer
= (char*)(item
+ itemCount
);
1742 for (PackageSet::iterator it
= packagesToActivate
.begin();
1743 it
!= packagesToActivate
.end(); ++it
, item
++) {
1744 _FillInActivationChangeItem(item
, PACKAGE_FS_ACTIVATE_PACKAGE
, *it
,
1748 for (PackageSet::iterator it
= packagesToDeactivate
.begin();
1749 it
!= packagesToDeactivate
.end(); ++it
, item
++) {
1750 _FillInActivationChangeItem(item
, PACKAGE_FS_DEACTIVATE_PACKAGE
, *it
,
1754 // issue the request
1755 int fd
= fVolume
->OpenRootDirectory();
1757 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY
)
1759 FSUtils::Entry(fVolume
->RootDirectoryRef()),
1760 "<packagefs root>"))
1761 .SetSystemError(fd
);
1763 FileDescriptorCloser
fdCloser(fd
);
1765 if (ioctl(fd
, PACKAGE_FS_OPERATION_CHANGE_ACTIVATION
, request
, requestSize
)
1767 // TODO: We need more error information and error handling!
1768 throw Exception(B_TRANSACTION_FAILED_TO_CHANGE_PACKAGE_ACTIVATION
)
1769 .SetSystemError(errno
);
1775 CommitTransactionHandler::_FillInActivationChangeItem(
1776 PackageFSActivationChangeItem
* item
, PackageFSActivationChangeType type
,
1777 Package
* package
, char*& nameBuffer
)
1780 item
->packageDeviceID
= package
->NodeRef().device
;
1781 item
->packageNodeID
= package
->NodeRef().node
;
1782 item
->nameLength
= package
->FileName().Length();
1783 item
->parentDeviceID
= fVolume
->PackagesDeviceID();
1784 item
->parentDirectoryID
= fVolume
->PackagesDirectoryID();
1785 item
->name
= nameBuffer
;
1786 strcpy(nameBuffer
, package
->FileName());
1787 nameBuffer
+= package
->FileName().Length() + 1;
1792 CommitTransactionHandler::_IsSystemPackage(Package
* package
)
1794 // package name should be "haiku[_<arch>]"
1795 const BString
& name
= package
->Info().Name();
1796 if (!name
.StartsWith("haiku"))
1798 if (name
.Length() == 5)
1803 BPackageArchitecture architecture
;
1804 return BPackageInfo::GetArchitectureByName(name
.String() + 6, architecture
)
1810 CommitTransactionHandler::_AddIssue(const TransactionIssueBuilder
& builder
)
1812 fResult
.AddIssue(builder
.BuildIssue(fCurrentPackage
));
1817 CommitTransactionHandler::_GetPath(const FSUtils::Entry
& entry
,
1818 const BString
& fallback
)
1820 BString path
= entry
.Path();
1821 return path
.IsEmpty() ? fallback
: path
;
1826 CommitTransactionHandler::_TagPackageEntriesRecursively(BDirectory
& directory
,
1827 const BString
& value
, bool nonDirectoriesOnly
)
1829 char buffer
[sizeof(dirent
) + B_FILE_NAME_LENGTH
];
1830 dirent
*entry
= (dirent
*)buffer
;
1831 while (directory
.GetNextDirents(entry
, sizeof(buffer
), 1) == 1) {
1832 if (strcmp(entry
->d_name
, ".") == 0
1833 || strcmp(entry
->d_name
, "..") == 0) {
1839 status_t error
= directory
.GetStatFor(entry
->d_name
, &st
);
1840 if (error
!= B_OK
) {
1841 throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY
)
1843 FSUtils::Entry(directory
, entry
->d_name
),
1845 .SetSystemError(error
);
1847 bool isDirectory
= S_ISDIR(st
.st_mode
);
1849 // open the node and set the attribute
1851 BDirectory stackDirectory
;
1854 node
= &stackDirectory
;
1855 error
= stackDirectory
.SetTo(&directory
, entry
->d_name
);
1858 error
= stackNode
.SetTo(&directory
, entry
->d_name
);
1861 if (error
!= B_OK
) {
1862 throw Exception(isDirectory
1863 ? B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY
1864 : B_TRANSACTION_FAILED_TO_OPEN_FILE
)
1866 FSUtils::Entry(directory
, entry
->d_name
),
1868 .SetSystemError(error
);
1871 if (!isDirectory
|| !nonDirectoriesOnly
) {
1872 error
= node
->WriteAttrString(kPackageFileAttribute
, &value
);
1873 if (error
!= B_OK
) {
1874 throw Exception(B_TRANSACTION_FAILED_TO_WRITE_FILE_ATTRIBUTE
)
1876 FSUtils::Entry(directory
, entry
->d_name
),
1878 .SetSystemError(error
);
1884 _TagPackageEntriesRecursively(stackDirectory
, value
,
1885 nonDirectoriesOnly
);