Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
e9d4cf4
Documentation
arch
block
crypto
drivers
firmware
fs
9p
adfs
affs
afs
autofs4
befs
bfs
btrfs
cachefiles
ceph
cifs
coda
configfs
cramfs
debugfs
devpts
dlm
ecryptfs
efivarfs
efs
exofs
exportfs
ext2
ext3
ext4
f2fs
fat
freevxfs
fscache
fuse
gfs2
hfs
hfsplus
hostfs
hpfs
hppfs
hugetlbfs
isofs
jbd
jbd2
jffs2
jfs
kernfs
lockd
logfs
minix
ncpfs
nfs
nfs_common
nfsd
nilfs2
nls
notify
ntfs
ocfs2
omfs
openpromfs
overlayfs
proc
pstore
qnx4
qnx6
quota
ramfs
reiserfs
romfs
squashfs
sysfs
sysv
tracefs
ubifs
udf
Kconfig
Makefile
balloc.c
dir.c
directory.c
ecma_167.h
file.c
ialloc.c
inode.c
lowlevel.c
misc.c
namei.c
osta_udf.h
partition.c
super.c
symlink.c
truncate.c
udf_i.h
udf_sb.h
udfdecl.h
udfend.h
udftime.c
unicode.c
ufs
xfs
Kconfig
Kconfig.binfmt
Makefile
aio.c
anon_inodes.c
attr.c
bad_inode.c
binfmt_aout.c
binfmt_elf.c
binfmt_elf_fdpic.c
binfmt_em86.c
binfmt_flat.c
binfmt_misc.c
binfmt_script.c
block_dev.c
buffer.c
char_dev.c
compat.c
compat_binfmt_elf.c
compat_ioctl.c
coredump.c
dax.c
dcache.c
dcookies.c
direct-io.c
drop_caches.c
eventfd.c
eventpoll.c
exec.c
fcntl.c
fhandle.c
file.c
file_table.c
filesystems.c
fs-writeback.c
fs_pin.c
fs_struct.c
inode.c
internal.h
ioctl.c
libfs.c
locks.c
mbcache.c
mount.h
mpage.c
namei.c
namespace.c
no-block.c
nsfs.c
open.c
pipe.c
pnode.c
pnode.h
posix_acl.c
proc_namespace.c
read_write.c
readdir.c
select.c
seq_file.c
signalfd.c
splice.c
stack.c
stat.c
statfs.c
super.c
sync.c
timerfd.c
utimes.c
xattr.c
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
REPORTING-BUGS
Breadcrumbs
linux
/
fs
/
udf
/
unicode.c
Blame
Blame
Latest commit
History
History
495 lines (437 loc) · 11.2 KB
Breadcrumbs
linux
/
fs
/
udf
/
unicode.c
Top
File metadata and controls
Code
Blame
495 lines (437 loc) · 11.2 KB
Raw
/* * unicode.c * * PURPOSE * Routines for converting between UTF-8 and OSTA Compressed Unicode. * Also handles filename mangling * * DESCRIPTION * OSTA Compressed Unicode is explained in the OSTA UDF specification. * http://www.osta.org/ * UTF-8 is explained in the IETF RFC XXXX. * ftp://ftp.internic.net/rfc/rfcxxxx.txt * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. */ #include "udfdecl.h" #include <linux/kernel.h> #include <linux/string.h> /* for memset */ #include <linux/nls.h> #include <linux/crc-itu-t.h> #include <linux/slab.h> #include "udf_sb.h" static int udf_translate_to_linux(uint8_t *, int, uint8_t *, int, uint8_t *, int); static int udf_char_to_ustr(struct ustr *dest, const uint8_t *src, int strlen) { if ((!dest) || (!src) || (!strlen) || (strlen > UDF_NAME_LEN - 2)) return 0; memset(dest, 0, sizeof(struct ustr)); memcpy(dest->u_name, src, strlen); dest->u_cmpID = 0x08; dest->u_len = strlen; return strlen; } /* * udf_build_ustr */ int udf_build_ustr(struct ustr *dest, dstring *ptr, int size) { int usesize; if (!dest || !ptr || !size) return -1; BUG_ON(size < 2); usesize = min_t(size_t, ptr[size - 1], sizeof(dest->u_name)); usesize = min(usesize, size - 2); dest->u_cmpID = ptr[0]; dest->u_len = usesize; memcpy(dest->u_name, ptr + 1, usesize); memset(dest->u_name + usesize, 0, sizeof(dest->u_name) - usesize); return 0; } /* * udf_build_ustr_exact */ static void udf_build_ustr_exact(struct ustr *dest, dstring *ptr, int exactsize) { memset(dest, 0, sizeof(struct ustr)); dest->u_cmpID = ptr[0]; dest->u_len = exactsize - 1; memcpy(dest->u_name, ptr + 1, exactsize - 1); } /* * udf_CS0toUTF8 * * PURPOSE * Convert OSTA Compressed Unicode to the UTF-8 equivalent. * * PRE-CONDITIONS * utf Pointer to UTF-8 output buffer. * ocu Pointer to OSTA Compressed Unicode input buffer * of size UDF_NAME_LEN bytes. * both of type "struct ustr *" * * POST-CONDITIONS * <return> >= 0 on success. * * HISTORY * November 12, 1997 - Andrew E. Mileski * Written, tested, and released. */ int udf_CS0toUTF8(struct ustr *utf_o, const struct ustr *ocu_i) { const uint8_t *ocu; uint8_t cmp_id, ocu_len; int i; ocu_len = ocu_i->u_len; if (ocu_len == 0) { memset(utf_o, 0, sizeof(struct ustr)); return 0; } cmp_id = ocu_i->u_cmpID; if (cmp_id != 8 && cmp_id != 16) { memset(utf_o, 0, sizeof(struct ustr)); pr_err("unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name); return -EINVAL; } ocu = ocu_i->u_name; utf_o->u_len = 0; for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN - 3));) { /* Expand OSTA compressed Unicode to Unicode */ uint32_t c = ocu[i++]; if (cmp_id == 16) c = (c << 8) | ocu[i++]; /* Compress Unicode to UTF-8 */ if (c < 0x80U) utf_o->u_name[utf_o->u_len++] = (uint8_t)c; else if (c < 0x800U) { utf_o->u_name[utf_o->u_len++] = (uint8_t)(0xc0 | (c >> 6)); utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | (c & 0x3f)); } else { utf_o->u_name[utf_o->u_len++] = (uint8_t)(0xe0 | (c >> 12)); utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | ((c >> 6) & 0x3f)); utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | (c & 0x3f)); } } utf_o->u_cmpID = 8; return utf_o->u_len; } /* * * udf_UTF8toCS0 * * PURPOSE * Convert UTF-8 to the OSTA Compressed Unicode equivalent. * * DESCRIPTION * This routine is only called by udf_lookup(). * * PRE-CONDITIONS * ocu Pointer to OSTA Compressed Unicode output * buffer of size UDF_NAME_LEN bytes. * utf Pointer to UTF-8 input buffer. * utf_len Length of UTF-8 input buffer in bytes. * * POST-CONDITIONS * <return> Zero on success. * * HISTORY * November 12, 1997 - Andrew E. Mileski * Written, tested, and released. */ static int udf_UTF8toCS0(dstring *ocu, struct ustr *utf, int length) { unsigned c, i, max_val, utf_char; int utf_cnt, u_len; memset(ocu, 0, sizeof(dstring) * length); ocu[0] = 8; max_val = 0xffU; try_again: u_len = 0U; utf_char = 0U; utf_cnt = 0U; for (i = 0U; i < utf->u_len; i++) { c = (uint8_t)utf->u_name[i]; /* Complete a multi-byte UTF-8 character */ if (utf_cnt) { utf_char = (utf_char << 6) | (c & 0x3fU); if (--utf_cnt) continue; } else { /* Check for a multi-byte UTF-8 character */ if (c & 0x80U) { /* Start a multi-byte UTF-8 character */ if ((c & 0xe0U) == 0xc0U) { utf_char = c & 0x1fU; utf_cnt = 1; } else if ((c & 0xf0U) == 0xe0U) { utf_char = c & 0x0fU; utf_cnt = 2; } else if ((c & 0xf8U) == 0xf0U) { utf_char = c & 0x07U; utf_cnt = 3; } else if ((c & 0xfcU) == 0xf8U) { utf_char = c & 0x03U; utf_cnt = 4; } else if ((c & 0xfeU) == 0xfcU) { utf_char = c & 0x01U; utf_cnt = 5; } else { goto error_out; } continue; } else { /* Single byte UTF-8 character (most common) */ utf_char = c; } } /* Choose no compression if necessary */ if (utf_char > max_val) { if (max_val == 0xffU) { max_val = 0xffffU; ocu[0] = (uint8_t)0x10U; goto try_again; } goto error_out; } if (max_val == 0xffffU) ocu[++u_len] = (uint8_t)(utf_char >> 8); ocu[++u_len] = (uint8_t)(utf_char & 0xffU); } if (utf_cnt) { error_out: ocu[++u_len] = '?'; printk(KERN_DEBUG pr_fmt("bad UTF-8 character\n")); } ocu[length - 1] = (uint8_t)u_len + 1; return u_len + 1; } static int udf_CS0toNLS(struct nls_table *nls, struct ustr *utf_o, const struct ustr *ocu_i) { const uint8_t *ocu; uint8_t cmp_id, ocu_len; int i, len; ocu_len = ocu_i->u_len; if (ocu_len == 0) { memset(utf_o, 0, sizeof(struct ustr)); return 0; } cmp_id = ocu_i->u_cmpID; if (cmp_id != 8 && cmp_id != 16) { memset(utf_o, 0, sizeof(struct ustr)); pr_err("unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name); return 0; } ocu = ocu_i->u_name; utf_o->u_len = 0; for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN - 3));) { /* Expand OSTA compressed Unicode to Unicode */ uint32_t c = ocu[i++]; if (cmp_id == 16) c = (c << 8) | ocu[i++]; len = nls->uni2char(c, &utf_o->u_name[utf_o->u_len], UDF_NAME_LEN - utf_o->u_len); /* Valid character? */ if (len >= 0) utf_o->u_len += len; else utf_o->u_name[utf_o->u_len++] = '?'; } utf_o->u_cmpID = 8; return utf_o->u_len; } static int udf_NLStoCS0(struct nls_table *nls, dstring *ocu, struct ustr *uni, int length) { int len; unsigned i, max_val; uint16_t uni_char; int u_len; memset(ocu, 0, sizeof(dstring) * length); ocu[0] = 8; max_val = 0xffU; try_again: u_len = 0U; for (i = 0U; i < uni->u_len; i++) { len = nls->char2uni(&uni->u_name[i], uni->u_len - i, &uni_char); if (!len) continue; /* Invalid character, deal with it */ if (len < 0) { len = 1; uni_char = '?'; } if (uni_char > max_val) { max_val = 0xffffU; ocu[0] = (uint8_t)0x10U; goto try_again; } if (max_val == 0xffffU) ocu[++u_len] = (uint8_t)(uni_char >> 8); ocu[++u_len] = (uint8_t)(uni_char & 0xffU); i += len - 1; } ocu[length - 1] = (uint8_t)u_len + 1; return u_len + 1; } int udf_get_filename(struct super_block *sb, uint8_t *sname, int slen, uint8_t *dname, int dlen) { struct ustr *filename, *unifilename; int ret = 0; if (!slen) return -EIO; filename = kmalloc(sizeof(struct ustr), GFP_NOFS); if (!filename) return -ENOMEM; unifilename = kmalloc(sizeof(struct ustr), GFP_NOFS); if (!unifilename) { ret = -ENOMEM; goto out1; } udf_build_ustr_exact(unifilename, sname, slen); if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) { ret = udf_CS0toUTF8(filename, unifilename); if (ret < 0) { udf_debug("Failed in udf_get_filename: sname = %s\n", sname); goto out2; } } else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) { if (!udf_CS0toNLS(UDF_SB(sb)->s_nls_map, filename, unifilename)) { udf_debug("Failed in udf_get_filename: sname = %s\n", sname); goto out2; } } else goto out2; ret = udf_translate_to_linux(dname, dlen, filename->u_name, filename->u_len, unifilename->u_name, unifilename->u_len); out2: kfree(unifilename); out1: kfree(filename); return ret; } int udf_put_filename(struct super_block *sb, const uint8_t *sname, uint8_t *dname, int flen) { struct ustr unifilename; int namelen; if (!udf_char_to_ustr(&unifilename, sname, flen)) return 0; if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) { namelen = udf_UTF8toCS0(dname, &unifilename, UDF_NAME_LEN); if (!namelen) return 0; } else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) { namelen = udf_NLStoCS0(UDF_SB(sb)->s_nls_map, dname, &unifilename, UDF_NAME_LEN); if (!namelen) return 0; } else return 0; return namelen; } #define ILLEGAL_CHAR_MARK '_' #define EXT_MARK '.' #define CRC_MARK '#' #define EXT_SIZE 5 /* Number of chars we need to store generated CRC to make filename unique */ #define CRC_LEN 5 static int udf_translate_to_linux(uint8_t *newName, int newLen, uint8_t *udfName, int udfLen, uint8_t *fidName, int fidNameLen) { int index, newIndex = 0, needsCRC = 0; int extIndex = 0, newExtIndex = 0, hasExt = 0; unsigned short valueCRC; uint8_t curr; if (udfName[0] == '.' && (udfLen == 1 || (udfLen == 2 && udfName[1] == '.'))) { needsCRC = 1; newIndex = udfLen; memcpy(newName, udfName, udfLen); } else { for (index = 0; index < udfLen; index++) { curr = udfName[index]; if (curr == '/' || curr == 0) { needsCRC = 1; curr = ILLEGAL_CHAR_MARK; while (index + 1 < udfLen && (udfName[index + 1] == '/' || udfName[index + 1] == 0)) index++; } if (curr == EXT_MARK && (udfLen - index - 1) <= EXT_SIZE) { if (udfLen == index + 1) hasExt = 0; else { hasExt = 1; extIndex = index; newExtIndex = newIndex; } } if (newIndex < newLen) newName[newIndex++] = curr; else needsCRC = 1; } } if (needsCRC) { uint8_t ext[EXT_SIZE]; int localExtIndex = 0; if (hasExt) { int maxFilenameLen; for (index = 0; index < EXT_SIZE && extIndex + index + 1 < udfLen; index++) { curr = udfName[extIndex + index + 1]; if (curr == '/' || curr == 0) { needsCRC = 1; curr = ILLEGAL_CHAR_MARK; while (extIndex + index + 2 < udfLen && (index + 1 < EXT_SIZE && (udfName[extIndex + index + 2] == '/' || udfName[extIndex + index + 2] == 0))) index++; } ext[localExtIndex++] = curr; } maxFilenameLen = newLen - CRC_LEN - localExtIndex; if (newIndex > maxFilenameLen) newIndex = maxFilenameLen; else newIndex = newExtIndex; } else if (newIndex > newLen - CRC_LEN) newIndex = newLen - CRC_LEN; newName[newIndex++] = CRC_MARK; valueCRC = crc_itu_t(0, fidName, fidNameLen); newName[newIndex++] = hex_asc_upper_hi(valueCRC >> 8); newName[newIndex++] = hex_asc_upper_lo(valueCRC >> 8); newName[newIndex++] = hex_asc_upper_hi(valueCRC); newName[newIndex++] = hex_asc_upper_lo(valueCRC); if (hasExt) { newName[newIndex++] = EXT_MARK; for (index = 0; index < localExtIndex; index++) newName[newIndex++] = ext[index]; } } return newIndex; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
You can’t perform that action at this time.