xref: /haiku/src/kits/package/hpkg/WriterImplBase.cpp (revision 268f99dd7dc4bd7474a8bd2742d3f1ec1de6752a)
1 /*
2  * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <package/hpkg/WriterImplBase.h>
8 
9 #include <errno.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 
15 #include <algorithm>
16 #include <new>
17 
18 #include <ByteOrder.h>
19 #include <File.h>
20 
21 #include <AutoDeleter.h>
22 #include <ZlibCompressionAlgorithm.h>
23 #include <ZstdCompressionAlgorithm.h>
24 
25 #include <package/hpkg/DataReader.h>
26 #include <package/hpkg/ErrorOutput.h>
27 
28 #include <package/hpkg/HPKGDefsPrivate.h>
29 
30 
31 namespace BPackageKit {
32 
33 namespace BHPKG {
34 
35 namespace BPrivate {
36 
37 
38 // #pragma mark - AttributeValue
39 
40 
AttributeValue()41 WriterImplBase::AttributeValue::AttributeValue()
42 	:
43 	type(B_HPKG_ATTRIBUTE_TYPE_INVALID),
44 	encoding(-1)
45 {
46 }
47 
48 
~AttributeValue()49 WriterImplBase::AttributeValue::~AttributeValue()
50 {
51 }
52 
53 
54 void
SetTo(int8 value)55 WriterImplBase::AttributeValue::SetTo(int8 value)
56 {
57 	signedInt = value;
58 	type = B_HPKG_ATTRIBUTE_TYPE_INT;
59 }
60 
61 
62 void
SetTo(uint8 value)63 WriterImplBase::AttributeValue::SetTo(uint8 value)
64 {
65 	unsignedInt = value;
66 	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
67 }
68 
69 
70 void
SetTo(int16 value)71 WriterImplBase::AttributeValue::SetTo(int16 value)
72 {
73 	signedInt = value;
74 	type = B_HPKG_ATTRIBUTE_TYPE_INT;
75 }
76 
77 
78 void
SetTo(uint16 value)79 WriterImplBase::AttributeValue::SetTo(uint16 value)
80 {
81 	unsignedInt = value;
82 	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
83 }
84 
85 
86 void
SetTo(int32 value)87 WriterImplBase::AttributeValue::SetTo(int32 value)
88 {
89 	signedInt = value;
90 	type = B_HPKG_ATTRIBUTE_TYPE_INT;
91 }
92 
93 
94 void
SetTo(uint32 value)95 WriterImplBase::AttributeValue::SetTo(uint32 value)
96 {
97 	unsignedInt = value;
98 	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
99 }
100 
101 
102 void
SetTo(int64 value)103 WriterImplBase::AttributeValue::SetTo(int64 value)
104 {
105 	signedInt = value;
106 	type = B_HPKG_ATTRIBUTE_TYPE_INT;
107 }
108 
109 
110 void
SetTo(uint64 value)111 WriterImplBase::AttributeValue::SetTo(uint64 value)
112 {
113 	unsignedInt = value;
114 	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
115 }
116 
117 
118 void
SetTo(CachedString * value)119 WriterImplBase::AttributeValue::SetTo(CachedString* value)
120 {
121 	string = value;
122 	type = B_HPKG_ATTRIBUTE_TYPE_STRING;
123 }
124 
125 
126 void
SetToData(uint64 size,uint64 offset)127 WriterImplBase::AttributeValue::SetToData(uint64 size, uint64 offset)
128 {
129 	data.size = size;
130 	data.offset = offset;
131 	type = B_HPKG_ATTRIBUTE_TYPE_RAW;
132 	encoding = B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP;
133 }
134 
135 
136 void
SetToData(uint64 size,const void * rawData)137 WriterImplBase::AttributeValue::SetToData(uint64 size, const void* rawData)
138 {
139 	data.size = size;
140 	if (size > 0)
141 		memcpy(data.raw, rawData, size);
142 	type = B_HPKG_ATTRIBUTE_TYPE_RAW;
143 	encoding = B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE;
144 }
145 
146 
147 uint8
ApplicableEncoding() const148 WriterImplBase::AttributeValue::ApplicableEncoding() const
149 {
150 	switch (type) {
151 		case B_HPKG_ATTRIBUTE_TYPE_INT:
152 			return _ApplicableIntEncoding(signedInt >= 0
153 				? (uint64)signedInt << 1
154 				: (uint64)(-(signedInt + 1) << 1));
155 		case B_HPKG_ATTRIBUTE_TYPE_UINT:
156 			return _ApplicableIntEncoding(unsignedInt);
157 		case B_HPKG_ATTRIBUTE_TYPE_STRING:
158 			return string->index >= 0
159 				? B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE
160 				: B_HPKG_ATTRIBUTE_ENCODING_STRING_INLINE;
161 		case B_HPKG_ATTRIBUTE_TYPE_RAW:
162 			return encoding;
163 		default:
164 			return 0;
165 	}
166 }
167 
168 
169 /*static*/ uint8
_ApplicableIntEncoding(uint64 value)170 WriterImplBase::AttributeValue::_ApplicableIntEncoding(uint64 value)
171 {
172 	if (value <= 0xff)
173 		return B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT;
174 	if (value <= 0xffff)
175 		return B_HPKG_ATTRIBUTE_ENCODING_INT_16_BIT;
176 	if (value <= 0xffffffff)
177 		return B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT;
178 
179 	return B_HPKG_ATTRIBUTE_ENCODING_INT_64_BIT;
180 }
181 
182 
183 // #pragma mark - PackageAttribute
184 
185 
PackageAttribute(BHPKGAttributeID id_,uint8 type_,uint8 encoding_)186 WriterImplBase::PackageAttribute::PackageAttribute(BHPKGAttributeID id_,
187 	uint8 type_, uint8 encoding_)
188 	:
189 	id(id_)
190 {
191 	type = type_;
192 	encoding = encoding_;
193 }
194 
195 
~PackageAttribute()196 WriterImplBase::PackageAttribute::~PackageAttribute()
197 {
198 	_DeleteChildren();
199 }
200 
201 
202 void
AddChild(PackageAttribute * child)203 WriterImplBase::PackageAttribute::AddChild(PackageAttribute* child)
204 {
205 	children.Add(child);
206 }
207 
208 
209 void
_DeleteChildren()210 WriterImplBase::PackageAttribute::_DeleteChildren()
211 {
212 	while (PackageAttribute* child = children.RemoveHead())
213 		delete child;
214 }
215 
216 
217 // #pragma mark - WriterImplBase
218 
219 
WriterImplBase(const char * fileType,BErrorOutput * errorOutput)220 WriterImplBase::WriterImplBase(const char* fileType, BErrorOutput* errorOutput)
221 	:
222 	fHeapWriter(NULL),
223 	fCompressionAlgorithm(NULL),
224 	fCompressionParameters(NULL),
225 	fDecompressionAlgorithm(NULL),
226 	fDecompressionParameters(NULL),
227 	fFileType(fileType),
228 	fErrorOutput(errorOutput),
229 	fFileName(NULL),
230 	fParameters(),
231 	fFile(NULL),
232 	fOwnsFile(false),
233 	fFinished(false)
234 {
235 }
236 
237 
~WriterImplBase()238 WriterImplBase::~WriterImplBase()
239 {
240 	delete fHeapWriter;
241 	delete fCompressionAlgorithm;
242 	delete fCompressionParameters;
243 	delete fDecompressionAlgorithm;
244 	delete fDecompressionParameters;
245 
246 	if (fOwnsFile)
247 		delete fFile;
248 
249 	if (!fFinished && fFileName != NULL
250 		&& (Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) == 0) {
251 		unlink(fFileName);
252 	}
253 }
254 
255 
256 status_t
Init(BPositionIO * file,bool keepFile,const char * fileName,const BPackageWriterParameters & parameters)257 WriterImplBase::Init(BPositionIO* file, bool keepFile, const char* fileName,
258 	const BPackageWriterParameters& parameters)
259 {
260 	fParameters = parameters;
261 
262 	if (fPackageStringCache.Init() != B_OK)
263 		throw std::bad_alloc();
264 
265 	if (file == NULL) {
266 		if (fileName == NULL)
267 			return B_BAD_VALUE;
268 
269 		// open file (don't truncate in update mode)
270 		int openMode = O_RDWR;
271 		if ((parameters.Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) == 0)
272 			openMode |= O_CREAT | O_TRUNC;
273 
274 		BFile* newFile = new BFile;
275 		status_t error = newFile->SetTo(fileName, openMode);
276 		if (error != B_OK) {
277 			fErrorOutput->PrintError("Failed to open %s file \"%s\": %s\n",
278 				fFileType, fileName, strerror(errno));
279 			delete newFile;
280 			return error;
281 		}
282 
283 		fFile = newFile;
284 		fOwnsFile = true;
285 	} else {
286 		fFile = file;
287 		fOwnsFile = keepFile;
288 	}
289 
290 	fFileName = fileName;
291 
292 	return B_OK;
293 }
294 
295 
296 status_t
InitHeapReader(size_t headerSize)297 WriterImplBase::InitHeapReader(size_t headerSize)
298 {
299 	// allocate the compression/decompression algorithm
300 	CompressionAlgorithmOwner* compressionAlgorithm = NULL;
301 	BReference<CompressionAlgorithmOwner> compressionAlgorithmReference;
302 
303 	DecompressionAlgorithmOwner* decompressionAlgorithm = NULL;
304 	BReference<DecompressionAlgorithmOwner> decompressionAlgorithmReference;
305 
306 	switch (fParameters.Compression()) {
307 		case B_HPKG_COMPRESSION_NONE:
308 			break;
309 		case B_HPKG_COMPRESSION_ZLIB:
310 			compressionAlgorithm = CompressionAlgorithmOwner::Create(
311 				new(std::nothrow) BZlibCompressionAlgorithm,
312 				new(std::nothrow) BZlibCompressionParameters(
313 					(fParameters.CompressionLevel() / float(B_HPKG_COMPRESSION_LEVEL_BEST))
314 						* B_ZLIB_COMPRESSION_BEST));
315 			compressionAlgorithmReference.SetTo(compressionAlgorithm, true);
316 
317 			decompressionAlgorithm = DecompressionAlgorithmOwner::Create(
318 				new(std::nothrow) BZlibCompressionAlgorithm,
319 				new(std::nothrow) BZlibDecompressionParameters);
320 			decompressionAlgorithmReference.SetTo(decompressionAlgorithm, true);
321 
322 			if (compressionAlgorithm == NULL
323 				|| compressionAlgorithm->algorithm == NULL
324 				|| compressionAlgorithm->parameters == NULL
325 				|| decompressionAlgorithm == NULL
326 				|| decompressionAlgorithm->algorithm == NULL
327 				|| decompressionAlgorithm->parameters == NULL) {
328 				throw std::bad_alloc();
329 			}
330 			break;
331 		case B_HPKG_COMPRESSION_ZSTD:
332 			compressionAlgorithm = CompressionAlgorithmOwner::Create(
333 				new(std::nothrow) BZstdCompressionAlgorithm,
334 				new(std::nothrow) BZstdCompressionParameters(
335 					(fParameters.CompressionLevel() / float(B_HPKG_COMPRESSION_LEVEL_BEST))
336 						* B_ZSTD_COMPRESSION_BEST));
337 			compressionAlgorithmReference.SetTo(compressionAlgorithm, true);
338 
339 			decompressionAlgorithm = DecompressionAlgorithmOwner::Create(
340 				new(std::nothrow) BZstdCompressionAlgorithm,
341 				new(std::nothrow) BZstdDecompressionParameters);
342 			decompressionAlgorithmReference.SetTo(decompressionAlgorithm, true);
343 
344 			if (compressionAlgorithm == NULL
345 				|| compressionAlgorithm->algorithm == NULL
346 				|| compressionAlgorithm->parameters == NULL
347 				|| decompressionAlgorithm == NULL
348 				|| decompressionAlgorithm->algorithm == NULL
349 				|| decompressionAlgorithm->parameters == NULL) {
350 				throw std::bad_alloc();
351 			}
352 			break;
353 		default:
354 			fErrorOutput->PrintError("Error: Invalid heap compression\n");
355 			return B_BAD_VALUE;
356 	}
357 
358 	// create heap writer
359 	fHeapWriter = new PackageFileHeapWriter(fErrorOutput, fFile, headerSize,
360 		compressionAlgorithm, decompressionAlgorithm);
361 	fHeapWriter->Init();
362 
363 	return B_OK;
364 }
365 
366 
367 void
SetCompression(uint32 compression)368 WriterImplBase::SetCompression(uint32 compression)
369 {
370 	fParameters.SetCompression(compression);
371 }
372 
373 
374 void
RegisterPackageInfo(PackageAttributeList & attributeList,const BPackageInfo & packageInfo)375 WriterImplBase::RegisterPackageInfo(PackageAttributeList& attributeList,
376 	const BPackageInfo& packageInfo)
377 {
378 	// name
379 	AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_NAME, packageInfo.Name(),
380 		attributeList);
381 
382 	// summary
383 	AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_SUMMARY,
384 		packageInfo.Summary(), attributeList);
385 
386 	// description
387 	AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_DESCRIPTION,
388 		packageInfo.Description(), attributeList);
389 
390 	// vendor
391 	AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_VENDOR,
392 		packageInfo.Vendor(), attributeList);
393 
394 	// packager
395 	AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_PACKAGER,
396 		packageInfo.Packager(), attributeList);
397 
398 	// base package (optional)
399 	_AddStringAttributeIfNotEmpty(B_HPKG_ATTRIBUTE_ID_PACKAGE_BASE_PACKAGE,
400 		packageInfo.BasePackage(), attributeList);
401 
402 	// flags
403 	PackageAttribute* flags = new PackageAttribute(
404 		B_HPKG_ATTRIBUTE_ID_PACKAGE_FLAGS, B_HPKG_ATTRIBUTE_TYPE_UINT,
405 		B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT);
406 	flags->unsignedInt = packageInfo.Flags();
407 	attributeList.Add(flags);
408 
409 	// architecture
410 	PackageAttribute* architecture = new PackageAttribute(
411 		B_HPKG_ATTRIBUTE_ID_PACKAGE_ARCHITECTURE, B_HPKG_ATTRIBUTE_TYPE_UINT,
412 		B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
413 	architecture->unsignedInt = packageInfo.Architecture();
414 	attributeList.Add(architecture);
415 
416 	// version
417 	RegisterPackageVersion(attributeList, packageInfo.Version());
418 
419 	// copyright list
420 	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_COPYRIGHT,
421 			packageInfo.CopyrightList(), attributeList);
422 
423 	// license list
424 	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_LICENSE,
425 		packageInfo.LicenseList(), attributeList);
426 
427 	// URL list
428 	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_URL,
429 		packageInfo.URLList(), attributeList);
430 
431 	// source URL list
432 	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_SOURCE_URL,
433 		packageInfo.SourceURLList(), attributeList);
434 
435 	// provides list
436 	const BObjectList<BPackageResolvable>& providesList
437 		= packageInfo.ProvidesList();
438 	for (int i = 0; i < providesList.CountItems(); ++i) {
439 		BPackageResolvable* resolvable = providesList.ItemAt(i);
440 		bool hasVersion = resolvable->Version().InitCheck() == B_OK;
441 		bool hasCompatibleVersion
442 			= resolvable->CompatibleVersion().InitCheck() == B_OK;
443 
444 		PackageAttribute* provides = AddStringAttribute(
445 			B_HPKG_ATTRIBUTE_ID_PACKAGE_PROVIDES, resolvable->Name(),
446 			attributeList);
447 
448 		if (hasVersion)
449 			RegisterPackageVersion(provides->children, resolvable->Version());
450 
451 		if (hasCompatibleVersion) {
452 			RegisterPackageVersion(provides->children,
453 				resolvable->CompatibleVersion(),
454 				B_HPKG_ATTRIBUTE_ID_PACKAGE_PROVIDES_COMPATIBLE);
455 		}
456 	}
457 
458 	// requires list
459 	RegisterPackageResolvableExpressionList(attributeList,
460 		packageInfo.RequiresList(), B_HPKG_ATTRIBUTE_ID_PACKAGE_REQUIRES);
461 
462 	// supplements list
463 	RegisterPackageResolvableExpressionList(attributeList,
464 		packageInfo.SupplementsList(), B_HPKG_ATTRIBUTE_ID_PACKAGE_SUPPLEMENTS);
465 
466 	// conflicts list
467 	RegisterPackageResolvableExpressionList(attributeList,
468 		packageInfo.ConflictsList(), B_HPKG_ATTRIBUTE_ID_PACKAGE_CONFLICTS);
469 
470 	// freshens list
471 	RegisterPackageResolvableExpressionList(attributeList,
472 		packageInfo.FreshensList(), B_HPKG_ATTRIBUTE_ID_PACKAGE_FRESHENS);
473 
474 	// replaces list
475 	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_REPLACES,
476 		packageInfo.ReplacesList(), attributeList);
477 
478 	// global writable file info list
479 	const BObjectList<BGlobalWritableFileInfo>& globalWritableFileInfos
480 		= packageInfo.GlobalWritableFileInfos();
481 	for (int32 i = 0; i < globalWritableFileInfos.CountItems(); ++i) {
482 		BGlobalWritableFileInfo* info = globalWritableFileInfos.ItemAt(i);
483 		PackageAttribute* attribute = AddStringAttribute(
484 			B_HPKG_ATTRIBUTE_ID_PACKAGE_GLOBAL_WRITABLE_FILE, info->Path(),
485 			attributeList);
486 
487 		if (info->IsDirectory()) {
488 			PackageAttribute* isDirectoryAttribute = new PackageAttribute(
489 				B_HPKG_ATTRIBUTE_ID_PACKAGE_IS_WRITABLE_DIRECTORY,
490 				B_HPKG_ATTRIBUTE_TYPE_UINT,
491 				B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
492 			isDirectoryAttribute->unsignedInt = 1;
493 			attribute->children.Add(isDirectoryAttribute);
494 		}
495 
496 		if (info->IsIncluded()) {
497 			PackageAttribute* updateTypeAttribute = new PackageAttribute(
498 				B_HPKG_ATTRIBUTE_ID_PACKAGE_WRITABLE_FILE_UPDATE_TYPE,
499 				B_HPKG_ATTRIBUTE_TYPE_UINT,
500 				B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
501 			updateTypeAttribute->unsignedInt = info->UpdateType();
502 			attribute->children.Add(updateTypeAttribute);
503 		}
504 	}
505 
506 	// user settings file info list
507 	const BObjectList<BUserSettingsFileInfo>& userSettingsFileInfos
508 		= packageInfo.UserSettingsFileInfos();
509 	for (int32 i = 0; i < userSettingsFileInfos.CountItems(); ++i) {
510 		BUserSettingsFileInfo* info = userSettingsFileInfos.ItemAt(i);
511 		PackageAttribute* attribute = AddStringAttribute(
512 			B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_SETTINGS_FILE, info->Path(),
513 			attributeList);
514 
515 		if (info->IsDirectory()) {
516 			PackageAttribute* isDirectoryAttribute = new PackageAttribute(
517 				B_HPKG_ATTRIBUTE_ID_PACKAGE_IS_WRITABLE_DIRECTORY,
518 				B_HPKG_ATTRIBUTE_TYPE_UINT,
519 				B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
520 			isDirectoryAttribute->unsignedInt = 1;
521 			attribute->children.Add(isDirectoryAttribute);
522 		} else {
523 			_AddStringAttributeIfNotEmpty(
524 				B_HPKG_ATTRIBUTE_ID_PACKAGE_SETTINGS_FILE_TEMPLATE,
525 				info->TemplatePath(), attribute->children);
526 		}
527 	}
528 
529 	// user list
530 	const BObjectList<BUser>& users = packageInfo.Users();
531 	for (int32 i = 0; i < users.CountItems(); ++i) {
532 		const BUser* user = users.ItemAt(i);
533 		PackageAttribute* attribute = AddStringAttribute(
534 			B_HPKG_ATTRIBUTE_ID_PACKAGE_USER, user->Name(), attributeList);
535 
536 		_AddStringAttributeIfNotEmpty(
537 			B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_REAL_NAME, user->RealName(),
538 			attribute->children);
539 		_AddStringAttributeIfNotEmpty(
540 			B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_HOME, user->Home(),
541 			attribute->children);
542 		_AddStringAttributeIfNotEmpty(
543 			B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_SHELL, user->Shell(),
544 			attribute->children);
545 
546 		for (int32 k = 0; k < user->Groups().CountStrings(); k++) {
547 			AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_GROUP,
548 				user->Groups().StringAt(k), attribute->children);
549 		}
550 	}
551 
552 	// group list
553 	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_GROUP,
554 		packageInfo.Groups(), attributeList);
555 
556 	// post install script list
557 	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_POST_INSTALL_SCRIPT,
558 		packageInfo.PostInstallScripts(), attributeList);
559 
560 	// pre uninstall script list
561 	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_PRE_UNINSTALL_SCRIPT,
562 		packageInfo.PreUninstallScripts(), attributeList);
563 
564 	// checksum (optional, only exists in repositories)
565 	_AddStringAttributeIfNotEmpty(B_HPKG_ATTRIBUTE_ID_PACKAGE_CHECKSUM,
566 		packageInfo.Checksum(), attributeList);
567 
568 	// install path (optional)
569 	_AddStringAttributeIfNotEmpty(B_HPKG_ATTRIBUTE_ID_PACKAGE_INSTALL_PATH,
570 		packageInfo.InstallPath(), attributeList);
571 }
572 
573 
574 void
RegisterPackageVersion(PackageAttributeList & attributeList,const BPackageVersion & version,BHPKGAttributeID attributeID)575 WriterImplBase::RegisterPackageVersion(PackageAttributeList& attributeList,
576 	const BPackageVersion& version, BHPKGAttributeID attributeID)
577 {
578 	PackageAttribute* versionMajor = AddStringAttribute(attributeID,
579 		version.Major(), attributeList);
580 
581 	if (!version.Minor().IsEmpty()) {
582 		AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_MINOR,
583 			version.Minor(), versionMajor->children);
584 		_AddStringAttributeIfNotEmpty(
585 			B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_MICRO, version.Micro(),
586 			versionMajor->children);
587 	}
588 
589 	_AddStringAttributeIfNotEmpty(
590 		B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_PRE_RELEASE,
591 		version.PreRelease(), versionMajor->children);
592 
593 	if (version.Revision() != 0) {
594 		PackageAttribute* versionRevision = new PackageAttribute(
595 			B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_REVISION,
596 			B_HPKG_ATTRIBUTE_TYPE_UINT, B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT);
597 		versionRevision->unsignedInt = version.Revision();
598 		versionMajor->children.Add(versionRevision);
599 	}
600 }
601 
602 
603 void
RegisterPackageResolvableExpressionList(PackageAttributeList & attributeList,const BObjectList<BPackageResolvableExpression> & expressionList,uint8 id)604 WriterImplBase::RegisterPackageResolvableExpressionList(
605 	PackageAttributeList& attributeList,
606 	const BObjectList<BPackageResolvableExpression>& expressionList, uint8 id)
607 {
608 	for (int i = 0; i < expressionList.CountItems(); ++i) {
609 		BPackageResolvableExpression* resolvableExpr = expressionList.ItemAt(i);
610 		PackageAttribute* name = AddStringAttribute((BHPKGAttributeID)id,
611 			resolvableExpr->Name(), attributeList);
612 
613 		if (resolvableExpr->Version().InitCheck() == B_OK) {
614 			PackageAttribute* op = new PackageAttribute(
615 				B_HPKG_ATTRIBUTE_ID_PACKAGE_RESOLVABLE_OPERATOR,
616 				B_HPKG_ATTRIBUTE_TYPE_UINT,
617 				B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
618 			op->unsignedInt = resolvableExpr->Operator();
619 			name->children.Add(op);
620 			RegisterPackageVersion(name->children, resolvableExpr->Version());
621 		}
622 	}
623 }
624 
625 
626 WriterImplBase::PackageAttribute*
AddStringAttribute(BHPKGAttributeID id,const BString & value,DoublyLinkedList<PackageAttribute> & list)627 WriterImplBase::AddStringAttribute(BHPKGAttributeID id, const BString& value,
628 	DoublyLinkedList<PackageAttribute>& list)
629 {
630 	PackageAttribute* attribute = new PackageAttribute(id,
631 		B_HPKG_ATTRIBUTE_TYPE_STRING, B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
632 	attribute->string = fPackageStringCache.Get(value);
633 	list.Add(attribute);
634 	return attribute;
635 }
636 
637 
638 int32
WriteCachedStrings(const StringCache & cache,uint32 minUsageCount)639 WriterImplBase::WriteCachedStrings(const StringCache& cache,
640 	uint32 minUsageCount)
641 {
642 	// create an array of the cached strings
643 	int32 count = cache.CountElements();
644 	CachedString** cachedStrings = new CachedString*[count];
645 	ArrayDeleter<CachedString*> cachedStringsDeleter(cachedStrings);
646 
647 	int32 index = 0;
648 	for (CachedStringTable::Iterator it = cache.GetIterator();
649 			CachedString* string = it.Next();) {
650 		cachedStrings[index++] = string;
651 	}
652 
653 	// sort it by descending usage count
654 	std::sort(cachedStrings, cachedStrings + count, CachedStringUsageGreater());
655 
656 	// assign the indices and write entries to disk
657 	int32 stringsWritten = 0;
658 	for (int32 i = 0; i < count; i++) {
659 		CachedString* cachedString = cachedStrings[i];
660 
661 		// empty strings must be stored inline, as they can't be distinguished
662 		// from the end-marker!
663 		if (strlen(cachedString->string) == 0)
664 			continue;
665 
666 		// strings that are used only once are better stored inline
667 		if (cachedString->usageCount < minUsageCount)
668 			break;
669 
670 		WriteString(cachedString->string);
671 
672 		cachedString->index = stringsWritten++;
673 	}
674 
675 	// write a terminating 0 byte
676 	Write<uint8>(0);
677 
678 	return stringsWritten;
679 }
680 
681 
682 int32
WritePackageAttributes(const PackageAttributeList & packageAttributes,uint32 & _stringsLengthUncompressed)683 WriterImplBase::WritePackageAttributes(
684 	const PackageAttributeList& packageAttributes,
685 	uint32& _stringsLengthUncompressed)
686 {
687 	// write the cached strings
688 	uint64 startOffset = fHeapWriter->UncompressedHeapSize();
689 	uint32 stringsCount = WriteCachedStrings(fPackageStringCache, 2);
690 	_stringsLengthUncompressed
691 		= fHeapWriter->UncompressedHeapSize() - startOffset;
692 
693 	_WritePackageAttributes(packageAttributes);
694 
695 	return stringsCount;
696 }
697 
698 
699 void
WriteAttributeValue(const AttributeValue & value,uint8 encoding)700 WriterImplBase::WriteAttributeValue(const AttributeValue& value, uint8 encoding)
701 {
702 	switch (value.type) {
703 		case B_HPKG_ATTRIBUTE_TYPE_INT:
704 		case B_HPKG_ATTRIBUTE_TYPE_UINT:
705 		{
706 			uint64 intValue = value.type == B_HPKG_ATTRIBUTE_TYPE_INT
707 				? (uint64)value.signedInt : value.unsignedInt;
708 
709 			switch (encoding) {
710 				case B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT:
711 					Write<uint8>((uint8)intValue);
712 					break;
713 				case B_HPKG_ATTRIBUTE_ENCODING_INT_16_BIT:
714 					Write<uint16>(
715 						B_HOST_TO_BENDIAN_INT16((uint16)intValue));
716 					break;
717 				case B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT:
718 					Write<uint32>(
719 						B_HOST_TO_BENDIAN_INT32((uint32)intValue));
720 					break;
721 				case B_HPKG_ATTRIBUTE_ENCODING_INT_64_BIT:
722 					Write<uint64>(
723 						B_HOST_TO_BENDIAN_INT64((uint64)intValue));
724 					break;
725 				default:
726 				{
727 					fErrorOutput->PrintError("WriteAttributeValue(): invalid "
728 						"encoding %d for int value type %d\n", encoding,
729 						value.type);
730 					throw status_t(B_BAD_VALUE);
731 				}
732 			}
733 
734 			break;
735 		}
736 
737 		case B_HPKG_ATTRIBUTE_TYPE_STRING:
738 		{
739 			if (encoding == B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE)
740 				WriteUnsignedLEB128(value.string->index);
741 			else
742 				WriteString(value.string->string);
743 			break;
744 		}
745 
746 		case B_HPKG_ATTRIBUTE_TYPE_RAW:
747 		{
748 			WriteUnsignedLEB128(value.data.size);
749 			if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP)
750 				WriteUnsignedLEB128(value.data.offset);
751 			else
752 				fHeapWriter->AddDataThrows(value.data.raw, value.data.size);
753 			break;
754 		}
755 
756 		default:
757 			fErrorOutput->PrintError(
758 				"WriteAttributeValue(): invalid value type: %d\n", value.type);
759 			throw status_t(B_BAD_VALUE);
760 	}
761 }
762 
763 
764 void
WriteUnsignedLEB128(uint64 value)765 WriterImplBase::WriteUnsignedLEB128(uint64 value)
766 {
767 	uint8 bytes[10];
768 	int32 count = 0;
769 	do {
770 		uint8 byte = value & 0x7f;
771 		value >>= 7;
772 		bytes[count++] = byte | (value != 0 ? 0x80 : 0);
773 	} while (value != 0);
774 
775 	fHeapWriter->AddDataThrows(bytes, count);
776 }
777 
778 
779 void
RawWriteBuffer(const void * buffer,size_t size,off_t offset)780 WriterImplBase::RawWriteBuffer(const void* buffer, size_t size, off_t offset)
781 {
782 	status_t error = fFile->WriteAtExactly(offset, buffer, size);
783 	if (error != B_OK) {
784 		fErrorOutput->PrintError(
785 			"RawWriteBuffer(%p, %lu) failed to write data: %s\n", buffer, size,
786 			strerror(error));
787 		throw error;
788 	}
789 }
790 
791 
792 void
_AddStringAttributeList(BHPKGAttributeID id,const BStringList & value,DoublyLinkedList<PackageAttribute> & list)793 WriterImplBase::_AddStringAttributeList(BHPKGAttributeID id,
794 	const BStringList& value, DoublyLinkedList<PackageAttribute>& list)
795 {
796 	for (int32 i = 0; i < value.CountStrings(); i++)
797 		AddStringAttribute(id, value.StringAt(i), list);
798 }
799 
800 
801 void
_WritePackageAttributes(const PackageAttributeList & packageAttributes)802 WriterImplBase::_WritePackageAttributes(
803 	const PackageAttributeList& packageAttributes)
804 {
805 	DoublyLinkedList<PackageAttribute>::ConstIterator it
806 		= packageAttributes.GetIterator();
807 	while (PackageAttribute* attribute = it.Next()) {
808 		uint8 encoding = attribute->ApplicableEncoding();
809 
810 		// write tag
811 		WriteUnsignedLEB128(compose_attribute_tag(
812 			attribute->id, attribute->type, encoding,
813 			!attribute->children.IsEmpty()));
814 
815 		// write value
816 		WriteAttributeValue(*attribute, encoding);
817 
818 		if (!attribute->children.IsEmpty())
819 			_WritePackageAttributes(attribute->children);
820 	}
821 
822 	WriteUnsignedLEB128(0);
823 }
824 
825 
826 }	// namespace BPrivate
827 
828 }	// namespace BHPKG
829 
830 }	// namespace BPackageKit
831