xref: /haiku/src/add-ons/kernel/file_systems/packagefs/kernel_interface.cpp (revision b46615c55ad2c8fe6de54412055a0713da3d610a)
1 /*
2  * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "kernel_interface.h"
8 
9 #include <dirent.h>
10 
11 #include <algorithm>
12 #include <new>
13 
14 #include <fs_info.h>
15 #include <fs_interface.h>
16 #include <KernelExport.h>
17 #include <io_requests.h>
18 
19 #include <AutoDeleter.h>
20 
21 #include "DebugSupport.h"
22 #include "Directory.h"
23 #include "GlobalFactory.h"
24 #include "LeafNode.h"
25 #include "Volume.h"
26 
27 
28 using BPackageKit::BHPKG::BBufferDataReader;
29 using BPackageKit::BHPKG::BFDDataReader;
30 
31 
32 static const uint32 kOptimalIOSize = 64 * 1024;
33 
34 
35 // #pragma mark - helper functions
36 
37 
38 static bool
39 is_user_in_group(gid_t gid)
40 {
41 	gid_t groups[NGROUPS_MAX];
42 	int groupCount = getgroups(NGROUPS_MAX, groups);
43 	for (int i = 0; i < groupCount; i++) {
44 		if (gid == groups[i])
45 			return true;
46 	}
47 
48 	return (gid == getegid());
49 }
50 
51 
52 static bool
53 set_dirent_name(struct dirent* buffer, size_t bufferSize, const char* name,
54 	size_t nameLen)
55 {
56 	size_t length = (buffer->d_name + nameLen + 1) - (char*)buffer;
57 	if (length > bufferSize)
58 		return false;
59 
60 	memcpy(buffer->d_name, name, nameLen);
61 	buffer->d_name[nameLen] = '\0';
62 	buffer->d_reclen = length;
63 	return true;
64 }
65 
66 
67 static status_t
68 check_access(Node* node, int mode)
69 {
70 	// write access requested?
71 	if (mode & W_OK)
72 		return B_READ_ONLY_DEVICE;
73 
74 	// get node permissions
75 	int userPermissions = (node->Mode() & S_IRWXU) >> 6;
76 	int groupPermissions = (node->Mode() & S_IRWXG) >> 3;
77 	int otherPermissions = node->Mode() & S_IRWXO;
78 
79 	// get the permissions for this uid/gid
80 	int permissions = 0;
81 	uid_t uid = geteuid();
82 
83 	if (uid == 0) {
84 		// user is root
85 		// root has always read/write permission, but at least one of the
86 		// X bits must be set for execute permission
87 		permissions = userPermissions | groupPermissions | otherPermissions
88 			| S_IROTH | S_IWOTH;
89 	} else if (uid == node->UserID()) {
90 		// user is node owner
91 		permissions = userPermissions;
92 	} else if (is_user_in_group(node->GroupID())) {
93 		// user is in owning group
94 		permissions = groupPermissions;
95 	} else {
96 		// user is one of the others
97 		permissions = otherPermissions;
98 	}
99 
100 	return (mode & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED;
101 }
102 
103 
104 //	#pragma mark - Volume
105 
106 
107 static status_t
108 packagefs_mount(fs_volume* fsVolume, const char* device, uint32 flags,
109 	const char* parameters, ino_t* _rootID)
110 {
111 	FUNCTION("fsVolume: %p, device: \"%s\", flags: %#lx, parameters: \"%s\"\n",
112 		fsVolume, device, flags, parameters);
113 
114 	// create a Volume object
115 	Volume* volume = new(std::nothrow) Volume(fsVolume);
116 	if (volume == NULL)
117 		RETURN_ERROR(B_NO_MEMORY);
118 	ObjectDeleter<Volume> volumeDeleter(volume);
119 
120 	status_t error = volume->Mount(parameters);
121 	if (error != B_OK)
122 		return error;
123 
124 	// set return values
125 	*_rootID = volume->RootDirectory()->ID();
126 	fsVolume->private_volume = volumeDeleter.Detach();
127 	fsVolume->ops = &gPackageFSVolumeOps;
128 
129 	return B_OK;
130 }
131 
132 
133 static status_t
134 packagefs_unmount(fs_volume* fsVolume)
135 {
136 	Volume* volume = (Volume*)fsVolume->private_volume;
137 
138 	FUNCTION("volume: %p\n", volume);
139 
140 	volume->Unmount();
141 	delete volume;
142 
143 	return B_OK;
144 }
145 
146 
147 static status_t
148 packagefs_read_fs_info(fs_volume* fsVolume, struct fs_info* info)
149 {
150 	Volume* volume = (Volume*)fsVolume->private_volume;
151 
152 	FUNCTION("volume: %p, info: %p\n", volume, info);
153 
154 	info->flags = B_FS_IS_READONLY;
155 	info->block_size = 4096;
156 	info->io_size = kOptimalIOSize;
157 	info->total_blocks = info->free_blocks = 1;
158 	strlcpy(info->volume_name, volume->RootDirectory()->Name(),
159 		sizeof(info->volume_name));
160 	return B_OK;
161 }
162 
163 
164 // #pragma mark - VNodes
165 
166 
167 static status_t
168 packagefs_lookup(fs_volume* fsVolume, fs_vnode* fsDir, const char* entryName,
169 	ino_t* _vnid)
170 {
171 	Volume* volume = (Volume*)fsVolume->private_volume;
172 	Node* dir = (Node*)fsDir->private_node;
173 
174 	FUNCTION("volume: %p, dir: %p (%lld), entry: \"%s\"\n", volume, dir,
175 		dir->ID(), entryName);
176 
177 	if (!S_ISDIR(dir->Mode()))
178 		return B_NOT_A_DIRECTORY;
179 
180 	// resolve "."
181 	if (strcmp(entryName, ".") == 0) {
182 		Node* node;
183 		*_vnid = dir->ID();
184 		return volume->GetVNode(*_vnid, node);
185 	}
186 
187 	// resolve ".."
188 	if (strcmp(entryName, "..") == 0) {
189 		Node* node;
190 		*_vnid = dir->Parent()->ID();
191 		return volume->GetVNode(*_vnid, node);
192 	}
193 
194 	// resolve normal entries -- look up the node
195 	NodeReadLocker dirLocker(dir);
196 	Node* node = dynamic_cast<Directory*>(dir)->FindChild(entryName);
197 	if (node == NULL)
198 		return B_ENTRY_NOT_FOUND;
199 	BReference<Node> nodeReference(node);
200 	dirLocker.Unlock();
201 
202 	// get the vnode reference
203 	*_vnid = node->ID();
204 	RETURN_ERROR(volume->GetVNode(*_vnid, node));
205 }
206 
207 
208 static status_t
209 packagefs_get_vnode(fs_volume* fsVolume, ino_t vnid, fs_vnode* fsNode,
210 	int* _type, uint32* _flags, bool reenter)
211 {
212 	Volume* volume = (Volume*)fsVolume->private_volume;
213 
214 	FUNCTION("volume: %p, vnid: %lld\n", volume, vnid);
215 
216 	VolumeReadLocker volumeLocker(volume);
217 	Node* node = volume->FindNode(vnid);
218 	if (node == NULL)
219 		return B_ENTRY_NOT_FOUND;
220 	BReference<Node> nodeReference(node);
221 	volumeLocker.Unlock();
222 
223 	NodeWriteLocker nodeLocker(node);
224 	status_t error = node->VFSInit(volume->ID());
225 	if (error != B_OK)
226 		RETURN_ERROR(error);
227 	nodeLocker.Unlock();
228 
229 	fsNode->private_node = nodeReference.Detach();
230 	fsNode->ops = &gPackageFSVnodeOps;
231 	*_type = node->Mode() & S_IFMT;
232 	*_flags = 0;
233 
234 	return B_OK;
235 }
236 
237 
238 static status_t
239 packagefs_put_vnode(fs_volume* fsVolume, fs_vnode* fsNode, bool reenter)
240 {
241 	Volume* volume = (Volume*)fsVolume->private_volume;
242 	Node* node = (Node*)fsNode->private_node;
243 
244 	FUNCTION("volume: %p, node: %p\n", volume, node);
245 	TOUCH(volume);
246 
247 	NodeWriteLocker nodeLocker(node);
248 	node->VFSUninit();
249 	nodeLocker.Unlock();
250 
251 	node->ReleaseReference();
252 
253 	return B_OK;
254 }
255 
256 
257 // #pragma mark - Request I/O
258 
259 
260 static status_t
261 packagefs_io(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
262 	io_request* request)
263 {
264 	Volume* volume = (Volume*)fsVolume->private_volume;
265 	Node* node = (Node*)fsNode->private_node;
266 
267 	FUNCTION("volume: %p, node: %p (%lld), cookie: %p, request: %p\n", volume,
268 		node, node->ID(), cookie, request);
269 	TOUCH(volume);
270 
271 	if (io_request_is_write(request))
272 		RETURN_ERROR(B_READ_ONLY_DEVICE);
273 
274 	status_t error = node->Read(request);
275 	notify_io_request(request, error);
276 	return error;
277 }
278 
279 
280 // #pragma mark - Nodes
281 
282 
283 static status_t
284 packagefs_read_symlink(fs_volume* fsVolume, fs_vnode* fsNode, char* buffer,
285 	size_t* _bufferSize)
286 {
287 	Volume* volume = (Volume*)fsVolume->private_volume;
288 	Node* node = (Node*)fsNode->private_node;
289 
290 	FUNCTION("volume: %p, node: %p (%lld)\n", volume, node, node->ID());
291 	TOUCH(volume);
292 
293 	NodeReadLocker nodeLocker(node);
294 
295 	if (!S_ISLNK(node->Mode()))
296 		return B_BAD_VALUE;
297 
298 	const char* linkPath = dynamic_cast<LeafNode*>(node)->SymlinkPath();
299 	if (linkPath == NULL) {
300 		*_bufferSize = 0;
301 		return B_OK;
302 	}
303 
304 	size_t toCopy = std::min(strlen(linkPath), *_bufferSize);
305 	memcpy(buffer, linkPath, toCopy);
306 	*_bufferSize = toCopy;
307 
308 	return B_OK;
309 }
310 
311 
312 static status_t
313 packagefs_access(fs_volume* fsVolume, fs_vnode* fsNode, int mode)
314 {
315 	Volume* volume = (Volume*)fsVolume->private_volume;
316 	Node* node = (Node*)fsNode->private_node;
317 
318 	FUNCTION("volume: %p, node: %p (%lld)\n", volume, node, node->ID());
319 	TOUCH(volume);
320 
321 	NodeReadLocker nodeLocker(node);
322 	return check_access(node, mode);
323 }
324 
325 
326 static status_t
327 packagefs_read_stat(fs_volume* fsVolume, fs_vnode* fsNode, struct stat* st)
328 {
329 	Volume* volume = (Volume*)fsVolume->private_volume;
330 	Node* node = (Node*)fsNode->private_node;
331 
332 	FUNCTION("volume: %p, node: %p (%lld)\n", volume, node, node->ID());
333 	TOUCH(volume);
334 
335 	NodeReadLocker nodeLocker(node);
336 
337 	st->st_mode = node->Mode();
338 	st->st_nlink = 1;
339 	st->st_uid = node->UserID();
340 	st->st_gid = node->GroupID();
341 	st->st_size = node->FileSize();
342 	st->st_blksize = kOptimalIOSize;
343 	st->st_mtim = node->ModifiedTime();
344 	st->st_atim = st->st_mtim;
345 	st->st_ctim = st->st_mtim;
346 		// TODO: Perhaps manage a changed time (particularly for directories)?
347 	st->st_crtim = st->st_mtim;
348 	st->st_blocks = (st->st_size + 511) / 512;
349 
350 	return B_OK;
351 }
352 
353 
354 // #pragma mark - Files
355 
356 
357 struct FileCookie {
358 	int	openMode;
359 
360 	FileCookie(int openMode)
361 		:
362 		openMode(openMode)
363 	{
364 	}
365 };
366 
367 
368 static status_t
369 packagefs_open(fs_volume* fsVolume, fs_vnode* fsNode, int openMode,
370 	void** _cookie)
371 {
372 	Volume* volume = (Volume*)fsVolume->private_volume;
373 	Node* node = (Node*)fsNode->private_node;
374 
375 	FUNCTION("volume: %p, node: %p (%lld), openMode %#x\n", volume, node,
376 		node->ID(), openMode);
377 	TOUCH(volume);
378 
379 	NodeReadLocker nodeLocker(node);
380 
381 	// check the open mode and permissions
382 	if (S_ISDIR(node->Mode()) && (openMode & O_RWMASK) != O_RDONLY)
383 		return B_IS_A_DIRECTORY;
384 
385 	if ((openMode & O_RWMASK) != O_RDONLY)
386 		return B_NOT_ALLOWED;
387 
388 	status_t error = check_access(node, R_OK);
389 	if (error != B_OK)
390 		return error;
391 
392 	// allocate the cookie
393 	FileCookie* cookie = new(std::nothrow) FileCookie(openMode);
394 	if (cookie == NULL)
395 		RETURN_ERROR(B_NO_MEMORY);
396 
397 	*_cookie = cookie;
398 
399 	return B_OK;
400 }
401 
402 
403 static status_t
404 packagefs_close(fs_volume* fs, fs_vnode* _node, void* cookie)
405 {
406 	return B_OK;
407 }
408 
409 
410 static status_t
411 packagefs_free_cookie(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie)
412 {
413 	Volume* volume = (Volume*)fsVolume->private_volume;
414 	Node* node = (Node*)fsNode->private_node;
415 	FileCookie* cookie = (FileCookie*)_cookie;
416 
417 	FUNCTION("volume: %p, node: %p (%lld), cookie: %p\n", volume, node,
418 		node->ID(), cookie);
419 	TOUCH(volume);
420 	TOUCH(node);
421 
422 	delete cookie;
423 
424 	return B_OK;
425 }
426 
427 
428 static status_t
429 packagefs_read(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie,
430 	off_t offset, void* buffer, size_t* bufferSize)
431 {
432 	Volume* volume = (Volume*)fsVolume->private_volume;
433 	Node* node = (Node*)fsNode->private_node;
434 	FileCookie* cookie = (FileCookie*)_cookie;
435 
436 	FUNCTION("volume: %p, node: %p (%lld), cookie: %p, offset: %lld, "
437 		"buffer: %p, size: %lu\n", volume, node, node->ID(), cookie, offset,
438 		buffer, *bufferSize);
439 	TOUCH(volume);
440 
441 	if ((cookie->openMode & O_RWMASK) != O_RDONLY)
442 		return EBADF;
443 
444 	return node->Read(offset, buffer, bufferSize);
445 }
446 
447 
448 // #pragma mark - Directories
449 
450 
451 struct DirectoryCookie : DirectoryIterator {
452 	Directory*	directory;
453 	int32		state;
454 	bool		registered;
455 
456 	DirectoryCookie(Directory* directory)
457 		:
458 		directory(directory),
459 		state(0),
460 		registered(false)
461 	{
462 		Rewind();
463 	}
464 
465 	~DirectoryCookie()
466 	{
467 		if (registered)
468 			directory->RemoveDirectoryIterator(this);
469 	}
470 
471 	void Rewind()
472 	{
473 		if (registered)
474 			directory->RemoveDirectoryIterator(this);
475 		registered = false;
476 
477 		state = 0;
478 		node = directory;
479 	}
480 
481 	Node* Current(const char*& _name) const
482 	{
483 		if (node == NULL)
484 			return NULL;
485 
486 		if (state == 0)
487 			_name = ".";
488 		else if (state == 1)
489 			_name = "..";
490 		else
491 			_name = node->Name();
492 
493 		return node;
494 	}
495 
496 	Node* Next()
497 	{
498 		if (state == 0) {
499 			state = 1;
500 			node = directory->Parent();
501 			if (node == NULL)
502 				node = directory;
503 			return node;
504 		}
505 
506 		if (state == 1) {
507 			node = directory->FirstChild();
508 			state = 2;
509 		} else {
510 			if (node != NULL)
511 				node = directory->NextChild(node);
512 		}
513 
514 		if (node == NULL) {
515 			if (registered) {
516 				directory->RemoveDirectoryIterator(this);
517 				registered = false;
518 			}
519 
520 			return NULL;
521 		}
522 
523 		if (!registered) {
524 			directory->AddDirectoryIterator(this);
525 			registered = true;
526 		}
527 
528 		return node;
529 	}
530 };
531 
532 
533 static status_t
534 packagefs_open_dir(fs_volume* fsVolume, fs_vnode* fsNode, void** _cookie)
535 {
536 	Volume* volume = (Volume*)fsVolume->private_volume;
537 	Node* node = (Node*)fsNode->private_node;
538 
539 	FUNCTION("volume: %p, node: %p (%lld)\n", volume, node, node->ID());
540 	TOUCH(volume);
541 
542 	if (!S_ISDIR(node->Mode()))
543 		return B_NOT_A_DIRECTORY;
544 
545 	Directory* dir = dynamic_cast<Directory*>(node);
546 
547 	status_t error = check_access(dir, R_OK);
548 	if (error != B_OK)
549 		return error;
550 
551 	// create a cookie
552 	NodeWriteLocker dirLocker(dir);
553 	DirectoryCookie* cookie = new(std::nothrow) DirectoryCookie(dir);
554 	if (cookie == NULL)
555 		RETURN_ERROR(B_NO_MEMORY);
556 
557 	*_cookie = cookie;
558 	return B_OK;
559 }
560 
561 
562 static status_t
563 packagefs_close_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
564 {
565 	return B_OK;
566 }
567 
568 
569 static status_t
570 packagefs_free_dir_cookie(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie)
571 {
572 	Volume* volume = (Volume*)fsVolume->private_volume;
573 	Node* node = (Node*)fsNode->private_node;
574 	DirectoryCookie* cookie = (DirectoryCookie*)_cookie;
575 
576 	FUNCTION("volume: %p, node: %p (%lld), cookie: %p\n", volume, node,
577 		node->ID(), cookie);
578 	TOUCH(volume);
579 	TOUCH(node);
580 
581 	NodeWriteLocker dirLocker(node);
582 	delete cookie;
583 
584 	return B_OK;
585 }
586 
587 
588 static status_t
589 packagefs_read_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie,
590 	struct dirent* buffer, size_t bufferSize, uint32* _count)
591 {
592 	Volume* volume = (Volume*)fsVolume->private_volume;
593 	Node* node = (Node*)fsNode->private_node;
594 	DirectoryCookie* cookie = (DirectoryCookie*)_cookie;
595 
596 	FUNCTION("volume: %p, node: %p (%lld), cookie: %p\n", volume, node,
597 		node->ID(), cookie);
598 	TOUCH(volume);
599 	TOUCH(node);
600 
601 	NodeWriteLocker dirLocker(cookie->directory);
602 
603 	uint32 maxCount = *_count;
604 	uint32 count = 0;
605 
606 	dirent* previousEntry = NULL;
607 
608 	const char* name;
609 	while (Node* child = cookie->Current(name)) {
610 		// don't read more entries than requested
611 		if (count >= maxCount)
612 			break;
613 
614 		// align the buffer for subsequent entries
615 		if (count > 0) {
616 			addr_t offset = (addr_t)buffer % 8;
617 			if (offset > 0) {
618 				offset = 8 - offset;
619 				if (bufferSize <= offset)
620 					break;
621 
622 				previousEntry->d_reclen += offset;
623 				buffer = (dirent*)((addr_t)buffer + offset);
624 				bufferSize -= offset;
625 			}
626 		}
627 
628 		// fill in the entry name -- checks whether the entry fits into the
629 		// buffer
630 		if (!set_dirent_name(buffer, bufferSize, name, strlen(name))) {
631 			if (count == 0)
632 				RETURN_ERROR(B_BUFFER_OVERFLOW);
633 			break;
634 		}
635 
636 		// fill in the other data
637 		buffer->d_dev = volume->ID();
638 		buffer->d_ino = child->ID();
639 
640 		count++;
641 		previousEntry = buffer;
642 		bufferSize -= buffer->d_reclen;
643 		buffer = (dirent*)((addr_t)buffer + buffer->d_reclen);
644 
645 		cookie->Next();
646 	}
647 
648 	*_count = count;
649 	return B_OK;
650 }
651 
652 
653 static status_t
654 packagefs_rewind_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie)
655 {
656 	Volume* volume = (Volume*)fsVolume->private_volume;
657 	Node* node = (Node*)fsNode->private_node;
658 	DirectoryCookie* cookie = (DirectoryCookie*)_cookie;
659 
660 	FUNCTION("volume: %p, node: %p (%lld), cookie: %p\n", volume, node,
661 		node->ID(), cookie);
662 	TOUCH(volume);
663 	TOUCH(node);
664 
665 	NodeWriteLocker dirLocker(node);
666 	cookie->Rewind();
667 
668 	return B_OK;
669 }
670 
671 
672 // #pragma mark - Attribute Directories
673 
674 
675 struct AttributeDirectoryCookie {
676 	Node*					node;
677 	PackageNode*			packageNode;
678 	PackageNodeAttribute*	attribute;
679 
680 	AttributeDirectoryCookie(Node* node)
681 		:
682 		node(node),
683 		packageNode(node->GetPackageNode()),
684 		attribute(NULL)
685 	{
686 		if (packageNode != NULL) {
687 			packageNode->AcquireReference();
688 			attribute = packageNode->Attributes().Head();
689 		}
690 	}
691 
692 	~AttributeDirectoryCookie()
693 	{
694 		if (packageNode != NULL)
695 			packageNode->ReleaseReference();
696 	}
697 
698 	PackageNodeAttribute* Current() const
699 	{
700 		return attribute;
701 	}
702 
703 	void Next()
704 	{
705 		if (attribute == NULL)
706 			return;
707 
708 		attribute = packageNode->Attributes().GetNext(attribute);
709 	}
710 
711 	void Rewind()
712 	{
713 		if (packageNode != NULL)
714 			attribute = packageNode->Attributes().Head();
715 	}
716 };
717 
718 
719 status_t
720 packagefs_open_attr_dir(fs_volume* fsVolume, fs_vnode* fsNode, void** _cookie)
721 {
722 	Volume* volume = (Volume*)fsVolume->private_volume;
723 	Node* node = (Node*)fsNode->private_node;
724 
725 	FUNCTION("volume: %p, node: %p (%lld)\n", volume, node, node->ID());
726 	TOUCH(volume);
727 
728 	status_t error = check_access(node, R_OK);
729 	if (error != B_OK)
730 		return error;
731 
732 	// create a cookie
733 	NodeReadLocker nodeLocker(node);
734 	AttributeDirectoryCookie* cookie
735 		= new(std::nothrow) AttributeDirectoryCookie(node);
736 	if (cookie == NULL)
737 		RETURN_ERROR(B_NO_MEMORY);
738 
739 	*_cookie = cookie;
740 	return B_OK;
741 }
742 
743 
744 status_t
745 packagefs_close_attr_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie)
746 {
747 	return B_OK;
748 }
749 
750 
751 status_t
752 packagefs_free_attr_dir_cookie(fs_volume* fsVolume, fs_vnode* fsNode,
753 	void* _cookie)
754 {
755 	Volume* volume = (Volume*)fsVolume->private_volume;
756 	Node* node = (Node*)fsNode->private_node;
757 	AttributeDirectoryCookie* cookie = (AttributeDirectoryCookie*)_cookie;
758 
759 	FUNCTION("volume: %p, node: %p (%lld), cookie: %p\n", volume, node,
760 		node->ID(), cookie);
761 	TOUCH(volume);
762 	TOUCH(node);
763 
764 	delete cookie;
765 
766 	return B_OK;
767 }
768 
769 
770 status_t
771 packagefs_read_attr_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie,
772 	struct dirent* buffer, size_t bufferSize, uint32* _count)
773 {
774 	Volume* volume = (Volume*)fsVolume->private_volume;
775 	Node* node = (Node*)fsNode->private_node;
776 	AttributeDirectoryCookie* cookie = (AttributeDirectoryCookie*)_cookie;
777 
778 	FUNCTION("volume: %p, node: %p (%lld), cookie: %p\n", volume, node,
779 		node->ID(), cookie);
780 	TOUCH(volume);
781 	TOUCH(node);
782 
783 	uint32 maxCount = *_count;
784 	uint32 count = 0;
785 
786 	dirent* previousEntry = NULL;
787 
788 	while (PackageNodeAttribute* attribute = cookie->Current()) {
789 		// don't read more entries than requested
790 		if (count >= maxCount)
791 			break;
792 
793 		// align the buffer for subsequent entries
794 		if (count > 0) {
795 			addr_t offset = (addr_t)buffer % 8;
796 			if (offset > 0) {
797 				offset = 8 - offset;
798 				if (bufferSize <= offset)
799 					break;
800 
801 				previousEntry->d_reclen += offset;
802 				buffer = (dirent*)((addr_t)buffer + offset);
803 				bufferSize -= offset;
804 			}
805 		}
806 
807 		// fill in the entry name -- checks whether the entry fits into the
808 		// buffer
809 		const char* name = attribute->Name();
810 		if (!set_dirent_name(buffer, bufferSize, name, strlen(name))) {
811 			if (count == 0)
812 				RETURN_ERROR(B_BUFFER_OVERFLOW);
813 			break;
814 		}
815 
816 		// fill in the other data
817 		buffer->d_dev = volume->ID();
818 		buffer->d_ino = node->ID();
819 
820 		count++;
821 		previousEntry = buffer;
822 		bufferSize -= buffer->d_reclen;
823 		buffer = (dirent*)((addr_t)buffer + buffer->d_reclen);
824 
825 		cookie->Next();
826 	}
827 
828 	*_count = count;
829 	return B_OK;
830 }
831 
832 
833 status_t
834 packagefs_rewind_attr_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie)
835 {
836 	Volume* volume = (Volume*)fsVolume->private_volume;
837 	Node* node = (Node*)fsNode->private_node;
838 	AttributeDirectoryCookie* cookie = (AttributeDirectoryCookie*)_cookie;
839 
840 	FUNCTION("volume: %p, node: %p (%lld), cookie: %p\n", volume, node,
841 		node->ID(), cookie);
842 	TOUCH(volume);
843 	TOUCH(node);
844 
845 	cookie->Rewind();
846 
847 	return B_OK;
848 }
849 
850 
851 // #pragma mark - Attribute Operations
852 
853 
854 struct AttributeCookie {
855 	PackageNode*			packageNode;
856 	Package*				package;
857 	PackageNodeAttribute*	attribute;
858 	int						openMode;
859 
860 	AttributeCookie(PackageNode* packageNode, PackageNodeAttribute* attribute,
861 		int openMode)
862 		:
863 		packageNode(packageNode),
864 		package(packageNode->GetPackage()),
865 		attribute(attribute),
866 		openMode(openMode)
867 	{
868 		packageNode->AcquireReference();
869 		package->AcquireReference();
870 	}
871 
872 	~AttributeCookie()
873 	{
874 		packageNode->ReleaseReference();
875 		package->ReleaseReference();
876 	}
877 };
878 
879 
880 status_t
881 packagefs_open_attr(fs_volume* fsVolume, fs_vnode* fsNode, const char* name,
882 	int openMode, void** _cookie)
883 {
884 	Volume* volume = (Volume*)fsVolume->private_volume;
885 	Node* node = (Node*)fsNode->private_node;
886 
887 	FUNCTION("volume: %p, node: %p (%lld), name: \"%s\", openMode %#x\n",
888 		volume, node, node->ID(), name, openMode);
889 	TOUCH(volume);
890 
891 	NodeReadLocker nodeLocker(node);
892 
893 	// check the open mode and permissions
894 	if ((openMode & O_RWMASK) != O_RDONLY)
895 		return B_NOT_ALLOWED;
896 
897 	status_t error = check_access(node, R_OK);
898 	if (error != B_OK)
899 		return error;
900 
901 	// get the package node and the respectively named attribute
902 	PackageNode* packageNode = node->GetPackageNode();
903 	PackageNodeAttribute* attribute = packageNode != NULL
904 		? packageNode->FindAttribute(name) : NULL;
905 	if (attribute == NULL)
906 		return B_ENTRY_NOT_FOUND;
907 
908 	// allocate the cookie
909 	AttributeCookie* cookie = new(std::nothrow) AttributeCookie(packageNode,
910 		attribute, openMode);
911 	if (cookie == NULL)
912 		RETURN_ERROR(B_NO_MEMORY);
913 
914 	*_cookie = cookie;
915 
916 	return B_OK;
917 }
918 
919 
920 status_t
921 packagefs_close_attr(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie)
922 {
923 	return B_OK;
924 }
925 
926 
927 status_t
928 packagefs_free_attr_cookie(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie)
929 {
930 	Volume* volume = (Volume*)fsVolume->private_volume;
931 	Node* node = (Node*)fsNode->private_node;
932 	AttributeCookie* cookie = (AttributeCookie*)_cookie;
933 
934 	FUNCTION("volume: %p, node: %p (%lld), cookie: %p\n", volume, node,
935 		node->ID(), cookie);
936 	TOUCH(volume);
937 	TOUCH(node);
938 
939 	delete cookie;
940 
941 	return B_OK;
942 }
943 
944 
945 static status_t
946 read_package_data(const BPackageData& data, BDataReader* dataReader, off_t offset,
947 	void* buffer, size_t* bufferSize)
948 {
949 	// create a BPackageDataReader
950 	BPackageDataReader* reader;
951 	status_t error = GlobalFactory::Default()->CreatePackageDataReader(
952 		dataReader, data, reader);
953 	if (error != B_OK)
954 		RETURN_ERROR(error);
955 	ObjectDeleter<BPackageDataReader> readerDeleter(reader);
956 
957 	// check the offset
958 	if (offset < 0 || (uint64)offset > data.UncompressedSize())
959 		return B_BAD_VALUE;
960 
961 	// clamp the size
962 	size_t toRead = std::min((uint64)*bufferSize,
963 		data.UncompressedSize() - offset);
964 
965 	// read
966 	if (toRead > 0) {
967 		status_t error = reader->ReadData(offset, buffer, toRead);
968 		if (error != B_OK)
969 			RETURN_ERROR(error);
970 	}
971 
972 	*bufferSize = toRead;
973 	return B_OK;
974 }
975 
976 
977 status_t
978 packagefs_read_attr(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie,
979 	off_t offset, void* buffer, size_t* bufferSize)
980 {
981 	Volume* volume = (Volume*)fsVolume->private_volume;
982 	Node* node = (Node*)fsNode->private_node;
983 	AttributeCookie* cookie = (AttributeCookie*)_cookie;
984 
985 	FUNCTION("volume: %p, node: %p (%lld), cookie: %p\n", volume, node,
986 		node->ID(), cookie);
987 	TOUCH(volume);
988 	TOUCH(node);
989 
990 	const BPackageData& data = cookie->attribute->Data();
991 	if (data.IsEncodedInline()) {
992 		// inline data
993 		BBufferDataReader dataReader(data.InlineData(), data.CompressedSize());
994 		return read_package_data(data, &dataReader, offset, buffer, bufferSize);
995 	}
996 
997 	// data not inline -- open the package
998 	int fd = cookie->package->Open();
999 	if (fd < 0)
1000 		RETURN_ERROR(fd);
1001 	PackageCloser packageCloser(cookie->package);
1002 
1003 	BFDDataReader dataReader(fd);
1004 	return read_package_data(data, &dataReader, offset, buffer, bufferSize);
1005 }
1006 
1007 
1008 status_t
1009 packagefs_read_attr_stat(fs_volume* fsVolume, fs_vnode* fsNode,
1010 	void* _cookie, struct stat* st)
1011 {
1012 	Volume* volume = (Volume*)fsVolume->private_volume;
1013 	Node* node = (Node*)fsNode->private_node;
1014 	AttributeCookie* cookie = (AttributeCookie*)_cookie;
1015 
1016 	FUNCTION("volume: %p, node: %p (%lld), cookie: %p\n", volume, node,
1017 		node->ID(), cookie);
1018 	TOUCH(volume);
1019 	TOUCH(node);
1020 
1021 	st->st_size = cookie->attribute->Data().UncompressedSize();
1022 	st->st_type = cookie->attribute->Type();
1023 
1024 	return B_OK;
1025 }
1026 
1027 
1028 // #pragma mark - Module Interface
1029 
1030 
1031 static status_t
1032 packagefs_std_ops(int32 op, ...)
1033 {
1034 	switch (op) {
1035 		case B_MODULE_INIT:
1036 		{
1037 			init_debugging();
1038 			PRINT("package_std_ops(): B_MODULE_INIT\n");
1039 
1040 			status_t error = GlobalFactory::CreateDefault();
1041 			if (error != B_OK) {
1042 				ERROR("Failed to init GlobalFactory\n");
1043 				exit_debugging();
1044 				return error;
1045 			}
1046 
1047 			return B_OK;
1048 		}
1049 
1050 		case B_MODULE_UNINIT:
1051 		{
1052 			PRINT("package_std_ops(): B_MODULE_UNINIT\n");
1053 			GlobalFactory::DeleteDefault();
1054 			exit_debugging();
1055 			return B_OK;
1056 		}
1057 
1058 		default:
1059 			return B_ERROR;
1060 	}
1061 }
1062 
1063 
1064 static file_system_module_info sPackageFSModuleInfo = {
1065 	{
1066 		"file_systems/packagefs" B_CURRENT_FS_API_VERSION,
1067 		0,
1068 		packagefs_std_ops,
1069 	},
1070 
1071 	"packagefs",				// short_name
1072 	"Package File System",		// pretty_name
1073 	0,							// DDM flags
1074 
1075 
1076 	// scanning
1077 	NULL,	// identify_partition,
1078 	NULL,	// scan_partition,
1079 	NULL,	// free_identify_partition_cookie,
1080 	NULL,	// free_partition_content_cookie()
1081 
1082 	&packagefs_mount
1083 };
1084 
1085 
1086 fs_volume_ops gPackageFSVolumeOps = {
1087 	&packagefs_unmount,
1088 	&packagefs_read_fs_info,
1089 	NULL,	// write_fs_info,
1090 	NULL,	// sync,
1091 
1092 	&packagefs_get_vnode
1093 
1094 	// TODO: index operations
1095 	// TODO: query operations
1096 	// TODO: FS layer operations
1097 };
1098 
1099 
1100 fs_vnode_ops gPackageFSVnodeOps = {
1101 	// vnode operations
1102 	&packagefs_lookup,
1103 	NULL,	// get_vnode_name,
1104 	&packagefs_put_vnode,
1105 	&packagefs_put_vnode,	// remove_vnode -- same as put_vnode
1106 
1107 	// VM file access
1108 	NULL,	// can_page,
1109 	NULL,	// read_pages,
1110 	NULL,	// write_pages,
1111 
1112 	&packagefs_io,
1113 	NULL,	// cancel_io()
1114 
1115 	NULL,	// get_file_map,
1116 
1117 	NULL,	// ioctl,
1118 	NULL,	// set_flags,
1119 	NULL,	// select,
1120 	NULL,	// deselect,
1121 	NULL,	// fsync,
1122 
1123 	&packagefs_read_symlink,
1124 	NULL,	// create_symlink,
1125 
1126 	NULL,	// link,
1127 	NULL,	// unlink,
1128 	NULL,	// rename,
1129 
1130 	&packagefs_access,
1131 	&packagefs_read_stat,
1132 	NULL,	// write_stat,
1133 	NULL,	// preallocate,
1134 
1135 	// file operations
1136 	NULL,	// create,
1137 	&packagefs_open,
1138 	&packagefs_close,
1139 	&packagefs_free_cookie,
1140 	&packagefs_read,
1141 	NULL,	// write,
1142 
1143 	// directory operations
1144 	NULL,	// create_dir,
1145 	NULL,	// remove_dir,
1146 	&packagefs_open_dir,
1147 	&packagefs_close_dir,
1148 	&packagefs_free_dir_cookie,
1149 	&packagefs_read_dir,
1150 	&packagefs_rewind_dir,
1151 
1152 	// attribute directory operations
1153 	&packagefs_open_attr_dir,
1154 	&packagefs_close_attr_dir,
1155 	&packagefs_free_attr_dir_cookie,
1156 	&packagefs_read_attr_dir,
1157 	&packagefs_rewind_attr_dir,
1158 
1159 	// attribute operations
1160 	NULL,	// create_attr,
1161 	&packagefs_open_attr,
1162 	&packagefs_close_attr,
1163 	&packagefs_free_attr_cookie,
1164 	&packagefs_read_attr,
1165 	NULL,	// write_attr,
1166 
1167 	&packagefs_read_attr_stat,
1168 	NULL,	// write_attr_stat,
1169 	NULL,	// rename_attr,
1170 	NULL	// remove_attr,
1171 
1172 	// TODO: FS layer operations
1173 };
1174 
1175 
1176 module_info *modules[] = {
1177 	(module_info *)&sPackageFSModuleInfo,
1178 	NULL,
1179 };
1180