2018-06-08 03:11:13 +03:00
/*
* Copyright 1997 - 1998 Transmeta Corporation - - All Rights Reserved
* Copyright 1999 - 2000 Jeremy Fitzhardinge < jeremy @ goop . org >
* Copyright 2001 - 2006 Ian Kent < raven @ themaw . net >
*
* This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License , version 2 , or at your
* option , any later version , incorporated herein by reference .
*/
# include "autofs_i.h"
/* Check if a dentry can be expired */
static inline int autofs_can_expire ( struct dentry * dentry ,
unsigned long timeout , int do_now )
{
struct autofs_info * ino = autofs_dentry_ino ( dentry ) ;
/* dentry in the process of being deleted */
if ( ino = = NULL )
return 0 ;
if ( ! do_now ) {
/* Too young to die */
2018-08-22 07:58:44 +03:00
if ( ! timeout | | time_after ( ino - > last_used + timeout , jiffies ) )
2018-06-08 03:11:13 +03:00
return 0 ;
}
return 1 ;
}
/* Check a mount point for busyness */
static int autofs_mount_busy ( struct vfsmount * mnt , struct dentry * dentry )
{
struct dentry * top = dentry ;
struct path path = { . mnt = mnt , . dentry = dentry } ;
int status = 1 ;
pr_debug ( " dentry %p %pd \n " , dentry , dentry ) ;
path_get ( & path ) ;
if ( ! follow_down_one ( & path ) )
goto done ;
if ( is_autofs_dentry ( path . dentry ) ) {
struct autofs_sb_info * sbi = autofs_sbi ( path . dentry - > d_sb ) ;
/* This is an autofs submount, we can't expire it */
if ( autofs_type_indirect ( sbi - > type ) )
goto done ;
}
/* Update the expiry counter if fs is busy */
if ( ! may_umount_tree ( path . mnt ) ) {
struct autofs_info * ino ;
ino = autofs_dentry_ino ( top ) ;
ino - > last_used = jiffies ;
goto done ;
}
status = 0 ;
done :
pr_debug ( " returning = %d \n " , status ) ;
path_put ( & path ) ;
return status ;
}
/*
* Calculate and dget next entry in the subdirs list under root .
*/
static struct dentry * get_next_positive_subdir ( struct dentry * prev ,
struct dentry * root )
{
struct autofs_sb_info * sbi = autofs_sbi ( root - > d_sb ) ;
struct list_head * next ;
struct dentry * q ;
spin_lock ( & sbi - > lookup_lock ) ;
spin_lock ( & root - > d_lock ) ;
if ( prev )
next = prev - > d_child . next ;
else {
prev = dget_dlock ( root ) ;
next = prev - > d_subdirs . next ;
}
cont :
if ( next = = & root - > d_subdirs ) {
spin_unlock ( & root - > d_lock ) ;
spin_unlock ( & sbi - > lookup_lock ) ;
dput ( prev ) ;
return NULL ;
}
q = list_entry ( next , struct dentry , d_child ) ;
spin_lock_nested ( & q - > d_lock , DENTRY_D_LOCK_NESTED ) ;
/* Already gone or negative dentry (under construction) - try next */
if ( ! d_count ( q ) | | ! simple_positive ( q ) ) {
spin_unlock ( & q - > d_lock ) ;
next = q - > d_child . next ;
goto cont ;
}
dget_dlock ( q ) ;
spin_unlock ( & q - > d_lock ) ;
spin_unlock ( & root - > d_lock ) ;
spin_unlock ( & sbi - > lookup_lock ) ;
dput ( prev ) ;
return q ;
}
/*
* Calculate and dget next entry in top down tree traversal .
*/
static struct dentry * get_next_positive_dentry ( struct dentry * prev ,
struct dentry * root )
{
struct autofs_sb_info * sbi = autofs_sbi ( root - > d_sb ) ;
struct list_head * next ;
struct dentry * p , * ret ;
if ( prev = = NULL )
return dget ( root ) ;
spin_lock ( & sbi - > lookup_lock ) ;
relock :
p = prev ;
spin_lock ( & p - > d_lock ) ;
again :
next = p - > d_subdirs . next ;
if ( next = = & p - > d_subdirs ) {
while ( 1 ) {
struct dentry * parent ;
if ( p = = root ) {
spin_unlock ( & p - > d_lock ) ;
spin_unlock ( & sbi - > lookup_lock ) ;
dput ( prev ) ;
return NULL ;
}
parent = p - > d_parent ;
if ( ! spin_trylock ( & parent - > d_lock ) ) {
spin_unlock ( & p - > d_lock ) ;
cpu_relax ( ) ;
goto relock ;
}
spin_unlock ( & p - > d_lock ) ;
next = p - > d_child . next ;
p = parent ;
if ( next ! = & parent - > d_subdirs )
break ;
}
}
ret = list_entry ( next , struct dentry , d_child ) ;
spin_lock_nested ( & ret - > d_lock , DENTRY_D_LOCK_NESTED ) ;
/* Negative dentry - try next */
if ( ! simple_positive ( ret ) ) {
spin_unlock ( & p - > d_lock ) ;
lock_set_subclass ( & ret - > d_lock . dep_map , 0 , _RET_IP_ ) ;
p = ret ;
goto again ;
}
dget_dlock ( ret ) ;
spin_unlock ( & ret - > d_lock ) ;
spin_unlock ( & p - > d_lock ) ;
spin_unlock ( & sbi - > lookup_lock ) ;
dput ( prev ) ;
return ret ;
}
/*
* Check a direct mount point for busyness .
* Direct mounts have similar expiry semantics to tree mounts .
* The tree is not busy iff no mountpoints are busy and there are no
* autofs submounts .
*/
static int autofs_direct_busy ( struct vfsmount * mnt ,
struct dentry * top ,
unsigned long timeout ,
int do_now )
{
pr_debug ( " top %p %pd \n " , top , top ) ;
/* If it's busy update the expiry counters */
if ( ! may_umount_tree ( mnt ) ) {
struct autofs_info * ino ;
ino = autofs_dentry_ino ( top ) ;
if ( ino )
ino - > last_used = jiffies ;
return 1 ;
}
/* Timeout of a direct mount is determined by its top dentry */
if ( ! autofs_can_expire ( top , timeout , do_now ) )
return 1 ;
return 0 ;
}
/*
* Check a directory tree of mount points for busyness
* The tree is not busy iff no mountpoints are busy
*/
static int autofs_tree_busy ( struct vfsmount * mnt ,
struct dentry * top ,
unsigned long timeout ,
int do_now )
{
struct autofs_info * top_ino = autofs_dentry_ino ( top ) ;
struct dentry * p ;
pr_debug ( " top %p %pd \n " , top , top ) ;
/* Negative dentry - give up */
if ( ! simple_positive ( top ) )
return 1 ;
p = NULL ;
while ( ( p = get_next_positive_dentry ( p , top ) ) ) {
pr_debug ( " dentry %p %pd \n " , p , p ) ;
/*
* Is someone visiting anywhere in the subtree ?
* If there ' s no mount we need to check the usage
* count for the autofs dentry .
* If the fs is busy update the expiry counter .
*/
if ( d_mountpoint ( p ) ) {
if ( autofs_mount_busy ( mnt , p ) ) {
top_ino - > last_used = jiffies ;
dput ( p ) ;
return 1 ;
}
} else {
struct autofs_info * ino = autofs_dentry_ino ( p ) ;
unsigned int ino_count = atomic_read ( & ino - > count ) ;
/* allow for dget above and top is already dgot */
if ( p = = top )
ino_count + = 2 ;
else
ino_count + + ;
if ( d_count ( p ) > ino_count ) {
top_ino - > last_used = jiffies ;
dput ( p ) ;
return 1 ;
}
}
}
/* Timeout of a tree mount is ultimately determined by its top dentry */
if ( ! autofs_can_expire ( top , timeout , do_now ) )
return 1 ;
return 0 ;
}
static struct dentry * autofs_check_leaves ( struct vfsmount * mnt ,
struct dentry * parent ,
unsigned long timeout ,
int do_now )
{
struct dentry * p ;
pr_debug ( " parent %p %pd \n " , parent , parent ) ;
p = NULL ;
while ( ( p = get_next_positive_dentry ( p , parent ) ) ) {
pr_debug ( " dentry %p %pd \n " , p , p ) ;
if ( d_mountpoint ( p ) ) {
/* Can we umount this guy */
if ( autofs_mount_busy ( mnt , p ) )
continue ;
/* Can we expire this guy */
if ( autofs_can_expire ( p , timeout , do_now ) )
return p ;
}
}
return NULL ;
}
/* Check if we can expire a direct mount (possibly a tree) */
2018-08-22 07:58:51 +03:00
static struct dentry * autofs_expire_direct ( struct super_block * sb ,
struct vfsmount * mnt ,
struct autofs_sb_info * sbi ,
int how )
2018-06-08 03:11:13 +03:00
{
unsigned long timeout ;
struct dentry * root = dget ( sb - > s_root ) ;
int do_now = how & AUTOFS_EXP_IMMEDIATE ;
struct autofs_info * ino ;
if ( ! root )
return NULL ;
timeout = sbi - > exp_timeout ;
if ( ! autofs_direct_busy ( mnt , root , timeout , do_now ) ) {
spin_lock ( & sbi - > fs_lock ) ;
ino = autofs_dentry_ino ( root ) ;
/* No point expiring a pending mount */
if ( ino - > flags & AUTOFS_INF_PENDING ) {
spin_unlock ( & sbi - > fs_lock ) ;
goto out ;
}
ino - > flags | = AUTOFS_INF_WANT_EXPIRE ;
spin_unlock ( & sbi - > fs_lock ) ;
synchronize_rcu ( ) ;
if ( ! autofs_direct_busy ( mnt , root , timeout , do_now ) ) {
spin_lock ( & sbi - > fs_lock ) ;
ino - > flags | = AUTOFS_INF_EXPIRING ;
init_completion ( & ino - > expire_complete ) ;
spin_unlock ( & sbi - > fs_lock ) ;
return root ;
}
spin_lock ( & sbi - > fs_lock ) ;
ino - > flags & = ~ AUTOFS_INF_WANT_EXPIRE ;
spin_unlock ( & sbi - > fs_lock ) ;
}
out :
dput ( root ) ;
return NULL ;
}
/* Check if 'dentry' should expire, or return a nearby
* dentry that is suitable .
* If returned dentry is different from arg dentry ,
* then a dget ( ) reference was taken , else not .
*/
static struct dentry * should_expire ( struct dentry * dentry ,
struct vfsmount * mnt ,
unsigned long timeout ,
int how )
{
int do_now = how & AUTOFS_EXP_IMMEDIATE ;
int exp_leaves = how & AUTOFS_EXP_LEAVES ;
struct autofs_info * ino = autofs_dentry_ino ( dentry ) ;
unsigned int ino_count ;
/* No point expiring a pending mount */
if ( ino - > flags & AUTOFS_INF_PENDING )
return NULL ;
/*
* Case 1 : ( i ) indirect mount or top level pseudo direct mount
* ( autofs - 4.1 ) .
* ( ii ) indirect mount with offset mount , check the " / "
* offset ( autofs - 5.0 + ) .
*/
if ( d_mountpoint ( dentry ) ) {
pr_debug ( " checking mountpoint %p %pd \n " , dentry , dentry ) ;
/* Can we umount this guy */
if ( autofs_mount_busy ( mnt , dentry ) )
return NULL ;
/* Can we expire this guy */
if ( autofs_can_expire ( dentry , timeout , do_now ) )
return dentry ;
return NULL ;
}
if ( d_really_is_positive ( dentry ) & & d_is_symlink ( dentry ) ) {
pr_debug ( " checking symlink %p %pd \n " , dentry , dentry ) ;
/*
* A symlink can ' t be " busy " in the usual sense so
* just check last used for expire timeout .
*/
if ( autofs_can_expire ( dentry , timeout , do_now ) )
return dentry ;
return NULL ;
}
if ( simple_empty ( dentry ) )
return NULL ;
/* Case 2: tree mount, expire iff entire tree is not busy */
if ( ! exp_leaves ) {
/* Path walk currently on this dentry? */
ino_count = atomic_read ( & ino - > count ) + 1 ;
if ( d_count ( dentry ) > ino_count )
return NULL ;
if ( ! autofs_tree_busy ( mnt , dentry , timeout , do_now ) )
return dentry ;
/*
* Case 3 : pseudo direct mount , expire individual leaves
* ( autofs - 4.1 ) .
*/
} else {
/* Path walk currently on this dentry? */
struct dentry * expired ;
ino_count = atomic_read ( & ino - > count ) + 1 ;
if ( d_count ( dentry ) > ino_count )
return NULL ;
expired = autofs_check_leaves ( mnt , dentry , timeout , do_now ) ;
if ( expired ) {
if ( expired = = dentry )
dput ( dentry ) ;
return expired ;
}
}
return NULL ;
}
/*
* Find an eligible tree to time - out
* A tree is eligible if : -
* - it is unused by any user process
* - it has been unused for exp_timeout time
*/
2018-08-22 07:58:54 +03:00
static struct dentry * autofs_expire_indirect ( struct super_block * sb ,
struct vfsmount * mnt ,
struct autofs_sb_info * sbi ,
int how )
2018-06-08 03:11:13 +03:00
{
unsigned long timeout ;
struct dentry * root = sb - > s_root ;
struct dentry * dentry ;
struct dentry * expired ;
struct dentry * found ;
struct autofs_info * ino ;
if ( ! root )
return NULL ;
timeout = sbi - > exp_timeout ;
dentry = NULL ;
while ( ( dentry = get_next_positive_subdir ( dentry , root ) ) ) {
int flags = how ;
spin_lock ( & sbi - > fs_lock ) ;
ino = autofs_dentry_ino ( dentry ) ;
if ( ino - > flags & AUTOFS_INF_WANT_EXPIRE ) {
spin_unlock ( & sbi - > fs_lock ) ;
continue ;
}
spin_unlock ( & sbi - > fs_lock ) ;
expired = should_expire ( dentry , mnt , timeout , flags ) ;
if ( ! expired )
continue ;
spin_lock ( & sbi - > fs_lock ) ;
ino = autofs_dentry_ino ( expired ) ;
ino - > flags | = AUTOFS_INF_WANT_EXPIRE ;
spin_unlock ( & sbi - > fs_lock ) ;
synchronize_rcu ( ) ;
/* Make sure a reference is not taken on found if
* things have changed .
*/
flags & = ~ AUTOFS_EXP_LEAVES ;
2018-08-22 07:58:48 +03:00
found = should_expire ( expired , mnt , timeout , flags ) ;
2018-06-08 03:11:13 +03:00
if ( ! found | | found ! = expired )
/* Something has changed, continue */
goto next ;
if ( expired ! = dentry )
dput ( dentry ) ;
spin_lock ( & sbi - > fs_lock ) ;
goto found ;
next :
spin_lock ( & sbi - > fs_lock ) ;
ino - > flags & = ~ AUTOFS_INF_WANT_EXPIRE ;
spin_unlock ( & sbi - > fs_lock ) ;
if ( expired ! = dentry )
dput ( expired ) ;
}
return NULL ;
found :
pr_debug ( " returning %p %pd \n " , expired , expired ) ;
ino - > flags | = AUTOFS_INF_EXPIRING ;
init_completion ( & ino - > expire_complete ) ;
spin_unlock ( & sbi - > fs_lock ) ;
return expired ;
}
int autofs_expire_wait ( const struct path * path , int rcu_walk )
{
struct dentry * dentry = path - > dentry ;
struct autofs_sb_info * sbi = autofs_sbi ( dentry - > d_sb ) ;
struct autofs_info * ino = autofs_dentry_ino ( dentry ) ;
int status ;
int state ;
/* Block on any pending expire */
if ( ! ( ino - > flags & AUTOFS_INF_WANT_EXPIRE ) )
return 0 ;
if ( rcu_walk )
return - ECHILD ;
retry :
spin_lock ( & sbi - > fs_lock ) ;
state = ino - > flags & ( AUTOFS_INF_WANT_EXPIRE | AUTOFS_INF_EXPIRING ) ;
if ( state = = AUTOFS_INF_WANT_EXPIRE ) {
spin_unlock ( & sbi - > fs_lock ) ;
/*
* Possibly being selected for expire , wait until
* it ' s selected or not .
*/
schedule_timeout_uninterruptible ( HZ / 10 ) ;
goto retry ;
}
if ( state & AUTOFS_INF_EXPIRING ) {
spin_unlock ( & sbi - > fs_lock ) ;
pr_debug ( " waiting for expire %p name=%pd \n " , dentry , dentry ) ;
status = autofs_wait ( sbi , path , NFY_NONE ) ;
wait_for_completion ( & ino - > expire_complete ) ;
pr_debug ( " expire done status=%d \n " , status ) ;
if ( d_unhashed ( dentry ) )
return - EAGAIN ;
return status ;
}
spin_unlock ( & sbi - > fs_lock ) ;
return 0 ;
}
/* Perform an expiry operation */
int autofs_expire_run ( struct super_block * sb ,
struct vfsmount * mnt ,
struct autofs_sb_info * sbi ,
struct autofs_packet_expire __user * pkt_p )
{
struct autofs_packet_expire pkt ;
struct autofs_info * ino ;
struct dentry * dentry ;
int ret = 0 ;
memset ( & pkt , 0 , sizeof ( pkt ) ) ;
pkt . hdr . proto_version = sbi - > version ;
pkt . hdr . type = autofs_ptype_expire ;
dentry = autofs_expire_indirect ( sb , mnt , sbi , 0 ) ;
if ( ! dentry )
return - EAGAIN ;
pkt . len = dentry - > d_name . len ;
memcpy ( pkt . name , dentry - > d_name . name , pkt . len ) ;
pkt . name [ pkt . len ] = ' \0 ' ;
dput ( dentry ) ;
if ( copy_to_user ( pkt_p , & pkt , sizeof ( struct autofs_packet_expire ) ) )
ret = - EFAULT ;
spin_lock ( & sbi - > fs_lock ) ;
ino = autofs_dentry_ino ( dentry ) ;
/* avoid rapid-fire expire attempts if expiry fails */
2018-08-22 07:58:44 +03:00
ino - > last_used = jiffies ;
2018-06-08 03:11:13 +03:00
ino - > flags & = ~ ( AUTOFS_INF_EXPIRING | AUTOFS_INF_WANT_EXPIRE ) ;
complete_all ( & ino - > expire_complete ) ;
spin_unlock ( & sbi - > fs_lock ) ;
return ret ;
}
int autofs_do_expire_multi ( struct super_block * sb , struct vfsmount * mnt ,
struct autofs_sb_info * sbi , int when )
{
struct dentry * dentry ;
int ret = - EAGAIN ;
if ( autofs_type_trigger ( sbi - > type ) )
dentry = autofs_expire_direct ( sb , mnt , sbi , when ) ;
else
dentry = autofs_expire_indirect ( sb , mnt , sbi , when ) ;
if ( dentry ) {
struct autofs_info * ino = autofs_dentry_ino ( dentry ) ;
const struct path path = { . mnt = mnt , . dentry = dentry } ;
/* This is synchronous because it makes the daemon a
* little easier
*/
ret = autofs_wait ( sbi , & path , NFY_EXPIRE ) ;
spin_lock ( & sbi - > fs_lock ) ;
/* avoid rapid-fire expire attempts if expiry fails */
2018-08-22 07:58:44 +03:00
ino - > last_used = jiffies ;
2018-06-08 03:11:13 +03:00
ino - > flags & = ~ ( AUTOFS_INF_EXPIRING | AUTOFS_INF_WANT_EXPIRE ) ;
complete_all ( & ino - > expire_complete ) ;
spin_unlock ( & sbi - > fs_lock ) ;
dput ( dentry ) ;
}
return ret ;
}
/*
* Call repeatedly until it returns - EAGAIN , meaning there ' s nothing
* more to be done .
*/
int autofs_expire_multi ( struct super_block * sb , struct vfsmount * mnt ,
struct autofs_sb_info * sbi , int __user * arg )
{
int do_now = 0 ;
if ( arg & & get_user ( do_now , arg ) )
return - EFAULT ;
return autofs_do_expire_multi ( sb , mnt , sbi , do_now ) ;
}