Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | * |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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> |
Bob Copeland | f845fce | 2008-04-17 09:47:48 +0200 | [diff] [blame] | 26 | #include <linux/crc-itu-t.h> |
Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 27 | #include <linux/slab.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 28 | |
| 29 | #include "udf_sb.h" |
| 30 | |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 31 | static int udf_translate_to_linux(uint8_t *, int, const uint8_t *, int, |
| 32 | const uint8_t *, int); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 33 | |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 34 | static int udf_uni2char_utf8(wchar_t uni, |
| 35 | unsigned char *out, |
| 36 | int boundlen) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 37 | { |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 38 | int u_len = 0; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 39 | |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 40 | if (boundlen <= 0) |
| 41 | return -ENAMETOOLONG; |
| 42 | |
| 43 | if (uni < 0x80) { |
| 44 | out[u_len++] = (unsigned char)uni; |
| 45 | } else if (uni < 0x800) { |
| 46 | if (boundlen < 2) |
| 47 | return -ENAMETOOLONG; |
| 48 | out[u_len++] = (unsigned char)(0xc0 | (uni >> 6)); |
| 49 | out[u_len++] = (unsigned char)(0x80 | (uni & 0x3f)); |
| 50 | } else { |
| 51 | if (boundlen < 3) |
| 52 | return -ENAMETOOLONG; |
| 53 | out[u_len++] = (unsigned char)(0xe0 | (uni >> 12)); |
| 54 | out[u_len++] = (unsigned char)(0x80 | ((uni >> 6) & 0x3f)); |
| 55 | out[u_len++] = (unsigned char)(0x80 | (uni & 0x3f)); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 56 | } |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 57 | return u_len; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 58 | } |
| 59 | |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 60 | static int udf_char2uni_utf8(const unsigned char *in, |
| 61 | int boundlen, |
| 62 | wchar_t *uni) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 63 | { |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 64 | unsigned int utf_char; |
| 65 | unsigned char c; |
| 66 | int utf_cnt, u_len; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 67 | |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 68 | utf_char = 0; |
| 69 | utf_cnt = 0; |
| 70 | for (u_len = 0; u_len < boundlen;) { |
| 71 | c = in[u_len++]; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 72 | |
| 73 | /* Complete a multi-byte UTF-8 character */ |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 74 | if (utf_cnt) { |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 75 | utf_char = (utf_char << 6) | (c & 0x3f); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 76 | if (--utf_cnt) |
| 77 | continue; |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 78 | } else { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 79 | /* Check for a multi-byte UTF-8 character */ |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 80 | if (c & 0x80) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 81 | /* Start a multi-byte UTF-8 character */ |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 82 | if ((c & 0xe0) == 0xc0) { |
| 83 | utf_char = c & 0x1f; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 84 | utf_cnt = 1; |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 85 | } else if ((c & 0xf0) == 0xe0) { |
| 86 | utf_char = c & 0x0f; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 87 | utf_cnt = 2; |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 88 | } else if ((c & 0xf8) == 0xf0) { |
| 89 | utf_char = c & 0x07; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 90 | utf_cnt = 3; |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 91 | } else if ((c & 0xfc) == 0xf8) { |
| 92 | utf_char = c & 0x03; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 93 | utf_cnt = 4; |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 94 | } else if ((c & 0xfe) == 0xfc) { |
| 95 | utf_char = c & 0x01; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 96 | utf_cnt = 5; |
Cyrill Gorcunov | 28de794 | 2007-07-21 04:37:18 -0700 | [diff] [blame] | 97 | } else { |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 98 | utf_cnt = -1; |
| 99 | break; |
Cyrill Gorcunov | 28de794 | 2007-07-21 04:37:18 -0700 | [diff] [blame] | 100 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 101 | continue; |
Cyrill Gorcunov | 28de794 | 2007-07-21 04:37:18 -0700 | [diff] [blame] | 102 | } else { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 103 | /* Single byte UTF-8 character (most common) */ |
| 104 | utf_char = c; |
Cyrill Gorcunov | 28de794 | 2007-07-21 04:37:18 -0700 | [diff] [blame] | 105 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 106 | } |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 107 | *uni = utf_char; |
| 108 | break; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 109 | } |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 110 | if (utf_cnt) { |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 111 | *uni = '?'; |
| 112 | return -EINVAL; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 113 | } |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 114 | return u_len; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 115 | } |
| 116 | |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 117 | static int udf_name_from_CS0(uint8_t *str_o, int str_max_len, |
| 118 | const uint8_t *ocu, int ocu_len, |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 119 | int (*conv_f)(wchar_t, unsigned char *, int)) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 120 | { |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 121 | uint8_t cmp_id; |
Jan Kara | 59285c2 | 2009-02-04 19:46:11 +0100 | [diff] [blame] | 122 | int i, len; |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 123 | int str_o_len = 0; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 124 | |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 125 | if (str_max_len <= 0) |
| 126 | return 0; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 127 | |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 128 | if (ocu_len == 0) { |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 129 | memset(str_o, 0, str_max_len); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 130 | return 0; |
| 131 | } |
| 132 | |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 133 | cmp_id = ocu[0]; |
marcin.slusarz@gmail.com | 34f953d | 2008-02-27 22:38:36 +0100 | [diff] [blame] | 134 | if (cmp_id != 8 && cmp_id != 16) { |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 135 | memset(str_o, 0, str_max_len); |
| 136 | pr_err("unknown compression code (%d) stri=%s\n", cmp_id, ocu); |
Fabian Frederick | 78fc2e6 | 2015-04-08 21:23:55 +0200 | [diff] [blame] | 137 | return -EINVAL; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 138 | } |
| 139 | |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 140 | for (i = 1; (i < ocu_len) && (str_o_len < str_max_len);) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 141 | /* Expand OSTA compressed Unicode to Unicode */ |
marcin.slusarz@gmail.com | 34f953d | 2008-02-27 22:38:36 +0100 | [diff] [blame] | 142 | uint32_t c = ocu[i++]; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 143 | if (cmp_id == 16) |
| 144 | c = (c << 8) | ocu[i++]; |
| 145 | |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 146 | len = conv_f(c, &str_o[str_o_len], str_max_len - str_o_len); |
Jan Kara | 59285c2 | 2009-02-04 19:46:11 +0100 | [diff] [blame] | 147 | /* Valid character? */ |
| 148 | if (len >= 0) |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 149 | str_o_len += len; |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 150 | else if (len == -ENAMETOOLONG) |
| 151 | break; |
Jan Kara | 59285c2 | 2009-02-04 19:46:11 +0100 | [diff] [blame] | 152 | else |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 153 | str_o[str_o_len++] = '?'; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 154 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 155 | |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 156 | return str_o_len; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 157 | } |
| 158 | |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 159 | static int udf_name_to_CS0(uint8_t *ocu, int ocu_max_len, |
| 160 | const uint8_t *str_i, int str_len, |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 161 | int (*conv_f)(const unsigned char *, int, wchar_t *)) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 162 | { |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 163 | int i, len; |
| 164 | unsigned int max_val; |
| 165 | wchar_t uni_char; |
Andrew Gabbasov | bb00c89 | 2015-12-24 10:25:33 -0600 | [diff] [blame] | 166 | int u_len, u_ch; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 167 | |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 168 | if (ocu_max_len <= 0) |
| 169 | return 0; |
| 170 | |
| 171 | memset(ocu, 0, ocu_max_len); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 172 | ocu[0] = 8; |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 173 | max_val = 0xff; |
Andrew Gabbasov | bb00c89 | 2015-12-24 10:25:33 -0600 | [diff] [blame] | 174 | u_ch = 1; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 175 | |
Cyrill Gorcunov | 28de794 | 2007-07-21 04:37:18 -0700 | [diff] [blame] | 176 | try_again: |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 177 | u_len = 1; |
| 178 | for (i = 0; i < str_len; i++) { |
Andrew Gabbasov | bb00c89 | 2015-12-24 10:25:33 -0600 | [diff] [blame] | 179 | /* Name didn't fit? */ |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 180 | if (u_len + u_ch > ocu_max_len) |
Andrew Gabbasov | bb00c89 | 2015-12-24 10:25:33 -0600 | [diff] [blame] | 181 | return 0; |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 182 | len = conv_f(&str_i[i], str_len - i, &uni_char); |
Jan Kara | 59285c2 | 2009-02-04 19:46:11 +0100 | [diff] [blame] | 183 | if (!len) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 184 | continue; |
Jan Kara | 59285c2 | 2009-02-04 19:46:11 +0100 | [diff] [blame] | 185 | /* Invalid character, deal with it */ |
| 186 | if (len < 0) { |
| 187 | len = 1; |
| 188 | uni_char = '?'; |
| 189 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 190 | |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 191 | if (uni_char > max_val) { |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 192 | max_val = 0xffff; |
| 193 | ocu[0] = 0x10; |
Andrew Gabbasov | bb00c89 | 2015-12-24 10:25:33 -0600 | [diff] [blame] | 194 | u_ch = 2; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 195 | goto try_again; |
| 196 | } |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 197 | |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 198 | if (max_val == 0xffff) |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 199 | ocu[u_len++] = (uint8_t)(uni_char >> 8); |
| 200 | ocu[u_len++] = (uint8_t)(uni_char & 0xff); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 201 | i += len - 1; |
| 202 | } |
| 203 | |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 204 | return u_len; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 205 | } |
| 206 | |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 207 | int udf_CS0toUTF8(uint8_t *utf_o, int o_len, const uint8_t *ocu_i, int i_len) |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 208 | { |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 209 | return udf_name_from_CS0(utf_o, o_len, ocu_i, i_len, |
| 210 | udf_uni2char_utf8); |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 211 | } |
| 212 | |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 213 | int udf_get_filename(struct super_block *sb, const uint8_t *sname, int slen, |
Jan Kara | 0e5cc9a | 2014-12-18 22:37:50 +0100 | [diff] [blame] | 214 | uint8_t *dname, int dlen) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 215 | { |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 216 | uint8_t *filename; |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 217 | int (*conv_f)(wchar_t, unsigned char *, int); |
Fabian Frederick | 6ce6383 | 2015-04-08 21:23:57 +0200 | [diff] [blame] | 218 | int ret; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 219 | |
Fabian Frederick | 31f2566 | 2015-04-08 21:23:52 +0200 | [diff] [blame] | 220 | if (!slen) |
| 221 | return -EIO; |
| 222 | |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 223 | if (dlen <= 0) |
| 224 | return 0; |
| 225 | |
| 226 | filename = kmalloc(dlen, GFP_NOFS); |
Marcin Slusarz | 530f1a5 | 2008-11-16 19:02:45 +0100 | [diff] [blame] | 227 | if (!filename) |
Fabian Frederick | 5ceb8b5 | 2015-04-08 21:23:51 +0200 | [diff] [blame] | 228 | return -ENOMEM; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 229 | |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 230 | if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) { |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 231 | conv_f = udf_uni2char_utf8; |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 232 | } else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) { |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 233 | conv_f = UDF_SB(sb)->s_nls_map->uni2char; |
Marcin Slusarz | 4b11111 | 2008-02-08 04:20:36 -0800 | [diff] [blame] | 234 | } else |
Fabian Frederick | 5dce54b | 2015-04-08 21:23:56 +0200 | [diff] [blame] | 235 | BUG(); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 236 | |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 237 | ret = udf_name_from_CS0(filename, dlen, sname, slen, conv_f); |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 238 | if (ret < 0) { |
| 239 | udf_debug("Failed in udf_get_filename: sname = %s\n", sname); |
| 240 | goto out2; |
| 241 | } |
| 242 | |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 243 | ret = udf_translate_to_linux(dname, dlen, filename, dlen, |
| 244 | sname + 1, slen - 1); |
Fabian Frederick | 6ce6383 | 2015-04-08 21:23:57 +0200 | [diff] [blame] | 245 | /* Zero length filename isn't valid... */ |
| 246 | if (ret == 0) |
| 247 | ret = -EINVAL; |
Marcin Slusarz | 530f1a5 | 2008-11-16 19:02:45 +0100 | [diff] [blame] | 248 | out2: |
Marcin Slusarz | 530f1a5 | 2008-11-16 19:02:45 +0100 | [diff] [blame] | 249 | kfree(filename); |
Fabian Frederick | 5ceb8b5 | 2015-04-08 21:23:51 +0200 | [diff] [blame] | 250 | return ret; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 251 | } |
| 252 | |
Andrew Gabbasov | 525e2c5 | 2016-01-15 02:44:19 -0600 | [diff] [blame] | 253 | int udf_put_filename(struct super_block *sb, const uint8_t *sname, int slen, |
| 254 | uint8_t *dname, int dlen) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 255 | { |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 256 | int (*conv_f)(const unsigned char *, int, wchar_t *); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 257 | |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 258 | if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) { |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 259 | conv_f = udf_char2uni_utf8; |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 260 | } else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) { |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 261 | conv_f = UDF_SB(sb)->s_nls_map->char2uni; |
Marcin Slusarz | 4b11111 | 2008-02-08 04:20:36 -0800 | [diff] [blame] | 262 | } else |
Andrew Gabbasov | 3e7fc20 | 2016-01-15 02:44:20 -0600 | [diff] [blame] | 263 | BUG(); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 264 | |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 265 | return udf_name_to_CS0(dname, dlen, sname, slen, conv_f); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 266 | } |
| 267 | |
| 268 | #define ILLEGAL_CHAR_MARK '_' |
Cyrill Gorcunov | 28de794 | 2007-07-21 04:37:18 -0700 | [diff] [blame] | 269 | #define EXT_MARK '.' |
| 270 | #define CRC_MARK '#' |
| 271 | #define EXT_SIZE 5 |
Jan Kara | 0e5cc9a | 2014-12-18 22:37:50 +0100 | [diff] [blame] | 272 | /* Number of chars we need to store generated CRC to make filename unique */ |
| 273 | #define CRC_LEN 5 |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 274 | |
Jan Kara | 0e5cc9a | 2014-12-18 22:37:50 +0100 | [diff] [blame] | 275 | static int udf_translate_to_linux(uint8_t *newName, int newLen, |
Andrew Gabbasov | 9293fcf | 2016-01-15 02:44:22 -0600 | [diff] [blame^] | 276 | const uint8_t *udfName, int udfLen, |
| 277 | const uint8_t *fidName, int fidNameLen) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 278 | { |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 279 | int index, newIndex = 0, needsCRC = 0; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 280 | int extIndex = 0, newExtIndex = 0, hasExt = 0; |
| 281 | unsigned short valueCRC; |
| 282 | uint8_t curr; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 283 | |
Cyrill Gorcunov | 28de794 | 2007-07-21 04:37:18 -0700 | [diff] [blame] | 284 | if (udfName[0] == '.' && |
| 285 | (udfLen == 1 || (udfLen == 2 && udfName[1] == '.'))) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 286 | needsCRC = 1; |
| 287 | newIndex = udfLen; |
| 288 | memcpy(newName, udfName, udfLen); |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 289 | } else { |
| 290 | for (index = 0; index < udfLen; index++) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 291 | curr = udfName[index]; |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 292 | if (curr == '/' || curr == 0) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 293 | needsCRC = 1; |
| 294 | curr = ILLEGAL_CHAR_MARK; |
Marcin Slusarz | 4b11111 | 2008-02-08 04:20:36 -0800 | [diff] [blame] | 295 | while (index + 1 < udfLen && |
| 296 | (udfName[index + 1] == '/' || |
| 297 | udfName[index + 1] == 0)) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 298 | index++; |
Marcin Slusarz | 4b11111 | 2008-02-08 04:20:36 -0800 | [diff] [blame] | 299 | } |
| 300 | if (curr == EXT_MARK && |
| 301 | (udfLen - index - 1) <= EXT_SIZE) { |
| 302 | if (udfLen == index + 1) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 303 | hasExt = 0; |
Marcin Slusarz | 4b11111 | 2008-02-08 04:20:36 -0800 | [diff] [blame] | 304 | else { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 305 | hasExt = 1; |
| 306 | extIndex = index; |
| 307 | newExtIndex = newIndex; |
| 308 | } |
| 309 | } |
Jan Kara | 0e5cc9a | 2014-12-18 22:37:50 +0100 | [diff] [blame] | 310 | if (newIndex < newLen) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 311 | newName[newIndex++] = curr; |
| 312 | else |
| 313 | needsCRC = 1; |
| 314 | } |
| 315 | } |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 316 | if (needsCRC) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 317 | uint8_t ext[EXT_SIZE]; |
| 318 | int localExtIndex = 0; |
| 319 | |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 320 | if (hasExt) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 321 | int maxFilenameLen; |
Marcin Slusarz | 4b11111 | 2008-02-08 04:20:36 -0800 | [diff] [blame] | 322 | for (index = 0; |
| 323 | index < EXT_SIZE && extIndex + index + 1 < udfLen; |
| 324 | index++) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 325 | curr = udfName[extIndex + index + 1]; |
| 326 | |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 327 | if (curr == '/' || curr == 0) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 328 | needsCRC = 1; |
| 329 | curr = ILLEGAL_CHAR_MARK; |
Marcin Slusarz | 4b11111 | 2008-02-08 04:20:36 -0800 | [diff] [blame] | 330 | while (extIndex + index + 2 < udfLen && |
| 331 | (index + 1 < EXT_SIZE && |
| 332 | (udfName[extIndex + index + 2] == '/' || |
| 333 | udfName[extIndex + index + 2] == 0))) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 334 | index++; |
| 335 | } |
| 336 | ext[localExtIndex++] = curr; |
| 337 | } |
Jan Kara | 0e5cc9a | 2014-12-18 22:37:50 +0100 | [diff] [blame] | 338 | maxFilenameLen = newLen - CRC_LEN - localExtIndex; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 339 | if (newIndex > maxFilenameLen) |
| 340 | newIndex = maxFilenameLen; |
| 341 | else |
| 342 | newIndex = newExtIndex; |
Jan Kara | 0e5cc9a | 2014-12-18 22:37:50 +0100 | [diff] [blame] | 343 | } else if (newIndex > newLen - CRC_LEN) |
| 344 | newIndex = newLen - CRC_LEN; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 345 | newName[newIndex++] = CRC_MARK; |
Bob Copeland | f845fce | 2008-04-17 09:47:48 +0200 | [diff] [blame] | 346 | valueCRC = crc_itu_t(0, fidName, fidNameLen); |
Andy Shevchenko | c7ff482 | 2014-07-09 15:35:30 +0300 | [diff] [blame] | 347 | newName[newIndex++] = hex_asc_upper_hi(valueCRC >> 8); |
| 348 | newName[newIndex++] = hex_asc_upper_lo(valueCRC >> 8); |
| 349 | newName[newIndex++] = hex_asc_upper_hi(valueCRC); |
| 350 | newName[newIndex++] = hex_asc_upper_lo(valueCRC); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 351 | |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 352 | if (hasExt) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 353 | newName[newIndex++] = EXT_MARK; |
Cyrill Gorcunov | cb00ea3 | 2007-07-19 01:47:43 -0700 | [diff] [blame] | 354 | for (index = 0; index < localExtIndex; index++) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 355 | newName[newIndex++] = ext[index]; |
| 356 | } |
| 357 | } |
Cyrill Gorcunov | 28de794 | 2007-07-21 04:37:18 -0700 | [diff] [blame] | 358 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 359 | return newIndex; |
| 360 | } |