2013-11-27 19:19:49 +04:00
/*
* Copyright ( C ) 2013 Red Hat , Inc .
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
* This library 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
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library . If not , see
* < http : //www.gnu.org/licenses/>.
*/
# include <config.h>
2016-06-16 11:42:47 +03:00
# include <fcntl.h>
2020-02-17 00:59:28 +03:00
# include <unistd.h>
2013-11-27 19:19:49 +04:00
# include "testutils.h"
# include "virfile.h"
# include "virstring.h"
2017-05-18 16:38:04 +03:00
# ifdef __linux__
# include <linux / falloc.h>
# endif
2018-10-09 14:06:00 +03:00
# define VIR_FROM_THIS VIR_FROM_NONE
2013-11-27 19:19:49 +04:00
2020-09-01 14:27:44 +03:00
# if defined WITH_MNTENT_H && defined WITH_GETMNTENT_R
2013-11-27 19:19:49 +04:00
static int testFileCheckMounts ( const char * prefix ,
char * * gotmounts ,
size_t gotnmounts ,
const char * const * wantmounts ,
size_t wantnmounts )
{
size_t i ;
if ( gotnmounts ! = wantnmounts ) {
fprintf ( stderr , " Expected %zu mounts under %s, but got %zu \n " ,
wantnmounts , prefix , gotnmounts ) ;
return - 1 ;
}
for ( i = 0 ; i < gotnmounts ; i + + ) {
if ( STRNEQ ( gotmounts [ i ] , wantmounts [ i ] ) ) {
fprintf ( stderr , " Expected mount[%zu] '%s' but got '%s' \n " ,
i , wantmounts [ i ] , gotmounts [ i ] ) ;
return - 1 ;
}
}
return 0 ;
}
struct testFileGetMountSubtreeData {
const char * path ;
const char * prefix ;
const char * const * mounts ;
size_t nmounts ;
bool rev ;
} ;
static int testFileGetMountSubtree ( const void * opaque )
{
int ret = - 1 ;
char * * gotmounts = NULL ;
size_t gotnmounts = 0 ;
const struct testFileGetMountSubtreeData * data = opaque ;
if ( data - > rev ) {
if ( virFileGetMountReverseSubtree ( data - > path ,
data - > prefix ,
& gotmounts ,
& gotnmounts ) < 0 )
goto cleanup ;
} else {
if ( virFileGetMountSubtree ( data - > path ,
data - > prefix ,
& gotmounts ,
& gotnmounts ) < 0 )
goto cleanup ;
}
ret = testFileCheckMounts ( data - > prefix ,
gotmounts , gotnmounts ,
data - > mounts , data - > nmounts ) ;
cleanup :
2020-08-02 20:36:03 +03:00
g_strfreev ( gotmounts ) ;
2013-11-27 19:19:49 +04:00
return ret ;
}
2020-09-01 14:27:44 +03:00
# endif /* ! defined WITH_MNTENT_H && defined WITH_GETMNTENT_R */
2013-11-27 19:19:49 +04:00
2015-04-08 11:27:30 +03:00
struct testFileSanitizePathData
{
const char * path ;
const char * expect ;
} ;
static int
testFileSanitizePath ( const void * opaque )
{
const struct testFileSanitizePathData * data = opaque ;
int ret = - 1 ;
char * actual ;
if ( ! ( actual = virFileSanitizePath ( data - > path ) ) )
return - 1 ;
if ( STRNEQ ( actual , data - > expect ) ) {
fprintf ( stderr , " \n expect: '%s' \n actual: '%s' \n " , data - > expect , actual ) ;
goto cleanup ;
}
ret = 0 ;
cleanup :
VIR_FREE ( actual ) ;
return ret ;
}
2020-09-01 14:27:44 +03:00
# if WITH_DECL_SEEK_HOLE && defined(__linux__)
2016-06-16 11:42:47 +03:00
/* Create a sparse file. @offsets in KiB. */
static int
makeSparseFile ( const off_t offsets [ ] ,
const bool startData )
{
int fd = - 1 ;
char path [ ] = abs_builddir " fileInData.XXXXXX " ;
off_t len = 0 ;
size_t i ;
2019-11-18 19:45:54 +03:00
if ( ( fd = g_mkstemp_full ( path , O_RDWR | O_CLOEXEC , S_IRUSR | S_IWUSR ) ) < 0 )
2016-06-16 11:42:47 +03:00
goto error ;
if ( unlink ( path ) < 0 )
goto error ;
for ( i = 0 ; offsets [ i ] ! = ( off_t ) - 1 ; i + + )
len + = offsets [ i ] * 1024 ;
while ( len ) {
const char buf [ ] = " abcdefghijklmnopqrstuvwxyz " ;
off_t toWrite = sizeof ( buf ) ;
if ( toWrite > len )
toWrite = len ;
if ( safewrite ( fd , buf , toWrite ) < 0 ) {
fprintf ( stderr , " unable to write to %s (errno=%d) \n " , path , errno ) ;
goto error ;
}
len - = toWrite ;
}
len = 0 ;
for ( i = 0 ; offsets [ i ] ! = ( off_t ) - 1 ; i + + ) {
bool inData = startData ;
if ( i % 2 )
inData = ! inData ;
if ( ! inData & &
fallocate ( fd ,
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE ,
len , offsets [ i ] * 1024 ) < 0 ) {
fprintf ( stderr , " unable to punch a hole at offset %lld length %lld \n " ,
( long long ) len , ( long long ) offsets [ i ] ) ;
goto error ;
}
len + = offsets [ i ] * 1024 ;
}
if ( lseek ( fd , 0 , SEEK_SET ) = = ( off_t ) - 1 ) {
fprintf ( stderr , " unable to lseek (errno=%d) \n " , errno ) ;
goto error ;
}
return fd ;
error :
VIR_FORCE_CLOSE ( fd ) ;
return - 1 ;
}
2017-05-18 15:36:41 +03:00
# define EXTENT 4
2016-06-16 11:42:47 +03:00
static bool
holesSupported ( void )
{
off_t offsets [ ] = { EXTENT , EXTENT , EXTENT , - 1 } ;
off_t tmp ;
int fd ;
bool ret = false ;
if ( ( fd = makeSparseFile ( offsets , true ) ) < 0 )
goto cleanup ;
/* The way this works is: there are 4K of data followed by 4K hole followed
* by 4 K hole again . Check if the filesystem we are running the test suite
* on supports holes . */
if ( ( tmp = lseek ( fd , 0 , SEEK_DATA ) ) = = ( off_t ) - 1 )
goto cleanup ;
if ( tmp ! = 0 )
goto cleanup ;
if ( ( tmp = lseek ( fd , tmp , SEEK_HOLE ) ) = = ( off_t ) - 1 )
goto cleanup ;
if ( tmp ! = EXTENT * 1024 )
goto cleanup ;
if ( ( tmp = lseek ( fd , tmp , SEEK_DATA ) ) = = ( off_t ) - 1 )
goto cleanup ;
if ( tmp ! = 2 * EXTENT * 1024 )
goto cleanup ;
if ( ( tmp = lseek ( fd , tmp , SEEK_HOLE ) ) = = ( off_t ) - 1 )
goto cleanup ;
if ( tmp ! = 3 * EXTENT * 1024 )
goto cleanup ;
ret = true ;
cleanup :
VIR_FORCE_CLOSE ( fd ) ;
return ret ;
}
2020-09-01 14:27:44 +03:00
# else /* !WITH_DECL_SEEK_HOLE || !defined(__linux__)*/
2017-05-18 15:36:41 +03:00
static int
2019-10-14 15:45:03 +03:00
makeSparseFile ( const off_t offsets [ ] G_GNUC_UNUSED ,
const bool startData G_GNUC_UNUSED )
2017-05-18 15:36:41 +03:00
{
return - 1 ;
}
static bool
holesSupported ( void )
{
return false ;
}
2020-09-01 14:27:44 +03:00
# endif /* !WITH_DECL_SEEK_HOLE || !defined(__linux__)*/
2016-06-16 11:42:47 +03:00
struct testFileInData {
bool startData ; /* whether the list of offsets starts with data section */
off_t * offsets ;
} ;
static int
testFileInData ( const void * opaque )
{
const struct testFileInData * data = opaque ;
int fd = - 1 ;
int ret = - 1 ;
size_t i ;
if ( ( fd = makeSparseFile ( data - > offsets , data - > startData ) ) < 0 )
goto cleanup ;
for ( i = 0 ; data - > offsets [ i ] ! = ( off_t ) - 1 ; i + + ) {
bool shouldInData = data - > startData ;
int realInData ;
long long shouldLen ;
long long realLen ;
if ( i % 2 )
shouldInData = ! shouldInData ;
if ( virFileInData ( fd , & realInData , & realLen ) < 0 )
goto cleanup ;
if ( realInData ! = shouldInData ) {
fprintf ( stderr , " Unexpected data/hole. Expected %s got %s \n " ,
shouldInData ? " data " : " hole " ,
realInData ? " data " : " hole " ) ;
goto cleanup ;
}
shouldLen = data - > offsets [ i ] * 1024 ;
if ( realLen ! = shouldLen ) {
fprintf ( stderr , " Unexpected section length. Expected %lld got %lld \n " ,
shouldLen , realLen ) ;
goto cleanup ;
}
if ( lseek ( fd , shouldLen , SEEK_CUR ) < 0 ) {
fprintf ( stderr , " Unable to seek \n " ) ;
goto cleanup ;
}
}
ret = 0 ;
cleanup :
VIR_FORCE_CLOSE ( fd ) ;
return ret ;
}
2018-10-09 14:06:00 +03:00
struct testFileIsSharedFSType {
const char * mtabFile ;
const char * filename ;
const bool expected ;
} ;
static int
2019-10-14 15:45:03 +03:00
testFileIsSharedFSType ( const void * opaque G_GNUC_UNUSED )
2018-10-09 14:06:00 +03:00
{
# ifndef __linux__
return EXIT_AM_SKIP ;
# else
const struct testFileIsSharedFSType * data = opaque ;
char * mtabFile = NULL ;
bool actual ;
int ret = - 1 ;
2019-10-22 16:26:14 +03:00
mtabFile = g_strdup_printf ( abs_srcdir " /virfiledata/%s " , data - > mtabFile ) ;
2018-10-09 14:06:00 +03:00
2019-12-18 20:16:19 +03:00
if ( g_setenv ( " LIBVIRT_MTAB " , mtabFile , TRUE ) = = FALSE ) {
2018-10-09 14:06:00 +03:00
fprintf ( stderr , " Unable to set env variable \n " ) ;
goto cleanup ;
}
actual = virFileIsSharedFS ( data - > filename ) ;
if ( actual ! = data - > expected ) {
fprintf ( stderr , " Unexpected FS type. Expected %d got %d \n " ,
data - > expected , actual ) ;
goto cleanup ;
}
ret = 0 ;
cleanup :
VIR_FREE ( mtabFile ) ;
2019-12-18 20:16:19 +03:00
g_unsetenv ( " LIBVIRT_MTAB " ) ;
2018-10-09 14:06:00 +03:00
return ret ;
# endif
}
2013-11-27 19:19:49 +04:00
static int
mymain ( void )
{
int ret = 0 ;
2015-04-08 11:27:30 +03:00
struct testFileSanitizePathData data1 ;
2013-11-27 19:19:49 +04:00
2020-09-01 14:27:44 +03:00
# if defined WITH_MNTENT_H && defined WITH_GETMNTENT_R
2013-11-27 19:19:49 +04:00
# define MTAB_PATH1 abs_srcdir " / virfiledata / mounts1.txt"
# define MTAB_PATH2 abs_srcdir " / virfiledata / mounts2.txt"
static const char * wantmounts1 [ ] = {
" /proc " , " /proc/sys/fs/binfmt_misc " , " /proc/sys/fs/binfmt_misc " ,
} ;
static const char * wantmounts1rev [ ] = {
" /proc/sys/fs/binfmt_misc " , " /proc/sys/fs/binfmt_misc " , " /proc "
} ;
2013-11-27 19:21:41 +04:00
static const char * wantmounts2a [ ] = {
" /etc/aliases "
} ;
static const char * wantmounts2b [ ] = {
" /etc/aliases.db "
} ;
2013-11-27 19:19:49 +04:00
2017-11-03 15:09:47 +03:00
# define DO_TEST_MOUNT_SUBTREE(name, path, prefix, mounts, rev) \
do { \
struct testFileGetMountSubtreeData data = { \
2019-10-15 14:55:26 +03:00
path , prefix , mounts , G_N_ELEMENTS ( mounts ) , rev \
2017-11-03 15:09:47 +03:00
} ; \
if ( virTestRun ( name , testFileGetMountSubtree , & data ) < 0 ) \
ret = - 1 ; \
2013-11-27 19:19:49 +04:00
} while ( 0 )
DO_TEST_MOUNT_SUBTREE ( " /proc normal " , MTAB_PATH1 , " /proc " , wantmounts1 , false ) ;
DO_TEST_MOUNT_SUBTREE ( " /proc reverse " , MTAB_PATH1 , " /proc " , wantmounts1rev , true ) ;
2013-11-27 19:21:41 +04:00
DO_TEST_MOUNT_SUBTREE ( " /etc/aliases " , MTAB_PATH2 , " /etc/aliases " , wantmounts2a , false ) ;
DO_TEST_MOUNT_SUBTREE ( " /etc/aliases.db " , MTAB_PATH2 , " /etc/aliases.db " , wantmounts2b , false ) ;
2020-09-01 14:27:44 +03:00
# endif /* ! defined WITH_MNTENT_H && defined WITH_GETMNTENT_R */
2013-11-27 19:19:49 +04:00
2017-11-03 15:09:47 +03:00
# define DO_TEST_SANITIZE_PATH(PATH, EXPECT) \
do { \
data1 . path = PATH ; \
data1 . expect = EXPECT ; \
if ( virTestRun ( virTestCounterNext ( ) , testFileSanitizePath , \
& data1 ) < 0 ) \
ret = - 1 ; \
2015-04-08 11:27:30 +03:00
} while ( 0 )
2015-04-08 11:57:07 +03:00
# define DO_TEST_SANITIZE_PATH_SAME(PATH) DO_TEST_SANITIZE_PATH(PATH, PATH)
2016-05-26 18:01:57 +03:00
virTestCounterReset ( " testFileSanitizePath " ) ;
2015-04-08 11:27:30 +03:00
DO_TEST_SANITIZE_PATH ( " " , " " ) ;
DO_TEST_SANITIZE_PATH ( " / " , " / " ) ;
DO_TEST_SANITIZE_PATH ( " /path " , " /path " ) ;
DO_TEST_SANITIZE_PATH ( " /path/to/blah " , " /path/to/blah " ) ;
DO_TEST_SANITIZE_PATH ( " /path/ " , " /path " ) ;
DO_TEST_SANITIZE_PATH ( " /////// " , " / " ) ;
DO_TEST_SANITIZE_PATH ( " // " , " // " ) ;
DO_TEST_SANITIZE_PATH ( " . " , " . " ) ;
DO_TEST_SANITIZE_PATH ( " ../ " , " .. " ) ;
DO_TEST_SANITIZE_PATH ( " ../../ " , " ../.. " ) ;
DO_TEST_SANITIZE_PATH ( " //foo//bar " , " //foo/bar " ) ;
DO_TEST_SANITIZE_PATH ( " /bar//foo " , " /bar/foo " ) ;
2015-04-08 11:57:07 +03:00
DO_TEST_SANITIZE_PATH_SAME ( " gluster://bar.baz/foo/hoo " ) ;
DO_TEST_SANITIZE_PATH_SAME ( " gluster://bar.baz//fooo/hoo " ) ;
DO_TEST_SANITIZE_PATH_SAME ( " gluster://bar.baz//////fooo/hoo " ) ;
DO_TEST_SANITIZE_PATH_SAME ( " gluster://bar.baz/fooo//hoo " ) ;
DO_TEST_SANITIZE_PATH_SAME ( " gluster://bar.baz/fooo///////hoo " ) ;
2015-04-08 11:27:30 +03:00
2017-11-03 15:09:47 +03:00
# define DO_TEST_IN_DATA(inData, ...) \
do { \
off_t offsets [ ] = { __VA_ARGS__ , - 1 } ; \
struct testFileInData data = { \
. startData = inData , . offsets = offsets , \
} ; \
if ( virTestRun ( virTestCounterNext ( ) , testFileInData , & data ) < 0 ) \
ret = - 1 ; \
2016-06-16 11:42:47 +03:00
} while ( 0 )
if ( holesSupported ( ) ) {
2018-10-09 15:50:53 +03:00
virTestCounterReset ( " testFileInData " ) ;
2016-06-16 11:42:47 +03:00
DO_TEST_IN_DATA ( true , 4 , 4 , 4 ) ;
DO_TEST_IN_DATA ( false , 4 , 4 , 4 ) ;
DO_TEST_IN_DATA ( true , 8 , 8 , 8 ) ;
DO_TEST_IN_DATA ( false , 8 , 8 , 8 ) ;
DO_TEST_IN_DATA ( true , 8 , 16 , 32 , 64 , 128 , 256 , 512 ) ;
DO_TEST_IN_DATA ( false , 8 , 16 , 32 , 64 , 128 , 256 , 512 ) ;
}
2018-10-09 14:06:00 +03:00
# define DO_TEST_FILE_IS_SHARED_FS_TYPE(mtab, file, exp) \
do { \
struct testFileIsSharedFSType data = { \
. mtabFile = mtab , . filename = file , . expected = exp \
} ; \
if ( virTestRun ( virTestCounterNext ( ) , testFileIsSharedFSType , & data ) < 0 ) \
ret = - 1 ; \
} while ( 0 )
virTestCounterReset ( " testFileIsSharedFSType " ) ;
DO_TEST_FILE_IS_SHARED_FS_TYPE ( " mounts1.txt " , " /boot/vmlinuz " , false ) ;
DO_TEST_FILE_IS_SHARED_FS_TYPE ( " mounts2.txt " , " /run/user/501/gvfs/some/file " , false ) ;
DO_TEST_FILE_IS_SHARED_FS_TYPE ( " mounts3.txt " , " /nfs/file " , true ) ;
2018-10-09 16:23:10 +03:00
DO_TEST_FILE_IS_SHARED_FS_TYPE ( " mounts3.txt " , " /nfs/blah " , false ) ;
2018-10-09 14:08:07 +03:00
DO_TEST_FILE_IS_SHARED_FS_TYPE ( " mounts3.txt " , " /gluster/file " , true ) ;
DO_TEST_FILE_IS_SHARED_FS_TYPE ( " mounts3.txt " , " /gluster/sshfs/file " , false ) ;
2018-10-18 15:57:19 +03:00
DO_TEST_FILE_IS_SHARED_FS_TYPE ( " mounts3.txt " , " /some/symlink/file " , true ) ;
2019-01-24 11:52:42 +03:00
DO_TEST_FILE_IS_SHARED_FS_TYPE ( " mounts3.txt " , " /ceph/file " , true ) ;
DO_TEST_FILE_IS_SHARED_FS_TYPE ( " mounts3.txt " , " /ceph/multi/file " , true ) ;
2019-02-25 21:19:03 +03:00
DO_TEST_FILE_IS_SHARED_FS_TYPE ( " mounts3.txt " , " /gpfs/data " , true ) ;
2019-05-28 15:55:29 +03:00
DO_TEST_FILE_IS_SHARED_FS_TYPE ( " mounts3.txt " , " /quobyte " , true ) ;
2018-10-09 14:06:00 +03:00
2013-11-27 19:19:49 +04:00
return ret ! = 0 ? EXIT_FAILURE : EXIT_SUCCESS ;
}
2018-10-11 11:36:02 +03:00
# ifdef __linux__
2019-08-21 19:13:16 +03:00
VIR_TEST_MAIN_PRELOAD ( mymain , VIR_TEST_MOCK ( " virfile " ) )
2018-10-11 11:36:02 +03:00
# else
VIR_TEST_MAIN ( mymain )
# endif