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