1 /* NFSv4.1 client for Windows 2 * Copyright � 2012 The Regents of the University of Michigan 3 * 4 * Olga Kornievskaia <aglo@umich.edu> 5 * Casey Bodley <cbodley@umich.edu> 6 * 7 * This library is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU Lesser General Public License as published by 9 * the Free Software Foundation; either version 2.1 of the License, or (at 10 * your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, but 13 * without any warranty; without even the implied warranty of merchantability 14 * or fitness for a particular purpose. See the GNU Lesser General Public 15 * License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public License 18 * along with this library; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 */ 21 22 #include <windows.h> 23 #include <process.h> 24 #include <tchar.h> 25 #include <stdio.h> 26 27 #include <devioctl.h> 28 #include <lmcons.h> /* UNLEN for GetUserName() */ 29 #include <iphlpapi.h> /* for GetNetworkParam() */ 30 #include "nfs41_driver.h" /* for NFS41_USER_DEVICE_NAME_A */ 31 #include "nfs41_np.h" /* for NFS41NP_SHARED_MEMORY */ 32 33 #include "idmap.h" 34 #include "daemon_debug.h" 35 #include "upcall.h" 36 #include "util.h" 37 38 #define MAX_NUM_THREADS 128 39 DWORD NFS41D_VERSION = 0; 40 41 static const char FILE_NETCONFIG[] = "C:\\ReactOS\\System32\\drivers\\etc\\netconfig"; 42 43 /* Globals */ 44 char localdomain_name[NFS41_HOSTNAME_LEN]; 45 int default_uid = 666; 46 int default_gid = 777; 47 48 #ifndef STANDALONE_NFSD //make sure to define it in "sources" not here 49 #include "service.h" 50 HANDLE stop_event = NULL; 51 #endif 52 typedef struct _nfs41_process_thread { 53 HANDLE handle; 54 uint32_t tid; 55 } nfs41_process_thread; 56 57 static int map_user_to_ids(nfs41_idmapper *idmapper, uid_t *uid, gid_t *gid) 58 { 59 char username[UNLEN + 1]; 60 DWORD len = UNLEN + 1; 61 int status = NO_ERROR; 62 63 if (!GetUserNameA(username, &len)) { 64 status = GetLastError(); 65 eprintf("GetUserName() failed with %d\n", status); 66 goto out; 67 } 68 dprintf(1, "map_user_to_ids: mapping user %s\n", username); 69 70 if (nfs41_idmap_name_to_ids(idmapper, username, uid, gid)) { 71 /* instead of failing for auth_sys, fall back to 'nobody' uid/gid */ 72 *uid = default_uid; 73 *gid = default_gid; 74 } 75 out: 76 return status; 77 } 78 79 static unsigned int WINAPI thread_main(void *args) 80 { 81 nfs41_idmapper *idmapper = (nfs41_idmapper*)args; 82 DWORD status = 0; 83 HANDLE pipe; 84 // buffer used to process upcall, assumed to be fixed size. 85 // if we ever need to handle non-cached IO, need to make it dynamic 86 unsigned char outbuf[UPCALL_BUF_SIZE], inbuf[UPCALL_BUF_SIZE]; 87 DWORD inbuf_len = UPCALL_BUF_SIZE, outbuf_len; 88 nfs41_upcall upcall; 89 90 pipe = CreateFile(NFS41_USER_DEVICE_NAME_A, GENERIC_READ | GENERIC_WRITE, 91 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 92 0, NULL); 93 if (pipe == INVALID_HANDLE_VALUE) 94 { 95 eprintf("Unable to open upcall pipe %d\n", GetLastError()); 96 return GetLastError(); 97 } 98 99 while(1) { 100 status = DeviceIoControl(pipe, IOCTL_NFS41_READ, NULL, 0, 101 outbuf, UPCALL_BUF_SIZE, (LPDWORD)&outbuf_len, NULL); 102 if (!status) { 103 eprintf("IOCTL_NFS41_READ failed %d\n", GetLastError()); 104 continue; 105 } 106 107 status = upcall_parse(outbuf, (uint32_t)outbuf_len, &upcall); 108 if (status) { 109 upcall.status = status; 110 goto write_downcall; 111 } 112 113 /* map username to uid/gid */ 114 status = map_user_to_ids(idmapper, &upcall.uid, &upcall.gid); 115 if (status) { 116 upcall.status = status; 117 goto write_downcall; 118 } 119 120 if (upcall.opcode == NFS41_SHUTDOWN) { 121 printf("Shutting down..\n"); 122 exit(0); 123 } 124 125 status = upcall_handle(&upcall); 126 127 write_downcall: 128 dprintf(1, "writing downcall: xid=%lld opcode=%s status=%d " 129 "get_last_error=%d\n", upcall.xid, opcode2string(upcall.opcode), 130 upcall.status, upcall.last_error); 131 132 upcall_marshall(&upcall, inbuf, (uint32_t)inbuf_len, (uint32_t*)&outbuf_len); 133 134 dprintf(2, "making a downcall: outbuf_len %ld\n\n", outbuf_len); 135 status = DeviceIoControl(pipe, IOCTL_NFS41_WRITE, 136 inbuf, inbuf_len, NULL, 0, (LPDWORD)&outbuf_len, NULL); 137 if (!status) { 138 eprintf("IOCTL_NFS41_WRITE failed with %d xid=%lld opcode=%s\n", 139 GetLastError(), upcall.xid, opcode2string(upcall.opcode)); 140 upcall_cancel(&upcall); 141 } 142 if (upcall.status != NFSD_VERSION_MISMATCH) 143 upcall_cleanup(&upcall); 144 } 145 CloseHandle(pipe); 146 147 return GetLastError(); 148 } 149 150 #ifndef STANDALONE_NFSD 151 VOID ServiceStop() 152 { 153 if (stop_event) 154 SetEvent(stop_event); 155 } 156 #endif 157 158 typedef struct _nfsd_args { 159 bool_t ldap_enable; 160 int debug_level; 161 } nfsd_args; 162 163 static bool_t check_for_files() 164 { 165 FILE *fd; 166 167 fd = fopen(FILE_NETCONFIG, "r"); 168 if (fd == NULL) { 169 fprintf(stderr,"nfsd() failed to open file '%s'\n", FILE_NETCONFIG); 170 return FALSE; 171 } 172 fclose(fd); 173 return TRUE; 174 } 175 176 static void PrintUsage() 177 { 178 fprintf(stderr, "Usage: nfsd.exe -d <debug_level> --noldap " 179 "--uid <non-zero value> --gid\n"); 180 } 181 static bool_t parse_cmdlineargs(int argc, TCHAR *argv[], nfsd_args *out) 182 { 183 int i; 184 185 /* set defaults. */ 186 out->debug_level = 1; 187 out->ldap_enable = TRUE; 188 189 /* parse command line */ 190 for (i = 1; i < argc; i++) { 191 if (argv[i][0] == TEXT('-')) { 192 if (_tcscmp(argv[i], TEXT("-h")) == 0) { /* help */ 193 PrintUsage(); 194 return FALSE; 195 } 196 else if (_tcscmp(argv[i], TEXT("-d")) == 0) { /* debug level */ 197 ++i; 198 if (i >= argc) { 199 fprintf(stderr, "Missing debug level value\n"); 200 PrintUsage(); 201 return FALSE; 202 } 203 out->debug_level = _ttoi(argv[i]); 204 } 205 else if (_tcscmp(argv[i], TEXT("--noldap")) == 0) { /* no LDAP */ 206 out->ldap_enable = FALSE; 207 } 208 else if (_tcscmp(argv[i], TEXT("--uid")) == 0) { /* no LDAP, setting default uid */ 209 ++i; 210 if (i >= argc) { 211 fprintf(stderr, "Missing uid value\n"); 212 PrintUsage(); 213 return FALSE; 214 } 215 default_uid = _ttoi(argv[i]); 216 if (!default_uid) { 217 fprintf(stderr, "Invalid (or missing) anonymous uid value of %d\n", 218 default_uid); 219 return FALSE; 220 } 221 } 222 else if (_tcscmp(argv[i], TEXT("--gid")) == 0) { /* no LDAP, setting default gid */ 223 ++i; 224 if (i >= argc) { 225 fprintf(stderr, "Missing gid value\n"); 226 PrintUsage(); 227 return FALSE; 228 } 229 default_gid = _ttoi(argv[i]); 230 } 231 else 232 fprintf(stderr, "Unrecognized option '%s', disregarding.\n", argv[i]); 233 } 234 } 235 fprintf(stdout, "parse_cmdlineargs: debug_level %d ldap is %d\n", 236 out->debug_level, out->ldap_enable); 237 return TRUE; 238 } 239 240 static void print_getaddrinfo(struct addrinfo *ptr) 241 { 242 char ipstringbuffer[46]; 243 DWORD ipbufferlength = 46; 244 245 dprintf(1, "getaddrinfo response flags: 0x%x\n", ptr->ai_flags); 246 switch (ptr->ai_family) { 247 case AF_UNSPEC: dprintf(1, "Family: Unspecified\n"); break; 248 case AF_INET: 249 dprintf(1, "Family: AF_INET IPv4 address %s\n", 250 inet_ntoa(((struct sockaddr_in *)ptr->ai_addr)->sin_addr)); 251 break; 252 case AF_INET6: 253 if (WSAAddressToString((LPSOCKADDR)ptr->ai_addr, (DWORD)ptr->ai_addrlen, 254 NULL, ipstringbuffer, &ipbufferlength)) 255 dprintf(1, "WSAAddressToString failed with %u\n", WSAGetLastError()); 256 else 257 dprintf(1, "Family: AF_INET6 IPv6 address %s\n", ipstringbuffer); 258 break; 259 case AF_NETBIOS: dprintf(1, "AF_NETBIOS (NetBIOS)\n"); break; 260 default: dprintf(1, "Other %ld\n", ptr->ai_family); break; 261 } 262 dprintf(1, "Canonical name: %s\n", ptr->ai_canonname); 263 } 264 265 static int getdomainname() 266 { 267 int status = 0; 268 PFIXED_INFO net_info = NULL; 269 DWORD size = 0; 270 BOOLEAN flag = FALSE; 271 272 status = GetNetworkParams(net_info, &size); 273 if (status != ERROR_BUFFER_OVERFLOW) { 274 eprintf("getdomainname: GetNetworkParams returned %d\n", status); 275 goto out; 276 } 277 net_info = calloc(1, size); 278 if (net_info == NULL) { 279 status = GetLastError(); 280 goto out; 281 } 282 status = GetNetworkParams(net_info, &size); 283 if (status) { 284 eprintf("getdomainname: GetNetworkParams returned %d\n", status); 285 goto out_free; 286 } 287 288 if (net_info->DomainName[0] == '\0') { 289 struct addrinfo *result = NULL, *ptr = NULL, hints = { 0 }; 290 char hostname[NI_MAXHOST], servInfo[NI_MAXSERV]; 291 292 hints.ai_socktype = SOCK_STREAM; 293 hints.ai_protocol = IPPROTO_TCP; 294 295 status = getaddrinfo(net_info->HostName, NULL, &hints, &result); 296 if (status) { 297 status = WSAGetLastError(); 298 eprintf("getdomainname: getaddrinfo failed with %d\n", status); 299 goto out_free; 300 } 301 302 for (ptr=result; ptr != NULL; ptr=ptr->ai_next) { 303 print_getaddrinfo(ptr); 304 305 switch (ptr->ai_family) { 306 case AF_INET6: 307 case AF_INET: 308 status = getnameinfo((struct sockaddr *)ptr->ai_addr, 309 (socklen_t)ptr->ai_addrlen, hostname, NI_MAXHOST, 310 servInfo, NI_MAXSERV, NI_NAMEREQD); 311 if (status) 312 dprintf(1, "getnameinfo failed %d\n", WSAGetLastError()); 313 else { 314 size_t i, len = strlen(hostname); 315 char *p = hostname; 316 dprintf(1, "getdomainname: hostname %s %d\n", hostname, len); 317 for (i = 0; i < len; i++) 318 if (p[i] == '.') 319 break; 320 if (i == len) 321 break; 322 flag = TRUE; 323 memcpy(localdomain_name, &hostname[i+1], len-i); 324 dprintf(1, "getdomainname: domainname %s %d\n", 325 localdomain_name, strlen(localdomain_name)); 326 goto out_loop; 327 } 328 break; 329 default: 330 break; 331 } 332 } 333 out_loop: 334 if (!flag) { 335 status = ERROR_INTERNAL_ERROR; 336 eprintf("getdomainname: unable to get a domain name. " 337 "Set this machine's domain name:\n" 338 "System > ComputerName > Change > More > mydomain\n"); 339 } 340 freeaddrinfo(result); 341 } else { 342 dprintf(1, "domain name is %s\n", net_info->DomainName); 343 memcpy(localdomain_name, net_info->DomainName, 344 strlen(net_info->DomainName)); 345 localdomain_name[strlen(net_info->DomainName)] = '\0'; 346 } 347 out_free: 348 free(net_info); 349 out: 350 return status; 351 } 352 353 #ifdef STANDALONE_NFSD 354 void __cdecl _tmain(int argc, TCHAR *argv[]) 355 #else 356 VOID ServiceStart(DWORD argc, LPTSTR *argv) 357 #endif 358 { 359 DWORD status = 0, len; 360 // handle to our drivers 361 HANDLE pipe; 362 nfs41_process_thread tids[MAX_NUM_THREADS]; 363 nfs41_idmapper *idmapper = NULL; 364 int i; 365 nfsd_args cmd_args; 366 367 if (!check_for_files()) 368 exit(0); 369 if (!parse_cmdlineargs(argc, argv, &cmd_args)) 370 exit(0); 371 set_debug_level(cmd_args.debug_level); 372 open_log_files(); 373 374 #ifdef __REACTOS__ 375 /* Start the kernel part */ 376 { 377 HANDLE hSvcMan = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); 378 if (hSvcMan) 379 { 380 HANDLE hSvc = OpenService(hSvcMan, "nfs41_driver", SERVICE_ALL_ACCESS); 381 if (hSvc) 382 { 383 SERVICE_STATUS SvcSt; 384 QueryServiceStatus(hSvc, &SvcSt); 385 if (SvcSt.dwCurrentState != SERVICE_RUNNING) 386 { 387 if (StartService(hSvc, 0, NULL)) 388 { 389 dprintf(1, "NFS41 driver started\n"); 390 } 391 else 392 { 393 eprintf("Driver failed to start: %d\n", GetLastError()); 394 } 395 } 396 else 397 { 398 eprintf("Driver in state: %x\n", SvcSt.dwCurrentState); 399 } 400 401 CloseServiceHandle(hSvc); 402 } 403 else 404 { 405 eprintf("Failed to open service: %d\n", GetLastError()); 406 } 407 408 CloseServiceHandle(hSvcMan); 409 } 410 else 411 { 412 eprintf("Failed to open service manager: %d\n", GetLastError()); 413 } 414 } 415 #endif 416 417 #ifdef _DEBUG 418 /* dump memory leaks to stderr on exit; this requires the debug heap, 419 /* available only when built in debug mode under visual studio -cbodley */ 420 _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); 421 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); 422 #pragma warning (push) 423 #pragma warning (disable : 4306) /* conversion from 'int' to '_HFILE' of greater size */ 424 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); 425 #pragma warning (pop) 426 dprintf(1, "debug mode. dumping memory leaks to stderr on exit.\n"); 427 #endif 428 /* acquire and store in global memory current dns domain name. 429 * needed for acls */ 430 if (getdomainname()) 431 exit(0); 432 433 nfs41_server_list_init(); 434 435 if (cmd_args.ldap_enable) { 436 status = nfs41_idmap_create(&idmapper); 437 if (status) { 438 eprintf("id mapping initialization failed with %d\n", status); 439 goto out_logs; 440 } 441 } 442 443 NFS41D_VERSION = GetTickCount(); 444 dprintf(1, "NFS41 Daemon starting: version %d\n", NFS41D_VERSION); 445 446 pipe = CreateFile(NFS41_USER_DEVICE_NAME_A, GENERIC_READ | GENERIC_WRITE, 447 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 448 0, NULL); 449 if (pipe == INVALID_HANDLE_VALUE) 450 { 451 eprintf("Unable to open upcall pipe %d\n", GetLastError()); 452 goto out_idmap; 453 } 454 455 dprintf(1, "starting nfs41 mini redirector\n"); 456 status = DeviceIoControl(pipe, IOCTL_NFS41_START, 457 &NFS41D_VERSION, sizeof(DWORD), NULL, 0, (LPDWORD)&len, NULL); 458 if (!status) { 459 eprintf("IOCTL_NFS41_START failed with %d\n", 460 GetLastError()); 461 goto out_pipe; 462 } 463 464 #ifndef STANDALONE_NFSD 465 stop_event = CreateEvent(NULL, TRUE, FALSE, NULL); 466 if (stop_event == NULL) 467 goto out_pipe; 468 #endif 469 470 for (i = 0; i < MAX_NUM_THREADS; i++) { 471 tids[i].handle = (HANDLE)_beginthreadex(NULL, 0, thread_main, 472 idmapper, 0, &tids[i].tid); 473 if (tids[i].handle == INVALID_HANDLE_VALUE) { 474 status = GetLastError(); 475 eprintf("_beginthreadex failed %d\n", status); 476 goto out_pipe; 477 } 478 } 479 #ifndef STANDALONE_NFSD 480 // report the status to the service control manager. 481 if (!ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0)) 482 goto out_pipe; 483 WaitForSingleObject(stop_event, INFINITE); 484 #else 485 //This can be changed to waiting on an array of handles and using waitformultipleobjects 486 dprintf(1, "Parent waiting for children threads\n"); 487 for (i = 0; i < MAX_NUM_THREADS; i++) 488 WaitForSingleObject(tids[i].handle, INFINITE ); 489 #endif 490 dprintf(1, "Parent woke up!!!!\n"); 491 492 out_pipe: 493 CloseHandle(pipe); 494 out_idmap: 495 if (idmapper) nfs41_idmap_free(idmapper); 496 out_logs: 497 #ifndef STANDALONE_NFSD 498 close_log_files(); 499 #endif 500 return; 501 } 502