Added tries to fix file systems with incorrect size

edge.strict_endians
Jean-Pierre André 2011-02-08 13:52:13 +01:00
parent 3160203c97
commit 6b570dfe96
1 changed files with 102 additions and 33 deletions

View File

@ -88,6 +88,8 @@ static const char *EXEC_NAME = "ntfsfix";
static const char OK[] = "OK\n";
static const char FAILED[] = "FAILED\n";
#define DEFAULT_SECTOR_SIZE 512
static struct {
char *volume;
BOOL no_action;
@ -624,46 +626,112 @@ static int rewrite_boot(struct ntfs_device *dev, NTFS_BOOT_SECTOR *bs)
return (res);
}
/*
* Try an alternate boot sector and fix the real one
*
* Only after successful checks is the boot sector rewritten.
*
* The alternate boot sector is not rewritten, either because it
* was found correct, or because we truncated the file system
* and the last actual sector might be part of some file.
*
* Returns 0 if successful
*/
static int try_fix_boot(ntfs_volume *vol, NTFS_BOOT_SECTOR *bs,
s64 read_sector, s64 fix_sectors)
{
s64 br;
int res;
s64 got_sectors;
res = -1;
br = ntfs_pread(vol->dev, read_sector*DEFAULT_SECTOR_SIZE,
sizeof(NTFS_BOOT_SECTOR), bs);
if (br != sizeof(NTFS_BOOT_SECTOR)) {
if (br != -1)
errno = EINVAL;
if (!br)
ntfs_log_error("Failed to read alternate bootsector (size=0)\n");
else
ntfs_log_perror("Error reading alternate bootsector");
} else {
got_sectors = le64_to_cpu(bs->number_of_sectors);
bs->number_of_sectors = cpu_to_le64(fix_sectors);
if (ntfs_boot_sector_is_ntfs(bs)
&& !ntfs_boot_sector_parse(vol, bs)) {
ntfs_log_info("The alternate bootsector is usable\n");
if (fix_sectors != got_sectors)
ntfs_log_info("Set sector count to %lld instead of %lld\n",
(long long)fix_sectors,
(long long)got_sectors);
/* fix the normal boot sector */
if (!opt.no_action) {
res = rewrite_boot(vol->dev, bs);
} else
res = 0;
}
if (!res && !opt.no_action)
ntfs_log_info("The boot sector has been rewritten\n");
}
return (res);
}
/*
* Try the alternate boot sector if the normal one is bad
*
* Actually :
* - first try the last sector of the partition (expected location)
* - then try the last sector as shown in the main boot sector,
* (could be meaningful for an undersized partition)
* - finally try truncating the file system actual size of partition
* (could be meaningful for an oversized partition)
*
* if successful, rewrite the normal boot sector accordingly
*
* Returns 0 if successful
*/
static int try_alternate_boot(struct ntfs_device *dev, NTFS_BOOT_SECTOR *bs)
static int try_alternate_boot(ntfs_volume *vol, NTFS_BOOT_SECTOR *bs,
s64 shown_sectors)
{
s64 blocks;
s64 br;
s64 actual_sectors;
int res;
res = -1;
blocks = ntfs_device_size_get(dev,512);
if (blocks > 0) {
br = ntfs_pread(dev, (blocks - 1)*512,
sizeof(NTFS_BOOT_SECTOR), bs);
if (br != sizeof(NTFS_BOOT_SECTOR)) {
if (br != -1)
errno = EINVAL;
if (!br)
ntfs_log_error("Failed to read alternate bootsector (size=0)\n");
else
ntfs_log_perror("Error reading alternate bootsector");
} else {
if (!ntfs_boot_sector_is_ntfs(bs)) {
ntfs_log_error("Both boot sectors are unusable\n");
// provide information about NTFS signature
} else {
ntfs_log_info("The alternate bootsector is usable\n");
/* fix the normal boot sector */
if (!opt.no_action) {
res = rewrite_boot(dev, bs);
} else
res = 0;
}
}
}
ntfs_log_info("Trying the alternate boot sector\n");
/*
* We do not rely on the sector size defined in the
* boot sector, supposed to be corrupt, so we assume the
* sector size is 512 (some ioctl might be be needed to
* get the actual size). This is only used to guess the
* alternate boot sector location and should not damage
* anything if wrong.
* Other values might be worth trying (1024, 2048, 4096).
*
* Note : the real last sector is not accounted for here.
*/
actual_sectors = ntfs_device_size_get(vol->dev,DEFAULT_SECTOR_SIZE) - 1;
/* first try the actual last sector */
if ((actual_sectors > 0)
&& !try_fix_boot(vol,bs,actual_sectors,actual_sectors))
res = 0;
/* then try the shown last sector, if less than actual */
if (res
&& (shown_sectors > 0)
&& (shown_sectors < actual_sectors)
&& !try_fix_boot(vol,bs,shown_sectors,shown_sectors))
res = 0;
/* then try reducing the number of sectors to actual value */
if (res
&& (shown_sectors > actual_sectors)
&& !try_fix_boot(vol,bs,0,actual_sectors))
res = 0;
return (res);
}
@ -679,6 +747,7 @@ static int fix_startup(struct ntfs_device *dev, unsigned long flags)
s64 br;
ntfs_volume *vol;
BOOL dev_open;
s64 shown_sectors;
NTFS_BOOT_SECTOR *bs;
int res;
int eo;
@ -737,16 +806,16 @@ static int fix_startup(struct ntfs_device *dev, unsigned long flags)
ntfs_log_perror("Error reading bootsector");
goto error_exit;
}
if (!ntfs_boot_sector_is_ntfs(bs)) {
if (!ntfs_boot_sector_is_ntfs(bs)
/* get the bootsector data, only fails when inconsistent */
|| (ntfs_boot_sector_parse(vol, bs) < 0)) {
shown_sectors = le64_to_cpu(bs->number_of_sectors);
/* boot sector is wrong, try the alternate boot sector */
if (try_alternate_boot(dev, bs)) {
if (try_alternate_boot(vol, bs, shown_sectors)) {
errno = EINVAL;
goto error_exit;
}
}
/* get the bootsector data, only fails when inconsistent */
if (ntfs_boot_sector_parse(vol, bs) < 0)
goto error_exit;
res = 0;
error_exit:
if (res) {