Changes by: antona
Update of /cvsroot/linux-ntfs/linux-ntfs/libntfs
In directory usw-pr-cvs1:/tmp/cvs-serv25241/libntfs
Added Files:
volume.c
Log Message:
Added volume.c code and fixed some typos and inconsistencies.
--- NEW FILE ---
/*
* volume.c - NTFS volume handling code. Part of the Linux-NTFS project.
*
* Copyright (c) 2000,2001 Anton Altaparmakov.
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program/include file is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the Linux-NTFS
* distribution in the file COPYING); if not, write to the Free Software
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/types.h>
#include "endians.h"
#include "attrib.h"
#include "volume.h"
#include "bootsect.h"
#include "disk_io.h"
#include "mft.h"
ntfs_volume *ntfs_open(const char *name)
{
const char *OK = "OK";
const char *FAILED = "FAILED";
ntfs_volume *vol = NULL;
BOOT_SECTOR *bs = NULL;
MFT_RECORD_HEADER *mb = NULL;
ATTRIBUTE_RECORD_HEADER *a;
VOLUME_INFORMATION *vinf;
__u32 u;
__u64 l;
ssize_t br;
__u8 SectorsPerCluster, bits;
__s8 c;
if (!name) {
errno = EINVAL;
return NULL;
}
/* Allocate the volume structure. */
if (!(vol = (ntfs_volume *)malloc(sizeof(ntfs_volume)))) {
perror("Error allocating memory for ntfs volume structure");
errno = ENOMEM;
goto error_exit;
}
/* Zero the volume structure. */
memset(vol, 0, sizeof(ntfs_volume));
/* Make a copy of the partition name. */
if (!(vol->dev_name = strdup(name))) {
perror("Error allocating memory for partition name");
errno = ENOMEM;
goto error_exit;
}
/* Allocate the boot sector structure. */
if (!(bs = (BOOT_SECTOR *)malloc(sizeof(BOOT_SECTOR)))) {
puts(FAILED);
perror("Error allocating memory for bootsector");
errno = ENOMEM;
goto error_exit;
}
printf("Reading bootsector... ");
if ((vol->fd = open(name, O_RDWR)) == -1) {
int eo = errno;
puts(FAILED);
perror("Error opening partition file");
errno = eo;
goto error_exit;
}
/* Now read the bootsector. */
br = ntfs_pread(vol->fd, bs, sizeof(BOOT_SECTOR), 1, 0);
if (br != 1) {
int eo = errno;
puts(FAILED);
#define ESTR "Error reading bootsector"
if (br == -1) {
perror(ESTR);
errno = eo;
} else if (!br) {
fprintf(stderr, "Error: partition is smaller than " \
"bootsector size. Weird!\n");
errno = EINVAL;
} else {
fprintf(stderr, ESTR ": unknown error\n");
errno = EINVAL;
}
#undef ESTR
goto error_exit;
}
puts(OK);
if (!is_boot_sector_ntfs(bs, 0)) {
fprintf(stderr, "Error: %s is not a valid NTFS partition!\n",
name);
errno = EINVAL;
goto error_exit;
}
/* The bounds checks on mft_lcn and mft_mirr_lcn (i.e. them being
below or equal the number_of_clusters) really belong in the
is_boot_sector_ntfs but in this way we can just do this once. */
sectors_per_cluster = bs->bpb.sectors_per_cluster;
#ifdef DEBUG
printf("NumberOfSectors = %Li\n", sle64_to_cpu(bs->number_of_sectors));
printf("SectorsPerCluster = 0x%x\n", sectors_per_cluster);
#endif
for (bits = 0; sectors_per_cluster > 1; sectors_per_cluster >>= 1)
bits++;
sectors_per_cluster = bs->bpb.sectors_per_cluster;
vol->number_of_clusters = sle64_to_cpu(bs->number_of_sectors) >> bits;
vol->mft_lcn = sle64_to_cpu(bs->mft_lcn);
vol->mftmirr_lcn = sle64_to_cpu(bs->mftmirr_lcn);
if ((vol->mft_lcn > vol->number_of_clusters) ||
(vol->mftmirr_lcn > vol->number_of_clusters)) {
fprintf(stderr, "Error: %s is not a valid NTFS partition! " \
"($Mft LCN or\n$MftMirr LCN is greater than " \
"the number of clusters!\n", name);
errno = EINVAL;
goto error_exit;
}
vol->cluster_size = sectors_per_cluster *
le16_to_cpu(bs->bpb.bytes_per_sector);
u = vol->cluster_size;
for (bits = 0; u > 1; u >>= 1)
bits++;
vol->cluster_size_bits = bits;
/* Need to get the clusters per mft record and handle it if it is
negative. Then calculate the mft_record_size and replace the
cluster_size with the mft_record_size where applicable below!
A value of 0x80 is illegal, thus signed char is actually ok! */
c = bs->clusters_per_mft_record;
#ifdef DEBUG
printf("ClusterSize = 0x%x\n", vol->cluster_size);
printf("ClusterSizeBits = %u\n", vol->cluster_size_bits);
printf("ClustersPerMftRecord = 0x%x\n", c);
#endif
/* When clusters_per_mft_record is negative, it means that it is to
be taken to be the negative base 2 logarithm of the mft_record_size
in bytes. Then:
mft_record_size = 2^(-clusters_per_mft_record) bytes. */
if (c < 0)
vol->mft_record_size = 1 << -c;
else
vol->mft_record_size = vol->cluster_size * c;
u = vol->mft_record_size;
for (bits = 0; u > 1; u >>= 1)
bits++;
vol->mft_record_size_bits = bits;
if (vol->cluster_size > vol->mft_record_size) {
vol->mft_records_per_cluster = vol->cluster_size /
vol->mft_record_size;
vol->clusters_per_mft_record = 0;
} else {
vol->mft_records_per_cluster = 0;
vol->clusters_per_mft_record = vol->mft_record_size /
vol->cluster_size;
}
#ifdef DEBUG
printf("MftRecordSize = 0x%x\n", vol->mft_record_size);
printf("MftRecordSizeBits = %u\n", vol->mft_record_size_bits);
printf("There are %i mft records in each cluster.\n",
vol->mft_records_per_cluster);
printf("There are %i clusters in each mft record.\n",
vol->clusters_per_mft_record);
#endif
/* Start with $Mft. */
printf("Loading $Mft... ");
if (!(mb = (MFT_RECORD_HEADER *)malloc(vol->mft_record_size))) {
puts(FAILED);
perror("Error allocating memory for $Mft");
errno = ENOMEM;
goto error_exit;
}
/* Can't use get_mft_records yet!!! */
br = mst_pread(vol->fd, (char *)mb, vol->mft_record_size, 1,
vol->mft_lcn << vol->cluster_size_bits);
if (br != 1) {
puts(FAILED);
#define ESTR "Error reading $Mft"
if (br == -1)
perror(ESTR);
else if (br < 4)
fprintf(stderr, "Error: $Mft LCN is outside of "
"the partition?!?\n");
else
fprintf(stderr, ESTR ": unknown error\n");
#undef ESTR
errno = EIO;
goto error_exit;
}
if (is_baad_record(mb)) {
puts(FAILED);
fprintf(stderr, "Error: Incomplete multi sector "
"transfer detected in $Mft.\nCannot "
"handle this yet. )-:\n");
errno = EIO;
goto error_exit;
}
if (!is_mft_recordp(mb)) {
puts(FAILED);
fprintf(stderr, "Error: Invalid mft record for $Mft.\n"
"Cannot handle this yet. )-:\n");
errno = EIO;
goto error_exit;
}
/* Find the bitmap attribute. */
a = find_attribute(mb, $BITMAP, NULL);
if (!a) {
puts(FAILED);
fprintf(stderr, "$bitmap attribute not found in $Mft?!?\n");
errno = EIO;
goto error_exit;
}
/* Get attribute value size and allocate a big enough buffer.*/
l = get_attribute_value_length(a);
if (!l) {
puts(OK);
puts("Error: $bitmap in $Mft has zero length! T'is weird!");
errno = EIO;
goto error_exit;
}
vol->mft_bitmap = (__u8*)malloc(l);
if (!vol->mft_bitmap) {
puts(FAILED);
puts("Not enough memory to load bitmap attribute from $Mft.");
errno = ENOMEM;
goto error_exit;
}
/* Read in the bitmap attribute value into the buffer. */
if (l != get_attribute_value(vol, a, vol->mft_bitmap)) {
puts(FAILED);
puts("Amount of data read does not correspond to expected "
"length!");
errno = EIO;
goto error_exit;
}
/* Find the $DATA attribute in $Mft. */
a = find_attribute(mb, $DATA, NULL);
if (!a) {
puts(FAILED);
fprintf(stderr, "$DATA attribute not found in $Mft?!?\n");
errno = EIO;
goto error_exit;
}
/* Determine the number of mft records in $Mft. */
vol->number_of_mft_records =
((NONRESIDENT_ATTRIBUTE_RECORD_HEADER*)a)->data_size >>
vol->mft_record_size_bits;
/* The $DATA attribute of the $Mft has to be non-resident. */
if (!a->non_resident) {
puts(FAILED);
fprintf(stderr, "$Mft $DATA attribute is resident!?!\n");
errno = EINVAL;
goto error_exit;
}
/* Get the run list. */
vol->mft_runlist = decompress_run_list(
(NONRESIDENT_ATTRIBUTE_RECORD_HEADER*)a);
if (!vol->mft_runlist) {
puts(FAILED);
fprintf(stderr, "Error decompressing run list from $Mft $DATA"
" attribute.\n");
errno = EINVAL;
goto error_exit;
}
puts(OK);
/* Now load the bitmap from $Bitmap. */
printf("Loading $Bitmap... ");
br = get_mft_records(vol, (char*)mb, 6, 1);
if (br != 1) {
puts(FAILED);
errno = EIO;
goto error_exit;
}
if (is_baad_record(mb)) {
puts(FAILED);
fprintf(stderr, "Error: Incomplete multi sector "
"transfer detected in $Bitmap.\nCannot "
"handle this yet. )-:\n");
errno = EIO;
goto error_exit;
}
if (!is_mft_recordp(mb)) {
puts(FAILED);
fprintf(stderr, "Error: Invalid $Mft record for $Bitmap.\n"
"Cannot handle this yet. )-:\n");
printf("Record is of type: %c%c%c%c\n", ((char*)mb)[0],
((char*)mb)[1],
((char*)mb)[2],
((char*)mb)[3]);
errno = EIO;
goto error_exit;
}
/* Find the bitmap (it is in the $DATA attribute). */
a = find_attribute(mb, $DATA, NULL);
if (!a) {
puts(FAILED);
fprintf(stderr, "bitmap attribute not found in $Bitmap?!?\n");
errno = EIO;
goto error_exit;
}
/* Get attribute value size and allocate a big enough buffer.*/
l = get_attribute_value_length(a);
if (!l) {
puts(OK);
puts("Error: bitmap in $Bitmap has zero length! T'is weird!");
errno = EIO;
goto error_exit;
}
vol->lcn_bitmap = (__u8*)malloc(l);
if (!vol->lcn_bitmap) {
puts(FAILED);
puts("Not enough memory to load $Bitmap.");
errno = ENOMEM;
goto error_exit;
}
/* Read in the bitmap attribute value into the buffer. */
if (l != get_attribute_value(vol, a, vol->lcn_bitmap)) {
puts(FAILED);
puts("Amount of data read does not correspond to expected "
"length!");
errno = EIO;
goto error_exit;
}
puts(OK);
/* Now load the upcase table from $UpCase. */
printf("Loading $UpCase... ");
br = get_mft_records(vol, (char*)mb, 10, 1);
if (br != 1) {
puts(FAILED);
errno = EIO;
goto error_exit;
}
if (is_baad_record(mb)) {
puts(FAILED);
fprintf(stderr, "Error: Incomplete multi sector "
"transfer detected in $UpCase.\nCannot "
"handle this yet. )-:\n");
errno = EIO;
goto error_exit;
}
if (!is_mft_recordp(mb)) {
puts(FAILED);
fprintf(stderr, "Error: Invalid $Mft record for $UpCase.\n"
"Cannot handle this yet. )-:\n");
errno = EIO;
goto error_exit;
}
/* Find the bitmap (it is in the $DATA attribute). */
a = find_attribute(mb, $DATA, NULL);
if (!a) {
puts(FAILED);
fprintf(stderr, "$DATA attribute not found in $UpCase?!?\n");
errno = EIO;
goto error_exit;
}
/* Get attribute value size and allocate a big enough buffer.*/
l = get_attribute_value_length(a);
if (!l) {
puts(OK);
puts("Error: $DATA in $UpCase has zero length! T'is weird!");
errno = EIO;
goto error_exit;
}
/* The upcase table has by definition to have a length equal to
65536 2-byte Unicode characters. */
if (l != 65536 * 2) {
puts(OK);
puts("Error: Upcase table (in $UpCase) has incorrect length!");
errno = EINVAL;
goto error_exit;
}
vol->upcase = (__u16*)malloc(l);
if (!vol->upcase) {
puts(FAILED);
puts("Not enough memory to load $UpCase.");
errno = ENOMEM;
goto error_exit;
}
/* Read in the $DATA attribute value into the buffer. */
if (l != get_attribute_value(vol, a, (__u8*)vol->upcase)) {
puts(FAILED);
puts("Amount of data read does not correspond to expected "
"length!");
errno = EIO;
goto error_exit;
}
puts(OK);
/* Now load $Volume and set the version information and flags in the
vol structure accordingly. */
printf("Loading $Volume... ");
br = get_mft_records(vol, (char*)mb, 3, 1);
if (br != 1) {
puts(FAILED);
errno = EIO;
goto error_exit;
}
if (is_baad_record(mb)) {
puts(FAILED);
fprintf(stderr, "Error: Incomplete multi sector "
"transfer detected in $Volume.\nCannot "
"handle this yet. )-:\n");
errno = EIO;
goto error_exit;
}
if (!is_mft_recordp(mb)) {
puts(FAILED);
fprintf(stderr, "Error: Invalid $Mft record for $Volume.\n"
"Cannot handle this yet. )-:\n");
errno = EIO;
goto error_exit;
}
/* Find the $VOLUME_INFORMATION attribute. */
a = find_attribute(mb, $VOLUME_INFORMATION, NULL);
if (!a) {
puts(FAILED);
fprintf(stderr, "$VOLUME_INFORMATION attribute not found in "
"$Volume?!?\n");
errno = EIO;
goto error_exit;
}
/* Has to be resident. */
if (a->non_resident) {
fprintf(stderr, "Error: Attribute $VOLUME_INFORMATION must " \
"be resident (and it isn't)!\n");
errno = EIO;
goto error_exit;
}
/* Get a pointer to the value of the attribute. */
#define RA(x) ((RESIDENT_ATTRIBUTE_RECORD_HEADER*)(x))
vinf = (VOLUME_INFORMATION*)(le16_to_cpu(RA(a)->value_offset) +
(char*)a);
/* Sanity checks. */
if ((char*)vinf + le32_to_cpu(RA(a)->value_length) >
le16_to_cpu(mb->bytes_in_use) + (char*)mb ||
le16_to_cpu(RA(a)->value_offset) +
le32_to_cpu(RA(a)->value_length) >
le32_to_cpu(a->length)) {
fprintf(stderr, "Error: Attribute $VOLUME_INFORMATION in " \
"$Volume is corrupt!\n");
errno = EIO;
goto error_exit;
}
#undef RA
/* Setup vol from the volume information attribute value. */
vol->major_ver = vinf->major_ver;
vol->minor_ver = vinf->minor_ver;
/* Do not use le16_to_cpu() macro here as our VOLUME_FLAGS are
defined using cpu_to_le16() macro and hence are consistent. */
vol->flags = vinf->flags;
/* Phew! Done. */
/* FIXME: Need to initialise vol->dev_name as well. */
puts(OK);
free(mb);
/* FIXME: Need to deal with $AttrDef (4). */
return vol;
error_exit:
{
int eo = errno;
if (bs)
free(bs);
if (mb)
free(mb);
if (vol) {
if (vol->fd)
close(vol->fd);
if (vol->dev_name)
free(vol->dev_name);
if (vol->vol_name)
free(vol->vol_name);
if (vol->lcn_bitmap)
free(vol->lcn_bitmap);
if (vol->mft_bitmap)
free(vol->mft_bitmap);
if (vol->mft_runlist)
free(vol->mft_runlist);
if (vol->upcase)
free(vol->upcase);
free(vol);
}
errno = eo;
}
return NULL;
}
int ntfs_close(ntfs_volume *vol, const int force)
{
if (!vol) {
errno = EINVAL;
return 0;
}
/* FIXME: Do the cleanup. */
if (vol->fd)
close(vol->fd);
if (vol->dev_name)
free(vol->dev_name);
if (vol->vol_name)
free(vol->vol_name);
if (vol->lcn_bitmap)
free(vol->lcn_bitmap);
if (vol->mft_bitmap)
free(vol->mft_bitmap);
if (vol->mft_runlist)
free(vol->mft_runlist);
if (vol->upcase)
free(vol->upcase);
free(vol);
return 1;
}
|