diff options
Diffstat (limited to 'ubi-utils/src/eb_chain.c')
-rw-r--r-- | ubi-utils/src/eb_chain.c | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/ubi-utils/src/eb_chain.c b/ubi-utils/src/eb_chain.c new file mode 100644 index 0000000..501a838 --- /dev/null +++ b/ubi-utils/src/eb_chain.c @@ -0,0 +1,287 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * 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 "eb_chain.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(eb_info_t *head, eb_info_t new) +{ + uint32_t vol, num, ver; + uint32_t new_vol, new_num, new_ver; + eb_info_t prev, cur, hist, ins; + eb_info_t *prev_ptr; + + if ((head == NULL) || (new == NULL)) + return 0; + + if (*head == NULL) + { + COPY(*head, new); + (*head)->next = NULL; + return 0; + } + + new_vol = ubi32_to_cpu(new->inner.vol_id); + new_num = ubi32_to_cpu(new->inner.lnum); + new_ver = ubi32_to_cpu(new->inner.leb_ver); + + /** TRAVERSE HORIZONTALY **/ + + cur = *head; + prev = NULL; + + /* traverse until vol_id/lnum align */ + vol = ubi32_to_cpu(cur->inner.vol_id); + num = ubi32_to_cpu(cur->inner.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 = ubi32_to_cpu(cur->inner.vol_id); + num = ubi32_to_cpu(cur->inner.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 = ubi32_to_cpu(cur->inner.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 = ubi32_to_cpu(hist->inner.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; + } + else + { + /* insert between versions, beneath active version */ + COPY(ins, new); + ins->next = NULL; + ins->older = prev->older; + prev->older = ins; + return 0; + } + + /* logically impossible to reach this point... hopefully */ + return -EAGAIN; +} + + +/** + * 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(eb_info_t *head, uint32_t vol_id, uint32_t *lnum, + eb_info_t *pos) +{ + uint32_t vol, num; + eb_info_t cur; + + if ((head == NULL) || (*head == NULL) || (pos == NULL)) + return 0; + + *pos = NULL; + + cur = *head; + while (cur != NULL) + { + vol = ubi32_to_cpu(cur->inner.vol_id); + num = ubi32_to_cpu(cur->inner.lnum); + + if (vol_id == vol) + if ((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; + **/ +int +eb_chain_print(FILE* stream, eb_info_t *head) +{ + eb_info_t cur; + + if (head == NULL) + return 0; + + if (stream == NULL) + stream = stdout; + + if (*head == NULL) + { + fprintf(stream, "EMPTY\n"); + return 0; + } + + cur = *head; + while (cur != NULL) + { + eb_info_t hist; + + fprintf(stream, " VOL %4u-%04u | VER 0x%8x\n", + ubi32_to_cpu(cur->inner.vol_id), + ubi32_to_cpu(cur->inner.lnum), + ubi32_to_cpu(cur->inner.leb_ver)); + + hist = cur->older; + while (hist != NULL) + { + fprintf(stream, "+ VOL %4u-%04u | VER 0x%8x\n", + ubi32_to_cpu(hist->inner.vol_id), + ubi32_to_cpu(hist->inner.lnum), + ubi32_to_cpu(hist->inner.leb_ver)); + + 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(eb_info_t *head) +{ + if (head == NULL) + return 0; + + while (*head != NULL) + { + eb_info_t cur; + eb_info_t hist; + + cur = *head; + *head = (*head)->next; + + hist = cur->older; + while (hist != NULL) + { + eb_info_t temp; + + temp = hist; + hist = hist->older; + + free(temp); + } + + free(cur); + } + + return 0; +} + |