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 "Package.h"
8
9#include <errno.h>
10#include <fcntl.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
14
15#include <package/hpkg/ErrorOutput.h>
16#include <package/hpkg/PackageDataReader.h>
17#include <package/hpkg/PackageEntry.h>
18#include <package/hpkg/PackageEntryAttribute.h>
19
20#include <AutoDeleter.h>
21#include <FdIO.h>
22#include <package/hpkg/PackageFileHeapReader.h>
23#include <package/hpkg/PackageReaderImpl.h>
24#include <util/AutoLock.h>
25
26#include "CachedDataReader.h"
27#include "DebugSupport.h"
28#include "PackageDirectory.h"
29#include "PackageFile.h"
30#include "PackagesDirectory.h"
31#include "PackageSettings.h"
32#include "PackageSymlink.h"
33#include "Version.h"
34#include "Volume.h"
35
36
37using namespace BPackageKit;
38
39using BPackageKit::BHPKG::BErrorOutput;
40using BPackageKit::BHPKG::BFDDataReader;
41using BPackageKit::BHPKG::BPackageInfoAttributeValue;
42using BPackageKit::BHPKG::BPackageVersionData;
43using BPackageKit::BHPKG::BPrivate::PackageFileHeapReader;
44
45// current format version types
46typedef BPackageKit::BHPKG::BPackageContentHandler BPackageContentHandler;
47typedef BPackageKit::BHPKG::BPackageEntry BPackageEntry;
48typedef BPackageKit::BHPKG::BPackageEntryAttribute BPackageEntryAttribute;
49typedef BPackageKit::BHPKG::BPrivate::PackageReaderImpl PackageReaderImpl;
50
51
52const char* const kArchitectureNames[B_PACKAGE_ARCHITECTURE_ENUM_COUNT] = {
53	"any",
54	"x86",
55	"x86_gcc2",
56	"source",
57	"x86_64",
58	"ppc",
59	"arm",
60	"m68k",
61	"sparc",
62	"arm64",
63	"riscv64"
64};
65
66
67// #pragma mark - LoaderErrorOutput
68
69
70struct Package::LoaderErrorOutput : BErrorOutput {
71	LoaderErrorOutput(Package* package)
72		:
73		fPackage(package)
74	{
75	}
76
77	virtual void PrintErrorVarArgs(const char* format, va_list args)
78	{
79		ERRORV(format, args);
80	}
81
82private:
83	Package*	fPackage;
84};
85
86
87// #pragma mark - LoaderContentHandler
88
89
90struct Package::LoaderContentHandler : BPackageContentHandler {
91	LoaderContentHandler(Package* package, const PackageSettings& settings)
92		:
93		fPackage(package),
94		fSettings(settings),
95		fSettingsItem(NULL),
96		fLastSettingsEntry(NULL),
97		fLastSettingsEntryEntry(NULL),
98		fErrorOccurred(false)
99	{
100	}
101
102	status_t Init()
103	{
104		return B_OK;
105	}
106
107	virtual status_t HandleEntry(BPackageEntry* entry)
108	{
109		if (fErrorOccurred
110			|| (fLastSettingsEntry != NULL
111				&& fLastSettingsEntry->IsBlocked())) {
112			return B_OK;
113		}
114
115		PackageDirectory* parentDir = NULL;
116		if (entry->Parent() != NULL) {
117			parentDir = dynamic_cast<PackageDirectory*>(
118				(PackageNode*)entry->Parent()->UserToken());
119			if (parentDir == NULL)
120				RETURN_ERROR(B_BAD_DATA);
121		}
122
123		if (fSettingsItem != NULL
124			&& (parentDir == NULL
125				|| entry->Parent() == fLastSettingsEntryEntry)) {
126			PackageSettingsItem::Entry* settingsEntry
127				= fSettingsItem->FindEntry(fLastSettingsEntry, entry->Name());
128			if (settingsEntry != NULL) {
129				fLastSettingsEntry = settingsEntry;
130				fLastSettingsEntryEntry = entry;
131				if (fLastSettingsEntry->IsBlocked())
132					return B_OK;
133			}
134		}
135
136		// get the file mode -- filter out write permissions
137		mode_t mode = entry->Mode() & ~(mode_t)(S_IWUSR | S_IWGRP | S_IWOTH);
138
139		// create the package node
140		PackageNode* node;
141		if (S_ISREG(mode)) {
142			// file
143			node = new PackageFile(fPackage, mode,
144				PackageData(entry->Data()));
145		} else if (S_ISLNK(mode)) {
146			// symlink
147			String path;
148			if (!path.SetTo(entry->SymlinkPath()))
149				RETURN_ERROR(B_NO_MEMORY);
150
151			PackageSymlink* symlink = new(std::nothrow) PackageSymlink(
152				fPackage, mode);
153			if (symlink == NULL)
154				RETURN_ERROR(B_NO_MEMORY);
155
156			symlink->SetSymlinkPath(path);
157			node = symlink;
158		} else if (S_ISDIR(mode)) {
159			// directory
160			node = new PackageDirectory(fPackage, mode);
161		} else
162			RETURN_ERROR(B_BAD_DATA);
163
164		if (node == NULL)
165			RETURN_ERROR(B_NO_MEMORY);
166		BReference<PackageNode> nodeReference(node, true);
167
168		String entryName;
169		if (!entryName.SetTo(entry->Name()))
170			RETURN_ERROR(B_NO_MEMORY);
171
172		status_t error = node->Init(parentDir, entryName);
173		if (error != B_OK)
174			RETURN_ERROR(error);
175
176		node->SetModifiedTime(entry->ModifiedTime());
177
178		// add it to the parent directory
179		if (parentDir != NULL)
180			parentDir->AddChild(node);
181		else
182			fPackage->AddNode(node);
183
184		entry->SetUserToken(node);
185
186		return B_OK;
187	}
188
189	virtual status_t HandleEntryAttribute(BPackageEntry* entry,
190		BPackageEntryAttribute* attribute)
191	{
192		if (fErrorOccurred
193			|| (fLastSettingsEntry != NULL
194				&& fLastSettingsEntry->IsBlocked())) {
195			return B_OK;
196		}
197
198		PackageNode* node = (PackageNode*)entry->UserToken();
199
200		String name;
201		if (!name.SetTo(attribute->Name()))
202			RETURN_ERROR(B_NO_MEMORY);
203
204		PackageNodeAttribute* nodeAttribute = new
205			PackageNodeAttribute(attribute->Type(),
206			PackageData(attribute->Data()));
207		if (nodeAttribute == NULL)
208			RETURN_ERROR(B_NO_MEMORY)
209
210		nodeAttribute->Init(name);
211		node->AddAttribute(nodeAttribute);
212
213		return B_OK;
214	}
215
216	virtual status_t HandleEntryDone(BPackageEntry* entry)
217	{
218		if (entry == fLastSettingsEntryEntry) {
219			fLastSettingsEntryEntry = entry->Parent();
220			fLastSettingsEntry = fLastSettingsEntry->Parent();
221		}
222
223		return B_OK;
224	}
225
226	virtual status_t HandlePackageAttribute(
227		const BPackageInfoAttributeValue& value)
228	{
229		switch (value.attributeID) {
230			case B_PACKAGE_INFO_NAME:
231			{
232				String name;
233				if (!name.SetTo(value.string))
234					return B_NO_MEMORY;
235				fPackage->SetName(name);
236
237				fSettingsItem = fSettings.PackageItemFor(fPackage->Name());
238
239				return B_OK;
240			}
241
242			case B_PACKAGE_INFO_INSTALL_PATH:
243			{
244				String path;
245				if (!path.SetTo(value.string))
246					return B_NO_MEMORY;
247				fPackage->SetInstallPath(path);
248				return B_OK;
249			}
250
251			case B_PACKAGE_INFO_VERSION:
252			{
253				::Version* version;
254				status_t error = Version::Create(value.version.major,
255					value.version.minor, value.version.micro,
256					value.version.preRelease, value.version.revision, version);
257				if (error != B_OK)
258					RETURN_ERROR(error);
259
260				fPackage->SetVersion(version);
261				break;
262			}
263
264			case B_PACKAGE_INFO_FLAGS:
265				fPackage->SetFlags(value.unsignedInt);
266				break;
267
268			case B_PACKAGE_INFO_ARCHITECTURE:
269				if (value.unsignedInt >= B_PACKAGE_ARCHITECTURE_ENUM_COUNT)
270					RETURN_ERROR(B_BAD_VALUE);
271
272				fPackage->SetArchitecture(
273					(BPackageArchitecture)value.unsignedInt);
274				break;
275
276			case B_PACKAGE_INFO_PROVIDES:
277			{
278				// create a version object, if a version is specified
279				::Version* version = NULL;
280				if (value.resolvable.haveVersion) {
281					const BPackageVersionData& versionInfo
282						= value.resolvable.version;
283					status_t error = Version::Create(versionInfo.major,
284						versionInfo.minor, versionInfo.micro,
285						versionInfo.preRelease, versionInfo.revision, version);
286					if (error != B_OK)
287						RETURN_ERROR(error);
288				}
289				ObjectDeleter< ::Version> versionDeleter(version);
290
291				// create a version object, if a compatible version is specified
292				::Version* compatibleVersion = NULL;
293				if (value.resolvable.haveCompatibleVersion) {
294					const BPackageVersionData& versionInfo
295						= value.resolvable.compatibleVersion;
296					status_t error = Version::Create(versionInfo.major,
297						versionInfo.minor, versionInfo.micro,
298						versionInfo.preRelease, versionInfo.revision,
299						compatibleVersion);
300					if (error != B_OK)
301						RETURN_ERROR(error);
302				}
303				ObjectDeleter< ::Version> compatibleVersionDeleter(
304					compatibleVersion);
305
306				// create the resolvable
307				Resolvable* resolvable = new(std::nothrow) Resolvable(fPackage);
308				if (resolvable == NULL)
309					RETURN_ERROR(B_NO_MEMORY);
310				ObjectDeleter<Resolvable> resolvableDeleter(resolvable);
311
312				status_t error = resolvable->Init(value.resolvable.name,
313					versionDeleter.Detach(), compatibleVersionDeleter.Detach());
314				if (error != B_OK)
315					RETURN_ERROR(error);
316
317				fPackage->AddResolvable(resolvableDeleter.Detach());
318
319				break;
320			}
321
322			case B_PACKAGE_INFO_REQUIRES:
323			{
324				// create the dependency
325				Dependency* dependency = new(std::nothrow) Dependency(fPackage);
326				if (dependency == NULL)
327					RETURN_ERROR(B_NO_MEMORY);
328				ObjectDeleter<Dependency> dependencyDeleter(dependency);
329
330				status_t error = dependency->Init(
331					value.resolvableExpression.name);
332				if (error != B_OK)
333					RETURN_ERROR(error);
334
335				// create a version object, if a version is specified
336				::Version* version = NULL;
337				if (value.resolvableExpression.haveOpAndVersion) {
338					const BPackageVersionData& versionInfo
339						= value.resolvableExpression.version;
340					status_t error = Version::Create(versionInfo.major,
341						versionInfo.minor, versionInfo.micro,
342						versionInfo.preRelease, versionInfo.revision, version);
343					if (error != B_OK)
344						RETURN_ERROR(error);
345
346					dependency->SetVersionRequirement(
347						value.resolvableExpression.op, version);
348				}
349
350				fPackage->AddDependency(dependencyDeleter.Detach());
351
352				break;
353			}
354
355			default:
356				break;
357		}
358
359		return B_OK;
360	}
361
362	virtual void HandleErrorOccurred()
363	{
364		fErrorOccurred = true;
365	}
366
367private:
368	Package*					fPackage;
369	const PackageSettings&		fSettings;
370	const PackageSettingsItem*	fSettingsItem;
371	PackageSettingsItem::Entry*	fLastSettingsEntry;
372	const BPackageEntry*		fLastSettingsEntryEntry;
373	bool						fErrorOccurred;
374};
375
376
377// #pragma mark - HeapReader
378
379
380struct Package::HeapReader {
381	virtual ~HeapReader()
382	{
383	}
384
385	virtual void UpdateFD(int fd) = 0;
386
387	virtual status_t CreateDataReader(const PackageData& data,
388		BAbstractBufferedDataReader*& _reader) = 0;
389};
390
391
392// #pragma mark - HeapReaderV2
393
394
395struct Package::HeapReaderV2 : public HeapReader, public CachedDataReader,
396	private BErrorOutput, private BFdIO {
397public:
398	HeapReaderV2()
399		:
400		fHeapReader(NULL)
401	{
402	}
403
404	~HeapReaderV2()
405	{
406		delete fHeapReader;
407	}
408
409	status_t Init(const PackageFileHeapReader* heapReader, int fd)
410	{
411		fHeapReader = heapReader->Clone();
412		if (fHeapReader == NULL)
413			return B_NO_MEMORY;
414
415		BFdIO::SetTo(fd, false);
416
417		fHeapReader->SetErrorOutput(this);
418		fHeapReader->SetFile(this);
419
420		status_t error = CachedDataReader::Init(fHeapReader,
421			fHeapReader->UncompressedHeapSize());
422		if (error != B_OK)
423			return error;
424
425		return B_OK;
426	}
427
428	virtual void UpdateFD(int fd)
429	{
430		BFdIO::SetTo(fd, false);
431	}
432
433	virtual status_t CreateDataReader(const PackageData& data,
434		BAbstractBufferedDataReader*& _reader)
435	{
436		return BPackageKit::BHPKG::BPackageDataReaderFactory()
437			.CreatePackageDataReader(this, data.DataV2(), _reader);
438	}
439
440private:
441	// BErrorOutput
442
443	virtual void PrintErrorVarArgs(const char* format, va_list args)
444	{
445		ERRORV(format, args);
446	}
447
448private:
449	PackageFileHeapReader*	fHeapReader;
450};
451
452
453// #pragma mark - Package
454
455
456struct Package::CachingPackageReader : public PackageReaderImpl {
457	CachingPackageReader(BErrorOutput* errorOutput)
458		:
459		PackageReaderImpl(errorOutput),
460		fCachedHeapReader(NULL),
461		fFD(-1)
462	{
463	}
464
465	~CachingPackageReader()
466	{
467	}
468
469	status_t Init(int fd, bool keepFD, uint32 flags)
470	{
471		fFD = fd;
472		return PackageReaderImpl::Init(fd, keepFD, flags);
473	}
474
475	virtual status_t CreateCachedHeapReader(
476		PackageFileHeapReader* rawHeapReader,
477		BAbstractBufferedDataReader*& _cachedReader)
478	{
479		fCachedHeapReader = new(std::nothrow) HeapReaderV2;
480		if (fCachedHeapReader == NULL)
481			RETURN_ERROR(B_NO_MEMORY);
482
483		status_t error = fCachedHeapReader->Init(rawHeapReader, fFD);
484		if (error != B_OK)
485			RETURN_ERROR(error);
486
487		_cachedReader = fCachedHeapReader;
488		return B_OK;
489	}
490
491	HeapReaderV2* DetachCachedHeapReader()
492	{
493		PackageFileHeapReader* rawHeapReader;
494		DetachHeapReader(rawHeapReader);
495
496		// We don't need the raw heap reader anymore, since the cached reader
497		// is not a wrapper around it, but completely independent from it.
498		delete rawHeapReader;
499
500		HeapReaderV2* cachedHeapReader = fCachedHeapReader;
501		fCachedHeapReader = NULL;
502		return cachedHeapReader;
503	}
504
505private:
506	HeapReaderV2*	fCachedHeapReader;
507	int				fFD;
508};
509
510
511// #pragma mark - Package
512
513
514Package::Package(::Volume* volume, PackagesDirectory* directory, dev_t deviceID,
515	ino_t nodeID)
516	:
517	fVolume(volume),
518	fPackagesDirectory(directory),
519	fFileName(),
520	fName(),
521	fInstallPath(),
522	fVersionedName(),
523	fVersion(NULL),
524	fFlags(0),
525	fArchitecture(B_PACKAGE_ARCHITECTURE_ENUM_COUNT),
526	fLinkDirectory(NULL),
527	fFD(-1),
528	fOpenCount(0),
529	fHeapReader(NULL),
530	fNodeID(nodeID),
531	fDeviceID(deviceID)
532{
533	mutex_init(&fLock, "packagefs package");
534
535	fPackagesDirectory->AcquireReference();
536}
537
538
539Package::~Package()
540{
541	delete fHeapReader;
542
543	while (PackageNode* node = fNodes.RemoveHead())
544		node->ReleaseReference();
545
546	while (Resolvable* resolvable = fResolvables.RemoveHead())
547		delete resolvable;
548
549	while (Dependency* dependency = fDependencies.RemoveHead())
550		delete dependency;
551
552	delete fVersion;
553
554	fPackagesDirectory->ReleaseReference();
555
556	mutex_destroy(&fLock);
557}
558
559
560status_t
561Package::Init(const char* fileName)
562{
563	if (!fFileName.SetTo(fileName))
564		RETURN_ERROR(B_NO_MEMORY);
565
566	return B_OK;
567}
568
569
570status_t
571Package::Load(const PackageSettings& settings)
572{
573	status_t error = _Load(settings);
574	if (error != B_OK)
575		return error;
576
577	if (!_InitVersionedName())
578		RETURN_ERROR(B_NO_MEMORY);
579
580	return B_OK;
581}
582
583
584void
585Package::SetName(const String& name)
586{
587	fName = name;
588}
589
590
591void
592Package::SetInstallPath(const String& installPath)
593{
594	fInstallPath = installPath;
595}
596
597
598void
599Package::SetVersion(::Version* version)
600{
601	if (fVersion != NULL)
602		delete fVersion;
603
604	fVersion = version;
605}
606
607
608const char*
609Package::ArchitectureName() const
610{
611	if (fArchitecture < 0
612		|| fArchitecture >= B_PACKAGE_ARCHITECTURE_ENUM_COUNT) {
613		return NULL;
614	}
615
616	return kArchitectureNames[fArchitecture];
617}
618
619
620void
621Package::AddNode(PackageNode* node)
622{
623	fNodes.Add(node);
624	node->AcquireReference();
625}
626
627
628void
629Package::AddResolvable(Resolvable* resolvable)
630{
631	fResolvables.Add(resolvable);
632}
633
634
635void
636Package::AddDependency(Dependency* dependency)
637{
638	fDependencies.Add(dependency);
639}
640
641
642int
643Package::Open()
644{
645	MutexLocker locker(fLock);
646	if (fOpenCount > 0) {
647		fOpenCount++;
648		return fFD;
649	}
650
651	// open the file
652	fFD = openat(fPackagesDirectory->DirectoryFD(), fFileName,
653		O_RDONLY | O_NOCACHE);
654	if (fFD < 0) {
655		ERROR("Failed to open package file \"%s\": %s\n", fFileName.Data(),
656			strerror(errno));
657		return errno;
658	}
659
660	// stat it to verify that it's still the same file
661	struct stat st;
662	if (fstat(fFD, &st) < 0) {
663		ERROR("Failed to stat package file \"%s\": %s\n", fFileName.Data(),
664			strerror(errno));
665		close(fFD);
666		fFD = -1;
667		return errno;
668	}
669
670	if (st.st_dev != fDeviceID || st.st_ino != fNodeID) {
671		close(fFD);
672		fFD = -1;
673		RETURN_ERROR(B_ENTRY_NOT_FOUND);
674	}
675
676	fOpenCount = 1;
677
678	if (fHeapReader != NULL)
679		fHeapReader->UpdateFD(fFD);
680
681	return fFD;
682}
683
684
685void
686Package::Close()
687{
688	MutexLocker locker(fLock);
689	if (fOpenCount == 0) {
690		ERROR("Package open count already 0!\n");
691		return;
692	}
693
694	if (--fOpenCount == 0) {
695		close(fFD);
696		fFD = -1;
697
698		if (fHeapReader != NULL)
699			fHeapReader->UpdateFD(fFD);
700	}
701}
702
703
704status_t
705Package::CreateDataReader(const PackageData& data,
706	BAbstractBufferedDataReader*& _reader)
707{
708	if (fHeapReader == NULL)
709		return B_BAD_VALUE;
710
711	return fHeapReader->CreateDataReader(data, _reader);
712}
713
714
715status_t
716Package::_Load(const PackageSettings& settings)
717{
718	// open package file
719	int fd = Open();
720	if (fd < 0)
721		RETURN_ERROR(fd);
722	PackageCloser packageCloser(this);
723
724	// initialize package reader
725	LoaderErrorOutput errorOutput(this);
726
727	// try current package file format version
728	{
729		CachingPackageReader packageReader(&errorOutput);
730		status_t error = packageReader.Init(fd, false,
731			BHPKG::B_HPKG_READER_DONT_PRINT_VERSION_MISMATCH_MESSAGE);
732		if (error == B_OK) {
733			// parse content
734			LoaderContentHandler handler(this, settings);
735			error = handler.Init();
736			if (error != B_OK)
737				RETURN_ERROR(error);
738
739			error = packageReader.ParseContent(&handler);
740			if (error != B_OK)
741				RETURN_ERROR(error);
742
743			// get the heap reader
744			fHeapReader = packageReader.DetachCachedHeapReader();
745			return B_OK;
746		}
747
748		if (error != B_MISMATCHED_VALUES)
749			RETURN_ERROR(error);
750	}
751
752	// we don't support this package file format
753	RETURN_ERROR(B_BAD_DATA);
754}
755
756
757bool
758Package::_InitVersionedName()
759{
760	// compute the allocation size needed for the versioned name
761	size_t nameLength = strlen(fName);
762	size_t size = nameLength + 1;
763
764	if (fVersion != NULL) {
765		size += 1 + fVersion->ToString(NULL, 0);
766			// + 1 for the '-'
767	}
768
769	// allocate the name and compose it
770	char* name = (char*)malloc(size);
771	if (name == NULL)
772		return false;
773	MemoryDeleter nameDeleter(name);
774
775	memcpy(name, fName, nameLength + 1);
776	if (fVersion != NULL) {
777		name[nameLength] = '-';
778		fVersion->ToString(name + nameLength + 1, size - nameLength - 1);
779	}
780
781	return fVersionedName.SetTo(name);
782}
783