From nobody Thu Feb 12 22:04:32 2026 Received: from szxga01-in.huawei.com (szxga01-in.huawei.com [45.249.212.187]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2E57F15572A for ; Fri, 7 Jun 2024 04:27:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=45.249.212.187 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717734440; cv=none; b=SyFuoGKDPzlG7pXdwAhbNC5JBuj4R2yU/vV2vXUkKPJ02m2wpsq8I+bXh3/Nm5WsbRa3xtzBjHPWb7CFfd8Ppi1cBHVkHsdy0N7UWVE2kBU0P0bT7MOdMvUM9IX64D2WKIBYv8HXYOcpYRbfyxaHQYzZQEpIGlEiETgO3DdwIC8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717734440; c=relaxed/simple; bh=EoJpPamG93fb7cfh28ig/eO+8F5KebDCklOQIEwBQeU=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=BjwxotmkurseSdhRwvojgl8uPokFadPkfqNgg0zvovNxlB1ooxmjYCrAd5q6FLS4kF6mh8+gcVFSdFIspVkHbZswa5E+3/4mzApXjgCP+TR5Pc3oaksDBfZHr5Sn0oL1Jd80auJ5B0cg6TZmqjeXxB3F4oc48ef2GtJq0xZwPpE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com; spf=pass smtp.mailfrom=huawei.com; arc=none smtp.client-ip=45.249.212.187 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei.com Received: from mail.maildlp.com (unknown [172.19.163.252]) by szxga01-in.huawei.com (SkyGuard) with ESMTP id 4VwSjr33zHzwS93; Fri, 7 Jun 2024 12:23:12 +0800 (CST) Received: from kwepemm600013.china.huawei.com (unknown [7.193.23.68]) by mail.maildlp.com (Postfix) with ESMTPS id 4E46A18007E; Fri, 7 Jun 2024 12:27:11 +0800 (CST) Received: from huawei.com (10.175.104.67) by kwepemm600013.china.huawei.com (7.193.23.68) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.39; Fri, 7 Jun 2024 12:26:58 +0800 From: Zhihao Cheng To: , , , , , CC: , Subject: [RFC PATCH mtd-utils 043/110] ubifs-utils: Adapt dir.c in libubifs Date: Fri, 7 Jun 2024 12:25:08 +0800 Message-ID: <20240607042615.2069840-44-chengzhihao1@huawei.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240607042615.2069840-1-chengzhihao1@huawei.com> References: <20240607042615.2069840-1-chengzhihao1@huawei.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To kwepemm600013.china.huawei.com (7.193.23.68) Content-Type: text/plain; charset="utf-8" Adapt dir.c in libubifs, compared with linux kernel implementations: 1. Remove all functions. Only keep an empty source file, fsck will add new functions for mkdir/link operations without using linux in-memory inode/dentry. Signed-off-by: Zhihao Cheng --- ubifs-utils/libubifs/dir.c | 1713 ----------------------------------------= ---- 1 file changed, 1713 deletions(-) diff --git a/ubifs-utils/libubifs/dir.c b/ubifs-utils/libubifs/dir.c index c77ea57f..9d5f6446 100644 --- a/ubifs-utils/libubifs/dir.c +++ b/ubifs-utils/libubifs/dir.c @@ -29,1716 +29,3 @@ */ =20 #include "ubifs.h" - -/** - * inherit_flags - inherit flags of the parent inode. - * @dir: parent inode - * @mode: new inode mode flags - * - * This is a helper function for 'ubifs_new_inode()' which inherits flag o= f the - * parent directory inode @dir. UBIFS inodes inherit the following flags: - * o %UBIFS_COMPR_FL, which is useful to switch compression on/of on - * sub-directory basis; - * o %UBIFS_SYNC_FL - useful for the same reasons; - * o %UBIFS_DIRSYNC_FL - similar, but relevant only to directories. - * - * This function returns the inherited flags. - */ -static int inherit_flags(const struct inode *dir, umode_t mode) -{ - int flags; - const struct ubifs_inode *ui =3D ubifs_inode(dir); - - if (!S_ISDIR(dir->i_mode)) - /* - * The parent is not a directory, which means that an extended - * attribute inode is being created. No flags. - */ - return 0; - - flags =3D ui->flags & (UBIFS_COMPR_FL | UBIFS_SYNC_FL | UBIFS_DIRSYNC_FL); - if (!S_ISDIR(mode)) - /* The "DIRSYNC" flag only applies to directories */ - flags &=3D ~UBIFS_DIRSYNC_FL; - return flags; -} - -/** - * ubifs_new_inode - allocate new UBIFS inode object. - * @c: UBIFS file-system description object - * @dir: parent directory inode - * @mode: inode mode flags - * @is_xattr: whether the inode is xattr inode - * - * This function finds an unused inode number, allocates new inode and - * initializes it. Non-xattr new inode may be written with xattrs(selinux/ - * encryption) before writing dentry, which could cause inconsistent probl= em - * when powercut happens between two operations. To deal with it, non-xattr - * new inode is initialized with zero-nlink and added into orphan list, ca= ller - * should make sure that inode is relinked later, and make sure that orphan - * removing and journal writing into an committing atomic operation. Retur= ns - * new inode in case of success and an error code in case of failure. - */ -struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir, - umode_t mode, bool is_xattr) -{ - int err; - struct inode *inode; - struct ubifs_inode *ui; - bool encrypted =3D false; - - inode =3D new_inode(c->vfs_sb); - ui =3D ubifs_inode(inode); - if (!inode) - return ERR_PTR(-ENOMEM); - - /* - * Set 'S_NOCMTIME' to prevent VFS form updating [mc]time of inodes and - * marking them dirty in file write path (see 'file_update_time()'). - * UBIFS has to fully control "clean <-> dirty" transitions of inodes - * to make budgeting work. - */ - inode->i_flags |=3D S_NOCMTIME; - - inode_init_owner(&nop_mnt_idmap, inode, dir, mode); - simple_inode_init_ts(inode); - inode->i_mapping->nrpages =3D 0; - - if (!is_xattr) { - err =3D fscrypt_prepare_new_inode(dir, inode, &encrypted); - if (err) { - ubifs_err(c, "fscrypt_prepare_new_inode failed: %i", err); - goto out_iput; - } - } - - switch (mode & S_IFMT) { - case S_IFREG: - inode->i_mapping->a_ops =3D &ubifs_file_address_operations; - inode->i_op =3D &ubifs_file_inode_operations; - inode->i_fop =3D &ubifs_file_operations; - break; - case S_IFDIR: - inode->i_op =3D &ubifs_dir_inode_operations; - inode->i_fop =3D &ubifs_dir_operations; - inode->i_size =3D ui->ui_size =3D UBIFS_INO_NODE_SZ; - break; - case S_IFLNK: - inode->i_op =3D &ubifs_symlink_inode_operations; - break; - case S_IFSOCK: - case S_IFIFO: - case S_IFBLK: - case S_IFCHR: - inode->i_op =3D &ubifs_file_inode_operations; - break; - default: - BUG(); - } - - ui->flags =3D inherit_flags(dir, mode); - ubifs_set_inode_flags(inode); - if (S_ISREG(mode)) - ui->compr_type =3D c->default_compr; - else - ui->compr_type =3D UBIFS_COMPR_NONE; - ui->synced_i_size =3D 0; - - spin_lock(&c->cnt_lock); - /* Inode number overflow is currently not supported */ - if (c->highest_inum >=3D INUM_WARN_WATERMARK) { - if (c->highest_inum >=3D INUM_WATERMARK) { - spin_unlock(&c->cnt_lock); - ubifs_err(c, "out of inode numbers"); - err =3D -EINVAL; - goto out_iput; - } - ubifs_warn(c, "running out of inode numbers (current %lu, max %u)", - (unsigned long)c->highest_inum, INUM_WATERMARK); - } - - inode->i_ino =3D ++c->highest_inum; - /* - * The creation sequence number remains with this inode for its - * lifetime. All nodes for this inode have a greater sequence number, - * and so it is possible to distinguish obsolete nodes belonging to a - * previous incarnation of the same inode number - for example, for the - * purpose of rebuilding the index. - */ - ui->creat_sqnum =3D ++c->max_sqnum; - spin_unlock(&c->cnt_lock); - - if (!is_xattr) { - set_nlink(inode, 0); - err =3D ubifs_add_orphan(c, inode->i_ino); - if (err) { - ubifs_err(c, "ubifs_add_orphan failed: %i", err); - goto out_iput; - } - down_read(&c->commit_sem); - ui->del_cmtno =3D c->cmt_no; - up_read(&c->commit_sem); - } - - if (encrypted) { - err =3D fscrypt_set_context(inode, NULL); - if (err) { - if (!is_xattr) { - set_nlink(inode, 1); - ubifs_delete_orphan(c, inode->i_ino); - } - ubifs_err(c, "fscrypt_set_context failed: %i", err); - goto out_iput; - } - } - - return inode; - -out_iput: - make_bad_inode(inode); - iput(inode); - return ERR_PTR(err); -} - -static int dbg_check_name(const struct ubifs_info *c, - const struct ubifs_dent_node *dent, - const struct fscrypt_name *nm) -{ - if (!dbg_is_chk_gen(c)) - return 0; - if (le16_to_cpu(dent->nlen) !=3D fname_len(nm)) - return -EINVAL; - if (memcmp(dent->name, fname_name(nm), fname_len(nm))) - return -EINVAL; - return 0; -} - -static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentr= y, - unsigned int flags) -{ - int err; - union ubifs_key key; - struct inode *inode =3D NULL; - struct ubifs_dent_node *dent =3D NULL; - struct ubifs_info *c =3D dir->i_sb->s_fs_info; - struct fscrypt_name nm; - - dbg_gen("'%pd' in dir ino %lu", dentry, dir->i_ino); - - err =3D fscrypt_prepare_lookup(dir, dentry, &nm); - if (err =3D=3D -ENOENT) - return d_splice_alias(NULL, dentry); - if (err) - return ERR_PTR(err); - - if (fname_len(&nm) > UBIFS_MAX_NLEN) { - inode =3D ERR_PTR(-ENAMETOOLONG); - goto done; - } - - dent =3D kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS); - if (!dent) { - inode =3D ERR_PTR(-ENOMEM); - goto done; - } - - if (fname_name(&nm) =3D=3D NULL) { - if (nm.hash & ~UBIFS_S_KEY_HASH_MASK) - goto done; /* ENOENT */ - dent_key_init_hash(c, &key, dir->i_ino, nm.hash); - err =3D ubifs_tnc_lookup_dh(c, &key, dent, nm.minor_hash); - } else { - dent_key_init(c, &key, dir->i_ino, &nm); - err =3D ubifs_tnc_lookup_nm(c, &key, dent, &nm); - } - - if (err) { - if (err =3D=3D -ENOENT) - dbg_gen("not found"); - else - inode =3D ERR_PTR(err); - goto done; - } - - if (dbg_check_name(c, dent, &nm)) { - inode =3D ERR_PTR(-EINVAL); - goto done; - } - - inode =3D ubifs_iget(dir->i_sb, le64_to_cpu(dent->inum)); - if (IS_ERR(inode)) { - /* - * This should not happen. Probably the file-system needs - * checking. - */ - err =3D PTR_ERR(inode); - ubifs_err(c, "dead directory entry '%pd', error %d", - dentry, err); - ubifs_ro_mode(c, err); - goto done; - } - - if (IS_ENCRYPTED(dir) && - (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) && - !fscrypt_has_permitted_context(dir, inode)) { - ubifs_warn(c, "Inconsistent encryption contexts: %lu/%lu", - dir->i_ino, inode->i_ino); - iput(inode); - inode =3D ERR_PTR(-EPERM); - } - -done: - kfree(dent); - fscrypt_free_filename(&nm); - return d_splice_alias(inode, dentry); -} - -static int ubifs_prepare_create(struct inode *dir, struct dentry *dentry, - struct fscrypt_name *nm) -{ - if (fscrypt_is_nokey_name(dentry)) - return -ENOKEY; - - return fscrypt_setup_filename(dir, &dentry->d_name, 0, nm); -} - -static int ubifs_create(struct mnt_idmap *idmap, struct inode *dir, - struct dentry *dentry, umode_t mode, bool excl) -{ - struct inode *inode; - struct ubifs_info *c =3D dir->i_sb->s_fs_info; - struct ubifs_budget_req req =3D { .new_ino =3D 1, .new_dent =3D 1, - .dirtied_ino =3D 1 }; - struct ubifs_inode *dir_ui =3D ubifs_inode(dir); - struct fscrypt_name nm; - int err, sz_change; - - /* - * Budget request settings: new inode, new direntry, changing the - * parent directory inode. - */ - - dbg_gen("dent '%pd', mode %#hx in dir ino %lu", - dentry, mode, dir->i_ino); - - err =3D ubifs_budget_space(c, &req); - if (err) - return err; - - err =3D ubifs_prepare_create(dir, dentry, &nm); - if (err) - goto out_budg; - - sz_change =3D CALC_DENT_SIZE(fname_len(&nm)); - - inode =3D ubifs_new_inode(c, dir, mode, false); - if (IS_ERR(inode)) { - err =3D PTR_ERR(inode); - goto out_fname; - } - - err =3D ubifs_init_security(dir, inode, &dentry->d_name); - if (err) - goto out_inode; - - set_nlink(inode, 1); - mutex_lock(&dir_ui->ui_mutex); - dir->i_size +=3D sz_change; - dir_ui->ui_size =3D dir->i_size; - inode_set_mtime_to_ts(dir, - inode_set_ctime_to_ts(dir, inode_get_ctime(inode))); - err =3D ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 1); - if (err) - goto out_cancel; - mutex_unlock(&dir_ui->ui_mutex); - - ubifs_release_budget(c, &req); - fscrypt_free_filename(&nm); - insert_inode_hash(inode); - d_instantiate(dentry, inode); - return 0; - -out_cancel: - dir->i_size -=3D sz_change; - dir_ui->ui_size =3D dir->i_size; - mutex_unlock(&dir_ui->ui_mutex); - set_nlink(inode, 0); -out_inode: - iput(inode); -out_fname: - fscrypt_free_filename(&nm); -out_budg: - ubifs_release_budget(c, &req); - ubifs_err(c, "cannot create regular file, error %d", err); - return err; -} - -static struct inode *create_whiteout(struct inode *dir, struct dentry *den= try) -{ - int err; - umode_t mode =3D S_IFCHR | WHITEOUT_MODE; - struct inode *inode; - struct ubifs_info *c =3D dir->i_sb->s_fs_info; - - /* - * Create an inode('nlink =3D 1') for whiteout without updating journal, - * let ubifs_jnl_rename() store it on flash to complete rename whiteout - * atomically. - */ - - dbg_gen("dent '%pd', mode %#hx in dir ino %lu", - dentry, mode, dir->i_ino); - - inode =3D ubifs_new_inode(c, dir, mode, false); - if (IS_ERR(inode)) { - err =3D PTR_ERR(inode); - goto out_free; - } - - init_special_inode(inode, inode->i_mode, WHITEOUT_DEV); - ubifs_assert(c, inode->i_op =3D=3D &ubifs_file_inode_operations); - - err =3D ubifs_init_security(dir, inode, &dentry->d_name); - if (err) - goto out_inode; - - /* The dir size is updated by do_rename. */ - insert_inode_hash(inode); - - return inode; - -out_inode: - iput(inode); -out_free: - ubifs_err(c, "cannot create whiteout file, error %d", err); - return ERR_PTR(err); -} - -/** - * lock_2_inodes - a wrapper for locking two UBIFS inodes. - * @inode1: first inode - * @inode2: second inode - * - * We do not implement any tricks to guarantee strict lock ordering, becau= se - * VFS has already done it for us on the @i_mutex. So this is just a simple - * wrapper function. - */ -static void lock_2_inodes(struct inode *inode1, struct inode *inode2) -{ - mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1); - mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2); -} - -/** - * unlock_2_inodes - a wrapper for unlocking two UBIFS inodes. - * @inode1: first inode - * @inode2: second inode - */ -static void unlock_2_inodes(struct inode *inode1, struct inode *inode2) -{ - mutex_unlock(&ubifs_inode(inode2)->ui_mutex); - mutex_unlock(&ubifs_inode(inode1)->ui_mutex); -} - -static int ubifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, - struct file *file, umode_t mode) -{ - struct dentry *dentry =3D file->f_path.dentry; - struct inode *inode; - struct ubifs_info *c =3D dir->i_sb->s_fs_info; - struct ubifs_budget_req req =3D { .new_ino =3D 1, .new_dent =3D 1, - .dirtied_ino =3D 1}; - struct ubifs_budget_req ino_req =3D { .dirtied_ino =3D 1 }; - struct ubifs_inode *ui; - int err, instantiated =3D 0; - struct fscrypt_name nm; - - /* - * Budget request settings: new inode, new direntry, changing the - * parent directory inode. - * Allocate budget separately for new dirtied inode, the budget will - * be released via writeback. - */ - - dbg_gen("dent '%pd', mode %#hx in dir ino %lu", - dentry, mode, dir->i_ino); - - err =3D fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm); - if (err) - return err; - - err =3D ubifs_budget_space(c, &req); - if (err) { - fscrypt_free_filename(&nm); - return err; - } - - err =3D ubifs_budget_space(c, &ino_req); - if (err) { - ubifs_release_budget(c, &req); - fscrypt_free_filename(&nm); - return err; - } - - inode =3D ubifs_new_inode(c, dir, mode, false); - if (IS_ERR(inode)) { - err =3D PTR_ERR(inode); - goto out_budg; - } - ui =3D ubifs_inode(inode); - - err =3D ubifs_init_security(dir, inode, &dentry->d_name); - if (err) - goto out_inode; - - set_nlink(inode, 1); - mutex_lock(&ui->ui_mutex); - insert_inode_hash(inode); - d_tmpfile(file, inode); - ubifs_assert(c, ui->dirty); - - instantiated =3D 1; - mutex_unlock(&ui->ui_mutex); - - lock_2_inodes(dir, inode); - err =3D ubifs_jnl_update(c, dir, &nm, inode, 1, 0, 1); - if (err) - goto out_cancel; - unlock_2_inodes(dir, inode); - - ubifs_release_budget(c, &req); - fscrypt_free_filename(&nm); - - return finish_open_simple(file, 0); - -out_cancel: - unlock_2_inodes(dir, inode); -out_inode: - if (!instantiated) - iput(inode); -out_budg: - ubifs_release_budget(c, &req); - if (!instantiated) - ubifs_release_budget(c, &ino_req); - fscrypt_free_filename(&nm); - ubifs_err(c, "cannot create temporary file, error %d", err); - return err; -} - -/** - * vfs_dent_type - get VFS directory entry type. - * @type: UBIFS directory entry type - * - * This function converts UBIFS directory entry type into VFS directory en= try - * type. - */ -static unsigned int vfs_dent_type(uint8_t type) -{ - switch (type) { - case UBIFS_ITYPE_REG: - return DT_REG; - case UBIFS_ITYPE_DIR: - return DT_DIR; - case UBIFS_ITYPE_LNK: - return DT_LNK; - case UBIFS_ITYPE_BLK: - return DT_BLK; - case UBIFS_ITYPE_CHR: - return DT_CHR; - case UBIFS_ITYPE_FIFO: - return DT_FIFO; - case UBIFS_ITYPE_SOCK: - return DT_SOCK; - default: - BUG(); - } - return 0; -} - -/* - * The classical Unix view for directory is that it is a linear array of - * (name, inode number) entries. Linux/VFS assumes this model as well. - * Particularly, 'readdir()' call wants us to return a directory entry off= set - * which later may be used to continue 'readdir()'ing the directory or to - * 'seek()' to that specific direntry. Obviously UBIFS does not really fit= this - * model because directory entries are identified by keys, which may colli= de. - * - * UBIFS uses directory entry hash value for directory offsets, so - * 'seekdir()'/'telldir()' may not always work because of possible key - * collisions. But UBIFS guarantees that consecutive 'readdir()' calls work - * properly by means of saving full directory entry name in the private fi= eld - * of the file description object. - * - * This means that UBIFS cannot support NFS which requires full - * 'seekdir()'/'telldir()' support. - */ -static int ubifs_readdir(struct file *file, struct dir_context *ctx) -{ - int fstr_real_len =3D 0, err =3D 0; - struct fscrypt_name nm; - struct fscrypt_str fstr =3D {0}; - union ubifs_key key; - struct ubifs_dent_node *dent; - struct inode *dir =3D file_inode(file); - struct ubifs_info *c =3D dir->i_sb->s_fs_info; - bool encrypted =3D IS_ENCRYPTED(dir); - - dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, ctx->pos); - - if (ctx->pos > UBIFS_S_KEY_HASH_MASK || ctx->pos =3D=3D 2) - /* - * The directory was seek'ed to a senseless position or there - * are no more entries. - */ - return 0; - - if (encrypted) { - err =3D fscrypt_prepare_readdir(dir); - if (err) - return err; - - err =3D fscrypt_fname_alloc_buffer(UBIFS_MAX_NLEN, &fstr); - if (err) - return err; - - fstr_real_len =3D fstr.len; - } - - if (file->f_version =3D=3D 0) { - /* - * The file was seek'ed, which means that @file->private_data - * is now invalid. This may also be just the first - * 'ubifs_readdir()' invocation, in which case - * @file->private_data is NULL, and the below code is - * basically a no-op. - */ - kfree(file->private_data); - file->private_data =3D NULL; - } - - /* - * 'generic_file_llseek()' unconditionally sets @file->f_version to - * zero, and we use this for detecting whether the file was seek'ed. - */ - file->f_version =3D 1; - - /* File positions 0 and 1 correspond to "." and ".." */ - if (ctx->pos < 2) { - ubifs_assert(c, !file->private_data); - if (!dir_emit_dots(file, ctx)) { - if (encrypted) - fscrypt_fname_free_buffer(&fstr); - return 0; - } - - /* Find the first entry in TNC and save it */ - lowest_dent_key(c, &key, dir->i_ino); - fname_len(&nm) =3D 0; - dent =3D ubifs_tnc_next_ent(c, &key, &nm); - if (IS_ERR(dent)) { - err =3D PTR_ERR(dent); - goto out; - } - - ctx->pos =3D key_hash_flash(c, &dent->key); - file->private_data =3D dent; - } - - dent =3D file->private_data; - if (!dent) { - /* - * The directory was seek'ed to and is now readdir'ed. - * Find the entry corresponding to @ctx->pos or the closest one. - */ - dent_key_init_hash(c, &key, dir->i_ino, ctx->pos); - fname_len(&nm) =3D 0; - dent =3D ubifs_tnc_next_ent(c, &key, &nm); - if (IS_ERR(dent)) { - err =3D PTR_ERR(dent); - goto out; - } - ctx->pos =3D key_hash_flash(c, &dent->key); - file->private_data =3D dent; - } - - while (1) { - dbg_gen("ino %llu, new f_pos %#x", - (unsigned long long)le64_to_cpu(dent->inum), - key_hash_flash(c, &dent->key)); - ubifs_assert(c, le64_to_cpu(dent->ch.sqnum) > - ubifs_inode(dir)->creat_sqnum); - - fname_len(&nm) =3D le16_to_cpu(dent->nlen); - fname_name(&nm) =3D dent->name; - - if (encrypted) { - fstr.len =3D fstr_real_len; - - err =3D fscrypt_fname_disk_to_usr(dir, key_hash_flash(c, - &dent->key), - le32_to_cpu(dent->cookie), - &nm.disk_name, &fstr); - if (err) - goto out; - } else { - fstr.len =3D fname_len(&nm); - fstr.name =3D fname_name(&nm); - } - - if (!dir_emit(ctx, fstr.name, fstr.len, - le64_to_cpu(dent->inum), - vfs_dent_type(dent->type))) { - if (encrypted) - fscrypt_fname_free_buffer(&fstr); - return 0; - } - - /* Switch to the next entry */ - key_read(c, &dent->key, &key); - dent =3D ubifs_tnc_next_ent(c, &key, &nm); - if (IS_ERR(dent)) { - err =3D PTR_ERR(dent); - goto out; - } - - kfree(file->private_data); - ctx->pos =3D key_hash_flash(c, &dent->key); - file->private_data =3D dent; - cond_resched(); - } - -out: - kfree(file->private_data); - file->private_data =3D NULL; - - if (encrypted) - fscrypt_fname_free_buffer(&fstr); - - if (err !=3D -ENOENT) - ubifs_err(c, "cannot find next direntry, error %d", err); - else - /* - * -ENOENT is a non-fatal error in this context, the TNC uses - * it to indicate that the cursor moved past the current directory - * and readdir() has to stop. - */ - err =3D 0; - - - /* 2 is a special value indicating that there are no more direntries */ - ctx->pos =3D 2; - return err; -} - -/* Free saved readdir() state when the directory is closed */ -static int ubifs_dir_release(struct inode *dir, struct file *file) -{ - kfree(file->private_data); - file->private_data =3D NULL; - return 0; -} - -static int ubifs_link(struct dentry *old_dentry, struct inode *dir, - struct dentry *dentry) -{ - struct ubifs_info *c =3D dir->i_sb->s_fs_info; - struct inode *inode =3D d_inode(old_dentry); - struct ubifs_inode *ui =3D ubifs_inode(inode); - struct ubifs_inode *dir_ui =3D ubifs_inode(dir); - int err, sz_change; - struct ubifs_budget_req req =3D { .new_dent =3D 1, .dirtied_ino =3D 2, - .dirtied_ino_d =3D ALIGN(ui->data_len, 8) }; - struct fscrypt_name nm; - - /* - * Budget request settings: new direntry, changing the target inode, - * changing the parent inode. - */ - - dbg_gen("dent '%pd' to ino %lu (nlink %d) in dir ino %lu", - dentry, inode->i_ino, - inode->i_nlink, dir->i_ino); - ubifs_assert(c, inode_is_locked(dir)); - ubifs_assert(c, inode_is_locked(inode)); - - err =3D fscrypt_prepare_link(old_dentry, dir, dentry); - if (err) - return err; - - err =3D fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm); - if (err) - return err; - - sz_change =3D CALC_DENT_SIZE(fname_len(&nm)); - - err =3D dbg_check_synced_i_size(c, inode); - if (err) - goto out_fname; - - err =3D ubifs_budget_space(c, &req); - if (err) - goto out_fname; - - lock_2_inodes(dir, inode); - - inc_nlink(inode); - ihold(inode); - inode_set_ctime_current(inode); - dir->i_size +=3D sz_change; - dir_ui->ui_size =3D dir->i_size; - inode_set_mtime_to_ts(dir, - inode_set_ctime_to_ts(dir, inode_get_ctime(inode))); - err =3D ubifs_jnl_update(c, dir, &nm, inode, 0, 0, inode->i_nlink =3D=3D = 1); - if (err) - goto out_cancel; - unlock_2_inodes(dir, inode); - - ubifs_release_budget(c, &req); - d_instantiate(dentry, inode); - fscrypt_free_filename(&nm); - return 0; - -out_cancel: - dir->i_size -=3D sz_change; - dir_ui->ui_size =3D dir->i_size; - drop_nlink(inode); - unlock_2_inodes(dir, inode); - ubifs_release_budget(c, &req); - iput(inode); -out_fname: - fscrypt_free_filename(&nm); - return err; -} - -static int ubifs_unlink(struct inode *dir, struct dentry *dentry) -{ - struct ubifs_info *c =3D dir->i_sb->s_fs_info; - struct inode *inode =3D d_inode(dentry); - struct ubifs_inode *dir_ui =3D ubifs_inode(dir); - int err, sz_change, budgeted =3D 1; - struct ubifs_budget_req req =3D { .mod_dent =3D 1, .dirtied_ino =3D 2 }; - unsigned int saved_nlink =3D inode->i_nlink; - struct fscrypt_name nm; - - /* - * Budget request settings: deletion direntry, deletion inode (+1 for - * @dirtied_ino), changing the parent directory inode. If budgeting - * fails, go ahead anyway because we have extra space reserved for - * deletions. - */ - - dbg_gen("dent '%pd' from ino %lu (nlink %d) in dir ino %lu", - dentry, inode->i_ino, - inode->i_nlink, dir->i_ino); - - err =3D fscrypt_setup_filename(dir, &dentry->d_name, 1, &nm); - if (err) - return err; - - err =3D ubifs_purge_xattrs(inode); - if (err) - return err; - - sz_change =3D CALC_DENT_SIZE(fname_len(&nm)); - - ubifs_assert(c, inode_is_locked(dir)); - ubifs_assert(c, inode_is_locked(inode)); - err =3D dbg_check_synced_i_size(c, inode); - if (err) - goto out_fname; - - err =3D ubifs_budget_space(c, &req); - if (err) { - if (err !=3D -ENOSPC) - goto out_fname; - budgeted =3D 0; - } - - lock_2_inodes(dir, inode); - inode_set_ctime_current(inode); - drop_nlink(inode); - dir->i_size -=3D sz_change; - dir_ui->ui_size =3D dir->i_size; - inode_set_mtime_to_ts(dir, - inode_set_ctime_to_ts(dir, inode_get_ctime(inode))); - err =3D ubifs_jnl_update(c, dir, &nm, inode, 1, 0, 0); - if (err) - goto out_cancel; - unlock_2_inodes(dir, inode); - - if (budgeted) - ubifs_release_budget(c, &req); - else { - /* We've deleted something - clean the "no space" flags */ - c->bi.nospace =3D c->bi.nospace_rp =3D 0; - smp_wmb(); - } - fscrypt_free_filename(&nm); - return 0; - -out_cancel: - dir->i_size +=3D sz_change; - dir_ui->ui_size =3D dir->i_size; - set_nlink(inode, saved_nlink); - unlock_2_inodes(dir, inode); - if (budgeted) - ubifs_release_budget(c, &req); -out_fname: - fscrypt_free_filename(&nm); - return err; -} - -/** - * ubifs_check_dir_empty - check if a directory is empty or not. - * @dir: VFS inode object of the directory to check - * - * This function checks if directory @dir is empty. Returns zero if the - * directory is empty, %-ENOTEMPTY if it is not, and other negative error = codes - * in case of errors. - */ -int ubifs_check_dir_empty(struct inode *dir) -{ - struct ubifs_info *c =3D dir->i_sb->s_fs_info; - struct fscrypt_name nm =3D { 0 }; - struct ubifs_dent_node *dent; - union ubifs_key key; - int err; - - lowest_dent_key(c, &key, dir->i_ino); - dent =3D ubifs_tnc_next_ent(c, &key, &nm); - if (IS_ERR(dent)) { - err =3D PTR_ERR(dent); - if (err =3D=3D -ENOENT) - err =3D 0; - } else { - kfree(dent); - err =3D -ENOTEMPTY; - } - return err; -} - -static int ubifs_rmdir(struct inode *dir, struct dentry *dentry) -{ - struct ubifs_info *c =3D dir->i_sb->s_fs_info; - struct inode *inode =3D d_inode(dentry); - int err, sz_change, budgeted =3D 1; - struct ubifs_inode *dir_ui =3D ubifs_inode(dir); - struct ubifs_budget_req req =3D { .mod_dent =3D 1, .dirtied_ino =3D 2 }; - struct fscrypt_name nm; - - /* - * Budget request settings: deletion direntry, deletion inode and - * changing the parent inode. If budgeting fails, go ahead anyway - * because we have extra space reserved for deletions. - */ - - dbg_gen("directory '%pd', ino %lu in dir ino %lu", dentry, - inode->i_ino, dir->i_ino); - ubifs_assert(c, inode_is_locked(dir)); - ubifs_assert(c, inode_is_locked(inode)); - err =3D ubifs_check_dir_empty(d_inode(dentry)); - if (err) - return err; - - err =3D fscrypt_setup_filename(dir, &dentry->d_name, 1, &nm); - if (err) - return err; - - err =3D ubifs_purge_xattrs(inode); - if (err) - return err; - - sz_change =3D CALC_DENT_SIZE(fname_len(&nm)); - - err =3D ubifs_budget_space(c, &req); - if (err) { - if (err !=3D -ENOSPC) - goto out_fname; - budgeted =3D 0; - } - - lock_2_inodes(dir, inode); - inode_set_ctime_current(inode); - clear_nlink(inode); - drop_nlink(dir); - dir->i_size -=3D sz_change; - dir_ui->ui_size =3D dir->i_size; - inode_set_mtime_to_ts(dir, - inode_set_ctime_to_ts(dir, inode_get_ctime(inode))); - err =3D ubifs_jnl_update(c, dir, &nm, inode, 1, 0, 0); - if (err) - goto out_cancel; - unlock_2_inodes(dir, inode); - - if (budgeted) - ubifs_release_budget(c, &req); - else { - /* We've deleted something - clean the "no space" flags */ - c->bi.nospace =3D c->bi.nospace_rp =3D 0; - smp_wmb(); - } - fscrypt_free_filename(&nm); - return 0; - -out_cancel: - dir->i_size +=3D sz_change; - dir_ui->ui_size =3D dir->i_size; - inc_nlink(dir); - set_nlink(inode, 2); - unlock_2_inodes(dir, inode); - if (budgeted) - ubifs_release_budget(c, &req); -out_fname: - fscrypt_free_filename(&nm); - return err; -} - -static int ubifs_mkdir(struct mnt_idmap *idmap, struct inode *dir, - struct dentry *dentry, umode_t mode) -{ - struct inode *inode; - struct ubifs_inode *dir_ui =3D ubifs_inode(dir); - struct ubifs_info *c =3D dir->i_sb->s_fs_info; - int err, sz_change; - struct ubifs_budget_req req =3D { .new_ino =3D 1, .new_dent =3D 1, - .dirtied_ino =3D 1}; - struct fscrypt_name nm; - - /* - * Budget request settings: new inode, new direntry and changing parent - * directory inode. - */ - - dbg_gen("dent '%pd', mode %#hx in dir ino %lu", - dentry, mode, dir->i_ino); - - err =3D ubifs_budget_space(c, &req); - if (err) - return err; - - err =3D ubifs_prepare_create(dir, dentry, &nm); - if (err) - goto out_budg; - - sz_change =3D CALC_DENT_SIZE(fname_len(&nm)); - - inode =3D ubifs_new_inode(c, dir, S_IFDIR | mode, false); - if (IS_ERR(inode)) { - err =3D PTR_ERR(inode); - goto out_fname; - } - - err =3D ubifs_init_security(dir, inode, &dentry->d_name); - if (err) - goto out_inode; - - set_nlink(inode, 1); - mutex_lock(&dir_ui->ui_mutex); - insert_inode_hash(inode); - inc_nlink(inode); - inc_nlink(dir); - dir->i_size +=3D sz_change; - dir_ui->ui_size =3D dir->i_size; - inode_set_mtime_to_ts(dir, - inode_set_ctime_to_ts(dir, inode_get_ctime(inode))); - err =3D ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 1); - if (err) { - ubifs_err(c, "cannot create directory, error %d", err); - goto out_cancel; - } - mutex_unlock(&dir_ui->ui_mutex); - - ubifs_release_budget(c, &req); - d_instantiate(dentry, inode); - fscrypt_free_filename(&nm); - return 0; - -out_cancel: - dir->i_size -=3D sz_change; - dir_ui->ui_size =3D dir->i_size; - drop_nlink(dir); - mutex_unlock(&dir_ui->ui_mutex); - set_nlink(inode, 0); -out_inode: - iput(inode); -out_fname: - fscrypt_free_filename(&nm); -out_budg: - ubifs_release_budget(c, &req); - return err; -} - -static int ubifs_mknod(struct mnt_idmap *idmap, struct inode *dir, - struct dentry *dentry, umode_t mode, dev_t rdev) -{ - struct inode *inode; - struct ubifs_inode *ui; - struct ubifs_inode *dir_ui =3D ubifs_inode(dir); - struct ubifs_info *c =3D dir->i_sb->s_fs_info; - union ubifs_dev_desc *dev =3D NULL; - int sz_change; - int err, devlen =3D 0; - struct ubifs_budget_req req =3D { .new_ino =3D 1, .new_dent =3D 1, - .dirtied_ino =3D 1 }; - struct fscrypt_name nm; - - /* - * Budget request settings: new inode, new direntry and changing parent - * directory inode. - */ - - dbg_gen("dent '%pd' in dir ino %lu", dentry, dir->i_ino); - - if (S_ISBLK(mode) || S_ISCHR(mode)) { - dev =3D kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS); - if (!dev) - return -ENOMEM; - devlen =3D ubifs_encode_dev(dev, rdev); - } - - req.new_ino_d =3D ALIGN(devlen, 8); - err =3D ubifs_budget_space(c, &req); - if (err) { - kfree(dev); - return err; - } - - err =3D ubifs_prepare_create(dir, dentry, &nm); - if (err) { - kfree(dev); - goto out_budg; - } - - sz_change =3D CALC_DENT_SIZE(fname_len(&nm)); - - inode =3D ubifs_new_inode(c, dir, mode, false); - if (IS_ERR(inode)) { - kfree(dev); - err =3D PTR_ERR(inode); - goto out_fname; - } - - err =3D ubifs_init_security(dir, inode, &dentry->d_name); - if (err) { - kfree(dev); - goto out_inode; - } - - init_special_inode(inode, inode->i_mode, rdev); - inode->i_size =3D ubifs_inode(inode)->ui_size =3D devlen; - ui =3D ubifs_inode(inode); - ui->data =3D dev; - ui->data_len =3D devlen; - set_nlink(inode, 1); - - mutex_lock(&dir_ui->ui_mutex); - dir->i_size +=3D sz_change; - dir_ui->ui_size =3D dir->i_size; - inode_set_mtime_to_ts(dir, - inode_set_ctime_to_ts(dir, inode_get_ctime(inode))); - err =3D ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 1); - if (err) - goto out_cancel; - mutex_unlock(&dir_ui->ui_mutex); - - ubifs_release_budget(c, &req); - insert_inode_hash(inode); - d_instantiate(dentry, inode); - fscrypt_free_filename(&nm); - return 0; - -out_cancel: - dir->i_size -=3D sz_change; - dir_ui->ui_size =3D dir->i_size; - mutex_unlock(&dir_ui->ui_mutex); - set_nlink(inode, 0); -out_inode: - iput(inode); -out_fname: - fscrypt_free_filename(&nm); -out_budg: - ubifs_release_budget(c, &req); - return err; -} - -static int ubifs_symlink(struct mnt_idmap *idmap, struct inode *dir, - struct dentry *dentry, const char *symname) -{ - struct inode *inode; - struct ubifs_inode *ui; - struct ubifs_inode *dir_ui =3D ubifs_inode(dir); - struct ubifs_info *c =3D dir->i_sb->s_fs_info; - int err, sz_change, len =3D strlen(symname); - struct fscrypt_str disk_link; - struct ubifs_budget_req req =3D { .new_ino =3D 1, .new_dent =3D 1, - .dirtied_ino =3D 1 }; - struct fscrypt_name nm; - - dbg_gen("dent '%pd', target '%s' in dir ino %lu", dentry, - symname, dir->i_ino); - - err =3D fscrypt_prepare_symlink(dir, symname, len, UBIFS_MAX_INO_DATA, - &disk_link); - if (err) - return err; - - /* - * Budget request settings: new inode, new direntry and changing parent - * directory inode. - */ - req.new_ino_d =3D ALIGN(disk_link.len - 1, 8); - err =3D ubifs_budget_space(c, &req); - if (err) - return err; - - err =3D ubifs_prepare_create(dir, dentry, &nm); - if (err) - goto out_budg; - - sz_change =3D CALC_DENT_SIZE(fname_len(&nm)); - - inode =3D ubifs_new_inode(c, dir, S_IFLNK | S_IRWXUGO, false); - if (IS_ERR(inode)) { - err =3D PTR_ERR(inode); - goto out_fname; - } - - err =3D ubifs_init_security(dir, inode, &dentry->d_name); - if (err) - goto out_inode; - - ui =3D ubifs_inode(inode); - ui->data =3D kmalloc(disk_link.len, GFP_NOFS); - if (!ui->data) { - err =3D -ENOMEM; - goto out_inode; - } - - if (IS_ENCRYPTED(inode)) { - disk_link.name =3D ui->data; /* encrypt directly into ui->data */ - err =3D fscrypt_encrypt_symlink(inode, symname, len, &disk_link); - if (err) - goto out_inode; - } else { - memcpy(ui->data, disk_link.name, disk_link.len); - inode->i_link =3D ui->data; - } - - /* - * The terminating zero byte is not written to the flash media and it - * is put just to make later in-memory string processing simpler. Thus, - * data length is @disk_link.len - 1, not @disk_link.len. - */ - ui->data_len =3D disk_link.len - 1; - inode->i_size =3D ubifs_inode(inode)->ui_size =3D disk_link.len - 1; - set_nlink(inode, 1); - - mutex_lock(&dir_ui->ui_mutex); - dir->i_size +=3D sz_change; - dir_ui->ui_size =3D dir->i_size; - inode_set_mtime_to_ts(dir, - inode_set_ctime_to_ts(dir, inode_get_ctime(inode))); - err =3D ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 1); - if (err) - goto out_cancel; - mutex_unlock(&dir_ui->ui_mutex); - - insert_inode_hash(inode); - d_instantiate(dentry, inode); - err =3D 0; - goto out_fname; - -out_cancel: - dir->i_size -=3D sz_change; - dir_ui->ui_size =3D dir->i_size; - mutex_unlock(&dir_ui->ui_mutex); - set_nlink(inode, 0); -out_inode: - /* Free inode->i_link before inode is marked as bad. */ - fscrypt_free_inode(inode); - iput(inode); -out_fname: - fscrypt_free_filename(&nm); -out_budg: - ubifs_release_budget(c, &req); - return err; -} - -/** - * lock_4_inodes - a wrapper for locking three UBIFS inodes. - * @inode1: first inode - * @inode2: second inode - * @inode3: third inode - * @inode4: fourth inode - * - * This function is used for 'ubifs_rename()' and @inode1 may be the same = as - * @inode2 whereas @inode3 and @inode4 may be %NULL. - * - * We do not implement any tricks to guarantee strict lock ordering, becau= se - * VFS has already done it for us on the @i_mutex. So this is just a simple - * wrapper function. - */ -static void lock_4_inodes(struct inode *inode1, struct inode *inode2, - struct inode *inode3, struct inode *inode4) -{ - mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1); - if (inode2 !=3D inode1) - mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2); - if (inode3) - mutex_lock_nested(&ubifs_inode(inode3)->ui_mutex, WB_MUTEX_3); - if (inode4) - mutex_lock_nested(&ubifs_inode(inode4)->ui_mutex, WB_MUTEX_4); -} - -/** - * unlock_4_inodes - a wrapper for unlocking three UBIFS inodes for rename. - * @inode1: first inode - * @inode2: second inode - * @inode3: third inode - * @inode4: fourth inode - */ -static void unlock_4_inodes(struct inode *inode1, struct inode *inode2, - struct inode *inode3, struct inode *inode4) -{ - if (inode4) - mutex_unlock(&ubifs_inode(inode4)->ui_mutex); - if (inode3) - mutex_unlock(&ubifs_inode(inode3)->ui_mutex); - if (inode1 !=3D inode2) - mutex_unlock(&ubifs_inode(inode2)->ui_mutex); - mutex_unlock(&ubifs_inode(inode1)->ui_mutex); -} - -static int do_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry, - unsigned int flags) -{ - struct ubifs_info *c =3D old_dir->i_sb->s_fs_info; - struct inode *old_inode =3D d_inode(old_dentry); - struct inode *new_inode =3D d_inode(new_dentry); - struct inode *whiteout =3D NULL; - struct ubifs_inode *old_inode_ui =3D ubifs_inode(old_inode); - struct ubifs_inode *whiteout_ui =3D NULL; - int err, release, sync =3D 0, move =3D (new_dir !=3D old_dir); - int is_dir =3D S_ISDIR(old_inode->i_mode); - int unlink =3D !!new_inode, new_sz, old_sz; - struct ubifs_budget_req req =3D { .new_dent =3D 1, .mod_dent =3D 1, - .dirtied_ino =3D 3 }; - struct ubifs_budget_req ino_req =3D { .dirtied_ino =3D 1, - .dirtied_ino_d =3D ALIGN(old_inode_ui->data_len, 8) }; - struct ubifs_budget_req wht_req; - unsigned int saved_nlink; - struct fscrypt_name old_nm, new_nm; - - /* - * Budget request settings: - * req: deletion direntry, new direntry, removing the old inode, - * and changing old and new parent directory inodes. - * - * wht_req: new whiteout inode for RENAME_WHITEOUT. - * - * ino_req: marks the target inode as dirty and does not write it. - */ - - dbg_gen("dent '%pd' ino %lu in dir ino %lu to dent '%pd' in dir ino %lu f= lags 0x%x", - old_dentry, old_inode->i_ino, old_dir->i_ino, - new_dentry, new_dir->i_ino, flags); - - if (unlink) { - ubifs_assert(c, inode_is_locked(new_inode)); - - /* Budget for old inode's data when its nlink > 1. */ - req.dirtied_ino_d =3D ALIGN(ubifs_inode(new_inode)->data_len, 8); - err =3D ubifs_purge_xattrs(new_inode); - if (err) - return err; - } - - if (unlink && is_dir) { - err =3D ubifs_check_dir_empty(new_inode); - if (err) - return err; - } - - err =3D fscrypt_setup_filename(old_dir, &old_dentry->d_name, 0, &old_nm); - if (err) - return err; - - err =3D fscrypt_setup_filename(new_dir, &new_dentry->d_name, 0, &new_nm); - if (err) { - fscrypt_free_filename(&old_nm); - return err; - } - - new_sz =3D CALC_DENT_SIZE(fname_len(&new_nm)); - old_sz =3D CALC_DENT_SIZE(fname_len(&old_nm)); - - err =3D ubifs_budget_space(c, &req); - if (err) { - fscrypt_free_filename(&old_nm); - fscrypt_free_filename(&new_nm); - return err; - } - err =3D ubifs_budget_space(c, &ino_req); - if (err) { - fscrypt_free_filename(&old_nm); - fscrypt_free_filename(&new_nm); - ubifs_release_budget(c, &req); - return err; - } - - if (flags & RENAME_WHITEOUT) { - union ubifs_dev_desc *dev =3D NULL; - - dev =3D kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS); - if (!dev) { - err =3D -ENOMEM; - goto out_release; - } - - /* - * The whiteout inode without dentry is pinned in memory, - * umount won't happen during rename process because we - * got parent dentry. - */ - whiteout =3D create_whiteout(old_dir, old_dentry); - if (IS_ERR(whiteout)) { - err =3D PTR_ERR(whiteout); - kfree(dev); - goto out_release; - } - - whiteout_ui =3D ubifs_inode(whiteout); - whiteout_ui->data =3D dev; - whiteout_ui->data_len =3D ubifs_encode_dev(dev, MKDEV(0, 0)); - ubifs_assert(c, !whiteout_ui->dirty); - - memset(&wht_req, 0, sizeof(struct ubifs_budget_req)); - wht_req.new_ino =3D 1; - wht_req.new_ino_d =3D ALIGN(whiteout_ui->data_len, 8); - /* - * To avoid deadlock between space budget (holds ui_mutex and - * waits wb work) and writeback work(waits ui_mutex), do space - * budget before ubifs inodes locked. - */ - err =3D ubifs_budget_space(c, &wht_req); - if (err) { - iput(whiteout); - goto out_release; - } - set_nlink(whiteout, 1); - - /* Add the old_dentry size to the old_dir size. */ - old_sz -=3D CALC_DENT_SIZE(fname_len(&old_nm)); - } - - lock_4_inodes(old_dir, new_dir, new_inode, whiteout); - - /* - * Like most other Unix systems, set the @i_ctime for inodes on a - * rename. - */ - simple_rename_timestamp(old_dir, old_dentry, new_dir, new_dentry); - - /* We must adjust parent link count when renaming directories */ - if (is_dir) { - if (move) { - /* - * @old_dir loses a link because we are moving - * @old_inode to a different directory. - */ - drop_nlink(old_dir); - /* - * @new_dir only gains a link if we are not also - * overwriting an existing directory. - */ - if (!unlink) - inc_nlink(new_dir); - } else { - /* - * @old_inode is not moving to a different directory, - * but @old_dir still loses a link if we are - * overwriting an existing directory. - */ - if (unlink) - drop_nlink(old_dir); - } - } - - old_dir->i_size -=3D old_sz; - ubifs_inode(old_dir)->ui_size =3D old_dir->i_size; - - /* - * And finally, if we unlinked a direntry which happened to have the - * same name as the moved direntry, we have to decrement @i_nlink of - * the unlinked inode. - */ - if (unlink) { - /* - * Directories cannot have hard-links, so if this is a - * directory, just clear @i_nlink. - */ - saved_nlink =3D new_inode->i_nlink; - if (is_dir) - clear_nlink(new_inode); - else - drop_nlink(new_inode); - } else { - new_dir->i_size +=3D new_sz; - ubifs_inode(new_dir)->ui_size =3D new_dir->i_size; - } - - /* - * Do not ask 'ubifs_jnl_rename()' to flush write-buffer if @old_inode - * is dirty, because this will be done later on at the end of - * 'ubifs_rename()'. - */ - if (IS_SYNC(old_inode)) { - sync =3D IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir); - if (unlink && IS_SYNC(new_inode)) - sync =3D 1; - /* - * S_SYNC flag of whiteout inherits from the old_dir, and we - * have already checked the old dir inode. So there is no need - * to check whiteout. - */ - } - - err =3D ubifs_jnl_rename(c, old_dir, old_inode, &old_nm, new_dir, - new_inode, &new_nm, whiteout, sync, !!whiteout); - if (err) - goto out_cancel; - - unlock_4_inodes(old_dir, new_dir, new_inode, whiteout); - ubifs_release_budget(c, &req); - - if (whiteout) { - ubifs_release_budget(c, &wht_req); - iput(whiteout); - } - - mutex_lock(&old_inode_ui->ui_mutex); - release =3D old_inode_ui->dirty; - mark_inode_dirty_sync(old_inode); - mutex_unlock(&old_inode_ui->ui_mutex); - - if (release) - ubifs_release_budget(c, &ino_req); - if (IS_SYNC(old_inode)) - /* - * Rename finished here. Although old inode cannot be updated - * on flash, old ctime is not a big problem, don't return err - * code to userspace. - */ - old_inode->i_sb->s_op->write_inode(old_inode, NULL); - - fscrypt_free_filename(&old_nm); - fscrypt_free_filename(&new_nm); - return 0; - -out_cancel: - if (unlink) { - set_nlink(new_inode, saved_nlink); - } else { - new_dir->i_size -=3D new_sz; - ubifs_inode(new_dir)->ui_size =3D new_dir->i_size; - } - old_dir->i_size +=3D old_sz; - ubifs_inode(old_dir)->ui_size =3D old_dir->i_size; - if (is_dir) { - if (move) { - inc_nlink(old_dir); - if (!unlink) - drop_nlink(new_dir); - } else { - if (unlink) - inc_nlink(old_dir); - } - } - unlock_4_inodes(old_dir, new_dir, new_inode, whiteout); - if (whiteout) { - ubifs_release_budget(c, &wht_req); - set_nlink(whiteout, 0); - iput(whiteout); - } -out_release: - ubifs_release_budget(c, &ino_req); - ubifs_release_budget(c, &req); - fscrypt_free_filename(&old_nm); - fscrypt_free_filename(&new_nm); - return err; -} - -static int ubifs_xrename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - struct ubifs_info *c =3D old_dir->i_sb->s_fs_info; - struct ubifs_budget_req req =3D { .new_dent =3D 1, .mod_dent =3D 1, - .dirtied_ino =3D 2 }; - int sync =3D IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir); - struct inode *fst_inode =3D d_inode(old_dentry); - struct inode *snd_inode =3D d_inode(new_dentry); - int err; - struct fscrypt_name fst_nm, snd_nm; - - ubifs_assert(c, fst_inode && snd_inode); - - /* - * Budget request settings: changing two direntries, changing the two - * parent directory inodes. - */ - - dbg_gen("dent '%pd' ino %lu in dir ino %lu exchange dent '%pd' ino %lu in= dir ino %lu", - old_dentry, fst_inode->i_ino, old_dir->i_ino, - new_dentry, snd_inode->i_ino, new_dir->i_ino); - - err =3D fscrypt_setup_filename(old_dir, &old_dentry->d_name, 0, &fst_nm); - if (err) - return err; - - err =3D fscrypt_setup_filename(new_dir, &new_dentry->d_name, 0, &snd_nm); - if (err) { - fscrypt_free_filename(&fst_nm); - return err; - } - - err =3D ubifs_budget_space(c, &req); - if (err) - goto out; - - lock_4_inodes(old_dir, new_dir, NULL, NULL); - - simple_rename_timestamp(old_dir, old_dentry, new_dir, new_dentry); - - if (old_dir !=3D new_dir) { - if (S_ISDIR(fst_inode->i_mode) && !S_ISDIR(snd_inode->i_mode)) { - inc_nlink(new_dir); - drop_nlink(old_dir); - } - else if (!S_ISDIR(fst_inode->i_mode) && S_ISDIR(snd_inode->i_mode)) { - drop_nlink(new_dir); - inc_nlink(old_dir); - } - } - - err =3D ubifs_jnl_xrename(c, old_dir, fst_inode, &fst_nm, new_dir, - snd_inode, &snd_nm, sync); - - unlock_4_inodes(old_dir, new_dir, NULL, NULL); - ubifs_release_budget(c, &req); - -out: - fscrypt_free_filename(&fst_nm); - fscrypt_free_filename(&snd_nm); - return err; -} - -static int ubifs_rename(struct mnt_idmap *idmap, - struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry, - unsigned int flags) -{ - int err; - struct ubifs_info *c =3D old_dir->i_sb->s_fs_info; - - if (flags & ~(RENAME_NOREPLACE | RENAME_WHITEOUT | RENAME_EXCHANGE)) - return -EINVAL; - - ubifs_assert(c, inode_is_locked(old_dir)); - ubifs_assert(c, inode_is_locked(new_dir)); - - err =3D fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry, - flags); - if (err) - return err; - - if (flags & RENAME_EXCHANGE) - return ubifs_xrename(old_dir, old_dentry, new_dir, new_dentry); - - return do_rename(old_dir, old_dentry, new_dir, new_dentry, flags); -} - -int ubifs_getattr(struct mnt_idmap *idmap, const struct path *path, - struct kstat *stat, u32 request_mask, unsigned int flags) -{ - loff_t size; - struct inode *inode =3D d_inode(path->dentry); - struct ubifs_inode *ui =3D ubifs_inode(inode); - - mutex_lock(&ui->ui_mutex); - - if (ui->flags & UBIFS_APPEND_FL) - stat->attributes |=3D STATX_ATTR_APPEND; - if (ui->flags & UBIFS_COMPR_FL) - stat->attributes |=3D STATX_ATTR_COMPRESSED; - if (ui->flags & UBIFS_CRYPT_FL) - stat->attributes |=3D STATX_ATTR_ENCRYPTED; - if (ui->flags & UBIFS_IMMUTABLE_FL) - stat->attributes |=3D STATX_ATTR_IMMUTABLE; - - stat->attributes_mask |=3D (STATX_ATTR_APPEND | - STATX_ATTR_COMPRESSED | - STATX_ATTR_ENCRYPTED | - STATX_ATTR_IMMUTABLE); - - generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat); - stat->blksize =3D UBIFS_BLOCK_SIZE; - stat->size =3D ui->ui_size; - - /* - * Unfortunately, the 'stat()' system call was designed for block - * device based file systems, and it is not appropriate for UBIFS, - * because UBIFS does not have notion of "block". For example, it is - * difficult to tell how many block a directory takes - it actually - * takes less than 300 bytes, but we have to round it to block size, - * which introduces large mistake. This makes utilities like 'du' to - * report completely senseless numbers. This is the reason why UBIFS - * goes the same way as JFFS2 - it reports zero blocks for everything - * but regular files, which makes more sense than reporting completely - * wrong sizes. - */ - if (S_ISREG(inode->i_mode)) { - size =3D ui->xattr_size; - size +=3D stat->size; - size =3D ALIGN(size, UBIFS_BLOCK_SIZE); - /* - * Note, user-space expects 512-byte blocks count irrespectively - * of what was reported in @stat->size. - */ - stat->blocks =3D size >> 9; - } else - stat->blocks =3D 0; - mutex_unlock(&ui->ui_mutex); - return 0; -} - -const struct inode_operations ubifs_dir_inode_operations =3D { - .lookup =3D ubifs_lookup, - .create =3D ubifs_create, - .link =3D ubifs_link, - .symlink =3D ubifs_symlink, - .unlink =3D ubifs_unlink, - .mkdir =3D ubifs_mkdir, - .rmdir =3D ubifs_rmdir, - .mknod =3D ubifs_mknod, - .rename =3D ubifs_rename, - .setattr =3D ubifs_setattr, - .getattr =3D ubifs_getattr, - .listxattr =3D ubifs_listxattr, - .update_time =3D ubifs_update_time, - .tmpfile =3D ubifs_tmpfile, - .fileattr_get =3D ubifs_fileattr_get, - .fileattr_set =3D ubifs_fileattr_set, -}; - -const struct file_operations ubifs_dir_operations =3D { - .llseek =3D generic_file_llseek, - .release =3D ubifs_dir_release, - .read =3D generic_read_dir, - .iterate_shared =3D ubifs_readdir, - .fsync =3D ubifs_fsync, - .unlocked_ioctl =3D ubifs_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl =3D ubifs_compat_ioctl, -#endif -}; --=20 2.13.6