1 /* NFSv4.1 client for Windows 2 * Copyright � 2012 The Regents of the University of Michigan 3 * 4 * Olga Kornievskaia <aglo@umich.edu> 5 * Casey Bodley <cbodley@umich.edu> 6 * 7 * This library is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU Lesser General Public License as published by 9 * the Free Software Foundation; either version 2.1 of the License, or (at 10 * your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, but 13 * without any warranty; without even the implied warranty of merchantability 14 * or fitness for a particular purpose. See the GNU Lesser General Public 15 * License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public License 18 * along with this library; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 */ 21 22 #include <windows.h> 23 #include <strsafe.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <wincrypt.h> /* for Crypt*() functions */ 27 28 #include "daemon_debug.h" 29 #include "util.h" 30 #include "nfs41_ops.h" 31 32 33 int safe_read(unsigned char **pos, uint32_t *remaining, void *dest, uint32_t dest_len) 34 { 35 if (*remaining < dest_len) 36 return ERROR_BUFFER_OVERFLOW; 37 38 CopyMemory(dest, *pos, dest_len); 39 *pos += dest_len; 40 *remaining -= dest_len; 41 return 0; 42 } 43 44 int safe_write(unsigned char **pos, uint32_t *remaining, void *src, uint32_t src_len) 45 { 46 if (*remaining < src_len) 47 return ERROR_BUFFER_OVERFLOW; 48 49 CopyMemory(*pos, src, src_len); 50 *pos += src_len; 51 *remaining -= src_len; 52 return 0; 53 } 54 55 int get_name(unsigned char **pos, uint32_t *remaining, const char **out_name) 56 { 57 int status; 58 USHORT len; 59 60 status = safe_read(pos, remaining, &len, sizeof(USHORT)); 61 if (status) goto out; 62 if (*remaining < len) { 63 status = ERROR_BUFFER_OVERFLOW; 64 goto out; 65 } 66 *out_name = (const char*)*pos; 67 *pos += len; 68 *remaining -= len; 69 out: 70 return status; 71 } 72 73 const char* strip_path( 74 IN const char *path, 75 OUT uint32_t *len_out) 76 { 77 const char *name = strrchr(path, '\\'); 78 name = name ? name + 1 : path; 79 if (len_out) 80 *len_out = (uint32_t)strlen(name); 81 return name; 82 } 83 84 uint32_t max_read_size( 85 IN const nfs41_session *session, 86 IN const nfs41_fh *fh) 87 { 88 const uint32_t maxresponse = session->fore_chan_attrs.ca_maxresponsesize; 89 return (uint32_t)min(fh->superblock->maxread, maxresponse - READ_OVERHEAD); 90 } 91 92 uint32_t max_write_size( 93 IN const nfs41_session *session, 94 IN const nfs41_fh *fh) 95 { 96 const uint32_t maxrequest = session->fore_chan_attrs.ca_maxrequestsize; 97 return (uint32_t)min(fh->superblock->maxwrite, maxrequest - WRITE_OVERHEAD); 98 } 99 100 bool_t verify_write( 101 IN nfs41_write_verf *verf, 102 IN OUT enum stable_how4 *stable) 103 { 104 if (verf->committed != UNSTABLE4) { 105 *stable = verf->committed; 106 dprintf(3, "verify_write: committed to stable storage\n"); 107 return 1; 108 } 109 110 if (*stable != UNSTABLE4) { 111 memcpy(verf->expected, verf->verf, NFS4_VERIFIER_SIZE); 112 *stable = UNSTABLE4; 113 dprintf(3, "verify_write: first unstable write, saving verifier\n"); 114 return 1; 115 } 116 117 if (memcmp(verf->expected, verf->verf, NFS4_VERIFIER_SIZE) == 0) { 118 dprintf(3, "verify_write: verifier matches expected\n"); 119 return 1; 120 } 121 122 dprintf(2, "verify_write: verifier changed; writes have been lost!\n"); 123 return 0; 124 } 125 126 bool_t verify_commit( 127 IN nfs41_write_verf *verf) 128 { 129 if (memcmp(verf->expected, verf->verf, NFS4_VERIFIER_SIZE) == 0) { 130 dprintf(3, "verify_commit: verifier matches expected\n"); 131 return 1; 132 } 133 dprintf(2, "verify_commit: verifier changed; writes have been lost!\n"); 134 return 0; 135 } 136 137 ULONG nfs_file_info_to_attributes( 138 IN const nfs41_file_info *info) 139 { 140 ULONG attrs = 0; 141 if (info->type == NF4DIR) 142 attrs |= FILE_ATTRIBUTE_DIRECTORY; 143 else if (info->type == NF4LNK) { 144 attrs |= FILE_ATTRIBUTE_REPARSE_POINT; 145 if (info->symlink_dir) 146 attrs |= FILE_ATTRIBUTE_DIRECTORY; 147 } else if (info->type != NF4REG) 148 dprintf(1, "unhandled file type %d, defaulting to NF4REG\n", 149 info->type); 150 151 if (info->mode == 0444) /* XXX: 0444 for READONLY */ 152 attrs |= FILE_ATTRIBUTE_READONLY; 153 154 if (info->hidden) attrs |= FILE_ATTRIBUTE_HIDDEN; 155 if (info->system) attrs |= FILE_ATTRIBUTE_SYSTEM; 156 if (info->archive) attrs |= FILE_ATTRIBUTE_ARCHIVE; 157 158 // FILE_ATTRIBUTE_NORMAL attribute is only set if no other attributes are present. 159 // all other override this value. 160 return attrs ? attrs : FILE_ATTRIBUTE_NORMAL; 161 } 162 163 void nfs_to_basic_info( 164 IN const nfs41_file_info *info, 165 OUT PFILE_BASIC_INFO basic_out) 166 { 167 nfs_time_to_file_time(&info->time_create, &basic_out->CreationTime); 168 nfs_time_to_file_time(&info->time_access, &basic_out->LastAccessTime); 169 nfs_time_to_file_time(&info->time_modify, &basic_out->LastWriteTime); 170 /* XXX: was using 'change' attr, but that wasn't giving a time */ 171 nfs_time_to_file_time(&info->time_modify, &basic_out->ChangeTime); 172 basic_out->FileAttributes = nfs_file_info_to_attributes(info); 173 } 174 175 void nfs_to_standard_info( 176 IN const nfs41_file_info *info, 177 OUT PFILE_STANDARD_INFO std_out) 178 { 179 const ULONG FileAttributes = nfs_file_info_to_attributes(info); 180 181 std_out->AllocationSize.QuadPart = 182 std_out->EndOfFile.QuadPart = (LONGLONG)info->size; 183 std_out->NumberOfLinks = info->numlinks; 184 std_out->DeletePending = FALSE; 185 std_out->Directory = FileAttributes & FILE_ATTRIBUTE_DIRECTORY ? 186 TRUE : FALSE; 187 } 188 189 void nfs_to_network_openinfo( 190 IN const nfs41_file_info *info, 191 OUT PFILE_NETWORK_OPEN_INFORMATION net_out) 192 { 193 194 nfs_time_to_file_time(&info->time_create, &net_out->CreationTime); 195 nfs_time_to_file_time(&info->time_access, &net_out->LastAccessTime); 196 nfs_time_to_file_time(&info->time_modify, &net_out->LastWriteTime); 197 /* XXX: was using 'change' attr, but that wasn't giving a time */ 198 nfs_time_to_file_time(&info->time_modify, &net_out->ChangeTime); 199 net_out->AllocationSize.QuadPart = 200 net_out->EndOfFile.QuadPart = (LONGLONG)info->size; 201 net_out->FileAttributes = nfs_file_info_to_attributes(info); 202 } 203 204 void get_file_time( 205 OUT PLARGE_INTEGER file_time) 206 { 207 GetSystemTimeAsFileTime((LPFILETIME)file_time); 208 } 209 210 void get_nfs_time( 211 OUT nfstime4 *nfs_time) 212 { 213 LARGE_INTEGER file_time; 214 get_file_time(&file_time); 215 file_time_to_nfs_time(&file_time, nfs_time); 216 } 217 218 bool_t multi_addr_find( 219 IN const multi_addr4 *addrs, 220 IN const netaddr4 *addr, 221 OUT OPTIONAL uint32_t *index_out) 222 { 223 uint32_t i; 224 for (i = 0; i < addrs->count; i++) { 225 const netaddr4 *saddr = &addrs->arr[i]; 226 if (!strncmp(saddr->netid, addr->netid, NFS41_NETWORK_ID_LEN) && 227 !strncmp(saddr->uaddr, addr->uaddr, NFS41_UNIVERSAL_ADDR_LEN)) { 228 if (index_out) *index_out = i; 229 return 1; 230 } 231 } 232 return 0; 233 } 234 235 int nfs_to_windows_error(int status, int default_error) 236 { 237 /* make sure this is actually an nfs error */ 238 if (status < 0 || (status > 70 && status < 10001) || status > 10087) { 239 eprintf("nfs_to_windows_error called with non-nfs " 240 "error code %d; returning the error as is\n", status); 241 return status; 242 } 243 244 switch (status) { 245 case NFS4_OK: return NO_ERROR; 246 case NFS4ERR_PERM: return ERROR_ACCESS_DENIED; 247 case NFS4ERR_NOENT: return ERROR_FILE_NOT_FOUND; 248 case NFS4ERR_IO: return ERROR_NET_WRITE_FAULT; 249 case NFS4ERR_ACCESS: return ERROR_ACCESS_DENIED; 250 case NFS4ERR_EXIST: return ERROR_FILE_EXISTS; 251 case NFS4ERR_XDEV: return ERROR_NOT_SAME_DEVICE; 252 case NFS4ERR_INVAL: return ERROR_INVALID_PARAMETER; 253 case NFS4ERR_FBIG: return ERROR_FILE_TOO_LARGE; 254 case NFS4ERR_NOSPC: return ERROR_DISK_FULL; 255 case NFS4ERR_ROFS: return ERROR_NETWORK_ACCESS_DENIED; 256 case NFS4ERR_MLINK: return ERROR_TOO_MANY_LINKS; 257 case NFS4ERR_NAMETOOLONG: return ERROR_FILENAME_EXCED_RANGE; 258 case NFS4ERR_STALE: return ERROR_NETNAME_DELETED; 259 case NFS4ERR_NOTEMPTY: return ERROR_NOT_EMPTY; 260 case NFS4ERR_DENIED: return ERROR_LOCK_FAILED; 261 case NFS4ERR_TOOSMALL: return ERROR_BUFFER_OVERFLOW; 262 case NFS4ERR_LOCKED: return ERROR_LOCK_VIOLATION; 263 case NFS4ERR_SHARE_DENIED: return ERROR_SHARING_VIOLATION; 264 case NFS4ERR_LOCK_RANGE: return ERROR_NOT_LOCKED; 265 case NFS4ERR_ATTRNOTSUPP: return ERROR_NOT_SUPPORTED; 266 case NFS4ERR_OPENMODE: return ERROR_ACCESS_DENIED; 267 case NFS4ERR_LOCK_NOTSUPP: return ERROR_ATOMIC_LOCKS_NOT_SUPPORTED; 268 269 case NFS4ERR_BADCHAR: 270 case NFS4ERR_BADNAME: return ERROR_INVALID_NAME; 271 272 case NFS4ERR_NOTDIR: 273 case NFS4ERR_ISDIR: 274 case NFS4ERR_SYMLINK: 275 case NFS4ERR_WRONG_TYPE: return ERROR_INVALID_PARAMETER; 276 277 case NFS4ERR_EXPIRED: 278 case NFS4ERR_NOFILEHANDLE: 279 case NFS4ERR_OLD_STATEID: 280 case NFS4ERR_BAD_STATEID: 281 case NFS4ERR_ADMIN_REVOKED: return ERROR_FILE_INVALID; 282 283 case NFS4ERR_WRONGSEC: return ERROR_ACCESS_DENIED; 284 285 default: 286 dprintf(1, "nfs error %s not mapped to windows error; " 287 "returning default error %d\n", 288 nfs_error_string(status), default_error); 289 return default_error; 290 } 291 } 292 293 int map_symlink_errors(int status) 294 { 295 switch (status) { 296 case NFS4ERR_BADCHAR: 297 case NFS4ERR_BADNAME: return ERROR_INVALID_REPARSE_DATA; 298 case NFS4ERR_WRONG_TYPE: return ERROR_NOT_A_REPARSE_POINT; 299 case NFS4ERR_ACCESS: return ERROR_ACCESS_DENIED; 300 case NFS4ERR_NOTEMPTY: return ERROR_NOT_EMPTY; 301 default: return nfs_to_windows_error(status, ERROR_BAD_NET_RESP); 302 } 303 } 304 305 bool_t next_component( 306 IN const char *path, 307 IN const char *path_end, 308 OUT nfs41_component *component) 309 { 310 const char *component_end; 311 component->name = next_non_delimiter(path, path_end); 312 component_end = next_delimiter(component->name, path_end); 313 component->len = (unsigned short)(component_end - component->name); 314 return component->len > 0; 315 } 316 317 bool_t last_component( 318 IN const char *path, 319 IN const char *path_end, 320 OUT nfs41_component *component) 321 { 322 const char *component_end = prev_delimiter(path_end, path); 323 component->name = prev_non_delimiter(component_end, path); 324 component->name = prev_delimiter(component->name, path); 325 component->name = next_non_delimiter(component->name, component_end); 326 component->len = (unsigned short)(component_end - component->name); 327 return component->len > 0; 328 } 329 330 bool_t is_last_component( 331 IN const char *path, 332 IN const char *path_end) 333 { 334 path = next_delimiter(path, path_end); 335 return next_non_delimiter(path, path_end) == path_end; 336 } 337 338 void abs_path_copy( 339 OUT nfs41_abs_path *dst, 340 IN const nfs41_abs_path *src) 341 { 342 dst->len = src->len; 343 StringCchCopyNA(dst->path, NFS41_MAX_PATH_LEN, src->path, dst->len); 344 } 345 346 void path_fh_init( 347 OUT nfs41_path_fh *file, 348 IN nfs41_abs_path *path) 349 { 350 file->path = path; 351 last_component(path->path, path->path + path->len, &file->name); 352 } 353 354 void fh_copy( 355 OUT nfs41_fh *dst, 356 IN const nfs41_fh *src) 357 { 358 dst->fileid = src->fileid; 359 dst->superblock = src->superblock; 360 dst->len = src->len; 361 memcpy(dst->fh, src->fh, dst->len); 362 } 363 364 void path_fh_copy( 365 OUT nfs41_path_fh *dst, 366 IN const nfs41_path_fh *src) 367 { 368 dst->path = src->path; 369 if (dst->path) { 370 const size_t name_start = src->name.name - src->path->path; 371 dst->name.name = dst->path->path + name_start; 372 dst->name.len = src->name.len; 373 } else { 374 dst->name.name = NULL; 375 dst->name.len = 0; 376 } 377 fh_copy(&dst->fh, &src->fh); 378 } 379 380 int create_silly_rename( 381 IN nfs41_abs_path *path, 382 IN const nfs41_fh *fh, 383 OUT nfs41_component *silly) 384 { 385 HCRYPTPROV context; 386 HCRYPTHASH hash; 387 PBYTE buffer; 388 DWORD length; 389 const char *end = path->path + NFS41_MAX_PATH_LEN; 390 const unsigned short extra_len = 2 + 16; //md5 is 16 391 char name[NFS41_MAX_COMPONENT_LEN+1]; 392 unsigned char fhmd5[17] = { 0 }; 393 char *tmp; 394 int status = NO_ERROR, i; 395 396 if (path->len + extra_len >= NFS41_MAX_PATH_LEN) { 397 status = ERROR_FILENAME_EXCED_RANGE; 398 goto out; 399 } 400 401 /* set up the md5 hash generator */ 402 if (!CryptAcquireContext(&context, NULL, NULL, 403 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { 404 status = GetLastError(); 405 eprintf("CryptAcquireContext() failed with %d\n", status); 406 goto out; 407 } 408 if (!CryptCreateHash(context, CALG_MD5, 0, 0, &hash)) { 409 status = GetLastError(); 410 eprintf("CryptCreateHash() failed with %d\n", status); 411 goto out_context; 412 } 413 414 if (!CryptHashData(hash, (const BYTE*)fh->fh, (DWORD)fh->len, 0)) { 415 status = GetLastError(); 416 eprintf("CryptHashData() failed with %d\n", status); 417 goto out_hash; 418 } 419 420 /* extract the hash buffer */ 421 buffer = (PBYTE)fhmd5; 422 length = 16; 423 if (!CryptGetHashParam(hash, HP_HASHVAL, buffer, &length, 0)) { 424 status = GetLastError(); 425 eprintf("CryptGetHashParam(val) failed with %d\n", status); 426 goto out_hash; 427 } 428 429 last_component(path->path, path->path + path->len, silly); 430 StringCchCopyNA(name, NFS41_MAX_COMPONENT_LEN+1, silly->name, silly->len); 431 432 tmp = (char*)silly->name; 433 StringCchPrintf(tmp, end - tmp, ".%s.", name); 434 tmp += silly->len + 2; 435 436 for (i = 0; i < 16; i++, tmp++) 437 StringCchPrintf(tmp, end - tmp, "%x", fhmd5[i]); 438 439 path->len = path->len + extra_len; 440 silly->len = silly->len + extra_len; 441 442 out_hash: 443 CryptDestroyHash(hash); 444 out_context: 445 CryptReleaseContext(context, 0); 446 out: 447 return status; 448 } 449