diff options
author | Zhihao Cheng <chengzhihao1@huawei.com> | 2024-11-11 17:08:12 +0800 |
---|---|---|
committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2024-11-11 10:32:46 +0100 |
commit | df5d5489aed7ae9de007776e19350bd5aebbfea2 (patch) | |
tree | e22d46cedff685cc8fa66bb695d14c5571bdfe45 /ubifs-utils/libubifs/dir.c | |
parent | 7e6ae3a8dcbedf0e65c091e985aff56f293cf8ec (diff) |
ubifs-utils: libubifs: Support some file operations
Add some file operations, such as ubifs_lookup, ubifs_mkdir, etc., this
is a preparation for recovering disconnected files or root dir in fsck.
File writing operations are based on the journal subsystem, generated
dirty data depends on a new commit in subsequent steps to update disk
content.
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'ubifs-utils/libubifs/dir.c')
-rw-r--r-- | ubifs-utils/libubifs/dir.c | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/ubifs-utils/libubifs/dir.c b/ubifs-utils/libubifs/dir.c index 9d5f644..89f77eb 100644 --- a/ubifs-utils/libubifs/dir.c +++ b/ubifs-utils/libubifs/dir.c @@ -28,4 +28,363 @@ * write it, but just marks it as dirty. */ +#include <sys/stat.h> + +#include "linux_err.h" +#include "bitops.h" +#include "kmem.h" #include "ubifs.h" +#include "defs.h" +#include "debug.h" +#include "key.h" +#include "misc.h" + +/** + * inherit_flags - inherit flags of the parent inode. + * @c: UBIFS file-system description object + * @dir: parent inode + * @mode: new inode mode flags + * + * This is a helper function for 'ubifs_new_inode()' which inherits flag of 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(struct ubifs_info *c, const struct inode *dir, + unsigned int mode) +{ + int flags; + const struct ubifs_inode *ui = ubifs_inode(dir); + + ubifs_assert(c, S_ISDIR(dir->mode)); + + flags = ui->flags & (UBIFS_COMPR_FL | UBIFS_SYNC_FL | UBIFS_DIRSYNC_FL); + if (!S_ISDIR(mode)) + /* The "DIRSYNC" flag only applies to directories */ + flags &= ~UBIFS_DIRSYNC_FL; + return flags; +} + +/** + * ubifs_new_inode - allocate new UBIFS inode object. + * @c: UBIFS file-system description object + * @dir: parent inode + * @mode: inode mode flags + * + * This function finds an unused inode number, allocates new ubifs inode and + * initializes it. Returns new ubifs inode in case of success and an error code + * in case of failure. + */ +static struct ubifs_inode *ubifs_new_inode(struct ubifs_info *c, + const struct inode *dir, + unsigned int mode) +{ + int err; + time_t now = time(NULL); + struct ubifs_inode *ui; + struct inode *inode; + + ui = kzalloc(sizeof(struct ubifs_inode), GFP_KERNEL); + if (!ui) + return ERR_PTR(-ENOMEM); + + inode = &ui->vfs_inode; + inode->atime_sec = inode->ctime_sec = inode->mtime_sec = now; + inode->nlink = 1; + inode->mode = mode; + if (dir) { + /* Create non root dir. */ + inode->uid = dir->uid; + inode->gid = dir->gid; + if ((dir->mode & S_ISGID) && S_ISDIR(mode)) + inode->mode |= S_ISGID; + ui->flags = inherit_flags(c, dir, mode); + } + if (S_ISDIR(mode)) + ui->ui_size = UBIFS_INO_NODE_SZ; + if (S_ISREG(mode)) + ui->compr_type = c->default_compr; + else + ui->compr_type = UBIFS_COMPR_NONE; + + if (dir) { + spin_lock(&c->cnt_lock); + /* Inode number overflow is currently not supported */ + if (c->highest_inum >= INUM_WARN_WATERMARK) { + if (c->highest_inum >= INUM_WATERMARK) { + spin_unlock(&c->cnt_lock); + ubifs_err(c, "out of inode numbers"); + err = -EINVAL; + goto out; + } + ubifs_warn(c, "running out of inode numbers (current %lu, max %u)", + (unsigned long)c->highest_inum, INUM_WATERMARK); + } + inode->inum = ++c->highest_inum; + } else { + /* Create root dir. */ + inode->inum = UBIFS_ROOT_INO; + } + /* + * 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 = ++c->max_sqnum; + spin_unlock(&c->cnt_lock); + + return ui; + +out: + kfree(ui); + return ERR_PTR(err); +} + +/** + * ubifs_lookup_by_inum - look up the UBIFS inode according to inode number. + * @c: UBIFS file-system description object + * @inum: inode number + * + * This function looks up the UBIFS inode according to a given inode number. + * Returns zero in case of success and an error code in case of failure. + */ +struct ubifs_inode *ubifs_lookup_by_inum(struct ubifs_info *c, ino_t inum) +{ + int err; + union ubifs_key key; + struct inode *inode; + struct ubifs_inode *ui; + struct ubifs_ino_node *ino = NULL; + + ino = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS); + if (!ino) + return ERR_PTR(-ENOMEM); + + ui = kzalloc(sizeof(struct ubifs_inode), GFP_KERNEL); + if (!ui) { + err = -ENOMEM; + goto out; + } + + inode = &ui->vfs_inode; + ino_key_init(c, &key, inum); + err = ubifs_tnc_lookup(c, &key, ino); + if (err) { + kfree(ui); + ubifs_assert(c, !get_failure_reason_callback(c)); + goto out; + } + + inode = &ui->vfs_inode; + inode->inum = inum; + inode->uid = le32_to_cpu(ino->uid); + inode->gid = le32_to_cpu(ino->gid); + inode->mode = le32_to_cpu(ino->mode); + inode->nlink = le32_to_cpu(ino->nlink); + inode->atime_sec = le64_to_cpu(ino->atime_sec); + inode->ctime_sec = le64_to_cpu(ino->ctime_sec); + inode->mtime_sec = le64_to_cpu(ino->mtime_sec); + inode->atime_nsec = le32_to_cpu(ino->atime_nsec); + inode->ctime_nsec = le32_to_cpu(ino->ctime_nsec); + inode->mtime_nsec = le32_to_cpu(ino->mtime_nsec); + ui->creat_sqnum = le64_to_cpu(ino->creat_sqnum); + ui->xattr_size = le32_to_cpu(ino->xattr_size); + ui->xattr_cnt = le32_to_cpu(ino->xattr_cnt); + ui->xattr_names = le32_to_cpu(ino->xattr_names); + ui->compr_type = le16_to_cpu(ino->compr_type); + ui->ui_size = le64_to_cpu(ino->size); + ui->flags = le32_to_cpu(ino->flags); + ui->data_len = le32_to_cpu(ino->data_len); + +out: + kfree(ino); + return err ? ERR_PTR(err) : ui; +} + +struct ubifs_inode *ubifs_lookup(struct ubifs_info *c, + struct ubifs_inode *dir_ui, + const struct fscrypt_name *nm) +{ + int err; + ino_t inum; + union ubifs_key key; + struct ubifs_dent_node *dent; + + if (fname_len(nm) > UBIFS_MAX_NLEN) + return ERR_PTR(-ENAMETOOLONG); + + dent = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS); + if (!dent) + return ERR_PTR(-ENOMEM); + + dent_key_init(c, &key, dir_ui->vfs_inode.inum, nm); + err = ubifs_tnc_lookup_nm(c, &key, dent, nm); + if (err) { + kfree(dent); + ubifs_assert(c, !get_failure_reason_callback(c)); + return ERR_PTR(err); + } + inum = le64_to_cpu(dent->inum); + kfree(dent); + + return ubifs_lookup_by_inum(c, inum); +} + +int ubifs_mkdir(struct ubifs_info *c, struct ubifs_inode *dir_ui, + const struct fscrypt_name *nm, unsigned int mode) +{ + struct ubifs_inode *ui; + struct inode *inode, *dir = &dir_ui->vfs_inode; + int err, sz_change; + struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1, + .dirtied_ino = 1}; + /* + * Budget request settings: new inode, new direntry and changing parent + * directory inode. + */ + dbg_gen("dent '%s', mode %#hx in dir ino %lu", + fname_name(nm), mode, dir->inum); + + /* New dir is not allowed to be created under an encrypted directory. */ + ubifs_assert(c, !(dir_ui->flags & UBIFS_CRYPT_FL)); + + err = ubifs_budget_space(c, &req); + if (err) + return err; + + sz_change = CALC_DENT_SIZE(fname_len(nm)); + + ui = ubifs_new_inode(c, dir, S_IFDIR | mode); + if (IS_ERR(ui)) { + err = PTR_ERR(ui); + goto out_budg; + } + + inode = &ui->vfs_inode; + inode->nlink++; + dir->nlink++; + dir_ui->ui_size += sz_change; + dir->ctime_sec = dir->mtime_sec = inode->ctime_sec; + err = ubifs_jnl_update_file(c, dir_ui, nm, ui); + if (err) { + ubifs_err(c, "cannot create directory, error %d", err); + goto out_cancel; + } + + kfree(ui); + ubifs_release_budget(c, &req); + return 0; + +out_cancel: + dir_ui->ui_size -= sz_change; + dir->nlink--; + kfree(ui); +out_budg: + ubifs_release_budget(c, &req); + return err; +} + +/** + * ubifs_link_recovery - link a disconnected file into the target directory. + * @c: UBIFS file-system description object + * @dir_ui: target directory + * @ui: the UBIFS inode of disconnected file + * @nm: directory entry name + * + * This function links the inode of disconnected file to a directory entry name + * under the target directory. Returns zero in case of success and an error code + * in case of failure. + */ +int ubifs_link_recovery(struct ubifs_info *c, struct ubifs_inode *dir_ui, + struct ubifs_inode *ui, const struct fscrypt_name *nm) +{ + struct inode *inode = &ui->vfs_inode, *dir = &dir_ui->vfs_inode; + int err, sz_change; + struct ubifs_budget_req req = { .new_dent = 1, .dirtied_ino = 2, + .dirtied_ino_d = ALIGN(ui->data_len, 8) }; + time_t now = time(NULL); + + /* + * Budget request settings: new direntry, changing the target inode, + * changing the parent inode. + */ + dbg_gen("dent '%s' to ino %lu (nlink %d) in dir ino %lu", + fname_name(nm), inode->inum, inode->nlink, dir->inum); + + /* New dir is not allowed to be created under an encrypted directory. */ + ubifs_assert(c, !(dir_ui->flags & UBIFS_CRYPT_FL)); + + sz_change = CALC_DENT_SIZE(fname_len(nm)); + + err = ubifs_budget_space(c, &req); + if (err) + return err; + + inode->ctime_sec = now; + dir_ui->ui_size += sz_change; + dir->ctime_sec = dir->mtime_sec = now; + err = ubifs_jnl_update_file(c, dir_ui, nm, ui); + if (err) + goto out_cancel; + + ubifs_release_budget(c, &req); + return 0; + +out_cancel: + dir_ui->ui_size -= sz_change; + ubifs_release_budget(c, &req); + return err; +} + +/** + * ubifs_create_root - create the root inode. + * @c: UBIFS file-system description object + * + * This function creates a new inode for the root directory. Returns zero in + * case of success and an error code in case of failure. + */ +int ubifs_create_root(struct ubifs_info *c) +{ + int err; + struct inode *inode; + struct ubifs_budget_req req = { .new_ino = 1 }; + struct ubifs_inode *ui; + + /* Budget request settings: new inode. */ + dbg_gen("create root dir"); + + err = ubifs_budget_space(c, &req); + if (err) + return err; + + ui = ubifs_new_inode(c, NULL, S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO); + if (IS_ERR(ui)) { + err = PTR_ERR(ui); + goto out_budg; + } + + inode = &ui->vfs_inode; + inode->nlink = 2; + ui->ui_size = UBIFS_INO_NODE_SZ; + ui->flags = UBIFS_COMPR_FL; + err = ubifs_jnl_update_file(c, NULL, NULL, ui); + if (err) + goto out_ui; + + kfree(ui); + ubifs_release_budget(c, &req); + return 0; + +out_ui: + kfree(ui); +out_budg: + ubifs_release_budget(c, &req); + ubifs_err(c, "cannot create root dir, error %d", err); + return err; +} |