/* 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; }
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); }
/** * 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; }
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; }
/* * 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; }
/** * 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; }
/** * 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); } }
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; }
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; }
/* * 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; }
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; }
/* * 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; }
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; }
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; }