xref: /reactos/base/services/nfsd/util.c (revision c2c66aff)
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