6844df5fddf2c11dd70268699955f249cc35c904
[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 nfs41_open_stateid_arg(upcall->state_ref, &stateid);
146
147 #ifdef PNFS_ENABLE_READ
148 status = read_from_pnfs(upcall, &stateid);
149
150 if (status == NO_ERROR || status == ERROR_HANDLE_EOF)
151 goto out;
152
153 if (args->out_len) {
154 pnfs_bytes_read = args->out_len;
155 args->out_len = 0;
156
157 args->offset += pnfs_bytes_read;
158 args->buffer += pnfs_bytes_read;
159 args->len -= pnfs_bytes_read;
160 }
161 #endif
162
163 status = read_from_mds(upcall, &stateid);
164
165 args->out_len += pnfs_bytes_read;
166 out:
167 return status;
168 }
169
170
171 /* NFS41_WRITE */
172 static int write_to_mds(
173 IN nfs41_upcall *upcall,
174 IN stateid_arg *stateid)
175 {
176 nfs41_session *session = upcall->state_ref->session;
177 nfs41_path_fh *file = &upcall->state_ref->file;
178 readwrite_upcall_args *args = &upcall->args.rw;
179 nfs41_write_verf verf;
180 enum stable_how4 stable, committed;
181 unsigned char *p;
182 const uint32_t maxwritesize = max_write_size(session, &file->fh);
183 uint32_t to_send, reloffset, len;
184 int status = 0;
185 /* on write verifier mismatch, retry N times before failing */
186 uint32_t retries = MAX_WRITE_RETRIES;
187 nfs41_file_info info = { 0 };
188
189 retry_write:
190 p = args->buffer;
191 to_send = args->len;
192 reloffset = 0;
193 len = 0;
194 stable = to_send <= maxwritesize ? FILE_SYNC4 : UNSTABLE4;
195 committed = FILE_SYNC4;
196
197 if (to_send > maxwritesize)
198 dprintf(1, "handle_nfs41_write: writing %d in chunks of %d\n",
199 to_send, maxwritesize);
200
201 while(to_send > 0) {
202 uint32_t bytes_written = 0, chunk = min(to_send, maxwritesize);
203
204 status = nfs41_write(session, file, stateid, p, chunk,
205 args->offset + reloffset, stable, &bytes_written, &verf, &info);
206 if (status && !len)
207 goto out;
208 p += bytes_written;
209 to_send -= bytes_written;
210 len += bytes_written;
211 reloffset += bytes_written;
212 if (status) {
213 status = 0;
214 break;
215 }
216 if (!verify_write(&verf, &committed)) {
217 if (retries--) goto retry_write;
218 goto out_verify_failed;
219 }
220 }
221 if (committed != FILE_SYNC4) {
222 dprintf(1, "sending COMMIT for offset=%d and len=%d\n", args->offset, len);
223 status = nfs41_commit(session, file, args->offset, len, 1, &verf, &info);
224 if (status)
225 goto out;
226
227 if (!verify_commit(&verf)) {
228 if (retries--) goto retry_write;
229 goto out_verify_failed;
230 }
231 } else if (stable == UNSTABLE4) {
232 nfs41_file_info info;
233 bitmap4 attr_request;
234 nfs41_superblock_getattr_mask(file->fh.superblock, &attr_request);
235 status = nfs41_getattr(session, file, &attr_request, &info);
236 if (status)
237 goto out;
238 }
239 args->ctime = info.change;
240 out:
241 args->out_len = len;
242 return nfs_to_windows_error(status, ERROR_NET_WRITE_FAULT);
243
244 out_verify_failed:
245 len = 0;
246 status = NFS4ERR_IO;
247 goto out;
248 }
249
250 static int write_to_pnfs(
251 IN nfs41_upcall *upcall,
252 IN stateid_arg *stateid)
253 {
254 readwrite_upcall_args *args = &upcall->args.rw;
255 pnfs_layout_state *layout;
256 int status = NO_ERROR;
257 nfs41_file_info info = { 0 };
258
259 if (pnfs_layout_state_open(upcall->state_ref, &layout)) {
260 status = ERROR_NOT_SUPPORTED;
261 goto out;
262 }
263
264 if (pnfs_write(upcall->root_ref, upcall->state_ref, stateid, layout,
265 args->offset, args->len, args->buffer, &args->out_len, &info)) {
266 status = ERROR_WRITE_FAULT;
267 goto out;
268 }
269 args->ctime = info.change;
270 out:
271 return status;
272 }
273
274 static int handle_write(nfs41_upcall *upcall)
275 {
276 readwrite_upcall_args *args = &upcall->args.rw;
277 stateid_arg stateid;
278 uint32_t pnfs_bytes_written = 0;
279 int status;
280
281 nfs41_open_stateid_arg(upcall->state_ref, &stateid);
282
283 #ifdef PNFS_ENABLE_WRITE
284 status = write_to_pnfs(upcall, &stateid);
285 if (args->out_len) {
286 pnfs_bytes_written = args->out_len;
287 args->out_len = 0;
288
289 args->offset += pnfs_bytes_written;
290 args->buffer += pnfs_bytes_written;
291 args->len -= pnfs_bytes_written;
292
293 if (args->len == 0)
294 goto out;
295 }
296 #endif
297
298 status = write_to_mds(upcall, &stateid);
299 out:
300 args->out_len += pnfs_bytes_written;
301 return status;
302 }
303
304 static int marshall_rw(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
305 {
306 readwrite_upcall_args *args = &upcall->args.rw;
307 int status;
308 status = safe_write(&buffer, length, &args->out_len, sizeof(args->out_len));
309 if (status) goto out;
310 status = safe_write(&buffer, length, &args->ctime, sizeof(args->ctime));
311 out:
312 return status;
313 }
314
315
316 const nfs41_upcall_op nfs41_op_read = {
317 parse_rw,
318 handle_read,
319 marshall_rw
320 };
321 const nfs41_upcall_op nfs41_op_write = {
322 parse_rw,
323 handle_write,
324 marshall_rw
325 };