00001
00002
00003
00004
00005
00006 #include "sysincludes.h"
00007 #include "msdos.h"
00008 #include "mtools.h"
00009 #include "vfat.h"
00010 #include "file.h"
00011 #include "dirCache.h"
00012
00013
00014
00015 const char *short_illegals=";+=[]',\"*\\<>/?:|";
00016 const char *long_illegals = "\"*\\<>/?:|\005";
00017
00018
00019 static void autorename(char *name,
00020 char tilda, char dot, const char *illegals,
00021 int limit, int bump)
00022 {
00023 int tildapos, dotpos;
00024 unsigned int seqnum=0, maxseq=0;
00025 char tmp;
00026 char *p;
00027
00028 #ifdef DEBUG
00029 printf("In autorename for name=%s.\n", name);
00030 #endif
00031 tildapos = -1;
00032
00033 for(p=name; *p ; p++)
00034 if((*p < ' ' && *p != '\005') || strchr(illegals, *p)) {
00035 *p = '_';
00036 bump = 0;
00037 }
00038
00039 for(dotpos=0;
00040 name[dotpos] && dotpos < limit && name[dotpos] != dot ;
00041 dotpos++) {
00042 if(name[dotpos] == tilda) {
00043 tildapos = dotpos;
00044 seqnum = 0;
00045 maxseq = 1;
00046 } else if (name[dotpos] >= '0' && name[dotpos] <= '9') {
00047 seqnum = seqnum * 10 + name[dotpos] - '0';
00048 maxseq = maxseq * 10;
00049 } else
00050 tildapos = -1;
00051 }
00052 if(tildapos == -1) {
00053
00054 if(dotpos > limit - 2) {
00055 tildapos = limit - 2;
00056 dotpos = limit;
00057 } else {
00058 tildapos = dotpos;
00059 dotpos += 2;
00060 }
00061 seqnum = 1;
00062 } else {
00063 if(bump)
00064 seqnum++;
00065 if(seqnum > 999999) {
00066 seqnum = 1;
00067 tildapos = dotpos - 2;
00068
00069
00070 }
00071 if (seqnum == maxseq) {
00072 if(dotpos >= limit)
00073 tildapos--;
00074 else
00075 dotpos++;
00076 }
00077 }
00078
00079 tmp = name[dotpos];
00080 if((bump && seqnum == 1) || seqnum > 1 || mtools_numeric_tail)
00081 sprintf(name+tildapos,"%c%d",tilda, seqnum);
00082 if(dot)
00083 name[dotpos]=tmp;
00084
00085 }
00086
00087
00088 void autorename_short(char *name, int bump)
00089 {
00090 autorename(name, '~', ' ', short_illegals, 8, bump);
00091 }
00092
00093 void autorename_long(char *name, int bump)
00094 {
00095 autorename(name, '-', '\0', long_illegals, 255, bump);
00096 }
00097
00098
00099 static inline int unicode_read(struct unicode_char *in, char *out, int num)
00100 {
00101 char *end_out = out+num;
00102
00103 while(out < end_out) {
00104 if (in->uchar)
00105 *out = '_';
00106 else
00107 *out = in->lchar;
00108 ++out;
00109 ++in;
00110 }
00111 return num;
00112 }
00113
00114
00115 void clear_vfat(struct vfat_state *v)
00116 {
00117 v->subentries = 0;
00118 v->status = 0;
00119 v->present = 0;
00120 }
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139 static inline unsigned char sum_shortname(char *name)
00140 {
00141 unsigned char sum;
00142 char *end = name+11;
00143
00144 for (sum=0; name<end; ++name)
00145 sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1)
00146 + (*name ? *name : ' ');
00147 return(sum);
00148 }
00149
00150
00151
00152
00153
00154
00155
00156 static inline void check_vfat(struct vfat_state *v, struct directory *dir)
00157 {
00158 char name[12];
00159
00160 if (! v->subentries) {
00161 #ifdef DEBUG
00162 fprintf(stderr, "check_vfat: no VSEs.\n");
00163 #endif
00164 return;
00165 }
00166
00167 strncpy((char *)name, (char *)dir->name, 8);
00168 strncpy((char *)name + 8, (char *)dir->ext, 3);
00169 name[11] = '\0';
00170
00171 if (v->sum != sum_shortname(name))
00172 return;
00173
00174 if( (v->status & ((1<<v->subentries) - 1)) != (1<<v->subentries) - 1)
00175 return;
00176
00177
00178 v->name[VSE_NAMELEN * v->subentries] = 0;
00179 v->present = 1;
00180 }
00181
00182
00183 int clear_vses(Stream_t *Dir, int entrySlot, size_t last)
00184 {
00185 direntry_t entry;
00186 dirCache_t *cache;
00187 int error;
00188
00189 entry.Dir = Dir;
00190 entry.entry = entrySlot;
00191
00192
00193 cache = allocDirCache(Dir, last);
00194 if(!cache) {
00195 fprintf(stderr, "Out of memory error in clear_vses\n");
00196 exit(1);
00197 }
00198 addFreeEntry(cache, entry.entry, last);
00199 for (; entry.entry < last; ++entry.entry) {
00200 #ifdef DEBUG
00201 fprintf(stderr,"Clearing entry %d.\n", entry.entry);
00202 #endif
00203 dir_read(&entry, &error);
00204 if(error)
00205 return error;
00206 if(!entry.dir.name[0] || entry.dir.name[0] == DELMARK)
00207 break;
00208 entry.dir.name[0] = DELMARK;
00209 if (entry.dir.attr == 0xf)
00210 entry.dir.attr = '\0';
00211 low_level_dir_write(&entry);
00212 }
00213 return 0;
00214 }
00215
00216 int write_vfat(Stream_t *Dir, char *shortname, char *longname, int start,
00217 direntry_t *mainEntry)
00218 {
00219 struct vfat_subentry *vse;
00220 int vse_id, num_vses;
00221 char *c;
00222 direntry_t entry;
00223 dirCache_t *cache;
00224 char unixyName[13];
00225
00226 if(longname) {
00227 #ifdef DEBUG
00228 printf("Entering write_vfat with longname=\"%s\", start=%d.\n",
00229 longname,start);
00230 #endif
00231 entry.Dir = Dir;
00232 vse = (struct vfat_subentry *) &entry.dir;
00233
00234 vse->attribute = 0x0f;
00235 vse->hash1 = vse->sector_l = vse->sector_u = 0;
00236 vse->sum = sum_shortname(shortname);
00237 #ifdef DEBUG
00238 printf("Wrote checksum=%d for shortname %s.\n",
00239 vse->sum,shortname);
00240 #endif
00241 num_vses = strlen(longname)/VSE_NAMELEN + 1;
00242 for (vse_id = num_vses; vse_id; --vse_id) {
00243 int end = 0;
00244
00245 c = longname + (vse_id - 1) * VSE_NAMELEN;
00246
00247 c += unicode_write(c, vse->text1, VSE1SIZE, &end);
00248 c += unicode_write(c, vse->text2, VSE2SIZE, &end);
00249 c += unicode_write(c, vse->text3, VSE3SIZE, &end);
00250
00251 vse->id = (vse_id == num_vses) ? (vse_id | VSE_LAST) : vse_id;
00252 #ifdef DEBUG
00253 printf("Writing longname=(%s), VSE %d (%13s) at %d, end = %d.\n",
00254 longname, vse_id, longname + (vse_id-1) * VSE_NAMELEN,
00255 start + num_vses - vse_id, start + num_vses);
00256 #endif
00257
00258 entry.entry = start + num_vses - vse_id;
00259 low_level_dir_write(&entry);
00260 }
00261 } else
00262 num_vses = 0;
00263 cache = allocDirCache(Dir, start + num_vses + 1);
00264 if(!cache) {
00265 fprintf(stderr, "Out of memory error\n");
00266 exit(1);
00267 }
00268 unix_name(shortname, shortname+8, 0, unixyName);
00269 addUsedEntry(cache, start, start + num_vses + 1, longname, unixyName,
00270 &mainEntry->dir);
00271 low_level_dir_write(mainEntry);
00272 return start + num_vses;
00273 }
00274
00275 void dir_write(direntry_t *entry)
00276 {
00277 dirCacheEntry_t *dce;
00278 dirCache_t *cache;
00279
00280 if(entry->entry == -3) {
00281 fprintf(stderr, "Attempt to write root directory pointer\n");
00282 exit(1);
00283 }
00284
00285 cache = allocDirCache(entry->Dir, entry->entry + 1);
00286 if(!cache) {
00287 fprintf(stderr, "Out of memory error in dir_write\n");
00288 exit(1);
00289 }
00290 dce = cache->entries[entry->entry];
00291 if(dce) {
00292 if(entry->dir.name[0] == DELMARK) {
00293 addFreeEntry(cache, dce->beginSlot, dce->endSlot);
00294 } else {
00295 dce->dir = entry->dir;
00296 }
00297 }
00298 low_level_dir_write(entry);
00299 }
00300
00301
00302
00303
00304
00305
00306 static inline void parse_vses(direntry_t *entry,
00307 struct vfat_state *v)
00308 {
00309 struct vfat_subentry *vse;
00310 unsigned char id, last_flag;
00311 char *c;
00312
00313 vse = (struct vfat_subentry *) &entry->dir;
00314
00315 id = vse->id & VSE_MASK;
00316 last_flag = (vse->id & VSE_LAST);
00317 if (id > MAX_VFAT_SUBENTRIES) {
00318 fprintf(stderr, "parse_vses: invalid VSE ID %d at %d.\n",
00319 id, entry->entry);
00320 return;
00321 }
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338 if(v->sum != vse->sum) {
00339 clear_vfat(v);
00340 v->sum = vse->sum;
00341 }
00342
00343 #ifdef DEBUG
00344 if(v->status & (1 << (id-1)))
00345 fprintf(stderr,
00346 "parse_vses: duplicate VSE %d\n", vse->id);
00347 #endif
00348
00349 v->status |= 1 << (id-1);
00350 if(last_flag)
00351 v->subentries = id;
00352
00353 #ifdef DEBUG
00354 if (id > v->subentries)
00355
00356
00357 fprintf(stderr,
00358 "parse_vses: new VSE %d sans LAST flag\n",
00359 vse->id);
00360 #endif
00361
00362 c = &(v->name[VSE_NAMELEN * (id-1)]);
00363 c += unicode_read(vse->text1, c, VSE1SIZE);
00364 c += unicode_read(vse->text2, c, VSE2SIZE);
00365 c += unicode_read(vse->text3, c, VSE3SIZE);
00366 #ifdef DEBUG
00367 printf("Read VSE %d at %d, subentries=%d, = (%13s).\n",
00368 id,entry->entry,v->subentries,&(v->name[VSE_NAMELEN * (id-1)]));
00369 #endif
00370 if (last_flag)
00371 *c = '\0';
00372 }
00373
00374
00375 static dirCacheEntry_t *vfat_lookup_loop_common(direntry_t *direntry,
00376 dirCache_t *cache,
00377 int lookForFreeSpace,
00378 int *io_error)
00379 {
00380 char newfile[13];
00381 int initpos = direntry->entry + 1;
00382 struct vfat_state vfat;
00383 char *longname;
00384 int error;
00385
00386
00387 *io_error = 0;
00388 clear_vfat(&vfat);
00389 while(1) {
00390 ++direntry->entry;
00391 if(!dir_read(direntry, &error)){
00392 if(error) {
00393 *io_error = error;
00394 return NULL;
00395 }
00396 addFreeEntry(cache, initpos, direntry->entry);
00397 return addEndEntry(cache, direntry->entry);
00398 }
00399
00400 if (direntry->dir.name[0] == '\0'){
00401
00402 if(lookForFreeSpace)
00403 continue;
00404 return addEndEntry(cache, direntry->entry);
00405 }
00406 if(direntry->dir.name[0] != DELMARK &&
00407 direntry->dir.attr == 0x0f)
00408 parse_vses(direntry, &vfat);
00409 else
00410
00411 break;
00412 }
00413
00414
00415
00416
00417
00418
00419 if (direntry->dir.name[0] == DELMARK) {
00420 return addFreeEntry(cache, initpos,
00421 direntry->entry + 1);
00422 }
00423
00424 check_vfat(&vfat, &direntry->dir);
00425 if(!vfat.present)
00426 vfat.subentries = 0;
00427
00428
00429 addFreeEntry(cache, initpos,
00430 direntry->entry - vfat.subentries);
00431
00432 if (direntry->dir.attr & 0x8){
00433 strncpy(newfile, direntry->dir.name,8);
00434 newfile[8]='\0';
00435 strncat(newfile, direntry->dir.ext,3);
00436 newfile[11]='\0';
00437 } else
00438 unix_name(direntry->dir.name,
00439 direntry->dir.ext,
00440 direntry->dir.Case,
00441 newfile);
00442
00443 if(vfat.present)
00444 longname = vfat.name;
00445 else
00446 longname = 0;
00447
00448 return addUsedEntry(cache, direntry->entry - vfat.subentries,
00449 direntry->entry + 1, longname,
00450 newfile, &direntry->dir);
00451 }
00452
00453 static inline dirCacheEntry_t *vfat_lookup_loop_for_read(direntry_t *direntry,
00454 dirCache_t *cache,
00455 int *io_error)
00456 {
00457 int initpos = direntry->entry + 1;
00458 dirCacheEntry_t *dce;
00459
00460 *io_error = 0;
00461 dce = cache->entries[initpos];
00462 if(dce) {
00463 direntry->entry = dce->endSlot - 1;
00464 return dce;
00465 } else {
00466 return vfat_lookup_loop_common(direntry, cache, 0, io_error);
00467 }
00468 }
00469
00470
00471 typedef enum result_t {
00472 RES_NOMATCH,
00473 RES_MATCH,
00474 RES_END,
00475 RES_ERROR
00476 } result_t;
00477
00478
00479
00480
00481
00482
00483
00484 static result_t checkNameForMatch(struct direntry_t *direntry,
00485 dirCacheEntry_t *dce,
00486 const char *filename,
00487 char *longname,
00488 char *shortname,
00489 int length,
00490 int flags)
00491 {
00492 switch(dce->type) {
00493 case DCET_FREE:
00494 return RES_NOMATCH;
00495 case DCET_END:
00496 return RES_END;
00497 case DCET_USED:
00498 break;
00499 default:
00500 fprintf(stderr, "Unexpected entry type %d\n",
00501 dce->type);
00502 return RES_ERROR;
00503 }
00504
00505 direntry->dir = dce->dir;
00506
00507
00508 if((direntry->dir.attr & 0x8) && !(flags & ACCEPT_LABEL))
00509 return RES_NOMATCH;
00510
00511
00512
00513 if(!((flags & MATCH_ANY) ||
00514 (dce->longName &&
00515 match(dce->longName, filename, direntry->name, 0, length)) ||
00516 match(dce->shortName, filename, direntry->name, 1, length))) {
00517
00518 return RES_NOMATCH;
00519 }
00520
00521
00522
00523 if(IS_DIR(direntry) && !(flags & ACCEPT_DIR)) {
00524 if(!(flags & (ACCEPT_LABEL|MATCH_ANY|NO_MSG)))
00525 fprintf(stderr,
00526 "Skipping \"%s\", is a directory\n",
00527 dce->shortName);
00528 return RES_NOMATCH;
00529 }
00530
00531 if(!(direntry->dir.attr & (ATTR_LABEL | ATTR_DIR)) &&
00532 !(flags & ACCEPT_PLAIN)) {
00533 if(!(flags & (ACCEPT_LABEL|MATCH_ANY|NO_MSG)))
00534 fprintf(stderr,
00535 "Skipping \"%s\", is not a directory\n",
00536 dce->shortName);
00537 return RES_NOMATCH;
00538 }
00539
00540 return RES_MATCH;
00541 }
00542
00543
00544
00545
00546
00547
00548
00549
00550 int vfat_lookup(direntry_t *direntry, const char *filename, int length,
00551 int flags, char *shortname, char *longname)
00552 {
00553 dirCacheEntry_t *dce;
00554 result_t result;
00555 dirCache_t *cache;
00556 int io_error;
00557
00558 if(length == -1 && filename)
00559 length = strlen(filename);
00560
00561 if (direntry->entry == -2)
00562 return -1;
00563
00564 cache = allocDirCache(direntry->Dir, direntry->entry+1);
00565 if(!cache) {
00566 fprintf(stderr, "Out of memory error in vfat_lookup [0]\n");
00567 exit(1);
00568 }
00569
00570 do {
00571 dce = vfat_lookup_loop_for_read(direntry, cache, &io_error);
00572 if(!dce) {
00573 if (io_error)
00574 return -2;
00575 fprintf(stderr, "Out of memory error in vfat_lookup\n");
00576 exit(1);
00577 }
00578 result = checkNameForMatch(direntry, dce,
00579 filename,
00580 longname, shortname,
00581 length, flags);
00582 } while(result == RES_NOMATCH);
00583
00584 if(result == RES_MATCH){
00585 if(longname){
00586 if(dce->longName)
00587 strcpy(longname, dce->longName);
00588 else
00589 *longname ='\0';
00590 }
00591 if(shortname)
00592 strcpy(shortname, dce->shortName);
00593 direntry->beginSlot = dce->beginSlot;
00594 direntry->endSlot = dce->endSlot-1;
00595 return 0;
00596 } else {
00597 direntry->entry = -2;
00598 return -1;
00599 }
00600 }
00601
00602 static inline dirCacheEntry_t *vfat_lookup_loop_for_insert(direntry_t *direntry,
00603 int initpos,
00604 dirCache_t *cache)
00605 {
00606 dirCacheEntry_t *dce;
00607 int io_error;
00608
00609 dce = cache->entries[initpos];
00610 if(dce && dce->type != DCET_END) {
00611 return dce;
00612 } else {
00613 direntry->entry = initpos - 1;
00614 dce = vfat_lookup_loop_common(direntry, cache, 1, &io_error);
00615 if(!dce) {
00616 if (io_error) {
00617 return NULL;
00618 }
00619 fprintf(stderr,
00620 "Out of memory error in vfat_lookup_loop\n");
00621 exit(1);
00622 }
00623 return cache->entries[initpos];
00624 }
00625 }
00626
00627 static void accountFreeSlots(struct scan_state *ssp, dirCacheEntry_t *dce)
00628 {
00629 if(ssp->got_slots)
00630 return;
00631
00632 if(ssp->free_end != dce->beginSlot) {
00633 ssp->free_start = dce->beginSlot;
00634 }
00635 ssp->free_end = dce->endSlot;
00636
00637 if(ssp->free_end - ssp->free_start >= ssp->size_needed) {
00638 ssp->got_slots = 1;
00639 ssp->slot = ssp->free_start + ssp->size_needed - 1;
00640 }
00641 }
00642
00643
00644
00645
00646
00647 int lookupForInsert(Stream_t *Dir,
00648 char *dosname,
00649 char *longname,
00650 struct scan_state *ssp,
00651 int ignore_entry,
00652 int source_entry,
00653 int pessimisticShortRename)
00654 {
00655 direntry_t entry;
00656 int ignore_match;
00657 dirCacheEntry_t *dce;
00658 dirCache_t *cache;
00659 int pos;
00660 char shortName[13];
00661
00662 ignore_match = (ignore_entry == -2 );
00663
00664 initializeDirentry(&entry, Dir);
00665 ssp->match_free = 0;
00666
00667
00668
00669
00670 cache = allocDirCache(Dir, 1);
00671 if(!cache) {
00672 fprintf(stderr, "Out of memory error in lookupForInsert\n");
00673 exit(1);
00674 }
00675
00676 if(!ignore_match)
00677 unix_name(dosname, dosname + 8, 0, shortName);
00678
00679 pos = cache->nrHashed;
00680 if(source_entry >= 0 ||
00681 (pos && isHashed(cache, longname))) {
00682 pos = 0;
00683 } else if(pos && !ignore_match && isHashed(cache, shortName)) {
00684 if(pessimisticShortRename) {
00685 ssp->shortmatch = -2;
00686 return 1;
00687 }
00688 pos = 0;
00689 } else if(growDirCache(cache, pos) < 0) {
00690 fprintf(stderr, "Out of memory error in vfat_looup [0]\n");
00691 exit(1);
00692 }
00693 do {
00694 dce = vfat_lookup_loop_for_insert(&entry, pos, cache);
00695 switch(dce->type) {
00696 case DCET_FREE:
00697 accountFreeSlots(ssp, dce);
00698 break;
00699 case DCET_USED:
00700 if(!(dce->dir.attr & 0x8) &&
00701 dce->endSlot - 1 == source_entry)
00702 accountFreeSlots(ssp, dce);
00703
00704
00705
00706 if( (dce->dir.attr & 0x8) ||
00707 (dce->endSlot - 1 == ignore_entry) )
00708 break;
00709
00710
00711 if((dce->longName &&
00712 !strcasecmp(dce->longName, longname)) ||
00713 (dce->shortName &&
00714 !strcasecmp(dce->shortName, longname))) {
00715 ssp->longmatch = dce->endSlot - 1;
00716
00717
00718 return 1;
00719 }
00720
00721
00722
00723 if (!ignore_match &&
00724 !strcasecmp(shortName, dce->shortName))
00725 ssp->shortmatch = dce->endSlot - 1;
00726 break;
00727 case DCET_END:
00728 break;
00729 }
00730 pos = dce->endSlot;
00731 } while(dce->type != DCET_END);
00732 if (ssp->shortmatch > -1)
00733 return 1;
00734 ssp->max_entry = dce->beginSlot;
00735 if (ssp->got_slots)
00736 return 6;
00737
00738
00739 if(!isRootDir(Dir))
00740 return 5;
00741
00742 fprintf(stderr, "No directory slots\n");
00743 return -1;
00744 }
00745
00746
00747
00748