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, sr->session, B_FALSE); 184 185 for (count = 0; count <= 3; count++) { 186 if (count) { 187 smb_node_end_crit(src_node); 188 delay(MSEC_TO_TICK(400)); 189 } 190 191 smb_node_start_crit(src_node, RW_READER); 192 193 status = smb_node_rename_check(src_node); 194 195 if (status != NT_STATUS_SHARING_VIOLATION) 196 break; 197 } 198 199 if (status == NT_STATUS_SHARING_VIOLATION) { 200 smb_node_end_crit(src_node); 201 202 smb_node_release(src_node); 203 smb_node_release(src_fqi->dir_snode); 204 205 SMB_NULL_FQI_NODES(*src_fqi); 206 SMB_NULL_FQI_NODES(*dst_fqi); 207 return (EPIPE); /* = ERRbadshare */ 208 } 209 210 status = smb_range_check(sr, src_node, 0, UINT64_MAX, B_TRUE); 211 212 if (status != NT_STATUS_SUCCESS) { 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 (EACCES); 221 } 222 223 if (utf8_strcasecmp(src_fqi->path, dst_fqi->path) == 0) { 224 if ((rc = smbd_fs_query(sr, dst_fqi, 0)) != 0) { 225 smb_node_end_crit(src_node); 226 227 smb_node_release(src_node); 228 smb_node_release(src_fqi->dir_snode); 229 230 SMB_NULL_FQI_NODES(*src_fqi); 231 SMB_NULL_FQI_NODES(*dst_fqi); 232 return (rc); 233 } 234 235 /* 236 * Because the fqm parameter to smbd_fs_query() was 0, 237 * a successful return value means that dst_fqi->last_snode 238 * may be NULL. 239 */ 240 if (dst_fqi->last_snode) 241 smb_node_release(dst_fqi->last_snode); 242 243 rc = strcmp(src_fqi->last_comp_od, dst_fqi->last_comp); 244 if (rc == 0) { 245 smb_node_end_crit(src_node); 246 247 smb_node_release(src_node); 248 smb_node_release(src_fqi->dir_snode); 249 smb_node_release(dst_fqi->dir_snode); 250 251 SMB_NULL_FQI_NODES(*src_fqi); 252 SMB_NULL_FQI_NODES(*dst_fqi); 253 return (0); 254 } 255 256 rc = smb_fsop_rename(sr, sr->user_cr, 257 src_fqi->dir_snode, 258 src_fqi->last_comp_od, 259 dst_fqi->dir_snode, 260 dst_fqi->last_comp); 261 262 if (rc != 0) { 263 smb_node_release(src_fqi->dir_snode); 264 smb_node_release(dst_fqi->dir_snode); 265 266 SMB_NULL_FQI_NODES(*src_fqi); 267 SMB_NULL_FQI_NODES(*dst_fqi); 268 } 269 270 smb_node_end_crit(src_node); 271 272 smb_node_release(src_node); 273 return (rc); 274 } 275 276 rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST); 277 if (rc != 0) { 278 smb_node_end_crit(src_node); 279 280 smb_node_release(src_node); 281 smb_node_release(src_fqi->dir_snode); 282 283 SMB_NULL_FQI_NODES(*src_fqi); 284 SMB_NULL_FQI_NODES(*dst_fqi); 285 return (rc); 286 } 287 288 /* 289 * Because of FQM_PATH_MUST_NOT_EXIST and the successful return 290 * value, only dst_fqi->dir_snode is valid (dst_fqi->last_snode 291 * is NULL). 292 */ 293 294 /* 295 * Use the unmangled form of the destination name if the 296 * source and destination names are the same and the source 297 * name is mangled. (We are taking a chance here, assuming 298 * that this is what the user wants.) 299 */ 300 301 if ((smb_maybe_mangled_name(src_fqi->last_comp)) && 302 (strcmp(src_fqi->last_comp, dst_fqi->last_comp) == 0)) { 303 dstname = src_fqi->last_comp_od; 304 } else { 305 dstname = dst_fqi->last_comp; 306 } 307 308 rc = smb_fsop_rename(sr, sr->user_cr, 309 src_fqi->dir_snode, 310 src_fqi->last_comp_od, 311 dst_fqi->dir_snode, 312 dstname); 313 314 if (rc != 0) { 315 smb_node_release(src_fqi->dir_snode); 316 smb_node_release(dst_fqi->dir_snode); 317 318 SMB_NULL_FQI_NODES(*src_fqi); 319 SMB_NULL_FQI_NODES(*dst_fqi); 320 } 321 322 smb_node_end_crit(src_node); 323 324 smb_node_release(src_node); 325 326 return (rc); 327 } 328