2004-09-20 11:28:43 +04:00
/*
Unix SMB / CIFS implementation .
POSIX NTVFS backend - setfileinfo
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 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2004-09-20 11:28:43 +04: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 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2004-09-20 11:28:43 +04:00
*/
2004-11-05 10:29:02 +03:00
# include "includes.h"
2004-09-20 11:28:43 +04:00
# include "vfs_posix.h"
2004-11-02 03:24:21 +03:00
# include "system/time.h"
2006-03-16 03:23:11 +03:00
# include "librpc/gen_ndr/xattr.h"
2004-11-05 14:31:35 +03:00
2004-12-30 08:50:23 +03:00
/*
determine what access bits are needed for a call
*/
2004-12-31 11:56:32 +03:00
static uint32_t pvfs_setfileinfo_access ( union smb_setfileinfo * info )
2004-12-30 08:50:23 +03:00
{
uint32_t needed ;
2004-12-31 11:56:32 +03:00
switch ( info - > generic . level ) {
2004-12-30 08:50:23 +03:00
case RAW_SFILEINFO_EA_SET :
needed = SEC_FILE_WRITE_EA ;
break ;
case RAW_SFILEINFO_DISPOSITION_INFO :
case RAW_SFILEINFO_DISPOSITION_INFORMATION :
needed = SEC_STD_DELETE ;
break ;
case RAW_SFILEINFO_END_OF_FILE_INFO :
needed = SEC_FILE_WRITE_DATA ;
break ;
case RAW_SFILEINFO_POSITION_INFORMATION :
needed = 0 ;
break ;
2004-12-31 11:56:32 +03:00
case RAW_SFILEINFO_SEC_DESC :
needed = 0 ;
2006-04-12 20:27:53 +04:00
if ( info - > set_secdesc . in . secinfo_flags & ( SECINFO_OWNER | SECINFO_GROUP ) ) {
needed | = SEC_STD_WRITE_OWNER ;
}
if ( info - > set_secdesc . in . secinfo_flags & SECINFO_DACL ) {
2004-12-31 11:56:32 +03:00
needed | = SEC_STD_WRITE_DAC ;
}
2006-04-12 20:27:53 +04:00
if ( info - > set_secdesc . in . secinfo_flags & SECINFO_SACL ) {
needed | = SEC_FLAG_SYSTEM_SECURITY ;
}
2004-12-31 11:56:32 +03:00
break ;
2008-02-14 04:30:31 +03:00
case RAW_SFILEINFO_RENAME_INFORMATION :
2008-02-14 06:54:21 +03:00
case RAW_SFILEINFO_RENAME_INFORMATION_SMB2 :
2008-02-14 04:30:31 +03:00
needed = SEC_STD_DELETE ;
break ;
2004-12-30 08:50:23 +03:00
default :
needed = SEC_FILE_WRITE_ATTRIBUTE ;
break ;
}
2006-04-12 20:27:53 +04:00
return needed ;
2004-12-30 08:50:23 +03:00
}
2009-01-07 08:46:34 +03:00
/*
rename_information level for streams
*/
static NTSTATUS pvfs_setfileinfo_rename_stream ( struct pvfs_state * pvfs ,
struct ntvfs_request * req ,
struct pvfs_filename * name ,
int fd ,
DATA_BLOB * odb_locking_key ,
union smb_setfileinfo * info )
{
NTSTATUS status ;
struct odb_lock * lck = NULL ;
2009-10-18 07:15:48 +04:00
/* strangely, this gives a sharing violation, not invalid
parameter */
2009-01-07 08:46:34 +03:00
if ( info - > rename_information . in . new_name [ 0 ] ! = ' : ' ) {
2009-10-18 07:15:48 +04:00
return NT_STATUS_SHARING_VIOLATION ;
2009-01-07 08:46:34 +03:00
}
status = pvfs_access_check_simple ( pvfs , req , name , SEC_FILE_WRITE_ATTRIBUTE ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
lck = odb_lock ( req , pvfs - > odb_context , odb_locking_key ) ;
if ( lck = = NULL ) {
DEBUG ( 0 , ( " Unable to lock opendb for can_stat \n " ) ) ;
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
status = pvfs_stream_rename ( pvfs , name , fd ,
2009-10-18 07:19:27 +04:00
info - > rename_information . in . new_name + 1 ,
info - > rename_information . in . overwrite ) ;
2009-01-07 08:46:34 +03:00
return status ;
}
2004-11-08 06:54:12 +03:00
/*
rename_information level
*/
static NTSTATUS pvfs_setfileinfo_rename ( struct pvfs_state * pvfs ,
2006-03-10 17:31:17 +03:00
struct ntvfs_request * req ,
2004-11-08 06:54:12 +03:00
struct pvfs_filename * name ,
2009-01-07 08:46:34 +03:00
int fd ,
2008-02-28 14:19:18 +03:00
DATA_BLOB * odb_locking_key ,
2006-03-13 01:48:25 +03:00
union smb_setfileinfo * info )
2004-11-08 06:54:12 +03:00
{
NTSTATUS status ;
struct pvfs_filename * name2 ;
2004-11-12 05:45:52 +03:00
char * new_name , * p ;
2008-02-28 14:19:18 +03:00
struct odb_lock * lck = NULL ;
2004-11-08 06:54:12 +03:00
/* renames are only allowed within a directory */
2008-02-14 04:30:31 +03:00
if ( strchr_m ( info - > rename_information . in . new_name , ' \\ ' ) & &
( req - > ctx - > protocol ! = PROTOCOL_SMB2 ) ) {
2004-11-08 06:54:12 +03:00
return NT_STATUS_NOT_SUPPORTED ;
}
2009-01-07 08:46:34 +03:00
/* handle stream renames specially */
2004-11-17 09:44:50 +03:00
if ( name - > stream_name ) {
2009-01-07 08:46:34 +03:00
return pvfs_setfileinfo_rename_stream ( pvfs , req , name , fd ,
odb_locking_key , info ) ;
2004-11-17 09:44:50 +03:00
}
2008-02-14 04:50:11 +03:00
/* w2k3 does not appear to allow relative rename. On SMB2, vista sends it sometimes,
but I suspect it is just uninitialised memory */
if ( info - > rename_information . in . root_fid ! = 0 & &
( req - > ctx - > protocol ! = PROTOCOL_SMB2 ) ) {
return NT_STATUS_INVALID_PARAMETER ;
2004-11-12 05:45:52 +03:00
}
/* construct the fully qualified windows name for the new file name */
2008-02-14 04:30:31 +03:00
if ( req - > ctx - > protocol = = PROTOCOL_SMB2 ) {
/* SMB2 sends the full path of the new name */
new_name = talloc_asprintf ( req , " \\ %s " , info - > rename_information . in . new_name ) ;
} else {
new_name = talloc_strdup ( req , name - > original_name ) ;
if ( new_name = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
p = strrchr_m ( new_name , ' \\ ' ) ;
if ( p = = NULL ) {
return NT_STATUS_OBJECT_NAME_INVALID ;
} else {
* p = 0 ;
}
2004-11-08 06:54:12 +03:00
2008-02-14 04:30:31 +03:00
new_name = talloc_asprintf ( req , " %s \\ %s " , new_name ,
info - > rename_information . in . new_name ) ;
}
2004-11-12 05:45:52 +03:00
if ( new_name = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2004-11-08 06:54:12 +03:00
/* resolve the new name */
2009-10-17 14:17:29 +04:00
status = pvfs_resolve_name ( pvfs , req , new_name , 0 , & name2 ) ;
2004-11-08 06:54:12 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2004-11-12 05:45:52 +03:00
/* if the destination exists, then check the rename is allowed */
if ( name2 - > exists ) {
if ( strcmp ( name2 - > full_name , name - > full_name ) = = 0 ) {
/* rename to same name is null-op */
return NT_STATUS_OK ;
}
2006-03-13 01:48:25 +03:00
if ( ! info - > rename_information . in . overwrite ) {
2004-11-12 05:45:52 +03:00
return NT_STATUS_OBJECT_NAME_COLLISION ;
}
2008-02-21 21:59:13 +03:00
status = pvfs_can_delete ( pvfs , req , name2 , NULL ) ;
2008-02-26 11:14:54 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_DELETE_PENDING ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
2004-11-12 05:45:52 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_SHARING_VIOLATION ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
}
2005-01-09 11:27:35 +03:00
status = pvfs_access_check_parent ( pvfs , req , name2 , SEC_DIR_ADD_FILE ) ;
2004-12-30 08:50:23 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2008-02-28 14:19:18 +03:00
lck = odb_lock ( req , pvfs - > odb_context , odb_locking_key ) ;
if ( lck = = NULL ) {
DEBUG ( 0 , ( " Unable to lock opendb for can_stat \n " ) ) ;
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
2008-03-01 12:05:25 +03:00
status = pvfs_do_rename ( pvfs , lck , name , name2 - > full_name ) ;
2008-02-28 14:19:18 +03:00
talloc_free ( lck ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
2006-04-05 12:52:03 +04:00
if ( NT_STATUS_IS_OK ( status ) ) {
name - > full_name = talloc_steal ( name , name2 - > full_name ) ;
name - > original_name = talloc_steal ( name , name2 - > original_name ) ;
2004-11-08 06:54:12 +03:00
}
2004-11-12 05:45:52 +03:00
return NT_STATUS_OK ;
2004-11-08 06:54:12 +03:00
}
2004-11-05 14:31:35 +03:00
/*
add a single DOS EA
*/
2004-11-17 15:36:14 +03:00
NTSTATUS pvfs_setfileinfo_ea_set ( struct pvfs_state * pvfs ,
struct pvfs_filename * name ,
2004-12-17 07:51:23 +03:00
int fd , uint16_t num_eas ,
struct ea_struct * eas )
2004-11-05 14:31:35 +03:00
{
2004-12-17 07:51:23 +03:00
struct xattr_DosEAs * ealist ;
int i , j ;
2004-11-05 14:31:35 +03:00
NTSTATUS status ;
2004-12-17 07:51:23 +03:00
if ( num_eas = = 0 ) {
return NT_STATUS_OK ;
}
2004-11-05 14:31:35 +03:00
if ( ! ( pvfs - > flags & PVFS_FLAG_XATTR_ENABLE ) ) {
return NT_STATUS_NOT_SUPPORTED ;
}
2005-01-27 10:08:20 +03:00
ealist = talloc ( name , struct xattr_DosEAs ) ;
2004-12-17 07:51:23 +03:00
2004-11-05 14:31:35 +03:00
/* load the current list */
status = pvfs_doseas_load ( pvfs , name , fd , ealist ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2004-12-17 07:51:23 +03:00
for ( j = 0 ; j < num_eas ; j + + ) {
struct ea_struct * ea = & eas [ j ] ;
/* see if its already there */
for ( i = 0 ; i < ealist - > num_eas ; i + + ) {
2005-08-30 15:55:05 +04:00
if ( strcasecmp_m ( ealist - > eas [ i ] . name , ea - > name . s ) = = 0 ) {
2004-12-17 07:51:23 +03:00
ealist - > eas [ i ] . value = ea - > value ;
break ;
}
2004-11-05 14:31:35 +03:00
}
2004-12-17 07:51:23 +03:00
if ( i = = ealist - > num_eas ) {
/* add it */
2005-01-27 10:08:20 +03:00
ealist - > eas = talloc_realloc ( ealist , ealist - > eas ,
2004-12-17 07:51:23 +03:00
struct xattr_EA ,
ealist - > num_eas + 1 ) ;
if ( ealist - > eas = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
ealist - > eas [ i ] . name = ea - > name . s ;
ealist - > eas [ i ] . value = ea - > value ;
ealist - > num_eas + + ;
}
2004-11-05 14:31:35 +03:00
}
2004-11-15 09:57:26 +03:00
/* pull out any null EAs */
for ( i = 0 ; i < ealist - > num_eas ; i + + ) {
if ( ealist - > eas [ i ] . value . length = = 0 ) {
memmove ( & ealist - > eas [ i ] ,
& ealist - > eas [ i + 1 ] ,
( ealist - > num_eas - ( i + 1 ) ) * sizeof ( ealist - > eas [ i ] ) ) ;
ealist - > num_eas - - ;
i - - ;
}
}
2004-11-05 14:31:35 +03:00
status = pvfs_doseas_save ( pvfs , name , fd , ealist ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2006-03-30 13:24:31 +04:00
notify_trigger ( pvfs - > notify_context ,
NOTIFY_ACTION_MODIFIED ,
FILE_NOTIFY_CHANGE_EA ,
name - > full_name ) ;
2004-11-05 14:31:35 +03:00
name - > dos . ea_size = 4 ;
for ( i = 0 ; i < ealist - > num_eas ; i + + ) {
name - > dos . ea_size + = 4 + strlen ( ealist - > eas [ i ] . name ) + 1 +
ealist - > eas [ i ] . value . length ;
}
/* update the ea_size attrib */
return pvfs_dosattrib_save ( pvfs , name , fd ) ;
}
2004-09-20 11:28:43 +04:00
/*
set info on a open file
*/
2004-09-29 17:17:09 +04:00
NTSTATUS pvfs_setfileinfo ( struct ntvfs_module_context * ntvfs ,
2006-03-10 17:31:17 +03:00
struct ntvfs_request * req ,
2004-09-20 11:28:43 +04:00
union smb_setfileinfo * info )
{
2009-02-04 10:52:41 +03:00
struct pvfs_state * pvfs = talloc_get_type ( ntvfs - > private_data ,
struct pvfs_state ) ;
2004-09-20 11:28:43 +04:00
struct pvfs_file * f ;
2004-11-08 06:54:12 +03:00
struct pvfs_file_handle * h ;
2004-10-26 09:39:54 +04:00
struct pvfs_filename newstats ;
NTSTATUS status ;
2004-12-30 08:50:23 +03:00
uint32_t access_needed ;
2006-03-30 13:24:31 +04:00
uint32_t change_mask = 0 ;
2004-09-20 11:28:43 +04:00
2006-05-20 12:15:22 +04:00
f = pvfs_find_fd ( pvfs , req , info - > generic . in . file . ntvfs ) ;
2004-09-20 11:28:43 +04:00
if ( ! f ) {
return NT_STATUS_INVALID_HANDLE ;
}
2004-11-08 06:54:12 +03:00
h = f - > handle ;
2004-12-31 11:56:32 +03:00
access_needed = pvfs_setfileinfo_access ( info ) ;
2004-12-30 09:02:54 +03:00
if ( ( f - > access_mask & access_needed ) ! = access_needed ) {
2004-12-30 08:50:23 +03:00
return NT_STATUS_ACCESS_DENIED ;
}
2004-10-26 09:39:54 +04:00
/* update the file information */
2008-02-28 14:17:59 +03:00
status = pvfs_resolve_name_handle ( pvfs , h ) ;
2004-10-26 09:39:54 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
/* we take a copy of the current file stats, then update
newstats in each of the elements below . At the end we
compare , and make any changes needed */
2004-11-08 06:54:12 +03:00
newstats = * h - > name ;
2004-10-26 09:39:54 +04:00
2004-09-20 11:28:43 +04:00
switch ( info - > generic . level ) {
2004-10-26 09:39:54 +04:00
case RAW_SFILEINFO_SETATTR :
if ( ! null_time ( info - > setattr . in . write_time ) ) {
unix_to_nt_time ( & newstats . dos . write_time , info - > setattr . in . write_time ) ;
2004-09-20 11:28:43 +04:00
}
2004-10-26 09:39:54 +04:00
if ( info - > setattr . in . attrib ! = FILE_ATTRIBUTE_NORMAL ) {
newstats . dos . attrib = info - > setattr . in . attrib ;
}
break ;
2004-10-24 17:41:27 +04:00
2004-09-20 11:28:43 +04:00
case RAW_SFILEINFO_SETATTRE :
2004-10-26 09:39:54 +04:00
case RAW_SFILEINFO_STANDARD :
if ( ! null_time ( info - > setattre . in . create_time ) ) {
unix_to_nt_time ( & newstats . dos . create_time , info - > setattre . in . create_time ) ;
}
if ( ! null_time ( info - > setattre . in . access_time ) ) {
unix_to_nt_time ( & newstats . dos . access_time , info - > setattre . in . access_time ) ;
2004-09-20 11:28:43 +04:00
}
2004-10-26 09:39:54 +04:00
if ( ! null_time ( info - > setattre . in . write_time ) ) {
unix_to_nt_time ( & newstats . dos . write_time , info - > setattre . in . write_time ) ;
}
break ;
2004-09-20 11:28:43 +04:00
2004-11-05 14:31:35 +03:00
case RAW_SFILEINFO_EA_SET :
2004-11-08 06:54:12 +03:00
return pvfs_setfileinfo_ea_set ( pvfs , h - > name , h - > fd ,
2004-12-17 07:51:23 +03:00
info - > ea_set . in . num_eas ,
info - > ea_set . in . eas ) ;
2004-11-05 14:31:35 +03:00
2004-10-26 09:39:54 +04:00
case RAW_SFILEINFO_BASIC_INFO :
case RAW_SFILEINFO_BASIC_INFORMATION :
2005-03-06 01:50:13 +03:00
if ( ! null_nttime ( info - > basic_info . in . create_time ) ) {
2004-10-26 09:39:54 +04:00
newstats . dos . create_time = info - > basic_info . in . create_time ;
}
2005-03-06 01:50:13 +03:00
if ( ! null_nttime ( info - > basic_info . in . access_time ) ) {
2004-10-26 09:39:54 +04:00
newstats . dos . access_time = info - > basic_info . in . access_time ;
}
2005-03-06 01:50:13 +03:00
if ( ! null_nttime ( info - > basic_info . in . write_time ) ) {
2004-10-26 09:39:54 +04:00
newstats . dos . write_time = info - > basic_info . in . write_time ;
}
2005-03-06 01:50:13 +03:00
if ( ! null_nttime ( info - > basic_info . in . change_time ) ) {
2004-10-26 09:39:54 +04:00
newstats . dos . change_time = info - > basic_info . in . change_time ;
}
2004-10-26 11:04:10 +04:00
if ( info - > basic_info . in . attrib ! = 0 ) {
2004-10-26 09:39:54 +04:00
newstats . dos . attrib = info - > basic_info . in . attrib ;
2004-09-20 11:28:43 +04:00
}
break ;
2004-10-24 17:41:27 +04:00
2004-10-24 16:39:15 +04:00
case RAW_SFILEINFO_DISPOSITION_INFO :
2004-10-24 17:41:27 +04:00
case RAW_SFILEINFO_DISPOSITION_INFORMATION :
2006-02-28 06:47:02 +03:00
return pvfs_set_delete_on_close ( pvfs , req , f ,
info - > disposition_info . in . delete_on_close ) ;
2004-10-24 18:18:03 +04:00
2004-10-26 09:39:54 +04:00
case RAW_SFILEINFO_ALLOCATION_INFO :
case RAW_SFILEINFO_ALLOCATION_INFORMATION :
2008-02-23 13:46:43 +03:00
status = pvfs_break_level2_oplocks ( f ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
2004-10-26 09:39:54 +04:00
newstats . dos . alloc_size = info - > allocation_info . in . alloc_size ;
2004-11-07 13:05:35 +03:00
if ( newstats . dos . alloc_size < newstats . st . st_size ) {
newstats . st . st_size = newstats . dos . alloc_size ;
}
2004-11-17 10:17:55 +03:00
newstats . dos . alloc_size = pvfs_round_alloc_size ( pvfs ,
newstats . dos . alloc_size ) ;
2004-10-26 09:39:54 +04:00
break ;
case RAW_SFILEINFO_END_OF_FILE_INFO :
case RAW_SFILEINFO_END_OF_FILE_INFORMATION :
2008-02-23 13:46:43 +03:00
status = pvfs_break_level2_oplocks ( f ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
2004-10-26 09:39:54 +04:00
newstats . st . st_size = info - > end_of_file_info . in . size ;
break ;
2004-10-24 18:18:03 +04:00
case RAW_SFILEINFO_POSITION_INFORMATION :
2004-11-08 06:54:12 +03:00
h - > position = info - > position_information . in . position ;
2004-10-24 18:18:03 +04:00
break ;
2004-10-26 09:39:54 +04:00
2004-11-07 13:05:35 +03:00
case RAW_SFILEINFO_MODE_INFORMATION :
/* this one is a puzzle */
if ( info - > mode_information . in . mode ! = 0 & &
info - > mode_information . in . mode ! = 2 & &
info - > mode_information . in . mode ! = 4 & &
info - > mode_information . in . mode ! = 6 ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2004-11-08 06:54:12 +03:00
h - > mode = info - > mode_information . in . mode ;
2004-11-07 13:05:35 +03:00
break ;
2004-11-08 06:54:12 +03:00
case RAW_SFILEINFO_RENAME_INFORMATION :
2008-02-14 06:54:21 +03:00
case RAW_SFILEINFO_RENAME_INFORMATION_SMB2 :
2009-01-07 08:46:34 +03:00
return pvfs_setfileinfo_rename ( pvfs , req , h - > name , f - > handle - > fd ,
2008-02-28 14:19:18 +03:00
& h - > odb_locking_key ,
2006-03-13 01:48:25 +03:00
info ) ;
2004-11-08 06:54:12 +03:00
2004-11-18 06:31:35 +03:00
case RAW_SFILEINFO_SEC_DESC :
2006-03-30 13:24:31 +04:00
notify_trigger ( pvfs - > notify_context ,
NOTIFY_ACTION_MODIFIED ,
FILE_NOTIFY_CHANGE_SECURITY ,
h - > name - > full_name ) ;
2004-12-31 11:56:32 +03:00
return pvfs_acl_set ( pvfs , req , h - > name , h - > fd , f - > access_mask , info ) ;
2004-11-18 06:31:35 +03:00
2004-10-26 09:39:54 +04:00
default :
return NT_STATUS_INVALID_LEVEL ;
}
/* possibly change the file size */
2004-11-08 06:54:12 +03:00
if ( newstats . st . st_size ! = h - > name - > st . st_size ) {
if ( h - > name - > dos . attrib & FILE_ATTRIBUTE_DIRECTORY ) {
2004-10-29 08:43:28 +04:00
return NT_STATUS_FILE_IS_A_DIRECTORY ;
}
2004-11-17 08:58:04 +03:00
if ( h - > name - > stream_name ) {
status = pvfs_stream_truncate ( pvfs , h - > name , h - > fd , newstats . st . st_size ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2006-03-30 13:24:31 +04:00
change_mask | = FILE_NOTIFY_CHANGE_STREAM_SIZE ;
2004-11-06 10:58:45 +03:00
} else {
2004-11-17 08:58:04 +03:00
int ret ;
2004-11-30 07:33:27 +03:00
if ( f - > access_mask &
( SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA ) ) {
2004-11-17 08:58:04 +03:00
ret = ftruncate ( h - > fd , newstats . st . st_size ) ;
} else {
ret = truncate ( h - > name - > full_name , newstats . st . st_size ) ;
}
if ( ret = = - 1 ) {
return pvfs_map_errno ( pvfs , errno ) ;
}
2006-04-06 15:07:21 +04:00
change_mask | = FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES ;
2004-10-26 09:39:54 +04:00
}
}
/* possibly change the file timestamps */
2006-04-06 05:56:04 +04:00
if ( newstats . dos . create_time ! = h - > name - > dos . create_time ) {
change_mask | = FILE_NOTIFY_CHANGE_CREATION ;
}
2004-11-08 06:54:12 +03:00
if ( newstats . dos . access_time ! = h - > name - > dos . access_time ) {
2006-03-30 13:24:31 +04:00
change_mask | = FILE_NOTIFY_CHANGE_LAST_ACCESS ;
2004-10-26 09:39:54 +04:00
}
2004-11-08 06:54:12 +03:00
if ( newstats . dos . write_time ! = h - > name - > dos . write_time ) {
2006-03-30 13:24:31 +04:00
change_mask | = FILE_NOTIFY_CHANGE_LAST_WRITE ;
2004-10-26 09:39:54 +04:00
}
2008-06-03 15:32:04 +04:00
if ( ( change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS ) | |
( change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE ) ) {
struct timeval tv [ 2 ] ;
nttime_to_timeval ( & tv [ 0 ] , newstats . dos . access_time ) ;
nttime_to_timeval ( & tv [ 1 ] , newstats . dos . write_time ) ;
if ( ! timeval_is_zero ( & tv [ 0 ] ) | | ! timeval_is_zero ( & tv [ 1 ] ) ) {
if ( utimes ( h - > name - > full_name , tv ) = = - 1 ) {
DEBUG ( 0 , ( " pvfs_setfileinfo: utimes() failed '%s' - %s \n " ,
h - > name - > full_name , strerror ( errno ) ) ) ;
return pvfs_map_errno ( pvfs , errno ) ;
}
2004-10-26 09:39:54 +04:00
}
}
2008-06-03 13:47:12 +04:00
if ( change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE ) {
struct odb_lock * lck ;
lck = odb_lock ( req , h - > pvfs - > odb_context , & h - > odb_locking_key ) ;
if ( lck = = NULL ) {
DEBUG ( 0 , ( " Unable to lock opendb for write time update \n " ) ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
status = odb_set_write_time ( lck , newstats . dos . write_time , true ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " Unable to update write time: %s \n " ,
nt_errstr ( status ) ) ) ;
talloc_free ( lck ) ;
return status ;
}
talloc_free ( lck ) ;
h - > write_time . update_forced = true ;
h - > write_time . update_on_close = false ;
talloc_free ( h - > write_time . update_event ) ;
h - > write_time . update_event = NULL ;
}
2004-10-26 09:39:54 +04:00
/* possibly change the attribute */
2004-11-08 06:54:12 +03:00
if ( newstats . dos . attrib ! = h - > name - > dos . attrib ) {
2008-05-28 14:06:04 +04:00
mode_t mode ;
if ( ( newstats . dos . attrib & FILE_ATTRIBUTE_DIRECTORY ) & &
! ( h - > name - > dos . attrib & FILE_ATTRIBUTE_DIRECTORY ) ) {
return NT_STATUS_INVALID_PARAMETER ;
}
mode = pvfs_fileperms ( pvfs , newstats . dos . attrib ) ;
2004-12-30 06:19:27 +03:00
if ( ! ( h - > name - > dos . attrib & FILE_ATTRIBUTE_DIRECTORY ) ) {
2010-03-05 14:48:09 +03:00
if ( pvfs_sys_fchmod ( pvfs , h - > fd , mode ) = = - 1 ) {
2004-12-30 06:19:27 +03:00
return pvfs_map_errno ( pvfs , errno ) ;
}
2004-10-26 09:39:54 +04:00
}
2006-03-30 13:24:31 +04:00
change_mask | = FILE_NOTIFY_CHANGE_ATTRIBUTES ;
2004-09-20 11:28:43 +04:00
}
2004-10-24 16:39:15 +04:00
2004-11-08 06:54:12 +03:00
* h - > name = newstats ;
2004-11-05 10:29:02 +03:00
2006-03-30 13:24:31 +04:00
notify_trigger ( pvfs - > notify_context ,
NOTIFY_ACTION_MODIFIED ,
change_mask ,
h - > name - > full_name ) ;
2004-11-08 06:54:12 +03:00
return pvfs_dosattrib_save ( pvfs , h - > name , h - > fd ) ;
2004-09-20 11:28:43 +04:00
}
2004-10-24 18:18:03 +04:00
2008-02-23 13:50:19 +03:00
/*
retry an open after a sharing violation
*/
static void pvfs_retry_setpathinfo ( struct pvfs_odb_retry * r ,
struct ntvfs_module_context * ntvfs ,
struct ntvfs_request * req ,
void * _info ,
void * private_data ,
enum pvfs_wait_notice reason )
{
union smb_setfileinfo * info = talloc_get_type ( _info ,
union smb_setfileinfo ) ;
NTSTATUS status = NT_STATUS_INTERNAL_ERROR ;
talloc_free ( r ) ;
switch ( reason ) {
case PVFS_WAIT_CANCEL :
/*TODO*/
status = NT_STATUS_CANCELLED ;
break ;
case PVFS_WAIT_TIMEOUT :
/* if it timed out, then give the failure
immediately */
/*TODO*/
status = NT_STATUS_SHARING_VIOLATION ;
break ;
case PVFS_WAIT_EVENT :
/* try the open again, which could trigger another retry setup
if it wants to , so we have to unmark the async flag so we
will know if it does a second async reply */
req - > async_states - > state & = ~ NTVFS_ASYNC_STATE_ASYNC ;
status = pvfs_setpathinfo ( ntvfs , req , info ) ;
if ( req - > async_states - > state & NTVFS_ASYNC_STATE_ASYNC ) {
/* the 2nd try also replied async, so we don't send
the reply yet */
return ;
}
/* re-mark it async, just in case someone up the chain does
paranoid checking */
req - > async_states - > state | = NTVFS_ASYNC_STATE_ASYNC ;
break ;
}
/* send the reply up the chain */
req - > async_states - > status = status ;
req - > async_states - > send_fn ( req ) ;
}
/*
setup for a unlink retry after a sharing violation
or a non granted oplock
*/
static NTSTATUS pvfs_setpathinfo_setup_retry ( struct ntvfs_module_context * ntvfs ,
struct ntvfs_request * req ,
union smb_setfileinfo * info ,
struct odb_lock * lck ,
NTSTATUS status )
{
2009-02-04 10:52:41 +03:00
struct pvfs_state * pvfs = talloc_get_type ( ntvfs - > private_data ,
struct pvfs_state ) ;
2008-02-23 13:50:19 +03:00
struct timeval end_time ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_SHARING_VIOLATION ) ) {
end_time = timeval_add ( & req - > statistics . request_time ,
0 , pvfs - > sharing_violation_delay ) ;
} else if ( NT_STATUS_EQUAL ( status , NT_STATUS_OPLOCK_NOT_GRANTED ) ) {
end_time = timeval_add ( & req - > statistics . request_time ,
pvfs - > oplock_break_timeout , 0 ) ;
} else {
return NT_STATUS_INTERNAL_ERROR ;
}
return pvfs_odb_retry_setup ( ntvfs , req , lck , end_time , info , NULL ,
pvfs_retry_setpathinfo ) ;
}
2004-10-24 18:18:03 +04:00
/*
set info on a pathname
*/
NTSTATUS pvfs_setpathinfo ( struct ntvfs_module_context * ntvfs ,
2006-03-10 17:31:17 +03:00
struct ntvfs_request * req , union smb_setfileinfo * info )
2004-10-24 18:18:03 +04:00
{
2009-02-04 10:52:41 +03:00
struct pvfs_state * pvfs = talloc_get_type ( ntvfs - > private_data ,
struct pvfs_state ) ;
2004-10-24 18:18:03 +04:00
struct pvfs_filename * name ;
2004-10-26 09:39:54 +04:00
struct pvfs_filename newstats ;
2004-10-24 18:18:03 +04:00
NTSTATUS status ;
2004-12-30 08:50:23 +03:00
uint32_t access_needed ;
2006-03-30 13:24:31 +04:00
uint32_t change_mask = 0 ;
2008-02-23 13:50:19 +03:00
struct odb_lock * lck = NULL ;
2008-02-28 14:19:18 +03:00
DATA_BLOB odb_locking_key ;
2004-10-24 18:18:03 +04:00
/* resolve the cifs name to a posix name */
2006-03-13 01:48:25 +03:00
status = pvfs_resolve_name ( pvfs , req , info - > generic . in . file . path ,
2004-11-17 09:44:50 +03:00
PVFS_RESOLVE_STREAMS , & name ) ;
2004-10-24 18:18:03 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
if ( ! name - > exists ) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
2004-12-31 11:56:32 +03:00
access_needed = pvfs_setfileinfo_access ( info ) ;
2004-12-30 08:50:23 +03:00
status = pvfs_access_check_simple ( pvfs , req , name , access_needed ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2004-10-26 09:39:54 +04:00
/* we take a copy of the current file stats, then update
newstats in each of the elements below . At the end we
compare , and make any changes needed */
newstats = * name ;
2004-10-24 18:18:03 +04:00
switch ( info - > generic . level ) {
2004-10-26 09:39:54 +04:00
case RAW_SFILEINFO_SETATTR :
if ( ! null_time ( info - > setattr . in . write_time ) ) {
unix_to_nt_time ( & newstats . dos . write_time , info - > setattr . in . write_time ) ;
2004-10-24 18:18:03 +04:00
}
2006-06-30 12:16:59 +04:00
if ( info - > setattr . in . attrib = = 0 ) {
newstats . dos . attrib = FILE_ATTRIBUTE_NORMAL ;
} else if ( info - > setattr . in . attrib ! = FILE_ATTRIBUTE_NORMAL ) {
2004-10-26 09:39:54 +04:00
newstats . dos . attrib = info - > setattr . in . attrib ;
}
break ;
2004-10-24 18:18:03 +04:00
case RAW_SFILEINFO_SETATTRE :
2004-10-26 09:39:54 +04:00
case RAW_SFILEINFO_STANDARD :
if ( ! null_time ( info - > setattre . in . create_time ) ) {
unix_to_nt_time ( & newstats . dos . create_time , info - > setattre . in . create_time ) ;
}
if ( ! null_time ( info - > setattre . in . access_time ) ) {
unix_to_nt_time ( & newstats . dos . access_time , info - > setattre . in . access_time ) ;
2004-10-24 18:18:03 +04:00
}
2004-10-26 09:39:54 +04:00
if ( ! null_time ( info - > setattre . in . write_time ) ) {
unix_to_nt_time ( & newstats . dos . write_time , info - > setattre . in . write_time ) ;
}
break ;
2004-10-24 18:18:03 +04:00
2004-11-05 14:31:35 +03:00
case RAW_SFILEINFO_EA_SET :
2004-12-17 07:51:23 +03:00
return pvfs_setfileinfo_ea_set ( pvfs , name , - 1 ,
info - > ea_set . in . num_eas ,
info - > ea_set . in . eas ) ;
2004-11-05 14:31:35 +03:00
2004-10-26 09:39:54 +04:00
case RAW_SFILEINFO_BASIC_INFO :
case RAW_SFILEINFO_BASIC_INFORMATION :
2005-03-06 01:50:13 +03:00
if ( ! null_nttime ( info - > basic_info . in . create_time ) ) {
2004-10-26 09:39:54 +04:00
newstats . dos . create_time = info - > basic_info . in . create_time ;
}
2005-03-06 01:50:13 +03:00
if ( ! null_nttime ( info - > basic_info . in . access_time ) ) {
2004-10-26 09:39:54 +04:00
newstats . dos . access_time = info - > basic_info . in . access_time ;
}
2005-03-06 01:50:13 +03:00
if ( ! null_nttime ( info - > basic_info . in . write_time ) ) {
2004-10-26 09:39:54 +04:00
newstats . dos . write_time = info - > basic_info . in . write_time ;
}
2005-03-06 01:50:13 +03:00
if ( ! null_nttime ( info - > basic_info . in . change_time ) ) {
2004-10-26 09:39:54 +04:00
newstats . dos . change_time = info - > basic_info . in . change_time ;
}
2004-10-26 13:52:00 +04:00
if ( info - > basic_info . in . attrib ! = 0 ) {
2004-10-26 09:39:54 +04:00
newstats . dos . attrib = info - > basic_info . in . attrib ;
2004-10-24 18:18:03 +04:00
}
break ;
2004-10-26 09:39:54 +04:00
case RAW_SFILEINFO_ALLOCATION_INFO :
case RAW_SFILEINFO_ALLOCATION_INFORMATION :
2008-02-23 13:50:19 +03:00
status = pvfs_can_update_file_size ( pvfs , req , name , & lck ) ;
/*
* on a sharing violation we need to retry when the file is closed by
* the other user , or after 1 second
* on a non granted oplock we need to retry when the file is closed by
* the other user , or after 30 seconds
*/
if ( ( NT_STATUS_EQUAL ( status , NT_STATUS_SHARING_VIOLATION ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_OPLOCK_NOT_GRANTED ) ) & &
( req - > async_states - > state & NTVFS_ASYNC_STATE_MAY_ASYNC ) ) {
return pvfs_setpathinfo_setup_retry ( pvfs - > ntvfs , req , info , lck , status ) ;
}
NT_STATUS_NOT_OK_RETURN ( status ) ;
2004-11-07 13:05:35 +03:00
if ( info - > allocation_info . in . alloc_size > newstats . dos . alloc_size ) {
/* strange. Increasing the allocation size via setpathinfo
should be silently ignored */
break ;
}
2004-10-26 09:39:54 +04:00
newstats . dos . alloc_size = info - > allocation_info . in . alloc_size ;
2004-11-07 13:05:35 +03:00
if ( newstats . dos . alloc_size < newstats . st . st_size ) {
newstats . st . st_size = newstats . dos . alloc_size ;
}
2004-11-17 10:17:55 +03:00
newstats . dos . alloc_size = pvfs_round_alloc_size ( pvfs ,
newstats . dos . alloc_size ) ;
2004-10-26 09:39:54 +04:00
break ;
case RAW_SFILEINFO_END_OF_FILE_INFO :
case RAW_SFILEINFO_END_OF_FILE_INFORMATION :
2008-02-23 13:50:19 +03:00
status = pvfs_can_update_file_size ( pvfs , req , name , & lck ) ;
/*
* on a sharing violation we need to retry when the file is closed by
* the other user , or after 1 second
* on a non granted oplock we need to retry when the file is closed by
* the other user , or after 30 seconds
*/
if ( ( NT_STATUS_EQUAL ( status , NT_STATUS_SHARING_VIOLATION ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_OPLOCK_NOT_GRANTED ) ) & &
( req - > async_states - > state & NTVFS_ASYNC_STATE_MAY_ASYNC ) ) {
return pvfs_setpathinfo_setup_retry ( pvfs - > ntvfs , req , info , lck , status ) ;
}
NT_STATUS_NOT_OK_RETURN ( status ) ;
2004-10-26 09:39:54 +04:00
newstats . st . st_size = info - > end_of_file_info . in . size ;
break ;
2004-11-07 13:05:35 +03:00
case RAW_SFILEINFO_MODE_INFORMATION :
if ( info - > mode_information . in . mode ! = 0 & &
info - > mode_information . in . mode ! = 2 & &
info - > mode_information . in . mode ! = 4 & &
info - > mode_information . in . mode ! = 6 ) {
return NT_STATUS_INVALID_PARAMETER ;
}
return NT_STATUS_OK ;
2004-11-08 06:54:12 +03:00
case RAW_SFILEINFO_RENAME_INFORMATION :
2008-02-14 06:54:21 +03:00
case RAW_SFILEINFO_RENAME_INFORMATION_SMB2 :
2008-02-28 14:19:18 +03:00
status = pvfs_locking_key ( name , name , & odb_locking_key ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
2009-01-07 08:46:34 +03:00
status = pvfs_setfileinfo_rename ( pvfs , req , name , - 1 ,
2008-02-28 14:19:18 +03:00
& odb_locking_key , info ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
return NT_STATUS_OK ;
2004-11-08 06:54:12 +03:00
2004-10-24 18:18:03 +04:00
case RAW_SFILEINFO_DISPOSITION_INFO :
case RAW_SFILEINFO_DISPOSITION_INFORMATION :
case RAW_SFILEINFO_POSITION_INFORMATION :
return NT_STATUS_OK ;
2004-10-26 09:39:54 +04:00
default :
return NT_STATUS_INVALID_LEVEL ;
2004-10-24 18:18:03 +04:00
}
2004-10-26 09:39:54 +04:00
/* possibly change the file size */
if ( newstats . st . st_size ! = name - > st . st_size ) {
2004-11-17 08:58:04 +03:00
if ( name - > stream_name ) {
status = pvfs_stream_truncate ( pvfs , name , - 1 , newstats . st . st_size ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
} else if ( truncate ( name - > full_name , newstats . st . st_size ) = = - 1 ) {
2004-10-26 09:39:54 +04:00
return pvfs_map_errno ( pvfs , errno ) ;
}
2006-04-06 15:07:21 +04:00
change_mask | = FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES ;
2004-10-26 09:39:54 +04:00
}
/* possibly change the file timestamps */
2006-04-06 05:56:04 +04:00
if ( newstats . dos . create_time ! = name - > dos . create_time ) {
change_mask | = FILE_NOTIFY_CHANGE_CREATION ;
}
2004-10-26 09:39:54 +04:00
if ( newstats . dos . access_time ! = name - > dos . access_time ) {
2006-04-06 05:56:04 +04:00
change_mask | = FILE_NOTIFY_CHANGE_LAST_ACCESS ;
2004-10-26 09:39:54 +04:00
}
if ( newstats . dos . write_time ! = name - > dos . write_time ) {
2006-04-06 05:56:04 +04:00
change_mask | = FILE_NOTIFY_CHANGE_LAST_WRITE ;
2004-10-26 09:39:54 +04:00
}
2008-06-03 15:32:04 +04:00
if ( ( change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS ) | |
( change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE ) ) {
struct timeval tv [ 2 ] ;
nttime_to_timeval ( & tv [ 0 ] , newstats . dos . access_time ) ;
nttime_to_timeval ( & tv [ 1 ] , newstats . dos . write_time ) ;
if ( ! timeval_is_zero ( & tv [ 0 ] ) | | ! timeval_is_zero ( & tv [ 1 ] ) ) {
if ( utimes ( name - > full_name , tv ) = = - 1 ) {
DEBUG ( 0 , ( " pvfs_setpathinfo: utimes() failed '%s' - %s \n " ,
name - > full_name , strerror ( errno ) ) ) ;
return pvfs_map_errno ( pvfs , errno ) ;
}
2004-10-26 09:39:54 +04:00
}
}
2008-06-03 13:47:12 +04:00
if ( change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE ) {
if ( lck = = NULL ) {
DATA_BLOB lkey ;
status = pvfs_locking_key ( name , name , & lkey ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
lck = odb_lock ( req , pvfs - > odb_context , & lkey ) ;
data_blob_free ( & lkey ) ;
if ( lck = = NULL ) {
DEBUG ( 0 , ( " Unable to lock opendb for write time update \n " ) ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
}
status = odb_set_write_time ( lck , newstats . dos . write_time , true ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_NAME_NOT_FOUND ) ) {
/* it could be that nobody has opened the file */
} else if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " Unable to update write time: %s \n " ,
nt_errstr ( status ) ) ) ;
return status ;
}
}
2004-10-26 09:39:54 +04:00
/* possibly change the attribute */
2004-11-14 12:16:03 +03:00
newstats . dos . attrib | = ( name - > dos . attrib & FILE_ATTRIBUTE_DIRECTORY ) ;
2004-10-26 09:39:54 +04:00
if ( newstats . dos . attrib ! = name - > dos . attrib ) {
mode_t mode = pvfs_fileperms ( pvfs , newstats . dos . attrib ) ;
2010-03-05 14:48:09 +03:00
if ( pvfs_sys_chmod ( pvfs , name - > full_name , mode ) = = - 1 ) {
2004-10-26 09:39:54 +04:00
return pvfs_map_errno ( pvfs , errno ) ;
}
2006-04-06 05:56:04 +04:00
change_mask | = FILE_NOTIFY_CHANGE_ATTRIBUTES ;
2004-10-26 09:39:54 +04:00
}
2004-11-05 10:29:02 +03:00
* name = newstats ;
2006-04-06 15:07:21 +04:00
if ( change_mask ! = 0 ) {
notify_trigger ( pvfs - > notify_context ,
NOTIFY_ACTION_MODIFIED ,
change_mask ,
name - > full_name ) ;
}
2006-03-30 13:24:31 +04:00
2004-11-05 14:31:35 +03:00
return pvfs_dosattrib_save ( pvfs , name , - 1 ) ;
2004-10-24 18:18:03 +04:00
}