xref: /haiku/src/add-ons/kernel/file_systems/packagefs/kernel_interface.cpp (revision fc7456e9b1ec38c941134ed6d01c438cf289381e)
1 /*
2  * Copyright 2009-2011, 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 <new>
12 
13 #include <fs_info.h>
14 #include <fs_interface.h>
15 #include <KernelExport.h>
16 #include <io_requests.h>
17 #include <slab/Slab.h>
18 
19 #include <AutoDeleter.h>
20 
21 #include <package/hpkg/PackageFileHeapAccessorBase.h>
22 #include "util/TwoKeyAVLTree.h"
23 
24 #include "AttributeCookie.h"
25 #include "AttributeDirectoryCookie.h"
26 #include "DebugSupport.h"
27 #include "Directory.h"
28 #include "Query.h"
29 #include "PackageFSRoot.h"
30 #include "StringConstants.h"
31 #include "StringPool.h"
32 #include "Utils.h"
33 #include "Volume.h"
34 
35 
36 template<> object_cache* TwoKeyAVLTreeNode<void*>::sNodeCache = NULL;
37 
38 static const uint32 kOptimalIOSize = 64 * 1024;
39 
40 
41 // #pragma mark - helper functions
42 
43 
44 static bool
45 lock_directory_for_node(Volume* volume, Node* node, DirectoryReadLocker& locker)
46 {
47 	if (Directory* directory = dynamic_cast<Directory*>(node)) {
48 		locker.SetTo(directory, false, true);
49 		return locker.IsLocked();
50 	} else {
51 		BReference<Directory> parentRef = node->GetParent();
52 		if (!parentRef.IsSet())
53 			return false;
54 		locker.SetTo(parentRef.Get(), false, true);
55 		return locker.IsLocked() && node->GetParentUnchecked() == locker.Get();
56 	}
57 }
58 
59 
60 static status_t
61 check_access(Node* node, int mode)
62 {
63 	// write access requested?
64 	if (mode & W_OK)
65 		return B_READ_ONLY_DEVICE;
66 
67 	return check_access_permissions(mode, node->Mode(), node->GroupID(),
68 		node->UserID());
69 }
70 
71 
72 //	#pragma mark - Volume
73 
74 
75 static status_t
76 packagefs_mount(fs_volume* fsVolume, const char* device, uint32 flags,
77 	const char* parameters, ino_t* _rootID)
78 {
79 	FUNCTION("fsVolume: %p, device: \"%s\", flags: %#" B_PRIx32 ", parameters: "
80 			"\"%s\"\n", fsVolume, device, flags, parameters);
81 
82 	// create a Volume object
83 	Volume* volume = new(std::nothrow) Volume(fsVolume);
84 	if (volume == NULL)
85 		RETURN_ERROR(B_NO_MEMORY);
86 	VolumeWriteLocker volumeWriteLocker(volume);
87 
88 	// Initialize the fs_volume now already, so it is mostly usable in during
89 	// mounting.
90 	fsVolume->private_volume = volume;
91 	fsVolume->ops = &gPackageFSVolumeOps;
92 
93 	status_t error = volume->Mount(parameters);
94 	if (error != B_OK) {
95 		volumeWriteLocker.Unlock();
96 		delete volume;
97 		return error;
98 	}
99 
100 	// set return values
101 	*_rootID = volume->RootDirectory()->ID();
102 
103 	return B_OK;
104 }
105 
106 
107 static status_t
108 packagefs_unmount(fs_volume* fsVolume)
109 {
110 	Volume* volume = (Volume*)fsVolume->private_volume;
111 
112 	FUNCTION("volume: %p\n", volume);
113 
114 	volume->WriteLock();
115 	volume->Unmount();
116 	delete volume;
117 
118 	return B_OK;
119 }
120 
121 
122 static status_t
123 packagefs_read_fs_info(fs_volume* fsVolume, struct fs_info* info)
124 {
125 	Volume* volume = (Volume*)fsVolume->private_volume;
126 
127 	FUNCTION("volume: %p, info: %p\n", volume, info);
128 
129 	info->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY | B_FS_HAS_MIME
130 		| B_FS_HAS_ATTR | B_FS_HAS_QUERY | B_FS_SUPPORTS_NODE_MONITORING;
131 	info->block_size = 4096;
132 	info->io_size = kOptimalIOSize;
133 	info->total_blocks = info->free_blocks = 0;
134 	strlcpy(info->volume_name, volume->RootDirectory()->Name(),
135 		sizeof(info->volume_name));
136 	return B_OK;
137 }
138 
139 
140 // #pragma mark - VNodes
141 
142 
143 static status_t
144 packagefs_lookup(fs_volume* fsVolume, fs_vnode* fsDir, const char* entryName,
145 	ino_t* _vnid)
146 {
147 	Volume* volume = (Volume*)fsVolume->private_volume;
148 	Node* node = (Node*)fsDir->private_node;
149 
150 	FUNCTION("volume: %p, dir: %p (%" B_PRId64 "), entry: \"%s\"\n", volume,
151 		node, node->ID(), entryName);
152 
153 	if (!S_ISDIR(node->Mode()))
154 		return B_NOT_A_DIRECTORY;
155 
156 	Directory* dir = dynamic_cast<Directory*>(node);
157 
158 	// resolve "."
159 	if (strcmp(entryName, ".") == 0) {
160 		Node* self;
161 		*_vnid = dir->ID();
162 		return volume->GetVNode(*_vnid, self);
163 	}
164 
165 	// resolve ".."
166 	if (strcmp(entryName, "..") == 0) {
167 		Node* parent;
168 		*_vnid = dir->GetParentUnchecked()->ID();
169 		return volume->GetVNode(*_vnid, parent);
170 	}
171 
172 	// resolve normal entries -- look up the node
173 	DirectoryReadLocker dirLocker(dir);
174 	String entryNameString;
175 	Node* child = dir->FindChild(StringKey(entryName));
176 	if (child == NULL)
177 		return B_ENTRY_NOT_FOUND;
178 	BReference<Node> childReference(child);
179 	dirLocker.Unlock();
180 
181 	// get the vnode reference
182 	*_vnid = child->ID();
183 	RETURN_ERROR(volume->GetVNode(*_vnid, child));
184 }
185 
186 
187 static status_t
188 packagefs_get_vnode_name(fs_volume* fsVolume, fs_vnode* fsNode, char* buffer,
189 	size_t bufferSize)
190 {
191 	Node* node = (Node*)fsNode->private_node;
192 
193 	FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), %p, %zu\n",
194 		fsVolume->private_volume, node, node->ID(), buffer, bufferSize);
195 
196 	if (strlcpy(buffer, node->Name(), bufferSize) >= bufferSize)
197 		return B_BUFFER_OVERFLOW;
198 
199 	return B_OK;
200 }
201 
202 
203 static status_t
204 packagefs_get_vnode(fs_volume* fsVolume, ino_t vnid, fs_vnode* fsNode,
205 	int* _type, uint32* _flags, bool reenter)
206 {
207 	Volume* volume = (Volume*)fsVolume->private_volume;
208 
209 	FUNCTION("volume: %p, vnid: %" B_PRId64 "\n", volume, vnid);
210 
211 	VolumeReadLocker volumeLocker(volume);
212 	Node* node = volume->FindNode(vnid);
213 	if (node == NULL)
214 		return B_ENTRY_NOT_FOUND;
215 	BReference<Node> nodeReference(node);
216 
217 	DirectoryWriteLocker dirLocker;
218 	if (Directory* directory = dynamic_cast<Directory*>(node)) {
219 		dirLocker.SetTo(directory, false, true);
220 		if (!dirLocker.IsLocked())
221 			return B_NO_INIT;
222 	} else {
223 		dirLocker.SetTo(node->GetParentUnchecked(), false, true);
224 		if (!dirLocker.IsLocked())
225 			return B_NO_INIT;
226 	}
227 	volumeLocker.Unlock();
228 
229 	status_t error = node->VFSInit(volume->ID());
230 	if (error != B_OK)
231 		RETURN_ERROR(error);
232 	dirLocker.Unlock();
233 
234 	fsNode->private_node = nodeReference.Detach();
235 	fsNode->ops = &gPackageFSVnodeOps;
236 	*_type = node->Mode() & S_IFMT;
237 	*_flags = 0;
238 
239 	return B_OK;
240 }
241 
242 
243 static status_t
244 packagefs_put_vnode(fs_volume* fsVolume, fs_vnode* fsNode, bool reenter)
245 {
246 	Volume* volume = (Volume*)fsVolume->private_volume;
247 	Node* node = (Node*)fsNode->private_node;
248 
249 	FUNCTION("volume: %p, node: %p\n", volume, node);
250 	TOUCH(volume);
251 
252 	VolumeReadLocker volumeLocker(volume);
253 	DirectoryWriteLocker dirLocker;
254 	if (Directory* directory = dynamic_cast<Directory*>(node)) {
255 		dirLocker.SetTo(directory, false, true);
256 		ASSERT(dirLocker.IsLocked());
257 	} else {
258 		dirLocker.SetTo(node->GetParentUnchecked(), false, true);
259 		if (dirLocker.Get() == NULL) {
260 			// This node does not have a parent. This should only happen during
261 			// removal, in which case we should either have the Volume write lock,
262 			// or the node should have only one reference remaining.
263 			ASSERT(volume->IsWriteLocked() || node->CountReferences() == 1);
264 		}
265 	}
266 	volumeLocker.Unlock();
267 
268 	node->VFSUninit();
269 	dirLocker.Unlock();
270 
271 	node->ReleaseReference();
272 
273 	return B_OK;
274 }
275 
276 
277 // #pragma mark - Request I/O
278 
279 
280 static status_t
281 packagefs_io(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
282 	io_request* request)
283 {
284 	Volume* volume = (Volume*)fsVolume->private_volume;
285 	Node* node = (Node*)fsNode->private_node;
286 
287 	FUNCTION("volume: %p, node: %p (%" B_PRId64 "), cookie: %p, request: %p\n",
288 		volume, node, node->ID(), cookie, request);
289 	TOUCH(volume);
290 
291 	if (io_request_is_write(request))
292 		RETURN_ERROR(B_READ_ONLY_DEVICE);
293 
294 	status_t error = node->Read(request);
295 	notify_io_request(request, error);
296 	return error;
297 }
298 
299 
300 // #pragma mark - Nodes
301 
302 
303 status_t
304 packagefs_ioctl(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
305 	uint32 operation, void* buffer, size_t size)
306 {
307 	Volume* volume = (Volume*)fsVolume->private_volume;
308 	Node* node = (Node*)fsNode->private_node;
309 
310 	FUNCTION("volume: %p, node: %p (%" B_PRId64 "), cookie: %p, operation: %"
311 		B_PRIu32 ", buffer: %p, size: %zu\n", volume, node, node->ID(), cookie,
312 		operation, buffer, size);
313 	TOUCH(cookie);
314 
315 	return volume->IOCtl(node, operation, buffer, size);
316 }
317 
318 
319 static status_t
320 packagefs_read_symlink(fs_volume* fsVolume, fs_vnode* fsNode, char* buffer,
321 	size_t* _bufferSize)
322 {
323 	Volume* volume = (Volume*)fsVolume->private_volume;
324 	Node* node = (Node*)fsNode->private_node;
325 
326 	FUNCTION("volume: %p, node: %p (%" B_PRId64 ")\n", volume, node,
327 		node->ID());
328 	TOUCH(volume);
329 
330 	DirectoryReadLocker dirLocker;
331 	if (!lock_directory_for_node(volume, node, dirLocker))
332 		return B_NO_INIT;
333 
334 	if (!S_ISLNK(node->Mode()))
335 		return B_BAD_VALUE;
336 
337 	return node->ReadSymlink(buffer, _bufferSize);
338 }
339 
340 
341 static status_t
342 packagefs_access(fs_volume* fsVolume, fs_vnode* fsNode, int mode)
343 {
344 	Volume* volume = (Volume*)fsVolume->private_volume;
345 	Node* node = (Node*)fsNode->private_node;
346 
347 	FUNCTION("volume: %p, node: %p (%" B_PRId64 ")\n", volume, node,
348 		node->ID());
349 	TOUCH(volume);
350 
351 	DirectoryReadLocker dirLocker;
352 	if (!lock_directory_for_node(volume, node, dirLocker))
353 		return B_NO_INIT;
354 
355 	return check_access(node, mode);
356 }
357 
358 
359 static status_t
360 packagefs_read_stat(fs_volume* fsVolume, fs_vnode* fsNode, struct stat* st)
361 {
362 	Volume* volume = (Volume*)fsVolume->private_volume;
363 	Node* node = (Node*)fsNode->private_node;
364 
365 	FUNCTION("volume: %p, node: %p (%" B_PRId64 ")\n", volume, node,
366 		node->ID());
367 	TOUCH(volume);
368 
369 	DirectoryReadLocker dirLocker;
370 	if (!lock_directory_for_node(volume, node, dirLocker))
371 		return B_NO_INIT;
372 
373 	st->st_mode = node->Mode();
374 	st->st_nlink = 1;
375 	st->st_uid = node->UserID();
376 	st->st_gid = node->GroupID();
377 	st->st_size = node->FileSize();
378 	st->st_blksize = kOptimalIOSize;
379 	st->st_mtim = node->ModifiedTime();
380 	st->st_atim = st->st_mtim;
381 	st->st_ctim = st->st_mtim;
382 		// TODO: Perhaps manage a changed time (particularly for directories)?
383 	st->st_crtim = st->st_mtim;
384 	st->st_blocks = (st->st_size + 511) / 512;
385 
386 	return B_OK;
387 }
388 
389 
390 // #pragma mark - Files
391 
392 
393 struct FileCookie {
394 	int	openMode;
395 
396 	FileCookie(int openMode)
397 		:
398 		openMode(openMode)
399 	{
400 	}
401 };
402 
403 
404 static status_t
405 packagefs_open(fs_volume* fsVolume, fs_vnode* fsNode, int openMode,
406 	void** _cookie)
407 {
408 	Volume* volume = (Volume*)fsVolume->private_volume;
409 	Node* node = (Node*)fsNode->private_node;
410 
411 	FUNCTION("volume: %p, node: %p (%" B_PRId64 "), openMode %#x\n",
412 		volume, node, node->ID(), openMode);
413 	TOUCH(volume);
414 
415 	DirectoryReadLocker dirLocker;
416 	if (!lock_directory_for_node(volume, node, dirLocker))
417 		return B_NO_INIT;
418 
419 	// check the open mode and permissions
420 	if (S_ISDIR(node->Mode()) && (openMode & O_RWMASK) != O_RDONLY)
421 		return B_IS_A_DIRECTORY;
422 
423 	if ((openMode & O_RWMASK) != O_RDONLY)
424 		return B_NOT_ALLOWED;
425 
426 	status_t error = check_access(node, R_OK);
427 	if (error != B_OK)
428 		return error;
429 
430 	// allocate the cookie
431 	FileCookie* cookie = new(std::nothrow) FileCookie(openMode);
432 	if (cookie == NULL)
433 		RETURN_ERROR(B_NO_MEMORY);
434 
435 	*_cookie = cookie;
436 
437 	return B_OK;
438 }
439 
440 
441 static status_t
442 packagefs_close(fs_volume* fs, fs_vnode* _node, void* cookie)
443 {
444 	return B_OK;
445 }
446 
447 
448 static status_t
449 packagefs_free_cookie(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie)
450 {
451 	Volume* volume = (Volume*)fsVolume->private_volume;
452 	Node* node = (Node*)fsNode->private_node;
453 	FileCookie* cookie = (FileCookie*)_cookie;
454 
455 	FUNCTION("volume: %p, node: %p (%" B_PRId64 "), cookie: %p\n", volume, node,
456 		node->ID(), cookie);
457 	TOUCH(volume);
458 	TOUCH(node);
459 
460 	delete cookie;
461 
462 	return B_OK;
463 }
464 
465 
466 static status_t
467 packagefs_read(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie,
468 	off_t offset, void* buffer, size_t* bufferSize)
469 {
470 	Volume* volume = (Volume*)fsVolume->private_volume;
471 	Node* node = (Node*)fsNode->private_node;
472 	FileCookie* cookie = (FileCookie*)_cookie;
473 
474 	FUNCTION("volume: %p, node: %p (%" B_PRId64 "), cookie: %p, offset: %"
475 		B_PRId64 ", buffer: %p, size: %" B_PRIuSIZE "\n", volume, node,
476 		node->ID(), cookie, offset, buffer, *bufferSize);
477 	TOUCH(volume);
478 
479 	if ((cookie->openMode & O_RWMASK) != O_RDONLY)
480 		return EBADF;
481 
482 	return node->Read(offset, buffer, bufferSize);
483 }
484 
485 
486 // #pragma mark - Directories
487 
488 
489 struct DirectoryCookie : DirectoryIterator {
490 	Directory*	directory;
491 	int32		state;
492 	bool		registered;
493 
494 	DirectoryCookie(Directory* directory)
495 		:
496 		directory(directory),
497 		state(0),
498 		registered(false)
499 	{
500 		Rewind();
501 	}
502 
503 	~DirectoryCookie()
504 	{
505 		if (registered)
506 			directory->RemoveDirectoryIterator(this);
507 	}
508 
509 	void Rewind()
510 	{
511 		if (registered)
512 			directory->RemoveDirectoryIterator(this);
513 		registered = false;
514 
515 		state = 0;
516 		node = directory;
517 	}
518 
519 	Node* Current(const char*& _name) const
520 	{
521 		if (node == NULL)
522 			return NULL;
523 
524 		if (state == 0)
525 			_name = ".";
526 		else if (state == 1)
527 			_name = "..";
528 		else
529 			_name = node->Name();
530 
531 		return node;
532 	}
533 
534 	Node* Next()
535 	{
536 		if (state == 0) {
537 			state = 1;
538 			node = directory->GetParentUnchecked();
539 			if (node == NULL)
540 				node = directory;
541 			return node;
542 		}
543 
544 		if (state == 1) {
545 			node = directory->FirstChild();
546 			state = 2;
547 		} else {
548 			if (node != NULL)
549 				node = directory->NextChild(node);
550 		}
551 
552 		if (node == NULL) {
553 			if (registered) {
554 				directory->RemoveDirectoryIterator(this);
555 				registered = false;
556 			}
557 
558 			return NULL;
559 		}
560 
561 		if (!registered) {
562 			directory->AddDirectoryIterator(this);
563 			registered = true;
564 		}
565 
566 		return node;
567 	}
568 };
569 
570 
571 static status_t
572 packagefs_open_dir(fs_volume* fsVolume, fs_vnode* fsNode, void** _cookie)
573 {
574 	Volume* volume = (Volume*)fsVolume->private_volume;
575 	Node* node = (Node*)fsNode->private_node;
576 
577 	FUNCTION("volume: %p, node: %p (%" B_PRId64 ")\n", volume, node,
578 		node->ID());
579 	TOUCH(volume);
580 
581 	if (!S_ISDIR(node->Mode()))
582 		return B_NOT_A_DIRECTORY;
583 
584 	Directory* dir = dynamic_cast<Directory*>(node);
585 
586 	status_t error = check_access(dir, R_OK);
587 	if (error != B_OK)
588 		return error;
589 
590 	// create a cookie
591 	DirectoryWriteLocker dirLocker(dir);
592 	DirectoryCookie* cookie = new(std::nothrow) DirectoryCookie(dir);
593 	if (cookie == NULL)
594 		RETURN_ERROR(B_NO_MEMORY);
595 
596 	*_cookie = cookie;
597 	return B_OK;
598 }
599 
600 
601 static status_t
602 packagefs_close_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
603 {
604 	return B_OK;
605 }
606 
607 
608 static status_t
609 packagefs_free_dir_cookie(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie)
610 {
611 	Volume* volume = (Volume*)fsVolume->private_volume;
612 	Node* node = (Node*)fsNode->private_node;
613 	DirectoryCookie* cookie = (DirectoryCookie*)_cookie;
614 
615 	FUNCTION("volume: %p, node: %p (%" B_PRId64 "), cookie: %p\n", volume, node,
616 		node->ID(), cookie);
617 	TOUCH(volume);
618 	TOUCH(node);
619 
620 	DirectoryWriteLocker dirLocker(dynamic_cast<Directory*>(node));
621 	delete cookie;
622 
623 	return B_OK;
624 }
625 
626 
627 static status_t
628 packagefs_read_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie,
629 	struct dirent* buffer, size_t bufferSize, uint32* _count)
630 {
631 	Volume* volume = (Volume*)fsVolume->private_volume;
632 	Node* node = (Node*)fsNode->private_node;
633 	DirectoryCookie* cookie = (DirectoryCookie*)_cookie;
634 
635 	FUNCTION("volume: %p, node: %p (%" B_PRId64 "), cookie: %p\n", volume, node,
636 		node->ID(), cookie);
637 	TOUCH(volume);
638 	TOUCH(node);
639 
640 	DirectoryWriteLocker dirLocker(cookie->directory);
641 
642 	uint32 maxCount = *_count;
643 	uint32 count = 0;
644 
645 	dirent* previousEntry = NULL;
646 
647 	const char* name;
648 	while (Node* child = cookie->Current(name)) {
649 		// don't read more entries than requested
650 		if (count >= maxCount)
651 			break;
652 
653 		// align the buffer for subsequent entries
654 		if (count > 0) {
655 			addr_t offset = (addr_t)buffer % 8;
656 			if (offset > 0) {
657 				offset = 8 - offset;
658 				if (bufferSize <= offset)
659 					break;
660 
661 				previousEntry->d_reclen += offset;
662 				buffer = (dirent*)((addr_t)buffer + offset);
663 				bufferSize -= offset;
664 			}
665 		}
666 
667 		// fill in the entry name -- checks whether the entry fits into the
668 		// buffer
669 		if (!set_dirent_name(buffer, bufferSize, name)) {
670 			if (count == 0)
671 				RETURN_ERROR(B_BUFFER_OVERFLOW);
672 			break;
673 		}
674 
675 		// fill in the other data
676 		buffer->d_dev = volume->ID();
677 		buffer->d_ino = child->ID();
678 
679 		count++;
680 		previousEntry = buffer;
681 		bufferSize -= buffer->d_reclen;
682 		buffer = (dirent*)((addr_t)buffer + buffer->d_reclen);
683 
684 		cookie->Next();
685 	}
686 
687 	*_count = count;
688 	return B_OK;
689 }
690 
691 
692 static status_t
693 packagefs_rewind_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie)
694 {
695 	Volume* volume = (Volume*)fsVolume->private_volume;
696 	Node* node = (Node*)fsNode->private_node;
697 	DirectoryCookie* cookie = (DirectoryCookie*)_cookie;
698 
699 	FUNCTION("volume: %p, node: %p (%" B_PRId64 "), cookie: %p\n", volume, node,
700 		node->ID(), cookie);
701 	TOUCH(volume);
702 	TOUCH(node);
703 
704 	DirectoryWriteLocker dirLocker(dynamic_cast<Directory*>(node));
705 	cookie->Rewind();
706 
707 	return B_OK;
708 }
709 
710 
711 // #pragma mark - Attribute Directories
712 
713 
714 status_t
715 packagefs_open_attr_dir(fs_volume* fsVolume, fs_vnode* fsNode, void** _cookie)
716 {
717 	Volume* volume = (Volume*)fsVolume->private_volume;
718 	Node* node = (Node*)fsNode->private_node;
719 
720 	FUNCTION("volume: %p, node: %p (%" B_PRId64 ")\n", volume, node,
721 		node->ID());
722 	TOUCH(volume);
723 
724 	status_t error = check_access(node, R_OK);
725 	if (error != B_OK)
726 		return error;
727 
728 	// create a cookie
729 	DirectoryReadLocker dirLocker;
730 	if (!lock_directory_for_node(volume, node, dirLocker))
731 		return B_NO_INIT;
732 
733 	AttributeDirectoryCookie* cookie;
734 	error = node->OpenAttributeDirectory(cookie);
735 	if (error != B_OK)
736 		RETURN_ERROR(error);
737 
738 	*_cookie = cookie;
739 	return B_OK;
740 }
741 
742 
743 status_t
744 packagefs_close_attr_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie)
745 {
746 	AttributeDirectoryCookie* cookie = (AttributeDirectoryCookie*)_cookie;
747 	return cookie->Close();
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 (%" B_PRId64 "), 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 (%" B_PRId64 "), cookie: %p\n", volume, node,
779 		node->ID(), cookie);
780 	TOUCH(volume);
781 	TOUCH(node);
782 
783 	return cookie->Read(volume->ID(), node->ID(), buffer, bufferSize, _count);
784 }
785 
786 
787 status_t
788 packagefs_rewind_attr_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie)
789 {
790 	Volume* volume = (Volume*)fsVolume->private_volume;
791 	Node* node = (Node*)fsNode->private_node;
792 	AttributeDirectoryCookie* cookie = (AttributeDirectoryCookie*)_cookie;
793 
794 	FUNCTION("volume: %p, node: %p (%" B_PRId64 "), cookie: %p\n", volume, node,
795 		node->ID(), cookie);
796 	TOUCH(volume);
797 	TOUCH(node);
798 
799 	return cookie->Rewind();
800 }
801 
802 
803 // #pragma mark - Attribute Operations
804 
805 
806 status_t
807 packagefs_open_attr(fs_volume* fsVolume, fs_vnode* fsNode, const char* name,
808 	int openMode, void** _cookie)
809 {
810 	Volume* volume = (Volume*)fsVolume->private_volume;
811 	Node* node = (Node*)fsNode->private_node;
812 
813 	FUNCTION("volume: %p, node: %p (%" B_PRId64 "), name: \"%s\", openMode "
814 			"%#x\n", volume, node, node->ID(), name, openMode);
815 	TOUCH(volume);
816 
817 	DirectoryReadLocker dirLocker;
818 	if (!lock_directory_for_node(volume, node, dirLocker))
819 		return B_NO_INIT;
820 
821 	// check the open mode and permissions
822 	if ((openMode & O_RWMASK) != O_RDONLY)
823 		return B_NOT_ALLOWED;
824 
825 	status_t error = check_access(node, R_OK);
826 	if (error != B_OK)
827 		return error;
828 
829 	AttributeCookie* cookie;
830 	error = node->OpenAttribute(StringKey(name), openMode, cookie);
831 	if (error != B_OK)
832 		return error;
833 
834 	*_cookie = cookie;
835 	return B_OK;
836 }
837 
838 
839 status_t
840 packagefs_close_attr(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie)
841 {
842 	AttributeCookie* cookie = (AttributeCookie*)_cookie;
843 	RETURN_ERROR(cookie->Close());
844 }
845 
846 
847 status_t
848 packagefs_free_attr_cookie(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie)
849 {
850 	Volume* volume = (Volume*)fsVolume->private_volume;
851 	Node* node = (Node*)fsNode->private_node;
852 	AttributeCookie* cookie = (AttributeCookie*)_cookie;
853 
854 	FUNCTION("volume: %p, node: %p (%" B_PRId64 "), cookie: %p\n", volume, node,
855 		node->ID(), cookie);
856 	TOUCH(volume);
857 	TOUCH(node);
858 
859 	delete cookie;
860 
861 	return B_OK;
862 }
863 
864 
865 status_t
866 packagefs_read_attr(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie,
867 	off_t offset, void* buffer, size_t* bufferSize)
868 {
869 	Volume* volume = (Volume*)fsVolume->private_volume;
870 	Node* node = (Node*)fsNode->private_node;
871 	AttributeCookie* cookie = (AttributeCookie*)_cookie;
872 
873 	FUNCTION("volume: %p, node: %p (%" B_PRId64 "), cookie: %p\n", volume, node,
874 		node->ID(), cookie);
875 	TOUCH(volume);
876 	TOUCH(node);
877 
878 	return cookie->ReadAttribute(offset, buffer, bufferSize);
879 }
880 
881 
882 status_t
883 packagefs_read_attr_stat(fs_volume* fsVolume, fs_vnode* fsNode,
884 	void* _cookie, struct stat* st)
885 {
886 	Volume* volume = (Volume*)fsVolume->private_volume;
887 	Node* node = (Node*)fsNode->private_node;
888 	AttributeCookie* cookie = (AttributeCookie*)_cookie;
889 
890 	FUNCTION("volume: %p, node: %p (%" B_PRId64 "), cookie: %p\n", volume, node,
891 		node->ID(), cookie);
892 	TOUCH(volume);
893 	TOUCH(node);
894 
895 	return cookie->ReadAttributeStat(st);
896 }
897 
898 
899 // #pragma mark - index directory & index operations
900 
901 
902 // NOTE: We don't do any locking in the index dir hooks, since once mounted
903 // the index directory is immutable.
904 
905 
906 status_t
907 packagefs_open_index_dir(fs_volume* fsVolume, void** _cookie)
908 {
909 	Volume* volume = (Volume*)fsVolume->private_volume;
910 
911 	FUNCTION("volume: %p\n", volume);
912 
913 	IndexDirIterator* iterator = new(std::nothrow) IndexDirIterator(
914 		volume->GetIndexDirIterator());
915 	if (iterator == NULL)
916 		return B_NO_MEMORY;
917 
918 	*_cookie = iterator;
919 	return B_OK;
920 }
921 
922 
923 status_t
924 packagefs_close_index_dir(fs_volume* fsVolume, void* cookie)
925 {
926 	return B_OK;
927 }
928 
929 
930 status_t
931 packagefs_free_index_dir_cookie(fs_volume* fsVolume, void* cookie)
932 {
933 	FUNCTION("volume: %p, cookie: %p\n", fsVolume->private_volume, cookie);
934 
935 	delete (IndexDirIterator*)cookie;
936 	return B_OK;
937 }
938 
939 
940 status_t
941 packagefs_read_index_dir(fs_volume* fsVolume, void* cookie,
942 	struct dirent* buffer, size_t bufferSize, uint32* _num)
943 {
944 	Volume* volume = (Volume*)fsVolume->private_volume;
945 
946 	FUNCTION("volume: %p, cookie: %p, buffer: %p, bufferSize: %zu, num: %"
947 		B_PRIu32 "\n", volume, cookie, buffer, bufferSize, *_num);
948 
949 	IndexDirIterator* iterator = (IndexDirIterator*)cookie;
950 
951 	if (*_num == 0)
952 		return B_BAD_VALUE;
953 
954 	IndexDirIterator previousIterator = *iterator;
955 
956 	// get the next index
957 	Index* index = iterator->Next();
958 	if (index == NULL) {
959 		*_num = 0;
960 		return B_OK;
961 	}
962 
963 	// fill in the entry
964 	if (!set_dirent_name(buffer, bufferSize, index->Name())) {
965 		*iterator = previousIterator;
966 		return B_BUFFER_OVERFLOW;
967 	}
968 
969 	buffer->d_dev = volume->ID();
970 	buffer->d_ino = 0;
971 
972 	*_num = 1;
973 	return B_OK;
974 }
975 
976 
977 status_t
978 packagefs_rewind_index_dir(fs_volume* fsVolume, void* cookie)
979 {
980 	Volume* volume = (Volume*)fsVolume->private_volume;
981 
982 	FUNCTION("volume: %p, cookie: %p\n", volume, cookie);
983 
984 	IndexDirIterator* iterator = (IndexDirIterator*)cookie;
985 	*iterator = volume->GetIndexDirIterator();
986 
987 	return B_OK;
988 }
989 
990 
991 status_t
992 packagefs_create_index(fs_volume* fsVolume, const char* name, uint32 type,
993 	uint32 flags)
994 {
995 	return B_NOT_SUPPORTED;
996 }
997 
998 
999 status_t
1000 packagefs_remove_index(fs_volume* fsVolume, const char* name)
1001 {
1002 	return B_NOT_SUPPORTED;
1003 }
1004 
1005 
1006 status_t
1007 packagefs_read_index_stat(fs_volume* fsVolume, const char* name,
1008 	struct stat* stat)
1009 {
1010 	Volume* volume = (Volume*)fsVolume->private_volume;
1011 
1012 	FUNCTION("volume: %p, name: \"%s\", stat: %p\n", volume, name, stat);
1013 
1014 	Index* index = volume->FindIndex(StringKey(name));
1015 	if (index == NULL)
1016 		return B_ENTRY_NOT_FOUND;
1017 
1018 	VolumeReadLocker volumeReadLocker(volume);
1019 
1020 	memset(stat, 0, sizeof(*stat));
1021 		// TODO: st_mtime, st_crtime, st_uid, st_gid are made available to
1022 		// userland, so we should make an attempt to fill in values that make
1023 		// sense.
1024 
1025 	stat->st_type = index->Type();
1026 	stat->st_size = index->CountEntries();
1027 
1028 	return B_OK;
1029 }
1030 
1031 
1032 // #pragma mark - query operations
1033 
1034 
1035 status_t
1036 packagefs_open_query(fs_volume* fsVolume, const char* queryString, uint32 flags,
1037 	port_id port, uint32 token, void** _cookie)
1038 {
1039 	Volume* volume = (Volume*)fsVolume->private_volume;
1040 
1041 	FUNCTION("volume: %p, query: \"%s\", flags: %#" B_PRIx32 ", port: %"
1042 		B_PRId32 ", token: %" B_PRIu32 "\n", volume, queryString, flags, port,
1043 		token);
1044 
1045 	VolumeWriteLocker volumeWriteLocker(volume);
1046 
1047 	Query* query;
1048 	status_t error = Query::Create(volume, queryString, flags, port, token,
1049 		query);
1050 	if (error != B_OK)
1051 		return error;
1052 
1053 	*_cookie = query;
1054 	return B_OK;
1055 }
1056 
1057 
1058 status_t
1059 packagefs_close_query(fs_volume* fsVolume, void* cookie)
1060 {
1061 	FUNCTION_START();
1062 	return B_OK;
1063 }
1064 
1065 
1066 status_t
1067 packagefs_free_query_cookie(fs_volume* fsVolume, void* cookie)
1068 {
1069 	Volume* volume = (Volume*)fsVolume->private_volume;
1070 	Query* query = (Query*)cookie;
1071 
1072 	FUNCTION("volume: %p, query: %p\n", volume, query);
1073 
1074 	VolumeWriteLocker volumeWriteLocker(volume);
1075 
1076 	delete query;
1077 
1078 	return B_OK;
1079 }
1080 
1081 
1082 status_t
1083 packagefs_read_query(fs_volume* fsVolume, void* cookie, struct dirent* buffer,
1084 	size_t bufferSize, uint32* _num)
1085 {
1086 	Volume* volume = (Volume*)fsVolume->private_volume;
1087 	Query* query = (Query*)cookie;
1088 
1089 	FUNCTION("volume: %p, query: %p\n", volume, query);
1090 
1091 	VolumeWriteLocker volumeWriteLocker(volume);
1092 
1093 	status_t error = query->GetNextEntry(buffer, bufferSize);
1094 	if (error == B_OK)
1095 		*_num = 1;
1096 	else if (error == B_ENTRY_NOT_FOUND)
1097 		*_num = 0;
1098 	else
1099 		return error;
1100 
1101 	return B_OK;
1102 }
1103 
1104 
1105 status_t
1106 packagefs_rewind_query(fs_volume* fsVolume, void* cookie)
1107 {
1108 	Volume* volume = (Volume*)fsVolume->private_volume;
1109 	Query* query = (Query*)cookie;
1110 
1111 	FUNCTION("volume: %p, query: %p\n", volume, query);
1112 
1113 	VolumeWriteLocker volumeWriteLocker(volume);
1114 
1115 	return query->Rewind();
1116 }
1117 
1118 
1119 // #pragma mark - Module Interface
1120 
1121 
1122 static status_t
1123 packagefs_std_ops(int32 op, ...)
1124 {
1125 	using BPackageKit::BHPKG::BPrivate::PackageFileHeapAccessorBase;
1126 
1127 	switch (op) {
1128 		case B_MODULE_INIT:
1129 		{
1130 			init_debugging();
1131 			PRINT("package_std_ops(): B_MODULE_INIT\n");
1132 
1133 			status_t error = StringPool::Init();
1134 			if (error != B_OK) {
1135 				ERROR("Failed to init StringPool\n");
1136 				exit_debugging();
1137 				return error;
1138 			}
1139 
1140 			if (!StringConstants::Init()) {
1141 				ERROR("Failed to init string constants\n");
1142 				StringPool::Cleanup();
1143 				exit_debugging();
1144 				return error;
1145 			}
1146 
1147 			PackageFileHeapAccessorBase::sChunkCache =
1148 				create_object_cache_etc("pkgfs heap buffers",
1149 					PackageFileHeapAccessorBase::kChunkSize, sizeof(void*),
1150 					0, /* magazine capacity, count */ 2, 1,
1151 					0, NULL, NULL, NULL, NULL);
1152 			TwoKeyAVLTreeNode<void*>::sNodeCache =
1153 				create_object_cache_etc("pkgfs TKAVLTreeNodes",
1154 					sizeof(TwoKeyAVLTreeNode<void*>), 8,
1155 					0, 0, 0, CACHE_NO_DEPOT, NULL, NULL, NULL, NULL);
1156 
1157 			error = PackageFSRoot::GlobalInit();
1158 			if (error != B_OK) {
1159 				ERROR("Failed to init PackageFSRoot\n");
1160 				StringConstants::Cleanup();
1161 				StringPool::Cleanup();
1162 				exit_debugging();
1163 				return error;
1164 			}
1165 
1166 			return B_OK;
1167 		}
1168 
1169 		case B_MODULE_UNINIT:
1170 		{
1171 			PRINT("package_std_ops(): B_MODULE_UNINIT\n");
1172 			PackageFSRoot::GlobalUninit();
1173 			delete_object_cache(TwoKeyAVLTreeNode<void*>::sNodeCache);
1174 			delete_object_cache((object_cache*)
1175 				PackageFileHeapAccessorBase::sChunkCache);
1176 			StringConstants::Cleanup();
1177 			StringPool::Cleanup();
1178 			exit_debugging();
1179 			return B_OK;
1180 		}
1181 
1182 		default:
1183 			return B_ERROR;
1184 	}
1185 }
1186 
1187 
1188 static file_system_module_info sPackageFSModuleInfo = {
1189 	{
1190 		"file_systems/packagefs" B_CURRENT_FS_API_VERSION,
1191 		0,
1192 		packagefs_std_ops,
1193 	},
1194 
1195 	"packagefs",				// short_name
1196 	"Package File System",		// pretty_name
1197 	0,							// DDM flags
1198 
1199 
1200 	// scanning
1201 	NULL,	// identify_partition,
1202 	NULL,	// scan_partition,
1203 	NULL,	// free_identify_partition_cookie,
1204 	NULL,	// free_partition_content_cookie()
1205 
1206 	&packagefs_mount
1207 };
1208 
1209 
1210 fs_volume_ops gPackageFSVolumeOps = {
1211 	&packagefs_unmount,
1212 	&packagefs_read_fs_info,
1213 	NULL,	// write_fs_info,
1214 	NULL,	// sync,
1215 
1216 	&packagefs_get_vnode,
1217 
1218 	// index directory
1219 	&packagefs_open_index_dir,
1220 	&packagefs_close_index_dir,
1221 	&packagefs_free_index_dir_cookie,
1222 	&packagefs_read_index_dir,
1223 	&packagefs_rewind_index_dir,
1224 
1225 	&packagefs_create_index,
1226 	&packagefs_remove_index,
1227 	&packagefs_read_index_stat,
1228 
1229 	// query operations
1230 	&packagefs_open_query,
1231 	&packagefs_close_query,
1232 	&packagefs_free_query_cookie,
1233 	&packagefs_read_query,
1234 	&packagefs_rewind_query,
1235 
1236 	// TODO: FS layer operations
1237 };
1238 
1239 
1240 fs_vnode_ops gPackageFSVnodeOps = {
1241 	// vnode operations
1242 	&packagefs_lookup,
1243 	&packagefs_get_vnode_name,
1244 	&packagefs_put_vnode,
1245 	&packagefs_put_vnode,	// remove_vnode -- same as put_vnode
1246 
1247 	// VM file access
1248 	NULL,	// can_page,
1249 	NULL,	// read_pages,
1250 	NULL,	// write_pages,
1251 
1252 	&packagefs_io,
1253 	NULL,	// cancel_io()
1254 
1255 	NULL,	// get_file_map,
1256 
1257 	&packagefs_ioctl,
1258 	NULL,	// set_flags,
1259 	NULL,	// select,
1260 	NULL,	// deselect,
1261 	NULL,	// fsync,
1262 
1263 	&packagefs_read_symlink,
1264 	NULL,	// create_symlink,
1265 
1266 	NULL,	// link,
1267 	NULL,	// unlink,
1268 	NULL,	// rename,
1269 
1270 	&packagefs_access,
1271 	&packagefs_read_stat,
1272 	NULL,	// write_stat,
1273 	NULL,	// preallocate,
1274 
1275 	// file operations
1276 	NULL,	// create,
1277 	&packagefs_open,
1278 	&packagefs_close,
1279 	&packagefs_free_cookie,
1280 	&packagefs_read,
1281 	NULL,	// write,
1282 
1283 	// directory operations
1284 	NULL,	// create_dir,
1285 	NULL,	// remove_dir,
1286 	&packagefs_open_dir,
1287 	&packagefs_close_dir,
1288 	&packagefs_free_dir_cookie,
1289 	&packagefs_read_dir,
1290 	&packagefs_rewind_dir,
1291 
1292 	// attribute directory operations
1293 	&packagefs_open_attr_dir,
1294 	&packagefs_close_attr_dir,
1295 	&packagefs_free_attr_dir_cookie,
1296 	&packagefs_read_attr_dir,
1297 	&packagefs_rewind_attr_dir,
1298 
1299 	// attribute operations
1300 	NULL,	// create_attr,
1301 	&packagefs_open_attr,
1302 	&packagefs_close_attr,
1303 	&packagefs_free_attr_cookie,
1304 	&packagefs_read_attr,
1305 	NULL,	// write_attr,
1306 
1307 	&packagefs_read_attr_stat,
1308 	NULL,	// write_attr_stat,
1309 	NULL,	// rename_attr,
1310 	NULL	// remove_attr,
1311 
1312 	// TODO: FS layer operations
1313 };
1314 
1315 
1316 module_info *modules[] = {
1317 	(module_info *)&sPackageFSModuleInfo,
1318 	NULL,
1319 };
1320