]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/makefs/msdos/msdosfs_conv.c
bhyvectl(8): Normalize the man page date
[FreeBSD/FreeBSD.git] / usr.sbin / makefs / msdos / msdosfs_conv.c
1 /*-
2  * SPDX-License-Identifier: BSD-4-Clause
3  *
4  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
5  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
6  * All rights reserved.
7  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by TooLs GmbH.
20  * 4. The name of TooLs GmbH may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 /*-
35  * Written by Paul Popelka (paulp@uts.amdahl.com)
36  *
37  * You can do anything you want with this software, just don't say you wrote
38  * it, and don't remove this notice.
39  *
40  * This software is provided "as is".
41  *
42  * The author supplies this software to be publicly redistributed on the
43  * understanding that the author is not responsible for the correct
44  * functioning of this software in any circumstances and is not liable for
45  * any damages caused by this software.
46  *
47  * October 1992
48  */
49
50 #include <sys/cdefs.h>
51 __FBSDID("$FreeBSD$");
52
53 #include <sys/param.h>
54 #include <sys/endian.h>
55
56 #include <dirent.h>
57 #include <stdio.h>
58 #include <string.h>
59
60 #include <fs/msdosfs/bpb.h>
61 #include "msdos/direntry.h"
62 #include <fs/msdosfs/msdosfsmount.h>
63
64 #include "makefs.h"
65 #include "msdos.h"
66
67 static int char8ucs2str(const uint8_t *in, int n, uint16_t *out, int m);
68 static void ucs2pad(uint16_t *buf, int len, int size);
69 static int char8match(uint16_t *w1, uint16_t *w2, int n);
70
71 static const u_char unix2dos[256] = {
72         0,    0,    0,    0,    0,    0,    0,    0,    /* 00-07 */
73         0,    0,    0,    0,    0,    0,    0,    0,    /* 08-0f */
74         0,    0,    0,    0,    0,    0,    0,    0,    /* 10-17 */
75         0,    0,    0,    0,    0,    0,    0,    0,    /* 18-1f */
76         0,    '!',  0,    '#',  '$',  '%',  '&',  '\'', /* 20-27 */
77         '(',  ')',  0,    '+',  0,    '-',  0,    0,    /* 28-2f */
78         '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',  /* 30-37 */
79         '8',  '9',  0,    0,    0,    0,    0,    0,    /* 38-3f */
80         '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',  /* 40-47 */
81         'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',  /* 48-4f */
82         'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',  /* 50-57 */
83         'X',  'Y',  'Z',  0,    0,    0,    '^',  '_',  /* 58-5f */
84         '`',  'A',  'B',  'C',  'D',  'E',  'F',  'G',  /* 60-67 */
85         'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',  /* 68-6f */
86         'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',  /* 70-77 */
87         'X',  'Y',  'Z',  '{',  0,    '}',  '~',  0,    /* 78-7f */
88         0,    0,    0,    0,    0,    0,    0,    0,    /* 80-87 */
89         0,    0,    0,    0,    0,    0,    0,    0,    /* 88-8f */
90         0,    0,    0,    0,    0,    0,    0,    0,    /* 90-97 */
91         0,    0,    0,    0,    0,    0,    0,    0,    /* 98-9f */
92         0,    0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, /* a0-a7 */
93         0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee, /* a8-af */
94         0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, /* b0-b7 */
95         0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8, /* b8-bf */
96         0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* c0-c7 */
97         0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* c8-cf */
98         0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, /* d0-d7 */
99         0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1, /* d8-df */
100         0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* e0-e7 */
101         0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* e8-ef */
102         0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0xf6, /* f0-f7 */
103         0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0x98, /* f8-ff */
104 };
105
106 static const u_char u2l[256] = {
107         0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 00-07 */
108         0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 08-0f */
109         0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10-17 */
110         0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 18-1f */
111         ' ',  '!',  '"',  '#',  '$',  '%',  '&', '\'', /* 20-27 */
112         '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/', /* 28-2f */
113         '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7', /* 30-37 */
114         '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?', /* 38-3f */
115         '@',  'a',  'b',  'c',  'd',  'e',  'f',  'g', /* 40-47 */
116         'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o', /* 48-4f */
117         'p',  'q',  'r',  's',  't',  'u',  'v',  'w', /* 50-57 */
118         'x',  'y',  'z',  '[', '\\',  ']',  '^',  '_', /* 58-5f */
119         '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g', /* 60-67 */
120         'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o', /* 68-6f */
121         'p',  'q',  'r',  's',  't',  'u',  'v',  'w', /* 70-77 */
122         'x',  'y',  'z',  '{',  '|',  '}',  '~', 0x7f, /* 78-7f */
123         0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 80-87 */
124         0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 88-8f */
125         0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 90-97 */
126         0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 98-9f */
127         0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* a0-a7 */
128         0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* a8-af */
129         0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* b0-b7 */
130         0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* b8-bf */
131         0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* c0-c7 */
132         0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* c8-cf */
133         0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* d0-d7 */
134         0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* d8-df */
135         0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* e0-e7 */
136         0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* e8-ef */
137         0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* f0-f7 */
138         0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* f8-ff */
139 };
140
141 /*
142  * Determine the number of slots necessary for Win95 names
143  */
144 int
145 winSlotCnt(const u_char *un, size_t unlen)
146 {
147         const u_char *cp;
148
149         /*
150          * Drop trailing blanks and dots
151          */
152         for (cp = un + unlen; unlen > 0; unlen--)
153                 if (*--cp != ' ' && *cp != '.')
154                         break;
155
156         return howmany(unlen, WIN_CHARS);
157 }
158
159 /*
160  * Compare our filename to the one in the Win95 entry
161  * Returns the checksum or -1 if no match
162  */
163 int
164 winChkName(const u_char *un, size_t unlen, struct winentry *wep, int chksum)
165 {
166         uint16_t wn[WIN_MAXLEN], *p;
167         uint16_t buf[WIN_CHARS];
168         int i, len;
169
170         /*
171          * First compare checksums
172          */
173         if (wep->weCnt & WIN_LAST)
174                 chksum = wep->weChksum;
175         else if (chksum != wep->weChksum)
176                 chksum = -1;
177         if (chksum == -1)
178                 return -1;
179
180         /*
181          * Offset of this entry
182          */
183         i = ((wep->weCnt & WIN_CNT) - 1) * WIN_CHARS;
184
185         /*
186          * Translate UNIX name to ucs-2
187          */
188         len = char8ucs2str(un, unlen, wn, WIN_MAXLEN);
189         ucs2pad(wn, len, WIN_MAXLEN);
190
191         if (i >= len + 1)
192                 return -1;
193         if ((wep->weCnt & WIN_LAST) && (len - i > WIN_CHARS))
194                 return -1;
195
196         /*
197          * Fetch name segment from directory entry
198          */
199         p = &buf[0];
200         memcpy(p, wep->wePart1, sizeof(wep->wePart1));
201         p += sizeof(wep->wePart1) / sizeof(*p);
202         memcpy(p, wep->wePart2, sizeof(wep->wePart2));
203         p += sizeof(wep->wePart2) / sizeof(*p);
204         memcpy(p, wep->wePart3, sizeof(wep->wePart3));
205
206         /*
207          * And compare name segment
208          */
209         if (!(char8match(&wn[i], buf, WIN_CHARS)))
210                 return -1;
211
212         return chksum;
213 }
214
215 /*
216  * Compute the checksum of a DOS filename for Win95 use
217  */
218 uint8_t
219 winChksum(uint8_t *name)
220 {
221         int i;
222         uint8_t s;
223
224         for (s = 0, i = 11; --i >= 0; s += *name++)
225                 s = (s << 7) | (s >> 1);
226         return s;
227 }
228
229 /*
230  * Create a Win95 long name directory entry
231  * Note: assumes that the filename is valid,
232  *       i.e. doesn't consist solely of blanks and dots
233  */
234 int
235 unix2winfn(const u_char *un, size_t unlen, struct winentry *wep, int cnt,
236     int chksum)
237 {
238         uint16_t wn[WIN_MAXLEN], *p;
239         int i, len;
240         const u_char *cp;
241
242         /*
243          * Drop trailing blanks and dots
244          */
245         for (cp = un + unlen; unlen > 0; unlen--)
246                 if (*--cp != ' ' && *cp != '.')
247                         break;
248
249         /*
250          * Offset of this entry
251          */
252         i = (cnt - 1) * WIN_CHARS;
253
254         /*
255          * Translate UNIX name to ucs-2
256          */
257         len = char8ucs2str(un, unlen, wn, WIN_MAXLEN);
258         ucs2pad(wn, len, WIN_MAXLEN);
259
260         /*
261          * Initialize winentry to some useful default
262          */
263         memset(wep, 0xff, sizeof(*wep));
264         wep->weCnt = cnt;
265         wep->weAttributes = ATTR_WIN95;
266         wep->weReserved1 = 0;
267         wep->weChksum = chksum;
268         wep->weReserved2 = 0;
269
270         /*
271          * Store name segment into directory entry
272          */
273         p = &wn[i];
274         memcpy(wep->wePart1, p, sizeof(wep->wePart1));
275         p += sizeof(wep->wePart1) / sizeof(*p);
276         memcpy(wep->wePart2, p, sizeof(wep->wePart2));
277         p += sizeof(wep->wePart2) / sizeof(*p);
278         memcpy(wep->wePart3, p, sizeof(wep->wePart3));
279
280         if (len > i + WIN_CHARS)
281                 return 1;
282
283         wep->weCnt |= WIN_LAST;
284         return 0;
285 }
286
287 /*
288  * Convert a unix filename to a DOS filename according to Win95 rules.
289  * If applicable and gen is not 0, it is inserted into the converted
290  * filename as a generation number.
291  * Returns
292  *      0 if name couldn't be converted
293  *      1 if the converted name is the same as the original
294  *        (no long filename entry necessary for Win95)
295  *      2 if conversion was successful
296  *      3 if conversion was successful and generation number was inserted
297  */
298 int
299 unix2dosfn(const u_char *un, u_char dn[12], size_t unlen, u_int gen)
300 {
301         int i, j, l;
302         int conv = 1;
303         const u_char *cp, *dp, *dp1;
304         u_char gentext[6], *wcp;
305         int shortlen;
306
307         /*
308          * Fill the dos filename string with blanks. These are DOS's pad
309          * characters.
310          */
311         for (i = 0; i < 11; i++)
312                 dn[i] = ' ';
313         dn[11] = 0;
314
315         /*
316          * The filenames "." and ".." are handled specially, since they
317          * don't follow dos filename rules.
318          */
319         if (un[0] == '.' && unlen == 1) {
320                 dn[0] = '.';
321                 return gen <= 1;
322         }
323         if (un[0] == '.' && un[1] == '.' && unlen == 2) {
324                 dn[0] = '.';
325                 dn[1] = '.';
326                 return gen <= 1;
327         }
328
329         /*
330          * Filenames with only blanks and dots are not allowed!
331          */
332         for (cp = un, i = unlen; --i >= 0; cp++)
333                 if (*cp != ' ' && *cp != '.')
334                         break;
335         if (i < 0)
336                 return 0;
337
338         /*
339          * Now find the extension
340          * Note: dot as first char doesn't start extension
341          *       and trailing dots and blanks are ignored
342          */
343         dp = dp1 = 0;
344         for (cp = un + 1, i = unlen - 1; --i >= 0;) {
345                 switch (*cp++) {
346                 case '.':
347                         if (!dp1)
348                                 dp1 = cp;
349                         break;
350                 case ' ':
351                         break;
352                 default:
353                         if (dp1)
354                                 dp = dp1;
355                         dp1 = 0;
356                         break;
357                 }
358         }
359
360         /*
361          * Now convert it
362          */
363         if (dp) {
364                 if (dp1)
365                         l = dp1 - dp;
366                 else
367                         l = unlen - (dp - un);
368                 for (i = 0, j = 8; i < l && j < 11; i++, j++) {
369                         if (dp[i] != (dn[j] = unix2dos[dp[i]])
370                             && conv != 3)
371                                 conv = 2;
372                         if (!dn[j]) {
373                                 conv = 3;
374                                 dn[j--] = ' ';
375                         }
376                 }
377                 if (i < l)
378                         conv = 3;
379                 dp--;
380         } else {
381                 for (dp = cp; *--dp == ' ' || *dp == '.';);
382                 dp++;
383         }
384
385         shortlen = (dp - un) <= 8;
386
387         /*
388          * Now convert the rest of the name
389          */
390         for (i = j = 0; un < dp && j < 8; i++, j++, un++) {
391                 if ((*un == ' ') && shortlen)
392                         dn[j] = ' ';
393                 else
394                         dn[j] = unix2dos[*un];
395                 if ((*un != dn[j])
396                     && conv != 3)
397                         conv = 2;
398                 if (!dn[j]) {
399                         conv = 3;
400                         dn[j--] = ' ';
401                 }
402         }
403         if (un < dp)
404                 conv = 3;
405         /*
406          * If we didn't have any chars in filename,
407          * generate a default
408          */
409         if (!j)
410                 dn[0] = '_';
411
412         /*
413          * The first character cannot be E5,
414          * because that means a deleted entry
415          */
416         if (dn[0] == 0xe5)
417                 dn[0] = SLOT_E5;
418
419         /*
420          * If there wasn't any char dropped,
421          * there is no place for generation numbers
422          */
423         if (conv != 3) {
424                 if (gen > 1)
425                         return 0;
426                 return conv;
427         }
428
429         /*
430          * Now insert the generation number into the filename part
431          */
432         for (wcp = gentext + sizeof(gentext); wcp > gentext && gen; gen /= 10)
433                 *--wcp = gen % 10 + '0';
434         if (gen)
435                 return 0;
436         for (i = 8; dn[--i] == ' ';);
437         i++;
438         if (gentext + sizeof(gentext) - wcp + 1 > 8 - i)
439                 i = 8 - (gentext + sizeof(gentext) - wcp + 1);
440         dn[i++] = '~';
441         while (wcp < gentext + sizeof(gentext))
442                 dn[i++] = *wcp++;
443         return 3;
444 }
445
446 /*
447  * Convert 8bit character string into UCS-2 string
448  * return total number of output chacters
449  */
450 static int
451 char8ucs2str(const uint8_t *in, int n, uint16_t *out, int m)
452 {
453         uint16_t *p;
454
455         p = out;
456         while (n > 0 && in[0] != 0) {
457                 if (m < 1)
458                         break;
459                 if (p)
460                         p[0] = htole16(in[0]);
461                 p += 1;
462                 m -= 1;
463                 in += 1;
464                 n -= 1;
465         }
466
467         return p - out;
468 }
469
470 static void
471 ucs2pad(uint16_t *buf, int len, int size)
472 {
473
474         if (len < size-1)
475                 buf[len++] = 0x0000;
476         while (len < size)
477                 buf[len++] = 0xffff;
478 }
479
480 /*
481  * Compare two 8bit char conversions case-insensitive
482  *
483  * uses the DOS case folding table
484  */
485 static int
486 char8match(uint16_t *w1, uint16_t *w2, int n)
487 {
488         uint16_t u1, u2;
489
490         while (n > 0) {
491                 u1 = le16toh(*w1);
492                 u2 = le16toh(*w2);
493                 if (u1 == 0 || u2 == 0)
494                         return u1 == u2;
495                 if (u1 > 255 || u2 > 255)
496                         return 0;
497                 u1 = u2l[u1 & 0xff];
498                 u2 = u2l[u2 & 0xff];
499                 if (u1 != u2)
500                         return 0;
501                 ++w1;
502                 ++w2;
503                 --n;
504         }
505
506         return 1;
507 }