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