OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k/trunk/linux/linux-2.4/fs/ntfs
    from Rev 1275 to Rev 1765
    Reverse comparison

Rev 1275 → Rev 1765

/super.c
0,0 → 1,1416
/*
* super.c
*
 
 
* Copyright (C) 1999 Steve Dodd
* Copyright (C) 2000-2001 Anton Altparmakov (AIA)
*/
 
#include <linux/ntfs_fs.h>
#include <linux/errno.h>
#include <linux/bitops.h>
#include <linux/module.h>
#include "ntfstypes.h"
#include "struct.h"
#include "super.h"
#include "macros.h"
#include "inode.h"
#include "support.h"
#include "util.h"
#include <linux/smp_lock.h>
 
/* All important structures in NTFS use 2 consistency checks:
* . a magic structure identifier (FILE, INDX, RSTR, RCRD...)
* . a fixup technique : the last word of each sector (called a fixup) of a
* structure's record should end with the word at offset <n> of the first
* sector, and if it is the case, must be replaced with the words following
* <n>. The value of <n> and the number of fixups is taken from the fields
* at the offsets 4 and 6. Note that the sector size is defined as
* NTFS_SECTOR_SIZE and not as the hardware sector size (this is concordant
* with what the Windows NTFS driver does).
*
* This function performs these 2 checks, and _fails_ if:
* . the input size is invalid
* . the fixup header is invalid
* . the size does not match the number of sectors
* . the magic identifier is wrong
* . a fixup is invalid
*/
int ntfs_fixup_record(char *record, char *magic, int size)
{
int start, count, offset;
ntfs_u16 fixup;
 
if (!IS_MAGIC(record, magic))
return 0;
start = NTFS_GETU16(record + 4);
count = NTFS_GETU16(record + 6) - 1;
if (size & (NTFS_SECTOR_SIZE - 1) || start & 1 ||
start + count * 2 > size || size >> 9 != count) {
if (size <= 0)
printk(KERN_ERR "NTFS: BUG: ntfs_fixup_record() got "
"zero size! Please report this to "
"linux-ntfs-dev@lists.sf.net\n");
return 0;
}
fixup = NTFS_GETU16(record + start);
start += 2;
offset = NTFS_SECTOR_SIZE - 2;
while (count--) {
if (NTFS_GETU16(record + offset) != fixup)
return 0;
NTFS_PUTU16(record + offset, NTFS_GETU16(record + start));
start += 2;
offset += NTFS_SECTOR_SIZE;
}
return 1;
}
 
/*
* Get vital informations about the ntfs partition from the boot sector.
* Return 0 on success or -1 on error.
*/
int ntfs_init_volume(ntfs_volume *vol, char *boot)
{
int sectors_per_cluster_bits;
__s64 ll;
ntfs_cluster_t mft_zone_size, tc;
 
/* System defined default values, in case we don't load $AttrDef. */
vol->at_standard_information = 0x10;
vol->at_attribute_list = 0x20;
vol->at_file_name = 0x30;
vol->at_volume_version = 0x40;
vol->at_security_descriptor = 0x50;
vol->at_volume_name = 0x60;
vol->at_volume_information = 0x70;
vol->at_data = 0x80;
vol->at_index_root = 0x90;
vol->at_index_allocation = 0xA0;
vol->at_bitmap = 0xB0;
vol->at_symlink = 0xC0;
/* Sector size. */
vol->sector_size = NTFS_GETU16(boot + 0xB);
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->sector_size = 0x%x\n",
vol->sector_size);
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: sectors_per_cluster = "
"0x%x\n", NTFS_GETU8(boot + 0xD));
sectors_per_cluster_bits = ffs(NTFS_GETU8(boot + 0xD)) - 1;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: sectors_per_cluster_bits "
"= 0x%x\n", sectors_per_cluster_bits);
vol->mft_clusters_per_record = NTFS_GETS8(boot + 0x40);
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_clusters_per_record"
" = 0x%x\n", vol->mft_clusters_per_record);
vol->index_clusters_per_record = NTFS_GETS8(boot + 0x44);
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: "
"vol->index_clusters_per_record = 0x%x\n",
vol->index_clusters_per_record);
vol->cluster_size = vol->sector_size << sectors_per_cluster_bits;
vol->cluster_size_bits = ffs(vol->cluster_size) - 1;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->cluster_size = 0x%x\n",
vol->cluster_size);
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->cluster_size_bits = "
"0x%x\n", vol->cluster_size_bits);
if (vol->mft_clusters_per_record > 0)
vol->mft_record_size = vol->cluster_size <<
(ffs(vol->mft_clusters_per_record) - 1);
else
/*
* When mft_record_size < cluster_size, mft_clusters_per_record
* = -log2(mft_record_size) bytes. mft_record_size normaly is
* 1024 bytes, which is encoded as 0xF6 (-10 in decimal).
*/
vol->mft_record_size = 1 << -vol->mft_clusters_per_record;
vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_record_size = 0x%x"
"\n", vol->mft_record_size);
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_record_size_bits = "
"0x%x\n", vol->mft_record_size_bits);
if (vol->index_clusters_per_record > 0)
vol->index_record_size = vol->cluster_size <<
(ffs(vol->index_clusters_per_record) - 1);
else
/*
* When index_record_size < cluster_size,
* index_clusters_per_record = -log2(index_record_size) bytes.
* index_record_size normaly equals 4096 bytes, which is
* encoded as 0xF4 (-12 in decimal).
*/
vol->index_record_size = 1 << -vol->index_clusters_per_record;
vol->index_record_size_bits = ffs(vol->index_record_size) - 1;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->index_record_size = "
"0x%x\n", vol->index_record_size);
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->index_record_size_bits "
"= 0x%x\n", vol->index_record_size_bits);
/*
* Get the size of the volume in clusters (ofs 0x28 is nr_sectors) and
* check for 64-bit-ness. Windows currently only uses 32 bits to save
* the clusters so we do the same as it is much faster on 32-bit CPUs.
*/
ll = NTFS_GETS64(boot + 0x28) >> sectors_per_cluster_bits;
if (ll >= (__s64)1 << 31) {
ntfs_error("Cannot handle 64-bit clusters. Please inform "
"linux-ntfs-dev@lists.sf.net that you got this "
"error.\n");
return -1;
}
vol->nr_clusters = (ntfs_cluster_t)ll;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->nr_clusters = 0x%x\n",
vol->nr_clusters);
vol->mft_lcn = (ntfs_cluster_t)NTFS_GETS64(boot + 0x30);
vol->mft_mirr_lcn = (ntfs_cluster_t)NTFS_GETS64(boot + 0x38);
/* Determine MFT zone size. */
mft_zone_size = vol->nr_clusters;
switch (vol->mft_zone_multiplier) { /* % of volume size in clusters */
case 4:
mft_zone_size >>= 1; /* 50% */
break;
case 3:
mft_zone_size = mft_zone_size * 3 >> 3; /* 37.5% */
break;
case 2:
mft_zone_size >>= 2; /* 25% */
break;
/* case 1: */
default:
mft_zone_size >>= 3; /* 12.5% */
break;
}
/* Setup mft zone. */
vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_zone_pos = %x\n",
vol->mft_zone_pos);
/*
* Calculate the mft_lcn for an unmodified NTFS volume (see mkntfs
* source) and if the actual mft_lcn is in the expected place or even
* further to the front of the volume, extend the mft_zone to cover the
* beginning of the volume as well. This is in order to protect the
* area reserved for the mft bitmap as well within the mft_zone itself.
* On non-standard volumes we don't protect it as well as the overhead
* would be higher than the speed increase we would get by doing it.
*/
tc = (8192 + 2 * vol->cluster_size - 1) / vol->cluster_size;
if (tc * vol->cluster_size < 16 * 1024)
tc = (16 * 1024 + vol->cluster_size - 1) / vol->cluster_size;
if (vol->mft_zone_start <= tc)
vol->mft_zone_start = (ntfs_cluster_t)0;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_zone_start = %x\n",
vol->mft_zone_start);
/*
* Need to cap the mft zone on non-standard volumes so that it does
* not point outside the boundaries of the volume, we do this by
* halving the zone size until we are inside the volume.
*/
vol->mft_zone_end = vol->mft_lcn + mft_zone_size;
while (vol->mft_zone_end >= vol->nr_clusters) {
mft_zone_size >>= 1;
vol->mft_zone_end = vol->mft_lcn + mft_zone_size;
}
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_zone_end = %x\n",
vol->mft_zone_end);
/*
* Set the current position within each data zone to the start of the
* respective zone.
*/
vol->data1_zone_pos = vol->mft_zone_end;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->data1_zone_pos = %x\n",
vol->data1_zone_pos);
vol->data2_zone_pos = (ntfs_cluster_t)0;
ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->data2_zone_pos = %x\n",
vol->data2_zone_pos);
/* Set the mft data allocation position to mft record 24. */
vol->mft_data_pos = 24UL;
/* This will be initialized later. */
vol->upcase = 0;
vol->upcase_length = 0;
vol->mft_ino = 0;
return 0;
}
 
static void ntfs_init_upcase(ntfs_inode *upcase)
{
ntfs_io io;
#define UPCASE_LENGTH 256
upcase->vol->upcase = ntfs_malloc(UPCASE_LENGTH << 1);
if (!upcase->vol->upcase)
return;
io.fn_put = ntfs_put;
io.fn_get = 0;
io.param = (char*)upcase->vol->upcase;
io.size = UPCASE_LENGTH << 1;
ntfs_read_attr(upcase, upcase->vol->at_data, 0, 0, &io);
upcase->vol->upcase_length = io.size >> 1;
}
 
static int process_attrdef(ntfs_inode* attrdef, ntfs_u8* def)
{
int type = NTFS_GETU32(def+0x80);
int check_type = 0;
ntfs_volume *vol = attrdef->vol;
ntfs_u16* name = (ntfs_u16*)def;
 
if (!type) {
ntfs_debug(DEBUG_OTHER, "process_atrdef: finished processing "
"and returning 1\n");
return 1;
}
if (ntfs_ua_strncmp(name, "$STANDARD_INFORMATION", 64) == 0) {
vol->at_standard_information = type;
check_type = 0x10;
} else if (ntfs_ua_strncmp(name, "$ATTRIBUTE_LIST", 64) == 0) {
vol->at_attribute_list = type;
check_type = 0x20;
} else if (ntfs_ua_strncmp(name, "$FILE_NAME", 64) == 0) {
vol->at_file_name = type;
check_type = 0x30;
} else if (ntfs_ua_strncmp(name, "$VOLUME_VERSION", 64) == 0) {
vol->at_volume_version = type;
check_type = 0x40;
} else if (ntfs_ua_strncmp(name, "$SECURITY_DESCRIPTOR", 64) == 0) {
vol->at_security_descriptor = type;
check_type = 0x50;
} else if (ntfs_ua_strncmp(name, "$VOLUME_NAME", 64) == 0) {
vol->at_volume_name = type;
check_type = 0x60;
} else if (ntfs_ua_strncmp(name, "$VOLUME_INFORMATION", 64) == 0) {
vol->at_volume_information = type;
check_type = 0x70;
} else if (ntfs_ua_strncmp(name, "$DATA", 64) == 0) {
vol->at_data = type;
check_type = 0x80;
} else if (ntfs_ua_strncmp(name, "$INDEX_ROOT", 64) == 0) {
vol->at_index_root = type;
check_type = 0x90;
} else if (ntfs_ua_strncmp(name, "$INDEX_ALLOCATION", 64) == 0) {
vol->at_index_allocation = type;
check_type = 0xA0;
} else if (ntfs_ua_strncmp(name, "$BITMAP", 64) == 0) {
vol->at_bitmap = type;
check_type = 0xB0;
} else if (ntfs_ua_strncmp(name, "$SYMBOLIC_LINK", 64) == 0 ||
ntfs_ua_strncmp(name, "$REPARSE_POINT", 64) == 0) {
vol->at_symlink = type;
check_type = 0xC0;
}
if (check_type && check_type != type) {
ntfs_error("process_attrdef: unexpected type 0x%x for 0x%x\n",
type, check_type);
return -EINVAL;
}
ntfs_debug(DEBUG_OTHER, "process_attrdef: found %s attribute of type "
"0x%x\n", check_type ? "known" : "unknown", type);
return 0;
}
 
int ntfs_init_attrdef(ntfs_inode* attrdef)
{
ntfs_u8 *buf;
ntfs_io io;
__s64 offset;
unsigned i;
int error;
ntfs_attribute *data;
 
ntfs_debug(DEBUG_BSD, "Entered ntfs_init_attrdef()\n");
buf = ntfs_malloc(4050); /* 90*45 */
if (!buf)
return -ENOMEM;
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
io.do_read = 1;
offset = 0;
data = ntfs_find_attr(attrdef, attrdef->vol->at_data, 0);
ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() after call to "
"ntfs_find_attr.\n");
if (!data) {
ntfs_free(buf);
return -EINVAL;
}
do {
io.param = buf;
io.size = 4050;
ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() going to call "
"ntfs_readwrite_attr.\n");
error = ntfs_readwrite_attr(attrdef, data, offset, &io);
ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() after call to "
"ntfs_readwrite_attr.\n");
for (i = 0; !error && i <= io.size - 0xA0; i += 0xA0) {
ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() going "
"to call process_attrdef.\n");
error = process_attrdef(attrdef, buf + i);
ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() after "
"call to process_attrdef.\n");
}
offset += 4096;
} while (!error && io.size);
ntfs_debug(DEBUG_BSD, "Exiting ntfs_init_attrdef()\n");
ntfs_free(buf);
return error == 1 ? 0 : error;
}
 
/* ntfs_get_version will determine the NTFS version of the volume and will
* return the version in a BCD format, with the MSB being the major version
* number and the LSB the minor one. Otherwise return <0 on error.
* Example: version 3.1 will be returned as 0x0301. This has the obvious
* limitation of not coping with version numbers above 0x80 but that shouldn't
* be a problem... */
int ntfs_get_version(ntfs_inode* volume)
{
ntfs_attribute *volinfo;
 
volinfo = ntfs_find_attr(volume, volume->vol->at_volume_information, 0);
if (!volinfo)
return -EINVAL;
if (!volinfo->resident) {
ntfs_error("Volume information attribute is not resident!\n");
return -EINVAL;
}
return ((ntfs_u8*)volinfo->d.data)[8] << 8 |
((ntfs_u8*)volinfo->d.data)[9];
}
 
int ntfs_load_special_files(ntfs_volume *vol)
{
int error;
ntfs_inode upcase, attrdef, volume;
 
vol->mft_ino = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode));
vol->mftmirr = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode));
vol->bitmap = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode));
vol->ino_flags = 4 | 2 | 1;
error = -ENOMEM;
ntfs_debug(DEBUG_BSD, "Going to load MFT\n");
if (!vol->mft_ino || (error = ntfs_init_inode(vol->mft_ino, vol,
FILE_Mft))) {
ntfs_error("Problem loading MFT\n");
return error;
}
ntfs_debug(DEBUG_BSD, "Going to load MIRR\n");
if ((error = ntfs_init_inode(vol->mftmirr, vol, FILE_MftMirr))) {
ntfs_error("Problem %d loading MFTMirr\n", error);
return error;
}
ntfs_debug(DEBUG_BSD, "Going to load BITMAP\n");
if ((error = ntfs_init_inode(vol->bitmap, vol, FILE_BitMap))) {
ntfs_error("Problem loading Bitmap\n");
return error;
}
ntfs_debug(DEBUG_BSD, "Going to load UPCASE\n");
error = ntfs_init_inode(&upcase, vol, FILE_UpCase);
if (error)
return error;
ntfs_init_upcase(&upcase);
ntfs_clear_inode(&upcase);
ntfs_debug(DEBUG_BSD, "Going to load ATTRDEF\n");
error = ntfs_init_inode(&attrdef, vol, FILE_AttrDef);
if (error)
return error;
error = ntfs_init_attrdef(&attrdef);
ntfs_clear_inode(&attrdef);
if (error)
return error;
 
/* Check for NTFS version and if Win2k version (ie. 3.0+) do not allow
* write access since the driver write support is broken. */
ntfs_debug(DEBUG_BSD, "Going to load VOLUME\n");
error = ntfs_init_inode(&volume, vol, FILE_Volume);
if (error)
return error;
if ((error = ntfs_get_version(&volume)) >= 0x0300 &&
!(NTFS_SB(vol)->s_flags & MS_RDONLY)) {
NTFS_SB(vol)->s_flags |= MS_RDONLY;
ntfs_error("Warning! NTFS volume version is Win2k+: Mounting "
"read-only\n");
}
ntfs_clear_inode(&volume);
if (error < 0)
return error;
ntfs_debug(DEBUG_BSD, "NTFS volume is v%d.%d\n", error >> 8,
error & 0xff);
return 0;
}
 
int ntfs_release_volume(ntfs_volume *vol)
{
if (((vol->ino_flags & 1) == 1) && vol->mft_ino) {
ntfs_clear_inode(vol->mft_ino);
ntfs_free(vol->mft_ino);
vol->mft_ino = 0;
}
if (((vol->ino_flags & 2) == 2) && vol->mftmirr) {
ntfs_clear_inode(vol->mftmirr);
ntfs_free(vol->mftmirr);
vol->mftmirr = 0;
}
if (((vol->ino_flags & 4) == 4) && vol->bitmap) {
ntfs_clear_inode(vol->bitmap);
ntfs_free(vol->bitmap);
vol->bitmap = 0;
}
ntfs_free(vol->mft);
ntfs_free(vol->upcase);
return 0;
}
 
/*
* Writes the volume size (units of clusters) into vol_size.
* Returns 0 if successful or error.
*/
int ntfs_get_volumesize(ntfs_volume *vol, ntfs_s64 *vol_size)
{
ntfs_io io;
char *cluster0;
 
if (!vol_size)
return -EFAULT;
cluster0 = ntfs_malloc(vol->cluster_size);
if (!cluster0)
return -ENOMEM;
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
io.param = cluster0;
io.do_read = 1;
io.size = vol->cluster_size;
ntfs_getput_clusters(vol, 0, 0, &io);
*vol_size = NTFS_GETU64(cluster0 + 0x28) >>
(ffs(NTFS_GETU8(cluster0 + 0xD)) - 1);
ntfs_free(cluster0);
return 0;
}
 
static int nc[16]={4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0};
 
int ntfs_get_free_cluster_count(ntfs_inode *bitmap)
{
ntfs_io io;
int offset, error, clusters;
unsigned char *bits = ntfs_malloc(2048);
if (!bits)
return -ENOMEM;
offset = clusters = 0;
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
while (1) {
register int i;
io.param = bits;
io.size = 2048;
error = ntfs_read_attr(bitmap, bitmap->vol->at_data, 0, offset,
&io);
if (error || io.size == 0)
break;
/* I never thought I would do loop unrolling some day */
for (i = 0; i < io.size - 8; ) {
clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
}
while (i < io.size) {
clusters += nc[bits[i] >> 4];
clusters += nc[bits[i++] & 0xF];
}
offset += io.size;
}
ntfs_free(bits);
return clusters;
}
 
/*
* Insert the fixups for the record. The number and location of the fixes
* is obtained from the record header but we double check with @rec_size and
* use that as the upper boundary, if necessary overwriting the count value in
* the record header.
*
* We return 0 on success or -1 if fixup header indicated the beginning of the
* update sequence array to be beyond the valid limit.
*/
int ntfs_insert_fixups(unsigned char *rec, int rec_size)
{
int first;
int count;
int offset = -2;
ntfs_u16 fix;
first = NTFS_GETU16(rec + 4);
count = (rec_size >> NTFS_SECTOR_BITS) + 1;
if (first + count * 2 > NTFS_SECTOR_SIZE - 2) {
printk(KERN_CRIT "NTFS: ntfs_insert_fixups() detected corrupt "
"NTFS record update sequence array position. - "
"Cannot hotfix.\n");
return -1;
}
if (count != NTFS_GETU16(rec + 6)) {
printk(KERN_ERR "NTFS: ntfs_insert_fixups() detected corrupt "
"NTFS record update sequence array size. - "
"Applying hotfix.\n");
NTFS_PUTU16(rec + 6, count);
}
fix = (NTFS_GETU16(rec + first) + 1) & 0xffff;
if (fix == 0xffff || !fix)
fix = 1;
NTFS_PUTU16(rec + first, fix);
count--;
while (count--) {
first += 2;
offset += NTFS_SECTOR_SIZE;
NTFS_PUTU16(rec + first, NTFS_GETU16(rec + offset));
NTFS_PUTU16(rec + offset, fix);
}
return 0;
}
 
/**
* ntfs_allocate_clusters - allocate logical clusters on an ntfs volume
* @vol: volume on which to allocate clusters
* @location: preferred location for first allocated cluster
* @count: number of clusters to allocate
* @rl: address of pointer in which to return the allocated run list
* @rl_len: the number of elements returned in @*rl
*
* Allocate @*count clusters (LCNs), preferably beginning at @*location in the
* bitmap of the volume @vol. If @*location is -1, it does not matter where the
* clusters are. @rl is the address of a ntfs_runlist pointer which this
* function will allocate and fill with the runlist of the allocated clusters.
* It is the callers responsibility to ntfs_vfree() @*rl after she is finished
* with it. If the function was not successful, @*rl will be set to NULL.
* @*rl_len will contain the number of ntfs_runlist elements in @*rl or 0 if
* @*rl is NULL.
*
* Return 0 on success, or -errno on error. On success, @*location and @*count
* say what was really allocated. On -ENOSPC, @*location and @*count say what
* could have been allocated. If nothing could be allocated or a different
* error occured, @*location = -1 and @*count = 0.
*
* There are two data zones. First is the area between the end of the mft zone
* and the end of the volume, and second is the area between the start of the
* volume and the start of the mft zone. On unmodified/standard volumes, the
* second mft zone doesn't exist due to the mft zone being expanded to cover
* the start of volume in order to reserve space for the mft bitmap attribute.
*
* This is not the prettiest function but the complexity stems from the need of
* implementing the mft vs data zoned approach and from the fact that we have
* access to the lcn bitmap in portions of PAGE_SIZE bytes at a time, so we
* need to cope with crossing over boundaries of two pages. Further, the fact
* that the allocator allows for caller supplied hints as to the location of
* where allocation should begin and the fact that the allocator keeps track of
* where in the data zones the next natural allocation should occur, contribute
* to the complexity of the function. But it should all be worthwhile, because
* this allocator should: 1) be a full implementation of the MFT zone approach
* used by Windows, 2) cause reduction in fragmentation as much as possible,
* and 3) be speedy in allocations (the code is not optimized for speed, but
* the algorithm is, so further speed improvements are probably possible).
*
* FIXME: Really need finer-grained locking but this will do for the moment. I
* just want to kill all races and have a working allocator. When that is done,
* we can beautify... (AIA)
*
* FIXME: We should be monitoring cluster allocation and increment the MFT zone
* size dynamically but this is something for the future. We will just cause
* heavier fragmentation by not doing it and I am not even sure Windows would
* grow the MFT zone dynamically, so might even be correct not doing this. The
* overhead in doing dynamic MFT zone expansion would be very large and unlikely
* worth the effort. (AIA)
*
* TODO: I have added in double the required zone position pointer wrap around
* logic which can be optimized to having only one of the two logic sets.
* However, having the double logic will work fine, but if we have only one of
* the sets and we get it wrong somewhere, then we get into trouble, so
* removing the duplicate logic requires _very_ careful consideration of _all_
* possible code paths. So at least for now, I am leaving the double logic -
* better safe than sorry... (AIA)
*/
int ntfs_allocate_clusters(ntfs_volume *vol, ntfs_cluster_t *location,
ntfs_cluster_t *count, ntfs_runlist **rl, int *rl_len,
const NTFS_CLUSTER_ALLOCATION_ZONES zone)
{
ntfs_runlist *rl2 = NULL, *rlt;
ntfs_attribute *data;
ntfs_cluster_t buf_pos, zone_start, zone_end, mft_zone_size;
ntfs_cluster_t lcn, last_read_pos, prev_lcn = (ntfs_cluster_t)0;
ntfs_cluster_t initial_location, prev_run_len = (ntfs_cluster_t)0;
ntfs_cluster_t clusters = (ntfs_cluster_t)0;
unsigned char *buf, *byte, bit, search_zone, done_zones;
unsigned char pass, need_writeback;
int rlpos = 0, rlsize, buf_size, err = 0;
ntfs_io io;
 
ntfs_debug(DEBUG_OTHER, "%s(): Entering with *location = 0x%x, "
"*count = 0x%x, zone = %s_ZONE.\n", __FUNCTION__,
*location, *count, zone == DATA_ZONE ? "DATA" : "MFT");
buf = (char*)__get_free_page(GFP_NOFS);
if (!buf) {
ntfs_debug(DEBUG_OTHER, "%s(): Returning -ENOMEM.\n",
__FUNCTION__);
return -ENOMEM;
}
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
lock_kernel();
/* Get the $DATA attribute of $Bitmap. */
data = ntfs_find_attr(vol->bitmap, vol->at_data, 0);
if (!data) {
err = -EINVAL;
goto err_ret;
}
/*
* If no specific location was requested, use the current data zone
* position, otherwise use the requested location but make sure it lies
* outside the mft zone. Also set done_zones to 0 (no zones done) and
* pass depending on whether we are starting inside a zone (1) or
* at the beginning of a zone (2). If requesting from the MFT_ZONE, then
* we either start at the current position within the mft zone or at the
* specified position and if the latter is out of bounds then we start
* at the beginning of the MFT_ZONE.
*/
done_zones = 0;
pass = 1;
/*
* zone_start and zone_end are the current search range. search_zone
* is 1 for mft zone, 2 for data zone 1 (end of mft zone till end of
* volume) and 4 for data zone 2 (start of volume till start of mft
* zone).
*/
zone_start = *location;
if (zone_start < 0) {
if (zone == DATA_ZONE)
zone_start = vol->data1_zone_pos;
else
zone_start = vol->mft_zone_pos;
if (!zone_start)
/*
* Zone starts at beginning of volume which means a
* single pass is sufficient.
*/
pass = 2;
} else if (zone_start >= vol->mft_zone_start && zone_start <
vol->mft_zone_end && zone == DATA_ZONE) {
zone_start = vol->mft_zone_end;
pass = 2;
} else if ((zone_start < vol->mft_zone_start || zone_start >=
vol->mft_zone_end) && zone == MFT_ZONE) {
zone_start = vol->mft_lcn;
if (!vol->mft_zone_end)
zone_start = (ntfs_cluster_t)0;
pass = 2;
}
if (zone == DATA_ZONE) {
/* Skip searching the mft zone. */
done_zones |= 1;
if (zone_start >= vol->mft_zone_end) {
zone_end = vol->nr_clusters;
search_zone = 2;
} else {
zone_end = vol->mft_zone_start;
search_zone = 4;
}
} else /* if (zone == MFT_ZONE) */ {
zone_end = vol->mft_zone_end;
search_zone = 1;
}
/*
* buf_pos is the current bit position inside the bitmap. We use
* initial_location to determine whether or not to do a zone switch.
*/
buf_pos = initial_location = zone_start;
/* Loop until all clusters are allocated, i.e. clusters == 0. */
clusters = *count;
rlpos = rlsize = 0;
if (*count <= 0) {
ntfs_debug(DEBUG_OTHER, "%s(): *count <= 0, "
"returning -EINVAL.\n", __FUNCTION__);
err = -EINVAL;
goto err_ret;
}
while (1) {
ntfs_debug(DEBUG_OTHER, "%s(): Start of outer while "
"loop: done_zones = 0x%x, search_zone = %i, "
"pass = %i, zone_start = 0x%x, zone_end = "
"0x%x, initial_location = 0x%x, buf_pos = "
"0x%x, rlpos = %i, rlsize = %i.\n",
__FUNCTION__, done_zones, search_zone, pass,
zone_start, zone_end, initial_location, buf_pos,
rlpos, rlsize);
/* Loop until we run out of free clusters. */
io.param = buf;
io.size = PAGE_SIZE;
io.do_read = 1;
last_read_pos = buf_pos >> 3;
ntfs_debug(DEBUG_OTHER, "%s(): last_read_pos = 0x%x.\n",
__FUNCTION__, last_read_pos);
err = ntfs_readwrite_attr(vol->bitmap, data, last_read_pos,
&io);
if (err) {
ntfs_debug(DEBUG_OTHER, "%s(): ntfs_read_attr failed "
"with error code %i, going to "
"err_ret.\n", __FUNCTION__, -err);
goto err_ret;
}
if (!io.size) {
ntfs_debug(DEBUG_OTHER, "%s(): !io.size, going to "
"zone_pass_done.\n", __FUNCTION__);
goto zone_pass_done;
}
buf_size = io.size << 3;
lcn = buf_pos & 7;
buf_pos &= ~7;
need_writeback = 0;
ntfs_debug(DEBUG_OTHER, "%s(): Before inner while "
"loop: buf_size = 0x%x, lcn = 0x%x, buf_pos = "
"0x%x, need_writeback = %i.\n", __FUNCTION__,
buf_size, lcn, buf_pos, need_writeback);
while (lcn < buf_size && lcn + buf_pos < zone_end) {
byte = buf + (lcn >> 3);
ntfs_debug(DEBUG_OTHER, "%s(): In inner while loop: "
"buf_size = 0x%x, lcn = 0x%x, buf_pos "
"= 0x%x, need_writeback = %i, byte ofs "
"= 0x%x, *byte = 0x%x.\n", __FUNCTION__,
buf_size, lcn, buf_pos, need_writeback,
lcn >> 3, *byte);
/* Skip full bytes. */
if (*byte == 0xff) {
lcn += 8;
ntfs_debug(DEBUG_OTHER, "%s(): continuing while"
" loop 1.\n", __FUNCTION__);
continue;
}
bit = 1 << (lcn & 7);
ntfs_debug(DEBUG_OTHER, "%s(): bit = %i.\n",
__FUNCTION__, bit);
/* If the bit is already set, go onto the next one. */
if (*byte & bit) {
lcn++;
ntfs_debug(DEBUG_OTHER, "%s(): continuing while"
" loop 2.\n", __FUNCTION__);
continue;
}
/* Allocate the bitmap bit. */
*byte |= bit;
/* We need to write this bitmap buffer back to disk! */
need_writeback = 1;
ntfs_debug(DEBUG_OTHER, "%s(): *byte = 0x%x, "
"need_writeback = %i.\n", __FUNCTION__,
*byte, need_writeback);
/* Reallocate memory if necessary. */
if ((rlpos + 2) * sizeof(ntfs_runlist) >= rlsize) {
ntfs_debug(DEBUG_OTHER, "%s(): Reallocating "
"space.\n", __FUNCTION__);
/* Setup first free bit return value. */
if (!rl2) {
*location = lcn + buf_pos;
ntfs_debug(DEBUG_OTHER, "%s(): "
"*location = 0x%x.\n",
__FUNCTION__,
*location);
}
rlsize += PAGE_SIZE;
rlt = ntfs_vmalloc(rlsize);
if (!rlt) {
err = -ENOMEM;
ntfs_debug(DEBUG_OTHER, "%s(): Failed "
"to allocate memory, "
"returning -ENOMEM, "
"going to wb_err_ret.\n",
__FUNCTION__);
goto wb_err_ret;
}
if (rl2) {
ntfs_memcpy(rlt, rl2, rlsize -
PAGE_SIZE);
ntfs_vfree(rl2);
}
rl2 = rlt;
ntfs_debug(DEBUG_OTHER, "%s(): Reallocated "
"memory, rlsize = 0x%x.\n",
__FUNCTION__, rlsize);
}
/*
* Coalesce with previous run if adjacent LCNs.
* Otherwise, append a new run.
*/
ntfs_debug(DEBUG_OTHER, "%s(): Adding run (lcn 0x%x, "
"len 0x%x), prev_lcn = 0x%x, lcn = "
"0x%x, buf_pos = 0x%x, prev_run_len = "
"0x%x, rlpos = %i.\n", __FUNCTION__,
lcn + buf_pos, 1, prev_lcn, lcn,
buf_pos, prev_run_len, rlpos);
if (prev_lcn == lcn + buf_pos - prev_run_len && rlpos) {
ntfs_debug(DEBUG_OTHER, "%s(): Coalescing to "
"run (lcn 0x%x, len 0x%x).\n",
__FUNCTION__,
rl2[rlpos - 1].lcn,
rl2[rlpos - 1].len);
rl2[rlpos - 1].len = ++prev_run_len;
ntfs_debug(DEBUG_OTHER, "%s(): Run now (lcn "
"0x%x, len 0x%x), prev_run_len "
"= 0x%x.\n", __FUNCTION__,
rl2[rlpos - 1].lcn,
rl2[rlpos - 1].len,
prev_run_len);
} else {
if (rlpos)
ntfs_debug(DEBUG_OTHER, "%s(): Adding "
"new run, (previous "
"run lcn 0x%x, "
"len 0x%x).\n",
__FUNCTION__,
rl2[rlpos - 1].lcn,
rl2[rlpos - 1].len);
else
ntfs_debug(DEBUG_OTHER, "%s(): Adding "
"new run, is first "
"run.\n", __FUNCTION__);
rl2[rlpos].lcn = prev_lcn = lcn + buf_pos;
rl2[rlpos].len = prev_run_len =
(ntfs_cluster_t)1;
rlpos++;
}
/* Done? */
if (!--clusters) {
ntfs_cluster_t tc;
/*
* Update the current zone position. Positions
* of already scanned zones have been updated
* during the respective zone switches.
*/
tc = lcn + buf_pos + 1;
ntfs_debug(DEBUG_OTHER, "%s(): Done. Updating "
"current zone position, tc = "
"0x%x, search_zone = %i.\n",
__FUNCTION__, tc, search_zone);
switch (search_zone) {
case 1:
ntfs_debug(DEBUG_OTHER,
"%s(): Before checks, "
"vol->mft_zone_pos = "
"0x%x.\n", __FUNCTION__,
vol->mft_zone_pos);
if (tc >= vol->mft_zone_end) {
vol->mft_zone_pos =
vol->mft_lcn;
if (!vol->mft_zone_end)
vol->mft_zone_pos =
(ntfs_cluster_t)0;
} else if ((initial_location >=
vol->mft_zone_pos ||
tc > vol->mft_zone_pos)
&& tc >= vol->mft_lcn)
vol->mft_zone_pos = tc;
ntfs_debug(DEBUG_OTHER,
"%s(): After checks, "
"vol->mft_zone_pos = "
"0x%x.\n", __FUNCTION__,
vol->mft_zone_pos);
break;
case 2:
ntfs_debug(DEBUG_OTHER,
"%s(): Before checks, "
"vol->data1_zone_pos = "
"0x%x.\n", __FUNCTION__,
vol->data1_zone_pos);
if (tc >= vol->nr_clusters)
vol->data1_zone_pos =
vol->mft_zone_end;
else if ((initial_location >=
vol->data1_zone_pos ||
tc > vol->data1_zone_pos)
&& tc >= vol->mft_zone_end)
vol->data1_zone_pos = tc;
ntfs_debug(DEBUG_OTHER,
"%s(): After checks, "
"vol->data1_zone_pos = "
"0x%x.\n", __FUNCTION__,
vol->data1_zone_pos);
break;
case 4:
ntfs_debug(DEBUG_OTHER,
"%s(): Before checks, "
"vol->data2_zone_pos = "
"0x%x.\n", __FUNCTION__,
vol->data2_zone_pos);
if (tc >= vol->mft_zone_start)
vol->data2_zone_pos =
(ntfs_cluster_t)0;
else if (initial_location >=
vol->data2_zone_pos ||
tc > vol->data2_zone_pos)
vol->data2_zone_pos = tc;
ntfs_debug(DEBUG_OTHER,
"%s(): After checks, "
"vol->data2_zone_pos = "
"0x%x.\n", __FUNCTION__,
vol->data2_zone_pos);
break;
default:
BUG();
}
ntfs_debug(DEBUG_OTHER, "%s(): Going to "
"done_ret.\n", __FUNCTION__);
goto done_ret;
}
lcn++;
}
buf_pos += buf_size;
ntfs_debug(DEBUG_OTHER, "%s(): After inner while "
"loop: buf_size = 0x%x, lcn = 0x%x, buf_pos = "
"0x%x, need_writeback = %i.\n", __FUNCTION__,
buf_size, lcn, buf_pos, need_writeback);
if (need_writeback) {
ntfs_debug(DEBUG_OTHER, "%s(): Writing back.\n",
__FUNCTION__);
need_writeback = 0;
io.param = buf;
io.do_read = 0;
err = ntfs_readwrite_attr(vol->bitmap, data,
last_read_pos, &io);
if (err) {
ntfs_error("%s(): Bitmap writeback failed "
"in read next buffer code "
"path with error code %i.\n",
__FUNCTION__, -err);
goto err_ret;
}
}
if (buf_pos < zone_end) {
ntfs_debug(DEBUG_OTHER, "%s(): Continuing "
"outer while loop, buf_pos = 0x%x, "
"zone_end = 0x%x.\n", __FUNCTION__,
buf_pos, zone_end);
continue;
}
zone_pass_done: /* Finished with the current zone pass. */
ntfs_debug(DEBUG_OTHER, "%s(): At zone_pass_done, pass = %i.\n",
__FUNCTION__, pass);
if (pass == 1) {
/*
* Now do pass 2, scanning the first part of the zone
* we omitted in pass 1.
*/
pass = 2;
zone_end = zone_start;
switch (search_zone) {
case 1: /* mft_zone */
zone_start = vol->mft_zone_start;
break;
case 2: /* data1_zone */
zone_start = vol->mft_zone_end;
break;
case 4: /* data2_zone */
zone_start = (ntfs_cluster_t)0;
break;
default:
BUG();
}
/* Sanity check. */
if (zone_end < zone_start)
zone_end = zone_start;
buf_pos = zone_start;
ntfs_debug(DEBUG_OTHER, "%s(): Continuing "
"outer while loop, pass = 2, "
"zone_start = 0x%x, zone_end = 0x%x, "
"buf_pos = 0x%x.\n", __FUNCTION__,
zone_start, zone_end, buf_pos);
continue;
} /* pass == 2 */
done_zones_check:
ntfs_debug(DEBUG_OTHER, "%s(): At done_zones_check, "
"search_zone = %i, done_zones before = 0x%x, "
"done_zones after = 0x%x.\n", __FUNCTION__,
search_zone, done_zones, done_zones |
search_zone);
done_zones |= search_zone;
if (done_zones < 7) {
ntfs_debug(DEBUG_OTHER, "%s(): Switching zone.\n",
__FUNCTION__);
/* Now switch to the next zone we haven't done yet. */
pass = 1;
switch (search_zone) {
case 1:
ntfs_debug(DEBUG_OTHER, "%s(): Switching from "
"mft zone to data1 zone.\n",
__FUNCTION__);
/* Update mft zone position. */
if (rlpos) {
ntfs_cluster_t tc;
ntfs_debug(DEBUG_OTHER,
"%s(): Before checks, "
"vol->mft_zone_pos = "
"0x%x.\n", __FUNCTION__,
vol->mft_zone_pos);
tc = rl2[rlpos - 1].lcn +
rl2[rlpos - 1].len;
if (tc >= vol->mft_zone_end) {
vol->mft_zone_pos =
vol->mft_lcn;
if (!vol->mft_zone_end)
vol->mft_zone_pos =
(ntfs_cluster_t)0;
} else if ((initial_location >=
vol->mft_zone_pos ||
tc > vol->mft_zone_pos)
&& tc >= vol->mft_lcn)
vol->mft_zone_pos = tc;
ntfs_debug(DEBUG_OTHER,
"%s(): After checks, "
"vol->mft_zone_pos = "
"0x%x.\n", __FUNCTION__,
vol->mft_zone_pos);
}
/* Switch from mft zone to data1 zone. */
switch_to_data1_zone: search_zone = 2;
zone_start = initial_location =
vol->data1_zone_pos;
zone_end = vol->nr_clusters;
if (zone_start == vol->mft_zone_end)
pass = 2;
if (zone_start >= zone_end) {
vol->data1_zone_pos = zone_start =
vol->mft_zone_end;
pass = 2;
}
break;
case 2:
ntfs_debug(DEBUG_OTHER, "%s(): Switching from "
"data1 zone to data2 zone.\n",
__FUNCTION__);
/* Update data1 zone position. */
if (rlpos) {
ntfs_cluster_t tc;
ntfs_debug(DEBUG_OTHER,
"%s(): Before checks, "
"vol->data1_zone_pos = "
"0x%x.\n", __FUNCTION__,
vol->data1_zone_pos);
tc = rl2[rlpos - 1].lcn +
rl2[rlpos - 1].len;
if (tc >= vol->nr_clusters)
vol->data1_zone_pos =
vol->mft_zone_end;
else if ((initial_location >=
vol->data1_zone_pos ||
tc > vol->data1_zone_pos)
&& tc >= vol->mft_zone_end)
vol->data1_zone_pos = tc;
ntfs_debug(DEBUG_OTHER,
"%s(): After checks, "
"vol->data1_zone_pos = "
"0x%x.\n", __FUNCTION__,
vol->data1_zone_pos);
}
/* Switch from data1 zone to data2 zone. */
search_zone = 4;
zone_start = initial_location =
vol->data2_zone_pos;
zone_end = vol->mft_zone_start;
if (!zone_start)
pass = 2;
if (zone_start >= zone_end) {
vol->data2_zone_pos = zone_start =
initial_location =
(ntfs_cluster_t)0;
pass = 2;
}
break;
case 4:
ntfs_debug(DEBUG_OTHER, "%s(): Switching from "
"data2 zone to data1 zone.\n",
__FUNCTION__);
/* Update data2 zone position. */
if (rlpos) {
ntfs_cluster_t tc;
ntfs_debug(DEBUG_OTHER,
"%s(): Before checks, "
"vol->data2_zone_pos = "
"0x%x.\n", __FUNCTION__,
vol->data2_zone_pos);
tc = rl2[rlpos - 1].lcn +
rl2[rlpos - 1].len;
if (tc >= vol->mft_zone_start)
vol->data2_zone_pos =
(ntfs_cluster_t)0;
else if (initial_location >=
vol->data2_zone_pos ||
tc > vol->data2_zone_pos)
vol->data2_zone_pos = tc;
ntfs_debug(DEBUG_OTHER,
"%s(): After checks, "
"vol->data2_zone_pos = "
"0x%x.\n", __FUNCTION__,
vol->data2_zone_pos);
}
/* Switch from data2 zone to data1 zone. */
goto switch_to_data1_zone; /* See above. */
default:
BUG();
}
ntfs_debug(DEBUG_OTHER, "%s(): After zone switch, "
"search_zone = %i, pass = %i, "
"initial_location = 0x%x, zone_start "
"= 0x%x, zone_end = 0x%x.\n",
__FUNCTION__, search_zone, pass,
initial_location, zone_start, zone_end);
buf_pos = zone_start;
if (zone_start == zone_end) {
ntfs_debug(DEBUG_OTHER, "%s(): Empty zone, "
"going to done_zones_check.\n",
__FUNCTION__);
/* Empty zone. Don't bother searching it. */
goto done_zones_check;
}
ntfs_debug(DEBUG_OTHER, "%s(): Continuing outer while "
"loop.\n", __FUNCTION__);
continue;
} /* done_zones == 7 */
ntfs_debug(DEBUG_OTHER, "%s(): All zones are finished.\n",
__FUNCTION__);
/*
* All zones are finished! If DATA_ZONE, shrink mft zone. If
* MFT_ZONE, we have really run out of space.
*/
mft_zone_size = vol->mft_zone_end - vol->mft_zone_start;
ntfs_debug(DEBUG_OTHER, "%s(): vol->mft_zone_start = 0x%x, "
"vol->mft_zone_end = 0x%x, mft_zone_size = "
"0x%x.\n", __FUNCTION__, vol->mft_zone_start,
vol->mft_zone_end, mft_zone_size);
if (zone == MFT_ZONE || mft_zone_size <= (ntfs_cluster_t)0) {
ntfs_debug(DEBUG_OTHER, "%s(): No free clusters left, "
"returning -ENOSPC, going to "
"fail_ret.\n", __FUNCTION__);
/* Really no more space left on device. */
err = -ENOSPC;
goto fail_ret;
} /* zone == DATA_ZONE && mft_zone_size > 0 */
ntfs_debug(DEBUG_OTHER, "%s(): Shrinking mft zone.\n",
__FUNCTION__);
zone_end = vol->mft_zone_end;
mft_zone_size >>= 1;
if (mft_zone_size > (ntfs_cluster_t)0)
vol->mft_zone_end = vol->mft_zone_start + mft_zone_size;
else /* mft zone and data2 zone no longer exist. */
vol->data2_zone_pos = vol->mft_zone_start =
vol->mft_zone_end = (ntfs_cluster_t)0;
if (vol->mft_zone_pos >= vol->mft_zone_end) {
vol->mft_zone_pos = vol->mft_lcn;
if (!vol->mft_zone_end)
vol->mft_zone_pos = (ntfs_cluster_t)0;
}
buf_pos = zone_start = initial_location =
vol->data1_zone_pos = vol->mft_zone_end;
search_zone = 2;
pass = 2;
done_zones &= ~2;
ntfs_debug(DEBUG_OTHER, "%s(): After shrinking mft "
"zone, mft_zone_size = 0x%x, "
"vol->mft_zone_start = 0x%x, vol->mft_zone_end "
"= 0x%x, vol->mft_zone_pos = 0x%x, search_zone "
"= 2, pass = 2, dones_zones = 0x%x, zone_start "
"= 0x%x, zone_end = 0x%x, vol->data1_zone_pos "
"= 0x%x, continuing outer while loop.\n",
__FUNCTION__, mft_zone_size,
vol->mft_zone_start, vol->mft_zone_end,
vol->mft_zone_pos, done_zones, zone_start,
zone_end, vol->data1_zone_pos);
}
ntfs_debug(DEBUG_OTHER, "%s(): After outer while loop.\n",
__FUNCTION__);
done_ret:
ntfs_debug(DEBUG_OTHER, "%s(): At done_ret.\n", __FUNCTION__);
rl2[rlpos].lcn = (ntfs_cluster_t)-1;
rl2[rlpos].len = (ntfs_cluster_t)0;
*rl = rl2;
*rl_len = rlpos;
if (need_writeback) {
ntfs_debug(DEBUG_OTHER, "%s(): Writing back.\n", __FUNCTION__);
need_writeback = 0;
io.param = buf;
io.do_read = 0;
err = ntfs_readwrite_attr(vol->bitmap, data, last_read_pos,
&io);
if (err) {
ntfs_error("%s(): Bitmap writeback failed in done "
"code path with error code %i.\n",
__FUNCTION__, -err);
goto err_ret;
}
ntfs_debug(DEBUG_OTHER, "%s(): Wrote 0x%Lx bytes.\n",
__FUNCTION__, io.size);
}
done_fail_ret:
ntfs_debug(DEBUG_OTHER, "%s(): At done_fail_ret (follows done_ret).\n",
__FUNCTION__);
unlock_kernel();
free_page((unsigned long)buf);
if (err)
ntfs_debug(DEBUG_FILE3, "%s(): Failed to allocate "
"clusters. Returning with error code %i.\n",
__FUNCTION__, -err);
ntfs_debug(DEBUG_OTHER, "%s(): Syncing $Bitmap inode.\n", __FUNCTION__);
if (ntfs_update_inode(vol->bitmap))
ntfs_error("%s(): Failed to sync inode $Bitmap. "
"Continuing anyway.\n", __FUNCTION__);
ntfs_debug(DEBUG_OTHER, "%s(): Returning with code %i.\n", __FUNCTION__,
err);
return err;
fail_ret:
ntfs_debug(DEBUG_OTHER, "%s(): At fail_ret.\n", __FUNCTION__);
if (rl2) {
if (err == -ENOSPC) {
/* Return first free lcn and count of free clusters. */
*location = rl2[0].lcn;
*count -= clusters;
ntfs_debug(DEBUG_OTHER, "%s(): err = -ENOSPC, "
"*location = 0x%x, *count = 0x%x.\n",
__FUNCTION__, *location, *count);
}
/* Deallocate all allocated clusters. */
ntfs_debug(DEBUG_OTHER, "%s(): Deallocating allocated "
"clusters.\n", __FUNCTION__);
ntfs_deallocate_clusters(vol, rl2, rlpos);
/* Free the runlist. */
ntfs_vfree(rl2);
} else {
if (err == -ENOSPC) {
/* Nothing free at all. */
*location = vol->data1_zone_pos; /* Irrelevant... */
*count = 0;
ntfs_debug(DEBUG_OTHER, "%s(): No space left at all, "
"err = -ENOSPC, *location = 0x%x, "
"*count = 0.\n",
__FUNCTION__, *location);
}
}
*rl = NULL;
*rl_len = 0;
ntfs_debug(DEBUG_OTHER, "%s(): *rl = NULL, *rl_len = 0, "
"going to done_fail_ret.\n", __FUNCTION__);
goto done_fail_ret;
wb_err_ret:
ntfs_debug(DEBUG_OTHER, "%s(): At wb_err_ret.\n", __FUNCTION__);
if (need_writeback) {
int __err;
ntfs_debug(DEBUG_OTHER, "%s(): Writing back.\n", __FUNCTION__);
io.param = buf;
io.do_read = 0;
__err = ntfs_readwrite_attr(vol->bitmap, data, last_read_pos,
&io);
if (__err)
ntfs_error("%s(): Bitmap writeback failed in error "
"code path with error code %i.\n",
__FUNCTION__, -__err);
need_writeback = 0;
}
err_ret:
ntfs_debug(DEBUG_OTHER, "%s(): At err_ret, *location = -1, "
"*count = 0, going to fail_ret.\n", __FUNCTION__);
*location = -1;
*count = 0;
goto fail_ret;
}
 
/*
* IMPORTANT: Caller has to hold big kernel lock or the race monster will come
* to get you! (-;
* TODO: Need our own lock for bitmap accesses but BKL is more secure for now,
* considering we might not have covered all places with a lock yet. In that
* case the BKL offers a one way exclusion which is better than no exclusion
* at all... (AIA)
*/
static int ntfs_clear_bitrange(ntfs_inode *bitmap,
const ntfs_cluster_t start_bit, const ntfs_cluster_t count)
{
ntfs_cluster_t buf_size, bit, nr_bits = count;
unsigned char *buf, *byte;
int err;
ntfs_io io;
 
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
/* Calculate the required buffer size in bytes. */
buf_size = (ntfs_cluster_t)((start_bit & 7) + nr_bits + 7) >> 3;
if (buf_size <= (ntfs_cluster_t)(64 * 1024))
buf = ntfs_malloc(buf_size);
else
buf = ntfs_vmalloc(buf_size);
if (!buf)
return -ENOMEM;
/* Read the bitmap from the data attribute. */
io.param = byte = buf;
io.size = buf_size;
err = ntfs_read_attr(bitmap, bitmap->vol->at_data, 0, start_bit >> 3,
&io);
if (err || io.size != buf_size)
goto err_out;
/* Now clear the bits in the read bitmap. */
bit = start_bit & 7;
while (bit && nr_bits) { /* Process first partial byte, if present. */
*byte &= ~(1 << bit++);
nr_bits--;
bit &= 7;
if (!bit)
byte++;
}
while (nr_bits >= 8) { /* Process full bytes. */
*byte = 0;
nr_bits -= 8;
byte++;
}
bit = 0;
while (nr_bits) { /* Process last partial byte, if present. */
*byte &= ~(1 << bit);
nr_bits--;
bit++;
}
/* Write the modified bitmap back to disk. */
io.param = buf;
io.size = buf_size;
err = ntfs_write_attr(bitmap, bitmap->vol->at_data, 0, start_bit >> 3,
&io);
err_out:
if (buf_size <= (ntfs_cluster_t)(64 * 1024))
ntfs_free(buf);
else
ntfs_vfree(buf);
if (!err && io.size != buf_size)
err = -EIO;
return err;
}
 
/*
* See comments for lack of zone adjustments below in the description of the
* function ntfs_deallocate_clusters().
*/
int ntfs_deallocate_cluster_run(const ntfs_volume *vol,
const ntfs_cluster_t lcn, const ntfs_cluster_t len)
{
int err;
 
lock_kernel();
err = ntfs_clear_bitrange(vol->bitmap, lcn, len);
unlock_kernel();
return err;
}
 
/*
* This is inefficient, but logically trivial, so will do for now. Note, we
* do not touch the mft nor the data zones here because we want to minimize
* recycling of clusters to enhance the chances of data being undeleteable.
* Also we don't want the overhead. Instead we do one additional sweep of the
* current data zone during cluster allocation to check for freed clusters.
*/
int ntfs_deallocate_clusters(const ntfs_volume *vol, const ntfs_runlist *rl,
const int rl_len)
{
int i, err;
 
lock_kernel();
for (i = err = 0; i < rl_len && !err; i++)
err = ntfs_clear_bitrange(vol->bitmap, rl[i].lcn, rl[i].len);
unlock_kernel();
return err;
}
 
/inode.c
0,0 → 1,2322
/*
* inode.c
*
 
* Copyright (C) 1996 Albert D. Cahalan
 
* Copyright (C) 1998 Joseph Malicki
* Copyright (C) 1999 Steve Dodd
* Copyright (C) 2000-2001 Anton Altaparmakov (AIA)
*/
#include "ntfstypes.h"
#include "ntfsendian.h"
#include "struct.h"
#include "inode.h"
#include <linux/errno.h>
#include "macros.h"
#include "attr.h"
#include "super.h"
#include "dir.h"
#include "support.h"
#include "util.h"
#include <linux/ntfs_fs.h>
#include <linux/smp_lock.h>
 
typedef struct {
int recno;
unsigned char *record;
} ntfs_mft_record;
 
typedef struct {
int size;
int count;
ntfs_mft_record *records;
} ntfs_disk_inode;
 
static void ntfs_fill_mft_header(ntfs_u8 *mft, int rec_size, int seq_no,
int links, int flags)
{
int fixup_ofs = 0x2a;
int fixup_cnt = rec_size / NTFS_SECTOR_SIZE + 1;
int attr_ofs = (fixup_ofs + 2 * fixup_cnt + 7) & ~7;
 
NTFS_PUTU32(mft + 0x00, 0x454c4946); /* FILE */
NTFS_PUTU16(mft + 0x04, fixup_ofs); /* Offset to fixup. */
NTFS_PUTU16(mft + 0x06, fixup_cnt); /* Number of fixups. */
NTFS_PUTU64(mft + 0x08, 0); /* Logical sequence number. */
NTFS_PUTU16(mft + 0x10, seq_no); /* Sequence number. */
NTFS_PUTU16(mft + 0x12, links); /* Hard link count. */
NTFS_PUTU16(mft + 0x14, attr_ofs); /* Offset to attributes. */
NTFS_PUTU16(mft + 0x16, flags); /* Flags: 1 = In use,
2 = Directory. */
NTFS_PUTU32(mft + 0x18, attr_ofs + 8); /* Bytes in use. */
NTFS_PUTU32(mft + 0x1c, rec_size); /* Total allocated size. */
NTFS_PUTU64(mft + 0x20, 0); /* Base mft record. */
NTFS_PUTU16(mft + 0x28, 0); /* Next attr instance. */
NTFS_PUTU16(mft + fixup_ofs, 1); /* Fixup word. */
NTFS_PUTU32(mft + attr_ofs, (__u32)-1); /* End of attributes marker. */
}
 
/*
* Search in an inode an attribute by type and name.
* FIXME: Check that when attributes are inserted all attribute list
* attributes are expanded otherwise need to modify this function to deal
* with attribute lists. (AIA)
*/
ntfs_attribute *ntfs_find_attr(ntfs_inode *ino, int type, char *name)
{
int i;
if (!ino) {
ntfs_error("ntfs_find_attr: NO INODE!\n");
return 0;
}
for (i = 0; i < ino->attr_count; i++) {
if (type < ino->attrs[i].type)
return 0;
if (type == ino->attrs[i].type) {
if (!name) {
if (!ino->attrs[i].name)
return ino->attrs + i;
} else if (ino->attrs[i].name &&
!ntfs_ua_strncmp(ino->attrs[i].name, name,
strlen(name)))
return ino->attrs + i;
}
}
return 0;
}
 
/*
* Insert all attributes from the record mftno of the MFT in the inode ino.
* If mftno is a base mft record we abort as soon as we find the attribute
* list, but only on the first pass. We will get called later when the attribute
* list attribute is being parsed so we need to distinguish the two cases.
* FIXME: We should be performing structural consistency checks. (AIA)
* Return 0 on success or -errno on error.
*/
static int ntfs_insert_mft_attributes(ntfs_inode* ino, char *mft, int mftno)
{
int i, error, type, len, present = 0;
char *it;
 
/* Check for duplicate extension record. */
for(i = 0; i < ino->record_count; i++)
if (ino->records[i] == mftno) {
if (i)
return 0;
present = 1;
break;
}
if (!present) {
/* (re-)allocate space if necessary. */
if (ino->record_count % 8 == 0) {
int *new;
 
new = ntfs_malloc((ino->record_count + 8) *
sizeof(int));
if (!new)
return -ENOMEM;
if (ino->records) {
for (i = 0; i < ino->record_count; i++)
new[i] = ino->records[i];
ntfs_free(ino->records);
}
ino->records = new;
}
ino->records[ino->record_count] = mftno;
ino->record_count++;
}
it = mft + NTFS_GETU16(mft + 0x14); /* mft->attrs_offset */
do {
type = NTFS_GETU32(it);
len = NTFS_GETU32(it + 4);
if (type != -1) {
error = ntfs_insert_attribute(ino, it);
if (error)
return error;
}
/* If we have just processed the attribute list and this is
* the first time we are parsing this (base) mft record then we
* are done so that the attribute list gets parsed before the
* entries in the base mft record. Otherwise we run into
* problems with encountering attributes out of order and when
* this happens with different attribute extents we die. )-:
* This way we are ok as the attribute list is always sorted
* fully and correctly. (-: */
if (type == 0x20 && !present)
return 0;
it += len;
} while (type != -1); /* Attribute listing ends with type -1. */
return 0;
}
 
/*
* Insert a single specific attribute from the record mftno of the MFT in the
* inode ino. We disregard the attribute list assuming we have already parsed
* it.
* FIXME: We should be performing structural consistency checks. (AIA)
* Return 0 on success or -errno on error.
*/
static int ntfs_insert_mft_attribute(ntfs_inode* ino, int mftno,
ntfs_u8 *attr)
{
int i, error, present = 0;
 
/* Check for duplicate extension record. */
for(i = 0; i < ino->record_count; i++)
if (ino->records[i] == mftno) {
present = 1;
break;
}
if (!present) {
/* (re-)allocate space if necessary. */
if (ino->record_count % 8 == 0) {
int *new;
 
new = ntfs_malloc((ino->record_count + 8) *
sizeof(int));
if (!new)
return -ENOMEM;
if (ino->records) {
for (i = 0; i < ino->record_count; i++)
new[i] = ino->records[i];
ntfs_free(ino->records);
}
ino->records = new;
}
ino->records[ino->record_count] = mftno;
ino->record_count++;
}
if (NTFS_GETU32(attr) == -1) {
ntfs_debug(DEBUG_FILE3, "ntfs_insert_mft_attribute: attribute "
"type is -1.\n");
return 0;
}
error = ntfs_insert_attribute(ino, attr);
if (error)
return error;
return 0;
}
 
/* Read and insert all the attributes of an 'attribute list' attribute.
* Return the number of remaining bytes in *plen. */
static int parse_attributes(ntfs_inode *ino, ntfs_u8 *alist, int *plen)
{
ntfs_u8 *mft, *attr;
int mftno, l, error;
int last_mft = -1;
int len = *plen;
int tries = 0;
if (!ino->attr) {
ntfs_error("parse_attributes: called on inode 0x%x without a "
"loaded base mft record.\n", ino->i_number);
return -EINVAL;
}
mft = ntfs_malloc(ino->vol->mft_record_size);
if (!mft)
return -ENOMEM;
while (len > 8) {
l = NTFS_GETU16(alist + 4);
if (l > len)
break;
/* Process an attribute description. */
mftno = NTFS_GETU32(alist + 0x10);
/* FIXME: The mft reference (alist + 0x10) is __s64.
* - Not a problem unless we encounter a huge partition.
* - Should be consistency checking the sequence numbers
* though! This should maybe happen in
* ntfs_read_mft_record() itself and a hotfix could
* then occur there or the user notified to run
* ntfsck. (AIA) */
if (mftno != ino->i_number && mftno != last_mft) {
continue_after_loading_mft_data:
last_mft = mftno;
error = ntfs_read_mft_record(ino->vol, mftno, mft);
if (error) {
if (error == -EINVAL && !tries)
goto force_load_mft_data;
failed_reading_mft_data:
ntfs_debug(DEBUG_FILE3, "parse_attributes: "
"ntfs_read_mft_record(mftno = 0x%x) "
"failed\n", mftno);
ntfs_free(mft);
return error;
}
}
attr = ntfs_find_attr_in_mft_rec(
ino->vol, /* ntfs volume */
mftno == ino->i_number ?/* mft record is: */
ino->attr: /* base record */
mft, /* extension record */
NTFS_GETU32(alist + 0), /* type */
(wchar_t*)(alist + alist[7]), /* name */
alist[6], /* name length */
1, /* ignore case */
NTFS_GETU16(alist + 24) /* instance number */
);
if (!attr) {
ntfs_error("parse_attributes: mft records 0x%x and/or "
"0x%x corrupt!\n", ino->i_number, mftno);
ntfs_free(mft);
return -EINVAL; /* FIXME: Better error code? (AIA) */
}
error = ntfs_insert_mft_attribute(ino, mftno, attr);
if (error) {
ntfs_debug(DEBUG_FILE3, "parse_attributes: "
"ntfs_insert_mft_attribute(mftno 0x%x, "
"attribute type 0x%x) failed\n", mftno,
NTFS_GETU32(alist + 0));
ntfs_free(mft);
return error;
}
len -= l;
alist += l;
}
ntfs_free(mft);
*plen = len;
return 0;
force_load_mft_data:
{
ntfs_u8 *mft2, *attr2;
int mftno2;
int last_mft2 = last_mft;
int len2 = len;
int error2;
int found2 = 0;
ntfs_u8 *alist2 = alist;
/*
* We only get here if $DATA wasn't found in $MFT which only happens
* on volume mount when $MFT has an attribute list and there are
* attributes before $DATA which are inside extent mft records. So
* we just skip forward to the $DATA attribute and read that. Then we
* restart which is safe as an attribute will not be inserted twice.
*
* This still will not fix the case where the attribute list is non-
* resident, larger than 1024 bytes, and the $DATA attribute list entry
* is not in the first 1024 bytes. FIXME: This should be implemented
* somehow! Perhaps by passing special error code up to
* ntfs_load_attributes() so it keeps going trying to get to $DATA
* regardless. Then it would have to restart just like we do here.
*/
mft2 = ntfs_malloc(ino->vol->mft_record_size);
if (!mft2) {
ntfs_free(mft);
return -ENOMEM;
}
ntfs_memcpy(mft2, mft, ino->vol->mft_record_size);
while (len2 > 8) {
l = NTFS_GETU16(alist2 + 4);
if (l > len2)
break;
if (NTFS_GETU32(alist2 + 0x0) < ino->vol->at_data) {
len2 -= l;
alist2 += l;
continue;
}
if (NTFS_GETU32(alist2 + 0x0) > ino->vol->at_data) {
if (found2)
break;
/* Uh-oh! It really isn't there! */
ntfs_error("Either the $MFT is corrupt or, equally "
"likely, the $MFT is too complex for "
"the current driver to handle. Please "
"email the ntfs maintainer that you "
"saw this message. Thank you.\n");
goto failed_reading_mft_data;
}
/* Process attribute description. */
mftno2 = NTFS_GETU32(alist2 + 0x10);
if (mftno2 != ino->i_number && mftno2 != last_mft2) {
last_mft2 = mftno2;
error2 = ntfs_read_mft_record(ino->vol, mftno2, mft2);
if (error2) {
ntfs_debug(DEBUG_FILE3, "parse_attributes: "
"ntfs_read_mft_record(mftno2 = 0x%x) "
"failed\n", mftno2);
ntfs_free(mft2);
goto failed_reading_mft_data;
}
}
attr2 = ntfs_find_attr_in_mft_rec(
ino->vol, /* ntfs volume */
mftno2 == ino->i_number ?/* mft record is: */
ino->attr: /* base record */
mft2, /* extension record */
NTFS_GETU32(alist2 + 0), /* type */
(wchar_t*)(alist2 + alist2[7]), /* name */
alist2[6], /* name length */
1, /* ignore case */
NTFS_GETU16(alist2 + 24) /* instance number */
);
if (!attr2) {
ntfs_error("parse_attributes: mft records 0x%x and/or "
"0x%x corrupt!\n", ino->i_number,
mftno2);
ntfs_free(mft2);
goto failed_reading_mft_data;
}
error2 = ntfs_insert_mft_attribute(ino, mftno2, attr2);
if (error2) {
ntfs_debug(DEBUG_FILE3, "parse_attributes: "
"ntfs_insert_mft_attribute(mftno2 0x%x, "
"attribute2 type 0x%x) failed\n", mftno2,
NTFS_GETU32(alist2 + 0));
ntfs_free(mft2);
goto failed_reading_mft_data;
}
len2 -= l;
alist2 += l;
found2 = 1;
}
ntfs_free(mft2);
tries = 1;
goto continue_after_loading_mft_data;
}
}
 
static void ntfs_load_attributes(ntfs_inode *ino)
{
ntfs_attribute *alist;
int datasize;
int offset, len, delta;
char *buf;
ntfs_volume *vol = ino->vol;
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 1\n", ino->i_number);
if (ntfs_insert_mft_attributes(ino, ino->attr, ino->i_number))
return;
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 2\n", ino->i_number);
alist = ntfs_find_attr(ino, vol->at_attribute_list, 0);
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 3\n", ino->i_number);
if (!alist)
return;
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 4\n", ino->i_number);
datasize = alist->size;
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: alist->size = 0x%x\n",
ino->i_number, alist->size);
if (alist->resident) {
parse_attributes(ino, alist->d.data, &datasize);
return;
}
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 5\n", ino->i_number);
buf = ntfs_malloc(1024);
if (!buf) /* FIXME: Should be passing error code to caller. (AIA) */
return;
delta = 0;
for (offset = 0; datasize; datasize -= len, offset += len) {
ntfs_io io;
io.fn_put = ntfs_put;
io.fn_get = 0;
io.param = buf + delta;
len = 1024 - delta;
if (len > datasize)
len = datasize;
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: len = %i\n",
ino->i_number, len);
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: delta = %i\n",
ino->i_number, delta);
io.size = len;
if (ntfs_read_attr(ino, vol->at_attribute_list, 0, offset,
&io))
ntfs_error("error in load_attributes\n");
delta += len;
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: after += len, "
"delta = %i\n", ino->i_number, delta);
parse_attributes(ino, buf, &delta);
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: after "
"parse_attr, delta = %i\n", ino->i_number,
delta);
if (delta)
/* Move remaining bytes to buffer start. */
ntfs_memmove(buf, buf + len - delta, delta);
}
ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 6\n", ino->i_number);
ntfs_free(buf);
}
int ntfs_init_inode(ntfs_inode *ino, ntfs_volume *vol, int inum)
{
char *buf;
int error;
 
ntfs_debug(DEBUG_FILE1, "Initializing inode 0x%x\n", inum);
ino->i_number = inum;
ino->vol = vol;
ino->attr = buf = ntfs_malloc(vol->mft_record_size);
if (!buf)
return -ENOMEM;
error = ntfs_read_mft_record(vol, inum, ino->attr);
if (error) {
ntfs_debug(DEBUG_OTHER, "Init inode: 0x%x failed\n", inum);
return error;
}
ntfs_debug(DEBUG_FILE2, "Init inode: got mft 0x%x\n", inum);
ino->sequence_number = NTFS_GETU16(buf + 0x10);
ino->attr_count = 0;
ino->record_count = 0;
ino->records = 0;
ino->attrs = 0;
ntfs_load_attributes(ino);
ntfs_debug(DEBUG_FILE2, "Init inode: done 0x%x\n", inum);
return 0;
}
 
void ntfs_clear_inode(ntfs_inode *ino)
{
int i;
if (!ino->attr) {
ntfs_error("ntfs_clear_inode: double free\n");
return;
}
ntfs_free(ino->attr);
ino->attr = 0;
ntfs_free(ino->records);
ino->records = 0;
for (i = 0; i < ino->attr_count; i++) {
if (ino->attrs[i].name)
ntfs_free(ino->attrs[i].name);
if (ino->attrs[i].resident) {
if (ino->attrs[i].d.data)
ntfs_free(ino->attrs[i].d.data);
} else {
if (ino->attrs[i].d.r.runlist)
ntfs_vfree(ino->attrs[i].d.r.runlist);
}
}
ntfs_free(ino->attrs);
ino->attrs = 0;
}
 
/* Check and fixup a MFT record. */
int ntfs_check_mft_record(ntfs_volume *vol, char *record)
{
return ntfs_fixup_record(record, "FILE", vol->mft_record_size);
}
 
/* Return (in result) the value indicating the next available attribute
* chunk number. Works for inodes w/o extension records only. */
int ntfs_allocate_attr_number(ntfs_inode *ino, int *result)
{
if (ino->record_count != 1)
return -EOPNOTSUPP;
*result = NTFS_GETU16(ino->attr + 0x28);
NTFS_PUTU16(ino->attr + 0x28, (*result) + 1);
return 0;
}
 
/* Find the location of an attribute in the inode. A name of NULL indicates
* unnamed attributes. Return pointer to attribute or NULL if not found. */
char *ntfs_get_attr(ntfs_inode *ino, int attr, char *name)
{
/* Location of first attribute. */
char *it = ino->attr + NTFS_GETU16(ino->attr + 0x14);
int type;
int len;
/* Only check for magic DWORD here, fixup should have happened before.*/
if (!IS_MFT_RECORD(ino->attr))
return 0;
do {
type = NTFS_GETU32(it);
len = NTFS_GETU16(it + 4);
/* We found the attribute type. Is the name correct, too? */
if (type == attr) {
int namelen = NTFS_GETU8(it + 9);
char *name_it, *n = name;
/* Match given name and attribute name if present.
Make sure attribute name is Unicode. */
if (!name) {
goto check_namelen;
} else if (namelen) {
for (name_it = it + NTFS_GETU16(it + 10);
namelen; n++, name_it += 2, namelen--)
if (*name_it != *n || name_it[1])
break;
check_namelen:
if (!namelen)
break;
}
}
it += len;
} while (type != -1); /* List of attributes ends with type -1. */
if (type == -1)
return 0;
return it;
}
 
__s64 ntfs_get_attr_size(ntfs_inode *ino, int type, char *name)
{
ntfs_attribute *attr = ntfs_find_attr(ino, type, name);
if (!attr)
return 0;
return
attr->size;
}
int ntfs_attr_is_resident(ntfs_inode *ino, int type, char *name)
{
ntfs_attribute *attr = ntfs_find_attr(ino, type, name);
if (!attr)
return 0;
return attr->resident;
}
/*
* A run is coded as a type indicator, an unsigned length, and a signed cluster
* offset.
* . To save space, length and offset are fields of variable length. The low
* nibble of the type indicates the width of the length :), the high nibble
* the width of the offset.
* . The first offset is relative to cluster 0, later offsets are relative to
* the previous cluster.
*
* This function decodes a run. Length is an output parameter, data and cluster
* are in/out parameters.
*/
int ntfs_decompress_run(unsigned char **data, int *length,
ntfs_cluster_t *cluster, int *ctype)
{
unsigned char type = *(*data)++;
*ctype = 0;
switch (type & 0xF) {
case 1:
*length = NTFS_GETS8(*data);
break;
case 2:
*length = NTFS_GETS16(*data);
break;
case 3:
*length = NTFS_GETS24(*data);
break;
case 4:
*length = NTFS_GETS32(*data);
break;
/* Note: cases 5-8 are probably pointless to code, since how
* many runs > 4GB of length are there? At the most, cases 5
* and 6 are probably necessary, and would also require making
* length 64-bit throughout. */
default:
ntfs_error("Can't decode run type field 0x%x\n", type);
return -1;
}
// ntfs_debug(DEBUG_FILE3, "ntfs_decompress_run: length = 0x%x\n",*length);
if (*length < 0)
{
ntfs_error("Negative run length decoded\n");
return -1;
}
*data += (type & 0xF);
switch (type & 0xF0) {
case 0:
*ctype = 2;
break;
case 0x10:
*cluster += NTFS_GETS8(*data);
break;
case 0x20:
*cluster += NTFS_GETS16(*data);
break;
case 0x30:
*cluster += NTFS_GETS24(*data);
break;
case 0x40:
*cluster += NTFS_GETS32(*data);
break;
#if 0 /* Keep for future, in case ntfs_cluster_t ever becomes 64bit. */
case 0x50:
*cluster += NTFS_GETS40(*data);
break;
case 0x60:
*cluster += NTFS_GETS48(*data);
break;
case 0x70:
*cluster += NTFS_GETS56(*data);
break;
case 0x80:
*cluster += NTFS_GETS64(*data);
break;
#endif
default:
ntfs_error("Can't decode run type field 0x%x\n", type);
return -1;
}
// ntfs_debug(DEBUG_FILE3, "ntfs_decompress_run: cluster = 0x%x\n",
// *cluster);
*data += (type >> 4);
return 0;
}
 
static void dump_runlist(const ntfs_runlist *rl, const int rlen);
 
/*
* FIXME: ntfs_readwrite_attr() has the effect of writing @dest to @offset of
* the attribute value of the attribute @attr in the in memory inode @ino.
* If the attribute value of @attr is non-resident the value's contents at
* @offset are actually written to disk (from @dest). The on disk mft record
* describing the non-resident attribute value is not updated!
* If the attribute value is resident then the value is written only in
* memory. The on disk mft record containing the value is not written to disk.
* A possible fix would be to call ntfs_update_inode() before returning. (AIA)
*/
/* Reads l bytes of the attribute (attr, name) of ino starting at offset on
* vol into buf. Returns the number of bytes read in the ntfs_io struct.
* Returns 0 on success, errno on failure */
int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
ntfs_io *dest)
{
int rnum, s_vcn, error, clustersizebits;
ntfs_cluster_t cluster, s_cluster, vcn, len;
__s64 l, chunk, copied;
 
ntfs_debug(DEBUG_FILE3, "%s(): %s 0x%x bytes at offset "
"0x%Lx %s inode 0x%x, attr type 0x%x.\n", __FUNCTION__,
dest->do_read ? "Read" : "Write", dest->size, offset,
dest->do_read ? "from" : "to", ino->i_number,
attr->type);
l = dest->size;
if (l == 0)
return 0;
if (dest->do_read) {
/* If read _starts_ beyond end of stream, return nothing. */
if (offset >= attr->size) {
dest->size = 0;
return 0;
}
/* If read _extends_ beyond end of stream, return as much
* initialised data as we have. */
if (offset + l >= attr->size)
l = dest->size = attr->size - offset;
} else {
/*
* If write extends beyond _allocated_ size, extend attribute,
* updating attr->allocated and attr->size in the process. (AIA)
*/
if ((!attr->resident && offset + l > attr->allocated) ||
(attr->resident && offset + l > attr->size)) {
error = ntfs_resize_attr(ino, attr, offset + l);
if (error)
return error;
}
if (!attr->resident) {
/* Has amount of data increased? */
if (offset + l > attr->size)
attr->size = offset + l;
/* Has amount of initialised data increased? */
if (offset + l > attr->initialized) {
/* FIXME: Clear the section between the old
* initialised length and the write start.
* (AIA) */
attr->initialized = offset + l;
}
}
}
if (attr->resident) {
if (dest->do_read)
dest->fn_put(dest, (ntfs_u8*)attr->d.data + offset, l);
else
dest->fn_get((ntfs_u8*)attr->d.data + offset, dest, l);
dest->size = l;
return 0;
}
if (dest->do_read) {
/* Read uninitialized data. */
if (offset >= attr->initialized)
return ntfs_read_zero(dest, l);
if (offset + l > attr->initialized) {
dest->size = chunk = attr->initialized - offset;
error = ntfs_readwrite_attr(ino, attr, offset, dest);
if (error || (dest->size != chunk && (error = -EIO, 1)))
return error;
dest->size += l - chunk;
return ntfs_read_zero(dest, l - chunk);
}
if (attr->flags & ATTR_IS_COMPRESSED)
return ntfs_read_compressed(ino, attr, offset, dest);
} else {
if (attr->flags & ATTR_IS_COMPRESSED)
return ntfs_write_compressed(ino, attr, offset, dest);
}
vcn = 0;
clustersizebits = ino->vol->cluster_size_bits;
s_vcn = offset >> clustersizebits;
for (rnum = 0; rnum < attr->d.r.len &&
vcn + attr->d.r.runlist[rnum].len <= s_vcn; rnum++)
vcn += attr->d.r.runlist[rnum].len;
if (rnum == attr->d.r.len) {
ntfs_debug(DEBUG_FILE3, "%s(): EOPNOTSUPP: "
"inode = 0x%x, rnum = %i, offset = 0x%Lx, vcn = 0x%x, "
"s_vcn = 0x%x.\n", __FUNCTION__, ino->i_number, rnum,
offset, vcn, s_vcn);
dump_runlist(attr->d.r.runlist, attr->d.r.len);
/*FIXME: Should extend runlist. */
return -EOPNOTSUPP;
}
copied = 0;
while (l) {
s_vcn = offset >> clustersizebits;
cluster = attr->d.r.runlist[rnum].lcn;
len = attr->d.r.runlist[rnum].len;
s_cluster = cluster + s_vcn - vcn;
chunk = ((__s64)(vcn + len) << clustersizebits) - offset;
if (chunk > l)
chunk = l;
dest->size = chunk;
error = ntfs_getput_clusters(ino->vol, s_cluster, offset -
((__s64)s_vcn << clustersizebits), dest);
if (error) {
ntfs_error("Read/write error.\n");
dest->size = copied;
return error;
}
l -= chunk;
copied += chunk;
offset += chunk;
if (l && offset >= ((__s64)(vcn + len) << clustersizebits)) {
rnum++;
vcn += len;
cluster = attr->d.r.runlist[rnum].lcn;
len = attr->d.r.runlist[rnum].len;
}
}
dest->size = copied;
return 0;
}
 
int ntfs_read_attr(ntfs_inode *ino, int type, char *name, __s64 offset,
ntfs_io *buf)
{
ntfs_attribute *attr;
 
buf->do_read = 1;
attr = ntfs_find_attr(ino, type, name);
if (!attr) {
ntfs_debug(DEBUG_FILE3, "%s(): attr 0x%x not found in inode "
"0x%x\n", __FUNCTION__, type, ino->i_number);
return -EINVAL;
}
return ntfs_readwrite_attr(ino, attr, offset, buf);
}
 
int ntfs_write_attr(ntfs_inode *ino, int type, char *name, __s64 offset,
ntfs_io *buf)
{
ntfs_attribute *attr;
buf->do_read = 0;
attr = ntfs_find_attr(ino, type, name);
if (!attr) {
ntfs_debug(DEBUG_FILE3, "%s(): attr 0x%x not found in inode "
"0x%x\n", __FUNCTION__, type, ino->i_number);
return -EINVAL;
}
return ntfs_readwrite_attr(ino, attr, offset, buf);
}
 
/* -2 = error, -1 = hole, >= 0 means real disk cluster (lcn). */
int ntfs_vcn_to_lcn(ntfs_inode *ino, int vcn)
{
int rnum;
ntfs_attribute *data;
data = ntfs_find_attr(ino, ino->vol->at_data, 0);
if (!data || data->resident || data->flags & (ATTR_IS_COMPRESSED |
ATTR_IS_ENCRYPTED))
return -2;
if (data->size <= (__s64)vcn << ino->vol->cluster_size_bits)
return -2;
if (data->initialized <= (__s64)vcn << ino->vol->cluster_size_bits)
return -1;
for (rnum = 0; rnum < data->d.r.len &&
vcn >= data->d.r.runlist[rnum].len; rnum++)
vcn -= data->d.r.runlist[rnum].len;
if (data->d.r.runlist[rnum].lcn >= 0)
return data->d.r.runlist[rnum].lcn + vcn;
return data->d.r.runlist[rnum].lcn + vcn;
}
 
static int allocate_store(ntfs_volume *vol, ntfs_disk_inode *store, int count)
{
int i;
if (store->count > count)
return 0;
if (store->size < count) {
ntfs_mft_record *n = ntfs_malloc((count + 4) *
sizeof(ntfs_mft_record));
if (!n)
return -ENOMEM;
if (store->size) {
for (i = 0; i < store->size; i++)
n[i] = store->records[i];
ntfs_free(store->records);
}
store->size = count + 4;
store->records = n;
}
for (i = store->count; i < count; i++) {
store->records[i].record = ntfs_malloc(vol->mft_record_size);
if (!store->records[i].record)
return -ENOMEM;
store->count++;
}
return 0;
}
 
static void deallocate_store(ntfs_disk_inode* store)
{
int i;
for (i = 0; i < store->count; i++)
ntfs_free(store->records[i].record);
ntfs_free(store->records);
store->count = store->size = 0;
store->records = 0;
}
 
/**
* layout_runs - compress runlist into mapping pairs array
* @attr: attribute containing the runlist to compress
* @rec: destination buffer to hold the mapping pairs array
* @offs: current position in @rec (in/out variable)
* @size: size of the buffer @rec
*
* layout_runs walks the runlist in @attr, compresses it and writes it out the
* resulting mapping pairs array into @rec (up to a maximum of @size bytes are
* written). On entry @offs is the offset in @rec at which to begin writing the
* mapping pairs array. On exit, it contains the offset in @rec of the first
* byte after the end of the mapping pairs array.
*/
static int layout_runs(ntfs_attribute *attr, char *rec, int *offs, int size)
{
int i, len, offset, coffs;
/* ntfs_cluster_t MUST be signed! (AIA) */
ntfs_cluster_t cluster, rclus;
ntfs_runlist *rl = attr->d.r.runlist;
cluster = 0;
offset = *offs;
for (i = 0; i < attr->d.r.len; i++) {
/*
* We cheat with this check on the basis that lcn will never
* be less than -1 and the lcn delta will fit in signed
* 32-bits (ntfs_cluster_t). (AIA)
*/
if (rl[i].lcn < (ntfs_cluster_t)-1) {
ntfs_error("layout_runs() encountered an out of bounds "
"cluster delta, lcn = %i.\n",
rl[i].lcn);
return -ERANGE;
}
rclus = rl[i].lcn - cluster;
len = rl[i].len;
rec[offset] = 0;
if (offset + 9 > size)
return -E2BIG; /* It might still fit, but this
* simplifies testing. */
/*
* Run length is stored as signed number, so deal with it
* properly, i.e. observe that a negative number will have all
* its most significant bits set to 1 but we don't store that
* in the mapping pairs array. We store the smallest type of
* negative number required, thus in the first if we check
* whether len fits inside a signed byte and if so we store it
* as such, the next ifs check for a signed short, then a signed
* 24-bit and finally the full blown signed 32-bit. Same goes
* for rlus below. (AIA)
*/
if (len >= -0x80 && len <= 0x7f) {
NTFS_PUTU8(rec + offset + 1, len & 0xff);
coffs = 1;
} else if (len >= -0x8000 && len <= 0x7fff) {
NTFS_PUTU16(rec + offset + 1, len & 0xffff);
coffs = 2;
} else if (len >= -0x800000 && len <= 0x7fffff) {
NTFS_PUTU24(rec + offset + 1, len & 0xffffff);
coffs = 3;
} else /* if (len >= -0x80000000LL && len <= 0x7fffffff */ {
NTFS_PUTU32(rec + offset + 1, len);
coffs = 4;
} /* else ... FIXME: When len becomes 64-bit we need to extend
* the else if () statements. (AIA) */
*(rec + offset) |= coffs++;
if (rl[i].lcn == (ntfs_cluster_t)-1) /* Compressed run. */
/* Nothing */;
else if (rclus >= -0x80 && rclus <= 0x7f) {
*(rec + offset) |= 0x10;
NTFS_PUTS8(rec + offset + coffs, rclus & 0xff);
coffs += 1;
} else if (rclus >= -0x8000 && rclus <= 0x7fff) {
*(rec + offset) |= 0x20;
NTFS_PUTS16(rec + offset + coffs, rclus & 0xffff);
coffs += 2;
} else if (rclus >= -0x800000 && rclus <= 0x7fffff) {
*(rec + offset) |= 0x30;
NTFS_PUTS24(rec + offset + coffs, rclus & 0xffffff);
coffs += 3;
} else /* if (rclus >= -0x80000000LL && rclus <= 0x7fffffff)*/ {
*(rec + offset) |= 0x40;
NTFS_PUTS32(rec + offset + coffs, rclus
/* & 0xffffffffLL */);
coffs += 4;
} /* FIXME: When rclus becomes 64-bit.
else if (rclus >= -0x8000000000 && rclus <= 0x7FFFFFFFFF) {
*(rec + offset) |= 0x50;
NTFS_PUTS40(rec + offset + coffs, rclus &
0xffffffffffLL);
coffs += 5;
} else if (rclus >= -0x800000000000 &&
rclus <= 0x7FFFFFFFFFFF) {
*(rec + offset) |= 0x60;
NTFS_PUTS48(rec + offset + coffs, rclus &
0xffffffffffffLL);
coffs += 6;
} else if (rclus >= -0x80000000000000 &&
rclus <= 0x7FFFFFFFFFFFFF) {
*(rec + offset) |= 0x70;
NTFS_PUTS56(rec + offset + coffs, rclus &
0xffffffffffffffLL);
coffs += 7;
} else {
*(rec + offset) |= 0x80;
NTFS_PUTS64(rec + offset + coffs, rclus);
coffs += 8;
} */
offset += coffs;
if (rl[i].lcn)
cluster = rl[i].lcn;
}
if (offset >= size)
return -E2BIG;
/* Terminating null. */
*(rec + offset++) = 0;
*offs = offset;
return 0;
}
 
static void count_runs(ntfs_attribute *attr, char *buf)
{
ntfs_u32 first, count, last, i;
first = 0;
for (i = 0, count = 0; i < attr->d.r.len; i++)
count += attr->d.r.runlist[i].len;
last = first + count - 1;
NTFS_PUTU64(buf + 0x10, first);
NTFS_PUTU64(buf + 0x18, last);
}
 
/**
* layout_attr - convert in memory attribute to on disk attribute record
* @attr: in memory attribute to convert
* @buf: destination buffer for on disk attribute record
* @size: size of the destination buffer
* @psize: size of converted on disk attribute record (out variable)
*
* layout_attr() takes the attribute @attr and converts it into the appropriate
* on disk structure, writing it into @buf (up to @size bytes are written).
*
* On success we return 0 and set @*psize to the actual byte size of the on-
* disk attribute that was written into @buf.
*/
static int layout_attr(ntfs_attribute *attr, char *buf, int size, int *psize)
{
int nameoff, hdrsize, asize;
if (attr->resident) {
nameoff = 0x18;
hdrsize = (nameoff + 2 * attr->namelen + 7) & ~7;
asize = (hdrsize + attr->size + 7) & ~7;
if (size < asize)
return -E2BIG;
NTFS_PUTU32(buf + 0x10, attr->size);
NTFS_PUTU8(buf + 0x16, attr->indexed);
NTFS_PUTU16(buf + 0x14, hdrsize);
if (attr->size)
ntfs_memcpy(buf + hdrsize, attr->d.data, attr->size);
} else {
int error;
 
if (attr->flags & ATTR_IS_COMPRESSED)
nameoff = 0x48;
else
nameoff = 0x40;
hdrsize = (nameoff + 2 * attr->namelen + 7) & ~7;
if (size < hdrsize)
return -E2BIG;
/* Make asize point at the end of the attribute record header,
i.e. at the beginning of the mapping pairs array. */
asize = hdrsize;
error = layout_runs(attr, buf, &asize, size);
/* Now, asize points one byte beyond the end of the mapping
pairs array. */
if (error)
return error;
/* The next attribute has to begin on 8-byte boundary. */
asize = (asize + 7) & ~7;
/* FIXME: fragments */
count_runs(attr, buf);
NTFS_PUTU16(buf + 0x20, hdrsize);
NTFS_PUTU16(buf + 0x22, attr->cengine);
NTFS_PUTU32(buf + 0x24, 0);
NTFS_PUTS64(buf + 0x28, attr->allocated);
NTFS_PUTS64(buf + 0x30, attr->size);
NTFS_PUTS64(buf + 0x38, attr->initialized);
if (attr->flags & ATTR_IS_COMPRESSED)
NTFS_PUTS64(buf + 0x40, attr->compsize);
}
NTFS_PUTU32(buf, attr->type);
NTFS_PUTU32(buf + 4, asize);
NTFS_PUTU8(buf + 8, attr->resident ? 0 : 1);
NTFS_PUTU8(buf + 9, attr->namelen);
NTFS_PUTU16(buf + 0xa, nameoff);
NTFS_PUTU16(buf + 0xc, attr->flags);
NTFS_PUTU16(buf + 0xe, attr->attrno);
if (attr->namelen)
ntfs_memcpy(buf + nameoff, attr->name, 2 * attr->namelen);
*psize = asize;
return 0;
}
 
/**
* layout_inode - convert an in-memory inode into on disk mft record(s)
* @ino: in memory inode to convert
* @store: on disk inode, contain buffers for the on disk mft record(s)
*
* layout_inode takes the in memory inode @ino, converts it into a (sequence of)
* mft record(s) and writes them to the appropriate buffers in the @store.
*
* Return 0 on success,
* the required mft record count (>0) if the inode does not fit,
* -ENOMEM if memory allocation problem, or
* -EOPNOTSUP if beyond our capabilities.
*
* TODO: We at the moment do not support extension mft records. (AIA)
*/
int layout_inode(ntfs_inode *ino, ntfs_disk_inode *store)
{
int offset, i, size, psize, error, count, recno;
ntfs_attribute *attr;
unsigned char *rec;
 
error = allocate_store(ino->vol, store, ino->record_count);
if (error)
return error;
size = ino->vol->mft_record_size;
count = i = 0;
do {
if (count < ino->record_count) {
recno = ino->records[count];
} else {
error = allocate_store(ino->vol, store, count + 1);
if (error)
return error;
recno = -1;
}
/*
* FIXME: We need to support extension records properly.
* At the moment they wouldn't work. Probably would "just" get
* corrupted if we write to them... (AIA)
*/
store->records[count].recno = recno;
rec = store->records[count].record;
count++;
/* Copy mft record header. */
offset = NTFS_GETU16(ino->attr + 0x14); /* attrs_offset */
ntfs_memcpy(rec, ino->attr, offset);
/* Copy attributes. */
while (i < ino->attr_count) {
attr = ino->attrs + i;
error = layout_attr(attr, rec + offset,
size - offset - 8, &psize);
if (error == -E2BIG && offset != NTFS_GETU16(ino->attr
+ 0x14))
break;
if (error)
return error;
offset += psize;
i++;
}
/* Terminating attribute. */
NTFS_PUTU32(rec + offset, 0xFFFFFFFF);
offset += 4;
NTFS_PUTU32(rec + offset, 0);
offset += 4;
NTFS_PUTU32(rec + 0x18, offset);
} while (i < ino->attr_count || count < ino->record_count);
return count - ino->record_count;
}
 
/*
* FIXME: ntfs_update_inode() calls layout_inode() to create the mft record on
* disk structure corresponding to the inode @ino. After that, ntfs_write_attr()
* is called to write out the created mft record to disk.
* We shouldn't need to re-layout every single time we are updating an mft
* record. No wonder the ntfs driver is slow like hell. (AIA)
*/
int ntfs_update_inode(ntfs_inode *ino)
{
int error, i;
ntfs_disk_inode store;
ntfs_io io;
 
ntfs_bzero(&store, sizeof(store));
error = layout_inode(ino, &store);
if (error == -E2BIG) {
i = ntfs_split_indexroot(ino);
if (i != -ENOTDIR) {
if (!i)
i = layout_inode(ino, &store);
error = i;
}
}
if (error == -E2BIG) {
error = ntfs_attr_allnonresident(ino);
if (!error)
error = layout_inode(ino, &store);
}
if (error > 0) {
/* FIXME: Introduce extension records. */
error = -E2BIG;
}
if (error) {
if (error == -E2BIG)
ntfs_error("Cannot handle saving inode 0x%x.\n",
ino->i_number);
deallocate_store(&store);
return error;
}
io.fn_get = ntfs_get;
io.fn_put = 0;
for (i = 0; i < store.count; i++) {
error = ntfs_insert_fixups(store.records[i].record,
ino->vol->mft_record_size);
if (error) {
printk(KERN_ALERT "NTFS: ntfs_update_inode() caught "
"corrupt %s mtf record ntfs record "
"header. Refusing to write corrupt "
"data to disk. Unmount and run chkdsk "
"immediately!\n", i ? "extension":
"base");
deallocate_store(&store);
return -EIO;
}
io.param = store.records[i].record;
io.size = ino->vol->mft_record_size;
error = ntfs_write_attr(ino->vol->mft_ino, ino->vol->at_data,
0, (__s64)store.records[i].recno <<
ino->vol->mft_record_size_bits, &io);
if (error || io.size != ino->vol->mft_record_size) {
/* Big trouble, partially written file. */
ntfs_error("Please unmount: Write error in inode "
"0x%x\n", ino->i_number);
deallocate_store(&store);
return error ? error : -EIO;
}
}
deallocate_store(&store);
return 0;
}
 
void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l)
{
int head, comp;
int copied = 0;
unsigned char *stop;
int bits;
int tag = 0;
int clear_pos;
while (1) {
head = NTFS_GETU16(src) & 0xFFF;
/* High bit indicates that compression was performed. */
comp = NTFS_GETU16(src) & 0x8000;
src += 2;
stop = src + head;
bits = 0;
clear_pos = 0;
if (head == 0)
/* Block is not used. */
return;/* FIXME: copied */
if (!comp) { /* uncompressible */
ntfs_memcpy(dest, src, 0x1000);
dest += 0x1000;
copied += 0x1000;
src += 0x1000;
if (l == copied)
return;
continue;
}
while (src <= stop) {
if (clear_pos > 4096) {
ntfs_error("Error 1 in decompress\n");
return;
}
if (!bits) {
tag = NTFS_GETU8(src);
bits = 8;
src++;
if (src > stop)
break;
}
if (tag & 1) {
int i, len, delta, code, lmask, dshift;
code = NTFS_GETU16(src);
src += 2;
if (!clear_pos) {
ntfs_error("Error 2 in decompress\n");
return;
}
for (i = clear_pos - 1, lmask = 0xFFF,
dshift = 12; i >= 0x10; i >>= 1) {
lmask >>= 1;
dshift--;
}
delta = code >> dshift;
len = (code & lmask) + 3;
for (i = 0; i < len; i++) {
dest[clear_pos] = dest[clear_pos -
delta - 1];
clear_pos++;
copied++;
if (copied==l)
return;
}
} else {
dest[clear_pos++] = NTFS_GETU8(src);
src++;
copied++;
if (copied==l)
return;
}
tag >>= 1;
bits--;
}
dest += clear_pos;
}
}
 
/*
* NOTE: Neither of the ntfs_*_bit functions are atomic! But we don't need
* them atomic at present as we never operate on shared/cached bitmaps.
*/
static __inline__ int ntfs_test_bit(unsigned char *byte, const int bit)
{
return byte[bit >> 3] & (1 << (bit & 7)) ? 1 : 0;
}
 
static __inline__ void ntfs_set_bit(unsigned char *byte, const int bit)
{
byte[bit >> 3] |= 1 << (bit & 7);
}
 
static __inline__ void ntfs_clear_bit(unsigned char *byte, const int bit)
{
byte[bit >> 3] &= ~(1 << (bit & 7));
}
 
static __inline__ int ntfs_test_and_clear_bit(unsigned char *byte,
const int bit)
{
unsigned char *ptr = byte + (bit >> 3);
int b = 1 << (bit & 7);
int oldbit = *ptr & b ? 1 : 0;
*ptr &= ~b;
return oldbit;
}
 
static void dump_runlist(const ntfs_runlist *rl, const int rlen)
{
#ifdef DEBUG
int i;
ntfs_cluster_t ct;
 
ntfs_debug(DEBUG_OTHER, "%s(): rlen = %i.\n", __FUNCTION__, rlen);
ntfs_debug(DEBUG_OTHER, "VCN LCN Run length\n");
for (i = 0, ct = 0; i < rlen; ct += rl[i++].len) {
if (rl[i].lcn == (ntfs_cluster_t)-1)
ntfs_debug(DEBUG_OTHER, "0x%-8x LCN_HOLE 0x%-8x "
"(%s)\n", ct, rl[i].len, rl[i].len ?
"sparse run" : "run list end");
else
ntfs_debug(DEBUG_OTHER, "0x%-8x 0x%-8x 0x%-8x%s\n", ct,
rl[i].lcn, rl[i].len, rl[i].len &&
i + 1 < rlen ? "" : " (run list end)");
if (!rl[i].len)
break;
}
#endif
}
 
/**
* splice_runlists - splice two run lists into one
* @rl1: pointer to address of first run list
* @r1len: number of elementfs in first run list
* @rl2: pointer to second run list
* @r2len: number of elements in second run list
*
* Append the run list @rl2 to the run list *@rl1 and return the result in
* *@rl1 and *@r1len.
*
* Return 0 on success or -errno on error, in which case *@rl1 and *@r1len are
* left untouched.
*
* The only possible error code at the moment is -ENOMEM and only happens if
* there is insufficient memory to allocate the new run list (only happens
* when size of (rl1 + rl2) > allocated size of rl1).
*/
int splice_runlists(ntfs_runlist **rl1, int *r1len, const ntfs_runlist *rl2,
int r2len)
{
ntfs_runlist *rl;
int rlen, rl_size, rl2_pos;
 
ntfs_debug(DEBUG_OTHER, "%s(): Entering with *r1len = %i, "
"r2len = %i.\n", __FUNCTION__, *r1len, r2len);
ntfs_debug(DEBUG_OTHER, "%s(): Dumping 1st runlist.\n", __FUNCTION__);
if (*rl1)
dump_runlist(*rl1, *r1len);
else
ntfs_debug(DEBUG_OTHER, "%s(): Not present.\n", __FUNCTION__);
ntfs_debug(DEBUG_OTHER, "%s(): Dumping 2nd runlist.\n", __FUNCTION__);
dump_runlist(rl2, r2len);
rlen = *r1len + r2len + 1;
rl_size = (rlen * sizeof(ntfs_runlist) + PAGE_SIZE - 1) &
PAGE_MASK;
ntfs_debug(DEBUG_OTHER, "%s(): rlen = %i, rl_size = %i.\n",
__FUNCTION__, rlen, rl_size);
/* Do we have enough space? */
if (rl_size <= ((*r1len * sizeof(ntfs_runlist) + PAGE_SIZE - 1) &
PAGE_MASK)) {
/* Have enough space already. */
rl = *rl1;
ntfs_debug(DEBUG_OTHER, "%s(): Have enough space already.\n",
__FUNCTION__);
} else {
/* Need more space. Reallocate. */
ntfs_debug(DEBUG_OTHER, "%s(): Need more space.\n",
__FUNCTION__);
rl = ntfs_vmalloc(rlen << sizeof(ntfs_runlist));
if (!rl)
return -ENOMEM;
/* Copy over rl1. */
ntfs_memcpy(rl, *rl1, *r1len * sizeof(ntfs_runlist));
ntfs_vfree(*rl1);
*rl1 = rl;
}
/* Reuse rl_size as the current position index into rl. */
rl_size = *r1len - 1;
ntfs_debug(DEBUG_OTHER, "%s(): rl_size = %i.\n", __FUNCTION__,rl_size);
/* Coalesce neighbouring elements, if present. */
rl2_pos = 0;
if (rl[rl_size].lcn + rl[rl_size].len == rl2[rl2_pos].lcn) {
ntfs_debug(DEBUG_OTHER, "%s(): Coalescing adjacent runs.\n",
__FUNCTION__);
ntfs_debug(DEBUG_OTHER, "%s(): Before: rl[rl_size].len = %i.\n",
__FUNCTION__, rl[rl_size].len);
rl[rl_size].len += rl2[rl2_pos].len;
ntfs_debug(DEBUG_OTHER, "%s(): After: rl[rl_size].len = %i.\n",
__FUNCTION__, rl[rl_size].len);
rl2_pos++;
r2len--;
rlen--;
}
rl_size++;
/* Copy over rl2. */
ntfs_memcpy(rl + rl_size, rl2 + rl2_pos, r2len * sizeof(ntfs_runlist));
rlen--;
rl[rlen].lcn = (ntfs_cluster_t)-1;
rl[rlen].len = (ntfs_cluster_t)0;
*r1len = rlen;
ntfs_debug(DEBUG_OTHER, "%s(): Dumping result runlist.\n",
__FUNCTION__);
dump_runlist(*rl1, *r1len);
ntfs_debug(DEBUG_OTHER, "%s(): Returning with *r1len = %i.\n",
__FUNCTION__, rlen);
return 0;
}
 
/**
* ntfs_alloc_mft_record - allocate an mft record
* @vol: volume to allocate an mft record on
* @result: the mft record number allocated
*
* Allocate a new mft record on disk. Return 0 on success or -ERRNO on error.
* On success, *@result contains the allocated mft record number. On error,
* *@result is -1UL.
*
* Note, this function doesn't actually set the mft record to be in use. This
* is done by the caller, which at the moment is only ntfs_alloc_inode().
*
* To find a free mft record, we scan the mft bitmap for a zero bit. To
* optimize this we start scanning at the place where we last stopped and we
* perform wrap around when we reach the end. Note, we do not try to allocate
* mft records below number 24 because numbers 0 to 15 are the defined system
* files anyway and 16 to 24 are special in that they are used for storing
* extension mft records for $MFT's $DATA attribute. This is required to avoid
* the possibility of creating a run list with a circular dependence which once
* written to disk can never be read in again. Windows will only use records
* 16 to 24 for normal files if the volume is completely out of space. We never
* use them which means that when the volume is really out of space we cannot
* create any more files while Windows can still create up to 8 small files. We
* can start doing this at some later time, doesn't matter much for now.
*
* When scanning the mft bitmap, we only search up to the last allocated mft
* record. If there are no free records left in the range 24 to number of
* allocated mft records, then we extend the mft data in order to create free
* mft records. We extend the allocated size of $MFT/$DATA by 16 records at a
* time or one cluster, if cluster size is above 16kiB. If there isn't
* sufficient space to do this, we try to extend by a single mft record or one
* cluster, if cluster size is above mft record size, but we only do this if
* there is enough free space, which we know from the values returned by the
* failed cluster allocation function when we tried to do the first allocation.
*
* No matter how many mft records we allocate, we initialize only the first
* allocated mft record (incrementing mft data size and initialized size) and
* return its number to the caller in @*result, unless there are less than 24
* mft records, in which case we allocate and initialize mft records until we
* reach record 24 which we consider as the first free mft record for use by
* normal files.
*
* If during any stage we overflow the initialized data in the mft bitmap, we
* extend the initialized size (and data size) by 8 bytes, allocating another
* cluster if required. The bitmap data size has to be at least equal to the
* number of mft records in the mft, but it can be bigger, in which case the
* superflous bits are padded with zeroes.
*
* Thus, when we return successfully (return value 0), we will have:
* - initialized / extended the mft bitmap if necessary,
* - initialized / extended the mft data if necessary,
* - set the bit corresponding to the mft record being allocated in the
* mft bitmap, and we will
* - return the mft record number in @*result.
*
* On error (return value below zero), nothing will have changed. If we had
* changed anything before the error occured, we will have reverted back to
* the starting state before returning to the caller. Thus, except for bugs,
* we should always leave the volume in a consitents state when returning from
* this function. NOTE: Small exception to this is that we set the bit in the
* mft bitmap but we do not mark the mft record in use, which is inconsistent.
* However, the caller will immediately add the wanted attributes to the mft
* record, set it in use and write it out to disk, so there should be no
* problem.
*
* Note, this function cannot make use of most of the normal functions, like
* for example for attribute resizing, etc, because when the run list overflows
* the base mft record and an attribute list is used, it is very important
* that the extension mft records used to store the $DATA attribute of $MFT
* can be reached without having to read the information contained inside
* them, as this would make it impossible to find them in the first place
* after the volume is dismounted. $MFT/$BITMAP probably doesn't need to
* follow this rule because the bitmap is not essential for finding the mft
* records, but on the other hand, handling the bitmap in this special way
* would make life easier because otherwise there might be circular invocations
* of functions when reading the bitmap but if we are careful, we should be
* able to avoid all problems.
*
* FIXME: Don't forget $MftMirr, though this probably belongs in
* ntfs_update_inode() (or even deeper). (AIA)
*
* FIXME: Want finer grained locking. (AIA)
*/
static int ntfs_alloc_mft_record(ntfs_volume *vol, unsigned long *result)
{
unsigned long nr_mft_records, buf_size, buf_pos, pass_start, pass_end;
unsigned long last_read_pos, mft_rec_size, bit, l;
ntfs_attribute *data, *bmp;
__u8 *buf, *byte, pass, b, have_allocated_mftbmp = 0;
int rlen, rl_size = 0, r2len, rl2_size, old_data_rlen, err = 0;
ntfs_runlist *rl, *rl2;
ntfs_cluster_t lcn = 0, old_data_len;
ntfs_io io;
__s64 ll, old_data_allocated, old_data_initialized, old_data_size;
 
*result = -1UL;
/* Allocate a buffer and setup the io structure. */
buf = (__u8*)__get_free_page(GFP_NOFS);
if (!buf)
return -ENOMEM;
lock_kernel();
/* Get the $DATA and $BITMAP attributes of $MFT. */
data = ntfs_find_attr(vol->mft_ino, vol->at_data, 0);
bmp = ntfs_find_attr(vol->mft_ino, vol->at_bitmap, 0);
if (!data || !bmp) {
err = -EINVAL;
goto err_ret;
}
/* Determine the number of allocated mft records in the mft. */
pass_end = nr_mft_records = data->allocated >>
vol->mft_record_size_bits;
ntfs_debug(DEBUG_OTHER, "%s(): nr_mft_records = %lu.\n", __FUNCTION__,
nr_mft_records);
/* Make sure we don't overflow the bitmap. */
l = bmp->initialized << 3;
if (l < nr_mft_records)
// FIXME: It might be a good idea to extend the bitmap instead.
pass_end = l;
pass = 1;
buf_pos = vol->mft_data_pos;
if (buf_pos >= pass_end) {
buf_pos = 24UL;
pass = 2;
}
pass_start = buf_pos;
rl = bmp->d.r.runlist;
rlen = bmp->d.r.len - 1;
lcn = rl[rlen].lcn + rl[rlen].len;
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
ntfs_debug(DEBUG_OTHER, "%s(): Starting bitmap search.\n",
__FUNCTION__);
ntfs_debug(DEBUG_OTHER, "%s(): pass = %i, pass_start = %lu, pass_end = "
"%lu.\n", __FUNCTION__, pass, pass_start, pass_end);
byte = NULL; // FIXME: For debugging only.
/* Loop until a free mft record is found. */
io.size = (nr_mft_records >> 3) & ~PAGE_MASK;
for (;; io.size = PAGE_SIZE) {
io.param = buf;
io.do_read = 1;
last_read_pos = buf_pos >> 3;
ntfs_debug(DEBUG_OTHER, "%s(): Before: bmp->allocated = 0x%Lx, "
"bmp->size = 0x%Lx, bmp->initialized = "
"0x%Lx.\n", __FUNCTION__, bmp->allocated,
bmp->size, bmp->initialized);
err = ntfs_readwrite_attr(vol->mft_ino, bmp, last_read_pos,
&io);
if (err)
goto err_ret;
ntfs_debug(DEBUG_OTHER, "%s(): Read %lu bytes.\n", __FUNCTION__,
(unsigned long)io.size);
ntfs_debug(DEBUG_OTHER, "%s(): After: bmp->allocated = 0x%Lx, "
"bmp->size = 0x%Lx, bmp->initialized = "
"0x%Lx.\n", __FUNCTION__, bmp->allocated,
bmp->size, bmp->initialized);
if (!io.size)
goto pass_done;
buf_size = io.size << 3;
bit = buf_pos & 7UL;
buf_pos &= ~7UL;
ntfs_debug(DEBUG_OTHER, "%s(): Before loop: buf_size = %lu, "
"buf_pos = %lu, bit = %lu, *byte = 0x%x, b = "
"%u.\n", __FUNCTION__, buf_size, buf_pos, bit,
byte ? *byte : -1, b);
for (; bit < buf_size && bit + buf_pos < pass_end;
bit &= ~7UL, bit += 8UL) {
byte = buf + (bit >> 3);
if (*byte == 0xff)
continue;
b = ffz((unsigned long)*byte);
if (b < (__u8)8 && b >= (bit & 7UL)) {
bit = b + (bit & ~7UL) + buf_pos;
ntfs_debug(DEBUG_OTHER, "%s(): Found free rec "
"in for loop. bit = %lu\n",
__FUNCTION__, bit);
goto found_free_rec;
}
}
ntfs_debug(DEBUG_OTHER, "%s(): After loop: buf_size = %lu, "
"buf_pos = %lu, bit = %lu, *byte = 0x%x, b = "
"%u.\n", __FUNCTION__, buf_size, buf_pos, bit,
byte ? *byte : -1, b);
buf_pos += buf_size;
if (buf_pos < pass_end)
continue;
pass_done: /* Finished with the current pass. */
ntfs_debug(DEBUG_OTHER, "%s(): At pass_done.\n", __FUNCTION__);
if (pass == 1) {
/*
* Now do pass 2, scanning the first part of the zone
* we omitted in pass 1.
*/
ntfs_debug(DEBUG_OTHER, "%s(): Done pass 1.\n",
__FUNCTION__);
ntfs_debug(DEBUG_OTHER, "%s(): Pass = 2.\n",
__FUNCTION__);
pass = 2;
pass_end = pass_start;
buf_pos = pass_start = 24UL;
ntfs_debug(DEBUG_OTHER, "%s(): pass = %i, pass_start = "
"%lu, pass_end = %lu.\n", __FUNCTION__,
pass, pass_start, pass_end);
continue;
} /* pass == 2 */
/* No free records left. */
if (bmp->initialized << 3 > nr_mft_records &&
bmp->initialized > 3) {
/*
* The mft bitmap is already bigger but the space is
* not covered by mft records, this implies that the
* next records are all free, so we already have found
* a free record.
*/
bit = nr_mft_records;
if (bit < 24UL)
bit = 24UL;
ntfs_debug(DEBUG_OTHER, "%s(): Found free record bit "
"(#1) = 0x%lx.\n", __FUNCTION__, bit);
goto found_free_rec;
}
ntfs_debug(DEBUG_OTHER, "%s(): Done pass 2.\n", __FUNCTION__);
ntfs_debug(DEBUG_OTHER, "%s(): Before: bmp->allocated = 0x%Lx, "
"bmp->size = 0x%Lx, bmp->initialized = "
"0x%Lx.\n", __FUNCTION__, bmp->allocated,
bmp->size, bmp->initialized);
/* Need to extend the mft bitmap. */
if (bmp->initialized + 8LL > bmp->allocated) {
ntfs_io io2;
 
ntfs_debug(DEBUG_OTHER, "%s(): Initialized "
"> allocated.\n", __FUNCTION__);
/* Need to extend bitmap by one more cluster. */
rl = bmp->d.r.runlist;
rlen = bmp->d.r.len - 1;
lcn = rl[rlen].lcn + rl[rlen].len;
io2.fn_put = ntfs_put;
io2.fn_get = ntfs_get;
io2.param = &b;
io2.size = 1;
io2.do_read = 1;
err = ntfs_readwrite_attr(vol->bitmap, data, lcn >> 3,
&io2);
if (err)
goto err_ret;
ntfs_debug(DEBUG_OTHER, "%s(): Read %lu bytes.\n",
__FUNCTION__, (unsigned long)io2.size);
if (io2.size == 1 && b != 0xff) {
__u8 tb = 1 << (lcn & (ntfs_cluster_t)7);
if (!(b & tb)) {
/* Next cluster is free. Allocate it. */
b |= tb;
io2.param = &b;
io2.do_read = 0;
err = ntfs_readwrite_attr(vol->bitmap,
data, lcn >> 3, &io2);
if (err || io.size != 1) {
if (!err)
err = -EIO;
goto err_ret;
}
append_mftbmp_simple: rl[rlen].len++;
have_allocated_mftbmp |= 1;
ntfs_debug(DEBUG_OTHER, "%s(): "
"Appending one cluster "
"to mftbmp.\n",
__FUNCTION__);
}
}
if (!have_allocated_mftbmp) {
/* Allocate a cluster from the DATA_ZONE. */
ntfs_cluster_t lcn2 = lcn;
ntfs_cluster_t count = 1;
err = ntfs_allocate_clusters(vol, &lcn2,
&count, &rl2, &r2len,
DATA_ZONE);
if (err)
goto err_ret;
if (count != 1 || lcn2 <= 0) {
if (count > 0) {
rl2_dealloc_err_out: if (ntfs_deallocate_clusters(
vol, rl2, r2len))
ntfs_error("%s(): "
"Cluster "
"deallocation in error "
"code path failed! You "
"should run chkdsk.\n",
__FUNCTION__);
}
ntfs_vfree(rl2);
if (!err)
err = -EINVAL;
goto err_ret;
}
if (lcn2 == lcn) {
ntfs_vfree(rl2);
goto append_mftbmp_simple;
}
/* We need to append a new run. */
rl_size = (rlen * sizeof(ntfs_runlist) +
PAGE_SIZE - 1) & PAGE_MASK;
/* Reallocate memory if necessary. */
if ((rlen + 2) * sizeof(ntfs_runlist) >=
rl_size) {
ntfs_runlist *rlt;
 
rl_size += PAGE_SIZE;
rlt = ntfs_vmalloc(rl_size);
if (!rlt) {
err = -ENOMEM;
goto rl2_dealloc_err_out;
}
ntfs_memcpy(rlt, rl, rl_size -
PAGE_SIZE);
ntfs_vfree(rl);
bmp->d.r.runlist = rl = rlt;
}
ntfs_vfree(rl2);
rl[rlen].lcn = lcn = lcn2;
rl[rlen].len = count;
bmp->d.r.len = ++rlen;
have_allocated_mftbmp |= 2;
ntfs_debug(DEBUG_OTHER, "%s(): Adding run to "
"mftbmp. LCN = %i, len = %i\n",
__FUNCTION__, lcn, count);
}
/*
* We now have extended the mft bitmap allocated size
* by one cluster. Reflect this in the attribute.
*/
bmp->allocated += (__s64)vol->cluster_size;
}
ntfs_debug(DEBUG_OTHER, "%s(): After: bmp->allocated = 0x%Lx, "
"bmp->size = 0x%Lx, bmp->initialized = "
"0x%Lx.\n", __FUNCTION__, bmp->allocated,
bmp->size, bmp->initialized);
/* We now have sufficient allocated space. */
ntfs_debug(DEBUG_OTHER, "%s(): Now have sufficient allocated "
"space in mftbmp.\n", __FUNCTION__);
ntfs_debug(DEBUG_OTHER, "%s(): Before: bmp->allocated = 0x%Lx, "
"bmp->size = 0x%Lx, bmp->initialized = "
"0x%Lx.\n", __FUNCTION__, bmp->allocated,
bmp->size, bmp->initialized);
buf_pos = bmp->initialized;
bmp->initialized += 8LL;
if (bmp->initialized > bmp->size)
bmp->size = bmp->initialized;
ntfs_debug(DEBUG_OTHER, "%s(): After: bmp->allocated = 0x%Lx, "
"bmp->size = 0x%Lx, bmp->initialized = "
"0x%Lx.\n", __FUNCTION__, bmp->allocated,
bmp->size, bmp->initialized);
have_allocated_mftbmp |= 4;
/* Update the mft bitmap attribute value. */
memset(buf, 0, 8);
io.param = buf;
io.size = 8;
io.do_read = 0;
err = ntfs_readwrite_attr(vol->mft_ino, bmp, buf_pos, &io);
if (err || io.size != 8) {
if (!err)
err = -EIO;
goto shrink_mftbmp_err_ret;
}
ntfs_debug(DEBUG_OTHER, "%s(): Wrote extended mftbmp bytes "
"%lu.\n", __FUNCTION__, (unsigned long)io.size);
ntfs_debug(DEBUG_OTHER, "%s(): After write: bmp->allocated = "
"0x%Lx, bmp->size = 0x%Lx, bmp->initialized = "
"0x%Lx.\n", __FUNCTION__, bmp->allocated,
bmp->size, bmp->initialized);
bit = buf_pos << 3;
ntfs_debug(DEBUG_OTHER, "%s(): Found free record bit (#2) = "
"0x%lx.\n", __FUNCTION__, bit);
goto found_free_rec;
}
found_free_rec:
/* bit is the found free mft record. Allocate it in the mft bitmap. */
vol->mft_data_pos = bit;
ntfs_debug(DEBUG_OTHER, "%s(): At found_free_rec.\n", __FUNCTION__);
io.param = buf;
io.size = 1;
io.do_read = 1;
ntfs_debug(DEBUG_OTHER, "%s(): Before update: bmp->allocated = 0x%Lx, "
"bmp->size = 0x%Lx, bmp->initialized = 0x%Lx.\n",
__FUNCTION__, bmp->allocated,
bmp->size, bmp->initialized);
err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io);
if (err || io.size != 1) {
if (!err)
err = -EIO;
goto shrink_mftbmp_err_ret;
}
ntfs_debug(DEBUG_OTHER, "%s(): Read %lu bytes.\n", __FUNCTION__,
(unsigned long)io.size);
#ifdef DEBUG
/* Check our bit is really zero! */
if (*buf & (1 << (bit & 7)))
BUG();
#endif
*buf |= 1 << (bit & 7);
io.param = buf;
io.do_read = 0;
err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io);
if (err || io.size != 1) {
if (!err)
err = -EIO;
goto shrink_mftbmp_err_ret;
}
ntfs_debug(DEBUG_OTHER, "%s(): Wrote %lu bytes.\n", __FUNCTION__,
(unsigned long)io.size);
ntfs_debug(DEBUG_OTHER, "%s(): After update: bmp->allocated = 0x%Lx, "
"bmp->size = 0x%Lx, bmp->initialized = 0x%Lx.\n",
__FUNCTION__, bmp->allocated,
bmp->size, bmp->initialized);
/* The mft bitmap is now uptodate. Deal with mft data attribute now. */
ll = (__s64)(bit + 1) << vol->mft_record_size_bits;
if (ll <= data->initialized) {
/* The allocated record is already initialized. We are done! */
ntfs_debug(DEBUG_OTHER, "%s(): Allocated mft record "
"already initialized!\n", __FUNCTION__);
goto done_ret;
}
ntfs_debug(DEBUG_OTHER, "%s(): Allocated mft record needs "
"to be initialized.\n", __FUNCTION__);
/* The mft record is outside the initialized data. */
mft_rec_size = (unsigned long)vol->mft_record_size;
/* Preserve old values for undo purposes. */
old_data_allocated = data->allocated;
old_data_rlen = data->d.r.len - 1;
old_data_len = data->d.r.runlist[old_data_rlen].len;
/*
* If necessary, extend the mft until it covers the allocated record.
* The loop is only actually used when a freshly formatted volume is
* first written to. But it optimizes away nicely in the common case.
*/
while (ll > data->allocated) {
ntfs_cluster_t lcn2, nr_lcn2, nr, min_nr;
 
ntfs_debug(DEBUG_OTHER, "%s(): Extending mft data allocation, "
"data->allocated = 0x%Lx, data->size = 0x%Lx, "
"data->initialized = 0x%Lx.\n", __FUNCTION__,
data->allocated, data->size, data->initialized);
/* Minimum allocation is one mft record worth of clusters. */
if (mft_rec_size <= vol->cluster_size)
min_nr = (ntfs_cluster_t)1;
else
min_nr = mft_rec_size >> vol->cluster_size_bits;
ntfs_debug(DEBUG_OTHER, "%s(): min_nr = %i.\n", __FUNCTION__,
min_nr);
/* Allocate 16 mft records worth of clusters. */
nr = mft_rec_size << 4 >> vol->cluster_size_bits;
if (!nr)
nr = (ntfs_cluster_t)1;
/* Determine the preferred allocation location. */
ntfs_debug(DEBUG_OTHER, "%s(): nr = %i.\n", __FUNCTION__, nr);
rl2 = data->d.r.runlist;
r2len = data->d.r.len;
lcn2 = rl2[r2len - 1].lcn + rl2[r2len - 1].len;
ntfs_debug(DEBUG_OTHER, "%s(): rl2[r2len - 1].lcn = %i, .len = "
"%i.\n", __FUNCTION__, rl2[r2len - 1].lcn,
rl2[r2len - 1].len);
ntfs_debug(DEBUG_OTHER, "%s(): lcn2 = %i, r2len = %i.\n",
__FUNCTION__, lcn2, r2len);
retry_mft_data_allocation:
nr_lcn2 = nr;
err = ntfs_allocate_clusters(vol, &lcn2, &nr_lcn2, &rl2,
&r2len, MFT_ZONE);
#ifdef DEBUG
if (!err && nr_lcn2 < min_nr)
/* Allocated less than minimum needed. Weird! */
BUG();
#endif
if (err) {
/*
* If there isn't enough space to do the wanted
* allocation, but there is enough space to do a
* minimal allocation, then try that, unless the wanted
* allocation was already the minimal allocation.
*/
if (err == -ENOSPC && nr > min_nr &&
nr_lcn2 >= min_nr) {
nr = min_nr;
ntfs_debug(DEBUG_OTHER, "%s(): Retrying mft "
"data allocation, nr = min_nr "
"= %i.\n", __FUNCTION__, nr);
goto retry_mft_data_allocation;
}
goto undo_mftbmp_alloc_err_ret;
}
ntfs_debug(DEBUG_OTHER, "%s(): Allocated %i clusters starting "
"at LCN %i.\n", __FUNCTION__, nr_lcn2, lcn2);
ntfs_debug(DEBUG_OTHER, "%s(): Allocated runlist:\n",
__FUNCTION__);
dump_runlist(rl2, r2len);
/* Append rl2 to the mft data attribute's run list. */
err = splice_runlists(&data->d.r.runlist, (int*)&data->d.r.len,
rl2, r2len);
if (err) {
ntfs_debug(DEBUG_OTHER, "%s(): splice_runlists failed "
"with error code %i.\n", __FUNCTION__,
-err);
goto undo_partial_data_alloc_err_ret;
}
/* Reflect the allocated clusters in the mft allocated data. */
data->allocated += nr_lcn2 << vol->cluster_size_bits;
ntfs_debug(DEBUG_OTHER, "%s(): After extending mft data "
"allocation, data->allocated = 0x%Lx, "
"data->size = 0x%Lx, data->initialized = "
"0x%Lx.\n", __FUNCTION__, data->allocated,
data->size, data->initialized);
}
/* Prepare a formatted (empty) mft record. */
memset(buf, 0, mft_rec_size);
ntfs_fill_mft_header(buf, mft_rec_size, 0, 0, 0);
err = ntfs_insert_fixups(buf, mft_rec_size);
if (err)
goto undo_data_alloc_err_ret;
/*
* Extend mft data initialized size to reach the allocated mft record
* and write the formatted mft record buffer to each mft record being
* initialized. Note, that ntfs_readwrite_attr extends both
* data->initialized and data->size, so no need for us to touch them.
*/
old_data_initialized = data->initialized;
old_data_size = data->size;
while (ll > data->initialized) {
ntfs_debug(DEBUG_OTHER, "%s(): Initializing mft record "
"0x%Lx.\n", __FUNCTION__,
data->initialized >> vol->mft_record_size_bits);
io.param = buf;
io.size = mft_rec_size;
io.do_read = 0;
err = ntfs_readwrite_attr(vol->mft_ino, data,
data->initialized, &io);
if (err || io.size != mft_rec_size) {
if (!err)
err = -EIO;
goto undo_data_init_err_ret;
}
ntfs_debug(DEBUG_OTHER, "%s(): Wrote %i bytes to mft data.\n",
__FUNCTION__, io.size);
}
/* Update the VFS inode size as well. */
VFS_I(vol->mft_ino)->i_size = data->size;
#ifdef DEBUG
ntfs_debug(DEBUG_OTHER, "%s(): After mft record "
"initialization: data->allocated = 0x%Lx, data->size "
"= 0x%Lx, data->initialized = 0x%Lx.\n", __FUNCTION__,
data->allocated, data->size, data->initialized);
/* Sanity checks. */
if (data->size > data->allocated || data->size < data->initialized ||
data->initialized > data->allocated)
BUG();
#endif
done_ret:
/* Return the number of the allocated mft record. */
ntfs_debug(DEBUG_OTHER, "%s(): At done_ret. *result = bit = 0x%lx.\n",
__FUNCTION__, bit);
*result = bit;
vol->mft_data_pos = bit + 1;
err_ret:
unlock_kernel();
free_page((unsigned long)buf);
ntfs_debug(DEBUG_OTHER, "%s(): Syncing inode $MFT.\n", __FUNCTION__);
if (ntfs_update_inode(vol->mft_ino))
ntfs_error("%s(): Failed to sync inode $MFT. "
"Continuing anyway.\n",__FUNCTION__);
if (!err) {
ntfs_debug(DEBUG_FILE3, "%s(): Done. Allocated mft record "
"number *result = 0x%lx.\n", __FUNCTION__,
*result);
return 0;
}
if (err != -ENOSPC)
ntfs_error("%s(): Failed to allocate an mft record. Returning "
"error code %i.\n", __FUNCTION__, -err);
else
ntfs_debug(DEBUG_FILE3, "%s(): Failed to allocate an mft "
"record due to lack of free space.\n",
__FUNCTION__);
return err;
undo_data_init_err_ret:
ntfs_debug(DEBUG_OTHER, "%s(): At undo_data_init_err_ret.\n",
__FUNCTION__);
data->initialized = old_data_initialized;
data->size = old_data_size;
undo_data_alloc_err_ret:
ntfs_debug(DEBUG_OTHER, "%s(): At undo_data_alloc_err_ret.\n",
__FUNCTION__);
data->allocated = old_data_allocated;
undo_partial_data_alloc_err_ret:
ntfs_debug(DEBUG_OTHER, "%s(): At undo_partial_data_alloc_err_ret.\n",
__FUNCTION__);
/* Deallocate the clusters. */
if (ntfs_deallocate_clusters(vol, rl2, r2len))
ntfs_error("%s(): Error deallocating clusters in error code "
"path. You should run chkdsk.\n", __FUNCTION__);
ntfs_vfree(rl2);
/* Revert the run list back to what it was before. */
r2len = data->d.r.len;
rl2 = data->d.r.runlist;
rl2[old_data_rlen++].len = old_data_len;
rl2[old_data_rlen].lcn = (ntfs_cluster_t)-1;
rl2[old_data_rlen].len = (ntfs_cluster_t)0;
data->d.r.len = old_data_rlen;
rl2_size = ((old_data_rlen + 1) * sizeof(ntfs_runlist) + PAGE_SIZE -
1) & PAGE_MASK;
/* Reallocate memory freeing any extra memory allocated. */
if (rl2_size < ((r2len * sizeof(ntfs_runlist) + PAGE_SIZE - 1) &
PAGE_MASK)) {
rl2 = ntfs_vmalloc(rl2_size);
if (rl2) {
ntfs_memcpy(rl2, data->d.r.runlist, rl2_size);
ntfs_vfree(data->d.r.runlist);
data->d.r.runlist = rl2;
} else
ntfs_error("%s(): Error reallocating "
"memory in error code path. This "
"should be harmless.\n", __FUNCTION__);
}
undo_mftbmp_alloc_err_ret:
ntfs_debug(DEBUG_OTHER, "%s(): At undo_mftbmp_alloc_err_ret.\n",
__FUNCTION__);
/* Deallocate the allocated bit in the mft bitmap. */
io.param = buf;
io.size = 1;
io.do_read = 1;
err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io);
if (!err && io.size == 1) {
*buf &= ~(1 << (bit & 7));
io.param = buf;
io.do_read = 0;
err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io);
}
if (err || io.size != 1) {
if (!err)
err = -EIO;
ntfs_error("%s(): Error deallocating mft record in error code "
"path. You should run chkdsk.\n", __FUNCTION__);
}
shrink_mftbmp_err_ret:
ntfs_debug(DEBUG_OTHER, "%s(): At shrink_mftbmp_err_ret.\n",
__FUNCTION__);
ntfs_debug(DEBUG_OTHER, "%s(): have_allocated_mftbmp = %i.\n",
__FUNCTION__, have_allocated_mftbmp);
if (!have_allocated_mftbmp)
goto err_ret;
/* Shrink the mftbmp back to previous size. */
if (bmp->size == bmp->initialized)
bmp->size -= 8LL;
bmp->initialized -= 8LL;
have_allocated_mftbmp &= ~4;
/* If no allocation occured then we are done. */
ntfs_debug(DEBUG_OTHER, "%s(): have_allocated_mftbmp = %i.\n",
__FUNCTION__, have_allocated_mftbmp);
if (!have_allocated_mftbmp)
goto err_ret;
/* Deallocate the allocated cluster. */
bmp->allocated -= (__s64)vol->cluster_size;
if (ntfs_deallocate_cluster_run(vol, lcn, (ntfs_cluster_t)1))
ntfs_error("%s(): Error deallocating cluster in error code "
"path. You should run chkdsk.\n", __FUNCTION__);
switch (have_allocated_mftbmp & 3) {
case 1:
/* Delete the last lcn from the last run of mftbmp. */
rl[rlen - 1].len--;
break;
case 2:
/* Delete the last run of mftbmp. */
bmp->d.r.len = --rlen;
/* Reallocate memory if necessary. */
if ((rlen + 1) * sizeof(ntfs_runlist) <= rl_size - PAGE_SIZE) {
ntfs_runlist *rlt;
 
rl_size -= PAGE_SIZE;
rlt = ntfs_vmalloc(rl_size);
if (rlt) {
ntfs_memcpy(rlt, rl, rl_size);
ntfs_vfree(rl);
bmp->d.r.runlist = rl = rlt;
} else
ntfs_error("%s(): Error "
"reallocating memory in error "
"code path. This should be "
"harmless.\n", __FUNCTION__);
}
bmp->d.r.runlist[bmp->d.r.len].lcn = (ntfs_cluster_t)-1;
bmp->d.r.runlist[bmp->d.r.len].len = (ntfs_cluster_t)0;
break;
default:
BUG();
}
goto err_ret;
}
 
/* We need 0x48 bytes in total. */
static int add_standard_information(ntfs_inode *ino)
{
ntfs_time64_t now;
char data[0x30];
char *position = data;
ntfs_attribute *si;
 
now = ntfs_now();
NTFS_PUTU64(position + 0x00, now); /* File creation */
NTFS_PUTU64(position + 0x08, now); /* Last modification */
NTFS_PUTU64(position + 0x10, now); /* Last mod for MFT */
NTFS_PUTU64(position + 0x18, now); /* Last access */
NTFS_PUTU64(position + 0x20, 0); /* MSDOS file perms */
NTFS_PUTU64(position + 0x28, 0); /* unknown */
return ntfs_create_attr(ino, ino->vol->at_standard_information, 0,
data, sizeof(data), &si);
}
 
static int add_filename(ntfs_inode *ino, ntfs_inode *dir,
const unsigned char *filename, int length, ntfs_u32 flags)
{
unsigned char *position;
unsigned int size;
ntfs_time64_t now;
int count, error;
unsigned char* data;
ntfs_attribute *fn;
 
/* Work out the size. */
size = 0x42 + 2 * length;
data = ntfs_malloc(size);
if (!data)
return -ENOMEM;
/* Search for a position. */
position = data;
NTFS_PUTINUM(position, dir); /* Inode num of dir */
now = ntfs_now();
NTFS_PUTU64(position + 0x08, now); /* File creation */
NTFS_PUTU64(position + 0x10, now); /* Last modification */
NTFS_PUTU64(position + 0x18, now); /* Last mod for MFT */
NTFS_PUTU64(position + 0x20, now); /* Last access */
/* FIXME: Get the following two sizes by finding the data attribute
* in ino->attr and copying the corresponding fields from there.
* If no data present then set to zero. In current implementation
* add_data is called after add_filename so zero is correct on
* creation. Need to change when we have hard links / support different
* filename namespaces. (AIA) */
NTFS_PUTS64(position + 0x28, 0); /* Allocated size */
NTFS_PUTS64(position + 0x30, 0); /* Data size */
NTFS_PUTU32(position + 0x38, flags); /* File flags */
NTFS_PUTU32(position + 0x3c, 0); /* We don't use these
* features yet. */
NTFS_PUTU8(position + 0x40, length); /* Filename length */
NTFS_PUTU8(position + 0x41, 0); /* Only long name */
/* FIXME: This is madness. We are defining the POSIX namespace
* for the filename here which can mean that the file will be
* invisible when in Windows NT/2k! )-: (AIA) */
position += 0x42;
for (count = 0; count < length; count++) {
NTFS_PUTU16(position + 2 * count, filename[count]);
}
error = ntfs_create_attr(ino, ino->vol->at_file_name, 0, data, size,
&fn);
if (!error)
error = ntfs_dir_add(dir, ino, fn);
ntfs_free(data);
return error;
}
 
int add_security(ntfs_inode* ino, ntfs_inode* dir)
{
int error;
char *buf;
int size;
ntfs_attribute* attr;
ntfs_io io;
ntfs_attribute *se;
 
attr = ntfs_find_attr(dir, ino->vol->at_security_descriptor, 0);
if (!attr)
return -EOPNOTSUPP; /* Need security in directory. */
size = attr->size;
if (size > 512)
return -EOPNOTSUPP;
buf = ntfs_malloc(size);
if (!buf)
return -ENOMEM;
io.fn_get = ntfs_get;
io.fn_put = ntfs_put;
io.param = buf;
io.size = size;
error = ntfs_read_attr(dir, ino->vol->at_security_descriptor, 0, 0,&io);
if (!error && io.size != size)
ntfs_error("wrong size in add_security\n");
if (error) {
ntfs_free(buf);
return error;
}
/* FIXME: Consider ACL inheritance. */
error = ntfs_create_attr(ino, ino->vol->at_security_descriptor,
0, buf, size, &se);
ntfs_free(buf);
return error;
}
 
static int add_data(ntfs_inode* ino, unsigned char *data, int length)
{
ntfs_attribute *da;
return ntfs_create_attr(ino, ino->vol->at_data, 0, data, length, &da);
}
 
/*
* We _could_ use 'dir' to help optimise inode allocation.
*
* FIXME: Need to undo what we do in ntfs_alloc_mft_record if we get an error
* further on in ntfs_alloc_inode. Either fold the two functions to allow
* proper undo or just deallocate the record from the mft bitmap. (AIA)
*/
int ntfs_alloc_inode(ntfs_inode *dir, ntfs_inode *result, const char *filename,
int namelen, ntfs_u32 flags)
{
ntfs_volume *vol = dir->vol;
int err;
ntfs_u8 buffer[2];
ntfs_io io;
 
err = ntfs_alloc_mft_record(vol, &(result->i_number));
if (err) {
if (err == -ENOSPC)
ntfs_error("%s(): No free inodes.\n", __FUNCTION__);
return err;
}
/* Get the sequence number. */
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
io.param = buffer;
io.size = 2;
err = ntfs_read_attr(vol->mft_ino, vol->at_data, 0,
((__s64)result->i_number << vol->mft_record_size_bits)
+ 0x10, &io);
// FIXME: We are leaving the MFT in inconsistent state! (AIA)
if (err)
return err;
/* Increment the sequence number skipping zero. */
result->sequence_number = (NTFS_GETU16(buffer) + 1) & 0xffff;
if (!result->sequence_number)
result->sequence_number++;
result->vol = vol;
result->attr_count = 0;
result->attrs = 0;
result->record_count = 1;
result->records = ntfs_calloc(8 * sizeof(int));
if (!result->records)
goto mem_err_out;
result->records[0] = result->i_number;
result->attr = ntfs_calloc(vol->mft_record_size);
if (!result->attr) {
ntfs_free(result->records);
result->records = NULL;
goto mem_err_out;
}
ntfs_fill_mft_header(result->attr, vol->mft_record_size,
result->sequence_number, 1, 1);
err = add_standard_information(result);
if (!err)
err = add_filename(result, dir, filename, namelen, flags);
if (!err)
err = add_security(result, dir);
// FIXME: We are leaving the MFT in inconsistent state on error! (AIA)
return err;
mem_err_out:
// FIXME: We are leaving the MFT in inconsistent state! (AIA)
result->record_count = 0;
result->attr = NULL;
return -ENOMEM;
}
 
int ntfs_alloc_file(ntfs_inode *dir, ntfs_inode *result, char *filename,
int namelen)
{
int err;
 
err = ntfs_alloc_inode(dir, result, filename, namelen, 0);
if (!err)
err = add_data(result, 0, 0);
return err;
}
 
/ntfsendian.h
0,0 → 1,60
/*
* ntfsendian.h
*
 
* Copyright (C) 1998 Joseph Malicki
* Copyright (C) 1999 Werner Seiler
* Copyright (C) 2001 Anton Altaparmakov (AIA)
*/
#include <asm/byteorder.h>
 
#define CPU_TO_LE16(a) __cpu_to_le16(a)
#define CPU_TO_LE32(a) __cpu_to_le32(a)
#define CPU_TO_LE64(a) __cpu_to_le64(a)
 
#define LE16_TO_CPU(a) __cpu_to_le16(a)
#define LE32_TO_CPU(a) __cpu_to_le32(a)
#define LE64_TO_CPU(a) __cpu_to_le64(a)
 
#define NTFS_GETU8(p) (*(ntfs_u8*)(p))
#define NTFS_GETU16(p) ((ntfs_u16)LE16_TO_CPU(*(ntfs_u16*)(p)))
#define NTFS_GETU24(p) ((ntfs_u32)NTFS_GETU16(p) | \
((ntfs_u32)NTFS_GETU8(((char*)(p)) + 2) << 16))
#define NTFS_GETU32(p) ((ntfs_u32)LE32_TO_CPU(*(ntfs_u32*)(p)))
#define NTFS_GETU40(p) ((ntfs_u64)NTFS_GETU32(p) | \
(((ntfs_u64)NTFS_GETU8(((char*)(p)) + 4)) << 32))
#define NTFS_GETU48(p) ((ntfs_u64)NTFS_GETU32(p) | \
(((ntfs_u64)NTFS_GETU16(((char*)(p)) + 4)) << 32))
#define NTFS_GETU56(p) ((ntfs_u64)NTFS_GETU32(p) | \
(((ntfs_u64)NTFS_GETU24(((char*)(p)) + 4)) << 32))
#define NTFS_GETU64(p) ((ntfs_u64)LE64_TO_CPU(*(ntfs_u64*)(p)))
/* Macros writing unsigned integers */
#define NTFS_PUTU8(p,v) ((*(ntfs_u8*)(p)) = (v))
#define NTFS_PUTU16(p,v) ((*(ntfs_u16*)(p)) = CPU_TO_LE16(v))
#define NTFS_PUTU24(p,v) NTFS_PUTU16(p, (v) & 0xFFFF);\
NTFS_PUTU8(((char*)(p)) + 2, (v) >> 16)
#define NTFS_PUTU32(p,v) ((*(ntfs_u32*)(p)) = CPU_TO_LE32(v))
#define NTFS_PUTU64(p,v) ((*(ntfs_u64*)(p)) = CPU_TO_LE64(v))
/* Macros reading signed integers */
#define NTFS_GETS8(p) ((*(ntfs_s8*)(p)))
#define NTFS_GETS16(p) ((ntfs_s16)LE16_TO_CPU(*(short*)(p)))
#define NTFS_GETS24(p) (NTFS_GETU24(p) < 0x800000 ? \
(int)NTFS_GETU24(p) : \
(int)(NTFS_GETU24(p) - 0x1000000))
#define NTFS_GETS32(p) ((ntfs_s32)LE32_TO_CPU(*(int*)(p)))
#define NTFS_GETS40(p) (((ntfs_s64)NTFS_GETU32(p)) | \
(((ntfs_s64)NTFS_GETS8(((char*)(p)) + 4)) << 32))
#define NTFS_GETS48(p) (((ntfs_s64)NTFS_GETU32(p)) | \
(((ntfs_s64)NTFS_GETS16(((char*)(p)) + 4)) << 32))
#define NTFS_GETS56(p) (((ntfs_s64)NTFS_GETU32(p)) | \
(((ntfs_s64)NTFS_GETS24(((char*)(p)) + 4)) << 32))
#define NTFS_GETS64(p) ((ntfs_s64)NTFS_GETU64(p))
#define NTFS_PUTS8(p,v) NTFS_PUTU8(p,v)
#define NTFS_PUTS16(p,v) NTFS_PUTU16(p,v)
#define NTFS_PUTS24(p,v) NTFS_PUTU24(p,v)
#define NTFS_PUTS32(p,v) NTFS_PUTU32(p,v)
#define NTFS_PUTS64(p,v) NTFS_PUTU64(p,v)
 
/super.h
0,0 → 1,32
/*
* super.h - Header file for super.c
*
 
 
* Copyright (c) 2001 Anton Altaparmakov
*/
 
int ntfs_get_free_cluster_count(ntfs_inode *bitmap);
 
int ntfs_get_volumesize(ntfs_volume *vol, __s64 *vol_size);
 
int ntfs_init_volume(ntfs_volume *vol, char *boot);
 
int ntfs_load_special_files(ntfs_volume *vol);
 
int ntfs_release_volume(ntfs_volume *vol);
 
int ntfs_insert_fixups(unsigned char *rec, int rec_size);
 
int ntfs_fixup_record(char *record, char *magic, int size);
 
int ntfs_allocate_clusters(ntfs_volume *vol, ntfs_cluster_t *location,
ntfs_cluster_t *count, ntfs_runlist **rl, int *rl_len,
const NTFS_CLUSTER_ALLOCATION_ZONES zone);
 
int ntfs_deallocate_cluster_run(const ntfs_volume *vol,
const ntfs_cluster_t lcn, const ntfs_cluster_t len);
 
int ntfs_deallocate_clusters(const ntfs_volume *vol, const ntfs_runlist *rl,
const int rl_len);
 
/inode.h
0,0 → 1,58
/*
* inode.h - Header file for inode.c
*
 
 
* Copyright (c) 2001 Anton Altparmakov (AIA)
*/
 
ntfs_attribute *ntfs_find_attr(ntfs_inode *ino, int type, char *name);
 
int ntfs_read_attr(ntfs_inode *ino, int type, char *name, __s64 offset,
ntfs_io *buf);
 
int ntfs_write_attr(ntfs_inode *ino, int type, char *name, __s64 offset,
ntfs_io *buf);
 
int ntfs_init_inode(ntfs_inode *ino, ntfs_volume *vol, int inum);
 
void ntfs_clear_inode(ntfs_inode *ino);
 
int ntfs_check_mft_record(ntfs_volume *vol, char *record);
 
int ntfs_alloc_inode(ntfs_inode *dir, ntfs_inode *result, const char *filename,
int namelen, ntfs_u32);
 
int ntfs_alloc_file(ntfs_inode *dir, ntfs_inode *result, char *filename,
int namelen);
 
int ntfs_update_inode(ntfs_inode *ino);
 
int ntfs_vcn_to_lcn(ntfs_inode *ino, int vcn);
 
int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
ntfs_io *dest);
 
int ntfs_allocate_attr_number(ntfs_inode *ino, int *result);
 
int ntfs_decompress_run(unsigned char **data, int *length,
ntfs_cluster_t *cluster, int *ctype);
 
void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l);
 
int splice_runlists(ntfs_runlist **rl1, int *r1len, const ntfs_runlist *rl2,
int r2len);
 
/*
* NOTE: Neither of the ntfs_*_bit functions are atomic! But we don't need
* them atomic at present as we never operate on shared/cached bitmaps.
*/
static __inline__ int ntfs_test_and_set_bit(unsigned char *byte, const int bit)
{
unsigned char *ptr = byte + (bit >> 3);
int b = 1 << (bit & 7);
int oldbit = *ptr & b ? 1 : 0;
*ptr |= b;
return oldbit;
}
 
/ntfstypes.h
0,0 → 1,84
/*
* ntfstypes.h - This file defines four things:
* - Generic platform independent fixed-size types (e.g. ntfs_u32).
* - Specific fixed-size types (e.g. ntfs_offset_t).
* - Macros that read and write those types from and to byte arrays.
* - Types derived from OS specific ones.
*
 
* Copyright (C) 2001 Anton Altaparmakov (AIA)
*/
#include <linux/fs.h>
#include "ntfsendian.h"
#include <asm/types.h>
 
/* Integral types */
#ifndef NTFS_INTEGRAL_TYPES
#define NTFS_INTEGRAL_TYPES
typedef u8 ntfs_u8;
typedef u16 ntfs_u16;
typedef u32 ntfs_u32;
typedef u64 ntfs_u64;
typedef s8 ntfs_s8;
typedef s16 ntfs_s16;
typedef s32 ntfs_s32;
typedef s64 ntfs_s64;
#endif
 
/* Unicode character type */
#ifndef NTFS_WCHAR_T
#define NTFS_WCHAR_T
typedef u16 ntfs_wchar_t;
#endif
/* File offset */
#ifndef NTFS_OFFSET_T
#define NTFS_OFFSET_T
typedef s64 ntfs_offset_t;
#endif
/* UTC */
#ifndef NTFS_TIME64_T
#define NTFS_TIME64_T
typedef u64 ntfs_time64_t;
#endif
/*
* This is really signed long long. So we support only volumes up to 2Tb. This
* is ok as Win2k also only uses 32-bits to store clusters.
* Whatever you do keep this a SIGNED value or a lot of NTFS users with
* corrupted filesystems will lynch you! It causes massive fs corruption when
* unsigned due to the nature of many checks relying on being performed on
* signed quantities. (AIA)
*/
#ifndef NTFS_CLUSTER_T
#define NTFS_CLUSTER_T
typedef s32 ntfs_cluster_t;
#endif
 
/* Architecture independent macros. */
 
/* PUTU32 would not clear all bytes. */
#define NTFS_PUTINUM(p,i) NTFS_PUTU64(p, i->i_number); \
NTFS_PUTU16(((char*)p) + 6, i->sequence_number)
 
/* System dependent types. */
#include <asm/posix_types.h>
#ifndef NTMODE_T
#define NTMODE_T
typedef __kernel_mode_t ntmode_t;
#endif
#ifndef NTFS_UID_T
#define NTFS_UID_T
typedef uid_t ntfs_uid_t;
#endif
#ifndef NTFS_GID_T
#define NTFS_GID_T
typedef gid_t ntfs_gid_t;
#endif
#ifndef NTFS_SIZE_T
#define NTFS_SIZE_T
typedef __kernel_size_t ntfs_size_t;
#endif
#ifndef NTFS_TIME_T
#define NTFS_TIME_T
typedef __kernel_time_t ntfs_time_t;
#endif
 
/fs.c
0,0 → 1,1167
/*
* fs.c - NTFS driver for Linux 2.4.x
*
* Legato Systems, Inc. (http://www.legato.com) have sponsored Anton
* Altaparmakov to develop NTFS on Linux since June 2001.
*
 
* Copyright (C) 1996 Richard Russon
 
* Copyright (C) 2000-2001, Anton Altaparmakov (AIA)
*/
 
#include <linux/config.h>
#include <linux/errno.h>
#include "ntfstypes.h"
#include "struct.h"
#include "util.h"
#include "inode.h"
#include "super.h"
#include "dir.h"
#include "support.h"
#include "macros.h"
#include "sysctl.h"
#include "attr.h"
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/locks.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <linux/blkdev.h>
#include <asm/page.h>
#include <linux/nls.h>
#include <linux/ntfs_fs.h>
 
/* Forward declarations. */
static struct inode_operations ntfs_dir_inode_operations;
static struct file_operations ntfs_dir_operations;
 
#define ITEM_SIZE 2040
 
/* Io functions to user space. */
static void ntfs_putuser(ntfs_io* dest, void *src, ntfs_size_t len)
{
copy_to_user(dest->param, src, len);
dest->param += len;
}
 
#ifdef CONFIG_NTFS_RW
struct ntfs_getuser_update_vm_s {
const char *user;
struct inode *ino;
loff_t off;
};
 
static void ntfs_getuser_update_vm(void *dest, ntfs_io *src, ntfs_size_t len)
{
struct ntfs_getuser_update_vm_s *p = src->param;
copy_from_user(dest, p->user, len);
p->user += len;
p->off += len;
}
#endif
 
/* loff_t is 64 bit signed, so is cool. */
static ssize_t ntfs_read(struct file *filp, char *buf, size_t count,loff_t *off)
{
int error;
ntfs_io io;
ntfs_attribute *attr;
ntfs_inode *ino = NTFS_LINO2NINO(filp->f_dentry->d_inode);
 
/* Inode is not properly initialized. */
if (!ino)
return -EINVAL;
ntfs_debug(DEBUG_OTHER, "ntfs_read %x, %Lx, %x ->",
(unsigned)ino->i_number, (unsigned long long)*off,
(unsigned)count);
attr = ntfs_find_attr(ino, ino->vol->at_data, NULL);
/* Inode has no unnamed data attribute. */
if (!attr) {
ntfs_debug(DEBUG_OTHER, "ntfs_read: $DATA not found!\n");
return -EINVAL;
}
if (attr->flags & ATTR_IS_ENCRYPTED)
return -EACCES;
/* Read the data. */
io.fn_put = ntfs_putuser;
io.fn_get = 0;
io.param = buf;
io.size = count;
error = ntfs_read_attr(ino, ino->vol->at_data, NULL, *off, &io);
if (error && !io.size) {
ntfs_debug(DEBUG_OTHER, "ntfs_read: read_attr failed with "
"error %i, io size %u.\n", error, io.size);
return error;
}
*off += io.size;
ntfs_debug(DEBUG_OTHER, "ntfs_read: finished. read %u bytes.\n",
io.size);
return io.size;
}
 
#ifdef CONFIG_NTFS_RW
static ssize_t ntfs_write(struct file *filp, const char *buf, size_t count,
loff_t *pos)
{
int err;
struct inode *vfs_ino = filp->f_dentry->d_inode;
ntfs_inode *ntfs_ino = NTFS_LINO2NINO(vfs_ino);
ntfs_attribute *data;
ntfs_io io;
struct ntfs_getuser_update_vm_s param;
 
if (!ntfs_ino)
return -EINVAL;
ntfs_debug(DEBUG_LINUX, "%s(): Entering for inode 0x%lx, *pos 0x%Lx, "
"count 0x%x.\n", __FUNCTION__, ntfs_ino->i_number,
*pos, count);
/* Allows to lock fs ro at any time. */
if (vfs_ino->i_sb->s_flags & MS_RDONLY)
return -EROFS;
data = ntfs_find_attr(ntfs_ino, ntfs_ino->vol->at_data, NULL);
if (!data)
return -EINVAL;
/* Evaluating O_APPEND is the file system's job... */
if (filp->f_flags & O_APPEND)
*pos = vfs_ino->i_size;
if (!data->resident && *pos + count > data->allocated) {
err = ntfs_extend_attr(ntfs_ino, data, *pos + count);
if (err < 0)
return err;
}
param.user = buf;
param.ino = vfs_ino;
param.off = *pos;
io.fn_put = 0;
io.fn_get = ntfs_getuser_update_vm;
io.param = &param;
io.size = count;
io.do_read = 0;
err = ntfs_readwrite_attr(ntfs_ino, data, *pos, &io);
ntfs_debug(DEBUG_LINUX, "%s(): Returning %i\n", __FUNCTION__, -err);
if (!err) {
*pos += io.size;
if (*pos > vfs_ino->i_size)
vfs_ino->i_size = *pos;
mark_inode_dirty(vfs_ino);
return io.size;
}
return err;
}
#endif
 
struct ntfs_filldir {
struct inode *dir;
filldir_t filldir;
unsigned int type;
u32 ph, pl;
void *dirent;
char *name;
int namelen;
int ret_code;
};
 
static int ntfs_printcb(ntfs_u8 *entry, void *param)
{
unsigned long inum = NTFS_GETU64(entry) & 0xffffffffffff;
struct ntfs_filldir *nf = param;
u32 flags = NTFS_GETU32(entry + 0x48);
char show_sys_files = 0;
u8 name_len = NTFS_GETU8(entry + 0x50);
u8 name_type = NTFS_GETU8(entry + 0x51);
int err;
unsigned file_type;
 
switch (nf->type) {
case ngt_dos:
/* Don't display long names. */
if (!(name_type & 2))
return 0;
break;
case ngt_nt:
/* Don't display short-only names. */
if ((name_type & 3) == 2)
return 0;
break;
case ngt_posix:
break;
case ngt_full:
show_sys_files = 1;
break;
default:
BUG();
}
err = ntfs_encodeuni(NTFS_INO2VOL(nf->dir), (ntfs_u16*)(entry + 0x52),
name_len, &nf->name, &nf->namelen);
if (err) {
ntfs_debug(DEBUG_OTHER, "%s(): Skipping unrepresentable "
"file.\n", __FUNCTION__);
err = 0;
goto err_noname;
}
if (!show_sys_files && inum < 0x10UL) {
ntfs_debug(DEBUG_OTHER, "%s(): Skipping system file (%s).\n",
__FUNCTION__, nf->name);
err = 0;
goto err_ret;
}
/* Do not return ".", as this is faked. */
if (nf->namelen == 1 && nf->name[0] == '.') {
ntfs_debug(DEBUG_OTHER, "%s(): Skipping \".\"\n", __FUNCTION__);
err = 0;
goto err_ret;
}
nf->name[nf->namelen] = 0;
if (flags & 0x10000000) /* FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT */
file_type = DT_DIR;
else
file_type = DT_REG;
ntfs_debug(DEBUG_OTHER, "%s(): Calling filldir for %s with "
"len %i, f_pos 0x%Lx, inode %lu, %s.\n", __FUNCTION__,
nf->name, nf->namelen, (loff_t)(nf->ph << 16) | nf->pl,
inum, file_type == DT_DIR ? "DT_DIR" : "DT_REG");
/*
* Userspace side of filldir expects an off_t rather than an loff_t.
* And it also doesn't like the most significant bit being set as it
* then considers the value to be negative. Thus this implementation
* limits the number of index records to 32766, which should be plenty.
*/
err = nf->filldir(nf->dirent, nf->name, nf->namelen,
(loff_t)(nf->ph << 16) | nf->pl, inum, file_type);
if (err)
nf->ret_code = err;
err_ret:
ntfs_free(nf->name);
err_noname:
nf->namelen = 0;
nf->name = NULL;
return err;
}
 
/*
* readdir returns '.', then '..', then the directory entries in sequence.
* As the root directory contains an entry for itself, '.' is not emulated for
* the root directory.
*/
static int ntfs_readdir(struct file* filp, void *dirent, filldir_t filldir)
{
struct inode *dir = filp->f_dentry->d_inode;
int err;
struct ntfs_filldir cb;
 
cb.ret_code = 0;
cb.pl = filp->f_pos & 0xffff;
cb.ph = (filp->f_pos >> 16) & 0x7fff;
filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl;
ntfs_debug(DEBUG_OTHER, "%s(): Entering for inode %lu, f_pos 0x%Lx, "
"i_mode 0x%x, i_count %lu.\n", __FUNCTION__,
dir->i_ino, filp->f_pos, (unsigned int)dir->i_mode,
atomic_read(&dir->i_count));
if (!cb.ph) {
/* Start of directory. Emulate "." and "..". */
if (!cb.pl) {
ntfs_debug(DEBUG_OTHER, "%s(): Calling filldir for . "
"with len 1, f_pos 0x%Lx, inode %lu, "
"DT_DIR.\n", __FUNCTION__, filp->f_pos,
dir->i_ino);
cb.ret_code = filldir(dirent, ".", 1, filp->f_pos,
dir->i_ino, DT_DIR);
if (cb.ret_code)
goto done;
cb.pl++;
filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl;
}
if (cb.pl == (u32)1) {
ntfs_debug(DEBUG_OTHER, "%s(): Calling filldir for .. "
"with len 2, f_pos 0x%Lx, inode %lu, "
"DT_DIR.\n", __FUNCTION__, filp->f_pos,
filp->f_dentry->d_parent->d_inode->i_ino);
cb.ret_code = filldir(dirent, "..", 2, filp->f_pos,
filp->f_dentry->d_parent->d_inode->i_ino,
DT_DIR);
if (cb.ret_code)
goto done;
cb.pl++;
filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl;
}
} else if (cb.ph >= 0x7fff)
/* End of directory. */
goto done;
cb.dir = dir;
cb.filldir = filldir;
cb.dirent = dirent;
cb.type = NTFS_INO2VOL(dir)->ngt;
do {
ntfs_debug(DEBUG_OTHER, "%s(): Looking for next file using "
"ntfs_getdir_unsorted(), f_pos 0x%Lx.\n",
__FUNCTION__, (loff_t)(cb.ph << 16) | cb.pl);
err = ntfs_getdir_unsorted(NTFS_LINO2NINO(dir), &cb.ph, &cb.pl,
ntfs_printcb, &cb);
} while (!err && !cb.ret_code && cb.ph < 0x7fff);
filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl;
ntfs_debug(DEBUG_OTHER, "%s(): After ntfs_getdir_unsorted()"
" calls, f_pos 0x%Lx.\n", __FUNCTION__, filp->f_pos);
if (!err) {
done:
#ifdef DEBUG
if (!cb.ret_code)
ntfs_debug(DEBUG_OTHER, "%s(): EOD, f_pos 0x%Lx, "
"returning 0.\n", __FUNCTION__,
filp->f_pos);
else
ntfs_debug(DEBUG_OTHER, "%s(): filldir returned %i, "
"returning 0, f_pos 0x%Lx.\n",
__FUNCTION__, cb.ret_code, filp->f_pos);
#endif
return 0;
}
ntfs_debug(DEBUG_OTHER, "%s(): Returning %i, f_pos 0x%Lx.\n",
__FUNCTION__, err, filp->f_pos);
return err;
}
 
/* Copied from vfat driver. */
static int simple_getbool(char *s, int *setval)
{
if (s) {
if (!strcmp(s, "1") || !strcmp(s, "yes") || !strcmp(s, "true"))
*setval = 1;
else if (!strcmp(s, "0") || !strcmp(s, "no") ||
!strcmp(s, "false"))
*setval = 0;
else
return 0;
} else
*setval = 1;
return 1;
}
 
/*
* This needs to be outside parse_options() otherwise a remount will reset
* these unintentionally.
*/
static void init_ntfs_super_block(ntfs_volume* vol)
{
vol->uid = vol->gid = 0;
vol->umask = 0077;
vol->ngt = ngt_nt;
vol->nls_map = (void*)-1;
vol->mft_zone_multiplier = -1;
}
 
/* Parse the (re)mount options. */
static int parse_options(ntfs_volume *vol, char *opt)
{
char *value; /* Defaults if not specified and !remount. */
ntfs_uid_t uid = -1; /* 0, root user only */
ntfs_gid_t gid = -1; /* 0, root user only */
int umask = -1; /* 0077, owner access only */
unsigned int ngt = -1; /* ngt_nt */
void *nls_map = NULL; /* Try to load the default NLS. */
int use_utf8 = -1; /* If no NLS specified and loading the default
NLS failed use utf8. */
int mft_zone_mul = -1; /* 1 */
 
if (!opt)
goto done;
for (opt = strtok(opt, ","); opt; opt = strtok(NULL, ",")) {
if ((value = strchr(opt, '=')) != NULL)
*value ++= '\0';
if (strcmp(opt, "uid") == 0) {
if (!value || !*value)
goto needs_arg;
uid = simple_strtoul(value, &value, 0);
if (*value) {
printk(KERN_ERR "NTFS: uid invalid argument\n");
return 0;
}
} else if (strcmp(opt, "gid") == 0) {
if (!value || !*value)
goto needs_arg;
gid = simple_strtoul(value, &value, 0);
if (*value) {
printk(KERN_ERR "NTFS: gid invalid argument\n");
return 0;
}
} else if (strcmp(opt, "umask") == 0) {
if (!value || !*value)
goto needs_arg;
umask = simple_strtoul(value, &value, 0);
if (*value) {
printk(KERN_ERR "NTFS: umask invalid "
"argument\n");
return 0;
}
} else if (strcmp(opt, "mft_zone_multiplier") == 0) {
unsigned long ul;
 
if (!value || !*value)
goto needs_arg;
ul = simple_strtoul(value, &value, 0);
if (*value) {
printk(KERN_ERR "NTFS: mft_zone_multiplier "
"invalid argument\n");
return 0;
}
if (ul >= 1 && ul <= 4)
mft_zone_mul = ul;
else {
mft_zone_mul = 1;
printk(KERN_WARNING "NTFS: mft_zone_multiplier "
"out of range. Setting to 1.\n");
}
} else if (strcmp(opt, "posix") == 0) {
int val;
if (!value || !*value)
goto needs_arg;
if (!simple_getbool(value, &val))
goto needs_bool;
ngt = val ? ngt_posix : ngt_nt;
} else if (strcmp(opt, "show_sys_files") == 0) {
int val = 0;
if (!value || !*value)
val = 1;
else if (!simple_getbool(value, &val))
goto needs_bool;
ngt = val ? ngt_full : ngt_nt;
} else if (strcmp(opt, "iocharset") == 0) {
if (!value || !*value)
goto needs_arg;
nls_map = load_nls(value);
if (!nls_map) {
printk(KERN_ERR "NTFS: charset not found");
return 0;
}
} else if (strcmp(opt, "utf8") == 0) {
int val = 0;
if (!value || !*value)
val = 1;
else if (!simple_getbool(value, &val))
goto needs_bool;
use_utf8 = val;
} else {
printk(KERN_ERR "NTFS: unkown option '%s'\n", opt);
return 0;
}
}
done:
if (use_utf8 == -1) {
/* utf8 was not specified at all. */
if (!nls_map) {
/*
* No NLS was specified. If first mount, load the
* default NLS, otherwise don't change the NLS setting.
*/
if (vol->nls_map == (void*)-1)
vol->nls_map = load_nls_default();
} else {
/* If an NLS was already loaded, unload it first. */
if (vol->nls_map && vol->nls_map != (void*)-1)
unload_nls(vol->nls_map);
/* Use the specified NLS. */
vol->nls_map = nls_map;
}
} else {
/* utf8 was specified. */
if (use_utf8 && nls_map) {
unload_nls(nls_map);
printk(KERN_ERR "NTFS: utf8 cannot be combined with "
"iocharset.\n");
return 0;
}
/* If an NLS was already loaded, unload it first. */
if (vol->nls_map && vol->nls_map != (void*)-1)
unload_nls(vol->nls_map);
if (!use_utf8) {
/* utf8 was specified as false. */
if (!nls_map)
/* No NLS was specified, load the default. */
vol->nls_map = load_nls_default();
else
/* Use the specified NLS. */
vol->nls_map = nls_map;
} else
/* utf8 was specified as true. */
vol->nls_map = NULL;
}
if (uid != -1)
vol->uid = uid;
if (gid != -1)
vol->gid = gid;
if (umask != -1)
vol->umask = (ntmode_t)umask;
if (ngt != -1)
vol->ngt = ngt;
if (mft_zone_mul != -1) {
/* mft_zone_multiplier was specified. */
if (vol->mft_zone_multiplier != -1) {
/* This is a remount, ignore a change and warn user. */
if (vol->mft_zone_multiplier != mft_zone_mul)
printk(KERN_WARNING "NTFS: Ignoring changes in "
"mft_zone_multiplier on "
"remount. If you want to "
"change this you need to "
"umount and mount again.\n");
} else
/* Use the specified multiplier. */
vol->mft_zone_multiplier = mft_zone_mul;
} else if (vol->mft_zone_multiplier == -1)
/* No multiplier specified and first mount, so set default. */
vol->mft_zone_multiplier = 1;
return 1;
needs_arg:
printk(KERN_ERR "NTFS: %s needs an argument", opt);
return 0;
needs_bool:
printk(KERN_ERR "NTFS: %s needs boolean argument", opt);
return 0;
}
static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *d)
{
struct inode *res = 0;
char *item = 0;
ntfs_iterate_s walk;
int err;
ntfs_debug(DEBUG_NAME1, "%s(): Looking up %s in directory ino 0x%x.\n",
__FUNCTION__, d->d_name.name, (unsigned)dir->i_ino);
walk.name = NULL;
walk.namelen = 0;
/* Convert to wide string. */
err = ntfs_decodeuni(NTFS_INO2VOL(dir), (char*)d->d_name.name,
d->d_name.len, &walk.name, &walk.namelen);
if (err)
goto err_ret;
item = ntfs_malloc(ITEM_SIZE);
if (!item) {
err = -ENOMEM;
goto err_ret;
}
/* ntfs_getdir will place the directory entry into item, and the first
* long long is the MFT record number. */
walk.type = BY_NAME;
walk.dir = NTFS_LINO2NINO(dir);
walk.result = item;
if (ntfs_getdir_byname(&walk))
res = iget(dir->i_sb, NTFS_GETU32(item));
d_add(d, res);
ntfs_free(item);
ntfs_free(walk.name);
/* Always return success, the dcache will handle negative entries. */
return NULL;
err_ret:
ntfs_free(walk.name);
return ERR_PTR(err);
}
 
static struct file_operations ntfs_file_operations = {
llseek: generic_file_llseek,
read: ntfs_read,
#ifdef CONFIG_NTFS_RW
write: ntfs_write,
#endif
open: generic_file_open,
};
 
static struct inode_operations ntfs_inode_operations;
 
#ifdef CONFIG_NTFS_RW
static int ntfs_create(struct inode* dir, struct dentry *d, int mode)
{
struct inode *r = 0;
ntfs_inode *ino = 0;
ntfs_volume *vol;
int error = 0;
ntfs_attribute *si;
 
r = new_inode(dir->i_sb);
if (!r) {
error = -ENOMEM;
goto fail;
}
ntfs_debug(DEBUG_OTHER, "ntfs_create %s\n", d->d_name.name);
vol = NTFS_INO2VOL(dir);
ino = NTFS_LINO2NINO(r);
error = ntfs_alloc_file(NTFS_LINO2NINO(dir), ino, (char*)d->d_name.name,
d->d_name.len);
if (error) {
ntfs_error("ntfs_alloc_file FAILED: error = %i", error);
goto fail;
}
/* Not doing this one was causing a huge amount of corruption! Now the
* bugger bytes the dust! (-8 (AIA) */
r->i_ino = ino->i_number;
error = ntfs_update_inode(ino);
if (error)
goto fail;
error = ntfs_update_inode(NTFS_LINO2NINO(dir));
if (error)
goto fail;
r->i_uid = vol->uid;
r->i_gid = vol->gid;
/* FIXME: dirty? dev? */
/* Get the file modification times from the standard information. */
si = ntfs_find_attr(ino, vol->at_standard_information, NULL);
if (si) {
char *attr = si->d.data;
r->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18));
r->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr));
r->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8));
}
/* It's not a directory */
r->i_op = &ntfs_inode_operations;
r->i_fop = &ntfs_file_operations;
r->i_mode = S_IFREG | S_IRUGO;
#ifdef CONFIG_NTFS_RW
r->i_mode |= S_IWUGO;
#endif
r->i_mode &= ~vol->umask;
insert_inode_hash(r);
d_instantiate(d, r);
return 0;
fail:
if (r)
iput(r);
return error;
}
 
static int _linux_ntfs_mkdir(struct inode *dir, struct dentry* d, int mode)
{
int error;
struct inode *r = 0;
ntfs_volume *vol;
ntfs_inode *ino;
ntfs_attribute *si;
 
ntfs_debug (DEBUG_DIR1, "mkdir %s in %x\n", d->d_name.name, dir->i_ino);
error = -ENAMETOOLONG;
if (d->d_name.len > /* FIXME: */ 255)
goto out;
error = -EIO;
r = new_inode(dir->i_sb);
if (!r)
goto out;
vol = NTFS_INO2VOL(dir);
ino = NTFS_LINO2NINO(r);
error = ntfs_mkdir(NTFS_LINO2NINO(dir), d->d_name.name, d->d_name.len,
ino);
if (error)
goto out;
/* Not doing this one was causing a huge amount of corruption! Now the
* bugger bytes the dust! (-8 (AIA) */
r->i_ino = ino->i_number;
r->i_uid = vol->uid;
r->i_gid = vol->gid;
si = ntfs_find_attr(ino, vol->at_standard_information, NULL);
if (si) {
char *attr = si->d.data;
r->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18));
r->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr));
r->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8));
}
/* It's a directory. */
r->i_op = &ntfs_dir_inode_operations;
r->i_fop = &ntfs_dir_operations;
r->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
#ifdef CONFIG_NTFS_RW
r->i_mode |= S_IWUGO;
#endif
r->i_mode &= ~vol->umask;
insert_inode_hash(r);
d_instantiate(d, r);
error = 0;
out:
ntfs_debug (DEBUG_DIR1, "mkdir returns %d\n", error);
return error;
}
#endif
 
static struct file_operations ntfs_dir_operations = {
read: generic_read_dir,
readdir: ntfs_readdir,
};
 
static struct inode_operations ntfs_dir_inode_operations = {
lookup: ntfs_lookup,
#ifdef CONFIG_NTFS_RW
create: ntfs_create,
mkdir: _linux_ntfs_mkdir,
#endif
};
 
/* ntfs_read_inode() is called by the Virtual File System (the kernel layer
* that deals with filesystems) when iget is called requesting an inode not
* already present in the inode table. Typically filesystems have separate
* inode_operations for directories, files and symlinks. */
static void ntfs_read_inode(struct inode* inode)
{
ntfs_volume *vol;
ntfs_inode *ino;
ntfs_attribute *data;
ntfs_attribute *si;
 
vol = NTFS_INO2VOL(inode);
inode->i_mode = 0;
ntfs_debug(DEBUG_OTHER, "ntfs_read_inode 0x%lx\n", inode->i_ino);
switch (inode->i_ino) {
/* Those are loaded special files. */
case FILE_Mft:
if (!vol->mft_ino || ((vol->ino_flags & 1) == 0))
goto sys_file_error;
ntfs_memcpy(&inode->u.ntfs_i, vol->mft_ino, sizeof(ntfs_inode));
ino = vol->mft_ino;
vol->mft_ino = &inode->u.ntfs_i;
vol->ino_flags &= ~1;
ntfs_free(ino);
ino = vol->mft_ino;
ntfs_debug(DEBUG_OTHER, "Opening $MFT!\n");
break;
case FILE_MftMirr:
if (!vol->mftmirr || ((vol->ino_flags & 2) == 0))
goto sys_file_error;
ntfs_memcpy(&inode->u.ntfs_i, vol->mftmirr, sizeof(ntfs_inode));
ino = vol->mftmirr;
vol->mftmirr = &inode->u.ntfs_i;
vol->ino_flags &= ~2;
ntfs_free(ino);
ino = vol->mftmirr;
ntfs_debug(DEBUG_OTHER, "Opening $MFTMirr!\n");
break;
case FILE_BitMap:
if (!vol->bitmap || ((vol->ino_flags & 4) == 0))
goto sys_file_error;
ntfs_memcpy(&inode->u.ntfs_i, vol->bitmap, sizeof(ntfs_inode));
ino = vol->bitmap;
vol->bitmap = &inode->u.ntfs_i;
vol->ino_flags &= ~4;
ntfs_free(ino);
ino = vol->bitmap;
ntfs_debug(DEBUG_OTHER, "Opening $Bitmap!\n");
break;
case FILE_LogFile ... FILE_AttrDef:
/* No need to log root directory accesses. */
case FILE_Boot ... FILE_UpCase:
ntfs_debug(DEBUG_OTHER, "Opening system file %i!\n",
inode->i_ino);
default:
ino = &inode->u.ntfs_i;
if (!ino || ntfs_init_inode(ino, NTFS_INO2VOL(inode),
inode->i_ino))
{
ntfs_debug(DEBUG_OTHER, "NTFS: Error loading inode "
"0x%x\n", (unsigned int)inode->i_ino);
return;
}
}
/* Set uid/gid from mount options */
inode->i_uid = vol->uid;
inode->i_gid = vol->gid;
inode->i_nlink = 1;
/* Use the size of the data attribute as file size */
data = ntfs_find_attr(ino, vol->at_data, NULL);
if (!data)
inode->i_size = 0;
else
inode->i_size = data->size;
/* Get the file modification times from the standard information. */
si = ntfs_find_attr(ino, vol->at_standard_information, NULL);
if (si) {
char *attr = si->d.data;
inode->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18));
inode->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr));
inode->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8));
}
/* If it has an index root, it's a directory. */
if (ntfs_find_attr(ino, vol->at_index_root, "$I30")) {
ntfs_attribute *at;
at = ntfs_find_attr(ino, vol->at_index_allocation, "$I30");
inode->i_size = at ? at->size : 0;
inode->i_op = &ntfs_dir_inode_operations;
inode->i_fop = &ntfs_dir_operations;
inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
} else {
inode->i_op = &ntfs_inode_operations;
inode->i_fop = &ntfs_file_operations;
inode->i_mode = S_IFREG | S_IRUGO;
}
#ifdef CONFIG_NTFS_RW
if (!data || !(data->flags & (ATTR_IS_COMPRESSED | ATTR_IS_ENCRYPTED)))
inode->i_mode |= S_IWUGO;
#endif
inode->i_mode &= ~vol->umask;
return;
sys_file_error:
ntfs_error("Critical error. Tried to call ntfs_read_inode() before we "
"have completed read_super() or VFS error.\n");
// FIXME: Should we panic() at this stage?
}
 
#ifdef CONFIG_NTFS_RW
static void ntfs_write_inode(struct inode *ino, int unused)
{
lock_kernel();
ntfs_debug(DEBUG_LINUX, "ntfs_write_inode 0x%x\n", ino->i_ino);
ntfs_update_inode(NTFS_LINO2NINO(ino));
unlock_kernel();
}
#endif
 
static void _ntfs_clear_inode(struct inode *inode)
{
ntfs_inode *ino;
ntfs_volume *vol;
lock_kernel();
ntfs_debug(DEBUG_OTHER, "_ntfs_clear_inode 0x%x\n", inode->i_ino);
vol = NTFS_INO2VOL(inode);
if (!vol)
ntfs_error("_ntfs_clear_inode: vol = NTFS_INO2VOL(inode) is "
"NULL.\n");
switch (inode->i_ino) {
case FILE_Mft:
if (vol->mft_ino && ((vol->ino_flags & 1) == 0)) {
ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode));
ntfs_memcpy(ino, &inode->u.ntfs_i, sizeof(ntfs_inode));
vol->mft_ino = ino;
vol->ino_flags |= 1;
goto unl_out;
}
break;
case FILE_MftMirr:
if (vol->mftmirr && ((vol->ino_flags & 2) == 0)) {
ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode));
ntfs_memcpy(ino, &inode->u.ntfs_i, sizeof(ntfs_inode));
vol->mftmirr = ino;
vol->ino_flags |= 2;
goto unl_out;
}
break;
case FILE_BitMap:
if (vol->bitmap && ((vol->ino_flags & 4) == 0)) {
ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode));
ntfs_memcpy(ino, &inode->u.ntfs_i, sizeof(ntfs_inode));
vol->bitmap = ino;
vol->ino_flags |= 4;
goto unl_out;
}
break;
/* Nothing. Just clear the inode and exit. */
}
ntfs_clear_inode(&inode->u.ntfs_i);
unl_out:
unlock_kernel();
return;
}
 
/* Called when umounting a filesystem by do_umount() in fs/super.c. */
static void ntfs_put_super(struct super_block *sb)
{
ntfs_volume *vol;
 
ntfs_debug(DEBUG_OTHER, "ntfs_put_super\n");
vol = NTFS_SB2VOL(sb);
ntfs_release_volume(vol);
if (vol->nls_map)
unload_nls(vol->nls_map);
ntfs_debug(DEBUG_OTHER, "ntfs_put_super: done\n");
}
 
/* Called by the kernel when asking for stats. */
static int ntfs_statfs(struct super_block *sb, struct statfs *sf)
{
struct inode *mft;
ntfs_volume *vol;
__s64 size;
int error;
 
ntfs_debug(DEBUG_OTHER, "ntfs_statfs\n");
vol = NTFS_SB2VOL(sb);
sf->f_type = NTFS_SUPER_MAGIC;
sf->f_bsize = vol->cluster_size;
error = ntfs_get_volumesize(NTFS_SB2VOL(sb), &size);
if (error)
return error;
sf->f_blocks = size; /* Volumesize is in clusters. */
size = (__s64)ntfs_get_free_cluster_count(vol->bitmap);
/* Just say zero if the call failed. */
if (size < 0LL)
size = 0;
sf->f_bfree = sf->f_bavail = size;
ntfs_debug(DEBUG_OTHER, "ntfs_statfs: calling mft = iget(sb, "
"FILE_Mft)\n");
mft = iget(sb, FILE_Mft);
ntfs_debug(DEBUG_OTHER, "ntfs_statfs: iget(sb, FILE_Mft) returned "
"0x%x\n", mft);
if (!mft)
return -EIO;
sf->f_files = mft->i_size >> vol->mft_record_size_bits;
ntfs_debug(DEBUG_OTHER, "ntfs_statfs: calling iput(mft)\n");
iput(mft);
/* Should be read from volume. */
sf->f_namelen = 255;
return 0;
}
 
/* Called when remounting a filesystem by do_remount_sb() in fs/super.c. */
static int ntfs_remount_fs(struct super_block *sb, int *flags, char *options)
{
if (!parse_options(NTFS_SB2VOL(sb), options))
return -EINVAL;
return 0;
}
 
/* Define the super block operation that are implemented */
static struct super_operations ntfs_super_operations = {
read_inode: ntfs_read_inode,
#ifdef CONFIG_NTFS_RW
write_inode: ntfs_write_inode,
#endif
put_super: ntfs_put_super,
statfs: ntfs_statfs,
remount_fs: ntfs_remount_fs,
clear_inode: _ntfs_clear_inode,
};
 
/**
* is_boot_sector_ntfs - check an NTFS boot sector for validity
* @b: buffer containing bootsector to check
*
* Check whether @b contains a valid NTFS boot sector.
* Return 1 if @b is a valid NTFS bootsector or 0 if not.
*/
static int is_boot_sector_ntfs(ntfs_u8 *b)
{
ntfs_u32 i;
 
/* FIXME: We don't use checksumming yet as NT4(SP6a) doesn't either...
* But we might as well have the code ready to do it. (AIA) */
#if 0
/* Calculate the checksum. */
if (b < b + 0x50) {
ntfs_u32 *u;
ntfs_u32 *bi = (ntfs_u32 *)(b + 0x50);
for (u = bi, i = 0; u < bi; ++u)
i += NTFS_GETU32(*u);
}
#endif
/* Check magic is "NTFS " */
if (b[3] != 0x4e) goto not_ntfs;
if (b[4] != 0x54) goto not_ntfs;
if (b[5] != 0x46) goto not_ntfs;
if (b[6] != 0x53) goto not_ntfs;
for (i = 7; i < 0xb; ++i)
if (b[i] != 0x20) goto not_ntfs;
/* Check bytes per sector value is between 512 and 4096. */
if (b[0xb] != 0) goto not_ntfs;
if (b[0xc] > 0x10) goto not_ntfs;
/* Check sectors per cluster value is valid. */
switch (b[0xd]) {
case 1: case 2: case 4: case 8: case 16:
case 32: case 64: case 128:
break;
default:
goto not_ntfs;
}
/* Check reserved sectors value and four other fields are zero. */
for (i = 0xe; i < 0x15; ++i)
if (b[i] != 0) goto not_ntfs;
if (b[0x16] != 0) goto not_ntfs;
if (b[0x17] != 0) goto not_ntfs;
for (i = 0x20; i < 0x24; ++i)
if (b[i] != 0) goto not_ntfs;
/* Check clusters per file record segment value is valid. */
if (b[0x40] < 0xe1 || b[0x40] > 0xf7) {
switch (b[0x40]) {
case 1: case 2: case 4: case 8: case 16: case 32: case 64:
break;
default:
goto not_ntfs;
}
}
/* Check clusters per index block value is valid. */
if (b[0x44] < 0xe1 || b[0x44] > 0xf7) {
switch (b[0x44]) {
case 1: case 2: case 4: case 8: case 16: case 32: case 64:
break;
default:
goto not_ntfs;
}
}
return 1;
not_ntfs:
return 0;
}
 
/* Called to mount a filesystem by read_super() in fs/super.c.
* Return a super block, the main structure of a filesystem.
*
* NOTE : Don't store a pointer to an option, as the page containing the
* options is freed after ntfs_read_super() returns.
*
* NOTE : A context switch can happen in kernel code only if the code blocks
* (= calls schedule() in kernel/sched.c). */
struct super_block *ntfs_read_super(struct super_block *sb, void *options,
int silent)
{
ntfs_volume *vol;
struct buffer_head *bh;
int i, to_read, blocksize;
 
ntfs_debug(DEBUG_OTHER, "ntfs_read_super\n");
vol = NTFS_SB2VOL(sb);
init_ntfs_super_block(vol);
if (!parse_options(vol, (char*)options))
goto ntfs_read_super_vol;
blocksize = get_hardsect_size(sb->s_dev);
if (blocksize < 512)
blocksize = 512;
if (set_blocksize(sb->s_dev, blocksize) < 0) {
ntfs_error("Unable to set blocksize %d.\n", blocksize);
goto ntfs_read_super_vol;
}
sb->s_blocksize = blocksize;
/* Read the super block (boot block). */
if (!(bh = sb_bread(sb, 0))) {
ntfs_error("Reading super block failed\n");
goto ntfs_read_super_unl;
}
ntfs_debug(DEBUG_OTHER, "Done reading boot block\n");
/* Check for valid 'NTFS' boot sector. */
if (!is_boot_sector_ntfs(bh->b_data)) {
ntfs_debug(DEBUG_OTHER, "Not a NTFS volume\n");
bforget(bh);
goto ntfs_read_super_unl;
}
ntfs_debug(DEBUG_OTHER, "Going to init volume\n");
if (ntfs_init_volume(vol, bh->b_data) < 0) {
ntfs_debug(DEBUG_OTHER, "Init volume failed.\n");
bforget(bh);
goto ntfs_read_super_unl;
}
ntfs_debug(DEBUG_OTHER, "$Mft at cluster 0x%lx\n", vol->mft_lcn);
brelse(bh);
NTFS_SB(vol) = sb;
if (vol->cluster_size > PAGE_SIZE) {
ntfs_error("Partition cluster size is not supported yet (it "
"is > max kernel blocksize).\n");
goto ntfs_read_super_unl;
}
ntfs_debug(DEBUG_OTHER, "Done to init volume\n");
/* Inform the kernel that a device block is a NTFS cluster. */
sb->s_blocksize = vol->cluster_size;
sb->s_blocksize_bits = vol->cluster_size_bits;
if (blocksize != vol->cluster_size &&
set_blocksize(sb->s_dev, sb->s_blocksize) < 0) {
ntfs_error("Cluster size too small for device.\n");
goto ntfs_read_super_unl;
}
ntfs_debug(DEBUG_OTHER, "set_blocksize\n");
/* Allocate an MFT record (MFT record can be smaller than a cluster). */
i = vol->cluster_size;
if (i < vol->mft_record_size)
i = vol->mft_record_size;
if (!(vol->mft = ntfs_malloc(i)))
goto ntfs_read_super_unl;
 
/* Read at least the MFT record for $Mft. */
to_read = vol->mft_clusters_per_record;
if (to_read < 1)
to_read = 1;
for (i = 0; i < to_read; i++) {
if (!(bh = sb_bread(sb, vol->mft_lcn + i))) {
ntfs_error("Could not read $Mft record 0\n");
goto ntfs_read_super_mft;
}
ntfs_memcpy(vol->mft + ((__s64)i << vol->cluster_size_bits),
bh->b_data, vol->cluster_size);
brelse(bh);
ntfs_debug(DEBUG_OTHER, "Read cluster 0x%x\n",
vol->mft_lcn + i);
}
/* Check and fixup this MFT record */
if (!ntfs_check_mft_record(vol, vol->mft)){
ntfs_error("Invalid $Mft record 0\n");
goto ntfs_read_super_mft;
}
/* Inform the kernel about which super operations are available. */
sb->s_op = &ntfs_super_operations;
sb->s_magic = NTFS_SUPER_MAGIC;
sb->s_maxbytes = MAX_LFS_FILESIZE;
ntfs_debug(DEBUG_OTHER, "Reading special files\n");
if (ntfs_load_special_files(vol)) {
ntfs_error("Error loading special files\n");
goto ntfs_read_super_mft;
}
ntfs_debug(DEBUG_OTHER, "Getting RootDir\n");
/* Get the root directory. */
if (!(sb->s_root = d_alloc_root(iget(sb, FILE_root)))) {
ntfs_error("Could not get root dir inode\n");
goto ntfs_read_super_mft;
}
ntfs_read_super_ret:
ntfs_debug(DEBUG_OTHER, "read_super: done\n");
return sb;
ntfs_read_super_mft:
ntfs_free(vol->mft);
ntfs_read_super_unl:
ntfs_read_super_vol:
sb = NULL;
goto ntfs_read_super_ret;
}
 
/* Define the filesystem */
static DECLARE_FSTYPE_DEV(ntfs_fs_type, "ntfs", ntfs_read_super);
 
static int __init init_ntfs_fs(void)
{
/* Comment this if you trust klogd. There are reasons not to trust it */
#if defined(DEBUG) && !defined(MODULE)
console_verbose();
#endif
printk(KERN_NOTICE "NTFS driver v" NTFS_VERSION " [Flags: R/"
#ifdef CONFIG_NTFS_RW
"W"
#else
"O"
#endif
#ifdef DEBUG
" DEBUG"
#endif
#ifdef MODULE
" MODULE"
#endif
"]\n");
SYSCTL(1);
ntfs_debug(DEBUG_OTHER, "registering %s\n", ntfs_fs_type.name);
/* Add this filesystem to the kernel table of filesystems. */
return register_filesystem(&ntfs_fs_type);
}
 
static void __exit exit_ntfs_fs(void)
{
SYSCTL(0);
ntfs_debug(DEBUG_OTHER, "unregistering %s\n", ntfs_fs_type.name);
unregister_filesystem(&ntfs_fs_type);
}
 
EXPORT_NO_SYMBOLS;
/*
 
* I am just maintaining and rewriting it.
*/
MODULE_AUTHOR("Anton Altaparmakov <aia21@cus.cam.ac.uk>");
MODULE_DESCRIPTION("Linux NTFS driver");
MODULE_LICENSE("GPL");
#ifdef DEBUG
MODULE_PARM(ntdebug, "i");
MODULE_PARM_DESC(ntdebug, "Debug level");
#endif
 
module_init(init_ntfs_fs)
module_exit(exit_ntfs_fs)
 
/attr.c
0,0 → 1,872
/*
* attr.c
*
 
 
* Copyright (C) 1998 Joseph Malicki
* Copyright (C) 1999 Steve Dodd
* Copyright (C) 2001 Anton Altaparmakov (AIA)
*/
 
#include "ntfstypes.h"
#include "struct.h"
#include "attr.h"
 
#include <linux/errno.h>
#include <linux/ntfs_fs.h>
#include "macros.h"
#include "support.h"
#include "util.h"
#include "super.h"
#include "inode.h"
#include "unistr.h"
 
/**
* ntfs_find_attr_in_mft_rec - find attribute in mft record
* @vol: volume on which attr resides
* @m: mft record to search
* @type: attribute type to find
* @name: attribute name to find (optional, i.e. NULL means don't care)
* @name_len: attribute name length (only needed if @name present)
* @ic: ignore case if 1 or case sensitive if 0 (ignored if @name NULL)
* @instance: instance number to find
*
* Only search the specified mft record and it ignores the presence of an
* attribute list attribute (unless it is the one being searched for,
* obviously, in which case it is returned).
*/
ntfs_u8* ntfs_find_attr_in_mft_rec(ntfs_volume *vol, ntfs_u8 *m, __u32 type,
wchar_t *name, __u32 name_len, int ic, __u16 instance)
{
ntfs_u8 *a;
/* Iterate over attributes in mft record @m. */
a = m + NTFS_GETU16(m + 20); /* attrs_offset */
for (; a >= m && a <= m + vol->mft_record_size;
a += NTFS_GETU32(a + 4 /* length */)) {
/* We catch $END with this more general check, too... */
if (NTFS_GETU32(a + 0 /* type */) > type)
return NULL;
if (!NTFS_GETU32(a + 4 /* length */))
break;
if (NTFS_GETU32(a + 0 /* type */) != type)
continue;
/* If @name is present, compare the two names. */
if (name && !ntfs_are_names_equal(name, name_len, (wchar_t*)
(a + NTFS_GETU16(a + 10 /* name_offset */)),
a[9] /* name_length */, ic, vol->upcase,
vol->upcase_length)) {
register int rc;
rc = ntfs_collate_names(vol->upcase, vol->upcase_length,
name, name_len, (wchar_t*)(a +
NTFS_GETU16(a + 10 /* name_offset */)),
a[9] /* name_length */, 1, 1);
/*
* If @name collates before a->name, there is no
* matching attribute.
*/
if (rc == -1)
return NULL;
/* If the strings are not equal, continue search. */
if (rc)
continue;
rc = ntfs_collate_names(vol->upcase, vol->upcase_length,
name, name_len, (wchar_t*)(a +
NTFS_GETU16(a + 10 /* name_offset */)),
a[9] /* name_length */, 0, 1);
if (rc == -1)
return NULL;
if (rc)
continue;
}
/*
* The names match or @name not present. Check instance number.
* and if it matches we have found the attribute and are done.
*/
if (instance != NTFS_GETU16(a + 14 /* instance */))
continue;
ntfs_debug(DEBUG_FILE3, "ntfs_find_attr_in_mft_record: found: "
"attr type 0x%x, instance number = 0x%x.\n",
NTFS_GETU32(a + 0), instance);
return a;
}
ntfs_error("ntfs_find_attr_in_mft_record: mft record 0x%x is corrupt"
". Run chkdsk.\n", m);
return NULL;
}
 
/* Look if an attribute already exists in the inode, and if not, create it. */
int ntfs_new_attr(ntfs_inode *ino, int type, void *name, int namelen,
void *value, int value_len, int *pos, int *found)
{
int do_insert = 0;
int i, m;
ntfs_attribute *a;
 
for (i = 0; i < ino->attr_count; i++)
{
a = ino->attrs + i;
if (a->type < type)
continue;
if (a->type > type) {
do_insert = 1;
break;
}
/* If @name is present, compare the two names. */
if (namelen && !ntfs_are_names_equal((wchar_t*)name, namelen,
a->name, a->namelen /* name_length */,
1 /* ignore case*/, ino->vol->upcase,
ino->vol->upcase_length)) {
register int rc;
 
rc = ntfs_collate_names(ino->vol->upcase,
ino->vol->upcase_length, a->name,
a->namelen, (wchar_t*)name, namelen,
1 /* ignore case */, 1);
if (rc == -1)
continue;
if (rc == 1) {
do_insert = 1;
break;
}
rc = ntfs_collate_names(ino->vol->upcase,
ino->vol->upcase_length, a->name,
a->namelen, (wchar_t*)name, namelen,
0 /* case sensitive */, 1);
if (rc == -1)
continue;
if (rc == 1) {
do_insert = 1;
break;
}
}
/* Names are equal or no name was asked for. */
/* If a value was specified compare the values. */
if (value_len && a->resident) {
if (!a->resident) {
ntfs_error("ntfs_new_attr: Value specified but "
"attribute non-resident. Bug!\n");
return -EINVAL;
}
m = value_len;
if (m > a->size)
m = a->size;
m = memcmp(value, a->d.data, m);
if (m > 0)
continue;
if (m < 0) {
do_insert = 1;
break;
}
/* Values match until min of value lengths. */
if (value_len > a->size)
continue;
if (value_len < a->size) {
do_insert = 1;
break;
}
}
/* Full match! */
*found = 1;
*pos = i;
return 0;
}
/* Re-allocate space. */
if (ino->attr_count % 8 == 0)
{
ntfs_attribute* new;
new = (ntfs_attribute*)ntfs_malloc((ino->attr_count + 8) *
sizeof(ntfs_attribute));
if (!new)
return -ENOMEM;
if (ino->attrs) {
ntfs_memcpy(new, ino->attrs, ino->attr_count *
sizeof(ntfs_attribute));
ntfs_free(ino->attrs);
}
ino->attrs = new;
}
if (do_insert)
ntfs_memmove(ino->attrs + i + 1, ino->attrs + i,
(ino->attr_count - i) * sizeof(ntfs_attribute));
ino->attr_count++;
ino->attrs[i].type = type;
ino->attrs[i].namelen = namelen;
ino->attrs[i].name = name;
*pos = i;
*found = 0;
return 0;
}
 
int ntfs_make_attr_resident(ntfs_inode *ino, ntfs_attribute *attr)
{
__s64 size = attr->size;
if (size > 0) {
/* FIXME: read data, free clusters */
return -EOPNOTSUPP;
}
attr->resident = 1;
return 0;
}
 
/* Store in the inode readable information about a run. */
int ntfs_insert_run(ntfs_attribute *attr, int cnum, ntfs_cluster_t cluster,
int len)
{
/* (re-)allocate space if necessary. */
if ((attr->d.r.len * sizeof(ntfs_runlist)) % PAGE_SIZE == 0) {
ntfs_runlist* new;
unsigned long new_size;
 
ntfs_debug(DEBUG_MALLOC, "ntfs_insert_run: re-allocating "
"space: old attr->d.r.len = 0x%x\n",
attr->d.r.len);
new_size = attr->d.r.len * sizeof(ntfs_runlist) + PAGE_SIZE;
if ((new_size >> PAGE_SHIFT) > num_physpages) {
ntfs_error("ntfs_insert_run: attempted to allocate "
"more pages than num_physpages."
"This might be a bug or a corrupt"
"file system.\n");
return -1;
}
new = ntfs_vmalloc(new_size);
if (!new) {
ntfs_error("ntfs_insert_run: ntfs_vmalloc(new_size = "
"0x%x) failed\n", new_size);
return -1;
}
if (attr->d.r.runlist) {
ntfs_memcpy(new, attr->d.r.runlist, attr->d.r.len
* sizeof(ntfs_runlist));
ntfs_vfree(attr->d.r.runlist);
}
attr->d.r.runlist = new;
}
if (attr->d.r.len > cnum)
ntfs_memmove(attr->d.r.runlist + cnum + 1,
attr->d.r.runlist + cnum,
(attr->d.r.len - cnum) * sizeof(ntfs_runlist));
attr->d.r.runlist[cnum].lcn = cluster;
attr->d.r.runlist[cnum].len = len;
attr->d.r.len++;
return 0;
}
 
/**
* ntfs_extend_attr - extend allocated size of an attribute
* @ino: ntfs inode containing the attribute to extend
* @attr: attribute which to extend
* @len: desired new length for @attr (_not_ the amount to extend by)
*
* Extends an attribute. Allocate clusters on the volume which @ino belongs to.
* Extends the run list accordingly, preferably by extending the last run of
* the existing run list, first.
*
* Only modifies attr->allocated, i.e. doesn't touch attr->size, nor
* attr->initialized.
*/
int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, const __s64 len)
{
int rlen, rl2_len, err = 0;
ntfs_cluster_t cluster, clen;
ntfs_runlist *rl, *rl2;
 
if ((attr->flags & (ATTR_IS_COMPRESSED | ATTR_IS_ENCRYPTED)) ||
ino->record_count > 1)
return -EOPNOTSUPP;
/*
* FIXME: Don't make non-resident if the attribute type is not right.
* For example cannot make index attribute non-resident! (AIA)
*/
if (attr->resident) {
err = ntfs_make_attr_nonresident(ino, attr);
if (err)
return err;
}
if (len <= attr->allocated)
return 0; /* Truly stupid things do sometimes happen. */
rl = attr->d.r.runlist;
rlen = attr->d.r.len;
if (rlen > 0)
cluster = rl[rlen - 1].lcn + rl[rlen - 1].len;
else
/* No preference for allocation space. */
cluster = (ntfs_cluster_t)-1;
/*
* Calculate the extra space we need, and round up to multiple of
* cluster size to get number of new clusters needed.
*/
clen = (len - attr->allocated + ino->vol->cluster_size - 1) >>
ino->vol->cluster_size_bits;
if (!clen)
return 0;
err = ntfs_allocate_clusters(ino->vol, &cluster, &clen, &rl2,
&rl2_len, DATA_ZONE);
if (err)
return err;
attr->allocated += (__s64)clen << ino->vol->cluster_size_bits;
if (rlen > 0) {
err = splice_runlists(&rl, &rlen, rl2, rl2_len);
ntfs_vfree(rl2);
if (err)
return err;
} else {
if (rl)
ntfs_vfree(rl);
rl = rl2;
rlen = rl2_len;
}
attr->d.r.runlist = rl;
attr->d.r.len = rlen;
return 0;
}
 
int ntfs_make_attr_nonresident(ntfs_inode *ino, ntfs_attribute *attr)
{
int error;
ntfs_io io;
void *data = attr->d.data;
__s64 len = attr->size;
 
attr->d.r.len = 0;
attr->d.r.runlist = NULL;
attr->resident = 0;
/*
* ->allocated is updated by ntfs_extend_attr(), while ->initialized
* and ->size are updated by ntfs_readwrite_attr(). (AIA)
*/
attr->allocated = attr->initialized = 0;
error = ntfs_extend_attr(ino, attr, len);
if (error)
return error; /* FIXME: On error, restore old values. */
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
io.param = data;
io.size = len;
io.do_read = 0;
return ntfs_readwrite_attr(ino, attr, 0, &io);
}
 
int ntfs_attr_allnonresident(ntfs_inode *ino)
{
int i, error = 0;
ntfs_volume *vol = ino->vol;
 
for (i = 0; !error && i < ino->attr_count; i++)
{
if (ino->attrs[i].type != vol->at_security_descriptor &&
ino->attrs[i].type != vol->at_data)
continue;
error = ntfs_make_attr_nonresident(ino, ino->attrs + i);
}
return error;
}
 
/*
* Resize the attribute to a newsize. attr->allocated and attr->size are
* updated, but attr->initialized is not changed unless it becomes bigger than
* attr->size, in which case it is set to attr->size.
*/
int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 newsize)
{
int error = 0;
__s64 oldsize = attr->size;
int clustersizebits = ino->vol->cluster_size_bits;
int i, count, newcount;
ntfs_runlist *rl, *rlt;
 
if (newsize == oldsize)
return 0;
if (attr->flags & (ATTR_IS_COMPRESSED | ATTR_IS_ENCRYPTED))
return -EOPNOTSUPP;
if (attr->resident) {
void *v;
if (newsize > ino->vol->mft_record_size) {
error = ntfs_make_attr_nonresident(ino, attr);
if (error)
return error;
return ntfs_resize_attr(ino, attr, newsize);
}
v = attr->d.data;
if (newsize) {
__s64 minsize = newsize;
attr->d.data = ntfs_malloc(newsize);
if (!attr->d.data) {
ntfs_free(v);
return -ENOMEM;
}
if (newsize > oldsize) {
minsize = oldsize;
ntfs_bzero((char*)attr->d.data + oldsize,
newsize - oldsize);
}
ntfs_memcpy((char*)attr->d.data, v, minsize);
} else
attr->d.data = 0;
ntfs_free(v);
attr->size = newsize;
return 0;
}
/* Non-resident attribute. */
rl = attr->d.r.runlist;
if (newsize < oldsize) {
int rl_size;
/*
* FIXME: We might be going awfully wrong for newsize = 0,
* possibly even leaking memory really badly. But considering
* in that case there is more breakage due to -EOPNOTSUPP stuff
* further down the code path, who cares for the moment... (AIA)
*/
for (i = 0, count = 0; i < attr->d.r.len; i++) {
if ((__s64)(count + rl[i].len) << clustersizebits >
newsize) {
i++;
break;
}
count += (int)rl[i].len;
}
newcount = count;
/* Free unused clusters in current run, unless sparse. */
if (rl[--i].lcn != (ntfs_cluster_t)-1) {
ntfs_cluster_t rounded = newsize - ((__s64)count <<
clustersizebits);
rounded = (rounded + ino->vol->cluster_size - 1) >>
clustersizebits;
error = ntfs_deallocate_cluster_run(ino->vol,
rl[i].lcn + rounded,
rl[i].len - rounded);
if (error)
return error; /* FIXME: Incomplete operation. */
rl[i].len = rounded;
newcount = count + rounded;
}
/* Free all other runs. */
i++;
error = ntfs_deallocate_clusters(ino->vol, rl + i,
attr->d.r.len - i);
if (error)
return error; /* FIXME: Incomplete operation. */
/*
* Free space for extra runs in memory if enough memory left
* to do so. FIXME: Only do it if it would free memory. (AIA)
*/
rl_size = ((i + 1) * sizeof(ntfs_runlist) + PAGE_SIZE - 1) &
PAGE_MASK;
if (rl_size < ((attr->d.r.len * sizeof(ntfs_runlist) +
PAGE_SIZE - 1) & PAGE_MASK)) {
rlt = ntfs_vmalloc(rl_size);
if (rlt) {
ntfs_memcpy(rlt, rl, i * sizeof(ntfs_runlist));
ntfs_vfree(rl);
attr->d.r.runlist = rl = rlt;
}
}
rl[i].lcn = (ntfs_cluster_t)-1;
rl[i].len = (ntfs_cluster_t)0;
attr->d.r.len = i;
} else {
error = ntfs_extend_attr(ino, attr, newsize);
if (error)
return error; /* FIXME: Incomplete operation. */
newcount = (newsize + ino->vol->cluster_size - 1) >>
clustersizebits;
}
/* Fill in new sizes. */
attr->allocated = (__s64)newcount << clustersizebits;
attr->size = newsize;
if (attr->initialized > newsize)
attr->initialized = newsize;
if (!newsize)
error = ntfs_make_attr_resident(ino, attr);
return error;
}
 
int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data,
int dsize, ntfs_attribute **rattr)
{
void *name;
int namelen;
int found, i;
int error;
ntfs_attribute *attr;
if (dsize > ino->vol->mft_record_size)
/* FIXME: Non-resident attributes. */
return -EOPNOTSUPP;
if (aname) {
namelen = strlen(aname);
name = ntfs_malloc(2 * namelen);
if (!name)
return -ENOMEM;
ntfs_ascii2uni(name, aname, namelen);
} else {
name = 0;
namelen = 0;
}
error = ntfs_new_attr(ino, anum, name, namelen, data, dsize, &i,
&found);
if (error || found) {
ntfs_free(name);
return error ? error : -EEXIST;
}
*rattr = attr = ino->attrs + i;
/* Allocate a new number.
* FIXME: Should this happen on inode writeback?
* FIXME: Extension records not supported. */
error = ntfs_allocate_attr_number(ino, &i);
if (error)
return error;
attr->attrno = i;
if (attr->attrno + 1 != NTFS_GETU16(ino->attr + 0x28))
ntfs_error("UH OH! attr->attrno (%i) != NTFS_GETU16(ino->attr "
"+ 0x28) (%i)\n", attr->attrno,
NTFS_GETU16(ino->attr + 0x28));
attr->resident = 1;
attr->flags = 0;
attr->cengine = 0;
attr->size = attr->allocated = attr->initialized = dsize;
 
/* FIXME: INDEXED information should come from $AttrDef
* Currently, only file names are indexed. As of NTFS v3.0 (Win2k),
* this is no longer true. Different attributes can be indexed now. */
if (anum == ino->vol->at_file_name)
attr->indexed = 1;
else
attr->indexed = 0;
attr->d.data = ntfs_malloc(dsize);
if (!attr->d.data)
return -ENOMEM;
ntfs_memcpy(attr->d.data, data, dsize);
return 0;
}
 
/*
* Non-resident attributes are stored in runs (intervals of clusters).
*
* This function stores in the inode readable information about a non-resident
* attribute.
*/
static int ntfs_process_runs(ntfs_inode *ino, ntfs_attribute* attr,
unsigned char *data)
{
int startvcn, endvcn;
int vcn, cnum;
ntfs_cluster_t cluster;
int len, ctype;
int er = 0;
startvcn = NTFS_GETS64(data + 0x10);
endvcn = NTFS_GETS64(data + 0x18);
 
/* Check whether this chunk really belongs to the end. Problem with
* this: this functions can get called on the last extent first, before
* it is called on the other extents in sequence. This happens when the
* base mft record contains the last extent instead of the first one
* and the first extent is stored, like any intermediate extents in
* extension mft records. This would be difficult to allow the way the
* runlist is stored in memory. Thus we fix elsewhere by causing the
* attribute list attribute to be processed immediately when found. The
* extents will then be processed starting with the first one. */
for (cnum = 0, vcn = 0; cnum < attr->d.r.len; cnum++)
vcn += attr->d.r.runlist[cnum].len;
if (vcn != startvcn) {
ntfs_debug(DEBUG_FILE3, "ntfs_process_runs: ino = 0x%x, "
"attr->type = 0x%x, startvcn = 0x%x, endvcn = 0x%x, "
"vcn = 0x%x, cnum = 0x%x\n", ino->i_number, attr->type,
startvcn, endvcn, vcn, cnum);
if (vcn < startvcn) {
ntfs_error("Problem with runlist in extended record\n");
return -1;
}
/* Tried to insert an already inserted runlist. */
return 0;
}
if (!endvcn) {
if (!startvcn) {
/* Allocated length. */
endvcn = NTFS_GETS64(data + 0x28) - 1;
endvcn >>= ino->vol->cluster_size_bits;
} else {
/* This is an extent. Allocated length is not defined!
* Extents must have an endvcn though so this is an
* error. */
ntfs_error("Corrupt attribute extent. (endvcn is "
"missing)\n");
return -1;
}
}
data = data + NTFS_GETU16(data + 0x20);
cnum = attr->d.r.len;
cluster = 0;
for (vcn = startvcn; vcn <= endvcn; vcn += len) {
if (ntfs_decompress_run(&data, &len, &cluster, &ctype)) {
ntfs_debug(DEBUG_FILE3, "ntfs_process_runs: "
"ntfs_decompress_run failed. i_number = 0x%x\n",
ino->i_number);
return -1;
}
if (ctype)
er = ntfs_insert_run(attr, cnum, -1, len);
else
er = ntfs_insert_run(attr, cnum, cluster, len);
if (er)
break;
cnum++;
}
if (er)
ntfs_error("ntfs_process_runs: ntfs_insert_run failed\n");
ntfs_debug(DEBUG_FILE3, "ntfs_process_runs: startvcn = 0x%x, vcn = 0x%x"
", endvcn = 0x%x, cnum = %i\n", startvcn, vcn,
endvcn, cnum);
return er;
}
/* Insert the attribute starting at attr in the inode ino. */
int ntfs_insert_attribute(ntfs_inode *ino, unsigned char *attrdata)
{
int i, found;
int type;
short int *name;
int namelen;
void *data;
ntfs_attribute *attr;
int error;
 
type = NTFS_GETU32(attrdata);
namelen = NTFS_GETU8(attrdata + 9);
ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute: ino->i_number 0x%x, "
"attr type 0x%x\n", ino->i_number, type);
/* Read the attribute's name if it has one. */
if (!namelen)
name = 0;
else {
/* 1 Unicode character fits in 2 bytes. */
name = ntfs_malloc(2 * namelen);
if (!name)
return -ENOMEM;
ntfs_memcpy(name, attrdata + NTFS_GETU16(attrdata + 10),
2 * namelen);
}
/* If resident look for value, too. */
if (NTFS_GETU8(attrdata + 8) == 0)
error = ntfs_new_attr(ino, type, name, namelen,
attrdata + NTFS_GETU16(attrdata + 0x14),
NTFS_GETU16(attrdata + 0x10), &i, &found);
else
error = ntfs_new_attr(ino, type, name, namelen, NULL, 0, &i,
&found);
if (error) {
ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute: ntfs_new_attr "
"failed.\n");
if (name)
ntfs_free(name);
return error;
}
if (found) {
/* It's already there, if not resident just process the runs. */
if (!ino->attrs[i].resident) {
ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute:"
" processing runs 1.\n");
/* FIXME: Check error code! (AIA) */
ntfs_process_runs(ino, ino->attrs + i, attrdata);
}
return 0;
}
attr = ino->attrs + i;
attr->resident = NTFS_GETU8(attrdata + 8) == 0;
attr->flags = *(__u16*)(attrdata + 0xC);
attr->attrno = NTFS_GETU16(attrdata + 0xE);
if (attr->resident) {
attr->size = NTFS_GETU16(attrdata + 0x10);
data = attrdata + NTFS_GETU16(attrdata + 0x14);
attr->d.data = (void*)ntfs_malloc(attr->size);
if (!attr->d.data)
return -ENOMEM;
ntfs_memcpy(attr->d.data, data, attr->size);
attr->indexed = NTFS_GETU8(attrdata + 0x16);
} else {
attr->allocated = NTFS_GETS64(attrdata + 0x28);
attr->size = NTFS_GETS64(attrdata + 0x30);
attr->initialized = NTFS_GETS64(attrdata + 0x38);
attr->cengine = NTFS_GETU16(attrdata + 0x22);
if (attr->flags & ATTR_IS_COMPRESSED)
attr->compsize = NTFS_GETS64(attrdata + 0x40);
ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute: "
"attr->allocated = 0x%Lx, attr->size = 0x%Lx, "
"attr->initialized = 0x%Lx\n", attr->allocated,
attr->size, attr->initialized);
ino->attrs[i].d.r.runlist = 0;
ino->attrs[i].d.r.len = 0;
ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute: processing "
"runs 2.\n");
/* FIXME: Check error code! (AIA) */
ntfs_process_runs(ino, attr, attrdata);
}
return 0;
}
 
int ntfs_read_zero(ntfs_io *dest, int size)
{
int i;
char *sparse = ntfs_calloc(512);
if (!sparse)
return -ENOMEM;
i = 512;
while (size) {
if (i > size)
i = size;
dest->fn_put(dest, sparse, i);
size -= i;
}
ntfs_free(sparse);
return 0;
}
 
/* Process compressed attributes. */
int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
ntfs_io *dest)
{
int error = 0;
int clustersizebits;
int s_vcn, rnum, vcn, got, l1;
__s64 copied, len, chunk, offs1, l, chunk2;
ntfs_cluster_t cluster, cl1;
char *comp = 0, *comp1;
char *decomp = 0;
ntfs_io io;
ntfs_runlist *rl;
 
l = dest->size;
clustersizebits = ino->vol->cluster_size_bits;
/* Starting cluster of potential chunk. There are three situations:
a) In a large uncompressible or sparse chunk, s_vcn is in the middle
of a run.
b) s_vcn is right on a run border.
c) When several runs make a chunk, s_vcn is before the chunks. */
s_vcn = offset >> clustersizebits;
/* Round down to multiple of 16. */
s_vcn &= ~15;
rl = attr->d.r.runlist;
for (rnum = vcn = 0; rnum < attr->d.r.len && vcn + rl->len <= s_vcn;
rnum++, rl++)
vcn += rl->len;
if (rnum == attr->d.r.len) {
/* Beyond end of file. */
/* FIXME: Check allocated / initialized. */
dest->size = 0;
return 0;
}
io.do_read = 1;
io.fn_put = ntfs_put;
io.fn_get = 0;
cluster = rl->lcn;
len = rl->len;
copied = 0;
while (l) {
chunk = 0;
if (cluster == (ntfs_cluster_t)-1) {
/* Sparse cluster. */
__s64 ll;
 
if ((len - (s_vcn - vcn)) & 15)
ntfs_error("Unexpected sparse chunk size.");
ll = ((__s64)(vcn + len) << clustersizebits) - offset;
if (ll > l)
ll = l;
chunk = ll;
error = ntfs_read_zero(dest, ll);
if (error)
goto out;
} else if (dest->do_read) {
if (!comp) {
comp = ntfs_malloc(16 << clustersizebits);
if (!comp) {
error = -ENOMEM;
goto out;
}
}
got = 0;
/* We might need to start in the middle of a run. */
cl1 = cluster + s_vcn - vcn;
comp1 = comp;
do {
int delta;
 
io.param = comp1;
delta = s_vcn - vcn;
if (delta < 0)
delta = 0;
l1 = len - delta;
if (l1 > 16 - got)
l1 = 16 - got;
io.size = (__s64)l1 << clustersizebits;
error = ntfs_getput_clusters(ino->vol, cl1, 0,
&io);
if (error)
goto out;
if (l1 + delta == len) {
rnum++;
rl++;
vcn += len;
cluster = cl1 = rl->lcn;
len = rl->len;
}
got += l1;
comp1 += (__s64)l1 << clustersizebits;
} while (cluster != (ntfs_cluster_t)-1 && got < 16);
/* Until empty run. */
chunk = 16 << clustersizebits;
if (cluster != (ntfs_cluster_t)-1 || got == 16)
/* Uncompressible */
comp1 = comp;
else {
if (!decomp) {
decomp = ntfs_malloc(16 <<
clustersizebits);
if (!decomp) {
error = -ENOMEM;
goto out;
}
}
/* Make sure there are null bytes after the
* last block. */
*(ntfs_u32*)comp1 = 0;
ntfs_decompress(decomp, comp, chunk);
comp1 = decomp;
}
offs1 = offset - ((__s64)s_vcn << clustersizebits);
chunk2 = (16 << clustersizebits) - offs1;
if (chunk2 > l)
chunk2 = l;
if (chunk > chunk2)
chunk = chunk2;
dest->fn_put(dest, comp1 + offs1, chunk);
}
l -= chunk;
copied += chunk;
offset += chunk;
s_vcn = (offset >> clustersizebits) & ~15;
if (l && offset >= ((__s64)(vcn + len) << clustersizebits)) {
rnum++;
rl++;
vcn += len;
cluster = rl->lcn;
len = rl->len;
}
}
out:
if (comp)
ntfs_free(comp);
if (decomp)
ntfs_free(decomp);
dest->size = copied;
return error;
}
 
int ntfs_write_compressed(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
ntfs_io *dest)
{
return -EOPNOTSUPP;
}
 
/support.c
0,0 → 1,316
/*
* support.c - Specific support functions
*
 
 
* Copyright (C) 2001 Anton Altaparmakov (AIA)
*/
 
#include "ntfstypes.h"
#include "struct.h"
#include "support.h"
 
#include <stdarg.h>
#include <linux/slab.h>
#include <linux/locks.h>
#include <linux/fs.h>
#include "util.h"
#include "inode.h"
#include "macros.h"
#include <linux/nls.h>
 
static char print_buf[1024];
 
#ifdef DEBUG
#include "sysctl.h"
#include <linux/kernel.h>
 
/* Debugging output */
void ntfs_debug(int mask, const char *fmt, ...)
{
va_list ap;
 
/* Filter it with the debugging level required */
if (ntdebug & mask) {
va_start(ap,fmt);
strcpy(print_buf, KERN_DEBUG "NTFS: ");
vsprintf(print_buf + 9, fmt, ap);
printk(print_buf);
va_end(ap);
}
}
 
#ifndef ntfs_malloc
/* Verbose kmalloc */
void *ntfs_malloc(int size)
{
void *ret;
 
ret = kmalloc(size, GFP_KERNEL);
ntfs_debug(DEBUG_MALLOC, "Allocating %x at %p\n", size, ret);
 
return ret;
}
#endif
 
#ifndef ntfs_free
/* Verbose kfree() */
void ntfs_free(void *block)
{
ntfs_debug(DEBUG_MALLOC, "Freeing memory at %p\n", block);
kfree(block);
}
#endif
#else /* End of DEBUG functions. Normal ones below... */
 
#ifndef ntfs_malloc
void *ntfs_malloc(int size)
{
return kmalloc(size, GFP_KERNEL);
}
#endif
 
#ifndef ntfs_free
void ntfs_free(void *block)
{
kfree(block);
}
#endif
#endif /* DEBUG */
 
void ntfs_bzero(void *s, int n)
{
memset(s, 0, n);
}
 
/* These functions deliberately return no value. It is dest, anyway,
and not used anywhere in the NTFS code. */
 
void ntfs_memcpy(void *dest, const void *src, ntfs_size_t n)
{
memcpy(dest, src, n);
}
 
void ntfs_memmove(void *dest, const void *src, ntfs_size_t n)
{
memmove(dest, src, n);
}
 
/* Warn that an error occurred. */
void ntfs_error(const char *fmt,...)
{
va_list ap;
 
va_start(ap, fmt);
strcpy(print_buf, KERN_ERR "NTFS: ");
vsprintf(print_buf + 9, fmt, ap);
printk(print_buf);
va_end(ap);
}
 
int ntfs_read_mft_record(ntfs_volume *vol, int mftno, char *buf)
{
int error;
ntfs_io io;
 
ntfs_debug(DEBUG_OTHER, "read_mft_record 0x%x\n", mftno);
if (mftno == FILE_Mft)
{
ntfs_memcpy(buf, vol->mft, vol->mft_record_size);
return 0;
}
if (!vol->mft_ino)
{
printk(KERN_ERR "NTFS: mft_ino is NULL. Something is terribly "
"wrong here!\n");
return -ENODATA;
}
io.fn_put = ntfs_put;
io.fn_get = 0;
io.param = buf;
io.size = vol->mft_record_size;
ntfs_debug(DEBUG_OTHER, "read_mft_record: calling ntfs_read_attr with: "
"mftno = 0x%x, vol->mft_record_size_bits = 0x%x, "
"mftno << vol->mft_record_size_bits = 0x%Lx\n", mftno,
vol->mft_record_size_bits,
(__s64)mftno << vol->mft_record_size_bits);
error = ntfs_read_attr(vol->mft_ino, vol->at_data, NULL,
(__s64)mftno << vol->mft_record_size_bits, &io);
if (error || (io.size != vol->mft_record_size)) {
ntfs_debug(DEBUG_OTHER, "read_mft_record: read 0x%x failed "
"(%d,%d,%d)\n", mftno, error, io.size,
vol->mft_record_size);
return error ? error : -ENODATA;
}
ntfs_debug(DEBUG_OTHER, "read_mft_record: finished read 0x%x\n", mftno);
if (!ntfs_check_mft_record(vol, buf)) {
/* FIXME: This is incomplete behaviour. We might be able to
* recover at this stage. ntfs_check_mft_record() is too
* conservative at aborting it's operations. It is OK for
* now as we just can't handle some on disk structures
* this way. (AIA) */
printk(KERN_WARNING "NTFS: Invalid MFT record for 0x%x\n", mftno);
return -EIO;
}
ntfs_debug(DEBUG_OTHER, "read_mft_record: Done 0x%x\n", mftno);
return 0;
}
 
int ntfs_getput_clusters(ntfs_volume *vol, int cluster, ntfs_size_t start_offs,
ntfs_io *buf)
{
struct super_block *sb = NTFS_SB(vol);
struct buffer_head *bh;
int length = buf->size;
int error = 0;
ntfs_size_t to_copy;
 
ntfs_debug(DEBUG_OTHER, "%s_clusters %d %d %d\n",
buf->do_read ? "get" : "put", cluster, start_offs, length);
to_copy = vol->cluster_size - start_offs;
while (length) {
if (!(bh = sb_bread(sb, cluster))) {
ntfs_debug(DEBUG_OTHER, "%s failed\n",
buf->do_read ? "Reading" : "Writing");
error = -EIO;
goto error_ret;
}
if (to_copy > length)
to_copy = length;
lock_buffer(bh);
if (buf->do_read) {
buf->fn_put(buf, bh->b_data + start_offs, to_copy);
unlock_buffer(bh);
} else {
buf->fn_get(bh->b_data + start_offs, buf, to_copy);
mark_buffer_dirty(bh);
unlock_buffer(bh);
/*
* Note: We treat synchronous IO on a per volume basis
* disregarding flags of individual inodes. This can
* lead to some strange write ordering effects upon a
* remount with a change in the sync flag but it should
* not break anything. [Except if the system crashes
* at that point in time but there would be more thigs
* to worry about than that in that case...]. (AIA)
*/
if (sb->s_flags & MS_SYNCHRONOUS) {
ll_rw_block(WRITE, 1, &bh);
wait_on_buffer(bh);
if (buffer_req(bh) && !buffer_uptodate(bh)) {
printk(KERN_ERR "IO error syncing NTFS "
"cluster [%s:%i]\n",
bdevname(sb->s_dev), cluster);
brelse(bh);
error = -EIO;
goto error_ret;
}
}
}
brelse(bh);
length -= to_copy;
start_offs = 0;
to_copy = vol->cluster_size;
cluster++;
}
error_ret:
return error;
}
 
ntfs_time64_t ntfs_now(void)
{
return ntfs_unixutc2ntutc(CURRENT_TIME);
}
 
int ntfs_dupuni2map(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
int *out_len)
{
int i, o, chl, chi;
char *result, *buf, charbuf[NLS_MAX_CHARSET_SIZE];
struct nls_table *nls = vol->nls_map;
 
result = ntfs_malloc(in_len + 1);
if (!result)
return -ENOMEM;
*out_len = in_len;
for (i = o = 0; i < in_len; i++) {
/* FIXME: Byte order? */
wchar_t uni = in[i];
if ((chl = nls->uni2char(uni, charbuf,
NLS_MAX_CHARSET_SIZE)) > 0) {
/* Adjust result buffer. */
if (chl > 1) {
buf = ntfs_malloc(*out_len + chl);
if (!buf) {
i = -ENOMEM;
goto err_ret;
}
memcpy(buf, result, o);
ntfs_free(result);
result = buf;
*out_len += (chl - 1);
}
for (chi = 0; chi < chl; chi++)
result[o++] = charbuf[chi];
} else {
/* Invalid character. */
printk(KERN_ERR "NTFS: Unicode name contains a "
"character that cannot be converted "
"to chosen character set. Remount "
"with utf8 encoding and this should "
"work.\n");
i = -EILSEQ;
goto err_ret;
}
}
result[*out_len] = '\0';
*out = result;
return 0;
err_ret:
ntfs_free(result);
*out_len = 0;
*out = NULL;
return i;
}
 
int ntfs_dupmap2uni(ntfs_volume *vol, char* in, int in_len, ntfs_u16 **out,
int *out_len)
{
int i, o;
ntfs_u16 *result;
struct nls_table *nls = vol->nls_map;
 
*out = result = ntfs_malloc(2 * in_len);
if (!result) {
*out_len = 0;
return -ENOMEM;
}
*out_len = in_len;
for (i = o = 0; i < in_len; i++, o++) {
wchar_t uni;
int charlen;
 
charlen = nls->char2uni(&in[i], in_len - i, &uni);
if (charlen < 0) {
i = charlen;
goto err_ret;
}
*out_len -= charlen - 1;
i += charlen - 1;
/* FIXME: Byte order? */
result[o] = uni;
if (!result[o]) {
i = -EILSEQ;
goto err_ret;
}
}
return 0;
err_ret:
printk(KERN_ERR "NTFS: Name contains a character that cannot be "
"converted to Unicode.\n");
ntfs_free(result);
*out_len = 0;
*out = NULL;
return i;
}
 
/util.c
0,0 → 1,265
/*
* util.c - Miscellaneous support
*
 
 
* Copyright (C) 2001 Anton Altaparmakov (AIA)
*
* The utf8 routines are copied from Python wstrop module.
*/
 
#include "ntfstypes.h"
#include "struct.h"
#include "util.h"
#include <linux/string.h>
#include <linux/errno.h>
#include <asm/div64.h> /* For do_div(). */
#include "support.h"
 
/*
* Converts a single wide character to a sequence of utf8 bytes.
* The character is represented in host byte order.
* Returns the number of bytes, or 0 on error.
*/
static int to_utf8(ntfs_u16 c, unsigned char *buf)
{
if (c == 0)
return 0; /* No support for embedded 0 runes. */
if (c < 0x80) {
if (buf)
buf[0] = (unsigned char)c;
return 1;
}
if (c < 0x800) {
if (buf) {
buf[0] = 0xc0 | (c >> 6);
buf[1] = 0x80 | (c & 0x3f);
}
return 2;
}
/* c < 0x10000 */
if (buf) {
buf[0] = 0xe0 | (c >> 12);
buf[1] = 0x80 | ((c >> 6) & 0x3f);
buf[2] = 0x80 | (c & 0x3f);
}
return 3;
}
 
/*
* Decodes a sequence of utf8 bytes into a single wide character.
* The character is returned in host byte order.
* Returns the number of bytes consumed, or 0 on error.
*/
static int from_utf8(const unsigned char *str, ntfs_u16 *c)
{
int l = 0, i;
 
if (*str < 0x80) {
*c = *str;
return 1;
}
if (*str < 0xc0) /* Lead byte must not be 10xxxxxx. */
return 0; /* Is c0 a possible lead byte? */
if (*str < 0xe0) { /* 110xxxxx */
*c = *str & 0x1f;
l = 2;
} else if (*str < 0xf0) { /* 1110xxxx */
*c = *str & 0xf;
l = 3;
} else if (*str < 0xf8) { /* 11110xxx */
*c = *str & 7;
l = 4;
} else /* We don't support characters above 0xFFFF in NTFS. */
return 0;
for (i = 1; i < l; i++) {
/* All other bytes must be 10xxxxxx. */
if ((str[i] & 0xc0) != 0x80)
return 0;
*c <<= 6;
*c |= str[i] & 0x3f;
}
return l;
}
 
/*
* Converts wide string to UTF-8. Expects two in- and two out-parameters.
* Returns 0 on success, or error code.
* The caller has to free the result string.
*/
static int ntfs_dupuni2utf8(ntfs_u16 *in, int in_len, char **out, int *out_len)
{
int i, tmp;
int len8;
unsigned char *result;
 
ntfs_debug(DEBUG_NAME1, "converting l = %d\n", in_len);
/* Count the length of the resulting UTF-8. */
for (i = len8 = 0; i < in_len; i++) {
tmp = to_utf8(NTFS_GETU16(in + i), 0);
if (!tmp)
/* Invalid character. */
return -EILSEQ;
len8 += tmp;
}
*out = result = ntfs_malloc(len8 + 1); /* allow for zero-termination */
if (!result)
return -ENOMEM;
result[len8] = '\0';
*out_len = len8;
for (i = len8 = 0; i < in_len; i++)
len8 += to_utf8(NTFS_GETU16(in + i), result + len8);
ntfs_debug(DEBUG_NAME1, "result %p:%s\n", result, result);
return 0;
}
 
/*
* Converts an UTF-8 sequence to a wide string. Same conventions as the
* previous function.
*/
static int ntfs_duputf82uni(unsigned char* in, int in_len, ntfs_u16** out,
int *out_len)
{
int i, tmp;
int len16;
ntfs_u16* result;
ntfs_u16 wtmp;
 
for (i = len16 = 0; i < in_len; i += tmp, len16++) {
tmp = from_utf8(in + i, &wtmp);
if (!tmp)
return -EILSEQ;
}
*out = result = ntfs_malloc(2 * (len16 + 1));
if (!result)
return -ENOMEM;
result[len16] = 0;
*out_len = len16;
for (i = len16 = 0; i < in_len; i += tmp, len16++) {
tmp = from_utf8(in + i, &wtmp);
NTFS_PUTU16(result + len16, wtmp);
}
return 0;
}
 
/* Encodings dispatchers. */
int ntfs_encodeuni(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
int *out_len)
{
if (vol->nls_map)
return ntfs_dupuni2map(vol, in, in_len, out, out_len);
else
return ntfs_dupuni2utf8(in, in_len, out, out_len);
}
 
int ntfs_decodeuni(ntfs_volume *vol, char *in, int in_len, ntfs_u16 **out,
int *out_len)
{
if (vol->nls_map)
return ntfs_dupmap2uni(vol, in, in_len, out, out_len);
else
return ntfs_duputf82uni(in, in_len, out, out_len);
}
 
/* Same address space copies. */
void ntfs_put(ntfs_io *dest, void *src, ntfs_size_t n)
{
ntfs_memcpy(dest->param, src, n);
((char*)dest->param) += n;
}
 
void ntfs_get(void* dest, ntfs_io *src, ntfs_size_t n)
{
ntfs_memcpy(dest, src->param, n);
((char*)src->param) += n;
}
 
void *ntfs_calloc(int size)
{
void *result = ntfs_malloc(size);
if (result)
ntfs_bzero(result, size);
return result;
}
 
/* Copy len ascii characters from from to to. :) */
void ntfs_ascii2uni(short int *to, char *from, int len)
{
int i;
 
for (i = 0; i < len; i++)
NTFS_PUTU16(to + i, from[i]);
to[i] = 0;
}
 
/* strncmp for Unicode strings. */
int ntfs_uni_strncmp(short int* a, short int *b, int n)
{
int i;
 
for(i = 0; i < n; i++)
{
if (NTFS_GETU16(a + i) < NTFS_GETU16(b + i))
return -1;
if (NTFS_GETU16(b + i) < NTFS_GETU16(a + i))
return 1;
if (NTFS_GETU16(a + i) == 0)
break;
}
return 0;
}
 
/* strncmp between Unicode and ASCII strings. */
int ntfs_ua_strncmp(short int* a, char* b, int n)
{
int i;
 
for (i = 0; i < n; i++) {
if(NTFS_GETU16(a + i) < b[i])
return -1;
if(b[i] < NTFS_GETU16(a + i))
return 1;
if (b[i] == 0)
return 0;
}
return 0;
}
 
#define NTFS_TIME_OFFSET ((ntfs_time64_t)(369*365 + 89) * 24 * 3600 * 10000000)
 
/* Convert the NT UTC (based 1.1.1601, in hundred nanosecond units)
* into Unix UTC (based 1.1.1970, in seconds). */
ntfs_time_t ntfs_ntutc2unixutc(ntfs_time64_t ntutc)
{
/* Subtract the NTFS time offset, then convert to 1s intervals. */
ntfs_time64_t t = ntutc - NTFS_TIME_OFFSET;
do_div(t, 10000000);
return (ntfs_time_t)t;
}
 
/* Convert the Unix UTC into NT UTC. */
ntfs_time64_t ntfs_unixutc2ntutc(ntfs_time_t t)
{
/* Convert to 100ns intervals and then add the NTFS time offset. */
return (ntfs_time64_t)t * 10000000 + NTFS_TIME_OFFSET;
}
 
#undef NTFS_TIME_OFFSET
 
/* Fill index name. */
void ntfs_indexname(char *buf, int type)
{
char hex[] = "0123456789ABCDEF";
int index;
*buf++ = '$';
*buf++ = 'I';
for (index = 24; index > 0; index -= 4)
if ((0xF << index) & type)
break;
while (index >= 0) {
*buf++ = hex[(type >> index) & 0xF];
index -= 4;
}
*buf = '\0';
}
 
/dir.c
0,0 → 1,1103
/*
* dir.c
*
 
* Copyright (C) 1999 Steve Dodd
* Copyright (C) 1999 Joseph Malicki
* Copyright (C) 2001 Anton Altaparmakov (AIA)
*/
 
#include "ntfstypes.h"
#include "struct.h"
#include "dir.h"
#include "macros.h"
 
#include <linux/errno.h>
#include "super.h"
#include "inode.h"
#include "attr.h"
#include "support.h"
#include "util.h"
#include <linux/smp_lock.h>
#include <linux/bitops.h>
 
static char I30[] = "$I30";
 
/* An index record should start with INDX, and the last word in each block
* should contain the check value. If it passes, the original values need to
* be restored. */
int ntfs_check_index_record(ntfs_inode *ino, char *record)
{
return ntfs_fixup_record(record, "INDX", ino->u.index.recordsize);
}
 
static inline int ntfs_is_top(ntfs_u64 stack)
{
return stack == 14;
}
 
static int ntfs_pop(ntfs_u64 *stack)
{
static int width[16] = {1,2,1,3,1,2,1,4,1,2,1,3,1,2,1,-1};
int res = -1;
switch (width[*stack & 15]) {
case 1:
res = (int)((*stack & 15) >> 1);
*stack >>= 4;
break;
case 2:
res = (int)(((*stack & 63) >> 2) + 7);
*stack >>= 6;
break;
case 3:
res = (int)(((*stack & 255) >> 3) + 23);
*stack >>= 8;
break;
case 4:
res = (int)(((*stack & 1023) >> 4) + 55);
*stack >>= 10;
break;
default:
ntfs_error("Unknown encoding\n");
}
return res;
}
 
static inline unsigned int ntfs_top(void)
{
return 14;
}
 
static ntfs_u64 ntfs_push(ntfs_u64 stack, int i)
{
if (i < 7)
return (stack << 4) | (i << 1);
if (i < 23)
return (stack << 6) | ((i - 7) << 2) | 1;
if (i < 55)
return (stack << 8) | ((i - 23) << 3) | 3;
if (i < 120)
return (stack << 10) | ((i - 55) << 4) | 7;
ntfs_error("Too many entries\n");
return ~((ntfs_u64)0);
}
 
#if 0
static void ntfs_display_stack(ntfs_u64 stack)
{
while(!ntfs_is_top(stack))
{
printf("%d ", ntfs_pop(&stack));
}
printf("\n");
}
#endif
 
/* True if the entry points to another block of entries. */
static inline int ntfs_entry_has_subnodes(char *entry)
{
return (NTFS_GETU16(entry + 0xc) & 1);
}
 
/* True if it is not the 'end of dir' entry. */
static inline int ntfs_entry_is_used(char *entry)
{
return !(NTFS_GETU16(entry + 0xc) & 2);
}
 
/*
* Removed RACE for allocating index blocks. But stil not too happy.
* There might be more races afterwards. (AIA)
*/
static int ntfs_allocate_index_block(ntfs_iterate_s *walk)
{
ntfs_attribute *allocation, *bitmap = 0;
int error, size, i, bit;
ntfs_u8 *bmap;
ntfs_io io;
ntfs_volume *vol = walk->dir->vol;
 
/* Check for allocation attribute. */
allocation = ntfs_find_attr(walk->dir, vol->at_index_allocation, I30);
if (!allocation) {
ntfs_u8 bmp[8];
/* Create index allocation attribute. */
error = ntfs_create_attr(walk->dir, vol->at_index_allocation,
I30, 0, 0, &allocation);
if (error)
goto err_ret;
ntfs_bzero(bmp, sizeof(bmp));
error = ntfs_create_attr(walk->dir, vol->at_bitmap, I30, bmp,
sizeof(bmp), &bitmap);
if (error)
goto err_ret;
} else
bitmap = ntfs_find_attr(walk->dir, vol->at_bitmap, I30);
if (!bitmap) {
ntfs_error("Directory w/o bitmap\n");
error = -EINVAL;
goto err_ret;
}
size = bitmap->size;
bmap = ntfs_malloc(size);
if (!bmap) {
error = -ENOMEM;
goto err_ret;
}
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
try_again:
io.param = bmap;
io.size = size;
error = ntfs_read_attr(walk->dir, vol->at_bitmap, I30, 0, &io);
if (error || (io.size != size && (error = -EIO, 1)))
goto err_fb_out;
/* Allocate a bit. */
for (bit = i = 0; i < size; i++) {
if (bmap[i] == 0xFF)
continue;
bit = ffz(bmap[i]);
if (bit < 8)
break;
}
if (i >= size) {
/* FIXME: Extend bitmap. */
error = -EOPNOTSUPP;
goto err_fb_out;
}
/* Get the byte containing our bit again, now taking the BKL. */
io.param = bmap;
io.size = 1;
lock_kernel();
error = ntfs_read_attr(walk->dir, vol->at_bitmap, I30, i, &io);
if (error || (io.size != 1 && (error = -EIO, 1)))
goto err_unl_out;
if (ntfs_test_and_set_bit(bmap, bit)) {
unlock_kernel();
/* Give other process(es) a chance to finish. */
schedule();
goto try_again;
}
walk->newblock = (i * 8 + bit) * walk->dir->u.index.clusters_per_record;
io.param = bmap;
error = ntfs_write_attr(walk->dir, vol->at_bitmap, I30, i, &io);
if (error || (io.size != size && (error = -EIO, 1)))
goto err_unl_out;
/* Change inode on disk, required when bitmap is resident. */
error = ntfs_update_inode(walk->dir);
if (error)
goto err_unl_out;
unlock_kernel();
ntfs_free(bmap);
/* Check whether record is out of allocated range. */
size = allocation->size;
if (walk->newblock * vol->cluster_size >= size) {
/* Build index record. */
int hsize;
int s1 = walk->dir->u.index.recordsize;
int nr_fix = (s1 >> vol->sector_size) + 1;
char *record = ntfs_malloc(s1);
if (!record) {
error = -ENOMEM;
goto err_ret;
}
ntfs_bzero(record, s1);
/* Magic */
ntfs_memcpy(record, "INDX", 4);
/* Offset to fixups */
NTFS_PUTU16(record + 4, 0x28);
/* Number of fixups. */
NTFS_PUTU16(record + 6, nr_fix);
/* Log file sequence number - We don't do journalling so we
* just set it to zero which should be the Right Thing. (AIA) */
NTFS_PUTU64(record + 8, 0);
/* VCN of buffer */
NTFS_PUTU64(record + 0x10, walk->newblock);
/* Header size. */
hsize = 0x10 + 2 * nr_fix;
hsize = (hsize + 7) & ~7; /* Align. */
NTFS_PUTU16(record + 0x18, hsize);
/* Total size of record. */
NTFS_PUTU32(record + 0x20, s1 - 0x18);
/* Writing the data will extend the attribute. */
io.param = record;
io.size = s1;
io.do_read = 0;
error = ntfs_readwrite_attr(walk->dir, allocation, size, &io);
ntfs_free(record);
if (error || (io.size != s1 && (error = -EIO, 1)))
goto err_ret;
error = ntfs_update_inode(walk->dir);
if (error)
goto err_ret;
}
return 0;
err_unl_out:
unlock_kernel();
err_fb_out:
ntfs_free(bmap);
err_ret:
return error;
}
 
/* Write an index block (root or allocation) back to storage.
* Used is the total number of bytes in buf, including all headers. */
static int ntfs_index_writeback(ntfs_iterate_s *walk, ntfs_u8 *buf, int block,
int used)
{
ntfs_io io;
int error;
ntfs_attribute *a;
ntfs_volume *vol = walk->dir->vol;
io.fn_put = 0;
io.fn_get = ntfs_get;
io.param = buf;
if (block == -1) { /* Index root. */
NTFS_PUTU16(buf + 0x14, used - 0x10);
/* 0x18 is a copy thereof. */
NTFS_PUTU16(buf + 0x18, used - 0x10);
io.size = used;
error = ntfs_write_attr(walk->dir, vol->at_index_root, I30, 0,
&io);
if (error || (io.size != used && (error = -EIO, 1)))
return error;
/* Shrink if necessary. */
a = ntfs_find_attr(walk->dir, vol->at_index_root, I30);
ntfs_resize_attr(walk->dir, a, used);
} else {
NTFS_PUTU16(buf + 0x1C, used - 0x18);
io.size = walk->dir->u.index.recordsize;
error = ntfs_insert_fixups(buf, io.size);
if (error) {
printk(KERN_ALERT "NTFS: ntfs_index_writeback() caught "
"corrupt index record ntfs record "
"header. Refusing to write corrupt "
"data to disk. Unmount and run chkdsk "
"immediately!\n");
return -EIO;
}
error = ntfs_write_attr(walk->dir, vol->at_index_allocation,
I30, (__s64)block << vol->cluster_size_bits,
&io);
if (error || (io.size != walk->dir->u.index.recordsize &&
(error = -EIO, 1)))
return error;
}
return 0;
}
 
static int ntfs_split_record(ntfs_iterate_s *walk, char *start, int bsize,
int usize)
{
char *entry, *prev;
ntfs_u8 *newbuf = 0, *middle = 0;
int error, othersize, mlen;
ntfs_io io;
ntfs_volume *vol = walk->dir->vol;
int oldblock;
 
error = ntfs_allocate_index_block(walk);
if (error)
return error;
/* This should not happen. */
if (walk->block == -1) {
ntfs_error("Trying to split root");
return -EOPNOTSUPP;
}
entry = start + NTFS_GETU16(start + 0x18) + 0x18;
for (prev = entry; entry - start < usize / 2;
entry += NTFS_GETU16(entry + 8))
prev = entry;
newbuf = ntfs_malloc(vol->index_record_size);
if (!newbuf)
return -ENOMEM;
io.fn_put = ntfs_put;
io.fn_get = ntfs_get;
io.param = newbuf;
io.size = vol->index_record_size;
/* Read in old header. FIXME: Reading everything is overkill. */
error = ntfs_read_attr(walk->dir, vol->at_index_allocation, I30,
(__s64)walk->newblock << vol->cluster_size_bits, &io);
if (error)
goto out;
if (io.size != vol->index_record_size) {
error = -EIO;
goto out;
}
/* FIXME: Adjust header. */
/* Copy everything from entry to new block. */
othersize = usize - (entry - start);
ntfs_memcpy(newbuf + NTFS_GETU16(newbuf + 0x18) + 0x18, entry,
othersize);
/* Copy flags. */
NTFS_PUTU32(newbuf + 0x24, NTFS_GETU32(start + 0x24));
error = ntfs_index_writeback(walk, newbuf, walk->newblock,
othersize + NTFS_GETU16(newbuf + 0x18) + 0x18);
if (error)
goto out;
/* Move prev to walk. */
mlen = NTFS_GETU16(prev + 0x8);
/* Remember old child node. */
if (ntfs_entry_has_subnodes(prev))
oldblock = NTFS_GETU32(prev + mlen - 8);
else
oldblock = -1;
/* Allow for pointer to subnode. */
middle = ntfs_malloc(ntfs_entry_has_subnodes(prev) ? mlen : mlen + 8);
if (!middle){
error = -ENOMEM;
goto out;
}
ntfs_memcpy(middle, prev, mlen);
/* Set has_subnodes flag. */
NTFS_PUTU8(middle + 0xC, NTFS_GETU8(middle + 0xC) | 1);
/* Middle entry points to block, parent entry will point to newblock. */
NTFS_PUTU64(middle + mlen - 8, walk->block);
if (walk->new_entry)
ntfs_error("Entry not reset");
walk->new_entry = middle;
walk->u.flags |= ITERATE_SPLIT_DONE;
/* Terminate old block. */
othersize = usize - (prev-start);
NTFS_PUTU64(prev, 0);
if (oldblock == -1) {
NTFS_PUTU32(prev + 8, 0x10);
NTFS_PUTU32(prev + 0xC, 2);
othersize += 0x10;
} else {
NTFS_PUTU32(prev + 8, 0x18);
NTFS_PUTU32(prev + 0xC, 3);
NTFS_PUTU64(prev + 0x10, oldblock);
othersize += 0x18;
}
/* Write back original block. */
error = ntfs_index_writeback(walk, start, walk->block, othersize);
out:
if (newbuf)
ntfs_free(newbuf);
if (middle)
ntfs_free(middle);
return error;
}
 
static int ntfs_dir_insert(ntfs_iterate_s *walk, char *start, char* entry)
{
int blocksize, usedsize, error, offset;
int do_split = 0;
offset = entry - start;
if (walk->block == -1) { /* index root */
blocksize = walk->dir->vol->mft_record_size;
usedsize = NTFS_GETU16(start + 0x14) + 0x10;
} else {
blocksize = walk->dir->u.index.recordsize;
usedsize = NTFS_GETU16(start + 0x1C) + 0x18;
}
if (usedsize + walk->new_entry_size > blocksize) {
char* s1 = ntfs_malloc(blocksize + walk->new_entry_size);
if (!s1)
return -ENOMEM;
ntfs_memcpy(s1, start, usedsize);
do_split = 1;
/* Adjust entry to s1. */
entry = s1 + (entry - start);
start = s1;
}
ntfs_memmove(entry + walk->new_entry_size, entry, usedsize - offset);
ntfs_memcpy(entry, walk->new_entry, walk->new_entry_size);
usedsize += walk->new_entry_size;
ntfs_free(walk->new_entry);
walk->new_entry = 0;
if (do_split) {
error = ntfs_split_record(walk, start, blocksize, usedsize);
ntfs_free(start);
} else {
error = ntfs_index_writeback(walk, start, walk->block,usedsize);
if (error)
return error;
}
return 0;
}
 
/* Try to split INDEX_ROOT attributes. Return -E2BIG if nothing changed. */
int ntfs_split_indexroot(ntfs_inode *ino)
{
ntfs_attribute *ra;
ntfs_u8 *root = 0, *index = 0;
ntfs_io io;
int error, off, i, bsize, isize;
ntfs_iterate_s walk;
 
ra = ntfs_find_attr(ino, ino->vol->at_index_root, I30);
if (!ra)
return -ENOTDIR;
bsize = ino->vol->mft_record_size;
root = ntfs_malloc(bsize);
if (!root)
return -E2BIG;
io.fn_put = ntfs_put;
io.param = root;
io.size = bsize;
error = ntfs_read_attr(ino, ino->vol->at_index_root, I30, 0, &io);
if (error)
goto out;
off = 0x20;
/* Count number of entries. */
for (i = 0; ntfs_entry_is_used(root + off); i++)
off += NTFS_GETU16(root + off + 8);
if (i <= 2) {
/* We don't split small index roots. */
error = -E2BIG;
goto out;
}
index = ntfs_malloc(ino->vol->index_record_size);
if (!index) {
error = -ENOMEM;
goto out;
}
walk.dir = ino;
walk.block = -1;
walk.result = walk.new_entry = 0;
walk.name = 0;
error = ntfs_allocate_index_block(&walk);
if (error)
goto out;
/* Write old root to new index block. */
io.param = index;
io.size = ino->vol->index_record_size;
error = ntfs_read_attr(ino, ino->vol->at_index_allocation, I30,
(__s64)walk.newblock << ino->vol->cluster_size_bits, &io);
if (error)
goto out;
isize = NTFS_GETU16(root + 0x18) - 0x10;
ntfs_memcpy(index + NTFS_GETU16(index + 0x18) + 0x18, root+0x20, isize);
/* Copy flags. */
NTFS_PUTU32(index + 0x24, NTFS_GETU32(root + 0x1C));
error = ntfs_index_writeback(&walk, index, walk.newblock,
isize + NTFS_GETU16(index + 0x18) + 0x18);
if (error)
goto out;
/* Mark root as split. */
NTFS_PUTU32(root + 0x1C, 1);
/* Truncate index root. */
NTFS_PUTU64(root + 0x20, 0);
NTFS_PUTU32(root + 0x28, 0x18);
NTFS_PUTU32(root + 0x2C, 3);
NTFS_PUTU64(root + 0x30, walk.newblock);
error = ntfs_index_writeback(&walk, root, -1, 0x38);
out:
ntfs_free(root);
ntfs_free(index);
return error;
}
 
/* The entry has been found. Copy the result in the caller's buffer */
static int ntfs_copyresult(char *dest, char *source)
{
int length = NTFS_GETU16(source + 8);
ntfs_memcpy(dest, source, length);
return 1;
}
 
/* Use $UpCase some day. */
static inline unsigned short ntfs_my_toupper(ntfs_volume *vol, ntfs_u16 x)
{
/* We should read any pending rest of $UpCase here. */
if (x >= vol->upcase_length)
return x;
return vol->upcase[x];
}
 
/* Everything passed in walk and entry. */
static int ntfs_my_strcmp(ntfs_iterate_s *walk, const unsigned char *entry)
{
int lu = *(entry + 0x50);
int i;
 
ntfs_u16* name = (ntfs_u16*)(entry + 0x52);
ntfs_volume *vol = walk->dir->vol;
for (i = 0; i < lu && i < walk->namelen; i++)
if (ntfs_my_toupper(vol, NTFS_GETU16(name + i)) !=
ntfs_my_toupper(vol, NTFS_GETU16(walk->name + i)))
break;
if (i == lu && i == walk->namelen)
return 0;
if (i == lu)
return 1;
if (i == walk->namelen)
return -1;
if (ntfs_my_toupper(vol, NTFS_GETU16(name + i)) <
ntfs_my_toupper(vol, NTFS_GETU16(walk->name + i)))
return 1;
return -1;
}
 
/* Necessary forward declaration. */
static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry);
 
/* Parse a block of entries. Load the block, fix it up, and iterate over the
* entries. The block is given as virtual cluster number. */
static int ntfs_getdir_record(ntfs_iterate_s *walk, int block)
{
int length = walk->dir->u.index.recordsize;
char *record = (char*)ntfs_malloc(length);
char *offset;
int retval,error;
int oldblock;
ntfs_io io;
 
if (!record)
return -ENOMEM;
io.fn_put = ntfs_put;
io.param = record;
io.size = length;
/* Read the block from the index allocation attribute. */
error = ntfs_read_attr(walk->dir, walk->dir->vol->at_index_allocation,
I30, (__s64)block << walk->dir->vol->cluster_size_bits, &io);
if (error || io.size != length) {
ntfs_error("read failed\n");
ntfs_free(record);
return 0;
}
if (!ntfs_check_index_record(walk->dir, record)) {
ntfs_error("%x is not an index record\n", block);
ntfs_free(record);
return 0;
}
offset = record + NTFS_GETU16(record + 0x18) + 0x18;
oldblock = walk->block;
walk->block = block;
retval = ntfs_getdir_iterate(walk, record, offset);
walk->block = oldblock;
ntfs_free(record);
return retval;
}
 
/* Go down to the next block of entries. These collate before the current
* entry. */
static int ntfs_descend(ntfs_iterate_s *walk, ntfs_u8 *start, ntfs_u8 *entry)
{
int length = NTFS_GETU16(entry + 8);
int nextblock = NTFS_GETU32(entry + length - 8);
int error;
 
if (!ntfs_entry_has_subnodes(entry)) {
ntfs_error("illegal ntfs_descend call\n");
return 0;
}
error = ntfs_getdir_record(walk, nextblock);
if (!error && walk->type == DIR_INSERT &&
(walk->u.flags & ITERATE_SPLIT_DONE)) {
/* Split has occurred. Adjust entry, insert new_entry. */
NTFS_PUTU32(entry + length - 8, walk->newblock);
/* Reset flags, as the current block might be split again. */
walk->u.flags &= ~ITERATE_SPLIT_DONE;
error = ntfs_dir_insert(walk, start, entry);
}
return error;
}
 
static int ntfs_getdir_iterate_byposition(ntfs_iterate_s *walk, char* start,
char *entry)
{
int retval = 0;
int curpos = 0, destpos = 0;
int length;
if (walk->u.pos != 0) {
if (ntfs_is_top(walk->u.pos))
return 0;
destpos = ntfs_pop(&walk->u.pos);
}
while (1) {
if (walk->u.pos == 0) {
if (ntfs_entry_has_subnodes(entry))
ntfs_descend(walk, start, entry);
else
walk->u.pos = ntfs_top();
if (ntfs_is_top(walk->u.pos) &&
!ntfs_entry_is_used(entry))
return 1;
walk->u.pos = ntfs_push(walk->u.pos, curpos);
return 1;
}
if (curpos == destpos) {
if (!ntfs_is_top(walk->u.pos) &&
ntfs_entry_has_subnodes(entry)) {
retval = ntfs_descend(walk, start, entry);
if (retval) {
walk->u.pos = ntfs_push(walk->u.pos,
curpos);
return retval;
}
if (!ntfs_entry_is_used(entry))
return 0;
walk->u.pos = 0;
}
if (ntfs_entry_is_used(entry)) {
retval = ntfs_copyresult(walk->result, entry);
walk->u.pos = 0;
} else {
walk->u.pos = ntfs_top();
return 0;
}
}
curpos++;
if (!ntfs_entry_is_used(entry))
break;
length = NTFS_GETU16(entry + 8);
if (!length) {
ntfs_error("infinite loop\n");
break;
}
entry += length;
}
return -1;
}
/* Iterate over a list of entries, either from an index block, or from the
* index root.
* If searching BY_POSITION, pop the top index from the position. If the
* position stack is empty then, return the item at the index and set the
* position to the next entry. If the position stack is not empty,
* recursively proceed for subnodes. If the entry at the position is the
* 'end of dir' entry, return 'not found' and the empty stack.
* If searching BY_NAME, walk through the items until found or until
* one item is collated after the requested item. In the former case, return
* the result. In the latter case, recursively proceed to the subnodes.
* If 'end of dir' is reached, the name is not in the directory */
static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry)
{
int length;
int cmp;
 
if (walk->type == BY_POSITION)
return ntfs_getdir_iterate_byposition(walk, start, entry);
do {
/* If the current entry is a real one, compare with the
* requested item. If the current entry is the last item, it
* is always larger than the requested item. */
cmp = ntfs_entry_is_used(entry) ?
ntfs_my_strcmp(walk,entry) : -1;
switch (walk->type) {
case BY_NAME:
switch (cmp) {
case -1:
return ntfs_entry_has_subnodes(entry) ?
ntfs_descend(walk, start, entry) : 0;
case 0:
return ntfs_copyresult(walk->result, entry);
case 1:
break;
}
break;
case DIR_INSERT:
switch (cmp) {
case -1:
return ntfs_entry_has_subnodes(entry) ?
ntfs_descend(walk, start, entry) :
ntfs_dir_insert(walk, start, entry);
case 0:
return -EEXIST;
case 1:
break;
}
break;
default:
ntfs_error("TODO\n"); /* FIXME: ? */
}
if (!ntfs_entry_is_used(entry))
break;
length = NTFS_GETU16(entry + 8);
if (!length) {
ntfs_error("infinite loop\n");
break;
}
entry += length;
} while (1);
return 0;
}
 
/* Tree walking is done using position numbers. The following numbers have a
* special meaning:
* 0 start (.)
* -1 no more entries
* -2 ..
* All other numbers encode sequences of indices. The sequence a, b, c is
* encoded as <stop><c><b><a>, where <foo> is the encoding of foo. The
* first few integers are encoded as follows:
* 0: 0000 1: 0010 2: 0100 3: 0110
* 4: 1000 5: 1010 6: 1100 stop: 1110
* 7: 000001 8: 000101 9: 001001 10: 001101
* The least significant bits give the width of this encoding, the other bits
* encode the value, starting from the first value of the interval.
* tag width first value last value
* 0 3 0 6
* 01 4 7 22
* 011 5 23 54
* 0111 6 55 119
* More values are hopefully not needed, as the file position has currently
* 64 bits in total. */
 
/* Find an entry in the directory. Return 0 if not found, otherwise copy the
* entry to the result buffer. */
int ntfs_getdir(ntfs_iterate_s *walk)
{
int length = walk->dir->vol->mft_record_size;
int retval, error;
/* Start at the index root. */
char *root = ntfs_malloc(length);
ntfs_io io;
 
if (!root)
return -ENOMEM;
io.fn_put = ntfs_put;
io.param = root;
io.size = length;
error = ntfs_read_attr(walk->dir, walk->dir->vol->at_index_root, I30,
0, &io);
if (error) {
ntfs_error("Not a directory\n");
return 0;
}
walk->block = -1;
/* FIXME: Move these to walk. */
walk->dir->u.index.recordsize = NTFS_GETU32(root + 0x8);
walk->dir->u.index.clusters_per_record = NTFS_GETU32(root + 0xC);
/* FIXME: Consistency check. */
/* Skip header. */
retval = ntfs_getdir_iterate(walk, root, root + 0x20);
ntfs_free(root);
return retval;
}
 
/* Find an entry in the directory by its position stack. Iteration starts
* if the stack is 0, in which case the position is set to the first item
* in the directory. If the position is nonzero, return the item at the
* position and change the position to the next item. The position is -1
* if there are no more items. */
int ntfs_getdir_byposition(ntfs_iterate_s *walk)
{
walk->type = BY_POSITION;
return ntfs_getdir(walk);
}
 
/* Find an entry in the directory by its name. Return 0 if not found. */
int ntfs_getdir_byname(ntfs_iterate_s *walk)
{
walk->type = BY_NAME;
return ntfs_getdir(walk);
}
 
int ntfs_getdir_unsorted(ntfs_inode *ino, u32 *p_high, u32 *p_low,
int (*cb)(ntfs_u8 *, void *), void *param)
{
s64 ib_ofs;
char *buf = 0, *entry = 0;
ntfs_attribute *attr;
ntfs_volume *vol;
int byte, bit, err = 0;
u32 start, finish, ibs, max_size;
ntfs_io io;
u8 ibs_bits;
 
if (!ino) {
ntfs_error("%s(): No inode! Returning -EINVAL.\n",__FUNCTION__);
return -EINVAL;
}
vol = ino->vol;
if (!vol) {
ntfs_error("%s(): Inode 0x%lx has no volume. Returning "
"-EINVAL.\n", __FUNCTION__, ino->i_number);
return -EINVAL;
}
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 1: Entering for inode 0x%lx, "
"p_high = 0x%x, p_low = 0x%x.\n", __FUNCTION__,
ino->i_number, *p_high, *p_low);
if (!*p_high) {
/* We are still in the index root. */
buf = ntfs_malloc(io.size = vol->mft_record_size);
if (!buf)
return -ENOMEM;
io.fn_put = ntfs_put;
io.param = buf;
err = ntfs_read_attr(ino, vol->at_index_root, I30, 0, &io);
if (err || !io.size)
goto read_err_ret;
ino->u.index.recordsize = ibs = NTFS_GETU32(buf + 0x8);
ino->u.index.clusters_per_record = NTFS_GETU32(buf + 0xC);
entry = buf + 0x20;
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 2: In index root.\n",
__FUNCTION__);
ibs_bits = ffs(ibs) - 1;
/* Compensate for faked "." and "..". */
start = 2;
} else { /* We are in an index record. */
io.size = ibs = ino->u.index.recordsize;
buf = ntfs_malloc(ibs);
if (!buf)
return -ENOMEM;
ibs_bits = ffs(ibs) - 1;
io.fn_put = ntfs_put;
io.param = buf;
/*
* 0 is index root, index allocation starts at 1 and works in
* units of index block size (ibs).
*/
ib_ofs = (s64)(*p_high - 1) << ibs_bits;
err = ntfs_read_attr(ino, vol->at_index_allocation, I30, ib_ofs,
&io);
if (err || io.size != ibs)
goto read_err_ret;
if (!ntfs_check_index_record(ino, buf)) {
ntfs_error("%s(): Index block 0x%x is not an index "
"record. Returning -ENOTDIR.\n",
__FUNCTION__, *p_high - 1);
ntfs_free(buf);
return -ENOTDIR;
}
entry = buf + 0x18 + NTFS_GETU16(buf + 0x18);
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 3: In index "
"allocation.\n", __FUNCTION__);
start = 0;
}
/* Process the entries. */
finish = *p_low;
for (; entry < (buf + ibs) && ntfs_entry_is_used(entry);
entry += NTFS_GETU16(entry + 8)) {
if (start < finish) {
/* Skip entries that were already processed. */
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 4: Skipping "
"already processed entry p_high 0x%x, "
"p_low 0x%x.\n", __FUNCTION__, *p_high,
start);
start++;
continue;
}
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 5: Processing entry "
"p_high 0x%x, p_low 0x%x.\n", __FUNCTION__,
*p_high, *p_low);
if ((err = cb(entry, param))) {
/* filldir signalled us to stop. */
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 6: cb returned "
"%i, returning 0, p_high 0x%x, "
"p_low 0x%x.\n", __FUNCTION__, err,
*p_high, *p_low);
ntfs_free(buf);
return 0;
}
++*p_low;
}
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 7: After processing entries, "
"p_high 0x%x, p_low 0x%x.\n", __FUNCTION__, *p_high,
*p_low);
/* We have to locate the next record. */
ntfs_free(buf);
buf = 0;
*p_low = 0;
attr = ntfs_find_attr(ino, vol->at_bitmap, I30);
if (!attr) {
/* Directory does not have index bitmap and index allocation. */
*p_high = 0x7fff;
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 8: No index allocation. "
"Returning 0, p_high 0x7fff, p_low 0x0.\n",
__FUNCTION__);
return 0;
}
max_size = attr->size;
if (max_size > 0x7fff >> 3) {
ntfs_error("%s(): Directory too large. Visible "
"length is truncated.\n", __FUNCTION__);
max_size = 0x7fff >> 3;
}
buf = ntfs_malloc(max_size);
if (!buf)
return -ENOMEM;
io.param = buf;
io.size = max_size;
err = ntfs_read_attr(ino, vol->at_bitmap, I30, 0, &io);
if (err || io.size != max_size)
goto read_err_ret;
attr = ntfs_find_attr(ino, vol->at_index_allocation, I30);
if (!attr) {
ntfs_free(buf);
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 9: Find attr failed. "
"Returning -EIO.\n", __FUNCTION__);
return -EIO;
}
if (attr->resident) {
ntfs_free(buf);
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 9.5: IA is resident. Not"
" allowed. Returning EINVAL.\n", __FUNCTION__);
return -EINVAL;
}
/* Loop while going through non-allocated index records. */
max_size <<= 3;
while (1) {
if (++*p_high >= 0x7fff) {
ntfs_error("%s(): Unsorted 10: Directory "
"inode 0x%lx overflowed the maximum "
"number of index allocation buffers "
"the driver can cope with. Pretending "
"to be at end of directory.\n",
__FUNCTION__, ino->i_number);
goto fake_eod;
}
if (*p_high > max_size || (s64)*p_high << ibs_bits >
attr->initialized) {
fake_eod:
/* No more index records. */
*p_high = 0x7fff;
*p_low = 0;
ntfs_free(buf);
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 10.5: No more "
"index records. Returning 0, p_high "
"0x7fff, p_low 0.\n", __FUNCTION__);
return 0;
}
byte = (ntfs_cluster_t)(*p_high - 1);
bit = 1 << (byte & 7);
byte >>= 3;
if ((buf[byte] & bit))
break;
};
ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 11: Done. Returning 0, p_high "
"0x%x, p_low 0x%x.\n", __FUNCTION__, *p_high, *p_low);
ntfs_free(buf);
return 0;
read_err_ret:
if (!err)
err = -EIO;
ntfs_error("%s(): Read failed. Returning error code %i.\n",
__FUNCTION__, err);
ntfs_free(buf);
return err;
}
 
int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name)
{
ntfs_iterate_s walk;
int nsize, esize;
ntfs_u8* entry, *ndata;
int error;
 
walk.type = DIR_INSERT;
walk.dir = dir;
walk.u.flags = 0;
nsize = name->size;
ndata = name->d.data;
walk.name = (ntfs_u16*)(ndata + 0x42);
walk.namelen = NTFS_GETU8(ndata + 0x40);
walk.new_entry_size = esize = (nsize + 0x10 + 7) & ~7;
walk.new_entry = entry = ntfs_malloc(esize);
if (!entry)
return -ENOMEM;
NTFS_PUTINUM(entry, new);
NTFS_PUTU16(entry + 0x8, esize); /* Size of entry. */
NTFS_PUTU16(entry + 0xA, nsize); /* Size of original name attribute. */
NTFS_PUTU16(entry + 0xC, 0); /* Flags. */
NTFS_PUTU16(entry + 0xE, 0); /* Reserved. */
ntfs_memcpy(entry + 0x10, ndata, nsize);
ntfs_bzero(entry + 0x10 + nsize, esize - 0x10 - nsize);
error = ntfs_getdir(&walk);
if (walk.new_entry)
ntfs_free(walk.new_entry);
return error;
}
 
#if 0
int ntfs_dir_add1(ntfs_inode *dir, const char* name, int namelen,
ntfs_inode *ino)
{
ntfs_iterate_s walk;
int error;
int nsize;
char *entry;
ntfs_attribute *name_attr;
error = ntfs_decodeuni(dir->vol, name, namelen, &walk.name,
&walk.namelen);
if (error)
return error;
/* FIXME: Set flags. */
walk.type = DIR_INSERT;
walk.dir = dir;
/* walk.new = ino; */
/* Prepare new entry. */
/* Round up to a multiple of 8. */
walk.new_entry_size = nsize = ((0x52 + 2 * walk.namelen + 7) / 8) * 8;
walk.new_entry = entry = ntfs_malloc(nsize);
if (!entry)
return -ENOMEM;
ntfs_bzero(entry, nsize);
NTFS_PUTINUM(entry, ino);
NTFS_PUTU16(entry + 8, nsize);
NTFS_PUTU16(entry + 0xA, 0x42 + 2 * namelen); /* FIXME: Size of name
* attribute. */
NTFS_PUTU32(entry + 0xC, 0); /* FIXME: D-F? */
name_attr = ntfs_find_attr(ino, vol->at_file_name, 0);
/* FIXME: multiple names */
if (!name_attr || !name_attr->resident)
return -EIDRM;
/* Directory, file stamps, sizes, filename. */
ntfs_memcpy(entry + 0x10, name_attr->d.data, 0x42 + 2 * namelen);
error = ntfs_getdir(&walk);
ntfs_free(walk.name);
return error;
}
#endif
 
/* Fills out and creates an INDEX_ROOT attribute. */
int ntfs_add_index_root(ntfs_inode *ino, int type)
{
ntfs_attribute *da;
ntfs_u8 data[0x30]; /* 0x20 header, 0x10 last entry. */
char name[10];
 
NTFS_PUTU32(data, type);
/* Collation rule. 1 == COLLATION_FILENAME */
NTFS_PUTU32(data + 4, 1);
NTFS_PUTU32(data + 8, ino->vol->index_record_size);
NTFS_PUTU32(data + 0xC, ino->vol->index_clusters_per_record);
/* Byte offset to first INDEX_ENTRY. */
NTFS_PUTU32(data + 0x10, 0x10);
/* Size of entries, including header. */
NTFS_PUTU32(data + 0x14, 0x20);
NTFS_PUTU32(data + 0x18, 0x20);
/* No index allocation, yet. */
NTFS_PUTU32(data + 0x1C, 0);
/* Add last entry. */
/* Indexed MFT record. */
NTFS_PUTU64(data + 0x20, 0);
/* Size of entry. */
NTFS_PUTU32(data + 0x28, 0x10);
/* Flags: Last entry, no child nodes. */
NTFS_PUTU32(data + 0x2C, 2);
/* Compute name. */
ntfs_indexname(name, type);
return ntfs_create_attr(ino, ino->vol->at_index_root, name,
data, sizeof(data), &da);
}
 
int ntfs_mkdir(ntfs_inode *dir, const char *name, int namelen,
ntfs_inode *result)
{
int error;
error = ntfs_alloc_inode(dir, result, name, namelen, NTFS_AFLAG_DIR);
if (error)
goto out;
error = ntfs_add_index_root(result, 0x30);
if (error)
goto out;
/* Set directory bit. */
result->attr[0x16] |= 2;
error = ntfs_update_inode(dir);
if (error)
goto out;
error = ntfs_update_inode(result);
if (error)
goto out;
out:
return error;
}
 
/attr.h
0,0 → 1,38
/*
* attr.h - Header file for attr.c
*
 
* Copyright (c) 2001 Anton Altaparmakov (AIA)
*/
#include <linux/nls.h>
 
ntfs_u8* ntfs_find_attr_in_mft_rec(ntfs_volume *vol, ntfs_u8 *m, __u32 type,
wchar_t *name, __u32 name_len, int ic, __u16 instance);
 
int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, const __s64 len);
 
int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 newsize);
 
int ntfs_insert_attribute(ntfs_inode *ino, unsigned char* attrdata);
 
int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
ntfs_io *dest);
 
int ntfs_write_compressed(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
ntfs_io *dest);
 
int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data,
int dsize, ntfs_attribute **rattr);
 
int ntfs_read_zero(ntfs_io *dest, int size);
 
int ntfs_make_attr_nonresident(ntfs_inode *ino, ntfs_attribute *attr);
 
int ntfs_attr_allnonresident(ntfs_inode *ino);
 
int ntfs_new_attr(ntfs_inode *ino, int type, void *name, int namelen,
void *value, int value_len, int *pos, int *found);
 
int ntfs_insert_run(ntfs_attribute *attr, int cnum, ntfs_cluster_t cluster,
int len);
 
/sysctl.c
0,0 → 1,55
/*
* sysctl.c - System control stuff
*
 
 
*/
 
#include "sysctl.h"
 
#ifdef DEBUG
#include <linux/locks.h>
#include <linux/sysctl.h>
 
int ntdebug = 0;
 
/* Add or remove the debug sysctl
* Is this really the only file system with sysctls ?
*/
void ntfs_sysctl(int add)
{
#define FS_NTFS 1
/* Definition of the sysctl */
static ctl_table ntfs_sysctls[]={
{FS_NTFS, /* ID */
"ntfs-debug", /* name in /proc */
&ntdebug,sizeof(ntdebug), /* data ptr, data size */
0644, /* mode */
0, /* child */
proc_dointvec, /* proc handler */
0, /* strategy */
0, /* proc control block */
0,0}, /* extra */
{0}
};
/* Define the parent file : /proc/sys/fs */
static ctl_table sysctls_root[]={
{CTL_FS,
"fs",
NULL,0,
0555,
ntfs_sysctls},
{0}
};
static struct ctl_table_header *sysctls_root_header = NULL;
 
if(add){
if(!sysctls_root_header)
sysctls_root_header = register_sysctl_table(sysctls_root, 0);
} else if(sysctls_root_header) {
unregister_sysctl_table(sysctls_root_header);
sysctls_root_header = NULL;
}
}
#endif /* DEBUG */
 
/support.h
0,0 → 1,89
/*
* support.h - Header file for specific support.c
*
 
* Copyright (c) 2001 Anton Altaparmakov (AIA)
*/
 
/* Debug levels */
#define DEBUG_OTHER 1
#define DEBUG_MALLOC 2
#define DEBUG_BSD 4
#define DEBUG_LINUX 8
#define DEBUG_DIR1 16
#define DEBUG_DIR2 32
#define DEBUG_DIR3 64
#define DEBUG_FILE1 128
#define DEBUG_FILE2 256
#define DEBUG_FILE3 512
#define DEBUG_NAME1 1024
#define DEBUG_NAME2 2048
 
#ifdef DEBUG
void ntfs_debug(int mask, const char *fmt, ...);
#else
#define ntfs_debug(mask, fmt...) do {} while (0)
#endif
 
#include <linux/slab.h>
#include <linux/vmalloc.h>
 
#define ntfs_malloc(size) kmalloc(size, GFP_KERNEL)
 
#define ntfs_free(ptr) kfree(ptr)
 
/**
* ntfs_vmalloc - allocate memory in multiples of pages
* @size number of bytes to allocate
*
* Allocates @size bytes of memory, rounded up to multiples of PAGE_SIZE and
* returns a pointer to the allocated memory.
*
* If there was insufficient memory to complete the request, return NULL.
*/
static inline void *ntfs_vmalloc(unsigned long size)
{
if (size <= PAGE_SIZE) {
if (size) {
/* kmalloc() has per-CPU caches so if faster for now. */
return kmalloc(PAGE_SIZE, GFP_NOFS);
/* return (void *)__get_free_page(GFP_NOFS |
__GFP_HIGHMEM); */
}
BUG();
}
if (size >> PAGE_SHIFT < num_physpages)
return __vmalloc(size, GFP_NOFS | __GFP_HIGHMEM, PAGE_KERNEL);
return NULL;
}
 
static inline void ntfs_vfree(void *addr)
{
if ((unsigned long)addr < VMALLOC_START) {
return kfree(addr);
/* return free_page((unsigned long)addr); */
}
vfree(addr);
}
 
void ntfs_bzero(void *s, int n);
 
void ntfs_memcpy(void *dest, const void *src, ntfs_size_t n);
 
void ntfs_memmove(void *dest, const void *src, ntfs_size_t n);
 
void ntfs_error(const char *fmt,...);
 
int ntfs_read_mft_record(ntfs_volume *vol, int mftno, char *buf);
 
int ntfs_getput_clusters(ntfs_volume *pvol, int cluster, ntfs_size_t offs,
ntfs_io *buf);
 
ntfs_time64_t ntfs_now(void);
 
int ntfs_dupuni2map(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
int *out_len);
 
int ntfs_dupmap2uni(ntfs_volume *vol, char* in, int in_len, ntfs_u16 **out,
int *out_len);
 
/util.h
0,0 → 1,56
/*
* util.h - Header file for util.c
*
 
* Copyright (C) 2001 Anton Altaparmakov (AIA)
*/
 
/* The first 16 inodes correspond to NTFS special files. */
typedef enum {
FILE_Mft = 0,
FILE_MftMirr = 1,
FILE_LogFile = 2,
FILE_Volume = 3,
FILE_AttrDef = 4,
FILE_root = 5,
FILE_BitMap = 6,
FILE_Boot = 7,
FILE_BadClus = 8,
FILE_Secure = 9,
FILE_UpCase = 10,
FILE_Extend = 11,
FILE_Reserved12 = 12,
FILE_Reserved13 = 13,
FILE_Reserved14 = 14,
FILE_Reserved15 = 15,
} NTFS_SYSTEM_FILES;
/* Memory management */
void *ntfs_calloc(int size);
 
/* String operations */
/* Copy Unicode <-> ASCII */
void ntfs_ascii2uni(short int *to, char *from, int len);
 
/* Comparison */
int ntfs_uni_strncmp(short int* a, short int *b, int n);
int ntfs_ua_strncmp(short int* a, char* b, int n);
 
/* Same address space copies */
void ntfs_put(ntfs_io *dest, void *src, ntfs_size_t n);
void ntfs_get(void* dest, ntfs_io *src, ntfs_size_t n);
 
/* Charset conversion */
int ntfs_encodeuni(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
int *out_len);
int ntfs_decodeuni(ntfs_volume *vol, char *in, int in_len, ntfs_u16 **out,
int *out_len);
 
/* Time conversion */
/* NT <-> Unix */
ntfs_time_t ntfs_ntutc2unixutc(ntfs_time64_t ntutc);
ntfs_time64_t ntfs_unixutc2ntutc(ntfs_time_t t);
 
/* Attribute names */
void ntfs_indexname(char *buf, int type);
 
/dir.h
0,0 → 1,48
/*
* dir.h - Header file for dir.c
*
 
*/
#define ITERATE_SPLIT_DONE 1
 
enum ntfs_iterate_e {
BY_POSITION,
BY_NAME,
DIR_INSERT
};
 
/* not all fields are used for all operations */
typedef struct ntfs_iterate_s {
enum ntfs_iterate_e type;
ntfs_inode *dir;
union{
ntfs_u64 pos;
int flags;
}u;
char *result; /* pointer to entry if found */
ntfs_u16* name;
int namelen;
int block; /* current index record */
int newblock; /* index record created in a split */
char *new_entry;
int new_entry_size;
/*ntfs_inode* new;*/
} ntfs_iterate_s;
 
int ntfs_getdir_unsorted(ntfs_inode *ino, ntfs_u32 *p_high, ntfs_u32* p_low,
int (*cb)(ntfs_u8*, void*), void *param);
 
int ntfs_getdir_byname(ntfs_iterate_s *walk);
 
int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name);
 
int ntfs_check_index_record(ntfs_inode *ino, char *record);
 
int ntfs_getdir_byposition(ntfs_iterate_s *walk);
 
int ntfs_mkdir(ntfs_inode* dir,const char* name,int namelen, ntfs_inode *ino);
 
int ntfs_split_indexroot(ntfs_inode *ino);
 
int ntfs_add_index_root(ntfs_inode *ino, int type);
 
/unistr.c
0,0 → 1,167
/*
* unistr.c - Unicode string handling. 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 <linux/string.h>
#include <asm/byteorder.h>
 
#include "unistr.h"
#include "macros.h"
 
/*
* This is used by the name collation functions to quickly determine what
* characters are (in)valid.
*/
const __u8 legal_ansi_char_array[0x40] = {
0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
 
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
 
0x17, 0x07, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17,
0x17, 0x17, 0x18, 0x16, 0x16, 0x17, 0x07, 0x00,
 
0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
0x17, 0x17, 0x04, 0x16, 0x18, 0x16, 0x18, 0x18,
};
 
/**
* ntfs_are_names_equal - compare two Unicode names for equality
* @s1: name to compare to @s2
* @s1_len: length in Unicode characters of @s1
* @s2: name to compare to @s1
* @s2_len: length in Unicode characters of @s2
* @ic: ignore case bool
* @upcase: upcase table (only if @ic == IGNORE_CASE)
* @upcase_size: length in Unicode characters of @upcase (if present)
*
* Compare the names @s1 and @s2 and return TRUE (1) if the names are
* identical, or FALSE (0) if they are not identical. If @ic is IGNORE_CASE,
* the @upcase table is used to performa a case insensitive comparison.
*/
int ntfs_are_names_equal(wchar_t *s1, size_t s1_len,
wchar_t *s2, size_t s2_len, int ic,
wchar_t *upcase, __u32 upcase_size)
{
if (s1_len != s2_len)
return 0;
if (!ic)
return memcmp(s1, s2, s1_len << 1) ? 0: 1;
return ntfs_wcsncasecmp(s1, s2, s1_len, upcase, upcase_size) ? 0: 1;
}
 
/**
* ntfs_collate_names - collate two Unicode names
* @upcase: upcase table (ignored if @ic is CASE_SENSITIVE)
* @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE)
* @name1: first Unicode name to compare
* @name2: second Unicode name to compare
* @ic: either CASE_SENSITIVE or IGNORE_CASE
* @err_val: if @name1 contains an invalid character return this value
*
* ntfs_collate_names collates two Unicode names and returns:
*
* -1 if the first name collates before the second one,
* 0 if the names match,
* 1 if the second name collates before the first one, or
* @ec if an invalid character is encountered in @name1 during the comparison.
*
* The following characters are considered invalid: '"', '*', '<', '>' and '?'.
*/
int ntfs_collate_names(wchar_t *upcase, __u32 upcase_len,
wchar_t *name1, __u32 name1_len,
wchar_t *name2, __u32 name2_len,
int ic, int err_val)
{
__u32 cnt, min_len;
wchar_t c1, c2;
 
min_len = name1_len;
if (min_len > name2_len)
min_len = name2_len;
for (cnt = 0; cnt < min_len; ++cnt) {
c1 = le16_to_cpu(*name1++);
c2 = le16_to_cpu(*name2++);
if (ic) {
if (c1 < upcase_len)
c1 = le16_to_cpu(upcase[c1]);
if (c2 < upcase_len)
c2 = le16_to_cpu(upcase[c2]);
}
if (c1 < 64 && legal_ansi_char_array[c1] & 8)
return err_val;
if (c1 < c2)
return -1;
if (c1 > c2)
return 1;
++name1;
++name2;
}
if (name1_len < name2_len)
return -1;
if (name1_len == name2_len)
return 0;
/* name1_len > name2_len */
c1 = le16_to_cpu(*name1);
if (c1 < 64 && legal_ansi_char_array[c1] & 8)
return err_val;
return 1;
}
 
/**
* ntfs_wcsncasecmp - compare two little endian Unicode strings, ignoring case
* @s1: first string
* @s2: second string
* @n: maximum unicode characters to compare
* @upcase: upcase table
* @upcase_size: upcase table size in Unicode characters
*
* Compare the first @n characters of the Unicode strings @s1 and @s2,
* ignoring case. The strings in little endian format and appropriate
* le16_to_cpu() conversion is performed on non-little endian machines.
*
* Each character is uppercased using the @upcase table before the comparison.
*
* The function returns an integer less than, equal to, or greater than zero
* if @s1 (or the first @n Unicode characters thereof) is found, respectively,
* to be less than, to match, or be greater than @s2.
*/
int ntfs_wcsncasecmp(wchar_t *s1, wchar_t *s2, size_t n,
wchar_t *upcase, __u32 upcase_size)
{
wchar_t c1, c2;
size_t i;
 
for (i = 0; i < n; ++i) {
if ((c1 = le16_to_cpu(s1[i])) < upcase_size)
c1 = le16_to_cpu(upcase[c1]);
if ((c2 = le16_to_cpu(s2[i])) < upcase_size)
c2 = le16_to_cpu(upcase[c2]);
if (c1 < c2)
return -1;
if (c1 > c2)
return 1;
if (!c1)
break;
}
return 0;
}
 
/sysctl.h
0,0 → 1,17
/*
* sysctl.h - Header file for sysctl.c
*
 
 
*/
 
#ifdef DEBUG
extern int ntdebug;
 
void ntfs_sysctl(int add);
 
#define SYSCTL(x) ntfs_sysctl(x)
#else
#define SYSCTL(x)
#endif /* DEBUG */
 
/macros.h
0,0 → 1,43
/*
* macros.h
*
 
 
* Copyright (c) 2001 Anton Altaparmakov
*/
#include <linux/ntfs_fs_i.h>
#include <linux/fs.h>
#include <asm/page.h>
 
#define NTFS_FD(vol) ((vol)->u.fd)
 
#define NTFS_SB(vol) ((struct super_block*)(vol)->sb)
#define NTFS_SB2VOL(sb) (&(sb)->u.ntfs_sb)
#define NTFS_INO2VOL(ino) (&((ino)->i_sb->u.ntfs_sb))
#define NTFS_LINO2NINO(ino) ((struct ntfs_inode_info*)(&((ino)->u.ntfs_i)))
static inline struct inode *VFS_I(struct ntfs_inode_info *ntfs_ino)
{
struct inode *i = (struct inode*)((char*)ntfs_ino -
((char*)&(((struct inode*)NULL)->u.ntfs_i) -
(char*)NULL));
#ifdef DEBUG
if ((char*)NTFS_LINO2NINO(i) != (char*)ntfs_ino)
BUG();
#endif
return i;
}
 
#define IS_MAGIC(a,b) (*(int*)(a) == *(int*)(b))
#define IS_MFT_RECORD(a) IS_MAGIC((a),"FILE")
#define IS_INDEX_RECORD(a) IS_MAGIC((a),"INDX")
 
/* 'NTFS' in little endian */
#define NTFS_SUPER_MAGIC 0x5346544E
 
#define NTFS_AFLAG_RO 1
#define NTFS_AFLAG_HIDDEN 2
#define NTFS_AFLAG_SYSTEM 4
#define NTFS_AFLAG_ARCHIVE 20
#define NTFS_AFLAG_COMPRESSED 0x800
#define NTFS_AFLAG_DIR 0x10000000
 
/unistr.h
0,0 → 1,44
/*
* unistr.h - Exports for unicode string handling. 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
*/
 
#ifndef _LINUX_NTFS_UNISTR_H
#define _LINUX_NTFS_UNISTR_H
 
#include <linux/types.h>
#include <linux/nls.h>
 
extern const __u8 legal_ansi_char_array[0x40];
 
int ntfs_are_names_equal(wchar_t *s1, size_t s1_len,
wchar_t *s2, size_t s2_len, int ic,
wchar_t *upcase, __u32 upcase_size);
 
int ntfs_collate_names(wchar_t *upcase, __u32 upcase_len,
wchar_t *name1, __u32 name1_len,
wchar_t *name2, __u32 name2_len,
int ic, int err_val);
 
int ntfs_wcsncasecmp(wchar_t *s1, wchar_t *s2, size_t n,
wchar_t *upcase, __u32 upcase_size);
 
#endif /* defined _LINUX_NTFS_UNISTR_H */
 
/struct.h
0,0 → 1,69
/*
* struct.h - Structure definitions
*
 
* Copyright (C) 2000-2001 Anton Altaparmakov (AIA)
*/
#include <linux/ntfs_fs.h>
 
/* Necessary forward definition. */
struct ntfs_inode;
 
/* Which files should be returned from a director listing. */
#define ngt_dos 1 /* only short names, no system files */
#define ngt_nt 2 /* only long names, all-uppercase becomes
* all-lowercase, no system files */
#define ngt_posix 3 /* all names except system files */
#define ngt_full 4 /* all entries */
 
typedef struct ntfs_sb_info ntfs_volume;
 
typedef struct {
ntfs_cluster_t lcn;
ntfs_cluster_t len;
} ntfs_runlist;
 
typedef struct ntfs_attribute {
int type;
ntfs_u16 *name;
int namelen;
int attrno;
__s64 size, allocated, initialized, compsize;
ATTR_FLAGS flags;
__u8 resident, indexed;
int cengine;
union {
void *data; /* if resident */
struct {
ntfs_runlist *runlist;
unsigned long len;
} r;
} d;
} ntfs_attribute;
 
typedef struct ntfs_inode_info ntfs_inode;
 
/* Structure to define IO to user buffer. do_read means that the destination
* has to be written using fn_put, do_write means that the destination has to
* read using fn_get. So, do_read is from a user's point of view, while put and
* get are from the driver's point of view. The first argument is always the
* destination of the IO. */
typedef struct ntfs_io{
int do_read;
void (*fn_put)(struct ntfs_io *dest, void *buf, ntfs_size_t);
void (*fn_get)(void *buf, struct ntfs_io *src, ntfs_size_t len);
void *param;
unsigned long size;
} ntfs_io;
 
#if 0
typedef struct {
ntfs_volume *vol;
ntfs_inode *ino;
int type;
char *name;
int mftno;
int start_vcn;
} ntfs_attrlist_item;
#endif
 
/Makefile
0,0 → 1,11
# Rules for making the NTFS driver
 
O_TARGET := ntfs.o
 
obj-y := fs.o sysctl.o support.o util.o inode.o dir.o super.o attr.o unistr.o
obj-m := $(O_TARGET)
# New version format started 3 February 2001.
EXTRA_CFLAGS = -DNTFS_VERSION=\"1.1.22\" #-DDEBUG
 
include $(TOPDIR)/Rules.make
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.