The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/fs/udf/unicode.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * unicode.c
    3  *
    4  * PURPOSE
    5  *      Routines for converting between UTF-8 and OSTA Compressed Unicode.
    6  *      Also handles filename mangling
    7  *
    8  * DESCRIPTION
    9  *      OSTA Compressed Unicode is explained in the OSTA UDF specification.
   10  *              http://www.osta.org/
   11  *      UTF-8 is explained in the IETF RFC XXXX.
   12  *              ftp://ftp.internic.net/rfc/rfcxxxx.txt
   13  *
   14  * COPYRIGHT
   15  *      This file is distributed under the terms of the GNU General Public
   16  *      License (GPL). Copies of the GPL can be obtained from:
   17  *              ftp://prep.ai.mit.edu/pub/gnu/GPL
   18  *      Each contributing author retains all rights to their own work.
   19  */
   20 
   21 #include "udfdecl.h"
   22 
   23 #include <linux/kernel.h>
   24 #include <linux/string.h>       /* for memset */
   25 #include <linux/nls.h>
   26 #include <linux/crc-itu-t.h>
   27 #include <linux/slab.h>
   28 
   29 #include "udf_sb.h"
   30 
   31 static int udf_translate_to_linux(uint8_t *, uint8_t *, int, uint8_t *, int);
   32 
   33 static int udf_char_to_ustr(struct ustr *dest, const uint8_t *src, int strlen)
   34 {
   35         if ((!dest) || (!src) || (!strlen) || (strlen > UDF_NAME_LEN - 2))
   36                 return 0;
   37 
   38         memset(dest, 0, sizeof(struct ustr));
   39         memcpy(dest->u_name, src, strlen);
   40         dest->u_cmpID = 0x08;
   41         dest->u_len = strlen;
   42 
   43         return strlen;
   44 }
   45 
   46 /*
   47  * udf_build_ustr
   48  */
   49 int udf_build_ustr(struct ustr *dest, dstring *ptr, int size)
   50 {
   51         int usesize;
   52 
   53         if (!dest || !ptr || !size)
   54                 return -1;
   55         BUG_ON(size < 2);
   56 
   57         usesize = min_t(size_t, ptr[size - 1], sizeof(dest->u_name));
   58         usesize = min(usesize, size - 2);
   59         dest->u_cmpID = ptr[0];
   60         dest->u_len = usesize;
   61         memcpy(dest->u_name, ptr + 1, usesize);
   62         memset(dest->u_name + usesize, 0, sizeof(dest->u_name) - usesize);
   63 
   64         return 0;
   65 }
   66 
   67 /*
   68  * udf_build_ustr_exact
   69  */
   70 static int udf_build_ustr_exact(struct ustr *dest, dstring *ptr, int exactsize)
   71 {
   72         if ((!dest) || (!ptr) || (!exactsize))
   73                 return -1;
   74 
   75         memset(dest, 0, sizeof(struct ustr));
   76         dest->u_cmpID = ptr[0];
   77         dest->u_len = exactsize - 1;
   78         memcpy(dest->u_name, ptr + 1, exactsize - 1);
   79 
   80         return 0;
   81 }
   82 
   83 /*
   84  * udf_ocu_to_utf8
   85  *
   86  * PURPOSE
   87  *      Convert OSTA Compressed Unicode to the UTF-8 equivalent.
   88  *
   89  * PRE-CONDITIONS
   90  *      utf                     Pointer to UTF-8 output buffer.
   91  *      ocu                     Pointer to OSTA Compressed Unicode input buffer
   92  *                              of size UDF_NAME_LEN bytes.
   93  *                              both of type "struct ustr *"
   94  *
   95  * POST-CONDITIONS
   96  *      <return>                Zero on success.
   97  *
   98  * HISTORY
   99  *      November 12, 1997 - Andrew E. Mileski
  100  *      Written, tested, and released.
  101  */
  102 int udf_CS0toUTF8(struct ustr *utf_o, const struct ustr *ocu_i)
  103 {
  104         const uint8_t *ocu;
  105         uint8_t cmp_id, ocu_len;
  106         int i;
  107 
  108         ocu_len = ocu_i->u_len;
  109         if (ocu_len == 0) {
  110                 memset(utf_o, 0, sizeof(struct ustr));
  111                 return 0;
  112         }
  113 
  114         cmp_id = ocu_i->u_cmpID;
  115         if (cmp_id != 8 && cmp_id != 16) {
  116                 memset(utf_o, 0, sizeof(struct ustr));
  117                 pr_err("unknown compression code (%d) stri=%s\n",
  118                        cmp_id, ocu_i->u_name);
  119                 return 0;
  120         }
  121 
  122         ocu = ocu_i->u_name;
  123         utf_o->u_len = 0;
  124         for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN - 3));) {
  125 
  126                 /* Expand OSTA compressed Unicode to Unicode */
  127                 uint32_t c = ocu[i++];
  128                 if (cmp_id == 16)
  129                         c = (c << 8) | ocu[i++];
  130 
  131                 /* Compress Unicode to UTF-8 */
  132                 if (c < 0x80U)
  133                         utf_o->u_name[utf_o->u_len++] = (uint8_t)c;
  134                 else if (c < 0x800U) {
  135                         utf_o->u_name[utf_o->u_len++] =
  136                                                 (uint8_t)(0xc0 | (c >> 6));
  137                         utf_o->u_name[utf_o->u_len++] =
  138                                                 (uint8_t)(0x80 | (c & 0x3f));
  139                 } else {
  140                         utf_o->u_name[utf_o->u_len++] =
  141                                                 (uint8_t)(0xe0 | (c >> 12));
  142                         utf_o->u_name[utf_o->u_len++] =
  143                                                 (uint8_t)(0x80 |
  144                                                           ((c >> 6) & 0x3f));
  145                         utf_o->u_name[utf_o->u_len++] =
  146                                                 (uint8_t)(0x80 | (c & 0x3f));
  147                 }
  148         }
  149         utf_o->u_cmpID = 8;
  150 
  151         return utf_o->u_len;
  152 }
  153 
  154 /*
  155  *
  156  * udf_utf8_to_ocu
  157  *
  158  * PURPOSE
  159  *      Convert UTF-8 to the OSTA Compressed Unicode equivalent.
  160  *
  161  * DESCRIPTION
  162  *      This routine is only called by udf_lookup().
  163  *
  164  * PRE-CONDITIONS
  165  *      ocu                     Pointer to OSTA Compressed Unicode output
  166  *                              buffer of size UDF_NAME_LEN bytes.
  167  *      utf                     Pointer to UTF-8 input buffer.
  168  *      utf_len                 Length of UTF-8 input buffer in bytes.
  169  *
  170  * POST-CONDITIONS
  171  *      <return>                Zero on success.
  172  *
  173  * HISTORY
  174  *      November 12, 1997 - Andrew E. Mileski
  175  *      Written, tested, and released.
  176  */
  177 static int udf_UTF8toCS0(dstring *ocu, struct ustr *utf, int length)
  178 {
  179         unsigned c, i, max_val, utf_char;
  180         int utf_cnt, u_len;
  181 
  182         memset(ocu, 0, sizeof(dstring) * length);
  183         ocu[0] = 8;
  184         max_val = 0xffU;
  185 
  186 try_again:
  187         u_len = 0U;
  188         utf_char = 0U;
  189         utf_cnt = 0U;
  190         for (i = 0U; i < utf->u_len; i++) {
  191                 c = (uint8_t)utf->u_name[i];
  192 
  193                 /* Complete a multi-byte UTF-8 character */
  194                 if (utf_cnt) {
  195                         utf_char = (utf_char << 6) | (c & 0x3fU);
  196                         if (--utf_cnt)
  197                                 continue;
  198                 } else {
  199                         /* Check for a multi-byte UTF-8 character */
  200                         if (c & 0x80U) {
  201                                 /* Start a multi-byte UTF-8 character */
  202                                 if ((c & 0xe0U) == 0xc0U) {
  203                                         utf_char = c & 0x1fU;
  204                                         utf_cnt = 1;
  205                                 } else if ((c & 0xf0U) == 0xe0U) {
  206                                         utf_char = c & 0x0fU;
  207                                         utf_cnt = 2;
  208                                 } else if ((c & 0xf8U) == 0xf0U) {
  209                                         utf_char = c & 0x07U;
  210                                         utf_cnt = 3;
  211                                 } else if ((c & 0xfcU) == 0xf8U) {
  212                                         utf_char = c & 0x03U;
  213                                         utf_cnt = 4;
  214                                 } else if ((c & 0xfeU) == 0xfcU) {
  215                                         utf_char = c & 0x01U;
  216                                         utf_cnt = 5;
  217                                 } else {
  218                                         goto error_out;
  219                                 }
  220                                 continue;
  221                         } else {
  222                                 /* Single byte UTF-8 character (most common) */
  223                                 utf_char = c;
  224                         }
  225                 }
  226 
  227                 /* Choose no compression if necessary */
  228                 if (utf_char > max_val) {
  229                         if (max_val == 0xffU) {
  230                                 max_val = 0xffffU;
  231                                 ocu[0] = (uint8_t)0x10U;
  232                                 goto try_again;
  233                         }
  234                         goto error_out;
  235                 }
  236 
  237                 if (max_val == 0xffffU)
  238                         ocu[++u_len] = (uint8_t)(utf_char >> 8);
  239                 ocu[++u_len] = (uint8_t)(utf_char & 0xffU);
  240         }
  241 
  242         if (utf_cnt) {
  243 error_out:
  244                 ocu[++u_len] = '?';
  245                 printk(KERN_DEBUG pr_fmt("bad UTF-8 character\n"));
  246         }
  247 
  248         ocu[length - 1] = (uint8_t)u_len + 1;
  249 
  250         return u_len + 1;
  251 }
  252 
  253 static int udf_CS0toNLS(struct nls_table *nls, struct ustr *utf_o,
  254                         const struct ustr *ocu_i)
  255 {
  256         const uint8_t *ocu;
  257         uint8_t cmp_id, ocu_len;
  258         int i, len;
  259 
  260 
  261         ocu_len = ocu_i->u_len;
  262         if (ocu_len == 0) {
  263                 memset(utf_o, 0, sizeof(struct ustr));
  264                 return 0;
  265         }
  266 
  267         cmp_id = ocu_i->u_cmpID;
  268         if (cmp_id != 8 && cmp_id != 16) {
  269                 memset(utf_o, 0, sizeof(struct ustr));
  270                 pr_err("unknown compression code (%d) stri=%s\n",
  271                        cmp_id, ocu_i->u_name);
  272                 return 0;
  273         }
  274 
  275         ocu = ocu_i->u_name;
  276         utf_o->u_len = 0;
  277         for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN - 3));) {
  278                 /* Expand OSTA compressed Unicode to Unicode */
  279                 uint32_t c = ocu[i++];
  280                 if (cmp_id == 16)
  281                         c = (c << 8) | ocu[i++];
  282 
  283                 len = nls->uni2char(c, &utf_o->u_name[utf_o->u_len],
  284                                     UDF_NAME_LEN - utf_o->u_len);
  285                 /* Valid character? */
  286                 if (len >= 0)
  287                         utf_o->u_len += len;
  288                 else
  289                         utf_o->u_name[utf_o->u_len++] = '?';
  290         }
  291         utf_o->u_cmpID = 8;
  292 
  293         return utf_o->u_len;
  294 }
  295 
  296 static int udf_NLStoCS0(struct nls_table *nls, dstring *ocu, struct ustr *uni,
  297                         int length)
  298 {
  299         int len;
  300         unsigned i, max_val;
  301         uint16_t uni_char;
  302         int u_len;
  303 
  304         memset(ocu, 0, sizeof(dstring) * length);
  305         ocu[0] = 8;
  306         max_val = 0xffU;
  307 
  308 try_again:
  309         u_len = 0U;
  310         for (i = 0U; i < uni->u_len; i++) {
  311                 len = nls->char2uni(&uni->u_name[i], uni->u_len - i, &uni_char);
  312                 if (!len)
  313                         continue;
  314                 /* Invalid character, deal with it */
  315                 if (len < 0) {
  316                         len = 1;
  317                         uni_char = '?';
  318                 }
  319 
  320                 if (uni_char > max_val) {
  321                         max_val = 0xffffU;
  322                         ocu[0] = (uint8_t)0x10U;
  323                         goto try_again;
  324                 }
  325 
  326                 if (max_val == 0xffffU)
  327                         ocu[++u_len] = (uint8_t)(uni_char >> 8);
  328                 ocu[++u_len] = (uint8_t)(uni_char & 0xffU);
  329                 i += len - 1;
  330         }
  331 
  332         ocu[length - 1] = (uint8_t)u_len + 1;
  333         return u_len + 1;
  334 }
  335 
  336 int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname,
  337                      int flen)
  338 {
  339         struct ustr *filename, *unifilename;
  340         int len = 0;
  341 
  342         filename = kmalloc(sizeof(struct ustr), GFP_NOFS);
  343         if (!filename)
  344                 return 0;
  345 
  346         unifilename = kmalloc(sizeof(struct ustr), GFP_NOFS);
  347         if (!unifilename)
  348                 goto out1;
  349 
  350         if (udf_build_ustr_exact(unifilename, sname, flen))
  351                 goto out2;
  352 
  353         if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) {
  354                 if (!udf_CS0toUTF8(filename, unifilename)) {
  355                         udf_debug("Failed in udf_get_filename: sname = %s\n",
  356                                   sname);
  357                         goto out2;
  358                 }
  359         } else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) {
  360                 if (!udf_CS0toNLS(UDF_SB(sb)->s_nls_map, filename,
  361                                   unifilename)) {
  362                         udf_debug("Failed in udf_get_filename: sname = %s\n",
  363                                   sname);
  364                         goto out2;
  365                 }
  366         } else
  367                 goto out2;
  368 
  369         len = udf_translate_to_linux(dname, filename->u_name, filename->u_len,
  370                                      unifilename->u_name, unifilename->u_len);
  371 out2:
  372         kfree(unifilename);
  373 out1:
  374         kfree(filename);
  375         return len;
  376 }
  377 
  378 int udf_put_filename(struct super_block *sb, const uint8_t *sname,
  379                      uint8_t *dname, int flen)
  380 {
  381         struct ustr unifilename;
  382         int namelen;
  383 
  384         if (!udf_char_to_ustr(&unifilename, sname, flen))
  385                 return 0;
  386 
  387         if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) {
  388                 namelen = udf_UTF8toCS0(dname, &unifilename, UDF_NAME_LEN);
  389                 if (!namelen)
  390                         return 0;
  391         } else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) {
  392                 namelen = udf_NLStoCS0(UDF_SB(sb)->s_nls_map, dname,
  393                                         &unifilename, UDF_NAME_LEN);
  394                 if (!namelen)
  395                         return 0;
  396         } else
  397                 return 0;
  398 
  399         return namelen;
  400 }
  401 
  402 #define ILLEGAL_CHAR_MARK       '_'
  403 #define EXT_MARK                '.'
  404 #define CRC_MARK                '#'
  405 #define EXT_SIZE                5
  406 
  407 static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName,
  408                                   int udfLen, uint8_t *fidName,
  409                                   int fidNameLen)
  410 {
  411         int index, newIndex = 0, needsCRC = 0;
  412         int extIndex = 0, newExtIndex = 0, hasExt = 0;
  413         unsigned short valueCRC;
  414         uint8_t curr;
  415         const uint8_t hexChar[] = "0123456789ABCDEF";
  416 
  417         if (udfName[0] == '.' &&
  418             (udfLen == 1 || (udfLen == 2 && udfName[1] == '.'))) {
  419                 needsCRC = 1;
  420                 newIndex = udfLen;
  421                 memcpy(newName, udfName, udfLen);
  422         } else {
  423                 for (index = 0; index < udfLen; index++) {
  424                         curr = udfName[index];
  425                         if (curr == '/' || curr == 0) {
  426                                 needsCRC = 1;
  427                                 curr = ILLEGAL_CHAR_MARK;
  428                                 while (index + 1 < udfLen &&
  429                                                 (udfName[index + 1] == '/' ||
  430                                                  udfName[index + 1] == 0))
  431                                         index++;
  432                         }
  433                         if (curr == EXT_MARK &&
  434                                         (udfLen - index - 1) <= EXT_SIZE) {
  435                                 if (udfLen == index + 1)
  436                                         hasExt = 0;
  437                                 else {
  438                                         hasExt = 1;
  439                                         extIndex = index;
  440                                         newExtIndex = newIndex;
  441                                 }
  442                         }
  443                         if (newIndex < 256)
  444                                 newName[newIndex++] = curr;
  445                         else
  446                                 needsCRC = 1;
  447                 }
  448         }
  449         if (needsCRC) {
  450                 uint8_t ext[EXT_SIZE];
  451                 int localExtIndex = 0;
  452 
  453                 if (hasExt) {
  454                         int maxFilenameLen;
  455                         for (index = 0;
  456                              index < EXT_SIZE && extIndex + index + 1 < udfLen;
  457                              index++) {
  458                                 curr = udfName[extIndex + index + 1];
  459 
  460                                 if (curr == '/' || curr == 0) {
  461                                         needsCRC = 1;
  462                                         curr = ILLEGAL_CHAR_MARK;
  463                                         while (extIndex + index + 2 < udfLen &&
  464                                               (index + 1 < EXT_SIZE &&
  465                                                 (udfName[extIndex + index + 2] == '/' ||
  466                                                  udfName[extIndex + index + 2] == 0)))
  467                                                 index++;
  468                                 }
  469                                 ext[localExtIndex++] = curr;
  470                         }
  471                         maxFilenameLen = 250 - localExtIndex;
  472                         if (newIndex > maxFilenameLen)
  473                                 newIndex = maxFilenameLen;
  474                         else
  475                                 newIndex = newExtIndex;
  476                 } else if (newIndex > 250)
  477                         newIndex = 250;
  478                 newName[newIndex++] = CRC_MARK;
  479                 valueCRC = crc_itu_t(0, fidName, fidNameLen);
  480                 newName[newIndex++] = hexChar[(valueCRC & 0xf000) >> 12];
  481                 newName[newIndex++] = hexChar[(valueCRC & 0x0f00) >> 8];
  482                 newName[newIndex++] = hexChar[(valueCRC & 0x00f0) >> 4];
  483                 newName[newIndex++] = hexChar[(valueCRC & 0x000f)];
  484 
  485                 if (hasExt) {
  486                         newName[newIndex++] = EXT_MARK;
  487                         for (index = 0; index < localExtIndex; index++)
  488                                 newName[newIndex++] = ext[index];
  489                 }
  490         }
  491 
  492         return newIndex;
  493 }

Cache object: 646e42772afce7418ab96bfb49a1fd08


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.