1// btAdd.cpp : Defines the entry point for the console application.
2//
3
4// To-do:
5//
6// X Better reconnect logic
7// _ Node monitoring
8// _ Don't allow following links outside the shared folder
9// _ Restart of server erases inode-to-filename map
10// _ Ability to reconnect shares at login
11// _ Restricting logon times via a schedule
12// _ Distributed file sharing
13// _ Use a gathered write semaphore per thread, not one global semaphore
14//
15
16// dividing the files:
17// 1.  main(), start/stop service, request thread launching, misc
18// 2.  signal handlers
19// 3.  UDP command thread/handlers
20// 4.  config file parsing
21// 5.  RPC code
22// 6.  actual file handling commands
23// 7.  net file handling commands
24
25// Potential Uses:
26// 1.  Domain server, keeping track of users for logging in
27// 2.  File server, to share BeOS volume files across a network
28// 3.  Document management, to store documents with attributes
29// 4.  Version Control System, to manage multiple versions of documents
30// 5.  General directory server, for local or network settings and information
31
32#include "FindDirectory.h"
33
34#include "betalk.h"
35#include "authentication.h"
36#include "sysdepdefs.h"
37#include "fsproto.h"
38#include "netdb.h"
39
40#include "utime.h"
41#include "ctype.h"
42#include "time.h"
43#include "signal.h"
44#include "stdlib.h"
45#include "syslog.h"
46#include "sys/utsname.h"
47
48#define BT_MAX_THREADS		100
49#define BT_MAX_RETRIES		3
50#define BT_MAX_FILE_SHARES	128
51
52#define BT_MAIN_NAME		"BeServed Daemon"
53#define BT_THREAD_NAME		"BeServed Handler"
54#define BT_HOST_THREAD_NAME	"BeServed Host Publisher"
55#define BT_SIGNATURE		"application/x-vnd.Teldar-BeServed"
56
57#define PATH_ROOT			"/boot"
58#define PATH_DELIMITER		'/'
59
60#ifndef iswhite
61#define iswhite(c)			((c == ' ' || c == '\t'))
62#endif
63
64typedef struct
65{
66	unsigned int type;
67	unsigned int length;
68	char *data;
69} bt_arg_t;
70
71typedef struct btblock
72{
73	vnode_id vnid;
74	off_t pos;
75	int32 len;
76	int32 count;
77	char *buffer;
78	struct btblock *next;
79	struct btblock *prev;
80} bt_block;
81
82typedef struct userRights
83{
84	char *user;
85	int rights;
86	bool isGroup;
87	struct userRights *next;
88} bt_user_rights;
89
90typedef struct session
91{
92	int socket;
93	unsigned int s_addr;
94	thread_id handlerID;
95
96	bool killed;
97
98	// What share did the client connect to?  And when?
99	int share;
100	int rights;
101	time_t logon;
102
103	// Buffered write support.
104	bt_block *rootBlock;
105	sem_id blockSem;
106	int32 blockVar;
107
108	char ioBuffer[BT_MAX_IO_BUFFER + 9];
109	char attrBuffer[BT_MAX_ATTR_BUFFER + 1];
110	char pathBuffer[B_PATH_NAME_LENGTH + 1];
111} bt_session_t;
112
113typedef void (*bt_net_func)(bt_session_t *, unsigned int, int, bt_arg_t *);
114
115typedef struct dirCommand
116{
117	unsigned char command;
118	bt_net_func handler;
119} bt_command_t;
120
121typedef struct btnode
122{
123	vnode_id vnid;
124	char name[B_FILE_NAME_LENGTH];
125	int refCount;
126	bool invalid;
127	struct btnode *next;
128	struct btnode *prev;
129	struct btnode *parent;
130} bt_node;
131
132typedef struct fileShare
133{
134	char path[B_PATH_NAME_LENGTH];
135	char name[B_FILE_NAME_LENGTH];
136
137	bool used;
138	bool readOnly;
139
140	// What rights does each user have?
141	bt_user_rights *rights;
142	int security;
143
144	struct fileShare *next;
145} bt_fileShare_t;
146
147bt_node *rootNode = NULL;
148
149
150int main(int argc, char *argv[]);
151void daemonInit();
152bool dateCheck();
153int32 btSendHost(void *data);
154int getSharedResources(char *buffer, int bufSize);
155void getHostInfo(bt_hostinfo *info);
156int getHostUsers(char *buffer);
157void startService();
158void endService(int sig);
159void restartService();
160void initSessions();
161void initShares();
162void freeFileHandles();
163void freeFileShares();
164void getFileShare(const char *buffer);
165void getShareProperty(const char *buffer);
166void getGrant(const char *buffer);
167void getAuthenticate(const char *buffer);
168void addUserRights(char *share, char *user, int rights, bool isGroup);
169int getToken();
170int receiveRequest(bt_session_t *session);
171void handleRequest(bt_session_t *session, unsigned int xid, unsigned char command, int argc, bt_arg_t argv[]);
172void launchThread(int client, struct sockaddr_in *addr);
173int tooManyConnections(unsigned int s_addr);
174void sendErrorToClient(int client, unsigned int xid, int error);
175void getArguments(bt_session_t *session, bt_inPacket *packet, unsigned char command);
176int32 requestThread(void *data);
177
178bt_node *btGetNodeFromVnid(vnode_id vnid);
179void btAddHandle(vnode_id dir_vnid, vnode_id file_vnid, char *name);
180void btRemoveHandle(vnode_id vnid);
181void btPurgeNodes(vnode_id vnid);
182void btRemoveNode(bt_node *deadNode);
183bool btIsAncestorNode(vnode_id vnid, bt_node *node);
184char *btGetLocalFileName(char *path, vnode_id vnid);
185bt_node *btFindNode(bt_node *parent, char *fileName);
186char *btGetSharePath(char *shareName);
187int btGetShareId(char *shareName);
188int btGetShareIdByPath(char *path);
189void btGetRootPath(vnode_id vnid, char *path);
190void btLock(sem_id semaphore, int32 *atomic);
191void btUnlock(sem_id semaphore, int32 *atomic);
192
193int btPreMount(bt_session_t *session, char *shareName);
194int btMount(bt_session_t *session, char *shareName, char *user, char *password, vnode_id *vnid);
195int btGetFSInfo(char *rootPath, fs_info *fsInfo);
196int btLookup(char *pathBuf, vnode_id dir_vnid, char *fileName, vnode_id *file_vnid);
197int btStat(char *pathBuf, vnode_id vnid, struct stat *st);
198int btReadDir(char *pathBuf, vnode_id dir_vnid, DIR **dir, vnode_id *file_vnid, char *filename, struct stat *st);
199int32 btRead(char *pathBuf, vnode_id vnid, off_t pos, int32 len, char *buffer);
200int32 btWrite(bt_session_t *session, vnode_id vnid, off_t pos, int32 len, int32 totalLen, char *buffer);
201int btCreate(char *pathBuf, vnode_id dir_vnid, char *name, int omode, int perms, vnode_id *file_vnid);
202int btTruncate(char *pathBuf, vnode_id vnid, int64 len);
203int btCreateDir(char *pathBuf, vnode_id dir_vnid, char *name, int perms, vnode_id *file_vnid, struct stat *st);
204int btDeleteDir(char *pathBuf, vnode_id vnid, char *name);
205int btRename(char *pathBuf, vnode_id old_vnid, char *oldName, vnode_id new_vnid, char *newName);
206int btUnlink(char *pathBuf, vnode_id vnid, char *name);
207int btReadLink(char *pathBuf, vnode_id vnid, char *buffer, int length);
208int btSymLink(char *pathBuf, vnode_id vnid, char *name, char *dest);
209int btWStat(char *pathBuf, vnode_id vnid, long mask, int32 mode, int32 uid, int32 gid, int64 size, int32 atime, int32 mtime);
210int btReadAttrib(char *pathBuf, vnode_id vnid, char *name, int32 dataType, void *buffer, int32 pos, int32 len);
211int btWriteAttrib(char *pathBuf, vnode_id vnid, char *name, int32 dataType, void *buffer, int32 pos, int32 len);
212int btReadAttribDir(char *pathBuf, vnode_id vnid, DIR **dir, char *attrName);
213int btRemoveAttrib(char *pathBuf, vnode_id vnid, char *name);
214int btStatAttrib(char *pathBuf, vnode_id vnid, char *name, struct attr_info *info);
215int btReadIndexDir(char *rootPath, DIR **dir, char *indexName);
216int btCreateIndex(char *rootPath, char *name, int type, int flags);
217int btRemoveIndex(char *rootPath, char *name);
218int btStatIndex(char *rootPath, char *name, struct index_info *info);
219int btReadQuery(char *rootPath, DIR **dir, char *query, char *fileName, vnode_id *vnid, vnode_id *parent);
220int btCommit(bt_session_t *session, vnode_id vnid);
221
222bt_block *btGetWriteBlock(bt_session_t *session, vnode_id vnid);
223void btInsertWriteBlock(bt_session_t *session, bt_block *block);
224void btRemoveWriteBlock(bt_session_t *session, bt_block *block);
225
226void netbtPreMount(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
227void netbtMount(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
228void netbtFSInfo(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
229void netbtLookup(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
230void netbtStat(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
231void netbtReadDir(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
232void netbtRead(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
233void netbtWrite(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
234void netbtCreate(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
235void netbtTruncate(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
236void netbtCreateDir(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
237void netbtDeleteDir(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
238void netbtRename(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
239void netbtUnlink(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
240void netbtReadLink(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
241void netbtSymLink(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
242void netbtWStat(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
243void netbtReadAttrib(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
244void netbtWriteAttrib(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
245void netbtReadAttribDir(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
246void netbtRemoveAttrib(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
247void netbtStatAttrib(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
248void netbtReadIndexDir(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
249void netbtCreateIndex(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
250void netbtRemoveIndex(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
251void netbtStatIndex(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
252void netbtReadQuery(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
253void netbtCommit(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
254void netbtPrintJobNew(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
255void netbtPrintJobData(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
256void netbtPrintJobCommit(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
257void netbtAuthenticate(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
258void netbtQuit(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[]);
259
260bt_session_t sessions[BT_MAX_THREADS];
261bt_fileShare_t fileShares[BT_MAX_FILE_SHARES];
262char tokBuffer[B_PATH_NAME_LENGTH], *tokPtr;
263bool running = true;
264int server;
265unsigned int authServerIP;
266thread_id hostThread;
267sem_id handleSem;
268int32 handleVar;
269
270bt_command_t dirCommands[] =
271{
272	{ BT_CMD_PREMOUNT,			netbtPreMount },
273	{ BT_CMD_MOUNT,				netbtMount },
274	{ BT_CMD_FSINFO,			netbtFSInfo },
275	{ BT_CMD_LOOKUP,			netbtLookup },
276	{ BT_CMD_STAT,				netbtStat },
277	{ BT_CMD_READDIR,			netbtReadDir },
278	{ BT_CMD_READ,				netbtRead },
279	{ BT_CMD_WRITE,				netbtWrite },
280	{ BT_CMD_CREATE,			netbtCreate },
281	{ BT_CMD_TRUNCATE,			netbtTruncate },
282	{ BT_CMD_MKDIR,				netbtCreateDir },
283	{ BT_CMD_RMDIR,				netbtDeleteDir },
284	{ BT_CMD_RENAME,			netbtRename },
285	{ BT_CMD_UNLINK,			netbtUnlink },
286	{ BT_CMD_READLINK,			netbtReadLink },
287	{ BT_CMD_SYMLINK,			netbtSymLink },
288	{ BT_CMD_WSTAT,				netbtWStat },
289	{ BT_CMD_READATTRIB,		netbtReadAttrib },
290	{ BT_CMD_WRITEATTRIB,		netbtWriteAttrib },
291	{ BT_CMD_READATTRIBDIR,		netbtReadAttribDir },
292	{ BT_CMD_REMOVEATTRIB,		netbtRemoveAttrib },
293	{ BT_CMD_STATATTRIB,		netbtStatAttrib },
294	{ BT_CMD_READINDEXDIR,		netbtReadIndexDir },
295	{ BT_CMD_CREATEINDEX,		netbtCreateIndex },
296	{ BT_CMD_REMOVEINDEX,		netbtRemoveIndex },
297	{ BT_CMD_STATINDEX,			netbtStatIndex },
298	{ BT_CMD_READQUERY,			netbtReadQuery },
299	{ BT_CMD_COMMIT,			netbtCommit },
300	{ BT_CMD_PRINTJOB_NEW,		netbtPrintJobNew },
301	{ BT_CMD_PRINTJOB_DATA,		netbtPrintJobData },
302	{ BT_CMD_PRINTJOB_COMMIT,	netbtPrintJobCommit },
303	{ BT_CMD_AUTHENTICATE,		netbtAuthenticate },
304	{ BT_CMD_QUIT,				netbtQuit },
305	{ 0,						NULL }
306};
307
308char *keywords[] =
309{
310	"share",
311	"as",
312	"set",
313	"read",
314	"write",
315	"read-write",
316	"promiscuous",
317	"on",
318	"to",
319	"authenticate",
320	"with",
321	"group",
322	"printer",
323	"print",
324	"is",
325	"spooled",
326	"device",
327	"type",
328	NULL
329};
330
331/*----------------------------------------------------------------
332class BeServedServer : public BApplication
333{
334	thread_id appThread;
335	bool running;
336
337	public:
338		BeServedServer(const char *signature);
339
340		virtual void ReadyToRun();
341		virtual bool QuitRequested();
342};
343
344BeServedServer::BeServedServer(const char *signature)
345	: BApplication(signature)
346{
347}
348
349void BeServedServer::ReadyToRun()
350{
351	running = true;
352	appThread = spawn_thread(appMain, BT_MAIN_NAME, B_NORMAL_PRIORITY, this);
353	resume_thread(appThread);
354}
355
356bool BeServedServer::QuitRequested()
357{
358	status_t result;
359
360	if (!BApplication::QuitRequested())
361		return false;
362
363	running = false;
364	wait_for_thread(appThread, &result);
365	return true;
366}
367
368int main(int argc, char *argv[])
369{
370	BeServedServer app(BT_SIGNATURE);
371	app.Run();
372	return 0;
373}
374----------------------------------------------------------------------*/
375
376int main(int argc, char *argv[])
377{
378	daemonInit();
379
380	initSessions();
381	initShares();
382	signal(SIGINT, endService);
383	signal(SIGTERM, endService);
384	signal(SIGHUP, restartService);
385	signal(SIGPIPE, SIG_IGN);
386
387	if ((handleSem = create_sem(0, "File Handle Semaphore")) > B_OK)
388	{
389		hostThread = spawn_thread(btSendHost, BT_HOST_THREAD_NAME, B_NORMAL_PRIORITY, 0);
390		resume_thread(hostThread);
391
392		// Run the daemon.  We will not return until the service is being stopped.
393		startService();
394
395		if (hostThread > 0)
396			kill_thread(hostThread);
397
398		delete_sem(handleSem);
399	}
400
401	return 0;
402}
403
404bool dateCheck()
405{
406	struct stat st;
407	time_t curTime;
408
409	time(&curTime);
410	if (curTime > 1012537700)
411		return false;
412
413	if (stat("/boot/home/config/servers/beserved_server", &st) == 0)
414		if (curTime < st.st_ctime || curTime > st.st_ctime + 7776000)
415			return false;
416
417	return true;
418}
419
420void daemonInit()
421{
422	int i;
423
424	// Cause the parent task to terminate, freeing the terminal.
425	if (fork() != 0)
426		exit(0);
427
428	// In the child process, become the session leader.
429	setsid();
430
431	// Now fork again, causing the first child to exit, since the session
432	// leader can be assigned a controlling terminal under SVR4.
433	signal(SIGHUP, SIG_IGN);
434	if (fork() != 0)
435		exit(0);
436
437	// Change to the root directory, since if we hold on to a working
438	// folder that was in a mounted file system, that file system cannot
439	// be unmounted.
440	chdir("/");
441
442	// Reset the file creation mask to zero to eliminate the inherited value.
443	umask(0);
444
445	// Close open file descriptors.  Since we can't know how many of a
446	// potentially unlimited value can be open, just close the first 64
447	// and assume that will be enough.
448	for (i = 0; i < 64; i++)
449		close(i);
450
451	// Open the syslog.
452	openlog("beserved_server", LOG_PID, LOG_DAEMON);
453}
454
455void restartService()
456{
457	bt_fileShare_t *oldShares;
458	int i;
459
460	// Delay all mounting and other file system operations.
461	btLock(handleSem, &handleVar);
462
463	// Copy existing share data.
464	oldShares = (bt_fileShare_t *) malloc(sizeof(bt_fileShare_t) * BT_MAX_FILE_SHARES);
465	if (oldShares)
466	{
467		for (i = 0; i < BT_MAX_FILE_SHARES; i++)
468			memcpy(&oldShares[i], &fileShares[i], sizeof(bt_fileShare_t));
469
470		// Reload the share data.
471		initShares();
472
473		// Now loop through the old file shares.  For each one, check if the same
474		// path exists in the new shares.
475		for (i = 0; i < BT_MAX_FILE_SHARES; i++)
476			if (oldShares[i].used)
477			{
478				int share = btGetShareIdByPath(oldShares[i].path);
479				if (share == -1)
480				{
481					for (i = 0; i < BT_MAX_THREADS; i++)
482						if (sessions[i].share == i)
483							sessions[i].killed = true;
484				}
485				else if (share != i)
486					for (i = 0; i < BT_MAX_THREADS; i++)
487						if (sessions[i].share == i)
488							sessions[i].share = share;
489			}
490
491		free(oldShares);
492	}
493
494	// Resume normal operation.
495	btUnlock(handleSem, &handleVar);
496}
497
498int32 btSendHost(void *data)
499{
500	bt_request request;
501	bt_hostinfo info;
502	struct sockaddr_in serverAddr, clientAddr;
503	char buffer[4096];
504	int server, addrLen, bufLen, replyLen;
505
506	buffer[0] = 0;
507	bufLen = sizeof(buffer);
508
509	server = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
510	if (server == INVALID_SOCKET)
511		return -1;
512
513	memset(&serverAddr, 0, sizeof(serverAddr));
514	serverAddr.sin_family = AF_INET;
515	serverAddr.sin_port = htons(BT_QUERYHOST_PORT);
516	serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
517
518	// Bind that socket to the address constructed above.
519	if (bind(server, (struct sockaddr *) &serverAddr, sizeof(serverAddr)))
520		return -1;
521
522	while (running)
523	{
524		addrLen = sizeof(struct sockaddr_in);
525		replyLen = 0;
526		if (recvfrom(server, (char *) &request, sizeof(request), 0, (struct sockaddr *) &clientAddr, &addrLen) <= 0)
527			continue;
528
529		switch (request.command)
530		{
531			case BT_REQ_HOST_PROBE:
532				gethostname(buffer, bufLen);
533				break;
534
535			case BT_REQ_SHARE_PROBE:
536				replyLen = getSharedResources(buffer, sizeof(buffer));
537				break;
538
539			case BT_REQ_HOST_INFO:
540				getHostInfo(&info);
541				memcpy(buffer, &info, sizeof(bt_hostinfo));
542				replyLen = sizeof(bt_hostinfo);
543				break;
544
545			case BT_REQ_HOST_USERS:
546				replyLen = getHostUsers(buffer);
547				break;
548
549			case BT_REQ_AUTH_TYPES:
550				break;
551		}
552
553		// If no reply length has been specified, calculate it now by taking the
554		// length of the buffer.
555		if (replyLen == 0)
556			replyLen = strlen(buffer);
557
558		sendto(server, buffer, replyLen, 0, (struct sockaddr *) &clientAddr, addrLen);
559	}
560
561	// Close the socket.  Technically, I believe we should call shutdown()
562	// first, but the BeOS header file socket.h indicates that this
563	// function is not currently working.  It is present but may not have
564	// any effect.
565	shutdown(server, 2);
566	closesocket(server);
567	return 0;
568}
569
570// getSharedResources()
571//
572int getSharedResources(char *buffer, int bufSize)
573{
574	bt_resource resource;
575	int i, bufPos = 0;
576
577	// If the supplied buffer can't hold at least two resource structures, one
578	// for a shared resource and one to terminate the list, then don't bother
579	// building the list.
580	if (bufSize < 2 * sizeof(bt_resource))
581		return 0;
582
583	for (i = 0; i < BT_MAX_FILE_SHARES; i++)
584		if (fileShares[i].used)
585		{
586			// If this is the last resource structure that will fit in the
587			// buffer, then don't add any more into the list.
588			if (bufPos + sizeof(bt_resource) >= bufSize)
589				break;
590
591			// Fill out the resource structure.
592			resource.type = B_HOST_TO_LENDIAN_INT32(BT_SHARED_FOLDER);
593			strcpy(resource.name, fileShares[i].name);
594
595			// Copy the resource structure into the buffer at the current offset.
596			memcpy(buffer + bufPos, &resource, sizeof(bt_resource));
597			bufPos += sizeof(bt_resource);
598		}
599
600	// Copy the null terminating structure.
601	resource.type = B_HOST_TO_LENDIAN_INT32(BT_SHARED_NULL);
602	resource.name[0] = 0;
603	memcpy(buffer + bufPos, &resource, sizeof(bt_resource));
604	bufPos += sizeof(bt_resource);
605
606	return bufPos;
607}
608
609// getHostInfo()
610//
611void getHostInfo(bt_hostinfo *info)
612{
613	system_info sysinfo;
614	struct utsname uts;
615	char buf[100];
616	int i;
617
618	struct cpuMap
619	{
620		int cpuType;
621		char *cpuName;
622	} cpuList[] =
623	{
624		{ B_CPU_PPC_601, "PowerPC 601" },
625		{ B_CPU_PPC_603, "PowerPC 603" },
626		{ B_CPU_PPC_603e, "PowerPC 603e" },
627		{ B_CPU_PPC_604, "PowerPC 604" },
628		{ B_CPU_PPC_604e, "PowerPC 604e" },
629		{ B_CPU_PPC_750, "PowerPC 750" },
630		{ B_CPU_PPC_686, "PowerPC 686" },
631
632		{ B_CPU_INTEL_X86, "Intel 80x86" },
633		{ B_CPU_INTEL_PENTIUM, "Intel Pentium" },
634		{ B_CPU_INTEL_PENTIUM75, "Intel Pentium" },
635		{ B_CPU_INTEL_PENTIUM_486_OVERDRIVE, "Intel 486 Overdrive" },
636		{ B_CPU_INTEL_PENTIUM_MMX, "Intel Pentium MMX" },
637		{ B_CPU_INTEL_PENTIUM_MMX_MODEL_4, "Intel Pentium MMX" },
638		{ B_CPU_INTEL_PENTIUM_MMX_MODEL_8, "Intel Pentium MMX" },
639		{ B_CPU_INTEL_PENTIUM75_486_OVERDRIVE, "Intel 486 Overdrive" },
640		{ B_CPU_INTEL_PENTIUM_PRO, "Intel Pentium Pro" },
641		{ B_CPU_INTEL_PENTIUM_II, "Intel Pentium II" },
642		{ B_CPU_INTEL_PENTIUM_II_MODEL_3, "Intel Pentium II" },
643		{ B_CPU_INTEL_PENTIUM_II_MODEL_5, "Intel Pentium II" },
644		{ B_CPU_INTEL_CELERON, "Intel Celeron" },
645		{ B_CPU_INTEL_PENTIUM_III, "Intel Pentium III" },
646
647		{ B_CPU_AMD_X86, "AMD x86" },
648		{ B_CPU_AMD_K5_MODEL0, "AMD K5" },
649		{ B_CPU_AMD_K5_MODEL1, "AMD K5" },
650		{ B_CPU_AMD_K5_MODEL2, "AMD K5" },
651		{ B_CPU_AMD_K5_MODEL3, "AMD K5" },
652		{ B_CPU_AMD_K6_MODEL6, "AMD K6" },
653		{ B_CPU_AMD_K6_MODEL7, "AMD K6" },
654		{ B_CPU_AMD_K6_MODEL8, "AMD K6" },
655		{ B_CPU_AMD_K6_2, "AMD K6-2" },
656		{ B_CPU_AMD_K6_MODEL9, "AMD K6" },
657		{ B_CPU_AMD_K6_III, "AMD K6-3" },
658		{ B_CPU_AMD_ATHLON_MODEL1, "AMD Athlon" },
659
660		{ B_CPU_CYRIX_X86, "Cyrix x86" },
661		{ B_CPU_CYRIX_GXm, "Cyrix GXm" },
662		{ B_CPU_CYRIX_6x86MX, "Cyrix 6x86MX" },
663
664		{ B_CPU_IDT_X86, "IDT x86" },
665		{ B_CPU_IDT_WINCHIP_C6, "IDT WinChip C6" },
666		{ B_CPU_IDT_WINCHIP_2, "IDT WinChip 2" },
667
668		{ B_CPU_RISE_X86, "Rise x86" },
669		{ B_CPU_RISE_mP6, "Rise mP6" },
670
671		{ 0, NULL }
672	};
673
674	uname(&uts);
675	get_system_info(&sysinfo);
676
677	strcpy(info->system, uts.sysname);
678	strcat(info->system, " ");
679	strcat(info->system, uts.release);
680	strcpy(info->beServed, "BeServed 1.2.5");
681
682	info->cpus = B_HOST_TO_LENDIAN_INT32(sysinfo.cpu_count);
683	info->maxConnections = B_HOST_TO_LENDIAN_INT32(BT_MAX_THREADS);
684
685	strcpy(info->platform, "Unknown");
686	for (i = 0; cpuList[i].cpuType; i++)
687		if (cpuList[i].cpuType == sysinfo.cpu_type)
688		{
689			strcpy(info->platform, cpuList[i].cpuName);
690			break;
691		}
692
693	sprintf(buf, " at %ldMHz", (long) (sysinfo.cpu_clock_speed / 1000000));
694	strcat(info->platform, buf);
695
696	info->connections = 0;
697	for (i = 0; i < BT_MAX_THREADS; i++)
698		if (sessions[i].socket != INVALID_SOCKET)
699			info->connections++;
700
701	info->connections = B_HOST_TO_LENDIAN_INT32(info->connections);
702}
703
704// getHostUsers()
705//
706int getHostUsers(char *buffer)
707{
708	char addr[20];
709	int i, len, bufSize;
710
711	// Initialize the buffer to be empty.
712	buffer[0] = 0;
713	bufSize = 0;
714
715	for (i = 0; i < BT_MAX_THREADS; i++)
716		if (sessions[i].socket != INVALID_SOCKET)
717		{
718			uint8 *s_addr = (uint8 *) sessions[i].s_addr;
719			sprintf(addr, "%d.%d.%d.%d", s_addr[0], s_addr[1], s_addr[2], s_addr[3]);
720			len = strlen(buffer);
721			strcpy(&buffer[len > 0 ? len + 1 : 0], addr);
722			bufSize += len + 1;
723		}
724
725	buffer[bufSize++] = 0;
726	return bufSize;
727}
728
729// initSessions()
730//
731void initSessions()
732{
733	int i;
734
735	for (i = 0; i < BT_MAX_THREADS; i++)
736	{
737		sessions[i].socket = INVALID_SOCKET;
738		sessions[i].handlerID = 0;
739		sessions[i].killed = false;
740		sessions[i].rights = 0;
741	}
742}
743
744void initShares()
745{
746	FILE *fp;
747	char path[B_PATH_NAME_LENGTH], buffer[512];
748	int i, length;
749
750	for (i = 0; i < BT_MAX_FILE_SHARES; i++)
751	{
752		fileShares[i].name[0] = 0;
753		fileShares[i].path[0] = 0;
754		fileShares[i].used = false;
755		fileShares[i].readOnly = true;
756		fileShares[i].security = BT_AUTH_NONE;
757		fileShares[i].rights = NULL;
758		fileShares[i].next = NULL;
759	}
760
761	find_directory(B_COMMON_SETTINGS_DIRECTORY, 0, false, path, sizeof(path));
762	strcat(path, "/BeServed-Settings");
763
764	fp = fopen(path, "r");
765	if (fp)
766	{
767		while (fgets(buffer, sizeof(buffer) - 1, fp))
768		{
769			length = strlen(buffer);
770			if (length <= 1 || buffer[0] == '#')
771				continue;
772
773			if (buffer[length - 1] == '\n')
774				buffer[--length] = 0;
775
776			if (strncmp(buffer, "share ", 6) == 0)
777				getFileShare(buffer);
778			else if (strncmp(buffer, "set ", 4) == 0)
779				getShareProperty(buffer);
780			else if (strncmp(buffer, "grant ", 6) == 0)
781				getGrant(buffer);
782			else if (strncmp(buffer, "authenticate ", 13) == 0)
783				getAuthenticate(buffer);
784		}
785
786		fclose(fp);
787	}
788}
789
790void getFileShare(const char *buffer)
791{
792	struct stat st;
793	char path[B_PATH_NAME_LENGTH], share[MAX_NAME_LENGTH + 1], *folder;
794	int i, tok;
795
796	// Skip over SHARE command.
797	tokPtr = (char *) buffer + (6 * sizeof(char));
798
799	tok = getToken();
800	if (tok != BT_TOKEN_STRING)
801		return;
802
803	strcpy(path, tokBuffer);
804	tok = getToken();
805	if (tok != BT_TOKEN_AS)
806		return;
807
808	tok = getToken();
809	if (tok != BT_TOKEN_STRING)
810		return;
811
812	strcpy(share, tokBuffer);
813
814	// Now verify that the share name specified has not already been
815	// used to share another path.
816	folder = btGetSharePath(share);
817	if (folder)
818	{
819		syslog(LOG_WARNING, "%s already defined as %s\n", share, folder);
820		return;
821	}
822
823	// Check the path to ensure its validity.
824	if (stat(path, &st) != 0)
825		return;
826
827	for (i = 0; i < BT_MAX_FILE_SHARES; i++)
828		if (!fileShares[i].used)
829		{
830			syslog(LOG_INFO, "Defining %s as %s\n", share, path);
831			strcpy(fileShares[i].name, share);
832			strcpy(fileShares[i].path, path);
833			fileShares[i].used = true;
834			return;
835		}
836
837	syslog(LOG_WARNING, "Share %s could not be defined (too many shares)\n", share);
838}
839
840void getShareProperty(const char *buffer)
841{
842	char share[B_FILE_NAME_LENGTH + 1];
843	int tok, shareId;
844
845	// Skip over SET command.
846	tokPtr = (char *) buffer + (4 * sizeof(char));
847
848	tok = getToken();
849	if (tok != BT_TOKEN_STRING)
850		return;
851
852	strcpy(share, tokBuffer);
853
854	// Get the index of the share referred to.  If the named share cannot be
855	// found, then abort.
856	shareId = btGetShareId(share);
857	if (shareId < 0)
858		return;
859
860	tok = getToken();
861	if (tok == BT_TOKEN_READWRITE)
862	{
863		fileShares[shareId].readOnly = false;
864		syslog(LOG_INFO, "%s permits writing\n", share);
865	}
866}
867
868void getGrant(const char *buffer)
869{
870	char share[MAX_NAME_LENGTH + 1];
871	int tok, rights;
872	bool isGroup = false;
873
874	// Skip over GRANT command.
875	tokPtr = (char *) buffer + (6 * sizeof(char));
876	rights = 0;
877
878	do
879	{
880		tok = getToken();
881		if (tok == BT_TOKEN_READ)
882		{
883			rights |= BT_RIGHTS_READ;
884			tok = getToken();
885		}
886		else if (tok == BT_TOKEN_WRITE)
887		{
888			rights |= BT_RIGHTS_WRITE;
889			tok = getToken();
890		}
891	} while (tok == BT_TOKEN_COMMA);
892
893	if (tok != BT_TOKEN_ON)
894		return;
895
896	tok = getToken();
897	if (tok != BT_TOKEN_STRING)
898		return;
899
900	strcpy(share, tokBuffer);
901	tok = getToken();
902	if (tok != BT_TOKEN_TO)
903		return;
904
905	tok = getToken();
906	if (tok == BT_TOKEN_GROUP)
907	{
908		isGroup = true;
909		tok = getToken();
910	}
911
912	if (tok != BT_TOKEN_STRING)
913		return;
914
915	addUserRights(share, tokBuffer, rights, isGroup);
916}
917
918void getAuthenticate(const char *buffer)
919{
920	struct hostent *ent;
921	int i, tok;
922
923	// Skip over AUTHENTICATE command.
924	tokPtr = (char *) buffer + (13 * sizeof(char));
925
926	tok = getToken();
927	if (tok != BT_TOKEN_WITH)
928		return;
929
930	tok = getToken();
931	if (tok != BT_TOKEN_STRING)
932		return;
933
934	// Look up address for given host.
935	ent = gethostbyname(tokBuffer);
936	if (ent == NULL)
937	{
938		syslog(LOG_ERR, "Authentication server %s is unavailable.\n", tokBuffer);
939		endService(0);
940	}
941
942	authServerIP = ntohl(*((unsigned int *) ent->h_addr));
943
944	// Make all file shares use BeSure authentication.
945	for (i = 0; i < BT_MAX_FILE_SHARES; i++)
946		fileShares[i].security = BT_AUTH_BESURE;
947
948	syslog(LOG_INFO, "Using authentication server at %s\n", tokBuffer);
949}
950
951void addUserRights(char *share, char *user, int rights, bool isGroup)
952{
953	bt_user_rights *ur;
954	int shareId;
955
956	shareId = btGetShareId(share);
957	if (shareId < 0)
958		return;
959
960	ur = (bt_user_rights *) malloc(sizeof(bt_user_rights));
961	if (ur)
962	{
963		ur->user = (char *) malloc(strlen(user) + 1);
964		if (ur->user)
965		{
966			strcpy(ur->user, user);
967			ur->rights = rights;
968			ur->isGroup = isGroup;
969			ur->next = fileShares[shareId].rights;
970			fileShares[shareId].rights = ur;
971		}
972		else
973			free(ur);
974	}
975}
976
977int getToken()
978{
979	bool quoted = false;
980
981	tokBuffer[0] = 0;
982	while (*tokPtr && iswhite(*tokPtr))
983		tokPtr++;
984
985	if (*tokPtr == ',')
986	{
987		tokPtr++;
988		return BT_TOKEN_COMMA;
989	}
990	else if (*tokPtr == '\"')
991	{
992		quoted = true;
993		tokPtr++;
994	}
995
996	if (isalnum(*tokPtr) || *tokPtr == '/')
997	{
998		int i = 0;
999		while (isalnum(*tokPtr) || isValid(*tokPtr) || (quoted && *tokPtr == ' '))
1000			if (i < B_PATH_NAME_LENGTH)
1001				tokBuffer[i++] = *tokPtr++;
1002			else
1003				tokPtr++;
1004
1005		tokBuffer[i] = 0;
1006
1007		if (!quoted)
1008			for (i = 0; keywords[i]; i++)
1009				if (strcasecmp(tokBuffer, keywords[i]) == 0)
1010					return ++i;
1011
1012		if (quoted)
1013			if (*tokPtr != '\"')
1014				return BT_TOKEN_ERROR;
1015			else
1016				tokPtr++;
1017
1018		return BT_TOKEN_STRING;
1019	}
1020
1021	return BT_TOKEN_ERROR;
1022}
1023
1024void startService()
1025{
1026	struct sockaddr_in serverAddr, clientAddr;
1027	int client, addrLen;
1028	int flags;
1029
1030	// Store the length of the socket addressing structure for accept().
1031	addrLen = sizeof(struct sockaddr_in);
1032
1033	// Initialize the server address structure.
1034	memset(&serverAddr, 0, sizeof(serverAddr));
1035	serverAddr.sin_port = htons(BT_TCPIP_PORT);
1036	serverAddr.sin_family = AF_INET;
1037	serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
1038
1039	// Create a new socket to receive incoming requests.
1040	server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1041	if (server == INVALID_SOCKET)
1042		return;
1043
1044	// Set the socket option to reuse the current address in case it was
1045	// in use by a prior version of the service that has not yet relinquished
1046	// the socket.
1047	flags = 1;
1048	setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
1049
1050	// Bind that socket to the address constructed above.
1051	if (bind(server, (struct sockaddr *) &serverAddr, sizeof(serverAddr)))
1052		return;
1053
1054	// Listen for incoming connections.
1055	if (listen(server, 5))
1056		return;
1057
1058	// Continually accept incoming connections.  When one is found,
1059	// fire off a handler thread to accept commands.
1060	while (running)
1061	{
1062		client = accept(server, (struct sockaddr *) &clientAddr, &addrLen);
1063		if (client != INVALID_SOCKET)
1064			launchThread(client, &clientAddr);
1065	}
1066
1067	// Close the socket.  Technically, I believe we should call shutdown()
1068	// first, but the BeOS header file socket.h indicates that this
1069	// function is not currently working.  It is present but may not have
1070	// any effect.
1071	shutdown(server, 2);
1072	closesocket(server);
1073	server = INVALID_SOCKET;
1074}
1075
1076void endService(int sig)
1077{
1078	// Close the syslog.
1079	closelog();
1080
1081	if (hostThread > 0)
1082		kill_thread(hostThread);
1083
1084	if (handleSem > 0)
1085		delete_sem(handleSem);
1086
1087	freeFileHandles();
1088	freeFileShares();
1089
1090	signal(SIGINT, SIG_DFL);
1091	signal(SIGTERM, SIG_DFL);
1092	signal(SIGHUP, SIG_DFL);
1093	signal(SIGPIPE, SIG_DFL);
1094	exit(0);
1095}
1096
1097// freeFileHandles()
1098//
1099void freeFileHandles()
1100{
1101	bt_node *nextNode, *curNode = rootNode;
1102
1103	while (curNode)
1104	{
1105		nextNode = curNode->next;
1106		free(curNode);
1107		curNode = nextNode;
1108	}
1109}
1110
1111// freeFileShares()
1112//
1113void freeFileShares()
1114{
1115	bt_user_rights *ur, *nextUr;
1116	int i;
1117
1118	for (i = 0; i < BT_MAX_FILE_SHARES; i++)
1119		for (ur = fileShares[i].rights; ur; )
1120		{
1121			nextUr = ur->next;
1122			if (ur->user)
1123				free(ur->user);
1124
1125			free(ur);
1126			ur = nextUr;
1127		}
1128}
1129
1130// launchThread()
1131//
1132void launchThread(int client, struct sockaddr_in *addr)
1133{
1134	int i, retry, found;
1135
1136	// We need to find an available session for this connection.  We don't
1137	// want to create threads willy nilly until we bring down the OS, so we
1138	// establish a pool of available sessions (threads) from which we must
1139	// allocate with every request.
1140	found = FALSE;
1141	for (retry = 0; retry < BT_MAX_RETRIES && !found; retry++)
1142	{
1143		for (i = 0; i < BT_MAX_THREADS; i++)
1144			if (sessions[i].socket == INVALID_SOCKET)
1145			{
1146				found = TRUE;
1147				sessions[i].socket = client;
1148				sessions[i].s_addr = addr->sin_addr.s_addr;
1149				sessions[i].handlerID =
1150					spawn_thread(requestThread, BT_THREAD_NAME, B_NORMAL_PRIORITY, &sessions[i]);
1151				resume_thread(sessions[i].handlerID);
1152				break;
1153			}
1154
1155		if (!found)
1156			snooze(100000);
1157	}
1158
1159	if (!found)
1160	{
1161		sendErrorToClient(client, 0, EBUSY);
1162		shutdown(client, 2);
1163		closesocket(client);
1164	}
1165}
1166
1167int32 requestThread(void *data)
1168{
1169	bt_session_t *session = (bt_session_t *) data;
1170//	int flags;
1171
1172	if (!session)
1173		return 0;
1174
1175	// Ensure that this connection remains alive.  If a periodic message (handled by the OS)
1176	// fails, then blocked socket calls are interrupted and return with a ESIGPIPE error.
1177//	flags = 1;
1178//	setsockopt(server, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags));
1179
1180	if ((session->blockSem = create_sem(0, "Gathered Write Semaphore")) > B_OK)
1181	{
1182		session->rootBlock = NULL;
1183		while (!session->killed && receiveRequest(session));
1184		delete_sem(session->blockSem);
1185	}
1186
1187	shutdown(session->socket, 2);
1188	closesocket(session->socket);
1189	session->socket = INVALID_SOCKET;
1190	return 0;
1191}
1192
1193int receiveRequest(bt_session_t *session)
1194{
1195	bt_inPacket packet;
1196	char signature[20], *buffer;
1197	unsigned char command;
1198	int client, sigLen;
1199	int32 length;
1200
1201	client = session->socket;
1202
1203	// Read the BeTalk RPC header.
1204	sigLen = strlen(BT_RPC_SIGNATURE);
1205	if (btRecvMsg(client, signature, sigLen, 0) == -1)
1206		return 0;
1207//	recv(client, &verHi, sizeof(verHi), 0);
1208//	recv(client, &verLo, sizeof(verLo), 0);
1209
1210	signature[sigLen] = 0;
1211	if (strcmp(signature, BT_RPC_SIGNATURE))
1212		return 0;
1213
1214	// Read in the rest of the packet.
1215	if (btRecvMsg(client, &length, sizeof(int32), 0) == -1)
1216		return 0;
1217
1218	length = B_LENDIAN_TO_HOST_INT32(length);
1219	if (length == 0 || length > BT_RPC_MAX_PACKET_SIZE)
1220		return 0;
1221
1222	buffer = (char *) malloc(length + 1);
1223	if (!buffer)
1224		return 0;
1225
1226	if (btRecvMsg(client, buffer, length, 0) == -1)
1227	{
1228		free(buffer);
1229		return 0;
1230	}
1231
1232	buffer[length] = 0;
1233	packet.buffer = buffer;
1234	packet.length = length;
1235	packet.offset = 0;
1236
1237	// Read the transmission ID and command.
1238	command = btRPCGetChar(&packet);
1239	getArguments(session, &packet, command);
1240	free(buffer);
1241	return (command != BT_CMD_QUIT && command != BT_CMD_PREMOUNT);
1242}
1243
1244void getArguments(bt_session_t *session, bt_inPacket *packet, unsigned char command)
1245{
1246	bt_arg_t args[MAX_COMMAND_ARGS];
1247	int i, client;
1248	bool error;
1249	unsigned char argc, terminator;
1250	int32 xid;
1251
1252	error = false;
1253	client = session->socket;
1254	argc = btRPCGetChar(packet);
1255	if (argc > MAX_COMMAND_ARGS)
1256		return;
1257
1258	for (i = 0; i < argc && !error; i++)
1259	{
1260		args[i].type = btRPCGetInt32(packet);
1261		args[i].data = btRPCGetNewString(packet);
1262		if (args[i].data == NULL)
1263			error = true;
1264	}
1265
1266	if (!error)
1267	{
1268		xid = btRPCGetInt32(packet);
1269		terminator = btRPCGetChar(packet);
1270		if (terminator == BT_CMD_TERMINATOR)
1271			handleRequest(session, xid, command, argc, args);
1272	}
1273	else
1274		sendErrorToClient(session->socket, 0, EINVAL);
1275
1276	while (--i >= 0)
1277		free(args[i].data);
1278}
1279
1280void handleRequest(bt_session_t *session, unsigned int xid, unsigned char command, int argc, bt_arg_t argv[])
1281{
1282	int i;
1283
1284	for (i = 0; dirCommands[i].handler; i++)
1285		if (command == dirCommands[i].command)
1286		{
1287			(*dirCommands[i].handler)(session, xid, argc, argv);
1288			return;
1289		}
1290
1291	sendErrorToClient(session->socket, xid, EINVAL);
1292}
1293
1294void sendErrorToClient(int client, unsigned int xid, int error)
1295{
1296	bt_outPacket packet;
1297	btRPCCreateAck(&packet, xid, error);
1298	btRPCSendAck(client, &packet);
1299}
1300
1301int btRecvMsg(int sock, void *data, int dataLen, int flags)
1302{
1303	int bytesRead = 0;
1304	do
1305	{
1306		int bytes = btRecv(sock, (char *) data + bytesRead, dataLen - bytesRead, flags);
1307		if (bytes == -1)
1308			return -1;
1309
1310		bytesRead += bytes;
1311	} while (bytesRead < dataLen);
1312
1313	return bytesRead;
1314}
1315
1316// btRecv()
1317//
1318int btRecv(int sock, void *data, int dataLen, int flags)
1319{
1320	int bytes;
1321
1322	for (;;)
1323	{
1324		bytes = recv(sock, data, dataLen, flags);
1325		if (bytes == 0)
1326			return -1;
1327		else if (bytes == -1)
1328			if (errno == EINTR)
1329				continue;
1330			else
1331				return -1;
1332		else
1333			break;
1334	}
1335
1336	return bytes;
1337}
1338
1339int btSendMsg(int sock, void *data, int dataLen, int flags)
1340{
1341	int bytesSent = 0;
1342	do
1343	{
1344		int bytes = btSend(sock, (char *) data + bytesSent, dataLen - bytesSent, flags);
1345		if (bytes == -1)
1346			return -1;
1347
1348		bytesSent += bytes;
1349	} while (bytesSent < dataLen);
1350
1351	return bytesSent;
1352}
1353
1354// btSend()
1355//
1356int btSend(int sock, void *data, int dataLen, int flags)
1357{
1358	int bytes;
1359
1360	for (;;)
1361	{
1362		bytes = send(sock, data, dataLen, flags);
1363		if (bytes == -1)
1364			if (errno == EINTR)
1365				continue;
1366			else
1367				return -1;
1368		else
1369			break;
1370	}
1371
1372	return bytes;
1373}
1374
1375void btRPCCreateAck(bt_outPacket *packet, unsigned int xid, int error)
1376{
1377	packet->size = BT_RPC_MIN_PACKET_SIZE;
1378	packet->buffer = (char *) malloc(packet->size);
1379	packet->length = 0;
1380
1381	if (!packet->buffer)
1382		return;
1383
1384	strcpy(packet->buffer, BT_RPC_SIGNATURE);
1385	packet->length += strlen(BT_RPC_SIGNATURE);
1386	btRPCPutInt32(packet, xid);
1387	btRPCPutInt32(packet, 0);
1388	btRPCPutInt32(packet, error);
1389}
1390
1391void btRPCSendAck(int client, bt_outPacket *packet)
1392{
1393	if (packet)
1394		if (packet->buffer)
1395		{
1396			*(int32 *)(&packet->buffer[9]) = packet->length - 13;
1397			btSendMsg(client, packet->buffer, packet->length, 0);
1398			free(packet->buffer);
1399		}
1400}
1401
1402unsigned char btRPCGetChar(bt_inPacket *packet)
1403{
1404	unsigned char value;
1405
1406	if (packet->offset < packet->length)
1407		value = (unsigned char) packet->buffer[packet->offset];
1408	else
1409		value = 0;
1410
1411	packet->offset += sizeof(value);
1412	return value;
1413}
1414
1415unsigned int btRPCGetInt32(bt_inPacket *packet)
1416{
1417	int32 value;
1418
1419	if (packet->offset < packet->length)
1420		value = B_LENDIAN_TO_HOST_INT32(*((int32 *) &packet->buffer[packet->offset]));
1421	else
1422		value = 0;
1423
1424	packet->offset += sizeof(value);
1425	return value;
1426}
1427
1428int64 btRPCGetInt64(bt_inPacket *packet)
1429{
1430	int64 value;
1431
1432	if (packet->offset < packet->length)
1433		value = B_LENDIAN_TO_HOST_INT64(*((int64 *) &packet->buffer[packet->offset]));
1434	else
1435		value = 0;
1436
1437	packet->offset += sizeof(value);
1438	return value;
1439}
1440
1441char *btRPCGetNewString(bt_inPacket *packet)
1442{
1443	char *str;
1444	unsigned int bytes;
1445
1446	if (packet->offset >= packet->length)
1447		return NULL;
1448
1449	bytes = B_LENDIAN_TO_HOST_INT32(*((int32 *) &packet->buffer[packet->offset]));
1450	packet->offset += sizeof(bytes);
1451	if (bytes < 0 || bytes > BT_MAX_IO_BUFFER)
1452		return NULL;
1453
1454	str = (char *) malloc(bytes + 1);
1455	if (!str)
1456		return NULL;
1457
1458	if (bytes > 0)
1459		memcpy(str, &packet->buffer[packet->offset], bytes);
1460
1461	str[bytes] = 0;
1462	packet->offset += bytes;
1463
1464	return str;
1465}
1466
1467int btRPCGetString(bt_inPacket *packet, char *buffer, int length)
1468{
1469	unsigned int bytes;
1470
1471	if (packet->offset >= packet->length)
1472		return ERANGE;
1473
1474	bytes = B_LENDIAN_TO_HOST_INT32(*((int32 *) &packet->buffer[packet->offset]));
1475	packet->offset += sizeof(bytes);
1476	if (bytes < 0 || bytes > BT_MAX_IO_BUFFER)
1477		return ERANGE;
1478
1479	if (length < bytes)
1480		return ERANGE;
1481
1482	if (bytes > 0)
1483		memcpy(buffer, &packet->buffer[packet->offset], bytes);
1484
1485	packet->offset += bytes;
1486	return bytes;
1487}
1488
1489void btRPCGrowPacket(bt_outPacket *packet, int bytes)
1490{
1491	if (packet->length + bytes > packet->size)
1492	{
1493		int growth = ((bytes / BT_RPC_MIN_PACKET_SIZE) + 1) * BT_RPC_MIN_PACKET_SIZE;
1494		packet->buffer = (char *) realloc(packet->buffer, packet->size + growth);
1495		packet->size += growth;
1496	}
1497}
1498
1499void btRPCPutChar(bt_outPacket *packet, char value)
1500{
1501	btRPCGrowPacket(packet, sizeof(value));
1502	packet->buffer[packet->length] = value;
1503	packet->length += sizeof(value);
1504}
1505
1506void btRPCPutInt32(bt_outPacket *packet, int32 value)
1507{
1508	btRPCGrowPacket(packet, sizeof(value));
1509	*(int32 *)(&packet->buffer[packet->length]) = B_HOST_TO_LENDIAN_INT32(value);
1510	packet->length += sizeof(value);
1511}
1512
1513void btRPCPutInt64(bt_outPacket *packet, int64 value)
1514{
1515	btRPCGrowPacket(packet, sizeof(value));
1516	*(int64 *)(&packet->buffer[packet->length]) = B_HOST_TO_LENDIAN_INT64(value);
1517	packet->length += sizeof(value);
1518}
1519
1520void btRPCPutString(bt_outPacket *packet, char *buffer, int length)
1521{
1522	if (packet && buffer)
1523	{
1524		btRPCGrowPacket(packet, sizeof(length) + length);
1525		btRPCPutInt32(packet, length);
1526		memcpy(&packet->buffer[packet->length], buffer, length);
1527		packet->length += length;
1528	}
1529}
1530
1531void btRPCPutBinary(bt_outPacket *packet, void *buffer, int length)
1532{
1533	if (packet && buffer)
1534	{
1535		btRPCGrowPacket(packet, length);
1536		memcpy(&packet->buffer[packet->length], buffer, length);
1537		packet->length += length;
1538	}
1539}
1540
1541int btRPCGetStat(bt_inPacket *packet, struct stat *st)
1542{
1543	st->st_dev = 0;
1544	st->st_nlink = btRPCGetInt32(packet);
1545	st->st_uid = btRPCGetInt32(packet);
1546	st->st_gid = btRPCGetInt32(packet);
1547	st->st_size = btRPCGetInt64(packet);
1548	st->st_blksize = btRPCGetInt32(packet);
1549	st->st_rdev = btRPCGetInt32(packet);
1550	st->st_ino = btRPCGetInt64(packet);
1551	st->st_mode = btRPCGetInt32(packet);
1552	st->st_atime = btRPCGetInt32(packet);
1553	st->st_mtime = btRPCGetInt32(packet);
1554	st->st_ctime = btRPCGetInt32(packet);
1555}
1556
1557void btRPCPutStat(bt_outPacket *packet, struct stat *st)
1558{
1559	if (packet && st)
1560	{
1561		btRPCPutInt32(packet, (int) st->st_nlink);
1562		btRPCPutInt32(packet, (int) st->st_uid);
1563		btRPCPutInt32(packet, (int) st->st_gid);
1564		btRPCPutInt64(packet, (int64) st->st_size);
1565		btRPCPutInt32(packet, (int) st->st_blksize);
1566		btRPCPutInt32(packet, (int) st->st_rdev);
1567		btRPCPutInt64(packet, (int64) st->st_ino);
1568		btRPCPutInt32(packet, (int) st->st_mode);
1569		btRPCPutInt32(packet, (int) st->st_atime);
1570		btRPCPutInt32(packet, (int) st->st_mtime);
1571		btRPCPutInt32(packet, (int) st->st_ctime);
1572	}
1573}
1574
1575////////////////////////////////////////////////////////////////////
1576
1577bt_node *btGetNodeFromVnid(vnode_id vnid)
1578{
1579	register bt_node *curNode = rootNode;
1580
1581	while (curNode && curNode->vnid != vnid)
1582		curNode = curNode->next;
1583
1584	return curNode;
1585}
1586
1587// btAddHandle()
1588//
1589void btAddHandle(vnode_id dir_vnid, vnode_id file_vnid, char *name)
1590{
1591	bt_node *curNode, *dirNode;
1592
1593	// We don't store the references to the current and the parent directory.
1594	if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
1595		return;
1596
1597	btLock(handleSem, &handleVar);
1598
1599	// Obtain the parent node.  If no parent vnid is provided, then this must be
1600	// the root node.  The parent of the root node is NULL, but it should be the
1601	// only node for which this is true.
1602	if (dir_vnid)
1603		dirNode = btGetNodeFromVnid(dir_vnid);
1604	else
1605		dirNode = NULL;
1606
1607	// If a node already exists with the given vnid, then it is either a symbolic
1608	// link or an attempt to add the same node again, such as when mounting or
1609	// walking a directory tree.  If we find a matching vnid whose parent directory
1610	// and name also match, this is a duplicate and can be ignored.
1611	curNode = btGetNodeFromVnid(file_vnid);
1612	if (curNode)
1613		if (curNode->parent == dirNode && strcmp(curNode->name, name) == 0)
1614		{
1615			btUnlock(handleSem, &handleVar);
1616			return;
1617		}
1618
1619	// Allocate a new node.
1620	curNode = (bt_node *) malloc(sizeof(bt_node));
1621	if (curNode == NULL)
1622	{
1623		btUnlock(handleSem, &handleVar);
1624		return;
1625	}
1626
1627	// Copy over the name, vnid, and parent node.  Obtaining the parent
1628	// node requires scanning the list.
1629	strcpy(curNode->name, name);
1630	curNode->refCount = 0;
1631	curNode->invalid = false;
1632	curNode->vnid = file_vnid;
1633	curNode->parent = dirNode;
1634
1635	// Add the node to the head of the list.
1636	curNode->next = rootNode;
1637	curNode->prev = NULL;
1638	if (rootNode)
1639		rootNode->prev = curNode;
1640	rootNode = curNode;
1641
1642	btUnlock(handleSem, &handleVar);
1643}
1644
1645// btRemoveHandle()
1646//
1647void btRemoveHandle(vnode_id vnid)
1648{
1649	bt_node *deadNode;
1650
1651	btLock(handleSem, &handleVar);
1652
1653	// Obtain the node in question.  If no such node exists, then we
1654	// probably have a bad handle.
1655	deadNode = btGetNodeFromVnid(vnid);
1656	btRemoveNode(deadNode);
1657
1658	btUnlock(handleSem, &handleVar);
1659}
1660
1661// btRemoveNode()
1662//
1663void btRemoveNode(bt_node *deadNode)
1664{
1665	if (deadNode)
1666	{
1667		// If this node is the root node, then we need to reset the root node
1668		// to the next node in the list.
1669		if (deadNode == rootNode)
1670			rootNode = deadNode->next;
1671
1672		// Make this entry's predecessor point to its successor.
1673		if (deadNode->prev)
1674			deadNode->prev->next = deadNode->next;
1675
1676		// Make this entry's successor point to its predecessor.
1677		if (deadNode->next)
1678			deadNode->next->prev = deadNode->prev;
1679
1680		// Now deallocate this node.
1681		free(deadNode);
1682	}
1683}
1684
1685// btPurgeNodes()
1686//
1687void btPurgeNodes(vnode_id vnid)
1688{
1689	bt_node *curNode, *nextNode;
1690
1691	btLock(handleSem, &handleVar);
1692
1693	// First loop through, marking this node and all its children as invalid.
1694	curNode = rootNode;
1695	while (curNode)
1696	{
1697		if (curNode->vnid == vnid || btIsAncestorNode(vnid, curNode))
1698			curNode->invalid = true;
1699
1700		curNode = curNode->next;
1701	}
1702
1703	// Now loop through again, removing all invalid nodes.  This prevents removing
1704	// a parent node and all its children being orphaned (with invalid pointers
1705	// back to the destroyed parent).
1706	curNode = rootNode;
1707	while (curNode)
1708		if (curNode->invalid)
1709		{
1710			nextNode = curNode->next;
1711			btRemoveNode(curNode);
1712			curNode = nextNode;
1713		}
1714		else
1715			curNode = curNode->next;
1716
1717	btUnlock(handleSem, &handleVar);
1718}
1719
1720// btIsAncestorNode()
1721//
1722bool btIsAncestorNode(vnode_id vnid, bt_node *node)
1723{
1724	bt_node *curNode = node->parent;
1725
1726	while (curNode)
1727	{
1728		if (curNode->vnid == vnid)
1729			return true;
1730
1731		curNode = curNode->parent;
1732	}
1733
1734	return false;
1735}
1736
1737// btGetLocalFileName()
1738//
1739char *btGetLocalFileName(char *path, vnode_id vnid)
1740{
1741	bt_node *node, *nodeStack[100];
1742	int stackSize;
1743
1744	path[0] = 0;
1745	stackSize = 1;
1746
1747	btLock(handleSem, &handleVar);
1748
1749	node = btGetNodeFromVnid(vnid);
1750	if (node == NULL)
1751	{
1752		btUnlock(handleSem, &handleVar);
1753		return NULL;
1754	}
1755
1756	nodeStack[0] = node;
1757	while ((node = node->parent) != NULL)
1758		nodeStack[stackSize++] = node;
1759
1760	while (--stackSize >= 0)
1761	{
1762		strcat(path, nodeStack[stackSize]->name);
1763		if (stackSize)
1764			strcat(path, "/");
1765	}
1766
1767	btUnlock(handleSem, &handleVar);
1768	return path;
1769}
1770
1771bt_node *btFindNode(bt_node *parent, char *fileName)
1772{
1773	bt_node *node;
1774
1775	btLock(handleSem, &handleVar);
1776
1777	node = rootNode;
1778	while (node)
1779	{
1780		if (node->parent == parent)
1781			if (strcmp(node->name, fileName) == 0)
1782				break;
1783
1784		node = node->next;
1785	}
1786
1787	btUnlock(handleSem, &handleVar);
1788	return node;
1789}
1790
1791char *btGetSharePath(char *shareName)
1792{
1793	int i;
1794
1795	for (i = 0; i < BT_MAX_FILE_SHARES; i++)
1796		if (fileShares[i].used)
1797			if (strcasecmp(fileShares[i].name, shareName) == 0)
1798				return fileShares[i].path;
1799
1800	return NULL;
1801}
1802
1803int btGetShareId(char *shareName)
1804{
1805	int i;
1806
1807	for (i = 0; i < BT_MAX_FILE_SHARES; i++)
1808		if (fileShares[i].used)
1809			if (strcasecmp(fileShares[i].name, shareName) == 0)
1810				return i;
1811
1812	return -1;
1813}
1814
1815int btGetShareIdByPath(char *path)
1816{
1817	int i;
1818
1819	for (i = 0; i < BT_MAX_FILE_SHARES; i++)
1820		if (fileShares[i].used)
1821			if (strcmp(fileShares[i].path, path) == 0)
1822				return i;
1823
1824	return -1;
1825}
1826
1827// btGetRootPath()
1828//
1829void btGetRootPath(vnode_id vnid, char *path)
1830{
1831	bt_node *curNode;
1832
1833	btLock(handleSem, &handleVar);
1834
1835	curNode = btGetNodeFromVnid(vnid);
1836	while (curNode && curNode->parent)
1837		curNode = curNode->parent;
1838
1839	if (curNode)
1840		strcpy(path, curNode->name);
1841	else
1842		path[0] = 0;
1843
1844	btUnlock(handleSem, &handleVar);
1845}
1846
1847void btLock(sem_id semaphore, int32 *atomic)
1848{
1849	int32 previous = atomic_add(atomic, 1);
1850	if (previous >= 1)
1851		while (acquire_sem(semaphore) == B_INTERRUPTED);
1852}
1853
1854void btUnlock(sem_id semaphore, int32 *atomic)
1855{
1856	int32 previous = atomic_add(atomic, -1);
1857	if (previous > 1)
1858		release_sem(semaphore);
1859}
1860
1861////////////////////////////////////////////////////////////////////
1862/*
1863void btNotifyListeners(char *shareName)
1864{
1865	struct sockaddr_in toAddr, fromAddr;
1866	int i;
1867
1868	for (i = 0; i < BT_MAX_THREADS; i++)
1869		if (strcasecmp(sessions[i].share, shareName) == 0)
1870		{
1871			memset(&toAddr, 0, sizeof(toAddr));
1872			toAddr.sin_port = htons(BT_NODE_MONITOR_PORT);
1873			toAddr.sin_family = AF_INET;
1874			toAddr.sin_addr.s_addr = sessions[i].s_addr;
1875
1876			sendto(sock, packet, sizeof(packet), 0, &fromAddr, sizeof(fromAddr));
1877		}
1878}
1879*/
1880////////////////////////////////////////////////////////////////////
1881
1882int btPreMount(bt_session_t *session, char *shareName)
1883{
1884	// Look for the specified share name.  If it can't be found, it must no longer be
1885	// shared on this host.
1886	int shareId = btGetShareId(shareName);
1887	if (shareId < 0)
1888		return ENOENT;
1889
1890	return fileShares[shareId].security;
1891}
1892
1893int btMount(bt_session_t *session, char *shareName, char *user, char *password, vnode_id *vnid)
1894{
1895	bt_user_rights *ur;
1896	struct stat st;
1897	char *path, *groups[MAX_GROUPS_PER_USER];
1898	int i, shareId;
1899	bool authenticated = false;
1900
1901	// Initialize the groups array.  We may need to release the memory later.
1902	for (i = 0; i < MAX_GROUPS_PER_USER; i++)
1903		groups[i] = NULL;
1904
1905	// Look for the specified share name.  If it can't be found, it must no longer be
1906	// shared on this host.
1907	shareId = btGetShareId(shareName);
1908	if (shareId < 0)
1909		return ENOENT;
1910
1911	if (fileShares[shareId].security != BT_AUTH_NONE)
1912	{
1913		// Authenticate the user with name/password
1914		authenticated = authenticateUser(user, password);
1915		if (!authenticated)
1916			return EACCES;
1917
1918		// Does the authenticated user have any rights on this file share?
1919		session->rights = 0;
1920		for (ur = fileShares[shareId].rights; ur; ur = ur->next)
1921			if (!ur->isGroup && strcasecmp(ur->user, user) == 0)
1922				session->rights |= ur->rights;
1923
1924		// Does the authenticated user belong to any groups that have any rights on this
1925		// file share?
1926		getUserGroups(user, groups);
1927		for (ur = fileShares[shareId].rights; ur; ur = ur->next)
1928			if (ur->isGroup)
1929				for (i = 0; i < MAX_GROUPS_PER_USER; i++)
1930					if (groups[i] && strcasecmp(ur->user, groups[i]) == 0)
1931					{
1932						session->rights |= ur->rights;
1933						break;
1934					}
1935
1936		// Free the memory occupied by the group list.
1937		for (i = 0; i < MAX_GROUPS_PER_USER; i++)
1938			if (groups[i])
1939				free(groups[i]);
1940
1941		// If no rights have been granted, deny access.
1942		if (session->rights == 0)
1943			return EACCES;
1944
1945		// If write access has been globally disabled, this user's rights must be
1946		// correspondingly synchronized.
1947		if (fileShares[shareId].readOnly)
1948			session->rights = BT_RIGHTS_READ;
1949	}
1950	else
1951		session->rights = fileShares[shareId].readOnly
1952			? BT_RIGHTS_READ
1953			: BT_RIGHTS_READ | BT_RIGHTS_WRITE;
1954
1955	// Make sure the folder we want to share still exists.
1956	path = fileShares[shareId].path;
1957	if (stat(path, &st) != 0)
1958		return ENOENT;
1959
1960	// Make sure it really is a folder and not a file.
1961	if (!S_ISDIR(st.st_mode))
1962		return EACCES;
1963
1964	*vnid = st.st_ino;
1965	btAddHandle(0, *vnid, path);
1966	return B_OK;
1967}
1968
1969int btGetFSInfo(char *rootPath, fs_info *fsInfo)
1970{
1971	dev_t device = dev_for_path(rootPath);
1972	if (device < 0)
1973		return device;
1974
1975	if (fs_stat_dev(device, fsInfo) != 0)
1976		return errno;
1977
1978	return B_OK;
1979}
1980
1981int btLookup(char *pathBuf, vnode_id dir_vnid, char *fileName, vnode_id *file_vnid)
1982{
1983	bt_node *dnode, *fnode;
1984	struct stat st;
1985	char path[B_PATH_NAME_LENGTH], *folder;
1986
1987	*file_vnid = 0;
1988
1989	btLock(handleSem, &handleVar);
1990	dnode = btGetNodeFromVnid(dir_vnid);
1991	btUnlock(handleSem, &handleVar);
1992
1993	if (!dnode)
1994		return EACCES;
1995
1996	// Search all nodes for one with the given parent vnid and file
1997	// name.  If one is found, we can simply use that node to fill in
1998	// the new handle.
1999	fnode = btFindNode(dnode, fileName);
2000	if (fnode)
2001	{
2002		*file_vnid = fnode->vnid;
2003
2004		folder = btGetLocalFileName(pathBuf, *file_vnid);
2005		if (folder)
2006			if (lstat(folder, &st) != 0)
2007			{
2008				btRemoveHandle(*file_vnid);
2009				*file_vnid = 0;
2010				return ENOENT;
2011			}
2012	}
2013	else
2014	{
2015		folder = btGetLocalFileName(pathBuf, dir_vnid);
2016		if (folder)
2017		{
2018			sprintf(path, "%s/%s", folder, fileName);
2019			if (lstat(path, &st) != 0)
2020				return ENOENT;
2021
2022			*file_vnid = st.st_ino;
2023			btAddHandle(dir_vnid, *file_vnid, fileName);
2024		}
2025	}
2026
2027	return B_OK;
2028}
2029
2030int btStat(char *pathBuf, vnode_id vnid, struct stat *st)
2031{
2032	char *fileName;
2033	int error;
2034
2035	fileName = btGetLocalFileName(pathBuf, vnid);
2036	if (fileName)
2037	{
2038		error = lstat(fileName, st);
2039		return (error != 0 ? ENOENT : B_OK);
2040	}
2041
2042	return ENOENT;
2043}
2044
2045int btReadDir(char *pathBuf, vnode_id dir_vnid, DIR **dir, vnode_id *file_vnid, char *filename, struct stat *st)
2046{
2047	struct dirent *dirInfo;
2048	char *folder, path[B_PATH_NAME_LENGTH];
2049
2050	if (dir_vnid == 0 || !file_vnid || !filename)
2051		return EINVAL;
2052
2053	if (!*dir)
2054	{
2055		folder = btGetLocalFileName(pathBuf, dir_vnid);
2056		if (folder)
2057			*dir = opendir(folder);
2058	}
2059
2060	if (*dir)
2061	{
2062		if ((dirInfo = readdir(*dir)) != NULL)
2063		{
2064			folder = btGetLocalFileName(pathBuf, dir_vnid);
2065			if (folder)
2066			{
2067				sprintf(path, "%s/%s", folder, dirInfo->d_name);
2068				if (lstat(path, st) != 0)
2069					return ENOENT;
2070
2071				strcpy(filename, dirInfo->d_name);
2072				*file_vnid = st->st_ino;
2073				btAddHandle(dir_vnid, *file_vnid, filename);
2074				return B_OK;
2075			}
2076		}
2077		else
2078		{
2079			closedir(*dir);
2080			return ENOENT;
2081		}
2082	}
2083
2084	return EINVAL;
2085}
2086
2087int32 btRead(char *pathBuf, vnode_id vnid, off_t pos, int32 len, char *buffer)
2088{
2089	char *path;
2090	int bytes;
2091
2092	path = btGetLocalFileName(pathBuf, vnid);
2093	if (path)
2094	{
2095		int file = open(path, O_RDONLY);
2096		if (file < 0)
2097			return errno;
2098
2099		lseek(file, (int32) pos, SEEK_SET);
2100		bytes = read(file, buffer, len);
2101		close(file);
2102
2103		// Return zero on any error.
2104		if (bytes == -1)
2105			bytes = 0;
2106
2107		buffer[bytes] = 0;
2108		return bytes;
2109	}
2110
2111	return 0;
2112}
2113
2114int32 btWrite(bt_session_t *session, vnode_id vnid, off_t pos, int32 len, int32 totalLen, char *buffer)
2115{
2116	bt_block *block;
2117
2118	// If we've been given a total length, then we have a new buffered write
2119	// session coming.  A block will need to be allocated.
2120	if (totalLen > 0)
2121	{
2122		// Make sure we don't have a wildly inaccurate total length to allocate.
2123		if (totalLen > 10 * 1024 * 1024)
2124			return 0;
2125
2126		// Allocate a new buffered I/O block.
2127		block = (bt_block *) malloc(sizeof(bt_block));
2128		if (block)
2129		{
2130			block->vnid = vnid;
2131			block->pos = pos;
2132			block->len = totalLen;
2133			block->count = 0;
2134
2135			block->buffer = (char *) malloc(totalLen + 1);
2136			if (!block->buffer)
2137			{
2138				free(block);
2139				return 0;
2140			}
2141
2142			btInsertWriteBlock(session, block);
2143		}
2144		else
2145			return 0;
2146	}
2147	else
2148	{
2149		block = btGetWriteBlock(session, vnid);
2150		if (!block)
2151			return 0;
2152	}
2153
2154	memcpy(block->buffer + block->count, buffer, len);
2155	block->count += len;
2156	return len;
2157}
2158
2159// btGetWriteBlock()
2160//
2161bt_block *btGetWriteBlock(bt_session_t *session, vnode_id vnid)
2162{
2163	bt_block *block;
2164
2165	btLock(session->blockSem, &session->blockVar);
2166
2167	block = session->rootBlock;
2168	while (block && block->vnid != vnid)
2169		block = block->next;
2170
2171	btUnlock(session->blockSem, &session->blockVar);
2172	return block;
2173}
2174
2175// btInsertWriteBlock()
2176//
2177void btInsertWriteBlock(bt_session_t *session, bt_block *block)
2178{
2179	btLock(session->blockSem, &session->blockVar);
2180
2181	block->next = session->rootBlock;
2182	block->prev = NULL;
2183	if (session->rootBlock)
2184		session->rootBlock->prev = block;
2185
2186	session->rootBlock = block;
2187
2188	btUnlock(session->blockSem, &session->blockVar);
2189}
2190
2191int btCommit(bt_session_t *session, vnode_id vnid)
2192{
2193	bt_block *block;
2194	char *path;
2195	int file;
2196
2197	// Get the full path for the specified file.
2198	path = btGetLocalFileName(session->pathBuffer, vnid);
2199	if (!path)
2200		return ENOENT;
2201
2202	// Obtain the buffered I/O block.  If one can't be found, no buffered I/O
2203	// session was started for this vnode.
2204	block = btGetWriteBlock(session, vnid);
2205	if (!block)
2206		return ENOENT;
2207
2208	// Open the file for writing.
2209	file = open(path, O_WRONLY | O_CREAT);
2210	if (file < 0)
2211		return errno;
2212
2213	btLock(session->blockSem, &session->blockVar);
2214
2215	// Write the data.
2216	lseek(file, (int32) block->pos, SEEK_SET);
2217	write(file, block->buffer, block->len);
2218
2219	btRemoveWriteBlock(session, block);
2220	btUnlock(session->blockSem, &session->blockVar);
2221
2222	close(file);
2223	return B_OK;
2224}
2225
2226void btRemoveWriteBlock(bt_session_t *session, bt_block *block)
2227{
2228	// If we're removing the root, then adjust the root block pointer.
2229	if (session->rootBlock == block)
2230		session->rootBlock = block->next;
2231
2232	// If there's a previous block, it should now point beyond this block.
2233	if (block->prev)
2234		block->prev->next = block->next;
2235
2236	// If there's a next block, it should now point to the current predecessor.
2237	if (block->next)
2238		block->next->prev = block->prev;
2239
2240	// Release the memory used by this block.
2241	free(block->buffer);
2242	free(block);
2243}
2244
2245int btCreate(char *pathBuf, vnode_id dir_vnid, char *name, int omode, int perms, vnode_id *file_vnid)
2246{
2247	struct stat st;
2248	char path[B_PATH_NAME_LENGTH], *folder;
2249	int fh;
2250
2251	folder = btGetLocalFileName(pathBuf, dir_vnid);
2252	if (folder)
2253	{
2254		sprintf(path, "%s/%s", folder, name);
2255		fh = open(path, O_WRONLY | O_CREAT | O_TRUNC | omode, perms);
2256		if (fh == -1)
2257			return errno;
2258		else
2259		{
2260			close(fh);
2261			if (lstat(path, &st) == 0)
2262			{
2263				*file_vnid = st.st_ino;
2264				btAddHandle(dir_vnid, *file_vnid, name);
2265			}
2266			else
2267				return EACCES;
2268		}
2269	}
2270
2271	return B_OK;
2272}
2273
2274int btTruncate(char *pathBuf, vnode_id vnid, int64 len)
2275{
2276	char *path;
2277	int error;
2278
2279	path = btGetLocalFileName(pathBuf, vnid);
2280	if (path)
2281	{
2282		error = truncate(path, len);
2283		if (error == -1)
2284			return errno;
2285
2286		return B_OK;
2287	}
2288
2289	return EACCES;
2290}
2291
2292// btCreateDir()
2293//
2294int btCreateDir(char *pathBuf, vnode_id dir_vnid, char *name, int perms, vnode_id *file_vnid, struct stat *st)
2295{
2296	char path[B_PATH_NAME_LENGTH], *folder;
2297
2298	folder = btGetLocalFileName(pathBuf, dir_vnid);
2299	if (folder)
2300	{
2301		sprintf(path, "%s/%s", folder, name);
2302		if (mkdir(path, perms) != B_OK)
2303			return EACCES;
2304
2305		if (lstat(path, st) != 0)
2306			return errno;
2307
2308		*file_vnid = st->st_ino;
2309		btAddHandle(dir_vnid, *file_vnid, name);
2310		return B_OK;
2311	}
2312
2313	return ENOENT;
2314}
2315
2316// btDeleteDir()
2317//
2318int btDeleteDir(char *pathBuf, vnode_id vnid, char *name)
2319{
2320	struct stat st;
2321	char path[B_PATH_NAME_LENGTH], *folder;
2322
2323	folder = btGetLocalFileName(pathBuf, vnid);
2324	if (folder)
2325	{
2326		sprintf(path, "%s/%s", folder, name);
2327		if (lstat(path, &st) != 0)
2328			return errno;
2329
2330		if (rmdir(path) == -1)
2331			return errno;
2332
2333		btPurgeNodes(st.st_ino);
2334		return B_OK;
2335	}
2336
2337	return ENOENT;
2338}
2339
2340// btRename()
2341//
2342int btRename(char *pathBuf, vnode_id old_vnid, char *oldName, vnode_id new_vnid, char *newName)
2343{
2344	struct stat st;
2345	char oldPath[B_PATH_NAME_LENGTH], newPath[B_PATH_NAME_LENGTH], *oldFolder, *newFolder;
2346
2347	oldFolder = btGetLocalFileName(pathBuf, old_vnid);
2348	if (oldFolder)
2349	{
2350		sprintf(oldPath, "%s/%s", oldFolder, oldName);
2351
2352		newFolder = btGetLocalFileName(pathBuf, new_vnid);
2353		if (newFolder)
2354		{
2355			sprintf(newPath, "%s/%s", newFolder, newName);
2356
2357			if (lstat(oldPath, &st) != 0)
2358				return errno;
2359
2360			btPurgeNodes(st.st_ino);
2361
2362			if (rename(oldPath, newPath) == -1)
2363				return errno;
2364
2365			return B_OK;
2366		}
2367	}
2368
2369	return ENOENT;
2370}
2371
2372// btUnlink()
2373//
2374int btUnlink(char *pathBuf, vnode_id vnid, char *name)
2375{
2376	struct stat st;
2377	char path[B_PATH_NAME_LENGTH], *folder;
2378	int error;
2379
2380	folder = btGetLocalFileName(pathBuf, vnid);
2381	if (folder)
2382	{
2383		sprintf(path, "%s/%s", folder, name);
2384
2385		// Obtain the inode (vnid) of the specified file through lstat().
2386		if (lstat(path, &st) != 0)
2387			return errno;
2388
2389		// Construct a dummy file descriptor and cause it to be removed from
2390		// the list.
2391		btRemoveHandle(st.st_ino);
2392
2393		error = unlink(path);
2394		return (error == -1 ? errno : B_OK);
2395	}
2396
2397	return EACCES;
2398}
2399
2400int btReadLink(char *pathBuf, vnode_id vnid, char *buffer, int length)
2401{
2402	char *path;
2403	int error;
2404
2405	path = btGetLocalFileName(pathBuf, vnid);
2406	if (path)
2407	{
2408		error = readlink(path, buffer, length);
2409		if (error == -1)
2410			return errno;
2411
2412		// If readlink() didn't return -1, it returned the number of bytes supplied in the
2413		// buffer.  It seems, however, that it does not null-terminate the string for us.
2414		buffer[error] = 0;
2415		return B_OK;
2416	}
2417
2418	return ENOENT;
2419}
2420
2421int btSymLink(char *pathBuf, vnode_id vnid, char *name, char *dest)
2422{
2423	char path[B_PATH_NAME_LENGTH], *folder;
2424
2425	folder = btGetLocalFileName(pathBuf, vnid);
2426	if (folder)
2427	{
2428		sprintf(path, "%s/%s", folder, name);
2429		if (symlink(dest, path) == -1)
2430			return errno;
2431
2432		return B_OK;
2433	}
2434
2435	return ENOENT;
2436}
2437
2438int btWStat(char *pathBuf, vnode_id vnid, long mask, int32 mode, int32 uid, int32 gid, int64 size, int32 atime, int32 mtime)
2439{
2440	struct utimbuf ftimes;
2441	struct stat st;
2442	char *path;
2443
2444	path = btGetLocalFileName(pathBuf, vnid);
2445	if (path)
2446	{
2447		if (mask & WSTAT_MODE)
2448			chmod(path, mode);
2449
2450		// BeOS doesn't support passing -1 as the user ID or group ID, which normally would
2451		// simply leave the value unchanged.  This complicates things a bit, but keep in
2452		// mind that BeOS doesn't really support multiple users anyway.
2453		if (mask & WSTAT_UID && mask & WSTAT_GID)
2454			chown(path, uid, gid);
2455
2456//		if (mask & WSTAT_UID)
2457//			chown(path, uid, -1);
2458
2459//		if (mask & WSTAT_GID)
2460//			chown(path, -1, gid);
2461
2462		if (mask & WSTAT_SIZE)
2463			truncate(path, size);
2464
2465		if (lstat(path, &st) == 0)
2466			if (mask & WSTAT_ATIME || mask & WSTAT_MTIME)
2467			{
2468				ftimes.actime = mask & WSTAT_ATIME ? atime : st.st_atime;
2469				ftimes.modtime = mask & WSTAT_MTIME ? mtime : st.st_mtime;
2470				utime(path, &ftimes);
2471			}
2472
2473		return B_OK;
2474	}
2475
2476	return ENOENT;
2477}
2478
2479int btReadAttrib(char *pathBuf, vnode_id vnid, char *name, int32 dataType, void *buffer, int32 pos, int32 len)
2480{
2481	char *path;
2482	int file;
2483
2484	path = btGetLocalFileName(pathBuf, vnid);
2485	if (path)
2486	{
2487		file = open(path, O_RDONLY);
2488
2489		if (file)
2490		{
2491			int bytes = (int) fs_read_attr(file, name, dataType, pos, buffer, len);
2492			close(file);
2493
2494			if ((dataType == B_STRING_TYPE || dataType == B_MIME_TYPE) && bytes < len && bytes >= 0)
2495				((char *) buffer)[bytes] = 0;
2496
2497			return bytes;
2498		}
2499	}
2500
2501	return ENOENT;
2502}
2503
2504int btWriteAttrib(char *pathBuf, vnode_id vnid, char *name, int32 dataType, void *buffer, int32 pos, int32 len)
2505{
2506	char *path;
2507	int file;
2508
2509	path = btGetLocalFileName(pathBuf, vnid);
2510	if (path)
2511	{
2512		file = open(path, O_RDONLY);
2513
2514		if (file)
2515		{
2516			int bytes = (int) fs_write_attr(file, name, dataType, pos, buffer, len);
2517			close(file);
2518			return bytes;
2519		}
2520	}
2521
2522	return ENOENT;
2523}
2524
2525int btReadAttribDir(char *pathBuf, vnode_id vnid, DIR **dir, char *attrName)
2526{
2527	dirent_t *entry;
2528	char *path;
2529
2530	if (!attrName)
2531		return EINVAL;
2532
2533	if (!*dir)
2534	{
2535		path = btGetLocalFileName(pathBuf, vnid);
2536		if (path)
2537			*dir = fs_open_attr_dir(path);
2538	}
2539
2540	if (*dir)
2541		do
2542		{
2543			entry = fs_read_attr_dir(*dir);
2544			if (entry)
2545			{
2546				if (strncmp(entry->d_name, "_trk/", 5) == 0)
2547					continue;
2548
2549				strcpy(attrName, entry->d_name);
2550				return B_OK;
2551			}
2552		} while (entry);
2553
2554	if (*dir)
2555		fs_close_attr_dir(*dir);
2556
2557	return ENOENT;
2558}
2559
2560int btRemoveAttrib(char *pathBuf, vnode_id vnid, char *name)
2561{
2562	char *path;
2563	int file;
2564
2565	path = btGetLocalFileName(pathBuf, vnid);
2566	if (path)
2567	{
2568		file = open(path, O_RDONLY);
2569
2570		if (file)
2571		{
2572			int error = fs_remove_attr(file, name);
2573			if (error == -1)
2574				error = errno;
2575
2576			close(file);
2577			return error;
2578		}
2579	}
2580
2581	return ENOENT;
2582}
2583
2584int btStatAttrib(char *pathBuf, vnode_id vnid, char *name, struct attr_info *info)
2585{
2586	char *path;
2587	int file;
2588
2589	path = btGetLocalFileName(pathBuf, vnid);
2590	if (path)
2591	{
2592		file = open(path, O_RDONLY);
2593
2594		if (file)
2595		{
2596			int error = fs_stat_attr(file, name, info);
2597			if (error == -1)
2598				error = errno;
2599
2600			close(file);
2601			return error;
2602		}
2603	}
2604
2605	return ENOENT;
2606}
2607
2608int btReadIndexDir(char *rootPath, DIR **dir, char *indexName)
2609{
2610	struct dirent *dirInfo;
2611
2612	if (!*dir)
2613	{
2614		dev_t device = dev_for_path(rootPath);
2615		if (device < 0)
2616			return device;
2617
2618		*dir = fs_open_index_dir(device);
2619	}
2620
2621	if (*dir)
2622		if ((dirInfo = fs_read_index_dir(*dir)) != NULL)
2623		{
2624			strcpy(indexName, dirInfo->d_name);
2625			return B_OK;
2626		}
2627		else
2628		{
2629			fs_close_index_dir(*dir);
2630			*dir = NULL;
2631			return ENOENT;
2632		}
2633
2634	return ENOENT;
2635}
2636
2637int btCreateIndex(char *rootPath, char *name, int type, int flags)
2638{
2639	dev_t device = dev_for_path(rootPath);
2640	if (device < 0)
2641		return device;
2642
2643	if (fs_create_index(device, name, type, flags) == -1)
2644		return errno;
2645
2646	return B_OK;
2647}
2648
2649int btRemoveIndex(char *rootPath, char *name)
2650{
2651	dev_t device = dev_for_path(rootPath);
2652	if (device < 0)
2653		return device;
2654
2655	if (fs_remove_index(device, name) == -1)
2656		return errno;
2657
2658	return B_OK;
2659}
2660
2661int btStatIndex(char *rootPath, char *name, struct index_info *info)
2662{
2663	dev_t device = dev_for_path(rootPath);
2664	if (device < 0)
2665		return device;
2666
2667	if (fs_stat_index(device, name, info) == -1)
2668		return errno;
2669
2670	return B_OK;
2671}
2672
2673int btReadQuery(char *rootPath, DIR **dir, char *query, char *fileName, vnode_id *vnid, vnode_id *parent)
2674{
2675	struct dirent *dirInfo;
2676
2677	if (!*dir)
2678	{
2679		dev_t device = dev_for_path(rootPath);
2680		if (device < 0)
2681			return device;
2682
2683		*dir = fs_open_query(device, query, 0);
2684	}
2685
2686	if (*dir)
2687		if ((dirInfo = fs_read_query(*dir)) != NULL)
2688		{
2689			*vnid = dirInfo->d_ino;
2690			*parent = dirInfo->d_pino;
2691			strcpy(fileName, dirInfo->d_name);
2692			return B_OK;
2693		}
2694		else
2695		{
2696			fs_close_query(*dir);
2697			*dir = NULL;
2698			return ENOENT;
2699		}
2700
2701	return ENOENT;
2702}
2703
2704////////////////////////////////////////////////////////////////////
2705
2706void netbtPreMount(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
2707{
2708	bt_outPacket packet;
2709	int client, security;
2710
2711	client = session->socket;
2712	if (argc == 1)
2713	{
2714		if (argv[0].type == B_STRING_TYPE)
2715		{
2716			security = btPreMount(session, argv[0].data);
2717			btRPCCreateAck(&packet, xid, security);
2718			btRPCSendAck(client, &packet);
2719			return;
2720		}
2721	}
2722
2723	sendErrorToClient(client, xid, EINVAL);
2724}
2725
2726void netbtMount(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
2727{
2728	bt_outPacket packet;
2729	vnode_id vnid;
2730	int client, error;
2731
2732	client = session->socket;
2733	if (argc == 3)
2734	{
2735		if (argv[0].type == B_STRING_TYPE && argv[1].type == B_STRING_TYPE &&
2736			argv[2].type == B_STRING_TYPE)
2737		{
2738			char *shareName = argv[0].data;
2739			char *user = argv[1].data;
2740			char *password = argv[2].data;
2741			error = btMount(session, shareName, user, password, &vnid);
2742			if (error == B_OK)
2743			{
2744				// Record this session having logged in to a specific share.
2745				session->share = btGetShareId(shareName);
2746				session->logon = time(NULL);
2747
2748				// Now send the client a response with the root vnid.
2749				btRPCCreateAck(&packet, xid, error);
2750				btRPCPutInt64(&packet, vnid);
2751			}
2752			else
2753				btRPCCreateAck(&packet, xid, error);
2754
2755			btRPCSendAck(client, &packet);
2756			return;
2757		}
2758	}
2759
2760	sendErrorToClient(client, xid, EINVAL);
2761}
2762
2763void netbtFSInfo(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
2764{
2765	bt_outPacket packet;
2766	int client, error;
2767
2768	client = session->socket;
2769	if (argc == 1)
2770	{
2771		if (argv[0].type == B_INT64_TYPE)
2772		{
2773			fs_info info;
2774
2775			error = btGetFSInfo(fileShares[session->share].path, &info);
2776			if (error == B_OK)
2777			{
2778				btRPCCreateAck(&packet, xid, error);
2779				btRPCPutInt32(&packet, info.block_size);
2780				btRPCPutInt32(&packet, info.total_blocks);
2781				btRPCPutInt32(&packet, info.free_blocks);
2782			}
2783			else
2784				btRPCCreateAck(&packet, xid, error);
2785
2786			btRPCSendAck(client, &packet);
2787			return;
2788		}
2789	}
2790
2791	sendErrorToClient(client, xid, EINVAL);
2792}
2793
2794void netbtLookup(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
2795{
2796	bt_outPacket packet;
2797	struct stat st;
2798	int client, error;
2799
2800	client = session->socket;
2801	if (argc == 2)
2802	{
2803		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_STRING_TYPE)
2804		{
2805			vnode_id dir_vnid = *((vnode_id *) argv[0].data);
2806			vnode_id file_vnid;
2807			error = btLookup(session->pathBuffer, dir_vnid, argv[1].data, &file_vnid);
2808			if (error == B_OK)
2809				error = btStat(session->pathBuffer, file_vnid, &st);
2810
2811			if (error == B_OK)
2812			{
2813				btRPCCreateAck(&packet, xid, B_OK);
2814				btRPCPutInt64(&packet, file_vnid);
2815				btRPCPutStat(&packet, &st);
2816			}
2817			else
2818				btRPCCreateAck(&packet, xid, error);
2819
2820			btRPCSendAck(client, &packet);
2821			return;
2822		}
2823	}
2824
2825	sendErrorToClient(client, xid, EINVAL);
2826}
2827
2828void netbtReadDir(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
2829{
2830	bt_outPacket packet;
2831	struct stat st;
2832	int client, error;
2833
2834	client = session->socket;
2835	if (argc == 2)
2836	{
2837		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_STRING_TYPE)
2838		{
2839			vnode_id dir_vnid = *((vnode_id *) argv[0].data);
2840			vnode_id file_vnid;
2841			DIR *dir;
2842			char filename[B_PATH_NAME_LENGTH];
2843			int entries = 0;
2844
2845			dir = (DIR *)(*((int32 *) argv[1].data));
2846			error = btReadDir(session->pathBuffer, dir_vnid, &dir, &file_vnid, filename, &st);
2847
2848			if (error != B_OK)
2849			{
2850				btRPCCreateAck(&packet, xid, error);
2851				btRPCSendAck(client, &packet);
2852				return;
2853			}
2854
2855			btRPCCreateAck(&packet, xid, B_OK);
2856			while (error == B_OK)
2857			{
2858				btRPCPutInt64(&packet, file_vnid);
2859				btRPCPutString(&packet, filename, strlen(filename));
2860				btRPCPutInt32(&packet, (int32) dir);
2861				btRPCPutStat(&packet, &st);
2862
2863				if (++entries >= 32)
2864					break;
2865
2866				error = btReadDir(session->pathBuffer, dir_vnid, &dir, &file_vnid, filename, &st);
2867				btRPCPutInt32(&packet, error);
2868			}
2869
2870			// If we exhausted the list of directory entries without filling
2871			// the buffer, add an error message that will prevent the client
2872			// from requesting further entries.
2873			if (entries < 32)
2874				btRPCPutInt32(&packet, ENOENT);
2875
2876			btRPCSendAck(client, &packet);
2877			return;
2878		}
2879	}
2880
2881	sendErrorToClient(client, xid, EINVAL);
2882}
2883
2884void netbtStat(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
2885{
2886	bt_outPacket packet;
2887	int client, error;
2888
2889	client = session->socket;
2890	if (argc == 1)
2891		if (argv[0].type == B_INT64_TYPE)
2892		{
2893			struct stat info;
2894			vnode_id vnid = *((vnode_id *) argv[0].data);
2895
2896			error = btStat(session->pathBuffer, vnid, &info);
2897
2898			if (error == B_OK)
2899			{
2900				btRPCCreateAck(&packet, xid, error);
2901				btRPCPutStat(&packet, &info);
2902			}
2903			else
2904				btRPCCreateAck(&packet, xid, error);
2905
2906			btRPCSendAck(client, &packet);
2907			return;
2908		}
2909
2910	sendErrorToClient(client, xid, EINVAL);
2911}
2912
2913void netbtRead(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
2914{
2915	bt_outPacket packet;
2916	int client;
2917
2918	client = session->socket;
2919	if (argc == 3)
2920		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_INT32_TYPE && argv[2].type == B_INT32_TYPE)
2921		{
2922			vnode_id vnid = *((vnode_id *) argv[0].data);
2923			off_t pos = *((off_t *) argv[1].data);
2924			int32 len = *((int32 *) argv[2].data);
2925			int32 bytes = 0;
2926
2927			session->ioBuffer[len] = 0;
2928			bytes = btRead(session->pathBuffer, vnid, pos, len, session->ioBuffer);
2929
2930			btRPCCreateAck(&packet, xid, B_OK);
2931			btRPCPutString(&packet, session->ioBuffer, bytes);
2932			btRPCSendAck(client, &packet);
2933			return;
2934		}
2935
2936	sendErrorToClient(client, xid, EINVAL);
2937}
2938
2939void netbtWrite(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
2940{
2941	int client;
2942
2943	client = session->socket;
2944	if (argc == 5)
2945		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_INT64_TYPE &&
2946			argv[2].type == B_INT32_TYPE && argv[3].type == B_INT32_TYPE &&
2947			argv[4].type == B_STRING_TYPE)
2948		{
2949			vnode_id vnid = *((vnode_id *) argv[0].data);
2950			off_t pos = *((off_t *) argv[1].data);
2951			int32 len = *((int32 *) argv[2].data);
2952			int32 totalLen = *((int32 *) argv[3].data);
2953
2954			// If the file share this user is connected to is read-only, the command
2955			// cannot be honored.
2956			if (session->rights & BT_RIGHTS_WRITE)
2957				btWrite(session, vnid, pos, len, totalLen, argv[4].data);
2958		}
2959}
2960
2961void netbtCreate(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
2962{
2963	bt_outPacket packet;
2964	struct stat st;
2965	int client, error;
2966
2967	client = session->socket;
2968	if (argc == 4)
2969		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_STRING_TYPE && argv[2].type == B_INT32_TYPE && argv[3].type == B_INT32_TYPE)
2970		{
2971			vnode_id dir_vnid = *((vnode_id *) argv[0].data);
2972			vnode_id file_vnid;
2973			int omode = *((int *) argv[2].data);
2974			int perms = *((int *) argv[3].data);
2975
2976			// If the file share this user is connected to is read-only, the command
2977			// cannot be honored.
2978			if (!(session->rights & BT_RIGHTS_WRITE))
2979			{
2980				btRPCCreateAck(&packet, xid, EACCES);
2981				btRPCSendAck(client, &packet);
2982				return;
2983			}
2984
2985			error = btCreate(session->pathBuffer, dir_vnid, argv[1].data, omode, perms, &file_vnid);
2986			if (error == B_OK)
2987				error = btStat(session->pathBuffer, file_vnid, &st);
2988
2989			if (error == B_OK)
2990			{
2991				btRPCCreateAck(&packet, xid, B_OK);
2992				btRPCPutInt64(&packet, file_vnid);
2993				btRPCPutStat(&packet, &st);
2994			}
2995			else
2996				btRPCCreateAck(&packet, xid, error);
2997
2998			btRPCSendAck(client, &packet);
2999			return;
3000		}
3001
3002	sendErrorToClient(client, xid, EINVAL);
3003}
3004
3005void netbtTruncate(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3006{
3007	bt_outPacket packet;
3008	int client, error;
3009
3010	client = session->socket;
3011	if (argc == 2)
3012		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_INT64_TYPE)
3013		{
3014			vnode_id vnid = *((vnode_id *) argv[0].data);
3015
3016			// If the file share this user is connected to is read-only, the command
3017			// cannot be honored.
3018			if (!(session->rights & BT_RIGHTS_WRITE))
3019			{
3020				btRPCCreateAck(&packet, xid, EACCES);
3021				btRPCSendAck(client, &packet);
3022				return;
3023			}
3024
3025			error = btTruncate(session->pathBuffer, vnid, *((int64 *) argv[1].data));
3026			btRPCCreateAck(&packet, xid, error);
3027			btRPCSendAck(client, &packet);
3028			return;
3029		}
3030
3031	sendErrorToClient(client, xid, EINVAL);
3032}
3033
3034void netbtUnlink(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3035{
3036	bt_outPacket packet;
3037	int client, error;
3038
3039	client = session->socket;
3040	if (argc == 2)
3041		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_STRING_TYPE)
3042		{
3043			vnode_id vnid = *((vnode_id *) argv[0].data);
3044
3045			// If the file share this user is connected to is read-only, the command
3046			// cannot be honored.
3047			if (!(session->rights & BT_RIGHTS_WRITE))
3048			{
3049				btRPCCreateAck(&packet, xid, EACCES);
3050				btRPCSendAck(client, &packet);
3051				return;
3052			}
3053
3054			error = btUnlink(session->pathBuffer, vnid, argv[1].data);
3055			btRPCCreateAck(&packet, xid, error);
3056			btRPCSendAck(client, &packet);
3057			return;
3058		}
3059
3060	sendErrorToClient(client, xid, EINVAL);
3061}
3062
3063void netbtRename(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3064{
3065	bt_outPacket packet;
3066	int client, error;
3067
3068	client = session->socket;
3069	if (argc == 4)
3070		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_STRING_TYPE && argv[2].type == B_INT64_TYPE && argv[3].type == B_STRING_TYPE)
3071		{
3072			vnode_id old_vnid = *((vnode_id *) argv[0].data);
3073			vnode_id new_vnid = *((vnode_id *) argv[2].data);
3074
3075			// If the file share this user is connected to is read-only, the command
3076			// cannot be honored.
3077			if (!(session->rights & BT_RIGHTS_WRITE))
3078			{
3079				btRPCCreateAck(&packet, xid, EACCES);
3080				btRPCSendAck(client, &packet);
3081				return;
3082			}
3083
3084			error = btRename(session->pathBuffer, old_vnid, argv[1].data, new_vnid, argv[3].data);
3085			btRPCCreateAck(&packet, xid, error);
3086			btRPCSendAck(client, &packet);
3087			return;
3088		}
3089
3090	sendErrorToClient(client, xid, EINVAL);
3091}
3092
3093void netbtCreateDir(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3094{
3095	bt_outPacket packet;
3096	int client, error;
3097
3098	client = session->socket;
3099	if (argc == 3)
3100		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_STRING_TYPE && argv[2].type == B_INT32_TYPE)
3101		{
3102			vnode_id dir_vnid = *((vnode_id *) argv[0].data);
3103			vnode_id file_vnid;
3104			struct stat st;
3105
3106			// If the file share this user is connected to is read-only, the command
3107			// cannot be honored.
3108			if (!(session->rights & BT_RIGHTS_WRITE))
3109			{
3110				btRPCCreateAck(&packet, xid, EACCES);
3111				btRPCSendAck(client, &packet);
3112				return;
3113			}
3114
3115			error = btCreateDir(session->pathBuffer, dir_vnid, argv[1].data, *((int *) argv[2].data), &file_vnid, &st);
3116			if (error == B_OK)
3117			{
3118				btRPCCreateAck(&packet, xid, B_OK);
3119				btRPCPutInt64(&packet, file_vnid);
3120				btRPCPutStat(&packet, &st);
3121			}
3122			else
3123				btRPCCreateAck(&packet, xid, error);
3124
3125			btRPCSendAck(client, &packet);
3126			return;
3127		}
3128
3129	sendErrorToClient(client, xid, EINVAL);
3130}
3131
3132void netbtDeleteDir(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3133{
3134	bt_outPacket packet;
3135	int client, error;
3136
3137	client = session->socket;
3138	if (argc == 2)
3139		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_STRING_TYPE)
3140		{
3141			vnode_id vnid = *((vnode_id *) argv[0].data);
3142
3143			// If the file share this user is connected to is read-only, the command
3144			// cannot be honored.
3145			if (!(session->rights & BT_RIGHTS_WRITE))
3146			{
3147				btRPCCreateAck(&packet, xid, EACCES);
3148				btRPCSendAck(client, &packet);
3149				return;
3150			}
3151
3152			error = btDeleteDir(session->pathBuffer, vnid, argv[1].data);
3153			btRPCCreateAck(&packet, xid, error);
3154			btRPCSendAck(client, &packet);
3155			return;
3156		}
3157
3158	sendErrorToClient(client, xid, EINVAL);
3159}
3160
3161void netbtReadLink(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3162{
3163	bt_outPacket packet;
3164	int client, error;
3165
3166	client = session->socket;
3167	if (argc == 1)
3168		if (argv[0].type == B_INT64_TYPE)
3169		{
3170			char path[B_PATH_NAME_LENGTH];
3171			vnode_id vnid = *((vnode_id *) argv[0].data);
3172
3173			error = btReadLink(session->pathBuffer, vnid, path, B_PATH_NAME_LENGTH);
3174			if (error == B_OK)
3175			{
3176				int length = strlen(path);
3177				btRPCCreateAck(&packet, xid, B_OK);
3178				btRPCPutString(&packet, path, length);
3179			}
3180			else
3181				btRPCCreateAck(&packet, xid, error);
3182
3183			btRPCSendAck(client, &packet);
3184			return;
3185		}
3186
3187	sendErrorToClient(client, xid, EINVAL);
3188}
3189
3190void netbtSymLink(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3191{
3192	bt_outPacket packet;
3193	int client, error;
3194
3195	client = session->socket;
3196	if (argc == 3)
3197		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_STRING_TYPE && argv[2].type == B_STRING_TYPE)
3198		{
3199			vnode_id vnid = *((vnode_id *) argv[0].data);
3200
3201			// If the file share this user is connected to is read-only, the command
3202			// cannot be honored.
3203			if (!(session->rights & BT_RIGHTS_WRITE))
3204			{
3205				btRPCCreateAck(&packet, xid, EACCES);
3206				btRPCSendAck(client, &packet);
3207				return;
3208			}
3209
3210			error = btSymLink(session->pathBuffer, vnid, argv[1].data, argv[2].data);
3211			btRPCCreateAck(&packet, xid, error);
3212			btRPCSendAck(client, &packet);
3213			return;
3214		}
3215
3216	sendErrorToClient(client, xid, EINVAL);
3217}
3218
3219void netbtWStat(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3220{
3221	bt_outPacket packet;
3222	int client, error;
3223
3224	client = session->socket;
3225	if (argc == 8)
3226		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_INT32_TYPE && argv[2].type == B_INT32_TYPE &&
3227			argv[3].type == B_INT32_TYPE && argv[4].type == B_INT32_TYPE && argv[5].type == B_INT32_TYPE &&
3228			argv[6].type == B_INT32_TYPE && argv[7].type == B_INT32_TYPE)
3229		{
3230			vnode_id vnid = *((vnode_id *) argv[0].data);
3231			int32 mask = *((int32 *) argv[1].data);
3232			int32 mode = *((int32 *) argv[2].data);
3233			int32 uid = *((int32 *) argv[3].data);
3234			int32 gid = *((int32 *) argv[4].data);
3235			int64 size = (int64) *((int32 *) argv[5].data);
3236			int32 atime = *((int32 *) argv[6].data);
3237			int32 mtime = *((int32 *) argv[7].data);
3238
3239			// If the file share this user is connected to is read-only, the command
3240			// cannot be honored.
3241			if (!(session->rights & BT_RIGHTS_WRITE))
3242			{
3243				btRPCCreateAck(&packet, xid, EACCES);
3244				btRPCSendAck(client, &packet);
3245				return;
3246			}
3247
3248			error = btWStat(session->pathBuffer, vnid, mask, mode, uid, gid, size, atime, mtime);
3249			btRPCCreateAck(&packet, xid, error);
3250			btRPCSendAck(client, &packet);
3251			return;
3252		}
3253
3254	sendErrorToClient(client, xid, EINVAL);
3255}
3256
3257void netbtReadAttrib(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3258{
3259	bt_outPacket packet;
3260	int client, bytesRead;
3261
3262	client = session->socket;
3263	if (argc == 5)
3264		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_STRING_TYPE && argv[2].type == B_INT32_TYPE &&
3265			argv[3].type == B_INT32_TYPE && argv[4].type == B_INT32_TYPE)
3266		{
3267			char *buffer;
3268			vnode_id vnid = *((vnode_id *) argv[0].data);
3269			int32 type = *((int32 *) argv[2].data);
3270			int32 pos = *((int32 *) argv[3].data);
3271			int32 len = *((int32 *) argv[4].data);
3272
3273			if (len <= BT_MAX_ATTR_BUFFER)
3274				buffer = session->attrBuffer;
3275			else
3276				buffer = (char *) malloc(len + 1);
3277
3278			if (buffer)
3279			{
3280				bytesRead = btReadAttrib(session->pathBuffer, vnid, argv[1].data, type, buffer, pos, len);
3281				if (bytesRead >= 0)
3282				{
3283					btRPCCreateAck(&packet, xid, B_OK);
3284					btRPCPutInt32(&packet, (int32) bytesRead);
3285					if (bytesRead > 0)
3286						btRPCPutString(&packet, buffer, bytesRead);
3287				}
3288				else
3289					btRPCCreateAck(&packet, xid, B_ENTRY_NOT_FOUND);
3290
3291				if (len > BT_MAX_ATTR_BUFFER)
3292					free(buffer);
3293			}
3294			else
3295				btRPCCreateAck(&packet, xid, ENOMEM);
3296
3297			btRPCSendAck(client, &packet);
3298			return;
3299		}
3300
3301	sendErrorToClient(client, xid, EINVAL);
3302}
3303
3304void netbtWriteAttrib(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3305{
3306	bt_outPacket packet;
3307	int client, bytesWritten;
3308
3309	client = session->socket;
3310	if (argc == 6)
3311		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_STRING_TYPE && argv[2].type == B_INT32_TYPE &&
3312			argv[3].type == B_STRING_TYPE && argv[4].type == B_INT32_TYPE && argv[5].type == B_INT32_TYPE)
3313		{
3314			vnode_id vnid = *((vnode_id *) argv[0].data);
3315			int32 type = *((int32 *) argv[2].data);
3316			int32 pos = *((int32 *) argv[4].data);
3317			int32 len = *((int32 *) argv[5].data);
3318
3319			// If the file share this user is connected to is read-only, the command
3320			// cannot be honored.
3321			if (!(session->rights & BT_RIGHTS_WRITE))
3322			{
3323				btRPCCreateAck(&packet, xid, EACCES);
3324				btRPCSendAck(client, &packet);
3325				return;
3326			}
3327
3328			bytesWritten = btWriteAttrib(session->pathBuffer, vnid, argv[1].data, type, argv[3].data, pos, len);
3329			if (bytesWritten >= 0)
3330			{
3331				btRPCCreateAck(&packet, xid, B_OK);
3332				btRPCPutInt32(&packet, (int32) bytesWritten);
3333			}
3334			else
3335				btRPCCreateAck(&packet, xid, B_ENTRY_NOT_FOUND);
3336
3337			btRPCSendAck(client, &packet);
3338			return;
3339		}
3340
3341	sendErrorToClient(client, xid, EINVAL);
3342}
3343
3344void netbtReadAttribDir(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3345{
3346	bt_outPacket packet;
3347	int client, error;
3348
3349	client = session->socket;
3350	if (argc == 2)
3351	{
3352		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_STRING_TYPE)
3353		{
3354			vnode_id vnid = *((vnode_id *) argv[0].data);
3355			DIR *dir = (DIR *)(*((int32 *) argv[1].data));
3356			char attrName[100];
3357			int entries = 0;
3358
3359			error = btReadAttribDir(session->pathBuffer, vnid, &dir, attrName);
3360
3361			if (error != B_OK)
3362			{
3363				btRPCCreateAck(&packet, xid, error);
3364				btRPCSendAck(client, &packet);
3365				return;
3366			}
3367
3368			btRPCCreateAck(&packet, xid, B_OK);
3369			while (error == B_OK)
3370			{
3371				btRPCPutString(&packet, attrName, strlen(attrName));
3372				btRPCPutInt32(&packet, (int32) dir);
3373
3374				if (++entries >= 32)
3375					break;
3376
3377				error = btReadAttribDir(session->pathBuffer, vnid, &dir, attrName);
3378				btRPCPutInt32(&packet, error);
3379			}
3380
3381			if (entries < 32)
3382				btRPCPutInt32(&packet, ENOENT);
3383
3384			btRPCSendAck(client, &packet);
3385			return;
3386		}
3387	}
3388
3389	sendErrorToClient(client, xid, EINVAL);
3390}
3391
3392void netbtRemoveAttrib(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3393{
3394	bt_outPacket packet;
3395	int client, error;
3396
3397	client = session->socket;
3398	if (argc == 2)
3399		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_STRING_TYPE)
3400		{
3401			vnode_id vnid = *((vnode_id *) argv[0].data);
3402
3403			// If the file share this user is connected to is read-only, the command
3404			// cannot be honored.
3405			if (!(session->rights & BT_RIGHTS_WRITE))
3406			{
3407				btRPCCreateAck(&packet, xid, EACCES);
3408				btRPCSendAck(client, &packet);
3409				return;
3410			}
3411
3412			error = btRemoveAttrib(session->pathBuffer, vnid, argv[1].data);
3413			btRPCCreateAck(&packet, xid, error);
3414			btRPCSendAck(client, &packet);
3415			return;
3416		}
3417
3418	sendErrorToClient(client, xid, EINVAL);
3419}
3420
3421void netbtStatAttrib(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3422{
3423	bt_outPacket packet;
3424	int client, error;
3425
3426	client = session->socket;
3427	if (argc == 2)
3428		if (argv[0].type == B_INT64_TYPE && argv[1].type == B_STRING_TYPE)
3429		{
3430			vnode_id vnid = *((vnode_id *) argv[0].data);
3431			struct attr_info info;
3432
3433			error = btStatAttrib(session->pathBuffer, vnid, argv[1].data, &info);
3434			if (error == B_OK)
3435			{
3436				btRPCCreateAck(&packet, xid, B_OK);
3437				btRPCPutInt32(&packet, info.type);
3438				btRPCPutInt64(&packet, info.size);
3439			}
3440			else
3441				btRPCCreateAck(&packet, xid, error);
3442
3443			btRPCSendAck(client, &packet);
3444			return;
3445		}
3446
3447	sendErrorToClient(client, xid, EINVAL);
3448}
3449
3450void netbtReadIndexDir(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3451{
3452	bt_outPacket packet;
3453	int client, error;
3454
3455	client = session->socket;
3456	if (argc == 1)
3457		if (argv[0].type == B_STRING_TYPE)
3458		{
3459			DIR *dir;
3460			char indexName[100];
3461
3462			dir = (DIR *)(*((int32 *) argv[0].data));
3463
3464			error = btReadIndexDir(fileShares[session->share].path, &dir, indexName);
3465			if (error == B_OK)
3466			{
3467				btRPCCreateAck(&packet, xid, B_OK);
3468				btRPCPutString(&packet, indexName, strlen(indexName));
3469				btRPCPutInt32(&packet, (int32) dir);
3470			}
3471			else
3472				btRPCCreateAck(&packet, xid, error);
3473
3474			btRPCSendAck(client, &packet);
3475			return;
3476		}
3477
3478	sendErrorToClient(client, xid, EINVAL);
3479}
3480
3481void netbtCreateIndex(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3482{
3483	bt_outPacket packet;
3484	int client, error;
3485
3486	client = session->socket;
3487	if (argc == 3)
3488		if (argv[0].type == B_STRING_TYPE && argv[1].type == B_INT32_TYPE && argv[2].type == B_INT32_TYPE)
3489		{
3490			int type = *((int32 *) argv[1].data);
3491			int flags = *((int32 *) argv[2].data);
3492
3493			// If the file share this user is connected to is read-only, the command
3494			// cannot be honored.
3495			if (!(session->rights & BT_RIGHTS_WRITE))
3496			{
3497				btRPCCreateAck(&packet, xid, EACCES);
3498				btRPCSendAck(client, &packet);
3499				return;
3500			}
3501
3502			error = btCreateIndex(fileShares[session->share].path, argv[0].data, type, flags);
3503			btRPCCreateAck(&packet, xid, error);
3504			btRPCSendAck(client, &packet);
3505			return;
3506		}
3507
3508	sendErrorToClient(client, xid, EINVAL);
3509}
3510
3511void netbtRemoveIndex(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3512{
3513	bt_outPacket packet;
3514	int client, error;
3515
3516	client = session->socket;
3517	if (argc == 1)
3518		if (argv[0].type == B_STRING_TYPE)
3519		{
3520			// If the file share this user is connected to is read-only, the command
3521			// cannot be honored.
3522			if (!(session->rights & BT_RIGHTS_WRITE))
3523			{
3524				btRPCCreateAck(&packet, xid, EACCES);
3525				btRPCSendAck(client, &packet);
3526				return;
3527			}
3528
3529			error = btRemoveIndex(fileShares[session->share].path, argv[0].data);
3530			btRPCCreateAck(&packet, xid, error);
3531			btRPCSendAck(client, &packet);
3532			return;
3533		}
3534
3535	sendErrorToClient(client, xid, EINVAL);
3536}
3537
3538void netbtStatIndex(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3539{
3540	bt_outPacket packet;
3541	int client, error;
3542
3543	client = session->socket;
3544	if (argc == 1)
3545		if (argv[0].type == B_STRING_TYPE)
3546		{
3547			struct index_info info;
3548
3549			error = btStatIndex(fileShares[session->share].path, argv[0].data, &info);
3550			if (error == B_OK)
3551			{
3552				btRPCCreateAck(&packet, xid, B_OK);
3553				btRPCPutInt32(&packet, info.type);
3554				btRPCPutInt64(&packet, info.size);
3555				btRPCPutInt32(&packet, info.modification_time);
3556				btRPCPutInt32(&packet, info.creation_time);
3557				btRPCPutInt32(&packet, info.uid);
3558				btRPCPutInt32(&packet, info.gid);
3559			}
3560			else
3561				btRPCCreateAck(&packet, xid, error);
3562
3563			btRPCSendAck(client, &packet);
3564			return;
3565		}
3566
3567	sendErrorToClient(client, xid, EINVAL);
3568}
3569
3570void netbtReadQuery(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3571{
3572	bt_outPacket packet;
3573	int client, error;
3574
3575	client = session->socket;
3576	if (argc == 2)
3577		if (argv[0].type == B_STRING_TYPE && argv[1].type == B_STRING_TYPE)
3578		{
3579			DIR *dir;
3580			char fileName[B_PATH_NAME_LENGTH];
3581			vnode_id vnid, parent;
3582
3583			dir = (DIR *)(*((int32 *) argv[0].data));
3584
3585			error = btReadQuery(fileShares[session->share].path, &dir, argv[1].data, fileName, &vnid, &parent);
3586			if (error == B_OK)
3587			{
3588				btRPCCreateAck(&packet, xid, B_OK);
3589				btRPCPutInt64(&packet, vnid);
3590				btRPCPutInt64(&packet, parent);
3591				btRPCPutString(&packet, fileName, strlen(fileName));
3592				btRPCPutInt32(&packet, (int32) dir);
3593			}
3594			else
3595				btRPCCreateAck(&packet, xid, error);
3596
3597			btRPCSendAck(client, &packet);
3598			return;
3599		}
3600
3601	sendErrorToClient(client, xid, EINVAL);
3602}
3603
3604// netbtCommit()
3605//
3606void netbtCommit(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3607{
3608	bt_outPacket packet;
3609	int client, error;
3610
3611	client = session->socket;
3612	if (argc == 1)
3613		if (argv[0].type == B_INT64_TYPE)
3614		{
3615			vnode_id vnid = *((vnode_id *) argv[0].data);
3616
3617			// If the file share this user is connected to is read-only, the command
3618			// cannot be honored.
3619			if (!(session->rights & BT_RIGHTS_WRITE))
3620			{
3621				btRPCCreateAck(&packet, xid, EACCES);
3622				btRPCSendAck(client, &packet);
3623				return;
3624			}
3625
3626			error = btCommit(session, vnid);
3627			btRPCCreateAck(&packet, xid, error);
3628			btRPCSendAck(client, &packet);
3629			return;
3630		}
3631
3632	sendErrorToClient(client, xid, EINVAL);
3633}
3634
3635void netbtPrintJobNew(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3636{
3637	bt_outPacket packet;
3638	int client;
3639
3640	client = session->socket;
3641	btRPCCreateAck(&packet, xid, EOPNOTSUPP);
3642	btRPCSendAck(client, &packet);
3643}
3644
3645void netbtPrintJobData(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3646{
3647	bt_outPacket packet;
3648	int client;
3649
3650	client = session->socket;
3651	btRPCCreateAck(&packet, xid, EOPNOTSUPP);
3652	btRPCSendAck(client, &packet);
3653}
3654
3655void netbtPrintJobCommit(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3656{
3657	bt_outPacket packet;
3658	int client;
3659
3660	client = session->socket;
3661	btRPCCreateAck(&packet, xid, EOPNOTSUPP);
3662	btRPCSendAck(client, &packet);
3663}
3664
3665void netbtAuthenticate(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3666{
3667	bt_outPacket packet;
3668	int client;
3669
3670	client = session->socket;
3671	btRPCCreateAck(&packet, xid, EOPNOTSUPP);
3672	btRPCSendAck(client, &packet);
3673}
3674
3675// netbtQuit()
3676//
3677void netbtQuit(bt_session_t *session, unsigned int xid, int argc, bt_arg_t argv[])
3678{
3679	bt_outPacket packet;
3680	int client;
3681
3682	client = session->socket;
3683	if (argc == 0)
3684	{
3685		btRPCCreateAck(&packet, xid, B_OK);
3686		btRPCSendAck(client, &packet);
3687		return;
3688	}
3689
3690	sendErrorToClient(client, xid, EINVAL);
3691}
3692