to determine that the daemon has restarted -- rather that daemon is receiving upcalls from the kernel that were processed by the old instance of the daemon -- add a version to the upcall mechanism. when daemon starts up it generates a version number (just a timestamp). it passes this value to the driver on start up via "start_ioctl" downcall. the driver saves that value in its device extensions. it uses that value in the mount and shtudown upcalls. when daemon replies to the mount command it again sends its version as a part of the reply. this reply is stored in driver;s netroot extensions. the driver uses netroot's value in each upcall to the daemon. if the daemon receives an upcall for an operation where the included version does not equal to the its current version, it fails the upcall (error_code=116). a restart of the daemon would change driver's device extension value which the driver will then use in the new mount upcalls that would establish new sessions. then the correct daemon version would be returned as a part of the mount downcalled and saved in the netroot.
257 lines
8.3 KiB
C
257 lines
8.3 KiB
C
/* Copyright (c) 2010
|
|
* The Regents of the University of Michigan
|
|
* All Rights Reserved
|
|
*
|
|
* Permission is granted to use, copy and redistribute this software
|
|
* for noncommercial education and research purposes, so long as no
|
|
* fee is charged, and so long as the name of the University of Michigan
|
|
* is not used in any advertising or publicity pertaining to the use
|
|
* or distribution of this software without specific, written prior
|
|
* authorization. Permission to modify or otherwise create derivative
|
|
* works of this software is not granted.
|
|
*
|
|
* This software is provided as is, without representation or warranty
|
|
* of any kind either express or implied, including without limitation
|
|
* the implied warranties of merchantability, fitness for a particular
|
|
* purpose, or noninfringement. The Regents of the University of
|
|
* Michigan shall not be liable for any damages, including special,
|
|
* indirect, incidental, or consequential damages, with respect to any
|
|
* claim arising out of or in connection with the use of the software,
|
|
* even if it has been or is hereafter advised of the possibility of
|
|
* such damages.
|
|
*/
|
|
|
|
#include <Windows.h>
|
|
#include <process.h>
|
|
#include <tchar.h>
|
|
#include <stdio.h>
|
|
|
|
#include <devioctl.h>
|
|
#include <lmcons.h> /* UNLEN for GetUserName() */
|
|
|
|
#include "nfs41_driver.h" /* for NFS41_USER_DEVICE_NAME_A */
|
|
#include "nfs41_np.h" /* for NFS41NP_SHARED_MEMORY */
|
|
|
|
#include "idmap.h"
|
|
#include "daemon_debug.h"
|
|
#include "upcall.h"
|
|
#include "util.h"
|
|
|
|
#define MAX_NUM_THREADS 128
|
|
DWORD NFS41D_VERSION = 0;
|
|
|
|
#ifndef STANDALONE_NFSD //make sure to define it in "sources" not here
|
|
#include "service.h"
|
|
HANDLE stop_event = NULL;
|
|
#endif
|
|
typedef struct _nfs41_process_thread {
|
|
HANDLE handle;
|
|
uint32_t tid;
|
|
} nfs41_process_thread;
|
|
|
|
static int map_user_to_ids(nfs41_idmapper *idmapper, uid_t *uid, gid_t *gid)
|
|
{
|
|
char username[UNLEN + 1];
|
|
DWORD len = UNLEN + 1;
|
|
int status = NO_ERROR;
|
|
|
|
if (!GetUserNameA(username, &len)) {
|
|
status = GetLastError();
|
|
eprintf("GetUserName() failed with %d\n", status);
|
|
goto out;
|
|
}
|
|
dprintf(1, "map_user_to_ids: mapping user %s\n", username);
|
|
|
|
if (nfs41_idmap_name_to_ids(idmapper, username, uid, gid)) {
|
|
/* instead of failing for auth_sys, fall back to 'nobody' uid/gid */
|
|
*uid = 666;
|
|
*gid = 777;
|
|
}
|
|
out:
|
|
return status;
|
|
}
|
|
|
|
static unsigned int WINAPI thread_main(void *args)
|
|
{
|
|
nfs41_idmapper *idmapper = (nfs41_idmapper*)args;
|
|
DWORD status = 0;
|
|
HANDLE pipe;
|
|
// buffer used to process upcall, assumed to be fixed size.
|
|
// if we ever need to handle non-cached IO, need to make it dynamic
|
|
unsigned char outbuf[UPCALL_BUF_SIZE];
|
|
// buffer used to send downcall content, need to dynamically allocated
|
|
// as we don't know the length of the buffer (ie. size of directory listing
|
|
unsigned char *inbuf = NULL;
|
|
DWORD inbuf_len, outbuf_len;
|
|
nfs41_upcall upcall;
|
|
|
|
pipe = CreateFile(NFS41_USER_DEVICE_NAME_A, GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
|
0, NULL);
|
|
if (pipe == INVALID_HANDLE_VALUE)
|
|
{
|
|
eprintf("Unable to open upcall pipe %d\n", GetLastError());
|
|
return GetLastError();
|
|
}
|
|
|
|
while(1) {
|
|
status = DeviceIoControl(pipe, IOCTL_NFS41_READ, NULL, 0,
|
|
outbuf, UPCALL_BUF_SIZE, (LPDWORD)&outbuf_len, NULL);
|
|
if (!status) {
|
|
eprintf("IOCTL_NFS41_READ failed %d\n", GetLastError());
|
|
continue;
|
|
}
|
|
|
|
status = upcall_parse(outbuf, (uint32_t)outbuf_len, &upcall);
|
|
if (status) {
|
|
upcall.status = status;
|
|
goto write_downcall;
|
|
}
|
|
|
|
/* map username to uid/gid */
|
|
status = map_user_to_ids(idmapper, &upcall.uid, &upcall.gid);
|
|
if (status) {
|
|
upcall.status = status;
|
|
goto write_downcall;
|
|
}
|
|
|
|
if (upcall.opcode == NFS41_SHUTDOWN) {
|
|
printf("Shutting down..\n");
|
|
exit(0);
|
|
}
|
|
|
|
status = upcall_handle(&upcall);
|
|
|
|
write_downcall:
|
|
dprintf(1, "writing downcall: xid=%d opcode=%s status=%d "
|
|
"get_last_error=%d\n", upcall.xid, opcode2string(upcall.opcode),
|
|
upcall.status, upcall.last_error);
|
|
|
|
if (upcall.opcode == NFS41_DIR_QUERY)
|
|
inbuf_len = UPCALL_BUF_SIZE + upcall.args.readdir.query_reply_len;
|
|
else
|
|
inbuf_len = UPCALL_BUF_SIZE;
|
|
|
|
inbuf = malloc(inbuf_len);
|
|
upcall_marshall(&upcall, inbuf, (uint32_t)inbuf_len, (uint32_t*)&outbuf_len);
|
|
|
|
dprintf(2, "making a downcall: outbuf_len %ld\n\n", outbuf_len);
|
|
status = DeviceIoControl(pipe, IOCTL_NFS41_WRITE,
|
|
inbuf, inbuf_len, NULL, 0, (LPDWORD)&outbuf_len, NULL);
|
|
free(inbuf);
|
|
if (!status) {
|
|
eprintf("IOCTL_NFS41_WRITE failed with %d xid=%d opcode=%s\n",
|
|
GetLastError(), upcall.xid, opcode2string(upcall.opcode));
|
|
upcall_cancel(&upcall);
|
|
}
|
|
upcall_cleanup(&upcall);
|
|
}
|
|
CloseHandle(pipe);
|
|
|
|
return GetLastError();
|
|
}
|
|
|
|
#ifndef STANDALONE_NFSD
|
|
VOID ServiceStop()
|
|
{
|
|
if (stop_event)
|
|
SetEvent(stop_event);
|
|
}
|
|
#endif
|
|
|
|
#ifdef STANDALONE_NFSD
|
|
void __cdecl _tmain(int argc, TCHAR *argv[])
|
|
#else
|
|
VOID ServiceStart(DWORD argc, LPTSTR *argv)
|
|
#endif
|
|
{
|
|
DWORD status = 0, len;
|
|
// handle to our drivers
|
|
HANDLE pipe;
|
|
nfs41_process_thread tids[MAX_NUM_THREADS];
|
|
nfs41_idmapper *idmapper;
|
|
int i;
|
|
|
|
if (argc > 2) {
|
|
const char *process = strip_path(argv[0], NULL);
|
|
printf("Usage: %s [#debug level]\n", process);
|
|
} else if (argc == 2) {
|
|
set_debug_level(_ttoi(argv[1]));
|
|
}
|
|
open_log_files();
|
|
#ifdef _DEBUG
|
|
/* dump memory leaks to stderr on exit; this requires the debug heap,
|
|
/* available only when built in debug mode under visual studio -cbodley */
|
|
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
|
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
|
|
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
|
|
dprintf(1, "debug mode. dumping memory leaks to stderr on exit.\n");
|
|
#endif
|
|
|
|
nfs41_server_list_init();
|
|
|
|
status = nfs41_idmap_create(&idmapper);
|
|
if (status) {
|
|
eprintf("id mapping initialization failed with %d\n", status);
|
|
goto out_logs;
|
|
}
|
|
|
|
NFS41D_VERSION = GetTickCount();
|
|
dprintf(1, "NFS41 Daemon starting: version %d\n", NFS41D_VERSION);
|
|
|
|
pipe = CreateFile(NFS41_USER_DEVICE_NAME_A, GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
|
0, NULL);
|
|
if (pipe == INVALID_HANDLE_VALUE)
|
|
{
|
|
eprintf("Unable to open upcall pipe %d\n", GetLastError());
|
|
goto out_idmap;
|
|
}
|
|
|
|
dprintf(1, "starting nfs41 mini redirector\n");
|
|
status = DeviceIoControl(pipe, IOCTL_NFS41_START,
|
|
&NFS41D_VERSION, sizeof(DWORD), NULL, 0, (LPDWORD)&len, NULL);
|
|
if (!status) {
|
|
eprintf("IOCTL_NFS41_START failed with %d\n",
|
|
GetLastError());
|
|
goto out_pipe;
|
|
}
|
|
|
|
#ifndef STANDALONE_NFSD
|
|
stop_event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (stop_event == NULL)
|
|
goto quit;
|
|
#endif
|
|
|
|
for (i = 0; i < MAX_NUM_THREADS; i++) {
|
|
tids[i].handle = (HANDLE)_beginthreadex(NULL, 0, thread_main,
|
|
idmapper, 0, &tids[i].tid);
|
|
if (tids[i].handle == INVALID_HANDLE_VALUE) {
|
|
status = GetLastError();
|
|
eprintf("_beginthreadex failed %d\n", status);
|
|
goto out_pipe;
|
|
}
|
|
}
|
|
#ifndef STANDALONE_NFSD
|
|
// report the status to the service control manager.
|
|
if (!ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0))
|
|
goto out_pipe;
|
|
WaitForSingleObject(stop_event, INFINITE);
|
|
#else
|
|
//This can be changed to waiting on an array of handles and using waitformultipleobjects
|
|
dprintf(1, "Parent waiting for children threads\n");
|
|
for (i = 0; i < MAX_NUM_THREADS; i++)
|
|
WaitForSingleObject(tids[i].handle, INFINITE );
|
|
#endif
|
|
dprintf(1, "Parent woke up!!!!\n");
|
|
|
|
out_pipe:
|
|
CloseHandle(pipe);
|
|
out_idmap:
|
|
nfs41_idmap_free(idmapper);
|
|
out_logs:
|
|
#ifndef STANDALONE_NFSD
|
|
close_log_files();
|
|
#endif
|
|
return;
|
|
}
|