15331Samw /* 25331Samw * CDDL HEADER START 35331Samw * 45331Samw * The contents of this file are subject to the terms of the 55331Samw * Common Development and Distribution License (the "License"). 65331Samw * You may not use this file except in compliance with the License. 75331Samw * 85331Samw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 95331Samw * or http://www.opensolaris.org/os/licensing. 105331Samw * See the License for the specific language governing permissions 115331Samw * and limitations under the License. 125331Samw * 135331Samw * When distributing Covered Code, include this CDDL HEADER in each 145331Samw * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 155331Samw * If applicable, add the following below this CDDL HEADER, with the 165331Samw * fields enclosed by brackets "[]" replaced with your own identifying 175331Samw * information: Portions Copyright [yyyy] [name of copyright owner] 185331Samw * 195331Samw * CDDL HEADER END 205331Samw */ 215331Samw /* 22*5772Sas200622 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 235331Samw * Use is subject to license terms. 245331Samw */ 255331Samw 265331Samw #pragma ident "%Z%%M% %I% %E% SMI" 275331Samw 285331Samw #include <smbsrv/nterror.h> 295331Samw #include <sys/synch.h> 305331Samw #include <smbsrv/smb_incl.h> 315331Samw #include <smbsrv/smb_fsops.h> 32*5772Sas200622 #include <sys/nbmlock.h> 335331Samw 345331Samw static int smb_do_rename(struct smb_request *sr, 355331Samw struct smb_fqi *src_fqi, 365331Samw struct smb_fqi *dst_fqi); 375331Samw 385331Samw /* 395331Samw * smb_com_rename 405331Samw * 415331Samw * Rename a file. Files OldFileName must exist and NewFileName must not. 425331Samw * Both pathnames must be relative to the Tid specified in the request. 435331Samw * Open files may be renamed. 445331Samw * 455331Samw * Multiple files may be renamed in response to a single request as Rename 465331Samw * File supports wildcards in the file name (last component of the path). 475331Samw * NOTE: we don't support rename with wildcards. 485331Samw * 495331Samw * SearchAttributes indicates the attributes that the target file(s) must 505331Samw * have. If SearchAttributes is zero then only normal files are renamed. 515331Samw * If the system file or hidden attributes are specified then the rename 525331Samw * is inclusive - both the specified type(s) of files and normal files are 535331Samw * renamed. The encoding of SearchAttributes is described in section 3.10 545331Samw * - File Attribute Encoding. 555331Samw */ 565331Samw int 575331Samw smb_com_rename(struct smb_request *sr) 585331Samw { 595331Samw static kmutex_t mutex; 605331Samw struct smb_fqi *src_fqi; 615331Samw struct smb_fqi *dst_fqi; 625331Samw struct smb_node *dst_node; 635331Samw int rc; 645331Samw 655331Samw if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { 66*5772Sas200622 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 675331Samw ERRDOS, ERROR_ACCESS_DENIED); 685331Samw /* NOTREACHED */ 695331Samw } 705331Samw 715331Samw src_fqi = &sr->arg.dirop.fqi; 725331Samw dst_fqi = &sr->arg.dirop.dst_fqi; 735331Samw 745331Samw if (smbsr_decode_vwv(sr, "w", &src_fqi->srch_attr) != 0) { 755331Samw smbsr_decode_error(sr); 765331Samw /* NOTREACHED */ 775331Samw } 785331Samw 795331Samw rc = smbsr_decode_data(sr, "%SS", sr, &src_fqi->path, &dst_fqi->path); 805331Samw if (rc != 0) { 815331Samw smbsr_decode_error(sr); 825331Samw /* NOTREACHED */ 835331Samw } 845331Samw 855331Samw dst_fqi->srch_attr = 0; 865331Samw 875331Samw mutex_enter(&mutex); 885331Samw rc = smb_do_rename(sr, src_fqi, dst_fqi); 895331Samw mutex_exit(&mutex); 905331Samw 915331Samw if (rc != 0) { 925331Samw /* 935331Samw * ERROR_FILE_EXISTS doesn't work for Windows98 clients. 945331Samw * 955331Samw * Windows95 clients don't see this problem because the target 965331Samw * is deleted before the rename request. 975331Samw * 985331Samw * The following values are based on observed WFWG, Win9x, 995331Samw * NT and W2K client behaviour. 1005331Samw */ 1015331Samw if (rc == EEXIST) { 102*5772Sas200622 smbsr_error(sr, NT_STATUS_OBJECT_NAME_COLLISION, 1035331Samw ERRDOS, ERROR_ALREADY_EXISTS); 1045331Samw /* NOTREACHED */ 1055331Samw } 1065331Samw 1075331Samw if (rc == EPIPE) { 108*5772Sas200622 smbsr_error(sr, NT_STATUS_SHARING_VIOLATION, 1095331Samw ERRDOS, ERROR_SHARING_VIOLATION); 1105331Samw /* NOTREACHED */ 1115331Samw } 1125331Samw 113*5772Sas200622 smbsr_errno(sr, rc); 1145331Samw /* NOTREACHED */ 1155331Samw } 1165331Samw 1175331Samw if (src_fqi->dir_snode) 1185331Samw smb_node_release(src_fqi->dir_snode); 1195331Samw 1205331Samw dst_node = dst_fqi->dir_snode; 1215331Samw if (dst_node) { 1225331Samw if (dst_node->flags & NODE_FLAGS_NOTIFY_CHANGE) { 1235331Samw dst_node->flags |= NODE_FLAGS_CHANGED; 1245331Samw smb_process_node_notify_change_queue(dst_node); 1255331Samw } 1265331Samw smb_node_release(dst_node); 1275331Samw } 1285331Samw 1295331Samw SMB_NULL_FQI_NODES(*src_fqi); 1305331Samw SMB_NULL_FQI_NODES(*dst_fqi); 1315331Samw 1325331Samw smbsr_encode_empty_result(sr); 1335331Samw 1345331Samw return (SDRC_NORMAL_REPLY); 1355331Samw } 1365331Samw 1375331Samw /* 1385331Samw * smb_do_rename 1395331Samw * 1405331Samw * Backend to smb_com_rename to ensure that the rename operation is atomic. 1415331Samw * This function should be called within a mutual exclusion region. If the 1425331Samw * source and destination are identical, we don't actually do a rename, we 1435331Samw * just check that the conditions are right. If the source and destination 1445331Samw * files differ only in case, we a case-sensitive rename. Otherwise, we do 1455331Samw * a full case-insensitive rename. 1465331Samw * 1475331Samw * This function should always return errno values. 1485331Samw * 1495331Samw * Upon success, the last_snode's and dir_snode's of both src_fqi and dst_fqi 1505331Samw * are not released in this routine but in smb_com_rename(). 1515331Samw */ 1525331Samw static int 1535331Samw smb_do_rename( 1545331Samw struct smb_request *sr, 1555331Samw struct smb_fqi *src_fqi, 1565331Samw struct smb_fqi *dst_fqi) 1575331Samw { 1585331Samw struct smb_node *src_node; 1595331Samw char *dstname; 1605331Samw DWORD status; 1615331Samw int rc; 1625331Samw int count; 1635331Samw 1645331Samw if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0) { 1655331Samw return (rc); 1665331Samw } 1675331Samw 1685331Samw src_node = src_fqi->last_snode; 1695331Samw 1705331Samw /* 1715331Samw * Break the oplock before access checks. If a client 1725331Samw * has a file open, this will force a flush or close, 1735331Samw * which may affect the outcome of any share checking. 1745331Samw */ 1755331Samw if (OPLOCKS_IN_FORCE(src_node)) { 1765331Samw status = smb_break_oplock(sr, src_node); 1775331Samw 1785331Samw if (status != NT_STATUS_SUCCESS) { 1795331Samw smb_node_release(src_node); 1805331Samw smb_node_release(src_fqi->dir_snode); 1815331Samw 1825331Samw SMB_NULL_FQI_NODES(*src_fqi); 1835331Samw SMB_NULL_FQI_NODES(*dst_fqi); 1845331Samw return (EACCES); 1855331Samw } 1865331Samw } 1875331Samw 188*5772Sas200622 for (count = 0; count <= 3; count++) { 189*5772Sas200622 if (count) { 190*5772Sas200622 smb_node_end_crit(src_node); 191*5772Sas200622 delay(MSEC_TO_TICK(400)); 192*5772Sas200622 } 193*5772Sas200622 194*5772Sas200622 smb_node_start_crit(src_node, RW_READER); 195*5772Sas200622 196*5772Sas200622 status = smb_node_rename_check(src_node); 197*5772Sas200622 198*5772Sas200622 if (status != NT_STATUS_SHARING_VIOLATION) 199*5772Sas200622 break; 200*5772Sas200622 } 201*5772Sas200622 202*5772Sas200622 if (status == NT_STATUS_SHARING_VIOLATION) { 203*5772Sas200622 smb_node_end_crit(src_node); 204*5772Sas200622 205*5772Sas200622 smb_node_release(src_node); 206*5772Sas200622 smb_node_release(src_fqi->dir_snode); 207*5772Sas200622 208*5772Sas200622 SMB_NULL_FQI_NODES(*src_fqi); 209*5772Sas200622 SMB_NULL_FQI_NODES(*dst_fqi); 210*5772Sas200622 return (EPIPE); /* = ERRbadshare */ 211*5772Sas200622 } 212*5772Sas200622 213*5772Sas200622 status = smb_range_check(sr, sr->user_cr, src_node, 0, UINT64_MAX, 214*5772Sas200622 B_TRUE); 215*5772Sas200622 2165331Samw if (status != NT_STATUS_SUCCESS) { 217*5772Sas200622 smb_node_end_crit(src_node); 218*5772Sas200622 2195331Samw smb_node_release(src_node); 2205331Samw smb_node_release(src_fqi->dir_snode); 2215331Samw 2225331Samw SMB_NULL_FQI_NODES(*src_fqi); 2235331Samw SMB_NULL_FQI_NODES(*dst_fqi); 2245331Samw return (EACCES); 2255331Samw } 2265331Samw 2275331Samw if (utf8_strcasecmp(src_fqi->path, dst_fqi->path) == 0) { 2285331Samw if ((rc = smbd_fs_query(sr, dst_fqi, 0)) != 0) { 229*5772Sas200622 smb_node_end_crit(src_node); 230*5772Sas200622 231*5772Sas200622 smb_node_release(src_node); 2325331Samw smb_node_release(src_fqi->dir_snode); 2335331Samw 2345331Samw SMB_NULL_FQI_NODES(*src_fqi); 2355331Samw SMB_NULL_FQI_NODES(*dst_fqi); 2365331Samw return (rc); 2375331Samw } 2385331Samw 2395331Samw /* 2405331Samw * Because the fqm parameter to smbd_fs_query() was 0, 2415331Samw * a successful return value means that dst_fqi->last_snode 2425331Samw * may be NULL. 2435331Samw */ 2445331Samw if (dst_fqi->last_snode) 2455331Samw smb_node_release(dst_fqi->last_snode); 2465331Samw 2475331Samw rc = strcmp(src_fqi->last_comp_od, dst_fqi->last_comp); 2485331Samw if (rc == 0) { 249*5772Sas200622 smb_node_end_crit(src_node); 250*5772Sas200622 251*5772Sas200622 smb_node_release(src_node); 2525331Samw smb_node_release(src_fqi->dir_snode); 2535331Samw smb_node_release(dst_fqi->dir_snode); 2545331Samw 2555331Samw SMB_NULL_FQI_NODES(*src_fqi); 2565331Samw SMB_NULL_FQI_NODES(*dst_fqi); 2575331Samw return (0); 2585331Samw } 2595331Samw 2605331Samw rc = smb_fsop_rename(sr, sr->user_cr, 2615331Samw src_fqi->dir_snode, 2625331Samw src_fqi->last_comp_od, 2635331Samw dst_fqi->dir_snode, 2645331Samw dst_fqi->last_comp); 2655331Samw 2665331Samw if (rc != 0) { 2675331Samw smb_node_release(src_fqi->dir_snode); 2685331Samw smb_node_release(dst_fqi->dir_snode); 2695331Samw 2705331Samw SMB_NULL_FQI_NODES(*src_fqi); 2715331Samw SMB_NULL_FQI_NODES(*dst_fqi); 2725331Samw } 273*5772Sas200622 274*5772Sas200622 smb_node_end_crit(src_node); 275*5772Sas200622 276*5772Sas200622 smb_node_release(src_node); 2775331Samw return (rc); 2785331Samw } 2795331Samw 2805331Samw rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST); 2815331Samw if (rc != 0) { 282*5772Sas200622 smb_node_end_crit(src_node); 283*5772Sas200622 284*5772Sas200622 smb_node_release(src_node); 2855331Samw smb_node_release(src_fqi->dir_snode); 2865331Samw 2875331Samw SMB_NULL_FQI_NODES(*src_fqi); 2885331Samw SMB_NULL_FQI_NODES(*dst_fqi); 2895331Samw return (rc); 2905331Samw } 2915331Samw 2925331Samw /* 2935331Samw * Because of FQM_PATH_MUST_NOT_EXIST and the successful return 2945331Samw * value, only dst_fqi->dir_snode is valid (dst_fqi->last_snode 2955331Samw * is NULL). 2965331Samw */ 2975331Samw 2985331Samw /* 2995331Samw * Use the unmangled form of the destination name if the 3005331Samw * source and destination names are the same and the source 3015331Samw * name is mangled. (We are taking a chance here, assuming 3025331Samw * that this is what the user wants.) 3035331Samw */ 3045331Samw 3055331Samw if ((smb_maybe_mangled_name(src_fqi->last_comp)) && 3065331Samw (strcmp(src_fqi->last_comp, dst_fqi->last_comp) == 0)) { 3075331Samw dstname = src_fqi->last_comp_od; 3085331Samw } else { 3095331Samw dstname = dst_fqi->last_comp; 3105331Samw } 3115331Samw 3125331Samw rc = smb_fsop_rename(sr, sr->user_cr, 3135331Samw src_fqi->dir_snode, 3145331Samw src_fqi->last_comp_od, 3155331Samw dst_fqi->dir_snode, 3165331Samw dstname); 3175331Samw 3185331Samw if (rc != 0) { 3195331Samw smb_node_release(src_fqi->dir_snode); 3205331Samw smb_node_release(dst_fqi->dir_snode); 3215331Samw 3225331Samw SMB_NULL_FQI_NODES(*src_fqi); 3235331Samw SMB_NULL_FQI_NODES(*dst_fqi); 3245331Samw } 3255331Samw 326*5772Sas200622 smb_node_end_crit(src_node); 327*5772Sas200622 328*5772Sas200622 smb_node_release(src_node); 329*5772Sas200622 3305331Samw return (rc); 3315331Samw } 332