2014-06-27 17:31:35 +04:00
/*
* Module for snapshot IO using snapper
*
* Copyright ( C ) David Disseldorp 2012 - 2014
*
2014-06-27 17:31:36 +04:00
* Portions taken from vfs_shadow_copy2 . c :
* Copyright ( C ) Andrew Tridgell 2007
* Copyright ( C ) Ed Plese 2009
* Copyright ( C ) Volker Lendecke 2011
* Copyright ( C ) Christian Ambach 2011
* Copyright ( C ) Michael Adam 2013
*
2014-06-27 17:31:35 +04:00
* 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
* the Free Software Foundation ; either version 3 of the License , or
* ( 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
* along with this program ; if not , see < http : //www.gnu.org/licenses/>.
*/
# include <dbus/dbus.h>
2014-11-09 00:18:08 +03:00
# ifdef HAVE_LINUX_IOCTL_H
2014-06-27 17:31:35 +04:00
# include <linux/ioctl.h>
2014-11-09 00:18:08 +03:00
# endif
2014-06-27 17:31:35 +04:00
# include <sys/ioctl.h>
# include <dirent.h>
# include <libgen.h>
# include "includes.h"
# include "include/ntioctl.h"
2014-06-30 16:40:13 +04:00
# include "include/smb.h"
2014-06-27 17:31:35 +04:00
# include "system/filesys.h"
# include "smbd/smbd.h"
# include "lib/util/tevent_ntstatus.h"
2020-07-03 09:11:20 +03:00
# include "lib/util/smb_strtox.h"
2014-06-27 17:31:35 +04:00
# define SNAPPER_SIG_LIST_SNAPS_RSP "a(uquxussa{ss})"
# define SNAPPER_SIG_LIST_CONFS_RSP "a(ssa{ss})"
2012-10-14 21:54:24 +04:00
# define SNAPPER_SIG_CREATE_SNAP_RSP "u"
# define SNAPPER_SIG_DEL_SNAPS_RSP ""
2014-06-27 17:31:35 +04:00
# define SNAPPER_SIG_STRING_DICT "{ss}"
struct snapper_dict {
char * key ;
char * val ;
} ;
struct snapper_snap {
uint32_t id ;
uint16_t type ;
uint32_t pre_id ;
int64_t time ;
uint32_t creator_uid ;
char * desc ;
char * cleanup ;
uint32_t num_user_data ;
struct snapper_dict * user_data ;
} ;
struct snapper_conf {
char * name ;
char * mnt ;
uint32_t num_attrs ;
struct snapper_dict * attrs ;
} ;
static const struct {
const char * snapper_err_str ;
NTSTATUS status ;
} snapper_err_map [ ] = {
{ " error.no_permissions " , NT_STATUS_ACCESS_DENIED } ,
} ;
static NTSTATUS snapper_err_ntstatus_map ( const char * snapper_err_str )
{
int i ;
if ( snapper_err_str = = NULL ) {
return NT_STATUS_UNSUCCESSFUL ;
}
for ( i = 0 ; i < ARRAY_SIZE ( snapper_err_map ) ; i + + ) {
if ( ! strcmp ( snapper_err_map [ i ] . snapper_err_str ,
snapper_err_str ) ) {
return snapper_err_map [ i ] . status ;
}
}
DEBUG ( 2 , ( " no explicit mapping for dbus error: %s \n " , snapper_err_str ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
2015-01-21 20:16:56 +03:00
/*
* Strings are UTF - 8. Other characters must be encoded hexadecimal as " \ x?? " .
* As a consequence " \" must be encoded as " \ \ " .
*/
static NTSTATUS snapper_dbus_str_encode ( TALLOC_CTX * mem_ctx , const char * in_str ,
char * * _out_str )
{
size_t in_len ;
char * out_str ;
int i ;
int out_off ;
int out_len ;
if ( in_str = = NULL ) {
return NT_STATUS_INVALID_PARAMETER ;
}
in_len = strlen ( in_str ) ;
/* output can be max 4 times the length of @in_str, +1 for terminator */
out_len = ( in_len * 4 ) + 1 ;
out_str = talloc_array ( mem_ctx , char , out_len ) ;
if ( out_str = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
out_off = 0 ;
for ( i = 0 ; i < in_len ; i + + ) {
size_t pushed ;
if ( in_str [ i ] = = ' \\ ' ) {
pushed = snprintf ( out_str + out_off , out_len - out_off ,
" \\ \\ " ) ;
} else if ( ( unsigned char ) in_str [ i ] > 127 ) {
pushed = snprintf ( out_str + out_off , out_len - out_off ,
" \\ x%02x " , ( unsigned char ) in_str [ i ] ) ;
} else {
/* regular character */
* ( out_str + out_off ) = in_str [ i ] ;
pushed = sizeof ( char ) ;
}
if ( pushed > = out_len - out_off ) {
/* truncated, should never happen */
talloc_free ( out_str ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
out_off + = pushed ;
}
* ( out_str + out_off ) = ' \0 ' ;
* _out_str = out_str ;
return NT_STATUS_OK ;
}
static NTSTATUS snapper_dbus_str_decode ( TALLOC_CTX * mem_ctx , const char * in_str ,
char * * _out_str )
{
size_t in_len ;
char * out_str ;
int i ;
int out_off ;
int out_len ;
if ( in_str = = NULL ) {
return NT_STATUS_INVALID_PARAMETER ;
}
in_len = strlen ( in_str ) ;
/* output cannot be larger than input, +1 for terminator */
out_len = in_len + 1 ;
out_str = talloc_array ( mem_ctx , char , out_len ) ;
if ( out_str = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
out_off = 0 ;
for ( i = 0 ; i < in_len ; i + + ) {
int j ;
char hex_buf [ 3 ] ;
unsigned int non_ascii_byte ;
if ( in_str [ i ] ! = ' \\ ' ) {
out_str [ out_off ] = in_str [ i ] ;
out_off + + ;
continue ;
}
i + + ;
if ( in_str [ i ] = = ' \\ ' ) {
out_str [ out_off ] = ' \\ ' ;
out_off + + ;
continue ;
} else if ( in_str [ i ] ! = ' x ' ) {
goto err_invalid_src_encoding ;
}
/* non-ASCII, encoded as two hex chars */
for ( j = 0 ; j < 2 ; j + + ) {
i + + ;
if ( ( in_str [ i ] = = ' \0 ' ) | | ! isxdigit ( in_str [ i ] ) ) {
goto err_invalid_src_encoding ;
}
hex_buf [ j ] = in_str [ i ] ;
}
hex_buf [ 2 ] = ' \0 ' ;
sscanf ( hex_buf , " %x " , & non_ascii_byte ) ;
out_str [ out_off ] = ( unsigned char ) non_ascii_byte ;
out_off + + ;
}
out_str [ out_off ] = ' \0 ' ;
* _out_str = out_str ;
return NT_STATUS_OK ;
err_invalid_src_encoding :
DEBUG ( 0 , ( " invalid encoding %s \n " , in_str ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
2014-06-27 17:31:35 +04:00
static DBusConnection * snapper_dbus_conn_create ( void )
{
DBusError err ;
DBusConnection * dconn ;
dbus_error_init ( & err ) ;
/*
* Always create a new DBus connection , to ensure snapperd detects the
* correct client [ E ] UID . With dbus_bus_get ( ) it does not !
*/
dconn = dbus_bus_get_private ( DBUS_BUS_SYSTEM , & err ) ;
if ( dbus_error_is_set ( & err ) ) {
DEBUG ( 0 , ( " dbus connection error: %s \n " , err . message ) ) ;
dbus_error_free ( & err ) ;
}
if ( dconn = = NULL ) {
return NULL ;
}
/* dbus_bus_get_private() sets exit-on-disconnect by default, undo it */
dbus_connection_set_exit_on_disconnect ( dconn , false ) ;
return dconn ;
}
static void snapper_dbus_conn_destroy ( DBusConnection * dconn )
{
if ( dconn = = NULL ) {
DEBUG ( 2 , ( " attempt to destroy NULL dbus connection \n " ) ) ;
return ;
}
dbus_connection_close ( dconn ) ;
dbus_connection_unref ( dconn ) ;
}
/*
* send the message @ send_msg over the dbus and wait for a response , return the
* responsee via @ recv_msg_out .
* @ send_msg is not freed , dbus_message_unref ( ) must be handled by the caller .
*/
static NTSTATUS snapper_dbus_msg_xchng ( DBusConnection * dconn ,
DBusMessage * send_msg ,
DBusMessage * * recv_msg_out )
{
DBusPendingCall * pending ;
DBusMessage * recv_msg ;
/* send message and get a handle for a reply */
if ( ! dbus_connection_send_with_reply ( dconn , send_msg , & pending , - 1 ) ) {
return NT_STATUS_NO_MEMORY ;
}
if ( NULL = = pending ) {
DEBUG ( 0 , ( " dbus msg send failed \n " ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
dbus_connection_flush ( dconn ) ;
/* block until we receive a reply */
dbus_pending_call_block ( pending ) ;
/* get the reply message */
recv_msg = dbus_pending_call_steal_reply ( pending ) ;
if ( recv_msg = = NULL ) {
DEBUG ( 0 , ( " Reply Null \n " ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
/* free the pending message handle */
dbus_pending_call_unref ( pending ) ;
* recv_msg_out = recv_msg ;
return NT_STATUS_OK ;
}
static NTSTATUS snapper_type_check ( DBusMessageIter * iter ,
int expected_type )
{
int type = dbus_message_iter_get_arg_type ( iter ) ;
if ( type ! = expected_type ) {
DEBUG ( 0 , ( " got type %d, expecting %d \n " ,
type , expected_type ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
return NT_STATUS_OK ;
}
static NTSTATUS snapper_type_check_get ( DBusMessageIter * iter ,
int expected_type ,
void * val )
{
NTSTATUS status ;
status = snapper_type_check ( iter , expected_type ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dbus_message_iter_get_basic ( iter , val ) ;
return NT_STATUS_OK ;
}
2015-01-21 20:16:57 +03:00
static NTSTATUS snapper_dict_unpack ( TALLOC_CTX * mem_ctx ,
DBusMessageIter * iter ,
2014-06-27 17:31:35 +04:00
struct snapper_dict * dict_out )
{
NTSTATUS status ;
DBusMessageIter dct_iter ;
2015-01-21 20:16:57 +03:00
char * key_encoded ;
char * val_encoded ;
2014-06-27 17:31:35 +04:00
status = snapper_type_check ( iter , DBUS_TYPE_DICT_ENTRY ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dbus_message_iter_recurse ( iter , & dct_iter ) ;
status = snapper_type_check_get ( & dct_iter , DBUS_TYPE_STRING ,
2015-01-21 20:16:57 +03:00
& key_encoded ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
status = snapper_dbus_str_decode ( mem_ctx , key_encoded , & dict_out - > key ) ;
2014-06-27 17:31:35 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dbus_message_iter_next ( & dct_iter ) ;
status = snapper_type_check_get ( & dct_iter , DBUS_TYPE_STRING ,
2015-01-21 20:16:57 +03:00
& val_encoded ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( dict_out - > key ) ;
return status ;
}
status = snapper_dbus_str_decode ( mem_ctx , val_encoded , & dict_out - > val ) ;
2014-06-27 17:31:35 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2015-01-21 20:16:57 +03:00
talloc_free ( dict_out - > key ) ;
2014-06-27 17:31:35 +04:00
return status ;
}
return NT_STATUS_OK ;
}
static void snapper_dict_array_print ( uint32_t num_dicts ,
struct snapper_dict * dicts )
{
int i ;
for ( i = 0 ; i < num_dicts ; i + + ) {
DEBUG ( 10 , ( " dict (key: %s, val: %s) \n " ,
dicts [ i ] . key , dicts [ i ] . val ) ) ;
}
}
static NTSTATUS snapper_dict_array_unpack ( TALLOC_CTX * mem_ctx ,
DBusMessageIter * iter ,
uint32_t * num_dicts_out ,
struct snapper_dict * * dicts_out )
{
NTSTATUS status ;
DBusMessageIter array_iter ;
uint32_t num_dicts ;
struct snapper_dict * dicts = NULL ;
status = snapper_type_check ( iter , DBUS_TYPE_ARRAY ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dbus_message_iter_recurse ( iter , & array_iter ) ;
num_dicts = 0 ;
while ( dbus_message_iter_get_arg_type ( & array_iter )
! = DBUS_TYPE_INVALID ) {
num_dicts + + ;
dicts = talloc_realloc ( mem_ctx , dicts , struct snapper_dict ,
num_dicts ) ;
if ( dicts = = NULL )
abort ( ) ;
2015-01-21 20:16:57 +03:00
status = snapper_dict_unpack ( mem_ctx , & array_iter ,
2014-06-27 17:31:35 +04:00
& dicts [ num_dicts - 1 ] ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( dicts ) ;
return status ;
}
dbus_message_iter_next ( & array_iter ) ;
}
* num_dicts_out = num_dicts ;
* dicts_out = dicts ;
return NT_STATUS_OK ;
}
static NTSTATUS snapper_list_confs_pack ( DBusMessage * * req_msg_out )
{
DBusMessage * msg ;
msg = dbus_message_new_method_call ( " org.opensuse.Snapper " ,
" /org/opensuse/Snapper " ,
" org.opensuse.Snapper " ,
" ListConfigs " ) ;
if ( msg = = NULL ) {
DEBUG ( 0 , ( " null msg \n " ) ) ;
return NT_STATUS_NO_MEMORY ;
}
/* no arguments to append */
* req_msg_out = msg ;
return NT_STATUS_OK ;
}
static NTSTATUS snapper_conf_unpack ( TALLOC_CTX * mem_ctx ,
DBusMessageIter * iter ,
struct snapper_conf * conf_out )
{
NTSTATUS status ;
DBusMessageIter st_iter ;
2015-01-21 20:16:57 +03:00
char * name_encoded ;
char * mnt_encoded ;
2014-06-27 17:31:35 +04:00
status = snapper_type_check ( iter , DBUS_TYPE_STRUCT ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dbus_message_iter_recurse ( iter , & st_iter ) ;
status = snapper_type_check_get ( & st_iter , DBUS_TYPE_STRING ,
2015-01-21 20:16:57 +03:00
& name_encoded ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
status = snapper_dbus_str_decode ( mem_ctx , name_encoded ,
& conf_out - > name ) ;
2014-06-27 17:31:35 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dbus_message_iter_next ( & st_iter ) ;
status = snapper_type_check_get ( & st_iter , DBUS_TYPE_STRING ,
2015-01-21 20:16:57 +03:00
& mnt_encoded ) ;
2014-06-27 17:31:35 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2015-01-21 20:16:57 +03:00
talloc_free ( conf_out - > name ) ;
return status ;
}
status = snapper_dbus_str_decode ( mem_ctx , mnt_encoded ,
& conf_out - > mnt ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( conf_out - > name ) ;
2014-06-27 17:31:35 +04:00
return status ;
}
dbus_message_iter_next ( & st_iter ) ;
status = snapper_dict_array_unpack ( mem_ctx , & st_iter ,
& conf_out - > num_attrs ,
& conf_out - > attrs ) ;
2015-01-21 20:16:57 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( conf_out - > mnt ) ;
talloc_free ( conf_out - > name ) ;
return status ;
}
2014-06-27 17:31:35 +04:00
2015-01-21 20:16:57 +03:00
return NT_STATUS_OK ;
2014-06-27 17:31:35 +04:00
}
static struct snapper_conf * snapper_conf_array_base_find ( int32_t num_confs ,
struct snapper_conf * confs ,
const char * base )
{
int i ;
for ( i = 0 ; i < num_confs ; i + + ) {
if ( strcmp ( confs [ i ] . mnt , base ) = = 0 ) {
DEBUG ( 5 , ( " found snapper conf %s for path %s \n " ,
confs [ i ] . name , base ) ) ;
return & confs [ i ] ;
}
}
DEBUG ( 5 , ( " config for base %s not found \n " , base ) ) ;
return NULL ;
}
static void snapper_conf_array_print ( int32_t num_confs ,
struct snapper_conf * confs )
{
int i ;
for ( i = 0 ; i < num_confs ; i + + ) {
DEBUG ( 10 , ( " name: %s, mnt: %s \n " ,
confs [ i ] . name , confs [ i ] . mnt ) ) ;
snapper_dict_array_print ( confs [ i ] . num_attrs , confs [ i ] . attrs ) ;
}
}
static NTSTATUS snapper_conf_array_unpack ( TALLOC_CTX * mem_ctx ,
DBusMessageIter * iter ,
uint32_t * num_confs_out ,
struct snapper_conf * * confs_out )
{
uint32_t num_confs ;
NTSTATUS status ;
struct snapper_conf * confs = NULL ;
DBusMessageIter array_iter ;
status = snapper_type_check ( iter , DBUS_TYPE_ARRAY ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dbus_message_iter_recurse ( iter , & array_iter ) ;
num_confs = 0 ;
while ( dbus_message_iter_get_arg_type ( & array_iter )
! = DBUS_TYPE_INVALID ) {
num_confs + + ;
confs = talloc_realloc ( mem_ctx , confs , struct snapper_conf ,
num_confs ) ;
if ( confs = = NULL )
abort ( ) ;
2014-06-30 16:40:14 +04:00
status = snapper_conf_unpack ( confs , & array_iter ,
2014-06-27 17:31:35 +04:00
& confs [ num_confs - 1 ] ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( confs ) ;
return status ;
}
dbus_message_iter_next ( & array_iter ) ;
}
* num_confs_out = num_confs ;
* confs_out = confs ;
return NT_STATUS_OK ;
}
static NTSTATUS snapper_list_confs_unpack ( TALLOC_CTX * mem_ctx ,
DBusConnection * dconn ,
DBusMessage * rsp_msg ,
uint32_t * num_confs_out ,
struct snapper_conf * * confs_out )
{
NTSTATUS status ;
DBusMessageIter iter ;
int msg_type ;
uint32_t num_confs ;
struct snapper_conf * confs ;
const char * sig ;
msg_type = dbus_message_get_type ( rsp_msg ) ;
if ( msg_type = = DBUS_MESSAGE_TYPE_ERROR ) {
const char * err_str = dbus_message_get_error_name ( rsp_msg ) ;
DEBUG ( 0 , ( " list_confs error response: %s \n " , err_str ) ) ;
return snapper_err_ntstatus_map ( err_str ) ;
}
if ( msg_type ! = DBUS_MESSAGE_TYPE_METHOD_RETURN ) {
DEBUG ( 0 , ( " unexpected list_confs ret type: %d \n " ,
msg_type ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
sig = dbus_message_get_signature ( rsp_msg ) ;
if ( ( sig = = NULL )
| | ( strcmp ( sig , SNAPPER_SIG_LIST_CONFS_RSP ) ! = 0 ) ) {
DEBUG ( 0 , ( " bad list confs response sig: %s, expected: %s \n " ,
( sig ? sig : " NULL " ) , SNAPPER_SIG_LIST_CONFS_RSP ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
if ( ! dbus_message_iter_init ( rsp_msg , & iter ) ) {
/* FIXME return empty? */
DEBUG ( 0 , ( " Message has no arguments! \n " ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
status = snapper_conf_array_unpack ( mem_ctx , & iter , & num_confs , & confs ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " failed to unpack conf array \n " ) ) ;
return status ;
}
snapper_conf_array_print ( num_confs , confs ) ;
* num_confs_out = num_confs ;
* confs_out = confs ;
return NT_STATUS_OK ;
}
2015-01-21 20:16:57 +03:00
static NTSTATUS snapper_list_snaps_pack ( TALLOC_CTX * mem_ctx ,
char * snapper_conf ,
2014-06-27 17:31:35 +04:00
DBusMessage * * req_msg_out )
{
DBusMessage * msg ;
DBusMessageIter args ;
2015-01-21 20:16:57 +03:00
char * conf_encoded ;
NTSTATUS status ;
2014-06-27 17:31:35 +04:00
msg = dbus_message_new_method_call ( " org.opensuse.Snapper " , /* target for the method call */
" /org/opensuse/Snapper " , /* object to call on */
" org.opensuse.Snapper " , /* interface to call on */
" ListSnapshots " ) ; /* method name */
if ( msg = = NULL ) {
DEBUG ( 0 , ( " failed to create list snaps message \n " ) ) ;
return NT_STATUS_NO_MEMORY ;
}
2015-01-21 20:16:57 +03:00
status = snapper_dbus_str_encode ( mem_ctx , snapper_conf , & conf_encoded ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
dbus_message_unref ( msg ) ;
return status ;
}
2014-06-27 17:31:35 +04:00
/* append arguments */
dbus_message_iter_init_append ( msg , & args ) ;
if ( ! dbus_message_iter_append_basic ( & args , DBUS_TYPE_STRING ,
2015-01-21 20:16:57 +03:00
& conf_encoded ) ) {
talloc_free ( conf_encoded ) ;
2015-01-21 20:16:55 +03:00
dbus_message_unref ( msg ) ;
2014-06-27 17:31:35 +04:00
return NT_STATUS_NO_MEMORY ;
}
* req_msg_out = msg ;
return NT_STATUS_OK ;
}
static NTSTATUS snapper_snap_struct_unpack ( TALLOC_CTX * mem_ctx ,
DBusMessageIter * iter ,
struct snapper_snap * snap_out )
{
NTSTATUS status ;
DBusMessageIter st_iter ;
2015-01-21 20:16:57 +03:00
char * desc_encoded ;
char * cleanup_encoded ;
2014-06-27 17:31:35 +04:00
status = snapper_type_check ( iter , DBUS_TYPE_STRUCT ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dbus_message_iter_recurse ( iter , & st_iter ) ;
status = snapper_type_check_get ( & st_iter , DBUS_TYPE_UINT32 ,
& snap_out - > id ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dbus_message_iter_next ( & st_iter ) ;
status = snapper_type_check_get ( & st_iter , DBUS_TYPE_UINT16 ,
& snap_out - > type ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dbus_message_iter_next ( & st_iter ) ;
status = snapper_type_check_get ( & st_iter , DBUS_TYPE_UINT32 ,
& snap_out - > pre_id ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dbus_message_iter_next ( & st_iter ) ;
status = snapper_type_check_get ( & st_iter , DBUS_TYPE_INT64 ,
& snap_out - > time ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dbus_message_iter_next ( & st_iter ) ;
status = snapper_type_check_get ( & st_iter , DBUS_TYPE_UINT32 ,
& snap_out - > creator_uid ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dbus_message_iter_next ( & st_iter ) ;
status = snapper_type_check_get ( & st_iter , DBUS_TYPE_STRING ,
2015-01-21 20:16:57 +03:00
& desc_encoded ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
status = snapper_dbus_str_decode ( mem_ctx , desc_encoded ,
& snap_out - > desc ) ;
2014-06-27 17:31:35 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dbus_message_iter_next ( & st_iter ) ;
status = snapper_type_check_get ( & st_iter , DBUS_TYPE_STRING ,
2015-01-21 20:16:57 +03:00
& cleanup_encoded ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( snap_out - > desc ) ;
return status ;
}
status = snapper_dbus_str_decode ( mem_ctx , cleanup_encoded ,
& snap_out - > cleanup ) ;
2014-06-27 17:31:35 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2015-01-21 20:16:57 +03:00
talloc_free ( snap_out - > desc ) ;
2014-06-27 17:31:35 +04:00
return status ;
}
dbus_message_iter_next ( & st_iter ) ;
status = snapper_dict_array_unpack ( mem_ctx , & st_iter ,
& snap_out - > num_user_data ,
& snap_out - > user_data ) ;
2015-01-21 20:16:57 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( snap_out - > cleanup ) ;
talloc_free ( snap_out - > desc ) ;
return status ;
}
2014-06-27 17:31:35 +04:00
2015-01-21 20:16:57 +03:00
return NT_STATUS_OK ;
2014-06-27 17:31:35 +04:00
}
static void snapper_snap_array_print ( int32_t num_snaps ,
struct snapper_snap * snaps )
{
int i ;
for ( i = 0 ; i < num_snaps ; i + + ) {
DEBUG ( 10 , ( " id: %u, "
" type: %u, "
" pre_id: %u, "
" time: %ld, "
" creator_uid: %u, "
" desc: %s, "
" cleanup: %s \n " ,
( unsigned int ) snaps [ i ] . id ,
( unsigned int ) snaps [ i ] . type ,
( unsigned int ) snaps [ i ] . pre_id ,
( long int ) snaps [ i ] . time ,
( unsigned int ) snaps [ i ] . creator_uid ,
snaps [ i ] . desc ,
snaps [ i ] . cleanup ) ) ;
snapper_dict_array_print ( snaps [ i ] . num_user_data ,
snaps [ i ] . user_data ) ;
}
}
static NTSTATUS snapper_snap_array_unpack ( TALLOC_CTX * mem_ctx ,
DBusMessageIter * iter ,
uint32_t * num_snaps_out ,
struct snapper_snap * * snaps_out )
{
uint32_t num_snaps ;
NTSTATUS status ;
struct snapper_snap * snaps = NULL ;
DBusMessageIter array_iter ;
status = snapper_type_check ( iter , DBUS_TYPE_ARRAY ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dbus_message_iter_recurse ( iter , & array_iter ) ;
num_snaps = 0 ;
while ( dbus_message_iter_get_arg_type ( & array_iter )
! = DBUS_TYPE_INVALID ) {
num_snaps + + ;
snaps = talloc_realloc ( mem_ctx , snaps , struct snapper_snap ,
num_snaps ) ;
if ( snaps = = NULL )
abort ( ) ;
2014-06-30 16:40:14 +04:00
status = snapper_snap_struct_unpack ( snaps , & array_iter ,
2014-06-27 17:31:35 +04:00
& snaps [ num_snaps - 1 ] ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( snaps ) ;
return status ;
}
dbus_message_iter_next ( & array_iter ) ;
}
* num_snaps_out = num_snaps ;
* snaps_out = snaps ;
return NT_STATUS_OK ;
}
static NTSTATUS snapper_list_snaps_unpack ( TALLOC_CTX * mem_ctx ,
DBusMessage * rsp_msg ,
uint32_t * num_snaps_out ,
struct snapper_snap * * snaps_out )
{
NTSTATUS status ;
DBusMessageIter iter ;
int msg_type ;
uint32_t num_snaps ;
struct snapper_snap * snaps ;
const char * sig ;
msg_type = dbus_message_get_type ( rsp_msg ) ;
if ( msg_type = = DBUS_MESSAGE_TYPE_ERROR ) {
const char * err_str = dbus_message_get_error_name ( rsp_msg ) ;
DEBUG ( 0 , ( " list_snaps error response: %s \n " , err_str ) ) ;
return snapper_err_ntstatus_map ( err_str ) ;
}
if ( msg_type ! = DBUS_MESSAGE_TYPE_METHOD_RETURN ) {
DEBUG ( 0 , ( " unexpected list_snaps ret type: %d \n " ,
msg_type ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
sig = dbus_message_get_signature ( rsp_msg ) ;
if ( ( sig = = NULL )
| | ( strcmp ( sig , SNAPPER_SIG_LIST_SNAPS_RSP ) ! = 0 ) ) {
DEBUG ( 0 , ( " bad list snaps response sig: %s, "
" expected: %s \n " ,
( sig ? sig : " NULL " ) ,
SNAPPER_SIG_LIST_SNAPS_RSP ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
/* read the parameters */
if ( ! dbus_message_iter_init ( rsp_msg , & iter ) ) {
DEBUG ( 0 , ( " response has no arguments! \n " ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
status = snapper_snap_array_unpack ( mem_ctx , & iter , & num_snaps , & snaps ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " failed to unpack snap array \n " ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
snapper_snap_array_print ( num_snaps , snaps ) ;
* num_snaps_out = num_snaps ;
* snaps_out = snaps ;
return NT_STATUS_OK ;
}
2012-10-14 21:54:24 +04:00
static NTSTATUS snapper_create_snap_pack ( TALLOC_CTX * mem_ctx ,
const char * snapper_conf ,
const char * desc ,
uint32_t num_user_data ,
struct snapper_dict * user_data ,
DBusMessage * * req_msg_out )
{
DBusMessage * msg ;
DBusMessageIter args ;
DBusMessageIter array_iter ;
DBusMessageIter struct_iter ;
const char * empty = " " ;
char * str_encoded ;
uint32_t i ;
bool ok ;
TALLOC_CTX * enc_ctx ;
NTSTATUS status ;
DEBUG ( 10 , ( " CreateSingleSnapshot: %s, %s, %s, num user %u \n " ,
snapper_conf , desc , empty , num_user_data ) ) ;
enc_ctx = talloc_new ( mem_ctx ) ;
if ( enc_ctx = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
msg = dbus_message_new_method_call ( " org.opensuse.Snapper " ,
" /org/opensuse/Snapper " ,
" org.opensuse.Snapper " ,
" CreateSingleSnapshot " ) ;
if ( msg = = NULL ) {
DEBUG ( 0 , ( " failed to create req msg \n " ) ) ;
talloc_free ( enc_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
status = snapper_dbus_str_encode ( enc_ctx , snapper_conf , & str_encoded ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
dbus_message_unref ( msg ) ;
talloc_free ( enc_ctx ) ;
return status ;
}
/* append arguments */
dbus_message_iter_init_append ( msg , & args ) ;
ok = dbus_message_iter_append_basic ( & args , DBUS_TYPE_STRING ,
& str_encoded ) ;
if ( ! ok ) {
dbus_message_unref ( msg ) ;
talloc_free ( enc_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
status = snapper_dbus_str_encode ( enc_ctx , desc , & str_encoded ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
dbus_message_unref ( msg ) ;
talloc_free ( enc_ctx ) ;
return status ;
}
ok = dbus_message_iter_append_basic ( & args , DBUS_TYPE_STRING ,
& str_encoded ) ;
if ( ! ok ) {
dbus_message_unref ( msg ) ;
talloc_free ( enc_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
/* cleanup - no need to encode empty string */
ok = dbus_message_iter_append_basic ( & args , DBUS_TYPE_STRING ,
& empty ) ;
if ( ! ok ) {
dbus_message_unref ( msg ) ;
talloc_free ( enc_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
ok = dbus_message_iter_open_container ( & args , DBUS_TYPE_ARRAY ,
SNAPPER_SIG_STRING_DICT ,
& array_iter ) ;
if ( ! ok ) {
dbus_message_unref ( msg ) ;
talloc_free ( enc_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
for ( i = 0 ; i < num_user_data ; i + + ) {
ok = dbus_message_iter_open_container ( & array_iter ,
DBUS_TYPE_DICT_ENTRY ,
NULL , & struct_iter ) ;
if ( ! ok ) {
dbus_message_unref ( msg ) ;
talloc_free ( enc_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
status = snapper_dbus_str_encode ( enc_ctx , user_data [ i ] . key ,
& str_encoded ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
dbus_message_unref ( msg ) ;
talloc_free ( enc_ctx ) ;
return status ;
}
ok = dbus_message_iter_append_basic ( & struct_iter ,
DBUS_TYPE_STRING ,
& str_encoded ) ;
if ( ! ok ) {
dbus_message_unref ( msg ) ;
talloc_free ( enc_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
status = snapper_dbus_str_encode ( enc_ctx , user_data [ i ] . val ,
& str_encoded ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
dbus_message_unref ( msg ) ;
talloc_free ( enc_ctx ) ;
return status ;
}
ok = dbus_message_iter_append_basic ( & struct_iter ,
DBUS_TYPE_STRING ,
& str_encoded ) ;
if ( ! ok ) {
dbus_message_unref ( msg ) ;
talloc_free ( enc_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
ok = dbus_message_iter_close_container ( & array_iter , & struct_iter ) ;
if ( ! ok ) {
dbus_message_unref ( msg ) ;
talloc_free ( enc_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
}
ok = dbus_message_iter_close_container ( & args , & array_iter ) ;
if ( ! ok ) {
dbus_message_unref ( msg ) ;
talloc_free ( enc_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
* req_msg_out = msg ;
return NT_STATUS_OK ;
}
static NTSTATUS snapper_create_snap_unpack ( DBusConnection * conn ,
DBusMessage * rsp_msg ,
uint32_t * snap_id_out )
{
NTSTATUS status ;
DBusMessageIter iter ;
int msg_type ;
const char * sig ;
uint32_t snap_id ;
msg_type = dbus_message_get_type ( rsp_msg ) ;
if ( msg_type = = DBUS_MESSAGE_TYPE_ERROR ) {
const char * err_str = dbus_message_get_error_name ( rsp_msg ) ;
DEBUG ( 0 , ( " create snap error response: %s, euid %d egid %d \n " ,
err_str , geteuid ( ) , getegid ( ) ) ) ;
return snapper_err_ntstatus_map ( err_str ) ;
}
if ( msg_type ! = DBUS_MESSAGE_TYPE_METHOD_RETURN ) {
DEBUG ( 0 , ( " unexpected create snap ret type: %d \n " ,
msg_type ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
sig = dbus_message_get_signature ( rsp_msg ) ;
if ( ( sig = = NULL )
| | ( strcmp ( sig , SNAPPER_SIG_CREATE_SNAP_RSP ) ! = 0 ) ) {
DEBUG ( 0 , ( " bad create snap response sig: %s, expected: %s \n " ,
( sig ? sig : " NULL " ) , SNAPPER_SIG_CREATE_SNAP_RSP ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
/* read the parameters */
if ( ! dbus_message_iter_init ( rsp_msg , & iter ) ) {
DEBUG ( 0 , ( " response has no arguments! \n " ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
status = snapper_type_check_get ( & iter , DBUS_TYPE_UINT32 , & snap_id ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
* snap_id_out = snap_id ;
return NT_STATUS_OK ;
}
static NTSTATUS snapper_del_snap_pack ( TALLOC_CTX * mem_ctx ,
const char * snapper_conf ,
uint32_t snap_id ,
DBusMessage * * req_msg_out )
{
DBusMessage * msg ;
DBusMessageIter args ;
DBusMessageIter array_iter ;
char * conf_encoded ;
bool ok ;
NTSTATUS status ;
msg = dbus_message_new_method_call ( " org.opensuse.Snapper " ,
" /org/opensuse/Snapper " ,
" org.opensuse.Snapper " ,
" DeleteSnapshots " ) ;
if ( msg = = NULL ) {
DEBUG ( 0 , ( " failed to create req msg \n " ) ) ;
return NT_STATUS_NO_MEMORY ;
}
status = snapper_dbus_str_encode ( mem_ctx , snapper_conf , & conf_encoded ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
dbus_message_unref ( msg ) ;
return status ;
}
/* append arguments */
dbus_message_iter_init_append ( msg , & args ) ;
ok = dbus_message_iter_append_basic ( & args , DBUS_TYPE_STRING ,
& conf_encoded ) ;
if ( ! ok ) {
talloc_free ( conf_encoded ) ;
dbus_message_unref ( msg ) ;
return NT_STATUS_NO_MEMORY ;
}
ok = dbus_message_iter_open_container ( & args , DBUS_TYPE_ARRAY ,
DBUS_TYPE_UINT32_AS_STRING ,
& array_iter ) ;
if ( ! ok ) {
talloc_free ( conf_encoded ) ;
dbus_message_unref ( msg ) ;
return NT_STATUS_NO_MEMORY ;
}
ok = dbus_message_iter_append_basic ( & array_iter ,
DBUS_TYPE_UINT32 ,
& snap_id ) ;
if ( ! ok ) {
talloc_free ( conf_encoded ) ;
dbus_message_unref ( msg ) ;
return NT_STATUS_NO_MEMORY ;
}
dbus_message_iter_close_container ( & args , & array_iter ) ;
* req_msg_out = msg ;
return NT_STATUS_OK ;
}
static NTSTATUS snapper_del_snap_unpack ( DBusConnection * conn ,
DBusMessage * rsp_msg )
{
int msg_type ;
const char * sig ;
msg_type = dbus_message_get_type ( rsp_msg ) ;
if ( msg_type = = DBUS_MESSAGE_TYPE_ERROR ) {
const char * err_str = dbus_message_get_error_name ( rsp_msg ) ;
DEBUG ( 0 , ( " del snap error response: %s \n " , err_str ) ) ;
return snapper_err_ntstatus_map ( err_str ) ;
}
if ( msg_type ! = DBUS_MESSAGE_TYPE_METHOD_RETURN ) {
DEBUG ( 0 , ( " unexpected del snap ret type: %d \n " ,
msg_type ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
sig = dbus_message_get_signature ( rsp_msg ) ;
if ( ( sig = = NULL )
| | ( strcmp ( sig , SNAPPER_SIG_DEL_SNAPS_RSP ) ! = 0 ) ) {
DEBUG ( 0 , ( " bad create snap response sig: %s, expected: %s \n " ,
( sig ? sig : " NULL " ) , SNAPPER_SIG_DEL_SNAPS_RSP ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
/* no parameters in response */
return NT_STATUS_OK ;
}
2015-01-21 20:16:57 +03:00
static NTSTATUS snapper_list_snaps_at_time_pack ( TALLOC_CTX * mem_ctx ,
const char * snapper_conf ,
2014-06-27 17:31:35 +04:00
time_t time_lower ,
time_t time_upper ,
DBusMessage * * req_msg_out )
{
DBusMessage * msg ;
DBusMessageIter args ;
2015-01-21 20:16:57 +03:00
char * conf_encoded ;
NTSTATUS status ;
2014-06-27 17:31:35 +04:00
msg = dbus_message_new_method_call ( " org.opensuse.Snapper " ,
" /org/opensuse/Snapper " ,
" org.opensuse.Snapper " ,
" ListSnapshotsAtTime " ) ;
if ( msg = = NULL ) {
DEBUG ( 0 , ( " failed to create list snaps message \n " ) ) ;
return NT_STATUS_NO_MEMORY ;
}
2015-01-21 20:16:57 +03:00
status = snapper_dbus_str_encode ( mem_ctx , snapper_conf , & conf_encoded ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
dbus_message_unref ( msg ) ;
return status ;
}
2014-06-27 17:31:35 +04:00
dbus_message_iter_init_append ( msg , & args ) ;
if ( ! dbus_message_iter_append_basic ( & args , DBUS_TYPE_STRING ,
2015-01-21 20:16:57 +03:00
& conf_encoded ) ) {
talloc_free ( conf_encoded ) ;
2015-01-21 20:16:55 +03:00
dbus_message_unref ( msg ) ;
2014-06-27 17:31:35 +04:00
return NT_STATUS_NO_MEMORY ;
}
if ( ! dbus_message_iter_append_basic ( & args , DBUS_TYPE_INT64 ,
& time_lower ) ) {
2015-01-21 20:16:57 +03:00
talloc_free ( conf_encoded ) ;
2015-01-21 20:16:55 +03:00
dbus_message_unref ( msg ) ;
2014-06-27 17:31:35 +04:00
return NT_STATUS_NO_MEMORY ;
}
if ( ! dbus_message_iter_append_basic ( & args , DBUS_TYPE_INT64 ,
& time_upper ) ) {
2015-01-21 20:16:57 +03:00
talloc_free ( conf_encoded ) ;
2015-01-21 20:16:55 +03:00
dbus_message_unref ( msg ) ;
2014-06-27 17:31:35 +04:00
return NT_STATUS_NO_MEMORY ;
}
* req_msg_out = msg ;
return NT_STATUS_OK ;
}
/* no snapper_list_snaps_at_time_unpack, use snapper_list_snaps_unpack */
2012-10-14 21:54:24 +04:00
/*
* Determine the snapper snapshot id given a path .
* Ideally this should be determined via a lookup .
*/
static NTSTATUS snapper_snap_path_to_id ( TALLOC_CTX * mem_ctx ,
const char * snap_path ,
uint32_t * snap_id_out )
{
char * path_dup ;
char * str_idx ;
uint32_t snap_id ;
2019-01-28 16:30:15 +03:00
int error = 0 ;
2012-10-14 21:54:24 +04:00
path_dup = talloc_strdup ( mem_ctx , snap_path ) ;
if ( path_dup = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
/* trim trailing '/' */
str_idx = path_dup + strlen ( path_dup ) - 1 ;
while ( * str_idx = = ' / ' ) {
* str_idx = ' \0 ' ;
str_idx - - ;
}
str_idx = strrchr ( path_dup , ' / ' ) ;
if ( ( str_idx = = NULL )
| | ( strcmp ( str_idx + 1 , " snapshot " ) ! = 0 ) ) {
talloc_free ( path_dup ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
while ( * str_idx = = ' / ' ) {
* str_idx = ' \0 ' ;
str_idx - - ;
}
str_idx = strrchr ( path_dup , ' / ' ) ;
if ( str_idx = = NULL ) {
talloc_free ( path_dup ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
str_idx + + ;
2019-06-04 10:04:15 +03:00
snap_id = smb_strtoul ( str_idx , NULL , 10 , & error , SMB_STR_STANDARD ) ;
2019-03-06 11:34:10 +03:00
if ( error ! = 0 ) {
2012-10-14 21:54:24 +04:00
talloc_free ( path_dup ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
talloc_free ( path_dup ) ;
* snap_id_out = snap_id ;
return NT_STATUS_OK ;
}
2014-06-27 17:31:35 +04:00
/*
* Determine the snapper snapshot path given an id and base .
* Ideally this should be determined via a lookup .
*/
static NTSTATUS snapper_snap_id_to_path ( TALLOC_CTX * mem_ctx ,
const char * base_path ,
uint32_t snap_id ,
char * * snap_path_out )
{
char * snap_path ;
snap_path = talloc_asprintf ( mem_ctx , " %s/.snapshots/%u/snapshot " ,
base_path , snap_id ) ;
if ( snap_path = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
* snap_path_out = snap_path ;
return NT_STATUS_OK ;
}
static NTSTATUS snapper_get_conf_call ( TALLOC_CTX * mem_ctx ,
DBusConnection * dconn ,
const char * path ,
char * * conf_name_out ,
char * * base_path_out )
{
NTSTATUS status ;
DBusMessage * req_msg ;
DBusMessage * rsp_msg ;
uint32_t num_confs = 0 ;
struct snapper_conf * confs = NULL ;
struct snapper_conf * conf ;
char * conf_name ;
char * base_path ;
status = snapper_list_confs_pack ( & req_msg ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_out ;
}
status = snapper_dbus_msg_xchng ( dconn , req_msg , & rsp_msg ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_req_free ;
}
status = snapper_list_confs_unpack ( mem_ctx , dconn , rsp_msg ,
& num_confs , & confs ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_rsp_free ;
}
/*
* for now we only support shares where the path directly corresponds
* to a snapper configuration .
*/
conf = snapper_conf_array_base_find ( num_confs , confs ,
path ) ;
if ( conf = = NULL ) {
status = NT_STATUS_NOT_SUPPORTED ;
goto err_array_free ;
}
conf_name = talloc_strdup ( mem_ctx , conf - > name ) ;
if ( conf_name = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto err_array_free ;
}
base_path = talloc_strdup ( mem_ctx , conf - > mnt ) ;
if ( base_path = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto err_conf_name_free ;
}
2014-06-30 16:40:14 +04:00
talloc_free ( confs ) ;
2014-06-27 17:31:35 +04:00
dbus_message_unref ( rsp_msg ) ;
dbus_message_unref ( req_msg ) ;
* conf_name_out = conf_name ;
* base_path_out = base_path ;
return NT_STATUS_OK ;
err_conf_name_free :
talloc_free ( conf_name ) ;
err_array_free :
2014-06-30 16:40:14 +04:00
talloc_free ( confs ) ;
2014-06-27 17:31:35 +04:00
err_rsp_free :
dbus_message_unref ( rsp_msg ) ;
err_req_free :
dbus_message_unref ( req_msg ) ;
err_out :
return status ;
}
2012-10-14 21:54:24 +04:00
/*
* Check whether a path can be shadow copied . Return the base volume , allowing
* the caller to determine if multiple paths lie on the same base volume .
*/
static NTSTATUS snapper_snap_check_path ( struct vfs_handle_struct * handle ,
TALLOC_CTX * mem_ctx ,
const char * service_path ,
char * * base_volume )
{
NTSTATUS status ;
DBusConnection * dconn ;
char * conf_name ;
char * base_path ;
dconn = snapper_dbus_conn_create ( ) ;
if ( dconn = = NULL ) {
return NT_STATUS_UNSUCCESSFUL ;
}
status = snapper_get_conf_call ( mem_ctx , dconn , service_path ,
& conf_name , & base_path ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_conn_close ;
}
talloc_free ( conf_name ) ;
* base_volume = base_path ;
snapper_dbus_conn_destroy ( dconn ) ;
return NT_STATUS_OK ;
err_conn_close :
snapper_dbus_conn_destroy ( dconn ) ;
return status ;
}
static NTSTATUS snapper_create_snap_call ( TALLOC_CTX * mem_ctx ,
DBusConnection * dconn ,
const char * conf_name ,
const char * base_path ,
const char * snap_desc ,
uint32_t num_user_data ,
struct snapper_dict * user_data ,
char * * snap_path_out )
{
NTSTATUS status ;
DBusMessage * req_msg ;
DBusMessage * rsp_msg ;
2015-11-13 16:14:10 +03:00
uint32_t snap_id = 0 ;
2012-10-14 21:54:24 +04:00
char * snap_path ;
status = snapper_create_snap_pack ( mem_ctx ,
conf_name ,
snap_desc ,
num_user_data ,
user_data ,
& req_msg ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_out ;
}
status = snapper_dbus_msg_xchng ( dconn , req_msg , & rsp_msg ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_req_free ;
}
status = snapper_create_snap_unpack ( dconn , rsp_msg , & snap_id ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_rsp_free ;
}
status = snapper_snap_id_to_path ( mem_ctx , base_path , snap_id ,
& snap_path ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_rsp_free ;
}
dbus_message_unref ( rsp_msg ) ;
dbus_message_unref ( req_msg ) ;
DEBUG ( 6 , ( " created new snapshot %u at %s \n " , snap_id , snap_path ) ) ;
* snap_path_out = snap_path ;
return NT_STATUS_OK ;
err_rsp_free :
dbus_message_unref ( rsp_msg ) ;
err_req_free :
dbus_message_unref ( req_msg ) ;
err_out :
return status ;
}
static NTSTATUS snapper_snap_create ( struct vfs_handle_struct * handle ,
TALLOC_CTX * mem_ctx ,
const char * base_volume ,
time_t * tstamp ,
bool rw ,
char * * _base_path ,
char * * _snap_path )
{
DBusConnection * dconn ;
NTSTATUS status ;
char * conf_name ;
char * base_path ;
2016-06-07 06:43:01 +03:00
char * snap_path = NULL ;
2012-10-14 21:54:24 +04:00
TALLOC_CTX * tmp_ctx ;
tmp_ctx = talloc_new ( mem_ctx ) ;
if ( tmp_ctx = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
dconn = snapper_dbus_conn_create ( ) ;
if ( dconn = = NULL ) {
talloc_free ( tmp_ctx ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
status = snapper_get_conf_call ( tmp_ctx , dconn , base_volume ,
& conf_name , & base_path ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
snapper_dbus_conn_destroy ( dconn ) ;
talloc_free ( tmp_ctx ) ;
return status ;
}
status = snapper_create_snap_call ( tmp_ctx , dconn ,
conf_name , base_path ,
" Snapshot created by Samba " ,
0 , NULL ,
& snap_path ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
snapper_dbus_conn_destroy ( dconn ) ;
talloc_free ( tmp_ctx ) ;
return status ;
}
snapper_dbus_conn_destroy ( dconn ) ;
* _base_path = talloc_steal ( mem_ctx , base_path ) ;
* _snap_path = talloc_steal ( mem_ctx , snap_path ) ;
talloc_free ( tmp_ctx ) ;
return NT_STATUS_OK ;
}
static NTSTATUS snapper_delete_snap_call ( TALLOC_CTX * mem_ctx ,
DBusConnection * dconn ,
const char * conf_name ,
uint32_t snap_id )
{
NTSTATUS status ;
2016-06-07 06:43:01 +03:00
DBusMessage * req_msg = NULL ;
2012-10-14 21:54:24 +04:00
DBusMessage * rsp_msg ;
status = snapper_del_snap_pack ( mem_ctx , conf_name , snap_id , & req_msg ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_out ;
}
status = snapper_dbus_msg_xchng ( dconn , req_msg , & rsp_msg ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_req_free ;
}
status = snapper_del_snap_unpack ( dconn , rsp_msg ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_rsp_free ;
}
dbus_message_unref ( rsp_msg ) ;
dbus_message_unref ( req_msg ) ;
DEBUG ( 6 , ( " deleted snapshot %u \n " , snap_id ) ) ;
return NT_STATUS_OK ;
err_rsp_free :
dbus_message_unref ( rsp_msg ) ;
err_req_free :
dbus_message_unref ( req_msg ) ;
err_out :
return status ;
}
static NTSTATUS snapper_snap_delete ( struct vfs_handle_struct * handle ,
TALLOC_CTX * mem_ctx ,
char * base_path ,
char * snap_path )
{
DBusConnection * dconn ;
NTSTATUS status ;
char * conf_name ;
char * snap_base_path ;
uint32_t snap_id ;
TALLOC_CTX * tmp_ctx ;
tmp_ctx = talloc_new ( mem_ctx ) ;
if ( tmp_ctx = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
dconn = snapper_dbus_conn_create ( ) ;
if ( dconn = = NULL ) {
talloc_free ( tmp_ctx ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
status = snapper_get_conf_call ( tmp_ctx , dconn , base_path ,
& conf_name , & snap_base_path ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
snapper_dbus_conn_destroy ( dconn ) ;
talloc_free ( tmp_ctx ) ;
return status ;
}
status = snapper_snap_path_to_id ( tmp_ctx , snap_path , & snap_id ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
snapper_dbus_conn_destroy ( dconn ) ;
talloc_free ( tmp_ctx ) ;
return status ;
}
status = snapper_delete_snap_call ( tmp_ctx , dconn , conf_name , snap_id ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
snapper_dbus_conn_destroy ( dconn ) ;
talloc_free ( tmp_ctx ) ;
return status ;
}
snapper_dbus_conn_destroy ( dconn ) ;
talloc_free ( tmp_ctx ) ;
return NT_STATUS_OK ;
}
2014-06-27 17:31:36 +04:00
/* sc_data used as parent talloc context for all labels */
static int snapper_get_shadow_copy_data ( struct vfs_handle_struct * handle ,
struct files_struct * fsp ,
struct shadow_copy_data * sc_data ,
bool labels )
{
DBusConnection * dconn ;
TALLOC_CTX * tmp_ctx ;
NTSTATUS status ;
char * conf_name ;
char * base_path ;
2016-06-07 06:43:01 +03:00
DBusMessage * req_msg = NULL ;
2014-06-27 17:31:36 +04:00
DBusMessage * rsp_msg ;
uint32_t num_snaps ;
struct snapper_snap * snaps ;
uint32_t i ;
uint32_t lbl_off ;
tmp_ctx = talloc_new ( sc_data ) ;
if ( tmp_ctx = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto err_out ;
}
dconn = snapper_dbus_conn_create ( ) ;
if ( dconn = = NULL ) {
status = NT_STATUS_UNSUCCESSFUL ;
goto err_mem_ctx_free ;
}
if ( fsp - > conn - > connectpath = = NULL ) {
status = NT_STATUS_INVALID_PARAMETER ;
goto err_conn_free ;
}
status = snapper_get_conf_call ( tmp_ctx , dconn ,
fsp - > conn - > connectpath ,
& conf_name ,
& base_path ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_conn_free ;
}
2015-01-21 20:16:57 +03:00
status = snapper_list_snaps_pack ( tmp_ctx , conf_name , & req_msg ) ;
2014-06-27 17:31:36 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_conn_free ;
}
status = snapper_dbus_msg_xchng ( dconn , req_msg , & rsp_msg ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_req_free ;
}
status = snapper_list_snaps_unpack ( tmp_ctx , rsp_msg ,
& num_snaps , & snaps ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_rsp_free ;
}
/* we should always get at least one snapshot (current) */
if ( num_snaps = = 0 ) {
DEBUG ( 1 , ( " zero snapshots in snap list response \n " ) ) ;
status = NT_STATUS_UNSUCCESSFUL ;
goto err_rsp_free ;
}
/* subtract 1, (current) snapshot is not returned */
sc_data - > num_volumes = num_snaps - 1 ;
sc_data - > labels = NULL ;
if ( ( labels = = false ) | | ( sc_data - > num_volumes = = 0 ) ) {
/* tokens need not be added to the labels array */
goto done ;
}
sc_data - > labels = talloc_array ( sc_data , SHADOW_COPY_LABEL ,
sc_data - > num_volumes ) ;
if ( sc_data - > labels = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto err_rsp_free ;
}
/* start at end for decending order, do not include 0 (current) */
lbl_off = 0 ;
for ( i = num_snaps - 1 ; i > 0 ; i - - ) {
char * lbl = sc_data - > labels [ lbl_off + + ] ;
struct tm gmt_snap_time ;
struct tm * tm_ret ;
size_t str_sz ;
tm_ret = gmtime_r ( ( time_t * ) & snaps [ i ] . time , & gmt_snap_time ) ;
if ( tm_ret = = NULL ) {
status = NT_STATUS_UNSUCCESSFUL ;
goto err_labels_free ;
}
str_sz = strftime ( lbl , sizeof ( SHADOW_COPY_LABEL ) ,
" @GMT-%Y.%m.%d-%H.%M.%S " , & gmt_snap_time ) ;
if ( str_sz = = 0 ) {
status = NT_STATUS_UNSUCCESSFUL ;
goto err_labels_free ;
}
}
done :
talloc_free ( tmp_ctx ) ;
dbus_message_unref ( rsp_msg ) ;
dbus_message_unref ( req_msg ) ;
snapper_dbus_conn_destroy ( dconn ) ;
return 0 ;
err_labels_free :
TALLOC_FREE ( sc_data - > labels ) ;
err_rsp_free :
dbus_message_unref ( rsp_msg ) ;
err_req_free :
dbus_message_unref ( req_msg ) ;
err_conn_free :
snapper_dbus_conn_destroy ( dconn ) ;
err_mem_ctx_free :
talloc_free ( tmp_ctx ) ;
err_out :
errno = map_errno_from_nt_status ( status ) ;
return - 1 ;
}
static bool snapper_gmt_strip_snapshot ( TALLOC_CTX * mem_ctx ,
struct vfs_handle_struct * handle ,
2020-05-02 12:39:58 +03:00
const struct smb_filename * smb_fname ,
2014-06-27 17:31:36 +04:00
time_t * ptimestamp ,
char * * pstripped )
{
char * stripped ;
2020-05-02 12:45:11 +03:00
if ( smb_fname - > twrp = = 0 ) {
goto no_snapshot ;
}
2014-06-27 17:31:36 +04:00
if ( pstripped ! = NULL ) {
2020-05-04 11:37:22 +03:00
stripped = talloc_strdup ( mem_ctx , smb_fname - > base_name ) ;
2014-06-27 17:31:36 +04:00
if ( stripped = = NULL ) {
return false ;
}
* pstripped = stripped ;
}
2020-05-04 11:37:22 +03:00
2020-05-02 12:45:11 +03:00
* ptimestamp = nt_time_to_unix ( smb_fname - > twrp ) ;
2014-06-27 17:31:36 +04:00
return true ;
no_snapshot :
* ptimestamp = 0 ;
return true ;
}
static NTSTATUS snapper_get_snap_at_time_call ( TALLOC_CTX * mem_ctx ,
DBusConnection * dconn ,
const char * conf_name ,
const char * base_path ,
time_t snaptime ,
char * * snap_path_out )
{
NTSTATUS status ;
2016-06-07 06:43:01 +03:00
DBusMessage * req_msg = NULL ;
2014-06-27 17:31:36 +04:00
DBusMessage * rsp_msg ;
uint32_t num_snaps ;
struct snapper_snap * snaps ;
char * snap_path ;
2015-01-21 20:16:57 +03:00
status = snapper_list_snaps_at_time_pack ( mem_ctx ,
conf_name ,
2014-06-27 17:31:36 +04:00
snaptime ,
snaptime ,
& req_msg ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_out ;
}
status = snapper_dbus_msg_xchng ( dconn , req_msg , & rsp_msg ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_req_free ;
}
status = snapper_list_snaps_unpack ( mem_ctx , rsp_msg ,
& num_snaps , & snaps ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_rsp_free ;
}
if ( num_snaps = = 0 ) {
2016-10-17 21:56:20 +03:00
DEBUG ( 4 , ( " no snapshots found with time: %lu \n " ,
( unsigned long ) snaptime ) ) ;
2014-06-27 17:31:36 +04:00
status = NT_STATUS_INVALID_PARAMETER ;
goto err_snap_array_free ;
} else if ( num_snaps > 0 ) {
DEBUG ( 4 , ( " got %u snapshots for single time %lu, using top \n " ,
2016-10-17 21:56:20 +03:00
num_snaps , ( unsigned long ) snaptime ) ) ;
2014-06-27 17:31:36 +04:00
}
status = snapper_snap_id_to_path ( mem_ctx , base_path , snaps [ 0 ] . id ,
& snap_path ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_snap_array_free ;
}
* snap_path_out = snap_path ;
err_snap_array_free :
2014-06-30 16:40:14 +04:00
talloc_free ( snaps ) ;
2014-06-27 17:31:36 +04:00
err_rsp_free :
dbus_message_unref ( rsp_msg ) ;
err_req_free :
dbus_message_unref ( req_msg ) ;
err_out :
return status ;
}
static NTSTATUS snapper_snap_path_expand ( struct connection_struct * conn ,
TALLOC_CTX * mem_ctx ,
time_t snap_time ,
char * * snap_dir_out )
{
DBusConnection * dconn ;
NTSTATUS status ;
char * conf_name ;
char * base_path ;
2016-04-02 03:08:05 +03:00
char * snap_path = NULL ;
2014-06-27 17:31:36 +04:00
dconn = snapper_dbus_conn_create ( ) ;
if ( dconn = = NULL ) {
status = NT_STATUS_UNSUCCESSFUL ;
goto err_out ;
}
if ( conn - > connectpath = = NULL ) {
status = NT_STATUS_INVALID_PARAMETER ;
goto err_conn_free ;
}
status = snapper_get_conf_call ( mem_ctx , dconn ,
conn - > connectpath ,
& conf_name ,
& base_path ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_conn_free ;
}
status = snapper_get_snap_at_time_call ( mem_ctx , dconn ,
conf_name , base_path , snap_time ,
& snap_path ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err_conf_name_free ;
}
/* confirm snapshot path is nested under base path */
if ( strncmp ( snap_path , base_path , strlen ( base_path ) ) ! = 0 ) {
status = NT_STATUS_INVALID_PARAMETER ;
goto err_snap_path_free ;
}
talloc_free ( conf_name ) ;
talloc_free ( base_path ) ;
snapper_dbus_conn_destroy ( dconn ) ;
* snap_dir_out = snap_path ;
return NT_STATUS_OK ;
err_snap_path_free :
talloc_free ( snap_path ) ;
err_conf_name_free :
talloc_free ( conf_name ) ;
talloc_free ( base_path ) ;
err_conn_free :
snapper_dbus_conn_destroy ( dconn ) ;
err_out :
return status ;
}
static char * snapper_gmt_convert ( TALLOC_CTX * mem_ctx ,
struct vfs_handle_struct * handle ,
const char * name , time_t timestamp )
{
char * snap_path = NULL ;
char * path = NULL ;
NTSTATUS status ;
int saved_errno ;
status = snapper_snap_path_expand ( handle - > conn , mem_ctx , timestamp ,
& snap_path ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
errno = map_errno_from_nt_status ( status ) ;
goto err_out ;
}
path = talloc_asprintf ( mem_ctx , " %s/%s " , snap_path , name ) ;
if ( path = = NULL ) {
errno = ENOMEM ;
goto err_snap_path_free ;
}
DEBUG ( 10 , ( " converted %s/%s @ time to %s \n " ,
handle - > conn - > connectpath , name , path ) ) ;
return path ;
err_snap_path_free :
saved_errno = errno ;
talloc_free ( snap_path ) ;
errno = saved_errno ;
err_out :
return NULL ;
}
2019-08-10 01:28:34 +03:00
static int snapper_gmt_renameat ( vfs_handle_struct * handle ,
files_struct * srcfsp ,
const struct smb_filename * smb_fname_src ,
files_struct * dstfsp ,
const struct smb_filename * smb_fname_dst )
{
time_t timestamp_src , timestamp_dst ;
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) , handle ,
2020-05-02 12:39:58 +03:00
smb_fname_src ,
2019-08-10 01:28:34 +03:00
& timestamp_src , NULL ) ) {
return - 1 ;
}
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) , handle ,
2020-05-02 12:39:58 +03:00
smb_fname_dst ,
2019-08-10 01:28:34 +03:00
& timestamp_dst , NULL ) ) {
return - 1 ;
}
if ( timestamp_src ! = 0 ) {
errno = EXDEV ;
return - 1 ;
}
if ( timestamp_dst ! = 0 ) {
errno = EROFS ;
return - 1 ;
}
return SMB_VFS_NEXT_RENAMEAT ( handle ,
srcfsp ,
smb_fname_src ,
dstfsp ,
smb_fname_dst ) ;
}
2019-08-30 23:54:52 +03:00
static int snapper_gmt_symlinkat ( vfs_handle_struct * handle ,
2020-04-30 20:30:50 +03:00
const struct smb_filename * link_contents ,
2019-08-30 23:54:52 +03:00
struct files_struct * dirfsp ,
const struct smb_filename * new_smb_fname )
{
time_t timestamp_old = 0 ;
time_t timestamp_new = 0 ;
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) ,
handle ,
2020-05-02 12:39:58 +03:00
link_contents ,
2019-08-30 23:54:52 +03:00
& timestamp_old ,
NULL ) ) {
return - 1 ;
}
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) ,
handle ,
2020-05-02 12:39:58 +03:00
new_smb_fname ,
2019-08-30 23:54:52 +03:00
& timestamp_new ,
NULL ) ) {
return - 1 ;
}
if ( ( timestamp_old ! = 0 ) | | ( timestamp_new ! = 0 ) ) {
errno = EROFS ;
return - 1 ;
}
return SMB_VFS_NEXT_SYMLINKAT ( handle ,
link_contents ,
dirfsp ,
new_smb_fname ) ;
}
2019-08-17 02:01:22 +03:00
static int snapper_gmt_linkat ( vfs_handle_struct * handle ,
files_struct * srcfsp ,
const struct smb_filename * old_smb_fname ,
files_struct * dstfsp ,
const struct smb_filename * new_smb_fname ,
int flags )
{
time_t timestamp_old = 0 ;
time_t timestamp_new = 0 ;
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) ,
handle ,
2020-05-02 12:39:58 +03:00
old_smb_fname ,
2019-08-17 02:01:22 +03:00
& timestamp_old ,
NULL ) ) {
return - 1 ;
}
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) ,
handle ,
2020-05-02 12:39:58 +03:00
new_smb_fname ,
2019-08-17 02:01:22 +03:00
& timestamp_new ,
NULL ) ) {
return - 1 ;
}
if ( ( timestamp_old ! = 0 ) | | ( timestamp_new ! = 0 ) ) {
errno = EROFS ;
return - 1 ;
}
return SMB_VFS_NEXT_LINKAT ( handle ,
srcfsp ,
old_smb_fname ,
dstfsp ,
new_smb_fname ,
flags ) ;
}
2014-06-27 17:31:36 +04:00
static int snapper_gmt_stat ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname )
{
time_t timestamp ;
char * stripped , * tmp ;
int ret , saved_errno ;
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) , handle ,
2020-05-02 12:39:58 +03:00
smb_fname ,
2014-06-27 17:31:36 +04:00
& timestamp , & stripped ) ) {
return - 1 ;
}
if ( timestamp = = 0 ) {
return SMB_VFS_NEXT_STAT ( handle , smb_fname ) ;
}
tmp = smb_fname - > base_name ;
smb_fname - > base_name = snapper_gmt_convert ( talloc_tos ( ) , handle ,
stripped , timestamp ) ;
TALLOC_FREE ( stripped ) ;
if ( smb_fname - > base_name = = NULL ) {
smb_fname - > base_name = tmp ;
return - 1 ;
}
ret = SMB_VFS_NEXT_STAT ( handle , smb_fname ) ;
saved_errno = errno ;
TALLOC_FREE ( smb_fname - > base_name ) ;
smb_fname - > base_name = tmp ;
errno = saved_errno ;
return ret ;
}
static int snapper_gmt_lstat ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname )
{
time_t timestamp ;
char * stripped , * tmp ;
int ret , saved_errno ;
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) , handle ,
2020-05-02 12:39:58 +03:00
smb_fname ,
2014-06-27 17:31:36 +04:00
& timestamp , & stripped ) ) {
return - 1 ;
}
if ( timestamp = = 0 ) {
return SMB_VFS_NEXT_LSTAT ( handle , smb_fname ) ;
}
tmp = smb_fname - > base_name ;
smb_fname - > base_name = snapper_gmt_convert ( talloc_tos ( ) , handle ,
stripped , timestamp ) ;
TALLOC_FREE ( stripped ) ;
if ( smb_fname - > base_name = = NULL ) {
smb_fname - > base_name = tmp ;
return - 1 ;
}
ret = SMB_VFS_NEXT_LSTAT ( handle , smb_fname ) ;
saved_errno = errno ;
TALLOC_FREE ( smb_fname - > base_name ) ;
smb_fname - > base_name = tmp ;
errno = saved_errno ;
return ret ;
}
2020-05-20 23:02:48 +03:00
static int snapper_gmt_openat ( struct vfs_handle_struct * handle ,
const struct files_struct * dirfsp ,
const struct smb_filename * smb_fname_in ,
struct files_struct * fsp ,
int flags ,
mode_t mode )
{
struct smb_filename * smb_fname = NULL ;
time_t timestamp ;
char * stripped = NULL ;
int ret ;
int saved_errno = 0 ;
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) , handle ,
smb_fname_in ,
& timestamp , & stripped ) ) {
return - 1 ;
}
if ( timestamp = = 0 ) {
return SMB_VFS_NEXT_OPENAT ( handle ,
dirfsp ,
smb_fname_in ,
fsp ,
flags ,
mode ) ;
}
smb_fname = cp_smb_filename ( talloc_tos ( ) , smb_fname_in ) ;
if ( smb_fname = = NULL ) {
TALLOC_FREE ( stripped ) ;
return - 1 ;
}
smb_fname - > base_name = snapper_gmt_convert ( smb_fname , handle ,
stripped , timestamp ) ;
TALLOC_FREE ( stripped ) ;
if ( smb_fname - > base_name = = NULL ) {
TALLOC_FREE ( smb_fname ) ;
errno = ENOMEM ;
return - 1 ;
}
ret = SMB_VFS_NEXT_OPENAT ( handle , dirfsp , smb_fname , fsp , flags , mode ) ;
if ( ret = = - 1 ) {
saved_errno = errno ;
}
TALLOC_FREE ( smb_fname ) ;
if ( saved_errno ! = 0 ) {
errno = saved_errno ;
}
return ret ;
}
2019-09-12 23:21:41 +03:00
static int snapper_gmt_unlinkat ( vfs_handle_struct * handle ,
struct files_struct * dirfsp ,
const struct smb_filename * smb_fname ,
int flags )
{
time_t timestamp = 0 ;
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) , handle ,
2020-05-02 12:39:58 +03:00
smb_fname ,
2019-09-12 23:21:41 +03:00
& timestamp , NULL ) ) {
return - 1 ;
}
if ( timestamp ! = 0 ) {
errno = EROFS ;
return - 1 ;
}
return SMB_VFS_NEXT_UNLINKAT ( handle ,
dirfsp ,
smb_fname ,
flags ) ;
}
2021-04-09 16:57:08 +03:00
static int snapper_gmt_fchmod ( vfs_handle_struct * handle ,
struct files_struct * fsp ,
mode_t mode )
{
time_t timestamp = 0 ;
const struct smb_filename * smb_fname = NULL ;
smb_fname = fsp - > fsp_name ;
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) ,
handle ,
smb_fname ,
& timestamp ,
NULL ) ) {
return - 1 ;
}
if ( timestamp ! = 0 ) {
errno = EROFS ;
return - 1 ;
}
return SMB_VFS_NEXT_FCHMOD ( handle , fsp , mode ) ;
}
2014-06-27 17:31:36 +04:00
static int snapper_gmt_chdir ( vfs_handle_struct * handle ,
2017-06-29 21:29:33 +03:00
const struct smb_filename * smb_fname )
2014-06-27 17:31:36 +04:00
{
2017-06-29 21:29:33 +03:00
time_t timestamp = 0 ;
char * stripped = NULL ;
int ret ;
int saved_errno = 0 ;
char * conv = NULL ;
struct smb_filename * conv_smb_fname = NULL ;
2014-06-27 17:31:36 +04:00
2017-06-29 21:29:33 +03:00
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) ,
handle ,
2020-05-02 12:39:58 +03:00
smb_fname ,
2017-06-29 21:29:33 +03:00
& timestamp ,
& stripped ) ) {
2014-06-27 17:31:36 +04:00
return - 1 ;
}
if ( timestamp = = 0 ) {
2017-06-29 21:29:33 +03:00
return SMB_VFS_NEXT_CHDIR ( handle , smb_fname ) ;
2014-06-27 17:31:36 +04:00
}
conv = snapper_gmt_convert ( talloc_tos ( ) , handle , stripped , timestamp ) ;
TALLOC_FREE ( stripped ) ;
if ( conv = = NULL ) {
return - 1 ;
}
2017-06-29 21:29:33 +03:00
conv_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
conv ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
0 ,
2017-06-29 21:29:33 +03:00
smb_fname - > flags ) ;
if ( conv_smb_fname = = NULL ) {
TALLOC_FREE ( conv ) ;
errno = ENOMEM ;
return - 1 ;
}
ret = SMB_VFS_NEXT_CHDIR ( handle , conv_smb_fname ) ;
if ( ret = = - 1 ) {
saved_errno = errno ;
}
2014-06-27 17:31:36 +04:00
TALLOC_FREE ( conv ) ;
2017-06-29 21:29:33 +03:00
TALLOC_FREE ( conv_smb_fname ) ;
if ( saved_errno ! = 0 ) {
errno = saved_errno ;
}
2014-06-27 17:31:36 +04:00
return ret ;
}
2021-04-13 14:39:09 +03:00
static int snapper_gmt_fntimes ( vfs_handle_struct * handle ,
files_struct * fsp ,
struct smb_file_time * ft )
{
time_t timestamp = 0 ;
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) ,
handle ,
fsp - > fsp_name ,
& timestamp ,
NULL ) ) {
return - 1 ;
}
if ( timestamp ! = 0 ) {
errno = EROFS ;
return - 1 ;
}
return SMB_VFS_NEXT_FNTIMES ( handle , fsp , ft ) ;
}
2019-08-23 00:28:26 +03:00
static int snapper_gmt_readlinkat ( vfs_handle_struct * handle ,
2020-10-13 16:19:30 +03:00
const struct files_struct * dirfsp ,
2019-08-23 00:28:26 +03:00
const struct smb_filename * smb_fname ,
char * buf ,
size_t bufsiz )
{
time_t timestamp = 0 ;
int ret ;
int saved_errno = 0 ;
2021-02-11 22:43:50 +03:00
struct smb_filename * full_fname = NULL ;
2019-08-23 00:28:26 +03:00
2021-02-11 22:43:50 +03:00
/*
* Now this function only looks at smb_fname - > twrp
* we don ' t need to copy out the path . Just use
* smb_fname - > base_name directly .
*/
2019-08-23 00:28:26 +03:00
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) , handle ,
2020-05-02 12:39:58 +03:00
smb_fname ,
2021-02-11 22:43:50 +03:00
& timestamp , NULL ) ) {
2019-08-23 00:28:26 +03:00
return - 1 ;
}
if ( timestamp = = 0 ) {
return SMB_VFS_NEXT_READLINKAT ( handle ,
dirfsp ,
smb_fname ,
buf ,
bufsiz ) ;
}
2021-02-11 22:43:50 +03:00
full_fname = full_path_from_dirfsp_atname ( talloc_tos ( ) ,
dirfsp ,
smb_fname ) ;
if ( full_fname = = NULL ) {
2019-08-23 00:28:26 +03:00
return - 1 ;
}
2021-02-11 22:43:50 +03:00
/* Find the snapshot path from the full pathname. */
full_fname - > base_name = snapper_gmt_convert ( full_fname ,
handle ,
full_fname - > base_name ,
timestamp ) ;
if ( full_fname - > base_name = = NULL ) {
TALLOC_FREE ( full_fname ) ;
2019-08-23 00:28:26 +03:00
return - 1 ;
}
ret = SMB_VFS_NEXT_READLINKAT ( handle ,
2021-02-11 22:43:50 +03:00
handle - > conn - > cwd_fsp ,
full_fname ,
2019-08-23 00:28:26 +03:00
buf ,
bufsiz ) ;
if ( ret = = - 1 ) {
saved_errno = errno ;
}
2021-02-11 22:43:50 +03:00
TALLOC_FREE ( full_fname ) ;
2019-08-23 00:28:26 +03:00
if ( saved_errno ! = 0 ) {
errno = saved_errno ;
}
return ret ;
}
2019-08-21 21:03:41 +03:00
static int snapper_gmt_mknodat ( vfs_handle_struct * handle ,
files_struct * dirfsp ,
const struct smb_filename * smb_fname ,
mode_t mode ,
SMB_DEV_T dev )
{
time_t timestamp = ( time_t ) 0 ;
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) , handle ,
2020-05-02 12:39:58 +03:00
smb_fname ,
2019-08-21 21:03:41 +03:00
& timestamp , NULL ) ) {
return - 1 ;
}
if ( timestamp ! = 0 ) {
errno = EROFS ;
return - 1 ;
}
return SMB_VFS_NEXT_MKNODAT ( handle ,
dirfsp ,
smb_fname ,
mode ,
dev ) ;
}
2017-06-30 21:32:59 +03:00
static struct smb_filename * snapper_gmt_realpath ( vfs_handle_struct * handle ,
TALLOC_CTX * ctx ,
const struct smb_filename * smb_fname )
2014-06-27 17:31:36 +04:00
{
2017-06-30 21:32:59 +03:00
time_t timestamp = 0 ;
2014-06-27 17:31:36 +04:00
char * stripped = NULL ;
2017-06-30 21:32:59 +03:00
struct smb_filename * result_fname = NULL ;
struct smb_filename * conv_smb_fname = NULL ;
int saved_errno = 0 ;
2014-06-27 17:31:36 +04:00
2017-06-30 21:32:59 +03:00
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) , handle ,
2020-05-02 12:39:58 +03:00
smb_fname ,
2014-06-27 17:31:36 +04:00
& timestamp , & stripped ) ) {
goto done ;
}
if ( timestamp = = 0 ) {
2017-06-30 21:32:59 +03:00
return SMB_VFS_NEXT_REALPATH ( handle , ctx , smb_fname ) ;
2014-06-27 17:31:36 +04:00
}
2017-06-30 21:32:59 +03:00
conv_smb_fname = cp_smb_filename ( talloc_tos ( ) , smb_fname ) ;
if ( conv_smb_fname = = NULL ) {
2014-06-27 17:31:36 +04:00
goto done ;
}
2017-06-30 21:32:59 +03:00
conv_smb_fname - > base_name = snapper_gmt_convert ( conv_smb_fname , handle ,
stripped , timestamp ) ;
if ( conv_smb_fname - > base_name = = NULL ) {
2014-06-27 17:31:36 +04:00
goto done ;
}
2017-06-30 21:32:59 +03:00
result_fname = SMB_VFS_NEXT_REALPATH ( handle , ctx , conv_smb_fname ) ;
2014-06-27 17:31:36 +04:00
done :
2017-06-30 21:32:59 +03:00
if ( result_fname = = NULL ) {
saved_errno = errno ;
}
TALLOC_FREE ( conv_smb_fname ) ;
2014-06-27 17:31:36 +04:00
TALLOC_FREE ( stripped ) ;
2017-06-30 21:32:59 +03:00
if ( saved_errno ! = 0 ) {
errno = saved_errno ;
}
return result_fname ;
2014-06-27 17:31:36 +04:00
}
2019-09-05 20:33:38 +03:00
static int snapper_gmt_mkdirat ( vfs_handle_struct * handle ,
struct files_struct * dirfsp ,
const struct smb_filename * fname ,
mode_t mode )
{
time_t timestamp = 0 ;
2020-05-02 12:39:58 +03:00
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) , handle , fname ,
2019-09-05 20:33:38 +03:00
& timestamp , NULL ) ) {
return - 1 ;
}
if ( timestamp ! = 0 ) {
errno = EROFS ;
return - 1 ;
}
return SMB_VFS_NEXT_MKDIRAT ( handle ,
dirfsp ,
fname ,
mode ) ;
}
2021-06-11 17:46:56 +03:00
static int snapper_gmt_fchflags ( vfs_handle_struct * handle ,
struct files_struct * fsp ,
unsigned int flags )
{
time_t timestamp = 0 ;
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) , handle ,
fsp - > fsp_name , & timestamp , NULL ) ) {
return - 1 ;
}
if ( timestamp ! = 0 ) {
errno = EROFS ;
return - 1 ;
}
return SMB_VFS_NEXT_FCHFLAGS ( handle , fsp , flags ) ;
}
2021-03-10 12:24:04 +03:00
static int snapper_gmt_fsetxattr ( struct vfs_handle_struct * handle ,
struct files_struct * fsp ,
const char * aname , const void * value ,
size_t size , int flags )
{
time_t timestamp = 0 ;
const struct smb_filename * smb_fname = NULL ;
smb_fname = fsp - > fsp_name ;
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) ,
handle ,
smb_fname ,
& timestamp ,
NULL ) ) {
return - 1 ;
}
if ( timestamp ! = 0 ) {
errno = EROFS ;
return - 1 ;
}
return SMB_VFS_NEXT_FSETXATTR ( handle , fsp ,
aname , value , size , flags ) ;
}
2022-03-07 20:00:20 +03:00
static NTSTATUS snapper_gmt_get_real_filename (
struct vfs_handle_struct * handle ,
const struct smb_filename * fpath ,
const char * name ,
TALLOC_CTX * mem_ctx ,
char * * found_name )
2014-06-27 17:31:36 +04:00
{
time_t timestamp ;
char * stripped ;
char * conv ;
2020-04-30 17:40:28 +03:00
struct smb_filename conv_fname ;
2022-03-07 20:00:20 +03:00
NTSTATUS status ;
2014-06-27 17:31:36 +04:00
2020-05-02 12:39:58 +03:00
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) , handle , fpath ,
2014-06-27 17:31:36 +04:00
& timestamp , & stripped ) ) {
2022-03-07 20:00:20 +03:00
return NT_STATUS_NO_MEMORY ;
2014-06-27 17:31:36 +04:00
}
if ( timestamp = = 0 ) {
2020-04-30 17:40:28 +03:00
return SMB_VFS_NEXT_GET_REAL_FILENAME ( handle , fpath , name ,
2014-06-27 17:31:36 +04:00
mem_ctx , found_name ) ;
}
if ( stripped [ 0 ] = = ' \0 ' ) {
* found_name = talloc_strdup ( mem_ctx , name ) ;
if ( * found_name = = NULL ) {
2022-03-07 20:00:20 +03:00
return NT_STATUS_NO_MEMORY ;
2014-06-27 17:31:36 +04:00
}
2022-03-07 20:00:20 +03:00
return NT_STATUS_OK ;
2014-06-27 17:31:36 +04:00
}
conv = snapper_gmt_convert ( talloc_tos ( ) , handle , stripped , timestamp ) ;
TALLOC_FREE ( stripped ) ;
if ( conv = = NULL ) {
2022-03-07 20:00:20 +03:00
return map_nt_error_from_unix ( errno ) ;
2014-06-27 17:31:36 +04:00
}
2020-04-30 17:40:28 +03:00
conv_fname = ( struct smb_filename ) {
. base_name = conv ,
} ;
2022-03-07 20:00:20 +03:00
status = SMB_VFS_NEXT_GET_REAL_FILENAME (
handle , & conv_fname , name , mem_ctx , found_name ) ;
2014-06-27 17:31:36 +04:00
TALLOC_FREE ( conv ) ;
2022-03-07 20:00:20 +03:00
return status ;
2014-06-27 17:31:36 +04:00
}
static uint64_t snapper_gmt_disk_free ( vfs_handle_struct * handle ,
2017-05-23 20:40:47 +03:00
const struct smb_filename * smb_fname ,
uint64_t * bsize ,
uint64_t * dfree ,
uint64_t * dsize )
2014-06-27 17:31:36 +04:00
{
2017-05-23 20:40:47 +03:00
time_t timestamp = 0 ;
char * stripped = NULL ;
uint64_t ret ;
int saved_errno = 0 ;
char * conv = NULL ;
struct smb_filename * conv_smb_fname = NULL ;
2014-06-27 17:31:36 +04:00
2017-05-23 20:40:47 +03:00
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) , handle ,
2020-05-02 12:39:58 +03:00
smb_fname , & timestamp , & stripped ) ) {
2017-05-23 20:40:47 +03:00
return ( uint64_t ) - 1 ;
2014-06-27 17:31:36 +04:00
}
if ( timestamp = = 0 ) {
2017-05-23 20:40:47 +03:00
return SMB_VFS_NEXT_DISK_FREE ( handle , smb_fname ,
2014-06-27 17:31:36 +04:00
bsize , dfree , dsize ) ;
}
conv = snapper_gmt_convert ( talloc_tos ( ) , handle , stripped , timestamp ) ;
TALLOC_FREE ( stripped ) ;
if ( conv = = NULL ) {
2017-05-23 20:40:47 +03:00
return ( uint64_t ) - 1 ;
}
conv_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
conv ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
0 ,
2017-05-23 20:40:47 +03:00
smb_fname - > flags ) ;
if ( conv_smb_fname = = NULL ) {
TALLOC_FREE ( conv ) ;
errno = ENOMEM ;
return ( uint64_t ) - 1 ;
2014-06-27 17:31:36 +04:00
}
2017-05-23 20:40:47 +03:00
ret = SMB_VFS_NEXT_DISK_FREE ( handle , conv_smb_fname ,
bsize , dfree , dsize ) ;
2014-06-27 17:31:36 +04:00
2017-05-23 20:40:47 +03:00
if ( ret = = ( uint64_t ) - 1 ) {
saved_errno = errno ;
}
TALLOC_FREE ( conv_smb_fname ) ;
if ( saved_errno ! = 0 ) {
errno = saved_errno ;
}
2014-06-27 17:31:36 +04:00
return ret ;
}
2017-06-01 21:45:25 +03:00
static int snapper_gmt_get_quota ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
enum SMB_QUOTA_TYPE qtype ,
unid_t id ,
SMB_DISK_QUOTA * dq )
2016-01-10 15:14:58 +03:00
{
2017-06-01 21:45:25 +03:00
time_t timestamp = 0 ;
char * stripped = NULL ;
2016-01-10 15:14:58 +03:00
int ret ;
2017-06-01 21:45:25 +03:00
int saved_errno = 0 ;
char * conv = NULL ;
struct smb_filename * conv_smb_fname = NULL ;
2016-01-10 15:14:58 +03:00
2017-06-01 21:45:25 +03:00
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) , handle ,
2020-05-02 12:39:58 +03:00
smb_fname , & timestamp , & stripped ) ) {
2016-01-10 15:14:58 +03:00
return - 1 ;
}
if ( timestamp = = 0 ) {
2017-06-01 21:45:25 +03:00
return SMB_VFS_NEXT_GET_QUOTA ( handle , smb_fname , qtype , id , dq ) ;
2016-01-10 15:14:58 +03:00
}
conv = snapper_gmt_convert ( talloc_tos ( ) , handle , stripped , timestamp ) ;
TALLOC_FREE ( stripped ) ;
if ( conv = = NULL ) {
return - 1 ;
}
2017-06-01 21:45:25 +03:00
conv_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
conv ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
0 ,
2017-06-01 21:45:25 +03:00
smb_fname - > flags ) ;
2016-01-10 15:14:58 +03:00
TALLOC_FREE ( conv ) ;
2017-06-01 21:45:25 +03:00
if ( conv_smb_fname = = NULL ) {
errno = ENOMEM ;
return - 1 ;
}
2016-01-10 15:14:58 +03:00
2017-06-01 21:45:25 +03:00
ret = SMB_VFS_NEXT_GET_QUOTA ( handle , conv_smb_fname , qtype , id , dq ) ;
if ( ret = = - 1 ) {
saved_errno = errno ;
}
TALLOC_FREE ( conv_smb_fname ) ;
if ( saved_errno ! = 0 ) {
errno = saved_errno ;
}
2016-01-10 15:14:58 +03:00
return ret ;
}
2020-01-10 00:13:29 +03:00
static NTSTATUS snapper_create_dfs_pathat ( struct vfs_handle_struct * handle ,
struct files_struct * dirfsp ,
const struct smb_filename * smb_fname ,
const struct referral * reflist ,
size_t referral_count )
{
time_t timestamp = 0 ;
if ( ! snapper_gmt_strip_snapshot ( talloc_tos ( ) ,
handle ,
2020-05-02 12:39:58 +03:00
smb_fname ,
2020-01-10 00:13:29 +03:00
& timestamp ,
NULL ) ) {
return NT_STATUS_NO_MEMORY ;
}
if ( timestamp ! = 0 ) {
return NT_STATUS_MEDIA_WRITE_PROTECTED ;
}
return SMB_VFS_NEXT_CREATE_DFS_PATHAT ( handle ,
dirfsp ,
smb_fname ,
reflist ,
referral_count ) ;
}
2014-06-27 17:31:36 +04:00
2014-06-27 17:31:35 +04:00
static struct vfs_fn_pointers snapper_fns = {
2012-10-14 21:54:24 +04:00
. snap_check_path_fn = snapper_snap_check_path ,
. snap_create_fn = snapper_snap_create ,
. snap_delete_fn = snapper_snap_delete ,
2014-06-27 17:31:36 +04:00
. get_shadow_copy_data_fn = snapper_get_shadow_copy_data ,
2020-01-10 00:13:29 +03:00
. create_dfs_pathat_fn = snapper_create_dfs_pathat ,
2014-06-27 17:31:36 +04:00
. disk_free_fn = snapper_gmt_disk_free ,
2016-01-10 15:14:58 +03:00
. get_quota_fn = snapper_gmt_get_quota ,
2019-08-10 01:28:34 +03:00
. renameat_fn = snapper_gmt_renameat ,
2019-08-17 02:01:22 +03:00
. linkat_fn = snapper_gmt_linkat ,
2019-08-30 23:54:52 +03:00
. symlinkat_fn = snapper_gmt_symlinkat ,
2014-06-27 17:31:36 +04:00
. stat_fn = snapper_gmt_stat ,
. lstat_fn = snapper_gmt_lstat ,
2020-05-20 23:02:48 +03:00
. openat_fn = snapper_gmt_openat ,
2019-09-12 23:21:41 +03:00
. unlinkat_fn = snapper_gmt_unlinkat ,
2021-04-09 16:57:08 +03:00
. fchmod_fn = snapper_gmt_fchmod ,
2014-06-27 17:31:36 +04:00
. chdir_fn = snapper_gmt_chdir ,
2021-04-13 14:39:09 +03:00
. fntimes_fn = snapper_gmt_fntimes ,
2019-08-23 00:28:26 +03:00
. readlinkat_fn = snapper_gmt_readlinkat ,
2019-08-21 21:03:41 +03:00
. mknodat_fn = snapper_gmt_mknodat ,
2014-06-27 17:31:36 +04:00
. realpath_fn = snapper_gmt_realpath ,
2019-09-05 20:33:38 +03:00
. mkdirat_fn = snapper_gmt_mkdirat ,
2018-03-13 10:14:53 +03:00
. getxattrat_send_fn = vfs_not_implemented_getxattrat_send ,
. getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv ,
2021-03-10 12:24:04 +03:00
. fsetxattr_fn = snapper_gmt_fsetxattr ,
2021-06-11 17:46:56 +03:00
. fchflags_fn = snapper_gmt_fchflags ,
2014-06-27 17:31:36 +04:00
. get_real_filename_fn = snapper_gmt_get_real_filename ,
2014-06-27 17:31:35 +04:00
} ;
2017-12-16 01:32:12 +03:00
static_decl_vfs ;
2017-04-20 22:24:43 +03:00
NTSTATUS vfs_snapper_init ( TALLOC_CTX * ctx )
2014-06-27 17:31:35 +04:00
{
return smb_register_vfs ( SMB_VFS_INTERFACE_VERSION ,
" snapper " , & snapper_fns ) ;
}