Reactos
at master 325 lines 9.2 kB view raw
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 36const stateid4 special_read_stateid = {0xffffffff, 37 {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; 38 39static 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); 53out: 54 return status; 55} 56 57/* NFS41_READ */ 58static 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 } 103out: 104 args->out_len = len; 105 return status; 106} 107 108static 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 } 134out: 135 return status; 136} 137 138static 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; 166out: 167 return status; 168} 169 170 171/* NFS41_WRITE */ 172static 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 189retry_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; 240out: 241 args->out_len = len; 242 return nfs_to_windows_error(status, ERROR_NET_WRITE_FAULT); 243 244out_verify_failed: 245 len = 0; 246 status = NFS4ERR_IO; 247 goto out; 248} 249 250static 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; 270out: 271 return status; 272} 273 274static 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); 299out: 300 args->out_len += pnfs_bytes_written; 301 return status; 302} 303 304static 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)); 311out: 312 return status; 313} 314 315 316const nfs41_upcall_op nfs41_op_read = { 317 parse_rw, 318 handle_read, 319 marshall_rw 320}; 321const nfs41_upcall_op nfs41_op_write = { 322 parse_rw, 323 handle_write, 324 marshall_rw 325};