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
25 #include "nfs41_ops.h"
26 #include "name_cache.h"
28 #include "daemon_debug.h"
32 /* number of times to retry on write/commit verifier mismatch */
33 #define MAX_WRITE_RETRIES 6
36 const stateid4 special_read_stateid
= {0xffffffff,
37 {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
39 static int parse_rw(unsigned char *buffer
, uint32_t length
, nfs41_upcall
*upcall
)
42 readwrite_upcall_args
*args
= &upcall
->args
.rw
;
44 status
= safe_read(&buffer
, &length
, &args
->len
, sizeof(args
->len
));
46 status
= safe_read(&buffer
, &length
, &args
->offset
, sizeof(args
->offset
));
48 status
= safe_read(&buffer
, &length
, &args
->buffer
, sizeof(args
->buffer
));
51 dprintf(1, "parsing %s len=%lu offset=%llu buf=%p\n",
52 opcode2string(upcall
->opcode
), args
->len
, args
->offset
, args
->buffer
);
58 static int read_from_mds(
59 IN nfs41_upcall
*upcall
,
60 IN stateid_arg
*stateid
)
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
;
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
);
71 if (to_rcv
> maxreadsize
)
72 dprintf(1, "handle_nfs41_read: reading %d in chunks of %d\n",
76 uint32_t bytes_read
= 0, chunk
= min(to_rcv
, maxreadsize
);
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
));
84 } else if (status
&& !len
) {
85 status
= nfs_to_windows_error(status
, ERROR_NET_WRITE_FAULT
);
92 args
->offset
+= bytes_read
;
99 status
= ERROR_HANDLE_EOF
;
108 static int read_from_pnfs(
109 IN nfs41_upcall
*upcall
,
110 IN stateid_arg
*stateid
)
112 readwrite_upcall_args
*args
= &upcall
->args
.rw
;
113 pnfs_layout_state
*layout
;
114 enum pnfs_status pnfsstat
;
115 int status
= NO_ERROR
;
117 if (pnfs_layout_state_open(upcall
->state_ref
, &layout
)) {
118 status
= ERROR_NOT_SUPPORTED
;
122 pnfsstat
= pnfs_read(upcall
->root_ref
, upcall
->state_ref
, stateid
, layout
,
123 args
->offset
, args
->len
, args
->buffer
, &args
->out_len
);
128 status
= ERROR_HANDLE_EOF
;
131 status
= ERROR_READ_FAULT
;
138 static int handle_read(nfs41_upcall
*upcall
)
140 readwrite_upcall_args
*args
= &upcall
->args
.rw
;
142 ULONG pnfs_bytes_read
= 0;
143 int status
= NO_ERROR
;
149 nfs41_open_stateid_arg(upcall
->state_ref
, &stateid
);
151 #ifdef PNFS_ENABLE_READ
152 status
= read_from_pnfs(upcall
, &stateid
);
154 if (status
== NO_ERROR
|| status
== ERROR_HANDLE_EOF
)
158 pnfs_bytes_read
= args
->out_len
;
161 args
->offset
+= pnfs_bytes_read
;
162 args
->buffer
+= pnfs_bytes_read
;
163 args
->len
-= pnfs_bytes_read
;
167 status
= read_from_mds(upcall
, &stateid
);
169 /* Status returned by NFS server when session is to be renewed */
172 nfs41_session_renew(upcall
->state_ref
->session
);
173 dprintf(1, "Session renewed (read)!\n");
178 args
->out_len
+= pnfs_bytes_read
;
190 static int write_to_mds(
191 IN nfs41_upcall
*upcall
,
192 IN stateid_arg
*stateid
)
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
;
200 const uint32_t maxwritesize
= max_write_size(session
, &file
->fh
);
201 uint32_t to_send
, reloffset
, len
;
203 /* on write verifier mismatch, retry N times before failing */
204 uint32_t retries
= MAX_WRITE_RETRIES
;
205 nfs41_file_info info
= { 0 };
212 stable
= to_send
<= maxwritesize
? FILE_SYNC4
: UNSTABLE4
;
213 committed
= FILE_SYNC4
;
215 if (to_send
> maxwritesize
)
216 dprintf(1, "handle_nfs41_write: writing %d in chunks of %d\n",
217 to_send
, maxwritesize
);
220 uint32_t bytes_written
= 0, chunk
= min(to_send
, maxwritesize
);
222 status
= nfs41_write(session
, file
, stateid
, p
, chunk
,
223 args
->offset
+ reloffset
, stable
, &bytes_written
, &verf
, &info
);
227 to_send
-= bytes_written
;
228 len
+= bytes_written
;
229 reloffset
+= bytes_written
;
234 if (!verify_write(&verf
, &committed
)) {
235 if (retries
--) goto retry_write
;
236 goto out_verify_failed
;
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
);
245 if (!verify_commit(&verf
)) {
246 if (retries
--) goto retry_write
;
247 goto out_verify_failed
;
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
);
257 args
->ctime
= info
.change
;
260 return nfs_to_windows_error(status
, ERROR_NET_WRITE_FAULT
);
268 static int write_to_pnfs(
269 IN nfs41_upcall
*upcall
,
270 IN stateid_arg
*stateid
)
272 readwrite_upcall_args
*args
= &upcall
->args
.rw
;
273 pnfs_layout_state
*layout
;
274 int status
= NO_ERROR
;
275 nfs41_file_info info
= { 0 };
277 if (pnfs_layout_state_open(upcall
->state_ref
, &layout
)) {
278 status
= ERROR_NOT_SUPPORTED
;
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
;
287 args
->ctime
= info
.change
;
292 static int handle_write(nfs41_upcall
*upcall
)
294 readwrite_upcall_args
*args
= &upcall
->args
.rw
;
296 uint32_t pnfs_bytes_written
= 0;
303 nfs41_open_stateid_arg(upcall
->state_ref
, &stateid
);
305 #ifdef PNFS_ENABLE_WRITE
306 status
= write_to_pnfs(upcall
, &stateid
);
308 pnfs_bytes_written
= args
->out_len
;
311 args
->offset
+= pnfs_bytes_written
;
312 args
->buffer
+= pnfs_bytes_written
;
313 args
->len
-= pnfs_bytes_written
;
320 status
= write_to_mds(upcall
, &stateid
);
322 /* Status returned by NFS server when session is to be renewed */
325 nfs41_session_renew(upcall
->state_ref
->session
);
326 dprintf(1, "Session renewed (write)!\n");
335 args
->out_len
+= pnfs_bytes_written
;
339 static int marshall_rw(unsigned char *buffer
, uint32_t *length
, nfs41_upcall
*upcall
)
341 readwrite_upcall_args
*args
= &upcall
->args
.rw
;
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
));
351 const nfs41_upcall_op nfs41_op_read
= {
356 const nfs41_upcall_op nfs41_op_write
= {