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 = ¶m; |
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 |
|