/* * Copyright (c) International Business Machines Corp., 2006, 2007 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Author: Drake Dowsett, dowsett@de.ibm.com * Contact: Andreas Arnez, arnez@de.ibm.com */ /* see eb_chain.h */ #include <errno.h> #include <stdlib.h> #include <string.h> #include <mtd_swab.h> #include "unubi_analyze.h" #include "crc32.h" #define COPY(dst, src) \ do { \ dst = malloc(sizeof(*dst)); \ if (dst == NULL) \ return -ENOMEM; \ memcpy(dst, src, sizeof(*dst)); \ } while (0) /** * inserts an eb_info into the chain starting at head, then searching * linearly for the correct position; * new should contain valid vid and ec headers and the data_crc should * already have been checked before insertion, otherwise the chain * could be have un an undesired manner; * returns -ENOMEM if alloc fails, otherwise SHOULD always return 0, * if not, the code reached the last line and returned -EAGAIN, * meaning there is a bug or a case not being handled here; **/ int eb_chain_insert(struct eb_info **head, struct eb_info *new) { uint32_t vol, num, ver; uint32_t new_vol, new_num, new_ver; struct eb_info *prev, *cur, *hist, *ins; struct eb_info **prev_ptr; if ((head == NULL) || (new == NULL)) return 0; if (*head == NULL) { COPY(*head, new); (*head)->next = NULL; return 0; } new_vol = be32_to_cpu(new->vid.vol_id); new_num = be32_to_cpu(new->vid.lnum); new_ver = be32_to_cpu(new->vid.leb_ver); /** TRAVERSE HORIZONTALY **/ cur = *head; prev = NULL; /* traverse until vol_id/lnum align */ vol = be32_to_cpu(cur->vid.vol_id); num = be32_to_cpu(cur->vid.lnum); while ((new_vol > vol) || ((new_vol == vol) && (new_num > num))) { /* insert new at end of chain */ if (cur->next == NULL) { COPY(ins, new); ins->next = NULL; cur->next = ins; return 0; } prev = cur; cur = cur->next; vol = be32_to_cpu(cur->vid.vol_id); num = be32_to_cpu(cur->vid.lnum); } if (prev == NULL) prev_ptr = head; else prev_ptr = &(prev->next); /* insert new into the middle of chain */ if ((new_vol != vol) || (new_num != num)) { COPY(ins, new); ins->next = cur; *prev_ptr = ins; return 0; } /** TRAVERSE VERTICALY **/ hist = cur; prev = NULL; /* traverse until versions align */ ver = be32_to_cpu(cur->vid.leb_ver); while (new_ver < ver) { /* insert new at bottom of history */ if (hist->older == NULL) { COPY(ins, new); ins->next = NULL; ins->older = NULL; hist->older = ins; return 0; } prev = hist; hist = hist->older; ver = be32_to_cpu(hist->vid.leb_ver); } if (prev == NULL) { /* replace active version */ COPY(ins, new); ins->next = hist->next; *prev_ptr = ins; /* place cur in vertical histroy */ ins->older = hist; hist->next = NULL; return 0; } /* insert between versions, beneath active version */ COPY(ins, new); ins->next = NULL; ins->older = prev->older; prev->older = ins; return 0; } /** * sets the pointer at pos to the position of the first entry in the chain * with of vol_id and, if given, with the same lnum as *lnum; * if there is no entry in the chain, then *pos is NULL on return; * always returns 0; **/ int eb_chain_position(struct eb_info **head, uint32_t vol_id, uint32_t *lnum, struct eb_info **pos) { uint32_t vol, num; struct eb_info *cur; if ((head == NULL) || (*head == NULL) || (pos == NULL)) return 0; *pos = NULL; cur = *head; while (cur != NULL) { vol = be32_to_cpu(cur->vid.vol_id); num = be32_to_cpu(cur->vid.lnum); if ((vol_id == vol) && ((lnum == NULL) || (*lnum == num))) { *pos = cur; return 0; } cur = cur->next; } return 0; } /** * prints to stream, the vol_id, lnum and leb_ver for each entry in the * chain, starting at head; * this is intended for debuging purposes; * always returns 0; * * FIXME I do not like the double list traversion ... **/ int eb_chain_print(FILE* stream, struct eb_info *head) { struct eb_info *cur; if (stream == NULL) stream = stdout; if (head == NULL) { fprintf(stream, "EMPTY\n"); return 0; } /* 012345678012345678012345678012301230123 0123 01234567 0123457 01234567*/ fprintf(stream, "VOL_ID LNUM LEB_VER EC VID DAT PBLK PADDR DSIZE EC\n"); cur = head; while (cur != NULL) { struct eb_info *hist; fprintf(stream, "%08x %-8u %08x %-4s%-4s", be32_to_cpu(cur->vid.vol_id), be32_to_cpu(cur->vid.lnum), be32_to_cpu(cur->vid.leb_ver), cur->ec_crc_ok ? "ok":"bad", cur->vid_crc_ok ? "ok":"bad"); if (cur->vid.vol_type == UBI_VID_STATIC) fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"bad"); else fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"ign"); fprintf(stream, " %-4d %08x %-8u %-8llu\n", cur->phys_block, cur->phys_addr, be32_to_cpu(cur->vid.data_size), (unsigned long long)be64_to_cpu(cur->ec.ec)); hist = cur->older; while (hist != NULL) { fprintf(stream, "%08x %-8u %08x %-4s%-4s", be32_to_cpu(hist->vid.vol_id), be32_to_cpu(hist->vid.lnum), be32_to_cpu(hist->vid.leb_ver), hist->ec_crc_ok ? "ok":"bad", hist->vid_crc_ok ? "ok":"bad"); if (hist->vid.vol_type == UBI_VID_STATIC) fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"bad"); else fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"ign"); fprintf(stream, " %-4d %08x %-8u %-8llu (*)\n", hist->phys_block, hist->phys_addr, be32_to_cpu(hist->vid.data_size), (unsigned long long)be64_to_cpu(hist->ec.ec)); hist = hist->older; } cur = cur->next; } return 0; } /** * frees the memory of the entire chain, starting at head; * head will be NULL on return; * always returns 0; **/ int eb_chain_destroy(struct eb_info **head) { if (head == NULL) return 0; while (*head != NULL) { struct eb_info *cur; struct eb_info *hist; cur = *head; *head = (*head)->next; hist = cur->older; while (hist != NULL) { struct eb_info *temp; temp = hist; hist = hist->older; free(temp); } free(cur); } return 0; }