Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenlfn.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
1.7.6.1
|