• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 + (uint8_t)(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 			+ (uint8_t) *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 #pragma GCC diagnostic push
210 #pragma GCC diagnostic ignored "-Wsign-conversion"
211 /* We have indeed different types for the entry slot
212  * - the higher levels have a "signed" type, in order to accomodate
213  *   reserved values for "root directory" entry, "not found" entries, and
214  *   "uninitialized"
215  * - the lower levels always consider it as an index into the
216  *   directory viewed as a table, i.e. always positive
217  */
clear_vses(Stream_t * Dir,int entrySlot,unsigned int last)218 int clear_vses(Stream_t *Dir, int entrySlot, unsigned int last)
219 {
220 	direntry_t entry;
221 	dirCache_t *cache;
222 	int error;
223 
224 	entry.Dir = Dir;
225 	entry.entry = entrySlot;
226 
227 	/*maximize(last, entry.entry + MAX_VFAT_SUBENTRIES);*/
228 	cache = allocDirCache(Dir, last);
229 	if(!cache) {
230 		fprintf(stderr, "Out of memory error in clear_vses\n");
231 		exit(1);
232 	}
233 	addFreeEntry(cache, entry.entry, last);
234 	for (; entry.entry < (signed int) last; ++entry.entry) {
235 #ifdef DEBUG
236 		fprintf(stderr,"Clearing entry %d.\n", entry.entry);
237 #endif
238 		dir_read(&entry, &error);
239 		if(error)
240 			return error;
241 		if(!entry.dir.name[0] || entry.dir.name[0] == DELMARK)
242 			break;
243 		entry.dir.name[0] = DELMARK;
244 		if (entry.dir.attr == 0xf)
245 			entry.dir.attr = '\0';
246 		low_level_dir_write(&entry);
247 	}
248 	return 0;
249 }
250 
write_vfat(Stream_t * Dir,dos_name_t * shortname,char * longname,unsigned int start,direntry_t * mainEntry)251 int write_vfat(Stream_t *Dir, dos_name_t *shortname, char *longname,
252 	       unsigned int start,
253 	       direntry_t *mainEntry)
254 {
255 	struct vfat_subentry *vse;
256 	uint8_t vse_id, num_vses;
257 	wchar_t *c;
258 	direntry_t entry;
259 	dirCache_t *cache;
260 	wchar_t unixyName[13];
261 	doscp_t *cp = GET_DOSCONVERT(Dir);
262 
263 	wchar_t wlongname[MAX_VNAMELEN+1];
264 	size_t wlen;
265 
266 	if(longname) {
267 #ifdef DEBUG
268 		printf("Entering write_vfat with longname=\"%s\", start=%d.\n",
269 		       longname,start);
270 #endif
271 		entry.Dir = Dir;
272 		vse = (struct vfat_subentry *) &entry.dir;
273 		/* Fill in invariant part of vse */
274 		vse->attribute = 0x0f;
275 		vse->hash1 = vse->sector_l = vse->sector_u = 0;
276 		vse->sum = sum_shortname(shortname);
277 #ifdef DEBUG
278 		printf("Wrote checksum=%d for shortname %s.%s\n",
279 		       vse->sum,shortname->base,shortname->ext);
280 #endif
281 
282 		wlen = native_to_wchar(longname, wlongname, MAX_VNAMELEN+1,
283 				       0, 0);
284 		num_vses = (uint8_t)((wlen + VSE_NAMELEN - 1)/VSE_NAMELEN);
285 		for (vse_id = num_vses; vse_id; --vse_id) {
286 			int end = 0;
287 
288 			c = wlongname + (vse_id - 1) * VSE_NAMELEN;
289 
290 			c += unicode_write(c, vse->text1, VSE1SIZE, &end);
291 			c += unicode_write(c, vse->text2, VSE2SIZE, &end);
292 			c += unicode_write(c, vse->text3, VSE3SIZE, &end);
293 
294 			vse->id = (vse_id == num_vses) ? (vse_id | VSE_LAST) : vse_id;
295 #ifdef DEBUG
296 			printf("Writing longname=(%s), VSE %d (%13s) at %d, end = %d.\n",
297 			       longname, vse_id, longname + (vse_id-1) * VSE_NAMELEN,
298 			       start + num_vses - vse_id, start + num_vses);
299 #endif
300 
301 			entry.entry = start + num_vses - vse_id;
302 			low_level_dir_write(&entry);
303 		}
304 	} else {
305 		num_vses = 0;
306 		wlongname[0]='\0';
307 	}
308 	cache = allocDirCache(Dir, start + num_vses + 1);
309 	if(!cache) {
310 		fprintf(stderr, "Out of memory error\n");
311 		exit(1);
312 	}
313 	unix_name(cp, shortname->base, shortname->ext, 0, unixyName);
314 	addUsedEntry(cache, start, start + num_vses + 1, wlongname, unixyName,
315 		     &mainEntry->dir);
316 	low_level_dir_write(mainEntry);
317 	return start + num_vses;
318 }
319 
dir_write(direntry_t * entry)320 void dir_write(direntry_t *entry)
321 {
322 	dirCacheEntry_t *dce;
323 	dirCache_t *cache;
324 
325 	if(entry->entry == -3) {
326 		fprintf(stderr, "Attempt to write root directory pointer\n");
327 		exit(1);
328 	}
329 
330 	cache = allocDirCache(entry->Dir, entry->entry + 1);
331 	if(!cache) {
332 		fprintf(stderr, "Out of memory error in dir_write\n");
333 		exit(1);
334 	}
335 	dce = cache->entries[entry->entry];
336 	if(dce) {
337 		if(entry->dir.name[0] == DELMARK) {
338 			addFreeEntry(cache, dce->beginSlot, dce->endSlot);
339 		} else {
340 			dce->dir = entry->dir;
341 		}
342 	}
343 	low_level_dir_write(entry);
344 }
345 
346 
347 /*
348  * The following function translates a series of vfat_subentries into
349  * data suitable for a dircache entry
350  */
parse_vses(direntry_t * entry,struct vfat_state * v)351 static __inline__ void parse_vses(direntry_t *entry,
352 				  struct vfat_state *v)
353 {
354 	struct vfat_subentry *vse;
355 	unsigned char id, last_flag;
356 	wchar_t *c;
357 
358 	vse = (struct vfat_subentry *) &entry->dir;
359 
360 	id = vse->id & VSE_MASK;
361 	last_flag = (vse->id & VSE_LAST);
362 	if (id > MAX_VFAT_SUBENTRIES) {
363 		fprintf(stderr, "parse_vses: invalid VSE ID %d at %d.\n",
364 			id, entry->entry);
365 		return;
366 	}
367 
368 /* 950819: This code enforced finding the VSEs in order.  Well, Win95
369  * likes to write them in *reverse* order for some bizarre reason!  So
370  * we pretty much have to tolerate them coming in any possible order.
371  * So skip this check, we'll do without it (What does this do, Alain?).
372  *
373  * 950820: Totally rearranged code to tolerate any order but to warn if
374  * they are not in reverse order like Win95 uses.
375  *
376  * 950909: Tolerate any order. We recognize new chains by mismatching
377  * checksums. In the event that the checksums match, new entries silently
378  * overwrite old entries of the same id. This should accept all valid
379  * entries, but may fail to reject invalid entries in some rare cases.
380  */
381 
382 	/* bad checksum, begin new chain */
383 	if(v->sum != vse->sum) {
384 		clear_vfat(v);
385 		v->sum = vse->sum;
386 	}
387 
388 #ifdef DEBUG
389 	if(v->status & (1 << (id-1)))
390 		fprintf(stderr,
391 			"parse_vses: duplicate VSE %d\n", vse->id);
392 #endif
393 
394 	v->status |= 1 << (id-1);
395 	if(last_flag)
396 		v->subentries = id;
397 
398 #ifdef DEBUG
399 	if (id > v->subentries)
400 		/* simple test to detect entries preceding
401 		 * the "last" entry (really the first) */
402 		fprintf(stderr,
403 			"parse_vses: new VSE %d sans LAST flag\n",
404 			vse->id);
405 #endif
406 
407 	c = &(v->name[VSE_NAMELEN * (id-1)]);
408 	c += unicode_read(vse->text1, c, VSE1SIZE);
409 	c += unicode_read(vse->text2, c, VSE2SIZE);
410 	c += unicode_read(vse->text3, c, VSE3SIZE);
411 #ifdef DEBUG
412 	printf("Read VSE %d at %d, subentries=%d, = (%13ls).\n",
413 	       id,entry->entry,v->subentries,&(v->name[VSE_NAMELEN * (id-1)]));
414 #endif
415 	if (last_flag)
416 		*c = '\0';	/* Null terminate long name */
417 }
418 
419 /**
420  * Read one complete entry from directory (main name plus any VSEs
421  * belonging to it)
422  */
vfat_lookup_loop_common(doscp_t * cp,direntry_t * direntry,dirCache_t * cache,int lookForFreeSpace,int * io_error)423 static dirCacheEntry_t *vfat_lookup_loop_common(doscp_t *cp,
424 						direntry_t *direntry,
425 						dirCache_t *cache,
426 						int lookForFreeSpace,
427 						int *io_error)
428 {
429 	wchar_t newfile[13];
430 	unsigned int initpos = direntry->entry + 1;
431 	struct vfat_state vfat;
432 	wchar_t *longname;
433 	int error;
434 	int endmarkSeen = 0;
435 
436 	/* not yet cached */
437 	*io_error = 0;
438 	clear_vfat(&vfat);
439 	while(1) {
440 		++direntry->entry;
441 		if(!dir_read(direntry, &error)){
442 			if(error) {
443 			    *io_error = error;
444 			    return NULL;
445 			}
446 			addFreeEndEntry(cache, initpos, direntry->entry,
447 					endmarkSeen);
448 			return addEndEntry(cache, direntry->entry);
449 		}
450 
451 		if (endmarkSeen || direntry->dir.name[0] == ENDMARK){
452 				/* the end of the directory */
453 			if(lookForFreeSpace) {
454 				endmarkSeen = 1;
455 				continue;
456 			}
457 			return addEndEntry(cache, direntry->entry);
458 		}
459 		if(direntry->dir.name[0] != DELMARK &&
460 		   direntry->dir.attr == 0x0f)
461 			parse_vses(direntry, &vfat);
462 		else
463 			/* the main entry */
464 			break;
465 	}
466 
467 	/* If we get here, it's a short name FAT entry, maybe erased.
468 	 * thus we should make sure that the vfat structure will be
469 	 * cleared before the next loop run */
470 
471 	/* deleted file */
472 	if (direntry->dir.name[0] == DELMARK) {
473 		return addFreeEntry(cache, initpos,
474 				    direntry->entry + 1);
475 	}
476 
477 	check_vfat(&vfat, &direntry->dir);
478 	if(!vfat.present)
479 		vfat.subentries = 0;
480 
481 	/* mark space between last entry and this one as free */
482 	addFreeEntry(cache, initpos,
483 		     direntry->entry - vfat.subentries);
484 
485 	if (direntry->dir.attr & 0x8){
486 		/* Read entry as a label */
487 		wchar_t *ptr = newfile;
488 		if (direntry->dir.name[0] == '\x05') {
489 			ptr += dos_to_wchar(cp, "\xE5", ptr, 1);
490 			ptr += dos_to_wchar(cp, direntry->dir.name+1, ptr, 7);
491 		} else {
492 			ptr += dos_to_wchar(cp, direntry->dir.name, ptr, 8);
493 		}
494 		ptr += dos_to_wchar(cp, direntry->dir.ext, ptr, 3);
495 		*ptr = '\0';
496 	} else
497 		unix_name(cp,
498 			  direntry->dir.name,
499 			  direntry->dir.ext,
500 			  direntry->dir.Case,
501 			  newfile);
502 
503 	if(vfat.present)
504 		longname = vfat.name;
505 	else
506 		longname = 0;
507 
508 	return addUsedEntry(cache, direntry->entry - vfat.subentries,
509 			    direntry->entry + 1, longname,
510 			    newfile, &direntry->dir);
511 }
512 
vfat_lookup_loop_for_read(doscp_t * cp,direntry_t * direntry,dirCache_t * cache,int * io_error)513 static __inline__ dirCacheEntry_t *vfat_lookup_loop_for_read(doscp_t *cp,
514 							     direntry_t *direntry,
515 							     dirCache_t *cache,
516 							     int *io_error)
517 {
518 	int initpos = direntry->entry + 1;
519 	dirCacheEntry_t *dce;
520 
521 	*io_error = 0;
522 	dce = cache->entries[initpos];
523 	if(dce) {
524 		direntry->entry = dce->endSlot - 1;
525 		return dce;
526 	} else {
527 		return vfat_lookup_loop_common(cp,
528 					       direntry, cache, 0, io_error);
529 	}
530 }
531 
532 
533 typedef enum result_t {
534 	RES_NOMATCH,
535 	RES_MATCH,
536 	RES_END,
537 	RES_ERROR
538 } result_t;
539 
540 
541 /*
542  * 0 does not match
543  * 1 matches
544  * 2 end
545  */
checkNameForMatch(struct direntry_t * direntry,dirCacheEntry_t * dce,const wchar_t * filename,int length,int flags)546 static result_t checkNameForMatch(struct direntry_t *direntry,
547 				  dirCacheEntry_t *dce,
548 				  const wchar_t *filename,
549 				  int length,
550 				  int flags)
551 {
552 	switch(dce->type) {
553 		case DCET_FREE:
554 			return RES_NOMATCH;
555 		case DCET_END:
556 			return RES_END;
557 		case DCET_USED:
558 			break;
559 #ifdef DEBUG
560 		default:
561 			fprintf(stderr, "Unexpected entry type %d\n",
562 				dce->type);
563 			return RES_ERROR;
564 #endif
565 	}
566 
567 	direntry->dir = dce->dir;
568 
569 	/* make sure the entry is of an accepted type */
570 	if((direntry->dir.attr & 0x8) && !(flags & ACCEPT_LABEL))
571 		return RES_NOMATCH;
572 
573 
574 	/*---------- multiple files ----------*/
575 	if(!((flags & MATCH_ANY) ||
576 	     (dce->longName &&
577 	      match(dce->longName, filename, direntry->name, 0, length)) ||
578 	     match(dce->shortName, filename, direntry->name, 1, length))) {
579 
580 		return RES_NOMATCH;
581 	}
582 
583 	/* entry of non-requested type, has to come after name
584 	 * checking because of clash handling */
585 	if(IS_DIR(direntry) && !(flags & ACCEPT_DIR)) {
586 		if(!(flags & (ACCEPT_LABEL|MATCH_ANY|NO_MSG))) {
587 			char tmp[4*13+1];
588 			WCHAR_TO_NATIVE(dce->shortName,tmp,13);
589 			fprintf(stderr, "Skipping \"%s\", is a directory\n",
590 				tmp);
591 		}
592 		return RES_NOMATCH;
593 	}
594 
595 	if(!(direntry->dir.attr & (ATTR_LABEL | ATTR_DIR)) &&
596 	   !(flags & ACCEPT_PLAIN)) {
597 		if(!(flags & (ACCEPT_LABEL|MATCH_ANY|NO_MSG))) {
598 			char tmp[4*13+1];
599 			WCHAR_TO_NATIVE(dce->shortName,tmp,13);
600 			fprintf(stderr,
601 				"Skipping \"%s\", is not a directory\n",
602 				tmp);
603 		}
604 		return RES_NOMATCH;
605 	}
606 
607 	return RES_MATCH;
608 }
609 
610 
vfat_lookup_zt(direntry_t * direntry,const char * filename,int flags,char * shortname,size_t shortname_size,char * longname,size_t longname_size)611 int vfat_lookup_zt(direntry_t *direntry, const char *filename,
612 		   int flags, char *shortname, size_t shortname_size,
613 		   char *longname, size_t longname_size) {
614 	return vfat_lookup(direntry, filename, strlen(filename),
615 			   flags, shortname, shortname_size,
616 			   longname, longname_size);
617 }
618 
619 /*
620  * vfat_lookup looks for filenames in directory dir.
621  * if a name if found, it is returned in outname
622  * if applicable, the file is opened and its stream is returned in File
623  */
624 
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 int vfat_lookup(direntry_t *direntry, const char *filename,
626 		size_t length,
627 		int flags, char *shortname, size_t shortname_size,
628 		char *longname, size_t longname_size)
629 {
630 	dirCacheEntry_t *dce;
631 	result_t result;
632 	dirCache_t *cache;
633 	int io_error;
634 	wchar_t wfilename[MAX_VNAMELEN+1];
635 	doscp_t *cp = GET_DOSCONVERT(direntry->Dir);
636 
637 	if(filename != NULL)
638 		length = native_to_wchar(filename, wfilename, MAX_VNAMELEN,
639 					 filename+length, 0);
640 	else
641 		length = 0;
642 
643 	if (direntry->entry == -2)
644 		return -1;
645 
646 	cache = allocDirCache(direntry->Dir, direntry->entry+1);
647 	if(!cache) {
648 		fprintf(stderr, "Out of memory error in vfat_lookup [0]\n");
649 		exit(1);
650 	}
651 
652 	do {
653 		dce = vfat_lookup_loop_for_read(cp, direntry, cache, &io_error);
654 		if(!dce) {
655 			if (io_error)
656 				return -2;
657 			fprintf(stderr, "Out of memory error in vfat_lookup\n");
658 			exit(1);
659 		}
660 		result = checkNameForMatch(direntry, dce,
661 					   wfilename,
662 					   (int) length, flags);
663 	} while(result == RES_NOMATCH);
664 
665 	if(result == RES_MATCH){
666 		if(longname){
667 			if(dce->longName)
668 				wchar_to_native(dce->longName, longname,
669 						MAX_VNAMELEN, longname_size);
670 			else
671 				*longname ='\0';
672 		}
673 		if(shortname)
674 			wchar_to_native(dce->shortName, shortname,
675 					12, shortname_size);
676 		direntry->beginSlot = dce->beginSlot;
677 		direntry->endSlot = dce->endSlot-1;
678 		return 0; /* file found */
679 	} else {
680 		direntry->entry = -2;
681 		return -1; /* no file found */
682 	}
683 }
684 
vfat_lookup_loop_for_insert(doscp_t * cp,direntry_t * direntry,unsigned int initpos,dirCache_t * cache)685 static __inline__ dirCacheEntry_t *vfat_lookup_loop_for_insert(doscp_t *cp,
686 							       direntry_t *direntry,
687 							       unsigned int initpos,
688 							       dirCache_t *cache)
689 {
690 	dirCacheEntry_t *dce;
691 	int io_error;
692 
693 	dce = cache->entries[initpos];
694 	if(dce && dce->type != DCET_END) {
695 		return dce;
696 	} else {
697 		direntry->entry = initpos - 1;
698 		dce = vfat_lookup_loop_common(cp,
699 					      direntry, cache, 1, &io_error);
700 		if(!dce) {
701 			if (io_error) {
702 				return NULL;
703 			}
704 			fprintf(stderr,
705 				"Out of memory error in vfat_lookup_loop\n");
706 			exit(1);
707 		}
708 		return cache->entries[initpos];
709 	}
710 }
711 
accountFreeSlots(struct scan_state * ssp,dirCacheEntry_t * dce)712 static void accountFreeSlots(struct scan_state *ssp, dirCacheEntry_t *dce)
713 {
714 	if(ssp->got_slots)
715 		return;
716 
717 	if(ssp->free_end != dce->beginSlot) {
718 		ssp->free_start = dce->beginSlot;
719 	}
720 	ssp->free_end = dce->endSlot;
721 
722 	if(ssp->free_end - ssp->free_start >= ssp->size_needed) {
723 		ssp->got_slots = 1;
724 		ssp->slot = ssp->free_start + ssp->size_needed - 1;
725 	}
726 }
727 
clear_scan(wchar_t * longname,int use_longname,struct scan_state * s)728 static void clear_scan(wchar_t *longname, int use_longname,
729 		       struct scan_state *s)
730 {
731 	s->shortmatch = s->longmatch = s->slot = -1;
732 	s->free_end = s->got_slots = s->free_start = 0;
733 
734 	if (use_longname & 1)
735 		s->size_needed = (unsigned)
736 			(1 + (wcslen(longname) + VSE_NAMELEN - 1)/VSE_NAMELEN);
737 	else
738                 s->size_needed = 1;
739 }
740 
741 /* lookup_for_insert replaces the old scandir function.  It directly
742  * calls into vfat_lookup_loop, thus eliminating the overhead of the
743  * normal vfat_lookup
744  */
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 int lookupForInsert(Stream_t *Dir,
746 		    struct direntry_t *direntry,
747 		    dos_name_t *dosname,
748 		    char *longname,
749 		    struct scan_state *ssp,
750 		    int ignore_entry,
751 		    int source_entry,
752 		    int pessimisticShortRename,
753 		    int use_longname)
754 {
755 	direntry_t entry;
756 	int ignore_match;
757 	dirCacheEntry_t *dce;
758 	dirCache_t *cache;
759 	unsigned int pos; /* position _before_ the next answered entry */
760 	wchar_t shortName[13];
761 	wchar_t wlongname[MAX_VNAMELEN+1];
762 	doscp_t *cp = GET_DOSCONVERT(Dir);
763 
764 	native_to_wchar(longname, wlongname, MAX_VNAMELEN+1, 0, 0);
765 	clear_scan(wlongname, use_longname, ssp);
766 
767 	ignore_match = (ignore_entry == -2 );
768 
769 	initializeDirentry(&entry, Dir);
770 	ssp->match_free = 0;
771 
772 	/* hash bitmap of already encountered names.  Speeds up batch appends
773 	 * to huge directories, because in the best case, we only need to scan
774 	 * the new entries rather than the whole directory */
775 	cache = allocDirCache(Dir, 1);
776 	if(!cache) {
777 		fprintf(stderr, "Out of memory error in lookupForInsert\n");
778 		exit(1);
779 	}
780 
781 	if(!ignore_match)
782 		unix_name(cp, dosname->base, dosname->ext, 0, shortName);
783 
784 	pos = cache->nrHashed;
785 	if(source_entry >= 0 ||
786 	   (pos && isHashed(cache, wlongname))) {
787 		pos = 0;
788 	} else if(pos && !ignore_match && isHashed(cache, shortName)) {
789 		if(pessimisticShortRename) {
790 			ssp->shortmatch = -2;
791 			return 1;
792 		}
793 		pos = 0;
794 	} else if(growDirCache(cache, pos) < 0) {
795 		fprintf(stderr, "Out of memory error in vfat_looup [0]\n");
796 		exit(1);
797 	}
798 	do {
799 		dce = vfat_lookup_loop_for_insert(cp, &entry, pos, cache);
800 		switch(dce->type) {
801 			case DCET_FREE:
802 				accountFreeSlots(ssp, dce);
803 				break;
804 			case DCET_USED:
805 				if(!(dce->dir.attr & 0x8) &&
806 				   (signed int)dce->endSlot-1 == source_entry)
807 				   accountFreeSlots(ssp, dce);
808 
809 				/* labels never match, neither does the
810 				 * ignored entry */
811 				if( (dce->dir.attr & 0x8) ||
812 				    ((signed int)dce->endSlot-1==ignore_entry))
813 					break;
814 
815 				/* check long name */
816 				if((dce->longName &&
817 				    !wcscasecmp(dce->longName, wlongname)) ||
818 				   (dce->shortName &&
819 				    !wcscasecmp(dce->shortName, wlongname))) {
820 					ssp->longmatch = dce->endSlot - 1;
821 					/* long match is a reason for
822 					 * immediate stop */
823 					direntry->beginSlot = dce->beginSlot;
824 					direntry->endSlot = dce->endSlot - 1;
825 					return 1;
826 				}
827 
828 				/* Long name or not, always check for
829 				 * short name match */
830 				if (!ignore_match &&
831 				    !wcscasecmp(shortName, dce->shortName))
832 					ssp->shortmatch = dce->endSlot - 1;
833 				break;
834 			case DCET_END:
835 				break;
836 		}
837 		pos = dce->endSlot;
838 	} while(dce->type != DCET_END);
839 	if (ssp->shortmatch > -1)
840 		return 1;
841 	ssp->max_entry = dce->beginSlot;
842 	if (ssp->got_slots)
843 		return 6;	/* Success */
844 
845 	/* Need more room.  Can we grow the directory? */
846 	if(!isRootDir(Dir))
847 		return 5;	/* OK, try to grow the directory */
848 
849 	fprintf(stderr, "No directory slots\n");
850 	return -1;
851 }
852 #pragma GCC diagnostic pop
853 
854 
855 /* End vfat.c */
856