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