| /* |
| * 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; |
| } |
| |