162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * unicode.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * PURPOSE
662306a36Sopenharmony_ci *	Routines for converting between UTF-8 and OSTA Compressed Unicode.
762306a36Sopenharmony_ci *      Also handles filename mangling
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * DESCRIPTION
1062306a36Sopenharmony_ci *	OSTA Compressed Unicode is explained in the OSTA UDF specification.
1162306a36Sopenharmony_ci *		http://www.osta.org/
1262306a36Sopenharmony_ci *	UTF-8 is explained in the IETF RFC XXXX.
1362306a36Sopenharmony_ci *		ftp://ftp.internic.net/rfc/rfcxxxx.txt
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "udfdecl.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/kernel.h>
2062306a36Sopenharmony_ci#include <linux/string.h>	/* for memset */
2162306a36Sopenharmony_ci#include <linux/nls.h>
2262306a36Sopenharmony_ci#include <linux/crc-itu-t.h>
2362306a36Sopenharmony_ci#include <linux/slab.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "udf_sb.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define PLANE_SIZE 0x10000
2862306a36Sopenharmony_ci#define UNICODE_MAX 0x10ffff
2962306a36Sopenharmony_ci#define SURROGATE_MASK 0xfffff800
3062306a36Sopenharmony_ci#define SURROGATE_PAIR 0x0000d800
3162306a36Sopenharmony_ci#define SURROGATE_LOW  0x00000400
3262306a36Sopenharmony_ci#define SURROGATE_CHAR_BITS 10
3362306a36Sopenharmony_ci#define SURROGATE_CHAR_MASK ((1 << SURROGATE_CHAR_BITS) - 1)
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define ILLEGAL_CHAR_MARK	'_'
3662306a36Sopenharmony_ci#define EXT_MARK		'.'
3762306a36Sopenharmony_ci#define CRC_MARK		'#'
3862306a36Sopenharmony_ci#define EXT_SIZE		5
3962306a36Sopenharmony_ci/* Number of chars we need to store generated CRC to make filename unique */
4062306a36Sopenharmony_ci#define CRC_LEN			5
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic unicode_t get_utf16_char(const uint8_t *str_i, int str_i_max_len,
4362306a36Sopenharmony_ci				int str_i_idx, int u_ch, unicode_t *ret)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	unicode_t c;
4662306a36Sopenharmony_ci	int start_idx = str_i_idx;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	/* Expand OSTA compressed Unicode to Unicode */
4962306a36Sopenharmony_ci	c = str_i[str_i_idx++];
5062306a36Sopenharmony_ci	if (u_ch > 1)
5162306a36Sopenharmony_ci		c = (c << 8) | str_i[str_i_idx++];
5262306a36Sopenharmony_ci	if ((c & SURROGATE_MASK) == SURROGATE_PAIR) {
5362306a36Sopenharmony_ci		unicode_t next;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		/* Trailing surrogate char */
5662306a36Sopenharmony_ci		if (str_i_idx >= str_i_max_len) {
5762306a36Sopenharmony_ci			c = UNICODE_MAX + 1;
5862306a36Sopenharmony_ci			goto out;
5962306a36Sopenharmony_ci		}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		/* Low surrogate must follow the high one... */
6262306a36Sopenharmony_ci		if (c & SURROGATE_LOW) {
6362306a36Sopenharmony_ci			c = UNICODE_MAX + 1;
6462306a36Sopenharmony_ci			goto out;
6562306a36Sopenharmony_ci		}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci		WARN_ON_ONCE(u_ch != 2);
6862306a36Sopenharmony_ci		next = str_i[str_i_idx++] << 8;
6962306a36Sopenharmony_ci		next |= str_i[str_i_idx++];
7062306a36Sopenharmony_ci		if ((next & SURROGATE_MASK) != SURROGATE_PAIR ||
7162306a36Sopenharmony_ci		    !(next & SURROGATE_LOW)) {
7262306a36Sopenharmony_ci			c = UNICODE_MAX + 1;
7362306a36Sopenharmony_ci			goto out;
7462306a36Sopenharmony_ci		}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci		c = PLANE_SIZE +
7762306a36Sopenharmony_ci		    ((c & SURROGATE_CHAR_MASK) << SURROGATE_CHAR_BITS) +
7862306a36Sopenharmony_ci		    (next & SURROGATE_CHAR_MASK);
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ciout:
8162306a36Sopenharmony_ci	*ret = c;
8262306a36Sopenharmony_ci	return str_i_idx - start_idx;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int udf_name_conv_char(uint8_t *str_o, int str_o_max_len,
8762306a36Sopenharmony_ci			      int *str_o_idx,
8862306a36Sopenharmony_ci			      const uint8_t *str_i, int str_i_max_len,
8962306a36Sopenharmony_ci			      int *str_i_idx,
9062306a36Sopenharmony_ci			      int u_ch, int *needsCRC,
9162306a36Sopenharmony_ci			      int (*conv_f)(wchar_t, unsigned char *, int),
9262306a36Sopenharmony_ci			      int translate)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	unicode_t c;
9562306a36Sopenharmony_ci	int illChar = 0;
9662306a36Sopenharmony_ci	int len, gotch = 0;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	while (!gotch && *str_i_idx < str_i_max_len) {
9962306a36Sopenharmony_ci		if (*str_o_idx >= str_o_max_len) {
10062306a36Sopenharmony_ci			*needsCRC = 1;
10162306a36Sopenharmony_ci			return gotch;
10262306a36Sopenharmony_ci		}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		len = get_utf16_char(str_i, str_i_max_len, *str_i_idx, u_ch,
10562306a36Sopenharmony_ci				     &c);
10662306a36Sopenharmony_ci		/* These chars cannot be converted. Replace them. */
10762306a36Sopenharmony_ci		if (c == 0 || c > UNICODE_MAX || (conv_f && c > MAX_WCHAR_T) ||
10862306a36Sopenharmony_ci		    (translate && c == '/')) {
10962306a36Sopenharmony_ci			illChar = 1;
11062306a36Sopenharmony_ci			if (!translate)
11162306a36Sopenharmony_ci				gotch = 1;
11262306a36Sopenharmony_ci		} else if (illChar)
11362306a36Sopenharmony_ci			break;
11462306a36Sopenharmony_ci		else
11562306a36Sopenharmony_ci			gotch = 1;
11662306a36Sopenharmony_ci		*str_i_idx += len;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci	if (illChar) {
11962306a36Sopenharmony_ci		*needsCRC = 1;
12062306a36Sopenharmony_ci		c = ILLEGAL_CHAR_MARK;
12162306a36Sopenharmony_ci		gotch = 1;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci	if (gotch) {
12462306a36Sopenharmony_ci		if (conv_f) {
12562306a36Sopenharmony_ci			len = conv_f(c, &str_o[*str_o_idx],
12662306a36Sopenharmony_ci				     str_o_max_len - *str_o_idx);
12762306a36Sopenharmony_ci		} else {
12862306a36Sopenharmony_ci			len = utf32_to_utf8(c, &str_o[*str_o_idx],
12962306a36Sopenharmony_ci					    str_o_max_len - *str_o_idx);
13062306a36Sopenharmony_ci			if (len < 0)
13162306a36Sopenharmony_ci				len = -ENAMETOOLONG;
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci		/* Valid character? */
13462306a36Sopenharmony_ci		if (len >= 0)
13562306a36Sopenharmony_ci			*str_o_idx += len;
13662306a36Sopenharmony_ci		else if (len == -ENAMETOOLONG) {
13762306a36Sopenharmony_ci			*needsCRC = 1;
13862306a36Sopenharmony_ci			gotch = 0;
13962306a36Sopenharmony_ci		} else {
14062306a36Sopenharmony_ci			str_o[(*str_o_idx)++] = ILLEGAL_CHAR_MARK;
14162306a36Sopenharmony_ci			*needsCRC = 1;
14262306a36Sopenharmony_ci		}
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci	return gotch;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int udf_name_from_CS0(struct super_block *sb,
14862306a36Sopenharmony_ci			     uint8_t *str_o, int str_max_len,
14962306a36Sopenharmony_ci			     const uint8_t *ocu, int ocu_len,
15062306a36Sopenharmony_ci			     int translate)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	uint32_t c;
15362306a36Sopenharmony_ci	uint8_t cmp_id;
15462306a36Sopenharmony_ci	int idx, len;
15562306a36Sopenharmony_ci	int u_ch;
15662306a36Sopenharmony_ci	int needsCRC = 0;
15762306a36Sopenharmony_ci	int ext_i_len, ext_max_len;
15862306a36Sopenharmony_ci	int str_o_len = 0;	/* Length of resulting output */
15962306a36Sopenharmony_ci	int ext_o_len = 0;	/* Extension output length */
16062306a36Sopenharmony_ci	int ext_crc_len = 0;	/* Extension output length if used with CRC */
16162306a36Sopenharmony_ci	int i_ext = -1;		/* Extension position in input buffer */
16262306a36Sopenharmony_ci	int o_crc = 0;		/* Rightmost possible output pos for CRC+ext */
16362306a36Sopenharmony_ci	unsigned short valueCRC;
16462306a36Sopenharmony_ci	uint8_t ext[EXT_SIZE * NLS_MAX_CHARSET_SIZE + 1];
16562306a36Sopenharmony_ci	uint8_t crc[CRC_LEN];
16662306a36Sopenharmony_ci	int (*conv_f)(wchar_t, unsigned char *, int);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (str_max_len <= 0)
16962306a36Sopenharmony_ci		return 0;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (ocu_len == 0) {
17262306a36Sopenharmony_ci		memset(str_o, 0, str_max_len);
17362306a36Sopenharmony_ci		return 0;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (UDF_SB(sb)->s_nls_map)
17762306a36Sopenharmony_ci		conv_f = UDF_SB(sb)->s_nls_map->uni2char;
17862306a36Sopenharmony_ci	else
17962306a36Sopenharmony_ci		conv_f = NULL;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	cmp_id = ocu[0];
18262306a36Sopenharmony_ci	if (cmp_id != 8 && cmp_id != 16) {
18362306a36Sopenharmony_ci		memset(str_o, 0, str_max_len);
18462306a36Sopenharmony_ci		pr_err("unknown compression code (%u)\n", cmp_id);
18562306a36Sopenharmony_ci		return -EINVAL;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci	u_ch = cmp_id >> 3;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	ocu++;
19062306a36Sopenharmony_ci	ocu_len--;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (ocu_len % u_ch) {
19362306a36Sopenharmony_ci		pr_err("incorrect filename length (%d)\n", ocu_len + 1);
19462306a36Sopenharmony_ci		return -EINVAL;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (translate) {
19862306a36Sopenharmony_ci		/* Look for extension */
19962306a36Sopenharmony_ci		for (idx = ocu_len - u_ch, ext_i_len = 0;
20062306a36Sopenharmony_ci		     (idx >= 0) && (ext_i_len < EXT_SIZE);
20162306a36Sopenharmony_ci		     idx -= u_ch, ext_i_len++) {
20262306a36Sopenharmony_ci			c = ocu[idx];
20362306a36Sopenharmony_ci			if (u_ch > 1)
20462306a36Sopenharmony_ci				c = (c << 8) | ocu[idx + 1];
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci			if (c == EXT_MARK) {
20762306a36Sopenharmony_ci				if (ext_i_len)
20862306a36Sopenharmony_ci					i_ext = idx;
20962306a36Sopenharmony_ci				break;
21062306a36Sopenharmony_ci			}
21162306a36Sopenharmony_ci		}
21262306a36Sopenharmony_ci		if (i_ext >= 0) {
21362306a36Sopenharmony_ci			/* Convert extension */
21462306a36Sopenharmony_ci			ext_max_len = min_t(int, sizeof(ext), str_max_len);
21562306a36Sopenharmony_ci			ext[ext_o_len++] = EXT_MARK;
21662306a36Sopenharmony_ci			idx = i_ext + u_ch;
21762306a36Sopenharmony_ci			while (udf_name_conv_char(ext, ext_max_len, &ext_o_len,
21862306a36Sopenharmony_ci						  ocu, ocu_len, &idx,
21962306a36Sopenharmony_ci						  u_ch, &needsCRC,
22062306a36Sopenharmony_ci						  conv_f, translate)) {
22162306a36Sopenharmony_ci				if ((ext_o_len + CRC_LEN) < str_max_len)
22262306a36Sopenharmony_ci					ext_crc_len = ext_o_len;
22362306a36Sopenharmony_ci			}
22462306a36Sopenharmony_ci		}
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	idx = 0;
22862306a36Sopenharmony_ci	while (1) {
22962306a36Sopenharmony_ci		if (translate && (idx == i_ext)) {
23062306a36Sopenharmony_ci			if (str_o_len > (str_max_len - ext_o_len))
23162306a36Sopenharmony_ci				needsCRC = 1;
23262306a36Sopenharmony_ci			break;
23362306a36Sopenharmony_ci		}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci		if (!udf_name_conv_char(str_o, str_max_len, &str_o_len,
23662306a36Sopenharmony_ci					ocu, ocu_len, &idx,
23762306a36Sopenharmony_ci					u_ch, &needsCRC, conv_f, translate))
23862306a36Sopenharmony_ci			break;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		if (translate &&
24162306a36Sopenharmony_ci		    (str_o_len <= (str_max_len - ext_o_len - CRC_LEN)))
24262306a36Sopenharmony_ci			o_crc = str_o_len;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (translate) {
24662306a36Sopenharmony_ci		if (str_o_len > 0 && str_o_len <= 2 && str_o[0] == '.' &&
24762306a36Sopenharmony_ci		    (str_o_len == 1 || str_o[1] == '.'))
24862306a36Sopenharmony_ci			needsCRC = 1;
24962306a36Sopenharmony_ci		if (needsCRC) {
25062306a36Sopenharmony_ci			str_o_len = o_crc;
25162306a36Sopenharmony_ci			valueCRC = crc_itu_t(0, ocu, ocu_len);
25262306a36Sopenharmony_ci			crc[0] = CRC_MARK;
25362306a36Sopenharmony_ci			crc[1] = hex_asc_upper_hi(valueCRC >> 8);
25462306a36Sopenharmony_ci			crc[2] = hex_asc_upper_lo(valueCRC >> 8);
25562306a36Sopenharmony_ci			crc[3] = hex_asc_upper_hi(valueCRC);
25662306a36Sopenharmony_ci			crc[4] = hex_asc_upper_lo(valueCRC);
25762306a36Sopenharmony_ci			len = min_t(int, CRC_LEN, str_max_len - str_o_len);
25862306a36Sopenharmony_ci			memcpy(&str_o[str_o_len], crc, len);
25962306a36Sopenharmony_ci			str_o_len += len;
26062306a36Sopenharmony_ci			ext_o_len = ext_crc_len;
26162306a36Sopenharmony_ci		}
26262306a36Sopenharmony_ci		if (ext_o_len > 0) {
26362306a36Sopenharmony_ci			memcpy(&str_o[str_o_len], ext, ext_o_len);
26462306a36Sopenharmony_ci			str_o_len += ext_o_len;
26562306a36Sopenharmony_ci		}
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return str_o_len;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic int udf_name_to_CS0(struct super_block *sb,
27262306a36Sopenharmony_ci			   uint8_t *ocu, int ocu_max_len,
27362306a36Sopenharmony_ci			   const uint8_t *str_i, int str_len)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	int i, len;
27662306a36Sopenharmony_ci	unsigned int max_val;
27762306a36Sopenharmony_ci	int u_len, u_ch;
27862306a36Sopenharmony_ci	unicode_t uni_char;
27962306a36Sopenharmony_ci	int (*conv_f)(const unsigned char *, int, wchar_t *);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (ocu_max_len <= 0)
28262306a36Sopenharmony_ci		return 0;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (UDF_SB(sb)->s_nls_map)
28562306a36Sopenharmony_ci		conv_f = UDF_SB(sb)->s_nls_map->char2uni;
28662306a36Sopenharmony_ci	else
28762306a36Sopenharmony_ci		conv_f = NULL;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	memset(ocu, 0, ocu_max_len);
29062306a36Sopenharmony_ci	ocu[0] = 8;
29162306a36Sopenharmony_ci	max_val = 0xff;
29262306a36Sopenharmony_ci	u_ch = 1;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_citry_again:
29562306a36Sopenharmony_ci	u_len = 1;
29662306a36Sopenharmony_ci	for (i = 0; i < str_len; i += len) {
29762306a36Sopenharmony_ci		/* Name didn't fit? */
29862306a36Sopenharmony_ci		if (u_len + u_ch > ocu_max_len)
29962306a36Sopenharmony_ci			return 0;
30062306a36Sopenharmony_ci		if (conv_f) {
30162306a36Sopenharmony_ci			wchar_t wchar;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci			len = conv_f(&str_i[i], str_len - i, &wchar);
30462306a36Sopenharmony_ci			if (len > 0)
30562306a36Sopenharmony_ci				uni_char = wchar;
30662306a36Sopenharmony_ci		} else {
30762306a36Sopenharmony_ci			len = utf8_to_utf32(&str_i[i], str_len - i,
30862306a36Sopenharmony_ci					    &uni_char);
30962306a36Sopenharmony_ci		}
31062306a36Sopenharmony_ci		/* Invalid character, deal with it */
31162306a36Sopenharmony_ci		if (len <= 0 || uni_char > UNICODE_MAX) {
31262306a36Sopenharmony_ci			len = 1;
31362306a36Sopenharmony_ci			uni_char = '?';
31462306a36Sopenharmony_ci		}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		if (uni_char > max_val) {
31762306a36Sopenharmony_ci			unicode_t c;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci			if (max_val == 0xff) {
32062306a36Sopenharmony_ci				max_val = 0xffff;
32162306a36Sopenharmony_ci				ocu[0] = 0x10;
32262306a36Sopenharmony_ci				u_ch = 2;
32362306a36Sopenharmony_ci				goto try_again;
32462306a36Sopenharmony_ci			}
32562306a36Sopenharmony_ci			/*
32662306a36Sopenharmony_ci			 * Use UTF-16 encoding for chars outside we
32762306a36Sopenharmony_ci			 * cannot encode directly.
32862306a36Sopenharmony_ci			 */
32962306a36Sopenharmony_ci			if (u_len + 2 * u_ch > ocu_max_len)
33062306a36Sopenharmony_ci				return 0;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci			uni_char -= PLANE_SIZE;
33362306a36Sopenharmony_ci			c = SURROGATE_PAIR |
33462306a36Sopenharmony_ci			    ((uni_char >> SURROGATE_CHAR_BITS) &
33562306a36Sopenharmony_ci			     SURROGATE_CHAR_MASK);
33662306a36Sopenharmony_ci			ocu[u_len++] = (uint8_t)(c >> 8);
33762306a36Sopenharmony_ci			ocu[u_len++] = (uint8_t)(c & 0xff);
33862306a36Sopenharmony_ci			uni_char = SURROGATE_PAIR | SURROGATE_LOW |
33962306a36Sopenharmony_ci					(uni_char & SURROGATE_CHAR_MASK);
34062306a36Sopenharmony_ci		}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci		if (max_val == 0xffff)
34362306a36Sopenharmony_ci			ocu[u_len++] = (uint8_t)(uni_char >> 8);
34462306a36Sopenharmony_ci		ocu[u_len++] = (uint8_t)(uni_char & 0xff);
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return u_len;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci/*
35162306a36Sopenharmony_ci * Convert CS0 dstring to output charset. Warning: This function may truncate
35262306a36Sopenharmony_ci * input string if it is too long as it is used for informational strings only
35362306a36Sopenharmony_ci * and it is better to truncate the string than to refuse mounting a media.
35462306a36Sopenharmony_ci */
35562306a36Sopenharmony_ciint udf_dstrCS0toChar(struct super_block *sb, uint8_t *utf_o, int o_len,
35662306a36Sopenharmony_ci		      const uint8_t *ocu_i, int i_len)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	int s_len = 0;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	if (i_len > 0) {
36162306a36Sopenharmony_ci		s_len = ocu_i[i_len - 1];
36262306a36Sopenharmony_ci		if (s_len >= i_len) {
36362306a36Sopenharmony_ci			pr_warn("incorrect dstring lengths (%d/%d),"
36462306a36Sopenharmony_ci				" truncating\n", s_len, i_len);
36562306a36Sopenharmony_ci			s_len = i_len - 1;
36662306a36Sopenharmony_ci			/* 2-byte encoding? Need to round properly... */
36762306a36Sopenharmony_ci			if (ocu_i[0] == 16)
36862306a36Sopenharmony_ci				s_len -= (s_len - 1) & 2;
36962306a36Sopenharmony_ci		}
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	return udf_name_from_CS0(sb, utf_o, o_len, ocu_i, s_len, 0);
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ciint udf_get_filename(struct super_block *sb, const uint8_t *sname, int slen,
37662306a36Sopenharmony_ci		     uint8_t *dname, int dlen)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	int ret;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	if (!slen)
38162306a36Sopenharmony_ci		return -EIO;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (dlen <= 0)
38462306a36Sopenharmony_ci		return 0;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	ret = udf_name_from_CS0(sb, dname, dlen, sname, slen, 1);
38762306a36Sopenharmony_ci	/* Zero length filename isn't valid... */
38862306a36Sopenharmony_ci	if (ret == 0)
38962306a36Sopenharmony_ci		ret = -EINVAL;
39062306a36Sopenharmony_ci	return ret;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ciint udf_put_filename(struct super_block *sb, const uint8_t *sname, int slen,
39462306a36Sopenharmony_ci		     uint8_t *dname, int dlen)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	return udf_name_to_CS0(sb, dname, dlen, sname, slen);
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
399