* Question about ext2
@ 2001-07-13 8:08 malfet
2001-07-13 8:42 ` Andrew Morton
0 siblings, 1 reply; 8+ messages in thread
From: malfet @ 2001-07-13 8:08 UTC (permalink / raw)
To: linux-kernel
Hi all!
I look up in implementation in ext2_rename and see the following statment:
if (S_ISDIR(old_inode->i_mode)) {
if (new_inode) {
retval = -ENOTEMPTY;
if (!empty_dir (new_inode))
goto end_rename;
}
But I don't see any checkl like S_ISDIR on new_inode neither in ext2_rename neither in empty_dir. Is this bug? And, anyway, can I rename directory into not-empty file?
Thanks in advance,
Nikita
P.S. Please, CC answer on malfet@mipt.sw.ru, because I'm not subscribed on this list
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: Question about ext2 2001-07-13 8:08 Question about ext2 malfet @ 2001-07-13 8:42 ` Andrew Morton 2001-07-13 9:57 ` Re[2]: " malfet 2001-07-13 10:57 ` Alexander Viro 0 siblings, 2 replies; 8+ messages in thread From: Andrew Morton @ 2001-07-13 8:42 UTC (permalink / raw) To: malfet; +Cc: linux-kernel malfet@gw.mipt.sw.ru wrote: > > Hi all! > I look up in implementation in ext2_rename and see the following statment: > if (S_ISDIR(old_inode->i_mode)) { > if (new_inode) { > retval = -ENOTEMPTY; > if (!empty_dir (new_inode)) > goto end_rename; > } > But I don't see any checkl like S_ISDIR on new_inode neither in > ext2_rename neither in empty_dir. Is this bug? And, anyway, can > I rename directory into not-empty file? It's implicit - rename can only rename files to files, and directories to directories. So the check is made at a higher level. Consequently when we get to ext2_rename, if we find that the old inode is a directory, we *know* that the new one is a directory as well. I recently spent an hour decrypting this function. Here is a commented version which may prove helpful. It is from a non-mainline branch of ext3, but it's much the same. static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry) { handle_t *handle; /* old_inode is the thing we're renaming */ struct inode * old_inode = old_dentry->d_inode; /* new_inode is what we're renaming it to (may be NULL) */ struct inode * new_inode = new_dentry->d_inode; /* dir_bh is the buffer which contains old_inode's ".." entry */ struct buffer_head * dir_bh = NULL; /* dir_de is the old_inode's ".." de. Points into dir_bh->b_data */ struct ext3_dir_entry_2 * dir_de = NULL; /* old_bh contains the old entry's de */ struct buffer_head * old_bh; /* old_de points to the old entry's de, inside old_bh->b_data */ struct ext3_dir_entry_2 * old_de; int err = -ENOENT; old_bh = NULL; handle = ext3_journal_start(old_dir, 2 * EXT3_DATA_TRANS_BLOCKS + 2); if (IS_ERR(handle)) return PTR_ERR(handle); /* Find the current directory entry's bh and de */ old_bh = ext3_find_entry (old_dentry, &old_de); if (!old_bh) goto end_rename; if (S_ISDIR(old_inode->i_mode)) { /* * If the thing we're renaming is a directory, we'll need to * change its ".." to point to a different parent. Go find * the ".." directory entry */ err = -EIO; dir_de = ext3_dotdot(handle, old_inode, &dir_bh); if (!dir_de) goto end_rename; } if (new_inode) { /* We're overwriting another object */ struct buffer_head * new_bh; struct ext3_dir_entry_2 * new_de; /* If the renamee is a dir, then the victim MUST be a dir. * It must not have any entries */ err = -ENOTEMPTY; if (dir_de && !empty_dir (new_inode)) goto out_dir; /* Go find the buffer and de for the victim */ err = -ENOENT; new_bh = ext3_find_entry (new_dentry, &new_de); if (!new_bh) goto out_dir; /* Temporarily bump the renamee's link count. Dunno why */ ext3_inc_count(handle, old_inode); /* Overwrite the victim's directory info */ ext3_set_link(handle, new_dir, new_de, new_bh, old_inode); new_inode->i_ctime = CURRENT_TIME; /* If renamee is a dir then the victim is a dir. Drop nlink * to account for the "." entry */ if (dir_de) new_inode->i_nlink--; /* victim loses a refcount. If it is a dir, it will be * removed altogether, in do_rename->dput->iput */ ext3_dec_count(handle, new_inode); /* Add an orphan record into this transaction. If the victim * dir is huge, iput's truncate may cross multiple transactions. * We remove the orphan record inside the transaction which * actually releases the inode */ if (!new_inode->i_nlink) ext3_orphan_add(handle, new_inode); } else { /* The new name is not currently used */ if (dir_de) { /* Moving a directory will add an extra ref to its * parent because of the ".." entry */ err = -EMLINK; if (new_dir->i_nlink >= EXT3_LINK_MAX) goto out_dir; } /* Temporarily bump the renamee's link count. Dunno why */ ext3_inc_count(handle, old_inode); /* Add the renamee to its new directory */ err = ext3_add_entry (handle, new_dentry, old_inode); if (err) { ext3_dec_count(handle, old_inode); goto out_dir; } /* If we just moved a directory, parent gets another ref for * ".." */ if (dir_de) ext3_inc_count(handle, new_dir); } /* Remove the renamee's old directory entry */ ext3_delete_entry(handle, old_dir, old_de, old_bh); old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; ext3_mark_inode_dirty(handle, old_dir); brelse (old_bh); old_inode->i_ctime = CURRENT_TIME; /* Drop the temp refcount. Also marks the renamee's inode dirty */ ext3_dec_count(handle, old_inode); if (dir_bh) { /* We moved a directory. Make its ".." entry point to the new * parent */ ext3_set_link(handle, old_inode, dir_de, dir_bh, new_dir); /* The old parent no longer has the renamee's ".." pointing * to it */ ext3_dec_count(handle, old_dir); } ext3_journal_stop(handle, old_dir); return 0; out_dir: brelse (dir_bh); end_rename: brelse (old_bh); ext3_journal_stop(handle, old_dir); return err; } ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re[2]: Question about ext2 2001-07-13 8:42 ` Andrew Morton @ 2001-07-13 9:57 ` malfet 2001-07-13 10:23 ` Alexander Viro 2001-07-13 10:57 ` Alexander Viro 1 sibling, 1 reply; 8+ messages in thread From: malfet @ 2001-07-13 9:57 UTC (permalink / raw) To: Andrew Morton; +Cc: linux-kernel > It's implicit - rename can only rename files to files, and > directories to directories. So the check is made at a higher > level. Consequently when we get to ext2_rename, if we find > that the old inode is a directory, we *know* that the new > one is a directory as well. Ok, let's look at the higher levels We call vfs_rename (old_dir, old_dentry, new_dir, new_dentry), which checks, wether old_dentry->d_inode is directory If so it calls vfs_rename_dir, if not calls vfs_rename_other In vfs_rename_dir checked if we can delete old_dentry (by means of may_delete), then if new_dentry->d_inode is present, check if we can delete it But it is not checked, wether new_dentry->d_inode directory or not I don't find this check in chain started from syscall and ends in ext2_rename Correct me if I'm wrong. Thanks in advance, Nikita ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Re[2]: Question about ext2 2001-07-13 9:57 ` Re[2]: " malfet @ 2001-07-13 10:23 ` Alexander Viro 0 siblings, 0 replies; 8+ messages in thread From: Alexander Viro @ 2001-07-13 10:23 UTC (permalink / raw) To: malfet; +Cc: Andrew Morton, linux-kernel On Fri, 13 Jul 2001 malfet@gw.mipt.sw.ru wrote: > I don't find this check in chain started from syscall and ends in ext2_rename > Correct me if I'm wrong. Take a look at may_delete(). Heck, it's even documented there - * 7. If we were asked to remove a directory and victim isn't one - ENOTDIR. * 8. If we were asked to remove a non-directory and victim isn't one - EISDIR. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Question about ext2 2001-07-13 8:42 ` Andrew Morton 2001-07-13 9:57 ` Re[2]: " malfet @ 2001-07-13 10:57 ` Alexander Viro 2001-07-13 20:22 ` Kai Henningsen 1 sibling, 1 reply; 8+ messages in thread From: Alexander Viro @ 2001-07-13 10:57 UTC (permalink / raw) To: Andrew Morton; +Cc: malfet, linux-kernel On Fri, 13 Jul 2001, Andrew Morton wrote: > I recently spent an hour decrypting this function. Here is > a commented version which may prove helpful. It is from a > non-mainline branch of ext3, but it's much the same. <raised brows> OK, here's an algorithm used in rename() - hopefully it answers "dunno why" part. find directory entry of source or fail (-ENOENT) if moving directory find directory entry of ".." in object we move or fail (-EIO) if there is a victim if moving directory and victim is not empty - fail (-ENOTEMPTY) find directory entry of victim or fail (-ENOENT) grab a reference to old_inode and redirect that entry to it if victim is a directory drop reference to victim twice else drop reference to victim once else if moving directory check that we can grab an extra reference to new parent or fail with -EMLINK grab a reference to old inode create a link to old_inode (with new name) if link creation failed drop a reference we've just got and fail if it was a directory grab a reference to new parent /* Now we had created a new link to object we are moving and link to victim (if any) is destroyed. */ delete old link and drop the reference held by it. if moving directory redirect ".." to new parent and drop reference to old parent Notice that we always grab a reference before creating a link and drop it only after the link removal. All checks are done before any directory modifications start - that way we can bail out if they fail. The only really obscure part is dropping an extra reference if victim is a directory - then we know that we are cannibalizing the last external link to it and the only link that remains is victim's ".". We don't want it to prevent victim's removal, so we drive i_nlink of victim to zero. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Question about ext2 2001-07-13 10:57 ` Alexander Viro @ 2001-07-13 20:22 ` Kai Henningsen 2001-07-14 5:58 ` Alexander Viro 0 siblings, 1 reply; 8+ messages in thread From: Kai Henningsen @ 2001-07-13 20:22 UTC (permalink / raw) To: viro; +Cc: linux-kernel viro@math.psu.edu (Alexander Viro) wrote on 13.07.01 in <Pine.GSO.4.21.0107130623510.17323-100000@weyl.math.psu.edu>: > The only really obscure part is dropping an extra reference if victim is > a directory - then we know that we are cannibalizing the last external > link to it and the only link that remains is victim's ".". We don't want > it to prevent victim's removal, so we drive i_nlink of victim to zero. Does this stuff work right with those cases which do linkcount=1 either because the fs doesn't have a link count, or because the real link count has grown too large? MfG Kai ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Question about ext2 2001-07-13 20:22 ` Kai Henningsen @ 2001-07-14 5:58 ` Alexander Viro 2001-07-14 11:37 ` Kai Henningsen 0 siblings, 1 reply; 8+ messages in thread From: Alexander Viro @ 2001-07-14 5:58 UTC (permalink / raw) To: Kai Henningsen; +Cc: linux-kernel On 13 Jul 2001, Kai Henningsen wrote: > viro@math.psu.edu (Alexander Viro) wrote on 13.07.01 in <Pine.GSO.4.21.0107130623510.17323-100000@weyl.math.psu.edu>: > > > The only really obscure part is dropping an extra reference if victim is > > a directory - then we know that we are cannibalizing the last external > > link to it and the only link that remains is victim's ".". We don't want > > it to prevent victim's removal, so we drive i_nlink of victim to zero. > > Does this stuff work right with those cases which do linkcount=1 either > because the fs doesn't have a link count, or because the real link count > has grown too large? It doesn't. If fs doesn't have link count you are very likely to need other ways to deal with rename() anyway (e.g. you are pretty likely to have part of metadata stored in directory entry). If you are playing with "set i_nlink to 1 if it's too large" (which works only for directories, BTW) - change according to your encoding scheme for link count. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Question about ext2 2001-07-14 5:58 ` Alexander Viro @ 2001-07-14 11:37 ` Kai Henningsen 0 siblings, 0 replies; 8+ messages in thread From: Kai Henningsen @ 2001-07-14 11:37 UTC (permalink / raw) To: viro; +Cc: linux-kernel viro@math.psu.edu (Alexander Viro) wrote on 14.07.01 in <Pine.GSO.4.21.0107140151420.19749-100000@weyl.math.psu.edu>: > On 13 Jul 2001, Kai Henningsen wrote: > > > viro@math.psu.edu (Alexander Viro) wrote on 13.07.01 in > > <Pine.GSO.4.21.0107130623510.17323-100000@weyl.math.psu.edu>: > > > > > The only really obscure part is dropping an extra reference if victim is > > > a directory - then we know that we are cannibalizing the last external > > > link to it and the only link that remains is victim's ".". We don't want > > > it to prevent victim's removal, so we drive i_nlink of victim to zero. > > > > Does this stuff work right with those cases which do linkcount=1 either > > because the fs doesn't have a link count, or because the real link count > > has grown too large? > > It doesn't. If fs doesn't have link count you are very likely to need > other ways to deal with rename() anyway (e.g. you are pretty likely to > have part of metadata stored in directory entry). If you are playing > with "set i_nlink to 1 if it's too large" (which works only for directories, > BTW) - change according to your encoding scheme for link count. You are, of course, aware that ext2 (or at least current patches to ext2, I'm not sure if this particular thing has gone in yet) does use that scheme. MfG Kai ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2001-07-14 12:38 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2001-07-13 8:08 Question about ext2 malfet 2001-07-13 8:42 ` Andrew Morton 2001-07-13 9:57 ` Re[2]: " malfet 2001-07-13 10:23 ` Alexander Viro 2001-07-13 10:57 ` Alexander Viro 2001-07-13 20:22 ` Kai Henningsen 2001-07-14 5:58 ` Alexander Viro 2001-07-14 11:37 ` Kai Henningsen
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox