Messages in this thread Patch in this message | | | Date | Fri, 18 Sep 1998 22:49:08 -0400 | From | Bill Hawes <> | Subject | updated patch for 2.1.122 msdos-fs |
| |
I found another problem in the msdos-fs rename code and have attached an updated patch. There was a missing copy of the i_nlink value when changing inodes for a cross-directories move; the default of 1 worked OK for files but was disastrous for directories, sometimes resulting in cyclical structures.
Regards, Bill--- linux-2.1.122/fs/msdos/namei.c.old Wed Sep 16 21:59:38 1998 +++ linux-2.1.122/fs/msdos/namei.c Fri Sep 18 15:28:55 1998 @@ -266,6 +266,7 @@ res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &bh, &de, &ino); + if (res == -ENOENT) goto add; if (res < 0) @@ -275,7 +276,7 @@ /* try to get the inode */ res = -EACCES; - inode = iget(dir->i_sb, ino); + inode = iget(sb, ino); if (!inode) goto out; if (!inode->i_sb || @@ -289,9 +290,9 @@ iput(inode); goto out; } - res = 0; add: d_add(dentry, inode); + res = 0; out: return res; } @@ -446,14 +447,14 @@ if (dir->i_dev != inode->i_dev || dir == inode) printk("msdos_rmdir: impossible condition\n"); /* - * Prune any child dentries, then verify that - * the directory is empty and not in use. + * Check whether the directory is empty, then prune + * any child dentries and make sure it's not in use. */ - shrink_dcache_parent(dentry); res = msdos_empty(inode); if (res) goto rmdir_done; res = -EBUSY; + shrink_dcache_parent(dentry); if (dentry->d_count > 1) { #ifdef MSDOS_DEBUG printk("msdos_rmdir: %s/%s busy, d_count=%d\n", @@ -476,6 +477,7 @@ de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); res = 0; + rmdir_done: fat_brelse(sb, bh); return res; @@ -499,18 +501,21 @@ return res; is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.'); fat_lock_creation(); - if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) { - fat_unlock_creation(); - /* N.B. does this need to be released on the other path? */ - fat_brelse(sb, bh); - return -EEXIST; - } + if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) + goto out_exist; + res = msdos_create_entry(dir,msdos_name,1,is_hid, &inode); if (res < 0) goto out_unlock; + dir->i_nlink++; inode->i_nlink = 2; /* no need to mark them dirty */ MSDOS_I(inode)->i_busy = 1; /* prevent lookups */ + /* + * Instantiate the dentry now, in case we need to cleanup. + */ + d_instantiate(dentry, inode); + if ((res = fat_add_cluster(inode)) < 0) goto mkdir_error; if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0) @@ -521,9 +526,9 @@ dot->i_nlink = inode->i_nlink; mark_inode_dirty(dot); iput(dot); + if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0) goto mkdir_error; - fat_unlock_creation(); dot->i_size = dir->i_size; MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start; MSDOS_I(dot)->i_logstart = MSDOS_I(dir)->i_logstart; @@ -531,15 +536,22 @@ mark_inode_dirty(dot); MSDOS_I(inode)->i_busy = 0; iput(dot); - d_instantiate(dentry, inode); - return 0; + res = 0; -mkdir_error: - if (msdos_rmdir(dir,dentry) < 0) - fat_fs_panic(dir->i_sb,"rmdir in mkdir failed"); out_unlock: fat_unlock_creation(); return res; + +mkdir_error: + printk("msdos_mkdir: error=%d, attempting cleanup\n", res); + if (msdos_rmdir(dir,dentry) < 0) + fat_fs_panic(dir->i_sb,"rmdir in mkdir failed"); + goto out_unlock; + +out_exist: + fat_brelse(sb, bh); + res = -EEXIST; + goto out_unlock; } /***** Unlink a file */ @@ -636,10 +648,14 @@ if ((old_de->attr & ATTR_SYS)) goto out_error; + if (S_ISDIR(new_inode->i_mode)) { + /* make sure it's empty */ + error = msdos_empty(new_inode); + if (error) + goto out_error; #ifdef MSDOS_CHECK_BUSY - /* check for a busy dentry */ - error = -EBUSY; - if (new_dentry->d_count > 1) { + /* check for a busy dentry */ + error = -EBUSY; shrink_dcache_parent(new_dentry); if (new_dentry->d_count > 1) { printk("msdos_rename_same: %s/%s busy, count=%d\n", @@ -647,20 +663,19 @@ new_dentry->d_count); goto out_error; } - } #endif - - if (S_ISDIR(new_inode->i_mode)) { new_dir->i_nlink--; mark_inode_dirty(new_dir); } new_inode->i_nlink = 0; MSDOS_I(new_inode)->i_busy = 1; mark_inode_dirty(new_inode); -#ifdef MSDOS_CHECK_BUSY - /* d_delete the dentry, as we killed its inode */ - d_delete(new_dentry); -#endif + /* + * Make it negative if it's not busy; + * otherwise let d_move() drop it. + */ + if (new_dentry->d_count == 1) + d_delete(new_dentry); new_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, new_bh, 1); @@ -763,16 +778,22 @@ } #endif if (S_ISDIR(new_inode->i_mode)) { + /* make sure it's empty */ + error = msdos_empty(new_inode); + if (error) + goto out_new; new_dir->i_nlink--; mark_inode_dirty(new_dir); } new_inode->i_nlink = 0; MSDOS_I(new_inode)->i_busy = 1; mark_inode_dirty(new_inode); -#ifdef MSDOS_CHECK_BUSY - /* d_delete the dentry, as we killed its inode */ - d_delete(new_dentry); -#endif + /* + * Make it negative if it's not busy; + * otherwise let d_move() drop it. + */ + if (new_dentry->d_count == 1) + d_delete(new_dentry); new_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, new_bh, 1); fat_brelse(sb, new_bh); @@ -785,8 +806,13 @@ if (S_ISDIR(old_inode->i_mode)) { error = fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh, &dotdot_de, &dotdot_ino, SCAN_ANY); - if (error < 0) + if (error < 0) { + printk(KERN_WARNING + "MSDOS: %s/%s, get dotdot failed, ret=%d\n", + old_dentry->d_parent->d_name.name, + old_dentry->d_name.name, error); goto rename_done; + } error = -EIO; dotdot_inode = iget(sb, dotdot_ino); if (!dotdot_inode) @@ -804,12 +830,19 @@ free_inode = iget(sb, free_ino); if (!free_inode) goto out_iput; + /* make sure it's not busy! */ + if (MSDOS_I(free_inode)->i_busy) + printk(KERN_ERR "msdos_rename_diff: new inode %ld busy!\n", + (ino_t) free_ino); + if (!list_empty(&free_inode->i_dentry)) + printk("msdos_rename_diff: free inode has aliases??\n"); msdos_read_inode(free_inode); /* * Make sure the old dentry isn't busy, * as we need to change inodes ... */ + error = -EBUSY; if (old_dentry->d_count > 1) { shrink_dcache_parent(old_dentry); if (old_dentry->d_count > 1) { @@ -825,6 +858,7 @@ d_delete(old_dentry); free_inode->i_mode = old_inode->i_mode; + free_inode->i_nlink = old_inode->i_nlink; free_inode->i_size = old_inode->i_size; free_inode->i_blocks = old_inode->i_blocks; free_inode->i_mtime = old_inode->i_mtime; @@ -836,16 +870,21 @@ MSDOS_I(free_inode)->i_logstart = MSDOS_I(old_inode)->i_logstart; MSDOS_I(free_inode)->i_attrs = MSDOS_I(old_inode)->i_attrs; + /* release the old inode's resources */ + MSDOS_I(old_inode)->i_start = 0; + MSDOS_I(old_inode)->i_logstart = 0; + old_inode->i_nlink = 0; + /* * Install the new inode ... */ d_instantiate(old_dentry, free_inode); + fat_mark_buffer_dirty(sb, free_bh, 1); fat_cache_inval_inode(old_inode); mark_inode_dirty(old_inode); old_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, old_bh, 1); - fat_mark_buffer_dirty(sb, free_bh, 1); iput(old_inode); /* a directory? */ @@ -854,13 +893,13 @@ MSDOS_I(dotdot_inode)->i_logstart = MSDOS_I(new_dir)->i_logstart; dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart); dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16); - mark_inode_dirty(dotdot_inode); - fat_mark_buffer_dirty(sb, dotdot_bh, 1); old_dir->i_nlink--; new_dir->i_nlink++; /* no need to mark them dirty */ dotdot_inode->i_nlink = new_dir->i_nlink; + mark_inode_dirty(dotdot_inode); iput(dotdot_inode); + fat_mark_buffer_dirty(sb, dotdot_bh, 1); fat_brelse(sb, dotdot_bh); } | |