Blame src/check.c

Packit ff56ff
/* check.c - Check and repair a PC/MS-DOS filesystem
Packit ff56ff
Packit ff56ff
   Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
Packit ff56ff
   Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
Packit ff56ff
   Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch>
Packit ff56ff
   Copyright (C) 2015 Andreas Bombe <aeb@debian.org>
Packit ff56ff
Packit ff56ff
   This program is free software: you can redistribute it and/or modify
Packit ff56ff
   it under the terms of the GNU General Public License as published by
Packit ff56ff
   the Free Software Foundation, either version 3 of the License, or
Packit ff56ff
   (at your option) any later version.
Packit ff56ff
Packit ff56ff
   This program is distributed in the hope that it will be useful,
Packit ff56ff
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit ff56ff
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Packit ff56ff
   GNU General Public License for more details.
Packit ff56ff
Packit ff56ff
   You should have received a copy of the GNU General Public License
Packit ff56ff
   along with this program. If not, see <http://www.gnu.org/licenses/>.
Packit ff56ff
Packit ff56ff
   The complete text of the GNU General Public License
Packit ff56ff
   can be found in /usr/share/common-licenses/GPL-3 file.
Packit ff56ff
*/
Packit ff56ff
Packit ff56ff
/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
Packit ff56ff
 * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
Packit ff56ff
Packit ff56ff
#include <stdio.h>
Packit ff56ff
#include <stdlib.h>
Packit ff56ff
#include <string.h>
Packit ff56ff
#include <time.h>
Packit ff56ff
Packit ff56ff
#include "common.h"
Packit ff56ff
#include "fsck.fat.h"
Packit ff56ff
#include "io.h"
Packit ff56ff
#include "fat.h"
Packit ff56ff
#include "file.h"
Packit ff56ff
#include "lfn.h"
Packit ff56ff
#include "check.h"
Packit ff56ff
Packit ff56ff
Packit ff56ff
/* the longest path on the filesystem that can be handled by path_name() */
Packit ff56ff
#define PATH_NAME_MAX 1023
Packit ff56ff
Packit ff56ff
static DOS_FILE *root;
Packit ff56ff
Packit ff56ff
/* get start field of a dir entry */
Packit ff56ff
#define FSTART(p,fs) \
Packit ff56ff
  ((uint32_t)le16toh(p->dir_ent.start) | \
Packit ff56ff
   (fs->fat_bits == 32 ? le16toh(p->dir_ent.starthi) << 16 : 0))
Packit ff56ff
Packit ff56ff
#define MODIFY(p,i,v)					\
Packit ff56ff
  do {							\
Packit ff56ff
    if (p->offset) {					\
Packit ff56ff
	p->dir_ent.i = v;				\
Packit ff56ff
	fs_write(p->offset+offsetof(DIR_ENT,i),		\
Packit ff56ff
		 sizeof(p->dir_ent.i),&p->dir_ent.i);	\
Packit ff56ff
    }							\
Packit ff56ff
  } while(0)
Packit ff56ff
Packit ff56ff
#define MODIFY_START(p,v,fs)						\
Packit ff56ff
  do {									\
Packit ff56ff
    uint32_t __v = (v);							\
Packit ff56ff
    if (!p->offset) {							\
Packit ff56ff
	/* writing to fake entry for FAT32 root dir */			\
Packit ff56ff
	if (!__v) die("Oops, deleting FAT32 root dir!");		\
Packit ff56ff
	fs->root_cluster = __v;						\
Packit ff56ff
	p->dir_ent.start = htole16(__v&0xffff);				\
Packit ff56ff
	p->dir_ent.starthi = htole16(__v>>16);				\
Packit ff56ff
	__v = htole32(__v);						\
Packit ff56ff
	fs_write(offsetof(struct boot_sector,root_cluster),		\
Packit ff56ff
	         sizeof(((struct boot_sector *)0)->root_cluster),	\
Packit ff56ff
		 &__v);							\
Packit ff56ff
    }									\
Packit ff56ff
    else {								\
Packit ff56ff
	MODIFY(p,start,htole16((__v)&0xffff));				\
Packit ff56ff
	if (fs->fat_bits == 32)						\
Packit ff56ff
	    MODIFY(p,starthi,htole16((__v)>>16));			\
Packit ff56ff
    }									\
Packit ff56ff
  } while(0)
Packit ff56ff
Packit ff56ff
off_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern, int gen_name)
Packit ff56ff
{
Packit ff56ff
    static int curr_num = 0;
Packit ff56ff
    off_t offset;
Packit ff56ff
Packit ff56ff
    if (fs->root_cluster) {
Packit ff56ff
	DIR_ENT d2;
Packit ff56ff
	int i = 0, got = 0;
Packit ff56ff
	uint32_t clu_num, prev = 0;
Packit ff56ff
	off_t offset2;
Packit ff56ff
Packit ff56ff
	clu_num = fs->root_cluster;
Packit ff56ff
	offset = cluster_start(fs, clu_num);
Packit ff56ff
	while (clu_num > 0 && clu_num != -1) {
Packit ff56ff
	    fs_read(offset, sizeof(DIR_ENT), &d2;;
Packit ff56ff
	    if (IS_FREE(d2.name) && d2.attr != VFAT_LN_ATTR) {
Packit ff56ff
		got = 1;
Packit ff56ff
		break;
Packit ff56ff
	    }
Packit ff56ff
	    i += sizeof(DIR_ENT);
Packit ff56ff
	    offset += sizeof(DIR_ENT);
Packit ff56ff
	    if ((i % fs->cluster_size) == 0) {
Packit ff56ff
		prev = clu_num;
Packit ff56ff
		if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
Packit ff56ff
		    break;
Packit ff56ff
		offset = cluster_start(fs, clu_num);
Packit ff56ff
	    }
Packit ff56ff
	}
Packit ff56ff
	if (!got) {
Packit ff56ff
	    /* no free slot, need to extend root dir: alloc next free cluster
Packit ff56ff
	     * after previous one */
Packit ff56ff
	    if (!prev)
Packit ff56ff
		die("Root directory has no cluster allocated!");
Packit ff56ff
	    for (clu_num = prev + 1; clu_num != prev; clu_num++) {
Packit ff56ff
		FAT_ENTRY entry;
Packit ff56ff
Packit ff56ff
		if (clu_num >= fs->data_clusters + 2)
Packit ff56ff
		    clu_num = 2;
Packit ff56ff
		get_fat(&entry, fs->fat, clu_num, fs);
Packit ff56ff
		if (!entry.value)
Packit ff56ff
		    break;
Packit ff56ff
	    }
Packit ff56ff
	    if (clu_num == prev)
Packit ff56ff
		die("Root directory full and no free cluster");
Packit ff56ff
	    set_fat(fs, prev, clu_num);
Packit ff56ff
	    set_fat(fs, clu_num, -1);
Packit ff56ff
	    set_owner(fs, clu_num, get_owner(fs, fs->root_cluster));
Packit ff56ff
	    /* clear new cluster */
Packit ff56ff
	    memset(&d2, 0, sizeof(d2));
Packit ff56ff
	    offset = cluster_start(fs, clu_num);
Packit ff56ff
	    for (i = 0; i < fs->cluster_size; i += sizeof(DIR_ENT))
Packit ff56ff
		fs_write(offset + i, sizeof(d2), &d2;;
Packit ff56ff
	}
Packit ff56ff
	memset(de, 0, sizeof(DIR_ENT));
Packit ff56ff
	if (gen_name) {
Packit ff56ff
	    while (1) {
Packit ff56ff
		char expanded[12];
Packit ff56ff
		sprintf(expanded, pattern, curr_num);
Packit ff56ff
		memcpy(de->name, expanded, MSDOS_NAME);
Packit ff56ff
		clu_num = fs->root_cluster;
Packit ff56ff
		i = 0;
Packit ff56ff
		offset2 = cluster_start(fs, clu_num);
Packit ff56ff
		while (clu_num > 0 && clu_num != -1) {
Packit ff56ff
		    fs_read(offset2, sizeof(DIR_ENT), &d2;;
Packit ff56ff
		    if (offset2 != offset &&
Packit ff56ff
			!strncmp((const char *)d2.name, (const char *)de->name,
Packit ff56ff
				 MSDOS_NAME))
Packit ff56ff
			break;
Packit ff56ff
		    i += sizeof(DIR_ENT);
Packit ff56ff
		    offset2 += sizeof(DIR_ENT);
Packit ff56ff
		    if ((i % fs->cluster_size) == 0) {
Packit ff56ff
			if ((clu_num = next_cluster(fs, clu_num)) == 0 ||
Packit ff56ff
			    clu_num == -1)
Packit ff56ff
			    break;
Packit ff56ff
			offset2 = cluster_start(fs, clu_num);
Packit ff56ff
		    }
Packit ff56ff
		}
Packit ff56ff
		if (clu_num == 0 || clu_num == -1)
Packit ff56ff
		    break;
Packit ff56ff
		if (++curr_num >= 10000)
Packit ff56ff
		    die("Unable to create unique name");
Packit ff56ff
	    }
Packit ff56ff
	} else {
Packit ff56ff
	    memcpy(de->name, pattern, MSDOS_NAME);
Packit ff56ff
	}
Packit ff56ff
    } else {
Packit ff56ff
	DIR_ENT *root;
Packit ff56ff
	int next_free = 0, scan;
Packit ff56ff
Packit ff56ff
	root = alloc(fs->root_entries * sizeof(DIR_ENT));
Packit ff56ff
	fs_read(fs->root_start, fs->root_entries * sizeof(DIR_ENT), root);
Packit ff56ff
Packit ff56ff
	while (next_free < fs->root_entries)
Packit ff56ff
	    if (IS_FREE(root[next_free].name) &&
Packit ff56ff
		root[next_free].attr != VFAT_LN_ATTR)
Packit ff56ff
		break;
Packit ff56ff
	    else
Packit ff56ff
		next_free++;
Packit ff56ff
	if (next_free == fs->root_entries)
Packit ff56ff
	    die("Root directory is full.");
Packit ff56ff
	offset = fs->root_start + next_free * sizeof(DIR_ENT);
Packit ff56ff
	memset(de, 0, sizeof(DIR_ENT));
Packit ff56ff
	if (gen_name) {
Packit ff56ff
	    while (1) {
Packit ff56ff
		char expanded[12];
Packit ff56ff
		sprintf(expanded, pattern, curr_num);
Packit ff56ff
		memcpy(de->name, expanded, MSDOS_NAME);
Packit ff56ff
		for (scan = 0; scan < fs->root_entries; scan++)
Packit ff56ff
		    if (scan != next_free &&
Packit ff56ff
			!strncmp((const char *)root[scan].name,
Packit ff56ff
				 (const char *)de->name, MSDOS_NAME))
Packit ff56ff
			break;
Packit ff56ff
		if (scan == fs->root_entries)
Packit ff56ff
		    break;
Packit ff56ff
		if (++curr_num >= 10000)
Packit ff56ff
		    die("Unable to create unique name");
Packit ff56ff
	    }
Packit ff56ff
	} else {
Packit ff56ff
	    memcpy(de->name, pattern, MSDOS_NAME);
Packit ff56ff
	}
Packit ff56ff
	free(root);
Packit ff56ff
    }
Packit ff56ff
    ++n_files;
Packit ff56ff
    return offset;
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
/**
Packit ff56ff
 * Construct a full path (starting with '/') for the specified dentry,
Packit ff56ff
 * relative to the partition. All components are "long" names where possible.
Packit ff56ff
 *
Packit ff56ff
 * @param[in]   file    Information about dentry (file or directory) of interest
Packit ff56ff
 *
Packit ff56ff
 * return       Pointer to static string containing file's full path
Packit ff56ff
 */
Packit ff56ff
static char *path_name(DOS_FILE * file)
Packit ff56ff
{
Packit ff56ff
    static char path[PATH_NAME_MAX * 2];
Packit ff56ff
Packit ff56ff
    if (!file)
Packit ff56ff
	*path = 0;		/* Reached the root directory */
Packit ff56ff
    else {
Packit ff56ff
	if (strlen(path_name(file->parent)) > PATH_NAME_MAX)
Packit ff56ff
	    die("Path name too long.");
Packit ff56ff
	if (strcmp(path, "/") != 0)
Packit ff56ff
	    strcat(path, "/");
Packit ff56ff
Packit ff56ff
	/* Append the long name to the path,
Packit ff56ff
	 * or the short name if there isn't a long one
Packit ff56ff
	 */
Packit ff56ff
	strcpy(strrchr(path, 0),
Packit ff56ff
	       file->lfn ? file->lfn : file_name(file->dir_ent.name));
Packit ff56ff
    }
Packit ff56ff
    return path;
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
static const int day_n[] =
Packit ff56ff
    {   0,  31,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0 };
Packit ff56ff
/*    Jan  Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct  Nov  Dec              */
Packit ff56ff
Packit ff56ff
/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
Packit ff56ff
Packit ff56ff
static time_t date_dos2unix(unsigned short time, unsigned short date)
Packit ff56ff
{
Packit ff56ff
    int month, year;
Packit ff56ff
    time_t secs;
Packit ff56ff
Packit ff56ff
    month = ((date >> 5) & 15) - 1;
Packit ff56ff
    if (month < 0) {
Packit ff56ff
	/* make sure that nothing bad happens if the month bits were zero */
Packit ff56ff
	month = 0;
Packit ff56ff
    }
Packit ff56ff
    year = date >> 9;
Packit ff56ff
    secs =
Packit ff56ff
	(time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 +
Packit ff56ff
	86400 * ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 -
Packit ff56ff
		 ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653);
Packit ff56ff
    /* days since 1.1.70 plus 80's leap day */
Packit ff56ff
    return secs;
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
static char *file_stat(DOS_FILE * file)
Packit ff56ff
{
Packit ff56ff
    static char temp[100];
Packit ff56ff
    struct tm *tm;
Packit ff56ff
    char tmp[100];
Packit ff56ff
    time_t date;
Packit ff56ff
Packit ff56ff
    date =
Packit ff56ff
	date_dos2unix(le16toh(file->dir_ent.time), le16toh(file->dir_ent.date));
Packit ff56ff
    tm = localtime(&date);
Packit ff56ff
    strftime(tmp, 99, "%H:%M:%S %b %d %Y", tm);
Packit ff56ff
    sprintf(temp, "  Size %u bytes, date %s", le32toh(file->dir_ent.size), tmp);
Packit ff56ff
    return temp;
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
static int bad_name(DOS_FILE * file)
Packit ff56ff
{
Packit ff56ff
    int i, spc, suspicious = 0;
Packit ff56ff
    const char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:";
Packit ff56ff
    const unsigned char *name = file->dir_ent.name;
Packit ff56ff
    const unsigned char *ext = name + 8;
Packit ff56ff
Packit ff56ff
    /* Do not complain about (and auto-correct) the extended attribute files
Packit ff56ff
     * of OS/2. */
Packit ff56ff
    if (strncmp((const char *)name, "EA DATA  SF", 11) == 0 ||
Packit ff56ff
	strncmp((const char *)name, "WP ROOT  SF", 11) == 0)
Packit ff56ff
	return 0;
Packit ff56ff
Packit ff56ff
    /* check if we have neither a long filename nor a short name */
Packit ff56ff
    if ((file->lfn == NULL) && (file->dir_ent.lcase & FAT_NO_83NAME)) {
Packit ff56ff
	return 1;
Packit ff56ff
    }
Packit ff56ff
Packit ff56ff
    /* don't complain about the dummy 11 bytes used by patched Linux
Packit ff56ff
       kernels */
Packit ff56ff
    if (file->dir_ent.lcase & FAT_NO_83NAME)
Packit ff56ff
	return 0;
Packit ff56ff
Packit ff56ff
    for (i = 0; i < MSDOS_NAME; i++) {
Packit ff56ff
	if (name[i] < ' ' || name[i] == 0x7f)
Packit ff56ff
	    return 1;
Packit ff56ff
	if (name[i] > 0x7f)
Packit ff56ff
	    ++suspicious;
Packit ff56ff
	if (strchr(bad_chars, name[i]))
Packit ff56ff
	    return 1;
Packit ff56ff
    }
Packit ff56ff
Packit ff56ff
    spc = 0;
Packit ff56ff
    for (i = 0; i < 8; i++) {
Packit ff56ff
	if (name[i] == ' ')
Packit ff56ff
	    spc = 1;
Packit ff56ff
	else if (spc)
Packit ff56ff
	    /* non-space after a space not allowed, space terminates the name
Packit ff56ff
	     * part */
Packit ff56ff
	    return 1;
Packit ff56ff
    }
Packit ff56ff
Packit ff56ff
    spc = 0;
Packit ff56ff
    for (i = 0; i < 3; i++) {
Packit ff56ff
	if (ext[i] == ' ')
Packit ff56ff
	    spc = 1;
Packit ff56ff
	else if (spc)
Packit ff56ff
	    /* non-space after a space not allowed, space terminates the ext
Packit ff56ff
	     * part */
Packit ff56ff
	    return 1;
Packit ff56ff
    }
Packit ff56ff
Packit ff56ff
    /* Under GEMDOS, chars >= 128 are never allowed. */
Packit ff56ff
    if (atari_format && suspicious)
Packit ff56ff
	return 1;
Packit ff56ff
Packit ff56ff
    /* Under MS-DOS and Windows, chars >= 128 in short names are valid
Packit ff56ff
     * (but these characters can be visualised differently depending on
Packit ff56ff
     * local codepage: CP437, CP866, etc). The chars are all basically ok,
Packit ff56ff
     * so we shouldn't auto-correct such names. */
Packit ff56ff
    return 0;
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
static void lfn_remove(off_t from, off_t to)
Packit ff56ff
{
Packit ff56ff
    DIR_ENT empty;
Packit ff56ff
Packit ff56ff
    /* New dir entry is zeroed except first byte, which is set to 0xe5.
Packit ff56ff
     * This is to avoid that some FAT-reading OSes (not Linux! ;) stop reading
Packit ff56ff
     * a directory at the first zero entry...
Packit ff56ff
     */
Packit ff56ff
    memset(&empty, 0, sizeof(empty));
Packit ff56ff
    empty.name[0] = DELETED_FLAG;
Packit ff56ff
Packit ff56ff
    for (; from < to; from += sizeof(empty)) {
Packit ff56ff
	fs_write(from, sizeof(DIR_ENT), &empty);
Packit ff56ff
    }
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
static void drop_file(DOS_FS * fs, DOS_FILE * file)
Packit ff56ff
{
Packit ff56ff
    uint32_t cluster;
Packit ff56ff
Packit ff56ff
    MODIFY(file, name[0], DELETED_FLAG);
Packit ff56ff
    if (file->lfn)
Packit ff56ff
	lfn_remove(file->lfn_offset, file->offset);
Packit ff56ff
    for (cluster = FSTART(file, fs); cluster > 0 && cluster <
Packit ff56ff
	 fs->data_clusters + 2; cluster = next_cluster(fs, cluster))
Packit ff56ff
	set_owner(fs, cluster, NULL);
Packit ff56ff
    --n_files;
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
static void truncate_file(DOS_FS * fs, DOS_FILE * file, uint32_t clusters)
Packit ff56ff
{
Packit ff56ff
    int deleting;
Packit ff56ff
    uint32_t walk, next;
Packit ff56ff
Packit ff56ff
    walk = FSTART(file, fs);
Packit ff56ff
    if ((deleting = !clusters))
Packit ff56ff
	MODIFY_START(file, 0, fs);
Packit ff56ff
    while (walk > 0 && walk != -1) {
Packit ff56ff
	next = next_cluster(fs, walk);
Packit ff56ff
	if (deleting)
Packit ff56ff
	    set_fat(fs, walk, 0);
Packit ff56ff
	else if ((deleting = !--clusters))
Packit ff56ff
	    set_fat(fs, walk, -1);
Packit ff56ff
	walk = next;
Packit ff56ff
    }
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
static void auto_rename(DOS_FILE * file)
Packit ff56ff
{
Packit ff56ff
    DOS_FILE *first, *walk;
Packit ff56ff
    uint32_t number;
Packit ff56ff
Packit ff56ff
    if (!file->offset)
Packit ff56ff
	return;			/* cannot rename FAT32 root dir */
Packit ff56ff
    first = file->parent ? file->parent->first : root;
Packit ff56ff
    number = 0;
Packit ff56ff
    while (1) {
Packit ff56ff
	char num[8];
Packit ff56ff
	sprintf(num, "%07lu", (unsigned long)number);
Packit ff56ff
	memcpy(file->dir_ent.name, "FSCK", 4);
Packit ff56ff
	memcpy(file->dir_ent.name + 4, num, 7);
Packit ff56ff
	for (walk = first; walk; walk = walk->next)
Packit ff56ff
	    if (walk != file
Packit ff56ff
		&& !strncmp((const char *)walk->dir_ent.name,
Packit ff56ff
			    (const char *)file->dir_ent.name, MSDOS_NAME))
Packit ff56ff
		break;
Packit ff56ff
	if (!walk) {
Packit ff56ff
	    if (file->dir_ent.lcase & FAT_NO_83NAME) {
Packit ff56ff
		/* as we only assign a new 8.3 filename, reset flag that 8.3 name is not
Packit ff56ff
		   present */
Packit ff56ff
		file->dir_ent.lcase &= ~FAT_NO_83NAME;
Packit ff56ff
		/* reset the attributes, only keep DIR and VOLUME */
Packit ff56ff
		file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME);
Packit ff56ff
		fs_write(file->offset, MSDOS_NAME + 2, &file->dir_ent);
Packit ff56ff
	    } else {
Packit ff56ff
		fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
Packit ff56ff
	    }
Packit ff56ff
	    if (file->lfn)
Packit ff56ff
		lfn_fix_checksum(file->lfn_offset, file->offset,
Packit ff56ff
				 (const char *)file->dir_ent.name);
Packit ff56ff
	    return;
Packit ff56ff
	}
Packit ff56ff
	number++;
Packit ff56ff
	if (number > 9999999) {
Packit ff56ff
	    die("Too many files need repair.");
Packit ff56ff
	}
Packit ff56ff
    }
Packit ff56ff
    die("Can't generate a unique name.");
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
static void rename_file(DOS_FILE * file)
Packit ff56ff
{
Packit ff56ff
    unsigned char name[46];
Packit ff56ff
    unsigned char *walk, *here;
Packit ff56ff
Packit ff56ff
    if (!file->offset) {
Packit ff56ff
	printf("Cannot rename FAT32 root dir\n");
Packit ff56ff
	return;			/* cannot rename FAT32 root dir */
Packit ff56ff
    }
Packit ff56ff
    while (1) {
Packit ff56ff
	printf("New name: ");
Packit ff56ff
	fflush(stdout);
Packit ff56ff
	if (fgets((char *)name, 45, stdin)) {
Packit ff56ff
	    if ((here = (unsigned char *)strchr((const char *)name, '\n')))
Packit ff56ff
		*here = 0;
Packit ff56ff
	    for (walk = (unsigned char *)strrchr((const char *)name, 0);
Packit ff56ff
		 walk >= name && (*walk == ' ' || *walk == '\t'); walk--) ;
Packit ff56ff
	    walk[1] = 0;
Packit ff56ff
	    for (walk = name; *walk == ' ' || *walk == '\t'; walk++) ;
Packit ff56ff
	    if (file_cvt(walk, file->dir_ent.name)) {
Packit ff56ff
		if (file->dir_ent.lcase & FAT_NO_83NAME) {
Packit ff56ff
		    /* as we only assign a new 8.3 filename, reset flag that 8.3 name is not
Packit ff56ff
		       present */
Packit ff56ff
		    file->dir_ent.lcase &= ~FAT_NO_83NAME;
Packit ff56ff
		    /* reset the attributes, only keep DIR and VOLUME */
Packit ff56ff
		    file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME);
Packit ff56ff
		    fs_write(file->offset, MSDOS_NAME + 2, &file->dir_ent);
Packit ff56ff
		} else {
Packit ff56ff
		    fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
Packit ff56ff
		}
Packit ff56ff
		if (file->lfn)
Packit ff56ff
		    lfn_fix_checksum(file->lfn_offset, file->offset,
Packit ff56ff
				     (const char *)file->dir_ent.name);
Packit ff56ff
		return;
Packit ff56ff
	    }
Packit ff56ff
	}
Packit ff56ff
    }
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
static int handle_dot(DOS_FS * fs, DOS_FILE * file, int dots)
Packit ff56ff
{
Packit ff56ff
    const char *name;
Packit ff56ff
Packit ff56ff
    name =
Packit ff56ff
	strncmp((const char *)file->dir_ent.name, MSDOS_DOT,
Packit ff56ff
		MSDOS_NAME) ? ".." : ".";
Packit ff56ff
    if (!(file->dir_ent.attr & ATTR_DIR)) {
Packit ff56ff
	printf("%s\n  Is a non-directory.\n", path_name(file));
Packit ff56ff
	if (interactive)
Packit ff56ff
	    printf("1) Drop it\n2) Auto-rename\n3) Rename\n"
Packit ff56ff
		   "4) Convert to directory\n");
Packit ff56ff
	else
Packit ff56ff
	    printf("  Auto-renaming it.\n");
Packit ff56ff
	switch (interactive ? get_key("1234", "?") : '2') {
Packit ff56ff
	case '1':
Packit ff56ff
	    drop_file(fs, file);
Packit ff56ff
	    return 1;
Packit ff56ff
	case '2':
Packit ff56ff
	    auto_rename(file);
Packit ff56ff
	    printf("  Renamed to %s\n", file_name(file->dir_ent.name));
Packit ff56ff
	    return 0;
Packit ff56ff
	case '3':
Packit ff56ff
	    rename_file(file);
Packit ff56ff
	    return 0;
Packit ff56ff
	case '4':
Packit ff56ff
	    MODIFY(file, size, htole32(0));
Packit ff56ff
	    MODIFY(file, attr, file->dir_ent.attr | ATTR_DIR);
Packit ff56ff
	    break;
Packit ff56ff
	}
Packit ff56ff
    }
Packit ff56ff
    if (!dots) {
Packit ff56ff
	printf("Root contains directory \"%s\". Dropping it.\n", name);
Packit ff56ff
	drop_file(fs, file);
Packit ff56ff
	return 1;
Packit ff56ff
    }
Packit ff56ff
    return 0;
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
static int check_file(DOS_FS * fs, DOS_FILE * file)
Packit ff56ff
{
Packit ff56ff
    DOS_FILE *owner;
Packit ff56ff
    int restart;
Packit ff56ff
    uint32_t expect, curr, this, clusters, prev, walk, clusters2;
Packit ff56ff
Packit ff56ff
    if (file->dir_ent.attr & ATTR_DIR) {
Packit ff56ff
	if (le32toh(file->dir_ent.size)) {
Packit ff56ff
	    printf("%s\n  Directory has non-zero size. Fixing it.\n",
Packit ff56ff
		   path_name(file));
Packit ff56ff
	    MODIFY(file, size, htole32(0));
Packit ff56ff
	}
Packit ff56ff
	if (file->parent
Packit ff56ff
	    && !strncmp((const char *)file->dir_ent.name, MSDOS_DOT,
Packit ff56ff
			MSDOS_NAME)) {
Packit ff56ff
	    expect = FSTART(file->parent, fs);
Packit ff56ff
	    if (FSTART(file, fs) != expect) {
Packit ff56ff
		printf("%s\n  Start (%lu) does not point to parent (%lu)\n",
Packit ff56ff
		       path_name(file), (unsigned long)FSTART(file, fs), (long)expect);
Packit ff56ff
		MODIFY_START(file, expect, fs);
Packit ff56ff
	    }
Packit ff56ff
	    return 0;
Packit ff56ff
	}
Packit ff56ff
	if (file->parent
Packit ff56ff
	    && !strncmp((const char *)file->dir_ent.name, MSDOS_DOTDOT,
Packit ff56ff
			MSDOS_NAME)) {
Packit ff56ff
	    expect =
Packit ff56ff
		file->parent->parent ? FSTART(file->parent->parent, fs) : 0;
Packit ff56ff
	    if (fs->root_cluster && expect == fs->root_cluster)
Packit ff56ff
		expect = 0;
Packit ff56ff
	    if (FSTART(file, fs) != expect) {
Packit ff56ff
		printf("%s\n  Start (%lu) does not point to .. (%lu)\n",
Packit ff56ff
		       path_name(file), (unsigned long)FSTART(file, fs), (unsigned long)expect);
Packit ff56ff
		MODIFY_START(file, expect, fs);
Packit ff56ff
	    }
Packit ff56ff
	    return 0;
Packit ff56ff
	}
Packit ff56ff
	if (FSTART(file, fs) == 0) {
Packit ff56ff
	    printf("%s\n Start does point to root directory. Deleting dir. \n",
Packit ff56ff
		   path_name(file));
Packit ff56ff
	    MODIFY(file, name[0], DELETED_FLAG);
Packit ff56ff
	    return 0;
Packit ff56ff
	}
Packit ff56ff
    }
Packit ff56ff
    if (FSTART(file, fs) == 1) {
Packit ff56ff
	printf("%s\n  Bad start cluster 1. Truncating file.\n",
Packit ff56ff
	       path_name(file));
Packit ff56ff
	if (!file->offset)
Packit ff56ff
	    die("Bad FAT32 root directory! (bad start cluster 1)\n");
Packit ff56ff
	MODIFY_START(file, 0, fs);
Packit ff56ff
    }
Packit ff56ff
    if (FSTART(file, fs) >= fs->data_clusters + 2) {
Packit ff56ff
	printf
Packit ff56ff
	    ("%s\n  Start cluster beyond limit (%lu > %lu). Truncating file.\n",
Packit ff56ff
	     path_name(file), (unsigned long)FSTART(file, fs),
Packit ff56ff
	     (unsigned long)(fs->data_clusters + 1));
Packit ff56ff
	if (!file->offset)
Packit ff56ff
	    die("Bad FAT32 root directory! (start cluster beyond limit: %lu > %lu)\n",
Packit ff56ff
		(unsigned long)FSTART(file, fs),
Packit ff56ff
		(unsigned long)(fs->data_clusters + 1));
Packit ff56ff
	MODIFY_START(file, 0, fs);
Packit ff56ff
    }
Packit ff56ff
    clusters = prev = 0;
Packit ff56ff
    for (curr = FSTART(file, fs) ? FSTART(file, fs) :
Packit ff56ff
	 -1; curr != -1; curr = next_cluster(fs, curr)) {
Packit ff56ff
	FAT_ENTRY curEntry;
Packit ff56ff
	get_fat(&curEntry, fs->fat, curr, fs);
Packit ff56ff
Packit ff56ff
	if (!curEntry.value || bad_cluster(fs, curr)) {
Packit ff56ff
	    printf("%s\n  Contains a %s cluster (%lu). Assuming EOF.\n",
Packit ff56ff
		   path_name(file), curEntry.value ? "bad" : "free", (unsigned long)curr);
Packit ff56ff
	    if (prev)
Packit ff56ff
		set_fat(fs, prev, -1);
Packit ff56ff
	    else if (!file->offset)
Packit ff56ff
		die("FAT32 root dir starts with a bad cluster!");
Packit ff56ff
	    else
Packit ff56ff
		MODIFY_START(file, 0, fs);
Packit ff56ff
	    break;
Packit ff56ff
	}
Packit ff56ff
	if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) <=
Packit ff56ff
	    (uint64_t)clusters * fs->cluster_size) {
Packit ff56ff
	    printf
Packit ff56ff
		("%s\n  File size is %u bytes, cluster chain length is > %llu "
Packit ff56ff
		 "bytes.\n  Truncating file to %u bytes.\n", path_name(file),
Packit ff56ff
		 le32toh(file->dir_ent.size),
Packit ff56ff
		 (unsigned long long)clusters * fs->cluster_size,
Packit ff56ff
		 le32toh(file->dir_ent.size));
Packit ff56ff
	    truncate_file(fs, file, clusters);
Packit ff56ff
	    break;
Packit ff56ff
	}
Packit ff56ff
	if ((owner = get_owner(fs, curr))) {
Packit ff56ff
	    int do_trunc = 0;
Packit ff56ff
	    printf("%s  and\n", path_name(owner));
Packit ff56ff
	    printf("%s\n  share clusters.\n", path_name(file));
Packit ff56ff
	    clusters2 = 0;
Packit ff56ff
	    for (walk = FSTART(owner, fs); walk > 0 && walk != -1; walk =
Packit ff56ff
		 next_cluster(fs, walk))
Packit ff56ff
		if (walk == curr)
Packit ff56ff
		    break;
Packit ff56ff
		else
Packit ff56ff
		    clusters2++;
Packit ff56ff
	    restart = file->dir_ent.attr & ATTR_DIR;
Packit ff56ff
	    if (!owner->offset) {
Packit ff56ff
		printf("  Truncating second to %llu bytes because first "
Packit ff56ff
		       "is FAT32 root dir.\n",
Packit ff56ff
		       (unsigned long long)clusters * fs->cluster_size);
Packit ff56ff
		do_trunc = 2;
Packit ff56ff
	    } else if (!file->offset) {
Packit ff56ff
		printf("  Truncating first to %llu bytes because second "
Packit ff56ff
		       "is FAT32 root dir.\n",
Packit ff56ff
		       (unsigned long long)clusters2 * fs->cluster_size);
Packit ff56ff
		do_trunc = 1;
Packit ff56ff
	    } else if (interactive)
Packit ff56ff
		printf("1) Truncate first to %llu bytes%s\n"
Packit ff56ff
		       "2) Truncate second to %llu bytes\n",
Packit ff56ff
		       (unsigned long long)clusters2 * fs->cluster_size,
Packit ff56ff
		       restart ? " and restart" : "",
Packit ff56ff
		       (unsigned long long)clusters * fs->cluster_size);
Packit ff56ff
	    else
Packit ff56ff
		printf("  Truncating second to %llu bytes.\n",
Packit ff56ff
		       (unsigned long long)clusters * fs->cluster_size);
Packit ff56ff
	    if (do_trunc != 2
Packit ff56ff
		&& (do_trunc == 1
Packit ff56ff
		    || (interactive && get_key("12", "?") == '1'))) {
Packit ff56ff
		prev = 0;
Packit ff56ff
		clusters = 0;
Packit ff56ff
		for (this = FSTART(owner, fs); this > 0 && this != -1; this =
Packit ff56ff
		     next_cluster(fs, this)) {
Packit ff56ff
		    if (this == curr) {
Packit ff56ff
			if (prev)
Packit ff56ff
			    set_fat(fs, prev, -1);
Packit ff56ff
			else
Packit ff56ff
			    MODIFY_START(owner, 0, fs);
Packit ff56ff
			MODIFY(owner, size,
Packit ff56ff
			       htole32((uint64_t)clusters *
Packit ff56ff
				       fs->cluster_size));
Packit ff56ff
			if (restart)
Packit ff56ff
			    return 1;
Packit ff56ff
			while (this > 0 && this != -1) {
Packit ff56ff
			    set_owner(fs, this, NULL);
Packit ff56ff
			    this = next_cluster(fs, this);
Packit ff56ff
			}
Packit ff56ff
			this = curr;
Packit ff56ff
			break;
Packit ff56ff
		    }
Packit ff56ff
		    clusters++;
Packit ff56ff
		    prev = this;
Packit ff56ff
		}
Packit ff56ff
		if (this != curr)
Packit ff56ff
		    die("Internal error: didn't find cluster %d in chain"
Packit ff56ff
			" starting at %d", curr, FSTART(owner, fs));
Packit ff56ff
	    } else {
Packit ff56ff
		if (prev)
Packit ff56ff
		    set_fat(fs, prev, -1);
Packit ff56ff
		else
Packit ff56ff
		    MODIFY_START(file, 0, fs);
Packit ff56ff
		break;
Packit ff56ff
	    }
Packit ff56ff
	}
Packit ff56ff
	set_owner(fs, curr, file);
Packit ff56ff
	clusters++;
Packit ff56ff
	prev = curr;
Packit ff56ff
    }
Packit ff56ff
    if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) >
Packit ff56ff
	(uint64_t)clusters * fs->cluster_size) {
Packit ff56ff
	printf
Packit ff56ff
	    ("%s\n  File size is %u bytes, cluster chain length is %llu bytes."
Packit ff56ff
	     "\n  Truncating file to %llu bytes.\n", path_name(file),
Packit ff56ff
	     le32toh(file->dir_ent.size),
Packit ff56ff
	     (unsigned long long)clusters * fs->cluster_size,
Packit ff56ff
	     (unsigned long long)clusters * fs->cluster_size);
Packit ff56ff
	MODIFY(file, size,
Packit ff56ff
	       htole32((uint64_t)clusters * fs->cluster_size));
Packit ff56ff
    }
Packit ff56ff
    return 0;
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
static int check_files(DOS_FS * fs, DOS_FILE * start)
Packit ff56ff
{
Packit ff56ff
    while (start) {
Packit ff56ff
	if (check_file(fs, start))
Packit ff56ff
	    return 1;
Packit ff56ff
	start = start->next;
Packit ff56ff
    }
Packit ff56ff
    return 0;
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
static int check_dir(DOS_FS * fs, DOS_FILE ** root, int dots)
Packit ff56ff
{
Packit ff56ff
    DOS_FILE *parent, **walk, **scan;
Packit ff56ff
    int dot, dotdot, skip, redo;
Packit ff56ff
    int good, bad;
Packit ff56ff
Packit ff56ff
    if (!*root)
Packit ff56ff
	return 0;
Packit ff56ff
    parent = (*root)->parent;
Packit ff56ff
    good = bad = 0;
Packit ff56ff
    for (walk = root; *walk; walk = &(*walk)->next)
Packit ff56ff
	if (bad_name(*walk))
Packit ff56ff
	    bad++;
Packit ff56ff
	else
Packit ff56ff
	    good++;
Packit ff56ff
    if (*root && parent && good + bad > 4 && bad > good / 2) {
Packit ff56ff
	printf("%s\n  Has a large number of bad entries. (%d/%d)\n",
Packit ff56ff
	       path_name(parent), bad, good + bad);
Packit ff56ff
	if (!dots)
Packit ff56ff
	    printf("  Not dropping root directory.\n");
Packit ff56ff
	else if (!interactive)
Packit ff56ff
	    printf("  Not dropping it in auto-mode.\n");
Packit ff56ff
	else if (get_key("yn", "Drop directory ? (y/n)") == 'y') {
Packit ff56ff
	    truncate_file(fs, parent, 0);
Packit ff56ff
	    MODIFY(parent, name[0], DELETED_FLAG);
Packit ff56ff
	    /* buglet: deleted directory stays in the list. */
Packit ff56ff
	    return 1;
Packit ff56ff
	}
Packit ff56ff
    }
Packit ff56ff
    dot = dotdot = redo = 0;
Packit ff56ff
    walk = root;
Packit ff56ff
    while (*walk) {
Packit ff56ff
	if (!strncmp
Packit ff56ff
	    ((const char *)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME)
Packit ff56ff
	    || !strncmp((const char *)((*walk)->dir_ent.name), MSDOS_DOTDOT,
Packit ff56ff
			MSDOS_NAME)) {
Packit ff56ff
	    if (handle_dot(fs, *walk, dots)) {
Packit ff56ff
		*walk = (*walk)->next;
Packit ff56ff
		continue;
Packit ff56ff
	    }
Packit ff56ff
	    if (!strncmp
Packit ff56ff
		((const char *)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME))
Packit ff56ff
		dot++;
Packit ff56ff
	    else
Packit ff56ff
		dotdot++;
Packit ff56ff
	}
Packit ff56ff
	if (!((*walk)->dir_ent.attr & ATTR_VOLUME) && bad_name(*walk)) {
Packit ff56ff
	    puts(path_name(*walk));
Packit ff56ff
	    printf("  Bad short file name (%s).\n",
Packit ff56ff
		   file_name((*walk)->dir_ent.name));
Packit ff56ff
	    if (interactive)
Packit ff56ff
		printf("1) Drop file\n2) Rename file\n3) Auto-rename\n"
Packit ff56ff
		       "4) Keep it\n");
Packit ff56ff
	    else
Packit ff56ff
		printf("  Auto-renaming it.\n");
Packit ff56ff
	    switch (interactive ? get_key("1234", "?") : '3') {
Packit ff56ff
	    case '1':
Packit ff56ff
		drop_file(fs, *walk);
Packit ff56ff
		walk = &(*walk)->next;
Packit ff56ff
		continue;
Packit ff56ff
	    case '2':
Packit ff56ff
		rename_file(*walk);
Packit ff56ff
		redo = 1;
Packit ff56ff
		break;
Packit ff56ff
	    case '3':
Packit ff56ff
		auto_rename(*walk);
Packit ff56ff
		printf("  Renamed to %s\n", file_name((*walk)->dir_ent.name));
Packit ff56ff
		break;
Packit ff56ff
	    case '4':
Packit ff56ff
		break;
Packit ff56ff
	    }
Packit ff56ff
	}
Packit ff56ff
	/* don't check for duplicates of the volume label */
Packit ff56ff
	if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) {
Packit ff56ff
	    scan = &(*walk)->next;
Packit ff56ff
	    skip = 0;
Packit ff56ff
	    while (*scan && !skip) {
Packit ff56ff
		if (!((*scan)->dir_ent.attr & ATTR_VOLUME) &&
Packit ff56ff
		    !memcmp((*walk)->dir_ent.name, (*scan)->dir_ent.name,
Packit ff56ff
			    MSDOS_NAME)) {
Packit ff56ff
		    printf("%s\n  Duplicate directory entry.\n  First  %s\n",
Packit ff56ff
			   path_name(*walk), file_stat(*walk));
Packit ff56ff
		    printf("  Second %s\n", file_stat(*scan));
Packit ff56ff
		    if (interactive)
Packit ff56ff
			printf
Packit ff56ff
			    ("1) Drop first\n2) Drop second\n3) Rename first\n"
Packit ff56ff
			     "4) Rename second\n5) Auto-rename first\n"
Packit ff56ff
			     "6) Auto-rename second\n");
Packit ff56ff
		    else
Packit ff56ff
			printf("  Auto-renaming second.\n");
Packit ff56ff
		    switch (interactive ? get_key("123456", "?") : '6') {
Packit ff56ff
		    case '1':
Packit ff56ff
			drop_file(fs, *walk);
Packit ff56ff
			*walk = (*walk)->next;
Packit ff56ff
			skip = 1;
Packit ff56ff
			break;
Packit ff56ff
		    case '2':
Packit ff56ff
			drop_file(fs, *scan);
Packit ff56ff
			*scan = (*scan)->next;
Packit ff56ff
			continue;
Packit ff56ff
		    case '3':
Packit ff56ff
			rename_file(*walk);
Packit ff56ff
			printf("  Renamed to %s\n", path_name(*walk));
Packit ff56ff
			redo = 1;
Packit ff56ff
			break;
Packit ff56ff
		    case '4':
Packit ff56ff
			rename_file(*scan);
Packit ff56ff
			printf("  Renamed to %s\n", path_name(*walk));
Packit ff56ff
			redo = 1;
Packit ff56ff
			break;
Packit ff56ff
		    case '5':
Packit ff56ff
			auto_rename(*walk);
Packit ff56ff
			printf("  Renamed to %s\n",
Packit ff56ff
			       file_name((*walk)->dir_ent.name));
Packit ff56ff
			break;
Packit ff56ff
		    case '6':
Packit ff56ff
			auto_rename(*scan);
Packit ff56ff
			printf("  Renamed to %s\n",
Packit ff56ff
			       file_name((*scan)->dir_ent.name));
Packit ff56ff
			break;
Packit ff56ff
		    }
Packit ff56ff
		}
Packit ff56ff
		scan = &(*scan)->next;
Packit ff56ff
	    }
Packit ff56ff
	    if (skip)
Packit ff56ff
		continue;
Packit ff56ff
	}
Packit ff56ff
	if (!redo)
Packit ff56ff
	    walk = &(*walk)->next;
Packit ff56ff
	else {
Packit ff56ff
	    walk = root;
Packit ff56ff
	    dot = dotdot = redo = 0;
Packit ff56ff
	}
Packit ff56ff
    }
Packit ff56ff
    if (dots && !dot)
Packit ff56ff
	printf("%s\n  \".\" is missing. Can't fix this yet.\n",
Packit ff56ff
	       path_name(parent));
Packit ff56ff
    if (dots && !dotdot)
Packit ff56ff
	printf("%s\n  \"..\" is missing. Can't fix this yet.\n",
Packit ff56ff
	       path_name(parent));
Packit ff56ff
    return 0;
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
/**
Packit ff56ff
 * Check a dentry's cluster chain for bad clusters.
Packit ff56ff
 * If requested, we verify readability and mark unreadable clusters as bad.
Packit ff56ff
 *
Packit ff56ff
 * @param[inout]    fs          Information about the filesystem
Packit ff56ff
 * @param[in]       file        dentry to check
Packit ff56ff
 * @param[in]       read_test   Nonzero == verify that dentry's clusters can
Packit ff56ff
 *                              be read
Packit ff56ff
 */
Packit ff56ff
static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test)
Packit ff56ff
{
Packit ff56ff
    DOS_FILE *owner;
Packit ff56ff
    uint32_t walk, prev, clusters, next_clu;
Packit ff56ff
Packit ff56ff
    prev = clusters = 0;
Packit ff56ff
    for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2;
Packit ff56ff
	 walk = next_clu) {
Packit ff56ff
	next_clu = next_cluster(fs, walk);
Packit ff56ff
Packit ff56ff
	/* In this stage we are checking only for a loop within our own
Packit ff56ff
	 * cluster chain.
Packit ff56ff
	 * Cross-linking of clusters is handled in check_file()
Packit ff56ff
	 */
Packit ff56ff
	if ((owner = get_owner(fs, walk))) {
Packit ff56ff
	    if (owner == file) {
Packit ff56ff
		printf("%s\n  Circular cluster chain. Truncating to %lu "
Packit ff56ff
		       "cluster%s.\n", path_name(file), (unsigned long)clusters,
Packit ff56ff
		       clusters == 1 ? "" : "s");
Packit ff56ff
		if (prev)
Packit ff56ff
		    set_fat(fs, prev, -1);
Packit ff56ff
		else if (!file->offset)
Packit ff56ff
		    die("Bad FAT32 root directory! (bad start cluster)\n");
Packit ff56ff
		else
Packit ff56ff
		    MODIFY_START(file, 0, fs);
Packit ff56ff
	    }
Packit ff56ff
	    break;
Packit ff56ff
	}
Packit ff56ff
	if (bad_cluster(fs, walk))
Packit ff56ff
	    break;
Packit ff56ff
	if (read_test) {
Packit ff56ff
	    if (fs_test(cluster_start(fs, walk), fs->cluster_size)) {
Packit ff56ff
		prev = walk;
Packit ff56ff
		clusters++;
Packit ff56ff
	    } else {
Packit ff56ff
		printf("%s\n  Cluster %lu (%lu) is unreadable. Skipping it.\n",
Packit ff56ff
		       path_name(file), (unsigned long)clusters, (unsigned long)walk);
Packit ff56ff
		if (prev)
Packit ff56ff
		    set_fat(fs, prev, next_cluster(fs, walk));
Packit ff56ff
		else
Packit ff56ff
		    MODIFY_START(file, next_cluster(fs, walk), fs);
Packit ff56ff
		set_fat(fs, walk, -2);
Packit ff56ff
	    }
Packit ff56ff
	} else {
Packit ff56ff
	    prev = walk;
Packit ff56ff
	    clusters++;
Packit ff56ff
	}
Packit ff56ff
	set_owner(fs, walk, file);
Packit ff56ff
    }
Packit ff56ff
    /* Revert ownership (for now) */
Packit ff56ff
    for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2;
Packit ff56ff
	 walk = next_cluster(fs, walk))
Packit ff56ff
	if (bad_cluster(fs, walk))
Packit ff56ff
	    break;
Packit ff56ff
	else if (get_owner(fs, walk) == file)
Packit ff56ff
	    set_owner(fs, walk, NULL);
Packit ff56ff
	else
Packit ff56ff
	    break;
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
static void undelete(DOS_FS * fs, DOS_FILE * file)
Packit ff56ff
{
Packit ff56ff
    uint32_t clusters, left, prev, walk;
Packit ff56ff
Packit ff56ff
    clusters = left = (le32toh(file->dir_ent.size) + fs->cluster_size - 1) /
Packit ff56ff
	fs->cluster_size;
Packit ff56ff
    prev = 0;
Packit ff56ff
Packit ff56ff
    walk = FSTART(file, fs);
Packit ff56ff
Packit ff56ff
    while (left && (walk >= 2) && (walk < fs->data_clusters + 2)) {
Packit ff56ff
Packit ff56ff
	FAT_ENTRY curEntry;
Packit ff56ff
	get_fat(&curEntry, fs->fat, walk, fs);
Packit ff56ff
Packit ff56ff
	if (!curEntry.value)
Packit ff56ff
	    break;
Packit ff56ff
Packit ff56ff
	left--;
Packit ff56ff
	if (prev)
Packit ff56ff
	    set_fat(fs, prev, walk);
Packit ff56ff
	prev = walk;
Packit ff56ff
	walk++;
Packit ff56ff
    }
Packit ff56ff
    if (prev)
Packit ff56ff
	set_fat(fs, prev, -1);
Packit ff56ff
    else
Packit ff56ff
	MODIFY_START(file, 0, fs);
Packit ff56ff
    if (left)
Packit ff56ff
	printf("Warning: Did only undelete %lu of %lu cluster%s.\n",
Packit ff56ff
	       (unsigned long)clusters - left, (unsigned long)clusters, clusters == 1 ? "" : "s");
Packit ff56ff
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
static void new_dir(void)
Packit ff56ff
{
Packit ff56ff
    lfn_reset();
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
/**
Packit ff56ff
 * Create a description for a referenced dentry and insert it in our dentry
Packit ff56ff
 * tree. Then, go check the dentry's cluster chain for bad clusters and
Packit ff56ff
 * cluster loops.
Packit ff56ff
 *
Packit ff56ff
 * @param[inout]    fs      Information about the filesystem
Packit ff56ff
 * @param[out]      chain
Packit ff56ff
 * @param[in]       parent  Information about parent directory of this file
Packit ff56ff
 *                          NULL == no parent ('file' is root directory)
Packit ff56ff
 * @param[in]       offset  Partition-relative byte offset of directory entry of interest
Packit ff56ff
 *                          0 == Root directory
Packit ff56ff
 * @param           cp
Packit ff56ff
 */
Packit ff56ff
static void add_file(DOS_FS * fs, DOS_FILE *** chain, DOS_FILE * parent,
Packit ff56ff
		     off_t offset, FDSC ** cp)
Packit ff56ff
{
Packit ff56ff
    DOS_FILE *new;
Packit ff56ff
    DIR_ENT de;
Packit ff56ff
    FD_TYPE type;
Packit ff56ff
Packit ff56ff
    if (offset)
Packit ff56ff
	fs_read(offset, sizeof(DIR_ENT), &de);
Packit ff56ff
    else {
Packit ff56ff
	/* Construct a DIR_ENT for the root directory */
Packit ff56ff
	memset(&de, 0, sizeof de);
Packit ff56ff
	memcpy(de.name, "           ", MSDOS_NAME);
Packit ff56ff
	de.attr = ATTR_DIR;
Packit ff56ff
	de.start = htole16(fs->root_cluster & 0xffff);
Packit ff56ff
	de.starthi = htole16((fs->root_cluster >> 16) & 0xffff);
Packit ff56ff
    }
Packit ff56ff
    if ((type = file_type(cp, (char *)de.name)) != fdt_none) {
Packit ff56ff
	if (type == fdt_undelete && (de.attr & ATTR_DIR))
Packit ff56ff
	    die("Can't undelete directories.");
Packit ff56ff
	file_modify(cp, (char *)de.name);
Packit ff56ff
	fs_write(offset, 1, &de);
Packit ff56ff
    }
Packit ff56ff
    if (IS_FREE(de.name)) {
Packit ff56ff
	lfn_check_orphaned();
Packit ff56ff
	return;
Packit ff56ff
    }
Packit ff56ff
    if (de.attr == VFAT_LN_ATTR) {
Packit ff56ff
	lfn_add_slot(&de, offset);
Packit ff56ff
	return;
Packit ff56ff
    }
Packit ff56ff
    new = qalloc(&mem_queue, sizeof(DOS_FILE));
Packit ff56ff
    new->lfn = lfn_get(&de, &new->lfn_offset);
Packit ff56ff
    new->offset = offset;
Packit ff56ff
    memcpy(&new->dir_ent, &de, sizeof(de));
Packit ff56ff
    new->next = new->first = NULL;
Packit ff56ff
    new->parent = parent;
Packit ff56ff
    if (type == fdt_undelete)
Packit ff56ff
	undelete(fs, new);
Packit ff56ff
    **chain = new;
Packit ff56ff
    *chain = &new->next;
Packit ff56ff
    if (list) {
Packit ff56ff
	printf("Checking file %s", path_name(new));
Packit ff56ff
	if (new->lfn)
Packit ff56ff
	    printf(" (%s)", file_name(new->dir_ent.name));	/* (8.3) */
Packit ff56ff
	printf("\n");
Packit ff56ff
    }
Packit ff56ff
    /* Don't include root directory, '.', or '..' in the total file count */
Packit ff56ff
    if (offset &&
Packit ff56ff
	strncmp((const char *)de.name, MSDOS_DOT, MSDOS_NAME) != 0 &&
Packit ff56ff
	strncmp((const char *)de.name, MSDOS_DOTDOT, MSDOS_NAME) != 0)
Packit ff56ff
	++n_files;
Packit ff56ff
    test_file(fs, new, test);	/* Bad cluster check */
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp);
Packit ff56ff
Packit ff56ff
static int scan_dir(DOS_FS * fs, DOS_FILE * this, FDSC ** cp)
Packit ff56ff
{
Packit ff56ff
    DOS_FILE **chain;
Packit ff56ff
    int i;
Packit ff56ff
    uint32_t clu_num;
Packit ff56ff
Packit ff56ff
    chain = &this->first;
Packit ff56ff
    i = 0;
Packit ff56ff
    clu_num = FSTART(this, fs);
Packit ff56ff
    new_dir();
Packit ff56ff
    while (clu_num > 0 && clu_num != -1) {
Packit ff56ff
	add_file(fs, &chain, this,
Packit ff56ff
		 cluster_start(fs, clu_num) + (i % fs->cluster_size), cp);
Packit ff56ff
	i += sizeof(DIR_ENT);
Packit ff56ff
	if (!(i % fs->cluster_size))
Packit ff56ff
	    if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
Packit ff56ff
		break;
Packit ff56ff
    }
Packit ff56ff
    lfn_check_orphaned();
Packit ff56ff
    if (check_dir(fs, &this->first, this->offset))
Packit ff56ff
	return 0;
Packit ff56ff
    if (check_files(fs, this->first))
Packit ff56ff
	return 1;
Packit ff56ff
    return subdirs(fs, this, cp);
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
/**
Packit ff56ff
 * Recursively scan subdirectories of the specified parent directory.
Packit ff56ff
 *
Packit ff56ff
 * @param[inout]    fs      Information about the filesystem
Packit ff56ff
 * @param[in]       parent  Identifies the directory to scan
Packit ff56ff
 * @param[in]       cp
Packit ff56ff
 *
Packit ff56ff
 * @return  0   Success
Packit ff56ff
 * @return  1   Error
Packit ff56ff
 */
Packit ff56ff
static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp)
Packit ff56ff
{
Packit ff56ff
    DOS_FILE *walk;
Packit ff56ff
Packit ff56ff
    for (walk = parent ? parent->first : root; walk; walk = walk->next)
Packit ff56ff
	if (walk->dir_ent.attr & ATTR_DIR)
Packit ff56ff
	    if (strncmp((const char *)walk->dir_ent.name, MSDOS_DOT, MSDOS_NAME)
Packit ff56ff
		&& strncmp((const char *)walk->dir_ent.name, MSDOS_DOTDOT,
Packit ff56ff
			   MSDOS_NAME))
Packit ff56ff
		if (scan_dir(fs, walk, file_cd(cp, (char *)walk->dir_ent.name)))
Packit ff56ff
		    return 1;
Packit ff56ff
    return 0;
Packit ff56ff
}
Packit ff56ff
Packit ff56ff
/**
Packit ff56ff
 * Scan all directory and file information for errors.
Packit ff56ff
 *
Packit ff56ff
 * @param[inout]    fs      Information about the filesystem
Packit ff56ff
 *
Packit ff56ff
 * @return  0   Success
Packit ff56ff
 * @return  1   Error
Packit ff56ff
 */
Packit ff56ff
int scan_root(DOS_FS * fs)
Packit ff56ff
{
Packit ff56ff
    DOS_FILE **chain;
Packit ff56ff
    int i;
Packit ff56ff
Packit ff56ff
    root = NULL;
Packit ff56ff
    chain = &roo;;
Packit ff56ff
    new_dir();
Packit ff56ff
    if (fs->root_cluster) {
Packit ff56ff
	add_file(fs, &chain, NULL, 0, &fp_root);
Packit ff56ff
    } else {
Packit ff56ff
	for (i = 0; i < fs->root_entries; i++)
Packit ff56ff
	    add_file(fs, &chain, NULL, fs->root_start + i * sizeof(DIR_ENT),
Packit ff56ff
		     &fp_root);
Packit ff56ff
    }
Packit ff56ff
    lfn_check_orphaned();
Packit ff56ff
    (void)check_dir(fs, &root, 0);
Packit ff56ff
    if (check_files(fs, root))
Packit ff56ff
	return 1;
Packit ff56ff
    return subdirs(fs, NULL, &fp_root);
Packit ff56ff
}