The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: Andrew Morton <andrewm@uow.edu.au>
To: malfet@gw.mipt.sw.ru
Cc: linux-kernel@vger.kernel.org
Subject: Re: Question about ext2
Date: Fri, 13 Jul 2001 18:42:59 +1000	[thread overview]
Message-ID: <3B4EB493.DC805F45@uow.edu.au> (raw)
In-Reply-To: <20010713120840.A9431@srv.mipt.sw.ru>

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;
}

  reply	other threads:[~2001-07-13  8:43 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2001-07-13  8:08 Question about ext2 malfet
2001-07-13  8:42 ` Andrew Morton [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=3B4EB493.DC805F45@uow.edu.au \
    --to=andrewm@uow.edu.au \
    --cc=linux-kernel@vger.kernel.org \
    --cc=malfet@gw.mipt.sw.ru \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox