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 "@(#)smb_rename.c 1.6 08/08/04 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 *, smb_fqi_t *, smb_fqi_t *); 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 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 58 smb_fqi_t *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 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 85 smb_fqi_t *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 smb_fqi_t *src_fqi, 166 smb_fqi_t *dst_fqi) 167 { 168 smb_node_t *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 186 smb_oplock_break(src_node); 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, src_node, 0, UINT64_MAX, B_TRUE); 214 215 if (status != NT_STATUS_SUCCESS) { 216 smb_node_end_crit(src_node); 217 218 smb_node_release(src_node); 219 smb_node_release(src_fqi->dir_snode); 220 221 SMB_NULL_FQI_NODES(*src_fqi); 222 SMB_NULL_FQI_NODES(*dst_fqi); 223 return (EACCES); 224 } 225 226 if (utf8_strcasecmp(src_fqi->path, dst_fqi->path) == 0) { 227 if ((rc = smbd_fs_query(sr, dst_fqi, 0)) != 0) { 228 smb_node_end_crit(src_node); 229 230 smb_node_release(src_node); 231 smb_node_release(src_fqi->dir_snode); 232 233 SMB_NULL_FQI_NODES(*src_fqi); 234 SMB_NULL_FQI_NODES(*dst_fqi); 235 return (rc); 236 } 237 238 /* 239 * Because the fqm parameter to smbd_fs_query() was 0, 240 * a successful return value means that dst_fqi->last_snode 241 * may be NULL. 242 */ 243 if (dst_fqi->last_snode) 244 smb_node_release(dst_fqi->last_snode); 245 246 rc = strcmp(src_fqi->last_comp_od, dst_fqi->last_comp); 247 if (rc == 0) { 248 smb_node_end_crit(src_node); 249 250 smb_node_release(src_node); 251 smb_node_release(src_fqi->dir_snode); 252 smb_node_release(dst_fqi->dir_snode); 253 254 SMB_NULL_FQI_NODES(*src_fqi); 255 SMB_NULL_FQI_NODES(*dst_fqi); 256 return (0); 257 } 258 259 rc = smb_fsop_rename(sr, sr->user_cr, 260 src_fqi->dir_snode, 261 src_fqi->last_comp_od, 262 dst_fqi->dir_snode, 263 dst_fqi->last_comp); 264 265 if (rc != 0) { 266 smb_node_release(src_fqi->dir_snode); 267 smb_node_release(dst_fqi->dir_snode); 268 269 SMB_NULL_FQI_NODES(*src_fqi); 270 SMB_NULL_FQI_NODES(*dst_fqi); 271 } 272 273 smb_node_end_crit(src_node); 274 275 smb_node_release(src_node); 276 return (rc); 277 } 278 279 rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST); 280 if (rc != 0) { 281 smb_node_end_crit(src_node); 282 283 smb_node_release(src_node); 284 smb_node_release(src_fqi->dir_snode); 285 286 SMB_NULL_FQI_NODES(*src_fqi); 287 SMB_NULL_FQI_NODES(*dst_fqi); 288 return (rc); 289 } 290 291 /* 292 * Because of FQM_PATH_MUST_NOT_EXIST and the successful return 293 * value, only dst_fqi->dir_snode is valid (dst_fqi->last_snode 294 * is NULL). 295 */ 296 297 /* 298 * Use the unmangled form of the destination name if the 299 * source and destination names are the same and the source 300 * name is mangled. (We are taking a chance here, assuming 301 * that this is what the user wants.) 302 */ 303 304 if ((smb_maybe_mangled_name(src_fqi->last_comp)) && 305 (strcmp(src_fqi->last_comp, dst_fqi->last_comp) == 0)) { 306 dstname = src_fqi->last_comp_od; 307 } else { 308 dstname = dst_fqi->last_comp; 309 } 310 311 rc = smb_fsop_rename(sr, sr->user_cr, 312 src_fqi->dir_snode, 313 src_fqi->last_comp_od, 314 dst_fqi->dir_snode, 315 dstname); 316 317 if (rc != 0) { 318 smb_node_release(src_fqi->dir_snode); 319 smb_node_release(dst_fqi->dir_snode); 320 321 SMB_NULL_FQI_NODES(*src_fqi); 322 SMB_NULL_FQI_NODES(*dst_fqi); 323 } 324 325 smb_node_end_crit(src_node); 326 327 smb_node_release(src_node); 328 329 return (rc); 330 } 331