Home | History | Annotate | Line # | Download | only in fsck_msdos
fat.c revision 1.1
      1 /*	$NetBSD: fat.c,v 1.1 1996/05/14 17:39:34 ws Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 1995, 1996 Wolfgang Solfrank
      5  * Copyright (c) 1995 Martin Husemann
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by Martin Husemann
     18  *	and Wolfgang Solfrank.
     19  * 4. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
     24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     26  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     28  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     32  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     33  */
     34 
     35 
     36 #ifndef lint
     37 static char rcsid[] = "$NetBSD: fat.c,v 1.1 1996/05/14 17:39:34 ws Exp $";
     38 #endif /* not lint */
     39 
     40 #include <stdlib.h>
     41 #include <string.h>
     42 #include <ctype.h>
     43 #include <stdio.h>
     44 #include <unistd.h>
     45 
     46 #include "ext.h"
     47 
     48 /*
     49  * Check a cluster number for valid value
     50  */
     51 static int
     52 checkclnum(boot, fat, cl, next)
     53 	struct bootblock *boot;
     54 	int fat;
     55 	cl_t cl;
     56 	cl_t *next;
     57 {
     58 	if (!boot->Is16BitFat && *next >= (CLUST_RSRVD&0xfff))
     59 		*next |= 0xf000;
     60 	if (*next == CLUST_FREE) {
     61 		boot->NumFree++;
     62 		return FSOK;
     63 	}
     64 	if (*next < CLUST_FIRST
     65 	    || (*next >= boot->NumClusters && *next < CLUST_EOFS)) {
     66 		pwarn("Cluster %d in FAT %d continues with %s cluster number %d\n",
     67 		      cl, fat,
     68 		      *next < CLUST_RSRVD ? "out of range" : "reserved",
     69 		      *next);
     70 		if (ask(0, "Truncate")) {
     71 			*next = CLUST_EOF;
     72 			return FSFATMOD;
     73 		}
     74 		return FSERROR;
     75 	}
     76 	return FSOK;
     77 }
     78 
     79 /*
     80  * Read a FAT and decode it into internal format
     81  */
     82 int
     83 readfat(fs, boot, no, fp)
     84 	int fs;
     85 	struct bootblock *boot;
     86 	int no;
     87 	struct fatEntry **fp;
     88 {
     89 	struct fatEntry *fat;
     90 	u_char *buffer, *p;
     91 	cl_t cl;
     92 	off_t off;
     93 	int size;
     94 	int ret = FSOK;
     95 
     96 	boot->NumFree = 0;
     97 	fat = malloc(sizeof(struct fatEntry) * boot->NumClusters);
     98 	buffer = malloc(boot->FATsecs * boot->BytesPerSec);
     99 	if (fat == NULL || buffer == NULL) {
    100 		perror("No space for FAT");
    101 		if (fat)
    102 			free(fat);
    103 		return FSFATAL;
    104 	}
    105 
    106 	memset(fat, 0, sizeof(struct fatEntry) * boot->NumClusters);
    107 
    108 	off = boot->ResSectors + no * boot->FATsecs;
    109 	off *= boot->BytesPerSec;
    110 
    111 	if (lseek(fs, off, SEEK_SET) != off) {
    112 		perror("Unable to read FAT");
    113 		free(buffer);
    114 		free(fat);
    115 		return FSFATAL;
    116 	}
    117 
    118 	if ((size = read(fs, buffer, boot->FATsecs * boot->BytesPerSec))
    119 	    != boot->FATsecs * boot->BytesPerSec) {
    120 		if (size < 0)
    121 			perror("Unable to read FAT");
    122 		else
    123 			pfatal("Short FAT?");
    124 		free(buffer);
    125 		free(fat);
    126 		return FSFATAL;
    127 	}
    128 
    129 	/*
    130 	 * Remember start of FAT to allow keeping it in write_fat.
    131 	 */
    132 	fat[0].length = buffer[0]|(buffer[1] << 8)|(buffer[2] << 16);
    133 	if (boot->Is16BitFat)
    134 		fat[0].length |= buffer[3] << 24;
    135 	if (buffer[1] != 0xff || buffer[2] != 0xff
    136 	    || (boot->Is16BitFat && buffer[3] != 0xff)) {
    137 		char *msg = boot->Is16BitFat
    138 			? "FAT starts with odd byte sequence (%02x%02x%02x%02x)\n"
    139 			: "FAT starts with odd byte sequence (%02x%02x%02x)\n";
    140 		pwarn(msg, buffer[0], buffer[1], buffer[2], buffer[3]);
    141 		if (ask(1, "Correct")) {
    142 			fat[0].length = boot->Media|0xffffff;
    143 			ret |= FSFATMOD;
    144 		}
    145 	}
    146 	p = buffer + (boot->Is16BitFat ? 4 : 3);
    147 	for (cl = CLUST_FIRST; cl < boot->NumClusters;) {
    148 		if (boot->Is16BitFat) {
    149 			fat[cl].next = p[0] + (p[1] << 8);
    150 			ret |= checkclnum(boot, no, cl, &fat[cl].next);
    151 			cl++;
    152 			p += 2;
    153 		} else {
    154 			fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff;
    155 			ret |= checkclnum(boot, no, cl, &fat[cl].next);
    156 			cl++;
    157 			if (cl >= boot->NumClusters)
    158 				break;
    159 			fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff;
    160 			ret |= checkclnum(boot, no, cl, &fat[cl].next);
    161 			cl++;
    162 			p += 3;
    163 		}
    164 	}
    165 
    166 	free(buffer);
    167 	*fp = fat;
    168 	return ret;
    169 }
    170 
    171 /*
    172  * Get type of reserved cluster
    173  */
    174 char *
    175 rsrvdcltype(cl)
    176 	cl_t cl;
    177 {
    178 	if (cl < CLUST_BAD)
    179 		return "reserved";
    180 	if (cl > CLUST_BAD)
    181 		return "as EOF";
    182 	return "bad";
    183 }
    184 
    185 static int
    186 clustdiffer(cl, cp1, cp2, fatnum)
    187 	cl_t cl;
    188 	cl_t *cp1;
    189 	cl_t *cp2;
    190 	int fatnum;
    191 {
    192 	if (*cp1 >= CLUST_RSRVD) {
    193 		if (*cp2 >= CLUST_RSRVD) {
    194 			if ((*cp1 < CLUST_BAD && *cp2 < CLUST_BAD)
    195 			    || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) {
    196 				pwarn("Cluster %d is marked %s with different indicators, ",
    197 				      cl, rsrvdcltype(*cp1));
    198 				if (ask(1, "fix")) {
    199 					*cp2 = *cp1;
    200 					return FSFATMOD;
    201 				}
    202 				return FSFATAL;
    203 			}
    204 			pwarn("Cluster %d is marked %s in FAT 1, %s in FAT %d\n",
    205 			      cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
    206 			if (ask(0, "use FAT #1's entry")) {
    207 				*cp2 = *cp1;
    208 				return FSFATMOD;
    209 			}
    210 			if (ask(0, "use FAT #%d's entry", fatnum)) {
    211 				*cp1 = *cp2;
    212 				return FSFATMOD;
    213 			}
    214 			return FSFATAL;
    215 		}
    216 		pwarn("Cluster %d is marked %s in FAT 1, but continues with cluster %d in FAT %d\n",
    217 		      cl, rsrvdcltype(*cp1), *cp2, fatnum);
    218 		if (ask(0, "Use continuation from FAT %d", fatnum)) {
    219 			*cp1 = *cp2;
    220 			return FSFATMOD;
    221 		}
    222 		if (ask(0, "Use mark from FAT 1")) {
    223 			*cp2 = *cp1;
    224 			return FSFATMOD;
    225 		}
    226 		return FSFATAL;
    227 	}
    228 	if (*cp2 >= CLUST_RSRVD) {
    229 		pwarn("Cluster %d continues with cluster %d in FAT 1, but is marked %s in FAT %d\n",
    230 		      cl, *cp1, rsrvdcltype(*cp2), fatnum);
    231 		if (ask(0, "Use continuation from FAT 1")) {
    232 			*cp2 = *cp1;
    233 			return FSFATMOD;
    234 		}
    235 		if (ask(0, "Use mark from FAT %d", fatnum)) {
    236 			*cp1 = *cp2;
    237 			return FSFATMOD;
    238 		}
    239 		return FSERROR;
    240 	}
    241 	pwarn("Cluster %d continues with cluster %d in FAT 1, but with cluster %d in FAT %d\n",
    242 	      cl, *cp1, *cp2, fatnum);
    243 	if (ask(0, "Use continuation from FAT 1")) {
    244 		*cp2 = *cp1;
    245 		return FSFATMOD;
    246 	}
    247 	if (ask(0, "Use continuation from FAT %d", fatnum)) {
    248 		*cp1 = *cp2;
    249 		return FSFATMOD;
    250 	}
    251 	return FSERROR;
    252 }
    253 
    254 /*
    255  * Compare two FAT copies in memory. Resolve any conflicts and merge them
    256  * into the first one.
    257  */
    258 int
    259 comparefat(boot, first, second, fatnum)
    260 	struct bootblock *boot;
    261 	struct fatEntry *first;
    262 	struct fatEntry *second;
    263 	int fatnum;
    264 {
    265 	cl_t cl;
    266 	int ret = FSOK;
    267 
    268 	if (first[0].next != second[0].next) {
    269 		pwarn("Media bytes in cluster 1(%02x) and %d(%02x) differ\n",
    270 		      first[0].next, fatnum, second[0].next);
    271 		if (ask(1, "Use media byte from FAT 1")) {
    272 			second[0].next = first[0].next;
    273 			ret |= FSFATMOD;
    274 		} else if (ask(0, "Use media byte from FAT %d", fatnum)) {
    275 			first[0].next = second[0].next;
    276 			ret |= FSFATMOD;
    277 		} else
    278 			ret |= FSERROR;
    279 	}
    280 	for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++)
    281 		if (first[cl].next != second[cl].next)
    282 			ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum);
    283 	return ret;
    284 }
    285 
    286 void
    287 clearchain(boot, fat, head)
    288 	struct bootblock *boot;
    289 	struct fatEntry *fat;
    290 	cl_t head;
    291 {
    292 	cl_t p, q;
    293 
    294 	for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) {
    295 		if (fat[p].head != head)
    296 			break;
    297 		q = fat[p].next;
    298 		fat[p].next = fat[p].head = CLUST_FREE;
    299 		fat[p].length = 0;
    300 	}
    301 }
    302 
    303 /*
    304  * Check a complete FAT in-memory for crosslinks
    305  */
    306 int
    307 checkfat(boot, fat)
    308 	struct bootblock *boot;
    309 	struct fatEntry *fat;
    310 {
    311 	cl_t head, p, h;
    312 	u_int len;
    313 	int ret = 0;
    314 	int conf;
    315 
    316 	/*
    317 	 * pass 1: figure out the cluster chains.
    318 	 */
    319 	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
    320 		/* find next untraveled chain */
    321 		if (fat[head].head != 0		/* cluster already belongs to some chain*/
    322 		    || fat[head].next == CLUST_FREE)
    323 			continue;		/* skip it. */
    324 
    325 		/* follow the chain and mark all clusters on the way */
    326 		for (len = 0, p = head;
    327 		     p >= CLUST_FIRST && p < boot->NumClusters;
    328 		     p = fat[p].next) {
    329 			fat[p].head = head;
    330 			len++;
    331 		}
    332 
    333 		/* the head record gets the length */
    334 		fat[head].length = len;
    335 	}
    336 
    337 	/*
    338 	 * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because
    339 	 * we didn't know the real start of the chain then - would have treated partial
    340 	 * chains as interlinked with their main chain)
    341 	 */
    342 	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
    343 		/* find next untraveled chain */
    344 		if (fat[head].head != head)
    345 			continue;
    346 
    347 		/* follow the chain to its end (hopefully) */
    348 		for (p = head;
    349 		     fat[p].next >= CLUST_FIRST && fat[p].next < boot->NumClusters;
    350 		     p = fat[p].next)
    351 			if (fat[fat[p].next].head != head)
    352 				break;
    353 		if (fat[p].next >= CLUST_EOFS)
    354 			continue;
    355 
    356 		if (fat[p].next == 0) {
    357 			pwarn("Cluster chain starting at %d ends with free cluster\n", head);
    358 			if (ask(0, "Clear chain starting at %d", head)) {
    359 				clearchain(boot, fat, head);
    360 				ret |= FSFATMOD;
    361 			} else
    362 				ret |= FSERROR;
    363 			continue;
    364 		}
    365 		if (fat[p].next >= CLUST_RSRVD) {
    366 			pwarn("Cluster chain starting at %d ends with cluster marked %s\n",
    367 			      head, rsrvdcltype(fat[p].next));
    368 			if (ask(0, "Clear chain starting at %d", head)) {
    369 				clearchain(boot, fat, head);
    370 				ret |= FSFATMOD;
    371 			} else
    372 				ret |= FSERROR;
    373 			continue;
    374 		}
    375 		if (fat[p].next < CLUST_FIRST || fat[p].next >= boot->NumClusters) {
    376 			pwarn("Cluster chain starting at %d ends with cluster out of range (%d)\n",
    377 			      head, fat[p].next);
    378 			if (ask(0, "Clear chain starting at %d", head)) {
    379 				clearchain(boot, fat, head);
    380 				ret |= FSFATMOD;
    381 			} else
    382 				ret |= FSERROR;
    383 		}
    384 		pwarn("Cluster chains starting at %d and %d are linked at cluster %d\n",
    385 		      head, fat[p].head, p);
    386 		conf = FSERROR;
    387 		if (ask(0, "Clear chain starting at %d", head)) {
    388 			clearchain(boot, fat, head);
    389 			conf = FSFATMOD;
    390 		}
    391 		if (ask(0, "Clear chain starting at %d", h = fat[p].head)) {
    392 			if (conf == FSERROR) {
    393 				/*
    394 				 * Transfer the common chain to the one not cleared above.
    395 				 */
    396 				for (; p >= CLUST_FIRST && p < boot->NumClusters;
    397 				     p = fat[p].next) {
    398 					if (h != fat[p].head) {
    399 						/*
    400 						 * Have to reexamine this chain.
    401 						 */
    402 						head--;
    403 						break;
    404 					}
    405 					fat[p].head = head;
    406 				}
    407 			}
    408 			clearchain(boot, fat, h);
    409 			conf |= FSFATMOD;
    410 		}
    411 		ret |= conf;
    412 	}
    413 
    414 	return ret;
    415 }
    416 
    417 /*
    418  * Write out FATs encoding them from the internal format
    419  */
    420 int
    421 writefat(fs, boot, fat)
    422 	int fs;
    423 	struct bootblock *boot;
    424 	struct fatEntry *fat;
    425 {
    426 	u_char *buffer, *p;
    427 	cl_t cl;
    428 	int i;
    429 	u_int32_t fatsz;
    430 	off_t off;
    431 	int ret = FSOK;
    432 
    433 	buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec);
    434 	if (buffer == NULL) {
    435 		perror("No space for FAT");
    436 		return FSFATAL;
    437 	}
    438 	memset(buffer, 0, fatsz);
    439 	boot->NumFree = 0;
    440 	buffer[0] = (u_char)fat[0].length;
    441 	buffer[1] = (u_char)(fat[0].length >> 8);
    442 	if (boot->Is16BitFat)
    443 		buffer[3] = (u_char)(fat[0].length >> 24);
    444 	for (cl = CLUST_FIRST, p = buffer; cl < boot->NumClusters;) {
    445 		if (boot->Is16BitFat) {
    446 			p[0] = (u_char)fat[cl].next;
    447 			if (fat[cl].next == CLUST_FREE)
    448 				boot->NumFree++;
    449 			p[1] = (u_char)(fat[cl++].next >> 8);
    450 			p += 2;
    451 		} else {
    452 			if (fat[cl].next == CLUST_FREE)
    453 				boot->NumFree++;
    454 			if (cl + 1 < boot->NumClusters
    455 			    && fat[cl + 1].next == CLUST_FREE)
    456 				boot->NumFree++;
    457 			p[0] = (u_char)fat[cl].next;
    458 			p[1] = (u_char)((fat[cl].next >> 8) & 0xf)
    459 				|(u_char)(fat[cl+1].next << 4);
    460 			p[2] = (u_char)(fat[cl++].next >> 8);
    461 			p += 3;
    462 		}
    463 	}
    464 	for (i = 0; i < boot->FATs; i++) {
    465 		off = boot->ResSectors + i * boot->FATsecs;
    466 		off *= boot->BytesPerSec;
    467 		if (lseek(fs, off, SEEK_SET) != off
    468 		    || write(fs, buffer, fatsz) != fatsz) {
    469 			perror("Unable to write FAT");
    470 			ret = FSFATAL; /* Return immediately?		XXX */
    471 		}
    472 	}
    473 	free(buffer);
    474 	return ret;
    475 }
    476 
    477 /*
    478  * Check a complete in-memory FAT for lost cluster chains
    479  */
    480 int
    481 checklost(dosfs, boot, fat, rootDir)
    482 	int dosfs;
    483 	struct bootblock *boot;
    484 	struct fatEntry *fat;
    485 	struct dosDirEntry *rootDir;
    486 {
    487 	cl_t head;
    488 	struct dosDirEntry *lfdir;
    489 	int mod = FSOK;
    490 
    491 	for (lfdir = rootDir->child; lfdir; lfdir = lfdir->next) {
    492 		if (!strcmp(lfdir->name, LOSTDIR))
    493 			break;
    494 	}
    495 	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
    496 		/* find next untraveled chain */
    497 		if (fat[head].head != head
    498 		    || fat[head].next == CLUST_FREE
    499 		    || (fat[head].next >= CLUST_RSRVD
    500 			&& fat[head].next < CLUST_EOFS))
    501 			continue;
    502 
    503 		if (fat[head].dirp == NULL) {
    504 			pwarn("Lost cluster chain at cluster 0x%04x\n%d Cluster(s) lost\n",
    505 			      head, fat[head].length);
    506 			mod |= reconnect(dosfs, boot, fat, head, lfdir);
    507 			if (mod&FSFATAL)
    508 				break;
    509 		}
    510 	}
    511 	finishlf();
    512 
    513 	return mod;
    514 }
    515