1*d5c9a868SElliott Hughes /* Copyright 1995 David C. Niemi
2*d5c9a868SElliott Hughes * Copyright 1996-2003,2005,2007-2009 Alain Knaff.
3*d5c9a868SElliott Hughes * This file is part of mtools.
4*d5c9a868SElliott Hughes *
5*d5c9a868SElliott Hughes * Mtools is free software: you can redistribute it and/or modify
6*d5c9a868SElliott Hughes * it under the terms of the GNU General Public License as published by
7*d5c9a868SElliott Hughes * the Free Software Foundation, either version 3 of the License, or
8*d5c9a868SElliott Hughes * (at your option) any later version.
9*d5c9a868SElliott Hughes *
10*d5c9a868SElliott Hughes * Mtools is distributed in the hope that it will be useful,
11*d5c9a868SElliott Hughes * but WITHOUT ANY WARRANTY; without even the implied warranty of
12*d5c9a868SElliott Hughes * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13*d5c9a868SElliott Hughes * GNU General Public License for more details.
14*d5c9a868SElliott Hughes *
15*d5c9a868SElliott Hughes * You should have received a copy of the GNU General Public License
16*d5c9a868SElliott Hughes * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
17*d5c9a868SElliott Hughes *
18*d5c9a868SElliott Hughes * vfat.c
19*d5c9a868SElliott Hughes *
20*d5c9a868SElliott Hughes * Miscellaneous VFAT-related functions
21*d5c9a868SElliott Hughes */
22*d5c9a868SElliott Hughes
23*d5c9a868SElliott Hughes #include "sysincludes.h"
24*d5c9a868SElliott Hughes #include "msdos.h"
25*d5c9a868SElliott Hughes #include "mtools.h"
26*d5c9a868SElliott Hughes #include "vfat.h"
27*d5c9a868SElliott Hughes #include "file.h"
28*d5c9a868SElliott Hughes #include "dirCache.h"
29*d5c9a868SElliott Hughes #include "dirCacheP.h"
30*d5c9a868SElliott Hughes #include "file_name.h"
31*d5c9a868SElliott Hughes
32*d5c9a868SElliott Hughes /* #define DEBUG */
33*d5c9a868SElliott Hughes
34*d5c9a868SElliott Hughes const char *short_illegals=";+=[]',\"*\\<>/?:|";
35*d5c9a868SElliott Hughes const char *long_illegals = "\"*\\<>/?:|\005";
36*d5c9a868SElliott Hughes
37*d5c9a868SElliott Hughes /* Automatically derive a new name */
autorename(char * name,char tilda,char dot,const char * illegals,int limit,int bump)38*d5c9a868SElliott Hughes static void autorename(char *name,
39*d5c9a868SElliott Hughes char tilda, char dot, const char *illegals,
40*d5c9a868SElliott Hughes int limit, int bump)
41*d5c9a868SElliott Hughes {
42*d5c9a868SElliott Hughes int tildapos, dotpos;
43*d5c9a868SElliott Hughes unsigned int seqnum=0, maxseq=0;
44*d5c9a868SElliott Hughes char tmp;
45*d5c9a868SElliott Hughes char *p;
46*d5c9a868SElliott Hughes
47*d5c9a868SElliott Hughes #ifdef DEBUG
48*d5c9a868SElliott Hughes printf("In autorename for name=%s.\n", name);
49*d5c9a868SElliott Hughes #endif
50*d5c9a868SElliott Hughes tildapos = -1;
51*d5c9a868SElliott Hughes
52*d5c9a868SElliott Hughes for(p=name; *p ; p++)
53*d5c9a868SElliott Hughes if (strchr(illegals, *p)) {
54*d5c9a868SElliott Hughes *p = '_';
55*d5c9a868SElliott Hughes bump = 0;
56*d5c9a868SElliott Hughes }
57*d5c9a868SElliott Hughes
58*d5c9a868SElliott Hughes for(dotpos=0;
59*d5c9a868SElliott Hughes name[dotpos] && dotpos < limit && name[dotpos] != dot ;
60*d5c9a868SElliott Hughes dotpos++) {
61*d5c9a868SElliott Hughes if(name[dotpos] == tilda) {
62*d5c9a868SElliott Hughes tildapos = dotpos;
63*d5c9a868SElliott Hughes seqnum = 0;
64*d5c9a868SElliott Hughes maxseq = 1;
65*d5c9a868SElliott Hughes } else if (name[dotpos] >= '0' && name[dotpos] <= '9') {
66*d5c9a868SElliott Hughes seqnum = seqnum * 10 + (uint8_t)(name[dotpos] - '0');
67*d5c9a868SElliott Hughes maxseq = maxseq * 10;
68*d5c9a868SElliott Hughes } else
69*d5c9a868SElliott Hughes tildapos = -1; /* sequence number interrupted */
70*d5c9a868SElliott Hughes }
71*d5c9a868SElliott Hughes if(tildapos == -1) {
72*d5c9a868SElliott Hughes /* no sequence number yet */
73*d5c9a868SElliott Hughes if(dotpos > limit - 2) {
74*d5c9a868SElliott Hughes tildapos = limit - 2;
75*d5c9a868SElliott Hughes dotpos = limit;
76*d5c9a868SElliott Hughes } else {
77*d5c9a868SElliott Hughes tildapos = dotpos;
78*d5c9a868SElliott Hughes dotpos += 2;
79*d5c9a868SElliott Hughes }
80*d5c9a868SElliott Hughes seqnum = 1;
81*d5c9a868SElliott Hughes } else {
82*d5c9a868SElliott Hughes if(bump)
83*d5c9a868SElliott Hughes seqnum++;
84*d5c9a868SElliott Hughes if(seqnum > 999999) {
85*d5c9a868SElliott Hughes seqnum = 1;
86*d5c9a868SElliott Hughes tildapos = dotpos - 2;
87*d5c9a868SElliott Hughes /* this matches Win95's behavior, and also guarantees
88*d5c9a868SElliott Hughes * us that the sequence numbers never get shorter */
89*d5c9a868SElliott Hughes }
90*d5c9a868SElliott Hughes if (seqnum == maxseq) {
91*d5c9a868SElliott Hughes if(dotpos >= limit)
92*d5c9a868SElliott Hughes tildapos--;
93*d5c9a868SElliott Hughes else
94*d5c9a868SElliott Hughes dotpos++;
95*d5c9a868SElliott Hughes }
96*d5c9a868SElliott Hughes }
97*d5c9a868SElliott Hughes
98*d5c9a868SElliott Hughes tmp = name[dotpos];
99*d5c9a868SElliott Hughes if((bump && seqnum == 1) || seqnum > 1 || mtools_numeric_tail)
100*d5c9a868SElliott Hughes sprintf(name+tildapos,"%c%d",tilda, seqnum);
101*d5c9a868SElliott Hughes if(dot)
102*d5c9a868SElliott Hughes name[dotpos]=tmp;
103*d5c9a868SElliott Hughes /* replace the character if it wasn't a space */
104*d5c9a868SElliott Hughes #ifdef DEBUG
105*d5c9a868SElliott Hughes printf("Out autorename for name=%s.\n", name);
106*d5c9a868SElliott Hughes #endif
107*d5c9a868SElliott Hughes }
108*d5c9a868SElliott Hughes
109*d5c9a868SElliott Hughes
autorename_short(dos_name_t * name,int bump)110*d5c9a868SElliott Hughes void autorename_short(dos_name_t *name, int bump)
111*d5c9a868SElliott Hughes {
112*d5c9a868SElliott Hughes autorename(name->base, '~', ' ', short_illegals, 8, bump);
113*d5c9a868SElliott Hughes }
114*d5c9a868SElliott Hughes
autorename_long(char * name,int bump)115*d5c9a868SElliott Hughes void autorename_long(char *name, int bump)
116*d5c9a868SElliott Hughes {
117*d5c9a868SElliott Hughes autorename(name, '-', '\0', long_illegals, 255, bump);
118*d5c9a868SElliott Hughes }
119*d5c9a868SElliott Hughes
120*d5c9a868SElliott Hughes
unicode_read(struct unicode_char * in,wchar_t * out,int num)121*d5c9a868SElliott Hughes static __inline__ int unicode_read(struct unicode_char *in,
122*d5c9a868SElliott Hughes wchar_t *out, int num)
123*d5c9a868SElliott Hughes {
124*d5c9a868SElliott Hughes wchar_t *end_out = out+num;
125*d5c9a868SElliott Hughes
126*d5c9a868SElliott Hughes while(out < end_out) {
127*d5c9a868SElliott Hughes #ifdef HAVE_WCHAR_H
128*d5c9a868SElliott Hughes *out = in->lchar | ((in->uchar) << 8);
129*d5c9a868SElliott Hughes #else
130*d5c9a868SElliott Hughes if (in->uchar)
131*d5c9a868SElliott Hughes *out = '_';
132*d5c9a868SElliott Hughes else
133*d5c9a868SElliott Hughes *out = in->lchar;
134*d5c9a868SElliott Hughes #endif
135*d5c9a868SElliott Hughes ++out;
136*d5c9a868SElliott Hughes ++in;
137*d5c9a868SElliott Hughes }
138*d5c9a868SElliott Hughes return num;
139*d5c9a868SElliott Hughes }
140*d5c9a868SElliott Hughes
141*d5c9a868SElliott Hughes
clear_vfat(struct vfat_state * v)142*d5c9a868SElliott Hughes void clear_vfat(struct vfat_state *v)
143*d5c9a868SElliott Hughes {
144*d5c9a868SElliott Hughes v->subentries = 0;
145*d5c9a868SElliott Hughes v->status = 0;
146*d5c9a868SElliott Hughes v->present = 0;
147*d5c9a868SElliott Hughes }
148*d5c9a868SElliott Hughes
149*d5c9a868SElliott Hughes
150*d5c9a868SElliott Hughes /* sum_shortname
151*d5c9a868SElliott Hughes *
152*d5c9a868SElliott Hughes * Calculate the checksum that results from the short name in *dir.
153*d5c9a868SElliott Hughes *
154*d5c9a868SElliott Hughes * The sum is formed by circularly right-shifting the previous sum
155*d5c9a868SElliott Hughes * and adding in each character, from left to right, padding both
156*d5c9a868SElliott Hughes * the name and extension to maximum length with spaces and skipping
157*d5c9a868SElliott Hughes * the "." (hence always summing exactly 11 characters).
158*d5c9a868SElliott Hughes *
159*d5c9a868SElliott Hughes * This exact algorithm is required in order to remain compatible
160*d5c9a868SElliott Hughes * with Microsoft Windows-95 and Microsoft Windows NT 3.5.
161*d5c9a868SElliott Hughes * Thanks to Jeffrey Richter of Microsoft Systems Journal for
162*d5c9a868SElliott Hughes * pointing me to the correct algorithm.
163*d5c9a868SElliott Hughes *
164*d5c9a868SElliott Hughes * David C. Niemi ([email protected]) 95.01.19
165*d5c9a868SElliott Hughes */
sum_shortname(const dos_name_t * dn)166*d5c9a868SElliott Hughes static __inline__ unsigned char sum_shortname(const dos_name_t *dn)
167*d5c9a868SElliott Hughes {
168*d5c9a868SElliott Hughes unsigned char sum;
169*d5c9a868SElliott Hughes const char *name=dn->base;
170*d5c9a868SElliott Hughes const char *end = name+11;
171*d5c9a868SElliott Hughes
172*d5c9a868SElliott Hughes for (sum=0; name<end; ++name)
173*d5c9a868SElliott Hughes sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1)
174*d5c9a868SElliott Hughes + (uint8_t) *name;
175*d5c9a868SElliott Hughes return(sum);
176*d5c9a868SElliott Hughes }
177*d5c9a868SElliott Hughes
178*d5c9a868SElliott Hughes /* check_vfat
179*d5c9a868SElliott Hughes *
180*d5c9a868SElliott Hughes * Inspect a directory and any associated VSEs.
181*d5c9a868SElliott Hughes * Return 1 if the VSEs comprise a valid long file name,
182*d5c9a868SElliott Hughes * 0 if not.
183*d5c9a868SElliott Hughes */
check_vfat(struct vfat_state * v,struct directory * dir)184*d5c9a868SElliott Hughes static __inline__ void check_vfat(struct vfat_state *v, struct directory *dir)
185*d5c9a868SElliott Hughes {
186*d5c9a868SElliott Hughes dos_name_t dn;
187*d5c9a868SElliott Hughes
188*d5c9a868SElliott Hughes if (! v->subentries) {
189*d5c9a868SElliott Hughes #ifdef DEBUG
190*d5c9a868SElliott Hughes fprintf(stderr, "check_vfat: no VSEs.\n");
191*d5c9a868SElliott Hughes #endif
192*d5c9a868SElliott Hughes return;
193*d5c9a868SElliott Hughes }
194*d5c9a868SElliott Hughes
195*d5c9a868SElliott Hughes memcpy(dn.base, (char *)dir->name, 8);
196*d5c9a868SElliott Hughes memcpy(dn.ext, (char *)dir->ext, 3);
197*d5c9a868SElliott Hughes
198*d5c9a868SElliott Hughes if (v->sum != sum_shortname(&dn))
199*d5c9a868SElliott Hughes return;
200*d5c9a868SElliott Hughes
201*d5c9a868SElliott Hughes if( (v->status & ((1<<v->subentries) - 1)) != (1<<v->subentries) - 1)
202*d5c9a868SElliott Hughes return; /* missing entries */
203*d5c9a868SElliott Hughes
204*d5c9a868SElliott Hughes /* zero out byte following last entry, for good measure */
205*d5c9a868SElliott Hughes v->name[VSE_NAMELEN * v->subentries] = 0;
206*d5c9a868SElliott Hughes v->present = 1;
207*d5c9a868SElliott Hughes }
208*d5c9a868SElliott Hughes
209*d5c9a868SElliott Hughes #pragma GCC diagnostic push
210*d5c9a868SElliott Hughes #pragma GCC diagnostic ignored "-Wsign-conversion"
211*d5c9a868SElliott Hughes /* We have indeed different types for the entry slot
212*d5c9a868SElliott Hughes * - the higher levels have a "signed" type, in order to accomodate
213*d5c9a868SElliott Hughes * reserved values for "root directory" entry, "not found" entries, and
214*d5c9a868SElliott Hughes * "uninitialized"
215*d5c9a868SElliott Hughes * - the lower levels always consider it as an index into the
216*d5c9a868SElliott Hughes * directory viewed as a table, i.e. always positive
217*d5c9a868SElliott Hughes */
clear_vses(Stream_t * Dir,int entrySlot,unsigned int last)218*d5c9a868SElliott Hughes int clear_vses(Stream_t *Dir, int entrySlot, unsigned int last)
219*d5c9a868SElliott Hughes {
220*d5c9a868SElliott Hughes direntry_t entry;
221*d5c9a868SElliott Hughes dirCache_t *cache;
222*d5c9a868SElliott Hughes int error;
223*d5c9a868SElliott Hughes
224*d5c9a868SElliott Hughes entry.Dir = Dir;
225*d5c9a868SElliott Hughes entry.entry = entrySlot;
226*d5c9a868SElliott Hughes
227*d5c9a868SElliott Hughes /*maximize(last, entry.entry + MAX_VFAT_SUBENTRIES);*/
228*d5c9a868SElliott Hughes cache = allocDirCache(Dir, last);
229*d5c9a868SElliott Hughes if(!cache) {
230*d5c9a868SElliott Hughes fprintf(stderr, "Out of memory error in clear_vses\n");
231*d5c9a868SElliott Hughes exit(1);
232*d5c9a868SElliott Hughes }
233*d5c9a868SElliott Hughes addFreeEntry(cache, entry.entry, last);
234*d5c9a868SElliott Hughes for (; entry.entry < (signed int) last; ++entry.entry) {
235*d5c9a868SElliott Hughes #ifdef DEBUG
236*d5c9a868SElliott Hughes fprintf(stderr,"Clearing entry %d.\n", entry.entry);
237*d5c9a868SElliott Hughes #endif
238*d5c9a868SElliott Hughes dir_read(&entry, &error);
239*d5c9a868SElliott Hughes if(error)
240*d5c9a868SElliott Hughes return error;
241*d5c9a868SElliott Hughes if(!entry.dir.name[0] || entry.dir.name[0] == DELMARK)
242*d5c9a868SElliott Hughes break;
243*d5c9a868SElliott Hughes entry.dir.name[0] = DELMARK;
244*d5c9a868SElliott Hughes if (entry.dir.attr == 0xf)
245*d5c9a868SElliott Hughes entry.dir.attr = '\0';
246*d5c9a868SElliott Hughes low_level_dir_write(&entry);
247*d5c9a868SElliott Hughes }
248*d5c9a868SElliott Hughes return 0;
249*d5c9a868SElliott Hughes }
250*d5c9a868SElliott Hughes
write_vfat(Stream_t * Dir,dos_name_t * shortname,char * longname,unsigned int start,direntry_t * mainEntry)251*d5c9a868SElliott Hughes int write_vfat(Stream_t *Dir, dos_name_t *shortname, char *longname,
252*d5c9a868SElliott Hughes unsigned int start,
253*d5c9a868SElliott Hughes direntry_t *mainEntry)
254*d5c9a868SElliott Hughes {
255*d5c9a868SElliott Hughes struct vfat_subentry *vse;
256*d5c9a868SElliott Hughes uint8_t vse_id, num_vses;
257*d5c9a868SElliott Hughes wchar_t *c;
258*d5c9a868SElliott Hughes direntry_t entry;
259*d5c9a868SElliott Hughes dirCache_t *cache;
260*d5c9a868SElliott Hughes wchar_t unixyName[13];
261*d5c9a868SElliott Hughes doscp_t *cp = GET_DOSCONVERT(Dir);
262*d5c9a868SElliott Hughes
263*d5c9a868SElliott Hughes wchar_t wlongname[MAX_VNAMELEN+1];
264*d5c9a868SElliott Hughes size_t wlen;
265*d5c9a868SElliott Hughes
266*d5c9a868SElliott Hughes if(longname) {
267*d5c9a868SElliott Hughes #ifdef DEBUG
268*d5c9a868SElliott Hughes printf("Entering write_vfat with longname=\"%s\", start=%d.\n",
269*d5c9a868SElliott Hughes longname,start);
270*d5c9a868SElliott Hughes #endif
271*d5c9a868SElliott Hughes entry.Dir = Dir;
272*d5c9a868SElliott Hughes vse = (struct vfat_subentry *) &entry.dir;
273*d5c9a868SElliott Hughes /* Fill in invariant part of vse */
274*d5c9a868SElliott Hughes vse->attribute = 0x0f;
275*d5c9a868SElliott Hughes vse->hash1 = vse->sector_l = vse->sector_u = 0;
276*d5c9a868SElliott Hughes vse->sum = sum_shortname(shortname);
277*d5c9a868SElliott Hughes #ifdef DEBUG
278*d5c9a868SElliott Hughes printf("Wrote checksum=%d for shortname %s.%s\n",
279*d5c9a868SElliott Hughes vse->sum,shortname->base,shortname->ext);
280*d5c9a868SElliott Hughes #endif
281*d5c9a868SElliott Hughes
282*d5c9a868SElliott Hughes wlen = native_to_wchar(longname, wlongname, MAX_VNAMELEN+1,
283*d5c9a868SElliott Hughes 0, 0);
284*d5c9a868SElliott Hughes num_vses = (uint8_t)((wlen + VSE_NAMELEN - 1)/VSE_NAMELEN);
285*d5c9a868SElliott Hughes for (vse_id = num_vses; vse_id; --vse_id) {
286*d5c9a868SElliott Hughes int end = 0;
287*d5c9a868SElliott Hughes
288*d5c9a868SElliott Hughes c = wlongname + (vse_id - 1) * VSE_NAMELEN;
289*d5c9a868SElliott Hughes
290*d5c9a868SElliott Hughes c += unicode_write(c, vse->text1, VSE1SIZE, &end);
291*d5c9a868SElliott Hughes c += unicode_write(c, vse->text2, VSE2SIZE, &end);
292*d5c9a868SElliott Hughes c += unicode_write(c, vse->text3, VSE3SIZE, &end);
293*d5c9a868SElliott Hughes
294*d5c9a868SElliott Hughes vse->id = (vse_id == num_vses) ? (vse_id | VSE_LAST) : vse_id;
295*d5c9a868SElliott Hughes #ifdef DEBUG
296*d5c9a868SElliott Hughes printf("Writing longname=(%s), VSE %d (%13s) at %d, end = %d.\n",
297*d5c9a868SElliott Hughes longname, vse_id, longname + (vse_id-1) * VSE_NAMELEN,
298*d5c9a868SElliott Hughes start + num_vses - vse_id, start + num_vses);
299*d5c9a868SElliott Hughes #endif
300*d5c9a868SElliott Hughes
301*d5c9a868SElliott Hughes entry.entry = start + num_vses - vse_id;
302*d5c9a868SElliott Hughes low_level_dir_write(&entry);
303*d5c9a868SElliott Hughes }
304*d5c9a868SElliott Hughes } else {
305*d5c9a868SElliott Hughes num_vses = 0;
306*d5c9a868SElliott Hughes wlongname[0]='\0';
307*d5c9a868SElliott Hughes }
308*d5c9a868SElliott Hughes cache = allocDirCache(Dir, start + num_vses + 1);
309*d5c9a868SElliott Hughes if(!cache) {
310*d5c9a868SElliott Hughes fprintf(stderr, "Out of memory error\n");
311*d5c9a868SElliott Hughes exit(1);
312*d5c9a868SElliott Hughes }
313*d5c9a868SElliott Hughes unix_name(cp, shortname->base, shortname->ext, 0, unixyName);
314*d5c9a868SElliott Hughes addUsedEntry(cache, start, start + num_vses + 1, wlongname, unixyName,
315*d5c9a868SElliott Hughes &mainEntry->dir);
316*d5c9a868SElliott Hughes low_level_dir_write(mainEntry);
317*d5c9a868SElliott Hughes return start + num_vses;
318*d5c9a868SElliott Hughes }
319*d5c9a868SElliott Hughes
dir_write(direntry_t * entry)320*d5c9a868SElliott Hughes void dir_write(direntry_t *entry)
321*d5c9a868SElliott Hughes {
322*d5c9a868SElliott Hughes dirCacheEntry_t *dce;
323*d5c9a868SElliott Hughes dirCache_t *cache;
324*d5c9a868SElliott Hughes
325*d5c9a868SElliott Hughes if(entry->entry == -3) {
326*d5c9a868SElliott Hughes fprintf(stderr, "Attempt to write root directory pointer\n");
327*d5c9a868SElliott Hughes exit(1);
328*d5c9a868SElliott Hughes }
329*d5c9a868SElliott Hughes
330*d5c9a868SElliott Hughes cache = allocDirCache(entry->Dir, entry->entry + 1);
331*d5c9a868SElliott Hughes if(!cache) {
332*d5c9a868SElliott Hughes fprintf(stderr, "Out of memory error in dir_write\n");
333*d5c9a868SElliott Hughes exit(1);
334*d5c9a868SElliott Hughes }
335*d5c9a868SElliott Hughes dce = cache->entries[entry->entry];
336*d5c9a868SElliott Hughes if(dce) {
337*d5c9a868SElliott Hughes if(entry->dir.name[0] == DELMARK) {
338*d5c9a868SElliott Hughes addFreeEntry(cache, dce->beginSlot, dce->endSlot);
339*d5c9a868SElliott Hughes } else {
340*d5c9a868SElliott Hughes dce->dir = entry->dir;
341*d5c9a868SElliott Hughes }
342*d5c9a868SElliott Hughes }
343*d5c9a868SElliott Hughes low_level_dir_write(entry);
344*d5c9a868SElliott Hughes }
345*d5c9a868SElliott Hughes
346*d5c9a868SElliott Hughes
347*d5c9a868SElliott Hughes /*
348*d5c9a868SElliott Hughes * The following function translates a series of vfat_subentries into
349*d5c9a868SElliott Hughes * data suitable for a dircache entry
350*d5c9a868SElliott Hughes */
parse_vses(direntry_t * entry,struct vfat_state * v)351*d5c9a868SElliott Hughes static __inline__ void parse_vses(direntry_t *entry,
352*d5c9a868SElliott Hughes struct vfat_state *v)
353*d5c9a868SElliott Hughes {
354*d5c9a868SElliott Hughes struct vfat_subentry *vse;
355*d5c9a868SElliott Hughes unsigned char id, last_flag;
356*d5c9a868SElliott Hughes wchar_t *c;
357*d5c9a868SElliott Hughes
358*d5c9a868SElliott Hughes vse = (struct vfat_subentry *) &entry->dir;
359*d5c9a868SElliott Hughes
360*d5c9a868SElliott Hughes id = vse->id & VSE_MASK;
361*d5c9a868SElliott Hughes last_flag = (vse->id & VSE_LAST);
362*d5c9a868SElliott Hughes if (id > MAX_VFAT_SUBENTRIES) {
363*d5c9a868SElliott Hughes fprintf(stderr, "parse_vses: invalid VSE ID %d at %d.\n",
364*d5c9a868SElliott Hughes id, entry->entry);
365*d5c9a868SElliott Hughes return;
366*d5c9a868SElliott Hughes }
367*d5c9a868SElliott Hughes
368*d5c9a868SElliott Hughes /* 950819: This code enforced finding the VSEs in order. Well, Win95
369*d5c9a868SElliott Hughes * likes to write them in *reverse* order for some bizarre reason! So
370*d5c9a868SElliott Hughes * we pretty much have to tolerate them coming in any possible order.
371*d5c9a868SElliott Hughes * So skip this check, we'll do without it (What does this do, Alain?).
372*d5c9a868SElliott Hughes *
373*d5c9a868SElliott Hughes * 950820: Totally rearranged code to tolerate any order but to warn if
374*d5c9a868SElliott Hughes * they are not in reverse order like Win95 uses.
375*d5c9a868SElliott Hughes *
376*d5c9a868SElliott Hughes * 950909: Tolerate any order. We recognize new chains by mismatching
377*d5c9a868SElliott Hughes * checksums. In the event that the checksums match, new entries silently
378*d5c9a868SElliott Hughes * overwrite old entries of the same id. This should accept all valid
379*d5c9a868SElliott Hughes * entries, but may fail to reject invalid entries in some rare cases.
380*d5c9a868SElliott Hughes */
381*d5c9a868SElliott Hughes
382*d5c9a868SElliott Hughes /* bad checksum, begin new chain */
383*d5c9a868SElliott Hughes if(v->sum != vse->sum) {
384*d5c9a868SElliott Hughes clear_vfat(v);
385*d5c9a868SElliott Hughes v->sum = vse->sum;
386*d5c9a868SElliott Hughes }
387*d5c9a868SElliott Hughes
388*d5c9a868SElliott Hughes #ifdef DEBUG
389*d5c9a868SElliott Hughes if(v->status & (1 << (id-1)))
390*d5c9a868SElliott Hughes fprintf(stderr,
391*d5c9a868SElliott Hughes "parse_vses: duplicate VSE %d\n", vse->id);
392*d5c9a868SElliott Hughes #endif
393*d5c9a868SElliott Hughes
394*d5c9a868SElliott Hughes v->status |= 1 << (id-1);
395*d5c9a868SElliott Hughes if(last_flag)
396*d5c9a868SElliott Hughes v->subentries = id;
397*d5c9a868SElliott Hughes
398*d5c9a868SElliott Hughes #ifdef DEBUG
399*d5c9a868SElliott Hughes if (id > v->subentries)
400*d5c9a868SElliott Hughes /* simple test to detect entries preceding
401*d5c9a868SElliott Hughes * the "last" entry (really the first) */
402*d5c9a868SElliott Hughes fprintf(stderr,
403*d5c9a868SElliott Hughes "parse_vses: new VSE %d sans LAST flag\n",
404*d5c9a868SElliott Hughes vse->id);
405*d5c9a868SElliott Hughes #endif
406*d5c9a868SElliott Hughes
407*d5c9a868SElliott Hughes c = &(v->name[VSE_NAMELEN * (id-1)]);
408*d5c9a868SElliott Hughes c += unicode_read(vse->text1, c, VSE1SIZE);
409*d5c9a868SElliott Hughes c += unicode_read(vse->text2, c, VSE2SIZE);
410*d5c9a868SElliott Hughes c += unicode_read(vse->text3, c, VSE3SIZE);
411*d5c9a868SElliott Hughes #ifdef DEBUG
412*d5c9a868SElliott Hughes printf("Read VSE %d at %d, subentries=%d, = (%13ls).\n",
413*d5c9a868SElliott Hughes id,entry->entry,v->subentries,&(v->name[VSE_NAMELEN * (id-1)]));
414*d5c9a868SElliott Hughes #endif
415*d5c9a868SElliott Hughes if (last_flag)
416*d5c9a868SElliott Hughes *c = '\0'; /* Null terminate long name */
417*d5c9a868SElliott Hughes }
418*d5c9a868SElliott Hughes
419*d5c9a868SElliott Hughes /**
420*d5c9a868SElliott Hughes * Read one complete entry from directory (main name plus any VSEs
421*d5c9a868SElliott Hughes * belonging to it)
422*d5c9a868SElliott Hughes */
vfat_lookup_loop_common(doscp_t * cp,direntry_t * direntry,dirCache_t * cache,int lookForFreeSpace,int * io_error)423*d5c9a868SElliott Hughes static dirCacheEntry_t *vfat_lookup_loop_common(doscp_t *cp,
424*d5c9a868SElliott Hughes direntry_t *direntry,
425*d5c9a868SElliott Hughes dirCache_t *cache,
426*d5c9a868SElliott Hughes int lookForFreeSpace,
427*d5c9a868SElliott Hughes int *io_error)
428*d5c9a868SElliott Hughes {
429*d5c9a868SElliott Hughes wchar_t newfile[13];
430*d5c9a868SElliott Hughes unsigned int initpos = direntry->entry + 1;
431*d5c9a868SElliott Hughes struct vfat_state vfat;
432*d5c9a868SElliott Hughes wchar_t *longname;
433*d5c9a868SElliott Hughes int error;
434*d5c9a868SElliott Hughes int endmarkSeen = 0;
435*d5c9a868SElliott Hughes
436*d5c9a868SElliott Hughes /* not yet cached */
437*d5c9a868SElliott Hughes *io_error = 0;
438*d5c9a868SElliott Hughes clear_vfat(&vfat);
439*d5c9a868SElliott Hughes while(1) {
440*d5c9a868SElliott Hughes ++direntry->entry;
441*d5c9a868SElliott Hughes if(!dir_read(direntry, &error)){
442*d5c9a868SElliott Hughes if(error) {
443*d5c9a868SElliott Hughes *io_error = error;
444*d5c9a868SElliott Hughes return NULL;
445*d5c9a868SElliott Hughes }
446*d5c9a868SElliott Hughes addFreeEndEntry(cache, initpos, direntry->entry,
447*d5c9a868SElliott Hughes endmarkSeen);
448*d5c9a868SElliott Hughes return addEndEntry(cache, direntry->entry);
449*d5c9a868SElliott Hughes }
450*d5c9a868SElliott Hughes
451*d5c9a868SElliott Hughes if (endmarkSeen || direntry->dir.name[0] == ENDMARK){
452*d5c9a868SElliott Hughes /* the end of the directory */
453*d5c9a868SElliott Hughes if(lookForFreeSpace) {
454*d5c9a868SElliott Hughes endmarkSeen = 1;
455*d5c9a868SElliott Hughes continue;
456*d5c9a868SElliott Hughes }
457*d5c9a868SElliott Hughes return addEndEntry(cache, direntry->entry);
458*d5c9a868SElliott Hughes }
459*d5c9a868SElliott Hughes if(direntry->dir.name[0] != DELMARK &&
460*d5c9a868SElliott Hughes direntry->dir.attr == 0x0f)
461*d5c9a868SElliott Hughes parse_vses(direntry, &vfat);
462*d5c9a868SElliott Hughes else
463*d5c9a868SElliott Hughes /* the main entry */
464*d5c9a868SElliott Hughes break;
465*d5c9a868SElliott Hughes }
466*d5c9a868SElliott Hughes
467*d5c9a868SElliott Hughes /* If we get here, it's a short name FAT entry, maybe erased.
468*d5c9a868SElliott Hughes * thus we should make sure that the vfat structure will be
469*d5c9a868SElliott Hughes * cleared before the next loop run */
470*d5c9a868SElliott Hughes
471*d5c9a868SElliott Hughes /* deleted file */
472*d5c9a868SElliott Hughes if (direntry->dir.name[0] == DELMARK) {
473*d5c9a868SElliott Hughes return addFreeEntry(cache, initpos,
474*d5c9a868SElliott Hughes direntry->entry + 1);
475*d5c9a868SElliott Hughes }
476*d5c9a868SElliott Hughes
477*d5c9a868SElliott Hughes check_vfat(&vfat, &direntry->dir);
478*d5c9a868SElliott Hughes if(!vfat.present)
479*d5c9a868SElliott Hughes vfat.subentries = 0;
480*d5c9a868SElliott Hughes
481*d5c9a868SElliott Hughes /* mark space between last entry and this one as free */
482*d5c9a868SElliott Hughes addFreeEntry(cache, initpos,
483*d5c9a868SElliott Hughes direntry->entry - vfat.subentries);
484*d5c9a868SElliott Hughes
485*d5c9a868SElliott Hughes if (direntry->dir.attr & 0x8){
486*d5c9a868SElliott Hughes /* Read entry as a label */
487*d5c9a868SElliott Hughes wchar_t *ptr = newfile;
488*d5c9a868SElliott Hughes if (direntry->dir.name[0] == '\x05') {
489*d5c9a868SElliott Hughes ptr += dos_to_wchar(cp, "\xE5", ptr, 1);
490*d5c9a868SElliott Hughes ptr += dos_to_wchar(cp, direntry->dir.name+1, ptr, 7);
491*d5c9a868SElliott Hughes } else {
492*d5c9a868SElliott Hughes ptr += dos_to_wchar(cp, direntry->dir.name, ptr, 8);
493*d5c9a868SElliott Hughes }
494*d5c9a868SElliott Hughes ptr += dos_to_wchar(cp, direntry->dir.ext, ptr, 3);
495*d5c9a868SElliott Hughes *ptr = '\0';
496*d5c9a868SElliott Hughes } else
497*d5c9a868SElliott Hughes unix_name(cp,
498*d5c9a868SElliott Hughes direntry->dir.name,
499*d5c9a868SElliott Hughes direntry->dir.ext,
500*d5c9a868SElliott Hughes direntry->dir.Case,
501*d5c9a868SElliott Hughes newfile);
502*d5c9a868SElliott Hughes
503*d5c9a868SElliott Hughes if(vfat.present)
504*d5c9a868SElliott Hughes longname = vfat.name;
505*d5c9a868SElliott Hughes else
506*d5c9a868SElliott Hughes longname = 0;
507*d5c9a868SElliott Hughes
508*d5c9a868SElliott Hughes return addUsedEntry(cache, direntry->entry - vfat.subentries,
509*d5c9a868SElliott Hughes direntry->entry + 1, longname,
510*d5c9a868SElliott Hughes newfile, &direntry->dir);
511*d5c9a868SElliott Hughes }
512*d5c9a868SElliott Hughes
vfat_lookup_loop_for_read(doscp_t * cp,direntry_t * direntry,dirCache_t * cache,int * io_error)513*d5c9a868SElliott Hughes static __inline__ dirCacheEntry_t *vfat_lookup_loop_for_read(doscp_t *cp,
514*d5c9a868SElliott Hughes direntry_t *direntry,
515*d5c9a868SElliott Hughes dirCache_t *cache,
516*d5c9a868SElliott Hughes int *io_error)
517*d5c9a868SElliott Hughes {
518*d5c9a868SElliott Hughes int initpos = direntry->entry + 1;
519*d5c9a868SElliott Hughes dirCacheEntry_t *dce;
520*d5c9a868SElliott Hughes
521*d5c9a868SElliott Hughes *io_error = 0;
522*d5c9a868SElliott Hughes dce = cache->entries[initpos];
523*d5c9a868SElliott Hughes if(dce) {
524*d5c9a868SElliott Hughes direntry->entry = dce->endSlot - 1;
525*d5c9a868SElliott Hughes return dce;
526*d5c9a868SElliott Hughes } else {
527*d5c9a868SElliott Hughes return vfat_lookup_loop_common(cp,
528*d5c9a868SElliott Hughes direntry, cache, 0, io_error);
529*d5c9a868SElliott Hughes }
530*d5c9a868SElliott Hughes }
531*d5c9a868SElliott Hughes
532*d5c9a868SElliott Hughes
533*d5c9a868SElliott Hughes typedef enum result_t {
534*d5c9a868SElliott Hughes RES_NOMATCH,
535*d5c9a868SElliott Hughes RES_MATCH,
536*d5c9a868SElliott Hughes RES_END,
537*d5c9a868SElliott Hughes RES_ERROR
538*d5c9a868SElliott Hughes } result_t;
539*d5c9a868SElliott Hughes
540*d5c9a868SElliott Hughes
541*d5c9a868SElliott Hughes /*
542*d5c9a868SElliott Hughes * 0 does not match
543*d5c9a868SElliott Hughes * 1 matches
544*d5c9a868SElliott Hughes * 2 end
545*d5c9a868SElliott Hughes */
checkNameForMatch(struct direntry_t * direntry,dirCacheEntry_t * dce,const wchar_t * filename,int length,int flags)546*d5c9a868SElliott Hughes static result_t checkNameForMatch(struct direntry_t *direntry,
547*d5c9a868SElliott Hughes dirCacheEntry_t *dce,
548*d5c9a868SElliott Hughes const wchar_t *filename,
549*d5c9a868SElliott Hughes int length,
550*d5c9a868SElliott Hughes int flags)
551*d5c9a868SElliott Hughes {
552*d5c9a868SElliott Hughes switch(dce->type) {
553*d5c9a868SElliott Hughes case DCET_FREE:
554*d5c9a868SElliott Hughes return RES_NOMATCH;
555*d5c9a868SElliott Hughes case DCET_END:
556*d5c9a868SElliott Hughes return RES_END;
557*d5c9a868SElliott Hughes case DCET_USED:
558*d5c9a868SElliott Hughes break;
559*d5c9a868SElliott Hughes #ifdef DEBUG
560*d5c9a868SElliott Hughes default:
561*d5c9a868SElliott Hughes fprintf(stderr, "Unexpected entry type %d\n",
562*d5c9a868SElliott Hughes dce->type);
563*d5c9a868SElliott Hughes return RES_ERROR;
564*d5c9a868SElliott Hughes #endif
565*d5c9a868SElliott Hughes }
566*d5c9a868SElliott Hughes
567*d5c9a868SElliott Hughes direntry->dir = dce->dir;
568*d5c9a868SElliott Hughes
569*d5c9a868SElliott Hughes /* make sure the entry is of an accepted type */
570*d5c9a868SElliott Hughes if((direntry->dir.attr & 0x8) && !(flags & ACCEPT_LABEL))
571*d5c9a868SElliott Hughes return RES_NOMATCH;
572*d5c9a868SElliott Hughes
573*d5c9a868SElliott Hughes
574*d5c9a868SElliott Hughes /*---------- multiple files ----------*/
575*d5c9a868SElliott Hughes if(!((flags & MATCH_ANY) ||
576*d5c9a868SElliott Hughes (dce->longName &&
577*d5c9a868SElliott Hughes match(dce->longName, filename, direntry->name, 0, length)) ||
578*d5c9a868SElliott Hughes match(dce->shortName, filename, direntry->name, 1, length))) {
579*d5c9a868SElliott Hughes
580*d5c9a868SElliott Hughes return RES_NOMATCH;
581*d5c9a868SElliott Hughes }
582*d5c9a868SElliott Hughes
583*d5c9a868SElliott Hughes /* entry of non-requested type, has to come after name
584*d5c9a868SElliott Hughes * checking because of clash handling */
585*d5c9a868SElliott Hughes if(IS_DIR(direntry) && !(flags & ACCEPT_DIR)) {
586*d5c9a868SElliott Hughes if(!(flags & (ACCEPT_LABEL|MATCH_ANY|NO_MSG))) {
587*d5c9a868SElliott Hughes char tmp[4*13+1];
588*d5c9a868SElliott Hughes WCHAR_TO_NATIVE(dce->shortName,tmp,13);
589*d5c9a868SElliott Hughes fprintf(stderr, "Skipping \"%s\", is a directory\n",
590*d5c9a868SElliott Hughes tmp);
591*d5c9a868SElliott Hughes }
592*d5c9a868SElliott Hughes return RES_NOMATCH;
593*d5c9a868SElliott Hughes }
594*d5c9a868SElliott Hughes
595*d5c9a868SElliott Hughes if(!(direntry->dir.attr & (ATTR_LABEL | ATTR_DIR)) &&
596*d5c9a868SElliott Hughes !(flags & ACCEPT_PLAIN)) {
597*d5c9a868SElliott Hughes if(!(flags & (ACCEPT_LABEL|MATCH_ANY|NO_MSG))) {
598*d5c9a868SElliott Hughes char tmp[4*13+1];
599*d5c9a868SElliott Hughes WCHAR_TO_NATIVE(dce->shortName,tmp,13);
600*d5c9a868SElliott Hughes fprintf(stderr,
601*d5c9a868SElliott Hughes "Skipping \"%s\", is not a directory\n",
602*d5c9a868SElliott Hughes tmp);
603*d5c9a868SElliott Hughes }
604*d5c9a868SElliott Hughes return RES_NOMATCH;
605*d5c9a868SElliott Hughes }
606*d5c9a868SElliott Hughes
607*d5c9a868SElliott Hughes return RES_MATCH;
608*d5c9a868SElliott Hughes }
609*d5c9a868SElliott Hughes
610*d5c9a868SElliott Hughes
vfat_lookup_zt(direntry_t * direntry,const char * filename,int flags,char * shortname,size_t shortname_size,char * longname,size_t longname_size)611*d5c9a868SElliott Hughes int vfat_lookup_zt(direntry_t *direntry, const char *filename,
612*d5c9a868SElliott Hughes int flags, char *shortname, size_t shortname_size,
613*d5c9a868SElliott Hughes char *longname, size_t longname_size) {
614*d5c9a868SElliott Hughes return vfat_lookup(direntry, filename, strlen(filename),
615*d5c9a868SElliott Hughes flags, shortname, shortname_size,
616*d5c9a868SElliott Hughes longname, longname_size);
617*d5c9a868SElliott Hughes }
618*d5c9a868SElliott Hughes
619*d5c9a868SElliott Hughes /*
620*d5c9a868SElliott Hughes * vfat_lookup looks for filenames in directory dir.
621*d5c9a868SElliott Hughes * if a name if found, it is returned in outname
622*d5c9a868SElliott Hughes * if applicable, the file is opened and its stream is returned in File
623*d5c9a868SElliott Hughes */
624*d5c9a868SElliott Hughes
vfat_lookup(direntry_t * direntry,const char * filename,size_t length,int flags,char * shortname,size_t shortname_size,char * longname,size_t longname_size)625*d5c9a868SElliott Hughes int vfat_lookup(direntry_t *direntry, const char *filename,
626*d5c9a868SElliott Hughes size_t length,
627*d5c9a868SElliott Hughes int flags, char *shortname, size_t shortname_size,
628*d5c9a868SElliott Hughes char *longname, size_t longname_size)
629*d5c9a868SElliott Hughes {
630*d5c9a868SElliott Hughes dirCacheEntry_t *dce;
631*d5c9a868SElliott Hughes result_t result;
632*d5c9a868SElliott Hughes dirCache_t *cache;
633*d5c9a868SElliott Hughes int io_error;
634*d5c9a868SElliott Hughes wchar_t wfilename[MAX_VNAMELEN+1];
635*d5c9a868SElliott Hughes doscp_t *cp = GET_DOSCONVERT(direntry->Dir);
636*d5c9a868SElliott Hughes
637*d5c9a868SElliott Hughes if(filename != NULL)
638*d5c9a868SElliott Hughes length = native_to_wchar(filename, wfilename, MAX_VNAMELEN,
639*d5c9a868SElliott Hughes filename+length, 0);
640*d5c9a868SElliott Hughes else
641*d5c9a868SElliott Hughes length = 0;
642*d5c9a868SElliott Hughes
643*d5c9a868SElliott Hughes if (direntry->entry == -2)
644*d5c9a868SElliott Hughes return -1;
645*d5c9a868SElliott Hughes
646*d5c9a868SElliott Hughes cache = allocDirCache(direntry->Dir, direntry->entry+1);
647*d5c9a868SElliott Hughes if(!cache) {
648*d5c9a868SElliott Hughes fprintf(stderr, "Out of memory error in vfat_lookup [0]\n");
649*d5c9a868SElliott Hughes exit(1);
650*d5c9a868SElliott Hughes }
651*d5c9a868SElliott Hughes
652*d5c9a868SElliott Hughes do {
653*d5c9a868SElliott Hughes dce = vfat_lookup_loop_for_read(cp, direntry, cache, &io_error);
654*d5c9a868SElliott Hughes if(!dce) {
655*d5c9a868SElliott Hughes if (io_error)
656*d5c9a868SElliott Hughes return -2;
657*d5c9a868SElliott Hughes fprintf(stderr, "Out of memory error in vfat_lookup\n");
658*d5c9a868SElliott Hughes exit(1);
659*d5c9a868SElliott Hughes }
660*d5c9a868SElliott Hughes result = checkNameForMatch(direntry, dce,
661*d5c9a868SElliott Hughes wfilename,
662*d5c9a868SElliott Hughes (int) length, flags);
663*d5c9a868SElliott Hughes } while(result == RES_NOMATCH);
664*d5c9a868SElliott Hughes
665*d5c9a868SElliott Hughes if(result == RES_MATCH){
666*d5c9a868SElliott Hughes if(longname){
667*d5c9a868SElliott Hughes if(dce->longName)
668*d5c9a868SElliott Hughes wchar_to_native(dce->longName, longname,
669*d5c9a868SElliott Hughes MAX_VNAMELEN, longname_size);
670*d5c9a868SElliott Hughes else
671*d5c9a868SElliott Hughes *longname ='\0';
672*d5c9a868SElliott Hughes }
673*d5c9a868SElliott Hughes if(shortname)
674*d5c9a868SElliott Hughes wchar_to_native(dce->shortName, shortname,
675*d5c9a868SElliott Hughes 12, shortname_size);
676*d5c9a868SElliott Hughes direntry->beginSlot = dce->beginSlot;
677*d5c9a868SElliott Hughes direntry->endSlot = dce->endSlot-1;
678*d5c9a868SElliott Hughes return 0; /* file found */
679*d5c9a868SElliott Hughes } else {
680*d5c9a868SElliott Hughes direntry->entry = -2;
681*d5c9a868SElliott Hughes return -1; /* no file found */
682*d5c9a868SElliott Hughes }
683*d5c9a868SElliott Hughes }
684*d5c9a868SElliott Hughes
vfat_lookup_loop_for_insert(doscp_t * cp,direntry_t * direntry,unsigned int initpos,dirCache_t * cache)685*d5c9a868SElliott Hughes static __inline__ dirCacheEntry_t *vfat_lookup_loop_for_insert(doscp_t *cp,
686*d5c9a868SElliott Hughes direntry_t *direntry,
687*d5c9a868SElliott Hughes unsigned int initpos,
688*d5c9a868SElliott Hughes dirCache_t *cache)
689*d5c9a868SElliott Hughes {
690*d5c9a868SElliott Hughes dirCacheEntry_t *dce;
691*d5c9a868SElliott Hughes int io_error;
692*d5c9a868SElliott Hughes
693*d5c9a868SElliott Hughes dce = cache->entries[initpos];
694*d5c9a868SElliott Hughes if(dce && dce->type != DCET_END) {
695*d5c9a868SElliott Hughes return dce;
696*d5c9a868SElliott Hughes } else {
697*d5c9a868SElliott Hughes direntry->entry = initpos - 1;
698*d5c9a868SElliott Hughes dce = vfat_lookup_loop_common(cp,
699*d5c9a868SElliott Hughes direntry, cache, 1, &io_error);
700*d5c9a868SElliott Hughes if(!dce) {
701*d5c9a868SElliott Hughes if (io_error) {
702*d5c9a868SElliott Hughes return NULL;
703*d5c9a868SElliott Hughes }
704*d5c9a868SElliott Hughes fprintf(stderr,
705*d5c9a868SElliott Hughes "Out of memory error in vfat_lookup_loop\n");
706*d5c9a868SElliott Hughes exit(1);
707*d5c9a868SElliott Hughes }
708*d5c9a868SElliott Hughes return cache->entries[initpos];
709*d5c9a868SElliott Hughes }
710*d5c9a868SElliott Hughes }
711*d5c9a868SElliott Hughes
accountFreeSlots(struct scan_state * ssp,dirCacheEntry_t * dce)712*d5c9a868SElliott Hughes static void accountFreeSlots(struct scan_state *ssp, dirCacheEntry_t *dce)
713*d5c9a868SElliott Hughes {
714*d5c9a868SElliott Hughes if(ssp->got_slots)
715*d5c9a868SElliott Hughes return;
716*d5c9a868SElliott Hughes
717*d5c9a868SElliott Hughes if(ssp->free_end != dce->beginSlot) {
718*d5c9a868SElliott Hughes ssp->free_start = dce->beginSlot;
719*d5c9a868SElliott Hughes }
720*d5c9a868SElliott Hughes ssp->free_end = dce->endSlot;
721*d5c9a868SElliott Hughes
722*d5c9a868SElliott Hughes if(ssp->free_end - ssp->free_start >= ssp->size_needed) {
723*d5c9a868SElliott Hughes ssp->got_slots = 1;
724*d5c9a868SElliott Hughes ssp->slot = ssp->free_start + ssp->size_needed - 1;
725*d5c9a868SElliott Hughes }
726*d5c9a868SElliott Hughes }
727*d5c9a868SElliott Hughes
clear_scan(wchar_t * longname,int use_longname,struct scan_state * s)728*d5c9a868SElliott Hughes static void clear_scan(wchar_t *longname, int use_longname,
729*d5c9a868SElliott Hughes struct scan_state *s)
730*d5c9a868SElliott Hughes {
731*d5c9a868SElliott Hughes s->shortmatch = s->longmatch = s->slot = -1;
732*d5c9a868SElliott Hughes s->free_end = s->got_slots = s->free_start = 0;
733*d5c9a868SElliott Hughes
734*d5c9a868SElliott Hughes if (use_longname & 1)
735*d5c9a868SElliott Hughes s->size_needed = (unsigned)
736*d5c9a868SElliott Hughes (1 + (wcslen(longname) + VSE_NAMELEN - 1)/VSE_NAMELEN);
737*d5c9a868SElliott Hughes else
738*d5c9a868SElliott Hughes s->size_needed = 1;
739*d5c9a868SElliott Hughes }
740*d5c9a868SElliott Hughes
741*d5c9a868SElliott Hughes /* lookup_for_insert replaces the old scandir function. It directly
742*d5c9a868SElliott Hughes * calls into vfat_lookup_loop, thus eliminating the overhead of the
743*d5c9a868SElliott Hughes * normal vfat_lookup
744*d5c9a868SElliott Hughes */
lookupForInsert(Stream_t * Dir,struct direntry_t * direntry,dos_name_t * dosname,char * longname,struct scan_state * ssp,int ignore_entry,int source_entry,int pessimisticShortRename,int use_longname)745*d5c9a868SElliott Hughes int lookupForInsert(Stream_t *Dir,
746*d5c9a868SElliott Hughes struct direntry_t *direntry,
747*d5c9a868SElliott Hughes dos_name_t *dosname,
748*d5c9a868SElliott Hughes char *longname,
749*d5c9a868SElliott Hughes struct scan_state *ssp,
750*d5c9a868SElliott Hughes int ignore_entry,
751*d5c9a868SElliott Hughes int source_entry,
752*d5c9a868SElliott Hughes int pessimisticShortRename,
753*d5c9a868SElliott Hughes int use_longname)
754*d5c9a868SElliott Hughes {
755*d5c9a868SElliott Hughes direntry_t entry;
756*d5c9a868SElliott Hughes int ignore_match;
757*d5c9a868SElliott Hughes dirCacheEntry_t *dce;
758*d5c9a868SElliott Hughes dirCache_t *cache;
759*d5c9a868SElliott Hughes unsigned int pos; /* position _before_ the next answered entry */
760*d5c9a868SElliott Hughes wchar_t shortName[13];
761*d5c9a868SElliott Hughes wchar_t wlongname[MAX_VNAMELEN+1];
762*d5c9a868SElliott Hughes doscp_t *cp = GET_DOSCONVERT(Dir);
763*d5c9a868SElliott Hughes
764*d5c9a868SElliott Hughes native_to_wchar(longname, wlongname, MAX_VNAMELEN+1, 0, 0);
765*d5c9a868SElliott Hughes clear_scan(wlongname, use_longname, ssp);
766*d5c9a868SElliott Hughes
767*d5c9a868SElliott Hughes ignore_match = (ignore_entry == -2 );
768*d5c9a868SElliott Hughes
769*d5c9a868SElliott Hughes initializeDirentry(&entry, Dir);
770*d5c9a868SElliott Hughes ssp->match_free = 0;
771*d5c9a868SElliott Hughes
772*d5c9a868SElliott Hughes /* hash bitmap of already encountered names. Speeds up batch appends
773*d5c9a868SElliott Hughes * to huge directories, because in the best case, we only need to scan
774*d5c9a868SElliott Hughes * the new entries rather than the whole directory */
775*d5c9a868SElliott Hughes cache = allocDirCache(Dir, 1);
776*d5c9a868SElliott Hughes if(!cache) {
777*d5c9a868SElliott Hughes fprintf(stderr, "Out of memory error in lookupForInsert\n");
778*d5c9a868SElliott Hughes exit(1);
779*d5c9a868SElliott Hughes }
780*d5c9a868SElliott Hughes
781*d5c9a868SElliott Hughes if(!ignore_match)
782*d5c9a868SElliott Hughes unix_name(cp, dosname->base, dosname->ext, 0, shortName);
783*d5c9a868SElliott Hughes
784*d5c9a868SElliott Hughes pos = cache->nrHashed;
785*d5c9a868SElliott Hughes if(source_entry >= 0 ||
786*d5c9a868SElliott Hughes (pos && isHashed(cache, wlongname))) {
787*d5c9a868SElliott Hughes pos = 0;
788*d5c9a868SElliott Hughes } else if(pos && !ignore_match && isHashed(cache, shortName)) {
789*d5c9a868SElliott Hughes if(pessimisticShortRename) {
790*d5c9a868SElliott Hughes ssp->shortmatch = -2;
791*d5c9a868SElliott Hughes return 1;
792*d5c9a868SElliott Hughes }
793*d5c9a868SElliott Hughes pos = 0;
794*d5c9a868SElliott Hughes } else if(growDirCache(cache, pos) < 0) {
795*d5c9a868SElliott Hughes fprintf(stderr, "Out of memory error in vfat_looup [0]\n");
796*d5c9a868SElliott Hughes exit(1);
797*d5c9a868SElliott Hughes }
798*d5c9a868SElliott Hughes do {
799*d5c9a868SElliott Hughes dce = vfat_lookup_loop_for_insert(cp, &entry, pos, cache);
800*d5c9a868SElliott Hughes switch(dce->type) {
801*d5c9a868SElliott Hughes case DCET_FREE:
802*d5c9a868SElliott Hughes accountFreeSlots(ssp, dce);
803*d5c9a868SElliott Hughes break;
804*d5c9a868SElliott Hughes case DCET_USED:
805*d5c9a868SElliott Hughes if(!(dce->dir.attr & 0x8) &&
806*d5c9a868SElliott Hughes (signed int)dce->endSlot-1 == source_entry)
807*d5c9a868SElliott Hughes accountFreeSlots(ssp, dce);
808*d5c9a868SElliott Hughes
809*d5c9a868SElliott Hughes /* labels never match, neither does the
810*d5c9a868SElliott Hughes * ignored entry */
811*d5c9a868SElliott Hughes if( (dce->dir.attr & 0x8) ||
812*d5c9a868SElliott Hughes ((signed int)dce->endSlot-1==ignore_entry))
813*d5c9a868SElliott Hughes break;
814*d5c9a868SElliott Hughes
815*d5c9a868SElliott Hughes /* check long name */
816*d5c9a868SElliott Hughes if((dce->longName &&
817*d5c9a868SElliott Hughes !wcscasecmp(dce->longName, wlongname)) ||
818*d5c9a868SElliott Hughes (dce->shortName &&
819*d5c9a868SElliott Hughes !wcscasecmp(dce->shortName, wlongname))) {
820*d5c9a868SElliott Hughes ssp->longmatch = dce->endSlot - 1;
821*d5c9a868SElliott Hughes /* long match is a reason for
822*d5c9a868SElliott Hughes * immediate stop */
823*d5c9a868SElliott Hughes direntry->beginSlot = dce->beginSlot;
824*d5c9a868SElliott Hughes direntry->endSlot = dce->endSlot - 1;
825*d5c9a868SElliott Hughes return 1;
826*d5c9a868SElliott Hughes }
827*d5c9a868SElliott Hughes
828*d5c9a868SElliott Hughes /* Long name or not, always check for
829*d5c9a868SElliott Hughes * short name match */
830*d5c9a868SElliott Hughes if (!ignore_match &&
831*d5c9a868SElliott Hughes !wcscasecmp(shortName, dce->shortName))
832*d5c9a868SElliott Hughes ssp->shortmatch = dce->endSlot - 1;
833*d5c9a868SElliott Hughes break;
834*d5c9a868SElliott Hughes case DCET_END:
835*d5c9a868SElliott Hughes break;
836*d5c9a868SElliott Hughes }
837*d5c9a868SElliott Hughes pos = dce->endSlot;
838*d5c9a868SElliott Hughes } while(dce->type != DCET_END);
839*d5c9a868SElliott Hughes if (ssp->shortmatch > -1)
840*d5c9a868SElliott Hughes return 1;
841*d5c9a868SElliott Hughes ssp->max_entry = dce->beginSlot;
842*d5c9a868SElliott Hughes if (ssp->got_slots)
843*d5c9a868SElliott Hughes return 6; /* Success */
844*d5c9a868SElliott Hughes
845*d5c9a868SElliott Hughes /* Need more room. Can we grow the directory? */
846*d5c9a868SElliott Hughes if(!isRootDir(Dir))
847*d5c9a868SElliott Hughes return 5; /* OK, try to grow the directory */
848*d5c9a868SElliott Hughes
849*d5c9a868SElliott Hughes fprintf(stderr, "No directory slots\n");
850*d5c9a868SElliott Hughes return -1;
851*d5c9a868SElliott Hughes }
852*d5c9a868SElliott Hughes #pragma GCC diagnostic pop
853*d5c9a868SElliott Hughes
854*d5c9a868SElliott Hughes
855*d5c9a868SElliott Hughes /* End vfat.c */
856