mirror of
https://github.com/reactos/reactos.git
synced 2024-06-29 01:12:06 +00:00
[NFSD]
Import the nfsd deamon from the nfs41 project. CORE-8204 svn path=/trunk/; revision=75114
This commit is contained in:
parent
de51bd8b04
commit
f445db2c5c
|
@ -2,6 +2,7 @@
|
|||
add_subdirectory(audiosrv)
|
||||
add_subdirectory(dhcpcsvc)
|
||||
add_subdirectory(eventlog)
|
||||
add_subdirectory(nfsd)
|
||||
add_subdirectory(rpcss)
|
||||
add_subdirectory(schedsvc)
|
||||
add_subdirectory(shsvcs)
|
||||
|
|
61
reactos/base/services/nfsd/CMakeLists.txt
Normal file
61
reactos/base/services/nfsd/CMakeLists.txt
Normal file
|
@ -0,0 +1,61 @@
|
|||
remove_definitions(-D_WIN32_WINNT=0x502)
|
||||
add_definitions(-D_WIN32_WINNT=0x600)
|
||||
add_definitions(-DNTDDI_VERSION=0x06010000)
|
||||
|
||||
include_directories(
|
||||
${REACTOS_SOURCE_DIR}/dll/3rdparty/libtirpc/tirpc
|
||||
${REACTOS_SOURCE_DIR}/drivers/filesystems/nfs
|
||||
${REACTOS_SOURCE_DIR}/dll/np/nfs)
|
||||
|
||||
list(APPEND SOURCE
|
||||
nfs41_daemon.c
|
||||
daemon_debug.c
|
||||
nfs41_ops.c
|
||||
nfs41_compound.c
|
||||
nfs41_xdr.c
|
||||
nfs41_server.c
|
||||
nfs41_client.c
|
||||
nfs41_superblock.c
|
||||
nfs41_session.c
|
||||
lookup.c
|
||||
mount.c
|
||||
open.c
|
||||
readwrite.c
|
||||
lock.c
|
||||
readdir.c
|
||||
getattr.c
|
||||
setattr.c
|
||||
upcall.c
|
||||
nfs41_rpc.c
|
||||
util.c
|
||||
pnfs_layout.c
|
||||
pnfs_device.c
|
||||
pnfs_debug.c
|
||||
pnfs_io.c
|
||||
name_cache.c
|
||||
namespace.c
|
||||
volume.c
|
||||
callback_server.c
|
||||
callback_xdr.c
|
||||
service.c
|
||||
symlink.c
|
||||
idmap.c
|
||||
delegation.c
|
||||
recovery.c
|
||||
acl.c
|
||||
ea.c)
|
||||
|
||||
add_executable(nfsd ${SOURCE} nfsd.rc)
|
||||
|
||||
if(MSVC)
|
||||
else()
|
||||
# FIXME: Tons of warnings.
|
||||
replace_compile_flags("-Werror" " ")
|
||||
endif()
|
||||
|
||||
set_module_type(nfsd win32cui)
|
||||
target_link_libraries(nfsd wldap32)
|
||||
add_importlibs(nfsd advapi32 iphlpapi kernel32 kernel32_vista tirpc ntdll msvcrt shell32 ws2_32)
|
||||
add_cd_file(TARGET nfsd DESTINATION reactos/system32 FOR all)
|
||||
add_cd_file(FILE "${CMAKE_CURRENT_SOURCE_DIR}/netconfig" DESTINATION reactos/system32/drivers/etc FOR all)
|
||||
add_cd_file(FILE "${CMAKE_CURRENT_SOURCE_DIR}/ms-nfs41-idmap.conf" DESTINATION reactos/system32/drivers/etc FOR all)
|
801
reactos/base/services/nfsd/acl.c
Normal file
801
reactos/base/services/nfsd/acl.c
Normal file
|
@ -0,0 +1,801 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
#include <sddl.h>
|
||||
|
||||
#include "nfs41_ops.h"
|
||||
#include "delegation.h"
|
||||
#include "daemon_debug.h"
|
||||
#include "util.h"
|
||||
#include "upcall.h"
|
||||
#include "nfs41_xdr.h"
|
||||
|
||||
//#define DEBUG_ACLS
|
||||
#define ACLLVL 2 /* dprintf level for acl logging */
|
||||
|
||||
extern char localdomain_name[NFS41_HOSTNAME_LEN];
|
||||
|
||||
static int parse_getacl(unsigned char *buffer, uint32_t length,
|
||||
nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
getacl_upcall_args *args = &upcall->args.getacl;
|
||||
|
||||
status = safe_read(&buffer, &length, &args->query, sizeof(args->query));
|
||||
if (status) goto out;
|
||||
|
||||
dprintf(1, "parsing NFS41_ACL_QUERY: info_class=%d\n", args->query);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int create_unknownsid(WELL_KNOWN_SID_TYPE type, PSID *sid,
|
||||
DWORD *sid_len)
|
||||
{
|
||||
int status;
|
||||
*sid_len = 0;
|
||||
*sid = NULL;
|
||||
|
||||
status = CreateWellKnownSid(type, NULL, *sid, sid_len);
|
||||
dprintf(ACLLVL, "create_unknownsid: CreateWellKnownSid type %d returned %d "
|
||||
"GetLastError %d sid len %d needed\n", type, status,
|
||||
GetLastError(), *sid_len);
|
||||
if (status)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
status = GetLastError();
|
||||
if (status != ERROR_INSUFFICIENT_BUFFER)
|
||||
return status;
|
||||
*sid = malloc(*sid_len);
|
||||
if (*sid == NULL)
|
||||
return ERROR_INSUFFICIENT_BUFFER;
|
||||
status = CreateWellKnownSid(type, NULL, *sid, sid_len);
|
||||
if (status)
|
||||
return ERROR_SUCCESS;
|
||||
free(*sid);
|
||||
status = GetLastError();
|
||||
eprintf("create_unknownsid: CreateWellKnownSid failed with %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void convert_nfs4name_2_user_domain(LPSTR nfs4name,
|
||||
LPSTR *domain)
|
||||
{
|
||||
LPSTR p = nfs4name;
|
||||
for(; p[0] != '\0'; p++) {
|
||||
if (p[0] == '@') {
|
||||
p[0] = '\0';
|
||||
*domain = &p[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int map_name_2_sid(DWORD *sid_len, PSID *sid, LPCSTR name)
|
||||
{
|
||||
int status = ERROR_INTERNAL_ERROR;
|
||||
SID_NAME_USE sid_type;
|
||||
LPSTR tmp_buf = NULL;
|
||||
DWORD tmp = 0;
|
||||
|
||||
status = LookupAccountName(NULL, name, NULL, sid_len, NULL, &tmp, &sid_type);
|
||||
dprintf(ACLLVL, "map_name_2_sid: LookupAccountName for %s returned %d "
|
||||
"GetLastError %d name len %d domain len %d\n", name, status,
|
||||
GetLastError(), *sid_len, tmp);
|
||||
if (status)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
status = GetLastError();
|
||||
switch(status) {
|
||||
case ERROR_INSUFFICIENT_BUFFER:
|
||||
*sid = malloc(*sid_len);
|
||||
if (*sid == NULL) {
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
tmp_buf = (LPSTR) malloc(tmp);
|
||||
if (tmp_buf == NULL)
|
||||
goto out_free_sid;
|
||||
status = LookupAccountName(NULL, name, *sid, sid_len, tmp_buf,
|
||||
&tmp, &sid_type);
|
||||
free(tmp_buf);
|
||||
if (!status) {
|
||||
eprintf("map_name_2_sid: LookupAccountName for %s failed "
|
||||
"with %d\n", name, GetLastError());
|
||||
goto out_free_sid;
|
||||
} else {
|
||||
#ifdef DEBUG_ACLS
|
||||
LPSTR ssid = NULL;
|
||||
if (IsValidSid(*sid))
|
||||
if (ConvertSidToStringSidA(*sid, &ssid))
|
||||
dprintf(1, "map_name_2_sid: sid_type = %d SID %s\n",
|
||||
sid_type, ssid);
|
||||
else
|
||||
dprintf(1, "map_name_2_sid: ConvertSidToStringSidA failed "
|
||||
"with %d\n", GetLastError());
|
||||
else
|
||||
dprintf(1, "map_name_2_sid: Invalid Sid ?\n");
|
||||
if (ssid) LocalFree(ssid);
|
||||
#endif
|
||||
}
|
||||
status = ERROR_SUCCESS;
|
||||
break;
|
||||
case ERROR_NONE_MAPPED:
|
||||
status = create_unknownsid(WinNullSid, sid, sid_len);
|
||||
if (status)
|
||||
goto out_free_sid;
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
out_free_sid:
|
||||
status = GetLastError();
|
||||
free(*sid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void free_sids(PSID *sids, int count)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < count; i++)
|
||||
free(sids[i]);
|
||||
free(sids);
|
||||
}
|
||||
|
||||
static int check_4_special_identifiers(char *who, PSID *sid, DWORD *sid_len,
|
||||
BOOLEAN *flag)
|
||||
{
|
||||
int status = ERROR_SUCCESS;
|
||||
WELL_KNOWN_SID_TYPE type = 0;
|
||||
*flag = TRUE;
|
||||
if (!strncmp(who, ACE4_OWNER, strlen(ACE4_OWNER)-1))
|
||||
type = WinCreatorOwnerSid;
|
||||
else if (!strncmp(who, ACE4_GROUP, strlen(ACE4_GROUP)-1))
|
||||
type = WinCreatorGroupSid;
|
||||
else if (!strncmp(who, ACE4_EVERYONE, strlen(ACE4_EVERYONE)-1))
|
||||
type = WinWorldSid;
|
||||
else if (!strncmp(who, ACE4_NOBODY, strlen(ACE4_NOBODY)))
|
||||
type = WinNullSid;
|
||||
else
|
||||
*flag = FALSE;
|
||||
if (*flag)
|
||||
status = create_unknownsid(type, sid, sid_len);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int convert_nfs4acl_2_dacl(nfsacl41 *acl, int file_type,
|
||||
PACL *dacl_out, PSID **sids_out)
|
||||
{
|
||||
int status = ERROR_NOT_SUPPORTED, size = 0;
|
||||
uint32_t i;
|
||||
DWORD sid_len;
|
||||
PSID *sids;
|
||||
PACL dacl;
|
||||
LPSTR domain = NULL;
|
||||
BOOLEAN flag;
|
||||
|
||||
sids = malloc(acl->count * sizeof(PSID));
|
||||
if (sids == NULL) {
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
for (i = 0; i < acl->count; i++) {
|
||||
convert_nfs4name_2_user_domain(acl->aces[i].who, &domain);
|
||||
dprintf(ACLLVL, "handle_getacl: for user=%s domain=%s\n",
|
||||
acl->aces[i].who, domain?domain:"<null>");
|
||||
status = check_4_special_identifiers(acl->aces[i].who, &sids[i],
|
||||
&sid_len, &flag);
|
||||
if (status) {
|
||||
free_sids(sids, i);
|
||||
goto out;
|
||||
}
|
||||
if (!flag) {
|
||||
status = map_name_2_sid(&sid_len, &sids[i], acl->aces[i].who);
|
||||
if (status) {
|
||||
free_sids(sids, i);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
size += sid_len - sizeof(DWORD);
|
||||
}
|
||||
size += sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE)*acl->count);
|
||||
size = (size + sizeof(DWORD) - 1) & 0xfffffffc; //align size on word boundry
|
||||
dacl = malloc(size);
|
||||
if (dacl == NULL)
|
||||
goto out_free_sids;
|
||||
|
||||
if (InitializeAcl(dacl, size, ACL_REVISION)) {
|
||||
ACCESS_MASK mask;
|
||||
for (i = 0; i < acl->count; i++) {
|
||||
// nfs4 acemask should be exactly the same as file access mask
|
||||
mask = acl->aces[i].acemask;
|
||||
dprintf(ACLLVL, "access mask %x ace type %s\n", mask,
|
||||
acl->aces[i].acetype?"DENIED ACE":"ALLOWED ACE");
|
||||
if (acl->aces[i].acetype == ACE4_ACCESS_ALLOWED_ACE_TYPE) {
|
||||
status = AddAccessAllowedAce(dacl, ACL_REVISION, mask, sids[i]);
|
||||
if (!status) {
|
||||
eprintf("convert_nfs4acl_2_dacl: AddAccessAllowedAce failed "
|
||||
"with %d\n", status);
|
||||
goto out_free_dacl;
|
||||
}
|
||||
else status = ERROR_SUCCESS;
|
||||
} else if (acl->aces[i].acetype == ACE4_ACCESS_DENIED_ACE_TYPE) {
|
||||
status = AddAccessDeniedAce(dacl, ACL_REVISION, mask, sids[i]);
|
||||
if (!status) {
|
||||
eprintf("convert_nfs4acl_2_dacl: AddAccessDeniedAce failed "
|
||||
"with %d\n", status);
|
||||
goto out_free_dacl;
|
||||
}
|
||||
else status = ERROR_SUCCESS;
|
||||
} else {
|
||||
eprintf("convert_nfs4acl_2_dacl: unknown acetype %d\n",
|
||||
acl->aces[i].acetype);
|
||||
status = ERROR_INTERNAL_ERROR;
|
||||
free(dacl);
|
||||
free_sids(sids, acl->count);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintf("convert_nfs4acl_2_dacl: InitializeAcl failed with %d\n", status);
|
||||
goto out_free_dacl;
|
||||
}
|
||||
status = ERROR_SUCCESS;
|
||||
*sids_out = sids;
|
||||
*dacl_out = dacl;
|
||||
out:
|
||||
return status;
|
||||
out_free_dacl:
|
||||
free(dacl);
|
||||
out_free_sids:
|
||||
free_sids(sids, acl->count);
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int handle_getacl(nfs41_upcall *upcall)
|
||||
{
|
||||
int status = ERROR_NOT_SUPPORTED;
|
||||
getacl_upcall_args *args = &upcall->args.getacl;
|
||||
nfs41_open_state *state = upcall->state_ref;
|
||||
nfs41_file_info info = { 0 };
|
||||
bitmap4 attr_request = { 0 };
|
||||
LPSTR domain = NULL;
|
||||
SECURITY_DESCRIPTOR sec_desc;
|
||||
PACL dacl = NULL;
|
||||
PSID *sids = NULL;
|
||||
PSID osid = NULL, gsid = NULL;
|
||||
DWORD sid_len;
|
||||
char owner[NFS4_OPAQUE_LIMIT], group[NFS4_OPAQUE_LIMIT];
|
||||
nfsacl41 acl = { 0 };
|
||||
|
||||
// need to cache owner/group information XX
|
||||
attr_request.count = 2;
|
||||
attr_request.arr[1] = FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP;
|
||||
if (args->query & DACL_SECURITY_INFORMATION) {
|
||||
info.acl = &acl;
|
||||
attr_request.arr[0] |= FATTR4_WORD0_ACL;
|
||||
}
|
||||
info.owner = owner;
|
||||
info.owner_group = group;
|
||||
status = nfs41_getattr(state->session, &state->file, &attr_request, &info);
|
||||
if (status) {
|
||||
eprintf("handle_getacl: nfs41_cached_getattr() failed with %d\n",
|
||||
status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = InitializeSecurityDescriptor(&sec_desc,
|
||||
SECURITY_DESCRIPTOR_REVISION);
|
||||
if (!status) {
|
||||
status = GetLastError();
|
||||
eprintf("handle_getacl: InitializeSecurityDescriptor failed with %d\n",
|
||||
status);
|
||||
goto out;
|
||||
}
|
||||
/* can't (re)use the same sid variable for both owner and group sids
|
||||
* because security descriptor is created in absolute-form and it just
|
||||
* stores pointers to the sids. thus each owner and group needs its own
|
||||
* memory. free them after creating self-relative security descriptor.
|
||||
*/
|
||||
if (args->query & OWNER_SECURITY_INFORMATION) {
|
||||
// parse user@domain. currently ignoring domain part XX
|
||||
convert_nfs4name_2_user_domain(info.owner, &domain);
|
||||
dprintf(ACLLVL, "handle_getacl: OWNER_SECURITY_INFORMATION: for user=%s "
|
||||
"domain=%s\n", info.owner, domain?domain:"<null>");
|
||||
sid_len = 0;
|
||||
status = map_name_2_sid(&sid_len, &osid, info.owner);
|
||||
if (status)
|
||||
goto out;
|
||||
status = SetSecurityDescriptorOwner(&sec_desc, osid, TRUE);
|
||||
if (!status) {
|
||||
status = GetLastError();
|
||||
eprintf("handle_getacl: SetSecurityDescriptorOwner failed with "
|
||||
"%d\n", status);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (args->query & GROUP_SECURITY_INFORMATION) {
|
||||
convert_nfs4name_2_user_domain(info.owner_group, &domain);
|
||||
dprintf(ACLLVL, "handle_getacl: GROUP_SECURITY_INFORMATION: for %s "
|
||||
"domain=%s\n", info.owner_group, domain?domain:"<null>");
|
||||
sid_len = 0;
|
||||
status = map_name_2_sid(&sid_len, &gsid, info.owner_group);
|
||||
if (status)
|
||||
goto out;
|
||||
status = SetSecurityDescriptorGroup(&sec_desc, gsid, TRUE);
|
||||
if (!status) {
|
||||
status = GetLastError();
|
||||
eprintf("handle_getacl: SetSecurityDescriptorGroup failed with "
|
||||
"%d\n", status);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (args->query & DACL_SECURITY_INFORMATION) {
|
||||
dprintf(ACLLVL, "handle_getacl: DACL_SECURITY_INFORMATION\n");
|
||||
status = convert_nfs4acl_2_dacl(info.acl, state->type, &dacl, &sids);
|
||||
if (status)
|
||||
goto out;
|
||||
status = SetSecurityDescriptorDacl(&sec_desc, TRUE, dacl, TRUE);
|
||||
if (!status) {
|
||||
status = GetLastError();
|
||||
eprintf("handle_getacl: SetSecurityDescriptorDacl failed with "
|
||||
"%d\n", status);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
args->sec_desc_len = 0;
|
||||
status = MakeSelfRelativeSD(&sec_desc, args->sec_desc, &args->sec_desc_len);
|
||||
if (status) {
|
||||
status = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
status = GetLastError();
|
||||
if (status != ERROR_INSUFFICIENT_BUFFER) {
|
||||
eprintf("handle_getacl: MakeSelfRelativeSD failes with %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
args->sec_desc = malloc(args->sec_desc_len);
|
||||
if (args->sec_desc == NULL) {
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
status = MakeSelfRelativeSD(&sec_desc, args->sec_desc, &args->sec_desc_len);
|
||||
if (!status) {
|
||||
status = GetLastError();
|
||||
eprintf("handle_getacl: MakeSelfRelativeSD failes with %d\n", status);
|
||||
free(args->sec_desc);
|
||||
goto out;
|
||||
} else status = ERROR_SUCCESS;
|
||||
|
||||
out:
|
||||
if (args->query & OWNER_SECURITY_INFORMATION) {
|
||||
if (osid) free(osid);
|
||||
}
|
||||
if (args->query & GROUP_SECURITY_INFORMATION) {
|
||||
if (gsid) free(gsid);
|
||||
}
|
||||
if (args->query & DACL_SECURITY_INFORMATION) {
|
||||
if (sids) free_sids(sids, info.acl->count);
|
||||
free(dacl);
|
||||
nfsacl41_free(info.acl);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int marshall_getacl(unsigned char *buffer, uint32_t *length,
|
||||
nfs41_upcall *upcall)
|
||||
{
|
||||
int status = ERROR_NOT_SUPPORTED;
|
||||
getacl_upcall_args *args = &upcall->args.getacl;
|
||||
|
||||
status = safe_write(&buffer, length, &args->sec_desc_len, sizeof(DWORD));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, args->sec_desc, args->sec_desc_len);
|
||||
free(args->sec_desc);
|
||||
if (status) goto out;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
const nfs41_upcall_op nfs41_op_getacl = {
|
||||
parse_getacl,
|
||||
handle_getacl,
|
||||
marshall_getacl
|
||||
};
|
||||
|
||||
static int parse_setacl(unsigned char *buffer, uint32_t length,
|
||||
nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
setacl_upcall_args *args = &upcall->args.setacl;
|
||||
ULONG sec_desc_len;
|
||||
|
||||
status = safe_read(&buffer, &length, &args->query, sizeof(args->query));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &sec_desc_len, sizeof(ULONG));
|
||||
if (status) goto out;
|
||||
args->sec_desc = (PSECURITY_DESCRIPTOR)buffer;
|
||||
|
||||
dprintf(1, "parsing NFS41_ACL_SET: info_class=%d sec_desc_len=%d\n",
|
||||
args->query, sec_desc_len);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int is_well_known_sid(PSID sid, char *who)
|
||||
{
|
||||
int status, i;
|
||||
for (i = 0; i < 78; i++) {
|
||||
status = IsWellKnownSid(sid, (WELL_KNOWN_SID_TYPE)i);
|
||||
if (!status) continue;
|
||||
else {
|
||||
dprintf(ACLLVL, "WELL_KNOWN_SID_TYPE %d\n", i);
|
||||
switch((WELL_KNOWN_SID_TYPE)i) {
|
||||
case WinCreatorOwnerSid:
|
||||
memcpy(who, ACE4_OWNER, strlen(ACE4_OWNER)+1);
|
||||
return TRUE;
|
||||
case WinNullSid:
|
||||
memcpy(who, ACE4_NOBODY, strlen(ACE4_NOBODY)+1);
|
||||
return TRUE;
|
||||
case WinAnonymousSid:
|
||||
memcpy(who, ACE4_ANONYMOUS, strlen(ACE4_ANONYMOUS)+1);
|
||||
return TRUE;
|
||||
case WinWorldSid:
|
||||
memcpy(who, ACE4_EVERYONE, strlen(ACE4_EVERYONE)+1);
|
||||
return TRUE;
|
||||
case WinCreatorGroupSid:
|
||||
case WinBuiltinUsersSid:
|
||||
memcpy(who, ACE4_GROUP, strlen(ACE4_GROUP)+1);
|
||||
return TRUE;
|
||||
case WinAuthenticatedUserSid:
|
||||
memcpy(who, ACE4_AUTHENTICATED, strlen(ACE4_AUTHENTICATED)+1);
|
||||
return TRUE;
|
||||
case WinDialupSid:
|
||||
memcpy(who, ACE4_DIALUP, strlen(ACE4_DIALUP)+1);
|
||||
return TRUE;
|
||||
case WinNetworkSid:
|
||||
memcpy(who, ACE4_NETWORK, strlen(ACE4_NETWORK)+1);
|
||||
return TRUE;
|
||||
case WinBatchSid:
|
||||
memcpy(who, ACE4_BATCH, strlen(ACE4_BATCH)+1);
|
||||
return TRUE;
|
||||
case WinInteractiveSid:
|
||||
memcpy(who, ACE4_INTERACTIVE, strlen(ACE4_INTERACTIVE)+1);
|
||||
return TRUE;
|
||||
case WinNetworkServiceSid:
|
||||
case WinLocalServiceSid:
|
||||
case WinServiceSid:
|
||||
memcpy(who, ACE4_SERVICE, strlen(ACE4_SERVICE)+1);
|
||||
return TRUE;
|
||||
default: return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void map_aceflags(BYTE win_aceflags, uint32_t *nfs4_aceflags)
|
||||
{
|
||||
if (win_aceflags & OBJECT_INHERIT_ACE)
|
||||
*nfs4_aceflags |= ACE4_FILE_INHERIT_ACE;
|
||||
if (win_aceflags & CONTAINER_INHERIT_ACE)
|
||||
*nfs4_aceflags |= ACE4_DIRECTORY_INHERIT_ACE;
|
||||
if (win_aceflags & NO_PROPAGATE_INHERIT_ACE)
|
||||
*nfs4_aceflags |= ACE4_NO_PROPAGATE_INHERIT_ACE;
|
||||
if (win_aceflags & INHERIT_ONLY_ACE)
|
||||
*nfs4_aceflags |= ACE4_INHERIT_ONLY_ACE;
|
||||
if (win_aceflags & INHERITED_ACE)
|
||||
*nfs4_aceflags |= ACE4_INHERITED_ACE;
|
||||
dprintf(ACLLVL, "ACE FLAGS: %x nfs4 aceflags %x\n",
|
||||
win_aceflags, *nfs4_aceflags);
|
||||
}
|
||||
|
||||
static void map_acemask(ACCESS_MASK mask, int file_type, uint32_t *nfs4_mask)
|
||||
{
|
||||
dprintf(ACLLVL, "ACE MASK: %x\n", mask);
|
||||
print_windows_access_mask(0, mask);
|
||||
/* check if any GENERIC bits set */
|
||||
if (mask & 0xf000000) {
|
||||
if (mask & GENERIC_ALL) {
|
||||
if (file_type == NF4DIR)
|
||||
*nfs4_mask |= ACE4_ALL_DIR;
|
||||
else
|
||||
*nfs4_mask |= ACE4_ALL_FILE;
|
||||
} else {
|
||||
if (mask & GENERIC_READ)
|
||||
*nfs4_mask |= ACE4_GENERIC_READ;
|
||||
if (mask & GENERIC_WRITE)
|
||||
*nfs4_mask |= ACE4_GENERIC_WRITE;
|
||||
if (mask & GENERIC_EXECUTE)
|
||||
*nfs4_mask |= ACE4_GENERIC_EXECUTE;
|
||||
}
|
||||
}
|
||||
else /* ignoring generic and reserved bits */
|
||||
*nfs4_mask = mask & 0x00ffffff;
|
||||
print_nfs_access_mask(0, *nfs4_mask);
|
||||
}
|
||||
|
||||
static int map_nfs4ace_who(PSID sid, PSID owner_sid, PSID group_sid, char *who_out, char *domain)
|
||||
{
|
||||
int status = ERROR_INTERNAL_ERROR;
|
||||
DWORD size = 0, tmp_size = 0;
|
||||
SID_NAME_USE sid_type;
|
||||
LPSTR tmp_buf = NULL, who = NULL;
|
||||
|
||||
/* for ace mapping, we want to map owner's sid into "owner@"
|
||||
* but for set_owner attribute we want to map owner into a user name
|
||||
* same applies to group
|
||||
*/
|
||||
status = 0;
|
||||
if (owner_sid) {
|
||||
if (EqualSid(sid, owner_sid)) {
|
||||
dprintf(ACLLVL, "map_nfs4ace_who: this is owner's sid\n");
|
||||
memcpy(who_out, ACE4_OWNER, strlen(ACE4_OWNER)+1);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
if (group_sid) {
|
||||
if (EqualSid(sid, group_sid)) {
|
||||
dprintf(ACLLVL, "map_nfs4ace_who: this is group's sid\n");
|
||||
memcpy(who_out, ACE4_GROUP, strlen(ACE4_GROUP)+1);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
status = is_well_known_sid(sid, who_out);
|
||||
if (status) {
|
||||
if (!strncmp(who_out, ACE4_NOBODY, strlen(ACE4_NOBODY))) {
|
||||
size = (DWORD)strlen(ACE4_NOBODY);
|
||||
goto add_domain;
|
||||
}
|
||||
else
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
status = LookupAccountSid(NULL, sid, who, &size, tmp_buf,
|
||||
&tmp_size, &sid_type);
|
||||
dprintf(ACLLVL, "map_nfs4ace_who: LookupAccountSid returned %d GetLastError "
|
||||
"%d name len %d domain len %d\n", status, GetLastError(),
|
||||
size, tmp_size);
|
||||
if (status)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
status = GetLastError();
|
||||
if (status != ERROR_INSUFFICIENT_BUFFER)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
who = malloc(size);
|
||||
if (who == NULL) {
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
tmp_buf = malloc(tmp_size);
|
||||
if (tmp_buf == NULL)
|
||||
goto out_free_who;
|
||||
status = LookupAccountSid(NULL, sid, who, &size, tmp_buf,
|
||||
&tmp_size, &sid_type);
|
||||
free(tmp_buf);
|
||||
if (!status) {
|
||||
eprintf("map_nfs4ace_who: LookupAccountSid failed with %d\n",
|
||||
GetLastError());
|
||||
goto out_free_who;
|
||||
}
|
||||
memcpy(who_out, who, size);
|
||||
add_domain:
|
||||
memcpy(who_out+size, "@", sizeof(char));
|
||||
memcpy(who_out+size+1, domain, strlen(domain)+1);
|
||||
dprintf(ACLLVL, "map_nfs4ace_who: who=%s\n", who_out);
|
||||
if (who) free(who);
|
||||
status = ERROR_SUCCESS;
|
||||
out:
|
||||
return status;
|
||||
out_free_who:
|
||||
free(who);
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
static int map_dacl_2_nfs4acl(PACL acl, PSID sid, PSID gsid, nfsacl41 *nfs4_acl,
|
||||
int file_type, char *domain)
|
||||
{
|
||||
int status;
|
||||
if (acl == NULL) {
|
||||
dprintf(ACLLVL, "this is a NULL dacl: all access to an object\n");
|
||||
nfs4_acl->count = 1;
|
||||
nfs4_acl->aces = calloc(1, sizeof(nfsace4));
|
||||
if (nfs4_acl->aces == NULL) {
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
nfs4_acl->flag = 0;
|
||||
memcpy(nfs4_acl->aces->who, ACE4_EVERYONE, strlen(ACE4_EVERYONE)+1);
|
||||
nfs4_acl->aces->acetype = ACE4_ACCESS_ALLOWED_ACE_TYPE;
|
||||
if (file_type == NF4DIR)
|
||||
nfs4_acl->aces->acemask = ACE4_ALL_DIR;
|
||||
else
|
||||
nfs4_acl->aces->acemask = ACE4_ALL_FILE;
|
||||
nfs4_acl->aces->aceflag = 0;
|
||||
} else {
|
||||
int i;
|
||||
PACE_HEADER ace;
|
||||
PBYTE tmp_pointer;
|
||||
|
||||
dprintf(ACLLVL, "NON-NULL dacl with %d ACEs\n", acl->AceCount);
|
||||
print_hexbuf_no_asci(3, (unsigned char *)"ACL\n",
|
||||
(unsigned char *)acl, acl->AclSize);
|
||||
nfs4_acl->count = acl->AceCount;
|
||||
nfs4_acl->aces = calloc(nfs4_acl->count, sizeof(nfsace4));
|
||||
if (nfs4_acl->aces == NULL) {
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
nfs4_acl->flag = 0;
|
||||
for (i = 0; i < acl->AceCount; i++) {
|
||||
status = GetAce(acl, i, &ace);
|
||||
if (!status) {
|
||||
status = GetLastError();
|
||||
eprintf("map_dacl_2_nfs4acl: GetAce failed with %d\n", status);
|
||||
goto out_free;
|
||||
}
|
||||
tmp_pointer = (PBYTE)ace;
|
||||
print_hexbuf_no_asci(3, (unsigned char *)"ACE\n",
|
||||
(unsigned char *)ace, ace->AceSize);
|
||||
dprintf(ACLLVL, "ACE TYPE: %x\n", ace->AceType);
|
||||
if (ace->AceType == ACCESS_ALLOWED_ACE_TYPE)
|
||||
nfs4_acl->aces[i].acetype = ACE4_ACCESS_ALLOWED_ACE_TYPE;
|
||||
else if (ace->AceType == ACCESS_DENIED_ACE_TYPE)
|
||||
nfs4_acl->aces[i].acetype = ACE4_ACCESS_DENIED_ACE_TYPE;
|
||||
else {
|
||||
eprintf("map_dacl_2_nfs4acl: unsupported ACE type %d\n",
|
||||
ace->AceType);
|
||||
status = ERROR_NOT_SUPPORTED;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
map_aceflags(ace->AceFlags, &nfs4_acl->aces[i].aceflag);
|
||||
map_acemask(*(PACCESS_MASK)(ace + 1), file_type,
|
||||
&nfs4_acl->aces[i].acemask);
|
||||
|
||||
tmp_pointer += sizeof(ACCESS_MASK) + sizeof(ACE_HEADER);
|
||||
status = map_nfs4ace_who(tmp_pointer, sid, gsid, nfs4_acl->aces[i].who,
|
||||
domain);
|
||||
if (status)
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
status = ERROR_SUCCESS;
|
||||
out:
|
||||
return status;
|
||||
out_free:
|
||||
free(nfs4_acl->aces);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int handle_setacl(nfs41_upcall *upcall)
|
||||
{
|
||||
int status = ERROR_NOT_SUPPORTED;
|
||||
setacl_upcall_args *args = &upcall->args.setacl;
|
||||
nfs41_open_state *state = upcall->state_ref;
|
||||
nfs41_file_info info = { 0 };
|
||||
stateid_arg stateid;
|
||||
nfsacl41 nfs4_acl = { 0 };
|
||||
PSID sid = NULL, gsid = NULL;
|
||||
BOOL sid_default, gsid_default;
|
||||
|
||||
if (args->query & OWNER_SECURITY_INFORMATION) {
|
||||
char owner[NFS4_OPAQUE_LIMIT];
|
||||
dprintf(ACLLVL, "handle_setacl: OWNER_SECURITY_INFORMATION\n");
|
||||
status = GetSecurityDescriptorOwner(args->sec_desc, &sid, &sid_default);
|
||||
if (!status) {
|
||||
status = GetLastError();
|
||||
eprintf("GetSecurityDescriptorOwner failed with %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
info.owner = owner;
|
||||
status = map_nfs4ace_who(sid, NULL, NULL, info.owner, localdomain_name);
|
||||
if (status)
|
||||
goto out;
|
||||
else {
|
||||
info.attrmask.arr[1] |= FATTR4_WORD1_OWNER;
|
||||
info.attrmask.count = 2;
|
||||
}
|
||||
}
|
||||
if (args->query & GROUP_SECURITY_INFORMATION) {
|
||||
char group[NFS4_OPAQUE_LIMIT];
|
||||
dprintf(ACLLVL, "handle_setacl: GROUP_SECURITY_INFORMATION\n");
|
||||
status = GetSecurityDescriptorGroup(args->sec_desc, &sid, &sid_default);
|
||||
if (!status) {
|
||||
status = GetLastError();
|
||||
eprintf("GetSecurityDescriptorOwner failed with %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
info.owner_group = group;
|
||||
status = map_nfs4ace_who(sid, NULL, NULL, info.owner_group,
|
||||
localdomain_name);
|
||||
if (status)
|
||||
goto out;
|
||||
else {
|
||||
info.attrmask.arr[1] |= FATTR4_WORD1_OWNER_GROUP;
|
||||
info.attrmask.count = 2;
|
||||
}
|
||||
}
|
||||
if (args->query & DACL_SECURITY_INFORMATION) {
|
||||
BOOL dacl_present, dacl_default;
|
||||
PACL acl;
|
||||
dprintf(ACLLVL, "handle_setacl: DACL_SECURITY_INFORMATION\n");
|
||||
status = GetSecurityDescriptorDacl(args->sec_desc, &dacl_present,
|
||||
&acl, &dacl_default);
|
||||
if (!status) {
|
||||
status = GetLastError();
|
||||
eprintf("GetSecurityDescriptorDacl failed with %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
status = GetSecurityDescriptorOwner(args->sec_desc, &sid, &sid_default);
|
||||
if (!status) {
|
||||
status = GetLastError();
|
||||
eprintf("GetSecurityDescriptorOwner failed with %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
status = GetSecurityDescriptorGroup(args->sec_desc, &gsid, &gsid_default);
|
||||
if (!status) {
|
||||
status = GetLastError();
|
||||
eprintf("GetSecurityDescriptorOwner failed with %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
status = map_dacl_2_nfs4acl(acl, sid, gsid, &nfs4_acl, state->type,
|
||||
localdomain_name);
|
||||
if (status)
|
||||
goto out;
|
||||
else {
|
||||
info.acl = &nfs4_acl;
|
||||
info.attrmask.arr[0] |= FATTR4_WORD0_ACL;
|
||||
if (!info.attrmask.count)
|
||||
info.attrmask.count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* break read delegations before SETATTR */
|
||||
nfs41_delegation_return(state->session, &state->file,
|
||||
OPEN_DELEGATE_WRITE, FALSE);
|
||||
|
||||
nfs41_open_stateid_arg(state, &stateid);
|
||||
status = nfs41_setattr(state->session, &state->file, &stateid, &info);
|
||||
if (status) {
|
||||
dprintf(ACLLVL, "handle_setacl: nfs41_setattr() failed with error %s.\n",
|
||||
nfs_error_string(status));
|
||||
status = nfs_to_windows_error(status, ERROR_NOT_SUPPORTED);
|
||||
}
|
||||
args->ctime = info.change;
|
||||
if (args->query & DACL_SECURITY_INFORMATION)
|
||||
free(nfs4_acl.aces);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int marshall_setacl(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
|
||||
{
|
||||
setacl_upcall_args *args = &upcall->args.setacl;
|
||||
return safe_write(&buffer, length, &args->ctime, sizeof(args->ctime));
|
||||
}
|
||||
|
||||
const nfs41_upcall_op nfs41_op_setacl = {
|
||||
parse_setacl,
|
||||
handle_setacl,
|
||||
marshall_setacl
|
||||
};
|
547
reactos/base/services/nfsd/callback_server.c
Normal file
547
reactos/base/services/nfsd/callback_server.c
Normal file
|
@ -0,0 +1,547 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#include "nfs41_ops.h"
|
||||
#include "delegation.h"
|
||||
#include "nfs41_callback.h"
|
||||
#include "daemon_debug.h"
|
||||
|
||||
|
||||
#define CBSLVL 2 /* dprintf level for callback server logging */
|
||||
|
||||
|
||||
static const char g_server_tag[] = "ms-nfs41-callback";
|
||||
|
||||
|
||||
/* callback session */
|
||||
static void replay_cache_write(
|
||||
IN nfs41_cb_session *session,
|
||||
IN struct cb_compound_args *args,
|
||||
IN struct cb_compound_res *res,
|
||||
IN bool_t cachethis);
|
||||
|
||||
void nfs41_callback_session_init(
|
||||
IN nfs41_session *session)
|
||||
{
|
||||
/* initialize the replay cache with status NFS4ERR_SEQ_MISORDERED */
|
||||
struct cb_compound_res res = { 0 };
|
||||
StringCchCopyA(res.tag.str, CB_COMPOUND_MAX_TAG, g_server_tag);
|
||||
res.tag.len = sizeof(g_server_tag);
|
||||
res.status = NFS4ERR_SEQ_MISORDERED;
|
||||
|
||||
session->cb_session.cb_sessionid = session->session_id;
|
||||
|
||||
replay_cache_write(&session->cb_session, NULL, &res, FALSE);
|
||||
}
|
||||
|
||||
|
||||
/* OP_CB_LAYOUTRECALL */
|
||||
static enum_t handle_cb_layoutrecall(
|
||||
IN nfs41_rpc_clnt *rpc_clnt,
|
||||
IN struct cb_layoutrecall_args *args,
|
||||
OUT struct cb_layoutrecall_res *res)
|
||||
{
|
||||
enum pnfs_status status;
|
||||
|
||||
status = pnfs_file_layout_recall(rpc_clnt->client, args);
|
||||
switch (status) {
|
||||
case PNFS_PENDING:
|
||||
/* not enough information to process the recall yet */
|
||||
res->status = NFS4ERR_DELAY;
|
||||
break;
|
||||
default:
|
||||
/* forgetful model for layout recalls */
|
||||
res->status = NFS4ERR_NOMATCHING_LAYOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
dprintf(CBSLVL, " OP_CB_LAYOUTRECALL { %s, %s, recall %u } %s\n",
|
||||
pnfs_layout_type_string(args->type),
|
||||
pnfs_iomode_string(args->iomode), args->recall.type,
|
||||
nfs_error_string(res->status));
|
||||
return res->status;
|
||||
}
|
||||
|
||||
/* OP_CB_RECALL_SLOT */
|
||||
static enum_t handle_cb_recall_slot(
|
||||
IN nfs41_rpc_clnt *rpc_clnt,
|
||||
IN struct cb_recall_slot_args *args,
|
||||
OUT struct cb_recall_slot_res *res)
|
||||
{
|
||||
res->status = nfs41_session_recall_slot(rpc_clnt->client->session,
|
||||
args->target_highest_slotid);
|
||||
|
||||
dprintf(CBSLVL, " OP_CB_RECALL_SLOT { %u } %s\n",
|
||||
args->target_highest_slotid, nfs_error_string(res->status));
|
||||
return res->status;
|
||||
}
|
||||
|
||||
/* OP_CB_SEQUENCE */
|
||||
static enum_t handle_cb_sequence(
|
||||
IN nfs41_rpc_clnt *rpc_clnt,
|
||||
IN struct cb_sequence_args *args,
|
||||
OUT struct cb_sequence_res *res,
|
||||
OUT nfs41_cb_session **session_out,
|
||||
OUT bool_t *cachethis)
|
||||
{
|
||||
nfs41_cb_session *cb_session = &rpc_clnt->client->session->cb_session;
|
||||
uint32_t status = NFS4_OK;
|
||||
res->status = NFS4_OK;
|
||||
|
||||
*session_out = cb_session;
|
||||
|
||||
/* validate the sessionid */
|
||||
if (memcmp(cb_session->cb_sessionid, args->sessionid,
|
||||
NFS4_SESSIONID_SIZE)) {
|
||||
eprintf("[cb] received sessionid doesn't match session\n");
|
||||
res->status = NFS4ERR_BADSESSION;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* we only support 1 slot for the back channel so slotid MUST be 0 */
|
||||
if (args->slotid != 0) {
|
||||
eprintf("[cb] received unexpected slotid=%d\n", args->slotid);
|
||||
res->status = NFS4ERR_BADSLOT;
|
||||
goto out;
|
||||
}
|
||||
if (args->highest_slotid != 0) {
|
||||
eprintf("[cb] received unexpected highest_slotid=%d\n",
|
||||
args->highest_slotid);
|
||||
res->status = NFS4ERR_BAD_HIGH_SLOT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* check for a retry with the same seqid */
|
||||
if (args->sequenceid == cb_session->cb_seqnum) {
|
||||
if (!cb_session->replay.res.length) {
|
||||
/* return success for sequence, but fail the next operation */
|
||||
res->status = NFS4_OK;
|
||||
status = NFS4ERR_RETRY_UNCACHED_REP;
|
||||
} else {
|
||||
/* return NFS4ERR_SEQ_FALSE_RETRY for all replays; if the retry
|
||||
* turns out to be valid, this response will be replaced anyway */
|
||||
status = res->status = NFS4ERR_SEQ_FALSE_RETRY;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* error on any unexpected seqids */
|
||||
if (args->sequenceid != cb_session->cb_seqnum+1) {
|
||||
eprintf("[cb] bad received seq#=%d, expected=%d\n",
|
||||
args->sequenceid, cb_session->cb_seqnum+1);
|
||||
res->status = NFS4ERR_SEQ_MISORDERED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cb_session->cb_seqnum = args->sequenceid;
|
||||
*cachethis = args->cachethis;
|
||||
|
||||
memcpy(res->ok.sessionid, args->sessionid, NFS4_SESSIONID_SIZE);
|
||||
res->ok.sequenceid = args->sequenceid;
|
||||
res->ok.slotid = args->slotid;
|
||||
res->ok.highest_slotid = args->highest_slotid;
|
||||
res->ok.target_highest_slotid = args->highest_slotid;
|
||||
|
||||
out:
|
||||
dprintf(CBSLVL, " OP_CB_SEQUENCE { seqid %u, slot %u, cachethis %d } "
|
||||
"%s\n", args->sequenceid, args->slotid, args->cachethis,
|
||||
nfs_error_string(res->status));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* OP_CB_GETATTR */
|
||||
static enum_t handle_cb_getattr(
|
||||
IN nfs41_rpc_clnt *rpc_clnt,
|
||||
IN struct cb_getattr_args *args,
|
||||
OUT struct cb_getattr_res *res)
|
||||
{
|
||||
/* look up cached attributes for the given filehandle */
|
||||
res->status = nfs41_delegation_getattr(rpc_clnt->client,
|
||||
&args->fh, &args->attr_request, &res->info);
|
||||
return res->status;
|
||||
}
|
||||
|
||||
/* OP_CB_RECALL */
|
||||
static enum_t handle_cb_recall(
|
||||
IN nfs41_rpc_clnt *rpc_clnt,
|
||||
IN struct cb_recall_args *args,
|
||||
OUT struct cb_recall_res *res)
|
||||
{
|
||||
/* return the delegation asynchronously */
|
||||
res->status = nfs41_delegation_recall(rpc_clnt->client,
|
||||
&args->fh, &args->stateid, args->truncate);
|
||||
return res->status;
|
||||
}
|
||||
|
||||
/* OP_CB_NOTIFY_DEVICEID */
|
||||
static enum_t handle_cb_notify_deviceid(
|
||||
IN nfs41_rpc_clnt *rpc_clnt,
|
||||
IN struct cb_notify_deviceid_args *args,
|
||||
OUT struct cb_notify_deviceid_res *res)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < args->change_count; i++) {
|
||||
pnfs_file_device_notify(rpc_clnt->client->devices,
|
||||
&args->change_list[i]);
|
||||
}
|
||||
res->status = NFS4_OK;
|
||||
return res->status;
|
||||
}
|
||||
|
||||
static void replay_cache_write(
|
||||
IN nfs41_cb_session *session,
|
||||
IN OPTIONAL struct cb_compound_args *args,
|
||||
IN struct cb_compound_res *res,
|
||||
IN bool_t cachethis)
|
||||
{
|
||||
XDR xdr;
|
||||
uint32_t i;
|
||||
|
||||
session->replay.arg.length = 0;
|
||||
session->replay.res.length = 0;
|
||||
|
||||
/* encode the reply directly into the replay cache */
|
||||
xdrmem_create(&xdr, (char*)session->replay.res.buffer,
|
||||
NFS41_MAX_SERVER_CACHE, XDR_ENCODE);
|
||||
|
||||
/* always try to cache the result */
|
||||
if (proc_cb_compound_res(&xdr, res)) {
|
||||
session->replay.res.length = XDR_GETPOS(&xdr);
|
||||
|
||||
if (args) {
|
||||
/* encode the arguments into the request cache */
|
||||
xdrmem_create(&xdr, (char*)session->replay.arg.buffer,
|
||||
NFS41_MAX_SERVER_CACHE, XDR_ENCODE);
|
||||
|
||||
if (proc_cb_compound_args(&xdr, args))
|
||||
session->replay.arg.length = XDR_GETPOS(&xdr);
|
||||
}
|
||||
} else if (cachethis) {
|
||||
/* on failure, only return errors if caching was requested */
|
||||
res->status = NFS4ERR_REP_TOO_BIG_TO_CACHE;
|
||||
|
||||
/* find the first operation that failed to encode */
|
||||
for (i = 0; i < res->resarray_count; i++) {
|
||||
if (!res->resarray[i].xdr_ok) {
|
||||
res->resarray[i].res.status = NFS4ERR_REP_TOO_BIG_TO_CACHE;
|
||||
res->resarray_count = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool_t replay_validate_args(
|
||||
IN struct cb_compound_args *args,
|
||||
IN const struct replay_cache *cache)
|
||||
{
|
||||
char buffer[NFS41_MAX_SERVER_CACHE];
|
||||
XDR xdr;
|
||||
|
||||
/* encode the current arguments into a temporary buffer */
|
||||
xdrmem_create(&xdr, buffer, NFS41_MAX_SERVER_CACHE, XDR_ENCODE);
|
||||
|
||||
if (!proc_cb_compound_args(&xdr, args))
|
||||
return FALSE;
|
||||
|
||||
/* must match the cached length */
|
||||
if (XDR_GETPOS(&xdr) != cache->length)
|
||||
return FALSE;
|
||||
|
||||
/* must match the cached buffer contents */
|
||||
return memcmp(cache->buffer, buffer, cache->length) == 0;
|
||||
}
|
||||
|
||||
static bool_t replay_validate_ops(
|
||||
IN const struct cb_compound_args *args,
|
||||
IN const struct cb_compound_res *res)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < res->resarray_count; i++) {
|
||||
/* can't have more operations than the request */
|
||||
if (i >= args->argarray_count)
|
||||
return FALSE;
|
||||
|
||||
/* each opnum must match the request */
|
||||
if (args->argarray[i].opnum != res->resarray[i].opnum)
|
||||
return FALSE;
|
||||
|
||||
if (res->resarray[i].res.status)
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int replay_cache_read(
|
||||
IN nfs41_cb_session *session,
|
||||
IN struct cb_compound_args *args,
|
||||
OUT struct cb_compound_res **res_out)
|
||||
{
|
||||
XDR xdr;
|
||||
struct cb_compound_res *replay;
|
||||
struct cb_compound_res *res = *res_out;
|
||||
uint32_t status = NFS4_OK;
|
||||
|
||||
replay = calloc(1, sizeof(struct cb_compound_res));
|
||||
if (replay == NULL) {
|
||||
eprintf("[cb] failed to allocate replay buffer\n");
|
||||
status = NFS4ERR_SERVERFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* decode the response from the replay cache */
|
||||
xdrmem_create(&xdr, (char*)session->replay.res.buffer,
|
||||
NFS41_MAX_SERVER_CACHE, XDR_DECODE);
|
||||
if (!proc_cb_compound_res(&xdr, replay)) {
|
||||
eprintf("[cb] failed to decode replay buffer\n");
|
||||
status = NFS4ERR_SEQ_FALSE_RETRY;
|
||||
goto out_free_replay;
|
||||
}
|
||||
|
||||
/* if we cached the arguments, use them to validate the retry */
|
||||
if (session->replay.arg.length) {
|
||||
if (!replay_validate_args(args, &session->replay.arg)) {
|
||||
eprintf("[cb] retry attempt with different arguments\n");
|
||||
status = NFS4ERR_SEQ_FALSE_RETRY;
|
||||
goto out_free_replay;
|
||||
}
|
||||
} else { /* otherwise, comparing opnums is the best we can do */
|
||||
if (!replay_validate_ops(args, replay)) {
|
||||
eprintf("[cb] retry attempt with different operations\n");
|
||||
status = NFS4ERR_SEQ_FALSE_RETRY;
|
||||
goto out_free_replay;
|
||||
}
|
||||
}
|
||||
|
||||
/* free previous response and replace it with the replay */
|
||||
xdr.x_op = XDR_FREE;
|
||||
proc_cb_compound_res(&xdr, res);
|
||||
|
||||
dprintf(2, "[cb] retry: returning cached response\n");
|
||||
|
||||
*res_out = replay;
|
||||
out:
|
||||
return status;
|
||||
|
||||
out_free_replay:
|
||||
xdr.x_op = XDR_FREE;
|
||||
proc_cb_compound_res(&xdr, replay);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* CB_COMPOUND */
|
||||
static void handle_cb_compound(nfs41_rpc_clnt *rpc_clnt, cb_req *req, struct cb_compound_res **reply)
|
||||
{
|
||||
struct cb_compound_args args = { 0 };
|
||||
struct cb_compound_res *res = NULL;
|
||||
struct cb_argop *argop;
|
||||
struct cb_resop *resop;
|
||||
XDR *xdr = (XDR*)req->xdr;
|
||||
nfs41_cb_session *session = NULL;
|
||||
bool_t cachethis = FALSE;
|
||||
uint32_t i, status = NFS4_OK;
|
||||
|
||||
dprintf(CBSLVL, "--> handle_cb_compound()\n");
|
||||
|
||||
/* decode the arguments */
|
||||
if (!proc_cb_compound_args(xdr, &args)) {
|
||||
status = NFS4ERR_BADXDR;
|
||||
eprintf("failed to decode compound arguments\n");
|
||||
}
|
||||
|
||||
/* allocate the compound results */
|
||||
res = calloc(1, sizeof(struct cb_compound_res));
|
||||
if (res == NULL) {
|
||||
status = NFS4ERR_SERVERFAULT;
|
||||
goto out;
|
||||
}
|
||||
res->status = status;
|
||||
StringCchCopyA(res->tag.str, CB_COMPOUND_MAX_TAG, g_server_tag);
|
||||
res->tag.str[CB_COMPOUND_MAX_TAG-1] = 0;
|
||||
res->tag.len = (uint32_t)strlen(res->tag.str);
|
||||
res->resarray = calloc(args.argarray_count, sizeof(struct cb_resop));
|
||||
if (res->resarray == NULL) {
|
||||
res->status = NFS4ERR_SERVERFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dprintf(CBSLVL, "CB_COMPOUND('%s', %u)\n", args.tag.str, args.argarray_count);
|
||||
if (args.minorversion != 1) {
|
||||
res->status = NFS4ERR_MINOR_VERS_MISMATCH; //XXXXX
|
||||
eprintf("args.minorversion %u != 1\n", args.minorversion);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* handle each operation in the compound */
|
||||
for (i = 0; i < args.argarray_count && res->status == NFS4_OK; i++) {
|
||||
argop = &args.argarray[i];
|
||||
resop = &res->resarray[i];
|
||||
resop->opnum = argop->opnum;
|
||||
res->resarray_count++;
|
||||
|
||||
/* 20.9.3: The error NFS4ERR_SEQUENCE_POS MUST be returned
|
||||
* when CB_SEQUENCE is found in any position in a CB_COMPOUND
|
||||
* beyond the first. If any other operation is in the first
|
||||
* position of CB_COMPOUND, NFS4ERR_OP_NOT_IN_SESSION MUST
|
||||
* be returned.
|
||||
*/
|
||||
if (i == 0 && argop->opnum != OP_CB_SEQUENCE) {
|
||||
res->status = resop->res.status = NFS4ERR_OP_NOT_IN_SESSION;
|
||||
break;
|
||||
}
|
||||
if (i != 0 && argop->opnum == OP_CB_SEQUENCE) {
|
||||
res->status = resop->res.status = NFS4ERR_SEQUENCE_POS;
|
||||
break;
|
||||
}
|
||||
if (status == NFS4ERR_RETRY_UNCACHED_REP) {
|
||||
res->status = resop->res.status = status;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (argop->opnum) {
|
||||
case OP_CB_LAYOUTRECALL:
|
||||
dprintf(1, "OP_CB_LAYOUTRECALL\n");
|
||||
res->status = handle_cb_layoutrecall(rpc_clnt,
|
||||
&argop->args.layoutrecall, &resop->res.layoutrecall);
|
||||
break;
|
||||
case OP_CB_RECALL_SLOT:
|
||||
dprintf(1, "OP_CB_RECALL_SLOT\n");
|
||||
res->status = handle_cb_recall_slot(rpc_clnt,
|
||||
&argop->args.recall_slot, &resop->res.recall_slot);
|
||||
break;
|
||||
case OP_CB_SEQUENCE:
|
||||
dprintf(1, "OP_CB_SEQUENCE\n");
|
||||
status = handle_cb_sequence(rpc_clnt, &argop->args.sequence,
|
||||
&resop->res.sequence, &session, &cachethis);
|
||||
|
||||
if (status == NFS4ERR_SEQ_FALSE_RETRY) {
|
||||
/* replace the current results with the cached response */
|
||||
status = replay_cache_read(session, &args, &res);
|
||||
if (status) res->status = status;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (status == NFS4_OK)
|
||||
res->status = resop->res.sequence.status;
|
||||
break;
|
||||
case OP_CB_GETATTR:
|
||||
dprintf(1, "OP_CB_GETATTR\n");
|
||||
res->status = handle_cb_getattr(rpc_clnt,
|
||||
&argop->args.getattr, &resop->res.getattr);
|
||||
break;
|
||||
case OP_CB_RECALL:
|
||||
dprintf(1, "OP_CB_RECALL\n");
|
||||
res->status = handle_cb_recall(rpc_clnt,
|
||||
&argop->args.recall, &resop->res.recall);
|
||||
break;
|
||||
case OP_CB_NOTIFY:
|
||||
dprintf(1, "OP_CB_NOTIFY\n");
|
||||
res->status = NFS4ERR_NOTSUPP;
|
||||
break;
|
||||
case OP_CB_PUSH_DELEG:
|
||||
dprintf(1, "OP_CB_PUSH_DELEG\n");
|
||||
res->status = NFS4ERR_NOTSUPP;
|
||||
break;
|
||||
case OP_CB_RECALL_ANY:
|
||||
dprintf(1, "OP_CB_RECALL_ANY\n");
|
||||
res->status = NFS4ERR_NOTSUPP;
|
||||
break;
|
||||
case OP_CB_RECALLABLE_OBJ_AVAIL:
|
||||
dprintf(1, "OP_CB_RECALLABLE_OBJ_AVAIL\n");
|
||||
res->status = NFS4ERR_NOTSUPP;
|
||||
break;
|
||||
case OP_CB_WANTS_CANCELLED:
|
||||
dprintf(1, "OP_CB_WANTS_CANCELLED\n");
|
||||
res->status = NFS4ERR_NOTSUPP;
|
||||
break;
|
||||
case OP_CB_NOTIFY_LOCK:
|
||||
dprintf(1, "OP_CB_NOTIFY_LOCK\n");
|
||||
res->status = NFS4ERR_NOTSUPP;
|
||||
break;
|
||||
case OP_CB_NOTIFY_DEVICEID:
|
||||
dprintf(1, "OP_CB_NOTIFY_DEVICEID\n");
|
||||
res->status = NFS4_OK;
|
||||
break;
|
||||
case OP_CB_ILLEGAL:
|
||||
dprintf(1, "OP_CB_ILLEGAL\n");
|
||||
res->status = NFS4ERR_NOTSUPP;
|
||||
break;
|
||||
default:
|
||||
eprintf("operation %u not supported\n", argop->opnum);
|
||||
res->status = NFS4ERR_NOTSUPP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* always attempt to cache the reply */
|
||||
if (session)
|
||||
replay_cache_write(session, &args, res, cachethis);
|
||||
out:
|
||||
/* free the arguments */
|
||||
xdr->x_op = XDR_FREE;
|
||||
proc_cb_compound_args(xdr, &args);
|
||||
|
||||
*reply = res;
|
||||
dprintf(CBSLVL, "<-- handle_cb_compound() returning %s (%u results)\n",
|
||||
nfs_error_string(res ? res->status : status),
|
||||
res ? res->resarray_count : 0);
|
||||
}
|
||||
|
||||
#ifdef __REACTOS__
|
||||
int nfs41_handle_callback(void *rpc_clnt, void *cb, void * dummy)
|
||||
{
|
||||
struct cb_compound_res **reply = dummy;
|
||||
#else
|
||||
int nfs41_handle_callback(void *rpc_clnt, void *cb, struct cb_compound_res **reply)
|
||||
{
|
||||
#endif
|
||||
nfs41_rpc_clnt *rpc = (nfs41_rpc_clnt *)rpc_clnt;
|
||||
cb_req *request = (cb_req *)cb;
|
||||
uint32_t status = 0;
|
||||
|
||||
dprintf(1, "nfs41_handle_callback: received call\n");
|
||||
if (request->rq_prog != NFS41_RPC_CBPROGRAM) {
|
||||
eprintf("invalid rpc program %u\n", request->rq_prog);
|
||||
status = 2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (request->rq_proc) {
|
||||
case CB_NULL:
|
||||
dprintf(1, "CB_NULL\n");
|
||||
break;
|
||||
|
||||
case CB_COMPOUND:
|
||||
dprintf(1, "CB_COMPOUND\n");
|
||||
handle_cb_compound(rpc, request, reply);
|
||||
break;
|
||||
|
||||
default:
|
||||
dprintf(1, "invalid rpc procedure %u\n", request->rq_proc);
|
||||
status = 3;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
}
|
659
reactos/base/services/nfsd/callback_xdr.c
Normal file
659
reactos/base/services/nfsd/callback_xdr.c
Normal file
|
@ -0,0 +1,659 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include "nfs41_callback.h"
|
||||
#include "nfs41_ops.h"
|
||||
#include "util.h"
|
||||
#include "daemon_debug.h"
|
||||
|
||||
|
||||
#define CBXLVL 2 /* dprintf level for callback xdr logging */
|
||||
#ifdef __REACTOS__
|
||||
#define CBX_ERR(msg) dprintf((CBXLVL), "%s: failed at %s\n", __FUNCTION__, msg)
|
||||
#else
|
||||
#define CBX_ERR(msg) dprintf((CBXLVL), __FUNCTION__ ": failed at " msg "\n")
|
||||
#endif
|
||||
|
||||
/* common types */
|
||||
bool_t xdr_bitmap4(XDR *xdr, bitmap4 *bitmap);
|
||||
bool_t xdr_fattr4(XDR *xdr, fattr4 *fattr);
|
||||
|
||||
static bool_t common_stateid(XDR *xdr, stateid4 *stateid)
|
||||
{
|
||||
return xdr_u_int32_t(xdr, &stateid->seqid)
|
||||
&& xdr_opaque(xdr, (char*)stateid->other, NFS4_STATEID_OTHER);
|
||||
}
|
||||
|
||||
static bool_t common_fh(XDR *xdr, nfs41_fh *fh)
|
||||
{
|
||||
return xdr_u_int32_t(xdr, &fh->len)
|
||||
&& fh->len <= NFS4_FHSIZE
|
||||
&& xdr_opaque(xdr, (char*)fh->fh, fh->len);
|
||||
}
|
||||
|
||||
static bool_t common_fsid(XDR *xdr, nfs41_fsid *fsid)
|
||||
{
|
||||
return xdr_u_int64_t(xdr, &fsid->major)
|
||||
&& xdr_u_int64_t(xdr, &fsid->minor);
|
||||
}
|
||||
|
||||
static bool_t common_notify4(XDR *xdr, struct notify4 *notify)
|
||||
{
|
||||
return xdr_bitmap4(xdr, ¬ify->mask)
|
||||
&& xdr_bytes(xdr, ¬ify->list, ¬ify->len, NFS4_OPAQUE_LIMIT);
|
||||
}
|
||||
|
||||
/* OP_CB_LAYOUTRECALL */
|
||||
static bool_t op_cb_layoutrecall_file(XDR *xdr, struct cb_recall_file *args)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = common_fh(xdr, &args->fh);
|
||||
if (!result) { CBX_ERR("layoutrecall_file.fh"); goto out; }
|
||||
|
||||
result = xdr_u_int64_t(xdr, &args->offset);
|
||||
if (!result) { CBX_ERR("layoutrecall_file.offset"); goto out; }
|
||||
|
||||
result = xdr_u_int64_t(xdr, &args->length);
|
||||
if (!result) { CBX_ERR("layoutrecall_file.length"); goto out; }
|
||||
|
||||
result = common_stateid(xdr, &args->stateid);
|
||||
if (!result) { CBX_ERR("layoutrecall_file.stateid"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t op_cb_layoutrecall_fsid(XDR *xdr, union cb_recall_file_args *args)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = common_fsid(xdr, &args->fsid);
|
||||
if (!result) { CBX_ERR("layoutrecall_fsid.fsid"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static const struct xdr_discrim cb_layoutrecall_discrim[] = {
|
||||
{ PNFS_RETURN_FILE, (xdrproc_t)op_cb_layoutrecall_file },
|
||||
{ PNFS_RETURN_FSID, (xdrproc_t)op_cb_layoutrecall_fsid },
|
||||
{ PNFS_RETURN_ALL, (xdrproc_t)xdr_void },
|
||||
{ 0, NULL_xdrproc_t }
|
||||
};
|
||||
|
||||
static bool_t op_cb_layoutrecall_args(XDR *xdr, struct cb_layoutrecall_args *args)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_enum(xdr, (enum_t*)&args->type);
|
||||
if (!result) { CBX_ERR("layoutrecall_args.type"); goto out; }
|
||||
|
||||
result = xdr_enum(xdr, (enum_t*)&args->iomode);
|
||||
if (!result) { CBX_ERR("layoutrecall_args.iomode"); goto out; }
|
||||
|
||||
result = xdr_bool(xdr, &args->changed);
|
||||
if (!result) { CBX_ERR("layoutrecall_args.changed"); goto out; }
|
||||
|
||||
result = xdr_union(xdr, (enum_t*)&args->recall.type,
|
||||
(char*)&args->recall.args, cb_layoutrecall_discrim, NULL_xdrproc_t);
|
||||
if (!result) { CBX_ERR("layoutrecall_args.recall"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t op_cb_layoutrecall_res(XDR *xdr, struct cb_layoutrecall_res *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_enum(xdr, &res->status);
|
||||
if (!result) { CBX_ERR("layoutrecall_res.status"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* OP_CB_RECALL_SLOT */
|
||||
static bool_t op_cb_recall_slot_args(XDR *xdr, struct cb_recall_slot_args *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
|
||||
if (!result) { CBX_ERR("recall_slot.target_highest_slotid"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t op_cb_recall_slot_res(XDR *xdr, struct cb_recall_slot_res *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_enum(xdr, &res->status);
|
||||
if (!result) { CBX_ERR("recall_slot.status"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* OP_CB_SEQUENCE */
|
||||
static bool_t op_cb_sequence_ref(XDR *xdr, struct cb_sequence_ref *args)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_u_int32_t(xdr, &args->sequenceid);
|
||||
if (!result) { CBX_ERR("sequence_ref.sequenceid"); goto out; }
|
||||
|
||||
result = xdr_u_int32_t(xdr, &args->slotid);
|
||||
if (!result) { CBX_ERR("sequence_ref.slotid"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t op_cb_sequence_ref_list(XDR *xdr, struct cb_sequence_ref_list *args)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_opaque(xdr, args->sessionid, NFS4_SESSIONID_SIZE);
|
||||
if (!result) { CBX_ERR("sequence_ref_list.sessionid"); goto out; }
|
||||
|
||||
result = xdr_array(xdr, (char**)&args->calls, &args->call_count,
|
||||
64, sizeof(struct cb_sequence_ref), (xdrproc_t)op_cb_sequence_ref);
|
||||
if (!result) { CBX_ERR("sequence_ref_list.calls"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t op_cb_sequence_args(XDR *xdr, struct cb_sequence_args *args)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_opaque(xdr, args->sessionid, NFS4_SESSIONID_SIZE);
|
||||
if (!result) { CBX_ERR("sequence_args.sessionid"); goto out; }
|
||||
|
||||
result = xdr_u_int32_t(xdr, &args->sequenceid);
|
||||
if (!result) { CBX_ERR("sequence_args.sequenceid"); goto out; }
|
||||
|
||||
result = xdr_u_int32_t(xdr, &args->slotid);
|
||||
if (!result) { CBX_ERR("sequence_args.slotid"); goto out; }
|
||||
|
||||
result = xdr_u_int32_t(xdr, &args->highest_slotid);
|
||||
if (!result) { CBX_ERR("sequence_args.highest_slotid"); goto out; }
|
||||
|
||||
result = xdr_bool(xdr, &args->cachethis);
|
||||
if (!result) { CBX_ERR("sequence_args.cachethis"); goto out; }
|
||||
|
||||
result = xdr_array(xdr, (char**)&args->ref_lists,
|
||||
&args->ref_list_count, 64, sizeof(struct cb_sequence_ref_list),
|
||||
(xdrproc_t)op_cb_sequence_ref_list);
|
||||
if (!result) { CBX_ERR("sequence_args.ref_lists"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t op_cb_sequence_res_ok(XDR *xdr, struct cb_sequence_res_ok *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_opaque(xdr, res->sessionid, NFS4_SESSIONID_SIZE);
|
||||
if (!result) { CBX_ERR("sequence_res.sessionid"); goto out; }
|
||||
|
||||
result = xdr_u_int32_t(xdr, &res->sequenceid);
|
||||
if (!result) { CBX_ERR("sequence_res.sequenceid"); goto out; }
|
||||
|
||||
result = xdr_u_int32_t(xdr, &res->slotid);
|
||||
if (!result) { CBX_ERR("sequence_res.slotid"); goto out; }
|
||||
|
||||
result = xdr_u_int32_t(xdr, &res->highest_slotid);
|
||||
if (!result) { CBX_ERR("sequence_res.highest_slotid"); goto out; }
|
||||
|
||||
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
|
||||
if (!result) { CBX_ERR("sequence_res.target_highest_slotid"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static const struct xdr_discrim cb_sequence_res_discrim[] = {
|
||||
{ NFS4_OK, (xdrproc_t)op_cb_sequence_res_ok },
|
||||
{ 0, NULL_xdrproc_t }
|
||||
};
|
||||
|
||||
static bool_t op_cb_sequence_res(XDR *xdr, struct cb_sequence_res *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_union(xdr, &res->status, (char*)&res->ok,
|
||||
cb_sequence_res_discrim, (xdrproc_t)xdr_void);
|
||||
if (!result) { CBX_ERR("seq:argop.args"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/* OP_CB_GETATTR */
|
||||
static bool_t op_cb_getattr_args(XDR *xdr, struct cb_getattr_args *args)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = common_fh(xdr, &args->fh);
|
||||
if (!result) { CBX_ERR("getattr.fh"); goto out; }
|
||||
|
||||
result = xdr_bitmap4(xdr, &args->attr_request);
|
||||
if (!result) { CBX_ERR("getattr.attr_request"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t info_to_fattr4(nfs41_file_info *info, fattr4 *fattr)
|
||||
{
|
||||
XDR fattr_xdr;
|
||||
bool_t result = TRUE;
|
||||
|
||||
/* encode nfs41_file_info into fattr4 */
|
||||
xdrmem_create(&fattr_xdr, (char*)fattr->attr_vals,
|
||||
NFS4_OPAQUE_LIMIT, XDR_ENCODE);
|
||||
|
||||
/* The only attributes that the server can reliably
|
||||
* query via CB_GETATTR are size and change. */
|
||||
if (bitmap_isset(&info->attrmask, 0, FATTR4_WORD0_CHANGE)) {
|
||||
result = xdr_u_hyper(&fattr_xdr, &info->change);
|
||||
if (!result) { CBX_ERR("getattr.info.change"); goto out; }
|
||||
bitmap_set(&fattr->attrmask, 0, FATTR4_WORD0_CHANGE);
|
||||
}
|
||||
if (bitmap_isset(&info->attrmask, 0, FATTR4_WORD0_SIZE)) {
|
||||
result = xdr_u_hyper(&fattr_xdr, &info->size);
|
||||
if (!result) { CBX_ERR("getattr.info.size"); goto out; }
|
||||
bitmap_set(&fattr->attrmask, 0, FATTR4_WORD0_SIZE);
|
||||
}
|
||||
fattr->attr_vals_len = xdr_getpos(&fattr_xdr);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t op_cb_getattr_res(XDR *xdr, struct cb_getattr_res *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_enum(xdr, &res->status);
|
||||
if (!result) { CBX_ERR("getattr.status"); goto out; }
|
||||
|
||||
if (res->status == NFS4_OK) {
|
||||
fattr4 fattr = { 0 };
|
||||
|
||||
result = info_to_fattr4(&res->info, &fattr);
|
||||
if (!result) { goto out; }
|
||||
|
||||
result = xdr_fattr4(xdr, &fattr);
|
||||
if (!result) { CBX_ERR("getattr.obj_attributes"); goto out; }
|
||||
}
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/* OP_CB_RECALL */
|
||||
static bool_t op_cb_recall_args(XDR *xdr, struct cb_recall_args *args)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = common_stateid(xdr, &args->stateid);
|
||||
if (!result) { CBX_ERR("recall.stateid"); goto out; }
|
||||
|
||||
result = xdr_bool(xdr, &args->truncate);
|
||||
if (!result) { CBX_ERR("recall.truncate"); goto out; }
|
||||
|
||||
result = common_fh(xdr, &args->fh);
|
||||
if (!result) { CBX_ERR("recall.fh"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t op_cb_recall_res(XDR *xdr, struct cb_recall_res *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_enum(xdr, &res->status);
|
||||
if (!result) { CBX_ERR("recall.status"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/* OP_CB_NOTIFY */
|
||||
static bool_t op_cb_notify_args(XDR *xdr, struct cb_notify_args *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
|
||||
if (!result) { CBX_ERR("notify.target_highest_slotid"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t op_cb_notify_res(XDR *xdr, struct cb_notify_res *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_enum(xdr, &res->status);
|
||||
if (!result) { CBX_ERR("notify.status"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/* OP_CB_PUSH_DELEG */
|
||||
static bool_t op_cb_push_deleg_args(XDR *xdr, struct cb_push_deleg_args *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
|
||||
if (!result) { CBX_ERR("push_deleg.target_highest_slotid"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t op_cb_push_deleg_res(XDR *xdr, struct cb_push_deleg_res *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_enum(xdr, &res->status);
|
||||
if (!result) { CBX_ERR("push_deleg.status"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/* OP_CB_RECALL_ANY */
|
||||
static bool_t op_cb_recall_any_args(XDR *xdr, struct cb_recall_any_args *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
|
||||
if (!result) { CBX_ERR("recall_any.target_highest_slotid"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t op_cb_recall_any_res(XDR *xdr, struct cb_recall_any_res *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_enum(xdr, &res->status);
|
||||
if (!result) { CBX_ERR("recall_any.status"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/* OP_CB_RECALLABLE_OBJ_AVAIL */
|
||||
static bool_t op_cb_recallable_obj_avail_args(XDR *xdr, struct cb_recallable_obj_avail_args *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
|
||||
if (!result) { CBX_ERR("recallable_obj_avail.target_highest_slotid"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t op_cb_recallable_obj_avail_res(XDR *xdr, struct cb_recallable_obj_avail_res *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_enum(xdr, &res->status);
|
||||
if (!result) { CBX_ERR("recallable_obj_avail.status"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/* OP_CB_WANTS_CANCELLED */
|
||||
static bool_t op_cb_wants_cancelled_args(XDR *xdr, struct cb_wants_cancelled_args *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
|
||||
if (!result) { CBX_ERR("wants_cancelled.target_highest_slotid"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t op_cb_wants_cancelled_res(XDR *xdr, struct cb_wants_cancelled_res *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_enum(xdr, &res->status);
|
||||
if (!result) { CBX_ERR("wants_cancelled.status"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/* OP_CB_NOTIFY_LOCK */
|
||||
static bool_t op_cb_notify_lock_args(XDR *xdr, struct cb_notify_lock_args *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
|
||||
if (!result) { CBX_ERR("notify_lock.target_highest_slotid"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t op_cb_notify_lock_res(XDR *xdr, struct cb_notify_lock_res *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_enum(xdr, &res->status);
|
||||
if (!result) { CBX_ERR("notify_lock.status"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/* OP_CB_NOTIFY_DEVICEID */
|
||||
static bool_t cb_notify_deviceid_change(XDR *xdr, struct notify_deviceid4 *change)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_u_int32_t(xdr, (uint32_t*)&change->layouttype);
|
||||
if (!result) { CBX_ERR("notify_deviceid.change.layouttype"); goto out; }
|
||||
|
||||
result = xdr_opaque(xdr, (char*)change->deviceid, PNFS_DEVICEID_SIZE);
|
||||
if (!result) { CBX_ERR("notify_deviceid.change.deviceid"); goto out; }
|
||||
|
||||
result = xdr_bool(xdr, &change->immediate);
|
||||
if (!result) { CBX_ERR("notify_deviceid.change.immediate"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t cb_notify_deviceid_delete(XDR *xdr, struct notify_deviceid4 *change)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_u_int32_t(xdr, (uint32_t*)&change->layouttype);
|
||||
if (!result) { CBX_ERR("notify_deviceid.delete.layouttype"); goto out; }
|
||||
|
||||
result = xdr_opaque(xdr, (char*)change->deviceid, PNFS_DEVICEID_SIZE);
|
||||
if (!result) { CBX_ERR("notify_deviceid.delete.deviceid"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t op_cb_notify_deviceid_args(XDR *xdr, struct cb_notify_deviceid_args *args)
|
||||
{
|
||||
XDR notify_xdr;
|
||||
uint32_t i, j, c;
|
||||
bool_t result;
|
||||
|
||||
/* decode the generic notify4 list */
|
||||
result = xdr_array(xdr, (char**)&args->notify_list,
|
||||
&args->notify_count, CB_COMPOUND_MAX_OPERATIONS,
|
||||
sizeof(struct notify4), (xdrproc_t)common_notify4);
|
||||
if (!result) { CBX_ERR("notify_deviceid.notify_list"); goto out; }
|
||||
|
||||
switch (xdr->x_op) {
|
||||
case XDR_FREE:
|
||||
free(args->change_list);
|
||||
case XDR_ENCODE:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* count the number of device changes */
|
||||
args->change_count = 0;
|
||||
for (i = 0; i < args->notify_count; i++)
|
||||
args->change_count += args->notify_list[i].mask.count;
|
||||
|
||||
args->change_list = calloc(args->change_count, sizeof(struct notify_deviceid4));
|
||||
if (args->change_list == NULL)
|
||||
return FALSE;
|
||||
|
||||
c = 0;
|
||||
for (i = 0; i < args->notify_count; i++) {
|
||||
struct notify4 *notify = &args->notify_list[i];
|
||||
|
||||
/* decode the device notifications out of the opaque buffer */
|
||||
xdrmem_create(¬ify_xdr, notify->list, notify->len, XDR_DECODE);
|
||||
|
||||
for (j = 0; j < notify->mask.count; j++) {
|
||||
struct notify_deviceid4 *change = &args->change_list[c++];
|
||||
change->type = notify->mask.arr[j];
|
||||
|
||||
switch (change->type) {
|
||||
case NOTIFY_DEVICEID4_CHANGE:
|
||||
result = cb_notify_deviceid_change(¬ify_xdr, change);
|
||||
if (!result) { CBX_ERR("notify_deviceid.change"); goto out; }
|
||||
break;
|
||||
case NOTIFY_DEVICEID4_DELETE:
|
||||
result = cb_notify_deviceid_delete(¬ify_xdr, change);
|
||||
if (!result) { CBX_ERR("notify_deviceid.delete"); goto out; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool_t op_cb_notify_deviceid_res(XDR *xdr, struct cb_notify_deviceid_res *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_enum(xdr, &res->status);
|
||||
if (!result) { CBX_ERR("notify_deviceid.status"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/* CB_COMPOUND */
|
||||
static bool_t cb_compound_tag(XDR *xdr, struct cb_compound_tag *args)
|
||||
{
|
||||
return xdr_u_int32_t(xdr, &args->len)
|
||||
&& args->len <= CB_COMPOUND_MAX_TAG
|
||||
&& xdr_opaque(xdr, args->str, args->len);
|
||||
}
|
||||
|
||||
static const struct xdr_discrim cb_argop_discrim[] = {
|
||||
{ OP_CB_LAYOUTRECALL, (xdrproc_t)op_cb_layoutrecall_args },
|
||||
{ OP_CB_RECALL_SLOT, (xdrproc_t)op_cb_recall_slot_args },
|
||||
{ OP_CB_SEQUENCE, (xdrproc_t)op_cb_sequence_args },
|
||||
{ OP_CB_GETATTR, (xdrproc_t)op_cb_getattr_args },
|
||||
{ OP_CB_RECALL, (xdrproc_t)op_cb_recall_args },
|
||||
{ OP_CB_NOTIFY, (xdrproc_t)op_cb_notify_args },
|
||||
{ OP_CB_PUSH_DELEG, (xdrproc_t)op_cb_push_deleg_args },
|
||||
{ OP_CB_RECALL_ANY, (xdrproc_t)op_cb_recall_any_args },
|
||||
{ OP_CB_RECALLABLE_OBJ_AVAIL, (xdrproc_t)op_cb_recallable_obj_avail_args },
|
||||
{ OP_CB_WANTS_CANCELLED, (xdrproc_t)op_cb_wants_cancelled_args },
|
||||
{ OP_CB_NOTIFY_LOCK, (xdrproc_t)op_cb_notify_lock_args },
|
||||
{ OP_CB_NOTIFY_DEVICEID, (xdrproc_t)op_cb_notify_deviceid_args },
|
||||
{ OP_CB_ILLEGAL, NULL_xdrproc_t },
|
||||
};
|
||||
|
||||
static bool_t cb_compound_argop(XDR *xdr, struct cb_argop *args)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = xdr_union(xdr, &args->opnum, (char*)&args->args,
|
||||
cb_argop_discrim, NULL_xdrproc_t);
|
||||
if (!result) { CBX_ERR("cmb:argop.args"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
bool_t proc_cb_compound_args(XDR *xdr, struct cb_compound_args *args)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
result = cb_compound_tag(xdr, &args->tag);
|
||||
if (!result) { CBX_ERR("compound.tag"); goto out; }
|
||||
|
||||
result = xdr_u_int32_t(xdr, &args->minorversion);
|
||||
if (!result) { CBX_ERR("compound.minorversion"); goto out; }
|
||||
|
||||
/* "superfluous in NFSv4.1 and MUST be ignored by the client" */
|
||||
result = xdr_u_int32_t(xdr, &args->callback_ident);
|
||||
if (!result) { CBX_ERR("compound.callback_ident"); goto out; }
|
||||
|
||||
result = xdr_array(xdr, (char**)&args->argarray,
|
||||
&args->argarray_count, CB_COMPOUND_MAX_OPERATIONS,
|
||||
sizeof(struct cb_argop), (xdrproc_t)cb_compound_argop);
|
||||
if (!result) { CBX_ERR("compound.argarray"); goto out; }
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static const struct xdr_discrim cb_resop_discrim[] = {
|
||||
{ OP_CB_LAYOUTRECALL, (xdrproc_t)op_cb_layoutrecall_res },
|
||||
{ OP_CB_RECALL_SLOT, (xdrproc_t)op_cb_recall_slot_res },
|
||||
{ OP_CB_SEQUENCE, (xdrproc_t)op_cb_sequence_res },
|
||||
{ OP_CB_GETATTR, (xdrproc_t)op_cb_getattr_res },
|
||||
{ OP_CB_RECALL, (xdrproc_t)op_cb_recall_res },
|
||||
{ OP_CB_NOTIFY, (xdrproc_t)op_cb_notify_res },
|
||||
{ OP_CB_PUSH_DELEG, (xdrproc_t)op_cb_push_deleg_res },
|
||||
{ OP_CB_RECALL_ANY, (xdrproc_t)op_cb_recall_any_res },
|
||||
{ OP_CB_RECALLABLE_OBJ_AVAIL, (xdrproc_t)op_cb_recallable_obj_avail_res },
|
||||
{ OP_CB_WANTS_CANCELLED, (xdrproc_t)op_cb_wants_cancelled_res },
|
||||
{ OP_CB_NOTIFY_LOCK, (xdrproc_t)op_cb_notify_lock_res },
|
||||
{ OP_CB_NOTIFY_DEVICEID, (xdrproc_t)op_cb_notify_deviceid_res },
|
||||
{ OP_CB_ILLEGAL, NULL_xdrproc_t },
|
||||
};
|
||||
|
||||
static bool_t cb_compound_resop(XDR *xdr, struct cb_resop *res)
|
||||
{
|
||||
/* save xdr encode/decode status to see which operation failed */
|
||||
res->xdr_ok = xdr_union(xdr, &res->opnum, (char*)&res->res,
|
||||
cb_resop_discrim, NULL_xdrproc_t);
|
||||
if (!res->xdr_ok) { CBX_ERR("resop.res"); goto out; }
|
||||
out:
|
||||
return res->xdr_ok;
|
||||
}
|
||||
|
||||
bool_t proc_cb_compound_res(XDR *xdr, struct cb_compound_res *res)
|
||||
{
|
||||
bool_t result;
|
||||
|
||||
if (res == NULL)
|
||||
return TRUE;
|
||||
|
||||
result = xdr_enum(xdr, &res->status);
|
||||
if (!result) { CBX_ERR("compound_res.status"); goto out; }
|
||||
|
||||
result = cb_compound_tag(xdr, &res->tag);
|
||||
if (!result) { CBX_ERR("compound_res.tag"); goto out; }
|
||||
|
||||
result = xdr_array(xdr, (char**)&res->resarray,
|
||||
&res->resarray_count, CB_COMPOUND_MAX_OPERATIONS,
|
||||
sizeof(struct cb_resop), (xdrproc_t)cb_compound_resop);
|
||||
if (!result) { CBX_ERR("compound_res.resarray"); goto out; }
|
||||
out:
|
||||
if (xdr->x_op == XDR_FREE)
|
||||
free(res);
|
||||
return result;
|
||||
}
|
678
reactos/base/services/nfsd/daemon_debug.c
Normal file
678
reactos/base/services/nfsd/daemon_debug.c
Normal file
|
@ -0,0 +1,678 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "daemon_debug.h"
|
||||
#include "from_kernel.h"
|
||||
#include "nfs41_driver.h"
|
||||
#include "nfs41_ops.h"
|
||||
#include "service.h"
|
||||
#include "rpc/rpc.h"
|
||||
#include "rpc/auth_sspi.h"
|
||||
|
||||
static int g_debug_level = DEFAULT_DEBUG_LEVEL;
|
||||
|
||||
void set_debug_level(int level) { g_debug_level = level; }
|
||||
|
||||
FILE *dlog_file, *elog_file;
|
||||
|
||||
#ifndef STANDALONE_NFSD
|
||||
void open_log_files()
|
||||
{
|
||||
const char dfile[] = "nfsddbg.log";
|
||||
const char efile[] = "nfsderr.log";
|
||||
const char mode[] = "w";
|
||||
if (g_debug_level > 0) {
|
||||
dlog_file = fopen(dfile, mode);
|
||||
if (dlog_file == NULL) {
|
||||
ReportStatusToSCMgr(SERVICE_STOPPED, GetLastError(), 0);
|
||||
exit (GetLastError());
|
||||
}
|
||||
}
|
||||
elog_file = fopen(efile, mode);
|
||||
if (elog_file == NULL) {
|
||||
ReportStatusToSCMgr(SERVICE_STOPPED, GetLastError(), 0);
|
||||
exit (GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
void close_log_files()
|
||||
{
|
||||
if (dlog_file) fclose(dlog_file);
|
||||
if (elog_file) fclose(elog_file);
|
||||
}
|
||||
#else
|
||||
void open_log_files()
|
||||
{
|
||||
dlog_file = stdout;
|
||||
elog_file = stderr;
|
||||
}
|
||||
#endif
|
||||
|
||||
void dprintf(int level, LPCSTR format, ...)
|
||||
{
|
||||
if (level <= g_debug_level) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
fprintf(dlog_file, "%04x: ", GetCurrentThreadId());
|
||||
vfprintf(dlog_file, format, args);
|
||||
#ifndef STANDALONE_NFSD
|
||||
fflush(dlog_file);
|
||||
#endif
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
void eprintf(LPCSTR format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
fprintf(elog_file, "%04x: ", GetCurrentThreadId());
|
||||
vfprintf(elog_file, format, args);
|
||||
#ifndef STANDALONE_NFSD
|
||||
fflush(elog_file);
|
||||
#endif
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void print_hexbuf(int level, unsigned char *title, unsigned char *buf, int len)
|
||||
{
|
||||
int j, k;
|
||||
if (level > g_debug_level) return;
|
||||
fprintf(dlog_file, "%s", title);
|
||||
for(j = 0, k = 0; j < len; j++, k++) {
|
||||
fprintf(dlog_file, "%02x '%c' ", buf[j], isascii(buf[j])? buf[j]:' ');
|
||||
if (((k+1) % 10 == 0 && k > 0)) {
|
||||
fprintf(dlog_file, "\n");
|
||||
}
|
||||
}
|
||||
fprintf(dlog_file, "\n");
|
||||
}
|
||||
|
||||
void print_hexbuf_no_asci(int level, unsigned char *title, unsigned char *buf, int len)
|
||||
{
|
||||
int j, k;
|
||||
if (level > g_debug_level) return;
|
||||
fprintf(dlog_file, "%s", title);
|
||||
for(j = 0, k = 0; j < len; j++, k++) {
|
||||
fprintf(dlog_file, "%02x ", buf[j]);
|
||||
if (((k+1) % 10 == 0 && k > 0)) {
|
||||
fprintf(dlog_file, "\n");
|
||||
}
|
||||
}
|
||||
fprintf(dlog_file, "\n");
|
||||
}
|
||||
|
||||
void print_create_attributes(int level, DWORD create_opts) {
|
||||
if (level > g_debug_level) return;
|
||||
fprintf(dlog_file, "create attributes: ");
|
||||
if (create_opts & FILE_DIRECTORY_FILE)
|
||||
fprintf(dlog_file, "DIRECTORY_FILE ");
|
||||
if (create_opts & FILE_NON_DIRECTORY_FILE)
|
||||
fprintf(dlog_file, "NON_DIRECTORY_FILE ");
|
||||
if (create_opts & FILE_WRITE_THROUGH)
|
||||
fprintf(dlog_file, "WRITE_THROUGH ");
|
||||
if (create_opts & FILE_SEQUENTIAL_ONLY)
|
||||
fprintf(dlog_file, "SEQUENTIAL_ONLY ");
|
||||
if (create_opts & FILE_RANDOM_ACCESS)
|
||||
fprintf(dlog_file, "RANDOM_ACCESS ");
|
||||
if (create_opts & FILE_NO_INTERMEDIATE_BUFFERING)
|
||||
fprintf(dlog_file, "NO_INTERMEDIATE_BUFFERING ");
|
||||
if (create_opts & FILE_SYNCHRONOUS_IO_ALERT)
|
||||
fprintf(dlog_file, "SYNCHRONOUS_IO_ALERT ");
|
||||
if (create_opts & FILE_SYNCHRONOUS_IO_NONALERT)
|
||||
fprintf(dlog_file, "SYNCHRONOUS_IO_NONALERT ");
|
||||
if (create_opts & FILE_CREATE_TREE_CONNECTION)
|
||||
fprintf(dlog_file, "CREATE_TREE_CONNECTION ");
|
||||
if (create_opts & FILE_COMPLETE_IF_OPLOCKED)
|
||||
fprintf(dlog_file, "COMPLETE_IF_OPLOCKED ");
|
||||
if (create_opts & FILE_NO_EA_KNOWLEDGE)
|
||||
fprintf(dlog_file, "NO_EA_KNOWLEDGE ");
|
||||
if (create_opts & FILE_OPEN_REPARSE_POINT)
|
||||
fprintf(dlog_file, "OPEN_REPARSE_POINT ");
|
||||
if (create_opts & FILE_DELETE_ON_CLOSE)
|
||||
fprintf(dlog_file, "DELETE_ON_CLOSE ");
|
||||
if (create_opts & FILE_OPEN_BY_FILE_ID)
|
||||
fprintf(dlog_file, "OPEN_BY_FILE_ID ");
|
||||
if (create_opts & FILE_OPEN_FOR_BACKUP_INTENT)
|
||||
fprintf(dlog_file, "OPEN_FOR_BACKUP_INTENT ");
|
||||
if (create_opts & FILE_RESERVE_OPFILTER)
|
||||
fprintf(dlog_file, "RESERVE_OPFILTER");
|
||||
fprintf(dlog_file, "\n");
|
||||
}
|
||||
|
||||
void print_disposition(int level, DWORD disposition) {
|
||||
if (level > g_debug_level) return;
|
||||
fprintf(dlog_file, "userland disposition = ");
|
||||
if (disposition == FILE_SUPERSEDE)
|
||||
fprintf(dlog_file, "FILE_SUPERSEDE\n");
|
||||
else if (disposition == FILE_CREATE)
|
||||
fprintf(dlog_file, "FILE_CREATE\n");
|
||||
else if (disposition == FILE_OPEN)
|
||||
fprintf(dlog_file, "FILE_OPEN\n");
|
||||
else if (disposition == FILE_OPEN_IF)
|
||||
fprintf(dlog_file, "FILE_OPEN_IF\n");
|
||||
else if (disposition == FILE_OVERWRITE)
|
||||
fprintf(dlog_file, "FILE_OVERWRITE\n");
|
||||
else if (disposition == FILE_OVERWRITE_IF)
|
||||
fprintf(dlog_file, "FILE_OVERWRITE_IF\n");
|
||||
}
|
||||
|
||||
void print_access_mask(int level, DWORD access_mask) {
|
||||
if (level > g_debug_level) return;
|
||||
fprintf(dlog_file, "access mask: ");
|
||||
if (access_mask & FILE_READ_DATA)
|
||||
fprintf(dlog_file, "READ ");
|
||||
if (access_mask & STANDARD_RIGHTS_READ)
|
||||
fprintf(dlog_file, "READ_ACL ");
|
||||
if (access_mask & FILE_READ_ATTRIBUTES)
|
||||
fprintf(dlog_file, "READ_ATTR ");
|
||||
if (access_mask & FILE_READ_EA)
|
||||
fprintf(dlog_file, "READ_EA ");
|
||||
if (access_mask & FILE_WRITE_DATA)
|
||||
fprintf(dlog_file, "WRITE ");
|
||||
if (access_mask & STANDARD_RIGHTS_WRITE)
|
||||
fprintf(dlog_file, "WRITE_ACL ");
|
||||
if (access_mask & FILE_WRITE_ATTRIBUTES)
|
||||
fprintf(dlog_file, "WRITE_ATTR ");
|
||||
if (access_mask & FILE_WRITE_EA)
|
||||
fprintf(dlog_file, "WRITE_EA ");
|
||||
if (access_mask & FILE_APPEND_DATA)
|
||||
fprintf(dlog_file, "APPEND ");
|
||||
if (access_mask & FILE_EXECUTE)
|
||||
fprintf(dlog_file, "EXECUTE ");
|
||||
if (access_mask & FILE_LIST_DIRECTORY)
|
||||
fprintf(dlog_file, "LIST ");
|
||||
if (access_mask & FILE_TRAVERSE)
|
||||
fprintf(dlog_file, "TRAVERSE ");
|
||||
if (access_mask & SYNCHRONIZE)
|
||||
fprintf(dlog_file, "SYNC ");
|
||||
if (access_mask & FILE_DELETE_CHILD)
|
||||
fprintf(dlog_file, "DELETE_CHILD");
|
||||
fprintf(dlog_file, "\n");
|
||||
}
|
||||
|
||||
void print_share_mode(int level, DWORD mode)
|
||||
{
|
||||
if (level > g_debug_level) return;
|
||||
fprintf(dlog_file, "share mode: ");
|
||||
if (mode & FILE_SHARE_READ)
|
||||
fprintf(dlog_file, "READ ");
|
||||
if (mode & FILE_SHARE_WRITE)
|
||||
fprintf(dlog_file, "WRITE ");
|
||||
if (mode & FILE_SHARE_DELETE)
|
||||
fprintf(dlog_file, "DELETE");
|
||||
fprintf(dlog_file, "\n");
|
||||
}
|
||||
|
||||
void print_file_id_both_dir_info(int level, FILE_ID_BOTH_DIR_INFO *pboth_dir_info)
|
||||
{
|
||||
if (level > g_debug_level) return;
|
||||
fprintf(dlog_file, "FILE_ID_BOTH_DIR_INFO %p %d\n",
|
||||
pboth_dir_info, sizeof(unsigned char *));
|
||||
fprintf(dlog_file, "\tNextEntryOffset=%ld %d %d\n",
|
||||
pboth_dir_info->NextEntryOffset,
|
||||
sizeof(pboth_dir_info->NextEntryOffset), sizeof(DWORD));
|
||||
fprintf(dlog_file, "\tFileIndex=%ld %d\n", pboth_dir_info->FileIndex,
|
||||
sizeof(pboth_dir_info->FileIndex));
|
||||
fprintf(dlog_file, "\tCreationTime=0x%x %d\n",
|
||||
pboth_dir_info->CreationTime.QuadPart,
|
||||
sizeof(pboth_dir_info->CreationTime));
|
||||
fprintf(dlog_file, "\tLastAccessTime=0x%x %d\n",
|
||||
pboth_dir_info->LastAccessTime.QuadPart,
|
||||
sizeof(pboth_dir_info->LastAccessTime));
|
||||
fprintf(dlog_file, "\tLastWriteTime=0x%x %d\n",
|
||||
pboth_dir_info->LastWriteTime.QuadPart,
|
||||
sizeof(pboth_dir_info->LastWriteTime));
|
||||
fprintf(dlog_file, "\tChangeTime=0x%x %d\n",
|
||||
pboth_dir_info->ChangeTime.QuadPart,
|
||||
sizeof(pboth_dir_info->ChangeTime));
|
||||
fprintf(dlog_file, "\tEndOfFile=0x%x %d\n",
|
||||
pboth_dir_info->EndOfFile.QuadPart,
|
||||
sizeof(pboth_dir_info->EndOfFile));
|
||||
fprintf(dlog_file, "\tAllocationSize=0x%x %d\n",
|
||||
pboth_dir_info->AllocationSize.QuadPart,
|
||||
sizeof(pboth_dir_info->AllocationSize));
|
||||
fprintf(dlog_file, "\tFileAttributes=%ld %d\n",
|
||||
pboth_dir_info->FileAttributes,
|
||||
sizeof(pboth_dir_info->FileAttributes));
|
||||
fprintf(dlog_file, "\tFileNameLength=%ld %d\n",
|
||||
pboth_dir_info->FileNameLength,
|
||||
sizeof(pboth_dir_info->FileNameLength));
|
||||
fprintf(dlog_file, "\tEaSize=%ld %d\n",
|
||||
pboth_dir_info->EaSize, sizeof(pboth_dir_info->EaSize));
|
||||
fprintf(dlog_file, "\tShortNameLength=%d %d\n",
|
||||
pboth_dir_info->ShortNameLength,
|
||||
sizeof(pboth_dir_info->ShortNameLength));
|
||||
fprintf(dlog_file, "\tShortName='%S' %d\n", pboth_dir_info->ShortName,
|
||||
sizeof(pboth_dir_info->ShortName));
|
||||
fprintf(dlog_file, "\tFileId=0x%x %d\n", pboth_dir_info->FileId.QuadPart,
|
||||
sizeof(pboth_dir_info->FileId));
|
||||
fprintf(dlog_file, "\tFileName='%S' %p\n", pboth_dir_info->FileName,
|
||||
pboth_dir_info->FileName);
|
||||
}
|
||||
|
||||
void print_opcode(int level, DWORD opcode)
|
||||
{
|
||||
dprintf(level, (LPCSTR)opcode2string(opcode));
|
||||
}
|
||||
|
||||
const char* opcode2string(DWORD opcode)
|
||||
{
|
||||
switch(opcode) {
|
||||
case NFS41_SHUTDOWN: return "NFS41_SHUTDOWN";
|
||||
case NFS41_MOUNT: return "NFS41_MOUNT";
|
||||
case NFS41_UNMOUNT: return "NFS41_UNMOUNT";
|
||||
case NFS41_OPEN: return "NFS41_OPEN";
|
||||
case NFS41_CLOSE: return "NFS41_CLOSE";
|
||||
case NFS41_READ: return "NFS41_READ";
|
||||
case NFS41_WRITE: return "NFS41_WRITE";
|
||||
case NFS41_LOCK: return "NFS41_LOCK";
|
||||
case NFS41_UNLOCK: return "NFS41_UNLOCK";
|
||||
case NFS41_DIR_QUERY: return "NFS41_DIR_QUERY";
|
||||
case NFS41_FILE_QUERY: return "NFS41_FILE_QUERY";
|
||||
case NFS41_FILE_SET: return "NFS41_FILE_SET";
|
||||
case NFS41_EA_SET: return "NFS41_EA_SET";
|
||||
case NFS41_EA_GET: return "NFS41_EA_GET";
|
||||
case NFS41_SYMLINK: return "NFS41_SYMLINK";
|
||||
case NFS41_VOLUME_QUERY: return "NFS41_VOLUME_QUERY";
|
||||
case NFS41_ACL_QUERY: return "NFS41_ACL_QUERY";
|
||||
case NFS41_ACL_SET: return "NFS41_ACL_SET";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char* nfs_opnum_to_string(int opnum)
|
||||
{
|
||||
switch (opnum)
|
||||
{
|
||||
case OP_ACCESS: return "ACCESS";
|
||||
case OP_CLOSE: return "CLOSE";
|
||||
case OP_COMMIT: return "COMMIT";
|
||||
case OP_CREATE: return "CREATE";
|
||||
case OP_DELEGPURGE: return "DELEGPURGE";
|
||||
case OP_DELEGRETURN: return "DELEGRETURN";
|
||||
case OP_GETATTR: return "GETATTR";
|
||||
case OP_GETFH: return "GETFH";
|
||||
case OP_LINK: return "LINK";
|
||||
case OP_LOCK: return "LOCK";
|
||||
case OP_LOCKT: return "LOCKT";
|
||||
case OP_LOCKU: return "LOCKU";
|
||||
case OP_LOOKUP: return "LOOKUP";
|
||||
case OP_LOOKUPP: return "LOOKUPP";
|
||||
case OP_NVERIFY: return "NVERIFY";
|
||||
case OP_OPEN: return "OPEN";
|
||||
case OP_OPENATTR: return "OPENATTR";
|
||||
case OP_OPEN_CONFIRM: return "OPEN_CONFIRM";
|
||||
case OP_OPEN_DOWNGRADE: return "OPEN_DOWNGRADE";
|
||||
case OP_PUTFH: return "PUTFH";
|
||||
case OP_PUTPUBFH: return "PUTPUBFH";
|
||||
case OP_PUTROOTFH: return "PUTROOTFH";
|
||||
case OP_READ: return "READ";
|
||||
case OP_READDIR: return "READDIR";
|
||||
case OP_READLINK: return "READLINK";
|
||||
case OP_REMOVE: return "REMOVE";
|
||||
case OP_RENAME: return "RENAME";
|
||||
case OP_RENEW: return "RENEW";
|
||||
case OP_RESTOREFH: return "RESTOREFH";
|
||||
case OP_SAVEFH: return "SAVEFH";
|
||||
case OP_SECINFO: return "SECINFO";
|
||||
case OP_SETATTR: return "SETATTR";
|
||||
case OP_SETCLIENTID: return "SETCLIENTID";
|
||||
case OP_SETCLIENTID_CONFIRM: return "SETCLIENTID_CONFIRM";
|
||||
case OP_VERIFY: return "VERIFY";
|
||||
case OP_WRITE: return "WRITE";
|
||||
case OP_RELEASE_LOCKOWNER: return "RELEASE_LOCKOWNER";
|
||||
case OP_BACKCHANNEL_CTL: return "BACKCHANNEL_CTL";
|
||||
case OP_BIND_CONN_TO_SESSION: return "BIND_CONN_TO_SESSION";
|
||||
case OP_EXCHANGE_ID: return "EXCHANGE_ID";
|
||||
case OP_CREATE_SESSION: return "CREATE_SESSION";
|
||||
case OP_DESTROY_SESSION: return "DESTROY_SESSION";
|
||||
case OP_FREE_STATEID: return "FREE_STATEID";
|
||||
case OP_GET_DIR_DELEGATION: return "GET_DIR_DELEGATION";
|
||||
case OP_GETDEVICEINFO: return "GETDEVICEINFO";
|
||||
case OP_GETDEVICELIST: return "GETDEVICELIST";
|
||||
case OP_LAYOUTCOMMIT: return "LAYOUTCOMMIT";
|
||||
case OP_LAYOUTGET: return "LAYOUTGET";
|
||||
case OP_LAYOUTRETURN: return "LAYOUTRETURN";
|
||||
case OP_SECINFO_NO_NAME: return "SECINFO_NO_NAME";
|
||||
case OP_SEQUENCE: return "SEQUENCE";
|
||||
case OP_SET_SSV: return "SET_SSV";
|
||||
case OP_TEST_STATEID: return "TEST_STATEID";
|
||||
case OP_WANT_DELEGATION: return "WANT_DELEGATION";
|
||||
case OP_DESTROY_CLIENTID: return "DESTROY_CLIENTID";
|
||||
case OP_RECLAIM_COMPLETE: return "RECLAIM_COMPLETE";
|
||||
case OP_ILLEGAL: return "ILLEGAL";
|
||||
default: return "invalid nfs opnum";
|
||||
}
|
||||
}
|
||||
|
||||
const char* nfs_error_string(int status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case NFS4_OK: return "NFS4_OK";
|
||||
case NFS4ERR_PERM: return "NFS4ERR_PERM";
|
||||
case NFS4ERR_NOENT: return "NFS4ERR_NOENT";
|
||||
case NFS4ERR_IO: return "NFS4ERR_IO";
|
||||
case NFS4ERR_NXIO: return "NFS4ERR_NXIO";
|
||||
case NFS4ERR_ACCESS: return "NFS4ERR_ACCESS";
|
||||
case NFS4ERR_EXIST: return "NFS4ERR_EXIST";
|
||||
case NFS4ERR_XDEV: return "NFS4ERR_XDEV";
|
||||
case NFS4ERR_NOTDIR: return "NFS4ERR_NOTDIR";
|
||||
case NFS4ERR_ISDIR: return "NFS4ERR_ISDIR";
|
||||
case NFS4ERR_INVAL: return "NFS4ERR_INVAL";
|
||||
case NFS4ERR_FBIG: return "NFS4ERR_FBIG";
|
||||
case NFS4ERR_NOSPC: return "NFS4ERR_NOSPC";
|
||||
case NFS4ERR_ROFS: return "NFS4ERR_ROFS";
|
||||
case NFS4ERR_MLINK: return "NFS4ERR_MLINK";
|
||||
case NFS4ERR_NAMETOOLONG: return "NFS4ERR_NAMETOOLONG";
|
||||
case NFS4ERR_NOTEMPTY: return "NFS4ERR_NOTEMPTY";
|
||||
case NFS4ERR_DQUOT: return "NFS4ERR_DQUOT";
|
||||
case NFS4ERR_STALE: return "NFS4ERR_STALE";
|
||||
case NFS4ERR_BADHANDLE: return "NFS4ERR_BADHANDLE";
|
||||
case NFS4ERR_BAD_COOKIE: return "NFS4ERR_BAD_COOKIE";
|
||||
case NFS4ERR_NOTSUPP: return "NFS4ERR_NOTSUPP";
|
||||
case NFS4ERR_TOOSMALL: return "NFS4ERR_TOOSMALL";
|
||||
case NFS4ERR_SERVERFAULT: return "NFS4ERR_SERVERFAULT";
|
||||
case NFS4ERR_BADTYPE: return "NFS4ERR_BADTYPE";
|
||||
case NFS4ERR_DELAY: return "NFS4ERR_DELAY";
|
||||
case NFS4ERR_SAME: return "NFS4ERR_SAME";
|
||||
case NFS4ERR_DENIED: return "NFS4ERR_DENIED";
|
||||
case NFS4ERR_EXPIRED: return "NFS4ERR_EXPIRED";
|
||||
case NFS4ERR_LOCKED: return "NFS4ERR_LOCKED";
|
||||
case NFS4ERR_GRACE: return "NFS4ERR_GRACE";
|
||||
case NFS4ERR_FHEXPIRED: return "NFS4ERR_FHEXPIRED";
|
||||
case NFS4ERR_SHARE_DENIED: return "NFS4ERR_SHARE_DENIED";
|
||||
case NFS4ERR_WRONGSEC: return "NFS4ERR_WRONGSEC";
|
||||
case NFS4ERR_CLID_INUSE: return "NFS4ERR_CLID_INUSE";
|
||||
case NFS4ERR_RESOURCE: return "NFS4ERR_RESOURCE";
|
||||
case NFS4ERR_MOVED: return "NFS4ERR_MOVED";
|
||||
case NFS4ERR_NOFILEHANDLE: return "NFS4ERR_NOFILEHANDLE";
|
||||
case NFS4ERR_MINOR_VERS_MISMATCH: return "NFS4ERR_MINOR_VERS_MISMATCH";
|
||||
case NFS4ERR_STALE_CLIENTID: return "NFS4ERR_STALE_CLIENTID";
|
||||
case NFS4ERR_STALE_STATEID: return "NFS4ERR_STALE_STATEID";
|
||||
case NFS4ERR_OLD_STATEID: return "NFS4ERR_OLD_STATEID";
|
||||
case NFS4ERR_BAD_STATEID: return "NFS4ERR_BAD_STATEID";
|
||||
case NFS4ERR_BAD_SEQID: return "NFS4ERR_BAD_SEQID";
|
||||
case NFS4ERR_NOT_SAME: return "NFS4ERR_NOT_SAME";
|
||||
case NFS4ERR_LOCK_RANGE: return "NFS4ERR_LOCK_RANGE";
|
||||
case NFS4ERR_SYMLINK: return "NFS4ERR_SYMLINK";
|
||||
case NFS4ERR_RESTOREFH: return "NFS4ERR_RESTOREFH";
|
||||
case NFS4ERR_LEASE_MOVED: return "NFS4ERR_LEASE_MOVED";
|
||||
case NFS4ERR_ATTRNOTSUPP: return "NFS4ERR_ATTRNOTSUPP";
|
||||
case NFS4ERR_NO_GRACE: return "NFS4ERR_NO_GRACE";
|
||||
case NFS4ERR_RECLAIM_BAD: return "NFS4ERR_RECLAIM_BAD";
|
||||
case NFS4ERR_RECLAIM_CONFLICT: return "NFS4ERR_RECLAIM_CONFLICT";
|
||||
case NFS4ERR_BADXDR: return "NFS4ERR_BADXDR";
|
||||
case NFS4ERR_LOCKS_HELD: return "NFS4ERR_LOCKS_HELD";
|
||||
case NFS4ERR_OPENMODE: return "NFS4ERR_OPENMODE";
|
||||
case NFS4ERR_BADOWNER: return "NFS4ERR_BADOWNER";
|
||||
case NFS4ERR_BADCHAR: return "NFS4ERR_BADCHAR";
|
||||
case NFS4ERR_BADNAME: return "NFS4ERR_BADNAME";
|
||||
case NFS4ERR_BAD_RANGE: return "NFS4ERR_BAD_RANGE";
|
||||
case NFS4ERR_LOCK_NOTSUPP: return "NFS4ERR_LOCK_NOTSUPP";
|
||||
case NFS4ERR_OP_ILLEGAL: return "NFS4ERR_OP_ILLEGAL";
|
||||
case NFS4ERR_DEADLOCK: return "NFS4ERR_DEADLOCK";
|
||||
case NFS4ERR_FILE_OPEN: return "NFS4ERR_FILE_OPEN";
|
||||
case NFS4ERR_ADMIN_REVOKED: return "NFS4ERR_ADMIN_REVOKED";
|
||||
case NFS4ERR_CB_PATH_DOWN: return "NFS4ERR_CB_PATH_DOWN";
|
||||
case NFS4ERR_BADIOMODE: return "NFS4ERR_BADIOMODE";
|
||||
case NFS4ERR_BADLAYOUT: return "NFS4ERR_BADLAYOUT";
|
||||
case NFS4ERR_BAD_SESSION_DIGEST: return "NFS4ERR_BAD_SESSION_DIGEST";
|
||||
case NFS4ERR_BADSESSION: return "NFS4ERR_BADSESSION";
|
||||
case NFS4ERR_BADSLOT: return "NFS4ERR_BADSLOT";
|
||||
case NFS4ERR_COMPLETE_ALREADY: return "NFS4ERR_COMPLETE_ALREADY";
|
||||
case NFS4ERR_CONN_NOT_BOUND_TO_SESSION: return "NFS4ERR_CONN_NOT_BOUND_TO_SESSION";
|
||||
case NFS4ERR_DELEG_ALREADY_WANTED: return "NFS4ERR_DELEG_ALREADY_WANTED";
|
||||
case NFS4ERR_BACK_CHAN_BUSY: return "NFS4ERR_BACK_CHAN_BUSY";
|
||||
case NFS4ERR_LAYOUTTRYLATER: return "NFS4ERR_LAYOUTTRYLATER";
|
||||
case NFS4ERR_LAYOUTUNAVAILABLE: return "NFS4ERR_LAYOUTUNAVAILABLE";
|
||||
case NFS4ERR_NOMATCHING_LAYOUT: return "NFS4ERR_NOMATCHING_LAYOUT";
|
||||
case NFS4ERR_RECALLCONFLICT: return "NFS4ERR_RECALLCONFLICT";
|
||||
case NFS4ERR_UNKNOWN_LAYOUTTYPE: return "NFS4ERR_UNKNOWN_LAYOUTTYPE";
|
||||
case NFS4ERR_SEQ_MISORDERED: return "NFS4ERR_SEQ_MISORDERED";
|
||||
case NFS4ERR_SEQUENCE_POS: return "NFS4ERR_SEQUENCE_POS";
|
||||
case NFS4ERR_REQ_TOO_BIG: return "NFS4ERR_REQ_TOO_BIG";
|
||||
case NFS4ERR_REP_TOO_BIG: return "NFS4ERR_REP_TOO_BIG";
|
||||
case NFS4ERR_REP_TOO_BIG_TO_CACHE: return "NFS4ERR_REP_TOO_BIG_TO_CACHE";
|
||||
case NFS4ERR_RETRY_UNCACHED_REP: return "NFS4ERR_RETRY_UNCACHED_REP";
|
||||
case NFS4ERR_UNSAFE_COMPOUND: return "NFS4ERR_UNSAFE_COMPOUND";
|
||||
case NFS4ERR_TOO_MANY_OPS: return "NFS4ERR_TOO_MANY_OPS";
|
||||
case NFS4ERR_OP_NOT_IN_SESSION: return "NFS4ERR_OP_NOT_IN_SESSION";
|
||||
case NFS4ERR_HASH_ALG_UNSUPP: return "NFS4ERR_HASH_ALG_UNSUPP";
|
||||
case NFS4ERR_CLIENTID_BUSY: return "NFS4ERR_CLIENTID_BUSY";
|
||||
case NFS4ERR_PNFS_IO_HOLE: return "NFS4ERR_PNFS_IO_HOLE";
|
||||
case NFS4ERR_SEQ_FALSE_RETRY: return "NFS4ERR_SEQ_FALSE_RETRY";
|
||||
case NFS4ERR_BAD_HIGH_SLOT: return "NFS4ERR_BAD_HIGH_SLOT";
|
||||
case NFS4ERR_DEADSESSION: return "NFS4ERR_DEADSESSION";
|
||||
case NFS4ERR_ENCR_ALG_UNSUPP: return "NFS4ERR_ENCR_ALG_UNSUPP";
|
||||
case NFS4ERR_PNFS_NO_LAYOUT: return "NFS4ERR_PNFS_NO_LAYOUT";
|
||||
case NFS4ERR_NOT_ONLY_OP: return "NFS4ERR_NOT_ONLY_OP";
|
||||
case NFS4ERR_WRONG_CRED: return "NFS4ERR_WRONG_CRED";
|
||||
case NFS4ERR_WRONG_TYPE: return "NFS4ERR_WRONG_TYPE";
|
||||
case NFS4ERR_DIRDELEG_UNAVAIL: return "NFS4ERR_DIRDELEG_UNAVAIL";
|
||||
case NFS4ERR_REJECT_DELEG: return "NFS4ERR_REJECT_DELEG";
|
||||
case NFS4ERR_RETURNCONFLICT: return "NFS4ERR_RETURNCONFLICT";
|
||||
case NFS4ERR_DELEG_REVOKED: return "NFS4ERR_DELEG_REVOKED";
|
||||
default: return "invalid nfs error code";
|
||||
}
|
||||
}
|
||||
|
||||
const char* rpc_error_string(int status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case RPC_CANTENCODEARGS: return "RPC_CANTENCODEARGS";
|
||||
case RPC_CANTDECODERES: return "RPC_CANTDECODERES";
|
||||
case RPC_CANTSEND: return "RPC_CANTSEND";
|
||||
case RPC_CANTRECV: return "RPC_CANTRECV";
|
||||
case RPC_TIMEDOUT: return "RPC_TIMEDOUT";
|
||||
case RPC_INTR: return "RPC_INTR";
|
||||
case RPC_UDERROR: return "RPC_UDERROR";
|
||||
case RPC_VERSMISMATCH: return "RPC_VERSMISMATCH";
|
||||
case RPC_AUTHERROR: return "RPC_AUTHERROR";
|
||||
case RPC_PROGUNAVAIL: return "RPC_PROGUNAVAIL";
|
||||
case RPC_PROGVERSMISMATCH: return "RPC_PROGVERSMISMATCH";
|
||||
case RPC_PROCUNAVAIL: return "RPC_PROCUNAVAIL";
|
||||
case RPC_CANTDECODEARGS: return "RPC_CANTDECODEARGS";
|
||||
case RPC_SYSTEMERROR: return "RPC_SYSTEMERROR";
|
||||
default: return "invalid rpc error code";
|
||||
}
|
||||
}
|
||||
|
||||
const char* gssauth_string(int type) {
|
||||
switch(type) {
|
||||
case RPCSEC_SSPI_SVC_NONE: return "RPCSEC_SSPI_SVC_NONE";
|
||||
case RPCSEC_SSPI_SVC_INTEGRITY: return "RPCSEC_SSPI_SVC_INTEGRITY";
|
||||
case RPCSEC_SSPI_SVC_PRIVACY: return "RPCSEC_SSPI_SVC_PRIVACY";
|
||||
default: return "invalid gss auth type";
|
||||
}
|
||||
}
|
||||
|
||||
void print_condwait_status(int level, int status)
|
||||
{
|
||||
if (level > g_debug_level) return;
|
||||
switch(status) {
|
||||
case WAIT_ABANDONED: fprintf(dlog_file, "WAIT_ABANDONED\n"); break;
|
||||
case WAIT_OBJECT_0: fprintf(dlog_file, "WAIT_OBJECT_0\n"); break;
|
||||
case WAIT_TIMEOUT: fprintf(dlog_file, "WAIT_TIMEOUT\n"); break;
|
||||
case WAIT_FAILED: fprintf(dlog_file, "WAIT_FAILED %d\n", GetLastError());
|
||||
default: fprintf(dlog_file, "unknown status =%d\n", status);
|
||||
}
|
||||
}
|
||||
|
||||
void print_sr_status_flags(int level, int flags)
|
||||
{
|
||||
if (level > g_debug_level) return;
|
||||
fprintf(dlog_file, "%04x: sr_status_flags: ", GetCurrentThreadId());
|
||||
if (flags & SEQ4_STATUS_CB_PATH_DOWN)
|
||||
fprintf(dlog_file, "SEQ4_STATUS_CB_PATH_DOWN ");
|
||||
if (flags & SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRING)
|
||||
fprintf(dlog_file, "SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRING ");
|
||||
if (flags & SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRED)
|
||||
fprintf(dlog_file, "SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRED ");
|
||||
if (flags & SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED)
|
||||
fprintf(dlog_file, "SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED ");
|
||||
if (flags & SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED)
|
||||
fprintf(dlog_file, "SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED ");
|
||||
if (flags & SEQ4_STATUS_ADMIN_STATE_REVOKED)
|
||||
fprintf(dlog_file, "SEQ4_STATUS_ADMIN_STATE_REVOKED ");
|
||||
if (flags & SEQ4_STATUS_RECALLABLE_STATE_REVOKED)
|
||||
fprintf(dlog_file, "SEQ4_STATUS_RECALLABLE_STATE_REVOKED ");
|
||||
if (flags & SEQ4_STATUS_LEASE_MOVED)
|
||||
fprintf(dlog_file, "SEQ4_STATUS_LEASE_MOVED ");
|
||||
if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED)
|
||||
fprintf(dlog_file, "SEQ4_STATUS_RESTART_RECLAIM_NEEDED ");
|
||||
if (flags & SEQ4_STATUS_CB_PATH_DOWN_SESSION)
|
||||
fprintf(dlog_file, "SEQ4_STATUS_CB_PATH_DOWN_SESSION ");
|
||||
if (flags & SEQ4_STATUS_BACKCHANNEL_FAULT)
|
||||
fprintf(dlog_file, "SEQ4_STATUS_BACKCHANNEL_FAULT ");
|
||||
if (flags & SEQ4_STATUS_DEVID_CHANGED)
|
||||
fprintf(dlog_file, "SEQ4_STATUS_DEVID_CHANGED ");
|
||||
if (flags & SEQ4_STATUS_DEVID_DELETED)
|
||||
fprintf(dlog_file, "SEQ4_STATUS_DEVID_DELETED ");
|
||||
fprintf(dlog_file, "\n");
|
||||
}
|
||||
|
||||
const char* secflavorop2name(DWORD sec_flavor)
|
||||
{
|
||||
switch(sec_flavor) {
|
||||
case RPCSEC_AUTH_SYS: return "AUTH_SYS";
|
||||
case RPCSEC_AUTHGSS_KRB5: return "AUTHGSS_KRB5";
|
||||
case RPCSEC_AUTHGSS_KRB5I: return "AUTHGSS_KRB5I";
|
||||
case RPCSEC_AUTHGSS_KRB5P: return "AUTHGSS_KRB5P";
|
||||
}
|
||||
|
||||
return "UNKNOWN FLAVOR";
|
||||
}
|
||||
|
||||
void print_windows_access_mask(int on, ACCESS_MASK m)
|
||||
{
|
||||
if (!on) return;
|
||||
dprintf(1, "--> print_windows_access_mask: %x\n", m);
|
||||
if (m & GENERIC_READ)
|
||||
dprintf(1, "\tGENERIC_READ\n");
|
||||
if (m & GENERIC_WRITE)
|
||||
dprintf(1, "\tGENERIC_WRITE\n");
|
||||
if (m & GENERIC_EXECUTE)
|
||||
dprintf(1, "\tGENERIC_EXECUTE\n");
|
||||
if (m & GENERIC_ALL)
|
||||
dprintf(1, "\tGENERIC_ALL\n");
|
||||
if (m & MAXIMUM_ALLOWED)
|
||||
dprintf(1, "\tMAXIMUM_ALLOWED\n");
|
||||
if (m & ACCESS_SYSTEM_SECURITY)
|
||||
dprintf(1, "\tACCESS_SYSTEM_SECURITY\n");
|
||||
if ((m & SPECIFIC_RIGHTS_ALL) == SPECIFIC_RIGHTS_ALL)
|
||||
dprintf(1, "\tSPECIFIC_RIGHTS_ALL\n");
|
||||
if ((m & STANDARD_RIGHTS_ALL) == STANDARD_RIGHTS_ALL)
|
||||
dprintf(1, "\tSTANDARD_RIGHTS_ALL\n");
|
||||
if ((m & STANDARD_RIGHTS_REQUIRED) == STANDARD_RIGHTS_REQUIRED)
|
||||
dprintf(1, "\tSTANDARD_RIGHTS_REQUIRED\n");
|
||||
if (m & SYNCHRONIZE)
|
||||
dprintf(1, "\tSYNCHRONIZE\n");
|
||||
if (m & WRITE_OWNER)
|
||||
dprintf(1, "\tWRITE_OWNER\n");
|
||||
if (m & WRITE_DAC)
|
||||
dprintf(1, "\tWRITE_DAC\n");
|
||||
if (m & READ_CONTROL)
|
||||
dprintf(1, "\tREAD_CONTROL\n");
|
||||
if (m & DELETE)
|
||||
dprintf(1, "\tDELETE\n");
|
||||
if (m & FILE_READ_DATA)
|
||||
dprintf(1, "\tFILE_READ_DATA\n");
|
||||
if (m & FILE_LIST_DIRECTORY)
|
||||
dprintf(1, "\tFILE_LIST_DIRECTORY\n");
|
||||
if (m & FILE_WRITE_DATA)
|
||||
dprintf(1, "\tFILE_WRITE_DATA\n");
|
||||
if (m & FILE_ADD_FILE)
|
||||
dprintf(1, "\tFILE_ADD_FILE\n");
|
||||
if (m & FILE_APPEND_DATA)
|
||||
dprintf(1, "\tFILE_APPEND_DATA\n");
|
||||
if (m & FILE_ADD_SUBDIRECTORY)
|
||||
dprintf(1, "\tFILE_ADD_SUBDIRECTORY\n");
|
||||
if (m & FILE_CREATE_PIPE_INSTANCE)
|
||||
dprintf(1, "\tFILE_CREATE_PIPE_INSTANCE\n");
|
||||
if (m & FILE_READ_EA)
|
||||
dprintf(1, "\tFILE_READ_EA\n");
|
||||
if (m & FILE_WRITE_EA)
|
||||
dprintf(1, "\tFILE_WRITE_EA\n");
|
||||
if (m & FILE_EXECUTE)
|
||||
dprintf(1, "\tFILE_EXECUTE\n");
|
||||
if (m & FILE_TRAVERSE)
|
||||
dprintf(1, "\tFILE_TRAVERSE\n");
|
||||
if (m & FILE_DELETE_CHILD)
|
||||
dprintf(1, "\tFILE_DELETE_CHILD\n");
|
||||
if (m & FILE_READ_ATTRIBUTES)
|
||||
dprintf(1, "\tFILE_READ_ATTRIBUTES\n");
|
||||
if (m & FILE_WRITE_ATTRIBUTES)
|
||||
dprintf(1, "\tFILE_WRITE_ATTRIBUTES\n");
|
||||
if ((m & FILE_ALL_ACCESS) == FILE_ALL_ACCESS)
|
||||
dprintf(1, "\tFILE_ALL_ACCESS\n");
|
||||
if ((m & FILE_GENERIC_READ) == FILE_GENERIC_READ)
|
||||
dprintf(1, "\tFILE_GENERIC_READ\n");
|
||||
if ((m & FILE_GENERIC_WRITE) == FILE_GENERIC_WRITE)
|
||||
dprintf(1, "\tFILE_GENERIC_WRITE\n");
|
||||
if ((m & FILE_GENERIC_EXECUTE) == FILE_GENERIC_EXECUTE)
|
||||
dprintf(1, "\tFILE_GENERIC_EXECUTE\n");
|
||||
}
|
||||
|
||||
void print_nfs_access_mask(int on, int m)
|
||||
{
|
||||
if (!on) return;
|
||||
dprintf(1, "--> print_nfs_access_mask: %x\n", m);
|
||||
if (m & ACE4_READ_DATA)
|
||||
dprintf(1, "\tACE4_READ_DATA\n");
|
||||
if (m & ACE4_LIST_DIRECTORY)
|
||||
dprintf(1, "\tACE4_LIST_DIRECTORY\n");
|
||||
if (m & ACE4_WRITE_DATA)
|
||||
dprintf(1, "\tACE4_WRITE_DATA\n");
|
||||
if (m & ACE4_ADD_FILE)
|
||||
dprintf(1, "\tACE4_ADD_FILE\n");
|
||||
if (m & ACE4_APPEND_DATA)
|
||||
dprintf(1, "\tACE4_APPEND_DATA\n");
|
||||
if (m & ACE4_ADD_SUBDIRECTORY)
|
||||
dprintf(1, "\tACE4_ADD_SUBDIRECTORY\n");
|
||||
if (m & ACE4_READ_NAMED_ATTRS)
|
||||
dprintf(1, "\tACE4_READ_NAMED_ATTRS\n");
|
||||
if (m & ACE4_WRITE_NAMED_ATTRS)
|
||||
dprintf(1, "\tACE4_WRITE_NAMED_ATTRS\n");
|
||||
if (m & ACE4_EXECUTE)
|
||||
dprintf(1, "\tACE4_EXECUTE\n");
|
||||
if (m & ACE4_DELETE_CHILD)
|
||||
dprintf(1, "\tACE4_DELETE_CHILD\n");
|
||||
if (m & ACE4_READ_ATTRIBUTES)
|
||||
dprintf(1, "\tACE4_READ_ATTRIBUTES\n");
|
||||
if (m & ACE4_WRITE_ATTRIBUTES)
|
||||
dprintf(1, "\tACE4_WRITE_ATTRIBUTES\n");
|
||||
if (m & ACE4_DELETE)
|
||||
dprintf(1, "\tACE4_DELETE\n");
|
||||
if (m & ACE4_READ_ACL)
|
||||
dprintf(1, "\tACE4_READ_ACL\n");
|
||||
if (m & ACE4_WRITE_ACL)
|
||||
dprintf(1, "\tACE4_WRITE_ACL\n");
|
||||
if (m & ACE4_WRITE_OWNER)
|
||||
dprintf(1, "\tACE4_WRITE_OWNER\n");
|
||||
if (m & ACE4_SYNCHRONIZE)
|
||||
dprintf(1, "\tACE4_SYNCHRONIZE\n");
|
||||
}
|
78
reactos/base/services/nfsd/daemon_debug.h
Normal file
78
reactos/base/services/nfsd/daemon_debug.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#ifndef _DAEMON_DEBUG_
|
||||
#define _DAEMON_DEBUG_
|
||||
|
||||
#ifdef _DEBUG
|
||||
/* use visual studio's debug heap */
|
||||
# define _CRTDBG_MAP_ALLOC
|
||||
# include <stdlib.h>
|
||||
# include <crtdbg.h>
|
||||
#else
|
||||
# include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#define DEFAULT_DEBUG_LEVEL 1
|
||||
|
||||
|
||||
/* daemon_debug.h */
|
||||
void set_debug_level(int level);
|
||||
void dprintf(int level, LPCSTR format, ...);
|
||||
void eprintf(LPCSTR format, ...);
|
||||
|
||||
void print_windows_access_mask(int on, ACCESS_MASK m);
|
||||
void print_nfs_access_mask(int on, int m);
|
||||
void print_hexbuf_no_asci(int on, unsigned char *title, unsigned char *buf, int len);
|
||||
void print_hexbuf(int level, unsigned char *title, unsigned char *buf, int len);
|
||||
void print_create_attributes(int level, DWORD create_opts);
|
||||
void print_disposition(int level, DWORD disposition);
|
||||
void print_access_mask(int level, DWORD access_mask);
|
||||
void print_share_mode(int level, DWORD mode);
|
||||
void print_file_id_both_dir_info(int level, FILE_ID_BOTH_DIR_INFO *p);
|
||||
void print_opcode(int level, DWORD opcode);
|
||||
const char* opcode2string(DWORD opcode);
|
||||
const char* nfs_opnum_to_string(int opnum);
|
||||
const char* nfs_error_string(int status);
|
||||
const char* rpc_error_string(int status);
|
||||
const char* gssauth_string(int type);
|
||||
void print_condwait_status(int level, int status);
|
||||
void print_sr_status_flags(int level, int flags);
|
||||
void open_log_files();
|
||||
void close_log_files();
|
||||
const char* secflavorop2name(DWORD sec_flavor);
|
||||
|
||||
/* pnfs_debug.c */
|
||||
enum pnfs_status;
|
||||
enum pnfs_layout_type;
|
||||
enum pnfs_iomode;
|
||||
struct __pnfs_file_layout;
|
||||
struct __pnfs_file_device;
|
||||
|
||||
const char* pnfs_error_string(enum pnfs_status status);
|
||||
const char* pnfs_layout_type_string(enum pnfs_layout_type type);
|
||||
const char* pnfs_iomode_string(enum pnfs_iomode iomode);
|
||||
|
||||
void dprint_deviceid(int level, const char *title, const unsigned char *deviceid);
|
||||
void dprint_layout(int level, const struct __pnfs_file_layout *layout);
|
||||
void dprint_device(int level, const struct __pnfs_file_device *device);
|
||||
|
||||
#endif
|
941
reactos/base/services/nfsd/delegation.c
Normal file
941
reactos/base/services/nfsd/delegation.c
Normal file
|
@ -0,0 +1,941 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include "delegation.h"
|
||||
#include "nfs41_ops.h"
|
||||
#include "name_cache.h"
|
||||
#include "util.h"
|
||||
#include "daemon_debug.h"
|
||||
|
||||
#include <devioctl.h>
|
||||
#include "nfs41_driver.h" /* for making downcall to invalidate cache */
|
||||
#include "util.h"
|
||||
|
||||
#define DGLVL 2 /* dprintf level for delegation logging */
|
||||
|
||||
|
||||
/* allocation and reference counting */
|
||||
static int delegation_create(
|
||||
IN const nfs41_path_fh *parent,
|
||||
IN const nfs41_path_fh *file,
|
||||
IN const open_delegation4 *delegation,
|
||||
OUT nfs41_delegation_state **deleg_out)
|
||||
{
|
||||
nfs41_delegation_state *state;
|
||||
int status = NO_ERROR;
|
||||
|
||||
state = calloc(1, sizeof(nfs41_delegation_state));
|
||||
if (state == NULL) {
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(&state->state, delegation, sizeof(open_delegation4));
|
||||
|
||||
abs_path_copy(&state->path, file->path);
|
||||
path_fh_init(&state->file, &state->path);
|
||||
fh_copy(&state->file.fh, &file->fh);
|
||||
path_fh_init(&state->parent, &state->path);
|
||||
last_component(state->path.path, state->file.name.name,
|
||||
&state->parent.name);
|
||||
fh_copy(&state->parent.fh, &parent->fh);
|
||||
|
||||
list_init(&state->client_entry);
|
||||
state->status = DELEGATION_GRANTED;
|
||||
InitializeSRWLock(&state->lock);
|
||||
InitializeConditionVariable(&state->cond);
|
||||
state->ref_count = 1;
|
||||
*deleg_out = state;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
void nfs41_delegation_ref(
|
||||
IN nfs41_delegation_state *state)
|
||||
{
|
||||
const LONG count = InterlockedIncrement(&state->ref_count);
|
||||
dprintf(DGLVL, "nfs41_delegation_ref(%s) count %d\n",
|
||||
state->path.path, count);
|
||||
}
|
||||
|
||||
void nfs41_delegation_deref(
|
||||
IN nfs41_delegation_state *state)
|
||||
{
|
||||
const LONG count = InterlockedDecrement(&state->ref_count);
|
||||
dprintf(DGLVL, "nfs41_delegation_deref(%s) count %d\n",
|
||||
state->path.path, count);
|
||||
if (count == 0)
|
||||
free(state);
|
||||
}
|
||||
|
||||
#define open_entry(pos) list_container(pos, nfs41_open_state, client_entry)
|
||||
|
||||
static void delegation_remove(
|
||||
IN nfs41_client *client,
|
||||
IN nfs41_delegation_state *deleg)
|
||||
{
|
||||
struct list_entry *entry;
|
||||
|
||||
/* remove from the client's list */
|
||||
EnterCriticalSection(&client->state.lock);
|
||||
list_remove(&deleg->client_entry);
|
||||
|
||||
/* remove from each associated open */
|
||||
list_for_each(entry, &client->state.opens) {
|
||||
nfs41_open_state *open = open_entry(entry);
|
||||
AcquireSRWLockExclusive(&open->lock);
|
||||
if (open->delegation.state == deleg) {
|
||||
/* drop the delegation reference */
|
||||
nfs41_delegation_deref(open->delegation.state);
|
||||
open->delegation.state = NULL;
|
||||
}
|
||||
ReleaseSRWLockExclusive(&open->lock);
|
||||
}
|
||||
LeaveCriticalSection(&client->state.lock);
|
||||
|
||||
/* signal threads waiting on delegreturn */
|
||||
AcquireSRWLockExclusive(&deleg->lock);
|
||||
deleg->status = DELEGATION_RETURNED;
|
||||
WakeAllConditionVariable(&deleg->cond);
|
||||
ReleaseSRWLockExclusive(&deleg->lock);
|
||||
|
||||
/* release the client's reference */
|
||||
nfs41_delegation_deref(deleg);
|
||||
}
|
||||
|
||||
|
||||
/* delegation return */
|
||||
#define lock_entry(pos) list_container(pos, nfs41_lock_state, open_entry)
|
||||
|
||||
static bool_t has_delegated_locks(
|
||||
IN nfs41_open_state *open)
|
||||
{
|
||||
struct list_entry *entry;
|
||||
list_for_each(entry, &open->locks.list) {
|
||||
if (lock_entry(entry)->delegated)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int open_deleg_cmp(const struct list_entry *entry, const void *value)
|
||||
{
|
||||
nfs41_open_state *open = open_entry(entry);
|
||||
int result = -1;
|
||||
|
||||
/* open must match the delegation and have state to reclaim */
|
||||
AcquireSRWLockShared(&open->lock);
|
||||
if (open->delegation.state != value) goto out;
|
||||
if (open->do_close && !has_delegated_locks(open)) goto out;
|
||||
result = 0;
|
||||
out:
|
||||
ReleaseSRWLockShared(&open->lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* find the first open that needs recovery */
|
||||
static nfs41_open_state* deleg_open_find(
|
||||
IN struct client_state *state,
|
||||
IN const nfs41_delegation_state *deleg)
|
||||
{
|
||||
struct list_entry *entry;
|
||||
nfs41_open_state *open = NULL;
|
||||
|
||||
EnterCriticalSection(&state->lock);
|
||||
entry = list_search(&state->opens, deleg, open_deleg_cmp);
|
||||
if (entry) {
|
||||
open = open_entry(entry);
|
||||
nfs41_open_state_ref(open); /* return a reference */
|
||||
}
|
||||
LeaveCriticalSection(&state->lock);
|
||||
return open;
|
||||
}
|
||||
|
||||
/* find the first lock that needs recovery */
|
||||
static bool_t deleg_lock_find(
|
||||
IN nfs41_open_state *open,
|
||||
OUT nfs41_lock_state *lock_out)
|
||||
{
|
||||
struct list_entry *entry;
|
||||
bool_t found = FALSE;
|
||||
|
||||
AcquireSRWLockShared(&open->lock);
|
||||
list_for_each(entry, &open->locks.list) {
|
||||
nfs41_lock_state *lock = lock_entry(entry);
|
||||
if (lock->delegated) {
|
||||
/* copy offset, length, type */
|
||||
lock_out->offset = lock->offset;
|
||||
lock_out->length = lock->length;
|
||||
lock_out->exclusive = lock->exclusive;
|
||||
lock_out->id = lock->id;
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ReleaseSRWLockShared(&open->lock);
|
||||
return found;
|
||||
}
|
||||
|
||||
/* find the matching lock by id, and reset lock.delegated */
|
||||
static void deleg_lock_update(
|
||||
IN nfs41_open_state *open,
|
||||
IN const nfs41_lock_state *source)
|
||||
{
|
||||
struct list_entry *entry;
|
||||
|
||||
AcquireSRWLockExclusive(&open->lock);
|
||||
list_for_each(entry, &open->locks.list) {
|
||||
nfs41_lock_state *lock = lock_entry(entry);
|
||||
if (lock->id == source->id) {
|
||||
lock->delegated = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ReleaseSRWLockExclusive(&open->lock);
|
||||
}
|
||||
|
||||
static int delegation_flush_locks(
|
||||
IN nfs41_open_state *open,
|
||||
IN bool_t try_recovery)
|
||||
{
|
||||
stateid_arg stateid;
|
||||
nfs41_lock_state lock;
|
||||
int status = NFS4_OK;
|
||||
|
||||
stateid.open = open;
|
||||
stateid.delegation = NULL;
|
||||
|
||||
/* get the starting open/lock stateid */
|
||||
AcquireSRWLockShared(&open->lock);
|
||||
if (open->locks.stateid.seqid) {
|
||||
memcpy(&stateid.stateid, &open->locks.stateid, sizeof(stateid4));
|
||||
stateid.type = STATEID_LOCK;
|
||||
} else {
|
||||
memcpy(&stateid.stateid, &open->stateid, sizeof(stateid4));
|
||||
stateid.type = STATEID_OPEN;
|
||||
}
|
||||
ReleaseSRWLockShared(&open->lock);
|
||||
|
||||
/* send LOCK requests for each delegated lock range */
|
||||
while (deleg_lock_find(open, &lock)) {
|
||||
status = nfs41_lock(open->session, &open->file,
|
||||
&open->owner, lock.exclusive ? WRITE_LT : READ_LT,
|
||||
lock.offset, lock.length, FALSE, try_recovery, &stateid);
|
||||
if (status)
|
||||
break;
|
||||
deleg_lock_update(open, &lock);
|
||||
}
|
||||
|
||||
/* save the updated lock stateid */
|
||||
if (stateid.type == STATEID_LOCK) {
|
||||
AcquireSRWLockExclusive(&open->lock);
|
||||
if (open->locks.stateid.seqid == 0) {
|
||||
/* if it's a new lock stateid, copy it in */
|
||||
memcpy(&open->locks.stateid, &stateid.stateid, sizeof(stateid4));
|
||||
} else if (stateid.stateid.seqid > open->locks.stateid.seqid) {
|
||||
/* update the seqid if it's more recent */
|
||||
open->locks.stateid.seqid = stateid.stateid.seqid;
|
||||
}
|
||||
ReleaseSRWLockExclusive(&open->lock);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
#pragma warning (disable : 4706) /* assignment within conditional expression */
|
||||
|
||||
static int delegation_return(
|
||||
IN nfs41_client *client,
|
||||
IN nfs41_delegation_state *deleg,
|
||||
IN bool_t truncate,
|
||||
IN bool_t try_recovery)
|
||||
{
|
||||
stateid_arg stateid;
|
||||
nfs41_open_state *open;
|
||||
int status;
|
||||
|
||||
if (deleg->srv_open) {
|
||||
/* make an upcall to the kernel: invalide data cache */
|
||||
HANDLE pipe;
|
||||
unsigned char inbuf[sizeof(HANDLE)], *buffer = inbuf;
|
||||
DWORD inbuf_len = sizeof(HANDLE), outbuf_len, dstatus;
|
||||
uint32_t length;
|
||||
dprintf(1, "delegation_return: making a downcall for srv_open=%x\n",
|
||||
deleg->srv_open);
|
||||
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("delegation_return: Unable to open downcall pipe %d\n",
|
||||
GetLastError());
|
||||
goto out_downcall;
|
||||
}
|
||||
length = inbuf_len;
|
||||
safe_write(&buffer, &length, &deleg->srv_open, sizeof(HANDLE));
|
||||
|
||||
dstatus = DeviceIoControl(pipe, IOCTL_NFS41_INVALCACHE, inbuf, inbuf_len,
|
||||
NULL, 0, (LPDWORD)&outbuf_len, NULL);
|
||||
if (!dstatus)
|
||||
eprintf("IOCTL_NFS41_INVALCACHE failed %d\n", GetLastError());
|
||||
CloseHandle(pipe);
|
||||
}
|
||||
out_downcall:
|
||||
|
||||
/* recover opens and locks associated with the delegation */
|
||||
while (open = deleg_open_find(&client->state, deleg)) {
|
||||
status = nfs41_delegation_to_open(open, try_recovery);
|
||||
if (status == NFS4_OK)
|
||||
status = delegation_flush_locks(open, try_recovery);
|
||||
nfs41_open_state_deref(open);
|
||||
|
||||
if (status)
|
||||
break;
|
||||
}
|
||||
|
||||
/* return the delegation */
|
||||
stateid.type = STATEID_DELEG_FILE;
|
||||
stateid.open = NULL;
|
||||
stateid.delegation = deleg;
|
||||
AcquireSRWLockShared(&deleg->lock);
|
||||
memcpy(&stateid.stateid, &deleg->state.stateid, sizeof(stateid4));
|
||||
ReleaseSRWLockShared(&deleg->lock);
|
||||
|
||||
status = nfs41_delegreturn(client->session,
|
||||
&deleg->file, &stateid, try_recovery);
|
||||
if (status == NFS4ERR_BADSESSION)
|
||||
goto out;
|
||||
|
||||
delegation_remove(client, deleg);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
/* open delegation */
|
||||
int nfs41_delegation_granted(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *parent,
|
||||
IN nfs41_path_fh *file,
|
||||
IN open_delegation4 *delegation,
|
||||
IN bool_t try_recovery,
|
||||
OUT nfs41_delegation_state **deleg_out)
|
||||
{
|
||||
stateid_arg stateid;
|
||||
nfs41_client *client = session->client;
|
||||
nfs41_delegation_state *state;
|
||||
int status = NO_ERROR;
|
||||
|
||||
if (delegation->type != OPEN_DELEGATE_READ &&
|
||||
delegation->type != OPEN_DELEGATE_WRITE)
|
||||
goto out;
|
||||
|
||||
if (delegation->recalled) {
|
||||
status = NFS4ERR_DELEG_REVOKED;
|
||||
goto out_return;
|
||||
}
|
||||
|
||||
/* allocate the delegation state */
|
||||
status = delegation_create(parent, file, delegation, &state);
|
||||
if (status)
|
||||
goto out_return;
|
||||
|
||||
/* register the delegation with the client */
|
||||
EnterCriticalSection(&client->state.lock);
|
||||
/* XXX: check for duplicates by fh and stateid? */
|
||||
list_add_tail(&client->state.delegations, &state->client_entry);
|
||||
LeaveCriticalSection(&client->state.lock);
|
||||
|
||||
nfs41_delegation_ref(state); /* return a reference */
|
||||
*deleg_out = state;
|
||||
out:
|
||||
return status;
|
||||
|
||||
out_return: /* return the delegation on failure */
|
||||
memcpy(&stateid.stateid, &delegation->stateid, sizeof(stateid4));
|
||||
stateid.type = STATEID_DELEG_FILE;
|
||||
stateid.open = NULL;
|
||||
stateid.delegation = NULL;
|
||||
nfs41_delegreturn(session, file, &stateid, try_recovery);
|
||||
goto out;
|
||||
}
|
||||
|
||||
#define deleg_entry(pos) list_container(pos, nfs41_delegation_state, client_entry)
|
||||
|
||||
static int deleg_file_cmp(const struct list_entry *entry, const void *value)
|
||||
{
|
||||
const nfs41_fh *lhs = &deleg_entry(entry)->file.fh;
|
||||
const nfs41_fh *rhs = (const nfs41_fh*)value;
|
||||
if (lhs->superblock != rhs->superblock) return -1;
|
||||
if (lhs->fileid != rhs->fileid) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool_t delegation_compatible(
|
||||
IN enum open_delegation_type4 type,
|
||||
IN uint32_t create,
|
||||
IN uint32_t access,
|
||||
IN uint32_t deny)
|
||||
{
|
||||
switch (type) {
|
||||
case OPEN_DELEGATE_WRITE:
|
||||
/* An OPEN_DELEGATE_WRITE delegation allows the client to handle,
|
||||
* on its own, all opens. */
|
||||
return TRUE;
|
||||
|
||||
case OPEN_DELEGATE_READ:
|
||||
/* An OPEN_DELEGATE_READ delegation allows a client to handle,
|
||||
* on its own, requests to open a file for reading that do not
|
||||
* deny OPEN4_SHARE_ACCESS_READ access to others. */
|
||||
if (create == OPEN4_CREATE)
|
||||
return FALSE;
|
||||
if (access & OPEN4_SHARE_ACCESS_WRITE || deny & OPEN4_SHARE_DENY_READ)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static int delegation_find(
|
||||
IN nfs41_client *client,
|
||||
IN const void *value,
|
||||
IN list_compare_fn cmp,
|
||||
OUT nfs41_delegation_state **deleg_out)
|
||||
{
|
||||
struct list_entry *entry;
|
||||
int status = NFS4ERR_BADHANDLE;
|
||||
|
||||
EnterCriticalSection(&client->state.lock);
|
||||
entry = list_search(&client->state.delegations, value, cmp);
|
||||
if (entry) {
|
||||
/* return a reference to the delegation */
|
||||
*deleg_out = deleg_entry(entry);
|
||||
nfs41_delegation_ref(*deleg_out);
|
||||
|
||||
/* move to the 'most recently used' end of the list */
|
||||
list_remove(entry);
|
||||
list_add_tail(&client->state.delegations, entry);
|
||||
status = NFS4_OK;
|
||||
}
|
||||
LeaveCriticalSection(&client->state.lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int delegation_truncate(
|
||||
IN nfs41_delegation_state *deleg,
|
||||
IN nfs41_client *client,
|
||||
IN stateid_arg *stateid,
|
||||
IN nfs41_file_info *info)
|
||||
{
|
||||
nfs41_superblock *superblock = deleg->file.fh.superblock;
|
||||
|
||||
/* use SETATTR to truncate the file */
|
||||
info->attrmask.arr[1] |= FATTR4_WORD1_TIME_CREATE |
|
||||
FATTR4_WORD1_TIME_MODIFY_SET;
|
||||
|
||||
get_nfs_time(&info->time_create);
|
||||
get_nfs_time(&info->time_modify);
|
||||
info->time_delta = &superblock->time_delta;
|
||||
|
||||
/* mask out unsupported attributes */
|
||||
nfs41_superblock_supported_attrs(superblock, &info->attrmask);
|
||||
|
||||
return nfs41_setattr(client->session, &deleg->file, stateid, info);
|
||||
}
|
||||
|
||||
int nfs41_delegate_open(
|
||||
IN nfs41_open_state *state,
|
||||
IN uint32_t create,
|
||||
IN OPTIONAL nfs41_file_info *createattrs,
|
||||
OUT nfs41_file_info *info)
|
||||
{
|
||||
nfs41_client *client = state->session->client;
|
||||
nfs41_path_fh *file = &state->file;
|
||||
uint32_t access = state->share_access;
|
||||
uint32_t deny = state->share_deny;
|
||||
nfs41_delegation_state *deleg;
|
||||
stateid_arg stateid;
|
||||
int status;
|
||||
|
||||
/* search for a delegation with this filehandle */
|
||||
status = delegation_find(client, &file->fh, deleg_file_cmp, &deleg);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
AcquireSRWLockExclusive(&deleg->lock);
|
||||
if (deleg->status != DELEGATION_GRANTED) {
|
||||
/* the delegation is being returned, wait for it to finish */
|
||||
while (deleg->status != DELEGATION_RETURNED)
|
||||
SleepConditionVariableSRW(&deleg->cond, &deleg->lock, INFINITE, 0);
|
||||
status = NFS4ERR_BADHANDLE;
|
||||
}
|
||||
else if (!delegation_compatible(deleg->state.type, create, access, deny)) {
|
||||
#ifdef DELEGATION_RETURN_ON_CONFLICT
|
||||
/* this open will conflict, start the delegation return */
|
||||
deleg->status = DELEGATION_RETURNING;
|
||||
status = NFS4ERR_DELEG_REVOKED;
|
||||
#else
|
||||
status = NFS4ERR_BADHANDLE;
|
||||
#endif
|
||||
} else if (create == OPEN4_CREATE) {
|
||||
/* copy the stateid for SETATTR */
|
||||
stateid.open = NULL;
|
||||
stateid.delegation = deleg;
|
||||
stateid.type = STATEID_DELEG_FILE;
|
||||
memcpy(&stateid.stateid, &deleg->state.stateid, sizeof(stateid4));
|
||||
}
|
||||
if (!status) {
|
||||
dprintf(1, "nfs41_delegate_open: updating srv_open from %x to %x\n",
|
||||
deleg->srv_open, state->srv_open);
|
||||
deleg->srv_open = state->srv_open;
|
||||
}
|
||||
ReleaseSRWLockExclusive(&deleg->lock);
|
||||
|
||||
if (status == NFS4ERR_DELEG_REVOKED)
|
||||
goto out_return;
|
||||
if (status)
|
||||
goto out_deleg;
|
||||
|
||||
if (create == OPEN4_CREATE) {
|
||||
memcpy(info, createattrs, sizeof(nfs41_file_info));
|
||||
|
||||
/* write delegations allow us to simulate OPEN4_CREATE with SETATTR */
|
||||
status = delegation_truncate(deleg, client, &stateid, info);
|
||||
if (status)
|
||||
goto out_deleg;
|
||||
}
|
||||
|
||||
/* TODO: check access against deleg->state.permissions or send ACCESS */
|
||||
|
||||
state->delegation.state = deleg;
|
||||
status = NFS4_OK;
|
||||
out:
|
||||
return status;
|
||||
|
||||
out_return:
|
||||
delegation_return(client, deleg, create == OPEN4_CREATE, TRUE);
|
||||
|
||||
out_deleg:
|
||||
nfs41_delegation_deref(deleg);
|
||||
goto out;
|
||||
}
|
||||
|
||||
int nfs41_delegation_to_open(
|
||||
IN nfs41_open_state *open,
|
||||
IN bool_t try_recovery)
|
||||
{
|
||||
open_delegation4 ignore;
|
||||
open_claim4 claim;
|
||||
stateid4 open_stateid = { 0 };
|
||||
stateid_arg deleg_stateid;
|
||||
int status = NFS4_OK;
|
||||
|
||||
AcquireSRWLockExclusive(&open->lock);
|
||||
if (open->delegation.state == NULL) /* no delegation to reclaim */
|
||||
goto out_unlock;
|
||||
|
||||
if (open->do_close) /* already have an open stateid */
|
||||
goto out_unlock;
|
||||
|
||||
/* if another thread is reclaiming the open stateid,
|
||||
* wait for it to finish before returning success */
|
||||
if (open->delegation.reclaim) {
|
||||
do {
|
||||
SleepConditionVariableSRW(&open->delegation.cond, &open->lock,
|
||||
INFINITE, 0);
|
||||
} while (open->delegation.reclaim);
|
||||
if (open->do_close)
|
||||
goto out_unlock;
|
||||
}
|
||||
open->delegation.reclaim = 1;
|
||||
|
||||
AcquireSRWLockShared(&open->delegation.state->lock);
|
||||
deleg_stateid.open = open;
|
||||
deleg_stateid.delegation = NULL;
|
||||
deleg_stateid.type = STATEID_DELEG_FILE;
|
||||
memcpy(&deleg_stateid.stateid, &open->delegation.state->state.stateid,
|
||||
sizeof(stateid4));
|
||||
ReleaseSRWLockShared(&open->delegation.state->lock);
|
||||
|
||||
ReleaseSRWLockExclusive(&open->lock);
|
||||
|
||||
/* send OPEN with CLAIM_DELEGATE_CUR */
|
||||
claim.claim = CLAIM_DELEGATE_CUR;
|
||||
claim.u.deleg_cur.delegate_stateid = &deleg_stateid;
|
||||
claim.u.deleg_cur.name = &open->file.name;
|
||||
|
||||
status = nfs41_open(open->session, &open->parent, &open->file,
|
||||
&open->owner, &claim, open->share_access, open->share_deny,
|
||||
OPEN4_NOCREATE, 0, NULL, try_recovery, &open_stateid, &ignore, NULL);
|
||||
|
||||
AcquireSRWLockExclusive(&open->lock);
|
||||
if (status == NFS4_OK) {
|
||||
/* save the new open stateid */
|
||||
memcpy(&open->stateid, &open_stateid, sizeof(stateid4));
|
||||
open->do_close = 1;
|
||||
} else if (open->do_close && (status == NFS4ERR_BAD_STATEID ||
|
||||
status == NFS4ERR_STALE_STATEID || status == NFS4ERR_EXPIRED)) {
|
||||
/* something triggered client state recovery, and the open stateid
|
||||
* has already been reclaimed; see recover_stateid_delegation() */
|
||||
status = NFS4_OK;
|
||||
}
|
||||
open->delegation.reclaim = 0;
|
||||
|
||||
/* signal anyone waiting on the open stateid */
|
||||
WakeAllConditionVariable(&open->delegation.cond);
|
||||
out_unlock:
|
||||
ReleaseSRWLockExclusive(&open->lock);
|
||||
if (status)
|
||||
eprintf("nfs41_delegation_to_open(%p) failed with %s\n",
|
||||
open, nfs_error_string(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
void nfs41_delegation_remove_srvopen(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *file)
|
||||
{
|
||||
nfs41_delegation_state *deleg = NULL;
|
||||
|
||||
/* find a delegation for this file */
|
||||
if (delegation_find(session->client, &file->fh, deleg_file_cmp, &deleg))
|
||||
return;
|
||||
dprintf(1, "nfs41_delegation_remove_srvopen: removing reference to "
|
||||
"srv_open=%x\n", deleg->srv_open);
|
||||
AcquireSRWLockExclusive(&deleg->lock);
|
||||
deleg->srv_open = NULL;
|
||||
ReleaseSRWLockExclusive(&deleg->lock);
|
||||
nfs41_delegation_deref(deleg);
|
||||
}
|
||||
|
||||
/* synchronous delegation return */
|
||||
#ifdef DELEGATION_RETURN_ON_CONFLICT
|
||||
int nfs41_delegation_return(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *file,
|
||||
#ifndef __REACTOS__
|
||||
IN enum open_delegation_type4 access,
|
||||
#else
|
||||
IN int access,
|
||||
#endif
|
||||
IN bool_t truncate)
|
||||
{
|
||||
nfs41_client *client = session->client;
|
||||
nfs41_delegation_state *deleg;
|
||||
int status;
|
||||
|
||||
/* find a delegation for this file */
|
||||
status = delegation_find(client, &file->fh, deleg_file_cmp, &deleg);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
AcquireSRWLockExclusive(&deleg->lock);
|
||||
if (deleg->status == DELEGATION_GRANTED) {
|
||||
/* return unless delegation is write and access is read */
|
||||
if (deleg->state.type != OPEN_DELEGATE_WRITE
|
||||
|| access != OPEN_DELEGATE_READ) {
|
||||
deleg->status = DELEGATION_RETURNING;
|
||||
status = NFS4ERR_DELEG_REVOKED;
|
||||
}
|
||||
} else {
|
||||
/* the delegation is being returned, wait for it to finish */
|
||||
while (deleg->status == DELEGATION_RETURNING)
|
||||
SleepConditionVariableSRW(&deleg->cond, &deleg->lock, INFINITE, 0);
|
||||
status = NFS4ERR_BADHANDLE;
|
||||
}
|
||||
ReleaseSRWLockExclusive(&deleg->lock);
|
||||
|
||||
if (status == NFS4ERR_DELEG_REVOKED) {
|
||||
delegation_return(client, deleg, truncate, TRUE);
|
||||
status = NFS4_OK;
|
||||
}
|
||||
|
||||
nfs41_delegation_deref(deleg);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* asynchronous delegation recall */
|
||||
struct recall_thread_args {
|
||||
nfs41_client *client;
|
||||
nfs41_delegation_state *delegation;
|
||||
bool_t truncate;
|
||||
};
|
||||
|
||||
static unsigned int WINAPI delegation_recall_thread(void *args)
|
||||
{
|
||||
struct recall_thread_args *recall = (struct recall_thread_args*)args;
|
||||
|
||||
delegation_return(recall->client, recall->delegation, recall->truncate, TRUE);
|
||||
|
||||
/* clean up thread arguments */
|
||||
nfs41_delegation_deref(recall->delegation);
|
||||
nfs41_root_deref(recall->client->root);
|
||||
free(recall);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int deleg_stateid_cmp(const struct list_entry *entry, const void *value)
|
||||
{
|
||||
const stateid4 *lhs = &deleg_entry(entry)->state.stateid;
|
||||
const stateid4 *rhs = (const stateid4*)value;
|
||||
return memcmp(lhs->other, rhs->other, NFS4_STATEID_OTHER);
|
||||
}
|
||||
|
||||
int nfs41_delegation_recall(
|
||||
IN nfs41_client *client,
|
||||
IN nfs41_fh *fh,
|
||||
IN const stateid4 *stateid,
|
||||
IN bool_t truncate)
|
||||
{
|
||||
nfs41_delegation_state *deleg;
|
||||
struct recall_thread_args *args;
|
||||
int status;
|
||||
|
||||
dprintf(2, "--> nfs41_delegation_recall()\n");
|
||||
|
||||
/* search for the delegation by stateid instead of filehandle;
|
||||
* deleg_file_cmp() relies on a proper superblock and fileid,
|
||||
* which we don't get with CB_RECALL */
|
||||
status = delegation_find(client, stateid, deleg_stateid_cmp, &deleg);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
AcquireSRWLockExclusive(&deleg->lock);
|
||||
if (deleg->state.recalled) {
|
||||
/* return BADHANDLE if we've already responded to CB_RECALL */
|
||||
status = NFS4ERR_BADHANDLE;
|
||||
} else {
|
||||
deleg->state.recalled = 1;
|
||||
|
||||
if (deleg->status == DELEGATION_GRANTED) {
|
||||
/* start the delegation return */
|
||||
deleg->status = DELEGATION_RETURNING;
|
||||
status = NFS4ERR_DELEG_REVOKED;
|
||||
} /* else return NFS4_OK */
|
||||
}
|
||||
ReleaseSRWLockExclusive(&deleg->lock);
|
||||
|
||||
if (status != NFS4ERR_DELEG_REVOKED)
|
||||
goto out_deleg;
|
||||
|
||||
/* allocate thread arguments */
|
||||
args = calloc(1, sizeof(struct recall_thread_args));
|
||||
if (args == NULL) {
|
||||
status = NFS4ERR_SERVERFAULT;
|
||||
eprintf("nfs41_delegation_recall() failed to allocate arguments\n");
|
||||
goto out_deleg;
|
||||
}
|
||||
|
||||
/* hold a reference on the root */
|
||||
nfs41_root_ref(client->root);
|
||||
args->client = client;
|
||||
args->delegation = deleg;
|
||||
args->truncate = truncate;
|
||||
|
||||
/* the callback thread can't make rpc calls, so spawn a separate thread */
|
||||
if (_beginthreadex(NULL, 0, delegation_recall_thread, args, 0, NULL) == 0) {
|
||||
status = NFS4ERR_SERVERFAULT;
|
||||
eprintf("nfs41_delegation_recall() failed to start thread\n");
|
||||
goto out_args;
|
||||
}
|
||||
status = NFS4_OK;
|
||||
out:
|
||||
dprintf(DGLVL, "<-- nfs41_delegation_recall() returning %s\n",
|
||||
nfs_error_string(status));
|
||||
return status;
|
||||
|
||||
out_args:
|
||||
free(args);
|
||||
nfs41_root_deref(client->root);
|
||||
out_deleg:
|
||||
nfs41_delegation_deref(deleg);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
static int deleg_fh_cmp(const struct list_entry *entry, const void *value)
|
||||
{
|
||||
const nfs41_fh *lhs = &deleg_entry(entry)->file.fh;
|
||||
const nfs41_fh *rhs = (const nfs41_fh*)value;
|
||||
if (lhs->len != rhs->len) return -1;
|
||||
return memcmp(lhs->fh, rhs->fh, lhs->len);
|
||||
}
|
||||
|
||||
int nfs41_delegation_getattr(
|
||||
IN nfs41_client *client,
|
||||
IN const nfs41_fh *fh,
|
||||
IN const bitmap4 *attr_request,
|
||||
OUT nfs41_file_info *info)
|
||||
{
|
||||
nfs41_delegation_state *deleg;
|
||||
uint64_t fileid;
|
||||
int status;
|
||||
|
||||
dprintf(2, "--> nfs41_delegation_getattr()\n");
|
||||
|
||||
/* search for a delegation on this file handle */
|
||||
status = delegation_find(client, fh, deleg_fh_cmp, &deleg);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
AcquireSRWLockShared(&deleg->lock);
|
||||
fileid = deleg->file.fh.fileid;
|
||||
if (deleg->status != DELEGATION_GRANTED ||
|
||||
deleg->state.type != OPEN_DELEGATE_WRITE) {
|
||||
status = NFS4ERR_BADHANDLE;
|
||||
}
|
||||
ReleaseSRWLockShared(&deleg->lock);
|
||||
if (status)
|
||||
goto out_deleg;
|
||||
|
||||
ZeroMemory(info, sizeof(nfs41_file_info));
|
||||
|
||||
/* find attributes for the given fileid */
|
||||
status = nfs41_attr_cache_lookup(
|
||||
client_name_cache(client), fileid, info);
|
||||
if (status) {
|
||||
status = NFS4ERR_BADHANDLE;
|
||||
goto out_deleg;
|
||||
}
|
||||
out_deleg:
|
||||
nfs41_delegation_deref(deleg);
|
||||
out:
|
||||
dprintf(DGLVL, "<-- nfs41_delegation_getattr() returning %s\n",
|
||||
nfs_error_string(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
void nfs41_client_delegation_free(
|
||||
IN nfs41_client *client)
|
||||
{
|
||||
struct list_entry *entry, *tmp;
|
||||
|
||||
EnterCriticalSection(&client->state.lock);
|
||||
list_for_each_tmp (entry, tmp, &client->state.delegations) {
|
||||
list_remove(entry);
|
||||
nfs41_delegation_deref(deleg_entry(entry));
|
||||
}
|
||||
LeaveCriticalSection(&client->state.lock);
|
||||
}
|
||||
|
||||
|
||||
static int delegation_recovery_status(
|
||||
IN nfs41_delegation_state *deleg)
|
||||
{
|
||||
int status = NFS4_OK;
|
||||
|
||||
AcquireSRWLockExclusive(&deleg->lock);
|
||||
if (deleg->status == DELEGATION_GRANTED) {
|
||||
if (deleg->revoked) {
|
||||
deleg->status = DELEGATION_RETURNED;
|
||||
status = NFS4ERR_BADHANDLE;
|
||||
} else if (deleg->state.recalled) {
|
||||
deleg->status = DELEGATION_RETURNING;
|
||||
status = NFS4ERR_DELEG_REVOKED;
|
||||
}
|
||||
}
|
||||
ReleaseSRWLockExclusive(&deleg->lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
int nfs41_client_delegation_recovery(
|
||||
IN nfs41_client *client)
|
||||
{
|
||||
struct list_entry *entry, *tmp;
|
||||
nfs41_delegation_state *deleg;
|
||||
int status = NFS4_OK;
|
||||
|
||||
list_for_each_tmp(entry, tmp, &client->state.delegations) {
|
||||
deleg = list_container(entry, nfs41_delegation_state, client_entry);
|
||||
|
||||
status = delegation_recovery_status(deleg);
|
||||
switch (status) {
|
||||
case NFS4ERR_DELEG_REVOKED:
|
||||
/* the delegation was reclaimed, but flagged as recalled;
|
||||
* return it with try_recovery=FALSE */
|
||||
status = delegation_return(client, deleg, FALSE, FALSE);
|
||||
break;
|
||||
|
||||
case NFS4ERR_BADHANDLE:
|
||||
/* reclaim failed, so we have no delegation state on the server;
|
||||
* 'forget' the delegation without trying to return it */
|
||||
delegation_remove(client, deleg);
|
||||
status = NFS4_OK;
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == NFS4ERR_BADSESSION)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* use DELEGPURGE to indicate that we're done reclaiming delegations */
|
||||
status = nfs41_delegpurge(client->session);
|
||||
|
||||
/* support for DELEGPURGE is optional; ignore any errors but BADSESSION */
|
||||
if (status != NFS4ERR_BADSESSION)
|
||||
status = NFS4_OK;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
int nfs41_client_delegation_return_lru(
|
||||
IN nfs41_client *client)
|
||||
{
|
||||
struct list_entry *entry;
|
||||
nfs41_delegation_state *state = NULL;
|
||||
int status = NFS4ERR_BADHANDLE;
|
||||
|
||||
/* starting from the least recently opened, find and return
|
||||
* the first delegation that's not 'in use' (currently open) */
|
||||
|
||||
/* TODO: use a more robust algorithm, taking into account:
|
||||
* -number of total opens
|
||||
* -time since last operation on an associated open, or
|
||||
* -number of operations/second over last n seconds */
|
||||
EnterCriticalSection(&client->state.lock);
|
||||
list_for_each(entry, &client->state.delegations) {
|
||||
state = deleg_entry(entry);
|
||||
|
||||
/* skip if it's currently in use for an open; note that ref_count
|
||||
* can't go from 1 to 2 without holding client->state.lock */
|
||||
if (state->ref_count > 1)
|
||||
continue;
|
||||
|
||||
AcquireSRWLockExclusive(&state->lock);
|
||||
if (state->status == DELEGATION_GRANTED) {
|
||||
/* start returning the delegation */
|
||||
state->status = DELEGATION_RETURNING;
|
||||
status = NFS4ERR_DELEG_REVOKED;
|
||||
}
|
||||
ReleaseSRWLockExclusive(&state->lock);
|
||||
|
||||
if (status == NFS4ERR_DELEG_REVOKED)
|
||||
break;
|
||||
}
|
||||
LeaveCriticalSection(&client->state.lock);
|
||||
|
||||
if (status == NFS4ERR_DELEG_REVOKED)
|
||||
status = delegation_return(client, state, FALSE, TRUE);
|
||||
return status;
|
||||
}
|
113
reactos/base/services/nfsd/delegation.h
Normal file
113
reactos/base/services/nfsd/delegation.h
Normal file
|
@ -0,0 +1,113 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#ifndef DELEGATION_H
|
||||
#define DELEGATION_H
|
||||
|
||||
#include "nfs41.h"
|
||||
|
||||
|
||||
/* option to avoid conflicts by returning the delegation */
|
||||
#define DELEGATION_RETURN_ON_CONFLICT
|
||||
|
||||
|
||||
/* reference counting and cleanup */
|
||||
void nfs41_delegation_ref(
|
||||
IN nfs41_delegation_state *state);
|
||||
|
||||
void nfs41_delegation_deref(
|
||||
IN nfs41_delegation_state *state);
|
||||
|
||||
void nfs41_client_delegation_free(
|
||||
IN nfs41_client *client);
|
||||
|
||||
|
||||
/* open delegation */
|
||||
int nfs41_delegation_granted(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *parent,
|
||||
IN nfs41_path_fh *file,
|
||||
IN open_delegation4 *delegation,
|
||||
IN bool_t try_recovery,
|
||||
OUT nfs41_delegation_state **deleg_out);
|
||||
|
||||
int nfs41_delegate_open(
|
||||
IN nfs41_open_state *state,
|
||||
IN uint32_t create,
|
||||
IN OPTIONAL nfs41_file_info *createattrs,
|
||||
OUT nfs41_file_info *info);
|
||||
|
||||
int nfs41_delegation_to_open(
|
||||
IN nfs41_open_state *open,
|
||||
IN bool_t try_recovery);
|
||||
|
||||
void nfs41_delegation_remove_srvopen(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *file);
|
||||
|
||||
/* synchronous delegation return */
|
||||
#ifdef DELEGATION_RETURN_ON_CONFLICT
|
||||
int nfs41_delegation_return(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *file,
|
||||
#ifndef __REACTOS__
|
||||
IN enum open_delegation_type4 access,
|
||||
#else
|
||||
IN int access,
|
||||
#endif
|
||||
IN bool_t truncate);
|
||||
#else
|
||||
static int nfs41_delegation_return(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *file,
|
||||
IN enum open_delegation_type4 access,
|
||||
IN bool_t truncate)
|
||||
{
|
||||
return NFS4_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* asynchronous delegation recall */
|
||||
int nfs41_delegation_recall(
|
||||
IN nfs41_client *client,
|
||||
IN nfs41_fh *fh,
|
||||
IN const stateid4 *stateid,
|
||||
IN bool_t truncate);
|
||||
|
||||
int nfs41_delegation_getattr(
|
||||
IN nfs41_client *client,
|
||||
IN const nfs41_fh *fh,
|
||||
IN const bitmap4 *attr_request,
|
||||
OUT nfs41_file_info *info);
|
||||
|
||||
|
||||
/* after client state recovery, return any 'recalled' delegations;
|
||||
* must be called under the client's state lock */
|
||||
int nfs41_client_delegation_recovery(
|
||||
IN nfs41_client *client);
|
||||
|
||||
/* attempt to return the least recently used delegation;
|
||||
* fails with NFS4ERR_BADHANDLE if all delegations are in use */
|
||||
int nfs41_client_delegation_return_lru(
|
||||
IN nfs41_client *client);
|
||||
|
||||
#endif /* DELEGATION_H */
|
693
reactos/base/services/nfsd/ea.c
Normal file
693
reactos/base/services/nfsd/ea.c
Normal file
|
@ -0,0 +1,693 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#include "from_kernel.h"
|
||||
#include "nfs41_ops.h"
|
||||
#include "delegation.h"
|
||||
#include "upcall.h"
|
||||
#include "daemon_debug.h"
|
||||
|
||||
|
||||
#define EALVL 2 /* dprintf level for extended attribute logging */
|
||||
|
||||
|
||||
static int set_ea_value(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *parent,
|
||||
IN state_owner4 *owner,
|
||||
IN PFILE_FULL_EA_INFORMATION ea)
|
||||
{
|
||||
nfs41_path_fh file = { 0 };
|
||||
nfs41_file_info createattrs;
|
||||
open_claim4 claim;
|
||||
stateid_arg stateid;
|
||||
open_delegation4 delegation = { 0 };
|
||||
nfs41_write_verf verf;
|
||||
uint32_t bytes_written;
|
||||
int status;
|
||||
|
||||
/* don't allow values larger than NFS4_EASIZE */
|
||||
if (ea->EaValueLength > NFS4_EASIZE) {
|
||||
eprintf("trying to write extended attribute value of size %d, "
|
||||
"max allowed %d\n", ea->EaValueLength, NFS4_EASIZE);
|
||||
status = NFS4ERR_FBIG;
|
||||
goto out;
|
||||
}
|
||||
/* remove the file on empty value */
|
||||
if (ea->EaValueLength == 0) {
|
||||
nfs41_component name;
|
||||
name.name = ea->EaName;
|
||||
name.len = ea->EaNameLength;
|
||||
nfs41_remove(session, parent, &name, 0);
|
||||
status = NFS4_OK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
claim.claim = CLAIM_NULL;
|
||||
claim.u.null.filename = &file.name;
|
||||
file.name.name = ea->EaName;
|
||||
file.name.len = ea->EaNameLength;
|
||||
|
||||
createattrs.attrmask.count = 2;
|
||||
createattrs.attrmask.arr[0] = FATTR4_WORD0_SIZE;
|
||||
createattrs.attrmask.arr[1] = FATTR4_WORD1_MODE;
|
||||
createattrs.size = 0;
|
||||
createattrs.mode = 0664;
|
||||
|
||||
status = nfs41_open(session, parent, &file, owner, &claim,
|
||||
OPEN4_SHARE_ACCESS_WRITE | OPEN4_SHARE_ACCESS_WANT_NO_DELEG,
|
||||
OPEN4_SHARE_DENY_BOTH, OPEN4_CREATE, UNCHECKED4,
|
||||
&createattrs, TRUE, &stateid.stateid, &delegation, NULL);
|
||||
if (status) {
|
||||
eprintf("nfs41_open() failed with %s\n", nfs_error_string(status));
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = nfs41_write(session, &file, &stateid,
|
||||
(unsigned char*)ea->EaName + ea->EaNameLength + 1,
|
||||
ea->EaValueLength, 0, FILE_SYNC4, &bytes_written,
|
||||
&verf, NULL);
|
||||
if (status) {
|
||||
eprintf("nfs41_write() failed with %s\n", nfs_error_string(status));
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
out_close:
|
||||
nfs41_close(session, &file, &stateid);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int is_cygwin_ea(
|
||||
PFILE_FULL_EA_INFORMATION ea)
|
||||
{
|
||||
return (strncmp("NfsV3Attributes", ea->EaName, ea->EaNameLength) == 0
|
||||
&& sizeof("NfsV3Attributes")-1 == ea->EaNameLength)
|
||||
|| (strncmp("NfsActOnLink", ea->EaName, ea->EaNameLength) == 0
|
||||
&& sizeof("NfsActOnLink")-1 == ea->EaNameLength)
|
||||
|| (strncmp("NfsSymlinkTargetName", ea->EaName, ea->EaNameLength) == 0
|
||||
&& sizeof("NfsSymlinkTargetName")-1 == ea->EaNameLength);
|
||||
}
|
||||
|
||||
#define NEXT_ENTRY(ea) ((PBYTE)(ea) + (ea)->NextEntryOffset)
|
||||
|
||||
int nfs41_ea_set(
|
||||
IN nfs41_open_state *state,
|
||||
IN PFILE_FULL_EA_INFORMATION ea)
|
||||
{
|
||||
nfs41_path_fh attrdir = { 0 };
|
||||
int status;
|
||||
|
||||
status = nfs41_rpc_openattr(state->session, &state->file, TRUE, &attrdir.fh);
|
||||
if (status) {
|
||||
eprintf("nfs41_rpc_openattr() failed with error %s\n",
|
||||
nfs_error_string(status));
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (status == NFS4_OK) {
|
||||
if (!is_cygwin_ea(ea))
|
||||
status = set_ea_value(state->session, &attrdir, &state->owner, ea);
|
||||
|
||||
if (ea->NextEntryOffset == 0)
|
||||
break;
|
||||
ea = (PFILE_FULL_EA_INFORMATION)NEXT_ENTRY(ea);
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/* NFS41_EA_SET */
|
||||
static int parse_setexattr(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
setexattr_upcall_args *args = &upcall->args.setexattr;
|
||||
|
||||
status = get_name(&buffer, &length, &args->path);
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->mode, sizeof(args->mode));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->buf_len, sizeof(args->buf_len));
|
||||
if (status) goto out;
|
||||
args->buf = buffer;
|
||||
|
||||
dprintf(1, "parsing NFS41_EA_SET: mode=%o\n", args->mode);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int handle_setexattr(nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
setexattr_upcall_args *args = &upcall->args.setexattr;
|
||||
nfs41_open_state *state = upcall->state_ref;
|
||||
PFILE_FULL_EA_INFORMATION ea =
|
||||
(PFILE_FULL_EA_INFORMATION)args->buf;
|
||||
|
||||
/* break read delegations before SETATTR */
|
||||
nfs41_delegation_return(state->session, &state->file,
|
||||
OPEN_DELEGATE_READ, FALSE);
|
||||
|
||||
if (strncmp("NfsV3Attributes", ea->EaName, ea->EaNameLength) == 0
|
||||
&& sizeof("NfsV3Attributes")-1 == ea->EaNameLength) {
|
||||
nfs41_file_info info;
|
||||
stateid_arg stateid;
|
||||
|
||||
nfs41_open_stateid_arg(state, &stateid);
|
||||
|
||||
info.mode = args->mode;
|
||||
info.attrmask.arr[0] = 0;
|
||||
info.attrmask.arr[1] = FATTR4_WORD1_MODE;
|
||||
info.attrmask.count = 2;
|
||||
|
||||
status = nfs41_setattr(state->session, &state->file, &stateid, &info);
|
||||
if (status) {
|
||||
dprintf(1, "nfs41_setattr() failed with error %s.\n",
|
||||
nfs_error_string(status));
|
||||
goto out;
|
||||
}
|
||||
|
||||
args->ctime = info.change;
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = nfs41_ea_set(state, ea);
|
||||
out:
|
||||
return nfs_to_windows_error(status, ERROR_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
static int marshall_setexattr(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
|
||||
{
|
||||
setexattr_upcall_args *args = &upcall->args.setexattr;
|
||||
return safe_write(&buffer, length, &args->ctime, sizeof(args->ctime));
|
||||
}
|
||||
|
||||
|
||||
/* NFS41_EA_GET */
|
||||
static int parse_getexattr(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
getexattr_upcall_args *args = &upcall->args.getexattr;
|
||||
|
||||
status = get_name(&buffer, &length, &args->path);
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->eaindex, sizeof(args->eaindex));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->restart, sizeof(args->restart));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->single, sizeof(args->single));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->buf_len, sizeof(args->buf_len));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->ealist_len, sizeof(args->ealist_len));
|
||||
if (status) goto out;
|
||||
args->ealist = args->ealist_len ? buffer : NULL;
|
||||
|
||||
dprintf(1, "parsing NFS41_EA_GET: buf_len=%d Index %d Restart %d "
|
||||
"Single %d\n", args->buf_len,args->eaindex, args->restart, args->single);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
#define READDIR_LEN_INITIAL 8192
|
||||
#define READDIR_LEN_MIN 2048
|
||||
|
||||
/* call readdir repeatedly to get a complete list of entries */
|
||||
static int read_entire_dir(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *eadir,
|
||||
OUT unsigned char **buffer_out,
|
||||
OUT uint32_t *length_out)
|
||||
{
|
||||
nfs41_readdir_cookie cookie = { 0 };
|
||||
bitmap4 attr_request;
|
||||
nfs41_readdir_entry *last_entry;
|
||||
unsigned char *buffer;
|
||||
uint32_t buffer_len, len, total_len;
|
||||
bool_t eof;
|
||||
int status = NO_ERROR;
|
||||
|
||||
attr_request.count = 0; /* don't request attributes */
|
||||
|
||||
/* allocate the buffer for readdir entries */
|
||||
buffer_len = READDIR_LEN_INITIAL;
|
||||
buffer = calloc(1, buffer_len);
|
||||
if (buffer == NULL) {
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
|
||||
last_entry = NULL;
|
||||
total_len = 0;
|
||||
eof = FALSE;
|
||||
|
||||
while (!eof) {
|
||||
len = buffer_len - total_len;
|
||||
if (len < READDIR_LEN_MIN) {
|
||||
const ptrdiff_t diff = (unsigned char*)last_entry - buffer;
|
||||
/* realloc the buffer to fit more entries */
|
||||
unsigned char *tmp = realloc(buffer, buffer_len * 2);
|
||||
if (tmp == NULL) {
|
||||
status = GetLastError();
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (last_entry) /* fix last_entry pointer */
|
||||
last_entry = (nfs41_readdir_entry*)(tmp + diff);
|
||||
buffer = tmp;
|
||||
buffer_len *= 2;
|
||||
len = buffer_len - total_len;
|
||||
}
|
||||
|
||||
/* fetch the next group of entries */
|
||||
status = nfs41_readdir(session, eadir, &attr_request,
|
||||
&cookie, buffer + total_len, &len, &eof);
|
||||
if (status)
|
||||
goto out_free;
|
||||
|
||||
if (last_entry == NULL) {
|
||||
/* initialize last_entry to the front of the list */
|
||||
last_entry = (nfs41_readdir_entry*)(buffer + total_len);
|
||||
} else if (len) {
|
||||
/* link the previous list to the new one */
|
||||
last_entry->next_entry_offset = (uint32_t)FIELD_OFFSET(
|
||||
nfs41_readdir_entry, name) + last_entry->name_len;
|
||||
}
|
||||
|
||||
/* find the new last entry */
|
||||
while (last_entry->next_entry_offset) {
|
||||
last_entry = (nfs41_readdir_entry*)((char*)last_entry +
|
||||
last_entry->next_entry_offset);
|
||||
}
|
||||
|
||||
cookie.cookie = last_entry->cookie;
|
||||
total_len += len;
|
||||
}
|
||||
|
||||
*buffer_out = buffer;
|
||||
*length_out = total_len;
|
||||
out:
|
||||
return status;
|
||||
|
||||
out_free:
|
||||
free(buffer);
|
||||
goto out;
|
||||
}
|
||||
|
||||
#define ALIGNED_EASIZE(len) (align4(sizeof(FILE_GET_EA_INFORMATION) + len))
|
||||
|
||||
static uint32_t calculate_ea_list_length(
|
||||
IN const unsigned char *position,
|
||||
IN uint32_t remaining)
|
||||
{
|
||||
const nfs41_readdir_entry *entry;
|
||||
uint32_t length = 0;
|
||||
|
||||
while (remaining) {
|
||||
entry = (const nfs41_readdir_entry*)position;
|
||||
length += ALIGNED_EASIZE(entry->name_len);
|
||||
|
||||
if (!entry->next_entry_offset)
|
||||
break;
|
||||
|
||||
position += entry->next_entry_offset;
|
||||
remaining -= entry->next_entry_offset;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static void populate_ea_list(
|
||||
IN const unsigned char *position,
|
||||
OUT PFILE_GET_EA_INFORMATION ea_list)
|
||||
{
|
||||
const nfs41_readdir_entry *entry;
|
||||
PFILE_GET_EA_INFORMATION ea = ea_list, prev = NULL;
|
||||
|
||||
for (;;) {
|
||||
entry = (const nfs41_readdir_entry*)position;
|
||||
StringCchCopyA(ea->EaName, entry->name_len, entry->name);
|
||||
ea->EaNameLength = (UCHAR)entry->name_len - 1;
|
||||
|
||||
if (!entry->next_entry_offset) {
|
||||
ea->NextEntryOffset = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
prev = ea;
|
||||
ea->NextEntryOffset = ALIGNED_EASIZE(ea->EaNameLength);
|
||||
ea = (PFILE_GET_EA_INFORMATION)NEXT_ENTRY(ea);
|
||||
position += entry->next_entry_offset;
|
||||
}
|
||||
}
|
||||
|
||||
static int get_ea_list(
|
||||
IN OUT nfs41_open_state *state,
|
||||
IN nfs41_path_fh *eadir,
|
||||
OUT PFILE_GET_EA_INFORMATION *ealist_out,
|
||||
OUT uint32_t *eaindex_out)
|
||||
{
|
||||
unsigned char *entry_list;
|
||||
PFILE_GET_EA_INFORMATION ea_list;
|
||||
uint32_t entry_len, ea_size;
|
||||
int status = NO_ERROR;
|
||||
|
||||
EnterCriticalSection(&state->ea.lock);
|
||||
|
||||
if (state->ea.list != INVALID_HANDLE_VALUE) {
|
||||
/* use cached ea names */
|
||||
*ealist_out = state->ea.list;
|
||||
*eaindex_out = state->ea.index;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* read the entire directory into a nfs41_readdir_entry buffer */
|
||||
status = read_entire_dir(state->session, eadir, &entry_list, &entry_len);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
ea_size = calculate_ea_list_length(entry_list, entry_len);
|
||||
if (ea_size == 0) {
|
||||
*ealist_out = state->ea.list = NULL;
|
||||
goto out_free;
|
||||
}
|
||||
ea_list = calloc(1, ea_size);
|
||||
if (ea_list == NULL) {
|
||||
status = GetLastError();
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
populate_ea_list(entry_list, ea_list);
|
||||
|
||||
*ealist_out = state->ea.list = ea_list;
|
||||
*eaindex_out = state->ea.index;
|
||||
out_free:
|
||||
free(entry_list); /* allocated by read_entire_dir() */
|
||||
out:
|
||||
LeaveCriticalSection(&state->ea.lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int get_ea_value(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *parent,
|
||||
IN state_owner4 *owner,
|
||||
OUT PFILE_FULL_EA_INFORMATION ea,
|
||||
IN uint32_t length,
|
||||
OUT uint32_t *needed)
|
||||
{
|
||||
nfs41_path_fh file = { 0 };
|
||||
open_claim4 claim;
|
||||
stateid_arg stateid;
|
||||
open_delegation4 delegation = { 0 };
|
||||
nfs41_file_info info;
|
||||
unsigned char *buffer;
|
||||
uint32_t diff, bytes_read;
|
||||
bool_t eof;
|
||||
int status;
|
||||
|
||||
if (parent->fh.len == 0) /* no named attribute directory */
|
||||
goto out_empty;
|
||||
|
||||
claim.claim = CLAIM_NULL;
|
||||
claim.u.null.filename = &file.name;
|
||||
file.name.name = ea->EaName;
|
||||
file.name.len = ea->EaNameLength;
|
||||
|
||||
status = nfs41_open(session, parent, &file, owner, &claim,
|
||||
OPEN4_SHARE_ACCESS_READ | OPEN4_SHARE_ACCESS_WANT_NO_DELEG,
|
||||
OPEN4_SHARE_DENY_WRITE, OPEN4_NOCREATE, UNCHECKED4, NULL, TRUE,
|
||||
&stateid.stateid, &delegation, &info);
|
||||
if (status) {
|
||||
eprintf("nfs41_open() failed with %s\n", nfs_error_string(status));
|
||||
if (status == NFS4ERR_NOENT)
|
||||
goto out_empty;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (info.size > NFS4_EASIZE) {
|
||||
status = NFS4ERR_FBIG;
|
||||
eprintf("EA value for '%s' longer than maximum %u "
|
||||
"(%llu bytes), returning %s\n", ea->EaName, NFS4_EASIZE,
|
||||
info.size, nfs_error_string(status));
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
buffer = (unsigned char*)ea->EaName + ea->EaNameLength + 1;
|
||||
diff = (uint32_t)(buffer - (unsigned char*)ea);
|
||||
|
||||
/* make sure we have room for the value */
|
||||
if (length < diff + info.size) {
|
||||
*needed = (uint32_t)(sizeof(FILE_FULL_EA_INFORMATION) +
|
||||
ea->EaNameLength + info.size);
|
||||
status = NFS4ERR_TOOSMALL;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
/* read directly into the ea buffer */
|
||||
status = nfs41_read(session, &file, &stateid,
|
||||
0, length - diff, buffer, &bytes_read, &eof);
|
||||
if (status) {
|
||||
eprintf("nfs41_read() failed with %s\n", nfs_error_string(status));
|
||||
goto out_close;
|
||||
}
|
||||
if (!eof) {
|
||||
*needed = (uint32_t)(sizeof(FILE_FULL_EA_INFORMATION) +
|
||||
ea->EaNameLength + NFS4_EASIZE);
|
||||
status = NFS4ERR_TOOSMALL;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
ea->EaValueLength = (USHORT)bytes_read;
|
||||
|
||||
out_close:
|
||||
nfs41_close(session, &file, &stateid);
|
||||
out:
|
||||
return status;
|
||||
|
||||
out_empty: /* return an empty value */
|
||||
ea->EaValueLength = 0;
|
||||
status = NFS4_OK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int empty_ea_error(
|
||||
IN uint32_t index,
|
||||
IN BOOLEAN restart)
|
||||
{
|
||||
/* choose an error value depending on the arguments */
|
||||
if (index)
|
||||
return ERROR_INVALID_EA_HANDLE;
|
||||
|
||||
if (!restart)
|
||||
return ERROR_NO_MORE_FILES; /* -> STATUS_NO_MORE_EAS */
|
||||
|
||||
return ERROR_FILE_NOT_FOUND; /* -> STATUS_NO_EAS_ON_FILE */
|
||||
}
|
||||
|
||||
static int overflow_error(
|
||||
IN OUT getexattr_upcall_args *args,
|
||||
IN PFILE_FULL_EA_INFORMATION prev,
|
||||
IN uint32_t needed)
|
||||
{
|
||||
if (prev) {
|
||||
/* unlink the overflowing entry, but copy the entries that fit */
|
||||
prev->NextEntryOffset = 0;
|
||||
args->overflow = ERROR_BUFFER_OVERFLOW;
|
||||
} else {
|
||||
/* no entries fit; return only the length needed */
|
||||
args->buf_len = needed;
|
||||
args->overflow = ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
/* in either case, the upcall must return NO_ERROR so we
|
||||
* can copy this information down to the driver */
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
static int handle_getexattr(nfs41_upcall *upcall)
|
||||
{
|
||||
getexattr_upcall_args *args = &upcall->args.getexattr;
|
||||
PFILE_GET_EA_INFORMATION query = (PFILE_GET_EA_INFORMATION)args->ealist;
|
||||
PFILE_FULL_EA_INFORMATION ea, prev = NULL;
|
||||
nfs41_open_state *state = upcall->state_ref;
|
||||
nfs41_path_fh parent = { 0 };
|
||||
uint32_t remaining, needed, index = 0;
|
||||
int status;
|
||||
|
||||
status = nfs41_rpc_openattr(state->session, &state->file, FALSE, &parent.fh);
|
||||
if (status == NFS4ERR_NOENT) { /* no named attribute directory */
|
||||
dprintf(EALVL, "no named attribute directory for '%s'\n", args->path);
|
||||
if (query == NULL) {
|
||||
status = empty_ea_error(args->eaindex, args->restart);
|
||||
goto out;
|
||||
}
|
||||
} else if (status) {
|
||||
eprintf("nfs41_rpc_openattr() failed with %s\n",
|
||||
nfs_error_string(status));
|
||||
status = nfs_to_windows_error(status, ERROR_EAS_NOT_SUPPORTED);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (query == NULL) {
|
||||
/* if no names are queried, use READDIR to list them all */
|
||||
uint32_t i;
|
||||
status = get_ea_list(state, &parent, &query, &index);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
if (query == NULL) { /* the file has no EAs */
|
||||
dprintf(EALVL, "empty named attribute directory for '%s'\n",
|
||||
args->path);
|
||||
status = empty_ea_error(args->eaindex, args->restart);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (args->eaindex)
|
||||
index = args->eaindex - 1; /* convert to zero-based index */
|
||||
else if (args->restart)
|
||||
index = 0;
|
||||
|
||||
/* advance the list to the specified index */
|
||||
for (i = 0; i < index; i++) {
|
||||
if (query->NextEntryOffset == 0) {
|
||||
if (args->eaindex)
|
||||
status = ERROR_INVALID_EA_HANDLE;
|
||||
else
|
||||
status = ERROR_NO_MORE_FILES; /* STATUS_NO_MORE_EAS */
|
||||
goto out;
|
||||
}
|
||||
query = (PFILE_GET_EA_INFORMATION)NEXT_ENTRY(query);
|
||||
}
|
||||
}
|
||||
|
||||
/* returned ea information can't exceed the downcall buffer size */
|
||||
if (args->buf_len > UPCALL_BUF_SIZE - 2 * sizeof(uint32_t))
|
||||
args->buf_len = UPCALL_BUF_SIZE - 2 * sizeof(uint32_t);
|
||||
|
||||
args->buf = malloc(args->buf_len);
|
||||
if (args->buf == NULL) {
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
|
||||
ea = (PFILE_FULL_EA_INFORMATION)args->buf;
|
||||
remaining = args->buf_len;
|
||||
|
||||
for (;;) {
|
||||
/* make sure we have room for at least the name */
|
||||
needed = sizeof(FILE_FULL_EA_INFORMATION) + query->EaNameLength;
|
||||
if (needed > remaining) {
|
||||
status = overflow_error(args, prev, needed + NFS4_EASIZE);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ea->EaNameLength = query->EaNameLength;
|
||||
StringCchCopy(ea->EaName, ea->EaNameLength + 1, query->EaName);
|
||||
ea->Flags = 0;
|
||||
|
||||
/* read the value from file */
|
||||
status = get_ea_value(state->session, &parent,
|
||||
&state->owner, ea, remaining, &needed);
|
||||
if (status == NFS4ERR_TOOSMALL) {
|
||||
status = overflow_error(args, prev, needed);
|
||||
goto out;
|
||||
}
|
||||
if (status) {
|
||||
status = nfs_to_windows_error(status, ERROR_EA_FILE_CORRUPT);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
needed = align4(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) +
|
||||
ea->EaNameLength + 1 + ea->EaValueLength);
|
||||
|
||||
if (remaining < needed) {
|
||||
/* align4 may push NextEntryOffset past our buffer, but we
|
||||
* were still able to fit the ea value. set remaining = 0
|
||||
* so we'll fail on the next ea (if any) */
|
||||
remaining = 0;
|
||||
} else
|
||||
remaining -= needed;
|
||||
|
||||
index++;
|
||||
if (query->NextEntryOffset == 0 || args->single)
|
||||
break;
|
||||
|
||||
prev = ea;
|
||||
ea->NextEntryOffset = needed;
|
||||
ea = (PFILE_FULL_EA_INFORMATION)NEXT_ENTRY(ea);
|
||||
query = (PFILE_GET_EA_INFORMATION)NEXT_ENTRY(query);
|
||||
}
|
||||
|
||||
ea->NextEntryOffset = 0;
|
||||
args->buf_len -= remaining;
|
||||
out:
|
||||
if (args->ealist == NULL) { /* update the ea index */
|
||||
EnterCriticalSection(&state->ea.lock);
|
||||
state->ea.index = index;
|
||||
if (status == NO_ERROR && !args->overflow && !args->single) {
|
||||
/* listing was completed, free the cache */
|
||||
free(state->ea.list);
|
||||
state->ea.list = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
LeaveCriticalSection(&state->ea.lock);
|
||||
}
|
||||
return status;
|
||||
|
||||
out_free:
|
||||
free(args->buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int marshall_getexattr(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status = NO_ERROR;
|
||||
getexattr_upcall_args *args = &upcall->args.getexattr;
|
||||
|
||||
status = safe_write(&buffer, length, &args->overflow, sizeof(args->overflow));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &args->buf_len, sizeof(args->buf_len));
|
||||
if (status) goto out;
|
||||
if (args->overflow == ERROR_INSUFFICIENT_BUFFER)
|
||||
goto out;
|
||||
status = safe_write(&buffer, length, args->buf, args->buf_len);
|
||||
if (status) goto out;
|
||||
out:
|
||||
free(args->buf);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
const nfs41_upcall_op nfs41_op_setexattr = {
|
||||
parse_setexattr,
|
||||
handle_setexattr,
|
||||
marshall_setexattr
|
||||
};
|
||||
|
||||
const nfs41_upcall_op nfs41_op_getexattr = {
|
||||
parse_getexattr,
|
||||
handle_getexattr,
|
||||
marshall_getexattr
|
||||
};
|
280
reactos/base/services/nfsd/from_kernel.h
Normal file
280
reactos/base/services/nfsd/from_kernel.h
Normal file
|
@ -0,0 +1,280 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#ifndef _NFS41_DAEMON_
|
||||
#define _NFS41_DAEMON_
|
||||
|
||||
#define FILE_DIRECTORY_FILE 0x00000001
|
||||
#define FILE_WRITE_THROUGH 0x00000002
|
||||
#define FILE_SEQUENTIAL_ONLY 0x00000004
|
||||
#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
|
||||
|
||||
#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
|
||||
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
|
||||
#define FILE_NON_DIRECTORY_FILE 0x00000040
|
||||
#define FILE_CREATE_TREE_CONNECTION 0x00000080
|
||||
|
||||
#define FILE_COMPLETE_IF_OPLOCKED 0x00000100
|
||||
#define FILE_NO_EA_KNOWLEDGE 0x00000200
|
||||
#define FILE_OPEN_REMOTE_INSTANCE 0x00000400
|
||||
#define FILE_RANDOM_ACCESS 0x00000800
|
||||
|
||||
#define FILE_DELETE_ON_CLOSE 0x00001000
|
||||
#define FILE_OPEN_BY_FILE_ID 0x00002000
|
||||
#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000
|
||||
#define FILE_NO_COMPRESSION 0x00008000
|
||||
|
||||
#define FILE_RESERVE_OPFILTER 0x00100000
|
||||
#define FILE_OPEN_REPARSE_POINT 0x00200000
|
||||
#define FILE_OPEN_NO_RECALL 0x00400000
|
||||
#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000
|
||||
|
||||
#define FILE_COPY_STRUCTURED_STORAGE 0x00000041
|
||||
#define FILE_STRUCTURED_STORAGE 0x00000441
|
||||
|
||||
#define FILE_SUPERSEDE 0x00000000
|
||||
#define FILE_OPEN 0x00000001
|
||||
#define FILE_CREATE 0x00000002
|
||||
#define FILE_OPEN_IF 0x00000003
|
||||
#define FILE_OVERWRITE 0x00000004
|
||||
#define FILE_OVERWRITE_IF 0x00000005
|
||||
#define FILE_MAXIMUM_DISPOSITION 0x00000005
|
||||
|
||||
typedef enum _FILE_INFORMATION_CLASS {
|
||||
FileDirectoryInformation = 1,
|
||||
FileFullDirectoryInformation, // 2
|
||||
FileBothDirectoryInformation, // 3
|
||||
FileBasicInformation, // 4
|
||||
FileStandardInformation, // 5
|
||||
FileInternalInformation, // 6
|
||||
FileEaInformation, // 7
|
||||
FileAccessInformation, // 8
|
||||
FileNameInformation, // 9
|
||||
FileRenameInformation, // 10
|
||||
FileLinkInformation, // 11
|
||||
FileNamesInformation, // 12
|
||||
FileDispositionInformation, // 13
|
||||
FilePositionInformation, // 14
|
||||
FileFullEaInformation, // 15
|
||||
FileModeInformation, // 16
|
||||
FileAlignmentInformation, // 17
|
||||
FileAllInformation, // 18
|
||||
FileAllocationInformation, // 19
|
||||
FileEndOfFileInformation, // 20
|
||||
FileAlternateNameInformation, // 21
|
||||
FileStreamInformation, // 22
|
||||
FilePipeInformation, // 23
|
||||
FilePipeLocalInformation, // 24
|
||||
FilePipeRemoteInformation, // 25
|
||||
FileMailslotQueryInformation, // 26
|
||||
FileMailslotSetInformation, // 27
|
||||
FileCompressionInformation, // 28
|
||||
FileObjectIdInformation, // 29
|
||||
FileCompletionInformation, // 30
|
||||
FileMoveClusterInformation, // 31
|
||||
FileQuotaInformation, // 32
|
||||
FileReparsePointInformation, // 33
|
||||
FileNetworkOpenInformation, // 34
|
||||
FileAttributeTagInformation, // 35
|
||||
FileTrackingInformation, // 36
|
||||
FileIdBothDirectoryInformation, // 37
|
||||
FileIdFullDirectoryInformation, // 38
|
||||
FileValidDataLengthInformation, // 39
|
||||
FileShortNameInformation, // 40
|
||||
FileIoCompletionNotificationInformation, // 41
|
||||
FileIoStatusBlockRangeInformation, // 42
|
||||
FileIoPriorityHintInformation, // 43
|
||||
FileSfioReserveInformation, // 44
|
||||
FileSfioVolumeInformation, // 45
|
||||
FileHardLinkInformation, // 46
|
||||
FileProcessIdsUsingFileInformation, // 47
|
||||
FileNormalizedNameInformation, // 48
|
||||
FileNetworkPhysicalNameInformation, // 49
|
||||
FileIdGlobalTxDirectoryInformation, // 50
|
||||
FileMaximumInformation
|
||||
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
|
||||
|
||||
|
||||
/* kernel structures for QueryDirectory results */
|
||||
typedef struct _FILE_NAMES_INFORMATION {
|
||||
ULONG NextEntryOffset;
|
||||
ULONG FileIndex;
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
} FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION;
|
||||
|
||||
typedef struct _FILE_DIRECTORY_INFO {
|
||||
ULONG NextEntryOffset;
|
||||
ULONG FileIndex;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
ULONG FileAttributes;
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
} FILE_DIRECTORY_INFO, *PFILE_DIRECTORY_INFO;
|
||||
|
||||
typedef struct _FILE_BOTH_DIR_INFORMATION {
|
||||
ULONG NextEntryOffset;
|
||||
ULONG FileIndex;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
ULONG FileAttributes;
|
||||
ULONG FileNameLength;
|
||||
ULONG EaSize;
|
||||
CCHAR ShortNameLength;
|
||||
WCHAR ShortName[12];
|
||||
WCHAR FileName[1];
|
||||
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
|
||||
|
||||
typedef struct _FILE_FULL_DIR_INFO {
|
||||
ULONG NextEntryOffset;
|
||||
ULONG FileIndex;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
ULONG FileAttributes;
|
||||
ULONG FileNameLength;
|
||||
ULONG EaSize;
|
||||
WCHAR FileName[1];
|
||||
} FILE_FULL_DIR_INFO, *PFILE_FULL_DIR_INFO;
|
||||
|
||||
typedef struct _FILE_ID_FULL_DIR_INFO {
|
||||
ULONG NextEntryOffset;
|
||||
ULONG FileIndex;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
ULONG FileAttributes;
|
||||
ULONG FileNameLength;
|
||||
ULONG EaSize;
|
||||
LARGE_INTEGER FileId;
|
||||
WCHAR FileName[1];
|
||||
} FILE_ID_FULL_DIR_INFO, *PFILE_ID_FULL_DIR_INFO;
|
||||
|
||||
typedef struct _FILE_LINK_INFORMATION {
|
||||
BOOLEAN ReplaceIfExists;
|
||||
HANDLE RootDirectory;
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
} FILE_LINK_INFORMATION, *PFILE_LINK_INFORMATION;
|
||||
|
||||
typedef struct _FILE_FULL_EA_INFORMATION {
|
||||
ULONG NextEntryOffset;
|
||||
UCHAR Flags;
|
||||
UCHAR EaNameLength;
|
||||
USHORT EaValueLength;
|
||||
CHAR EaName[1];
|
||||
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;
|
||||
|
||||
typedef struct _FILE_GET_EA_INFORMATION {
|
||||
ULONG NextEntryOffset;
|
||||
UCHAR EaNameLength;
|
||||
CHAR EaName[1];
|
||||
} FILE_GET_EA_INFORMATION, *PFILE_GET_EA_INFORMATION;
|
||||
|
||||
typedef struct _FILE_NETWORK_OPEN_INFORMATION {
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
ULONG FileAttributes;
|
||||
} FILE_NETWORK_OPEN_INFORMATION, *PFILE_NETWORK_OPEN_INFORMATION;
|
||||
|
||||
/* wdm.h */
|
||||
typedef enum _FSINFOCLASS {
|
||||
FileFsVolumeInformation = 1,
|
||||
FileFsLabelInformation, // 2
|
||||
FileFsSizeInformation, // 3
|
||||
FileFsDeviceInformation, // 4
|
||||
FileFsAttributeInformation, // 5
|
||||
FileFsControlInformation, // 6
|
||||
FileFsFullSizeInformation, // 7
|
||||
FileFsObjectIdInformation, // 8
|
||||
FileFsDriverPathInformation, // 9
|
||||
FileFsVolumeFlagsInformation,// 10
|
||||
FileFsMaximumInformation
|
||||
} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS;
|
||||
|
||||
/* ntifs.h */
|
||||
#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
|
||||
#define FILE_CASE_PRESERVED_NAMES 0x00000002
|
||||
#define FILE_UNICODE_ON_DISK 0x00000004
|
||||
#define FILE_PERSISTENT_ACLS 0x00000008
|
||||
#define FILE_FILE_COMPRESSION 0x00000010
|
||||
#define FILE_VOLUME_QUOTAS 0x00000020
|
||||
#define FILE_SUPPORTS_SPARSE_FILES 0x00000040
|
||||
#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080
|
||||
#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100
|
||||
#define FILE_VOLUME_IS_COMPRESSED 0x00008000
|
||||
#define FILE_SUPPORTS_OBJECT_IDS 0x00010000
|
||||
#define FILE_SUPPORTS_ENCRYPTION 0x00020000
|
||||
#define FILE_NAMED_STREAMS 0x00040000
|
||||
#define FILE_READ_ONLY_VOLUME 0x00080000
|
||||
#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000
|
||||
#define FILE_SUPPORTS_TRANSACTIONS 0x00200000
|
||||
#define FILE_SUPPORTS_HARD_LINKS 0x00400000
|
||||
#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000
|
||||
#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000
|
||||
#define FILE_SUPPORTS_USN_JOURNAL 0x02000000
|
||||
|
||||
typedef struct _FILE_FS_ATTRIBUTE_INFORMATION {
|
||||
ULONG FileSystemAttributes;
|
||||
LONG MaximumComponentNameLength;
|
||||
ULONG FileSystemNameLength;
|
||||
WCHAR FileSystemName[1];
|
||||
} FILE_FS_ATTRIBUTE_INFORMATION, *PFILE_FS_ATTRIBUTE_INFORMATION;
|
||||
|
||||
/* ntddk.h */
|
||||
typedef struct _FILE_FS_SIZE_INFORMATION {
|
||||
LARGE_INTEGER TotalAllocationUnits;
|
||||
LARGE_INTEGER AvailableAllocationUnits;
|
||||
ULONG SectorsPerAllocationUnit;
|
||||
ULONG BytesPerSector;
|
||||
} FILE_FS_SIZE_INFORMATION, *PFILE_FS_SIZE_INFORMATION;
|
||||
|
||||
typedef struct _FILE_FS_FULL_SIZE_INFORMATION {
|
||||
LARGE_INTEGER TotalAllocationUnits;
|
||||
LARGE_INTEGER CallerAvailableAllocationUnits;
|
||||
LARGE_INTEGER ActualAvailableAllocationUnits;
|
||||
ULONG SectorsPerAllocationUnit;
|
||||
ULONG BytesPerSector;
|
||||
} FILE_FS_FULL_SIZE_INFORMATION, *PFILE_FS_FULL_SIZE_INFORMATION;
|
||||
|
||||
typedef struct _FILE_INTERNAL_INFORMATION {
|
||||
LARGE_INTEGER IndexNumber;
|
||||
} FILE_INTERNAL_INFORMATION, *PFILE_INTERNAL_INFORMATION;
|
||||
#endif
|
184
reactos/base/services/nfsd/getattr.c
Normal file
184
reactos/base/services/nfsd/getattr.c
Normal file
|
@ -0,0 +1,184 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#include "nfs41_ops.h"
|
||||
#include "name_cache.h"
|
||||
#include "upcall.h"
|
||||
#include "daemon_debug.h"
|
||||
|
||||
|
||||
int nfs41_cached_getattr(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *file,
|
||||
OUT nfs41_file_info *info)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* first look for cached attributes */
|
||||
status = nfs41_attr_cache_lookup(session_name_cache(session),
|
||||
file->fh.fileid, info);
|
||||
|
||||
if (status) {
|
||||
/* fetch attributes from the server */
|
||||
bitmap4 attr_request;
|
||||
nfs41_superblock_getattr_mask(file->fh.superblock, &attr_request);
|
||||
|
||||
status = nfs41_getattr(session, file, &attr_request, info);
|
||||
if (status) {
|
||||
eprintf("nfs41_getattr() failed with %s\n",
|
||||
nfs_error_string(status));
|
||||
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/* NFS41_FILE_QUERY */
|
||||
static int parse_getattr(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
getattr_upcall_args *args = &upcall->args.getattr;
|
||||
|
||||
status = safe_read(&buffer, &length, &args->query_class, sizeof(args->query_class));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->buf_len, sizeof(args->buf_len));
|
||||
if (status) goto out;
|
||||
|
||||
dprintf(1, "parsing NFS41_FILE_QUERY: info_class=%d buf_len=%d file=%.*s\n",
|
||||
args->query_class, args->buf_len, upcall->state_ref->path.len,
|
||||
upcall->state_ref->path.path);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int handle_getattr(nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
getattr_upcall_args *args = &upcall->args.getattr;
|
||||
nfs41_open_state *state = upcall->state_ref;
|
||||
nfs41_file_info info = { 0 };
|
||||
|
||||
status = nfs41_cached_getattr(state->session, &state->file, &info);
|
||||
if (status) {
|
||||
eprintf("nfs41_cached_getattr() failed with %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (info.type == NF4LNK) {
|
||||
nfs41_file_info target_info;
|
||||
int target_status = nfs41_symlink_follow(upcall->root_ref,
|
||||
state->session, &state->file, &target_info);
|
||||
if (target_status == NO_ERROR && target_info.type == NF4DIR)
|
||||
info.symlink_dir = TRUE;
|
||||
}
|
||||
|
||||
switch (args->query_class) {
|
||||
case FileBasicInformation:
|
||||
nfs_to_basic_info(&info, &args->basic_info);
|
||||
args->ctime = info.change;
|
||||
break;
|
||||
case FileStandardInformation:
|
||||
nfs_to_standard_info(&info, &args->std_info);
|
||||
break;
|
||||
case FileAttributeTagInformation:
|
||||
args->tag_info.FileAttributes = nfs_file_info_to_attributes(&info);
|
||||
args->tag_info.ReparseTag = info.type == NF4LNK ?
|
||||
IO_REPARSE_TAG_SYMLINK : 0;
|
||||
break;
|
||||
case FileInternalInformation:
|
||||
args->intr_info.IndexNumber.QuadPart = info.fileid;
|
||||
break;
|
||||
case FileNetworkOpenInformation:
|
||||
nfs_to_network_openinfo(&info, &args->network_info);
|
||||
break;
|
||||
default:
|
||||
eprintf("unhandled file query class %d\n", args->query_class);
|
||||
status = ERROR_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int marshall_getattr(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
getattr_upcall_args *args = &upcall->args.getattr;
|
||||
uint32_t info_len;
|
||||
|
||||
switch (args->query_class) {
|
||||
case FileBasicInformation:
|
||||
info_len = sizeof(args->basic_info);
|
||||
status = safe_write(&buffer, length, &info_len, sizeof(info_len));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &args->basic_info, info_len);
|
||||
if (status) goto out;
|
||||
break;
|
||||
case FileStandardInformation:
|
||||
info_len = sizeof(args->std_info);
|
||||
status = safe_write(&buffer, length, &info_len, sizeof(info_len));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &args->std_info, info_len);
|
||||
if (status) goto out;
|
||||
break;
|
||||
case FileAttributeTagInformation:
|
||||
info_len = sizeof(args->tag_info);
|
||||
status = safe_write(&buffer, length, &info_len, sizeof(info_len));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &args->tag_info, info_len);
|
||||
if (status) goto out;
|
||||
break;
|
||||
case FileInternalInformation:
|
||||
info_len = sizeof(args->intr_info);
|
||||
status = safe_write(&buffer, length, &info_len, sizeof(info_len));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &args->intr_info, info_len);
|
||||
if (status) goto out;
|
||||
break;
|
||||
case FileNetworkOpenInformation:
|
||||
info_len = sizeof(args->network_info);
|
||||
status = safe_write(&buffer, length, &info_len, sizeof(info_len));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &args->network_info, info_len);
|
||||
if (status) goto out;
|
||||
break;
|
||||
default:
|
||||
eprintf("unknown file query class %d\n", args->query_class);
|
||||
status = 103;
|
||||
goto out;
|
||||
}
|
||||
status = safe_write(&buffer, length, &args->ctime, sizeof(args->ctime));
|
||||
if (status) goto out;
|
||||
dprintf(1, "NFS41_FILE_QUERY: downcall changattr=%llu\n", args->ctime);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
const nfs41_upcall_op nfs41_op_getattr = {
|
||||
parse_getattr,
|
||||
handle_getattr,
|
||||
marshall_getattr
|
||||
};
|
1063
reactos/base/services/nfsd/idmap.c
Normal file
1063
reactos/base/services/nfsd/idmap.c
Normal file
File diff suppressed because it is too large
Load diff
67
reactos/base/services/nfsd/idmap.h
Normal file
67
reactos/base/services/nfsd/idmap.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#ifndef IDMAP_H
|
||||
#define IDMAP_H
|
||||
|
||||
#include "nfs41_types.h"
|
||||
|
||||
|
||||
/* idmap.c */
|
||||
typedef struct idmap_context nfs41_idmapper;
|
||||
|
||||
int nfs41_idmap_create(
|
||||
nfs41_idmapper **context_out);
|
||||
|
||||
void nfs41_idmap_free(
|
||||
nfs41_idmapper *context);
|
||||
|
||||
|
||||
int nfs41_idmap_name_to_ids(
|
||||
nfs41_idmapper *context,
|
||||
const char *username,
|
||||
uid_t *uid_out,
|
||||
gid_t *gid_out);
|
||||
|
||||
int nfs41_idmap_uid_to_name(
|
||||
nfs41_idmapper *context,
|
||||
uid_t uid,
|
||||
char *name_out,
|
||||
size_t len);
|
||||
|
||||
int nfs41_idmap_principal_to_ids(
|
||||
nfs41_idmapper *context,
|
||||
const char *principal,
|
||||
uid_t *uid_out,
|
||||
gid_t *gid_out);
|
||||
|
||||
int nfs41_idmap_group_to_gid(
|
||||
nfs41_idmapper *context,
|
||||
const char *name,
|
||||
gid_t *gid_out);
|
||||
|
||||
int nfs41_idmap_gid_to_group(
|
||||
nfs41_idmapper *context,
|
||||
gid_t gid,
|
||||
char *name_out,
|
||||
size_t len);
|
||||
|
||||
#endif /* !IDMAP_H */
|
114
reactos/base/services/nfsd/list.h
Normal file
114
reactos/base/services/nfsd/list.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#ifndef NFS41_LIST_H
|
||||
#define NFS41_LIST_H
|
||||
|
||||
|
||||
/* doubly-linked list */
|
||||
struct list_entry {
|
||||
struct list_entry *prev;
|
||||
struct list_entry *next;
|
||||
};
|
||||
|
||||
|
||||
#define list_container(entry, type, field) \
|
||||
((type*)((const char*)(entry) - (const char*)(&((type*)0)->field)))
|
||||
|
||||
#define list_for_each(entry, head) \
|
||||
for (entry = (head)->next; entry != (head); entry = entry->next)
|
||||
|
||||
#define list_for_each_tmp(entry, tmp, head) \
|
||||
for (entry = (head)->next, tmp = entry->next; entry != (head); \
|
||||
entry = tmp, tmp = entry->next)
|
||||
|
||||
#define list_for_each_reverse(entry, head) \
|
||||
for (entry = (head)->prev; entry != (head); entry = entry->prev)
|
||||
|
||||
#define list_for_each_reverse_tmp(entry, tmp, head) \
|
||||
for (entry = (head)->next, tmp = entry->next; entry != (head); \
|
||||
entry = tmp, tmp = entry->next)
|
||||
|
||||
|
||||
static void list_init(
|
||||
struct list_entry *head)
|
||||
{
|
||||
head->prev = head;
|
||||
head->next = head;
|
||||
}
|
||||
|
||||
static int list_empty(
|
||||
struct list_entry *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
static void list_add(
|
||||
struct list_entry *entry,
|
||||
struct list_entry *prev,
|
||||
struct list_entry *next)
|
||||
{
|
||||
/* assert(prev->next == next && next->prev == prev); */
|
||||
entry->prev = prev;
|
||||
entry->next = next;
|
||||
prev->next = entry;
|
||||
next->prev = entry;
|
||||
}
|
||||
|
||||
static void list_add_head(
|
||||
struct list_entry *head,
|
||||
struct list_entry *entry)
|
||||
{
|
||||
list_add(entry, head, head->next);
|
||||
}
|
||||
|
||||
static void list_add_tail(
|
||||
struct list_entry *head,
|
||||
struct list_entry *entry)
|
||||
{
|
||||
list_add(entry, head->prev, head);
|
||||
}
|
||||
|
||||
static void list_remove(
|
||||
struct list_entry *entry)
|
||||
{
|
||||
if (!list_empty(entry)) {
|
||||
entry->next->prev = entry->prev;
|
||||
entry->prev->next = entry->next;
|
||||
list_init(entry);
|
||||
}
|
||||
}
|
||||
|
||||
typedef int (*list_compare_fn)(const struct list_entry*, const void*);
|
||||
|
||||
static struct list_entry* list_search(
|
||||
const struct list_entry *head,
|
||||
const void *value,
|
||||
list_compare_fn compare)
|
||||
{
|
||||
struct list_entry *entry;
|
||||
list_for_each(entry, head)
|
||||
if (compare(entry, value) == 0)
|
||||
return entry;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* !NFS41_LIST_H */
|
369
reactos/base/services/nfsd/lock.c
Normal file
369
reactos/base/services/nfsd/lock.c
Normal file
|
@ -0,0 +1,369 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "daemon_debug.h"
|
||||
#include "delegation.h"
|
||||
#include "nfs41_ops.h"
|
||||
#include "upcall.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
#define LKLVL 2 /* dprintf level for lock logging */
|
||||
|
||||
|
||||
static void lock_stateid_arg(
|
||||
IN nfs41_open_state *state,
|
||||
OUT stateid_arg *arg)
|
||||
{
|
||||
arg->open = state;
|
||||
arg->delegation = NULL;
|
||||
|
||||
AcquireSRWLockShared(&state->lock);
|
||||
if (state->locks.stateid.seqid) {
|
||||
memcpy(&arg->stateid, &state->locks.stateid, sizeof(stateid4));
|
||||
arg->type = STATEID_LOCK;
|
||||
} else if (state->do_close) {
|
||||
memcpy(&arg->stateid, &state->stateid, sizeof(stateid4));
|
||||
arg->type = STATEID_OPEN;
|
||||
} else {
|
||||
memset(&arg->stateid, 0, sizeof(stateid4));
|
||||
arg->type = STATEID_SPECIAL;
|
||||
}
|
||||
ReleaseSRWLockShared(&state->lock);
|
||||
}
|
||||
|
||||
/* expects the caller to hold an exclusive lock on nfs41_open_state.lock */
|
||||
static void lock_stateid_update(
|
||||
OUT nfs41_open_state *state,
|
||||
IN const stateid4 *stateid)
|
||||
{
|
||||
if (state->locks.stateid.seqid == 0) {
|
||||
/* if it's a new lock stateid, copy it in */
|
||||
memcpy(&state->locks.stateid, stateid, sizeof(stateid4));
|
||||
} else if (stateid->seqid > state->locks.stateid.seqid) {
|
||||
/* update the seqid if it's more recent */
|
||||
state->locks.stateid.seqid = stateid->seqid;
|
||||
}
|
||||
}
|
||||
|
||||
static void open_lock_add(
|
||||
IN nfs41_open_state *open,
|
||||
IN const stateid_arg *stateid,
|
||||
IN nfs41_lock_state *lock)
|
||||
{
|
||||
AcquireSRWLockExclusive(&open->lock);
|
||||
|
||||
if (stateid->type == STATEID_LOCK)
|
||||
lock_stateid_update(open, &stateid->stateid);
|
||||
|
||||
lock->id = open->locks.counter++;
|
||||
list_add_tail(&open->locks.list, &lock->open_entry);
|
||||
|
||||
ReleaseSRWLockExclusive(&open->lock);
|
||||
}
|
||||
|
||||
static bool_t open_lock_delegate(
|
||||
IN nfs41_open_state *open,
|
||||
IN nfs41_lock_state *lock)
|
||||
{
|
||||
bool_t delegated = FALSE;
|
||||
|
||||
AcquireSRWLockExclusive(&open->lock);
|
||||
if (open->delegation.state) {
|
||||
nfs41_delegation_state *deleg = open->delegation.state;
|
||||
AcquireSRWLockShared(&deleg->lock);
|
||||
if (deleg->state.type == OPEN_DELEGATE_WRITE
|
||||
&& deleg->status == DELEGATION_GRANTED) {
|
||||
lock->delegated = 1;
|
||||
lock->id = open->locks.counter++;
|
||||
list_add_tail(&open->locks.list, &lock->open_entry);
|
||||
delegated = TRUE;
|
||||
}
|
||||
ReleaseSRWLockShared(&deleg->lock);
|
||||
}
|
||||
ReleaseSRWLockExclusive(&open->lock);
|
||||
|
||||
return delegated;
|
||||
}
|
||||
|
||||
#define lock_entry(pos) list_container(pos, nfs41_lock_state, open_entry)
|
||||
|
||||
static int lock_range_cmp(const struct list_entry *entry, const void *value)
|
||||
{
|
||||
const nfs41_lock_state *lhs = lock_entry(entry);
|
||||
const nfs41_lock_state *rhs = (const nfs41_lock_state*)value;
|
||||
if (lhs->offset != rhs->offset) return -1;
|
||||
if (lhs->length != rhs->length) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int open_unlock_delegate(
|
||||
IN nfs41_open_state *open,
|
||||
IN const nfs41_lock_state *input)
|
||||
{
|
||||
struct list_entry *entry;
|
||||
int status = ERROR_NOT_LOCKED;
|
||||
|
||||
AcquireSRWLockExclusive(&open->lock);
|
||||
|
||||
/* find lock state that matches this range */
|
||||
entry = list_search(&open->locks.list, input, lock_range_cmp);
|
||||
if (entry) {
|
||||
nfs41_lock_state *lock = lock_entry(entry);
|
||||
if (lock->delegated) {
|
||||
/* if the lock was delegated, remove/free it and return success */
|
||||
list_remove(entry);
|
||||
free(lock);
|
||||
status = NO_ERROR;
|
||||
} else
|
||||
status = ERROR_LOCKED;
|
||||
}
|
||||
|
||||
ReleaseSRWLockExclusive(&open->lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void open_unlock_remove(
|
||||
IN nfs41_open_state *open,
|
||||
IN const stateid_arg *stateid,
|
||||
IN const nfs41_lock_state *input)
|
||||
{
|
||||
struct list_entry *entry;
|
||||
|
||||
AcquireSRWLockExclusive(&open->lock);
|
||||
if (stateid->type == STATEID_LOCK)
|
||||
lock_stateid_update(open, &stateid->stateid);
|
||||
|
||||
/* find and remove the unlocked range */
|
||||
entry = list_search(&open->locks.list, input, lock_range_cmp);
|
||||
if (entry) {
|
||||
list_remove(entry);
|
||||
free(lock_entry(entry));
|
||||
}
|
||||
ReleaseSRWLockExclusive(&open->lock);
|
||||
}
|
||||
|
||||
|
||||
/* NFS41_LOCK */
|
||||
static int parse_lock(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
lock_upcall_args *args = &upcall->args.lock;
|
||||
|
||||
status = safe_read(&buffer, &length, &args->offset, sizeof(LONGLONG));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->length, sizeof(LONGLONG));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->exclusive, sizeof(BOOLEAN));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->blocking, sizeof(BOOLEAN));
|
||||
if (status) goto out;
|
||||
|
||||
dprintf(1, "parsing NFS41_LOCK: offset=0x%llx length=0x%llx exclusive=%u "
|
||||
"blocking=%u\n", args->offset, args->length, args->exclusive,
|
||||
args->blocking);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static __inline uint32_t get_lock_type(BOOLEAN exclusive, BOOLEAN blocking)
|
||||
{
|
||||
return blocking == 0
|
||||
? ( exclusive == 0 ? READ_LT : WRITE_LT )
|
||||
: ( exclusive == 0 ? READW_LT : WRITEW_LT );
|
||||
}
|
||||
|
||||
static int handle_lock(nfs41_upcall *upcall)
|
||||
{
|
||||
stateid_arg stateid;
|
||||
lock_upcall_args *args = &upcall->args.lock;
|
||||
nfs41_open_state *state = upcall->state_ref;
|
||||
nfs41_lock_state *lock;
|
||||
const uint32_t type = get_lock_type(args->exclusive, args->blocking);
|
||||
int status = NO_ERROR;
|
||||
|
||||
/* 18.10.3. Operation 12: LOCK - Create Lock
|
||||
* "To lock the file from a specific offset through the end-of-file
|
||||
* (no matter how long the file actually is) use a length field equal
|
||||
* to NFS4_UINT64_MAX." */
|
||||
if (args->length >= NFS4_UINT64_MAX - args->offset)
|
||||
args->length = NFS4_UINT64_MAX;
|
||||
|
||||
/* allocate the lock state */
|
||||
lock = calloc(1, sizeof(nfs41_lock_state));
|
||||
if (lock == NULL) {
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
lock->offset = args->offset;
|
||||
lock->length = args->length;
|
||||
lock->exclusive = args->exclusive;
|
||||
|
||||
/* if we hold a write delegation, handle the lock locally */
|
||||
if (open_lock_delegate(state, lock)) {
|
||||
dprintf(LKLVL, "delegated lock { %llu, %llu }\n",
|
||||
lock->offset, lock->length);
|
||||
args->acquired = TRUE; /* for cancel_lock() */
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* open_to_lock_owner4 requires an open stateid; if we
|
||||
* have a delegation, convert it to an open stateid */
|
||||
status = nfs41_delegation_to_open(state, TRUE);
|
||||
if (status) {
|
||||
status = ERROR_FILE_INVALID;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
EnterCriticalSection(&state->locks.lock);
|
||||
|
||||
lock_stateid_arg(state, &stateid);
|
||||
|
||||
status = nfs41_lock(state->session, &state->file, &state->owner,
|
||||
type, lock->offset, lock->length, FALSE, TRUE, &stateid);
|
||||
if (status) {
|
||||
dprintf(LKLVL, "nfs41_lock failed with %s\n",
|
||||
nfs_error_string(status));
|
||||
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
|
||||
LeaveCriticalSection(&state->locks.lock);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* save lock state with the open */
|
||||
open_lock_add(state, &stateid, lock);
|
||||
LeaveCriticalSection(&state->locks.lock);
|
||||
|
||||
args->acquired = TRUE; /* for cancel_lock() */
|
||||
out:
|
||||
return status;
|
||||
|
||||
out_free:
|
||||
free(lock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void cancel_lock(IN nfs41_upcall *upcall)
|
||||
{
|
||||
stateid_arg stateid;
|
||||
nfs41_lock_state input;
|
||||
lock_upcall_args *args = &upcall->args.lock;
|
||||
nfs41_open_state *state = upcall->state_ref;
|
||||
int status = NO_ERROR;
|
||||
|
||||
dprintf(1, "--> cancel_lock()\n");
|
||||
|
||||
/* can't do 'if (upcall->status)' here, because a handle_lock() success
|
||||
* could be overwritten by upcall_marshall() or allocation failure */
|
||||
if (!args->acquired)
|
||||
goto out;
|
||||
|
||||
input.offset = args->offset;
|
||||
input.length = args->length;
|
||||
|
||||
/* search for the range to unlock, and remove if delegated */
|
||||
status = open_unlock_delegate(state, &input);
|
||||
if (status != ERROR_LOCKED)
|
||||
goto out;
|
||||
|
||||
EnterCriticalSection(&state->locks.lock);
|
||||
lock_stateid_arg(state, &stateid);
|
||||
|
||||
status = nfs41_unlock(state->session, &state->file,
|
||||
args->offset, args->length, &stateid);
|
||||
|
||||
open_unlock_remove(state, &stateid, &input);
|
||||
LeaveCriticalSection(&state->locks.lock);
|
||||
|
||||
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
|
||||
out:
|
||||
dprintf(1, "<-- cancel_lock() returning %d\n", status);
|
||||
}
|
||||
|
||||
|
||||
/* NFS41_UNLOCK */
|
||||
static int parse_unlock(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
unlock_upcall_args *args = &upcall->args.unlock;
|
||||
|
||||
status = safe_read(&buffer, &length, &args->count, sizeof(ULONG));
|
||||
if (status) goto out;
|
||||
|
||||
args->buf = buffer;
|
||||
args->buf_len = length;
|
||||
|
||||
dprintf(1, "parsing NFS41_UNLOCK: count=%u\n", args->count);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int handle_unlock(nfs41_upcall *upcall)
|
||||
{
|
||||
nfs41_lock_state input;
|
||||
stateid_arg stateid;
|
||||
unlock_upcall_args *args = &upcall->args.unlock;
|
||||
nfs41_open_state *state = upcall->state_ref;
|
||||
unsigned char *buf = args->buf;
|
||||
uint32_t buf_len = args->buf_len;
|
||||
uint32_t i;
|
||||
int status = NO_ERROR;
|
||||
|
||||
for (i = 0; i < args->count; i++) {
|
||||
if (safe_read(&buf, &buf_len, &input.offset, sizeof(LONGLONG))) break;
|
||||
if (safe_read(&buf, &buf_len, &input.length, sizeof(LONGLONG))) break;
|
||||
|
||||
/* do the same translation as LOCK, or the ranges won't match */
|
||||
if (input.length >= NFS4_UINT64_MAX - input.offset)
|
||||
input.length = NFS4_UINT64_MAX;
|
||||
|
||||
/* search for the range to unlock, and remove if delegated */
|
||||
status = open_unlock_delegate(state, &input);
|
||||
if (status != ERROR_LOCKED)
|
||||
continue;
|
||||
|
||||
EnterCriticalSection(&state->locks.lock);
|
||||
lock_stateid_arg(state, &stateid);
|
||||
|
||||
status = nfs41_unlock(state->session, &state->file,
|
||||
input.offset, input.length, &stateid);
|
||||
|
||||
open_unlock_remove(state, &stateid, &input);
|
||||
LeaveCriticalSection(&state->locks.lock);
|
||||
|
||||
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
const nfs41_upcall_op nfs41_op_lock = {
|
||||
parse_lock,
|
||||
handle_lock,
|
||||
NULL,
|
||||
cancel_lock
|
||||
};
|
||||
const nfs41_upcall_op nfs41_op_unlock = {
|
||||
parse_unlock,
|
||||
handle_unlock
|
||||
};
|
508
reactos/base/services/nfsd/lookup.c
Normal file
508
reactos/base/services/nfsd/lookup.c
Normal file
|
@ -0,0 +1,508 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "nfs41_compound.h"
|
||||
#include "nfs41_ops.h"
|
||||
#include "name_cache.h"
|
||||
#include "util.h"
|
||||
#include "daemon_debug.h"
|
||||
|
||||
|
||||
#define LULVL 2 /* dprintf level for lookup logging */
|
||||
|
||||
|
||||
#define MAX_LOOKUP_COMPONENTS 8
|
||||
|
||||
/* map NFS4ERR_MOVED to an arbitrary windows error */
|
||||
#define ERROR_FILESYSTEM_ABSENT ERROR_DEVICE_REMOVED
|
||||
|
||||
struct lookup_referral {
|
||||
nfs41_path_fh parent;
|
||||
nfs41_component name;
|
||||
};
|
||||
|
||||
|
||||
typedef struct __nfs41_lookup_component_args {
|
||||
nfs41_sequence_args sequence;
|
||||
nfs41_putfh_args putfh;
|
||||
nfs41_lookup_args lookup[MAX_LOOKUP_COMPONENTS];
|
||||
nfs41_getattr_args getrootattr;
|
||||
nfs41_getattr_args getattr[MAX_LOOKUP_COMPONENTS];
|
||||
bitmap4 attr_request;
|
||||
} nfs41_lookup_component_args;
|
||||
|
||||
typedef struct __nfs41_lookup_component_res {
|
||||
nfs41_sequence_res sequence;
|
||||
nfs41_putfh_res putfh;
|
||||
nfs41_lookup_res lookup[MAX_LOOKUP_COMPONENTS];
|
||||
nfs41_getfh_res getrootfh;
|
||||
nfs41_getfh_res getfh[MAX_LOOKUP_COMPONENTS];
|
||||
nfs41_path_fh root;
|
||||
nfs41_path_fh file[MAX_LOOKUP_COMPONENTS];
|
||||
nfs41_getattr_res getrootattr;
|
||||
nfs41_getattr_res getattr[MAX_LOOKUP_COMPONENTS];
|
||||
nfs41_file_info rootinfo;
|
||||
nfs41_file_info info[MAX_LOOKUP_COMPONENTS];
|
||||
struct lookup_referral *referral;
|
||||
} nfs41_lookup_component_res;
|
||||
|
||||
|
||||
static void init_component_args(
|
||||
IN nfs41_lookup_component_args *args,
|
||||
IN nfs41_lookup_component_res *res,
|
||||
IN nfs41_abs_path *path,
|
||||
IN struct lookup_referral *referral)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
args->attr_request.count = 2;
|
||||
args->attr_request.arr[0] = FATTR4_WORD0_TYPE
|
||||
| FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE
|
||||
| FATTR4_WORD0_FSID | FATTR4_WORD0_FILEID
|
||||
| FATTR4_WORD0_HIDDEN | FATTR4_WORD0_ARCHIVE;
|
||||
args->attr_request.arr[1] = FATTR4_WORD1_MODE
|
||||
| FATTR4_WORD1_NUMLINKS | FATTR4_WORD1_SYSTEM
|
||||
| FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_CREATE
|
||||
| FATTR4_WORD1_TIME_MODIFY;
|
||||
|
||||
args->getrootattr.attr_request = &args->attr_request;
|
||||
res->root.path = path;
|
||||
res->getrootfh.fh = &res->root.fh;
|
||||
res->getrootattr.info = &res->rootinfo;
|
||||
res->getrootattr.obj_attributes.attr_vals_len = NFS4_OPAQUE_LIMIT;
|
||||
res->referral = referral;
|
||||
|
||||
for (i = 0; i < MAX_LOOKUP_COMPONENTS; i++) {
|
||||
args->getattr[i].attr_request = &args->attr_request;
|
||||
res->file[i].path = path;
|
||||
args->lookup[i].name = &res->file[i].name;
|
||||
res->getfh[i].fh = &res->file[i].fh;
|
||||
res->getattr[i].info = &res->info[i];
|
||||
res->getattr[i].obj_attributes.attr_vals_len = NFS4_OPAQUE_LIMIT;
|
||||
}
|
||||
}
|
||||
|
||||
static int lookup_rpc(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *dir,
|
||||
IN uint32_t component_count,
|
||||
IN nfs41_lookup_component_args *args,
|
||||
OUT nfs41_lookup_component_res *res)
|
||||
{
|
||||
int status;
|
||||
uint32_t i;
|
||||
nfs41_compound compound;
|
||||
nfs_argop4 argops[4+MAX_LOOKUP_COMPONENTS*3];
|
||||
nfs_resop4 resops[4+MAX_LOOKUP_COMPONENTS*3];
|
||||
|
||||
compound_init(&compound, argops, resops, "lookup");
|
||||
|
||||
compound_add_op(&compound, OP_SEQUENCE, &args->sequence, &res->sequence);
|
||||
nfs41_session_sequence(&args->sequence, session, 0);
|
||||
|
||||
if (dir == &res->root) {
|
||||
compound_add_op(&compound, OP_PUTROOTFH, NULL, &res->putfh);
|
||||
compound_add_op(&compound, OP_GETFH, NULL, &res->getrootfh);
|
||||
compound_add_op(&compound, OP_GETATTR, &args->getrootattr,
|
||||
&res->getrootattr);
|
||||
} else {
|
||||
args->putfh.file = dir;
|
||||
compound_add_op(&compound, OP_PUTFH, &args->putfh, &res->putfh);
|
||||
}
|
||||
|
||||
for (i = 0; i < component_count; i++) {
|
||||
compound_add_op(&compound, OP_LOOKUP, &args->lookup[i], &res->lookup[i]);
|
||||
compound_add_op(&compound, OP_GETFH, NULL, &res->getfh[i]);
|
||||
compound_add_op(&compound, OP_GETATTR, &args->getattr[i],
|
||||
&res->getattr[i]);
|
||||
}
|
||||
|
||||
status = compound_encode_send_decode(session, &compound, TRUE);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
compound_error(status = compound.res.status);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int map_lookup_error(int status, bool_t last_component)
|
||||
{
|
||||
switch (status) {
|
||||
case NFS4ERR_NOENT:
|
||||
if (last_component) return ERROR_FILE_NOT_FOUND;
|
||||
else return ERROR_PATH_NOT_FOUND;
|
||||
case NFS4ERR_SYMLINK: return ERROR_REPARSE;
|
||||
case NFS4ERR_MOVED: return ERROR_FILESYSTEM_ABSENT;
|
||||
default: return nfs_to_windows_error(status, ERROR_FILE_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
static int server_lookup(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *dir,
|
||||
IN const char *path,
|
||||
IN const char *path_end,
|
||||
IN uint32_t count,
|
||||
IN nfs41_lookup_component_args *args,
|
||||
IN nfs41_lookup_component_res *res,
|
||||
OUT OPTIONAL nfs41_path_fh **parent_out,
|
||||
OUT OPTIONAL nfs41_path_fh **target_out,
|
||||
OUT OPTIONAL nfs41_file_info *info_out)
|
||||
{
|
||||
nfs41_path_fh *file, *parent;
|
||||
uint32_t i = 0;
|
||||
int status;
|
||||
|
||||
if (parent_out) *parent_out = NULL;
|
||||
if (target_out) *target_out = NULL;
|
||||
|
||||
lookup_rpc(session, dir, count, args, res);
|
||||
|
||||
status = res->sequence.sr_status; if (status) goto out;
|
||||
status = res->putfh.status; if (status) goto out;
|
||||
status = res->getrootfh.status; if (status) goto out;
|
||||
status = res->getrootattr.status; if (status) goto out;
|
||||
|
||||
if (dir == &res->root) {
|
||||
nfs41_component name = { 0 };
|
||||
|
||||
/* fill in the file handle's fileid and superblock */
|
||||
dir->fh.fileid = res->getrootattr.info->fileid;
|
||||
status = nfs41_superblock_for_fh(session,
|
||||
&res->getrootattr.info->fsid, NULL, dir);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
/* get the name of the parent (empty if its the root) */
|
||||
last_component(path, count ? args->lookup[0].name->name : path_end, &name);
|
||||
|
||||
/* add the file handle and attributes to the name cache */
|
||||
memcpy(&res->getrootattr.info->attrmask,
|
||||
&res->getrootattr.obj_attributes.attrmask, sizeof(bitmap4));
|
||||
nfs41_name_cache_insert(session_name_cache(session), path, &name,
|
||||
&dir->fh, res->getrootattr.info, NULL, OPEN_DELEGATE_NONE);
|
||||
}
|
||||
file = dir;
|
||||
|
||||
if (count == 0) {
|
||||
if (target_out)
|
||||
*target_out = dir;
|
||||
if (info_out)
|
||||
memcpy(info_out, res->getrootattr.info, sizeof(nfs41_file_info));
|
||||
} else if (count == 1) {
|
||||
if (parent_out)
|
||||
*parent_out = dir;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (res->lookup[i].status == NFS4ERR_SYMLINK) {
|
||||
/* return the symlink as the parent file */
|
||||
last_component(path, args->lookup[i].name->name, &file->name);
|
||||
if (parent_out) *parent_out = file;
|
||||
} else if (res->lookup[i].status == NFS4ERR_NOENT) {
|
||||
/* insert a negative lookup entry */
|
||||
nfs41_name_cache_insert(session_name_cache(session), path,
|
||||
args->lookup[i].name, NULL, NULL, NULL, OPEN_DELEGATE_NONE);
|
||||
}
|
||||
status = res->lookup[i].status; if (status) break;
|
||||
|
||||
if (res->getfh[i].status == NFS4ERR_MOVED) {
|
||||
/* save enough information to follow the referral */
|
||||
path_fh_copy(&res->referral->parent, file);
|
||||
res->referral->name.name = args->lookup[i].name->name;
|
||||
res->referral->name.len = args->lookup[i].name->len;
|
||||
}
|
||||
status = res->getfh[i].status; if (status) break;
|
||||
status = res->getattr[i].status; if (status) break;
|
||||
|
||||
parent = file;
|
||||
file = &res->file[i];
|
||||
|
||||
/* fill in the file handle's fileid and superblock */
|
||||
file->fh.fileid = res->getattr[i].info->fileid;
|
||||
status = nfs41_superblock_for_fh(session,
|
||||
&res->getattr[i].info->fsid, &parent->fh, file);
|
||||
if (status)
|
||||
break;
|
||||
|
||||
/* add the file handle and attributes to the name cache */
|
||||
memcpy(&res->getattr[i].info->attrmask,
|
||||
&res->getattr[i].obj_attributes.attrmask, sizeof(bitmap4));
|
||||
nfs41_name_cache_insert(session_name_cache(session),
|
||||
path, args->lookup[i].name, &res->file[i].fh,
|
||||
res->getattr[i].info, NULL, OPEN_DELEGATE_NONE);
|
||||
|
||||
if (i == count-1) {
|
||||
if (target_out)
|
||||
*target_out = file;
|
||||
if (info_out)
|
||||
memcpy(info_out, res->getattr[i].info, sizeof(nfs41_file_info));
|
||||
} else if (i == count-2) {
|
||||
if (parent_out)
|
||||
*parent_out = file;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return map_lookup_error(status, i == count-1);
|
||||
}
|
||||
|
||||
static uint32_t max_lookup_components(
|
||||
IN const nfs41_session *session)
|
||||
{
|
||||
const uint32_t comps = (session->fore_chan_attrs.ca_maxoperations - 4) / 3;
|
||||
return min(comps, MAX_LOOKUP_COMPONENTS);
|
||||
}
|
||||
|
||||
static uint32_t get_component_array(
|
||||
IN OUT const char **path_pos,
|
||||
IN const char *path_end,
|
||||
IN uint32_t max_components,
|
||||
OUT nfs41_path_fh *components,
|
||||
OUT uint32_t *component_count)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < max_components; i++) {
|
||||
if (!next_component(*path_pos, path_end, &components[i].name))
|
||||
break;
|
||||
*path_pos = components[i].name.name + components[i].name.len;
|
||||
}
|
||||
|
||||
*component_count = i;
|
||||
return i;
|
||||
}
|
||||
|
||||
static int server_lookup_loop(
|
||||
IN nfs41_session *session,
|
||||
IN OPTIONAL nfs41_path_fh *parent_in,
|
||||
IN nfs41_abs_path *path,
|
||||
IN const char *path_pos,
|
||||
IN struct lookup_referral *referral,
|
||||
OUT OPTIONAL nfs41_path_fh *parent_out,
|
||||
OUT OPTIONAL nfs41_path_fh *target_out,
|
||||
OUT OPTIONAL nfs41_file_info *info_out)
|
||||
{
|
||||
nfs41_lookup_component_args args = { 0 };
|
||||
nfs41_lookup_component_res res = { 0 };
|
||||
nfs41_path_fh *dir, *parent, *target;
|
||||
const char *path_end;
|
||||
const uint32_t max_components = max_lookup_components(session);
|
||||
uint32_t count;
|
||||
int status = NO_ERROR;
|
||||
|
||||
init_component_args(&args, &res, path, referral);
|
||||
parent = NULL;
|
||||
target = NULL;
|
||||
|
||||
path_end = path->path + path->len;
|
||||
dir = parent_in ? parent_in : &res.root;
|
||||
|
||||
while (get_component_array(&path_pos, path_end,
|
||||
max_components, res.file, &count)) {
|
||||
|
||||
status = server_lookup(session, dir, path->path, path_end, count,
|
||||
&args, &res, &parent, &target, info_out);
|
||||
|
||||
if (status == ERROR_REPARSE) {
|
||||
/* copy the component name of the symlink */
|
||||
if (parent_out && parent) {
|
||||
const ptrdiff_t offset = parent->name.name - path->path;
|
||||
parent_out->name.name = parent_out->path->path + offset;
|
||||
parent_out->name.len = parent->name.len;
|
||||
}
|
||||
goto out_parent;
|
||||
}
|
||||
if (status == ERROR_FILE_NOT_FOUND && is_last_component(path_pos, path_end))
|
||||
goto out_parent;
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
dir = target;
|
||||
}
|
||||
|
||||
if (dir == &res.root && (target_out || info_out)) {
|
||||
/* didn't get any components, so we just need the root */
|
||||
status = server_lookup(session, dir, path->path, path_end,
|
||||
0, &args, &res, &parent, &target, info_out);
|
||||
if (status)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (target_out && target) fh_copy(&target_out->fh, &target->fh);
|
||||
out_parent:
|
||||
if (parent_out && parent) fh_copy(&parent_out->fh, &parent->fh);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static void referral_locations_free(
|
||||
IN fs_locations4 *locations)
|
||||
{
|
||||
uint32_t i;
|
||||
if (locations->locations) {
|
||||
for (i = 0; i < locations->location_count; i++)
|
||||
free(locations->locations[i].servers);
|
||||
free(locations->locations);
|
||||
}
|
||||
}
|
||||
|
||||
static int referral_resolve(
|
||||
IN nfs41_root *root,
|
||||
IN nfs41_session *session_in,
|
||||
IN struct lookup_referral *referral,
|
||||
OUT nfs41_abs_path *path_out,
|
||||
OUT nfs41_session **session_out)
|
||||
{
|
||||
char rest_of_path[NFS41_MAX_PATH_LEN];
|
||||
fs_locations4 locations = { 0 };
|
||||
const fs_location4 *location;
|
||||
nfs41_client *client;
|
||||
int status;
|
||||
|
||||
/* get fs_locations */
|
||||
status = nfs41_fs_locations(session_in, &referral->parent,
|
||||
&referral->name, &locations);
|
||||
if (status) {
|
||||
eprintf("nfs41_fs_locations() failed with %s\n",
|
||||
nfs_error_string(status));
|
||||
status = nfs_to_windows_error(status, ERROR_PATH_NOT_FOUND);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* mount the first location available */
|
||||
status = nfs41_root_mount_referral(root, &locations, &location, &client);
|
||||
if (status) {
|
||||
eprintf("nfs41_root_mount_referral() failed with %d\n",
|
||||
status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* format a new path from that location's root */
|
||||
if (FAILED(StringCchCopyA(rest_of_path, NFS41_MAX_PATH_LEN,
|
||||
referral->name.name + referral->name.len))) {
|
||||
status = ERROR_FILENAME_EXCED_RANGE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
AcquireSRWLockExclusive(&path_out->lock);
|
||||
abs_path_copy(path_out, &location->path);
|
||||
if (FAILED(StringCchCatA(path_out->path, NFS41_MAX_PATH_LEN, rest_of_path)))
|
||||
status = ERROR_FILENAME_EXCED_RANGE;
|
||||
path_out->len = path_out->len + (unsigned short)strlen(rest_of_path);
|
||||
ReleaseSRWLockExclusive(&path_out->lock);
|
||||
|
||||
if (session_out) *session_out = client->session;
|
||||
out:
|
||||
referral_locations_free(&locations);
|
||||
return status;
|
||||
}
|
||||
|
||||
int nfs41_lookup(
|
||||
IN nfs41_root *root,
|
||||
IN nfs41_session *session,
|
||||
IN OUT nfs41_abs_path *path_inout,
|
||||
OUT OPTIONAL nfs41_path_fh *parent_out,
|
||||
OUT OPTIONAL nfs41_path_fh *target_out,
|
||||
OUT OPTIONAL nfs41_file_info *info_out,
|
||||
OUT nfs41_session **session_out)
|
||||
{
|
||||
nfs41_abs_path path;
|
||||
struct nfs41_name_cache *cache = session_name_cache(session);
|
||||
nfs41_path_fh parent, target, *server_start;
|
||||
const char *path_pos, *path_end;
|
||||
struct lookup_referral referral;
|
||||
bool_t negative = 0;
|
||||
int status;
|
||||
|
||||
if (session_out) *session_out = session;
|
||||
|
||||
InitializeSRWLock(&path.lock);
|
||||
|
||||
/* to avoid holding this lock over multiple rpcs,
|
||||
* make a copy of the path and use that instead */
|
||||
AcquireSRWLockShared(&path_inout->lock);
|
||||
abs_path_copy(&path, path_inout);
|
||||
ReleaseSRWLockShared(&path_inout->lock);
|
||||
|
||||
path_pos = path.path;
|
||||
path_end = path.path + path.len;
|
||||
|
||||
dprintf(LULVL, "--> nfs41_lookup('%s')\n", path.path);
|
||||
|
||||
if (parent_out == NULL) parent_out = &parent;
|
||||
if (target_out == NULL) target_out = ⌖
|
||||
parent_out->fh.len = target_out->fh.len = 0;
|
||||
|
||||
status = nfs41_name_cache_lookup(cache, path_pos, path_end, &path_pos,
|
||||
&parent_out->fh, &target_out->fh, info_out, &negative);
|
||||
if (status == NO_ERROR || negative)
|
||||
goto out;
|
||||
|
||||
if (parent_out->fh.len) {
|
||||
/* start where the name cache left off */
|
||||
if (&parent != parent_out) {
|
||||
/* must make a copy for server_start, because
|
||||
* server_lookup_loop() will overwrite parent_out */
|
||||
path_fh_copy(&parent, parent_out);
|
||||
}
|
||||
server_start = &parent;
|
||||
} else {
|
||||
/* start with PUTROOTFH */
|
||||
server_start = NULL;
|
||||
}
|
||||
|
||||
status = server_lookup_loop(session, server_start,
|
||||
&path, path_pos, &referral, parent_out, target_out, info_out);
|
||||
|
||||
if (status == ERROR_FILESYSTEM_ABSENT) {
|
||||
nfs41_session *new_session;
|
||||
|
||||
/* create a session to the referred server and
|
||||
* reformat the path relative to that server's root */
|
||||
status = referral_resolve(root, session,
|
||||
&referral, path_inout, &new_session);
|
||||
if (status) {
|
||||
eprintf("referral_resolve() failed with %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* update the positions of the parent and target components */
|
||||
last_component(path_inout->path, path_inout->path + path_inout->len,
|
||||
&target_out->name);
|
||||
last_component(path_inout->path, target_out->name.name,
|
||||
&parent_out->name);
|
||||
|
||||
if (session_out) *session_out = new_session;
|
||||
|
||||
/* look up the new path */
|
||||
status = nfs41_lookup(root, new_session, path_inout,
|
||||
parent_out, target_out, info_out, session_out);
|
||||
}
|
||||
out:
|
||||
dprintf(LULVL, "<-- nfs41_lookup() returning %d\n", status);
|
||||
return status;
|
||||
}
|
8
reactos/base/services/nfsd/makefile
Normal file
8
reactos/base/services/nfsd/makefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
#
|
||||
# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
|
||||
# file to this component. This file merely indirects to the real make file
|
||||
# that is shared by all the driver components of the Windows NT DDK
|
||||
#
|
||||
|
||||
!INCLUDE $(NTMAKEENV)\makefile.def
|
||||
|
176
reactos/base/services/nfsd/mount.c
Normal file
176
reactos/base/services/nfsd/mount.c
Normal file
|
@ -0,0 +1,176 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "daemon_debug.h"
|
||||
#include "nfs41_ops.h"
|
||||
#include "upcall.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
/* NFS41_MOUNT */
|
||||
static int parse_mount(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
mount_upcall_args *args = &upcall->args.mount;
|
||||
|
||||
status = get_name(&buffer, &length, &args->hostname);
|
||||
if(status) goto out;
|
||||
status = get_name(&buffer, &length, &args->path);
|
||||
if(status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->sec_flavor, sizeof(DWORD));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->rsize, sizeof(DWORD));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->wsize, sizeof(DWORD));
|
||||
if (status) goto out;
|
||||
|
||||
dprintf(1, "parsing NFS14_MOUNT: srv_name=%s root=%s sec_flavor=%s "
|
||||
"rsize=%d wsize=%d\n", args->hostname, args->path,
|
||||
secflavorop2name(args->sec_flavor), args->rsize, args->wsize);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int handle_mount(nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
mount_upcall_args *args = &upcall->args.mount;
|
||||
nfs41_abs_path path;
|
||||
multi_addr4 addrs;
|
||||
nfs41_root *root;
|
||||
nfs41_client *client;
|
||||
nfs41_path_fh file;
|
||||
|
||||
// resolve hostname,port
|
||||
status = nfs41_server_resolve(args->hostname, 2049, &addrs);
|
||||
if (status) {
|
||||
eprintf("nfs41_server_resolve() failed with %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (upcall->root_ref != INVALID_HANDLE_VALUE) {
|
||||
/* use an existing root from a previous mount, but don't take an
|
||||
* extra reference; we'll only get one UNMOUNT upcall for each root */
|
||||
root = upcall->root_ref;
|
||||
} else {
|
||||
// create root
|
||||
status = nfs41_root_create(args->hostname, args->sec_flavor,
|
||||
args->wsize + WRITE_OVERHEAD, args->rsize + READ_OVERHEAD, &root);
|
||||
if (status) {
|
||||
eprintf("nfs41_root_create() failed %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
root->uid = upcall->uid;
|
||||
root->gid = upcall->gid;
|
||||
}
|
||||
|
||||
// find or create the client/session
|
||||
status = nfs41_root_mount_addrs(root, &addrs, 0, 0, &client);
|
||||
if (status) {
|
||||
eprintf("nfs41_root_mount_addrs() failed with %d\n", status);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
// make a copy of the path for nfs41_lookup()
|
||||
InitializeSRWLock(&path.lock);
|
||||
if (FAILED(StringCchCopyA(path.path, NFS41_MAX_PATH_LEN, args->path))) {
|
||||
status = ERROR_FILENAME_EXCED_RANGE;
|
||||
goto out_err;
|
||||
}
|
||||
path.len = (unsigned short)strlen(path.path);
|
||||
|
||||
// look up the mount path, and fail if it doesn't exist
|
||||
status = nfs41_lookup(root, client->session,
|
||||
&path, NULL, &file, NULL, NULL);
|
||||
if (status) {
|
||||
eprintf("nfs41_lookup('%s') failed with %d\n", path.path, status);
|
||||
status = ERROR_BAD_NETPATH;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
nfs41_superblock_fs_attributes(file.fh.superblock, &args->FsAttrs);
|
||||
|
||||
if (upcall->root_ref == INVALID_HANDLE_VALUE)
|
||||
nfs41_root_ref(root);
|
||||
upcall->root_ref = root;
|
||||
args->lease_time = client->session->lease_time;
|
||||
out:
|
||||
return status;
|
||||
|
||||
out_err:
|
||||
if (upcall->root_ref == INVALID_HANDLE_VALUE)
|
||||
nfs41_root_deref(root);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int marshall_mount(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
|
||||
{
|
||||
mount_upcall_args *args = &upcall->args.mount;
|
||||
int status;
|
||||
dprintf(2, "NFS41_MOUNT: writing pointer to nfs41_root %p, version %d, "
|
||||
"lease_time %d\n", upcall->root_ref, NFS41D_VERSION, args->lease_time);
|
||||
status = safe_write(&buffer, length, &upcall->root_ref, sizeof(HANDLE));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &NFS41D_VERSION, sizeof(DWORD));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &args->lease_time, sizeof(DWORD));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &args->FsAttrs, sizeof(args->FsAttrs));
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static void cancel_mount(IN nfs41_upcall *upcall)
|
||||
{
|
||||
if (upcall->root_ref != INVALID_HANDLE_VALUE)
|
||||
nfs41_root_deref(upcall->root_ref);
|
||||
}
|
||||
|
||||
const nfs41_upcall_op nfs41_op_mount = {
|
||||
parse_mount,
|
||||
handle_mount,
|
||||
marshall_mount,
|
||||
cancel_mount
|
||||
};
|
||||
|
||||
|
||||
/* NFS41_UNMOUNT */
|
||||
static int parse_unmount(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
||||
{
|
||||
dprintf(1, "parsing NFS41_UNMOUNT: root=%p\n", upcall->root_ref);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int handle_unmount(nfs41_upcall *upcall)
|
||||
{
|
||||
/* release the original reference from nfs41_root_create() */
|
||||
nfs41_root_deref(upcall->root_ref);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
const nfs41_upcall_op nfs41_op_unmount = {
|
||||
parse_unmount,
|
||||
handle_unmount
|
||||
};
|
20
reactos/base/services/nfsd/ms-nfs41-idmap.conf
Normal file
20
reactos/base/services/nfsd/ms-nfs41-idmap.conf
Normal file
|
@ -0,0 +1,20 @@
|
|||
# ldap server information
|
||||
#ldap_hostname="localhost"
|
||||
#ldap_port="389"
|
||||
#ldap_version="3"
|
||||
#ldap_timeout="5"
|
||||
|
||||
# ldap schema information
|
||||
#ldap_base="cn=localhost"
|
||||
|
||||
#ldap_class_users="user"
|
||||
#ldap_class_groups="group"
|
||||
|
||||
#ldap_attr_username="cn"
|
||||
#ldap_attr_groupname="cn"
|
||||
#ldap_attr_gssAuthName="gssAuthName"
|
||||
#ldap_attr_uidNumber="uidNumber"
|
||||
#ldap_attr_gidNumber="gidNumber"
|
||||
|
||||
# caching configuration
|
||||
#cache_ttl="60"
|
1399
reactos/base/services/nfsd/name_cache.c
Normal file
1399
reactos/base/services/nfsd/name_cache.c
Normal file
File diff suppressed because it is too large
Load diff
106
reactos/base/services/nfsd/name_cache.h
Normal file
106
reactos/base/services/nfsd/name_cache.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#ifndef __NFS41_DAEMON_NAME_CACHE_H__
|
||||
#define __NFS41_DAEMON_NAME_CACHE_H__
|
||||
|
||||
#include "nfs41.h"
|
||||
|
||||
|
||||
static __inline struct nfs41_name_cache* client_name_cache(
|
||||
IN nfs41_client *client)
|
||||
{
|
||||
return client_server(client)->name_cache;
|
||||
}
|
||||
|
||||
static __inline struct nfs41_name_cache* session_name_cache(
|
||||
IN nfs41_session *session)
|
||||
{
|
||||
return client_name_cache(session->client);
|
||||
}
|
||||
|
||||
|
||||
/* attribute cache */
|
||||
int nfs41_attr_cache_lookup(
|
||||
IN struct nfs41_name_cache *cache,
|
||||
IN uint64_t fileid,
|
||||
OUT nfs41_file_info *info_out);
|
||||
|
||||
int nfs41_attr_cache_update(
|
||||
IN struct nfs41_name_cache *cache,
|
||||
IN uint64_t fileid,
|
||||
IN const nfs41_file_info *info);
|
||||
|
||||
|
||||
/* name cache */
|
||||
int nfs41_name_cache_create(
|
||||
OUT struct nfs41_name_cache **cache_out);
|
||||
|
||||
int nfs41_name_cache_free(
|
||||
IN OUT struct nfs41_name_cache **cache_out);
|
||||
|
||||
int nfs41_name_cache_lookup(
|
||||
IN struct nfs41_name_cache *cache,
|
||||
IN const char *path,
|
||||
IN const char *path_end,
|
||||
OUT OPTIONAL const char **remaining_path_out,
|
||||
OUT OPTIONAL nfs41_fh *parent_out,
|
||||
OUT OPTIONAL nfs41_fh *target_out,
|
||||
OUT OPTIONAL nfs41_file_info *info_out,
|
||||
OUT OPTIONAL bool_t *is_negative);
|
||||
|
||||
int nfs41_name_cache_insert(
|
||||
IN struct nfs41_name_cache *cache,
|
||||
IN const char *path,
|
||||
IN const nfs41_component *name,
|
||||
IN OPTIONAL const nfs41_fh *fh,
|
||||
IN OPTIONAL const nfs41_file_info *info,
|
||||
IN OPTIONAL const change_info4 *cinfo,
|
||||
IN enum open_delegation_type4 delegation);
|
||||
|
||||
int nfs41_name_cache_delegreturn(
|
||||
IN struct nfs41_name_cache *cache,
|
||||
IN uint64_t fileid,
|
||||
IN const char *path,
|
||||
IN const nfs41_component *name);
|
||||
|
||||
int nfs41_name_cache_remove(
|
||||
IN struct nfs41_name_cache *cache,
|
||||
IN const char *path,
|
||||
IN const nfs41_component *name,
|
||||
IN uint64_t fileid,
|
||||
IN const change_info4 *cinfo);
|
||||
|
||||
int nfs41_name_cache_rename(
|
||||
IN struct nfs41_name_cache *cache,
|
||||
IN const char *src_path,
|
||||
IN const nfs41_component *src_name,
|
||||
IN const change_info4 *src_cinfo,
|
||||
IN const char *dst_path,
|
||||
IN const nfs41_component *dst_name,
|
||||
IN const change_info4 *dst_cinfo);
|
||||
|
||||
int nfs41_name_cache_remove_stale(
|
||||
IN struct nfs41_name_cache *cache,
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_abs_path *path);
|
||||
|
||||
#endif /* !__NFS41_DAEMON_NAME_CACHE_H__ */
|
478
reactos/base/services/nfsd/namespace.c
Normal file
478
reactos/base/services/nfsd/namespace.c
Normal file
|
@ -0,0 +1,478 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#include "nfs41_ops.h"
|
||||
#include "util.h"
|
||||
#include "daemon_debug.h"
|
||||
|
||||
|
||||
#define NSLVL 2 /* dprintf level for namespace logging */
|
||||
|
||||
|
||||
#define client_entry(pos) list_container(pos, nfs41_client, root_entry)
|
||||
|
||||
|
||||
/* nfs41_root */
|
||||
int nfs41_root_create(
|
||||
IN const char *name,
|
||||
IN uint32_t sec_flavor,
|
||||
IN uint32_t wsize,
|
||||
IN uint32_t rsize,
|
||||
OUT nfs41_root **root_out)
|
||||
{
|
||||
int status = NO_ERROR;
|
||||
nfs41_root *root;
|
||||
|
||||
dprintf(NSLVL, "--> nfs41_root_create()\n");
|
||||
|
||||
root = calloc(1, sizeof(nfs41_root));
|
||||
if (root == NULL) {
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_init(&root->clients);
|
||||
root->wsize = wsize;
|
||||
root->rsize = rsize;
|
||||
InitializeCriticalSection(&root->lock);
|
||||
root->ref_count = 1;
|
||||
root->sec_flavor = sec_flavor;
|
||||
|
||||
/* generate a unique client_owner */
|
||||
status = nfs41_client_owner(name, sec_flavor, &root->client_owner);
|
||||
if (status) {
|
||||
eprintf("nfs41_client_owner() failed with %d\n", status);
|
||||
free(root);
|
||||
goto out;
|
||||
}
|
||||
|
||||
*root_out = root;
|
||||
out:
|
||||
dprintf(NSLVL, "<-- nfs41_root_create() returning %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void root_free(
|
||||
IN nfs41_root *root)
|
||||
{
|
||||
struct list_entry *entry, *tmp;
|
||||
|
||||
dprintf(NSLVL, "--> nfs41_root_free()\n");
|
||||
|
||||
/* free clients */
|
||||
list_for_each_tmp(entry, tmp, &root->clients)
|
||||
nfs41_client_free(client_entry(entry));
|
||||
DeleteCriticalSection(&root->lock);
|
||||
free(root);
|
||||
|
||||
dprintf(NSLVL, "<-- nfs41_root_free()\n");
|
||||
}
|
||||
|
||||
void nfs41_root_ref(
|
||||
IN nfs41_root *root)
|
||||
{
|
||||
const LONG count = InterlockedIncrement(&root->ref_count);
|
||||
|
||||
dprintf(NSLVL, "nfs41_root_ref() count %d\n", count);
|
||||
}
|
||||
|
||||
void nfs41_root_deref(
|
||||
IN nfs41_root *root)
|
||||
{
|
||||
const LONG count = InterlockedDecrement(&root->ref_count);
|
||||
|
||||
dprintf(NSLVL, "nfs41_root_deref() count %d\n", count);
|
||||
if (count == 0)
|
||||
root_free(root);
|
||||
}
|
||||
|
||||
|
||||
/* root_client_find_addrs() */
|
||||
struct cl_addr_info {
|
||||
const multi_addr4 *addrs;
|
||||
uint32_t roles;
|
||||
};
|
||||
|
||||
static int cl_addr_compare(
|
||||
IN const struct list_entry *entry,
|
||||
IN const void *value)
|
||||
{
|
||||
nfs41_client *client = client_entry(entry);
|
||||
const struct cl_addr_info *info = (const struct cl_addr_info*)value;
|
||||
uint32_t i, roles;
|
||||
|
||||
/* match any of the desired roles */
|
||||
AcquireSRWLockShared(&client->exid_lock);
|
||||
roles = info->roles & client->roles;
|
||||
ReleaseSRWLockShared(&client->exid_lock);
|
||||
|
||||
if (roles == 0)
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
|
||||
/* match any address in 'addrs' with any address in client->rpc->addrs */
|
||||
for (i = 0; i < info->addrs->count; i++)
|
||||
if (multi_addr_find(&client->rpc->addrs, &info->addrs->arr[i], NULL))
|
||||
return NO_ERROR;
|
||||
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
static int root_client_find_addrs(
|
||||
IN nfs41_root *root,
|
||||
IN const multi_addr4 *addrs,
|
||||
IN bool_t is_data,
|
||||
OUT nfs41_client **client_out)
|
||||
{
|
||||
struct cl_addr_info info;
|
||||
struct list_entry *entry;
|
||||
int status;
|
||||
|
||||
dprintf(NSLVL, "--> root_client_find_addrs()\n");
|
||||
|
||||
info.addrs = addrs;
|
||||
info.roles = nfs41_exchange_id_flags(is_data) & EXCHGID4_FLAG_MASK_PNFS;
|
||||
|
||||
entry = list_search(&root->clients, &info, cl_addr_compare);
|
||||
if (entry) {
|
||||
*client_out = client_entry(entry);
|
||||
status = NO_ERROR;
|
||||
dprintf(NSLVL, "<-- root_client_find_addrs() returning 0x%p\n",
|
||||
*client_out);
|
||||
} else {
|
||||
status = ERROR_FILE_NOT_FOUND;
|
||||
dprintf(NSLVL, "<-- root_client_find_addrs() failed with %d\n",
|
||||
status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/* root_client_find() */
|
||||
struct cl_exid_info {
|
||||
const nfs41_exchange_id_res *exchangeid;
|
||||
uint32_t roles;
|
||||
};
|
||||
|
||||
static int cl_exid_compare(
|
||||
IN const struct list_entry *entry,
|
||||
IN const void *value)
|
||||
{
|
||||
nfs41_client *client = client_entry(entry);
|
||||
const struct cl_exid_info *info = (const struct cl_exid_info*)value;
|
||||
int status = ERROR_FILE_NOT_FOUND;
|
||||
|
||||
AcquireSRWLockShared(&client->exid_lock);
|
||||
|
||||
/* match any of the desired roles */
|
||||
if ((info->roles & client->roles) == 0)
|
||||
goto out;
|
||||
/* match server_owner.major_id */
|
||||
if (strncmp(info->exchangeid->server_owner.so_major_id,
|
||||
client->server->owner, NFS4_OPAQUE_LIMIT) != 0)
|
||||
goto out;
|
||||
/* match server_scope */
|
||||
if (strncmp(info->exchangeid->server_scope,
|
||||
client->server->scope, NFS4_OPAQUE_LIMIT) != 0)
|
||||
goto out;
|
||||
/* match clientid */
|
||||
if (info->exchangeid->clientid != client->clnt_id)
|
||||
goto out;
|
||||
|
||||
status = NO_ERROR;
|
||||
out:
|
||||
ReleaseSRWLockShared(&client->exid_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int root_client_find(
|
||||
IN nfs41_root *root,
|
||||
IN const nfs41_exchange_id_res *exchangeid,
|
||||
IN bool_t is_data,
|
||||
OUT nfs41_client **client_out)
|
||||
{
|
||||
struct cl_exid_info info;
|
||||
struct list_entry *entry;
|
||||
int status;
|
||||
|
||||
dprintf(NSLVL, "--> root_client_find()\n");
|
||||
|
||||
info.exchangeid = exchangeid;
|
||||
info.roles = nfs41_exchange_id_flags(is_data) & EXCHGID4_FLAG_MASK_PNFS;
|
||||
|
||||
entry = list_search(&root->clients, &info, cl_exid_compare);
|
||||
if (entry) {
|
||||
*client_out = client_entry(entry);
|
||||
status = NO_ERROR;
|
||||
dprintf(NSLVL, "<-- root_client_find() returning 0x%p\n",
|
||||
*client_out);
|
||||
} else {
|
||||
status = ERROR_FILE_NOT_FOUND;
|
||||
dprintf(NSLVL, "<-- root_client_find() failed with %d\n",
|
||||
status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int session_get_lease(
|
||||
IN nfs41_session *session,
|
||||
IN OPTIONAL uint32_t lease_time)
|
||||
{
|
||||
bool_t use_mds_lease;
|
||||
int status;
|
||||
|
||||
/* http://tools.ietf.org/html/rfc5661#section-13.1.1
|
||||
* 13.1.1. Sessions Considerations for Data Servers:
|
||||
* If the reply to EXCHANGE_ID has just the EXCHGID4_FLAG_USE_PNFS_DS role
|
||||
* set, then (as noted in Section 13.6) the client will not be able to
|
||||
* determine the data server's lease_time attribute because GETATTR will
|
||||
* not be permitted. Instead, the rule is that any time a client
|
||||
* receives a layout referring it to a data server that returns just the
|
||||
* EXCHGID4_FLAG_USE_PNFS_DS role, the client MAY assume that the
|
||||
* lease_time attribute from the metadata server that returned the
|
||||
* layout applies to the data server. */
|
||||
AcquireSRWLockShared(&session->client->exid_lock);
|
||||
use_mds_lease = session->client->roles == EXCHGID4_FLAG_USE_PNFS_DS;
|
||||
ReleaseSRWLockShared(&session->client->exid_lock);
|
||||
|
||||
if (!use_mds_lease) {
|
||||
/* the client is allowed to GETATTR, so query the lease_time */
|
||||
nfs41_file_info info = { 0 };
|
||||
bitmap4 attr_request = { 1, { FATTR4_WORD0_LEASE_TIME, 0, 0 } };
|
||||
|
||||
status = nfs41_getattr(session, NULL, &attr_request, &info);
|
||||
if (status) {
|
||||
eprintf("nfs41_getattr() failed with %s\n",
|
||||
nfs_error_string(status));
|
||||
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
|
||||
goto out;
|
||||
}
|
||||
lease_time = info.lease_time;
|
||||
}
|
||||
|
||||
status = nfs41_session_set_lease(session, lease_time);
|
||||
if (status) {
|
||||
eprintf("nfs41_session_set_lease() failed %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int root_client_create(
|
||||
IN nfs41_root *root,
|
||||
IN nfs41_rpc_clnt *rpc,
|
||||
IN bool_t is_data,
|
||||
IN OPTIONAL uint32_t lease_time,
|
||||
IN const nfs41_exchange_id_res *exchangeid,
|
||||
OUT nfs41_client **client_out)
|
||||
{
|
||||
nfs41_client *client;
|
||||
nfs41_session *session;
|
||||
int status;
|
||||
|
||||
/* create client (transfers ownership of rpc to client) */
|
||||
status = nfs41_client_create(rpc, &root->client_owner,
|
||||
is_data, exchangeid, &client);
|
||||
if (status) {
|
||||
eprintf("nfs41_client_create() failed with %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
client->root = root;
|
||||
rpc->client = client;
|
||||
|
||||
/* create session (and client takes ownership) */
|
||||
status = nfs41_session_create(client, &session);
|
||||
if (status) {
|
||||
eprintf("nfs41_session_create failed %d\n", status);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (!is_data) {
|
||||
/* send RECLAIM_COMPLETE, but don't fail on ERR_NOTSUPP */
|
||||
status = nfs41_reclaim_complete(session);
|
||||
if (status && status != NFS4ERR_NOTSUPP) {
|
||||
eprintf("nfs41_reclaim_complete() failed with %s\n",
|
||||
nfs_error_string(status));
|
||||
status = ERROR_BAD_NETPATH;
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
|
||||
/* get least time and start session renewal thread */
|
||||
status = session_get_lease(session, lease_time);
|
||||
if (status)
|
||||
goto out_err;
|
||||
|
||||
*client_out = client;
|
||||
out:
|
||||
return status;
|
||||
|
||||
out_err:
|
||||
nfs41_client_free(client);
|
||||
goto out;
|
||||
}
|
||||
|
||||
int nfs41_root_mount_addrs(
|
||||
IN nfs41_root *root,
|
||||
IN const multi_addr4 *addrs,
|
||||
IN bool_t is_data,
|
||||
IN OPTIONAL uint32_t lease_time,
|
||||
OUT nfs41_client **client_out)
|
||||
{
|
||||
nfs41_exchange_id_res exchangeid = { 0 };
|
||||
nfs41_rpc_clnt *rpc;
|
||||
nfs41_client *client, *existing;
|
||||
int status;
|
||||
|
||||
dprintf(NSLVL, "--> nfs41_root_mount_addrs()\n");
|
||||
|
||||
/* look for an existing client that matches the address and role */
|
||||
EnterCriticalSection(&root->lock);
|
||||
status = root_client_find_addrs(root, addrs, is_data, &client);
|
||||
LeaveCriticalSection(&root->lock);
|
||||
|
||||
if (status == NO_ERROR)
|
||||
goto out;
|
||||
|
||||
/* create an rpc client */
|
||||
status = nfs41_rpc_clnt_create(addrs, root->wsize, root->rsize,
|
||||
root->uid, root->gid, root->sec_flavor, &rpc);
|
||||
if (status) {
|
||||
eprintf("nfs41_rpc_clnt_create() failed %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* get a clientid with exchangeid */
|
||||
status = nfs41_exchange_id(rpc, &root->client_owner,
|
||||
nfs41_exchange_id_flags(is_data), &exchangeid);
|
||||
if (status) {
|
||||
eprintf("nfs41_exchange_id() failed %s\n", nfs_error_string(status));
|
||||
status = ERROR_BAD_NET_RESP;
|
||||
goto out_free_rpc;
|
||||
}
|
||||
|
||||
/* attempt to match existing clients by the exchangeid response */
|
||||
EnterCriticalSection(&root->lock);
|
||||
status = root_client_find(root, &exchangeid, is_data, &client);
|
||||
LeaveCriticalSection(&root->lock);
|
||||
|
||||
if (status == NO_ERROR)
|
||||
goto out_free_rpc;
|
||||
|
||||
/* create a client for this clientid */
|
||||
status = root_client_create(root, rpc, is_data,
|
||||
lease_time, &exchangeid, &client);
|
||||
if (status) {
|
||||
eprintf("nfs41_client_create() failed %d\n", status);
|
||||
/* root_client_create takes care of cleaning up
|
||||
* thus don't go to out_free_rpc */
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* because we don't hold the root's lock over session creation,
|
||||
* we could end up creating multiple clients with the same
|
||||
* server and roles */
|
||||
EnterCriticalSection(&root->lock);
|
||||
status = root_client_find(root, &exchangeid, is_data, &existing);
|
||||
|
||||
if (status) {
|
||||
dprintf(NSLVL, "caching new client 0x%p\n", client);
|
||||
|
||||
/* the client is not a duplicate, so add it to the list */
|
||||
list_add_tail(&root->clients, &client->root_entry);
|
||||
status = NO_ERROR;
|
||||
} else {
|
||||
dprintf(NSLVL, "created a duplicate client 0x%p! using "
|
||||
"existing client 0x%p instead\n", client, existing);
|
||||
|
||||
/* a matching client has been created in parallel, so free
|
||||
* the one we created and use the existing client instead */
|
||||
nfs41_client_free(client);
|
||||
client = existing;
|
||||
}
|
||||
LeaveCriticalSection(&root->lock);
|
||||
|
||||
out:
|
||||
if (status == NO_ERROR)
|
||||
*client_out = client;
|
||||
dprintf(NSLVL, "<-- nfs41_root_mount_addrs() returning %d\n", status);
|
||||
return status;
|
||||
|
||||
out_free_rpc:
|
||||
nfs41_rpc_clnt_free(rpc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
/* http://tools.ietf.org/html/rfc5661#section-11.9
|
||||
* 11.9. The Attribute fs_locations
|
||||
* An entry in the server array is a UTF-8 string and represents one of a
|
||||
* traditional DNS host name, IPv4 address, IPv6 address, or a zero-length
|
||||
* string. An IPv4 or IPv6 address is represented as a universal address
|
||||
* (see Section 3.3.9 and [15]), minus the netid, and either with or without
|
||||
* the trailing ".p1.p2" suffix that represents the port number. If the
|
||||
* suffix is omitted, then the default port, 2049, SHOULD be assumed. A
|
||||
* zero-length string SHOULD be used to indicate the current address being
|
||||
* used for the RPC call. */
|
||||
static int referral_mount_location(
|
||||
IN nfs41_root *root,
|
||||
IN const fs_location4 *loc,
|
||||
OUT nfs41_client **client_out)
|
||||
{
|
||||
multi_addr4 addrs;
|
||||
int status = ERROR_BAD_NET_NAME;
|
||||
uint32_t i;
|
||||
|
||||
/* create a client and session for the first available server */
|
||||
for (i = 0; i < loc->server_count; i++) {
|
||||
/* XXX: only deals with 'address' as a hostname with default port */
|
||||
status = nfs41_server_resolve(loc->servers[i].address, 2049, &addrs);
|
||||
if (status) continue;
|
||||
|
||||
status = nfs41_root_mount_addrs(root, &addrs, 0, 0, client_out);
|
||||
if (status == NO_ERROR)
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
int nfs41_root_mount_referral(
|
||||
IN nfs41_root *root,
|
||||
IN const fs_locations4 *locations,
|
||||
OUT const fs_location4 **loc_out,
|
||||
OUT nfs41_client **client_out)
|
||||
{
|
||||
int status = ERROR_BAD_NET_NAME;
|
||||
uint32_t i;
|
||||
|
||||
/* establish a mount to the first available location */
|
||||
for (i = 0; i < locations->location_count; i++) {
|
||||
status = referral_mount_location(root,
|
||||
&locations->locations[i], client_out);
|
||||
if (status == NO_ERROR) {
|
||||
*loc_out = &locations->locations[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
19
reactos/base/services/nfsd/netconfig
Normal file
19
reactos/base/services/nfsd/netconfig
Normal file
|
@ -0,0 +1,19 @@
|
|||
#
|
||||
# The network configuration file. This file is currently only used in
|
||||
# conjunction with the TI-RPC code in the libtirpc library.
|
||||
#
|
||||
# Entries consist of:
|
||||
#
|
||||
# <network_id> <semantics> <flags> <protofamily> <protoname> \
|
||||
# <device> <nametoaddr_libs>
|
||||
#
|
||||
# The <device> and <nametoaddr_libs> fields are always empty in this
|
||||
# implementation.
|
||||
#
|
||||
udp tpi_clts v inet udp - -
|
||||
tcp tpi_cots_ord v inet tcp - -
|
||||
udp6 tpi_clts v inet6 udp - -
|
||||
tcp6 tpi_cots_ord v inet6 tcp - -
|
||||
rawip tpi_raw - inet - - -
|
||||
local tpi_cots_ord - loopback - - -
|
||||
unix tpi_cots_ord - loopback - - -
|
532
reactos/base/services/nfsd/nfs41.h
Normal file
532
reactos/base/services/nfsd/nfs41.h
Normal file
|
@ -0,0 +1,532 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#ifndef __NFS41__
|
||||
#define __NFS41__
|
||||
|
||||
#include "util.h"
|
||||
#include "list.h"
|
||||
|
||||
|
||||
struct __nfs41_session;
|
||||
struct __nfs41_client;
|
||||
struct __rpc_client;
|
||||
struct __nfs41_root;
|
||||
|
||||
struct _FILE_GET_EA_INFORMATION;
|
||||
struct _FILE_FULL_EA_INFORMATION;
|
||||
|
||||
typedef struct __nfs41_superblock {
|
||||
nfs41_fsid fsid;
|
||||
struct list_entry entry; /* position in nfs41_server.superblocks */
|
||||
|
||||
bitmap4 supported_attrs;
|
||||
bitmap4 suppattr_exclcreat;
|
||||
bitmap4 default_getattr;
|
||||
|
||||
nfstime4 time_delta;
|
||||
uint64_t maxread;
|
||||
uint64_t maxwrite;
|
||||
|
||||
/* constant filesystem attributes */
|
||||
unsigned int layout_types : 3;
|
||||
unsigned int aclsupport : 3;
|
||||
unsigned int cansettime : 1;
|
||||
unsigned int link_support : 1;
|
||||
unsigned int symlink_support : 1;
|
||||
unsigned int ea_support : 1;
|
||||
unsigned int case_preserving : 1;
|
||||
unsigned int case_insensitive : 1;
|
||||
|
||||
/* variable filesystem attributes */
|
||||
uint64_t space_avail;
|
||||
uint64_t space_free;
|
||||
uint64_t space_total;
|
||||
time_t cache_expiration; /* applies to space_ attributes */
|
||||
|
||||
SRWLOCK lock;
|
||||
} nfs41_superblock;
|
||||
|
||||
typedef struct __nfs41_superblock_list {
|
||||
struct list_entry head;
|
||||
SRWLOCK lock;
|
||||
} nfs41_superblock_list;
|
||||
|
||||
struct server_addrs {
|
||||
multi_addr4 addrs; /* list of addrs we've used with this server */
|
||||
uint32_t next_index;
|
||||
SRWLOCK lock;
|
||||
};
|
||||
|
||||
typedef struct __nfs41_server {
|
||||
char scope[NFS4_OPAQUE_LIMIT]; /* server_scope from exchangeid */
|
||||
char owner[NFS4_OPAQUE_LIMIT]; /* server_owner.major_id from exchangeid */
|
||||
struct server_addrs addrs;
|
||||
nfs41_superblock_list superblocks;
|
||||
struct nfs41_name_cache *name_cache;
|
||||
struct list_entry entry; /* position in global server list */
|
||||
LONG ref_count;
|
||||
} nfs41_server;
|
||||
|
||||
enum delegation_status {
|
||||
DELEGATION_GRANTED,
|
||||
DELEGATION_RETURNING,
|
||||
DELEGATION_RETURNED,
|
||||
};
|
||||
|
||||
typedef struct __nfs41_delegation_state {
|
||||
open_delegation4 state;
|
||||
nfs41_abs_path path;
|
||||
nfs41_path_fh parent;
|
||||
nfs41_path_fh file;
|
||||
struct list_entry client_entry; /* entry in nfs41_client.delegations */
|
||||
LONG ref_count;
|
||||
|
||||
enum delegation_status status;
|
||||
SRWLOCK lock;
|
||||
CONDITION_VARIABLE cond;
|
||||
|
||||
bool_t revoked; /* for recovery, accessed under client.state.lock */
|
||||
|
||||
HANDLE srv_open; /* for rdbss cache invalidation */
|
||||
} nfs41_delegation_state;
|
||||
|
||||
typedef struct __nfs41_lock_state {
|
||||
struct list_entry open_entry; /* entry in nfs41_open_state.locks */
|
||||
uint64_t offset;
|
||||
uint64_t length;
|
||||
uint32_t exclusive : 1;
|
||||
uint32_t delegated : 1; /* whether or not there is state on the server */
|
||||
uint32_t id : 30;
|
||||
} nfs41_lock_state;
|
||||
|
||||
/* nfs41_open_state reference counting:
|
||||
* one reference is held implicitly by the driver (initialized to 1 on
|
||||
* OPEN and released on CLOSE). other references must be held during
|
||||
* upcalls to prevent a parallel CLOSE from freeing it prematurely. by
|
||||
* calling upcall_open_state_ref() when parsing the upcall, you are
|
||||
* guaranteed a matching dereference on upcall_cleanup() */
|
||||
typedef struct __nfs41_open_state {
|
||||
nfs41_abs_path path;
|
||||
nfs41_path_fh parent;
|
||||
nfs41_path_fh file;
|
||||
nfs41_readdir_cookie cookie;
|
||||
struct __nfs41_session *session;
|
||||
uint32_t type;
|
||||
bool_t do_close;
|
||||
stateid4 stateid;
|
||||
state_owner4 owner;
|
||||
struct __pnfs_layout_state *layout;
|
||||
struct list_entry client_entry; /* entry in nfs41_client.opens */
|
||||
SRWLOCK lock;
|
||||
LONG ref_count;
|
||||
uint32_t share_access;
|
||||
uint32_t share_deny;
|
||||
uint64_t pnfs_last_offset; /* for layoutcommit */
|
||||
|
||||
struct {
|
||||
nfs41_delegation_state *state;
|
||||
bool_t reclaim;
|
||||
CONDITION_VARIABLE cond;
|
||||
} delegation;
|
||||
|
||||
struct { /* list of open lock state for recovery */
|
||||
stateid4 stateid;
|
||||
struct list_entry list;
|
||||
uint32_t counter;
|
||||
CRITICAL_SECTION lock;
|
||||
} locks;
|
||||
|
||||
struct {
|
||||
struct _FILE_GET_EA_INFORMATION *list;
|
||||
uint32_t index;
|
||||
CRITICAL_SECTION lock;
|
||||
} ea;
|
||||
|
||||
HANDLE srv_open; /* for data cache invalidation */
|
||||
} nfs41_open_state;
|
||||
|
||||
typedef struct __nfs41_rpc_clnt {
|
||||
struct __rpc_client *rpc;
|
||||
SRWLOCK lock;
|
||||
HANDLE cond;
|
||||
struct __nfs41_client *client;
|
||||
multi_addr4 addrs;
|
||||
uint32_t addr_index; /* index of addr we're using */
|
||||
uint32_t wsize;
|
||||
uint32_t rsize;
|
||||
uint32_t version;
|
||||
uint32_t sec_flavor;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
char server_name[NI_MAXHOST];
|
||||
bool_t is_valid_session;
|
||||
bool_t in_recovery;
|
||||
bool_t needcb;
|
||||
} nfs41_rpc_clnt;
|
||||
|
||||
struct client_state {
|
||||
struct list_entry opens; /* list of associated nfs41_open_state */
|
||||
struct list_entry delegations; /* list of associated delegations */
|
||||
CRITICAL_SECTION lock;
|
||||
};
|
||||
|
||||
typedef struct __nfs41_client {
|
||||
nfs41_server *server;
|
||||
client_owner4 owner;
|
||||
uint64_t clnt_id;
|
||||
uint32_t seq_id;
|
||||
uint32_t roles;
|
||||
SRWLOCK exid_lock;
|
||||
struct __nfs41_session *session;
|
||||
SRWLOCK session_lock;
|
||||
nfs41_rpc_clnt *rpc;
|
||||
bool_t is_data;
|
||||
struct pnfs_layout_list *layouts;
|
||||
struct pnfs_file_device_list *devices;
|
||||
struct list_entry root_entry; /* position in nfs41_root.clients */
|
||||
struct __nfs41_root *root;
|
||||
|
||||
struct {
|
||||
CONDITION_VARIABLE cond;
|
||||
CRITICAL_SECTION lock;
|
||||
bool_t in_recovery;
|
||||
} recovery;
|
||||
|
||||
/* for state recovery on server reboot */
|
||||
struct client_state state;
|
||||
} nfs41_client;
|
||||
|
||||
#define NFS41_MAX_NUM_SLOTS NFS41_MAX_RPC_REQS
|
||||
typedef struct __nfs41_slot_table {
|
||||
uint32_t seq_nums[NFS41_MAX_NUM_SLOTS];
|
||||
uint32_t used_slots[NFS41_MAX_NUM_SLOTS];
|
||||
uint32_t max_slots;
|
||||
uint32_t highest_used;
|
||||
uint32_t num_used;
|
||||
ULONGLONG target_delay;
|
||||
CRITICAL_SECTION lock;
|
||||
CONDITION_VARIABLE cond;
|
||||
} nfs41_slot_table;
|
||||
|
||||
typedef struct __nfs41_channel_attrs {
|
||||
uint32_t ca_headerpadsize;
|
||||
uint32_t ca_maxrequestsize;
|
||||
uint32_t ca_maxresponsesize;
|
||||
uint32_t ca_maxresponsesize_cached;
|
||||
uint32_t ca_maxoperations;
|
||||
uint32_t ca_maxrequests;
|
||||
uint32_t *ca_rdma_ird;
|
||||
} nfs41_channel_attrs;
|
||||
|
||||
struct replay_cache {
|
||||
unsigned char buffer[NFS41_MAX_SERVER_CACHE];
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
typedef struct __nfs41_cb_session {
|
||||
struct {
|
||||
struct replay_cache arg;
|
||||
struct replay_cache res;
|
||||
} replay;
|
||||
const unsigned char *cb_sessionid; /* -> nfs41_session.session_id */
|
||||
uint32_t cb_seqnum;
|
||||
uint32_t cb_slotid;
|
||||
} nfs41_cb_session;
|
||||
|
||||
typedef struct __nfs41_session {
|
||||
nfs41_client *client;
|
||||
unsigned char session_id[NFS4_SESSIONID_SIZE];
|
||||
nfs41_channel_attrs fore_chan_attrs;
|
||||
nfs41_channel_attrs back_chan_attrs;
|
||||
uint32_t lease_time;
|
||||
nfs41_slot_table table;
|
||||
// array of slots
|
||||
HANDLE renew_thread;
|
||||
bool_t isValidState;
|
||||
uint32_t flags;
|
||||
nfs41_cb_session cb_session;
|
||||
} nfs41_session;
|
||||
|
||||
/* nfs41_root reference counting:
|
||||
* similar to nfs41_open_state, the driver holds an implicit reference
|
||||
* between MOUNT and UNMOUNT. all other upcalls use upcall_root_ref() on
|
||||
* upcall_parse(), which prevents the root/clients from being freed and
|
||||
* guarantees a matching deref on upcall_cleanup() */
|
||||
typedef struct __nfs41_root {
|
||||
client_owner4 client_owner;
|
||||
CRITICAL_SECTION lock;
|
||||
struct list_entry clients;
|
||||
uint32_t wsize;
|
||||
uint32_t rsize;
|
||||
LONG ref_count;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
DWORD sec_flavor;
|
||||
} nfs41_root;
|
||||
|
||||
|
||||
/* nfs41_namespace.c */
|
||||
int nfs41_root_create(
|
||||
IN const char *name,
|
||||
IN uint32_t sec_flavor,
|
||||
IN uint32_t wsize,
|
||||
IN uint32_t rsize,
|
||||
OUT nfs41_root **root_out);
|
||||
|
||||
void nfs41_root_ref(
|
||||
IN nfs41_root *root);
|
||||
|
||||
void nfs41_root_deref(
|
||||
IN nfs41_root *root);
|
||||
|
||||
int nfs41_root_mount_addrs(
|
||||
IN nfs41_root *root,
|
||||
IN const multi_addr4 *addrs,
|
||||
IN bool_t is_data,
|
||||
IN OPTIONAL uint32_t lease_time,
|
||||
OUT nfs41_client **client_out);
|
||||
|
||||
int nfs41_root_mount_server(
|
||||
IN nfs41_root *root,
|
||||
IN nfs41_server *server,
|
||||
IN bool_t is_data,
|
||||
IN OPTIONAL uint32_t lease_time,
|
||||
OUT nfs41_client **client_out);
|
||||
|
||||
int nfs41_root_mount_referral(
|
||||
IN nfs41_root *root,
|
||||
IN const fs_locations4 *locations,
|
||||
OUT const fs_location4 **loc_out,
|
||||
OUT nfs41_client **client_out);
|
||||
|
||||
static __inline nfs41_session* nfs41_root_session(
|
||||
IN nfs41_root *root)
|
||||
{
|
||||
nfs41_client *client;
|
||||
/* return a session for the server at the root of the namespace.
|
||||
* because we created it on mount, it's the first one in the list */
|
||||
EnterCriticalSection(&root->lock);
|
||||
client = list_container(root->clients.next, nfs41_client, root_entry);
|
||||
LeaveCriticalSection(&root->lock);
|
||||
return client->session;
|
||||
}
|
||||
|
||||
|
||||
/* nfs41_session.c */
|
||||
int nfs41_session_create(
|
||||
IN nfs41_client *client,
|
||||
IN nfs41_session **session_out);
|
||||
|
||||
int nfs41_session_renew(
|
||||
IN nfs41_session *session);
|
||||
|
||||
int nfs41_session_set_lease(
|
||||
IN nfs41_session *session,
|
||||
IN uint32_t lease_time);
|
||||
|
||||
void nfs41_session_free(
|
||||
IN nfs41_session *session);
|
||||
|
||||
void nfs41_session_bump_seq(
|
||||
IN nfs41_session *session,
|
||||
IN uint32_t slotid,
|
||||
IN uint32_t target_highest_slotid);
|
||||
|
||||
void nfs41_session_free_slot(
|
||||
IN nfs41_session *session,
|
||||
IN uint32_t slotid);
|
||||
|
||||
void nfs41_session_get_slot(
|
||||
IN nfs41_session *session,
|
||||
OUT uint32_t *slot,
|
||||
OUT uint32_t *seq,
|
||||
OUT uint32_t *highest);
|
||||
|
||||
int nfs41_session_recall_slot(
|
||||
IN nfs41_session *session,
|
||||
IN OUT uint32_t target_highest_slotid);
|
||||
|
||||
struct __nfs41_sequence_args;
|
||||
void nfs41_session_sequence(
|
||||
struct __nfs41_sequence_args *args,
|
||||
nfs41_session *session,
|
||||
bool_t cachethis);
|
||||
|
||||
int nfs41_session_bad_slot(
|
||||
IN nfs41_session *session,
|
||||
IN OUT struct __nfs41_sequence_args *args);
|
||||
|
||||
|
||||
/* nfs41_server.c */
|
||||
void nfs41_server_list_init();
|
||||
|
||||
int nfs41_server_resolve(
|
||||
IN const char *hostname,
|
||||
IN unsigned short port,
|
||||
OUT multi_addr4 *addrs);
|
||||
|
||||
int nfs41_server_find_or_create(
|
||||
IN const char *server_owner_major_id,
|
||||
IN const char *server_scope,
|
||||
IN const netaddr4 *addr,
|
||||
OUT nfs41_server **server_out);
|
||||
|
||||
void nfs41_server_ref(
|
||||
IN nfs41_server *server);
|
||||
|
||||
void nfs41_server_deref(
|
||||
IN nfs41_server *server);
|
||||
|
||||
void nfs41_server_addrs(
|
||||
IN nfs41_server *server,
|
||||
OUT multi_addr4 *addrs);
|
||||
|
||||
|
||||
/* nfs41_client.c */
|
||||
int nfs41_client_owner(
|
||||
IN const char *name,
|
||||
IN uint32_t sec_flavor,
|
||||
OUT client_owner4 *owner);
|
||||
|
||||
uint32_t nfs41_exchange_id_flags(
|
||||
IN bool_t is_data);
|
||||
|
||||
struct __nfs41_exchange_id_res;
|
||||
|
||||
int nfs41_client_create(
|
||||
IN nfs41_rpc_clnt *rpc,
|
||||
IN const client_owner4 *owner,
|
||||
IN bool_t is_data,
|
||||
IN const struct __nfs41_exchange_id_res *exchangeid,
|
||||
OUT nfs41_client **client_out);
|
||||
|
||||
int nfs41_client_renew(
|
||||
IN nfs41_client *client);
|
||||
|
||||
void nfs41_client_free(
|
||||
IN nfs41_client *client);
|
||||
|
||||
static __inline nfs41_server* client_server(
|
||||
IN nfs41_client *client)
|
||||
{
|
||||
/* the client's server could change during nfs41_client_renew(),
|
||||
* so access to client->server must be protected */
|
||||
nfs41_server *server;
|
||||
AcquireSRWLockShared(&client->exid_lock);
|
||||
server = client->server;
|
||||
ReleaseSRWLockShared(&client->exid_lock);
|
||||
return server;
|
||||
}
|
||||
|
||||
|
||||
/* nfs41_superblock.c */
|
||||
int nfs41_superblock_for_fh(
|
||||
IN nfs41_session *session,
|
||||
IN const nfs41_fsid *fsid,
|
||||
IN const nfs41_fh *parent OPTIONAL,
|
||||
OUT nfs41_path_fh *file);
|
||||
|
||||
static __inline void nfs41_superblock_getattr_mask(
|
||||
IN const nfs41_superblock *superblock,
|
||||
OUT bitmap4 *attrs)
|
||||
{
|
||||
memcpy(attrs, &superblock->default_getattr, sizeof(bitmap4));
|
||||
}
|
||||
static __inline void nfs41_superblock_supported_attrs(
|
||||
IN const nfs41_superblock *superblock,
|
||||
IN OUT bitmap4 *attrs)
|
||||
{
|
||||
bitmap_intersect(attrs, &superblock->supported_attrs);
|
||||
}
|
||||
static __inline void nfs41_superblock_supported_attrs_exclcreat(
|
||||
IN const nfs41_superblock *superblock,
|
||||
IN OUT bitmap4 *attrs)
|
||||
{
|
||||
bitmap_intersect(attrs, &superblock->suppattr_exclcreat);
|
||||
}
|
||||
|
||||
struct _FILE_FS_ATTRIBUTE_INFORMATION;
|
||||
void nfs41_superblock_fs_attributes(
|
||||
IN const nfs41_superblock *superblock,
|
||||
OUT struct _FILE_FS_ATTRIBUTE_INFORMATION *FsAttrs);
|
||||
|
||||
void nfs41_superblock_space_changed(
|
||||
IN nfs41_superblock *superblock);
|
||||
|
||||
void nfs41_superblock_list_init(
|
||||
IN nfs41_superblock_list *superblocks);
|
||||
|
||||
void nfs41_superblock_list_free(
|
||||
IN nfs41_superblock_list *superblocks);
|
||||
|
||||
|
||||
/* nfs41_rpc.c */
|
||||
int nfs41_rpc_clnt_create(
|
||||
IN const multi_addr4 *addrs,
|
||||
IN uint32_t wsize,
|
||||
IN uint32_t rsize,
|
||||
IN uint32_t uid,
|
||||
IN uint32_t gid,
|
||||
IN uint32_t sec_flavor,
|
||||
OUT nfs41_rpc_clnt **rpc_out);
|
||||
|
||||
void nfs41_rpc_clnt_free(
|
||||
IN nfs41_rpc_clnt *rpc);
|
||||
|
||||
int nfs41_send_compound(
|
||||
IN nfs41_rpc_clnt *rpc,
|
||||
IN char *inbuf,
|
||||
OUT char *outbuf);
|
||||
|
||||
static __inline netaddr4* nfs41_rpc_netaddr(
|
||||
IN nfs41_rpc_clnt *rpc)
|
||||
{
|
||||
uint32_t id;
|
||||
AcquireSRWLockShared(&rpc->lock);
|
||||
/* only addr_index needs to be protected, as rpc->addrs is write-once */
|
||||
id = rpc->addr_index;
|
||||
ReleaseSRWLockShared(&rpc->lock);
|
||||
|
||||
/* return the netaddr used to create the rpc client */
|
||||
return &rpc->addrs.arr[id];
|
||||
}
|
||||
|
||||
|
||||
/* open.c */
|
||||
void nfs41_open_state_ref(
|
||||
IN nfs41_open_state *state);
|
||||
|
||||
void nfs41_open_state_deref(
|
||||
IN nfs41_open_state *state);
|
||||
|
||||
struct __stateid_arg;
|
||||
void nfs41_open_stateid_arg(
|
||||
IN nfs41_open_state *state,
|
||||
OUT struct __stateid_arg *arg);
|
||||
|
||||
|
||||
/* ea.c */
|
||||
int nfs41_ea_set(
|
||||
IN nfs41_open_state *state,
|
||||
IN struct _FILE_FULL_EA_INFORMATION *ea);
|
||||
|
||||
#endif /* __NFS41__ */
|
295
reactos/base/services/nfsd/nfs41_callback.h
Normal file
295
reactos/base/services/nfsd/nfs41_callback.h
Normal file
|
@ -0,0 +1,295 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#ifndef __NFS41_CALLBACK_H__
|
||||
#define __NFS41_CALLBACK_H__
|
||||
|
||||
#include "wintirpc.h"
|
||||
#include "rpc/rpc.h"
|
||||
#include "nfs41_types.h"
|
||||
|
||||
|
||||
enum nfs41_callback_proc {
|
||||
CB_NULL = 0,
|
||||
CB_COMPOUND = 1,
|
||||
};
|
||||
|
||||
enum nfs41_callback_op {
|
||||
OP_CB_GETATTR = 3,
|
||||
OP_CB_RECALL = 4,
|
||||
OP_CB_LAYOUTRECALL = 5,
|
||||
OP_CB_NOTIFY = 6,
|
||||
OP_CB_PUSH_DELEG = 7,
|
||||
OP_CB_RECALL_ANY = 8,
|
||||
OP_CB_RECALLABLE_OBJ_AVAIL = 9,
|
||||
OP_CB_RECALL_SLOT = 10,
|
||||
OP_CB_SEQUENCE = 11,
|
||||
OP_CB_WANTS_CANCELLED = 12,
|
||||
OP_CB_NOTIFY_LOCK = 13,
|
||||
OP_CB_NOTIFY_DEVICEID = 14,
|
||||
OP_CB_ILLEGAL = 10044
|
||||
};
|
||||
|
||||
int nfs41_handle_callback(void *, void *, void *);
|
||||
|
||||
/* OP_CB_LAYOUTRECALL */
|
||||
struct cb_recall_file {
|
||||
nfs41_fh fh;
|
||||
uint64_t offset;
|
||||
uint64_t length;
|
||||
stateid4 stateid;
|
||||
};
|
||||
union cb_recall_file_args {
|
||||
struct cb_recall_file file;
|
||||
nfs41_fsid fsid;
|
||||
};
|
||||
struct cb_recall {
|
||||
#ifdef __REACTOS__
|
||||
uint32_t type;
|
||||
#else
|
||||
enum pnfs_return_type type;
|
||||
#endif
|
||||
union cb_recall_file_args args;
|
||||
};
|
||||
struct cb_layoutrecall_args {
|
||||
#ifdef __REACTOS__
|
||||
uint32_t type;
|
||||
uint32_t iomode;
|
||||
#else
|
||||
enum pnfs_return_type type;
|
||||
enum pnfs_iomode iomode;
|
||||
#endif
|
||||
bool_t changed;
|
||||
struct cb_recall recall;
|
||||
};
|
||||
|
||||
struct cb_layoutrecall_res {
|
||||
enum_t status;
|
||||
};
|
||||
|
||||
/* OP_CB_RECALL_SLOT */
|
||||
struct cb_recall_slot_args {
|
||||
uint32_t target_highest_slotid;
|
||||
};
|
||||
|
||||
struct cb_recall_slot_res {
|
||||
enum_t status;
|
||||
};
|
||||
|
||||
/* OP_CB_SEQUENCE */
|
||||
struct cb_sequence_ref {
|
||||
uint32_t sequenceid;
|
||||
uint32_t slotid;
|
||||
};
|
||||
struct cb_sequence_ref_list {
|
||||
char sessionid[NFS4_SESSIONID_SIZE];
|
||||
struct cb_sequence_ref *calls;
|
||||
uint32_t call_count;
|
||||
};
|
||||
struct cb_sequence_args {
|
||||
char sessionid[NFS4_SESSIONID_SIZE];
|
||||
uint32_t sequenceid;
|
||||
uint32_t slotid;
|
||||
uint32_t highest_slotid;
|
||||
bool_t cachethis;
|
||||
struct cb_sequence_ref_list *ref_lists;
|
||||
uint32_t ref_list_count;
|
||||
};
|
||||
|
||||
struct cb_sequence_res_ok {
|
||||
char sessionid[NFS4_SESSIONID_SIZE];
|
||||
uint32_t sequenceid;
|
||||
uint32_t slotid;
|
||||
uint32_t highest_slotid;
|
||||
uint32_t target_highest_slotid;
|
||||
};
|
||||
struct cb_sequence_res {
|
||||
enum_t status;
|
||||
struct cb_sequence_res_ok ok;
|
||||
};
|
||||
|
||||
/* OP_CB_GETATTR */
|
||||
struct cb_getattr_args {
|
||||
nfs41_fh fh;
|
||||
bitmap4 attr_request;
|
||||
};
|
||||
|
||||
struct cb_getattr_res {
|
||||
enum_t status;
|
||||
nfs41_file_info info;
|
||||
};
|
||||
|
||||
/* OP_CB_RECALL */
|
||||
struct cb_recall_args {
|
||||
stateid4 stateid;
|
||||
bool_t truncate;
|
||||
nfs41_fh fh;
|
||||
};
|
||||
|
||||
struct cb_recall_res {
|
||||
enum_t status;
|
||||
};
|
||||
|
||||
/* OP_CB_NOTIFY */
|
||||
struct cb_notify_args {
|
||||
uint32_t target_highest_slotid;
|
||||
};
|
||||
|
||||
struct cb_notify_res {
|
||||
enum_t status;
|
||||
};
|
||||
|
||||
/* OP_CB_PUSH_DELEG */
|
||||
struct cb_push_deleg_args {
|
||||
uint32_t target_highest_slotid;
|
||||
};
|
||||
|
||||
struct cb_push_deleg_res {
|
||||
enum_t status;
|
||||
};
|
||||
|
||||
/* OP_CB_RECALL_ANY */
|
||||
struct cb_recall_any_args {
|
||||
uint32_t target_highest_slotid;
|
||||
};
|
||||
|
||||
struct cb_recall_any_res {
|
||||
enum_t status;
|
||||
};
|
||||
|
||||
/* OP_CB_RECALLABLE_OBJ_AVAIL */
|
||||
struct cb_recallable_obj_avail_args {
|
||||
uint32_t target_highest_slotid;
|
||||
};
|
||||
|
||||
struct cb_recallable_obj_avail_res {
|
||||
enum_t status;
|
||||
};
|
||||
|
||||
/* OP_CB_WANTS_CANCELLED */
|
||||
struct cb_wants_cancelled_args {
|
||||
uint32_t target_highest_slotid;
|
||||
};
|
||||
|
||||
struct cb_wants_cancelled_res {
|
||||
enum_t status;
|
||||
};
|
||||
|
||||
/* OP_CB_NOTIFY_LOCK */
|
||||
struct cb_notify_lock_args {
|
||||
uint32_t target_highest_slotid;
|
||||
};
|
||||
|
||||
struct cb_notify_lock_res {
|
||||
enum_t status;
|
||||
};
|
||||
|
||||
/* OP_CB_NOTIFY_DEVICEID */
|
||||
enum notify_deviceid_type4 {
|
||||
NOTIFY_DEVICEID4_CHANGE = 1,
|
||||
NOTIFY_DEVICEID4_DELETE = 2
|
||||
};
|
||||
struct notify_deviceid4 {
|
||||
unsigned char deviceid[16];
|
||||
enum notify_deviceid_type4 type;
|
||||
#ifdef __REACTOS__
|
||||
uint32_t layouttype;
|
||||
#else
|
||||
enum pnfs_layout_type layouttype;
|
||||
#endif
|
||||
bool_t immediate;
|
||||
};
|
||||
struct notify4 {
|
||||
bitmap4 mask;
|
||||
char *list;
|
||||
uint32_t len;
|
||||
};
|
||||
struct cb_notify_deviceid_args {
|
||||
struct notify4 *notify_list;
|
||||
uint32_t notify_count;
|
||||
struct notify_deviceid4 *change_list;
|
||||
uint32_t change_count;
|
||||
};
|
||||
|
||||
struct cb_notify_deviceid_res {
|
||||
enum_t status;
|
||||
};
|
||||
|
||||
/* CB_COMPOUND */
|
||||
#define CB_COMPOUND_MAX_TAG 64
|
||||
#define CB_COMPOUND_MAX_OPERATIONS 16
|
||||
|
||||
union cb_op_args {
|
||||
struct cb_layoutrecall_args layoutrecall;
|
||||
struct cb_recall_slot_args recall_slot;
|
||||
struct cb_sequence_args sequence;
|
||||
struct cb_getattr_args getattr;
|
||||
struct cb_recall_args recall;
|
||||
struct cb_notify_deviceid_args notify_deviceid;
|
||||
};
|
||||
struct cb_argop {
|
||||
enum_t opnum;
|
||||
union cb_op_args args;
|
||||
};
|
||||
struct cb_compound_tag {
|
||||
char str[CB_COMPOUND_MAX_TAG];
|
||||
uint32_t len;
|
||||
};
|
||||
struct cb_compound_args {
|
||||
struct cb_compound_tag tag;
|
||||
uint32_t minorversion;
|
||||
uint32_t callback_ident; /* client MUST ignore */
|
||||
struct cb_argop *argarray;
|
||||
uint32_t argarray_count; /* <= CB_COMPOUND_MAX_OPERATIONS */
|
||||
};
|
||||
|
||||
union cb_op_res {
|
||||
enum_t status; /* all results start with status */
|
||||
struct cb_layoutrecall_res layoutrecall;
|
||||
struct cb_recall_slot_res recall_slot;
|
||||
struct cb_sequence_res sequence;
|
||||
struct cb_getattr_res getattr;
|
||||
struct cb_recall_res recall;
|
||||
struct cb_notify_deviceid_res notify_deviceid;
|
||||
};
|
||||
struct cb_resop {
|
||||
enum_t opnum;
|
||||
union cb_op_res res;
|
||||
bool_t xdr_ok;
|
||||
};
|
||||
struct cb_compound_res {
|
||||
enum_t status;
|
||||
struct cb_compound_tag tag;
|
||||
struct cb_resop *resarray;
|
||||
uint32_t resarray_count; /* <= CB_COMPOUND_MAX_OPERATIONS */
|
||||
};
|
||||
|
||||
|
||||
/* callback_xdr.c */
|
||||
bool_t proc_cb_compound_args(XDR *xdr, struct cb_compound_args *args);
|
||||
bool_t proc_cb_compound_res(XDR *xdr, struct cb_compound_res *res);
|
||||
|
||||
/* callback_server.c */
|
||||
struct __nfs41_session;
|
||||
void nfs41_callback_session_init(
|
||||
IN struct __nfs41_session *session);
|
||||
|
||||
#endif /* !__NFS41_CALLBACK_H__ */
|
442
reactos/base/services/nfsd/nfs41_client.c
Normal file
442
reactos/base/services/nfsd/nfs41_client.c
Normal file
|
@ -0,0 +1,442 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <winsock2.h>
|
||||
#include <iphlpapi.h> /* for GetAdaptersAddresses() */
|
||||
#include <wincrypt.h> /* for Crypt*() functions */
|
||||
#include <winsock2.h> /* for hostent struct */
|
||||
|
||||
#include "tree.h"
|
||||
#include "delegation.h"
|
||||
#include "daemon_debug.h"
|
||||
#include "nfs41_ops.h"
|
||||
|
||||
|
||||
uint32_t nfs41_exchange_id_flags(
|
||||
IN bool_t is_data)
|
||||
{
|
||||
uint32_t flags = EXCHGID4_FLAG_SUPP_MOVED_REFER;
|
||||
if (is_data)
|
||||
flags |= EXCHGID4_FLAG_USE_PNFS_DS;
|
||||
else
|
||||
flags |= EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_USE_PNFS_MDS;
|
||||
return flags;
|
||||
}
|
||||
|
||||
static int pnfs_client_init(
|
||||
IN nfs41_client *client)
|
||||
{
|
||||
enum pnfs_status pnfsstat;
|
||||
int status = NO_ERROR;
|
||||
|
||||
/* initialize the pnfs layout and device lists for metadata clients */
|
||||
pnfsstat = pnfs_layout_list_create(&client->layouts);
|
||||
if (pnfsstat) {
|
||||
status = ERROR_NOT_ENOUGH_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
pnfsstat = pnfs_file_device_list_create(&client->devices);
|
||||
if (pnfsstat) {
|
||||
status = ERROR_NOT_ENOUGH_MEMORY;
|
||||
goto out_err_layouts;
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
|
||||
out_err_layouts:
|
||||
pnfs_layout_list_free(client->layouts);
|
||||
client->layouts = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int update_server(
|
||||
IN nfs41_client *client,
|
||||
IN const char *server_scope,
|
||||
IN const server_owner4 *owner)
|
||||
{
|
||||
nfs41_server *server;
|
||||
int status;
|
||||
|
||||
/* find a server matching the owner.major_id and scope */
|
||||
status = nfs41_server_find_or_create(owner->so_major_id,
|
||||
server_scope, nfs41_rpc_netaddr(client->rpc), &server);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
/* if the server is the same, we now have an extra reference. if
|
||||
* the servers are different, we still need to deref the old server.
|
||||
* so both cases can be treated the same */
|
||||
if (client->server)
|
||||
nfs41_server_deref(client->server);
|
||||
client->server = server;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int update_exchangeid_res(
|
||||
IN nfs41_client *client,
|
||||
IN const nfs41_exchange_id_res *exchangeid)
|
||||
{
|
||||
client->clnt_id = exchangeid->clientid;
|
||||
client->seq_id = exchangeid->sequenceid;
|
||||
client->roles = exchangeid->flags & EXCHGID4_FLAG_MASK_PNFS;
|
||||
return update_server(client, exchangeid->server_scope,
|
||||
&exchangeid->server_owner);
|
||||
}
|
||||
|
||||
int nfs41_client_create(
|
||||
IN nfs41_rpc_clnt *rpc,
|
||||
IN const client_owner4 *owner,
|
||||
IN bool_t is_data,
|
||||
IN const nfs41_exchange_id_res *exchangeid,
|
||||
OUT nfs41_client **client_out)
|
||||
{
|
||||
int status;
|
||||
nfs41_client *client;
|
||||
|
||||
client = calloc(1, sizeof(nfs41_client));
|
||||
if (client == NULL) {
|
||||
status = GetLastError();
|
||||
goto out_err_rpc;
|
||||
}
|
||||
|
||||
memcpy(&client->owner, owner, sizeof(client_owner4));
|
||||
client->rpc = rpc;
|
||||
client->is_data = is_data;
|
||||
|
||||
status = update_exchangeid_res(client, exchangeid);
|
||||
if (status)
|
||||
goto out_err_client;
|
||||
|
||||
list_init(&client->state.opens);
|
||||
list_init(&client->state.delegations);
|
||||
InitializeCriticalSection(&client->state.lock);
|
||||
|
||||
//initialize a lock used to protect access to client id and client id seq#
|
||||
InitializeSRWLock(&client->exid_lock);
|
||||
|
||||
InitializeConditionVariable(&client->recovery.cond);
|
||||
InitializeCriticalSection(&client->recovery.lock);
|
||||
|
||||
status = pnfs_client_init(client);
|
||||
if (status) {
|
||||
eprintf("pnfs_client_init() failed with %d\n", status);
|
||||
goto out_err_client;
|
||||
}
|
||||
*client_out = client;
|
||||
out:
|
||||
return status;
|
||||
out_err_client:
|
||||
nfs41_client_free(client); /* also calls nfs41_rpc_clnt_free() */
|
||||
goto out;
|
||||
out_err_rpc:
|
||||
nfs41_rpc_clnt_free(rpc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void dprint_roles(
|
||||
IN int level,
|
||||
IN uint32_t roles)
|
||||
{
|
||||
dprintf(level, "roles: %s%s%s\n",
|
||||
(roles & EXCHGID4_FLAG_USE_NON_PNFS) ? "USE_NON_PNFS " : "",
|
||||
(roles & EXCHGID4_FLAG_USE_PNFS_MDS) ? "USE_PNFS_MDS " : "",
|
||||
(roles & EXCHGID4_FLAG_USE_PNFS_DS) ? "USE_PNFS_DS" : "");
|
||||
}
|
||||
|
||||
int nfs41_client_renew(
|
||||
IN nfs41_client *client)
|
||||
{
|
||||
nfs41_exchange_id_res exchangeid = { 0 };
|
||||
int status;
|
||||
|
||||
status = nfs41_exchange_id(client->rpc, &client->owner,
|
||||
nfs41_exchange_id_flags(client->is_data), &exchangeid);
|
||||
if (status) {
|
||||
eprintf("nfs41_exchange_id() failed with %d\n", status);
|
||||
status = ERROR_BAD_NET_RESP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (client->is_data) { /* require USE_PNFS_DS */
|
||||
if ((exchangeid.flags & EXCHGID4_FLAG_USE_PNFS_DS) == 0) {
|
||||
eprintf("client expected USE_PNFS_DS\n");
|
||||
status = ERROR_BAD_NET_RESP;
|
||||
goto out;
|
||||
}
|
||||
} else { /* require USE_NON_PNFS or USE_PNFS_MDS */
|
||||
if ((exchangeid.flags & EXCHGID4_FLAG_USE_NON_PNFS) == 0 &&
|
||||
(exchangeid.flags & EXCHGID4_FLAG_USE_PNFS_MDS) == 0) {
|
||||
eprintf("client expected USE_NON_PNFS OR USE_PNFS_MDS\n");
|
||||
status = ERROR_BAD_NET_RESP;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
dprint_roles(2, exchangeid.flags);
|
||||
|
||||
AcquireSRWLockExclusive(&client->exid_lock);
|
||||
status = update_exchangeid_res(client, &exchangeid);
|
||||
ReleaseSRWLockExclusive(&client->exid_lock);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
void nfs41_client_free(
|
||||
IN nfs41_client *client)
|
||||
{
|
||||
dprintf(2, "nfs41_client_free(%llu)\n", client->clnt_id);
|
||||
nfs41_client_delegation_free(client);
|
||||
if (client->session) nfs41_session_free(client->session);
|
||||
nfs41_destroy_clientid(client->rpc, client->clnt_id);
|
||||
if (client->server) nfs41_server_deref(client->server);
|
||||
nfs41_rpc_clnt_free(client->rpc);
|
||||
if (client->layouts) pnfs_layout_list_free(client->layouts);
|
||||
if (client->devices) pnfs_file_device_list_free(client->devices);
|
||||
DeleteCriticalSection(&client->state.lock);
|
||||
DeleteCriticalSection(&client->recovery.lock);
|
||||
free(client);
|
||||
}
|
||||
|
||||
|
||||
/* client_owner generation
|
||||
* we choose to use MAC addresses to generate a client_owner value that
|
||||
* is unique to a machine and persists over restarts. because the client
|
||||
* can have multiple network adapters/addresses, we take each adapter into
|
||||
* account. the specification suggests that "for privacy reasons, it is
|
||||
* best to perform some one-way function," so we apply an md5 hash to the
|
||||
* sorted list of MAC addresses */
|
||||
|
||||
/* References:
|
||||
* RFC 5661: 2.4. Client Identifiers and Client Owners
|
||||
* http://tools.ietf.org/html/rfc5661#section-2.4
|
||||
*
|
||||
* MSDN: GetAdaptersAddresses Function
|
||||
* http://msdn.microsoft.com/en-us/library/aa365915%28VS.85%29.aspx
|
||||
*
|
||||
* MSDN: Example C Program: Creating an MD5 Hash from File Content
|
||||
* http://msdn.microsoft.com/en-us/library/aa382380%28VS.85%29.aspx */
|
||||
|
||||
|
||||
/* use an rbtree to sort mac address entries */
|
||||
struct mac_entry {
|
||||
RB_ENTRY(mac_entry) rbnode;
|
||||
PBYTE address;
|
||||
ULONG length;
|
||||
};
|
||||
|
||||
int mac_cmp(struct mac_entry *lhs, struct mac_entry *rhs)
|
||||
{
|
||||
const int diff = rhs->length - lhs->length;
|
||||
return diff ? diff : strncmp((const char*)lhs->address,
|
||||
(const char*)rhs->address, lhs->length);
|
||||
}
|
||||
RB_HEAD(mac_tree, mac_entry);
|
||||
RB_GENERATE(mac_tree, mac_entry, rbnode, mac_cmp)
|
||||
|
||||
static void mac_entry_insert(
|
||||
IN struct mac_tree *root,
|
||||
IN PBYTE address,
|
||||
IN ULONG length)
|
||||
{
|
||||
struct mac_entry *entry;
|
||||
|
||||
entry = calloc(1, sizeof(struct mac_entry));
|
||||
if (entry == NULL)
|
||||
return;
|
||||
|
||||
entry->address = address;
|
||||
entry->length = length;
|
||||
|
||||
if (RB_INSERT(mac_tree, root, entry))
|
||||
free(entry);
|
||||
}
|
||||
|
||||
static int adapter_valid(
|
||||
IN const IP_ADAPTER_ADDRESSES *addr)
|
||||
{
|
||||
/* ignore generic interfaces whose address is not unique */
|
||||
switch (addr->IfType) {
|
||||
case IF_TYPE_SOFTWARE_LOOPBACK:
|
||||
case IF_TYPE_TUNNEL:
|
||||
return 0;
|
||||
}
|
||||
/* must have an address */
|
||||
if (addr->PhysicalAddressLength == 0)
|
||||
return 0;
|
||||
#ifndef __REACTOS__
|
||||
/* must support ip */
|
||||
return addr->Ipv4Enabled || addr->Ipv6Enabled;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static DWORD hash_mac_addrs(
|
||||
IN HCRYPTHASH hash)
|
||||
{
|
||||
PIP_ADAPTER_ADDRESSES addr, addrs = NULL;
|
||||
struct mac_tree rbtree = RB_INITIALIZER(rbtree);
|
||||
struct mac_entry *entry, *node;
|
||||
ULONG len;
|
||||
DWORD status;
|
||||
|
||||
/* start with enough room for DEFAULT_MINIMUM_ENTITIES */
|
||||
len = DEFAULT_MINIMUM_ENTITIES * sizeof(IP_ADAPTER_ADDRESSES);
|
||||
|
||||
do {
|
||||
PIP_ADAPTER_ADDRESSES tmp;
|
||||
/* reallocate the buffer until we can fit all of it */
|
||||
tmp = realloc(addrs, len);
|
||||
if (tmp == NULL) {
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
addrs = tmp;
|
||||
status = GetAdaptersAddresses(AF_UNSPEC,
|
||||
GAA_FLAG_INCLUDE_ALL_INTERFACES | GAA_FLAG_SKIP_ANYCAST |
|
||||
GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME |
|
||||
GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_UNICAST,
|
||||
NULL, addrs, &len);
|
||||
} while (status == ERROR_BUFFER_OVERFLOW);
|
||||
|
||||
if (status) {
|
||||
eprintf("GetAdaptersAddresses() failed with %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* get the mac address of each adapter */
|
||||
for (addr = addrs; addr; addr = addr->Next)
|
||||
if (adapter_valid(addr))
|
||||
mac_entry_insert(&rbtree, addr->PhysicalAddress,
|
||||
addr->PhysicalAddressLength);
|
||||
|
||||
/* require at least one valid address */
|
||||
if (RB_EMPTY(&rbtree)) {
|
||||
status = ERROR_FILE_NOT_FOUND;
|
||||
eprintf("GetAdaptersAddresses() did not return "
|
||||
"any valid mac addresses, failing with %d.\n", status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
RB_FOREACH_SAFE(entry, mac_tree, &rbtree, node) {
|
||||
RB_REMOVE(mac_tree, &rbtree, entry);
|
||||
|
||||
if (!CryptHashData(hash, entry->address, entry->length, 0)) {
|
||||
status = GetLastError();
|
||||
eprintf("CryptHashData() failed with %d\n", status);
|
||||
/* don't break here, we need to free the rest */
|
||||
}
|
||||
free(entry);
|
||||
}
|
||||
out:
|
||||
free(addrs);
|
||||
return status;
|
||||
}
|
||||
|
||||
int nfs41_client_owner(
|
||||
IN const char *name,
|
||||
IN uint32_t sec_flavor,
|
||||
OUT client_owner4 *owner)
|
||||
{
|
||||
HCRYPTPROV context;
|
||||
HCRYPTHASH hash;
|
||||
PBYTE buffer;
|
||||
DWORD length;
|
||||
const ULONGLONG time_created = GetTickCount64();
|
||||
int status;
|
||||
char username[UNLEN + 1];
|
||||
DWORD len = UNLEN + 1;
|
||||
|
||||
if (!GetUserNameA(username, &len)) {
|
||||
status = GetLastError();
|
||||
eprintf("GetUserName() failed with %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* owner.verifier = "time created" */
|
||||
memcpy(owner->co_verifier, &time_created, sizeof(time_created));
|
||||
|
||||
/* set up the md5 hash generator */
|
||||
if (!CryptAcquireContext(&context, NULL, NULL,
|
||||
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
|
||||
status = GetLastError();
|
||||
eprintf("CryptAcquireContext() failed with %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
if (!CryptCreateHash(context, CALG_MD5, 0, 0, &hash)) {
|
||||
status = GetLastError();
|
||||
eprintf("CryptCreateHash() failed with %d\n", status);
|
||||
goto out_context;
|
||||
}
|
||||
|
||||
if (!CryptHashData(hash, (const BYTE*)&sec_flavor, (DWORD)sizeof(sec_flavor), 0)) {
|
||||
status = GetLastError();
|
||||
eprintf("CryptHashData() failed with %d\n", status);
|
||||
goto out_hash;
|
||||
}
|
||||
|
||||
if (!CryptHashData(hash, (const BYTE*)username, (DWORD)strlen(username), 0)) {
|
||||
status = GetLastError();
|
||||
eprintf("CryptHashData() failed with %d\n", status);
|
||||
goto out_hash;
|
||||
}
|
||||
|
||||
if (!CryptHashData(hash, (const BYTE*)name, (DWORD)strlen(name), 0)) {
|
||||
status = GetLastError();
|
||||
eprintf("CryptHashData() failed with %d\n", status);
|
||||
goto out_hash;
|
||||
}
|
||||
|
||||
/* add the mac address from each applicable adapter to the hash */
|
||||
status = hash_mac_addrs(hash);
|
||||
if (status) {
|
||||
eprintf("hash_mac_addrs() failed with %d\n", status);
|
||||
goto out_hash;
|
||||
}
|
||||
|
||||
/* extract the hash size (should always be 16 for md5) */
|
||||
buffer = (PBYTE)&owner->co_ownerid_len;
|
||||
length = (DWORD)sizeof(DWORD);
|
||||
if (!CryptGetHashParam(hash, HP_HASHSIZE, buffer, &length, 0)) {
|
||||
status = GetLastError();
|
||||
eprintf("CryptGetHashParam(size) failed with %d\n", status);
|
||||
goto out_hash;
|
||||
}
|
||||
/* extract the hash buffer */
|
||||
buffer = owner->co_ownerid;
|
||||
length = owner->co_ownerid_len;
|
||||
if (!CryptGetHashParam(hash, HP_HASHVAL, buffer, &length, 0)) {
|
||||
status = GetLastError();
|
||||
eprintf("CryptGetHashParam(val) failed with %d\n", status);
|
||||
goto out_hash;
|
||||
}
|
||||
|
||||
out_hash:
|
||||
CryptDestroyHash(hash);
|
||||
out_context:
|
||||
CryptReleaseContext(context, 0);
|
||||
out:
|
||||
return status;
|
||||
}
|
457
reactos/base/services/nfsd/nfs41_compound.c
Normal file
457
reactos/base/services/nfsd/nfs41_compound.c
Normal file
|
@ -0,0 +1,457 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "nfs41_compound.h"
|
||||
#include "nfs41_xdr.h"
|
||||
#include "nfs41_ops.h"
|
||||
#include "recovery.h"
|
||||
#include "name_cache.h"
|
||||
#include "daemon_debug.h"
|
||||
#include "rpc/rpc.h"
|
||||
#include "rpc/auth_sspi.h"
|
||||
|
||||
int compound_error(int status)
|
||||
{
|
||||
if (status != NFS4_OK)
|
||||
dprintf(1, "COMPOUND failed with status %d.\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
void compound_init(
|
||||
nfs41_compound *compound,
|
||||
nfs_argop4 *argops,
|
||||
nfs_resop4 *resops,
|
||||
const char *tag)
|
||||
{
|
||||
/* initialize args */
|
||||
compound->args.tag_len = (uint32_t)strlen(tag);
|
||||
memcpy(compound->args.tag, tag, compound->args.tag_len);
|
||||
compound->args.minorversion = 1;
|
||||
compound->args.argarray_count = 0;
|
||||
compound->args.argarray = argops;
|
||||
|
||||
/* initialize results */
|
||||
ZeroMemory(&compound->res, sizeof(nfs41_compound_res));
|
||||
compound->res.tag_len = NFS4_OPAQUE_LIMIT;
|
||||
compound->res.resarray_count = 0;
|
||||
compound->res.resarray = resops;
|
||||
}
|
||||
|
||||
void compound_add_op(
|
||||
nfs41_compound *compound,
|
||||
uint32_t opnum,
|
||||
void *arg,
|
||||
void *res)
|
||||
{
|
||||
const uint32_t i = compound->args.argarray_count++;
|
||||
const uint32_t j = compound->res.resarray_count++;
|
||||
compound->args.argarray[i].op = opnum;
|
||||
compound->args.argarray[i].arg = arg;
|
||||
compound->res.resarray[j].op = opnum;
|
||||
compound->res.resarray[j].res = res;
|
||||
}
|
||||
|
||||
/* Due to the possibility of replays, we might get a response to a different
|
||||
* call than the one we're expecting. If we don't have a way to check for
|
||||
* this, we'll likely crash trying to decode into the wrong structures.
|
||||
* This function copies the number of operations and all of the operation
|
||||
* numbers from the compound arguments into the response, so we can verify
|
||||
* them on decode and fail before doing any damage. */
|
||||
static void set_expected_res(
|
||||
nfs41_compound *compound)
|
||||
{
|
||||
uint32_t i;
|
||||
compound->res.resarray_count = compound->args.argarray_count;
|
||||
for (i = 0; i < compound->res.resarray_count; i++)
|
||||
compound->res.resarray[i].op = compound->args.argarray[i].op;
|
||||
}
|
||||
|
||||
|
||||
static int create_new_rpc_auth(nfs41_session *session, uint32_t op,
|
||||
nfs41_secinfo_info *secinfo)
|
||||
{
|
||||
AUTH *auth = NULL;
|
||||
int status = ERROR_NETWORK_UNREACHABLE, i;
|
||||
uint32_t sec_flavor;
|
||||
|
||||
for (i = 0; i < MAX_SECINFOS; i++) {
|
||||
if (!secinfo[i].sec_flavor && !secinfo[i].type)
|
||||
goto out;
|
||||
if (secinfo[i].sec_flavor == RPCSEC_GSS) {
|
||||
auth = authsspi_create_default(session->client->rpc->rpc,
|
||||
session->client->rpc->server_name, secinfo[i].type);
|
||||
if (auth == NULL) {
|
||||
eprintf("handle_wrongsecinfo_noname: authsspi_create_default for "
|
||||
"gsstype %s failed\n", gssauth_string(secinfo[i].type));
|
||||
continue;
|
||||
}
|
||||
sec_flavor = secinfo[i].type;
|
||||
} else {
|
||||
char machname[MAXHOSTNAMELEN + 1];
|
||||
gid_t gids[1];
|
||||
if (gethostname(machname, sizeof(machname)) == -1) {
|
||||
eprintf("nfs41_rpc_clnt_create: gethostname failed\n");
|
||||
continue;
|
||||
}
|
||||
machname[sizeof(machname) - 1] = '\0';
|
||||
auth = authsys_create(machname, session->client->rpc->uid,
|
||||
session->client->rpc->gid, 0, gids);
|
||||
if (auth == NULL) {
|
||||
eprintf("handle_wrongsecinfo_noname: authsys_create failed\n");
|
||||
continue;
|
||||
}
|
||||
sec_flavor = AUTH_SYS;
|
||||
}
|
||||
AcquireSRWLockExclusive(&session->client->rpc->lock);
|
||||
session->client->rpc->sec_flavor = sec_flavor;
|
||||
session->client->rpc->rpc->cl_auth = auth;
|
||||
ReleaseSRWLockExclusive(&session->client->rpc->lock);
|
||||
status = 0;
|
||||
break;
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
int compound_encode_send_decode(
|
||||
nfs41_session *session,
|
||||
nfs41_compound *compound,
|
||||
bool_t try_recovery)
|
||||
{
|
||||
int status, retry_count = 0, delayby = 0, secinfo_status;
|
||||
nfs41_sequence_args *args = (nfs41_sequence_args *)
|
||||
compound->args.argarray[0].arg;
|
||||
uint32_t saved_sec_flavor;
|
||||
AUTH *saved_auth;
|
||||
int op1 = compound->args.argarray[0].op;
|
||||
|
||||
retry:
|
||||
/* send compound */
|
||||
retry_count++;
|
||||
set_expected_res(compound);
|
||||
status = nfs41_send_compound(session->client->rpc,
|
||||
(char *)&compound->args, (char *)&compound->res);
|
||||
// bump sequence number if sequence op succeeded.
|
||||
if (compound->res.resarray_count > 0 &&
|
||||
compound->res.resarray[0].op == OP_SEQUENCE) {
|
||||
nfs41_sequence_res *seq =
|
||||
(nfs41_sequence_res *)compound->res.resarray[0].res;
|
||||
if (seq->sr_status == NFS4_OK) {
|
||||
// returned slotid must be the same we sent
|
||||
if (seq->sr_resok4.sr_slotid != args->sa_slotid) {
|
||||
eprintf("[session] sr_slotid=%d != sa_slotid=%d\n",
|
||||
seq->sr_resok4.sr_slotid, args->sa_slotid);
|
||||
status = NFS4ERR_IO;
|
||||
goto out_free_slot;
|
||||
}
|
||||
// returned sessionid must be the same we sent
|
||||
if (memcmp(seq->sr_resok4.sr_sessionid, args->sa_sessionid,
|
||||
NFS4_SESSIONID_SIZE)) {
|
||||
eprintf("[session] sr_sessionid != sa_sessionid\n");
|
||||
print_hexbuf(1, (unsigned char *)"sr_sessionid",
|
||||
seq->sr_resok4.sr_sessionid, NFS4_SESSIONID_SIZE);
|
||||
print_hexbuf(1, (unsigned char *)"sa_sessionid",
|
||||
args->sa_sessionid, NFS4_SESSIONID_SIZE);
|
||||
status = NFS4ERR_IO;
|
||||
goto out_free_slot;
|
||||
}
|
||||
if (seq->sr_resok4.sr_status_flags)
|
||||
print_sr_status_flags(1, seq->sr_resok4.sr_status_flags);
|
||||
|
||||
nfs41_session_bump_seq(session, args->sa_slotid,
|
||||
seq->sr_resok4.sr_target_highest_slotid);
|
||||
|
||||
/* check sequence status flags for state revocation */
|
||||
if (try_recovery && seq->sr_resok4.sr_status_flags)
|
||||
nfs41_recover_sequence_flags(session,
|
||||
seq->sr_resok4.sr_status_flags);
|
||||
}
|
||||
}
|
||||
|
||||
if (status) {
|
||||
eprintf("nfs41_send_compound failed %d for seqid=%d, slotid=%d\n",
|
||||
status, args->sa_sequenceid, args->sa_slotid);
|
||||
status = NFS4ERR_IO;
|
||||
goto out_free_slot;
|
||||
}
|
||||
|
||||
if (compound->res.status != NFS4_OK)
|
||||
dprintf(1, "\n################ %s ################\n\n",
|
||||
nfs_error_string(compound->res.status));
|
||||
|
||||
switch (compound->res.status) {
|
||||
case NFS4_OK:
|
||||
break;
|
||||
|
||||
case NFS4ERR_STALE_CLIENTID:
|
||||
if (!try_recovery)
|
||||
goto out;
|
||||
if (!nfs41_recovery_start_or_wait(session->client))
|
||||
goto do_retry;
|
||||
// try to create a new client
|
||||
status = nfs41_client_renew(session->client);
|
||||
|
||||
nfs41_recovery_finish(session->client);
|
||||
if (status) {
|
||||
eprintf("nfs41_client_renew() failed with %d\n", status);
|
||||
status = ERROR_BAD_NET_RESP;
|
||||
goto out;
|
||||
}
|
||||
if (op1 == OP_CREATE_SESSION) {
|
||||
nfs41_create_session_args *csa = (nfs41_create_session_args*)
|
||||
compound->args.argarray[0].arg;
|
||||
AcquireSRWLockShared(&session->client->exid_lock);
|
||||
csa->csa_clientid = session->client->clnt_id;
|
||||
csa->csa_sequence = session->client->seq_id;
|
||||
AcquireSRWLockShared(&session->client->exid_lock);
|
||||
}
|
||||
goto do_retry;
|
||||
|
||||
case NFS4ERR_BADSESSION:
|
||||
if (!try_recovery)
|
||||
goto out;
|
||||
if (!nfs41_recovery_start_or_wait(session->client))
|
||||
goto do_retry;
|
||||
// try to create a new session
|
||||
status = nfs41_recover_session(session, FALSE);
|
||||
|
||||
nfs41_recovery_finish(session->client);
|
||||
if (status) {
|
||||
eprintf("nfs41_recover_session() failed with %d\n", status);
|
||||
status = ERROR_BAD_NET_RESP;
|
||||
goto out;
|
||||
}
|
||||
goto do_retry;
|
||||
|
||||
case NFS4ERR_EXPIRED: /* revoked by lease expiration */
|
||||
case NFS4ERR_BAD_STATEID:
|
||||
case NFS4ERR_STALE_STATEID: /* server reboot */
|
||||
if (op1 == OP_SEQUENCE)
|
||||
nfs41_session_free_slot(session, args->sa_slotid);
|
||||
if (try_recovery && nfs41_recover_stateid(session,
|
||||
&compound->args.argarray[compound->res.resarray_count-1]))
|
||||
goto do_retry;
|
||||
goto out;
|
||||
|
||||
case NFS4ERR_BADSLOT:
|
||||
/* free the slot and retry with a new one */
|
||||
if (op1 != OP_SEQUENCE || nfs41_session_bad_slot(session, args))
|
||||
goto out;
|
||||
goto retry;
|
||||
|
||||
case NFS4ERR_GRACE:
|
||||
case NFS4ERR_DELAY:
|
||||
#define RETRY_INDEFINITELY
|
||||
#ifndef RETRY_INDEFINITELY
|
||||
#define NUMBER_2_RETRY 19
|
||||
#endif
|
||||
|
||||
#ifndef RETRY_INDEFINITELY
|
||||
if (retry_count < NUMBER_2_RETRY) {
|
||||
#endif
|
||||
if (op1 == OP_SEQUENCE)
|
||||
nfs41_session_free_slot(session, args->sa_slotid);
|
||||
if (compound->res.status == NFS4ERR_GRACE)
|
||||
delayby = 5000;
|
||||
else
|
||||
delayby = 500*retry_count;
|
||||
dprintf(1, "Compound returned %s: sleeping for %ums..\n",
|
||||
(compound->res.status==NFS4ERR_GRACE)?"NFS4ERR_GRACE":"NFS4ERR_DELAY",
|
||||
delayby);
|
||||
Sleep(delayby);
|
||||
dprintf(1, "Attempting to resend compound.\n");
|
||||
goto do_retry;
|
||||
#ifndef RETRY_INDEFINITELY
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case NFS4ERR_FHEXPIRED: /* TODO: recover expired volatile filehandles */
|
||||
status = NFS4ERR_STALE; /* for now, treat them as ERR_STALE */
|
||||
/* no break */
|
||||
case NFS4ERR_STALE:
|
||||
{
|
||||
nfs_argop4 *argarray = compound->args.argarray;
|
||||
struct nfs41_name_cache *name_cache =
|
||||
session_name_cache(session);
|
||||
nfs41_putfh_args *putfh;
|
||||
uint32_t i, start = 0;
|
||||
|
||||
/* NFS4ERR_STALE generally comes from a PUTFH operation. in
|
||||
* this case, remove its filehandle from the name cache. but
|
||||
* because COMPOUNDs are not atomic, a file can be removed
|
||||
* between PUTFH and the operation that uses it. in this
|
||||
* case, we can't tell which PUTFH operation is to blame, so
|
||||
* we must invalidate filehandles of all PUTFH operations in
|
||||
* the COMPOUND */
|
||||
|
||||
if (argarray[compound->res.resarray_count-1].op == OP_PUTFH)
|
||||
start = compound->res.resarray_count-1;
|
||||
|
||||
for (i = start; i < compound->res.resarray_count; i++) {
|
||||
if (argarray[i].op == OP_PUTFH) {
|
||||
putfh = (nfs41_putfh_args*)argarray[i].arg;
|
||||
|
||||
if (!putfh->in_recovery && putfh->file->path)
|
||||
nfs41_name_cache_remove_stale(name_cache,
|
||||
session, putfh->file->path);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NFS4ERR_WRONGSEC:
|
||||
{
|
||||
nfs41_secinfo_info secinfo[MAX_SECINFOS] = { 0 };
|
||||
uint32_t rcount = compound->res.resarray_count;
|
||||
nfs_argop4 *argarray = compound->args.argarray;
|
||||
uint32_t op = argarray[rcount-1].op;
|
||||
nfs41_putfh_args *putfh;
|
||||
nfs41_path_fh *file = NULL;
|
||||
switch(op) {
|
||||
case OP_PUTFH:
|
||||
case OP_RESTOREFH:
|
||||
case OP_LINK:
|
||||
case OP_RENAME:
|
||||
case OP_PUTROOTFH:
|
||||
case OP_LOOKUP:
|
||||
case OP_OPEN:
|
||||
case OP_SECINFO_NO_NAME:
|
||||
case OP_SECINFO:
|
||||
if (op1 == OP_SEQUENCE)
|
||||
nfs41_session_free_slot(session, args->sa_slotid);
|
||||
/* from: 2.6.3.1.1.5. Put Filehandle Operation + SECINFO/SECINFO_NO_NAME
|
||||
* The NFSv4.1 server MUST NOT return NFS4ERR_WRONGSEC to a put
|
||||
* filehandle operation that is immediately followed by SECINFO or
|
||||
* SECINFO_NO_NAME. The NFSv4.1 server MUST NOT return NFS4ERR_WRONGSEC
|
||||
* from SECINFO or SECINFO_NO_NAME.
|
||||
*/
|
||||
if (op1 == OP_SEQUENCE &&
|
||||
(argarray[1].op == OP_PUTFH ||
|
||||
argarray[1].op == OP_PUTROOTFH) &&
|
||||
(argarray[2].op == OP_SECINFO_NO_NAME ||
|
||||
argarray[2].op == OP_SECINFO)) {
|
||||
dprintf(1, "SECINFO: BROKEN SERVER\n");
|
||||
goto out;
|
||||
}
|
||||
if (!try_recovery)
|
||||
goto out;
|
||||
if (!nfs41_recovery_start_or_wait(session->client))
|
||||
goto do_retry;
|
||||
|
||||
saved_sec_flavor = session->client->rpc->sec_flavor;
|
||||
saved_auth = session->client->rpc->rpc->cl_auth;
|
||||
if (op == OP_LOOKUP || op == OP_OPEN) {
|
||||
const nfs41_component *name;
|
||||
nfs41_path_fh tmp = { 0 };
|
||||
nfs41_getfh_res *getfh;
|
||||
nfs41_lookup_args *largs;
|
||||
nfs41_op_open_args *oargs;
|
||||
if (argarray[rcount-2].op == OP_PUTFH) {
|
||||
putfh = (nfs41_putfh_args *)argarray[rcount-2].arg;
|
||||
file = putfh->file;
|
||||
} else if (argarray[rcount-2].op == OP_GETATTR &&
|
||||
argarray[rcount-3].op == OP_GETFH) {
|
||||
getfh = (nfs41_getfh_res *)compound->res.resarray[rcount-3].res;
|
||||
memcpy(&tmp.fh, getfh->fh, sizeof(nfs41_fh));
|
||||
file = &tmp;
|
||||
}
|
||||
else {
|
||||
nfs41_recovery_finish(session->client);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (op == OP_LOOKUP) {
|
||||
largs = (nfs41_lookup_args *)argarray[rcount-1].arg;
|
||||
name = largs->name;
|
||||
} else if (op == OP_OPEN) {
|
||||
oargs = (nfs41_op_open_args *)argarray[rcount-1].arg;
|
||||
name = oargs->claim->u.null.filename;
|
||||
}
|
||||
secinfo_status = nfs41_secinfo(session, file, name, secinfo);
|
||||
if (secinfo_status) {
|
||||
eprintf("nfs41_secinfo failed with %d\n", secinfo_status);
|
||||
nfs41_recovery_finish(session->client);
|
||||
if (secinfo_status == NFS4ERR_BADSESSION) {
|
||||
if (op1 == OP_SEQUENCE)
|
||||
nfs41_session_free_slot(session, args->sa_slotid);
|
||||
goto do_retry;
|
||||
}
|
||||
goto out_free_slot;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (op == OP_PUTFH) {
|
||||
putfh = (nfs41_putfh_args *)argarray[rcount-1].arg;
|
||||
file = putfh->file;
|
||||
}
|
||||
secinfo_status = nfs41_secinfo_noname(session, file, secinfo);
|
||||
if (secinfo_status) {
|
||||
eprintf("nfs41_secinfo_noname failed with %d\n",
|
||||
secinfo_status);
|
||||
nfs41_recovery_finish(session->client);
|
||||
if (op1 == OP_SEQUENCE)
|
||||
nfs41_session_free_slot(session, args->sa_slotid);
|
||||
goto out_free_slot;
|
||||
}
|
||||
}
|
||||
secinfo_status = create_new_rpc_auth(session, op, secinfo);
|
||||
if (!secinfo_status) {
|
||||
auth_destroy(saved_auth);
|
||||
nfs41_recovery_finish(session->client);
|
||||
// Need to retry only
|
||||
goto do_retry;
|
||||
} else {
|
||||
AcquireSRWLockExclusive(&session->client->rpc->lock);
|
||||
session->client->rpc->sec_flavor = saved_sec_flavor;
|
||||
session->client->rpc->rpc->cl_auth = saved_auth;
|
||||
ReleaseSRWLockExclusive(&session->client->rpc->lock);
|
||||
nfs41_recovery_finish(session->client);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (compound->res.resarray[0].op == OP_SEQUENCE) {
|
||||
nfs41_sequence_res *seq =
|
||||
(nfs41_sequence_res *)compound->res.resarray[0].res;
|
||||
if (seq->sr_status == NFS4_OK && session->client->rpc->needcb &&
|
||||
(seq->sr_resok4.sr_status_flags & SEQ4_STATUS_CB_PATH_DOWN)) {
|
||||
nfs41_session_free_slot(session, args->sa_slotid);
|
||||
nfs41_bind_conn_to_session(session->client->rpc,
|
||||
session->session_id, CDFC4_BACK_OR_BOTH);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out_free_slot:
|
||||
if (op1 == OP_SEQUENCE)
|
||||
nfs41_session_free_slot(session, args->sa_slotid);
|
||||
out:
|
||||
return status;
|
||||
|
||||
do_retry:
|
||||
if (compound->res.resarray[0].op == OP_SEQUENCE)
|
||||
nfs41_session_get_slot(session, &args->sa_slotid,
|
||||
&args->sa_sequenceid, &args->sa_highest_slotid);
|
||||
goto retry;
|
||||
}
|
80
reactos/base/services/nfsd/nfs41_compound.h
Normal file
80
reactos/base/services/nfsd/nfs41_compound.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#ifndef __NFS41_DAEMON_COMPOUND_H__
|
||||
#define __NFS41_DAEMON_COMPOUND_H__
|
||||
|
||||
#include "nfs41.h"
|
||||
|
||||
|
||||
/* COMPOUND */
|
||||
typedef struct __nfs_argop4 {
|
||||
uint32_t op;
|
||||
void *arg;
|
||||
} nfs_argop4;
|
||||
|
||||
typedef struct __nfs41_compound_args {
|
||||
uint32_t tag_len;
|
||||
unsigned char tag[NFS4_OPAQUE_LIMIT];
|
||||
uint32_t minorversion;
|
||||
uint32_t argarray_count;
|
||||
nfs_argop4 *argarray; /* <> */
|
||||
} nfs41_compound_args;
|
||||
|
||||
typedef struct __nfs_resop4 {
|
||||
uint32_t op;
|
||||
void *res;
|
||||
} nfs_resop4;
|
||||
|
||||
typedef struct __nfs41_compound_res {
|
||||
uint32_t status;
|
||||
uint32_t tag_len;
|
||||
unsigned char tag[NFS4_OPAQUE_LIMIT];
|
||||
uint32_t resarray_count;
|
||||
nfs_resop4 *resarray; /* <> */
|
||||
} nfs41_compound_res;
|
||||
|
||||
typedef struct __nfs41_compound {
|
||||
nfs41_compound_args args;
|
||||
nfs41_compound_res res;
|
||||
} nfs41_compound;
|
||||
|
||||
|
||||
int compound_error(int status);
|
||||
|
||||
void compound_init(
|
||||
nfs41_compound *compound,
|
||||
nfs_argop4 *argops,
|
||||
nfs_resop4 *resops,
|
||||
const char *tag);
|
||||
|
||||
void compound_add_op(
|
||||
nfs41_compound *compound,
|
||||
uint32_t opnum,
|
||||
void *arg,
|
||||
void *res);
|
||||
|
||||
int compound_encode_send_decode(
|
||||
nfs41_session *session,
|
||||
nfs41_compound *compound,
|
||||
bool_t try_recovery);
|
||||
|
||||
#endif /* __NFS41_DAEMON_COMPOUND_H__ */
|
399
reactos/base/services/nfsd/nfs41_const.h
Normal file
399
reactos/base/services/nfsd/nfs41_const.h
Normal file
|
@ -0,0 +1,399 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#ifndef __NFS41_NFS_CONST_H__
|
||||
#define __NFS41_NFS_CONST_H__
|
||||
|
||||
|
||||
/*
|
||||
* Sizes
|
||||
*/
|
||||
#define NFS4_FHSIZE 128
|
||||
#define NFS4_VERIFIER_SIZE 8
|
||||
#define NFS4_OPAQUE_LIMIT 1024
|
||||
#define NFS4_SESSIONID_SIZE 16
|
||||
#define NFS4_STATEID_OTHER 12
|
||||
#define NFS4_EASIZE 256
|
||||
#define NFS4_EANAME_SIZE 128
|
||||
|
||||
|
||||
#define NFS41_MAX_FILEIO_SIZE (1024 * 1024)
|
||||
#define NFS41_MAX_SERVER_CACHE 1024
|
||||
#define NFS41_MAX_RPC_REQS 128
|
||||
|
||||
#define UPCALL_BUF_SIZE 2048
|
||||
|
||||
/* MaximumComponentNameLength reported for FileFsAttributeInformation */
|
||||
#define NFS41_MAX_COMPONENT_LEN 256
|
||||
#define NFS41_MAX_PATH_LEN 1280
|
||||
|
||||
#define NFS41_HOSTNAME_LEN 64
|
||||
#define NFS41_ADDRS_PER_SERVER 4
|
||||
|
||||
/* max length of ipv6 address 48
|
||||
* sizeof(".255.255") + 8 */
|
||||
#define NFS41_UNIVERSAL_ADDR_LEN 56
|
||||
|
||||
/* "udp" "tcp" "udp6" "tcp6" */
|
||||
#define NFS41_NETWORK_ID_LEN 4
|
||||
|
||||
/* msdn: There is a maximum of 31 reparse points (and
|
||||
* therefore symbolic links) allowed in a particular path. */
|
||||
#define NFS41_MAX_SYMLINK_DEPTH 31
|
||||
|
||||
|
||||
/* 424 bytes: max rpc header for reply with data */
|
||||
/* 32 bytes: max COMPOUND response */
|
||||
/* 40 bytes: max SEQUENCE response */
|
||||
/* 4 bytes: max PUTFH response */
|
||||
/* 12 bytes: max READ response */
|
||||
#define READ_OVERHEAD 512
|
||||
|
||||
/* 840 bytes: max rpc header for call */
|
||||
/* 32 bytes: max COMPOUND request */
|
||||
/* 32 bytes: max SEQUENCE request */
|
||||
/* 132 bytes: max PUTFH request */
|
||||
/* 32 bytes: max WRITE request */
|
||||
#define WRITE_OVERHEAD 1068
|
||||
|
||||
|
||||
#define NFS41_RPC_PROGRAM 100003
|
||||
#define NFS41_RPC_VERSION 4
|
||||
#define NFS41_RPC_CBPROGRAM 0x2358
|
||||
|
||||
|
||||
/*
|
||||
* Error status
|
||||
*/
|
||||
enum nfsstat4 {
|
||||
NFS4_OK = 0, /* everything is okay */
|
||||
NFS4ERR_PERM = 1, /* caller not privileged */
|
||||
NFS4ERR_NOENT = 2, /* no such file/directory */
|
||||
NFS4ERR_IO = 5, /* hard I/O error */
|
||||
NFS4ERR_NXIO = 6, /* no such device */
|
||||
NFS4ERR_ACCESS = 13, /* access denied */
|
||||
NFS4ERR_EXIST = 17, /* file already exists */
|
||||
NFS4ERR_XDEV = 18, /* different filesystems */
|
||||
|
||||
NFS4ERR_NOTDIR = 20, /* should be a directory */
|
||||
NFS4ERR_ISDIR = 21, /* should not be directory */
|
||||
NFS4ERR_INVAL = 22, /* invalid argument */
|
||||
NFS4ERR_FBIG = 27, /* file exceeds server max */
|
||||
NFS4ERR_NOSPC = 28, /* no space on filesystem */
|
||||
NFS4ERR_ROFS = 30, /* read-only filesystem */
|
||||
NFS4ERR_MLINK = 31, /* too many hard links */
|
||||
NFS4ERR_NAMETOOLONG = 63, /* name exceeds server max */
|
||||
NFS4ERR_NOTEMPTY = 66, /* directory not empty */
|
||||
NFS4ERR_DQUOT = 69, /* hard quota limit reached*/
|
||||
NFS4ERR_STALE = 70, /* file no longer exists */
|
||||
NFS4ERR_BADHANDLE = 10001, /* Illegal filehandle */
|
||||
NFS4ERR_BAD_COOKIE = 10003, /* READDIR cookie is stale */
|
||||
NFS4ERR_NOTSUPP = 10004, /* operation not supported */
|
||||
NFS4ERR_TOOSMALL = 10005, /* response limit exceeded */
|
||||
NFS4ERR_SERVERFAULT = 10006, /* undefined server error */
|
||||
NFS4ERR_BADTYPE = 10007, /* type invalid for CREATE */
|
||||
NFS4ERR_DELAY = 10008, /* file "busy" - retry */
|
||||
NFS4ERR_SAME = 10009, /* nverify says attrs same */
|
||||
NFS4ERR_DENIED = 10010, /* lock unavailable */
|
||||
NFS4ERR_EXPIRED = 10011, /* lock lease expired */
|
||||
NFS4ERR_LOCKED = 10012, /* I/O failed due to lock */
|
||||
NFS4ERR_GRACE = 10013, /* in grace period */
|
||||
NFS4ERR_FHEXPIRED = 10014, /* filehandle expired */
|
||||
NFS4ERR_SHARE_DENIED = 10015, /* share reserve denied */
|
||||
NFS4ERR_WRONGSEC = 10016, /* wrong security flavor */
|
||||
NFS4ERR_CLID_INUSE = 10017, /* clientid in use */
|
||||
|
||||
/* NFS4ERR_RESOURCE is not a valid error in NFSv4.1 */
|
||||
NFS4ERR_RESOURCE = 10018, /* resource exhaustion */
|
||||
NFS4ERR_MOVED = 10019, /* filesystem relocated */
|
||||
NFS4ERR_NOFILEHANDLE = 10020, /* current FH is not set */
|
||||
NFS4ERR_MINOR_VERS_MISMATCH = 10021, /* minor vers not supp */
|
||||
NFS4ERR_STALE_CLIENTID = 10022, /* server has rebooted */
|
||||
NFS4ERR_STALE_STATEID = 10023, /* server has rebooted */
|
||||
NFS4ERR_OLD_STATEID = 10024, /* state is out of sync */
|
||||
NFS4ERR_BAD_STATEID = 10025, /* incorrect stateid */
|
||||
NFS4ERR_BAD_SEQID = 10026, /* request is out of seq. */
|
||||
NFS4ERR_NOT_SAME = 10027, /* verify - attrs not same */
|
||||
NFS4ERR_LOCK_RANGE = 10028, /* overlapping lock range */
|
||||
NFS4ERR_SYMLINK = 10029, /* should be file/directory*/
|
||||
NFS4ERR_RESTOREFH = 10030, /* no saved filehandle */
|
||||
NFS4ERR_LEASE_MOVED = 10031, /* some filesystem moved */
|
||||
NFS4ERR_ATTRNOTSUPP = 10032, /* recommended attr not sup*/
|
||||
NFS4ERR_NO_GRACE = 10033, /* reclaim outside of grace*/
|
||||
NFS4ERR_RECLAIM_BAD = 10034, /* reclaim error at server */
|
||||
NFS4ERR_RECLAIM_CONFLICT = 10035, /* conflict on reclaim */
|
||||
NFS4ERR_BADXDR = 10036, /* XDR decode failed */
|
||||
NFS4ERR_LOCKS_HELD = 10037, /* file locks held at CLOSE*/
|
||||
NFS4ERR_OPENMODE = 10038, /* conflict in OPEN and I/O*/
|
||||
NFS4ERR_BADOWNER = 10039, /* owner translation bad */
|
||||
NFS4ERR_BADCHAR = 10040, /* utf-8 char not supported*/
|
||||
NFS4ERR_BADNAME = 10041, /* name not supported */
|
||||
NFS4ERR_BAD_RANGE = 10042, /* lock range not supported*/
|
||||
NFS4ERR_LOCK_NOTSUPP = 10043, /* no atomic up/downgrade */
|
||||
NFS4ERR_OP_ILLEGAL = 10044, /* undefined operation */
|
||||
NFS4ERR_DEADLOCK = 10045, /* file locking deadlock */
|
||||
NFS4ERR_FILE_OPEN = 10046, /* open file blocks op. */
|
||||
NFS4ERR_ADMIN_REVOKED = 10047, /* lockowner state revoked */
|
||||
NFS4ERR_CB_PATH_DOWN = 10048, /* callback path down */
|
||||
|
||||
/* NFSv4.1 errors start here. */
|
||||
NFS4ERR_BADIOMODE = 10049,
|
||||
NFS4ERR_BADLAYOUT = 10050,
|
||||
NFS4ERR_BAD_SESSION_DIGEST = 10051,
|
||||
NFS4ERR_BADSESSION = 10052,
|
||||
NFS4ERR_BADSLOT = 10053,
|
||||
NFS4ERR_COMPLETE_ALREADY = 10054,
|
||||
NFS4ERR_CONN_NOT_BOUND_TO_SESSION = 10055,
|
||||
NFS4ERR_DELEG_ALREADY_WANTED = 10056,
|
||||
NFS4ERR_BACK_CHAN_BUSY = 10057, /*backchan reqs outstanding*/
|
||||
NFS4ERR_LAYOUTTRYLATER = 10058,
|
||||
NFS4ERR_LAYOUTUNAVAILABLE = 10059,
|
||||
NFS4ERR_NOMATCHING_LAYOUT = 10060,
|
||||
NFS4ERR_RECALLCONFLICT = 10061,
|
||||
NFS4ERR_UNKNOWN_LAYOUTTYPE = 10062,
|
||||
NFS4ERR_SEQ_MISORDERED = 10063, /* unexpected seq.ID in req*/
|
||||
NFS4ERR_SEQUENCE_POS = 10064, /* [CB_]SEQ. op not 1st op */
|
||||
NFS4ERR_REQ_TOO_BIG = 10065, /* request too big */
|
||||
NFS4ERR_REP_TOO_BIG = 10066, /* reply too big */
|
||||
NFS4ERR_REP_TOO_BIG_TO_CACHE = 10067, /* rep. not all cached */
|
||||
NFS4ERR_RETRY_UNCACHED_REP = 10068, /* retry & rep. uncached */
|
||||
NFS4ERR_UNSAFE_COMPOUND = 10069, /* retry/recovery too hard */
|
||||
NFS4ERR_TOO_MANY_OPS = 10070, /*too many ops in [CB_]COMP*/
|
||||
NFS4ERR_OP_NOT_IN_SESSION = 10071, /* op needs [CB_]SEQ. op */
|
||||
NFS4ERR_HASH_ALG_UNSUPP = 10072, /* hash alg. not supp. */
|
||||
/* Error 10073 is unused. */
|
||||
NFS4ERR_CLIENTID_BUSY = 10074, /* clientid has state */
|
||||
NFS4ERR_PNFS_IO_HOLE = 10075, /* IO to _SPARSE file hole */
|
||||
NFS4ERR_SEQ_FALSE_RETRY = 10076, /* Retry != original req. */
|
||||
NFS4ERR_BAD_HIGH_SLOT = 10077, /* req has bad highest_slot*/
|
||||
NFS4ERR_DEADSESSION = 10078, /*new req sent to dead sess*/
|
||||
NFS4ERR_ENCR_ALG_UNSUPP = 10079, /* encr alg. not supp. */
|
||||
NFS4ERR_PNFS_NO_LAYOUT = 10080, /* I/O without a layout */
|
||||
NFS4ERR_NOT_ONLY_OP = 10081, /* addl ops not allowed */
|
||||
NFS4ERR_WRONG_CRED = 10082, /* op done by wrong cred */
|
||||
NFS4ERR_WRONG_TYPE = 10083, /* op on wrong type object */
|
||||
NFS4ERR_DIRDELEG_UNAVAIL = 10084, /* delegation not avail. */
|
||||
NFS4ERR_REJECT_DELEG = 10085, /* cb rejected delegation */
|
||||
NFS4ERR_RETURNCONFLICT = 10086, /* layout get before return*/
|
||||
NFS4ERR_DELEG_REVOKED = 10087 /* deleg./layout revoked */
|
||||
};
|
||||
|
||||
#define MAKE_WORD0(XXX) (1 << XXX)
|
||||
#define MAKE_WORD1(XXX) (1 << (XXX-32))
|
||||
#define MAKE_WORD2(XXX) (1 << (XXX-64))
|
||||
|
||||
enum {
|
||||
/*
|
||||
* Mandatory Attributes
|
||||
*/
|
||||
FATTR4_WORD0_SUPPORTED_ATTRS = MAKE_WORD0(0),
|
||||
FATTR4_WORD0_TYPE = MAKE_WORD0(1),
|
||||
FATTR4_WORD0_FH_EXPIRE_TYPE = MAKE_WORD0(2),
|
||||
FATTR4_WORD0_CHANGE = MAKE_WORD0(3),
|
||||
FATTR4_WORD0_SIZE = MAKE_WORD0(4),
|
||||
FATTR4_WORD0_LINK_SUPPORT = MAKE_WORD0(5),
|
||||
FATTR4_WORD0_SYMLINK_SUPPORT = MAKE_WORD0(6),
|
||||
FATTR4_WORD0_NAMED_ATTR = MAKE_WORD0(7),
|
||||
FATTR4_WORD0_FSID = MAKE_WORD0(8),
|
||||
FATTR4_WORD0_UNIQUE_HANDLES = MAKE_WORD0(9),
|
||||
FATTR4_WORD0_LEASE_TIME = MAKE_WORD0(10),
|
||||
FATTR4_WORD0_RDATTR_ERROR = MAKE_WORD0(11),
|
||||
FATTR4_WORD0_FILEHANDLE = MAKE_WORD0(19),
|
||||
FATTR4_WORD2_SUPPATTR_EXCLCREAT = MAKE_WORD2(75),
|
||||
|
||||
/*
|
||||
* Recommended Attributes
|
||||
*/
|
||||
FATTR4_WORD0_ACL = MAKE_WORD0(12),
|
||||
FATTR4_WORD0_ACLSUPPORT = MAKE_WORD0(13),
|
||||
FATTR4_WORD0_ARCHIVE = MAKE_WORD0(14),
|
||||
FATTR4_WORD0_CANSETTIME = MAKE_WORD0(15),
|
||||
FATTR4_WORD0_CASE_INSENSITIVE = MAKE_WORD0(16),
|
||||
FATTR4_WORD0_CASE_PRESERVING = MAKE_WORD0(17),
|
||||
FATTR4_WORD0_CHOWN_RESTRICTED = MAKE_WORD0(18),
|
||||
FATTR4_WORD0_FILEID = MAKE_WORD0(20),
|
||||
FATTR4_WORD0_FILES_AVAIL = MAKE_WORD0(21),
|
||||
FATTR4_WORD0_FILES_FREE = MAKE_WORD0(22),
|
||||
FATTR4_WORD0_FILES_TOTAL = MAKE_WORD0(23),
|
||||
FATTR4_WORD0_FS_LOCATIONS = MAKE_WORD0(24),
|
||||
FATTR4_WORD0_HIDDEN = MAKE_WORD0(25),
|
||||
FATTR4_WORD0_HOMOGENEOUS = MAKE_WORD0(26),
|
||||
FATTR4_WORD0_MAXFILESIZE = MAKE_WORD0(27),
|
||||
FATTR4_WORD0_MAXLINK = MAKE_WORD0(28),
|
||||
FATTR4_WORD0_MAXNAME = MAKE_WORD0(29),
|
||||
FATTR4_WORD0_MAXREAD = MAKE_WORD0(30),
|
||||
FATTR4_WORD0_MAXWRITE = MAKE_WORD0(31),
|
||||
FATTR4_WORD1_MIMETYPE = MAKE_WORD1(32),
|
||||
FATTR4_WORD1_MODE = MAKE_WORD1(33),
|
||||
FATTR4_WORD1_NO_TRUNC = MAKE_WORD1(34),
|
||||
FATTR4_WORD1_NUMLINKS = MAKE_WORD1(35),
|
||||
FATTR4_WORD1_OWNER = MAKE_WORD1(36),
|
||||
FATTR4_WORD1_OWNER_GROUP = MAKE_WORD1(37),
|
||||
FATTR4_WORD1_QUOTA_AVAIL_HARD = MAKE_WORD1(38),
|
||||
FATTR4_WORD1_QUOTA_AVAIL_SOFT = MAKE_WORD1(39),
|
||||
FATTR4_WORD1_QUOTA_USED = MAKE_WORD1(40),
|
||||
FATTR4_WORD1_RAWDEV = MAKE_WORD1(41),
|
||||
FATTR4_WORD1_SPACE_AVAIL = MAKE_WORD1(42),
|
||||
FATTR4_WORD1_SPACE_FREE = MAKE_WORD1(43),
|
||||
FATTR4_WORD1_SPACE_TOTAL = MAKE_WORD1(44),
|
||||
FATTR4_WORD1_SPACE_USED = MAKE_WORD1(45),
|
||||
FATTR4_WORD1_SYSTEM = MAKE_WORD1(46),
|
||||
FATTR4_WORD1_TIME_ACCESS = MAKE_WORD1(47),
|
||||
FATTR4_WORD1_TIME_ACCESS_SET = MAKE_WORD1(48),
|
||||
FATTR4_WORD1_TIME_BACKUP = MAKE_WORD1(49),
|
||||
FATTR4_WORD1_TIME_CREATE = MAKE_WORD1(50),
|
||||
FATTR4_WORD1_TIME_DELTA = MAKE_WORD1(51),
|
||||
FATTR4_WORD1_TIME_METADATA = MAKE_WORD1(52),
|
||||
FATTR4_WORD1_TIME_MODIFY = MAKE_WORD1(53),
|
||||
FATTR4_WORD1_TIME_MODIFY_SET = MAKE_WORD1(54),
|
||||
FATTR4_WORD1_MOUNTED_ON_FILEID = MAKE_WORD1(55),
|
||||
FATTR4_WORD1_DIR_NOTIF_DELAY = MAKE_WORD1(56),
|
||||
FATTR4_WORD1_DIRENT_NOTIF_DELAY = MAKE_WORD1(57),
|
||||
FATTR4_WORD1_DACL = MAKE_WORD1(58),
|
||||
FATTR4_WORD1_SACL = MAKE_WORD1(59),
|
||||
FATTR4_WORD1_CHANGE_POLICY = MAKE_WORD1(60),
|
||||
FATTR4_WORD1_FS_STATUS = MAKE_WORD1(61),
|
||||
FATTR4_WORD1_FS_LAYOUT_TYPE = MAKE_WORD1(62),
|
||||
FATTR4_WORD1_LAYOUT_HINT = MAKE_WORD1(63),
|
||||
FATTR4_WORD2_LAYOUT_TYPE = MAKE_WORD2(64),
|
||||
FATTR4_WORD2_LAYOUT_BLKSIZE = MAKE_WORD2(65),
|
||||
FATTR4_WORD2_LAYOUT_ALIGNMENT = MAKE_WORD2(66),
|
||||
FATTR4_WORD2_FS_LOCATIONS_INFO = MAKE_WORD2(67),
|
||||
FATTR4_WORD2_MDSTHRESHOLD = MAKE_WORD2(68),
|
||||
FATTR4_WORD2_RETENTION_GET = MAKE_WORD2(69),
|
||||
FATTR4_WORD2_RETENTION_SET = MAKE_WORD2(70),
|
||||
FATTR4_WORD2_RETENTEVT_GET = MAKE_WORD2(71),
|
||||
FATTR4_WORD2_RETENTEVT_SET = MAKE_WORD2(72),
|
||||
FATTR4_WORD2_RETENTION_HOLD = MAKE_WORD2(73),
|
||||
FATTR4_WORD2_MODE_SET_MASKED = MAKE_WORD2(74),
|
||||
FATTR4_WORD2_FS_CHARSET_CAP = MAKE_WORD2(76),
|
||||
};
|
||||
|
||||
/*
|
||||
* File types
|
||||
*/
|
||||
enum nfs_ftype4 {
|
||||
NF4REG = 1, /* Regular File */
|
||||
NF4DIR = 2, /* Directory */
|
||||
NF4BLK = 3, /* Special File - block device */
|
||||
NF4CHR = 4, /* Special File - character device */
|
||||
NF4LNK = 5, /* Symbolic Link */
|
||||
NF4SOCK = 6, /* Special File - socket */
|
||||
NF4FIFO = 7, /* Special File - fifo */
|
||||
NF4ATTRDIR = 8, /* Attribute Directory */
|
||||
NF4NAMEDATTR = 9, /* Named Attribute */
|
||||
|
||||
NFS_FTYPE_MASK = 0xF
|
||||
};
|
||||
|
||||
#define CREATE_SESSION4_FLAG_PERSIST 0x00000001
|
||||
#define CREATE_SESSION4_FLAG_CONN_BACK_CHAN 0x00000002
|
||||
#define CREATE_SESSION4_FLAG_CONN_RDMA 0x00000004
|
||||
|
||||
/* ACLS aclsupport attribute values */
|
||||
#define ACL4_SUPPORT_ALLOW_ACL 0x00000001
|
||||
#define ACL4_SUPPORT_DENY_ACL 0x00000002
|
||||
#define ACL4_SUPPORT_AUDIT_ACL 0x00000004
|
||||
#define ACL4_SUPPORT_ALARM_ACL 0x00000008
|
||||
|
||||
/* ACLS acetype4 field constants */
|
||||
#define ACE4_ACCESS_ALLOWED_ACE_TYPE 0x00000000
|
||||
#define ACE4_ACCESS_DENIED_ACE_TYPE 0x00000001
|
||||
#define ACE4_SYSTEM_AUDIT_ACE_TYPE 0x00000002
|
||||
#define ACE4_SYSTEM_ALARM_ACE_TYPE 0x00000003
|
||||
|
||||
/* ACLS acemask4 field constants */
|
||||
#define ACE4_READ_DATA 0x00000001
|
||||
#define ACE4_LIST_DIRECTORY 0x00000001
|
||||
#define ACE4_WRITE_DATA 0x00000002
|
||||
#define ACE4_ADD_FILE 0x00000002
|
||||
#define ACE4_APPEND_DATA 0x00000004
|
||||
#define ACE4_ADD_SUBDIRECTORY 0x00000004
|
||||
#define ACE4_READ_NAMED_ATTRS 0x00000008
|
||||
#define ACE4_WRITE_NAMED_ATTRS 0x00000010
|
||||
#define ACE4_EXECUTE 0x00000020
|
||||
#define ACE4_DELETE_CHILD 0x00000040
|
||||
#define ACE4_READ_ATTRIBUTES 0x00000080
|
||||
#define ACE4_WRITE_ATTRIBUTES 0x00000100
|
||||
#define ACE4_WRITE_RETENTION 0x00000200
|
||||
#define ACE4_WRITE_RETENTION_HOLD 0x00000400
|
||||
|
||||
#define ACE4_DELETE 0x00010000
|
||||
#define ACE4_READ_ACL 0x00020000
|
||||
#define ACE4_WRITE_ACL 0x00040000
|
||||
#define ACE4_WRITE_OWNER 0x00080000
|
||||
#define ACE4_SYNCHRONIZE 0x00100000
|
||||
|
||||
#define ACE4_ALL_FILE ACE4_READ_DATA|ACE4_WRITE_DATA|ACE4_APPEND_DATA| \
|
||||
ACE4_READ_NAMED_ATTRS|ACE4_WRITE_NAMED_ATTRS|ACE4_EXECUTE| \
|
||||
ACE4_READ_ATTRIBUTES|ACE4_WRITE_ATTRIBUTES| \
|
||||
ACE4_DELETE|ACE4_READ_ACL|ACE4_WRITE_ACL|ACE4_WRITE_OWNER| \
|
||||
ACE4_SYNCHRONIZE
|
||||
#define ACE4_ALL_DIR ACE4_READ_DATA|ACE4_WRITE_DATA|ACE4_APPEND_DATA| \
|
||||
ACE4_READ_NAMED_ATTRS|ACE4_WRITE_NAMED_ATTRS|ACE4_EXECUTE| \
|
||||
ACE4_DELETE_CHILD|ACE4_READ_ATTRIBUTES|ACE4_WRITE_ATTRIBUTES| \
|
||||
ACE4_DELETE|ACE4_READ_ACL|ACE4_WRITE_ACL|ACE4_WRITE_OWNER| \
|
||||
ACE4_SYNCHRONIZE
|
||||
|
||||
#define ACE4_GENERIC_READ ACE4_READ_DATA|ACE4_READ_NAMED_ATTRS| \
|
||||
ACE4_READ_ATTRIBUTES|ACE4_READ_ACL|ACE4_SYNCHRONIZE
|
||||
#define ACE4_GENERIC_WRITE ACE4_WRITE_DATA|ACE4_WRITE_NAMED_ATTRS| \
|
||||
ACE4_WRITE_ATTRIBUTES|ACE4_READ_ACL|ACE4_SYNCHRONIZE
|
||||
#define ACE4_GENERIC_EXECUTE ACE4_EXECUTE|ACE4_READ_ATTRIBUTES| \
|
||||
ACE4_READ_ACL|ACE4_SYNCHRONIZE
|
||||
|
||||
|
||||
|
||||
#define ACE4_FILE_ALL_ACCESS ACE4_READ_DATA|ACE4_LIST_DIRECTORY| \
|
||||
ACE4_WRITE_DATA|ACE4_ADD_FILE|ACE4_APPEND_DATA|ACE4_ADD_SUBDIRECTORY| \
|
||||
ACE4_READ_NAMED_ATTRS|ACE4_WRITE_NAMED_ATTRS|ACE4_EXECUTE| \
|
||||
ACE4_READ_ATTRIBUTES|ACE4_WRITE_ATTRIBUTES
|
||||
|
||||
/* ACLS aceflag4 field constants */
|
||||
#define ACE4_FILE_INHERIT_ACE 0x00000001
|
||||
#define ACE4_DIRECTORY_INHERIT_ACE 0x00000002
|
||||
#define ACE4_NO_PROPAGATE_INHERIT_ACE 0x00000004
|
||||
#define ACE4_INHERIT_ONLY_ACE 0x00000008
|
||||
#define ACE4_SUCCESSFUL_ACCESS_ACE_FLAG 0x00000010
|
||||
#define ACE4_FAILED_ACCESS_ACE_FLAG 0x00000020
|
||||
#define ACE4_IDENTIFIER_GROUP 0x00000040
|
||||
#define ACE4_INHERITED_ACE 0x00000080
|
||||
|
||||
/* ACLS well-defined WHOs */
|
||||
#define ACE4_OWNER "OWNER@"
|
||||
#define ACE4_GROUP "GROUP@"
|
||||
#define ACE4_EVERYONE "EVERYONE@"
|
||||
#define ACE4_INTERACTIVE "INTERACTIVE@"
|
||||
#define ACE4_NETWORK "NETWORK@"
|
||||
#define ACE4_DIALUP "DIALUP@"
|
||||
#define ACE4_BATCH "BATCH@"
|
||||
#define ACE4_ANONYMOUS "ANONYMOUS@"
|
||||
#define ACE4_AUTHENTICATED "AUTHENTICATED@"
|
||||
#define ACE4_SERVICE "SERVICE@"
|
||||
#define ACE4_NOBODY "nobody"
|
||||
|
||||
/* ACLE nfsacl41 aclflag4 constants */
|
||||
#define ACL4_AUTO_INHERIT 0x00000001
|
||||
#define ACL4_PROTECTED 0x00000002
|
||||
#define ACL4_DEFAULTED 0x00000004
|
||||
|
||||
|
||||
#endif /* !__NFS41_NFS_CONST_H__ */
|
501
reactos/base/services/nfsd/nfs41_daemon.c
Normal file
501
reactos/base/services/nfsd/nfs41_daemon.c
Normal file
|
@ -0,0 +1,501 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <process.h>
|
||||
#include <tchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <devioctl.h>
|
||||
#include <lmcons.h> /* UNLEN for GetUserName() */
|
||||
#include <iphlpapi.h> /* for GetNetworkParam() */
|
||||
#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;
|
||||
|
||||
static const char FILE_NETCONFIG[] = "C:\\ReactOS\\System32\\drivers\\etc\\netconfig";
|
||||
|
||||
/* Globals */
|
||||
char localdomain_name[NFS41_HOSTNAME_LEN];
|
||||
int default_uid = 666;
|
||||
int default_gid = 777;
|
||||
|
||||
#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 = default_uid;
|
||||
*gid = default_gid;
|
||||
}
|
||||
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], inbuf[UPCALL_BUF_SIZE];
|
||||
DWORD inbuf_len = UPCALL_BUF_SIZE, 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=%lld opcode=%s status=%d "
|
||||
"get_last_error=%d\n", upcall.xid, opcode2string(upcall.opcode),
|
||||
upcall.status, upcall.last_error);
|
||||
|
||||
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);
|
||||
if (!status) {
|
||||
eprintf("IOCTL_NFS41_WRITE failed with %d xid=%lld opcode=%s\n",
|
||||
GetLastError(), upcall.xid, opcode2string(upcall.opcode));
|
||||
upcall_cancel(&upcall);
|
||||
}
|
||||
if (upcall.status != NFSD_VERSION_MISMATCH)
|
||||
upcall_cleanup(&upcall);
|
||||
}
|
||||
CloseHandle(pipe);
|
||||
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
#ifndef STANDALONE_NFSD
|
||||
VOID ServiceStop()
|
||||
{
|
||||
if (stop_event)
|
||||
SetEvent(stop_event);
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef struct _nfsd_args {
|
||||
bool_t ldap_enable;
|
||||
int debug_level;
|
||||
} nfsd_args;
|
||||
|
||||
static bool_t check_for_files()
|
||||
{
|
||||
FILE *fd;
|
||||
|
||||
fd = fopen(FILE_NETCONFIG, "r");
|
||||
if (fd == NULL) {
|
||||
fprintf(stderr,"nfsd() failed to open file '%s'\n", FILE_NETCONFIG);
|
||||
return FALSE;
|
||||
}
|
||||
fclose(fd);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void PrintUsage()
|
||||
{
|
||||
fprintf(stderr, "Usage: nfsd.exe -d <debug_level> --noldap "
|
||||
"--uid <non-zero value> --gid\n");
|
||||
}
|
||||
static bool_t parse_cmdlineargs(int argc, TCHAR *argv[], nfsd_args *out)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* set defaults. */
|
||||
out->debug_level = 1;
|
||||
out->ldap_enable = TRUE;
|
||||
|
||||
/* parse command line */
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (argv[i][0] == TEXT('-')) {
|
||||
if (_tcscmp(argv[i], TEXT("-h")) == 0) { /* help */
|
||||
PrintUsage();
|
||||
return FALSE;
|
||||
}
|
||||
else if (_tcscmp(argv[i], TEXT("-d")) == 0) { /* debug level */
|
||||
++i;
|
||||
if (i >= argc) {
|
||||
fprintf(stderr, "Missing debug level value\n");
|
||||
PrintUsage();
|
||||
return FALSE;
|
||||
}
|
||||
out->debug_level = _ttoi(argv[i]);
|
||||
}
|
||||
else if (_tcscmp(argv[i], TEXT("--noldap")) == 0) { /* no LDAP */
|
||||
out->ldap_enable = FALSE;
|
||||
}
|
||||
else if (_tcscmp(argv[i], TEXT("--uid")) == 0) { /* no LDAP, setting default uid */
|
||||
++i;
|
||||
if (i >= argc) {
|
||||
fprintf(stderr, "Missing uid value\n");
|
||||
PrintUsage();
|
||||
return FALSE;
|
||||
}
|
||||
default_uid = _ttoi(argv[i]);
|
||||
if (!default_uid) {
|
||||
fprintf(stderr, "Invalid (or missing) anonymous uid value of %d\n",
|
||||
default_uid);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if (_tcscmp(argv[i], TEXT("--gid")) == 0) { /* no LDAP, setting default gid */
|
||||
++i;
|
||||
if (i >= argc) {
|
||||
fprintf(stderr, "Missing gid value\n");
|
||||
PrintUsage();
|
||||
return FALSE;
|
||||
}
|
||||
default_gid = _ttoi(argv[i]);
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "Unrecognized option '%s', disregarding.\n", argv[i]);
|
||||
}
|
||||
}
|
||||
fprintf(stdout, "parse_cmdlineargs: debug_level %d ldap is %d\n",
|
||||
out->debug_level, out->ldap_enable);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void print_getaddrinfo(struct addrinfo *ptr)
|
||||
{
|
||||
char ipstringbuffer[46];
|
||||
DWORD ipbufferlength = 46;
|
||||
|
||||
dprintf(1, "getaddrinfo response flags: 0x%x\n", ptr->ai_flags);
|
||||
switch (ptr->ai_family) {
|
||||
case AF_UNSPEC: dprintf(1, "Family: Unspecified\n"); break;
|
||||
case AF_INET:
|
||||
dprintf(1, "Family: AF_INET IPv4 address %s\n",
|
||||
inet_ntoa(((struct sockaddr_in *)ptr->ai_addr)->sin_addr));
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (WSAAddressToString((LPSOCKADDR)ptr->ai_addr, (DWORD)ptr->ai_addrlen,
|
||||
NULL, ipstringbuffer, &ipbufferlength))
|
||||
dprintf(1, "WSAAddressToString failed with %u\n", WSAGetLastError());
|
||||
else
|
||||
dprintf(1, "Family: AF_INET6 IPv6 address %s\n", ipstringbuffer);
|
||||
break;
|
||||
case AF_NETBIOS: dprintf(1, "AF_NETBIOS (NetBIOS)\n"); break;
|
||||
default: dprintf(1, "Other %ld\n", ptr->ai_family); break;
|
||||
}
|
||||
dprintf(1, "Canonical name: %s\n", ptr->ai_canonname);
|
||||
}
|
||||
|
||||
static int getdomainname()
|
||||
{
|
||||
int status = 0;
|
||||
PFIXED_INFO net_info = NULL;
|
||||
DWORD size = 0;
|
||||
BOOLEAN flag = FALSE;
|
||||
|
||||
status = GetNetworkParams(net_info, &size);
|
||||
if (status != ERROR_BUFFER_OVERFLOW) {
|
||||
eprintf("getdomainname: GetNetworkParams returned %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
net_info = calloc(1, size);
|
||||
if (net_info == NULL) {
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
status = GetNetworkParams(net_info, &size);
|
||||
if (status) {
|
||||
eprintf("getdomainname: GetNetworkParams returned %d\n", status);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (net_info->DomainName[0] == '\0') {
|
||||
struct addrinfo *result = NULL, *ptr = NULL, hints = { 0 };
|
||||
char hostname[NI_MAXHOST], servInfo[NI_MAXSERV];
|
||||
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
status = getaddrinfo(net_info->HostName, NULL, &hints, &result);
|
||||
if (status) {
|
||||
status = WSAGetLastError();
|
||||
eprintf("getdomainname: getaddrinfo failed with %d\n", status);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
for (ptr=result; ptr != NULL; ptr=ptr->ai_next) {
|
||||
print_getaddrinfo(ptr);
|
||||
|
||||
switch (ptr->ai_family) {
|
||||
case AF_INET6:
|
||||
case AF_INET:
|
||||
status = getnameinfo((struct sockaddr *)ptr->ai_addr,
|
||||
(socklen_t)ptr->ai_addrlen, hostname, NI_MAXHOST,
|
||||
servInfo, NI_MAXSERV, NI_NAMEREQD);
|
||||
if (status)
|
||||
dprintf(1, "getnameinfo failed %d\n", WSAGetLastError());
|
||||
else {
|
||||
size_t i, len = strlen(hostname);
|
||||
char *p = hostname;
|
||||
dprintf(1, "getdomainname: hostname %s %d\n", hostname, len);
|
||||
for (i = 0; i < len; i++)
|
||||
if (p[i] == '.')
|
||||
break;
|
||||
if (i == len)
|
||||
break;
|
||||
flag = TRUE;
|
||||
memcpy(localdomain_name, &hostname[i+1], len-i);
|
||||
dprintf(1, "getdomainname: domainname %s %d\n",
|
||||
localdomain_name, strlen(localdomain_name));
|
||||
goto out_loop;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
out_loop:
|
||||
if (!flag) {
|
||||
status = ERROR_INTERNAL_ERROR;
|
||||
eprintf("getdomainname: unable to get a domain name. "
|
||||
"Set this machine's domain name:\n"
|
||||
"System > ComputerName > Change > More > mydomain\n");
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
} else {
|
||||
dprintf(1, "domain name is %s\n", net_info->DomainName);
|
||||
memcpy(localdomain_name, net_info->DomainName,
|
||||
strlen(net_info->DomainName));
|
||||
localdomain_name[strlen(net_info->DomainName)] = '\0';
|
||||
}
|
||||
out_free:
|
||||
free(net_info);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
#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 = NULL;
|
||||
int i;
|
||||
nfsd_args cmd_args;
|
||||
|
||||
if (!check_for_files())
|
||||
exit(0);
|
||||
if (!parse_cmdlineargs(argc, argv, &cmd_args))
|
||||
exit(0);
|
||||
set_debug_level(cmd_args.debug_level);
|
||||
open_log_files();
|
||||
|
||||
#ifdef __REACTOS__
|
||||
/* Start the kernel part */
|
||||
{
|
||||
HANDLE hSvcMan = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
||||
if (hSvcMan)
|
||||
{
|
||||
HANDLE hSvc = OpenService(hSvcMan, "nfs41_driver", SERVICE_ALL_ACCESS);
|
||||
if (hSvc)
|
||||
{
|
||||
SERVICE_STATUS SvcSt;
|
||||
QueryServiceStatus(hSvc, &SvcSt);
|
||||
if (SvcSt.dwCurrentState != SERVICE_RUNNING)
|
||||
{
|
||||
if (StartService(hSvc, 0, NULL))
|
||||
{
|
||||
dprintf(1, "NFS41 driver started\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
eprintf("Driver failed to start: %d\n", GetLastError());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
eprintf("Driver in state: %x\n", SvcSt.dwCurrentState);
|
||||
}
|
||||
|
||||
CloseServiceHandle(hSvc);
|
||||
}
|
||||
else
|
||||
{
|
||||
eprintf("Failed to open service: %d\n", GetLastError());
|
||||
}
|
||||
|
||||
CloseServiceHandle(hSvcMan);
|
||||
}
|
||||
else
|
||||
{
|
||||
eprintf("Failed to open service manager: %d\n", GetLastError());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#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);
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable : 4306) /* conversion from 'int' to '_HFILE' of greater size */
|
||||
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
|
||||
#pragma warning (pop)
|
||||
dprintf(1, "debug mode. dumping memory leaks to stderr on exit.\n");
|
||||
#endif
|
||||
/* acquire and store in global memory current dns domain name.
|
||||
* needed for acls */
|
||||
if (getdomainname())
|
||||
exit(0);
|
||||
|
||||
nfs41_server_list_init();
|
||||
|
||||
if (cmd_args.ldap_enable) {
|
||||
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 out_pipe;
|
||||
#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:
|
||||
if (idmapper) nfs41_idmap_free(idmapper);
|
||||
out_logs:
|
||||
#ifndef STANDALONE_NFSD
|
||||
close_log_files();
|
||||
#endif
|
||||
return;
|
||||
}
|
2187
reactos/base/services/nfsd/nfs41_ops.c
Normal file
2187
reactos/base/services/nfsd/nfs41_ops.c
Normal file
File diff suppressed because it is too large
Load diff
1286
reactos/base/services/nfsd/nfs41_ops.h
Normal file
1286
reactos/base/services/nfsd/nfs41_ops.h
Normal file
File diff suppressed because it is too large
Load diff
409
reactos/base/services/nfsd/nfs41_rpc.c
Normal file
409
reactos/base/services/nfsd/nfs41_rpc.c
Normal file
|
@ -0,0 +1,409 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include "nfs41_ops.h"
|
||||
#include "daemon_debug.h"
|
||||
#include "nfs41_xdr.h"
|
||||
#include "nfs41_callback.h"
|
||||
#include "nfs41_driver.h" /* for AUTH_SYS, AUTHGSS_KRB5s defines */
|
||||
|
||||
#include "rpc/rpc.h"
|
||||
#define SECURITY_WIN32
|
||||
#include <security.h>
|
||||
#include "rpc/auth_sspi.h"
|
||||
|
||||
static enum clnt_stat send_null(CLIENT *client)
|
||||
{
|
||||
struct timeval timeout = {10, 100};
|
||||
|
||||
return clnt_call(client, 0,
|
||||
(xdrproc_t)xdr_void, NULL,
|
||||
(xdrproc_t)xdr_void, NULL, timeout);
|
||||
}
|
||||
|
||||
static int get_client_for_netaddr(
|
||||
IN const netaddr4 *netaddr,
|
||||
IN uint32_t wsize,
|
||||
IN uint32_t rsize,
|
||||
IN nfs41_rpc_clnt *rpc,
|
||||
OUT OPTIONAL char *server_name,
|
||||
OUT CLIENT **client_out)
|
||||
{
|
||||
int status = ERROR_NETWORK_UNREACHABLE;
|
||||
struct netconfig *nconf;
|
||||
struct netbuf *addr;
|
||||
CLIENT *client;
|
||||
|
||||
nconf = getnetconfigent(netaddr->netid);
|
||||
if (nconf == NULL)
|
||||
goto out;
|
||||
|
||||
addr = uaddr2taddr(nconf, netaddr->uaddr);
|
||||
if (addr == NULL)
|
||||
goto out_free_conf;
|
||||
|
||||
if (server_name) {
|
||||
getnameinfo(addr->buf, addr->len, server_name, NI_MAXHOST, NULL, 0, 0);
|
||||
dprintf(1, "servername is %s\n", server_name);
|
||||
}
|
||||
dprintf(1, "callback function %p args %p\n", nfs41_handle_callback, rpc);
|
||||
client = clnt_tli_create(RPC_ANYFD, nconf, addr, NFS41_RPC_PROGRAM,
|
||||
NFS41_RPC_VERSION, wsize, rsize, rpc ? proc_cb_compound_res : NULL,
|
||||
rpc ? nfs41_handle_callback : NULL, rpc ? rpc : NULL);
|
||||
if (client) {
|
||||
*client_out = client;
|
||||
status = NO_ERROR;
|
||||
}
|
||||
|
||||
freenetbuf(addr);
|
||||
out_free_conf:
|
||||
freenetconfigent(nconf);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int get_client_for_multi_addr(
|
||||
IN const multi_addr4 *addrs,
|
||||
IN uint32_t wsize,
|
||||
IN uint32_t rsize,
|
||||
IN nfs41_rpc_clnt *rpc,
|
||||
OUT OPTIONAL char *server_name,
|
||||
OUT CLIENT **client_out,
|
||||
OUT uint32_t *addr_index)
|
||||
{
|
||||
int status = ERROR_NETWORK_UNREACHABLE;
|
||||
uint32_t i;
|
||||
for (i = 0; i < addrs->count; i++) {
|
||||
status = get_client_for_netaddr(&addrs->arr[i],
|
||||
wsize, rsize, rpc, server_name, client_out);
|
||||
if (status == NO_ERROR) {
|
||||
*addr_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
int create_rpcsec_auth_client(
|
||||
IN uint32_t sec_flavor,
|
||||
IN char *server_name,
|
||||
CLIENT *client
|
||||
)
|
||||
{
|
||||
int status = ERROR_NETWORK_UNREACHABLE;
|
||||
|
||||
switch (sec_flavor) {
|
||||
case RPCSEC_AUTHGSS_KRB5:
|
||||
client->cl_auth = authsspi_create_default(client, server_name,
|
||||
RPCSEC_SSPI_SVC_NONE);
|
||||
break;
|
||||
case RPCSEC_AUTHGSS_KRB5I:
|
||||
client->cl_auth = authsspi_create_default(client, server_name,
|
||||
RPCSEC_SSPI_SVC_INTEGRITY);
|
||||
break;
|
||||
case RPCSEC_AUTHGSS_KRB5P:
|
||||
client->cl_auth = authsspi_create_default(client, server_name,
|
||||
RPCSEC_SSPI_SVC_PRIVACY);
|
||||
break;
|
||||
default:
|
||||
eprintf("create_rpc_auth_client: unknown rpcsec flavor %d\n",
|
||||
sec_flavor);
|
||||
client->cl_auth = NULL;
|
||||
}
|
||||
|
||||
if (client->cl_auth == NULL) {
|
||||
eprintf("nfs41_rpc_clnt_create: failed to create %s\n",
|
||||
secflavorop2name(sec_flavor));
|
||||
goto out;
|
||||
} else
|
||||
dprintf(1, "nfs41_rpc_clnt_create: successfully created %s\n",
|
||||
secflavorop2name(sec_flavor));
|
||||
status = 0;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Returns a client structure and an associated lock */
|
||||
int nfs41_rpc_clnt_create(
|
||||
IN const multi_addr4 *addrs,
|
||||
IN uint32_t wsize,
|
||||
IN uint32_t rsize,
|
||||
IN uint32_t uid,
|
||||
IN uint32_t gid,
|
||||
IN uint32_t sec_flavor,
|
||||
OUT nfs41_rpc_clnt **rpc_out)
|
||||
{
|
||||
CLIENT *client;
|
||||
nfs41_rpc_clnt *rpc;
|
||||
uint32_t addr_index;
|
||||
int status;
|
||||
char machname[MAXHOSTNAMELEN + 1];
|
||||
gid_t gids[1];
|
||||
bool_t needcb = 1;
|
||||
|
||||
rpc = calloc(1, sizeof(nfs41_rpc_clnt));
|
||||
if (rpc == NULL) {
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
#ifdef NO_CB_4_KRB5P
|
||||
if (sec_flavor == RPCSEC_AUTHGSS_KRB5P)
|
||||
needcb = 0;
|
||||
#endif
|
||||
rpc->needcb = needcb;
|
||||
rpc->cond = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (rpc->cond == NULL) {
|
||||
status = GetLastError();
|
||||
eprintf("CreateEvent failed %d\n", status);
|
||||
goto out_free_rpc_clnt;
|
||||
}
|
||||
status = get_client_for_multi_addr(addrs, wsize, rsize, needcb?rpc:NULL,
|
||||
rpc->server_name, &client, &addr_index);
|
||||
if (status) {
|
||||
clnt_pcreateerror("connecting failed");
|
||||
goto out_free_rpc_cond;
|
||||
}
|
||||
if (send_null(client) != RPC_SUCCESS) {
|
||||
// XXX Do what here?
|
||||
eprintf("nfs41_rpc_clnt_create: send_null failed\n");
|
||||
status = ERROR_NETWORK_UNREACHABLE;
|
||||
goto out_err_client;
|
||||
}
|
||||
|
||||
rpc->sec_flavor = sec_flavor;
|
||||
if (sec_flavor == RPCSEC_AUTH_SYS) {
|
||||
if (gethostname(machname, sizeof(machname)) == -1) {
|
||||
eprintf("nfs41_rpc_clnt_create: gethostname failed\n");
|
||||
goto out_err_client;
|
||||
}
|
||||
machname[sizeof(machname) - 1] = '\0';
|
||||
client->cl_auth = authsys_create(machname, uid, gid, 0, gids);
|
||||
if (client->cl_auth == NULL) {
|
||||
eprintf("nfs41_rpc_clnt_create: failed to create rpc authsys\n");
|
||||
status = ERROR_NETWORK_UNREACHABLE;
|
||||
goto out_err_client;
|
||||
}
|
||||
} else {
|
||||
status = create_rpcsec_auth_client(sec_flavor, rpc->server_name, client);
|
||||
if (status) {
|
||||
eprintf("nfs41_rpc_clnt_create: failed to establish security "
|
||||
"context with %s\n", rpc->server_name);
|
||||
status = ERROR_NETWORK_UNREACHABLE;
|
||||
goto out_err_client;
|
||||
} else
|
||||
dprintf(1, "nfs41_rpc_clnt_create: successfully created %s\n",
|
||||
secflavorop2name(sec_flavor));
|
||||
}
|
||||
rpc->rpc = client;
|
||||
|
||||
/* keep a copy of the address and buffer sizes for reconnect */
|
||||
memcpy(&rpc->addrs, addrs, sizeof(multi_addr4));
|
||||
/* save the index of the address we connected to */
|
||||
rpc->addr_index = addr_index;
|
||||
rpc->wsize = wsize;
|
||||
rpc->rsize = rsize;
|
||||
rpc->is_valid_session = TRUE;
|
||||
rpc->uid = uid;
|
||||
rpc->gid = gid;
|
||||
|
||||
//initialize rpc client lock
|
||||
InitializeSRWLock(&rpc->lock);
|
||||
|
||||
*rpc_out = rpc;
|
||||
out:
|
||||
return status;
|
||||
out_err_client:
|
||||
clnt_destroy(client);
|
||||
out_free_rpc_cond:
|
||||
CloseHandle(rpc->cond);
|
||||
out_free_rpc_clnt:
|
||||
free(rpc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Frees resources allocated in clnt_create */
|
||||
void nfs41_rpc_clnt_free(
|
||||
IN nfs41_rpc_clnt *rpc)
|
||||
{
|
||||
auth_destroy(rpc->rpc->cl_auth);
|
||||
clnt_destroy(rpc->rpc);
|
||||
CloseHandle(rpc->cond);
|
||||
free(rpc);
|
||||
}
|
||||
|
||||
static bool_t rpc_renew_in_progress(nfs41_rpc_clnt *rpc, int *value)
|
||||
{
|
||||
bool_t status = FALSE;
|
||||
AcquireSRWLockExclusive(&rpc->lock);
|
||||
if (value) {
|
||||
dprintf(1, "nfs41_rpc_renew_in_progress: setting value %d\n", *value);
|
||||
rpc->in_recovery = *value;
|
||||
if (!rpc->in_recovery)
|
||||
SetEvent(rpc->cond);
|
||||
} else {
|
||||
status = rpc->in_recovery;
|
||||
dprintf(1, "nfs41_rpc_renew_in_progress: returning value %d\n", status);
|
||||
}
|
||||
ReleaseSRWLockExclusive(&rpc->lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool_t rpc_should_retry(nfs41_rpc_clnt *rpc, uint32_t version)
|
||||
{
|
||||
bool_t status = 0;
|
||||
AcquireSRWLockExclusive(&rpc->lock);
|
||||
if (rpc->version > version)
|
||||
status = 1;
|
||||
ReleaseSRWLockExclusive(&rpc->lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int rpc_reconnect(
|
||||
IN nfs41_rpc_clnt *rpc)
|
||||
{
|
||||
CLIENT *client = NULL;
|
||||
uint32_t addr_index;
|
||||
int status;
|
||||
|
||||
AcquireSRWLockExclusive(&rpc->lock);
|
||||
|
||||
status = get_client_for_multi_addr(&rpc->addrs, rpc->wsize, rpc->rsize,
|
||||
rpc->needcb?rpc:NULL, NULL, &client, &addr_index);
|
||||
if (status)
|
||||
goto out_unlock;
|
||||
|
||||
if(rpc->sec_flavor == RPCSEC_AUTH_SYS)
|
||||
client->cl_auth = rpc->rpc->cl_auth;
|
||||
else {
|
||||
auth_destroy(rpc->rpc->cl_auth);
|
||||
status = create_rpcsec_auth_client(rpc->sec_flavor, rpc->server_name, client);
|
||||
if (status) {
|
||||
eprintf("Failed to reestablish security context\n");
|
||||
status = ERROR_NETWORK_UNREACHABLE;
|
||||
goto out_err_client;
|
||||
}
|
||||
}
|
||||
if (send_null(client) != RPC_SUCCESS) {
|
||||
eprintf("rpc_reconnect: send_null failed\n");
|
||||
status = ERROR_NETWORK_UNREACHABLE;
|
||||
goto out_err_client;
|
||||
}
|
||||
|
||||
clnt_destroy(rpc->rpc);
|
||||
rpc->rpc = client;
|
||||
rpc->addr_index = addr_index;
|
||||
rpc->version++;
|
||||
dprintf(1, "nfs41_send_compound: reestablished RPC connection\n");
|
||||
|
||||
out_unlock:
|
||||
ReleaseSRWLockExclusive(&rpc->lock);
|
||||
|
||||
/* after releasing the rpc lock, send a BIND_CONN_TO_SESSION if
|
||||
* we need to associate the connection with the backchannel */
|
||||
if (status == NO_ERROR && rpc->needcb &&
|
||||
rpc->client && rpc->client->session) {
|
||||
status = nfs41_bind_conn_to_session(rpc,
|
||||
rpc->client->session->session_id, CDFC4_BACK_OR_BOTH);
|
||||
if (status)
|
||||
eprintf("nfs41_bind_conn_to_session() failed with %s\n",
|
||||
nfs_error_string(status));
|
||||
status = NFS4_OK;
|
||||
}
|
||||
return status;
|
||||
|
||||
out_err_client:
|
||||
clnt_destroy(client);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
int nfs41_send_compound(
|
||||
IN nfs41_rpc_clnt *rpc,
|
||||
IN char *inbuf,
|
||||
OUT char *outbuf)
|
||||
{
|
||||
struct timeval timeout = {90, 100};
|
||||
enum clnt_stat rpc_status;
|
||||
int status, count = 0, one = 1, zero = 0;
|
||||
uint32_t version;
|
||||
|
||||
try_again:
|
||||
AcquireSRWLockShared(&rpc->lock);
|
||||
version = rpc->version;
|
||||
rpc_status = clnt_call(rpc->rpc, 1,
|
||||
(xdrproc_t)nfs_encode_compound, inbuf,
|
||||
(xdrproc_t)nfs_decode_compound, outbuf,
|
||||
timeout);
|
||||
ReleaseSRWLockShared(&rpc->lock);
|
||||
|
||||
if (rpc_status != RPC_SUCCESS) {
|
||||
eprintf("clnt_call returned rpc_status = %s\n",
|
||||
rpc_error_string(rpc_status));
|
||||
switch(rpc_status) {
|
||||
case RPC_CANTRECV:
|
||||
case RPC_CANTSEND:
|
||||
case RPC_TIMEDOUT:
|
||||
case RPC_AUTHERROR:
|
||||
if (++count > 3 || !rpc->is_valid_session) {
|
||||
status = ERROR_NETWORK_UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
if (rpc_should_retry(rpc, version))
|
||||
goto try_again;
|
||||
while (rpc_renew_in_progress(rpc, NULL)) {
|
||||
status = WaitForSingleObject(rpc->cond, INFINITE);
|
||||
if (status != WAIT_OBJECT_0) {
|
||||
dprintf(1, "rpc_renew_in_progress: WaitForSingleObject failed\n");
|
||||
print_condwait_status(1, status);
|
||||
status = ERROR_LOCK_VIOLATION;
|
||||
goto out;
|
||||
}
|
||||
rpc_renew_in_progress(rpc, &zero);
|
||||
goto try_again;
|
||||
}
|
||||
rpc_renew_in_progress(rpc, &one);
|
||||
if (rpc_status == RPC_AUTHERROR && rpc->sec_flavor != RPCSEC_AUTH_SYS) {
|
||||
AcquireSRWLockExclusive(&rpc->lock);
|
||||
auth_destroy(rpc->rpc->cl_auth);
|
||||
status = create_rpcsec_auth_client(rpc->sec_flavor,
|
||||
rpc->server_name, rpc->rpc);
|
||||
ReleaseSRWLockExclusive(&rpc->lock);
|
||||
if (status) {
|
||||
eprintf("Failed to reestablish security context\n");
|
||||
status = ERROR_NETWORK_UNREACHABLE;
|
||||
goto out;
|
||||
}
|
||||
} else
|
||||
if (rpc_reconnect(rpc))
|
||||
eprintf("rpc_reconnect: Failed to reconnect!\n");
|
||||
rpc_renew_in_progress(rpc, &zero);
|
||||
goto try_again;
|
||||
default:
|
||||
eprintf("UNHANDLED RPC_ERROR: %d\n", rpc_status);
|
||||
status = ERROR_NETWORK_UNREACHABLE;
|
||||
goto out;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = 0;
|
||||
out:
|
||||
return status;
|
||||
}
|
343
reactos/base/services/nfsd/nfs41_server.c
Normal file
343
reactos/base/services/nfsd/nfs41_server.c
Normal file
|
@ -0,0 +1,343 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "wintirpc.h"
|
||||
#include "rpc/rpc.h"
|
||||
|
||||
#include "name_cache.h"
|
||||
#include "daemon_debug.h"
|
||||
#include "nfs41.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
#define SRVLVL 2 /* dprintf level for server logging */
|
||||
|
||||
|
||||
/* nfs41_server_list */
|
||||
struct server_list {
|
||||
struct list_entry head;
|
||||
CRITICAL_SECTION lock;
|
||||
};
|
||||
static struct server_list g_server_list;
|
||||
|
||||
#define server_entry(pos) list_container(pos, nfs41_server, entry)
|
||||
|
||||
|
||||
void nfs41_server_list_init()
|
||||
{
|
||||
list_init(&g_server_list.head);
|
||||
InitializeCriticalSection(&g_server_list.lock);
|
||||
}
|
||||
|
||||
/* http://tools.ietf.org/html/rfc5661#section-1.6
|
||||
* 1.6. General Definitions: Server Owner:
|
||||
* "When the client has two connections each to a peer with the same major
|
||||
* identifier, the client assumes that both peers are the same server (the
|
||||
* server namespace is the same via each connection)" */
|
||||
|
||||
/* http://tools.ietf.org/html/rfc5661#section-2.10.4
|
||||
* 2.10.4. Server Scope
|
||||
* "When the server scope values are the same, server owner value may be
|
||||
* validly compared. In cases where the server scope values are different,
|
||||
* server owner values are treated as different even if they contain all
|
||||
* identical bytes." */
|
||||
|
||||
/* given these definitions, we require that both the server_owner.major_id
|
||||
* and server_scope are identical when matching instances of nfs41_server */
|
||||
|
||||
struct server_info {
|
||||
const char *scope;
|
||||
const char *owner;
|
||||
};
|
||||
|
||||
static int server_compare(
|
||||
const struct list_entry *entry,
|
||||
const void *value)
|
||||
{
|
||||
const nfs41_server *server = server_entry(entry);
|
||||
const struct server_info *info = (const struct server_info*)value;
|
||||
const int diff = strncmp(server->scope, info->scope, NFS4_OPAQUE_LIMIT);
|
||||
return diff ? diff : strncmp(server->owner, info->owner, NFS4_OPAQUE_LIMIT);
|
||||
}
|
||||
|
||||
static int server_entry_find(
|
||||
IN struct server_list *servers,
|
||||
IN const struct server_info *info,
|
||||
OUT struct list_entry **entry_out)
|
||||
{
|
||||
*entry_out = list_search(&servers->head, info, server_compare);
|
||||
return *entry_out ? NO_ERROR : ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
static int server_create(
|
||||
IN const struct server_info *info,
|
||||
OUT nfs41_server **server_out)
|
||||
{
|
||||
int status = NO_ERROR;
|
||||
nfs41_server *server;
|
||||
|
||||
server = calloc(1, sizeof(nfs41_server));
|
||||
if (server == NULL) {
|
||||
status = GetLastError();
|
||||
eprintf("failed to allocate server %s\n", info->owner);
|
||||
goto out;
|
||||
}
|
||||
|
||||
StringCchCopyA(server->scope, NFS4_OPAQUE_LIMIT, info->scope);
|
||||
StringCchCopyA(server->owner, NFS4_OPAQUE_LIMIT, info->owner);
|
||||
InitializeSRWLock(&server->addrs.lock);
|
||||
nfs41_superblock_list_init(&server->superblocks);
|
||||
|
||||
status = nfs41_name_cache_create(&server->name_cache);
|
||||
if (status) {
|
||||
eprintf("nfs41_name_cache_create() failed with %d\n", status);
|
||||
goto out_free;
|
||||
}
|
||||
out:
|
||||
*server_out = server;
|
||||
return status;
|
||||
|
||||
out_free:
|
||||
free(server);
|
||||
server = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void server_free(
|
||||
IN nfs41_server *server)
|
||||
{
|
||||
dprintf(SRVLVL, "server_free(%s)\n", server->owner);
|
||||
nfs41_superblock_list_free(&server->superblocks);
|
||||
nfs41_name_cache_free(&server->name_cache);
|
||||
free(server);
|
||||
}
|
||||
|
||||
static __inline void server_ref_locked(
|
||||
IN nfs41_server *server)
|
||||
{
|
||||
server->ref_count++;
|
||||
dprintf(SRVLVL, "nfs41_server_ref(%s) count %d\n",
|
||||
server->owner, server->ref_count);
|
||||
}
|
||||
|
||||
void nfs41_server_ref(
|
||||
IN nfs41_server *server)
|
||||
{
|
||||
EnterCriticalSection(&g_server_list.lock);
|
||||
|
||||
server_ref_locked(server);
|
||||
|
||||
LeaveCriticalSection(&g_server_list.lock);
|
||||
}
|
||||
|
||||
void nfs41_server_deref(
|
||||
IN nfs41_server *server)
|
||||
{
|
||||
EnterCriticalSection(&g_server_list.lock);
|
||||
|
||||
server->ref_count--;
|
||||
dprintf(SRVLVL, "nfs41_server_deref(%s) count %d\n",
|
||||
server->owner, server->ref_count);
|
||||
if (server->ref_count == 0) {
|
||||
list_remove(&server->entry);
|
||||
server_free(server);
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&g_server_list.lock);
|
||||
}
|
||||
|
||||
static void server_addrs_add(
|
||||
IN OUT struct server_addrs *addrs,
|
||||
IN const netaddr4 *addr)
|
||||
{
|
||||
/* we keep a list of addrs used to connect to each server. once it gets
|
||||
* bigger than NFS41_ADDRS_PER_SERVER, overwrite the oldest addrs. use
|
||||
* server_addrs.next_index to implement a circular array */
|
||||
|
||||
AcquireSRWLockExclusive(&addrs->lock);
|
||||
|
||||
if (multi_addr_find(&addrs->addrs, addr, NULL)) {
|
||||
dprintf(SRVLVL, "server_addrs_add() found existing addr '%s'.\n",
|
||||
addr->uaddr);
|
||||
} else {
|
||||
/* overwrite the address at 'next_index' */
|
||||
StringCchCopyA(addrs->addrs.arr[addrs->next_index].netid,
|
||||
NFS41_NETWORK_ID_LEN+1, addr->netid);
|
||||
StringCchCopyA(addrs->addrs.arr[addrs->next_index].uaddr,
|
||||
NFS41_UNIVERSAL_ADDR_LEN+1, addr->uaddr);
|
||||
|
||||
/* increment/wrap next_index */
|
||||
addrs->next_index = (addrs->next_index + 1) % NFS41_ADDRS_PER_SERVER;
|
||||
/* update addrs.count if necessary */
|
||||
if (addrs->addrs.count < addrs->next_index)
|
||||
addrs->addrs.count = addrs->next_index;
|
||||
|
||||
dprintf(SRVLVL, "server_addrs_add() added new addr '%s'.\n",
|
||||
addr->uaddr);
|
||||
}
|
||||
ReleaseSRWLockExclusive(&addrs->lock);
|
||||
}
|
||||
|
||||
void nfs41_server_addrs(
|
||||
IN nfs41_server *server,
|
||||
OUT multi_addr4 *addrs)
|
||||
{
|
||||
struct server_addrs *saddrs = &server->addrs;
|
||||
uint32_t i, j;
|
||||
|
||||
/* make a copy of the server's addrs, with most recent first */
|
||||
AcquireSRWLockShared(&saddrs->lock);
|
||||
j = saddrs->next_index;
|
||||
for (i = 0; i < saddrs->addrs.count; i++) {
|
||||
/* decrement/wrap j */
|
||||
j = (NFS41_ADDRS_PER_SERVER + j - 1) % NFS41_ADDRS_PER_SERVER;
|
||||
memcpy(&addrs->arr[i], &saddrs->addrs.arr[j], sizeof(netaddr4));
|
||||
}
|
||||
ReleaseSRWLockShared(&saddrs->lock);
|
||||
}
|
||||
|
||||
int nfs41_server_find_or_create(
|
||||
IN const char *server_owner_major_id,
|
||||
IN const char *server_scope,
|
||||
IN const netaddr4 *addr,
|
||||
OUT nfs41_server **server_out)
|
||||
{
|
||||
struct server_info info;
|
||||
struct list_entry *entry;
|
||||
nfs41_server *server;
|
||||
int status;
|
||||
|
||||
info.owner = server_owner_major_id;
|
||||
info.scope = server_scope;
|
||||
|
||||
dprintf(SRVLVL, "--> nfs41_server_find_or_create(%s)\n", info.owner);
|
||||
|
||||
EnterCriticalSection(&g_server_list.lock);
|
||||
|
||||
/* search for an existing server */
|
||||
entry = list_search(&g_server_list.head, &info, server_compare);
|
||||
if (entry == NULL) {
|
||||
/* create a new server */
|
||||
status = server_create(&info, &server);
|
||||
if (status == NO_ERROR) {
|
||||
/* add it to the list */
|
||||
list_add_tail(&g_server_list.head, &server->entry);
|
||||
*server_out = server;
|
||||
|
||||
dprintf(SRVLVL, "<-- nfs41_server_find_or_create() "
|
||||
"returning new server %p\n", server);
|
||||
} else {
|
||||
dprintf(SRVLVL, "<-- nfs41_server_find_or_create() "
|
||||
"returning %d\n", status);
|
||||
}
|
||||
} else {
|
||||
server = server_entry(entry);
|
||||
status = NO_ERROR;
|
||||
|
||||
dprintf(SRVLVL, "<-- nfs41_server_find_or_create() "
|
||||
"returning existing server %p\n", server);
|
||||
}
|
||||
|
||||
if (server) {
|
||||
/* register the address used to connect */
|
||||
server_addrs_add(&server->addrs, addr);
|
||||
|
||||
server_ref_locked(server);
|
||||
}
|
||||
|
||||
*server_out = server;
|
||||
LeaveCriticalSection(&g_server_list.lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
int nfs41_server_resolve(
|
||||
IN const char *hostname,
|
||||
IN unsigned short port,
|
||||
OUT multi_addr4 *addrs)
|
||||
{
|
||||
int status = ERROR_BAD_NET_NAME;
|
||||
char service[16];
|
||||
struct addrinfo hints = { 0 }, *res, *info;
|
||||
struct netconfig *nconf;
|
||||
struct netbuf addr;
|
||||
char *netid, *uaddr;
|
||||
|
||||
dprintf(SRVLVL, "--> nfs41_server_resolve(%s:%u)\n",
|
||||
hostname, port);
|
||||
|
||||
addrs->count = 0;
|
||||
|
||||
StringCchPrintfA(service, 16, "%u", port);
|
||||
|
||||
/* request a list of tcp addrs for the given hostname,port */
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
if (getaddrinfo(hostname, service, &hints, &res) != 0)
|
||||
goto out;
|
||||
|
||||
for (info = res; info != NULL; info = info->ai_next) {
|
||||
/* find the appropriate entry in /etc/netconfig */
|
||||
switch (info->ai_family) {
|
||||
case AF_INET: netid = "tcp"; break;
|
||||
case AF_INET6: netid = "tcp6"; break;
|
||||
default: continue;
|
||||
}
|
||||
|
||||
nconf = getnetconfigent(netid);
|
||||
if (nconf == NULL)
|
||||
continue;
|
||||
|
||||
/* convert to a transport-independent universal address */
|
||||
addr.buf = info->ai_addr;
|
||||
addr.maxlen = addr.len = (unsigned int)info->ai_addrlen;
|
||||
|
||||
uaddr = taddr2uaddr(nconf, &addr);
|
||||
freenetconfigent(nconf);
|
||||
|
||||
if (uaddr == NULL)
|
||||
continue;
|
||||
|
||||
StringCchCopyA(addrs->arr[addrs->count].netid,
|
||||
NFS41_NETWORK_ID_LEN+1, netid);
|
||||
StringCchCopyA(addrs->arr[addrs->count].uaddr,
|
||||
NFS41_UNIVERSAL_ADDR_LEN+1, uaddr);
|
||||
freeuaddr(uaddr);
|
||||
|
||||
status = NO_ERROR;
|
||||
if (++addrs->count >= NFS41_ADDRS_PER_SERVER)
|
||||
break;
|
||||
}
|
||||
freeaddrinfo(res);
|
||||
out:
|
||||
if (status)
|
||||
dprintf(SRVLVL, "<-- nfs41_server_resolve(%s:%u) returning "
|
||||
"error %d\n", hostname, port, status);
|
||||
else
|
||||
dprintf(SRVLVL, "<-- nfs41_server_resolve(%s:%u) returning "
|
||||
"%s\n", hostname, port, addrs->arr[0].uaddr);
|
||||
return status;
|
||||
}
|
381
reactos/base/services/nfsd/nfs41_session.c
Normal file
381
reactos/base/services/nfsd/nfs41_session.c
Normal file
|
@ -0,0 +1,381 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <process.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nfs41_ops.h"
|
||||
#include "nfs41_callback.h"
|
||||
#include "util.h"
|
||||
#include "daemon_debug.h"
|
||||
|
||||
|
||||
/* after a CB_RECALL_SLOT or NFS4ERR_BADSLOT, wait a short time for the
|
||||
* SEQUENCE.target_highest_slotid to catch up before updating max_slots again */
|
||||
#define MAX_SLOTS_DELAY 2000 /* in milliseconds */
|
||||
|
||||
|
||||
/* predicate for nfs41_slot_table.cond */
|
||||
static int slot_table_avail(
|
||||
IN const nfs41_slot_table *table)
|
||||
{
|
||||
return table->num_used < table->max_slots;
|
||||
}
|
||||
|
||||
/* session slot mechanism */
|
||||
static void init_slot_table(nfs41_slot_table *table)
|
||||
{
|
||||
uint32_t i;
|
||||
EnterCriticalSection(&table->lock);
|
||||
table->max_slots = NFS41_MAX_NUM_SLOTS;
|
||||
for (i = 0; i < NFS41_MAX_NUM_SLOTS; i++) {
|
||||
table->seq_nums[i] = 1;
|
||||
table->used_slots[i] = 0;
|
||||
}
|
||||
table->highest_used = table->num_used = 0;
|
||||
table->target_delay = 0;
|
||||
|
||||
/* wake any threads waiting on a slot */
|
||||
if (slot_table_avail(table))
|
||||
WakeAllConditionVariable(&table->cond);
|
||||
LeaveCriticalSection(&table->lock);
|
||||
}
|
||||
|
||||
static void resize_slot_table(
|
||||
IN nfs41_slot_table *table,
|
||||
IN uint32_t target_highest_slotid)
|
||||
{
|
||||
if (target_highest_slotid >= NFS41_MAX_NUM_SLOTS)
|
||||
target_highest_slotid = NFS41_MAX_NUM_SLOTS - 1;
|
||||
|
||||
if (table->max_slots != target_highest_slotid + 1) {
|
||||
dprintf(2, "updated max_slots %u to %u\n",
|
||||
table->max_slots, target_highest_slotid + 1);
|
||||
table->max_slots = target_highest_slotid + 1;
|
||||
|
||||
if (slot_table_avail(table))
|
||||
WakeAllConditionVariable(&table->cond);
|
||||
}
|
||||
}
|
||||
|
||||
void nfs41_session_bump_seq(
|
||||
IN nfs41_session *session,
|
||||
IN uint32_t slotid,
|
||||
IN uint32_t target_highest_slotid)
|
||||
{
|
||||
nfs41_slot_table *table = &session->table;
|
||||
|
||||
AcquireSRWLockShared(&session->client->session_lock);
|
||||
EnterCriticalSection(&table->lock);
|
||||
|
||||
if (slotid < NFS41_MAX_NUM_SLOTS)
|
||||
table->seq_nums[slotid]++;
|
||||
|
||||
/* adjust max_slots in response to changes in target_highest_slotid,
|
||||
* but not immediately after a CB_RECALL_SLOT or NFS4ERR_BADSLOT error */
|
||||
if (table->target_delay <= GetTickCount64())
|
||||
resize_slot_table(table, target_highest_slotid);
|
||||
|
||||
LeaveCriticalSection(&table->lock);
|
||||
ReleaseSRWLockShared(&session->client->session_lock);
|
||||
}
|
||||
|
||||
void nfs41_session_free_slot(
|
||||
IN nfs41_session *session,
|
||||
IN uint32_t slotid)
|
||||
{
|
||||
nfs41_slot_table *table = &session->table;
|
||||
|
||||
AcquireSRWLockShared(&session->client->session_lock);
|
||||
EnterCriticalSection(&table->lock);
|
||||
|
||||
/* flag the slot as unused */
|
||||
if (slotid < NFS41_MAX_NUM_SLOTS && table->used_slots[slotid]) {
|
||||
table->used_slots[slotid] = 0;
|
||||
table->num_used--;
|
||||
}
|
||||
/* update highest_used if necessary */
|
||||
if (slotid == table->highest_used) {
|
||||
while (table->highest_used && !table->used_slots[table->highest_used])
|
||||
table->highest_used--;
|
||||
}
|
||||
dprintf(3, "freeing slot#=%d used=%d highest=%d\n",
|
||||
slotid, table->num_used, table->highest_used);
|
||||
|
||||
/* wake any threads waiting on a slot */
|
||||
if (slot_table_avail(table))
|
||||
WakeAllConditionVariable(&table->cond);
|
||||
|
||||
LeaveCriticalSection(&table->lock);
|
||||
ReleaseSRWLockShared(&session->client->session_lock);
|
||||
}
|
||||
|
||||
void nfs41_session_get_slot(
|
||||
IN nfs41_session *session,
|
||||
OUT uint32_t *slot,
|
||||
OUT uint32_t *seqid,
|
||||
OUT uint32_t *highest)
|
||||
{
|
||||
nfs41_slot_table *table = &session->table;
|
||||
uint32_t i;
|
||||
|
||||
AcquireSRWLockShared(&session->client->session_lock);
|
||||
EnterCriticalSection(&table->lock);
|
||||
|
||||
/* wait for an available slot */
|
||||
while (!slot_table_avail(table))
|
||||
SleepConditionVariableCS(&table->cond, &table->lock, INFINITE);
|
||||
|
||||
for (i = 0; i < table->max_slots; i++) {
|
||||
if (table->used_slots[i])
|
||||
continue;
|
||||
|
||||
table->used_slots[i] = 1;
|
||||
table->num_used++;
|
||||
if (i > table->highest_used)
|
||||
table->highest_used = i;
|
||||
|
||||
*slot = i;
|
||||
*seqid = table->seq_nums[i];
|
||||
*highest = table->highest_used;
|
||||
break;
|
||||
}
|
||||
LeaveCriticalSection(&table->lock);
|
||||
ReleaseSRWLockShared(&session->client->session_lock);
|
||||
|
||||
dprintf(2, "session %p: using slot#=%d with seq#=%d highest=%d\n",
|
||||
session, *slot, *seqid, *highest);
|
||||
}
|
||||
|
||||
int nfs41_session_recall_slot(
|
||||
IN nfs41_session *session,
|
||||
IN OUT uint32_t target_highest_slotid)
|
||||
{
|
||||
nfs41_slot_table *table = &session->table;
|
||||
|
||||
AcquireSRWLockShared(&session->client->session_lock);
|
||||
EnterCriticalSection(&table->lock);
|
||||
resize_slot_table(table, target_highest_slotid);
|
||||
table->target_delay = GetTickCount64() + MAX_SLOTS_DELAY;
|
||||
LeaveCriticalSection(&table->lock);
|
||||
ReleaseSRWLockShared(&session->client->session_lock);
|
||||
|
||||
return NFS4_OK;
|
||||
}
|
||||
|
||||
int nfs41_session_bad_slot(
|
||||
IN nfs41_session *session,
|
||||
IN OUT nfs41_sequence_args *args)
|
||||
{
|
||||
nfs41_slot_table *table = &session->table;
|
||||
int status = NFS4ERR_BADSLOT;
|
||||
|
||||
if (args->sa_slotid == 0) {
|
||||
eprintf("server bug detected: NFS4ERR_BADSLOT for slotid=0\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* avoid using any slots >= bad_slotid */
|
||||
EnterCriticalSection(&table->lock);
|
||||
if (table->max_slots > args->sa_slotid) {
|
||||
resize_slot_table(table, args->sa_slotid);
|
||||
table->target_delay = GetTickCount64() + MAX_SLOTS_DELAY;
|
||||
}
|
||||
LeaveCriticalSection(&table->lock);
|
||||
|
||||
/* get a new slot */
|
||||
nfs41_session_free_slot(session, args->sa_slotid);
|
||||
nfs41_session_get_slot(session, &args->sa_slotid,
|
||||
&args->sa_sequenceid, &args->sa_highest_slotid);
|
||||
status = NFS4_OK;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
void nfs41_session_sequence(
|
||||
nfs41_sequence_args *args,
|
||||
nfs41_session *session,
|
||||
bool_t cachethis)
|
||||
{
|
||||
nfs41_session_get_slot(session, &args->sa_slotid,
|
||||
&args->sa_sequenceid, &args->sa_highest_slotid);
|
||||
args->sa_sessionid = session->session_id;
|
||||
args->sa_cachethis = cachethis;
|
||||
}
|
||||
|
||||
|
||||
/* session renewal */
|
||||
static unsigned int WINAPI renew_session(void *args)
|
||||
{
|
||||
int status = NO_ERROR;
|
||||
nfs41_session *session = (nfs41_session *)args;
|
||||
/* sleep for 2/3 of lease_time */
|
||||
const uint32_t sleep_time = (2 * session->lease_time*1000)/3;
|
||||
|
||||
dprintf(1, "Creating renew_session thread: %p\n", session->renew_thread);
|
||||
while(1) {
|
||||
dprintf(1, "Going to sleep for %dmsecs\n", sleep_time);
|
||||
Sleep(sleep_time);
|
||||
status = nfs41_send_sequence(session);
|
||||
if (status)
|
||||
dprintf(1, "renewal thread: nfs41_send_sequence failed %d\n", status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/* session creation */
|
||||
static int session_alloc(
|
||||
IN nfs41_client *client,
|
||||
OUT nfs41_session **session_out)
|
||||
{
|
||||
nfs41_session *session;
|
||||
int status = NO_ERROR;
|
||||
|
||||
session = calloc(1, sizeof(nfs41_session));
|
||||
if (session == NULL) {
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
session->client = client;
|
||||
session->renew_thread = INVALID_HANDLE_VALUE;
|
||||
session->isValidState = FALSE;
|
||||
|
||||
InitializeCriticalSection(&session->table.lock);
|
||||
InitializeConditionVariable(&session->table.cond);
|
||||
|
||||
init_slot_table(&session->table);
|
||||
|
||||
//initialize session lock
|
||||
InitializeSRWLock(&client->session_lock);
|
||||
|
||||
/* initialize the back channel */
|
||||
nfs41_callback_session_init(session);
|
||||
|
||||
*session_out = session;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
int nfs41_session_create(
|
||||
IN nfs41_client *client,
|
||||
IN nfs41_session **session_out)
|
||||
{
|
||||
nfs41_session *session;
|
||||
int status;
|
||||
|
||||
status = session_alloc(client, &session);
|
||||
if (status) {
|
||||
eprintf("session_alloc() failed with %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
AcquireSRWLockShared(&client->exid_lock);
|
||||
if (client->rpc->needcb)
|
||||
session->flags |= CREATE_SESSION4_FLAG_CONN_BACK_CHAN;
|
||||
session->flags |= CREATE_SESSION4_FLAG_PERSIST;
|
||||
ReleaseSRWLockShared(&client->exid_lock);
|
||||
|
||||
status = nfs41_create_session(client, session, TRUE);
|
||||
if (status) {
|
||||
eprintf("nfs41_create_session failed %d\n", status);
|
||||
status = ERROR_BAD_NET_RESP;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
AcquireSRWLockExclusive(&session->client->session_lock);
|
||||
client->session = session;
|
||||
session->isValidState = TRUE;
|
||||
ReleaseSRWLockExclusive(&session->client->session_lock);
|
||||
*session_out = session;
|
||||
out:
|
||||
return status;
|
||||
|
||||
out_err:
|
||||
nfs41_session_free(session);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* session renewal */
|
||||
int nfs41_session_renew(
|
||||
IN nfs41_session *session)
|
||||
{
|
||||
int status;
|
||||
|
||||
AcquireSRWLockExclusive(&session->client->session_lock);
|
||||
session->cb_session.cb_seqnum = 0;
|
||||
init_slot_table(&session->table);
|
||||
|
||||
status = nfs41_create_session(session->client, session, FALSE);
|
||||
ReleaseSRWLockExclusive(&session->client->session_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
int nfs41_session_set_lease(
|
||||
IN nfs41_session *session,
|
||||
IN uint32_t lease_time)
|
||||
{
|
||||
int status = NO_ERROR;
|
||||
uint32_t thread_id;
|
||||
|
||||
if (valid_handle(session->renew_thread)) {
|
||||
eprintf("nfs41_session_set_lease(): session "
|
||||
"renewal thread already started!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (lease_time == 0) {
|
||||
eprintf("nfs41_session_set_lease(): invalid lease_time=0\n");
|
||||
status = ERROR_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
session->lease_time = lease_time;
|
||||
session->renew_thread = (HANDLE)_beginthreadex(NULL,
|
||||
0, renew_session, session, 0, &thread_id);
|
||||
if (!valid_handle(session->renew_thread)) {
|
||||
status = GetLastError();
|
||||
eprintf("_beginthreadex failed %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
void nfs41_session_free(
|
||||
IN nfs41_session *session)
|
||||
{
|
||||
AcquireSRWLockExclusive(&session->client->session_lock);
|
||||
if (valid_handle(session->renew_thread)) {
|
||||
dprintf(1, "nfs41_session_free: terminating session renewal thread\n");
|
||||
if (!TerminateThread(session->renew_thread, NO_ERROR))
|
||||
eprintf("failed to terminate renewal thread %p\n",
|
||||
session->renew_thread);
|
||||
}
|
||||
|
||||
if (session->isValidState) {
|
||||
session->client->rpc->is_valid_session = FALSE;
|
||||
nfs41_destroy_session(session);
|
||||
}
|
||||
DeleteCriticalSection(&session->table.lock);
|
||||
ReleaseSRWLockExclusive(&session->client->session_lock);
|
||||
free(session);
|
||||
}
|
302
reactos/base/services/nfsd/nfs41_superblock.c
Normal file
302
reactos/base/services/nfsd/nfs41_superblock.c
Normal file
|
@ -0,0 +1,302 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "daemon_debug.h"
|
||||
#include "nfs41.h"
|
||||
#include "nfs41_ops.h"
|
||||
#include "from_kernel.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
#define SBLVL 3 /* dprintf level for superblock logging */
|
||||
|
||||
|
||||
static __inline int compare_fsid(
|
||||
IN const nfs41_fsid *lhs,
|
||||
IN const nfs41_fsid *rhs)
|
||||
{
|
||||
if (lhs->major > rhs->major) return 1;
|
||||
if (lhs->major < rhs->major) return -1;
|
||||
if (lhs->minor > rhs->minor) return 1;
|
||||
if (lhs->minor < rhs->minor) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* nfs41_superblock */
|
||||
static int superblock_create(
|
||||
IN const nfs41_fsid *fsid,
|
||||
OUT nfs41_superblock **superblock_out)
|
||||
{
|
||||
int status = NO_ERROR;
|
||||
nfs41_superblock *superblock;
|
||||
|
||||
dprintf(SBLVL, "creating superblock for fsid(%llu,%llu)\n",
|
||||
fsid->major, fsid->minor);
|
||||
|
||||
superblock = calloc(1, sizeof(nfs41_superblock));
|
||||
if (superblock == NULL) {
|
||||
status = GetLastError();
|
||||
eprintf("failed to allocate superblock "
|
||||
"for fsid(%llu,%llu)\n", fsid->major, fsid->minor);
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(&superblock->fsid, fsid, sizeof(nfs41_fsid));
|
||||
InitializeSRWLock(&superblock->lock);
|
||||
|
||||
*superblock_out = superblock;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int get_superblock_attrs(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_superblock *superblock,
|
||||
IN nfs41_path_fh *file)
|
||||
{
|
||||
bool_t supports_named_attrs;
|
||||
int status;
|
||||
bitmap4 attr_request;
|
||||
nfs41_file_info info = { 0 };
|
||||
|
||||
attr_request.arr[0] = FATTR4_WORD0_SUPPORTED_ATTRS |
|
||||
FATTR4_WORD0_LINK_SUPPORT | FATTR4_WORD0_SYMLINK_SUPPORT |
|
||||
FATTR4_WORD0_ACLSUPPORT | FATTR4_WORD0_CANSETTIME |
|
||||
FATTR4_WORD0_CASE_INSENSITIVE | FATTR4_WORD0_CASE_PRESERVING |
|
||||
FATTR4_WORD0_MAXREAD | (uint32_t)(FATTR4_WORD0_MAXWRITE);
|
||||
attr_request.arr[1] = FATTR4_WORD1_FS_LAYOUT_TYPE |
|
||||
FATTR4_WORD1_TIME_DELTA;
|
||||
attr_request.arr[2] = FATTR4_WORD2_SUPPATTR_EXCLCREAT;
|
||||
attr_request.count = 3;
|
||||
|
||||
info.supported_attrs = &superblock->supported_attrs;
|
||||
info.suppattr_exclcreat = &superblock->suppattr_exclcreat;
|
||||
info.time_delta = &superblock->time_delta;
|
||||
|
||||
status = nfs41_superblock_getattr(session, file,
|
||||
&attr_request, &info, &supports_named_attrs);
|
||||
if (status) {
|
||||
eprintf("nfs41_superblock_getattr() failed with %s when fetching "
|
||||
"attributes for fsid(%llu,%llu)\n", nfs_error_string(status),
|
||||
superblock->fsid.major, superblock->fsid.minor);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (info.maxread)
|
||||
superblock->maxread = info.maxread;
|
||||
else
|
||||
superblock->maxread = session->fore_chan_attrs.ca_maxresponsesize;
|
||||
|
||||
if (info.maxwrite)
|
||||
superblock->maxwrite = info.maxwrite;
|
||||
else
|
||||
superblock->maxwrite = session->fore_chan_attrs.ca_maxrequestsize;
|
||||
|
||||
superblock->layout_types = info.fs_layout_types;
|
||||
superblock->aclsupport = info.aclsupport;
|
||||
superblock->link_support = info.link_support;
|
||||
superblock->symlink_support = info.symlink_support;
|
||||
superblock->ea_support = supports_named_attrs;
|
||||
superblock->case_preserving = info.case_preserving;
|
||||
superblock->case_insensitive = info.case_insensitive;
|
||||
|
||||
if (bitmap_isset(&info.attrmask, 0, FATTR4_WORD0_CANSETTIME))
|
||||
superblock->cansettime = info.cansettime;
|
||||
else /* cansettime is not supported, try setting them anyway */
|
||||
superblock->cansettime = 1;
|
||||
|
||||
/* if time_delta is not supported, default to 1s */
|
||||
if (!bitmap_isset(&info.attrmask, 1, FATTR4_WORD1_TIME_DELTA))
|
||||
superblock->time_delta.seconds = 1;
|
||||
|
||||
/* initialize the default getattr mask */
|
||||
superblock->default_getattr.count = 2;
|
||||
superblock->default_getattr.arr[0] = FATTR4_WORD0_TYPE
|
||||
| FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE
|
||||
| FATTR4_WORD0_FILEID | FATTR4_WORD0_HIDDEN
|
||||
| FATTR4_WORD0_ARCHIVE;
|
||||
superblock->default_getattr.arr[1] = FATTR4_WORD1_MODE
|
||||
| FATTR4_WORD1_NUMLINKS | FATTR4_WORD1_SYSTEM
|
||||
| FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_CREATE
|
||||
| FATTR4_WORD1_TIME_MODIFY;
|
||||
superblock->default_getattr.arr[2] = 0;
|
||||
|
||||
nfs41_superblock_supported_attrs(superblock, &superblock->default_getattr);
|
||||
|
||||
dprintf(SBLVL, "attributes for fsid(%llu,%llu): "
|
||||
"maxread=%llu, maxwrite=%llu, layout_types: 0x%X, "
|
||||
"cansettime=%u, time_delta={%llu,%u}, aclsupport=%u, "
|
||||
"link_support=%u, symlink_support=%u, case_preserving=%u, "
|
||||
"case_insensitive=%u\n",
|
||||
superblock->fsid.major, superblock->fsid.minor,
|
||||
superblock->maxread, superblock->maxwrite,
|
||||
superblock->layout_types, superblock->cansettime,
|
||||
superblock->time_delta.seconds, superblock->time_delta.nseconds,
|
||||
superblock->aclsupport, superblock->link_support,
|
||||
superblock->symlink_support, superblock->case_preserving,
|
||||
superblock->case_insensitive);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
void nfs41_superblock_fs_attributes(
|
||||
IN const nfs41_superblock *superblock,
|
||||
OUT PFILE_FS_ATTRIBUTE_INFORMATION FsAttrs)
|
||||
{
|
||||
FsAttrs->FileSystemAttributes = FILE_SUPPORTS_REMOTE_STORAGE;
|
||||
if (superblock->link_support)
|
||||
FsAttrs->FileSystemAttributes |= FILE_SUPPORTS_HARD_LINKS;
|
||||
if (superblock->symlink_support)
|
||||
FsAttrs->FileSystemAttributes |= FILE_SUPPORTS_REPARSE_POINTS;
|
||||
if (superblock->ea_support)
|
||||
FsAttrs->FileSystemAttributes |= FILE_SUPPORTS_EXTENDED_ATTRIBUTES;
|
||||
if (superblock->case_preserving)
|
||||
FsAttrs->FileSystemAttributes |= FILE_CASE_PRESERVED_NAMES;
|
||||
if (!superblock->case_insensitive)
|
||||
FsAttrs->FileSystemAttributes |= FILE_CASE_SENSITIVE_SEARCH;
|
||||
if (superblock->aclsupport)
|
||||
FsAttrs->FileSystemAttributes |= FILE_PERSISTENT_ACLS;
|
||||
|
||||
FsAttrs->MaximumComponentNameLength = NFS41_MAX_COMPONENT_LEN;
|
||||
|
||||
/* let the driver fill in FileSystemName */
|
||||
FsAttrs->FileSystemNameLength = 0;
|
||||
|
||||
dprintf(SBLVL, "FileFsAttributeInformation: case_preserving %u, "
|
||||
"case_insensitive %u, max component %u\n",
|
||||
superblock->case_preserving, superblock->case_insensitive,
|
||||
FsAttrs->MaximumComponentNameLength);
|
||||
}
|
||||
|
||||
|
||||
/* nfs41_superblock_list */
|
||||
#define superblock_entry(pos) list_container(pos, nfs41_superblock, entry)
|
||||
|
||||
static int superblock_compare(
|
||||
const struct list_entry *entry,
|
||||
const void *value)
|
||||
{
|
||||
const nfs41_superblock *superblock = superblock_entry(entry);
|
||||
return compare_fsid(&superblock->fsid, (const nfs41_fsid*)value);
|
||||
}
|
||||
|
||||
static nfs41_superblock* find_superblock(
|
||||
IN nfs41_superblock_list *superblocks,
|
||||
IN const nfs41_fsid *fsid)
|
||||
{
|
||||
struct list_entry *entry;
|
||||
entry = list_search(&superblocks->head, fsid, superblock_compare);
|
||||
return entry ? superblock_entry(entry) : NULL;
|
||||
}
|
||||
|
||||
void nfs41_superblock_list_init(
|
||||
IN nfs41_superblock_list *superblocks)
|
||||
{
|
||||
list_init(&superblocks->head);
|
||||
InitializeSRWLock(&superblocks->lock);
|
||||
}
|
||||
|
||||
void nfs41_superblock_list_free(
|
||||
IN nfs41_superblock_list *superblocks)
|
||||
{
|
||||
struct list_entry *entry, *tmp;
|
||||
|
||||
dprintf(SBLVL, "nfs41_superblock_list_free()\n");
|
||||
|
||||
list_for_each_tmp(entry, tmp, &superblocks->head)
|
||||
free(superblock_entry(entry));
|
||||
}
|
||||
|
||||
|
||||
int nfs41_superblock_for_fh(
|
||||
IN nfs41_session *session,
|
||||
IN const nfs41_fsid *fsid,
|
||||
IN const nfs41_fh *parent OPTIONAL,
|
||||
OUT nfs41_path_fh *file)
|
||||
{
|
||||
int status = NFS4_OK;
|
||||
nfs41_server *server = client_server(session->client);
|
||||
nfs41_superblock_list *superblocks = &server->superblocks;
|
||||
nfs41_superblock *superblock;
|
||||
|
||||
dprintf(SBLVL, "--> nfs41_superblock_for_fh(fsid(%llu,%llu))\n",
|
||||
fsid->major, fsid->minor);
|
||||
|
||||
/* compare with the parent's fsid, and use that if it matches */
|
||||
if (parent && parent->superblock &&
|
||||
compare_fsid(fsid, &parent->superblock->fsid) == 0) {
|
||||
file->fh.superblock = parent->superblock;
|
||||
dprintf(SBLVL, "using superblock from parent\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* using a shared lock, search for an existing superblock */
|
||||
AcquireSRWLockShared(&superblocks->lock);
|
||||
superblock = find_superblock(superblocks, fsid);
|
||||
ReleaseSRWLockShared(&superblocks->lock);
|
||||
|
||||
if (superblock) {
|
||||
dprintf(SBLVL, "found existing superblock in server list "
|
||||
"[shared lock]\n");
|
||||
} else {
|
||||
AcquireSRWLockExclusive(&superblocks->lock);
|
||||
/* must search again under an exclusive lock, in case another thread
|
||||
* created it after our first search */
|
||||
superblock = find_superblock(superblocks, fsid);
|
||||
if (superblock) {
|
||||
dprintf(SBLVL, "found newly created superblock in server list "
|
||||
"[exclusive lock]\n");
|
||||
} else {
|
||||
/* create the superblock */
|
||||
status = superblock_create(fsid, &superblock);
|
||||
if (status == NO_ERROR) /* add it to the list */
|
||||
list_add_tail(&superblocks->head, &superblock->entry);
|
||||
}
|
||||
ReleaseSRWLockExclusive(&superblocks->lock);
|
||||
}
|
||||
|
||||
if (status == NO_ERROR && superblock->supported_attrs.count == 0) {
|
||||
/* exclusive lock on the superblock while fetching attributes */
|
||||
AcquireSRWLockExclusive(&superblock->lock);
|
||||
if (superblock->supported_attrs.count == 0)
|
||||
status = get_superblock_attrs(session, superblock, file);
|
||||
ReleaseSRWLockExclusive(&superblock->lock);
|
||||
}
|
||||
|
||||
file->fh.superblock = superblock;
|
||||
out:
|
||||
dprintf(SBLVL, "<-- nfs41_superblock_for_fh() returning %p, status %d\n",
|
||||
file->fh.superblock, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
void nfs41_superblock_space_changed(
|
||||
IN nfs41_superblock *superblock)
|
||||
{
|
||||
/* invalidate cached volume size attributes */
|
||||
AcquireSRWLockExclusive(&superblock->lock);
|
||||
superblock->cache_expiration = 0;
|
||||
ReleaseSRWLockExclusive(&superblock->lock);
|
||||
}
|
249
reactos/base/services/nfsd/nfs41_types.h
Normal file
249
reactos/base/services/nfsd/nfs41_types.h
Normal file
|
@ -0,0 +1,249 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#ifndef __NFS41_DAEMON_TYPES_H__
|
||||
#define __NFS41_DAEMON_TYPES_H__
|
||||
|
||||
#include "wintirpc.h"
|
||||
#include "rpc/xdr.h"
|
||||
#include "nfs41_const.h"
|
||||
|
||||
typedef char* caddr_t;
|
||||
|
||||
static const int64_t NFS4_INT64_MAX = 0x7fffffffffffffff;
|
||||
static const uint64_t NFS4_UINT64_MAX = 0xffffffffffffffff;
|
||||
static const int32_t NFS4_INT32_MAX = 0x7fffffff;
|
||||
static const uint32_t NFS4_UINT32_MAX = 0xffffffff;
|
||||
|
||||
static const uint64_t NFS4_MAXFILELEN = 0xffffffffffffffff;
|
||||
static const uint64_t NFS4_MAXFILEOFF = 0xfffffffffffffffe;
|
||||
|
||||
|
||||
/* common nfs types */
|
||||
typedef struct __nfs41_abs_path {
|
||||
char path[NFS41_MAX_PATH_LEN];
|
||||
unsigned short len;
|
||||
SRWLOCK lock;
|
||||
} nfs41_abs_path;
|
||||
|
||||
typedef struct __nfs41_component {
|
||||
const char *name;
|
||||
unsigned short len;
|
||||
} nfs41_component;
|
||||
|
||||
typedef struct __nfs41_fh {
|
||||
unsigned char fh[NFS4_FHSIZE];
|
||||
uint32_t len;
|
||||
uint64_t fileid;
|
||||
struct __nfs41_superblock *superblock;
|
||||
} nfs41_fh;
|
||||
|
||||
typedef struct __nfs41_path_fh {
|
||||
nfs41_abs_path *path;
|
||||
nfs41_component name;
|
||||
nfs41_fh fh;
|
||||
} nfs41_path_fh;
|
||||
|
||||
typedef struct __nfs41_fsid {
|
||||
uint64_t major;
|
||||
uint64_t minor;
|
||||
} nfs41_fsid;
|
||||
|
||||
typedef struct __nfs41_readdir_cookie {
|
||||
uint64_t cookie;
|
||||
unsigned char verf[NFS4_VERIFIER_SIZE];
|
||||
} nfs41_readdir_cookie;
|
||||
|
||||
typedef struct __nfs41_write_verf {
|
||||
unsigned char verf[NFS4_VERIFIER_SIZE];
|
||||
unsigned char expected[NFS4_VERIFIER_SIZE];
|
||||
#ifdef __REACTOS__
|
||||
uint32_t committed;
|
||||
#else
|
||||
enum stable_how4 committed;
|
||||
#endif
|
||||
} nfs41_write_verf;
|
||||
|
||||
typedef struct __netaddr4 {
|
||||
char netid[NFS41_NETWORK_ID_LEN+1];
|
||||
char uaddr[NFS41_UNIVERSAL_ADDR_LEN+1];
|
||||
} netaddr4;
|
||||
|
||||
typedef struct __multi_addr4 {
|
||||
netaddr4 arr[NFS41_ADDRS_PER_SERVER];
|
||||
uint32_t count;
|
||||
} multi_addr4;
|
||||
|
||||
typedef struct __bitmap4 {
|
||||
uint32_t count;
|
||||
uint32_t arr[3];
|
||||
} bitmap4;
|
||||
|
||||
typedef struct __nfstime4 {
|
||||
int64_t seconds;
|
||||
uint32_t nseconds;
|
||||
} nfstime4;
|
||||
|
||||
typedef struct __client_owner4 {
|
||||
unsigned char co_verifier[NFS4_VERIFIER_SIZE];
|
||||
uint32_t co_ownerid_len;
|
||||
unsigned char co_ownerid[NFS4_OPAQUE_LIMIT];
|
||||
} client_owner4;
|
||||
|
||||
typedef struct __server_owner4 {
|
||||
uint64_t so_minor_id;
|
||||
uint32_t so_major_id_len;
|
||||
char so_major_id[NFS4_OPAQUE_LIMIT];
|
||||
} server_owner4;
|
||||
|
||||
typedef struct __state_owner4 {
|
||||
uint32_t owner_len;
|
||||
unsigned char owner[NFS4_OPAQUE_LIMIT];
|
||||
} state_owner4;
|
||||
|
||||
typedef struct __nfs_impl_id4 {
|
||||
uint32_t nii_domain_len;
|
||||
unsigned char *nii_domain;
|
||||
uint32_t nii_name_len;
|
||||
unsigned char *nii_name;
|
||||
nfstime4 nii_date;
|
||||
} nfs_impl_id4;
|
||||
|
||||
typedef struct __nfsace4 {
|
||||
uint32_t acetype;
|
||||
uint32_t aceflag;
|
||||
uint32_t acemask;
|
||||
char who[NFS4_OPAQUE_LIMIT];
|
||||
} nfsace4;
|
||||
|
||||
typedef struct __nfsacl41 {
|
||||
uint32_t flag;
|
||||
nfsace4 *aces;
|
||||
uint32_t count;
|
||||
} nfsacl41;
|
||||
|
||||
typedef struct __stateid4 {
|
||||
uint32_t seqid;
|
||||
unsigned char other[NFS4_STATEID_OTHER];
|
||||
} stateid4;
|
||||
|
||||
typedef struct __open_delegation4 {
|
||||
stateid4 stateid;
|
||||
nfsace4 permissions;
|
||||
#ifdef __REACTOS__
|
||||
uint32_t type;
|
||||
#else
|
||||
enum open_delegation_type4 type;
|
||||
#endif
|
||||
bool_t recalled;
|
||||
} open_delegation4;
|
||||
|
||||
typedef struct __fattr4 {
|
||||
bitmap4 attrmask;
|
||||
uint32_t attr_vals_len;
|
||||
unsigned char attr_vals[NFS4_OPAQUE_LIMIT];
|
||||
} fattr4;
|
||||
|
||||
typedef struct __change_info4 {
|
||||
bool_t atomic;
|
||||
uint64_t before;
|
||||
uint64_t after;
|
||||
} change_info4;
|
||||
|
||||
typedef struct __fs_location_server {
|
||||
/* 'address' represents one of a traditional DNS host name,
|
||||
* IPv4 address, IPv6 address, or a zero-length string */
|
||||
char address[NFS41_HOSTNAME_LEN+1];
|
||||
} fs_location_server;
|
||||
|
||||
typedef struct __fs_location4 {
|
||||
nfs41_abs_path path; /* path to fs from referred server's root */
|
||||
fs_location_server *servers;
|
||||
uint32_t server_count;
|
||||
} fs_location4;
|
||||
|
||||
typedef struct __fs_locations4 {
|
||||
nfs41_abs_path path; /* path to fs from referring server's root */
|
||||
fs_location4 *locations;
|
||||
uint32_t location_count;
|
||||
} fs_locations4;
|
||||
|
||||
enum {
|
||||
MDSTHRESH_READ = 0,
|
||||
MDSTHRESH_WRITE,
|
||||
MDSTHRESH_READ_IO,
|
||||
MDSTHRESH_WRITE_IO,
|
||||
|
||||
MAX_MDSTHRESH_HINTS
|
||||
};
|
||||
typedef struct __threshold_item4 {
|
||||
uint32_t type;
|
||||
uint64_t hints[MAX_MDSTHRESH_HINTS];
|
||||
} threshold_item4;
|
||||
|
||||
#define MAX_MDSTHRESHOLD_ITEMS 1
|
||||
typedef struct __mdsthreshold4 {
|
||||
uint32_t count;
|
||||
threshold_item4 items[MAX_MDSTHRESHOLD_ITEMS];
|
||||
} mdsthreshold4;
|
||||
|
||||
typedef struct __nfs41_file_info {
|
||||
nfs41_fsid fsid;
|
||||
mdsthreshold4 mdsthreshold;
|
||||
nfstime4 time_access;
|
||||
nfstime4 time_create;
|
||||
nfstime4 time_modify;
|
||||
nfsacl41 *acl;
|
||||
nfstime4 *time_delta; /* XXX: per-fs */
|
||||
bitmap4 attrmask;
|
||||
bitmap4 *supported_attrs; /* XXX: per-fs */
|
||||
bitmap4 *suppattr_exclcreat; /* XXX: per-fs */
|
||||
uint64_t maxread; /* XXX: per-fs */
|
||||
uint64_t maxwrite; /* XXX: per-fs */
|
||||
uint64_t change;
|
||||
uint64_t size;
|
||||
uint64_t fileid;
|
||||
uint64_t space_avail; /* XXX: per-fs */
|
||||
uint64_t space_free; /* XXX: per-fs */
|
||||
uint64_t space_total; /* XXX: per-fs */
|
||||
uint32_t type;
|
||||
uint32_t numlinks;
|
||||
uint32_t rdattr_error;
|
||||
uint32_t mode;
|
||||
uint32_t mode_mask;
|
||||
fs_locations4 *fs_locations; /* XXX: per-fs */
|
||||
uint32_t lease_time; /* XXX: per-server */
|
||||
uint32_t fs_layout_types; /* pnfs, XXX: per-fs */
|
||||
bool_t hidden;
|
||||
bool_t system;
|
||||
bool_t archive;
|
||||
bool_t cansettime; /* XXX: per-fs */
|
||||
bool_t case_insensitive;
|
||||
bool_t case_preserving;
|
||||
bool_t symlink_dir;
|
||||
bool_t symlink_support;
|
||||
bool_t link_support;
|
||||
char *owner;
|
||||
char *owner_group;
|
||||
uint32_t aclsupport;
|
||||
} nfs41_file_info;
|
||||
|
||||
#endif /* !__NFS41_DAEMON_TYPES_H__ */
|
3679
reactos/base/services/nfsd/nfs41_xdr.c
Normal file
3679
reactos/base/services/nfsd/nfs41_xdr.c
Normal file
File diff suppressed because it is too large
Load diff
32
reactos/base/services/nfsd/nfs41_xdr.h
Normal file
32
reactos/base/services/nfsd/nfs41_xdr.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#ifndef __NFS41_NFS_XDR_H__
|
||||
#define __NFS41_NFS_XDR_H__
|
||||
|
||||
#include "nfs41_types.h"
|
||||
|
||||
bool_t nfs_encode_compound(XDR *xdr, caddr_t *args);
|
||||
bool_t nfs_decode_compound(XDR *xdr, caddr_t *res);
|
||||
|
||||
void nfsacl41_free(nfsacl41 *acl);
|
||||
|
||||
#endif /* !__NFS41_NFS_XDR_H__ */
|
4
reactos/base/services/nfsd/nfsd.rc
Normal file
4
reactos/base/services/nfsd/nfsd.rc
Normal file
|
@ -0,0 +1,4 @@
|
|||
#define REACTOS_STR_FILE_DESCRIPTION "NFS daemon"
|
||||
#define REACTOS_STR_INTERNAL_NAME "nfsd"
|
||||
#define REACTOS_STR_ORIGINAL_FILENAME "nfsd.exe"
|
||||
#include <reactos/version.rc>
|
948
reactos/base/services/nfsd/open.c
Normal file
948
reactos/base/services/nfsd/open.c
Normal file
|
@ -0,0 +1,948 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#include "nfs41_ops.h"
|
||||
#include "delegation.h"
|
||||
#include "from_kernel.h"
|
||||
#include "daemon_debug.h"
|
||||
#include "upcall.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
static int create_open_state(
|
||||
IN const char *path,
|
||||
IN uint32_t open_owner_id,
|
||||
OUT nfs41_open_state **state_out)
|
||||
{
|
||||
int status;
|
||||
nfs41_open_state *state;
|
||||
|
||||
state = calloc(1, sizeof(nfs41_open_state));
|
||||
if (state == NULL) {
|
||||
status = GetLastError();
|
||||
goto out;
|
||||
}
|
||||
|
||||
InitializeSRWLock(&state->path.lock);
|
||||
if (FAILED(StringCchCopyA(state->path.path, NFS41_MAX_PATH_LEN, path))) {
|
||||
status = ERROR_FILENAME_EXCED_RANGE;
|
||||
goto out_free;
|
||||
}
|
||||
state->path.len = (unsigned short)strlen(state->path.path);
|
||||
path_fh_init(&state->file, &state->path);
|
||||
path_fh_init(&state->parent, &state->path);
|
||||
last_component(state->path.path, state->file.name.name, &state->parent.name);
|
||||
|
||||
StringCchPrintfA((LPSTR)state->owner.owner, NFS4_OPAQUE_LIMIT, "%u",
|
||||
open_owner_id);
|
||||
state->owner.owner_len = (uint32_t)strlen((const char*)state->owner.owner);
|
||||
state->ref_count = 1;
|
||||
list_init(&state->locks.list);
|
||||
list_init(&state->client_entry);
|
||||
InitializeCriticalSection(&state->locks.lock);
|
||||
|
||||
state->ea.list = INVALID_HANDLE_VALUE;
|
||||
InitializeCriticalSection(&state->ea.lock);
|
||||
|
||||
*state_out = state;
|
||||
status = NO_ERROR;
|
||||
out:
|
||||
return status;
|
||||
|
||||
out_free:
|
||||
free(state);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void open_state_free(
|
||||
IN nfs41_open_state *state)
|
||||
{
|
||||
struct list_entry *entry, *tmp;
|
||||
|
||||
/* free associated lock state */
|
||||
list_for_each_tmp(entry, tmp, &state->locks.list)
|
||||
free(list_container(entry, nfs41_lock_state, open_entry));
|
||||
if (state->delegation.state)
|
||||
nfs41_delegation_deref(state->delegation.state);
|
||||
if (state->ea.list != INVALID_HANDLE_VALUE)
|
||||
free(state->ea.list);
|
||||
free(state);
|
||||
}
|
||||
|
||||
|
||||
/* open state reference counting */
|
||||
void nfs41_open_state_ref(
|
||||
IN nfs41_open_state *state)
|
||||
{
|
||||
const LONG count = InterlockedIncrement(&state->ref_count);
|
||||
|
||||
dprintf(2, "nfs41_open_state_ref(%s) count %d\n", state->path.path, count);
|
||||
}
|
||||
|
||||
void nfs41_open_state_deref(
|
||||
IN nfs41_open_state *state)
|
||||
{
|
||||
const LONG count = InterlockedDecrement(&state->ref_count);
|
||||
|
||||
dprintf(2, "nfs41_open_state_deref(%s) count %d\n", state->path.path, count);
|
||||
if (count == 0)
|
||||
open_state_free(state);
|
||||
}
|
||||
|
||||
/* 8.2.5. Stateid Use for I/O Operations
|
||||
* o If the client holds a delegation for the file in question, the
|
||||
* delegation stateid SHOULD be used.
|
||||
* o Otherwise, if the entity corresponding to the lock-owner (e.g., a
|
||||
* process) sending the I/O has a byte-range lock stateid for the
|
||||
* associated open file, then the byte-range lock stateid for that
|
||||
* lock-owner and open file SHOULD be used.
|
||||
* o If there is no byte-range lock stateid, then the OPEN stateid for
|
||||
* the open file in question SHOULD be used.
|
||||
* o Finally, if none of the above apply, then a special stateid SHOULD
|
||||
* be used. */
|
||||
void nfs41_open_stateid_arg(
|
||||
IN nfs41_open_state *state,
|
||||
OUT stateid_arg *arg)
|
||||
{
|
||||
arg->open = state;
|
||||
arg->delegation = NULL;
|
||||
|
||||
AcquireSRWLockShared(&state->lock);
|
||||
|
||||
if (state->delegation.state) {
|
||||
nfs41_delegation_state *deleg = state->delegation.state;
|
||||
AcquireSRWLockShared(&deleg->lock);
|
||||
if (deleg->status == DELEGATION_GRANTED) {
|
||||
arg->type = STATEID_DELEG_FILE;
|
||||
memcpy(&arg->stateid, &deleg->state.stateid, sizeof(stateid4));
|
||||
}
|
||||
ReleaseSRWLockShared(&deleg->lock);
|
||||
|
||||
if (arg->type == STATEID_DELEG_FILE)
|
||||
goto out;
|
||||
|
||||
dprintf(2, "delegation recalled, waiting for open stateid..\n");
|
||||
|
||||
/* wait for nfs41_delegation_to_open() to recover open stateid */
|
||||
while (!state->do_close)
|
||||
SleepConditionVariableSRW(&state->delegation.cond, &state->lock,
|
||||
INFINITE, CONDITION_VARIABLE_LOCKMODE_SHARED);
|
||||
}
|
||||
|
||||
if (state->locks.stateid.seqid) {
|
||||
memcpy(&arg->stateid, &state->locks.stateid, sizeof(stateid4));
|
||||
arg->type = STATEID_LOCK;
|
||||
} else if (state->do_close) {
|
||||
memcpy(&arg->stateid, &state->stateid, sizeof(stateid4));
|
||||
arg->type = STATEID_OPEN;
|
||||
} else {
|
||||
memset(&arg->stateid, 0, sizeof(stateid4));
|
||||
arg->type = STATEID_SPECIAL;
|
||||
}
|
||||
out:
|
||||
ReleaseSRWLockShared(&state->lock);
|
||||
}
|
||||
|
||||
/* client list of associated open state */
|
||||
static void client_state_add(
|
||||
IN nfs41_open_state *state)
|
||||
{
|
||||
nfs41_client *client = state->session->client;
|
||||
|
||||
EnterCriticalSection(&client->state.lock);
|
||||
list_add_tail(&client->state.opens, &state->client_entry);
|
||||
LeaveCriticalSection(&client->state.lock);
|
||||
}
|
||||
|
||||
static void client_state_remove(
|
||||
IN nfs41_open_state *state)
|
||||
{
|
||||
nfs41_client *client = state->session->client;
|
||||
|
||||
EnterCriticalSection(&client->state.lock);
|
||||
list_remove(&state->client_entry);
|
||||
LeaveCriticalSection(&client->state.lock);
|
||||
}
|
||||
|
||||
static int do_open(
|
||||
IN OUT nfs41_open_state *state,
|
||||
IN uint32_t create,
|
||||
IN uint32_t createhow,
|
||||
IN nfs41_file_info *createattrs,
|
||||
IN bool_t try_recovery,
|
||||
OUT nfs41_file_info *info)
|
||||
{
|
||||
open_claim4 claim;
|
||||
stateid4 open_stateid;
|
||||
open_delegation4 delegation = { 0 };
|
||||
nfs41_delegation_state *deleg_state = NULL;
|
||||
int status;
|
||||
|
||||
claim.claim = CLAIM_NULL;
|
||||
claim.u.null.filename = &state->file.name;
|
||||
|
||||
status = nfs41_open(state->session, &state->parent, &state->file,
|
||||
&state->owner, &claim, state->share_access, state->share_deny,
|
||||
create, createhow, createattrs, TRUE, &open_stateid,
|
||||
&delegation, info);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
/* allocate delegation state and register it with the client */
|
||||
nfs41_delegation_granted(state->session, &state->parent,
|
||||
&state->file, &delegation, TRUE, &deleg_state);
|
||||
if (deleg_state) {
|
||||
deleg_state->srv_open = state->srv_open;
|
||||
dprintf(1, "do_open: received delegation: saving srv_open = %x\n",
|
||||
state->srv_open);
|
||||
}
|
||||
|
||||
AcquireSRWLockExclusive(&state->lock);
|
||||
/* update the stateid */
|
||||
memcpy(&state->stateid, &open_stateid, sizeof(open_stateid));
|
||||
state->do_close = 1;
|
||||
state->delegation.state = deleg_state;
|
||||
ReleaseSRWLockExclusive(&state->lock);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int open_or_delegate(
|
||||
IN OUT nfs41_open_state *state,
|
||||
IN uint32_t create,
|
||||
IN uint32_t createhow,
|
||||
IN nfs41_file_info *createattrs,
|
||||
IN bool_t try_recovery,
|
||||
OUT nfs41_file_info *info)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* check for existing delegation */
|
||||
status = nfs41_delegate_open(state, create, createattrs, info);
|
||||
|
||||
/* get an open stateid if we have no delegation stateid */
|
||||
if (status)
|
||||
status = do_open(state, create, createhow,
|
||||
createattrs, try_recovery, info);
|
||||
|
||||
state->pnfs_last_offset = info->size ? info->size - 1 : 0;
|
||||
|
||||
/* register the client's open state on success */
|
||||
if (status == NFS4_OK)
|
||||
client_state_add(state);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static int parse_abs_path(unsigned char **buffer, uint32_t *length, nfs41_abs_path *path)
|
||||
{
|
||||
int status = safe_read(buffer, length, &path->len, sizeof(USHORT));
|
||||
if (status) goto out;
|
||||
if (path->len == 0)
|
||||
goto out;
|
||||
if (path->len >= NFS41_MAX_PATH_LEN) {
|
||||
status = ERROR_BUFFER_OVERFLOW;
|
||||
goto out;
|
||||
}
|
||||
status = safe_read(buffer, length, path->path, path->len);
|
||||
if (status) goto out;
|
||||
path->len--; /* subtract 1 for null */
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
/* NFS41_OPEN */
|
||||
static int parse_open(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
open_upcall_args *args = &upcall->args.open;
|
||||
|
||||
status = get_name(&buffer, &length, &args->path);
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->access_mask, sizeof(ULONG));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->access_mode, sizeof(ULONG));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->file_attrs, sizeof(ULONG));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->create_opts, sizeof(ULONG));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->disposition, sizeof(ULONG));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->open_owner_id, sizeof(LONG));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->mode, sizeof(DWORD));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->srv_open, sizeof(HANDLE));
|
||||
if (status) goto out;
|
||||
status = parse_abs_path(&buffer, &length, &args->symlink);
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->ea, sizeof(HANDLE));
|
||||
if (status) goto out;
|
||||
|
||||
dprintf(1, "parsing NFS41_OPEN: filename='%s' access mask=%d "
|
||||
"access mode=%d\n\tfile attrs=0x%x create attrs=0x%x "
|
||||
"(kernel) disposition=%d\n\topen_owner_id=%d mode=%o "
|
||||
"srv_open=%p symlink=%s ea=%p\n", args->path, args->access_mask,
|
||||
args->access_mode, args->file_attrs, args->create_opts,
|
||||
args->disposition, args->open_owner_id, args->mode, args->srv_open,
|
||||
args->symlink.path, args->ea);
|
||||
print_disposition(2, args->disposition);
|
||||
print_access_mask(2, args->access_mask);
|
||||
print_share_mode(2, args->access_mode);
|
||||
print_create_attributes(2, args->create_opts);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static BOOLEAN open_for_attributes(uint32_t type, ULONG access_mask,
|
||||
ULONG disposition, int status)
|
||||
{
|
||||
if (type == NF4DIR) {
|
||||
if (disposition == FILE_OPEN || disposition == FILE_OVERWRITE ||
|
||||
(!status && (disposition == FILE_OPEN_IF ||
|
||||
disposition == FILE_OVERWRITE_IF ||
|
||||
disposition == FILE_SUPERSEDE))) {
|
||||
dprintf(1, "Opening a directory\n");
|
||||
return TRUE;
|
||||
} else {
|
||||
dprintf(1, "Creating a directory\n");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if ((access_mask & FILE_READ_DATA) ||
|
||||
(access_mask & FILE_WRITE_DATA) ||
|
||||
(access_mask & FILE_APPEND_DATA) ||
|
||||
(access_mask & FILE_EXECUTE) ||
|
||||
disposition == FILE_CREATE ||
|
||||
disposition == FILE_OVERWRITE_IF ||
|
||||
disposition == FILE_SUPERSEDE ||
|
||||
disposition == FILE_OPEN_IF ||
|
||||
disposition == FILE_OVERWRITE)
|
||||
return FALSE;
|
||||
else {
|
||||
dprintf(1, "Open call that wants to manage attributes\n");
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static int map_disposition_2_nfsopen(ULONG disposition, int in_status, bool_t persistent,
|
||||
uint32_t *create, uint32_t *createhowmode,
|
||||
uint32_t *last_error)
|
||||
{
|
||||
int status = NO_ERROR;
|
||||
if (disposition == FILE_SUPERSEDE) {
|
||||
if (in_status == NFS4ERR_NOENT)
|
||||
*last_error = ERROR_FILE_NOT_FOUND;
|
||||
//remove and recreate the file
|
||||
*create = OPEN4_CREATE;
|
||||
if (persistent) *createhowmode = GUARDED4;
|
||||
else *createhowmode = EXCLUSIVE4_1;
|
||||
} else if (disposition == FILE_CREATE) {
|
||||
// if lookup succeeded which means the file exist, return an error
|
||||
if (!in_status)
|
||||
status = ERROR_FILE_EXISTS;
|
||||
else {
|
||||
*create = OPEN4_CREATE;
|
||||
if (persistent) *createhowmode = GUARDED4;
|
||||
else *createhowmode = EXCLUSIVE4_1;
|
||||
}
|
||||
} else if (disposition == FILE_OPEN) {
|
||||
if (in_status == NFS4ERR_NOENT)
|
||||
status = ERROR_FILE_NOT_FOUND;
|
||||
else
|
||||
*create = OPEN4_NOCREATE;
|
||||
} else if (disposition == FILE_OPEN_IF) {
|
||||
if (in_status == NFS4ERR_NOENT) {
|
||||
dprintf(1, "creating new file\n");
|
||||
*create = OPEN4_CREATE;
|
||||
*last_error = ERROR_FILE_NOT_FOUND;
|
||||
} else {
|
||||
dprintf(1, "opening existing file\n");
|
||||
*create = OPEN4_NOCREATE;
|
||||
}
|
||||
} else if (disposition == FILE_OVERWRITE) {
|
||||
if (in_status == NFS4ERR_NOENT)
|
||||
status = ERROR_FILE_NOT_FOUND;
|
||||
//truncate file
|
||||
*create = OPEN4_CREATE;
|
||||
} else if (disposition == FILE_OVERWRITE_IF) {
|
||||
if (in_status == NFS4ERR_NOENT)
|
||||
*last_error = ERROR_FILE_NOT_FOUND;
|
||||
//truncate file
|
||||
*create = OPEN4_CREATE;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static void map_access_2_allowdeny(ULONG access_mask, ULONG access_mode,
|
||||
ULONG disposition, uint32_t *allow, uint32_t *deny)
|
||||
{
|
||||
if ((access_mask &
|
||||
(FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES)) &&
|
||||
(access_mask & (FILE_READ_DATA | FILE_EXECUTE)))
|
||||
*allow = OPEN4_SHARE_ACCESS_BOTH;
|
||||
else if (access_mask & (FILE_READ_DATA | FILE_EXECUTE))
|
||||
*allow = OPEN4_SHARE_ACCESS_READ;
|
||||
else if (access_mask &
|
||||
(FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES))
|
||||
*allow = OPEN4_SHARE_ACCESS_WRITE;
|
||||
/* if we are creating a file and no data access is specified, then
|
||||
* do an open and request no delegations. example open with share access 0
|
||||
* and share deny 0 (ie deny_both).
|
||||
*/
|
||||
if ((disposition == FILE_CREATE || disposition == FILE_OPEN_IF ||
|
||||
disposition == FILE_OVERWRITE_IF || disposition == FILE_SUPERSEDE ||
|
||||
disposition == FILE_OVERWRITE) &&
|
||||
!(access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA |
|
||||
FILE_WRITE_ATTRIBUTES | FILE_READ_DATA | FILE_EXECUTE)))
|
||||
*allow = OPEN4_SHARE_ACCESS_READ | OPEN4_SHARE_ACCESS_WANT_NO_DELEG;
|
||||
|
||||
#define FIX_ALLOW_DENY_WIN2NFS_CONVERSION
|
||||
#ifdef FIX_ALLOW_DENY_WIN2NFS_CONVERSION
|
||||
if ((access_mode & FILE_SHARE_READ) &&
|
||||
(access_mode & FILE_SHARE_WRITE))
|
||||
*deny = OPEN4_SHARE_DENY_NONE;
|
||||
else if (access_mode & FILE_SHARE_READ)
|
||||
*deny = OPEN4_SHARE_DENY_WRITE;
|
||||
else if (access_mode & FILE_SHARE_WRITE)
|
||||
*deny = OPEN4_SHARE_DENY_READ;
|
||||
else
|
||||
*deny = OPEN4_SHARE_DENY_BOTH;
|
||||
#else
|
||||
// AGLO: 11/13/2009.
|
||||
// readonly file that is being opened for reading with a
|
||||
// share read mode given above logic translates into deny
|
||||
// write and linux server does not allow it.
|
||||
*deny = OPEN4_SHARE_DENY_NONE;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int check_execute_access(nfs41_open_state *state)
|
||||
{
|
||||
uint32_t supported, access;
|
||||
int status = nfs41_access(state->session, &state->file,
|
||||
ACCESS4_EXECUTE | ACCESS4_READ, &supported, &access);
|
||||
if (status) {
|
||||
eprintf("nfs41_access() failed with %s for %s\n",
|
||||
nfs_error_string(status), state->path.path);
|
||||
status = ERROR_ACCESS_DENIED;
|
||||
} else if ((supported & ACCESS4_EXECUTE) == 0) {
|
||||
/* server can't verify execute access;
|
||||
* for now, assume that read access is good enough */
|
||||
if ((supported & ACCESS4_READ) == 0 || (access & ACCESS4_READ) == 0) {
|
||||
eprintf("server can't verify execute access, and user does "
|
||||
"not have read access to file %s\n", state->path.path);
|
||||
status = ERROR_ACCESS_DENIED;
|
||||
}
|
||||
} else if ((access & ACCESS4_EXECUTE) == 0) {
|
||||
dprintf(1, "user does not have execute access to file %s\n",
|
||||
state->path.path);
|
||||
status = ERROR_ACCESS_DENIED;
|
||||
} else
|
||||
dprintf(2, "user has execute access to file\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
static int create_with_ea(
|
||||
IN uint32_t disposition,
|
||||
IN uint32_t lookup_status)
|
||||
{
|
||||
/* only set EAs on file creation */
|
||||
return disposition == FILE_SUPERSEDE || disposition == FILE_CREATE
|
||||
|| disposition == FILE_OVERWRITE || disposition == FILE_OVERWRITE_IF
|
||||
|| (disposition == FILE_OPEN_IF && lookup_status == NFS4ERR_NOENT);
|
||||
}
|
||||
|
||||
static int handle_open(nfs41_upcall *upcall)
|
||||
{
|
||||
int status = 0;
|
||||
open_upcall_args *args = &upcall->args.open;
|
||||
nfs41_open_state *state;
|
||||
nfs41_file_info info = { 0 };
|
||||
|
||||
status = create_open_state(args->path, args->open_owner_id, &state);
|
||||
if (status) {
|
||||
eprintf("create_open_state(%d) failed with %d\n",
|
||||
args->open_owner_id, status);
|
||||
goto out;
|
||||
}
|
||||
state->srv_open = args->srv_open;
|
||||
|
||||
// first check if windows told us it's a directory
|
||||
if (args->create_opts & FILE_DIRECTORY_FILE)
|
||||
state->type = NF4DIR;
|
||||
else
|
||||
state->type = NF4REG;
|
||||
|
||||
// always do a lookup
|
||||
status = nfs41_lookup(upcall->root_ref, nfs41_root_session(upcall->root_ref),
|
||||
&state->path, &state->parent, &state->file, &info, &state->session);
|
||||
|
||||
if (status == ERROR_REPARSE) {
|
||||
uint32_t depth = 0;
|
||||
/* one of the parent components was a symlink */
|
||||
do {
|
||||
if (++depth > NFS41_MAX_SYMLINK_DEPTH) {
|
||||
status = ERROR_TOO_MANY_LINKS;
|
||||
goto out_free_state;
|
||||
}
|
||||
|
||||
/* replace the path with the symlink target's */
|
||||
status = nfs41_symlink_target(state->session,
|
||||
&state->parent, &state->path);
|
||||
if (status) {
|
||||
/* can't do the reparse if we can't get the target */
|
||||
eprintf("nfs41_symlink_target() failed with %d\n", status);
|
||||
goto out_free_state;
|
||||
}
|
||||
|
||||
/* redo the lookup until it doesn't return REPARSE */
|
||||
status = nfs41_lookup(upcall->root_ref, state->session,
|
||||
&state->path, &state->parent, NULL, NULL, &state->session);
|
||||
} while (status == ERROR_REPARSE);
|
||||
|
||||
if (status == NO_ERROR || status == ERROR_FILE_NOT_FOUND) {
|
||||
abs_path_copy(&args->symlink, &state->path);
|
||||
status = NO_ERROR;
|
||||
upcall->last_error = ERROR_REPARSE;
|
||||
args->symlink_embedded = TRUE;
|
||||
}
|
||||
goto out_free_state;
|
||||
}
|
||||
|
||||
// now if file/dir exists, use type returned by lookup
|
||||
if (status == NO_ERROR) {
|
||||
if (info.type == NF4DIR) {
|
||||
dprintf(2, "handle_nfs41_open: DIRECTORY\n");
|
||||
if (args->create_opts & FILE_NON_DIRECTORY_FILE) {
|
||||
eprintf("trying to open directory %s as a file\n",
|
||||
state->path.path);
|
||||
status = ERROR_DIRECTORY;
|
||||
goto out_free_state;
|
||||
}
|
||||
} else if (info.type == NF4REG) {
|
||||
dprintf(2, "handle nfs41_open: FILE\n");
|
||||
if (args->create_opts & FILE_DIRECTORY_FILE) {
|
||||
eprintf("trying to open file %s as a directory\n",
|
||||
state->path.path);
|
||||
status = ERROR_BAD_FILE_TYPE;
|
||||
goto out_free_state;
|
||||
}
|
||||
} else if (info.type == NF4LNK) {
|
||||
dprintf(2, "handle nfs41_open: SYMLINK\n");
|
||||
if (args->create_opts & FILE_OPEN_REPARSE_POINT) {
|
||||
/* continue and open the symlink itself, but we need to
|
||||
* know if the target is a regular file or directory */
|
||||
nfs41_file_info target_info;
|
||||
int target_status = nfs41_symlink_follow(upcall->root_ref,
|
||||
state->session, &state->file, &target_info);
|
||||
if (target_status == NO_ERROR && target_info.type == NF4DIR)
|
||||
info.symlink_dir = TRUE;
|
||||
} else {
|
||||
/* replace the path with the symlink target */
|
||||
status = nfs41_symlink_target(state->session,
|
||||
&state->file, &args->symlink);
|
||||
if (status) {
|
||||
eprintf("nfs41_symlink_target() for %s failed with %d\n",
|
||||
args->path, status);
|
||||
} else {
|
||||
/* tell the driver to call RxPrepareToReparseSymbolicLink() */
|
||||
upcall->last_error = ERROR_REPARSE;
|
||||
args->symlink_embedded = FALSE;
|
||||
}
|
||||
goto out_free_state;
|
||||
}
|
||||
} else
|
||||
dprintf(2, "handle_open(): unsupported type=%d\n", info.type);
|
||||
state->type = info.type;
|
||||
} else if (status != ERROR_FILE_NOT_FOUND)
|
||||
goto out_free_state;
|
||||
|
||||
/* XXX: this is a hard-coded check for the open arguments we see from
|
||||
* the CreateSymbolicLink() system call. we respond to this by deferring
|
||||
* the CREATE until we get the upcall to set the symlink. this approach
|
||||
* is troublesome for two reasons:
|
||||
* -an application might use these exact arguments to create a normal
|
||||
* file, and we would return success without actually creating it
|
||||
* -an application could create a symlink by sending the FSCTL to set
|
||||
* the reparse point manually, and their open might be different. in
|
||||
* this case we'd create the file on open, and need to remove it
|
||||
* before creating the symlink */
|
||||
if (args->disposition == FILE_CREATE &&
|
||||
args->access_mask == (FILE_WRITE_ATTRIBUTES | SYNCHRONIZE | DELETE) &&
|
||||
args->access_mode == 0 &&
|
||||
args->create_opts & FILE_OPEN_REPARSE_POINT) {
|
||||
/* fail if the file already exists */
|
||||
if (status == NO_ERROR) {
|
||||
status = ERROR_FILE_EXISTS;
|
||||
goto out_free_state;
|
||||
}
|
||||
|
||||
/* defer the call to CREATE until we get the symlink set upcall */
|
||||
dprintf(1, "trying to create a symlink, deferring create\n");
|
||||
|
||||
/* because of WRITE_ATTR access, be prepared for a setattr upcall;
|
||||
* will crash if the superblock is null, so use the parent's */
|
||||
state->file.fh.superblock = state->parent.fh.superblock;
|
||||
|
||||
status = NO_ERROR;
|
||||
} else if (args->symlink.len) {
|
||||
/* handle cygwin symlinks */
|
||||
nfs41_file_info createattrs;
|
||||
createattrs.attrmask.count = 2;
|
||||
createattrs.attrmask.arr[0] = 0;
|
||||
createattrs.attrmask.arr[1] = FATTR4_WORD1_MODE;
|
||||
createattrs.mode = 0777;
|
||||
|
||||
dprintf(1, "creating cygwin symlink %s -> %s\n",
|
||||
state->file.name.name, args->symlink.path);
|
||||
|
||||
status = nfs41_create(state->session, NF4LNK, &createattrs,
|
||||
args->symlink.path, &state->parent, &state->file, &info);
|
||||
if (status) {
|
||||
eprintf("nfs41_create() for symlink=%s failed with %s\n",
|
||||
args->symlink.path, nfs_error_string(status));
|
||||
status = map_symlink_errors(status);
|
||||
goto out_free_state;
|
||||
}
|
||||
nfs_to_basic_info(&info, &args->basic_info);
|
||||
nfs_to_standard_info(&info, &args->std_info);
|
||||
args->mode = info.mode;
|
||||
args->changeattr = info.change;
|
||||
} else if (open_for_attributes(state->type, args->access_mask,
|
||||
args->disposition, status)) {
|
||||
if (status) {
|
||||
dprintf(1, "nfs41_lookup failed with %d\n", status);
|
||||
goto out_free_state;
|
||||
}
|
||||
|
||||
nfs_to_basic_info(&info, &args->basic_info);
|
||||
nfs_to_standard_info(&info, &args->std_info);
|
||||
args->mode = info.mode;
|
||||
args->changeattr = info.change;
|
||||
} else {
|
||||
nfs41_file_info createattrs = { 0 };
|
||||
uint32_t create = 0, createhowmode = 0, lookup_status = status;
|
||||
|
||||
if (!lookup_status && (args->disposition == FILE_OVERWRITE ||
|
||||
args->disposition == FILE_OVERWRITE_IF ||
|
||||
args->disposition == FILE_SUPERSEDE)) {
|
||||
if ((info.hidden && !(args->file_attrs & FILE_ATTRIBUTE_HIDDEN)) ||
|
||||
(info.system && !(args->file_attrs & FILE_ATTRIBUTE_SYSTEM))) {
|
||||
status = ERROR_ACCESS_DENIED;
|
||||
goto out_free_state;
|
||||
}
|
||||
if (args->disposition != FILE_SUPERSEDE)
|
||||
args->mode = info.mode;
|
||||
}
|
||||
createattrs.attrmask.count = 2;
|
||||
createattrs.attrmask.arr[0] = FATTR4_WORD0_HIDDEN | FATTR4_WORD0_ARCHIVE;
|
||||
createattrs.attrmask.arr[1] = FATTR4_WORD1_MODE | FATTR4_WORD1_SYSTEM;
|
||||
createattrs.mode = args->mode;
|
||||
createattrs.hidden = args->file_attrs & FILE_ATTRIBUTE_HIDDEN ? 1 : 0;
|
||||
createattrs.system = args->file_attrs & FILE_ATTRIBUTE_SYSTEM ? 1 : 0;
|
||||
createattrs.archive = args->file_attrs & FILE_ATTRIBUTE_ARCHIVE ? 1 : 0;
|
||||
|
||||
map_access_2_allowdeny(args->access_mask, args->access_mode,
|
||||
args->disposition, &state->share_access, &state->share_deny);
|
||||
status = map_disposition_2_nfsopen(args->disposition, status,
|
||||
state->session->flags & CREATE_SESSION4_FLAG_PERSIST,
|
||||
&create, &createhowmode, &upcall->last_error);
|
||||
if (status)
|
||||
goto out_free_state;
|
||||
|
||||
if (args->access_mask & FILE_EXECUTE && state->file.fh.len) {
|
||||
status = check_execute_access(state);
|
||||
if (status)
|
||||
goto out_free_state;
|
||||
}
|
||||
|
||||
supersede_retry:
|
||||
// XXX file exists and we have to remove it first
|
||||
if (args->disposition == FILE_SUPERSEDE && lookup_status == NO_ERROR) {
|
||||
nfs41_component *name = &state->file.name;
|
||||
if (!(args->create_opts & FILE_DIRECTORY_FILE))
|
||||
nfs41_delegation_return(state->session, &state->file,
|
||||
OPEN_DELEGATE_WRITE, TRUE);
|
||||
|
||||
dprintf(1, "open for FILE_SUPERSEDE removing %s first\n", name->name);
|
||||
status = nfs41_remove(state->session, &state->parent,
|
||||
name, state->file.fh.fileid);
|
||||
if (status)
|
||||
goto out_free_state;
|
||||
}
|
||||
|
||||
if (create == OPEN4_CREATE && (args->create_opts & FILE_DIRECTORY_FILE)) {
|
||||
status = nfs41_create(state->session, NF4DIR, &createattrs, NULL,
|
||||
&state->parent, &state->file, &info);
|
||||
args->created = status == NFS4_OK ? TRUE : FALSE;
|
||||
} else {
|
||||
createattrs.attrmask.arr[0] |= FATTR4_WORD0_SIZE;
|
||||
createattrs.size = 0;
|
||||
dprintf(1, "creating with mod %o\n", args->mode);
|
||||
status = open_or_delegate(state, create, createhowmode, &createattrs,
|
||||
TRUE, &info);
|
||||
if (status == NFS4_OK && state->delegation.state)
|
||||
args->deleg_type = state->delegation.state->state.type;
|
||||
}
|
||||
if (status) {
|
||||
dprintf(1, "%s failed with %s\n", (create == OPEN4_CREATE &&
|
||||
(args->create_opts & FILE_DIRECTORY_FILE))?"nfs41_create":"nfs41_open",
|
||||
nfs_error_string(status));
|
||||
if (args->disposition == FILE_SUPERSEDE && status == NFS4ERR_EXIST)
|
||||
goto supersede_retry;
|
||||
status = nfs_to_windows_error(status, ERROR_FILE_NOT_FOUND);
|
||||
goto out_free_state;
|
||||
} else {
|
||||
nfs_to_basic_info(&info, &args->basic_info);
|
||||
nfs_to_standard_info(&info, &args->std_info);
|
||||
args->mode = info.mode;
|
||||
args->changeattr = info.change;
|
||||
}
|
||||
|
||||
/* set extended attributes on file creation */
|
||||
if (args->ea && create_with_ea(args->disposition, lookup_status)) {
|
||||
status = nfs41_ea_set(state, args->ea);
|
||||
status = nfs_to_windows_error(status, ERROR_FILE_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
upcall->state_ref = state;
|
||||
nfs41_open_state_ref(upcall->state_ref);
|
||||
out:
|
||||
return status;
|
||||
out_free_state:
|
||||
nfs41_open_state_deref(state);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int marshall_open(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
open_upcall_args *args = &upcall->args.open;
|
||||
|
||||
status = safe_write(&buffer, length, &args->basic_info, sizeof(args->basic_info));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &args->std_info, sizeof(args->std_info));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &upcall->state_ref, sizeof(HANDLE));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &args->mode, sizeof(args->mode));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &args->changeattr, sizeof(args->changeattr));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &args->deleg_type, sizeof(args->deleg_type));
|
||||
if (status) goto out;
|
||||
if (upcall->last_error == ERROR_REPARSE) {
|
||||
unsigned short len = (args->symlink.len + 1) * sizeof(WCHAR);
|
||||
status = safe_write(&buffer, length, &args->symlink_embedded, sizeof(BOOLEAN));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &len, sizeof(len));
|
||||
if (status) goto out;
|
||||
/* convert args->symlink to wchar */
|
||||
if (*length <= len || !MultiByteToWideChar(CP_UTF8, 0,
|
||||
args->symlink.path, args->symlink.len,
|
||||
(LPWSTR)buffer, len / sizeof(WCHAR))) {
|
||||
status = ERROR_BUFFER_OVERFLOW;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
dprintf(2, "NFS41_OPEN: downcall open_state=0x%p mode %o changeattr 0x%llu\n",
|
||||
upcall->state_ref, args->mode, args->changeattr);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static void cancel_open(IN nfs41_upcall *upcall)
|
||||
{
|
||||
int status = NFS4_OK;
|
||||
open_upcall_args *args = &upcall->args.open;
|
||||
nfs41_open_state *state = upcall->state_ref;
|
||||
|
||||
dprintf(1, "--> cancel_open('%s')\n", args->path);
|
||||
|
||||
if (upcall->state_ref == NULL ||
|
||||
upcall->state_ref == INVALID_HANDLE_VALUE)
|
||||
goto out; /* if handle_open() failed, the state was already freed */
|
||||
|
||||
if (state->do_close) {
|
||||
stateid_arg stateid;
|
||||
stateid.open = state;
|
||||
stateid.delegation = NULL;
|
||||
stateid.type = STATEID_OPEN;
|
||||
memcpy(&stateid.stateid, &state->stateid, sizeof(stateid4));
|
||||
|
||||
status = nfs41_close(state->session, &state->file, &stateid);
|
||||
if (status)
|
||||
dprintf(1, "cancel_open: nfs41_close() failed with %s\n",
|
||||
nfs_error_string(status));
|
||||
|
||||
} else if (args->created) {
|
||||
const nfs41_component *name = &state->file.name;
|
||||
/* break any delegations and truncate before REMOVE */
|
||||
nfs41_delegation_return(state->session, &state->file,
|
||||
OPEN_DELEGATE_WRITE, TRUE);
|
||||
status = nfs41_remove(state->session, &state->parent,
|
||||
name, state->file.fh.fileid);
|
||||
if (status)
|
||||
dprintf(1, "cancel_open: nfs41_remove() failed with %s\n",
|
||||
nfs_error_string(status));
|
||||
}
|
||||
|
||||
/* remove from the client's list of state for recovery */
|
||||
client_state_remove(state);
|
||||
nfs41_open_state_deref(state);
|
||||
out:
|
||||
status = nfs_to_windows_error(status, ERROR_INTERNAL_ERROR);
|
||||
dprintf(1, "<-- cancel_open() returning %d\n", status);
|
||||
}
|
||||
|
||||
|
||||
/* NFS41_CLOSE */
|
||||
static int parse_close(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
close_upcall_args *args = &upcall->args.close;
|
||||
|
||||
status = safe_read(&buffer, &length, &args->remove, sizeof(BOOLEAN));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->srv_open, sizeof(HANDLE));
|
||||
if (status) goto out;
|
||||
if (args->remove) {
|
||||
status = get_name(&buffer, &length, &args->path);
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->renamed, sizeof(BOOLEAN));
|
||||
if (status) goto out;
|
||||
}
|
||||
|
||||
dprintf(1, "parsing NFS41_CLOSE: remove=%d srv_open=%x renamed=%d "
|
||||
"filename='%s'\n", args->remove, args->srv_open, args->renamed,
|
||||
args->remove ? args->path : "");
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int do_nfs41_close(nfs41_open_state *state)
|
||||
{
|
||||
int status;
|
||||
stateid_arg stateid;
|
||||
stateid.open = state;
|
||||
stateid.delegation = NULL;
|
||||
stateid.type = STATEID_OPEN;
|
||||
memcpy(&stateid.stateid, &state->stateid, sizeof(stateid4));
|
||||
|
||||
status = nfs41_close(state->session, &state->file, &stateid);
|
||||
if (status) {
|
||||
dprintf(1, "nfs41_close() failed with error %s.\n",
|
||||
nfs_error_string(status));
|
||||
status = nfs_to_windows_error(status, ERROR_INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int handle_close(nfs41_upcall *upcall)
|
||||
{
|
||||
int status = NFS4_OK, rm_status = NFS4_OK;
|
||||
close_upcall_args *args = &upcall->args.close;
|
||||
nfs41_open_state *state = upcall->state_ref;
|
||||
|
||||
/* return associated file layouts if necessary */
|
||||
if (state->type == NF4REG)
|
||||
pnfs_layout_state_close(state->session, state, args->remove);
|
||||
|
||||
if (state->srv_open == args->srv_open)
|
||||
nfs41_delegation_remove_srvopen(state->session, &state->file);
|
||||
|
||||
if (args->remove) {
|
||||
nfs41_component *name = &state->file.name;
|
||||
|
||||
if (args->renamed) {
|
||||
dprintf(1, "removing a renamed file %s\n", name->name);
|
||||
create_silly_rename(&state->path, &state->file.fh, name);
|
||||
status = do_nfs41_close(state);
|
||||
if (status)
|
||||
goto out;
|
||||
else
|
||||
state->do_close = 0;
|
||||
}
|
||||
|
||||
/* break any delegations and truncate before REMOVE */
|
||||
nfs41_delegation_return(state->session, &state->file,
|
||||
OPEN_DELEGATE_WRITE, TRUE);
|
||||
|
||||
dprintf(1, "calling nfs41_remove for %s\n", name->name);
|
||||
retry_delete:
|
||||
rm_status = nfs41_remove(state->session, &state->parent,
|
||||
name, state->file.fh.fileid);
|
||||
if (rm_status) {
|
||||
if (rm_status == NFS4ERR_FILE_OPEN) {
|
||||
status = do_nfs41_close(state);
|
||||
if (!status) {
|
||||
state->do_close = 0;
|
||||
goto retry_delete;
|
||||
} else goto out;
|
||||
}
|
||||
dprintf(1, "nfs41_remove() failed with error %s.\n",
|
||||
nfs_error_string(rm_status));
|
||||
rm_status = nfs_to_windows_error(rm_status, ERROR_INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
if (state->do_close) {
|
||||
status = do_nfs41_close(state);
|
||||
}
|
||||
out:
|
||||
/* remove from the client's list of state for recovery */
|
||||
client_state_remove(state);
|
||||
|
||||
if (status || !rm_status)
|
||||
return status;
|
||||
else
|
||||
return rm_status;
|
||||
}
|
||||
|
||||
static void cleanup_close(nfs41_upcall *upcall)
|
||||
{
|
||||
/* release the initial reference from create_open_state() */
|
||||
nfs41_open_state_deref(upcall->state_ref);
|
||||
}
|
||||
|
||||
|
||||
const nfs41_upcall_op nfs41_op_open = {
|
||||
parse_open,
|
||||
handle_open,
|
||||
marshall_open,
|
||||
cancel_open
|
||||
};
|
||||
const nfs41_upcall_op nfs41_op_close = {
|
||||
parse_close,
|
||||
handle_close,
|
||||
NULL,
|
||||
NULL,
|
||||
cleanup_close
|
||||
};
|
368
reactos/base/services/nfsd/pnfs.h
Normal file
368
reactos/base/services/nfsd/pnfs.h
Normal file
|
@ -0,0 +1,368 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#ifndef __PNFS_H__
|
||||
#define __PNFS_H__
|
||||
|
||||
#include "nfs41_types.h"
|
||||
#include "list.h"
|
||||
|
||||
|
||||
/* preprocessor options */
|
||||
#ifndef PNFS_DISABLE
|
||||
|
||||
# ifndef PNFS_DISABLE_READ
|
||||
# define PNFS_ENABLE_READ
|
||||
# endif
|
||||
# ifndef PNFS_DISABLE_WRITE
|
||||
# define PNFS_ENABLE_WRITE
|
||||
# endif
|
||||
|
||||
# define PNFS_THREADING
|
||||
|
||||
/* XXX: the thread-by-server model breaks down when using dense layouts,
|
||||
* because multiple stripes could be mapped to a single data server, and
|
||||
* the per-data-server thread would have to send a COMMIT for each stripe */
|
||||
//# define PNFS_THREAD_BY_SERVER
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* forward declarations from nfs41.h */
|
||||
struct __nfs41_client;
|
||||
struct __nfs41_session;
|
||||
struct __nfs41_open_state;
|
||||
struct __nfs41_root;
|
||||
struct __stateid_arg;
|
||||
|
||||
|
||||
/* pnfs error values, in order of increasing severity */
|
||||
enum pnfs_status {
|
||||
PNFS_SUCCESS = 0,
|
||||
PNFS_PENDING,
|
||||
PNFS_READ_EOF,
|
||||
PNFSERR_NOT_SUPPORTED,
|
||||
PNFSERR_NOT_CONNECTED,
|
||||
PNFSERR_IO,
|
||||
PNFSERR_NO_DEVICE,
|
||||
PNFSERR_NO_LAYOUT,
|
||||
PNFSERR_INVALID_FH_LIST,
|
||||
PNFSERR_INVALID_DS_INDEX,
|
||||
PNFSERR_RESOURCES,
|
||||
PNFSERR_LAYOUT_RECALLED,
|
||||
PNFSERR_LAYOUT_CHANGED,
|
||||
};
|
||||
|
||||
enum pnfs_layout_type {
|
||||
PNFS_LAYOUTTYPE_FILE = 1,
|
||||
PNFS_LAYOUTTYPE_OBJECT = 2,
|
||||
PNFS_LAYOUTTYPE_BLOCK = 3
|
||||
};
|
||||
|
||||
enum pnfs_iomode {
|
||||
PNFS_IOMODE_READ = 0x1,
|
||||
PNFS_IOMODE_RW = 0x2,
|
||||
PNFS_IOMODE_ANY = PNFS_IOMODE_READ | PNFS_IOMODE_RW
|
||||
};
|
||||
|
||||
enum pnfs_layout_status {
|
||||
/* a LAYOUTGET error indicated that this layout will never be granted */
|
||||
PNFS_LAYOUT_UNAVAILABLE = 0x10,
|
||||
/* LAYOUTGET returned BADIOMODE, so a RW layout will never be granted */
|
||||
PNFS_LAYOUT_NOT_RW = 0x20,
|
||||
};
|
||||
|
||||
enum pnfs_device_status {
|
||||
/* GETDEVICEINFO was successful */
|
||||
PNFS_DEVICE_GRANTED = 0x1,
|
||||
/* a bulk recall or lease expiration led to device invalidation */
|
||||
PNFS_DEVICE_REVOKED = 0x2,
|
||||
};
|
||||
|
||||
enum pnfs_return_type {
|
||||
PNFS_RETURN_FILE = 1,
|
||||
PNFS_RETURN_FSID = 2,
|
||||
PNFS_RETURN_ALL = 3
|
||||
};
|
||||
|
||||
#define NFL4_UFLG_MASK 0x0000003F
|
||||
#define NFL4_UFLG_DENSE 0x00000001
|
||||
#define NFL4_UFLG_COMMIT_THRU_MDS 0x00000002
|
||||
#define NFL4_UFLG_STRIPE_UNIT_SIZE_MASK 0xFFFFFFC0
|
||||
|
||||
#define PNFS_DEVICEID_SIZE 16
|
||||
|
||||
|
||||
/* device */
|
||||
typedef struct __pnfs_device {
|
||||
unsigned char deviceid[PNFS_DEVICEID_SIZE];
|
||||
enum pnfs_layout_type type;
|
||||
enum pnfs_device_status status;
|
||||
uint32_t layout_count; /* layouts using this device */
|
||||
CRITICAL_SECTION lock;
|
||||
} pnfs_device;
|
||||
|
||||
typedef struct __pnfs_stripe_indices {
|
||||
uint32_t count;
|
||||
uint32_t *arr;
|
||||
} pnfs_stripe_indices;
|
||||
|
||||
typedef struct __pnfs_data_server {
|
||||
struct __nfs41_client *client;
|
||||
multi_addr4 addrs;
|
||||
SRWLOCK lock;
|
||||
} pnfs_data_server;
|
||||
|
||||
typedef struct __pnfs_data_server_list {
|
||||
uint32_t count;
|
||||
pnfs_data_server *arr;
|
||||
} pnfs_data_server_list;
|
||||
|
||||
typedef struct __pnfs_file_device {
|
||||
pnfs_device device;
|
||||
pnfs_stripe_indices stripes;
|
||||
pnfs_data_server_list servers;
|
||||
struct pnfs_file_device_list *devices; /* -> nfs41_client.devices */
|
||||
struct list_entry entry; /* position in devices */
|
||||
} pnfs_file_device;
|
||||
|
||||
|
||||
/* layout */
|
||||
typedef struct __pnfs_layout_state {
|
||||
nfs41_fh meta_fh;
|
||||
stateid4 stateid;
|
||||
struct list_entry entry; /* position in nfs41_client.layouts */
|
||||
struct list_entry layouts; /* list of pnfs_file_layouts */
|
||||
struct list_entry recalls; /* list of pnfs_layouts */
|
||||
enum pnfs_layout_status status;
|
||||
bool_t return_on_close;
|
||||
LONG open_count; /* for return on last close */
|
||||
uint32_t io_count; /* number of pending io operations */
|
||||
bool_t pending; /* pending LAYOUTGET/LAYOUTRETURN */
|
||||
SRWLOCK lock;
|
||||
CONDITION_VARIABLE cond;
|
||||
} pnfs_layout_state;
|
||||
|
||||
typedef struct __pnfs_layout {
|
||||
struct list_entry entry;
|
||||
uint64_t offset;
|
||||
uint64_t length;
|
||||
enum pnfs_iomode iomode;
|
||||
enum pnfs_layout_type type;
|
||||
} pnfs_layout;
|
||||
|
||||
typedef struct __pnfs_file_layout_handles {
|
||||
uint32_t count;
|
||||
nfs41_path_fh *arr;
|
||||
} pnfs_file_layout_handles;
|
||||
|
||||
typedef struct __pnfs_file_layout {
|
||||
pnfs_layout layout;
|
||||
pnfs_file_layout_handles filehandles;
|
||||
unsigned char deviceid[PNFS_DEVICEID_SIZE];
|
||||
pnfs_file_device *device;
|
||||
uint64_t pattern_offset;
|
||||
uint32_t first_index;
|
||||
uint32_t util;
|
||||
} pnfs_file_layout;
|
||||
|
||||
|
||||
/* pnfs_layout.c */
|
||||
struct pnfs_layout_list;
|
||||
struct cb_layoutrecall_args;
|
||||
|
||||
enum pnfs_status pnfs_layout_list_create(
|
||||
OUT struct pnfs_layout_list **layouts_out);
|
||||
|
||||
void pnfs_layout_list_free(
|
||||
IN struct pnfs_layout_list *layouts);
|
||||
|
||||
enum pnfs_status pnfs_layout_state_open(
|
||||
IN struct __nfs41_open_state *state,
|
||||
OUT pnfs_layout_state **layout_out);
|
||||
|
||||
/* expects caller to hold an exclusive lock on pnfs_layout_state */
|
||||
enum pnfs_status pnfs_layout_state_prepare(
|
||||
IN pnfs_layout_state *state,
|
||||
IN struct __nfs41_session *session,
|
||||
IN nfs41_path_fh *meta_file,
|
||||
IN struct __stateid_arg *stateid,
|
||||
IN enum pnfs_iomode iomode,
|
||||
IN uint64_t offset,
|
||||
IN uint64_t length);
|
||||
|
||||
void pnfs_layout_state_close(
|
||||
IN struct __nfs41_session *session,
|
||||
IN struct __nfs41_open_state *state,
|
||||
IN bool_t remove);
|
||||
|
||||
enum pnfs_status pnfs_file_layout_recall(
|
||||
IN struct __nfs41_client *client,
|
||||
IN const struct cb_layoutrecall_args *recall);
|
||||
|
||||
/* expects caller to hold a shared lock on pnfs_layout_state */
|
||||
enum pnfs_status pnfs_layout_recall_status(
|
||||
IN const pnfs_layout_state *state,
|
||||
IN const pnfs_layout *layout);
|
||||
|
||||
void pnfs_layout_recall_fenced(
|
||||
IN pnfs_layout_state *state,
|
||||
IN const pnfs_layout *layout);
|
||||
|
||||
/* expects caller to hold an exclusive lock on pnfs_layout_state */
|
||||
void pnfs_layout_io_start(
|
||||
IN pnfs_layout_state *state);
|
||||
|
||||
void pnfs_layout_io_finished(
|
||||
IN pnfs_layout_state *state);
|
||||
|
||||
|
||||
/* pnfs_device.c */
|
||||
struct pnfs_file_device_list;
|
||||
|
||||
enum pnfs_status pnfs_file_device_list_create(
|
||||
OUT struct pnfs_file_device_list **devices_out);
|
||||
|
||||
void pnfs_file_device_list_free(
|
||||
IN struct pnfs_file_device_list *devices);
|
||||
|
||||
void pnfs_file_device_list_invalidate(
|
||||
IN struct pnfs_file_device_list *devices);
|
||||
|
||||
enum pnfs_status pnfs_file_device_get(
|
||||
IN struct __nfs41_session *session,
|
||||
IN struct pnfs_file_device_list *devices,
|
||||
IN unsigned char *deviceid,
|
||||
OUT pnfs_file_device **device_out);
|
||||
|
||||
void pnfs_file_device_put(
|
||||
IN pnfs_file_device *device);
|
||||
|
||||
struct notify_deviceid4; /* from nfs41_callback.h */
|
||||
enum notify_deviceid_type4;
|
||||
enum pnfs_status pnfs_file_device_notify(
|
||||
IN struct pnfs_file_device_list *devices,
|
||||
IN const struct notify_deviceid4 *change);
|
||||
|
||||
enum pnfs_status pnfs_data_server_client(
|
||||
IN struct __nfs41_root *root,
|
||||
IN pnfs_data_server *server,
|
||||
IN uint32_t default_lease,
|
||||
OUT struct __nfs41_client **client_out);
|
||||
|
||||
|
||||
/* pnfs_io.c */
|
||||
enum pnfs_status pnfs_read(
|
||||
IN struct __nfs41_root *root,
|
||||
IN struct __nfs41_open_state *state,
|
||||
IN struct __stateid_arg *stateid,
|
||||
IN pnfs_layout_state *layout,
|
||||
IN uint64_t offset,
|
||||
IN uint64_t length,
|
||||
OUT unsigned char *buffer_out,
|
||||
OUT ULONG *len_out);
|
||||
|
||||
enum pnfs_status pnfs_write(
|
||||
IN struct __nfs41_root *root,
|
||||
IN struct __nfs41_open_state *state,
|
||||
IN struct __stateid_arg *stateid,
|
||||
IN pnfs_layout_state *layout,
|
||||
IN uint64_t offset,
|
||||
IN uint64_t length,
|
||||
IN unsigned char *buffer,
|
||||
OUT ULONG *len_out,
|
||||
OUT nfs41_file_info *cinfo);
|
||||
|
||||
|
||||
/* helper functions */
|
||||
#ifndef __REACTOS__
|
||||
__inline int is_dense(
|
||||
#else
|
||||
FORCEINLINE int is_dense(
|
||||
#endif
|
||||
IN const pnfs_file_layout *layout)
|
||||
{
|
||||
return (layout->util & NFL4_UFLG_DENSE) != 0;
|
||||
}
|
||||
#ifndef __REACTOS__
|
||||
__inline int should_commit_to_mds(
|
||||
#else
|
||||
FORCEINLINE int should_commit_to_mds(
|
||||
#endif
|
||||
IN const pnfs_file_layout *layout)
|
||||
{
|
||||
return (layout->util & NFL4_UFLG_COMMIT_THRU_MDS) != 0;
|
||||
}
|
||||
#ifndef __REACTOS__
|
||||
__inline uint32_t layout_unit_size(
|
||||
#else
|
||||
FORCEINLINE uint32_t layout_unit_size(
|
||||
#endif
|
||||
IN const pnfs_file_layout *layout)
|
||||
{
|
||||
return layout->util & NFL4_UFLG_STRIPE_UNIT_SIZE_MASK;
|
||||
}
|
||||
#ifndef __REACTOS__
|
||||
__inline uint64_t stripe_unit_number(
|
||||
#else
|
||||
FORCEINLINE uint64_t stripe_unit_number(
|
||||
#endif
|
||||
IN const pnfs_file_layout *layout,
|
||||
IN uint64_t offset,
|
||||
IN uint32_t unit_size)
|
||||
{
|
||||
const uint64_t relative_offset = offset - layout->pattern_offset;
|
||||
return relative_offset / unit_size;
|
||||
}
|
||||
#ifndef __REACTOS__
|
||||
__inline uint64_t stripe_unit_offset(
|
||||
#else
|
||||
FORCEINLINE uint64_t stripe_unit_offset(
|
||||
#endif
|
||||
IN const pnfs_file_layout *layout,
|
||||
IN uint64_t sui,
|
||||
IN uint32_t unit_size)
|
||||
{
|
||||
return layout->pattern_offset + unit_size * sui;
|
||||
}
|
||||
#ifndef __REACTOS__
|
||||
__inline uint32_t stripe_index(
|
||||
#else
|
||||
FORCEINLINE uint32_t stripe_index(
|
||||
#endif
|
||||
IN const pnfs_file_layout *layout,
|
||||
IN uint64_t sui,
|
||||
IN uint32_t stripe_count)
|
||||
{
|
||||
return (uint32_t)((sui + layout->first_index) % stripe_count);
|
||||
}
|
||||
#ifndef __REACTOS__
|
||||
__inline uint32_t data_server_index(
|
||||
#else
|
||||
FORCEINLINE uint32_t data_server_index(
|
||||
#endif
|
||||
IN const pnfs_file_device *device,
|
||||
IN uint32_t stripeid)
|
||||
{
|
||||
return device->stripes.arr[stripeid];
|
||||
}
|
||||
|
||||
#endif /* !__PNFS_H__ */
|
123
reactos/base/services/nfsd/pnfs_debug.c
Normal file
123
reactos/base/services/nfsd/pnfs_debug.c
Normal file
|
@ -0,0 +1,123 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <strsafe.h>
|
||||
#include "pnfs.h"
|
||||
#include "daemon_debug.h"
|
||||
|
||||
|
||||
const char* pnfs_error_string(enum pnfs_status status)
|
||||
{
|
||||
switch (status) {
|
||||
case PNFS_SUCCESS: return "PNFS_SUCCESS";
|
||||
case PNFS_PENDING: return "PNFS_PENDING";
|
||||
case PNFS_READ_EOF: return "PNFS_READ_EOF";
|
||||
case PNFSERR_NOT_SUPPORTED: return "PNFSERR_NOT_SUPPORTED";
|
||||
case PNFSERR_NOT_CONNECTED: return "PNFSERR_NOT_CONNECTED";
|
||||
case PNFSERR_IO: return "PNFSERR_IO";
|
||||
case PNFSERR_NO_DEVICE: return "PNFSERR_NO_DEVICE";
|
||||
case PNFSERR_NO_LAYOUT: return "PNFSERR_NO_LAYOUT";
|
||||
case PNFSERR_INVALID_FH_LIST: return "PNFSERR_INVALID_FH_LIST";
|
||||
case PNFSERR_INVALID_DS_INDEX: return "PNFSERR_INVALID_DS_INDEX";
|
||||
case PNFSERR_RESOURCES: return "PNFSERR_RESOURCES";
|
||||
case PNFSERR_LAYOUT_RECALLED: return "PNFSERR_LAYOUT_RECALLED";
|
||||
case PNFSERR_LAYOUT_CHANGED: return "PNFSERR_LAYOUT_CHANGED";
|
||||
default: return "Invalid pnfs status";
|
||||
}
|
||||
}
|
||||
|
||||
const char* pnfs_layout_type_string(enum pnfs_layout_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case PNFS_LAYOUTTYPE_FILE: return "PNFS_LAYOUTTYPE_FILE";
|
||||
case PNFS_LAYOUTTYPE_OBJECT: return "PNFS_LAYOUTTYPE_OBJECT";
|
||||
case PNFS_LAYOUTTYPE_BLOCK: return "PNFS_LAYOUTTYPE_BLOCK";
|
||||
default: return "Invalid layout type";
|
||||
}
|
||||
}
|
||||
|
||||
const char* pnfs_iomode_string(enum pnfs_iomode iomode)
|
||||
{
|
||||
switch (iomode) {
|
||||
case PNFS_IOMODE_READ: return "PNFS_IOMODE_READ";
|
||||
case PNFS_IOMODE_RW: return "PNFS_IOMODE_RW";
|
||||
case PNFS_IOMODE_ANY: return "PNFS_IOMODE_ANY";
|
||||
default: return "Invalid io mode";
|
||||
}
|
||||
}
|
||||
|
||||
void dprint_deviceid(
|
||||
IN int level,
|
||||
IN const char *title,
|
||||
IN const unsigned char *deviceid)
|
||||
{
|
||||
/* deviceid is 16 bytes, so print it as 4 uints */
|
||||
uint32_t *p = (uint32_t*)deviceid;
|
||||
dprintf(level, "%s%08X.%08X.%08X.%08X\n",
|
||||
title, htonl(p[0]), htonl(p[1]), htonl(p[2]), htonl(p[3]));
|
||||
}
|
||||
|
||||
void dprint_layout(
|
||||
IN int level,
|
||||
IN const pnfs_file_layout *layout)
|
||||
{
|
||||
dprintf(level, " type: %s\n", pnfs_layout_type_string(layout->layout.type));
|
||||
dprintf(level, " iomode: %s\n", pnfs_iomode_string(layout->layout.iomode));
|
||||
dprint_deviceid(level, " deviceid: ", layout->deviceid);
|
||||
dprintf(level, " offset: %llu\n", layout->layout.offset);
|
||||
dprintf(level, " length: %llu\n", layout->layout.length);
|
||||
dprintf(level, " pattern_offset: %llu\n", layout->pattern_offset);
|
||||
dprintf(level, " first_index: %u\n", layout->first_index);
|
||||
dprintf(level, " dense: %u\n", is_dense(layout));
|
||||
dprintf(level, " commit_to_mds: %u\n", should_commit_to_mds(layout));
|
||||
dprintf(level, " stripe_unit_size: %u\n", layout_unit_size(layout));
|
||||
dprintf(level, " file handles: %u\n", layout->filehandles.count);
|
||||
}
|
||||
|
||||
#define MULTI_ADDR_BUFFER_LEN \
|
||||
(NFS41_ADDRS_PER_SERVER*(NFS41_UNIVERSAL_ADDR_LEN+1)+1)
|
||||
|
||||
static void dprint_multi_addr(
|
||||
IN int level,
|
||||
IN uint32_t index,
|
||||
IN const multi_addr4 *addrs)
|
||||
{
|
||||
char buffer[MULTI_ADDR_BUFFER_LEN] = "";
|
||||
uint32_t i;
|
||||
for (i = 0; i < addrs->count; i++) {
|
||||
StringCchCatA(buffer, MULTI_ADDR_BUFFER_LEN, addrs->arr[i].uaddr);
|
||||
StringCchCatA(buffer, MULTI_ADDR_BUFFER_LEN, " ");
|
||||
}
|
||||
dprintf(level, " servers[%d]: [ %s]\n", index, buffer);
|
||||
}
|
||||
|
||||
void dprint_device(
|
||||
IN int level,
|
||||
IN const pnfs_file_device *device)
|
||||
{
|
||||
uint32_t i;
|
||||
dprint_deviceid(level, " deviceid: ", device->device.deviceid);
|
||||
dprintf(level, " type: %s\n", pnfs_layout_type_string(device->device.type));
|
||||
dprintf(level, " stripes: %u\n", device->stripes.count);
|
||||
for (i = 0; i < device->servers.count; i++)
|
||||
dprint_multi_addr(level, i, &device->servers.arr[i].addrs);
|
||||
}
|
365
reactos/base/services/nfsd/pnfs_device.c
Normal file
365
reactos/base/services/nfsd/pnfs_device.c
Normal file
|
@ -0,0 +1,365 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nfs41_ops.h"
|
||||
#include "nfs41_callback.h"
|
||||
#include "daemon_debug.h"
|
||||
|
||||
|
||||
#define FDLVL 2 /* dprintf level for file device logging */
|
||||
|
||||
|
||||
/* pnfs_file_device_list */
|
||||
struct pnfs_file_device_list {
|
||||
struct list_entry head;
|
||||
CRITICAL_SECTION lock;
|
||||
};
|
||||
|
||||
#define device_entry(pos) list_container(pos, pnfs_file_device, entry)
|
||||
|
||||
|
||||
static enum pnfs_status file_device_create(
|
||||
IN const unsigned char *deviceid,
|
||||
IN struct pnfs_file_device_list *devices,
|
||||
OUT pnfs_file_device **device_out)
|
||||
{
|
||||
enum pnfs_status status = PNFS_SUCCESS;
|
||||
pnfs_file_device *device;
|
||||
|
||||
device = calloc(1, sizeof(pnfs_file_device));
|
||||
if (device == NULL) {
|
||||
status = PNFSERR_RESOURCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(device->device.deviceid, deviceid, PNFS_DEVICEID_SIZE);
|
||||
device->devices = devices;
|
||||
InitializeCriticalSection(&device->device.lock);
|
||||
*device_out = device;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static void file_device_free(
|
||||
IN pnfs_file_device *device)
|
||||
{
|
||||
free(device->servers.arr);
|
||||
free(device->stripes.arr);
|
||||
DeleteCriticalSection(&device->device.lock);
|
||||
free(device);
|
||||
}
|
||||
|
||||
static int deviceid_compare(
|
||||
const struct list_entry *entry,
|
||||
const void *deviceid)
|
||||
{
|
||||
const pnfs_file_device *device = device_entry(entry);
|
||||
return memcmp(device->device.deviceid, deviceid, PNFS_DEVICEID_SIZE);
|
||||
}
|
||||
|
||||
static enum pnfs_status file_device_find_or_create(
|
||||
IN const unsigned char *deviceid,
|
||||
IN struct pnfs_file_device_list *devices,
|
||||
OUT pnfs_file_device **device_out)
|
||||
{
|
||||
struct list_entry *entry;
|
||||
enum pnfs_status status;
|
||||
|
||||
dprintf(FDLVL, "--> pnfs_file_device_find_or_create()\n");
|
||||
|
||||
EnterCriticalSection(&devices->lock);
|
||||
|
||||
/* search for an existing device */
|
||||
entry = list_search(&devices->head, deviceid, deviceid_compare);
|
||||
if (entry == NULL) {
|
||||
/* create a new device */
|
||||
pnfs_file_device *device;
|
||||
status = file_device_create(deviceid, devices, &device);
|
||||
if (status == PNFS_SUCCESS) {
|
||||
/* add it to the list */
|
||||
list_add_tail(&devices->head, &device->entry);
|
||||
*device_out = device;
|
||||
|
||||
dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() "
|
||||
"returning new device %p\n", device);
|
||||
} else {
|
||||
dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() "
|
||||
"returning %s\n", pnfs_error_string(status));
|
||||
}
|
||||
} else {
|
||||
*device_out = device_entry(entry);
|
||||
status = PNFS_SUCCESS;
|
||||
|
||||
dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() "
|
||||
"returning existing device %p\n", *device_out);
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&devices->lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
enum pnfs_status pnfs_file_device_list_create(
|
||||
OUT struct pnfs_file_device_list **devices_out)
|
||||
{
|
||||
enum pnfs_status status = PNFS_SUCCESS;
|
||||
struct pnfs_file_device_list *devices;
|
||||
|
||||
devices = calloc(1, sizeof(struct pnfs_file_device_list));
|
||||
if (devices == NULL) {
|
||||
status = PNFSERR_RESOURCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_init(&devices->head);
|
||||
InitializeCriticalSection(&devices->lock);
|
||||
|
||||
*devices_out = devices;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
void pnfs_file_device_list_free(
|
||||
IN struct pnfs_file_device_list *devices)
|
||||
{
|
||||
struct list_entry *entry, *tmp;
|
||||
|
||||
EnterCriticalSection(&devices->lock);
|
||||
|
||||
list_for_each_tmp(entry, tmp, &devices->head)
|
||||
file_device_free(device_entry(entry));
|
||||
|
||||
LeaveCriticalSection(&devices->lock);
|
||||
DeleteCriticalSection(&devices->lock);
|
||||
free(devices);
|
||||
}
|
||||
|
||||
void pnfs_file_device_list_invalidate(
|
||||
IN struct pnfs_file_device_list *devices)
|
||||
{
|
||||
struct list_entry *entry, *tmp;
|
||||
pnfs_file_device *device;
|
||||
|
||||
dprintf(FDLVL, "--> pnfs_file_device_list_invalidate()\n");
|
||||
|
||||
EnterCriticalSection(&devices->lock);
|
||||
|
||||
list_for_each_tmp(entry, tmp, &devices->head) {
|
||||
device = device_entry(entry);
|
||||
EnterCriticalSection(&device->device.lock);
|
||||
/* if there are layouts still using the device, flag it
|
||||
* as revoked and clean up on last reference */
|
||||
if (device->device.layout_count) {
|
||||
device->device.status |= PNFS_DEVICE_REVOKED;
|
||||
LeaveCriticalSection(&device->device.lock);
|
||||
} else {
|
||||
LeaveCriticalSection(&device->device.lock);
|
||||
/* no layouts are using it, so it's safe to free */
|
||||
list_remove(entry);
|
||||
file_device_free(device);
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&devices->lock);
|
||||
|
||||
dprintf(FDLVL, "<-- pnfs_file_device_list_invalidate()\n");
|
||||
}
|
||||
|
||||
|
||||
/* pnfs_file_device */
|
||||
enum pnfs_status pnfs_file_device_get(
|
||||
IN nfs41_session *session,
|
||||
IN struct pnfs_file_device_list *devices,
|
||||
IN unsigned char *deviceid,
|
||||
OUT pnfs_file_device **device_out)
|
||||
{
|
||||
pnfs_file_device *device;
|
||||
enum pnfs_status status;
|
||||
enum nfsstat4 nfsstat;
|
||||
|
||||
dprintf(FDLVL, "--> pnfs_file_device_get()\n");
|
||||
|
||||
status = file_device_find_or_create(deviceid, devices, &device);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
EnterCriticalSection(&device->device.lock);
|
||||
|
||||
/* don't give out a device that's been revoked */
|
||||
if (device->device.status & PNFS_DEVICE_REVOKED)
|
||||
status = PNFSERR_NO_DEVICE;
|
||||
else if (device->device.status & PNFS_DEVICE_GRANTED)
|
||||
status = PNFS_SUCCESS;
|
||||
else {
|
||||
nfsstat = pnfs_rpc_getdeviceinfo(session, deviceid, device);
|
||||
if (nfsstat == NFS4_OK) {
|
||||
device->device.status = PNFS_DEVICE_GRANTED;
|
||||
status = PNFS_SUCCESS;
|
||||
|
||||
dprintf(FDLVL, "Received device info:\n");
|
||||
dprint_device(FDLVL, device);
|
||||
} else {
|
||||
status = PNFSERR_NO_DEVICE;
|
||||
|
||||
eprintf("pnfs_rpc_getdeviceinfo() failed with %s\n",
|
||||
nfs_error_string(nfsstat));
|
||||
}
|
||||
}
|
||||
|
||||
if (status == PNFS_SUCCESS) {
|
||||
device->device.layout_count++;
|
||||
dprintf(FDLVL, "pnfs_file_device_get() -> %u\n",
|
||||
device->device.layout_count);
|
||||
*device_out = device;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&device->device.lock);
|
||||
out:
|
||||
dprintf(FDLVL, "<-- pnfs_file_device_get() returning %s\n",
|
||||
pnfs_error_string(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
void pnfs_file_device_put(
|
||||
IN pnfs_file_device *device)
|
||||
{
|
||||
uint32_t count;
|
||||
EnterCriticalSection(&device->device.lock);
|
||||
count = --device->device.layout_count;
|
||||
dprintf(FDLVL, "pnfs_file_device_put() -> %u\n", count);
|
||||
|
||||
/* if the device was revoked, remove/free the device on last reference */
|
||||
if (count == 0 && device->device.status & PNFS_DEVICE_REVOKED) {
|
||||
EnterCriticalSection(&device->devices->lock);
|
||||
list_remove(&device->entry);
|
||||
LeaveCriticalSection(&device->devices->lock);
|
||||
|
||||
LeaveCriticalSection(&device->device.lock);
|
||||
|
||||
file_device_free(device);
|
||||
dprintf(FDLVL, "revoked file device freed after last reference\n");
|
||||
} else {
|
||||
LeaveCriticalSection(&device->device.lock);
|
||||
}
|
||||
}
|
||||
|
||||
static enum pnfs_status data_client_status(
|
||||
IN pnfs_data_server *server,
|
||||
OUT nfs41_client **client_out)
|
||||
{
|
||||
enum pnfs_status status = PNFSERR_NOT_CONNECTED;
|
||||
|
||||
if (server->client) {
|
||||
dprintf(FDLVL, "pnfs_data_server_client() returning "
|
||||
"existing client %llu\n", server->client->clnt_id);
|
||||
*client_out = server->client;
|
||||
status = PNFS_SUCCESS;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
enum pnfs_status pnfs_data_server_client(
|
||||
IN nfs41_root *root,
|
||||
IN pnfs_data_server *server,
|
||||
IN uint32_t default_lease,
|
||||
OUT nfs41_client **client_out)
|
||||
{
|
||||
int status;
|
||||
enum pnfs_status pnfsstat;
|
||||
|
||||
dprintf(FDLVL, "--> pnfs_data_server_client('%s')\n",
|
||||
server->addrs.arr[0].uaddr);
|
||||
|
||||
/* if we've already created the client, return it */
|
||||
AcquireSRWLockShared(&server->lock);
|
||||
pnfsstat = data_client_status(server, client_out);
|
||||
ReleaseSRWLockShared(&server->lock);
|
||||
|
||||
if (!pnfsstat)
|
||||
goto out;
|
||||
|
||||
AcquireSRWLockExclusive(&server->lock);
|
||||
|
||||
pnfsstat = data_client_status(server, client_out);
|
||||
if (pnfsstat) {
|
||||
status = nfs41_root_mount_addrs(root, &server->addrs, 1, default_lease,
|
||||
&server->client);
|
||||
if (status) {
|
||||
dprintf(FDLVL, "data_client_create('%s') failed with %d\n",
|
||||
server->addrs.arr[0].uaddr, status);
|
||||
} else {
|
||||
*client_out = server->client;
|
||||
pnfsstat = PNFS_SUCCESS;
|
||||
|
||||
dprintf(FDLVL, "pnfs_data_server_client() returning new client "
|
||||
"%llu\n", server->client->clnt_id);
|
||||
}
|
||||
}
|
||||
|
||||
ReleaseSRWLockExclusive(&server->lock);
|
||||
out:
|
||||
return pnfsstat;
|
||||
}
|
||||
|
||||
|
||||
/* CB_NOTIFY_DEVICEID */
|
||||
enum pnfs_status pnfs_file_device_notify(
|
||||
IN struct pnfs_file_device_list *devices,
|
||||
IN const struct notify_deviceid4 *change)
|
||||
{
|
||||
struct list_entry *entry;
|
||||
enum pnfs_status status = PNFSERR_NO_DEVICE;
|
||||
|
||||
dprintf(FDLVL, "--> pnfs_file_device_notify(%u, %0llX:%0llX)\n",
|
||||
change->type, change->deviceid);
|
||||
|
||||
if (change->layouttype != PNFS_LAYOUTTYPE_FILE) {
|
||||
status = PNFSERR_NOT_SUPPORTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
EnterCriticalSection(&devices->lock);
|
||||
|
||||
entry = list_search(&devices->head, change->deviceid, deviceid_compare);
|
||||
if (entry) {
|
||||
dprintf(FDLVL, "found file device %p\n", device_entry(entry));
|
||||
|
||||
if (change->type == NOTIFY_DEVICEID4_CHANGE) {
|
||||
/* if (change->immediate) ... */
|
||||
dprintf(FDLVL, "CHANGE (%u)\n", change->immediate);
|
||||
} else if (change->type == NOTIFY_DEVICEID4_DELETE) {
|
||||
/* This notification MUST NOT be sent if the client
|
||||
* has a layout that refers to the device ID. */
|
||||
dprintf(FDLVL, "DELETE\n");
|
||||
}
|
||||
status = PNFS_SUCCESS;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&devices->lock);
|
||||
out:
|
||||
dprintf(FDLVL, "<-- pnfs_file_device_notify() returning %s\n",
|
||||
pnfs_error_string(status));
|
||||
return status;
|
||||
}
|
871
reactos/base/services/nfsd/pnfs_io.c
Normal file
871
reactos/base/services/nfsd/pnfs_io.c
Normal file
|
@ -0,0 +1,871 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <process.h>
|
||||
|
||||
#include "nfs41_ops.h"
|
||||
#include "util.h"
|
||||
#include "daemon_debug.h"
|
||||
|
||||
|
||||
#define IOLVL 2 /* dprintf level for pnfs io logging */
|
||||
|
||||
#define file_layout_entry(pos) list_container(pos, pnfs_file_layout, layout.entry)
|
||||
|
||||
typedef struct __pnfs_io_pattern {
|
||||
struct __pnfs_io_thread *threads;
|
||||
nfs41_root *root;
|
||||
nfs41_path_fh *meta_file;
|
||||
const stateid_arg *stateid;
|
||||
pnfs_layout_state *state;
|
||||
unsigned char *buffer;
|
||||
uint64_t offset_start;
|
||||
uint64_t offset_end;
|
||||
uint32_t count;
|
||||
uint32_t default_lease;
|
||||
} pnfs_io_pattern;
|
||||
|
||||
typedef struct __pnfs_io_thread {
|
||||
nfs41_write_verf verf;
|
||||
pnfs_io_pattern *pattern;
|
||||
pnfs_file_layout *layout;
|
||||
nfs41_path_fh *file;
|
||||
uint64_t offset;
|
||||
uint32_t id;
|
||||
enum stable_how4 stable;
|
||||
} pnfs_io_thread;
|
||||
|
||||
typedef struct __pnfs_io_unit {
|
||||
unsigned char *buffer;
|
||||
uint64_t offset;
|
||||
uint64_t length;
|
||||
uint32_t stripeid;
|
||||
uint32_t serverid;
|
||||
} pnfs_io_unit;
|
||||
|
||||
typedef uint32_t (WINAPI *pnfs_io_thread_fn)(void*);
|
||||
|
||||
|
||||
static enum pnfs_status stripe_next_unit(
|
||||
IN const pnfs_file_layout *layout,
|
||||
IN uint32_t stripeid,
|
||||
IN uint64_t *position,
|
||||
IN uint64_t offset_end,
|
||||
OUT pnfs_io_unit *io);
|
||||
|
||||
/* 13.4.2. Interpreting the File Layout Using Sparse Packing
|
||||
* http://tools.ietf.org/html/rfc5661#section-13.4.2 */
|
||||
|
||||
static enum pnfs_status get_sparse_fh(
|
||||
IN pnfs_file_layout *layout,
|
||||
IN nfs41_path_fh *meta_file,
|
||||
IN uint32_t stripeid,
|
||||
OUT nfs41_path_fh **file_out)
|
||||
{
|
||||
const uint32_t filehandle_count = layout->filehandles.count;
|
||||
const uint32_t server_count = layout->device->servers.count;
|
||||
enum pnfs_status status = PNFS_SUCCESS;
|
||||
|
||||
if (filehandle_count == server_count) {
|
||||
const uint32_t serverid = data_server_index(layout->device, stripeid);
|
||||
*file_out = &layout->filehandles.arr[serverid];
|
||||
} else if (filehandle_count == 1) {
|
||||
*file_out = &layout->filehandles.arr[0];
|
||||
} else if (filehandle_count == 0) {
|
||||
*file_out = meta_file;
|
||||
} else {
|
||||
eprintf("invalid sparse layout! has %u file handles "
|
||||
"and %u servers\n", filehandle_count, server_count);
|
||||
status = PNFSERR_INVALID_FH_LIST;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/* 13.4.3. Interpreting the File Layout Using Dense Packing
|
||||
* http://tools.ietf.org/html/rfc5661#section-13.4.3 */
|
||||
|
||||
static enum pnfs_status get_dense_fh(
|
||||
IN pnfs_file_layout *layout,
|
||||
IN uint32_t stripeid,
|
||||
OUT nfs41_path_fh **file_out)
|
||||
{
|
||||
const uint32_t filehandle_count = layout->filehandles.count;
|
||||
const uint32_t stripe_count = layout->device->stripes.count;
|
||||
enum pnfs_status status = PNFS_SUCCESS;
|
||||
|
||||
if (filehandle_count == stripe_count) {
|
||||
*file_out = &layout->filehandles.arr[stripeid];
|
||||
} else {
|
||||
eprintf("invalid dense layout! has %u file handles "
|
||||
"and %u stripes\n", filehandle_count, stripe_count);
|
||||
status = PNFSERR_INVALID_FH_LIST;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static __inline bool_t layout_compatible(
|
||||
IN const pnfs_layout *layout,
|
||||
IN enum pnfs_iomode iomode,
|
||||
IN uint64_t position)
|
||||
{
|
||||
return layout->iomode >= iomode
|
||||
&& layout->offset <= position
|
||||
&& position < layout->offset + layout->length;
|
||||
}
|
||||
|
||||
/* count stripes for all layout segments that intersect the range
|
||||
* and have not been covered by previous segments */
|
||||
static uint32_t thread_count(
|
||||
IN pnfs_layout_state *state,
|
||||
IN enum pnfs_iomode iomode,
|
||||
IN uint64_t offset,
|
||||
IN uint64_t length)
|
||||
{
|
||||
uint64_t position = offset;
|
||||
struct list_entry *entry;
|
||||
uint32_t count = 0;
|
||||
|
||||
list_for_each(entry, &state->layouts) {
|
||||
pnfs_file_layout *layout = file_layout_entry(entry);
|
||||
|
||||
if (!layout_compatible(&layout->layout, iomode, position))
|
||||
continue;
|
||||
|
||||
position = layout->layout.offset + layout->layout.length;
|
||||
count += layout->device->stripes.count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static enum pnfs_status thread_init(
|
||||
IN pnfs_io_pattern *pattern,
|
||||
IN pnfs_io_thread *thread,
|
||||
IN pnfs_file_layout *layout,
|
||||
IN uint32_t stripeid,
|
||||
IN uint64_t offset)
|
||||
{
|
||||
thread->pattern = pattern;
|
||||
thread->layout = layout;
|
||||
thread->stable = FILE_SYNC4;
|
||||
thread->offset = offset;
|
||||
thread->id = stripeid;
|
||||
|
||||
return is_dense(layout) ? get_dense_fh(layout, stripeid, &thread->file)
|
||||
: get_sparse_fh(layout, pattern->meta_file, stripeid, &thread->file);
|
||||
}
|
||||
|
||||
static enum pnfs_status pattern_threads_init(
|
||||
IN pnfs_io_pattern *pattern,
|
||||
IN enum pnfs_iomode iomode,
|
||||
IN uint64_t offset,
|
||||
IN uint64_t length)
|
||||
{
|
||||
pnfs_io_unit io;
|
||||
uint64_t position = offset;
|
||||
struct list_entry *entry;
|
||||
uint32_t s, t = 0;
|
||||
enum pnfs_status status = PNFS_SUCCESS;
|
||||
|
||||
list_for_each(entry, &pattern->state->layouts) {
|
||||
pnfs_file_layout *layout = file_layout_entry(entry);
|
||||
|
||||
if (!layout_compatible(&layout->layout, iomode, position))
|
||||
continue;
|
||||
|
||||
for (s = 0; s < layout->device->stripes.count; s++) {
|
||||
uint64_t off = position;
|
||||
|
||||
/* does the range contain this stripe? */
|
||||
status = stripe_next_unit(layout, s, &off, offset + length, &io);
|
||||
if (status != PNFS_PENDING)
|
||||
continue;
|
||||
|
||||
if (t >= pattern->count) { /* miscounted threads needed? */
|
||||
status = PNFSERR_NO_LAYOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = thread_init(pattern, &pattern->threads[t++], layout, s, off);
|
||||
if (status)
|
||||
goto out;
|
||||
}
|
||||
position = layout->layout.offset + layout->layout.length;
|
||||
}
|
||||
|
||||
if (position < offset + length) {
|
||||
/* unable to satisfy the entire range */
|
||||
status = PNFSERR_NO_LAYOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* update the pattern with the actual number of threads used */
|
||||
pattern->count = t;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static enum pnfs_status pattern_init(
|
||||
IN pnfs_io_pattern *pattern,
|
||||
IN nfs41_root *root,
|
||||
IN nfs41_path_fh *meta_file,
|
||||
IN const stateid_arg *stateid,
|
||||
IN pnfs_layout_state *state,
|
||||
IN unsigned char *buffer,
|
||||
IN enum pnfs_iomode iomode,
|
||||
IN uint64_t offset,
|
||||
IN uint64_t length,
|
||||
IN uint32_t default_lease)
|
||||
{
|
||||
enum pnfs_status status;
|
||||
|
||||
/* calculate an upper bound on the number of threads to allocate */
|
||||
pattern->count = thread_count(state, iomode, offset, length);
|
||||
pattern->threads = calloc(pattern->count, sizeof(pnfs_io_thread));
|
||||
if (pattern->threads == NULL) {
|
||||
status = PNFSERR_RESOURCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* information shared between threads */
|
||||
pattern->root = root;
|
||||
pattern->meta_file = meta_file;
|
||||
pattern->stateid = stateid;
|
||||
pattern->state = state;
|
||||
pattern->buffer = buffer;
|
||||
pattern->offset_start = offset;
|
||||
pattern->offset_end = offset + length;
|
||||
pattern->default_lease = default_lease;
|
||||
|
||||
/* initialize a thread for every stripe necessary to cover the range */
|
||||
status = pattern_threads_init(pattern, iomode, offset, length);
|
||||
if (status)
|
||||
goto out_err_free;
|
||||
|
||||
/* take a reference on the layout so we don't return it during io */
|
||||
pnfs_layout_io_start(state);
|
||||
out:
|
||||
return status;
|
||||
|
||||
out_err_free:
|
||||
free(pattern->threads);
|
||||
pattern->threads = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void pattern_free(
|
||||
IN pnfs_io_pattern *pattern)
|
||||
{
|
||||
/* inform the layout that our io is finished */
|
||||
pnfs_layout_io_finished(pattern->state);
|
||||
free(pattern->threads);
|
||||
}
|
||||
|
||||
static __inline uint64_t positive_remainder(
|
||||
IN uint64_t dividend,
|
||||
IN uint32_t divisor)
|
||||
{
|
||||
const uint64_t remainder = dividend % divisor;
|
||||
return remainder < divisor ? remainder : remainder + divisor;
|
||||
}
|
||||
|
||||
/* return the next unit of the given stripeid */
|
||||
static enum pnfs_status stripe_next_unit(
|
||||
IN const pnfs_file_layout *layout,
|
||||
IN uint32_t stripeid,
|
||||
IN uint64_t *position,
|
||||
IN uint64_t offset_end,
|
||||
OUT pnfs_io_unit *io)
|
||||
{
|
||||
const uint32_t unit_size = layout_unit_size(layout);
|
||||
const uint32_t stripe_count = layout->device->stripes.count;
|
||||
uint64_t sui = stripe_unit_number(layout, *position, unit_size);
|
||||
|
||||
/* advance to the desired stripeid */
|
||||
sui += abs(stripeid - stripe_index(layout, sui, stripe_count));
|
||||
|
||||
io->offset = stripe_unit_offset(layout, sui, unit_size);
|
||||
if (io->offset < *position) /* don't start before position */
|
||||
io->offset = *position;
|
||||
else
|
||||
*position = io->offset;
|
||||
|
||||
io->length = stripe_unit_offset(layout, sui + 1, unit_size);
|
||||
if (io->length > offset_end) /* don't end past offset_end */
|
||||
io->length = offset_end;
|
||||
|
||||
if (io->offset >= io->length) /* nothing to do, return success */
|
||||
return PNFS_SUCCESS;
|
||||
|
||||
io->length -= io->offset;
|
||||
|
||||
if (is_dense(layout)) {
|
||||
const uint64_t rel_offset = io->offset - layout->pattern_offset;
|
||||
const uint64_t remainder = positive_remainder(rel_offset, unit_size);
|
||||
const uint32_t stride = unit_size * stripe_count;
|
||||
|
||||
io->offset = (rel_offset / stride) * unit_size + remainder;
|
||||
}
|
||||
return PNFS_PENDING;
|
||||
}
|
||||
|
||||
static enum pnfs_status thread_next_unit(
|
||||
IN pnfs_io_thread *thread,
|
||||
OUT pnfs_io_unit *io)
|
||||
{
|
||||
pnfs_io_pattern *pattern = thread->pattern;
|
||||
pnfs_layout_state *state = pattern->state;
|
||||
enum pnfs_status status;
|
||||
|
||||
AcquireSRWLockShared(&state->lock);
|
||||
|
||||
/* stop io if the layout is recalled */
|
||||
status = pnfs_layout_recall_status(state, &thread->layout->layout);
|
||||
if (status)
|
||||
goto out_unlock;
|
||||
|
||||
status = stripe_next_unit(thread->layout, thread->id,
|
||||
&thread->offset, pattern->offset_end, io);
|
||||
if (status == PNFS_PENDING)
|
||||
io->buffer = pattern->buffer + thread->offset - pattern->offset_start;
|
||||
|
||||
out_unlock:
|
||||
ReleaseSRWLockShared(&state->lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static enum pnfs_status thread_data_server(
|
||||
IN pnfs_io_thread *thread,
|
||||
OUT pnfs_data_server **server_out)
|
||||
{
|
||||
pnfs_file_device *device = thread->layout->device;
|
||||
const uint32_t serverid = data_server_index(device, thread->id);
|
||||
|
||||
if (serverid >= device->servers.count)
|
||||
return PNFSERR_INVALID_DS_INDEX;
|
||||
|
||||
*server_out = &device->servers.arr[serverid];
|
||||
return PNFS_SUCCESS;
|
||||
}
|
||||
|
||||
static enum pnfs_status pattern_join(
|
||||
IN HANDLE *threads,
|
||||
IN DWORD count)
|
||||
{
|
||||
DWORD status;
|
||||
/* WaitForMultipleObjects() supports a maximum of 64 objects */
|
||||
while (count) {
|
||||
const DWORD n = min(count, MAXIMUM_WAIT_OBJECTS);
|
||||
status = WaitForMultipleObjects(n, threads, TRUE, INFINITE);
|
||||
if (status != WAIT_OBJECT_0)
|
||||
return PNFSERR_RESOURCES;
|
||||
|
||||
count -= n;
|
||||
threads += n;
|
||||
}
|
||||
return PNFS_SUCCESS;
|
||||
}
|
||||
|
||||
static enum pnfs_status pattern_fork(
|
||||
IN pnfs_io_pattern *pattern,
|
||||
IN pnfs_io_thread_fn thread_fn)
|
||||
{
|
||||
HANDLE *threads;
|
||||
uint32_t i;
|
||||
enum pnfs_status status = PNFS_SUCCESS;
|
||||
|
||||
if (pattern->count == 0)
|
||||
goto out;
|
||||
|
||||
if (pattern->count == 1) {
|
||||
/* no need to fork if there's only 1 thread */
|
||||
status = (enum pnfs_status)thread_fn(pattern->threads);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* create a thread for each unit that has actual io */
|
||||
threads = calloc(pattern->count, sizeof(HANDLE));
|
||||
if (threads == NULL) {
|
||||
status = PNFSERR_RESOURCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < pattern->count; i++) {
|
||||
threads[i] = (HANDLE)_beginthreadex(NULL, 0,
|
||||
thread_fn, &pattern->threads[i], 0, NULL);
|
||||
if (threads[i] == NULL) {
|
||||
eprintf("_beginthreadex() failed with %d\n", GetLastError());
|
||||
pattern->count = i; /* join any threads already started */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* wait on all threads to finish */
|
||||
status = pattern_join(threads, pattern->count);
|
||||
if (status) {
|
||||
eprintf("pattern_join() failed with %s\n", pnfs_error_string(status));
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < pattern->count; i++) {
|
||||
/* keep track of the most severe error returned by a thread */
|
||||
DWORD exitcode;
|
||||
if (GetExitCodeThread(threads[i], &exitcode))
|
||||
status = max(status, (enum pnfs_status)exitcode);
|
||||
|
||||
CloseHandle(threads[i]);
|
||||
}
|
||||
|
||||
free(threads);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static uint64_t pattern_bytes_transferred(
|
||||
IN pnfs_io_pattern *pattern,
|
||||
OUT OPTIONAL enum stable_how4 *stable)
|
||||
{
|
||||
uint64_t lowest_offset = pattern->offset_end;
|
||||
uint32_t i;
|
||||
|
||||
if (stable) *stable = FILE_SYNC4;
|
||||
|
||||
for (i = 0; i < pattern->count; i++) {
|
||||
lowest_offset = min(lowest_offset, pattern->threads[i].offset);
|
||||
if (stable) *stable = min(*stable, pattern->threads[i].stable);
|
||||
}
|
||||
return lowest_offset - pattern->offset_start;
|
||||
}
|
||||
|
||||
|
||||
static enum pnfs_status map_ds_error(
|
||||
IN enum nfsstat4 nfsstat,
|
||||
IN pnfs_layout_state *state,
|
||||
IN const pnfs_file_layout *layout)
|
||||
{
|
||||
switch (nfsstat) {
|
||||
case NO_ERROR:
|
||||
return PNFS_SUCCESS;
|
||||
|
||||
/* 13.11 Layout Revocation and Fencing
|
||||
* http://tools.ietf.org/html/rfc5661#section-13.11
|
||||
* if we've been fenced, we'll either get ERR_STALE when we PUTFH
|
||||
* something in layout.filehandles, or ERR_PNFS_NO_LAYOUT when
|
||||
* attempting to READ or WRITE */
|
||||
case NFS4ERR_STALE:
|
||||
case NFS4ERR_PNFS_NO_LAYOUT:
|
||||
dprintf(IOLVL, "data server fencing detected!\n");
|
||||
|
||||
pnfs_layout_recall_fenced(state, &layout->layout);
|
||||
|
||||
/* return CHANGED to prevent any further use of the layout */
|
||||
return PNFSERR_LAYOUT_CHANGED;
|
||||
|
||||
default:
|
||||
return PNFSERR_IO;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t WINAPI file_layout_read_thread(void *args)
|
||||
{
|
||||
pnfs_io_unit io;
|
||||
stateid_arg stateid;
|
||||
pnfs_io_thread *thread = (pnfs_io_thread*)args;
|
||||
pnfs_io_pattern *pattern = thread->pattern;
|
||||
pnfs_data_server *server;
|
||||
nfs41_client *client;
|
||||
uint32_t maxreadsize, bytes_read, total_read;
|
||||
enum pnfs_status status;
|
||||
enum nfsstat4 nfsstat;
|
||||
bool_t eof;
|
||||
|
||||
dprintf(IOLVL, "--> file_layout_read_thread(%u)\n", thread->id);
|
||||
|
||||
/* get the data server for this thread */
|
||||
status = thread_data_server(thread, &server);
|
||||
if (status) {
|
||||
eprintf("thread_data_server() failed with %s\n",
|
||||
pnfs_error_string(status));
|
||||
goto out;
|
||||
}
|
||||
/* find or establish a client for this data server */
|
||||
status = pnfs_data_server_client(pattern->root,
|
||||
server, pattern->default_lease, &client);
|
||||
if (status) {
|
||||
eprintf("pnfs_data_server_client() failed with %s\n",
|
||||
pnfs_error_string(status));
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(&stateid, pattern->stateid, sizeof(stateid));
|
||||
stateid.stateid.seqid = 0;
|
||||
|
||||
total_read = 0;
|
||||
while (thread_next_unit(thread, &io) == PNFS_PENDING) {
|
||||
maxreadsize = max_read_size(client->session, &thread->file->fh);
|
||||
if (io.length > maxreadsize)
|
||||
io.length = maxreadsize;
|
||||
|
||||
nfsstat = nfs41_read(client->session, thread->file, &stateid,
|
||||
io.offset, (uint32_t)io.length, io.buffer, &bytes_read, &eof);
|
||||
if (nfsstat) {
|
||||
eprintf("nfs41_read() failed with %s\n",
|
||||
nfs_error_string(nfsstat));
|
||||
status = map_ds_error(nfsstat, pattern->state, thread->layout);
|
||||
break;
|
||||
}
|
||||
|
||||
total_read += bytes_read;
|
||||
thread->offset += bytes_read;
|
||||
|
||||
if (eof) {
|
||||
dprintf(IOLVL, "read thread %u reached eof: offset %llu\n",
|
||||
thread->id, thread->offset);
|
||||
status = total_read ? PNFS_SUCCESS : PNFS_READ_EOF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
out:
|
||||
dprintf(IOLVL, "<-- file_layout_read_thread(%u) returning %s\n",
|
||||
thread->id, pnfs_error_string(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
static uint32_t WINAPI file_layout_write_thread(void *args)
|
||||
{
|
||||
pnfs_io_unit io;
|
||||
stateid_arg stateid;
|
||||
pnfs_io_thread *thread = (pnfs_io_thread*)args;
|
||||
pnfs_io_pattern *pattern = thread->pattern;
|
||||
pnfs_data_server *server;
|
||||
nfs41_client *client;
|
||||
const uint64_t offset_start = thread->offset;
|
||||
uint64_t commit_min, commit_max;
|
||||
uint32_t maxwritesize, bytes_written, total_written;
|
||||
enum pnfs_status status;
|
||||
enum nfsstat4 nfsstat;
|
||||
|
||||
dprintf(IOLVL, "--> file_layout_write_thread(%u)\n", thread->id);
|
||||
|
||||
/* get the data server for this thread */
|
||||
status = thread_data_server(thread, &server);
|
||||
if (status) {
|
||||
eprintf("thread_data_server() failed with %s\n",
|
||||
pnfs_error_string(status));
|
||||
goto out;
|
||||
}
|
||||
/* find or establish a client for this data server */
|
||||
status = pnfs_data_server_client(pattern->root,
|
||||
server, pattern->default_lease, &client);
|
||||
if (status) {
|
||||
eprintf("pnfs_data_server_client() failed with %s\n",
|
||||
pnfs_error_string(status));
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(&stateid, pattern->stateid, sizeof(stateid));
|
||||
stateid.stateid.seqid = 0;
|
||||
|
||||
maxwritesize = max_write_size(client->session, &thread->file->fh);
|
||||
|
||||
retry_write:
|
||||
thread->offset = offset_start;
|
||||
thread->stable = FILE_SYNC4;
|
||||
commit_min = NFS4_UINT64_MAX;
|
||||
commit_max = 0;
|
||||
total_written = 0;
|
||||
|
||||
while (thread_next_unit(thread, &io) == PNFS_PENDING) {
|
||||
if (io.length > maxwritesize)
|
||||
io.length = maxwritesize;
|
||||
|
||||
nfsstat = nfs41_write(client->session, thread->file, &stateid,
|
||||
io.buffer, (uint32_t)io.length, io.offset, UNSTABLE4,
|
||||
&bytes_written, &thread->verf, NULL);
|
||||
if (nfsstat) {
|
||||
eprintf("nfs41_write() failed with %s\n",
|
||||
nfs_error_string(nfsstat));
|
||||
status = map_ds_error(nfsstat, pattern->state, thread->layout);
|
||||
break;
|
||||
}
|
||||
if (!verify_write(&thread->verf, &thread->stable))
|
||||
goto retry_write;
|
||||
|
||||
total_written += bytes_written;
|
||||
thread->offset += bytes_written;
|
||||
|
||||
/* track the range for commit */
|
||||
if (commit_min > io.offset)
|
||||
commit_min = io.offset;
|
||||
if (commit_max < io.offset + io.length)
|
||||
commit_max = io.offset + io.length;
|
||||
}
|
||||
|
||||
/* nothing to commit */
|
||||
if (commit_max <= commit_min)
|
||||
goto out;
|
||||
/* layout changed; redo all io against metadata server */
|
||||
if (status == PNFSERR_LAYOUT_CHANGED)
|
||||
goto out;
|
||||
/* the data is already in stable storage */
|
||||
if (thread->stable != UNSTABLE4)
|
||||
goto out;
|
||||
/* the metadata server expects us to commit there instead */
|
||||
if (should_commit_to_mds(thread->layout))
|
||||
goto out;
|
||||
|
||||
dprintf(1, "sending COMMIT to data server for offset=%lld len=%lld\n",
|
||||
commit_min, commit_max - commit_min);
|
||||
nfsstat = nfs41_commit(client->session, thread->file,
|
||||
commit_min, (uint32_t)(commit_max - commit_min), 0, &thread->verf, NULL);
|
||||
|
||||
if (nfsstat)
|
||||
status = map_ds_error(nfsstat, pattern->state, thread->layout);
|
||||
else if (!verify_commit(&thread->verf)) {
|
||||
/* resend the writes unless the layout was recalled */
|
||||
if (status != PNFSERR_LAYOUT_RECALLED)
|
||||
goto retry_write;
|
||||
status = PNFSERR_IO;
|
||||
} else {
|
||||
/* on successful commit, leave pnfs_status unchanged; if the
|
||||
* layout was recalled, we still want to return the error */
|
||||
thread->stable = DATA_SYNC4;
|
||||
}
|
||||
out:
|
||||
dprintf(IOLVL, "<-- file_layout_write_thread(%u) returning %s\n",
|
||||
thread->id, pnfs_error_string(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
enum pnfs_status pnfs_read(
|
||||
IN nfs41_root *root,
|
||||
IN nfs41_open_state *state,
|
||||
IN stateid_arg *stateid,
|
||||
IN pnfs_layout_state *layout,
|
||||
IN uint64_t offset,
|
||||
IN uint64_t length,
|
||||
OUT unsigned char *buffer_out,
|
||||
OUT ULONG *len_out)
|
||||
{
|
||||
pnfs_io_pattern pattern;
|
||||
enum pnfs_status status;
|
||||
|
||||
dprintf(IOLVL, "--> pnfs_read(%llu, %llu)\n", offset, length);
|
||||
|
||||
*len_out = 0;
|
||||
|
||||
AcquireSRWLockExclusive(&layout->lock);
|
||||
|
||||
/* get layouts/devices for the entire range; PNFS_PENDING means we
|
||||
* dropped the lock to send an rpc, so repeat until it succeeds */
|
||||
do {
|
||||
status = pnfs_layout_state_prepare(layout, state->session,
|
||||
&state->file, stateid, PNFS_IOMODE_READ, offset, length);
|
||||
} while (status == PNFS_PENDING);
|
||||
|
||||
if (status == PNFS_SUCCESS) {
|
||||
/* interpret the layout and set up threads for io */
|
||||
status = pattern_init(&pattern, root, &state->file, stateid,
|
||||
layout, buffer_out, PNFS_IOMODE_READ, offset, length,
|
||||
state->session->lease_time);
|
||||
if (status)
|
||||
eprintf("pattern_init() failed with %s\n",
|
||||
pnfs_error_string(status));
|
||||
}
|
||||
|
||||
ReleaseSRWLockExclusive(&layout->lock);
|
||||
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
status = pattern_fork(&pattern, file_layout_read_thread);
|
||||
if (status != PNFS_SUCCESS && status != PNFS_READ_EOF)
|
||||
goto out_free_pattern;
|
||||
|
||||
*len_out = (ULONG)pattern_bytes_transferred(&pattern, NULL);
|
||||
|
||||
out_free_pattern:
|
||||
pattern_free(&pattern);
|
||||
out:
|
||||
dprintf(IOLVL, "<-- pnfs_read() returning %s\n",
|
||||
pnfs_error_string(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
static enum pnfs_status mds_commit(
|
||||
IN nfs41_open_state *state,
|
||||
IN uint64_t offset,
|
||||
IN uint32_t length,
|
||||
IN const pnfs_io_pattern *pattern,
|
||||
OUT nfs41_file_info *info)
|
||||
{
|
||||
nfs41_write_verf verf;
|
||||
enum nfsstat4 nfsstat;
|
||||
enum pnfs_status status = PNFS_SUCCESS;
|
||||
uint32_t i;
|
||||
|
||||
nfsstat = nfs41_commit(state->session,
|
||||
&state->file, offset, length, 1, &verf, info);
|
||||
if (nfsstat) {
|
||||
eprintf("nfs41_commit() to mds failed with %s\n",
|
||||
nfs_error_string(nfsstat));
|
||||
status = PNFSERR_IO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* 13.7. COMMIT through Metadata Server:
|
||||
* If nfl_util & NFL4_UFLG_COMMIT_THRU_MDS is TRUE, then in order to
|
||||
* maintain the current NFSv4.1 commit and recovery model, the data
|
||||
* servers MUST return a common writeverf verifier in all WRITE
|
||||
* responses for a given file layout, and the metadata server's
|
||||
* COMMIT implementation must return the same writeverf. */
|
||||
for (i = 0; i < pattern->count; i++) {
|
||||
const pnfs_io_thread *thread = &pattern->threads[i];
|
||||
if (thread->stable != UNSTABLE4) /* already committed */
|
||||
continue;
|
||||
|
||||
if (!should_commit_to_mds(thread->layout)) {
|
||||
/* commit to mds is not allowed on this layout */
|
||||
eprintf("mds commit: failed to commit to data server\n");
|
||||
status = PNFSERR_IO;
|
||||
break;
|
||||
}
|
||||
if (memcmp(verf.verf, thread->verf.verf, NFS4_VERIFIER_SIZE) != 0) {
|
||||
eprintf("mds commit verifier doesn't match ds write verifiers\n");
|
||||
status = PNFSERR_IO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static enum pnfs_status layout_commit(
|
||||
IN nfs41_open_state *state,
|
||||
IN pnfs_layout_state *layout,
|
||||
IN uint64_t offset,
|
||||
IN uint64_t length,
|
||||
OUT nfs41_file_info *info)
|
||||
{
|
||||
stateid4 layout_stateid;
|
||||
uint64_t last_offset = offset + length - 1;
|
||||
uint64_t *new_last_offset = NULL;
|
||||
enum nfsstat4 nfsstat;
|
||||
enum pnfs_status status = PNFS_SUCCESS;
|
||||
|
||||
AcquireSRWLockExclusive(&state->lock);
|
||||
/* if this is past the current eof, update the open state's
|
||||
* last offset, and pass a pointer to LAYOUTCOMMIT */
|
||||
if (state->pnfs_last_offset < last_offset ||
|
||||
(state->pnfs_last_offset == 0 && last_offset == 0)) {
|
||||
state->pnfs_last_offset = last_offset;
|
||||
new_last_offset = &last_offset;
|
||||
}
|
||||
ReleaseSRWLockExclusive(&state->lock);
|
||||
|
||||
AcquireSRWLockShared(&layout->lock);
|
||||
memcpy(&layout_stateid, &layout->stateid, sizeof(layout_stateid));
|
||||
ReleaseSRWLockShared(&layout->lock);
|
||||
|
||||
dprintf(1, "LAYOUTCOMMIT for offset=%lld len=%lld new_last_offset=%u\n",
|
||||
offset, length, new_last_offset ? 1 : 0);
|
||||
nfsstat = pnfs_rpc_layoutcommit(state->session, &state->file,
|
||||
&layout_stateid, offset, length, new_last_offset, NULL, info);
|
||||
if (nfsstat) {
|
||||
dprintf(IOLVL, "pnfs_rpc_layoutcommit() failed with %s\n",
|
||||
nfs_error_string(nfsstat));
|
||||
status = PNFSERR_IO;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
enum pnfs_status pnfs_write(
|
||||
IN nfs41_root *root,
|
||||
IN nfs41_open_state *state,
|
||||
IN stateid_arg *stateid,
|
||||
IN pnfs_layout_state *layout,
|
||||
IN uint64_t offset,
|
||||
IN uint64_t length,
|
||||
IN unsigned char *buffer,
|
||||
OUT ULONG *len_out,
|
||||
OUT nfs41_file_info *info)
|
||||
{
|
||||
pnfs_io_pattern pattern;
|
||||
enum stable_how4 stable;
|
||||
enum pnfs_status status;
|
||||
|
||||
dprintf(IOLVL, "--> pnfs_write(%llu, %llu)\n", offset, length);
|
||||
|
||||
*len_out = 0;
|
||||
|
||||
AcquireSRWLockExclusive(&layout->lock);
|
||||
|
||||
/* get layouts/devices for the entire range; PNFS_PENDING means we
|
||||
* dropped the lock to send an rpc, so repeat until it succeeds */
|
||||
do {
|
||||
status = pnfs_layout_state_prepare(layout, state->session,
|
||||
&state->file, stateid, PNFS_IOMODE_RW, offset, length);
|
||||
} while (status == PNFS_PENDING);
|
||||
|
||||
if (status == PNFS_SUCCESS) {
|
||||
/* interpret the layout and set up threads for io */
|
||||
status = pattern_init(&pattern, root, &state->file, stateid,
|
||||
layout, buffer, PNFS_IOMODE_RW, offset, length,
|
||||
state->session->lease_time);
|
||||
if (status)
|
||||
eprintf("pattern_init() failed with %s\n",
|
||||
pnfs_error_string(status));
|
||||
}
|
||||
|
||||
ReleaseSRWLockExclusive(&layout->lock);
|
||||
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
status = pattern_fork(&pattern, file_layout_write_thread);
|
||||
/* on layout recall, we still attempt to commit what we wrote */
|
||||
if (status != PNFS_SUCCESS && status != PNFSERR_LAYOUT_RECALLED)
|
||||
goto out_free_pattern;
|
||||
|
||||
*len_out = (ULONG)pattern_bytes_transferred(&pattern, &stable);
|
||||
if (*len_out == 0)
|
||||
goto out_free_pattern;
|
||||
|
||||
if (stable == UNSTABLE4) {
|
||||
/* send COMMIT to the mds and verify against all ds writes */
|
||||
status = mds_commit(state, offset, *len_out, &pattern, info);
|
||||
} else if (stable == DATA_SYNC4) {
|
||||
/* send LAYOUTCOMMIT to sync the metadata */
|
||||
status = layout_commit(state, layout, offset, *len_out, info);
|
||||
} else {
|
||||
/* send a GETATTR to update the cached size */
|
||||
bitmap4 attr_request;
|
||||
nfs41_superblock_getattr_mask(state->file.fh.superblock, &attr_request);
|
||||
nfs41_getattr(state->session, &state->file, &attr_request, info);
|
||||
}
|
||||
out_free_pattern:
|
||||
pattern_free(&pattern);
|
||||
out:
|
||||
dprintf(IOLVL, "<-- pnfs_write() returning %s\n",
|
||||
pnfs_error_string(status));
|
||||
return status;
|
||||
}
|
1289
reactos/base/services/nfsd/pnfs_layout.c
Normal file
1289
reactos/base/services/nfsd/pnfs_layout.c
Normal file
File diff suppressed because it is too large
Load diff
649
reactos/base/services/nfsd/readdir.c
Normal file
649
reactos/base/services/nfsd/readdir.c
Normal file
|
@ -0,0 +1,649 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
#include <stdlib.h>
|
||||
#include "from_kernel.h"
|
||||
#include "nfs41_ops.h"
|
||||
#include "daemon_debug.h"
|
||||
#include "upcall.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
typedef union _FILE_DIR_INFO_UNION {
|
||||
ULONG NextEntryOffset;
|
||||
FILE_NAMES_INFORMATION fni;
|
||||
FILE_DIRECTORY_INFO fdi;
|
||||
FILE_FULL_DIR_INFO ffdi;
|
||||
FILE_ID_FULL_DIR_INFO fifdi;
|
||||
FILE_BOTH_DIR_INFORMATION fbdi;
|
||||
FILE_ID_BOTH_DIR_INFO fibdi;
|
||||
} FILE_DIR_INFO_UNION, *PFILE_DIR_INFO_UNION;
|
||||
|
||||
|
||||
/* NFS41_DIR_QUERY */
|
||||
static int parse_readdir(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
readdir_upcall_args *args = &upcall->args.readdir;
|
||||
|
||||
status = safe_read(&buffer, &length, &args->query_class, sizeof(args->query_class));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->buf_len, sizeof(args->buf_len));
|
||||
if (status) goto out;
|
||||
status = get_name(&buffer, &length, &args->filter);
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->initial, sizeof(args->initial));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->restart, sizeof(args->restart));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->single, sizeof(args->single));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->kbuf, sizeof(args->kbuf));
|
||||
if (status) goto out;
|
||||
args->root = upcall->root_ref;
|
||||
args->state = upcall->state_ref;
|
||||
|
||||
dprintf(1, "parsing NFS41_DIR_QUERY: info_class=%d buf_len=%d "
|
||||
"filter='%s'\n\tInitial\\Restart\\Single %d\\%d\\%d buf=%p\n",
|
||||
args->query_class, args->buf_len, args->filter,
|
||||
args->initial, args->restart, args->single, args->kbuf);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
#define FILTER_STAR '*'
|
||||
#define FILTER_QM '>'
|
||||
|
||||
static __inline const char* skip_stars(
|
||||
const char *filter)
|
||||
{
|
||||
while (*filter == FILTER_STAR)
|
||||
filter++;
|
||||
return filter;
|
||||
}
|
||||
|
||||
static int readdir_filter(
|
||||
const char *filter,
|
||||
const char *name)
|
||||
{
|
||||
const char *f = filter, *n = name;
|
||||
|
||||
while (*f && *n) {
|
||||
if (*f == FILTER_STAR) {
|
||||
f = skip_stars(f);
|
||||
if (*f == '\0')
|
||||
return 1;
|
||||
while (*n && !readdir_filter(f, n))
|
||||
n++;
|
||||
} else if (*f == FILTER_QM || *f == *n) {
|
||||
f++;
|
||||
n++;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
return *f == *n || *skip_stars(f) == '\0';
|
||||
}
|
||||
|
||||
static uint32_t readdir_size_for_entry(
|
||||
IN int query_class,
|
||||
IN uint32_t wname_size)
|
||||
{
|
||||
uint32_t needed = wname_size;
|
||||
switch (query_class)
|
||||
{
|
||||
case FileDirectoryInformation:
|
||||
needed += FIELD_OFFSET(FILE_DIRECTORY_INFO, FileName);
|
||||
break;
|
||||
case FileIdFullDirectoryInformation:
|
||||
needed += FIELD_OFFSET(FILE_ID_FULL_DIR_INFO, FileName);
|
||||
break;
|
||||
case FileFullDirectoryInformation:
|
||||
needed += FIELD_OFFSET(FILE_FULL_DIR_INFO, FileName);
|
||||
break;
|
||||
case FileIdBothDirectoryInformation:
|
||||
needed += FIELD_OFFSET(FILE_ID_BOTH_DIR_INFO, FileName);
|
||||
break;
|
||||
case FileBothDirectoryInformation:
|
||||
needed += FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName);
|
||||
break;
|
||||
case FileNamesInformation:
|
||||
needed += FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName);
|
||||
break;
|
||||
default:
|
||||
eprintf("unhandled dir query class %d\n", query_class);
|
||||
return 0;
|
||||
}
|
||||
return needed;
|
||||
}
|
||||
|
||||
static void readdir_copy_dir_info(
|
||||
IN nfs41_readdir_entry *entry,
|
||||
IN PFILE_DIR_INFO_UNION info)
|
||||
{
|
||||
info->fdi.FileIndex = (ULONG)entry->attr_info.fileid;
|
||||
nfs_time_to_file_time(&entry->attr_info.time_create,
|
||||
&info->fdi.CreationTime);
|
||||
nfs_time_to_file_time(&entry->attr_info.time_access,
|
||||
&info->fdi.LastAccessTime);
|
||||
nfs_time_to_file_time(&entry->attr_info.time_modify,
|
||||
&info->fdi.LastWriteTime);
|
||||
/* XXX: was using 'change' attr, but that wasn't giving a time */
|
||||
nfs_time_to_file_time(&entry->attr_info.time_modify,
|
||||
&info->fdi.ChangeTime);
|
||||
info->fdi.EndOfFile.QuadPart =
|
||||
info->fdi.AllocationSize.QuadPart =
|
||||
entry->attr_info.size;
|
||||
info->fdi.FileAttributes = nfs_file_info_to_attributes(
|
||||
&entry->attr_info);
|
||||
}
|
||||
|
||||
static void readdir_copy_shortname(
|
||||
IN LPCWSTR name,
|
||||
OUT LPWSTR name_out,
|
||||
OUT CCHAR *name_size_out)
|
||||
{
|
||||
/* GetShortPathName returns number of characters, not including \0 */
|
||||
*name_size_out = (CCHAR)GetShortPathNameW(name, name_out, 12);
|
||||
if (*name_size_out) {
|
||||
*name_size_out++;
|
||||
*name_size_out *= sizeof(WCHAR);
|
||||
}
|
||||
}
|
||||
|
||||
static void readdir_copy_full_dir_info(
|
||||
IN nfs41_readdir_entry *entry,
|
||||
IN PFILE_DIR_INFO_UNION info)
|
||||
{
|
||||
readdir_copy_dir_info(entry, info);
|
||||
/* for files with the FILE_ATTRIBUTE_REPARSE_POINT attribute,
|
||||
* EaSize is used instead to specify its reparse tag. this makes
|
||||
* the 'dir' command to show files as <SYMLINK>, and triggers a
|
||||
* FSCTL_GET_REPARSE_POINT to query the symlink target
|
||||
*/
|
||||
info->fifdi.EaSize = entry->attr_info.type == NF4LNK ?
|
||||
IO_REPARSE_TAG_SYMLINK : 0;
|
||||
}
|
||||
|
||||
static void readdir_copy_both_dir_info(
|
||||
IN nfs41_readdir_entry *entry,
|
||||
IN LPWSTR wname,
|
||||
IN PFILE_DIR_INFO_UNION info)
|
||||
{
|
||||
readdir_copy_full_dir_info(entry, info);
|
||||
readdir_copy_shortname(wname, info->fbdi.ShortName,
|
||||
&info->fbdi.ShortNameLength);
|
||||
}
|
||||
|
||||
static void readdir_copy_filename(
|
||||
IN LPCWSTR name,
|
||||
IN uint32_t name_size,
|
||||
OUT LPWSTR name_out,
|
||||
OUT ULONG *name_size_out)
|
||||
{
|
||||
*name_size_out = name_size;
|
||||
memcpy(name_out, name, name_size);
|
||||
}
|
||||
|
||||
static int format_abs_path(
|
||||
IN const nfs41_abs_path *path,
|
||||
IN const nfs41_component *name,
|
||||
OUT nfs41_abs_path *path_out)
|
||||
{
|
||||
/* format an absolute path 'parent\name' */
|
||||
int status = NO_ERROR;
|
||||
|
||||
InitializeSRWLock(&path_out->lock);
|
||||
abs_path_copy(path_out, path);
|
||||
if (FAILED(StringCchPrintfA(path_out->path + path_out->len,
|
||||
NFS41_MAX_PATH_LEN - path_out->len, "\\%s", name->name))) {
|
||||
status = ERROR_FILENAME_EXCED_RANGE;
|
||||
goto out;
|
||||
}
|
||||
path_out->len += name->len + 1;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int lookup_entry(
|
||||
IN nfs41_root *root,
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *parent,
|
||||
OUT nfs41_readdir_entry *entry)
|
||||
{
|
||||
nfs41_abs_path path;
|
||||
nfs41_component name;
|
||||
int status;
|
||||
|
||||
name.name = entry->name;
|
||||
name.len = (unsigned short)entry->name_len - 1;
|
||||
|
||||
status = format_abs_path(parent->path, &name, &path);
|
||||
if (status) goto out;
|
||||
|
||||
status = nfs41_lookup(root, session, &path,
|
||||
NULL, NULL, &entry->attr_info, NULL);
|
||||
if (status) goto out;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int lookup_symlink(
|
||||
IN nfs41_root *root,
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *parent,
|
||||
IN const nfs41_component *name,
|
||||
OUT nfs41_file_info *info_out)
|
||||
{
|
||||
nfs41_abs_path path;
|
||||
nfs41_path_fh file;
|
||||
nfs41_file_info info;
|
||||
int status;
|
||||
|
||||
status = format_abs_path(parent->path, name, &path);
|
||||
if (status) goto out;
|
||||
|
||||
file.path = &path;
|
||||
status = nfs41_lookup(root, session, &path, NULL, &file, &info, &session);
|
||||
if (status) goto out;
|
||||
|
||||
last_component(path.path, path.path + path.len, &file.name);
|
||||
|
||||
status = nfs41_symlink_follow(root, session, &file, &info);
|
||||
if (status) goto out;
|
||||
|
||||
info_out->symlink_dir = info.type == NF4DIR;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int readdir_copy_entry(
|
||||
IN readdir_upcall_args *args,
|
||||
IN nfs41_readdir_entry *entry,
|
||||
IN OUT unsigned char **dst_pos,
|
||||
IN OUT uint32_t *dst_len)
|
||||
{
|
||||
int status = 0;
|
||||
WCHAR wname[NFS4_OPAQUE_LIMIT];
|
||||
uint32_t wname_len, wname_size, needed;
|
||||
PFILE_DIR_INFO_UNION info;
|
||||
|
||||
wname_len = MultiByteToWideChar(CP_UTF8, 0,
|
||||
entry->name, entry->name_len, wname, NFS4_OPAQUE_LIMIT);
|
||||
wname_size = (wname_len - 1) * sizeof(WCHAR);
|
||||
|
||||
needed = readdir_size_for_entry(args->query_class, wname_size);
|
||||
if (!needed || needed > *dst_len) {
|
||||
status = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
info = (PFILE_DIR_INFO_UNION)*dst_pos;
|
||||
info->NextEntryOffset = align8(needed);
|
||||
*dst_pos += info->NextEntryOffset;
|
||||
*dst_len -= info->NextEntryOffset;
|
||||
|
||||
if (entry->attr_info.rdattr_error == NFS4ERR_MOVED) {
|
||||
entry->attr_info.type = NF4DIR; /* default to dir */
|
||||
/* look up attributes for referral entries, but ignore return value;
|
||||
* it's okay if lookup fails, we'll just write garbage attributes */
|
||||
lookup_entry(args->root, args->state->session,
|
||||
&args->state->file, entry);
|
||||
} else if (entry->attr_info.type == NF4LNK) {
|
||||
nfs41_component name;
|
||||
name.name = entry->name;
|
||||
name.len = (unsigned short)entry->name_len - 1;
|
||||
/* look up the symlink target to see whether it's a directory */
|
||||
lookup_symlink(args->root, args->state->session,
|
||||
&args->state->file, &name, &entry->attr_info);
|
||||
}
|
||||
|
||||
switch (args->query_class)
|
||||
{
|
||||
case FileNamesInformation:
|
||||
info->fni.FileIndex = 0;
|
||||
readdir_copy_filename(wname, wname_size,
|
||||
info->fni.FileName, &info->fni.FileNameLength);
|
||||
break;
|
||||
case FileDirectoryInformation:
|
||||
readdir_copy_dir_info(entry, info);
|
||||
readdir_copy_filename(wname, wname_size,
|
||||
info->fdi.FileName, &info->fdi.FileNameLength);
|
||||
break;
|
||||
case FileFullDirectoryInformation:
|
||||
readdir_copy_full_dir_info(entry, info);
|
||||
readdir_copy_filename(wname, wname_size,
|
||||
info->ffdi.FileName, &info->ffdi.FileNameLength);
|
||||
break;
|
||||
case FileIdFullDirectoryInformation:
|
||||
readdir_copy_full_dir_info(entry, info);
|
||||
info->fibdi.FileId.QuadPart = (LONGLONG)entry->attr_info.fileid;
|
||||
readdir_copy_filename(wname, wname_size,
|
||||
info->fifdi.FileName, &info->fifdi.FileNameLength);
|
||||
break;
|
||||
case FileBothDirectoryInformation:
|
||||
readdir_copy_both_dir_info(entry, wname, info);
|
||||
readdir_copy_filename(wname, wname_size,
|
||||
info->fbdi.FileName, &info->fbdi.FileNameLength);
|
||||
break;
|
||||
case FileIdBothDirectoryInformation:
|
||||
readdir_copy_both_dir_info(entry, wname, info);
|
||||
info->fibdi.FileId.QuadPart = (LONGLONG)entry->attr_info.fileid;
|
||||
readdir_copy_filename(wname, wname_size,
|
||||
info->fibdi.FileName, &info->fibdi.FileNameLength);
|
||||
break;
|
||||
default:
|
||||
eprintf("unhandled dir query class %d\n", args->query_class);
|
||||
status = -1;
|
||||
break;
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
#define COOKIE_DOT ((uint64_t)-2)
|
||||
#define COOKIE_DOTDOT ((uint64_t)-1)
|
||||
|
||||
static int readdir_add_dots(
|
||||
IN readdir_upcall_args *args,
|
||||
IN OUT unsigned char *entry_buf,
|
||||
IN uint32_t entry_buf_len,
|
||||
OUT uint32_t *len_out,
|
||||
OUT uint32_t **last_offset)
|
||||
{
|
||||
int status = 0;
|
||||
const uint32_t entry_len = (uint32_t)FIELD_OFFSET(nfs41_readdir_entry, name);
|
||||
nfs41_readdir_entry *entry;
|
||||
nfs41_open_state *state = args->state;
|
||||
|
||||
*len_out = 0;
|
||||
*last_offset = NULL;
|
||||
switch (state->cookie.cookie) {
|
||||
case 0:
|
||||
if (entry_buf_len < entry_len + 2) {
|
||||
status = ERROR_BUFFER_OVERFLOW;
|
||||
dprintf(1, "not enough room for '.' entry. received %d need %d\n",
|
||||
entry_buf_len, entry_len + 2);
|
||||
args->query_reply_len = entry_len + 2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
entry = (nfs41_readdir_entry*)entry_buf;
|
||||
ZeroMemory(&entry->attr_info, sizeof(nfs41_file_info));
|
||||
|
||||
status = nfs41_cached_getattr(state->session,
|
||||
&state->file, &entry->attr_info);
|
||||
if (status) {
|
||||
dprintf(1, "failed to add '.' entry.\n");
|
||||
goto out;
|
||||
}
|
||||
entry->cookie = COOKIE_DOT;
|
||||
entry->name_len = 2;
|
||||
StringCbCopyA(entry->name, entry->name_len, ".");
|
||||
entry->next_entry_offset = entry_len + entry->name_len;
|
||||
|
||||
entry_buf += entry->next_entry_offset;
|
||||
entry_buf_len -= entry->next_entry_offset;
|
||||
*len_out += entry->next_entry_offset;
|
||||
*last_offset = &entry->next_entry_offset;
|
||||
if (args->single)
|
||||
break;
|
||||
/* else no break! */
|
||||
case COOKIE_DOT:
|
||||
if (entry_buf_len < entry_len + 3) {
|
||||
status = ERROR_BUFFER_OVERFLOW;
|
||||
dprintf(1, "not enough room for '..' entry. received %d need %d\n",
|
||||
entry_buf_len, entry_len);
|
||||
args->query_reply_len = entry_len + 2;
|
||||
goto out;
|
||||
}
|
||||
/* XXX: this skips '..' when listing root fh */
|
||||
if (state->file.name.len == 0)
|
||||
break;
|
||||
|
||||
entry = (nfs41_readdir_entry*)entry_buf;
|
||||
ZeroMemory(&entry->attr_info, sizeof(nfs41_file_info));
|
||||
|
||||
status = nfs41_cached_getattr(state->session,
|
||||
&state->parent, &entry->attr_info);
|
||||
if (status) {
|
||||
status = ERROR_FILE_NOT_FOUND;
|
||||
dprintf(1, "failed to add '..' entry.\n");
|
||||
goto out;
|
||||
}
|
||||
entry->cookie = COOKIE_DOTDOT;
|
||||
entry->name_len = 3;
|
||||
StringCbCopyA(entry->name, entry->name_len, "..");
|
||||
entry->next_entry_offset = entry_len + entry->name_len;
|
||||
|
||||
entry_buf += entry->next_entry_offset;
|
||||
entry_buf_len -= entry->next_entry_offset;
|
||||
*len_out += entry->next_entry_offset;
|
||||
*last_offset = &entry->next_entry_offset;
|
||||
break;
|
||||
}
|
||||
if (state->cookie.cookie == COOKIE_DOTDOT ||
|
||||
state->cookie.cookie == COOKIE_DOT)
|
||||
ZeroMemory(&state->cookie, sizeof(nfs41_readdir_cookie));
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int handle_readdir(nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
readdir_upcall_args *args = &upcall->args.readdir;
|
||||
nfs41_open_state *state = upcall->state_ref;
|
||||
unsigned char *entry_buf = NULL;
|
||||
uint32_t entry_buf_len;
|
||||
bitmap4 attr_request;
|
||||
bool_t eof;
|
||||
/* make sure we allocate enough space for one nfs41_readdir_entry */
|
||||
const uint32_t max_buf_len = max(args->buf_len,
|
||||
sizeof(nfs41_readdir_entry) + NFS41_MAX_COMPONENT_LEN);
|
||||
|
||||
dprintf(1, "-> handle_nfs41_dirquery(%s,%d,%d,%d)\n",
|
||||
args->filter, args->initial, args->restart, args->single);
|
||||
|
||||
args->query_reply_len = 0;
|
||||
|
||||
if (args->initial || args->restart) {
|
||||
ZeroMemory(&state->cookie, sizeof(nfs41_readdir_cookie));
|
||||
if (!state->cookie.cookie)
|
||||
dprintf(1, "initializing the 1st readdir cookie\n");
|
||||
else if (args->restart)
|
||||
dprintf(1, "restarting; clearing previous cookie %llu\n",
|
||||
state->cookie.cookie);
|
||||
else if (args->initial)
|
||||
dprintf(1, "*** initial; clearing previous cookie %llu!\n",
|
||||
state->cookie.cookie);
|
||||
} else if (!state->cookie.cookie) {
|
||||
dprintf(1, "handle_nfs41_readdir: EOF\n");
|
||||
status = ERROR_NO_MORE_FILES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
entry_buf = calloc(max_buf_len, sizeof(unsigned char));
|
||||
if (entry_buf == NULL) {
|
||||
status = GetLastError();
|
||||
goto out_free_cookie;
|
||||
}
|
||||
fetch_entries:
|
||||
entry_buf_len = max_buf_len;
|
||||
|
||||
nfs41_superblock_getattr_mask(state->file.fh.superblock, &attr_request);
|
||||
attr_request.arr[0] |= FATTR4_WORD0_RDATTR_ERROR;
|
||||
|
||||
if (strchr(args->filter, FILTER_STAR) || strchr(args->filter, FILTER_QM)) {
|
||||
/* use READDIR for wildcards */
|
||||
|
||||
uint32_t dots_len = 0;
|
||||
uint32_t *dots_next_offset = NULL;
|
||||
|
||||
if (args->filter[0] == '*' && args->filter[1] == '\0') {
|
||||
status = readdir_add_dots(args, entry_buf,
|
||||
entry_buf_len, &dots_len, &dots_next_offset);
|
||||
if (status)
|
||||
goto out_free_cookie;
|
||||
entry_buf_len -= dots_len;
|
||||
}
|
||||
|
||||
if (dots_len && args->single) {
|
||||
dprintf(2, "skipping nfs41_readdir because the single query "
|
||||
"will use . or ..\n");
|
||||
entry_buf_len = 0;
|
||||
eof = 0;
|
||||
} else {
|
||||
dprintf(2, "calling nfs41_readdir with cookie %llu\n",
|
||||
state->cookie.cookie);
|
||||
status = nfs41_readdir(state->session, &state->file,
|
||||
&attr_request, &state->cookie, entry_buf + dots_len,
|
||||
&entry_buf_len, &eof);
|
||||
if (status) {
|
||||
dprintf(1, "nfs41_readdir failed with %s\n",
|
||||
nfs_error_string(status));
|
||||
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
|
||||
goto out_free_cookie;
|
||||
}
|
||||
}
|
||||
|
||||
if (!entry_buf_len && dots_next_offset)
|
||||
*dots_next_offset = 0;
|
||||
entry_buf_len += dots_len;
|
||||
} else {
|
||||
/* use LOOKUP for single files */
|
||||
nfs41_readdir_entry *entry = (nfs41_readdir_entry*)entry_buf;
|
||||
entry->cookie = 0;
|
||||
entry->name_len = (uint32_t)strlen(args->filter) + 1;
|
||||
StringCbCopyA(entry->name, entry->name_len, args->filter);
|
||||
entry->next_entry_offset = 0;
|
||||
|
||||
status = lookup_entry(upcall->root_ref,
|
||||
state->session, &state->file, entry);
|
||||
if (status) {
|
||||
dprintf(1, "single_lookup failed with %d\n", status);
|
||||
goto out_free_cookie;
|
||||
}
|
||||
entry_buf_len = entry->name_len +
|
||||
FIELD_OFFSET(nfs41_readdir_entry, name);
|
||||
|
||||
eof = 1;
|
||||
}
|
||||
|
||||
status = args->initial ? ERROR_FILE_NOT_FOUND : ERROR_NO_MORE_FILES;
|
||||
|
||||
if (entry_buf_len) {
|
||||
unsigned char *entry_pos = entry_buf;
|
||||
unsigned char *dst_pos = args->kbuf;
|
||||
uint32_t dst_len = args->buf_len;
|
||||
nfs41_readdir_entry *entry;
|
||||
PULONG offset, last_offset = NULL;
|
||||
|
||||
for (;;) {
|
||||
entry = (nfs41_readdir_entry*)entry_pos;
|
||||
offset = (PULONG)dst_pos; /* ULONG NextEntryOffset */
|
||||
|
||||
dprintf(2, "filter %s looking at %s with cookie %d\n",
|
||||
args->filter, entry->name, entry->cookie);
|
||||
if (readdir_filter((const char*)args->filter, entry->name)) {
|
||||
if (readdir_copy_entry(args, entry, &dst_pos, &dst_len)) {
|
||||
eof = 0;
|
||||
dprintf(2, "not enough space to copy entry %s (cookie %d)\n",
|
||||
entry->name, entry->cookie);
|
||||
break;
|
||||
}
|
||||
last_offset = offset;
|
||||
status = NO_ERROR;
|
||||
}
|
||||
state->cookie.cookie = entry->cookie;
|
||||
|
||||
/* last entry we got from the server */
|
||||
if (!entry->next_entry_offset)
|
||||
break;
|
||||
|
||||
/* we found our single entry, but the server has more */
|
||||
if (args->single && last_offset) {
|
||||
eof = 0;
|
||||
break;
|
||||
}
|
||||
entry_pos += entry->next_entry_offset;
|
||||
}
|
||||
args->query_reply_len = args->buf_len - dst_len;
|
||||
if (last_offset) {
|
||||
*last_offset = 0;
|
||||
} else if (!eof) {
|
||||
dprintf(1, "no entries matched; fetch more\n");
|
||||
goto fetch_entries;
|
||||
}
|
||||
}
|
||||
|
||||
if (eof) {
|
||||
dprintf(1, "we don't need to save a cookie\n");
|
||||
goto out_free_cookie;
|
||||
} else
|
||||
dprintf(1, "saving cookie %llu\n", state->cookie.cookie);
|
||||
|
||||
out_free_entry:
|
||||
free(entry_buf);
|
||||
out:
|
||||
dprintf(1, "<- handle_nfs41_dirquery(%s,%d,%d,%d) returning ",
|
||||
args->filter, args->initial, args->restart, args->single);
|
||||
if (status) {
|
||||
switch (status) {
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
dprintf(1, "ERROR_FILE_NOT_FOUND.\n");
|
||||
break;
|
||||
case ERROR_NO_MORE_FILES:
|
||||
dprintf(1, "ERROR_NO_MORE_FILES.\n");
|
||||
break;
|
||||
case ERROR_BUFFER_OVERFLOW:
|
||||
upcall->last_error = status;
|
||||
status = ERROR_SUCCESS;
|
||||
break;
|
||||
default:
|
||||
dprintf(1, "error code %d.\n", status);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
dprintf(1, "success!\n");
|
||||
}
|
||||
return status;
|
||||
out_free_cookie:
|
||||
state->cookie.cookie = 0;
|
||||
goto out_free_entry;
|
||||
}
|
||||
|
||||
static int marshall_readdir(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
readdir_upcall_args *args = &upcall->args.readdir;
|
||||
|
||||
status = safe_write(&buffer, length, &args->query_reply_len, sizeof(args->query_reply_len));
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
const nfs41_upcall_op nfs41_op_readdir = {
|
||||
parse_readdir,
|
||||
handle_readdir,
|
||||
marshall_readdir
|
||||
};
|
325
reactos/base/services/nfsd/readwrite.c
Normal file
325
reactos/base/services/nfsd/readwrite.c
Normal file
|
@ -0,0 +1,325 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nfs41_ops.h"
|
||||
#include "name_cache.h"
|
||||
#include "upcall.h"
|
||||
#include "daemon_debug.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
/* number of times to retry on write/commit verifier mismatch */
|
||||
#define MAX_WRITE_RETRIES 6
|
||||
|
||||
|
||||
const stateid4 special_read_stateid = {0xffffffff,
|
||||
{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
|
||||
|
||||
static int parse_rw(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
readwrite_upcall_args *args = &upcall->args.rw;
|
||||
|
||||
status = safe_read(&buffer, &length, &args->len, sizeof(args->len));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->offset, sizeof(args->offset));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->buffer, sizeof(args->buffer));
|
||||
if (status) goto out;
|
||||
|
||||
dprintf(1, "parsing %s len=%lu offset=%llu buf=%p\n",
|
||||
opcode2string(upcall->opcode), args->len, args->offset, args->buffer);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
/* NFS41_READ */
|
||||
static int read_from_mds(
|
||||
IN nfs41_upcall *upcall,
|
||||
IN stateid_arg *stateid)
|
||||
{
|
||||
nfs41_session *session = upcall->state_ref->session;
|
||||
nfs41_path_fh *file = &upcall->state_ref->file;
|
||||
readwrite_upcall_args *args = &upcall->args.rw;
|
||||
int status = 0;
|
||||
bool_t eof;
|
||||
unsigned char *p = args->buffer;
|
||||
ULONG to_rcv = args->len, reloffset = 0, len = 0;
|
||||
const uint32_t maxreadsize = max_read_size(session, &file->fh);
|
||||
|
||||
if (to_rcv > maxreadsize)
|
||||
dprintf(1, "handle_nfs41_read: reading %d in chunks of %d\n",
|
||||
to_rcv, maxreadsize);
|
||||
|
||||
while(to_rcv > 0) {
|
||||
uint32_t bytes_read = 0, chunk = min(to_rcv, maxreadsize);
|
||||
|
||||
status = nfs41_read(session, file, stateid, args->offset + reloffset, chunk,
|
||||
p, &bytes_read, &eof);
|
||||
if (status == NFS4ERR_OPENMODE && !len) {
|
||||
stateid->type = STATEID_SPECIAL;
|
||||
memcpy(&stateid->stateid, &special_read_stateid, sizeof(stateid4));
|
||||
continue;
|
||||
} else if (status && !len) {
|
||||
status = nfs_to_windows_error(status, ERROR_NET_WRITE_FAULT);
|
||||
goto out;
|
||||
}
|
||||
|
||||
p += bytes_read;
|
||||
to_rcv -= bytes_read;
|
||||
len += bytes_read;
|
||||
args->offset += bytes_read;
|
||||
if (status) {
|
||||
status = NO_ERROR;
|
||||
break;
|
||||
}
|
||||
if (eof) {
|
||||
if (!len)
|
||||
status = ERROR_HANDLE_EOF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
out:
|
||||
args->out_len = len;
|
||||
return status;
|
||||
}
|
||||
|
||||
static int read_from_pnfs(
|
||||
IN nfs41_upcall *upcall,
|
||||
IN stateid_arg *stateid)
|
||||
{
|
||||
readwrite_upcall_args *args = &upcall->args.rw;
|
||||
pnfs_layout_state *layout;
|
||||
enum pnfs_status pnfsstat;
|
||||
int status = NO_ERROR;
|
||||
|
||||
if (pnfs_layout_state_open(upcall->state_ref, &layout)) {
|
||||
status = ERROR_NOT_SUPPORTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pnfsstat = pnfs_read(upcall->root_ref, upcall->state_ref, stateid, layout,
|
||||
args->offset, args->len, args->buffer, &args->out_len);
|
||||
switch (pnfsstat) {
|
||||
case PNFS_SUCCESS:
|
||||
break;
|
||||
case PNFS_READ_EOF:
|
||||
status = ERROR_HANDLE_EOF;
|
||||
break;
|
||||
default:
|
||||
status = ERROR_READ_FAULT;
|
||||
break;
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int handle_read(nfs41_upcall *upcall)
|
||||
{
|
||||
readwrite_upcall_args *args = &upcall->args.rw;
|
||||
stateid_arg stateid;
|
||||
ULONG pnfs_bytes_read = 0;
|
||||
int status = NO_ERROR;
|
||||
|
||||
nfs41_open_stateid_arg(upcall->state_ref, &stateid);
|
||||
|
||||
#ifdef PNFS_ENABLE_READ
|
||||
status = read_from_pnfs(upcall, &stateid);
|
||||
|
||||
if (status == NO_ERROR || status == ERROR_HANDLE_EOF)
|
||||
goto out;
|
||||
|
||||
if (args->out_len) {
|
||||
pnfs_bytes_read = args->out_len;
|
||||
args->out_len = 0;
|
||||
|
||||
args->offset += pnfs_bytes_read;
|
||||
args->buffer += pnfs_bytes_read;
|
||||
args->len -= pnfs_bytes_read;
|
||||
}
|
||||
#endif
|
||||
|
||||
status = read_from_mds(upcall, &stateid);
|
||||
|
||||
args->out_len += pnfs_bytes_read;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/* NFS41_WRITE */
|
||||
static int write_to_mds(
|
||||
IN nfs41_upcall *upcall,
|
||||
IN stateid_arg *stateid)
|
||||
{
|
||||
nfs41_session *session = upcall->state_ref->session;
|
||||
nfs41_path_fh *file = &upcall->state_ref->file;
|
||||
readwrite_upcall_args *args = &upcall->args.rw;
|
||||
nfs41_write_verf verf;
|
||||
enum stable_how4 stable, committed;
|
||||
unsigned char *p;
|
||||
const uint32_t maxwritesize = max_write_size(session, &file->fh);
|
||||
uint32_t to_send, reloffset, len;
|
||||
int status = 0;
|
||||
/* on write verifier mismatch, retry N times before failing */
|
||||
uint32_t retries = MAX_WRITE_RETRIES;
|
||||
nfs41_file_info info = { 0 };
|
||||
|
||||
retry_write:
|
||||
p = args->buffer;
|
||||
to_send = args->len;
|
||||
reloffset = 0;
|
||||
len = 0;
|
||||
stable = to_send <= maxwritesize ? FILE_SYNC4 : UNSTABLE4;
|
||||
committed = FILE_SYNC4;
|
||||
|
||||
if (to_send > maxwritesize)
|
||||
dprintf(1, "handle_nfs41_write: writing %d in chunks of %d\n",
|
||||
to_send, maxwritesize);
|
||||
|
||||
while(to_send > 0) {
|
||||
uint32_t bytes_written = 0, chunk = min(to_send, maxwritesize);
|
||||
|
||||
status = nfs41_write(session, file, stateid, p, chunk,
|
||||
args->offset + reloffset, stable, &bytes_written, &verf, &info);
|
||||
if (status && !len)
|
||||
goto out;
|
||||
p += bytes_written;
|
||||
to_send -= bytes_written;
|
||||
len += bytes_written;
|
||||
reloffset += bytes_written;
|
||||
if (status) {
|
||||
status = 0;
|
||||
break;
|
||||
}
|
||||
if (!verify_write(&verf, &committed)) {
|
||||
if (retries--) goto retry_write;
|
||||
goto out_verify_failed;
|
||||
}
|
||||
}
|
||||
if (committed != FILE_SYNC4) {
|
||||
dprintf(1, "sending COMMIT for offset=%d and len=%d\n", args->offset, len);
|
||||
status = nfs41_commit(session, file, args->offset, len, 1, &verf, &info);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
if (!verify_commit(&verf)) {
|
||||
if (retries--) goto retry_write;
|
||||
goto out_verify_failed;
|
||||
}
|
||||
} else if (stable == UNSTABLE4) {
|
||||
nfs41_file_info info;
|
||||
bitmap4 attr_request;
|
||||
nfs41_superblock_getattr_mask(file->fh.superblock, &attr_request);
|
||||
status = nfs41_getattr(session, file, &attr_request, &info);
|
||||
if (status)
|
||||
goto out;
|
||||
}
|
||||
args->ctime = info.change;
|
||||
out:
|
||||
args->out_len = len;
|
||||
return nfs_to_windows_error(status, ERROR_NET_WRITE_FAULT);
|
||||
|
||||
out_verify_failed:
|
||||
len = 0;
|
||||
status = NFS4ERR_IO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int write_to_pnfs(
|
||||
IN nfs41_upcall *upcall,
|
||||
IN stateid_arg *stateid)
|
||||
{
|
||||
readwrite_upcall_args *args = &upcall->args.rw;
|
||||
pnfs_layout_state *layout;
|
||||
int status = NO_ERROR;
|
||||
nfs41_file_info info = { 0 };
|
||||
|
||||
if (pnfs_layout_state_open(upcall->state_ref, &layout)) {
|
||||
status = ERROR_NOT_SUPPORTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pnfs_write(upcall->root_ref, upcall->state_ref, stateid, layout,
|
||||
args->offset, args->len, args->buffer, &args->out_len, &info)) {
|
||||
status = ERROR_WRITE_FAULT;
|
||||
goto out;
|
||||
}
|
||||
args->ctime = info.change;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int handle_write(nfs41_upcall *upcall)
|
||||
{
|
||||
readwrite_upcall_args *args = &upcall->args.rw;
|
||||
stateid_arg stateid;
|
||||
uint32_t pnfs_bytes_written = 0;
|
||||
int status;
|
||||
|
||||
nfs41_open_stateid_arg(upcall->state_ref, &stateid);
|
||||
|
||||
#ifdef PNFS_ENABLE_WRITE
|
||||
status = write_to_pnfs(upcall, &stateid);
|
||||
if (args->out_len) {
|
||||
pnfs_bytes_written = args->out_len;
|
||||
args->out_len = 0;
|
||||
|
||||
args->offset += pnfs_bytes_written;
|
||||
args->buffer += pnfs_bytes_written;
|
||||
args->len -= pnfs_bytes_written;
|
||||
|
||||
if (args->len == 0)
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
status = write_to_mds(upcall, &stateid);
|
||||
out:
|
||||
args->out_len += pnfs_bytes_written;
|
||||
return status;
|
||||
}
|
||||
|
||||
static int marshall_rw(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
|
||||
{
|
||||
readwrite_upcall_args *args = &upcall->args.rw;
|
||||
int status;
|
||||
status = safe_write(&buffer, length, &args->out_len, sizeof(args->out_len));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &args->ctime, sizeof(args->ctime));
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
const nfs41_upcall_op nfs41_op_read = {
|
||||
parse_rw,
|
||||
handle_read,
|
||||
marshall_rw
|
||||
};
|
||||
const nfs41_upcall_op nfs41_op_write = {
|
||||
parse_rw,
|
||||
handle_write,
|
||||
marshall_rw
|
||||
};
|
855
reactos/base/services/nfsd/recovery.c
Normal file
855
reactos/base/services/nfsd/recovery.c
Normal file
|
@ -0,0 +1,855 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "recovery.h"
|
||||
#include "delegation.h"
|
||||
#include "nfs41_callback.h"
|
||||
#include "nfs41_compound.h"
|
||||
#include "nfs41_ops.h"
|
||||
#include "daemon_debug.h"
|
||||
|
||||
|
||||
/* session/client recovery uses a lock and condition variable in nfs41_client
|
||||
* to prevent multiple threads from attempting to recover at the same time */
|
||||
bool_t nfs41_recovery_start_or_wait(
|
||||
IN nfs41_client *client)
|
||||
{
|
||||
bool_t status = TRUE;
|
||||
|
||||
EnterCriticalSection(&client->recovery.lock);
|
||||
|
||||
if (!client->recovery.in_recovery) {
|
||||
dprintf(1, "Entering recovery mode for client %llu\n", client->clnt_id);
|
||||
client->recovery.in_recovery = TRUE;
|
||||
} else {
|
||||
status = FALSE;
|
||||
dprintf(1, "Waiting for recovery of client %llu\n", client->clnt_id);
|
||||
while (client->recovery.in_recovery)
|
||||
SleepConditionVariableCS(&client->recovery.cond,
|
||||
&client->recovery.lock, INFINITE);
|
||||
dprintf(1, "Woke up after recovery of client %llu\n", client->clnt_id);
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&client->recovery.lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
void nfs41_recovery_finish(
|
||||
IN nfs41_client *client)
|
||||
{
|
||||
EnterCriticalSection(&client->recovery.lock);
|
||||
dprintf(1, "Finished recovery for client %llu\n", client->clnt_id);
|
||||
client->recovery.in_recovery = FALSE;
|
||||
WakeAllConditionVariable(&client->recovery.cond);
|
||||
LeaveCriticalSection(&client->recovery.lock);
|
||||
}
|
||||
|
||||
|
||||
/* session/client/state recovery */
|
||||
int nfs41_recover_session(
|
||||
IN nfs41_session *session,
|
||||
IN bool_t client_state_lost)
|
||||
{
|
||||
enum nfsstat4 status = NFS4_OK;
|
||||
|
||||
restart_recovery:
|
||||
/* recover the session */
|
||||
status = nfs41_session_renew(session);
|
||||
|
||||
if (status == NFS4ERR_STALE_CLIENTID) {
|
||||
/* recover the client */
|
||||
client_state_lost = TRUE;
|
||||
status = nfs41_client_renew(session->client);
|
||||
if (status == NFS4_OK)
|
||||
goto restart_recovery; /* resume session recovery */
|
||||
|
||||
eprintf("nfs41_client_renew() failed with %d\n", status);
|
||||
} else if (status) {
|
||||
eprintf("nfs41_session_renew() failed with %d\n", status);
|
||||
} else if (client_state_lost) {
|
||||
/* recover the client's state */
|
||||
status = nfs41_recover_client_state(session, session->client);
|
||||
if (status == NFS4ERR_BADSESSION)
|
||||
goto restart_recovery;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void nfs41_recover_sequence_flags(
|
||||
IN nfs41_session *session,
|
||||
IN uint32_t flags)
|
||||
{
|
||||
const uint32_t revoked = flags &
|
||||
(SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED
|
||||
| SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED
|
||||
| SEQ4_STATUS_ADMIN_STATE_REVOKED
|
||||
| SEQ4_STATUS_RECALLABLE_STATE_REVOKED);
|
||||
const uint32_t restarted = flags &
|
||||
SEQ4_STATUS_RESTART_RECLAIM_NEEDED;
|
||||
|
||||
/* no state recovery needed */
|
||||
if (revoked == 0 && restarted == 0)
|
||||
return;
|
||||
|
||||
if (!nfs41_recovery_start_or_wait(session->client))
|
||||
return;
|
||||
|
||||
if (revoked) {
|
||||
/* free stateids and attempt to recover them */
|
||||
nfs41_client_state_revoked(session, session->client, revoked);
|
||||
|
||||
/* if RESTART_RECLAIM_NEEDED is also set, just do RECLAIM_COMPLETE */
|
||||
if (restarted) nfs41_reclaim_complete(session);
|
||||
|
||||
} else if (restarted) {
|
||||
/* do server reboot state recovery */
|
||||
uint32_t status = nfs41_recover_client_state(session, session->client);
|
||||
if (status == NFS4ERR_BADSESSION) {
|
||||
/* recover the session and finish state recovery */
|
||||
nfs41_recover_session(session, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
nfs41_recovery_finish(session->client);
|
||||
}
|
||||
|
||||
|
||||
/* client state recovery for server reboot or lease expiration */
|
||||
static int recover_open_grace(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *parent,
|
||||
IN nfs41_path_fh *file,
|
||||
IN state_owner4 *owner,
|
||||
IN uint32_t access,
|
||||
IN uint32_t deny,
|
||||
OUT stateid4 *stateid,
|
||||
OUT open_delegation4 *delegation)
|
||||
{
|
||||
/* reclaim the open stateid with CLAIM_PREVIOUS */
|
||||
open_claim4 claim;
|
||||
claim.claim = CLAIM_PREVIOUS;
|
||||
claim.u.prev.delegate_type = delegation->type;
|
||||
|
||||
return nfs41_open(session, parent, file, owner, &claim, access, deny,
|
||||
OPEN4_NOCREATE, 0, NULL, FALSE, stateid, delegation, NULL);
|
||||
}
|
||||
|
||||
static int recover_open_no_grace(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *parent,
|
||||
IN nfs41_path_fh *file,
|
||||
IN state_owner4 *owner,
|
||||
IN uint32_t access,
|
||||
IN uint32_t deny,
|
||||
OUT stateid4 *stateid,
|
||||
OUT open_delegation4 *delegation)
|
||||
{
|
||||
open_claim4 claim;
|
||||
int status;
|
||||
|
||||
if (delegation->type != OPEN_DELEGATE_NONE) {
|
||||
/* attempt out-of-grace recovery with CLAIM_DELEGATE_PREV */
|
||||
claim.claim = CLAIM_DELEGATE_PREV;
|
||||
claim.u.deleg_prev.filename = &file->name;
|
||||
|
||||
status = nfs41_open(session, parent, file, owner,
|
||||
&claim, access, deny, OPEN4_NOCREATE, 0, NULL, FALSE,
|
||||
stateid, delegation, NULL);
|
||||
if (status == NFS4_OK || status == NFS4ERR_BADSESSION)
|
||||
goto out;
|
||||
|
||||
/* server support for CLAIM_DELEGATE_PREV is optional;
|
||||
* fall back to CLAIM_NULL on errors */
|
||||
}
|
||||
|
||||
/* attempt out-of-grace recovery with CLAIM_NULL */
|
||||
claim.claim = CLAIM_NULL;
|
||||
claim.u.null.filename = &file->name;
|
||||
|
||||
/* ask nicely for the delegation we had */
|
||||
if (delegation->type == OPEN_DELEGATE_READ)
|
||||
access |= OPEN4_SHARE_ACCESS_WANT_READ_DELEG;
|
||||
else if (delegation->type == OPEN_DELEGATE_WRITE)
|
||||
access |= OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG;
|
||||
|
||||
status = nfs41_open(session, parent, file, owner,
|
||||
&claim, access, deny, OPEN4_NOCREATE, 0, NULL, FALSE,
|
||||
stateid, delegation, NULL);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int recover_open(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_open_state *open,
|
||||
IN OUT bool_t *grace)
|
||||
{
|
||||
open_delegation4 delegation = { 0 };
|
||||
stateid4 stateid = { 0 };
|
||||
int status = NFS4ERR_BADHANDLE;
|
||||
|
||||
/* check for an associated delegation */
|
||||
AcquireSRWLockExclusive(&open->lock);
|
||||
if (open->delegation.state) {
|
||||
nfs41_delegation_state *deleg = open->delegation.state;
|
||||
if (deleg->revoked) {
|
||||
/* reclaim the delegation along with the open */
|
||||
AcquireSRWLockShared(&deleg->lock);
|
||||
delegation.type = deleg->state.type;
|
||||
ReleaseSRWLockShared(&deleg->lock);
|
||||
} else if (deleg->state.recalled) {
|
||||
/* we'll need an open stateid regardless */
|
||||
} else if (list_empty(&open->locks.list)) {
|
||||
/* if there are locks, we need an open stateid to
|
||||
* reclaim them; otherwise, the open can be delegated */
|
||||
open->do_close = FALSE;
|
||||
status = NFS4_OK;
|
||||
}
|
||||
}
|
||||
ReleaseSRWLockExclusive(&open->lock);
|
||||
|
||||
if (status == NFS4_OK) /* use existing delegation */
|
||||
goto out;
|
||||
|
||||
if (*grace) {
|
||||
status = recover_open_grace(session, &open->parent, &open->file,
|
||||
&open->owner, open->share_access, open->share_deny,
|
||||
&stateid, &delegation);
|
||||
if (status == NFS4ERR_NO_GRACE) {
|
||||
*grace = FALSE;
|
||||
/* send RECLAIM_COMPLETE before any out-of-grace recovery */
|
||||
nfs41_reclaim_complete(session);
|
||||
}
|
||||
}
|
||||
if (!*grace) {
|
||||
status = recover_open_no_grace(session, &open->parent, &open->file,
|
||||
&open->owner, open->share_access, open->share_deny,
|
||||
&stateid, &delegation);
|
||||
}
|
||||
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
AcquireSRWLockExclusive(&open->lock);
|
||||
/* update the open stateid */
|
||||
memcpy(&open->stateid, &stateid, sizeof(stateid4));
|
||||
open->do_close = TRUE;
|
||||
|
||||
if (open->delegation.state) {
|
||||
nfs41_delegation_state *deleg = open->delegation.state;
|
||||
if (deleg->revoked) {
|
||||
/* update delegation state */
|
||||
AcquireSRWLockExclusive(&deleg->lock);
|
||||
if (delegation.type != OPEN_DELEGATE_READ &&
|
||||
delegation.type != OPEN_DELEGATE_WRITE) {
|
||||
eprintf("recover_open() got delegation type %u, "
|
||||
"expected %u\n", delegation.type, deleg->state.type);
|
||||
} else {
|
||||
memcpy(&deleg->state, &delegation, sizeof(open_delegation4));
|
||||
deleg->revoked = FALSE;
|
||||
}
|
||||
ReleaseSRWLockExclusive(&deleg->lock);
|
||||
}
|
||||
} else /* granted a new delegation? */
|
||||
nfs41_delegation_granted(session, &open->parent, &open->file,
|
||||
&delegation, FALSE, &open->delegation.state);
|
||||
ReleaseSRWLockExclusive(&open->lock);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int recover_locks(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_open_state *open,
|
||||
IN OUT bool_t *grace)
|
||||
{
|
||||
stateid_arg stateid;
|
||||
struct list_entry *entry;
|
||||
nfs41_lock_state *lock;
|
||||
int status = NFS4_OK;
|
||||
|
||||
AcquireSRWLockExclusive(&open->lock);
|
||||
|
||||
/* initialize the open stateid for the first lock request */
|
||||
memcpy(&stateid.stateid, &open->stateid, sizeof(stateid4));
|
||||
stateid.type = STATEID_OPEN;
|
||||
stateid.open = open;
|
||||
stateid.delegation = NULL;
|
||||
|
||||
/* recover any locks for this open */
|
||||
list_for_each(entry, &open->locks.list) {
|
||||
lock = list_container(entry, nfs41_lock_state, open_entry);
|
||||
if (lock->delegated)
|
||||
continue;
|
||||
|
||||
if (*grace) {
|
||||
status = nfs41_lock(session, &open->file, &open->owner,
|
||||
lock->exclusive ? WRITE_LT : READ_LT, lock->offset,
|
||||
lock->length, TRUE, FALSE, &stateid);
|
||||
if (status == NFS4ERR_NO_GRACE) {
|
||||
*grace = FALSE;
|
||||
/* send RECLAIM_COMPLETE before any out-of-grace recovery */
|
||||
nfs41_reclaim_complete(session);
|
||||
}
|
||||
}
|
||||
if (!*grace) {
|
||||
/* attempt out-of-grace recovery with a normal LOCK */
|
||||
status = nfs41_lock(session, &open->file, &open->owner,
|
||||
lock->exclusive ? WRITE_LT : READ_LT, lock->offset,
|
||||
lock->length, FALSE, FALSE, &stateid);
|
||||
}
|
||||
if (status == NFS4ERR_BADSESSION)
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != NFS4ERR_BADSESSION) {
|
||||
/* if we got a lock stateid back, save the lock with the open */
|
||||
if (stateid.type == STATEID_LOCK)
|
||||
memcpy(&open->locks.stateid, &stateid.stateid, sizeof(stateid4));
|
||||
else
|
||||
open->locks.stateid.seqid = 0;
|
||||
}
|
||||
|
||||
ReleaseSRWLockExclusive(&open->lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* delegation recovery via WANT_DELEGATION */
|
||||
static int recover_delegation_want(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_delegation_state *deleg,
|
||||
IN OUT bool_t *grace)
|
||||
{
|
||||
deleg_claim4 claim;
|
||||
open_delegation4 delegation = { 0 };
|
||||
uint32_t want_flags = 0;
|
||||
int status = NFS4_OK;
|
||||
|
||||
AcquireSRWLockShared(&deleg->lock);
|
||||
delegation.type = deleg->state.type;
|
||||
ReleaseSRWLockShared(&deleg->lock);
|
||||
|
||||
if (delegation.type == OPEN_DELEGATE_READ)
|
||||
want_flags |= OPEN4_SHARE_ACCESS_WANT_READ_DELEG;
|
||||
else
|
||||
want_flags |= OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG;
|
||||
|
||||
if (*grace) {
|
||||
/* recover the delegation with WANT_DELEGATION/CLAIM_PREVIOUS */
|
||||
claim.claim = CLAIM_PREVIOUS;
|
||||
claim.prev_delegate_type = delegation.type;
|
||||
|
||||
status = nfs41_want_delegation(session, &deleg->file, &claim,
|
||||
want_flags, FALSE, &delegation);
|
||||
if (status == NFS4ERR_NO_GRACE) {
|
||||
*grace = FALSE;
|
||||
/* send RECLAIM_COMPLETE before any out-of-grace recovery */
|
||||
nfs41_reclaim_complete(session);
|
||||
}
|
||||
}
|
||||
if (!*grace) {
|
||||
/* attempt out-of-grace recovery with with CLAIM_DELEG_PREV_FH */
|
||||
claim.claim = CLAIM_DELEG_PREV_FH;
|
||||
|
||||
status = nfs41_want_delegation(session, &deleg->file, &claim,
|
||||
want_flags, FALSE, &delegation);
|
||||
}
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
/* update delegation state */
|
||||
AcquireSRWLockExclusive(&deleg->lock);
|
||||
if (delegation.type != OPEN_DELEGATE_READ &&
|
||||
delegation.type != OPEN_DELEGATE_WRITE) {
|
||||
eprintf("recover_delegation_want() got delegation type %u, "
|
||||
"expected %u\n", delegation.type, deleg->state.type);
|
||||
} else {
|
||||
memcpy(&deleg->state, &delegation, sizeof(open_delegation4));
|
||||
deleg->revoked = FALSE;
|
||||
}
|
||||
ReleaseSRWLockExclusive(&deleg->lock);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
/* delegation recovery via OPEN (requires corresponding CLOSE) */
|
||||
static int recover_delegation_open(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_delegation_state *deleg,
|
||||
IN OUT bool_t *grace)
|
||||
{
|
||||
state_owner4 owner;
|
||||
open_delegation4 delegation = { 0 };
|
||||
stateid_arg stateid;
|
||||
uint32_t access = OPEN4_SHARE_ACCESS_READ;
|
||||
uint32_t deny = OPEN4_SHARE_DENY_NONE;
|
||||
int status = NFS4_OK;
|
||||
|
||||
/* choose the desired access mode based on delegation type */
|
||||
AcquireSRWLockShared(&deleg->lock);
|
||||
delegation.type = deleg->state.type;
|
||||
if (delegation.type == OPEN_DELEGATE_WRITE)
|
||||
access |= OPEN4_SHARE_ACCESS_WRITE | OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG;
|
||||
else
|
||||
access |= OPEN4_SHARE_ACCESS_WANT_READ_DELEG;
|
||||
ReleaseSRWLockShared(&deleg->lock);
|
||||
|
||||
/* construct a temporary open owner by concatenating the time
|
||||
* in seconds with the delegation pointer */
|
||||
time((time_t*)owner.owner);
|
||||
memcpy(owner.owner + sizeof(time_t), deleg, sizeof(deleg));
|
||||
owner.owner_len = sizeof(time_t) + sizeof(deleg);
|
||||
|
||||
if (*grace) {
|
||||
status = recover_open_grace(session, &deleg->parent, &deleg->file,
|
||||
&owner, access, deny, &stateid.stateid, &delegation);
|
||||
if (status == NFS4ERR_NO_GRACE) {
|
||||
*grace = FALSE;
|
||||
/* send RECLAIM_COMPLETE before any out-of-grace recovery */
|
||||
nfs41_reclaim_complete(session);
|
||||
}
|
||||
}
|
||||
if (!*grace) {
|
||||
status = recover_open_no_grace(session, &deleg->parent, &deleg->file,
|
||||
&owner, access, deny, &stateid.stateid, &delegation);
|
||||
}
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
/* update delegation state */
|
||||
AcquireSRWLockExclusive(&deleg->lock);
|
||||
if (delegation.type != OPEN_DELEGATE_READ &&
|
||||
delegation.type != OPEN_DELEGATE_WRITE) {
|
||||
eprintf("recover_delegation_open() got delegation type %u, "
|
||||
"expected %u\n", delegation.type, deleg->state.type);
|
||||
} else {
|
||||
memcpy(&deleg->state, &delegation, sizeof(open_delegation4));
|
||||
deleg->revoked = FALSE;
|
||||
}
|
||||
ReleaseSRWLockExclusive(&deleg->lock);
|
||||
|
||||
/* send CLOSE to free the open stateid */
|
||||
stateid.open = NULL;
|
||||
stateid.delegation = NULL;
|
||||
stateid.type = STATEID_OPEN;
|
||||
nfs41_close(session, &deleg->file, &stateid);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int recover_delegation(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_delegation_state *deleg,
|
||||
IN OUT bool_t *grace,
|
||||
IN OUT bool_t *want_supported)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* 10.2.1. Delegation Recovery
|
||||
* When a client needs to reclaim a delegation and there is no
|
||||
* associated open, the client may use the CLAIM_PREVIOUS variant
|
||||
* of the WANT_DELEGATION operation. However, since the server is
|
||||
* not required to support this operation, an alternative is to
|
||||
* reclaim via a dummy OPEN together with the delegation using an
|
||||
* OPEN of type CLAIM_PREVIOUS. */
|
||||
if (*want_supported)
|
||||
status = recover_delegation_want(session, deleg, grace);
|
||||
else
|
||||
status = NFS4ERR_NOTSUPP;
|
||||
|
||||
if (status == NFS4ERR_NOTSUPP) {
|
||||
*want_supported = FALSE;
|
||||
status = recover_delegation_open(session, deleg, grace);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
int nfs41_recover_client_state(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_client *client)
|
||||
{
|
||||
const struct cb_layoutrecall_args recall = { PNFS_LAYOUTTYPE_FILE,
|
||||
PNFS_IOMODE_ANY, TRUE, { PNFS_RETURN_ALL } };
|
||||
struct client_state *state = &session->client->state;
|
||||
struct list_entry *entry;
|
||||
nfs41_open_state *open;
|
||||
nfs41_delegation_state *deleg;
|
||||
bool_t grace = TRUE;
|
||||
bool_t want_supported = TRUE;
|
||||
int status = NFS4_OK;
|
||||
|
||||
EnterCriticalSection(&state->lock);
|
||||
|
||||
/* flag all delegations as revoked until successful recovery;
|
||||
* recover_open() and recover_delegation_open() will only ask
|
||||
* for delegations when revoked = TRUE */
|
||||
list_for_each(entry, &state->delegations) {
|
||||
deleg = list_container(entry, nfs41_delegation_state, client_entry);
|
||||
deleg->revoked = TRUE;
|
||||
}
|
||||
|
||||
/* recover each of the client's opens and associated delegations */
|
||||
list_for_each(entry, &state->opens) {
|
||||
open = list_container(entry, nfs41_open_state, client_entry);
|
||||
status = recover_open(session, open, &grace);
|
||||
if (status == NFS4_OK)
|
||||
status = recover_locks(session, open, &grace);
|
||||
if (status == NFS4ERR_BADSESSION)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* recover delegations that weren't associated with any opens */
|
||||
list_for_each(entry, &state->delegations) {
|
||||
deleg = list_container(entry, nfs41_delegation_state, client_entry);
|
||||
if (deleg->revoked) {
|
||||
status = recover_delegation(session,
|
||||
deleg, &grace, &want_supported);
|
||||
if (status == NFS4ERR_BADSESSION)
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
|
||||
/* return any delegations that were reclaimed as 'recalled' */
|
||||
status = nfs41_client_delegation_recovery(client);
|
||||
unlock:
|
||||
LeaveCriticalSection(&state->lock);
|
||||
|
||||
/* revoke all of the client's layouts */
|
||||
pnfs_file_layout_recall(client, &recall);
|
||||
|
||||
if (grace && status != NFS4ERR_BADSESSION) {
|
||||
/* send reclaim_complete, but don't fail on errors */
|
||||
nfs41_reclaim_complete(session);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static uint32_t stateid_array(
|
||||
IN struct list_entry *delegations,
|
||||
IN struct list_entry *opens,
|
||||
OUT stateid_arg **stateids_out,
|
||||
OUT uint32_t **statuses_out)
|
||||
{
|
||||
struct list_entry *entry;
|
||||
nfs41_open_state *open;
|
||||
nfs41_delegation_state *deleg;
|
||||
stateid_arg *stateids = NULL;
|
||||
uint32_t *statuses = NULL;
|
||||
uint32_t i = 0, count = 0;
|
||||
|
||||
/* count how many stateids the client needs to test */
|
||||
list_for_each(entry, delegations)
|
||||
count++;
|
||||
list_for_each(entry, opens)
|
||||
count += 3; /* open and potentially lock and layout */
|
||||
|
||||
if (count == 0)
|
||||
goto out;
|
||||
|
||||
/* allocate the stateid and status arrays */
|
||||
stateids = calloc(count, sizeof(stateid_arg));
|
||||
if (stateids == NULL)
|
||||
goto out_err;
|
||||
statuses = calloc(count, sizeof(uint32_t));
|
||||
if (statuses == NULL)
|
||||
goto out_err;
|
||||
memset(statuses, NFS4ERR_BAD_STATEID, count * sizeof(uint32_t));
|
||||
|
||||
/* copy stateids into the array */
|
||||
list_for_each(entry, delegations) {
|
||||
deleg = list_container(entry, nfs41_delegation_state, client_entry);
|
||||
AcquireSRWLockShared(&deleg->lock);
|
||||
/* delegation stateid */
|
||||
memcpy(&stateids[i].stateid, &deleg->state.stateid, sizeof(stateid4));
|
||||
stateids[i].type = STATEID_DELEG_FILE;
|
||||
stateids[i].delegation = deleg;
|
||||
i++;
|
||||
ReleaseSRWLockShared(&deleg->lock);
|
||||
}
|
||||
|
||||
list_for_each(entry, opens) {
|
||||
open = list_container(entry, nfs41_open_state, client_entry);
|
||||
|
||||
AcquireSRWLockShared(&open->lock);
|
||||
/* open stateid */
|
||||
memcpy(&stateids[i].stateid, &open->stateid, sizeof(stateid4));
|
||||
stateids[i].type = STATEID_OPEN;
|
||||
stateids[i].open = open;
|
||||
i++;
|
||||
|
||||
if (open->locks.stateid.seqid) { /* lock stateid? */
|
||||
memcpy(&stateids[i].stateid, &open->locks.stateid, sizeof(stateid4));
|
||||
stateids[i].type = STATEID_LOCK;
|
||||
stateids[i].open = open;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (open->layout) { /* layout stateid? */
|
||||
AcquireSRWLockShared(&open->layout->lock);
|
||||
if (open->layout->stateid.seqid) {
|
||||
memcpy(&stateids[i].stateid, &open->layout->stateid, sizeof(stateid4));
|
||||
stateids[i].type = STATEID_LAYOUT;
|
||||
stateids[i].open = open;
|
||||
i++;
|
||||
}
|
||||
ReleaseSRWLockShared(&open->layout->lock);
|
||||
}
|
||||
ReleaseSRWLockShared(&open->lock);
|
||||
}
|
||||
|
||||
count = i;
|
||||
*stateids_out = stateids;
|
||||
*statuses_out = statuses;
|
||||
out:
|
||||
return count;
|
||||
|
||||
out_err:
|
||||
free(stateids);
|
||||
free(statuses);
|
||||
count = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
void nfs41_client_state_revoked(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_client *client,
|
||||
IN uint32_t revoked)
|
||||
{
|
||||
const struct cb_layoutrecall_args recall = { PNFS_LAYOUTTYPE_FILE,
|
||||
PNFS_IOMODE_ANY, TRUE, { PNFS_RETURN_ALL } };
|
||||
struct list_entry empty, *opens;
|
||||
struct client_state *clientstate = &session->client->state;
|
||||
stateid_arg *stateids = NULL;
|
||||
uint32_t *statuses = NULL;
|
||||
uint32_t i, count;
|
||||
bool_t grace = TRUE;
|
||||
bool_t want_supported = TRUE;
|
||||
|
||||
EnterCriticalSection(&clientstate->lock);
|
||||
|
||||
if (revoked == SEQ4_STATUS_RECALLABLE_STATE_REVOKED) {
|
||||
/* only delegations were revoked. use an empty list for opens */
|
||||
list_init(&empty);
|
||||
opens = ∅
|
||||
} else {
|
||||
opens = &clientstate->opens;
|
||||
}
|
||||
|
||||
/* get an array of the client's stateids */
|
||||
count = stateid_array(&clientstate->delegations,
|
||||
opens, &stateids, &statuses);
|
||||
if (count == 0)
|
||||
goto out;
|
||||
|
||||
/* determine which stateids were revoked with TEST_STATEID */
|
||||
if ((revoked & SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED) == 0)
|
||||
nfs41_test_stateid(session, stateids, count, statuses);
|
||||
|
||||
/* free all revoked stateids with FREE_STATEID */
|
||||
for (i = 0; i < count; i++)
|
||||
if (statuses[i])
|
||||
nfs41_free_stateid(session, &stateids[i].stateid);
|
||||
|
||||
/* revoke all of the client's layouts */
|
||||
pnfs_file_layout_recall(client, &recall);
|
||||
|
||||
/* recover the revoked stateids */
|
||||
for (i = 0; i < count; i++) {
|
||||
if (statuses[i]) {
|
||||
if (stateids[i].type == STATEID_DELEG_FILE)
|
||||
stateids[i].delegation->revoked = TRUE;
|
||||
else if (stateids[i].type == STATEID_OPEN)
|
||||
recover_open(session, stateids[i].open, &grace);
|
||||
else if (stateids[i].type == STATEID_LOCK)
|
||||
recover_locks(session, stateids[i].open, &grace);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < count; i++) {
|
||||
/* delegations that weren't recovered by recover_open() */
|
||||
if (statuses[i] && stateids[i].type == STATEID_DELEG_FILE
|
||||
&& stateids[i].delegation->revoked)
|
||||
recover_delegation(session, stateids[i].delegation,
|
||||
&grace, &want_supported);
|
||||
}
|
||||
|
||||
nfs41_client_delegation_recovery(client);
|
||||
out:
|
||||
LeaveCriticalSection(&clientstate->lock);
|
||||
free(stateids);
|
||||
free(statuses);
|
||||
}
|
||||
|
||||
|
||||
static bool_t recover_stateid_open(
|
||||
IN nfs_argop4 *argop,
|
||||
IN stateid_arg *stateid)
|
||||
{
|
||||
bool_t retry = FALSE;
|
||||
|
||||
if (stateid->open) {
|
||||
stateid4 *source = &stateid->open->stateid;
|
||||
|
||||
/* if the source stateid is different, update and retry */
|
||||
AcquireSRWLockShared(&stateid->open->lock);
|
||||
if (memcmp(&stateid->stateid, source, sizeof(stateid4))) {
|
||||
memcpy(&stateid->stateid, source, sizeof(stateid4));
|
||||
retry = TRUE;
|
||||
}
|
||||
ReleaseSRWLockShared(&stateid->open->lock);
|
||||
}
|
||||
return retry;
|
||||
}
|
||||
|
||||
static bool_t recover_stateid_lock(
|
||||
IN nfs_argop4 *argop,
|
||||
IN stateid_arg *stateid)
|
||||
{
|
||||
bool_t retry = FALSE;
|
||||
|
||||
if (stateid->open) {
|
||||
stateid4 *source = &stateid->open->locks.stateid;
|
||||
|
||||
/* if the source stateid is different, update and retry */
|
||||
AcquireSRWLockShared(&stateid->open->lock);
|
||||
if (memcmp(&stateid->stateid, source, sizeof(stateid4))) {
|
||||
if (argop->op == OP_LOCK && source->seqid == 0) {
|
||||
/* resend LOCK with an open stateid */
|
||||
nfs41_lock_args *lock = (nfs41_lock_args*)argop->arg;
|
||||
lock->locker.new_lock_owner = 1;
|
||||
lock->locker.u.open_owner.open_stateid = stateid;
|
||||
lock->locker.u.open_owner.lock_owner = &stateid->open->owner;
|
||||
source = &stateid->open->stateid;
|
||||
}
|
||||
|
||||
memcpy(&stateid->stateid, source, sizeof(stateid4));
|
||||
retry = TRUE;
|
||||
}
|
||||
ReleaseSRWLockShared(&stateid->open->lock);
|
||||
}
|
||||
return retry;
|
||||
}
|
||||
|
||||
static bool_t recover_stateid_delegation(
|
||||
IN nfs_argop4 *argop,
|
||||
IN stateid_arg *stateid)
|
||||
{
|
||||
bool_t retry = FALSE;
|
||||
|
||||
if (stateid->open) {
|
||||
/* if the source stateid is different, update and retry */
|
||||
AcquireSRWLockShared(&stateid->open->lock);
|
||||
if (argop->op == OP_OPEN && stateid->open->do_close) {
|
||||
/* for nfs41_delegation_to_open(); if we've already reclaimed
|
||||
* an open stateid, just fail this OPEN with BAD_STATEID */
|
||||
} else if (stateid->open->delegation.state) {
|
||||
nfs41_delegation_state *deleg = stateid->open->delegation.state;
|
||||
stateid4 *source = &deleg->state.stateid;
|
||||
AcquireSRWLockShared(&deleg->lock);
|
||||
if (memcmp(&stateid->stateid, source, sizeof(stateid4))) {
|
||||
memcpy(&stateid->stateid, source, sizeof(stateid4));
|
||||
retry = TRUE;
|
||||
}
|
||||
ReleaseSRWLockShared(&deleg->lock);
|
||||
}
|
||||
ReleaseSRWLockShared(&stateid->open->lock);
|
||||
} else if (stateid->delegation) {
|
||||
nfs41_delegation_state *deleg = stateid->delegation;
|
||||
stateid4 *source = &deleg->state.stateid;
|
||||
AcquireSRWLockShared(&deleg->lock);
|
||||
if (memcmp(&stateid->stateid, source, sizeof(stateid4))) {
|
||||
memcpy(&stateid->stateid, source, sizeof(stateid4));
|
||||
retry = TRUE;
|
||||
}
|
||||
ReleaseSRWLockShared(&deleg->lock);
|
||||
}
|
||||
return retry;
|
||||
}
|
||||
|
||||
bool_t nfs41_recover_stateid(
|
||||
IN nfs41_session *session,
|
||||
IN nfs_argop4 *argop)
|
||||
{
|
||||
stateid_arg *stateid = NULL;
|
||||
|
||||
/* get the stateid_arg from the operation's arguments */
|
||||
if (argop->op == OP_OPEN) {
|
||||
nfs41_op_open_args *open = (nfs41_op_open_args*)argop->arg;
|
||||
if (open->claim->claim == CLAIM_DELEGATE_CUR)
|
||||
stateid = open->claim->u.deleg_cur.delegate_stateid;
|
||||
else if (open->claim->claim == CLAIM_DELEG_CUR_FH)
|
||||
stateid = open->claim->u.deleg_cur_fh.delegate_stateid;
|
||||
} else if (argop->op == OP_CLOSE) {
|
||||
nfs41_op_close_args *close = (nfs41_op_close_args*)argop->arg;
|
||||
stateid = close->stateid;
|
||||
} else if (argop->op == OP_READ) {
|
||||
nfs41_read_args *read = (nfs41_read_args*)argop->arg;
|
||||
stateid = read->stateid;
|
||||
} else if (argop->op == OP_WRITE) {
|
||||
nfs41_write_args *write = (nfs41_write_args*)argop->arg;
|
||||
stateid = write->stateid;
|
||||
} else if (argop->op == OP_LOCK) {
|
||||
nfs41_lock_args *lock = (nfs41_lock_args*)argop->arg;
|
||||
if (lock->locker.new_lock_owner)
|
||||
stateid = lock->locker.u.open_owner.open_stateid;
|
||||
else
|
||||
stateid = lock->locker.u.lock_owner.lock_stateid;
|
||||
} else if (argop->op == OP_LOCKU) {
|
||||
nfs41_locku_args *locku = (nfs41_locku_args*)argop->arg;
|
||||
stateid = locku->lock_stateid;
|
||||
} else if (argop->op == OP_SETATTR) {
|
||||
nfs41_setattr_args *setattr = (nfs41_setattr_args*)argop->arg;
|
||||
stateid = setattr->stateid;
|
||||
} else if (argop->op == OP_LAYOUTGET) {
|
||||
pnfs_layoutget_args *lget = (pnfs_layoutget_args*)argop->arg;
|
||||
stateid = lget->stateid;
|
||||
} else if (argop->op == OP_DELEGRETURN) {
|
||||
nfs41_delegreturn_args *dr = (nfs41_delegreturn_args*)argop->arg;
|
||||
stateid = dr->stateid;
|
||||
}
|
||||
if (stateid == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* if there's recovery in progress, wait for it to finish */
|
||||
EnterCriticalSection(&session->client->recovery.lock);
|
||||
while (session->client->recovery.in_recovery)
|
||||
SleepConditionVariableCS(&session->client->recovery.cond,
|
||||
&session->client->recovery.lock, INFINITE);
|
||||
LeaveCriticalSection(&session->client->recovery.lock);
|
||||
|
||||
switch (stateid->type) {
|
||||
case STATEID_OPEN:
|
||||
return recover_stateid_open(argop, stateid);
|
||||
|
||||
case STATEID_LOCK:
|
||||
return recover_stateid_lock(argop, stateid);
|
||||
|
||||
case STATEID_DELEG_FILE:
|
||||
return recover_stateid_delegation(argop, stateid);
|
||||
|
||||
default:
|
||||
eprintf("%s can't recover stateid type %u\n",
|
||||
nfs_opnum_to_string(argop->op), stateid->type);
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
59
reactos/base/services/nfsd/recovery.h
Normal file
59
reactos/base/services/nfsd/recovery.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#ifndef RECOVERY_H
|
||||
#define RECOVERY_H
|
||||
|
||||
#include "nfs41.h"
|
||||
|
||||
|
||||
/* session/client recovery uses a lock and condition variable in nfs41_client
|
||||
* to prevent multiple threads from attempting to recover at the same time */
|
||||
bool_t nfs41_recovery_start_or_wait(
|
||||
IN nfs41_client *client);
|
||||
|
||||
void nfs41_recovery_finish(
|
||||
IN nfs41_client *client);
|
||||
|
||||
|
||||
int nfs41_recover_session(
|
||||
IN nfs41_session *session,
|
||||
IN bool_t client_state_lost);
|
||||
|
||||
void nfs41_recover_sequence_flags(
|
||||
IN nfs41_session *session,
|
||||
IN uint32_t flags);
|
||||
|
||||
int nfs41_recover_client_state(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_client *client);
|
||||
|
||||
void nfs41_client_state_revoked(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_client *client,
|
||||
IN uint32_t revoked);
|
||||
|
||||
struct __nfs_argop4;
|
||||
bool_t nfs41_recover_stateid(
|
||||
IN nfs41_session *session,
|
||||
IN struct __nfs_argop4 *argop);
|
||||
|
||||
#endif /* RECOVERY_H */
|
607
reactos/base/services/nfsd/service.c
Normal file
607
reactos/base/services/nfsd/service.c
Normal file
|
@ -0,0 +1,607 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
||||
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
||||
PARTICULAR PURPOSE.
|
||||
|
||||
Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
|
||||
MODULE: service.c
|
||||
|
||||
PURPOSE: Implements functions required by all Windows NT services
|
||||
|
||||
FUNCTIONS:
|
||||
main(int argc, char **argv);
|
||||
service_ctrl(DWORD dwCtrlCode);
|
||||
service_main(DWORD dwArgc, LPTSTR *lpszArgv);
|
||||
CmdInstallService();
|
||||
CmdRemoveService();
|
||||
CmdDebugService(int argc, char **argv);
|
||||
ControlHandler ( DWORD dwCtrlType );
|
||||
GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
#include <windows.h>
|
||||
#ifndef STANDALONE_NFSD
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <process.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#include "service.h"
|
||||
|
||||
// internal variables
|
||||
SERVICE_STATUS ssStatus; // current status of the service
|
||||
SERVICE_STATUS_HANDLE sshStatusHandle;
|
||||
DWORD dwErr = 0;
|
||||
BOOL bDebug = FALSE;
|
||||
TCHAR szErr[256];
|
||||
|
||||
// internal function prototypes
|
||||
VOID WINAPI service_ctrl(DWORD dwCtrlCode);
|
||||
VOID WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv);
|
||||
VOID CmdInstallService();
|
||||
VOID CmdRemoveService();
|
||||
VOID CmdDebugService(int argc, char **argv);
|
||||
BOOL WINAPI ControlHandler ( DWORD dwCtrlType );
|
||||
LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
|
||||
|
||||
//
|
||||
// FUNCTION: main
|
||||
//
|
||||
// PURPOSE: entrypoint for service
|
||||
//
|
||||
// PARAMETERS:
|
||||
// argc - number of command line arguments
|
||||
// argv - array of command line arguments
|
||||
//
|
||||
// RETURN VALUE:
|
||||
// none
|
||||
//
|
||||
// COMMENTS:
|
||||
// main() either performs the command line task, or
|
||||
// call StartServiceCtrlDispatcher to register the
|
||||
// main service thread. When the this call returns,
|
||||
// the service has stopped, so exit.
|
||||
//
|
||||
void __cdecl main(int argc, char **argv)
|
||||
{
|
||||
SERVICE_TABLE_ENTRY dispatchTable[] =
|
||||
{
|
||||
{ TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main},
|
||||
{ NULL, NULL}
|
||||
};
|
||||
|
||||
if ( (argc > 1) &&
|
||||
((*argv[1] == '-') || (*argv[1] == '/')) )
|
||||
{
|
||||
if ( _stricmp( "install", argv[1]+1 ) == 0 )
|
||||
{
|
||||
CmdInstallService();
|
||||
}
|
||||
else if ( _stricmp( "remove", argv[1]+1 ) == 0 )
|
||||
{
|
||||
CmdRemoveService();
|
||||
}
|
||||
else if ( _stricmp( "debug", argv[1]+1 ) == 0 )
|
||||
{
|
||||
bDebug = TRUE;
|
||||
CmdDebugService(argc, argv);
|
||||
}
|
||||
else
|
||||
{
|
||||
goto dispatch;
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// if it doesn't match any of the above parameters
|
||||
// the service control manager may be starting the service
|
||||
// so we must call StartServiceCtrlDispatcher
|
||||
dispatch:
|
||||
// this is just to be friendly
|
||||
printf( "%s -install to install the service\n", SZAPPNAME );
|
||||
printf( "%s -remove to remove the service\n", SZAPPNAME );
|
||||
printf( "%s -debug <params> to run as a console app for debugging\n", SZAPPNAME );
|
||||
printf( "\nStartServiceCtrlDispatcher being called.\n" );
|
||||
printf( "This may take several seconds. Please wait.\n" );
|
||||
|
||||
if (!StartServiceCtrlDispatcher(dispatchTable))
|
||||
AddToMessageLog(TEXT("StartServiceCtrlDispatcher failed."));
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// FUNCTION: service_main
|
||||
//
|
||||
// PURPOSE: To perform actual initialization of the service
|
||||
//
|
||||
// PARAMETERS:
|
||||
// dwArgc - number of command line arguments
|
||||
// lpszArgv - array of command line arguments
|
||||
//
|
||||
// RETURN VALUE:
|
||||
// none
|
||||
//
|
||||
// COMMENTS:
|
||||
// This routine performs the service initialization and then calls
|
||||
// the user defined ServiceStart() routine to perform majority
|
||||
// of the work.
|
||||
//
|
||||
void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv)
|
||||
{
|
||||
|
||||
// register our service control handler:
|
||||
//
|
||||
sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), service_ctrl);
|
||||
|
||||
if (!sshStatusHandle)
|
||||
goto cleanup;
|
||||
|
||||
// SERVICE_STATUS members that don't change in example
|
||||
//
|
||||
ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||
ssStatus.dwServiceSpecificExitCode = 0;
|
||||
|
||||
|
||||
// report the status to the service control manager.
|
||||
//
|
||||
if (!ReportStatusToSCMgr(
|
||||
SERVICE_START_PENDING, // service state
|
||||
NO_ERROR, // exit code
|
||||
3000)) // wait hint
|
||||
goto cleanup;
|
||||
|
||||
|
||||
ServiceStart( dwArgc, lpszArgv );
|
||||
|
||||
cleanup:
|
||||
|
||||
// try to report the stopped status to the service control manager.
|
||||
//
|
||||
if (sshStatusHandle)
|
||||
(VOID)ReportStatusToSCMgr(
|
||||
SERVICE_STOPPED,
|
||||
dwErr,
|
||||
0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// FUNCTION: service_ctrl
|
||||
//
|
||||
// PURPOSE: This function is called by the SCM whenever
|
||||
// ControlService() is called on this service.
|
||||
//
|
||||
// PARAMETERS:
|
||||
// dwCtrlCode - type of control requested
|
||||
//
|
||||
// RETURN VALUE:
|
||||
// none
|
||||
//
|
||||
// COMMENTS:
|
||||
//
|
||||
VOID WINAPI service_ctrl(DWORD dwCtrlCode)
|
||||
{
|
||||
// Handle the requested control code.
|
||||
//
|
||||
switch (dwCtrlCode)
|
||||
{
|
||||
// Stop the service.
|
||||
//
|
||||
// SERVICE_STOP_PENDING should be reported before
|
||||
// setting the Stop Event - hServerStopEvent - in
|
||||
// ServiceStop(). This avoids a race condition
|
||||
// which may result in a 1053 - The Service did not respond...
|
||||
// error.
|
||||
case SERVICE_CONTROL_STOP:
|
||||
ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0);
|
||||
ServiceStop();
|
||||
return;
|
||||
|
||||
// Update the service status.
|
||||
//
|
||||
case SERVICE_CONTROL_INTERROGATE:
|
||||
break;
|
||||
|
||||
// invalid control code
|
||||
//
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// FUNCTION: ReportStatusToSCMgr()
|
||||
//
|
||||
// PURPOSE: Sets the current status of the service and
|
||||
// reports it to the Service Control Manager
|
||||
//
|
||||
// PARAMETERS:
|
||||
// dwCurrentState - the state of the service
|
||||
// dwWin32ExitCode - error code to report
|
||||
// dwWaitHint - worst case estimate to next checkpoint
|
||||
//
|
||||
// RETURN VALUE:
|
||||
// TRUE - success
|
||||
// FALSE - failure
|
||||
//
|
||||
// COMMENTS:
|
||||
//
|
||||
BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
|
||||
DWORD dwWin32ExitCode,
|
||||
DWORD dwWaitHint)
|
||||
{
|
||||
static DWORD dwCheckPoint = 1;
|
||||
BOOL fResult = TRUE;
|
||||
|
||||
|
||||
if ( !bDebug ) // when debugging we don't report to the SCM
|
||||
{
|
||||
if (dwCurrentState == SERVICE_START_PENDING)
|
||||
ssStatus.dwControlsAccepted = 0;
|
||||
else
|
||||
ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
||||
|
||||
ssStatus.dwCurrentState = dwCurrentState;
|
||||
ssStatus.dwWin32ExitCode = dwWin32ExitCode;
|
||||
ssStatus.dwWaitHint = dwWaitHint;
|
||||
|
||||
if ( ( dwCurrentState == SERVICE_RUNNING ) ||
|
||||
( dwCurrentState == SERVICE_STOPPED ) )
|
||||
ssStatus.dwCheckPoint = 0;
|
||||
else
|
||||
ssStatus.dwCheckPoint = dwCheckPoint++;
|
||||
|
||||
|
||||
// Report the status of the service to the service control manager.
|
||||
fResult = SetServiceStatus(sshStatusHandle, &ssStatus);
|
||||
if (!fResult)
|
||||
AddToMessageLog(TEXT("SetServiceStatus"));
|
||||
}
|
||||
return fResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
|
||||
//
|
||||
// PURPOSE: Allows any thread to log an error message
|
||||
//
|
||||
// PARAMETERS:
|
||||
// lpszMsg - text for message
|
||||
//
|
||||
// RETURN VALUE:
|
||||
// none
|
||||
//
|
||||
// COMMENTS:
|
||||
//
|
||||
VOID AddToMessageLog(LPTSTR lpszMsg)
|
||||
{
|
||||
TCHAR szMsg [(sizeof(SZSERVICENAME) / sizeof(TCHAR)) + 100 ];
|
||||
HANDLE hEventSource;
|
||||
LPTSTR lpszStrings[2];
|
||||
|
||||
if ( !bDebug )
|
||||
{
|
||||
dwErr = GetLastError();
|
||||
|
||||
// Use event logging to log the error.
|
||||
//
|
||||
hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
|
||||
|
||||
#ifndef __REACTOS__
|
||||
_stprintf_s(szMsg,(sizeof(SZSERVICENAME) / sizeof(TCHAR)) + 100, TEXT("%s error: %d"), TEXT(SZSERVICENAME), dwErr);
|
||||
#else
|
||||
_sntprintf(szMsg,(sizeof(SZSERVICENAME) / sizeof(TCHAR)) + 100, TEXT("%s error: %d"), TEXT(SZSERVICENAME), dwErr);
|
||||
#endif
|
||||
lpszStrings[0] = szMsg;
|
||||
lpszStrings[1] = lpszMsg;
|
||||
|
||||
if (hEventSource != NULL)
|
||||
{
|
||||
ReportEvent(hEventSource, // handle of event source
|
||||
EVENTLOG_ERROR_TYPE, // event type
|
||||
0, // event category
|
||||
0, // event ID
|
||||
NULL, // current user's SID
|
||||
2, // strings in lpszStrings
|
||||
0, // no bytes of raw data
|
||||
lpszStrings, // array of error strings
|
||||
NULL); // no raw data
|
||||
|
||||
(VOID) DeregisterEventSource(hEventSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// The following code handles service installation and removal
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// FUNCTION: CmdInstallService()
|
||||
//
|
||||
// PURPOSE: Installs the service
|
||||
//
|
||||
// PARAMETERS:
|
||||
// none
|
||||
//
|
||||
// RETURN VALUE:
|
||||
// none
|
||||
//
|
||||
// COMMENTS:
|
||||
//
|
||||
void CmdInstallService()
|
||||
{
|
||||
SC_HANDLE schService;
|
||||
SC_HANDLE schSCManager;
|
||||
|
||||
TCHAR szPath[512];
|
||||
|
||||
if ( GetModuleFileName( NULL, szPath, 512 ) == 0 )
|
||||
{
|
||||
_tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256));
|
||||
return;
|
||||
}
|
||||
|
||||
schSCManager = OpenSCManager(
|
||||
NULL, // machine (NULL == local)
|
||||
NULL, // database (NULL == default)
|
||||
SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE // access required
|
||||
);
|
||||
if ( schSCManager )
|
||||
{
|
||||
schService = CreateService(
|
||||
schSCManager, // SCManager database
|
||||
TEXT(SZSERVICENAME), // name of service
|
||||
TEXT(SZSERVICEDISPLAYNAME), // name to display
|
||||
SERVICE_QUERY_STATUS, // desired access
|
||||
SERVICE_WIN32_OWN_PROCESS, // service type
|
||||
SERVICE_AUTO_START, // start type
|
||||
SERVICE_ERROR_NORMAL, // error control type
|
||||
szPath, // service's binary
|
||||
NULL, // no load ordering group
|
||||
NULL, // no tag identifier
|
||||
TEXT(SZDEPENDENCIES), // dependencies
|
||||
NULL, // LocalSystem account
|
||||
NULL); // no password
|
||||
|
||||
if ( schService )
|
||||
{
|
||||
_tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
|
||||
CloseServiceHandle(schService);
|
||||
}
|
||||
else
|
||||
{
|
||||
_tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
|
||||
}
|
||||
|
||||
CloseServiceHandle(schSCManager);
|
||||
}
|
||||
else
|
||||
_tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// FUNCTION: CmdRemoveService()
|
||||
//
|
||||
// PURPOSE: Stops and removes the service
|
||||
//
|
||||
// PARAMETERS:
|
||||
// none
|
||||
//
|
||||
// RETURN VALUE:
|
||||
// none
|
||||
//
|
||||
// COMMENTS:
|
||||
//
|
||||
void CmdRemoveService()
|
||||
{
|
||||
SC_HANDLE schService;
|
||||
SC_HANDLE schSCManager;
|
||||
|
||||
schSCManager = OpenSCManager(
|
||||
NULL, // machine (NULL == local)
|
||||
NULL, // database (NULL == default)
|
||||
SC_MANAGER_CONNECT // access required
|
||||
);
|
||||
if ( schSCManager )
|
||||
{
|
||||
schService = OpenService(schSCManager, TEXT(SZSERVICENAME), DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
|
||||
|
||||
if (schService)
|
||||
{
|
||||
// try to stop the service
|
||||
if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
|
||||
{
|
||||
_tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
|
||||
Sleep( 1000 );
|
||||
|
||||
while ( QueryServiceStatus( schService, &ssStatus ) )
|
||||
{
|
||||
if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
|
||||
{
|
||||
_tprintf(TEXT("."));
|
||||
Sleep( 1000 );
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
|
||||
_tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME) );
|
||||
else
|
||||
_tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) );
|
||||
|
||||
}
|
||||
|
||||
// now remove the service
|
||||
if ( DeleteService(schService) )
|
||||
_tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
|
||||
else
|
||||
_tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
|
||||
|
||||
|
||||
CloseServiceHandle(schService);
|
||||
}
|
||||
else
|
||||
_tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
|
||||
|
||||
CloseServiceHandle(schSCManager);
|
||||
}
|
||||
else
|
||||
_tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// The following code is for running the service as a console app
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// FUNCTION: CmdDebugService(int argc, char ** argv)
|
||||
//
|
||||
// PURPOSE: Runs the service as a console application
|
||||
//
|
||||
// PARAMETERS:
|
||||
// argc - number of command line arguments
|
||||
// argv - array of command line arguments
|
||||
//
|
||||
// RETURN VALUE:
|
||||
// none
|
||||
//
|
||||
// COMMENTS:
|
||||
//
|
||||
void CmdDebugService(int argc, char ** argv)
|
||||
{
|
||||
DWORD dwArgc;
|
||||
LPTSTR *lpszArgv;
|
||||
|
||||
#ifdef UNICODE
|
||||
lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
|
||||
if (NULL == lpszArgv)
|
||||
{
|
||||
// CommandLineToArvW failed!!
|
||||
_tprintf(TEXT("CmdDebugService CommandLineToArgvW returned NULL\n"));
|
||||
return;
|
||||
}
|
||||
#else
|
||||
dwArgc = (DWORD) argc;
|
||||
lpszArgv = argv;
|
||||
#endif
|
||||
|
||||
_tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
|
||||
|
||||
SetConsoleCtrlHandler( ControlHandler, TRUE );
|
||||
|
||||
ServiceStart( dwArgc, lpszArgv );
|
||||
|
||||
#ifdef UNICODE
|
||||
// Must free memory allocated for arguments
|
||||
|
||||
GlobalFree(lpszArgv);
|
||||
#endif // UNICODE
|
||||
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// FUNCTION: ControlHandler ( DWORD dwCtrlType )
|
||||
//
|
||||
// PURPOSE: Handled console control events
|
||||
//
|
||||
// PARAMETERS:
|
||||
// dwCtrlType - type of control event
|
||||
//
|
||||
// RETURN VALUE:
|
||||
// True - handled
|
||||
// False - unhandled
|
||||
//
|
||||
// COMMENTS:
|
||||
//
|
||||
BOOL WINAPI ControlHandler ( DWORD dwCtrlType )
|
||||
{
|
||||
switch ( dwCtrlType )
|
||||
{
|
||||
case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
|
||||
case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
|
||||
_tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
|
||||
ServiceStop();
|
||||
return TRUE;
|
||||
break;
|
||||
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//
|
||||
// FUNCTION: GetLastErrorText
|
||||
//
|
||||
// PURPOSE: copies error message text to string
|
||||
//
|
||||
// PARAMETERS:
|
||||
// lpszBuf - destination buffer
|
||||
// dwSize - size of buffer
|
||||
//
|
||||
// RETURN VALUE:
|
||||
// destination buffer
|
||||
//
|
||||
// COMMENTS:
|
||||
//
|
||||
LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize )
|
||||
{
|
||||
DWORD dwRet;
|
||||
LPTSTR lpszTemp = NULL;
|
||||
|
||||
dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
||||
NULL,
|
||||
GetLastError(),
|
||||
LANG_NEUTRAL,
|
||||
(LPTSTR)&lpszTemp,
|
||||
0,
|
||||
NULL );
|
||||
|
||||
// supplied buffer is not long enough
|
||||
if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
|
||||
lpszBuf[0] = TEXT('\0');
|
||||
else
|
||||
{
|
||||
if (NULL != lpszTemp)
|
||||
{
|
||||
lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character
|
||||
#ifndef __REACTOS__
|
||||
_stprintf_s( lpszBuf, dwSize, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
|
||||
#else
|
||||
_sntprintf( lpszBuf, dwSize, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if ( NULL != lpszTemp )
|
||||
LocalFree((HLOCAL) lpszTemp );
|
||||
|
||||
return lpszBuf;
|
||||
}
|
||||
#endif
|
137
reactos/base/services/nfsd/service.h
Normal file
137
reactos/base/services/nfsd/service.h
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
||||
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
||||
PARTICULAR PURPOSE.
|
||||
|
||||
Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
|
||||
MODULE: service.h
|
||||
|
||||
Comments: The use of this header file and the accompanying service.c
|
||||
file simplifies the process of writting a service. You as a developer
|
||||
simply need to follow the TODO's outlined in this header file, and
|
||||
implement the ServiceStart() and ServiceStop() functions.
|
||||
|
||||
There is no need to modify the code in service.c. Just add service.c
|
||||
to your project and link with the following libraries...
|
||||
|
||||
libcmt.lib kernel32.lib advapi.lib shell32.lib
|
||||
|
||||
This code also supports unicode. Be sure to compile both service.c and
|
||||
and code #include "service.h" with the same Unicode setting.
|
||||
|
||||
Upon completion, your code will have the following command line interface
|
||||
|
||||
<service exe> -? to display this list
|
||||
<service exe> -install to install the service
|
||||
<service exe> -remove to remove the service
|
||||
<service exe> -debug <params> to run as a console app for debugging
|
||||
|
||||
Note: This code also implements Ctrl+C and Ctrl+Break handlers
|
||||
when using the debug option. These console events cause
|
||||
your ServiceStop routine to be called
|
||||
|
||||
Also, this code only handles the OWN_SERVICE service type
|
||||
running in the LOCAL_SYSTEM security context.
|
||||
|
||||
To control your service ( start, stop, etc ) you may use the
|
||||
Services control panel applet or the NET.EXE program.
|
||||
|
||||
To aid in writing/debugging service, the
|
||||
SDK contains a utility (MSTOOLS\BIN\SC.EXE) that
|
||||
can be used to control, configure, or obtain service status.
|
||||
SC displays complete status for any service/driver
|
||||
in the service database, and allows any of the configuration
|
||||
parameters to be easily changed at the command line.
|
||||
For more information on SC.EXE, type SC at the command line.
|
||||
|
||||
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _SERVICE_H
|
||||
#define _SERVICE_H
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// todo: change to desired strings
|
||||
////
|
||||
// name of the executable
|
||||
#define SZAPPNAME "nfsd"
|
||||
// internal name of the service
|
||||
#define SZSERVICENAME "pnfs"
|
||||
// displayed name of the service
|
||||
#define SZSERVICEDISPLAYNAME "NFSv4.1 Client"
|
||||
// list of service dependencies - "dep1\0dep2\0\0"
|
||||
#define SZDEPENDENCIES ""
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// todo: ServiceStart()must be defined by in your code.
|
||||
//// The service should use ReportStatusToSCMgr to indicate
|
||||
//// progress. This routine must also be used by StartService()
|
||||
//// to report to the SCM when the service is running.
|
||||
////
|
||||
//// If a ServiceStop procedure is going to take longer than
|
||||
//// 3 seconds to execute, it should spawn a thread to
|
||||
//// execute the stop code, and return. Otherwise, the
|
||||
//// ServiceControlManager will believe that the service has
|
||||
//// stopped responding
|
||||
////
|
||||
VOID ServiceStart(DWORD dwArgc, LPTSTR *lpszArgv);
|
||||
VOID ServiceStop();
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// The following are procedures which
|
||||
//// may be useful to call within the above procedures,
|
||||
//// but require no implementation by the user.
|
||||
//// They are implemented in service.c
|
||||
|
||||
//
|
||||
// FUNCTION: ReportStatusToSCMgr()
|
||||
//
|
||||
// PURPOSE: Sets the current status of the service and
|
||||
// reports it to the Service Control Manager
|
||||
//
|
||||
// PARAMETERS:
|
||||
// dwCurrentState - the state of the service
|
||||
// dwWin32ExitCode - error code to report
|
||||
// dwWaitHint - worst case estimate to next checkpoint
|
||||
//
|
||||
// RETURN VALUE:
|
||||
// TRUE - success
|
||||
// FALSE - failure
|
||||
//
|
||||
BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);
|
||||
|
||||
|
||||
//
|
||||
// FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
|
||||
//
|
||||
// PURPOSE: Allows any thread to log an error message
|
||||
//
|
||||
// PARAMETERS:
|
||||
// lpszMsg - text for message
|
||||
//
|
||||
// RETURN VALUE:
|
||||
// none
|
||||
//
|
||||
void AddToMessageLog(LPTSTR lpszMsg);
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
526
reactos/base/services/nfsd/setattr.c
Normal file
526
reactos/base/services/nfsd/setattr.c
Normal file
|
@ -0,0 +1,526 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#include "from_kernel.h"
|
||||
#include "nfs41_ops.h"
|
||||
#include "delegation.h"
|
||||
#include "name_cache.h"
|
||||
#include "upcall.h"
|
||||
#include "util.h"
|
||||
#include "daemon_debug.h"
|
||||
|
||||
|
||||
/* NFS41_FILE_SET */
|
||||
static int parse_setattr(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
setattr_upcall_args *args = &upcall->args.setattr;
|
||||
|
||||
status = get_name(&buffer, &length, &args->path);
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->set_class, sizeof(args->set_class));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->buf_len, sizeof(args->buf_len));
|
||||
if (status) goto out;
|
||||
|
||||
args->buf = buffer;
|
||||
args->root = upcall->root_ref;
|
||||
args->state = upcall->state_ref;
|
||||
|
||||
dprintf(1, "parsing NFS41_FILE_SET: filename='%s' info_class=%d "
|
||||
"buf_len=%d\n", args->path, args->set_class, args->buf_len);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int handle_nfs41_setattr(setattr_upcall_args *args)
|
||||
{
|
||||
PFILE_BASIC_INFO basic_info = (PFILE_BASIC_INFO)args->buf;
|
||||
nfs41_open_state *state = args->state;
|
||||
nfs41_superblock *superblock = state->file.fh.superblock;
|
||||
stateid_arg stateid;
|
||||
nfs41_file_info info = { 0 }, old_info = { 0 };
|
||||
int status = NO_ERROR, getattr_status;
|
||||
|
||||
if (basic_info->FileAttributes) {
|
||||
info.hidden = basic_info->FileAttributes & FILE_ATTRIBUTE_HIDDEN ? 1 : 0;
|
||||
info.system = basic_info->FileAttributes & FILE_ATTRIBUTE_SYSTEM ? 1 : 0;
|
||||
info.archive = basic_info->FileAttributes & FILE_ATTRIBUTE_ARCHIVE ? 1 : 0;
|
||||
getattr_status = nfs41_attr_cache_lookup(session_name_cache(state->session),
|
||||
state->file.fh.fileid, &old_info);
|
||||
|
||||
if (getattr_status || info.hidden != old_info.hidden) {
|
||||
info.attrmask.arr[0] = FATTR4_WORD0_HIDDEN;
|
||||
info.attrmask.count = 1;
|
||||
}
|
||||
if (getattr_status || info.archive != old_info.archive) {
|
||||
info.attrmask.arr[0] |= FATTR4_WORD0_ARCHIVE;
|
||||
info.attrmask.count = 1;
|
||||
}
|
||||
if (getattr_status || info.system != old_info.system) {
|
||||
info.attrmask.arr[1] = FATTR4_WORD1_SYSTEM;
|
||||
info.attrmask.count = 2;
|
||||
}
|
||||
}
|
||||
if (old_info.mode == 0444 &&
|
||||
((basic_info->FileAttributes & FILE_ATTRIBUTE_READONLY) == 0)) {
|
||||
info.mode = 0644;
|
||||
info.attrmask.arr[1] |= FATTR4_WORD1_MODE;
|
||||
info.attrmask.count = 2;
|
||||
}
|
||||
|
||||
if (superblock->cansettime) {
|
||||
/* set the time_delta so xdr_settime4() can decide
|
||||
* whether or not to use SET_TO_SERVER_TIME4 */
|
||||
info.time_delta = &superblock->time_delta;
|
||||
|
||||
/* time_create */
|
||||
if (basic_info->CreationTime.QuadPart > 0) {
|
||||
file_time_to_nfs_time(&basic_info->CreationTime,
|
||||
&info.time_create);
|
||||
info.attrmask.arr[1] |= FATTR4_WORD1_TIME_CREATE;
|
||||
info.attrmask.count = 2;
|
||||
}
|
||||
/* time_access_set */
|
||||
if (basic_info->LastAccessTime.QuadPart > 0) {
|
||||
file_time_to_nfs_time(&basic_info->LastAccessTime,
|
||||
&info.time_access);
|
||||
info.attrmask.arr[1] |= FATTR4_WORD1_TIME_ACCESS_SET;
|
||||
info.attrmask.count = 2;
|
||||
}
|
||||
/* time_modify_set */
|
||||
if (basic_info->LastWriteTime.QuadPart > 0) {
|
||||
file_time_to_nfs_time(&basic_info->LastWriteTime,
|
||||
&info.time_modify);
|
||||
info.attrmask.arr[1] |= FATTR4_WORD1_TIME_MODIFY_SET;
|
||||
info.attrmask.count = 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* mode */
|
||||
if (basic_info->FileAttributes & FILE_ATTRIBUTE_READONLY) {
|
||||
info.mode = 0444;
|
||||
info.attrmask.arr[1] |= FATTR4_WORD1_MODE;
|
||||
info.attrmask.count = 2;
|
||||
}
|
||||
|
||||
/* mask out unsupported attributes */
|
||||
nfs41_superblock_supported_attrs(superblock, &info.attrmask);
|
||||
|
||||
if (!info.attrmask.count)
|
||||
goto out;
|
||||
|
||||
/* break read delegations before SETATTR */
|
||||
nfs41_delegation_return(state->session, &state->file,
|
||||
OPEN_DELEGATE_READ, FALSE);
|
||||
|
||||
nfs41_open_stateid_arg(state, &stateid);
|
||||
|
||||
status = nfs41_setattr(state->session, &state->file, &stateid, &info);
|
||||
if (status) {
|
||||
dprintf(1, "nfs41_setattr() failed with error %s.\n",
|
||||
nfs_error_string(status));
|
||||
status = nfs_to_windows_error(status, ERROR_NOT_SUPPORTED);
|
||||
}
|
||||
args->ctime = info.change;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int handle_nfs41_remove(setattr_upcall_args *args)
|
||||
{
|
||||
nfs41_open_state *state = args->state;
|
||||
int status;
|
||||
|
||||
/* break any delegations and truncate before REMOVE */
|
||||
nfs41_delegation_return(state->session, &state->file,
|
||||
OPEN_DELEGATE_WRITE, TRUE);
|
||||
|
||||
status = nfs41_remove(state->session, &state->parent,
|
||||
&state->file.name, state->file.fh.fileid);
|
||||
if (status)
|
||||
dprintf(1, "nfs41_remove() failed with error %s.\n",
|
||||
nfs_error_string(status));
|
||||
|
||||
return nfs_to_windows_error(status, ERROR_ACCESS_DENIED);
|
||||
}
|
||||
|
||||
static void open_state_rename(
|
||||
OUT nfs41_open_state *state,
|
||||
IN const nfs41_abs_path *path)
|
||||
{
|
||||
AcquireSRWLockExclusive(&state->path.lock);
|
||||
|
||||
abs_path_copy(&state->path, path);
|
||||
last_component(state->path.path, state->path.path + state->path.len,
|
||||
&state->file.name);
|
||||
last_component(state->path.path, state->file.name.name,
|
||||
&state->parent.name);
|
||||
|
||||
ReleaseSRWLockExclusive(&state->path.lock);
|
||||
}
|
||||
|
||||
static int nfs41_abs_path_compare(
|
||||
IN const struct list_entry *entry,
|
||||
IN const void *value)
|
||||
{
|
||||
nfs41_open_state *client = list_container(entry, nfs41_open_state, client_entry);
|
||||
const nfs41_abs_path *name = (const nfs41_abs_path *)value;
|
||||
if (client->path.len == name->len &&
|
||||
!strncmp(client->path.path, name->path, client->path.len))
|
||||
return NO_ERROR;
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
static int is_dst_name_opened(nfs41_abs_path *dst_path, nfs41_session *dst_session)
|
||||
{
|
||||
int status;
|
||||
nfs41_client *client = dst_session->client;
|
||||
|
||||
EnterCriticalSection(&client->state.lock);
|
||||
if (list_search(&client->state.opens, dst_path, nfs41_abs_path_compare))
|
||||
status = TRUE;
|
||||
else
|
||||
status = FALSE;
|
||||
LeaveCriticalSection(&client->state.lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
static int handle_nfs41_rename(setattr_upcall_args *args)
|
||||
{
|
||||
nfs41_open_state *state = args->state;
|
||||
nfs41_session *dst_session;
|
||||
PFILE_RENAME_INFO rename = (PFILE_RENAME_INFO)args->buf;
|
||||
nfs41_abs_path dst_path = { 0 };
|
||||
nfs41_path_fh dst_dir, dst;
|
||||
nfs41_component dst_name, *src_name;
|
||||
uint32_t depth = 0;
|
||||
int status;
|
||||
|
||||
src_name = &state->file.name;
|
||||
|
||||
if (rename->FileNameLength == 0) {
|
||||
/* start from state->path instead of args->path, in case we got
|
||||
* the file from a referred server */
|
||||
AcquireSRWLockShared(&state->path.lock);
|
||||
abs_path_copy(&dst_path, &state->path);
|
||||
ReleaseSRWLockShared(&state->path.lock);
|
||||
|
||||
path_fh_init(&dst_dir, &dst_path);
|
||||
fh_copy(&dst_dir.fh, &state->parent.fh);
|
||||
|
||||
create_silly_rename(&dst_path, &state->file.fh, &dst_name);
|
||||
dprintf(1, "silly rename: %s -> %s\n", src_name->name, dst_name.name);
|
||||
|
||||
/* break any delegations and truncate before silly rename */
|
||||
nfs41_delegation_return(state->session, &state->file,
|
||||
OPEN_DELEGATE_WRITE, TRUE);
|
||||
|
||||
status = nfs41_rename(state->session,
|
||||
&state->parent, src_name,
|
||||
&dst_dir, &dst_name);
|
||||
if (status) {
|
||||
dprintf(1, "nfs41_rename() failed with error %s.\n",
|
||||
nfs_error_string(status));
|
||||
status = nfs_to_windows_error(status, ERROR_ACCESS_DENIED);
|
||||
} else {
|
||||
/* rename state->path on success */
|
||||
open_state_rename(state, &dst_path);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
dst_path.len = (unsigned short)WideCharToMultiByte(CP_UTF8, 0,
|
||||
rename->FileName, rename->FileNameLength/sizeof(WCHAR),
|
||||
dst_path.path, NFS41_MAX_PATH_LEN, NULL, NULL);
|
||||
if (dst_path.len == 0) {
|
||||
eprintf("WideCharToMultiByte failed to convert destination "
|
||||
"filename %S.\n", rename->FileName);
|
||||
status = ERROR_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
path_fh_init(&dst_dir, &dst_path);
|
||||
|
||||
/* the destination path is absolute, so start from the root session */
|
||||
status = nfs41_lookup(args->root, nfs41_root_session(args->root),
|
||||
&dst_path, &dst_dir, &dst, NULL, &dst_session);
|
||||
|
||||
while (status == ERROR_REPARSE) {
|
||||
if (++depth > NFS41_MAX_SYMLINK_DEPTH) {
|
||||
status = ERROR_TOO_MANY_LINKS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* replace the path with the symlink target's */
|
||||
status = nfs41_symlink_target(dst_session, &dst_dir, &dst_path);
|
||||
if (status) {
|
||||
eprintf("nfs41_symlink_target() for %s failed with %d\n",
|
||||
dst_dir.path->path, status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* redo the lookup until it doesn't return REPARSE */
|
||||
status = nfs41_lookup(args->root, dst_session,
|
||||
&dst_path, &dst_dir, NULL, NULL, &dst_session);
|
||||
}
|
||||
|
||||
/* get the components after lookup in case a referral changed its path */
|
||||
last_component(dst_path.path, dst_path.path + dst_path.len, &dst_name);
|
||||
last_component(dst_path.path, dst_name.name, &dst_dir.name);
|
||||
|
||||
if (status == NO_ERROR) {
|
||||
if (!rename->ReplaceIfExists) {
|
||||
status = ERROR_FILE_EXISTS;
|
||||
goto out;
|
||||
}
|
||||
/* break any delegations and truncate the destination file */
|
||||
nfs41_delegation_return(dst_session, &dst,
|
||||
OPEN_DELEGATE_WRITE, TRUE);
|
||||
} else if (status != ERROR_FILE_NOT_FOUND) {
|
||||
dprintf(1, "nfs41_lookup('%s') failed to find destination "
|
||||
"directory with %d\n", dst_path.path, status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* http://tools.ietf.org/html/rfc5661#section-18.26.3
|
||||
* "Source and target directories MUST reside on the same
|
||||
* file system on the server." */
|
||||
if (state->parent.fh.superblock != dst_dir.fh.superblock) {
|
||||
status = ERROR_NOT_SAME_DEVICE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = is_dst_name_opened(&dst_path, dst_session);
|
||||
if (status) {
|
||||
/* AGLO: 03/21/2011: we can't handle rename of a file with a filename
|
||||
* that is currently opened by this client
|
||||
*/
|
||||
eprintf("handle_nfs41_rename: %s is opened\n", dst_path.path);
|
||||
status = ERROR_FILE_EXISTS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* break any delegations on the source file */
|
||||
nfs41_delegation_return(state->session, &state->file,
|
||||
OPEN_DELEGATE_WRITE, FALSE);
|
||||
|
||||
status = nfs41_rename(state->session,
|
||||
&state->parent, src_name,
|
||||
&dst_dir, &dst_name);
|
||||
if (status) {
|
||||
dprintf(1, "nfs41_rename() failed with error %s.\n",
|
||||
nfs_error_string(status));
|
||||
status = nfs_to_windows_error(status, ERROR_ACCESS_DENIED);
|
||||
} else {
|
||||
/* rename state->path on success */
|
||||
open_state_rename(state, &dst_path);
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int handle_nfs41_set_size(setattr_upcall_args *args)
|
||||
{
|
||||
nfs41_file_info info = { 0 };
|
||||
stateid_arg stateid;
|
||||
/* note: this is called with either FILE_END_OF_FILE_INFO or
|
||||
* FILE_ALLOCATION_INFO, both of which contain a single LARGE_INTEGER */
|
||||
PLARGE_INTEGER size = (PLARGE_INTEGER)args->buf;
|
||||
nfs41_open_state *state = args->state;
|
||||
int status;
|
||||
|
||||
/* break read delegations before SETATTR */
|
||||
nfs41_delegation_return(state->session, &state->file,
|
||||
OPEN_DELEGATE_READ, FALSE);
|
||||
|
||||
nfs41_open_stateid_arg(state, &stateid);
|
||||
|
||||
info.size = size->QuadPart;
|
||||
info.attrmask.count = 1;
|
||||
info.attrmask.arr[0] = FATTR4_WORD0_SIZE;
|
||||
|
||||
dprintf(2, "calling setattr() with size=%lld\n", info.size);
|
||||
status = nfs41_setattr(state->session, &state->file, &stateid, &info);
|
||||
if (status) {
|
||||
dprintf(1, "nfs41_setattr() failed with error %s.\n",
|
||||
nfs_error_string(status));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* update the last offset for LAYOUTCOMMIT */
|
||||
AcquireSRWLockExclusive(&state->lock);
|
||||
state->pnfs_last_offset = info.size ? info.size - 1 : 0;
|
||||
ReleaseSRWLockExclusive(&state->lock);
|
||||
args->ctime = info.change;
|
||||
out:
|
||||
return status = nfs_to_windows_error(status, ERROR_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
static int handle_nfs41_link(setattr_upcall_args *args)
|
||||
{
|
||||
nfs41_open_state *state = args->state;
|
||||
PFILE_LINK_INFORMATION link = (PFILE_LINK_INFORMATION)args->buf;
|
||||
nfs41_session *dst_session;
|
||||
nfs41_abs_path dst_path = { 0 };
|
||||
nfs41_path_fh dst_dir, dst;
|
||||
nfs41_component dst_name;
|
||||
uint32_t depth = 0;
|
||||
nfs41_file_info info = { 0 };
|
||||
int status;
|
||||
|
||||
dst_path.len = (unsigned short)WideCharToMultiByte(CP_UTF8, 0,
|
||||
link->FileName, link->FileNameLength/sizeof(WCHAR),
|
||||
dst_path.path, NFS41_MAX_PATH_LEN, NULL, NULL);
|
||||
if (dst_path.len == 0) {
|
||||
eprintf("WideCharToMultiByte failed to convert destination "
|
||||
"filename %S.\n", link->FileName);
|
||||
status = ERROR_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
path_fh_init(&dst_dir, &dst_path);
|
||||
|
||||
/* the destination path is absolute, so start from the root session */
|
||||
status = nfs41_lookup(args->root, nfs41_root_session(args->root),
|
||||
&dst_path, &dst_dir, &dst, NULL, &dst_session);
|
||||
|
||||
while (status == ERROR_REPARSE) {
|
||||
if (++depth > NFS41_MAX_SYMLINK_DEPTH) {
|
||||
status = ERROR_TOO_MANY_LINKS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* replace the path with the symlink target's */
|
||||
status = nfs41_symlink_target(dst_session, &dst_dir, &dst_path);
|
||||
if (status) {
|
||||
eprintf("nfs41_symlink_target() for %s failed with %d\n",
|
||||
dst_dir.path->path, status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* redo the lookup until it doesn't return REPARSE */
|
||||
status = nfs41_lookup(args->root, dst_session,
|
||||
&dst_path, &dst_dir, &dst, NULL, &dst_session);
|
||||
}
|
||||
|
||||
/* get the components after lookup in case a referral changed its path */
|
||||
last_component(dst_path.path, dst_path.path + dst_path.len, &dst_name);
|
||||
last_component(dst_path.path, dst_name.name, &dst_dir.name);
|
||||
|
||||
if (status == NO_ERROR) {
|
||||
if (!link->ReplaceIfExists) {
|
||||
status = ERROR_FILE_EXISTS;
|
||||
goto out;
|
||||
}
|
||||
} else if (status != ERROR_FILE_NOT_FOUND) {
|
||||
dprintf(1, "nfs41_lookup('%s') failed to find destination "
|
||||
"directory with %d\n", dst_path.path, status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* http://tools.ietf.org/html/rfc5661#section-18.9.3
|
||||
* "The existing file and the target directory must reside within
|
||||
* the same file system on the server." */
|
||||
if (state->file.fh.superblock != dst_dir.fh.superblock) {
|
||||
status = ERROR_NOT_SAME_DEVICE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (status == NO_ERROR) {
|
||||
/* break any delegations and truncate the destination file */
|
||||
nfs41_delegation_return(dst_session, &dst,
|
||||
OPEN_DELEGATE_WRITE, TRUE);
|
||||
|
||||
/* LINK will return NFS4ERR_EXIST if the target file exists,
|
||||
* so we have to remove it ourselves */
|
||||
status = nfs41_remove(state->session,
|
||||
&dst_dir, &dst_name, dst.fh.fileid);
|
||||
if (status) {
|
||||
dprintf(1, "nfs41_remove() failed with error %s.\n",
|
||||
nfs_error_string(status));
|
||||
status = ERROR_FILE_EXISTS;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* break read delegations on the source file */
|
||||
nfs41_delegation_return(state->session, &state->file,
|
||||
OPEN_DELEGATE_READ, FALSE);
|
||||
|
||||
status = nfs41_link(state->session, &state->file, &dst_dir, &dst_name,
|
||||
&info);
|
||||
if (status) {
|
||||
dprintf(1, "nfs41_link() failed with error %s.\n",
|
||||
nfs_error_string(status));
|
||||
status = nfs_to_windows_error(status, ERROR_INVALID_PARAMETER);
|
||||
}
|
||||
args->ctime = info.change;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int handle_setattr(nfs41_upcall *upcall)
|
||||
{
|
||||
setattr_upcall_args *args = &upcall->args.setattr;
|
||||
int status;
|
||||
|
||||
switch (args->set_class) {
|
||||
case FileBasicInformation:
|
||||
status = handle_nfs41_setattr(args);
|
||||
break;
|
||||
case FileDispositionInformation:
|
||||
status = handle_nfs41_remove(args);
|
||||
break;
|
||||
case FileRenameInformation:
|
||||
status = handle_nfs41_rename(args);
|
||||
break;
|
||||
case FileAllocationInformation:
|
||||
case FileEndOfFileInformation:
|
||||
status = handle_nfs41_set_size(args);
|
||||
break;
|
||||
case FileLinkInformation:
|
||||
status = handle_nfs41_link(args);
|
||||
break;
|
||||
default:
|
||||
eprintf("unknown set_file information class %d\n",
|
||||
args->set_class);
|
||||
status = ERROR_NOT_SUPPORTED;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int marshall_setattr(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
|
||||
{
|
||||
setattr_upcall_args *args = &upcall->args.setattr;
|
||||
return safe_write(&buffer, length, &args->ctime, sizeof(args->ctime));
|
||||
}
|
||||
|
||||
|
||||
const nfs41_upcall_op nfs41_op_setattr = {
|
||||
parse_setattr,
|
||||
handle_setattr,
|
||||
marshall_setattr
|
||||
};
|
32
reactos/base/services/nfsd/sources
Normal file
32
reactos/base/services/nfsd/sources
Normal file
|
@ -0,0 +1,32 @@
|
|||
TARGETTYPE=PROGRAM
|
||||
TARGETNAME=nfsd
|
||||
SOURCES=nfs41_daemon.c daemon_debug.c nfs41_ops.c nfs41_compound.c nfs41_xdr.c \
|
||||
nfs41_server.c nfs41_client.c nfs41_superblock.c nfs41_session.c lookup.c \
|
||||
mount.c open.c readwrite.c lock.c readdir.c getattr.c setattr.c upcall.c \
|
||||
nfs41_rpc.c util.c pnfs_layout.c pnfs_device.c pnfs_debug.c pnfs_io.c \
|
||||
name_cache.c namespace.c rbtree.c volume.c callback_server.c callback_xdr.c \
|
||||
service.c symlink.c idmap.c
|
||||
UMTYPE=console
|
||||
USE_LIBCMT=1
|
||||
#USE_MSVCRT=1
|
||||
C_DEFINES=-DSTANDALONE_NFSD #-- use this for non-service nfsd
|
||||
INCLUDES=..\sys;..\dll;..\libtirpc\tirpc
|
||||
# Use the following for "service" version of nfsd
|
||||
#TARGETLIBS=$(SDK_LIB_PATH)\ws2_32.lib $(SDK_LIB_PATH)\iphlpapi.lib \
|
||||
# ..\libtirpc\src\obj$(BUILD_ALT_DIR)\*\libtirpc.lib \
|
||||
# $(SDK_LIB_PATH)\kernel32.lib \
|
||||
# $(SDK_LIB_PATH)\advapi32.lib \
|
||||
# $(SDK_LIB_PATH)\shell32.lib
|
||||
TARGETLIBS=$(SDK_LIB_PATH)\ws2_32.lib $(SDK_LIB_PATH)\iphlpapi.lib \
|
||||
..\libtirpc\src\obj$(BUILD_ALT_DIR)\*\libtirpc.lib
|
||||
|
||||
!IF 0
|
||||
/W3 is default level
|
||||
bump to /Wall, but suppress warnings generated by system includes,
|
||||
as well as the following warnings:
|
||||
4100 - unused function call arguments (we have lots of stubs)
|
||||
4127 - constant conditional (I like to use if(0) or if(1))
|
||||
4220 - varargs matching remaining parameters
|
||||
4204 - nonstandard extension
|
||||
!ENDIF
|
||||
MSC_WARNING_LEVEL=/Wall /wd4668 /wd4619 /wd4820 /wd4255 /wd4100 /wd4127 /wd4711 /wd4220 /wd4204
|
299
reactos/base/services/nfsd/symlink.c
Normal file
299
reactos/base/services/nfsd/symlink.c
Normal file
|
@ -0,0 +1,299 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#include "nfs41_ops.h"
|
||||
#include "upcall.h"
|
||||
#include "util.h"
|
||||
#include "daemon_debug.h"
|
||||
|
||||
|
||||
static int abs_path_link(
|
||||
OUT nfs41_abs_path *path,
|
||||
IN char *path_pos,
|
||||
IN const char *link,
|
||||
IN uint32_t link_len)
|
||||
{
|
||||
nfs41_component name;
|
||||
const char *path_max = path->path + NFS41_MAX_PATH_LEN;
|
||||
const char *link_pos = link;
|
||||
const char *link_end = link + link_len;
|
||||
int status = NO_ERROR;
|
||||
|
||||
/* if link is an absolute path, start path_pos at the beginning */
|
||||
if (is_delimiter(*link))
|
||||
path_pos = path->path;
|
||||
|
||||
/* copy each component of link into the path */
|
||||
while (next_component(link_pos, link_end, &name)) {
|
||||
link_pos = name.name + name.len;
|
||||
|
||||
if (is_delimiter(*path_pos))
|
||||
path_pos++;
|
||||
|
||||
/* handle special components . and .. */
|
||||
if (name.len == 1 && name.name[0] == '.')
|
||||
continue;
|
||||
if (name.len == 2 && name.name[0] == '.' && name.name[1] == '.') {
|
||||
/* back path_pos up by one component */
|
||||
if (!last_component(path->path, path_pos, &name)) {
|
||||
eprintf("symlink with .. that points below server root!\n");
|
||||
status = ERROR_BAD_NETPATH;
|
||||
goto out;
|
||||
}
|
||||
path_pos = (char*)prev_delimiter(name.name, path->path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* copy the component and add a \ */
|
||||
if (FAILED(StringCchCopyNA(path_pos, path_max-path_pos, name.name,
|
||||
name.len))) {
|
||||
status = ERROR_BUFFER_OVERFLOW;
|
||||
goto out;
|
||||
}
|
||||
path_pos += name.len;
|
||||
if (FAILED(StringCchCopyNA(path_pos, path_max-path_pos, "\\", 1))) {
|
||||
status = ERROR_BUFFER_OVERFLOW;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* make sure the path is null terminated */
|
||||
if (path_pos == path_max) {
|
||||
status = ERROR_BUFFER_OVERFLOW;
|
||||
goto out;
|
||||
}
|
||||
*path_pos = '\0';
|
||||
out:
|
||||
path->len = (unsigned short)(path_pos - path->path);
|
||||
return status;
|
||||
}
|
||||
|
||||
int nfs41_symlink_target(
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *file,
|
||||
OUT nfs41_abs_path *target)
|
||||
{
|
||||
char link[NFS41_MAX_PATH_LEN];
|
||||
const nfs41_abs_path *path = file->path;
|
||||
ptrdiff_t path_offset;
|
||||
uint32_t link_len;
|
||||
int status;
|
||||
|
||||
/* read the link */
|
||||
status = nfs41_readlink(session, file, NFS41_MAX_PATH_LEN, link, &link_len);
|
||||
if (status) {
|
||||
eprintf("nfs41_readlink() for %s failed with %s\n", file->path->path,
|
||||
nfs_error_string(status));
|
||||
status = ERROR_PATH_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dprintf(2, "--> nfs41_symlink_target('%s', '%s')\n", path->path, link);
|
||||
|
||||
/* append any components after the symlink */
|
||||
if (FAILED(StringCchCatA(link, NFS41_MAX_PATH_LEN,
|
||||
file->name.name + file->name.len))) {
|
||||
status = ERROR_BUFFER_OVERFLOW;
|
||||
goto out;
|
||||
}
|
||||
link_len = (uint32_t)strlen(link);
|
||||
|
||||
/* overwrite the last component of the path; get the starting offset */
|
||||
path_offset = file->name.name - path->path;
|
||||
|
||||
/* copy the path and update it with the results from link */
|
||||
if (target != path) {
|
||||
target->len = path->len;
|
||||
if (FAILED(StringCchCopyNA(target->path, NFS41_MAX_PATH_LEN,
|
||||
path->path, path->len))) {
|
||||
status = ERROR_BUFFER_OVERFLOW;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
status = abs_path_link(target, target->path + path_offset, link, link_len);
|
||||
if (status) {
|
||||
eprintf("abs_path_link() for path %s with link %s failed with %d\n",
|
||||
target->path, link, status);
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
dprintf(2, "<-- nfs41_symlink_target('%s') returning %d\n",
|
||||
target->path, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
int nfs41_symlink_follow(
|
||||
IN nfs41_root *root,
|
||||
IN nfs41_session *session,
|
||||
IN nfs41_path_fh *symlink,
|
||||
OUT nfs41_file_info *info)
|
||||
{
|
||||
nfs41_abs_path path;
|
||||
nfs41_path_fh file;
|
||||
uint32_t depth = 0;
|
||||
int status = NO_ERROR;
|
||||
|
||||
file.path = &path;
|
||||
InitializeSRWLock(&path.lock);
|
||||
|
||||
dprintf(2, "--> nfs41_symlink_follow('%s')\n", symlink->path->path);
|
||||
|
||||
do {
|
||||
if (++depth > NFS41_MAX_SYMLINK_DEPTH) {
|
||||
status = ERROR_TOO_MANY_LINKS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* construct the target path */
|
||||
status = nfs41_symlink_target(session, symlink, &path);
|
||||
if (status) goto out;
|
||||
|
||||
dprintf(2, "looking up '%s'\n", path.path);
|
||||
|
||||
last_component(path.path, path.path + path.len, &file.name);
|
||||
|
||||
/* get attributes for the target */
|
||||
status = nfs41_lookup(root, session, &path,
|
||||
NULL, &file, info, &session);
|
||||
if (status) goto out;
|
||||
|
||||
symlink = &file;
|
||||
} while (info->type == NF4LNK);
|
||||
out:
|
||||
dprintf(2, "<-- nfs41_symlink_follow() returning %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/* NFS41_SYMLINK */
|
||||
static int parse_symlink(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
||||
{
|
||||
symlink_upcall_args *args = &upcall->args.symlink;
|
||||
int status;
|
||||
|
||||
status = get_name(&buffer, &length, &args->path);
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &args->set, sizeof(BOOLEAN));
|
||||
if (status) goto out;
|
||||
|
||||
if (args->set)
|
||||
status = get_name(&buffer, &length, &args->target_set);
|
||||
else
|
||||
args->target_set = NULL;
|
||||
|
||||
dprintf(1, "parsing NFS41_SYMLINK: path='%s' set=%u target='%s'\n",
|
||||
args->path, args->set, args->target_set);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int handle_symlink(nfs41_upcall *upcall)
|
||||
{
|
||||
symlink_upcall_args *args = &upcall->args.symlink;
|
||||
nfs41_open_state *state = upcall->state_ref;
|
||||
int status = NO_ERROR;
|
||||
|
||||
if (args->set) {
|
||||
nfs41_file_info info, createattrs;
|
||||
|
||||
/* don't send windows slashes to the server */
|
||||
char *p;
|
||||
for (p = args->target_set; *p; p++) if (*p == '\\') *p = '/';
|
||||
|
||||
if (state->file.fh.len) {
|
||||
/* the check in handle_open() didn't catch that we're creating
|
||||
* a symlink, so we have to remove the file it already created */
|
||||
eprintf("handle_symlink: attempting to create a symlink when "
|
||||
"the file=%s was already created on open; sending REMOVE "
|
||||
"first\n", state->file.path->path);
|
||||
status = nfs41_remove(state->session, &state->parent,
|
||||
&state->file.name, state->file.fh.fileid);
|
||||
if (status) {
|
||||
eprintf("nfs41_remove() for symlink=%s failed with %s\n",
|
||||
args->target_set, nfs_error_string(status));
|
||||
status = map_symlink_errors(status);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* create the symlink */
|
||||
createattrs.attrmask.count = 2;
|
||||
createattrs.attrmask.arr[0] = 0;
|
||||
createattrs.attrmask.arr[1] = FATTR4_WORD1_MODE;
|
||||
createattrs.mode = 0777;
|
||||
status = nfs41_create(state->session, NF4LNK, &createattrs,
|
||||
args->target_set, &state->parent, &state->file, &info);
|
||||
if (status) {
|
||||
eprintf("nfs41_create() for symlink=%s failed with %s\n",
|
||||
args->target_set, nfs_error_string(status));
|
||||
status = map_symlink_errors(status);
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
uint32_t len;
|
||||
|
||||
/* read the link */
|
||||
status = nfs41_readlink(state->session, &state->file,
|
||||
NFS41_MAX_PATH_LEN, args->target_get.path, &len);
|
||||
if (status) {
|
||||
eprintf("nfs41_readlink() for filename=%s failed with %s\n",
|
||||
state->file.path->path, nfs_error_string(status));
|
||||
status = map_symlink_errors(status);
|
||||
goto out;
|
||||
}
|
||||
args->target_get.len = (unsigned short)len;
|
||||
dprintf(2, "returning symlink target '%s'\n", args->target_get.path);
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int marshall_symlink(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
|
||||
{
|
||||
symlink_upcall_args *args = &upcall->args.symlink;
|
||||
unsigned short len = (args->target_get.len + 1) * sizeof(WCHAR);
|
||||
int status = NO_ERROR;
|
||||
|
||||
if (args->set)
|
||||
goto out;
|
||||
|
||||
status = safe_write(&buffer, length, &len, sizeof(len));
|
||||
if (status) goto out;
|
||||
|
||||
if (*length <= len || !MultiByteToWideChar(CP_UTF8, 0,
|
||||
args->target_get.path, args->target_get.len,
|
||||
(LPWSTR)buffer, len / sizeof(WCHAR))) {
|
||||
status = ERROR_BUFFER_OVERFLOW;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
const nfs41_upcall_op nfs41_op_symlink = {
|
||||
parse_symlink,
|
||||
handle_symlink,
|
||||
marshall_symlink
|
||||
};
|
765
reactos/base/services/nfsd/tree.h
Normal file
765
reactos/base/services/nfsd/tree.h
Normal file
|
@ -0,0 +1,765 @@
|
|||
/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */
|
||||
/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
|
||||
/* $FreeBSD: src/sys/sys/tree.h,v 1.9.2.1.4.1 2010/06/14 02:09:06 kensmith Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_TREE_H_
|
||||
#define _SYS_TREE_H_
|
||||
|
||||
//#include <sys/cdefs.h>
|
||||
|
||||
/*
|
||||
* This file defines data structures for different types of trees:
|
||||
* splay trees and red-black trees.
|
||||
*
|
||||
* A splay tree is a self-organizing data structure. Every operation
|
||||
* on the tree causes a splay to happen. The splay moves the requested
|
||||
* node to the root of the tree and partly rebalances it.
|
||||
*
|
||||
* This has the benefit that request locality causes faster lookups as
|
||||
* the requested nodes move to the top of the tree. On the other hand,
|
||||
* every lookup causes memory writes.
|
||||
*
|
||||
* The Balance Theorem bounds the total access time for m operations
|
||||
* and n inserts on an initially empty tree as O((m + n)lg n). The
|
||||
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
|
||||
*
|
||||
* A red-black tree is a binary search tree with the node color as an
|
||||
* extra attribute. It fulfills a set of conditions:
|
||||
* - every search path from the root to a leaf consists of the
|
||||
* same number of black nodes,
|
||||
* - each red node (except for the root) has a black parent,
|
||||
* - each leaf node is black.
|
||||
*
|
||||
* Every operation on a red-black tree is bounded as O(lg n).
|
||||
* The maximum height of a red-black tree is 2lg (n+1).
|
||||
*/
|
||||
|
||||
#define SPLAY_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sph_root; /* root of the tree */ \
|
||||
}
|
||||
|
||||
#define SPLAY_INITIALIZER(root) \
|
||||
{ NULL }
|
||||
|
||||
#define SPLAY_INIT(root) do { \
|
||||
(root)->sph_root = NULL; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *spe_left; /* left element */ \
|
||||
struct type *spe_right; /* right element */ \
|
||||
}
|
||||
|
||||
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
|
||||
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
|
||||
#define SPLAY_ROOT(head) (head)->sph_root
|
||||
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
|
||||
|
||||
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
|
||||
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_LINKLEFT(head, tmp, field) do { \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
|
||||
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
|
||||
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
|
||||
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
|
||||
void name##_SPLAY(struct name *, struct type *); \
|
||||
void name##_SPLAY_MINMAX(struct name *, int); \
|
||||
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
|
||||
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
|
||||
\
|
||||
/* Finds the node with the same key as elm */ \
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return(NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) \
|
||||
return (head->sph_root); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
name##_SPLAY(head, elm); \
|
||||
if (SPLAY_RIGHT(elm, field) != NULL) { \
|
||||
elm = SPLAY_RIGHT(elm, field); \
|
||||
while (SPLAY_LEFT(elm, field) != NULL) { \
|
||||
elm = SPLAY_LEFT(elm, field); \
|
||||
} \
|
||||
} else \
|
||||
elm = NULL; \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_MIN_MAX(struct name *head, int val) \
|
||||
{ \
|
||||
name##_SPLAY_MINMAX(head, val); \
|
||||
return (SPLAY_ROOT(head)); \
|
||||
}
|
||||
|
||||
/* Main splay operation.
|
||||
* Moves node close to the key of elm to top
|
||||
*/
|
||||
#define SPLAY_GENERATE(name, type, field, cmp) \
|
||||
struct type * \
|
||||
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) { \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
|
||||
} else { \
|
||||
int __comp; \
|
||||
name##_SPLAY(head, elm); \
|
||||
__comp = (cmp)(elm, (head)->sph_root); \
|
||||
if(__comp < 0) { \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
|
||||
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_LEFT((head)->sph_root, field) = NULL; \
|
||||
} else if (__comp > 0) { \
|
||||
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
|
||||
SPLAY_LEFT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
|
||||
} else \
|
||||
return ((head)->sph_root); \
|
||||
} \
|
||||
(head)->sph_root = (elm); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
struct type * \
|
||||
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *__tmp; \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return (NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) { \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
|
||||
} else { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
|
||||
name##_SPLAY(head, elm); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
void \
|
||||
name##_SPLAY(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
int __comp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) < 0){ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} else if (__comp > 0) { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) > 0){ \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
} \
|
||||
\
|
||||
/* Splay with either the minimum or the maximum element \
|
||||
* Used to find minimum or maximum element in tree. \
|
||||
*/ \
|
||||
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while (1) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp < 0){ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} else if (__comp > 0) { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp > 0) { \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
}
|
||||
|
||||
#define SPLAY_NEGINF -1
|
||||
#define SPLAY_INF 1
|
||||
|
||||
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
|
||||
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
|
||||
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
|
||||
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
|
||||
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
|
||||
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
|
||||
|
||||
#define SPLAY_FOREACH(x, name, head) \
|
||||
for ((x) = SPLAY_MIN(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = SPLAY_NEXT(name, head, x))
|
||||
|
||||
/* Macros that define a red-black tree */
|
||||
#define RB_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *rbh_root; /* root of the tree */ \
|
||||
}
|
||||
|
||||
#define RB_INITIALIZER(root) \
|
||||
{ NULL }
|
||||
|
||||
#define RB_INIT(root) do { \
|
||||
(root)->rbh_root = NULL; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define RB_BLACK 0
|
||||
#define RB_RED 1
|
||||
#define RB_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *rbe_left; /* left element */ \
|
||||
struct type *rbe_right; /* right element */ \
|
||||
struct type *rbe_parent; /* parent element */ \
|
||||
int rbe_color; /* node color */ \
|
||||
}
|
||||
|
||||
#define RB_LEFT(elm, field) (elm)->field.rbe_left
|
||||
#define RB_RIGHT(elm, field) (elm)->field.rbe_right
|
||||
#define RB_PARENT(elm, field) (elm)->field.rbe_parent
|
||||
#define RB_COLOR(elm, field) (elm)->field.rbe_color
|
||||
#define RB_ROOT(head) (head)->rbh_root
|
||||
#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
|
||||
|
||||
#define RB_SET(elm, parent, field) do { \
|
||||
RB_PARENT(elm, field) = parent; \
|
||||
RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
|
||||
RB_COLOR(elm, field) = RB_RED; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define RB_SET_BLACKRED(black, red, field) do { \
|
||||
RB_COLOR(black, field) = RB_BLACK; \
|
||||
RB_COLOR(red, field) = RB_RED; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#ifndef RB_AUGMENT
|
||||
#define RB_AUGMENT(x) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
|
||||
(tmp) = RB_RIGHT(elm, field); \
|
||||
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \
|
||||
RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
|
||||
} \
|
||||
RB_AUGMENT(elm); \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
|
||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
} else \
|
||||
(head)->rbh_root = (tmp); \
|
||||
RB_LEFT(tmp, field) = (elm); \
|
||||
RB_PARENT(elm, field) = (tmp); \
|
||||
RB_AUGMENT(tmp); \
|
||||
if ((RB_PARENT(tmp, field))) \
|
||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
|
||||
(tmp) = RB_LEFT(elm, field); \
|
||||
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \
|
||||
RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
|
||||
} \
|
||||
RB_AUGMENT(elm); \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
|
||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
} else \
|
||||
(head)->rbh_root = (tmp); \
|
||||
RB_RIGHT(tmp, field) = (elm); \
|
||||
RB_PARENT(elm, field) = (tmp); \
|
||||
RB_AUGMENT(tmp); \
|
||||
if ((RB_PARENT(tmp, field))) \
|
||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
#define RB_PROTOTYPE(name, type, field, cmp) \
|
||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
|
||||
#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
|
||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
|
||||
#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
|
||||
attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \
|
||||
attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
|
||||
attr struct type *name##_RB_REMOVE(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_INSERT(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_FIND(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_NFIND(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_NEXT(struct type *); \
|
||||
attr struct type *name##_RB_PREV(struct type *); \
|
||||
attr struct type *name##_RB_MINMAX(struct name *, int); \
|
||||
\
|
||||
|
||||
/* Main rb operation.
|
||||
* Moves node close to the key of elm to top
|
||||
*/
|
||||
#define RB_GENERATE(name, type, field, cmp) \
|
||||
RB_GENERATE_INTERNAL(name, type, field, cmp,)
|
||||
#define RB_GENERATE_STATIC(name, type, field, cmp) \
|
||||
RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
|
||||
#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
|
||||
attr void \
|
||||
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *parent, *gparent, *tmp; \
|
||||
while ((parent = RB_PARENT(elm, field)) != NULL && \
|
||||
RB_COLOR(parent, field) == RB_RED) { \
|
||||
gparent = RB_PARENT(parent, field); \
|
||||
if (parent == RB_LEFT(gparent, field)) { \
|
||||
tmp = RB_RIGHT(gparent, field); \
|
||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
||||
RB_SET_BLACKRED(parent, gparent, field);\
|
||||
elm = gparent; \
|
||||
continue; \
|
||||
} \
|
||||
if (RB_RIGHT(parent, field) == elm) { \
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
||||
tmp = parent; \
|
||||
parent = elm; \
|
||||
elm = tmp; \
|
||||
} \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
RB_ROTATE_RIGHT(head, gparent, tmp, field); \
|
||||
} else { \
|
||||
tmp = RB_LEFT(gparent, field); \
|
||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
||||
RB_SET_BLACKRED(parent, gparent, field);\
|
||||
elm = gparent; \
|
||||
continue; \
|
||||
} \
|
||||
if (RB_LEFT(parent, field) == elm) { \
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
||||
tmp = parent; \
|
||||
parent = elm; \
|
||||
elm = tmp; \
|
||||
} \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
RB_ROTATE_LEFT(head, gparent, tmp, field); \
|
||||
} \
|
||||
} \
|
||||
RB_COLOR(head->rbh_root, field) = RB_BLACK; \
|
||||
} \
|
||||
\
|
||||
attr void \
|
||||
name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp; \
|
||||
while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
|
||||
elm != RB_ROOT(head)) { \
|
||||
if (RB_LEFT(parent, field) == elm) { \
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
if (RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_SET_BLACKRED(tmp, parent, field); \
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
} \
|
||||
if ((RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
|
||||
(RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
elm = parent; \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
} else { \
|
||||
if (RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
|
||||
struct type *oleft; \
|
||||
if ((oleft = RB_LEFT(tmp, field)) \
|
||||
!= NULL) \
|
||||
RB_COLOR(oleft, field) = RB_BLACK;\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
RB_ROTATE_RIGHT(head, tmp, oleft, field);\
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
} \
|
||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
|
||||
RB_COLOR(parent, field) = RB_BLACK; \
|
||||
if (RB_RIGHT(tmp, field)) \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
||||
elm = RB_ROOT(head); \
|
||||
break; \
|
||||
} \
|
||||
} else { \
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
if (RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_SET_BLACKRED(tmp, parent, field); \
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
} \
|
||||
if ((RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
|
||||
(RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
elm = parent; \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
} else { \
|
||||
if (RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
|
||||
struct type *oright; \
|
||||
if ((oright = RB_RIGHT(tmp, field)) \
|
||||
!= NULL) \
|
||||
RB_COLOR(oright, field) = RB_BLACK;\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
RB_ROTATE_LEFT(head, tmp, oright, field);\
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
} \
|
||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
|
||||
RB_COLOR(parent, field) = RB_BLACK; \
|
||||
if (RB_LEFT(tmp, field)) \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
||||
elm = RB_ROOT(head); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (elm) \
|
||||
RB_COLOR(elm, field) = RB_BLACK; \
|
||||
} \
|
||||
\
|
||||
attr struct type * \
|
||||
name##_RB_REMOVE(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *child, *parent, *old = elm; \
|
||||
int color; \
|
||||
if (RB_LEFT(elm, field) == NULL) \
|
||||
child = RB_RIGHT(elm, field); \
|
||||
else if (RB_RIGHT(elm, field) == NULL) \
|
||||
child = RB_LEFT(elm, field); \
|
||||
else { \
|
||||
struct type *left; \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
while ((left = RB_LEFT(elm, field)) != NULL) \
|
||||
elm = left; \
|
||||
child = RB_RIGHT(elm, field); \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
color = RB_COLOR(elm, field); \
|
||||
if (child) \
|
||||
RB_PARENT(child, field) = parent; \
|
||||
if (parent) { \
|
||||
if (RB_LEFT(parent, field) == elm) \
|
||||
RB_LEFT(parent, field) = child; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = child; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = child; \
|
||||
if (RB_PARENT(elm, field) == old) \
|
||||
parent = elm; \
|
||||
(elm)->field = (old)->field; \
|
||||
if (RB_PARENT(old, field)) { \
|
||||
if (RB_LEFT(RB_PARENT(old, field), field) == old)\
|
||||
RB_LEFT(RB_PARENT(old, field), field) = elm;\
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(old, field), field) = elm;\
|
||||
RB_AUGMENT(RB_PARENT(old, field)); \
|
||||
} else \
|
||||
RB_ROOT(head) = elm; \
|
||||
RB_PARENT(RB_LEFT(old, field), field) = elm; \
|
||||
if (RB_RIGHT(old, field)) \
|
||||
RB_PARENT(RB_RIGHT(old, field), field) = elm; \
|
||||
if (parent) { \
|
||||
left = parent; \
|
||||
do { \
|
||||
RB_AUGMENT(left); \
|
||||
} while ((left = RB_PARENT(left, field)) != NULL); \
|
||||
} \
|
||||
goto color; \
|
||||
} \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
color = RB_COLOR(elm, field); \
|
||||
if (child) \
|
||||
RB_PARENT(child, field) = parent; \
|
||||
if (parent) { \
|
||||
if (RB_LEFT(parent, field) == elm) \
|
||||
RB_LEFT(parent, field) = child; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = child; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = child; \
|
||||
color: \
|
||||
if (color == RB_BLACK) \
|
||||
name##_RB_REMOVE_COLOR(head, parent, child); \
|
||||
return (old); \
|
||||
} \
|
||||
\
|
||||
/* Inserts a node into the RB tree */ \
|
||||
attr struct type * \
|
||||
name##_RB_INSERT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp; \
|
||||
struct type *parent = NULL; \
|
||||
int comp = 0; \
|
||||
tmp = RB_ROOT(head); \
|
||||
while (tmp) { \
|
||||
parent = tmp; \
|
||||
comp = (cmp)(elm, parent); \
|
||||
if (comp < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
RB_SET(elm, parent, field); \
|
||||
if (parent != NULL) { \
|
||||
if (comp < 0) \
|
||||
RB_LEFT(parent, field) = elm; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = elm; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = elm; \
|
||||
name##_RB_INSERT_COLOR(head, elm); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
/* Finds the node with the same key as elm */ \
|
||||
attr struct type * \
|
||||
name##_RB_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
int comp; \
|
||||
while (tmp) { \
|
||||
comp = cmp(elm, tmp); \
|
||||
if (comp < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
/* Finds the first node greater than or equal to the search key */ \
|
||||
attr struct type * \
|
||||
name##_RB_NFIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
struct type *res = NULL; \
|
||||
int comp; \
|
||||
while (tmp) { \
|
||||
comp = cmp(elm, tmp); \
|
||||
if (comp < 0) { \
|
||||
res = tmp; \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
} \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
return (res); \
|
||||
} \
|
||||
\
|
||||
/* ARGSUSED */ \
|
||||
attr struct type * \
|
||||
name##_RB_NEXT(struct type *elm) \
|
||||
{ \
|
||||
if (RB_RIGHT(elm, field)) { \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
while (RB_LEFT(elm, field)) \
|
||||
elm = RB_LEFT(elm, field); \
|
||||
} else { \
|
||||
if (RB_PARENT(elm, field) && \
|
||||
(elm == RB_LEFT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
else { \
|
||||
while (RB_PARENT(elm, field) && \
|
||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
|
||||
elm = RB_PARENT(elm, field); \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
} \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
/* ARGSUSED */ \
|
||||
attr struct type * \
|
||||
name##_RB_PREV(struct type *elm) \
|
||||
{ \
|
||||
if (RB_LEFT(elm, field)) { \
|
||||
elm = RB_LEFT(elm, field); \
|
||||
while (RB_RIGHT(elm, field)) \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
} else { \
|
||||
if (RB_PARENT(elm, field) && \
|
||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
else { \
|
||||
while (RB_PARENT(elm, field) && \
|
||||
(elm == RB_LEFT(RB_PARENT(elm, field), field)))\
|
||||
elm = RB_PARENT(elm, field); \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
} \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
attr struct type * \
|
||||
name##_RB_MINMAX(struct name *head, int val) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
struct type *parent = NULL; \
|
||||
while (tmp) { \
|
||||
parent = tmp; \
|
||||
if (val < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
} \
|
||||
return (parent); \
|
||||
}
|
||||
|
||||
#define RB_NEGINF -1
|
||||
#define RB_INF 1
|
||||
|
||||
#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
|
||||
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
|
||||
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
|
||||
#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
|
||||
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
|
||||
#define RB_PREV(name, x, y) name##_RB_PREV(y)
|
||||
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
|
||||
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
|
||||
|
||||
#define RB_FOREACH(x, name, head) \
|
||||
for ((x) = RB_MIN(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = name##_RB_NEXT(x))
|
||||
|
||||
#define RB_FOREACH_FROM(x, name, y) \
|
||||
for ((x) = (y); \
|
||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_SAFE(x, name, head, y) \
|
||||
for ((x) = RB_MIN(name, head); \
|
||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_REVERSE(x, name, head) \
|
||||
for ((x) = RB_MAX(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = name##_RB_PREV(x))
|
||||
|
||||
#define RB_FOREACH_REVERSE_FROM(x, name, y) \
|
||||
for ((x) = (y); \
|
||||
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
|
||||
for ((x) = RB_MAX(name, head); \
|
||||
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#endif /* _SYS_TREE_H_ */
|
212
reactos/base/services/nfsd/upcall.c
Normal file
212
reactos/base/services/nfsd/upcall.c
Normal file
|
@ -0,0 +1,212 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "upcall.h"
|
||||
#include "daemon_debug.h"
|
||||
#include "util.h"
|
||||
|
||||
extern const nfs41_upcall_op nfs41_op_mount;
|
||||
extern const nfs41_upcall_op nfs41_op_unmount;
|
||||
extern const nfs41_upcall_op nfs41_op_open;
|
||||
extern const nfs41_upcall_op nfs41_op_close;
|
||||
extern const nfs41_upcall_op nfs41_op_read;
|
||||
extern const nfs41_upcall_op nfs41_op_write;
|
||||
extern const nfs41_upcall_op nfs41_op_lock;
|
||||
extern const nfs41_upcall_op nfs41_op_unlock;
|
||||
extern const nfs41_upcall_op nfs41_op_readdir;
|
||||
extern const nfs41_upcall_op nfs41_op_getattr;
|
||||
extern const nfs41_upcall_op nfs41_op_setattr;
|
||||
extern const nfs41_upcall_op nfs41_op_getexattr;
|
||||
extern const nfs41_upcall_op nfs41_op_setexattr;
|
||||
extern const nfs41_upcall_op nfs41_op_symlink;
|
||||
extern const nfs41_upcall_op nfs41_op_volume;
|
||||
extern const nfs41_upcall_op nfs41_op_getacl;
|
||||
extern const nfs41_upcall_op nfs41_op_setacl;
|
||||
|
||||
static const nfs41_upcall_op *g_upcall_op_table[] = {
|
||||
&nfs41_op_mount,
|
||||
&nfs41_op_unmount,
|
||||
&nfs41_op_open,
|
||||
&nfs41_op_close,
|
||||
&nfs41_op_read,
|
||||
&nfs41_op_write,
|
||||
&nfs41_op_lock,
|
||||
&nfs41_op_unlock,
|
||||
&nfs41_op_readdir,
|
||||
&nfs41_op_getattr,
|
||||
&nfs41_op_setattr,
|
||||
&nfs41_op_getexattr,
|
||||
&nfs41_op_setexattr,
|
||||
&nfs41_op_symlink,
|
||||
&nfs41_op_volume,
|
||||
&nfs41_op_getacl,
|
||||
&nfs41_op_setacl,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
#ifdef __REACTOS__
|
||||
static const uint32_t g_upcall_op_table_size = (sizeof(g_upcall_op_table) / sizeof(g_upcall_op_table[0]));
|
||||
#else
|
||||
static const uint32_t g_upcall_op_table_size = ARRAYSIZE(g_upcall_op_table);
|
||||
#endif
|
||||
|
||||
int upcall_parse(
|
||||
IN unsigned char *buffer,
|
||||
IN uint32_t length,
|
||||
OUT nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
const nfs41_upcall_op *op;
|
||||
DWORD version;
|
||||
|
||||
ZeroMemory(upcall, sizeof(nfs41_upcall));
|
||||
if (!length) {
|
||||
eprintf("empty upcall\n");
|
||||
upcall->status = status = 102;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dprintf(2, "received %d bytes upcall data: processing upcall\n", length);
|
||||
print_hexbuf(4, (unsigned char *)"upcall buffer: ", buffer, length);
|
||||
|
||||
/* parse common elements */
|
||||
status = safe_read(&buffer, &length, &version, sizeof(uint32_t));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &upcall->xid, sizeof(uint64_t));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &upcall->opcode, sizeof(uint32_t));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &upcall->root_ref, sizeof(HANDLE));
|
||||
if (status) goto out;
|
||||
status = safe_read(&buffer, &length, &upcall->state_ref, sizeof(HANDLE));
|
||||
if (status) goto out;
|
||||
|
||||
dprintf(2, "time=%ld version=%d xid=%d opcode=%s session=0x%x open_state=0x%x\n",
|
||||
time(NULL), version, upcall->xid, opcode2string(upcall->opcode), upcall->root_ref,
|
||||
upcall->state_ref);
|
||||
if (version != NFS41D_VERSION) {
|
||||
eprintf("received version %d expecting version %d\n", version, NFS41D_VERSION);
|
||||
upcall->status = status = NFSD_VERSION_MISMATCH;
|
||||
goto out;
|
||||
}
|
||||
if (upcall->opcode >= g_upcall_op_table_size) {
|
||||
status = ERROR_NOT_SUPPORTED;
|
||||
eprintf("unrecognized upcall opcode %d!\n", upcall->opcode);
|
||||
goto out;
|
||||
}
|
||||
if (upcall->root_ref != INVALID_HANDLE_VALUE)
|
||||
nfs41_root_ref(upcall->root_ref);
|
||||
if (upcall->state_ref != INVALID_HANDLE_VALUE)
|
||||
nfs41_open_state_ref(upcall->state_ref);
|
||||
|
||||
/* parse the operation's arguments */
|
||||
op = g_upcall_op_table[upcall->opcode];
|
||||
if (op && op->parse) {
|
||||
status = op->parse(buffer, length, upcall);
|
||||
if (status) {
|
||||
eprintf("parsing of upcall '%s' failed with %d.\n",
|
||||
opcode2string(upcall->opcode), status);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
int upcall_handle(
|
||||
IN nfs41_upcall *upcall)
|
||||
{
|
||||
int status = NO_ERROR;
|
||||
const nfs41_upcall_op *op;
|
||||
|
||||
op = g_upcall_op_table[upcall->opcode];
|
||||
if (op == NULL || op->handle == NULL) {
|
||||
status = ERROR_NOT_SUPPORTED;
|
||||
eprintf("upcall '%s' missing handle function!\n",
|
||||
opcode2string(upcall->opcode));
|
||||
goto out;
|
||||
}
|
||||
|
||||
upcall->status = op->handle(upcall);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
#pragma warning (disable : 4706) /* assignment within conditional expression */
|
||||
void upcall_marshall(
|
||||
IN nfs41_upcall *upcall,
|
||||
OUT unsigned char *buffer,
|
||||
IN uint32_t length,
|
||||
OUT uint32_t *length_out)
|
||||
{
|
||||
const nfs41_upcall_op *op;
|
||||
unsigned char *orig_buf = buffer;
|
||||
const uint32_t total = length, orig_len = length;
|
||||
|
||||
/* marshall common elements */
|
||||
write_downcall:
|
||||
length = orig_len;
|
||||
buffer = orig_buf;
|
||||
safe_write(&buffer, &length, &upcall->xid, sizeof(upcall->xid));
|
||||
safe_write(&buffer, &length, &upcall->opcode, sizeof(upcall->opcode));
|
||||
safe_write(&buffer, &length, &upcall->status, sizeof(upcall->status));
|
||||
safe_write(&buffer, &length, &upcall->last_error, sizeof(upcall->last_error));
|
||||
|
||||
if (upcall->status)
|
||||
goto out;
|
||||
|
||||
/* marshall the operation's results */
|
||||
op = g_upcall_op_table[upcall->opcode];
|
||||
if (op && op->marshall) {
|
||||
if ((upcall->status = op->marshall(buffer, &length, upcall)))
|
||||
goto write_downcall;
|
||||
}
|
||||
out:
|
||||
*length_out = total - length;
|
||||
}
|
||||
|
||||
void upcall_cancel(
|
||||
IN nfs41_upcall *upcall)
|
||||
{
|
||||
const nfs41_upcall_op *op = g_upcall_op_table[upcall->opcode];
|
||||
if (op && op->cancel)
|
||||
op->cancel(upcall);
|
||||
}
|
||||
|
||||
void upcall_cleanup(
|
||||
IN nfs41_upcall *upcall)
|
||||
{
|
||||
const nfs41_upcall_op *op = g_upcall_op_table[upcall->opcode];
|
||||
if (op && op->cleanup && upcall->status != NFSD_VERSION_MISMATCH)
|
||||
op->cleanup(upcall);
|
||||
|
||||
if (upcall->state_ref && upcall->state_ref != INVALID_HANDLE_VALUE) {
|
||||
nfs41_open_state_deref(upcall->state_ref);
|
||||
upcall->state_ref = NULL;
|
||||
}
|
||||
if (upcall->root_ref && upcall->root_ref != INVALID_HANDLE_VALUE) {
|
||||
nfs41_root_deref(upcall->root_ref);
|
||||
upcall->root_ref = NULL;
|
||||
}
|
||||
}
|
248
reactos/base/services/nfsd/upcall.h
Normal file
248
reactos/base/services/nfsd/upcall.h
Normal file
|
@ -0,0 +1,248 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#ifndef __NFS41_DAEMON_UPCALL_H__
|
||||
#define __NFS41_DAEMON_UPCALL_H__
|
||||
|
||||
#include "nfs41_ops.h"
|
||||
#include "from_kernel.h"
|
||||
|
||||
#define NFSD_VERSION_MISMATCH 116
|
||||
|
||||
/* structures for upcall arguments */
|
||||
typedef struct __mount_upcall_args {
|
||||
const char *hostname;
|
||||
const char *path;
|
||||
DWORD sec_flavor;
|
||||
DWORD rsize;
|
||||
DWORD wsize;
|
||||
DWORD lease_time;
|
||||
FILE_FS_ATTRIBUTE_INFORMATION FsAttrs;
|
||||
} mount_upcall_args;
|
||||
|
||||
typedef struct __open_upcall_args {
|
||||
nfs41_abs_path symlink;
|
||||
FILE_BASIC_INFO basic_info;
|
||||
FILE_STANDARD_INFO std_info;
|
||||
const char *path;
|
||||
ULONG access_mask;
|
||||
ULONG access_mode;
|
||||
ULONG file_attrs;
|
||||
ULONG disposition;
|
||||
ULONG create_opts;
|
||||
LONG open_owner_id;
|
||||
DWORD mode;
|
||||
ULONGLONG changeattr;
|
||||
HANDLE srv_open;
|
||||
DWORD deleg_type;
|
||||
PFILE_FULL_EA_INFORMATION ea;
|
||||
BOOLEAN created;
|
||||
BOOLEAN symlink_embedded;
|
||||
} open_upcall_args;
|
||||
|
||||
typedef struct __close_upcall_args {
|
||||
HANDLE srv_open;
|
||||
const char *path;
|
||||
BOOLEAN remove;
|
||||
BOOLEAN renamed;
|
||||
} close_upcall_args;
|
||||
|
||||
typedef struct __readwrite_upcall_args {
|
||||
unsigned char *buffer;
|
||||
ULONGLONG offset;
|
||||
ULONG len;
|
||||
ULONG out_len;
|
||||
ULONGLONG ctime;
|
||||
} readwrite_upcall_args;
|
||||
|
||||
typedef struct __lock_upcall_args {
|
||||
uint64_t offset;
|
||||
uint64_t length;
|
||||
BOOLEAN exclusive;
|
||||
BOOLEAN blocking;
|
||||
BOOLEAN acquired;
|
||||
} lock_upcall_args;
|
||||
|
||||
typedef struct __unlock_upcall_args {
|
||||
uint32_t count;
|
||||
unsigned char *buf;
|
||||
uint32_t buf_len;
|
||||
} unlock_upcall_args;
|
||||
|
||||
typedef struct __getattr_upcall_args {
|
||||
FILE_BASIC_INFO basic_info;
|
||||
FILE_STANDARD_INFO std_info;
|
||||
FILE_ATTRIBUTE_TAG_INFO tag_info;
|
||||
FILE_INTERNAL_INFORMATION intr_info;
|
||||
FILE_NETWORK_OPEN_INFORMATION network_info;
|
||||
int query_class;
|
||||
int buf_len;
|
||||
int query_reply_len;
|
||||
ULONGLONG ctime;
|
||||
} getattr_upcall_args;
|
||||
|
||||
typedef struct __setattr_upcall_args {
|
||||
const char *path;
|
||||
nfs41_root *root;
|
||||
nfs41_open_state *state;
|
||||
unsigned char *buf;
|
||||
uint32_t buf_len;
|
||||
int set_class;
|
||||
ULONGLONG ctime;
|
||||
} setattr_upcall_args;
|
||||
|
||||
typedef struct __getexattr_upcall_args {
|
||||
const char *path;
|
||||
unsigned char *buf;
|
||||
uint32_t buf_len;
|
||||
ULONG eaindex;
|
||||
unsigned char *ealist;
|
||||
uint32_t ealist_len;
|
||||
uint32_t overflow;
|
||||
BOOLEAN single;
|
||||
BOOLEAN restart;
|
||||
} getexattr_upcall_args;
|
||||
|
||||
|
||||
typedef struct __setexattr_upcall_args {
|
||||
const char *path;
|
||||
unsigned char *buf;
|
||||
uint32_t buf_len;
|
||||
uint32_t mode;
|
||||
ULONGLONG ctime;
|
||||
} setexattr_upcall_args;
|
||||
|
||||
typedef struct __readdir_upcall_args {
|
||||
const char *filter;
|
||||
nfs41_root *root;
|
||||
nfs41_open_state *state;
|
||||
int buf_len;
|
||||
int query_class;
|
||||
int query_reply_len;
|
||||
BOOLEAN initial;
|
||||
BOOLEAN restart;
|
||||
BOOLEAN single;
|
||||
unsigned char *kbuf;
|
||||
} readdir_upcall_args;
|
||||
|
||||
typedef struct __symlink_upcall_args {
|
||||
nfs41_abs_path target_get;
|
||||
char *target_set;
|
||||
const char *path;
|
||||
BOOLEAN set;
|
||||
} symlink_upcall_args;
|
||||
|
||||
typedef struct __volume_upcall_args {
|
||||
FS_INFORMATION_CLASS query;
|
||||
int len;
|
||||
union {
|
||||
FILE_FS_SIZE_INFORMATION size;
|
||||
FILE_FS_FULL_SIZE_INFORMATION fullsize;
|
||||
FILE_FS_ATTRIBUTE_INFORMATION attribute;
|
||||
} info;
|
||||
} volume_upcall_args;
|
||||
|
||||
typedef struct __getacl_upcall_args {
|
||||
SECURITY_INFORMATION query;
|
||||
PSECURITY_DESCRIPTOR sec_desc;
|
||||
DWORD sec_desc_len;
|
||||
} getacl_upcall_args;
|
||||
|
||||
typedef struct __setacl_upcall_args {
|
||||
SECURITY_INFORMATION query;
|
||||
PSECURITY_DESCRIPTOR sec_desc;
|
||||
ULONGLONG ctime;
|
||||
} setacl_upcall_args;
|
||||
|
||||
typedef union __upcall_args {
|
||||
mount_upcall_args mount;
|
||||
open_upcall_args open;
|
||||
close_upcall_args close;
|
||||
readwrite_upcall_args rw;
|
||||
lock_upcall_args lock;
|
||||
unlock_upcall_args unlock;
|
||||
getattr_upcall_args getattr;
|
||||
getexattr_upcall_args getexattr;
|
||||
setattr_upcall_args setattr;
|
||||
setexattr_upcall_args setexattr;
|
||||
readdir_upcall_args readdir;
|
||||
symlink_upcall_args symlink;
|
||||
volume_upcall_args volume;
|
||||
getacl_upcall_args getacl;
|
||||
setacl_upcall_args setacl;
|
||||
} upcall_args;
|
||||
|
||||
typedef struct __nfs41_upcall {
|
||||
uint64_t xid;
|
||||
uint32_t opcode;
|
||||
uint32_t status;
|
||||
uint32_t last_error;
|
||||
upcall_args args;
|
||||
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
|
||||
/* store referenced pointers with the upcall for
|
||||
* automatic dereferencing on upcall_cleanup();
|
||||
* see upcall_root_ref() and upcall_open_state_ref() */
|
||||
nfs41_root *root_ref;
|
||||
nfs41_open_state *state_ref;
|
||||
} nfs41_upcall;
|
||||
|
||||
|
||||
/* upcall operation interface */
|
||||
typedef int (*upcall_parse_proc)(unsigned char*, uint32_t, nfs41_upcall*);
|
||||
typedef int (*upcall_handle_proc)(nfs41_upcall*);
|
||||
typedef int (*upcall_marshall_proc)(unsigned char*, uint32_t*, nfs41_upcall*);
|
||||
typedef void (*upcall_cancel_proc)(nfs41_upcall*);
|
||||
typedef void (*upcall_cleanup_proc)(nfs41_upcall*);
|
||||
|
||||
typedef struct __nfs41_upcall_op {
|
||||
upcall_parse_proc parse;
|
||||
upcall_handle_proc handle;
|
||||
upcall_marshall_proc marshall;
|
||||
upcall_cancel_proc cancel;
|
||||
upcall_cleanup_proc cleanup;
|
||||
} nfs41_upcall_op;
|
||||
|
||||
|
||||
/* upcall.c */
|
||||
int upcall_parse(
|
||||
IN unsigned char *buffer,
|
||||
IN uint32_t length,
|
||||
OUT nfs41_upcall *upcall);
|
||||
|
||||
int upcall_handle(
|
||||
IN nfs41_upcall *upcall);
|
||||
|
||||
void upcall_marshall(
|
||||
IN nfs41_upcall *upcall,
|
||||
OUT unsigned char *buffer,
|
||||
IN uint32_t length,
|
||||
OUT uint32_t *length_out);
|
||||
|
||||
void upcall_cancel(
|
||||
IN nfs41_upcall *upcall);
|
||||
|
||||
void upcall_cleanup(
|
||||
IN nfs41_upcall *upcall);
|
||||
|
||||
#endif /* !__NFS41_DAEMON_UPCALL_H__ */
|
448
reactos/base/services/nfsd/util.c
Normal file
448
reactos/base/services/nfsd/util.c
Normal file
|
@ -0,0 +1,448 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <wincrypt.h> /* for Crypt*() functions */
|
||||
|
||||
#include "daemon_debug.h"
|
||||
#include "util.h"
|
||||
#include "nfs41_ops.h"
|
||||
|
||||
|
||||
int safe_read(unsigned char **pos, uint32_t *remaining, void *dest, uint32_t dest_len)
|
||||
{
|
||||
if (*remaining < dest_len)
|
||||
return ERROR_BUFFER_OVERFLOW;
|
||||
|
||||
CopyMemory(dest, *pos, dest_len);
|
||||
*pos += dest_len;
|
||||
*remaining -= dest_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int safe_write(unsigned char **pos, uint32_t *remaining, void *src, uint32_t src_len)
|
||||
{
|
||||
if (*remaining < src_len)
|
||||
return ERROR_BUFFER_OVERFLOW;
|
||||
|
||||
CopyMemory(*pos, src, src_len);
|
||||
*pos += src_len;
|
||||
*remaining -= src_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_name(unsigned char **pos, uint32_t *remaining, const char **out_name)
|
||||
{
|
||||
int status;
|
||||
USHORT len;
|
||||
|
||||
status = safe_read(pos, remaining, &len, sizeof(USHORT));
|
||||
if (status) goto out;
|
||||
if (*remaining < len) {
|
||||
status = ERROR_BUFFER_OVERFLOW;
|
||||
goto out;
|
||||
}
|
||||
*out_name = (const char*)*pos;
|
||||
*pos += len;
|
||||
*remaining -= len;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
const char* strip_path(
|
||||
IN const char *path,
|
||||
OUT uint32_t *len_out)
|
||||
{
|
||||
const char *name = strrchr(path, '\\');
|
||||
name = name ? name + 1 : path;
|
||||
if (len_out)
|
||||
*len_out = (uint32_t)strlen(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
uint32_t max_read_size(
|
||||
IN const nfs41_session *session,
|
||||
IN const nfs41_fh *fh)
|
||||
{
|
||||
const uint32_t maxresponse = session->fore_chan_attrs.ca_maxresponsesize;
|
||||
return (uint32_t)min(fh->superblock->maxread, maxresponse - READ_OVERHEAD);
|
||||
}
|
||||
|
||||
uint32_t max_write_size(
|
||||
IN const nfs41_session *session,
|
||||
IN const nfs41_fh *fh)
|
||||
{
|
||||
const uint32_t maxrequest = session->fore_chan_attrs.ca_maxrequestsize;
|
||||
return (uint32_t)min(fh->superblock->maxwrite, maxrequest - WRITE_OVERHEAD);
|
||||
}
|
||||
|
||||
bool_t verify_write(
|
||||
IN nfs41_write_verf *verf,
|
||||
IN OUT enum stable_how4 *stable)
|
||||
{
|
||||
if (verf->committed != UNSTABLE4) {
|
||||
*stable = verf->committed;
|
||||
dprintf(3, "verify_write: committed to stable storage\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (*stable != UNSTABLE4) {
|
||||
memcpy(verf->expected, verf->verf, NFS4_VERIFIER_SIZE);
|
||||
*stable = UNSTABLE4;
|
||||
dprintf(3, "verify_write: first unstable write, saving verifier\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (memcmp(verf->expected, verf->verf, NFS4_VERIFIER_SIZE) == 0) {
|
||||
dprintf(3, "verify_write: verifier matches expected\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
dprintf(2, "verify_write: verifier changed; writes have been lost!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool_t verify_commit(
|
||||
IN nfs41_write_verf *verf)
|
||||
{
|
||||
if (memcmp(verf->expected, verf->verf, NFS4_VERIFIER_SIZE) == 0) {
|
||||
dprintf(3, "verify_commit: verifier matches expected\n");
|
||||
return 1;
|
||||
}
|
||||
dprintf(2, "verify_commit: verifier changed; writes have been lost!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ULONG nfs_file_info_to_attributes(
|
||||
IN const nfs41_file_info *info)
|
||||
{
|
||||
ULONG attrs = 0;
|
||||
if (info->type == NF4DIR)
|
||||
attrs |= FILE_ATTRIBUTE_DIRECTORY;
|
||||
else if (info->type == NF4LNK) {
|
||||
attrs |= FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
if (info->symlink_dir)
|
||||
attrs |= FILE_ATTRIBUTE_DIRECTORY;
|
||||
} else if (info->type != NF4REG)
|
||||
dprintf(1, "unhandled file type %d, defaulting to NF4REG\n",
|
||||
info->type);
|
||||
|
||||
if (info->mode == 0444) /* XXX: 0444 for READONLY */
|
||||
attrs |= FILE_ATTRIBUTE_READONLY;
|
||||
|
||||
if (info->hidden) attrs |= FILE_ATTRIBUTE_HIDDEN;
|
||||
if (info->system) attrs |= FILE_ATTRIBUTE_SYSTEM;
|
||||
if (info->archive) attrs |= FILE_ATTRIBUTE_ARCHIVE;
|
||||
|
||||
// FILE_ATTRIBUTE_NORMAL attribute is only set if no other attributes are present.
|
||||
// all other override this value.
|
||||
return attrs ? attrs : FILE_ATTRIBUTE_NORMAL;
|
||||
}
|
||||
|
||||
void nfs_to_basic_info(
|
||||
IN const nfs41_file_info *info,
|
||||
OUT PFILE_BASIC_INFO basic_out)
|
||||
{
|
||||
nfs_time_to_file_time(&info->time_create, &basic_out->CreationTime);
|
||||
nfs_time_to_file_time(&info->time_access, &basic_out->LastAccessTime);
|
||||
nfs_time_to_file_time(&info->time_modify, &basic_out->LastWriteTime);
|
||||
/* XXX: was using 'change' attr, but that wasn't giving a time */
|
||||
nfs_time_to_file_time(&info->time_modify, &basic_out->ChangeTime);
|
||||
basic_out->FileAttributes = nfs_file_info_to_attributes(info);
|
||||
}
|
||||
|
||||
void nfs_to_standard_info(
|
||||
IN const nfs41_file_info *info,
|
||||
OUT PFILE_STANDARD_INFO std_out)
|
||||
{
|
||||
const ULONG FileAttributes = nfs_file_info_to_attributes(info);
|
||||
|
||||
std_out->AllocationSize.QuadPart =
|
||||
std_out->EndOfFile.QuadPart = (LONGLONG)info->size;
|
||||
std_out->NumberOfLinks = info->numlinks;
|
||||
std_out->DeletePending = FALSE;
|
||||
std_out->Directory = FileAttributes & FILE_ATTRIBUTE_DIRECTORY ?
|
||||
TRUE : FALSE;
|
||||
}
|
||||
|
||||
void nfs_to_network_openinfo(
|
||||
IN const nfs41_file_info *info,
|
||||
OUT PFILE_NETWORK_OPEN_INFORMATION net_out)
|
||||
{
|
||||
|
||||
nfs_time_to_file_time(&info->time_create, &net_out->CreationTime);
|
||||
nfs_time_to_file_time(&info->time_access, &net_out->LastAccessTime);
|
||||
nfs_time_to_file_time(&info->time_modify, &net_out->LastWriteTime);
|
||||
/* XXX: was using 'change' attr, but that wasn't giving a time */
|
||||
nfs_time_to_file_time(&info->time_modify, &net_out->ChangeTime);
|
||||
net_out->AllocationSize.QuadPart =
|
||||
net_out->EndOfFile.QuadPart = (LONGLONG)info->size;
|
||||
net_out->FileAttributes = nfs_file_info_to_attributes(info);
|
||||
}
|
||||
|
||||
void get_file_time(
|
||||
OUT PLARGE_INTEGER file_time)
|
||||
{
|
||||
GetSystemTimeAsFileTime((LPFILETIME)file_time);
|
||||
}
|
||||
|
||||
void get_nfs_time(
|
||||
OUT nfstime4 *nfs_time)
|
||||
{
|
||||
LARGE_INTEGER file_time;
|
||||
get_file_time(&file_time);
|
||||
file_time_to_nfs_time(&file_time, nfs_time);
|
||||
}
|
||||
|
||||
bool_t multi_addr_find(
|
||||
IN const multi_addr4 *addrs,
|
||||
IN const netaddr4 *addr,
|
||||
OUT OPTIONAL uint32_t *index_out)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < addrs->count; i++) {
|
||||
const netaddr4 *saddr = &addrs->arr[i];
|
||||
if (!strncmp(saddr->netid, addr->netid, NFS41_NETWORK_ID_LEN) &&
|
||||
!strncmp(saddr->uaddr, addr->uaddr, NFS41_UNIVERSAL_ADDR_LEN)) {
|
||||
if (index_out) *index_out = i;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nfs_to_windows_error(int status, int default_error)
|
||||
{
|
||||
/* make sure this is actually an nfs error */
|
||||
if (status < 0 || (status > 70 && status < 10001) || status > 10087) {
|
||||
eprintf("nfs_to_windows_error called with non-nfs "
|
||||
"error code %d; returning the error as is\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case NFS4_OK: return NO_ERROR;
|
||||
case NFS4ERR_PERM: return ERROR_ACCESS_DENIED;
|
||||
case NFS4ERR_NOENT: return ERROR_FILE_NOT_FOUND;
|
||||
case NFS4ERR_IO: return ERROR_NET_WRITE_FAULT;
|
||||
case NFS4ERR_ACCESS: return ERROR_ACCESS_DENIED;
|
||||
case NFS4ERR_EXIST: return ERROR_FILE_EXISTS;
|
||||
case NFS4ERR_XDEV: return ERROR_NOT_SAME_DEVICE;
|
||||
case NFS4ERR_INVAL: return ERROR_INVALID_PARAMETER;
|
||||
case NFS4ERR_FBIG: return ERROR_FILE_TOO_LARGE;
|
||||
case NFS4ERR_NOSPC: return ERROR_DISK_FULL;
|
||||
case NFS4ERR_ROFS: return ERROR_NETWORK_ACCESS_DENIED;
|
||||
case NFS4ERR_MLINK: return ERROR_TOO_MANY_LINKS;
|
||||
case NFS4ERR_NAMETOOLONG: return ERROR_FILENAME_EXCED_RANGE;
|
||||
case NFS4ERR_STALE: return ERROR_NETNAME_DELETED;
|
||||
case NFS4ERR_NOTEMPTY: return ERROR_NOT_EMPTY;
|
||||
case NFS4ERR_DENIED: return ERROR_LOCK_FAILED;
|
||||
case NFS4ERR_TOOSMALL: return ERROR_BUFFER_OVERFLOW;
|
||||
case NFS4ERR_LOCKED: return ERROR_LOCK_VIOLATION;
|
||||
case NFS4ERR_SHARE_DENIED: return ERROR_SHARING_VIOLATION;
|
||||
case NFS4ERR_LOCK_RANGE: return ERROR_NOT_LOCKED;
|
||||
case NFS4ERR_ATTRNOTSUPP: return ERROR_NOT_SUPPORTED;
|
||||
case NFS4ERR_OPENMODE: return ERROR_ACCESS_DENIED;
|
||||
case NFS4ERR_LOCK_NOTSUPP: return ERROR_ATOMIC_LOCKS_NOT_SUPPORTED;
|
||||
|
||||
case NFS4ERR_BADCHAR:
|
||||
case NFS4ERR_BADNAME: return ERROR_INVALID_NAME;
|
||||
|
||||
case NFS4ERR_NOTDIR:
|
||||
case NFS4ERR_ISDIR:
|
||||
case NFS4ERR_SYMLINK:
|
||||
case NFS4ERR_WRONG_TYPE: return ERROR_INVALID_PARAMETER;
|
||||
|
||||
case NFS4ERR_EXPIRED:
|
||||
case NFS4ERR_NOFILEHANDLE:
|
||||
case NFS4ERR_OLD_STATEID:
|
||||
case NFS4ERR_BAD_STATEID:
|
||||
case NFS4ERR_ADMIN_REVOKED: return ERROR_FILE_INVALID;
|
||||
|
||||
case NFS4ERR_WRONGSEC: return ERROR_ACCESS_DENIED;
|
||||
|
||||
default:
|
||||
dprintf(1, "nfs error %s not mapped to windows error; "
|
||||
"returning default error %d\n",
|
||||
nfs_error_string(status), default_error);
|
||||
return default_error;
|
||||
}
|
||||
}
|
||||
|
||||
int map_symlink_errors(int status)
|
||||
{
|
||||
switch (status) {
|
||||
case NFS4ERR_BADCHAR:
|
||||
case NFS4ERR_BADNAME: return ERROR_INVALID_REPARSE_DATA;
|
||||
case NFS4ERR_WRONG_TYPE: return ERROR_NOT_A_REPARSE_POINT;
|
||||
case NFS4ERR_ACCESS: return ERROR_ACCESS_DENIED;
|
||||
case NFS4ERR_NOTEMPTY: return ERROR_NOT_EMPTY;
|
||||
default: return nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
|
||||
}
|
||||
}
|
||||
|
||||
bool_t next_component(
|
||||
IN const char *path,
|
||||
IN const char *path_end,
|
||||
OUT nfs41_component *component)
|
||||
{
|
||||
const char *component_end;
|
||||
component->name = next_non_delimiter(path, path_end);
|
||||
component_end = next_delimiter(component->name, path_end);
|
||||
component->len = (unsigned short)(component_end - component->name);
|
||||
return component->len > 0;
|
||||
}
|
||||
|
||||
bool_t last_component(
|
||||
IN const char *path,
|
||||
IN const char *path_end,
|
||||
OUT nfs41_component *component)
|
||||
{
|
||||
const char *component_end = prev_delimiter(path_end, path);
|
||||
component->name = prev_non_delimiter(component_end, path);
|
||||
component->name = prev_delimiter(component->name, path);
|
||||
component->name = next_non_delimiter(component->name, component_end);
|
||||
component->len = (unsigned short)(component_end - component->name);
|
||||
return component->len > 0;
|
||||
}
|
||||
|
||||
bool_t is_last_component(
|
||||
IN const char *path,
|
||||
IN const char *path_end)
|
||||
{
|
||||
path = next_delimiter(path, path_end);
|
||||
return next_non_delimiter(path, path_end) == path_end;
|
||||
}
|
||||
|
||||
void abs_path_copy(
|
||||
OUT nfs41_abs_path *dst,
|
||||
IN const nfs41_abs_path *src)
|
||||
{
|
||||
dst->len = src->len;
|
||||
StringCchCopyNA(dst->path, NFS41_MAX_PATH_LEN, src->path, dst->len);
|
||||
}
|
||||
|
||||
void path_fh_init(
|
||||
OUT nfs41_path_fh *file,
|
||||
IN nfs41_abs_path *path)
|
||||
{
|
||||
file->path = path;
|
||||
last_component(path->path, path->path + path->len, &file->name);
|
||||
}
|
||||
|
||||
void fh_copy(
|
||||
OUT nfs41_fh *dst,
|
||||
IN const nfs41_fh *src)
|
||||
{
|
||||
dst->fileid = src->fileid;
|
||||
dst->superblock = src->superblock;
|
||||
dst->len = src->len;
|
||||
memcpy(dst->fh, src->fh, dst->len);
|
||||
}
|
||||
|
||||
void path_fh_copy(
|
||||
OUT nfs41_path_fh *dst,
|
||||
IN const nfs41_path_fh *src)
|
||||
{
|
||||
dst->path = src->path;
|
||||
if (dst->path) {
|
||||
const size_t name_start = src->name.name - src->path->path;
|
||||
dst->name.name = dst->path->path + name_start;
|
||||
dst->name.len = src->name.len;
|
||||
} else {
|
||||
dst->name.name = NULL;
|
||||
dst->name.len = 0;
|
||||
}
|
||||
fh_copy(&dst->fh, &src->fh);
|
||||
}
|
||||
|
||||
int create_silly_rename(
|
||||
IN nfs41_abs_path *path,
|
||||
IN const nfs41_fh *fh,
|
||||
OUT nfs41_component *silly)
|
||||
{
|
||||
HCRYPTPROV context;
|
||||
HCRYPTHASH hash;
|
||||
PBYTE buffer;
|
||||
DWORD length;
|
||||
const char *end = path->path + NFS41_MAX_PATH_LEN;
|
||||
const unsigned short extra_len = 2 + 16; //md5 is 16
|
||||
char name[NFS41_MAX_COMPONENT_LEN+1];
|
||||
unsigned char fhmd5[17] = { 0 };
|
||||
char *tmp;
|
||||
int status = NO_ERROR, i;
|
||||
|
||||
if (path->len + extra_len >= NFS41_MAX_PATH_LEN) {
|
||||
status = ERROR_FILENAME_EXCED_RANGE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* set up the md5 hash generator */
|
||||
if (!CryptAcquireContext(&context, NULL, NULL,
|
||||
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
|
||||
status = GetLastError();
|
||||
eprintf("CryptAcquireContext() failed with %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
if (!CryptCreateHash(context, CALG_MD5, 0, 0, &hash)) {
|
||||
status = GetLastError();
|
||||
eprintf("CryptCreateHash() failed with %d\n", status);
|
||||
goto out_context;
|
||||
}
|
||||
|
||||
if (!CryptHashData(hash, (const BYTE*)fh->fh, (DWORD)fh->len, 0)) {
|
||||
status = GetLastError();
|
||||
eprintf("CryptHashData() failed with %d\n", status);
|
||||
goto out_hash;
|
||||
}
|
||||
|
||||
/* extract the hash buffer */
|
||||
buffer = (PBYTE)fhmd5;
|
||||
length = 16;
|
||||
if (!CryptGetHashParam(hash, HP_HASHVAL, buffer, &length, 0)) {
|
||||
status = GetLastError();
|
||||
eprintf("CryptGetHashParam(val) failed with %d\n", status);
|
||||
goto out_hash;
|
||||
}
|
||||
|
||||
last_component(path->path, path->path + path->len, silly);
|
||||
StringCchCopyNA(name, NFS41_MAX_COMPONENT_LEN+1, silly->name, silly->len);
|
||||
|
||||
tmp = (char*)silly->name;
|
||||
StringCchPrintf(tmp, end - tmp, ".%s.", name);
|
||||
tmp += silly->len + 2;
|
||||
|
||||
for (i = 0; i < 16; i++, tmp++)
|
||||
StringCchPrintf(tmp, end - tmp, "%x", fhmd5[i]);
|
||||
|
||||
path->len = path->len + extra_len;
|
||||
silly->len = silly->len + extra_len;
|
||||
|
||||
out_hash:
|
||||
CryptDestroyHash(hash);
|
||||
out_context:
|
||||
CryptReleaseContext(context, 0);
|
||||
out:
|
||||
return status;
|
||||
}
|
288
reactos/base/services/nfsd/util.h
Normal file
288
reactos/base/services/nfsd/util.h
Normal file
|
@ -0,0 +1,288 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#ifndef __NFS41_DAEMON_UTIL_H__
|
||||
#define __NFS41_DAEMON_UTIL_H__
|
||||
|
||||
#include "nfs41_types.h"
|
||||
#include "from_kernel.h"
|
||||
|
||||
extern DWORD NFS41D_VERSION;
|
||||
struct __nfs41_session;
|
||||
struct __nfs41_write_verf;
|
||||
enum stable_how4;
|
||||
|
||||
int safe_read(unsigned char **pos, uint32_t *remaining, void *dest, uint32_t dest_len);
|
||||
int safe_write(unsigned char **pos, uint32_t *remaining, void *dest, uint32_t dest_len);
|
||||
int get_name(unsigned char **pos, uint32_t *remaining, const char **out_name);
|
||||
|
||||
const char* strip_path(
|
||||
IN const char *path,
|
||||
OUT uint32_t *len_out OPTIONAL);
|
||||
|
||||
uint32_t max_read_size(
|
||||
IN const struct __nfs41_session *session,
|
||||
IN const nfs41_fh *fh);
|
||||
uint32_t max_write_size(
|
||||
IN const struct __nfs41_session *session,
|
||||
IN const nfs41_fh *fh);
|
||||
|
||||
bool_t verify_write(
|
||||
IN nfs41_write_verf *verf,
|
||||
IN OUT enum stable_how4 *stable);
|
||||
bool_t verify_commit(
|
||||
IN nfs41_write_verf *verf);
|
||||
|
||||
/* bitmap4 */
|
||||
static __inline bool_t bitmap_isset(
|
||||
IN const bitmap4 *mask,
|
||||
IN uint32_t word,
|
||||
IN uint32_t flag)
|
||||
{
|
||||
return mask->count > word && mask->arr[word] & flag;
|
||||
}
|
||||
static __inline void bitmap_set(
|
||||
IN bitmap4 *mask,
|
||||
IN uint32_t word,
|
||||
IN uint32_t flag)
|
||||
{
|
||||
if (mask->count > word)
|
||||
mask->arr[word] |= flag;
|
||||
else {
|
||||
mask->count = word + 1;
|
||||
mask->arr[word] = flag;
|
||||
}
|
||||
}
|
||||
static __inline void bitmap_unset(
|
||||
IN bitmap4 *mask,
|
||||
IN uint32_t word,
|
||||
IN uint32_t flag)
|
||||
{
|
||||
if (mask->count > word) {
|
||||
mask->arr[word] &= ~flag;
|
||||
while (mask->count && mask->arr[mask->count-1] == 0)
|
||||
mask->count--;
|
||||
}
|
||||
}
|
||||
static __inline void bitmap_intersect(
|
||||
IN bitmap4 *dst,
|
||||
IN const bitmap4 *src)
|
||||
{
|
||||
uint32_t i, count = 0;
|
||||
for (i = 0; i < 3; i++) {
|
||||
dst->arr[i] &= src->arr[i];
|
||||
if (dst->arr[i])
|
||||
count = i+1;
|
||||
}
|
||||
dst->count = min(dst->count, count);
|
||||
}
|
||||
|
||||
ULONG nfs_file_info_to_attributes(
|
||||
IN const nfs41_file_info *info);
|
||||
void nfs_to_basic_info(
|
||||
IN const nfs41_file_info *info,
|
||||
OUT PFILE_BASIC_INFO basic_out);
|
||||
void nfs_to_standard_info(
|
||||
IN const nfs41_file_info *info,
|
||||
OUT PFILE_STANDARD_INFO std_out);
|
||||
void nfs_to_network_openinfo(
|
||||
IN const nfs41_file_info *info,
|
||||
OUT PFILE_NETWORK_OPEN_INFORMATION std_out);
|
||||
|
||||
/* http://msdn.microsoft.com/en-us/library/ms724290%28VS.85%29.aspx:
|
||||
* A file time is a 64-bit value that represents the number of
|
||||
* 100-nanosecond intervals that have elapsed since 12:00 A.M.
|
||||
* January 1, 1601 Coordinated Universal Time (UTC). */
|
||||
#define FILETIME_EPOCH 116444736000000000LL
|
||||
|
||||
static __inline void file_time_to_nfs_time(
|
||||
IN const PLARGE_INTEGER file_time,
|
||||
OUT nfstime4 *nfs_time)
|
||||
{
|
||||
LONGLONG diff = file_time->QuadPart - FILETIME_EPOCH;
|
||||
nfs_time->seconds = diff / 10000000;
|
||||
nfs_time->nseconds = (uint32_t)((diff % 10000000)*100);
|
||||
}
|
||||
|
||||
static __inline void nfs_time_to_file_time(
|
||||
IN const nfstime4 *nfs_time,
|
||||
OUT PLARGE_INTEGER file_time)
|
||||
{
|
||||
file_time->QuadPart = FILETIME_EPOCH +
|
||||
nfs_time->seconds * 10000000 +
|
||||
nfs_time->nseconds / 100;
|
||||
}
|
||||
|
||||
void get_file_time(
|
||||
OUT PLARGE_INTEGER file_time);
|
||||
void get_nfs_time(
|
||||
OUT nfstime4 *nfs_time);
|
||||
|
||||
static __inline void nfstime_normalize(
|
||||
IN OUT nfstime4 *nfstime)
|
||||
{
|
||||
/* return time in normalized form (0 <= nsec < 1s) */
|
||||
while ((int32_t)nfstime->nseconds < 0) {
|
||||
nfstime->nseconds += 1000000000;
|
||||
nfstime->seconds--;
|
||||
}
|
||||
}
|
||||
static __inline void nfstime_diff(
|
||||
IN const nfstime4 *lhs,
|
||||
IN const nfstime4 *rhs,
|
||||
OUT nfstime4 *result)
|
||||
{
|
||||
/* result = lhs - rhs */
|
||||
result->seconds = lhs->seconds - rhs->seconds;
|
||||
result->nseconds = lhs->nseconds - rhs->nseconds;
|
||||
nfstime_normalize(result);
|
||||
}
|
||||
static __inline void nfstime_abs(
|
||||
IN const nfstime4 *nt,
|
||||
OUT nfstime4 *result)
|
||||
{
|
||||
if (nt->seconds < 0) {
|
||||
const nfstime4 zero = { 0, 0 };
|
||||
nfstime_diff(&zero, nt, result); /* result = 0 - nt */
|
||||
} else if (result != nt)
|
||||
memcpy(result, nt, sizeof(nfstime4));
|
||||
}
|
||||
|
||||
|
||||
int create_silly_rename(
|
||||
IN nfs41_abs_path *path,
|
||||
IN const nfs41_fh *fh,
|
||||
OUT nfs41_component *silly);
|
||||
|
||||
bool_t multi_addr_find(
|
||||
IN const multi_addr4 *addrs,
|
||||
IN const netaddr4 *addr,
|
||||
OUT OPTIONAL uint32_t *index_out);
|
||||
|
||||
/* nfs_to_windows_error
|
||||
* Returns a windows ERROR_ code corresponding to the given NFS4ERR_ status.
|
||||
* If the status is outside the range of valid NFS4ERR_ values, it is returned
|
||||
* unchanged. Otherwise, if the status does not match a value in the mapping,
|
||||
* a debug warning is generated and the default_error value is returned.
|
||||
*/
|
||||
int nfs_to_windows_error(int status, int default_error);
|
||||
|
||||
int map_symlink_errors(int status);
|
||||
|
||||
#ifndef __REACTOS__
|
||||
__inline uint32_t align8(uint32_t offset) {
|
||||
#else
|
||||
FORCEINLINE uint32_t align8(uint32_t offset) {
|
||||
#endif
|
||||
return 8 + ((offset - 1) & ~7);
|
||||
}
|
||||
#ifndef __REACTOS__
|
||||
__inline uint32_t align4(uint32_t offset) {
|
||||
#else
|
||||
FORCEINLINE uint32_t align4(uint32_t offset) {
|
||||
#endif
|
||||
return 4 + ((offset - 1) & ~3);
|
||||
}
|
||||
|
||||
/* path parsing */
|
||||
#ifndef __REACTOS__
|
||||
__inline int is_delimiter(char c) {
|
||||
#else
|
||||
FORCEINLINE int is_delimiter(char c) {
|
||||
#endif
|
||||
return c == '\\' || c == '/' || c == '\0';
|
||||
}
|
||||
#ifndef __REACTOS__
|
||||
__inline const char* next_delimiter(const char *pos, const char *end) {
|
||||
#else
|
||||
FORCEINLINE const char* next_delimiter(const char *pos, const char *end) {
|
||||
#endif
|
||||
while (pos < end && !is_delimiter(*pos))
|
||||
pos++;
|
||||
return pos;
|
||||
}
|
||||
#ifndef __REACTOS__
|
||||
__inline const char* prev_delimiter(const char *pos, const char *start) {
|
||||
#else
|
||||
FORCEINLINE const char* prev_delimiter(const char *pos, const char *start) {
|
||||
#endif
|
||||
while (pos > start && !is_delimiter(*pos))
|
||||
pos--;
|
||||
return pos;
|
||||
}
|
||||
#ifndef __REACTOS__
|
||||
__inline const char* next_non_delimiter(const char *pos, const char *end) {
|
||||
#else
|
||||
FORCEINLINE const char* next_non_delimiter(const char *pos, const char *end) {
|
||||
#endif
|
||||
while (pos < end && is_delimiter(*pos))
|
||||
pos++;
|
||||
return pos;
|
||||
}
|
||||
#ifndef __REACTOS__
|
||||
__inline const char* prev_non_delimiter(const char *pos, const char *start) {
|
||||
#else
|
||||
FORCEINLINE const char* prev_non_delimiter(const char *pos, const char *start) {
|
||||
#endif
|
||||
while (pos > start && is_delimiter(*pos))
|
||||
pos--;
|
||||
return pos;
|
||||
}
|
||||
|
||||
bool_t next_component(
|
||||
IN const char *path,
|
||||
IN const char *path_end,
|
||||
OUT nfs41_component *component);
|
||||
|
||||
bool_t last_component(
|
||||
IN const char *path,
|
||||
IN const char *path_end,
|
||||
OUT nfs41_component *component);
|
||||
|
||||
bool_t is_last_component(
|
||||
IN const char *path,
|
||||
IN const char *path_end);
|
||||
|
||||
void abs_path_copy(
|
||||
OUT nfs41_abs_path *dst,
|
||||
IN const nfs41_abs_path *src);
|
||||
|
||||
void path_fh_init(
|
||||
OUT nfs41_path_fh *file,
|
||||
IN nfs41_abs_path *path);
|
||||
|
||||
void fh_copy(
|
||||
OUT nfs41_fh *dst,
|
||||
IN const nfs41_fh *src);
|
||||
|
||||
void path_fh_copy(
|
||||
OUT nfs41_path_fh *dst,
|
||||
IN const nfs41_path_fh *src);
|
||||
|
||||
#ifndef __REACTOS__
|
||||
__inline int valid_handle(HANDLE handle) {
|
||||
#else
|
||||
FORCEINLINE int valid_handle(HANDLE handle) {
|
||||
#endif
|
||||
return handle != INVALID_HANDLE_VALUE && handle != 0;
|
||||
}
|
||||
|
||||
#endif /* !__NFS41_DAEMON_UTIL_H__ */
|
176
reactos/base/services/nfsd/volume.c
Normal file
176
reactos/base/services/nfsd/volume.c
Normal file
|
@ -0,0 +1,176 @@
|
|||
/* NFSv4.1 client for Windows
|
||||
* Copyright © 2012 The Regents of the University of Michigan
|
||||
*
|
||||
* Olga Kornievskaia <aglo@umich.edu>
|
||||
* Casey Bodley <cbodley@umich.edu>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* without any warranty; without even the implied warranty of merchantability
|
||||
* or fitness for a particular purpose. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "nfs41_ops.h"
|
||||
#include "from_kernel.h"
|
||||
#include "upcall.h"
|
||||
#include "util.h"
|
||||
#include "daemon_debug.h"
|
||||
|
||||
|
||||
/* windows volume queries want size in 'units', so we have to
|
||||
* convert the nfs space_* attributes from bytes to units */
|
||||
#define SECTORS_PER_UNIT 8
|
||||
#define BYTES_PER_SECTOR 512
|
||||
#define BYTES_PER_UNIT (SECTORS_PER_UNIT * BYTES_PER_SECTOR)
|
||||
|
||||
#define TO_UNITS(bytes) (bytes / BYTES_PER_UNIT)
|
||||
|
||||
#define VOLUME_CACHE_EXPIRATION 20
|
||||
|
||||
|
||||
/* NFS41_VOLUME_QUERY */
|
||||
static int parse_volume(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
volume_upcall_args *args = &upcall->args.volume;
|
||||
|
||||
status = safe_read(&buffer, &length, &args->query, sizeof(FS_INFORMATION_CLASS));
|
||||
if (status) goto out;
|
||||
|
||||
dprintf(1, "parsing NFS41_VOLUME_QUERY: query=%d\n", args->query);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int get_volume_size_info(
|
||||
IN nfs41_open_state *state,
|
||||
IN const char *query,
|
||||
OUT OPTIONAL PLONGLONG total_out,
|
||||
OUT OPTIONAL PLONGLONG user_out,
|
||||
OUT OPTIONAL PLONGLONG avail_out)
|
||||
{
|
||||
nfs41_file_info info = { 0 };
|
||||
nfs41_superblock *superblock = state->file.fh.superblock;
|
||||
int status = ERROR_NOT_FOUND;
|
||||
|
||||
AcquireSRWLockShared(&superblock->lock);
|
||||
/* check superblock for cached attributes */
|
||||
if (time(NULL) <= superblock->cache_expiration) {
|
||||
info.space_total = superblock->space_total;
|
||||
info.space_avail = superblock->space_avail;
|
||||
info.space_free = superblock->space_free;
|
||||
status = NO_ERROR;
|
||||
|
||||
dprintf(2, "%s cached: %llu user, %llu free of %llu total\n",
|
||||
query, info.space_avail, info.space_free, info.space_total);
|
||||
}
|
||||
ReleaseSRWLockShared(&superblock->lock);
|
||||
|
||||
if (status) {
|
||||
bitmap4 attr_request = { 2, { 0, FATTR4_WORD1_SPACE_AVAIL |
|
||||
FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL } };
|
||||
|
||||
/* query the space_ attributes of the filesystem */
|
||||
status = nfs41_getattr(state->session, &state->file,
|
||||
&attr_request, &info);
|
||||
if (status) {
|
||||
eprintf("nfs41_getattr() failed with %s\n",
|
||||
nfs_error_string(status));
|
||||
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
|
||||
goto out;
|
||||
}
|
||||
|
||||
AcquireSRWLockExclusive(&superblock->lock);
|
||||
superblock->space_total = info.space_total;
|
||||
superblock->space_avail = info.space_avail;
|
||||
superblock->space_free = info.space_free;
|
||||
superblock->cache_expiration = time(NULL) + VOLUME_CACHE_EXPIRATION;
|
||||
ReleaseSRWLockExclusive(&superblock->lock);
|
||||
|
||||
dprintf(2, "%s: %llu user, %llu free of %llu total\n",
|
||||
query, info.space_avail, info.space_free, info.space_total);
|
||||
}
|
||||
|
||||
if (total_out) *total_out = TO_UNITS(info.space_total);
|
||||
if (user_out) *user_out = TO_UNITS(info.space_avail);
|
||||
if (avail_out) *avail_out = TO_UNITS(info.space_free);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int handle_volume(nfs41_upcall *upcall)
|
||||
{
|
||||
volume_upcall_args *args = &upcall->args.volume;
|
||||
int status = NO_ERROR;
|
||||
|
||||
switch (args->query) {
|
||||
case FileFsSizeInformation:
|
||||
args->len = sizeof(args->info.size);
|
||||
args->info.size.SectorsPerAllocationUnit = SECTORS_PER_UNIT;
|
||||
args->info.size.BytesPerSector = BYTES_PER_SECTOR;
|
||||
|
||||
status = get_volume_size_info(upcall->state_ref,
|
||||
"FileFsSizeInformation",
|
||||
&args->info.size.TotalAllocationUnits.QuadPart,
|
||||
&args->info.size.AvailableAllocationUnits.QuadPart,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
case FileFsFullSizeInformation:
|
||||
args->len = sizeof(args->info.fullsize);
|
||||
args->info.fullsize.SectorsPerAllocationUnit = SECTORS_PER_UNIT;
|
||||
args->info.fullsize.BytesPerSector = BYTES_PER_SECTOR;
|
||||
|
||||
status = get_volume_size_info(upcall->state_ref,
|
||||
"FileFsFullSizeInformation",
|
||||
&args->info.fullsize.TotalAllocationUnits.QuadPart,
|
||||
&args->info.fullsize.CallerAvailableAllocationUnits.QuadPart,
|
||||
&args->info.fullsize.ActualAvailableAllocationUnits.QuadPart);
|
||||
break;
|
||||
|
||||
case FileFsAttributeInformation:
|
||||
args->len = sizeof(args->info.attribute);
|
||||
nfs41_superblock_fs_attributes(upcall->state_ref->file.fh.superblock,
|
||||
&args->info.attribute);
|
||||
break;
|
||||
|
||||
default:
|
||||
eprintf("unhandled fs query class %d\n", args->query);
|
||||
status = ERROR_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int marshall_volume(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
|
||||
{
|
||||
int status;
|
||||
volume_upcall_args *args = &upcall->args.volume;
|
||||
|
||||
status = safe_write(&buffer, length, &args->len, sizeof(args->len));
|
||||
if (status) goto out;
|
||||
status = safe_write(&buffer, length, &args->info, args->len);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
const nfs41_upcall_op nfs41_op_volume = {
|
||||
parse_volume,
|
||||
handle_volume,
|
||||
marshall_volume
|
||||
};
|
Loading…
Reference in a new issue