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
;
145 nfs41_open_stateid_arg(upcall
->state_ref
, &stateid
);
147 #ifdef PNFS_ENABLE_READ
148 status
= read_from_pnfs(upcall
, &stateid
);
150 if (status
== NO_ERROR
|| status
== ERROR_HANDLE_EOF
)
154 pnfs_bytes_read
= args
->out_len
;
157 args
->offset
+= pnfs_bytes_read
;
158 args
->buffer
+= pnfs_bytes_read
;
159 args
->len
-= pnfs_bytes_read
;
163 status
= read_from_mds(upcall
, &stateid
);
165 args
->out_len
+= pnfs_bytes_read
;
172 static int write_to_mds(
173 IN nfs41_upcall
*upcall
,
174 IN stateid_arg
*stateid
)
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
;
182 const uint32_t maxwritesize
= max_write_size(session
, &file
->fh
);
183 uint32_t to_send
, reloffset
, len
;
185 /* on write verifier mismatch, retry N times before failing */
186 uint32_t retries
= MAX_WRITE_RETRIES
;
187 nfs41_file_info info
= { 0 };
194 stable
= to_send
<= maxwritesize
? FILE_SYNC4
: UNSTABLE4
;
195 committed
= FILE_SYNC4
;
197 if (to_send
> maxwritesize
)
198 dprintf(1, "handle_nfs41_write: writing %d in chunks of %d\n",
199 to_send
, maxwritesize
);
202 uint32_t bytes_written
= 0, chunk
= min(to_send
, maxwritesize
);
204 status
= nfs41_write(session
, file
, stateid
, p
, chunk
,
205 args
->offset
+ reloffset
, stable
, &bytes_written
, &verf
, &info
);
209 to_send
-= bytes_written
;
210 len
+= bytes_written
;
211 reloffset
+= bytes_written
;
216 if (!verify_write(&verf
, &committed
)) {
217 if (retries
--) goto retry_write
;
218 goto out_verify_failed
;
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
);
227 if (!verify_commit(&verf
)) {
228 if (retries
--) goto retry_write
;
229 goto out_verify_failed
;
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
);
239 args
->ctime
= info
.change
;
242 return nfs_to_windows_error(status
, ERROR_NET_WRITE_FAULT
);
250 static int write_to_pnfs(
251 IN nfs41_upcall
*upcall
,
252 IN stateid_arg
*stateid
)
254 readwrite_upcall_args
*args
= &upcall
->args
.rw
;
255 pnfs_layout_state
*layout
;
256 int status
= NO_ERROR
;
257 nfs41_file_info info
= { 0 };
259 if (pnfs_layout_state_open(upcall
->state_ref
, &layout
)) {
260 status
= ERROR_NOT_SUPPORTED
;
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
;
269 args
->ctime
= info
.change
;
274 static int handle_write(nfs41_upcall
*upcall
)
276 readwrite_upcall_args
*args
= &upcall
->args
.rw
;
278 uint32_t pnfs_bytes_written
= 0;
281 nfs41_open_stateid_arg(upcall
->state_ref
, &stateid
);
283 #ifdef PNFS_ENABLE_WRITE
284 status
= write_to_pnfs(upcall
, &stateid
);
286 pnfs_bytes_written
= args
->out_len
;
289 args
->offset
+= pnfs_bytes_written
;
290 args
->buffer
+= pnfs_bytes_written
;
291 args
->len
-= pnfs_bytes_written
;
298 status
= write_to_mds(upcall
, &stateid
);
300 args
->out_len
+= pnfs_bytes_written
;
304 static int marshall_rw(unsigned char *buffer
, uint32_t *length
, nfs41_upcall
*upcall
)
306 readwrite_upcall_args
*args
= &upcall
->args
.rw
;
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
));
316 const nfs41_upcall_op nfs41_op_read
= {
321 const nfs41_upcall_op nfs41_op_write
= {