1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
25 */
26
27 #include <sys/sdt.h>
28 #include <smbsrv/smb_kproto.h>
29 #include <smbsrv/smb_fsops.h>
30 #include <smbsrv/netbios.h>
31
32
33 static int smb_write_truncate(smb_request_t *, smb_rw_param_t *);
34
35
36 /*
37 * Write count bytes at the specified offset in a file. The offset is
38 * limited to 32-bits. If the count is zero, the file is truncated to
39 * the length specified by the offset.
40 *
41 * The response count indicates the actual number of bytes written, which
42 * will equal the requested count on success. If request and response
43 * counts differ but there is no error, the client will assume that the
44 * server encountered a resource issue.
45 */
46 smb_sdrc_t
47 smb_pre_write(smb_request_t *sr)
48 {
49 smb_rw_param_t *param;
50 uint32_t off;
51 uint16_t count;
52 int rc;
53
54 param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
55 sr->arg.rw = param;
56 param->rw_magic = SMB_RW_MAGIC;
57
58 rc = smbsr_decode_vwv(sr, "wwl", &sr->smb_fid, &count, &off);
59
60 param->rw_count = (uint32_t)count;
61 param->rw_offset = (uint64_t)off;
62 param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
63
64 DTRACE_SMB_1(op__Write__start, smb_request_t *, sr); /* arg.rw */
65
66 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
67 }
68
69 void
70 smb_post_write(smb_request_t *sr)
71 {
72 DTRACE_SMB_1(op__Write__done, smb_request_t *, sr); /* arg.rw */
73
74 kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
75 }
76
77 smb_sdrc_t
78 smb_com_write(smb_request_t *sr)
79 {
80 smb_rw_param_t *param = sr->arg.rw;
81 int rc;
82
83 smbsr_lookup_file(sr);
84 if (sr->fid_ofile == NULL) {
85 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
86 return (SDRC_ERROR);
87 }
88
89 sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
90
91 if (param->rw_count == 0) {
92 rc = smb_write_truncate(sr, param);
93 } else {
94 rc = smbsr_decode_data(sr, "D", ¶m->rw_vdb);
95
96 if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
97 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
98 ERRDOS, ERROR_INVALID_PARAMETER);
99 return (SDRC_ERROR);
100 }
101
102 param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
103
104 rc = smb_common_write(sr, param);
105 }
106
107 if (rc != 0) {
108 if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
109 smbsr_errno(sr, rc);
110 return (SDRC_ERROR);
111 }
112
113 rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
114 (uint16_t)param->rw_count, 0);
115 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
116 }
117
118 /*
119 * Write count bytes to a file and then close the file. This function
120 * can only be used to write to 32-bit offsets and the client must set
121 * WordCount (6 or 12) correctly in order to locate the data to be
122 * written. If an error occurs on the write, the file should still be
123 * closed. If Count is 0, the file is truncated (or extended) to offset.
124 *
125 * If the last_write time is non-zero, last_write should be used to set
126 * the mtime. Otherwise the file system stamps the mtime. Failure to
127 * set mtime should not result in an error response.
128 */
129 smb_sdrc_t
130 smb_pre_write_and_close(smb_request_t *sr)
131 {
132 smb_rw_param_t *param;
133 uint32_t off;
134 uint16_t count;
135 int rc;
136
137 param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
138 sr->arg.rw = param;
139 param->rw_magic = SMB_RW_MAGIC;
140
141 if (sr->smb_wct == 12) {
142 rc = smbsr_decode_vwv(sr, "wwll12.", &sr->smb_fid,
143 &count, &off, ¶m->rw_last_write);
144 } else {
145 rc = smbsr_decode_vwv(sr, "wwll", &sr->smb_fid,
146 &count, &off, ¶m->rw_last_write);
147 }
148
149 param->rw_count = (uint32_t)count;
150 param->rw_offset = (uint64_t)off;
151
152 DTRACE_SMB_1(op__WriteAndClose__start, smb_request_t *, sr); /* arg.rw */
153
154 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
155 }
156
157 void
158 smb_post_write_and_close(smb_request_t *sr)
159 {
160 DTRACE_SMB_1(op__WriteAndClose__done, smb_request_t *, sr); /* arg.rw */
161
162 kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
163 }
164
165 smb_sdrc_t
166 smb_com_write_and_close(smb_request_t *sr)
167 {
168 smb_rw_param_t *param = sr->arg.rw;
169 uint16_t count;
170 int rc = 0;
171
172 smbsr_lookup_file(sr);
173 if (sr->fid_ofile == NULL) {
174 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
175 return (SDRC_ERROR);
176 }
177
178 sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
179
180 if (param->rw_count == 0) {
181 rc = smb_write_truncate(sr, param);
182 } else {
183 /*
184 * There may be a bug here: should this be "3.#B"?
185 */
186 rc = smbsr_decode_data(sr, ".#B", param->rw_count,
187 ¶m->rw_vdb);
188
189 if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
190 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
191 ERRDOS, ERROR_INVALID_PARAMETER);
192 return (SDRC_ERROR);
193 }
194
195 param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
196
197 rc = smb_common_write(sr, param);
198 }
199
200 if (rc != 0) {
201 if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
202 smbsr_errno(sr, rc);
203 return (SDRC_ERROR);
204 }
205
206 smb_ofile_close(sr->fid_ofile, param->rw_last_write);
207
208 count = (uint16_t)param->rw_count;
209 rc = smbsr_encode_result(sr, 1, 0, "bww", 1, count, 0);
210 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
211 }
212
213 /*
214 * Write count bytes to a file at the specified offset and then unlock
215 * them. Write behind is safe because the client should have the range
216 * locked and this request is allowed to extend the file - note that
217 * offset is limited to 32-bits.
218 *
219 * Spec advice: it is an error for count to be zero. For compatibility,
220 * we take no action and return success.
221 *
222 * The SmbLockAndRead/SmbWriteAndUnlock sub-dialect is only valid on disk
223 * files. Reject any attempt to use it on other shares.
224 *
225 * The response count indicates the actual number of bytes written, which
226 * will equal the requested count on success. If request and response
227 * counts differ but there is no error, the client will assume that the
228 * server encountered a resource issue.
229 */
230 smb_sdrc_t
231 smb_pre_write_and_unlock(smb_request_t *sr)
232 {
233 smb_rw_param_t *param;
234 uint32_t off;
235 uint16_t count;
236 uint16_t remcnt;
237 int rc;
238
239 param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
240 sr->arg.rw = param;
241 param->rw_magic = SMB_RW_MAGIC;
242
243 rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid, &count, &off, &remcnt);
244
245 param->rw_count = (uint32_t)count;
246 param->rw_offset = (uint64_t)off;
247
248 DTRACE_SMB_1(op__WriteAndUnlock__start, smb_request_t *, sr); /* arg.rw */
249
250 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
251 }
252
253 void
254 smb_post_write_and_unlock(smb_request_t *sr)
255 {
256 DTRACE_SMB_1(op__WriteAndUnlock__done, smb_request_t *, sr); /* arg.rw */
257
258 kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
259 }
260
261 smb_sdrc_t
262 smb_com_write_and_unlock(smb_request_t *sr)
263 {
264 smb_rw_param_t *param = sr->arg.rw;
265 uint32_t lk_pid;
266 uint32_t status;
267 int rc = 0;
268
269 if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
270 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
271 return (SDRC_ERROR);
272 }
273
274 smbsr_lookup_file(sr);
275 if (sr->fid_ofile == NULL) {
276 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
277 return (SDRC_ERROR);
278 }
279
280 sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
281
282 if (param->rw_count == 0) {
283 rc = smbsr_encode_result(sr, 1, 0, "bww", 1, 0, 0);
284 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
285 }
286
287
288 rc = smbsr_decode_data(sr, "D", ¶m->rw_vdb);
289
290 if ((rc != 0) || (param->rw_count != param->rw_vdb.vdb_len)) {
291 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
292 ERRDOS, ERROR_INVALID_PARAMETER);
293 return (SDRC_ERROR);
294 }
295
296 param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
297
298 if ((rc = smb_common_write(sr, param)) != 0) {
299 if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
300 smbsr_errno(sr, rc);
301 return (SDRC_ERROR);
302 }
303
304
305 /* Note: SMB1 locking uses 16-bit PIDs. */
306 lk_pid = sr->smb_pid & 0xFFFF;
307
308 status = smb_unlock_range(sr, param->rw_offset,
309 (uint64_t)param->rw_count, lk_pid);
310 if (status != NT_STATUS_SUCCESS) {
311 smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
312 ERRDOS, ERROR_NOT_LOCKED);
313 return (SDRC_ERROR);
314 }
315
316 rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
317 (uint16_t)param->rw_count, 0);
318 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
319 }
320
321 /*
322 * The SMB_COM_WRITE_RAW protocol was a negotiated option introduced in
323 * SMB Core Plus to maximize performance when writing a large block
324 * of data to a server. It's obsolete and no longer supported.
325 *
326 * We keep a handler for it so the dtrace provider can see if
327 * the client tried to use this command.
328 */
329 smb_sdrc_t
330 smb_pre_write_raw(smb_request_t *sr)
331 {
332 smb_rw_param_t *param;
333 uint32_t off_low;
334 uint32_t timeout;
335 uint32_t off_high;
336 uint16_t datalen;
337 uint16_t total;
338 int rc;
339
340 param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
341 sr->arg.rw = param;
342
343 if (sr->smb_wct == 12) {
344 rc = smbsr_decode_vwv(sr, "ww2.llw4.ww", &sr->smb_fid, &total,
345 &off_low, &timeout, ¶m->rw_mode, &datalen,
346 ¶m->rw_dsoff);
347
348 param->rw_offset = (uint64_t)off_low;
349 } else {
350 rc = smbsr_decode_vwv(sr, "ww2.llw4.wwl", &sr->smb_fid, &total,
351 &off_low, &timeout, ¶m->rw_mode, &datalen,
352 ¶m->rw_dsoff, &off_high);
353
354 param->rw_offset = ((uint64_t)off_high << 32) | off_low;
355 }
356
357 param->rw_count = (uint32_t)datalen;
358 param->rw_total = (uint32_t)total;
359
360 DTRACE_SMB_1(op__WriteRaw__start, smb_request_t *, sr); /* arg.rw */
361
362 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
363 }
364
365 void
366 smb_post_write_raw(smb_request_t *sr)
367 {
368 DTRACE_SMB_1(op__WriteRaw__done, smb_request_t *, sr); /* arg.rw */
369
370 kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
371 }
372
373 smb_sdrc_t
374 smb_com_write_raw(struct smb_request *sr)
375 {
376 smbsr_error(sr, NT_STATUS_NOT_SUPPORTED, ERRDOS,
377 ERROR_NOT_SUPPORTED);
378 return (SDRC_ERROR);
379 }
380
381 /*
382 * Write bytes to a file (SMB Core). This request was extended in
383 * LM 0.12 to support 64-bit offsets, indicated by sending a wct of
384 * 14, instead of 12, and including additional offset information.
385 *
386 * A ByteCount of 0 does not truncate the file - use SMB_COM_WRITE
387 * to truncate a file. A zero length merely transfers zero bytes.
388 *
389 * If bit 0 of WriteMode is set, Fid must refer to a disk file and
390 * the data must be on stable storage before responding.
391 *
392 * MS-SMB 3.3.5.8 update to LM 0.12 4.2.5:
393 * If CAP_LARGE_WRITEX is set, the byte count may be larger than the
394 * negotiated buffer size and the server is expected to write the
395 * number of bytes specified.
396 */
397 smb_sdrc_t
398 smb_pre_write_andx(smb_request_t *sr)
399 {
400 smb_rw_param_t *param;
401 uint32_t off_low;
402 uint32_t off_high;
403 uint16_t datalen_low;
404 uint16_t datalen_high;
405 uint16_t remcnt;
406 int rc;
407
408 param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
409 sr->arg.rw = param;
410 param->rw_magic = SMB_RW_MAGIC;
411
412 if (sr->smb_wct == 14) {
413 rc = smbsr_decode_vwv(sr, "4.wl4.wwwwwl", &sr->smb_fid,
414 &off_low, ¶m->rw_mode, &remcnt, &datalen_high,
415 &datalen_low, ¶m->rw_dsoff, &off_high);
416
417 if (param->rw_dsoff >= 63)
418 param->rw_dsoff -= 63;
419 param->rw_offset = ((uint64_t)off_high << 32) | off_low;
420 } else if (sr->smb_wct == 12) {
421 rc = smbsr_decode_vwv(sr, "4.wl4.wwwww", &sr->smb_fid,
422 &off_low, ¶m->rw_mode, &remcnt, &datalen_high,
423 &datalen_low, ¶m->rw_dsoff);
424
425 if (param->rw_dsoff >= 59)
426 param->rw_dsoff -= 59;
427 param->rw_offset = (uint64_t)off_low;
428 /* off_high not present */
429 } else {
430 rc = -1;
431 }
432
433 param->rw_count = (uint32_t)datalen_low;
434
435 /*
436 * Work-around a Win7 bug, where it fails to set the
437 * CAP_LARGE_WRITEX flag during session setup. Assume
438 * a large write if the data remaining is >= 64k.
439 */
440 if ((sr->session->capabilities & CAP_LARGE_WRITEX) != 0 ||
441 (sr->smb_data.max_bytes > (sr->smb_data.chain_offset + 0xFFFF)))
442 param->rw_count |= ((uint32_t)datalen_high << 16);
443
444 DTRACE_SMB_1(op__WriteX__start, smb_request_t *, sr); /* arg.rw */
445
446 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
447 }
448
449 void
450 smb_post_write_andx(smb_request_t *sr)
451 {
452 DTRACE_SMB_1(op__WriteX__done, smb_request_t *, sr); /* arg.rw */
453
454 kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
455 }
456
457 smb_sdrc_t
458 smb_com_write_andx(smb_request_t *sr)
459 {
460 smb_rw_param_t *param = sr->arg.rw;
461 uint16_t count_high;
462 uint16_t count_low;
463 int rc;
464
465 ASSERT(param);
466 ASSERT(param->rw_magic == SMB_RW_MAGIC);
467
468 smbsr_lookup_file(sr);
469 if (sr->fid_ofile == NULL) {
470 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
471 return (SDRC_ERROR);
472 }
473
474 sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
475
476 if (SMB_WRMODE_IS_STABLE(param->rw_mode) &&
477 STYPE_ISIPC(sr->tid_tree->t_res_type)) {
478 smbsr_error(sr, 0, ERRSRV, ERRaccess);
479 return (SDRC_ERROR);
480 }
481
482 rc = smbsr_decode_data(sr, "#.#B", param->rw_dsoff, param->rw_count,
483 ¶m->rw_vdb);
484
485 if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
486 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
487 ERRDOS, ERROR_INVALID_PARAMETER);
488 return (SDRC_ERROR);
489 }
490
491 param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
492
493 if (param->rw_count != 0) {
494 if ((rc = smb_common_write(sr, param)) != 0) {
495 if (sr->smb_error.status !=
496 NT_STATUS_FILE_LOCK_CONFLICT)
497 smbsr_errno(sr, rc);
498 return (SDRC_ERROR);
499 }
500 }
501
502 count_low = param->rw_count & 0xFFFF;
503 count_high = (param->rw_count >> 16) & 0xFF;
504
505 rc = smbsr_encode_result(sr, 6, 0, "bb1.wwwwww",
506 6, sr->andx_com, 15, count_low, 0, count_high, 0, 0);
507
508 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
509 }
510
511 /*
512 * Common function for writing files or IPC/MSRPC named pipes.
513 *
514 * Returns errno values.
515 */
516 int
517 smb_common_write(smb_request_t *sr, smb_rw_param_t *param)
518 {
519 smb_ofile_t *ofile = sr->fid_ofile;
520 smb_node_t *node;
521 int stability = 0;
522 uint32_t lcount;
523 int rc = 0;
524
525 switch (sr->tid_tree->t_res_type & STYPE_MASK) {
526 case STYPE_DISKTREE:
527 case STYPE_PRINTQ:
528 node = ofile->f_node;
529
530 if (!smb_node_is_dir(node)) {
531 rc = smb_lock_range_access(sr, node, param->rw_offset,
532 param->rw_count, B_TRUE);
533 if (rc != NT_STATUS_SUCCESS) {
534 smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
535 ERRDOS, ERROR_LOCK_VIOLATION);
536 return (EACCES);
537 }
538 }
539
540 if (SMB_WRMODE_IS_STABLE(param->rw_mode) ||
541 (node->flags & NODE_FLAGS_WRITE_THROUGH)) {
542 stability = FSYNC;
543 }
544
545 rc = smb_fsop_write(sr, sr->user_cr, node, ofile,
546 ¶m->rw_vdb.vdb_uio, &lcount, stability);
547
548 if (rc)
549 return (rc);
550
551 /*
552 * Used to have code here to set mtime.
553 * We have just done a write, so we know
554 * the file system will update mtime.
555 * No need to do it again here.
556 *
557 * However, keep track of the fact that
558 * we have written data via this handle.
559 */
560 ofile->f_written = B_TRUE;
561
562 if (!smb_node_is_dir(node))
563 smb_oplock_break_levelII(node);
564
565 param->rw_count = lcount;
566 break;
567
568 case STYPE_IPC:
569 param->rw_count = param->rw_vdb.vdb_uio.uio_resid;
570
571 if ((rc = smb_opipe_write(sr, ¶m->rw_vdb.vdb_uio)) != 0)
572 param->rw_count = 0;
573 break;
574
575 default:
576 rc = EACCES;
577 break;
578 }
579
580 if (rc != 0)
581 return (rc);
582
583 mutex_enter(&ofile->f_mutex);
584 ofile->f_seek_pos = param->rw_offset + param->rw_count;
585 mutex_exit(&ofile->f_mutex);
586 return (rc);
587 }
588
589 /*
590 * Truncate a disk file to the specified offset.
591 * Typically, w_count will be zero here.
592 *
593 * Note that smb_write_andx cannot be used to reduce the file size so,
594 * if this is required, smb_write is called with a count of zero and
595 * the appropriate file length in offset. The file should be resized
596 * to the length specified by the offset.
597 *
598 * Returns errno values.
599 */
600 static int
601 smb_write_truncate(smb_request_t *sr, smb_rw_param_t *param)
602 {
603 smb_ofile_t *ofile = sr->fid_ofile;
604 smb_node_t *node = ofile->f_node;
605 smb_attr_t attr;
606 uint32_t status;
607 int rc;
608
609 if (STYPE_ISIPC(sr->tid_tree->t_res_type))
610 return (0);
611
612 mutex_enter(&node->n_mutex);
613 if (!smb_node_is_dir(node)) {
614 status = smb_lock_range_access(sr, node, param->rw_offset,
615 param->rw_count, B_TRUE);
616 if (status != NT_STATUS_SUCCESS) {
617 mutex_exit(&node->n_mutex);
618 smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
619 ERRDOS, ERROR_LOCK_VIOLATION);
620 return (EACCES);
621 }
622 }
623 mutex_exit(&node->n_mutex);
624
625 bzero(&attr, sizeof (smb_attr_t));
626 attr.sa_mask = SMB_AT_SIZE;
627 attr.sa_vattr.va_size = param->rw_offset;
628 rc = smb_node_setattr(sr, node, sr->user_cr, ofile, &attr);
629 if (rc != 0)
630 return (rc);
631
632 mutex_enter(&ofile->f_mutex);
633 ofile->f_seek_pos = param->rw_offset + param->rw_count;
634 mutex_exit(&ofile->f_mutex);
635 return (0);
636 }