diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1996-05-22 11:23:57 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1996-05-22 11:23:57 +0000 |
commit | 11a765303f21eabf78addcce4a9d0b96915bfa48 (patch) | |
tree | 927e2beea471f9f50297e1564f62a1df9ce475c1 | |
parent | dcbd4928bd97b1e9011c207e5cab1da61b369a5d (diff) |
add fsck_msdos
-rw-r--r-- | sbin/Makefile | 6 | ||||
-rw-r--r-- | sbin/fsck_msdos/Makefile | 7 | ||||
-rw-r--r-- | sbin/fsck_msdos/boot.c | 117 | ||||
-rw-r--r-- | sbin/fsck_msdos/check.c | 196 | ||||
-rw-r--r-- | sbin/fsck_msdos/dir.c | 760 | ||||
-rw-r--r-- | sbin/fsck_msdos/dosfs.h | 123 | ||||
-rw-r--r-- | sbin/fsck_msdos/ext.h | 153 | ||||
-rw-r--r-- | sbin/fsck_msdos/fat.c | 514 | ||||
-rw-r--r-- | sbin/fsck_msdos/fsck_msdos.8 | 76 | ||||
-rw-r--r-- | sbin/fsck_msdos/main.c | 252 |
10 files changed, 2201 insertions, 3 deletions
diff --git a/sbin/Makefile b/sbin/Makefile index 0e32ab9c6ff..312cadd00da 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -1,5 +1,5 @@ -# $OpenBSD: Makefile,v 1.9 1996/05/17 03:47:30 dm Exp $ -# $NetBSD: Makefile,v 1.28 1996/04/05 01:44:24 cgd Exp $ +# $OpenBSD: Makefile,v 1.10 1996/05/22 11:23:51 deraadt Exp $ +# $NetBSD: Makefile,v 1.29 1996/05/14 17:39:21 ws Exp $ # @(#)Makefile 8.5 (Berkeley) 3/31/94 # Not ported: XNSrouted enpload scsiformat startslip @@ -18,7 +18,7 @@ SUBDIR+= mount_ffs newfs fsck_ffs fsdb dumpfs dump restore clri tunefs SUBDIR+= mount_kernfs SUBDIR+= mount_lfs newlfs dumplfs # mount_mfs -> newfs -SUBDIR+= mount_msdos +SUBDIR+= mount_msdos fsck_msdos SUBDIR+= mount_nfs SUBDIR+= mount_null SUBDIR+= mount_portal diff --git a/sbin/fsck_msdos/Makefile b/sbin/fsck_msdos/Makefile new file mode 100644 index 00000000000..1c394d0e16a --- /dev/null +++ b/sbin/fsck_msdos/Makefile @@ -0,0 +1,7 @@ +# $NetBSD: Makefile,v 1.1 1996/05/14 17:39:26 ws Exp $ + +PROG= fsck_msdos +MAN= fsck_msdos.8 +SRCS= main.c check.c boot.c fat.c dir.c + +.include <bsd.prog.mk> diff --git a/sbin/fsck_msdos/boot.c b/sbin/fsck_msdos/boot.c new file mode 100644 index 00000000000..17304504540 --- /dev/null +++ b/sbin/fsck_msdos/boot.c @@ -0,0 +1,117 @@ +/* $NetBSD: boot.c,v 1.1 1996/05/14 17:39:28 ws Exp $ */ + +/* + * Copyright (C) 1995 Wolfgang Solfrank + * Copyright (c) 1995 Martin Husemann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Martin Husemann + * and Wolfgang Solfrank. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef lint +static char rcsid[] = "$NetBSD: boot.c,v 1.1 1996/05/14 17:39:28 ws Exp $"; +#endif /* not lint */ + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include <unistd.h> + +#include "ext.h" + +int +readboot(dosfs, boot) + int dosfs; + struct bootblock *boot; +{ + u_char block[DOSBOOTBLOCKSIZE]; + int n; + + if ((n = read(dosfs, block, sizeof block)) < (int)sizeof block) { + if (n < 0) + perror("could not read boot block"); + else + pfatal("Short bootblock?"); + return FSFATAL; + } + + /* decode bios parameter block */ + boot->BytesPerSec = block[11] + (block[12] << 8); + boot->SecPerClust = block[13]; + boot->ResSectors = block[14] + (block[15] << 8); + boot->FATs = block[16]; + boot->RootDirEnts = block[17] + (block[18] << 8); + boot->Sectors = block[19] + (block[20] << 8); + boot->Media = block[21]; + boot->FATsecs = block[22] + (block[23] << 8); + boot->SecPerTrack = block[24] + (block[25] << 8); + boot->Heads = block[26] + (block[27] << 8); + boot->HiddenSecs = block[28] + (block[29] << 8) + (block[30] << 16) + (block[31] << 24); + boot->HugeSectors = block[32] + (block[33] << 8) + (block[34] << 16) + (block[35] << 24); + boot->ClusterOffset = (boot->RootDirEnts * 32 + boot->BytesPerSec - 1) + / boot->BytesPerSec + + boot->ResSectors + + boot->FATs * boot->FATsecs + - CLUST_FIRST * boot->SecPerClust; + + if (boot->BytesPerSec % DOSBOOTBLOCKSIZE != 0) { + pfatal("Invalid sector size: %u\n", boot->BytesPerSec); + return FSFATAL; + } + if (boot->SecPerClust == 0) { + pfatal("Invalid cluster size: %u\n", boot->SecPerClust); + return FSFATAL; + } + if (boot->Sectors) { + boot->HugeSectors = 0; + boot->NumSectors = boot->Sectors; + } else + boot->NumSectors = boot->HugeSectors; + boot->NumClusters = (boot->NumSectors - boot->ClusterOffset) / boot->SecPerClust; + if (boot->NumClusters >= MAX12BITCLUSTERS) + boot->Is16BitFat = 1; + else + boot->Is16BitFat = 0; + + if (boot->Is16BitFat) + boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec) / 2; + else + boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec * 2) / 3; + if (boot->NumFatEntries < boot->NumClusters) { + pfatal("FAT size too small, %d entries won't fit into %u sectors\n", + boot->NumClusters, boot->FATsecs); + return FSFATAL; + } + boot->ClusterSize = boot->BytesPerSec * boot->SecPerClust; + + boot->NumFiles = 1; + boot->NumFree = 0; + + return FSOK; +} diff --git a/sbin/fsck_msdos/check.c b/sbin/fsck_msdos/check.c new file mode 100644 index 00000000000..adf41300c79 --- /dev/null +++ b/sbin/fsck_msdos/check.c @@ -0,0 +1,196 @@ +/* $NetBSD: check.c,v 1.1 1996/05/14 17:39:29 ws Exp $ */ + +/* + * Copyright (C) 1995, 1996 Wolfgang Solfrank + * Copyright (c) 1995 Martin Husemann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Martin Husemann + * and Wolfgang Solfrank. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef lint +static char rcsid[] = "$NetBSD: check.c,v 1.1 1996/05/14 17:39:29 ws Exp $"; +#endif /* not lint */ + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> + +#include "ext.h" + +int +checkfilesys(fname) + const char *fname; +{ + int dosfs; + struct dosDirEntry *rootDir; + struct bootblock boot; + struct fatEntry * fat = NULL; + int i; + int mod = 0; + + rdonly = alwaysno; + if (!preen) + printf("** %s", fname); + + dosfs = open(fname, rdonly ? O_RDONLY : O_RDWR, 0); + if (dosfs < 0 && !rdonly) { + dosfs = open(fname, O_RDONLY, 0); + if (dosfs >= 0) + pwarn(" (NO WRITE)\n"); + else if (!preen) + printf("\n"); + rdonly = 1; + } else if (!preen) + printf("\n"); + + if (dosfs < 0) { + perror("Can't open"); + return 8; + } + + if (readboot(dosfs, &boot) != FSOK) { + close(dosfs); + return 8; + } + + if (!preen) + printf("** Phase 1 - Read and Compare FATs\n"); + + for (i = 0; i < boot.FATs; i++) { + struct fatEntry *currentFat; + + mod |= readfat(dosfs, &boot, i, ¤tFat); + + if (mod&FSFATAL) { + close(dosfs); + return 8; + } + + if (fat == NULL) + fat = currentFat; + else { + mod |= comparefat(&boot, fat, currentFat, i + 1); + if (mod&FSFATAL) { + close(dosfs); + return 8; + } + } + } + + if (!preen) + printf("** Phase 2 - Check Cluster Chains\n"); + + mod |= checkfat(&boot, fat); + if (mod&FSFATAL) { + close(dosfs); + return 8; + } + + if (mod&FSFATMOD) + mod |= writefat(dosfs, &boot, fat); /* delay writing fats? XXX */ + if (mod&FSFATAL) { + close(dosfs); + return 8; + } + + if (!preen) + printf("** Phase 3 - Checking Directories\n"); + + rootDir = malloc(sizeof(struct dosDirEntry)); + memset(rootDir, 0, sizeof(struct dosDirEntry)); + rootDir->fullpath = strdup("/"); + if (resetDosDirSection(&boot)&FSFATAL) { + close(dosfs); + return 8; + } + + mod = readDosDirSection(dosfs, &boot, fat, rootDir); + if (mod&FSFATAL) { + close(dosfs); + return 8; + } + + if (mod&FSFATMOD) + mod |= writefat(dosfs, &boot, fat); /* delay writing fats? XXX */ + if (mod&FSFATAL) { + close(dosfs); + return 8; + } + + /* + * process the directory todo list + */ + while (pendingDirectories) { + struct dosDirEntry *dir = pendingDirectories->dir; + struct dirTodoNode *n = pendingDirectories->next; + + /* + * remove TODO entry now, the list might change during + * directory reads + */ + free(pendingDirectories); + pendingDirectories = n; + + /* + * handle subdirectory + */ + mod |= readDosDirSection(dosfs, &boot, fat, dir); + if (mod&FSFATAL) { + close(dosfs); + return 8; + } + if (mod&FSFATMOD) + mod |= writefat(dosfs, &boot, fat); /* delay writing fats? XXX */ + if (mod&FSFATAL) { + close(dosfs); + return 8; + } + } + finishDosDirSection(); + + if (!preen) + printf("** Phase 4 - Checking for Lost Files\n"); + + mod |= checklost(dosfs, &boot, fat, rootDir); + + close(dosfs); + pwarn("%d files, %d free (%d clusters)\n", + boot.NumFiles, boot.NumFree * boot.ClusterSize / 1024, + boot.NumFree); + if (mod&(FSFATAL|FSERROR)) + return 8; + if (mod) { + pwarn("\n***** FILE SYSTEM WAS MODIFIED *****\n"); + return 4; + } + return 0; +} diff --git a/sbin/fsck_msdos/dir.c b/sbin/fsck_msdos/dir.c new file mode 100644 index 00000000000..d0b205cf98f --- /dev/null +++ b/sbin/fsck_msdos/dir.c @@ -0,0 +1,760 @@ +/* $NetBSD: dir.c,v 1.1 1996/05/14 17:39:30 ws Exp $ */ + +/* + * Copyright (C) 1995, 1996 Wolfgang Solfrank + * Copyright (c) 1995 Martin Husemann + * Some structure declaration borrowed from Paul Popelka + * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Martin Husemann + * and Wolfgang Solfrank. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef lint +static char rcsid[] = "$NetBSD: dir.c,v 1.1 1996/05/14 17:39:30 ws Exp $"; +#endif /* not lint */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include <unistd.h> +#include <time.h> + +#include "ext.h" + +#define SLOT_EMPTY 0x00 /* slot has never been used */ +#define SLOT_E5 0x05 /* the real value is 0xe5 */ +#define SLOT_DELETED 0xe5 /* file in this slot deleted */ + +#define ATTR_NORMAL 0x00 /* normal file */ +#define ATTR_READONLY 0x01 /* file is readonly */ +#define ATTR_HIDDEN 0x02 /* file is hidden */ +#define ATTR_SYSTEM 0x04 /* file is a system file */ +#define ATTR_VOLUME 0x08 /* entry is a volume label */ +#define ATTR_DIRECTORY 0x10 /* entry is a directory name */ +#define ATTR_ARCHIVE 0x20 /* file is new or modified */ + +#define ATTR_WIN95 0x0f /* long name record */ + +/* + * This is the format of the contents of the deTime field in the direntry + * structure. + * We don't use bitfields because we don't know how compilers for + * arbitrary machines will lay them out. + */ +#define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ +#define DT_2SECONDS_SHIFT 0 +#define DT_MINUTES_MASK 0x7E0 /* minutes */ +#define DT_MINUTES_SHIFT 5 +#define DT_HOURS_MASK 0xF800 /* hours */ +#define DT_HOURS_SHIFT 11 + +/* + * This is the format of the contents of the deDate field in the direntry + * structure. + */ +#define DD_DAY_MASK 0x1F /* day of month */ +#define DD_DAY_SHIFT 0 +#define DD_MONTH_MASK 0x1E0 /* month */ +#define DD_MONTH_SHIFT 5 +#define DD_YEAR_MASK 0xFE00 /* year - 1980 */ +#define DD_YEAR_SHIFT 9 + +/* + * Calculate a checksum over an 8.3 alias name + */ +static u_char +calcShortSum(p) + u_char *p; +{ + u_char sum = 0; + int i; + + for (i = 0; i < 11; i++) { + sum = (sum << 7)|(sum >> 1); /* rotate right */ + sum += p[i]; + } + + return sum; +} + +/* + * Global variables temporarily used during a directory scan + */ +static char longName[DOSLONGNAMELEN] = ""; +static u_char *buffer = NULL; +static u_char *delbuf = NULL; + +/* + * Init internal state for a new directory scan. + */ +int +resetDosDirSection(boot) + struct bootblock *boot; +{ + int b1, b2; + + b1 = boot->RootDirEnts * 32; + b2 = boot->SecPerClust * boot->BytesPerSec; + + if (!(buffer = malloc(b1 > b2 ? b1 : b2)) + || !(delbuf = malloc(b2))) { + perror("No space for directory"); + return FSFATAL; + } + return FSOK; +} + +/* + * Cleanup after a directory scan + */ +void +finishDosDirSection() +{ + free(buffer); + free(delbuf); + buffer = NULL; + delbuf = NULL; +} + +/* + * Delete directory entries between startcl, startoff and endcl, endoff. + */ +static int +delete(f, boot, fat, startcl, startoff, endcl, endoff, notlast) + int f; + struct bootblock *boot; + struct fatEntry *fat; + cl_t startcl; + int startoff; + cl_t endcl; + int endoff; + int notlast; +{ + u_char *s, *e; + off_t off; + int clsz = boot->SecPerClust * boot->BytesPerSec; + + s = delbuf + startoff; + e = delbuf + clsz; + while (startcl >= CLUST_FIRST && startcl < boot->NumClusters) { + if (startcl == endcl) { + if (notlast) + break; + e = delbuf + endoff; + } + off = startcl * boot->SecPerClust + boot->ClusterOffset; + off *= boot->BytesPerSec; + if (lseek(f, off, SEEK_SET) != off + || read(f, delbuf, clsz) != clsz) { + perror("Unable to read directory"); + return FSFATAL; + } + while (s < e) { + *s = SLOT_DELETED; + s += 32; + } + if (lseek(f, off, SEEK_SET) != off + || write(f, delbuf, clsz) != clsz) { + perror("Unable to write directory"); + return FSFATAL; + } + if (startcl == endcl) + break; + startcl = fat[startcl].next; + s = delbuf; + } + return FSOK; +} + +static int +removede(f, boot, fat, start, end, startcl, endcl, curcl, path, eof) + int f; + struct bootblock *boot; + struct fatEntry *fat; + u_char *start; + u_char *end; + cl_t startcl; + cl_t endcl; + cl_t curcl; + char *path; + int eof; +{ + if (!eof) + pwarn("Invalid long filename entry for %s\n", path); + else + pwarn("Invalid long filename entry at end of directory %s\n", path); + if (ask(0, "Remove")) { + if (startcl != curcl) { + if (delete(f, boot, fat, + startcl, start - buffer, + endcl, end - buffer, + endcl == curcl) == FSFATAL) + return FSFATAL; + start = buffer; + } + if (endcl == curcl) + for (; start < end; start += 32) + *start = SLOT_DELETED; + return FSDIRMOD; + } + return FSERROR; +} + +/* + * Check an in-memory file entry + */ +static int +checksize(boot, fat, p, dir) + struct bootblock *boot; + struct fatEntry *fat; + u_char *p; + struct dosDirEntry *dir; +{ + /* + * Check size on ordinary files + */ + u_int32_t physicalSize; + + if (dir->head < CLUST_FIRST || dir->head >= boot->NumClusters) + return FSERROR; + physicalSize = fat[dir->head].length * boot->ClusterSize; + if (physicalSize < dir->size) { + pwarn("size of %s is %lu, should at most be %lu\n", + dir->fullpath, dir->size, physicalSize); + if (ask(1, "Truncate")) { + dir->size = physicalSize; + p[28] = (u_char)physicalSize; + p[29] = (u_char)(physicalSize >> 8); + p[30] = (u_char)(physicalSize >> 16); + p[31] = (u_char)(physicalSize >> 24); + return FSDIRMOD; + } else + return FSERROR; + } else if (physicalSize - dir->size >= boot->ClusterSize) { + pwarn("%s has too many clusters allocated\n", + dir->fullpath); + if (ask(1, "Drop superfluous clusters")) { + cl_t cl; + u_int32_t sz = 0; + + for (cl = dir->head; (sz += boot->ClusterSize) < dir->size;) + cl = fat[cl].next; + clearchain(boot, fat, fat[cl].next); + fat[cl].next = CLUST_EOF; + return FSFATMOD; + } else + return FSERROR; + } + return FSOK; +} + +/* + * The stack of unread directories + */ +struct dirTodoNode *pendingDirectories = NULL; + +/* + * Read a directory and + * - resolve long name records + * - enter file and directory records into the parent's list + * - push directories onto the todo-stack + */ +int +readDosDirSection(f, boot, fat, dir) + int f; + struct bootblock *boot; + struct fatEntry *fat; + struct dosDirEntry *dir; +{ + struct dosDirEntry dirent, *d; + u_char *p, *vallfn, *invlfn, *empty; + off_t off; + int i, j, k, last; + cl_t cl, valcl, invcl, empcl; + char *t; + u_int lidx = 0; + int shortSum; + int mod = FSOK; +#define THISMOD 0x8000 /* Only used within this routine */ + + cl = dir->head; + if (dir->fullpath[1] && (cl < CLUST_FIRST || cl >= boot->NumClusters)) { + /* + * Already handled somewhere else. + */ + return FSOK; + } + shortSum = -1; + vallfn = invlfn = empty = NULL; + do { + if (!dir->fullpath[1]) { + last = boot->RootDirEnts * 32; + off = boot->ResSectors + boot->FATs * boot->FATsecs; + } else { + last = boot->SecPerClust * boot->BytesPerSec; + off = cl * boot->SecPerClust + boot->ClusterOffset; + } + + off *= boot->BytesPerSec; + if (lseek(f, off, SEEK_SET) != off + || read(f, buffer, last) != last) { + perror("Unable to read directory"); + return FSFATAL; + } + last /= 32; + /* + * Check `.' and `..' entries here? XXX + */ + for (p = buffer, i = 0; i < last; i++, p += 32) { + if (dir->fsckflags & DIREMPWARN) { + *p = SLOT_EMPTY; + continue; + } + + if (*p == SLOT_EMPTY || *p == SLOT_DELETED) { + if (*p == SLOT_EMPTY) { + dir->fsckflags |= DIREMPTY; + empty = p; + empcl = cl; + } + continue; + } + + if (dir->fsckflags & DIREMPTY) { + if (!(dir->fsckflags & DIREMPWARN)) { + pwarn("%s has entries after end of directory\n", + dir->fullpath); + if (ask(1, "Extend")) { + dir->fsckflags &= ~DIREMPTY; + if (delete(f, boot, fat, + empcl, empty - buffer, + cl, p - buffer) == FSFATAL) + return FSFATAL; + } else if (ask(0, "Truncate")) + dir->fsckflags |= DIREMPWARN; + } + if (dir->fsckflags & DIREMPWARN) { + *p = SLOT_DELETED; + mod |= THISMOD|FSDIRMOD; + continue; + } else if (dir->fsckflags & DIREMPTY) + mod |= FSERROR; + empty = NULL; + } + + if (p[11] == ATTR_WIN95) { + if (*p & LRFIRST) { + if (shortSum != -1) { + if (!invlfn) { + invlfn = vallfn; + invcl = valcl; + } + } + memset(longName, 0, sizeof longName); + shortSum = p[13]; + vallfn = p; + valcl = cl; + } else if (shortSum != p[13] + || lidx != *p & LRNOMASK) { + if (!invlfn) { + invlfn = vallfn; + invcl = valcl; + } + if (!invlfn) { + invlfn = p; + invcl = cl; + } + vallfn = NULL; + } + lidx = *p & LRNOMASK; + t = longName + --lidx * 13; + for (k = 1; k < 11 && t < longName + sizeof(longName); k += 2) { + if (!p[k] && !p[k + 1]) + break; + *t++ = p[k]; + /* + * Warn about those unusable chars in msdosfs here? XXX + */ + if (p[k + 1]) + t[-1] = '?'; + } + if (k >= 11) + for (k = 14; k < 26 && t < longName + sizeof(longName); k += 2) { + if (!p[k] && !p[k + 1]) + break; + *t++ = p[k]; + if (p[k + 1]) + t[-1] = '?'; + } + if (k >= 26) + for (k = 28; k < 32 && t < longName + sizeof(longName); k += 2) { + if (!p[k] && !p[k + 1]) + break; + *t++ = p[k]; + if (p[k + 1]) + t[-1] = '?'; + } + if (t >= longName + sizeof(longName)) { + pwarn("long filename too long\n"); + if (!invlfn) { + invlfn = vallfn; + invcl = valcl; + } + vallfn = NULL; + } + if (p[26] | (p[27] << 8)) { + pwarn("long filename record cluster start != 0\n"); + if (!invlfn) { + invlfn = vallfn; + invcl = cl; + } + vallfn = NULL; + } + continue; /* long records don't carry further + * information */ + } + + /* + * This is a standard msdosfs directory entry. + */ + memset(&dirent, 0, sizeof dirent); + + /* + * it's a short name record, but we need to know + * more, so get the flags first. + */ + dirent.flags = p[11]; + + /* + * Translate from 850 to ISO here XXX + */ + for (j = 0; j < 8; j++) + dirent.name[j] = p[j]; + dirent.name[8] = '\0'; + for (k = 7; k >= 0 && dirent.name[k] == ' '; k--) + dirent.name[k] = '\0'; + if (dirent.name[k] != '\0') + k++; + if (dirent.name[0] == SLOT_E5) + dirent.name[0] = 0xe5; + /* + * What about volume names with extensions? XXX + */ + if ((dirent.flags & ATTR_VOLUME) == 0 && p[8] != ' ') + dirent.name[k++] = '.'; + for (j = 0; j < 3; j++) + dirent.name[k++] = p[j+8]; + dirent.name[k] = '\0'; + for (k--; k >= 0 && dirent.name[k] == ' '; k--) + dirent.name[k] = '\0'; + + if (vallfn && shortSum != calcShortSum(p)) { + if (!invlfn) { + invlfn = vallfn; + invcl = valcl; + } + vallfn = NULL; + } + dirent.head = p[26] | (p[27] << 8); + dirent.size = p[28] | (p[29] << 8) | (p[30] << 16) | (p[31] << 24); + if (vallfn) { + strcpy(dirent.lname, longName); + longName[0] = '\0'; + shortSum = -1; + } + + k = strlen(dirent.lname[0] ? dirent.lname : dirent.name); + k += strlen(dir->fullpath) + 2; + dirent.fullpath = malloc(k); + strcpy(dirent.fullpath, dir->fullpath); + if (dir->fullpath[1]) + strcat(dirent.fullpath, "/"); + strcat(dirent.fullpath, + dirent.lname[0] ? dirent.lname : dirent.name); + if (invlfn) { + mod |= k = removede(f, boot, fat, + invlfn, vallfn ? vallfn : p, + invcl, vallfn ? valcl : cl, cl, + dirent.fullpath, 0); + if (mod & FSFATAL) + return FSFATAL; + if (vallfn + ? (valcl == cl && vallfn != buffer) + : p != buffer) + if (k & FSDIRMOD) + mod |= THISMOD; + } + vallfn = NULL; /* not used any longer */ + invlfn = NULL; + + if (dirent.size == 0 && !(dirent.flags & ATTR_DIRECTORY)) { + if (dirent.head != 0) { + pwarn("%s has clusters, but size 0\n", + dirent.fullpath); + if (ask(1, "Drop allocated clusters")) { + p[26] = p[27] = 0; + clearchain(boot, fat, dirent.head); + dirent.head = 0; + mod |= THISMOD|FSDIRMOD|FSFATMOD; + } else + mod |= FSERROR; + } + } else if (dirent.head == 0 + && !strcmp(dirent.name, "..") + && !strcmp(dir->parent->fullpath, "/")) { + /* + * Do nothing, the parent is the root + */ + } else if (dirent.head < CLUST_FIRST + || dirent.head >= boot->NumClusters + || fat[dirent.head].next == CLUST_FREE + || (fat[dirent.head].next >= CLUST_RSRVD + && fat[dirent.head].next < CLUST_EOFS) + || fat[dirent.head].head != dirent.head) { + if (dirent.head == 0) + pwarn("%s has no clusters\n", + dirent.fullpath); + else if (dirent.head < CLUST_FIRST + || dirent.head >= boot->NumClusters) + pwarn("%s starts with cluster out of range(%d)\n", + dirent.fullpath, + dirent.head); + else if (fat[dirent.head].next == CLUST_FREE) + pwarn("%s starts with free cluster\n", + dirent.fullpath); + else if (fat[dirent.head].next >= CLUST_RSRVD) + pwarn("%s starts with %s cluster\n", + dirent.fullpath, + rsrvdcltype(fat[dirent.head].next)); + else + pwarn("%s doesn't start a cluster chain\n", + dirent.fullpath); + if (dirent.flags & ATTR_DIRECTORY) { + if (ask(0, "Remove")) { + *p = SLOT_DELETED; + mod |= THISMOD|FSDIRMOD; + } else + mod |= FSERROR; + continue; + } else { + if (ask(1, "Truncate")) { + p[28] = p[29] = p[30] = p[31] = 0; + dirent.size = 0; + mod |= THISMOD|FSDIRMOD; + } else + mod |= FSERROR; + } + } + + /* create directory tree node */ + d = malloc(sizeof(struct dosDirEntry)); + memcpy(d, &dirent, sizeof(struct dosDirEntry)); + /* link it into the directory tree */ + d->parent = dir; + d->next = dir->child; + dir->child = d; + if (d->head >= CLUST_FIRST && d->head < boot->NumClusters) + fat[d->head].dirp = d; + + if (d->flags & ATTR_DIRECTORY) { + /* + * gather more info for directories + */ + struct dirTodoNode * n; + + if (d->size) { + pwarn("Directory %s has size != 0\n", + d->fullpath); + if (ask(1, "Correct")) { + p[28] = p[29] = p[30] = p[31] = 0; + d->size = 0; + mod |= THISMOD|FSDIRMOD; + } else + mod |= FSERROR; + } + /* + * handle `.' and `..' specially + */ + if (strcmp(d->name, ".") == 0) { + if (d->head != dir->head) { + pwarn("`.' entry in %s has incorrect start cluster\n", + dir->fullpath); + if (ask(1, "Correct")) { + d->head = dir->head; + p[26] = (u_char)d->head; + p[27] = (u_char)(d->head >> 8); + mod |= THISMOD|FSDIRMOD; + } else + mod |= FSERROR; + } + continue; + } + if (strcmp(d->name, "..") == 0) { + if (d->head != dir->parent->head) { + pwarn("`..' entry in %s has incorrect start cluster\n", + dir->fullpath); + if (ask(1, "Correct")) { + d->head = dir->parent->head; + p[26] = (u_char)d->head; + p[27] = (u_char)(d->head >> 8); + mod |= THISMOD|FSDIRMOD; + } else + mod |= FSERROR; + } + continue; + } + + boot->NumFiles++; + /* Enter this directory into the todo list */ + n = malloc(sizeof(struct dirTodoNode)); + n->next = pendingDirectories; + n->dir = d; + pendingDirectories = n; + } else { + mod |= k = checksize(boot, fat, p, d); + if (k & FSDIRMOD) + mod |= THISMOD; + boot->NumFiles++; + } + } + if (mod & THISMOD) { + last *= 32; + if (lseek(f, off, SEEK_SET) != off + || write(f, buffer, last) != last) { + perror("Unable to write directory"); + return FSFATAL; + } + mod &= ~THISMOD; + } + } while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters); + if (invlfn || vallfn) + mod |= removede(f, boot, fat, + invlfn ? invlfn : vallfn, p, + invlfn ? invcl : valcl, -1, 0, + dir->fullpath, 1); + return mod & ~THISMOD; +} + +/* + * Try to reconnect a FAT chain into dir + */ +static u_char *lfbuf; +static cl_t lfcl; +static off_t lfoff; + +int +reconnect(dosfs, boot, fat, head, dir) + int dosfs; + struct bootblock *boot; + struct fatEntry *fat; + cl_t head; + struct dosDirEntry *dir; +{ + struct dosDirEntry d; + u_char *p; + + if (!dir) /* Create lfdir? XXX */ + return FSERROR; + if (!lfbuf) { + lfbuf = malloc(boot->ClusterSize); + if (!lfbuf) { + perror("No space for buffer"); + return FSFATAL; + } + p = NULL; + } else + p = lfbuf; + while (1) { + if (p) + while (p < lfbuf + boot->ClusterSize) + if (*p == SLOT_EMPTY + || *p == SLOT_DELETED) + break; + if (p && p < lfbuf + boot->ClusterSize) + break; + lfcl = p ? fat[lfcl].next : dir->head; + if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) { + /* Extend lfdir? XXX */ + pwarn("No space in %s\n", LOSTDIR); + return FSERROR; + } + lfoff = lfcl * boot->ClusterSize + + boot->ClusterOffset * boot->BytesPerSec; + if (lseek(dosfs, lfoff, SEEK_SET) != lfoff + || read(dosfs, buffer, boot->ClusterSize) != boot->ClusterSize) { + perror("could not read LOST.DIR"); + return FSFATAL; + } + p = lfbuf; + } + + if (!ask(0, "Reconnect")) + return FSERROR; + + boot->NumFiles++; + /* Ensure uniqueness of entry here! XXX */ + memset(&d, 0, sizeof d); + sprintf(d.name, "%d", head); + d.fullpath = malloc(strlen(dir->fullpath) + strlen(d.name) + 2); + sprintf(d.fullpath, "%s/%s", dir->fullpath, d.name); + d.flags = 0; + d.head = head; + d.size = fat[head].length * boot->ClusterSize; + d.parent = dir; + d.next = dir->child; + dir->child = malloc(sizeof(struct dosDirEntry)); + memcpy(dir->child, &d, sizeof(struct dosDirEntry)); + + memset(p, 0, 32); + memset(p, ' ', 11); + memcpy(p, dir->name, strlen(dir->name)); + p[26] = (u_char)dir->head; + p[27] = (u_char)(dir->head >> 8); + p[28] = (u_char)dir->size; + p[29] = (u_char)(dir->size >> 8); + p[30] = (u_char)(dir->size >> 16); + p[31] = (u_char)(dir->size >> 24); + fat[head].dirp = dir; + if (lseek(dosfs, lfoff, SEEK_SET) != lfoff + || write(dosfs, buffer, boot->ClusterSize) != boot->ClusterSize) { + perror("could not write LOST.DIR"); + return FSFATAL; + } + return FSDIRMOD; +} + +void +finishlf() +{ + if (lfbuf) + free(lfbuf); + lfbuf = NULL; +} diff --git a/sbin/fsck_msdos/dosfs.h b/sbin/fsck_msdos/dosfs.h new file mode 100644 index 00000000000..c0295bc7b10 --- /dev/null +++ b/sbin/fsck_msdos/dosfs.h @@ -0,0 +1,123 @@ + +/* + * Copyright (C) 1995, 1996 Wolfgang Solfrank + * Copyright (c) 1995 Martin Husemann + * Some structure declaration borrowed from Paul Popelka + * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Martin Husemann + * and Wolfgang Solfrank. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DOSFS_H +#define DOSFS_H + +#define DOSBOOTBLOCKSIZE 512 + +#define MAX12BITCLUSTERS 4078 + +typedef u_int16_t cl_t; /* type holding a cluster number */ + +/* + * architecture independent description of all the info stored in a + * FAT boot block. + */ +struct bootblock { + u_int BytesPerSec; /* bytes per sector */ + u_int SecPerClust; /* sectors per cluster */ + u_int ResSectors; /* number of reserved sectors */ + u_int FATs; /* number of FATs */ + u_int RootDirEnts; /* number of root directory entries */ + u_int32_t Sectors; /* total number of sectors */ + u_int Media; /* media descriptor */ + u_int FATsecs; /* number of sectors per FAT */ + u_int SecPerTrack; /* sectors per track */ + u_int Heads; /* number of heads */ + u_int32_t HiddenSecs; /* # of hidden sectors */ + u_int32_t HugeSectors; /* # of sectors if bpbSectors == 0 */ + + /* and some more calculated values */ + int Is16BitFat; /* 0 for 12 bit, 1 for 16 bit */ + cl_t NumClusters; /* # of entries in a FAT */ + u_int32_t NumSectors; /* how many sectors are there */ + u_int32_t NumFatEntries; /* how many entries really are there */ + u_int ClusterOffset; /* at what sector would sector 0 start */ + u_int ClusterSize; /* Cluster size in bytes */ + + /* Now some statistics: */ + u_int NumFiles; /* # of plain files */ + u_int NumFree; /* # of free clusters */ +}; + +struct fatEntry { + cl_t next; /* pointer to next cluster */ + cl_t head; /* pointer to start of chain */ + u_int32_t length; /* number of clusters on chain */ + struct dosDirEntry *dirp; /* corresponding file name */ +}; + +#define CLUST_FREE 0 /* 0 means cluster is free */ +#define CLUST_FIRST 2 /* 2 is the minimum valid cluster number */ +#define CLUST_RSRVD 0xfff0 /* start of reserved clusters */ +#define CLUST_BAD 0xfff7 /* a cluster with a defect */ +#define CLUST_EOFS 0xfff8 /* start of EOF indicators */ +#define CLUST_EOF 0xffff /* standard value for last cluster */ + +#define DOSLONGNAMELEN 256 /* long name maximal length */ +#define LRFIRST 0x40 /* first long name record */ +#define LRNOMASK 0x1f /* mask to extract long record + * sequence number */ + +/* + * Architecture independent description of a directory entry + */ +struct dosDirEntry { + struct dosDirEntry + *parent, /* previous tree level */ + *next, /* next brother */ + *child; /* if this is a directory */ + char *fullpath; /* path name from root to this directory */ + char name[8+1+3+1]; /* alias name first part */ + char lname[DOSLONGNAMELEN]; /* real name */ + uint flags; /* attributes */ + cl_t head; /* cluster no */ + u_int32_t size; /* filesize in bytes */ + uint fsckflags; /* flags during fsck */ +}; +/* Flags in fsckflags: */ +#define DIREMPTY 1 +#define DIREMPWARN 2 + +/* + * TODO-list of unread directories + */ +struct dirTodoNode { + struct dosDirEntry *dir; + struct dirTodoNode *next; +}; + +#endif diff --git a/sbin/fsck_msdos/ext.h b/sbin/fsck_msdos/ext.h new file mode 100644 index 00000000000..63615e8e195 --- /dev/null +++ b/sbin/fsck_msdos/ext.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 1995, 1996 Wolfgang Solfrank + * Copyright (c) 1995 Martin Husemann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Martin Husemann + * and Wolfgang Solfrank. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EXT_H +#define EXT_H + +#include <sys/types.h> + +#if sun +#define __P(a) a + +typedef char int8_t; +typedef unsigned char u_int8_t; +typedef short int16_t; +typedef unsigned short u_int16_t; +typedef long int32_t; +typedef unsigned long u_int32_t; +#endif + +#include "dosfs.h" + +#define LOSTDIR "LOST.DIR" + +/* + * Options: + */ +extern int alwaysno; /* assume "no" for all questions */ +extern int alwaysyes; /* assume "yes" for all questions */ +extern int preen; /* we are preening */ +extern int rdonly; /* device is opened read only (supersedes above) */ + +extern char *fname; /* filesystem currently checked */ + +/* + * function declarations + */ +void errexit __P((const char *, ...)); +void pfatal __P((const char *, ...)); +void pwarn __P((const char *, ...)); +int ask __P((int, const char *, ...)); +void perror __P((const char *)); + +/* + * Check filesystem given as arg + */ +int checkfilesys __P((const char *)); + +/* + * Return values of various functions + */ +#define FSOK 0 /* Check was OK */ +#define FSDIRMOD 1 /* Some directory was modified */ +#define FSFATMOD 2 /* The FAT was modified */ +#define FSERROR 4 /* Some unrecovered error remains */ +#define FSFATAL 8 /* Some unrecoverable error occured */ + +/* + * read a boot block in a machine independend fashion and translate + * it into our struct bootblock. + */ +int readboot __P((int, struct bootblock *)); + + +/* + * Read one of the FAT copies and return a pointer to the new + * allocated array holding our description of it. + */ +int readfat __P((int, struct bootblock *, int, struct fatEntry **)); + +/* + * Check two FAT copies for consistency and merge changes into the + * first if neccessary. + */ +int comparefat __P((struct bootblock *, struct fatEntry *, struct fatEntry *, int)); + +/* + * Check a FAT + */ +int checkfat __P((struct bootblock *, struct fatEntry *)); + +/* + * Write back FAT entries + */ +int writefat __P((int, struct bootblock *, struct fatEntry *)); + +/* + * Read a directory + */ +int resetDosDirSection __P((struct bootblock *)); +void finishDosDirSection __P((void)); +int readDosDirSection __P((int, struct bootblock *, struct fatEntry *, struct dosDirEntry *)); + +/* + * A stack of directories which should be examined later + */ +extern struct dirTodoNode *pendingDirectories; + +/* + * Cross-check routines run after everything is completely in memory + */ +/* + * Check for lost cluster chains + */ +int checklost __P((int, struct bootblock *, struct fatEntry *, struct dosDirEntry *)); +/* + * Try to reconnect a lost cluster chain + */ +int reconnect __P((int, struct bootblock *, struct fatEntry *, cl_t, struct dosDirEntry *)); +void finishlf __P((void)); + +/* + * Small helper functions + */ +/* + * Return the type of a reserved cluster as text + */ +char *rsrvdcltype __P((cl_t)); + +/* + * Clear a cluster chain in a FAT + */ +void clearchain __P((struct bootblock *, struct fatEntry *, cl_t)); + +#endif diff --git a/sbin/fsck_msdos/fat.c b/sbin/fsck_msdos/fat.c new file mode 100644 index 00000000000..6ebb99e529d --- /dev/null +++ b/sbin/fsck_msdos/fat.c @@ -0,0 +1,514 @@ +/* $NetBSD: fat.c,v 1.1 1996/05/14 17:39:34 ws Exp $ */ + +/* + * Copyright (C) 1995, 1996 Wolfgang Solfrank + * Copyright (c) 1995 Martin Husemann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Martin Husemann + * and Wolfgang Solfrank. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef lint +static char rcsid[] = "$NetBSD: fat.c,v 1.1 1996/05/14 17:39:34 ws Exp $"; +#endif /* not lint */ + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include <unistd.h> + +#include "ext.h" + +/* + * Check a cluster number for valid value + */ +static int +checkclnum(boot, fat, cl, next) + struct bootblock *boot; + int fat; + cl_t cl; + cl_t *next; +{ + if (!boot->Is16BitFat && *next >= (CLUST_RSRVD&0xfff)) + *next |= 0xf000; + if (*next == CLUST_FREE) { + boot->NumFree++; + return FSOK; + } + if (*next < CLUST_FIRST + || (*next >= boot->NumClusters && *next < CLUST_EOFS)) { + pwarn("Cluster %d in FAT %d continues with %s cluster number %d\n", + cl, fat, + *next < CLUST_RSRVD ? "out of range" : "reserved", + *next); + if (ask(0, "Truncate")) { + *next = CLUST_EOF; + return FSFATMOD; + } + return FSERROR; + } + return FSOK; +} + +/* + * Read a FAT and decode it into internal format + */ +int +readfat(fs, boot, no, fp) + int fs; + struct bootblock *boot; + int no; + struct fatEntry **fp; +{ + struct fatEntry *fat; + u_char *buffer, *p; + cl_t cl; + off_t off; + int size; + int ret = FSOK; + + boot->NumFree = 0; + fat = malloc(sizeof(struct fatEntry) * boot->NumClusters); + buffer = malloc(boot->FATsecs * boot->BytesPerSec); + if (fat == NULL || buffer == NULL) { + perror("No space for FAT"); + if (fat) + free(fat); + return FSFATAL; + } + + memset(fat, 0, sizeof(struct fatEntry) * boot->NumClusters); + + off = boot->ResSectors + no * boot->FATsecs; + off *= boot->BytesPerSec; + + if (lseek(fs, off, SEEK_SET) != off) { + perror("Unable to read FAT"); + free(buffer); + free(fat); + return FSFATAL; + } + + if ((size = read(fs, buffer, boot->FATsecs * boot->BytesPerSec)) + != boot->FATsecs * boot->BytesPerSec) { + if (size < 0) + perror("Unable to read FAT"); + else + pfatal("Short FAT?"); + free(buffer); + free(fat); + return FSFATAL; + } + + /* + * Remember start of FAT to allow keeping it in write_fat. + */ + fat[0].length = buffer[0]|(buffer[1] << 8)|(buffer[2] << 16); + if (boot->Is16BitFat) + fat[0].length |= buffer[3] << 24; + if (buffer[1] != 0xff || buffer[2] != 0xff + || (boot->Is16BitFat && buffer[3] != 0xff)) { + char *msg = boot->Is16BitFat + ? "FAT starts with odd byte sequence (%02x%02x%02x%02x)\n" + : "FAT starts with odd byte sequence (%02x%02x%02x)\n"; + pwarn(msg, buffer[0], buffer[1], buffer[2], buffer[3]); + if (ask(1, "Correct")) { + fat[0].length = boot->Media|0xffffff; + ret |= FSFATMOD; + } + } + p = buffer + (boot->Is16BitFat ? 4 : 3); + for (cl = CLUST_FIRST; cl < boot->NumClusters;) { + if (boot->Is16BitFat) { + fat[cl].next = p[0] + (p[1] << 8); + ret |= checkclnum(boot, no, cl, &fat[cl].next); + cl++; + p += 2; + } else { + fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff; + ret |= checkclnum(boot, no, cl, &fat[cl].next); + cl++; + if (cl >= boot->NumClusters) + break; + fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff; + ret |= checkclnum(boot, no, cl, &fat[cl].next); + cl++; + p += 3; + } + } + + free(buffer); + *fp = fat; + return ret; +} + +/* + * Get type of reserved cluster + */ +char * +rsrvdcltype(cl) + cl_t cl; +{ + if (cl < CLUST_BAD) + return "reserved"; + if (cl > CLUST_BAD) + return "as EOF"; + return "bad"; +} + +static int +clustdiffer(cl, cp1, cp2, fatnum) + cl_t cl; + cl_t *cp1; + cl_t *cp2; + int fatnum; +{ + if (*cp1 >= CLUST_RSRVD) { + if (*cp2 >= CLUST_RSRVD) { + if ((*cp1 < CLUST_BAD && *cp2 < CLUST_BAD) + || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) { + pwarn("Cluster %d is marked %s with different indicators, ", + cl, rsrvdcltype(*cp1)); + if (ask(1, "fix")) { + *cp2 = *cp1; + return FSFATMOD; + } + return FSFATAL; + } + pwarn("Cluster %d is marked %s in FAT 1, %s in FAT %d\n", + cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum); + if (ask(0, "use FAT #1's entry")) { + *cp2 = *cp1; + return FSFATMOD; + } + if (ask(0, "use FAT #%d's entry", fatnum)) { + *cp1 = *cp2; + return FSFATMOD; + } + return FSFATAL; + } + pwarn("Cluster %d is marked %s in FAT 1, but continues with cluster %d in FAT %d\n", + cl, rsrvdcltype(*cp1), *cp2, fatnum); + if (ask(0, "Use continuation from FAT %d", fatnum)) { + *cp1 = *cp2; + return FSFATMOD; + } + if (ask(0, "Use mark from FAT 1")) { + *cp2 = *cp1; + return FSFATMOD; + } + return FSFATAL; + } + if (*cp2 >= CLUST_RSRVD) { + pwarn("Cluster %d continues with cluster %d in FAT 1, but is marked %s in FAT %d\n", + cl, *cp1, rsrvdcltype(*cp2), fatnum); + if (ask(0, "Use continuation from FAT 1")) { + *cp2 = *cp1; + return FSFATMOD; + } + if (ask(0, "Use mark from FAT %d", fatnum)) { + *cp1 = *cp2; + return FSFATMOD; + } + return FSERROR; + } + pwarn("Cluster %d continues with cluster %d in FAT 1, but with cluster %d in FAT %d\n", + cl, *cp1, *cp2, fatnum); + if (ask(0, "Use continuation from FAT 1")) { + *cp2 = *cp1; + return FSFATMOD; + } + if (ask(0, "Use continuation from FAT %d", fatnum)) { + *cp1 = *cp2; + return FSFATMOD; + } + return FSERROR; +} + +/* + * Compare two FAT copies in memory. Resolve any conflicts and merge them + * into the first one. + */ +int +comparefat(boot, first, second, fatnum) + struct bootblock *boot; + struct fatEntry *first; + struct fatEntry *second; + int fatnum; +{ + cl_t cl; + int ret = FSOK; + + if (first[0].next != second[0].next) { + pwarn("Media bytes in cluster 1(%02x) and %d(%02x) differ\n", + first[0].next, fatnum, second[0].next); + if (ask(1, "Use media byte from FAT 1")) { + second[0].next = first[0].next; + ret |= FSFATMOD; + } else if (ask(0, "Use media byte from FAT %d", fatnum)) { + first[0].next = second[0].next; + ret |= FSFATMOD; + } else + ret |= FSERROR; + } + for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) + if (first[cl].next != second[cl].next) + ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum); + return ret; +} + +void +clearchain(boot, fat, head) + struct bootblock *boot; + struct fatEntry *fat; + cl_t head; +{ + cl_t p, q; + + for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) { + if (fat[p].head != head) + break; + q = fat[p].next; + fat[p].next = fat[p].head = CLUST_FREE; + fat[p].length = 0; + } +} + +/* + * Check a complete FAT in-memory for crosslinks + */ +int +checkfat(boot, fat) + struct bootblock *boot; + struct fatEntry *fat; +{ + cl_t head, p, h; + u_int len; + int ret = 0; + int conf; + + /* + * pass 1: figure out the cluster chains. + */ + for (head = CLUST_FIRST; head < boot->NumClusters; head++) { + /* find next untraveled chain */ + if (fat[head].head != 0 /* cluster already belongs to some chain*/ + || fat[head].next == CLUST_FREE) + continue; /* skip it. */ + + /* follow the chain and mark all clusters on the way */ + for (len = 0, p = head; + p >= CLUST_FIRST && p < boot->NumClusters; + p = fat[p].next) { + fat[p].head = head; + len++; + } + + /* the head record gets the length */ + fat[head].length = len; + } + + /* + * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because + * we didn't know the real start of the chain then - would have treated partial + * chains as interlinked with their main chain) + */ + for (head = CLUST_FIRST; head < boot->NumClusters; head++) { + /* find next untraveled chain */ + if (fat[head].head != head) + continue; + + /* follow the chain to its end (hopefully) */ + for (p = head; + fat[p].next >= CLUST_FIRST && fat[p].next < boot->NumClusters; + p = fat[p].next) + if (fat[fat[p].next].head != head) + break; + if (fat[p].next >= CLUST_EOFS) + continue; + + if (fat[p].next == 0) { + pwarn("Cluster chain starting at %d ends with free cluster\n", head); + if (ask(0, "Clear chain starting at %d", head)) { + clearchain(boot, fat, head); + ret |= FSFATMOD; + } else + ret |= FSERROR; + continue; + } + if (fat[p].next >= CLUST_RSRVD) { + pwarn("Cluster chain starting at %d ends with cluster marked %s\n", + head, rsrvdcltype(fat[p].next)); + if (ask(0, "Clear chain starting at %d", head)) { + clearchain(boot, fat, head); + ret |= FSFATMOD; + } else + ret |= FSERROR; + continue; + } + if (fat[p].next < CLUST_FIRST || fat[p].next >= boot->NumClusters) { + pwarn("Cluster chain starting at %d ends with cluster out of range (%d)\n", + head, fat[p].next); + if (ask(0, "Clear chain starting at %d", head)) { + clearchain(boot, fat, head); + ret |= FSFATMOD; + } else + ret |= FSERROR; + } + pwarn("Cluster chains starting at %d and %d are linked at cluster %d\n", + head, fat[p].head, p); + conf = FSERROR; + if (ask(0, "Clear chain starting at %d", head)) { + clearchain(boot, fat, head); + conf = FSFATMOD; + } + if (ask(0, "Clear chain starting at %d", h = fat[p].head)) { + if (conf == FSERROR) { + /* + * Transfer the common chain to the one not cleared above. + */ + for (; p >= CLUST_FIRST && p < boot->NumClusters; + p = fat[p].next) { + if (h != fat[p].head) { + /* + * Have to reexamine this chain. + */ + head--; + break; + } + fat[p].head = head; + } + } + clearchain(boot, fat, h); + conf |= FSFATMOD; + } + ret |= conf; + } + + return ret; +} + +/* + * Write out FATs encoding them from the internal format + */ +int +writefat(fs, boot, fat) + int fs; + struct bootblock *boot; + struct fatEntry *fat; +{ + u_char *buffer, *p; + cl_t cl; + int i; + u_int32_t fatsz; + off_t off; + int ret = FSOK; + + buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec); + if (buffer == NULL) { + perror("No space for FAT"); + return FSFATAL; + } + memset(buffer, 0, fatsz); + boot->NumFree = 0; + buffer[0] = (u_char)fat[0].length; + buffer[1] = (u_char)(fat[0].length >> 8); + if (boot->Is16BitFat) + buffer[3] = (u_char)(fat[0].length >> 24); + for (cl = CLUST_FIRST, p = buffer; cl < boot->NumClusters;) { + if (boot->Is16BitFat) { + p[0] = (u_char)fat[cl].next; + if (fat[cl].next == CLUST_FREE) + boot->NumFree++; + p[1] = (u_char)(fat[cl++].next >> 8); + p += 2; + } else { + if (fat[cl].next == CLUST_FREE) + boot->NumFree++; + if (cl + 1 < boot->NumClusters + && fat[cl + 1].next == CLUST_FREE) + boot->NumFree++; + p[0] = (u_char)fat[cl].next; + p[1] = (u_char)((fat[cl].next >> 8) & 0xf) + |(u_char)(fat[cl+1].next << 4); + p[2] = (u_char)(fat[cl++].next >> 8); + p += 3; + } + } + for (i = 0; i < boot->FATs; i++) { + off = boot->ResSectors + i * boot->FATsecs; + off *= boot->BytesPerSec; + if (lseek(fs, off, SEEK_SET) != off + || write(fs, buffer, fatsz) != fatsz) { + perror("Unable to write FAT"); + ret = FSFATAL; /* Return immediately? XXX */ + } + } + free(buffer); + return ret; +} + +/* + * Check a complete in-memory FAT for lost cluster chains + */ +int +checklost(dosfs, boot, fat, rootDir) + int dosfs; + struct bootblock *boot; + struct fatEntry *fat; + struct dosDirEntry *rootDir; +{ + cl_t head; + struct dosDirEntry *lfdir; + int mod = FSOK; + + for (lfdir = rootDir->child; lfdir; lfdir = lfdir->next) { + if (!strcmp(lfdir->name, LOSTDIR)) + break; + } + for (head = CLUST_FIRST; head < boot->NumClusters; head++) { + /* find next untraveled chain */ + if (fat[head].head != head + || fat[head].next == CLUST_FREE + || (fat[head].next >= CLUST_RSRVD + && fat[head].next < CLUST_EOFS)) + continue; + + if (fat[head].dirp == NULL) { + pwarn("Lost cluster chain at cluster 0x%04x\n%d Cluster(s) lost\n", + head, fat[head].length); + mod |= reconnect(dosfs, boot, fat, head, lfdir); + if (mod&FSFATAL) + break; + } + } + finishlf(); + + return mod; +} diff --git a/sbin/fsck_msdos/fsck_msdos.8 b/sbin/fsck_msdos/fsck_msdos.8 new file mode 100644 index 00000000000..127c4554316 --- /dev/null +++ b/sbin/fsck_msdos/fsck_msdos.8 @@ -0,0 +1,76 @@ +.\" $NetBSD: fsck_msdos.8,v 1.1 1996/05/14 17:39:35 ws Exp $ +.\" +.\" Copyright (C) 1995 Wolfgang Solfrank +.\" Copyright (c) 1995 Martin Husemann +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Martin Husemann +.\" and Wolfgang Solfrank. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.Dd August 13, 1995 +.Dt FSCK_MSDOS 8 +.Os NetBSD 1.1a +.Sh NAME +.Nm fsck_msdos +.Nd DOS/Windows (FAT) filesystem consistency checker +.Sh SYNOPSIS +.Nm fsck_msdos +.Fl p +.Ar filesystem +.Ar ... +.Nm fsck_msdos +.Op Fl y +.Op Fl n +.Ar filesystem +.Ar ... +.Sh DESCRIPTION +The first form of +.Nm +preens the specified filesystems. +It is normally started by +.Xr fsck 8 +run from +.Pa /etc/rc +during automatic reboot, when a FAT filesystem is detected. +.Pp +.Nm fsck_msdos +verifies a FAT filesystem (more commonly known as DOS filesystem) and tries +to recover it from inconsistencies. +.Pp +Options are: +.Bl -hang -offset indent +.It Em -y +assume yes as answer to all questions. +.It Em -n +assume no as answer to all questions. +.El +.Sh SEE ALSO +.Xr fsck 8 , +.Xr mount_msdos 8 +.Sh BUGS +.Nm fsck_msdos +is still under construction. diff --git a/sbin/fsck_msdos/main.c b/sbin/fsck_msdos/main.c new file mode 100644 index 00000000000..67634aa278f --- /dev/null +++ b/sbin/fsck_msdos/main.c @@ -0,0 +1,252 @@ +/* $NetBSD: main.c,v 1.1 1996/05/14 17:39:36 ws Exp $ */ + +/* + * Copyright (C) 1995 Wolfgang Solfrank + * Copyright (c) 1995 Martin Husemann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Martin Husemann + * and Wolfgang Solfrank. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef lint +static char rcsid[] = "$NetBSD: main.c,v 1.1 1996/05/14 17:39:36 ws Exp $"; +#endif /* not lint */ + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include "ext.h" + +int alwaysno; /* assume "no" for all questions */ +int alwaysyes; /* assume "yes" for all questions */ +int preen; /* set when preening */ +int rdonly; /* device is opened read only (supersedes above) */ + +char *fname; /* filesystem currently checked */ + +static void +usage() +{ + errexit("Usage: fsck_msdos [-pny] filesystem ... \n"); +} + +int +main(argc, argv) + int argc; + char **argv; +{ + extern int optind; + int ret = 0, erg; + int ch; + + while ((ch = getopt(argc, argv, "vpyn")) != EOF) { + switch (ch) { + case 'n': + alwaysno = 1; + alwaysyes = preen = 0; + break; + case 'y': + alwaysyes = 1; + alwaysno = preen = 0; + break; + + case 'p': + preen = 1; + alwaysyes = alwaysno = 0; + break; + + default: + usage(); + break; + } + } + argc -= optind; + argv += optind; + + if (!argc) + usage(); + + while (argc-- > 0) { + erg = checkfilesys(fname = *argv++); + if (erg > ret) + ret = erg; + } + exit(ret); +} + +/*VARARGS*/ +void +#if __STDC__ +errexit(const char *fmt, ...) +#else +errexit(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vprintf(fmt, ap); + va_end(ap); + exit(8); +} + +/*VARARGS*/ +void +#if __STDC__ +pfatal(const char *fmt, ...) +#else +pfatal(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; + + if (preen) + printf("%s: ", fname); +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vprintf(fmt, ap); + va_end(ap); + printf("\n"); + if (preen) + exit(8); +} + +/*VARARGS*/ +void +#if __STDC__ +pwarn(const char *fmt, ...) +#else +pwarn(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; + + if (preen) + printf("%s: ", fname); +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vprintf(fmt, ap); + va_end(ap); +} + +#if sun +char * +strerror(n) + int n; +{ + extern int sys_nerr; + extern char *sys_errlist[]; + static char alt[80]; + + if (n < sys_nerr) + return sys_errlist[n]; + sprintf(alt, "Unknown error %d", n); + return alt; +} +#endif + +void +perror(s) + const char *s; +{ + pfatal("%s (%s)", s, strerror(errno)); +} + +/*VARARGS*/ +int +#if __STDC__ +ask(int def, const char *fmt, ...) +#else +ask(def, fmt, va_alist) + int def; + char *fmt; + va_dcl +#endif +{ + va_list ap; + + char prompt[256]; + int c; + + if (preen) { + if (rdonly) + def = 0; + if (def) + printf("FIXED\n"); + return def; + } + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif +#if sun + vsprintf(prompt, fmt, ap); +#else + vsnprintf(prompt, sizeof(prompt), fmt, ap); +#endif + if (alwaysyes || rdonly) { + printf("%s? %s\n", prompt, rdonly ? "no" : "yes"); + return !rdonly; + } + do { + printf("%s? [yn] ", prompt); + fflush(stdout); + c = getchar(); + while (c != '\n' && getchar() != '\n') + if (feof(stdin)) + return 0; + } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); + return c == 'y' || c == 'Y'; +} |