1#define _BUILDING_fs 1
2
3#include "betalk.h"
4#include "nfs_add_on.h"
5
6#ifndef BONE_VERSION
7#include "ksocket.h"
8#else
9#include <sys/socket_module.h>
10#endif
11
12#include <errno.h>
13#include <string.h>
14#include <KernelExport.h>
15#include <sys/stat.h>
16#include <dirent.h>
17#include <SupportDefs.h>
18
19
20_EXPORT vnode_ops fs_entry =
21{
22	(op_read_vnode *) &fs_read_vnode,
23	(op_write_vnode *) &fs_write_vnode,
24	(op_remove_vnode *) &fs_remove_vnode,
25	(op_secure_vnode *) &fs_secure_vnode,
26	(op_walk *) &fs_walk,
27	(op_access *) &fs_access,
28	(op_create *) &fs_create,
29	(op_mkdir *) &fs_mkdir,
30	(op_symlink *) &fs_symlink,
31	NULL, //	&fs_link,
32	(op_rename *) &fs_rename,
33	(op_unlink *) &fs_unlink,
34	(op_rmdir *) &fs_rmdir,
35	(op_readlink *) &fs_readlink,
36	(op_opendir *) &fs_opendir,
37	(op_closedir *) &fs_closedir,
38	(op_free_cookie *) &fs_free_dircookie,
39	(op_rewinddir *) &fs_rewinddir,
40	(op_readdir *) &fs_readdir,
41	(op_open *) &fs_open,
42	(op_close *) &fs_close,
43	(op_free_cookie *) &fs_free_cookie,
44	(op_read *) &fs_read,
45	(op_write *) &fs_write,
46	NULL, //	&fs_readv
47	NULL, //	&fs_writev
48	NULL, //	&fs_ioctl,
49	NULL, //	&fs_setflags,
50	(op_rstat *) &fs_rstat,
51	(op_wstat *) &fs_wstat,
52	NULL, //	&fs_fsync,
53	NULL, //	&fs_initialize,
54	(op_mount *) &fs_mount,
55	(op_unmount *) &fs_unmount,
56	NULL, //	&fs_sync,
57	(op_rfsstat *) &fs_rfsstat,
58	(op_wfsstat *) &fs_wfsstat,
59	NULL, //	&fs_select
60	NULL, //	&fs_deselect
61	(op_open_indexdir *) &fs_open_indexdir,
62	(op_close_indexdir *) &fs_close_indexdir,
63	(op_free_cookie *) &fs_free_indexdircookie,
64	(op_rewind_indexdir *) &fs_rewind_indexdir,
65	(op_read_indexdir *) &fs_read_indexdir,
66	(op_create_index *) &fsCreateIndex,
67	(op_remove_index *) &fsRemoveIndex,
68	NULL, //	&fs_rename_index,
69	(op_stat_index *) &fsStatIndex,
70	(op_open_attrdir *) &fs_open_attribdir,
71	(op_close_attrdir *) &fs_close_attribdir,
72	(op_free_cookie *) &fs_free_attribdircookie,
73	(op_rewind_attrdir *) &fs_rewind_attribdir,
74	(op_read_attrdir *) &fs_read_attribdir,
75	(op_write_attr *) &fs_write_attrib,
76	(op_read_attr *) &fs_read_attrib,
77	(op_remove_attr *) &fs_remove_attrib,
78	NULL, //	&fs_rename_attrib,
79	(op_stat_attr *) &fs_stat_attrib,
80	(op_open_query *) &fsOpenQuery,
81	(op_close_query *) &fsCloseQuery,
82	(op_free_cookie *) &fsFreeQueryCookie,
83	(op_read_query *) &fsReadQuery
84};
85
86_EXPORT int32 api_version = B_CUR_FS_API_VERSION;
87
88#ifdef BONE_VERSION
89
90struct bone_socket_info *bone_module = NULL;
91
92// bone_init()
93//
94bool bone_init()
95{
96	// Get a pointer to the BONE module.
97	get_module(BONE_SOCKET_MODULE, (module_info **) &bone_module);
98	return (bone_module != NULL);
99}
100
101// bone_cleanup()
102//
103void bone_cleanup()
104{
105	put_module(BONE_SOCKET_MODULE);
106}
107#endif
108
109fs_node *create_node(fs_nspace *ns, vnode_id dir_vnid, const char *name, vnode_id file_vnid, bool newVnid)
110{
111	fs_node *newNode, *dupNode;
112	bool obsolete_nodes = false;
113
114	while (acquire_sem(ns->sem) == B_INTERRUPTED);
115
116	// Check to see if we find a node with this vnid.  If we do, we need to know if it
117	// is the same file.  This can be determined by examining the parent directory vnid
118	// and the filename.  If everything matches, just return that node.
119	dupNode = getDuplicateVnid(ns, file_vnid);
120	if (dupNode)
121	{
122		// If it looks like this vnid, smells like this vnid, and tastes like this vnid,
123		// it must be this vnid.  Alright, there's one exception -- symbolic links.  These
124		// have the same vnid, but another name.
125		if (dupNode->parent == dir_vnid)
126			if (strcmp(dupNode->name, name) == 0)
127			{
128				release_sem(ns->sem);
129				return dupNode;
130			}
131
132		// If we still found a vnid, but the parent directory vnid or filename don't
133		// match, then the original file is gone and another file has taken this vnid.
134		// Another user caused this change, which isn't registered with the local
135		// system's vnid table.  In this case we adjust our internal node, and notify
136		// any listeners that the file has been renamed.  Note that if this is the user
137		// that renamed the file, a duplicate notify_listener() call will be made by
138		// fs_rename(), but this seems to have no adverse effect.
139		dprintf("DUP VNID:  %s, %s\n", name, dupNode->name);
140//		dprintf("notify_listener(B_ENTRY_MOVED, %s, %lu); -- duplicate vnid notice\n", name, file_vnid);
141//		notify_listener(B_ENTRY_MOVED, ns->nsid, dupNode->parent, dir_vnid, file_vnid, name);
142//		dupNode->parent = dir_vnid;
143//		strcpy(dupNode->name, name);
144//		release_sem(ns->sem);
145//		return dupNode;
146	}
147
148	// Well, we now know we have a unique vnid.  However, it is possible that the same
149	// file exists in our list under a different vnid.  If a file is deleted, then re-created
150	// by another user and assigned a different vnid, then our existing vnid is obsolete
151	// and we must abandon it in favor of the replacement.  Are we having fun yet?
152	dupNode = getDuplicateName(ns, dir_vnid, name);
153	if (dupNode)
154		if (dupNode->vnid != file_vnid)
155		{
156			dprintf("DUP NAME:  %s (%lu)\n", dupNode->name, dupNode->vnid);
157			remove_obsolete_node(ns, dupNode);
158			obsolete_nodes = true;
159		}
160
161	newNode = (fs_node *) malloc(sizeof(fs_node));
162	if (newNode)
163	{
164		// Initialize the members of this new node.
165		newNode->parent = dir_vnid;
166		strcpy(newNode->name, name);
167		newNode->vnid = file_vnid;
168
169		// Link the node in the list by making it the head.
170		newNode->next = ns->first;
171		ns->first = newNode;
172
173		// If we're creating a new file, or we just had to delete our old vnid-cookie pair
174		// above, then we need to register this vnid with the newly created node.
175		if (newVnid || obsolete_nodes)
176			new_vnode(ns->nsid, file_vnid, newNode);
177
178		dprintf("NEW NODE:  %s (%lu)\n", name, file_vnid);
179//		print_vnode_list(ns);
180	}
181
182	release_sem(ns->sem);
183
184	if (obsolete_nodes)
185		notify_listener(B_ENTRY_CREATED, ns->nsid, dir_vnid, 0, file_vnid, name);
186
187	return newNode;
188}
189
190void print_vnode_list(fs_nspace *ns)
191{
192	fs_node *current;
193
194	dprintf("    --- VNODE LIST --------------------------\n");
195	current = ns->first;
196	while (current)
197	{
198		dprintf("        %-35s   %lu\n", current->name, current->vnid);
199		current = current->next;
200	}
201	dprintf("    --- END OF LIST -------------------------\n");
202}
203
204void remove_obsolete_node(fs_nspace *ns, fs_node *node)
205{
206	release_sem(ns->sem);
207
208	remove_node(ns, node->vnid);
209
210	dprintf("    Notifying others that %s (%lu) was removed\n", node->name, node->vnid);
211	remove_vnode(ns->nsid, node->vnid);
212	put_vnode(ns->nsid, node->vnid);
213	notify_listener(B_ENTRY_REMOVED, ns->nsid, node->parent, 0, node->vnid, node->name);
214
215	while (acquire_sem(ns->sem) == B_INTERRUPTED);
216}
217
218void rename_node(fs_nspace *ns, vnode_id old_dir_vnid, const char *oldname, vnode_id new_dir_vnid, const char *newname, vnode_id file_vnid)
219{
220	fs_node *current;
221
222	while (acquire_sem(ns->sem) == B_INTERRUPTED);
223
224	// Check to see if we find a node with this vnid.  If we do, we need to know if it
225	// is the same file.  This can be determined by examining the parent directory vnid
226	// and the filename.  If everything matches, rename the node to the new name.
227	current = ns->first;
228	while (current)
229	{
230		if (current->vnid == file_vnid)
231			if (current->parent == old_dir_vnid)
232				if (strcmp(current->name, oldname) == 0)
233				{
234					strcpy(current->name, newname);
235					current->parent = new_dir_vnid;
236					break;
237				}
238
239		current = current->next;
240	}
241
242	release_sem(ns->sem);
243}
244
245fs_node *getDuplicateVnid(fs_nspace *ns, vnode_id vnid)
246{
247	fs_node *current;
248
249	current = ns->first;
250	while (current && current->vnid != vnid)
251		current = current->next;
252
253	return current;
254}
255
256fs_node *getDuplicateName(fs_nspace *ns, vnode_id parent, const char *name)
257{
258	fs_node *current;
259
260	current = ns->first;
261	while (current)
262	{
263		if (current->parent == parent)
264			if (strcmp(current->name, name) == 0)
265				break;
266
267		current = current->next;
268	}
269
270	return current;
271}
272
273void remove_node(fs_nspace *ns, vnode_id vnid)
274{
275	fs_node *current;
276	fs_node *previous;
277
278	while (acquire_sem(ns->sem) == B_INTERRUPTED);
279
280	current = ns->first;
281	previous = NULL;
282
283	while (current && current->vnid != vnid)
284	{
285		previous = current;
286		current = current->next;
287	}
288
289	if (current)
290	{
291		if (previous)
292			previous->next = current->next;
293		else
294			ns->first = current->next;
295
296		free(current);
297	}
298
299	release_sem(ns->sem);
300}
301
302// fs_read_vnode()
303//
304// Description:  Allocate resources required to load the specified vnid.  Also returns
305// a reference to the vnode via the supplied pointer.
306//
307// Parameters:
308// ns (in):		Private file system structure
309// vnid (in):	vnid of the vnode to load
310// r (in):		??
311// node (out):	A reference to the vnode loaded
312//
313// Returns:
314// B_OK if everything is successful, or
315// EINVAL if the specified vnid is not found
316//
317extern int fs_read_vnode(fs_nspace *ns, vnode_id vnid, char r, fs_node **node)
318{
319	fs_node *current;
320
321//dprintf("fs_read_vnode()\n");
322	*node = NULL;
323
324	// If we need to acquire the semaphone to search the list, do so now.
325	if (!r)
326		while (acquire_sem(ns->sem) == B_INTERRUPTED);
327
328	current = ns->first;
329	while (current && current->vnid != vnid)
330		current = current->next;
331
332	// TODO: shouldn't we release the semaphore here?
333	// If the specified parameter is non-null, then return the node we found.
334	if (current && node)
335		*node = current;
336
337	// Release the semaphore if necessary.
338	if (!r)
339		release_sem(ns->sem);
340
341	return (current ? B_OK : EINVAL);
342}
343
344// fs_write_vnode()
345//
346// Description:  Free resources allocated with the sister function fs_read_vnode().
347// Parameters:
348// ns (in):		Private file system structure
349// node (in):	Node that can be freed
350// r (in):		??
351// Returns:
352// B_OK if everything is successful (always in our case)
353//
354extern int fs_write_vnode(fs_nspace *ns, fs_node *node, char r)
355{
356//dprintf("fs_write_vnode()\n");
357	// In our case, there aren't any resources to free, so there isn't anything
358	// for this function to do but return successfully.
359	return B_OK;
360}
361
362// fs_walk()
363//
364// Description:  This function is the single file system location where a path is
365// traversed by name.  Given a directory handle and a file name, this function returns
366// the vnid of the file if it indeed exists.
367//
368// Parameters:
369// ns (in):		Private file system structure
370// base (in):	File handle of the directory to look in
371// file (in):	Name of the file to look for
372// newpath (out):	Name of the real file if the given file is a symbolic link
373// vnid (out):	Vnid of the file given by "file"
374//
375// Returns:
376// B_OK if everything is successful,
377// Otherwise an error code detailing the problem
378//
379extern int fs_walk(fs_nspace *ns, fs_node *base, const char *file, char **newpath, vnode_id *vnid)
380{
381	bool isLink;
382	void *dummy;
383	status_t result;
384
385//dprintf("fs_walk()\n");
386	// If the file specified is the current directory, then that parameter provides
387	// the necessary vnid.  Also note that the current directory entry cannot be a
388	// symbolic link.
389	if (!strcmp(".", file))
390	{
391		*vnid = base->vnid;
392		isLink = false;
393	}
394	else
395	{
396		struct stat st;
397		status_t result;
398
399 		// Look up the requested entry, getting the stat structure and file handle
400		// filled in.
401		if ((result = btLookup(ns, base->vnid, file, vnid, &st)) != B_OK)
402			return result;
403
404		// The vnid is the resulting file's inode.  Once this is established, insert
405		// the new node in our list.
406		if (create_node(ns, base->vnid, file, *vnid, false) == NULL)
407			return ENOMEM;
408
409		isLink = S_ISLNK(st.st_mode);
410	}
411
412	if ((result = get_vnode(ns->nsid, *vnid, (void **) &dummy)) < B_OK)
413		return result;
414
415	// If we've found a symbolic link, and the supplied character pointer is non-null,
416	// then the vnode layer is looking for the actual path.
417	if (isLink && newpath)
418	{
419		char path[B_PATH_NAME_LENGTH + 1];
420		size_t pathLen = B_PATH_NAME_LENGTH;
421
422		if ((result = fs_readlink(ns, dummy, path, &pathLen)) < B_OK)
423		{
424			put_vnode(ns->nsid, *vnid);
425			return result;
426		}
427
428		path[pathLen] = 0;
429		result = new_path(path, newpath);
430
431		if (result < B_OK)
432		{
433			put_vnode(ns->nsid, *vnid);
434			return result;
435		}
436
437		return put_vnode(ns->nsid, *vnid);
438	}
439
440	return B_OK;
441}
442
443// fs_opendir()
444//
445extern int fs_opendir(fs_nspace *ns, fs_node *node, btCookie **cookie)
446{
447	struct stat st;
448	status_t result;
449
450//dprintf("fs_opendir()\n");
451	// Do a basic stat() on this file to verify we indeed have a directory.
452	// If we cannot obtain this information, we have an even bigger problem.
453	if ((result = btStat(ns, node->vnid, &st)) < B_OK)
454		return result;
455
456	// Now that we have the right information, verify we're looking at a directory.
457	if (!S_ISDIR(st.st_mode))
458		return ENOTDIR;
459
460	// Allocate and initialize the cookie.
461	*cookie = (btCookie *) malloc(sizeof(btCookie));
462	if (!*cookie)
463		return ENOMEM;
464
465	memset((*cookie)->opaque, 0, BT_COOKIE_SIZE);
466	(*cookie)->lpbCache = false;
467	(*cookie)->eof = false;
468
469	return B_OK;
470}
471
472// fs_closedir()
473//
474extern int fs_closedir(fs_nspace *ns, fs_node *node, btCookie *cookie)
475{
476//dprintf("fs_closedir()\n");
477	btEmptyLPBCache(cookie);
478	return B_OK;
479}
480
481// fs_rewinddir()
482//
483extern int fs_rewinddir(fs_nspace *ns, fs_node *node, btCookie *cookie)
484{
485//dprintf("fs_rewinddir()\n");
486	memset(cookie->opaque, 0, BT_COOKIE_SIZE);
487	cookie->eof = false;
488	btEmptyLPBCache(cookie);
489	return B_OK;
490}
491
492// fs_readdir()
493//
494extern int fs_readdir(fs_nspace *ns, fs_node *node, btCookie *cookie, long *num, struct dirent *buf, size_t bufsize)
495{
496	status_t result;
497	vnode_id vnid;
498	char *filename;
499	long max, bufContent;
500	int32 value;
501
502//dprintf("fs_readdir()\n");
503	max = *num;
504	*num = 0;
505
506	// Cause the directory to be read.
507	while ((result = btReadDir(ns, node->vnid, &vnid, &filename, cookie)) == B_OK)
508	{
509		if (strcmp(filename, ".") && strcmp(filename, ".."))
510		{
511			create_node(ns, node->vnid, filename, vnid, false);
512
513//			dirCount = bufsize / sizeof(struct dirent);
514//			pktCount = BT_MAX_IO_BUFFER / sizeof(struct dirent);
515//			dirCount = min((), ());
516
517			bufContent = 2 * (sizeof(dev_t) + sizeof(ino_t)) + sizeof(unsigned short) + strlen(filename) + 1;
518			if (bufsize < bufContent)
519				return B_OK;
520
521			buf->d_dev = ns->nsid;
522			buf->d_pdev = ns->nsid;
523			buf->d_ino = vnid;
524			buf->d_pino = node->vnid;
525			buf->d_reclen = bufContent;
526			strcpy(buf->d_name, filename);
527
528			bufsize -= bufContent;
529			buf = (struct dirent *)((char *) buf + bufContent);
530
531			(*num)++;
532		}
533
534		free(filename);
535
536		if (*num == max)
537			return B_OK;
538	}
539
540	return B_OK;
541}
542
543// fs_free_dircookie()
544//
545extern int fs_free_dircookie(fs_nspace *ns, fs_node *node, btCookie *cookie)
546{
547//dprintf("fs_free_dircookie()\n");
548	if (cookie)
549	{
550		btEmptyLPBCache(cookie);
551		free(cookie);
552	}
553
554	return B_OK;
555}
556
557// fs_rstat()
558//
559extern int fs_rstat(fs_nspace *ns, fs_node *node, struct stat *st)
560{
561	status_t result;
562
563//dprintf("fs_rstat()\n");
564	if ((result = btStat(ns, node->vnid, st)) < B_OK)
565		return result;
566
567	st->st_dev = ns->nsid;
568	return B_OK;
569}
570
571// fs_mount()
572//
573extern int fs_mount(nspace_id nsid, const char *devname, ulong flags, struct mount_bt_params *parms, size_t len, fs_nspace **data, vnode_id *vnid)
574{
575	status_t result;
576	fs_nspace *ns;
577	fs_node *rootNode;
578	struct stat st;
579
580	result = EINVAL;
581
582dprintf("fs_mount running\n");
583	// If we didn't receive any mount parameters, we won't know the server to connect to,
584	// or the folder to be shared.
585	if (parms == NULL)
586		return EINVAL;
587
588	// Initialize the ksocket library for kernel-based network communication.
589#ifndef BONE_VERSION
590	if ((result = ksocket_init()) < B_OK)
591		return result;
592#else
593	if (!bone_init())
594		return ENETUNREACH;
595#endif
596
597dprintf("net initialized\n");
598	// Allocate our private file system information block.
599	ns = (fs_nspace *) malloc(sizeof(fs_nspace));
600	ns->nsid = nsid;
601	ns->dnlcRoot = NULL;
602
603	// Copy over the parameters specified, including the server, folder, user and group
604	// IDs, and so on.
605	ns->params.serverIP = parms->serverIP;
606	ns->params.server = strdup(parms->server);
607	ns->params.export = strdup(parms->export);
608	ns->params.uid = parms->uid;
609	ns->params.gid = parms->gid;
610	ns->params.hostname = strdup(parms->hostname);
611	ns->params.folder = strdup(parms->folder);
612
613	// The password is a binary token that is of fixed length, but can contain null characters.
614	// A strdup() therefore won't capture the entire string.  Manually copying the data is the
615	// only reliable way.
616	strcpy(ns->params.user, parms->user);
617	memcpy(ns->params.password, parms->password, BT_AUTH_TOKEN_LENGTH);
618	ns->params.password[BT_AUTH_TOKEN_LENGTH] = 0;
619
620	// Now connect to the remote server.  The socket returned will be used for all future
621	// communication with that server.
622	ns->s = btConnect(ns, ns->params.serverIP, BT_TCPIP_PORT);
623	if (ns->s != INVALID_SOCKET)
624	{
625		dprintf("connection established\n");
626		ns->xid = 0;
627
628		btRPCInit(ns);
629
630		// Create a semaphore for exclusive access to the vnode list when we need to scan
631		// for a particular vnode.
632		ns->sem = create_sem(1, "VNode List Semaphore");
633		if (ns->sem > 0)
634		{
635			dprintf("vnode semaphore created\n");
636			if (initManagedData(&ns->dnlcData))
637			{
638				dprintf("managed data struct initialized\n");
639				// The system should own this semaphore.
640				set_sem_owner(ns->sem, B_SYSTEM_TEAM);
641
642				// Allocate the root node.
643				rootNode = (fs_node *) malloc(sizeof(fs_node));
644				rootNode->next = NULL;
645
646				// Let the server know we are mounting the exported folder.  This operation
647				// will return the root node file handle.
648				result = btMount(ns, ns->params.export, ns->params.user, ns->params.password, &rootNode->vnid);
649				if (result == B_OK)
650				{
651					ns->rootid = rootNode->vnid;
652					*vnid = ns->rootid;
653
654					result = new_vnode(nsid, *vnid, rootNode);
655					if (result == B_OK)
656					{
657						*data = ns;
658						ns->first = rootNode;
659						notify_listener(B_DEVICE_MOUNTED, ns->nsid, 0, 0, 0, NULL);
660						dprintf("Mount successful.");
661						return B_OK;
662					}
663				}
664				else dprintf("Mount failed %d\n", result);
665
666				// We've failed.  The first step is to free the root node we allocated, and
667				// release the semaphore we created as well.
668				free(rootNode);
669				closeManagedData(&ns->dnlcData);
670			}
671
672			delete_sem(ns->sem);
673		}
674
675		// Disconnect from the server.
676		btDisconnect(ns);
677		btRPCClose(ns);
678	}
679	else
680		result = EHOSTUNREACH;
681
682	// De-allocate our private file system structure and all the string space we duplicated
683	// on startup.
684	free(ns->params.hostname);
685	free(ns->params.folder);
686	free(ns->params.export);
687	free(ns->params.server);
688	free(ns);
689
690#ifndef BONE_VERSION
691	ksocket_cleanup();
692#else
693	bone_cleanup();
694#endif
695
696	return result;
697}
698
699extern int fs_unmount(fs_nspace *ns)
700{
701//dprintf("fs_unmount()\n");
702	notify_listener(B_DEVICE_UNMOUNTED, ns->nsid, 0, 0, 0, NULL);
703
704	btDisconnect(ns);
705	btRPCClose(ns);
706
707	free(ns->params.hostname);
708	free(ns->params.folder);
709	free(ns->params.export);
710	free(ns->params.server);
711
712	while (acquire_sem(ns->sem) == B_INTERRUPTED);
713
714	while (ns->first)
715	{
716		fs_node *next = ns->first->next;
717		free(ns->first);
718		ns->first = next;
719	}
720
721	release_sem(ns->sem);
722	delete_sem(ns->sem);
723
724	ns->s = INVALID_SOCKET;
725	free(ns);
726
727#ifndef BONE_VERSION
728	ksocket_cleanup();
729#else
730	bone_cleanup();
731#endif
732
733	return B_OK;
734}
735
736// fs_rfsstat()
737//
738extern int fs_rfsstat(fs_nspace *ns, struct fs_info *info)
739{
740	bt_fsinfo fsinfo;
741	int result;
742
743//dprintf("fs_rfsstat()\n");
744	result = btGetFSInfo(ns, ns->rootid, &fsinfo);
745	if (result == B_OK)
746	{
747		info->dev = ns->nsid;
748		info->root = ns->rootid;
749		info->flags = B_FS_IS_SHARED | B_FS_IS_PERSISTENT |
750			B_FS_HAS_MIME | B_FS_HAS_ATTR | B_FS_HAS_QUERY;
751		info->block_size = fsinfo.blockSize;
752		info->io_size = 8192;
753		info->total_blocks = fsinfo.totalBlocks;
754		info->free_blocks = fsinfo.freeBlocks;
755		info->total_nodes = 0;
756		info->free_nodes = 0;
757		strcpy(info->device_name, "BeServed Shared Volume");
758		strcpy(info->volume_name, ns->params.folder);
759		strcpy(info->fsh_name, "");
760	}
761
762	return result;
763}
764
765// fs_open()
766//
767extern int fs_open(fs_nspace *ns, fs_node *node, int omode, fs_file_cookie **cookie)
768{
769	struct stat st;
770	status_t result;
771
772//dprintf("fs_open()\n");
773	if ((result = btStat(ns, node->vnid, &st)) < B_OK)
774		return result;
775
776	if (S_ISDIR(st.st_mode))
777	{
778		*cookie = NULL;
779		return B_OK; // permit opening of directories
780	}
781
782	*cookie = (fs_file_cookie *) malloc(sizeof(fs_file_cookie));
783	(*cookie)->omode = omode;
784	(*cookie)->original_size = st.st_size;
785	(*cookie)->st = st;
786
787	return B_OK;
788}
789
790// fs_close()
791//
792extern int fs_close(fs_nspace *ns, fs_node *node, fs_file_cookie *cookie)
793{
794//dprintf("fs_close()\n");
795	if (cookie && (cookie->omode & O_RDWR || cookie->omode & O_WRONLY))
796		return notify_listener(B_STAT_CHANGED, ns->nsid, 0, 0, node->vnid, NULL);
797
798	return B_OK;
799}
800
801// fs_free_cookie()
802//
803extern int fs_free_cookie(fs_nspace *ns, fs_node *node, fs_file_cookie *cookie)
804{
805//dprintf("fs_free_cookie()\n");
806	if (cookie)
807		free(cookie);
808
809	return B_OK;
810}
811
812// fs_read()
813//
814extern int fs_read(fs_nspace *ns, fs_node *node, fs_file_cookie *cookie, off_t pos, void *buf, size_t *len)
815{
816//dprintf("fs_read()\n");
817	if (!len)
818		return B_ERROR;
819
820	// If we weren't given proper file system or vnode pointers, something is awry.
821	if (!ns || !node || !buf)
822		return B_ERROR;
823
824	// Do not permit reading directories.
825	if (!cookie)
826		return EISDIR;
827
828	*len = btRead(ns, node->vnid, pos, *len, buf);
829
830	return B_OK;
831}
832
833extern int fs_write(fs_nspace *ns, fs_node *node, fs_file_cookie *cookie, off_t pos, const void *buf, size_t *len)
834{
835//dprintf("fs_write()\n");
836	// Do not permit writing directories.
837	if (!cookie)
838		return EISDIR;
839
840	if (cookie->omode & O_APPEND)
841		pos += cookie->original_size;
842
843	*len = btWrite(ns, node->vnid, pos, *len, buf);
844	btStat(ns, node->vnid, &cookie->st);
845
846	return B_OK;
847}
848
849extern int fs_wstat(fs_nspace *ns, fs_node *node, struct stat *st, long mask)
850{
851	int error;
852
853//dprintf("fs_wstat()\n");
854	error = btWStat(ns, node->vnid, st, mask);
855	if (error != B_OK)
856		return error;
857
858	return notify_listener(B_STAT_CHANGED, ns->nsid, 0, 0, node->vnid, NULL);
859}
860
861extern int fs_wfsstat(fs_nspace *ns, struct fs_info *info, long mask)
862{
863//dprintf("fs_wfsstat()\n");
864	return B_OK;
865}
866
867extern int fs_create(fs_nspace *ns, fs_node *dir, const char *name, int omode, int perms, vnode_id *vnid,
868					fs_file_cookie **cookie)
869{
870	struct stat st;
871	status_t result;
872	int error;
873
874//dprintf("fs_create()\n");
875	result = btLookup(ns, dir->vnid, name, vnid, &st);
876
877	if (result == B_OK)
878	{
879		void *dummy;
880		create_node(ns, dir->vnid, name, *vnid, false);
881
882		if ((result = get_vnode(ns->nsid, *vnid, (void **) &dummy)) < B_OK)
883			return result;
884
885		if (S_ISDIR(st.st_mode))
886			return EISDIR;
887
888		if (omode & O_EXCL)
889			return EEXIST;
890
891		if (omode & O_TRUNC)
892			if ((result = btTruncate(ns, *vnid, 0L)) < B_OK)
893				return result;
894
895		*cookie = (fs_file_cookie *) malloc(sizeof(fs_file_cookie));
896		(*cookie)->omode = omode;
897		(*cookie)->original_size = st.st_size;
898		(*cookie)->st = st;
899
900		return B_OK;
901	}
902	else if (result != ENOENT)
903		return result;
904	else
905	{
906		uint8 *replyBuf;
907		int32 status;
908		fs_node *newNode;
909
910		if (!(omode & O_CREAT))
911			return ENOENT;
912
913		error = btCreate(ns, dir->vnid, name, vnid, omode, perms, &st);
914		if (error != B_OK)
915			return error;
916
917		newNode = create_node(ns, dir->vnid, name, *vnid, true);
918
919		*cookie = (fs_file_cookie *) malloc(sizeof(fs_file_cookie));
920		(*cookie)->omode = omode;
921		(*cookie)->original_size = st.st_size;
922		(*cookie)->st = st;
923
924		return notify_listener(B_ENTRY_CREATED, ns->nsid, dir->vnid, 0, *vnid, name);
925	}
926}
927
928extern int fs_unlink(fs_nspace *ns, fs_node *dir, const char *name)
929{
930	fs_node *newNode;
931	fs_node *dummy;
932	struct stat st;
933	vnode_id vnid;
934	int error;
935
936//dprintf("fs_unlink()\n");
937	error = btLookup(ns, dir->vnid, name, &vnid, &st);
938	if (error != B_OK)
939		return error;
940
941	create_node(ns, dir->vnid, name, vnid, false);
942
943	error = get_vnode(ns->nsid, vnid, (void **) &dummy);
944	if (error != B_OK)
945		return error;
946
947	if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
948		return EISDIR;
949
950	error = remove_vnode(ns->nsid, vnid);
951	if (error != B_OK)
952		return error;
953
954	error = put_vnode(ns->nsid, vnid);
955	if (error != B_OK)
956		return error;
957
958	error = btUnlink(ns, dir->vnid, name);
959	if (error != B_OK)
960		return error;
961
962	return notify_listener(B_ENTRY_REMOVED, ns->nsid, dir->vnid, 0, vnid, name);
963}
964
965extern int fs_remove_vnode(fs_nspace *ns, fs_node *node, char r)
966{
967//dprintf("fs_remove_vnode()\n");
968	remove_node(ns, node->vnid);
969	return B_OK;
970}
971
972// fs_secure_vnode()
973//
974// Description:  Given a node, this function determines whether the user has the
975// required permission to access it.  This is currently unimplemented in BFS, and
976// we consequently dismiss it here as well.
977//
978extern int fs_secure_vnode(fs_nspace *ns, fs_node *node)
979{
980//dprintf("fs_secure_vnode()\n");
981	return B_OK;
982}
983
984extern int fs_mkdir(fs_nspace *ns, fs_node *dir, const char *name, int perms)
985{
986	fs_node *newNode;
987	struct stat st;
988	vnode_id vnid;
989	int error;
990
991//dprintf("fs_mkdir()\n");
992	error = btLookup(ns, dir->vnid, name, &vnid, &st);
993	if (error == B_OK)
994	{
995		void *dummy;
996		error = get_vnode(ns->nsid, vnid, (void **) &dummy);
997		if (error != B_OK)
998			return error;
999
1000		return EEXIST;
1001	}
1002	else if (error != ENOENT)
1003		return error;
1004
1005	error = btCreateDir(ns, dir->vnid, name, perms | S_IFDIR, &vnid, &st);
1006	if (error != B_OK)
1007		return error;
1008
1009	create_node(ns, dir->vnid, name, vnid, false);
1010	return notify_listener(B_ENTRY_CREATED, ns->nsid, dir->vnid, 0, vnid, name);
1011}
1012
1013extern int fs_rename(fs_nspace *ns, fs_node *olddir, const char *oldname, fs_node *newdir, const char *newname)
1014{
1015	struct stat st;
1016	fs_node *oldNode;
1017	vnode_id vnid;
1018	int error;
1019
1020//dprintf("fs_rename()\n");
1021	error = btLookup(ns, newdir->vnid, newname, &vnid, &st);
1022	if (error == B_OK)
1023	{
1024		if (S_ISREG(st.st_mode))
1025			error = fs_unlink(ns, newdir, newname);
1026		else
1027			error = fs_rmdir(ns, newdir, newname);
1028
1029		if (error != B_OK)
1030			return error;
1031	}
1032
1033	error = btLookup(ns, olddir->vnid, oldname, &vnid, &st);
1034	if (error != B_OK)
1035		return error;
1036
1037	error = btRename(ns, olddir->vnid, oldname, newdir->vnid, newname);
1038	if (error != B_OK)
1039		return error;
1040
1041	rename_node(ns, olddir->vnid, oldname, newdir->vnid, newname, vnid);
1042
1043	return notify_listener(B_ENTRY_MOVED, ns->nsid, olddir->vnid, newdir->vnid, vnid, newname);
1044}
1045
1046extern int fs_rmdir(fs_nspace *ns, fs_node *dir, const char *name)
1047{
1048	fs_node *newNode;
1049	fs_node *dummy;
1050	vnode_id vnid;
1051	struct stat st;
1052	int error;
1053
1054//dprintf("fs_rmdir()\n");
1055	error = btLookup(ns, dir->vnid, name, &vnid, &st);
1056	if (error != B_OK)
1057		return error;
1058
1059	create_node(ns, dir->vnid, name, vnid, false);
1060
1061	error = get_vnode(ns->nsid, vnid, (void **) &dummy);
1062	if (error != B_OK)
1063		return error;
1064
1065	if (!S_ISDIR(st.st_mode))
1066		return ENOTDIR;
1067
1068	error = remove_vnode(ns->nsid, vnid);
1069	if (error != B_OK)
1070		return error;
1071
1072	error = put_vnode(ns->nsid, vnid);
1073	if (error != B_OK)
1074		return error;
1075
1076	error = btDeleteDir(ns, dir->vnid, name);
1077	if (error != B_OK)
1078		return error;
1079
1080	return notify_listener(B_ENTRY_REMOVED, ns->nsid, dir->vnid, 0, vnid, name);
1081}
1082
1083extern int fs_readlink(fs_nspace *ns, fs_node *node, char *buf, size_t *bufsize)
1084{
1085//dprintf("fs_readlink()\n");
1086	return btReadLink(ns, node->vnid, buf, bufsize);
1087}
1088
1089extern int fs_symlink(fs_nspace *ns, fs_node *dir, const char *name, const char *path)
1090{
1091	struct stat st;
1092	vnode_id vnid;
1093	int error;
1094
1095//dprintf("fs_symlink()\n");
1096	error = btLookup(ns, dir->vnid, name, &vnid, &st);
1097	if (error == B_OK)
1098	{
1099		void *dummy;
1100		if ((error = get_vnode(ns->nsid, vnid, (void **) &dummy)) < B_OK)
1101			return error;
1102
1103		return EEXIST;
1104	}
1105	else if (error != ENOENT)
1106		return error;
1107
1108	error = btSymLink(ns, dir->vnid, name, path);
1109	if (error != B_OK)
1110		return error;
1111
1112	error = btLookup(ns, dir->vnid, name, &vnid, &st);
1113	if (error != B_OK)
1114		return error;
1115
1116	create_node(ns, dir->vnid, name, vnid, false);
1117
1118	error = notify_listener(B_ENTRY_CREATED, ns->nsid, dir->vnid, 0, vnid, name);
1119	return error;
1120}
1121
1122// fs_access()
1123//
1124// Description:  This function implements the access() system call.
1125//
1126int	fs_access(void *ns, void *node, int mode)
1127{
1128//dprintf("fs_access()\n");
1129	return B_OK;
1130}
1131
1132int fs_read_attrib(fs_nspace *ns, fs_node *node, const char *name, int type, void *buf, size_t *len, off_t pos)
1133{
1134	int bytes;
1135
1136	// Automatically deny reading Tracker attributes.
1137//dprintf("fs_read_attrib()\n");
1138	if (strncmp(name, "_trk/", 5) == 0)
1139	{
1140		errno = B_ENTRY_NOT_FOUND;
1141		return -1;
1142	}
1143
1144	bytes = btReadAttrib(ns, node->vnid, name, type, buf, *len, pos);
1145	if (bytes == -1)
1146		return -1;
1147
1148	*len = bytes;
1149	return B_OK;
1150}
1151
1152int	fs_write_attrib(fs_nspace *ns, fs_node *node, const char *name, int type, const void *buf, size_t *len, off_t pos)
1153{
1154	int bytes;
1155
1156//dprintf("fs_write_attrib()\n");
1157	// Automatically deny reading Tracker attributes.
1158	if (strncmp(name, "_trk/", 5) == 0)
1159	{
1160		errno = B_ENTRY_NOT_FOUND;
1161		return -1;
1162	}
1163
1164	bytes = btWriteAttrib(ns, node->vnid, name, type, buf, *len, pos);
1165	if (bytes == -1)
1166		return -1;
1167
1168	*len = bytes;
1169	notify_listener(B_ATTR_CHANGED, ns->nsid, 0, 0, node->vnid, node->name);
1170	return B_OK;
1171}
1172
1173// fs_open_attribdir()
1174//
1175extern int fs_open_attribdir(fs_nspace *ns, fs_node *node, btCookie **cookie)
1176{
1177	struct stat st;
1178	int error;
1179
1180//dprintf("fs_open_attribdir()\n");
1181	// Do a basic stat() on this file to verify we indeed have a valid file.
1182	// If we cannot obtain this information, we have an even bigger problem.
1183	if ((error = btStat(ns, node->vnid, &st)) < B_OK)
1184		return error;
1185
1186	// Allocate and initialize the cookie.
1187	*cookie = (btCookie *) malloc(sizeof(btCookie));
1188	if (!*cookie)
1189		return ENOMEM;
1190
1191	memset((*cookie)->opaque, 0, BT_COOKIE_SIZE);
1192	(*cookie)->lpbCache = false;
1193	(*cookie)->eof = false;
1194
1195	return B_OK;
1196}
1197
1198// fs_close_attribdir()
1199//
1200extern int fs_close_attribdir(fs_nspace *ns, fs_node *node, btCookie *cookie)
1201{
1202//dprintf("fs_close_attribdir()\n");
1203	btEmptyLPBCache(cookie);
1204	return B_OK;
1205}
1206
1207// fs_rewind_attribdir()
1208//
1209extern int fs_rewind_attribdir(fs_nspace *ns, fs_node *node, btCookie *cookie)
1210{
1211//dprintf("fs_rewind_attribdir()\n");
1212	if (cookie)
1213	{
1214		memset(cookie->opaque, 0, BT_COOKIE_SIZE);
1215		btEmptyLPBCache(cookie);
1216		cookie->eof = false;
1217	}
1218
1219	return B_OK;
1220}
1221
1222extern int fs_read_attribdir(fs_nspace *ns, fs_node *node, btCookie *cookie, long *num, struct dirent *buf, size_t bufsize)
1223{
1224	status_t result;
1225	char *attrName;
1226	long max;
1227
1228//dprintf("fs_read_attribdir()\n");
1229	max = *num;
1230	*num = 0;
1231
1232	// Cause the directory to be read.
1233	while ((result = btReadAttribDir(ns, node->vnid, &attrName, cookie)) == B_OK)
1234	{
1235		if (strncmp(attrName, "_trk/", 5) != 0)
1236		{
1237			if (bufsize < 2 * (sizeof(dev_t) + sizeof(ino_t)) + sizeof(unsigned short) + strlen(attrName) + 1)
1238				return B_OK;
1239
1240			buf->d_dev = 0;
1241			buf->d_pdev = 0;
1242			buf->d_ino = 0;
1243			buf->d_pino = 0;
1244			buf->d_reclen = 2 * (sizeof(dev_t) + sizeof(ino_t)) + sizeof(unsigned short) + strlen(attrName) + 1;
1245			strcpy(buf->d_name, attrName);
1246
1247			bufsize -= buf->d_reclen;
1248			buf = (struct dirent *)((char *) buf + buf->d_reclen);
1249
1250			(*num)++;
1251		}
1252
1253		free(attrName);
1254
1255		if (*num == max)
1256			break;
1257	}
1258
1259	return B_OK;
1260}
1261
1262// fs_free_attribdircookie()
1263//
1264extern int fs_free_attribdircookie(fs_nspace *ns, btCookie *cookie)
1265{
1266//dprintf("fs_free_attribdircookie()\n");
1267//	if (cookie)
1268//	{
1269//		btEmptyLPBCache(cookie);
1270//		free(cookie);
1271//	}
1272
1273	return B_OK;
1274}
1275
1276// fs_remove_attrib()
1277//
1278extern int fs_remove_attrib(fs_nspace *ns, fs_node *node, const char *name)
1279{
1280//dprintf("fs_remove_attrib()\n");
1281	// Automatically deny removing Tracker attributes.
1282	if (strncmp(name, "_trk/", 5) == 0)
1283	{
1284		errno = B_ENTRY_NOT_FOUND;
1285		return -1;
1286	}
1287
1288	return btRemoveAttrib(ns, node->vnid, name);
1289}
1290
1291// fs_stat_attrib()
1292//
1293extern int fs_stat_attrib(fs_nspace *ns, fs_node *node, const char *name, struct attr_info *buf)
1294{
1295	buf->type = 0;
1296	buf->size = 0;
1297
1298//dprintf("fs_stat_attrib()\n");
1299	// Automatically deny reading Tracker attributes.
1300	if (strncmp(name, "_trk/", 5) == 0)
1301	{
1302		errno = B_ENTRY_NOT_FOUND;
1303		return -1;
1304	}
1305
1306	return btStatAttrib(ns, node->vnid, name, buf);
1307}
1308
1309// fs_open_indexdir()
1310//
1311extern int fs_open_indexdir(fs_nspace *ns, btCookie **cookie)
1312{
1313//dprintf("fs_open_indexdir()\n");
1314	// Allocate and initialize the cookie.
1315	*cookie = (btCookie *) malloc(sizeof(btCookie));
1316	memset ((*cookie)->opaque, 0, BT_COOKIE_SIZE);
1317	(*cookie)->eof = false;
1318
1319	return B_OK;
1320}
1321
1322// fs_close_indexdir()
1323//
1324extern int fs_close_indexdir(fs_nspace *ns, btCookie *cookie)
1325{
1326//dprintf("fs_close_indexdir()\n");
1327	return B_OK;
1328}
1329
1330// fs_rewind_indexdir()
1331//
1332extern int fs_rewind_indexdir(fs_nspace *ns, btCookie *cookie)
1333{
1334//dprintf("fs_rewind_indexdir()\n");
1335	memset(cookie->opaque, 0, BT_COOKIE_SIZE);
1336	cookie->eof = false;
1337	return B_OK;
1338}
1339
1340// fs_read_indexdir()
1341//
1342extern int fs_read_indexdir(fs_nspace *ns, btCookie *cookie, long *num, struct dirent *buf, size_t bufsize)
1343{
1344	status_t result;
1345	char indexName[100];
1346	long max;
1347
1348//dprintf("fs_read_indexdir()\n");
1349	max = *num;
1350	*num = 0;
1351
1352	// Cause the directory to be read.
1353	while ((result = btReadIndexDir(ns, indexName, sizeof(indexName) - 1, cookie)) == B_OK)
1354	{
1355		if (bufsize < 2 * (sizeof(dev_t) + sizeof(ino_t)) + sizeof(unsigned short) + strlen(indexName) + 1)
1356			return B_OK;
1357
1358		buf->d_dev = 0;
1359		buf->d_pdev = 0;
1360		buf->d_ino = 0;
1361		buf->d_pino = 0;
1362		buf->d_reclen = 2 * (sizeof(dev_t) + sizeof(ino_t)) + sizeof(unsigned short) + strlen(indexName) + 1;
1363		strcpy(buf->d_name, indexName);
1364
1365		bufsize -= buf->d_reclen;
1366		buf = (struct dirent *)((char *) buf + buf->d_reclen);
1367
1368		(*num)++;
1369
1370		if (*num == max)
1371			return B_OK;
1372	}
1373
1374	return B_OK;
1375}
1376
1377// fs_free_indexdircookie()
1378//
1379extern int fs_free_indexdircookie(fs_nspace *ns, btCookie *cookie)
1380{
1381//dprintf("fs_free_indexdircookie()\n");
1382	if (cookie)
1383		free(cookie);
1384
1385	return B_OK;
1386}
1387
1388extern int fsCreateIndex(fs_nspace *ns, const char *name, int type, int flags)
1389{
1390//dprintf("fs_create_index()\n");
1391	return btCreateIndex(ns, name, type, flags);
1392}
1393
1394extern int fsRemoveIndex(fs_nspace *ns, const char *name)
1395{
1396//dprintf("fs_remove_index()\n");
1397	return btRemoveIndex(ns, name);
1398}
1399
1400extern int fsStatIndex(fs_nspace *ns, const char *name, struct index_info *buf)
1401{
1402//dprintf("fs_stat_index()\n");
1403	buf->type = 0;
1404	buf->size = 0;
1405	buf->modification_time = 0;
1406	buf->creation_time = 0;
1407	buf->uid = 0;
1408	buf->gid = 0;
1409
1410	return btStatIndex(ns, name, buf);
1411}
1412
1413extern int fsOpenQuery(fs_nspace *ns, const char *query, ulong flags, port_id port, long token, btQueryCookie **cookie)
1414{
1415//dprintf("fs_openquery()\n");
1416	// Allocate and initialize the cookie.
1417	*cookie = (btQueryCookie *) malloc(sizeof(btQueryCookie));
1418	memset ((*cookie)->opaque, 0, BT_COOKIE_SIZE);
1419	(*cookie)->query = strdup(query);
1420
1421	return B_OK;
1422}
1423
1424extern int fsCloseQuery(fs_nspace *ns, btQueryCookie *cookie)
1425{
1426//dprintf("fs_closequery()\n");
1427	return B_OK;
1428}
1429
1430extern int fsReadQuery(fs_nspace *ns, btQueryCookie *cookie, long *num, struct dirent *buf, size_t bufsize)
1431{
1432	char *filename;
1433	vnode_id vnid, parent;
1434	long max, bufContent;
1435
1436//dprintf("fs_readquery()\n");
1437	max = *num;
1438	*num = 0;
1439
1440	while (btReadQuery(ns, &filename, &vnid, &parent, cookie) == B_OK)
1441	{
1442		bufContent = 2 * (sizeof(dev_t) + sizeof(ino_t)) + sizeof(unsigned short) + strlen(filename) + 1;
1443		if (bufsize < bufContent)
1444			return B_OK;
1445
1446		buf->d_dev = ns->nsid;
1447		buf->d_pdev = ns->nsid;
1448		buf->d_ino = vnid;
1449		buf->d_pino = parent;
1450		buf->d_reclen = bufContent;
1451		strcpy(buf->d_name, filename);
1452
1453		bufsize -= bufContent;
1454		buf = (struct dirent *)((char *) buf + bufContent);
1455
1456		free(filename);
1457
1458		(*num)++;
1459		if (*num == max)
1460			break;
1461	}
1462
1463	return B_OK;
1464}
1465
1466extern int fsFreeQueryCookie(fs_nspace *ns, btQueryCookie *cookie)
1467{
1468//dprintf("fs_freequerycookie()\n");
1469	if (cookie)
1470	{
1471		if (cookie->query)
1472			free(cookie->query);
1473
1474		free(cookie);
1475	}
1476
1477	return B_OK;
1478}
1479