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//
14
15// dividing the files:
16// 1.  main(), start/stop service, request thread launching, misc
17// 2.  signal handlers
18// 3.  UDP command thread/handlers
19// 4.  config file parsing
20// 5.  RPC code
21// 6.  actual file handling commands
22// 7.  net file handling commands
23
24// Potential Uses:
25// 1.  Domain server, keeping track of users for logging in
26// 2.  File server, to share BeOS volume files across a network
27// 3.  Document management, to store documents with attributes
28// 4.  Version Control System, to manage multiple versions of documents
29// 5.  General directory server, for local or network settings and information
30
31#include "FindDirectory.h"
32
33#include "betalk.h"
34#include "sessions.h"
35#include "rpc_handlers.h"
36#include "rpc_workers.h"
37#include "file_shares.h"
38#include "readerWriter.h"
39
40#include "sysdepdefs.h"
41#include "fsproto.h"
42#include "netdb.h"
43
44#include "ctype.h"
45#include "time.h"
46#include "signal.h"
47#include "stdlib.h"
48#include "syslog.h"
49#include "sys/utsname.h"
50
51#define BT_MAX_THREADS		100
52#define BT_MAX_RETRIES		3
53
54#define BT_MAIN_NAME		"BeServed Daemon"
55#define BT_THREAD_NAME		"BeServed Handler"
56#define BT_HOST_THREAD_NAME	"BeServed Host Publisher"
57#define BT_SIGNATURE		"application/x-vnd.Teldar-BeServed"
58
59#define PATH_ROOT			"/boot"
60#define PATH_DELIMITER		'/'
61
62#ifndef iswhite
63#define iswhite(c)			((c == ' ' || c == '\t'))
64#endif
65
66
67int main(int argc, char *argv[]);
68void daemonInit();
69bool dateCheck();
70int32 btSendHost(void *data);
71int getSharedResources(char *buffer, int bufSize);
72void getHostInfo(bt_hostinfo *info);
73int getHostUsers(char *buffer);
74void startService();
75void endService(int sig);
76void restartService();
77void initShares();
78void freeFileHandles();
79void freeFileShares();
80void getFileShare(const char *buffer);
81void getShareProperty(const char *buffer);
82void getGrant(const char *buffer);
83void getAuthenticate(const char *buffer);
84bool getAuthServerAddress(const char *name);
85void addUserRights(char *share, char *user, int rights, bool isGroup);
86int getToken();
87int receiveRequest(bt_session_t *session);
88void handleRequest(bt_session_t *session, unsigned int xid, unsigned char command, int argc, bt_arg_t argv[]);
89void launchThread(int client, struct sockaddr_in *addr);
90int tooManyConnections(unsigned int s_addr);
91void sendErrorToClient(int client, unsigned int xid, int error);
92void getArguments(bt_session_t *session, bt_inPacket *packet, unsigned char command);
93int32 requestThread(void *data);
94
95
96bt_node *rootNode = NULL;
97bt_session_t *rootSession = NULL;
98bt_fileShare_t fileShares[BT_MAX_FILE_SHARES];
99char tokBuffer[B_PATH_NAME_LENGTH], *tokPtr;
100bool running = true;
101int server;
102char authServerName[B_FILE_NAME_LENGTH];
103unsigned int authServerIP;
104thread_id hostThread;
105bt_managed_data sessionData;
106bt_managed_data handleData;
107
108bt_command_t dirCommands[] =
109{
110	{ BT_CMD_PREMOUNT,			netbtPreMount,				true,	1,	{ B_STRING_TYPE } },
111	{ BT_CMD_MOUNT,				netbtMount,					true,	3,	{ B_STRING_TYPE, B_STRING_TYPE, B_STRING_TYPE } },
112	{ BT_CMD_FSINFO,			netbtFSInfo,				true,	1,	{ B_INT64_TYPE } },
113	{ BT_CMD_LOOKUP,			netbtLookup,				true,	2,	{ B_INT64_TYPE, B_STRING_TYPE } },
114	{ BT_CMD_STAT,				netbtStat,					true,	1,	{ B_INT64_TYPE } },
115	{ BT_CMD_READDIR,			netbtReadDir,				true,	2,	{ B_INT64_TYPE, B_STRING_TYPE } },
116	{ BT_CMD_READ,				netbtRead,					true,	3,	{ B_INT64_TYPE, B_INT32_TYPE, B_INT32_TYPE } },
117	{ BT_CMD_WRITE,				netbtWrite,					true,	5,	{ B_INT64_TYPE, B_INT64_TYPE, B_INT32_TYPE, B_INT32_TYPE, B_STRING_TYPE } },
118	{ BT_CMD_CREATE,			netbtCreate,				true,	4,	{ B_INT64_TYPE, B_STRING_TYPE, B_INT32_TYPE, B_INT32_TYPE } },
119	{ BT_CMD_TRUNCATE,			netbtTruncate,				true,	2,	{ B_INT64_TYPE, B_INT64_TYPE } },
120	{ BT_CMD_MKDIR,				netbtCreateDir, 			true,	3,	{ B_INT64_TYPE, B_STRING_TYPE, B_INT32_TYPE } },
121	{ BT_CMD_RMDIR,				netbtDeleteDir,				true,	2,	{ B_INT64_TYPE, B_STRING_TYPE } },
122	{ BT_CMD_RENAME,			netbtRename,				true,	4,	{ B_INT64_TYPE, B_STRING_TYPE, B_INT64_TYPE, B_STRING_TYPE } },
123	{ BT_CMD_UNLINK,			netbtUnlink,				true,	2,	{ B_INT64_TYPE, B_STRING_TYPE } },
124	{ BT_CMD_READLINK,			netbtReadLink,				true,	1,	{ B_INT64_TYPE } },
125	{ BT_CMD_SYMLINK,			netbtSymLink,				true,	3,	{ B_INT64_TYPE, B_STRING_TYPE, B_STRING_TYPE } },
126	{ BT_CMD_WSTAT,				netbtWStat,					true,	8,	{ B_INT64_TYPE, B_INT32_TYPE, B_INT32_TYPE, B_INT32_TYPE, B_INT32_TYPE, B_INT32_TYPE, B_INT32_TYPE, B_INT32_TYPE } },
127	{ BT_CMD_READATTRIB,		netbtReadAttrib,			true,	5,	{ B_INT64_TYPE, B_STRING_TYPE, B_INT32_TYPE, B_INT32_TYPE, B_INT32_TYPE } },
128	{ BT_CMD_WRITEATTRIB,		netbtWriteAttrib,			true,	6,	{ B_INT64_TYPE, B_STRING_TYPE, B_INT32_TYPE, B_STRING_TYPE, B_INT32_TYPE, B_INT32_TYPE } },
129	{ BT_CMD_READATTRIBDIR,		netbtReadAttribDir,			true,	2,	{ B_INT64_TYPE, B_STRING_TYPE } },
130	{ BT_CMD_REMOVEATTRIB,		netbtRemoveAttrib,			true,	2,	{ B_INT64_TYPE, B_STRING_TYPE } },
131	{ BT_CMD_STATATTRIB,		netbtStatAttrib,			true,	2,	{ B_INT64_TYPE, B_STRING_TYPE } },
132	{ BT_CMD_READINDEXDIR,		netbtReadIndexDir,			true,	1,	{ B_STRING_TYPE } },
133	{ BT_CMD_CREATEINDEX,		netbtCreateIndex,			true,	3,	{ B_STRING_TYPE, B_INT32_TYPE, B_INT32_TYPE } },
134	{ BT_CMD_REMOVEINDEX,		netbtRemoveIndex,			true,	1,	{ B_STRING_TYPE } },
135	{ BT_CMD_STATINDEX,			netbtStatIndex,				true,	1,	{ B_STRING_TYPE } },
136	{ BT_CMD_READQUERY,			netbtReadQuery,				true,	2,	{ B_STRING_TYPE, B_STRING_TYPE } },
137	{ BT_CMD_COMMIT,			netbtCommit,				true,	1,	{ B_INT64_TYPE } },
138	{ BT_CMD_PRINTJOB_NEW,		netbtPrintJobNew,			false,	0,	{ 0 } },
139	{ BT_CMD_PRINTJOB_DATA,		netbtPrintJobData,			false,	0,	{ 0 } },
140	{ BT_CMD_PRINTJOB_COMMIT,	netbtPrintJobCommit,		false,	0,	{ 0 } },
141	{ BT_CMD_AUTHENTICATE,		netbtAuthenticate,			false,	0,	{ 0 } },
142	{ BT_CMD_QUIT,				netbtQuit,					true,	0,	{ 0 } },
143	{ 0,						NULL,						false,	0,	{ 0 } }
144};
145
146char *keywords[] =
147{
148	"share",
149	"as",
150	"set",
151	"read",
152	"write",
153	"read-write",
154	"promiscuous",
155	"on",
156	"to",
157	"authenticate",
158	"with",
159	"group",
160	"printer",
161	"print",
162	"is",
163	"spooled",
164	"device",
165	"type",
166	NULL
167};
168
169/*----------------------------------------------------------------
170class BeServedServer : public BApplication
171{
172	thread_id appThread;
173	bool running;
174
175	public:
176		BeServedServer(const char *signature);
177
178		virtual void ReadyToRun();
179		virtual bool QuitRequested();
180};
181
182BeServedServer::BeServedServer(const char *signature)
183	: BApplication(signature)
184{
185}
186
187void BeServedServer::ReadyToRun()
188{
189	running = true;
190	appThread = spawn_thread(appMain, BT_MAIN_NAME, B_NORMAL_PRIORITY, this);
191	resume_thread(appThread);
192}
193
194bool BeServedServer::QuitRequested()
195{
196	status_t result;
197
198	if (!BApplication::QuitRequested())
199		return false;
200
201	running = false;
202	wait_for_thread(appThread, &result);
203	return true;
204}
205
206int main(int argc, char *argv[])
207{
208	BeServedServer app(BT_SIGNATURE);
209	app.Run();
210	return 0;
211}
212----------------------------------------------------------------------*/
213
214int main(int argc, char *argv[])
215{
216	daemonInit();
217
218	initShares();
219	signal(SIGINT, endService);
220	signal(SIGTERM, endService);
221	signal(SIGHUP, restartService);
222	signal(SIGPIPE, SIG_IGN);
223
224	if (initManagedData(&handleData))
225	{
226		if (initManagedData(&sessionData))
227		{
228			hostThread = spawn_thread(btSendHost, BT_HOST_THREAD_NAME, B_NORMAL_PRIORITY, 0);
229			resume_thread(hostThread);
230
231			// Run the daemon.  We will not return until the service is being stopped.
232			startService();
233
234			if (hostThread > 0)
235				kill_thread(hostThread);
236
237			closeManagedData(&sessionData);
238		}
239
240		closeManagedData(&handleData);
241	}
242
243	return 0;
244}
245
246bool dateCheck()
247{
248	struct stat st;
249	time_t curTime;
250
251	time(&curTime);
252	if (curTime > 1012537700)
253		return false;
254
255	if (stat("/boot/home/config/servers/beserved_server", &st) == 0)
256		if (curTime < st.st_ctime || curTime > st.st_ctime + 7776000)
257			return false;
258
259	return true;
260}
261
262void daemonInit()
263{
264	int i;
265
266	// Cause the parent task to terminate, freeing the terminal.
267	if (fork() != 0)
268		exit(0);
269
270	// In the child process, become the session leader.
271	setsid();
272
273	// Now fork again, causing the first child to exit, since the session
274	// leader can be assigned a controlling terminal under SVR4.
275	signal(SIGHUP, SIG_IGN);
276	if (fork() != 0)
277		exit(0);
278
279	// Change to the root directory, since if we hold on to a working
280	// folder that was in a mounted file system, that file system cannot
281	// be unmounted.
282	chdir("/");
283
284	// Reset the file creation mask to zero to eliminate the inherited value.
285	umask(0);
286
287	// Close open file descriptors.  Since we can't know how many of a
288	// potentially unlimited value can be open, just close the first 64
289	// and assume that will be enough.
290	for (i = 0; i < 64; i++)
291		close(i);
292
293	// Open the syslog.
294	openlog("beserved_server", LOG_PID, LOG_DAEMON);
295}
296
297void restartService()
298{
299	bt_fileShare_t *oldShares;
300	int i;
301
302	// Delay all mounting and other file system operations.
303	beginWriting(&handleData);
304	beginWriting(&sessionData);
305
306	// Copy existing share data.
307	oldShares = (bt_fileShare_t *) malloc(sizeof(bt_fileShare_t) * BT_MAX_FILE_SHARES);
308	if (oldShares)
309	{
310		for (i = 0; i < BT_MAX_FILE_SHARES; i++)
311			memcpy(&oldShares[i], &fileShares[i], sizeof(bt_fileShare_t));
312
313		// Reload the share data.
314		initShares();
315
316		// Now loop through the old file shares.  For each one, check if the same
317		// path exists in the new shares.
318		for (i = 0; i < BT_MAX_FILE_SHARES; i++)
319			if (oldShares[i].used)
320			{
321				bt_session_t *s;
322				int share = btGetShareIdByPath(oldShares[i].path);
323				if (share == -1)
324				{
325					for (s = rootSession; s; s = s->next)
326						if (s->share == i)
327							s->killed = true;
328				}
329				else if (share != i)
330				{
331					for (s = rootSession; s; s = s->next)
332						if (s->share == i)
333							s->share = share;
334				}
335			}
336
337		free(oldShares);
338	}
339
340	// Resume normal operation.
341	endWriting(&sessionData);
342	endWriting(&handleData);
343}
344
345int32 btSendHost(void *data)
346{
347	bt_request request;
348	bt_hostinfo info;
349	struct sockaddr_in serverAddr, clientAddr;
350	char buffer[4096];
351	int server, addrLen, bufLen, replyLen;
352
353	buffer[0] = 0;
354	bufLen = sizeof(buffer);
355
356	server = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
357	if (server == INVALID_SOCKET)
358		return -1;
359
360	memset(&serverAddr, 0, sizeof(serverAddr));
361	serverAddr.sin_family = AF_INET;
362	serverAddr.sin_port = htons(BT_QUERYHOST_PORT);
363	serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
364
365	// Bind that socket to the address constructed above.
366	if (bind(server, (struct sockaddr *) &serverAddr, sizeof(serverAddr)))
367		return -1;
368
369	while (running)
370	{
371		addrLen = sizeof(struct sockaddr_in);
372		replyLen = 0;
373		if (recvfrom(server, (char *) &request, sizeof(request), 0, (struct sockaddr *) &clientAddr, &addrLen) <= 0)
374			continue;
375
376		switch (request.command)
377		{
378			case BT_REQ_HOST_PROBE:
379				gethostname(buffer, bufLen);
380				break;
381
382			case BT_REQ_SHARE_PROBE:
383				replyLen = getSharedResources(buffer, sizeof(buffer));
384				break;
385
386			case BT_REQ_HOST_INFO:
387				getHostInfo(&info);
388				memcpy(buffer, &info, sizeof(bt_hostinfo));
389				replyLen = sizeof(bt_hostinfo);
390				break;
391
392			case BT_REQ_HOST_USERS:
393				replyLen = getHostUsers(buffer);
394				break;
395
396			case BT_REQ_AUTH_TYPES:
397				break;
398		}
399
400		// If no reply length has been specified, calculate it now by taking the
401		// length of the buffer.
402		if (replyLen == 0)
403			replyLen = strlen(buffer);
404
405		sendto(server, buffer, replyLen, 0, (struct sockaddr *) &clientAddr, addrLen);
406	}
407
408	// Close the socket.  Technically, I believe we should call shutdown()
409	// first, but the BeOS header file socket.h indicates that this
410	// function is not currently working.  It is present but may not have
411	// any effect.
412	shutdown(server, 2);
413	closesocket(server);
414	return 0;
415}
416
417// getSharedResources()
418//
419int getSharedResources(char *buffer, int bufSize)
420{
421	bt_resource resource;
422	int i, bufPos = 0;
423
424	// If the supplied buffer can't hold at least two resource structures, one
425	// for a shared resource and one to terminate the list, then don't bother
426	// building the list.
427	if (bufSize < 2 * sizeof(bt_resource))
428		return 0;
429
430	for (i = 0; i < BT_MAX_FILE_SHARES; i++)
431		if (fileShares[i].used)
432		{
433			// If this is the last resource structure that will fit in the
434			// buffer, then don't add any more into the list.
435			if (bufPos + sizeof(bt_resource) >= bufSize)
436				break;
437
438			// Fill out the resource structure.
439			resource.type = B_HOST_TO_LENDIAN_INT32(BT_SHARED_FOLDER);
440			strcpy(resource.name, fileShares[i].name);
441
442			// Copy the resource structure into the buffer at the current offset.
443			memcpy(buffer + bufPos, &resource, sizeof(bt_resource));
444			bufPos += sizeof(bt_resource);
445		}
446
447	// Copy the null terminating structure.
448	resource.type = B_HOST_TO_LENDIAN_INT32(BT_SHARED_NULL);
449	resource.name[0] = 0;
450	memcpy(buffer + bufPos, &resource, sizeof(bt_resource));
451	bufPos += sizeof(bt_resource);
452
453	return bufPos;
454}
455
456// getHostInfo()
457//
458void getHostInfo(bt_hostinfo *info)
459{
460	system_info sysinfo;
461	bt_session_t *s;
462	struct utsname uts;
463	char buf[100];
464	int i;
465
466	struct cpuMap
467	{
468		int cpuType;
469		char *cpuName;
470	} cpuList[] =
471	{
472		{ B_CPU_PPC_601, "PowerPC 601" },
473		{ B_CPU_PPC_603, "PowerPC 603" },
474		{ B_CPU_PPC_603e, "PowerPC 603e" },
475		{ B_CPU_PPC_604, "PowerPC 604" },
476		{ B_CPU_PPC_604e, "PowerPC 604e" },
477		{ B_CPU_PPC_750, "PowerPC 750" },
478		{ B_CPU_PPC_686, "PowerPC 686" },
479
480		{ B_CPU_INTEL_X86, "Intel 80x86" },
481		{ B_CPU_INTEL_PENTIUM, "Intel Pentium" },
482		{ B_CPU_INTEL_PENTIUM75, "Intel Pentium" },
483		{ B_CPU_INTEL_PENTIUM_486_OVERDRIVE, "Intel 486 Overdrive" },
484		{ B_CPU_INTEL_PENTIUM_MMX, "Intel Pentium MMX" },
485		{ B_CPU_INTEL_PENTIUM_MMX_MODEL_4, "Intel Pentium MMX" },
486		{ B_CPU_INTEL_PENTIUM_MMX_MODEL_8, "Intel Pentium MMX" },
487		{ B_CPU_INTEL_PENTIUM75_486_OVERDRIVE, "Intel 486 Overdrive" },
488		{ B_CPU_INTEL_PENTIUM_PRO, "Intel Pentium Pro" },
489		{ B_CPU_INTEL_PENTIUM_II, "Intel Pentium II" },
490		{ B_CPU_INTEL_PENTIUM_II_MODEL_3, "Intel Pentium II" },
491		{ B_CPU_INTEL_PENTIUM_II_MODEL_5, "Intel Pentium II" },
492		{ B_CPU_INTEL_CELERON, "Intel Celeron" },
493		{ B_CPU_INTEL_PENTIUM_III, "Intel Pentium III" },
494
495		{ B_CPU_AMD_X86, "AMD x86" },
496		{ B_CPU_AMD_K5_MODEL0, "AMD K5" },
497		{ B_CPU_AMD_K5_MODEL1, "AMD K5" },
498		{ B_CPU_AMD_K5_MODEL2, "AMD K5" },
499		{ B_CPU_AMD_K5_MODEL3, "AMD K5" },
500		{ B_CPU_AMD_K6_MODEL6, "AMD K6" },
501		{ B_CPU_AMD_K6_MODEL7, "AMD K6" },
502		{ B_CPU_AMD_K6_MODEL8, "AMD K6" },
503		{ B_CPU_AMD_K6_2, "AMD K6-2" },
504		{ B_CPU_AMD_K6_MODEL9, "AMD K6" },
505		{ B_CPU_AMD_K6_III, "AMD K6-3" },
506		{ B_CPU_AMD_ATHLON_MODEL1, "AMD Athlon" },
507
508		{ B_CPU_CYRIX_X86, "Cyrix x86" },
509		{ B_CPU_CYRIX_GXm, "Cyrix GXm" },
510		{ B_CPU_CYRIX_6x86MX, "Cyrix 6x86MX" },
511
512		{ B_CPU_IDT_X86, "IDT x86" },
513		{ B_CPU_IDT_WINCHIP_C6, "IDT WinChip C6" },
514		{ B_CPU_IDT_WINCHIP_2, "IDT WinChip 2" },
515
516		{ B_CPU_RISE_X86, "Rise x86" },
517		{ B_CPU_RISE_mP6, "Rise mP6" },
518
519		{ 0, NULL }
520	};
521
522	uname(&uts);
523	get_system_info(&sysinfo);
524
525	strcpy(info->system, uts.sysname);
526	strcat(info->system, " ");
527	strcat(info->system, uts.release);
528	strcpy(info->beServed, "BeServed 1.2.6");
529
530	info->cpus = B_HOST_TO_LENDIAN_INT32(sysinfo.cpu_count);
531	info->maxConnections = B_HOST_TO_LENDIAN_INT32(BT_MAX_THREADS);
532
533	strcpy(info->platform, "Unknown");
534	for (i = 0; cpuList[i].cpuType; i++)
535		if (cpuList[i].cpuType == sysinfo.cpu_type)
536		{
537			strcpy(info->platform, cpuList[i].cpuName);
538			break;
539		}
540
541	sprintf(buf, " at %ldMHz", (long) (sysinfo.cpu_clock_speed / 1000000));
542	strcat(info->platform, buf);
543
544	// Delay all new session creation.
545	beginReading(&sessionData);
546
547	info->connections = 0;
548	for (s = rootSession; s; s = s->next)
549		if (s->socket != INVALID_SOCKET)
550			info->connections++;
551
552	info->connections = B_HOST_TO_LENDIAN_INT32(info->connections);
553	endReading(&sessionData);
554}
555
556// getHostUsers()
557//
558int getHostUsers(char *buffer)
559{
560	bt_session_t *s;
561	char addr[20];
562	int len, bufSize;
563
564	// Initialize the buffer to be empty.
565	buffer[0] = 0;
566	bufSize = 0;
567
568	// Delay all new session creation.
569	beginReading(&sessionData);
570
571	for (s = rootSession; s; s = s->next)
572		if (s->socket != INVALID_SOCKET)
573		{
574			uint8 *s_addr = (uint8 *) s->client_s_addr;
575			sprintf(addr, "%d.%d.%d.%d", s_addr[0], s_addr[1], s_addr[2], s_addr[3]);
576			len = strlen(buffer);
577			strcpy(&buffer[len > 0 ? len + 1 : 0], addr);
578			bufSize += len + 1;
579		}
580
581	endReading(&sessionData);
582
583	buffer[bufSize++] = 0;
584	return bufSize;
585}
586
587void initShares()
588{
589	FILE *fp;
590	char path[B_PATH_NAME_LENGTH], buffer[512];
591	int i, length;
592
593	authServerIP = 0;
594	authServerName[0] = 0;
595
596	for (i = 0; i < BT_MAX_FILE_SHARES; i++)
597	{
598		fileShares[i].name[0] = 0;
599		fileShares[i].path[0] = 0;
600		fileShares[i].used = false;
601		fileShares[i].readOnly = true;
602		fileShares[i].security = BT_AUTH_NONE;
603		fileShares[i].rights = NULL;
604		fileShares[i].next = NULL;
605	}
606
607	find_directory(B_COMMON_SETTINGS_DIRECTORY, 0, false, path, sizeof(path));
608	strcat(path, "/BeServed-Settings");
609
610	fp = fopen(path, "r");
611	if (fp)
612	{
613		while (fgets(buffer, sizeof(buffer) - 1, fp))
614		{
615			length = strlen(buffer);
616			if (length <= 1 || buffer[0] == '#')
617				continue;
618
619			if (buffer[length - 1] == '\n')
620				buffer[--length] = 0;
621
622			if (strncmp(buffer, "share ", 6) == 0)
623				getFileShare(buffer);
624			else if (strncmp(buffer, "set ", 4) == 0)
625				getShareProperty(buffer);
626			else if (strncmp(buffer, "grant ", 6) == 0)
627				getGrant(buffer);
628			else if (strncmp(buffer, "authenticate ", 13) == 0)
629				getAuthenticate(buffer);
630		}
631
632		fclose(fp);
633	}
634}
635
636void getFileShare(const char *buffer)
637{
638	struct stat st;
639	char path[B_PATH_NAME_LENGTH], share[MAX_NAME_LENGTH + 1], *folder;
640	int i, tok;
641
642	// Skip over SHARE command.
643	tokPtr = (char *) buffer + (6 * sizeof(char));
644
645	tok = getToken();
646	if (tok != BT_TOKEN_STRING)
647		return;
648
649	strcpy(path, tokBuffer);
650	tok = getToken();
651	if (tok != BT_TOKEN_AS)
652		return;
653
654	tok = getToken();
655	if (tok != BT_TOKEN_STRING)
656		return;
657
658	strcpy(share, tokBuffer);
659
660	// Now verify that the share name specified has not already been
661	// used to share another path.
662	folder = btGetSharePath(share);
663	if (folder)
664	{
665		syslog(LOG_WARNING, "%s already defined as %s\n", share, folder);
666		return;
667	}
668
669	// Check the path to ensure its validity.
670	if (stat(path, &st) != 0)
671		return;
672
673	for (i = 0; i < BT_MAX_FILE_SHARES; i++)
674		if (!fileShares[i].used)
675		{
676			syslog(LOG_INFO, "Defining %s as %s\n", share, path);
677			strcpy(fileShares[i].name, share);
678			strcpy(fileShares[i].path, path);
679			fileShares[i].used = true;
680			return;
681		}
682
683	syslog(LOG_WARNING, "Share %s could not be defined (too many shares)\n", share);
684}
685
686void getShareProperty(const char *buffer)
687{
688	char share[B_FILE_NAME_LENGTH + 1];
689	int tok, shareId;
690
691	// Skip over SET command.
692	tokPtr = (char *) buffer + (4 * sizeof(char));
693
694	tok = getToken();
695	if (tok != BT_TOKEN_STRING)
696		return;
697
698	strcpy(share, tokBuffer);
699
700	// Get the index of the share referred to.  If the named share cannot be
701	// found, then abort.
702	shareId = btGetShareId(share);
703	if (shareId < 0)
704		return;
705
706	tok = getToken();
707	if (tok == BT_TOKEN_READWRITE)
708	{
709		fileShares[shareId].readOnly = false;
710		syslog(LOG_INFO, "%s permits writing\n", share);
711	}
712}
713
714void getGrant(const char *buffer)
715{
716	char share[MAX_NAME_LENGTH + 1];
717	int tok, rights;
718	bool isGroup = false;
719
720	// Skip over GRANT command.
721	tokPtr = (char *) buffer + (6 * sizeof(char));
722	rights = 0;
723
724	do
725	{
726		tok = getToken();
727		if (tok == BT_TOKEN_READ)
728		{
729			rights |= BT_RIGHTS_READ;
730			tok = getToken();
731		}
732		else if (tok == BT_TOKEN_WRITE)
733		{
734			rights |= BT_RIGHTS_WRITE;
735			tok = getToken();
736		}
737	} while (tok == BT_TOKEN_COMMA);
738
739	if (tok != BT_TOKEN_ON)
740		return;
741
742	tok = getToken();
743	if (tok != BT_TOKEN_STRING)
744		return;
745
746	strcpy(share, tokBuffer);
747	tok = getToken();
748	if (tok != BT_TOKEN_TO)
749		return;
750
751	tok = getToken();
752	if (tok == BT_TOKEN_GROUP)
753	{
754		isGroup = true;
755		tok = getToken();
756	}
757
758	if (tok != BT_TOKEN_STRING)
759		return;
760
761	addUserRights(share, tokBuffer, rights, isGroup);
762}
763
764void getAuthenticate(const char *buffer)
765{
766	struct hostent *ent;
767	int i, tok;
768
769	// Skip over AUTHENTICATE command.
770	tokPtr = (char *) buffer + (13 * sizeof(char));
771
772	tok = getToken();
773	if (tok != BT_TOKEN_WITH)
774		return;
775
776	tok = getToken();
777	if (tok != BT_TOKEN_STRING)
778		return;
779
780	// Look up address for given host.
781	getAuthServerAddress(tokBuffer);
782
783	// Make all file shares use BeSure authentication.
784	for (i = 0; i < BT_MAX_FILE_SHARES; i++)
785		fileShares[i].security = BT_AUTH_BESURE;
786
787	syslog(LOG_INFO, "Using authentication server at %s\n", tokBuffer);
788}
789
790bool getAuthServerAddress(const char *name)
791{
792	// Look up address for given host.
793	struct hostent *ent = gethostbyname(name);
794	if (ent == NULL)
795	{
796		syslog(LOG_ERR, "Authentication server %s is unavailable.\n", name);
797		return false;
798	}
799
800	strcpy(authServerName, name);
801	authServerIP = ntohl(*((unsigned int *) ent->h_addr));
802	return true;
803}
804
805void addUserRights(char *share, char *user, int rights, bool isGroup)
806{
807	bt_user_rights *ur;
808	int shareId;
809
810	shareId = btGetShareId(share);
811	if (shareId < 0)
812		return;
813
814	ur = (bt_user_rights *) malloc(sizeof(bt_user_rights));
815	if (ur)
816	{
817		ur->user = (char *) malloc(strlen(user) + 1);
818		if (ur->user)
819		{
820			strcpy(ur->user, user);
821			ur->rights = rights;
822			ur->isGroup = isGroup;
823			ur->next = fileShares[shareId].rights;
824			fileShares[shareId].rights = ur;
825		}
826		else
827			free(ur);
828	}
829}
830
831int getToken()
832{
833	bool quoted = false;
834
835	tokBuffer[0] = 0;
836	while (*tokPtr && iswhite(*tokPtr))
837		tokPtr++;
838
839	if (*tokPtr == ',')
840	{
841		tokPtr++;
842		return BT_TOKEN_COMMA;
843	}
844	else if (*tokPtr == '\"')
845	{
846		quoted = true;
847		tokPtr++;
848	}
849
850	if (isalnum(*tokPtr) || *tokPtr == '/')
851	{
852		int i = 0;
853		while (isalnum(*tokPtr) || isValid(*tokPtr) || (quoted && *tokPtr == ' '))
854			if (i < B_PATH_NAME_LENGTH)
855				tokBuffer[i++] = *tokPtr++;
856			else
857				tokPtr++;
858
859		tokBuffer[i] = 0;
860
861		if (!quoted)
862			for (i = 0; keywords[i]; i++)
863				if (strcasecmp(tokBuffer, keywords[i]) == 0)
864					return ++i;
865
866		if (quoted)
867			if (*tokPtr != '\"')
868				return BT_TOKEN_ERROR;
869			else
870				tokPtr++;
871
872		return BT_TOKEN_STRING;
873	}
874
875	return BT_TOKEN_ERROR;
876}
877
878void startService()
879{
880	struct sockaddr_in serverAddr, clientAddr;
881	int client, addrLen;
882	int flags;
883
884	// Store the length of the socket addressing structure for accept().
885	addrLen = sizeof(struct sockaddr_in);
886
887	// Initialize the server address structure.
888	memset(&serverAddr, 0, sizeof(serverAddr));
889	serverAddr.sin_port = htons(BT_TCPIP_PORT);
890	serverAddr.sin_family = AF_INET;
891	serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
892
893	// Create a new socket to receive incoming requests.
894	server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
895	if (server == INVALID_SOCKET)
896		return;
897
898	// Set the socket option to reuse the current address in case it was
899	// in use by a prior version of the service that has not yet relinquished
900	// the socket.
901	flags = 1;
902	setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
903
904	// Bind that socket to the address constructed above.
905	if (bind(server, (struct sockaddr *) &serverAddr, sizeof(serverAddr)))
906		return;
907
908	// Listen for incoming connections.
909	if (listen(server, 5))
910		return;
911
912	// Continually accept incoming connections.  When one is found,
913	// fire off a handler thread to accept commands.
914	while (running)
915	{
916		client = accept(server, (struct sockaddr *) &clientAddr, &addrLen);
917		if (client != INVALID_SOCKET)
918			launchThread(client, &clientAddr);
919	}
920
921	// Close the socket.  Technically, I believe we should call shutdown()
922	// first, but the BeOS header file socket.h indicates that this
923	// function is not currently working.  It is present but may not have
924	// any effect.
925	shutdown(server, 2);
926	closesocket(server);
927	server = INVALID_SOCKET;
928}
929
930void endService(int sig)
931{
932	// Close the syslog.
933	closelog();
934
935	if (hostThread > 0)
936		kill_thread(hostThread);
937
938	closeManagedData(&sessionData);
939	closeManagedData(&handleData);
940
941	freeFileHandles();
942	freeFileShares();
943
944	signal(SIGINT, SIG_DFL);
945	signal(SIGTERM, SIG_DFL);
946	signal(SIGHUP, SIG_DFL);
947	signal(SIGPIPE, SIG_DFL);
948	exit(0);
949}
950
951// freeFileHandles()
952//
953void freeFileHandles()
954{
955	bt_node *nextNode, *curNode = rootNode;
956
957	while (curNode)
958	{
959		nextNode = curNode->next;
960		free(curNode);
961		curNode = nextNode;
962	}
963}
964
965// freeFileShares()
966//
967void freeFileShares()
968{
969	bt_user_rights *ur, *nextUr;
970	int i;
971
972	for (i = 0; i < BT_MAX_FILE_SHARES; i++)
973		for (ur = fileShares[i].rights; ur; )
974		{
975			nextUr = ur->next;
976			if (ur->user)
977				free(ur->user);
978
979			free(ur);
980			ur = nextUr;
981		}
982}
983
984// launchThread()
985//
986void launchThread(int client, struct sockaddr_in *addr)
987{
988	bt_session_t *s, *cur, *last = NULL;
989	int count = 0;
990
991	// First verify that the server's not too busy by scanning the list of active
992	// sessions.  This is also useful because we need to eliminate unused sessions
993	// from the list, i.e., sessions that have closed.
994	beginWriting(&sessionData);
995
996	s = rootSession;
997	while (s)
998	{
999		if (s->socket == INVALID_SOCKET)
1000		{
1001			if (last)
1002				last->next = s->next;
1003			else
1004				rootSession = s->next;
1005
1006			cur = s->next;
1007			free(s);
1008			s = cur;
1009			continue;
1010		}
1011
1012		last = s;
1013		s = s->next;
1014		count++;
1015	}
1016
1017	// If the total number of valid sessions was less than our allowed maximum, then
1018	// we can create a new session.
1019	if (count < BT_MAX_THREADS)
1020	{
1021		// We need to create an available session for this connection.
1022		bt_session_t *session = (bt_session_t *) malloc(sizeof(bt_session_t));
1023		if (session)
1024		{
1025			session->socket = client;
1026			session->client_s_addr = addr->sin_addr.s_addr;
1027			session->rootBlock = NULL;
1028			session->killed = false;
1029			session->rights = 0;
1030
1031			session->handlerID =
1032				spawn_thread(requestThread, BT_THREAD_NAME, B_NORMAL_PRIORITY, session);
1033			resume_thread(session->handlerID);
1034
1035			// Add this to the session list.
1036			session->next = rootSession;
1037			rootSession = session;
1038			endWriting(&sessionData);
1039			return;
1040		}
1041	}
1042
1043	endWriting(&sessionData);
1044
1045	// We must have too many threads active, so let the client know we're busy.
1046	sendErrorToClient(client, 0, EBUSY);
1047	shutdown(client, 2);
1048	closesocket(client);
1049}
1050
1051int32 requestThread(void *data)
1052{
1053	bt_session_t *session = (bt_session_t *) data;
1054//	int flags;
1055
1056	if (!session)
1057		return 0;
1058
1059	// Ensure that this connection remains alive.  If a periodic message (handled by the OS)
1060	// fails, then blocked socket calls are interrupted and return with a ESIGPIPE error.
1061//	flags = 1;
1062//	setsockopt(server, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags));
1063
1064	if ((session->blockSem = create_sem(0, "Gathered Write Semaphore")) > B_OK)
1065	{
1066		while (!session->killed && receiveRequest(session));
1067		delete_sem(session->blockSem);
1068	}
1069
1070	shutdown(session->socket, 2);
1071	closesocket(session->socket);
1072	session->socket = INVALID_SOCKET;
1073	return 0;
1074}
1075
1076int receiveRequest(bt_session_t *session)
1077{
1078	bt_inPacket packet;
1079	char signature[20], *buffer;
1080	unsigned char command;
1081	int client, sigLen;
1082	int32 length;
1083
1084	client = session->socket;
1085
1086	// Read the BeTalk RPC header.
1087	sigLen = strlen(BT_RPC_SIGNATURE);
1088	if (btRecvMsg(client, signature, sigLen, 0) == -1)
1089		return 0;
1090//	recv(client, &verHi, sizeof(verHi), 0);
1091//	recv(client, &verLo, sizeof(verLo), 0);
1092
1093	signature[sigLen] = 0;
1094	if (strcmp(signature, BT_RPC_SIGNATURE))
1095		return 0;
1096
1097	// Read in the rest of the packet.
1098	if (btRecvMsg(client, &length, sizeof(int32), 0) == -1)
1099		return 0;
1100
1101	length = B_LENDIAN_TO_HOST_INT32(length);
1102	if (length == 0 || length > BT_RPC_MAX_PACKET_SIZE)
1103		return 0;
1104
1105	buffer = (char *) malloc(length + 1);
1106	if (!buffer)
1107		return 0;
1108
1109	if (btRecvMsg(client, buffer, length, 0) == -1)
1110	{
1111		free(buffer);
1112		return 0;
1113	}
1114
1115	buffer[length] = 0;
1116	packet.buffer = buffer;
1117	packet.length = length;
1118	packet.offset = 0;
1119
1120	// Read the transmission ID and command.
1121	command = btRPCGetChar(&packet);
1122	getArguments(session, &packet, command);
1123	free(buffer);
1124	return (command != BT_CMD_QUIT && command != BT_CMD_PREMOUNT);
1125}
1126
1127void getArguments(bt_session_t *session, bt_inPacket *packet, unsigned char command)
1128{
1129	bt_arg_t args[MAX_COMMAND_ARGS];
1130	int i, client;
1131	bool error;
1132	unsigned char argc, terminator;
1133	int32 xid;
1134
1135	error = false;
1136	client = session->socket;
1137	argc = btRPCGetChar(packet);
1138	if (argc > MAX_COMMAND_ARGS)
1139		return;
1140
1141	for (i = 0; i < argc && !error; i++)
1142	{
1143		args[i].type = btRPCGetInt32(packet);
1144		args[i].data = btRPCGetNewString(packet);
1145		if (args[i].data == NULL)
1146			error = true;
1147	}
1148
1149	if (!error)
1150	{
1151		xid = btRPCGetInt32(packet);
1152		terminator = btRPCGetChar(packet);
1153		if (terminator == BT_CMD_TERMINATOR)
1154			handleRequest(session, xid, command, argc, args);
1155	}
1156	else
1157		sendErrorToClient(session->socket, 0, EINVAL);
1158
1159	while (--i >= 0)
1160		free(args[i].data);
1161}
1162
1163void handleRequest(bt_session_t *session, unsigned int xid, unsigned char command, int argc, bt_arg_t argv[])
1164{
1165	bool validated = true;
1166	int i, j;
1167
1168	for (i = 0; dirCommands[i].handler; i++)
1169		if (command == dirCommands[i].command)
1170		{
1171			// We may have received a valid command, but one that is not supported by this
1172			// server.  In this case, we'll want to return an operation not supported error,
1173			// as opposed to an invalid command error.
1174			if (!dirCommands[i].supported)
1175			{
1176				sendErrorToClient(session->socket, xid, EOPNOTSUPP);
1177				return;
1178			}
1179
1180			// Now verify that the argument count is correct, and if so, the type of all
1181			// arguments is correct.  If not, an invalid command error is returned.
1182			// Otherise, the command is executed, and the handler returns any necessary
1183			// acknowledgement.
1184			if (argc == dirCommands[i].args)
1185			{
1186				for (j = 0; j < argc; j++)
1187					if (dirCommands[i].argTypes[j] != argv[j].type)
1188					{
1189						validated = false;
1190						break;
1191					}
1192
1193				if (validated)
1194				{
1195					(*dirCommands[i].handler)(session, xid, argc, argv);
1196					return;
1197				}
1198			}
1199		}
1200
1201	sendErrorToClient(session->socket, xid, EINVAL);
1202}
1203
1204void sendErrorToClient(int client, unsigned int xid, int error)
1205{
1206	bt_outPacket packet;
1207	btRPCCreateAck(&packet, xid, error);
1208	btRPCSendAck(client, &packet);
1209}
1210
1211int btRecvMsg(int sock, void *data, int dataLen, int flags)
1212{
1213	int bytesRead = 0;
1214	do
1215	{
1216		int bytes = btRecv(sock, (char *) data + bytesRead, dataLen - bytesRead, flags);
1217		if (bytes == -1)
1218			return -1;
1219
1220		bytesRead += bytes;
1221	} while (bytesRead < dataLen);
1222
1223	return bytesRead;
1224}
1225
1226// btRecv()
1227//
1228int btRecv(int sock, void *data, int dataLen, int flags)
1229{
1230	int bytes;
1231
1232	for (;;)
1233	{
1234		bytes = recv(sock, data, dataLen, flags);
1235		if (bytes == 0)
1236			return -1;
1237		else if (bytes == -1)
1238			if (errno == EINTR)
1239				continue;
1240			else
1241				return -1;
1242		else
1243			break;
1244	}
1245
1246	return bytes;
1247}
1248
1249int btSendMsg(int sock, void *data, int dataLen, int flags)
1250{
1251	int bytesSent = 0;
1252	do
1253	{
1254		int bytes = btSend(sock, (char *) data + bytesSent, dataLen - bytesSent, flags);
1255		if (bytes == -1)
1256			return -1;
1257
1258		bytesSent += bytes;
1259	} while (bytesSent < dataLen);
1260
1261	return bytesSent;
1262}
1263
1264// btSend()
1265//
1266int btSend(int sock, void *data, int dataLen, int flags)
1267{
1268	int bytes;
1269
1270	for (;;)
1271	{
1272		bytes = send(sock, data, dataLen, flags);
1273		if (bytes == -1)
1274			if (errno == EINTR)
1275				continue;
1276			else
1277				return -1;
1278		else
1279			break;
1280	}
1281
1282	return bytes;
1283}
1284
1285void btRPCCreateAck(bt_outPacket *packet, unsigned int xid, int error)
1286{
1287	packet->size = BT_RPC_MIN_PACKET_SIZE;
1288	packet->buffer = (char *) malloc(packet->size);
1289	packet->length = 0;
1290
1291	if (!packet->buffer)
1292		return;
1293
1294	strcpy(packet->buffer, BT_RPC_SIGNATURE);
1295	packet->length += strlen(BT_RPC_SIGNATURE);
1296	btRPCPutInt32(packet, xid);
1297	btRPCPutInt32(packet, 0);
1298	btRPCPutInt32(packet, error);
1299}
1300
1301void btRPCSendAck(int client, bt_outPacket *packet)
1302{
1303	if (packet)
1304		if (packet->buffer)
1305		{
1306			*(int32 *)(&packet->buffer[9]) = packet->length - 13;
1307			btSendMsg(client, packet->buffer, packet->length, 0);
1308			free(packet->buffer);
1309		}
1310}
1311
1312unsigned char btRPCGetChar(bt_inPacket *packet)
1313{
1314	unsigned char value;
1315
1316	if (packet->offset < packet->length)
1317		value = (unsigned char) packet->buffer[packet->offset];
1318	else
1319		value = 0;
1320
1321	packet->offset += sizeof(value);
1322	return value;
1323}
1324
1325unsigned int btRPCGetInt32(bt_inPacket *packet)
1326{
1327	int32 value;
1328
1329	if (packet->offset < packet->length)
1330		value = B_LENDIAN_TO_HOST_INT32(*((int32 *) &packet->buffer[packet->offset]));
1331	else
1332		value = 0;
1333
1334	packet->offset += sizeof(value);
1335	return value;
1336}
1337
1338int64 btRPCGetInt64(bt_inPacket *packet)
1339{
1340	int64 value;
1341
1342	if (packet->offset < packet->length)
1343		value = B_LENDIAN_TO_HOST_INT64(*((int64 *) &packet->buffer[packet->offset]));
1344	else
1345		value = 0;
1346
1347	packet->offset += sizeof(value);
1348	return value;
1349}
1350
1351char *btRPCGetNewString(bt_inPacket *packet)
1352{
1353	char *str;
1354	unsigned int bytes;
1355
1356	if (packet->offset >= packet->length)
1357		return NULL;
1358
1359	bytes = B_LENDIAN_TO_HOST_INT32(*((int32 *) &packet->buffer[packet->offset]));
1360	packet->offset += sizeof(bytes);
1361	if (bytes < 0 || bytes > BT_MAX_IO_BUFFER)
1362		return NULL;
1363
1364	str = (char *) malloc(bytes + 1);
1365	if (!str)
1366		return NULL;
1367
1368	if (bytes > 0)
1369		memcpy(str, &packet->buffer[packet->offset], bytes);
1370
1371	str[bytes] = 0;
1372	packet->offset += bytes;
1373
1374	return str;
1375}
1376
1377int btRPCGetString(bt_inPacket *packet, char *buffer, int length)
1378{
1379	unsigned int bytes;
1380
1381	if (packet->offset >= packet->length)
1382		return ERANGE;
1383
1384	bytes = B_LENDIAN_TO_HOST_INT32(*((int32 *) &packet->buffer[packet->offset]));
1385	packet->offset += sizeof(bytes);
1386	if (bytes < 0 || bytes > BT_MAX_IO_BUFFER)
1387		return ERANGE;
1388
1389	if (length < bytes)
1390		return ERANGE;
1391
1392	if (bytes > 0)
1393		memcpy(buffer, &packet->buffer[packet->offset], bytes);
1394
1395	packet->offset += bytes;
1396	return bytes;
1397}
1398
1399void btRPCGrowPacket(bt_outPacket *packet, int bytes)
1400{
1401	if (packet->length + bytes > packet->size)
1402	{
1403		int growth = ((bytes / BT_RPC_MIN_PACKET_SIZE) + 1) * BT_RPC_MIN_PACKET_SIZE;
1404		packet->buffer = (char *) realloc(packet->buffer, packet->size + growth);
1405		packet->size += growth;
1406	}
1407}
1408
1409void btRPCPutChar(bt_outPacket *packet, char value)
1410{
1411	btRPCGrowPacket(packet, sizeof(value));
1412	packet->buffer[packet->length] = value;
1413	packet->length += sizeof(value);
1414}
1415
1416void btRPCPutInt32(bt_outPacket *packet, int32 value)
1417{
1418	btRPCGrowPacket(packet, sizeof(value));
1419	*(int32 *)(&packet->buffer[packet->length]) = B_HOST_TO_LENDIAN_INT32(value);
1420	packet->length += sizeof(value);
1421}
1422
1423void btRPCPutInt64(bt_outPacket *packet, int64 value)
1424{
1425	btRPCGrowPacket(packet, sizeof(value));
1426	*(int64 *)(&packet->buffer[packet->length]) = B_HOST_TO_LENDIAN_INT64(value);
1427	packet->length += sizeof(value);
1428}
1429
1430void btRPCPutString(bt_outPacket *packet, char *buffer, int length)
1431{
1432	if (packet && buffer)
1433	{
1434		btRPCGrowPacket(packet, sizeof(length) + length);
1435		btRPCPutInt32(packet, length);
1436		memcpy(&packet->buffer[packet->length], buffer, length);
1437		packet->length += length;
1438	}
1439}
1440
1441void btRPCPutBinary(bt_outPacket *packet, void *buffer, int length)
1442{
1443	if (packet && buffer)
1444	{
1445		btRPCGrowPacket(packet, length);
1446		memcpy(&packet->buffer[packet->length], buffer, length);
1447		packet->length += length;
1448	}
1449}
1450
1451int btRPCGetStat(bt_inPacket *packet, struct stat *st)
1452{
1453	st->st_dev = 0;
1454	st->st_nlink = btRPCGetInt32(packet);
1455	st->st_uid = btRPCGetInt32(packet);
1456	st->st_gid = btRPCGetInt32(packet);
1457	st->st_size = btRPCGetInt64(packet);
1458	st->st_blksize = btRPCGetInt32(packet);
1459	st->st_rdev = btRPCGetInt32(packet);
1460	st->st_ino = btRPCGetInt64(packet);
1461	st->st_mode = btRPCGetInt32(packet);
1462	st->st_atime = btRPCGetInt32(packet);
1463	st->st_mtime = btRPCGetInt32(packet);
1464	st->st_ctime = btRPCGetInt32(packet);
1465}
1466
1467void btRPCPutStat(bt_outPacket *packet, struct stat *st)
1468{
1469	if (packet && st)
1470	{
1471		btRPCPutInt32(packet, (int) st->st_nlink);
1472		btRPCPutInt32(packet, (int) st->st_uid);
1473		btRPCPutInt32(packet, (int) st->st_gid);
1474		btRPCPutInt64(packet, (int64) st->st_size);
1475		btRPCPutInt32(packet, (int) st->st_blksize);
1476		btRPCPutInt32(packet, (int) st->st_rdev);
1477		btRPCPutInt64(packet, (int64) st->st_ino);
1478		btRPCPutInt32(packet, (int) st->st_mode);
1479		btRPCPutInt32(packet, (int) st->st_atime);
1480		btRPCPutInt32(packet, (int) st->st_mtime);
1481		btRPCPutInt32(packet, (int) st->st_ctime);
1482	}
1483}
1484
1485////////////////////////////////////////////////////////////////////
1486