diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/LICENSE.libiniparser | 20 | ||||
| -rw-r--r-- | lib/Makemodule.am | 18 | ||||
| -rw-r--r-- | lib/dictionary.c | 405 | ||||
| -rw-r--r-- | lib/libiniparser.c | 646 | ||||
| -rw-r--r-- | lib/libscan.c | 225 | ||||
| -rw-r--r-- | lib/libubi.c | 1391 | ||||
| -rw-r--r-- | lib/libubi_int.h | 134 | ||||
| -rw-r--r-- | lib/libubigen.c | 315 | 
8 files changed, 3154 insertions, 0 deletions
diff --git a/lib/LICENSE.libiniparser b/lib/LICENSE.libiniparser new file mode 100644 index 0000000..74c125c --- /dev/null +++ b/lib/LICENSE.libiniparser @@ -0,0 +1,20 @@ +Copyright (c) 2000-2007 by Nicolas Devillard. +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/Makemodule.am b/lib/Makemodule.am index 694a151..5bee5b6 100644 --- a/lib/Makemodule.am +++ b/lib/Makemodule.am @@ -9,4 +9,22 @@ libmtd_a_SOURCES = \  libmissing_a_SOURCES = \  	lib/execinfo.c +libubi_a_SOURCES = \ +	lib/libubi.c \ +	lib/libubi_int.h + +libubigen_a_SOURCES = \ +	lib/libubigen.c + +libscan_a_SOURCES = \ +	lib/libscan.c + +libiniparser_a_SOURCES = \ +	lib/libiniparser.c \ +	lib/dictionary.c + +EXTRA_DIST += lib/LICENSE.libiniparser +  noinst_LIBRARIES += libmtd.a libmissing.a +noinst_LIBRARIES += libubi.a libubigen.a libscan.a +noinst_LIBRARIES += libiniparser.a diff --git a/lib/dictionary.c b/lib/dictionary.c new file mode 100644 index 0000000..f4b7468 --- /dev/null +++ b/lib/dictionary.c @@ -0,0 +1,405 @@ +/*-------------------------------------------------------------------------*/ +/** +   @file	dictionary.c +   @author	N. Devillard +   @date	Sep 2007 +   @version	$Revision: 1.27 $ +   @brief	Implements a dictionary for string variables. + +   This module implements a simple dictionary object, i.e. a list +   of string/string associations. This object is useful to store e.g. +   informations retrieved from a configuration file (ini files). +*/ +/*--------------------------------------------------------------------------*/ + +/* +	$Id: dictionary.c,v 1.27 2007-11-23 21:39:18 ndevilla Exp $ +	$Revision: 1.27 $ +*/ +/*--------------------------------------------------------------------------- +   								Includes + ---------------------------------------------------------------------------*/ +#include "dictionary.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/** Maximum value size for integers and doubles. */ +#define MAXVALSZ	1024 + +/** Minimal allocated number of entries in a dictionary */ +#define DICTMINSZ	128 + +/** Invalid key token */ +#define DICT_INVALID_KEY    ((char*)-1) + +/*--------------------------------------------------------------------------- +  							Private functions + ---------------------------------------------------------------------------*/ + +/* Doubles the allocated size associated to a pointer */ +/* 'size' is the current allocated size. */ +static void * mem_double(void * ptr, int size) +{ +    void * newptr ; + +    newptr = calloc(2*size, 1); +    if (newptr==NULL) { +        return NULL ; +    } +    memcpy(newptr, ptr, size); +    free(ptr); +    return newptr ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief    Duplicate a string +  @param    s String to duplicate +  @return   Pointer to a newly allocated string, to be freed with free() + +  This is a replacement for strdup(). This implementation is provided +  for systems that do not have it. + */ +/*--------------------------------------------------------------------------*/ +static char * xstrdup(char * s) +{ +    char * t ; +    if (!s) +        return NULL ; +    t = malloc(strlen(s)+1) ; +    if (t) { +        strcpy(t,s); +    } +    return t ; +} + +/*--------------------------------------------------------------------------- +  							Function codes + ---------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ +/** +  @brief	Compute the hash key for a string. +  @param	key		Character string to use for key. +  @return	1 unsigned int on at least 32 bits. + +  This hash function has been taken from an Article in Dr Dobbs Journal. +  This is normally a collision-free function, distributing keys evenly. +  The key is stored anyway in the struct so that collision can be avoided +  by comparing the key itself in last resort. + */ +/*--------------------------------------------------------------------------*/ +unsigned dictionary_hash(char * key) +{ +	int			len ; +	unsigned	hash ; +	int			i ; + +	len = strlen(key); +	for (hash=0, i=0 ; i<len ; i++) { +		hash += (unsigned)key[i] ; +		hash += (hash<<10); +		hash ^= (hash>>6) ; +	} +	hash += (hash <<3); +	hash ^= (hash >>11); +	hash += (hash <<15); +	return hash ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief	Create a new dictionary object. +  @param	size	Optional initial size of the dictionary. +  @return	1 newly allocated dictionary objet. + +  This function allocates a new dictionary object of given size and returns +  it. If you do not know in advance (roughly) the number of entries in the +  dictionary, give size=0. + */ +/*--------------------------------------------------------------------------*/ +dictionary * dictionary_new(int size) +{ +	dictionary	*	d ; + +	/* If no size was specified, allocate space for DICTMINSZ */ +	if (size<DICTMINSZ) size=DICTMINSZ ; + +	if (!(d = (dictionary *)calloc(1, sizeof(dictionary)))) { +		return NULL; +	} +	d->size = size ; +	d->val  = (char **)calloc(size, sizeof(char*)); +	d->key  = (char **)calloc(size, sizeof(char*)); +	d->hash = (unsigned int *)calloc(size, sizeof(unsigned)); +	return d ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief	Delete a dictionary object +  @param	d	dictionary object to deallocate. +  @return	void + +  Deallocate a dictionary object and all memory associated to it. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_del(dictionary * d) +{ +	int		i ; + +	if (d==NULL) return ; +	for (i=0 ; i<d->size ; i++) { +		if (d->key[i]!=NULL) +			free(d->key[i]); +		if (d->val[i]!=NULL) +			free(d->val[i]); +	} +	free(d->val); +	free(d->key); +	free(d->hash); +	free(d); +	return ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief	Get a value from a dictionary. +  @param	d		dictionary object to search. +  @param	key		Key to look for in the dictionary. +  @param    def     Default value to return if key not found. +  @return	1 pointer to internally allocated character string. + +  This function locates a key in a dictionary and returns a pointer to its +  value, or the passed 'def' pointer if no such key can be found in +  dictionary. The returned character pointer points to data internal to the +  dictionary object, you should not try to free it or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * dictionary_get(dictionary * d, char * key, char * def) +{ +	unsigned	hash ; +	int			i ; + +	hash = dictionary_hash(key); +	for (i=0 ; i<d->size ; i++) { +        if (d->key[i]==NULL) +            continue ; +        /* Compare hash */ +		if (hash==d->hash[i]) { +            /* Compare string, to avoid hash collisions */ +            if (!strcmp(key, d->key[i])) { +				return d->val[i] ; +			} +		} +	} +	return def ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief    Set a value in a dictionary. +  @param    d       dictionary object to modify. +  @param    key     Key to modify or add. +  @param    val     Value to add. +  @return   int     0 if Ok, anything else otherwise + +  If the given key is found in the dictionary, the associated value is +  replaced by the provided one. If the key cannot be found in the +  dictionary, it is added to it. + +  It is Ok to provide a NULL value for val, but NULL values for the dictionary +  or the key are considered as errors: the function will return immediately +  in such a case. + +  Notice that if you dictionary_set a variable to NULL, a call to +  dictionary_get will return a NULL value: the variable will be found, and +  its value (NULL) is returned. In other words, setting the variable +  content to NULL is equivalent to deleting the variable from the +  dictionary. It is not possible (in this implementation) to have a key in +  the dictionary without value. + +  This function returns non-zero in case of failure. + */ +/*--------------------------------------------------------------------------*/ +int dictionary_set(dictionary * d, char * key, char * val) +{ +	int			i ; +	unsigned	hash ; + +	if (d==NULL || key==NULL) return -1 ; + +	/* Compute hash for this key */ +	hash = dictionary_hash(key) ; +	/* Find if value is already in dictionary */ +	if (d->n>0) { +		for (i=0 ; i<d->size ; i++) { +            if (d->key[i]==NULL) +                continue ; +			if (hash==d->hash[i]) { /* Same hash value */ +				if (!strcmp(key, d->key[i])) {	 /* Same key */ +					/* Found a value: modify and return */ +					if (d->val[i]!=NULL) +						free(d->val[i]); +                    d->val[i] = val ? xstrdup(val) : NULL ; +                    /* Value has been modified: return */ +					return 0 ; +				} +			} +		} +	} +	/* Add a new value */ +	/* See if dictionary needs to grow */ +	if (d->n==d->size) { + +		/* Reached maximum size: reallocate dictionary */ +		d->val  = (char **)mem_double(d->val,  d->size * sizeof(char*)) ; +		d->key  = (char **)mem_double(d->key,  d->size * sizeof(char*)) ; +		d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ; +        if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) { +            /* Cannot grow dictionary */ +            return -1 ; +        } +		/* Double size */ +		d->size *= 2 ; +	} + +    /* Insert key in the first empty slot */ +    for (i=0 ; i<d->size ; i++) { +        if (d->key[i]==NULL) { +            /* Add key here */ +            break ; +        } +    } +	/* Copy key */ +	d->key[i]  = xstrdup(key); +    d->val[i]  = val ? xstrdup(val) : NULL ; +	d->hash[i] = hash; +	d->n ++ ; +	return 0 ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief	Delete a key in a dictionary +  @param	d		dictionary object to modify. +  @param	key		Key to remove. +  @return   void + +  This function deletes a key in a dictionary. Nothing is done if the +  key cannot be found. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_unset(dictionary * d, char * key) +{ +	unsigned	hash ; +	int			i ; + +	if (key == NULL) { +		return; +	} + +	hash = dictionary_hash(key); +	for (i=0 ; i<d->size ; i++) { +        if (d->key[i]==NULL) +            continue ; +        /* Compare hash */ +		if (hash==d->hash[i]) { +            /* Compare string, to avoid hash collisions */ +            if (!strcmp(key, d->key[i])) { +                /* Found key */ +                break ; +			} +		} +	} +    if (i>=d->size) +        /* Key not found */ +        return ; + +    free(d->key[i]); +    d->key[i] = NULL ; +    if (d->val[i]!=NULL) { +        free(d->val[i]); +        d->val[i] = NULL ; +    } +    d->hash[i] = 0 ; +    d->n -- ; +    return ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief	Dump a dictionary to an opened file pointer. +  @param	d	Dictionary to dump +  @param	f	Opened file pointer. +  @return	void + +  Dumps a dictionary onto an opened file pointer. Key pairs are printed out +  as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as +  output file pointers. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_dump(dictionary * d, FILE * out) +{ +	int		i ; + +	if (d==NULL || out==NULL) return ; +	if (d->n<1) { +		fprintf(out, "empty dictionary\n"); +		return ; +	} +	for (i=0 ; i<d->size ; i++) { +        if (d->key[i]) { +            fprintf(out, "%20s\t[%s]\n", +                    d->key[i], +                    d->val[i] ? d->val[i] : "UNDEF"); +        } +	} +	return ; +} + + +/* Test code */ +#ifdef TESTDIC +#define NVALS 20000 +int main(int argc, char *argv[]) +{ +	dictionary	*	d ; +	char	*	val ; +	int			i ; +	char		cval[90] ; + +	/* Allocate dictionary */ +	printf("allocating...\n"); +	d = dictionary_new(0); + +	/* Set values in dictionary */ +	printf("setting %d values...\n", NVALS); +	for (i=0 ; i<NVALS ; i++) { +		sprintf(cval, "%04d", i); +		dictionary_set(d, cval, "salut"); +	} +	printf("getting %d values...\n", NVALS); +	for (i=0 ; i<NVALS ; i++) { +		sprintf(cval, "%04d", i); +		val = dictionary_get(d, cval, DICT_INVALID_KEY); +		if (val==DICT_INVALID_KEY) { +			printf("cannot get value for key [%s]\n", cval); +		} +	} +    printf("unsetting %d values...\n", NVALS); +	for (i=0 ; i<NVALS ; i++) { +		sprintf(cval, "%04d", i); +		dictionary_unset(d, cval); +	} +    if (d->n != 0) { +        printf("error deleting values\n"); +    } +	printf("deallocating...\n"); +	dictionary_del(d); +	return 0 ; +} +#endif +/* vim: set ts=4 et sw=4 tw=75 */ diff --git a/lib/libiniparser.c b/lib/libiniparser.c new file mode 100644 index 0000000..898f57f --- /dev/null +++ b/lib/libiniparser.c @@ -0,0 +1,646 @@ + +/*-------------------------------------------------------------------------*/ +/** +   @file    iniparser.c +   @author  N. Devillard +   @date    Sep 2007 +   @version 3.0 +   @brief   Parser for ini files. +*/ +/*--------------------------------------------------------------------------*/ +/* +    $Id: iniparser.c,v 2.18 2008-01-03 18:35:39 ndevilla Exp $ +    $Revision: 2.18 $ +    $Date: 2008-01-03 18:35:39 $ +*/ +/*---------------------------- Includes ------------------------------------*/ +#include <ctype.h> +#include <libiniparser.h> + +/*---------------------------- Defines -------------------------------------*/ +#define ASCIILINESZ         (1024) +#define INI_INVALID_KEY     ((char*)-1) + +/*--------------------------------------------------------------------------- +                        Private to this module + ---------------------------------------------------------------------------*/ +/** + * This enum stores the status for each parsed line (internal use only). + */ +typedef enum _line_status_ { +    LINE_UNPROCESSED, +    LINE_ERROR, +    LINE_EMPTY, +    LINE_COMMENT, +    LINE_SECTION, +    LINE_VALUE +} line_status ; + +/*-------------------------------------------------------------------------*/ +/** +  @brief	Convert a string to lowercase. +  @param	s	String to convert. +  @return	ptr to statically allocated string. + +  This function returns a pointer to a statically allocated string +  containing a lowercased version of the input string. Do not free +  or modify the returned string! Since the returned string is statically +  allocated, it will be modified at each function call (not re-entrant). + */ +/*--------------------------------------------------------------------------*/ +static char * strlwc(const char * s) +{ +    static char l[ASCIILINESZ+1]; +    int i ; + +    if (s==NULL) return NULL ; +    memset(l, 0, ASCIILINESZ+1); +    i=0 ; +    while (s[i] && i<ASCIILINESZ) { +        l[i] = (char)tolower((int)s[i]); +        i++ ; +    } +    l[ASCIILINESZ]=(char)0; +    return l ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief	Remove blanks at the beginning and the end of a string. +  @param	s	String to parse. +  @return	ptr to statically allocated string. + +  This function returns a pointer to a statically allocated string, +  which is identical to the input string, except that all blank +  characters at the end and the beg. of the string have been removed. +  Do not free or modify the returned string! Since the returned string +  is statically allocated, it will be modified at each function call +  (not re-entrant). + */ +/*--------------------------------------------------------------------------*/ +static char * strstrip(char * s) +{ +    static char l[ASCIILINESZ+1]; +	char * last ; + +    if (s==NULL) return NULL ; + +	while (isspace((int)*s) && *s) s++; +	memset(l, 0, ASCIILINESZ+1); +	strcpy(l, s); +	last = l + strlen(l); +	while (last > l) { +		if (!isspace((int)*(last-1))) +			break ; +		last -- ; +	} +	*last = (char)0; +	return (char*)l ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief    Get number of sections in a dictionary +  @param    d   Dictionary to examine +  @return   int Number of sections found in dictionary + +  This function returns the number of sections found in a dictionary. +  The test to recognize sections is done on the string stored in the +  dictionary: a section name is given as "section" whereas a key is +  stored as "section:key", thus the test looks for entries that do not +  contain a colon. + +  This clearly fails in the case a section name contains a colon, but +  this should simply be avoided. + +  This function returns -1 in case of error. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getnsec(dictionary * d) +{ +    int i ; +    int nsec ; + +    if (d==NULL) return -1 ; +    nsec=0 ; +    for (i=0 ; i<d->size ; i++) { +        if (d->key[i]==NULL) +            continue ; +        if (strchr(d->key[i], ':')==NULL) { +            nsec ++ ; +        } +    } +    return nsec ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief    Get name for section n in a dictionary. +  @param    d   Dictionary to examine +  @param    n   Section number (from 0 to nsec-1). +  @return   Pointer to char string + +  This function locates the n-th section in a dictionary and returns +  its name as a pointer to a string statically allocated inside the +  dictionary. Do not free or modify the returned string! + +  This function returns NULL in case of error. + */ +/*--------------------------------------------------------------------------*/ +char * iniparser_getsecname(dictionary * d, int n) +{ +    int i ; +    int foundsec ; + +    if (d==NULL || n<0) return NULL ; +    foundsec=0 ; +    for (i=0 ; i<d->size ; i++) { +        if (d->key[i]==NULL) +            continue ; +        if (strchr(d->key[i], ':')==NULL) { +            foundsec++ ; +            if (foundsec>n) +                break ; +        } +    } +    if (foundsec<=n) { +        return NULL ; +    } +    return d->key[i] ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief    Dump a dictionary to an opened file pointer. +  @param    d   Dictionary to dump. +  @param    f   Opened file pointer to dump to. +  @return   void + +  This function prints out the contents of a dictionary, one element by +  line, onto the provided file pointer. It is OK to specify @c stderr +  or @c stdout as output files. This function is meant for debugging +  purposes mostly. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump(dictionary * d, FILE * f) +{ +    int     i ; + +    if (d==NULL || f==NULL) return ; +    for (i=0 ; i<d->size ; i++) { +        if (d->key[i]==NULL) +            continue ; +        if (d->val[i]!=NULL) { +            fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]); +        } else { +            fprintf(f, "[%s]=UNDEF\n", d->key[i]); +        } +    } +    return ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief    Save a dictionary to a loadable ini file +  @param    d   Dictionary to dump +  @param    f   Opened file pointer to dump to +  @return   void + +  This function dumps a given dictionary into a loadable ini file. +  It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump_ini(dictionary * d, FILE * f) +{ +    int     i, j ; +    char    keym[ASCIILINESZ+1]; +    int     nsec ; +    char *  secname ; +    int     seclen ; + +    if (d==NULL || f==NULL) return ; + +    nsec = iniparser_getnsec(d); +    if (nsec<1) { +        /* No section in file: dump all keys as they are */ +        for (i=0 ; i<d->size ; i++) { +            if (d->key[i]==NULL) +                continue ; +            fprintf(f, "%s = %s\n", d->key[i], d->val[i]); +        } +        return ; +    } +    for (i=0 ; i<nsec ; i++) { +        secname = iniparser_getsecname(d, i) ; +        seclen  = (int)strlen(secname); +        fprintf(f, "\n[%s]\n", secname); +        sprintf(keym, "%s:", secname); +        for (j=0 ; j<d->size ; j++) { +            if (d->key[j]==NULL) +                continue ; +            if (!strncmp(d->key[j], keym, seclen+1)) { +                fprintf(f, +                        "%-30s = %s\n", +                        d->key[j]+seclen+1, +                        d->val[j] ? d->val[j] : ""); +            } +        } +    } +    fprintf(f, "\n"); +    return ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief    Get the string associated to a key +  @param    d       Dictionary to search +  @param    key     Key string to look for +  @param    def     Default value to return if key not found. +  @return   pointer to statically allocated character string + +  This function queries a dictionary for a key. A key as read from an +  ini file is given as "section:key". If the key cannot be found, +  the pointer passed as 'def' is returned. +  The returned char pointer is pointing to a string allocated in +  the dictionary, do not free or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * iniparser_getstring(dictionary * d, const char * key, char * def) +{ +    char * lc_key ; +    char * sval ; + +    if (d==NULL || key==NULL) +        return def ; + +    lc_key = strlwc(key); +    sval = dictionary_get(d, lc_key, def); +    return sval ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief    Get the string associated to a key, convert to an int +  @param    d Dictionary to search +  @param    key Key string to look for +  @param    notfound Value to return in case of error +  @return   integer + +  This function queries a dictionary for a key. A key as read from an +  ini file is given as "section:key". If the key cannot be found, +  the notfound value is returned. + +  Supported values for integers include the usual C notation +  so decimal, octal (starting with 0) and hexadecimal (starting with 0x) +  are supported. Examples: + +  "42"      ->  42 +  "042"     ->  34 (octal -> decimal) +  "0x42"    ->  66 (hexa  -> decimal) + +  Warning: the conversion may overflow in various ways. Conversion is +  totally outsourced to strtol(), see the associated man page for overflow +  handling. + +  Credits: Thanks to A. Becker for suggesting strtol() + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getint(dictionary * d, const char * key, int notfound) +{ +    char    *   str ; + +    str = iniparser_getstring(d, key, INI_INVALID_KEY); +    if (str==INI_INVALID_KEY) return notfound ; +    return (int)strtol(str, NULL, 0); +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief    Get the string associated to a key, convert to a double +  @param    d Dictionary to search +  @param    key Key string to look for +  @param    notfound Value to return in case of error +  @return   double + +  This function queries a dictionary for a key. A key as read from an +  ini file is given as "section:key". If the key cannot be found, +  the notfound value is returned. + */ +/*--------------------------------------------------------------------------*/ +double iniparser_getdouble(dictionary * d, char * key, double notfound) +{ +    char    *   str ; + +    str = iniparser_getstring(d, key, INI_INVALID_KEY); +    if (str==INI_INVALID_KEY) return notfound ; +    return atof(str); +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief    Get the string associated to a key, convert to a boolean +  @param    d Dictionary to search +  @param    key Key string to look for +  @param    notfound Value to return in case of error +  @return   integer + +  This function queries a dictionary for a key. A key as read from an +  ini file is given as "section:key". If the key cannot be found, +  the notfound value is returned. + +  A true boolean is found if one of the following is matched: + +  - A string starting with 'y' +  - A string starting with 'Y' +  - A string starting with 't' +  - A string starting with 'T' +  - A string starting with '1' + +  A false boolean is found if one of the following is matched: + +  - A string starting with 'n' +  - A string starting with 'N' +  - A string starting with 'f' +  - A string starting with 'F' +  - A string starting with '0' + +  The notfound value returned if no boolean is identified, does not +  necessarily have to be 0 or 1. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getboolean(dictionary * d, const char * key, int notfound) +{ +    char    *   c ; +    int         ret ; + +    c = iniparser_getstring(d, key, INI_INVALID_KEY); +    if (c==INI_INVALID_KEY) return notfound ; +    if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { +        ret = 1 ; +    } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { +        ret = 0 ; +    } else { +        ret = notfound ; +    } +    return ret; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief    Finds out if a given entry exists in a dictionary +  @param    ini     Dictionary to search +  @param    entry   Name of the entry to look for +  @return   integer 1 if entry exists, 0 otherwise + +  Finds out if a given entry exists in the dictionary. Since sections +  are stored as keys with NULL associated values, this is the only way +  of querying for the presence of sections in a dictionary. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_find_entry( +    dictionary  *   ini, +    char        *   entry +) +{ +    int found=0 ; +    if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) { +        found = 1 ; +    } +    return found ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief    Set an entry in a dictionary. +  @param    ini     Dictionary to modify. +  @param    entry   Entry to modify (entry name) +  @param    val     New value to associate to the entry. +  @return   int 0 if Ok, -1 otherwise. + +  If the given entry can be found in the dictionary, it is modified to +  contain the provided value. If it cannot be found, -1 is returned. +  It is Ok to set val to NULL. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_set(dictionary * ini, char * entry, char * val) +{ +    return dictionary_set(ini, strlwc(entry), val) ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief    Delete an entry in a dictionary +  @param    ini     Dictionary to modify +  @param    entry   Entry to delete (entry name) +  @return   void + +  If the given entry can be found, it is deleted from the dictionary. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_unset(dictionary * ini, char * entry) +{ +    dictionary_unset(ini, strlwc(entry)); +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief	Load a single line from an INI file +  @param    input_line  Input line, may be concatenated multi-line input +  @param    section     Output space to store section +  @param    key         Output space to store key +  @param    value       Output space to store value +  @return   line_status value + */ +/*--------------------------------------------------------------------------*/ +static line_status iniparser_line( +    char * input_line, +    char * section, +    char * key, +    char * value) +{ +    line_status sta ; +    char        line[ASCIILINESZ+1]; +    int         len ; + +    strcpy(line, strstrip(input_line)); +    len = (int)strlen(line); + +    sta = LINE_UNPROCESSED ; +    if (len<1) { +        /* Empty line */ +        sta = LINE_EMPTY ; +    } else if (line[0]=='#') { +        /* Comment line */ +        sta = LINE_COMMENT ; +    } else if (line[0]=='[' && line[len-1]==']') { +        /* Section name */ +        sscanf(line, "[%[^]]", section); +        strcpy(section, strstrip(section)); +        strcpy(section, strlwc(section)); +        sta = LINE_SECTION ; +    } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 +           ||  sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2 +           ||  sscanf (line, "%[^=] = %[^;#]",     key, value) == 2) { +        /* Usual key=value, with or without comments */ +        strcpy(key, strstrip(key)); +        strcpy(key, strlwc(key)); +        strcpy(value, strstrip(value)); +        /* +         * sscanf cannot handle '' or "" as empty values +         * this is done here +         */ +        if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) { +            value[0]=0 ; +        } +        sta = LINE_VALUE ; +    } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2 +           ||  sscanf(line, "%[^=] %[=]", key, value) == 2) { +        /* +         * Special cases: +         * key= +         * key=; +         * key=# +         */ +        strcpy(key, strstrip(key)); +        strcpy(key, strlwc(key)); +        value[0]=0 ; +        sta = LINE_VALUE ; +    } else { +        /* Generate syntax error */ +        sta = LINE_ERROR ; +    } +    return sta ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief    Parse an ini file and return an allocated dictionary object +  @param    ininame Name of the ini file to read. +  @return   Pointer to newly allocated dictionary + +  This is the parser for ini files. This function is called, providing +  the name of the file to be read. It returns a dictionary object that +  should not be accessed directly, but through accessor functions +  instead. + +  The returned dictionary must be freed using iniparser_freedict(). + */ +/*--------------------------------------------------------------------------*/ +dictionary * iniparser_load(const char * ininame) +{ +    FILE * in ; + +    char line    [ASCIILINESZ+1] ; +    char section [ASCIILINESZ+1] ; +    char key     [ASCIILINESZ+1] ; +    char tmp     [ASCIILINESZ+1] ; +    char val     [ASCIILINESZ+1] ; + +    int  last=0 ; +    int  len ; +    int  lineno=0 ; +    int  errs=0; + +    dictionary * dict ; + +    if ((in=fopen(ininame, "r"))==NULL) { +        fprintf(stderr, "iniparser: cannot open %s\n", ininame); +        return NULL ; +    } + +    dict = dictionary_new(0) ; +    if (!dict) { +        fclose(in); +        return NULL ; +    } + +    memset(line,    0, ASCIILINESZ); +    memset(section, 0, ASCIILINESZ); +    memset(key,     0, ASCIILINESZ); +    memset(val,     0, ASCIILINESZ); +    last=0 ; + +    while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) { +        lineno++ ; +        len = (int)strlen(line)-1; +        /* Safety check against buffer overflows */ +        if (line[len]!='\n') { +            fprintf(stderr, +                    "iniparser: input line too long in %s (%d)\n", +                    ininame, +                    lineno); +            dictionary_del(dict); +            fclose(in); +            return NULL ; +        } +        /* Get rid of \n and spaces at end of line */ +        while ((len>=0) && +                ((line[len]=='\n') || (isspace(line[len])))) { +            line[len]=0 ; +            len-- ; +        } +        /* Detect multi-line */ +        if (line[len]=='\\') { +            /* Multi-line value */ +            last=len ; +            continue ; +        } else { +            last=0 ; +        } +        switch (iniparser_line(line, section, key, val)) { +            case LINE_EMPTY: +            case LINE_COMMENT: +            break ; + +            case LINE_SECTION: +            errs = dictionary_set(dict, section, NULL); +            break ; + +            case LINE_VALUE: +            sprintf(tmp, "%s:%s", section, key); +            errs = dictionary_set(dict, tmp, val) ; +            break ; + +            case LINE_ERROR: +            fprintf(stderr, "iniparser: syntax error in %s (%d):\n", +                    ininame, +                    lineno); +            fprintf(stderr, "-> %s\n", line); +            errs++ ; +            break; + +            default: +            break ; +        } +        memset(line, 0, ASCIILINESZ); +        last=0; +        if (errs<0) { +            fprintf(stderr, "iniparser: memory allocation failure\n"); +            break ; +        } +    } +    if (errs) { +        dictionary_del(dict); +        dict = NULL ; +    } +    fclose(in); +    return dict ; +} + +/*-------------------------------------------------------------------------*/ +/** +  @brief    Free all memory associated to an ini dictionary +  @param    d Dictionary to free +  @return   void + +  Free all memory associated to an ini dictionary. +  It is mandatory to call this function before the dictionary object +  gets out of the current context. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_freedict(dictionary * d) +{ +    dictionary_del(d); +} + +/* vim: set ts=4 et sw=4 tw=75 */ diff --git a/lib/libscan.c b/lib/libscan.c new file mode 100644 index 0000000..dc47a89 --- /dev/null +++ b/lib/libscan.c @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2008 Nokia Corporation + * + * 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: Artem Bityutskiy + * + * UBI scanning library. + */ + +#define PROGRAM_NAME "libscan" + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdint.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +#include <mtd_swab.h> +#include <mtd/ubi-media.h> +#include <mtd/mtd-user.h> +#include <libmtd.h> +#include <libscan.h> +#include <crc32.h> +#include "common.h" + +static int all_ff(const void *buf, int len) +{ +	int i; +	const uint8_t *p = buf; + +	for (i = 0; i < len; i++) +		if (p[i] != 0xFF) +			return 0; +	return 1; +} + +int ubi_scan(struct mtd_dev_info *mtd, int fd, struct ubi_scan_info **info, +	     int verbose) +{ +	int eb, v = (verbose == 2), pr = (verbose == 1); +	struct ubi_scan_info *si; +	unsigned long long sum = 0; + +	si = calloc(1, sizeof(struct ubi_scan_info)); +	if (!si) +		return sys_errmsg("cannot allocate %zd bytes of memory", +				  sizeof(struct ubi_scan_info)); + +	si->ec = calloc(mtd->eb_cnt, sizeof(uint32_t)); +	if (!si->ec) { +		sys_errmsg("cannot allocate %zd bytes of memory", +			   sizeof(struct ubi_scan_info)); +		goto out_si; +	} + +	si->vid_hdr_offs = si->data_offs = -1; + +	verbose(v, "start scanning eraseblocks 0-%d", mtd->eb_cnt); +	for (eb = 0; eb < mtd->eb_cnt; eb++) { +		int ret; +		uint32_t crc; +		struct ubi_ec_hdr ech; +		unsigned long long ec; + +		if (v) { +			normsg_cont("scanning eraseblock %d", eb); +			fflush(stdout); +		} +		if (pr) { +			printf("\r" PROGRAM_NAME ": scanning eraseblock %d -- %2lld %% complete  ", +			       eb, (long long)(eb + 1) * 100 / mtd->eb_cnt); +			fflush(stdout); +		} + +		ret = mtd_is_bad(mtd, fd, eb); +		if (ret == -1) +			goto out_ec; +		if (ret) { +			si->bad_cnt += 1; +			si->ec[eb] = EB_BAD; +			if (v) +				printf(": bad\n"); +			continue; +		} + +		ret = mtd_read(mtd, fd, eb, 0, &ech, sizeof(struct ubi_ec_hdr)); +		if (ret < 0) +			goto out_ec; + +		if (be32_to_cpu(ech.magic) != UBI_EC_HDR_MAGIC) { +			if (all_ff(&ech, sizeof(struct ubi_ec_hdr))) { +				si->empty_cnt += 1; +				si->ec[eb] = EB_EMPTY; +				if (v) +					printf(": empty\n"); +			} else { +				si->alien_cnt += 1; +				si->ec[eb] = EB_ALIEN; +				if (v) +					printf(": alien\n"); +			} +			continue; +		} + +		crc = mtd_crc32(UBI_CRC32_INIT, &ech, UBI_EC_HDR_SIZE_CRC); +		if (be32_to_cpu(ech.hdr_crc) != crc) { +			si->corrupted_cnt += 1; +			si->ec[eb] = EB_CORRUPTED; +			if (v) +				printf(": bad CRC %#08x, should be %#08x\n", +				       crc, be32_to_cpu(ech.hdr_crc)); +			continue; +		} + +		ec = be64_to_cpu(ech.ec); +		if (ec > EC_MAX) { +			if (pr) +				printf("\n"); +			errmsg("erase counter in EB %d is %llu, while this " +			       "program expects them to be less than %u", +			       eb, ec, EC_MAX); +			goto out_ec; +		} + +		if (si->vid_hdr_offs == -1) { +			si->vid_hdr_offs = be32_to_cpu(ech.vid_hdr_offset); +			si->data_offs = be32_to_cpu(ech.data_offset); +			if (si->data_offs % mtd->min_io_size) { +				if (pr) +					printf("\n"); +				if (v) +					printf(": corrupted because of the below\n"); +				warnmsg("bad data offset %d at eraseblock %d (n" +					"of multiple of min. I/O unit size %d)", +					si->data_offs, eb, mtd->min_io_size); +				warnmsg("treat eraseblock %d as corrupted", eb); +				si->corrupted_cnt += 1; +				si->ec[eb] = EB_CORRUPTED; +				continue; + +			} +		} else { +			if ((int)be32_to_cpu(ech.vid_hdr_offset) != si->vid_hdr_offs) { +				if (pr) +					printf("\n"); +				if (v) +					printf(": corrupted because of the below\n"); +				warnmsg("inconsistent VID header offset: was " +					"%d, but is %d in eraseblock %d", +					si->vid_hdr_offs, +					be32_to_cpu(ech.vid_hdr_offset), eb); +				warnmsg("treat eraseblock %d as corrupted", eb); +				si->corrupted_cnt += 1; +				si->ec[eb] = EB_CORRUPTED; +				continue; +			} +			if ((int)be32_to_cpu(ech.data_offset) != si->data_offs) { +				if (pr) +					printf("\n"); +				if (v) +					printf(": corrupted because of the below\n"); +				warnmsg("inconsistent data offset: was %d, but" +					" is %d in eraseblock %d", +					si->data_offs, +					be32_to_cpu(ech.data_offset), eb); +				warnmsg("treat eraseblock %d as corrupted", eb); +				si->corrupted_cnt += 1; +				si->ec[eb] = EB_CORRUPTED; +				continue; +			} +		} + +		si->ok_cnt += 1; +		si->ec[eb] = ec; +		if (v) +			printf(": OK, erase counter %u\n", si->ec[eb]); +	} + +	if (si->ok_cnt != 0) { +		/* Calculate mean erase counter */ +		for (eb = 0; eb < mtd->eb_cnt; eb++) { +			if (si->ec[eb] > EC_MAX) +				continue; +			sum += si->ec[eb]; +		} +		si->mean_ec = sum / si->ok_cnt; +	} + +	si->good_cnt = mtd->eb_cnt - si->bad_cnt; +	verbose(v, "finished, mean EC %lld, %d OK, %d corrupted, %d empty, %d " +		"alien, bad %d", si->mean_ec, si->ok_cnt, si->corrupted_cnt, +		si->empty_cnt, si->alien_cnt, si->bad_cnt); + +	*info = si; +	if (pr) +		printf("\n"); +	return 0; + +out_ec: +	free(si->ec); +out_si: +	free(si); +	*info = NULL; +	return -1; +} + +void ubi_scan_free(struct ubi_scan_info *si) +{ +	free(si->ec); +	free(si); +} diff --git a/lib/libubi.c b/lib/libubi.c new file mode 100644 index 0000000..758d351 --- /dev/null +++ b/lib/libubi.c @@ -0,0 +1,1391 @@ +/* + * 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: Artem Bityutskiy + * + * UBI (Unsorted Block Images) library. + */ + +#define PROGRAM_NAME "libubi" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <limits.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <libubi.h> +#include "libubi_int.h" +#include "common.h" + +/** + * mkpath - compose full path from 2 given components. + * @path: the first component + * @name: the second component + * + * This function returns the resulting path in case of success and %NULL in + * case of failure. + */ +static char *mkpath(const char *path, const char *name) +{ +	char *n; +	int len1 = strlen(path); +	int len2 = strlen(name); + +	n = malloc(len1 + len2 + 2); +	if (!n) { +		sys_errmsg("cannot allocate %d bytes", len1 + len2 + 2); +		return NULL; +	} + +	memcpy(n, path, len1); +	if (n[len1 - 1] != '/') +		n[len1++] = '/'; + +	memcpy(n + len1, name, len2 + 1); +	return n; +} + +/** + * read_positive_ll - read a positive 'long long' value from a file. + * @file: the file to read from + * @value: the result is stored here + * + * This function reads file @file and interprets its contents as a positive + * 'long long' integer. If this is not true, it fails with %EINVAL error code. + * Returns %0 in case of success and %-1 in case of failure. + */ +static int read_positive_ll(const char *file, long long *value) +{ +	int fd, rd; +	char buf[50]; + +	fd = open(file, O_RDONLY); +	if (fd == -1) +		return -1; + +	rd = read(fd, buf, sizeof(buf)); +	if (rd == -1) { +		sys_errmsg("cannot read \"%s\"", file); +		goto out_error; +	} +	if (rd == sizeof(buf)) { +		errmsg("contents of \"%s\" is too long", file); +		errno = EINVAL; +		goto out_error; +	} +	buf[rd] = '\0'; + +	if (sscanf(buf, "%lld\n", value) != 1) { +		errmsg("cannot read integer from \"%s\"\n", file); +		errno = EINVAL; +		goto out_error; +	} + +	if (*value < 0) { +		errmsg("negative value %lld in \"%s\"", *value, file); +		errno = EINVAL; +		goto out_error; +	} + +	if (close(fd)) +		return sys_errmsg("close failed on \"%s\"", file); + +	return 0; + +out_error: +	close(fd); +	return -1; +} + +/** + * read_positive_int - read a positive 'int' value from a file. + * @file: the file to read from + * @value: the result is stored here + * + * This function is the same as 'read_positive_ll()', but it reads an 'int' + * value, not 'long long'. + */ +static int read_positive_int(const char *file, int *value) +{ +	long long res; + +	if (read_positive_ll(file, &res)) +		return -1; + +	/* Make sure the value is not too big */ +	if (res > INT_MAX) { +		errmsg("value %lld read from file \"%s\" is out of range", +		       res, file); +		errno = EINVAL; +		return -1; +	} + +	*value = res; +	return 0; +} + +/** + * read_data - read data from a file. + * @file: the file to read from + * @buf: the buffer to read to + * @buf_len: buffer length + * + * This function returns number of read bytes in case of success and %-1 in + * case of failure. Note, if the file contains more then @buf_len bytes of + * date, this function fails with %EINVAL error code. + */ +static int read_data(const char *file, void *buf, int buf_len) +{ +	int fd, rd, tmp, tmp1; + +	fd = open(file, O_RDONLY); +	if (fd == -1) +		return -1; + +	rd = read(fd, buf, buf_len); +	if (rd == -1) { +		sys_errmsg("cannot read \"%s\"", file); +		goto out_error; +	} + +	if (rd == buf_len) { +		errmsg("contents of \"%s\" is too long", file); +		errno = EINVAL; +		goto out_error; +	} + +	((char *)buf)[rd] = '\0'; + +	/* Make sure all data is read */ +	tmp1 = read(fd, &tmp, 1); +	if (tmp1 == 1) { +		sys_errmsg("cannot read \"%s\"", file); +		goto out_error; +	} +	if (tmp1) { +		errmsg("file \"%s\" contains too much data (> %d bytes)", +		       file, buf_len); +		errno = EINVAL; +		goto out_error; +	} + +	if (close(fd)) +		return sys_errmsg("close failed on \"%s\"", file); + +	return rd; + +out_error: +	close(fd); +	return -1; +} + +/** + * read_major - read major and minor numbers from a file. + * @file: name of the file to read from + * @major: major number is returned here + * @minor: minor number is returned here + * + * This function returns % in case of succes, and %-1 in case of failure. + */ +static int read_major(const char *file, int *major, int *minor) +{ +	int ret; +	char buf[50]; + +	ret = read_data(file, buf, 50); +	if (ret < 0) +		return ret; + +	ret = sscanf(buf, "%d:%d\n", major, minor); +	if (ret != 2) { +		errno = EINVAL; +		return errmsg("\"%s\" does not have major:minor format", file); +	} + +	if (*major < 0 || *minor < 0) { +		errno = EINVAL; +		return errmsg("bad major:minor %d:%d in \"%s\"", +			      *major, *minor, file); +	} + +	return 0; +} + +/** + * dev_read_int - read a positive 'int' value from an UBI device sysfs file. + * @patt: file pattern to read from + * @dev_num: UBI device number + * @value: the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int dev_read_int(const char *patt, int dev_num, int *value) +{ +	char file[strlen(patt) + 50]; + +	sprintf(file, patt, dev_num); +	return read_positive_int(file, value); +} + +/** + * vol_read_int - read a positive 'int' value from an UBI volume sysfs file. + * @patt: file pattern to read from + * @dev_num: UBI device number + * @vol_id: volume ID + * @value: the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value) +{ +	char file[strlen(patt) + 100]; + +	sprintf(file, patt, dev_num, vol_id); +	return read_positive_int(file, value); +} + +/** + * dev_read_ll - read a positive 'long long' value from an UBI device sysfs file. + * @patt: file pattern to read from + * @dev_num: UBI device number + * @value: the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int dev_read_ll(const char *patt, int dev_num, long long *value) +{ +	char file[strlen(patt) + 50]; + +	sprintf(file, patt, dev_num); +	return read_positive_ll(file, value); +} + +/** + * vol_read_ll - read a positive 'long long' value from an UBI volume sysfs file. + * @patt: file pattern to read from + * @dev_num: UBI device number + * @vol_id: volume ID + * @value: the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int vol_read_ll(const char *patt, int dev_num, int vol_id, +		       long long *value) +{ +	char file[strlen(patt) + 100]; + +	sprintf(file, patt, dev_num, vol_id); +	return read_positive_ll(file, value); +} + +/** + * vol_read_data - read data from an UBI volume's sysfs file. + * @patt: file pattern to read from + * @dev_num: UBI device number + * @vol_id: volume ID + * @buf: buffer to read to + * @buf_len: buffer length + * + * This function returns number of read bytes in case of success and %-1 in + * case of failure. + */ +static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf, +			 int buf_len) +{ +	char file[strlen(patt) + 100]; + +	sprintf(file, patt, dev_num, vol_id); +	return read_data(file, buf, buf_len); +} + +/** + * dev_get_major - get major and minor numbers of an UBI device. + * @lib: libubi descriptor + * @dev_num: UBI device number + * @major: major number is returned here + * @minor: minor number is returned here + * + * This function returns zero in case of succes and %-1 in case of failure. + */ +static int dev_get_major(struct libubi *lib, int dev_num, int *major, int *minor) +{ +	char file[strlen(lib->dev_dev) + 50]; + +	sprintf(file, lib->dev_dev, dev_num); +	return read_major(file, major, minor); +} + +/** + * vol_get_major - get major and minor numbers of an UBI volume. + * @lib: libubi descriptor + * @dev_num: UBI device number + * @vol_id: volume ID + * @major: major number is returned here + * @minor: minor number is returned here + * + * This function returns zero in case of succes and %-1 in case of failure. + */ +static int vol_get_major(struct libubi *lib, int dev_num, int vol_id, +			 int *major, int *minor) +{ +	char file[strlen(lib->vol_dev) + 100]; + +	sprintf(file, lib->vol_dev, dev_num, vol_id); +	return read_major(file, major, minor); +} + +/** + * vol_node2nums - find UBI device number and volume ID by volume device node + *                 file. + * @lib: UBI library descriptor + * @node: UBI character device node name + * @dev_num: UBI device number is returned here + * @vol_id: volume ID is returned hers + * + * This function returns zero in case of succes and %-1 in case of failure. + */ +static int vol_node2nums(struct libubi *lib, const char *node, int *dev_num, +			 int *vol_id) +{ +	struct stat st; +	struct ubi_info info; +	int i, fd, major, minor; +	char file[strlen(lib->ubi_vol) + 100]; + +	if (stat(node, &st)) +		return sys_errmsg("cannot get information about \"%s\"", +				  node); + +	if (!S_ISCHR(st.st_mode)) { +		errno = EINVAL; +		return errmsg("\"%s\" is not a character device", node); +	} + +	major = major(st.st_rdev); +	minor = minor(st.st_rdev); + +	if (minor == 0) { +		errno = EINVAL; +		return errmsg("\"%s\" is not a volume character device", node); +	} + +	if (ubi_get_info((libubi_t *)lib, &info)) +		return -1; + +	for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { +		int major1, minor1, ret; + +		ret = dev_get_major(lib, i, &major1, &minor1); +		if (ret) { +			if (errno == ENOENT) +				continue; +			return -1; +		} + +		if (major1 == major) +			break; +	} + +	if (i > info.highest_dev_num) { +		errno = ENODEV; +		return -1; +	} + +	/* Make sure this UBI volume exists */ +	sprintf(file, lib->ubi_vol, i, minor - 1); +	fd = open(file, O_RDONLY); +	if (fd == -1) { +		errno = ENODEV; +		return -1; +	} + +	if (close(fd)) +		return sys_errmsg("close failed on \"%s\"", file); + +	*dev_num = i; +	*vol_id = minor - 1; +	errno = 0; +	return 0; +} + +/** + * dev_node2num - find UBI device number by its character device node. + * @lib: UBI library descriptor + * @node: UBI character device node name + * @dev_num: UBI device number is returned here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int dev_node2num(struct libubi *lib, const char *node, int *dev_num) +{ +	struct stat st; +	struct ubi_info info; +	int i, major, minor; + +	if (stat(node, &st)) +		return sys_errmsg("cannot get information about \"%s\"", node); + +	if (!S_ISCHR(st.st_mode)) { +		errno = EINVAL; +		return errmsg("\"%s\" is not a character device", node); +	} + +	major = major(st.st_rdev); +	minor = minor(st.st_rdev); + +	if (minor != 0) { +		errno = EINVAL; +		return errmsg("\"%s\" is not an UBI character device", node); +	} + +	if (ubi_get_info((libubi_t *)lib, &info)) +		return -1; + +	for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { +		int major1, minor1, ret; + +		ret = dev_get_major(lib, i, &major1, &minor1); +		if (ret) { +			if (errno == ENOENT) +				continue; +			return -1; +		} + +		if (major1 == major) { +			if (minor1 != 0) { +				errmsg("UBI character device minor number is " +				       "%d, but must be 0", minor1); +				errno = EINVAL; +				return -1; +			} +			errno = 0; +			*dev_num = i; +			return 0; +		} +	} + +	errno = ENODEV; +	return -1; +} + +int mtd_num2ubi_dev(libubi_t desc, int mtd_num, int *dev_num) +{ +	struct ubi_info info; +	int i, ret, mtd_num1; +	struct libubi *lib = desc; + +	if (ubi_get_info(desc, &info)) +		return -1; + +	for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { +		ret = dev_read_int(lib->dev_mtd_num, i, &mtd_num1); +		if (ret) { +			if (errno == ENOENT) +				continue; +			return -1; +		} + +		if (mtd_num1 == mtd_num) { +			errno = 0; +			*dev_num = i; +			return 0; +		} +	} + +	errno = 0; +	return -1; +} + +libubi_t libubi_open(void) +{ +	int fd, version; +	struct libubi *lib; + +	lib = calloc(1, sizeof(struct libubi)); +	if (!lib) +		return NULL; + +	lib->sysfs_ctrl = mkpath(SYSFS_ROOT, SYSFS_CTRL); +	if (!lib->sysfs_ctrl) +		goto out_error; + +	lib->ctrl_dev = mkpath(lib->sysfs_ctrl, CTRL_DEV); +	if (!lib->ctrl_dev) +		goto out_error; + +	lib->sysfs_ubi = mkpath(SYSFS_ROOT, SYSFS_UBI); +	if (!lib->sysfs_ubi) +		goto out_error; + +	/* Make sure UBI is present */ +	fd = open(lib->sysfs_ubi, O_RDONLY); +	if (fd == -1) { +		errno = 0; +		goto out_error; +	} + +	if (close(fd)) { +		sys_errmsg("close failed on \"%s\"", lib->sysfs_ubi); +		goto out_error; +	} + +	lib->ubi_dev = mkpath(lib->sysfs_ubi, UBI_DEV_NAME_PATT); +	if (!lib->ubi_dev) +		goto out_error; + +	lib->ubi_version = mkpath(lib->sysfs_ubi, UBI_VER); +	if (!lib->ubi_version) +		goto out_error; + +	lib->dev_dev = mkpath(lib->ubi_dev, DEV_DEV); +	if (!lib->dev_dev) +		goto out_error; + +	lib->dev_avail_ebs = mkpath(lib->ubi_dev, DEV_AVAIL_EBS); +	if (!lib->dev_avail_ebs) +		goto out_error; + +	lib->dev_total_ebs = mkpath(lib->ubi_dev, DEV_TOTAL_EBS); +	if (!lib->dev_total_ebs) +		goto out_error; + +	lib->dev_bad_count = mkpath(lib->ubi_dev, DEV_BAD_COUNT); +	if (!lib->dev_bad_count) +		goto out_error; + +	lib->dev_eb_size = mkpath(lib->ubi_dev, DEV_EB_SIZE); +	if (!lib->dev_eb_size) +		goto out_error; + +	lib->dev_max_ec = mkpath(lib->ubi_dev, DEV_MAX_EC); +	if (!lib->dev_max_ec) +		goto out_error; + +	lib->dev_bad_rsvd = mkpath(lib->ubi_dev, DEV_MAX_RSVD); +	if (!lib->dev_bad_rsvd) +		goto out_error; + +	lib->dev_max_vols = mkpath(lib->ubi_dev, DEV_MAX_VOLS); +	if (!lib->dev_max_vols) +		goto out_error; + +	lib->dev_min_io_size = mkpath(lib->ubi_dev, DEV_MIN_IO_SIZE); +	if (!lib->dev_min_io_size) +		goto out_error; + +	lib->dev_mtd_num = mkpath(lib->ubi_dev, DEV_MTD_NUM); +	if (!lib->dev_mtd_num) +		goto out_error; + +	lib->ubi_vol = mkpath(lib->sysfs_ubi, UBI_VOL_NAME_PATT); +	if (!lib->ubi_vol) +		goto out_error; + +	lib->vol_type = mkpath(lib->ubi_vol, VOL_TYPE); +	if (!lib->vol_type) +		goto out_error; + +	lib->vol_dev = mkpath(lib->ubi_vol, VOL_DEV); +	if (!lib->vol_dev) +		goto out_error; + +	lib->vol_alignment = mkpath(lib->ubi_vol, VOL_ALIGNMENT); +	if (!lib->vol_alignment) +		goto out_error; + +	lib->vol_data_bytes = mkpath(lib->ubi_vol, VOL_DATA_BYTES); +	if (!lib->vol_data_bytes) +		goto out_error; + +	lib->vol_rsvd_ebs = mkpath(lib->ubi_vol, VOL_RSVD_EBS); +	if (!lib->vol_rsvd_ebs) +		goto out_error; + +	lib->vol_eb_size = mkpath(lib->ubi_vol, VOL_EB_SIZE); +	if (!lib->vol_eb_size) +		goto out_error; + +	lib->vol_corrupted = mkpath(lib->ubi_vol, VOL_CORRUPTED); +	if (!lib->vol_corrupted) +		goto out_error; + +	lib->vol_name = mkpath(lib->ubi_vol, VOL_NAME); +	if (!lib->vol_name) +		goto out_error; + +	if (read_positive_int(lib->ubi_version, &version)) +		goto out_error; +	if (version != LIBUBI_UBI_VERSION) { +		errmsg("this library was made for UBI version %d, but UBI " +		       "version %d is detected\n", LIBUBI_UBI_VERSION, version); +		goto out_error; +	} + +	return lib; + +out_error: +	libubi_close((libubi_t)lib); +	return NULL; +} + +void libubi_close(libubi_t desc) +{ +	struct libubi *lib = (struct libubi *)desc; + +	free(lib->vol_name); +	free(lib->vol_corrupted); +	free(lib->vol_eb_size); +	free(lib->vol_rsvd_ebs); +	free(lib->vol_data_bytes); +	free(lib->vol_alignment); +	free(lib->vol_dev); +	free(lib->vol_type); +	free(lib->ubi_vol); +	free(lib->dev_mtd_num); +	free(lib->dev_min_io_size); +	free(lib->dev_max_vols); +	free(lib->dev_bad_rsvd); +	free(lib->dev_max_ec); +	free(lib->dev_eb_size); +	free(lib->dev_bad_count); +	free(lib->dev_total_ebs); +	free(lib->dev_avail_ebs); +	free(lib->dev_dev); +	free(lib->ubi_version); +	free(lib->ubi_dev); +	free(lib->sysfs_ubi); +	free(lib->ctrl_dev); +	free(lib->sysfs_ctrl); +	free(lib); +} + +/** + * do_attach - perform the actual attach operation. + * @node: name of the UBI control character device node + * @r: attach request + * + * This function performs the actual UBI attach operation. Returns %0 in case of + * success and %-1 in case of failure. @r->ubi_num contains newly created UBI + * device number. + */ +static int do_attach(const char *node, const struct ubi_attach_req *r) +{ +	int fd, ret; + +	fd = open(node, O_RDONLY); +	if (fd == -1) +		return sys_errmsg("cannot open \"%s\"", node); + +	ret = ioctl(fd, UBI_IOCATT, r); +	close(fd); +	if (ret == -1) +		return -1; + +#ifdef UDEV_SETTLE_HACK +//	if (system("udevsettle") == -1) +//		return -1; +	usleep(100000); +#endif +	return ret; +} + +#ifndef MTD_CHAR_MAJOR +/* + * This is taken from kernel <linux/mtd/mtd.h> and is unlikely to change anytime + * soon. + */ +#define MTD_CHAR_MAJOR 90 +#endif + +/** + * mtd_node_to_num - converts device node to MTD number. + * @mtd_dev_node: path to device node to convert + * + * This function converts given @mtd_dev_node to MTD device number. + * @mtd_dev_node should contain path to the MTD device node. Returns MTD device + * number in case of success and %-1 in case of failure (errno is set). + */ +static int mtd_node_to_num(const char *mtd_dev_node) +{ +	int major, minor; +	struct stat sb; + +	if (stat(mtd_dev_node, &sb) < 0) +		return sys_errmsg("cannot stat \"%s\"", mtd_dev_node); + +	if (!S_ISCHR(sb.st_mode)) { +		errno = EINVAL; +		return sys_errmsg("\"%s\" is not a character device", +				  mtd_dev_node); +	} + +	major = major(sb.st_rdev); +	minor = minor(sb.st_rdev); + +	if (major != MTD_CHAR_MAJOR) { +		errno = EINVAL; +		return sys_errmsg("\"%s\" is not an MTD device", mtd_dev_node); +	} + +	return minor / 2; +} + +int ubi_attach(libubi_t desc, const char *node, struct ubi_attach_request *req) +{ +	struct ubi_attach_req r; +	int ret; + +	(void)desc; + +	if (req->mtd_dev_node) { +		/* +		 * User has passed path to device node. Lets find out MTD +		 * device number of the device and update req->mtd_num with it +		 */ +		req->mtd_num = mtd_node_to_num(req->mtd_dev_node); +		if (req->mtd_num == -1) +			return -1; +	} + +	memset(&r, 0, sizeof(struct ubi_attach_req)); +	r.ubi_num = req->dev_num; +	r.mtd_num = req->mtd_num; +	r.vid_hdr_offset = req->vid_hdr_offset; + +	if (req->max_beb_per1024) { +		/* +		 * We first have to check if the running kernel supports the +		 * 'max_beb_per1024' parameter. To do this, we invoke the +		 * "attach" ioctl 2 times: first with incorrect value %-1 of +		 * 'max_beb_per1024'. +		 * +		 * If the ioctl succeeds, it means that the kernel doesn't +		 * support the feature and just ignored our 'max_beb_per1024' +		 * value. +		 * +		 * If the ioctl returns -EINVAL, we assume this is because +		 * 'max_beb_per1024' was set to -1, and we invoke the ioctl for +		 * the second time with the 'max_beb_per1024' value. +		 */ +		r.max_beb_per1024 = -1; +		ret = do_attach(node, &r); +		if (ret == 0) { +			req->dev_num = r.ubi_num; +			/* +			 * The call succeeded. It means that the kernel ignored +			 * 'max_beb_per1024' parameter. +			 */ +			return 1; +		} else if (errno != EINVAL) +			return ret; +	} + +	r.max_beb_per1024 = req->max_beb_per1024; + +	ret = do_attach(node, &r); +	if (ret == 0) +		req->dev_num = r.ubi_num; + +	return ret; +} + +int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num) +{ +	int ret, ubi_dev; + +	ret = mtd_num2ubi_dev(desc, mtd_num, &ubi_dev); +	if (ret == -1) { +		errno = ENODEV; +		return ret; +	} + +	return ubi_remove_dev(desc, node, ubi_dev); +} + +int ubi_detach(libubi_t desc, const char *node, const char *mtd_dev_node) +{ +	int mtd_num; + +	if (!mtd_dev_node) { +		errno = EINVAL; +		return -1; +	} + +	mtd_num = mtd_node_to_num(mtd_dev_node); +	if (mtd_num == -1) +		return -1; + +	return ubi_detach_mtd(desc, node, mtd_num); +} + +int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev) +{ +	int fd, ret; + +	desc = desc; + +	fd = open(node, O_RDONLY); +	if (fd == -1) +		return sys_errmsg("cannot open \"%s\"", node); +	ret = ioctl(fd, UBI_IOCDET, &ubi_dev); +	if (ret == -1) +		goto out_close; + +#ifdef UDEV_SETTLE_HACK +//	if (system("udevsettle") == -1) +//		return -1; +	usleep(100000); +#endif + +out_close: +	close(fd); +	return ret; +} + +int ubi_probe_node(libubi_t desc, const char *node) +{ +	struct stat st; +	struct ubi_info info; +	int i, fd, major, minor; +	struct libubi *lib = (struct libubi *)desc; +	char file[strlen(lib->ubi_vol) + 100]; + +	if (stat(node, &st)) +		return sys_errmsg("cannot get information about \"%s\"", node); + +	if (!S_ISCHR(st.st_mode)) { +		errmsg("\"%s\" is not a character device", node); +		errno = EINVAL; +		return -1; +	} + +	major = major(st.st_rdev); +	minor = minor(st.st_rdev); + +	if (ubi_get_info((libubi_t *)lib, &info)) +		return -1; + +	for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { +		int major1, minor1, ret; + +		ret = dev_get_major(lib, i, &major1, &minor1); +		if (ret) { +			if (errno == ENOENT) +				continue; +			if (!errno) +				goto out_not_ubi; +			return -1; +		} + +		if (major1 == major) +			break; +	} + +	if (i > info.highest_dev_num) +		goto out_not_ubi; + +	if (minor == 0) +		return 1; + +	/* This is supposdely an UBI volume device node */ +	sprintf(file, lib->ubi_vol, i, minor - 1); +	fd = open(file, O_RDONLY); +	if (fd == -1) +		goto out_not_ubi; + +        if (close(fd)) +		sys_errmsg("close failed on \"%s\"", file); + +	return 2; + +out_not_ubi: +	errmsg("\"%s\" has major:minor %d:%d, but this does not correspond to " +	       "any existing UBI device or volume", node, major, minor); +	errno = ENODEV; +	return -1; +} + +int ubi_get_info(libubi_t desc, struct ubi_info *info) +{ +	DIR *sysfs_ubi; +	struct dirent *dirent; +	struct libubi *lib = (struct libubi *)desc; + +	memset(info, 0, sizeof(struct ubi_info)); + +	if (read_major(lib->ctrl_dev, &info->ctrl_major, &info->ctrl_minor)) { +		/* +		 * Older UBI versions did not have control device, so we do not +		 * panic here for compatibility reasons. May be few years later +		 * we could return -1 here, but for now just set major:minor to +		 * -1. +		 */ +		info->ctrl_major = info->ctrl_minor = -1; +	} + +	/* +	 * We have to scan the UBI sysfs directory to identify how many UBI +	 * devices are present. +	 */ +	sysfs_ubi = opendir(lib->sysfs_ubi); +	if (!sysfs_ubi) +		return -1; + +	info->lowest_dev_num = INT_MAX; +	while (1) { +		int dev_num, ret; +		char tmp_buf[256]; + +		errno = 0; +		dirent = readdir(sysfs_ubi); +		if (!dirent) +			break; + +		if (strlen(dirent->d_name) >= 255) { +			errmsg("invalid entry in %s: \"%s\"", +			       lib->sysfs_ubi, dirent->d_name); +			errno = EINVAL; +			goto out_close; +		} + +		ret = sscanf(dirent->d_name, UBI_DEV_NAME_PATT"%s", +			     &dev_num, tmp_buf); +		if (ret == 1) { +			info->dev_count += 1; +			if (dev_num > info->highest_dev_num) +				info->highest_dev_num = dev_num; +			if (dev_num < info->lowest_dev_num) +				info->lowest_dev_num = dev_num; +		} +	} + +	if (!dirent && errno) { +		sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); +		goto out_close; +	} + +	if (closedir(sysfs_ubi)) +		return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); + +	if (info->lowest_dev_num == INT_MAX) +		info->lowest_dev_num = 0; + +	if (read_positive_int(lib->ubi_version, &info->version)) +		return -1; + +	return 0; + +out_close: +	closedir(sysfs_ubi); +	return -1; +} + +int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req) +{ +	int fd, ret; +	struct ubi_mkvol_req r; +	size_t n; + +	memset(&r, 0, sizeof(struct ubi_mkvol_req)); + +	desc = desc; +	r.vol_id = req->vol_id; +	r.alignment = req->alignment; +	r.bytes = req->bytes; +	r.vol_type = req->vol_type; + +	n = strlen(req->name); +	if (n > UBI_MAX_VOLUME_NAME) +		return -1; + +	strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1); +	r.name_len = n; + +	desc = desc; +	fd = open(node, O_RDONLY); +	if (fd == -1) +		return sys_errmsg("cannot open \"%s\"", node); + +	ret = ioctl(fd, UBI_IOCMKVOL, &r); +	if (ret == -1) { +		close(fd); +		return ret; +	} + +	close(fd); +	req->vol_id = r.vol_id; + +#ifdef UDEV_SETTLE_HACK +//	if (system("udevsettle") == -1) +//		return -1; +	usleep(100000); +#endif + +	return 0; +} + +int ubi_rmvol(libubi_t desc, const char *node, int vol_id) +{ +	int fd, ret; + +	desc = desc; +	fd = open(node, O_RDONLY); +	if (fd == -1) +		return sys_errmsg("cannot open \"%s\"", node); + +	ret = ioctl(fd, UBI_IOCRMVOL, &vol_id); +	if (ret == -1) { +		close(fd); +		return ret; +	} + +	close(fd); + +#ifdef UDEV_SETTLE_HACK +//	if (system("udevsettle") == -1) +//		return -1; +	usleep(100000); +#endif + +	return 0; +} + +int ubi_rnvols(libubi_t desc, const char *node, struct ubi_rnvol_req *rnvol) +{ +	int fd, ret; + +	desc = desc; +	fd = open(node, O_RDONLY); +	if (fd == -1) +		return -1; + +	ret = ioctl(fd, UBI_IOCRNVOL, rnvol); +	if (ret == -1) { +		close(fd); +		return ret; +	} + +	close(fd); + +#ifdef UDEV_SETTLE_HACK +//	if (system("udevsettle") == -1) +//		return -1; +	usleep(100000); +#endif + +	return 0; +} + +int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes) +{ +	int fd, ret; +	struct ubi_rsvol_req req; + +	desc = desc; +	fd = open(node, O_RDONLY); +	if (fd == -1) +		return sys_errmsg("cannot open \"%s\"", node); + +	req.bytes = bytes; +	req.vol_id = vol_id; + +	ret = ioctl(fd, UBI_IOCRSVOL, &req); +	close(fd); +	return ret; +} + +int ubi_vol_block_create(int fd) +{ +	return ioctl(fd, UBI_IOCVOLCRBLK); +} + +int ubi_vol_block_remove(int fd) +{ +	return ioctl(fd, UBI_IOCVOLRMBLK); +} + +int ubi_update_start(libubi_t desc, int fd, long long bytes) +{ +	desc = desc; +	if (ioctl(fd, UBI_IOCVOLUP, &bytes)) +		return -1; +	return 0; +} + +int ubi_leb_change_start(libubi_t desc, int fd, int lnum, int bytes) +{ +	struct ubi_leb_change_req req; + +	desc = desc; +	memset(&req, 0, sizeof(struct ubi_leb_change_req)); +	req.lnum = lnum; +	req.bytes = bytes; +	req.dtype = 3; + +	if (ioctl(fd, UBI_IOCEBCH, &req)) +		return -1; +	return 0; +} + +int ubi_dev_present(libubi_t desc, int dev_num) +{ +	struct stat st; +	struct libubi *lib = (struct libubi *)desc; +	char file[strlen(lib->ubi_dev) + 50]; + +	sprintf(file, lib->ubi_dev, dev_num); +	return !stat(file, &st); +} + +int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info) +{ +	DIR *sysfs_ubi; +	struct dirent *dirent; +	struct libubi *lib = (struct libubi *)desc; + +	memset(info, 0, sizeof(struct ubi_dev_info)); +	info->dev_num = dev_num; + +	if (!ubi_dev_present(desc, dev_num)) +		return -1; + +	sysfs_ubi = opendir(lib->sysfs_ubi); +	if (!sysfs_ubi) +		return -1; + +	info->lowest_vol_id = INT_MAX; + +	while (1) { +		int vol_id, ret, devno; +		char tmp_buf[256]; + +		errno = 0; +		dirent = readdir(sysfs_ubi); +		if (!dirent) +			break; + +		if (strlen(dirent->d_name) >= 255) { +			errmsg("invalid entry in %s: \"%s\"", +			       lib->sysfs_ubi, dirent->d_name); +			goto out_close; +		} + +		ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT"%s", &devno, &vol_id, tmp_buf); +		if (ret == 2 && devno == dev_num) { +			info->vol_count += 1; +			if (vol_id > info->highest_vol_id) +				info->highest_vol_id = vol_id; +			if (vol_id < info->lowest_vol_id) +				info->lowest_vol_id = vol_id; +		} +	} + +	if (!dirent && errno) { +		sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); +		goto out_close; +	} + +	if (closedir(sysfs_ubi)) +		return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); + +	if (info->lowest_vol_id == INT_MAX) +		info->lowest_vol_id = 0; + +	if (dev_get_major(lib, dev_num, &info->major, &info->minor)) +		return -1; + +	if (dev_read_int(lib->dev_mtd_num, dev_num, &info->mtd_num)) +		return -1; +	if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_lebs)) +		return -1; +	if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_lebs)) +		return -1; +	if (dev_read_int(lib->dev_bad_count, dev_num, &info->bad_count)) +		return -1; +	if (dev_read_int(lib->dev_eb_size, dev_num, &info->leb_size)) +		return -1; +	if (dev_read_int(lib->dev_bad_rsvd, dev_num, &info->bad_rsvd)) +		return -1; +	if (dev_read_ll(lib->dev_max_ec, dev_num, &info->max_ec)) +		return -1; +	if (dev_read_int(lib->dev_max_vols, dev_num, &info->max_vol_count)) +		return -1; +	if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size)) +		return -1; + +	info->avail_bytes = (long long)info->avail_lebs * info->leb_size; +	info->total_bytes = (long long)info->total_lebs * info->leb_size; + +	return 0; + +out_close: +	closedir(sysfs_ubi); +	return -1; +} + +int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info) +{ +	int err, dev_num; +	struct libubi *lib = (struct libubi *)desc; + +	err = ubi_probe_node(desc, node); +	if (err != 1) { +		if (err == 2) +			errno = ENODEV; +		return -1; +	} + +	if (dev_node2num(lib, node, &dev_num)) +		return -1; + +	return ubi_get_dev_info1(desc, dev_num, info); +} + +int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, +		      struct ubi_vol_info *info) +{ +	int ret; +	struct libubi *lib = (struct libubi *)desc; +	char buf[50]; + +	memset(info, 0, sizeof(struct ubi_vol_info)); +	info->dev_num = dev_num; +	info->vol_id = vol_id; + +	if (vol_get_major(lib, dev_num, vol_id, &info->major, &info->minor)) +		return -1; + +	ret = vol_read_data(lib->vol_type, dev_num, vol_id, buf, 50); +	if (ret < 0) +		return -1; + +	if (strncmp(buf, "static\n", ret) == 0) +		info->type = UBI_STATIC_VOLUME; +	else if (strncmp(buf, "dynamic\n", ret) == 0) +		info->type = UBI_DYNAMIC_VOLUME; +	else { +		errmsg("bad value at \"%s\"", buf); +		errno = EINVAL; +		return -1; +	} + +	ret = vol_read_int(lib->vol_alignment, dev_num, vol_id, +			   &info->alignment); +	if (ret) +		return -1; +	ret = vol_read_ll(lib->vol_data_bytes, dev_num, vol_id, +			  &info->data_bytes); +	if (ret) +		return -1; +	ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_lebs); +	if (ret) +		return -1; +	ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->leb_size); +	if (ret) +		return -1; +	ret = vol_read_int(lib->vol_corrupted, dev_num, vol_id, +			   &info->corrupted); +	if (ret) +		return -1; +	info->rsvd_bytes = (long long)info->leb_size * info->rsvd_lebs; + +	ret = vol_read_data(lib->vol_name, dev_num, vol_id, &info->name, +			    UBI_VOL_NAME_MAX + 2); +	if (ret < 0) +		return -1; + +	info->name[ret - 1] = '\0'; +	return 0; +} + +int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info) +{ +	int err, vol_id, dev_num; +	struct libubi *lib = (struct libubi *)desc; + +	err = ubi_probe_node(desc, node); +	if (err != 2) { +		if (err == 1) +			errno = ENODEV; +		return -1; +	} + +	if (vol_node2nums(lib, node, &dev_num, &vol_id)) +		return -1; + +	return ubi_get_vol_info1(desc, dev_num, vol_id, info); +} + +int ubi_get_vol_info1_nm(libubi_t desc, int dev_num, const char *name, +			 struct ubi_vol_info *info) +{ +	int i, err; +	unsigned int nlen = strlen(name); +	struct ubi_dev_info dev_info; + +	if (nlen == 0) { +		errmsg("bad \"name\" input parameter"); +		errno = EINVAL; +		return -1; +	} + +	err = ubi_get_dev_info1(desc, dev_num, &dev_info); +	if (err) +		return err; + +	for (i = dev_info.lowest_vol_id; +	     i <= dev_info.highest_vol_id; i++) { +		err = ubi_get_vol_info1(desc, dev_num, i, info); +		if (err == -1) { +			if (errno == ENOENT) +				continue; +			return -1; +		} + +		if (nlen == strlen(info->name) && !strcmp(name, info->name)) +			return 0; +	} + +	errno = ENOENT; +	return -1; +} + +int ubi_set_property(int fd, uint8_t property, uint64_t value) +{ +	struct ubi_set_vol_prop_req r; + +	memset(&r, 0, sizeof(struct ubi_set_vol_prop_req)); +	r.property = property; +	r.value = value; + +	return ioctl(fd, UBI_IOCSETVOLPROP, &r); +} + +int ubi_leb_unmap(int fd, int lnum) +{ +	return ioctl(fd, UBI_IOCEBUNMAP, &lnum); +} + +int ubi_is_mapped(int fd, int lnum) +{ +	return ioctl(fd, UBI_IOCEBISMAP, &lnum); +} diff --git a/lib/libubi_int.h b/lib/libubi_int.h new file mode 100644 index 0000000..86ce18a --- /dev/null +++ b/lib/libubi_int.h @@ -0,0 +1,134 @@ +/* + * 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: Artem Bityutskiy + * + * UBI (Unsorted Block Images) library. + */ + +#ifndef __LIBUBI_INT_H__ +#define __LIBUBI_INT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The below are pre-define UBI file and directory names. + * + * Note, older kernels put 'ubiX_Y' directories straight to '/sys/class/ubi/'. + * New kernels puts 'ubiX_Y' directories to '/sys/class/ubi/ubiX/', which is + * saner. And for compatibility reasons it also puts symlinks to 'ubiX_Y' + * directories to '/sys/class/ubi/'. For now libubi assumes old layout. + */ + +#ifndef SYSFS_ROOT +#define SYSFS_ROOT	  "/sys" +#endif +#define SYSFS_UBI         "class/ubi" +#define SYSFS_CTRL        "class/misc/ubi_ctrl/" + +#define CTRL_DEV          "dev" + +#define UBI_VER           "version" +#define UBI_DEV_NAME_PATT "ubi%d" + +#define DEV_DEV           "dev" +#define DEV_AVAIL_EBS     "avail_eraseblocks" +#define DEV_TOTAL_EBS     "total_eraseblocks" +#define DEV_BAD_COUNT     "bad_peb_count" +#define DEV_EB_SIZE       "eraseblock_size" +#define DEV_MAX_EC        "max_ec" +#define DEV_MAX_RSVD      "reserved_for_bad" +#define DEV_MAX_VOLS      "max_vol_count" +#define DEV_MIN_IO_SIZE   "min_io_size" +#define DEV_MTD_NUM       "mtd_num" + +#define UBI_VOL_NAME_PATT "ubi%d_%d" +#define VOL_TYPE          "type" +#define VOL_DEV           "dev" +#define VOL_ALIGNMENT     "alignment" +#define VOL_DATA_BYTES    "data_bytes" +#define VOL_RSVD_EBS      "reserved_ebs" +#define VOL_EB_SIZE       "usable_eb_size" +#define VOL_CORRUPTED     "corrupted" +#define VOL_NAME          "name" + +/** + * libubi - UBI library description data structure. + * @sysfs: sysfs file system path + * @sysfs_ctrl: UBI control device directory in sysfs + * @ctrl_dev: UBI control device major/minor numbers sysfs file + * @sysfs_ubi: UBI directory in sysfs + * @ubi_dev: UBI device sysfs directory pattern + * @ubi_version: UBI version file sysfs path + * @dev_dev: UBI device major/minor numbers file pattern + * @dev_avail_ebs: count of available eraseblocks sysfs path pattern + * @dev_total_ebs: total eraseblocks count sysfs path pattern + * @dev_bad_count: count of bad eraseblocks sysfs path pattern + * @dev_eb_size: size of UBI device's eraseblocks sysfs path pattern + * @dev_max_ec: maximum erase counter sysfs path pattern + * @dev_bad_rsvd: count of physical eraseblock reserved for bad eraseblocks + *                handling + * @dev_max_vols: maximum volumes number count sysfs path pattern + * @dev_min_io_size: minimum I/O unit size sysfs path pattern + * @dev_mtd_num: MTD device number + * @ubi_vol: UBI volume sysfs directory pattern + * @vol_type: volume type sysfs path pattern + * @vol_dev: volume major/minor numbers file pattern + * @vol_alignment: volume alignment sysfs path pattern + * @vol_data_bytes: volume data size sysfs path pattern + * @vol_rsvd_ebs: volume reserved size sysfs path pattern + * @vol_eb_size: volume eraseblock size sysfs path pattern + * @vol_corrupted: volume corruption flag sysfs path pattern + * @vol_name: volume name sysfs path pattern + */ +struct libubi +{ +	char *sysfs; +	char *sysfs_ctrl; +	char *ctrl_dev; +	char *sysfs_ubi; +	char *ubi_dev; +	char *ubi_version; +	char *dev_dev; +	char *dev_avail_ebs; +	char *dev_total_ebs; +	char *dev_bad_count; +	char *dev_eb_size; +	char *dev_max_ec; +	char *dev_bad_rsvd; +	char *dev_max_vols; +	char *dev_min_io_size; +	char *dev_mtd_num; +	char *ubi_vol; +	char *vol_type; +	char *vol_dev; +	char *vol_alignment; +	char *vol_data_bytes; +	char *vol_rsvd_ebs; +	char *vol_eb_size; +	char *vol_corrupted; +	char *vol_name; +	char *vol_max_count; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* !__LIBUBI_INT_H__ */ diff --git a/lib/libubigen.c b/lib/libubigen.c new file mode 100644 index 0000000..d2a949b --- /dev/null +++ b/lib/libubigen.c @@ -0,0 +1,315 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (C) 2008 Nokia Corporation + * + * 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. + */ + +/* + * Generating UBI images. + * + * Authors: Oliver Lohmann + *          Artem Bityutskiy + */ + +#define PROGRAM_NAME "libubigen" + +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> +#include <string.h> + +#include <mtd/ubi-media.h> +#include <mtd_swab.h> +#include <libubigen.h> +#include <crc32.h> +#include "common.h" + +void ubigen_info_init(struct ubigen_info *ui, int peb_size, int min_io_size, +		      int subpage_size, int vid_hdr_offs, int ubi_ver, +		      uint32_t image_seq) +{ +	if (!vid_hdr_offs) { +		vid_hdr_offs = UBI_EC_HDR_SIZE + subpage_size - 1; +		vid_hdr_offs /= subpage_size; +		vid_hdr_offs *= subpage_size; +	} + +	ui->peb_size = peb_size; +	ui->min_io_size = min_io_size; +	ui->vid_hdr_offs = vid_hdr_offs; +	ui->data_offs = vid_hdr_offs + UBI_VID_HDR_SIZE + min_io_size - 1; +	ui->data_offs /= min_io_size; +	ui->data_offs *= min_io_size; +	ui->leb_size = peb_size - ui->data_offs; +	ui->ubi_ver = ubi_ver; +	ui->image_seq = image_seq; + +	ui->max_volumes = ui->leb_size / UBI_VTBL_RECORD_SIZE; +	if (ui->max_volumes > UBI_MAX_VOLUMES) +		ui->max_volumes = UBI_MAX_VOLUMES; +	ui->vtbl_size = ui->max_volumes * UBI_VTBL_RECORD_SIZE; +} + +struct ubi_vtbl_record *ubigen_create_empty_vtbl(const struct ubigen_info *ui) +{ +	struct ubi_vtbl_record *vtbl; +	int i; + +	vtbl = calloc(1, ui->vtbl_size); +	if (!vtbl) { +		sys_errmsg("cannot allocate %d bytes of memory", ui->vtbl_size); +		return NULL; +	} + +	for (i = 0; i < ui->max_volumes; i++) { +		uint32_t crc = mtd_crc32(UBI_CRC32_INIT, &vtbl[i], +				     UBI_VTBL_RECORD_SIZE_CRC); +		vtbl[i].crc = cpu_to_be32(crc); +	} + +	return vtbl; +} + +int ubigen_add_volume(const struct ubigen_info *ui, +		      const struct ubigen_vol_info *vi, +		      struct ubi_vtbl_record *vtbl) +{ +	struct ubi_vtbl_record *vtbl_rec = &vtbl[vi->id]; +	uint32_t tmp; + +	if (vi->id >= ui->max_volumes) { +		errmsg("too high volume id %d, max. volumes is %d", +		       vi->id, ui->max_volumes); +		errno = EINVAL; +		return -1; +	} + +	if (vi->alignment >= ui->leb_size) { +		errmsg("too large alignment %d, max is %d (LEB size)", +		       vi->alignment, ui->leb_size); +		errno = EINVAL; +		return -1; +	} + +	memset(vtbl_rec, 0, sizeof(struct ubi_vtbl_record)); +	tmp = (vi->bytes + ui->leb_size - 1) / ui->leb_size; +	vtbl_rec->reserved_pebs = cpu_to_be32(tmp); +	vtbl_rec->alignment = cpu_to_be32(vi->alignment); +	vtbl_rec->vol_type = vi->type; +	tmp = ui->leb_size % vi->alignment; +	vtbl_rec->data_pad = cpu_to_be32(tmp); +	vtbl_rec->flags = vi->flags; + +	memcpy(vtbl_rec->name, vi->name, vi->name_len); +	vtbl_rec->name[vi->name_len] = '\0'; +	vtbl_rec->name_len = cpu_to_be16(vi->name_len); + +	tmp = mtd_crc32(UBI_CRC32_INIT, vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC); +	vtbl_rec->crc =	 cpu_to_be32(tmp); +	return 0; +} + +void ubigen_init_ec_hdr(const struct ubigen_info *ui, +		        struct ubi_ec_hdr *hdr, long long ec) +{ +	uint32_t crc; + +	memset(hdr, 0, sizeof(struct ubi_ec_hdr)); + +	hdr->magic = cpu_to_be32(UBI_EC_HDR_MAGIC); +	hdr->version = ui->ubi_ver; +	hdr->ec = cpu_to_be64(ec); +	hdr->vid_hdr_offset = cpu_to_be32(ui->vid_hdr_offs); +	hdr->data_offset = cpu_to_be32(ui->data_offs); +	hdr->image_seq = cpu_to_be32(ui->image_seq); + +	crc = mtd_crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC); +	hdr->hdr_crc = cpu_to_be32(crc); +} + +void ubigen_init_vid_hdr(const struct ubigen_info *ui, +			 const struct ubigen_vol_info *vi, +			 struct ubi_vid_hdr *hdr, int lnum, +			 const void *data, int data_size) +{ +	uint32_t crc; + +	memset(hdr, 0, sizeof(struct ubi_vid_hdr)); + +	hdr->magic = cpu_to_be32(UBI_VID_HDR_MAGIC); +	hdr->version = ui->ubi_ver; +	hdr->vol_type = vi->type; +	hdr->vol_id = cpu_to_be32(vi->id); +	hdr->lnum = cpu_to_be32(lnum); +	hdr->data_pad = cpu_to_be32(vi->data_pad); +	hdr->compat = vi->compat; + +	if (vi->type == UBI_VID_STATIC) { +		hdr->data_size = cpu_to_be32(data_size); +		hdr->used_ebs = cpu_to_be32(vi->used_ebs); +		crc = mtd_crc32(UBI_CRC32_INIT, data, data_size); +		hdr->data_crc = cpu_to_be32(crc); +	} + +	crc = mtd_crc32(UBI_CRC32_INIT, hdr, UBI_VID_HDR_SIZE_CRC); +	hdr->hdr_crc = cpu_to_be32(crc); +} + +int ubigen_write_volume(const struct ubigen_info *ui, +			const struct ubigen_vol_info *vi, long long ec, +			long long bytes, int in, int out) +{ +	int len = vi->usable_leb_size, rd, lnum = 0; +	char *inbuf, *outbuf; + +	if (vi->id >= ui->max_volumes) { +		errmsg("too high volume id %d, max. volumes is %d", +		       vi->id, ui->max_volumes); +		errno = EINVAL; +		return -1; +	} + +	if (vi->alignment >= ui->leb_size) { +		errmsg("too large alignment %d, max is %d (LEB size)", +		       vi->alignment, ui->leb_size); +		errno = EINVAL; +		return -1; +	} + +	inbuf = malloc(ui->leb_size); +	if (!inbuf) +		return sys_errmsg("cannot allocate %d bytes of memory", +				  ui->leb_size); +	outbuf = malloc(ui->peb_size); +	if (!outbuf) { +		sys_errmsg("cannot allocate %d bytes of memory", ui->peb_size); +		goto out_free; +	} + +	memset(outbuf, 0xFF, ui->data_offs); +	ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec); + +	while (bytes) { +		int l; +		struct ubi_vid_hdr *vid_hdr; + +		if (bytes < len) +			len = bytes; +		bytes -= len; + +		l = len; +		do { +			rd = read(in, inbuf + len - l, l); +			if (rd != l) { +				sys_errmsg("cannot read %d bytes from the input file", l); +				goto out_free1; +			} + +			l -= rd; +		} while (l); + +		vid_hdr = (struct ubi_vid_hdr *)(&outbuf[ui->vid_hdr_offs]); +		ubigen_init_vid_hdr(ui, vi, vid_hdr, lnum, inbuf, len); + +		memcpy(outbuf + ui->data_offs, inbuf, len); +		memset(outbuf + ui->data_offs + len, 0xFF, +		       ui->peb_size - ui->data_offs - len); + +		if (write(out, outbuf, ui->peb_size) != ui->peb_size) { +			sys_errmsg("cannot write %d bytes to the output file", ui->peb_size); +			goto out_free1; +		} + +		lnum += 1; +	} + +	free(outbuf); +	free(inbuf); +	return 0; + +out_free1: +	free(outbuf); +out_free: +	free(inbuf); +	return -1; +} + +int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2, +			    long long ec1, long long ec2, +			    struct ubi_vtbl_record *vtbl, int fd) +{ +	int ret; +	struct ubigen_vol_info vi; +	char *outbuf; +	struct ubi_vid_hdr *vid_hdr; +	off_t seek; + +	vi.bytes = ui->leb_size * UBI_LAYOUT_VOLUME_EBS; +	vi.id = UBI_LAYOUT_VOLUME_ID; +	vi.alignment = UBI_LAYOUT_VOLUME_ALIGN; +	vi.data_pad = ui->leb_size % UBI_LAYOUT_VOLUME_ALIGN; +	vi.usable_leb_size = ui->leb_size - vi.data_pad; +	vi.data_pad = ui->leb_size - vi.usable_leb_size; +	vi.type = UBI_LAYOUT_VOLUME_TYPE; +	vi.name = UBI_LAYOUT_VOLUME_NAME; +	vi.name_len = strlen(UBI_LAYOUT_VOLUME_NAME); +	vi.compat = UBI_LAYOUT_VOLUME_COMPAT; + +	outbuf = malloc(ui->peb_size); +	if (!outbuf) +		return sys_errmsg("failed to allocate %d bytes", +				  ui->peb_size); + +	memset(outbuf, 0xFF, ui->data_offs); +	vid_hdr = (struct ubi_vid_hdr *)(&outbuf[ui->vid_hdr_offs]); +	memcpy(outbuf + ui->data_offs, vtbl, ui->vtbl_size); +	memset(outbuf + ui->data_offs + ui->vtbl_size, 0xFF, +	       ui->peb_size - ui->data_offs - ui->vtbl_size); + +	seek = (off_t) peb1 * ui->peb_size; +	if (lseek(fd, seek, SEEK_SET) != seek) { +		sys_errmsg("cannot seek output file"); +		goto out_free; +	} + +	ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec1); +	ubigen_init_vid_hdr(ui, &vi, vid_hdr, 0, NULL, 0); +	ret = write(fd, outbuf, ui->peb_size); +	if (ret != ui->peb_size) { +		sys_errmsg("cannot write %d bytes", ui->peb_size); +		goto out_free; +	} + +	seek = (off_t) peb2 * ui->peb_size; +	if (lseek(fd, seek, SEEK_SET) != seek) { +		sys_errmsg("cannot seek output file"); +		goto out_free; +	} +	ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec2); +	ubigen_init_vid_hdr(ui, &vi, vid_hdr, 1, NULL, 0); +	ret = write(fd, outbuf, ui->peb_size); +	if (ret != ui->peb_size) { +		sys_errmsg("cannot write %d bytes", ui->peb_size); +		goto out_free; +	} + +	free(outbuf); +	return 0; + +out_free: +	free(outbuf); +	return -1; +}  | 
