/*
 * process, that is going to call fix_nodes/do_balance must hold only
 * one path. If it holds 2 or more, it can get into endless waiting in
 * get_empty_nodes or its clones
 */
static int reiserfs_rename(struct inode *old_dir, struct dentry *old_dentry,
			   struct inode *new_dir, struct dentry *new_dentry)
{
	int retval;
	INITIALIZE_PATH(old_entry_path);
	INITIALIZE_PATH(new_entry_path);
	INITIALIZE_PATH(dot_dot_entry_path);
	struct item_head new_entry_ih, old_entry_ih, dot_dot_ih;
	struct reiserfs_dir_entry old_de, new_de, dot_dot_de;
	struct inode *old_inode, *new_dentry_inode;
	struct reiserfs_transaction_handle th;
	int jbegin_count;
	umode_t old_inode_mode;
	unsigned long savelink = 1;
	struct timespec ctime;

	/* three balancings: (1) old name removal, (2) new name insertion
	   and (3) maybe "save" link insertion
	   stat data updates: (1) old directory,
	   (2) new directory and (3) maybe old object stat data (when it is
	   directory) and (4) maybe stat data of object to which new entry
	   pointed initially and (5) maybe block containing ".." of
	   renamed directory
	   quota updates: two parent directories */
	jbegin_count =
	    JOURNAL_PER_BALANCE_CNT * 3 + 5 +
	    4 * REISERFS_QUOTA_TRANS_BLOCKS(old_dir->i_sb);

	dquot_initialize(old_dir);
	dquot_initialize(new_dir);

	old_inode = old_dentry->d_inode;
	new_dentry_inode = new_dentry->d_inode;

	// make sure, that oldname still exists and points to an object we
	// are going to rename
	old_de.de_gen_number_bit_string = NULL;
	reiserfs_write_lock(old_dir->i_sb);
	retval =
	    reiserfs_find_entry(old_dir, old_dentry->d_name.name,
				old_dentry->d_name.len, &old_entry_path,
				&old_de);
	pathrelse(&old_entry_path);
	if (retval == IO_ERROR) {
		reiserfs_write_unlock(old_dir->i_sb);
		return -EIO;
	}

	if (retval != NAME_FOUND || old_de.de_objectid != old_inode->i_ino) {
		reiserfs_write_unlock(old_dir->i_sb);
		return -ENOENT;
	}

	old_inode_mode = old_inode->i_mode;
	if (S_ISDIR(old_inode_mode)) {
		// make sure, that directory being renamed has correct ".."
		// and that its new parent directory has not too many links
		// already

		if (new_dentry_inode) {
			if (!reiserfs_empty_dir(new_dentry_inode)) {
				reiserfs_write_unlock(old_dir->i_sb);
				return -ENOTEMPTY;
			}
		}

		/* directory is renamed, its parent directory will be changed,
		 ** so find ".." entry
		 */
		dot_dot_de.de_gen_number_bit_string = NULL;
		retval =
		    reiserfs_find_entry(old_inode, "..", 2, &dot_dot_entry_path,
					&dot_dot_de);
		pathrelse(&dot_dot_entry_path);
		if (retval != NAME_FOUND) {
			reiserfs_write_unlock(old_dir->i_sb);
			return -EIO;
		}

		/* inode number of .. must equal old_dir->i_ino */
		if (dot_dot_de.de_objectid != old_dir->i_ino) {
			reiserfs_write_unlock(old_dir->i_sb);
			return -EIO;
		}
	}

	retval = journal_begin(&th, old_dir->i_sb, jbegin_count);
	if (retval) {
		reiserfs_write_unlock(old_dir->i_sb);
		return retval;
	}

	/* add new entry (or find the existing one) */
	retval =
	    reiserfs_add_entry(&th, new_dir, new_dentry->d_name.name,
			       new_dentry->d_name.len, old_inode, 0);
	if (retval == -EEXIST) {
		if (!new_dentry_inode) {
			reiserfs_panic(old_dir->i_sb, "vs-7050",
				       "new entry is found, new inode == 0");
		}
	} else if (retval) {
		int err = journal_end(&th, old_dir->i_sb, jbegin_count);
		reiserfs_write_unlock(old_dir->i_sb);
		return err ? err : retval;
	}

	reiserfs_update_inode_transaction(old_dir);
	reiserfs_update_inode_transaction(new_dir);

	/* this makes it so an fsync on an open fd for the old name will
	 ** commit the rename operation
	 */
	reiserfs_update_inode_transaction(old_inode);

	if (new_dentry_inode)
		reiserfs_update_inode_transaction(new_dentry_inode);

	while (1) {
		// look for old name using corresponding entry key (found by reiserfs_find_entry)
		if ((retval =
		     search_by_entry_key(new_dir->i_sb, &old_de.de_entry_key,
					 &old_entry_path,
					 &old_de)) != NAME_FOUND) {
			pathrelse(&old_entry_path);
			journal_end(&th, old_dir->i_sb, jbegin_count);
			reiserfs_write_unlock(old_dir->i_sb);
			return -EIO;
		}

		copy_item_head(&old_entry_ih, get_ih(&old_entry_path));

		reiserfs_prepare_for_journal(old_inode->i_sb, old_de.de_bh, 1);

		// look for new name by reiserfs_find_entry
		new_de.de_gen_number_bit_string = NULL;
		retval =
		    reiserfs_find_entry(new_dir, new_dentry->d_name.name,
					new_dentry->d_name.len, &new_entry_path,
					&new_de);
		// reiserfs_add_entry should not return IO_ERROR, because it is called with essentially same parameters from
		// reiserfs_add_entry above, and we'll catch any i/o errors before we get here.
		if (retval != NAME_FOUND_INVISIBLE && retval != NAME_FOUND) {
			pathrelse(&new_entry_path);
			pathrelse(&old_entry_path);
			journal_end(&th, old_dir->i_sb, jbegin_count);
			reiserfs_write_unlock(old_dir->i_sb);
			return -EIO;
		}

		copy_item_head(&new_entry_ih, get_ih(&new_entry_path));

		reiserfs_prepare_for_journal(old_inode->i_sb, new_de.de_bh, 1);

		if (S_ISDIR(old_inode->i_mode)) {
			if ((retval =
			     search_by_entry_key(new_dir->i_sb,
						 &dot_dot_de.de_entry_key,
						 &dot_dot_entry_path,
						 &dot_dot_de)) != NAME_FOUND) {
				pathrelse(&dot_dot_entry_path);
				pathrelse(&new_entry_path);
				pathrelse(&old_entry_path);
				journal_end(&th, old_dir->i_sb, jbegin_count);
				reiserfs_write_unlock(old_dir->i_sb);
				return -EIO;
			}
			copy_item_head(&dot_dot_ih,
				       get_ih(&dot_dot_entry_path));
			// node containing ".." gets into transaction
			reiserfs_prepare_for_journal(old_inode->i_sb,
						     dot_dot_de.de_bh, 1);
		}
		/* we should check seals here, not do
		   this stuff, yes? Then, having
		   gathered everything into RAM we
		   should lock the buffers, yes?  -Hans */
		/* probably.  our rename needs to hold more
		 ** than one path at once.  The seals would
		 ** have to be written to deal with multi-path
		 ** issues -chris
		 */
		/* sanity checking before doing the rename - avoid races many
		 ** of the above checks could have scheduled.  We have to be
		 ** sure our items haven't been shifted by another process.
		 */
		if (item_moved(&new_entry_ih, &new_entry_path) ||
		    !entry_points_to_object(new_dentry->d_name.name,
					    new_dentry->d_name.len,
					    &new_de, new_dentry_inode) ||
		    item_moved(&old_entry_ih, &old_entry_path) ||
		    !entry_points_to_object(old_dentry->d_name.name,
					    old_dentry->d_name.len,
					    &old_de, old_inode)) {
			reiserfs_restore_prepared_buffer(old_inode->i_sb,
							 new_de.de_bh);
			reiserfs_restore_prepared_buffer(old_inode->i_sb,
							 old_de.de_bh);
			if (S_ISDIR(old_inode_mode))
				reiserfs_restore_prepared_buffer(old_inode->
								 i_sb,
								 dot_dot_de.
								 de_bh);
			continue;
		}
		if (S_ISDIR(old_inode_mode)) {
			if (item_moved(&dot_dot_ih, &dot_dot_entry_path) ||
			    !entry_points_to_object("..", 2, &dot_dot_de,
						    old_dir)) {
				reiserfs_restore_prepared_buffer(old_inode->
								 i_sb,
								 old_de.de_bh);
				reiserfs_restore_prepared_buffer(old_inode->
								 i_sb,
								 new_de.de_bh);
				reiserfs_restore_prepared_buffer(old_inode->
								 i_sb,
								 dot_dot_de.
								 de_bh);
				continue;
			}
		}

		RFALSE(S_ISDIR(old_inode_mode) &&
		       !buffer_journal_prepared(dot_dot_de.de_bh), "");

		break;
	}

	/* ok, all the changes can be done in one fell swoop when we
	   have claimed all the buffers needed. */

	mark_de_visible(new_de.de_deh + new_de.de_entry_num);
	set_ino_in_dir_entry(&new_de, INODE_PKEY(old_inode));
	journal_mark_dirty(&th, old_dir->i_sb, new_de.de_bh);

	mark_de_hidden(old_de.de_deh + old_de.de_entry_num);
	journal_mark_dirty(&th, old_dir->i_sb, old_de.de_bh);
	ctime = CURRENT_TIME_SEC;
	old_dir->i_ctime = old_dir->i_mtime = ctime;
	new_dir->i_ctime = new_dir->i_mtime = ctime;
	/* thanks to Alex Adriaanse <*****@*****.**> for patch which adds ctime update of
	   renamed object */
	old_inode->i_ctime = ctime;

	if (new_dentry_inode) {
		// adjust link number of the victim
		if (S_ISDIR(new_dentry_inode->i_mode)) {
			clear_nlink(new_dentry_inode);
		} else {
			drop_nlink(new_dentry_inode);
		}
		new_dentry_inode->i_ctime = ctime;
		savelink = new_dentry_inode->i_nlink;
	}

	if (S_ISDIR(old_inode_mode)) {
		/* adjust ".." of renamed directory */
		set_ino_in_dir_entry(&dot_dot_de, INODE_PKEY(new_dir));
		journal_mark_dirty(&th, new_dir->i_sb, dot_dot_de.de_bh);

		if (!new_dentry_inode)
			/* there (in new_dir) was no directory, so it got new link
			   (".."  of renamed directory) */
			INC_DIR_INODE_NLINK(new_dir);

		/* old directory lost one link - ".. " of renamed directory */
		DEC_DIR_INODE_NLINK(old_dir);
	}
	// looks like in 2.3.99pre3 brelse is atomic. so we can use pathrelse
	pathrelse(&new_entry_path);
	pathrelse(&dot_dot_entry_path);

	// FIXME: this reiserfs_cut_from_item's return value may screw up
	// anybody, but it will panic if will not be able to find the
	// entry. This needs one more clean up
	if (reiserfs_cut_from_item
	    (&th, &old_entry_path, &(old_de.de_entry_key), old_dir, NULL,
	     0) < 0)
		reiserfs_error(old_dir->i_sb, "vs-7060",
			       "couldn't not cut old name. Fsck later?");

	old_dir->i_size -= DEH_SIZE + old_de.de_entrylen;

	reiserfs_update_sd(&th, old_dir);
	reiserfs_update_sd(&th, new_dir);
	reiserfs_update_sd(&th, old_inode);

	if (new_dentry_inode) {
		if (savelink == 0)
			add_save_link(&th, new_dentry_inode,
				      0 /* not truncate */ );
		reiserfs_update_sd(&th, new_dentry_inode);
	}

	retval = journal_end(&th, old_dir->i_sb, jbegin_count);
	reiserfs_write_unlock(old_dir->i_sb);
	return retval;
}
Example #2
0
/* 
 * process, that is going to call fix_nodes/do_balance must hold only
 * one path. If it holds 2 or more, it can get into endless waiting in
 * get_empty_nodes or its clones 
 */
static int do_reiserfs_rename (struct reiserfs_transaction_handle *th, struct inode * old_dir, struct dentry *old_dentry,
			       struct inode * new_dir, struct dentry *new_dentry)
{
  int retval;
  struct path old_entry_path, new_entry_path, dot_dot_entry_path;
  struct reiserfs_dir_entry old_de, new_de, dot_dot_de;
  struct inode * old_inode, * new_inode;
  int new_entry_added = 0;

  init_path (&old_entry_path);
  init_path (&new_entry_path);
  init_path (&dot_dot_entry_path);
  goto start_up;

try_again:
  current->policy |= SCHED_YIELD;
  schedule();
	
start_up:
  old_inode = new_inode = NULL;
  dot_dot_de.de_bh = 0;
  new_de.de_bh = 0;

  /* 
   * look for the old name in old directory 
   */
  retval = -ENOENT;
  old_de.de_gen_number_bit_string = 0;
  if (reiserfs_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_entry_path, &old_de) == POSITION_NOT_FOUND)
    goto end_rename;

  pathrelse (&old_entry_path);

  old_inode = old_dentry->d_inode;
  retval = -EPERM;

  if ((old_dir->i_mode & S_ISVTX) && 
      current->fsuid != old_inode->i_uid &&
      current->fsuid != old_dir->i_uid && !fsuser())
    goto end_rename;

  new_inode = new_dentry->d_inode;

  /* look for the new entry in target directory */
  new_de.de_gen_number_bit_string = 0;
  if (reiserfs_find_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_entry_path, &new_de) == POSITION_FOUND) {
    if (!new_inode) {
      printk ("do_reiserfs_rename: new entry found, inode == 0 though\n");
    }
    /* this entry already exists, we can just set key of object */
    new_entry_added = 1;
  } else {
#ifdef REISERFS_CHECK
    if (new_entry_added) {
      if (new_de.de_namelen != new_dentry->d_name.len || memcmp (new_de.de_name, new_dentry->d_name.name, new_de.de_namelen) ||
	  de_visible (new_de.de_deh))
	reiserfs_panic (old_dir->i_sb, "vs-7045: reiserfs_rename: suspicious entry found");
    }
#endif /* REISERFS_CHECK */
  }
  pathrelse (&new_entry_path);


  if (new_inode == old_inode) {
    retval = 0;
    goto end_rename;
  }

  if (new_inode && S_ISDIR(new_inode->i_mode)) {
    /* new name exists and points to directory */
    retval = -EISDIR;
    if (!S_ISDIR(old_inode->i_mode))
      goto end_rename;
    retval = -EINVAL;
    if (is_subdir (new_dentry, old_dentry))
      goto end_rename;
    retval = -ENOTEMPTY;
    if (!reiserfs_empty_dir (new_inode))
      goto end_rename;
    retval = -EBUSY;
    if (new_inode->i_count > 1)
      goto end_rename;
  }

  retval = -EPERM;
  if (new_inode && (new_dir->i_mode & S_ISVTX) && 
      current->fsuid != new_inode->i_uid &&
      current->fsuid != new_dir->i_uid && !fsuser())
    goto end_rename;

  if (S_ISDIR(old_inode->i_mode)) {
    /* old name points to directory */
    retval = -ENOTDIR;
    if (new_inode && !S_ISDIR(new_inode->i_mode))
      goto end_rename;

    retval = -EINVAL;
    if (is_subdir(new_dentry, old_dentry))
      goto end_rename;

    retval = -EIO;
    /* directory is renamed, its parent directory will be changed, so find ".." entry */
    dot_dot_de.de_gen_number_bit_string = 0;
    if (reiserfs_find_entry (old_inode, "..", 2, &dot_dot_entry_path, &dot_dot_de) == POSITION_NOT_FOUND)
      goto end_rename;
    if (dot_dot_de.de_objectid != old_dir->i_ino)
      goto end_rename;
    pathrelse (&dot_dot_entry_path);

    retval = -EMLINK;
    if (!new_inode && new_dir->i_nlink >= REISERFS_LINK_MAX)
      goto end_rename;
  }
  
  if (new_entry_added == 0) {
    /* add new entry if we did not do it, but do not mark it as visible */
    retval = reiserfs_add_entry (th, new_dir, new_dentry->d_name.name, new_dentry->d_name.len, INODE_PKEY (old_inode), &new_de, 0);
    if (retval)
      goto end_rename;
    if_in_ram_update_sd (th, new_dir);
    new_entry_added = 1;
    goto try_again;
  }


  /* 
   * look for old name, new name and ".." when renaming directories again
   */
  if (reiserfs_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_entry_path, &old_de) == POSITION_NOT_FOUND)
    reiserfs_panic (old_dir->i_sb, "vs-7050: reiserfs_rename: old name not found");
  if (reiserfs_find_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_entry_path, &new_de) == POSITION_NOT_FOUND)
    reiserfs_panic (old_dir->i_sb, "vs-7055: reiserfs_rename: new name not found");
  if (S_ISDIR(old_inode->i_mode) && reiserfs_find_entry (old_inode, "..", 2, &dot_dot_entry_path, &dot_dot_de) == POSITION_NOT_FOUND)
    reiserfs_panic (old_dir->i_sb, "vs-7060: reiserfs_rename: \"..\" name not found");
 

  /* sanity checking before doing the rename - avoid races */
  if (!entry_points_to_object (new_dentry->d_name.name, new_dentry->d_name.len, &new_de, new_inode))
    goto try_again;
  if (!entry_points_to_object (old_dentry->d_name.name, old_dentry->d_name.len, &old_de, old_inode))
    /* go to re-looking for old entry */
    goto try_again;

  if (S_ISDIR(old_inode->i_mode) && !entry_points_to_object ("..", 2, &dot_dot_de, old_dir))
    /* go to re-looking for ".." entry of renamed directory */
    goto try_again;
  
  /* ok, all the changes can be done in one fell swoop when we have
     claimed all the buffers needed.*/

  /* make old name hidden */
  mark_de_hidden (old_de.de_deh);
  journal_mark_dirty(th, old_dir->i_sb, old_de.de_bh) ;

  /* make new name visible and set key of old object (if entry
     existed, it is already visible, if not, key is correct already) */
  mark_de_visible (new_de.de_deh);
  new_de.de_deh->deh_dir_id = INODE_PKEY (old_inode)->k_dir_id;
  new_de.de_deh->deh_objectid = INODE_PKEY (old_inode)->k_objectid;
  journal_mark_dirty(th, old_dir->i_sb, new_de.de_bh) ;

  old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
  if_in_ram_update_sd (th, old_dir);

  new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
  if_in_ram_update_sd (th, new_dir);

  if (new_inode) {
    new_inode->i_nlink--;
    new_inode->i_ctime = CURRENT_TIME;
    if_in_ram_update_sd (th, new_inode);
  }
  if (dot_dot_de.de_bh) {
    set_ino_in_dir_entry (&dot_dot_de, INODE_PKEY (new_dir));
    journal_mark_dirty(th, old_dir->i_sb, dot_dot_de.de_bh) ;
    old_dir->i_nlink--;
    if_in_ram_update_sd (th, old_dir);
    if (new_inode) {
      new_inode->i_nlink--;
      if_in_ram_update_sd (th, new_inode);
    } else {
      new_dir->i_nlink++;
      if_in_ram_update_sd (th, new_dir);
    }
  }

  /* ok, renaming done */
  decrement_counters_in_path (&new_entry_path);
  decrement_counters_in_path (&dot_dot_entry_path);

  /* remove old name (it is hidden now) */
  if (reiserfs_cut_from_item (th, old_dir, old_dir->i_sb, &old_entry_path, &(old_de.de_entry_num),
			      &(old_de.de_entry_key), 0, PRESERVE_RENAMING) == 0)
    printk ("reiserfs_rename: could not remove old name\n");
  else {
    old_dir->i_size -= DEH_SIZE + old_de.de_entrylen;
    old_dir->i_blocks = old_dir->i_size / 512 + ((old_dir->i_size % 512) ? 1 : 0);
    if_in_ram_update_sd (th, old_dir);
  }

  /* Update the dcache */
  d_move(old_dentry, new_dentry);
  retval = 0;

end_rename:
  pathrelse (&old_entry_path);
  return retval;
}