📄 mangle.c
字号:
/* * linux/fs/umsdos/mangle.c * * Written 1993 by Jacques Gelinas * * Control the mangling of file name to fit msdos name space. * Many optimisations by GLU == dglaude@is1.vub.ac.be (Glaude David) */#include <linux/errno.h>#include <linux/string.h>#include <linux/kernel.h>#include <linux/umsdos_fs.h>/* (This file is used outside of the kernel) */#ifndef __KERNEL__#define KERN_WARNING#endif/* * Complete the mangling of the MSDOS fake name * based on the position of the entry in the EMD file. * * Simply complete the job of umsdos_parse; fill the extension. * * Beware that info->f_pos must be set. */void umsdos_manglename (struct umsdos_info *info){ if (info->msdos_reject) { /* #Specification: file name / non MSDOS conforming / mangling * Each non MSDOS conforming file has a special extension * build from the entry position in the EMD file. * * This number is then transform in a base 32 number, where * each digit is expressed like hexadecimal number, using * digit and letter, except it uses 22 letters from 'a' to 'v'. * The number 32 comes from 2**5. It is faster to split a binary * number using a base which is a power of two. And I was 32 * when I started this project. Pick your answer :-) . * * If the result is '0', it is replace with '_', simply * to make it odd. * * This is true for the first two character of the extension. * The last one is taken from a list of odd character, which * are: * * { } ( ) ! ` ^ & @ * * With this scheme, we can produce 9216 ( 9* 32 * 32) * different extensions which should not clash with any useful * extension already popular or meaningful. Since most directory * have much less than 32 * 32 files in it, the first character * of the extension of any mangled name will be {. * * Here are the reason to do this (this kind of mangling). * * -The mangling is deterministic. Just by the extension, we * are able to locate the entry in the EMD file. * * -By keeping to beginning of the file name almost unchanged, * we are helping the MSDOS user. * * -The mangling produces names not too ugly, so an msdos user * may live with it (remember it, type it, etc...). * * -The mangling produces names ugly enough so no one will * ever think of using such a name in real life. This is not * fool proof. I don't think there is a total solution to this. */ int entry_num; char *pt = info->fake.fname + info->fake.len; /* lookup for encoding the last character of the extension * It contains valid character after the ugly one to make sure * even if someone overflows the 32 * 32 * 9 limit, it still * does something */#define SPECIAL_MANGLING '{','}','(',')','!','`','^','&','@' static char lookup3[] = { SPECIAL_MANGLING, /* This is the start of lookup12 */ '_', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v' };#define lookup12 (lookup3+9) entry_num = info->f_pos / UMSDOS_REC_SIZE; if (entry_num > (9* 32 * 32)){ printk (KERN_WARNING "UMSDOS: more than 9216 files in a directory.\n" "This may break the mangling strategy.\n" "Not a killer problem. See doc.\n"); } *pt++ = '.'; *pt++ = lookup3 [(entry_num >> 10) & 31]; *pt++ = lookup12[(entry_num >> 5) & 31]; *pt++ = lookup12[entry_num & 31]; *pt = '\0'; /* help doing printk */ info->fake.len += 4; info->msdos_reject = 0; /* Avoid mangling twice */ }}/* * Evaluate the record size needed to store of name of len character. * The value returned is a multiple of UMSDOS_REC_SIZE. */int umsdos_evalrecsize (int len){ struct umsdos_dirent dirent; int nbrec = 1 + ((len - 1 + (dirent.name - (char *) &dirent)) / UMSDOS_REC_SIZE); return nbrec * UMSDOS_REC_SIZE; /* * GLU This should be inlined or something to speed it up to the max. * GLU nbrec is absolutely not needed to return the value. */}#ifdef TESTint umsdos_evalrecsize_old (int len){ struct umsdos_dirent dirent; int size = len + (dirent.name - (char *) &dirent); int nbrec = size / UMSDOS_REC_SIZE; int extra = size % UMSDOS_REC_SIZE; if (extra > 0) nbrec++; return nbrec * UMSDOS_REC_SIZE;}#endif/* * Fill the struct info with the full and msdos name of a file * Return 0 if all is OK, a negative error code otherwise. */int umsdos_parse ( const char *fname, int len, struct umsdos_info *info){ int ret = -ENAMETOOLONG; /* #Specification: file name / too long * If a file name exceed UMSDOS maxima, the file name is silently * truncated. This makes it conformant with the other file system * of Linux (minix and ext2 at least). */ if (len > UMSDOS_MAXNAME) len = UMSDOS_MAXNAME; { const char *firstpt = NULL; /* First place we saw a "." in fname */ /* #Specification: file name / non MSDOS conforming / base length 0 * file names beginning with a period '.' are invalid for MS-DOS. * It needs absolutely a base name. So the file name is mangled */ int ivldchar = fname[0] == '.'; /* At least one invalid character */ int msdos_len = len; int base_len; /* * cardinal_per_size tells if there exists at least one * DOS pseudo device on length n. See the test below. */ static const char cardinal_per_size[9] = { 0, 0, 0, 1, 1, 0, 1, 0, 1 }; /* * lkp translate all character to acceptable character (for DOS). * When lkp[n] == n, it means also it is an acceptable one. * So it serves both as a flag and as a translator. */ static char lkp[256]; static char is_init = 0; if (!is_init) { /* * Initialisation of the array is easier and less error * prone like this. */ int i; static const char *spc = "\"*+,/:;<=>?[\\]|~"; is_init = 1; for (i = 0; i <= 32; i++) lkp[i] = '#'; for (i = 33; i < 'A'; i++) lkp[i] = (char) i; for (i = 'A'; i <= 'Z'; i++) lkp[i] = (char) (i + ('a' - 'A')); for (i = 'Z' + 1; i < 127; i++) lkp[i] = (char) i; for (i = 128; i < 256; i++) lkp[i] = '#'; lkp['.'] = '_'; while (*spc != '\0') lkp[(unsigned char) (*spc++)] = '#'; } /* GLU * File names longer than 8+'.'+3 are invalid for MS-DOS, * so the file name is to be mangled--no further test is needed. * This speeds up handling of long names. * The position of the last point is no more necessary anyway. */ if (len <= (8 + 1 + 3)) { const char *pt = fname; const char *endpt = fname + len; while (pt < endpt) { if (*pt == '.') { if (firstpt != NULL) { /* 2 . in a file name. Reject */ ivldchar = 1; break; } else { int extlen = (int) (endpt - pt); firstpt = pt; if (firstpt - fname > 8) { /* base name longer than 8: reject */ ivldchar = 1; break; } else if (extlen > 4) { /* Extension longer than 4 (including .): reject */ ivldchar = 1; break; } else if (extlen == 1) { /* #Specification: file name / non MSDOS conforming / last char == . * If the last character of a file name is * a period, mangling is applied. MS-DOS does * not support those file names. */ ivldchar = 1; break; } else if (extlen == 4) { /* #Specification: file name / non MSDOS conforming / mangling clash * To avoid clash with the umsdos mangling, any file * with a special character as the first character * of the extension will be mangled. This solves the * following problem: * * # * touch FILE * # FILE is invalid for DOS, so mangling is applied * # file.{_1 is created in the DOS directory * touch file.{_1 * # To UMSDOS file point to a single DOS entry. * # So file.{_1 has to be mangled. * # */ static char special[] = { SPECIAL_MANGLING, '\0' };
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -