[NFSD]
[reactos.git] / reactos / base / services / nfsd / readwrite.c
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 <stdio.h>
24
25 #include "nfs41_ops.h"
26 #include "name_cache.h"
27 #include "upcall.h"
28 #include "daemon_debug.h"
29 #include "util.h"
30
31
32 /* number of times to retry on write/commit verifier mismatch */
33 #define MAX_WRITE_RETRIES 6
34
35
36 const stateid4 special_read_stateid = {0xffffffff,
37 {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
38
39 static int parse_rw(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
40 {
41 int status;
42 readwrite_upcall_args *args = &upcall->args.rw;
43
44 status = safe_read(&buffer, &length, &args->len, sizeof(args->len));
45 if (status) goto out;
46 status = safe_read(&buffer, &length, &args->offset, sizeof(args->offset));
47 if (status) goto out;
48 status = safe_read(&buffer, &length, &args->buffer, sizeof(args->buffer));
49 if (status) goto out;
50
51 dprintf(1, "parsing %s len=%lu offset=%llu buf=%p\n",
52 opcode2string(upcall->opcode), args->len, args->offset, args->buffer);
53 out:
54 return status;
55 }
56
57 /* NFS41_READ */
58 static int read_from_mds(
59 IN nfs41_upcall *upcall,
60 IN stateid_arg *stateid)
61 {
62 nfs41_session *session = upcall->state_ref->session;
63 nfs41_path_fh *file = &upcall->state_ref->file;
64 readwrite_upcall_args *args = &upcall->args.rw;
65 int status = 0;
66 bool_t eof;
67 unsigned char *p = args->buffer;
68 ULONG to_rcv = args->len, reloffset = 0, len = 0;
69 const uint32_t maxreadsize = max_read_size(session, &file->fh);
70
71 if (to_rcv > maxreadsize)
72 dprintf(1, "handle_nfs41_read: reading %d in chunks of %d\n",
73 to_rcv, maxreadsize);
74
75 while(to_rcv > 0) {
76 uint32_t bytes_read = 0, chunk = min(to_rcv, maxreadsize);
77
78 status = nfs41_read(session, file, stateid, args->offset + reloffset, chunk,
79 p, &bytes_read, &eof);
80 if (status == NFS4ERR_OPENMODE && !len) {
81 stateid->type = STATEID_SPECIAL;
82 memcpy(&stateid->stateid, &special_read_stateid, sizeof(stateid4));
83 continue;
84 } else if (status && !len) {
85 status = nfs_to_windows_error(status, ERROR_NET_WRITE_FAULT);
86 goto out;
87 }
88
89 p += bytes_read;
90 to_rcv -= bytes_read;
91 len += bytes_read;
92 args->offset += bytes_read;
93 if (status) {
94 status = NO_ERROR;
95 break;
96 }
97 if (eof) {
98 if (!len)
99 status = ERROR_HANDLE_EOF;
100 break;
101 }
102 }
103 out:
104 args->out_len = len;
105 return status;
106 }
107
108 static int read_from_pnfs(
109 IN nfs41_upcall *upcall,
110 IN stateid_arg *stateid)
111 {
112 readwrite_upcall_args *args = &upcall->args.rw;
113 pnfs_layout_state *layout;
114 enum pnfs_status pnfsstat;
115 int status = NO_ERROR;
116
117 if (pnfs_layout_state_open(upcall->state_ref, &layout)) {
118 status = ERROR_NOT_SUPPORTED;
119 goto out;
120 }
121
122 pnfsstat = pnfs_read(upcall->root_ref, upcall->state_ref, stateid, layout,
123 args->offset, args->len, args->buffer, &args->out_len);
124 switch (pnfsstat) {
125 case PNFS_SUCCESS:
126 break;
127 case PNFS_READ_EOF:
128 status = ERROR_HANDLE_EOF;
129 break;
130 default:
131 status = ERROR_READ_FAULT;
132 break;
133 }
134 out:
135 return status;
136 }
137
138 static int handle_read(nfs41_upcall *upcall)
139 {
140 readwrite_upcall_args *args = &upcall->args.rw;
141 stateid_arg stateid;
142 ULONG pnfs_bytes_read = 0;
143 int status = NO_ERROR;
144
145 #ifdef __REACTOS__
146 do
147 {
148 #endif
149 nfs41_open_stateid_arg(upcall->state_ref, &stateid);
150
151 #ifdef PNFS_ENABLE_READ
152 status = read_from_pnfs(upcall, &stateid);
153
154 if (status == NO_ERROR || status == ERROR_HANDLE_EOF)
155 goto out;
156
157 if (args->out_len) {
158 pnfs_bytes_read = args->out_len;
159 args->out_len = 0;
160
161 args->offset += pnfs_bytes_read;
162 args->buffer += pnfs_bytes_read;
163 args->len -= pnfs_bytes_read;
164 }
165 #endif
166
167 status = read_from_mds(upcall, &stateid);
168 #ifdef __REACTOS__
169 /* Status returned by NFS server when session is to be renewed */
170 if (status == 1006)
171 {
172 nfs41_session_renew(upcall->state_ref->session);
173 dprintf(1, "Session renewed (read)!\n");
174 continue;
175 }
176 #endif
177
178 args->out_len += pnfs_bytes_read;
179 #ifdef __REACTOS__
180 break;
181 }
182 while (TRUE);
183 #endif
184 out:
185 return status;
186 }
187
188
189 /* NFS41_WRITE */
190 static int write_to_mds(
191 IN nfs41_upcall *upcall,
192 IN stateid_arg *stateid)
193 {
194 nfs41_session *session = upcall->state_ref->session;
195 nfs41_path_fh *file = &upcall->state_ref->file;
196 readwrite_upcall_args *args = &upcall->args.rw;
197 nfs41_write_verf verf;
198 enum stable_how4 stable, committed;
199 unsigned char *p;
200 const uint32_t maxwritesize = max_write_size(session, &file->fh);
201 uint32_t to_send, reloffset, len;
202 int status = 0;
203 /* on write verifier mismatch, retry N times before failing */
204 uint32_t retries = MAX_WRITE_RETRIES;
205 nfs41_file_info info = { 0 };
206
207 retry_write:
208 p = args->buffer;
209 to_send = args->len;
210 reloffset = 0;
211 len = 0;
212 stable = to_send <= maxwritesize ? FILE_SYNC4 : UNSTABLE4;
213 committed = FILE_SYNC4;
214
215 if (to_send > maxwritesize)
216 dprintf(1, "handle_nfs41_write: writing %d in chunks of %d\n",
217 to_send, maxwritesize);
218
219 while(to_send > 0) {
220 uint32_t bytes_written = 0, chunk = min(to_send, maxwritesize);
221
222 status = nfs41_write(session, file, stateid, p, chunk,
223 args->offset + reloffset, stable, &bytes_written, &verf, &info);
224 if (status && !len)
225 goto out;
226 p += bytes_written;
227 to_send -= bytes_written;
228 len += bytes_written;
229 reloffset += bytes_written;
230 if (status) {
231 status = 0;
232 break;
233 }
234 if (!verify_write(&verf, &committed)) {
235 if (retries--) goto retry_write;
236 goto out_verify_failed;
237 }
238 }
239 if (committed != FILE_SYNC4) {
240 dprintf(1, "sending COMMIT for offset=%d and len=%d\n", args->offset, len);
241 status = nfs41_commit(session, file, args->offset, len, 1, &verf, &info);
242 if (status)
243 goto out;
244
245 if (!verify_commit(&verf)) {
246 if (retries--) goto retry_write;
247 goto out_verify_failed;
248 }
249 } else if (stable == UNSTABLE4) {
250 nfs41_file_info info;
251 bitmap4 attr_request;
252 nfs41_superblock_getattr_mask(file->fh.superblock, &attr_request);
253 status = nfs41_getattr(session, file, &attr_request, &info);
254 if (status)
255 goto out;
256 }
257 args->ctime = info.change;
258 out:
259 args->out_len = len;
260 return nfs_to_windows_error(status, ERROR_NET_WRITE_FAULT);
261
262 out_verify_failed:
263 len = 0;
264 status = NFS4ERR_IO;
265 goto out;
266 }
267
268 static int write_to_pnfs(
269 IN nfs41_upcall *upcall,
270 IN stateid_arg *stateid)
271 {
272 readwrite_upcall_args *args = &upcall->args.rw;
273 pnfs_layout_state *layout;
274 int status = NO_ERROR;
275 nfs41_file_info info = { 0 };
276
277 if (pnfs_layout_state_open(upcall->state_ref, &layout)) {
278 status = ERROR_NOT_SUPPORTED;
279 goto out;
280 }
281
282 if (pnfs_write(upcall->root_ref, upcall->state_ref, stateid, layout,
283 args->offset, args->len, args->buffer, &args->out_len, &info)) {
284 status = ERROR_WRITE_FAULT;
285 goto out;
286 }
287 args->ctime = info.change;
288 out:
289 return status;
290 }
291
292 static int handle_write(nfs41_upcall *upcall)
293 {
294 readwrite_upcall_args *args = &upcall->args.rw;
295 stateid_arg stateid;
296 uint32_t pnfs_bytes_written = 0;
297 int status;
298
299 #ifdef __REACTOS__
300 do
301 {
302 #endif
303 nfs41_open_stateid_arg(upcall->state_ref, &stateid);
304
305 #ifdef PNFS_ENABLE_WRITE
306 status = write_to_pnfs(upcall, &stateid);
307 if (args->out_len) {
308 pnfs_bytes_written = args->out_len;
309 args->out_len = 0;
310
311 args->offset += pnfs_bytes_written;
312 args->buffer += pnfs_bytes_written;
313 args->len -= pnfs_bytes_written;
314
315 if (args->len == 0)
316 goto out;
317 }
318 #endif
319
320 status = write_to_mds(upcall, &stateid);
321 #ifdef __REACTOS__
322 /* Status returned by NFS server when session is to be renewed */
323 if (status == 1006)
324 {
325 nfs41_session_renew(upcall->state_ref->session);
326 dprintf(1, "Session renewed (write)!\n");
327 continue;
328 }
329
330 break;
331 }
332 while (TRUE);
333 #endif
334 out:
335 args->out_len += pnfs_bytes_written;
336 return status;
337 }
338
339 static int marshall_rw(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
340 {
341 readwrite_upcall_args *args = &upcall->args.rw;
342 int status;
343 status = safe_write(&buffer, length, &args->out_len, sizeof(args->out_len));
344 if (status) goto out;
345 status = safe_write(&buffer, length, &args->ctime, sizeof(args->ctime));
346 out:
347 return status;
348 }
349
350
351 const nfs41_upcall_op nfs41_op_read = {
352 parse_rw,
353 handle_read,
354 marshall_rw
355 };
356 const nfs41_upcall_op nfs41_op_write = {
357 parse_rw,
358 handle_write,
359 marshall_rw
360 };