You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1718 lines
41 KiB
1718 lines
41 KiB
/* |
|
* Copyright (c) 2007 Vreixo Formoso |
|
* Copyright (c) 2007 Mario Danic |
|
* |
|
* This file is part of the libisofs project; you can redistribute it and/or |
|
* modify it under the terms of the GNU General Public License version 2 as |
|
* published by the Free Software Foundation. See COPYING file for details. |
|
*/ |
|
|
|
#include "util.h" |
|
#include "libisofs.h" |
|
#include "messages.h" |
|
#include "../version.h" |
|
|
|
#include <stdlib.h> |
|
#include <wchar.h> |
|
#include <string.h> |
|
#include <errno.h> |
|
#include <ctype.h> |
|
#include <stdio.h> |
|
#include <limits.h> |
|
#include <iconv.h> |
|
#include <locale.h> |
|
#include <langinfo.h> |
|
|
|
#include <unistd.h> |
|
|
|
/* if we don't have eaccess, we check file access by opening it */ |
|
#ifndef HAVE_EACCESS |
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
#include <fcntl.h> |
|
#endif |
|
|
|
|
|
/* Produce possibly inflationary error messages directly to stderr */ |
|
static int iso_iconv_debug = 0; |
|
|
|
|
|
struct iso_iconv_handle { |
|
int status; /* bit0= open , bit1= identical mapping */ |
|
iconv_t descr; |
|
}; |
|
|
|
|
|
/* |
|
@param flag bit0= shortcut by identical mapping is not allowed |
|
*/ |
|
static |
|
int iso_iconv_open(struct iso_iconv_handle *handle, |
|
char *tocode, char *fromcode, int flag) |
|
{ |
|
handle->status = 0; |
|
handle->descr = (iconv_t) -1; |
|
|
|
if (strcmp(tocode, fromcode) == 0 && !(flag & 1)) { |
|
handle->status = 1 | 2; |
|
return 1; |
|
} |
|
handle->descr = iconv_open(tocode, fromcode); |
|
if (handle->descr == (iconv_t) -1) { |
|
if (strlen(tocode) + strlen(fromcode) <= 160 && iso_iconv_debug) |
|
fprintf(stderr, |
|
"libisofs_DEBUG: iconv_open(\"%s\", \"%s\") failed: errno= %d %s\n", |
|
tocode, fromcode, errno, strerror(errno)); |
|
return 0; |
|
} |
|
handle->status = 1; |
|
return 1; |
|
} |
|
|
|
|
|
static |
|
size_t iso_iconv(struct iso_iconv_handle *handle, |
|
char **inbuf, size_t *inbytesleft, |
|
char **outbuf, size_t *outbytesleft, int flag) |
|
{ |
|
size_t ret; |
|
/* The build system might indicate iconv(,const char **inbuf,) by |
|
defining ICONV_CONST const |
|
*/ |
|
#ifndef ICONV_CONST |
|
#define ICONV_CONST |
|
#endif |
|
ICONV_CONST char **local_inbuf; |
|
|
|
local_inbuf = (ICONV_CONST char **) inbuf; |
|
|
|
if (!(handle->status & 1)) { |
|
if (iso_iconv_debug) |
|
fprintf(stderr, |
|
"libisofs_DEBUG: iso_iconv(): iso_iconv_handle not in open state\n"); |
|
return (size_t) -1; |
|
} |
|
if (handle->status & 2) { |
|
if (inbuf == NULL || outbuf == NULL) { |
|
null_buf:; |
|
if (iso_iconv_debug) |
|
fprintf(stderr, |
|
"libisofs_DEBUG: iso_iconv(): NULL buffers not allowed in shortcut mapping\n"); |
|
return (size_t) -1; |
|
} |
|
if (*inbuf == NULL || *outbuf == NULL) |
|
goto null_buf; |
|
while (*inbytesleft > 0 && *outbytesleft > 0) { |
|
*((*outbuf)++) = *((*inbuf)++); |
|
(*inbytesleft)--; |
|
(*outbytesleft)--; |
|
} |
|
if (*inbytesleft > 0 && *outbytesleft <= 0) |
|
return (size_t) -1; |
|
return (size_t) 0; |
|
} |
|
ret = iconv(handle->descr, local_inbuf, inbytesleft, outbuf, outbytesleft); |
|
if (ret == (size_t) -1) { |
|
if (iso_iconv_debug) |
|
fprintf(stderr, "libisofs_DEBUG: iconv() failed: errno= %d %s\n", |
|
errno, strerror(errno)); |
|
return (size_t) -1; |
|
} |
|
return ret; |
|
} |
|
|
|
|
|
static |
|
int iso_iconv_close(struct iso_iconv_handle *handle, int flag) |
|
{ |
|
int ret; |
|
|
|
if (!(handle->status & 1)) { |
|
if (iso_iconv_debug) |
|
fprintf(stderr, |
|
"libisofs_DEBUG: iso_iconv_close(): iso_iconv_handle not in open state\n"); |
|
return -1; |
|
} |
|
handle->status &= ~1; |
|
if (handle->status & 2) |
|
return 0; |
|
|
|
ret = iconv_close(handle->descr); |
|
if (ret == -1) { |
|
if (iso_iconv_debug) |
|
fprintf(stderr, |
|
"libisofs_DEBUG: iconv_close() failed: errno= %d %s\n", |
|
errno, strerror(errno)); |
|
return -1; |
|
} |
|
return ret; |
|
} |
|
|
|
|
|
int int_pow(int base, int power) |
|
{ |
|
int result = 1; |
|
while (--power >= 0) { |
|
result *= base; |
|
} |
|
return result; |
|
} |
|
|
|
/* This static variable can override the locale's charset by its getter |
|
function which should be used whenever the local character set name |
|
is to be inquired. I.e. instead of calling nl_langinfo(CODESET) directly. |
|
If the variable is empty then it forwards nl_langinfo(CODESET). |
|
*/ |
|
static char libisofs_local_charset[4096]= {""}; |
|
|
|
/* API function */ |
|
int iso_set_local_charset(char *name, int flag) |
|
{ |
|
if(strlen(name) >= sizeof(libisofs_local_charset)) |
|
return(0); |
|
strcpy(libisofs_local_charset, name); |
|
return 1; |
|
} |
|
|
|
/* API function */ |
|
char *iso_get_local_charset(int flag) |
|
{ |
|
if(libisofs_local_charset[0]) |
|
return libisofs_local_charset; |
|
return nl_langinfo(CODESET); |
|
} |
|
|
|
int strconv(const char *str, const char *icharset, const char *ocharset, |
|
char **output) |
|
{ |
|
size_t inbytes; |
|
size_t outbytes; |
|
size_t n; |
|
struct iso_iconv_handle conv; |
|
int conv_ret; |
|
|
|
char *out = NULL; |
|
char *src; |
|
char *ret; |
|
int retval; |
|
|
|
inbytes = strlen(str); |
|
outbytes = (inbytes + 1) * MB_LEN_MAX; |
|
out = calloc(outbytes, 1); |
|
if (out == NULL) { |
|
retval = ISO_OUT_OF_MEM; |
|
goto ex; |
|
} |
|
|
|
conv_ret = iso_iconv_open(&conv, (char *) ocharset, (char *) icharset, 0); |
|
if (conv_ret <= 0) { |
|
retval = ISO_CHARSET_CONV_ERROR; |
|
goto ex; |
|
} |
|
src = (char *)str; |
|
ret = (char *)out; |
|
n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); |
|
if (n == -1) { |
|
/* error */ |
|
iso_iconv_close(&conv, 0); |
|
retval = ISO_CHARSET_CONV_ERROR; |
|
goto ex; |
|
} |
|
*ret = '\0'; |
|
iso_iconv_close(&conv, 0); |
|
|
|
*output = malloc(ret - out + 1); |
|
if (*output == NULL) { |
|
retval = ISO_OUT_OF_MEM; |
|
goto ex; |
|
} |
|
memcpy(*output, out, ret - out + 1); |
|
retval = ISO_SUCCESS; |
|
ex:; |
|
if (out != NULL) |
|
free(out); |
|
return retval; |
|
} |
|
|
|
int strnconv(const char *str, const char *icharset, const char *ocharset, |
|
size_t len, char **output) |
|
{ |
|
size_t inbytes; |
|
size_t outbytes; |
|
size_t n; |
|
struct iso_iconv_handle conv; |
|
int conv_ret; |
|
char *out = NULL; |
|
char *src; |
|
char *ret; |
|
int retval; |
|
|
|
inbytes = len; |
|
outbytes = (inbytes + 1) * MB_LEN_MAX; |
|
out = calloc(outbytes, 1); |
|
if (out == NULL) { |
|
retval = ISO_OUT_OF_MEM; |
|
goto ex; |
|
} |
|
conv_ret = iso_iconv_open(&conv, (char *) ocharset, (char *) icharset, 0); |
|
if (conv_ret <= 0) { |
|
retval = ISO_CHARSET_CONV_ERROR; |
|
goto ex; |
|
} |
|
src = (char *)str; |
|
ret = (char *)out; |
|
n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); |
|
if (n == -1) { |
|
/* error */ |
|
iso_iconv_close(&conv, 0); |
|
retval = ISO_CHARSET_CONV_ERROR; |
|
goto ex; |
|
} |
|
*ret = '\0'; |
|
iso_iconv_close(&conv, 0); |
|
|
|
*output = malloc(ret - out + 1); |
|
if (*output == NULL) { |
|
retval = ISO_OUT_OF_MEM; |
|
goto ex; |
|
} |
|
memcpy(*output, out, ret - out + 1); |
|
retval = ISO_SUCCESS; |
|
ex:; |
|
if (out != NULL) |
|
free(out); |
|
return retval; |
|
} |
|
|
|
/** |
|
* Convert a str in a specified codeset to WCHAR_T. |
|
* The result must be free() when no more needed |
|
* |
|
* @return |
|
* 1 success, < 0 error |
|
*/ |
|
static |
|
int str2wchar(const char *icharset, const char *input, wchar_t **output) |
|
{ |
|
struct iso_iconv_handle conv; |
|
int conv_ret; |
|
|
|
/* That while loop smells like a potential show stopper */ |
|
size_t loop_counter = 0, loop_limit = 3; |
|
|
|
size_t inbytes; |
|
size_t outbytes; |
|
char *ret; |
|
char *src; |
|
wchar_t *wstr; |
|
size_t n; |
|
|
|
if (icharset == NULL || input == NULL || output == NULL) { |
|
return ISO_NULL_POINTER; |
|
} |
|
|
|
conv_ret = iso_iconv_open(&conv, "WCHAR_T", (char *) icharset, 0); |
|
if (conv_ret <= 0) { |
|
return ISO_CHARSET_CONV_ERROR; |
|
} |
|
|
|
inbytes = strlen(input); |
|
loop_limit = inbytes + 3; |
|
outbytes = (inbytes + 1) * sizeof(wchar_t); |
|
|
|
/* we are sure that numchars <= inbytes */ |
|
wstr = malloc(outbytes); |
|
if (wstr == NULL) { |
|
return ISO_OUT_OF_MEM; |
|
} |
|
ret = (char *)wstr; |
|
src = (char *)input; |
|
|
|
n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); |
|
while (n == -1) { |
|
|
|
if (errno == E2BIG) { |
|
/* error, should never occur */ |
|
goto conv_error; |
|
} else { |
|
wchar_t *wret; |
|
|
|
/* |
|
* Invalid input string charset. |
|
* This can happen if input is in fact encoded in a charset |
|
* different than icharset. |
|
* We can't do anything better than replace by "_" and continue. |
|
*/ |
|
inbytes--; |
|
src++; |
|
|
|
wret = (wchar_t*) ret; |
|
*wret++ = (wchar_t) '_'; |
|
ret = (char *) wret; |
|
outbytes -= sizeof(wchar_t); |
|
|
|
if (!inbytes) |
|
break; |
|
|
|
/* Just to appease my remorse about unclear loop ends */ |
|
loop_counter++; |
|
if (loop_counter > loop_limit) |
|
goto conv_error; |
|
n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); |
|
} |
|
} |
|
iso_iconv_close(&conv, 0); |
|
*( (wchar_t *)ret )='\0'; |
|
*output = wstr; |
|
return ISO_SUCCESS; |
|
|
|
conv_error:; |
|
iso_iconv_close(&conv, 0); |
|
free(wstr); |
|
return ISO_CHARSET_CONV_ERROR; |
|
} |
|
|
|
int str2ascii(const char *icharset, const char *input, char **output) |
|
{ |
|
int result; |
|
wchar_t *wsrc_; |
|
char *ret; |
|
char *ret_; |
|
char *src; |
|
struct iso_iconv_handle conv; |
|
int conv_ret; |
|
|
|
/* That while loop smells like a potential show stopper */ |
|
size_t loop_counter = 0, loop_limit = 3; |
|
|
|
/* Fallback in case that iconv() is too demanding for system */ |
|
unsigned char *cpt; |
|
|
|
size_t numchars; |
|
size_t outbytes; |
|
size_t inbytes; |
|
size_t n; |
|
|
|
|
|
if (icharset == NULL || input == NULL || output == NULL) { |
|
return ISO_NULL_POINTER; |
|
} |
|
|
|
/* convert the string to a wide character string. Note: outbytes |
|
* is in fact the number of characters in the string and doesn't |
|
* include the last NULL character. |
|
*/ |
|
result = str2wchar(icharset, input, &wsrc_); |
|
if (result < 0) { |
|
goto fallback; |
|
} |
|
src = (char *)wsrc_; |
|
numchars = wcslen(wsrc_); |
|
|
|
inbytes = numchars * sizeof(wchar_t); |
|
loop_limit = inbytes + 3; |
|
|
|
ret_ = malloc(numchars + 1); |
|
if (ret_ == NULL) { |
|
return ISO_OUT_OF_MEM; |
|
} |
|
outbytes = numchars; |
|
ret = ret_; |
|
|
|
/* initialize iconv */ |
|
conv_ret = iso_iconv_open(&conv, "ASCII", "WCHAR_T", 0); |
|
if (conv_ret <= 0) { |
|
free(wsrc_); |
|
free(ret_); |
|
goto fallback; |
|
} |
|
|
|
n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); |
|
while (n == -1) { |
|
/* The destination buffer is too small. Stops here. */ |
|
if (errno == E2BIG) |
|
break; |
|
|
|
/* An incomplete multi bytes sequence was found. We |
|
* can't do anything here. That's quite unlikely. */ |
|
if (errno == EINVAL) |
|
break; |
|
|
|
/* The last possible error is an invalid multi bytes |
|
* sequence. Just replace the character with a "_". |
|
* Probably the character doesn't exist in ascii like |
|
* "é, è, à, ç, ..." in French. */ |
|
*ret++ = '_'; |
|
outbytes--; |
|
|
|
if (!outbytes) |
|
break; |
|
|
|
/* There was an error with one character but some other remain |
|
* to be converted. That's probably a multibyte character. |
|
* See above comment. */ |
|
src += sizeof(wchar_t); |
|
inbytes -= sizeof(wchar_t); |
|
|
|
if (!inbytes) |
|
break; |
|
|
|
/* Just to appease my remorse about unclear loop ends */ |
|
loop_counter++; |
|
if (loop_counter > loop_limit) |
|
break; |
|
n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); |
|
} |
|
iso_iconv_close(&conv, 0); |
|
*ret='\0'; |
|
free(wsrc_); |
|
|
|
*output = ret_; |
|
return ISO_SUCCESS; |
|
|
|
fallback:; |
|
/* Assume to have a single byte charset with ASCII as core. |
|
Anything suspicious will be mapped to '_'. |
|
*/ |
|
*output = strdup(input); |
|
for (cpt = (unsigned char *) *output; *cpt; cpt++) { |
|
if (*cpt < 32 || *cpt > 126) |
|
*cpt = '_'; |
|
} |
|
return ISO_SUCCESS; |
|
} |
|
|
|
static |
|
void set_ucsbe(uint16_t *ucs, char c) |
|
{ |
|
char *v = (char*)ucs; |
|
v[0] = (char)0; |
|
v[1] = c; |
|
} |
|
|
|
/** |
|
* @return |
|
* -1, 0, 1 if *ucs <, == or > than c |
|
*/ |
|
static |
|
int cmp_ucsbe(const uint16_t *ucs, char c) |
|
{ |
|
char *v = (char*)ucs; |
|
if (v[0] != 0) { |
|
return 1; |
|
} else if (v[1] == c) { |
|
return 0; |
|
} else { |
|
return (uint8_t)c > (uint8_t)v[1] ? -1 : 1; |
|
} |
|
} |
|
|
|
int str2ucs(const char *icharset, const char *input, uint16_t **output) |
|
{ |
|
int result; |
|
wchar_t *wsrc_; |
|
char *src; |
|
char *ret; |
|
char *ret_; |
|
struct iso_iconv_handle conv; |
|
int conv_ret; |
|
|
|
/* That while loop smells like a potential show stopper */ |
|
size_t loop_counter = 0, loop_limit = 3; |
|
|
|
size_t numchars; |
|
size_t outbytes; |
|
size_t inbytes; |
|
size_t n; |
|
|
|
if (icharset == NULL || input == NULL || output == NULL) { |
|
return ISO_NULL_POINTER; |
|
} |
|
|
|
/* convert the string to a wide character string. Note: outbytes |
|
* is in fact the number of characters in the string and doesn't |
|
* include the last NULL character. |
|
*/ |
|
result = str2wchar(icharset, input, &wsrc_); |
|
if (result < 0) { |
|
return result; |
|
} |
|
src = (char *)wsrc_; |
|
numchars = wcslen(wsrc_); |
|
|
|
inbytes = numchars * sizeof(wchar_t); |
|
loop_limit = inbytes + 3; |
|
|
|
ret_ = malloc((numchars+1) * sizeof(uint16_t)); |
|
if (ret_ == NULL) { |
|
return ISO_OUT_OF_MEM; |
|
} |
|
outbytes = numchars * sizeof(uint16_t); |
|
ret = ret_; |
|
|
|
/* initialize iconv */ |
|
conv_ret = iso_iconv_open(&conv, "UCS-2BE", "WCHAR_T", 0); |
|
if (conv_ret <= 0) { |
|
free(wsrc_); |
|
free(ret_); |
|
return ISO_CHARSET_CONV_ERROR; |
|
} |
|
|
|
n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); |
|
while (n == -1) { |
|
/* The destination buffer is too small. Stops here. */ |
|
if (errno == E2BIG) |
|
break; |
|
|
|
/* An incomplete multi bytes sequence was found. We |
|
* can't do anything here. That's quite unlikely. */ |
|
if (errno == EINVAL) |
|
break; |
|
|
|
/* The last possible error is an invalid multi bytes |
|
* sequence. Just replace the character with a "_". |
|
* Probably the character doesn't exist in UCS */ |
|
set_ucsbe((uint16_t*) ret, '_'); |
|
ret += sizeof(uint16_t); |
|
outbytes -= sizeof(uint16_t); |
|
|
|
if (!outbytes) |
|
break; |
|
|
|
/* There was an error with one character but some other remain |
|
* to be converted. That's probably a multibyte character. |
|
* See above comment. */ |
|
src += sizeof(wchar_t); |
|
inbytes -= sizeof(wchar_t); |
|
|
|
if (!inbytes) |
|
break; |
|
|
|
/* Just to appease my remorse about unclear loop ends */ |
|
loop_counter++; |
|
if (loop_counter > loop_limit) |
|
break; |
|
n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); |
|
} |
|
iso_iconv_close(&conv, 0); |
|
|
|
/* close the ucs string */ |
|
set_ucsbe((uint16_t*) ret, '\0'); |
|
free(wsrc_); |
|
|
|
*output = (uint16_t*)ret_; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
static int valid_d_char(char c) |
|
{ |
|
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_'); |
|
} |
|
|
|
static int valid_a_char(char c) |
|
{ |
|
return (c >= ' ' && c <= '"') || (c >= '%' && c <= '?') || |
|
(c >= 'A' && c <= 'Z') || (c == '_'); |
|
} |
|
|
|
static int valid_j_char(uint16_t c) |
|
{ |
|
return cmp_ucsbe(&c, ' ') != -1 && cmp_ucsbe(&c, '*') && cmp_ucsbe(&c, '/') |
|
&& cmp_ucsbe(&c, ':') && cmp_ucsbe(&c, ';') && cmp_ucsbe(&c, '?') |
|
&& cmp_ucsbe(&c, '\\'); |
|
} |
|
|
|
static |
|
char *iso_dirid(const char *src, int size) |
|
{ |
|
size_t len, i; |
|
char name[32]; |
|
|
|
len = strlen(src); |
|
if (len > size) { |
|
len = size; |
|
} |
|
for (i = 0; i < len; i++) { |
|
char c= toupper(src[i]); |
|
name[i] = valid_d_char(c) ? c : '_'; |
|
} |
|
|
|
name[len] = '\0'; |
|
return strdup(name); |
|
} |
|
|
|
char *iso_1_dirid(const char *src) |
|
{ |
|
return iso_dirid(src, 8); |
|
} |
|
|
|
char *iso_2_dirid(const char *src) |
|
{ |
|
return iso_dirid(src, 31); |
|
} |
|
|
|
char *iso_1_fileid(const char *src) |
|
{ |
|
char *dot; /* Position of the last dot in the filename, will be used |
|
* to calculate lname and lext. */ |
|
int lname, lext, pos, i; |
|
char dest[13]; /* 13 = 8 (name) + 1 (.) + 3 (ext) + 1 (\0) */ |
|
|
|
if (src == NULL) { |
|
return NULL; |
|
} |
|
dot = strrchr(src, '.'); |
|
|
|
lext = dot ? strlen(dot + 1) : 0; |
|
lname = strlen(src) - lext - (dot ? 1 : 0); |
|
|
|
/* If we can't build a filename, return NULL. */ |
|
if (lname == 0 && lext == 0) { |
|
return NULL; |
|
} |
|
|
|
pos = 0; |
|
|
|
/* Convert up to 8 characters of the filename. */ |
|
for (i = 0; i < lname && i < 8; i++) { |
|
char c= toupper(src[i]); |
|
|
|
dest[pos++] = valid_d_char(c) ? c : '_'; |
|
} |
|
|
|
/* This dot is mandatory, even if there is no extension. */ |
|
dest[pos++] = '.'; |
|
|
|
/* Convert up to 3 characters of the extension, if any. */ |
|
for (i = 0; i < lext && i < 3; i++) { |
|
char c= toupper(src[lname + 1 + i]); |
|
|
|
dest[pos++] = valid_d_char(c) ? c : '_'; |
|
} |
|
|
|
dest[pos] = '\0'; |
|
return strdup(dest); |
|
} |
|
|
|
char *iso_2_fileid(const char *src) |
|
{ |
|
char *dot; |
|
int lname, lext, lnname, lnext, pos, i; |
|
char dest[32]; /* 32 = 30 (name + ext) + 1 (.) + 1 (\0) */ |
|
|
|
if (src == NULL) { |
|
return NULL; |
|
} |
|
|
|
dot = strrchr(src, '.'); |
|
|
|
/* |
|
* Since the maximum length can be divided freely over the name and |
|
* extension, we need to calculate their new lengths (lnname and |
|
* lnext). If the original filename is too long, we start by trimming |
|
* the extension, but keep a minimum extension length of 3. |
|
*/ |
|
if (dot == NULL || *(dot + 1) == '\0') { |
|
lname = strlen(src); |
|
lnname = (lname > 30) ? 30 : lname; |
|
lext = lnext = 0; |
|
} else { |
|
lext = strlen(dot + 1); |
|
lname = strlen(src) - lext - 1; |
|
lnext = (strlen(src) > 31 && lext > 3) ? (lname < 27 ? 30 - lname : 3) |
|
: lext; |
|
lnname = (strlen(src) > 31) ? 30 - lnext : lname; |
|
} |
|
|
|
if (lnname == 0 && lnext == 0) { |
|
return NULL; |
|
} |
|
|
|
pos = 0; |
|
|
|
/* Convert up to lnname characters of the filename. */ |
|
for (i = 0; i < lnname; i++) { |
|
char c= toupper(src[i]); |
|
|
|
dest[pos++] = valid_d_char(c) ? c : '_'; |
|
} |
|
dest[pos++] = '.'; |
|
|
|
/* Convert up to lnext characters of the extension, if any. */ |
|
for (i = 0; i < lnext; i++) { |
|
char c= toupper(src[lname + 1 + i]); |
|
|
|
dest[pos++] = valid_d_char(c) ? c : '_'; |
|
} |
|
dest[pos] = '\0'; |
|
return strdup(dest); |
|
} |
|
|
|
/** |
|
* Create a dir name suitable for an ISO image with relaxed constraints. |
|
* |
|
* @param size |
|
* Max len for the name |
|
* @param relaxed |
|
* 0 only allow d-characters, 1 allow also lowe case chars, |
|
* 2 allow all characters |
|
*/ |
|
char *iso_r_dirid(const char *src, int size, int relaxed) |
|
{ |
|
size_t len, i; |
|
char *dest; |
|
|
|
len = strlen(src); |
|
if (len > size) { |
|
len = size; |
|
} |
|
dest = malloc(len + 1); |
|
for (i = 0; i < len; i++) { |
|
char c= src[i]; |
|
if (relaxed == 2) { |
|
/* all chars are allowed */ |
|
dest[i] = c; |
|
} else if (valid_d_char(c)) { |
|
/* it is a valid char */ |
|
dest[i] = c; |
|
} else { |
|
c= toupper(src[i]); |
|
if (valid_d_char(c)) { |
|
if (relaxed) { |
|
/* lower chars are allowed */ |
|
dest[i] = src[i]; |
|
} else { |
|
dest[i] = c; |
|
} |
|
} else { |
|
dest[i] = '_'; |
|
} |
|
} |
|
} |
|
|
|
dest[len] = '\0'; |
|
return dest; |
|
} |
|
|
|
/** |
|
* Create a file name suitable for an ISO image with relaxed constraints. |
|
* |
|
* @param len |
|
* Max len for the name, without taken the "." into account. |
|
* @param relaxed |
|
* 0 only allow d-characters, 1 allow also lowe case chars, |
|
* 2 allow all characters |
|
* @param forcedot |
|
* Whether to ensure that "." is added |
|
*/ |
|
char *iso_r_fileid(const char *src, size_t len, int relaxed, int forcedot) |
|
{ |
|
char *dot, *retval = NULL; |
|
int lname, lext, lnname, lnext, pos, i; |
|
char *dest = NULL; |
|
|
|
dest = calloc(len + 1 + 1, 1); |
|
if (dest == NULL) |
|
goto ex; |
|
|
|
if (src == NULL) { |
|
goto ex; |
|
} |
|
|
|
dot = strrchr(src, '.'); |
|
|
|
/* |
|
* Since the maximum length can be divided freely over the name and |
|
* extension, we need to calculate their new lengths (lnname and |
|
* lnext). If the original filename is too long, we start by trimming |
|
* the extension, but keep a minimum extension length of 3. |
|
*/ |
|
if (dot == NULL || *(dot + 1) == '\0') { |
|
lname = strlen(src); |
|
lnname = (lname > len) ? len : lname; |
|
lext = lnext = 0; |
|
} else { |
|
lext = strlen(dot + 1); |
|
lname = strlen(src) - lext - 1; |
|
lnext = (strlen(src) > len + 1 && lext > 3) ? |
|
(lname < len - 3 ? len - lname : 3) |
|
: lext; |
|
lnname = (strlen(src) > len + 1) ? len - lnext : lname; |
|
} |
|
|
|
if (lnname == 0 && lnext == 0) { |
|
goto ex; |
|
} |
|
|
|
pos = 0; |
|
|
|
/* Convert up to lnname characters of the filename. */ |
|
for (i = 0; i < lnname; i++) { |
|
char c= src[i]; |
|
if (relaxed == 2) { |
|
/* all chars are allowed */ |
|
dest[pos++] = c; |
|
} else if (valid_d_char(c)) { |
|
/* it is a valid char */ |
|
dest[pos++] = c; |
|
} else { |
|
c= toupper(src[i]); |
|
if (valid_d_char(c)) { |
|
if (relaxed) { |
|
/* lower chars are allowed */ |
|
dest[pos++] = src[i]; |
|
} else { |
|
dest[pos++] = c; |
|
} |
|
} else { |
|
dest[pos++] = '_'; |
|
} |
|
} |
|
} |
|
if (lnext > 0 || forcedot) { |
|
dest[pos++] = '.'; |
|
} |
|
|
|
/* Convert up to lnext characters of the extension, if any. */ |
|
for (i = lname + 1; i < lname + 1 + lnext; i++) { |
|
char c= src[i]; |
|
if (relaxed == 2) { |
|
/* all chars are allowed */ |
|
dest[pos++] = c; |
|
} else if (valid_d_char(c)) { |
|
/* it is a valid char */ |
|
dest[pos++] = c; |
|
} else { |
|
c= toupper(src[i]); |
|
if (valid_d_char(c)) { |
|
if (relaxed) { |
|
/* lower chars are allowed */ |
|
dest[pos++] = src[i]; |
|
} else { |
|
dest[pos++] = c; |
|
} |
|
} else { |
|
dest[pos++] = '_'; |
|
} |
|
} |
|
} |
|
dest[pos] = '\0'; |
|
|
|
retval = strdup(dest); |
|
|
|
ex:; |
|
if (dest != NULL) |
|
free(dest); |
|
return retval; |
|
} |
|
|
|
uint16_t *iso_j_file_id(const uint16_t *src) |
|
{ |
|
uint16_t *dot; |
|
size_t lname, lext, lnname, lnext, pos, i; |
|
uint16_t dest[66]; /* 66 = 64 (name + ext) + 1 (.) + 1 (\0) */ |
|
|
|
if (src == NULL) { |
|
return NULL; |
|
} |
|
|
|
dot = ucsrchr(src, '.'); |
|
|
|
/* |
|
* Since the maximum length can be divided freely over the name and |
|
* extension, we need to calculate their new lengths (lnname and |
|
* lnext). If the original filename is too long, we start by trimming |
|
* the extension, but keep a minimum extension length of 3. |
|
*/ |
|
if (dot == NULL || cmp_ucsbe(dot + 1, '\0') == 0) { |
|
lname = ucslen(src); |
|
lnname = (lname > 64) ? 64 : lname; |
|
lext = lnext = 0; |
|
} else { |
|
lext = ucslen(dot + 1); |
|
lname = ucslen(src) - lext - 1; |
|
lnext = (ucslen(src) > 65 && lext > 3) ? (lname < 61 ? 64 - lname : 3) |
|
: lext; |
|
lnname = (ucslen(src) > 65) ? 64 - lnext : lname; |
|
} |
|
|
|
if (lnname == 0 && lnext == 0) { |
|
return NULL; |
|
} |
|
|
|
pos = 0; |
|
|
|
/* Convert up to lnname characters of the filename. */ |
|
for (i = 0; i < lnname; i++) { |
|
uint16_t c = src[i]; |
|
if (valid_j_char(c)) { |
|
dest[pos++] = c; |
|
} else { |
|
set_ucsbe(dest + pos, '_'); |
|
pos++; |
|
} |
|
} |
|
set_ucsbe(dest + pos, '.'); |
|
pos++; |
|
|
|
/* Convert up to lnext characters of the extension, if any. */ |
|
for (i = 0; i < lnext; i++) { |
|
uint16_t c = src[lname + 1 + i]; |
|
if (valid_j_char(c)) { |
|
dest[pos++] = c; |
|
} else { |
|
set_ucsbe(dest + pos, '_'); |
|
pos++; |
|
} |
|
} |
|
set_ucsbe(dest + pos, '\0'); |
|
return ucsdup(dest); |
|
} |
|
|
|
uint16_t *iso_j_dir_id(const uint16_t *src) |
|
{ |
|
size_t len, i; |
|
uint16_t dest[65]; /* 65 = 64 + 1 (\0) */ |
|
|
|
if (src == NULL) { |
|
return NULL; |
|
} |
|
|
|
len = ucslen(src); |
|
if (len > 64) { |
|
len = 64; |
|
} |
|
for (i = 0; i < len; i++) { |
|
uint16_t c = src[i]; |
|
if (valid_j_char(c)) { |
|
dest[i] = c; |
|
} else { |
|
set_ucsbe(dest + i, '_'); |
|
} |
|
} |
|
set_ucsbe(dest + len, '\0'); |
|
return ucsdup(dest); |
|
} |
|
|
|
size_t ucslen(const uint16_t *str) |
|
{ |
|
size_t i; |
|
|
|
for (i = 0; str[i]; i++) |
|
; |
|
return i; |
|
} |
|
|
|
uint16_t *ucsrchr(const uint16_t *str, char c) |
|
{ |
|
size_t len = ucslen(str); |
|
|
|
while (len-- > 0) { |
|
if (cmp_ucsbe(str + len, c) == 0) { |
|
return (uint16_t*)(str + len); |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
uint16_t *ucsdup(const uint16_t *str) |
|
{ |
|
uint16_t *ret; |
|
size_t len = ucslen(str); |
|
|
|
ret = malloc(2 * (len + 1)); |
|
if (ret != NULL) { |
|
memcpy(ret, str, 2 * (len + 1)); |
|
} |
|
return ret; |
|
} |
|
|
|
/** |
|
* Although each character is 2 bytes, we actually compare byte-by-byte |
|
* (thats what the spec says). |
|
*/ |
|
int ucscmp(const uint16_t *s1, const uint16_t *s2) |
|
{ |
|
const char *s = (const char*)s1; |
|
const char *t = (const char*)s2; |
|
size_t len1 = ucslen(s1); |
|
size_t len2 = ucslen(s2); |
|
size_t i, len = MIN(len1, len2) * 2; |
|
|
|
for (i = 0; i < len; i++) { |
|
if (s[i] < t[i]) { |
|
return -1; |
|
} else if (s[i] > t[i]) { |
|
return 1; |
|
} |
|
} |
|
|
|
if (len1 < len2) |
|
return -1; |
|
else if (len1 > len2) |
|
return 1; |
|
return 0; |
|
} |
|
|
|
uint16_t *ucscpy(uint16_t *dest, const uint16_t *src) |
|
{ |
|
size_t n = ucslen(src) + 1; |
|
memcpy(dest, src, n*2); |
|
return dest; |
|
} |
|
|
|
uint16_t *ucsncpy(uint16_t *dest, const uint16_t *src, size_t n) |
|
{ |
|
n = MIN(n, ucslen(src) + 1); |
|
memcpy(dest, src, n*2); |
|
return dest; |
|
} |
|
|
|
int str2d_char(const char *icharset, const char *input, char **output) |
|
{ |
|
int ret; |
|
char *ascii; |
|
size_t len, i; |
|
|
|
if (output == NULL) { |
|
return ISO_OUT_OF_MEM; |
|
} |
|
|
|
/** allow NULL input */ |
|
if (input == NULL) { |
|
*output = NULL; |
|
return 0; |
|
} |
|
|
|
/* this checks for NULL parameters */ |
|
ret = str2ascii(icharset, input, &ascii); |
|
if (ret < 0) { |
|
*output = NULL; |
|
return ret; |
|
} |
|
|
|
len = strlen(ascii); |
|
|
|
for (i = 0; i < len; ++i) { |
|
char c= toupper(ascii[i]); |
|
ascii[i] = valid_d_char(c) ? c : '_'; |
|
} |
|
|
|
*output = ascii; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
int str2a_char(const char *icharset, const char *input, char **output) |
|
{ |
|
int ret; |
|
char *ascii; |
|
size_t len, i; |
|
|
|
if (output == NULL) { |
|
return ISO_OUT_OF_MEM; |
|
} |
|
|
|
/** allow NULL input */ |
|
if (input == NULL) { |
|
*output = NULL; |
|
return 0; |
|
} |
|
|
|
/* this checks for NULL parameters */ |
|
ret = str2ascii(icharset, input, &ascii); |
|
if (ret < 0) { |
|
*output = NULL; |
|
return ret; |
|
} |
|
|
|
len = strlen(ascii); |
|
|
|
for (i = 0; i < len; ++i) { |
|
char c= toupper(ascii[i]); |
|
ascii[i] = valid_a_char(c) ? c : '_'; |
|
} |
|
|
|
*output = ascii; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
void iso_lsb(uint8_t *buf, uint32_t num, int bytes) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < bytes; ++i) |
|
buf[i] = (num >> (8 * i)) & 0xff; |
|
} |
|
|
|
void iso_msb(uint8_t *buf, uint32_t num, int bytes) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < bytes; ++i) |
|
buf[bytes - 1 - i] = (num >> (8 * i)) & 0xff; |
|
} |
|
|
|
void iso_bb(uint8_t *buf, uint32_t num, int bytes) |
|
{ |
|
iso_lsb(buf, num, bytes); |
|
iso_msb(buf+bytes, num, bytes); |
|
} |
|
|
|
uint32_t iso_read_lsb(const uint8_t *buf, int bytes) |
|
{ |
|
int i; |
|
uint32_t ret = 0; |
|
|
|
for (i=0; i<bytes; i++) { |
|
ret += ((uint32_t) buf[i]) << (i*8); |
|
} |
|
return ret; |
|
} |
|
|
|
uint32_t iso_read_msb(const uint8_t *buf, int bytes) |
|
{ |
|
int i; |
|
uint32_t ret = 0; |
|
|
|
for (i=0; i<bytes; i++) { |
|
ret += ((uint32_t) buf[bytes-i-1]) << (i*8); |
|
} |
|
return ret; |
|
} |
|
|
|
uint32_t iso_read_bb(const uint8_t *buf, int bytes, int *error) |
|
{ |
|
uint32_t v1 = iso_read_lsb(buf, bytes); |
|
|
|
if (error) { |
|
uint32_t v2 = iso_read_msb(buf + bytes, bytes); |
|
if (v1 != v2) |
|
*error = 1; |
|
} |
|
return v1; |
|
} |
|
|
|
void iso_datetime_7(unsigned char *buf, time_t t, int always_gmt) |
|
{ |
|
static int tzsetup = 0; |
|
int tzoffset; |
|
struct tm tm; |
|
|
|
if (!tzsetup) { |
|
tzset(); |
|
tzsetup = 1; |
|
} |
|
|
|
memset(&tm, 0, sizeof(tm)); |
|
tm.tm_isdst = -1; /* some Linuxes change tm_isdst only if it is -1 */ |
|
localtime_r(&t, &tm); |
|
|
|
#ifdef HAVE_TM_GMTOFF |
|
tzoffset = tm.tm_gmtoff / 60 / 15; |
|
#else |
|
if (tm.tm_isdst < 0) |
|
tm.tm_isdst = 0; |
|
tzoffset = ( - timezone / 60 / 15 ) + 4 * tm.tm_isdst; |
|
#endif |
|
|
|
if (tzoffset > 52 || tzoffset < -48 || always_gmt) { |
|
/* absurd timezone offset, represent time in GMT */ |
|
gmtime_r(&t, &tm); |
|
tzoffset = 0; |
|
} |
|
buf[0] = tm.tm_year; |
|
buf[1] = tm.tm_mon + 1; |
|
buf[2] = tm.tm_mday; |
|
buf[3] = tm.tm_hour; |
|
buf[4] = tm.tm_min; |
|
buf[5] = tm.tm_sec; |
|
buf[6] = tzoffset; |
|
} |
|
|
|
void iso_datetime_17(unsigned char *buf, time_t t, int always_gmt) |
|
{ |
|
static int tzsetup = 0; |
|
static int tzoffset; |
|
struct tm tm; |
|
|
|
if (t == (time_t) - 1) { |
|
/* unspecified time */ |
|
memset(buf, '0', 16); |
|
buf[16] = 0; |
|
return; |
|
} |
|
|
|
if (!tzsetup) { |
|
tzset(); |
|
tzsetup = 1; |
|
} |
|
|
|
memset(&tm, 0, sizeof(tm)); |
|
tm.tm_isdst = -1; /* some Linuxes change tm_isdst only if it is -1 */ |
|
localtime_r(&t, &tm); |
|
|
|
localtime_r(&t, &tm); |
|
|
|
#ifdef HAVE_TM_GMTOFF |
|
tzoffset = tm.tm_gmtoff / 60 / 15; |
|
#else |
|
if (tm.tm_isdst < 0) |
|
tm.tm_isdst = 0; |
|
tzoffset = ( - timezone / 60 / 15 ) + 4 * tm.tm_isdst; |
|
#endif |
|
|
|
if (tzoffset > 52 || tzoffset < -48 || always_gmt) { |
|
/* absurd timezone offset, represent time in GMT */ |
|
gmtime_r(&t, &tm); |
|
tzoffset = 0; |
|
} |
|
|
|
sprintf((char*)&buf[0], "%04d", tm.tm_year + 1900); |
|
sprintf((char*)&buf[4], "%02d", tm.tm_mon + 1); |
|
sprintf((char*)&buf[6], "%02d", tm.tm_mday); |
|
sprintf((char*)&buf[8], "%02d", tm.tm_hour); |
|
sprintf((char*)&buf[10], "%02d", tm.tm_min); |
|
sprintf((char*)&buf[12], "%02d", MIN(59, tm.tm_sec)); |
|
memcpy(&buf[14], "00", 2); |
|
buf[16] = tzoffset; |
|
|
|
} |
|
|
|
#ifndef HAVE_TIMEGM |
|
static |
|
time_t timegm(struct tm *tm) |
|
{ |
|
time_t ret; |
|
char *tz; |
|
|
|
tz = getenv("TZ"); |
|
setenv("TZ", "", 1); |
|
tzset(); |
|
ret = mktime(tm); |
|
if (tz) |
|
setenv("TZ", tz, 1); |
|
else |
|
unsetenv("TZ"); |
|
tzset(); |
|
return ret; |
|
} |
|
#endif |
|
|
|
time_t iso_datetime_read_7(const uint8_t *buf) |
|
{ |
|
struct tm tm; |
|
|
|
tm.tm_year = buf[0]; |
|
tm.tm_mon = buf[1] - 1; |
|
tm.tm_mday = buf[2]; |
|
tm.tm_hour = buf[3]; |
|
tm.tm_min = buf[4]; |
|
tm.tm_sec = buf[5]; |
|
return timegm(&tm) - ((int8_t)buf[6]) * 60 * 15; |
|
} |
|
|
|
time_t iso_datetime_read_17(const uint8_t *buf) |
|
{ |
|
struct tm tm; |
|
|
|
sscanf((char*)&buf[0], "%4d", &tm.tm_year); |
|
sscanf((char*)&buf[4], "%2d", &tm.tm_mon); |
|
sscanf((char*)&buf[6], "%2d", &tm.tm_mday); |
|
sscanf((char*)&buf[8], "%2d", &tm.tm_hour); |
|
sscanf((char*)&buf[10], "%2d", &tm.tm_min); |
|
sscanf((char*)&buf[12], "%2d", &tm.tm_sec); |
|
tm.tm_year -= 1900; |
|
tm.tm_mon -= 1; |
|
|
|
return timegm(&tm) - ((int8_t)buf[6]) * 60 * 15; |
|
} |
|
|
|
/** |
|
* Check whether the caller process has read access to the given local file. |
|
* |
|
* @return |
|
* 1 on success (i.e, the process has read access), < 0 on error |
|
* (including ISO_FILE_ACCESS_DENIED on access denied to the specified file |
|
* or any directory on the path). |
|
*/ |
|
int iso_eaccess(const char *path) |
|
{ |
|
int access; |
|
|
|
/* use non standard eaccess when available, open() otherwise */ |
|
#ifdef HAVE_EACCESS |
|
access = !eaccess(path, R_OK); |
|
#else |
|
int fd = open(path, O_RDONLY); |
|
if (fd != -1) { |
|
close(fd); |
|
access = 1; |
|
} else { |
|
access = 0; |
|
} |
|
#endif |
|
|
|
if (!access) { |
|
int err; |
|
|
|
/* error, choose an appropriate return code */ |
|
switch (errno) { |
|
case EACCES: |
|
err = ISO_FILE_ACCESS_DENIED; |
|
break; |
|
case ENOTDIR: |
|
case ENAMETOOLONG: |
|
case ELOOP: |
|
err = ISO_FILE_BAD_PATH; |
|
break; |
|
case ENOENT: |
|
err = ISO_FILE_DOESNT_EXIST; |
|
break; |
|
case EFAULT: |
|
case ENOMEM: |
|
err = ISO_OUT_OF_MEM; |
|
break; |
|
default: |
|
err = ISO_FILE_ERROR; |
|
break; |
|
} |
|
return err; |
|
} |
|
return ISO_SUCCESS; |
|
} |
|
|
|
char *strcopy(const char *buf, size_t len) |
|
{ |
|
char *str; |
|
|
|
str = malloc((len + 1) * sizeof(char)); |
|
if (str == NULL) { |
|
return NULL; |
|
} |
|
strncpy(str, buf, len); |
|
str[len] = '\0'; |
|
|
|
/* remove trailing spaces */ |
|
for (len = len-1; str[len] == ' ' && len > 0; --len) |
|
str[len] = '\0'; |
|
|
|
return str; |
|
} |
|
|
|
/** |
|
* Copy up to \p max characters from \p src to \p dest. If \p src has less than |
|
* \p max characters, we pad dest with " " characters. |
|
*/ |
|
void strncpy_pad(char *dest, const char *src, size_t max) |
|
{ |
|
size_t len, i; |
|
|
|
if (src != NULL) { |
|
len = MIN(strlen(src), max); |
|
} else { |
|
len = 0; |
|
} |
|
|
|
for (i = 0; i < len; ++i) |
|
dest[i] = src[i]; |
|
for (i = len; i < max; ++i) |
|
dest[i] = ' '; |
|
} |
|
|
|
char *ucs2str(const char *buf, size_t len) |
|
{ |
|
size_t outbytes, inbytes; |
|
char *str, *src, *out = NULL, *retval = NULL; |
|
struct iso_iconv_handle conv; |
|
int conv_ret; |
|
size_t n; |
|
|
|
inbytes = len; |
|
|
|
outbytes = (inbytes+1) * MB_LEN_MAX; |
|
|
|
/* ensure enought space */ |
|
out = calloc(outbytes, 1); |
|
|
|
/* convert to local charset */ |
|
conv_ret = iso_iconv_open(&conv, iso_get_local_charset(0), "UCS-2BE", 0); |
|
if (conv_ret <= 0) { |
|
goto ex; |
|
} |
|
src = (char *)buf; |
|
str = (char *)out; |
|
|
|
n = iso_iconv(&conv, &src, &inbytes, &str, &outbytes, 0); |
|
iso_iconv_close(&conv, 0); |
|
if (n == -1) { |
|
/* error */ |
|
goto ex; |
|
} |
|
*str = '\0'; |
|
|
|
/* remove trailing spaces */ |
|
for (len = strlen(out) - 1; out[len] == ' ' && len > 0; --len) |
|
out[len] = '\0'; |
|
|
|
retval = strdup(out); |
|
|
|
ex:; |
|
if (out != NULL) |
|
free(out); |
|
return retval; |
|
} |
|
|
|
void iso_lib_version(int *major, int *minor, int *micro) |
|
{ |
|
*major = LIBISOFS_MAJOR_VERSION; |
|
*minor = LIBISOFS_MINOR_VERSION; |
|
*micro = LIBISOFS_MICRO_VERSION; |
|
} |
|
|
|
int iso_lib_is_compatible(int major, int minor, int micro) |
|
{ |
|
int cmajor, cminor, cmicro; |
|
|
|
/* for now, the rule is that library is compitable if requested |
|
* version is lower */ |
|
iso_lib_version(&cmajor, &cminor, &cmicro); |
|
|
|
return cmajor > major |
|
|| (cmajor == major |
|
&& (cminor > minor |
|
|| (cminor == minor && cmicro >= micro))); |
|
} |
|
|
|
int iso_init_locale(int flag) |
|
{ |
|
setlocale(LC_CTYPE, ""); |
|
return 1; |
|
} |
|
|
|
|
|
int iso_util_encode_len_bytes(uint32_t data, char *buffer, int data_len, |
|
int *result_len, int flag) |
|
{ |
|
uint32_t x; |
|
int i, l; |
|
char *wpt = buffer; |
|
|
|
if (data_len <= 0) { |
|
x = data; |
|
for (i = 0; i < 4 && x != 0; i++) |
|
x = x >> 8; |
|
l = i; |
|
if (l == 0) |
|
l = 1; |
|
} else |
|
l = data_len; |
|
*((unsigned char *) (wpt++)) = l; |
|
for (i = 0; i < l; i++) |
|
*((unsigned char *) (wpt++)) = data >> (8 * (l - i - 1)); |
|
*result_len = l + 1; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
|
|
int iso_util_decode_len_bytes(uint32_t *data, char *buffer, int *data_len, |
|
int buffer_len, int flag) |
|
{ |
|
int i; |
|
|
|
*data = 0; |
|
*data_len = ((unsigned char *) buffer)[0]; |
|
if (*data_len > buffer_len - 1) |
|
*data_len = buffer_len - 1; |
|
for (i = 1; i <= *data_len; i++) |
|
*data = (*data << 8) | ((unsigned char *) buffer)[i]; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
|
|
int iso_util_dec_to_uint32(char *dec, uint32_t *value, int flag) |
|
{ |
|
double num; |
|
|
|
sscanf(dec, "%lf", &num); |
|
if (num < 0 || num > 4294967295.0) |
|
return 0; |
|
*value = num; |
|
return 1; |
|
} |
|
|
|
|
|
int iso_util_hex_to_bin(char *hex, char *bin, int bin_size, int *bin_count, |
|
int flag) |
|
{ |
|
static char *allowed = {"0123456789ABCDEFabcdef"}; |
|
char b[3]; |
|
int i; |
|
unsigned int u; |
|
|
|
b[2] = 0; |
|
*bin_count = 0; |
|
for (i = 0; i < bin_size; i++) { |
|
b[0] = hex[2 * i]; |
|
b[1] = hex[2 * i + 1]; |
|
if (strchr(allowed, b[0]) == NULL || strchr(allowed, b[1]) == NULL) |
|
break; |
|
sscanf(b, "%x", &u); |
|
((unsigned char *) bin)[i] = u; |
|
(*bin_count)++; |
|
} |
|
return (*bin_count > 0); |
|
} |
|
|
|
|
|
int iso_util_tag_magic(int tag_type, char **tag_magic, int *len, int flag) |
|
{ |
|
static char *magic[] = {"", |
|
"libisofs_checksum_tag_v1", |
|
"libisofs_sb_checksum_tag_v1", |
|
"libisofs_tree_checksum_tag_v1", |
|
"libisofs_rlsb32_checksum_tag_v1"}; |
|
static int magic_len[]= {0, 24, 27, 29, 31}; |
|
static int magic_max = 4; |
|
|
|
*tag_magic = NULL; |
|
*len = 0; |
|
if (tag_type < 0 || tag_type > magic_max) |
|
return ISO_WRONG_ARG_VALUE; |
|
*tag_magic = magic[tag_type]; |
|
*len = magic_len[tag_type]; |
|
return magic_max; |
|
} |
|
|
|
|
|
int iso_util_decode_md5_tag(char data[2048], int *tag_type, uint32_t *pos, |
|
uint32_t *range_start, uint32_t *range_size, |
|
uint32_t *next_tag, char md5[16], int flag) |
|
{ |
|
int ret, bin_count, i, mode, magic_first = 1, magic_last = 4; |
|
int magic_len = 0; |
|
char *cpt, self_md5[16], tag_md5[16], *tag_magic; |
|
void *ctx = NULL; |
|
|
|
*next_tag = 0; |
|
mode = flag & 255; |
|
if (mode > magic_last) |
|
return ISO_WRONG_ARG_VALUE; |
|
if (mode > 0) |
|
magic_first = magic_last = mode; |
|
for (i = magic_first; i <= magic_last; i++) { |
|
iso_util_tag_magic(i, &tag_magic, &magic_len, 0); |
|
if (strncmp(data, tag_magic, magic_len) == 0) |
|
break; |
|
} |
|
if (i > magic_last ) |
|
return 0; |
|
*tag_type = i; |
|
cpt = data + magic_len + 1; |
|
if (strncmp(cpt, "pos=", 4) != 0) |
|
return 0; |
|
cpt+= 4; |
|
ret = iso_util_dec_to_uint32(cpt, pos, 0); |
|
if (ret <= 0) |
|
return 0; |
|
cpt = strstr(cpt, "range_start="); |
|
if (cpt == NULL) |
|
return(0); |
|
ret = iso_util_dec_to_uint32(cpt + 12, range_start, 0); |
|
if (ret <= 0) |
|
return 0; |
|
cpt = strstr(cpt, "range_size="); |
|
if (cpt == NULL) |
|
return(0); |
|
ret = iso_util_dec_to_uint32(cpt + 11, range_size, 0); |
|
if (ret <= 0) |
|
return 0; |
|
if (*tag_type == 2 || *tag_type == 3) { |
|
cpt = strstr(cpt, "next="); |
|
if (cpt == NULL) |
|
return(0); |
|
ret = iso_util_dec_to_uint32(cpt + 5, next_tag, 0); |
|
if (ret <= 0) |
|
return 0; |
|
} else if (*tag_type == 4) { |
|
cpt = strstr(cpt, "session_start="); |
|
if (cpt == NULL) |
|
return(0); |
|
ret = iso_util_dec_to_uint32(cpt + 14, next_tag, 0); |
|
if (ret <= 0) |
|
return 0; |
|
} |
|
cpt = strstr(cpt, "md5="); |
|
if (cpt == NULL) |
|
return(0); |
|
ret = iso_util_hex_to_bin(cpt + 4, md5, 16, &bin_count, 0); |
|
if (ret <= 0 || bin_count != 16) |
|
return 0; |
|
|
|
cpt += 4 + 32; |
|
ret = iso_md5_start(&ctx); |
|
if (ret < 0) |
|
return ret; |
|
iso_md5_compute(ctx, data , cpt - data); |
|
iso_md5_end(&ctx, tag_md5); |
|
cpt = strstr(cpt, "self="); |
|
if (cpt == NULL) |
|
return(0); |
|
ret = iso_util_hex_to_bin(cpt + 5, self_md5, 16, &bin_count, 0); |
|
if (ret <= 0 || bin_count != 16) |
|
return 0; |
|
for(i= 0; i < 16; i++) |
|
if(self_md5[i] != tag_md5[i]) |
|
return ISO_MD5_AREA_CORRUPTED; |
|
if (*(cpt + 5 + 32) != '\n') |
|
return 0; |
|
return(1); |
|
} |
|
|
|
|
|
int iso_util_eval_md5_tag(char *block, int desired, uint32_t lba, |
|
void *ctx, uint32_t ctx_start_lba, |
|
int *tag_type, uint32_t *next_tag, int flag) |
|
{ |
|
int decode_ret, ret; |
|
char md5[16], cloned_md5[16]; |
|
uint32_t pos, range_start, range_size; |
|
void *cloned_ctx = NULL; |
|
|
|
*tag_type = 0; |
|
decode_ret = iso_util_decode_md5_tag(block, tag_type, &pos, |
|
&range_start, &range_size, next_tag, md5, 0); |
|
if (decode_ret != 1 && decode_ret != ISO_MD5_AREA_CORRUPTED) |
|
return 0; |
|
if (*tag_type > 30) |
|
goto unexpected_type; |
|
|
|
if (decode_ret == ISO_MD5_AREA_CORRUPTED) { |
|
ret = decode_ret; |
|
goto ex; |
|
} else if (!((1 << *tag_type) & desired)) { |
|
unexpected_type:; |
|
iso_msg_submit(-1, ISO_MD5_TAG_UNEXPECTED, 0, NULL); |
|
ret = 0; |
|
goto ex; |
|
} else if(pos != lba) { |
|
ret = ISO_MD5_TAG_MISPLACED; |
|
goto ex; |
|
} else if(range_start != ctx_start_lba) { |
|
ret = ISO_MD5_TAG_MISPLACED; |
|
} |
|
ret = iso_md5_clone(ctx, &cloned_ctx); |
|
if (ret < 0) |
|
goto ex; |
|
iso_md5_end(&cloned_ctx, cloned_md5); |
|
if (! iso_md5_match(cloned_md5, md5)) { |
|
ret = ISO_MD5_TAG_MISMATCH; |
|
goto ex; |
|
} |
|
ret = 1; |
|
ex:; |
|
if (ret < 0) |
|
iso_msg_submit(-1, ret, 0, NULL); |
|
return ret; |
|
} |
|
|
|
|
|
|