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
safe_read(unsigned char ** pos,uint32_t * remaining,void * dest,uint32_t dest_len)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
safe_write(unsigned char ** pos,uint32_t * remaining,void * src,uint32_t src_len)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
get_name(unsigned char ** pos,uint32_t * remaining,const char ** out_name)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
strip_path(IN const char * path,OUT uint32_t * len_out)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
max_read_size(IN const nfs41_session * session,IN const nfs41_fh * fh)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
max_write_size(IN const nfs41_session * session,IN const nfs41_fh * fh)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
verify_write(IN nfs41_write_verf * verf,IN OUT enum stable_how4 * stable)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
verify_commit(IN nfs41_write_verf * verf)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
nfs_file_info_to_attributes(IN const nfs41_file_info * info)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
nfs_to_basic_info(IN const nfs41_file_info * info,OUT PFILE_BASIC_INFO basic_out)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
nfs_to_standard_info(IN const nfs41_file_info * info,OUT PFILE_STANDARD_INFO std_out)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
nfs_to_network_openinfo(IN const nfs41_file_info * info,OUT PFILE_NETWORK_OPEN_INFORMATION net_out)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
get_file_time(OUT PLARGE_INTEGER file_time)204 void get_file_time(
205 OUT PLARGE_INTEGER file_time)
206 {
207 GetSystemTimeAsFileTime((LPFILETIME)file_time);
208 }
209
get_nfs_time(OUT nfstime4 * nfs_time)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
multi_addr_find(IN const multi_addr4 * addrs,IN const netaddr4 * addr,OUT OPTIONAL uint32_t * index_out)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
nfs_to_windows_error(int status,int default_error)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
map_symlink_errors(int status)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
next_component(IN const char * path,IN const char * path_end,OUT nfs41_component * component)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
last_component(IN const char * path,IN const char * path_end,OUT nfs41_component * component)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
is_last_component(IN const char * path,IN const char * path_end)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
abs_path_copy(OUT nfs41_abs_path * dst,IN const nfs41_abs_path * src)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
path_fh_init(OUT nfs41_path_fh * file,IN nfs41_abs_path * path)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
fh_copy(OUT nfs41_fh * dst,IN const nfs41_fh * src)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
path_fh_copy(OUT nfs41_path_fh * dst,IN const nfs41_path_fh * src)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
create_silly_rename(IN nfs41_abs_path * path,IN const nfs41_fh * fh,OUT nfs41_component * silly)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