aboutsummaryrefslogtreecommitdiff
path: root/ubifs-utils/fsck.ubifs/extract_files.c
diff options
context:
space:
mode:
Diffstat (limited to 'ubifs-utils/fsck.ubifs/extract_files.c')
-rw-r--r--ubifs-utils/fsck.ubifs/extract_files.c239
1 files changed, 211 insertions, 28 deletions
diff --git a/ubifs-utils/fsck.ubifs/extract_files.c b/ubifs-utils/fsck.ubifs/extract_files.c
index e9c71a3..51b83b8 100644
--- a/ubifs-utils/fsck.ubifs/extract_files.c
+++ b/ubifs-utils/fsck.ubifs/extract_files.c
@@ -784,6 +784,26 @@ void destroy_file_tree(struct ubifs_info *c, struct rb_root *file_tree)
}
/**
+ * destroy_file_list - destroy files from a given list head.
+ * @c: UBIFS file-system description object
+ * @file_list: list of the scanned files
+ *
+ * Destroy scanned files from a given list.
+ */
+void destroy_file_list(struct ubifs_info *c, struct list_head *file_list)
+{
+ struct scanned_file *file;
+
+ while (!list_empty(file_list)) {
+ file = list_entry(file_list->next, struct scanned_file, list);
+
+ destroy_file_content(c, file);
+ list_del(&file->list);
+ kfree(file);
+ }
+}
+
+/**
* lookup_file - lookup file according to inode number.
* @file_tree: tree of all scanned files
* @inum: inode number
@@ -810,6 +830,109 @@ struct scanned_file *lookup_file(struct rb_root *file_tree, ino_t inum)
return NULL;
}
+static void handle_invalid_file(struct ubifs_info *c, int problem_type,
+ struct scanned_file *file, void *priv)
+{
+ struct invalid_file_problem ifp = {
+ .file = file,
+ .priv = priv,
+ };
+
+ if (FSCK(c)->mode == REBUILD_MODE)
+ return;
+
+ fix_problem(c, problem_type, &ifp);
+}
+
+static int delete_node(struct ubifs_info *c, const union ubifs_key *key,
+ int lnum, int offs)
+{
+ int err;
+
+ err = ubifs_tnc_remove_node(c, key, lnum, offs);
+ if (err) {
+ /* TNC traversing is finished, any TNC path is accessible */
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ }
+
+ return err;
+}
+
+static int delete_dent_nodes(struct ubifs_info *c, struct scanned_file *file,
+ int err)
+{
+ int ret = 0;
+ struct rb_node *this = rb_first(&file->dent_nodes);
+ struct scanned_dent_node *dent_node;
+
+ while (this) {
+ dent_node = rb_entry(this, struct scanned_dent_node, rb);
+ this = rb_next(this);
+
+ if (!err) {
+ err = delete_node(c, &dent_node->key,
+ dent_node->header.lnum, dent_node->header.offs);
+ if (err)
+ ret = ret ? ret : err;
+ }
+
+ rb_erase(&dent_node->rb, &file->dent_nodes);
+ kfree(dent_node);
+ }
+
+ return ret;
+}
+
+int delete_file(struct ubifs_info *c, struct scanned_file *file)
+{
+ int err = 0, ret = 0;
+ struct rb_node *this;
+ struct scanned_file *xattr_file;
+ struct scanned_data_node *data_node;
+
+ if (file->ino.header.exist) {
+ err = delete_node(c, &file->ino.key, file->ino.header.lnum,
+ file->ino.header.offs);
+ if (err)
+ ret = ret ? ret : err;
+ }
+
+ this = rb_first(&file->data_nodes);
+ while (this) {
+ data_node = rb_entry(this, struct scanned_data_node, rb);
+ this = rb_next(this);
+
+ if (!err) {
+ err = delete_node(c, &data_node->key,
+ data_node->header.lnum, data_node->header.offs);
+ if (err)
+ ret = ret ? ret : err;
+ }
+
+ rb_erase(&data_node->rb, &file->data_nodes);
+ kfree(data_node);
+ }
+
+ err = delete_dent_nodes(c, file, err);
+ if (err)
+ ret = ret ? : err;
+
+ this = rb_first(&file->xattr_files);
+ while (this) {
+ xattr_file = rb_entry(this, struct scanned_file, rb);
+ this = rb_next(this);
+
+ ubifs_assert(c, !rb_first(&xattr_file->xattr_files));
+ err = delete_file(c, xattr_file);
+ if (err)
+ ret = ret ? ret : err;
+ rb_erase(&xattr_file->rb, &file->xattr_files);
+ kfree(xattr_file);
+ }
+
+ return ret;
+}
+
/**
* insert_xattr_file - insert xattr file into file's subtree.
* @c: UBIFS file-system description object
@@ -848,6 +971,7 @@ static void insert_xattr_file(struct ubifs_info *c,
* @c: UBIFS file-system description object
* @file: file object
* @file_tree: tree of all scanned files
+ * @is_diconnected: reason of invalid file, whether the @file is disconnected
*
* This function checks whether given @file is valid, following checks will
* be performed:
@@ -866,12 +990,13 @@ static void insert_xattr_file(struct ubifs_info *c,
* invalid.
* Xattr file will be inserted into corresponding host file's subtree.
*
- * Returns %true is @file is valid, otherwise %false is returned.
+ * Returns %1 is @file is valid, %0 if @file is invalid, otherwise a negative
+ * error code in case of failure.
* Notice: All xattr files should be traversed before non-xattr files, because
* checking item 7 depends on it.
*/
-bool file_is_valid(struct ubifs_info *c, struct scanned_file *file,
- struct rb_root *file_tree)
+int file_is_valid(struct ubifs_info *c, struct scanned_file *file,
+ struct rb_root *file_tree, int *is_diconnected)
{
int type;
struct rb_node *node;
@@ -880,8 +1005,17 @@ bool file_is_valid(struct ubifs_info *c, struct scanned_file *file,
struct scanned_data_node *data_node;
LIST_HEAD(drop_list);
- if (!file->ino.header.exist || !file->ino.nlink)
- return false;
+ dbg_fsck("check validation of file %lu, in %s", file->inum, c->dev_name);
+
+ if (!file->ino.header.exist) {
+ handle_invalid_file(c, FILE_HAS_NO_INODE, file, NULL);
+ return 0;
+ }
+
+ if (!file->ino.nlink) {
+ handle_invalid_file(c, FILE_HAS_0_NLINK_INODE, file, NULL);
+ return 0;
+ }
type = ubifs_get_dent_type(file->ino.mode);
@@ -901,6 +1035,14 @@ bool file_is_valid(struct ubifs_info *c, struct scanned_file *file,
dent_node = list_entry(drop_list.next, struct scanned_dent_node,
list);
+ handle_invalid_file(c, FILE_HAS_INCONSIST_TYPE, file, dent_node);
+ if (FSCK(c)->mode != REBUILD_MODE) {
+ int err = delete_node(c, &dent_node->key,
+ dent_node->header.lnum, dent_node->header.offs);
+ if (err)
+ return err;
+ }
+
list_del(&dent_node->list);
rb_erase(&dent_node->rb, &file->dent_nodes);
kfree(dent_node);
@@ -909,11 +1051,7 @@ bool file_is_valid(struct ubifs_info *c, struct scanned_file *file,
if (type != UBIFS_ITYPE_DIR && !file->ino.is_xattr)
goto check_data_nodes;
- /*
- * Make sure that directory/xattr type files only have one dentry.
- * This work should be done in step 3, but file type could be unknown
- * for lacking inode information at that time, so do it here.
- */
+ /* Make sure that directory/xattr type files only have one dentry. */
node = rb_first(&file->dent_nodes);
while (node) {
dent_node = rb_entry(node, struct scanned_dent_node, rb);
@@ -921,6 +1059,14 @@ bool file_is_valid(struct ubifs_info *c, struct scanned_file *file,
if (!node)
break;
+ handle_invalid_file(c, FILE_HAS_TOO_MANY_DENT, file, dent_node);
+ if (FSCK(c)->mode != REBUILD_MODE) {
+ int err = delete_node(c, &dent_node->key,
+ dent_node->header.lnum, dent_node->header.offs);
+ if (err)
+ return err;
+ }
+
rb_erase(&dent_node->rb, &file->dent_nodes);
kfree(dent_node);
}
@@ -929,43 +1075,65 @@ check_data_nodes:
if (type == UBIFS_ITYPE_REG && !file->ino.is_xattr)
goto check_dent_node;
- /*
- * Make sure that non regular type files not have data/trun nodes.
- * This work should be done in step 3, but file type could be unknown
- * for lacking inode information at that time, so do it here.
- */
+ /* Make sure that non regular type files not have data/trun nodes. */
file->trun.header.exist = 0;
node = rb_first(&file->data_nodes);
while (node) {
data_node = rb_entry(node, struct scanned_data_node, rb);
node = rb_next(node);
+ handle_invalid_file(c, FILE_SHOULDNT_HAVE_DATA, file, data_node);
+ if (FSCK(c)->mode != REBUILD_MODE) {
+ int err = delete_node(c, &data_node->key,
+ data_node->header.lnum, data_node->header.offs);
+ if (err)
+ return err;
+ }
+
rb_erase(&data_node->rb, &file->data_nodes);
kfree(data_node);
}
check_dent_node:
if (rb_first(&file->dent_nodes)) {
- if (file->inum == UBIFS_ROOT_INO)
+ if (file->inum == UBIFS_ROOT_INO) {
/* '/' has no dentries. */
- return false;
+ handle_invalid_file(c, FILE_ROOT_HAS_DENT, file,
+ rb_entry(rb_first(&file->dent_nodes),
+ struct scanned_dent_node, rb));
+ return 0;
+ }
node = rb_first(&file->dent_nodes);
dent_node = rb_entry(node, struct scanned_dent_node, rb);
parent_file = lookup_file(file_tree, key_inum(c, &dent_node->key));
} else {
/* Non-root files must have dentries. */
- if (file->inum != UBIFS_ROOT_INO)
- return false;
+ if (file->inum != UBIFS_ROOT_INO) {
+ if (type == UBIFS_ITYPE_REG && !file->ino.is_xattr) {
+ handle_invalid_file(c, FILE_IS_DISCONNECTED,
+ file, NULL);
+ if (is_diconnected)
+ *is_diconnected = 1;
+ } else {
+ handle_invalid_file(c, FILE_HAS_NO_DENT,
+ file, NULL);
+ }
+ return 0;
+ }
}
if (file->ino.is_xattr) {
- if (!parent_file)
+ if (!parent_file) {
/* Host inode is not found. */
- return false;
- if (parent_file->ino.is_xattr)
+ handle_invalid_file(c, XATTR_HAS_NO_HOST, file, NULL);
+ return 0;
+ }
+ if (parent_file->ino.is_xattr) {
/* Host cannot be a xattr file. */
- return false;
+ handle_invalid_file(c, XATTR_HAS_WRONG_HOST, file, parent_file);
+ return 0;
+ }
insert_xattr_file(c, file, parent_file);
if (parent_file->ino.is_encrypted) {
@@ -977,20 +1145,35 @@ check_dent_node:
parent_file->has_encrypted_info = true;
}
} else {
- if (parent_file && !S_ISDIR(parent_file->ino.mode))
+ if (parent_file && !S_ISDIR(parent_file->ino.mode)) {
/* Parent file should be directory. */
- return false;
+ if (type == UBIFS_ITYPE_REG) {
+ handle_invalid_file(c, FILE_IS_DISCONNECTED,
+ file, NULL);
+ if (FSCK(c)->mode != REBUILD_MODE) {
+ /* Delete dentries for the disconnected file. */
+ int err = delete_dent_nodes(c, file, 0);
+ if (err)
+ return err;
+ }
+ if (is_diconnected)
+ *is_diconnected = 1;
+ }
+ return 0;
+ }
/*
* Since xattr files are checked in first round, so all
* non-xattr files's @has_encrypted_info fields have been
* initialized.
*/
- if (file->ino.is_encrypted && !file->has_encrypted_info)
- return false;
+ if (file->ino.is_encrypted && !file->has_encrypted_info) {
+ handle_invalid_file(c, FILE_HAS_NO_ENCRYPT, file, NULL);
+ return 0;
+ }
}
- return true;
+ return 1;
}
static bool dentry_is_reachable(struct ubifs_info *c,