Print this page
5513 KM_NORMALPRI should be documented in kmem_alloc(9f) and kmem_cache_create(9f) man pages
14465 Present KM_NOSLEEP_LAZY as documented interface
Change-Id: I002ec28ddf390650f1fcba1ca94f6abfdb241439
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/uts/common/fs/smbsrv/smb2_fsctl_copychunk.c
+++ new/usr/src/uts/common/fs/smbsrv/smb2_fsctl_copychunk.c
1 1 /*
2 2 * This file and its contents are supplied under the terms of the
3 3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 4 * You may only use this file in accordance with the terms of version
5 5 * 1.0 of the CDDL.
6 6 *
7 7 * A full copy of the text of the CDDL should have accompanied this
8 8 * source. A copy of the CDDL is also available via the Internet at
9 9 * http://www.illumos.org/license/CDDL.
10 10 */
11 11
12 12 /*
13 13 * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
14 14 */
15 15
16 16 /*
17 17 * Support functions for smb2_ioctl/fsctl codes:
18 18 * FSCTL_SRV_COPYCHUNK
19 19 * FSCTL_SRV_COPYCHUNK_WRITE
20 20 * (and related)
21 21 */
22 22
23 23 #include <smbsrv/smb2_kproto.h>
24 24 #include <smbsrv/smb_fsops.h>
25 25 #include <smb/winioctl.h>
26 26
27 27 typedef struct chunk {
28 28 uint64_t src_off;
29 29 uint64_t dst_off;
30 30 uint32_t length;
31 31 uint32_t _reserved;
32 32 } chunk_t;
33 33
34 34 struct copychunk_resp {
35 35 uint32_t ChunksWritten;
36 36 uint32_t ChunkBytesWritten;
37 37 uint32_t TotalBytesWritten;
38 38 };
39 39
40 40 typedef struct copychunk_args {
41 41 smb_attr_t src_attr;
42 42 void *buffer;
43 43 size_t bufsize;
44 44 uint32_t ccnt;
45 45 chunk_t cvec[1]; /* actually longer */
46 46 } copychunk_args_t;
47 47
48 48 uint32_t smb2_copychunk_max_cnt = 256;
49 49 uint32_t smb2_copychunk_max_seg = (1<<20); /* 1M, == smb2_max_rwsize */
50 50 uint32_t smb2_copychunk_max_total = (1<<24); /* 16M */
51 51
52 52 static uint32_t smb2_fsctl_copychunk_decode(smb_request_t *, mbuf_chain_t *);
53 53 static uint32_t smb2_fsctl_copychunk_array(smb_request_t *, smb_ofile_t *,
54 54 struct copychunk_resp *);
55 55 static uint32_t smb2_fsctl_copychunk_aapl(smb_request_t *, smb_ofile_t *,
56 56 struct copychunk_resp *);
57 57 static uint32_t smb2_fsctl_copychunk_1(smb_request_t *, smb_ofile_t *,
58 58 struct chunk *);
59 59 static int smb2_fsctl_copychunk_meta(smb_request_t *, smb_ofile_t *);
60 60
61 61 /*
62 62 * FSCTL_SRV_COPYCHUNK
63 63 * FSCTL_SRV_COPYCHUNK_WRITE
64 64 *
65 65 * Copies from a source file identified by a "resume key"
66 66 * (previously returned by FSCTL_SRV_REQUEST_RESUME_KEY)
67 67 * to the file on which the ioctl is issues.
68 68 *
69 69 * The fsctl appears to _always_ respond with a data payload
70 70 * (struct copychunk_resp), even on fatal errors. Note that
71 71 * smb2_ioctl also has special handling to allow that.
72 72 */
73 73 uint32_t
74 74 smb2_fsctl_copychunk(smb_request_t *sr, smb_fsctl_t *fsctl)
75 75 {
76 76 struct copychunk_resp ccr;
77 77 smb_ofile_t *dst_of = sr->fid_ofile;
78 78 smb_ofile_t *src_of = NULL;
79 79 copychunk_args_t *args = NULL;
80 80 smb2fid_t smb2fid;
81 81 uint32_t status = NT_STATUS_INVALID_PARAMETER;
82 82 uint32_t desired_access; /* for dest */
83 83 uint32_t chunk_cnt;
84 84 int rc;
85 85 boolean_t aapl_copyfile = B_FALSE;
86 86
87 87 bzero(&ccr, sizeof (ccr));
88 88 if (fsctl->MaxOutputResp < sizeof (ccr)) {
89 89 status = NT_STATUS_INVALID_PARAMETER;
90 90 goto out;
91 91 }
92 92
93 93 /*
94 94 * Make sure dst_of is open on a regular file, and
95 95 * granted access is sufficient for this operation.
96 96 * FSCTL_SRV_COPYCHUNK requires READ+WRITE
97 97 * FSCTL_SRV_COPYCHUNK_WRITE just WRITE
98 98 */
99 99 if (!smb_node_is_file(dst_of->f_node)) {
100 100 status = NT_STATUS_ACCESS_DENIED;
101 101 goto out;
102 102 }
103 103 desired_access = FILE_WRITE_DATA;
104 104 if (fsctl->CtlCode == FSCTL_SRV_COPYCHUNK)
105 105 desired_access |= FILE_READ_DATA;
106 106 status = smb_ofile_access(dst_of, dst_of->f_cr, desired_access);
107 107 if (status != NT_STATUS_SUCCESS)
108 108 goto out;
109 109
110 110 /*
111 111 * Decode the resume key (src file ID) and length of the
112 112 * "chunks" array. Note the resume key is 24 bytes of
113 113 * opaque data from FSCTL_SRV_REQUEST_RESUME_KEY, but
114 114 * here know it's an smb2fid plus 8 bytes of padding.
115 115 */
116 116 rc = smb_mbc_decodef(
117 117 fsctl->in_mbc, "qq8.l4.",
118 118 &smb2fid.persistent, /* q */
119 119 &smb2fid.temporal, /* q */
120 120 /* pad 8. */
121 121 &chunk_cnt); /* l */
122 122 /* reserved 4. */
123 123 if (rc != 0) {
124 124 status = NT_STATUS_INVALID_PARAMETER;
125 125 goto out;
126 126 }
127 127
128 128 /*
129 129 * Lookup the source ofile using the resume key,
130 130 * which smb2_fsctl_get_resume_key encoded as an
131 131 * smb2fid_t. Similar to smb2sr_lookup_fid(),
132 132 * but different error code.
133 133 */
134 134 src_of = smb_ofile_lookup_by_fid(sr, (uint16_t)smb2fid.temporal);
135 135 if (src_of == NULL ||
136 136 src_of->f_persistid != smb2fid.persistent) {
137 137 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
138 138 goto out;
139 139 }
140 140
141 141 /*
142 142 * Make sure src_of is open on a regular file, and
143 143 * granted access includes READ_DATA
144 144 */
145 145 if (!smb_node_is_file(src_of->f_node)) {
146 146 status = NT_STATUS_ACCESS_DENIED;
147 147 goto out;
148 148 }
149 149 status = smb_ofile_access(src_of, src_of->f_cr, FILE_READ_DATA);
150 150 if (status != NT_STATUS_SUCCESS)
151 151 goto out;
152 152
153 153 /*
154 154 * Before decoding the chunks array, check the size. Note:
155 155 * When we offer the AAPL extensions, MacOS clients assume
156 156 * they can use chunk_cnt==0 to mean "copy the whole file".
157 157 */
158 158 if (chunk_cnt == 0) {
159 159 if ((sr->session->s_flags & SMB_SSN_AAPL_CCEXT) != 0) {
160 160 aapl_copyfile = B_TRUE;
161 161 } else {
162 162 status = NT_STATUS_INVALID_PARAMETER;
163 163 goto out;
164 164 }
165 165 }
166 166 if (chunk_cnt > smb2_copychunk_max_cnt) {
167 167 status = NT_STATUS_INVALID_PARAMETER;
168 168 goto out;
169 169 }
170 170
171 171 /*
172 172 * Get some memory for the array of chunks and decode it.
173 173 * Also checks the per-chunk and total size limits.
174 174 * Note that chunk_cnt may be zero here (MacOS).
175 175 */
176 176 args = smb_srm_zalloc(sr, sizeof (*args) +
177 177 (chunk_cnt * sizeof (args->cvec)));
178 178 args->ccnt = chunk_cnt;
179 179 sr->arg.other = args;
180 180 if (chunk_cnt > 0) {
181 181 status = smb2_fsctl_copychunk_decode(sr, fsctl->in_mbc);
182 182 if (status != 0)
183 183 goto out;
184 184 }
185 185
186 186 /*
187 187 * Normally need just the source file size, etc. If doing
188 188 * Apple server-side copy, we want all the attributes.
189 189 */
190 190 if (aapl_copyfile)
191 191 args->src_attr.sa_mask = SMB_AT_ALL;
192 192 else
193 193 args->src_attr.sa_mask = SMB_AT_STANDARD;
194 194 status = smb2_ofile_getattr(sr, src_of, &args->src_attr);
195 195 if (status != 0)
196 196 goto out;
|
↓ open down ↓ |
196 lines elided |
↑ open up ↑ |
197 197
198 198 /*
199 199 * Get a buffer used for copying, always
200 200 * smb2_copychunk_max_seg (1M)
201 201 *
202 202 * Rather than sleep for this relatively large allocation,
203 203 * allow the allocation to fail and return an error.
204 204 * The client should then fall back to normal copy.
205 205 */
206 206 args->bufsize = smb2_copychunk_max_seg;
207 - args->buffer = kmem_alloc(args->bufsize, KM_NOSLEEP | KM_NORMALPRI);
207 + args->buffer = kmem_alloc(args->bufsize, KM_NOSLEEP_LAZY);
208 208 if (args->buffer == NULL) {
209 209 status = NT_STATUS_INSUFF_SERVER_RESOURCES;
210 210 goto out;
211 211 }
212 212
213 213 /*
214 214 * Finally, do the I/O
215 215 */
216 216 if (aapl_copyfile) {
217 217 status = smb2_fsctl_copychunk_aapl(sr, src_of, &ccr);
218 218 } else {
219 219 status = smb2_fsctl_copychunk_array(sr, src_of, &ccr);
220 220 }
221 221
222 222 out:
223 223 if (args != NULL) {
224 224 if (args->buffer != NULL) {
225 225 kmem_free(args->buffer, args->bufsize);
226 226 }
227 227 }
228 228
229 229 if (src_of != NULL)
230 230 smb_ofile_release(src_of);
231 231
232 232 if (status == NT_STATUS_INVALID_PARAMETER) {
233 233 /*
234 234 * Tell the client our max chunk cnt, size, etc.
235 235 */
236 236 ccr.ChunksWritten = smb2_copychunk_max_cnt;
237 237 ccr.ChunkBytesWritten = smb2_copychunk_max_seg;
238 238 ccr.TotalBytesWritten = smb2_copychunk_max_total;
239 239 }
240 240
241 241 /* Checked MaxOutputResp above, so ignore errors here */
242 242 (void) smb_mbc_encodef(
243 243 fsctl->out_mbc, "lll",
244 244 ccr.ChunksWritten,
245 245 ccr.ChunkBytesWritten,
246 246 ccr.TotalBytesWritten);
247 247
248 248 sr->arg.other = NULL;
249 249 /* smb_srm_fini will free args */
250 250
251 251 return (status);
252 252 }
253 253
254 254 /*
255 255 * Decode the list of chunks and check each.
256 256 */
257 257 static uint32_t
258 258 smb2_fsctl_copychunk_decode(smb_request_t *sr, mbuf_chain_t *mbc)
259 259 {
260 260 copychunk_args_t *args = sr->arg.other;
261 261 chunk_t *cc;
262 262 uint32_t status = NT_STATUS_INVALID_PARAMETER;
263 263 uint32_t total_len = 0;
264 264 int i, rc;
265 265
266 266 for (i = 0; i < args->ccnt; i++) {
267 267 cc = &args->cvec[i];
268 268 rc = smb_mbc_decodef(
269 269 mbc, "qqll",
270 270 &cc->src_off, /* q */
271 271 &cc->dst_off, /* q */
272 272 &cc->length, /* l */
273 273 &cc->_reserved); /* l */
274 274 if (rc != 0 || cc->length == 0 ||
275 275 cc->length > smb2_copychunk_max_seg)
276 276 goto out;
277 277 total_len += cc->length;
278 278 }
279 279 if (total_len > smb2_copychunk_max_total)
280 280 goto out;
281 281 status = 0;
282 282
283 283 out:
284 284 return (status);
285 285 }
286 286
287 287 /*
288 288 * Run the actual I/O described by the copychunks array.
289 289 * (normal, non-apple case)
290 290 */
291 291 static uint32_t
292 292 smb2_fsctl_copychunk_array(smb_request_t *sr, smb_ofile_t *src_of,
293 293 struct copychunk_resp *ccr)
294 294 {
295 295 copychunk_args_t *args = sr->arg.other;
296 296 chunk_t *cc;
297 297 uint64_t src_size = args->src_attr.sa_vattr.va_size;
298 298 uint32_t save_len;
299 299 uint32_t copied;
300 300 uint32_t status = 0;
301 301 int i;
302 302
303 303 for (i = 0; i < args->ccnt; i++) {
304 304 cc = &args->cvec[i];
305 305
306 306 /* Chunk must be entirely within file bounds. */
307 307 if (cc->src_off > src_size ||
308 308 (cc->src_off + cc->length) < cc->src_off ||
309 309 (cc->src_off + cc->length) > src_size) {
310 310 status = NT_STATUS_INVALID_VIEW_SIZE;
311 311 goto out;
312 312 }
313 313
314 314 save_len = cc->length;
315 315 status = smb2_fsctl_copychunk_1(sr, src_of, cc);
316 316 if (status != 0) {
317 317 /* no part of this chunk written */
318 318 break;
319 319 }
320 320 /*
321 321 * All or part of the chunk written.
322 322 * cc->length is now the resid count.
323 323 */
324 324 copied = save_len - cc->length;
325 325 ccr->TotalBytesWritten += copied;
326 326 if (cc->length != 0) {
327 327 /* Did not write the whole chunk */
328 328 ccr->ChunkBytesWritten = copied;
329 329 break;
330 330 }
331 331 /* Whole chunk moved. */
332 332 ccr->ChunksWritten++;
333 333 }
334 334 if (ccr->ChunksWritten > 0)
335 335 status = NT_STATUS_SUCCESS;
336 336
337 337 out:
338 338 return (status);
339 339 }
340 340
341 341 /*
342 342 * Helper for smb2_fsctl_copychunk, where MacOS uses chunk_cnt==0
343 343 * to mean "copy the whole file". This interface does not have any
344 344 * way to report a partial copy (client ignores copychunk_resp) so
345 345 * if that happens we just report an error.
346 346 *
347 347 * This extension makes no provision for the server to impose any
348 348 * bound on the amount of data moved by one SMB copychunk request.
349 349 * We could impose a total size, but it's hard to know what size
350 350 * would be an appropriate limit because performance of various
351 351 * storage subsystems can vary quite a bit. The best we can do is
352 352 * limit the time we spend in this copy, and allow cancellation.
353 353 */
354 354 int smb2_fsctl_copychunk_aapl_timeout = 10; /* sec */
355 355 static uint32_t
356 356 smb2_fsctl_copychunk_aapl(smb_request_t *sr, smb_ofile_t *src_of,
357 357 struct copychunk_resp *ccr)
358 358 {
359 359 copychunk_args_t *args = sr->arg.other;
360 360 chunk_t *cc = args->cvec; /* always at least one element */
361 361 uint64_t src_size = args->src_attr.sa_vattr.va_size;
362 362 uint64_t off;
363 363 uint32_t xfer;
364 364 uint32_t status = 0;
365 365 hrtime_t end_time = sr->sr_time_active +
366 366 (smb2_fsctl_copychunk_aapl_timeout * NANOSEC);
367 367
368 368 off = 0;
369 369 while (off < src_size) {
370 370 /*
371 371 * Check that (a) the request has not been cancelled,
372 372 * and (b) we've not run past the timeout.
373 373 */
374 374 if (sr->sr_state != SMB_REQ_STATE_ACTIVE)
375 375 return (NT_STATUS_CANCELLED);
376 376 if (gethrtime() > end_time)
377 377 return (NT_STATUS_IO_TIMEOUT);
378 378
379 379 xfer = smb2_copychunk_max_seg;
380 380 if (off + xfer > src_size)
381 381 xfer = (uint32_t)(src_size - off);
382 382 cc->src_off = off;
383 383 cc->dst_off = off;
384 384 cc->length = xfer;
385 385 status = smb2_fsctl_copychunk_1(sr, src_of, cc);
386 386 if (status != 0)
387 387 break;
388 388 if (cc->length != 0) {
389 389 status = NT_STATUS_PARTIAL_COPY;
390 390 break;
391 391 }
392 392 /*
393 393 * Whole chunk moved. It appears that MacOS clients
394 394 * ignore the response here, but let's put something
395 395 * meaningful in it anyway, so one can see how far
396 396 * the copy went by looking at a network trace.
397 397 */
398 398 ccr->TotalBytesWritten += xfer;
399 399 ccr->ChunksWritten++;
400 400 off += xfer;
401 401 }
402 402
403 403 /*
404 404 * MacOS servers also copy meta-data from the old to new file.
405 405 * We need to do this because Finder does not set the meta-data
406 406 * when copying a file with this interface. If we fail to copy
407 407 * the meta-data, just log. We'd rather not fail the entire
408 408 * copy job if this fails.
409 409 */
410 410 if (status == 0) {
411 411 int rc = smb2_fsctl_copychunk_meta(sr, src_of);
412 412 if (rc != 0) {
413 413 cmn_err(CE_NOTE, "smb2 copychunk meta, rc=%d", rc);
414 414 }
415 415 }
416 416
417 417 return (status);
418 418 }
419 419
420 420 /*
421 421 * Helper for Apple copychunk, to copy meta-data
422 422 */
423 423 static int
424 424 smb2_fsctl_copychunk_meta(smb_request_t *sr, smb_ofile_t *src_of)
425 425 {
426 426 smb_fssd_t fs_sd;
427 427 copychunk_args_t *args = sr->arg.other;
428 428 smb_ofile_t *dst_of = sr->fid_ofile;
429 429 uint32_t sd_flags = 0;
430 430 uint32_t secinfo = SMB_DACL_SECINFO;
431 431 int error;
432 432
433 433 /*
434 434 * Copy attributes. We obtained SMB_AT_ALL above.
435 435 * Now correct the mask for what's settable.
436 436 */
437 437 args->src_attr.sa_mask = SMB_AT_MODE | SMB_AT_SIZE |
438 438 SMB_AT_ATIME | SMB_AT_MTIME | SMB_AT_CTIME |
439 439 SMB_AT_DOSATTR | SMB_AT_ALLOCSZ;
440 440 error = smb_node_setattr(sr, dst_of->f_node, sr->user_cr,
441 441 dst_of, &args->src_attr);
442 442 if (error != 0)
443 443 return (error);
444 444
445 445 /*
446 446 * Copy the ACL. Unfortunately, the ofiles used by the Mac
447 447 * here don't generally have WRITE_DAC access (sigh) so we
448 448 * have to bypass ofile access checks for this operation.
449 449 * The file-system level still does its access checking.
450 450 *
451 451 * TODO: this should really copy the SACL, too.
452 452 */
453 453 smb_fssd_init(&fs_sd, secinfo, sd_flags);
454 454 sr->fid_ofile = NULL;
455 455 error = smb_fsop_sdread(sr, sr->user_cr, src_of->f_node, &fs_sd);
456 456 if (error == 0) {
457 457 error = smb_fsop_sdwrite(sr, sr->user_cr, dst_of->f_node,
458 458 &fs_sd, 1);
459 459 }
460 460 sr->fid_ofile = dst_of;
461 461 smb_fssd_term(&fs_sd);
462 462
463 463 return (error);
464 464 }
465 465
466 466 /*
467 467 * Copy one chunk from src_of to sr->fid_ofile,
468 468 * with offsets and length from chunk *cc
469 469 */
470 470 static uint32_t
471 471 smb2_fsctl_copychunk_1(smb_request_t *sr, smb_ofile_t *src_ofile,
472 472 struct chunk *cc)
473 473 {
474 474 copychunk_args_t *args = sr->arg.other;
475 475 smb_ofile_t *dst_ofile = sr->fid_ofile;
476 476 uint32_t status;
477 477
478 478 if (cc->length > args->bufsize)
479 479 return (NT_STATUS_INTERNAL_ERROR);
480 480
481 481 /*
482 482 * Check for lock conflicting with the read.
483 483 */
484 484 status = smb_lock_range_access(sr, src_ofile->f_node,
485 485 cc->src_off, cc->length, B_FALSE);
486 486 if (status != 0)
487 487 return (status);
488 488
489 489 /*
490 490 * Check for lock conflicting with the write.
491 491 */
492 492 status = smb_lock_range_access(sr, dst_ofile->f_node,
493 493 cc->dst_off, cc->length, B_TRUE);
494 494 if (status != 0)
495 495 return (status);
496 496
497 497 /*
498 498 * Copy src to dst for cc->length
499 499 */
500 500 status = smb2_sparse_copy(sr, src_ofile, dst_ofile,
501 501 cc->src_off, cc->dst_off, &cc->length,
502 502 args->buffer, args->bufsize);
503 503
504 504 return (status);
505 505 }
|
↓ open down ↓ |
288 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX