1 /* NFSv4.1 client for Windows
2 * Copyright © 2012 The Regents of the University of Michigan
4 * Olga Kornievskaia <aglo@umich.edu>
5 * Casey Bodley <cbodley@umich.edu>
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.
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.
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
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 */
34 #include "daemon_debug.h"
38 #define MAX_NUM_THREADS 128
39 DWORD NFS41D_VERSION
= 0;
41 static const char FILE_NETCONFIG
[] = "C:\\ReactOS\\System32\\drivers\\etc\\netconfig";
44 char localdomain_name
[NFS41_HOSTNAME_LEN
];
45 int default_uid
= 666;
46 int default_gid
= 777;
48 #ifndef STANDALONE_NFSD //make sure to define it in "sources" not here
50 HANDLE stop_event
= NULL
;
52 typedef struct _nfs41_process_thread
{
55 } nfs41_process_thread
;
57 static int map_user_to_ids(nfs41_idmapper
*idmapper
, uid_t
*uid
, gid_t
*gid
)
59 char username
[UNLEN
+ 1];
60 DWORD len
= UNLEN
+ 1;
61 int status
= NO_ERROR
;
63 if (!GetUserNameA(username
, &len
)) {
64 status
= GetLastError();
65 eprintf("GetUserName() failed with %d\n", status
);
68 dprintf(1, "map_user_to_ids: mapping user %s\n", username
);
70 if (nfs41_idmap_name_to_ids(idmapper
, username
, uid
, gid
)) {
71 /* instead of failing for auth_sys, fall back to 'nobody' uid/gid */
79 static unsigned int WINAPI
thread_main(void *args
)
81 nfs41_idmapper
*idmapper
= (nfs41_idmapper
*)args
;
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
;
90 pipe
= CreateFile(NFS41_USER_DEVICE_NAME_A
, GENERIC_READ
| GENERIC_WRITE
,
91 FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
,
93 if (pipe
== INVALID_HANDLE_VALUE
)
95 eprintf("Unable to open upcall pipe %d\n", GetLastError());
96 return GetLastError();
100 status
= DeviceIoControl(pipe
, IOCTL_NFS41_READ
, NULL
, 0,
101 outbuf
, UPCALL_BUF_SIZE
, (LPDWORD
)&outbuf_len
, NULL
);
103 eprintf("IOCTL_NFS41_READ failed %d\n", GetLastError());
107 status
= upcall_parse(outbuf
, (uint32_t)outbuf_len
, &upcall
);
109 upcall
.status
= status
;
113 /* map username to uid/gid */
114 status
= map_user_to_ids(idmapper
, &upcall
.uid
, &upcall
.gid
);
116 upcall
.status
= status
;
120 if (upcall
.opcode
== NFS41_SHUTDOWN
) {
121 printf("Shutting down..\n");
125 status
= upcall_handle(&upcall
);
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
);
132 upcall_marshall(&upcall
, inbuf
, (uint32_t)inbuf_len
, (uint32_t*)&outbuf_len
);
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
);
138 eprintf("IOCTL_NFS41_WRITE failed with %d xid=%lld opcode=%s\n",
139 GetLastError(), upcall
.xid
, opcode2string(upcall
.opcode
));
140 upcall_cancel(&upcall
);
142 if (upcall
.status
!= NFSD_VERSION_MISMATCH
)
143 upcall_cleanup(&upcall
);
147 return GetLastError();
150 #ifndef STANDALONE_NFSD
154 SetEvent(stop_event
);
158 typedef struct _nfsd_args
{
163 static bool_t
check_for_files()
167 fd
= fopen(FILE_NETCONFIG
, "r");
169 fprintf(stderr
,"nfsd() failed to open file '%s'\n", FILE_NETCONFIG
);
176 static void PrintUsage()
178 fprintf(stderr
, "Usage: nfsd.exe -d <debug_level> --noldap "
179 "--uid <non-zero value> --gid\n");
181 static bool_t
parse_cmdlineargs(int argc
, TCHAR
*argv
[], nfsd_args
*out
)
186 out
->debug_level
= 1;
187 out
->ldap_enable
= TRUE
;
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 */
196 else if (_tcscmp(argv
[i
], TEXT("-d")) == 0) { /* debug level */
199 fprintf(stderr
, "Missing debug level value\n");
203 out
->debug_level
= _ttoi(argv
[i
]);
205 else if (_tcscmp(argv
[i
], TEXT("--noldap")) == 0) { /* no LDAP */
206 out
->ldap_enable
= FALSE
;
208 else if (_tcscmp(argv
[i
], TEXT("--uid")) == 0) { /* no LDAP, setting default uid */
211 fprintf(stderr
, "Missing uid value\n");
215 default_uid
= _ttoi(argv
[i
]);
217 fprintf(stderr
, "Invalid (or missing) anonymous uid value of %d\n",
222 else if (_tcscmp(argv
[i
], TEXT("--gid")) == 0) { /* no LDAP, setting default gid */
225 fprintf(stderr
, "Missing gid value\n");
229 default_gid
= _ttoi(argv
[i
]);
232 fprintf(stderr
, "Unrecognized option '%s', disregarding.\n", argv
[i
]);
235 fprintf(stdout
, "parse_cmdlineargs: debug_level %d ldap is %d\n",
236 out
->debug_level
, out
->ldap_enable
);
240 static void print_getaddrinfo(struct addrinfo
*ptr
)
242 char ipstringbuffer
[46];
243 DWORD ipbufferlength
= 46;
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;
249 dprintf(1, "Family: AF_INET IPv4 address %s\n",
250 inet_ntoa(((struct sockaddr_in
*)ptr
->ai_addr
)->sin_addr
));
253 if (WSAAddressToString((LPSOCKADDR
)ptr
->ai_addr
, (DWORD
)ptr
->ai_addrlen
,
254 NULL
, ipstringbuffer
, &ipbufferlength
))
255 dprintf(1, "WSAAddressToString failed with %u\n", WSAGetLastError());
257 dprintf(1, "Family: AF_INET6 IPv6 address %s\n", ipstringbuffer
);
259 case AF_NETBIOS
: dprintf(1, "AF_NETBIOS (NetBIOS)\n"); break;
260 default: dprintf(1, "Other %ld\n", ptr
->ai_family
); break;
262 dprintf(1, "Canonical name: %s\n", ptr
->ai_canonname
);
265 static int getdomainname()
268 PFIXED_INFO net_info
= NULL
;
270 BOOLEAN flag
= FALSE
;
272 status
= GetNetworkParams(net_info
, &size
);
273 if (status
!= ERROR_BUFFER_OVERFLOW
) {
274 eprintf("getdomainname: GetNetworkParams returned %d\n", status
);
277 net_info
= calloc(1, size
);
278 if (net_info
== NULL
) {
279 status
= GetLastError();
282 status
= GetNetworkParams(net_info
, &size
);
284 eprintf("getdomainname: GetNetworkParams returned %d\n", status
);
288 if (net_info
->DomainName
[0] == '\0') {
289 struct addrinfo
*result
= NULL
, *ptr
= NULL
, hints
= { 0 };
290 char hostname
[NI_MAXHOST
], servInfo
[NI_MAXSERV
];
292 hints
.ai_socktype
= SOCK_STREAM
;
293 hints
.ai_protocol
= IPPROTO_TCP
;
295 status
= getaddrinfo(net_info
->HostName
, NULL
, &hints
, &result
);
297 status
= WSAGetLastError();
298 eprintf("getdomainname: getaddrinfo failed with %d\n", status
);
302 for (ptr
=result
; ptr
!= NULL
; ptr
=ptr
->ai_next
) {
303 print_getaddrinfo(ptr
);
305 switch (ptr
->ai_family
) {
308 status
= getnameinfo((struct sockaddr
*)ptr
->ai_addr
,
309 (socklen_t
)ptr
->ai_addrlen
, hostname
, NI_MAXHOST
,
310 servInfo
, NI_MAXSERV
, NI_NAMEREQD
);
312 dprintf(1, "getnameinfo failed %d\n", WSAGetLastError());
314 size_t i
, len
= strlen(hostname
);
316 dprintf(1, "getdomainname: hostname %s %d\n", hostname
, len
);
317 for (i
= 0; i
< len
; i
++)
323 memcpy(localdomain_name
, &hostname
[i
+1], len
-i
);
324 dprintf(1, "getdomainname: domainname %s %d\n",
325 localdomain_name
, strlen(localdomain_name
));
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");
340 freeaddrinfo(result
);
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';
353 #ifdef STANDALONE_NFSD
354 void __cdecl
_tmain(int argc
, TCHAR
*argv
[])
356 VOID
ServiceStart(DWORD argc
, LPTSTR
*argv
)
359 DWORD status
= 0, len
;
360 // handle to our drivers
362 nfs41_process_thread tids
[MAX_NUM_THREADS
];
363 nfs41_idmapper
*idmapper
= NULL
;
367 if (!check_for_files())
369 if (!parse_cmdlineargs(argc
, argv
, &cmd_args
))
371 set_debug_level(cmd_args
.debug_level
);
375 /* Start the kernel part */
377 HANDLE hSvcMan
= OpenSCManager(NULL
, NULL
, SC_MANAGER_ALL_ACCESS
);
380 HANDLE hSvc
= OpenService(hSvcMan
, "nfs41_driver", SERVICE_ALL_ACCESS
);
383 SERVICE_STATUS SvcSt
;
384 QueryServiceStatus(hSvc
, &SvcSt
);
385 if (SvcSt
.dwCurrentState
!= SERVICE_RUNNING
)
387 if (StartService(hSvc
, 0, NULL
))
389 dprintf(1, "NFS41 driver started\n");
393 eprintf("Driver failed to start: %d\n", GetLastError());
398 eprintf("Driver in state: %x\n", SvcSt
.dwCurrentState
);
401 CloseServiceHandle(hSvc
);
405 eprintf("Failed to open service: %d\n", GetLastError());
408 CloseServiceHandle(hSvcMan
);
412 eprintf("Failed to open service manager: %d\n", GetLastError());
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");
428 /* acquire and store in global memory current dns domain name.
433 nfs41_server_list_init();
435 if (cmd_args
.ldap_enable
) {
436 status
= nfs41_idmap_create(&idmapper
);
438 eprintf("id mapping initialization failed with %d\n", status
);
443 NFS41D_VERSION
= GetTickCount();
444 dprintf(1, "NFS41 Daemon starting: version %d\n", NFS41D_VERSION
);
446 pipe
= CreateFile(NFS41_USER_DEVICE_NAME_A
, GENERIC_READ
| GENERIC_WRITE
,
447 FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
,
449 if (pipe
== INVALID_HANDLE_VALUE
)
451 eprintf("Unable to open upcall pipe %d\n", GetLastError());
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
);
459 eprintf("IOCTL_NFS41_START failed with %d\n",
464 #ifndef STANDALONE_NFSD
465 stop_event
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
466 if (stop_event
== NULL
)
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
);
479 #ifndef STANDALONE_NFSD
480 // report the status to the service control manager.
481 if (!ReportStatusToSCMgr(SERVICE_RUNNING
, NO_ERROR
, 0))
483 WaitForSingleObject(stop_event
, INFINITE
);
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
);
490 dprintf(1, "Parent woke up!!!!\n");
495 if (idmapper
) nfs41_idmap_free(idmapper
);
497 #ifndef STANDALONE_NFSD