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