btrfs: [] on the end of a struct field is a variable length array.
[haiku.git] / src / add-ons / kernel / file_systems / packagefs / volume / Volume.cpp
blobb8949b450f4ebbf680097e17db7e55f7e4807242
1 /*
2 * Copyright 2009-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "Volume.h"
9 #include <dirent.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <string.h>
13 #include <sys/param.h>
14 #include <sys/stat.h>
16 #include <new>
18 #include <AppDefs.h>
19 #include <driver_settings.h>
20 #include <KernelExport.h>
21 #include <NodeMonitor.h>
22 #include <package/PackageInfoAttributes.h>
24 #include <AutoDeleter.h>
25 #include <PackagesDirectoryDefs.h>
27 #include <vfs.h>
29 #include "AttributeIndex.h"
30 #include "DebugSupport.h"
31 #include "kernel_interface.h"
32 #include "LastModifiedIndex.h"
33 #include "NameIndex.h"
34 #include "OldUnpackingNodeAttributes.h"
35 #include "PackageFSRoot.h"
36 #include "PackageLinkDirectory.h"
37 #include "PackageLinksDirectory.h"
38 #include "Resolvable.h"
39 #include "SizeIndex.h"
40 #include "UnpackingLeafNode.h"
41 #include "UnpackingDirectory.h"
42 #include "Utils.h"
43 #include "Version.h"
46 // node ID of the root directory
47 static const ino_t kRootDirectoryID = 1;
49 static const uint32 kAllStatFields = B_STAT_MODE | B_STAT_UID | B_STAT_GID
50 | B_STAT_SIZE | B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME
51 | B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME;
53 // shine-through directories
54 const char* const kShineThroughDirectories[] = {
55 "cache", "non-packaged", "packages", "settings", "var", NULL
58 // sanity limit for activation change request
59 const size_t kMaxActivationRequestSize = 10 * 1024 * 1024;
61 // sanity limit for activation file size
62 const size_t kMaxActivationFileSize = 10 * 1024 * 1024;
64 static const char* const kAdministrativeDirectoryName
65 = PACKAGES_DIRECTORY_ADMIN_DIRECTORY;
66 static const char* const kActivationFileName
67 = PACKAGES_DIRECTORY_ACTIVATION_FILE;
68 static const char* const kActivationFilePath
69 = PACKAGES_DIRECTORY_ADMIN_DIRECTORY "/"
70 PACKAGES_DIRECTORY_ACTIVATION_FILE;
73 // #pragma mark - ShineThroughDirectory
76 struct Volume::ShineThroughDirectory : public Directory {
77 ShineThroughDirectory(ino_t id)
79 Directory(id)
81 get_real_time(fModifiedTime);
84 virtual timespec ModifiedTime() const
86 return fModifiedTime;
89 private:
90 timespec fModifiedTime;
94 // #pragma mark - ActivationChangeRequest
97 struct Volume::ActivationChangeRequest {
98 public:
99 ActivationChangeRequest()
101 fRequest(NULL),
102 fRequestSize(0)
106 ~ActivationChangeRequest()
108 free(fRequest);
111 status_t Init(const void* userRequest, size_t requestSize)
113 // copy request to kernel
114 if (requestSize > kMaxActivationRequestSize)
115 RETURN_ERROR(B_BAD_VALUE);
117 fRequest = (PackageFSActivationChangeRequest*)malloc(requestSize);
118 if (fRequest == NULL)
119 RETURN_ERROR(B_NO_MEMORY);
120 fRequestSize = requestSize;
122 status_t error = user_memcpy(fRequest, userRequest, fRequestSize);
123 if (error != B_OK)
124 RETURN_ERROR(error);
126 uint32 itemCount = fRequest->itemCount;
127 const char* requestEnd = (const char*)fRequest + requestSize;
128 if (&fRequest->items[itemCount] > (void*)requestEnd)
129 RETURN_ERROR(B_BAD_VALUE);
131 // adjust the item name pointers and check their validity
132 addr_t nameDelta = (addr_t)fRequest - (addr_t)userRequest;
133 for (uint32 i = 0; i < itemCount; i++) {
134 PackageFSActivationChangeItem& item = fRequest->items[i];
135 item.name += nameDelta;
136 if (item.name < (char*)fRequest || item.name >= requestEnd)
137 RETURN_ERROR(B_BAD_VALUE);
138 size_t maxNameSize = requestEnd - item.name;
139 if (strnlen(item.name, maxNameSize) == maxNameSize)
140 RETURN_ERROR(B_BAD_VALUE);
143 return B_OK;
146 uint32 CountItems() const
148 return fRequest->itemCount;
151 PackageFSActivationChangeItem* ItemAt(uint32 index) const
153 return index < CountItems() ? &fRequest->items[index] : NULL;
156 private:
157 PackageFSActivationChangeRequest* fRequest;
158 size_t fRequestSize;
162 // #pragma mark - Volume
165 Volume::Volume(fs_volume* fsVolume)
167 fFSVolume(fsVolume),
168 fRootDirectory(NULL),
169 fPackageFSRoot(NULL),
170 fPackagesDirectory(NULL),
171 fPackagesDirectories(),
172 fPackagesDirectoriesByNodeRef(),
173 fPackageSettings(),
174 fNextNodeID(kRootDirectoryID + 1)
176 rw_lock_init(&fLock, "packagefs volume");
180 Volume::~Volume()
182 // remove the packages from the node tree
184 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
185 VolumeWriteLocker volumeLocker(this);
186 for (PackageFileNameHashTable::Iterator it = fPackages.GetIterator();
187 Package* package = it.Next();) {
188 _RemovePackageContent(package, NULL, false);
192 // delete the packages
193 _RemoveAllPackages();
195 // delete all indices
196 Index* index = fIndices.Clear(true);
197 while (index != NULL) {
198 Index* next = index->IndexHashLink();
199 delete index;
200 index = next;
203 // remove all nodes from the ID hash table
204 Node* node = fNodes.Clear(true);
205 while (node != NULL) {
206 Node* next = node->IDHashTableNext();
207 node->ReleaseReference();
208 node = next;
211 if (fPackageFSRoot != NULL) {
212 if (this == fPackageFSRoot->SystemVolume())
213 _RemovePackageLinksDirectory();
215 fPackageFSRoot->UnregisterVolume(this);
218 if (fRootDirectory != NULL)
219 fRootDirectory->ReleaseReference();
221 while (PackagesDirectory* directory = fPackagesDirectories.RemoveHead())
222 directory->ReleaseReference();
224 rw_lock_destroy(&fLock);
228 status_t
229 Volume::Mount(const char* parameterString)
231 // init the hash tables
232 status_t error = fPackagesDirectoriesByNodeRef.Init();
233 if (error != B_OK)
234 RETURN_ERROR(error);
236 error = fNodes.Init();
237 if (error != B_OK)
238 RETURN_ERROR(error);
240 error = fNodeListeners.Init();
241 if (error != B_OK)
242 RETURN_ERROR(error);
244 error = fPackages.Init();
245 if (error != B_OK)
246 RETURN_ERROR(error);
248 error = fIndices.Init();
249 if (error != B_OK)
250 RETURN_ERROR(error);
252 // create the name index
254 NameIndex* index = new(std::nothrow) NameIndex;
255 if (index == NULL)
256 RETURN_ERROR(B_NO_MEMORY);
258 error = index->Init(this);
259 if (error != B_OK) {
260 delete index;
261 RETURN_ERROR(error);
264 fIndices.Insert(index);
267 // create the size index
269 SizeIndex* index = new(std::nothrow) SizeIndex;
270 if (index == NULL)
271 RETURN_ERROR(B_NO_MEMORY);
273 error = index->Init(this);
274 if (error != B_OK) {
275 delete index;
276 RETURN_ERROR(error);
279 fIndices.Insert(index);
282 // create the last modified index
284 LastModifiedIndex* index = new(std::nothrow) LastModifiedIndex;
285 if (index == NULL)
286 RETURN_ERROR(B_NO_MEMORY);
288 error = index->Init(this);
289 if (error != B_OK) {
290 delete index;
291 RETURN_ERROR(error);
294 fIndices.Insert(index);
297 // create a BEOS:APP_SIG index
299 AttributeIndex* index = new(std::nothrow) AttributeIndex;
300 if (index == NULL)
301 RETURN_ERROR(B_NO_MEMORY);
303 error = index->Init(this, "BEOS:APP_SIG", B_MIME_STRING_TYPE, 0);
304 if (error != B_OK) {
305 delete index;
306 RETURN_ERROR(error);
309 fIndices.Insert(index);
312 // get the mount parameters
313 const char* packages = NULL;
314 const char* volumeName = NULL;
315 const char* mountType = NULL;
316 const char* shineThrough = NULL;
317 const char* packagesState = NULL;
319 void* parameterHandle = parse_driver_settings_string(parameterString);
320 if (parameterHandle != NULL) {
321 packages = get_driver_parameter(parameterHandle, "packages", NULL,
322 NULL);
323 volumeName = get_driver_parameter(parameterHandle, "volume-name", NULL,
324 NULL);
325 mountType = get_driver_parameter(parameterHandle, "type", NULL, NULL);
326 shineThrough = get_driver_parameter(parameterHandle, "shine-through",
327 NULL, NULL);
328 packagesState = get_driver_parameter(parameterHandle, "state", NULL,
329 NULL);
332 CObjectDeleter<void, status_t> parameterHandleDeleter(parameterHandle,
333 &delete_driver_settings);
335 if (packages != NULL && packages[0] == '\0') {
336 FATAL("invalid package folder ('packages' parameter)!\n");
337 RETURN_ERROR(B_BAD_VALUE);
340 error = _InitMountType(mountType);
341 if (error != B_OK) {
342 FATAL("invalid mount type: \"%s\"\n", mountType);
343 RETURN_ERROR(error);
346 // get our mount point
347 error = vfs_get_mount_point(fFSVolume->id, &fMountPoint.deviceID,
348 &fMountPoint.nodeID);
349 if (error != B_OK)
350 RETURN_ERROR(error);
352 // load package settings
353 error = fPackageSettings.Load(fMountPoint.deviceID, fMountPoint.nodeID,
354 fMountType);
355 // abort only in case of serious issues (memory shortage)
356 if (error == B_NO_MEMORY)
357 RETURN_ERROR(error);
359 // create package domain
360 fPackagesDirectory = new(std::nothrow) PackagesDirectory;
361 if (fPackagesDirectory == NULL)
362 RETURN_ERROR(B_NO_MEMORY);
363 fPackagesDirectories.Add(fPackagesDirectory);
364 fPackagesDirectoriesByNodeRef.Insert(fPackagesDirectory);
366 struct stat st;
367 error = fPackagesDirectory->Init(packages, fMountPoint.deviceID,
368 fMountPoint.nodeID, st);
369 if (error != B_OK)
370 RETURN_ERROR(error);
372 // If a packages state has been specified, load the needed states.
373 if (packagesState != NULL) {
374 error = _LoadOldPackagesStates(packagesState);
375 if (error != B_OK)
376 RETURN_ERROR(error);
379 // If no volume name is given, infer it from the mount type.
380 if (volumeName == NULL) {
381 switch (fMountType) {
382 case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
383 volumeName = "system";
384 break;
385 case PACKAGE_FS_MOUNT_TYPE_HOME:
386 volumeName = "config";
387 break;
388 case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
389 default:
390 volumeName = "Package FS";
391 break;
395 String volumeNameString;
396 if (!volumeNameString.SetTo(volumeName))
397 RETURN_ERROR(B_NO_MEMORY);
399 // create the root node
400 fRootDirectory
401 = new(std::nothrow) ::RootDirectory(kRootDirectoryID, st.st_mtim);
402 if (fRootDirectory == NULL)
403 RETURN_ERROR(B_NO_MEMORY);
404 fRootDirectory->Init(NULL, volumeNameString);
405 fNodes.Insert(fRootDirectory);
406 fRootDirectory->AcquireReference();
407 // one reference for the table
409 // register with packagefs root
410 error = ::PackageFSRoot::RegisterVolume(this);
411 if (error != B_OK)
412 RETURN_ERROR(error);
414 if (this == fPackageFSRoot->SystemVolume()) {
415 error = _AddPackageLinksDirectory();
416 if (error != B_OK)
417 RETURN_ERROR(error);
420 // create shine-through directories
421 error = _CreateShineThroughDirectories(shineThrough);
422 if (error != B_OK)
423 RETURN_ERROR(error);
425 // add initial packages
426 error = _AddInitialPackages();
427 if (error != B_OK)
428 RETURN_ERROR(error);
430 // publish the root node
431 fRootDirectory->AcquireReference();
432 error = PublishVNode(fRootDirectory);
433 if (error != B_OK) {
434 fRootDirectory->ReleaseReference();
435 RETURN_ERROR(error);
438 // bind and publish the shine-through directories
439 error = _PublishShineThroughDirectories();
440 if (error != B_OK)
441 RETURN_ERROR(error);
443 StringPool::DumpUsageStatistics();
445 return B_OK;
449 void
450 Volume::Unmount()
455 status_t
456 Volume::IOCtl(Node* node, uint32 operation, void* buffer, size_t size)
458 switch (operation) {
459 case PACKAGE_FS_OPERATION_GET_VOLUME_INFO:
461 if (size < sizeof(PackageFSVolumeInfo))
462 RETURN_ERROR(B_BAD_VALUE);
464 PackageFSVolumeInfo* userVolumeInfo
465 = (PackageFSVolumeInfo*)buffer;
467 VolumeReadLocker volumeReadLocker(this);
469 PackageFSVolumeInfo volumeInfo;
470 volumeInfo.mountType = fMountType;
471 volumeInfo.rootDeviceID = fPackageFSRoot->DeviceID();
472 volumeInfo.rootDirectoryID = fPackageFSRoot->NodeID();
473 volumeInfo.packagesDirectoryCount = fPackagesDirectories.Count();
475 status_t error = user_memcpy(userVolumeInfo, &volumeInfo,
476 sizeof(volumeInfo));
477 if (error != B_OK)
478 RETURN_ERROR(error);
480 uint32 directoryIndex = 0;
481 for (PackagesDirectoryList::Iterator it
482 = fPackagesDirectories.GetIterator();
483 PackagesDirectory* directory = it.Next();
484 directoryIndex++) {
485 PackageFSDirectoryInfo info;
486 info.deviceID = directory->DeviceID();
487 info.nodeID = directory->NodeID();
489 PackageFSDirectoryInfo* userInfo
490 = userVolumeInfo->packagesDirectoryInfos + directoryIndex;
491 if (addr_t(userInfo + 1) > (addr_t)buffer + size)
492 break;
494 if (user_memcpy(userInfo, &info, sizeof(info)) != B_OK)
495 return B_BAD_ADDRESS;
498 return B_OK;
501 case PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS:
503 if (size < sizeof(PackageFSGetPackageInfosRequest))
504 RETURN_ERROR(B_BAD_VALUE);
506 PackageFSGetPackageInfosRequest* request
507 = (PackageFSGetPackageInfosRequest*)buffer;
509 VolumeReadLocker volumeReadLocker(this);
511 addr_t bufferEnd = (addr_t)buffer + size;
512 uint32 packageCount = fPackages.CountElements();
513 char* nameBuffer = (char*)(request->infos + packageCount);
515 uint32 packageIndex = 0;
516 for (PackageFileNameHashTable::Iterator it
517 = fPackages.GetIterator(); it.HasNext();
518 packageIndex++) {
519 Package* package = it.Next();
520 PackageFSPackageInfo info;
521 info.packageDeviceID = package->DeviceID();
522 info.packageNodeID = package->NodeID();
523 PackagesDirectory* directory = package->Directory();
524 info.directoryDeviceID = directory->DeviceID();
525 info.directoryNodeID = directory->NodeID();
526 info.name = nameBuffer;
528 PackageFSPackageInfo* userInfo = request->infos + packageIndex;
529 if (addr_t(userInfo + 1) <= bufferEnd) {
530 if (user_memcpy(userInfo, &info, sizeof(info)) != B_OK)
531 return B_BAD_ADDRESS;
534 const char* name = package->FileName();
535 size_t nameSize = strlen(name) + 1;
536 char* nameEnd = nameBuffer + nameSize;
537 if ((addr_t)nameEnd <= bufferEnd) {
538 if (user_memcpy(nameBuffer, name, nameSize) != B_OK)
539 return B_BAD_ADDRESS;
541 nameBuffer = nameEnd;
544 PackageFSGetPackageInfosRequest header;
545 header.bufferSize = nameBuffer - (char*)request;
546 header.packageCount = packageCount;
547 size_t headerSize = (char*)&request->infos - (char*)request;
548 RETURN_ERROR(user_memcpy(request, &header, headerSize));
551 case PACKAGE_FS_OPERATION_CHANGE_ACTIVATION:
553 ActivationChangeRequest request;
554 status_t error = request.Init(buffer, size);
555 if (error != B_OK)
556 RETURN_ERROR(B_BAD_VALUE);
558 return _ChangeActivation(request);
561 default:
562 return B_BAD_VALUE;
567 void
568 Volume::AddNodeListener(NodeListener* listener, Node* node)
570 ASSERT(!listener->IsListening());
572 listener->StartedListening(node);
574 if (NodeListener* list = fNodeListeners.Lookup(node))
575 list->AddNodeListener(listener);
576 else
577 fNodeListeners.Insert(listener);
581 void
582 Volume::RemoveNodeListener(NodeListener* listener)
584 ASSERT(listener->IsListening());
586 Node* node = listener->ListenedNode();
588 if (NodeListener* next = listener->RemoveNodeListener()) {
589 // list not empty yet -- if we removed the head, add a new head to the
590 // hash table
591 NodeListener* list = fNodeListeners.Lookup(node);
592 if (list == listener) {
593 fNodeListeners.Remove(listener);
594 fNodeListeners.Insert(next);
596 } else
597 fNodeListeners.Remove(listener);
599 listener->StoppedListening();
603 void
604 Volume::AddQuery(Query* query)
606 fQueries.Add(query);
610 void
611 Volume::RemoveQuery(Query* query)
613 fQueries.Remove(query);
617 void
618 Volume::UpdateLiveQueries(Node* node, const char* attribute, int32 type,
619 const void* oldKey, size_t oldLength, const void* newKey,
620 size_t newLength)
622 for (QueryList::Iterator it = fQueries.GetIterator();
623 Query* query = it.Next();) {
624 query->LiveUpdate(node, attribute, type, oldKey, oldLength, newKey,
625 newLength);
630 status_t
631 Volume::GetVNode(ino_t nodeID, Node*& _node)
633 return get_vnode(fFSVolume, nodeID, (void**)&_node);
637 status_t
638 Volume::PutVNode(ino_t nodeID)
640 return put_vnode(fFSVolume, nodeID);
644 status_t
645 Volume::RemoveVNode(ino_t nodeID)
647 return remove_vnode(fFSVolume, nodeID);
651 status_t
652 Volume::PublishVNode(Node* node)
654 return publish_vnode(fFSVolume, node->ID(), node, &gPackageFSVnodeOps,
655 node->Mode() & S_IFMT, 0);
659 void
660 Volume::PackageLinkNodeAdded(Node* node)
662 _AddPackageLinksNode(node);
664 notify_entry_created(ID(), node->Parent()->ID(), node->Name(), node->ID());
665 _NotifyNodeAdded(node);
669 void
670 Volume::PackageLinkNodeRemoved(Node* node)
672 _RemovePackageLinksNode(node);
674 notify_entry_removed(ID(), node->Parent()->ID(), node->Name(), node->ID());
675 _NotifyNodeRemoved(node);
679 void
680 Volume::PackageLinkNodeChanged(Node* node, uint32 statFields,
681 const OldNodeAttributes& oldAttributes)
683 Directory* parent = node->Parent();
684 notify_stat_changed(ID(), parent != NULL ? parent->ID() : -1, node->ID(),
685 statFields);
686 _NotifyNodeChanged(node, statFields, oldAttributes);
690 status_t
691 Volume::_LoadOldPackagesStates(const char* packagesState)
693 // open and stat the admininistrative dir
694 int fd = openat(fPackagesDirectory->DirectoryFD(),
695 kAdministrativeDirectoryName, O_RDONLY);
696 if (fd < 0) {
697 ERROR("Failed to open administrative directory: %s\n", strerror(errno));
698 RETURN_ERROR(errno);
701 struct stat adminDirStat;
702 if (fstat(fd, &adminDirStat) < 0) {
703 ERROR("Failed to fstat() administrative directory: %s\n",
704 strerror(errno));
705 RETURN_ERROR(errno);
708 // iterate through the "administrative" dir
709 DIR* dir = fdopendir(fd);
710 if (dir == NULL) {
711 ERROR("Failed to open administrative directory: %s\n", strerror(errno));
712 RETURN_ERROR(errno);
714 CObjectDeleter<DIR, int> dirCloser(dir, closedir);
716 while (dirent* entry = readdir(dir)) {
717 if (strncmp(entry->d_name, "state_", 6) != 0
718 || strcmp(entry->d_name, packagesState) < 0) {
719 continue;
722 PackagesDirectory* packagesDirectory
723 = new(std::nothrow) PackagesDirectory;
724 status_t error = packagesDirectory->InitOldState(adminDirStat.st_dev,
725 adminDirStat.st_ino, entry->d_name);
726 if (error != B_OK) {
727 delete packagesDirectory;
728 continue;
731 fPackagesDirectories.Add(packagesDirectory);
732 fPackagesDirectoriesByNodeRef.Insert(packagesDirectory);
734 INFORM("added old packages dir state \"%s\"\n",
735 packagesDirectory->StateName().Data());
738 // sort the packages directories by state age
739 fPackagesDirectories.Sort(&PackagesDirectory::IsNewer);
741 return B_OK;
745 status_t
746 Volume::_AddInitialPackages()
748 PackagesDirectory* packagesDirectory = fPackagesDirectories.Last();
749 INFORM("Adding packages from \"%s\"\n", packagesDirectory->Path());
751 // try reading the activation file of the oldest state
752 status_t error = _AddInitialPackagesFromActivationFile(packagesDirectory);
753 if (error != B_OK && packagesDirectory != fPackagesDirectory) {
754 WARN("Loading packages from old state \"%s\" failed. Loading packages "
755 "from latest state.\n", packagesDirectory->StateName().Data());
757 // remove all packages already added
759 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
760 VolumeWriteLocker volumeLocker(this);
761 _RemoveAllPackages();
764 // remove the old states
765 while (fPackagesDirectories.Last() != fPackagesDirectory)
766 fPackagesDirectories.RemoveTail()->ReleaseReference();
768 // try reading the activation file of the latest state
769 packagesDirectory = fPackagesDirectory;
770 error = _AddInitialPackagesFromActivationFile(packagesDirectory);
773 if (error != B_OK) {
774 INFORM("Loading packages from activation file failed. Loading all "
775 "packages in packages directory.\n");
777 // remove all packages already added
779 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
780 VolumeWriteLocker volumeLocker(this);
781 _RemoveAllPackages();
784 // read the whole directory
785 error = _AddInitialPackagesFromDirectory();
786 if (error != B_OK)
787 RETURN_ERROR(error);
790 // add the packages to the node tree
791 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
792 VolumeWriteLocker volumeLocker(this);
793 for (PackageFileNameHashTable::Iterator it = fPackages.GetIterator();
794 Package* package = it.Next();) {
795 error = _AddPackageContent(package, false);
796 if (error != B_OK) {
797 for (it.Rewind(); Package* activePackage = it.Next();) {
798 if (activePackage == package)
799 break;
800 _RemovePackageContent(activePackage, NULL, false);
802 RETURN_ERROR(error);
806 return B_OK;
810 status_t
811 Volume::_AddInitialPackagesFromActivationFile(
812 PackagesDirectory* packagesDirectory)
814 // try reading the activation file
815 int fd = openat(packagesDirectory->DirectoryFD(),
816 packagesDirectory == fPackagesDirectory
817 ? kActivationFilePath : kActivationFileName,
818 O_RDONLY);
819 if (fd < 0) {
820 INFORM("Failed to open packages activation file: %s\n",
821 strerror(errno));
822 RETURN_ERROR(errno);
824 FileDescriptorCloser fdCloser(fd);
826 // read the whole file into memory to simplify things
827 struct stat st;
828 if (fstat(fd, &st) != 0) {
829 ERROR("Failed to stat packages activation file: %s\n",
830 strerror(errno));
831 RETURN_ERROR(errno);
834 if (st.st_size > (off_t)kMaxActivationFileSize) {
835 ERROR("The packages activation file is too big.\n");
836 RETURN_ERROR(B_BAD_DATA);
839 char* fileContent = (char*)malloc(st.st_size + 1);
840 if (fileContent == NULL)
841 RETURN_ERROR(B_NO_MEMORY);
842 MemoryDeleter fileContentDeleter(fileContent);
844 ssize_t bytesRead = read(fd, fileContent, st.st_size);
845 if (bytesRead < 0) {
846 ERROR("Failed to read packages activation file: %s\n", strerror(errno));
847 RETURN_ERROR(errno);
850 if (bytesRead != st.st_size) {
851 ERROR("Failed to read whole packages activation file\n");
852 RETURN_ERROR(B_ERROR);
855 // null-terminate to simplify parsing
856 fileContent[st.st_size] = '\0';
858 // parse the file and add the respective packages
859 const char* packageName = fileContent;
860 char* const fileContentEnd = fileContent + st.st_size;
861 while (packageName < fileContentEnd) {
862 char* packageNameEnd = strchr(packageName, '\n');
863 if (packageNameEnd == NULL)
864 packageNameEnd = fileContentEnd;
866 // skip empty lines
867 if (packageName == packageNameEnd) {
868 packageName++;
869 continue;
871 *packageNameEnd = '\0';
873 if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) {
874 ERROR("Invalid packages activation file content.\n");
875 RETURN_ERROR(B_BAD_DATA);
878 status_t error = _LoadAndAddInitialPackage(packagesDirectory,
879 packageName);
880 if (error != B_OK)
881 RETURN_ERROR(error);
883 packageName = packageNameEnd + 1;
886 return B_OK;
890 status_t
891 Volume::_AddInitialPackagesFromDirectory()
893 // iterate through the dir and create packages
894 int fd = openat(fPackagesDirectory->DirectoryFD(), ".", O_RDONLY);
895 if (fd < 0) {
896 ERROR("Failed to open packages directory: %s\n", strerror(errno));
897 RETURN_ERROR(errno);
900 DIR* dir = fdopendir(fd);
901 if (dir == NULL) {
902 ERROR("Failed to open packages directory \"%s\": %s\n",
903 fPackagesDirectory->Path(), strerror(errno));
904 RETURN_ERROR(errno);
906 CObjectDeleter<DIR, int> dirCloser(dir, closedir);
908 while (dirent* entry = readdir(dir)) {
909 // skip "." and ".."
910 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
911 continue;
913 // also skip any entry without a ".hpkg" extension
914 size_t nameLength = strlen(entry->d_name);
915 if (nameLength < 5
916 || memcmp(entry->d_name + nameLength - 5, ".hpkg", 5) != 0) {
917 continue;
920 _LoadAndAddInitialPackage(fPackagesDirectory, entry->d_name);
923 return B_OK;
927 status_t
928 Volume::_LoadAndAddInitialPackage(PackagesDirectory* packagesDirectory,
929 const char* name)
931 Package* package;
932 status_t error = _LoadPackage(packagesDirectory, name, package);
933 if (error != B_OK) {
934 ERROR("Failed to load package \"%s\": %s\n", name, strerror(error));
935 RETURN_ERROR(error);
937 BReference<Package> packageReference(package, true);
939 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
940 VolumeWriteLocker volumeLocker(this);
941 _AddPackage(package);
943 return B_OK;
947 inline void
948 Volume::_AddPackage(Package* package)
950 fPackages.Insert(package);
951 package->AcquireReference();
955 inline void
956 Volume::_RemovePackage(Package* package)
958 fPackages.Remove(package);
959 package->ReleaseReference();
963 void
964 Volume::_RemoveAllPackages()
966 Package* package = fPackages.Clear(true);
967 while (package != NULL) {
968 Package* next = package->FileNameHashTableNext();
969 package->ReleaseReference();
970 package = next;
975 inline Package*
976 Volume::_FindPackage(const char* fileName) const
978 return fPackages.Lookup(fileName);
982 status_t
983 Volume::_AddPackageContent(Package* package, bool notify)
985 // Open the package. We don't need the FD here, but this is an optimization.
986 // The attribute indices may want to read the package nodes' attributes and
987 // the package file would be opened and closed for each attribute instance.
988 // Since Package keeps and shares the FD as long as at least one party has
989 // the package open, we prevent that.
990 int fd = package->Open();
991 if (fd < 0)
992 RETURN_ERROR(fd);
993 PackageCloser packageCloser(package);
995 status_t error = fPackageFSRoot->AddPackage(package);
996 if (error != B_OK)
997 RETURN_ERROR(error);
999 for (PackageNodeList::Iterator it = package->Nodes().GetIterator();
1000 PackageNode* node = it.Next();) {
1001 // skip over ".PackageInfo" file, it isn't part of the package content
1002 if (strcmp(node->Name(),
1003 BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME) == 0) {
1004 continue;
1006 error = _AddPackageContentRootNode(package, node, notify);
1007 if (error != B_OK) {
1008 _RemovePackageContent(package, node, notify);
1009 RETURN_ERROR(error);
1013 return B_OK;
1017 void
1018 Volume::_RemovePackageContent(Package* package, PackageNode* endNode,
1019 bool notify)
1021 PackageNode* node = package->Nodes().Head();
1022 while (node != NULL) {
1023 if (node == endNode)
1024 break;
1026 PackageNode* nextNode = package->Nodes().GetNext(node);
1028 // skip over ".PackageInfo" file, it isn't part of the package content
1029 if (strcmp(node->Name(),
1030 BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME) != 0) {
1031 _RemovePackageContentRootNode(package, node, NULL, notify);
1034 node = nextNode;
1037 fPackageFSRoot->RemovePackage(package);;
1041 /*! This method recursively iterates through the descendents of the given
1042 package root node and adds all package nodes to the node tree in
1043 pre-order.
1044 Due to limited kernel stack space we avoid deep recursive function calls
1045 and rather use the package node stack implied by the tree.
1047 status_t
1048 Volume::_AddPackageContentRootNode(Package* package,
1049 PackageNode* rootPackageNode, bool notify)
1051 PackageNode* packageNode = rootPackageNode;
1052 Directory* directory = fRootDirectory;
1053 directory->WriteLock();
1055 do {
1056 Node* node;
1057 status_t error = _AddPackageNode(directory, packageNode, notify, node);
1058 // returns B_OK with a NULL node, when skipping the node
1059 if (error != B_OK) {
1060 // unlock all directories
1061 while (directory != NULL) {
1062 directory->WriteUnlock();
1063 directory = directory->Parent();
1066 // remove the added package nodes
1067 _RemovePackageContentRootNode(package, rootPackageNode, packageNode,
1068 notify);
1069 RETURN_ERROR(error);
1072 // recurse into directory, unless we're supposed to skip the node
1073 if (node != NULL) {
1074 if (PackageDirectory* packageDirectory
1075 = dynamic_cast<PackageDirectory*>(packageNode)) {
1076 if (packageDirectory->FirstChild() != NULL) {
1077 directory = dynamic_cast<Directory*>(node);
1078 packageNode = packageDirectory->FirstChild();
1079 directory->WriteLock();
1080 continue;
1085 // continue with the next available (ancestors's) sibling
1086 do {
1087 PackageDirectory* packageDirectory = packageNode->Parent();
1088 PackageNode* sibling = packageDirectory != NULL
1089 ? packageDirectory->NextChild(packageNode) : NULL;
1091 if (sibling != NULL) {
1092 packageNode = sibling;
1093 break;
1096 // no more siblings -- go back up the tree
1097 packageNode = packageDirectory;
1098 directory->WriteUnlock();
1099 directory = directory->Parent();
1100 // the parent is still locked, so this is safe
1101 } while (packageNode != NULL);
1102 } while (packageNode != NULL);
1104 return B_OK;
1108 /*! Recursively iterates through the descendents of the given package root node
1109 and removes all package nodes from the node tree in post-order, until
1110 encountering \a endPackageNode (if non-null).
1111 Due to limited kernel stack space we avoid deep recursive function calls
1112 and rather use the package node stack implied by the tree.
1114 void
1115 Volume::_RemovePackageContentRootNode(Package* package,
1116 PackageNode* rootPackageNode, PackageNode* endPackageNode, bool notify)
1118 PackageNode* packageNode = rootPackageNode;
1119 Directory* directory = fRootDirectory;
1120 directory->WriteLock();
1122 do {
1123 if (packageNode == endPackageNode)
1124 break;
1126 // recurse into directory
1127 if (PackageDirectory* packageDirectory
1128 = dynamic_cast<PackageDirectory*>(packageNode)) {
1129 if (packageDirectory->FirstChild() != NULL) {
1130 if (Directory* childDirectory = dynamic_cast<Directory*>(
1131 directory->FindChild(packageNode->Name()))) {
1132 directory = childDirectory;
1133 packageNode = packageDirectory->FirstChild();
1134 directory->WriteLock();
1135 continue;
1140 // continue with the next available (ancestors's) sibling
1141 do {
1142 PackageDirectory* packageDirectory = packageNode->Parent();
1143 PackageNode* sibling = packageDirectory != NULL
1144 ? packageDirectory->NextChild(packageNode) : NULL;
1146 // we're done with the node -- remove it
1147 _RemovePackageNode(directory, packageNode,
1148 directory->FindChild(packageNode->Name()), notify);
1150 if (sibling != NULL) {
1151 packageNode = sibling;
1152 break;
1155 // no more siblings -- go back up the tree
1156 packageNode = packageDirectory;
1157 directory->WriteUnlock();
1158 directory = directory->Parent();
1159 // the parent is still locked, so this is safe
1160 } while (packageNode != NULL/* && packageNode != rootPackageNode*/);
1161 } while (packageNode != NULL/* && packageNode != rootPackageNode*/);
1165 status_t
1166 Volume::_AddPackageNode(Directory* directory, PackageNode* packageNode,
1167 bool notify, Node*& _node)
1169 bool newNode = false;
1170 UnpackingNode* unpackingNode;
1171 Node* node = directory->FindChild(packageNode->Name());
1172 PackageNode* oldPackageNode = NULL;
1174 if (node != NULL) {
1175 unpackingNode = dynamic_cast<UnpackingNode*>(node);
1176 if (unpackingNode == NULL) {
1177 _node = NULL;
1178 return B_OK;
1180 oldPackageNode = unpackingNode->GetPackageNode();
1181 } else {
1182 status_t error = _CreateUnpackingNode(packageNode->Mode(), directory,
1183 packageNode->Name(), unpackingNode);
1184 if (error != B_OK)
1185 RETURN_ERROR(error);
1187 node = unpackingNode->GetNode();
1188 newNode = true;
1191 BReference<Node> nodeReference(node);
1192 NodeWriteLocker nodeWriteLocker(node);
1194 BReference<Node> newNodeReference;
1195 NodeWriteLocker newNodeWriteLocker;
1196 Node* oldNode = NULL;
1198 if (!newNode && !S_ISDIR(node->Mode()) && oldPackageNode != NULL
1199 && unpackingNode->WillBeFirstPackageNode(packageNode)) {
1200 // The package node we're going to add will represent the node,
1201 // replacing the current head package node. Since the node isn't a
1202 // directory, we must make sure that clients having opened or mapped the
1203 // node won't be surprised. So we create a new node and remove the
1204 // current one.
1205 // create a new node and transfer the package nodes to it
1206 UnpackingNode* newUnpackingNode;
1207 status_t error = unpackingNode->CloneTransferPackageNodes(
1208 fNextNodeID++, newUnpackingNode);
1209 if (error != B_OK)
1210 RETURN_ERROR(error);
1212 // remove the old node
1213 _NotifyNodeRemoved(node);
1214 _RemoveNodeAndVNode(node);
1215 oldNode = node;
1217 // add the new node
1218 unpackingNode = newUnpackingNode;
1219 node = unpackingNode->GetNode();
1220 newNodeReference.SetTo(node);
1221 newNodeWriteLocker.SetTo(node, false);
1223 directory->AddChild(node);
1224 fNodes.Insert(node);
1225 newNode = true;
1228 status_t error = unpackingNode->AddPackageNode(packageNode, ID());
1229 if (error != B_OK) {
1230 // Remove the node, if created before. If the node was created to
1231 // replace the previous node, send out notifications instead.
1232 if (newNode) {
1233 if (oldNode != NULL) {
1234 _NotifyNodeAdded(node);
1235 if (notify) {
1236 notify_entry_removed(ID(), directory->ID(), oldNode->Name(),
1237 oldNode->ID());
1238 notify_entry_created(ID(), directory->ID(), node->Name(),
1239 node->ID());
1241 } else
1242 _RemoveNode(node);
1244 RETURN_ERROR(error);
1247 if (newNode) {
1248 _NotifyNodeAdded(node);
1249 } else if (packageNode == unpackingNode->GetPackageNode()) {
1250 _NotifyNodeChanged(node, kAllStatFields,
1251 OldUnpackingNodeAttributes(oldPackageNode));
1254 if (notify) {
1255 if (newNode) {
1256 if (oldNode != NULL) {
1257 notify_entry_removed(ID(), directory->ID(), oldNode->Name(),
1258 oldNode->ID());
1260 notify_entry_created(ID(), directory->ID(), node->Name(),
1261 node->ID());
1262 } else if (packageNode == unpackingNode->GetPackageNode()) {
1263 // The new package node has become the one representing the node.
1264 // Send stat changed notification for directories and entry
1265 // removed + created notifications for files and symlinks.
1266 notify_stat_changed(ID(), directory->ID(), node->ID(),
1267 kAllStatFields);
1268 // TODO: Actually the attributes might change, too!
1272 _node = node;
1273 return B_OK;
1277 void
1278 Volume::_RemovePackageNode(Directory* directory, PackageNode* packageNode,
1279 Node* node, bool notify)
1281 UnpackingNode* unpackingNode = dynamic_cast<UnpackingNode*>(node);
1282 if (unpackingNode == NULL)
1283 return;
1285 BReference<Node> nodeReference(node);
1286 NodeWriteLocker nodeWriteLocker(node);
1288 PackageNode* headPackageNode = unpackingNode->GetPackageNode();
1289 bool nodeRemoved = false;
1290 Node* newNode = NULL;
1292 BReference<Node> newNodeReference;
1293 NodeWriteLocker newNodeWriteLocker;
1295 // If this is the last package node of the node, remove it completely.
1296 if (unpackingNode->IsOnlyPackageNode(packageNode)) {
1297 // Notify before removing the node. Otherwise the indices might not
1298 // find the node anymore.
1299 _NotifyNodeRemoved(node);
1301 unpackingNode->PrepareForRemoval();
1303 _RemoveNodeAndVNode(node);
1304 nodeRemoved = true;
1305 } else if (packageNode == headPackageNode) {
1306 // The node does at least have one more package node, but the one to be
1307 // removed is the head. Unless it's a directory, we replace the node
1308 // with a completely new one and let the old one die. This is necessary
1309 // to avoid surprises for clients that have opened/mapped the node.
1310 if (S_ISDIR(packageNode->Mode())) {
1311 unpackingNode->RemovePackageNode(packageNode, ID());
1312 _NotifyNodeChanged(node, kAllStatFields,
1313 OldUnpackingNodeAttributes(headPackageNode));
1314 } else {
1315 // create a new node and transfer the package nodes to it
1316 UnpackingNode* newUnpackingNode;
1317 status_t error = unpackingNode->CloneTransferPackageNodes(
1318 fNextNodeID++, newUnpackingNode);
1319 if (error == B_OK) {
1320 // remove the package node
1321 newUnpackingNode->RemovePackageNode(packageNode, ID());
1323 // remove the old node
1324 _NotifyNodeRemoved(node);
1325 _RemoveNodeAndVNode(node);
1327 // add the new node
1328 newNode = newUnpackingNode->GetNode();
1329 newNodeReference.SetTo(newNode);
1330 newNodeWriteLocker.SetTo(newNode, false);
1332 directory->AddChild(newNode);
1333 fNodes.Insert(newNode);
1334 _NotifyNodeAdded(newNode);
1335 } else {
1336 // There's nothing we can do. Remove the node completely.
1337 _NotifyNodeRemoved(node);
1339 unpackingNode->PrepareForRemoval();
1341 _RemoveNodeAndVNode(node);
1342 nodeRemoved = true;
1345 } else {
1346 // The package node to remove is not the head of the node. This change
1347 // doesn't have any visible effect.
1348 unpackingNode->RemovePackageNode(packageNode, ID());
1351 if (!notify)
1352 return;
1354 // send notifications
1355 if (nodeRemoved) {
1356 notify_entry_removed(ID(), directory->ID(), node->Name(), node->ID());
1357 } else if (packageNode == headPackageNode) {
1358 // The removed package node was the one representing the node.
1359 // Send stat changed notification for directories and entry
1360 // removed + created notifications for files and symlinks.
1361 if (S_ISDIR(packageNode->Mode())) {
1362 notify_stat_changed(ID(), directory->ID(), node->ID(),
1363 kAllStatFields);
1364 // TODO: Actually the attributes might change, too!
1365 } else {
1366 notify_entry_removed(ID(), directory->ID(), node->Name(),
1367 node->ID());
1368 notify_entry_created(ID(), directory->ID(), newNode->Name(),
1369 newNode->ID());
1375 status_t
1376 Volume::_CreateUnpackingNode(mode_t mode, Directory* parent, const String& name,
1377 UnpackingNode*& _node)
1379 UnpackingNode* unpackingNode;
1380 if (S_ISREG(mode) || S_ISLNK(mode))
1381 unpackingNode = new(std::nothrow) UnpackingLeafNode(fNextNodeID++);
1382 else if (S_ISDIR(mode))
1383 unpackingNode = new(std::nothrow) UnpackingDirectory(fNextNodeID++);
1384 else
1385 RETURN_ERROR(B_UNSUPPORTED);
1387 if (unpackingNode == NULL)
1388 RETURN_ERROR(B_NO_MEMORY);
1390 Node* node = unpackingNode->GetNode();
1391 BReference<Node> nodeReference(node, true);
1393 status_t error = node->Init(parent, name);
1394 if (error != B_OK)
1395 RETURN_ERROR(error);
1397 parent->AddChild(node);
1399 fNodes.Insert(node);
1400 nodeReference.Detach();
1401 // we keep the initial node reference for the table
1403 _node = unpackingNode;
1404 return B_OK;
1408 void
1409 Volume::_RemoveNode(Node* node)
1411 // remove from parent
1412 Directory* parent = node->Parent();
1413 parent->RemoveChild(node);
1415 // remove from node table
1416 fNodes.Remove(node);
1417 node->ReleaseReference();
1421 void
1422 Volume::_RemoveNodeAndVNode(Node* node)
1424 // If the node is known to the VFS, we get the vnode, remove it, and put it,
1425 // so that the VFS will discard it as soon as possible (i.e. now, if no one
1426 // else is using it).
1427 NodeWriteLocker nodeWriteLocker(node);
1429 // Remove the node from its parent and the volume. This makes the node
1430 // inaccessible via the get_vnode() and lookup() hooks.
1431 _RemoveNode(node);
1433 bool getVNode = node->IsKnownToVFS();
1435 nodeWriteLocker.Unlock();
1437 // Get a vnode reference, if the node is already known to the VFS.
1438 Node* dummyNode;
1439 if (getVNode && GetVNode(node->ID(), dummyNode) == B_OK) {
1440 // TODO: There still is a race condition here which we can't avoid
1441 // without more help from the VFS. Right after we drop the write
1442 // lock a vnode for the node could be discarded by the VFS. At that
1443 // point another thread trying to get the vnode by ID would create
1444 // a vnode, mark it busy and call our get_vnode() hook. It would
1445 // block since we (i.e. the package loader thread executing this
1446 // method) still have the volume write lock. Our get_vnode() call
1447 // would block, since it finds the vnode marked busy. It times out
1448 // eventually, but until then a good deal of FS operations might
1449 // block as well due to us holding the volume lock and probably
1450 // several node locks as well. A get_vnode*() variant (e.g.
1451 // get_vnode_etc() with flags parameter) that wouldn't block and
1452 // only get the vnode, if already loaded and non-busy, would be
1453 // perfect here.
1454 RemoveVNode(node->ID());
1455 PutVNode(node->ID());
1460 status_t
1461 Volume::_LoadPackage(PackagesDirectory* packagesDirectory, const char* name,
1462 Package*& _package)
1464 // Find the package -- check the specified packages directory and iterate
1465 // toward the newer states.
1466 struct stat st;
1467 for (;;) {
1468 if (packagesDirectory == NULL)
1469 return B_ENTRY_NOT_FOUND;
1471 if (fstatat(packagesDirectory->DirectoryFD(), name, &st, 0) == 0) {
1472 // check whether the entry is a file
1473 if (!S_ISREG(st.st_mode))
1474 return B_BAD_VALUE;
1475 break;
1478 packagesDirectory = fPackagesDirectories.GetPrevious(packagesDirectory);
1481 // create a package
1482 Package* package = new(std::nothrow) Package(this, packagesDirectory,
1483 st.st_dev, st.st_ino);
1484 if (package == NULL)
1485 RETURN_ERROR(B_NO_MEMORY);
1486 BReference<Package> packageReference(package, true);
1488 status_t error = package->Init(name);
1489 if (error != B_OK)
1490 return error;
1492 error = package->Load(fPackageSettings);
1493 if (error != B_OK)
1494 return error;
1496 _package = packageReference.Detach();
1497 return B_OK;
1501 status_t
1502 Volume::_ChangeActivation(ActivationChangeRequest& request)
1504 uint32 itemCount = request.CountItems();
1505 if (itemCount == 0)
1506 return B_OK;
1508 // first check the request
1509 int32 newPackageCount = 0;
1510 int32 oldPackageCount = 0;
1512 VolumeReadLocker volumeLocker(this);
1514 for (uint32 i = 0; i < itemCount; i++) {
1515 PackageFSActivationChangeItem* item = request.ItemAt(i);
1516 if (item->parentDeviceID != fPackagesDirectory->DeviceID()
1517 || item->parentDirectoryID != fPackagesDirectory->NodeID()) {
1518 ERROR("Volume::_ChangeActivation(): mismatching packages "
1519 "directory\n");
1520 RETURN_ERROR(B_BAD_VALUE);
1523 Package* package = _FindPackage(item->name);
1524 // TODO: We should better look up the package by node_ref!
1525 if (item->type == PACKAGE_FS_ACTIVATE_PACKAGE) {
1526 if (package != NULL) {
1527 ERROR("Volume::_ChangeActivation(): package to activate "
1528 "already activated: \"%s\"\n", item->name);
1529 RETURN_ERROR(B_BAD_VALUE);
1531 newPackageCount++;
1532 } else if (item->type == PACKAGE_FS_DEACTIVATE_PACKAGE) {
1533 if (package == NULL) {
1534 ERROR("Volume::_ChangeActivation(): package to deactivate "
1535 "not found: \"%s\"\n", item->name);
1536 RETURN_ERROR(B_BAD_VALUE);
1538 oldPackageCount++;
1539 } else if (item->type == PACKAGE_FS_REACTIVATE_PACKAGE) {
1540 if (package == NULL) {
1541 ERROR("Volume::_ChangeActivation(): package to reactivate "
1542 "not found: \"%s\"\n", item->name);
1543 RETURN_ERROR(B_BAD_VALUE);
1545 oldPackageCount++;
1546 newPackageCount++;
1547 } else
1548 RETURN_ERROR(B_BAD_VALUE);
1551 INFORM("Volume::_ChangeActivation(): %" B_PRId32 " new packages, %" B_PRId32 " old packages\n", newPackageCount, oldPackageCount);
1553 // Things look good so far -- allocate reference arrays for the packages to
1554 // add and remove.
1555 BReference<Package>* newPackageReferences
1556 = new(std::nothrow) BReference<Package>[newPackageCount];
1557 if (newPackageReferences == NULL)
1558 RETURN_ERROR(B_NO_MEMORY);
1559 ArrayDeleter<BReference<Package> > newPackageReferencesDeleter(
1560 newPackageReferences);
1562 BReference<Package>* oldPackageReferences
1563 = new(std::nothrow) BReference<Package>[oldPackageCount];
1564 if (oldPackageReferences == NULL)
1565 RETURN_ERROR(B_NO_MEMORY);
1566 ArrayDeleter<BReference<Package> > oldPackageReferencesDeleter(
1567 oldPackageReferences);
1569 // load all new packages
1570 int32 newPackageIndex = 0;
1571 for (uint32 i = 0; i < itemCount; i++) {
1572 PackageFSActivationChangeItem* item = request.ItemAt(i);
1574 if (item->type != PACKAGE_FS_ACTIVATE_PACKAGE
1575 && item->type != PACKAGE_FS_REACTIVATE_PACKAGE) {
1576 continue;
1579 Package* package;
1580 status_t error = _LoadPackage(fPackagesDirectory, item->name, package);
1581 if (error != B_OK) {
1582 ERROR("Volume::_ChangeActivation(): failed to load package "
1583 "\"%s\"\n", item->name);
1584 RETURN_ERROR(error);
1587 newPackageReferences[newPackageIndex++].SetTo(package, true);
1590 // apply the changes
1591 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
1592 VolumeWriteLocker volumeLocker(this);
1593 // TODO: Add a change counter to Volume, so we can easily check whether
1594 // everything is still the same.
1596 // remove the old packages
1597 int32 oldPackageIndex = 0;
1598 for (uint32 i = 0; i < itemCount; i++) {
1599 PackageFSActivationChangeItem* item = request.ItemAt(i);
1601 if (item->type != PACKAGE_FS_DEACTIVATE_PACKAGE
1602 && item->type != PACKAGE_FS_REACTIVATE_PACKAGE) {
1603 continue;
1606 Package* package = _FindPackage(item->name);
1607 // TODO: We should better look up the package by node_ref!
1608 oldPackageReferences[oldPackageIndex++].SetTo(package);
1609 _RemovePackageContent(package, NULL, true);
1610 _RemovePackage(package);
1611 INFORM("package \"%s\" deactivated\n", package->FileName().Data());
1613 // TODO: Since package removal cannot fail, consider adding the new packages
1614 // first. The reactivation case may make that problematic, since two packages
1615 // with the same name would be active after activating the new one. Check!
1617 // add the new packages
1618 status_t error = B_OK;
1619 for (newPackageIndex = 0; newPackageIndex < newPackageCount;
1620 newPackageIndex++) {
1621 Package* package = newPackageReferences[newPackageIndex];
1622 _AddPackage(package);
1624 // add the package to the node tree
1625 error = _AddPackageContent(package, true);
1626 if (error != B_OK) {
1627 _RemovePackage(package);
1628 break;
1630 INFORM("package \"%s\" activated\n", package->FileName().Data());
1633 // Try to roll back the changes, if an error occurred.
1634 if (error != B_OK) {
1635 for (int32 i = newPackageIndex - 1; i >= 0; i--) {
1636 Package* package = newPackageReferences[i];
1637 _RemovePackageContent(package, NULL, true);
1638 _RemovePackage(package);
1641 for (int32 i = oldPackageCount - 1; i >= 0; i--) {
1642 Package* package = oldPackageReferences[i];
1643 _AddPackage(package);
1645 if (_AddPackageContent(package, true) != B_OK) {
1646 // nothing we can do here
1647 ERROR("Volume::_ChangeActivation(): failed to roll back "
1648 "deactivation of package \"%s\" after error\n",
1649 package->FileName().Data());
1650 _RemovePackage(package);
1655 return error;
1659 status_t
1660 Volume::_InitMountType(const char* mountType)
1662 if (mountType == NULL)
1663 fMountType = PACKAGE_FS_MOUNT_TYPE_CUSTOM;
1664 else if (strcmp(mountType, "system") == 0)
1665 fMountType = PACKAGE_FS_MOUNT_TYPE_SYSTEM;
1666 else if (strcmp(mountType, "home") == 0)
1667 fMountType = PACKAGE_FS_MOUNT_TYPE_HOME;
1668 else if (strcmp(mountType, "custom") == 0)
1669 fMountType = PACKAGE_FS_MOUNT_TYPE_CUSTOM;
1670 else
1671 RETURN_ERROR(B_BAD_VALUE);
1673 return B_OK;
1677 status_t
1678 Volume::_CreateShineThroughDirectory(Directory* parent, const char* name,
1679 Directory*& _directory)
1681 ShineThroughDirectory* directory = new(std::nothrow) ShineThroughDirectory(
1682 fNextNodeID++);
1683 if (directory == NULL)
1684 RETURN_ERROR(B_NO_MEMORY);
1685 BReference<ShineThroughDirectory> directoryReference(directory, true);
1687 String nameString;
1688 if (!nameString.SetTo(name))
1689 RETURN_ERROR(B_NO_MEMORY);
1691 status_t error = directory->Init(parent, nameString);
1692 if (error != B_OK)
1693 RETURN_ERROR(error);
1695 parent->AddChild(directory);
1697 fNodes.Insert(directory);
1698 directoryReference.Detach();
1699 // we keep the initial node reference for the table
1701 _directory = directory;
1702 return B_OK;
1706 status_t
1707 Volume::_CreateShineThroughDirectories(const char* shineThroughSetting)
1709 // get the directories to map
1710 const char* const* directories = NULL;
1712 if (shineThroughSetting == NULL) {
1713 // nothing specified -- derive from mount type
1714 switch (fMountType) {
1715 case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
1716 case PACKAGE_FS_MOUNT_TYPE_HOME:
1717 directories = kShineThroughDirectories;
1718 break;
1719 case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
1720 return B_OK;
1721 case PACKAGE_FS_MOUNT_TYPE_ENUM_COUNT:
1722 return B_BAD_VALUE;
1724 } else if (strcmp(shineThroughSetting, "system") == 0)
1725 directories = kShineThroughDirectories;
1726 else if (strcmp(shineThroughSetting, "home") == 0)
1727 directories = kShineThroughDirectories;
1728 else if (strcmp(shineThroughSetting, "none") == 0)
1729 directories = NULL;
1730 else
1731 RETURN_ERROR(B_BAD_VALUE);
1733 if (directories == NULL)
1734 return B_OK;
1736 // iterate through the directory list and create the directories
1737 while (const char* directoryName = *(directories++)) {
1738 // create the directory
1739 Directory* directory;
1740 status_t error = _CreateShineThroughDirectory(fRootDirectory,
1741 directoryName, directory);
1742 if (error != B_OK)
1743 RETURN_ERROR(error);
1746 return B_OK;
1750 status_t
1751 Volume::_PublishShineThroughDirectories()
1753 // Iterate through the root directory children and bind the shine-through
1754 // directories to the respective mount point subdirectories.
1755 Node* nextNode;
1756 for (Node* node = fRootDirectory->FirstChild(); node != NULL;
1757 node = nextNode) {
1758 nextNode = fRootDirectory->NextChild(node);
1760 // skip anything but shine-through directories
1761 ShineThroughDirectory* directory
1762 = dynamic_cast<ShineThroughDirectory*>(node);
1763 if (directory == NULL)
1764 continue;
1766 const char* directoryName = directory->Name();
1768 // look up the mount point subdirectory
1769 struct vnode* vnode;
1770 status_t error = vfs_entry_ref_to_vnode(fMountPoint.deviceID,
1771 fMountPoint.nodeID, directoryName, &vnode);
1772 if (error != B_OK) {
1773 dprintf("packagefs: Failed to get shine-through directory \"%s\": "
1774 "%s\n", directoryName, strerror(error));
1775 _RemoveNode(directory);
1776 continue;
1778 CObjectDeleter<struct vnode> vnodePutter(vnode, &vfs_put_vnode);
1780 // stat it
1781 struct stat st;
1782 error = vfs_stat_vnode(vnode, &st);
1783 if (error != B_OK) {
1784 dprintf("packagefs: Failed to stat shine-through directory \"%s\": "
1785 "%s\n", directoryName, strerror(error));
1786 _RemoveNode(directory);
1787 continue;
1790 if (!S_ISDIR(st.st_mode)) {
1791 dprintf("packagefs: Shine-through entry \"%s\" is not a "
1792 "directory\n", directoryName);
1793 _RemoveNode(directory);
1794 continue;
1797 // publish the vnode, so the VFS will find it without asking us
1798 directory->AcquireReference();
1799 error = PublishVNode(directory);
1800 if (error != B_OK) {
1801 directory->ReleaseReference();
1802 _RemoveNode(directory);
1803 RETURN_ERROR(error);
1806 // bind the directory
1807 error = vfs_bind_mount_directory(st.st_dev, st.st_ino, fFSVolume->id,
1808 directory->ID());
1810 PutVNode(directory->ID());
1811 // release our reference again -- on success
1812 // vfs_bind_mount_directory() got one
1814 if (error != B_OK)
1815 RETURN_ERROR(error);
1818 return B_OK;
1822 status_t
1823 Volume::_AddPackageLinksDirectory()
1825 // called when mounting, so we don't need to lock the volume
1827 PackageLinksDirectory* packageLinksDirectory
1828 = fPackageFSRoot->GetPackageLinksDirectory();
1830 NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory);
1831 NodeWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory);
1833 packageLinksDirectory->SetParent(fRootDirectory);
1834 fRootDirectory->AddChild(packageLinksDirectory);
1836 _AddPackageLinksNode(packageLinksDirectory);
1838 packageLinksDirectory->SetListener(this);
1840 return B_OK;
1844 void
1845 Volume::_RemovePackageLinksDirectory()
1847 PackageLinksDirectory* packageLinksDirectory
1848 = fPackageFSRoot->GetPackageLinksDirectory();
1850 VolumeWriteLocker volumeLocker(this);
1851 NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory);
1852 NodeWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory);
1854 if (packageLinksDirectory->Parent() == fRootDirectory) {
1855 packageLinksDirectory->SetListener(NULL);
1856 fRootDirectory->RemoveChild(packageLinksDirectory);
1857 packageLinksDirectory->SetParent(NULL);
1862 void
1863 Volume::_AddPackageLinksNode(Node* node)
1865 node->SetID(fNextNodeID++);
1867 fNodes.Insert(node);
1868 node->AcquireReference();
1870 // If this is a directory, recursively add descendants. The directory tree
1871 // for the package links isn't deep, so we can do recursion.
1872 if (Directory* directory = dynamic_cast<Directory*>(node)) {
1873 for (Node* child = directory->FirstChild(); child != NULL;
1874 child = directory->NextChild(child)) {
1875 NodeWriteLocker childWriteLocker(child);
1876 _AddPackageLinksNode(child);
1882 void
1883 Volume::_RemovePackageLinksNode(Node* node)
1885 // If this is a directory, recursively remove descendants. The directory
1886 // tree for the package links isn't deep, so we can do recursion.
1887 if (Directory* directory = dynamic_cast<Directory*>(node)) {
1888 for (Node* child = directory->FirstChild(); child != NULL;
1889 child = directory->NextChild(child)) {
1890 NodeWriteLocker childWriteLocker(child);
1891 _RemovePackageLinksNode(child);
1895 fNodes.Remove(node);
1896 node->ReleaseReference();
1900 inline Volume*
1901 Volume::_SystemVolumeIfNotSelf() const
1903 if (Volume* systemVolume = fPackageFSRoot->SystemVolume())
1904 return systemVolume == this ? NULL : systemVolume;
1905 return NULL;
1909 void
1910 Volume::_NotifyNodeAdded(Node* node)
1912 Node* key = node;
1914 for (int i = 0; i < 2; i++) {
1915 if (NodeListener* listener = fNodeListeners.Lookup(key)) {
1916 NodeListener* last = listener->PreviousNodeListener();
1918 while (true) {
1919 NodeListener* next = listener->NextNodeListener();
1921 listener->NodeAdded(node);
1923 if (listener == last)
1924 break;
1926 listener = next;
1930 key = NULL;
1935 void
1936 Volume::_NotifyNodeRemoved(Node* node)
1938 Node* key = node;
1940 for (int i = 0; i < 2; i++) {
1941 if (NodeListener* listener = fNodeListeners.Lookup(key)) {
1942 NodeListener* last = listener->PreviousNodeListener();
1944 while (true) {
1945 NodeListener* next = listener->NextNodeListener();
1947 listener->NodeRemoved(node);
1949 if (listener == last)
1950 break;
1952 listener = next;
1956 key = NULL;
1961 void
1962 Volume::_NotifyNodeChanged(Node* node, uint32 statFields,
1963 const OldNodeAttributes& oldAttributes)
1965 Node* key = node;
1967 for (int i = 0; i < 2; i++) {
1968 if (NodeListener* listener = fNodeListeners.Lookup(key)) {
1969 NodeListener* last = listener->PreviousNodeListener();
1971 while (true) {
1972 NodeListener* next = listener->NextNodeListener();
1974 listener->NodeChanged(node, statFields, oldAttributes);
1976 if (listener == last)
1977 break;
1979 listener = next;
1983 key = NULL;