13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
23d0407baSopenharmony_ci/*
33d0407baSopenharmony_ci * This file is part of UBIFS.
43d0407baSopenharmony_ci *
53d0407baSopenharmony_ci * Copyright (C) 2006-2008 Nokia Corporation
63d0407baSopenharmony_ci *
73d0407baSopenharmony_ci * Authors: Adrian Hunter
83d0407baSopenharmony_ci *          Artem Bityutskiy (Битюцкий Артём)
93d0407baSopenharmony_ci */
103d0407baSopenharmony_ci
113d0407baSopenharmony_ci/*
123d0407baSopenharmony_ci * This file implements functions needed to recover from unclean un-mounts.
133d0407baSopenharmony_ci * When UBIFS is mounted, it checks a flag on the master node to determine if
143d0407baSopenharmony_ci * an un-mount was completed successfully. If not, the process of mounting
153d0407baSopenharmony_ci * incorporates additional checking and fixing of on-flash data structures.
163d0407baSopenharmony_ci * UBIFS always cleans away all remnants of an unclean un-mount, so that
173d0407baSopenharmony_ci * errors do not accumulate. However UBIFS defers recovery if it is mounted
183d0407baSopenharmony_ci * read-only, and the flash is not modified in that case.
193d0407baSopenharmony_ci *
203d0407baSopenharmony_ci * The general UBIFS approach to the recovery is that it recovers from
213d0407baSopenharmony_ci * corruptions which could be caused by power cuts, but it refuses to recover
223d0407baSopenharmony_ci * from corruption caused by other reasons. And UBIFS tries to distinguish
233d0407baSopenharmony_ci * between these 2 reasons of corruptions and silently recover in the former
243d0407baSopenharmony_ci * case and loudly complain in the latter case.
253d0407baSopenharmony_ci *
263d0407baSopenharmony_ci * UBIFS writes only to erased LEBs, so it writes only to the flash space
273d0407baSopenharmony_ci * containing only 0xFFs. UBIFS also always writes strictly from the beginning
283d0407baSopenharmony_ci * of the LEB to the end. And UBIFS assumes that the underlying flash media
293d0407baSopenharmony_ci * writes in @c->max_write_size bytes at a time.
303d0407baSopenharmony_ci *
313d0407baSopenharmony_ci * Hence, if UBIFS finds a corrupted node at offset X, it expects only the min.
323d0407baSopenharmony_ci * I/O unit corresponding to offset X to contain corrupted data, all the
333d0407baSopenharmony_ci * following min. I/O units have to contain empty space (all 0xFFs). If this is
343d0407baSopenharmony_ci * not true, the corruption cannot be the result of a power cut, and UBIFS
353d0407baSopenharmony_ci * refuses to mount.
363d0407baSopenharmony_ci */
373d0407baSopenharmony_ci
383d0407baSopenharmony_ci#include <linux/crc32.h>
393d0407baSopenharmony_ci#include <linux/slab.h>
403d0407baSopenharmony_ci#include "ubifs.h"
413d0407baSopenharmony_ci
423d0407baSopenharmony_ci#define RECOVERY_TWO 2
433d0407baSopenharmony_ci#define RECOVERY_SEVEN 7
443d0407baSopenharmony_ci#define RECOVERY_EIGHT 8
453d0407baSopenharmony_ci
463d0407baSopenharmony_ci/**
473d0407baSopenharmony_ci * is_empty - determine whether a buffer is empty (contains all 0xff).
483d0407baSopenharmony_ci * @buf: buffer to clean
493d0407baSopenharmony_ci * @len: length of buffer
503d0407baSopenharmony_ci *
513d0407baSopenharmony_ci * This function returns %1 if the buffer is empty (contains all 0xff) otherwise
523d0407baSopenharmony_ci * %0 is returned.
533d0407baSopenharmony_ci */
543d0407baSopenharmony_cistatic int is_empty(void *buf, int len)
553d0407baSopenharmony_ci{
563d0407baSopenharmony_ci    uint8_t *p = buf;
573d0407baSopenharmony_ci    int i;
583d0407baSopenharmony_ci
593d0407baSopenharmony_ci    for (i = 0; i < len; i++) {
603d0407baSopenharmony_ci        if (*p++ != 0xff) {
613d0407baSopenharmony_ci            return 0;
623d0407baSopenharmony_ci        }
633d0407baSopenharmony_ci    }
643d0407baSopenharmony_ci    return 1;
653d0407baSopenharmony_ci}
663d0407baSopenharmony_ci
673d0407baSopenharmony_ci/**
683d0407baSopenharmony_ci * first_non_ff - find offset of the first non-0xff byte.
693d0407baSopenharmony_ci * @buf: buffer to search in
703d0407baSopenharmony_ci * @len: length of buffer
713d0407baSopenharmony_ci *
723d0407baSopenharmony_ci * This function returns offset of the first non-0xff byte in @buf or %-1 if
733d0407baSopenharmony_ci * the buffer contains only 0xff bytes.
743d0407baSopenharmony_ci */
753d0407baSopenharmony_cistatic int first_non_ff(void *buf, int len)
763d0407baSopenharmony_ci{
773d0407baSopenharmony_ci    uint8_t *p = buf;
783d0407baSopenharmony_ci    int i;
793d0407baSopenharmony_ci
803d0407baSopenharmony_ci    for (i = 0; i < len; i++) {
813d0407baSopenharmony_ci        if (*p++ != 0xff) {
823d0407baSopenharmony_ci            return i;
833d0407baSopenharmony_ci        }
843d0407baSopenharmony_ci    }
853d0407baSopenharmony_ci    return -1;
863d0407baSopenharmony_ci}
873d0407baSopenharmony_ci
883d0407baSopenharmony_ci/**
893d0407baSopenharmony_ci * get_master_node - get the last valid master node allowing for corruption.
903d0407baSopenharmony_ci * @c: UBIFS file-system description object
913d0407baSopenharmony_ci * @lnum: LEB number
923d0407baSopenharmony_ci * @pbuf: buffer containing the LEB read, is returned here
933d0407baSopenharmony_ci * @mst: master node, if found, is returned here
943d0407baSopenharmony_ci * @cor: corruption, if found, is returned here
953d0407baSopenharmony_ci *
963d0407baSopenharmony_ci * This function allocates a buffer, reads the LEB into it, and finds and
973d0407baSopenharmony_ci * returns the last valid master node allowing for one area of corruption.
983d0407baSopenharmony_ci * The corrupt area, if there is one, must be consistent with the assumption
993d0407baSopenharmony_ci * that it is the result of an unclean unmount while the master node was being
1003d0407baSopenharmony_ci * written. Under those circumstances, it is valid to use the previously written
1013d0407baSopenharmony_ci * master node.
1023d0407baSopenharmony_ci *
1033d0407baSopenharmony_ci * This function returns %0 on success and a negative error code on failure.
1043d0407baSopenharmony_ci */
1053d0407baSopenharmony_cistatic int get_master_node(const struct ubifs_info *c, int lnum, void **pbuf, struct ubifs_mst_node **mst, void **cor)
1063d0407baSopenharmony_ci{
1073d0407baSopenharmony_ci    const int sz = c->mst_node_alsz;
1083d0407baSopenharmony_ci    int err, offs, len;
1093d0407baSopenharmony_ci    void *sbuf, *buf;
1103d0407baSopenharmony_ci
1113d0407baSopenharmony_ci    sbuf = vmalloc(c->leb_size);
1123d0407baSopenharmony_ci    if (!sbuf) {
1133d0407baSopenharmony_ci        return -ENOMEM;
1143d0407baSopenharmony_ci    }
1153d0407baSopenharmony_ci
1163d0407baSopenharmony_ci    err = ubifs_leb_read(c, lnum, sbuf, 0, c->leb_size, 0);
1173d0407baSopenharmony_ci    if (err && err != -EBADMSG) {
1183d0407baSopenharmony_ci        goto out_free;
1193d0407baSopenharmony_ci    }
1203d0407baSopenharmony_ci
1213d0407baSopenharmony_ci    /* Find the first position that is definitely not a node */
1223d0407baSopenharmony_ci    offs = 0;
1233d0407baSopenharmony_ci    buf = sbuf;
1243d0407baSopenharmony_ci    len = c->leb_size;
1253d0407baSopenharmony_ci    while (offs + UBIFS_MST_NODE_SZ <= c->leb_size) {
1263d0407baSopenharmony_ci        struct ubifs_ch *ch = buf;
1273d0407baSopenharmony_ci
1283d0407baSopenharmony_ci        if (le32_to_cpu(ch->magic) != UBIFS_NODE_MAGIC) {
1293d0407baSopenharmony_ci            break;
1303d0407baSopenharmony_ci        }
1313d0407baSopenharmony_ci        offs += sz;
1323d0407baSopenharmony_ci        buf += sz;
1333d0407baSopenharmony_ci        len -= sz;
1343d0407baSopenharmony_ci    }
1353d0407baSopenharmony_ci    /* See if there was a valid master node before that */
1363d0407baSopenharmony_ci    if (offs) {
1373d0407baSopenharmony_ci        int ret;
1383d0407baSopenharmony_ci
1393d0407baSopenharmony_ci        offs -= sz;
1403d0407baSopenharmony_ci        buf -= sz;
1413d0407baSopenharmony_ci        len += sz;
1423d0407baSopenharmony_ci        ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1);
1433d0407baSopenharmony_ci        if (ret != SCANNED_A_NODE && offs) {
1443d0407baSopenharmony_ci            /* Could have been corruption so check one place back */
1453d0407baSopenharmony_ci            offs -= sz;
1463d0407baSopenharmony_ci            buf -= sz;
1473d0407baSopenharmony_ci            len += sz;
1483d0407baSopenharmony_ci            ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1);
1493d0407baSopenharmony_ci            if (ret != SCANNED_A_NODE) {
1503d0407baSopenharmony_ci                /*
1513d0407baSopenharmony_ci                 * We accept only one area of corruption because
1523d0407baSopenharmony_ci                 * we are assuming that it was caused while
1533d0407baSopenharmony_ci                 * trying to write a master node.
1543d0407baSopenharmony_ci                 */
1553d0407baSopenharmony_ci                goto out_err;
1563d0407baSopenharmony_ci            }
1573d0407baSopenharmony_ci        }
1583d0407baSopenharmony_ci        if (ret == SCANNED_A_NODE) {
1593d0407baSopenharmony_ci            struct ubifs_ch *ch = buf;
1603d0407baSopenharmony_ci
1613d0407baSopenharmony_ci            if (ch->node_type != UBIFS_MST_NODE) {
1623d0407baSopenharmony_ci                goto out_err;
1633d0407baSopenharmony_ci            }
1643d0407baSopenharmony_ci            dbg_rcvry("found a master node at %d:%d", lnum, offs);
1653d0407baSopenharmony_ci            *mst = buf;
1663d0407baSopenharmony_ci            offs += sz;
1673d0407baSopenharmony_ci            buf += sz;
1683d0407baSopenharmony_ci            len -= sz;
1693d0407baSopenharmony_ci        }
1703d0407baSopenharmony_ci    }
1713d0407baSopenharmony_ci    /* Check for corruption */
1723d0407baSopenharmony_ci    if (offs < c->leb_size) {
1733d0407baSopenharmony_ci        if (!is_empty(buf, min_t(int, len, sz))) {
1743d0407baSopenharmony_ci            *cor = buf;
1753d0407baSopenharmony_ci            dbg_rcvry("found corruption at %d:%d", lnum, offs);
1763d0407baSopenharmony_ci        }
1773d0407baSopenharmony_ci        offs += sz;
1783d0407baSopenharmony_ci        buf += sz;
1793d0407baSopenharmony_ci        len -= sz;
1803d0407baSopenharmony_ci    }
1813d0407baSopenharmony_ci    /* Check remaining empty space */
1823d0407baSopenharmony_ci    if (offs < c->leb_size) {
1833d0407baSopenharmony_ci        if (!is_empty(buf, len)) {
1843d0407baSopenharmony_ci            goto out_err;
1853d0407baSopenharmony_ci        }
1863d0407baSopenharmony_ci    }
1873d0407baSopenharmony_ci    *pbuf = sbuf;
1883d0407baSopenharmony_ci    return 0;
1893d0407baSopenharmony_ci
1903d0407baSopenharmony_ciout_err:
1913d0407baSopenharmony_ci    err = -EINVAL;
1923d0407baSopenharmony_ciout_free:
1933d0407baSopenharmony_ci    vfree(sbuf);
1943d0407baSopenharmony_ci    *mst = NULL;
1953d0407baSopenharmony_ci    *cor = NULL;
1963d0407baSopenharmony_ci    return err;
1973d0407baSopenharmony_ci}
1983d0407baSopenharmony_ci
1993d0407baSopenharmony_ci/**
2003d0407baSopenharmony_ci * write_rcvrd_mst_node - write recovered master node.
2013d0407baSopenharmony_ci * @c: UBIFS file-system description object
2023d0407baSopenharmony_ci * @mst: master node
2033d0407baSopenharmony_ci *
2043d0407baSopenharmony_ci * This function returns %0 on success and a negative error code on failure.
2053d0407baSopenharmony_ci */
2063d0407baSopenharmony_cistatic int write_rcvrd_mst_node(struct ubifs_info *c, struct ubifs_mst_node *mst)
2073d0407baSopenharmony_ci{
2083d0407baSopenharmony_ci    int err = 0, lnum = UBIFS_MST_LNUM, sz = c->mst_node_alsz;
2093d0407baSopenharmony_ci    __le32 save_flags;
2103d0407baSopenharmony_ci
2113d0407baSopenharmony_ci    dbg_rcvry("recovery");
2123d0407baSopenharmony_ci
2133d0407baSopenharmony_ci    save_flags = mst->flags;
2143d0407baSopenharmony_ci    mst->flags |= cpu_to_le32(UBIFS_MST_RCVRY);
2153d0407baSopenharmony_ci
2163d0407baSopenharmony_ci    err = ubifs_prepare_node_hmac(c, mst, UBIFS_MST_NODE_SZ, offsetof(struct ubifs_mst_node, hmac), 1);
2173d0407baSopenharmony_ci    if (err) {
2183d0407baSopenharmony_ci        goto out;
2193d0407baSopenharmony_ci    }
2203d0407baSopenharmony_ci    err = ubifs_leb_change(c, lnum, mst, sz);
2213d0407baSopenharmony_ci    if (err) {
2223d0407baSopenharmony_ci        goto out;
2233d0407baSopenharmony_ci    }
2243d0407baSopenharmony_ci    err = ubifs_leb_change(c, lnum + 1, mst, sz);
2253d0407baSopenharmony_ci    if (err) {
2263d0407baSopenharmony_ci        goto out;
2273d0407baSopenharmony_ci    }
2283d0407baSopenharmony_ciout:
2293d0407baSopenharmony_ci    mst->flags = save_flags;
2303d0407baSopenharmony_ci    return err;
2313d0407baSopenharmony_ci}
2323d0407baSopenharmony_ci
2333d0407baSopenharmony_ci/**
2343d0407baSopenharmony_ci * ubifs_recover_master_node - recover the master node.
2353d0407baSopenharmony_ci * @c: UBIFS file-system description object
2363d0407baSopenharmony_ci *
2373d0407baSopenharmony_ci * This function recovers the master node from corruption that may occur due to
2383d0407baSopenharmony_ci * an unclean unmount.
2393d0407baSopenharmony_ci *
2403d0407baSopenharmony_ci * This function returns %0 on success and a negative error code on failure.
2413d0407baSopenharmony_ci */
2423d0407baSopenharmony_ciint ubifs_recover_master_node(struct ubifs_info *c)
2433d0407baSopenharmony_ci{
2443d0407baSopenharmony_ci    void *buf1 = NULL, *buf2 = NULL, *cor1 = NULL, *cor2 = NULL;
2453d0407baSopenharmony_ci    struct ubifs_mst_node *mst1 = NULL, *mst2 = NULL, *mst;
2463d0407baSopenharmony_ci    const int sz = c->mst_node_alsz;
2473d0407baSopenharmony_ci    int err, offs1, offs2;
2483d0407baSopenharmony_ci
2493d0407baSopenharmony_ci    dbg_rcvry("recovery");
2503d0407baSopenharmony_ci
2513d0407baSopenharmony_ci    err = get_master_node(c, UBIFS_MST_LNUM, &buf1, &mst1, &cor1);
2523d0407baSopenharmony_ci    if (err) {
2533d0407baSopenharmony_ci        goto out_free;
2543d0407baSopenharmony_ci    }
2553d0407baSopenharmony_ci
2563d0407baSopenharmony_ci    err = get_master_node(c, UBIFS_MST_LNUM + 1, &buf2, &mst2, &cor2);
2573d0407baSopenharmony_ci    if (err) {
2583d0407baSopenharmony_ci        goto out_free;
2593d0407baSopenharmony_ci    }
2603d0407baSopenharmony_ci
2613d0407baSopenharmony_ci    if (mst1) {
2623d0407baSopenharmony_ci        offs1 = (void *)mst1 - buf1;
2633d0407baSopenharmony_ci        if ((le32_to_cpu(mst1->flags) & UBIFS_MST_RCVRY) && (offs1 == 0 && !cor1)) {
2643d0407baSopenharmony_ci            /*
2653d0407baSopenharmony_ci             * mst1 was written by recovery at offset 0 with no
2663d0407baSopenharmony_ci             * corruption.
2673d0407baSopenharmony_ci             */
2683d0407baSopenharmony_ci            dbg_rcvry("recovery recovery");
2693d0407baSopenharmony_ci            mst = mst1;
2703d0407baSopenharmony_ci        } else if (mst2) {
2713d0407baSopenharmony_ci            offs2 = (void *)mst2 - buf2;
2723d0407baSopenharmony_ci            if (offs1 == offs2) {
2733d0407baSopenharmony_ci                /* Same offset, so must be the same */
2743d0407baSopenharmony_ci                if (ubifs_compare_master_node(c, mst1, mst2)) {
2753d0407baSopenharmony_ci                    goto out_err;
2763d0407baSopenharmony_ci                }
2773d0407baSopenharmony_ci                mst = mst1;
2783d0407baSopenharmony_ci            } else if (offs2 + sz == offs1) {
2793d0407baSopenharmony_ci                /* 1st LEB was written, 2nd was not */
2803d0407baSopenharmony_ci                if (cor1) {
2813d0407baSopenharmony_ci                    goto out_err;
2823d0407baSopenharmony_ci                }
2833d0407baSopenharmony_ci                mst = mst1;
2843d0407baSopenharmony_ci            } else if (offs1 == 0 && c->leb_size - offs2 - sz < sz) {
2853d0407baSopenharmony_ci                /* 1st LEB was unmapped and written, 2nd not */
2863d0407baSopenharmony_ci                if (cor1) {
2873d0407baSopenharmony_ci                    goto out_err;
2883d0407baSopenharmony_ci                }
2893d0407baSopenharmony_ci                mst = mst1;
2903d0407baSopenharmony_ci            } else {
2913d0407baSopenharmony_ci                goto out_err;
2923d0407baSopenharmony_ci            }
2933d0407baSopenharmony_ci        } else {
2943d0407baSopenharmony_ci            /*
2953d0407baSopenharmony_ci             * 2nd LEB was unmapped and about to be written, so
2963d0407baSopenharmony_ci             * there must be only one master node in the first LEB
2973d0407baSopenharmony_ci             * and no corruption.
2983d0407baSopenharmony_ci             */
2993d0407baSopenharmony_ci            if (offs1 != 0 || cor1) {
3003d0407baSopenharmony_ci                goto out_err;
3013d0407baSopenharmony_ci            }
3023d0407baSopenharmony_ci            mst = mst1;
3033d0407baSopenharmony_ci        }
3043d0407baSopenharmony_ci    } else {
3053d0407baSopenharmony_ci        if (!mst2) {
3063d0407baSopenharmony_ci            goto out_err;
3073d0407baSopenharmony_ci        }
3083d0407baSopenharmony_ci        /*
3093d0407baSopenharmony_ci         * 1st LEB was unmapped and about to be written, so there must
3103d0407baSopenharmony_ci         * be no room left in 2nd LEB.
3113d0407baSopenharmony_ci         */
3123d0407baSopenharmony_ci        offs2 = (void *)mst2 - buf2;
3133d0407baSopenharmony_ci        if (offs2 + sz + sz <= c->leb_size) {
3143d0407baSopenharmony_ci            goto out_err;
3153d0407baSopenharmony_ci        }
3163d0407baSopenharmony_ci        mst = mst2;
3173d0407baSopenharmony_ci    }
3183d0407baSopenharmony_ci
3193d0407baSopenharmony_ci    ubifs_msg(c, "recovered master node from LEB %d", (mst == mst1 ? UBIFS_MST_LNUM : UBIFS_MST_LNUM + 1));
3203d0407baSopenharmony_ci
3213d0407baSopenharmony_ci    memcpy(c->mst_node, mst, UBIFS_MST_NODE_SZ);
3223d0407baSopenharmony_ci
3233d0407baSopenharmony_ci    if (c->ro_mount) {
3243d0407baSopenharmony_ci        /* Read-only mode. Keep a copy for switching to rw mode */
3253d0407baSopenharmony_ci        c->rcvrd_mst_node = kmalloc(sz, GFP_KERNEL);
3263d0407baSopenharmony_ci        if (!c->rcvrd_mst_node) {
3273d0407baSopenharmony_ci            err = -ENOMEM;
3283d0407baSopenharmony_ci            goto out_free;
3293d0407baSopenharmony_ci        }
3303d0407baSopenharmony_ci        memcpy(c->rcvrd_mst_node, c->mst_node, UBIFS_MST_NODE_SZ);
3313d0407baSopenharmony_ci
3323d0407baSopenharmony_ci        /*
3333d0407baSopenharmony_ci         * We had to recover the master node, which means there was an
3343d0407baSopenharmony_ci         * unclean reboot. However, it is possible that the master node
3353d0407baSopenharmony_ci         * is clean at this point, i.e., %UBIFS_MST_DIRTY is not set.
3363d0407baSopenharmony_ci         * E.g., consider the following chain of events:
3373d0407baSopenharmony_ci         *
3383d0407baSopenharmony_ci         * 1. UBIFS was cleanly unmounted, so the master node is clean
3393d0407baSopenharmony_ci         * 2. UBIFS is being mounted R/W and starts changing the master
3403d0407baSopenharmony_ci         *    node in the first (%UBIFS_MST_LNUM). A power cut happens,
3413d0407baSopenharmony_ci         *    so this LEB ends up with some amount of garbage at the
3423d0407baSopenharmony_ci         *    end.
3433d0407baSopenharmony_ci         * 3. UBIFS is being mounted R/O. We reach this place and
3443d0407baSopenharmony_ci         *    recover the master node from the second LEB
3453d0407baSopenharmony_ci         *    (%UBIFS_MST_LNUM + 1). But we cannot update the media
3463d0407baSopenharmony_ci         *    because we are being mounted R/O. We have to defer the
3473d0407baSopenharmony_ci         *    operation.
3483d0407baSopenharmony_ci         * 4. However, this master node (@c->mst_node) is marked as
3493d0407baSopenharmony_ci         *    clean (since the step 1). And if we just return, the
3503d0407baSopenharmony_ci         *    mount code will be confused and won't recover the master
3513d0407baSopenharmony_ci         *    node when it is re-mounter R/W later.
3523d0407baSopenharmony_ci         *
3533d0407baSopenharmony_ci         *    Thus, to force the recovery by marking the master node as
3543d0407baSopenharmony_ci         *    dirty.
3553d0407baSopenharmony_ci         */
3563d0407baSopenharmony_ci        c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
3573d0407baSopenharmony_ci    } else {
3583d0407baSopenharmony_ci        /* Write the recovered master node */
3593d0407baSopenharmony_ci        c->max_sqnum = le64_to_cpu(mst->ch.sqnum) - 1;
3603d0407baSopenharmony_ci        err = write_rcvrd_mst_node(c, c->mst_node);
3613d0407baSopenharmony_ci        if (err) {
3623d0407baSopenharmony_ci            goto out_free;
3633d0407baSopenharmony_ci        }
3643d0407baSopenharmony_ci    }
3653d0407baSopenharmony_ci
3663d0407baSopenharmony_ci    vfree(buf2);
3673d0407baSopenharmony_ci    vfree(buf1);
3683d0407baSopenharmony_ci
3693d0407baSopenharmony_ci    return 0;
3703d0407baSopenharmony_ci
3713d0407baSopenharmony_ciout_err:
3723d0407baSopenharmony_ci    err = -EINVAL;
3733d0407baSopenharmony_ciout_free:
3743d0407baSopenharmony_ci    ubifs_err(c, "failed to recover master node");
3753d0407baSopenharmony_ci    if (mst1) {
3763d0407baSopenharmony_ci        ubifs_err(c, "dumping first master node");
3773d0407baSopenharmony_ci        ubifs_dump_node(c, mst1, c->leb_size - ((void *)mst1 - buf1));
3783d0407baSopenharmony_ci    }
3793d0407baSopenharmony_ci    if (mst2) {
3803d0407baSopenharmony_ci        ubifs_err(c, "dumping second master node");
3813d0407baSopenharmony_ci        ubifs_dump_node(c, mst2, c->leb_size - ((void *)mst2 - buf2));
3823d0407baSopenharmony_ci    }
3833d0407baSopenharmony_ci    vfree(buf2);
3843d0407baSopenharmony_ci    vfree(buf1);
3853d0407baSopenharmony_ci    return err;
3863d0407baSopenharmony_ci}
3873d0407baSopenharmony_ci
3883d0407baSopenharmony_ci/**
3893d0407baSopenharmony_ci * ubifs_write_rcvrd_mst_node - write the recovered master node.
3903d0407baSopenharmony_ci * @c: UBIFS file-system description object
3913d0407baSopenharmony_ci *
3923d0407baSopenharmony_ci * This function writes the master node that was recovered during mounting in
3933d0407baSopenharmony_ci * read-only mode and must now be written because we are remounting rw.
3943d0407baSopenharmony_ci *
3953d0407baSopenharmony_ci * This function returns %0 on success and a negative error code on failure.
3963d0407baSopenharmony_ci */
3973d0407baSopenharmony_ciint ubifs_write_rcvrd_mst_node(struct ubifs_info *c)
3983d0407baSopenharmony_ci{
3993d0407baSopenharmony_ci    int err;
4003d0407baSopenharmony_ci
4013d0407baSopenharmony_ci    if (!c->rcvrd_mst_node) {
4023d0407baSopenharmony_ci        return 0;
4033d0407baSopenharmony_ci    }
4043d0407baSopenharmony_ci    c->rcvrd_mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
4053d0407baSopenharmony_ci    c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
4063d0407baSopenharmony_ci    err = write_rcvrd_mst_node(c, c->rcvrd_mst_node);
4073d0407baSopenharmony_ci    if (err) {
4083d0407baSopenharmony_ci        return err;
4093d0407baSopenharmony_ci    }
4103d0407baSopenharmony_ci    kfree(c->rcvrd_mst_node);
4113d0407baSopenharmony_ci    c->rcvrd_mst_node = NULL;
4123d0407baSopenharmony_ci    return 0;
4133d0407baSopenharmony_ci}
4143d0407baSopenharmony_ci
4153d0407baSopenharmony_ci/**
4163d0407baSopenharmony_ci * is_last_write - determine if an offset was in the last write to a LEB.
4173d0407baSopenharmony_ci * @c: UBIFS file-system description object
4183d0407baSopenharmony_ci * @buf: buffer to check
4193d0407baSopenharmony_ci * @offs: offset to check
4203d0407baSopenharmony_ci *
4213d0407baSopenharmony_ci * This function returns %1 if @offs was in the last write to the LEB whose data
4223d0407baSopenharmony_ci * is in @buf, otherwise %0 is returned. The determination is made by checking
4233d0407baSopenharmony_ci * for subsequent empty space starting from the next @c->max_write_size
4243d0407baSopenharmony_ci * boundary.
4253d0407baSopenharmony_ci */
4263d0407baSopenharmony_cistatic int is_last_write(const struct ubifs_info *c, void *buf, int offs)
4273d0407baSopenharmony_ci{
4283d0407baSopenharmony_ci    int empty_offs, check_len;
4293d0407baSopenharmony_ci    uint8_t *p;
4303d0407baSopenharmony_ci
4313d0407baSopenharmony_ci    /*
4323d0407baSopenharmony_ci     * Round up to the next @c->max_write_size boundary i.e. @offs is in
4333d0407baSopenharmony_ci     * the last wbuf written. After that should be empty space.
4343d0407baSopenharmony_ci     */
4353d0407baSopenharmony_ci    empty_offs = ALIGN(offs + 1, c->max_write_size);
4363d0407baSopenharmony_ci    check_len = c->leb_size - empty_offs;
4373d0407baSopenharmony_ci    p = buf + empty_offs - offs;
4383d0407baSopenharmony_ci    return is_empty(p, check_len);
4393d0407baSopenharmony_ci}
4403d0407baSopenharmony_ci
4413d0407baSopenharmony_ci/**
4423d0407baSopenharmony_ci * clean_buf - clean the data from an LEB sitting in a buffer.
4433d0407baSopenharmony_ci * @c: UBIFS file-system description object
4443d0407baSopenharmony_ci * @buf: buffer to clean
4453d0407baSopenharmony_ci * @lnum: LEB number to clean
4463d0407baSopenharmony_ci * @offs: offset from which to clean
4473d0407baSopenharmony_ci * @len: length of buffer
4483d0407baSopenharmony_ci *
4493d0407baSopenharmony_ci * This function pads up to the next min_io_size boundary (if there is one) and
4503d0407baSopenharmony_ci * sets empty space to all 0xff. @buf, @offs and @len are updated to the next
4513d0407baSopenharmony_ci * @c->min_io_size boundary.
4523d0407baSopenharmony_ci */
4533d0407baSopenharmony_cistatic void clean_buf(const struct ubifs_info *c, void **buf, int lnum, int *offs, int *len)
4543d0407baSopenharmony_ci{
4553d0407baSopenharmony_ci    int empty_offs, pad_len;
4563d0407baSopenharmony_ci
4573d0407baSopenharmony_ci    dbg_rcvry("cleaning corruption at %d:%d", lnum, *offs);
4583d0407baSopenharmony_ci
4593d0407baSopenharmony_ci    ubifs_assert(c, !(*offs & RECOVERY_SEVEN));
4603d0407baSopenharmony_ci    empty_offs = ALIGN(*offs, c->min_io_size);
4613d0407baSopenharmony_ci    pad_len = empty_offs - *offs;
4623d0407baSopenharmony_ci    ubifs_pad(c, *buf, pad_len);
4633d0407baSopenharmony_ci    *offs += pad_len;
4643d0407baSopenharmony_ci    *buf += pad_len;
4653d0407baSopenharmony_ci    *len -= pad_len;
4663d0407baSopenharmony_ci    memset(*buf, 0xff, c->leb_size - empty_offs);
4673d0407baSopenharmony_ci}
4683d0407baSopenharmony_ci
4693d0407baSopenharmony_ci/**
4703d0407baSopenharmony_ci * no_more_nodes - determine if there are no more nodes in a buffer.
4713d0407baSopenharmony_ci * @c: UBIFS file-system description object
4723d0407baSopenharmony_ci * @buf: buffer to check
4733d0407baSopenharmony_ci * @len: length of buffer
4743d0407baSopenharmony_ci * @lnum: LEB number of the LEB from which @buf was read
4753d0407baSopenharmony_ci * @offs: offset from which @buf was read
4763d0407baSopenharmony_ci *
4773d0407baSopenharmony_ci * This function ensures that the corrupted node at @offs is the last thing
4783d0407baSopenharmony_ci * written to a LEB. This function returns %1 if more data is not found and
4793d0407baSopenharmony_ci * %0 if more data is found.
4803d0407baSopenharmony_ci */
4813d0407baSopenharmony_cistatic int no_more_nodes(const struct ubifs_info *c, void *buf, int len, int lnum, int offs)
4823d0407baSopenharmony_ci{
4833d0407baSopenharmony_ci    struct ubifs_ch *ch = buf;
4843d0407baSopenharmony_ci    int skip, dlen = le32_to_cpu(ch->len);
4853d0407baSopenharmony_ci
4863d0407baSopenharmony_ci    /* Check for empty space after the corrupt node's common header */
4873d0407baSopenharmony_ci    skip = ALIGN(offs + UBIFS_CH_SZ, c->max_write_size) - offs;
4883d0407baSopenharmony_ci    if (is_empty(buf + skip, len - skip)) {
4893d0407baSopenharmony_ci        return 1;
4903d0407baSopenharmony_ci    }
4913d0407baSopenharmony_ci    /*
4923d0407baSopenharmony_ci     * The area after the common header size is not empty, so the common
4933d0407baSopenharmony_ci     * header must be intact. Check it.
4943d0407baSopenharmony_ci     */
4953d0407baSopenharmony_ci    if (ubifs_check_node(c, buf, len, lnum, offs, 1, 0) != -EUCLEAN) {
4963d0407baSopenharmony_ci        dbg_rcvry("unexpected bad common header at %d:%d", lnum, offs);
4973d0407baSopenharmony_ci        return 0;
4983d0407baSopenharmony_ci    }
4993d0407baSopenharmony_ci    /* Now we know the corrupt node's length we can skip over it */
5003d0407baSopenharmony_ci    skip = ALIGN(offs + dlen, c->max_write_size) - offs;
5013d0407baSopenharmony_ci    /* After which there should be empty space */
5023d0407baSopenharmony_ci    if (is_empty(buf + skip, len - skip)) {
5033d0407baSopenharmony_ci        return 1;
5043d0407baSopenharmony_ci    }
5053d0407baSopenharmony_ci    dbg_rcvry("unexpected data at %d:%d", lnum, offs + skip);
5063d0407baSopenharmony_ci    return 0;
5073d0407baSopenharmony_ci}
5083d0407baSopenharmony_ci
5093d0407baSopenharmony_ci/**
5103d0407baSopenharmony_ci * fix_unclean_leb - fix an unclean LEB.
5113d0407baSopenharmony_ci * @c: UBIFS file-system description object
5123d0407baSopenharmony_ci * @sleb: scanned LEB information
5133d0407baSopenharmony_ci * @start: offset where scan started
5143d0407baSopenharmony_ci */
5153d0407baSopenharmony_cistatic int fix_unclean_leb(struct ubifs_info *c, struct ubifs_scan_leb *sleb, int start)
5163d0407baSopenharmony_ci{
5173d0407baSopenharmony_ci    int lnum = sleb->lnum, endpt = start;
5183d0407baSopenharmony_ci
5193d0407baSopenharmony_ci    /* Get the end offset of the last node we are keeping */
5203d0407baSopenharmony_ci    if (!list_empty(&sleb->nodes)) {
5213d0407baSopenharmony_ci        struct ubifs_scan_node *snod;
5223d0407baSopenharmony_ci
5233d0407baSopenharmony_ci        snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node, list);
5243d0407baSopenharmony_ci        endpt = snod->offs + snod->len;
5253d0407baSopenharmony_ci    }
5263d0407baSopenharmony_ci
5273d0407baSopenharmony_ci    if (c->ro_mount && !c->remounting_rw) {
5283d0407baSopenharmony_ci        /* Add to recovery list */
5293d0407baSopenharmony_ci        struct ubifs_unclean_leb *ucleb;
5303d0407baSopenharmony_ci
5313d0407baSopenharmony_ci        dbg_rcvry("need to fix LEB %d start %d endpt %d", lnum, start, sleb->endpt);
5323d0407baSopenharmony_ci        ucleb = kzalloc(sizeof(struct ubifs_unclean_leb), GFP_NOFS);
5333d0407baSopenharmony_ci        if (!ucleb) {
5343d0407baSopenharmony_ci            return -ENOMEM;
5353d0407baSopenharmony_ci        }
5363d0407baSopenharmony_ci        ucleb->lnum = lnum;
5373d0407baSopenharmony_ci        ucleb->endpt = endpt;
5383d0407baSopenharmony_ci        list_add_tail(&ucleb->list, &c->unclean_leb_list);
5393d0407baSopenharmony_ci    } else {
5403d0407baSopenharmony_ci        /* Write the fixed LEB back to flash */
5413d0407baSopenharmony_ci        int err;
5423d0407baSopenharmony_ci
5433d0407baSopenharmony_ci        dbg_rcvry("fixing LEB %d start %d endpt %d", lnum, start, sleb->endpt);
5443d0407baSopenharmony_ci        if (endpt == 0) {
5453d0407baSopenharmony_ci            err = ubifs_leb_unmap(c, lnum);
5463d0407baSopenharmony_ci            if (err) {
5473d0407baSopenharmony_ci                return err;
5483d0407baSopenharmony_ci            }
5493d0407baSopenharmony_ci        } else {
5503d0407baSopenharmony_ci            int len = ALIGN(endpt, c->min_io_size);
5513d0407baSopenharmony_ci
5523d0407baSopenharmony_ci            if (start) {
5533d0407baSopenharmony_ci                err = ubifs_leb_read(c, lnum, sleb->buf, 0, start, 1);
5543d0407baSopenharmony_ci                if (err) {
5553d0407baSopenharmony_ci                    return err;
5563d0407baSopenharmony_ci                }
5573d0407baSopenharmony_ci            }
5583d0407baSopenharmony_ci            /* Pad to min_io_size */
5593d0407baSopenharmony_ci            if (len > endpt) {
5603d0407baSopenharmony_ci                int pad_len = len - ALIGN(endpt, RECOVERY_EIGHT);
5613d0407baSopenharmony_ci                if (pad_len > 0) {
5623d0407baSopenharmony_ci                    void *buf = sleb->buf + len - pad_len;
5633d0407baSopenharmony_ci
5643d0407baSopenharmony_ci                    ubifs_pad(c, buf, pad_len);
5653d0407baSopenharmony_ci                }
5663d0407baSopenharmony_ci            }
5673d0407baSopenharmony_ci            err = ubifs_leb_change(c, lnum, sleb->buf, len);
5683d0407baSopenharmony_ci            if (err) {
5693d0407baSopenharmony_ci                return err;
5703d0407baSopenharmony_ci            }
5713d0407baSopenharmony_ci        }
5723d0407baSopenharmony_ci    }
5733d0407baSopenharmony_ci    return 0;
5743d0407baSopenharmony_ci}
5753d0407baSopenharmony_ci
5763d0407baSopenharmony_ci/**
5773d0407baSopenharmony_ci * drop_last_group - drop the last group of nodes.
5783d0407baSopenharmony_ci * @sleb: scanned LEB information
5793d0407baSopenharmony_ci * @offs: offset of dropped nodes is returned here
5803d0407baSopenharmony_ci *
5813d0407baSopenharmony_ci * This is a helper function for 'ubifs_recover_leb()' which drops the last
5823d0407baSopenharmony_ci * group of nodes of the scanned LEB.
5833d0407baSopenharmony_ci */
5843d0407baSopenharmony_cistatic void drop_last_group(struct ubifs_scan_leb *sleb, int *offs)
5853d0407baSopenharmony_ci{
5863d0407baSopenharmony_ci    while (!list_empty(&sleb->nodes)) {
5873d0407baSopenharmony_ci        struct ubifs_scan_node *snod;
5883d0407baSopenharmony_ci        struct ubifs_ch *ch;
5893d0407baSopenharmony_ci
5903d0407baSopenharmony_ci        snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node, list);
5913d0407baSopenharmony_ci        ch = snod->node;
5923d0407baSopenharmony_ci        if (ch->group_type != UBIFS_IN_NODE_GROUP) {
5933d0407baSopenharmony_ci            break;
5943d0407baSopenharmony_ci        }
5953d0407baSopenharmony_ci
5963d0407baSopenharmony_ci        dbg_rcvry("dropping grouped node at %d:%d", sleb->lnum, snod->offs);
5973d0407baSopenharmony_ci        *offs = snod->offs;
5983d0407baSopenharmony_ci        list_del(&snod->list);
5993d0407baSopenharmony_ci        kfree(snod);
6003d0407baSopenharmony_ci        sleb->nodes_cnt -= 1;
6013d0407baSopenharmony_ci    }
6023d0407baSopenharmony_ci}
6033d0407baSopenharmony_ci
6043d0407baSopenharmony_ci/**
6053d0407baSopenharmony_ci * drop_last_node - drop the last node.
6063d0407baSopenharmony_ci * @sleb: scanned LEB information
6073d0407baSopenharmony_ci * @offs: offset of dropped nodes is returned here
6083d0407baSopenharmony_ci *
6093d0407baSopenharmony_ci * This is a helper function for 'ubifs_recover_leb()' which drops the last
6103d0407baSopenharmony_ci * node of the scanned LEB.
6113d0407baSopenharmony_ci */
6123d0407baSopenharmony_cistatic void drop_last_node(struct ubifs_scan_leb *sleb, int *offs)
6133d0407baSopenharmony_ci{
6143d0407baSopenharmony_ci    struct ubifs_scan_node *snod;
6153d0407baSopenharmony_ci
6163d0407baSopenharmony_ci    if (!list_empty(&sleb->nodes)) {
6173d0407baSopenharmony_ci        snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node, list);
6183d0407baSopenharmony_ci
6193d0407baSopenharmony_ci        dbg_rcvry("dropping last node at %d:%d", sleb->lnum, snod->offs);
6203d0407baSopenharmony_ci        *offs = snod->offs;
6213d0407baSopenharmony_ci        list_del(&snod->list);
6223d0407baSopenharmony_ci        kfree(snod);
6233d0407baSopenharmony_ci        sleb->nodes_cnt -= 1;
6243d0407baSopenharmony_ci    }
6253d0407baSopenharmony_ci}
6263d0407baSopenharmony_ci
6273d0407baSopenharmony_ci/**
6283d0407baSopenharmony_ci * ubifs_recover_leb - scan and recover a LEB.
6293d0407baSopenharmony_ci * @c: UBIFS file-system description object
6303d0407baSopenharmony_ci * @lnum: LEB number
6313d0407baSopenharmony_ci * @offs: offset
6323d0407baSopenharmony_ci * @sbuf: LEB-sized buffer to use
6333d0407baSopenharmony_ci * @jhead: journal head number this LEB belongs to (%-1 if the LEB does not
6343d0407baSopenharmony_ci *         belong to any journal head)
6353d0407baSopenharmony_ci *
6363d0407baSopenharmony_ci * This function does a scan of a LEB, but caters for errors that might have
6373d0407baSopenharmony_ci * been caused by the unclean unmount from which we are attempting to recover.
6383d0407baSopenharmony_ci * Returns the scanned information on success and a negative error code on
6393d0407baSopenharmony_ci * failure.
6403d0407baSopenharmony_ci */
6413d0407baSopenharmony_cistruct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf, int jhead)
6423d0407baSopenharmony_ci{
6433d0407baSopenharmony_ci    int ret = 0, err, len = c->leb_size - offs, start = offs, min_io_unit;
6443d0407baSopenharmony_ci    int grouped = jhead == -1 ? 0 : c->jheads[jhead].grouped;
6453d0407baSopenharmony_ci    struct ubifs_scan_leb *sleb;
6463d0407baSopenharmony_ci    void *buf = sbuf + offs;
6473d0407baSopenharmony_ci
6483d0407baSopenharmony_ci    dbg_rcvry("%d:%d, jhead %d, grouped %d", lnum, offs, jhead, grouped);
6493d0407baSopenharmony_ci
6503d0407baSopenharmony_ci    sleb = ubifs_start_scan(c, lnum, offs, sbuf);
6513d0407baSopenharmony_ci    if (IS_ERR(sleb)) {
6523d0407baSopenharmony_ci        return sleb;
6533d0407baSopenharmony_ci    }
6543d0407baSopenharmony_ci
6553d0407baSopenharmony_ci    ubifs_assert(c, len >= RECOVERY_EIGHT);
6563d0407baSopenharmony_ci    while (len >= RECOVERY_EIGHT) {
6573d0407baSopenharmony_ci        dbg_scan("look at LEB %d:%d (%d bytes left)", lnum, offs, len);
6583d0407baSopenharmony_ci
6593d0407baSopenharmony_ci        cond_resched();
6603d0407baSopenharmony_ci
6613d0407baSopenharmony_ci        /*
6623d0407baSopenharmony_ci         * Scan quietly until there is an error from which we cannot
6633d0407baSopenharmony_ci         * recover
6643d0407baSopenharmony_ci         */
6653d0407baSopenharmony_ci        ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1);
6663d0407baSopenharmony_ci        if (ret == SCANNED_A_NODE) {
6673d0407baSopenharmony_ci            /* A valid node, and not a padding node */
6683d0407baSopenharmony_ci            struct ubifs_ch *ch = buf;
6693d0407baSopenharmony_ci            int node_len;
6703d0407baSopenharmony_ci
6713d0407baSopenharmony_ci            err = ubifs_add_snod(c, sleb, buf, offs);
6723d0407baSopenharmony_ci            if (err) {
6733d0407baSopenharmony_ci                goto error;
6743d0407baSopenharmony_ci            }
6753d0407baSopenharmony_ci            node_len = ALIGN(le32_to_cpu(ch->len), RECOVERY_EIGHT);
6763d0407baSopenharmony_ci            offs += node_len;
6773d0407baSopenharmony_ci            buf += node_len;
6783d0407baSopenharmony_ci            len -= node_len;
6793d0407baSopenharmony_ci        } else if (ret > 0) {
6803d0407baSopenharmony_ci            /* Padding bytes or a valid padding node */
6813d0407baSopenharmony_ci            offs += ret;
6823d0407baSopenharmony_ci            buf += ret;
6833d0407baSopenharmony_ci            len -= ret;
6843d0407baSopenharmony_ci        } else if (ret == SCANNED_A_CORRUPT_NODE) {
6853d0407baSopenharmony_ci            dbg_rcvry("found corruption (%d) at %d:%d", ret, lnum, offs);
6863d0407baSopenharmony_ci            if (ubifs_check_node(c, buf, len, lnum, offs, 1, 1) == -EUCLEAN &&
6873d0407baSopenharmony_ci                !no_more_nodes(c, buf, len, lnum, offs)) {
6883d0407baSopenharmony_ci                int skip;
6893d0407baSopenharmony_ci                struct ubifs_ch *ch = buf;
6903d0407baSopenharmony_ci
6913d0407baSopenharmony_ci                /*
6923d0407baSopenharmony_ci                 * If the flash voltage power down suddenly in the programming
6933d0407baSopenharmony_ci                 * process, it may lead to abnormal data written by the flash
6943d0407baSopenharmony_ci                 * in the low-voltage operation process, and the last data
6953d0407baSopenharmony_ci                 * should be discarded.
6963d0407baSopenharmony_ci                 */
6973d0407baSopenharmony_ci                ubifs_msg(c, "recovery corrupt node\n");
6983d0407baSopenharmony_ci                skip = ALIGN(offs + le32_to_cpu(ch->len), c->max_write_size) - offs;
6993d0407baSopenharmony_ci                memset(buf + skip, 0xff, len - skip);
7003d0407baSopenharmony_ci            }
7013d0407baSopenharmony_ci
7023d0407baSopenharmony_ci            break;
7033d0407baSopenharmony_ci        } else if (ret == SCANNED_EMPTY_SPACE) {
7043d0407baSopenharmony_ci            dbg_rcvry("found corruption (%d) at %d:%d", ret, lnum, offs);
7053d0407baSopenharmony_ci            if (!is_empty(buf, len) && !is_last_write(c, buf, offs)) {
7063d0407baSopenharmony_ci                /*
7073d0407baSopenharmony_ci                 * If the flash voltage power down suddenly in the programming
7083d0407baSopenharmony_ci                 * process, it may lead to the data was programmed to the wroge
7093d0407baSopenharmony_ci                 * page written by the flash in the low-voltage operation process,
7103d0407baSopenharmony_ci                 * and the data should be discarded.
7113d0407baSopenharmony_ci                 */
7123d0407baSopenharmony_ci                ubifs_msg(c, "recovery empty space\n");
7133d0407baSopenharmony_ci                memset(buf, 0xff, len);
7143d0407baSopenharmony_ci            }
7153d0407baSopenharmony_ci
7163d0407baSopenharmony_ci            break;
7173d0407baSopenharmony_ci        } else if (ret == SCANNED_GARBAGE || ret == SCANNED_A_BAD_PAD_NODE) {
7183d0407baSopenharmony_ci            dbg_rcvry("found corruption (%d) at %d:%d", ret, lnum, offs);
7193d0407baSopenharmony_ci            break;
7203d0407baSopenharmony_ci        } else {
7213d0407baSopenharmony_ci            ubifs_err(c, "unexpected return value %d", ret);
7223d0407baSopenharmony_ci            err = -EINVAL;
7233d0407baSopenharmony_ci            goto error;
7243d0407baSopenharmony_ci        }
7253d0407baSopenharmony_ci    }
7263d0407baSopenharmony_ci
7273d0407baSopenharmony_ci    if (ret == SCANNED_GARBAGE || ret == SCANNED_A_BAD_PAD_NODE) {
7283d0407baSopenharmony_ci        if (!is_last_write(c, buf, offs)) {
7293d0407baSopenharmony_ci            goto corrupted_rescan;
7303d0407baSopenharmony_ci        }
7313d0407baSopenharmony_ci    } else if (ret == SCANNED_A_CORRUPT_NODE) {
7323d0407baSopenharmony_ci        if (!no_more_nodes(c, buf, len, lnum, offs)) {
7333d0407baSopenharmony_ci            goto corrupted_rescan;
7343d0407baSopenharmony_ci        }
7353d0407baSopenharmony_ci    } else if (!is_empty(buf, len)) {
7363d0407baSopenharmony_ci        if (!is_last_write(c, buf, offs)) {
7373d0407baSopenharmony_ci            int corruption = first_non_ff(buf, len);
7383d0407baSopenharmony_ci
7393d0407baSopenharmony_ci            /*
7403d0407baSopenharmony_ci             * See header comment for this file for more
7413d0407baSopenharmony_ci             * explanations about the reasons we have this check.
7423d0407baSopenharmony_ci             */
7433d0407baSopenharmony_ci            ubifs_err(c, "corrupt empty space LEB %d:%d, corruption starts at %d", lnum, offs, corruption);
7443d0407baSopenharmony_ci            /* Make sure we dump interesting non-0xFF data */
7453d0407baSopenharmony_ci            offs += corruption;
7463d0407baSopenharmony_ci            buf += corruption;
7473d0407baSopenharmony_ci            goto corrupted;
7483d0407baSopenharmony_ci        }
7493d0407baSopenharmony_ci    }
7503d0407baSopenharmony_ci
7513d0407baSopenharmony_ci    min_io_unit = round_down(offs, c->min_io_size);
7523d0407baSopenharmony_ci    if (grouped) {
7533d0407baSopenharmony_ci        /*
7543d0407baSopenharmony_ci         * If nodes are grouped, always drop the incomplete group at
7553d0407baSopenharmony_ci         * the end.
7563d0407baSopenharmony_ci         */
7573d0407baSopenharmony_ci        drop_last_group(sleb, &offs);
7583d0407baSopenharmony_ci    }
7593d0407baSopenharmony_ci
7603d0407baSopenharmony_ci    if (jhead == GCHD) {
7613d0407baSopenharmony_ci        /*
7623d0407baSopenharmony_ci         * If this LEB belongs to the GC head then while we are in the
7633d0407baSopenharmony_ci         * middle of the same min. I/O unit keep dropping nodes. So
7643d0407baSopenharmony_ci         * basically, what we want is to make sure that the last min.
7653d0407baSopenharmony_ci         * I/O unit where we saw the corruption is dropped completely
7663d0407baSopenharmony_ci         * with all the uncorrupted nodes which may possibly sit there.
7673d0407baSopenharmony_ci         *
7683d0407baSopenharmony_ci         * In other words, let's name the min. I/O unit where the
7693d0407baSopenharmony_ci         * corruption starts B, and the previous min. I/O unit A. The
7703d0407baSopenharmony_ci         * below code tries to deal with a situation when half of B
7713d0407baSopenharmony_ci         * contains valid nodes or the end of a valid node, and the
7723d0407baSopenharmony_ci         * second half of B contains corrupted data or garbage. This
7733d0407baSopenharmony_ci         * means that UBIFS had been writing to B just before the power
7743d0407baSopenharmony_ci         * cut happened. I do not know how realistic is this scenario
7753d0407baSopenharmony_ci         * that half of the min. I/O unit had been written successfully
7763d0407baSopenharmony_ci         * and the other half not, but this is possible in our 'failure
7773d0407baSopenharmony_ci         * mode emulation' infrastructure at least.
7783d0407baSopenharmony_ci         *
7793d0407baSopenharmony_ci         * So what is the problem, why we need to drop those nodes? Why
7803d0407baSopenharmony_ci         * can't we just clean-up the second half of B by putting a
7813d0407baSopenharmony_ci         * padding node there? We can, and this works fine with one
7823d0407baSopenharmony_ci         * exception which was reproduced with power cut emulation
7833d0407baSopenharmony_ci         * testing and happens extremely rarely.
7843d0407baSopenharmony_ci         *
7853d0407baSopenharmony_ci         * Imagine the file-system is full, we run GC which starts
7863d0407baSopenharmony_ci         * moving valid nodes from LEB X to LEB Y (obviously, LEB Y is
7873d0407baSopenharmony_ci         * the current GC head LEB). The @c->gc_lnum is -1, which means
7883d0407baSopenharmony_ci         * that GC will retain LEB X and will try to continue. Imagine
7893d0407baSopenharmony_ci         * that LEB X is currently the dirtiest LEB, and the amount of
7903d0407baSopenharmony_ci         * used space in LEB Y is exactly the same as amount of free
7913d0407baSopenharmony_ci         * space in LEB X.
7923d0407baSopenharmony_ci         *
7933d0407baSopenharmony_ci         * And a power cut happens when nodes are moved from LEB X to
7943d0407baSopenharmony_ci         * LEB Y. We are here trying to recover LEB Y which is the GC
7953d0407baSopenharmony_ci         * head LEB. We find the min. I/O unit B as described above.
7963d0407baSopenharmony_ci         * Then we clean-up LEB Y by padding min. I/O unit. And later
7973d0407baSopenharmony_ci         * 'ubifs_rcvry_gc_commit()' function fails, because it cannot
7983d0407baSopenharmony_ci         * find a dirty LEB which could be GC'd into LEB Y! Even LEB X
7993d0407baSopenharmony_ci         * does not match because the amount of valid nodes there does
8003d0407baSopenharmony_ci         * not fit the free space in LEB Y any more! And this is
8013d0407baSopenharmony_ci         * because of the padding node which we added to LEB Y. The
8023d0407baSopenharmony_ci         * user-visible effect of this which I once observed and
8033d0407baSopenharmony_ci         * analysed is that we cannot mount the file-system with
8043d0407baSopenharmony_ci         * -ENOSPC error.
8053d0407baSopenharmony_ci         *
8063d0407baSopenharmony_ci         * So obviously, to make sure that situation does not happen we
8073d0407baSopenharmony_ci         * should free min. I/O unit B in LEB Y completely and the last
8083d0407baSopenharmony_ci         * used min. I/O unit in LEB Y should be A. This is basically
8093d0407baSopenharmony_ci         * what the below code tries to do.
8103d0407baSopenharmony_ci         */
8113d0407baSopenharmony_ci        while (offs > min_io_unit) {
8123d0407baSopenharmony_ci            drop_last_node(sleb, &offs);
8133d0407baSopenharmony_ci        }
8143d0407baSopenharmony_ci    }
8153d0407baSopenharmony_ci
8163d0407baSopenharmony_ci    buf = sbuf + offs;
8173d0407baSopenharmony_ci    len = c->leb_size - offs;
8183d0407baSopenharmony_ci
8193d0407baSopenharmony_ci    clean_buf(c, &buf, lnum, &offs, &len);
8203d0407baSopenharmony_ci    ubifs_end_scan(c, sleb, lnum, offs);
8213d0407baSopenharmony_ci
8223d0407baSopenharmony_ci    err = fix_unclean_leb(c, sleb, start);
8233d0407baSopenharmony_ci    if (err) {
8243d0407baSopenharmony_ci        goto error;
8253d0407baSopenharmony_ci    }
8263d0407baSopenharmony_ci
8273d0407baSopenharmony_ci    return sleb;
8283d0407baSopenharmony_ci
8293d0407baSopenharmony_cicorrupted_rescan:
8303d0407baSopenharmony_ci    /* Re-scan the corrupted data with verbose messages */
8313d0407baSopenharmony_ci    ubifs_err(c, "corruption %d", ret);
8323d0407baSopenharmony_ci    ubifs_scan_a_node(c, buf, len, lnum, offs, 0);
8333d0407baSopenharmony_cicorrupted:
8343d0407baSopenharmony_ci    ubifs_scanned_corruption(c, lnum, offs, buf);
8353d0407baSopenharmony_ci    err = -EUCLEAN;
8363d0407baSopenharmony_cierror:
8373d0407baSopenharmony_ci    ubifs_err(c, "LEB %d scanning failed", lnum);
8383d0407baSopenharmony_ci    ubifs_scan_destroy(sleb);
8393d0407baSopenharmony_ci    return ERR_PTR(err);
8403d0407baSopenharmony_ci}
8413d0407baSopenharmony_ci
8423d0407baSopenharmony_ci/**
8433d0407baSopenharmony_ci * get_cs_sqnum - get commit start sequence number.
8443d0407baSopenharmony_ci * @c: UBIFS file-system description object
8453d0407baSopenharmony_ci * @lnum: LEB number of commit start node
8463d0407baSopenharmony_ci * @offs: offset of commit start node
8473d0407baSopenharmony_ci * @cs_sqnum: commit start sequence number is returned here
8483d0407baSopenharmony_ci *
8493d0407baSopenharmony_ci * This function returns %0 on success and a negative error code on failure.
8503d0407baSopenharmony_ci */
8513d0407baSopenharmony_cistatic int get_cs_sqnum(struct ubifs_info *c, int lnum, int offs, unsigned long long *cs_sqnum)
8523d0407baSopenharmony_ci{
8533d0407baSopenharmony_ci    struct ubifs_cs_node *cs_node = NULL;
8543d0407baSopenharmony_ci    int err, ret;
8553d0407baSopenharmony_ci
8563d0407baSopenharmony_ci    dbg_rcvry("at %d:%d", lnum, offs);
8573d0407baSopenharmony_ci    cs_node = kmalloc(UBIFS_CS_NODE_SZ, GFP_KERNEL);
8583d0407baSopenharmony_ci    if (!cs_node) {
8593d0407baSopenharmony_ci        return -ENOMEM;
8603d0407baSopenharmony_ci    }
8613d0407baSopenharmony_ci    if (c->leb_size - offs < UBIFS_CS_NODE_SZ) {
8623d0407baSopenharmony_ci        goto out_err;
8633d0407baSopenharmony_ci    }
8643d0407baSopenharmony_ci    err = ubifs_leb_read(c, lnum, (void *)cs_node, offs, UBIFS_CS_NODE_SZ, 0);
8653d0407baSopenharmony_ci    if (err && err != -EBADMSG) {
8663d0407baSopenharmony_ci        goto out_free;
8673d0407baSopenharmony_ci    }
8683d0407baSopenharmony_ci    ret = ubifs_scan_a_node(c, cs_node, UBIFS_CS_NODE_SZ, lnum, offs, 0);
8693d0407baSopenharmony_ci    if (ret != SCANNED_A_NODE) {
8703d0407baSopenharmony_ci        ubifs_err(c, "Not a valid node");
8713d0407baSopenharmony_ci        goto out_err;
8723d0407baSopenharmony_ci    }
8733d0407baSopenharmony_ci    if (cs_node->ch.node_type != UBIFS_CS_NODE) {
8743d0407baSopenharmony_ci        ubifs_err(c, "Not a CS node, type is %d", cs_node->ch.node_type);
8753d0407baSopenharmony_ci        goto out_err;
8763d0407baSopenharmony_ci    }
8773d0407baSopenharmony_ci    if (le64_to_cpu(cs_node->cmt_no) != c->cmt_no) {
8783d0407baSopenharmony_ci        ubifs_err(c, "CS node cmt_no %llu != current cmt_no %llu", (unsigned long long)le64_to_cpu(cs_node->cmt_no),
8793d0407baSopenharmony_ci                  c->cmt_no);
8803d0407baSopenharmony_ci        goto out_err;
8813d0407baSopenharmony_ci    }
8823d0407baSopenharmony_ci    *cs_sqnum = le64_to_cpu(cs_node->ch.sqnum);
8833d0407baSopenharmony_ci    dbg_rcvry("commit start sqnum %llu", *cs_sqnum);
8843d0407baSopenharmony_ci    kfree(cs_node);
8853d0407baSopenharmony_ci    return 0;
8863d0407baSopenharmony_ci
8873d0407baSopenharmony_ciout_err:
8883d0407baSopenharmony_ci    err = -EINVAL;
8893d0407baSopenharmony_ciout_free:
8903d0407baSopenharmony_ci    ubifs_err(c, "failed to get CS sqnum");
8913d0407baSopenharmony_ci    kfree(cs_node);
8923d0407baSopenharmony_ci    return err;
8933d0407baSopenharmony_ci}
8943d0407baSopenharmony_ci
8953d0407baSopenharmony_ci/**
8963d0407baSopenharmony_ci * ubifs_recover_log_leb - scan and recover a log LEB.
8973d0407baSopenharmony_ci * @c: UBIFS file-system description object
8983d0407baSopenharmony_ci * @lnum: LEB number
8993d0407baSopenharmony_ci * @offs: offset
9003d0407baSopenharmony_ci * @sbuf: LEB-sized buffer to use
9013d0407baSopenharmony_ci *
9023d0407baSopenharmony_ci * This function does a scan of a LEB, but caters for errors that might have
9033d0407baSopenharmony_ci * been caused by unclean reboots from which we are attempting to recover
9043d0407baSopenharmony_ci * (assume that only the last log LEB can be corrupted by an unclean reboot).
9053d0407baSopenharmony_ci *
9063d0407baSopenharmony_ci * This function returns %0 on success and a negative error code on failure.
9073d0407baSopenharmony_ci */
9083d0407baSopenharmony_cistruct ubifs_scan_leb *ubifs_recover_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf)
9093d0407baSopenharmony_ci{
9103d0407baSopenharmony_ci    struct ubifs_scan_leb *sleb;
9113d0407baSopenharmony_ci    int next_lnum;
9123d0407baSopenharmony_ci
9133d0407baSopenharmony_ci    dbg_rcvry("LEB %d", lnum);
9143d0407baSopenharmony_ci    next_lnum = lnum + 1;
9153d0407baSopenharmony_ci    if (next_lnum >= UBIFS_LOG_LNUM + c->log_lebs) {
9163d0407baSopenharmony_ci        next_lnum = UBIFS_LOG_LNUM;
9173d0407baSopenharmony_ci    }
9183d0407baSopenharmony_ci    if (next_lnum != c->ltail_lnum) {
9193d0407baSopenharmony_ci        /*
9203d0407baSopenharmony_ci         * We can only recover at the end of the log, so check that the
9213d0407baSopenharmony_ci         * next log LEB is empty or out of date.
9223d0407baSopenharmony_ci         */
9233d0407baSopenharmony_ci        sleb = ubifs_scan(c, next_lnum, 0, sbuf, 0);
9243d0407baSopenharmony_ci        if (IS_ERR(sleb)) {
9253d0407baSopenharmony_ci            return sleb;
9263d0407baSopenharmony_ci        }
9273d0407baSopenharmony_ci        if (sleb->nodes_cnt) {
9283d0407baSopenharmony_ci            struct ubifs_scan_node *snod;
9293d0407baSopenharmony_ci            unsigned long long cs_sqnum = c->cs_sqnum;
9303d0407baSopenharmony_ci
9313d0407baSopenharmony_ci            snod = list_entry(sleb->nodes.next, struct ubifs_scan_node, list);
9323d0407baSopenharmony_ci            if (cs_sqnum == 0) {
9333d0407baSopenharmony_ci                int err;
9343d0407baSopenharmony_ci
9353d0407baSopenharmony_ci                err = get_cs_sqnum(c, lnum, offs, &cs_sqnum);
9363d0407baSopenharmony_ci                if (err) {
9373d0407baSopenharmony_ci                    ubifs_scan_destroy(sleb);
9383d0407baSopenharmony_ci                    return ERR_PTR(err);
9393d0407baSopenharmony_ci                }
9403d0407baSopenharmony_ci            }
9413d0407baSopenharmony_ci            if (snod->sqnum > cs_sqnum) {
9423d0407baSopenharmony_ci                ubifs_err(c, "unrecoverable log corruption in LEB %d", lnum);
9433d0407baSopenharmony_ci                ubifs_scan_destroy(sleb);
9443d0407baSopenharmony_ci                return ERR_PTR(-EUCLEAN);
9453d0407baSopenharmony_ci            }
9463d0407baSopenharmony_ci        }
9473d0407baSopenharmony_ci        ubifs_scan_destroy(sleb);
9483d0407baSopenharmony_ci    }
9493d0407baSopenharmony_ci    return ubifs_recover_leb(c, lnum, offs, sbuf, -1);
9503d0407baSopenharmony_ci}
9513d0407baSopenharmony_ci
9523d0407baSopenharmony_ci/**
9533d0407baSopenharmony_ci * recover_head - recover a head.
9543d0407baSopenharmony_ci * @c: UBIFS file-system description object
9553d0407baSopenharmony_ci * @lnum: LEB number of head to recover
9563d0407baSopenharmony_ci * @offs: offset of head to recover
9573d0407baSopenharmony_ci * @sbuf: LEB-sized buffer to use
9583d0407baSopenharmony_ci *
9593d0407baSopenharmony_ci * This function ensures that there is no data on the flash at a head location.
9603d0407baSopenharmony_ci *
9613d0407baSopenharmony_ci * This function returns %0 on success and a negative error code on failure.
9623d0407baSopenharmony_ci */
9633d0407baSopenharmony_cistatic int recover_head(struct ubifs_info *c, int lnum, int offs, void *sbuf)
9643d0407baSopenharmony_ci{
9653d0407baSopenharmony_ci    int len = c->max_write_size, err;
9663d0407baSopenharmony_ci
9673d0407baSopenharmony_ci    if (offs + len > c->leb_size) {
9683d0407baSopenharmony_ci        len = c->leb_size - offs;
9693d0407baSopenharmony_ci    }
9703d0407baSopenharmony_ci
9713d0407baSopenharmony_ci    if (!len) {
9723d0407baSopenharmony_ci        return 0;
9733d0407baSopenharmony_ci    }
9743d0407baSopenharmony_ci
9753d0407baSopenharmony_ci    /* Read at the head location and check it is empty flash */
9763d0407baSopenharmony_ci    err = ubifs_leb_read(c, lnum, sbuf, offs, len, 1);
9773d0407baSopenharmony_ci    if (err || !is_empty(sbuf, len)) {
9783d0407baSopenharmony_ci        dbg_rcvry("cleaning head at %d:%d", lnum, offs);
9793d0407baSopenharmony_ci        if (offs == 0) {
9803d0407baSopenharmony_ci            return ubifs_leb_unmap(c, lnum);
9813d0407baSopenharmony_ci        }
9823d0407baSopenharmony_ci        err = ubifs_leb_read(c, lnum, sbuf, 0, offs, 1);
9833d0407baSopenharmony_ci        if (err) {
9843d0407baSopenharmony_ci            return err;
9853d0407baSopenharmony_ci        }
9863d0407baSopenharmony_ci        return ubifs_leb_change(c, lnum, sbuf, offs);
9873d0407baSopenharmony_ci    }
9883d0407baSopenharmony_ci
9893d0407baSopenharmony_ci    return 0;
9903d0407baSopenharmony_ci}
9913d0407baSopenharmony_ci
9923d0407baSopenharmony_ci/**
9933d0407baSopenharmony_ci * ubifs_recover_inl_heads - recover index and LPT heads.
9943d0407baSopenharmony_ci * @c: UBIFS file-system description object
9953d0407baSopenharmony_ci * @sbuf: LEB-sized buffer to use
9963d0407baSopenharmony_ci *
9973d0407baSopenharmony_ci * This function ensures that there is no data on the flash at the index and
9983d0407baSopenharmony_ci * LPT head locations.
9993d0407baSopenharmony_ci *
10003d0407baSopenharmony_ci * This deals with the recovery of a half-completed journal commit. UBIFS is
10013d0407baSopenharmony_ci * careful never to overwrite the last version of the index or the LPT. Because
10023d0407baSopenharmony_ci * the index and LPT are wandering trees, data from a half-completed commit will
10033d0407baSopenharmony_ci * not be referenced anywhere in UBIFS. The data will be either in LEBs that are
10043d0407baSopenharmony_ci * assumed to be empty and will be unmapped anyway before use, or in the index
10053d0407baSopenharmony_ci * and LPT heads.
10063d0407baSopenharmony_ci *
10073d0407baSopenharmony_ci * This function returns %0 on success and a negative error code on failure.
10083d0407baSopenharmony_ci */
10093d0407baSopenharmony_ciint ubifs_recover_inl_heads(struct ubifs_info *c, void *sbuf)
10103d0407baSopenharmony_ci{
10113d0407baSopenharmony_ci    int err;
10123d0407baSopenharmony_ci
10133d0407baSopenharmony_ci    ubifs_assert(c, !c->ro_mount || c->remounting_rw);
10143d0407baSopenharmony_ci
10153d0407baSopenharmony_ci    dbg_rcvry("checking index head at %d:%d", c->ihead_lnum, c->ihead_offs);
10163d0407baSopenharmony_ci    err = recover_head(c, c->ihead_lnum, c->ihead_offs, sbuf);
10173d0407baSopenharmony_ci    if (err) {
10183d0407baSopenharmony_ci        return err;
10193d0407baSopenharmony_ci    }
10203d0407baSopenharmony_ci
10213d0407baSopenharmony_ci    dbg_rcvry("checking LPT head at %d:%d", c->nhead_lnum, c->nhead_offs);
10223d0407baSopenharmony_ci
10233d0407baSopenharmony_ci    return recover_head(c, c->nhead_lnum, c->nhead_offs, sbuf);
10243d0407baSopenharmony_ci}
10253d0407baSopenharmony_ci
10263d0407baSopenharmony_ci/**
10273d0407baSopenharmony_ci * clean_an_unclean_leb - read and write a LEB to remove corruption.
10283d0407baSopenharmony_ci * @c: UBIFS file-system description object
10293d0407baSopenharmony_ci * @ucleb: unclean LEB information
10303d0407baSopenharmony_ci * @sbuf: LEB-sized buffer to use
10313d0407baSopenharmony_ci *
10323d0407baSopenharmony_ci * This function reads a LEB up to a point pre-determined by the mount recovery,
10333d0407baSopenharmony_ci * checks the nodes, and writes the result back to the flash, thereby cleaning
10343d0407baSopenharmony_ci * off any following corruption, or non-fatal ECC errors.
10353d0407baSopenharmony_ci *
10363d0407baSopenharmony_ci * This function returns %0 on success and a negative error code on failure.
10373d0407baSopenharmony_ci */
10383d0407baSopenharmony_cistatic int clean_an_unclean_leb(struct ubifs_info *c, struct ubifs_unclean_leb *ucleb, void *sbuf)
10393d0407baSopenharmony_ci{
10403d0407baSopenharmony_ci    int err, lnum = ucleb->lnum, offs = 0, len = ucleb->endpt, quiet = 1;
10413d0407baSopenharmony_ci    void *buf = sbuf;
10423d0407baSopenharmony_ci
10433d0407baSopenharmony_ci    dbg_rcvry("LEB %d len %d", lnum, len);
10443d0407baSopenharmony_ci
10453d0407baSopenharmony_ci    if (len == 0) {
10463d0407baSopenharmony_ci        /* Nothing to read, just unmap it */
10473d0407baSopenharmony_ci        return ubifs_leb_unmap(c, lnum);
10483d0407baSopenharmony_ci    }
10493d0407baSopenharmony_ci
10503d0407baSopenharmony_ci    err = ubifs_leb_read(c, lnum, buf, offs, len, 0);
10513d0407baSopenharmony_ci    if (err && err != -EBADMSG) {
10523d0407baSopenharmony_ci        return err;
10533d0407baSopenharmony_ci    }
10543d0407baSopenharmony_ci
10553d0407baSopenharmony_ci    while (len >= RECOVERY_EIGHT) {
10563d0407baSopenharmony_ci        int ret;
10573d0407baSopenharmony_ci
10583d0407baSopenharmony_ci        cond_resched();
10593d0407baSopenharmony_ci
10603d0407baSopenharmony_ci        /* Scan quietly until there is an error */
10613d0407baSopenharmony_ci        ret = ubifs_scan_a_node(c, buf, len, lnum, offs, quiet);
10623d0407baSopenharmony_ci        if (ret == SCANNED_A_NODE) {
10633d0407baSopenharmony_ci            /* A valid node, and not a padding node */
10643d0407baSopenharmony_ci            struct ubifs_ch *ch = buf;
10653d0407baSopenharmony_ci            int node_len;
10663d0407baSopenharmony_ci
10673d0407baSopenharmony_ci            node_len = ALIGN(le32_to_cpu(ch->len), RECOVERY_EIGHT);
10683d0407baSopenharmony_ci            offs += node_len;
10693d0407baSopenharmony_ci            buf += node_len;
10703d0407baSopenharmony_ci            len -= node_len;
10713d0407baSopenharmony_ci            continue;
10723d0407baSopenharmony_ci        }
10733d0407baSopenharmony_ci
10743d0407baSopenharmony_ci        if (ret > 0) {
10753d0407baSopenharmony_ci            /* Padding bytes or a valid padding node */
10763d0407baSopenharmony_ci            offs += ret;
10773d0407baSopenharmony_ci            buf += ret;
10783d0407baSopenharmony_ci            len -= ret;
10793d0407baSopenharmony_ci            continue;
10803d0407baSopenharmony_ci        }
10813d0407baSopenharmony_ci
10823d0407baSopenharmony_ci        if (ret == SCANNED_EMPTY_SPACE) {
10833d0407baSopenharmony_ci            ubifs_err(c, "unexpected empty space at %d:%d", lnum, offs);
10843d0407baSopenharmony_ci            return -EUCLEAN;
10853d0407baSopenharmony_ci        }
10863d0407baSopenharmony_ci
10873d0407baSopenharmony_ci        if (quiet) {
10883d0407baSopenharmony_ci            /* Redo the last scan but noisily */
10893d0407baSopenharmony_ci            quiet = 0;
10903d0407baSopenharmony_ci            continue;
10913d0407baSopenharmony_ci        }
10923d0407baSopenharmony_ci
10933d0407baSopenharmony_ci        ubifs_scanned_corruption(c, lnum, offs, buf);
10943d0407baSopenharmony_ci        return -EUCLEAN;
10953d0407baSopenharmony_ci    }
10963d0407baSopenharmony_ci
10973d0407baSopenharmony_ci    /* Pad to min_io_size */
10983d0407baSopenharmony_ci    len = ALIGN(ucleb->endpt, c->min_io_size);
10993d0407baSopenharmony_ci    if (len > ucleb->endpt) {
11003d0407baSopenharmony_ci        int pad_len = len - ALIGN(ucleb->endpt, RECOVERY_EIGHT);
11013d0407baSopenharmony_ci        if (pad_len > 0) {
11023d0407baSopenharmony_ci            buf = c->sbuf + len - pad_len;
11033d0407baSopenharmony_ci            ubifs_pad(c, buf, pad_len);
11043d0407baSopenharmony_ci        }
11053d0407baSopenharmony_ci    }
11063d0407baSopenharmony_ci
11073d0407baSopenharmony_ci    /* Write back the LEB atomically */
11083d0407baSopenharmony_ci    err = ubifs_leb_change(c, lnum, sbuf, len);
11093d0407baSopenharmony_ci    if (err) {
11103d0407baSopenharmony_ci        return err;
11113d0407baSopenharmony_ci    }
11123d0407baSopenharmony_ci
11133d0407baSopenharmony_ci    dbg_rcvry("cleaned LEB %d", lnum);
11143d0407baSopenharmony_ci
11153d0407baSopenharmony_ci    return 0;
11163d0407baSopenharmony_ci}
11173d0407baSopenharmony_ci
11183d0407baSopenharmony_ci/**
11193d0407baSopenharmony_ci * ubifs_clean_lebs - clean LEBs recovered during read-only mount.
11203d0407baSopenharmony_ci * @c: UBIFS file-system description object
11213d0407baSopenharmony_ci * @sbuf: LEB-sized buffer to use
11223d0407baSopenharmony_ci *
11233d0407baSopenharmony_ci * This function cleans a LEB identified during recovery that needs to be
11243d0407baSopenharmony_ci * written but was not because UBIFS was mounted read-only. This happens when
11253d0407baSopenharmony_ci * remounting to read-write mode.
11263d0407baSopenharmony_ci *
11273d0407baSopenharmony_ci * This function returns %0 on success and a negative error code on failure.
11283d0407baSopenharmony_ci */
11293d0407baSopenharmony_ciint ubifs_clean_lebs(struct ubifs_info *c, void *sbuf)
11303d0407baSopenharmony_ci{
11313d0407baSopenharmony_ci    dbg_rcvry("recovery");
11323d0407baSopenharmony_ci    while (!list_empty(&c->unclean_leb_list)) {
11333d0407baSopenharmony_ci        struct ubifs_unclean_leb *ucleb;
11343d0407baSopenharmony_ci        int err;
11353d0407baSopenharmony_ci
11363d0407baSopenharmony_ci        ucleb = list_entry(c->unclean_leb_list.next, struct ubifs_unclean_leb, list);
11373d0407baSopenharmony_ci        err = clean_an_unclean_leb(c, ucleb, sbuf);
11383d0407baSopenharmony_ci        if (err) {
11393d0407baSopenharmony_ci            return err;
11403d0407baSopenharmony_ci        }
11413d0407baSopenharmony_ci        list_del(&ucleb->list);
11423d0407baSopenharmony_ci        kfree(ucleb);
11433d0407baSopenharmony_ci    }
11443d0407baSopenharmony_ci    return 0;
11453d0407baSopenharmony_ci}
11463d0407baSopenharmony_ci
11473d0407baSopenharmony_ci/**
11483d0407baSopenharmony_ci * grab_empty_leb - grab an empty LEB to use as GC LEB and run commit.
11493d0407baSopenharmony_ci * @c: UBIFS file-system description object
11503d0407baSopenharmony_ci *
11513d0407baSopenharmony_ci * This is a helper function for 'ubifs_rcvry_gc_commit()' which grabs an empty
11523d0407baSopenharmony_ci * LEB to be used as GC LEB (@c->gc_lnum), and then runs the commit. Returns
11533d0407baSopenharmony_ci * zero in case of success and a negative error code in case of failure.
11543d0407baSopenharmony_ci */
11553d0407baSopenharmony_cistatic int grab_empty_leb(struct ubifs_info *c)
11563d0407baSopenharmony_ci{
11573d0407baSopenharmony_ci    int lnum, err;
11583d0407baSopenharmony_ci
11593d0407baSopenharmony_ci    /*
11603d0407baSopenharmony_ci     * Note, it is very important to first search for an empty LEB and then
11613d0407baSopenharmony_ci     * run the commit, not vice-versa. The reason is that there might be
11623d0407baSopenharmony_ci     * only one empty LEB at the moment, the one which has been the
11633d0407baSopenharmony_ci     * @c->gc_lnum just before the power cut happened. During the regular
11643d0407baSopenharmony_ci     * UBIFS operation (not now) @c->gc_lnum is marked as "taken", so no
11653d0407baSopenharmony_ci     * one but GC can grab it. But at this moment this single empty LEB is
11663d0407baSopenharmony_ci     * not marked as taken, so if we run commit - what happens? Right, the
11673d0407baSopenharmony_ci     * commit will grab it and write the index there. Remember that the
11683d0407baSopenharmony_ci     * index always expands as long as there is free space, and it only
11693d0407baSopenharmony_ci     * starts consolidating when we run out of space.
11703d0407baSopenharmony_ci     *
11713d0407baSopenharmony_ci     * IOW, if we run commit now, we might not be able to find a free LEB
11723d0407baSopenharmony_ci     * after this.
11733d0407baSopenharmony_ci     */
11743d0407baSopenharmony_ci    lnum = ubifs_find_free_leb_for_idx(c);
11753d0407baSopenharmony_ci    if (lnum < 0) {
11763d0407baSopenharmony_ci        ubifs_err(c, "could not find an empty LEB");
11773d0407baSopenharmony_ci        ubifs_dump_lprops(c);
11783d0407baSopenharmony_ci        ubifs_dump_budg(c, &c->bi);
11793d0407baSopenharmony_ci        return lnum;
11803d0407baSopenharmony_ci    }
11813d0407baSopenharmony_ci
11823d0407baSopenharmony_ci    /* Reset the index flag */
11833d0407baSopenharmony_ci    err = ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0, LPROPS_INDEX, 0);
11843d0407baSopenharmony_ci    if (err) {
11853d0407baSopenharmony_ci        return err;
11863d0407baSopenharmony_ci    }
11873d0407baSopenharmony_ci
11883d0407baSopenharmony_ci    c->gc_lnum = lnum;
11893d0407baSopenharmony_ci    dbg_rcvry("found empty LEB %d, run commit", lnum);
11903d0407baSopenharmony_ci
11913d0407baSopenharmony_ci    return ubifs_run_commit(c);
11923d0407baSopenharmony_ci}
11933d0407baSopenharmony_ci
11943d0407baSopenharmony_ci/**
11953d0407baSopenharmony_ci * ubifs_rcvry_gc_commit - recover the GC LEB number and run the commit.
11963d0407baSopenharmony_ci * @c: UBIFS file-system description object
11973d0407baSopenharmony_ci *
11983d0407baSopenharmony_ci * Out-of-place garbage collection requires always one empty LEB with which to
11993d0407baSopenharmony_ci * start garbage collection. The LEB number is recorded in c->gc_lnum and is
12003d0407baSopenharmony_ci * written to the master node on unmounting. In the case of an unclean unmount
12013d0407baSopenharmony_ci * the value of gc_lnum recorded in the master node is out of date and cannot
12023d0407baSopenharmony_ci * be used. Instead, recovery must allocate an empty LEB for this purpose.
12033d0407baSopenharmony_ci * However, there may not be enough empty space, in which case it must be
12043d0407baSopenharmony_ci * possible to GC the dirtiest LEB into the GC head LEB.
12053d0407baSopenharmony_ci *
12063d0407baSopenharmony_ci * This function also runs the commit which causes the TNC updates from
12073d0407baSopenharmony_ci * size-recovery and orphans to be written to the flash. That is important to
12083d0407baSopenharmony_ci * ensure correct replay order for subsequent mounts.
12093d0407baSopenharmony_ci *
12103d0407baSopenharmony_ci * This function returns %0 on success and a negative error code on failure.
12113d0407baSopenharmony_ci */
12123d0407baSopenharmony_ciint ubifs_rcvry_gc_commit(struct ubifs_info *c)
12133d0407baSopenharmony_ci{
12143d0407baSopenharmony_ci    struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
12153d0407baSopenharmony_ci    struct ubifs_lprops lp;
12163d0407baSopenharmony_ci    int err;
12173d0407baSopenharmony_ci
12183d0407baSopenharmony_ci    dbg_rcvry("GC head LEB %d, offs %d", wbuf->lnum, wbuf->offs);
12193d0407baSopenharmony_ci
12203d0407baSopenharmony_ci    c->gc_lnum = -1;
12213d0407baSopenharmony_ci    if (wbuf->lnum == -1 || wbuf->offs == c->leb_size) {
12223d0407baSopenharmony_ci        return grab_empty_leb(c);
12233d0407baSopenharmony_ci    }
12243d0407baSopenharmony_ci
12253d0407baSopenharmony_ci    err = ubifs_find_dirty_leb(c, &lp, wbuf->offs, RECOVERY_TWO);
12263d0407baSopenharmony_ci    if (err) {
12273d0407baSopenharmony_ci        if (err != -ENOSPC) {
12283d0407baSopenharmony_ci            return err;
12293d0407baSopenharmony_ci        }
12303d0407baSopenharmony_ci
12313d0407baSopenharmony_ci        dbg_rcvry("could not find a dirty LEB");
12323d0407baSopenharmony_ci        return grab_empty_leb(c);
12333d0407baSopenharmony_ci    }
12343d0407baSopenharmony_ci
12353d0407baSopenharmony_ci    ubifs_assert(c, !(lp.flags & LPROPS_INDEX));
12363d0407baSopenharmony_ci    ubifs_assert(c, lp.free + lp.dirty >= wbuf->offs);
12373d0407baSopenharmony_ci
12383d0407baSopenharmony_ci    /*
12393d0407baSopenharmony_ci     * We run the commit before garbage collection otherwise subsequent
12403d0407baSopenharmony_ci     * mounts will see the GC and orphan deletion in a different order.
12413d0407baSopenharmony_ci     */
12423d0407baSopenharmony_ci    dbg_rcvry("committing");
12433d0407baSopenharmony_ci    err = ubifs_run_commit(c);
12443d0407baSopenharmony_ci    if (err) {
12453d0407baSopenharmony_ci        return err;
12463d0407baSopenharmony_ci    }
12473d0407baSopenharmony_ci
12483d0407baSopenharmony_ci    dbg_rcvry("GC'ing LEB %d", lp.lnum);
12493d0407baSopenharmony_ci    mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
12503d0407baSopenharmony_ci    err = ubifs_garbage_collect_leb(c, &lp);
12513d0407baSopenharmony_ci    if (err >= 0) {
12523d0407baSopenharmony_ci        int err2 = ubifs_wbuf_sync_nolock(wbuf);
12533d0407baSopenharmony_ci        if (err2) {
12543d0407baSopenharmony_ci            err = err2;
12553d0407baSopenharmony_ci        }
12563d0407baSopenharmony_ci    }
12573d0407baSopenharmony_ci    mutex_unlock(&wbuf->io_mutex);
12583d0407baSopenharmony_ci    if (err < 0) {
12593d0407baSopenharmony_ci        ubifs_err(c, "GC failed, error %d", err);
12603d0407baSopenharmony_ci        if (err == -EAGAIN) {
12613d0407baSopenharmony_ci            err = -EINVAL;
12623d0407baSopenharmony_ci        }
12633d0407baSopenharmony_ci        return err;
12643d0407baSopenharmony_ci    }
12653d0407baSopenharmony_ci
12663d0407baSopenharmony_ci    ubifs_assert(c, err == LEB_RETAINED);
12673d0407baSopenharmony_ci    if (err != LEB_RETAINED) {
12683d0407baSopenharmony_ci        return -EINVAL;
12693d0407baSopenharmony_ci    }
12703d0407baSopenharmony_ci
12713d0407baSopenharmony_ci    err = ubifs_leb_unmap(c, c->gc_lnum);
12723d0407baSopenharmony_ci    if (err) {
12733d0407baSopenharmony_ci        return err;
12743d0407baSopenharmony_ci    }
12753d0407baSopenharmony_ci
12763d0407baSopenharmony_ci    dbg_rcvry("allocated LEB %d for GC", lp.lnum);
12773d0407baSopenharmony_ci    return 0;
12783d0407baSopenharmony_ci}
12793d0407baSopenharmony_ci
12803d0407baSopenharmony_ci/**
12813d0407baSopenharmony_ci * struct size_entry - inode size information for recovery.
12823d0407baSopenharmony_ci * @rb: link in the RB-tree of sizes
12833d0407baSopenharmony_ci * @inum: inode number
12843d0407baSopenharmony_ci * @i_size: size on inode
12853d0407baSopenharmony_ci * @d_size: maximum size based on data nodes
12863d0407baSopenharmony_ci * @exists: indicates whether the inode exists
12873d0407baSopenharmony_ci * @inode: inode if pinned in memory awaiting rw mode to fix it
12883d0407baSopenharmony_ci */
12893d0407baSopenharmony_cistruct size_entry {
12903d0407baSopenharmony_ci    struct rb_node rb;
12913d0407baSopenharmony_ci    ino_t inum;
12923d0407baSopenharmony_ci    loff_t i_size;
12933d0407baSopenharmony_ci    loff_t d_size;
12943d0407baSopenharmony_ci    int exists;
12953d0407baSopenharmony_ci    struct inode *inode;
12963d0407baSopenharmony_ci};
12973d0407baSopenharmony_ci
12983d0407baSopenharmony_ci/**
12993d0407baSopenharmony_ci * add_ino - add an entry to the size tree.
13003d0407baSopenharmony_ci * @c: UBIFS file-system description object
13013d0407baSopenharmony_ci * @inum: inode number
13023d0407baSopenharmony_ci * @i_size: size on inode
13033d0407baSopenharmony_ci * @d_size: maximum size based on data nodes
13043d0407baSopenharmony_ci * @exists: indicates whether the inode exists
13053d0407baSopenharmony_ci */
13063d0407baSopenharmony_cistatic int add_ino(struct ubifs_info *c, ino_t inum, loff_t i_size, loff_t d_size, int exists)
13073d0407baSopenharmony_ci{
13083d0407baSopenharmony_ci    struct rb_node **p = &c->size_tree.rb_node, *parent = NULL;
13093d0407baSopenharmony_ci    struct size_entry *e;
13103d0407baSopenharmony_ci
13113d0407baSopenharmony_ci    while (*p) {
13123d0407baSopenharmony_ci        parent = *p;
13133d0407baSopenharmony_ci        e = rb_entry(parent, struct size_entry, rb);
13143d0407baSopenharmony_ci        if (inum < e->inum) {
13153d0407baSopenharmony_ci            p = &(*p)->rb_left;
13163d0407baSopenharmony_ci        } else {
13173d0407baSopenharmony_ci            p = &(*p)->rb_right;
13183d0407baSopenharmony_ci        }
13193d0407baSopenharmony_ci    }
13203d0407baSopenharmony_ci
13213d0407baSopenharmony_ci    e = kzalloc(sizeof(struct size_entry), GFP_KERNEL);
13223d0407baSopenharmony_ci    if (!e) {
13233d0407baSopenharmony_ci        return -ENOMEM;
13243d0407baSopenharmony_ci    }
13253d0407baSopenharmony_ci
13263d0407baSopenharmony_ci    e->inum = inum;
13273d0407baSopenharmony_ci    e->i_size = i_size;
13283d0407baSopenharmony_ci    e->d_size = d_size;
13293d0407baSopenharmony_ci    e->exists = exists;
13303d0407baSopenharmony_ci
13313d0407baSopenharmony_ci    rb_link_node(&e->rb, parent, p);
13323d0407baSopenharmony_ci    rb_insert_color(&e->rb, &c->size_tree);
13333d0407baSopenharmony_ci
13343d0407baSopenharmony_ci    return 0;
13353d0407baSopenharmony_ci}
13363d0407baSopenharmony_ci
13373d0407baSopenharmony_ci/**
13383d0407baSopenharmony_ci * find_ino - find an entry on the size tree.
13393d0407baSopenharmony_ci * @c: UBIFS file-system description object
13403d0407baSopenharmony_ci * @inum: inode number
13413d0407baSopenharmony_ci */
13423d0407baSopenharmony_cistatic struct size_entry *find_ino(struct ubifs_info *c, ino_t inum)
13433d0407baSopenharmony_ci{
13443d0407baSopenharmony_ci    struct rb_node *p = c->size_tree.rb_node;
13453d0407baSopenharmony_ci    struct size_entry *e;
13463d0407baSopenharmony_ci
13473d0407baSopenharmony_ci    while (p) {
13483d0407baSopenharmony_ci        e = rb_entry(p, struct size_entry, rb);
13493d0407baSopenharmony_ci        if (inum < e->inum) {
13503d0407baSopenharmony_ci            p = p->rb_left;
13513d0407baSopenharmony_ci        } else if (inum > e->inum) {
13523d0407baSopenharmony_ci            p = p->rb_right;
13533d0407baSopenharmony_ci        } else {
13543d0407baSopenharmony_ci            return e;
13553d0407baSopenharmony_ci        }
13563d0407baSopenharmony_ci    }
13573d0407baSopenharmony_ci    return NULL;
13583d0407baSopenharmony_ci}
13593d0407baSopenharmony_ci
13603d0407baSopenharmony_ci/**
13613d0407baSopenharmony_ci * remove_ino - remove an entry from the size tree.
13623d0407baSopenharmony_ci * @c: UBIFS file-system description object
13633d0407baSopenharmony_ci * @inum: inode number
13643d0407baSopenharmony_ci */
13653d0407baSopenharmony_cistatic void remove_ino(struct ubifs_info *c, ino_t inum)
13663d0407baSopenharmony_ci{
13673d0407baSopenharmony_ci    struct size_entry *e = find_ino(c, inum);
13683d0407baSopenharmony_ci
13693d0407baSopenharmony_ci    if (!e) {
13703d0407baSopenharmony_ci        return;
13713d0407baSopenharmony_ci    }
13723d0407baSopenharmony_ci    rb_erase(&e->rb, &c->size_tree);
13733d0407baSopenharmony_ci    kfree(e);
13743d0407baSopenharmony_ci}
13753d0407baSopenharmony_ci
13763d0407baSopenharmony_ci/**
13773d0407baSopenharmony_ci * ubifs_destroy_size_tree - free resources related to the size tree.
13783d0407baSopenharmony_ci * @c: UBIFS file-system description object
13793d0407baSopenharmony_ci */
13803d0407baSopenharmony_civoid ubifs_destroy_size_tree(struct ubifs_info *c)
13813d0407baSopenharmony_ci{
13823d0407baSopenharmony_ci    struct size_entry *e, *n;
13833d0407baSopenharmony_ci
13843d0407baSopenharmony_ci    rbtree_postorder_for_each_entry_safe(e, n, &c->size_tree, rb)
13853d0407baSopenharmony_ci    {
13863d0407baSopenharmony_ci        iput(e->inode);
13873d0407baSopenharmony_ci        kfree(e);
13883d0407baSopenharmony_ci    }
13893d0407baSopenharmony_ci
13903d0407baSopenharmony_ci    c->size_tree = RB_ROOT;
13913d0407baSopenharmony_ci}
13923d0407baSopenharmony_ci
13933d0407baSopenharmony_ci/**
13943d0407baSopenharmony_ci * ubifs_recover_size_accum - accumulate inode sizes for recovery.
13953d0407baSopenharmony_ci * @c: UBIFS file-system description object
13963d0407baSopenharmony_ci * @key: node key
13973d0407baSopenharmony_ci * @deletion: node is for a deletion
13983d0407baSopenharmony_ci * @new_size: inode size
13993d0407baSopenharmony_ci *
14003d0407baSopenharmony_ci * This function has two purposes:
14013d0407baSopenharmony_ci *     1) to ensure there are no data nodes that fall outside the inode size
14023d0407baSopenharmony_ci *     2) to ensure there are no data nodes for inodes that do not exist
14033d0407baSopenharmony_ci * To accomplish those purposes, a rb-tree is constructed containing an entry
14043d0407baSopenharmony_ci * for each inode number in the journal that has not been deleted, and recording
14053d0407baSopenharmony_ci * the size from the inode node, the maximum size of any data node (also altered
14063d0407baSopenharmony_ci * by truncations) and a flag indicating a inode number for which no inode node
14073d0407baSopenharmony_ci * was present in the journal.
14083d0407baSopenharmony_ci *
14093d0407baSopenharmony_ci * Note that there is still the possibility that there are data nodes that have
14103d0407baSopenharmony_ci * been committed that are beyond the inode size, however the only way to find
14113d0407baSopenharmony_ci * them would be to scan the entire index. Alternatively, some provision could
14123d0407baSopenharmony_ci * be made to record the size of inodes at the start of commit, which would seem
14133d0407baSopenharmony_ci * very cumbersome for a scenario that is quite unlikely and the only negative
14143d0407baSopenharmony_ci * consequence of which is wasted space.
14153d0407baSopenharmony_ci *
14163d0407baSopenharmony_ci * This functions returns %0 on success and a negative error code on failure.
14173d0407baSopenharmony_ci */
14183d0407baSopenharmony_ciint ubifs_recover_size_accum(struct ubifs_info *c, union ubifs_key *key, int deletion, loff_t new_size)
14193d0407baSopenharmony_ci{
14203d0407baSopenharmony_ci    ino_t inum = key_inum(c, key);
14213d0407baSopenharmony_ci    struct size_entry *e;
14223d0407baSopenharmony_ci    int err;
14233d0407baSopenharmony_ci
14243d0407baSopenharmony_ci    switch (key_type(c, key)) {
14253d0407baSopenharmony_ci        case UBIFS_INO_KEY:
14263d0407baSopenharmony_ci            if (deletion) {
14273d0407baSopenharmony_ci                remove_ino(c, inum);
14283d0407baSopenharmony_ci            } else {
14293d0407baSopenharmony_ci                e = find_ino(c, inum);
14303d0407baSopenharmony_ci                if (e) {
14313d0407baSopenharmony_ci                    e->i_size = new_size;
14323d0407baSopenharmony_ci                    e->exists = 1;
14333d0407baSopenharmony_ci                } else {
14343d0407baSopenharmony_ci                    err = add_ino(c, inum, new_size, 0, 1);
14353d0407baSopenharmony_ci                    if (err) {
14363d0407baSopenharmony_ci                        return err;
14373d0407baSopenharmony_ci                    }
14383d0407baSopenharmony_ci                }
14393d0407baSopenharmony_ci            }
14403d0407baSopenharmony_ci            break;
14413d0407baSopenharmony_ci        case UBIFS_DATA_KEY:
14423d0407baSopenharmony_ci            e = find_ino(c, inum);
14433d0407baSopenharmony_ci            if (e) {
14443d0407baSopenharmony_ci                if (new_size > e->d_size) {
14453d0407baSopenharmony_ci                    e->d_size = new_size;
14463d0407baSopenharmony_ci                }
14473d0407baSopenharmony_ci            } else {
14483d0407baSopenharmony_ci                err = add_ino(c, inum, 0, new_size, 0);
14493d0407baSopenharmony_ci                if (err) {
14503d0407baSopenharmony_ci                    return err;
14513d0407baSopenharmony_ci                }
14523d0407baSopenharmony_ci            }
14533d0407baSopenharmony_ci            break;
14543d0407baSopenharmony_ci        case UBIFS_TRUN_KEY:
14553d0407baSopenharmony_ci            e = find_ino(c, inum);
14563d0407baSopenharmony_ci            if (e) {
14573d0407baSopenharmony_ci                e->d_size = new_size;
14583d0407baSopenharmony_ci            }
14593d0407baSopenharmony_ci            break;
14603d0407baSopenharmony_ci        default:
14613d0407baSopenharmony_ci            break;
14623d0407baSopenharmony_ci    }
14633d0407baSopenharmony_ci    return 0;
14643d0407baSopenharmony_ci}
14653d0407baSopenharmony_ci
14663d0407baSopenharmony_ci/**
14673d0407baSopenharmony_ci * fix_size_in_place - fix inode size in place on flash.
14683d0407baSopenharmony_ci * @c: UBIFS file-system description object
14693d0407baSopenharmony_ci * @e: inode size information for recovery
14703d0407baSopenharmony_ci */
14713d0407baSopenharmony_cistatic int fix_size_in_place(struct ubifs_info *c, struct size_entry *e)
14723d0407baSopenharmony_ci{
14733d0407baSopenharmony_ci    struct ubifs_ino_node *ino = c->sbuf;
14743d0407baSopenharmony_ci    unsigned char *p;
14753d0407baSopenharmony_ci    union ubifs_key key;
14763d0407baSopenharmony_ci    int err, lnum, offs, len;
14773d0407baSopenharmony_ci    loff_t i_size;
14783d0407baSopenharmony_ci    uint32_t crc;
14793d0407baSopenharmony_ci
14803d0407baSopenharmony_ci    /* Locate the inode node LEB number and offset */
14813d0407baSopenharmony_ci    ino_key_init(c, &key, e->inum);
14823d0407baSopenharmony_ci    err = ubifs_tnc_locate(c, &key, ino, &lnum, &offs);
14833d0407baSopenharmony_ci    if (err) {
14843d0407baSopenharmony_ci        goto out;
14853d0407baSopenharmony_ci    }
14863d0407baSopenharmony_ci    /*
14873d0407baSopenharmony_ci     * If the size recorded on the inode node is greater than the size that
14883d0407baSopenharmony_ci     * was calculated from nodes in the journal then don't change the inode.
14893d0407baSopenharmony_ci     */
14903d0407baSopenharmony_ci    i_size = le64_to_cpu(ino->size);
14913d0407baSopenharmony_ci    if (i_size >= e->d_size) {
14923d0407baSopenharmony_ci        return 0;
14933d0407baSopenharmony_ci    }
14943d0407baSopenharmony_ci    /* Read the LEB */
14953d0407baSopenharmony_ci    err = ubifs_leb_read(c, lnum, c->sbuf, 0, c->leb_size, 1);
14963d0407baSopenharmony_ci    if (err) {
14973d0407baSopenharmony_ci        goto out;
14983d0407baSopenharmony_ci    }
14993d0407baSopenharmony_ci    /* Change the size field and recalculate the CRC */
15003d0407baSopenharmony_ci    ino = c->sbuf + offs;
15013d0407baSopenharmony_ci    ino->size = cpu_to_le64(e->d_size);
15023d0407baSopenharmony_ci    len = le32_to_cpu(ino->ch.len);
15033d0407baSopenharmony_ci    crc = crc32(UBIFS_CRC32_INIT, (void *)ino + RECOVERY_EIGHT, len - RECOVERY_EIGHT);
15043d0407baSopenharmony_ci    ino->ch.crc = cpu_to_le32(crc);
15053d0407baSopenharmony_ci    /* Work out where data in the LEB ends and free space begins */
15063d0407baSopenharmony_ci    p = c->sbuf;
15073d0407baSopenharmony_ci    len = c->leb_size - 1;
15083d0407baSopenharmony_ci    while (p[len] == 0xff) {
15093d0407baSopenharmony_ci        len -= 1;
15103d0407baSopenharmony_ci    }
15113d0407baSopenharmony_ci    len = ALIGN(len + 1, c->min_io_size);
15123d0407baSopenharmony_ci    /* Atomically write the fixed LEB back again */
15133d0407baSopenharmony_ci    err = ubifs_leb_change(c, lnum, c->sbuf, len);
15143d0407baSopenharmony_ci    if (err) {
15153d0407baSopenharmony_ci        goto out;
15163d0407baSopenharmony_ci    }
15173d0407baSopenharmony_ci    dbg_rcvry("inode %lu at %d:%d size %lld -> %lld", (unsigned long)e->inum, lnum, offs, i_size, e->d_size);
15183d0407baSopenharmony_ci    return 0;
15193d0407baSopenharmony_ci
15203d0407baSopenharmony_ciout:
15213d0407baSopenharmony_ci    ubifs_warn(c, "inode %lu failed to fix size %lld -> %lld error %d", (unsigned long)e->inum, e->i_size, e->d_size,
15223d0407baSopenharmony_ci               err);
15233d0407baSopenharmony_ci    return err;
15243d0407baSopenharmony_ci}
15253d0407baSopenharmony_ci
15263d0407baSopenharmony_ci/**
15273d0407baSopenharmony_ci * inode_fix_size - fix inode size
15283d0407baSopenharmony_ci * @c: UBIFS file-system description object
15293d0407baSopenharmony_ci * @e: inode size information for recovery
15303d0407baSopenharmony_ci */
15313d0407baSopenharmony_cistatic int inode_fix_size(struct ubifs_info *c, struct size_entry *e)
15323d0407baSopenharmony_ci{
15333d0407baSopenharmony_ci    struct inode *inode;
15343d0407baSopenharmony_ci    struct ubifs_inode *ui;
15353d0407baSopenharmony_ci    int err;
15363d0407baSopenharmony_ci
15373d0407baSopenharmony_ci    if (c->ro_mount) {
15383d0407baSopenharmony_ci        ubifs_assert(c, !e->inode);
15393d0407baSopenharmony_ci    }
15403d0407baSopenharmony_ci
15413d0407baSopenharmony_ci    if (e->inode) {
15423d0407baSopenharmony_ci        /* Remounting rw, pick up inode we stored earlier */
15433d0407baSopenharmony_ci        inode = e->inode;
15443d0407baSopenharmony_ci    } else {
15453d0407baSopenharmony_ci        inode = ubifs_iget(c->vfs_sb, e->inum);
15463d0407baSopenharmony_ci        if (IS_ERR(inode)) {
15473d0407baSopenharmony_ci            return PTR_ERR(inode);
15483d0407baSopenharmony_ci        }
15493d0407baSopenharmony_ci
15503d0407baSopenharmony_ci        if (inode->i_size >= e->d_size) {
15513d0407baSopenharmony_ci            /*
15523d0407baSopenharmony_ci             * The original inode in the index already has a size
15533d0407baSopenharmony_ci             * big enough, nothing to do
15543d0407baSopenharmony_ci             */
15553d0407baSopenharmony_ci            iput(inode);
15563d0407baSopenharmony_ci            return 0;
15573d0407baSopenharmony_ci        }
15583d0407baSopenharmony_ci
15593d0407baSopenharmony_ci        dbg_rcvry("ino %lu size %lld -> %lld", (unsigned long)e->inum, inode->i_size, e->d_size);
15603d0407baSopenharmony_ci
15613d0407baSopenharmony_ci        ui = ubifs_inode(inode);
15623d0407baSopenharmony_ci
15633d0407baSopenharmony_ci        inode->i_size = e->d_size;
15643d0407baSopenharmony_ci        ui->ui_size = e->d_size;
15653d0407baSopenharmony_ci        ui->synced_i_size = e->d_size;
15663d0407baSopenharmony_ci
15673d0407baSopenharmony_ci        e->inode = inode;
15683d0407baSopenharmony_ci    }
15693d0407baSopenharmony_ci
15703d0407baSopenharmony_ci    /*
15713d0407baSopenharmony_ci     * In readonly mode just keep the inode pinned in memory until we go
15723d0407baSopenharmony_ci     * readwrite. In readwrite mode write the inode to the journal with the
15733d0407baSopenharmony_ci     * fixed size.
15743d0407baSopenharmony_ci     */
15753d0407baSopenharmony_ci    if (c->ro_mount) {
15763d0407baSopenharmony_ci        return 0;
15773d0407baSopenharmony_ci    }
15783d0407baSopenharmony_ci
15793d0407baSopenharmony_ci    err = ubifs_jnl_write_inode(c, inode);
15803d0407baSopenharmony_ci
15813d0407baSopenharmony_ci    iput(inode);
15823d0407baSopenharmony_ci
15833d0407baSopenharmony_ci    if (err) {
15843d0407baSopenharmony_ci        return err;
15853d0407baSopenharmony_ci    }
15863d0407baSopenharmony_ci
15873d0407baSopenharmony_ci    rb_erase(&e->rb, &c->size_tree);
15883d0407baSopenharmony_ci    kfree(e);
15893d0407baSopenharmony_ci
15903d0407baSopenharmony_ci    return 0;
15913d0407baSopenharmony_ci}
15923d0407baSopenharmony_ci
15933d0407baSopenharmony_ci/**
15943d0407baSopenharmony_ci * ubifs_recover_size - recover inode size.
15953d0407baSopenharmony_ci * @c: UBIFS file-system description object
15963d0407baSopenharmony_ci * @in_place: If true, do a in-place size fixup
15973d0407baSopenharmony_ci *
15983d0407baSopenharmony_ci * This function attempts to fix inode size discrepancies identified by the
15993d0407baSopenharmony_ci * 'ubifs_recover_size_accum()' function.
16003d0407baSopenharmony_ci *
16013d0407baSopenharmony_ci * This functions returns %0 on success and a negative error code on failure.
16023d0407baSopenharmony_ci */
16033d0407baSopenharmony_ciint ubifs_recover_size(struct ubifs_info *c, bool in_place)
16043d0407baSopenharmony_ci{
16053d0407baSopenharmony_ci    struct rb_node *this = rb_first(&c->size_tree);
16063d0407baSopenharmony_ci
16073d0407baSopenharmony_ci    while (this) {
16083d0407baSopenharmony_ci        struct size_entry *e;
16093d0407baSopenharmony_ci        int err;
16103d0407baSopenharmony_ci
16113d0407baSopenharmony_ci        e = rb_entry(this, struct size_entry, rb);
16123d0407baSopenharmony_ci
16133d0407baSopenharmony_ci        this = rb_next(this);
16143d0407baSopenharmony_ci
16153d0407baSopenharmony_ci        if (!e->exists) {
16163d0407baSopenharmony_ci            union ubifs_key key;
16173d0407baSopenharmony_ci
16183d0407baSopenharmony_ci            ino_key_init(c, &key, e->inum);
16193d0407baSopenharmony_ci            err = ubifs_tnc_lookup(c, &key, c->sbuf);
16203d0407baSopenharmony_ci            if (err && err != -ENOENT) {
16213d0407baSopenharmony_ci                return err;
16223d0407baSopenharmony_ci            }
16233d0407baSopenharmony_ci            if (err == -ENOENT) {
16243d0407baSopenharmony_ci                /* Remove data nodes that have no inode */
16253d0407baSopenharmony_ci                dbg_rcvry("removing ino %lu", (unsigned long)e->inum);
16263d0407baSopenharmony_ci                err = ubifs_tnc_remove_ino(c, e->inum);
16273d0407baSopenharmony_ci                if (err) {
16283d0407baSopenharmony_ci                    return err;
16293d0407baSopenharmony_ci                }
16303d0407baSopenharmony_ci            } else {
16313d0407baSopenharmony_ci                struct ubifs_ino_node *ino = c->sbuf;
16323d0407baSopenharmony_ci
16333d0407baSopenharmony_ci                e->exists = 1;
16343d0407baSopenharmony_ci                e->i_size = le64_to_cpu(ino->size);
16353d0407baSopenharmony_ci            }
16363d0407baSopenharmony_ci        }
16373d0407baSopenharmony_ci
16383d0407baSopenharmony_ci        if (e->exists && e->i_size < e->d_size) {
16393d0407baSopenharmony_ci            ubifs_assert(c, !(c->ro_mount && in_place));
16403d0407baSopenharmony_ci
16413d0407baSopenharmony_ci            /*
16423d0407baSopenharmony_ci             * We found data that is outside the found inode size,
16433d0407baSopenharmony_ci             * fixup the inode size
16443d0407baSopenharmony_ci             */
16453d0407baSopenharmony_ci
16463d0407baSopenharmony_ci            if (in_place) {
16473d0407baSopenharmony_ci                err = fix_size_in_place(c, e);
16483d0407baSopenharmony_ci                if (err) {
16493d0407baSopenharmony_ci                    return err;
16503d0407baSopenharmony_ci                }
16513d0407baSopenharmony_ci                iput(e->inode);
16523d0407baSopenharmony_ci            } else {
16533d0407baSopenharmony_ci                err = inode_fix_size(c, e);
16543d0407baSopenharmony_ci                if (err) {
16553d0407baSopenharmony_ci                    return err;
16563d0407baSopenharmony_ci                }
16573d0407baSopenharmony_ci                continue;
16583d0407baSopenharmony_ci            }
16593d0407baSopenharmony_ci        }
16603d0407baSopenharmony_ci
16613d0407baSopenharmony_ci        rb_erase(&e->rb, &c->size_tree);
16623d0407baSopenharmony_ci        kfree(e);
16633d0407baSopenharmony_ci    }
16643d0407baSopenharmony_ci
16653d0407baSopenharmony_ci    return 0;
16663d0407baSopenharmony_ci}
1667