1/*
2 * Copyright 2011-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "packagefs.h"
8
9#include <errno.h>
10#include <unistd.h>
11
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>
19
20#include <AutoDeleter.h>
21#include <FdIO.h>
22
23#include <util/DoublyLinkedList.h>
24
25#include <Referenceable.h>
26
27#include <boot/PathBlocklist.h>
28#include <boot/platform.h>
29
30#include "PackageSettingsItem.h"
31
32
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;														\
42	}
43#endif
44
45
46using namespace BPackageKit;
47using namespace BPackageKit::BHPKG;
48using BPackageKit::BHPKG::BPrivate::PackageFileHeapReader;
49using BPackageKit::BHPKG::BPrivate::PackageReaderImpl;
50
51using namespace PackageFS;
52
53
54namespace PackageFS {
55
56
57struct PackageDirectory;
58struct PackageNode;
59struct PackageVolume;
60
61
62static status_t create_node(PackageNode* packageNode, ::Node*& _node);
63
64
65// #pragma mark - PackageNode
66
67
68struct PackageNode : DoublyLinkedListLinkImpl<PackageNode> {
69	PackageNode(PackageVolume* volume, mode_t mode)
70		:
71		fVolume(volume),
72		fParentDirectory(NULL),
73		fName(NULL),
74		fNodeID(0),
75		fMode(mode)
76	{
77		fModifiedTime.tv_sec = 0;
78		fModifiedTime.tv_nsec = 0;
79	}
80
81	virtual ~PackageNode()
82	{
83		free(fName);
84	}
85
86	status_t Init(PackageDirectory* parentDir, const char* name, ino_t nodeID)
87	{
88		fParentDirectory = parentDir;
89		fName = strdup(name);
90		fNodeID = nodeID;
91
92		return fName != NULL ? B_OK : B_NO_MEMORY;
93	}
94
95	PackageVolume* Volume() const
96	{
97		return fVolume;
98	}
99
100	const char* Name() const
101	{
102		return fName;
103	}
104
105	ino_t NodeID() const
106	{
107		return fNodeID;
108	}
109
110	mode_t Mode() const
111	{
112		return fMode;
113	}
114
115	void SetModifiedTime(const timespec& time)
116	{
117		fModifiedTime = time;
118	}
119
120	const timespec& ModifiedTime() const
121	{
122		return fModifiedTime;
123	}
124
125	virtual void RemoveEntry(const char* path)
126	{
127	}
128
129protected:
130	PackageVolume*		fVolume;
131	PackageDirectory*	fParentDirectory;
132	char*				fName;
133	ino_t				fNodeID;
134	mode_t				fMode;
135	timespec			fModifiedTime;
136};
137
138
139// #pragma mark - PackageFile
140
141
142struct PackageFile : PackageNode {
143	PackageFile(PackageVolume* volume, mode_t mode, const BPackageData& data)
144		:
145		PackageNode(volume, mode),
146		fData(data)
147	{
148	}
149
150	const BPackageData& Data() const
151	{
152		return fData;
153	}
154
155	off_t Size() const
156	{
157		return fData.Size();
158	}
159
160private:
161	BPackageData	fData;
162};
163
164
165// #pragma mark - PackageSymlink
166
167
168struct PackageSymlink : PackageNode {
169	PackageSymlink(PackageVolume* volume, mode_t mode)
170		:
171		PackageNode(volume, mode),
172		fPath(NULL)
173	{
174	}
175
176	~PackageSymlink()
177	{
178		free(fPath);
179	}
180
181	status_t SetSymlinkPath(const char* path)
182	{
183		fPath = strdup(path);
184		return fPath != NULL ? B_OK : B_NO_MEMORY;
185	}
186
187	const char* SymlinkPath() const
188	{
189		return fPath;
190	}
191
192private:
193	char*	fPath;
194};
195
196
197// #pragma mark - PackageDirectory
198
199
200struct PackageDirectory : PackageNode {
201	PackageDirectory(PackageVolume* volume, mode_t mode)
202		:
203		PackageNode(volume, mode)
204	{
205	}
206
207	~PackageDirectory()
208	{
209		while (PackageNode* node = fEntries.RemoveHead())
210			delete node;
211	}
212
213	void AddChild(PackageNode* node)
214	{
215		fEntries.Add(node);
216	}
217
218	PackageNode* FirstChild() const
219	{
220		return fEntries.Head();
221	}
222
223	PackageNode* NextChild(PackageNode* child) const
224	{
225		return fEntries.GetNext(child);
226	}
227
228	PackageNode* Lookup(const char* name)
229	{
230		if (strcmp(name, ".") == 0)
231			return this;
232		if (strcmp(name, "..") == 0)
233			return fParentDirectory;
234
235		return _LookupChild(name, strlen(name));
236	}
237
238	virtual void RemoveEntry(const char* path)
239	{
240		const char* componentEnd = strchr(path, '/');
241		if (componentEnd == NULL)
242			componentEnd = path + strlen(path);
243
244		PackageNode* child = _LookupChild(path, componentEnd - path);
245		if (child == NULL)
246			return;
247
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);
255		}
256	}
257
258private:
259	typedef DoublyLinkedList<PackageNode>	NodeList;
260
261private:
262	PackageNode* _LookupChild(const char* name, size_t nameLength)
263	{
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;
269			}
270		}
271
272		return NULL;
273	}
274
275private:
276	NodeList	fEntries;
277};
278
279
280// #pragma mark - PackageLoaderErrorOutput
281
282
283struct PackageLoaderErrorOutput : BErrorOutput {
284	PackageLoaderErrorOutput()
285	{
286	}
287
288	virtual void PrintErrorVarArgs(const char* format, va_list args)
289	{
290		char buffer[256];
291		vsnprintf(buffer, sizeof(buffer), format, args);
292		dprintf("%s", buffer);
293	}
294};
295
296
297// #pragma mark - PackageVolume
298
299
300struct PackageVolume : BReferenceable, private PackageLoaderErrorOutput {
301	PackageVolume()
302		:
303		fNextNodeID(1),
304		fRootDirectory(this, S_IFDIR),
305		fHeapReader(NULL),
306		fFile(NULL)
307	{
308	}
309
310	~PackageVolume()
311	{
312		delete fHeapReader;
313		delete fFile;
314	}
315
316	status_t Init(int fd, const PackageFileHeapReader* heapReader)
317	{
318		status_t error = fRootDirectory.Init(&fRootDirectory, ".",
319			NextNodeID());
320		if (error != B_OK)
321			return error;
322
323		fd = dup(fd);
324		if (fd < 0)
325			return errno;
326
327		fFile = new(std::nothrow) BFdIO(fd, true);
328		if (fFile == NULL) {
329			close(fd);
330			return B_NO_MEMORY;
331		}
332
333		// clone a heap reader and adjust it for our use
334		fHeapReader = heapReader->Clone();
335		if (fHeapReader == NULL)
336			return B_NO_MEMORY;
337
338		fHeapReader->SetErrorOutput(this);
339		fHeapReader->SetFile(fFile);
340
341		return B_OK;
342	}
343
344	PackageDirectory* RootDirectory()
345	{
346		return &fRootDirectory;
347	}
348
349	ino_t NextNodeID()
350	{
351		return fNextNodeID++;
352	}
353
354	status_t CreateFileDataReader(const BPackageData& data,
355		BAbstractBufferedDataReader*& _reader)
356	{
357		return BPackageDataReaderFactory().CreatePackageDataReader(fHeapReader,
358			data, _reader);
359	}
360
361private:
362	ino_t						fNextNodeID;
363	PackageDirectory			fRootDirectory;
364	PackageFileHeapReader*		fHeapReader;
365	BPositionIO*				fFile;
366};
367
368
369// #pragma mark - PackageLoaderContentHandler
370
371
372struct PackageLoaderContentHandler : BPackageContentHandler {
373	PackageLoaderContentHandler(PackageVolume* volume,
374		PackageSettingsItem* settingsItem)
375		:
376		fVolume(volume),
377		fSettingsItem(settingsItem),
378		fLastSettingsEntry(NULL),
379		fLastSettingsEntryEntry(NULL),
380		fErrorOccurred(false)
381	{
382	}
383
384	status_t Init()
385	{
386		return B_OK;
387	}
388
389	virtual status_t HandleEntry(BPackageEntry* entry)
390	{
391		if (fErrorOccurred
392			|| (fLastSettingsEntry != NULL
393				&& fLastSettingsEntry->IsBlocked())) {
394			return B_OK;
395		}
396
397		PackageDirectory* parentDir = NULL;
398		if (const BPackageEntry* parentEntry = entry->Parent()) {
399			if (!S_ISDIR(parentEntry->Mode()))
400				RETURN_ERROR(B_BAD_DATA);
401
402			parentDir = static_cast<PackageDirectory*>(
403				(PackageNode*)parentEntry->UserToken());
404		}
405
406		if (fSettingsItem != NULL
407			&& (parentDir == NULL
408				|| entry->Parent() == fLastSettingsEntryEntry)) {
409			PackageSettingsItem::Entry* settingsEntry
410				= fSettingsItem->FindEntry(fLastSettingsEntry, entry->Name());
411			if (settingsEntry != NULL) {
412				fLastSettingsEntry = settingsEntry;
413				fLastSettingsEntryEntry = entry;
414				if (fLastSettingsEntry->IsBlocked())
415					return B_OK;
416			}
417		}
418
419		if (parentDir == NULL)
420			parentDir = fVolume->RootDirectory();
421
422		status_t error;
423
424		// get the file mode -- filter out write permissions
425		mode_t mode = entry->Mode() & ~(mode_t)(S_IWUSR | S_IWGRP | S_IWOTH);
426
427		// create the package node
428		PackageNode* node;
429		if (S_ISREG(mode)) {
430			// file
431			node = new(std::nothrow) PackageFile(fVolume, mode, entry->Data());
432		} else if (S_ISLNK(mode)) {
433			// symlink
434			PackageSymlink* symlink = new(std::nothrow) PackageSymlink(
435				fVolume, mode);
436			if (symlink == NULL)
437				RETURN_ERROR(B_NO_MEMORY);
438
439			error = symlink->SetSymlinkPath(entry->SymlinkPath());
440			if (error != B_OK) {
441				delete symlink;
442				return error;
443			}
444
445			node = symlink;
446		} else if (S_ISDIR(mode)) {
447			// directory
448			node = new(std::nothrow) PackageDirectory(fVolume, mode);
449		} else
450			RETURN_ERROR(B_BAD_DATA);
451
452		if (node == NULL)
453			RETURN_ERROR(B_NO_MEMORY);
454
455		error = node->Init(parentDir, entry->Name(), fVolume->NextNodeID());
456		if (error != B_OK) {
457			delete node;
458			RETURN_ERROR(error);
459		}
460
461		node->SetModifiedTime(entry->ModifiedTime());
462
463		// add it to the parent directory
464		parentDir->AddChild(node);
465
466		entry->SetUserToken(node);
467
468		return B_OK;
469	}
470
471	virtual status_t HandleEntryAttribute(BPackageEntry* entry,
472		BPackageEntryAttribute* attribute)
473	{
474		// attributes aren't needed in the boot loader
475		return B_OK;
476	}
477
478	virtual status_t HandleEntryDone(BPackageEntry* entry)
479	{
480		if (entry == fLastSettingsEntryEntry) {
481			fLastSettingsEntryEntry = entry->Parent();
482			fLastSettingsEntry = fLastSettingsEntry->Parent();
483		}
484
485		return B_OK;
486	}
487
488	virtual status_t HandlePackageAttribute(
489		const BPackageInfoAttributeValue& value)
490	{
491		// TODO?
492		return B_OK;
493	}
494
495	virtual void HandleErrorOccurred()
496	{
497		fErrorOccurred = true;
498	}
499
500private:
501	PackageVolume*				fVolume;
502	const PackageSettingsItem*	fSettingsItem;
503	PackageSettingsItem::Entry*	fLastSettingsEntry;
504	const BPackageEntry*		fLastSettingsEntryEntry;
505	bool						fErrorOccurred;
506};
507
508
509// #pragma mark - File
510
511
512struct File : ::Node {
513	File(PackageFile* file)
514		:
515		fFile(file)
516	{
517		fFile->Volume()->AcquireReference();
518	}
519
520	~File()
521	{
522		fFile->Volume()->ReleaseReference();
523	}
524
525	virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer,
526		size_t bufferSize)
527	{
528		off_t size = fFile->Size();
529		if (pos < 0 || pos > size)
530			return B_BAD_VALUE;
531		if (pos + (off_t)bufferSize > size)
532			bufferSize = size - pos;
533
534		if (bufferSize > 0) {
535			BAbstractBufferedDataReader* dataReader
536				= (BAbstractBufferedDataReader*)cookie;
537			status_t error = dataReader->ReadData(pos, buffer, bufferSize);
538			if (error != B_OK)
539				return error;
540		}
541
542		return bufferSize;
543	}
544
545	virtual ssize_t WriteAt(void* cookie, off_t pos, const void *buffer,
546		size_t bufferSize)
547	{
548		return B_READ_ONLY_DEVICE;
549	}
550
551	virtual status_t GetName(char* nameBuffer, size_t bufferSize) const
552	{
553		strlcpy(nameBuffer, fFile->Name(), bufferSize);
554		return B_OK;
555	}
556
557	virtual status_t Open(void** _cookie, int mode)
558	{
559		if ((mode & O_ACCMODE) != O_RDONLY && (mode & O_ACCMODE) != O_RDWR)
560			return B_NOT_ALLOWED;
561
562		BAbstractBufferedDataReader* dataReader;
563		status_t error = fFile->Volume()->CreateFileDataReader(fFile->Data(),
564			dataReader);
565		if (error != B_OK)
566			return error;
567
568		*_cookie = dataReader;
569		Acquire();
570		return B_OK;
571	}
572
573	virtual status_t Close(void* cookie)
574	{
575		BAbstractBufferedDataReader* dataReader
576			= (BAbstractBufferedDataReader*)cookie;
577		delete dataReader;
578		Release();
579		return B_OK;
580	}
581
582
583	virtual int32 Type() const
584	{
585		return fFile->Mode() & S_IFMT;
586	}
587
588	virtual off_t Size() const
589	{
590		return fFile->Size();
591	}
592
593	virtual ino_t Inode() const
594	{
595		return fFile->NodeID();
596	}
597
598private:
599	PackageFile*	fFile;
600};
601
602
603// #pragma mark - Symlink
604
605
606struct Symlink : ::Node {
607	Symlink(PackageSymlink* symlink)
608		:
609		fSymlink(symlink)
610	{
611		fSymlink->Volume()->AcquireReference();
612	}
613
614	~Symlink()
615	{
616		fSymlink->Volume()->ReleaseReference();
617	}
618
619	virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer,
620		size_t bufferSize)
621	{
622		return B_BAD_VALUE;
623	}
624
625	virtual ssize_t WriteAt(void* cookie, off_t pos, const void *buffer,
626		size_t bufferSize)
627	{
628		return B_READ_ONLY_DEVICE;
629	}
630
631	virtual status_t ReadLink(char* buffer, size_t bufferSize)
632	{
633		const char* path = fSymlink->SymlinkPath();
634		size_t size = strlen(path) + 1;
635
636		if (size > bufferSize)
637			return B_BUFFER_OVERFLOW;
638
639		memcpy(buffer, path, size);
640		return B_OK;
641	}
642
643	virtual status_t GetName(char* nameBuffer, size_t bufferSize) const
644	{
645		strlcpy(nameBuffer, fSymlink->Name(), bufferSize);
646		return B_OK;
647	}
648
649	virtual int32 Type() const
650	{
651		return fSymlink->Mode() & S_IFMT;
652	}
653
654	virtual off_t Size() const
655	{
656		return strlen(fSymlink->SymlinkPath()) + 1;
657	}
658
659	virtual ino_t Inode() const
660	{
661		return fSymlink->NodeID();
662	}
663
664private:
665	PackageSymlink*	fSymlink;
666};
667
668
669// #pragma mark - Directory
670
671
672struct Directory : ::Directory {
673	Directory(PackageDirectory* symlink)
674		:
675		fDirectory(symlink)
676	{
677		fDirectory->Volume()->AcquireReference();
678	}
679
680	~Directory()
681	{
682		fDirectory->Volume()->ReleaseReference();
683	}
684
685	void RemoveEntry(const char* path)
686	{
687		fDirectory->RemoveEntry(path);
688	}
689
690	virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer,
691		size_t bufferSize)
692	{
693		return B_IS_A_DIRECTORY;
694	}
695
696	virtual ssize_t WriteAt(void* cookie, off_t pos, const void *buffer,
697		size_t bufferSize)
698	{
699		return B_IS_A_DIRECTORY;
700	}
701
702	virtual status_t GetName(char* nameBuffer, size_t bufferSize) const
703	{
704		strlcpy(nameBuffer, fDirectory->Name(), bufferSize);
705		return B_OK;
706	}
707
708	virtual int32 Type() const
709	{
710		return fDirectory->Mode() & S_IFMT;
711	}
712
713	virtual ino_t Inode() const
714	{
715		return fDirectory->NodeID();
716	}
717
718	virtual status_t Open(void** _cookie, int mode)
719	{
720		if ((mode & O_ACCMODE) != O_RDONLY && (mode & O_ACCMODE) != O_RDWR)
721			return B_NOT_ALLOWED;
722
723		Cookie* cookie = new(std::nothrow) Cookie;
724		if (cookie == NULL)
725			return B_NO_MEMORY;
726
727		cookie->nextChild = fDirectory->FirstChild();
728
729		Acquire();
730		*_cookie = cookie;
731		return B_OK;
732	}
733
734	virtual status_t Close(void* _cookie)
735	{
736		Cookie* cookie = (Cookie*)_cookie;
737		delete cookie;
738		Release();
739		return B_OK;
740	}
741
742	virtual Node* LookupDontTraverse(const char* name)
743	{
744		// look up the child
745		PackageNode* child = fDirectory->Lookup(name);
746		if (child == NULL)
747			return NULL;
748
749		// create the node
750		::Node* node;
751		return create_node(child, node) == B_OK ? node : NULL;
752	}
753
754	virtual status_t GetNextEntry(void* _cookie, char* nameBuffer,
755		size_t bufferSize)
756	{
757		Cookie* cookie = (Cookie*)_cookie;
758		PackageNode* child = cookie->nextChild;
759		if (child == NULL)
760			return B_ENTRY_NOT_FOUND;
761
762		cookie->nextChild = fDirectory->NextChild(child);
763
764		strlcpy(nameBuffer, child->Name(), bufferSize);
765		return B_OK;
766	}
767
768	virtual status_t GetNextNode(void* _cookie, Node** _node)
769	{
770		Cookie* cookie = (Cookie*)_cookie;
771		PackageNode* child = cookie->nextChild;
772		if (child == NULL)
773			return B_ENTRY_NOT_FOUND;
774
775		cookie->nextChild = fDirectory->NextChild(child);
776
777		return create_node(child, *_node);
778	}
779
780	virtual status_t Rewind(void* _cookie)
781	{
782		Cookie* cookie = (Cookie*)_cookie;
783		cookie->nextChild = NULL;
784		return B_OK;
785	}
786
787	virtual bool IsEmpty()
788	{
789		return fDirectory->FirstChild() == NULL;
790	}
791
792private:
793	struct Cookie {
794		PackageNode*	nextChild;
795	};
796
797
798private:
799	PackageDirectory*	fDirectory;
800};
801
802
803// #pragma mark -
804
805
806static status_t
807create_node(PackageNode* packageNode, ::Node*& _node)
808{
809	if (packageNode == NULL)
810		return B_BAD_VALUE;
811
812	::Node* node;
813	switch (packageNode->Mode() & S_IFMT) {
814		case S_IFREG:
815			node = new(std::nothrow) File(
816				static_cast<PackageFile*>(packageNode));
817			break;
818		case S_IFLNK:
819			node = new(std::nothrow) Symlink(
820				static_cast<PackageSymlink*>(packageNode));
821			break;
822		case S_IFDIR:
823			node = new(std::nothrow) Directory(
824				static_cast<PackageDirectory*>(packageNode));
825			break;
826		default:
827			return B_BAD_VALUE;
828	}
829
830	if (node == NULL)
831		return B_NO_MEMORY;
832
833	_node = node;
834	return B_OK;
835}
836
837
838}	// namespace PackageFS
839
840
841status_t
842packagefs_mount_file(int fd, ::Directory* systemDirectory,
843	::Directory*& _mountedDirectory)
844{
845	PackageLoaderErrorOutput errorOutput;
846 	PackageReaderImpl packageReader(&errorOutput);
847	status_t error = packageReader.Init(fd, false, 0);
848 	if (error != B_OK)
849 		RETURN_ERROR(error);
850
851	// create the volume
852	PackageVolume* volume = new(std::nothrow) PackageVolume;
853	if (volume == NULL)
854		return B_NO_MEMORY;
855	BReference<PackageVolume> volumeReference(volume, true);
856
857	error = volume->Init(fd, packageReader.RawHeapReader());
858	if (error != B_OK)
859		RETURN_ERROR(error);
860
861	// load settings for the package
862	PackageSettingsItem* settings = PackageSettingsItem::Load(systemDirectory,
863		"haiku");
864	ObjectDeleter<PackageSettingsItem> settingsDeleter(settings);
865
866	// parse content -- this constructs the entry/node tree
867	PackageLoaderContentHandler handler(volume, settings);
868	error = handler.Init();
869	if (error != B_OK)
870		RETURN_ERROR(error);
871
872	error = packageReader.ParseContent(&handler);
873	if (error != B_OK)
874		RETURN_ERROR(error);
875
876	// create a VFS node for the root node
877	::Node* rootNode;
878	error = create_node(volume->RootDirectory(), rootNode);
879	if (error != B_OK)
880		RETURN_ERROR(error);
881
882	_mountedDirectory = static_cast< ::Directory*>(rootNode);
883	return B_OK;
884}
885
886
887void
888packagefs_apply_path_blocklist(::Directory* systemDirectory,
889	const PathBlocklist& pathBlocklist)
890{
891	PackageFS::Directory* directory
892		= static_cast<PackageFS::Directory*>(systemDirectory);
893
894	for (PathBlocklist::Iterator it = pathBlocklist.GetIterator();
895		BlockedPath* path = it.Next();) {
896		directory->RemoveEntry(path->Path());
897	}
898}
899
900