1 1.30 christos /* $NetBSD: fat.c,v 1.30 2019/06/04 00:08:00 christos Exp $ */ 2 1.1 ws 3 1.1 ws /* 4 1.8 ws * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank 5 1.1 ws * Copyright (c) 1995 Martin Husemann 6 1.1 ws * 7 1.1 ws * Redistribution and use in source and binary forms, with or without 8 1.1 ws * modification, are permitted provided that the following conditions 9 1.1 ws * are met: 10 1.1 ws * 1. Redistributions of source code must retain the above copyright 11 1.1 ws * notice, this list of conditions and the following disclaimer. 12 1.1 ws * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 ws * notice, this list of conditions and the following disclaimer in the 14 1.1 ws * documentation and/or other materials provided with the distribution. 15 1.1 ws * 16 1.1 ws * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 17 1.1 ws * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 1.1 ws * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 1.1 ws * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 1.1 ws * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 1.1 ws * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 1.1 ws * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 1.1 ws * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 1.1 ws * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 1.1 ws * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 1.1 ws */ 27 1.1 ws 28 1.1 ws 29 1.7 lukem #include <sys/cdefs.h> 30 1.1 ws #ifndef lint 31 1.30 christos __RCSID("$NetBSD: fat.c,v 1.30 2019/06/04 00:08:00 christos Exp $"); 32 1.1 ws #endif /* not lint */ 33 1.1 ws 34 1.1 ws #include <stdlib.h> 35 1.1 ws #include <string.h> 36 1.1 ws #include <ctype.h> 37 1.1 ws #include <stdio.h> 38 1.1 ws #include <unistd.h> 39 1.1 ws 40 1.1 ws #include "ext.h" 41 1.4 christos #include "fsutil.h" 42 1.3 christos 43 1.22 lukem static int checkclnum(struct bootblock *, u_int, cl_t, cl_t *); 44 1.22 lukem static int clustdiffer(cl_t, cl_t *, cl_t *, u_int); 45 1.16 xtraeme static int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *); 46 1.22 lukem static int _readfat(int, struct bootblock *, u_int, u_char **); 47 1.1 ws 48 1.1 ws /* 49 1.1 ws * Check a cluster number for valid value 50 1.1 ws */ 51 1.1 ws static int 52 1.22 lukem checkclnum(struct bootblock *boot, u_int fat, cl_t cl, cl_t *next) 53 1.1 ws { 54 1.8 ws if (*next >= (CLUST_RSRVD&boot->ClustMask)) 55 1.8 ws *next |= ~boot->ClustMask; 56 1.1 ws if (*next == CLUST_FREE) { 57 1.1 ws boot->NumFree++; 58 1.1 ws return FSOK; 59 1.1 ws } 60 1.5 ws if (*next == CLUST_BAD) { 61 1.5 ws boot->NumBad++; 62 1.5 ws return FSOK; 63 1.5 ws } 64 1.1 ws if (*next < CLUST_FIRST 65 1.1 ws || (*next >= boot->NumClusters && *next < CLUST_EOFS)) { 66 1.22 lukem pwarn("Cluster %u in FAT %u continues with %s cluster number %u\n", 67 1.1 ws cl, fat, 68 1.1 ws *next < CLUST_RSRVD ? "out of range" : "reserved", 69 1.8 ws *next&boot->ClustMask); 70 1.1 ws if (ask(0, "Truncate")) { 71 1.1 ws *next = CLUST_EOF; 72 1.1 ws return FSFATMOD; 73 1.1 ws } 74 1.1 ws return FSERROR; 75 1.1 ws } 76 1.1 ws return FSOK; 77 1.1 ws } 78 1.1 ws 79 1.1 ws /* 80 1.10 jdolecek * Read a FAT from disk. Returns 1 if successful, 0 otherwise. 81 1.1 ws */ 82 1.11 jdolecek static int 83 1.22 lukem _readfat(int fs, struct bootblock *boot, u_int no, u_char **buffer) 84 1.1 ws { 85 1.1 ws off_t off; 86 1.18 christos size_t len; 87 1.1 ws 88 1.18 christos *buffer = malloc(len = boot->FATsecs * boot->BytesPerSec); 89 1.10 jdolecek if (*buffer == NULL) { 90 1.18 christos perr("No space for FAT sectors (%zu)", len); 91 1.10 jdolecek return 0; 92 1.1 ws } 93 1.8 ws 94 1.1 ws off = boot->ResSectors + no * boot->FATsecs; 95 1.1 ws off *= boot->BytesPerSec; 96 1.1 ws 97 1.1 ws if (lseek(fs, off, SEEK_SET) != off) { 98 1.18 christos perr("Unable to read FAT"); 99 1.10 jdolecek goto err; 100 1.1 ws } 101 1.8 ws 102 1.22 lukem if ((size_t)read(fs, *buffer, boot->FATsecs * boot->BytesPerSec) 103 1.1 ws != boot->FATsecs * boot->BytesPerSec) { 104 1.18 christos perr("Unable to read FAT"); 105 1.10 jdolecek goto err; 106 1.10 jdolecek } 107 1.10 jdolecek 108 1.10 jdolecek return 1; 109 1.10 jdolecek 110 1.10 jdolecek err: 111 1.10 jdolecek free(*buffer); 112 1.10 jdolecek return 0; 113 1.10 jdolecek } 114 1.10 jdolecek 115 1.10 jdolecek /* 116 1.10 jdolecek * Read a FAT and decode it into internal format 117 1.10 jdolecek */ 118 1.10 jdolecek int 119 1.22 lukem readfat(int fs, struct bootblock *boot, u_int no, struct fatEntry **fp) 120 1.10 jdolecek { 121 1.10 jdolecek struct fatEntry *fat; 122 1.10 jdolecek u_char *buffer, *p; 123 1.10 jdolecek cl_t cl; 124 1.10 jdolecek int ret = FSOK; 125 1.18 christos size_t len; 126 1.10 jdolecek 127 1.10 jdolecek boot->NumFree = boot->NumBad = 0; 128 1.10 jdolecek 129 1.10 jdolecek if (!_readfat(fs, boot, no, &buffer)) 130 1.10 jdolecek return FSFATAL; 131 1.21 matthias 132 1.18 christos fat = malloc(len = boot->NumClusters * sizeof(struct fatEntry)); 133 1.10 jdolecek if (fat == NULL) { 134 1.18 christos perr("No space for FAT clusters (%zu)", len); 135 1.1 ws free(buffer); 136 1.1 ws return FSFATAL; 137 1.1 ws } 138 1.18 christos (void)memset(fat, 0, len); 139 1.1 ws 140 1.6 ws if (buffer[0] != boot->Media 141 1.6 ws || buffer[1] != 0xff || buffer[2] != 0xff 142 1.8 ws || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff) 143 1.8 ws || (boot->ClustMask == CLUST32_MASK 144 1.8 ws && ((buffer[3]&0x0f) != 0x0f 145 1.8 ws || buffer[4] != 0xff || buffer[5] != 0xff 146 1.8 ws || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) { 147 1.10 jdolecek 148 1.10 jdolecek /* Windows 95 OSR2 (and possibly any later) changes 149 1.10 jdolecek * the FAT signature to 0xXXffff7f for FAT16 and to 150 1.10 jdolecek * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the 151 1.10 jdolecek * filesystem is dirty if it doesn't reboot cleanly. 152 1.10 jdolecek * Check this special condition before errorring out. 153 1.10 jdolecek */ 154 1.10 jdolecek if (buffer[0] == boot->Media && buffer[1] == 0xff 155 1.10 jdolecek && buffer[2] == 0xff 156 1.10 jdolecek && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f) 157 1.10 jdolecek || (boot->ClustMask == CLUST32_MASK 158 1.10 jdolecek && buffer[3] == 0x0f && buffer[4] == 0xff 159 1.10 jdolecek && buffer[5] == 0xff && buffer[6] == 0xff 160 1.10 jdolecek && buffer[7] == 0x07))) 161 1.10 jdolecek ret |= FSDIRTY; 162 1.10 jdolecek else { 163 1.10 jdolecek /* just some odd byte sequence in FAT */ 164 1.21 matthias 165 1.10 jdolecek switch (boot->ClustMask) { 166 1.10 jdolecek case CLUST32_MASK: 167 1.12 is pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n", 168 1.12 is "FAT starts with odd byte sequence", 169 1.12 is buffer[0], buffer[1], buffer[2], buffer[3], 170 1.12 is buffer[4], buffer[5], buffer[6], buffer[7]); 171 1.10 jdolecek break; 172 1.10 jdolecek case CLUST16_MASK: 173 1.13 lukem pwarn("%s (%02x%02x%02x%02x)\n", 174 1.13 lukem "FAT starts with odd byte sequence", 175 1.13 lukem buffer[0], buffer[1], buffer[2], buffer[3]); 176 1.10 jdolecek break; 177 1.10 jdolecek default: 178 1.13 lukem pwarn("%s (%02x%02x%02x)\n", 179 1.13 lukem "FAT starts with odd byte sequence", 180 1.13 lukem buffer[0], buffer[1], buffer[2]); 181 1.10 jdolecek break; 182 1.10 jdolecek } 183 1.8 ws 184 1.21 matthias 185 1.10 jdolecek if (ask(1, "Correct")) 186 1.10 jdolecek ret |= FSFIXFAT; 187 1.8 ws } 188 1.1 ws } 189 1.8 ws switch (boot->ClustMask) { 190 1.8 ws case CLUST32_MASK: 191 1.8 ws p = buffer + 8; 192 1.8 ws break; 193 1.8 ws case CLUST16_MASK: 194 1.8 ws p = buffer + 4; 195 1.8 ws break; 196 1.8 ws default: 197 1.8 ws p = buffer + 3; 198 1.8 ws break; 199 1.8 ws } 200 1.1 ws for (cl = CLUST_FIRST; cl < boot->NumClusters;) { 201 1.8 ws switch (boot->ClustMask) { 202 1.8 ws case CLUST32_MASK: 203 1.8 ws fat[cl].next = p[0] + (p[1] << 8) 204 1.8 ws + (p[2] << 16) + (p[3] << 24); 205 1.8 ws fat[cl].next &= boot->ClustMask; 206 1.8 ws ret |= checkclnum(boot, no, cl, &fat[cl].next); 207 1.8 ws cl++; 208 1.8 ws p += 4; 209 1.8 ws break; 210 1.8 ws case CLUST16_MASK: 211 1.1 ws fat[cl].next = p[0] + (p[1] << 8); 212 1.1 ws ret |= checkclnum(boot, no, cl, &fat[cl].next); 213 1.1 ws cl++; 214 1.1 ws p += 2; 215 1.8 ws break; 216 1.8 ws default: 217 1.1 ws fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff; 218 1.1 ws ret |= checkclnum(boot, no, cl, &fat[cl].next); 219 1.1 ws cl++; 220 1.1 ws if (cl >= boot->NumClusters) 221 1.1 ws break; 222 1.1 ws fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff; 223 1.1 ws ret |= checkclnum(boot, no, cl, &fat[cl].next); 224 1.1 ws cl++; 225 1.1 ws p += 3; 226 1.8 ws break; 227 1.1 ws } 228 1.1 ws } 229 1.8 ws 230 1.1 ws free(buffer); 231 1.17 christos if (ret & FSFATAL) { 232 1.17 christos free(fat); 233 1.17 christos *fp = NULL; 234 1.17 christos } else 235 1.17 christos *fp = fat; 236 1.1 ws return ret; 237 1.1 ws } 238 1.1 ws 239 1.1 ws /* 240 1.1 ws * Get type of reserved cluster 241 1.1 ws */ 242 1.14 wiz const char * 243 1.16 xtraeme rsrvdcltype(cl_t cl) 244 1.1 ws { 245 1.9 ws if (cl == CLUST_FREE) 246 1.9 ws return "free"; 247 1.1 ws if (cl < CLUST_BAD) 248 1.1 ws return "reserved"; 249 1.1 ws if (cl > CLUST_BAD) 250 1.1 ws return "as EOF"; 251 1.1 ws return "bad"; 252 1.1 ws } 253 1.1 ws 254 1.1 ws static int 255 1.22 lukem clustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, u_int fatnum) 256 1.1 ws { 257 1.9 ws if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) { 258 1.9 ws if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 259 1.9 ws if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD 260 1.9 ws && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD) 261 1.1 ws || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) { 262 1.8 ws pwarn("Cluster %u is marked %s with different indicators, ", 263 1.1 ws cl, rsrvdcltype(*cp1)); 264 1.1 ws if (ask(1, "fix")) { 265 1.1 ws *cp2 = *cp1; 266 1.1 ws return FSFATMOD; 267 1.1 ws } 268 1.1 ws return FSFATAL; 269 1.1 ws } 270 1.22 lukem pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %u\n", 271 1.1 ws cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum); 272 1.8 ws if (ask(0, "use FAT 0's entry")) { 273 1.1 ws *cp2 = *cp1; 274 1.1 ws return FSFATMOD; 275 1.1 ws } 276 1.22 lukem if (ask(0, "use FAT %u's entry", fatnum)) { 277 1.1 ws *cp1 = *cp2; 278 1.1 ws return FSFATMOD; 279 1.1 ws } 280 1.1 ws return FSFATAL; 281 1.1 ws } 282 1.22 lukem pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %u\n", 283 1.1 ws cl, rsrvdcltype(*cp1), *cp2, fatnum); 284 1.22 lukem if (ask(0, "Use continuation from FAT %u", fatnum)) { 285 1.1 ws *cp1 = *cp2; 286 1.1 ws return FSFATMOD; 287 1.1 ws } 288 1.8 ws if (ask(0, "Use mark from FAT 0")) { 289 1.1 ws *cp2 = *cp1; 290 1.1 ws return FSFATMOD; 291 1.1 ws } 292 1.1 ws return FSFATAL; 293 1.1 ws } 294 1.9 ws if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 295 1.22 lukem pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %u\n", 296 1.1 ws cl, *cp1, rsrvdcltype(*cp2), fatnum); 297 1.8 ws if (ask(0, "Use continuation from FAT 0")) { 298 1.1 ws *cp2 = *cp1; 299 1.1 ws return FSFATMOD; 300 1.1 ws } 301 1.22 lukem if (ask(0, "Use mark from FAT %u", fatnum)) { 302 1.1 ws *cp1 = *cp2; 303 1.1 ws return FSFATMOD; 304 1.1 ws } 305 1.1 ws return FSERROR; 306 1.1 ws } 307 1.22 lukem pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %u\n", 308 1.1 ws cl, *cp1, *cp2, fatnum); 309 1.8 ws if (ask(0, "Use continuation from FAT 0")) { 310 1.1 ws *cp2 = *cp1; 311 1.1 ws return FSFATMOD; 312 1.1 ws } 313 1.22 lukem if (ask(0, "Use continuation from FAT %u", fatnum)) { 314 1.1 ws *cp1 = *cp2; 315 1.1 ws return FSFATMOD; 316 1.1 ws } 317 1.1 ws return FSERROR; 318 1.1 ws } 319 1.1 ws 320 1.1 ws /* 321 1.1 ws * Compare two FAT copies in memory. Resolve any conflicts and merge them 322 1.1 ws * into the first one. 323 1.1 ws */ 324 1.1 ws int 325 1.21 matthias comparefat(struct bootblock *boot, struct fatEntry *first, 326 1.22 lukem struct fatEntry *second, u_int fatnum) 327 1.1 ws { 328 1.1 ws cl_t cl; 329 1.1 ws int ret = FSOK; 330 1.1 ws 331 1.1 ws for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) 332 1.1 ws if (first[cl].next != second[cl].next) 333 1.1 ws ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum); 334 1.1 ws return ret; 335 1.1 ws } 336 1.1 ws 337 1.1 ws void 338 1.16 xtraeme clearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head) 339 1.1 ws { 340 1.1 ws cl_t p, q; 341 1.1 ws 342 1.1 ws for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) { 343 1.1 ws if (fat[p].head != head) 344 1.1 ws break; 345 1.1 ws q = fat[p].next; 346 1.1 ws fat[p].next = fat[p].head = CLUST_FREE; 347 1.1 ws fat[p].length = 0; 348 1.1 ws } 349 1.1 ws } 350 1.1 ws 351 1.9 ws int 352 1.16 xtraeme tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *truncp) 353 1.9 ws { 354 1.9 ws if (ask(0, "Clear chain starting at %u", head)) { 355 1.9 ws clearchain(boot, fat, head); 356 1.9 ws return FSFATMOD; 357 1.9 ws } else if (ask(0, "Truncate")) { 358 1.25 christos uint32_t len; 359 1.25 christos cl_t p; 360 1.25 christos 361 1.25 christos for (p = head, len = 0; 362 1.25 christos p >= CLUST_FIRST && p < boot->NumClusters; 363 1.25 christos p = fat[p].next, len++) 364 1.25 christos continue; 365 1.15 matt *truncp = CLUST_EOF; 366 1.25 christos fat[head].length = len; 367 1.9 ws return FSFATMOD; 368 1.9 ws } else 369 1.9 ws return FSERROR; 370 1.9 ws } 371 1.9 ws 372 1.1 ws /* 373 1.1 ws * Check a complete FAT in-memory for crosslinks 374 1.1 ws */ 375 1.1 ws int 376 1.16 xtraeme checkfat(struct bootblock *boot, struct fatEntry *fat) 377 1.1 ws { 378 1.9 ws cl_t head, p, h, n; 379 1.1 ws u_int len; 380 1.1 ws int ret = 0; 381 1.1 ws int conf; 382 1.8 ws 383 1.1 ws /* 384 1.1 ws * pass 1: figure out the cluster chains. 385 1.1 ws */ 386 1.1 ws for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 387 1.8 ws /* find next untravelled chain */ 388 1.6 ws if (fat[head].head != 0 /* cluster already belongs to some chain */ 389 1.5 ws || fat[head].next == CLUST_FREE 390 1.5 ws || fat[head].next == CLUST_BAD) 391 1.1 ws continue; /* skip it. */ 392 1.1 ws 393 1.1 ws /* follow the chain and mark all clusters on the way */ 394 1.1 ws for (len = 0, p = head; 395 1.29 christos p >= CLUST_FIRST && p < boot->NumClusters && 396 1.29 christos fat[p].head != head; 397 1.1 ws p = fat[p].next) { 398 1.1 ws fat[p].head = head; 399 1.1 ws len++; 400 1.1 ws } 401 1.1 ws 402 1.1 ws /* the head record gets the length */ 403 1.8 ws fat[head].length = fat[head].next == CLUST_FREE ? 0 : len; 404 1.1 ws } 405 1.8 ws 406 1.1 ws /* 407 1.1 ws * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because 408 1.1 ws * we didn't know the real start of the chain then - would have treated partial 409 1.1 ws * chains as interlinked with their main chain) 410 1.1 ws */ 411 1.1 ws for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 412 1.8 ws /* find next untravelled chain */ 413 1.1 ws if (fat[head].head != head) 414 1.1 ws continue; 415 1.1 ws 416 1.1 ws /* follow the chain to its end (hopefully) */ 417 1.26 christos for (len = fat[head].length, p = head; 418 1.9 ws (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters; 419 1.9 ws p = n) 420 1.26 christos if (fat[n].head != head || len-- < 2) 421 1.1 ws break; 422 1.9 ws if (n >= CLUST_EOFS) 423 1.1 ws continue; 424 1.8 ws 425 1.9 ws if (n == CLUST_FREE || n >= CLUST_RSRVD) { 426 1.8 ws pwarn("Cluster chain starting at %u ends with cluster marked %s\n", 427 1.9 ws head, rsrvdcltype(n)); 428 1.26 christos clear: 429 1.9 ws ret |= tryclear(boot, fat, head, &fat[p].next); 430 1.1 ws continue; 431 1.1 ws } 432 1.9 ws if (n < CLUST_FIRST || n >= boot->NumClusters) { 433 1.8 ws pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n", 434 1.26 christos head, n); 435 1.26 christos goto clear; 436 1.26 christos } 437 1.26 christos if (head == fat[n].head) { 438 1.26 christos pwarn("Cluster chain starting at %u loops at cluster %u\n", 439 1.26 christos 440 1.26 christos head, p); 441 1.26 christos goto clear; 442 1.1 ws } 443 1.8 ws pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n", 444 1.9 ws head, fat[n].head, n); 445 1.9 ws conf = tryclear(boot, fat, head, &fat[p].next); 446 1.9 ws if (ask(0, "Clear chain starting at %u", h = fat[n].head)) { 447 1.1 ws if (conf == FSERROR) { 448 1.1 ws /* 449 1.1 ws * Transfer the common chain to the one not cleared above. 450 1.1 ws */ 451 1.9 ws for (p = n; 452 1.9 ws p >= CLUST_FIRST && p < boot->NumClusters; 453 1.1 ws p = fat[p].next) { 454 1.1 ws if (h != fat[p].head) { 455 1.1 ws /* 456 1.1 ws * Have to reexamine this chain. 457 1.1 ws */ 458 1.1 ws head--; 459 1.1 ws break; 460 1.1 ws } 461 1.1 ws fat[p].head = head; 462 1.1 ws } 463 1.1 ws } 464 1.1 ws clearchain(boot, fat, h); 465 1.1 ws conf |= FSFATMOD; 466 1.1 ws } 467 1.1 ws ret |= conf; 468 1.1 ws } 469 1.1 ws 470 1.1 ws return ret; 471 1.1 ws } 472 1.1 ws 473 1.1 ws /* 474 1.1 ws * Write out FATs encoding them from the internal format 475 1.1 ws */ 476 1.1 ws int 477 1.16 xtraeme writefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat) 478 1.1 ws { 479 1.1 ws u_char *buffer, *p; 480 1.1 ws cl_t cl; 481 1.22 lukem u_int i; 482 1.19 christos size_t fatsz; 483 1.1 ws off_t off; 484 1.1 ws int ret = FSOK; 485 1.8 ws 486 1.1 ws buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec); 487 1.1 ws if (buffer == NULL) { 488 1.18 christos perr("No space for FAT sectors (%zu)", fatsz); 489 1.1 ws return FSFATAL; 490 1.1 ws } 491 1.1 ws memset(buffer, 0, fatsz); 492 1.1 ws boot->NumFree = 0; 493 1.6 ws p = buffer; 494 1.10 jdolecek if (correct_fat) { 495 1.10 jdolecek *p++ = (u_char)boot->Media; 496 1.8 ws *p++ = 0xff; 497 1.8 ws *p++ = 0xff; 498 1.10 jdolecek switch (boot->ClustMask) { 499 1.10 jdolecek case CLUST16_MASK: 500 1.10 jdolecek *p++ = 0xff; 501 1.10 jdolecek break; 502 1.10 jdolecek case CLUST32_MASK: 503 1.10 jdolecek *p++ = 0x0f; 504 1.10 jdolecek *p++ = 0xff; 505 1.10 jdolecek *p++ = 0xff; 506 1.10 jdolecek *p++ = 0xff; 507 1.10 jdolecek *p++ = 0x0f; 508 1.10 jdolecek break; 509 1.10 jdolecek } 510 1.10 jdolecek } else { 511 1.10 jdolecek /* use same FAT signature as the old FAT has */ 512 1.10 jdolecek int count; 513 1.10 jdolecek u_char *old_fat; 514 1.10 jdolecek 515 1.10 jdolecek switch (boot->ClustMask) { 516 1.10 jdolecek case CLUST32_MASK: 517 1.10 jdolecek count = 8; 518 1.10 jdolecek break; 519 1.10 jdolecek case CLUST16_MASK: 520 1.10 jdolecek count = 4; 521 1.10 jdolecek break; 522 1.10 jdolecek default: 523 1.10 jdolecek count = 3; 524 1.10 jdolecek break; 525 1.10 jdolecek } 526 1.10 jdolecek 527 1.10 jdolecek if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0, 528 1.10 jdolecek &old_fat)) { 529 1.10 jdolecek free(buffer); 530 1.10 jdolecek return FSFATAL; 531 1.10 jdolecek } 532 1.10 jdolecek 533 1.10 jdolecek memcpy(p, old_fat, count); 534 1.10 jdolecek free(old_fat); 535 1.10 jdolecek p += count; 536 1.8 ws } 537 1.21 matthias 538 1.6 ws for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) { 539 1.8 ws switch (boot->ClustMask) { 540 1.8 ws case CLUST32_MASK: 541 1.8 ws if (fat[cl].next == CLUST_FREE) 542 1.8 ws boot->NumFree++; 543 1.8 ws *p++ = (u_char)fat[cl].next; 544 1.8 ws *p++ = (u_char)(fat[cl].next >> 8); 545 1.8 ws *p++ = (u_char)(fat[cl].next >> 16); 546 1.8 ws *p &= 0xf0; 547 1.8 ws *p++ |= (fat[cl].next >> 24)&0x0f; 548 1.8 ws break; 549 1.8 ws case CLUST16_MASK: 550 1.1 ws if (fat[cl].next == CLUST_FREE) 551 1.1 ws boot->NumFree++; 552 1.8 ws *p++ = (u_char)fat[cl].next; 553 1.8 ws *p++ = (u_char)(fat[cl].next >> 8); 554 1.8 ws break; 555 1.8 ws default: 556 1.1 ws if (fat[cl].next == CLUST_FREE) 557 1.1 ws boot->NumFree++; 558 1.27 christos *p++ = (u_char)fat[cl].next; 559 1.28 christos *p = (u_char)((fat[cl].next >> 8) & 0xf); 560 1.27 christos cl++; 561 1.27 christos if (cl >= boot->NumClusters) 562 1.27 christos break; 563 1.27 christos if (fat[cl].next == CLUST_FREE) 564 1.1 ws boot->NumFree++; 565 1.30 christos *p++ |= (u_char)(fat[cl].next << 4); 566 1.30 christos *p++ = (u_char)(fat[cl].next >> 4); 567 1.8 ws break; 568 1.1 ws } 569 1.1 ws } 570 1.1 ws for (i = 0; i < boot->FATs; i++) { 571 1.1 ws off = boot->ResSectors + i * boot->FATsecs; 572 1.1 ws off *= boot->BytesPerSec; 573 1.1 ws if (lseek(fs, off, SEEK_SET) != off 574 1.22 lukem || (size_t)write(fs, buffer, fatsz) != fatsz) { 575 1.18 christos perr("Unable to write FAT"); 576 1.1 ws ret = FSFATAL; /* Return immediately? XXX */ 577 1.1 ws } 578 1.1 ws } 579 1.1 ws free(buffer); 580 1.1 ws return ret; 581 1.1 ws } 582 1.1 ws 583 1.1 ws /* 584 1.1 ws * Check a complete in-memory FAT for lost cluster chains 585 1.1 ws */ 586 1.1 ws int 587 1.16 xtraeme checklost(int dosfs, struct bootblock *boot, struct fatEntry *fat) 588 1.1 ws { 589 1.1 ws cl_t head; 590 1.1 ws int mod = FSOK; 591 1.8 ws int ret; 592 1.21 matthias 593 1.1 ws for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 594 1.8 ws /* find next untravelled chain */ 595 1.1 ws if (fat[head].head != head 596 1.1 ws || fat[head].next == CLUST_FREE 597 1.1 ws || (fat[head].next >= CLUST_RSRVD 598 1.2 ws && fat[head].next < CLUST_EOFS) 599 1.2 ws || (fat[head].flags & FAT_USED)) 600 1.1 ws continue; 601 1.1 ws 602 1.8 ws pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n", 603 1.2 ws head, fat[head].length); 604 1.8 ws mod |= ret = reconnect(dosfs, boot, fat, head); 605 1.2 ws if (mod & FSFATAL) 606 1.2 ws break; 607 1.8 ws if (ret == FSERROR && ask(0, "Clear")) { 608 1.8 ws clearchain(boot, fat, head); 609 1.8 ws mod |= FSFATMOD; 610 1.8 ws } 611 1.1 ws } 612 1.1 ws finishlf(); 613 1.8 ws 614 1.8 ws if (boot->FSInfo) { 615 1.8 ws ret = 0; 616 1.23 jakllsch if (boot->FSFree != 0xffffffffU && 617 1.23 jakllsch boot->FSFree != boot->NumFree) { 618 1.23 jakllsch pwarn("Free space in FSInfo block (%u) not correct (%u)\n", 619 1.8 ws boot->FSFree, boot->NumFree); 620 1.8 ws if (ask(1, "fix")) { 621 1.8 ws boot->FSFree = boot->NumFree; 622 1.8 ws ret = 1; 623 1.8 ws } 624 1.8 ws } 625 1.24 jakllsch if (boot->FSNext != 0xffffffffU && 626 1.24 jakllsch (boot->FSNext >= boot->NumClusters || 627 1.24 jakllsch (boot->NumFree && fat[boot->FSNext].next != CLUST_FREE))) { 628 1.21 matthias pwarn("Next free cluster in FSInfo block (%u) %s\n", 629 1.21 matthias boot->FSNext, 630 1.21 matthias (boot->FSNext >= boot->NumClusters) ? "invalid" : "not free"); 631 1.8 ws if (ask(1, "fix")) 632 1.8 ws for (head = CLUST_FIRST; head < boot->NumClusters; head++) 633 1.8 ws if (fat[head].next == CLUST_FREE) { 634 1.8 ws boot->FSNext = head; 635 1.8 ws ret = 1; 636 1.8 ws break; 637 1.8 ws } 638 1.8 ws } 639 1.8 ws if (ret) 640 1.8 ws mod |= writefsinfo(dosfs, boot); 641 1.8 ws } 642 1.8 ws 643 1.1 ws return mod; 644 1.1 ws } 645