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