2010-10-07 16:43:57 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd .
Copyright 2010 ProFUSION embedded systems
systemd 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 2 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
General Public License for more details .
You should have received a copy of the GNU General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <errno.h>
# include <fcntl.h>
# include <string.h>
# include <sys/mount.h>
# include <sys/swap.h>
# include <unistd.h>
# include <linux/loop.h>
2010-10-14 04:33:09 +04:00
# include <linux/dm-ioctl.h>
2010-10-07 16:43:57 +04:00
# include <libudev.h>
# include "list.h"
# include "mount-setup.h"
# include "umount.h"
# include "util.h"
typedef struct MountPoint {
char * path ;
2010-10-14 20:17:23 +04:00
dev_t devnum ;
2011-03-15 04:41:11 +03:00
bool skip_ro ;
2010-10-07 16:43:57 +04:00
LIST_FIELDS ( struct MountPoint , mount_point ) ;
} MountPoint ;
2010-10-14 20:55:04 +04:00
static void mount_point_free ( MountPoint * * head , MountPoint * m ) {
assert ( head ) ;
assert ( m ) ;
2010-10-07 16:43:57 +04:00
2010-10-14 20:55:04 +04:00
LIST_REMOVE ( MountPoint , mount_point , * head , m ) ;
free ( m - > path ) ;
free ( m ) ;
2010-10-07 16:43:57 +04:00
}
2010-10-14 20:55:04 +04:00
static void mount_points_list_free ( MountPoint * * head ) {
assert ( head ) ;
while ( * head )
mount_point_free ( head , * head ) ;
2010-10-07 16:43:57 +04:00
}
2010-10-14 20:55:04 +04:00
static int mount_points_list_get ( MountPoint * * head ) {
2010-10-07 16:43:57 +04:00
FILE * proc_self_mountinfo ;
char * path , * p ;
unsigned int i ;
int r ;
2010-10-14 20:55:04 +04:00
assert ( head ) ;
2010-10-07 16:43:57 +04:00
if ( ! ( proc_self_mountinfo = fopen ( " /proc/self/mountinfo " , " re " ) ) )
return - errno ;
for ( i = 1 ; ; i + + ) {
int k ;
2010-10-14 20:55:04 +04:00
MountPoint * m ;
2011-03-15 04:41:11 +03:00
char * root ;
bool skip_ro ;
2010-10-07 16:43:57 +04:00
path = p = NULL ;
if ( ( k = fscanf ( proc_self_mountinfo ,
" %*s " /* (1) mount id */
" %*s " /* (2) parent id */
" %*s " /* (3) major:minor */
2011-03-15 04:41:11 +03:00
" %ms " /* (4) root */
2010-10-07 16:43:57 +04:00
" %ms " /* (5) mount point */
" %*s " /* (6) mount options */
" %*[^-] " /* (7) optional fields */
" - " /* (8) separator */
" %*s " /* (9) file system type */
" %*s " /* (10) mount source */
" %*s " /* (11) mount options 2 */
" %*[^ \n ] " , /* some rubbish at the end */
2011-03-15 04:41:11 +03:00
& root ,
& path ) ) ! = 2 ) {
2010-10-07 16:43:57 +04:00
if ( k = = EOF )
break ;
log_warning ( " Failed to parse /proc/self/mountinfo:%u. " , i ) ;
free ( path ) ;
continue ;
}
2011-03-15 04:41:11 +03:00
/* If we encounter a bind mount, don't try to remount
* the source dir too early */
if ( ! streq ( root , " / " ) )
skip_ro = true ;
free ( root ) ;
2010-10-14 02:41:57 +04:00
p = cunescape ( path ) ;
free ( path ) ;
2010-10-07 16:43:57 +04:00
2010-10-14 02:41:57 +04:00
if ( ! p ) {
2010-10-07 16:43:57 +04:00
r = - ENOMEM ;
goto finish ;
}
2010-11-11 13:15:16 +03:00
if ( mount_point_is_api ( p ) | | mount_point_ignore ( p ) ) {
2010-10-14 02:41:57 +04:00
free ( p ) ;
continue ;
}
2010-10-14 20:55:04 +04:00
if ( ! ( m = new0 ( MountPoint , 1 ) ) ) {
2010-10-14 02:41:57 +04:00
free ( p ) ;
2010-10-07 16:43:57 +04:00
r = - ENOMEM ;
goto finish ;
}
2010-10-14 20:55:04 +04:00
m - > path = p ;
2011-03-15 04:41:11 +03:00
m - > skip_ro = skip_ro ;
2010-10-14 20:55:04 +04:00
LIST_PREPEND ( MountPoint , mount_point , * head , m ) ;
2010-10-07 16:43:57 +04:00
}
r = 0 ;
finish :
fclose ( proc_self_mountinfo ) ;
return r ;
}
2010-10-14 20:55:04 +04:00
static int swap_list_get ( MountPoint * * head ) {
2010-10-07 16:43:57 +04:00
FILE * proc_swaps ;
unsigned int i ;
int r ;
2010-10-14 20:55:04 +04:00
assert ( head ) ;
2010-10-07 16:43:57 +04:00
if ( ! ( proc_swaps = fopen ( " /proc/swaps " , " re " ) ) )
return - errno ;
( void ) fscanf ( proc_swaps , " %*s %*s %*s %*s %*s \n " ) ;
for ( i = 2 ; ; i + + ) {
MountPoint * swap ;
char * dev = NULL , * d ;
int k ;
if ( ( k = fscanf ( proc_swaps ,
" %ms " /* device/file */
" %*s " /* type of swap */
" %*s " /* swap size */
" %*s " /* used */
" %*s \n " , /* priority */
& dev ) ) ! = 1 ) {
if ( k = = EOF )
break ;
log_warning ( " Failed to parse /proc/swaps:%u. " , i ) ;
free ( dev ) ;
continue ;
}
if ( endswith ( dev , " (deleted) " ) ) {
free ( dev ) ;
continue ;
}
d = cunescape ( dev ) ;
free ( dev ) ;
if ( ! d ) {
r = - ENOMEM ;
goto finish ;
}
2010-10-14 20:17:23 +04:00
if ( ! ( swap = new0 ( MountPoint , 1 ) ) ) {
2010-10-07 16:43:57 +04:00
free ( d ) ;
r = - ENOMEM ;
goto finish ;
}
2010-10-14 20:17:23 +04:00
swap - > path = d ;
2010-10-14 20:55:04 +04:00
LIST_PREPEND ( MountPoint , mount_point , * head , swap ) ;
2010-10-07 16:43:57 +04:00
}
r = 0 ;
finish :
fclose ( proc_swaps ) ;
return r ;
}
2010-10-14 20:55:04 +04:00
static int loopback_list_get ( MountPoint * * head ) {
2010-10-07 16:43:57 +04:00
int r ;
struct udev * udev ;
struct udev_enumerate * e = NULL ;
struct udev_list_entry * item = NULL , * first = NULL ;
2010-10-14 20:55:04 +04:00
assert ( head ) ;
2010-10-07 16:43:57 +04:00
if ( ! ( udev = udev_new ( ) ) ) {
r = - ENOMEM ;
goto finish ;
}
if ( ! ( e = udev_enumerate_new ( udev ) ) ) {
r = - ENOMEM ;
goto finish ;
}
2010-10-14 04:33:09 +04:00
if ( udev_enumerate_add_match_subsystem ( e , " block " ) < 0 | |
udev_enumerate_add_match_sysname ( e , " loop* " ) < 0 ) {
2010-10-07 16:43:57 +04:00
r = - EIO ;
goto finish ;
}
if ( udev_enumerate_scan_devices ( e ) < 0 ) {
r = - EIO ;
goto finish ;
}
first = udev_enumerate_get_list_entry ( e ) ;
udev_list_entry_foreach ( item , first ) {
MountPoint * lb ;
2010-10-14 02:42:44 +04:00
struct udev_device * d ;
2010-10-07 16:43:57 +04:00
char * loop ;
2010-10-14 02:42:44 +04:00
const char * dn ;
2010-10-07 16:43:57 +04:00
2010-10-14 02:42:44 +04:00
if ( ! ( d = udev_device_new_from_syspath ( udev , udev_list_entry_get_name ( item ) ) ) ) {
2010-10-07 16:43:57 +04:00
r = - ENOMEM ;
goto finish ;
}
2010-10-14 20:17:23 +04:00
if ( ! ( dn = udev_device_get_devnode ( d ) ) ) {
2010-10-14 02:42:44 +04:00
udev_device_unref ( d ) ;
2010-10-14 20:17:23 +04:00
continue ;
}
2010-10-14 02:42:44 +04:00
2010-10-14 20:17:23 +04:00
loop = strdup ( dn ) ;
udev_device_unref ( d ) ;
if ( ! loop ) {
r = - ENOMEM ;
goto finish ;
}
2010-10-14 02:42:44 +04:00
2010-10-14 20:17:23 +04:00
if ( ! ( lb = new0 ( MountPoint , 1 ) ) ) {
2010-10-07 16:43:57 +04:00
free ( loop ) ;
r = - ENOMEM ;
goto finish ;
}
2010-10-14 20:17:23 +04:00
lb - > path = loop ;
2010-10-14 20:55:04 +04:00
LIST_PREPEND ( MountPoint , mount_point , * head , lb ) ;
2010-10-07 16:43:57 +04:00
}
r = 0 ;
finish :
if ( e )
udev_enumerate_unref ( e ) ;
2010-10-14 02:42:44 +04:00
if ( udev )
udev_unref ( udev ) ;
2010-10-07 16:43:57 +04:00
return r ;
}
2010-10-14 20:55:04 +04:00
static int dm_list_get ( MountPoint * * head ) {
2010-10-14 04:33:09 +04:00
int r ;
struct udev * udev ;
struct udev_enumerate * e = NULL ;
struct udev_list_entry * item = NULL , * first = NULL ;
2010-10-14 20:55:04 +04:00
assert ( head ) ;
2010-10-14 04:33:09 +04:00
if ( ! ( udev = udev_new ( ) ) ) {
r = - ENOMEM ;
goto finish ;
}
if ( ! ( e = udev_enumerate_new ( udev ) ) ) {
r = - ENOMEM ;
goto finish ;
}
if ( udev_enumerate_add_match_subsystem ( e , " block " ) < 0 | |
udev_enumerate_add_match_sysname ( e , " dm-* " ) < 0 ) {
r = - EIO ;
goto finish ;
}
if ( udev_enumerate_scan_devices ( e ) < 0 ) {
r = - EIO ;
goto finish ;
}
first = udev_enumerate_get_list_entry ( e ) ;
udev_list_entry_foreach ( item , first ) {
2010-10-14 20:17:23 +04:00
MountPoint * m ;
2010-10-14 04:33:09 +04:00
struct udev_device * d ;
2010-10-14 20:17:23 +04:00
dev_t devnum ;
char * node ;
const char * dn ;
2010-10-14 04:33:09 +04:00
if ( ! ( d = udev_device_new_from_syspath ( udev , udev_list_entry_get_name ( item ) ) ) ) {
r = - ENOMEM ;
goto finish ;
}
2010-10-14 20:17:23 +04:00
devnum = udev_device_get_devnum ( d ) ;
dn = udev_device_get_devnode ( d ) ;
2010-10-14 04:33:09 +04:00
2010-10-14 20:17:23 +04:00
if ( major ( devnum ) = = 0 | | ! dn ) {
udev_device_unref ( d ) ;
continue ;
2010-10-14 04:33:09 +04:00
}
2010-10-14 20:17:23 +04:00
node = strdup ( dn ) ;
2010-10-14 04:33:09 +04:00
udev_device_unref ( d ) ;
2010-10-14 20:17:23 +04:00
if ( ! node ) {
r = - ENOMEM ;
goto finish ;
}
2010-10-14 04:33:09 +04:00
2010-10-14 20:17:23 +04:00
if ( ! ( m = new ( MountPoint , 1 ) ) ) {
free ( node ) ;
2010-10-14 04:33:09 +04:00
r = - ENOMEM ;
goto finish ;
}
2010-10-14 20:17:23 +04:00
m - > path = node ;
m - > devnum = devnum ;
2010-10-14 20:55:04 +04:00
LIST_PREPEND ( MountPoint , mount_point , * head , m ) ;
2010-10-14 04:33:09 +04:00
}
r = 0 ;
finish :
if ( e )
udev_enumerate_unref ( e ) ;
if ( udev )
udev_unref ( udev ) ;
return r ;
}
2010-10-07 16:43:57 +04:00
static int delete_loopback ( const char * device ) {
int fd , r ;
2010-10-14 02:43:13 +04:00
if ( ( fd = open ( device , O_RDONLY | O_CLOEXEC ) ) < 0 )
2011-03-14 07:37:47 +03:00
return errno = = ENOENT ? 0 : - errno ;
2010-10-07 16:43:57 +04:00
2010-10-14 02:43:13 +04:00
r = ioctl ( fd , LOOP_CLR_FD , 0 ) ;
2010-10-07 22:46:35 +04:00
close_nointr_nofail ( fd ) ;
2010-10-07 16:43:57 +04:00
2010-10-14 20:55:04 +04:00
if ( r > = 0 )
return 1 ;
2010-10-14 02:43:13 +04:00
/* ENXIO: not bound, so no error */
2010-10-14 20:55:04 +04:00
if ( errno = = ENXIO )
return 0 ;
return - errno ;
2010-10-07 16:43:57 +04:00
}
2010-10-14 20:17:23 +04:00
static int delete_dm ( dev_t devnum ) {
2010-10-14 04:33:09 +04:00
int fd , r ;
struct dm_ioctl dm ;
2010-10-14 20:17:23 +04:00
assert ( major ( devnum ) ! = 0 ) ;
2010-10-14 04:33:09 +04:00
if ( ( fd = open ( " /dev/mapper/control " , O_RDWR | O_CLOEXEC ) ) < 0 )
return - errno ;
zero ( dm ) ;
dm . version [ 0 ] = DM_VERSION_MAJOR ;
dm . version [ 1 ] = DM_VERSION_MINOR ;
dm . version [ 2 ] = DM_VERSION_PATCHLEVEL ;
dm . data_size = sizeof ( dm ) ;
2010-10-14 20:17:23 +04:00
dm . dev = devnum ;
2010-10-14 04:33:09 +04:00
r = ioctl ( fd , DM_DEV_REMOVE , & dm ) ;
close_nointr_nofail ( fd ) ;
return r > = 0 ? 0 : - errno ;
}
2010-10-14 20:55:04 +04:00
static int mount_points_list_umount ( MountPoint * * head , bool * changed ) {
MountPoint * m , * n ;
int n_failed = 0 ;
2010-10-07 16:43:57 +04:00
2010-10-14 20:55:04 +04:00
assert ( head ) ;
LIST_FOREACH_SAFE ( mount_point , m , n , * head ) {
2010-10-24 20:40:44 +04:00
if ( streq ( m - > path , " / " ) ) {
n_failed + + ;
2010-10-07 16:43:57 +04:00
continue ;
2010-10-24 20:40:44 +04:00
}
2010-10-07 16:43:57 +04:00
/* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
2010-10-14 20:55:04 +04:00
if ( umount2 ( m - > path , MNT_FORCE ) = = 0 ) {
if ( changed )
* changed = true ;
mount_point_free ( head , m ) ;
} else {
log_warning ( " Could not unmount %s: %m " , m - > path ) ;
n_failed + + ;
2010-10-07 16:43:57 +04:00
}
}
2010-10-14 20:55:04 +04:00
return n_failed ;
2010-10-07 16:43:57 +04:00
}
2010-10-14 20:55:04 +04:00
static int mount_points_list_remount_read_only ( MountPoint * * head , bool * changed ) {
MountPoint * m , * n ;
int n_failed = 0 ;
assert ( head ) ;
LIST_FOREACH_SAFE ( mount_point , m , n , * head ) {
2010-10-07 16:43:57 +04:00
2011-03-15 04:41:11 +03:00
if ( m - > skip_ro ) {
n_failed + + ;
continue ;
}
2010-10-07 16:43:57 +04:00
/* Trying to remount read-only */
2010-10-14 20:55:04 +04:00
if ( mount ( NULL , m - > path , NULL , MS_MGC_VAL | MS_REMOUNT | MS_RDONLY , NULL ) = = 0 ) {
if ( changed )
* changed = true ;
mount_point_free ( head , m ) ;
} else {
log_warning ( " Could not remount as read-only %s: %m " , m - > path ) ;
n_failed + + ;
2010-10-07 16:43:57 +04:00
}
}
2010-10-14 20:55:04 +04:00
return n_failed ;
2010-10-07 16:43:57 +04:00
}
2010-10-14 20:55:04 +04:00
static int swap_points_list_off ( MountPoint * * head , bool * changed ) {
MountPoint * m , * n ;
int n_failed = 0 ;
assert ( head ) ;
2010-10-07 16:43:57 +04:00
2010-10-14 20:55:04 +04:00
LIST_FOREACH_SAFE ( mount_point , m , n , * head ) {
if ( swapoff ( m - > path ) = = 0 ) {
if ( changed )
* changed = true ;
mount_point_free ( head , m ) ;
} else {
log_warning ( " Could not deactivate swap %s: %m " , m - > path ) ;
n_failed + + ;
2010-10-07 16:43:57 +04:00
}
}
2010-10-14 20:55:04 +04:00
return n_failed ;
2010-10-07 16:43:57 +04:00
}
2010-10-14 20:55:04 +04:00
static int loopback_points_list_detach ( MountPoint * * head , bool * changed ) {
MountPoint * m , * n ;
2011-01-06 21:10:17 +03:00
int n_failed = 0 , k ;
struct stat root_st ;
2010-10-14 20:55:04 +04:00
assert ( head ) ;
2011-01-06 21:10:17 +03:00
k = lstat ( " / " , & root_st ) ;
2010-10-14 20:55:04 +04:00
LIST_FOREACH_SAFE ( mount_point , m , n , * head ) {
int r ;
2011-01-06 21:10:17 +03:00
struct stat loopback_st ;
if ( k > = 0 & &
major ( root_st . st_dev ) ! = 0 & &
lstat ( m - > path , & loopback_st ) > = 0 & &
root_st . st_dev = = loopback_st . st_rdev ) {
n_failed + + ;
continue ;
}
2010-10-07 16:43:57 +04:00
2010-10-14 20:55:04 +04:00
if ( ( r = delete_loopback ( m - > path ) ) > = 0 ) {
if ( r > 0 & & changed )
* changed = true ;
mount_point_free ( head , m ) ;
} else {
log_warning ( " Could not delete loopback %s: %m " , m - > path ) ;
n_failed + + ;
2010-10-07 16:43:57 +04:00
}
}
2010-10-14 20:55:04 +04:00
return n_failed ;
2010-10-07 16:43:57 +04:00
}
2010-10-14 20:55:04 +04:00
static int dm_points_list_detach ( MountPoint * * head , bool * changed ) {
MountPoint * m , * n ;
2011-01-06 21:10:17 +03:00
int n_failed = 0 , k ;
struct stat root_st ;
2010-10-14 20:55:04 +04:00
assert ( head ) ;
2011-01-06 21:10:17 +03:00
k = lstat ( " / " , & root_st ) ;
2010-10-14 20:55:04 +04:00
LIST_FOREACH_SAFE ( mount_point , m , n , * head ) {
int r ;
2011-01-06 21:10:17 +03:00
if ( k > = 0 & &
major ( root_st . st_dev ) ! = 0 & &
root_st . st_dev = = m - > devnum ) {
n_failed + + ;
continue ;
}
2010-10-14 20:55:04 +04:00
if ( ( r = delete_dm ( m - > devnum ) ) > = 0 ) {
if ( r > 0 & & changed )
* changed = true ;
2010-10-14 04:33:09 +04:00
2010-10-14 20:55:04 +04:00
mount_point_free ( head , m ) ;
} else {
log_warning ( " Could not delete dm %s: %m " , m - > path ) ;
n_failed + + ;
2010-10-14 04:33:09 +04:00
}
}
2010-10-14 20:55:04 +04:00
return n_failed ;
2010-10-14 04:33:09 +04:00
}
2010-10-14 20:55:04 +04:00
int umount_all ( bool * changed ) {
2010-10-07 16:43:57 +04:00
int r ;
LIST_HEAD ( MountPoint , mp_list_head ) ;
LIST_HEAD_INIT ( MountPoint , mp_list_head ) ;
r = mount_points_list_get ( & mp_list_head ) ;
if ( r < 0 )
goto end ;
2010-10-14 20:55:04 +04:00
r = mount_points_list_umount ( & mp_list_head , changed ) ;
2010-10-07 16:43:57 +04:00
if ( r < = 0 )
goto end ;
2010-10-14 20:55:04 +04:00
r = mount_points_list_remount_read_only ( & mp_list_head , changed ) ;
2010-10-07 16:43:57 +04:00
end :
mount_points_list_free ( & mp_list_head ) ;
return r ;
}
2010-10-14 20:55:04 +04:00
int swapoff_all ( bool * changed ) {
2010-10-07 16:43:57 +04:00
int r ;
LIST_HEAD ( MountPoint , swap_list_head ) ;
LIST_HEAD_INIT ( MountPoint , swap_list_head ) ;
r = swap_list_get ( & swap_list_head ) ;
if ( r < 0 )
goto end ;
2010-10-14 20:55:04 +04:00
r = swap_points_list_off ( & swap_list_head , changed ) ;
2010-10-07 16:43:57 +04:00
end :
mount_points_list_free ( & swap_list_head ) ;
return r ;
}
2010-10-14 20:55:04 +04:00
int loopback_detach_all ( bool * changed ) {
2010-10-07 16:43:57 +04:00
int r ;
LIST_HEAD ( MountPoint , loopback_list_head ) ;
LIST_HEAD_INIT ( MountPoint , loopback_list_head ) ;
r = loopback_list_get ( & loopback_list_head ) ;
if ( r < 0 )
goto end ;
2010-10-14 20:55:04 +04:00
r = loopback_points_list_detach ( & loopback_list_head , changed ) ;
2010-10-07 16:43:57 +04:00
end :
mount_points_list_free ( & loopback_list_head ) ;
return r ;
}
2010-10-14 04:33:09 +04:00
2010-10-14 20:55:04 +04:00
int dm_detach_all ( bool * changed ) {
2010-10-14 04:33:09 +04:00
int r ;
LIST_HEAD ( MountPoint , dm_list_head ) ;
LIST_HEAD_INIT ( MountPoint , dm_list_head ) ;
r = dm_list_get ( & dm_list_head ) ;
if ( r < 0 )
goto end ;
2010-10-14 20:55:04 +04:00
r = dm_points_list_detach ( & dm_list_head , changed ) ;
2010-10-14 04:33:09 +04:00
end :
mount_points_list_free ( & dm_list_head ) ;
return r ;
}