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 */
25
26 #include <sys/sdt.h>
27 #include <smbsrv/smb_kproto.h>
28 #include <smbsrv/smb_fsops.h>
29 #include <smbsrv/netbios.h>
30
31
32 /*
33 * The limit in bytes that the marshalling will grow the buffer
34 * chain to accomodate incoming data on SmbWriteX requests.
35 * This sets the upper limit for the data-count per SmbWriteX
36 * request.
37 */
38 #define SMB_WRITEX_MAX 102400
39
40 static int smb_write_truncate(smb_request_t *, smb_rw_param_t *);
41
42
43 /*
44 * Write count bytes at the specified offset in a file. The offset is
45 * limited to 32-bits. If the count is zero, the file is truncated to
46 * the length specified by the offset.
47 *
48 * The response count indicates the actual number of bytes written, which
49 * will equal the requested count on success. If request and response
50 * counts differ but there is no error, the client will assume that the
51 * server encountered a resource issue.
52 */
53 smb_sdrc_t
smb_pre_write(smb_request_t * sr)54 smb_pre_write(smb_request_t *sr)
55 {
56 smb_rw_param_t *param;
57 uint32_t off;
58 uint16_t count;
59 int rc;
60
61 param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
62 sr->arg.rw = param;
63 param->rw_magic = SMB_RW_MAGIC;
64
65 rc = smbsr_decode_vwv(sr, "wwl", &sr->smb_fid, &count, &off);
66
67 param->rw_count = (uint32_t)count;
68 param->rw_offset = (uint64_t)off;
69 param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
70
71 DTRACE_SMB_2(op__Write__start, smb_request_t *, sr,
72 smb_rw_param_t *, param);
73
74 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
75 }
76
77 void
smb_post_write(smb_request_t * sr)78 smb_post_write(smb_request_t *sr)
79 {
80 DTRACE_SMB_2(op__Write__done, smb_request_t *, sr,
81 smb_rw_param_t *, sr->arg.rw);
82
83 kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
84 }
85
86 smb_sdrc_t
smb_com_write(smb_request_t * sr)87 smb_com_write(smb_request_t *sr)
88 {
89 smb_rw_param_t *param = sr->arg.rw;
90 int rc;
91
92 smbsr_lookup_file(sr);
93 if (sr->fid_ofile == NULL) {
94 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
95 return (SDRC_ERROR);
96 }
97
98 sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
99
100 if (param->rw_count == 0) {
101 rc = smb_write_truncate(sr, param);
102 } else {
103 rc = smbsr_decode_data(sr, "D", ¶m->rw_vdb);
104
105 if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
106 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
107 ERRDOS, ERROR_INVALID_PARAMETER);
108 return (SDRC_ERROR);
109 }
110
111 param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
112
113 rc = smb_common_write(sr, param);
114 }
115
116 if (rc != 0) {
117 if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
118 smbsr_errno(sr, rc);
119 return (SDRC_ERROR);
120 }
121
122 rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
123 (uint16_t)param->rw_count, 0);
124 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
125 }
126
127 /*
128 * Write count bytes to a file and then close the file. This function
129 * can only be used to write to 32-bit offsets and the client must set
130 * WordCount (6 or 12) correctly in order to locate the data to be
131 * written. If an error occurs on the write, the file should still be
132 * closed. If Count is 0, the file is truncated (or extended) to offset.
133 *
134 * If the last_write time is non-zero, last_write should be used to set
135 * the mtime. Otherwise the file system stamps the mtime. Failure to
136 * set mtime should not result in an error response.
137 */
138 smb_sdrc_t
smb_pre_write_and_close(smb_request_t * sr)139 smb_pre_write_and_close(smb_request_t *sr)
140 {
141 smb_rw_param_t *param;
142 uint32_t off;
143 uint16_t count;
144 int rc;
145
146 param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
147 sr->arg.rw = param;
148 param->rw_magic = SMB_RW_MAGIC;
149
150 if (sr->smb_wct == 12) {
151 rc = smbsr_decode_vwv(sr, "wwll12.", &sr->smb_fid,
152 &count, &off, ¶m->rw_last_write);
153 } else {
154 rc = smbsr_decode_vwv(sr, "wwll", &sr->smb_fid,
155 &count, &off, ¶m->rw_last_write);
156 }
157
158 param->rw_count = (uint32_t)count;
159 param->rw_offset = (uint64_t)off;
160
161 DTRACE_SMB_2(op__WriteAndClose__start, smb_request_t *, sr,
162 smb_rw_param_t *, param);
163
164 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
165 }
166
167 void
smb_post_write_and_close(smb_request_t * sr)168 smb_post_write_and_close(smb_request_t *sr)
169 {
170 DTRACE_SMB_2(op__WriteAndClose__done, smb_request_t *, sr,
171 smb_rw_param_t *, sr->arg.rw);
172
173 kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
174 }
175
176 smb_sdrc_t
smb_com_write_and_close(smb_request_t * sr)177 smb_com_write_and_close(smb_request_t *sr)
178 {
179 smb_rw_param_t *param = sr->arg.rw;
180 uint16_t count;
181 int rc = 0;
182
183 smbsr_lookup_file(sr);
184 if (sr->fid_ofile == NULL) {
185 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
186 return (SDRC_ERROR);
187 }
188
189 sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
190
191 if (param->rw_count == 0) {
192 rc = smb_write_truncate(sr, param);
193 } else {
194 /*
195 * There may be a bug here: should this be "3.#B"?
196 */
197 rc = smbsr_decode_data(sr, ".#B", param->rw_count,
198 ¶m->rw_vdb);
199
200 if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
201 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
202 ERRDOS, ERROR_INVALID_PARAMETER);
203 return (SDRC_ERROR);
204 }
205
206 param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
207
208 rc = smb_common_write(sr, param);
209 }
210
211 if (rc != 0) {
212 if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
213 smbsr_errno(sr, rc);
214 return (SDRC_ERROR);
215 }
216
217 smb_ofile_close(sr->fid_ofile, param->rw_last_write);
218
219 count = (uint16_t)param->rw_count;
220 rc = smbsr_encode_result(sr, 1, 0, "bww", 1, count, 0);
221 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
222 }
223
224 /*
225 * Write count bytes to a file at the specified offset and then unlock
226 * them. Write behind is safe because the client should have the range
227 * locked and this request is allowed to extend the file - note that
228 * offset is limited to 32-bits.
229 *
230 * Spec advice: it is an error for count to be zero. For compatibility,
231 * we take no action and return success.
232 *
233 * The SmbLockAndRead/SmbWriteAndUnlock sub-dialect is only valid on disk
234 * files. Reject any attempt to use it on other shares.
235 *
236 * The response count indicates the actual number of bytes written, which
237 * will equal the requested count on success. If request and response
238 * counts differ but there is no error, the client will assume that the
239 * server encountered a resource issue.
240 */
241 smb_sdrc_t
smb_pre_write_and_unlock(smb_request_t * sr)242 smb_pre_write_and_unlock(smb_request_t *sr)
243 {
244 smb_rw_param_t *param;
245 uint32_t off;
246 uint16_t count;
247 uint16_t remcnt;
248 int rc;
249
250 param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
251 sr->arg.rw = param;
252 param->rw_magic = SMB_RW_MAGIC;
253
254 rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid, &count, &off, &remcnt);
255
256 param->rw_count = (uint32_t)count;
257 param->rw_offset = (uint64_t)off;
258
259 DTRACE_SMB_2(op__WriteAndUnlock__start, smb_request_t *, sr,
260 smb_rw_param_t *, param);
261
262 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
263 }
264
265 void
smb_post_write_and_unlock(smb_request_t * sr)266 smb_post_write_and_unlock(smb_request_t *sr)
267 {
268 DTRACE_SMB_2(op__WriteAndUnlock__done, smb_request_t *, sr,
269 smb_rw_param_t *, sr->arg.rw);
270
271 kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
272 }
273
274 smb_sdrc_t
smb_com_write_and_unlock(smb_request_t * sr)275 smb_com_write_and_unlock(smb_request_t *sr)
276 {
277 smb_rw_param_t *param = sr->arg.rw;
278 uint32_t status;
279 int rc = 0;
280
281 if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
282 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
283 return (SDRC_ERROR);
284 }
285
286 smbsr_lookup_file(sr);
287 if (sr->fid_ofile == NULL) {
288 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
289 return (SDRC_ERROR);
290 }
291
292 sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
293
294 if (param->rw_count == 0) {
295 rc = smbsr_encode_result(sr, 1, 0, "bww", 1, 0, 0);
296 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
297 }
298
299
300 rc = smbsr_decode_data(sr, "D", ¶m->rw_vdb);
301
302 if ((rc != 0) || (param->rw_count != param->rw_vdb.vdb_len)) {
303 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
304 ERRDOS, ERROR_INVALID_PARAMETER);
305 return (SDRC_ERROR);
306 }
307
308 param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
309
310 if ((rc = smb_common_write(sr, param)) != 0) {
311 if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
312 smbsr_errno(sr, rc);
313 return (SDRC_ERROR);
314 }
315
316 status = smb_unlock_range(sr, sr->fid_ofile->f_node, param->rw_offset,
317 (uint64_t)param->rw_count);
318 if (status != NT_STATUS_SUCCESS) {
319 smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
320 ERRDOS, ERROR_NOT_LOCKED);
321 return (SDRC_ERROR);
322 }
323
324 rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
325 (uint16_t)param->rw_count, 0);
326 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
327 }
328
329 /*
330 * Write bytes to a file (SMB Core). This request was extended in
331 * LM 0.12 to support 64-bit offsets, indicated by sending a wct of
332 * 14, instead of 12, and including additional offset information.
333 *
334 * A ByteCount of 0 does not truncate the file - use SMB_COM_WRITE
335 * to truncate a file. A zero length merely transfers zero bytes.
336 *
337 * If bit 0 of WriteMode is set, Fid must refer to a disk file and
338 * the data must be on stable storage before responding.
339 *
340 * MS-SMB 3.3.5.8 update to LM 0.12 4.2.5:
341 * If CAP_LARGE_WRITEX is set, the byte count may be larger than the
342 * negotiated buffer size and the server is expected to write the
343 * number of bytes specified.
344 */
345 smb_sdrc_t
smb_pre_write_andx(smb_request_t * sr)346 smb_pre_write_andx(smb_request_t *sr)
347 {
348 smb_rw_param_t *param;
349 uint32_t off_low;
350 uint32_t off_high;
351 uint16_t datalen_low;
352 uint16_t datalen_high;
353 uint16_t remcnt;
354 int rc;
355
356 param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
357 sr->arg.rw = param;
358 param->rw_magic = SMB_RW_MAGIC;
359
360 if (sr->smb_wct == 14) {
361 rc = smbsr_decode_vwv(sr, "4.wl4.wwwwwl", &sr->smb_fid,
362 &off_low, ¶m->rw_mode, &remcnt, &datalen_high,
363 &datalen_low, ¶m->rw_dsoff, &off_high);
364
365 param->rw_dsoff -= 63;
366 param->rw_offset = ((uint64_t)off_high << 32) | off_low;
367 } else {
368 rc = smbsr_decode_vwv(sr, "4.wl4.wwwww", &sr->smb_fid,
369 &off_low, ¶m->rw_mode, &remcnt, &datalen_high,
370 &datalen_low, ¶m->rw_dsoff);
371
372 param->rw_offset = (uint64_t)off_low;
373 param->rw_dsoff -= 59;
374 }
375
376 param->rw_count = (uint32_t)datalen_low;
377
378 if (sr->session->capabilities & CAP_LARGE_WRITEX)
379 param->rw_count |= ((uint32_t)datalen_high << 16);
380
381 DTRACE_SMB_2(op__WriteX__start, smb_request_t *, sr,
382 smb_rw_param_t *, param);
383
384 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
385 }
386
387 void
smb_post_write_andx(smb_request_t * sr)388 smb_post_write_andx(smb_request_t *sr)
389 {
390 DTRACE_SMB_2(op__WriteX__done, smb_request_t *, sr,
391 smb_rw_param_t *, sr->arg.rw);
392
393 kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
394 }
395
396 smb_sdrc_t
smb_com_write_andx(smb_request_t * sr)397 smb_com_write_andx(smb_request_t *sr)
398 {
399 smb_rw_param_t *param = sr->arg.rw;
400 uint16_t count_high;
401 uint16_t count_low;
402 int rc;
403
404 ASSERT(param);
405 ASSERT(param->rw_magic == SMB_RW_MAGIC);
406
407 smbsr_lookup_file(sr);
408 if (sr->fid_ofile == NULL) {
409 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
410 return (SDRC_ERROR);
411 }
412
413 sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
414
415 if (SMB_WRMODE_IS_STABLE(param->rw_mode) &&
416 STYPE_ISIPC(sr->tid_tree->t_res_type)) {
417 smbsr_error(sr, 0, ERRSRV, ERRaccess);
418 return (SDRC_ERROR);
419 }
420
421 sr->smb_data.max_bytes = SMB_WRITEX_MAX;
422 rc = smbsr_decode_data(sr, "#.#B", param->rw_dsoff, param->rw_count,
423 ¶m->rw_vdb);
424
425 if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
426 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
427 ERRDOS, ERROR_INVALID_PARAMETER);
428 return (SDRC_ERROR);
429 }
430
431 param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
432
433 if (param->rw_count != 0) {
434 if ((rc = smb_common_write(sr, param)) != 0) {
435 if (sr->smb_error.status !=
436 NT_STATUS_FILE_LOCK_CONFLICT)
437 smbsr_errno(sr, rc);
438 return (SDRC_ERROR);
439 }
440 }
441
442 count_low = param->rw_count & 0xFFFF;
443 count_high = (param->rw_count >> 16) & 0xFF;
444
445 rc = smbsr_encode_result(sr, 6, 0, "bb1.wwwwww",
446 6, sr->andx_com, 15, count_low, 0, count_high, 0, 0);
447
448 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
449 }
450
451 /*
452 * Common function for writing files or IPC/MSRPC named pipes.
453 *
454 * Returns errno values.
455 */
456 int
smb_common_write(smb_request_t * sr,smb_rw_param_t * param)457 smb_common_write(smb_request_t *sr, smb_rw_param_t *param)
458 {
459 smb_ofile_t *ofile = sr->fid_ofile;
460 smb_node_t *node;
461 int stability = 0;
462 uint32_t lcount;
463 int rc = 0;
464
465 switch (sr->tid_tree->t_res_type & STYPE_MASK) {
466 case STYPE_DISKTREE:
467 case STYPE_PRINTQ:
468 node = ofile->f_node;
469
470 if (!smb_node_is_dir(node)) {
471 rc = smb_lock_range_access(sr, node, param->rw_offset,
472 param->rw_count, B_TRUE);
473 if (rc != NT_STATUS_SUCCESS) {
474 smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
475 ERRDOS, ERROR_LOCK_VIOLATION);
476 return (EACCES);
477 }
478 }
479
480 if (SMB_WRMODE_IS_STABLE(param->rw_mode) ||
481 (node->flags & NODE_FLAGS_WRITE_THROUGH)) {
482 stability = FSYNC;
483 }
484
485 rc = smb_fsop_write(sr, sr->user_cr, node,
486 ¶m->rw_vdb.vdb_uio, &lcount, stability);
487
488 if (rc)
489 return (rc);
490
491 smb_ofile_set_write_time_pending(ofile);
492
493 if (!smb_node_is_dir(node))
494 smb_oplock_break_levelII(node);
495
496 param->rw_count = lcount;
497 break;
498
499 case STYPE_IPC:
500 param->rw_count = param->rw_vdb.vdb_uio.uio_resid;
501
502 if ((rc = smb_opipe_write(sr, ¶m->rw_vdb.vdb_uio)) != 0)
503 param->rw_count = 0;
504 break;
505
506 default:
507 rc = EACCES;
508 break;
509 }
510
511 if (rc != 0)
512 return (rc);
513
514 mutex_enter(&ofile->f_mutex);
515 ofile->f_seek_pos = param->rw_offset + param->rw_count;
516 mutex_exit(&ofile->f_mutex);
517 return (rc);
518 }
519
520 /*
521 * Truncate a disk file to the specified offset.
522 * Typically, w_count will be zero here.
523 *
524 * Note that smb_write_andx cannot be used to reduce the file size so,
525 * if this is required, smb_write is called with a count of zero and
526 * the appropriate file length in offset. The file should be resized
527 * to the length specified by the offset.
528 *
529 * Returns errno values.
530 */
531 static int
smb_write_truncate(smb_request_t * sr,smb_rw_param_t * param)532 smb_write_truncate(smb_request_t *sr, smb_rw_param_t *param)
533 {
534 smb_ofile_t *ofile = sr->fid_ofile;
535 smb_node_t *node = ofile->f_node;
536 smb_attr_t attr;
537 uint32_t status;
538 int rc;
539
540 if (STYPE_ISIPC(sr->tid_tree->t_res_type))
541 return (0);
542
543 mutex_enter(&node->n_mutex);
544 if (!smb_node_is_dir(node)) {
545 status = smb_lock_range_access(sr, node, param->rw_offset,
546 param->rw_count, B_TRUE);
547 if (status != NT_STATUS_SUCCESS) {
548 mutex_exit(&node->n_mutex);
549 smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
550 ERRDOS, ERROR_LOCK_VIOLATION);
551 return (EACCES);
552 }
553 }
554 mutex_exit(&node->n_mutex);
555
556 bzero(&attr, sizeof (smb_attr_t));
557 attr.sa_mask = SMB_AT_SIZE;
558 attr.sa_vattr.va_size = param->rw_offset;
559 rc = smb_node_setattr(sr, node, sr->user_cr, ofile, &attr);
560 if (rc != 0)
561 return (rc);
562
563 mutex_enter(&ofile->f_mutex);
564 ofile->f_seek_pos = param->rw_offset + param->rw_count;
565 mutex_exit(&ofile->f_mutex);
566 return (0);
567 }
568