1 /*
2 * Copyright 2009-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include "Volume.h"
8
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>
15
16 #include <new>
17
18 #include <AppDefs.h>
19 #include <driver_settings.h>
20 #include <KernelExport.h>
21 #include <NodeMonitor.h>
22 #include <package/PackageInfoAttributes.h>
23
24 #include <AutoDeleter.h>
25 #include <AutoDeleterPosix.h>
26 #include <AutoDeleterDrivers.h>
27 #include <PackagesDirectoryDefs.h>
28
29 #include <vfs.h>
30
31 #include "AttributeIndex.h"
32 #include "DebugSupport.h"
33 #include "kernel_interface.h"
34 #include "LastModifiedIndex.h"
35 #include "NameIndex.h"
36 #include "OldUnpackingNodeAttributes.h"
37 #include "PackageFSRoot.h"
38 #include "PackageLinkDirectory.h"
39 #include "PackageLinksDirectory.h"
40 #include "Resolvable.h"
41 #include "SizeIndex.h"
42 #include "UnpackingLeafNode.h"
43 #include "UnpackingDirectory.h"
44 #include "Utils.h"
45 #include "Version.h"
46
47
48 // node ID of the root directory
49 static const ino_t kRootDirectoryID = 1;
50
51 static const uint32 kAllStatFields = B_STAT_MODE | B_STAT_UID | B_STAT_GID
52 | B_STAT_SIZE | B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME
53 | B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME;
54
55 // shine-through directories
56 const char* const kShineThroughDirectories[] = {
57 "cache", "non-packaged", "packages", "settings", "var", NULL
58 };
59
60 // sanity limit for activation change request
61 const size_t kMaxActivationRequestSize = 10 * 1024 * 1024;
62
63 // sanity limit for activation file size
64 const size_t kMaxActivationFileSize = 10 * 1024 * 1024;
65
66 static const char* const kAdministrativeDirectoryName
67 = PACKAGES_DIRECTORY_ADMIN_DIRECTORY;
68 static const char* const kActivationFileName
69 = PACKAGES_DIRECTORY_ACTIVATION_FILE;
70 static const char* const kActivationFilePath
71 = PACKAGES_DIRECTORY_ADMIN_DIRECTORY "/"
72 PACKAGES_DIRECTORY_ACTIVATION_FILE;
73
74
75 // #pragma mark - ShineThroughDirectory
76
77
78 struct Volume::ShineThroughDirectory : public Directory {
ShineThroughDirectoryVolume::ShineThroughDirectory79 ShineThroughDirectory(ino_t id)
80 :
81 Directory(id)
82 {
83 get_real_time(fModifiedTime);
84 }
85
ModifiedTimeVolume::ShineThroughDirectory86 virtual timespec ModifiedTime() const
87 {
88 return fModifiedTime;
89 }
90
91 private:
92 timespec fModifiedTime;
93 };
94
95
96 // #pragma mark - ActivationChangeRequest
97
98
99 struct Volume::ActivationChangeRequest {
100 public:
ActivationChangeRequestVolume::ActivationChangeRequest101 ActivationChangeRequest()
102 :
103 fRequest(NULL),
104 fRequestSize(0)
105 {
106 }
107
~ActivationChangeRequestVolume::ActivationChangeRequest108 ~ActivationChangeRequest()
109 {
110 free(fRequest);
111 }
112
InitVolume::ActivationChangeRequest113 status_t Init(const void* userRequest, size_t requestSize)
114 {
115 // copy request to kernel
116 if (requestSize > kMaxActivationRequestSize)
117 RETURN_ERROR(B_BAD_VALUE);
118
119 fRequest = (PackageFSActivationChangeRequest*)malloc(requestSize);
120 if (fRequest == NULL)
121 RETURN_ERROR(B_NO_MEMORY);
122 fRequestSize = requestSize;
123
124 status_t error = user_memcpy(fRequest, userRequest, fRequestSize);
125 if (error != B_OK)
126 RETURN_ERROR(error);
127
128 uint32 itemCount = fRequest->itemCount;
129 const char* requestEnd = (const char*)fRequest + requestSize;
130 if (&fRequest->items[itemCount] > (void*)requestEnd)
131 RETURN_ERROR(B_BAD_VALUE);
132
133 // adjust the item name pointers and check their validity
134 addr_t nameDelta = (addr_t)fRequest - (addr_t)userRequest;
135 for (uint32 i = 0; i < itemCount; i++) {
136 PackageFSActivationChangeItem& item = fRequest->items[i];
137 item.name += nameDelta;
138 if (item.name < (char*)fRequest || item.name >= requestEnd)
139 RETURN_ERROR(B_BAD_VALUE);
140 size_t maxNameSize = requestEnd - item.name;
141 if (strnlen(item.name, maxNameSize) == maxNameSize)
142 RETURN_ERROR(B_BAD_VALUE);
143 }
144
145 return B_OK;
146 }
147
CountItemsVolume::ActivationChangeRequest148 uint32 CountItems() const
149 {
150 return fRequest->itemCount;
151 }
152
ItemAtVolume::ActivationChangeRequest153 PackageFSActivationChangeItem* ItemAt(uint32 index) const
154 {
155 return index < CountItems() ? &fRequest->items[index] : NULL;
156 }
157
158 private:
159 PackageFSActivationChangeRequest* fRequest;
160 size_t fRequestSize;
161 };
162
163
164 // #pragma mark - Volume
165
166
Volume(fs_volume * fsVolume)167 Volume::Volume(fs_volume* fsVolume)
168 :
169 fFSVolume(fsVolume),
170 fRootDirectory(NULL),
171 fPackageFSRoot(NULL),
172 fPackagesDirectory(NULL),
173 fPackagesDirectories(),
174 fPackagesDirectoriesByNodeRef(),
175 fPackageSettings(),
176 fNextNodeID(kRootDirectoryID + 1)
177 {
178 rw_lock_init(&fLock, "packagefs volume");
179 }
180
181
~Volume()182 Volume::~Volume()
183 {
184 // remove the packages from the node tree
185 {
186 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
187 VolumeWriteLocker volumeLocker(this);
188 for (PackageFileNameHashTable::Iterator it = fPackages.GetIterator();
189 Package* package = it.Next();) {
190 _RemovePackageContent(package, NULL, false);
191 }
192 }
193
194 // delete the packages
195 _RemoveAllPackages();
196
197 // delete all indices
198 Index* index = fIndices.Clear(true);
199 while (index != NULL) {
200 Index* next = index->IndexHashLink();
201 delete index;
202 index = next;
203 }
204
205 // remove all nodes from the ID hash table
206 Node* node = fNodes.Clear(true);
207 while (node != NULL) {
208 Node* next = node->IDHashTableNext();
209 node->ReleaseReference();
210 node = next;
211 }
212
213 if (fPackageFSRoot != NULL) {
214 if (this == fPackageFSRoot->SystemVolume())
215 _RemovePackageLinksDirectory();
216
217 fPackageFSRoot->UnregisterVolume(this);
218 }
219
220 if (fRootDirectory != NULL)
221 fRootDirectory->ReleaseReference();
222
223 while (PackagesDirectory* directory = fPackagesDirectories.RemoveHead())
224 directory->ReleaseReference();
225
226 rw_lock_destroy(&fLock);
227 }
228
229
230 status_t
Mount(const char * parameterString)231 Volume::Mount(const char* parameterString)
232 {
233 // init the hash tables
234 status_t error = fPackagesDirectoriesByNodeRef.Init();
235 if (error != B_OK)
236 RETURN_ERROR(error);
237
238 error = fNodes.Init();
239 if (error != B_OK)
240 RETURN_ERROR(error);
241
242 error = fNodeListeners.Init();
243 if (error != B_OK)
244 RETURN_ERROR(error);
245
246 error = fPackages.Init();
247 if (error != B_OK)
248 RETURN_ERROR(error);
249
250 error = fIndices.Init();
251 if (error != B_OK)
252 RETURN_ERROR(error);
253
254 // create the name index
255 {
256 NameIndex* index = new(std::nothrow) NameIndex;
257 if (index == NULL)
258 RETURN_ERROR(B_NO_MEMORY);
259
260 error = index->Init(this);
261 if (error != B_OK) {
262 delete index;
263 RETURN_ERROR(error);
264 }
265
266 fIndices.Insert(index);
267 }
268
269 // create the size index
270 {
271 SizeIndex* index = new(std::nothrow) SizeIndex;
272 if (index == NULL)
273 RETURN_ERROR(B_NO_MEMORY);
274
275 error = index->Init(this);
276 if (error != B_OK) {
277 delete index;
278 RETURN_ERROR(error);
279 }
280
281 fIndices.Insert(index);
282 }
283
284 // create the last modified index
285 {
286 LastModifiedIndex* index = new(std::nothrow) LastModifiedIndex;
287 if (index == NULL)
288 RETURN_ERROR(B_NO_MEMORY);
289
290 error = index->Init(this);
291 if (error != B_OK) {
292 delete index;
293 RETURN_ERROR(error);
294 }
295
296 fIndices.Insert(index);
297 }
298
299 // create a BEOS:APP_SIG index
300 {
301 AttributeIndex* index = new(std::nothrow) AttributeIndex;
302 if (index == NULL)
303 RETURN_ERROR(B_NO_MEMORY);
304
305 error = index->Init(this, "BEOS:APP_SIG", B_MIME_STRING_TYPE, 0);
306 if (error != B_OK) {
307 delete index;
308 RETURN_ERROR(error);
309 }
310
311 fIndices.Insert(index);
312 }
313
314 // get the mount parameters
315 const char* packages = NULL;
316 const char* volumeName = NULL;
317 const char* mountType = NULL;
318 const char* shineThrough = NULL;
319 const char* packagesState = NULL;
320
321 DriverSettingsUnloader parameterHandle(
322 parse_driver_settings_string(parameterString));
323 if (parameterHandle.IsSet()) {
324 packages = get_driver_parameter(parameterHandle.Get(), "packages",
325 NULL, NULL);
326 volumeName = get_driver_parameter(parameterHandle.Get(), "volume-name",
327 NULL, NULL);
328 mountType = get_driver_parameter(parameterHandle.Get(), "type", NULL,
329 NULL);
330 shineThrough = get_driver_parameter(parameterHandle.Get(),
331 "shine-through", NULL, NULL);
332 packagesState = get_driver_parameter(parameterHandle.Get(), "state",
333 NULL, NULL);
334 }
335
336 if (packages != NULL && packages[0] == '\0') {
337 FATAL("invalid package folder ('packages' parameter)!\n");
338 RETURN_ERROR(B_BAD_VALUE);
339 }
340
341 error = _InitMountType(mountType);
342 if (error != B_OK) {
343 FATAL("invalid mount type: \"%s\"\n", mountType);
344 RETURN_ERROR(error);
345 }
346
347 // get our mount point
348 error = vfs_get_mount_point(fFSVolume->id, &fMountPoint.deviceID,
349 &fMountPoint.nodeID);
350 if (error != B_OK)
351 RETURN_ERROR(error);
352
353 // load package settings
354 error = fPackageSettings.Load(fMountPoint.deviceID, fMountPoint.nodeID,
355 fMountType);
356 // abort only in case of serious issues (memory shortage)
357 if (error == B_NO_MEMORY)
358 RETURN_ERROR(error);
359
360 // create package domain
361 fPackagesDirectory = new(std::nothrow) PackagesDirectory;
362 if (fPackagesDirectory == NULL)
363 RETURN_ERROR(B_NO_MEMORY);
364 fPackagesDirectories.Add(fPackagesDirectory);
365 fPackagesDirectoriesByNodeRef.Insert(fPackagesDirectory);
366
367 struct stat st;
368 error = fPackagesDirectory->Init(packages, fMountPoint.deviceID,
369 fMountPoint.nodeID, st);
370 if (error != B_OK)
371 RETURN_ERROR(error);
372
373 // If a packages state has been specified, load the needed states.
374 if (packagesState != NULL) {
375 error = _LoadOldPackagesStates(packagesState);
376 if (error != B_OK)
377 RETURN_ERROR(error);
378 }
379
380 // If no volume name is given, infer it from the mount type.
381 if (volumeName == NULL) {
382 switch (fMountType) {
383 case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
384 volumeName = "system";
385 break;
386 case PACKAGE_FS_MOUNT_TYPE_HOME:
387 volumeName = "config";
388 break;
389 case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
390 default:
391 volumeName = "Package FS";
392 break;
393 }
394 }
395
396 String volumeNameString;
397 if (!volumeNameString.SetTo(volumeName))
398 RETURN_ERROR(B_NO_MEMORY);
399
400 // create the root node
401 fRootDirectory
402 = new ::RootDirectory(kRootDirectoryID, st.st_mtim);
403 if (fRootDirectory == NULL)
404 RETURN_ERROR(B_NO_MEMORY);
405 fRootDirectory->Init(volumeNameString);
406 fNodes.Insert(fRootDirectory);
407 fRootDirectory->AcquireReference();
408 // one reference for the table
409
410 // register with packagefs root
411 error = ::PackageFSRoot::RegisterVolume(this);
412 if (error != B_OK)
413 RETURN_ERROR(error);
414
415 if (this == fPackageFSRoot->SystemVolume()) {
416 error = _AddPackageLinksDirectory();
417 if (error != B_OK)
418 RETURN_ERROR(error);
419 }
420
421 // create shine-through directories
422 error = _CreateShineThroughDirectories(shineThrough);
423 if (error != B_OK)
424 RETURN_ERROR(error);
425
426 // add initial packages
427 error = _AddInitialPackages();
428 if (error != B_OK)
429 RETURN_ERROR(error);
430
431 // publish the root node
432 fRootDirectory->AcquireReference();
433 error = PublishVNode(fRootDirectory);
434 if (error != B_OK) {
435 fRootDirectory->ReleaseReference();
436 RETURN_ERROR(error);
437 }
438
439 // bind and publish the shine-through directories
440 error = _PublishShineThroughDirectories();
441 if (error != B_OK)
442 RETURN_ERROR(error);
443
444 StringPool::DumpUsageStatistics();
445
446 return B_OK;
447 }
448
449
450 void
Unmount()451 Volume::Unmount()
452 {
453 }
454
455
456 status_t
IOCtl(Node * node,uint32 operation,void * buffer,size_t size)457 Volume::IOCtl(Node* node, uint32 operation, void* buffer, size_t size)
458 {
459 switch (operation) {
460 case PACKAGE_FS_OPERATION_GET_VOLUME_INFO:
461 {
462 if (size < sizeof(PackageFSVolumeInfo))
463 RETURN_ERROR(B_BAD_VALUE);
464
465 PackageFSVolumeInfo* userVolumeInfo
466 = (PackageFSVolumeInfo*)buffer;
467
468 VolumeReadLocker volumeReadLocker(this);
469
470 PackageFSVolumeInfo volumeInfo;
471 volumeInfo.mountType = fMountType;
472 volumeInfo.rootDeviceID = fPackageFSRoot->DeviceID();
473 volumeInfo.rootDirectoryID = fPackageFSRoot->NodeID();
474 volumeInfo.packagesDirectoryCount = fPackagesDirectories.Count();
475
476 status_t error = user_memcpy(userVolumeInfo, &volumeInfo,
477 sizeof(volumeInfo));
478 if (error != B_OK)
479 RETURN_ERROR(error);
480
481 uint32 directoryIndex = 0;
482 for (PackagesDirectoryList::Iterator it
483 = fPackagesDirectories.GetIterator();
484 PackagesDirectory* directory = it.Next();
485 directoryIndex++) {
486 PackageFSDirectoryInfo info;
487 info.deviceID = directory->DeviceID();
488 info.nodeID = directory->NodeID();
489
490 PackageFSDirectoryInfo* userInfo
491 = userVolumeInfo->packagesDirectoryInfos + directoryIndex;
492 if (addr_t(userInfo + 1) > (addr_t)buffer + size)
493 break;
494
495 if (user_memcpy(userInfo, &info, sizeof(info)) != B_OK)
496 return B_BAD_ADDRESS;
497 }
498
499 return B_OK;
500 }
501
502 case PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS:
503 {
504 if (size < sizeof(PackageFSGetPackageInfosRequest))
505 RETURN_ERROR(B_BAD_VALUE);
506
507 PackageFSGetPackageInfosRequest* request
508 = (PackageFSGetPackageInfosRequest*)buffer;
509
510 VolumeReadLocker volumeReadLocker(this);
511
512 addr_t bufferEnd = (addr_t)buffer + size;
513 uint32 packageCount = fPackages.CountElements();
514 char* nameBuffer = (char*)(request->infos + packageCount);
515
516 uint32 packageIndex = 0;
517 for (PackageFileNameHashTable::Iterator it
518 = fPackages.GetIterator(); it.HasNext();
519 packageIndex++) {
520 Package* package = it.Next();
521 PackageFSPackageInfo info;
522 info.packageDeviceID = package->DeviceID();
523 info.packageNodeID = package->NodeID();
524 PackagesDirectory* directory = package->Directory();
525 info.directoryDeviceID = directory->DeviceID();
526 info.directoryNodeID = directory->NodeID();
527 info.name = nameBuffer;
528
529 PackageFSPackageInfo* userInfo = request->infos + packageIndex;
530 if (addr_t(userInfo + 1) <= bufferEnd) {
531 if (user_memcpy(userInfo, &info, sizeof(info)) != B_OK)
532 return B_BAD_ADDRESS;
533 }
534
535 const char* name = package->FileName();
536 size_t nameSize = strlen(name) + 1;
537 char* nameEnd = nameBuffer + nameSize;
538 if ((addr_t)nameEnd <= bufferEnd) {
539 if (user_memcpy(nameBuffer, name, nameSize) != B_OK)
540 return B_BAD_ADDRESS;
541 }
542 nameBuffer = nameEnd;
543 }
544
545 PackageFSGetPackageInfosRequest header;
546 header.bufferSize = nameBuffer - (char*)request;
547 header.packageCount = packageCount;
548 size_t headerSize = (char*)&request->infos - (char*)request;
549 RETURN_ERROR(user_memcpy(request, &header, headerSize));
550 }
551
552 case PACKAGE_FS_OPERATION_CHANGE_ACTIVATION:
553 {
554 ActivationChangeRequest request;
555 status_t error = request.Init(buffer, size);
556 if (error != B_OK)
557 RETURN_ERROR(B_BAD_VALUE);
558
559 return _ChangeActivation(request);
560 }
561
562 default:
563 return B_BAD_VALUE;
564 }
565 }
566
567
568 void
AddNodeListener(NodeListener * listener,Node * node)569 Volume::AddNodeListener(NodeListener* listener, Node* node)
570 {
571 ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
572 ASSERT(!listener->IsListening());
573
574 listener->StartedListening(node);
575
576 if (NodeListener* list = fNodeListeners.Lookup(node))
577 list->AddNodeListener(listener);
578 else
579 fNodeListeners.Insert(listener);
580 }
581
582
583 void
RemoveNodeListener(NodeListener * listener)584 Volume::RemoveNodeListener(NodeListener* listener)
585 {
586 ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
587 ASSERT(listener->IsListening());
588
589 Node* node = listener->ListenedNode();
590
591 if (NodeListener* next = listener->RemoveNodeListener()) {
592 // list not empty yet -- if we removed the head, add a new head to the
593 // hash table
594 NodeListener* list = fNodeListeners.Lookup(node);
595 if (list == listener) {
596 fNodeListeners.Remove(listener);
597 fNodeListeners.Insert(next);
598 }
599 } else
600 fNodeListeners.Remove(listener);
601
602 listener->StoppedListening();
603 }
604
605
606 void
AddQuery(Query * query)607 Volume::AddQuery(Query* query)
608 {
609 ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
610 fQueries.Add(query);
611 }
612
613
614 void
RemoveQuery(Query * query)615 Volume::RemoveQuery(Query* query)
616 {
617 ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
618 fQueries.Remove(query);
619 }
620
621
622 void
UpdateLiveQueries(Node * node,const char * attribute,int32 type,const void * oldKey,size_t oldLength,const void * newKey,size_t newLength)623 Volume::UpdateLiveQueries(Node* node, const char* attribute, int32 type,
624 const void* oldKey, size_t oldLength, const void* newKey,
625 size_t newLength)
626 {
627 for (QueryList::Iterator it = fQueries.GetIterator();
628 Query* query = it.Next();) {
629 query->LiveUpdate(node, attribute, type, oldKey, oldLength, newKey,
630 newLength);
631 }
632 }
633
634
635 status_t
GetVNode(ino_t nodeID,Node * & _node)636 Volume::GetVNode(ino_t nodeID, Node*& _node)
637 {
638 return get_vnode(fFSVolume, nodeID, (void**)&_node);
639 }
640
641
642 status_t
PutVNode(ino_t nodeID)643 Volume::PutVNode(ino_t nodeID)
644 {
645 return put_vnode(fFSVolume, nodeID);
646 }
647
648
649 status_t
RemoveVNode(ino_t nodeID)650 Volume::RemoveVNode(ino_t nodeID)
651 {
652 return remove_vnode(fFSVolume, nodeID);
653 }
654
655
656 status_t
PublishVNode(Node * node)657 Volume::PublishVNode(Node* node)
658 {
659 return publish_vnode(fFSVolume, node->ID(), node, &gPackageFSVnodeOps,
660 node->Mode() & S_IFMT, 0);
661 }
662
663
664 void
PackageLinkNodeAdded(Node * node)665 Volume::PackageLinkNodeAdded(Node* node)
666 {
667 ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
668
669 _AddPackageLinksNode(node);
670
671 notify_entry_created(ID(), node->GetParentUnchecked()->ID(), node->Name(), node->ID());
672 _NotifyNodeAdded(node);
673 }
674
675
676 void
PackageLinkNodeRemoved(Node * node)677 Volume::PackageLinkNodeRemoved(Node* node)
678 {
679 ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
680
681 _RemovePackageLinksNode(node);
682
683 notify_entry_removed(ID(), node->GetParentUnchecked()->ID(), node->Name(), node->ID());
684 _NotifyNodeRemoved(node);
685 }
686
687
688 void
PackageLinkNodeChanged(Node * node,uint32 statFields,const OldNodeAttributes & oldAttributes)689 Volume::PackageLinkNodeChanged(Node* node, uint32 statFields,
690 const OldNodeAttributes& oldAttributes)
691 {
692 ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
693
694 Directory* parent = node->GetParentUnchecked();
695 notify_stat_changed(ID(), parent != NULL ? parent->ID() : -1, node->ID(),
696 statFields);
697 _NotifyNodeChanged(node, statFields, oldAttributes);
698 }
699
700
701 status_t
_LoadOldPackagesStates(const char * packagesState)702 Volume::_LoadOldPackagesStates(const char* packagesState)
703 {
704 // open and stat the admininistrative dir
705 int fd = openat(fPackagesDirectory->DirectoryFD(),
706 kAdministrativeDirectoryName, O_RDONLY);
707 if (fd < 0) {
708 ERROR("Failed to open administrative directory: %s\n", strerror(errno));
709 RETURN_ERROR(errno);
710 }
711
712 struct stat adminDirStat;
713 if (fstat(fd, &adminDirStat) < 0) {
714 ERROR("Failed to fstat() administrative directory: %s\n",
715 strerror(errno));
716 RETURN_ERROR(errno);
717 }
718
719 // iterate through the "administrative" dir
720 DirCloser dir(fdopendir(fd));
721 if (!dir.IsSet()) {
722 ERROR("Failed to open administrative directory: %s\n", strerror(errno));
723 RETURN_ERROR(errno);
724 }
725
726 while (dirent* entry = readdir(dir.Get())) {
727 if (strncmp(entry->d_name, "state_", 6) != 0
728 || strcmp(entry->d_name, packagesState) < 0) {
729 continue;
730 }
731
732 PackagesDirectory* packagesDirectory
733 = new(std::nothrow) PackagesDirectory;
734 status_t error = packagesDirectory->InitOldState(adminDirStat.st_dev,
735 adminDirStat.st_ino, entry->d_name);
736 if (error != B_OK) {
737 delete packagesDirectory;
738 continue;
739 }
740
741 fPackagesDirectories.Add(packagesDirectory);
742 fPackagesDirectoriesByNodeRef.Insert(packagesDirectory);
743
744 INFORM("added old packages dir state \"%s\"\n",
745 packagesDirectory->StateName().Data());
746 }
747
748 // sort the packages directories by state age
749 fPackagesDirectories.Sort(&PackagesDirectory::IsNewer);
750
751 return B_OK;
752 }
753
754
755 status_t
_AddInitialPackages()756 Volume::_AddInitialPackages()
757 {
758 PackagesDirectory* packagesDirectory = fPackagesDirectories.Last();
759 INFORM("Adding packages from \"%s\"\n", packagesDirectory->Path());
760
761 // try reading the activation file of the oldest state
762 status_t error = _AddInitialPackagesFromActivationFile(packagesDirectory);
763 if (error != B_OK && packagesDirectory != fPackagesDirectory) {
764 WARN("Loading packages from old state \"%s\" failed. Loading packages "
765 "from latest state.\n", packagesDirectory->StateName().Data());
766
767 // remove all packages already added
768 {
769 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
770 VolumeWriteLocker volumeLocker(this);
771 _RemoveAllPackages();
772 }
773
774 // remove the old states
775 while (fPackagesDirectories.Last() != fPackagesDirectory)
776 fPackagesDirectories.RemoveTail()->ReleaseReference();
777
778 // try reading the activation file of the latest state
779 packagesDirectory = fPackagesDirectory;
780 error = _AddInitialPackagesFromActivationFile(packagesDirectory);
781 }
782
783 if (error != B_OK) {
784 INFORM("Loading packages from activation file failed. Loading all "
785 "packages in packages directory.\n");
786
787 // remove all packages already added
788 {
789 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
790 VolumeWriteLocker volumeLocker(this);
791 _RemoveAllPackages();
792 }
793
794 // read the whole directory
795 error = _AddInitialPackagesFromDirectory();
796 if (error != B_OK)
797 RETURN_ERROR(error);
798 }
799
800 // add the packages to the node tree
801 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
802 VolumeWriteLocker volumeLocker(this);
803 for (PackageFileNameHashTable::Iterator it = fPackages.GetIterator();
804 Package* package = it.Next();) {
805 error = _AddPackageContent(package, false);
806 if (error != B_OK) {
807 for (it.Rewind(); Package* activePackage = it.Next();) {
808 if (activePackage == package)
809 break;
810 _RemovePackageContent(activePackage, NULL, false);
811 }
812 RETURN_ERROR(error);
813 }
814 }
815
816 return B_OK;
817 }
818
819
820 status_t
_AddInitialPackagesFromActivationFile(PackagesDirectory * packagesDirectory)821 Volume::_AddInitialPackagesFromActivationFile(
822 PackagesDirectory* packagesDirectory)
823 {
824 // try reading the activation file
825 FileDescriptorCloser fd(openat(packagesDirectory->DirectoryFD(),
826 packagesDirectory == fPackagesDirectory
827 ? kActivationFilePath : kActivationFileName,
828 O_RDONLY));
829 if (!fd.IsSet()) {
830 INFORM("Failed to open packages activation file: %s\n",
831 strerror(errno));
832 RETURN_ERROR(errno);
833 }
834
835 // read the whole file into memory to simplify things
836 struct stat st;
837 if (fstat(fd.Get(), &st) != 0) {
838 ERROR("Failed to stat packages activation file: %s\n",
839 strerror(errno));
840 RETURN_ERROR(errno);
841 }
842
843 if (st.st_size > (off_t)kMaxActivationFileSize) {
844 ERROR("The packages activation file is too big.\n");
845 RETURN_ERROR(B_BAD_DATA);
846 }
847
848 char* fileContent = (char*)malloc(st.st_size + 1);
849 if (fileContent == NULL)
850 RETURN_ERROR(B_NO_MEMORY);
851 MemoryDeleter fileContentDeleter(fileContent);
852
853 ssize_t bytesRead = read(fd.Get(), fileContent, st.st_size);
854 if (bytesRead < 0) {
855 ERROR("Failed to read packages activation file: %s\n", strerror(errno));
856 RETURN_ERROR(errno);
857 }
858
859 if (bytesRead != st.st_size) {
860 ERROR("Failed to read whole packages activation file\n");
861 RETURN_ERROR(B_ERROR);
862 }
863
864 // null-terminate to simplify parsing
865 fileContent[st.st_size] = '\0';
866
867 // parse the file and add the respective packages
868 const char* packageName = fileContent;
869 char* const fileContentEnd = fileContent + st.st_size;
870 while (packageName < fileContentEnd) {
871 char* packageNameEnd = strchr(packageName, '\n');
872 if (packageNameEnd == NULL)
873 packageNameEnd = fileContentEnd;
874
875 // skip empty lines
876 if (packageName == packageNameEnd) {
877 packageName++;
878 continue;
879 }
880 *packageNameEnd = '\0';
881
882 if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) {
883 ERROR("Invalid packages activation file content.\n");
884 RETURN_ERROR(B_BAD_DATA);
885 }
886
887 status_t error = _LoadAndAddInitialPackage(packagesDirectory,
888 packageName);
889 if (error != B_OK)
890 RETURN_ERROR(error);
891
892 packageName = packageNameEnd + 1;
893 }
894
895 return B_OK;
896 }
897
898
899 status_t
_AddInitialPackagesFromDirectory()900 Volume::_AddInitialPackagesFromDirectory()
901 {
902 // iterate through the dir and create packages
903 int fd = openat(fPackagesDirectory->DirectoryFD(), ".", O_RDONLY);
904 if (fd < 0) {
905 ERROR("Failed to open packages directory: %s\n", strerror(errno));
906 RETURN_ERROR(errno);
907 }
908
909 DirCloser dir(fdopendir(fd));
910 if (!dir.IsSet()) {
911 ERROR("Failed to open packages directory \"%s\": %s\n",
912 fPackagesDirectory->Path(), strerror(errno));
913 RETURN_ERROR(errno);
914 }
915
916 while (dirent* entry = readdir(dir.Get())) {
917 // skip "." and ".."
918 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
919 continue;
920
921 // also skip any entry without a ".hpkg" extension
922 size_t nameLength = strlen(entry->d_name);
923 if (nameLength < 5
924 || memcmp(entry->d_name + nameLength - 5, ".hpkg", 5) != 0) {
925 continue;
926 }
927
928 _LoadAndAddInitialPackage(fPackagesDirectory, entry->d_name);
929 }
930
931 return B_OK;
932 }
933
934
935 status_t
_LoadAndAddInitialPackage(PackagesDirectory * packagesDirectory,const char * name)936 Volume::_LoadAndAddInitialPackage(PackagesDirectory* packagesDirectory,
937 const char* name)
938 {
939 Package* package;
940 status_t error = _LoadPackage(packagesDirectory, name, package);
941 if (error != B_OK) {
942 ERROR("Failed to load package \"%s\": %s\n", name, strerror(error));
943 RETURN_ERROR(error);
944 }
945 BReference<Package> packageReference(package, true);
946
947 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
948 VolumeWriteLocker volumeLocker(this);
949 _AddPackage(package);
950
951 return B_OK;
952 }
953
954
955 inline void
_AddPackage(Package * package)956 Volume::_AddPackage(Package* package)
957 {
958 fPackages.Insert(package);
959 package->AcquireReference();
960 }
961
962
963 inline void
_RemovePackage(Package * package)964 Volume::_RemovePackage(Package* package)
965 {
966 fPackages.Remove(package);
967 package->ReleaseReference();
968 }
969
970
971 void
_RemoveAllPackages()972 Volume::_RemoveAllPackages()
973 {
974 Package* package = fPackages.Clear(true);
975 while (package != NULL) {
976 Package* next = package->FileNameHashTableNext();
977 package->ReleaseReference();
978 package = next;
979 }
980 }
981
982
983 inline Package*
_FindPackage(const char * fileName) const984 Volume::_FindPackage(const char* fileName) const
985 {
986 return fPackages.Lookup(fileName);
987 }
988
989
990 status_t
_AddPackageContent(Package * package,bool notify)991 Volume::_AddPackageContent(Package* package, bool notify)
992 {
993 // Open the package. We don't need the FD here, but this is an optimization.
994 // The attribute indices may want to read the package nodes' attributes and
995 // the package file would be opened and closed for each attribute instance.
996 // Since Package keeps and shares the FD as long as at least one party has
997 // the package open, we prevent that.
998 int fd = package->Open();
999 if (fd < 0)
1000 RETURN_ERROR(fd);
1001 PackageCloser packageCloser(package);
1002
1003 status_t error = fPackageFSRoot->AddPackage(package);
1004 if (error != B_OK)
1005 RETURN_ERROR(error);
1006
1007 for (PackageNodeList::ConstIterator it = package->Nodes().GetIterator();
1008 PackageNode* node = it.Next();) {
1009 // skip over ".PackageInfo" file, it isn't part of the package content
1010 if (strcmp(node->Name(),
1011 BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME) == 0) {
1012 continue;
1013 }
1014 error = _AddPackageContentRootNode(package, node, notify);
1015 if (error != B_OK) {
1016 _RemovePackageContent(package, node, notify);
1017 RETURN_ERROR(error);
1018 }
1019 }
1020
1021 return B_OK;
1022 }
1023
1024
1025 void
_RemovePackageContent(Package * package,PackageNode * endNode,bool notify)1026 Volume::_RemovePackageContent(Package* package, PackageNode* endNode,
1027 bool notify)
1028 {
1029 PackageNode* node = package->Nodes().Head();
1030 while (node != NULL) {
1031 if (node == endNode)
1032 break;
1033
1034 PackageNode* nextNode = package->Nodes().GetNext(node);
1035
1036 // skip over ".PackageInfo" file, it isn't part of the package content
1037 if (strcmp(node->Name(),
1038 BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME) != 0) {
1039 _RemovePackageContentRootNode(package, node, NULL, notify);
1040 }
1041
1042 node = nextNode;
1043 }
1044
1045 fPackageFSRoot->RemovePackage(package);
1046 }
1047
1048
1049 /*! This method recursively iterates through the descendents of the given
1050 package root node and adds all package nodes to the node tree in
1051 pre-order.
1052 Due to limited kernel stack space we avoid deep recursive function calls
1053 and rather use the package node stack implied by the tree.
1054 */
1055 status_t
_AddPackageContentRootNode(Package * package,PackageNode * rootPackageNode,bool notify)1056 Volume::_AddPackageContentRootNode(Package* package,
1057 PackageNode* rootPackageNode, bool notify)
1058 {
1059 ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
1060
1061 PackageNode* packageNode = rootPackageNode;
1062 Directory* directory = fRootDirectory;
1063 directory->WriteLock();
1064
1065 do {
1066 Node* node;
1067 status_t error = _AddPackageNode(directory, packageNode, notify, node);
1068 // returns B_OK with a NULL node, when skipping the node
1069 if (error != B_OK) {
1070 // unlock all directories
1071 while (directory != NULL) {
1072 directory->WriteUnlock();
1073 directory = directory->GetParentUnchecked();
1074 }
1075
1076 // remove the added package nodes
1077 _RemovePackageContentRootNode(package, rootPackageNode, packageNode,
1078 notify);
1079 RETURN_ERROR(error);
1080 }
1081
1082 // recurse into directory, unless we're supposed to skip the node
1083 if (node != NULL) {
1084 if (PackageDirectory* packageDirectory
1085 = dynamic_cast<PackageDirectory*>(packageNode)) {
1086 if (packageDirectory->FirstChild() != NULL) {
1087 directory = dynamic_cast<Directory*>(node);
1088 packageNode = packageDirectory->FirstChild();
1089 directory->WriteLock();
1090 continue;
1091 }
1092 }
1093 }
1094
1095 // continue with the next available (ancestors's) sibling
1096 do {
1097 PackageDirectory* packageDirectory = packageNode->Parent();
1098 PackageNode* sibling = packageDirectory != NULL
1099 ? packageDirectory->NextChild(packageNode) : NULL;
1100
1101 if (sibling != NULL) {
1102 packageNode = sibling;
1103 break;
1104 }
1105
1106 // no more siblings -- go back up the tree
1107 packageNode = packageDirectory;
1108 directory->WriteUnlock();
1109 directory = directory->GetParentUnchecked();
1110 // the parent is still locked, so this is safe
1111 } while (packageNode != NULL);
1112 } while (packageNode != NULL);
1113
1114 ASSERT(directory == NULL);
1115
1116 return B_OK;
1117 }
1118
1119
1120 /*! Recursively iterates through the descendents of the given package root node
1121 and removes all package nodes from the node tree in post-order, until
1122 encountering \a endPackageNode (if non-null).
1123 Due to limited kernel stack space we avoid deep recursive function calls
1124 and rather use the package node stack implied by the tree.
1125 */
1126 void
_RemovePackageContentRootNode(Package * package,PackageNode * rootPackageNode,PackageNode * endPackageNode,bool notify)1127 Volume::_RemovePackageContentRootNode(Package* package,
1128 PackageNode* rootPackageNode, PackageNode* endPackageNode, bool notify)
1129 {
1130 ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
1131
1132 PackageNode* packageNode = rootPackageNode;
1133 Directory* directory = fRootDirectory;
1134 directory->WriteLock();
1135
1136 do {
1137 if (packageNode == endPackageNode) {
1138 // unlock all directories
1139 while (directory != NULL) {
1140 directory->WriteUnlock();
1141 directory = directory->GetParentUnchecked();
1142 }
1143 break;
1144 }
1145
1146 // recurse into directory
1147 if (PackageDirectory* packageDirectory
1148 = dynamic_cast<PackageDirectory*>(packageNode)) {
1149 if (packageDirectory->FirstChild() != NULL) {
1150 if (Directory* childDirectory = dynamic_cast<Directory*>(
1151 directory->FindChild(packageNode->Name()))) {
1152 directory = childDirectory;
1153 packageNode = packageDirectory->FirstChild();
1154 directory->WriteLock();
1155 continue;
1156 }
1157 }
1158 }
1159
1160 // continue with the next available (ancestors's) sibling
1161 do {
1162 PackageDirectory* packageDirectory = packageNode->Parent();
1163 PackageNode* sibling = packageDirectory != NULL
1164 ? packageDirectory->NextChild(packageNode) : NULL;
1165
1166 // we're done with the node -- remove it
1167 _RemovePackageNode(directory, packageNode,
1168 directory->FindChild(packageNode->Name()), notify);
1169
1170 if (sibling != NULL) {
1171 packageNode = sibling;
1172 break;
1173 }
1174
1175 // no more siblings -- go back up the tree
1176 packageNode = packageDirectory;
1177 directory->WriteUnlock();
1178 directory = directory->GetParentUnchecked();
1179 // the parent is still locked, so this is safe
1180 } while (packageNode != NULL/* && packageNode != rootPackageNode*/);
1181 } while (packageNode != NULL/* && packageNode != rootPackageNode*/);
1182
1183 ASSERT(directory == NULL);
1184 }
1185
1186
1187 status_t
_AddPackageNode(Directory * directory,PackageNode * packageNode,bool notify,Node * & _node)1188 Volume::_AddPackageNode(Directory* directory, PackageNode* packageNode,
1189 bool notify, Node*& _node)
1190 {
1191 bool newNode = false;
1192 UnpackingNode* unpackingNode;
1193 Node* node = directory->FindChild(packageNode->Name());
1194 PackageNode* oldPackageNode = NULL;
1195
1196 if (node != NULL) {
1197 unpackingNode = dynamic_cast<UnpackingNode*>(node);
1198 if (unpackingNode == NULL) {
1199 _node = NULL;
1200 return B_OK;
1201 }
1202 oldPackageNode = unpackingNode->GetPackageNode();
1203 } else {
1204 status_t error = _CreateUnpackingNode(packageNode->Mode(), directory,
1205 packageNode->Name(), unpackingNode);
1206 if (error != B_OK)
1207 RETURN_ERROR(error);
1208
1209 node = unpackingNode->GetNode();
1210 newNode = true;
1211 }
1212
1213 BReference<Node> nodeReference(node);
1214 DirectoryWriteLocker directoryNodeWriteLocker;
1215 if (Directory* directory = dynamic_cast<Directory*>(node))
1216 directoryNodeWriteLocker.SetTo(directory, false, true);
1217
1218 BReference<Node> newNodeReference;
1219 DirectoryWriteLocker newDirectoryNodeWriteLocker;
1220 Node* oldNode = NULL;
1221
1222 if (!newNode && !S_ISDIR(node->Mode()) && oldPackageNode != NULL
1223 && unpackingNode->WillBeFirstPackageNode(packageNode)) {
1224 // The package node we're going to add will represent the node,
1225 // replacing the current head package node. Since the node isn't a
1226 // directory, we must make sure that clients having opened or mapped the
1227 // node won't be surprised. So we create a new node and remove the
1228 // current one.
1229 // create a new node and transfer the package nodes to it
1230 UnpackingNode* newUnpackingNode;
1231 status_t error = unpackingNode->CloneTransferPackageNodes(
1232 fNextNodeID++, newUnpackingNode);
1233 if (error != B_OK)
1234 RETURN_ERROR(error);
1235
1236 // remove the old node
1237 _NotifyNodeRemoved(node);
1238 _RemoveNodeAndVNode(node);
1239 oldNode = node;
1240
1241 // add the new node
1242 unpackingNode = newUnpackingNode;
1243 node = unpackingNode->GetNode();
1244 newNodeReference.SetTo(node);
1245 if (Directory* newDirectory = dynamic_cast<Directory*>(node))
1246 newDirectoryNodeWriteLocker.SetTo(newDirectory, false, true);
1247
1248 directory->AddChild(node);
1249 fNodes.Insert(node);
1250 newNode = true;
1251 }
1252
1253 status_t error = unpackingNode->AddPackageNode(packageNode, ID());
1254 if (error != B_OK) {
1255 dprintf("packagefs: Failed to add node \"%s\" of package \"%s\": %s\n",
1256 packageNode->Name().Data(), packageNode->GetPackage()->Name().Data(),
1257 strerror(error));
1258
1259 // Remove the node, if created before. If the node was created to
1260 // replace the previous node, send out notifications instead.
1261 if (newNode) {
1262 if (oldNode != NULL) {
1263 _NotifyNodeAdded(node);
1264 if (notify) {
1265 notify_entry_removed(ID(), directory->ID(), oldNode->Name(),
1266 oldNode->ID());
1267 notify_entry_created(ID(), directory->ID(), node->Name(),
1268 node->ID());
1269 }
1270 } else
1271 _RemoveNode(node);
1272 }
1273 RETURN_ERROR(error);
1274 }
1275
1276 if (newNode) {
1277 _NotifyNodeAdded(node);
1278 } else if (packageNode == unpackingNode->GetPackageNode()) {
1279 _NotifyNodeChanged(node, kAllStatFields,
1280 OldUnpackingNodeAttributes(oldPackageNode));
1281 }
1282
1283 if (notify) {
1284 if (newNode) {
1285 if (oldNode != NULL) {
1286 notify_entry_removed(ID(), directory->ID(), oldNode->Name(),
1287 oldNode->ID());
1288 }
1289 notify_entry_created(ID(), directory->ID(), node->Name(),
1290 node->ID());
1291 } else if (packageNode == unpackingNode->GetPackageNode()) {
1292 // The new package node has become the one representing the node.
1293 // Send stat changed notification for directories and entry
1294 // removed + created notifications for files and symlinks.
1295 notify_stat_changed(ID(), directory->ID(), node->ID(),
1296 kAllStatFields);
1297 // TODO: Actually the attributes might change, too!
1298 }
1299 }
1300
1301 _node = node;
1302 return B_OK;
1303 }
1304
1305
1306 void
_RemovePackageNode(Directory * directory,PackageNode * packageNode,Node * node,bool notify)1307 Volume::_RemovePackageNode(Directory* directory, PackageNode* packageNode,
1308 Node* node, bool notify)
1309 {
1310 UnpackingNode* unpackingNode = dynamic_cast<UnpackingNode*>(node);
1311 if (unpackingNode == NULL)
1312 return;
1313
1314 BReference<Node> nodeReference(node);
1315 DirectoryWriteLocker directoryNodeWriteLocker;
1316 if (Directory* directory = dynamic_cast<Directory*>(node))
1317 directoryNodeWriteLocker.SetTo(directory, false, true);
1318
1319 PackageNode* headPackageNode = unpackingNode->GetPackageNode();
1320 bool nodeRemoved = false;
1321 Node* newNode = NULL;
1322
1323 BReference<Node> newNodeReference;
1324 DirectoryWriteLocker newDirectoryNodeWriteLocker;
1325
1326 // If this is the last package node of the node, remove it completely.
1327 if (unpackingNode->IsOnlyPackageNode(packageNode)) {
1328 // Notify before removing the node. Otherwise the indices might not
1329 // find the node anymore.
1330 _NotifyNodeRemoved(node);
1331
1332 unpackingNode->PrepareForRemoval();
1333
1334 _RemoveNodeAndVNode(node);
1335 nodeRemoved = true;
1336 } else if (packageNode == headPackageNode) {
1337 // The node does at least have one more package node, but the one to be
1338 // removed is the head. Unless it's a directory, we replace the node
1339 // with a completely new one and let the old one die. This is necessary
1340 // to avoid surprises for clients that have opened/mapped the node.
1341 if (S_ISDIR(packageNode->Mode())) {
1342 unpackingNode->RemovePackageNode(packageNode, ID());
1343 _NotifyNodeChanged(node, kAllStatFields,
1344 OldUnpackingNodeAttributes(headPackageNode));
1345 } else {
1346 // create a new node and transfer the package nodes to it
1347 UnpackingNode* newUnpackingNode;
1348 status_t error = unpackingNode->CloneTransferPackageNodes(
1349 fNextNodeID++, newUnpackingNode);
1350 if (error == B_OK) {
1351 // remove the package node
1352 newUnpackingNode->RemovePackageNode(packageNode, ID());
1353
1354 // remove the old node
1355 _NotifyNodeRemoved(node);
1356 _RemoveNodeAndVNode(node);
1357
1358 // add the new node
1359 newNode = newUnpackingNode->GetNode();
1360 newNodeReference.SetTo(newNode);
1361 if (Directory* newDirectory = dynamic_cast<Directory*>(newNode))
1362 newDirectoryNodeWriteLocker.SetTo(newDirectory, false, true);
1363
1364 directory->AddChild(newNode);
1365 fNodes.Insert(newNode);
1366 _NotifyNodeAdded(newNode);
1367 } else {
1368 // There's nothing we can do. Remove the node completely.
1369 _NotifyNodeRemoved(node);
1370
1371 unpackingNode->PrepareForRemoval();
1372
1373 _RemoveNodeAndVNode(node);
1374 nodeRemoved = true;
1375 }
1376 }
1377 } else {
1378 // The package node to remove is not the head of the node. This change
1379 // doesn't have any visible effect.
1380 unpackingNode->RemovePackageNode(packageNode, ID());
1381 }
1382
1383 if (!notify)
1384 return;
1385
1386 // send notifications
1387 if (nodeRemoved) {
1388 notify_entry_removed(ID(), directory->ID(), node->Name(), node->ID());
1389 } else if (packageNode == headPackageNode) {
1390 // The removed package node was the one representing the node.
1391 // Send stat changed notification for directories and entry
1392 // removed + created notifications for files and symlinks.
1393 if (S_ISDIR(packageNode->Mode())) {
1394 notify_stat_changed(ID(), directory->ID(), node->ID(),
1395 kAllStatFields);
1396 // TODO: Actually the attributes might change, too!
1397 } else {
1398 notify_entry_removed(ID(), directory->ID(), node->Name(),
1399 node->ID());
1400 notify_entry_created(ID(), directory->ID(), newNode->Name(),
1401 newNode->ID());
1402 }
1403 }
1404 }
1405
1406
1407 status_t
_CreateUnpackingNode(mode_t mode,Directory * parent,const String & name,UnpackingNode * & _node)1408 Volume::_CreateUnpackingNode(mode_t mode, Directory* parent, const String& name,
1409 UnpackingNode*& _node)
1410 {
1411 UnpackingNode* unpackingNode;
1412 if (S_ISREG(mode) || S_ISLNK(mode))
1413 unpackingNode = new UnpackingLeafNode(fNextNodeID++);
1414 else if (S_ISDIR(mode))
1415 unpackingNode = new UnpackingDirectory(fNextNodeID++);
1416 else
1417 RETURN_ERROR(B_UNSUPPORTED);
1418
1419 if (unpackingNode == NULL)
1420 RETURN_ERROR(B_NO_MEMORY);
1421
1422 Node* node = unpackingNode->GetNode();
1423 BReference<Node> nodeReference(node, true);
1424
1425 status_t error = node->Init(name);
1426 if (error != B_OK)
1427 RETURN_ERROR(error);
1428
1429 parent->AddChild(node);
1430
1431 fNodes.Insert(node);
1432 nodeReference.Detach();
1433 // we keep the initial node reference for the table
1434
1435 _node = unpackingNode;
1436 return B_OK;
1437 }
1438
1439
1440 void
_RemoveNode(Node * node)1441 Volume::_RemoveNode(Node* node)
1442 {
1443 // remove from parent
1444 Directory* parent = node->GetParentUnchecked();
1445 parent->RemoveChild(node);
1446
1447 // remove from node table
1448 fNodes.Remove(node);
1449 node->ReleaseReference();
1450 }
1451
1452
1453 void
_RemoveNodeAndVNode(Node * node)1454 Volume::_RemoveNodeAndVNode(Node* node)
1455 {
1456 Directory* parent = NULL;
1457 DirectoryWriteLocker nodeWriteLocker;
1458 if (Directory* directory = dynamic_cast<Directory*>(node))
1459 nodeWriteLocker.SetTo(directory, false, true);
1460 else
1461 parent = node->GetParentUnchecked();
1462
1463 // Remove the node from its parent and the volume. This makes the node
1464 // inaccessible via the get_vnode() and lookup() hooks, and (if this
1465 // is not a directory) prevents any future accesses, since regular Nodes
1466 // do not have a lock of their own.
1467 _RemoveNode(node);
1468
1469 const bool isKnownToVFS = node->IsKnownToVFS();
1470
1471 if (parent != NULL) {
1472 // This node is not a directory. In order to avoid deadlocks, unlock
1473 // its parent while we invoke the VFS, so that any threads trying
1474 // to acquire the directory's lock wake up and exit.
1475 parent->WriteUnlock();
1476 } else {
1477 nodeWriteLocker.Unlock();
1478 }
1479
1480 // If the node is known to the VFS, we get the vnode, remove it, and put it,
1481 // so that the VFS will discard it as soon as possible (i.e. now, if no one
1482 // else is using it).
1483 Node* dummyNode;
1484 if (isKnownToVFS && GetVNode(node->ID(), dummyNode) == B_OK) {
1485 // TODO: There still is a race condition here which we can't avoid
1486 // without more help from the VFS. Right after we drop the write
1487 // lock a vnode for the node could be discarded by the VFS. At that
1488 // point another thread trying to get the vnode by ID would create
1489 // a vnode, mark it busy and call our get_vnode() hook. It would
1490 // block since we (i.e. the package loader thread executing this
1491 // method) still have the volume write lock. Our get_vnode() call
1492 // would block, since it finds the vnode marked busy. It times out
1493 // eventually, but until then a good deal of FS operations might
1494 // block as well due to us holding the volume lock and probably
1495 // several node locks as well. A get_vnode*() variant (e.g.
1496 // get_vnode_etc() with flags parameter) that wouldn't block and
1497 // only get the vnode, if already loaded and non-busy, would be
1498 // perfect here.
1499 RemoveVNode(node->ID());
1500 PutVNode(node->ID());
1501 }
1502
1503 if (parent != NULL)
1504 parent->WriteLock();
1505 }
1506
1507
1508 status_t
_LoadPackage(PackagesDirectory * packagesDirectory,const char * name,Package * & _package)1509 Volume::_LoadPackage(PackagesDirectory* packagesDirectory, const char* name,
1510 Package*& _package)
1511 {
1512 // Find the package -- check the specified packages directory and iterate
1513 // toward the newer states.
1514 struct stat st;
1515 for (;;) {
1516 if (packagesDirectory == NULL)
1517 return B_ENTRY_NOT_FOUND;
1518
1519 if (fstatat(packagesDirectory->DirectoryFD(), name, &st, 0) == 0) {
1520 // check whether the entry is a file
1521 if (!S_ISREG(st.st_mode))
1522 return B_BAD_VALUE;
1523 break;
1524 }
1525
1526 packagesDirectory = fPackagesDirectories.GetPrevious(packagesDirectory);
1527 }
1528
1529 // create a package
1530 Package* package = new(std::nothrow) Package(this, packagesDirectory,
1531 st.st_dev, st.st_ino);
1532 if (package == NULL)
1533 RETURN_ERROR(B_NO_MEMORY);
1534 BReference<Package> packageReference(package, true);
1535
1536 status_t error = package->Init(name);
1537 if (error != B_OK)
1538 return error;
1539
1540 error = package->Load(fPackageSettings);
1541 if (error != B_OK)
1542 return error;
1543
1544 _package = packageReference.Detach();
1545 return B_OK;
1546 }
1547
1548
1549 status_t
_ChangeActivation(ActivationChangeRequest & request)1550 Volume::_ChangeActivation(ActivationChangeRequest& request)
1551 {
1552 uint32 itemCount = request.CountItems();
1553 if (itemCount == 0)
1554 return B_OK;
1555
1556 // first check the request
1557 int32 newPackageCount = 0;
1558 int32 oldPackageCount = 0;
1559 {
1560 VolumeReadLocker volumeLocker(this);
1561
1562 for (uint32 i = 0; i < itemCount; i++) {
1563 PackageFSActivationChangeItem* item = request.ItemAt(i);
1564 if (item->parentDeviceID != fPackagesDirectory->DeviceID()
1565 || item->parentDirectoryID != fPackagesDirectory->NodeID()) {
1566 ERROR("Volume::_ChangeActivation(): mismatching packages "
1567 "directory\n");
1568 RETURN_ERROR(B_MISMATCHED_VALUES);
1569 }
1570
1571 Package* package = _FindPackage(item->name);
1572 // TODO: We should better look up the package by node_ref!
1573 if (item->type == PACKAGE_FS_ACTIVATE_PACKAGE) {
1574 if (package != NULL) {
1575 ERROR("Volume::_ChangeActivation(): package to activate "
1576 "already activated: \"%s\"\n", item->name);
1577 RETURN_ERROR(B_NAME_IN_USE);
1578 }
1579 newPackageCount++;
1580 } else if (item->type == PACKAGE_FS_DEACTIVATE_PACKAGE) {
1581 if (package == NULL) {
1582 ERROR("Volume::_ChangeActivation(): package to deactivate "
1583 "not found: \"%s\"\n", item->name);
1584 RETURN_ERROR(B_NAME_NOT_FOUND);
1585 }
1586 oldPackageCount++;
1587 } else if (item->type == PACKAGE_FS_REACTIVATE_PACKAGE) {
1588 if (package == NULL) {
1589 ERROR("Volume::_ChangeActivation(): package to reactivate "
1590 "not found: \"%s\"\n", item->name);
1591 RETURN_ERROR(B_NAME_NOT_FOUND);
1592 }
1593 oldPackageCount++;
1594 newPackageCount++;
1595 } else
1596 RETURN_ERROR(B_BAD_VALUE);
1597 }
1598 }
1599
1600 INFORM("Volume::_ChangeActivation(): %" B_PRId32 " new packages, %" B_PRId32
1601 " old packages\n", newPackageCount, oldPackageCount);
1602
1603 // Things look good so far -- allocate reference arrays for the packages to
1604 // add and remove.
1605 BReference<Package>* newPackageReferences
1606 = new(std::nothrow) BReference<Package>[newPackageCount];
1607 if (newPackageReferences == NULL)
1608 RETURN_ERROR(B_NO_MEMORY);
1609 ArrayDeleter<BReference<Package> > newPackageReferencesDeleter(
1610 newPackageReferences);
1611
1612 BReference<Package>* oldPackageReferences
1613 = new(std::nothrow) BReference<Package>[oldPackageCount];
1614 if (oldPackageReferences == NULL)
1615 RETURN_ERROR(B_NO_MEMORY);
1616 ArrayDeleter<BReference<Package> > oldPackageReferencesDeleter(
1617 oldPackageReferences);
1618
1619 // load all new packages
1620 int32 newPackageIndex = 0;
1621 for (uint32 i = 0; i < itemCount; i++) {
1622 PackageFSActivationChangeItem* item = request.ItemAt(i);
1623
1624 if (item->type != PACKAGE_FS_ACTIVATE_PACKAGE
1625 && item->type != PACKAGE_FS_REACTIVATE_PACKAGE) {
1626 continue;
1627 }
1628
1629 Package* package;
1630 status_t error = _LoadPackage(fPackagesDirectory, item->name, package);
1631 if (error != B_OK) {
1632 ERROR("Volume::_ChangeActivation(): failed to load package "
1633 "\"%s\"\n", item->name);
1634 RETURN_ERROR(error);
1635 }
1636
1637 newPackageReferences[newPackageIndex++].SetTo(package, true);
1638 }
1639
1640 // apply the changes
1641 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
1642 VolumeWriteLocker volumeLocker(this);
1643 // TODO: Add a change counter to Volume, so we can easily check whether
1644 // everything is still the same.
1645
1646 // remove the old packages
1647 int32 oldPackageIndex = 0;
1648 for (uint32 i = 0; i < itemCount; i++) {
1649 PackageFSActivationChangeItem* item = request.ItemAt(i);
1650
1651 if (item->type != PACKAGE_FS_DEACTIVATE_PACKAGE
1652 && item->type != PACKAGE_FS_REACTIVATE_PACKAGE) {
1653 continue;
1654 }
1655
1656 Package* package = _FindPackage(item->name);
1657 // TODO: We should better look up the package by node_ref!
1658 oldPackageReferences[oldPackageIndex++].SetTo(package);
1659 _RemovePackageContent(package, NULL, true);
1660 _RemovePackage(package);
1661
1662 INFORM("package \"%s\" deactivated\n", package->FileName().Data());
1663 }
1664 // TODO: Since package removal cannot fail, consider adding the new packages
1665 // first. The reactivation case may make that problematic, since two packages
1666 // with the same name would be active after activating the new one. Check!
1667
1668 // add the new packages
1669 status_t error = B_OK;
1670 for (newPackageIndex = 0; newPackageIndex < newPackageCount;
1671 newPackageIndex++) {
1672 Package* package = newPackageReferences[newPackageIndex];
1673 _AddPackage(package);
1674
1675 // add the package to the node tree
1676 error = _AddPackageContent(package, true);
1677 if (error != B_OK) {
1678 _RemovePackage(package);
1679 break;
1680 }
1681 INFORM("package \"%s\" activated\n", package->FileName().Data());
1682 }
1683
1684 // Try to roll back the changes, if an error occurred.
1685 if (error != B_OK) {
1686 for (int32 i = newPackageIndex - 1; i >= 0; i--) {
1687 Package* package = newPackageReferences[i];
1688 _RemovePackageContent(package, NULL, true);
1689 _RemovePackage(package);
1690 }
1691
1692 for (int32 i = oldPackageCount - 1; i >= 0; i--) {
1693 Package* package = oldPackageReferences[i];
1694 _AddPackage(package);
1695
1696 if (_AddPackageContent(package, true) != B_OK) {
1697 // nothing we can do here
1698 ERROR("Volume::_ChangeActivation(): failed to roll back "
1699 "deactivation of package \"%s\" after error\n",
1700 package->FileName().Data());
1701 _RemovePackage(package);
1702 }
1703 }
1704 }
1705
1706 return error;
1707 }
1708
1709
1710 status_t
_InitMountType(const char * mountType)1711 Volume::_InitMountType(const char* mountType)
1712 {
1713 if (mountType == NULL)
1714 fMountType = PACKAGE_FS_MOUNT_TYPE_CUSTOM;
1715 else if (strcmp(mountType, "system") == 0)
1716 fMountType = PACKAGE_FS_MOUNT_TYPE_SYSTEM;
1717 else if (strcmp(mountType, "home") == 0)
1718 fMountType = PACKAGE_FS_MOUNT_TYPE_HOME;
1719 else if (strcmp(mountType, "custom") == 0)
1720 fMountType = PACKAGE_FS_MOUNT_TYPE_CUSTOM;
1721 else
1722 RETURN_ERROR(B_BAD_VALUE);
1723
1724 return B_OK;
1725 }
1726
1727
1728 status_t
_CreateShineThroughDirectory(Directory * parent,const char * name,Directory * & _directory)1729 Volume::_CreateShineThroughDirectory(Directory* parent, const char* name,
1730 Directory*& _directory)
1731 {
1732 ShineThroughDirectory* directory = new(std::nothrow) ShineThroughDirectory(
1733 fNextNodeID++);
1734 if (directory == NULL)
1735 RETURN_ERROR(B_NO_MEMORY);
1736 BReference<ShineThroughDirectory> directoryReference(directory, true);
1737
1738 String nameString;
1739 if (!nameString.SetTo(name))
1740 RETURN_ERROR(B_NO_MEMORY);
1741
1742 status_t error = directory->Init(nameString);
1743 if (error != B_OK)
1744 RETURN_ERROR(error);
1745
1746 parent->AddChild(directory);
1747
1748 fNodes.Insert(directory);
1749 directoryReference.Detach();
1750 // we keep the initial node reference for the table
1751
1752 _directory = directory;
1753 return B_OK;
1754 }
1755
1756
1757 status_t
_CreateShineThroughDirectories(const char * shineThroughSetting)1758 Volume::_CreateShineThroughDirectories(const char* shineThroughSetting)
1759 {
1760 // get the directories to map
1761 const char* const* directories = NULL;
1762
1763 if (shineThroughSetting == NULL) {
1764 // nothing specified -- derive from mount type
1765 switch (fMountType) {
1766 case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
1767 case PACKAGE_FS_MOUNT_TYPE_HOME:
1768 directories = kShineThroughDirectories;
1769 break;
1770 case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
1771 return B_OK;
1772 case PACKAGE_FS_MOUNT_TYPE_ENUM_COUNT:
1773 return B_BAD_VALUE;
1774 }
1775 } else if (strcmp(shineThroughSetting, "system") == 0)
1776 directories = kShineThroughDirectories;
1777 else if (strcmp(shineThroughSetting, "home") == 0)
1778 directories = kShineThroughDirectories;
1779 else if (strcmp(shineThroughSetting, "none") == 0)
1780 directories = NULL;
1781 else
1782 RETURN_ERROR(B_BAD_VALUE);
1783
1784 if (directories == NULL)
1785 return B_OK;
1786
1787 DirectoryWriteLocker rootDirectoryWriteLocker(fRootDirectory);
1788
1789 // iterate through the directory list and create the directories
1790 while (const char* directoryName = *(directories++)) {
1791 // create the directory
1792 Directory* directory;
1793 status_t error = _CreateShineThroughDirectory(fRootDirectory,
1794 directoryName, directory);
1795 if (error != B_OK)
1796 RETURN_ERROR(error);
1797 }
1798
1799 return B_OK;
1800 }
1801
1802
1803 status_t
_PublishShineThroughDirectories()1804 Volume::_PublishShineThroughDirectories()
1805 {
1806 DirectoryWriteLocker rootDirectoryWriteLocker(fRootDirectory);
1807
1808 // Iterate through the root directory children and bind the shine-through
1809 // directories to the respective mount point subdirectories.
1810 Node* nextNode;
1811 for (Node* node = fRootDirectory->FirstChild(); node != NULL;
1812 node = nextNode) {
1813 nextNode = fRootDirectory->NextChild(node);
1814
1815 // skip anything but shine-through directories
1816 ShineThroughDirectory* directory
1817 = dynamic_cast<ShineThroughDirectory*>(node);
1818 if (directory == NULL)
1819 continue;
1820
1821 const char* directoryName = directory->Name();
1822
1823 // look up the mount point subdirectory
1824 struct vnode* vnode;
1825 status_t error = vfs_entry_ref_to_vnode(fMountPoint.deviceID,
1826 fMountPoint.nodeID, directoryName, &vnode);
1827 if (error != B_OK) {
1828 dprintf("packagefs: Failed to get shine-through directory \"%s\": "
1829 "%s\n", directoryName, strerror(error));
1830 _RemoveNode(directory);
1831 continue;
1832 }
1833 VnodePutter vnodePutter(vnode);
1834
1835 // stat it
1836 struct stat st;
1837 error = vfs_stat_vnode(vnode, &st);
1838 if (error != B_OK) {
1839 dprintf("packagefs: Failed to stat shine-through directory \"%s\": "
1840 "%s\n", directoryName, strerror(error));
1841 _RemoveNode(directory);
1842 continue;
1843 }
1844
1845 if (!S_ISDIR(st.st_mode)) {
1846 dprintf("packagefs: Shine-through entry \"%s\" is not a "
1847 "directory\n", directoryName);
1848 _RemoveNode(directory);
1849 continue;
1850 }
1851
1852 // publish the vnode, so the VFS will find it without asking us
1853 directory->AcquireReference();
1854 error = PublishVNode(directory);
1855 if (error != B_OK) {
1856 directory->ReleaseReference();
1857 _RemoveNode(directory);
1858 RETURN_ERROR(error);
1859 }
1860
1861 // bind the directory
1862 error = vfs_bind_mount_directory(st.st_dev, st.st_ino, fFSVolume->id,
1863 directory->ID());
1864
1865 PutVNode(directory->ID());
1866 // release our reference again -- on success
1867 // vfs_bind_mount_directory() got one
1868
1869 if (error != B_OK)
1870 RETURN_ERROR(error);
1871 }
1872
1873 return B_OK;
1874 }
1875
1876
1877 status_t
_AddPackageLinksDirectory()1878 Volume::_AddPackageLinksDirectory()
1879 {
1880 // called when mounting, so we don't need to lock the volume
1881
1882 PackageLinksDirectory* packageLinksDirectory
1883 = fPackageFSRoot->GetPackageLinksDirectory();
1884
1885 DirectoryWriteLocker rootDirectoryWriteLocker(fRootDirectory);
1886 DirectoryWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory);
1887
1888 fRootDirectory->AddChild(packageLinksDirectory);
1889
1890 _AddPackageLinksNode(packageLinksDirectory);
1891
1892 packageLinksDirectory->SetListener(this);
1893
1894 return B_OK;
1895 }
1896
1897
1898 void
_RemovePackageLinksDirectory()1899 Volume::_RemovePackageLinksDirectory()
1900 {
1901 PackageLinksDirectory* packageLinksDirectory
1902 = fPackageFSRoot->GetPackageLinksDirectory();
1903
1904 VolumeWriteLocker volumeLocker(this);
1905 DirectoryWriteLocker rootDirectoryWriteLocker(fRootDirectory);
1906 DirectoryWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory);
1907
1908 if (packageLinksDirectory->GetParentUnchecked() == fRootDirectory) {
1909 packageLinksDirectory->SetListener(NULL);
1910 fRootDirectory->RemoveChild(packageLinksDirectory);
1911 }
1912 }
1913
1914
1915 void
_AddPackageLinksNode(Node * node)1916 Volume::_AddPackageLinksNode(Node* node)
1917 {
1918 node->SetID(fNextNodeID++);
1919
1920 fNodes.Insert(node);
1921 node->AcquireReference();
1922
1923 // If this is a directory, recursively add descendants. The directory tree
1924 // for the package links isn't deep, so we can do recursion.
1925 if (Directory* directory = dynamic_cast<Directory*>(node)) {
1926 DirectoryReadLocker directoryReadLocker(directory);
1927 for (Node* child = directory->FirstChild(); child != NULL;
1928 child = directory->NextChild(child)) {
1929 _AddPackageLinksNode(child);
1930 }
1931 }
1932 }
1933
1934
1935 void
_RemovePackageLinksNode(Node * node)1936 Volume::_RemovePackageLinksNode(Node* node)
1937 {
1938 // If this is a directory, recursively remove descendants. The directory
1939 // tree for the package links isn't deep, so we can do recursion.
1940 if (Directory* directory = dynamic_cast<Directory*>(node)) {
1941 DirectoryReadLocker directoryReadLocker(directory);
1942 for (Node* child = directory->FirstChild(); child != NULL;
1943 child = directory->NextChild(child)) {
1944 _RemovePackageLinksNode(child);
1945 }
1946 }
1947
1948 fNodes.Remove(node);
1949 node->ReleaseReference();
1950 }
1951
1952
1953 inline Volume*
_SystemVolumeIfNotSelf() const1954 Volume::_SystemVolumeIfNotSelf() const
1955 {
1956 if (Volume* systemVolume = fPackageFSRoot->SystemVolume())
1957 return systemVolume == this ? NULL : systemVolume;
1958 return NULL;
1959 }
1960
1961
1962 void
_NotifyNodeAdded(Node * node)1963 Volume::_NotifyNodeAdded(Node* node)
1964 {
1965 Node* key = node;
1966
1967 for (int i = 0; i < 2; i++) {
1968 if (NodeListener* listener = fNodeListeners.Lookup(key)) {
1969 NodeListener* last = listener->PreviousNodeListener();
1970
1971 while (true) {
1972 NodeListener* next = listener->NextNodeListener();
1973
1974 listener->NodeAdded(node);
1975
1976 if (listener == last)
1977 break;
1978
1979 listener = next;
1980 }
1981 }
1982
1983 key = NULL;
1984 }
1985 }
1986
1987
1988 void
_NotifyNodeRemoved(Node * node)1989 Volume::_NotifyNodeRemoved(Node* node)
1990 {
1991 Node* key = node;
1992
1993 for (int i = 0; i < 2; i++) {
1994 if (NodeListener* listener = fNodeListeners.Lookup(key)) {
1995 NodeListener* last = listener->PreviousNodeListener();
1996
1997 while (true) {
1998 NodeListener* next = listener->NextNodeListener();
1999
2000 listener->NodeRemoved(node);
2001
2002 if (listener == last)
2003 break;
2004
2005 listener = next;
2006 }
2007 }
2008
2009 key = NULL;
2010 }
2011 }
2012
2013
2014 void
_NotifyNodeChanged(Node * node,uint32 statFields,const OldNodeAttributes & oldAttributes)2015 Volume::_NotifyNodeChanged(Node* node, uint32 statFields,
2016 const OldNodeAttributes& oldAttributes)
2017 {
2018 Node* key = node;
2019
2020 for (int i = 0; i < 2; i++) {
2021 if (NodeListener* listener = fNodeListeners.Lookup(key)) {
2022 NodeListener* last = listener->PreviousNodeListener();
2023
2024 while (true) {
2025 NodeListener* next = listener->NextNodeListener();
2026
2027 listener->NodeChanged(node, statFields, oldAttributes);
2028
2029 if (listener == last)
2030 break;
2031
2032 listener = next;
2033 }
2034 }
2035
2036 key = NULL;
2037 }
2038 }
2039