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