Import the nfsd deamon from the nfs41 project.

CORE-8204

svn path=/trunk/; revision=75114
This commit is contained in:
Pierre Schweitzer 2017-06-19 07:57:04 +00:00
parent de51bd8b04
commit f445db2c5c
62 changed files with 30531 additions and 0 deletions

View file

@ -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)

View 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)

View 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
};

View 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;
}

View 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, &notify->mask)
&& xdr_bytes(xdr, &notify->list, &notify->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(&notify_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(&notify_xdr, change);
if (!result) { CBX_ERR("notify_deviceid.change"); goto out; }
break;
case NOTIFY_DEVICEID4_DELETE:
result = cb_notify_deviceid_delete(&notify_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;
}

View 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");
}

View 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

View 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;
}

View 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 */

View 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
};

View 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

View 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
};

File diff suppressed because it is too large Load diff

View 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 */

View 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 */

View 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
};

View 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 = &target;
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;
}

View 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

View 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
};

View 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"

File diff suppressed because it is too large Load diff

View 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__ */

View 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;
}

View 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 - - -

View 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__ */

View 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__ */

View 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;
}

View 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;
}

View 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__ */

View 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__ */

View 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;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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;
}

View 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;
}

View 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);
}

View 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);
}

View 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__ */

File diff suppressed because it is too large Load diff

View 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__ */

View 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>

View 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
};

View 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__ */

View 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);
}

View 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;
}

View 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;
}

File diff suppressed because it is too large Load diff

View 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
};

View 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
};

View 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 = &empty;
} 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;
}

View 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 */

View 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

View 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

View 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
};

View 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

View 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
};

View 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_ */

View 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;
}
}

View 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__ */

View 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;
}

View 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__ */

View 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
};