2 * Copyright 2011-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
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>
23 #include <util/DoublyLinkedList.h>
25 #include <Referenceable.h>
27 #include <boot/PathBlacklist.h>
28 #include <boot/platform.h>
30 #include "PackageSettingsItem.h"
34 # define RETURN_ERROR(error) return (error);
36 # define RETURN_ERROR(err) \
38 status_t _status = err; \
40 dprintf("%s:%d: %s\n", __FILE__, __LINE__, strerror(_status)); \
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
;
57 struct PackageDirectory
;
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
)
72 fParentDirectory(NULL
),
77 fModifiedTime
.tv_sec
= 0;
78 fModifiedTime
.tv_nsec
= 0;
81 virtual ~PackageNode()
86 status_t
Init(PackageDirectory
* parentDir
, const char* name
, ino_t nodeID
)
88 fParentDirectory
= parentDir
;
92 return fName
!= NULL
? B_OK
: B_NO_MEMORY
;
95 PackageVolume
* Volume() const
100 const char* Name() const
115 void SetModifiedTime(const timespec
& time
)
117 fModifiedTime
= time
;
120 const timespec
& ModifiedTime() const
122 return fModifiedTime
;
125 virtual void RemoveEntry(const char* path
)
130 PackageVolume
* fVolume
;
131 PackageDirectory
* fParentDirectory
;
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
),
150 const BPackageData
& Data() const
165 // #pragma mark - PackageSymlink
168 struct PackageSymlink
: PackageNode
{
169 PackageSymlink(PackageVolume
* volume
, mode_t mode
)
171 PackageNode(volume
, mode
),
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
197 // #pragma mark - PackageDirectory
200 struct PackageDirectory
: PackageNode
{
201 PackageDirectory(PackageVolume
* volume
, mode_t mode
)
203 PackageNode(volume
, mode
)
209 while (PackageNode
* node
= fEntries
.RemoveHead())
213 void AddChild(PackageNode
* 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)
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
);
248 if (*componentEnd
== '\0') {
249 // last path component -- delete the child
250 fEntries
.Remove(child
);
253 // must be a directory component -- continue resolving the path
254 child
->RemoveEntry(componentEnd
+ 1);
259 typedef DoublyLinkedList
<PackageNode
> NodeList
;
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') {
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
{
301 fRootDirectory(this, S_IFDIR
),
313 status_t
Init(int fd
, const PackageFileHeapReader
* heapReader
)
315 status_t error
= fRootDirectory
.Init(&fRootDirectory
, ".",
324 fFile
= new(std::nothrow
) BFdIO(fd
, true);
330 // clone a heap reader and adjust it for our use
331 fHeapReader
= heapReader
->Clone();
332 if (fHeapReader
== NULL
)
335 fHeapReader
->SetErrorOutput(this);
336 fHeapReader
->SetFile(fFile
);
341 PackageDirectory
* RootDirectory()
343 return &fRootDirectory
;
348 return fNextNodeID
++;
351 status_t
CreateFileDataReader(const BPackageData
& data
,
352 BAbstractBufferedDataReader
*& _reader
)
354 return BPackageDataReaderFactory().CreatePackageDataReader(fHeapReader
,
360 PackageDirectory fRootDirectory
;
361 PackageFileHeapReader
* fHeapReader
;
366 // #pragma mark - PackageLoaderContentHandler
369 struct PackageLoaderContentHandler
: BPackageContentHandler
{
370 PackageLoaderContentHandler(PackageVolume
* volume
,
371 PackageSettingsItem
* settingsItem
)
374 fSettingsItem(settingsItem
),
375 fLastSettingsEntry(NULL
),
376 fLastSettingsEntryEntry(NULL
),
377 fErrorOccurred(false)
386 virtual status_t
HandleEntry(BPackageEntry
* entry
)
389 || (fLastSettingsEntry
!= NULL
390 && fLastSettingsEntry
->IsBlackListed())) {
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())
416 if (parentDir
== NULL
)
417 parentDir
= fVolume
->RootDirectory();
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
428 node
= new(std::nothrow
) PackageFile(fVolume
, mode
, entry
->Data());
429 } else if (S_ISLNK(mode
)) {
431 PackageSymlink
* symlink
= new(std::nothrow
) PackageSymlink(
434 RETURN_ERROR(B_NO_MEMORY
);
436 error
= symlink
->SetSymlinkPath(entry
->SymlinkPath());
443 } else if (S_ISDIR(mode
)) {
445 node
= new(std::nothrow
) PackageDirectory(fVolume
, mode
);
447 RETURN_ERROR(B_BAD_DATA
);
450 RETURN_ERROR(B_NO_MEMORY
);
452 error
= node
->Init(parentDir
, entry
->Name(), fVolume
->NextNodeID());
458 node
->SetModifiedTime(entry
->ModifiedTime());
460 // add it to the parent directory
461 parentDir
->AddChild(node
);
463 entry
->SetUserToken(node
);
468 virtual status_t
HandleEntryAttribute(BPackageEntry
* entry
,
469 BPackageEntryAttribute
* attribute
)
471 // attributes aren't needed in the boot loader
475 virtual status_t
HandleEntryDone(BPackageEntry
* entry
)
477 if (entry
== fLastSettingsEntryEntry
) {
478 fLastSettingsEntryEntry
= entry
->Parent();
479 fLastSettingsEntry
= fLastSettingsEntry
->Parent();
485 virtual status_t
HandlePackageAttribute(
486 const BPackageInfoAttributeValue
& value
)
492 virtual void HandleErrorOccurred()
494 fErrorOccurred
= true;
498 PackageVolume
* fVolume
;
499 const PackageSettingsItem
* fSettingsItem
;
500 PackageSettingsItem::Entry
* fLastSettingsEntry
;
501 const BPackageEntry
* fLastSettingsEntryEntry
;
506 // #pragma mark - File
509 struct File
: ::Node
{
510 File(PackageFile
* file
)
514 fFile
->Volume()->AcquireReference();
519 fFile
->Volume()->ReleaseReference();
522 virtual ssize_t
ReadAt(void* cookie
, off_t pos
, void* buffer
,
525 off_t size
= fFile
->Size();
526 if (pos
< 0 || pos
> size
)
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
);
542 virtual ssize_t
WriteAt(void* cookie
, off_t pos
, const void *buffer
,
545 return B_READ_ONLY_DEVICE
;
548 virtual status_t
GetName(char* nameBuffer
, size_t bufferSize
) const
550 strlcpy(nameBuffer
, fFile
->Name(), bufferSize
);
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(),
565 *_cookie
= dataReader
;
570 virtual status_t
Close(void* cookie
)
572 BAbstractBufferedDataReader
* dataReader
573 = (BAbstractBufferedDataReader
*)cookie
;
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();
600 // #pragma mark - Symlink
603 struct Symlink
: ::Node
{
604 Symlink(PackageSymlink
* symlink
)
608 fSymlink
->Volume()->AcquireReference();
613 fSymlink
->Volume()->ReleaseReference();
616 virtual ssize_t
ReadAt(void* cookie
, off_t pos
, void* buffer
,
622 virtual ssize_t
WriteAt(void* cookie
, off_t pos
, const void *buffer
,
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
);
640 virtual status_t
GetName(char* nameBuffer
, size_t bufferSize
) const
642 strlcpy(nameBuffer
, fSymlink
->Name(), bufferSize
);
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();
662 PackageSymlink
* fSymlink
;
666 // #pragma mark - Directory
669 struct Directory
: ::Directory
{
670 Directory(PackageDirectory
* symlink
)
674 fDirectory
->Volume()->AcquireReference();
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
,
690 return B_IS_A_DIRECTORY
;
693 virtual ssize_t
WriteAt(void* cookie
, off_t pos
, const void *buffer
,
696 return B_IS_A_DIRECTORY
;
699 virtual status_t
GetName(char* nameBuffer
, size_t bufferSize
) const
701 strlcpy(nameBuffer
, fDirectory
->Name(), bufferSize
);
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
;
724 cookie
->nextChild
= fDirectory
->FirstChild();
731 virtual status_t
Close(void* _cookie
)
733 Cookie
* cookie
= (Cookie
*)_cookie
;
739 virtual Node
* LookupDontTraverse(const char* name
)
742 PackageNode
* child
= fDirectory
->Lookup(name
);
748 return create_node(child
, node
) == B_OK
? node
: NULL
;
751 virtual status_t
GetNextEntry(void* _cookie
, char* nameBuffer
,
754 Cookie
* cookie
= (Cookie
*)_cookie
;
755 PackageNode
* child
= cookie
->nextChild
;
757 return B_ENTRY_NOT_FOUND
;
759 cookie
->nextChild
= fDirectory
->NextChild(child
);
761 strlcpy(nameBuffer
, child
->Name(), bufferSize
);
765 virtual status_t
GetNextNode(void* _cookie
, Node
** _node
)
767 Cookie
* cookie
= (Cookie
*)_cookie
;
768 PackageNode
* child
= cookie
->nextChild
;
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
;
784 virtual bool IsEmpty()
786 return fDirectory
->FirstChild() == NULL
;
791 PackageNode
* nextChild
;
796 PackageDirectory
* fDirectory
;
804 create_node(PackageNode
* packageNode
, ::Node
*& _node
)
806 if (packageNode
== NULL
)
810 switch (packageNode
->Mode() & S_IFMT
) {
812 node
= new(std::nothrow
) File(
813 static_cast<PackageFile
*>(packageNode
));
816 node
= new(std::nothrow
) Symlink(
817 static_cast<PackageSymlink
*>(packageNode
));
820 node
= new(std::nothrow
) Directory(
821 static_cast<PackageDirectory
*>(packageNode
));
835 } // namespace PackageFS
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);
849 PackageVolume
* volume
= new(std::nothrow
) PackageVolume
;
852 BReference
<PackageVolume
> volumeReference(volume
, true);
854 error
= volume
->Init(fd
, packageReader
.RawHeapReader());
858 // load settings for the package
859 PackageSettingsItem
* settings
= PackageSettingsItem::Load(systemDirectory
,
861 ObjectDeleter
<PackageSettingsItem
> settingsDeleter(settings
);
863 // parse content -- this constructs the entry/node tree
864 PackageLoaderContentHandler
handler(volume
, settings
);
865 error
= handler
.Init();
869 error
= packageReader
.ParseContent(&handler
);
873 // create a VFS node for the root node
875 error
= create_node(volume
->RootDirectory(), rootNode
);
879 _mountedDirectory
= static_cast< ::Directory
*>(rootNode
);
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());