2017-11-18 19:09:20 +03:00
/* SPDX-License-Identifier: LGPL-2.1+ */
2016-11-22 22:19:08 +03:00
/***
This file is part of systemd .
Copyright 2016 Lennart Poettering
systemd 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 .
systemd 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 systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <sys/mount.h>
2017-11-20 18:05:41 +03:00
# include "alloc-util.h"
# include "def.h"
# include "fd-util.h"
# include "fileio.h"
# include "hashmap.h"
2016-11-22 22:19:08 +03:00
# include "log.h"
2017-10-31 12:52:30 +03:00
# include "log.h"
2016-11-22 22:19:08 +03:00
# include "mount-util.h"
2017-11-23 14:48:42 +03:00
# include "path-util.h"
2017-10-31 12:52:30 +03:00
# include "rm-rf.h"
2016-11-22 22:19:08 +03:00
# include "string-util.h"
2016-12-03 21:57:42 +03:00
static void test_mount_propagation_flags ( const char * name , int ret , unsigned long expected ) {
long unsigned flags ;
2016-11-22 22:19:08 +03:00
2017-11-07 16:04:20 +03:00
assert_se ( mount_propagation_flags_from_string ( name , & flags ) = = ret ) ;
2016-12-03 21:57:42 +03:00
if ( ret > = 0 ) {
const char * c ;
assert_se ( flags = = expected ) ;
c = mount_propagation_flags_to_string ( flags ) ;
if ( isempty ( name ) )
assert_se ( isempty ( c ) ) ;
else
assert_se ( streq ( c , name ) ) ;
}
2016-11-22 22:19:08 +03:00
}
2017-11-20 18:05:41 +03:00
static void test_mnt_id ( void ) {
_cleanup_fclose_ FILE * f = NULL ;
Hashmap * h ;
Iterator i ;
2017-11-23 14:48:42 +03:00
char * p ;
void * k ;
2017-11-20 18:05:41 +03:00
int r ;
assert_se ( f = fopen ( " /proc/self/mountinfo " , " re " ) ) ;
2017-11-23 14:48:42 +03:00
assert_se ( h = hashmap_new ( & trivial_hash_ops ) ) ;
2017-11-20 18:05:41 +03:00
for ( ; ; ) {
_cleanup_free_ char * line = NULL , * path = NULL ;
int mnt_id ;
r = read_line ( f , LONG_LINE_MAX , & line ) ;
if ( r = = 0 )
break ;
assert_se ( r > 0 ) ;
assert_se ( sscanf ( line , " %i %*s %*s %*s %ms " , & mnt_id , & path ) = = 2 ) ;
2017-11-23 14:48:42 +03:00
assert_se ( hashmap_put ( h , INT_TO_PTR ( mnt_id ) , path ) > = 0 ) ;
2017-11-20 18:05:41 +03:00
path = NULL ;
}
HASHMAP_FOREACH_KEY ( p , k , h , i ) {
2017-11-23 14:48:42 +03:00
int mnt_id = PTR_TO_INT ( k ) , mnt_id2 ;
2017-11-20 18:05:41 +03:00
2017-11-23 14:48:42 +03:00
r = path_get_mnt_id ( p , & mnt_id2 ) ;
2017-11-20 18:05:41 +03:00
if ( r = = - EOPNOTSUPP ) { /* kernel or file system too old? */
2017-11-23 14:48:42 +03:00
log_debug ( " %s doesn't support mount IDs \n " , p ) ;
2017-11-20 18:05:41 +03:00
continue ;
}
if ( IN_SET ( r , - EACCES , - EPERM ) ) {
2017-11-23 14:48:42 +03:00
log_debug ( " Can't access %s \n " , p ) ;
2017-11-20 18:05:41 +03:00
continue ;
}
2017-11-23 14:48:42 +03:00
log_debug ( " mnt id of %s is %i \n " , p , mnt_id2 ) ;
if ( mnt_id = = mnt_id2 )
continue ;
2017-11-20 18:05:41 +03:00
2017-11-23 14:48:42 +03:00
/* The ids don't match? If so, then there are two mounts on the same path, let's check if that's really
* the case */
assert_se ( path_equal_ptr ( hashmap_get ( h , INT_TO_PTR ( mnt_id2 ) ) , p ) ) ;
2017-11-20 18:05:41 +03:00
}
2017-11-28 14:31:27 +03:00
hashmap_free_free ( h ) ;
2017-11-20 18:05:41 +03:00
}
2017-10-31 12:52:30 +03:00
static void test_path_is_mount_point ( void ) {
int fd ;
char tmp_dir [ ] = " /tmp/test-path-is-mount-point-XXXXXX " ;
_cleanup_free_ char * file1 = NULL , * file2 = NULL , * link1 = NULL , * link2 = NULL ;
_cleanup_free_ char * dir1 = NULL , * dir1file = NULL , * dirlink1 = NULL , * dirlink1file = NULL ;
_cleanup_free_ char * dir2 = NULL , * dir2file = NULL ;
assert_se ( path_is_mount_point ( " / " , NULL , AT_SYMLINK_FOLLOW ) > 0 ) ;
assert_se ( path_is_mount_point ( " / " , NULL , 0 ) > 0 ) ;
assert_se ( path_is_mount_point ( " /proc " , NULL , AT_SYMLINK_FOLLOW ) > 0 ) ;
assert_se ( path_is_mount_point ( " /proc " , NULL , 0 ) > 0 ) ;
assert_se ( path_is_mount_point ( " /proc/1 " , NULL , AT_SYMLINK_FOLLOW ) = = 0 ) ;
assert_se ( path_is_mount_point ( " /proc/1 " , NULL , 0 ) = = 0 ) ;
assert_se ( path_is_mount_point ( " /sys " , NULL , AT_SYMLINK_FOLLOW ) > 0 ) ;
assert_se ( path_is_mount_point ( " /sys " , NULL , 0 ) > 0 ) ;
/* we'll create a hierarchy of different kinds of dir/file/link
* layouts :
*
* < tmp > / file1 , < tmp > / file2
* < tmp > / link1 - > file1 , < tmp > / link2 - > file2
* < tmp > / dir1 /
* < tmp > / dir1 / file
* < tmp > / dirlink1 - > dir1
* < tmp > / dirlink1file - > dirlink1 / file
* < tmp > / dir2 /
* < tmp > / dir2 / file
*/
/* file mountpoints */
assert_se ( mkdtemp ( tmp_dir ) ! = NULL ) ;
file1 = path_join ( NULL , tmp_dir , " file1 " ) ;
assert_se ( file1 ) ;
file2 = path_join ( NULL , tmp_dir , " file2 " ) ;
assert_se ( file2 ) ;
fd = open ( file1 , O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC , 0664 ) ;
assert_se ( fd > 0 ) ;
close ( fd ) ;
fd = open ( file2 , O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC , 0664 ) ;
assert_se ( fd > 0 ) ;
close ( fd ) ;
link1 = path_join ( NULL , tmp_dir , " link1 " ) ;
assert_se ( link1 ) ;
assert_se ( symlink ( " file1 " , link1 ) = = 0 ) ;
link2 = path_join ( NULL , tmp_dir , " link2 " ) ;
assert_se ( link1 ) ;
assert_se ( symlink ( " file2 " , link2 ) = = 0 ) ;
assert_se ( path_is_mount_point ( file1 , NULL , AT_SYMLINK_FOLLOW ) = = 0 ) ;
assert_se ( path_is_mount_point ( file1 , NULL , 0 ) = = 0 ) ;
assert_se ( path_is_mount_point ( link1 , NULL , AT_SYMLINK_FOLLOW ) = = 0 ) ;
assert_se ( path_is_mount_point ( link1 , NULL , 0 ) = = 0 ) ;
/* directory mountpoints */
dir1 = path_join ( NULL , tmp_dir , " dir1 " ) ;
assert_se ( dir1 ) ;
assert_se ( mkdir ( dir1 , 0755 ) = = 0 ) ;
dirlink1 = path_join ( NULL , tmp_dir , " dirlink1 " ) ;
assert_se ( dirlink1 ) ;
assert_se ( symlink ( " dir1 " , dirlink1 ) = = 0 ) ;
dirlink1file = path_join ( NULL , tmp_dir , " dirlink1file " ) ;
assert_se ( dirlink1file ) ;
assert_se ( symlink ( " dirlink1/file " , dirlink1file ) = = 0 ) ;
dir2 = path_join ( NULL , tmp_dir , " dir2 " ) ;
assert_se ( dir2 ) ;
assert_se ( mkdir ( dir2 , 0755 ) = = 0 ) ;
assert_se ( path_is_mount_point ( dir1 , NULL , AT_SYMLINK_FOLLOW ) = = 0 ) ;
assert_se ( path_is_mount_point ( dir1 , NULL , 0 ) = = 0 ) ;
assert_se ( path_is_mount_point ( dirlink1 , NULL , AT_SYMLINK_FOLLOW ) = = 0 ) ;
assert_se ( path_is_mount_point ( dirlink1 , NULL , 0 ) = = 0 ) ;
/* file in subdirectory mountpoints */
dir1file = path_join ( NULL , dir1 , " file " ) ;
assert_se ( dir1file ) ;
fd = open ( dir1file , O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC , 0664 ) ;
assert_se ( fd > 0 ) ;
close ( fd ) ;
assert_se ( path_is_mount_point ( dir1file , NULL , AT_SYMLINK_FOLLOW ) = = 0 ) ;
assert_se ( path_is_mount_point ( dir1file , NULL , 0 ) = = 0 ) ;
assert_se ( path_is_mount_point ( dirlink1file , NULL , AT_SYMLINK_FOLLOW ) = = 0 ) ;
assert_se ( path_is_mount_point ( dirlink1file , NULL , 0 ) = = 0 ) ;
/* these tests will only work as root */
if ( mount ( file1 , file2 , NULL , MS_BIND , NULL ) > = 0 ) {
int rt , rf , rlt , rlf , rl1t , rl1f ;
/* files */
/* capture results in vars, to avoid dangling mounts on failure */
rf = path_is_mount_point ( file2 , NULL , 0 ) ;
rt = path_is_mount_point ( file2 , NULL , AT_SYMLINK_FOLLOW ) ;
rlf = path_is_mount_point ( link2 , NULL , 0 ) ;
rlt = path_is_mount_point ( link2 , NULL , AT_SYMLINK_FOLLOW ) ;
assert_se ( umount ( file2 ) = = 0 ) ;
assert_se ( rf = = 1 ) ;
assert_se ( rt = = 1 ) ;
assert_se ( rlf = = 0 ) ;
assert_se ( rlt = = 1 ) ;
/* dirs */
dir2file = path_join ( NULL , dir2 , " file " ) ;
assert_se ( dir2file ) ;
fd = open ( dir2file , O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC , 0664 ) ;
assert_se ( fd > 0 ) ;
close ( fd ) ;
assert_se ( mount ( dir2 , dir1 , NULL , MS_BIND , NULL ) > = 0 ) ;
rf = path_is_mount_point ( dir1 , NULL , 0 ) ;
rt = path_is_mount_point ( dir1 , NULL , AT_SYMLINK_FOLLOW ) ;
rlf = path_is_mount_point ( dirlink1 , NULL , 0 ) ;
rlt = path_is_mount_point ( dirlink1 , NULL , AT_SYMLINK_FOLLOW ) ;
/* its parent is a mount point, but not /file itself */
rl1f = path_is_mount_point ( dirlink1file , NULL , 0 ) ;
rl1t = path_is_mount_point ( dirlink1file , NULL , AT_SYMLINK_FOLLOW ) ;
assert_se ( umount ( dir1 ) = = 0 ) ;
assert_se ( rf = = 1 ) ;
assert_se ( rt = = 1 ) ;
assert_se ( rlf = = 0 ) ;
assert_se ( rlt = = 1 ) ;
assert_se ( rl1f = = 0 ) ;
assert_se ( rl1t = = 0 ) ;
} else
printf ( " Skipping bind mount file test: %m \n " ) ;
assert_se ( rm_rf ( tmp_dir , REMOVE_ROOT | REMOVE_PHYSICAL ) = = 0 ) ;
}
2016-11-22 22:19:08 +03:00
int main ( int argc , char * argv [ ] ) {
log_set_max_level ( LOG_DEBUG ) ;
2016-12-03 21:57:42 +03:00
test_mount_propagation_flags ( " shared " , 0 , MS_SHARED ) ;
test_mount_propagation_flags ( " slave " , 0 , MS_SLAVE ) ;
test_mount_propagation_flags ( " private " , 0 , MS_PRIVATE ) ;
test_mount_propagation_flags ( NULL , 0 , 0 ) ;
test_mount_propagation_flags ( " " , 0 , 0 ) ;
test_mount_propagation_flags ( " xxxx " , - EINVAL , 0 ) ;
test_mount_propagation_flags ( " " , - EINVAL , 0 ) ;
2016-11-22 22:19:08 +03:00
2017-11-20 18:05:41 +03:00
test_mnt_id ( ) ;
2017-10-31 12:52:30 +03:00
test_path_is_mount_point ( ) ;
2017-11-20 18:05:41 +03:00
2016-11-22 22:19:08 +03:00
return 0 ;
}