ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

lfn.c
Go to the documentation of this file.
00001 /* lfn.c  -  Functions for handling VFAT long filenames */
00002 
00003 /* Written 1998 by Roman Hodek */
00004 
00005 #include "vfatlib.h"
00006 
00007 #define NDEBUG
00008 #include <debug.h>
00009 
00010 typedef struct {
00011     __u8    id;     /* sequence number for slot */
00012     __u8    name0_4[10];    /* first 5 characters in name */
00013     __u8    attr;       /* attribute byte */
00014     __u8    reserved;   /* always 0 */
00015     __u8    alias_checksum; /* checksum for 8.3 alias */
00016     __u8    name5_10[12];   /* 6 more characters in name */
00017     __u16   start;      /* starting cluster number, 0 in long slots */
00018     __u8    name11_12[4];   /* last 2 characters in name */
00019 } LFN_ENT;
00020 
00021 #define LFN_ID_START    0x40
00022 #define LFN_ID_SLOTMASK 0x1f
00023 
00024 #define CHARS_PER_LFN   13
00025 
00026 /* These modul-global vars represent the state of the LFN parser */
00027 unsigned char *lfn_unicode = NULL;
00028 unsigned char lfn_checksum;
00029 int lfn_slot = -1;
00030 loff_t *lfn_offsets = NULL;
00031 int lfn_parts = 0;
00032 
00033 static unsigned char fat_uni2esc[64] = {
00034     '0', '1', '2', '3', '4', '5', '6', '7',
00035     '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
00036     'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
00037     'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
00038     'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
00039     'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
00040     'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
00041     'u', 'v', 'w', 'x', 'y', 'z', '+', '-'
00042 };
00043 
00044 /* This defines which unicode chars are directly convertable to ISO-8859-1 */
00045 #define UNICODE_CONVERTABLE(cl,ch)  (ch == 0 && (cl < 0x80 || cl >= 0xa0))
00046 
00047 /* for maxlen param */
00048 #define UNTIL_0     INT_MAX
00049 
00050 static void copy_lfn_part( char *dst, LFN_ENT *lfn );
00051 static char *cnv_unicode( const unsigned char *uni, int maxlen, int use_q );
00052 
00053 /* Convert name part in 'lfn' from unicode to ASCII */
00054 static __inline char* CNV_THIS_PART(LFN_ENT *lfn)
00055 {                           \
00056     char __part_uni[CHARS_PER_LFN*2];
00057     copy_lfn_part( __part_uni, lfn );
00058     return cnv_unicode( (unsigned char*)__part_uni, CHARS_PER_LFN, 0 );
00059 }
00060 
00061 /* Convert name parts collected so far (from previous slots) from unicode to
00062  * ASCII */
00063 #define CNV_PARTS_SO_FAR()                  \
00064     (cnv_unicode( lfn_unicode+(lfn_slot*CHARS_PER_LFN*2),   \
00065               lfn_parts*CHARS_PER_LFN, 0 ))
00066 
00067 /* This function converts an unicode string to a normal ASCII string, assuming
00068  * ISO-8859-1 charset. Characters not in 8859-1 are converted to the same
00069  * escape notation as used by the kernel, i.e. the uuencode-like ":xxx" */
00070 static char *cnv_unicode( const unsigned char *uni, int maxlen, int use_q )
00071 {
00072     const unsigned char *up;
00073     unsigned char *out, *cp;
00074     int len, val;
00075 
00076     for( len = 0, up = uni; (up-uni)/2 < maxlen && (up[0] || up[1]); up += 2 ){
00077     if (UNICODE_CONVERTABLE(up[0],up[1]))
00078         ++len;
00079     else
00080         len += 4;
00081     }
00082     cp = out = use_q ? qalloc( &FsCheckMemQueue, len+1 ) : vfalloc( len+1 );
00083 
00084     for( up = uni; (up-uni)/2 < maxlen && (up[0] || up[1]); up += 2 ) {
00085     if (UNICODE_CONVERTABLE(up[0],up[1]))
00086         *cp++ = up[0];
00087     else {
00088         /* here the same escape notation is used as in the Linux kernel */
00089         *cp++ = ':';
00090         val = (up[1] << 8) + up[0];
00091         cp[2] = fat_uni2esc[val & 0x3f];
00092         val >>= 6;
00093         cp[1] = fat_uni2esc[val & 0x3f];
00094         val >>= 6;
00095         cp[0] = fat_uni2esc[val & 0x3f];
00096         cp += 3;
00097     }
00098     }
00099     *cp = 0;
00100 
00101     return (char *)out;
00102 }
00103 
00104 
00105 static void copy_lfn_part( char *dst, LFN_ENT *lfn )
00106 {
00107     memcpy( dst,    lfn->name0_4,   10 );
00108     memcpy( dst+10, lfn->name5_10,  12 );
00109     memcpy( dst+22, lfn->name11_12, 4 );
00110 }
00111 
00112 
00113 static void clear_lfn_slots( int start, int end )
00114 {
00115     int i;
00116     LFN_ENT empty;
00117 
00118     /* New dir entry is zeroed except first byte, which is set to 0xe5.
00119      * This is to avoid that some FAT-reading OSes (not Linux! ;) stop reading
00120      * a directory at the first zero entry...
00121      */
00122     memset( &empty, 0, sizeof(empty) );
00123     empty.id = DELETED_FLAG;
00124 
00125     for( i = start; i <= end; ++i ) {
00126     fs_write( lfn_offsets[i], sizeof(LFN_ENT), &empty );
00127     }
00128 }
00129 
00130 void lfn_reset( void )
00131 {
00132     if (lfn_unicode)
00133     vffree( lfn_unicode );
00134     lfn_unicode = NULL;
00135     if (lfn_offsets)
00136     vffree( lfn_offsets );
00137     lfn_offsets = NULL;
00138     lfn_slot = -1;
00139 }
00140 
00141 
00142 /* This function is only called with de->attr == VFAT_LN_ATTR. It stores part
00143  * of the long name. */
00144 void lfn_add_slot( DIR_ENT *de, loff_t dir_offset )
00145 {
00146     LFN_ENT *lfn = (LFN_ENT *)de;
00147     unsigned offset;
00148 
00149     if (de->attr != VFAT_LN_ATTR)
00150     die("lfn_add_slot called with non-LFN directory entry");
00151 
00152     if (lfn->id & LFN_ID_START) {
00153     if (lfn_slot != -1) {
00154         int can_clear = 0;
00155         /* There is already a LFN "in progess", so it is an error that a
00156          * new start entry is here. */
00157         /* Causes: 1) if slot# == expected: start bit set mysteriously, 2)
00158          *         old LFN overwritten by new one */
00159         /* Fixes: 1) delete previous LFN 2) if slot# == expected and
00160          *        checksum ok: clear start bit */
00161         /* XXX: Should delay that until next LFN known (then can better
00162          * display the name) */
00163         VfatPrint( "A new long file name starts within an old one.\n" );
00164         if ((lfn->id & LFN_ID_SLOTMASK) == lfn_slot &&
00165         lfn->alias_checksum == lfn_checksum) {
00166         char *part1 = CNV_THIS_PART(lfn);
00167         char *part2 = CNV_PARTS_SO_FAR();
00168         VfatPrint( "  It could be that the LFN start bit is wrong here\n"
00169             "  if \"%s\" seems to match \"%s\".\n", part1, part2 );
00170         vffree( part1 );
00171         vffree( part2 );
00172         can_clear = 1;
00173         }
00174         if (FsCheckFlags & FSCHECK_INTERACTIVE) {
00175         VfatPrint( "1: Delete previous LFN\n2: Leave it as it is.\n" );
00176         if (can_clear)
00177             VfatPrint( "3: Clear start bit and concatenate LFNs\n" );
00178         }
00179         else VfatPrint( "  Not auto-correcting this.\n" );
00180         if (FsCheckFlags & FSCHECK_INTERACTIVE) {
00181         switch( get_key( can_clear ? "123" : "12", "?" )) {
00182           case '1':
00183             clear_lfn_slots( 0, lfn_parts-1 );
00184             lfn_reset();
00185             break;
00186           case '2':
00187             break;
00188           case '3':
00189             lfn->id &= ~LFN_ID_START;
00190             fs_write( dir_offset+offsetof(LFN_ENT,id),
00191                   sizeof(lfn->id), &lfn->id );
00192             break;
00193         }
00194         }
00195     }
00196     lfn_slot = lfn->id & LFN_ID_SLOTMASK;
00197     lfn_checksum = lfn->alias_checksum;
00198     lfn_unicode = vfalloc( (lfn_slot*CHARS_PER_LFN+1)*2 );
00199     lfn_offsets = vfalloc( lfn_slot*sizeof(loff_t) );
00200     lfn_parts = 0;
00201     }
00202     else if (lfn_slot == -1) {
00203     /* No LFN in progress, but slot found; start bit missing */
00204     /* Causes: 1) start bit got lost, 2) Previous slot with start bit got
00205      *         lost */
00206     /* Fixes: 1) delete LFN, 2) set start bit */
00207     char *part = CNV_THIS_PART(lfn);
00208     VfatPrint( "Long filename fragment \"%s\" found outside a LFN "
00209         "sequence.\n  (Maybe the start bit is missing on the "
00210         "last fragment)\n", part );
00211     if (FsCheckFlags & FSCHECK_INTERACTIVE) {
00212         VfatPrint( "1: Delete fragment\n2: Leave it as it is.\n"
00213             "3: Set start bit\n" );
00214     }
00215     else VfatPrint( "  Not auto-correcting this.\n" );
00216     if (FsCheckFlags & FSCHECK_INTERACTIVE) {
00217         switch( get_key( "123", "?" )) {
00218           case '1':
00219         if (!lfn_offsets)
00220             lfn_offsets = vfalloc( sizeof(loff_t) );
00221         lfn_offsets[0] = dir_offset;
00222         clear_lfn_slots( 0, 0 );
00223         lfn_reset();
00224         return;
00225           case '2':
00226         lfn_reset();
00227         return;
00228           case '3':
00229         lfn->id |= LFN_ID_START;
00230         fs_write( dir_offset+offsetof(LFN_ENT,id),
00231               sizeof(lfn->id), &lfn->id );
00232         lfn_slot = lfn->id & LFN_ID_SLOTMASK;
00233         lfn_checksum = lfn->alias_checksum;
00234         lfn_unicode = vfalloc( (lfn_slot*CHARS_PER_LFN+1)*2 );
00235         lfn_offsets = vfalloc( lfn_slot*sizeof(loff_t) );
00236         lfn_parts = 0;
00237         break;
00238         }
00239     }
00240     }
00241     else if ((lfn->id & LFN_ID_SLOTMASK) != lfn_slot) {
00242     /* wrong sequence number */
00243     /* Causes: 1) seq-no destroyed */
00244     /* Fixes: 1) delete LFN, 2) fix number (maybe only if following parts
00245      *        are ok?, maybe only if checksum is ok?) (Attention: space
00246      *        for name was allocated before!) */
00247     int can_fix = 0;
00248     VfatPrint( "Unexpected long filename sequence number "
00249         "(%d vs. expected %d).\n",
00250         (lfn->id & LFN_ID_SLOTMASK), lfn_slot );
00251     if (lfn->alias_checksum == lfn_checksum) {
00252         char *part1 = CNV_THIS_PART(lfn);
00253         char *part2 = CNV_PARTS_SO_FAR();
00254         VfatPrint( "  It could be that just the number is wrong\n"
00255             "  if \"%s\" seems to match \"%s\".\n", part1, part2 );
00256         vffree( part1 );
00257         vffree( part2 );
00258         can_fix = 1;
00259     }
00260     if (FsCheckFlags & FSCHECK_INTERACTIVE) {
00261         VfatPrint( "1: Delete LFN\n2: Leave it as it is (and ignore LFN so far)\n" );
00262         if (can_fix)
00263         VfatPrint( "3: Correct sequence number\n" );
00264     }
00265     else VfatPrint( "  Not auto-correcting this.\n" );
00266     if (FsCheckFlags & FSCHECK_INTERACTIVE) {
00267         switch( get_key( can_fix ? "123" : "12", "?" )) {
00268           case '1':
00269         lfn_offsets[lfn_parts++] = dir_offset;
00270         clear_lfn_slots( 0, lfn_parts-1 );
00271         lfn_reset();
00272         return;
00273           case '2':
00274         lfn_reset();
00275         return;
00276           case '3':
00277         lfn->id = (lfn->id & ~LFN_ID_SLOTMASK) | lfn_slot;
00278         fs_write( dir_offset+offsetof(LFN_ENT,id),
00279               sizeof(lfn->id), &lfn->id );
00280         break;
00281         }
00282     }
00283     }
00284 
00285     if (lfn->alias_checksum != lfn_checksum) {
00286     /* checksum mismatch */
00287     /* Causes: 1) checksum field here destroyed */
00288     /* Fixes: 1) delete LFN, 2) fix checksum */
00289     VfatPrint( "Checksum in long filename part wrong "
00290         "(%02x vs. expected %02x).\n",
00291         lfn->alias_checksum, lfn_checksum );
00292     if (FsCheckFlags & FSCHECK_INTERACTIVE) {
00293         VfatPrint( "1: Delete LFN\n2: Leave it as it is.\n"
00294             "3: Correct checksum\n" );
00295     }
00296     else VfatPrint( "  Not auto-correcting this.\n" );
00297     if (FsCheckFlags & FSCHECK_INTERACTIVE) {
00298         switch( get_key( "123", "?" )) {
00299           case '1':
00300         lfn_offsets[lfn_parts++] = dir_offset;
00301         clear_lfn_slots( 0, lfn_parts-1 );
00302         lfn_reset();
00303         return;
00304           case '2':
00305         break;
00306           case '3':
00307         lfn->alias_checksum = lfn_checksum;
00308         fs_write( dir_offset+offsetof(LFN_ENT,alias_checksum),
00309               sizeof(lfn->alias_checksum), &lfn->alias_checksum );
00310         break;
00311         }
00312     }
00313     }
00314 
00315     if (lfn_slot != -1) {
00316     lfn_slot--;
00317     offset = lfn_slot * CHARS_PER_LFN*2;
00318     copy_lfn_part( (char*)lfn_unicode+offset, lfn );
00319     if (lfn->id & LFN_ID_START)
00320         lfn_unicode[offset+26] = lfn_unicode[offset+27] = 0;
00321     lfn_offsets[lfn_parts++] = dir_offset;
00322     }
00323 
00324     if (lfn->reserved != 0) {
00325     VfatPrint( "Reserved field in VFAT long filename slot is not 0 "
00326         "(but 0x%02x).\n", lfn->reserved );
00327     if (FsCheckFlags & FSCHECK_INTERACTIVE)
00328         VfatPrint( "1: Fix.\n2: Leave it.\n" );
00329     else VfatPrint( "Auto-setting to 0.\n" );
00330     if (!(FsCheckFlags & FSCHECK_INTERACTIVE) || get_key("12","?") == '1') {
00331         lfn->reserved = 0;
00332         fs_write( dir_offset+offsetof(LFN_ENT,reserved),
00333               sizeof(lfn->reserved), &lfn->reserved );
00334     }
00335     }
00336     if (lfn->start != CT_LE_W(0)) {
00337     VfatPrint( "Start cluster field in VFAT long filename slot is not 0 "
00338         "(but 0x%04x).\n", lfn->start );
00339     if (FsCheckFlags & FSCHECK_INTERACTIVE)
00340         VfatPrint( "1: Fix.\n2: Leave it.\n" );
00341     else VfatPrint( "Auto-setting to 0.\n" );
00342     if (!(FsCheckFlags & FSCHECK_INTERACTIVE) || get_key("12","?") == '1') {
00343         lfn->start = CT_LE_W(0);
00344         fs_write( dir_offset+offsetof(LFN_ENT,start),
00345               sizeof(lfn->start),&lfn->start );
00346     }
00347     }
00348 }
00349 
00350 
00351 /* This function is always called when de->attr != VFAT_LN_ATTR is found, to
00352  * retrieve the previously constructed LFN. */
00353 char *lfn_get( DIR_ENT *de )
00354 {
00355     char *lfn;
00356     __u8 sum;
00357     int i;
00358 
00359     if (de->attr == VFAT_LN_ATTR)
00360     die("lfn_get called with LFN directory entry");
00361 
00362 #if 0
00363     if (de->lcase)
00364     VfatPrint( "lcase=%02x\n",de->lcase );
00365 #endif
00366 
00367     if (lfn_slot == -1)
00368     /* no long name for this file */
00369     return NULL;
00370 
00371     if (lfn_slot != 0) {
00372     /* The long name isn't finished yet. */
00373     /* Causes: 1) LFN slot overwritten by non-VFAT aware tool */
00374     /* Fixes: 1) delete LFN 2) move overwriting entry to somewhere else
00375      * and let user enter missing part of LFN (hard to do :-()
00376      * 3) renumber entries and truncate name */
00377     char *long_name = CNV_PARTS_SO_FAR();
00378     char *short_name = file_name(de->name);
00379     VfatPrint( "Unfinished long file name \"%s\".\n"
00380         "  (Start may have been overwritten by %s)\n",
00381         long_name, short_name );
00382     vffree( long_name );
00383     if (FsCheckFlags & FSCHECK_INTERACTIVE) {
00384         VfatPrint( "1: Delete LFN\n2: Leave it as it is.\n"
00385             "3: Fix numbering (truncates long name and attaches "
00386             "it to short name %s)\n", short_name );
00387     }
00388     else VfatPrint( "  Not auto-correcting this.\n" );
00389     if (FsCheckFlags & FSCHECK_INTERACTIVE) {
00390         switch( get_key( "123", "?" )) {
00391           case '1':
00392         clear_lfn_slots( 0, lfn_parts-1 );
00393         lfn_reset();
00394         return NULL;
00395           case '2':
00396         lfn_reset();
00397         return NULL;
00398           case '3':
00399         for( i = 0; i < lfn_parts; ++i ) {
00400             __u8 id = (lfn_parts-i) | (i==0 ? LFN_ID_START : 0);
00401             fs_write( lfn_offsets[i]+offsetof(LFN_ENT,id),
00402                   sizeof(id), &id );
00403         }
00404         memmove( lfn_unicode, lfn_unicode+lfn_slot*CHARS_PER_LFN*2,
00405              lfn_parts*CHARS_PER_LFN*2 );
00406         break;
00407         }
00408     }
00409     }
00410 
00411     for (sum = 0, i = 0; i < 8; i++)
00412     sum = (((sum&1) << 7) | ((sum&0xfe) >> 1)) + de->name[i];
00413 
00414     for (i = 0; i < 3; i++)
00415     sum = (((sum&1) << 7) | ((sum&0xfe) >> 1)) + de->ext[i];
00416 
00417     if (sum != lfn_checksum) {
00418     /* checksum doesn't match, long name doesn't apply to this alias */
00419     /* Causes: 1) alias renamed */
00420     /* Fixes: 1) Fix checksum in LFN entries */
00421     char *long_name = CNV_PARTS_SO_FAR();
00422     char *short_name = file_name(de->name);
00423     VfatPrint( "Wrong checksum for long file name \"%s\".\n"
00424         "  (Short name %s may have changed without updating the long name)\n",
00425         long_name, short_name );
00426     vffree( long_name );
00427     if (FsCheckFlags & FSCHECK_INTERACTIVE) {
00428         VfatPrint( "1: Delete LFN\n2: Leave it as it is.\n"
00429             "3: Fix checksum (attaches to short name %s)\n",
00430             short_name );
00431     }
00432     else VfatPrint( "  Not auto-correcting this.\n" );
00433     if (FsCheckFlags & FSCHECK_INTERACTIVE) {
00434         switch( get_key( "123", "?" )) {
00435           case '1':
00436         clear_lfn_slots( 0, lfn_parts-1 );
00437         lfn_reset();
00438         return NULL;
00439           case '2':
00440         lfn_reset();
00441         return NULL;
00442           case '3':
00443         for( i = 0; i < lfn_parts; ++i ) {
00444             fs_write( lfn_offsets[i]+offsetof(LFN_ENT,alias_checksum),
00445                   sizeof(sum), &sum );
00446         }
00447         break;
00448         }
00449     }
00450     }
00451 
00452     lfn = cnv_unicode( lfn_unicode, UNTIL_0, 1 );
00453     lfn_reset();
00454     return( lfn );
00455 }
00456 
00457 void lfn_check_orphaned(void)
00458 {
00459     char *long_name;
00460 
00461     if (lfn_slot == -1)
00462     return;
00463 
00464     long_name = CNV_PARTS_SO_FAR();
00465     VfatPrint("Orphaned long file name part \"%s\"\n", long_name);
00466     if (FsCheckFlags & FSCHECK_INTERACTIVE)
00467     VfatPrint( "1: Delete.\n2: Leave it.\n" );
00468     else VfatPrint( "  Auto-deleting.\n" );
00469     if (!(FsCheckFlags & FSCHECK_INTERACTIVE) || get_key("12","?") == '1') {
00470     clear_lfn_slots(0, lfn_parts - 1);
00471     }
00472     lfn_reset();
00473 }
00474 
00475 /* Local Variables: */
00476 /* tab-width: 8     */
00477 /* End:             */

Generated on Sat May 26 2012 04:35:04 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.