"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "dosfstools-2.11/dosfsck/fat.c" of archive dosfstools-2.11.src.tar.gz:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 /* fat.c  -  Read/write access to the FAT */
    2 
    3 /* Written 1993 by Werner Almesberger */
    4 
    5 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
    6  * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
    7 
    8 
    9 #include <stdio.h>
   10 #include <stdlib.h>
   11 #include <string.h>
   12 #include <unistd.h>
   13 
   14 #include "common.h"
   15 #include "dosfsck.h"
   16 #include "io.h"
   17 #include "check.h"
   18 #include "fat.h"
   19 
   20 
   21 static void get_fat(FAT_ENTRY *entry,void *fat,unsigned long cluster,DOS_FS *fs)
   22 {
   23     unsigned char *ptr;
   24 
   25     switch(fs->fat_bits) {
   26       case 12:
   27 	ptr = &((unsigned char *) fat)[cluster*3/2];
   28 	entry->value = 0xfff & (cluster & 1 ? (ptr[0] >> 4) | (ptr[1] << 4) :
   29 	  (ptr[0] | ptr[1] << 8));
   30 	break;
   31       case 16:
   32 	entry->value = CF_LE_W(((unsigned short *) fat)[cluster]);
   33 	break;
   34       case 32:
   35 	/* According to M$, the high 4 bits of a FAT32 entry are reserved and
   36 	 * are not part of the cluster number. So we cut them off. */
   37 	{
   38 	    unsigned long e = CF_LE_L(((unsigned int *) fat)[cluster]);
   39 	    entry->value = e & 0xfffffff;
   40 	    entry->reserved = e >> 28;
   41 	}
   42 	break;
   43       default:
   44 	die("Bad FAT entry size: %d bits.",fs->fat_bits);
   45     }
   46     entry->owner = NULL;
   47 }
   48 
   49 
   50 void read_fat(DOS_FS *fs)
   51 {
   52     int eff_size;
   53     unsigned long i;
   54     void *first,*second,*use;
   55     int first_ok,second_ok;
   56 
   57     eff_size = ((fs->clusters+2)*fs->fat_bits+7)/8;
   58     first = alloc(eff_size);
   59     fs_read(fs->fat_start,eff_size,first);
   60     use = first;
   61     if (fs->nfats > 1) {
   62 	second = alloc(eff_size);
   63 	fs_read(fs->fat_start+fs->fat_size,eff_size,second);
   64     }
   65     else
   66 	second = NULL;
   67     if (second && memcmp(first,second,eff_size) != 0) {
   68 	FAT_ENTRY first_media, second_media;
   69 	get_fat(&first_media,first,0,fs);
   70 	get_fat(&second_media,second,0,fs);
   71 	first_ok = (first_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs);
   72 	second_ok = (second_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs);
   73 	if (first_ok && !second_ok) {
   74 	    printf("FATs differ - using first FAT.\n");
   75 	    fs_write(fs->fat_start+fs->fat_size,eff_size,use = first);
   76 	}
   77 	if (!first_ok && second_ok) {
   78 	    printf("FATs differ - using second FAT.\n");
   79 	    fs_write(fs->fat_start,eff_size,use = second);
   80 	}
   81 	if (first_ok && second_ok) {
   82 	    if (interactive) {
   83 		printf("FATs differ but appear to be intact. Use which FAT ?\n"
   84 		  "1) Use first FAT\n2) Use second FAT\n");
   85 		if (get_key("12","?") == '1')
   86 		    fs_write(fs->fat_start+fs->fat_size,eff_size,use = first);
   87 		else fs_write(fs->fat_start,eff_size,use = second);
   88 	    }
   89 	    else {
   90 		printf("FATs differ but appear to be intact. Using first "
   91 		  "FAT.\n");
   92 		fs_write(fs->fat_start+fs->fat_size,eff_size,use = first);
   93 	    }
   94 	}
   95 	if (!first_ok && !second_ok) {
   96 	    printf("Both FATs appear to be corrupt. Giving up.\n");
   97 	    exit(1);
   98 	}
   99     }
  100     fs->fat = qalloc(&mem_queue,sizeof(FAT_ENTRY)*(fs->clusters+2));
  101     for (i = 2; i < fs->clusters+2; i++) get_fat(&fs->fat[i],use,i,fs);
  102     for (i = 2; i < fs->clusters+2; i++)
  103 	if (fs->fat[i].value >= fs->clusters+2 &&
  104 	    (fs->fat[i].value < FAT_MIN_BAD(fs))) {
  105 	    printf("Cluster %ld out of range (%ld > %ld). Setting to EOF.\n",
  106 		   i-2,fs->fat[i].value,fs->clusters+2-1);
  107 	    set_fat(fs,i,-1);
  108 	}
  109     free(first);
  110     if (second)
  111 	free(second);
  112 }
  113 
  114 
  115 void set_fat(DOS_FS *fs,unsigned long cluster,unsigned long new)
  116 {
  117     unsigned char data[4];
  118     int size;
  119     loff_t offs;
  120 
  121     if ((long)new == -1)
  122 	new = FAT_EOF(fs);
  123     else if ((long)new == -2)
  124 	new = FAT_BAD(fs);
  125     switch( fs->fat_bits ) {
  126       case 12:
  127 	offs = fs->fat_start+cluster*3/2;
  128 	if (cluster & 1) {
  129 	    data[0] = ((new & 0xf) << 4) | (fs->fat[cluster-1].value >> 8);
  130 	    data[1] = new >> 4;
  131 	}
  132 	else {
  133 	    data[0] = new & 0xff;
  134 	    data[1] = (new >> 8) | (cluster == fs->clusters-1 ? 0 :
  135 	      (0xff & fs->fat[cluster+1].value) << 4);
  136 	}
  137 	size = 2;
  138 	break;
  139       case 16:
  140 	offs = fs->fat_start+cluster*2;
  141 	*(unsigned short *) data = CT_LE_W(new);
  142 	size = 2;
  143 	break;
  144       case 32:
  145 	offs = fs->fat_start+cluster*4;
  146 	/* According to M$, the high 4 bits of a FAT32 entry are reserved and
  147 	 * are not part of the cluster number. So we never touch them. */
  148 	*(unsigned long *) data = CT_LE_L( (new & 0xfffffff) |
  149 					   (fs->fat[cluster].reserved << 28) );
  150 	size = 4;
  151 	break;
  152       default:
  153 	die("Bad FAT entry size: %d bits.",fs->fat_bits);
  154     }
  155     fs->fat[cluster].value = new;
  156     fs_write(offs,size,&data);
  157     fs_write(offs+fs->fat_size,size,&data);
  158 }
  159 
  160 
  161 int bad_cluster(DOS_FS *fs,unsigned long cluster)
  162 {
  163     return FAT_IS_BAD(fs,fs->fat[cluster].value);
  164 }
  165 
  166 
  167 unsigned long next_cluster(DOS_FS *fs,unsigned long cluster)
  168 {
  169     unsigned long value;
  170 
  171     value = fs->fat[cluster].value;
  172     if (FAT_IS_BAD(fs,value))
  173 	die("Internal error: next_cluster on bad cluster");
  174     return FAT_IS_EOF(fs,value) ? -1 : value;
  175 }
  176 
  177 
  178 loff_t cluster_start(DOS_FS *fs,unsigned long cluster)
  179 {
  180     return fs->data_start+((loff_t)cluster-2)*fs->cluster_size;
  181 }
  182 
  183 
  184 void set_owner(DOS_FS *fs,unsigned long cluster,DOS_FILE *owner)
  185 {
  186     if (owner && fs->fat[cluster].owner)
  187 	die("Internal error: attempt to change file owner");
  188     fs->fat[cluster].owner = owner;
  189 }
  190 
  191 
  192 DOS_FILE *get_owner(DOS_FS *fs,unsigned long cluster)
  193 {
  194     return fs->fat[cluster].owner;
  195 }
  196 
  197 
  198 void fix_bad(DOS_FS *fs)
  199 {
  200     unsigned long i;
  201 
  202     if (verbose)
  203 	printf("Checking for bad clusters.\n");
  204     for (i = 2; i < fs->clusters+2; i++)
  205 	if (!get_owner(fs,i) && !FAT_IS_BAD(fs,fs->fat[i].value))
  206 	    if (!fs_test(cluster_start(fs,i),fs->cluster_size)) {
  207 		printf("Cluster %lu is unreadable.\n",i);
  208 		set_fat(fs,i,-2);
  209 	    }
  210 }
  211 
  212 
  213 void reclaim_free(DOS_FS *fs)
  214 {
  215     int reclaimed;
  216     unsigned long i;
  217 
  218     if (verbose)
  219 	printf("Checking for unused clusters.\n");
  220     reclaimed = 0;
  221     for (i = 2; i < fs->clusters+2; i++)
  222 	if (!get_owner(fs,i) && fs->fat[i].value &&
  223 	    !FAT_IS_BAD(fs,fs->fat[i].value)) {
  224 	    set_fat(fs,i,0);
  225 	    reclaimed++;
  226 	}
  227     if (reclaimed)
  228 	printf("Reclaimed %d unused cluster%s (%d bytes).\n",reclaimed,
  229 	  reclaimed == 1 ?  "" : "s",reclaimed*fs->cluster_size);
  230 }
  231 
  232 
  233 static void tag_free(DOS_FS *fs,DOS_FILE *ptr)
  234 {
  235     DOS_FILE *owner;
  236     int prev;
  237     unsigned long i,walk;
  238 
  239     for (i = 2; i < fs->clusters+2; i++)
  240 	if (fs->fat[i].value && !FAT_IS_BAD(fs,fs->fat[i].value) &&
  241 	    !get_owner(fs,i) && !fs->fat[i].prev) {
  242 	    prev = 0;
  243 	    for (walk = i; walk > 0 && walk != -1;
  244 		 walk = next_cluster(fs,walk)) {
  245 		if (!(owner = get_owner(fs,walk))) set_owner(fs,walk,ptr);
  246 		else if (owner != ptr)
  247 		        die("Internal error: free chain collides with file");
  248 		    else {
  249 			set_fat(fs,prev,-1);
  250 			break;
  251 		    }
  252 		prev = walk;
  253 	    }
  254 	}
  255 }
  256 
  257 
  258 void reclaim_file(DOS_FS *fs)
  259 {
  260     DOS_FILE dummy;
  261     int reclaimed,files,changed;
  262     unsigned long i,next,walk;
  263 
  264     if (verbose)
  265 	printf("Reclaiming unconnected clusters.\n");
  266     for (i = 2; i < fs->clusters+2; i++) fs->fat[i].prev = 0;
  267     for (i = 2; i < fs->clusters+2; i++) {
  268 	next = fs->fat[i].value;
  269 	if (!get_owner(fs,i) && next && next < fs->clusters+2) {
  270 	    if (get_owner(fs,next) || !fs->fat[next].value ||
  271 		FAT_IS_BAD(fs,fs->fat[next].value)) set_fat(fs,i,-1);
  272 	    else fs->fat[next].prev++;
  273 	}
  274     }
  275     do {
  276 	tag_free(fs,&dummy);
  277 	changed = 0;
  278 	for (i = 2; i < fs->clusters+2; i++)
  279 	    if (fs->fat[i].value && !FAT_IS_BAD(fs,fs->fat[i].value) &&
  280 		!get_owner(fs, i)) {
  281 		if (!fs->fat[fs->fat[i].value].prev--)
  282 		    die("Internal error: prev going below zero");
  283 		set_fat(fs,i,-1);
  284 		changed = 1;
  285 		printf("Broke cycle at cluster %lu in free chain.\n",i);
  286 		break;
  287 	    }
  288     }
  289     while (changed);
  290     files = reclaimed = 0;
  291     for (i = 2; i < fs->clusters+2; i++)
  292 	if (get_owner(fs,i) == &dummy && !fs->fat[i].prev) {
  293 	    DIR_ENT de;
  294 	    loff_t offset;
  295 	    files++;
  296 	    offset = alloc_rootdir_entry(fs,&de,"FSCK%04dREC");
  297 	    de.start = CT_LE_W(i&0xffff);
  298 	    if (fs->fat_bits == 32)
  299 		de.starthi = CT_LE_W(i>>16);
  300 	    for (walk = i; walk > 0 && walk != -1;
  301 		 walk = next_cluster(fs,walk)) {
  302 		de.size = CT_LE_L(CF_LE_L(de.size)+fs->cluster_size);
  303 		reclaimed++;
  304 	    }
  305 	    fs_write(offset,sizeof(DIR_ENT),&de);
  306 	}
  307     if (reclaimed)
  308 	printf("Reclaimed %d unused cluster%s (%d bytes) in %d chain%s.\n",
  309 	  reclaimed,reclaimed == 1 ? "" : "s",reclaimed*fs->cluster_size,files,
  310 	  files == 1 ? "" : "s");
  311 }
  312 
  313 
  314 unsigned long update_free(DOS_FS *fs)
  315 {
  316     unsigned long i;
  317     unsigned long free = 0;
  318     int do_set = 0;
  319 
  320     for (i = 2; i < fs->clusters+2; i++)
  321 	if (!get_owner(fs,i) && !FAT_IS_BAD(fs,fs->fat[i].value))
  322 	    ++free;
  323 
  324     if (!fs->fsinfo_start)
  325 	return free;
  326 
  327     if (verbose)
  328 	printf("Checking free cluster summary.\n");
  329     if (fs->free_clusters >= 0) {
  330 	if (free != fs->free_clusters) {
  331 	    printf( "Free cluster summary wrong (%ld vs. really %ld)\n",
  332 		    fs->free_clusters,free);
  333 	    if (interactive)
  334 		printf( "1) Correct\n2) Don't correct\n" );
  335 	    else printf( "  Auto-correcting.\n" );
  336 	    if (!interactive || get_key("12","?") == '1')
  337 		do_set = 1;
  338 	}
  339     }
  340     else {
  341 	printf( "Free cluster summary uninitialized (should be %ld)\n", free );
  342 	if (interactive)
  343 	    printf( "1) Set it\n2) Leave it uninitialized\n" );
  344 	else printf( "  Auto-setting.\n" );
  345 	if (!interactive || get_key("12","?") == '1')
  346 	    do_set = 1;
  347     }
  348 
  349     if (do_set) {
  350 	fs->free_clusters = free;
  351 	free = CT_LE_L(free);
  352 	fs_write(fs->fsinfo_start+offsetof(struct info_sector,free_clusters),
  353 		 sizeof(free),&free);
  354     }
  355 
  356     return free;
  357 }
  358 
  359 /* Local Variables: */
  360 /* tab-width: 8     */
  361 /* End:             */