Skip to content

Commit

Permalink
[PATCH] autofs4: fix false negative return from expire
Browse files Browse the repository at this point in the history
Fix the case where an expire returns busy on a tree mount when it is in fact
not busy.  This case was overlooked when the patch to prevent the expiring
away of "scaffolding" directories for tree mounts was applied.

The problem arises when a tree of mounts is a member of a map with other keys.
 The current logic will not expire the tree if any other mount in the map is
busy.  The solution is to maintain a "minimum" use count for each autofs
dentry and compare this to the actual dentry usage count during expire.

Signed-off-by: Ian Kent <raven@themaw.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
  • Loading branch information
Ian Kent authored and Linus Torvalds committed Mar 27, 2006
1 parent 1ce12ba commit 1aff3c8
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 11 deletions.
1 change: 1 addition & 0 deletions fs/autofs4/autofs_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ struct autofs_info {

struct autofs_sb_info *sbi;
unsigned long last_used;
atomic_t count;

mode_t mode;
size_t size;
Expand Down
34 changes: 25 additions & 9 deletions fs/autofs4/expire.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
unsigned long timeout,
int do_now)
{
struct autofs_info *ino;
struct dentry *p;

DPRINTK("top %p %.*s",
Expand All @@ -110,14 +111,6 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
if (!simple_positive(top))
return 1;

/* Timeout of a tree mount is determined by its top dentry */
if (!autofs4_can_expire(top, timeout, do_now))
return 1;

/* Is someone visiting anywhere in the tree ? */
if (may_umount_tree(mnt))
return 1;

spin_lock(&dcache_lock);
for (p = top; p; p = next_dentry(p, top)) {
/* Negative dentry - give up */
Expand All @@ -130,17 +123,40 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
p = dget(p);
spin_unlock(&dcache_lock);

/*
* Is someone visiting anywhere in the subtree ?
* If there's no mount we need to check the usage
* count for the autofs dentry.
*/
ino = autofs4_dentry_ino(p);
if (d_mountpoint(p)) {
/* First busy => tree busy */
if (autofs4_mount_busy(mnt, p)) {
dput(p);
return 1;
}
} else {
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 (atomic_read(&p->d_count) > ino_count) {
dput(p);
return 1;
}
}
dput(p);
spin_lock(&dcache_lock);
}
spin_unlock(&dcache_lock);

/* Timeout of a tree mount is ultimately determined by its top dentry */
if (!autofs4_can_expire(top, timeout, do_now))
return 1;

return 0;
}

Expand Down
12 changes: 11 additions & 1 deletion fs/autofs4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino,
ino->size = 0;

ino->last_used = jiffies;
atomic_set(&ino->count, 0);

ino->sbi = sbi;

Expand All @@ -64,10 +65,19 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino,

void autofs4_free_ino(struct autofs_info *ino)
{
struct autofs_info *p_ino;

if (ino->dentry) {
ino->dentry->d_fsdata = NULL;
if (ino->dentry->d_inode)
if (ino->dentry->d_inode) {
struct dentry *parent = ino->dentry->d_parent;
if (atomic_dec_and_test(&ino->count)) {
p_ino = autofs4_dentry_ino(parent);
if (p_ino && parent != ino->dentry)
atomic_dec(&p_ino->count);
}
dput(ino->dentry);
}
ino->dentry = NULL;
}
if (ino->free)
Expand Down
23 changes: 22 additions & 1 deletion fs/autofs4/root.c
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ static int autofs4_dir_symlink(struct inode *dir,
{
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
struct autofs_info *p_ino;
struct inode *inode;
char *cp;

Expand Down Expand Up @@ -523,6 +524,10 @@ static int autofs4_dir_symlink(struct inode *dir,

dentry->d_fsdata = ino;
ino->dentry = dget(dentry);
atomic_inc(&ino->count);
p_ino = autofs4_dentry_ino(dentry->d_parent);
if (p_ino && dentry->d_parent != dentry)
atomic_inc(&p_ino->count);
ino->inode = inode;

dir->i_mtime = CURRENT_TIME;
Expand All @@ -549,11 +554,17 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry)
{
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
struct autofs_info *p_ino;

/* This allows root to remove symlinks */
if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) )
return -EACCES;

if (atomic_dec_and_test(&ino->count)) {
p_ino = autofs4_dentry_ino(dentry->d_parent);
if (p_ino && dentry->d_parent != dentry)
atomic_dec(&p_ino->count);
}
dput(ino->dentry);

dentry->d_inode->i_size = 0;
Expand All @@ -570,6 +581,7 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
{
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
struct autofs_info *p_ino;

if (!autofs4_oz_mode(sbi))
return -EACCES;
Expand All @@ -584,8 +596,12 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);

if (atomic_dec_and_test(&ino->count)) {
p_ino = autofs4_dentry_ino(dentry->d_parent);
if (p_ino && dentry->d_parent != dentry)
atomic_dec(&p_ino->count);
}
dput(ino->dentry);

dentry->d_inode->i_size = 0;
dentry->d_inode->i_nlink = 0;

Expand All @@ -599,6 +615,7 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
struct autofs_info *p_ino;
struct inode *inode;

if ( !autofs4_oz_mode(sbi) )
Expand All @@ -621,6 +638,10 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode)

dentry->d_fsdata = ino;
ino->dentry = dget(dentry);
atomic_inc(&ino->count);
p_ino = autofs4_dentry_ino(dentry->d_parent);
if (p_ino && dentry->d_parent != dentry)
atomic_inc(&p_ino->count);
ino->inode = inode;
dir->i_nlink++;
dir->i_mtime = CURRENT_TIME;
Expand Down

0 comments on commit 1aff3c8

Please sign in to comment.