Print this page
NEX-13644 File access audit logging
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-2831 panic in smb_make_link
NEX-2442 regression with smbtorture test raw.sfileinfo.rename
NEX-1920 SMB rename from Win2k8 fails
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/uts/common/fs/smbsrv/smb_cmn_rename.c
+++ new/usr/src/uts/common/fs/smbsrv/smb_cmn_rename.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
|
↓ open down ↓ |
12 lines elided |
↑ open up ↑ |
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 - * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
23 + * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
24 24 */
25 25
26 26 #include <sys/synch.h>
27 -#include <smbsrv/smb_kproto.h>
27 +#include <smbsrv/smb2_kproto.h>
28 28 #include <smbsrv/smb_fsops.h>
29 29 #include <sys/nbmlock.h>
30 30
31 31 /*
32 32 * SMB_TRANS2_SET_FILE/PATH_INFO (RENAME_INFORMATION level) flag
33 33 */
34 34 #define SMB_RENAME_FLAG_OVERWRITE 0x001
35 35
36 36 static int smb_rename_check_stream(smb_fqi_t *, smb_fqi_t *);
37 37 static int smb_rename_check_attr(smb_request_t *, smb_node_t *, uint16_t);
38 38 static int smb_rename_lookup_src(smb_request_t *);
39 +static uint32_t smb_rename_check_src(smb_request_t *, smb_fqi_t *);
39 40 static void smb_rename_release_src(smb_request_t *);
40 41 static uint32_t smb_rename_errno2status(int);
41 42
42 43 /*
43 44 * smb_setinfo_rename
44 45 *
45 46 * Implements SMB_FILE_RENAME_INFORMATION level of Trans2_Set_FileInfo
46 47 * and Trans2_Set_PathInfo and SMB2 set_info, FileRenameInformation.
47 48 * If the new filename (dst_fqi) already exists it may be overwritten
48 49 * if flags == 1.
49 50 *
50 51 * The passed path is a full path relative to the share root.
51 52 *
52 53 * Returns NT status codes.
53 54 *
54 55 * Similar to smb_setinfo_link(), below.
55 56 */
56 57 uint32_t
57 58 smb_setinfo_rename(smb_request_t *sr, smb_node_t *node, char *path, int flags)
58 59 {
59 60 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
60 61 smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
61 62 smb_pathname_t *dst_pn = &dst_fqi->fq_path;
62 63 uint32_t status;
63 64
64 65 sr->arg.dirop.flags = flags ? SMB_RENAME_FLAG_OVERWRITE : 0;
65 66 sr->arg.dirop.info_level = FileRenameInformation;
66 67
67 68 src_fqi->fq_sattr = SMB_SEARCH_ATTRIBUTES;
68 69 src_fqi->fq_fnode = node;
69 70 src_fqi->fq_dnode = node->n_dnode;
70 71
71 72 /* validate the dst pathname */
72 73 smb_pathname_init(sr, dst_pn, path);
73 74 if (!smb_pathname_validate(sr, dst_pn))
74 75 return (NT_STATUS_OBJECT_NAME_INVALID);
75 76
76 77 status = smb_common_rename(sr, src_fqi, dst_fqi);
77 78 return (status);
78 79 }
79 80
80 81 /*
81 82 * smb_common_rename
82 83 *
83 84 * Common code for renaming a file.
84 85 *
85 86 * If the source and destination are identical, we go through all
86 87 * the checks but we don't actually do the rename. If the source
87 88 * and destination files differ only in case, we do a case-sensitive
88 89 * rename. Otherwise, we do a full case-insensitive rename.
89 90 *
90 91 * Returns NT status values.
91 92 *
|
↓ open down ↓ |
43 lines elided |
↑ open up ↑ |
92 93 * Similar to smb_make_link(), below.
93 94 */
94 95 uint32_t
95 96 smb_common_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
96 97 {
97 98 smb_node_t *src_fnode, *src_dnode, *dst_dnode;
98 99 smb_node_t *dst_fnode = 0;
99 100 smb_node_t *tnode;
100 101 char *new_name, *path;
101 102 DWORD status;
102 - int rc, count;
103 + int rc;
104 + boolean_t have_src = B_FALSE;
105 + boolean_t dst_exists = B_FALSE;
106 + boolean_t do_audit;
107 + char *srcpath = NULL;
108 + char *dstpath = NULL;
103 109
104 110 tnode = sr->tid_tree->t_snode;
105 111 path = dst_fqi->fq_path.pn_path;
106 112
107 113 /* Check if attempting to rename a stream - not yet supported */
108 114 rc = smb_rename_check_stream(src_fqi, dst_fqi);
109 115 if (rc != 0)
110 116 return (smb_rename_errno2status(rc));
111 117
112 118 /*
113 119 * The source node may already have been provided,
114 - * i.e. when called by SMB1/SMB2 smb_setinfo_rename.
115 - * Not provided by smb_com_rename, smb_com_nt_rename.
120 + * i.e. when called by SMB1/SMB2 smb_setinfo_rename
121 + * with an ofile. When we have an ofile, open has
122 + * already checked for sharing violations. For
123 + * path-based operations, do sharing check here.
116 124 */
117 125 if (src_fqi->fq_fnode) {
118 - smb_node_start_crit(src_fqi->fq_fnode, RW_READER);
119 - smb_node_ref(src_fqi->fq_fnode);
120 126 smb_node_ref(src_fqi->fq_dnode);
127 + smb_node_ref(src_fqi->fq_fnode);
128 + have_src = B_TRUE;
121 129 } else {
122 130 /* lookup and validate src node */
123 131 rc = smb_rename_lookup_src(sr);
124 132 if (rc != 0)
125 133 return (smb_rename_errno2status(rc));
134 + /* Holding refs on dnode, fnode */
126 135 }
127 -
128 136 src_fnode = src_fqi->fq_fnode;
129 137 src_dnode = src_fqi->fq_dnode;
130 138
139 + /* Break oplocks, and check share modes. */
140 + status = smb_rename_check_src(sr, src_fqi);
141 + if (status != NT_STATUS_SUCCESS) {
142 + smb_node_release(src_fqi->fq_fnode);
143 + smb_node_release(src_fqi->fq_dnode);
144 + return (status);
145 + }
131 146 /*
147 + * NB: src_fnode is now "in crit" (critical section)
148 + * as if we did smb_node_start_crit(..., RW_READER);
149 + * Call smb_rename_release_src(sr) on errors.
150 + */
151 +
152 + /*
132 153 * Find the destination dnode and last component.
133 154 * May already be provided, i.e. when called via
134 155 * SMB1 trans2 setinfo.
135 156 */
136 157 if (dst_fqi->fq_dnode) {
137 158 /* called via smb_set_rename_info */
138 159 smb_node_ref(dst_fqi->fq_dnode);
139 160 } else {
140 161 /* called via smb2_setf_rename, smb_com_rename, etc. */
141 162 rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
142 163 &dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
143 164 if (rc != 0) {
144 165 smb_rename_release_src(sr);
145 166 return (smb_rename_errno2status(rc));
146 167 }
147 168 }
148 169
149 170 dst_dnode = dst_fqi->fq_dnode;
150 171 new_name = dst_fqi->fq_last_comp;
151 172
152 173 /* If exact name match in same directory, we're done */
153 174 if ((src_dnode == dst_dnode) &&
154 175 (strcmp(src_fnode->od_name, new_name) == 0)) {
155 176 smb_rename_release_src(sr);
156 177 smb_node_release(dst_dnode);
157 178 return (0);
158 179 }
159 180
160 181 /* Lookup destination node */
161 182 rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
162 183 dst_dnode, new_name, &dst_fqi->fq_fnode);
163 184
164 185 /* If the destination node doesn't already exist, validate new_name. */
165 186 if (rc == ENOENT) {
166 187 if (smb_is_invalid_filename(new_name)) {
167 188 smb_rename_release_src(sr);
168 189 smb_node_release(dst_dnode);
169 190 return (NT_STATUS_OBJECT_NAME_INVALID);
170 191 }
171 192 }
172 193
173 194 /*
174 195 * Handle case where changing case of the same directory entry.
175 196 *
176 197 * If we found the dst node in the same directory as the src node,
177 198 * and their names differ only in case:
178 199 *
179 200 * If the tree is case sensitive (or mixed):
180 201 * Do case sensitive lookup to see if exact match exists.
181 202 * If the exact match is the same node as src_node we're done.
182 203 *
183 204 * If the tree is case insensitive:
184 205 * There is currently no way to tell if the case is different
185 206 * or not, so do the rename (unless the specified new name was
186 207 * mangled).
187 208 */
188 209 if ((rc == 0) &&
189 210 (src_dnode == dst_dnode) &&
190 211 (smb_strcasecmp(src_fnode->od_name,
191 212 dst_fqi->fq_fnode->od_name, 0) == 0)) {
192 213 smb_node_release(dst_fqi->fq_fnode);
193 214 dst_fqi->fq_fnode = NULL;
194 215
195 216 if (smb_tree_has_feature(sr->tid_tree,
196 217 SMB_TREE_NO_CASESENSITIVE)) {
197 218 if (smb_strcasecmp(src_fnode->od_name,
198 219 dst_fqi->fq_last_comp, 0) != 0) {
199 220 smb_rename_release_src(sr);
200 221 smb_node_release(dst_dnode);
201 222 return (0);
202 223 }
203 224 } else {
204 225 rc = smb_fsop_lookup(sr, sr->user_cr,
205 226 SMB_CASE_SENSITIVE, tnode, dst_dnode, new_name,
206 227 &dst_fqi->fq_fnode);
207 228
208 229 if ((rc == 0) &&
209 230 (dst_fqi->fq_fnode == src_fnode)) {
210 231 smb_rename_release_src(sr);
211 232 smb_node_release(dst_fqi->fq_fnode);
212 233 smb_node_release(dst_dnode);
213 234 return (0);
214 235 }
215 236 }
216 237 }
217 238
218 239 if ((rc != 0) && (rc != ENOENT)) {
219 240 smb_rename_release_src(sr);
220 241 smb_node_release(dst_fqi->fq_dnode);
221 242 return (smb_rename_errno2status(rc));
222 243 }
223 244
224 245 if (dst_fqi->fq_fnode) {
225 246 /*
226 247 * Destination already exists. Do delete checks.
|
↓ open down ↓ |
85 lines elided |
↑ open up ↑ |
227 248 */
228 249 dst_fnode = dst_fqi->fq_fnode;
229 250
230 251 if (!(sr->arg.dirop.flags && SMB_RENAME_FLAG_OVERWRITE)) {
231 252 smb_rename_release_src(sr);
232 253 smb_node_release(dst_fnode);
233 254 smb_node_release(dst_dnode);
234 255 return (NT_STATUS_OBJECT_NAME_COLLISION);
235 256 }
236 257
237 - (void) smb_oplock_break(sr, dst_fnode,
238 - SMB_OPLOCK_BREAK_TO_NONE | SMB_OPLOCK_BREAK_BATCH);
258 + status = smb_oplock_break_DELETE(dst_fnode, NULL);
259 + if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
260 + if (sr->session->dialect >= SMB_VERS_2_BASE)
261 + (void) smb2sr_go_async(sr);
262 + (void) smb_oplock_wait_break(dst_fnode, 0);
263 + status = 0;
264 + }
265 + if (status != 0) {
266 + smb_rename_release_src(sr);
267 + smb_node_release(dst_fnode);
268 + smb_node_release(dst_dnode);
269 + return (status);
270 + }
239 271
240 - /*
241 - * Wait (a little) for the oplock break to be
242 - * responded to by clients closing handles.
243 - * Hold node->n_lock as reader to keep new
244 - * ofiles from showing up after we check.
245 - */
246 272 smb_node_rdlock(dst_fnode);
247 - for (count = 0; count <= 12; count++) {
248 - status = smb_node_delete_check(dst_fnode);
249 - if (status != NT_STATUS_SHARING_VIOLATION)
250 - break;
251 - smb_node_unlock(dst_fnode);
252 - delay(MSEC_TO_TICK(100));
253 - smb_node_rdlock(dst_fnode);
254 - }
273 + status = smb_node_delete_check(dst_fnode);
255 274 if (status != NT_STATUS_SUCCESS) {
256 275 smb_node_unlock(dst_fnode);
257 276 smb_rename_release_src(sr);
258 277 smb_node_release(dst_fnode);
259 278 smb_node_release(dst_dnode);
260 279 return (NT_STATUS_ACCESS_DENIED);
261 280 }
262 281
263 282 /*
264 283 * Note, the combination of these two:
265 284 * smb_node_rdlock(node);
266 285 * nbl_start_crit(node->vp, RW_READER);
267 286 * is equivalent to this call:
268 287 * smb_node_start_crit(node, RW_READER)
269 288 *
270 289 * Cleanup after this point should use:
271 290 * smb_node_end_crit(dst_fnode)
272 291 */
273 292 nbl_start_crit(dst_fnode->vp, RW_READER);
274 293
275 294 /*
276 295 * This checks nbl_share_conflict, nbl_lock_conflict
277 296 */
|
↓ open down ↓ |
13 lines elided |
↑ open up ↑ |
278 297 status = smb_nbl_conflict(dst_fnode, 0, UINT64_MAX, NBL_REMOVE);
279 298 if (status != NT_STATUS_SUCCESS) {
280 299 smb_node_end_crit(dst_fnode);
281 300 smb_rename_release_src(sr);
282 301 smb_node_release(dst_fnode);
283 302 smb_node_release(dst_dnode);
284 303 return (NT_STATUS_ACCESS_DENIED);
285 304 }
286 305
287 306 new_name = dst_fnode->od_name;
307 + dst_exists = B_TRUE;
288 308 }
289 309
310 + do_audit = smb_audit_rename_init(sr);
311 + /* save paths for later auditing */
312 + if (do_audit) {
313 + if (!have_src) {
314 + srcpath = kmem_alloc(SMB_MAXPATHLEN, KM_SLEEP);
315 + smb_node_getpath_nofail(src_fnode, smb_audit_rootvp(sr),
316 + srcpath, SMB_MAXPATHLEN);
317 + }
318 + if (dst_exists) {
319 + dstpath = kmem_alloc(SMB_MAXPATHLEN, KM_SLEEP);
320 + smb_node_getpath_nofail(dst_fnode, smb_audit_rootvp(sr),
321 + dstpath, SMB_MAXPATHLEN);
322 + }
323 + }
324 +
290 325 rc = smb_fsop_rename(sr, sr->user_cr,
291 326 src_dnode, src_fnode->od_name,
292 327 dst_dnode, new_name);
293 328
329 + if (do_audit) {
330 + smb_audit_rename_fini(sr,
331 + srcpath,
332 + dst_dnode,
333 + dstpath,
334 + rc == 0,
335 + smb_node_is_dir(src_fnode));
336 +
337 + if (srcpath != NULL)
338 + kmem_free(srcpath, SMB_MAXPATHLEN);
339 + if (dstpath != NULL)
340 + kmem_free(dstpath, SMB_MAXPATHLEN);
341 + }
342 +
294 343 if (rc == 0) {
295 344 /*
296 345 * Note that renames in the same directory are normally
297 346 * delivered in {old,new} pairs, and clients expect them
298 347 * in that order, if both events are delivered.
299 348 */
300 349 int a_src, a_dst; /* action codes */
301 350 if (src_dnode == dst_dnode) {
302 351 a_src = FILE_ACTION_RENAMED_OLD_NAME;
303 352 a_dst = FILE_ACTION_RENAMED_NEW_NAME;
304 353 } else {
305 354 a_src = FILE_ACTION_REMOVED;
306 355 a_dst = FILE_ACTION_ADDED;
307 356 }
308 357 smb_node_notify_change(src_dnode, a_src, src_fnode->od_name);
309 358 smb_node_notify_change(dst_dnode, a_dst, new_name);
310 359 }
311 360
312 361 smb_rename_release_src(sr);
313 362
314 363 if (dst_fqi->fq_fnode) {
315 364 smb_node_end_crit(dst_fnode);
316 365 smb_node_release(dst_fnode);
317 366 }
318 367 smb_node_release(dst_dnode);
319 368
320 369 return (smb_rename_errno2status(rc));
321 370 }
322 371
323 372 /*
324 373 * smb_rename_check_stream
325 374 *
326 375 * For a stream rename the dst path must begin with ':', or "\\:".
327 376 * We don't yet support stream rename, Return EACCES.
328 377 *
329 378 * If not a stream rename, in accordance with the above rule,
330 379 * it is not valid for either the src or dst to be a stream.
331 380 * Return EINVAL.
332 381 */
333 382 static int
334 383 smb_rename_check_stream(smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
335 384 {
336 385 smb_node_t *src_fnode = src_fqi->fq_fnode;
337 386 char *src_path = src_fqi->fq_path.pn_path;
338 387 char *dst_path = dst_fqi->fq_path.pn_path;
339 388
340 389 /* We do not yet support named stream rename - ACCESS DENIED */
341 390 if ((dst_path[0] == ':') ||
342 391 ((dst_path[0] == '\\') && (dst_path[1] == ':'))) {
343 392 return (EACCES);
344 393 }
345 394
346 395 /*
347 396 * If not stream rename (above) neither src or dst can be
348 397 * a named stream.
349 398 */
350 399
351 400 if (smb_is_stream_name(dst_path))
352 401 return (EINVAL);
353 402
354 403 if (src_fqi->fq_fnode) {
355 404 if (SMB_IS_STREAM(src_fnode))
356 405 return (EINVAL);
357 406 } else {
358 407 if (smb_is_stream_name(src_path))
359 408 return (EINVAL);
360 409 }
361 410
362 411 return (0);
363 412 }
364 413
365 414
366 415 /*
367 416 * smb_setinfo_link
368 417 *
369 418 * Implements FileRenameInformation for SMB1 Trans2 setinfo, SMB2 setinfo.
370 419 * If the new filename (dst_fqi) already exists it may be overwritten
371 420 * if flags == 1.
372 421 *
373 422 * The passed path is a full path relative to the share root.
374 423 *
375 424 * Returns NT status codes.
376 425 *
377 426 * Similar to smb_setinfo_rename(), above.
378 427 */
379 428 uint32_t
380 429 smb_setinfo_link(smb_request_t *sr, smb_node_t *node, char *path, int flags)
381 430 {
382 431 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
383 432 smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
384 433 smb_pathname_t *dst_pn = &dst_fqi->fq_path;
385 434 uint32_t status;
386 435
387 436 sr->arg.dirop.flags = flags ? SMB_RENAME_FLAG_OVERWRITE : 0;
388 437 sr->arg.dirop.info_level = FileLinkInformation;
389 438
390 439 src_fqi->fq_sattr = SMB_SEARCH_ATTRIBUTES;
391 440 src_fqi->fq_fnode = node;
392 441 src_fqi->fq_dnode = node->n_dnode;
393 442
394 443 /* validate the dst pathname */
395 444 smb_pathname_init(sr, dst_pn, path);
396 445 if (!smb_pathname_validate(sr, dst_pn))
397 446 return (NT_STATUS_OBJECT_NAME_INVALID);
398 447
399 448 status = smb_make_link(sr, src_fqi, dst_fqi);
400 449 return (status);
401 450 }
402 451
403 452 /*
404 453 * smb_make_link
405 454 *
406 455 * Creating a hard link (adding an additional name) for a file.
407 456 *
408 457 * If the source and destination are identical, we go through all
409 458 * the checks but we don't create a link.
410 459 *
411 460 * If the file is a symlink we create the hardlink on the target
412 461 * of the symlink (i.e. use SMB_FOLLOW_LINKS when looking up src).
413 462 * If the target of the symlink does not exist we fail with ENOENT.
414 463 *
415 464 * Returns NT status values.
416 465 *
417 466 * Similar to smb_common_rename() above.
418 467 */
419 468 uint32_t
420 469 smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
421 470 {
422 471 smb_node_t *tnode;
423 472 char *path;
424 473 int rc;
425 474
426 475 tnode = sr->tid_tree->t_snode;
|
↓ open down ↓ |
123 lines elided |
↑ open up ↑ |
427 476 path = dst_fqi->fq_path.pn_path;
428 477
429 478 /* Cannnot create link on named stream */
430 479 if (smb_is_stream_name(src_fqi->fq_path.pn_path) ||
431 480 smb_is_stream_name(dst_fqi->fq_path.pn_path)) {
432 481 return (NT_STATUS_INVALID_PARAMETER);
433 482 }
434 483
435 484 /* The source node may already have been provided */
436 485 if (src_fqi->fq_fnode) {
437 - smb_node_start_crit(src_fqi->fq_fnode, RW_READER);
438 - smb_node_ref(src_fqi->fq_fnode);
439 486 smb_node_ref(src_fqi->fq_dnode);
487 + smb_node_ref(src_fqi->fq_fnode);
440 488 } else {
441 489 /* lookup and validate src node */
442 490 rc = smb_rename_lookup_src(sr);
443 491 if (rc != 0)
444 492 return (smb_rename_errno2status(rc));
493 + /* Holding refs on dnode, fnode */
445 494 }
446 495
447 496 /* Not valid to create hardlink for directory */
448 497 if (smb_node_is_dir(src_fqi->fq_fnode)) {
449 - smb_rename_release_src(sr);
498 + smb_node_release(src_fqi->fq_dnode);
499 + smb_node_release(src_fqi->fq_fnode);
450 500 return (NT_STATUS_FILE_IS_A_DIRECTORY);
451 501 }
452 502
453 503 /*
504 + * Unlike in rename, we will not unlink the src,
505 + * so skip the smb_rename_check_src() call, and
506 + * just "start crit" instead.
507 + */
508 + smb_node_start_crit(src_fqi->fq_fnode, RW_READER);
509 +
510 + /*
454 511 * Find the destination dnode and last component.
455 512 * May already be provided, i.e. when called via
456 513 * SMB1 trans2 setinfo.
457 514 */
458 515 if (dst_fqi->fq_dnode) {
459 516 smb_node_ref(dst_fqi->fq_dnode);
460 517 } else {
461 518 rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
462 519 &dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
463 520 if (rc != 0) {
464 521 smb_rename_release_src(sr);
465 522 return (smb_rename_errno2status(rc));
466 523 }
467 524 }
468 525
469 526 /* If CI name match in same directory, we're done */
470 527 if ((src_fqi->fq_dnode == dst_fqi->fq_dnode) &&
471 528 (smb_strcasecmp(src_fqi->fq_fnode->od_name,
472 529 dst_fqi->fq_last_comp, 0) == 0)) {
473 530 smb_rename_release_src(sr);
474 531 smb_node_release(dst_fqi->fq_dnode);
475 532 return (0);
476 533 }
477 534
478 535 if (smb_is_invalid_filename(dst_fqi->fq_last_comp)) {
479 536 smb_rename_release_src(sr);
480 537 smb_node_release(dst_fqi->fq_dnode);
481 538 return (NT_STATUS_OBJECT_NAME_INVALID);
482 539 }
483 540
484 541 /* Lookup the destination node. It MUST NOT exist. */
485 542 rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
486 543 dst_fqi->fq_dnode, dst_fqi->fq_last_comp, &dst_fqi->fq_fnode);
487 544 if (rc == 0) {
488 545 smb_node_release(dst_fqi->fq_fnode);
489 546 rc = EEXIST;
490 547 }
491 548 if (rc != ENOENT) {
492 549 smb_rename_release_src(sr);
493 550 smb_node_release(dst_fqi->fq_dnode);
494 551 return (smb_rename_errno2status(rc));
495 552 }
496 553
497 554 rc = smb_fsop_link(sr, sr->user_cr, src_fqi->fq_fnode,
498 555 dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
499 556
500 557 if (rc == 0) {
501 558 smb_node_notify_change(dst_fqi->fq_dnode,
502 559 FILE_ACTION_ADDED, dst_fqi->fq_last_comp);
|
↓ open down ↓ |
39 lines elided |
↑ open up ↑ |
503 560 }
504 561
505 562 smb_rename_release_src(sr);
506 563 smb_node_release(dst_fqi->fq_dnode);
507 564 return (smb_rename_errno2status(rc));
508 565 }
509 566
510 567 /*
511 568 * smb_rename_lookup_src
512 569 *
513 - * Lookup the src node, checking for sharing violations and
514 - * breaking any existing BATCH oplock.
515 - * Populate sr->arg.dirop.fqi
570 + * Lookup the src node for a path-based link or rename.
516 571 *
517 - * Upon success, the dnode and fnode will have holds and the
518 - * fnode will be in a critical section. These should be
519 - * released using smb_rename_release_src().
572 + * On success, fills in sr->arg.dirop.fqi, and returns with
573 + * holds on the source dnode and fnode.
520 574 *
521 575 * Returns errno values.
522 576 */
523 577 static int
524 578 smb_rename_lookup_src(smb_request_t *sr)
525 579 {
526 - smb_node_t *src_node, *tnode;
527 - DWORD status;
528 - int rc;
529 - int count;
580 + smb_node_t *tnode;
530 581 char *path;
582 + int rc;
531 583
532 584 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
533 585
534 586 if (smb_is_stream_name(src_fqi->fq_path.pn_path))
535 587 return (EINVAL);
536 588
537 589 /* Lookup the source node */
538 590 tnode = sr->tid_tree->t_snode;
539 591 path = src_fqi->fq_path.pn_path;
540 592 rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
541 593 &src_fqi->fq_dnode, src_fqi->fq_last_comp);
542 594 if (rc != 0)
543 595 return (rc);
596 + /* hold fq_dnode */
544 597
545 598 rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
546 599 src_fqi->fq_dnode, src_fqi->fq_last_comp, &src_fqi->fq_fnode);
547 600 if (rc != 0) {
548 601 smb_node_release(src_fqi->fq_dnode);
549 602 return (rc);
550 603 }
551 - src_node = src_fqi->fq_fnode;
604 + /* hold fq_dnode, fq_fnode */
552 605
553 - rc = smb_rename_check_attr(sr, src_node, src_fqi->fq_sattr);
606 + rc = smb_rename_check_attr(sr, src_fqi->fq_fnode, src_fqi->fq_sattr);
554 607 if (rc != 0) {
555 608 smb_node_release(src_fqi->fq_fnode);
556 609 smb_node_release(src_fqi->fq_dnode);
557 610 return (rc);
558 611 }
559 612
613 + return (0);
614 +}
615 +
616 +/*
617 + * smb_rename_check_src
618 + *
619 + * Check for sharing violations on the file we'll unlink, and
620 + * break oplocks for the rename operation. Note that we've
621 + * already done oplock breaks associated with opening a handle
622 + * on the file to rename.
623 + *
624 + * On success, returns with fnode in a critical section,
625 + * as if smb_node_start_crit were called with the node.
626 + * Caller should release using smb_rename_release_src().
627 + */
628 +static uint32_t
629 +smb_rename_check_src(smb_request_t *sr, smb_fqi_t *src_fqi)
630 +{
631 + smb_node_t *src_node = src_fqi->fq_fnode;
632 + uint32_t status;
633 +
560 634 /*
561 635 * Break BATCH oplock before ofile checks. If a client
562 636 * has a file open, this will force a flush or close,
563 637 * which may affect the outcome of any share checking.
638 + *
639 + * This operation may have either a handle or path for
640 + * the source node (that will be unlinked via rename).
564 641 */
565 - (void) smb_oplock_break(sr, src_node,
566 - SMB_OPLOCK_BREAK_TO_LEVEL_II | SMB_OPLOCK_BREAK_BATCH);
567 642
643 + if (sr->fid_ofile != NULL) {
644 + status = smb_oplock_break_SETINFO(src_node, sr->fid_ofile,
645 + FileRenameInformation);
646 + if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
647 + if (sr->session->dialect >= SMB_VERS_2_BASE)
648 + (void) smb2sr_go_async(sr);
649 + (void) smb_oplock_wait_break(src_node, 0);
650 + status = 0;
651 + }
652 +
653 + /*
654 + * Sharing violations were checked at open time.
655 + * Just "start crit" to be consistent with the
656 + * state returned for path-based rename.
657 + */
658 + smb_node_start_crit(src_fqi->fq_fnode, RW_READER);
659 + return (NT_STATUS_SUCCESS);
660 + }
661 +
568 662 /*
569 - * Wait (a little) for the oplock break to be
570 - * responded to by clients closing handles.
571 - * Hold node->n_lock as reader to keep new
572 - * ofiles from showing up after we check.
663 + * This code path operates without a real open, so
664 + * break oplocks now as if we opened for delete.
665 + * Note: SMB2 does only ofile-based rename.
666 + *
667 + * Todo: Use an "internal open" for path-based
668 + * rename and delete, then delete this code.
573 669 */
574 - smb_node_rdlock(src_node);
575 - for (count = 0; count <= 12; count++) {
576 - status = smb_node_rename_check(src_node);
577 - if (status != NT_STATUS_SHARING_VIOLATION)
578 - break;
579 - smb_node_unlock(src_node);
580 - delay(MSEC_TO_TICK(100));
581 - smb_node_rdlock(src_node);
670 + ASSERT(sr->session->dialect < SMB_VERS_2_BASE);
671 + status = smb_oplock_break_DELETE(src_node, NULL);
672 + if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
673 + (void) smb_oplock_wait_break(src_node, 0);
582 674 }
675 +
676 + /*
677 + * Path-based access to the src file (no ofile)
678 + * so check for sharing violations here.
679 + */
680 + smb_node_rdlock(src_node);
681 + status = smb_node_rename_check(src_node);
583 682 if (status != NT_STATUS_SUCCESS) {
584 683 smb_node_unlock(src_node);
585 - smb_node_release(src_fqi->fq_fnode);
586 - smb_node_release(src_fqi->fq_dnode);
587 - return (EPIPE); /* = ERRbadshare */
684 + return (status);
588 685 }
589 686
687 + status = smb_oplock_break_SETINFO(src_node, NULL,
688 + FileRenameInformation);
689 + if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
690 + (void) smb_oplock_wait_break(src_node, 0);
691 + }
692 +
590 693 /*
591 694 * Note, the combination of these two:
592 695 * smb_node_rdlock(node);
593 696 * nbl_start_crit(node->vp, RW_READER);
594 697 * is equivalent to this call:
595 698 * smb_node_start_crit(node, RW_READER)
596 699 *
597 700 * Cleanup after this point should use:
598 701 * smb_node_end_crit(src_node)
599 702 */
600 703 nbl_start_crit(src_node->vp, RW_READER);
601 704
602 705 /*
603 706 * This checks nbl_share_conflict, nbl_lock_conflict
604 707 */
605 708 status = smb_nbl_conflict(src_node, 0, UINT64_MAX, NBL_RENAME);
606 709 if (status != NT_STATUS_SUCCESS) {
607 710 smb_node_end_crit(src_node);
608 - smb_node_release(src_fqi->fq_fnode);
609 - smb_node_release(src_fqi->fq_dnode);
610 - if (status == NT_STATUS_SHARING_VIOLATION)
611 - return (EPIPE); /* = ERRbadshare */
612 - return (EACCES);
613 711 }
614 712
615 - /* NB: Caller expects holds on src_fqi fnode, dnode */
616 - return (0);
713 + /* NB: Caller expects to be "in crit" on fnode. */
714 + return (status);
617 715 }
618 716
619 717 /*
620 718 * smb_rename_release_src
621 719 */
622 720 static void
623 721 smb_rename_release_src(smb_request_t *sr)
624 722 {
625 723 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
626 724
627 725 smb_node_end_crit(src_fqi->fq_fnode);
628 726 smb_node_release(src_fqi->fq_fnode);
629 727 smb_node_release(src_fqi->fq_dnode);
630 728 }
631 729
632 730
633 731 static int
634 732 smb_rename_check_attr(smb_request_t *sr, smb_node_t *node, uint16_t sattr)
635 733 {
636 734 smb_attr_t attr;
637 735
638 736 bzero(&attr, sizeof (attr));
639 737 attr.sa_mask = SMB_AT_DOSATTR;
640 738 if (smb_node_getattr(sr, node, zone_kcred(), NULL, &attr) != 0)
641 739 return (EACCES);
642 740
643 741 if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) &&
644 742 !(SMB_SEARCH_HIDDEN(sattr)))
645 743 return (ESRCH);
646 744
647 745 if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) &&
648 746 !(SMB_SEARCH_SYSTEM(sattr)))
649 747 return (ESRCH);
650 748
651 749 return (0);
652 750 }
653 751
654 752 /*
655 753 * The following values are based on observed WFWG, Windows 9x, Windows NT
656 754 * and Windows 2000 behaviour.
657 755 *
658 756 * ERROR_FILE_EXISTS doesn't work for Windows 98 clients.
659 757 *
660 758 * Windows 95 clients don't see the problem because the target is deleted
661 759 * before the rename request.
662 760 */
663 761 static uint32_t
664 762 smb_rename_errno2status(int errnum)
665 763 {
666 764 static struct {
667 765 int errnum;
668 766 uint32_t status32;
669 767 } rc_map[] = {
670 768 { EEXIST, NT_STATUS_OBJECT_NAME_COLLISION },
671 769 { EPIPE, NT_STATUS_SHARING_VIOLATION },
672 770 { ENOENT, NT_STATUS_OBJECT_NAME_NOT_FOUND },
673 771 { ESRCH, NT_STATUS_NO_SUCH_FILE },
674 772 { EINVAL, NT_STATUS_INVALID_PARAMETER },
675 773 { EACCES, NT_STATUS_ACCESS_DENIED },
676 774 { EISDIR, NT_STATUS_FILE_IS_A_DIRECTORY },
677 775 { EIO, NT_STATUS_INTERNAL_ERROR }
678 776 };
679 777
680 778 int i;
681 779
682 780 if (errnum == 0)
683 781 return (0);
684 782
685 783 for (i = 0; i < sizeof (rc_map)/sizeof (rc_map[0]); ++i) {
686 784 if (rc_map[i].errnum == errnum) {
687 785 return (rc_map[i].status32);
688 786 }
689 787 }
690 788
691 789 return (smb_errno2status(errnum));
692 790 }
|
↓ open down ↓ |
66 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX