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