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