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