2022-11-10 20:31:11 +03:00
# Unix SMB/CIFS implementation.
# Copyright Volker Lendecke <vl@samba.org> 2022
#
# 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/>.
#
from samba . samba3 import libsmb_samba_internal as libsmb
from samba import ( ntstatus , NTSTATUSError )
from samba . dcerpc import security as sec
from samba import reparse_symlink
import samba . tests . libsmb
class ReparsePoints ( samba . tests . libsmb . LibsmbTests ) :
def connection ( self ) :
share = samba . tests . env_get_var_value ( " SHARENAME " )
smb1 = samba . tests . env_get_var_value ( " SMB1 " , allow_missing = True )
conn = libsmb . Conn (
self . server_ip ,
share ,
self . lp ,
self . creds ,
force_smb1 = smb1 )
return conn
def clean_file ( self , conn , filename ) :
try :
conn . unlink ( filename )
except NTSTATUSError as e :
err = e . args [ 0 ]
ok = ( err == ntstatus . NT_STATUS_OBJECT_NAME_NOT_FOUND )
ok | = ( err == ntstatus . NT_STATUS_OBJECT_PATH_NOT_FOUND )
2022-12-01 16:48:46 +03:00
ok | = ( err == ntstatus . NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED )
2022-11-10 20:31:11 +03:00
if not ok :
raise
def test_error_not_a_reparse_point ( self ) :
conn = self . connection ( )
filename = ' reparse '
self . clean_file ( conn , filename )
fd = conn . create (
filename ,
DesiredAccess = sec . SEC_FILE_WRITE_ATTRIBUTE ,
CreateDisposition = libsmb . FILE_CREATE )
with self . assertRaises ( NTSTATUSError ) as e :
conn . fsctl ( fd , libsmb . FSCTL_GET_REPARSE_POINT , b ' ' , 1024 )
self . assertEqual ( e . args [ 0 ] , ntstatus . NT_STATUS_NOT_A_REPARSE_POINT )
conn . close ( fd )
self . clean_file ( conn , filename )
def test_create_reparse ( self ) :
conn = self . connection ( )
filename = ' reparse '
self . clean_file ( conn , filename )
fd = conn . create (
filename ,
DesiredAccess = sec . SEC_FILE_WRITE_ATTRIBUTE ,
CreateDisposition = libsmb . FILE_CREATE )
b = reparse_symlink . put ( 0x80000025 , 0 , b ' asdfasdfasdfasdfasdfasdf ' )
conn . fsctl ( fd , libsmb . FSCTL_SET_REPARSE_POINT , b , 0 )
b = reparse_symlink . put ( 0x80000026 , 0 , b ' asdfasdfasdfasdfasdfasdf ' )
conn . fsctl ( fd , libsmb . FSCTL_SET_REPARSE_POINT , b , 0 )
# Show that directories can carry reparse points
def test_create_reparse_directory ( self ) :
conn = self . connection ( )
dirname = " reparse_dir "
self . clean_file ( conn , dirname )
fd = conn . create (
dirname ,
DesiredAccess = sec . SEC_FILE_WRITE_ATTRIBUTE |
sec . SEC_STD_DELETE ,
CreateDisposition = libsmb . FILE_CREATE ,
CreateOptions = libsmb . FILE_DIRECTORY_FILE )
b = reparse_symlink . put ( 0x80000025 , 0 , b ' asdfasdfasdfasdfasdfasdf ' )
conn . fsctl ( fd , libsmb . FSCTL_SET_REPARSE_POINT , b , 0 )
conn . delete_on_close ( fd , 1 )
# Only empty directories can carry reparse points
def test_create_reparse_nonempty_directory ( self ) :
conn = self . connection ( )
dirname = " reparse_dir "
filename = f ' { dirname } \\ file.txt '
self . clean_file ( conn , filename )
self . clean_file ( conn , dirname )
dir_fd = conn . create (
dirname ,
DesiredAccess = sec . SEC_FILE_WRITE_ATTRIBUTE |
sec . SEC_STD_DELETE ,
CreateDisposition = libsmb . FILE_CREATE ,
CreateOptions = libsmb . FILE_DIRECTORY_FILE )
fd = conn . create (
filename ,
DesiredAccess = sec . SEC_FILE_WRITE_ATTRIBUTE |
sec . SEC_STD_DELETE ,
CreateDisposition = libsmb . FILE_CREATE )
b = reparse_symlink . put ( 0x80000025 , 0 , b ' asdf ' )
try :
conn . fsctl ( dir_fd , libsmb . FSCTL_SET_REPARSE_POINT , b , 0 )
except NTSTATUSError as e :
err = e . args [ 0 ]
ok = ( err == ntstatus . NT_STATUS_DIRECTORY_NOT_EMPTY )
if not ok :
raise
conn . delete_on_close ( fd , 1 )
conn . close ( fd )
conn . delete_on_close ( dir_fd , 1 )
conn . close ( dir_fd )
# Show that reparse point opens respect share modes
def test_reparse_share_modes ( self ) :
conn = self . connection ( )
filename = ' reparse '
self . clean_file ( conn , filename )
fd = conn . create (
filename ,
DesiredAccess = sec . SEC_FILE_WRITE_ATTRIBUTE ,
CreateDisposition = libsmb . FILE_CREATE )
b = reparse_symlink . put ( 0x80000025 , 0 , b ' asdfasdfasdfasdfasdfasdf ' )
conn . fsctl ( fd , libsmb . FSCTL_SET_REPARSE_POINT , b , 0 )
conn . close ( fd ) ;
fd1 = conn . create (
filename ,
DesiredAccess = sec . SEC_FILE_READ_DATA | sec . SEC_STD_DELETE ,
CreateDisposition = libsmb . FILE_OPEN ,
CreateOptions = libsmb . FILE_OPEN_REPARSE_POINT )
with self . assertRaises ( NTSTATUSError ) as e :
fd2 = conn . create (
filename ,
DesiredAccess = sec . SEC_FILE_READ_DATA ,
CreateDisposition = libsmb . FILE_OPEN ,
CreateOptions = libsmb . FILE_OPEN_REPARSE_POINT )
self . assertEqual ( e . args [ 0 ] , ntstatus . NT_STATUS_SHARING_VIOLATION )
conn . delete_on_close ( fd1 , 1 ) ;
conn . close ( fd1 )
if __name__ == ' __main__ ' :
import unittest
unittest . main ( )