pastebin - collaborative debugging tool
nrubsig.kpaste.net RSS

Difference between
modified post 09f by Anonymous on Wed 20th Aug 2025 08:58
original post 72f6de by Anonymous on Tue 19th Aug 2025 20:02
Download diff
Show old version | new version | both versions

    
11
diff --git a/daemon/daemon_debug.c b/daemon/daemon_debug.c
22
index 20fd420..02a0aec 100644
33
--- a/daemon/daemon_debug.c
44
+++ b/daemon/daemon_debug.c
55
@@ -485,6 +485,7 @@ const char* opcode2string(nfs41_opcodes opcode)
66
         NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES)
77
         NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_FSCTL_SET_ZERO_DATA)
88
         NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_FSCTL_DUPLICATE_DATA)
99
+        NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY)
1010
         NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_INVALID_OPCODE1)
1111
         default: break;
1212
     }
1313
diff --git a/daemon/fsctl.c b/daemon/fsctl.c
14-
index e1c4f19..a6c5048 100644
14+
index e1c4f19..fbbd8aa 100644
1515
--- a/daemon/fsctl.c
1616
+++ b/daemon/fsctl.c
1717
@@ -27,9 +27,6 @@
1818
 #include "daemon_debug.h"
1919
 #include "util.h"
2020
2121
-/* Testing only: Use NFS COPY instead of NFS CLONE */
2222
-// #define DUP_DATA_USE_NFSCOPY 1
2323
-
2424
 #define QARLVL 2 /* dprintf level for "query allocated ranges" logging */
2525
 #define SZDLVL 2 /* dprintf level for "set zero data" logging */
2626
 #define DDLVL  2 /* dprintf level for "duplicate data" logging */
2727
@@ -470,7 +467,8 @@ out:
2828
 }
2929
3030
 static
3131
-int duplicate_sparsefile(nfs41_open_state *src_state,
3232
+int duplicate_sparsefile(nfs41_opcodes opcode,
3333
+    nfs41_open_state *src_state,
3434
     nfs41_open_state *dst_state,
3535
     uint64_t srcfileoffset,
3636
     uint64_t destfileoffset,
3737
@@ -498,7 +496,8 @@ int duplicate_sparsefile(nfs41_open_state *src_state,
3838
     (void)memset(info, 0, sizeof(*info));
3939
4040
     DPRINTF(DDLVL,
4141
-        ("--> duplicate_sparsefile(src_state->path.path='%s')\n",
4242
+        ("--> duplicate_sparsefile(opcode='%s',src_state->path.path='%s')\n",
4343
+        opcode2string(opcode),
4444
         src_state->path.path));
4545
4646
     nfs41_open_stateid_arg(src_state, &src_stateid);
4747
@@ -518,9 +517,11 @@ int duplicate_sparsefile(nfs41_open_state *src_state,
4848
     if (status) {
4949
         DPRINTF(0/*DDLVL*/,
5050
             ("duplicate_sparsefile("
5151
+            "opcode='%s',"
5252
             "src_state->path.path='%s' "
5353
             "dst_state->path.path='%s'): "
5454
             "DEALLOCATE failed with '%s'\n",
5555
+            opcode2string(opcode),
5656
             src_state->path.path,
5757
             dst_state->path.path,
5858
             nfs_error_string(status)));
59-
@@ -594,46 +595,45 @@ int duplicate_sparsefile(nfs41_open_state *src_state,
59+
@@ -594,46 +595,60 @@ int duplicate_sparsefile(nfs41_open_state *src_state,
6060
             (int)data_seek_sr_eof,
6161
             (int)hole_seek_sr_eof));
6262
6363
-#ifdef DUP_DATA_USE_NFSCOPY
6464
-        nfs41_write_verf verf;
6565
-        status = nfs42_copy(session,
6666
-            src_file,
6767
-            dst_file,
6868
-            &src_stateid,
6969
-            &dst_stateid,
7070
-            data_seek_sr_offset,
7171
-            destfileoffset + (data_seek_sr_offset-srcfileoffset),
7272
-            data_size,
7373
-            &verf,
7474
-            info);
7575
-        /* FIXME: What should we do with |verf| ? Should we COMMIT this ? */
7676
-#else
7777
-        status = nfs42_clone(session,
7878
-            src_file,
7979
-            dst_file,
8080
-            &src_stateid,
8181
-            &dst_stateid,
8282
-            data_seek_sr_offset,
8383
-            destfileoffset + (data_seek_sr_offset-srcfileoffset),
8484
-            data_size,
8585
-            info);
8686
-#endif /* DUP_DATA_USE_NFSCOPY */
8787
-        if (status) {
8888
-            const char dup_op_name[] =
8989
-#ifdef DUP_DATA_USE_NFSCOPY
9090
-                "COPY";
9191
-#else
9292
-                "CLONE";
9393
-#endif /* DUP_DATA_USE_NFSCOPY */
9494
+        if (opcode == NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY) {
95-
+            nfs41_write_verf verf;
95+
+            uint64_t bytes_written;
96-
+            status = nfs42_copy(session,
96+
+            uint64_t bytestowrite = data_size;
97-
+                src_file,
97+
+            uint64_t writeoffset = 0ULL;
98-
+                dst_file,
98+
+
99-
+                &src_stateid,
99+
+            do
100-
+                &dst_stateid,
100+
+            {
101-
+                data_seek_sr_offset,
101+
+                nfs41_write_verf verf;
102-
+                destfileoffset + (data_seek_sr_offset-srcfileoffset),
102+
+                bytes_written = 0ULL;
103-
+                data_size,
103+
+
104-
+                &verf,
104+
+                status = nfs42_copy(session,
105-
+                info);
105+
+                    src_file,
106-
+            /* FIXME: What should we do with |verf| ? Should we COMMIT this ? */
106+
+                    dst_file,
107-
+        }
107+
+                    &src_stateid,
108-
+        else if (opcode == NFS41_SYSOP_FSCTL_DUPLICATE_DATA) {
108+
+                    &dst_stateid,
109-
+            status = nfs42_clone(session,
109+
+                    (data_seek_sr_offset + writeoffset),
110-
+                src_file,
110+
+                    (destfileoffset + (data_seek_sr_offset-srcfileoffset) + writeoffset),
111-
+                dst_file,
111+
+                    bytestowrite,
112-
+                &src_stateid,
112+
+                    &bytes_written,
113-
+                &dst_stateid,
113+
+                    &verf,
114-
+                data_seek_sr_offset,
114+
+                    info);
115-
+                destfileoffset + (data_seek_sr_offset-srcfileoffset),
115+
+                if (status)
116-
+                data_size,
116+
+                    break;
117-
+                info);
117+
+
118+
+                bytestowrite -= bytes_written;
118122
+        }
119-
+        else {
123+
+        else if (opcode == NFS41_SYSOP_FSCTL_DUPLICATE_DATA) {
120-
+            EASSERT(0);
124+
+            status = nfs42_clone(session,
125+
+                src_file,
121133
+        }
134+
+        else {
122137
123138
+        if (status) {
124139
             DPRINTF(0/*DDLVL*/,
125140
                 ("duplicate_sparsefile("
126141
+                "opcode='%s',"
127142
                 "src_state->path.path='%s' "
128143
                 "dst_state->path.path='%s'): "
129144
-                "'%s' failed with '%s'\n",
130145
+                "failed with '%s'\n",
131146
+                opcode2string(opcode),
132147
                 src_state->path.path,
133148
                 dst_state->path.path,
134149
-                dup_op_name,
135150
                 nfs_error_string(status)));
136151
             status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
137152
             goto out;
138-
@@ -688,25 +688,35 @@ int handle_duplicatedata(void *daemon_context,
153+
@@ -688,25 +703,35 @@ int handle_duplicatedata(void *daemon_context,
139154
             dst_state->path.path,
140155
             src_state->path.path));
141156
142157
-    /* NFS SEEK supported ? */
143158
-    if (src_session->client->root->supports_nfs42_seek == false) {
144159
-        status = ERROR_NOT_SUPPORTED;
145160
-        goto out;
146161
+    /*
147162
+     * Check whether we support the required NFS operations...
148163
+     */
149164
+    if (upcall->opcode == NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY) {
150165
+        if ((src_session->client->root->supports_nfs42_seek == false) ||
151166
+            (src_session->client->root->supports_nfs42_copy == false) ||
152167
+            (src_session->client->root->supports_nfs42_deallocate == false)) {
153168
+            status = ERROR_NOT_SUPPORTED;
154169
+            goto out;
155170
+        }
156171
     }
157172
-    /* NFS CLONE supported ? */
158173
-    if (
159174
-#ifdef DUP_DATA_USE_NFSCOPY
160175
-        src_session->client->root->supports_nfs42_copy == false
161176
-#else
162177
-        src_session->client->root->supports_nfs42_clone == false
163178
-#endif /* DUP_DATA_USE_NFSCOPY */
164179
-        ) {
165180
-        status = ERROR_NOT_SUPPORTED;
166181
-        goto out;
167182
+    else if (upcall->opcode == NFS41_SYSOP_FSCTL_DUPLICATE_DATA) {
168183
+        if ((src_session->client->root->supports_nfs42_seek == false) ||
169184
+            (src_session->client->root->supports_nfs42_clone == false) ||
170185
+            (src_session->client->root->supports_nfs42_deallocate == false)) {
171186
+            status = ERROR_NOT_SUPPORTED;
172187
+            goto out;
173188
+        }
174189
     }
175190
-    /* NFS DEALLOCATE supported ? */
176191
-    if (src_session->client->root->supports_nfs42_deallocate == false) {
177192
-        status = ERROR_NOT_SUPPORTED;
178193
+    else {
179194
+        status = ERROR_INVALID_PARAMETER;
180195
+        eprintf("duplicate_sparsefile("
181196
+            "opcode='%s',"
182197
+            "src_state->path.path='%s' "
183198
+            "dst_state->path.path='%s'): "
184199
+            "Unknown opcode.\n",
185200
+            opcode2string(upcall->opcode),
186201
+            src_state->path.path,
187202
+            dst_state->path.path);
188203
         goto out;
189204
     }
190205
191-
@@ -837,7 +847,8 @@ int handle_duplicatedata(void *daemon_context,
206+
@@ -837,7 +862,8 @@ int handle_duplicatedata(void *daemon_context,
192207
193208
     (void)memset(&info, 0, sizeof(info));
194209
195210
-    status = duplicate_sparsefile(src_state,
196211
+    status = duplicate_sparsefile(upcall->opcode,
197212
+        src_state,
198213
         dst_state,
199214
         args->srcfileoffset,
200215
         args->destfileoffset,
201-
@@ -879,3 +890,10 @@ const nfs41_upcall_op nfs41_op_duplicatedata = {
216+
@@ -879,3 +905,10 @@ const nfs41_upcall_op nfs41_op_duplicatedata = {
202217
     .marshall = marshall_duplicatedata,
203218
     .arg_size = sizeof(duplicatedata_upcall_args)
204219
 };
205220
+
206221
+const nfs41_upcall_op nfs41_op_offload_datacopy = {
207222
+    .parse = parse_duplicatedata,
208223
+    .handle = handle_duplicatedata,
209224
+    .marshall = marshall_duplicatedata,
210225
+    .arg_size = sizeof(duplicatedata_upcall_args)
211226
+};
212-
diff --git a/daemon/upcall.c b/daemon/upcall.c
227+
diff --git a/daemon/nfs41_ops.h b/daemon/nfs41_ops.h
213-
index 45e0729..53433c4 100644
228+
index dd9dcb4..bd93fbf 100644
214-
--- a/daemon/upcall.c
229+
--- a/daemon/nfs41_ops.h
215-
+++ b/daemon/upcall.c
230+
+++ b/daemon/nfs41_ops.h
216-
@@ -52,6 +52,7 @@ extern const nfs41_upcall_op nfs41_op_setacl;
231+
@@ -1299,6 +1299,7 @@ int nfs42_copy(
217-
 extern const nfs41_upcall_op nfs41_op_queryallocatedranges;
232+
     IN uint64_t src_offset,
218-
 extern const nfs41_upcall_op nfs41_op_setzerodata;
233+
     IN uint64_t dst_offset,
219-
 extern const nfs41_upcall_op nfs41_op_duplicatedata;
234+
     IN uint64_t length,
220-
+extern const nfs41_upcall_op nfs41_op_offload_datacopy;
235+
+    OUT uint64_t *bytes_written,
236+
     OUT nfs41_write_verf *writeverf,
221238
222-
 /* |_nfs41_opcodes| and |g_upcall_op_table| must be in sync! */
239+
diff --git a/daemon/nfs42_ops.c b/daemon/nfs42_ops.c
223-
 static const nfs41_upcall_op *g_upcall_op_table[] = {
240+
index 582a968..3494a3b 100644
224-
@@ -78,6 +79,7 @@ static const nfs41_upcall_op *g_upcall_op_table[] = {
241+
--- a/daemon/nfs42_ops.c
225-
     &nfs41_op_queryallocatedranges,
242+
+++ b/daemon/nfs42_ops.c
226-
     &nfs41_op_setzerodata,
243+
@@ -122,6 +122,7 @@ int nfs42_copy(
227-
     &nfs41_op_duplicatedata,
244+
     IN uint64_t src_offset,
228-
+    &nfs41_op_offload_datacopy,
245+
     IN uint64_t dst_offset,
229-
     NULL,
246+
     IN uint64_t length,
230-
     NULL
247+
+    OUT uint64_t *bytes_written,
231-
 };
248+
     OUT nfs41_write_verf *writeverf,
232-
diff --git a/include/nfs41_driver.h b/include/nfs41_driver.h
249+
     OUT nfs41_file_info *cinfo)
233-
index bf956f4..7a4ddfc 100644
250+
 {
234-
--- a/include/nfs41_driver.h
251+
@@ -205,13 +206,7 @@ int nfs42_copy(
235-
+++ b/include/nfs41_driver.h
252+
236-
@@ -88,6 +88,7 @@ typedef enum _nfs41_opcodes {
253+
     nfs41_superblock_space_changed(dst_file->fh.superblock);
237-
     NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES,
254+
238-
     NFS41_SYSOP_FSCTL_SET_ZERO_DATA,
255+
-    if (copy_res.u.resok4.response.count != length) {
239-
     NFS41_SYSOP_FSCTL_DUPLICATE_DATA,
256+
-        DPRINTF(0,
240-
+    NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY,
257+
-            ("nfs42_copy: "
241-
     NFS41_SYSOP_SHUTDOWN,
258+
-            "copy_res.u.resok4.response.count(=%lld) < length(=%lld)\n",
242-
     NFS41_SYSOP_INVALID_OPCODE1
259+
-            (long long)copy_res.u.resok4.response.count, (long long)length));
243-
 } nfs41_opcodes;
260+
-        status = ERROR_NET_WRITE_FAULT;
244-
diff --git a/sys/nfs41sys_debug.c b/sys/nfs41sys_debug.c
261+
-    }
245-
index ee95c9a..ffebee3 100644
262+
+    *bytes_written = copy_res.u.resok4.response.count;
246-
--- a/sys/nfs41sys_debug.c
263+
247-
+++ b/sys/nfs41sys_debug.c
264+
 out:
248-
@@ -683,6 +683,8 @@ const char *opcode2string(int opcode)
265+
     return status;
249-
     case NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES: return "NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES";
266+
diff --git a/daemon/upcall.c b/daemon/upcall.c
250-
     case NFS41_SYSOP_FSCTL_SET_ZERO_DATA: return "NFS41_SYSOP_FSCTL_SET_ZERO_DATA";
267+
index 45e0729..53433c4 100644
251-
     case NFS41_SYSOP_FSCTL_DUPLICATE_DATA: return "NFS41_SYSOP_FSCTL_DUPLICATE_DATA";
268+
--- a/daemon/upcall.c
252-
+    case NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY:
269+
+++ b/daemon/upcall.c
253-
+        return "NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY";
270+
@@ -52,6 +52,7 @@ extern const nfs41_upcall_op nfs41_op_setacl;
254-
     default: return "UNKNOWN";
271+
 extern const nfs41_upcall_op nfs41_op_queryallocatedranges;
255-
     }
272+
 extern const nfs41_upcall_op nfs41_op_setzerodata;
256-
 }
273+
 extern const nfs41_upcall_op nfs41_op_duplicatedata;
257-
diff --git a/sys/nfs41sys_driver.c b/sys/nfs41sys_driver.c
274+
+extern const nfs41_upcall_op nfs41_op_offload_datacopy;
267275
268-
 LONGLONG xid = 0;
276+
 /* |_nfs41_opcodes| and |g_upcall_op_table| must be in sync! */
269-
 LONG open_owner_id = 1;
277+
 static const nfs41_upcall_op *g_upcall_op_table[] = {
270-
@@ -772,6 +774,7 @@ NTSTATUS nfs41_DeallocateForFobx(
278+
@@ -78,6 +79,7 @@ static const nfs41_upcall_op *g_upcall_op_table[] = {
271-
     __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(pFobx);
279+
     &nfs41_op_queryallocatedranges,
272-
280+
     &nfs41_op_setzerodata,
273-
     nfs41_invalidate_fobx_entry(pFobx);
281+
     &nfs41_op_duplicatedata,
274-
+    nfs41_remove_offloadcontext_for_fobx(pFobx);
282+
+    &nfs41_op_offload_datacopy,
275-
283+
     NULL,
276-
     if (nfs41_fobx->acl) {
284+
     NULL
277-
         RxFreePool(nfs41_fobx->acl);
285+
 };
278-
@@ -1396,9 +1399,11 @@ NTSTATUS DriverEntry(
286+
diff --git a/include/nfs41_driver.h b/include/nfs41_driver.h
279-
     ExInitializeFastMutex(&downcallLock);
287+
index bf956f4..7a4ddfc 100644
280-
     ExInitializeFastMutex(&openOwnerLock);
288+
--- a/include/nfs41_driver.h
281-
     ExInitializeFastMutex(&fcblistLock);
289+
+++ b/include/nfs41_driver.h
282-
+    ExInitializeFastMutex(&offloadcontextLock);
290+
@@ -88,6 +88,7 @@ typedef enum _nfs41_opcodes {
283-
     InitializeListHead(&upcall.head);
291+
     NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES,
284-
     InitializeListHead(&downcall.head);
292+
     NFS41_SYSOP_FSCTL_SET_ZERO_DATA,
285-
     InitializeListHead(&openlist.head);
293+
     NFS41_SYSOP_FSCTL_DUPLICATE_DATA,
286-
+    InitializeListHead(&offloadcontext_list.head);
294+
+    NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY,
287-
 #ifdef USE_LOOKASIDELISTS_FOR_UPDOWNCALLENTRY_MEM
295+
     NFS41_SYSOP_SHUTDOWN,
288-
     /*
296+
     NFS41_SYSOP_INVALID_OPCODE1
289-
      * The |Depth| parameter is unfortunately ignored in Win10,
297+
 } nfs41_opcodes;
290-
diff --git a/sys/nfs41sys_driver.h b/sys/nfs41sys_driver.h
298+
diff --git a/sys/nfs41sys_debug.c b/sys/nfs41sys_debug.c
291-
index 597d207..3f8cc68 100644
299+
index ee95c9a..ffebee3 100644
292-
--- a/sys/nfs41sys_driver.h
300+
--- a/sys/nfs41sys_debug.c
293-
+++ b/sys/nfs41sys_driver.h
301+
+++ b/sys/nfs41sys_debug.c
294-
@@ -507,6 +507,12 @@ typedef struct _nfs41_fcb_list {
302+
@@ -683,6 +683,8 @@ const char *opcode2string(int opcode)
295-
 } nfs41_fcb_list;
303+
     case NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES: return "NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES";
296-
 nfs41_fcb_list openlist;
304+
     case NFS41_SYSOP_FSCTL_SET_ZERO_DATA: return "NFS41_SYSOP_FSCTL_SET_ZERO_DATA";
297-
305+
     case NFS41_SYSOP_FSCTL_DUPLICATE_DATA: return "NFS41_SYSOP_FSCTL_DUPLICATE_DATA";
298-
+typedef struct _nfs41_offloadcontext_list {
306+
+    case NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY:
299-
+    LIST_ENTRY head;
307+
+        return "NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY";
300-
+} nfs41_offloadcontext_list;
308+
     default: return "UNKNOWN";
301-
+extern nfs41_offloadcontext_list offloadcontext_list;
309+
     }
302-
+extern FAST_MUTEX offloadcontextLock;
310+
 }
303-
+
311+
diff --git a/sys/nfs41sys_driver.c b/sys/nfs41sys_driver.c
304-
 typedef enum _NULMRX_STORAGE_TYPE_CODES {
312+
index b079180..fd72305 100644
305-
     NTC_NFS41_DEVICE_EXTENSION      =   (NODE_TYPE_CODE)0xFC00,
313+
--- a/sys/nfs41sys_driver.c
306-
 } NFS41_STORAGE_TYPE_CODES;
314+
+++ b/sys/nfs41sys_driver.c
307-
@@ -685,6 +691,8 @@ NTSTATUS marshal_nfs41_duplicatedata(
315+
@@ -131,6 +131,8 @@ PRDBSS_DEVICE_OBJECT nfs41_dev;
308-
 NTSTATUS unmarshal_nfs41_duplicatedata(
316+
 KEVENT upcallEvent;
309-
     nfs41_updowncall_entry *cur,
317+
 FAST_MUTEX upcallLock, downcallLock, fcblistLock;
310-
     unsigned char **buf);
318+
 FAST_MUTEX openOwnerLock;
311-
+void nfs41_remove_offloadcontext_for_fobx(
319+
+FAST_MUTEX offloadcontextLock;
312-
+    IN PMRX_FOBX pFobx);
320+
+nfs41_offloadcontext_list offloadcontext_list;
313321
314-
 /* nfs41sys_ioctl.c */
322+
 LONGLONG xid = 0;
315-
 NTSTATUS nfs41_IoCtl(
323+
 LONG open_owner_id = 1;
316-
diff --git a/sys/nfs41sys_fsctl.c b/sys/nfs41sys_fsctl.c
324+
@@ -772,6 +774,7 @@ NTSTATUS nfs41_DeallocateForFobx(
317-
index c1a3f2b..5e2eb9f 100644
325+
     __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(pFobx);
327326
328-
 #include "nfs41sys_buildconfig.h"
327+
     nfs41_invalidate_fobx_entry(pFobx);
329-
@@ -863,6 +863,434 @@ NTSTATUS unmarshal_nfs41_duplicatedata(
328+
+    nfs41_remove_offloadcontext_for_fobx(pFobx);
332329
333-
+/*
330+
     if (nfs41_fobx->acl) {
334-
+ * |offloadcontext_entry| - context to store |FSCTL_OFFLOAD_READ| token
331+
         RxFreePool(nfs41_fobx->acl);
335-
+ * information
332+
@@ -1396,9 +1399,11 @@ NTSTATUS DriverEntry(
336-
+ *
333+
     ExInitializeFastMutex(&downcallLock);
337-
+ * * Notes:
334+
     ExInitializeFastMutex(&openOwnerLock);
338-
+ * - These are stored in a global list, as |FSCTL_OFFLOAD_READ|+
335+
     ExInitializeFastMutex(&fcblistLock);
339-
+ * |FSCTL_OFFLOAD_WRITE| is intended to work for intra-server and
336+
+    ExInitializeFastMutex(&offloadcontextLock);
340-
+ * inter-server copies, so |FSCTL_OFFLOAD_READ| might be done on one
337+
     InitializeListHead(&upcall.head);
341-
+ * filesystem but |FSCTL_OFFLOAD_WRITE| might be done on a different
338+
     InitializeListHead(&downcall.head);
342-
+ * one
339+
     InitializeListHead(&openlist.head);
343-
+ *
340+
+    InitializeListHead(&offloadcontext_list.head);
344-
+ * * FIXME:
341+
 #ifdef USE_LOOKASIDELISTS_FOR_UPDOWNCALLENTRY_MEM
345-
+ * - Is it legal if one user passes a token to another user, or
342+
     /*
346-
+ * should this be prevented ?
343+
      * The |Depth| parameter is unfortunately ignored in Win10,
347-
+ */
344+
diff --git a/sys/nfs41sys_driver.h b/sys/nfs41sys_driver.h
348-
+typedef struct _offloadcontext_entry
345+
index 597d207..3f8cc68 100644
349-
+{
346+
--- a/sys/nfs41sys_driver.h
350-
+    LIST_ENTRY              next;
347+
+++ b/sys/nfs41sys_driver.h
351-
+    /*
348+
@@ -507,6 +507,12 @@ typedef struct _nfs41_fcb_list {
352-
+     * r/w lock - shared access for |FSCTL_OFFLOAD_WRITE|, so one token can
349+
 } nfs41_fcb_list;
353-
+     * be used for multiple parallel writes, exclusive access for file delete
350+
 nfs41_fcb_list openlist;
354-
+     * (i.e. wait until all shared access before deleting the context)
351+
355-
+     */
352+
+typedef struct _nfs41_offloadcontext_list {
356-
+    ERESOURCE               resource;
353+
+    LIST_ENTRY head;
357-
+    STORAGE_OFFLOAD_TOKEN   token;
354+
+} nfs41_offloadcontext_list;
358-
+    PNFS41_FOBX             src_fobx;
355+
+extern nfs41_offloadcontext_list offloadcontext_list;
359-
+    ULONGLONG               src_fileoffset;
356+
+extern FAST_MUTEX offloadcontextLock;
362357
+
363-
+
358+
 typedef enum _NULMRX_STORAGE_TYPE_CODES {
359+
     NTC_NFS41_DEVICE_EXTENSION      =   (NODE_TYPE_CODE)0xFC00,
364365
+void nfs41_remove_offloadcontext_for_fobx(
365-
+    IN PMRX_FOBX pFobx)
366+
+    IN PMRX_FOBX pFobx);
366-
+{
367+
367-
+    PLIST_ENTRY pEntry;
368+
 /* nfs41sys_ioctl.c */
368-
+    offloadcontext_entry *cur, *found = NULL;
369+
 NTSTATUS nfs41_IoCtl(
369-
+    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(pFobx);
370+
diff --git a/sys/nfs41sys_fsctl.c b/sys/nfs41sys_fsctl.c
370-
+
371+
index c1a3f2b..5e2eb9f 100644
371-
+    ExAcquireFastMutexUnsafe(&offloadcontextLock);
372+
--- a/sys/nfs41sys_fsctl.c
372-
+
373+
+++ b/sys/nfs41sys_fsctl.c
373-
+    pEntry = offloadcontext_list.head.Flink;
374+
@@ -56,7 +56,7 @@
374-
+    while (!IsListEmpty(&offloadcontext_list.head)) {
375+
 #include <rx.h>
375-
+        cur = (offloadcontext_entry *)CONTAINING_RECORD(pEntry,
376+
 #include <windef.h>
376-
+            offloadcontext_entry, next);
377+
 #include <winerror.h>
377-
+        if (cur->src_fobx == nfs41_fobx) {
378+
-
378-
+            found = cur;
379+
+#include <ntddstor.h>
379-
+            break;
380+
 #include <Ntstrsafe.h>
380-
+        }
381+
381-
+        if (pEntry->Flink == &offloadcontext_list.head) {
382+
 #include "nfs41sys_buildconfig.h"
382-
+            break;
383+
@@ -863,6 +863,434 @@ NTSTATUS unmarshal_nfs41_duplicatedata(
383-
+        }
384+
     return status;
384-
+        pEntry = pEntry->Flink;
385+
 }
385-
+    }
386+
386-
+
387+
+/*
387-
+    if (found) {
388+
+ * |offloadcontext_entry| - context to store |FSCTL_OFFLOAD_READ| token
388-
+        DbgP("nfs41_remove_offloadcontext(pFobx=0x%p): "
389+
+ * information
389-
+            "removing found=0x%p\n",
390+
+ *
390-
+            pFobx,
391+
+ * * Notes:
391-
+            found);
392+
+ * - These are stored in a global list, as |FSCTL_OFFLOAD_READ|+
392-
+
393+
+ * |FSCTL_OFFLOAD_WRITE| is intended to work for intra-server and
393-
+        /* Wait for any shared access in |nfs41_OffloadWrite()| to finish */
394+
+ * inter-server copies, so |FSCTL_OFFLOAD_READ| might be done on one
394-
+        (void)ExAcquireResourceExclusiveLite(&found->resource, TRUE);
395+
+ * filesystem but |FSCTL_OFFLOAD_WRITE| might be done on a different
395-
+        ExReleaseResourceLite(&found->resource);
396+
+ * one
396-
+
397+
+ *
397-
+        RemoveEntryList(&found->next);
398+
+ * * FIXME:
398-
+
399+
+ * - Is it legal if one user passes a token to another user, or
399-
+        (void)ExDeleteResourceLite(&found->resource);
400+
+ * should this be prevented ?
400-
+        RxFreePool(found);
401+
+ */
401-
+    }
402+
+typedef struct _offloadcontext_entry
402-
+    else {
403+
+{
403-
+        DbgP("nfs41_remove_offloadcontext(pFobx=0x%p): Nothing found.\n",
404+
+    LIST_ENTRY              next;
404-
+            pFobx);
405+
+    /*
405-
+    }
406+
+     * r/w lock - shared access for |FSCTL_OFFLOAD_WRITE|, so one token can
407+
+     * be used for multiple parallel writes, exclusive access for file delete
406416
+
409417
+
410-
+static
418+
+void nfs41_remove_offloadcontext_for_fobx(
411-
+offloadcontext_entry *nfs41_find_offloadcontext_acquireshared(
419+
+    IN PMRX_FOBX pFobx)
413420
+{
414421
+    PLIST_ENTRY pEntry;
415422
+    offloadcontext_entry *cur, *found = NULL;
423+
+    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(pFobx);
416424
+
417425
+    ExAcquireFastMutexUnsafe(&offloadcontextLock);
418426
+
419427
+    pEntry = offloadcontext_list.head.Flink;
420428
+    while (!IsListEmpty(&offloadcontext_list.head)) {
421429
+        cur = (offloadcontext_entry *)CONTAINING_RECORD(pEntry,
422430
+            offloadcontext_entry, next);
423-
+        if (cur == unvalidated_oce) {
431+
+        if (cur->src_fobx == nfs41_fobx) {
424432
+            found = cur;
425433
+            break;
426434
+        }
427435
+        if (pEntry->Flink == &offloadcontext_list.head) {
428436
+            break;
429437
+        }
430438
+        pEntry = pEntry->Flink;
431439
+    }
432440
+
433441
+    if (found) {
434-
+        DbgP("nfs41_find_offloadcontext_acquireshared(unvalidated_oce=0x%p): "
442+
+        DbgP("nfs41_remove_offloadcontext(pFobx=0x%p): "
435-
+            "found=0x%p\n",
443+
+            "removing found=0x%p\n",
436-
+            unvalidated_oce);
444+
+            pFobx,
445+
+            found);
437446
+
438-
+        (void)ExAcquireSharedStarveExclusive(&found->resource, TRUE);
447+
+        /* Wait for any shared access in |nfs41_OffloadWrite()| to finish */
439-
+        ExReleaseFastMutexUnsafe(&offloadcontextLock);
448+
+        (void)ExAcquireResourceExclusiveLite(&found->resource, TRUE);
440-
+        return found;
449+
+        ExReleaseResourceLite(&found->resource);
450+
+
441455
+    }
442456
+    else {
443-
+        DbgP("nfs41_find_offloadcontext_acquireshared(unvalidated_oce=0x%p): "
457+
+        DbgP("nfs41_remove_offloadcontext(pFobx=0x%p): Nothing found.\n",
444-
+            "Nothing found.\n",
458+
+            pFobx);
448459
+    }
450460
+
461+
+    ExReleaseFastMutexUnsafe(&offloadcontextLock);
451464
+static
452-
+NTSTATUS nfs41_OffloadRead(
465+
+offloadcontext_entry *nfs41_find_offloadcontext_acquireshared(
453-
+    IN OUT PRX_CONTEXT RxContext)
466+
+    IN offloadcontext_entry *unvalidated_oce)
454467
+{
455-
+    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
468+
+    PLIST_ENTRY pEntry;
456-
+    __notnull XXCTL_LOWIO_COMPONENT *FsCtl =
469+
+    offloadcontext_entry *cur, *found = NULL;
459470
+
460-
+    DbgEn();
471+
+    ExAcquireFastMutexUnsafe(&offloadcontextLock);
461472
+
462-
+    RxContext->IoStatusBlock.Information = 0;
473+
+    pEntry = offloadcontext_list.head.Flink;
463-
+
474+
+    while (!IsListEmpty(&offloadcontext_list.head)) {
464-
+    if (FsCtl->pInputBuffer == NULL) {
475+
+        cur = (offloadcontext_entry *)CONTAINING_RECORD(pEntry,
465-
+        status = STATUS_INVALID_USER_BUFFER;
476+
+            offloadcontext_entry, next);
466-
+        goto out;
477+
+        if (cur == unvalidated_oce) {
478+
+            found = cur;
467485
+    }
468486
+
469-
+    if (FsCtl->pOutputBuffer == NULL) {
487+
+    if (found) {
470-
+        status = STATUS_INVALID_USER_BUFFER;
488+
+        DbgP("nfs41_find_offloadcontext_acquireshared(unvalidated_oce=0x%p): "
471-
+        goto out;
489+
+            "found=0x%p\n",
490+
+            unvalidated_oce);
473491
+
474-
+    if (FsCtl->InputBufferLength < sizeof(FSCTL_OFFLOAD_READ_INPUT)) {
492+
+        (void)ExAcquireSharedStarveExclusive(&found->resource, TRUE);
475-
+        DbgP("nfs41_OffloadRead: "
493+
+        ExReleaseFastMutexUnsafe(&offloadcontextLock);
476-
+            "buffer too small for FSCTL_OFFLOAD_READ_INPUT\n");
494+
+        return found;
479495
+    }
480-
+    if (FsCtl->OutputBufferLength < sizeof(FSCTL_OFFLOAD_READ_OUTPUT)) {
496+
+    else {
481-
+        DbgP("nfs41_OffloadRead: "
497+
+        DbgP("nfs41_find_offloadcontext_acquireshared(unvalidated_oce=0x%p): "
482-
+            "buffer too small for FSCTL_OFFLOAD_READ_OUTPUT\n");
498+
+            "Nothing found.\n",
483-
+        status = STATUS_BUFFER_TOO_SMALL;
499+
+            unvalidated_oce);
484-
+        goto out;
500+
+        ExReleaseFastMutexUnsafe(&offloadcontextLock);
501+
+        return NULL;
485502
+    }
503+
+}
486504
+
487-
+    PFSCTL_OFFLOAD_READ_INPUT ori =
505+
+static
488-
+        (PFSCTL_OFFLOAD_READ_INPUT)FsCtl->pInputBuffer;
506+
+NTSTATUS nfs41_OffloadRead(
489-
+    PFSCTL_OFFLOAD_READ_OUTPUT oro =
507+
+    IN OUT PRX_CONTEXT RxContext)
490-
+        (PFSCTL_OFFLOAD_READ_OUTPUT)FsCtl->pOutputBuffer;
508+
+{
509+
+    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
491513
+
492-
+    DbgP("nfs41_OffloadRead: "
514+
+    DbgEn();
501515
+
502-
+    offloadcontext_entry *oce = RxAllocatePoolWithTag(NonPagedPoolNx,
516+
+    RxContext->IoStatusBlock.Information = 0;
508517
+
509-
+    DbgP("nfs41_OffloadRead: oce=0x%p\n", oce);
518+
+    if (FsCtl->pInputBuffer == NULL) {
519+
+        status = STATUS_INVALID_USER_BUFFER;
510522
+
511-
+    (void)ExInitializeResourceLite(&oce->resource);
523+
+    if (FsCtl->pOutputBuffer == NULL) {
524+
+        status = STATUS_INVALID_USER_BUFFER;
512527
+
513-
+    (void)memset(&oce->token, 0, sizeof(oce->token));
528+
+    if (FsCtl->InputBufferLength < sizeof(FSCTL_OFFLOAD_READ_INPUT)) {
514-
+    /* Add safeguard to |TokenType| */
529+
+        DbgP("nfs41_OffloadRead: "
515-
+    oce->token.TokenType[0] = 'N';
530+
+            "buffer too small for FSCTL_OFFLOAD_READ_INPUT\n");
516-
+    oce->token.TokenType[1] = 'F';
531+
+        status = STATUS_BUFFER_TOO_SMALL;
517-
+    oce->token.TokenType[2] = 'S';
532+
+        goto out;
518-
+    oce->token.TokenType[3] = '4';
533+
+    }
519-
+    /* FIXME: What about the endianness of |TokenIdLength| ? */
534+
+    if (FsCtl->OutputBufferLength < sizeof(FSCTL_OFFLOAD_READ_OUTPUT)) {
520-
+    *((USHORT *)(&oce->token.TokenIdLength[0])) =
535+
+        DbgP("nfs41_OffloadRead: "
521-
+        STORAGE_OFFLOAD_TOKEN_ID_LENGTH;
536+
+            "buffer too small for FSCTL_OFFLOAD_READ_OUTPUT\n");
522-
+    *((void **)(&oce->token.Token[0])) = oce;
537+
+        status = STATUS_BUFFER_TOO_SMALL;
523-
+    oce->src_fobx = nfs41_fobx;
538+
+        goto out;
524-
+    oce->src_fileoffset = ori->FileOffset;
539+
+    }
526540
+
527-
+    oro->Size = sizeof(FSCTL_OFFLOAD_READ_OUTPUT);
541+
+    PFSCTL_OFFLOAD_READ_INPUT ori =
528-
+    oro->Flags = 0;
542+
+        (PFSCTL_OFFLOAD_READ_INPUT)FsCtl->pInputBuffer;
529-
+    oro->TransferLength = ori->CopyLength;
543+
+    PFSCTL_OFFLOAD_READ_OUTPUT oro =
530-
+    (void)memcpy(&oro->Token[0], &oce->token, sizeof(oce->token));
544+
+        (PFSCTL_OFFLOAD_READ_OUTPUT)FsCtl->pOutputBuffer;
531545
+
532-
+    nfs41_AddEntry(offloadcontextLock, offloadcontext_list, oce);
546+
+    DbgP("nfs41_OffloadRead: "
547+
+        "ori->(Size=%lu, Flags=0x%lx, TokenTimeToLive=%lu, Reserved=%lu, "
533555
+
534-
+    RxContext->CurrentIrp->IoStatus.Status = status = STATUS_SUCCESS;
556+
+    offloadcontext_entry *oce = RxAllocatePoolWithTag(NonPagedPoolNx,
535-
+    RxContext->InformationToReturn = sizeof(FSCTL_OFFLOAD_READ_OUTPUT);
557+
+        sizeof(offloadcontext_entry), NFS41_MM_POOLTAG);
558+
+    if (oce == NULL) {
536562
+
537-
+out:
563+
+    DbgP("nfs41_OffloadRead: oce=0x%p\n", oce);
541564
+
542-
+static
565+
+    (void)ExInitializeResourceLite(&oce->resource);
550566
+
551-
+    /* access checks */
567+
+    (void)memset(&oce->token, 0, sizeof(oce->token));
552-
+    if (VNetRootContext->read_only) {
568+
+    /* Add safeguard to |TokenType| */
553-
+        status = STATUS_MEDIA_WRITE_PROTECTED;
569+
+    oce->token.TokenType[0] = 'N';
554-
+        goto out;
570+
+    oce->token.TokenType[1] = 'F';
555-
+    }
571+
+    oce->token.TokenType[2] = 'S';
556-
+    if (!(SrvOpen->DesiredAccess &
572+
+    oce->token.TokenType[3] = '4';
557-
+        (FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES))) {
573+
+    /* FIXME: What about the endianness of |TokenIdLength| ? */
558-
+        status = STATUS_ACCESS_DENIED;
574+
+    *((USHORT *)(&oce->token.TokenIdLength[0])) =
559-
+        goto out;
575+
+        STORAGE_OFFLOAD_TOKEN_ID_LENGTH;
560-
+    }
576+
+    *((void **)(&oce->token.Token[0])) = oce;
577+
+    oce->src_fobx = nfs41_fobx;
561580
+
562-
+out:
581+
+    oro->Size = sizeof(FSCTL_OFFLOAD_READ_OUTPUT);
563-
+    return status;
582+
+    oro->Flags = 0;
564-
+}
583+
+    oro->TransferLength = ori->CopyLength;
584+
+    (void)memcpy(&oro->Token[0], &oce->token, sizeof(oce->token));
565585
+
566-
+static
586+
+    nfs41_AddEntry(offloadcontextLock, offloadcontext_list, oce);
567-
+NTSTATUS nfs41_OffloadWrite(
587+
+
568-
+    IN OUT PRX_CONTEXT RxContext)
588+
+    RxContext->CurrentIrp->IoStatus.Status = status = STATUS_SUCCESS;
569-
+{
589+
+    RxContext->InformationToReturn = sizeof(FSCTL_OFFLOAD_READ_OUTPUT);
570-
+    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
590+
+
571-
+    nfs41_updowncall_entry *entry = NULL;
591+
+out:
592+
+    DbgEx();
572601
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
573-
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
602+
+    __notnull PNFS41_V_NET_ROOT_EXTENSION VNetRootContext =
574603
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
581604
+
582-
+    struct {
605+
+    /* access checks */
583-
+        LONGLONG    srcfileoffset;
606+
+    if (VNetRootContext->read_only) {
584-
+        LONGLONG    destfileoffset;
607+
+        status = STATUS_MEDIA_WRITE_PROTECTED;
585-
+        LONGLONG    bytecount;
608+
+        goto out;
586-
+    } dd;
609+
+    }
610+
+    if (!(SrvOpen->DesiredAccess &
587615
+
588-
+    DbgEn();
616+
+out:
617+
+    return status;
589619
+
590-
+    LONGLONG io_delay;
620+
+static
591-
+    RxContext->IoStatusBlock.Information = 0;
621+
+NTSTATUS nfs41_OffloadWrite(
592-
+
622+
+    IN OUT PRX_CONTEXT RxContext)
593-
+    status = check_nfs41_offload_write_args(RxContext);
623+
+{
594-
+    if (status)
624+
+    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
595-
+        goto out;
625+
+    nfs41_updowncall_entry *entry = NULL;
626+
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
596635
+
597-
+    if (FsCtl->pInputBuffer == NULL) {
636+
+    struct {
598-
+        status = STATUS_INVALID_USER_BUFFER;
637+
+        LONGLONG    srcfileoffset;
599-
+        goto out;
638+
+        LONGLONG    destfileoffset;
600-
+    }
639+
+        LONGLONG    bytecount;
601-
+    if (FsCtl->pOutputBuffer == NULL) {
640+
+    } dd;
602-
+        status = STATUS_INVALID_USER_BUFFER;
641+
+
642+
+    DbgEn();
603649
+        goto out;
650+
+
604654
+    }
655+
+    if (FsCtl->pOutputBuffer == NULL) {
605659
+    if (FsCtl->InputBufferLength < sizeof(FSCTL_OFFLOAD_WRITE_INPUT)) {
606660
+        DbgP("nfs41_OffloadWrite: "
607661
+            "buffer too small for FSCTL_OFFLOAD_WRITE_INPUT\n");
608662
+        status = STATUS_BUFFER_TOO_SMALL;
609663
+        goto out;
610664
+    }
611665
+    if (FsCtl->OutputBufferLength < sizeof(FSCTL_OFFLOAD_WRITE_OUTPUT)) {
612666
+        DbgP("nfs41_OffloadWrite: "
613667
+            "buffer too small for FSCTL_OFFLOAD_WRITE_OUTPUT\n");
614668
+        status = STATUS_BUFFER_TOO_SMALL;
615669
+        goto out;
616670
+    }
617671
+
618672
+    PFSCTL_OFFLOAD_WRITE_INPUT owi =
619673
+        (PFSCTL_OFFLOAD_WRITE_INPUT)FsCtl->pInputBuffer;
620674
+    PFSCTL_OFFLOAD_WRITE_OUTPUT owo =
621675
+        (PFSCTL_OFFLOAD_WRITE_OUTPUT)FsCtl->pOutputBuffer;
622676
+
623677
+    offloadcontext_entry *unvalidated_src_oce;
624678
+
625679
+    /*
626680
+     * Peel |offloadcontext_entry| pointer from token...
627681
+     */
628682
+    unvalidated_src_oce =
629683
+        *((void **)(&(((STORAGE_OFFLOAD_TOKEN *)(&owi->Token[0]))->Token[0])));
630684
+    DbgP("nfs41_OffloadWrite: "
631685
+        "unvalidated_src_oce=0x%p\n", unvalidated_src_oce);
632686
+
633687
+    /*
634688
+     * ... and validate it (and take a shared lock if validation was
635689
+     * successful, so nobody can delete the context while we use it)!
636690
+     */
637691
+    src_oce = nfs41_find_offloadcontext_acquireshared(unvalidated_src_oce);
638692
+    if (src_oce == NULL) {
639693
+        DbgP("nfs41_OffloadWrite: "
640694
+            "nfs41_find_offloadcontext_acquireshared() failed\n");
641695
+        status = STATUS_INVALID_PARAMETER;
642696
+        goto out;
643697
+    }
644698
+
645699
+    DbgP("nfs41_OffloadWrite: src_oce=0x%p\n", src_oce);
646700
+
647701
+    /* Check safeguard... */
648702
+    if ((src_oce->token.TokenType[0] != 'N') ||
649703
+        (src_oce->token.TokenType[1] != 'F') ||
650704
+        (src_oce->token.TokenType[2] != 'S') ||
651705
+        (src_oce->token.TokenType[3] != '4')) {
652706
+        DbgP("nfs41_OffloadWrite: "
653707
+            "token in src_oce=0x%p not a 'NFS4' token\n",
654708
+            src_oce);
655709
+        status = STATUS_INVALID_PARAMETER;
656710
+        goto out;
657711
+    }
658712
+
659713
+    /*
660714
+     * FIXME: We should validate the length passed as
661715
+     * |FSCTL_OFFLOAD_READ_INPUT.CopyLength| here better, because it is
662716
+     * also used as some kind of access control to different parts of a
663717
+     * file
664718
+     */
665719
+    dd.srcfileoffset    = src_oce->src_fileoffset + owi->TransferOffset;
666720
+    dd.destfileoffset   = owi->FileOffset;
667721
+    dd.bytecount        = owi->CopyLength;
668722
+
669723
+    DbgP("nfs41_OffloadWrite: "
670724
+        "dd=(srcfileoffset=%lld,"
671725
+        "destfileoffset=%lld,"
672726
+        "bytecount=%lld)\n",
673727
+        (long long)dd.srcfileoffset,
674728
+        (long long)dd.destfileoffset,
675729
+        (long long)dd.bytecount);
676730
+
677731
+    if (dd.bytecount == 0LL) {
678732
+        status = STATUS_SUCCESS;
679733
+        goto out;
680734
+    }
681735
+
682736
+    PNFS41_FOBX nfs41_src_fobx = src_oce->src_fobx;
683737
+    if (!nfs41_src_fobx) {
684738
+        DbgP("nfs41_OffloadWrite: No nfs41_src_fobx\n");
685739
+        status = STATUS_INVALID_PARAMETER;
686740
+        goto out;
687741
+    }
688742
+
689743
+    /*
690744
+     * Disable caching because NFSv4.2 COPY is basically a
691745
+     * "write" operation. AFAIK we should flush the cache and wait
692746
+     * for the kernel lazy writer (which |RxChangeBufferingState()|
693747
+     * AFAIK does) before doing the COPY, to avoid that we
694748
+     * have outstanding writes in the kernel cache at the same
695749
+     * location where the COPY should do it's work
696750
+     */
697751
+    ULONG flag = DISABLE_CACHING;
698752
+    DbgP("nfs41_OffloadWrite: disableing caching for file '%wZ'\n",
699753
+        SrvOpen->pAlreadyPrefixedName);
700754
+    RxChangeBufferingState((PSRV_OPEN)SrvOpen, ULongToPtr(flag), 1);
701755
+
702756
+    status = nfs41_UpcallCreate(NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY,
703757
+        &nfs41_fobx->sec_ctx,
704758
+        pVNetRootContext->session,
705759
+        nfs41_fobx->nfs41_open_state,
706760
+        pNetRootContext->nfs41d_version,
707761
+        SrvOpen->pAlreadyPrefixedName,
708762
+        &entry);
709763
+
710764
+    if (status)
711765
+        goto out;
712766
+
713767
+    entry->u.DuplicateData.src_state = nfs41_src_fobx->nfs41_open_state;
714768
+    entry->u.DuplicateData.srcfileoffset = dd.srcfileoffset;
715769
+    entry->u.DuplicateData.destfileoffset = dd.destfileoffset;
716770
+    entry->u.DuplicateData.bytecount = dd.bytecount;
717771
+
718772
+    /* Add extra timeout depending on file size */
719773
+    io_delay = pVNetRootContext->timeout +
720774
+        EXTRA_TIMEOUT_PER_BYTE(entry->u.DuplicateData.bytecount);
721775
+
722776
+    status = nfs41_UpcallWaitForReply(entry, io_delay);
723777
+    if (status) {
724778
+        /* Timeout - |nfs41_downcall()| will free |entry|+contents */
725779
+        entry = NULL;
726780
+        goto out;
727781
+    }
728782
+
729783
+    if (!entry->status) {
730784
+        DbgP("nfs41_OffloadWrite: SUCCESS\n");
731785
+
732786
+        owo->Size = sizeof(FSCTL_OFFLOAD_READ_OUTPUT);
733787
+        owo->Flags = 0;
734788
+        owo->LengthWritten = dd.bytecount;
735789
+
736790
+        RxContext->CurrentIrp->IoStatus.Status = status = STATUS_SUCCESS;
737791
+        RxContext->InformationToReturn = sizeof(FSCTL_OFFLOAD_READ_OUTPUT);
738792
+    }
739793
+    else {
740794
+        DbgP("nfs41_OffloadWrite: "
741795
+            "FAILURE, entry->status=0x%lx\n", entry->status);
742796
+        status = map_setfile_error(entry->status);
743797
+        RxContext->CurrentIrp->IoStatus.Status = status;
744798
+        RxContext->IoStatusBlock.Information = 0;
745799
+    }
746800
+
747801
+out:
748802
+    if (src_oce) {
749803
+        /* Release resource we obtained in shared mode */
750804
+        ExReleaseResourceLite(&src_oce->resource);
751805
+    }
752806
+
753807
+    if (entry) {
754808
+        nfs41_UpcallDestroy(entry);
755809
+    }
756810
+
757811
+    DbgEx();
758812
+    return status;
759813
+}
760814
+
761815
 NTSTATUS nfs41_FsCtl(
762816
     IN OUT PRX_CONTEXT RxContext)
763817
 {
764818
@@ -895,6 +1323,12 @@ NTSTATUS nfs41_FsCtl(
765819
     case FSCTL_DUPLICATE_EXTENTS_TO_FILE:
766820
         status = nfs41_DuplicateData(RxContext);
767821
         break;
768822
+    case FSCTL_OFFLOAD_READ:
769823
+        status = nfs41_OffloadRead(RxContext);
770824
+        break;
771825
+    case FSCTL_OFFLOAD_WRITE:
772826
+        status = nfs41_OffloadWrite(RxContext);
773827
+        break;
774828
     default:
775829
         break;
776830
     }
777831
diff --git a/sys/nfs41sys_updowncall.c b/sys/nfs41sys_updowncall.c
778832
index cf5be35..fc523a4 100644
779833
--- a/sys/nfs41sys_updowncall.c
780834
+++ b/sys/nfs41sys_updowncall.c
781835
@@ -321,6 +321,7 @@ NTSTATUS handle_upcall(
782836
             pbOut, cbOut, len);
783837
         break;
784838
     case NFS41_SYSOP_FSCTL_DUPLICATE_DATA:
785839
+    case NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY:
786840
         status = marshal_nfs41_duplicatedata(entry,
787841
             pbOut, cbOut, len);
788842
         break;
789843
@@ -785,6 +786,7 @@ NTSTATUS nfs41_downcall(
790844
             unmarshal_nfs41_setzerodata(cur, &buf);
791845
             break;
792846
         case NFS41_SYSOP_FSCTL_DUPLICATE_DATA:
793847
+        case NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY:
794848
             unmarshal_nfs41_duplicatedata(cur, &buf);
795849
             break;
796850
         }
797851

Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at