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