1 /* NFSv4.1 client for Windows
2 * Copyright © 2012 The Regents of the University of Michigan
4 * Olga Kornievskaia <aglo@umich.edu>
5 * Casey Bodley <cbodley@umich.edu>
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.
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.
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
26 #include <wincrypt.h> /* for Crypt*() functions */
28 #include "daemon_debug.h"
30 #include "nfs41_ops.h"
33 int safe_read(unsigned char **pos
, uint32_t *remaining
, void *dest
, uint32_t dest_len
)
35 if (*remaining
< dest_len
)
36 return ERROR_BUFFER_OVERFLOW
;
38 CopyMemory(dest
, *pos
, dest_len
);
40 *remaining
-= dest_len
;
44 int safe_write(unsigned char **pos
, uint32_t *remaining
, void *src
, uint32_t src_len
)
46 if (*remaining
< src_len
)
47 return ERROR_BUFFER_OVERFLOW
;
49 CopyMemory(*pos
, src
, src_len
);
51 *remaining
-= src_len
;
55 int get_name(unsigned char **pos
, uint32_t *remaining
, const char **out_name
)
60 status
= safe_read(pos
, remaining
, &len
, sizeof(USHORT
));
62 if (*remaining
< len
) {
63 status
= ERROR_BUFFER_OVERFLOW
;
66 *out_name
= (const char*)*pos
;
73 const char* strip_path(
75 OUT
uint32_t *len_out
)
77 const char *name
= strrchr(path
, '\\');
78 name
= name
? name
+ 1 : path
;
80 *len_out
= (uint32_t)strlen(name
);
84 uint32_t max_read_size(
85 IN
const nfs41_session
*session
,
86 IN
const nfs41_fh
*fh
)
88 const uint32_t maxresponse
= session
->fore_chan_attrs
.ca_maxresponsesize
;
89 return (uint32_t)min(fh
->superblock
->maxread
, maxresponse
- READ_OVERHEAD
);
92 uint32_t max_write_size(
93 IN
const nfs41_session
*session
,
94 IN
const nfs41_fh
*fh
)
96 const uint32_t maxrequest
= session
->fore_chan_attrs
.ca_maxrequestsize
;
97 return (uint32_t)min(fh
->superblock
->maxwrite
, maxrequest
- WRITE_OVERHEAD
);
101 IN nfs41_write_verf
*verf
,
102 IN OUT
enum stable_how4
*stable
)
104 if (verf
->committed
!= UNSTABLE4
) {
105 *stable
= verf
->committed
;
106 dprintf(3, "verify_write: committed to stable storage\n");
110 if (*stable
!= UNSTABLE4
) {
111 memcpy(verf
->expected
, verf
->verf
, NFS4_VERIFIER_SIZE
);
113 dprintf(3, "verify_write: first unstable write, saving verifier\n");
117 if (memcmp(verf
->expected
, verf
->verf
, NFS4_VERIFIER_SIZE
) == 0) {
118 dprintf(3, "verify_write: verifier matches expected\n");
122 dprintf(2, "verify_write: verifier changed; writes have been lost!\n");
126 bool_t
verify_commit(
127 IN nfs41_write_verf
*verf
)
129 if (memcmp(verf
->expected
, verf
->verf
, NFS4_VERIFIER_SIZE
) == 0) {
130 dprintf(3, "verify_commit: verifier matches expected\n");
133 dprintf(2, "verify_commit: verifier changed; writes have been lost!\n");
137 ULONG
nfs_file_info_to_attributes(
138 IN
const nfs41_file_info
*info
)
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",
151 if (info
->mode
== 0444) /* XXX: 0444 for READONLY */
152 attrs
|= FILE_ATTRIBUTE_READONLY
;
154 if (info
->hidden
) attrs
|= FILE_ATTRIBUTE_HIDDEN
;
155 if (info
->system
) attrs
|= FILE_ATTRIBUTE_SYSTEM
;
156 if (info
->archive
) attrs
|= FILE_ATTRIBUTE_ARCHIVE
;
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
;
163 void nfs_to_basic_info(
164 IN
const nfs41_file_info
*info
,
165 OUT PFILE_BASIC_INFO basic_out
)
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
);
175 void nfs_to_standard_info(
176 IN
const nfs41_file_info
*info
,
177 OUT PFILE_STANDARD_INFO std_out
)
179 const ULONG FileAttributes
= nfs_file_info_to_attributes(info
);
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
?
189 void nfs_to_network_openinfo(
190 IN
const nfs41_file_info
*info
,
191 OUT PFILE_NETWORK_OPEN_INFORMATION net_out
)
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
);
205 OUT PLARGE_INTEGER file_time
)
207 GetSystemTimeAsFileTime((LPFILETIME
)file_time
);
211 OUT nfstime4
*nfs_time
)
213 LARGE_INTEGER file_time
;
214 get_file_time(&file_time
);
215 file_time_to_nfs_time(&file_time
, nfs_time
);
218 bool_t
multi_addr_find(
219 IN
const multi_addr4
*addrs
,
220 IN
const netaddr4
*addr
,
221 OUT OPTIONAL
uint32_t *index_out
)
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
;
235 int nfs_to_windows_error(int status
, int default_error
)
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
);
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
;
269 case NFS4ERR_BADCHAR
:
270 case NFS4ERR_BADNAME
: return ERROR_INVALID_NAME
;
274 case NFS4ERR_SYMLINK
:
275 case NFS4ERR_WRONG_TYPE
: return ERROR_INVALID_PARAMETER
;
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
;
283 case NFS4ERR_WRONGSEC
: return ERROR_ACCESS_DENIED
;
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
;
293 int map_symlink_errors(int 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
);
305 bool_t
next_component(
307 IN
const char *path_end
,
308 OUT nfs41_component
*component
)
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;
317 bool_t
last_component(
319 IN
const char *path_end
,
320 OUT nfs41_component
*component
)
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;
330 bool_t
is_last_component(
332 IN
const char *path_end
)
334 path
= next_delimiter(path
, path_end
);
335 return next_non_delimiter(path
, path_end
) == path_end
;
339 OUT nfs41_abs_path
*dst
,
340 IN
const nfs41_abs_path
*src
)
343 StringCchCopyNA(dst
->path
, NFS41_MAX_PATH_LEN
, src
->path
, dst
->len
);
347 OUT nfs41_path_fh
*file
,
348 IN nfs41_abs_path
*path
)
351 last_component(path
->path
, path
->path
+ path
->len
, &file
->name
);
356 IN
const nfs41_fh
*src
)
358 dst
->fileid
= src
->fileid
;
359 dst
->superblock
= src
->superblock
;
361 memcpy(dst
->fh
, src
->fh
, dst
->len
);
365 OUT nfs41_path_fh
*dst
,
366 IN
const nfs41_path_fh
*src
)
368 dst
->path
= src
->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
;
374 dst
->name
.name
= NULL
;
377 fh_copy(&dst
->fh
, &src
->fh
);
380 int create_silly_rename(
381 IN nfs41_abs_path
*path
,
382 IN
const nfs41_fh
*fh
,
383 OUT nfs41_component
*silly
)
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 };
394 int status
= NO_ERROR
, i
;
396 if (path
->len
+ extra_len
>= NFS41_MAX_PATH_LEN
) {
397 status
= ERROR_FILENAME_EXCED_RANGE
;
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
);
408 if (!CryptCreateHash(context
, CALG_MD5
, 0, 0, &hash
)) {
409 status
= GetLastError();
410 eprintf("CryptCreateHash() failed with %d\n", status
);
414 if (!CryptHashData(hash
, (const BYTE
*)fh
->fh
, (DWORD
)fh
->len
, 0)) {
415 status
= GetLastError();
416 eprintf("CryptHashData() failed with %d\n", status
);
420 /* extract the hash buffer */
421 buffer
= (PBYTE
)fhmd5
;
423 if (!CryptGetHashParam(hash
, HP_HASHVAL
, buffer
, &length
, 0)) {
424 status
= GetLastError();
425 eprintf("CryptGetHashParam(val) failed with %d\n", status
);
429 last_component(path
->path
, path
->path
+ path
->len
, silly
);
430 StringCchCopyNA(name
, NFS41_MAX_COMPONENT_LEN
+1, silly
->name
, silly
->len
);
432 tmp
= (char*)silly
->name
;
433 StringCchPrintf(tmp
, end
- tmp
, ".%s.", name
);
434 tmp
+= silly
->len
+ 2;
436 for (i
= 0; i
< 16; i
++, tmp
++)
437 StringCchPrintf(tmp
, end
- tmp
, "%x", fhmd5
[i
]);
439 path
->len
= path
->len
+ extra_len
;
440 silly
->len
= silly
->len
+ extra_len
;
443 CryptDestroyHash(hash
);
445 CryptReleaseContext(context
, 0);