--- linux/fs/dcache.c.orig Sun Jan 16 00:41:21 2000 +++ linux/fs/dcache.c Sun Jan 16 14:26:05 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; } /* @@ -336,6 +341,11 @@ return (count > 1); /* remaining users? */ } +int is_root_busy(struct dentry *root) +{ + return __is_root_busy(root,0); +} + /* * Search the dentry child list for the specified parent, * and move any unused dentries to the end of the unused @@ -704,7 +714,8 @@ /* * "buflen" should be PAGE_SIZE or more. */ -char * d_path(struct dentry *dentry, char *buffer, int buflen) +static char * __d_path(struct dentry *dentry, char *buffer, int buflen, + kdev_t *need_dev) { char * end = buffer+buflen; char * retval; @@ -712,6 +723,8 @@ *--end = '\0'; buflen--; + if (need_dev) *need_dev = 0; + if (!IS_ROOT(dentry) && list_empty(&dentry->d_hash)) { buflen -= 10; end -= 10; @@ -730,8 +743,10 @@ break; dentry = dentry->d_covers; parent = dentry->d_parent; - if (dentry == parent) + if (dentry == parent) { + if (need_dev) *need_dev = dentry->d_sb->s_dev; break; + } namelen = dentry->d_name.len; buflen -= namelen + 1; if (buflen < 0) @@ -743,6 +758,28 @@ dentry = parent; } return retval; +} + +char * d_path(struct dentry *dentry, char *buffer, int buflen) +{ + return __d_path(dentry,buffer,buflen,NULL); +} + +char * d_dev_path(struct dentry *dentry, char *buffer, int buflen) +{ + char *path,*prefix; + kdev_t need_dev; + int length; + + path = __d_path(dentry,buffer,buflen,&need_dev); + if (!need_dev) return path; + prefix = kdevname(need_dev); + length = strlen(prefix); + if (path >= buffer+length) { + path -= length; + memcpy(path,prefix,length); + } + return path; } /* --- linux/fs/super.c.orig Sat Jan 15 12:53:27 2000 +++ linux/fs/super.c Sun Jan 16 14:52:28 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_dev_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,19 +645,28 @@ struct dentry * root = sb->s_root; struct dentry * covered = root->d_covers; - if (root->d_count != 1) + printk(KERN_DEBUG "d_umount: ref %d\n",root->d_count); + if (root->d_count != 1 && __is_root_busy(sb->s_root,1)) return -EBUSY; if (root->d_inode->i_state) return -EBUSY; - sb->s_root = NULL; - if (covered != root) { root->d_covers = root; covered->d_mounts = covered; dput(covered); } + + /* fsync_dev may sleep, so we must disconnect the dentries first */ + if (root->d_count != 1) { + detach_mount_points(sb); + shrink_dcache_sb(sb); + fsync_dev(sb->s_dev); + } + + sb->s_root = NULL; + dput(root); return 0; } @@ -681,7 +723,9 @@ 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 (sb->s_root == sb->s_root->d_parent && !unmount_root && + __is_root_busy(sb->s_root,1)) { /* * Special case for "unmounting" root ... * we just try to remount it readonly. @@ -1273,7 +1317,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 14:16:26 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. @@ -176,6 +177,7 @@ /* write full pathname into buffer and return start of pathname */ extern char * d_path(struct dentry *, char *, int); +extern char * d_dev_path(struct dentry *dentry, char *buffer, int buflen); /* Allocation counts.. */ static __inline__ struct dentry * dget(struct dentry *dentry)