--- linux/fs/dcache.c.orig Sun Jan 16 00:41:21 2000 +++ linux/fs/dcache.c Sun Jan 16 01:17:39 2000 @@ -302,7 +302,7 @@ * child dentries were freed. This allows a non-destructive * test for unmounting a device. */ -int is_root_busy(struct dentry *root) +int __is_root_busy(struct dentry *root,int ignore_mount_points) { struct dentry *this_parent = root; struct list_head *next; @@ -314,15 +314,20 @@ while (next != &this_parent->d_subdirs) { struct list_head *tmp = next; struct dentry *dentry = list_entry(tmp, struct dentry, d_child); + int effective_count; next = tmp->next; + /* Mount points may not count */ + effective_count = dentry->d_count; + if (ignore_mount_points && dentry->d_mounts != dentry) + effective_count--; /* Decrement count for unused children */ - count += (dentry->d_count - 1); + count += (effective_count - 1); if (!list_empty(&dentry->d_subdirs)) { this_parent = dentry; goto repeat; } /* root is busy if any leaf is busy */ - if (dentry->d_count) + if (effective_count) return 1; } /* @@ -334,6 +339,11 @@ goto resume; } return (count > 1); /* remaining users? */ +} + +int is_root_busy(struct dentry *root) +{ + return __is_root_busy(root,0); } /* --- linux/fs/super.c.orig Sat Jan 15 12:53:27 2000 +++ linux/fs/super.c Sun Jan 16 01:37:10 2000 @@ -307,11 +307,15 @@ struct proc_nfs_info *nfs_infop; struct nfs_server *nfss; int len = 0; + char *path,*buffer = (char *) __get_free_page(GFP_KERNEL); + if (!buffer) return 0; while ( tmp && len < PAGE_SIZE - 160) { + path = d_path(tmp->mnt_sb->s_root,buffer,PAGE_SIZE); len += sprintf( buf + len, "%s %s %s %s", - tmp->mnt_devname, tmp->mnt_dirname, tmp->mnt_sb->s_type->name, + tmp->mnt_devname, path, + tmp->mnt_sb->s_type->name, tmp->mnt_flags & MS_RDONLY ? "ro" : "rw" ); for (fs_infop = fs_info; fs_infop->flag; fs_infop++) { if (tmp->mnt_flags & fs_infop->flag) { @@ -368,6 +372,7 @@ tmp = tmp->mnt_next; } + free_page((unsigned long) buffer); return len; } @@ -467,6 +472,34 @@ return NULL; } +static void detach_mount_points(struct super_block *sb) +{ + struct super_block *s; + + for (s = sb_entry(super_blocks.next); s != sb_entry(&super_blocks); + s = sb_entry(s->s_list.next)) { + struct dentry *root,*covered; + + if (!s->s_dev || s == sb) continue; + root = s->s_root; + covered = root->d_covers; + if (covered->d_sb != sb) continue; + if (covered->d_inode->i_count != 1) { + printk(KERN_CRIT "detach_mount_points: inode %ld on %s " + "count %d (must be 1)\n",covered->d_inode->i_ino, + kdevname(sb->s_dev),covered->d_inode->i_count); + continue; + } + covered->d_mounts = covered; /* l'art pour l'art ... */ + dput(covered); + root->d_covers = root; + printk(KERN_DEBUG "detached %s (count %d)",kdevname(s->s_dev), + root->d_count); + printk(" from %s (count %d)\n",kdevname(sb->s_dev), + sb->s_root->d_count); + } +} + asmlinkage long sys_ustat(dev_t dev, struct ustat * ubuf) { struct super_block *s; @@ -612,8 +645,15 @@ struct dentry * root = sb->s_root; struct dentry * covered = root->d_covers; - if (root->d_count != 1) - return -EBUSY; + printk(KERN_DEBUG "d_umount: ref %d\n",root->d_count); + if (root->d_count != 1) { + if (__is_root_busy(sb->s_root,1)) return -EBUSY; + else { + detach_mount_points(sb); + shrink_dcache_sb(sb); + fsync_dev(sb->s_dev); + } + } if (root->d_inode->i_state) return -EBUSY; @@ -681,7 +721,8 @@ shrink_dcache_sb(sb); fsync_dev(dev); - if (dev==ROOT_DEV && !unmount_root) { + printk(KERN_DEBUG "do_umount: ref %d\n",sb->s_root->d_count); + if (dev==ROOT_DEV && !unmount_root && __is_root_busy(sb->s_root,1)) { /* * Special case for "unmounting" root ... * we just try to remount it readonly. @@ -1273,7 +1314,7 @@ } return 0; } - printk(KERN_ERR "error %d\n",PTR_ERR(bdev)); + printk(KERN_ERR "error %ld\n",PTR_ERR(bdev)); return error; } remove_vfsmnt(old_root_dev); --- linux/include/linux/dcache.h.orig Fri Jan 7 01:21:23 2000 +++ linux/include/linux/dcache.h Sun Jan 16 01:17:16 2000 @@ -150,6 +150,7 @@ /* test whether root is busy without destroying dcache */ extern int is_root_busy(struct dentry *); +extern int __is_root_busy(struct dentry *,int ignore_mount_points); /* * This adds the entry to the hash queues.