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