2004-09-22 13:01:55 +00:00
/*
Unix SMB / CIFS implementation .
POSIX NTVFS backend - rename
Copyright ( C ) Andrew Tridgell 2004
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
2007-07-10 02:07:03 +00:00
the Free Software Foundation ; either version 3 of the License , or
2004-09-22 13:01:55 +00:00
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
2007-07-10 02:07:03 +00:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2004-09-22 13:01:55 +00:00
*/
2004-11-05 07:24:25 +00:00
# include "includes.h"
2004-09-22 13:01:55 +00:00
# include "vfs_posix.h"
2006-03-16 00:23:11 +00:00
# include "librpc/gen_ndr/security.h"
2007-12-13 22:46:09 +01:00
# include "param/param.h"
2004-11-13 05:47:27 +00:00
2006-04-05 08:52:03 +00:00
/*
do a file rename , and send any notify triggers
*/
2006-04-06 01:56:04 +00:00
NTSTATUS pvfs_do_rename ( struct pvfs_state * pvfs , const struct pvfs_filename * name1 ,
const char * name2 )
2006-04-05 08:52:03 +00:00
{
const char * r1 , * r2 ;
2006-04-06 01:56:04 +00:00
uint32_t mask ;
2006-04-05 08:52:03 +00:00
2006-04-06 01:56:04 +00:00
if ( rename ( name1 - > full_name , name2 ) = = - 1 ) {
2006-04-05 08:52:03 +00:00
return pvfs_map_errno ( pvfs , errno ) ;
}
2006-04-06 01:56:04 +00:00
if ( name1 - > dos . attrib & FILE_ATTRIBUTE_DIRECTORY ) {
mask = FILE_NOTIFY_CHANGE_DIR_NAME ;
} else {
2006-04-06 10:05:19 +00:00
mask = FILE_NOTIFY_CHANGE_FILE_NAME ;
2006-04-06 01:56:04 +00:00
}
2006-04-05 08:52:03 +00:00
/*
renames to the same directory cause a OLD_NAME - > NEW_NAME notify .
renames to a different directory are considered a remove / add
*/
2006-04-06 01:56:04 +00:00
r1 = strrchr_m ( name1 - > full_name , ' / ' ) ;
2006-04-05 08:52:03 +00:00
r2 = strrchr_m ( name2 , ' / ' ) ;
2006-04-06 01:56:04 +00:00
if ( ( r1 - name1 - > full_name ) ! = ( r2 - name2 ) | |
strncmp ( name1 - > full_name , name2 , r1 - name1 - > full_name ) ! = 0 ) {
2006-04-05 08:52:03 +00:00
notify_trigger ( pvfs - > notify_context ,
NOTIFY_ACTION_REMOVED ,
2006-04-06 01:56:04 +00:00
mask ,
name1 - > full_name ) ;
2006-04-05 08:52:03 +00:00
notify_trigger ( pvfs - > notify_context ,
NOTIFY_ACTION_ADDED ,
2006-04-06 01:56:04 +00:00
mask ,
2006-04-05 08:52:03 +00:00
name2 ) ;
} else {
notify_trigger ( pvfs - > notify_context ,
NOTIFY_ACTION_OLD_NAME ,
2006-04-06 01:56:04 +00:00
mask ,
name1 - > full_name ) ;
2006-04-05 08:52:03 +00:00
notify_trigger ( pvfs - > notify_context ,
NOTIFY_ACTION_NEW_NAME ,
2006-04-06 01:56:04 +00:00
mask ,
2006-04-05 08:52:03 +00:00
name2 ) ;
}
2006-04-06 10:05:19 +00:00
/* this is a strange one. w2k3 gives an additional event for CHANGE_ATTRIBUTES
2007-01-28 18:25:46 +00:00
and CHANGE_CREATION on the new file when renaming files , but not
2006-04-06 10:05:19 +00:00
directories */
if ( ( name1 - > dos . attrib & FILE_ATTRIBUTE_DIRECTORY ) = = 0 ) {
notify_trigger ( pvfs - > notify_context ,
NOTIFY_ACTION_MODIFIED ,
FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_CREATION ,
name2 ) ;
}
2006-04-05 08:52:03 +00:00
return NT_STATUS_OK ;
}
2004-11-13 05:47:27 +00:00
/*
resolve a wildcard rename pattern . This works on one component of the name
*/
static const char * pvfs_resolve_wildcard_component ( TALLOC_CTX * mem_ctx ,
const char * fname ,
const char * pattern )
{
const char * p1 , * p2 ;
char * dest , * d ;
/* the length is bounded by the length of the two strings combined */
2005-01-06 03:06:58 +00:00
dest = talloc_size ( mem_ctx , strlen ( fname ) + strlen ( pattern ) + 1 ) ;
2004-11-13 05:47:27 +00:00
if ( dest = = NULL ) {
return NULL ;
}
p1 = fname ;
p2 = pattern ;
d = dest ;
while ( * p2 ) {
codepoint_t c1 , c2 ;
size_t c_size1 , c_size2 ;
2007-12-13 22:46:09 +01:00
c1 = next_codepoint ( lp_iconv_convenience ( global_loadparm ) , p1 , & c_size1 ) ;
c2 = next_codepoint ( lp_iconv_convenience ( global_loadparm ) , p2 , & c_size2 ) ;
2004-11-13 05:47:27 +00:00
if ( c2 = = ' ? ' ) {
2007-12-13 22:46:09 +01:00
d + = push_codepoint ( lp_iconv_convenience ( global_loadparm ) , d , c1 ) ;
2004-11-13 05:47:27 +00:00
} else if ( c2 = = ' * ' ) {
memcpy ( d , p1 , strlen ( p1 ) ) ;
d + = strlen ( p1 ) ;
break ;
} else {
2007-12-13 22:46:09 +01:00
d + = push_codepoint ( lp_iconv_convenience ( global_loadparm ) , d , c2 ) ;
2004-11-13 05:47:27 +00:00
}
p1 + = c_size1 ;
p2 + = c_size2 ;
}
* d = 0 ;
return dest ;
}
/*
resolve a wildcard rename pattern .
*/
static const char * pvfs_resolve_wildcard ( TALLOC_CTX * mem_ctx ,
const char * fname ,
const char * pattern )
{
const char * base1 , * base2 ;
const char * ext1 , * ext2 ;
char * p ;
/* break into base part plus extension */
p = strrchr_m ( fname , ' . ' ) ;
if ( p = = NULL ) {
ext1 = " " ;
base1 = fname ;
} else {
ext1 = talloc_strdup ( mem_ctx , p + 1 ) ;
base1 = talloc_strndup ( mem_ctx , fname , p - fname ) ;
}
if ( ext1 = = NULL | | base1 = = NULL ) {
return NULL ;
}
p = strrchr_m ( pattern , ' . ' ) ;
if ( p = = NULL ) {
ext2 = " " ;
base2 = fname ;
} else {
ext2 = talloc_strdup ( mem_ctx , p + 1 ) ;
base2 = talloc_strndup ( mem_ctx , pattern , p - pattern ) ;
}
if ( ext2 = = NULL | | base2 = = NULL ) {
return NULL ;
}
base1 = pvfs_resolve_wildcard_component ( mem_ctx , base1 , base2 ) ;
ext1 = pvfs_resolve_wildcard_component ( mem_ctx , ext1 , ext2 ) ;
if ( base1 = = NULL | | ext1 = = NULL ) {
return NULL ;
}
if ( * ext1 = = 0 ) {
return base1 ;
}
return talloc_asprintf ( mem_ctx , " %s.%s " , base1 , ext1 ) ;
}
/*
rename one file from a wildcard set
*/
static NTSTATUS pvfs_rename_one ( struct pvfs_state * pvfs ,
2006-03-10 14:31:17 +00:00
struct ntvfs_request * req ,
2004-11-13 05:47:27 +00:00
const char * dir_path ,
const char * fname1 ,
const char * fname2 ,
uint16_t attrib )
{
struct pvfs_filename * name1 , * name2 ;
2005-01-06 02:32:43 +00:00
TALLOC_CTX * mem_ctx = talloc_new ( req ) ;
2004-11-13 05:47:27 +00:00
NTSTATUS status ;
2006-02-28 03:47:02 +00:00
struct odb_lock * lck , * lck2 ;
2004-11-13 05:47:27 +00:00
/* resolve the wildcard pattern for this name */
fname2 = pvfs_resolve_wildcard ( mem_ctx , fname1 , fname2 ) ;
if ( fname2 = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
/* get a pvfs_filename source object */
status = pvfs_resolve_partial ( pvfs , mem_ctx ,
dir_path , fname1 , & name1 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2004-12-30 06:37:37 +00:00
goto failed ;
2004-11-13 05:47:27 +00:00
}
/* make sure its matches the given attributes */
status = pvfs_match_attrib ( pvfs , name1 , attrib , 0 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2004-12-30 06:37:37 +00:00
goto failed ;
2004-11-13 05:47:27 +00:00
}
2006-02-28 03:47:02 +00:00
status = pvfs_can_rename ( pvfs , req , name1 , & lck ) ;
2004-11-13 05:47:27 +00:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2004-12-30 06:37:37 +00:00
goto failed ;
2004-11-13 05:47:27 +00:00
}
/* get a pvfs_filename dest object */
status = pvfs_resolve_partial ( pvfs , mem_ctx ,
dir_path , fname2 , & name2 ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
2006-02-28 03:47:02 +00:00
status = pvfs_can_delete ( pvfs , req , name2 , & lck2 ) ;
2004-11-13 05:47:27 +00:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2004-12-30 06:37:37 +00:00
goto failed ;
2004-11-13 05:47:27 +00:00
}
}
2004-12-30 06:37:37 +00:00
status = NT_STATUS_OK ;
2004-12-30 05:50:23 +00:00
2004-11-13 05:47:27 +00:00
fname2 = talloc_asprintf ( mem_ctx , " %s/%s " , dir_path , fname2 ) ;
if ( fname2 = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2006-04-06 01:56:04 +00:00
status = pvfs_do_rename ( pvfs , name1 , fname2 ) ;
2006-02-28 03:47:02 +00:00
2006-03-30 09:24:31 +00:00
if ( NT_STATUS_IS_OK ( status ) ) {
2006-04-05 08:52:03 +00:00
status = odb_rename ( lck , fname2 ) ;
2006-03-30 09:24:31 +00:00
}
2004-12-30 06:37:37 +00:00
failed :
2004-11-13 05:47:27 +00:00
talloc_free ( mem_ctx ) ;
2004-12-30 06:37:37 +00:00
return status ;
2004-11-13 05:47:27 +00:00
}
/*
rename a set of files with wildcards
*/
static NTSTATUS pvfs_rename_wildcard ( struct pvfs_state * pvfs ,
2006-03-10 14:31:17 +00:00
struct ntvfs_request * req ,
2004-11-13 05:47:27 +00:00
union smb_rename * ren ,
struct pvfs_filename * name1 ,
struct pvfs_filename * name2 )
{
struct pvfs_dir * dir ;
NTSTATUS status ;
2006-09-10 07:24:41 +00:00
off_t ofs = 0 ;
2004-11-13 05:47:27 +00:00
const char * fname , * fname2 , * dir_path ;
uint16_t attrib = ren - > rename . in . attrib ;
int total_renamed = 0 ;
/* get list of matching files */
status = pvfs_list_start ( pvfs , name1 , req , & dir ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
status = NT_STATUS_NO_SUCH_FILE ;
dir_path = pvfs_list_unix_path ( dir ) ;
/* only allow wildcard renames within a directory */
if ( strncmp ( dir_path , name2 - > full_name , strlen ( dir_path ) ) ! = 0 | |
name2 - > full_name [ strlen ( dir_path ) ] ! = ' / ' | |
strchr ( name2 - > full_name + strlen ( dir_path ) + 1 , ' / ' ) ) {
return NT_STATUS_INVALID_PARAMETER ;
}
fname2 = talloc_strdup ( name2 , name2 - > full_name + strlen ( dir_path ) + 1 ) ;
if ( fname2 = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
while ( ( fname = pvfs_list_next ( dir , & ofs ) ) ) {
status = pvfs_rename_one ( pvfs , req ,
dir_path ,
fname , fname2 , attrib ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
total_renamed + + ;
}
}
if ( total_renamed = = 0 ) {
return status ;
}
return NT_STATUS_OK ;
}
2004-09-22 13:01:55 +00:00
/*
2004-11-13 06:46:51 +00:00
rename a set of files - SMBmv interface
2004-09-22 13:01:55 +00:00
*/
2004-11-13 06:46:51 +00:00
static NTSTATUS pvfs_rename_mv ( struct ntvfs_module_context * ntvfs ,
2006-03-10 14:31:17 +00:00
struct ntvfs_request * req , union smb_rename * ren )
2004-09-22 13:01:55 +00:00
{
2004-09-29 13:17:09 +00:00
struct pvfs_state * pvfs = ntvfs - > private_data ;
2004-09-22 13:01:55 +00:00
NTSTATUS status ;
struct pvfs_filename * name1 , * name2 ;
2006-02-28 03:47:02 +00:00
struct odb_lock * lck ;
2004-09-22 13:01:55 +00:00
/* resolve the cifs name to a posix name */
2004-11-15 06:57:26 +00:00
status = pvfs_resolve_name ( pvfs , req , ren - > rename . in . pattern1 ,
PVFS_RESOLVE_WILDCARD , & name1 ) ;
2004-09-22 13:01:55 +00:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2004-11-15 06:57:26 +00:00
status = pvfs_resolve_name ( pvfs , req , ren - > rename . in . pattern2 ,
PVFS_RESOLVE_WILDCARD , & name2 ) ;
2004-09-22 13:01:55 +00:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
if ( name1 - > has_wildcard | | name2 - > has_wildcard ) {
2004-11-13 05:47:27 +00:00
return pvfs_rename_wildcard ( pvfs , req , ren , name1 , name2 ) ;
2004-09-22 13:01:55 +00:00
}
if ( ! name1 - > exists ) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
2004-11-13 05:47:27 +00:00
if ( strcmp ( name1 - > full_name , name2 - > full_name ) = = 0 ) {
return NT_STATUS_OK ;
}
2004-09-22 13:01:55 +00:00
if ( name2 - > exists ) {
return NT_STATUS_OBJECT_NAME_COLLISION ;
}
2004-11-13 06:46:51 +00:00
status = pvfs_match_attrib ( pvfs , name1 , ren - > rename . in . attrib , 0 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2005-01-09 08:27:35 +00:00
status = pvfs_access_check_parent ( pvfs , req , name2 , SEC_DIR_ADD_FILE ) ;
2004-12-30 05:50:23 +00:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2006-02-28 03:47:02 +00:00
status = pvfs_can_rename ( pvfs , req , name1 , & lck ) ;
2004-10-25 05:27:15 +00:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2006-04-06 01:56:04 +00:00
status = pvfs_do_rename ( pvfs , name1 , name2 - > full_name ) ;
2006-04-05 08:52:03 +00:00
if ( NT_STATUS_IS_OK ( status ) ) {
status = odb_rename ( lck , name2 - > full_name ) ;
2004-09-22 13:01:55 +00:00
}
return NT_STATUS_OK ;
}
2004-11-13 06:46:51 +00:00
/*
rename a set of files - ntrename interface
*/
static NTSTATUS pvfs_rename_nt ( struct ntvfs_module_context * ntvfs ,
2006-03-10 14:31:17 +00:00
struct ntvfs_request * req , union smb_rename * ren )
2004-11-13 06:46:51 +00:00
{
struct pvfs_state * pvfs = ntvfs - > private_data ;
NTSTATUS status ;
struct pvfs_filename * name1 , * name2 ;
2006-02-28 03:47:02 +00:00
struct odb_lock * lck ;
2004-11-13 06:46:51 +00:00
switch ( ren - > ntrename . in . flags ) {
case RENAME_FLAG_RENAME :
case RENAME_FLAG_HARD_LINK :
case RENAME_FLAG_COPY :
case RENAME_FLAG_MOVE_CLUSTER_INFORMATION :
break ;
default :
return NT_STATUS_ACCESS_DENIED ;
}
/* resolve the cifs name to a posix name */
2004-11-17 05:58:04 +00:00
status = pvfs_resolve_name ( pvfs , req , ren - > ntrename . in . old_name ,
PVFS_RESOLVE_WILDCARD , & name1 ) ;
2004-11-13 06:46:51 +00:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2004-11-17 05:58:04 +00:00
status = pvfs_resolve_name ( pvfs , req , ren - > ntrename . in . new_name ,
PVFS_RESOLVE_WILDCARD , & name2 ) ;
2004-11-13 06:46:51 +00:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2004-11-17 05:58:04 +00:00
if ( name1 - > has_wildcard | | name2 - > has_wildcard ) {
return NT_STATUS_OBJECT_PATH_SYNTAX_BAD ;
}
2004-11-13 06:46:51 +00:00
if ( ! name1 - > exists ) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
if ( strcmp ( name1 - > full_name , name2 - > full_name ) = = 0 ) {
return NT_STATUS_OK ;
}
if ( name2 - > exists ) {
return NT_STATUS_OBJECT_NAME_COLLISION ;
}
status = pvfs_match_attrib ( pvfs , name1 , ren - > ntrename . in . attrib , 0 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2006-02-28 03:47:02 +00:00
status = pvfs_can_rename ( pvfs , req , name1 , & lck ) ;
2004-11-13 06:46:51 +00:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
switch ( ren - > ntrename . in . flags ) {
case RENAME_FLAG_RENAME :
2005-01-09 08:27:35 +00:00
status = pvfs_access_check_parent ( pvfs , req , name2 , SEC_DIR_ADD_FILE ) ;
2006-04-05 08:52:03 +00:00
NT_STATUS_NOT_OK_RETURN ( status ) ;
2006-04-06 01:56:04 +00:00
status = pvfs_do_rename ( pvfs , name1 , name2 - > full_name ) ;
2006-04-05 08:52:03 +00:00
NT_STATUS_NOT_OK_RETURN ( status ) ;
2004-11-13 06:46:51 +00:00
break ;
case RENAME_FLAG_HARD_LINK :
2005-01-09 08:27:35 +00:00
status = pvfs_access_check_parent ( pvfs , req , name2 , SEC_DIR_ADD_FILE ) ;
2006-04-05 08:52:03 +00:00
NT_STATUS_NOT_OK_RETURN ( status ) ;
2004-11-13 06:46:51 +00:00
if ( link ( name1 - > full_name , name2 - > full_name ) = = - 1 ) {
return pvfs_map_errno ( pvfs , errno ) ;
}
break ;
case RENAME_FLAG_COPY :
2005-01-09 08:27:35 +00:00
status = pvfs_access_check_parent ( pvfs , req , name2 , SEC_DIR_ADD_FILE ) ;
2006-04-05 08:52:03 +00:00
NT_STATUS_NOT_OK_RETURN ( status ) ;
2004-11-13 06:46:51 +00:00
return pvfs_copy_file ( pvfs , name1 , name2 ) ;
case RENAME_FLAG_MOVE_CLUSTER_INFORMATION :
return NT_STATUS_INVALID_PARAMETER ;
default :
return NT_STATUS_ACCESS_DENIED ;
}
return NT_STATUS_OK ;
}
/*
rename a set of files - ntrename interface
*/
NTSTATUS pvfs_rename ( struct ntvfs_module_context * ntvfs ,
2006-03-10 14:31:17 +00:00
struct ntvfs_request * req , union smb_rename * ren )
2004-11-13 06:46:51 +00:00
{
switch ( ren - > generic . level ) {
case RAW_RENAME_RENAME :
return pvfs_rename_mv ( ntvfs , req , ren ) ;
case RAW_RENAME_NTRENAME :
return pvfs_rename_nt ( ntvfs , req , ren ) ;
default :
break ;
}
return NT_STATUS_INVALID_LEVEL ;
}