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 2007 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 33 static int smb_do_rename(struct smb_request *sr, 34 struct smb_fqi *src_fqi, 35 struct smb_fqi *dst_fqi); 36 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_raise_cifs_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_raise_cifs_error(sr, 103 NT_STATUS_OBJECT_NAME_COLLISION, 104 ERRDOS, ERROR_ALREADY_EXISTS); 105 /* NOTREACHED */ 106 } 107 108 if (rc == EPIPE) { 109 smbsr_raise_cifs_error(sr, NT_STATUS_SHARING_VIOLATION, 110 ERRDOS, ERROR_SHARING_VIOLATION); 111 /* NOTREACHED */ 112 } 113 114 smbsr_raise_errno(sr, rc); 115 /* NOTREACHED */ 116 } 117 118 if (src_fqi->dir_snode) 119 smb_node_release(src_fqi->dir_snode); 120 121 dst_node = dst_fqi->dir_snode; 122 if (dst_node) { 123 if (dst_node->flags & NODE_FLAGS_NOTIFY_CHANGE) { 124 dst_node->flags |= NODE_FLAGS_CHANGED; 125 smb_process_node_notify_change_queue(dst_node); 126 } 127 smb_node_release(dst_node); 128 } 129 130 SMB_NULL_FQI_NODES(*src_fqi); 131 SMB_NULL_FQI_NODES(*dst_fqi); 132 133 smbsr_encode_empty_result(sr); 134 135 return (SDRC_NORMAL_REPLY); 136 } 137 138 /* 139 * smb_rename_share_check 140 * 141 * An open file can be renamed if 142 * 143 * 1. isn't opened for data writing or deleting 144 * 145 * 2. Opened with "Deny Delete" share mode 146 * But not opened for data reading or executing 147 * (opened for accessing meta data) 148 */ 149 DWORD 150 smb_rename_share_check(struct smb_node *node) 151 { 152 struct smb_ofile *open; 153 154 if (node == 0 || node->n_refcnt <= 1) 155 return (NT_STATUS_SUCCESS); 156 157 smb_llist_enter(&node->n_ofile_list, RW_READER); 158 open = smb_llist_head(&node->n_ofile_list); 159 while (open) { 160 if (open->f_granted_access & 161 (FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE)) { 162 smb_llist_exit(&node->n_ofile_list); 163 return (NT_STATUS_SHARING_VIOLATION); 164 } 165 166 if ((open->f_share_access & FILE_SHARE_DELETE) == 0) { 167 if (open->f_granted_access & 168 (FILE_READ_DATA | FILE_EXECUTE)) { 169 smb_llist_exit(&node->n_ofile_list); 170 return (NT_STATUS_SHARING_VIOLATION); 171 } 172 } 173 open = smb_llist_next(&node->n_ofile_list, open); 174 } 175 smb_llist_exit(&node->n_ofile_list); 176 return (NT_STATUS_SUCCESS); 177 } 178 179 180 /* 181 * smb_do_rename 182 * 183 * Backend to smb_com_rename to ensure that the rename operation is atomic. 184 * This function should be called within a mutual exclusion region. If the 185 * source and destination are identical, we don't actually do a rename, we 186 * just check that the conditions are right. If the source and destination 187 * files differ only in case, we a case-sensitive rename. Otherwise, we do 188 * a full case-insensitive rename. 189 * 190 * This function should always return errno values. 191 * 192 * Upon success, the last_snode's and dir_snode's of both src_fqi and dst_fqi 193 * are not released in this routine but in smb_com_rename(). 194 */ 195 static int 196 smb_do_rename( 197 struct smb_request *sr, 198 struct smb_fqi *src_fqi, 199 struct smb_fqi *dst_fqi) 200 { 201 struct smb_node *src_node; 202 char *dstname; 203 DWORD status; 204 int rc; 205 int count; 206 207 if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0) { 208 return (rc); 209 } 210 211 src_node = src_fqi->last_snode; 212 213 /* 214 * Break the oplock before access checks. If a client 215 * has a file open, this will force a flush or close, 216 * which may affect the outcome of any share checking. 217 */ 218 if (OPLOCKS_IN_FORCE(src_node)) { 219 status = smb_break_oplock(sr, src_node); 220 221 if (status != NT_STATUS_SUCCESS) { 222 smb_node_release(src_node); 223 smb_node_release(src_fqi->dir_snode); 224 225 SMB_NULL_FQI_NODES(*src_fqi); 226 SMB_NULL_FQI_NODES(*dst_fqi); 227 return (EACCES); 228 } 229 } 230 231 status = smb_lock_range_access(sr, src_node, 0, 0, FILE_WRITE_DATA); 232 if (status != NT_STATUS_SUCCESS) { 233 smb_node_release(src_node); 234 smb_node_release(src_fqi->dir_snode); 235 236 SMB_NULL_FQI_NODES(*src_fqi); 237 SMB_NULL_FQI_NODES(*dst_fqi); 238 return (EACCES); 239 } 240 241 242 for (count = 0; count <= 3; count++) { 243 if (count) 244 delay(MSEC_TO_TICK(400)); 245 status = smb_rename_share_check(src_node); 246 if (status != NT_STATUS_SHARING_VIOLATION) 247 break; 248 } 249 250 smb_node_release(src_node); 251 252 if (status == NT_STATUS_SHARING_VIOLATION) { 253 smb_node_release(src_fqi->dir_snode); 254 255 SMB_NULL_FQI_NODES(*src_fqi); 256 SMB_NULL_FQI_NODES(*dst_fqi); 257 return (EPIPE); /* = ERRbadshare */ 258 } 259 260 if (utf8_strcasecmp(src_fqi->path, dst_fqi->path) == 0) { 261 if ((rc = smbd_fs_query(sr, dst_fqi, 0)) != 0) { 262 smb_node_release(src_fqi->dir_snode); 263 264 SMB_NULL_FQI_NODES(*src_fqi); 265 SMB_NULL_FQI_NODES(*dst_fqi); 266 return (rc); 267 } 268 269 /* 270 * Because the fqm parameter to smbd_fs_query() was 0, 271 * a successful return value means that dst_fqi->last_snode 272 * may be NULL. 273 */ 274 if (dst_fqi->last_snode) 275 smb_node_release(dst_fqi->last_snode); 276 277 rc = strcmp(src_fqi->last_comp_od, dst_fqi->last_comp); 278 if (rc == 0) { 279 smb_node_release(src_fqi->dir_snode); 280 smb_node_release(dst_fqi->dir_snode); 281 282 SMB_NULL_FQI_NODES(*src_fqi); 283 SMB_NULL_FQI_NODES(*dst_fqi); 284 return (0); 285 } 286 287 rc = smb_fsop_rename(sr, sr->user_cr, 288 src_fqi->dir_snode, 289 src_fqi->last_comp_od, 290 dst_fqi->dir_snode, 291 dst_fqi->last_comp); 292 293 if (rc != 0) { 294 smb_node_release(src_fqi->dir_snode); 295 smb_node_release(dst_fqi->dir_snode); 296 297 SMB_NULL_FQI_NODES(*src_fqi); 298 SMB_NULL_FQI_NODES(*dst_fqi); 299 } 300 return (rc); 301 } 302 303 rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST); 304 if (rc != 0) { 305 smb_node_release(src_fqi->dir_snode); 306 307 SMB_NULL_FQI_NODES(*src_fqi); 308 SMB_NULL_FQI_NODES(*dst_fqi); 309 return (rc); 310 } 311 312 /* 313 * Because of FQM_PATH_MUST_NOT_EXIST and the successful return 314 * value, only dst_fqi->dir_snode is valid (dst_fqi->last_snode 315 * is NULL). 316 */ 317 318 /* 319 * Use the unmangled form of the destination name if the 320 * source and destination names are the same and the source 321 * name is mangled. (We are taking a chance here, assuming 322 * that this is what the user wants.) 323 */ 324 325 if ((smb_maybe_mangled_name(src_fqi->last_comp)) && 326 (strcmp(src_fqi->last_comp, dst_fqi->last_comp) == 0)) { 327 dstname = src_fqi->last_comp_od; 328 } else { 329 dstname = dst_fqi->last_comp; 330 } 331 332 rc = smb_fsop_rename(sr, sr->user_cr, 333 src_fqi->dir_snode, 334 src_fqi->last_comp_od, 335 dst_fqi->dir_snode, 336 dstname); 337 338 if (rc != 0) { 339 smb_node_release(src_fqi->dir_snode); 340 smb_node_release(dst_fqi->dir_snode); 341 342 SMB_NULL_FQI_NODES(*src_fqi); 343 SMB_NULL_FQI_NODES(*dst_fqi); 344 } 345 346 return (rc); 347 } 348