Changes by: antona
Update of /cvsroot/linux-ntfs/linux-ntfs/libntfs
In directory usw-pr-cvs1:/tmp/cvs-serv8700/libntfs
Added Files:
attrib.c
Log Message:
Added in the current state of attribute handling to libntfs.
Now just missing the make files to make a first public release of ntfsfix!
--- NEW FILE ---
/*
* attrib.c - Attribute 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 <wchar.h>
#include <stddef.h>
#include <stdio.h>
#include <linux/types.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
/*
* This would be defining wchar_t in the kernel, but stdio.h defines it for
* userspace. FIXME: I have been a bit slack and defining __u16 instead of
* wchar_t in most places. This will need fixing at some point.
*/
/* #include <linux/nls.h> */
#include "attrib.h"
#include "mft.h"
#include "endians.h"
#include "disk_io.h"
#include "volume.h"
/*
* Generic macro to convert pointers to values for comparison purposes.
*/
#define p2n(p) ((ptrdiff_t)((ptrdiff_t*)(p)))
ATTRIBUTE_RECORD_HEADER *find_attribute(const MFT_RECORD_HEADER *b,
const ATTRIBUTE_TYPE t,
const wchar_t *n)
{
ATTRIBUTE_RECORD_HEADER *a;
/* Sanity/size/alignment checks. */
if (!b || !is_mft_recordp(b) ||
!(b->flags & MFT_RECORD_IN_USE) ||
b->base_file_record ||
p2n((char*)b + b->attributes_offset) & 7 ||
p2n((char*)b + b->bytes_in_use) & 7 ||
le16_to_cpu(b->attributes_offset) >= le32_to_cpu(b->bytes_in_use)) {
#ifdef DEBUG
puts("Sanity/size/allignment checks failed in " \
"find_attribute()!");
#endif
return NULL;
}
/* Position of the first attribute. */
a = (ATTRIBUTE_RECORD_HEADER*)(le16_to_cpu(b->attributes_offset) +
(char*)b);
/* Alignment check. Note: Can't move this inside while loop, since if
this fails a->type will fail too but it will cause an exception! */
if (p2n(a) & 7) {
#ifdef DEBUG
puts("Alignment check (2) failed in find_attribute()!");
#endif
return NULL;
}
/* Iterate over all atributes. */
while (a->type != $END) {
if (le32_to_cpu(a->length) + (char*)a >=
le32_to_cpu(b->bytes_in_use) + (char*)b) {
#ifdef DEBUG
puts("Attribute length out of bounds in " \
"find_attribute()!");
#endif
return NULL;
}
if (a->type == t) {
/* If want an unnamed attribute, check if current one
is unnamed and if so we have found it! */
if (!n) {
if (!a->name_length)
return a;
/* If we want a named attribute, check that the string
length is equal and if so, compare the two strings
and if they match, we have found it! */
} else if (a->name_length &&
a->name_length == wcslen(n)) {
/* Sanity check. */
if (a->name_length * 2 +
le16_to_cpu(a->name_offset)
> le32_to_cpu(a->length)) {
#ifdef DEBUG
puts("Warning: Encountered out of "
"bounds attribute name length in "
"find_attribute()!");
#endif
/* Even though this one is illegal,
maybe it wasn't the right one, so
continue with the search (i.e. don't
return NULL).
FIXME: Shouldn't we doing a hotfix
here, i.e. truncate the name to the
indicated size or change the
indicated size, whichever makes more
sense? */
/* FIXME: Should we really be case sensitive?
If not we should be using the $UpCase table,
thus we need a custom function for
comparisons. */
} else if (!wcscmp(n, (wchar_t*)((char*)a +
le16_to_cpu(a->name_offset))))
return a;
}
}
/* FIXME: If we find an attribute list then the attribute we
are looking for might be in there, but we don't handle those
yet, so just skip it for now and emitt a warning. */
if (a->type == $ATTRIBUTE_LIST) {
puts("Warning: Encountered attribute list attribute"
"which we can't handle yet.");
}
/* Get next attribute. */
a = (ATTRIBUTE_RECORD_HEADER*)((char *)a +
le32_to_cpu(a->length));
/* Alignment check. */
if (p2n(a) & 7) {
#ifdef DEBUG
puts("Allignment check (3) failed in " \
"find_attribute()!");
#endif
return NULL;
}
}
#ifdef DEBUG
if (n)
printf("Named attribute 0x%x not found in " \
"find_attribute()!", t);
else
printf("Unnamed attribute 0x%x not found in " \
"find_attribute()!", t);
#endif
return NULL;
}
int set_ntfs_volume_flags(MFT_RECORD_HEADER *b, const __u16 flags)
{
RESIDENT_ATTRIBUTE_RECORD_HEADER *r;
VOLUME_INFORMATION *c;
/* Sanity checks. */
if (!b || !is_mft_recordp(b))
return 0;
/* Get a pointer to the volume information attribute. */
if (!(r = (RESIDENT_ATTRIBUTE_RECORD_HEADER*)find_attribute(b,
$VOLUME_INFORMATION, NULL))) {
fprintf(stderr, "Error: Attribute $VOLUME_INFORMATION was " \
"not found in $Volume!\n");
return 0;
}
/* Sanity check. */
if (r->attribute.non_resident) {
fprintf(stderr, "Error: Attribute $VOLUME_INFORMATION must " \
"be resident (and it isn't)!\n");
return 0;
}
/* Get a pointer to the value of the attribute. */
c = (VOLUME_INFORMATION*)(le16_to_cpu(r->value_offset) + (char*)r);
/* Sanity checks. */
if ((char*)c + le32_to_cpu(r->value_length) >
le16_to_cpu(b->bytes_in_use) + (char*)b ||
le16_to_cpu(r->value_offset) + le32_to_cpu(r->value_length) >
le32_to_cpu(r->attribute.length)) {
fprintf(stderr, "Error: Attribute $VOLUME_INFORMATION in " \
"$Volume is corrupt!\n");
return 0;
}
/* Set the volume flags. */
c->flags = cpu_to_le16(flags);
/* Success! */
return 1;
}
__u64 get_attribute_value_length(const ATTRIBUTE_RECORD_HEADER *a)
{
if (!a) {
errno = EINVAL;
return 0;
}
errno = 0;
if (a->non_resident)
return le64_to_cpu(((NONRESIDENT_ATTRIBUTE_RECORD_HEADER*)
(a))->data_size);
else
return (__u64)le32_to_cpu(((RESIDENT_ATTRIBUTE_RECORD_HEADER*)
(a))->value_length);
errno = EINVAL;
return 0;
}
__u64 get_attribute_value(const ntfs_volume *vol,
const ATTRIBUTE_RECORD_HEADER *a, __u8 *b)
{
/* Sanity checks. */
if (!vol || !a || !b) {
errno = EINVAL;
return 0;
}
/* Complex attribute? */
if (a->flags) {
puts("Enountered non-zero attribute flags. Cannot handle this "
"yet.");
errno = ENOTSUP;
return 0;
}
if (!a->non_resident) { /* Attribute is resident. */
#define RA(a) ((RESIDENT_ATTRIBUTE_RECORD_HEADER*)(a))
/* Sanity check. */
if (le32_to_cpu(RA(a)->value_length) +
le16_to_cpu(RA(a)->value_offset) > le32_to_cpu(a->length)) {
return 0;
}
memcpy(b, (char*)a + le16_to_cpu(RA(a)->value_offset),
le32_to_cpu(RA(a)->value_length));
errno = 0;
return (__u64)le32_to_cpu(RA(a)->value_length);
#undef RA
} else { /* Attribute is not resident. */
#define NA(a) ((NONRESIDENT_ATTRIBUTE_RECORD_HEADER*)(a))
run_list rl;
__u64 total;
int r, i;
/* If no data, return 0. */
if (!(NA(a)->data_size)) {
errno = 0;
return 0;
}
/* Decompress the mapping pairs array into a run list. */
rl = decompress_run_list(NA(a));
if (!rl) {
errno = EINVAL;
return 0;
}
/*
* FIXED: We were overflowing here in a nasty fashion when we
* reach the last cluster in the run list as the buffer will
* only be big enough to hold data_size bytes while we are
* reading in allocated_size bytes which is usually larger
* than data_size, since the actual data is unlikely to have a
* size equal to a multiple of the cluster size!
*/
/* Now load all clusters in the run list into b. */
for (i = 0, total = 0; rl[i].length; i++) {
if (!rl[i+1].length) {
unsigned char *intbuf = NULL;
/*
* We have reached the last run so we were
* going to overflow when executing the
* ntfs_pread() which is BAAAAAAAD!
* Temporary fix:
* Allocate a new buffer with size:
* rl[i].length << vol->cluster_size_bits,
* do the read into our buffer, then
* memcpy the correct amount of data into
* the caller supplied buffer, free our
* buffer, and continue.
*/
intbuf = malloc(rl[i].length <<
vol->cluster_size_bits);
if (!intbuf) {
int eo = errno;
perror("Couldn't allocate memory for "
"internal buffer.\n");
errno = eo;
return 0;
}
/*
* FIXME: If compressed file: Only read if
* lcn != 0. Otherwise, we are dealing with a
* sparse run and we just memset the user buffer
* to 0 for the length of the run, which should
* be 16 (= compression unit size).
* FIXME: Really only when file is compressed,
* or can we have sparse runs in uncompressed
* files as well?
*/
r = ntfs_pread(vol->fd, intbuf,
vol->cluster_size,
rl[i].length,
rl[i].lcn <<
vol->cluster_size_bits);
if (r != rl[i].length) {
#define ESTR "Error reading attribute value"
if (r == -1) {
int eo = errno;
perror(ESTR);
errno = eo;
} else if (r < rl[i].length) {
fprintf(stderr, ESTR ": Ran "
"out of input data.\n");
errno = EIO;
} else {
fprintf(stderr, ESTR ": "
"unknown error\n");
errno = EIO;
}
#undef ESTR
return 0;
}
memcpy(b + total, intbuf,
le64_to_cpu(NA(a)->data_size) - total);
free(intbuf);
total = le64_to_cpu(NA(a)->data_size);
} else {
/*
* FIXME: If compressed file: Only read if
* lcn != 0. Otherwise, we are dealing with a
* sparse run and we just memset the user buffer
* to 0 for the length of the run, which should
* be 16 (= compression unit size).
*/
r = ntfs_pread(vol->fd, b + total,
vol->cluster_size,
rl[i].length,
rl[i].lcn <<
vol->cluster_size_bits);
if (r != rl[i].length) {
#define ESTR "Error reading attribute value"
if (r == -1) {
int eo = errno;
perror(ESTR);
errno = eo;
} else if (r < rl[i].length) {
fprintf(stderr, ESTR ": Ran "
"out of input data.\n");
errno = EIO;
} else {
fprintf(stderr, ESTR ": "
"unknown error\n");
errno = EIO;
}
#undef ESTR
return 0;
}
total += r * vol->cluster_size;
}
}
free(rl);
return total;
#undef NA
}
errno = EINVAL;
return 0;
}
int set_attribute_value(ntfs_volume *vol, ATTRIBUTE_RECORD_HEADER *a,
const __u8 *b, const __u64 l)
{
/* Sanity check. */
if (!vol || !a || !b) {
errno = EINVAL;
return 0;
}
/* FIXME: We can't deal with changes in attribute value length at the
moment. */
if ((l != get_attribute_value_length(a)) || !l) {
errno = ENOTSUP;
return 0;
}
/* Complex attribute? */
if (a->flags) {
puts("Enountered non-zero attribute flags. Cannot handle this "
"yet.");
errno = ENOTSUP;
return 0;
}
if (!a->non_resident) { /* Attribute is resident. */
#define RA(a) ((RESIDENT_ATTRIBUTE_RECORD_HEADER*)(a))
/* Sanity check. */
if (le32_to_cpu(RA(a)->value_length) +
le16_to_cpu(RA(a)->value_offset) > le32_to_cpu(a->length)) {
errno = EINVAL;
return 0;
}
memcpy((char*)a + le16_to_cpu(RA(a)->value_offset), b, l);
errno = 0;
return 1;
#undef RA
} else { /* Attribute is not resident. */
#define NA(a) ((NONRESIDENT_ATTRIBUTE_RECORD_HEADER*)(a))
run_list rl;
__u64 total;
int w, i;
/* If no data return 1. FIXME: This needs to change when we can
cope with changes in length of the attribute. */
if (!(NA(a)->data_size)) {
errno = 0;
return 1;
}
/* Decompress the mapping pairs array into a run list. */
rl = decompress_run_list(NA(a));
/* Now write all clusters to disk from b. */
for (i = 0, total = 0; rl[i].length && (l > 0); i++) {
w = ntfs_pwrite(vol->fd, b + total,
min(l, rl[i].length << vol->cluster_size_bits),
rl[i].lcn << vol->cluster_size_bits);
if (w != min(l, rl[i].length <<
vol->cluster_size_bits)) {
#define ESTR "Error writing attribute value"
if (w == -1) {
int eo = errno;
perror(ESTR);
errno = eo;
} else if (w < min(l,
rl[i].length <<
vol->cluster_size_bits)) {
fprintf(stderr, ESTR ": Ran out of "
"input data.\n");
errno = EIO;
} else {
fprintf(stderr, ESTR ": unknown "
"error\n");
errno = EIO;
}
#undef ESTR
return 0;
}
total += w;
l -= w;
if ((l <= 0) && rl[i+1].length) {
fprintf(stderr,
"Warning: Amount of data to write "
"doesn't match attribute length in\n"
"run list! - The old data that was "
"present beyond the new data written\n"
"has been preserved.\n");
} else if ((l > 0) && !rl[i+1].length) {
fprintf(stderr,
"Warning: Still have data to write but "
"the attribute length in the run list\n"
"was exceeded! - Data written has been "
"truncated!\n");
}
}
free(rl);
return 1;
#undef NA
}
errno = EINVAL;
return 0;
}
run_list decompress_run_list(const NONRESIDENT_ATTRIBUTE_RECORD_HEADER *attr)
{
__s64 vcn, lcn; /* Current vcn and lcn, respectively. */
__s64 deltaxcn; /* Change in [vl]cn. */
run_list rl = NULL; /* The output run_list. */
run_list rl2; /* Temporary run_list. */
__u8* buf; /* Current position in mapping pairs array. */
int rlsize; /* Size of run_list buffer. */
__u16 rlpos; /* Current run_list positions in units
of run_list_elements. */
__u8 b; /* Current byte offset in buf. */
/* Make sure attr exists and is non-resident. */
if (!attr || !attr->attribute.non_resident)
return NULL;
/*
* Other fail safe checks are not required since the caller is
* assumed to have done alignment and other consistency checks.
* See find_attribute() above for an example.
*/
/* FIXME: Compression not supported yet. */
if (attr->compression_unit) {
puts("Encountered compressed non-resident attribute."
"Cannot handle this yet!");
return NULL;
}
/* Start at vcn = lowest_vcn and lcn 0. */
vcn = le64_to_cpu(attr->lowest_vcn);
lcn = 0;
/* Get start of the mapping pairs array. */
buf = (__u8*)attr + le16_to_cpu(attr->mapping_pairs_offset);
/* Current position in run_list array. */
rlpos = 0;
/* Current run_list buffer size in bytes. */
rlsize = 0;
while (*buf) {
/* Allocate (more) memory if needed. */
if (rlpos * (int)sizeof(run_list_element) >=
rlsize - (int)sizeof(run_list_element)) {
rl2 = malloc(rlsize + 16 * sizeof(run_list_element));
if (!rl2) {
if (rl)
free(rl);
return NULL;
}
if (rl) {
memmove(rl2, rl, rlsize);
free(rl);
}
rl = rl2;
rlsize += 16 * sizeof(run_list_element);
}
/* Enter the current vcn into the current run_list_element. */
(rl + rlpos)->vcn = vcn;
/*
* Get the change in vcn, i.e. the run length in clusters.
* Doing it this way ensures that we signextend negative values.
* A negative run length doesn't make any sense, but hey, I
* didn't make up the NTFS specs and Windows NT4 treats the run
* length as a signed value so that's how it is...
*/
for (b = *buf & 0xf, deltaxcn = (__s8)buf[b--]; b; b--) {
deltaxcn = (deltaxcn << 8) + buf[b];
}
/* Assume a negative length to indicate data corruption and
hence clean-up and return NULL. */
if (deltaxcn < 0) {
free(rl);
return NULL;
}
/* Enter the current run length into the current
* run_list_element. */
(rl + rlpos)->length = deltaxcn;
/* Increment the current vcn by the current run length. */
vcn += deltaxcn;
/* Get the lcn change which really can be negative. */
for (b = (*buf & 0xf) + ((*buf >> 4) & 0xf),
deltaxcn = (__s8)buf[b--]; b > (*buf & 0xf); b--) {
deltaxcn = (deltaxcn << 8) + buf[b];
}
/* Change the current lcn to it's new value. */
lcn += deltaxcn;
/* Enter the current lcn into the current run_list_element. */
(rl + rlpos)->lcn = lcn;
/* Get to the next run_list_element. */
rlpos++;
/* Increment the buffer position to the next mapping pair. */
buf += (*buf & 0xf) + ((*buf >> 4) & 0xf) + 1;
}
/* If there were no mapping pairs in the run_list, then rl will still
be NULL. */
if (rl) {
/* Adjust buffer size to minimum before returning. */
rl2 = malloc((rlpos + 1) * sizeof(run_list_element));
if (!rl2) {
if (rl)
free(rl);
return NULL;
}
memmove(rl2, rl, (rlpos + 1) * sizeof(run_list_element));
free(rl);
rl = rl2;
/* Setup terminating run_list_element. */
(rl + rlpos)->vcn = vcn;
(rl + rlpos)->length = 0;
/*
* This is not necessary, but do it for completeness sake.
* FIXME: Do we really want this zero? Zero means it is sparse,
* but I guess better zero than an undefined random value...
*/
(rl + rlpos)->lcn = 0;
/* The final vcn - 1 has to be equal to highest_vcn or
something has gone badly wrong. */
if (vcn - 1 != le64_to_cpu(attr->highest_vcn)) {
free(rl);
puts("Corrupt Mapping Pairs Array in non-resident "
"attribute!");
return NULL;
}
}
#ifdef DEBUG
if (rl) {
int i;
puts("\nMapping Pairs array successfully decompressed.");
puts("Resulting run list (values in hexadecimal):");
puts("VCN LCN Run length");
for (i = 0; ; i++) {
printf("%-16Lx %-16Lx %-16Lx\n", (rl + i)->vcn,
(rl + i)->lcn,
(rl + i)->length);
if (!(rl + i)->length)
break;
}
}
#endif
/* Finally finished! */
return rl;
}
__s64 vcn_to_lcn(const run_list rl, const __s64 vcn)
{
int i = 0;
if (!rl || vcn < 0)
return -EINVAL;
if (!rl[0].length || vcn < rl[0].vcn)
return -ENOENT;
do {
if (vcn < rl[++i].vcn)
return rl[i-1].lcn + (vcn - rl[i-1].vcn);
} while (rl[i].length);
return -ENOENT;
}
|