Ejemplo n.º 1
0
/* this routine links an IS_ROOT dentry into the dcache tree.  It gains "parent"
 * as a parent and "name" as a name
 * It should possibly go in dcache.c
 */
int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name)
{
	struct dentry *tdentry;
#ifdef NFSD_PARANOIA
	if (!IS_ROOT(target))
		printk("nfsd: d_splice with no-root target: %s/%s\n", parent->d_name.name, name->name);
	if (!(target->d_flags & DCACHE_NFSD_DISCONNECTED))
		printk("nfsd: d_splice with non-DISCONNECTED target: %s/%s\n", parent->d_name.name, name->name);
#endif
	tdentry = d_alloc(parent, name);
	if (tdentry == NULL)
		return -ENOMEM;
	d_move(target, tdentry);

	/* tdentry will have been made a "child" of target (the parent of target)
	 * make it an IS_ROOT instead
	 */
	spin_lock(&dcache_lock);
	list_del_init(&tdentry->d_child);
	tdentry->d_parent = tdentry;
	spin_unlock(&dcache_lock);
	d_rehash(target);
	dput(tdentry);

	/* if parent is properly connected, then we can assert that
	 * the children are connected, but it must be a singluar (non-forking)
	 * branch
	 */
	if (!(parent->d_flags & DCACHE_NFSD_DISCONNECTED)) {
		while (target) {
			target->d_flags &= ~DCACHE_NFSD_DISCONNECTED;
			parent = target;
			spin_lock(&dcache_lock);
			if (list_empty(&parent->d_subdirs))
				target = NULL;
			else {
				target = list_entry(parent->d_subdirs.next, struct dentry, d_child);
#ifdef NFSD_PARANOIA
				/* must be only child */
				if (target->d_child.next != &parent->d_subdirs
				    || target->d_child.prev != &parent->d_subdirs)
					printk("nfsd: d_splice found non-singular disconnected branch: %s/%s\n",
					       parent->d_name.name, target->d_name.name);
#endif
			}
			spin_unlock(&dcache_lock);
		}
	}
	return 0;
}
Ejemplo n.º 2
0
void sysfs_rename_dir(struct kobject * kobj, const char *new_name)
{
	struct dentry * new_dentry, * parent;

	if (!strcmp(kobject_name(kobj), new_name))
		return;

	if (!kobj->parent)
		return;

	parent = kobj->parent->dentry;

	down(&parent->d_inode->i_sem);

	new_dentry = sysfs_get_dentry(parent, new_name);
	d_move(kobj->dentry, new_dentry);
	kobject_set_name(kobj,new_name);
	up(&parent->d_inode->i_sem);	
}
Ejemplo n.º 3
0
/**
 * debugfs_rename - rename a file/directory in the debugfs filesystem
 * @old_dir: a pointer to the parent dentry for the renamed object. This
 *          should be a directory dentry.
 * @old_dentry: dentry of an object to be renamed.
 * @new_dir: a pointer to the parent dentry where the object should be
 *          moved. This should be a directory dentry.
 * @new_name: a pointer to a string containing the target name.
 *
 * This function renames a file/directory in debugfs.  The target must not
 * exist for rename to succeed.
 *
 * This function will return a pointer to old_dentry (which is updated to
 * reflect renaming) if it succeeds. If an error occurs, %NULL will be
 * returned.
 *
 * If debugfs is not enabled in the kernel, the value -%ENODEV will be
 * returned.
 */
struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
		struct dentry *new_dir, const char *new_name)
{
	int error;
	struct dentry *dentry = NULL, *trap;
	const char *old_name;

	trap = lock_rename(new_dir, old_dir);
	/* Source or destination directories don't exist? */
	if (!old_dir->d_inode || !new_dir->d_inode)
		goto exit;
	/* Source does not exist, cyclic rename, or mountpoint? */
	if (!old_dentry->d_inode || old_dentry == trap ||
	    d_mountpoint(old_dentry))
		goto exit;
	dentry = lookup_one_len(new_name, new_dir, strlen(new_name));
	/* Lookup failed, cyclic rename or target exists? */
	if (IS_ERR(dentry) || dentry == trap || dentry->d_inode)
		goto exit;

	old_name = fsnotify_oldname_init(old_dentry->d_name.name);

	error = simple_rename(old_dir->d_inode, old_dentry, new_dir->d_inode,
		dentry);
	if (error) {
		fsnotify_oldname_free(old_name);
		goto exit;
	}
	d_move(old_dentry, dentry);
	fsnotify_move(old_dir->d_inode, new_dir->d_inode, old_name,
		old_dentry->d_name.name, S_ISDIR(old_dentry->d_inode->i_mode),
		NULL, old_dentry);
	fsnotify_oldname_free(old_name);
	unlock_rename(new_dir, old_dir);
	dput(dentry);
	return old_dentry;
exit:
	if (dentry && !IS_ERR(dentry))
		dput(dentry);
	unlock_rename(new_dir, old_dir);
	return NULL;
}
Ejemplo n.º 4
0
static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
				struct nameidata *nd)
{
	struct dentry *ret = NULL;
	struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata;
	struct sysfs_dirent *sd;
	struct inode *inode;

	mutex_lock(&sysfs_mutex);

	sd = sysfs_find_dirent(parent_sd, dentry->d_name.name);

	/* no such entry */
	if (!sd) {
		ret = ERR_PTR(-ENOENT);
		goto out_unlock;
	}

	/* attach dentry and inode */
	inode = sysfs_get_inode(dir->i_sb, sd);
	if (!inode) {
		ret = ERR_PTR(-ENOMEM);
		goto out_unlock;
	}

	/* instantiate and hash dentry */
	ret = d_find_alias(inode);
	if (!ret) {
		dentry->d_op = &sysfs_dentry_ops;
		dentry->d_fsdata = sysfs_get(sd);
		d_add(dentry, inode);
	} else {
		d_move(ret, dentry);
		iput(inode);
	}

 out_unlock:
	mutex_unlock(&sysfs_mutex);
	return ret;
}
Ejemplo n.º 5
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;
}
int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
		struct inode *_dst_dir, struct dentry *_dst_dentry)
{
	int err, flags;
	/* reduce stack space */
	struct au_ren_args *a;

	AuDbg("%.*s, %.*s\n", AuDLNPair(_src_dentry), AuDLNPair(_dst_dentry));
	IMustLock(_src_dir);
	IMustLock(_dst_dir);

	err = -ENOMEM;
	BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE);
	a = kzalloc(sizeof(*a), GFP_NOFS);
	if (unlikely(!a))
		goto out;

	a->src_dir = _src_dir;
	a->src_dentry = _src_dentry;
	a->src_inode = a->src_dentry->d_inode;
	a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */
	a->dst_dir = _dst_dir;
	a->dst_dentry = _dst_dentry;
	a->dst_inode = a->dst_dentry->d_inode;
	a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */
	if (a->dst_inode) {
		IMustLock(a->dst_inode);
		au_igrab(a->dst_inode);
	}

	err = -ENOTDIR;
	flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN;
	if (S_ISDIR(a->src_inode->i_mode)) {
		au_fset_ren(a->flags, ISDIR);
		if (unlikely(a->dst_inode && !S_ISDIR(a->dst_inode->i_mode)))
			goto out_free;
		err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry,
						AuLock_DIR | flags);
	} else
		err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry,
						flags);
	if (unlikely(err))
		goto out_free;

	err = au_d_hashed_positive(a->src_dentry);
	if (unlikely(err))
		goto out_unlock;
	err = -ENOENT;
	if (a->dst_inode) {
		/*
		 * If it is a dir, VFS unhash dst_dentry before this
		 * function. It means we cannot rely upon d_unhashed().
		 */
		if (unlikely(!a->dst_inode->i_nlink))
			goto out_unlock;
		if (!S_ISDIR(a->dst_inode->i_mode)) {
			err = au_d_hashed_positive(a->dst_dentry);
			if (unlikely(err))
				goto out_unlock;
		} else if (unlikely(IS_DEADDIR(a->dst_inode)))
			goto out_unlock;
	} else if (unlikely(d_unhashed(a->dst_dentry)))
		goto out_unlock;

	au_fset_ren(a->flags, ISSAMEDIR); /* temporary */
	di_write_lock_parent(a->dst_parent);

	/* which branch we process */
	err = au_ren_wbr(a);
	if (unlikely(err < 0))
		goto out_parent;
	a->br = au_sbr(a->dst_dentry->d_sb, a->btgt);
	a->h_path.mnt = a->br->br_mnt;

	/* are they available to be renamed */
	err = au_ren_may_dir(a);
	if (unlikely(err))
		goto out_children;

	/* prepare the writable parent dir on the same branch */
	if (a->dst_bstart == a->btgt) {
		au_fset_ren(a->flags, WHDST);
	} else {
		err = au_cpup_dirs(a->dst_dentry, a->btgt);
		if (unlikely(err))
			goto out_children;
	}

	if (a->src_dir != a->dst_dir) {
		/*
		 * this temporary unlock is safe,
		 * because both dir->i_mutex are locked.
		 */
		di_write_unlock(a->dst_parent);
		di_write_lock_parent(a->src_parent);
		err = au_wr_dir_need_wh(a->src_dentry,
					au_ftest_ren(a->flags, ISDIR),
					&a->btgt);
		di_write_unlock(a->src_parent);
		di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1);
		au_fclr_ren(a->flags, ISSAMEDIR);
	} else
		err = au_wr_dir_need_wh(a->src_dentry,
					au_ftest_ren(a->flags, ISDIR),
					&a->btgt);
	if (unlikely(err < 0))
		goto out_children;
	if (err)
		au_fset_ren(a->flags, WHSRC);

	/* lock them all */
	err = au_ren_lock(a);
	if (unlikely(err))
		goto out_children;

	if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE))
		err = au_may_ren(a);
	else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN))
		err = -ENAMETOOLONG;
	if (unlikely(err))
		goto out_hdir;

	/* store timestamps to be revertible */
	au_ren_dt(a);

	/* here we go */
	err = do_rename(a);
	if (unlikely(err))
		goto out_dt;

	/* update dir attributes */
	au_ren_refresh_dir(a);

	/* dput/iput all lower dentries */
	au_ren_refresh(a);

	goto out_hdir; /* success */

out_dt:
	au_ren_rev_dt(err, a);
out_hdir:
	au_ren_unlock(a);
out_children:
	au_nhash_wh_free(&a->whlist);
	if (err && a->dst_inode && a->dst_bstart != a->btgt) {
		AuDbg("bstart %d, btgt %d\n", a->dst_bstart, a->btgt);
		au_set_h_dptr(a->dst_dentry, a->btgt, NULL);
		au_set_dbstart(a->dst_dentry, a->dst_bstart);
	}
out_parent:
	if (!err)
		d_move(a->src_dentry, a->dst_dentry);
	else {
		au_update_dbstart(a->dst_dentry);
		if (!a->dst_inode)
			d_drop(a->dst_dentry);
	}
	if (au_ftest_ren(a->flags, ISSAMEDIR))
		di_write_unlock(a->dst_parent);
	else
		di_write_unlock2(a->src_parent, a->dst_parent);
out_unlock:
	aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry);
out_free:
	iput(a->dst_inode);
	if (a->thargs)
		au_whtmp_rmdir_free(a->thargs);
	kfree(a);
out:
	AuTraceErr(err);
	return err;
}
Ejemplo n.º 7
0
/**
 * nfs_sillyrename - Perform a silly-rename of a dentry
 * @dir: inode of directory that contains dentry
 * @dentry: dentry to be sillyrenamed
 *
 * NFSv2/3 is stateless and the server doesn't know when the client is
 * holding a file open. To prevent application problems when a file is
 * unlinked while it's still open, the client performs a "silly-rename".
 * That is, it renames the file to a hidden file in the same directory,
 * and only performs the unlink once the last reference to it is put.
 *
 * The final cleanup is done during dentry_iput.
 *
 * (Note: NFSv4 is stateful, and has opens, so in theory an NFSv4 server
 * could take responsibility for keeping open files referenced.  The server
 * would also need to ensure that opened-but-deleted files were kept over
 * reboots.  However, we may not assume a server does so.  (RFC 5661
 * does provide an OPEN4_RESULT_PRESERVE_UNLINKED flag that a server can
 * use to advertise that it does this; some day we may take advantage of
 * it.))
 */
int
nfs_sillyrename(struct inode *dir, struct dentry *dentry)
{
    static unsigned int sillycounter;
    const int      fileidsize  = sizeof(NFS_FILEID(dentry->d_inode))*2;
    const int      countersize = sizeof(sillycounter)*2;
    const int      slen        = sizeof(".nfs")+fileidsize+countersize-1;
    char           silly[slen+1];
    struct dentry *sdentry;
    struct rpc_task *task;
    int            error = -EIO;

    dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
             dentry->d_parent->d_name.name, dentry->d_name.name,
             dentry->d_count);
    nfs_inc_stats(dir, NFSIOS_SILLYRENAME);

    /*
     * We don't allow a dentry to be silly-renamed twice.
     */
    error = -EBUSY;
    if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
        goto out;

    sprintf(silly, ".nfs%*.*Lx",
            fileidsize, fileidsize,
            (unsigned long long)NFS_FILEID(dentry->d_inode));

    /* Return delegation in anticipation of the rename */
    NFS_PROTO(dentry->d_inode)->return_delegation(dentry->d_inode);

    sdentry = NULL;
    do {
        char *suffix = silly + slen - countersize;

        dput(sdentry);
        sillycounter++;
        sprintf(suffix, "%*.*x", countersize, countersize, sillycounter);

        dfprintk(VFS, "NFS: trying to rename %s to %s\n",
                 dentry->d_name.name, silly);

        sdentry = lookup_one_len(silly, dentry->d_parent, slen);
        /*
         * N.B. Better to return EBUSY here ... it could be
         * dangerous to delete the file while it's in use.
         */
        if (IS_ERR(sdentry))
            goto out;
    } while (sdentry->d_inode != NULL); /* need negative lookup */

    /* queue unlink first. Can't do this from rpc_release as it
     * has to allocate memory
     */
    error = nfs_async_unlink(dir, dentry);
    if (error)
        goto out_dput;

    /* populate unlinkdata with the right dname */
    error = nfs_copy_dname(sdentry,
                           (struct nfs_unlinkdata *)dentry->d_fsdata);
    if (error) {
        nfs_cancel_async_unlink(dentry);
        goto out_dput;
    }

    /* run the rename task, undo unlink if it fails */
    task = nfs_async_rename(dir, dir, dentry, sdentry);
    if (IS_ERR(task)) {
        error = -EBUSY;
        nfs_cancel_async_unlink(dentry);
        goto out_dput;
    }

    /* wait for the RPC task to complete, unless a SIGKILL intervenes */
    error = rpc_wait_for_completion_task(task);
    if (error == 0)
        error = task->tk_status;
    switch (error) {
    case 0:
        /* The rename succeeded */
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
        d_move(dentry, sdentry);
        break;
    case -ERESTARTSYS:
        /* The result of the rename is unknown. Play it safe by
         * forcing a new lookup */
        d_drop(dentry);
        d_drop(sdentry);
    }
    rpc_put_task(task);
out_dput:
    dput(sdentry);
out:
    return error;
}
Ejemplo n.º 8
0
/**
 * ntfs_lookup - find the inode represented by a dentry in a directory inode
 * @dir_ino:	directory inode in which to look for the inode
 * @dent:	dentry representing the inode to look for
 * @nd:		lookup nameidata
 *
 * In short, ntfs_lookup() looks for the inode represented by the dentry @dent
 * in the directory inode @dir_ino and if found attaches the inode to the
 * dentry @dent.
 *
 * In more detail, the dentry @dent specifies which inode to look for by
 * supplying the name of the inode in @dent->d_name.name. ntfs_lookup()
 * converts the name to Unicode and walks the contents of the directory inode
 * @dir_ino looking for the converted Unicode name. If the name is found in the
 * directory, the corresponding inode is loaded by calling ntfs_iget() on its
 * inode number and the inode is associated with the dentry @dent via a call to
 * d_splice_alias().
 *
 * If the name is not found in the directory, a NULL inode is inserted into the
 * dentry @dent via a call to d_add(). The dentry is then termed a negative
 * dentry.
 *
 * Only if an actual error occurs, do we return an error via ERR_PTR().
 *
 * In order to handle the case insensitivity issues of NTFS with regards to the
 * dcache and the dcache requiring only one dentry per directory, we deal with
 * dentry aliases that only differ in case in ->ntfs_lookup() while maintaining
 * a case sensitive dcache. This means that we get the full benefit of dcache
 * speed when the file/directory is looked up with the same case as returned by
 * ->ntfs_readdir() but that a lookup for any other case (or for the short file
 * name) will not find anything in dcache and will enter ->ntfs_lookup()
 * instead, where we search the directory for a fully matching file name
 * (including case) and if that is not found, we search for a file name that
 * matches with different case and if that has non-POSIX semantics we return
 * that. We actually do only one search (case sensitive) and keep tabs on
 * whether we have found a case insensitive match in the process.
 *
 * To simplify matters for us, we do not treat the short vs long filenames as
 * two hard links but instead if the lookup matches a short filename, we
 * return the dentry for the corresponding long filename instead.
 *
 * There are three cases we need to distinguish here:
 *
 * 1) @dent perfectly matches (i.e. including case) a directory entry with a
 *    file name in the WIN32 or POSIX namespaces. In this case
 *    ntfs_lookup_inode_by_name() will return with name set to NULL and we
 *    just d_splice_alias() @dent.
 * 2) @dent matches (not including case) a directory entry with a file name in
 *    the WIN32 namespace. In this case ntfs_lookup_inode_by_name() will return
 *    with name set to point to a kmalloc()ed ntfs_name structure containing
 *    the properly cased little endian Unicode name. We convert the name to the
 *    current NLS code page, search if a dentry with this name already exists
 *    and if so return that instead of @dent.  At this point things are
 *    complicated by the possibility of 'disconnected' dentries due to NFS
 *    which we deal with appropriately (see the code comments).  The VFS will
 *    then destroy the old @dent and use the one we returned.  If a dentry is
 *    not found, we allocate a new one, d_splice_alias() it, and return it as
 *    above.
 * 3) @dent matches either perfectly or not (i.e. we don't care about case) a
 *    directory entry with a file name in the DOS namespace. In this case
 *    ntfs_lookup_inode_by_name() will return with name set to point to a
 *    kmalloc()ed ntfs_name structure containing the mft reference (cpu endian)
 *    of the inode. We use the mft reference to read the inode and to find the
 *    file name in the WIN32 namespace corresponding to the matched short file
 *    name. We then convert the name to the current NLS code page, and proceed
 *    searching for a dentry with this name, etc, as in case 2), above.
 *
 * Locking: Caller must hold i_mutex on the directory.
 */
static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
		struct nameidata *nd)
{
	ntfs_volume *vol = NTFS_SB(dir_ino->i_sb);
	struct inode *dent_inode;
	ntfschar *uname;
	ntfs_name *name = NULL;
	MFT_REF mref;
	unsigned long dent_ino;
	int uname_len;

	ntfs_debug("Looking up %s in directory inode 0x%lx.",
			dent->d_name.name, dir_ino->i_ino);
	/* Convert the name of the dentry to Unicode. */
	uname_len = ntfs_nlstoucs(vol, dent->d_name.name, dent->d_name.len,
			&uname);
	if (uname_len < 0) {
		if (uname_len != -ENAMETOOLONG)
			ntfs_error(vol->sb, "Failed to convert name to "
					"Unicode.");
		return ERR_PTR(uname_len);
	}
	mref = ntfs_lookup_inode_by_name(NTFS_I(dir_ino), uname, uname_len,
			&name);
	kmem_cache_free(ntfs_name_cache, uname);
	if (!IS_ERR_MREF(mref)) {
		dent_ino = MREF(mref);
		ntfs_debug("Found inode 0x%lx. Calling ntfs_iget.", dent_ino);
		dent_inode = ntfs_iget(vol->sb, dent_ino);
		if (likely(!IS_ERR(dent_inode))) {
			/* Consistency check. */
			if (is_bad_inode(dent_inode) || MSEQNO(mref) ==
					NTFS_I(dent_inode)->seq_no ||
					dent_ino == FILE_MFT) {
				/* Perfect WIN32/POSIX match. -- Case 1. */
				if (!name) {
					ntfs_debug("Done.  (Case 1.)");
					return d_splice_alias(dent_inode, dent);
				}
				/*
				 * We are too indented.  Handle imperfect
				 * matches and short file names further below.
				 */
				goto handle_name;
			}
			ntfs_error(vol->sb, "Found stale reference to inode "
					"0x%lx (reference sequence number = "
					"0x%x, inode sequence number = 0x%x), "
					"returning -EIO. Run chkdsk.",
					dent_ino, MSEQNO(mref),
					NTFS_I(dent_inode)->seq_no);
			iput(dent_inode);
			dent_inode = ERR_PTR(-EIO);
		} else
			ntfs_error(vol->sb, "ntfs_iget(0x%lx) failed with "
					"error code %li.", dent_ino,
					PTR_ERR(dent_inode));
		kfree(name);
		/* Return the error code. */
		return (struct dentry *)dent_inode;
	}
	/* It is guaranteed that @name is no longer allocated at this point. */
	if (MREF_ERR(mref) == -ENOENT) {
		ntfs_debug("Entry was not found, adding negative dentry.");
		/* The dcache will handle negative entries. */
		d_add(dent, NULL);
		ntfs_debug("Done.");
		return NULL;
	}
	ntfs_error(vol->sb, "ntfs_lookup_ino_by_name() failed with error "
			"code %i.", -MREF_ERR(mref));
	return ERR_PTR(MREF_ERR(mref));
	// TODO: Consider moving this lot to a separate function! (AIA)
handle_name:
   {
	struct dentry *real_dent, *new_dent;
	MFT_RECORD *m;
	ntfs_attr_search_ctx *ctx;
	ntfs_inode *ni = NTFS_I(dent_inode);
	int err;
	struct qstr nls_name;

	nls_name.name = NULL;
	if (name->type != FILE_NAME_DOS) {			/* Case 2. */
		ntfs_debug("Case 2.");
		nls_name.len = (unsigned)ntfs_ucstonls(vol,
				(ntfschar*)&name->name, name->len,
				(unsigned char**)&nls_name.name, 0);
		kfree(name);
	} else /* if (name->type == FILE_NAME_DOS) */ {		/* Case 3. */
		FILE_NAME_ATTR *fn;

		ntfs_debug("Case 3.");
		kfree(name);

		/* Find the WIN32 name corresponding to the matched DOS name. */
		ni = NTFS_I(dent_inode);
		m = map_mft_record(ni);
		if (IS_ERR(m)) {
			err = PTR_ERR(m);
			m = NULL;
			ctx = NULL;
			goto err_out;
		}
		ctx = ntfs_attr_get_search_ctx(ni, m);
		if (unlikely(!ctx)) {
			err = -ENOMEM;
			goto err_out;
		}
		do {
			ATTR_RECORD *a;
			u32 val_len;

			err = ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0,
					NULL, 0, ctx);
			if (unlikely(err)) {
				ntfs_error(vol->sb, "Inode corrupt: No WIN32 "
						"namespace counterpart to DOS "
						"file name. Run chkdsk.");
				if (err == -ENOENT)
					err = -EIO;
				goto err_out;
			}
			/* Consistency checks. */
			a = ctx->attr;
			if (a->non_resident || a->flags)
				goto eio_err_out;
			val_len = le32_to_cpu(a->data.resident.value_length);
			if (le16_to_cpu(a->data.resident.value_offset) +
					val_len > le32_to_cpu(a->length))
				goto eio_err_out;
			fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + le16_to_cpu(
					ctx->attr->data.resident.value_offset));
			if ((u32)(fn->file_name_length * sizeof(ntfschar) +
					sizeof(FILE_NAME_ATTR)) > val_len)
				goto eio_err_out;
		} while (fn->file_name_type != FILE_NAME_WIN32);

		/* Convert the found WIN32 name to current NLS code page. */
		nls_name.len = (unsigned)ntfs_ucstonls(vol,
				(ntfschar*)&fn->file_name, fn->file_name_length,
				(unsigned char**)&nls_name.name, 0);

		ntfs_attr_put_search_ctx(ctx);
		unmap_mft_record(ni);
	}
	m = NULL;
	ctx = NULL;

	/* Check if a conversion error occurred. */
	if ((signed)nls_name.len < 0) {
		err = (signed)nls_name.len;
		goto err_out;
	}
	nls_name.hash = full_name_hash(nls_name.name, nls_name.len);

	/*
	 * Note: No need for dent->d_lock lock as i_mutex is held on the
	 * parent inode.
	 */

	/* Does a dentry matching the nls_name exist already? */
	real_dent = d_lookup(dent->d_parent, &nls_name);
	/* If not, create it now. */
	if (!real_dent) {
		real_dent = d_alloc(dent->d_parent, &nls_name);
		kfree(nls_name.name);
		if (!real_dent) {
			err = -ENOMEM;
			goto err_out;
		}
		new_dent = d_splice_alias(dent_inode, real_dent);
		if (new_dent)
			dput(real_dent);
		else
			new_dent = real_dent;
		ntfs_debug("Done.  (Created new dentry.)");
		return new_dent;
	}
	kfree(nls_name.name);
	/* Matching dentry exists, check if it is negative. */
	if (real_dent->d_inode) {
		if (unlikely(real_dent->d_inode != dent_inode)) {
			/* This can happen because bad inodes are unhashed. */
			BUG_ON(!is_bad_inode(dent_inode));
			BUG_ON(!is_bad_inode(real_dent->d_inode));
		}
		/*
		 * Already have the inode and the dentry attached, decrement
		 * the reference count to balance the ntfs_iget() we did
		 * earlier on.  We found the dentry using d_lookup() so it
		 * cannot be disconnected and thus we do not need to worry
		 * about any NFS/disconnectedness issues here.
		 */
		iput(dent_inode);
		ntfs_debug("Done.  (Already had inode and dentry.)");
		return real_dent;
	}
	/*
	 * Negative dentry: instantiate it unless the inode is a directory and
	 * has a 'disconnected' dentry (i.e. IS_ROOT and DCACHE_DISCONNECTED),
	 * in which case d_move() that in place of the found dentry.
	 */
	if (!S_ISDIR(dent_inode->i_mode)) {
		/* Not a directory; everything is easy. */
		d_instantiate(real_dent, dent_inode);
		ntfs_debug("Done.  (Already had negative file dentry.)");
		return real_dent;
	}
	spin_lock(&dcache_lock);
	if (list_empty(&dent_inode->i_dentry)) {
		/*
		 * Directory without a 'disconnected' dentry; we need to do
		 * d_instantiate() by hand because it takes dcache_lock which
		 * we already hold.
		 */
		list_add(&real_dent->d_alias, &dent_inode->i_dentry);
		real_dent->d_inode = dent_inode;
		spin_unlock(&dcache_lock);
		security_d_instantiate(real_dent, dent_inode);
		ntfs_debug("Done.  (Already had negative directory dentry.)");
		return real_dent;
	}
	/*
	 * Directory with a 'disconnected' dentry; get a reference to the
	 * 'disconnected' dentry.
	 */
	new_dent = list_entry(dent_inode->i_dentry.next, struct dentry,
			d_alias);
	dget_locked(new_dent);
	spin_unlock(&dcache_lock);
	/* Do security vodoo. */
	security_d_instantiate(real_dent, dent_inode);
	/* Move new_dent in place of real_dent. */
	d_move(new_dent, real_dent);
	/* Balance the ntfs_iget() we did above. */
	iput(dent_inode);
	/* Throw away real_dent. */
	dput(real_dent);
	/* Use new_dent as the actual dentry. */
	ntfs_debug("Done.  (Already had negative, disconnected directory "
			"dentry.)");
	return new_dent;

eio_err_out:
	ntfs_error(vol->sb, "Illegal file name attribute. Run chkdsk.");
	err = -EIO;
err_out:
	if (ctx)
		ntfs_attr_put_search_ctx(ctx);
	if (m)
		unmap_mft_record(ni);
	iput(dent_inode);
	ntfs_error(vol->sb, "Failed, returning error code %i.", err);
	return ERR_PTR(err);
   }
}
Ejemplo n.º 9
0
Archivo: dir.c Proyecto: cilynx/dd-wrt
int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj)
{
	struct sysfs_dirent *sd = kobj->sd;
	struct sysfs_dirent *new_parent_sd;
	struct dentry *old_parent, *new_parent = NULL;
	struct dentry *old_dentry = NULL, *new_dentry = NULL;
	int error;

	BUG_ON(!sd->s_parent);
	new_parent_sd = new_parent_kobj->sd ? new_parent_kobj->sd : &sysfs_root;

	/* get dentries */
	old_dentry = sysfs_get_dentry(sd);
	if (IS_ERR(old_dentry)) {
		error = PTR_ERR(old_dentry);
		goto out_dput;
	}
	old_parent = sd->s_parent->s_dentry;

	new_parent = sysfs_get_dentry(new_parent_sd);
	if (IS_ERR(new_parent)) {
		error = PTR_ERR(new_parent);
		goto out_dput;
	}

	if (old_parent->d_inode == new_parent->d_inode) {
		error = 0;
		goto out_dput;	/* nothing to move */
	}
again:
	mutex_lock(&old_parent->d_inode->i_mutex);
	if (!mutex_trylock(&new_parent->d_inode->i_mutex)) {
		mutex_unlock(&old_parent->d_inode->i_mutex);
		goto again;
	}

	new_dentry = lookup_one_len(kobj->name, new_parent, strlen(kobj->name));
	if (IS_ERR(new_dentry)) {
		error = PTR_ERR(new_dentry);
		goto out_unlock;
	} else
		error = 0;
	d_add(new_dentry, NULL);
	d_move(sd->s_dentry, new_dentry);
	dput(new_dentry);

	/* Remove from old parent's list and insert into new parent's list. */
	mutex_lock(&sysfs_mutex);

	sysfs_unlink_sibling(sd);
	sysfs_get(new_parent_sd);
	sysfs_put(sd->s_parent);
	sd->s_parent = new_parent_sd;
	sysfs_link_sibling(sd);

	mutex_unlock(&sysfs_mutex);

 out_unlock:
	mutex_unlock(&new_parent->d_inode->i_mutex);
	mutex_unlock(&old_parent->d_inode->i_mutex);
 out_dput:
	dput(new_parent);
	dput(old_dentry);
	dput(new_dentry);
	return error;
}
Ejemplo n.º 10
0
int
nfs_sillyrename(struct inode *dir, struct dentry *dentry)
{
	static unsigned int sillycounter;
	const int      fileidsize  = sizeof(NFS_FILEID(dentry->d_inode))*2;
	const int      countersize = sizeof(sillycounter)*2;
	const int      slen        = sizeof(".nfs")+fileidsize+countersize-1;
	char           silly[slen+1];
	struct dentry *sdentry;
	struct rpc_task *task;
	int            error = -EIO;

	dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
		dentry->d_parent->d_name.name, dentry->d_name.name,
		dentry->d_count);
	nfs_inc_stats(dir, NFSIOS_SILLYRENAME);

	error = -EBUSY;
	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
		goto out;

	sprintf(silly, ".nfs%*.*Lx",
		fileidsize, fileidsize,
		(unsigned long long)NFS_FILEID(dentry->d_inode));

	
	nfs_inode_return_delegation(dentry->d_inode);

	sdentry = NULL;
	do {
		char *suffix = silly + slen - countersize;

		dput(sdentry);
		sillycounter++;
		sprintf(suffix, "%*.*x", countersize, countersize, sillycounter);

		dfprintk(VFS, "NFS: trying to rename %s to %s\n",
				dentry->d_name.name, silly);

		sdentry = lookup_one_len(silly, dentry->d_parent, slen);
		if (IS_ERR(sdentry))
			goto out;
	} while (sdentry->d_inode != NULL); 

	error = nfs_async_unlink(dir, dentry);
	if (error)
		goto out_dput;

	
	error = nfs_copy_dname(sdentry,
				(struct nfs_unlinkdata *)dentry->d_fsdata);
	if (error) {
		nfs_cancel_async_unlink(dentry);
		goto out_dput;
	}

	
	task = nfs_async_rename(dir, dir, dentry, sdentry);
	if (IS_ERR(task)) {
		error = -EBUSY;
		nfs_cancel_async_unlink(dentry);
		goto out_dput;
	}

	
	error = rpc_wait_for_completion_task(task);
	if (error == 0)
		error = task->tk_status;
	switch (error) {
	case 0:
		/* The rename succeeded */
		nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
		d_move(dentry, sdentry);
		break;
	case -ERESTARTSYS:
		/* The result of the rename is unknown. Play it safe by
		 * forcing a new lookup */
		d_drop(dentry);
		d_drop(sdentry);
	}
	rpc_put_task(task);
out_dput:
	dput(sdentry);
out:
	return error;
}
Ejemplo n.º 11
0
/*
 * rename uses retrying to avoid race-conditions: at least they should be minimal.
 * it tries to allocate all the blocks, then sanity-checks, and if the sanity-
 * checks fail, it tries to restart itself again. Very practical - no changes
 * are done until we know everything works ok.. and then all the changes can be
 * done in one fell swoop when we have claimed all the buffers needed.
 *
 * Anybody can rename anything with this: the permission checks are left to the
 * higher-level routines.
 */
static int do_minix_rename(struct inode * old_dir, struct dentry *old_dentry,
			   struct inode * new_dir, struct dentry *new_dentry)
{
	struct inode * old_inode, * new_inode;
	struct buffer_head * old_bh, * new_bh, * dir_bh;
	struct minix_dir_entry * old_de, * new_de;
	struct minix_sb_info * info;
	int retval;

	info = &old_dir->i_sb->u.minix_sb;
	goto start_up;
try_again:
	brelse(old_bh);
	brelse(new_bh);
	brelse(dir_bh);
	current->counter = 0;
	schedule();
start_up:
	old_inode = new_inode = NULL;
	old_bh = new_bh = dir_bh = NULL;
	old_bh = minix_find_entry(old_dir, old_dentry->d_name.name,
				  old_dentry->d_name.len, &old_de);
	retval = -ENOENT;
	if (!old_bh)
		goto end_rename;
	old_inode = old_dentry->d_inode;
	retval = -EPERM;
	new_inode = new_dentry->d_inode;
	new_bh = minix_find_entry(new_dir, new_dentry->d_name.name,
				  new_dentry->d_name.len, &new_de);
	if (new_bh) {
		if (!new_inode) {
			brelse(new_bh);
			new_bh = NULL;
		}
	}
	if (new_inode == old_inode) {
		retval = 0;
		goto end_rename;
	}
	if (S_ISDIR(old_inode->i_mode)) {
		retval = -EINVAL;
		if (is_subdir(new_dentry, old_dentry))
			goto end_rename;
		if (new_inode) {
			/* Prune any children before testing for busy */
			if (new_dentry->d_count > 1)
				shrink_dcache_parent(new_dentry);
			retval = -EBUSY;
			if (new_dentry->d_count > 1)
			retval = -ENOTEMPTY;
			if (!empty_dir(new_inode))
				goto end_rename;
			retval = -EBUSY;
		}
		retval = -EIO;
		dir_bh = minix_bread(old_inode,0,0);
		if (!dir_bh)
			goto end_rename;
		if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino)
			goto end_rename;
		retval = -EMLINK;
		if (!new_inode && new_dir->i_nlink >= info->s_link_max)
			goto end_rename;
	}
	if (!new_bh) {
		retval = minix_add_entry(new_dir,
					 new_dentry->d_name.name,
					 new_dentry->d_name.len,
					 &new_bh, &new_de);
		if (retval)
			goto end_rename;
	}
/* sanity checking before doing the rename - avoid races */
	if (new_inode && (new_de->inode != new_inode->i_ino))
		goto try_again;
	if (new_de->inode && !new_inode)
		goto try_again;
	if (old_de->inode != old_inode->i_ino)
		goto try_again;
/* ok, that's it */
	old_de->inode = 0;
	new_de->inode = old_inode->i_ino;
	old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
	mark_inode_dirty(old_dir);
	old_dir->i_version = ++event;
	new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
	mark_inode_dirty(new_dir);
	new_dir->i_version = ++event;
	if (new_inode) {
		new_inode->i_nlink--;
		new_inode->i_ctime = CURRENT_TIME;
		mark_inode_dirty(new_inode);
	}
	mark_buffer_dirty(old_bh, 1);
	mark_buffer_dirty(new_bh, 1);
	if (dir_bh) {
		PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
		mark_buffer_dirty(dir_bh, 1);
		old_dir->i_nlink--;
		mark_inode_dirty(old_dir);
		if (new_inode) {
			new_inode->i_nlink--;
			mark_inode_dirty(new_inode);
		} else {
			new_dir->i_nlink++;
			mark_inode_dirty(new_dir);
		}
	}
	/* Update the dcache */
	d_move(old_dentry, new_dentry);
	retval = 0;
end_rename:
	brelse(dir_bh);
	brelse(old_bh);
	brelse(new_bh);
	return retval;
}
Ejemplo n.º 12
0
DENT_T *
vnode_iop_lookup(
    INODE_T *dir,
    struct dentry *dent,
    struct nameidata *nd
)
{
    char *name;
    mdki_boolean_t rele = FALSE;
    int err;
    VNODE_T *dvp;
    VNODE_T *rt_vnode;                  /* returned vnode */
    INODE_T *rt_inode = NULL;           /* returned inode ptr */
    DENT_T * real_dentry;
    DENT_T *found_dentry = dent;
    VATTR_T *vap;
    struct lookup_ctx ctx;
    CALL_DATA_T cd;

    ASSERT_I_SEM_MINE(dir);
    /* We can find our parent entry via the dentry provided to us. */
    ASSERT(dent->d_parent->d_inode == dir);

    if (dent->d_name.len > NAME_MAX)
        return ERR_PTR(-ENAMETOOLONG);
    name = /* drop the const */(char *) dent->d_name.name;
    mdki_linux_init_call_data(&cd);

    /* We pass along the dentry, as well as the parent inode so that
     * mvop_linux_lookup_* has everything it needs, even if it is passed in
     * the realvp, and it gets back a negative dentry.
     */
    dvp = ITOV(dir);
    ctx.dentrypp = &found_dentry;
    ctx.flags = LOOKUP_CTX_VALID;

    err = VOP_LOOKUP(dvp, name, &rt_vnode, (struct pathname *)NULL,
                     VNODE_LF_LOOKUP, NULL, &cd, &ctx);
    err = mdki_errno_unix_to_linux(err);

    if (!err) {
        ASSERT(rt_vnode != NULL);
        if (MDKI_INOISCLRVN(VTOI(rt_vnode))) {
            /* unwrap to the real object */
            ASSERT(CVN_TO_DENT(rt_vnode));
            rt_inode = CVN_TO_INO(rt_vnode);
            if (MDKI_INOISMVFS(rt_inode)) {
                VN_HOLD(ITOV(rt_inode));
                VN_RELE(rt_vnode);
                rt_vnode = ITOV(rt_inode);
            } else {
                igrab(rt_inode);
                VN_RELE(rt_vnode);
                rt_vnode = NULL;
            }
        } else
            rt_inode = VTOI(rt_vnode);
    }
    if (!err && (found_dentry != dent)) {
        mdki_linux_destroy_call_data(&cd);
        /* The hold was granted in makeloopnode() in the 'nocover' case. */
        if (rt_vnode != NULL)
            VN_RELE(rt_vnode);
        else
            iput(rt_inode);
        /*
         * found_dentry is the real socket/block/char device node's dentry.
         * See mvop_linux_lookup_component().
         *
         * For sockets, we use a dentry in our tree (we fill in the
         * provided dentry "dent") linked to the inode of the real
         * object.  This lets file name operations work in our
         * namespace, and lets socket connections all work (as they're
         * keyed off of the inode address) from inside to outside &
         * v.v.
         *
         * We also do this for VCHR, VBLK devices, and it seems to work OK
         * (e.g. make a node the same as /dev/tty, you can write to it)
         */
        switch (found_dentry->d_inode->i_mode & S_IFMT) {
          case S_IFSOCK:
          case S_IFCHR:
          case S_IFBLK:
            ASSERT(dent->d_inode == NULL);
            MDKI_SET_DOPS(dent, &vnode_shadow_dentry_ops);
            igrab(found_dentry->d_inode);
            VNODE_D_ADD(dent, found_dentry->d_inode);
            VNODE_DPUT(found_dentry);
            found_dentry = NULL; /* tell caller to use original dentry */
            break;
          default:
            /* use returned dentry */
            break;
        }
        return(found_dentry);
    }

    /* We need to pass back dentry ops even for negative dentries, I think.
     * Shadow inodes will have been taken care of in lookup_component.
     */
    if (dent->d_op != &vnode_shadow_dentry_ops) {
        if (dent->d_parent->d_op == &vnode_setview_dentry_ops)
            MDKI_SET_DOPS(dent, &vnode_setview_dentry_ops);
        else
            MDKI_SET_DOPS(dent, &vnode_dentry_ops);
    }
    vap = VATTR_ALLOC();
    if (vap == NULL) {
        err = -ENOMEM;
        goto alloc_err;
    }
    if (!err && MDKI_INOISMVFS(rt_inode)) {
        /* fetch attributes & place in inode */
        VATTR_SET_MASK(vap, AT_ALL);
        err = VOP_GETATTR(rt_vnode, vap, GETATTR_FLAG_UPDATE_ATTRS, &cd);
        err = mdki_errno_unix_to_linux(err);
        if (err == -EOPNOTSUPP)          /* ignore it */
            err = 0;
        else if (err)
            rele = TRUE;
        else if ((rt_vnode->v_flag & VLOOPROOT) != 0 &&
                 rt_inode == vnlayer_get_urdir_inode())
        {
            /* return the real root */
            VN_RELE(rt_vnode);
            VATTR_FREE(vap);
            mdki_linux_destroy_call_data(&cd);
            return VNODE_DGET(vnlayer_get_root_dentry());
        }
        else if (vnlayer_looproot_vp != NULL &&
                 rt_vnode == vnlayer_looproot_vp &&
                 (real_dentry = MVOP_DENT(rt_inode,
                                          &vnode_dentry_ops)) != NULL)
        {
            /* return the real /view */
            VN_RELE(rt_vnode);
            VATTR_FREE(vap);
            mdki_linux_destroy_call_data(&cd);
            return real_dentry;
        }
    }
    VATTR_FREE(vap);
alloc_err:
    mdki_linux_destroy_call_data(&cd);

    /* It's an mnode-based object, set up a dentry for it */

    /* We don't return ENOENT.  For Linux, the negative dentry is enough */
    switch (err) {
      case -ENOENT:
        err = 0;
        ASSERT(rt_inode == NULL);
        VNODE_D_ADD(dent, rt_inode);
        break;
      case 0:
        /* We will consume the count on rt_inode as a reference for dent */
        /*
         * For VOB vnodes, we maintain two separate dentry trees for
         * the vnodes.  One tree is for setview-mode names (process
         * sets to a view context, then looks directly at the VOB
         * mountpoint without any cover vnodes in the path).  The
         * other tree is for view-extended naming into a VOB, with
         * dentries starting at the view tag and covering non-VOB
         * objects until crossing a mount point into a VOB.
         *
         * Mostly the system doesn't care, as long as it goes down the
         * tree from parent to child, since it will be traversing only one
         * of the dentry trees.  But when the cache misses, the system calls
         * this lookup method and wants to get a dentry in return.
         * There are standard interfaces ( d_splice_alias() in 2.6) 
         * which can find a good dentry  referencing the inode returned
         * by the file system's lookup method, but these methods don't 
         * work right when we have VOB directory vnodes with both setview 
         * and view-extended dentries.  We implement our own function
         * [vnlayer_inode2dentry_internal()] which knows the
         * distinctions and the rules for determining that an existing
         * attached dentry is valid for the lookup request.
         *
         * We have our own d_compare() function which forces all VOB
         * lookups to come to the inode lookup method (this function),
         * and then we get to choose the right dentry to return.  We
         * have our own lookup cache inside MVFS so we don't care that
         * the dentry cache is always missing on our names.
         *
         * If we have to make a new dentry, we may need to merge it
         * with an NFS-created temporary dentry using d_move()
         * (d_splice_alias() would do this for us, but we can't use it
         * for reasons listed above).
         */
        /*
         * We want to find the "right" dentry (if there is one), so
         * look for one that has a d_parent with the same dentry ops
         * (indicating it's in the same dentry tree).
         */
        if (S_ISDIR(rt_inode->i_mode)) {
            /*
             * It has been empirically shown that we have to check the 
             * parent of the dentry.  If the parent has been checked out
             * it is possible for the cache lookup to return an inode
             * from the tree below the old parent directory.  If this 
             * happens on a rename, the system will panic because the
             * Linux rename code checks the parent of the returned
             * dentry to see that it matches what it has for a parent.
             */
            found_dentry = vnlayer_inode2dentry_internal(rt_inode,
                                                         dent->d_parent, NULL,
                                                         dent->d_op);
        } else {
            /*
             * For non-directories, we also need to consider the
             * parent & the requested name so that
             * vnlayer_inode2dentry_internal() finds the right dentry.
             * (There may be multiple hard links; we want the one in
             * the same directory with the same name)
             */
            found_dentry = vnlayer_inode2dentry_internal(rt_inode,
                                                         dent->d_parent,
                                                         &dent->d_name,
                                                         dent->d_op);
        }
        if (found_dentry != NULL) {
            ASSERT(found_dentry->d_inode == rt_inode);
            /*
             * If the existing one is a disconnected dentry, we need
             * to move the old one to the new one (just like
             * d_splice_alias) to get the proper name/parent attached
             * in the dcache.
             */
            if ((found_dentry->d_flags & DCACHE_DISCONNECTED) != 0) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,7)
                ASSERT((dent->d_flags & DCACHE_UNHASHED) != 0);
#else
                ASSERT((dent->d_vfs_flags & DCACHE_UNHASHED) != 0);
#endif
                d_rehash(dent);
                d_move(found_dentry, dent);
            } 
            /* Release our count.  found_dentry also references inode. */
            iput(rt_inode);
            return found_dentry;
        }
        /*
         * Nothing suitable, wire it up to the proposed dentry.
         */
        VNODE_D_ADD(dent, rt_inode);
        break;
      default:
        /* some other error case */
        if (rele)
            VN_RELE(rt_vnode);
        break;
    }
    if (err)
        return ERR_PTR(err);
    else
        return NULL;
}
Ejemplo n.º 13
0
/*
 * rename uses retrying to avoid race-conditions: at least they should be minimal.
 * it tries to allocate all the blocks, then sanity-checks, and if the sanity-
 * checks fail, it tries to restart itself again. Very practical - no changes
 * are done until we know everything works ok.. and then all the changes can be
 * done in one fell swoop when we have claimed all the buffers needed.
 *
 * Anybody can rename anything with this: the permission checks are left to the
 * higher-level routines.
 */
static int do_sysv_rename(struct inode * old_dir, struct dentry * old_dentry,
			  struct inode * new_dir, struct dentry * new_dentry)
{
	struct inode * old_inode, * new_inode;
	struct buffer_head * old_bh, * new_bh, * dir_bh;
	struct sysv_dir_entry * old_de, * new_de;
	int retval;

	goto start_up;
try_again:
	brelse(old_bh);
	brelse(new_bh);
	brelse(dir_bh);
	current->counter = 0;
	schedule();
start_up:
	old_inode = new_inode = NULL;
	old_bh = new_bh = dir_bh = NULL;
	old_bh = sysv_find_entry(old_dir, old_dentry->d_name.name,
				old_dentry->d_name.len, &old_de);
	retval = -ENOENT;
	if (!old_bh)
		goto end_rename;
	old_inode = old_dentry->d_inode;	/* don't cross mnt-points */
	retval = -EPERM;
	new_inode = new_dentry->d_inode;
	new_bh = sysv_find_entry(new_dir, new_dentry->d_name.name,
				new_dentry->d_name.len, &new_de);
	if (new_bh) {
		if (!new_inode) {
			brelse(new_bh);
			new_bh = NULL;
		}
	}
	if (new_inode == old_inode) {
		retval = 0;
		goto end_rename;
	}
	if (S_ISDIR(old_inode->i_mode)) {
		retval = -EINVAL;
		if (is_subdir(new_dentry, old_dentry))
			goto end_rename;
		if (new_inode) {
			if (new_dentry->d_count > 1)
				shrink_dcache_parent(new_dentry);
			retval = -EBUSY;
			if (new_dentry->d_count > 1)
				goto end_rename;
			retval = -ENOTEMPTY;
			if (!empty_dir(new_inode))
				goto end_rename;
		}
		retval = -EIO;
		dir_bh = sysv_file_bread(old_inode, 0, 0);
		if (!dir_bh)
			goto end_rename;
		if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino)
			goto end_rename;
		retval = -EMLINK;
		if (!new_inode && new_dir->i_nlink >= new_dir->i_sb->sv_link_max)
			goto end_rename;
	}
	if (!new_bh) {
		retval = sysv_add_entry(new_dir, new_dentry->d_name.name,
					new_dentry->d_name.len, &new_bh, &new_de);
		if (retval)
			goto end_rename;
	}
/* sanity checking before doing the rename - avoid races */
	if (new_inode && (new_de->inode != new_inode->i_ino))
		goto try_again;
	if (new_de->inode && !new_inode)
		goto try_again;
	if (old_de->inode != old_inode->i_ino)
		goto try_again;
/* ok, that's it */
	old_de->inode = 0;
	new_de->inode = old_inode->i_ino;
	old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
	mark_inode_dirty(old_dir);
	new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
	mark_inode_dirty(new_dir);
	if (new_inode) {
		new_inode->i_nlink--;
		new_inode->i_ctime = CURRENT_TIME;
		mark_inode_dirty(new_inode);
	}
	mark_buffer_dirty(old_bh, 1);
	mark_buffer_dirty(new_bh, 1);
	if (dir_bh) {
		PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
		mark_buffer_dirty(dir_bh, 1);
		old_dir->i_nlink--;
		mark_inode_dirty(old_dir);
		if (new_inode) {
			new_inode->i_nlink--;
			mark_inode_dirty(new_inode);
		} else {
			new_dir->i_nlink++;
			mark_inode_dirty(new_dir);
		}
	}
	d_move(old_dentry, new_dentry);
	retval = 0;
end_rename:
	brelse(dir_bh);
	brelse(old_bh);
	brelse(new_bh);
	return retval;
}
Ejemplo n.º 14
0
Archivo: namei.c Proyecto: Goon83/SALB
static int pvfs2_rename(
    struct inode *old_dir,
    struct dentry *old_dentry,
    struct inode *new_dir,
    struct dentry *new_dentry)
{
    int ret = -EINVAL, are_directories = 0;
    pvfs2_inode_t *pvfs2_old_parent_inode = PVFS2_I(old_dir);
    pvfs2_inode_t *pvfs2_new_parent_inode = PVFS2_I(new_dir);
    pvfs2_kernel_op_t *new_op = NULL;
    struct super_block *sb = NULL;

    gossip_debug(GOSSIP_NAME_DEBUG, "pvfs2_rename: called (%s/%s => %s/%s) ct=%d\n",
                old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
                new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
                atomic_read(&new_dentry->d_count));

    are_directories = S_ISDIR(old_dentry->d_inode->i_mode);
#if 0
    /* NOTE: we have no good way to keep nlink consistent for directories
     * across clients; keep constant at 1  -Phil
     */
    if (are_directories && (new_dir->i_nlink >= PVFS2_LINK_MAX))
    {
        gossip_err("pvfs2_rename: directory %s surpassed "
                    "PVFS2_LINK_MAX\n",
                    new_dentry->d_name.name);
        return -EMLINK;
    }
#endif

    new_op = op_alloc(PVFS2_VFS_OP_RENAME);
    if (!new_op)
    {
	return ret;
    }

    /*
      if no handle/fs_id is available in the parent,
      use the root handle/fs_id as specified by the
      inode's corresponding superblock
    */
    if (pvfs2_old_parent_inode &&
            pvfs2_old_parent_inode->refn.handle != PVFS_HANDLE_NULL &&
            pvfs2_old_parent_inode->refn.fs_id != PVFS_FS_ID_NULL)
    {
        new_op->upcall.req.rename.old_parent_refn =
            pvfs2_old_parent_inode->refn;
    }
    else
    {
        sb = old_dir->i_sb;
        new_op->upcall.req.rename.old_parent_refn.handle =
	    PVFS2_SB(sb)->root_handle;
        new_op->upcall.req.rename.old_parent_refn.fs_id =
	    PVFS2_SB(sb)->fs_id;
    }

    /* do the same for the new parent */
    if (pvfs2_new_parent_inode &&
            pvfs2_new_parent_inode->refn.handle != PVFS_HANDLE_NULL &&
            pvfs2_new_parent_inode->refn.fs_id != PVFS_FS_ID_NULL)
    {
        new_op->upcall.req.rename.new_parent_refn =
            pvfs2_new_parent_inode->refn;
    }
    else
    {
        sb = new_dir->i_sb;
        new_op->upcall.req.rename.new_parent_refn.handle =
	    PVFS2_SB(sb)->root_handle;
        new_op->upcall.req.rename.new_parent_refn.fs_id =
	    PVFS2_SB(sb)->fs_id;
    }
    strncpy(new_op->upcall.req.rename.d_old_name,
	    old_dentry->d_name.name, PVFS2_NAME_LEN);
    strncpy(new_op->upcall.req.rename.d_new_name,
	    new_dentry->d_name.name, PVFS2_NAME_LEN);

    ret = service_operation(
        new_op, "pvfs2_rename", 
        get_interruptible_flag(old_dentry->d_inode));

    gossip_debug(GOSSIP_NAME_DEBUG, "pvfs2_rename: got downcall status %d\n", ret);

    if (new_dentry->d_inode)
    {
        new_dentry->d_inode->i_ctime = CURRENT_TIME;
#if 0
        /* NOTE: we have no good way to keep nlink consistent for directories
         * across clients; keep constant at 1  -Phil
         */
        if (are_directories)
        {
            new_dentry->d_inode->i_nlink--;
        }
#endif
    }
#if 0
    /* NOTE: we have no good way to keep nlink consistent for directories
     * across clients; keep constant at 1  -Phil
     */
    else if (are_directories)
    {
        new_dir->i_nlink++;
        old_dir->i_nlink--;
    }
#endif

    op_release(new_op);

#ifdef PVFS2_LINUX_KERNEL_2_4
    if (ret == 0)
    {
        d_move(old_dentry, new_dentry);
    }
#endif
    return ret;
}
static int nfs_sillyrename(struct inode *dir, struct dentry *dentry)
{
	static unsigned int sillycounter;
	const int      i_inosize  = sizeof(dir->i_ino)*2;
	const int      countersize = sizeof(sillycounter)*2;
	const int      slen       = strlen(".nfs") + i_inosize + countersize;
	char           silly[slen+1];
	struct qstr    qsilly;
	struct dentry *sdentry;
	int            error = -EIO;

	dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
		dentry->d_parent->d_name.name, dentry->d_name.name, 
		atomic_read(&dentry->d_count));

	if (atomic_read(&dentry->d_count) == 1)
		goto out;  /* No need to silly rename. */


#ifdef NFS_PARANOIA
if (!dentry->d_inode)
printk("NFS: silly-renaming %s/%s, negative dentry??\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
#endif
	/*
	 * We don't allow a dentry to be silly-renamed twice.
	 */
	error = -EBUSY;
	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
		goto out;

	sprintf(silly, ".nfs%*.*lx",
		i_inosize, i_inosize, dentry->d_inode->i_ino);

	sdentry = NULL;
	do {
		char *suffix = silly + slen - countersize;

		dput(sdentry);
		sillycounter++;
		sprintf(suffix, "%*.*x", countersize, countersize, sillycounter);

		dfprintk(VFS, "trying to rename %s to %s\n",
			 dentry->d_name.name, silly);
		
		sdentry = lookup_one_len(silly, dentry->d_parent, slen);
		/*
		 * N.B. Better to return EBUSY here ... it could be
		 * dangerous to delete the file while it's in use.
		 */
		if (IS_ERR(sdentry))
			goto out;
	} while(sdentry->d_inode != NULL); /* need negative lookup */

	nfs_zap_caches(dir);
	qsilly.name = silly;
	qsilly.len  = strlen(silly);
	error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, dir, &qsilly);
	if (!error) {
		nfs_renew_times(dentry);
		d_move(dentry, sdentry);
		error = nfs_async_unlink(dentry);
 		/* If we return 0 we don't unlink */
	}
	dput(sdentry);
out:
	return error;
}
/*
 * RENAME
 * FIXME: Some nfsds, like the Linux user space nfsd, may generate a
 * different file handle for the same inode after a rename (e.g. when
 * moving to a different directory). A fail-safe method to do so would
 * be to look up old_dir/old_name, create a link to new_dir/new_name and
 * rename the old file using the sillyrename stuff. This way, the original
 * file in old_dir will go away when the last process iput()s the inode.
 *
 * FIXED.
 * 
 * It actually works quite well. One needs to have the possibility for
 * at least one ".nfs..." file in each directory the file ever gets
 * moved or linked to which happens automagically with the new
 * implementation that only depends on the dcache stuff instead of
 * using the inode layer
 *
 * Unfortunately, things are a little more complicated than indicated
 * above. For a cross-directory move, we want to make sure we can get
 * rid of the old inode after the operation.  This means there must be
 * no pending writes (if it's a file), and the use count must be 1.
 * If these conditions are met, we can drop the dentries before doing
 * the rename.
 */
static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
		      struct inode *new_dir, struct dentry *new_dentry)
{
	struct inode *old_inode = old_dentry->d_inode;
	struct inode *new_inode = new_dentry->d_inode;
	struct dentry *dentry = NULL, *rehash = NULL;
	int error = -EBUSY;

	/*
	 * To prevent any new references to the target during the rename,
	 * we unhash the dentry and free the inode in advance.
	 */
	if (!d_unhashed(new_dentry)) {
		d_drop(new_dentry);
		rehash = new_dentry;
	}

	dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n",
		 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
		 new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
		 atomic_read(&new_dentry->d_count));

	/*
	 * First check whether the target is busy ... we can't
	 * safely do _any_ rename if the target is in use.
	 *
	 * For files, make a copy of the dentry and then do a 
	 * silly-rename. If the silly-rename succeeds, the
	 * copied dentry is hashed and becomes the new target.
	 */
	if (!new_inode)
		goto go_ahead;
	if (S_ISDIR(new_inode->i_mode))
		goto out;
	else if (atomic_read(&new_dentry->d_count) > 1) {
		int err;
		/* copy the target dentry's name */
		dentry = d_alloc(new_dentry->d_parent,
				 &new_dentry->d_name);
		if (!dentry)
			goto out;

		/* silly-rename the existing target ... */
		err = nfs_sillyrename(new_dir, new_dentry);
		if (!err) {
			new_dentry = rehash = dentry;
			new_inode = NULL;
			/* instantiate the replacement target */
			d_instantiate(new_dentry, NULL);
		}

		/* dentry still busy? */
		if (atomic_read(&new_dentry->d_count) > 1) {
#ifdef NFS_PARANOIA
			printk("nfs_rename: target %s/%s busy, d_count=%d\n",
			       new_dentry->d_parent->d_name.name,
			       new_dentry->d_name.name,
			       atomic_read(&new_dentry->d_count));
#endif
			goto out;
		}
	}

go_ahead:
	/*
	 * ... prune child dentries and writebacks if needed.
	 */
	if (atomic_read(&old_dentry->d_count) > 1) {
		nfs_wb_all(old_inode);
		shrink_dcache_parent(old_dentry);
	}

	if (new_inode)
		d_delete(new_dentry);

	nfs_zap_caches(new_dir);
	nfs_zap_caches(old_dir);
	error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name,
					   new_dir, &new_dentry->d_name);
out:
	if (rehash)
		d_rehash(rehash);
	if (!error && !S_ISDIR(old_inode->i_mode))
		d_move(old_dentry, new_dentry);

	/* new dentry created? */
	if (dentry)
		dput(dentry);
	return error;
}
Ejemplo n.º 17
0
Archivo: dir.c Proyecto: cilynx/dd-wrt
int sysfs_rename_dir(struct kobject *kobj, struct sysfs_dirent *new_parent_sd,
		     const char *new_name)
{
	struct sysfs_dirent *sd = kobj->sd;
	struct dentry *new_parent = NULL;
	struct dentry *old_dentry = NULL, *new_dentry = NULL;
	const char *dup_name = NULL;
	int error;

	/* get dentries */
	old_dentry = sysfs_get_dentry(sd);
	if (IS_ERR(old_dentry)) {
		error = PTR_ERR(old_dentry);
		goto out_dput;
	}

	new_parent = sysfs_get_dentry(new_parent_sd);
	if (IS_ERR(new_parent)) {
		error = PTR_ERR(new_parent);
		goto out_dput;
	}

	/* lock new_parent and get dentry for new name */
	mutex_lock(&new_parent->d_inode->i_mutex);

	new_dentry = lookup_one_len(new_name, new_parent, strlen(new_name));
	if (IS_ERR(new_dentry)) {
		error = PTR_ERR(new_dentry);
		goto out_unlock;
	}

	/* By allowing two different directories with the same
	 * d_parent we allow this routine to move between different
	 * shadows of the same directory
	 */
	error = -EINVAL;
	if (old_dentry->d_parent->d_inode != new_parent->d_inode ||
	    new_dentry->d_parent->d_inode != new_parent->d_inode ||
	    old_dentry == new_dentry)
		goto out_unlock;

	error = -EEXIST;
	if (new_dentry->d_inode)
		goto out_unlock;

	/* rename kobject and sysfs_dirent */
	error = -ENOMEM;
	new_name = dup_name = kstrdup(new_name, GFP_KERNEL);
	if (!new_name)
		goto out_drop;

	error = kobject_set_name(kobj, "%s", new_name);
	if (error)
		goto out_drop;

	mutex_lock(&sysfs_mutex);

	dup_name = sd->s_name;
	sd->s_name = new_name;

	/* move under the new parent */
	d_add(new_dentry, NULL);
	d_move(sd->s_dentry, new_dentry);

	sysfs_unlink_sibling(sd);
	sysfs_get(new_parent_sd);
	sysfs_put(sd->s_parent);
	sd->s_parent = new_parent_sd;
	sysfs_link_sibling(sd);

	mutex_unlock(&sysfs_mutex);

	error = 0;
	goto out_unlock;

 out_drop:
	d_drop(new_dentry);
 out_unlock:
	mutex_unlock(&new_parent->d_inode->i_mutex);
 out_dput:
	kfree(dup_name);
	dput(new_parent);
	dput(old_dentry);
	dput(new_dentry);
	return error;
}