// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2021. Huawei Technologies Co., Ltd. All rights reserved. * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include "euler.h" #include "dht.h" #include "dep.h" #include "lock.h" /* * If sbi->s_draining is set, do fsync after each namei syscall! This is much * better than lock transfer for volatility quota. */ static void sync_on_draining(struct inode *dir, struct inode *inode) { struct eufs_sb_info *sbi = EUFS_SB(dir->i_sb); if (likely(!sbi->s_draining)) return; /* fsync the inodes to reduce the number of dirty inodes */ fsync_on_draining(dir, inode); } static __always_inline void eufs_trace_newfile(const char *prompt, struct inode *dir, struct inode *inode, struct eufs_inode *pi, struct nv_dict_entry *de) { eufs_dbg("%s (%s): inode=%px pi=%px pi->root=%llx pi->mode=0%o de=%px de->len=%lld de->name=%6s de->nextname=%llx inode->nlink=%d pi->nlink=%d de->volatile_next=%llx de->next=%llx\n", __func__, prompt, inode, pi, eufs_iread_root(pi), eufs_iread_mode(pi), de, HASHLEN_LEN(de->hv), de->name, de->nextname, inode->i_nlink, pi->i_nlink, de->volatile_next, de->next); BUG_ON(inode->i_mode != pi->i_mode); } static __always_inline void eufs_trace_delfile(const char *prompt, struct inode *dir, struct inode *inode, struct eufs_inode *pi) { eufs_dbg("%s (%s): inode=%px pi=%px pi->root=%llx pi->mode=0%o inode->i_nlink=%d pi->i_nlink=%d\n", __func__, prompt, inode, pi, eufs_iread_root(pi), eufs_iread_mode(pi), inode->i_nlink, eufs_iread_nlink(pi)); /* * because inode is locked by unlink/link, so the increment/decrement * of nlink should be in order and its max value is (EUFS_LINK_MAX - 1) * after unlink. */ if ((inode->i_mode & S_IFMT) != S_IFDIR) WARN(inode->i_nlink >= EUFS_LINK_MAX, "unexpected nlink %d for inode 0x%lx\n", inode->i_nlink, inode->i_ino); } static __always_inline struct nv_dict_entry * nv_dict_add_wrapper(struct inode *dir, u64 **nv_header, struct eufs_inode *pi, hashlen_t hv, const char *name) { struct eufs_inode_info *vi = EUFS_I(dir); NV_ASSERT(pi); if (!vi->i_volatile_dict) vi->i_volatile_dict = eufs_zalloc_page(); /* insert into parent dir hash table */ return nv_dict_add(dir, nv_header, hv, name, pi); } static __always_inline struct nv_dict_entry * nv_dict_del_wrapper(struct inode *dir, struct nv_dict_entry **prevde, u64 **nv_header, hashlen_t hv, const char *name) { struct eufs_inode_info *vi = EUFS_I(dir); /* Alloc for dict if necessary */ if (!vi->i_volatile_dict) vi->i_volatile_dict = eufs_zalloc_page(); /* insert into parent dir hash table */ return nv_dict_delete(dir, prevde, nv_header, hv, name); } /* * Methods themselves. */ static struct dentry *eufs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct inode *inode = NULL; struct nv_dict_entry *de; const char *name; u64 hv; if (dentry->d_name.len > EUFS_MAX_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); hv = hash(dentry->d_name.name, dentry->d_name.len); name = dentry->d_name.name; de = nv_dict_find(dir, hv, name); if (!de) goto not_found; inode = eufs_iget(dir->i_sb, s2p(dir->i_sb, de->inode)); if (inode == ERR_PTR(-ESTALE)) { eufs_err(dir->i_sb, "deleted inode referenced: 0x%lx", inode->i_ino); return ERR_PTR(-EIO); } not_found: if (inode) BUG_ON(atomic_read(&inode->i_count) < 1); return d_splice_alias(inode, dentry); } static int add_pinode(struct inode *dir, struct dentry *dentry, struct inode *inode, bool need_unlock_inode) { /* Name must be checked before this is invoked. */ struct eufs_inode_info *dir_vi = EUFS_I(dir); struct eufs_inode *pi; const char *name; struct nv_dict_entry *de; u64 *nv_header; u64 hv; struct dep_node *dep; int err; dep = eufs_alloc_dep_node(); if (!dep) return -ENOMEM; if (need_unlock_inode) eufs_inode_mark_lock_transferable(inode); /* Add to dict */ pi = EUFS_PI(inode); name = dentry->d_name.name; hv = hash(name, dentry->d_name.len); de = nv_dict_add_wrapper(dir, &nv_header, pi, hv, name); if (IS_ERR(de)) { err = PTR_ERR(de); goto err_out; } /* One more dentry */ dir->i_size++; eufs_dbg("diradd +> %lld of %px 0x%lx\n", dir->i_size, dir, dir->i_ino); /* Update dir time */ dir->i_ctime = dir->i_mtime = current_time(dir); dep_new_insert(dep, dir, DEP_DIRADD, NULL, nv_header, de, inode, dir_vi->i_next_dep_seq); if (need_unlock_inode) eufs_inode_wait_lock_transfer_done(inode); dir_vi->i_next_dep_seq++; return 0; err_out: if (need_unlock_inode) eufs_inode_wait_lock_transfer_done(inode); eufs_free_dep_node(dep); return err; } static __always_inline int del_pinode(struct inode *dir, struct dentry *dentry, bool is_dir) { struct eufs_inode_info *dir_vi = EUFS_I(dir); struct inode *inode = dentry->d_inode; struct nv_dict_entry *de, *prevde; u64 *nv_header; const char *name; u64 hv; struct dep_node *dep; struct eufs_inode *pi; int err; dep = eufs_alloc_dep_node(); if (!dep) return -ENOMEM; eufs_inode_mark_lock_transferable(inode); /* Remove from parent dir hash table */ name = dentry->d_name.name; hv = hash(name, dentry->d_name.len); de = nv_dict_del_wrapper(dir, &prevde, &nv_header, hv, name); if (unlikely(!de)) { err = -ENOENT; goto err_out; } /* Drop one dentry */ dir->i_size--; eufs_dbg("dirdel -> %lld of %px 0x%lx\n", dir->i_size, dir, dir->i_ino); /* Update parent dir time */ dir->i_ctime = dir->i_mtime = current_time(dir); /* Update inode ctime and link */ inode->i_ctime = dir->i_ctime; if (is_dir) { /* Update nlink and ctime for the removed inode */ WARN_ON(inode->i_nlink != 2); clear_nlink(inode); } else if (inode->i_nlink) { drop_nlink(inode); } else { pi = EUFS_PI(inode); eufs_info("!%s!: inode=%p, inode->i_nlink=%d inode->i_mode=0%o pi=%p pi->i_nlink=%d pi->i_mode=0%o\n", __func__, inode, inode->i_nlink, inode->i_mode, pi, eufs_iread_nlink(pi), eufs_iread_mode(pi)); BUG(); } dep_new_insert(dep, dir, DEP_DIRREM, prevde, nv_header, de, inode, dir_vi->i_next_dep_seq); eufs_inode_wait_lock_transfer_done(inode); dir_vi->i_next_dep_seq++; return 0; err_out: eufs_inode_wait_lock_transfer_done(inode); eufs_free_dep_node(dep); return err; } static void eufs_free_new_inode(struct inode *inode) { clear_nlink(inode); remove_inode_hash(inode); unlock_new_inode(inode); iput(inode); } /* * By the time this is called, we already have created * the directory cache entry for the new file, but it * is so far negative - it has no inode. * * If the create succeeds, we fill in the inode information * with d_instantiate(). */ static int eufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { struct inode *inode; int err; /* name checks */ if (unlikely(!dentry->d_name.len)) return -EINVAL; if (unlikely(dentry->d_name.len > EUFS_MAX_NAME_LEN)) return -ENAMETOOLONG; inode = pre_inodes_get(dentry, dir, mode, false, 0); if (IS_ERR(inode)) return PTR_ERR(inode); err = add_pinode(dir, dentry, inode, false); if (err) { eufs_free_new_inode(inode); return err; } inode->i_op = &eufs_file_inode_operations; inode->i_mapping->a_ops = &eufs_aops; inode->i_fop = &eufs_file_operations; eufs_trace_newfile("!create!", dir, inode, EUFS_PI(inode), NULL); EUFS_I(inode)->i_is_dirty = true; d_instantiate(dentry, inode); unlock_new_inode(inode); sync_on_draining(dir, NULL); return 0; } static int eufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev) { struct inode *inode; int err; if (unlikely(!dentry->d_name.len)) return -EINVAL; if (unlikely(dentry->d_name.len > EUFS_MAX_NAME_LEN)) return -ENAMETOOLONG; inode = pre_inodes_get(dentry, dir, mode, true, rdev); if (IS_ERR(inode)) return PTR_ERR(inode); err = add_pinode(dir, dentry, inode, false); if (err) { eufs_free_new_inode(inode); return err; } inode->i_op = &eufs_special_inode_operations; eufs_trace_newfile("!mknode!", dir, inode, EUFS_PI(inode), NULL); EUFS_I(inode)->i_is_dirty = true; d_instantiate(dentry, inode); unlock_new_inode(inode); sync_on_draining(dir, NULL); return 0; } static int eufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct inode *inode = NULL; struct eufs_inode *pi; u32 len = strlen(symname); void *pi_root; int err; /* name checks */ if (unlikely(!dentry->d_name.len)) return -EINVAL; if (unlikely(dentry->d_name.len > EUFS_MAX_NAME_LEN)) return -ENAMETOOLONG; if (unlikely(len > EUFS_MAX_SYMLINK_LEN)) return -ENAMETOOLONG; /* alloc vfs inode and xxfs inode */ inode = pre_inodes_get(dentry, dir, S_IFLNK | S_IRWXUGO, false, 0); if (IS_ERR(inode)) return PTR_ERR(inode); pi = EUFS_FRESH_PI(EUFS_PI(inode)); pi_root = o2p(dir->i_sb, eufs_iread_root(pi)); /* copy the symname */ *((u64 *)pi_root) = hash(symname, len); memcpy(((char *)pi_root) + sizeof(u64), symname, len); BUG_ON(!eufs_access_ok(inode->i_sb, pi_root, PAGE_SIZE)); /* update the size */ inode->i_size = len; err = add_pinode(dir, dentry, inode, false); if (err) { eufs_free_new_inode(inode); return err; } inode->i_op = &eufs_symlink_inode_operations; inode->i_mapping->a_ops = &eufs_aops; eufs_trace_newfile("!symlink!", dir, inode, pi, NULL); EUFS_I(inode)->i_is_dirty = true; d_instantiate(dentry, inode); unlock_new_inode(inode); sync_on_draining(dir, NULL); return 0; } static int eufs_link(struct dentry *dest_dentry, struct inode *dir, struct dentry *dentry) { struct inode *inode = dest_dentry->d_inode; struct eufs_inode *pi = EUFS_PI(inode); struct nv_dict_entry *de; int err; /* name checks */ if (unlikely(!dentry->d_name.len)) return -EINVAL; if (unlikely(dentry->d_name.len > EUFS_MAX_NAME_LEN)) return -ENAMETOOLONG; /* nlink check */ if (unlikely(inode->i_nlink >= EUFS_LINK_MAX)) return -EMLINK; ihold(inode); err = add_pinode(dir, dentry, inode, true); if (unlikely(err)) { iput(inode); return err; } /* update inode ctime */ inode->i_ctime = current_time(inode); inc_nlink(inode); EUFS_I(inode)->i_is_dirty = true; d_instantiate(dentry, inode); eufs_trace_newfile("!link!", dir, inode, pi, de); /* inode_lock() has been acquired */ sync_on_draining(dir, inode); return 0; } static int eufs_unlink(struct inode *dir, struct dentry *dentry) { struct inode *inode = dentry->d_inode; int ret; ret = del_pinode(dir, dentry, false); if (ret < 0) return ret; eufs_trace_delfile("!unlink!", dir, inode, EUFS_PI(inode)); EUFS_I(inode)->i_is_dirty = true; sync_on_draining(dir, inode); return 0; } /* NOTE: do not count the link for directories */ static int eufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { struct inode *inode; struct eufs_inode *dir_pi = EUFS_PI(dir); struct eufs_inode_info *vi; struct eufs_inode *pi; int err; /* name checks */ if (unlikely(!dentry->d_name.len)) return -EINVAL; if (unlikely(dentry->d_name.len > EUFS_MAX_NAME_LEN)) return -ENAMETOOLONG; /* alloc vfs inode and xxfs inode */ inode = pre_inodes_get(dentry, dir, S_IFDIR | mode, false, 0); if (IS_ERR(inode)) return PTR_ERR(inode); inode->i_op = &eufs_dir_inode_operations; inode->i_fop = &eufs_dir_operations; inode->i_mapping->a_ops = &eufs_aops; /* We have to mimic the nlink number */ inc_nlink(inode); /* alloc & init dir hash table for new inode */ pi = EUFS_FRESH_PI(EUFS_PI(inode)); vi = EUFS_I(inode); vi->i_dotdot = p2o(dir->i_sb, dir_pi); pi->i_dotdot = cpu_to_le64(vi->i_dotdot); err = add_pinode(dir, dentry, inode, false); if (err) { eufs_free_new_inode(inode); return err; } /* We have to mimic the nlink number */ inc_nlink(dir); eufs_trace_newfile("!mkdir!", dir, inode, pi, NULL); vi->i_is_dirty = true; d_instantiate(dentry, inode); unlock_new_inode(inode); sync_on_draining(dir, NULL); PRINT_PINODE(pi, "FINAL-CHECK: "); BUG_ON(atomic_read(&dir->i_count) < 1); if (inode) BUG_ON(atomic_read(&inode->i_count) < 1); return 0; } /* * routine to check that the specified directory is empty (for rmdir) */ static __always_inline int eufs_empty_dir(struct inode *inode) { return !inode->i_size; } static int eufs_rmdir(struct inode *dir, struct dentry *dentry) { struct inode *inode = dentry->d_inode; int ret; /* checks before rmdir */ if (!inode) return -ENOENT; if (!eufs_empty_dir(inode)) return -ENOTEMPTY; ret = del_pinode(dir, dentry, true); if (ret < 0) return ret; /* We have to mimic the nlink number */ drop_nlink(dir); EUFS_I(inode)->i_is_dirty = true; eufs_trace_delfile("!rmdir!", dir, inode, EUFS_PI(inode)); sync_on_draining(dir, inode); return 0; } /* * Precondition: old_dentry exists in the old directory */ static int eufs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { struct inode *old_inode = old_dentry->d_inode; struct inode *new_inode = new_dentry->d_inode; /* * TODO: need to lock old_inode * If old_inode is a directory, its inode lock will * not be acquired, so the offset of newest physical node * may be changed during the rename procedure. */ struct eufs_inode *pi = EUFS_FRESH_PI(EUFS_PI(old_inode)); struct eufs_inode *old_dir_pi; struct eufs_inode *new_dir_pi; struct inode *locked_inodes[EUFS_INODE_CNT_IN_RENAME] = { old_dir, new_dir, old_inode, new_inode }; struct super_block *sb = old_inode->i_sb; struct nv_dict_entry *new_de; u64 *new_dir_nv_header; struct nv_dict_entry *old_de, *old_prevde; u64 *old_dir_nv_header; bool in_same_dir = (old_dir == new_dir); const char *name; struct eufs_renamej *renamej; u64 old_hv, new_hv; struct nv_dict_entry **vde; int cpu; void *buffer[16]; NV_ASSERT(pi->i_mode == old_inode->i_mode); if (flags & (RENAME_EXCHANGE | RENAME_WHITEOUT)) return -EOPNOTSUPP; /* checks */ if (new_inode) { eufs_dbg( "!new_inode=%px new_inode->i_count=%d new_dentry->d_lockref.count=%d\n", new_inode, atomic_read(&new_inode->i_count), new_dentry->d_lockref.count); if (S_ISDIR(new_inode->i_mode) && !eufs_empty_dir(new_inode)) return -ENOTEMPTY; eufs_dbg("rename overwrites! newinode=%px newpi=%px newinode->i_mode=0%o, newinode->root=%px oldinode=%px oldpi=%px oldinode->i_mode=0%o, oldinode->root=%px\n", new_inode, EUFS_PI(new_inode), new_inode->i_mode, EUFS_I(new_inode)->i_volatile_root, old_inode, EUFS_PI(old_inode), old_inode->i_mode, EUFS_I(old_inode)->i_volatile_root); BUG_ON(new_inode->i_mode != EUFS_PI(new_inode)->i_mode); } eufs_dbg("%s: rename %s to %s before fsync, old_pi=%px new_pi=%px\n", __func__, old_dentry->d_name.name, new_dentry->d_name.name, EUFS_PI(old_inode), new_inode ? EUFS_PI(new_inode) : (void *)-1); eufs_dbg("old: dir=%px inode=%px; new: dir=%px inode=%px\n", old_dir, old_inode, new_dir, new_inode); if (S_ISDIR(old_inode->i_mode)) locked_inodes[2] = NULL; if (locked_inodes[0] == locked_inodes[1]) locked_inodes[1] = NULL; if (locked_inodes[0]) BUG_ON(!inode_is_locked(locked_inodes[0])); if (locked_inodes[1]) BUG_ON(!inode_is_locked(locked_inodes[1])); if (locked_inodes[2]) BUG_ON(!inode_is_locked(locked_inodes[2])); if (locked_inodes[3]) BUG_ON(!inode_is_locked(locked_inodes[3])); fsync_rename_inodes(old_dir, new_dir, locked_inodes); /* * get the newer inodes after fsync_rename_inodes() completes * which may update the offset of the newer inodes */ old_dir_pi = EUFS_FRESH_PI(EUFS_PI(old_dir)); new_dir_pi = EUFS_FRESH_PI(EUFS_PI(new_dir)); /* -------------- get new dentry info -------------- */ /* get new filename */ new_hv = hash(new_dentry->d_name.name, new_dentry->d_name.len); name = new_dentry->d_name.name; eufs_dbg("%s: rename %s to %s\n", __func__, old_dentry->d_name.name, new_dentry->d_name.name); /* -------------- insertion ---------------- */ /* insert into parent dir hash table */ if (new_inode) { new_de = nv_dict_find(new_dir, new_hv, name); if (!new_de) return -ENOENT; /* Delay the actual write */ BUG_ON(!new_inode->i_nlink); ihold(new_inode); /* We have new_inode in hand */ if (S_ISDIR(new_inode->i_mode)) { WARN_ON(new_inode->i_nlink != 2); clear_nlink(new_inode); } else { drop_nlink(new_inode); } new_dir_nv_header = NULL; } else { new_de = nv_dict_add_wrapper(new_dir, &new_dir_nv_header, EUFS_HEAD_PI(pi), new_hv, name); if (IS_ERR(new_de)) return PTR_ERR(new_de); if (unlikely(!new_de)) return -EEXIST; /* We have no dep in rename. Just release the header lock */ inode_header_unlock(new_dir); if (!in_same_dir) { new_dir->i_size++; if (S_ISDIR(old_inode->i_mode)) inc_nlink(new_dir); } eufs_dbg("rename diradd +> %lld of %px 0x%lx\n", new_dir->i_size, new_dir, new_dir->i_ino); } /* update dir time */ new_dir->i_ctime = new_dir->i_mtime = current_time(new_dir); /* -------------- get old dentry info -------------- */ /* get old filename */ old_hv = hash(old_dentry->d_name.name, old_dentry->d_name.len); name = old_dentry->d_name.name; if (!name) { BUG(); return -ENOENT; } /* -------------- removal ---------------- */ old_de = nv_dict_del_wrapper(old_dir, &old_prevde, &old_dir_nv_header, old_hv, name); if (unlikely(!old_de)) { BUG(); return -ENOENT; } /* We have no dep in rename. Just release the header lock */ inode_header_unlock(old_dir); if (!in_same_dir || new_inode) { old_dir->i_size--; if (S_ISDIR(old_inode->i_mode)) drop_nlink(old_dir); } if (old_dir != new_dir) old_dir->i_ctime = old_dir->i_mtime = new_dir->i_ctime; eufs_dbg("rename dirdel -> %lld of %px 0x%lx\n", old_dir->i_size, old_dir, old_dir->i_ino); /* old_inode may NOT be locked ? */ /* update ctime of source inode */ old_inode->i_ctime = new_dir->i_ctime; if (!in_same_dir && S_ISDIR(old_inode->i_mode)) { /* update parent pointer of source inode */ struct eufs_inode_info *vi = EUFS_I(old_inode); vi->i_dotdot = p2o(sb, EUFS_HEAD_PI(new_dir_pi)); } NV_ASSERT(new_de->inode == old_de->inode); NV_ASSERT(eufs_valid_inode_in_de(old_de, old_inode)); NV_ASSERT(old_inode->i_mode == eufs_iread_mode(EUFS_FRESH_PI( (struct eufs_inode *)(s2p(sb, old_de->inode))))); if (!new_inode) { struct alloc_batch ab; ab.n_used = 0; ab.size = 16; ab.batch = buffer; eufs_alloc_batch_add(old_dir->i_sb, &ab, new_de); persist_name(old_dir->i_sb, new_de, &ab); eufs_dentry_clr_not_persist_flag(new_de); persist_dentry(new_de); eufs_alloc_batch_persist_reset(old_dir->i_sb, &ab); } cpu = get_cpu(); /* RenameJ is redo log */ renamej = eufs_get_renamej(old_dir->i_sb, cpu); renamej->crc = 0; renamej->flags = 0; /* address to put old_de->next */ renamej->addr_of_oldnext = p2s(sb, (old_prevde ? &old_prevde->next : (void *)old_dir_nv_header)); /* the value: old_de->next */ renamej->oldnext = (old_prevde ? old_de->next : old_de->next == EUFS_DIR_EOC ? NULL_VAL : COMPOSE_DICT_HEAD_le64(sb, s2p(sb, old_de->next))); /* address to put new_de if necessary */ renamej->addr_of_newde = p2s(sb, new_dir_nv_header); /* the value: new_de */ renamej->composed_newde = COMPOSE_DICT_HEAD_le64(sb, new_de); /* the value: new_de->inode */ renamej->newde_inode = p2s(sb, EUFS_HEAD_PI(pi)); /* dir pi */ renamej->old_dir_pi = p2s(sb, EUFS_HEAD_PI(old_dir_pi)); renamej->new_dir_pi = p2s(sb, EUFS_HEAD_PI(new_dir_pi)); /* inode attributes */ renamej->time = cpu_to_le64(new_dir->i_ctime.tv_sec); renamej->time_nsec = cpu_to_le32(new_dir->i_ctime.tv_nsec); renamej->old_link = cpu_to_le16(old_dir->i_nlink); renamej->new_link = cpu_to_le16(new_dir->i_nlink); renamej->old_size = cpu_to_le32(old_dir->i_size); renamej->new_size = cpu_to_le32(new_dir->i_size); memset(renamej->pad, 0, sizeof(renamej->pad)); renamej->flags = EUFS_RENAME_IN_ACTION; renamej->crc = cpu_to_le32( crc32c(EUFS_CRC_SEED, (char *)renamej + sizeof(renamej->crc), sizeof(*renamej) - sizeof(renamej->crc))); eufs_flush_cacheline((char *)renamej + CACHELINE_SIZE); eufs_flush_cacheline(renamej); if (old_prevde) { old_prevde->next = old_de->next; } else { if (old_de->next == EUFS_DIR_EOC) *old_dir_nv_header = NULL_VAL; else *old_dir_nv_header = COMPOSE_DICT_HEAD_le64( sb, s2p(sb, old_de->next)); } eufs_flush_cacheline(old_prevde ? (void *)&old_prevde->next : (void *)old_dir_nv_header); vde = &(EUFS_I(old_dir)->i_volatile_dict->table[INDEX(old_hv)]); if (*vde) { bool vbool = (*vde == NULL || *vde == (void *)EUFS_DIR_EOC); bool pbool = (*old_dir_nv_header == NULL_VAL || *old_dir_nv_header == EUFS_DIR_EOC); BUG_ON(vbool != pbool); *vde = NULL; } if (new_inode) { new_de->inode = p2s(sb, EUFS_HEAD_PI(pi)); eufs_flush_cacheline(new_de); } else { *new_dir_nv_header = COMPOSE_DICT_HEAD_le64(sb, new_de); eufs_flush_cacheline(new_dir_nv_header); vde = &EUFS_I(new_dir)->i_volatile_dict->table[INDEX(new_hv)]; if (*vde) { bool vbool = (*vde == NULL || *vde == (void *)EUFS_DIR_EOC); bool pbool = (*new_dir_nv_header == NULL_VAL || *new_dir_nv_header == EUFS_DIR_EOC); BUG_ON(vbool != pbool); *vde = NULL; } } eufs_iwrite_size(old_dir_pi, old_dir->i_size); eufs_iwrite_nlink(old_dir_pi, old_dir->i_nlink); eufs_iwrite_ctime_mtime(old_dir_pi, old_dir); eufs_flush_pi(old_dir_pi); if (old_dir != new_dir) { eufs_iwrite_size(new_dir_pi, new_dir->i_size); eufs_iwrite_nlink(new_dir_pi, new_dir->i_nlink); eufs_iwrite_ctime_mtime(new_dir_pi, new_dir); eufs_flush_pi(new_dir_pi); } eufs_iwrite_ctime(pi, old_inode->i_ctime.tv_sec); eufs_iwrite_ctime_nsec(pi, old_inode->i_ctime.tv_nsec); if (!in_same_dir && S_ISDIR(old_inode->i_mode)) { struct eufs_inode_info *vi = EUFS_I(old_inode); eufs_iwrite_dotdot(pi, vi->i_dotdot); } eufs_flush_pi(pi); renamej->flags = 0; eufs_flush_cacheline(renamej); put_cpu(); /* remove overwritten inode */ if (new_inode) iput(new_inode); /* remove the source dentry */ eufs_free_name(old_dir->i_sb, old_de); nv_free(old_dir->i_sb, old_de); eufs_dbg("%s: renamed %s to %s , old_pi=%llx new_pi=%llx\n", __func__, old_dentry->d_name.name, new_dentry->d_name.name, old_de->inode, new_de->inode); return 0; } const struct inode_operations eufs_dir_inode_operations = { .create = eufs_create, .lookup = eufs_lookup, .link = eufs_link, .unlink = eufs_unlink, .symlink = eufs_symlink, .mkdir = eufs_mkdir, .rmdir = eufs_rmdir, .mknod = eufs_mknod, .rename = eufs_rename, .setattr = eufs_notify_change, }; const struct inode_operations eufs_special_inode_operations = { .setattr = eufs_notify_change, };