blob: 01331bca0b33bdb13d687ac69be2f048f425b886 [file] [log] [blame]
Alistair Delvabeaee832021-02-24 11:27:23 -08001/* 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
34const char *short_illegals=";+=[]',\"*\\<>/?:|";
35const char *long_illegals = "\"*\\<>/?:|\005";
36
37/* Automatically derive a new name */
38static 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;
Yi Kong39bbd962022-01-09 19:41:38 +080046
Alistair Delvabeaee832021-02-24 11:27:23 -080047#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') {
Yi Kong39bbd962022-01-09 19:41:38 +080066 seqnum = seqnum * 10 + (uint8_t)(name[dotpos] - '0');
Alistair Delvabeaee832021-02-24 11:27:23 -080067 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
110void autorename_short(dos_name_t *name, int bump)
111{
112 autorename(name->base, '~', ' ', short_illegals, 8, bump);
113}
114
115void autorename_long(char *name, int bump)
116{
117 autorename(name, '-', '\0', long_illegals, 255, bump);
118}
119
120
121static __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
142void 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).
Yi Kong39bbd962022-01-09 19:41:38 +0800158 *
Alistair Delvabeaee832021-02-24 11:27:23 -0800159 * 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 */
166static __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)
Yi Kong39bbd962022-01-09 19:41:38 +0800174 + (uint8_t) *name;
Alistair Delvabeaee832021-02-24 11:27:23 -0800175 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 */
184static __inline__ void check_vfat(struct vfat_state *v, struct directory *dir)
185{
Yi Kong39bbd962022-01-09 19:41:38 +0800186 dos_name_t dn;
Alistair Delvabeaee832021-02-24 11:27:23 -0800187
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;
Yi Kong39bbd962022-01-09 19:41:38 +0800200
Alistair Delvabeaee832021-02-24 11:27:23 -0800201 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
Yi Kong39bbd962022-01-09 19:41:38 +0800209#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 */
218int clear_vses(Stream_t *Dir, int entrySlot, unsigned int last)
Alistair Delvabeaee832021-02-24 11:27:23 -0800219{
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
Yi Kong39bbd962022-01-09 19:41:38 +0800251int write_vfat(Stream_t *Dir, dos_name_t *shortname, char *longname,
252 unsigned int start,
Alistair Delvabeaee832021-02-24 11:27:23 -0800253 direntry_t *mainEntry)
254{
255 struct vfat_subentry *vse;
Yi Kong39bbd962022-01-09 19:41:38 +0800256 uint8_t vse_id, num_vses;
Alistair Delvabeaee832021-02-24 11:27:23 -0800257 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];
Yi Kong39bbd962022-01-09 19:41:38 +0800264 size_t wlen;
Alistair Delvabeaee832021-02-24 11:27:23 -0800265
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);
Yi Kong39bbd962022-01-09 19:41:38 +0800284 num_vses = (uint8_t)((wlen + VSE_NAMELEN - 1)/VSE_NAMELEN);
Alistair Delvabeaee832021-02-24 11:27:23 -0800285 for (vse_id = num_vses; vse_id; --vse_id) {
286 int end = 0;
Yi Kong39bbd962022-01-09 19:41:38 +0800287
Alistair Delvabeaee832021-02-24 11:27:23 -0800288 c = wlongname + (vse_id - 1) * VSE_NAMELEN;
Yi Kong39bbd962022-01-09 19:41:38 +0800289
Alistair Delvabeaee832021-02-24 11:27:23 -0800290 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
Yi Kong39bbd962022-01-09 19:41:38 +0800300
Alistair Delvabeaee832021-02-24 11:27:23 -0800301 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
320void 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 */
Yi Kong39bbd962022-01-09 19:41:38 +0800351static __inline__ void parse_vses(direntry_t *entry,
Alistair Delvabeaee832021-02-24 11:27:23 -0800352 struct vfat_state *v)
353{
354 struct vfat_subentry *vse;
355 unsigned char id, last_flag;
356 wchar_t *c;
Yi Kong39bbd962022-01-09 19:41:38 +0800357
Alistair Delvabeaee832021-02-24 11:27:23 -0800358 vse = (struct vfat_subentry *) &entry->dir;
Yi Kong39bbd962022-01-09 19:41:38 +0800359
Alistair Delvabeaee832021-02-24 11:27:23 -0800360 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 }
Yi Kong39bbd962022-01-09 19:41:38 +0800367
Alistair Delvabeaee832021-02-24 11:27:23 -0800368/* 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 }
Yi Kong39bbd962022-01-09 19:41:38 +0800387
Alistair Delvabeaee832021-02-24 11:27:23 -0800388#ifdef DEBUG
389 if(v->status & (1 << (id-1)))
390 fprintf(stderr,
391 "parse_vses: duplicate VSE %d\n", vse->id);
392#endif
Yi Kong39bbd962022-01-09 19:41:38 +0800393
Alistair Delvabeaee832021-02-24 11:27:23 -0800394 v->status |= 1 << (id-1);
395 if(last_flag)
396 v->subentries = id;
Yi Kong39bbd962022-01-09 19:41:38 +0800397
Alistair Delvabeaee832021-02-24 11:27:23 -0800398#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)]));
Yi Kong39bbd962022-01-09 19:41:38 +0800414#endif
Alistair Delvabeaee832021-02-24 11:27:23 -0800415 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 */
423static 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];
Yi Kong39bbd962022-01-09 19:41:38 +0800430 unsigned int initpos = direntry->entry + 1;
Alistair Delvabeaee832021-02-24 11:27:23 -0800431 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 }
Yi Kong39bbd962022-01-09 19:41:38 +0800450
Alistair Delvabeaee832021-02-24 11:27:23 -0800451 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 }
Yi Kong39bbd962022-01-09 19:41:38 +0800466
Alistair Delvabeaee832021-02-24 11:27:23 -0800467 /* 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 */
Yi Kong39bbd962022-01-09 19:41:38 +0800470
Alistair Delvabeaee832021-02-24 11:27:23 -0800471 /* 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
513static __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
533typedef 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 */
546static 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
Yi Kong39bbd962022-01-09 19:41:38 +0800611int 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
Alistair Delvabeaee832021-02-24 11:27:23 -0800619/*
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
Yi Kong39bbd962022-01-09 19:41:38 +0800625int vfat_lookup(direntry_t *direntry, const char *filename,
626 size_t length,
Alistair Delvabeaee832021-02-24 11:27:23 -0800627 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
Alistair Delvabeaee832021-02-24 11:27:23 -0800637 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,
Yi Kong39bbd962022-01-09 19:41:38 +0800662 (int) length, flags);
Alistair Delvabeaee832021-02-24 11:27:23 -0800663 } 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
685static __inline__ dirCacheEntry_t *vfat_lookup_loop_for_insert(doscp_t *cp,
686 direntry_t *direntry,
Yi Kong39bbd962022-01-09 19:41:38 +0800687 unsigned int initpos,
Alistair Delvabeaee832021-02-24 11:27:23 -0800688 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
712static 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
728static 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)
Yi Kong39bbd962022-01-09 19:41:38 +0800735 s->size_needed = (unsigned)
736 (1 + (wcslen(longname) + VSE_NAMELEN - 1)/VSE_NAMELEN);
Alistair Delvabeaee832021-02-24 11:27:23 -0800737 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 */
745int 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;
Yi Kong39bbd962022-01-09 19:41:38 +0800759 unsigned int pos; /* position _before_ the next answered entry */
Alistair Delvabeaee832021-02-24 11:27:23 -0800760 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? */
Yi Kong39bbd962022-01-09 19:41:38 +0800846 if(!isRootDir(Dir))
Alistair Delvabeaee832021-02-24 11:27:23 -0800847 return 5; /* OK, try to grow the directory */
848
849 fprintf(stderr, "No directory slots\n");
850 return -1;
851}
Yi Kong39bbd962022-01-09 19:41:38 +0800852#pragma GCC diagnostic pop
Alistair Delvabeaee832021-02-24 11:27:23 -0800853
854
855/* End vfat.c */