xref: /reactos/sdk/lib/fslib/vfatlib/check/lfn.c (revision c9c3622e)
1c2c66affSColin Finck /* lfn.c - Functions for handling VFAT long filenames
2c2c66affSColin Finck 
3c2c66affSColin Finck    Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
4c2c66affSColin Finck    Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch>
5c2c66affSColin Finck    Copyright (C) 2015 Andreas Bombe <aeb@debian.org>
6c2c66affSColin Finck 
7c2c66affSColin Finck    This program is free software: you can redistribute it and/or modify
8c2c66affSColin Finck    it under the terms of the GNU General Public License as published by
9c2c66affSColin Finck    the Free Software Foundation, either version 3 of the License, or
10c2c66affSColin Finck    (at your option) any later version.
11c2c66affSColin Finck 
12c2c66affSColin Finck    This program is distributed in the hope that it will be useful,
13c2c66affSColin Finck    but WITHOUT ANY WARRANTY; without even the implied warranty of
14c2c66affSColin Finck    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15c2c66affSColin Finck    GNU General Public License for more details.
16c2c66affSColin Finck 
17c2c66affSColin Finck    You should have received a copy of the GNU General Public License
18c2c66affSColin Finck    along with this program. If not, see <http://www.gnu.org/licenses/>.
19c2c66affSColin Finck 
20c2c66affSColin Finck    The complete text of the GNU General Public License
21c2c66affSColin Finck    can be found in /usr/share/common-licenses/GPL-3 file.
22c2c66affSColin Finck */
23c2c66affSColin Finck 
24c2c66affSColin Finck #include "vfatlib.h"
25c2c66affSColin Finck 
26c2c66affSColin Finck #define NDEBUG
27c2c66affSColin Finck #include <debug.h>
28c2c66affSColin Finck 
29c2c66affSColin Finck typedef struct {
30c2c66affSColin Finck     uint8_t id;			/* sequence number for slot */
31c2c66affSColin Finck     uint8_t name0_4[10];	/* first 5 characters in name */
32c2c66affSColin Finck     uint8_t attr;		/* attribute byte */
33c2c66affSColin Finck     uint8_t reserved;		/* always 0 */
34c2c66affSColin Finck     uint8_t alias_checksum;	/* checksum for 8.3 alias */
35c2c66affSColin Finck     uint8_t name5_10[12];	/* 6 more characters in name */
36c2c66affSColin Finck     uint16_t start;		/* starting cluster number, 0 in long slots */
37c2c66affSColin Finck     uint8_t name11_12[4];	/* last 2 characters in name */
38c2c66affSColin Finck } LFN_ENT;
39c2c66affSColin Finck 
40c2c66affSColin Finck #define LFN_ID_START	0x40
41c2c66affSColin Finck #define LFN_ID_SLOTMASK	0x1f
42c2c66affSColin Finck 
43c2c66affSColin Finck #define CHARS_PER_LFN	13
44c2c66affSColin Finck 
45c2c66affSColin Finck /* These modul-global vars represent the state of the LFN parser */
46c2c66affSColin Finck unsigned char *lfn_unicode = NULL;
47c2c66affSColin Finck unsigned char lfn_checksum;
48c2c66affSColin Finck int lfn_slot = -1;
49c2c66affSColin Finck off_t *lfn_offsets = NULL;
50c2c66affSColin Finck int lfn_parts = 0;
51c2c66affSColin Finck 
52c2c66affSColin Finck static unsigned char fat_uni2esc[64] = {
53c2c66affSColin Finck     '0', '1', '2', '3', '4', '5', '6', '7',
54c2c66affSColin Finck     '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
55c2c66affSColin Finck     'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
56c2c66affSColin Finck     'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
57c2c66affSColin Finck     'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
58c2c66affSColin Finck     'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
59c2c66affSColin Finck     'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
60c2c66affSColin Finck     'u', 'v', 'w', 'x', 'y', 'z', '+', '-'
61c2c66affSColin Finck };
62c2c66affSColin Finck 
63c2c66affSColin Finck /* This defines which unicode chars are directly convertable to ISO-8859-1 */
64c2c66affSColin Finck #define UNICODE_CONVERTABLE(cl,ch)	(ch == 0 && (cl < 0x80 || cl >= 0xa0))
65c2c66affSColin Finck 
66c2c66affSColin Finck /* for maxlen param */
67c2c66affSColin Finck #define UNTIL_0		INT_MAX
68c2c66affSColin Finck 
6986198abdSPierre Schweitzer #ifdef __REACTOS__
7086198abdSPierre Schweitzer static void copy_lfn_part(unsigned char *dst, LFN_ENT * lfn);
7186198abdSPierre Schweitzer static char *cnv_unicode(const unsigned char *uni, int maxlen, int use_q);
7286198abdSPierre Schweitzer #endif
7386198abdSPierre Schweitzer 
74c2c66affSColin Finck /* Convert name part in 'lfn' from unicode to ASCII */
7586198abdSPierre Schweitzer #ifndef __REACTOS__
76469289edSPierre Schweitzer #define CNV_THIS_PART(lfn)				\
77469289edSPierre Schweitzer     ({							\
78469289edSPierre Schweitzer 	unsigned char __part_uni[CHARS_PER_LFN*2];		\
79469289edSPierre Schweitzer 	copy_lfn_part( __part_uni, lfn );		\
80469289edSPierre Schweitzer 	cnv_unicode( __part_uni, CHARS_PER_LFN, 0 );	\
81469289edSPierre Schweitzer     })
8286198abdSPierre Schweitzer #else
CNV_THIS_PART(LFN_ENT * lfn)8386198abdSPierre Schweitzer static __inline char* CNV_THIS_PART(LFN_ENT * lfn)
8486198abdSPierre Schweitzer {
8586198abdSPierre Schweitzer     unsigned char __part_uni[CHARS_PER_LFN*2];
8686198abdSPierre Schweitzer     copy_lfn_part(__part_uni, lfn);
8786198abdSPierre Schweitzer     return cnv_unicode(__part_uni, CHARS_PER_LFN, 0);
8886198abdSPierre Schweitzer }
8986198abdSPierre Schweitzer #endif
90c2c66affSColin Finck 
91c2c66affSColin Finck /* Convert name parts collected so far (from previous slots) from unicode to
92c2c66affSColin Finck  * ASCII */
93c2c66affSColin Finck #define CNV_PARTS_SO_FAR()					\
94c2c66affSColin Finck 	(cnv_unicode( lfn_unicode+(lfn_slot*CHARS_PER_LFN*2),	\
95c2c66affSColin Finck 		      lfn_parts*CHARS_PER_LFN, 0 ))
96c2c66affSColin Finck 
97c2c66affSColin Finck #define BYTES_TO_WCHAR(cl,ch) ((wchar_t)((unsigned)(cl) + ((unsigned)(ch) << 8)))
mbslen(wchar_t x)98c2c66affSColin Finck static size_t mbslen(wchar_t x)
99c2c66affSColin Finck {
100c2c66affSColin Finck     wchar_t wstr[] = { x, 0 };
101c2c66affSColin Finck     return wcstombs(NULL, wstr, 0);
102c2c66affSColin Finck }
103c2c66affSColin Finck 
wctombs(char * dest,wchar_t x)104c2c66affSColin Finck static size_t wctombs(char *dest, wchar_t x)
105c2c66affSColin Finck {
106c2c66affSColin Finck     wchar_t wstr[] = { x, 0 };
107c2c66affSColin Finck     size_t size = wcstombs(NULL, wstr, 0);
108c2c66affSColin Finck     if (size != (size_t) - 1)
109c2c66affSColin Finck 	size = wcstombs(dest, wstr, size + 1);
110c2c66affSColin Finck     return size;
111c2c66affSColin Finck }
112c2c66affSColin Finck 
113c2c66affSColin Finck /* This function converts an unicode string to a normal ASCII string, assuming
114c2c66affSColin Finck  * ISO-8859-1 charset. Characters not in 8859-1 are converted to the same
115c2c66affSColin Finck  * escape notation as used by the kernel, i.e. the uuencode-like ":xxx" */
cnv_unicode(const unsigned char * uni,int maxlen,int use_q)116c2c66affSColin Finck static char *cnv_unicode(const unsigned char *uni, int maxlen, int use_q)
117c2c66affSColin Finck {
118c2c66affSColin Finck     const unsigned char *up;
119c2c66affSColin Finck     unsigned char *out, *cp;
120c2c66affSColin Finck     int len, val;
121c2c66affSColin Finck     size_t x;
122c2c66affSColin Finck 
123c2c66affSColin Finck     for (len = 0, up = uni; (up - uni) / 2 < maxlen && (up[0] || up[1]);
124c2c66affSColin Finck 	 up += 2) {
125c2c66affSColin Finck 	if ((x = mbslen(BYTES_TO_WCHAR(up[0], up[1]))) != (size_t) - 1)
126c2c66affSColin Finck 	    len += x;
127c2c66affSColin Finck 	else if (UNICODE_CONVERTABLE(up[0], up[1]))
128c2c66affSColin Finck 	    ++len;
129c2c66affSColin Finck 	else
130c2c66affSColin Finck 	    len += 4;
131c2c66affSColin Finck     }
132c2c66affSColin Finck     cp = out = use_q ? qalloc(&mem_queue, len + 1) : alloc(len + 1);
133c2c66affSColin Finck 
134c2c66affSColin Finck     for (up = uni; (up - uni) / 2 < maxlen && (up[0] || up[1]); up += 2) {
135c2c66affSColin Finck 	if ((x =
136c2c66affSColin Finck 	     wctombs((char *)cp, BYTES_TO_WCHAR(up[0], up[1]))) != (size_t) - 1)
137c2c66affSColin Finck 	    cp += x;
138c2c66affSColin Finck 	else if (UNICODE_CONVERTABLE(up[0], up[1]))
139c2c66affSColin Finck 	    *cp++ = up[0];
140c2c66affSColin Finck 	else {
141c2c66affSColin Finck 	    /* here the same escape notation is used as in the Linux kernel */
142c2c66affSColin Finck 	    *cp++ = ':';
143c2c66affSColin Finck 	    val = (up[1] << 8) + up[0];
144c2c66affSColin Finck 	    cp[2] = fat_uni2esc[val & 0x3f];
145c2c66affSColin Finck 	    val >>= 6;
146c2c66affSColin Finck 	    cp[1] = fat_uni2esc[val & 0x3f];
147c2c66affSColin Finck 	    val >>= 6;
148c2c66affSColin Finck 	    cp[0] = fat_uni2esc[val & 0x3f];
149c2c66affSColin Finck 	    cp += 3;
150c2c66affSColin Finck 	}
151c2c66affSColin Finck     }
152c2c66affSColin Finck     *cp = 0;
153c2c66affSColin Finck 
154c2c66affSColin Finck     return (char *)out;
155c2c66affSColin Finck }
156c2c66affSColin Finck 
copy_lfn_part(unsigned char * dst,LFN_ENT * lfn)157c2c66affSColin Finck static void copy_lfn_part(unsigned char *dst, LFN_ENT * lfn)
158c2c66affSColin Finck {
159c2c66affSColin Finck     memcpy(dst, lfn->name0_4, 10);
160c2c66affSColin Finck     memcpy(dst + 10, lfn->name5_10, 12);
161c2c66affSColin Finck     memcpy(dst + 22, lfn->name11_12, 4);
162c2c66affSColin Finck }
163c2c66affSColin Finck 
clear_lfn_slots(int start,int end)164c2c66affSColin Finck static void clear_lfn_slots(int start, int end)
165c2c66affSColin Finck {
166c2c66affSColin Finck     int i;
167c2c66affSColin Finck     LFN_ENT empty;
168c2c66affSColin Finck 
169c2c66affSColin Finck     /* New dir entry is zeroed except first byte, which is set to 0xe5.
170c2c66affSColin Finck      * This is to avoid that some FAT-reading OSes (not Linux! ;) stop reading
171c2c66affSColin Finck      * a directory at the first zero entry...
172c2c66affSColin Finck      */
173c2c66affSColin Finck     memset(&empty, 0, sizeof(empty));
174c2c66affSColin Finck     empty.id = DELETED_FLAG;
175c2c66affSColin Finck 
176c2c66affSColin Finck     for (i = start; i <= end; ++i) {
177c2c66affSColin Finck 	fs_write(lfn_offsets[i], sizeof(LFN_ENT), &empty);
178c2c66affSColin Finck     }
179c2c66affSColin Finck }
180c2c66affSColin Finck 
lfn_fix_checksum(off_t from,off_t to,const char * short_name)181c2c66affSColin Finck void lfn_fix_checksum(off_t from, off_t to, const char *short_name)
182c2c66affSColin Finck {
183c2c66affSColin Finck     int i;
184c2c66affSColin Finck     uint8_t sum;
185c2c66affSColin Finck     for (sum = 0, i = 0; i < 11; i++)
186c2c66affSColin Finck 	sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + short_name[i];
187c2c66affSColin Finck 
188c2c66affSColin Finck     for (; from < to; from += sizeof(LFN_ENT)) {
189c2c66affSColin Finck 	fs_write(from + offsetof(LFN_ENT, alias_checksum), sizeof(sum), &sum);
190c2c66affSColin Finck     }
191c2c66affSColin Finck }
192c2c66affSColin Finck 
lfn_reset(void)193c2c66affSColin Finck void lfn_reset(void)
194c2c66affSColin Finck {
195c2c66affSColin Finck     if (lfn_unicode)
196c2c66affSColin Finck 	free(lfn_unicode);
197c2c66affSColin Finck     lfn_unicode = NULL;
198c2c66affSColin Finck     if (lfn_offsets)
199c2c66affSColin Finck 	free(lfn_offsets);
200c2c66affSColin Finck     lfn_offsets = NULL;
201c2c66affSColin Finck     lfn_slot = -1;
202c2c66affSColin Finck }
203c2c66affSColin Finck 
204c2c66affSColin Finck /* This function is only called with de->attr == VFAT_LN_ATTR. It stores part
205c2c66affSColin Finck  * of the long name. */
lfn_add_slot(DIR_ENT * de,off_t dir_offset)206c2c66affSColin Finck void lfn_add_slot(DIR_ENT * de, off_t dir_offset)
207c2c66affSColin Finck {
208c2c66affSColin Finck     LFN_ENT *lfn = (LFN_ENT *) de;
209c2c66affSColin Finck     int slot = lfn->id & LFN_ID_SLOTMASK;
210c2c66affSColin Finck     unsigned offset;
211c2c66affSColin Finck 
212c2c66affSColin Finck     if (lfn_slot == 0)
213c2c66affSColin Finck 	lfn_check_orphaned();
214c2c66affSColin Finck 
215c2c66affSColin Finck     if (de->attr != VFAT_LN_ATTR)
216c2c66affSColin Finck 	die("lfn_add_slot called with non-LFN directory entry");
217c2c66affSColin Finck 
218c2c66affSColin Finck     if (lfn->id & LFN_ID_START && slot != 0) {
219c2c66affSColin Finck 	if (lfn_slot != -1) {
220c2c66affSColin Finck 	    int can_clear = 0;
221c2c66affSColin Finck 	    /* There is already a LFN "in progess", so it is an error that a
222c2c66affSColin Finck 	     * new start entry is here. */
223c2c66affSColin Finck 	    /* Causes: 1) if slot# == expected: start bit set mysteriously, 2)
224c2c66affSColin Finck 	     *         old LFN overwritten by new one */
225c2c66affSColin Finck 	    /* Fixes: 1) delete previous LFN 2) if slot# == expected and
226c2c66affSColin Finck 	     *        checksum ok: clear start bit */
227c2c66affSColin Finck 	    /* XXX: Should delay that until next LFN known (then can better
228c2c66affSColin Finck 	     * display the name) */
229c2c66affSColin Finck 	    printf("A new long file name starts within an old one.\n");
230c2c66affSColin Finck 	    if (slot == lfn_slot && lfn->alias_checksum == lfn_checksum) {
231c2c66affSColin Finck 		char *part1 = CNV_THIS_PART(lfn);
232c2c66affSColin Finck 		char *part2 = CNV_PARTS_SO_FAR();
233c2c66affSColin Finck 		printf("  It could be that the LFN start bit is wrong here\n"
234c2c66affSColin Finck 		       "  if \"%s\" seems to match \"%s\".\n", part1, part2);
235c2c66affSColin Finck 		free(part1);
236c2c66affSColin Finck 		free(part2);
237c2c66affSColin Finck 		can_clear = 1;
238c2c66affSColin Finck 	    }
239c2c66affSColin Finck 	    if (interactive) {
240c2c66affSColin Finck 		printf("1: Delete previous LFN\n2: Leave it as it is.\n");
241c2c66affSColin Finck 		if (can_clear)
242c2c66affSColin Finck 		    printf("3: Clear start bit and concatenate LFNs\n");
243c2c66affSColin Finck 	    } else
244c2c66affSColin Finck 		printf("  Not auto-correcting this.\n");
245c2c66affSColin Finck 	    if (interactive) {
246c2c66affSColin Finck 		switch (get_key(can_clear ? "123" : "12", "?")) {
247c2c66affSColin Finck 		case '1':
248c2c66affSColin Finck 		    clear_lfn_slots(0, lfn_parts - 1);
249c2c66affSColin Finck 		    lfn_reset();
250c2c66affSColin Finck 		    break;
251c2c66affSColin Finck 		case '2':
252c2c66affSColin Finck 		    break;
253c2c66affSColin Finck 		case '3':
254c2c66affSColin Finck 		    lfn->id &= ~LFN_ID_START;
255c2c66affSColin Finck 		    fs_write(dir_offset + offsetof(LFN_ENT, id),
256c2c66affSColin Finck 			     sizeof(lfn->id), &lfn->id);
257c2c66affSColin Finck 		    break;
258c2c66affSColin Finck 		}
259c2c66affSColin Finck 	    }
260c2c66affSColin Finck 	}
261c2c66affSColin Finck 	lfn_slot = slot;
262c2c66affSColin Finck 	lfn_checksum = lfn->alias_checksum;
263c2c66affSColin Finck 	lfn_unicode = alloc((lfn_slot * CHARS_PER_LFN + 1) * 2);
264c2c66affSColin Finck 	lfn_offsets = alloc(lfn_slot * sizeof(off_t));
265c2c66affSColin Finck 	lfn_parts = 0;
266c2c66affSColin Finck     } else if (lfn_slot == -1 && slot != 0) {
267c2c66affSColin Finck 	/* No LFN in progress, but slot found; start bit missing */
268c2c66affSColin Finck 	/* Causes: 1) start bit got lost, 2) Previous slot with start bit got
269c2c66affSColin Finck 	 *         lost */
270c2c66affSColin Finck 	/* Fixes: 1) delete LFN, 2) set start bit */
271c2c66affSColin Finck 	char *part = CNV_THIS_PART(lfn);
272c2c66affSColin Finck 	printf("Long filename fragment \"%s\" found outside a LFN "
273c2c66affSColin Finck 	       "sequence.\n  (Maybe the start bit is missing on the "
274c2c66affSColin Finck 	       "last fragment)\n", part);
275c2c66affSColin Finck 	free(part);
276c2c66affSColin Finck 	if (interactive) {
277c2c66affSColin Finck 	    printf("1: Delete fragment\n2: Leave it as it is.\n"
278c2c66affSColin Finck 		   "3: Set start bit\n");
279c2c66affSColin Finck 	} else
280c2c66affSColin Finck 	    printf("  Not auto-correcting this.\n");
281c2c66affSColin Finck 	switch (interactive ? get_key("123", "?") : '2') {
282c2c66affSColin Finck 	case '1':
283c2c66affSColin Finck 	    if (!lfn_offsets)
284c2c66affSColin Finck 		lfn_offsets = alloc(sizeof(off_t));
285c2c66affSColin Finck 	    lfn_offsets[0] = dir_offset;
286c2c66affSColin Finck 	    clear_lfn_slots(0, 0);
287c2c66affSColin Finck 	    lfn_reset();
288c2c66affSColin Finck 	    return;
289c2c66affSColin Finck 	case '2':
290c2c66affSColin Finck 	    lfn_reset();
291c2c66affSColin Finck 	    return;
292c2c66affSColin Finck 	case '3':
293c2c66affSColin Finck 	    lfn->id |= LFN_ID_START;
294c2c66affSColin Finck 	    fs_write(dir_offset + offsetof(LFN_ENT, id),
295c2c66affSColin Finck 		     sizeof(lfn->id), &lfn->id);
296c2c66affSColin Finck 	    lfn_slot = slot;
297c2c66affSColin Finck 	    lfn_checksum = lfn->alias_checksum;
298c2c66affSColin Finck 	    lfn_unicode = alloc((lfn_slot * CHARS_PER_LFN + 1) * 2);
299c2c66affSColin Finck 	    lfn_offsets = alloc(lfn_slot * sizeof(off_t));
300c2c66affSColin Finck 	    lfn_parts = 0;
301c2c66affSColin Finck 	    break;
302c2c66affSColin Finck 	}
303c2c66affSColin Finck     } else if (slot != lfn_slot) {
304c2c66affSColin Finck 	/* wrong sequence number */
305c2c66affSColin Finck 	/* Causes: 1) seq-no destroyed */
306c2c66affSColin Finck 	/* Fixes: 1) delete LFN, 2) fix number (maybe only if following parts
307c2c66affSColin Finck 	 *        are ok?, maybe only if checksum is ok?) (Attention: space
308c2c66affSColin Finck 	 *        for name was allocated before!) */
309c2c66affSColin Finck 	int can_fix = 0;
310c2c66affSColin Finck 	printf("Unexpected long filename sequence number "
311c2c66affSColin Finck 	       "(%d vs. expected %d).\n", slot, lfn_slot);
312c2c66affSColin Finck 	if (lfn->alias_checksum == lfn_checksum && lfn_slot > 0) {
313c2c66affSColin Finck 	    char *part1 = CNV_THIS_PART(lfn);
314c2c66affSColin Finck 	    char *part2 = CNV_PARTS_SO_FAR();
315c2c66affSColin Finck 	    printf("  It could be that just the number is wrong\n"
316c2c66affSColin Finck 		   "  if \"%s\" seems to match \"%s\".\n", part1, part2);
317c2c66affSColin Finck 	    free(part1);
318c2c66affSColin Finck 	    free(part2);
319c2c66affSColin Finck 	    can_fix = 1;
320c2c66affSColin Finck 	}
321c2c66affSColin Finck 	if (interactive) {
322469289edSPierre Schweitzer 	    printf
323469289edSPierre Schweitzer 		("1: Delete LFN\n2: Leave it as it is (and ignore LFN so far)\n");
324c2c66affSColin Finck 	    if (can_fix)
325c2c66affSColin Finck 		printf("3: Correct sequence number\n");
326c2c66affSColin Finck 	} else
327c2c66affSColin Finck 	    printf("  Not auto-correcting this.\n");
328c2c66affSColin Finck 	switch (interactive ? get_key(can_fix ? "123" : "12", "?") : '2') {
329c2c66affSColin Finck 	case '1':
330c2c66affSColin Finck 	    if (!lfn_offsets) {
331c2c66affSColin Finck 		lfn_offsets = alloc(sizeof(off_t));
332c2c66affSColin Finck 		lfn_parts = 0;
333c2c66affSColin Finck 	    }
334c2c66affSColin Finck 	    lfn_offsets[lfn_parts++] = dir_offset;
335c2c66affSColin Finck 	    clear_lfn_slots(0, lfn_parts - 1);
336c2c66affSColin Finck 	    lfn_reset();
337c2c66affSColin Finck 	    return;
338c2c66affSColin Finck 	case '2':
339c2c66affSColin Finck 	    lfn_reset();
340c2c66affSColin Finck 	    return;
341c2c66affSColin Finck 	case '3':
342c2c66affSColin Finck 	    lfn->id = (lfn->id & ~LFN_ID_SLOTMASK) | lfn_slot;
343c2c66affSColin Finck 	    fs_write(dir_offset + offsetof(LFN_ENT, id),
344c2c66affSColin Finck 		     sizeof(lfn->id), &lfn->id);
345c2c66affSColin Finck 	    break;
346c2c66affSColin Finck 	}
347c2c66affSColin Finck     }
348c2c66affSColin Finck 
349c2c66affSColin Finck     if (lfn->alias_checksum != lfn_checksum) {
350c2c66affSColin Finck 	/* checksum mismatch */
351c2c66affSColin Finck 	/* Causes: 1) checksum field here destroyed */
352c2c66affSColin Finck 	/* Fixes: 1) delete LFN, 2) fix checksum */
353c2c66affSColin Finck 	printf("Checksum in long filename part wrong "
354c2c66affSColin Finck 	       "(%02x vs. expected %02x).\n",
355c2c66affSColin Finck 	       lfn->alias_checksum, lfn_checksum);
356c2c66affSColin Finck 	if (interactive) {
357c2c66affSColin Finck 	    printf("1: Delete LFN\n2: Leave it as it is.\n"
358c2c66affSColin Finck 		   "3: Correct checksum\n");
359c2c66affSColin Finck 	} else
360c2c66affSColin Finck 	    printf("  Not auto-correcting this.\n");
361c2c66affSColin Finck 	if (interactive) {
362c2c66affSColin Finck 	    switch (get_key("123", "?")) {
363c2c66affSColin Finck 	    case '1':
364c2c66affSColin Finck 		lfn_offsets[lfn_parts++] = dir_offset;
365c2c66affSColin Finck 		clear_lfn_slots(0, lfn_parts - 1);
366c2c66affSColin Finck 		lfn_reset();
367c2c66affSColin Finck 		return;
368c2c66affSColin Finck 	    case '2':
369c2c66affSColin Finck 		break;
370c2c66affSColin Finck 	    case '3':
371c2c66affSColin Finck 		lfn->alias_checksum = lfn_checksum;
372c2c66affSColin Finck 		fs_write(dir_offset + offsetof(LFN_ENT, alias_checksum),
373c2c66affSColin Finck 			 sizeof(lfn->alias_checksum), &lfn->alias_checksum);
374c2c66affSColin Finck 		break;
375c2c66affSColin Finck 	    }
376c2c66affSColin Finck 	}
377c2c66affSColin Finck     }
378c2c66affSColin Finck 
379c2c66affSColin Finck     if (lfn_slot != -1) {
380c2c66affSColin Finck 	lfn_slot--;
381c2c66affSColin Finck 	offset = lfn_slot * CHARS_PER_LFN * 2;
382c2c66affSColin Finck 	copy_lfn_part(lfn_unicode + offset, lfn);
383c2c66affSColin Finck 	if (lfn->id & LFN_ID_START)
384c2c66affSColin Finck 	    lfn_unicode[offset + 26] = lfn_unicode[offset + 27] = 0;
385c2c66affSColin Finck 	lfn_offsets[lfn_parts++] = dir_offset;
386c2c66affSColin Finck     }
387c2c66affSColin Finck 
388c2c66affSColin Finck     if (lfn->reserved != 0) {
389c2c66affSColin Finck 	printf("Reserved field in VFAT long filename slot is not 0 "
390c2c66affSColin Finck 	       "(but 0x%02x).\n", lfn->reserved);
391c2c66affSColin Finck 	if (interactive)
392c2c66affSColin Finck 	    printf("1: Fix.\n2: Leave it.\n");
393c2c66affSColin Finck 	else
394c2c66affSColin Finck 	    printf("Auto-setting to 0.\n");
395c2c66affSColin Finck 	if (!interactive || get_key("12", "?") == '1') {
396c2c66affSColin Finck 	    lfn->reserved = 0;
397c2c66affSColin Finck 	    fs_write(dir_offset + offsetof(LFN_ENT, reserved),
398c2c66affSColin Finck 		     sizeof(lfn->reserved), &lfn->reserved);
399c2c66affSColin Finck 	}
400c2c66affSColin Finck     }
401c2c66affSColin Finck     if (lfn->start != htole16(0)) {
402c2c66affSColin Finck 	printf("Start cluster field in VFAT long filename slot is not 0 "
403c2c66affSColin Finck 	       "(but 0x%04x).\n", lfn->start);
404c2c66affSColin Finck 	if (interactive)
405c2c66affSColin Finck 	    printf("1: Fix.\n2: Leave it.\n");
406c2c66affSColin Finck 	else
407c2c66affSColin Finck 	    printf("Auto-setting to 0.\n");
408c2c66affSColin Finck 	if (!interactive || get_key("12", "?") == '1') {
409c2c66affSColin Finck 	    lfn->start = htole16(0);
410c2c66affSColin Finck 	    fs_write(dir_offset + offsetof(LFN_ENT, start),
411c2c66affSColin Finck 		     sizeof(lfn->start), &lfn->start);
412c2c66affSColin Finck 	}
413c2c66affSColin Finck     }
414c2c66affSColin Finck }
415c2c66affSColin Finck 
416c2c66affSColin Finck /* This function is always called when de->attr != VFAT_LN_ATTR is found, to
417c2c66affSColin Finck  * retrieve the previously constructed LFN. */
lfn_get(DIR_ENT * de,off_t * lfn_offset)418c2c66affSColin Finck char *lfn_get(DIR_ENT * de, off_t * lfn_offset)
419c2c66affSColin Finck {
420c2c66affSColin Finck     char *lfn;
421c2c66affSColin Finck     uint8_t sum;
422c2c66affSColin Finck     int i;
423c2c66affSColin Finck 
424c2c66affSColin Finck     *lfn_offset = 0;
425c2c66affSColin Finck     if (de->attr == VFAT_LN_ATTR)
426c2c66affSColin Finck 	die("lfn_get called with LFN directory entry");
427c2c66affSColin Finck 
428c2c66affSColin Finck #if 0
429c2c66affSColin Finck     if (de->lcase)
430c2c66affSColin Finck 	printf("lcase=%02x\n", de->lcase);
431c2c66affSColin Finck #endif
432c2c66affSColin Finck 
433c2c66affSColin Finck     if (lfn_slot == -1)
434c2c66affSColin Finck 	/* no long name for this file */
435c2c66affSColin Finck 	return NULL;
436c2c66affSColin Finck 
437c2c66affSColin Finck     if (lfn_slot != 0) {
438c2c66affSColin Finck 	/* The long name isn't finished yet. */
439c2c66affSColin Finck 	/* Causes: 1) LFN slot overwritten by non-VFAT aware tool */
440c2c66affSColin Finck 	/* Fixes: 1) delete LFN 2) move overwriting entry to somewhere else
441c2c66affSColin Finck 	 * and let user enter missing part of LFN (hard to do :-()
442c2c66affSColin Finck 	 * 3) renumber entries and truncate name */
443c2c66affSColin Finck 	char *long_name = CNV_PARTS_SO_FAR();
444c2c66affSColin Finck 	char *short_name = file_name(de->name);
445c2c66affSColin Finck 	printf("Unfinished long file name \"%s\".\n"
446c2c66affSColin Finck 	       "  (Start may have been overwritten by %s)\n",
447c2c66affSColin Finck 	       long_name, short_name);
448c2c66affSColin Finck 	free(long_name);
449c2c66affSColin Finck 	if (interactive) {
450c2c66affSColin Finck 	    printf("1: Delete LFN\n2: Leave it as it is.\n"
451c2c66affSColin Finck 		   "3: Fix numbering (truncates long name and attaches "
452c2c66affSColin Finck 		   "it to short name %s)\n", short_name);
453c2c66affSColin Finck 	} else
454c2c66affSColin Finck 	    printf("  Not auto-correcting this.\n");
455c2c66affSColin Finck 	switch (interactive ? get_key("123", "?") : '2') {
456c2c66affSColin Finck 	case '1':
457c2c66affSColin Finck 	    clear_lfn_slots(0, lfn_parts - 1);
458c2c66affSColin Finck 	    lfn_reset();
459c2c66affSColin Finck 	    return NULL;
460c2c66affSColin Finck 	case '2':
461c2c66affSColin Finck 	    lfn_reset();
462c2c66affSColin Finck 	    return NULL;
463c2c66affSColin Finck 	case '3':
464c2c66affSColin Finck 	    for (i = 0; i < lfn_parts; ++i) {
465c2c66affSColin Finck 		uint8_t id = (lfn_parts - i) | (i == 0 ? LFN_ID_START : 0);
466c2c66affSColin Finck 		fs_write(lfn_offsets[i] + offsetof(LFN_ENT, id),
467c2c66affSColin Finck 			 sizeof(id), &id);
468c2c66affSColin Finck 	    }
469c2c66affSColin Finck 	    memmove(lfn_unicode, lfn_unicode + lfn_slot * CHARS_PER_LFN * 2,
470c2c66affSColin Finck 		    lfn_parts * CHARS_PER_LFN * 2);
471c2c66affSColin Finck 	    break;
472c2c66affSColin Finck 	}
473c2c66affSColin Finck     }
474c2c66affSColin Finck 
475c2c66affSColin Finck     for (sum = 0, i = 0; i < MSDOS_NAME; i++)
476c2c66affSColin Finck 	sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + de->name[i];
477c2c66affSColin Finck     if (sum != lfn_checksum) {
478c2c66affSColin Finck 	/* checksum doesn't match, long name doesn't apply to this alias */
479c2c66affSColin Finck 	/* Causes: 1) alias renamed */
480c2c66affSColin Finck 	/* Fixes: 1) Fix checksum in LFN entries */
481c2c66affSColin Finck 	char *long_name = CNV_PARTS_SO_FAR();
482c2c66affSColin Finck 	char *short_name = file_name(de->name);
483c2c66affSColin Finck 	printf("Wrong checksum for long file name \"%s\".\n"
484c2c66affSColin Finck 	       "  (Short name %s may have changed without updating the long name)\n",
485c2c66affSColin Finck 	       long_name, short_name);
486c2c66affSColin Finck 	free(long_name);
487c2c66affSColin Finck 	if (interactive) {
488c2c66affSColin Finck 	    printf("1: Delete LFN\n2: Leave it as it is.\n"
489c2c66affSColin Finck 		   "3: Fix checksum (attaches to short name %s)\n", short_name);
490c2c66affSColin Finck 	} else
491c2c66affSColin Finck 	    printf("  Not auto-correcting this.\n");
492c2c66affSColin Finck 	if (interactive) {
493c2c66affSColin Finck 	    switch (get_key("123", "?")) {
494c2c66affSColin Finck 	    case '1':
495c2c66affSColin Finck 		clear_lfn_slots(0, lfn_parts - 1);
496c2c66affSColin Finck 		lfn_reset();
497c2c66affSColin Finck 		return NULL;
498c2c66affSColin Finck 	    case '2':
499c2c66affSColin Finck 		lfn_reset();
500c2c66affSColin Finck 		return NULL;
501c2c66affSColin Finck 	    case '3':
502c2c66affSColin Finck 		for (i = 0; i < lfn_parts; ++i) {
503c2c66affSColin Finck 		    fs_write(lfn_offsets[i] + offsetof(LFN_ENT, alias_checksum),
504c2c66affSColin Finck 			     sizeof(sum), &sum);
505c2c66affSColin Finck 		}
506c2c66affSColin Finck 		break;
507c2c66affSColin Finck 	    }
508c2c66affSColin Finck 	}
509c2c66affSColin Finck     }
510c2c66affSColin Finck 
511c2c66affSColin Finck     *lfn_offset = lfn_offsets[0];
512c2c66affSColin Finck     lfn = cnv_unicode(lfn_unicode, UNTIL_0, 1);
513c2c66affSColin Finck     lfn_reset();
514c2c66affSColin Finck     return (lfn);
515c2c66affSColin Finck }
516c2c66affSColin Finck 
lfn_check_orphaned(void)517c2c66affSColin Finck void lfn_check_orphaned(void)
518c2c66affSColin Finck {
519c2c66affSColin Finck     char *long_name;
520c2c66affSColin Finck 
521c2c66affSColin Finck     if (lfn_slot == -1)
522c2c66affSColin Finck 	return;
523c2c66affSColin Finck 
524c2c66affSColin Finck     long_name = CNV_PARTS_SO_FAR();
525c2c66affSColin Finck     printf("Orphaned long file name part \"%s\"\n", long_name);
526c2c66affSColin Finck     free(long_name);
527c2c66affSColin Finck     if (interactive)
528c2c66affSColin Finck 	printf("1: Delete.\n2: Leave it.\n");
529*c9c3622eSPierre Schweitzer #ifdef __REACTOS__
530*c9c3622eSPierre Schweitzer     else if (rw)
531*c9c3622eSPierre Schweitzer #else
532c2c66affSColin Finck     else
533*c9c3622eSPierre Schweitzer #endif
534c2c66affSColin Finck 	printf("  Auto-deleting.\n");
535*c9c3622eSPierre Schweitzer #ifdef __REACTOS__
536*c9c3622eSPierre Schweitzer     if ((!interactive && rw) || (interactive && get_key("12", "?") == '1')) {
537*c9c3622eSPierre Schweitzer #else
538c2c66affSColin Finck     if (!interactive || get_key("12", "?") == '1') {
539*c9c3622eSPierre Schweitzer #endif
540c2c66affSColin Finck 	clear_lfn_slots(0, lfn_parts - 1);
541c2c66affSColin Finck     }
542c2c66affSColin Finck     lfn_reset();
543c2c66affSColin Finck }
544