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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <smbsrv/nterror.h> 29 #include <sys/synch.h> 30 #include <smbsrv/smb_incl.h> 31 #include <smbsrv/smb_fsops.h> 32 #include <sys/nbmlock.h> 33 34 static int smb_do_rename(smb_request_t *, struct smb_fqi *, struct smb_fqi *); 35 36 /* 37 * smb_com_rename 38 * 39 * Rename a file. Files OldFileName must exist and NewFileName must not. 40 * Both pathnames must be relative to the Tid specified in the request. 41 * Open files may be renamed. 42 * 43 * Multiple files may be renamed in response to a single request as Rename 44 * File supports wildcards in the file name (last component of the path). 45 * NOTE: we don't support rename with wildcards. 46 * 47 * SearchAttributes indicates the attributes that the target file(s) must 48 * have. If SearchAttributes is zero then only normal files are renamed. 49 * If the system file or hidden attributes are specified then the rename 50 * is inclusive - both the specified type(s) of files and normal files are 51 * renamed. The encoding of SearchAttributes is described in section 3.10 52 * - File Attribute Encoding. 53 */ 54 smb_sdrc_t 55 smb_pre_rename(smb_request_t *sr) 56 { 57 struct smb_fqi *src_fqi = &sr->arg.dirop.fqi; 58 struct smb_fqi *dst_fqi = &sr->arg.dirop.dst_fqi; 59 int rc; 60 61 if ((rc = smbsr_decode_vwv(sr, "w", &src_fqi->srch_attr)) == 0) { 62 rc = smbsr_decode_data(sr, "%SS", sr, &src_fqi->path, 63 &dst_fqi->path); 64 65 dst_fqi->srch_attr = 0; 66 } 67 68 DTRACE_SMB_2(op__Rename__start, smb_request_t *, sr, 69 struct dirop *, &sr->arg.dirop); 70 71 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 72 } 73 74 void 75 smb_post_rename(smb_request_t *sr) 76 { 77 DTRACE_SMB_1(op__Rename__done, smb_request_t *, sr); 78 } 79 80 smb_sdrc_t 81 smb_com_rename(smb_request_t *sr) 82 { 83 static kmutex_t mutex; 84 struct smb_fqi *src_fqi = &sr->arg.dirop.fqi; 85 struct smb_fqi *dst_fqi = &sr->arg.dirop.dst_fqi; 86 struct smb_node *dst_node; 87 int rc; 88 89 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { 90 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 91 ERRDOS, ERROR_ACCESS_DENIED); 92 return (SDRC_ERROR); 93 } 94 95 mutex_enter(&mutex); 96 rc = smb_do_rename(sr, src_fqi, dst_fqi); 97 mutex_exit(&mutex); 98 99 if (rc != 0) { 100 /* 101 * The following values are based on observed WFWG, 102 * Windows 9x, NT and Windows 2000 behaviour. 103 * ERROR_FILE_EXISTS doesn't work for Windows 98 clients. 104 * Windows 95 clients don't see the problem because the 105 * target is deleted before the rename request. 106 */ 107 switch (rc) { 108 case EEXIST: 109 smbsr_error(sr, NT_STATUS_OBJECT_NAME_COLLISION, 110 ERRDOS, ERROR_ALREADY_EXISTS); 111 break; 112 case EPIPE: 113 smbsr_error(sr, NT_STATUS_SHARING_VIOLATION, 114 ERRDOS, ERROR_SHARING_VIOLATION); 115 break; 116 case ENOENT: 117 smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, 118 ERRDOS, ERROR_FILE_NOT_FOUND); 119 break; 120 default: 121 smbsr_errno(sr, rc); 122 break; 123 } 124 125 return (SDRC_ERROR); 126 } 127 128 if (src_fqi->dir_snode) 129 smb_node_release(src_fqi->dir_snode); 130 131 dst_node = dst_fqi->dir_snode; 132 if (dst_node) { 133 if (dst_node->flags & NODE_FLAGS_NOTIFY_CHANGE) { 134 dst_node->flags |= NODE_FLAGS_CHANGED; 135 smb_process_node_notify_change_queue(dst_node); 136 } 137 smb_node_release(dst_node); 138 } 139 140 SMB_NULL_FQI_NODES(*src_fqi); 141 SMB_NULL_FQI_NODES(*dst_fqi); 142 143 rc = smbsr_encode_empty_result(sr); 144 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 145 } 146 147 /* 148 * smb_do_rename 149 * 150 * Backend to smb_com_rename to ensure that the rename operation is atomic. 151 * This function should be called within a mutual exclusion region. If the 152 * source and destination are identical, we don't actually do a rename, we 153 * just check that the conditions are right. If the source and destination 154 * files differ only in case, we a case-sensitive rename. Otherwise, we do 155 * a full case-insensitive rename. 156 * 157 * This function should always return errno values. 158 * 159 * Upon success, the last_snode's and dir_snode's of both src_fqi and dst_fqi 160 * are not released in this routine but in smb_com_rename(). 161 */ 162 static int 163 smb_do_rename( 164 smb_request_t *sr, 165 struct smb_fqi *src_fqi, 166 struct smb_fqi *dst_fqi) 167 { 168 struct smb_node *src_node; 169 char *dstname; 170 DWORD status; 171 int rc; 172 int count; 173 174 if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0) { 175 return (rc); 176 } 177 178 src_node = src_fqi->last_snode; 179 180 /* 181 * Break the oplock before access checks. If a client 182 * has a file open, this will force a flush or close, 183 * which may affect the outcome of any share checking. 184 */ 185 if (OPLOCKS_IN_FORCE(src_node)) { 186 status = smb_break_oplock(sr, src_node); 187 188 if (status != NT_STATUS_SUCCESS) { 189 smb_node_release(src_node); 190 smb_node_release(src_fqi->dir_snode); 191 192 SMB_NULL_FQI_NODES(*src_fqi); 193 SMB_NULL_FQI_NODES(*dst_fqi); 194 return (EACCES); 195 } 196 } 197 198 for (count = 0; count <= 3; count++) { 199 if (count) { 200 smb_node_end_crit(src_node); 201 delay(MSEC_TO_TICK(400)); 202 } 203 204 smb_node_start_crit(src_node, RW_READER); 205 206 status = smb_node_rename_check(src_node); 207 208 if (status != NT_STATUS_SHARING_VIOLATION) 209 break; 210 } 211 212 if (status == NT_STATUS_SHARING_VIOLATION) { 213 smb_node_end_crit(src_node); 214 215 smb_node_release(src_node); 216 smb_node_release(src_fqi->dir_snode); 217 218 SMB_NULL_FQI_NODES(*src_fqi); 219 SMB_NULL_FQI_NODES(*dst_fqi); 220 return (EPIPE); /* = ERRbadshare */ 221 } 222 223 status = smb_range_check(sr, sr->user_cr, src_node, 0, UINT64_MAX, 224 B_TRUE); 225 226 if (status != NT_STATUS_SUCCESS) { 227 smb_node_end_crit(src_node); 228 229 smb_node_release(src_node); 230 smb_node_release(src_fqi->dir_snode); 231 232 SMB_NULL_FQI_NODES(*src_fqi); 233 SMB_NULL_FQI_NODES(*dst_fqi); 234 return (EACCES); 235 } 236 237 if (utf8_strcasecmp(src_fqi->path, dst_fqi->path) == 0) { 238 if ((rc = smbd_fs_query(sr, dst_fqi, 0)) != 0) { 239 smb_node_end_crit(src_node); 240 241 smb_node_release(src_node); 242 smb_node_release(src_fqi->dir_snode); 243 244 SMB_NULL_FQI_NODES(*src_fqi); 245 SMB_NULL_FQI_NODES(*dst_fqi); 246 return (rc); 247 } 248 249 /* 250 * Because the fqm parameter to smbd_fs_query() was 0, 251 * a successful return value means that dst_fqi->last_snode 252 * may be NULL. 253 */ 254 if (dst_fqi->last_snode) 255 smb_node_release(dst_fqi->last_snode); 256 257 rc = strcmp(src_fqi->last_comp_od, dst_fqi->last_comp); 258 if (rc == 0) { 259 smb_node_end_crit(src_node); 260 261 smb_node_release(src_node); 262 smb_node_release(src_fqi->dir_snode); 263 smb_node_release(dst_fqi->dir_snode); 264 265 SMB_NULL_FQI_NODES(*src_fqi); 266 SMB_NULL_FQI_NODES(*dst_fqi); 267 return (0); 268 } 269 270 rc = smb_fsop_rename(sr, sr->user_cr, 271 src_fqi->dir_snode, 272 src_fqi->last_comp_od, 273 dst_fqi->dir_snode, 274 dst_fqi->last_comp); 275 276 if (rc != 0) { 277 smb_node_release(src_fqi->dir_snode); 278 smb_node_release(dst_fqi->dir_snode); 279 280 SMB_NULL_FQI_NODES(*src_fqi); 281 SMB_NULL_FQI_NODES(*dst_fqi); 282 } 283 284 smb_node_end_crit(src_node); 285 286 smb_node_release(src_node); 287 return (rc); 288 } 289 290 rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST); 291 if (rc != 0) { 292 smb_node_end_crit(src_node); 293 294 smb_node_release(src_node); 295 smb_node_release(src_fqi->dir_snode); 296 297 SMB_NULL_FQI_NODES(*src_fqi); 298 SMB_NULL_FQI_NODES(*dst_fqi); 299 return (rc); 300 } 301 302 /* 303 * Because of FQM_PATH_MUST_NOT_EXIST and the successful return 304 * value, only dst_fqi->dir_snode is valid (dst_fqi->last_snode 305 * is NULL). 306 */ 307 308 /* 309 * Use the unmangled form of the destination name if the 310 * source and destination names are the same and the source 311 * name is mangled. (We are taking a chance here, assuming 312 * that this is what the user wants.) 313 */ 314 315 if ((smb_maybe_mangled_name(src_fqi->last_comp)) && 316 (strcmp(src_fqi->last_comp, dst_fqi->last_comp) == 0)) { 317 dstname = src_fqi->last_comp_od; 318 } else { 319 dstname = dst_fqi->last_comp; 320 } 321 322 rc = smb_fsop_rename(sr, sr->user_cr, 323 src_fqi->dir_snode, 324 src_fqi->last_comp_od, 325 dst_fqi->dir_snode, 326 dstname); 327 328 if (rc != 0) { 329 smb_node_release(src_fqi->dir_snode); 330 smb_node_release(dst_fqi->dir_snode); 331 332 SMB_NULL_FQI_NODES(*src_fqi); 333 SMB_NULL_FQI_NODES(*dst_fqi); 334 } 335 336 smb_node_end_crit(src_node); 337 338 smb_node_release(src_node); 339 340 return (rc); 341 } 342