added 3 changesets to branch 'refs/remotes/HaikuPM-github/package-management' old head: 19a268a459bb8a117f42937901532ef3b117a8d7 new head: 40cbf171efee048cd28a69e791b2ebac38cad3c2 overview: https://github.com/haiku/HaikuPM/compare/19a268a...40cbf17 ---------------------------------------------------------------------------- a96531f: package daemon: On changes write a file with the active packages 19dc1d0: packagefs: Use the package activation file If the file exists load only the packages specified in it. If it doesn't exist or any kind of error occurs, fall back to loading all packages in the packages directory. 40cbf17: packagefs: don't dup() the packages directory FD Volume::_AddInitialPackagesFromDirectory(): Use openat() instead of dup() to get a FD for the packages directory. Currently our fdopendir() implementation doesn't use it directly anyway, but in theory it could and would then change the state of the original FD. [ Ingo Weinhold <ingo_weinhold@xxxxxx> ] ---------------------------------------------------------------------------- 5 files changed, 309 insertions(+), 31 deletions(-) headers/private/package/PackagesDirectoryDefs.h | 18 ++ .../kernel/file_systems/packagefs/Volume.cpp | 184 ++++++++++++++++--- .../kernel/file_systems/packagefs/Volume.h | 4 + src/servers/package/Volume.cpp | 128 ++++++++++++- src/servers/package/Volume.h | 6 + ############################################################################ Commit: a96531fc511e210c34cb054310b61b560a82a06c Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Tue Apr 9 20:12:22 2013 UTC package daemon: On changes write a file with the active packages ---------------------------------------------------------------------------- diff --git a/headers/private/package/PackagesDirectoryDefs.h b/headers/private/package/PackagesDirectoryDefs.h new file mode 100644 index 0000000..dacd05b --- /dev/null +++ b/headers/private/package/PackagesDirectoryDefs.h @@ -0,0 +1,18 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold <ingo_weinhold@xxxxxx> + */ +#ifndef _PACKAGE__PRIVATE__PACKAGES_DIRECTORY_DEFS_H_ +#define _PACKAGE__PRIVATE__PACKAGES_DIRECTORY_DEFS_H_ + + +#define PACKAGES_DIRECTORY_CONFIG_DIRECTORY "config" +#define PACKAGES_DIRECTORY_ACTIVATION_FILE "activated-packages" + + + +#endif // _PACKAGE__PRIVATE__PACKAGES_DIRECTORY_DEFS_H_ + diff --git a/src/servers/package/Volume.cpp b/src/servers/package/Volume.cpp index f5082ba..bd8ba7b 100644 --- a/src/servers/package/Volume.cpp +++ b/src/servers/package/Volume.cpp @@ -16,6 +16,7 @@ #include <Directory.h> #include <Entry.h> +#include <File.h> #include <Looper.h> #include <NodeMonitor.h> #include <Path.h> @@ -29,11 +30,18 @@ #include <AutoDeleter.h> #include <AutoLocker.h> +#include <PackagesDirectoryDefs.h> #include "DebugSupport.h" -static const char* kPackageFileNameExtension = ".hpkg"; +static const char* const kPackageFileNameExtension = ".hpkg"; +static const char* const kConfigDirectoryName + = PACKAGES_DIRECTORY_CONFIG_DIRECTORY; +static const char* const kActivationFileName + = PACKAGES_DIRECTORY_ACTIVATION_FILE; +static const char* const kTemporaryActivationFileName + = PACKAGES_DIRECTORY_ACTIVATION_FILE ".tmp"; // #pragma mark - Listener @@ -493,6 +501,47 @@ fPackagesToBeActivated.size(), fPackagesToBeDeactivated.size()); fPackagesToBeActivated.clear(); fPackagesToBeDeactivated.clear(); + + // write the package activation file + + // create the content + BString activationFileContent; + for (PackageFileNameHashTable::Iterator it + = fPackagesByFileName.GetIterator(); it.HasNext();) { + Package* package = it.Next(); + if (package->IsActive()) + activationFileContent << package->FileName() << '\n'; + } + + // open and write the temporary file + BFile activationFile; + BEntry activationFileEntry; + status_t error = _OpenPackagesFile(kConfigDirectoryName, + kTemporaryActivationFileName, + B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE, activationFile, + &activationFileEntry); + if (error != B_OK) { + ERROR("Volume::ProcessPendingPackageActivationChanges(): failed to " + "create activation file: %s\n", strerror(error)); + return; + } + + ssize_t bytesWritten = activationFile.Write(activationFileContent.String(), + activationFileContent.Length()); + if (bytesWritten < 0) { + ERROR("Volume::ProcessPendingPackageActivationChanges(): failed to " + "write activation file: %s\n", strerror(bytesWritten)); + return; + } + + // rename the temporary file to the final file + error = activationFileEntry.Rename(kActivationFileName, true); + if (error != B_OK) { + ERROR("Volume::ProcessPendingPackageActivationChanges(): failed to " + "rename temporary activation file to final file: %s\n", + strerror(error)); + return; + } } @@ -672,8 +721,8 @@ Volume::_ReadPackagesDirectory() BDirectory directory; status_t error = directory.SetTo(&fPackagesDirectoryRef); if (error != B_OK) { - ERROR("Volume::_ReadPackagesDirectory(): open packages directory: %s\n", - strerror(error)); + ERROR("Volume::_ReadPackagesDirectory(): failed to open packages " + "directory: %s\n", strerror(error)); RETURN_ERROR(error); } @@ -793,3 +842,76 @@ Volume::_AddRepository(BSolver* solver, BSolverRepository& repository, return B_OK; } + + +status_t +Volume::_OpenPackagesFile(const char* subDirectoryPath, const char* fileName, + uint32 openMode, BFile& _file, BEntry* _entry) +{ + BDirectory directory; + if (subDirectoryPath != NULL) { + status_t error = _OpenPackagesSubDirectory(subDirectoryPath, + (openMode & B_CREATE_FILE) != 0, directory); + if (error != B_OK) { + ERROR("Volume::_OpenPackagesFile(): failed to open packages " + "subdirectory \"%s\": %s\n", subDirectoryPath, strerror(error)); + RETURN_ERROR(error); + } + } else { + status_t error = directory.SetTo(&fPackagesDirectoryRef); + if (error != B_OK) { + ERROR("Volume::_OpenPackagesFile(): failed to open packages " + "directory: %s\n", strerror(error)); + RETURN_ERROR(error); + } + } + + BEntry stackEntry; + BEntry& entry = _entry != NULL ? *_entry : stackEntry; + status_t error = entry.SetTo(&directory, fileName); + if (error != B_OK) { + ERROR("Volume::_OpenPackagesFile(): failed to get entry for file: %s", + strerror(error)); + RETURN_ERROR(error); + } + + return _file.SetTo(&entry, openMode); +} + + +status_t +Volume::_OpenPackagesSubDirectory(const char* path, bool create, + BDirectory& _directory) +{ + // open the packages directory + BDirectory directory; + status_t error = directory.SetTo(&fPackagesDirectoryRef); + if (error != B_OK) { + ERROR("Volume::_OpenConfigSubDirectory(): failed to open packages " + "directory: %s\n", strerror(error)); + RETURN_ERROR(error); + } + + // If creating is not allowed, just try to open it. + if (!create) + RETURN_ERROR(_directory.SetTo(&directory, path)); + + // get an absolute path and create the subdirectory + BPath absolutePath; + error = absolutePath.SetTo(&directory, path); + if (error != B_OK) { + ERROR("Volume::_OpenConfigSubDirectory(): failed to get absolute path " + "for subdirectory \"%s\": %s\n", path, strerror(error)); + RETURN_ERROR(error); + } + + error = create_directory(absolutePath.Path(), + S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + if (error != B_OK) { + ERROR("Volume::_OpenConfigSubDirectory(): failed to create packages " + "subdirectory \"%s\": %s\n", path, strerror(error)); + RETURN_ERROR(error); + } + + RETURN_ERROR(_directory.SetTo(&directory, path)); +} diff --git a/src/servers/package/Volume.h b/src/servers/package/Volume.h index ba95caa..1885b1d 100644 --- a/src/servers/package/Volume.h +++ b/src/servers/package/Volume.h @@ -113,6 +113,12 @@ private: BSolverRepository& repository, bool activeOnly, bool installed); + status_t _OpenPackagesFile(const char* subDirectoryPath, + const char* fileName, uint32 openMode, + BFile& _file, BEntry* _entry = NULL); + status_t _OpenPackagesSubDirectory(const char* path, + bool create, BDirectory& _directory); + private: BString fPath; PackageFSMountType fMountType; ############################################################################ Commit: 19dc1d084f13bed77863dd1be56e846c314dd546 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Tue Apr 9 21:26:56 2013 UTC packagefs: Use the package activation file If the file exists load only the packages specified in it. If it doesn't exist or any kind of error occurs, fall back to loading all packages in the packages directory. ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/file_systems/packagefs/Volume.cpp b/src/add-ons/kernel/file_systems/packagefs/Volume.cpp index 16126af..21bc96f 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Volume.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/Volume.cpp @@ -22,6 +22,7 @@ #include <package/PackageInfoAttributes.h> #include <AutoDeleter.h> +#include <PackagesDirectoryDefs.h> #include <fs/KPath.h> #include <team.h> @@ -64,6 +65,13 @@ const char* const* kHomeShineThroughDirectories // sanity limit for activation change request const size_t kMaxActivationRequestSize = 10 * 1024 * 1024; +// sanity limit for activation file size +const size_t kMaxActivationFileSize = 10 * 1024 * 1024; + +static const char* const kActivationFilePath + = PACKAGES_DIRECTORY_CONFIG_DIRECTORY "/" + PACKAGES_DIRECTORY_ACTIVATION_FILE; + // #pragma mark - ShineThroughDirectory @@ -291,12 +299,7 @@ Volume::~Volume() } // delete the packages - Package* package = fPackages.Clear(true); - while (package != NULL) { - Package* next = package->FileNameHashTableNext(); - package->ReleaseReference(); - package = next; - } + _RemoveAllPackages(); // delete all indices Index* index = fIndices.Clear(true); @@ -731,6 +734,124 @@ Volume::_AddInitialPackages() dprintf("packagefs: Adding packages from \"%s\"\n", fPackagesDirectory->Path()); + // try reading the activation file + status_t error = _AddInitialPackagesFromActivationFile(); + if (error != B_OK) { + INFORM("Loading packages from activation file failed. Loading all " + "packages in packages directory.\n"); + + // remove all packages already added + { + VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf()); + VolumeWriteLocker volumeLocker(this); + _RemoveAllPackages(); + } + + // read the whole directory + error = _AddInitialPackagesFromDirectory(); + if (error != B_OK) + RETURN_ERROR(error); + } + + // add the packages to the node tree + VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf()); + VolumeWriteLocker volumeLocker(this); + for (PackageFileNameHashTable::Iterator it = fPackages.GetIterator(); + Package* package = it.Next();) { + error = _AddPackageContent(package, false); + if (error != B_OK) { + for (it.Rewind(); Package* activePackage = it.Next();) { + if (activePackage == package) + break; + _RemovePackageContent(activePackage, NULL, false); + } + RETURN_ERROR(error); + } + } + + return B_OK; +} + + +status_t +Volume::_AddInitialPackagesFromActivationFile() +{ + // try reading the activation file + int fd = openat(fPackagesDirectory->DirectoryFD(), + kActivationFilePath, O_RDONLY); + if (fd < 0) { + INFORM("Failed to open packages activation file: %s\n", + strerror(errno)); + RETURN_ERROR(errno); + } + FileDescriptorCloser fdCloser(fd); + + // read the whole file into memory to simplify things + struct stat st; + if (fstat(fd, &st) != 0) { + ERROR("Failed to stat packages activation file: %s\n", + strerror(errno)); + RETURN_ERROR(errno); + } + + if (st.st_size > kMaxActivationFileSize) { + ERROR("The packages activation file is too big.\n"); + RETURN_ERROR(B_BAD_DATA); + } + + char* fileContent = (char*)malloc(st.st_size + 1); + if (fileContent == NULL) + RETURN_ERROR(B_NO_MEMORY); + MemoryDeleter fileContentDeleter(fileContent); + + ssize_t bytesRead = read(fd, fileContent, st.st_size); + if (bytesRead < 0) { + ERROR("Failed to read packages activation file: %s\n", strerror(errno)); + RETURN_ERROR(errno); + } + + if (bytesRead != st.st_size) { + ERROR("Failed to read whole packages activation file\n"); + RETURN_ERROR(B_ERROR); + } + + // null-terminate to simplify parsing + fileContent[st.st_size] = '\0'; + + // parse the file and add the respective packages + const char* packageName = fileContent; + char* const fileContentEnd = fileContent + st.st_size; + while (packageName < fileContentEnd) { + char* packageNameEnd = strchr(packageName, '\n'); + if (packageNameEnd == NULL) + packageNameEnd = fileContentEnd; + + // skip empty lines + if (packageName == packageNameEnd) { + packageName++; + continue; + } + *packageNameEnd = '\0'; + + if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) { + ERROR("Invalid packages activation file content.\n"); + RETURN_ERROR(B_BAD_DATA); + } + + status_t error = _LoadAndAddInitialPackage(packageName); + if (error != B_OK) + RETURN_ERROR(error); + + packageName = packageNameEnd + 1; + } + + return B_OK; +} + + +status_t +Volume::_AddInitialPackagesFromDirectory() +{ // iterate through the dir and create packages int fd = dup(fPackagesDirectory->DirectoryFD()); if (fd < 0) { @@ -758,32 +879,27 @@ Volume::_AddInitialPackages() continue; } - Package* package; - if (_LoadPackage(entry->d_name, package) != B_OK) - continue; - BReference<Package> packageReference(package, true); + _LoadAndAddInitialPackage(entry->d_name); + } + + return B_OK; +} - VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf()); - VolumeWriteLocker volumeLocker(this); - _AddPackage(package); +status_t +Volume::_LoadAndAddInitialPackage(const char* name) +{ + Package* package; + status_t error = _LoadPackage(name, package); + if (error != B_OK) { + ERROR("Failed to load package \"%s\": %s\n", name, strerror(error)); + RETURN_ERROR(error); } + BReference<Package> packageReference(package, true); - // add the packages to the node tree VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf()); VolumeWriteLocker volumeLocker(this); - for (PackageFileNameHashTable::Iterator it = fPackages.GetIterator(); - Package* package = it.Next();) { - status_t error = _AddPackageContent(package, false); - if (error != B_OK) { - for (it.Rewind(); Package* activePackage = it.Next();) { - if (activePackage == package) - break; - _RemovePackageContent(activePackage, NULL, false); - } - RETURN_ERROR(error); - } - } + _AddPackage(package); return B_OK; } @@ -805,6 +921,18 @@ Volume::_RemovePackage(Package* package) } +void +Volume::_RemoveAllPackages() +{ + Package* package = fPackages.Clear(true); + while (package != NULL) { + Package* next = package->FileNameHashTableNext(); + package->ReleaseReference(); + package = next; + } +} + + inline Package* Volume::_FindPackage(const char* fileName) const { diff --git a/src/add-ons/kernel/file_systems/packagefs/Volume.h b/src/add-ons/kernel/file_systems/packagefs/Volume.h index 23caf19..525a979 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Volume.h +++ b/src/add-ons/kernel/file_systems/packagefs/Volume.h @@ -111,9 +111,13 @@ private: private: status_t _AddInitialPackages(); + status_t _AddInitialPackagesFromActivationFile(); + status_t _AddInitialPackagesFromDirectory(); + status_t _LoadAndAddInitialPackage(const char* name); inline void _AddPackage(Package* package); inline void _RemovePackage(Package* package); + void _RemoveAllPackages(); inline Package* _FindPackage(const char* fileName) const; status_t _AddPackageContent(Package* package, ############################################################################ Commit: 40cbf171efee048cd28a69e791b2ebac38cad3c2 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Tue Apr 9 21:35:57 2013 UTC packagefs: don't dup() the packages directory FD Volume::_AddInitialPackagesFromDirectory(): Use openat() instead of dup() to get a FD for the packages directory. Currently our fdopendir() implementation doesn't use it directly anyway, but in theory it could and would then change the state of the original FD. ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/file_systems/packagefs/Volume.cpp b/src/add-ons/kernel/file_systems/packagefs/Volume.cpp index 21bc96f..2e7af7d 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Volume.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/Volume.cpp @@ -853,9 +853,9 @@ status_t Volume::_AddInitialPackagesFromDirectory() { // iterate through the dir and create packages - int fd = dup(fPackagesDirectory->DirectoryFD()); + int fd = openat(fPackagesDirectory->DirectoryFD(), ".", O_RDONLY); if (fd < 0) { - ERROR("Failed to dup() packages directory FD: %s\n", strerror(errno)); + ERROR("Failed to open packages directory: %s\n", strerror(errno)); RETURN_ERROR(errno); }