mangle.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:16k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  *  linux/fs/umsdos/mangle.c
  3.  *
  4.  *      Written 1993 by Jacques Gelinas 
  5.  *
  6.  * Control the mangling of file name to fit msdos name space.
  7.  * Many optimisations by GLU == dglaude@is1.vub.ac.be (Glaude David)
  8.  */
  9. #include <linux/errno.h>
  10. #include <linux/string.h>
  11. #include <linux/kernel.h>
  12. #include <linux/umsdos_fs.h>
  13. /* (This file is used outside of the kernel) */
  14. #ifndef __KERNEL__
  15. #define KERN_WARNING
  16. #endif
  17. /*
  18.  * Complete the mangling of the MSDOS fake name
  19.  * based on the position of the entry in the EMD file.
  20.  * 
  21.  * Simply complete the job of umsdos_parse; fill the extension.
  22.  * 
  23.  * Beware that info->f_pos must be set.
  24.  */
  25. void umsdos_manglename (struct umsdos_info *info)
  26. {
  27. if (info->msdos_reject) {
  28. /* #Specification: file name / non MSDOS conforming / mangling
  29.  * Each non MSDOS conforming file has a special extension
  30.  * build from the entry position in the EMD file.
  31.  * 
  32.  * This number is then transform in a base 32 number, where
  33.  * each digit is expressed like hexadecimal number, using
  34.  * digit and letter, except it uses 22 letters from 'a' to 'v'.
  35.  * The number 32 comes from 2**5. It is faster to split a binary
  36.  * number using a base which is a power of two. And I was 32
  37.  * when I started this project. Pick your answer :-) .
  38.  * 
  39.  * If the result is '0', it is replace with '_', simply
  40.  * to make it odd.
  41.  * 
  42.  * This is true for the first two character of the extension.
  43.  * The last one is taken from a list of odd character, which
  44.  * are:
  45.  * 
  46.  * { } ( ) ! ` ^ & @
  47.  * 
  48.  * With this scheme, we can produce 9216 ( 9* 32 * 32)
  49.  * different extensions which should not clash with any useful
  50.  * extension already popular or meaningful. Since most directory
  51.  * have much less than 32 * 32 files in it, the first character
  52.  * of the extension of any mangled name will be {.
  53.  * 
  54.  * Here are the reason to do this (this kind of mangling).
  55.  * 
  56.  * -The mangling is deterministic. Just by the extension, we
  57.  * are able to locate the entry in the EMD file.
  58.  * 
  59.  * -By keeping to beginning of the file name almost unchanged,
  60.  * we are helping the MSDOS user.
  61.  * 
  62.  * -The mangling produces names not too ugly, so an msdos user
  63.  * may live with it (remember it, type it, etc...).
  64.  * 
  65.  * -The mangling produces names ugly enough so no one will
  66.  * ever think of using such a name in real life. This is not
  67.  * fool proof. I don't think there is a total solution to this.
  68.  */
  69. int entry_num;
  70. char *pt = info->fake.fname + info->fake.len;
  71. /* lookup for encoding the last character of the extension 
  72.  * It contains valid character after the ugly one to make sure 
  73.  * even if someone overflows the 32 * 32 * 9 limit, it still 
  74.  * does something 
  75.  */
  76. #define SPECIAL_MANGLING '{','}','(',')','!','`','^','&','@'
  77. static char lookup3[] =
  78. {
  79. SPECIAL_MANGLING,
  80. /* This is the start of lookup12 */
  81. '_', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  82. 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
  83. 'p', 'q', 'r', 's', 't', 'u', 'v'
  84. };
  85. #define lookup12 (lookup3+9)
  86. entry_num = info->f_pos / UMSDOS_REC_SIZE;
  87. if (entry_num > (9* 32 * 32)){
  88. printk (KERN_WARNING "UMSDOS: more than 9216 files in a directory.n"
  89. "This may break the mangling strategy.n"
  90. "Not a killer problem. See doc.n");
  91. }
  92. *pt++ = '.';
  93. *pt++ = lookup3 [(entry_num >> 10) & 31];
  94. *pt++ = lookup12[(entry_num >> 5) & 31];
  95. *pt++ = lookup12[entry_num & 31];
  96. *pt = ''; /* help doing printk */
  97. info->fake.len += 4;
  98. info->msdos_reject = 0; /* Avoid mangling twice */
  99. }
  100. }
  101. /*
  102.  * Evaluate the record size needed to store of name of len character.
  103.  * The value returned is a multiple of UMSDOS_REC_SIZE.
  104.  */
  105. int umsdos_evalrecsize (int len)
  106. {
  107. struct umsdos_dirent dirent;
  108. int nbrec = 1 + ((len - 1 + (dirent.name - (char *) &dirent))
  109.  / UMSDOS_REC_SIZE);
  110. return nbrec * UMSDOS_REC_SIZE;
  111. /*
  112.  * GLU        This should be inlined or something to speed it up to the max.
  113.  * GLU        nbrec is absolutely not needed to return the value.
  114.  */
  115. }
  116. #ifdef TEST
  117. int umsdos_evalrecsize_old (int len)
  118. {
  119. struct umsdos_dirent dirent;
  120. int size = len + (dirent.name - (char *) &dirent);
  121. int nbrec = size / UMSDOS_REC_SIZE;
  122. int extra = size % UMSDOS_REC_SIZE;
  123. if (extra > 0)
  124. nbrec++;
  125. return nbrec * UMSDOS_REC_SIZE;
  126. }
  127. #endif
  128. /*
  129.  * Fill the struct info with the full and msdos name of a file
  130.  * Return 0 if all is OK, a negative error code otherwise.
  131.  */
  132. int umsdos_parse (
  133.  const char *fname,
  134.  int len,
  135.  struct umsdos_info *info)
  136. {
  137. int ret = -ENAMETOOLONG;
  138. /* #Specification: file name / too long
  139.  * If a file name exceed UMSDOS maxima, the file name is silently
  140.  * truncated. This makes it conformant with the other file system
  141.  * of Linux (minix and ext2 at least).
  142.  */
  143. if (len > UMSDOS_MAXNAME)
  144. len = UMSDOS_MAXNAME;
  145. {
  146. const char *firstpt = NULL; /* First place we saw a "." in fname */
  147. /* #Specification: file name / non MSDOS conforming / base length 0
  148.  * file names beginning with a period '.' are invalid for MS-DOS.
  149.  * It needs absolutely a base name. So the file name is mangled
  150.  */
  151. int ivldchar = fname[0] == '.'; /* At least one invalid character */
  152. int msdos_len = len;
  153. int base_len;
  154. /*
  155.  * cardinal_per_size tells if there exists at least one
  156.  * DOS pseudo device on length n.  See the test below.
  157.  */
  158. static const char cardinal_per_size[9] =
  159. {
  160. 0, 0, 0, 1, 1, 0, 1, 0, 1
  161. };
  162. /*
  163.  * lkp translate all character to acceptable character (for DOS).
  164.  * When lkp[n] == n, it means also it is an acceptable one.
  165.  * So it serves both as a flag and as a translator.
  166.  */
  167. static char lkp[256];
  168. static char is_init = 0;
  169. if (!is_init) {
  170. /*
  171.  * Initialisation of the array is easier and less error
  172.                          * prone like this.
  173.  */
  174. int i;
  175. static const char *spc = ""*+,/:;<=>?[\]|~";
  176. is_init = 1;
  177. for (i = 0; i <= 32; i++)
  178. lkp[i] = '#';
  179. for (i = 33; i < 'A'; i++)
  180. lkp[i] = (char) i;
  181. for (i = 'A'; i <= 'Z'; i++)
  182. lkp[i] = (char) (i + ('a' - 'A'));
  183. for (i = 'Z' + 1; i < 127; i++)
  184. lkp[i] = (char) i;
  185. for (i = 128; i < 256; i++)
  186. lkp[i] = '#';
  187. lkp['.'] = '_';
  188. while (*spc != '')
  189. lkp[(unsigned char) (*spc++)] = '#';
  190. }
  191. /*  GLU
  192.  * File names longer than 8+'.'+3 are invalid for MS-DOS,
  193.  * so the file name is to be mangled--no further test is needed.
  194.  * This speeds up handling of long names.
  195.  * The position of the last point is no more necessary anyway.
  196.  */
  197. if (len <= (8 + 1 + 3)) {
  198. const char *pt = fname;
  199. const char *endpt = fname + len;
  200. while (pt < endpt) {
  201. if (*pt == '.') {
  202. if (firstpt != NULL) {
  203. /* 2 . in a file name. Reject */
  204. ivldchar = 1;
  205. break;
  206. } else {
  207. int extlen = (int) (endpt - pt);
  208. firstpt = pt;
  209. if (firstpt - fname > 8) {
  210. /* base name longer than 8: reject */
  211. ivldchar = 1;
  212. break;
  213. } else if (extlen > 4) {
  214. /* Extension longer than 4 (including .): reject */
  215. ivldchar = 1;
  216. break;
  217. } else if (extlen == 1) {
  218. /* #Specification: file name / non MSDOS conforming / last char == .
  219.  * If the last character of a file name is
  220.  * a period, mangling is applied. MS-DOS does
  221.  * not support those file names.
  222.  */
  223. ivldchar = 1;
  224. break;
  225. } else if (extlen == 4) {
  226. /* #Specification: file name / non MSDOS conforming / mangling clash
  227.  * To avoid clash with    the umsdos mangling, any file
  228.  * with a special character as the first character
  229.  * of the extension will be mangled. This solves the
  230.  * following problem:
  231.  * 
  232.  * #
  233.  * touch FILE
  234.  * # FILE is invalid for DOS, so mangling is applied
  235.  * # file.{_1 is created in the DOS directory
  236.  * touch file.{_1
  237.  * # To UMSDOS file point to a single DOS entry.
  238.  * # So file.{_1 has to be mangled.
  239.  * #
  240.  */
  241. static char special[] =
  242. {
  243. SPECIAL_MANGLING, ''
  244. };
  245. if (strchr (special, firstpt[1]) != NULL) {
  246. ivldchar = 1;
  247. break;
  248. }
  249. }
  250. }
  251. } else if (lkp[(unsigned char) (*pt)] != *pt) {
  252. ivldchar = 1;
  253. break;
  254. }
  255. pt++;
  256. }
  257. } else {
  258. ivldchar = 1;
  259. }
  260. if (ivldchar
  261.     || (firstpt == NULL && len > 8)
  262.     || (len == UMSDOS_EMD_NAMELEN
  263. && memcmp (fname, UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN) == 0)) {
  264. /* #Specification: file name / --linux-.---
  265.  * The name of the EMD file --linux-.--- is map to a mangled
  266.  * name. So UMSDOS does not restrict its use.
  267.  */
  268. /* #Specification: file name / non MSDOS conforming / mangling
  269.  * Non MSDOS conforming file names must use some alias to fit
  270.  * in the MSDOS name space.
  271.  * 
  272.  * The strategy is simple. The name is simply truncated to
  273.  * 8 char. points are replace with underscore and a
  274.  * number is given as an extension. This number correspond
  275.  * to the entry number in the EMD file. The EMD file
  276.  * only need to carry the real name.
  277.  * 
  278.  * Upper case is also converted to lower case.
  279.  * Control character are converted to #.
  280.  * Spaces are converted to #.
  281.  * The following characters are also converted to #.
  282.  * #
  283.  * " * + , / : ; < = > ? [  ] | ~
  284.  * #
  285.  * 
  286.  * Sometimes the problem is not in MS-DOS itself but in
  287.  * command.com.
  288.  */
  289. int i;
  290. char *pt = info->fake.fname;
  291. base_len = msdos_len = (msdos_len > 8) ? 8 : msdos_len;
  292. /*
  293.  * There is no '.' any more so we know for a fact that
  294.  * the base length is the length.
  295.  */
  296. memcpy (info->fake.fname, fname, msdos_len);
  297. for (i = 0; i < msdos_len; i++, pt++)
  298. *pt = lkp[(unsigned char) (*pt)];
  299. *pt = ''; /* GLU  We force null termination. */
  300. info->msdos_reject = 1;
  301. /*
  302.  * The numeric extension is added only when we know
  303.  * the position in the EMD file, in umsdos_newentry(),
  304.  * umsdos_delentry(), and umsdos_findentry().
  305.  * See umsdos_manglename().
  306.  */
  307. } else {
  308. /* Conforming MSDOS file name */
  309. strncpy (info->fake.fname, fname, len);
  310. info->msdos_reject = 0;
  311. base_len = firstpt != NULL ? (int) (firstpt - fname) : len;
  312. }
  313. if (cardinal_per_size[base_len]) {
  314. /* #Specification: file name / MSDOS devices / mangling
  315.  * To avoid unreachable file from MS-DOS, any MS-DOS conforming
  316.  * file with a basename equal to one of the MS-DOS pseudo
  317.  * devices will be mangled.
  318.  * 
  319.  * If a file such as "prn" was created, it would be unreachable
  320.  * under MS-DOS because "prn" is assumed to be the printer, even
  321.  * if the file does have an extension.
  322.  * 
  323.  * Since the extension is unimportant to MS-DOS, we must patch
  324.  * the basename also. We simply insert a minus '-'. To avoid
  325.  * conflict with valid file with a minus in front (such as
  326.  * "-prn"), we add an mangled extension like any other
  327.  * mangled file name.
  328.  * 
  329.  * Here is the list of DOS pseudo devices:
  330.  * 
  331.  * #
  332.  * "prn","con","aux","nul",
  333.  * "lpt1","lpt2","lpt3","lpt4",
  334.  * "com1","com2","com3","com4",
  335.  * "clock$"
  336.  * #
  337.  * 
  338.  * and some standard ones for common DOS programs
  339.  * 
  340.  * "emmxxxx0","xmsxxxx0","setverxx"
  341.  * 
  342.  * (Thanks to Chris Hall <cah17@phoenix.cambridge.ac.uk>
  343.  * for pointing these out to me).
  344.  * 
  345.  * Is there one missing?
  346.  */
  347. /* This table must be ordered by length */
  348. static const char *tbdev[] =
  349. {
  350. "prn", "con", "aux", "nul",
  351. "lpt1", "lpt2", "lpt3", "lpt4",
  352. "com1", "com2", "com3", "com4",
  353. "clock$",
  354. "emmxxxx0", "xmsxxxx0", "setverxx"
  355. };
  356. /* Tell where to find in tbdev[], the first name of */
  357. /* a certain length */
  358. static const char start_ind_dev[9] =
  359. {
  360. 0, 0, 0, 4, 12, 12, 13, 13, 16
  361. };
  362. char basen[9];
  363. int i;
  364. for (i = start_ind_dev[base_len - 1]; i < start_ind_dev[base_len]; i++) {
  365. if (memcmp (info->fake.fname, tbdev[i], base_len) == 0) {
  366. memcpy (basen, info->fake.fname, base_len);
  367. basen[base_len] = ''; /* GLU  We force null termination. */
  368. /*
  369.  * GLU        We do that only if necessary; we try to do the
  370.  * GLU        simple thing in the usual circumstance. 
  371.  */
  372. info->fake.fname[0] = '-';
  373. strcpy (info->fake.fname + 1, basen); /* GLU  We already guaranteed a null would be at the end. */
  374. msdos_len = (base_len == 8) ? 8 : base_len + 1;
  375. info->msdos_reject = 1;
  376. break;
  377. }
  378. }
  379. }
  380. info->fake.fname[msdos_len] = ''; /* Help doing printk */
  381. /* GLU      This zero should (always?) be there already. */
  382. info->fake.len = msdos_len;
  383. /* Why not use info->fake.len everywhere? Is it longer?
  384.                  */
  385. memcpy (info->entry.name, fname, len);
  386. info->entry.name[len] = ''; /* for printk */
  387. info->entry.name_len = len;
  388. ret = 0;
  389. }
  390. /*
  391.  * Evaluate how many records are needed to store this entry.
  392.  */
  393. info->recsize = umsdos_evalrecsize (len);
  394. return ret;
  395. }
  396. #ifdef TEST
  397. struct MANG_TEST {
  398. char *fname; /* Name to validate */
  399. int msdos_reject; /* Expected msdos_reject flag */
  400. char *msname; /* Expected msdos name */
  401. };
  402. struct MANG_TEST tb[] =
  403. {
  404. "hello", 0, "hello",
  405. "hello.1", 0, "hello.1",
  406. "hello.1_", 0, "hello.1_",
  407. "prm", 0, "prm",
  408. #ifdef PROPOSITION
  409. "HELLO", 1, "hello",
  410. "Hello.1", 1, "hello.1",
  411. "Hello.c", 1, "hello.c",
  412. #else
  413. /*
  414.  * I find the three examples below very unfortunate.  I propose to
  415.  * convert them to lower case in a quick preliminary pass, then test
  416.  * whether there are other troublesome characters.  I have not made
  417.  * this change, because it is not easy, but I wanted to mention the 
  418.  * principle.  Obviously something like that would increase the chance
  419.  * of collisions, for example between "HELLO" and "Hello", but these
  420.  * can be treated elsewhere along with the other collisions.
  421.  */
  422. "HELLO", 1, "hello",
  423. "Hello.1", 1, "hello_1",
  424. "Hello.c", 1, "hello_c",
  425. #endif
  426. "hello.{_1", 1, "hello_{_",
  427. "hellot", 1, "hello#",
  428. "hello.1.1", 1, "hello_1_",
  429. "hel,lo", 1, "hel#lo",
  430. "Salut.Tu.vas.bien?", 1, "salut_tu",
  431. ".profile", 1, "_profile",
  432. ".xv", 1, "_xv",
  433. "toto.", 1, "toto_",
  434. "clock$.x", 1, "-clock$",
  435. "emmxxxx0", 1, "-emmxxxx",
  436. "emmxxxx0.abcd", 1, "-emmxxxx",
  437. "aux", 1, "-aux",
  438. "prn", 1, "-prn",
  439. "prn.abc", 1, "-prn",
  440. "PRN", 1, "-prn",
  441.   /* 
  442.    * GLU        WARNING:  the results of these are different with my version
  443.    * GLU        of mangling compared to the original one.
  444.    * GLU        CAUSE:  the manner of calculating the baselen variable.
  445.    * GLU                For you they are always 3.
  446.    * GLU                For me they are respectively 7, 8, and 8.
  447.    */
  448. "PRN.abc", 1, "prn_abc",
  449. "Prn.abcd", 1, "prn_abcd",
  450. "prn.abcd", 1, "prn_abcd",
  451. "Prn.abcdefghij", 1, "prn_abcd"
  452. };
  453. int main (int argc, char *argv[])
  454. {
  455. int i, rold, rnew;
  456. printf ("Testing the umsdos_parse.n");
  457. for (i = 0; i < sizeof (tb) / sizeof (tb[0]); i++) {
  458. struct MANG_TEST *pttb = tb + i;
  459. struct umsdos_info info;
  460. int ok = umsdos_parse (pttb->fname, strlen (pttb->fname), &info);
  461. if (strcmp (info.fake.fname, pttb->msname) != 0) {
  462. printf ("**** %s -> ", pttb->fname);
  463. printf ("%s <> %sn", info.fake.fname, pttb->msname);
  464. } else if (info.msdos_reject != pttb->msdos_reject) {
  465. printf ("**** %s -> %s ", pttb->fname, pttb->msname);
  466. printf ("%d <> %dn", info.msdos_reject, pttb->msdos_reject);
  467. } else {
  468. printf ("     %s -> %s %dn", pttb->fname, pttb->msname
  469. ,pttb->msdos_reject);
  470. }
  471. }
  472. printf ("Testing the new umsdos_evalrecsize.");
  473. for (i = 0; i < UMSDOS_MAXNAME; i++) {
  474. rnew = umsdos_evalrecsize (i);
  475. rold = umsdos_evalrecsize_old (i);
  476. if (!(i % UMSDOS_REC_SIZE)) {
  477. printf ("n%d:t", i);
  478. }
  479. if (rnew != rold) {
  480. printf ("**** %d newres: %d != %d n", i, rnew, rold);
  481. } else {
  482. printf (".");
  483. }
  484. }
  485. printf ("nEnd of Testing.n");
  486. return 0;
  487. }
  488. #endif