headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / boot / loader / file_systems / packagefs / packagefs.cpp
blob3b5dbb59f2bdbbbe8dfc7a457f619be6ae169e01
1 /*
2 * Copyright 2011-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "packagefs.h"
9 #include <errno.h>
10 #include <unistd.h>
12 #include <package/hpkg/DataReader.h>
13 #include <package/hpkg/ErrorOutput.h>
14 #include <package/hpkg/PackageDataReader.h>
15 #include <package/hpkg/PackageEntry.h>
16 #include <package/hpkg/PackageEntryAttribute.h>
17 #include <package/hpkg/PackageFileHeapReader.h>
18 #include <package/hpkg/PackageReaderImpl.h>
20 #include <AutoDeleter.h>
21 #include <FdIO.h>
23 #include <util/DoublyLinkedList.h>
25 #include <Referenceable.h>
27 #include <boot/PathBlacklist.h>
28 #include <boot/platform.h>
30 #include "PackageSettingsItem.h"
33 #if 0
34 # define RETURN_ERROR(error) return (error);
35 #else
36 # define RETURN_ERROR(err) \
37 { \
38 status_t _status = err; \
39 if (_status < B_OK) \
40 dprintf("%s:%d: %s\n", __FILE__, __LINE__, strerror(_status)); \
41 return _status; \
43 #endif
46 using namespace BPackageKit;
47 using namespace BPackageKit::BHPKG;
48 using BPackageKit::BHPKG::BPrivate::PackageFileHeapReader;
49 using BPackageKit::BHPKG::BPrivate::PackageReaderImpl;
51 using namespace PackageFS;
54 namespace PackageFS {
57 struct PackageDirectory;
58 struct PackageNode;
59 struct PackageVolume;
62 static status_t create_node(PackageNode* packageNode, ::Node*& _node);
65 // #pragma mark - PackageNode
68 struct PackageNode : DoublyLinkedListLinkImpl<PackageNode> {
69 PackageNode(PackageVolume* volume, mode_t mode)
71 fVolume(volume),
72 fParentDirectory(NULL),
73 fName(NULL),
74 fNodeID(0),
75 fMode(mode)
77 fModifiedTime.tv_sec = 0;
78 fModifiedTime.tv_nsec = 0;
81 virtual ~PackageNode()
83 free(fName);
86 status_t Init(PackageDirectory* parentDir, const char* name, ino_t nodeID)
88 fParentDirectory = parentDir;
89 fName = strdup(name);
90 fNodeID = nodeID;
92 return fName != NULL ? B_OK : B_NO_MEMORY;
95 PackageVolume* Volume() const
97 return fVolume;
100 const char* Name() const
102 return fName;
105 ino_t NodeID() const
107 return fNodeID;
110 mode_t Mode() const
112 return fMode;
115 void SetModifiedTime(const timespec& time)
117 fModifiedTime = time;
120 const timespec& ModifiedTime() const
122 return fModifiedTime;
125 virtual void RemoveEntry(const char* path)
129 protected:
130 PackageVolume* fVolume;
131 PackageDirectory* fParentDirectory;
132 char* fName;
133 ino_t fNodeID;
134 mode_t fMode;
135 timespec fModifiedTime;
139 // #pragma mark - PackageFile
142 struct PackageFile : PackageNode {
143 PackageFile(PackageVolume* volume, mode_t mode, const BPackageData& data)
145 PackageNode(volume, mode),
146 fData(data)
150 const BPackageData& Data() const
152 return fData;
155 off_t Size() const
157 return fData.Size();
160 private:
161 BPackageData fData;
165 // #pragma mark - PackageSymlink
168 struct PackageSymlink : PackageNode {
169 PackageSymlink(PackageVolume* volume, mode_t mode)
171 PackageNode(volume, mode),
172 fPath(NULL)
176 ~PackageSymlink()
178 free(fPath);
181 status_t SetSymlinkPath(const char* path)
183 fPath = strdup(path);
184 return fPath != NULL ? B_OK : B_NO_MEMORY;
187 const char* SymlinkPath() const
189 return fPath;
192 private:
193 char* fPath;
197 // #pragma mark - PackageDirectory
200 struct PackageDirectory : PackageNode {
201 PackageDirectory(PackageVolume* volume, mode_t mode)
203 PackageNode(volume, mode)
207 ~PackageDirectory()
209 while (PackageNode* node = fEntries.RemoveHead())
210 delete node;
213 void AddChild(PackageNode* node)
215 fEntries.Add(node);
218 PackageNode* FirstChild() const
220 return fEntries.Head();
223 PackageNode* NextChild(PackageNode* child) const
225 return fEntries.GetNext(child);
228 PackageNode* Lookup(const char* name)
230 if (strcmp(name, ".") == 0)
231 return this;
232 if (strcmp(name, "..") == 0)
233 return fParentDirectory;
235 return _LookupChild(name, strlen(name));
238 virtual void RemoveEntry(const char* path)
240 const char* componentEnd = strchr(path, '/');
241 if (componentEnd == NULL)
242 componentEnd = path + strlen(path);
244 PackageNode* child = _LookupChild(path, componentEnd - path);
245 if (child == NULL)
246 return;
248 if (*componentEnd == '\0') {
249 // last path component -- delete the child
250 fEntries.Remove(child);
251 delete child;
252 } else {
253 // must be a directory component -- continue resolving the path
254 child->RemoveEntry(componentEnd + 1);
258 private:
259 typedef DoublyLinkedList<PackageNode> NodeList;
261 private:
262 PackageNode* _LookupChild(const char* name, size_t nameLength)
264 for (NodeList::Iterator it = fEntries.GetIterator();
265 PackageNode* child = it.Next();) {
266 if (strncmp(child->Name(), name, nameLength) == 0
267 && child->Name()[nameLength] == '\0') {
268 return child;
272 return NULL;
275 private:
276 NodeList fEntries;
280 // #pragma mark - PackageLoaderErrorOutput
283 struct PackageLoaderErrorOutput : BErrorOutput {
284 PackageLoaderErrorOutput()
288 virtual void PrintErrorVarArgs(const char* format, va_list args)
294 // #pragma mark - PackageVolume
297 struct PackageVolume : BReferenceable, private PackageLoaderErrorOutput {
298 PackageVolume()
300 fNextNodeID(1),
301 fRootDirectory(this, S_IFDIR),
302 fHeapReader(NULL),
303 fFile(NULL)
307 ~PackageVolume()
309 delete fHeapReader;
310 delete fFile;
313 status_t Init(int fd, const PackageFileHeapReader* heapReader)
315 status_t error = fRootDirectory.Init(&fRootDirectory, ".",
316 NextNodeID());
317 if (error != B_OK)
318 return error;
320 fd = dup(fd);
321 if (fd < 0)
322 return errno;
324 fFile = new(std::nothrow) BFdIO(fd, true);
325 if (fFile == NULL) {
326 close(fd);
327 return B_NO_MEMORY;
330 // clone a heap reader and adjust it for our use
331 fHeapReader = heapReader->Clone();
332 if (fHeapReader == NULL)
333 return B_NO_MEMORY;
335 fHeapReader->SetErrorOutput(this);
336 fHeapReader->SetFile(fFile);
338 return B_OK;
341 PackageDirectory* RootDirectory()
343 return &fRootDirectory;
346 ino_t NextNodeID()
348 return fNextNodeID++;
351 status_t CreateFileDataReader(const BPackageData& data,
352 BAbstractBufferedDataReader*& _reader)
354 return BPackageDataReaderFactory().CreatePackageDataReader(fHeapReader,
355 data, _reader);
358 private:
359 ino_t fNextNodeID;
360 PackageDirectory fRootDirectory;
361 PackageFileHeapReader* fHeapReader;
362 BPositionIO* fFile;
366 // #pragma mark - PackageLoaderContentHandler
369 struct PackageLoaderContentHandler : BPackageContentHandler {
370 PackageLoaderContentHandler(PackageVolume* volume,
371 PackageSettingsItem* settingsItem)
373 fVolume(volume),
374 fSettingsItem(settingsItem),
375 fLastSettingsEntry(NULL),
376 fLastSettingsEntryEntry(NULL),
377 fErrorOccurred(false)
381 status_t Init()
383 return B_OK;
386 virtual status_t HandleEntry(BPackageEntry* entry)
388 if (fErrorOccurred
389 || (fLastSettingsEntry != NULL
390 && fLastSettingsEntry->IsBlackListed())) {
391 return B_OK;
394 PackageDirectory* parentDir = NULL;
395 if (const BPackageEntry* parentEntry = entry->Parent()) {
396 if (!S_ISDIR(parentEntry->Mode()))
397 RETURN_ERROR(B_BAD_DATA);
399 parentDir = static_cast<PackageDirectory*>(
400 (PackageNode*)parentEntry->UserToken());
403 if (fSettingsItem != NULL
404 && (parentDir == NULL
405 || entry->Parent() == fLastSettingsEntryEntry)) {
406 PackageSettingsItem::Entry* settingsEntry
407 = fSettingsItem->FindEntry(fLastSettingsEntry, entry->Name());
408 if (settingsEntry != NULL) {
409 fLastSettingsEntry = settingsEntry;
410 fLastSettingsEntryEntry = entry;
411 if (fLastSettingsEntry->IsBlackListed())
412 return B_OK;
416 if (parentDir == NULL)
417 parentDir = fVolume->RootDirectory();
419 status_t error;
421 // get the file mode -- filter out write permissions
422 mode_t mode = entry->Mode() & ~(mode_t)(S_IWUSR | S_IWGRP | S_IWOTH);
424 // create the package node
425 PackageNode* node;
426 if (S_ISREG(mode)) {
427 // file
428 node = new(std::nothrow) PackageFile(fVolume, mode, entry->Data());
429 } else if (S_ISLNK(mode)) {
430 // symlink
431 PackageSymlink* symlink = new(std::nothrow) PackageSymlink(
432 fVolume, mode);
433 if (symlink == NULL)
434 RETURN_ERROR(B_NO_MEMORY);
436 error = symlink->SetSymlinkPath(entry->SymlinkPath());
437 if (error != B_OK) {
438 delete symlink;
439 return error;
442 node = symlink;
443 } else if (S_ISDIR(mode)) {
444 // directory
445 node = new(std::nothrow) PackageDirectory(fVolume, mode);
446 } else
447 RETURN_ERROR(B_BAD_DATA);
449 if (node == NULL)
450 RETURN_ERROR(B_NO_MEMORY);
452 error = node->Init(parentDir, entry->Name(), fVolume->NextNodeID());
453 if (error != B_OK) {
454 delete node;
455 RETURN_ERROR(error);
458 node->SetModifiedTime(entry->ModifiedTime());
460 // add it to the parent directory
461 parentDir->AddChild(node);
463 entry->SetUserToken(node);
465 return B_OK;
468 virtual status_t HandleEntryAttribute(BPackageEntry* entry,
469 BPackageEntryAttribute* attribute)
471 // attributes aren't needed in the boot loader
472 return B_OK;
475 virtual status_t HandleEntryDone(BPackageEntry* entry)
477 if (entry == fLastSettingsEntryEntry) {
478 fLastSettingsEntryEntry = entry->Parent();
479 fLastSettingsEntry = fLastSettingsEntry->Parent();
482 return B_OK;
485 virtual status_t HandlePackageAttribute(
486 const BPackageInfoAttributeValue& value)
488 // TODO?
489 return B_OK;
492 virtual void HandleErrorOccurred()
494 fErrorOccurred = true;
497 private:
498 PackageVolume* fVolume;
499 const PackageSettingsItem* fSettingsItem;
500 PackageSettingsItem::Entry* fLastSettingsEntry;
501 const BPackageEntry* fLastSettingsEntryEntry;
502 bool fErrorOccurred;
506 // #pragma mark - File
509 struct File : ::Node {
510 File(PackageFile* file)
512 fFile(file)
514 fFile->Volume()->AcquireReference();
517 ~File()
519 fFile->Volume()->ReleaseReference();
522 virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer,
523 size_t bufferSize)
525 off_t size = fFile->Size();
526 if (pos < 0 || pos > size)
527 return B_BAD_VALUE;
528 if (pos + (off_t)bufferSize > size)
529 bufferSize = size - pos;
531 if (bufferSize > 0) {
532 BAbstractBufferedDataReader* dataReader
533 = (BAbstractBufferedDataReader*)cookie;
534 status_t error = dataReader->ReadData(pos, buffer, bufferSize);
535 if (error != B_OK)
536 return error;
539 return bufferSize;
542 virtual ssize_t WriteAt(void* cookie, off_t pos, const void *buffer,
543 size_t bufferSize)
545 return B_READ_ONLY_DEVICE;
548 virtual status_t GetName(char* nameBuffer, size_t bufferSize) const
550 strlcpy(nameBuffer, fFile->Name(), bufferSize);
551 return B_OK;
554 virtual status_t Open(void** _cookie, int mode)
556 if ((mode & O_ACCMODE) != O_RDONLY && (mode & O_ACCMODE) != O_RDWR)
557 return B_NOT_ALLOWED;
559 BAbstractBufferedDataReader* dataReader;
560 status_t error = fFile->Volume()->CreateFileDataReader(fFile->Data(),
561 dataReader);
562 if (error != B_OK)
563 return error;
565 *_cookie = dataReader;
566 Acquire();
567 return B_OK;
570 virtual status_t Close(void* cookie)
572 BAbstractBufferedDataReader* dataReader
573 = (BAbstractBufferedDataReader*)cookie;
574 delete dataReader;
575 Release();
576 return B_OK;
580 virtual int32 Type() const
582 return fFile->Mode() & S_IFMT;
585 virtual off_t Size() const
587 return fFile->Size();
590 virtual ino_t Inode() const
592 return fFile->NodeID();
595 private:
596 PackageFile* fFile;
600 // #pragma mark - Symlink
603 struct Symlink : ::Node {
604 Symlink(PackageSymlink* symlink)
606 fSymlink(symlink)
608 fSymlink->Volume()->AcquireReference();
611 ~Symlink()
613 fSymlink->Volume()->ReleaseReference();
616 virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer,
617 size_t bufferSize)
619 return B_BAD_VALUE;
622 virtual ssize_t WriteAt(void* cookie, off_t pos, const void *buffer,
623 size_t bufferSize)
625 return B_READ_ONLY_DEVICE;
628 virtual status_t ReadLink(char* buffer, size_t bufferSize)
630 const char* path = fSymlink->SymlinkPath();
631 size_t size = strlen(path) + 1;
633 if (size > bufferSize)
634 return B_BUFFER_OVERFLOW;
636 memcpy(buffer, path, size);
637 return B_OK;
640 virtual status_t GetName(char* nameBuffer, size_t bufferSize) const
642 strlcpy(nameBuffer, fSymlink->Name(), bufferSize);
643 return B_OK;
646 virtual int32 Type() const
648 return fSymlink->Mode() & S_IFMT;
651 virtual off_t Size() const
653 return strlen(fSymlink->SymlinkPath()) + 1;
656 virtual ino_t Inode() const
658 return fSymlink->NodeID();
661 private:
662 PackageSymlink* fSymlink;
666 // #pragma mark - Directory
669 struct Directory : ::Directory {
670 Directory(PackageDirectory* symlink)
672 fDirectory(symlink)
674 fDirectory->Volume()->AcquireReference();
677 ~Directory()
679 fDirectory->Volume()->ReleaseReference();
682 void RemoveEntry(const char* path)
684 fDirectory->RemoveEntry(path);
687 virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer,
688 size_t bufferSize)
690 return B_IS_A_DIRECTORY;
693 virtual ssize_t WriteAt(void* cookie, off_t pos, const void *buffer,
694 size_t bufferSize)
696 return B_IS_A_DIRECTORY;
699 virtual status_t GetName(char* nameBuffer, size_t bufferSize) const
701 strlcpy(nameBuffer, fDirectory->Name(), bufferSize);
702 return B_OK;
705 virtual int32 Type() const
707 return fDirectory->Mode() & S_IFMT;
710 virtual ino_t Inode() const
712 return fDirectory->NodeID();
715 virtual status_t Open(void** _cookie, int mode)
717 if ((mode & O_ACCMODE) != O_RDONLY && (mode & O_ACCMODE) != O_RDWR)
718 return B_NOT_ALLOWED;
720 Cookie* cookie = new(std::nothrow) Cookie;
721 if (cookie == NULL)
722 return B_NO_MEMORY;
724 cookie->nextChild = fDirectory->FirstChild();
726 Acquire();
727 *_cookie = cookie;
728 return B_OK;
731 virtual status_t Close(void* _cookie)
733 Cookie* cookie = (Cookie*)_cookie;
734 delete cookie;
735 Release();
736 return B_OK;
739 virtual Node* LookupDontTraverse(const char* name)
741 // look up the child
742 PackageNode* child = fDirectory->Lookup(name);
743 if (child == NULL)
744 return NULL;
746 // create the node
747 ::Node* node;
748 return create_node(child, node) == B_OK ? node : NULL;
751 virtual status_t GetNextEntry(void* _cookie, char* nameBuffer,
752 size_t bufferSize)
754 Cookie* cookie = (Cookie*)_cookie;
755 PackageNode* child = cookie->nextChild;
756 if (child == NULL)
757 return B_ENTRY_NOT_FOUND;
759 cookie->nextChild = fDirectory->NextChild(child);
761 strlcpy(nameBuffer, child->Name(), bufferSize);
762 return B_OK;
765 virtual status_t GetNextNode(void* _cookie, Node** _node)
767 Cookie* cookie = (Cookie*)_cookie;
768 PackageNode* child = cookie->nextChild;
769 if (child == NULL)
770 return B_ENTRY_NOT_FOUND;
772 cookie->nextChild = fDirectory->NextChild(child);
774 return create_node(child, *_node);
777 virtual status_t Rewind(void* _cookie)
779 Cookie* cookie = (Cookie*)_cookie;
780 cookie->nextChild = NULL;
781 return B_OK;
784 virtual bool IsEmpty()
786 return fDirectory->FirstChild() == NULL;
789 private:
790 struct Cookie {
791 PackageNode* nextChild;
795 private:
796 PackageDirectory* fDirectory;
800 // #pragma mark -
803 static status_t
804 create_node(PackageNode* packageNode, ::Node*& _node)
806 if (packageNode == NULL)
807 return B_BAD_VALUE;
809 ::Node* node;
810 switch (packageNode->Mode() & S_IFMT) {
811 case S_IFREG:
812 node = new(std::nothrow) File(
813 static_cast<PackageFile*>(packageNode));
814 break;
815 case S_IFLNK:
816 node = new(std::nothrow) Symlink(
817 static_cast<PackageSymlink*>(packageNode));
818 break;
819 case S_IFDIR:
820 node = new(std::nothrow) Directory(
821 static_cast<PackageDirectory*>(packageNode));
822 break;
823 default:
824 return B_BAD_VALUE;
827 if (node == NULL)
828 return B_NO_MEMORY;
830 _node = node;
831 return B_OK;
835 } // namespace PackageFS
838 status_t
839 packagefs_mount_file(int fd, ::Directory* systemDirectory,
840 ::Directory*& _mountedDirectory)
842 PackageLoaderErrorOutput errorOutput;
843 PackageReaderImpl packageReader(&errorOutput);
844 status_t error = packageReader.Init(fd, false, 0);
845 if (error != B_OK)
846 RETURN_ERROR(error);
848 // create the volume
849 PackageVolume* volume = new(std::nothrow) PackageVolume;
850 if (volume == NULL)
851 return B_NO_MEMORY;
852 BReference<PackageVolume> volumeReference(volume, true);
854 error = volume->Init(fd, packageReader.RawHeapReader());
855 if (error != B_OK)
856 RETURN_ERROR(error);
858 // load settings for the package
859 PackageSettingsItem* settings = PackageSettingsItem::Load(systemDirectory,
860 "haiku");
861 ObjectDeleter<PackageSettingsItem> settingsDeleter(settings);
863 // parse content -- this constructs the entry/node tree
864 PackageLoaderContentHandler handler(volume, settings);
865 error = handler.Init();
866 if (error != B_OK)
867 RETURN_ERROR(error);
869 error = packageReader.ParseContent(&handler);
870 if (error != B_OK)
871 RETURN_ERROR(error);
873 // create a VFS node for the root node
874 ::Node* rootNode;
875 error = create_node(volume->RootDirectory(), rootNode);
876 if (error != B_OK)
877 RETURN_ERROR(error);
879 _mountedDirectory = static_cast< ::Directory*>(rootNode);
880 return B_OK;
884 void
885 packagefs_apply_path_blacklist(::Directory* systemDirectory,
886 const PathBlacklist& pathBlacklist)
888 PackageFS::Directory* directory
889 = static_cast<PackageFS::Directory*>(systemDirectory);
891 for (PathBlacklist::Iterator it = pathBlacklist.GetIterator();
892 BlacklistedPath* path = it.Next();) {
893 directory->RemoveEntry(path->Path());