aboutsummaryrefslogtreecommitdiff
path: root/ubifs-utils/libubifs/dir.c
diff options
context:
space:
mode:
authorZhihao Cheng <chengzhihao1@huawei.com>2024-11-11 17:08:12 +0800
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2024-11-11 10:32:46 +0100
commitdf5d5489aed7ae9de007776e19350bd5aebbfea2 (patch)
treee22d46cedff685cc8fa66bb695d14c5571bdfe45 /ubifs-utils/libubifs/dir.c
parent7e6ae3a8dcbedf0e65c091e985aff56f293cf8ec (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.c359
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;
+}