All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.