Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449/* 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;}