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