diff --git a/include/ntfs/Makefile.am b/include/ntfs/Makefile.am index 2725ae93..64bf2cc1 100644 --- a/include/ntfs/Makefile.am +++ b/include/ntfs/Makefile.am @@ -1,38 +1,10 @@ linux_ntfsincludedir = $(includedir)/ntfs linux_ntfsinclude_HEADERS = \ - attrib.h \ - attrlist.h \ - bitmap.h \ - bootsect.h \ - collate.h \ - compat.h \ - compress.h \ - debug.h \ - device.h \ - device_io.h \ - dir.h \ - endians.h \ gnome-vfs-method.h \ gnome-vfs-module.h \ - index.h \ - inode.h \ - layout.h \ - lcnalloc.h \ list.h \ - logfile.h \ - logging.h \ - mft.h \ - mst.h \ - ntfstime.h \ rich.h \ - runlist.h \ - security.h \ - support.h \ - tree.h \ - types.h \ - unistr.h \ - version.h \ - volume.h + tree.h MAINTAINERCLEANFILES = Makefile.in diff --git a/include/ntfs/attrib.h b/include/ntfs/attrib.h deleted file mode 100644 index 27b3a833..00000000 --- a/include/ntfs/attrib.h +++ /dev/null @@ -1,352 +0,0 @@ -/* - * attrib.h - Exports for attribute handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2004 Anton Altaparmakov - * Copyright (c) 2004-2005 Yura Pakhuchiy - * - * 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 _NTFS_ATTRIB_H -#define _NTFS_ATTRIB_H - -/* Forward declarations */ -typedef struct _ntfs_attr ntfs_attr; -typedef struct _ntfs_attr_search_ctx ntfs_attr_search_ctx; - -#include "types.h" -#include "inode.h" -#include "unistr.h" -#include "runlist.h" -#include "volume.h" -#include "debug.h" -#include "logging.h" - -extern ntfschar AT_UNNAMED[]; - -/** - * enum ntfs_lcn_special_values - special return values for ntfs_*_vcn_to_lcn() - * - * Special return values for ntfs_rl_vcn_to_lcn() and ntfs_attr_vcn_to_lcn(). - * - * TODO: Describe them. - */ -typedef enum { - LCN_HOLE = -1, /* Keep this as highest value or die! */ - LCN_RL_NOT_MAPPED = -2, - LCN_ENOENT = -3, - LCN_EINVAL = -4, - LCN_EIO = -5, -} ntfs_lcn_special_values; - -/** - * struct ntfs_attr_search_ctx - search context used in attribute search functions - * @mrec: buffer containing mft record to search - * @attr: attribute record in @mrec where to begin/continue search - * @is_first: if true lookup_attr() begins search with @attr, else after @attr - * - * Structure must be initialized to zero before the first call to one of the - * attribute search functions. Initialize @mrec to point to the mft record to - * search, and @attr to point to the first attribute within @mrec (not necessary - * if calling the _first() functions), and set @is_first to TRUE (not necessary - * if calling the _first() functions). - * - * If @is_first is TRUE, the search begins with @attr. If @is_first is FALSE, - * the search begins after @attr. This is so that, after the first call to one - * of the search attribute functions, we can call the function again, without - * any modification of the search context, to automagically get the next - * matching attribute. - */ -struct _ntfs_attr_search_ctx { - MFT_RECORD *mrec; - ATTR_RECORD *attr; - BOOL is_first; - ntfs_inode *ntfs_ino; - ATTR_LIST_ENTRY *al_entry; - ntfs_inode *base_ntfs_ino; - MFT_RECORD *base_mrec; - ATTR_RECORD *base_attr; -}; - -extern void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx); -extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, - MFT_RECORD *mrec); -extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx); - -extern int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const VCN lowest_vcn, const u8 *val, const u32 val_len, - ntfs_attr_search_ctx *ctx); - -extern ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, - const ATTR_TYPES type); - -/** - * ntfs_attrs_walk - syntactic sugar for walking all attributes in an inode - * @ctx: initialised attribute search context - * - * Syntactic sugar for walking attributes in an inode. - * - * Return 0 on success and -1 on error with errno set to the error code from - * ntfs_attr_lookup(). - * - * Example: When you want to enumerate all attributes in an open ntfs inode - * @ni, you can simply do: - * - * int err; - * ntfs_attr_search_ctx *ctx = ntfs_attr_get_search_ctx(ni, NULL); - * if (!ctx) - * // Error code is in errno. Handle this case. - * while (!(err = ntfs_attrs_walk(ctx))) { - * ATTR_RECORD *attr = ctx->attr; - * // attr now contains the next attribute. Do whatever you want - * // with it and then just continue with the while loop. - * } - * if (err && errno != ENOENT) - * // Ooops. An error occurred! You should handle this case. - * // Now finished with all attributes in the inode. - */ -static __inline__ int ntfs_attrs_walk(ntfs_attr_search_ctx *ctx) -{ - return ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0, - NULL, 0, ctx); -} - -/** - * struct ntfs_attr - ntfs in memory non-resident attribute structure - * @rl: if not NULL, the decompressed runlist - * @ni: base ntfs inode to which this attribute belongs - * @type: attribute type - * @name: Unicode name of the attribute - * @name_len: length of @name in Unicode characters - * @state: NTFS attribute specific flags describing this attribute - * @allocated_size: copy from the attribute record - * @data_size: copy from the attribute record - * @initialized_size: copy from the attribute record - * @compressed_size: copy from the attribute record - * @compression_block_size: size of a compression block (cb) - * @compression_block_size_bits: log2 of the size of a cb - * @compression_block_clusters: number of clusters per cb - * - * This structure exists purely to provide a mechanism of caching the runlist - * of an attribute. If you want to operate on a particular attribute extent, - * you should not be using this structure at all. If you want to work with a - * resident attribute, you should not be using this structure at all. As a - * fail-safe check make sure to test NAttrNonResident() and if it is false, you - * know you shouldn't be using this structure. - * - * If you want to work on a resident attribute or on a specific attribute - * extent, you should use ntfs_lookup_attr() to retrieve the attribute (extent) - * record, edit that, and then write back the mft record (or set the - * corresponding ntfs inode dirty for delayed write back). - * - * @rl is the decompressed runlist of the attribute described by this - * structure. Obviously this only makes sense if the attribute is not resident, - * i.e. NAttrNonResident() is true. If the runlist hasn't been decompressed yet - * @rl is NULL, so be prepared to cope with @rl == NULL. - * - * @ni is the base ntfs inode of the attribute described by this structure. - * - * @type is the attribute type (see layout.h for the definition of ATTR_TYPES), - * @name and @name_len are the little endian Unicode name and the name length - * in Unicode characters of the attribute, respectively. - * - * @state contains NTFS attribute specific flags describing this attribute - * structure. See ntfs_attr_state_bits above. - */ -struct _ntfs_attr { - runlist_element *rl; - ntfs_inode *ni; - ATTR_TYPES type; - ntfschar *name; - u32 name_len; - unsigned long state; - s64 allocated_size; - s64 data_size; - s64 initialized_size; - s64 compressed_size; - u32 compression_block_size; - u8 compression_block_size_bits; - u8 compression_block_clusters; -}; - -/** - * enum ntfs_attr_state_bits - bits for the state field in the ntfs_attr structure - */ -typedef enum { - NA_Initialized, /* 1: structure is initialized. */ - NA_NonResident, /* 1: Attribute is not resident. */ -} ntfs_attr_state_bits; - -#define test_nattr_flag(na, flag) test_bit(NA_##flag, (na)->state) -#define set_nattr_flag(na, flag) set_bit(NA_##flag, (na)->state) -#define clear_nattr_flag(na, flag) clear_bit(NA_##flag, (na)->state) - -#define NAttrInitialized(na) test_nattr_flag(na, Initialized) -#define NAttrSetInitialized(na) set_nattr_flag(na, Initialized) -#define NAttrClearInitialized(na) clear_nattr_flag(na, Initialized) - -#define NAttrNonResident(na) test_nattr_flag(na, NonResident) -#define NAttrSetNonResident(na) set_nattr_flag(na, NonResident) -#define NAttrClearNonResident(na) clear_nattr_flag(na, NonResident) - -#define GenNAttrIno(func_name,flag) \ -static inline int NAttr##func_name(ntfs_attr *na) \ -{ \ - if (na->type == AT_DATA && na->name == AT_UNNAMED) \ - return (na->ni->flags & FILE_ATTR_##flag); \ - return 0; \ -} \ -static inline void NAttrSet##func_name(ntfs_attr *na) \ -{ \ - if (na->type == AT_DATA && na->name == AT_UNNAMED) \ - na->ni->flags |= FILE_ATTR_##flag; \ - else \ - ntfs_log_trace("BUG! Should be called only for "\ - "unnamed data attribute.\n"); \ -} \ -static inline void NAttrClear##func_name(ntfs_attr *na) \ -{ \ - if (na->type == AT_DATA && na->name == AT_UNNAMED) \ - na->ni->flags &= ~FILE_ATTR_##flag; \ -} - -GenNAttrIno(Compressed, COMPRESSED) -GenNAttrIno(Encrypted, ENCRYPTED) -GenNAttrIno(Sparse, SPARSE_FILE) - -/** - * union attr_val - Union of all known attribute values - * - * For convenience. Used in the attr structure. - */ -typedef union { - u8 _default; /* Unnamed u8 to serve as default when just using - a_val without specifying any of the below. */ - STANDARD_INFORMATION std_inf; - ATTR_LIST_ENTRY al_entry; - FILE_NAME_ATTR filename; - OBJECT_ID_ATTR obj_id; - SECURITY_DESCRIPTOR_ATTR sec_desc; - VOLUME_NAME vol_name; - VOLUME_INFORMATION vol_inf; - DATA_ATTR data; - INDEX_ROOT index_root; - INDEX_BLOCK index_blk; - BITMAP_ATTR bmp; - REPARSE_POINT reparse; - EA_INFORMATION ea_inf; - EA_ATTR ea; - PROPERTY_SET property_set; - LOGGED_UTILITY_STREAM logged_util_stream; - EFS_ATTR_HEADER efs; -} attr_val; - -extern void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident, - const BOOL compressed, const BOOL encrypted, const BOOL sparse, - const s64 allocated_size, const s64 data_size, - const s64 initialized_size, const s64 compressed_size, - const u8 compression_unit); - -extern ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, - ntfschar *name, u32 name_len); -extern void ntfs_attr_close(ntfs_attr *na); - -extern s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, - void *b); -extern s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, - const void *b); - -extern void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, - ntfschar *name, u32 name_len, s64 *data_size); - -extern s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, - const s64 bk_cnt, const u32 bk_size, void *dst); -extern s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, - s64 bk_cnt, const u32 bk_size, void *src); - -extern int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn); -extern int ntfs_attr_map_whole_runlist(ntfs_attr *na); - -extern LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn); -extern runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn); - -extern int ntfs_attr_size_bounds_check(const ntfs_volume *vol, - const ATTR_TYPES type, const s64 size); -extern int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, - const ATTR_TYPES type); -extern int ntfs_attr_can_be_resident(const ntfs_volume *vol, - const ATTR_TYPES type); - -extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size); - -extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, u8 *val, u32 size, - ATTR_FLAGS flags); -extern int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size, - ATTR_FLAGS flags); -extern int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx); - -extern int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, u8 *val, s64 size); -extern int ntfs_attr_rm(ntfs_attr *na); - -extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size); - -extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, - const u32 new_size); - -extern int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni); -extern int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra); - -extern int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn); - -extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize); - -// FIXME / TODO: Above here the file is cleaned up. (AIA) -/** - * get_attribute_value_length - return the length of the value of an attribute - * @a: pointer to a buffer containing the attribute record - * - * Return the byte size of the attribute value of the attribute @a (as it - * would be after eventual decompression and filling in of holes if sparse). - * If we return 0, check errno. If errno is 0 the actual length was 0, - * otherwise errno describes the error. - * - * FIXME: Describe possible errnos. - */ -s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a); - -/** - * get_attribute_value - return the attribute value of an attribute - * @vol: volume on which the attribute is present - * @a: attribute to get the value of - * @b: destination buffer for the attribute value - * - * Make a copy of the attribute value of the attribute @a into the destination - * buffer @b. Note, that the size of @b has to be at least equal to the value - * returned by get_attribute_value_length(@a). - * - * Return number of bytes copied. If this is zero check errno. If errno is 0 - * then nothing was read due to a zero-length attribute value, otherwise - * errno describes the error. - */ -s64 ntfs_get_attribute_value(const ntfs_volume *vol, const ATTR_RECORD *a, - u8 *b); - -#endif /* defined _NTFS_ATTRIB_H */ - diff --git a/include/ntfs/attrlist.h b/include/ntfs/attrlist.h deleted file mode 100644 index 10dc9724..00000000 --- a/include/ntfs/attrlist.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * attrlist.h - Exports for attribute list attribute handling. Part of the - * Linux-NTFS project. - * - * Copyright (c) 2004 Anton Altaparmakov - * Copyright (c) 2004 Yura Pakhuchiy - * - * 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 _NTFS_ATTRLIST_H -#define _NTFS_ATTRLIST_H - -#include "attrib.h" - -extern int ntfs_attrlist_need(ntfs_inode *ni); - -extern int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr); -extern int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx); - -/** - * ntfs_attrlist_mark_dirty - set the attribute list dirty - * @ni: ntfs inode which base inode contain dirty attribute list - * - * Set the attribute list dirty so it is written out later (at the latest at - * ntfs_inode_close() time). - * - * This function cannot fail. - */ -static __inline__ void ntfs_attrlist_mark_dirty(ntfs_inode *ni) -{ - if (ni->nr_extents == -1) - NInoAttrListSetDirty(ni->base_ni); - else - NInoAttrListSetDirty(ni); -} - -#endif /* defined _NTFS_ATTRLIST_H */ diff --git a/include/ntfs/bitmap.h b/include/ntfs/bitmap.h deleted file mode 100644 index c12a786a..00000000 --- a/include/ntfs/bitmap.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * bitmap.h - Exports for bitmap handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2004 Anton Altaparmakov - * Copyright (c) 2004-2005 Richard Russon - * - * 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 _NTFS_BITMAP_H -#define _NTFS_BITMAP_H - -#include "types.h" -#include "attrib.h" - -/* - * NOTES: - * - * - Operations are 8-bit only to ensure the functions work both on little - * and big endian machines! So don't make them 32-bit ops! - * - bitmap starts at bit = 0 and ends at bit = bitmap size - 1. - * - _Caller_ has to make sure that the bit to operate on is less than the - * size of the bitmap. - */ - -/** - * ntfs_bit_set - set a bit in a field of bits - * @bitmap: field of bits - * @bit: bit to set - * @new_value: value to set bit to (0 or 1) - * - * Set the bit @bit in the @bitmap to @new_value. Ignore all errors. - */ -static __inline__ void ntfs_bit_set(u8 *bitmap, const u64 bit, - const u8 new_value) -{ - if (!bitmap || new_value > 1) - return; - if (!new_value) - bitmap[bit >> 3] &= ~(1 << (bit & 7)); - else - bitmap[bit >> 3] |= (1 << (bit & 7)); -} - -/** - * ntfs_bit_get - get value of a bit in a field of bits - * @bitmap: field of bits - * @bit: bit to get - * - * Get and return the value of the bit @bit in @bitmap (0 or 1). - * Return -1 on error. - */ -static __inline__ char ntfs_bit_get(const u8 *bitmap, const u64 bit) -{ - if (!bitmap) - return -1; - return (bitmap[bit >> 3] >> (bit & 7)) & 1; -} - -static __inline__ void ntfs_bit_change(u8 *bitmap, const u64 bit) -{ - if (!bitmap) - return; - bitmap[bit >> 3] ^= 1 << (bit & 7); -} - -/** - * ntfs_bit_get_and_set - get value of a bit in a field of bits and set it - * @bitmap: field of bits - * @bit: bit to get/set - * @new_value: value to set bit to (0 or 1) - * - * Return the value of the bit @bit and set it to @new_value (0 or 1). - * Return -1 on error. - */ -static __inline__ char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, - const u8 new_value) -{ - register u8 old_bit, shift; - - if (!bitmap || new_value > 1) - return -1; - shift = bit & 7; - old_bit = (bitmap[bit >> 3] >> shift) & 1; - if (new_value != old_bit) - bitmap[bit >> 3] ^= 1 << shift; - return old_bit; -} - -extern int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count); -extern int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count); - -/** - * ntfs_bitmap_set_bit - set a bit in a bitmap - * @na: attribute containing the bitmap - * @bit: bit to set - * - * Set the @bit in the bitmap described by the attribute @na. - * - * On success return 0 and on error return -1 with errno set to the error code. - */ -static __inline__ int ntfs_bitmap_set_bit(ntfs_attr *na, s64 bit) -{ - return ntfs_bitmap_set_run(na, bit, 1); -} - -/** - * ntfs_bitmap_clear_bit - clear a bit in a bitmap - * @na: attribute containing the bitmap - * @bit: bit to clear - * - * Clear @bit in the bitmap described by the attribute @na. - * - * On success return 0 and on error return -1 with errno set to the error code. - */ -static __inline__ int ntfs_bitmap_clear_bit(ntfs_attr *na, s64 bit) -{ - return ntfs_bitmap_clear_run(na, bit, 1); -} - - -#ifdef NTFS_RICH - -#include "layout.h" -#include "inode.h" - -/** - * struct ntfs_bmp - - * - * a cache for either dir/$BITMAP, $MFT/$BITMAP or $Bitmap/$DATA - */ -struct ntfs_bmp { - ntfs_volume *vol; - ntfs_attr *attr; - int count; - u8 **data; - VCN *data_vcn; -}; - - -int ntfs_bmp_rollback(struct ntfs_bmp *bmp); -int ntfs_bmp_commit(struct ntfs_bmp *bmp); -void ntfs_bmp_free(struct ntfs_bmp *bmp); -struct ntfs_bmp * ntfs_bmp_create(ntfs_inode *inode, ATTR_TYPES type, ntfschar *name, int name_len); -int ntfs_bmp_add_data(struct ntfs_bmp *bmp, VCN vcn, u8 *data); -u8 * ntfs_bmp_get_data(struct ntfs_bmp *bmp, VCN vcn); -int ntfs_bmp_set_range(struct ntfs_bmp *bmp, VCN vcn, s64 length, int value); -s64 ntfs_bmp_find_last_set(struct ntfs_bmp *bmp); -int ntfs_bmp_find_space(struct ntfs_bmp *bmp, LCN start, long size); - -#endif /* NTFS_RICH */ - -#endif /* defined _NTFS_BITMAP_H */ - diff --git a/include/ntfs/bootsect.h b/include/ntfs/bootsect.h deleted file mode 100644 index af0da7a9..00000000 --- a/include/ntfs/bootsect.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * bootsect.h - Exports for bootsector record handling. Part of the Linux-NTFS - * project. - * - * Copyright (c) 2000-2002 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 _NTFS_BOOTSECT_H -#define _NTFS_BOOTSECT_H - -#include "types.h" -#include "volume.h" -#include "layout.h" - -/** - * is_boot_sector_ntfs - check a boot sector for describing an ntfs volume - * @b: buffer containing the boot sector - * @silent: if 1 don't display progress information - * - * This function checks the boot sector in @b for describing a valid ntfs - * volume. Return TRUE if @b is a valid NTFS boot sector or FALSE otherwise. - * If silent is FALSE, progress output will be output to stdout. If silent is - * TRUE no output to stdout will occur. Errors/warnings to stderr will occur - * disregarding the value of silent (but only if configure was run with - * --enable-debug). - */ -extern BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b, BOOL silent); -extern int ntfs_boot_sector_parse(ntfs_volume *vol, - const NTFS_BOOT_SECTOR *bs); - -#endif /* defined _NTFS_BOOTSECT_H */ - diff --git a/include/ntfs/collate.h b/include/ntfs/collate.h deleted file mode 100644 index 2191ba59..00000000 --- a/include/ntfs/collate.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * collate.h - Defines for NTFS collation handling. Part of the Linux-NTFS - * project. - * - * Copyright (c) 2004 Anton Altaparmakov - * Copyright (c) 2005 Yura Pakhuchiy - * - * 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 _NTFS_COLLATE_H -#define _NTFS_COLLATE_H - -#include "types.h" -#include "volume.h" - -#define NTFS_COLLATION_ERROR -2 - -/** - * ntfs_is_collation_rule_supported - - * @cr: - * - * Description... - * - * Returns: - */ -static inline BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr) -{ - int i; - - /* - * FIXME: At the moment we only support COLLATION_BINARY, - * COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME so we return false - * for everything else. - */ - if (cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG && - cr != COLLATION_FILE_NAME) - return FALSE; - i = le32_to_cpu(cr); - if (((i >= 0) && (i <= 0x02)) || - ((i >= 0x10) && (i <= 0x13))) - return TRUE; - return FALSE; -} - -extern int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr, - const void *data1, const int data1_len, - const void *data2, const int data2_len); - -#endif /* _NTFS_COLLATE_H */ diff --git a/include/ntfs/compat.h b/include/ntfs/compat.h deleted file mode 100644 index 29d4797a..00000000 --- a/include/ntfs/compat.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * compat.h - Tweaks for Windows compatibility. - * - * Copyright (c) 2002 Richard Russon - * Copyright (c) 2002-2004 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 _NTFS_COMPAT_H -#define _NTFS_COMPAT_H - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef WINDOWS - -#ifndef HAVE_FFS -#define HAVE_FFS -extern int ffs(int i); -#endif /* HAVE_FFS */ - -#define HAVE_STDIO_H /* mimic config.h */ -#define HAVE_STDARG_H - -#define atoll _atoi64 -#define fdatasync commit -#define __inline__ inline -#define __attribute__(X) /*nothing*/ - -#else /* !defined WINDOWS */ - -#ifndef O_BINARY -#define O_BINARY 0 /* unix is binary by default */ -#endif - -#endif /* defined WINDOWS */ - -#endif /* defined _NTFS_COMPAT_H */ - diff --git a/include/ntfs/compress.h b/include/ntfs/compress.h deleted file mode 100644 index 93df37af..00000000 --- a/include/ntfs/compress.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * compress.h - Exports for compressed attribute handling. Part of the - * Linux-NTFS project. - * - * Copyright (c) 2004 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 _NTFS_COMPRESS_H -#define _NTFS_COMPRESS_H - -#include "types.h" -#include "attrib.h" - -extern s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, - void *b); - -#endif /* defined _NTFS_COMPRESS_H */ - diff --git a/include/ntfs/debug.h b/include/ntfs/debug.h deleted file mode 100644 index 57fdf3bc..00000000 --- a/include/ntfs/debug.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * debug.h - Debugging output functions. Part of the Linux-NTFS project. - * - * Copyright (c) 2002-2004 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 _NTFS_DEBUG_H -#define _NTFS_DEBUG_H - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "logging.h" - -struct _runlist_element; - -#ifdef NTFS_DISABLE_DEBUG_LOGGING -static __inline__ void ntfs_debug_runlist_dump(const struct _runlist_element *rl __attribute__((unused))) {} -#else -extern void ntfs_debug_runlist_dump(const struct _runlist_element *rl); -#endif - -#define NTFS_BUG(msg) \ -{ \ - int ___i; \ - ntfs_log_critical("Bug in %s(): %s\n", __FUNCTION__, msg); \ - ntfs_log_debug("Forcing segmentation fault!"); \ - ___i = ((int*)NULL)[1]; \ -} - -#endif /* defined _NTFS_DEBUG_H */ diff --git a/include/ntfs/device.h b/include/ntfs/device.h deleted file mode 100644 index eeadf13e..00000000 --- a/include/ntfs/device.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * device.h - Exports for low level device io. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2006 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 _NTFS_DEVICE_H -#define _NTFS_DEVICE_H - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "device_io.h" -#include "types.h" -#include "support.h" -#include "volume.h" - -/** - * enum ntfs_device_state_bits - - * - * Defined bits for the state field in the ntfs_device structure. - */ -typedef enum { - ND_Open, /* 1: Device is open. */ - ND_ReadOnly, /* 1: Device is read-only. */ - ND_Dirty, /* 1: Device is dirty, needs sync. */ - ND_Block, /* 1: Device is a block device. */ -} ntfs_device_state_bits; - -#define test_ndev_flag(nd, flag) test_bit(ND_##flag, (nd)->d_state) -#define set_ndev_flag(nd, flag) set_bit(ND_##flag, (nd)->d_state) -#define clear_ndev_flag(nd, flag) clear_bit(ND_##flag, (nd)->d_state) - -#define NDevOpen(nd) test_ndev_flag(nd, Open) -#define NDevSetOpen(nd) set_ndev_flag(nd, Open) -#define NDevClearOpen(nd) clear_ndev_flag(nd, Open) - -#define NDevReadOnly(nd) test_ndev_flag(nd, ReadOnly) -#define NDevSetReadOnly(nd) set_ndev_flag(nd, ReadOnly) -#define NDevClearReadOnly(nd) clear_ndev_flag(nd, ReadOnly) - -#define NDevDirty(nd) test_ndev_flag(nd, Dirty) -#define NDevSetDirty(nd) set_ndev_flag(nd, Dirty) -#define NDevClearDirty(nd) clear_ndev_flag(nd, Dirty) - -#define NDevBlock(nd) test_ndev_flag(nd, Block) -#define NDevSetBlock(nd) set_ndev_flag(nd, Block) -#define NDevClearBlock(nd) clear_ndev_flag(nd, Block) - -/** - * struct ntfs_device - - * - * The ntfs device structure defining all operations needed to access the low - * level device underlying the ntfs volume. - */ -struct ntfs_device { - struct ntfs_device_operations *d_ops; /* Device operations. */ - unsigned long d_state; /* State of the device. */ - char *d_name; /* Name of device. */ - void *d_private; /* Private data used by the - device operations. */ -}; - -struct stat; - -/** - * struct ntfs_device_operations - - * - * The ntfs device operations defining all operations that can be performed on - * the low level device described by an ntfs device structure. - */ -struct ntfs_device_operations { - int (*open)(struct ntfs_device *dev, int flags); - int (*close)(struct ntfs_device *dev); - s64 (*seek)(struct ntfs_device *dev, s64 offset, int whence); - s64 (*read)(struct ntfs_device *dev, void *buf, s64 count); - s64 (*write)(struct ntfs_device *dev, const void *buf, s64 count); - s64 (*pread)(struct ntfs_device *dev, void *buf, s64 count, s64 offset); - s64 (*pwrite)(struct ntfs_device *dev, const void *buf, s64 count, - s64 offset); - int (*sync)(struct ntfs_device *dev); - int (*stat)(struct ntfs_device *dev, struct stat *buf); - int (*ioctl)(struct ntfs_device *dev, int request, void *argp); -}; - -extern struct ntfs_device *ntfs_device_alloc(const char *name, const long state, - struct ntfs_device_operations *dops, void *priv_data); -extern int ntfs_device_free(struct ntfs_device *dev); - -extern s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, - void *b); -extern s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, - const void *b); - -extern s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count, - const u32 bksize, void *b); -extern s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, - const u32 bksize, void *b); - -extern s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, - const s64 count, void *b); -extern s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn, - const s64 count, const void *b); - -extern s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size); -extern s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev); -extern int ntfs_device_heads_get(struct ntfs_device *dev); -extern int ntfs_device_sectors_per_track_get(struct ntfs_device *dev); -extern int ntfs_device_sector_size_get(struct ntfs_device *dev); -extern int ntfs_device_block_size_set(struct ntfs_device *dev, int block_size); - -#endif /* defined _NTFS_DEVICE_H */ diff --git a/include/ntfs/device_io.h b/include/ntfs/device_io.h deleted file mode 100644 index 6665b680..00000000 --- a/include/ntfs/device_io.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * device_io.h - Exports for default device io. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2006 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 _NTFS_DEVICE_IO_H -#define _NTFS_DEVICE_IO_H - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS - -#ifndef __CYGWIN32__ - -/* Not on Cygwin; use standard Unix style low level device operations. */ -#define ntfs_device_default_io_ops ntfs_device_unix_io_ops - -#else /* __CYGWIN32__ */ - -#ifndef HDIO_GETGEO -# define HDIO_GETGEO 0x301 -/** - * struct hd_geometry - - */ -struct hd_geometry { - unsigned char heads; - unsigned char sectors; - unsigned short cylinders; - unsigned long start; -}; -#endif -#ifndef BLKGETSIZE -# define BLKGETSIZE 0x1260 -#endif -#ifndef BLKSSZGET -# define BLKSSZGET 0x1268 -#endif -#ifndef BLKGETSIZE64 -# define BLKGETSIZE64 0x80041272 -#endif -#ifndef BLKBSZSET -# define BLKBSZSET 0x40041271 -#endif - -/* On Cygwin; use Win32 low level device operations. */ -#define ntfs_device_default_io_ops ntfs_device_win32_io_ops - -#endif /* __CYGWIN32__ */ - - -/* Forward declaration. */ -struct ntfs_device_operations; - -extern struct ntfs_device_operations ntfs_device_default_io_ops; - -#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */ - -#endif /* defined _NTFS_DEVICE_IO_H */ - diff --git a/include/ntfs/dir.h b/include/ntfs/dir.h deleted file mode 100644 index 8e81a4ff..00000000 --- a/include/ntfs/dir.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * dir.h - Exports for directory handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2002 Anton Altaparmakov - * Copyright (c) 2005-2006 Yura Pakhuchiy - * Copyright (c) 2004-2005 Richard Russon - * - * 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 _NTFS_DIR_H -#define _NTFS_DIR_H - -#include "types.h" - -#define PATH_SEP '/' - -#ifndef MAX_PATH -#define MAX_PATH 1024 -#endif - -/* - * We do not have these under DJGPP, so define our version that do not conflict - * with other S_IFs defined under DJGPP. - */ -#ifdef DJGPP -#ifndef S_IFLNK -#define S_IFLNK 0120000 -#endif -#ifndef S_ISLNK -#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) -#endif -#ifndef S_IFSOCK -#define S_IFSOCK 0140000 -#endif -#ifndef S_ISSOCK -#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) -#endif -#endif - -/* - * The little endian Unicode strings $I30, $SII, $SDH, $O, $Q, $R - * as a global constant. - */ -extern ntfschar NTFS_INDEX_I30[5]; -extern ntfschar NTFS_INDEX_SII[5]; -extern ntfschar NTFS_INDEX_SDH[5]; -extern ntfschar NTFS_INDEX_O[3]; -extern ntfschar NTFS_INDEX_Q[3]; -extern ntfschar NTFS_INDEX_R[3]; - -extern u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, - const ntfschar *uname, const int uname_len); - -extern ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, - const char *pathname); - -extern ntfs_inode *ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, - dev_t type); -extern ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, - ntfschar *name, u8 name_len, dev_t type, dev_t dev); -extern ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, - ntfschar *name, u8 name_len, ntfschar *target, u8 target_len); - -extern int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, - u8 name_len); - -extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, - u8 name_len); - -/* - * File types (adapted from include ) - */ -#define NTFS_DT_UNKNOWN 0 -#define NTFS_DT_FIFO 1 -#define NTFS_DT_CHR 2 -#define NTFS_DT_DIR 4 -#define NTFS_DT_BLK 6 -#define NTFS_DT_REG 8 -#define NTFS_DT_LNK 10 -#define NTFS_DT_SOCK 12 -#define NTFS_DT_WHT 14 - -/* - * This is the "ntfs_filldir" function type, used by ntfs_readdir() to let - * the caller specify what kind of dirent layout it wants to have. - * This allows the caller to read directories into their application or - * to have different dirent layouts depending on the binary type. - */ -typedef int (*ntfs_filldir_t)(void *dirent, const ntfschar *name, - const int name_len, const int name_type, const s64 pos, - const MFT_REF mref, const unsigned dt_type); - -extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, - void *dirent, ntfs_filldir_t filldir); - -#ifdef NTFS_RICH - -/** - * struct ntfs_dir - - */ -struct ntfs_dir { - ntfs_volume *vol; - struct ntfs_dir *parent; - ntfschar *name; - int name_len; - MFT_REF mft_num; - struct ntfs_dt *index; - struct ntfs_dir **children; - int child_count; - struct ntfs_bmp *bitmap; - ntfs_inode *inode; - ntfs_attr *iroot; - ntfs_attr *ialloc; - int index_size; -}; - - -int ntfs_dir_rollback(struct ntfs_dir *dir); -int ntfs_dir_truncate(ntfs_volume *vol, struct ntfs_dir *dir); -int ntfs_dir_commit(struct ntfs_dir *dir); -void ntfs_dir_free(struct ntfs_dir *dir); -struct ntfs_dir * ntfs_dir_create(ntfs_volume *vol, MFT_REF mft_num); -void ntfs_dir_add(struct ntfs_dir *parent, struct ntfs_dir *child); -struct ntfs_dir * ntfs_dir_find2(struct ntfs_dir *dir, ntfschar *name, int name_len); - -#endif /* NTFS_RICH */ - -#endif /* defined _NTFS_DIR_H */ - diff --git a/include/ntfs/endians.h b/include/ntfs/endians.h deleted file mode 100644 index ec73c442..00000000 --- a/include/ntfs/endians.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * endians.h - Definitions related to handling of byte ordering. Part of the - * Linux-NTFS project. - * - * Copyright (c) 2000-2005 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 _NTFS_ENDIANS_H -#define _NTFS_ENDIANS_H - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -/* - * Notes: - * We define the conversion functions including typecasts since the - * defaults don't necessarily perform appropriate typecasts. - * Also, using our own functions means that we can change them if it - * turns out that we do need to use the unaligned access macros on - * architectures requiring aligned memory accesses... - */ - -#ifdef HAVE_ENDIAN_H -#include -#endif -#ifdef HAVE_SYS_ENDIAN_H -#include -#endif -#ifdef HAVE_MACHINE_ENDIAN_H -#include -#endif -#ifdef HAVE_SYS_BYTEORDER_H -#include -#endif -#ifdef HAVE_SYS_PARAM_H -#include -#endif - -#ifndef __BYTE_ORDER -# if defined(_BYTE_ORDER) -# define __BYTE_ORDER _BYTE_ORDER -# define __LITTLE_ENDIAN _LITTLE_ENDIAN -# define __BIG_ENDIAN _BIG_ENDIAN -# elif defined(BYTE_ORDER) -# define __BYTE_ORDER BYTE_ORDER -# define __LITTLE_ENDIAN LITTLE_ENDIAN -# define __BIG_ENDIAN BIG_ENDIAN -# elif defined(__BYTE_ORDER__) -# define __BYTE_ORDER __BYTE_ORDER__ -# define __LITTLE_ENDIAN __LITTLE_ENDIAN__ -# define __BIG_ENDIAN __BIG_ENDIAN__ -# elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) || \ - defined(WORDS_LITTLEENDIAN) -# define __BYTE_ORDER 1 -# define __LITTLE_ENDIAN 1 -# define __BIG_ENDIAN 0 -# elif (!defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)) || \ - defined(WORDS_BIGENDIAN) -# define __BYTE_ORDER 0 -# define __LITTLE_ENDIAN 1 -# define __BIG_ENDIAN 0 -# else -# error "__BYTE_ORDER is not defined." -# endif -#endif - -#define __ntfs_bswap_constant_16(x) \ - (u16)((((u16)(x) & 0xff00) >> 8) | \ - (((u16)(x) & 0x00ff) << 8)) - -#define __ntfs_bswap_constant_32(x) \ - (u32)((((u32)(x) & 0xff000000u) >> 24) | \ - (((u32)(x) & 0x00ff0000u) >> 8) | \ - (((u32)(x) & 0x0000ff00u) << 8) | \ - (((u32)(x) & 0x000000ffu) << 24)) - -#define __ntfs_bswap_constant_64(x) \ - (u64)((((u64)(x) & 0xff00000000000000ull) >> 56) | \ - (((u64)(x) & 0x00ff000000000000ull) >> 40) | \ - (((u64)(x) & 0x0000ff0000000000ull) >> 24) | \ - (((u64)(x) & 0x000000ff00000000ull) >> 8) | \ - (((u64)(x) & 0x00000000ff000000ull) << 8) | \ - (((u64)(x) & 0x0000000000ff0000ull) << 24) | \ - (((u64)(x) & 0x000000000000ff00ull) << 40) | \ - (((u64)(x) & 0x00000000000000ffull) << 56)) - -#ifdef HAVE_BYTESWAP_H -# include -#else -# define bswap_16(x) __ntfs_bswap_constant_16(x) -# define bswap_32(x) __ntfs_bswap_constant_32(x) -# define bswap_64(x) __ntfs_bswap_constant_64(x) -#endif - -#if defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN) - -#define __le16_to_cpu(x) (x) -#define __le32_to_cpu(x) (x) -#define __le64_to_cpu(x) (x) - -#define __cpu_to_le16(x) (x) -#define __cpu_to_le32(x) (x) -#define __cpu_to_le64(x) (x) - -#define __constant_le16_to_cpu(x) (x) -#define __constant_le32_to_cpu(x) (x) -#define __constant_le64_to_cpu(x) (x) - -#define __constant_cpu_to_le16(x) (x) -#define __constant_cpu_to_le32(x) (x) -#define __constant_cpu_to_le64(x) (x) - -#elif defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN) - -#define __le16_to_cpu(x) bswap_16(x) -#define __le32_to_cpu(x) bswap_32(x) -#define __le64_to_cpu(x) bswap_64(x) - -#define __cpu_to_le16(x) bswap_16(x) -#define __cpu_to_le32(x) bswap_32(x) -#define __cpu_to_le64(x) bswap_64(x) - -#define __constant_le16_to_cpu(x) __ntfs_bswap_constant_16((u16)(x)) -#define __constant_le32_to_cpu(x) __ntfs_bswap_constant_32((u32)(x)) -#define __constant_le64_to_cpu(x) __ntfs_bswap_constant_64((u64)(x)) - -#define __constant_cpu_to_le16(x) __ntfs_bswap_constant_16((u16)(x)) -#define __constant_cpu_to_le32(x) __ntfs_bswap_constant_32((u32)(x)) -#define __constant_cpu_to_le64(x) __ntfs_bswap_constant_64((u64)(x)) - -#else - -#error "You must define __BYTE_ORDER to be __LITTLE_ENDIAN or __BIG_ENDIAN." - -#endif - -/* Unsigned from LE to CPU conversion. */ - -#define le16_to_cpu(x) (u16)__le16_to_cpu((u16)(x)) -#define le32_to_cpu(x) (u32)__le32_to_cpu((u32)(x)) -#define le64_to_cpu(x) (u64)__le64_to_cpu((u64)(x)) - -#define le16_to_cpup(x) (u16)__le16_to_cpu(*(const u16*)(x)) -#define le32_to_cpup(x) (u32)__le32_to_cpu(*(const u32*)(x)) -#define le64_to_cpup(x) (u64)__le64_to_cpu(*(const u64*)(x)) - -/* Signed from LE to CPU conversion. */ - -#define sle16_to_cpu(x) (s16)__le16_to_cpu((s16)(x)) -#define sle32_to_cpu(x) (s32)__le32_to_cpu((s32)(x)) -#define sle64_to_cpu(x) (s64)__le64_to_cpu((s64)(x)) - -#define sle16_to_cpup(x) (s16)__le16_to_cpu(*(s16*)(x)) -#define sle32_to_cpup(x) (s32)__le32_to_cpu(*(s32*)(x)) -#define sle64_to_cpup(x) (s64)__le64_to_cpu(*(s64*)(x)) - -/* Unsigned from CPU to LE conversion. */ - -#define cpu_to_le16(x) (u16)__cpu_to_le16((u16)(x)) -#define cpu_to_le32(x) (u32)__cpu_to_le32((u32)(x)) -#define cpu_to_le64(x) (u64)__cpu_to_le64((u64)(x)) - -#define cpu_to_le16p(x) (u16)__cpu_to_le16(*(u16*)(x)) -#define cpu_to_le32p(x) (u32)__cpu_to_le32(*(u32*)(x)) -#define cpu_to_le64p(x) (u64)__cpu_to_le64(*(u64*)(x)) - -/* Signed from CPU to LE conversion. */ - -#define cpu_to_sle16(x) (s16)__cpu_to_le16((s16)(x)) -#define cpu_to_sle32(x) (s32)__cpu_to_le32((s32)(x)) -#define cpu_to_sle64(x) (s64)__cpu_to_le64((s64)(x)) - -#define cpu_to_sle16p(x) (s16)__cpu_to_le16(*(s16*)(x)) -#define cpu_to_sle32p(x) (s32)__cpu_to_le32(*(s32*)(x)) -#define cpu_to_sle64p(x) (s64)__cpu_to_le64(*(s64*)(x)) - -/* Constant endianness conversion defines. */ - -#define const_le16_to_cpu(x) __constant_le16_to_cpu(x) -#define const_le32_to_cpu(x) __constant_le32_to_cpu(x) -#define const_le64_to_cpu(x) __constant_le64_to_cpu(x) - -#define const_cpu_to_le16(x) __constant_cpu_to_le16(x) -#define const_cpu_to_le32(x) __constant_cpu_to_le32(x) -#define const_cpu_to_le64(x) __constant_cpu_to_le64(x) - -#endif /* defined _NTFS_ENDIANS_H */ diff --git a/include/ntfs/index.h b/include/ntfs/index.h deleted file mode 100644 index 2e558e44..00000000 --- a/include/ntfs/index.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * index.h - Defines for NTFS index handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2004 Anton Altaparmakov - * Copyright (c) 2005 Yura Pakhuchiy - * Copyright (c) 2004-2005 Richard Russon - * - * 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 _NTFS_INDEX_H -#define _NTFS_INDEX_H - -#include "attrib.h" -#include "types.h" -#include "layout.h" -#include "inode.h" -#include "mft.h" - -/** - * struct ntfs_index_context - - * @ni: inode containing the @entry described by this context - * @name: name of the index described by this context - * @name_len: length of the index name - * @entry: index entry (points into @ir or @ia) - * @data: index entry data (points into @entry) - * @data_len: length in bytes of @data - * @is_in_root: TRUE if @entry is in @ir or FALSE if it is in @ia - * @ir: index root if @is_in_root or NULL otherwise - * @actx: attribute search context if in root or NULL otherwise - * @ia: index block if @is_in_root is FALSE or NULL otherwise - * @ia_na: opened INDEX_ALLOCATION attribute - * @ia_vcn: VCN from which @ia where read from - * @ia_dirty: TRUE if index block was changed - * @block_size: index block size - * @vcn_size: VCN size for this index block - * @vcn_size_bits: use instead of @vcn_size to speedup multiplication - * - * @ni is the inode this context belongs to. - * - * @entry is the index entry described by this context. @data and @data_len - * are the index entry data and its length in bytes, respectively. @data - * simply points into @entry. This is probably what the user is interested in. - * - * If @is_in_root is TRUE, @entry is in the index root attribute @ir described - * by the attribute search context @actx and inode @ni. @ia, @ia_vcn and - * @ia_dirty are undefined in this case. - * - * If @is_in_root is FALSE, @entry is in the index allocation attribute and @ia - * and @ia_vcn point to the index allocation block and VCN where it's placed, - * respectively. @ir and @actx are NULL in this case. @ia_na is opened - * INDEX_ALLOCATION attribute. @ia_dirty is TRUE if index block was changed and - * FALSE otherwise. - * - * To obtain a context call ntfs_index_ctx_get(). - * - * When finished with the @entry and its @data, call ntfs_index_ctx_put() to - * free the context and other associated resources. - * - * If the index entry was modified, call ntfs_index_entry_mark_dirty() before - * the call to ntfs_index_ctx_put() to ensure that the changes are written - * to disk. - */ -typedef struct { - ntfs_inode *ni; - ntfschar *name; - u32 name_len; - INDEX_ENTRY *entry; - void *data; - u16 data_len; - BOOL is_in_root; - INDEX_ROOT *ir; - ntfs_attr_search_ctx *actx; - INDEX_ALLOCATION *ia; - ntfs_attr *ia_na; - VCN ia_vcn; - BOOL ia_dirty; - u32 block_size; - u32 vcn_size; - u8 vcn_size_bits; -} ntfs_index_context; - -extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni, - ntfschar *name, u32 name_len); -extern void ntfs_index_ctx_put(ntfs_index_context *ictx); -extern void ntfs_index_ctx_reinit(ntfs_index_context *ictx); - -extern int ntfs_index_lookup(const void *key, const int key_len, - ntfs_index_context *ictx); - -extern int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, - MFT_REF mref); -extern int ntfs_index_rm(ntfs_index_context *ictx); - -extern INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr); - -/** - * ntfs_index_entry_mark_dirty - mark an index entry dirty - * @ictx: ntfs index context describing the index entry - * - * Mark the index entry described by the index entry context @ictx dirty. - * - * If the index entry is in the index root attribute, simply mark the inode - * containing the index root attribute dirty. This ensures the mftrecord, and - * hence the index root attribute, will be written out to disk later. - * - * If the index entry is in an index block belonging to the index allocation - * attribute, set ia_dirty to TRUE, thus index block will be updated during - * ntfs_index_ctx_put. - */ -static inline void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx) -{ - if (ictx->is_in_root) - ntfs_inode_mark_dirty(ictx->actx->ntfs_ino); - else - ictx->ia_dirty = TRUE; -} - - -#ifdef NTFS_RICH - -#include "layout.h" - -void ntfs_ie_free(INDEX_ENTRY *ie); -INDEX_ENTRY * ntfs_ie_create(void); -VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie); -INDEX_ENTRY * ntfs_ie_copy(INDEX_ENTRY *ie); -INDEX_ENTRY * ntfs_ie_set_vcn(INDEX_ENTRY *ie, VCN vcn); -INDEX_ENTRY * ntfs_ie_remove_vcn(INDEX_ENTRY *ie); -INDEX_ENTRY * ntfs_ie_set_name(INDEX_ENTRY *ie, ntfschar *name, int namelen, FILE_NAME_TYPE_FLAGS nametype); -INDEX_ENTRY * ntfs_ie_remove_name(INDEX_ENTRY *ie); - -#endif /* NTFS_RICH */ - -#endif /* _NTFS_INDEX_H */ - diff --git a/include/ntfs/inode.h b/include/ntfs/inode.h deleted file mode 100644 index 032d0216..00000000 --- a/include/ntfs/inode.h +++ /dev/null @@ -1,198 +0,0 @@ -/* - * inode.h - Defines for NTFS inode handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2001,2002 Anton Altaparmakov - * Copyright (c) 2004-2005 Yura Pakhuchiy - * Copyright (c) 2004-2005 Richard Russon - * - * 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 _NTFS_INODE_H -#define _NTFS_INODE_H - -/* Forward declaration */ -typedef struct _ntfs_inode ntfs_inode; - -#include "types.h" -#include "layout.h" -#include "support.h" -#include "volume.h" - -/** - * enum ntfs_inode_state_bits - - * - * Defined bits for the state field in the ntfs_inode structure. - * (f) = files only, (d) = directories only - */ -typedef enum { - NI_Dirty, /* 1: Mft record needs to be written to disk. */ - - /* The NI_AttrList* tests only make sense for base inodes. */ - NI_AttrList, /* 1: Mft record contains an attribute list. */ - NI_AttrListDirty, /* 1: Attribute list needs to be written to the - mft record and then to disk. */ - NI_FileNameDirty, /* 1: FILE_NAME attributes need to be updated - in the index. */ -} ntfs_inode_state_bits; - -#define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state) -#define set_nino_flag(ni, flag) set_bit(NI_##flag, (ni)->state) -#define clear_nino_flag(ni, flag) clear_bit(NI_##flag, (ni)->state) - -#define test_and_set_nino_flag(ni, flag) \ - test_and_set_bit(NI_##flag, (ni)->state) -#define test_and_clear_nino_flag(ni, flag) \ - test_and_clear_bit(NI_##flag, (ni)->state) - -#define NInoDirty(ni) test_nino_flag(ni, Dirty) -#define NInoSetDirty(ni) set_nino_flag(ni, Dirty) -#define NInoClearDirty(ni) clear_nino_flag(ni, Dirty) -#define NInoTestAndSetDirty(ni) test_and_set_nino_flag(ni, Dirty) -#define NInoTestAndClearDirty(ni) test_and_clear_nino_flag(ni, Dirty) - -#define NInoAttrList(ni) test_nino_flag(ni, AttrList) -#define NInoSetAttrList(ni) set_nino_flag(ni, AttrList) -#define NInoClearAttrList(ni) clear_nino_flag(ni, AttrList) - - -#define test_nino_al_flag(ni, flag) test_nino_flag(ni, AttrList##flag) -#define set_nino_al_flag(ni, flag) set_nino_flag(ni, AttrList##flag) -#define clear_nino_al_flag(ni, flag) clear_nino_flag(ni, AttrList##flag) - -#define test_and_set_nino_al_flag(ni, flag) \ - test_and_set_nino_flag(ni, AttrList##flag) -#define test_and_clear_nino_al_flag(ni, flag) \ - test_and_clear_nino_flag(ni, AttrList##flag) - -#define NInoAttrListDirty(ni) test_nino_al_flag(ni, Dirty) -#define NInoAttrListSetDirty(ni) set_nino_al_flag(ni, Dirty) -#define NInoAttrListClearDirty(ni) clear_nino_al_flag(ni, Dirty) -#define NInoAttrListTestAndSetDirty(ni) test_and_set_nino_al_flag(ni, Dirty) -#define NInoAttrListTestAndClearDirty(ni) test_and_clear_nino_al_flag(ni, Dirty) - -#define NInoFileNameDirty(ni) \ - test_nino_flag(ni, FileNameDirty) -#define NInoFileNameSetDirty(ni) \ - set_nino_flag(ni, FileNameDirty) -#define NInoFileNameClearDirty(ni) \ - clear_nino_flag(ni, FileNameDirty) -#define NInoFileNameTestAndSetDirty(ni) \ - test_and_set_nino_flag(ni, FileNameDirty) -#define NInoFileNameTestAndClearDirty(ni) \ - test_and_clear_nino_flag(ni, FileNameDirty) - -/** - * struct _ntfs_inode - The NTFS in-memory inode structure. - * - * It is just used as an extension to the fields already provided in the VFS - * inode. - */ -struct _ntfs_inode { - u64 mft_no; /* Inode / mft record number. */ - MFT_RECORD *mrec; /* The actual mft record of the inode. */ - ntfs_volume *vol; /* Pointer to the ntfs volume of this inode. */ - unsigned long state; /* NTFS specific flags describing this inode. - See ntfs_inode_state_bits above. */ - FILE_ATTR_FLAGS flags; /* Flags describing the file. - (Copy from STANDARD_INFORMATION) */ - /* - * Attribute list support (for use by the attribute lookup functions). - * Setup during ntfs_open_inode() for all inodes with attribute lists. - * Only valid if NI_AttrList is set in state. - */ - u32 attr_list_size; /* Length of attribute list value in bytes. */ - u8 *attr_list; /* Attribute list value itself. */ - /* Below fields are always valid. */ - s32 nr_extents; /* For a base mft record, the number of - attached extent inodes (0 if none), for - extent records this is -1. */ - union { /* This union is only used if nr_extents != 0. */ - ntfs_inode **extent_nis;/* For nr_extents > 0, array of the - ntfs inodes of the extent mft - records belonging to this base - inode which have been loaded. */ - ntfs_inode *base_ni; /* For nr_extents == -1, the ntfs - inode of the base mft record. */ - }; - - /* Temp: for directory handling */ - void *private_data; /* ntfs_dt containing this inode */ - int ref_count; - - /* Below fields are valid only for base inode. */ - s64 data_size; /* Data size stored in the filename index. */ - s64 allocated_size; /* Allocated size stored in the filename - index. (NOTE: Equal to allocated size of - the unnamed data attribute for normal or - encrypted files and to compressed size - of the unnamed data attribute for sparse or - compressed files.) */ - - time_t creation_time; - time_t last_data_change_time; - time_t last_mft_change_time; - time_t last_access_time; -}; - -extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol); - -extern ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref); - -extern int ntfs_inode_close(ntfs_inode *ni); - -extern ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, - const MFT_REF mref); - -extern int ntfs_inode_attach_all_extents(ntfs_inode *ni); - -/** - * ntfs_inode_mark_dirty - set the inode (and its base inode if it exists) dirty - * @ni: ntfs inode to set dirty - * - * Set the inode @ni dirty so it is written out later (at the latest at - * ntfs_inode_close() time). If @ni is an extent inode, set the base inode - * dirty, too. - * - * This function cannot fail. - */ -static __inline__ void ntfs_inode_mark_dirty(ntfs_inode *ni) -{ - NInoSetDirty(ni); - if (ni->nr_extents == -1) - NInoSetDirty(ni->base_ni); -} - -extern void ntfs_inode_update_atime(ntfs_inode *ni); -extern void ntfs_inode_update_time(ntfs_inode *ni); - -extern int ntfs_inode_sync(ntfs_inode *ni); - -extern int ntfs_inode_add_attrlist(ntfs_inode *ni); - -extern int ntfs_inode_free_space(ntfs_inode *ni, int size); - -extern int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *a); - -#ifdef NTFS_RICH - -int ntfs_inode_close2(ntfs_inode *ni); -ntfs_inode * ntfs_inode_open2(ntfs_volume *vol, const MFT_REF mref); -ntfs_inode * ntfs_inode_open3(ntfs_volume *vol, const MFT_REF mref); - -#endif /* NTFS_RICH */ - -#endif /* defined _NTFS_INODE_H */ diff --git a/include/ntfs/layout.h b/include/ntfs/layout.h deleted file mode 100644 index 9db8066c..00000000 --- a/include/ntfs/layout.h +++ /dev/null @@ -1,2665 +0,0 @@ -/* - * layout.h - Ntfs on-disk layout structures. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2005 Anton Altaparmakov - * Copyright (c) 2005 Yura Pakhuchiy - * - * 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 _NTFS_LAYOUT_H -#define _NTFS_LAYOUT_H - -#include "types.h" -#include "endians.h" -#include "support.h" - -/* The NTFS oem_id */ -#define magicNTFS const_cpu_to_le64(0x202020205346544e) /* "NTFS " */ -#define NTFS_SB_MAGIC 0x5346544e /* 'NTFS' */ - -/* - * Location of bootsector on partition: - * The standard NTFS_BOOT_SECTOR is on sector 0 of the partition. - * On NT4 and above there is one backup copy of the boot sector to - * be found on the last sector of the partition (not normally accessible - * from within Windows as the bootsector contained number of sectors - * value is one less than the actual value!). - * On versions of NT 3.51 and earlier, the backup copy was located at - * number of sectors/2 (integer divide), i.e. in the middle of the volume. - */ - -/** - * struct BIOS_PARAMETER_BLOCK - BIOS parameter block (bpb) structure. - */ -typedef struct { - u16 bytes_per_sector; /* Size of a sector in bytes. */ - u8 sectors_per_cluster; /* Size of a cluster in sectors. */ - u16 reserved_sectors; /* zero */ - u8 fats; /* zero */ - u16 root_entries; /* zero */ - u16 sectors; /* zero */ - u8 media_type; /* 0xf8 = hard disk */ - u16 sectors_per_fat; /* zero */ -/*0x0d*/u16 sectors_per_track; /* Required to boot Windows. */ -/*0x0f*/u16 heads; /* Required to boot Windows. */ -/*0x11*/u32 hidden_sectors; /* Offset to the start of the partition - relative to the disk in sectors. - Required to boot Windows. */ -/*0x15*/u32 large_sectors; /* zero */ -/* sizeof() = 25 (0x19) bytes */ -} __attribute__((__packed__)) BIOS_PARAMETER_BLOCK; - -/** - * struct NTFS_BOOT_SECTOR - NTFS boot sector structure. - */ -typedef struct { - u8 jump[3]; /* Irrelevant (jump to boot up code).*/ - u64 oem_id; /* Magic "NTFS ". */ -/*0x0b*/BIOS_PARAMETER_BLOCK bpb; /* See BIOS_PARAMETER_BLOCK. */ - u8 physical_drive; /* 0x00 floppy, 0x80 hard disk */ - u8 current_head; /* zero */ - u8 extended_boot_signature; /* 0x80 */ - u8 reserved2; /* zero */ -/*0x28*/s64 number_of_sectors; /* Number of sectors in volume. Gives - maximum volume size of 2^63 sectors. - Assuming standard sector size of 512 - bytes, the maximum byte size is - approx. 4.7x10^21 bytes. (-; */ - s64 mft_lcn; /* Cluster location of mft data. */ - s64 mftmirr_lcn; /* Cluster location of copy of mft. */ - s8 clusters_per_mft_record; /* Mft record size in clusters. */ - u8 reserved0[3]; /* zero */ - s8 clusters_per_index_record; /* Index block size in clusters. */ - u8 reserved1[3]; /* zero */ - u64 volume_serial_number; /* Irrelevant (serial number). */ - u32 checksum; /* Boot sector checksum. */ -/*0x54*/u8 bootstrap[426]; /* Irrelevant (boot up code). */ - u16 end_of_sector_marker; /* End of bootsector magic. Always is - 0xaa55 in little endian. */ -/* sizeof() = 512 (0x200) bytes */ -} __attribute__((__packed__)) NTFS_BOOT_SECTOR; - -/** - * enum NTFS_RECORD_TYPES - - * - * Magic identifiers present at the beginning of all ntfs record containing - * records (like mft records for example). - */ -typedef enum { - /* Found in $MFT/$DATA. */ - magic_FILE = const_cpu_to_le32(0x454c4946), /* Mft entry. */ - magic_INDX = const_cpu_to_le32(0x58444e49), /* Index buffer. */ - magic_HOLE = const_cpu_to_le32(0x454c4f48), /* ? (NTFS 3.0+?) */ - - /* Found in $LogFile/$DATA. */ - magic_RSTR = const_cpu_to_le32(0x52545352), /* Restart page. */ - magic_RCRD = const_cpu_to_le32(0x44524352), /* Log record page. */ - - /* Found in $LogFile/$DATA. (May be found in $MFT/$DATA, also?) */ - magic_CHKD = const_cpu_to_le32(0x444b4843), /* Modified by chkdsk. */ - - /* Found in all ntfs record containing records. */ - magic_BAAD = const_cpu_to_le32(0x44414142), /* Failed multi sector - transfer was detected. */ - - /* - * Found in $LogFile/$DATA when a page is full or 0xff bytes and is - * thus not initialized. User has to initialize the page before using - * it. - */ - magic_empty = const_cpu_to_le32(0xffffffff),/* Record is empty and has - to be initialized before - it can be used. */ -} NTFS_RECORD_TYPES; - -/* - * Generic magic comparison macros. Finally found a use for the ## preprocessor - * operator! (-8 - */ -#define ntfs_is_magic(x, m) ( (u32)(x) == (u32)magic_##m ) -#define ntfs_is_magicp(p, m) ( *(u32*)(p) == (u32)magic_##m ) - -/* - * Specialised magic comparison macros for the NTFS_RECORD_TYPES defined above. - */ -#define ntfs_is_file_record(x) ( ntfs_is_magic (x, FILE) ) -#define ntfs_is_file_recordp(p) ( ntfs_is_magicp(p, FILE) ) -#define ntfs_is_mft_record(x) ( ntfs_is_file_record(x) ) -#define ntfs_is_mft_recordp(p) ( ntfs_is_file_recordp(p) ) -#define ntfs_is_indx_record(x) ( ntfs_is_magic (x, INDX) ) -#define ntfs_is_indx_recordp(p) ( ntfs_is_magicp(p, INDX) ) -#define ntfs_is_hole_record(x) ( ntfs_is_magic (x, HOLE) ) -#define ntfs_is_hole_recordp(p) ( ntfs_is_magicp(p, HOLE) ) - -#define ntfs_is_rstr_record(x) ( ntfs_is_magic (x, RSTR) ) -#define ntfs_is_rstr_recordp(p) ( ntfs_is_magicp(p, RSTR) ) -#define ntfs_is_rcrd_record(x) ( ntfs_is_magic (x, RCRD) ) -#define ntfs_is_rcrd_recordp(p) ( ntfs_is_magicp(p, RCRD) ) - -#define ntfs_is_chkd_record(x) ( ntfs_is_magic (x, CHKD) ) -#define ntfs_is_chkd_recordp(p) ( ntfs_is_magicp(p, CHKD) ) - -#define ntfs_is_baad_record(x) ( ntfs_is_magic (x, BAAD) ) -#define ntfs_is_baad_recordp(p) ( ntfs_is_magicp(p, BAAD) ) - -#define ntfs_is_empty_record(x) ( ntfs_is_magic (x, empty) ) -#define ntfs_is_empty_recordp(p) ( ntfs_is_magicp(p, empty) ) - - -#define NTFS_BLOCK_SIZE 512 -#define NTFS_BLOCK_SIZE_BITS 9 - -/** - * struct NTFS_RECORD - - * - * The Update Sequence Array (usa) is an array of the u16 values which belong - * to the end of each sector protected by the update sequence record in which - * this array is contained. Note that the first entry is the Update Sequence - * Number (usn), a cyclic counter of how many times the protected record has - * been written to disk. The values 0 and -1 (ie. 0xffff) are not used. All - * last u16's of each sector have to be equal to the usn (during reading) or - * are set to it (during writing). If they are not, an incomplete multi sector - * transfer has occurred when the data was written. - * The maximum size for the update sequence array is fixed to: - * maximum size = usa_ofs + (usa_count * 2) = 510 bytes - * The 510 bytes comes from the fact that the last u16 in the array has to - * (obviously) finish before the last u16 of the first 512-byte sector. - * This formula can be used as a consistency check in that usa_ofs + - * (usa_count * 2) has to be less than or equal to 510. - */ -typedef struct { - NTFS_RECORD_TYPES magic;/* A four-byte magic identifying the - record type and/or status. */ - u16 usa_ofs; /* Offset to the Update Sequence Array (usa) - from the start of the ntfs record. */ - u16 usa_count; /* Number of u16 sized entries in the usa - including the Update Sequence Number (usn), - thus the number of fixups is the usa_count - minus 1. */ -} __attribute__((__packed__)) NTFS_RECORD; - -/** - * enum NTFS_SYSTEM_FILES - System files mft record numbers. - * - * All these files are always marked as used in the bitmap attribute of the - * mft; presumably in order to avoid accidental allocation for random other - * mft records. Also, the sequence number for each of the system files is - * always equal to their mft record number and it is never modified. - */ -typedef enum { - FILE_MFT = 0, /* Master file table (mft). Data attribute - contains the entries and bitmap attribute - records which ones are in use (bit==1). */ - FILE_MFTMirr = 1, /* Mft mirror: copy of first four mft records - in data attribute. If cluster size > 4kiB, - copy of first N mft records, with - N = cluster_size / mft_record_size. */ - FILE_LogFile = 2, /* Journalling log in data attribute. */ - FILE_Volume = 3, /* Volume name attribute and volume information - attribute (flags and ntfs version). Windows - refers to this file as volume DASD (Direct - Access Storage Device). */ - FILE_AttrDef = 4, /* Array of attribute definitions in data - attribute. */ - FILE_root = 5, /* Root directory. */ - FILE_Bitmap = 6, /* Allocation bitmap of all clusters (lcns) in - data attribute. */ - FILE_Boot = 7, /* Boot sector (always at cluster 0) in data - attribute. */ - FILE_BadClus = 8, /* Contains all bad clusters in the non-resident - data attribute. */ - FILE_Secure = 9, /* Shared security descriptors in data attribute - and two indexes into the descriptors. - Appeared in Windows 2000. Before that, this - file was named $Quota but was unused. */ - FILE_UpCase = 10, /* Uppercase equivalents of all 65536 Unicode - characters in data attribute. */ - FILE_Extend = 11, /* Directory containing other system files (eg. - $ObjId, $Quota, $Reparse and $UsnJrnl). This - is new to NTFS3.0. */ - FILE_reserved12 = 12, /* Reserved for future use (records 12-15). */ - FILE_reserved13 = 13, - FILE_reserved14 = 14, - FILE_reserved15 = 15, - FILE_first_user = 16, /* First user file, used as test limit for - whether to allow opening a file or not. */ -} NTFS_SYSTEM_FILES; - -/** - * enum MFT_RECORD_FLAGS - - * - * These are the so far known MFT_RECORD_* flags (16-bit) which contain - * information about the mft record in which they are present. - * - * MFT_RECORD_IS_4 exists on all $Extend sub-files. - * It seems that it marks it is a metadata file with MFT record >24, however, - * it is unknown if it is limited to metadata files only. - * - * MFT_RECORD_IS_VIEW_INDEX exists on every metafile with a non directory - * index, that means an INDEX_ROOT and an INDEX_ALLOCATION with a name other - * than "$I30". It is unknown if it is limited to metadata files only. - */ -typedef enum { - MFT_RECORD_IN_USE = const_cpu_to_le16(0x0001), - MFT_RECORD_IS_DIRECTORY = const_cpu_to_le16(0x0002), - MFT_RECORD_IS_4 = const_cpu_to_le16(0x0004), - MFT_RECORD_IS_VIEW_INDEX = const_cpu_to_le16(0x0008), - MFT_REC_SPACE_FILLER = 0xffff, /* Just to make flags - 16-bit. */ -} __attribute__((__packed__)) MFT_RECORD_FLAGS; - -/* - * mft references (aka file references or file record segment references) are - * used whenever a structure needs to refer to a record in the mft. - * - * A reference consists of a 48-bit index into the mft and a 16-bit sequence - * number used to detect stale references. - * - * For error reporting purposes we treat the 48-bit index as a signed quantity. - * - * The sequence number is a circular counter (skipping 0) describing how many - * times the referenced mft record has been (re)used. This has to match the - * sequence number of the mft record being referenced, otherwise the reference - * is considered stale and removed (FIXME: only ntfsck or the driver itself?). - * - * If the sequence number is zero it is assumed that no sequence number - * consistency checking should be performed. - * - * FIXME: Since inodes are 32-bit as of now, the driver needs to always check - * for high_part being 0 and if not either BUG(), cause a panic() or handle - * the situation in some other way. This shouldn't be a problem as a volume has - * to become HUGE in order to need more than 32-bits worth of mft records. - * Assuming the standard mft record size of 1kb only the records (never mind - * the non-resident attributes, etc.) would require 4Tb of space on their own - * for the first 32 bits worth of records. This is only if some strange person - * doesn't decide to foul play and make the mft sparse which would be a really - * horrible thing to do as it would trash our current driver implementation. )-: - * Do I hear screams "we want 64-bit inodes!" ?!? (-; - * - * FIXME: The mft zone is defined as the first 12% of the volume. This space is - * reserved so that the mft can grow contiguously and hence doesn't become - * fragmented. Volume free space includes the empty part of the mft zone and - * when the volume's free 88% are used up, the mft zone is shrunk by a factor - * of 2, thus making more space available for more files/data. This process is - * repeated every time there is no more free space except for the mft zone until - * there really is no more free space. - */ - -/* - * Typedef the MFT_REF as a 64-bit value for easier handling. - * Also define two unpacking macros to get to the reference (MREF) and - * sequence number (MSEQNO) respectively. - * The _LE versions are to be applied on little endian MFT_REFs. - * Note: The _LE versions will return a CPU endian formatted value! - */ -#define MFT_REF_MASK_CPU 0x0000ffffffffffffULL -#define MFT_REF_MASK_LE const_cpu_to_le64(MFT_REF_MASK_CPU) - -typedef u64 MFT_REF; - -#define MK_MREF(m, s) ((MFT_REF)(((MFT_REF)(s) << 48) | \ - ((MFT_REF)(m) & MFT_REF_MASK_CPU))) -#define MK_LE_MREF(m, s) const_cpu_to_le64(((MFT_REF)(((MFT_REF)(s) << 48) | \ - ((MFT_REF)(m) & MFT_REF_MASK_CPU)))) - -#define MREF(x) ((u64)((x) & MFT_REF_MASK_CPU)) -#define MSEQNO(x) ((u16)(((x) >> 48) & 0xffff)) -#define MREF_LE(x) ((u64)(const_le64_to_cpu(x) & MFT_REF_MASK_CPU)) -#define MSEQNO_LE(x) ((u16)((const_le64_to_cpu(x) >> 48) & 0xffff)) - -#define IS_ERR_MREF(x) (((x) & 0x0000800000000000ULL) ? 1 : 0) -#define ERR_MREF(x) ((u64)((s64)(x))) -#define MREF_ERR(x) ((int)((s64)(x))) - -/** - * struct MFT_RECORD - An MFT record layout (NTFS 3.1+) - * - * The mft record header present at the beginning of every record in the mft. - * This is followed by a sequence of variable length attribute records which - * is terminated by an attribute of type AT_END which is a truncated attribute - * in that it only consists of the attribute type code AT_END and none of the - * other members of the attribute structure are present. - */ -typedef struct { -/*Ofs*/ -/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ - NTFS_RECORD_TYPES magic;/* Usually the magic is "FILE". */ - u16 usa_ofs; /* See NTFS_RECORD definition above. */ - u16 usa_count; /* See NTFS_RECORD definition above. */ - -/* 8*/ LSN lsn; /* $LogFile sequence number for this record. - Changed every time the record is modified. */ -/* 16*/ u16 sequence_number; /* Number of times this mft record has been - reused. (See description for MFT_REF - above.) NOTE: The increment (skipping zero) - is done when the file is deleted. NOTE: If - this is zero it is left zero. */ -/* 18*/ u16 link_count; /* Number of hard links, i.e. the number of - directory entries referencing this record. - NOTE: Only used in mft base records. - NOTE: When deleting a directory entry we - check the link_count and if it is 1 we - delete the file. Otherwise we delete the - FILE_NAME_ATTR being referenced by the - directory entry from the mft record and - decrement the link_count. - FIXME: Careful with Win32 + DOS names! */ -/* 20*/ u16 attrs_offset; /* Byte offset to the first attribute in this - mft record from the start of the mft record. - NOTE: Must be aligned to 8-byte boundary. */ -/* 22*/ MFT_RECORD_FLAGS flags; /* Bit array of MFT_RECORD_FLAGS. When a file - is deleted, the MFT_RECORD_IN_USE flag is - set to zero. */ -/* 24*/ u32 bytes_in_use; /* Number of bytes used in this mft record. - NOTE: Must be aligned to 8-byte boundary. */ -/* 28*/ u32 bytes_allocated; /* Number of bytes allocated for this mft - record. This should be equal to the mft - record size. */ -/* 32*/ MFT_REF base_mft_record; /* This is zero for base mft records. - When it is not zero it is a mft reference - pointing to the base mft record to which - this record belongs (this is then used to - locate the attribute list attribute present - in the base record which describes this - extension record and hence might need - modification when the extension record - itself is modified, also locating the - attribute list also means finding the other - potential extents, belonging to the non-base - mft record). */ -/* 40*/ u16 next_attr_instance; /* The instance number that will be - assigned to the next attribute added to this - mft record. NOTE: Incremented each time - after it is used. NOTE: Every time the mft - record is reused this number is set to zero. - NOTE: The first instance number is always 0. - */ -/* The below fields are specific to NTFS 3.1+ (Windows XP and above): */ -/* 42*/ u16 reserved; /* Reserved/alignment. */ -/* 44*/ u32 mft_record_number; /* Number of this mft record. */ -/* sizeof() = 48 bytes */ -/* - * When (re)using the mft record, we place the update sequence array at this - * offset, i.e. before we start with the attributes. This also makes sense, - * otherwise we could run into problems with the update sequence array - * containing in itself the last two bytes of a sector which would mean that - * multi sector transfer protection wouldn't work. As you can't protect data - * by overwriting it since you then can't get it back... - * When reading we obviously use the data from the ntfs record header. - */ -} __attribute__((__packed__)) MFT_RECORD; - -/** - * struct MFT_RECORD_OLD - An MFT record layout (NTFS <=3.0) - * - * This is the version without the NTFS 3.1+ specific fields. - */ -typedef struct { -/*Ofs*/ -/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ - NTFS_RECORD_TYPES magic;/* Usually the magic is "FILE". */ - u16 usa_ofs; /* See NTFS_RECORD definition above. */ - u16 usa_count; /* See NTFS_RECORD definition above. */ - -/* 8*/ LSN lsn; /* $LogFile sequence number for this record. - Changed every time the record is modified. */ -/* 16*/ u16 sequence_number; /* Number of times this mft record has been - reused. (See description for MFT_REF - above.) NOTE: The increment (skipping zero) - is done when the file is deleted. NOTE: If - this is zero it is left zero. */ -/* 18*/ u16 link_count; /* Number of hard links, i.e. the number of - directory entries referencing this record. - NOTE: Only used in mft base records. - NOTE: When deleting a directory entry we - check the link_count and if it is 1 we - delete the file. Otherwise we delete the - FILE_NAME_ATTR being referenced by the - directory entry from the mft record and - decrement the link_count. - FIXME: Careful with Win32 + DOS names! */ -/* 20*/ u16 attrs_offset; /* Byte offset to the first attribute in this - mft record from the start of the mft record. - NOTE: Must be aligned to 8-byte boundary. */ -/* 22*/ MFT_RECORD_FLAGS flags; /* Bit array of MFT_RECORD_FLAGS. When a file - is deleted, the MFT_RECORD_IN_USE flag is - set to zero. */ -/* 24*/ u32 bytes_in_use; /* Number of bytes used in this mft record. - NOTE: Must be aligned to 8-byte boundary. */ -/* 28*/ u32 bytes_allocated; /* Number of bytes allocated for this mft - record. This should be equal to the mft - record size. */ -/* 32*/ MFT_REF base_mft_record; /* This is zero for base mft records. - When it is not zero it is a mft reference - pointing to the base mft record to which - this record belongs (this is then used to - locate the attribute list attribute present - in the base record which describes this - extension record and hence might need - modification when the extension record - itself is modified, also locating the - attribute list also means finding the other - potential extents, belonging to the non-base - mft record). */ -/* 40*/ u16 next_attr_instance; /* The instance number that will be - assigned to the next attribute added to this - mft record. NOTE: Incremented each time - after it is used. NOTE: Every time the mft - record is reused this number is set to zero. - NOTE: The first instance number is always 0. - */ -/* sizeof() = 42 bytes */ -/* - * When (re)using the mft record, we place the update sequence array at this - * offset, i.e. before we start with the attributes. This also makes sense, - * otherwise we could run into problems with the update sequence array - * containing in itself the last two bytes of a sector which would mean that - * multi sector transfer protection wouldn't work. As you can't protect data - * by overwriting it since you then can't get it back... - * When reading we obviously use the data from the ntfs record header. - */ -} __attribute__((__packed__)) MFT_RECORD_OLD; - -/** - * enum ATTR_TYPES - System defined attributes (32-bit). - * - * Each attribute type has a corresponding attribute name (Unicode string of - * maximum 64 character length) as described by the attribute definitions - * present in the data attribute of the $AttrDef system file. - * - * On NTFS 3.0 volumes the names are just as the types are named in the below - * enum exchanging AT_ for the dollar sign ($). If that isn't a revealing - * choice of symbol... (-; - */ -typedef enum { - AT_UNUSED = const_cpu_to_le32( 0), - AT_STANDARD_INFORMATION = const_cpu_to_le32( 0x10), - AT_ATTRIBUTE_LIST = const_cpu_to_le32( 0x20), - AT_FILE_NAME = const_cpu_to_le32( 0x30), - AT_OBJECT_ID = const_cpu_to_le32( 0x40), - AT_SECURITY_DESCRIPTOR = const_cpu_to_le32( 0x50), - AT_VOLUME_NAME = const_cpu_to_le32( 0x60), - AT_VOLUME_INFORMATION = const_cpu_to_le32( 0x70), - AT_DATA = const_cpu_to_le32( 0x80), - AT_INDEX_ROOT = const_cpu_to_le32( 0x90), - AT_INDEX_ALLOCATION = const_cpu_to_le32( 0xa0), - AT_BITMAP = const_cpu_to_le32( 0xb0), - AT_REPARSE_POINT = const_cpu_to_le32( 0xc0), - AT_EA_INFORMATION = const_cpu_to_le32( 0xd0), - AT_EA = const_cpu_to_le32( 0xe0), - AT_PROPERTY_SET = const_cpu_to_le32( 0xf0), - AT_LOGGED_UTILITY_STREAM = const_cpu_to_le32( 0x100), - AT_FIRST_USER_DEFINED_ATTRIBUTE = const_cpu_to_le32( 0x1000), - AT_END = const_cpu_to_le32(0xffffffff), -} ATTR_TYPES; - -/** - * enum COLLATION_RULES - The collation rules for sorting views/indexes/etc - * (32-bit). - * - * COLLATION_UNICODE_STRING - Collate Unicode strings by comparing their binary - * Unicode values, except that when a character can be uppercased, the - * upper case value collates before the lower case one. - * COLLATION_FILE_NAME - Collate file names as Unicode strings. The collation - * is done very much like COLLATION_UNICODE_STRING. In fact I have no idea - * what the difference is. Perhaps the difference is that file names - * would treat some special characters in an odd way (see - * unistr.c::ntfs_collate_names() and unistr.c::legal_ansi_char_array[] - * for what I mean but COLLATION_UNICODE_STRING would not give any special - * treatment to any characters at all, but this is speculation. - * COLLATION_NTOFS_ULONG - Sorting is done according to ascending u32 key - * values. E.g. used for $SII index in FILE_Secure, which sorts by - * security_id (u32). - * COLLATION_NTOFS_SID - Sorting is done according to ascending SID values. - * E.g. used for $O index in FILE_Extend/$Quota. - * COLLATION_NTOFS_SECURITY_HASH - Sorting is done first by ascending hash - * values and second by ascending security_id values. E.g. used for $SDH - * index in FILE_Secure. - * COLLATION_NTOFS_ULONGS - Sorting is done according to a sequence of ascending - * u32 key values. E.g. used for $O index in FILE_Extend/$ObjId, which - * sorts by object_id (16-byte), by splitting up the object_id in four - * u32 values and using them as individual keys. E.g. take the following - * two security_ids, stored as follows on disk: - * 1st: a1 61 65 b7 65 7b d4 11 9e 3d 00 e0 81 10 42 59 - * 2nd: 38 14 37 d2 d2 f3 d4 11 a5 21 c8 6b 79 b1 97 45 - * To compare them, they are split into four u32 values each, like so: - * 1st: 0xb76561a1 0x11d47b65 0xe0003d9e 0x59421081 - * 2nd: 0xd2371438 0x11d4f3d2 0x6bc821a5 0x4597b179 - * Now, it is apparent why the 2nd object_id collates after the 1st: the - * first u32 value of the 1st object_id is less than the first u32 of - * the 2nd object_id. If the first u32 values of both object_ids were - * equal then the second u32 values would be compared, etc. - */ -typedef enum { - COLLATION_BINARY = const_cpu_to_le32(0), /* Collate by binary - compare where the first byte is most - significant. */ - COLLATION_FILE_NAME = const_cpu_to_le32(1), /* Collate file names - as Unicode strings. */ - COLLATION_UNICODE_STRING = const_cpu_to_le32(2), /* Collate Unicode - strings by comparing their binary - Unicode values, except that when a - character can be uppercased, the upper - case value collates before the lower - case one. */ - COLLATION_NTOFS_ULONG = const_cpu_to_le32(16), - COLLATION_NTOFS_SID = const_cpu_to_le32(17), - COLLATION_NTOFS_SECURITY_HASH = const_cpu_to_le32(18), - COLLATION_NTOFS_ULONGS = const_cpu_to_le32(19), -} COLLATION_RULES; - -/** - * enum ATTR_DEF_FLAGS - - * - * The flags (32-bit) describing attribute properties in the attribute - * definition structure. FIXME: This information is based on Regis's - * information and, according to him, it is not certain and probably - * incomplete. The INDEXABLE flag is fairly certainly correct as only the file - * name attribute has this flag set and this is the only attribute indexed in - * NT4. - */ -typedef enum { - ATTR_DEF_INDEXABLE = const_cpu_to_le32(0x02), /* Attribute can be - indexed. */ - ATTR_DEF_MULTIPLE = const_cpu_to_le32(0x04), /* Attribute type - can be present multiple times in the - mft records of an inode. */ - ATTR_DEF_NOT_ZERO = const_cpu_to_le32(0x08), /* Attribute value - must contain at least one non-zero - byte. */ - ATTR_DEF_INDEXED_UNIQUE = const_cpu_to_le32(0x10), /* Attribute must be - indexed and the attribute value must be - unique for the attribute type in all of - the mft records of an inode. */ - ATTR_DEF_NAMED_UNIQUE = const_cpu_to_le32(0x20), /* Attribute must be - named and the name must be unique for - the attribute type in all of the mft - records of an inode. */ - ATTR_DEF_RESIDENT = const_cpu_to_le32(0x40), /* Attribute must be - resident. */ - ATTR_DEF_ALWAYS_LOG = const_cpu_to_le32(0x80), /* Always log - modifications to this attribute, - regardless of whether it is resident or - non-resident. Without this, only log - modifications if the attribute is - resident. */ -} ATTR_DEF_FLAGS; - -/** - * struct ATTR_DEF - - * - * The data attribute of FILE_AttrDef contains a sequence of attribute - * definitions for the NTFS volume. With this, it is supposed to be safe for an - * older NTFS driver to mount a volume containing a newer NTFS version without - * damaging it (that's the theory. In practice it's: not damaging it too much). - * Entries are sorted by attribute type. The flags describe whether the - * attribute can be resident/non-resident and possibly other things, but the - * actual bits are unknown. - */ -typedef struct { -/*hex ofs*/ -/* 0*/ ntfschar name[0x40]; /* Unicode name of the attribute. Zero - terminated. */ -/* 80*/ ATTR_TYPES type; /* Type of the attribute. */ -/* 84*/ u32 display_rule; /* Default display rule. - FIXME: What does it mean? (AIA) */ -/* 88*/ COLLATION_RULES collation_rule; /* Default collation rule. */ -/* 8c*/ ATTR_DEF_FLAGS flags; /* Flags describing the attribute. */ -/* 90*/ s64 min_size; /* Optional minimum attribute size. */ -/* 98*/ s64 max_size; /* Maximum size of attribute. */ -/* sizeof() = 0xa0 or 160 bytes */ -} __attribute__((__packed__)) ATTR_DEF; - -/** - * enum ATTR_FLAGS - Attribute flags (16-bit). - */ -typedef enum { - ATTR_IS_COMPRESSED = const_cpu_to_le16(0x0001), - ATTR_COMPRESSION_MASK = const_cpu_to_le16(0x00ff), /* Compression - method mask. Also, first - illegal value. */ - ATTR_IS_ENCRYPTED = const_cpu_to_le16(0x4000), - ATTR_IS_SPARSE = const_cpu_to_le16(0x8000), -} __attribute__((__packed__)) ATTR_FLAGS; - -/* - * Attribute compression. - * - * Only the data attribute is ever compressed in the current ntfs driver in - * Windows. Further, compression is only applied when the data attribute is - * non-resident. Finally, to use compression, the maximum allowed cluster size - * on a volume is 4kib. - * - * The compression method is based on independently compressing blocks of X - * clusters, where X is determined from the compression_unit value found in the - * non-resident attribute record header (more precisely: X = 2^compression_unit - * clusters). On Windows NT/2k, X always is 16 clusters (compression_unit = 4). - * - * There are three different cases of how a compression block of X clusters - * can be stored: - * - * 1) The data in the block is all zero (a sparse block): - * This is stored as a sparse block in the runlist, i.e. the runlist - * entry has length = X and lcn = -1. The mapping pairs array actually - * uses a delta_lcn value length of 0, i.e. delta_lcn is not present at - * all, which is then interpreted by the driver as lcn = -1. - * NOTE: Even uncompressed files can be sparse on NTFS 3.0 volumes, then - * the same principles apply as above, except that the length is not - * restricted to being any particular value. - * - * 2) The data in the block is not compressed: - * This happens when compression doesn't reduce the size of the block - * in clusters. I.e. if compression has a small effect so that the - * compressed data still occupies X clusters, then the uncompressed data - * is stored in the block. - * This case is recognised by the fact that the runlist entry has - * length = X and lcn >= 0. The mapping pairs array stores this as - * normal with a run length of X and some specific delta_lcn, i.e. - * delta_lcn has to be present. - * - * 3) The data in the block is compressed: - * The common case. This case is recognised by the fact that the run - * list entry has length L < X and lcn >= 0. The mapping pairs array - * stores this as normal with a run length of X and some specific - * delta_lcn, i.e. delta_lcn has to be present. This runlist entry is - * immediately followed by a sparse entry with length = X - L and - * lcn = -1. The latter entry is to make up the vcn counting to the - * full compression block size X. - * - * In fact, life is more complicated because adjacent entries of the same type - * can be coalesced. This means that one has to keep track of the number of - * clusters handled and work on a basis of X clusters at a time being one - * block. An example: if length L > X this means that this particular runlist - * entry contains a block of length X and part of one or more blocks of length - * L - X. Another example: if length L < X, this does not necessarily mean that - * the block is compressed as it might be that the lcn changes inside the block - * and hence the following runlist entry describes the continuation of the - * potentially compressed block. The block would be compressed if the - * following runlist entry describes at least X - L sparse clusters, thus - * making up the compression block length as described in point 3 above. (Of - * course, there can be several runlist entries with small lengths so that the - * sparse entry does not follow the first data containing entry with - * length < X.) - * - * NOTE: At the end of the compressed attribute value, there most likely is not - * just the right amount of data to make up a compression block, thus this data - * is not even attempted to be compressed. It is just stored as is, unless - * the number of clusters it occupies is reduced when compressed in which case - * it is stored as a compressed compression block, complete with sparse - * clusters at the end. - */ - -/** - * enum RESIDENT_ATTR_FLAGS - Flags of resident attributes (8-bit). - */ -typedef enum { - RESIDENT_ATTR_IS_INDEXED = 0x01, /* Attribute is referenced in an index - (has implications for deleting and - modifying the attribute). */ -} __attribute__((__packed__)) RESIDENT_ATTR_FLAGS; - -/** - * struct ATTR_RECORD - Attribute record header. - * - * Always aligned to 8-byte boundary. - */ -typedef struct { -/*Ofs*/ -/* 0*/ ATTR_TYPES type; /* The (32-bit) type of the attribute. */ -/* 4*/ u32 length; /* Byte size of the resident part of the - attribute (aligned to 8-byte boundary). - Used to get to the next attribute. */ -/* 8*/ u8 non_resident; /* If 0, attribute is resident. - If 1, attribute is non-resident. */ -/* 9*/ u8 name_length; /* Unicode character size of name of attribute. - 0 if unnamed. */ -/* 10*/ u16 name_offset; /* If name_length != 0, the byte offset to the - beginning of the name from the attribute - record. Note that the name is stored as a - Unicode string. When creating, place offset - just at the end of the record header. Then, - follow with attribute value or mapping pairs - array, resident and non-resident attributes - respectively, aligning to an 8-byte - boundary. */ -/* 12*/ ATTR_FLAGS flags; /* Flags describing the attribute. */ -/* 14*/ u16 instance; /* The instance of this attribute record. This - number is unique within this mft record (see - MFT_RECORD/next_attribute_instance notes - above for more details). */ -/* 16*/ union { - /* Resident attributes. */ - struct { -/* 16 */ u32 value_length; /* Byte size of attribute value. */ -/* 20 */ u16 value_offset; /* Byte offset of the attribute - value from the start of the - attribute record. When creating, - align to 8-byte boundary if we - have a name present as this might - not have a length of a multiple - of 8-bytes. */ -/* 22 */ RESIDENT_ATTR_FLAGS resident_flags; /* See above. */ -/* 23 */ s8 reservedR; /* Reserved/alignment to 8-byte - boundary. */ -/* 24 */ void *resident_end[0]; /* Use offsetof(ATTR_RECORD, - resident_end) to get size of - a resident attribute. */ - } __attribute__((__packed__)); - /* Non-resident attributes. */ - struct { -/* 16*/ VCN lowest_vcn; /* Lowest valid virtual cluster number - for this portion of the attribute value or - 0 if this is the only extent (usually the - case). - Only when an attribute list is used - does lowest_vcn != 0 ever occur. */ -/* 24*/ VCN highest_vcn; /* Highest valid vcn of this extent of - the attribute value. - Usually there is only one - portion, so this usually equals the attribute - value size in clusters minus 1. Can be -1 for - zero length files. Can be 0 for "single extent" - attributes. */ -/* 32*/ u16 mapping_pairs_offset; /* Byte offset from the - beginning of the structure to the mapping pairs - array which contains the mappings between the - vcns and the logical cluster numbers (lcns). - When creating, place this at the end of this - record header aligned to 8-byte boundary. */ -/* 34*/ u8 compression_unit; /* The compression unit expressed - as the log to the base 2 of the number of - clusters in a compression unit. 0 means not - compressed. (This effectively limits the - compression unit size to be a power of two - clusters.) WinNT4 only uses a value of 4. */ -/* 35*/ u8 reserved1[5]; /* Align to 8-byte boundary. */ -/* The sizes below are only used when lowest_vcn is zero, as otherwise it would - be difficult to keep them up-to-date.*/ -/* 40*/ s64 allocated_size; /* Byte size of disk space - allocated to hold the attribute value. Always - is a multiple of the cluster size. When a file - is compressed, this field is a multiple of the - compression block size (2^compression_unit) and - it represents the logically allocated space - rather than the actual on disk usage. For this - use the compressed_size (see below). */ -/* 48*/ s64 data_size; /* Byte size of the attribute - value. Can be larger than allocated_size if - attribute value is compressed or sparse. */ -/* 56*/ s64 initialized_size; /* Byte size of initialized - portion of the attribute value. Usually equals - data_size. */ -/* 64 */ void *non_resident_end[0]; /* Use offsetof(ATTR_RECORD, - non_resident_end) to get - size of a non resident - attribute. */ -/* sizeof(uncompressed attr) = 64*/ -/* 64*/ s64 compressed_size; /* Byte size of the attribute - value after compression. Only present when - compressed. Always is a multiple of the - cluster size. Represents the actual amount of - disk space being used on the disk. */ -/* 72 */ void *compressed_end[0]; - /* Use offsetof(ATTR_RECORD, compressed_end) to - get size of a compressed attribute. */ -/* sizeof(compressed attr) = 72*/ - } __attribute__((__packed__)); - } __attribute__((__packed__)); -} __attribute__((__packed__)) ATTR_RECORD; - -typedef ATTR_RECORD ATTR_REC; - -/** - * enum FILE_ATTR_FLAGS - File attribute flags (32-bit). - */ -typedef enum { - /* - * These flags are only present in the STANDARD_INFORMATION attribute - * (in the field file_attributes). - */ - FILE_ATTR_READONLY = const_cpu_to_le32(0x00000001), - FILE_ATTR_HIDDEN = const_cpu_to_le32(0x00000002), - FILE_ATTR_SYSTEM = const_cpu_to_le32(0x00000004), - /* Old DOS volid. Unused in NT. = cpu_to_le32(0x00000008), */ - - FILE_ATTR_DIRECTORY = const_cpu_to_le32(0x00000010), - /* FILE_ATTR_DIRECTORY is not considered valid in NT. It is reserved - for the DOS SUBDIRECTORY flag. */ - FILE_ATTR_ARCHIVE = const_cpu_to_le32(0x00000020), - FILE_ATTR_DEVICE = const_cpu_to_le32(0x00000040), - FILE_ATTR_NORMAL = const_cpu_to_le32(0x00000080), - - FILE_ATTR_TEMPORARY = const_cpu_to_le32(0x00000100), - FILE_ATTR_SPARSE_FILE = const_cpu_to_le32(0x00000200), - FILE_ATTR_REPARSE_POINT = const_cpu_to_le32(0x00000400), - FILE_ATTR_COMPRESSED = const_cpu_to_le32(0x00000800), - - FILE_ATTR_OFFLINE = const_cpu_to_le32(0x00001000), - FILE_ATTR_NOT_CONTENT_INDEXED = const_cpu_to_le32(0x00002000), - FILE_ATTR_ENCRYPTED = const_cpu_to_le32(0x00004000), - - FILE_ATTR_VALID_FLAGS = const_cpu_to_le32(0x00007fb7), - /* FILE_ATTR_VALID_FLAGS masks out the old DOS VolId and the - FILE_ATTR_DEVICE and preserves everything else. This mask - is used to obtain all flags that are valid for reading. */ - FILE_ATTR_VALID_SET_FLAGS = const_cpu_to_le32(0x000031a7), - /* FILE_ATTR_VALID_SET_FLAGS masks out the old DOS VolId, the - FILE_ATTR_DEVICE, FILE_ATTR_DIRECTORY, FILE_ATTR_SPARSE_FILE, - FILE_ATTR_REPARSE_POINT, FILE_ATRE_COMPRESSED and FILE_ATTR_ENCRYPTED - and preserves the rest. This mask is used to to obtain all flags that - are valid for setting. */ - - /** - * FILE_ATTR_I30_INDEX_PRESENT - Is it a directory? - * - * This is a copy of the MFT_RECORD_IS_DIRECTORY bit from the mft - * record, telling us whether this is a directory or not, i.e. whether - * it has an index root attribute named "$I30" or not. - * - * This flag is only present in the FILE_NAME attribute (in the - * file_attributes field). - */ - FILE_ATTR_I30_INDEX_PRESENT = const_cpu_to_le32(0x10000000), - - /** - * FILE_ATTR_VIEW_INDEX_PRESENT - Does have a non-directory index? - * - * This is a copy of the MFT_RECORD_IS_VIEW_INDEX bit from the mft - * record, telling us whether this file has a view index present (eg. - * object id index, quota index, one of the security indexes and the - * reparse points index). - * - * This flag is only present in the $STANDARD_INFORMATION and - * $FILE_NAME attributes. - */ - FILE_ATTR_VIEW_INDEX_PRESENT = const_cpu_to_le32(0x20000000), -} __attribute__((__packed__)) FILE_ATTR_FLAGS; - -/* - * NOTE on times in NTFS: All times are in MS standard time format, i.e. they - * are the number of 100-nanosecond intervals since 1st January 1601, 00:00:00 - * universal coordinated time (UTC). (In Linux time starts 1st January 1970, - * 00:00:00 UTC and is stored as the number of 1-second intervals since then.) - */ - -/** - * struct STANDARD_INFORMATION - Attribute: Standard information (0x10). - * - * NOTE: Always resident. - * NOTE: Present in all base file records on a volume. - * NOTE: There is conflicting information about the meaning of each of the time - * fields but the meaning as defined below has been verified to be - * correct by practical experimentation on Windows NT4 SP6a and is hence - * assumed to be the one and only correct interpretation. - */ -typedef struct { -/*Ofs*/ -/* 0*/ s64 creation_time; /* Time file was created. Updated when - a filename is changed(?). */ -/* 8*/ s64 last_data_change_time; /* Time the data attribute was last - modified. */ -/* 16*/ s64 last_mft_change_time; /* Time this mft record was last - modified. */ -/* 24*/ s64 last_access_time; /* Approximate time when the file was - last accessed (obviously this is not - updated on read-only volumes). In - Windows this is only updated when - accessed if some time delta has - passed since the last update. Also, - last access times updates can be - disabled altogether for speed. */ -/* 32*/ FILE_ATTR_FLAGS file_attributes; /* Flags describing the file. */ -/* 36*/ union { - /* NTFS 1.2 (and previous, presumably) */ - struct { - /* 36 */ u8 reserved12[12]; /* Reserved/alignment to 8-byte - boundary. */ - /* 48 */ void *v1_end[0]; /* Marker for offsetof(). */ - } __attribute__((__packed__)); -/* sizeof() = 48 bytes */ - /* NTFS 3.0 */ - struct { -/* - * If a volume has been upgraded from a previous NTFS version, then these - * fields are present only if the file has been accessed since the upgrade. - * Recognize the difference by comparing the length of the resident attribute - * value. If it is 48, then the following fields are missing. If it is 72 then - * the fields are present. Maybe just check like this: - * if (resident.ValueLength < sizeof(STANDARD_INFORMATION)) { - * Assume NTFS 1.2- format. - * If (volume version is 3.0+) - * Upgrade attribute to NTFS 3.0 format. - * else - * Use NTFS 1.2- format for access. - * } else - * Use NTFS 3.0 format for access. - * Only problem is that it might be legal to set the length of the value to - * arbitrarily large values thus spoiling this check. - But chkdsk probably - * views that as a corruption, assuming that it behaves like this for all - * attributes. - */ - /* 36*/ u32 maximum_versions; /* Maximum allowed versions for - file. Zero if version numbering is disabled. */ - /* 40*/ u32 version_number; /* This file's version (if any). - Set to zero if maximum_versions is zero. */ - /* 44*/ u32 class_id; /* Class id from bidirectional - class id index (?). */ - /* 48*/ u32 owner_id; /* Owner_id of the user owning - the file. Translate via $Q index in FILE_Extend - /$Quota to the quota control entry for the user - owning the file. Zero if quotas are disabled. */ - /* 52*/ u32 security_id; /* Security_id for the file. - Translate via $SII index and $SDS data stream - in FILE_Secure to the security descriptor. */ - /* 56*/ u64 quota_charged; /* Byte size of the charge to - the quota for all streams of the file. Note: Is - zero if quotas are disabled. */ - /* 64*/ u64 usn; /* Last update sequence number - of the file. This is a direct index into the - change (aka usn) journal file. It is zero if - the usn journal is disabled. - NOTE: To disable the journal need to delete - the journal file itself and to then walk the - whole mft and set all Usn entries in all mft - records to zero! (This can take a while!) - The journal is FILE_Extend/$UsnJrnl. Win2k - will recreate the journal and initiate - logging if necessary when mounting the - partition. This, in contrast to disabling the - journal is a very fast process, so the user - won't even notice it. */ - /* 72*/ void *v3_end[0]; /* Marker for offsetof(). */ - } __attribute__((__packed__)); - } __attribute__((__packed__)); -/* sizeof() = 72 bytes (NTFS 3.0) */ -} __attribute__((__packed__)) STANDARD_INFORMATION; - -/** - * struct ATTR_LIST_ENTRY - Attribute: Attribute list (0x20). - * - * - Can be either resident or non-resident. - * - Value consists of a sequence of variable length, 8-byte aligned, - * ATTR_LIST_ENTRY records. - * - The attribute list attribute contains one entry for each attribute of - * the file in which the list is located, except for the list attribute - * itself. The list is sorted: first by attribute type, second by attribute - * name (if present), third by instance number. The extents of one - * non-resident attribute (if present) immediately follow after the initial - * extent. They are ordered by lowest_vcn and have their instance set to zero. - * It is not allowed to have two attributes with all sorting keys equal. - * - Further restrictions: - * - If not resident, the vcn to lcn mapping array has to fit inside the - * base mft record. - * - The attribute list attribute value has a maximum size of 256kb. This - * is imposed by the Windows cache manager. - * - Attribute lists are only used when the attributes of mft record do not - * fit inside the mft record despite all attributes (that can be made - * non-resident) having been made non-resident. This can happen e.g. when: - * - File has a large number of hard links (lots of file name - * attributes present). - * - The mapping pairs array of some non-resident attribute becomes so - * large due to fragmentation that it overflows the mft record. - * - The security descriptor is very complex (not applicable to - * NTFS 3.0 volumes). - * - There are many named streams. - */ -typedef struct { -/*Ofs*/ -/* 0*/ ATTR_TYPES type; /* Type of referenced attribute. */ -/* 4*/ u16 length; /* Byte size of this entry. */ -/* 6*/ u8 name_length; /* Size in Unicode chars of the name of the - attribute or 0 if unnamed. */ -/* 7*/ u8 name_offset; /* Byte offset to beginning of attribute name - (always set this to where the name would - start even if unnamed). */ -/* 8*/ VCN lowest_vcn; /* Lowest virtual cluster number of this portion - of the attribute value. This is usually 0. It - is non-zero for the case where one attribute - does not fit into one mft record and thus - several mft records are allocated to hold - this attribute. In the latter case, each mft - record holds one extent of the attribute and - there is one attribute list entry for each - extent. NOTE: This is DEFINITELY a signed - value! The windows driver uses cmp, followed - by jg when comparing this, thus it treats it - as signed. */ -/* 16*/ MFT_REF mft_reference; /* The reference of the mft record holding - the ATTR_RECORD for this portion of the - attribute value. */ -/* 24*/ u16 instance; /* If lowest_vcn = 0, the instance of the - attribute being referenced; otherwise 0. */ -/* 26*/ ntfschar name[0]; /* Use when creating only. When reading use - name_offset to determine the location of the - name. */ -/* sizeof() = 26 + (attribute_name_length * 2) bytes */ -} __attribute__((__packed__)) ATTR_LIST_ENTRY; - -/* - * The maximum allowed length for a file name. - */ -#define NTFS_MAX_NAME_LEN 255 - -/** - * enum FILE_NAME_TYPE_FLAGS - Possible namespaces for filenames in ntfs. - * (8-bit). - */ -typedef enum { - FILE_NAME_POSIX = 0x00, - /* This is the largest namespace. It is case sensitive and - allows all Unicode characters except for: '\0' and '/'. - Beware that in WinNT/2k files which eg have the same name - except for their case will not be distinguished by the - standard utilities and thus a "del filename" will delete - both "filename" and "fileName" without warning. */ - FILE_NAME_WIN32 = 0x01, - /* The standard WinNT/2k NTFS long filenames. Case insensitive. - All Unicode chars except: '\0', '"', '*', '/', ':', '<', - '>', '?', '\' and '|'. Further, names cannot end with a '.' - or a space. */ - FILE_NAME_DOS = 0x02, - /* The standard DOS filenames (8.3 format). Uppercase only. - All 8-bit characters greater space, except: '"', '*', '+', - ',', '/', ':', ';', '<', '=', '>', '?' and '\'. */ - FILE_NAME_WIN32_AND_DOS = 0x03, - /* 3 means that both the Win32 and the DOS filenames are - identical and hence have been saved in this single filename - record. */ -} __attribute__((__packed__)) FILE_NAME_TYPE_FLAGS; - -/** - * struct FILE_NAME_ATTR - Attribute: Filename (0x30). - * - * NOTE: Always resident. - * NOTE: All fields, except the parent_directory, are only updated when the - * filename is changed. Until then, they just become out of sync with - * reality and the more up to date values are present in the standard - * information attribute. - * NOTE: There is conflicting information about the meaning of each of the time - * fields but the meaning as defined below has been verified to be - * correct by practical experimentation on Windows NT4 SP6a and is hence - * assumed to be the one and only correct interpretation. - */ -typedef struct { -/*hex ofs*/ -/* 0*/ MFT_REF parent_directory; /* Directory this filename is - referenced from. */ -/* 8*/ s64 creation_time; /* Time file was created. */ -/* 10*/ s64 last_data_change_time; /* Time the data attribute was last - modified. */ -/* 18*/ s64 last_mft_change_time; /* Time this mft record was last - modified. */ -/* 20*/ s64 last_access_time; /* Last time this mft record was - accessed. */ -/* 28*/ s64 allocated_size; /* Byte size of on-disk allocated space - for the data attribute. So for - normal $DATA, this is the - allocated_size from the unnamed - $DATA attribute and for compressed - and/or sparse $DATA, this is the - compressed_size from the unnamed - $DATA attribute. NOTE: This is a - multiple of the cluster size. */ -/* 30*/ s64 data_size; /* Byte size of actual data in data - attribute. */ -/* 38*/ FILE_ATTR_FLAGS file_attributes; /* Flags describing the file. */ -/* 3c*/ union { - /* 3c*/ struct { - /* 3c*/ u16 packed_ea_size; /* Size of the buffer needed to - pack the extended attributes - (EAs), if such are present.*/ - /* 3e*/ u16 reserved; /* Reserved for alignment. */ - } __attribute__((__packed__)); - /* 3c*/ u32 reparse_point_tag; /* Type of reparse point, - present only in reparse - points and only if there are - no EAs. */ - } __attribute__((__packed__)); -/* 40*/ u8 file_name_length; /* Length of file name in - (Unicode) characters. */ -/* 41*/ FILE_NAME_TYPE_FLAGS file_name_type; /* Namespace of the file name.*/ -/* 42*/ ntfschar file_name[0]; /* File name in Unicode. */ -} __attribute__((__packed__)) FILE_NAME_ATTR; - -/** - * struct GUID - GUID structures store globally unique identifiers (GUID). - * - * A GUID is a 128-bit value consisting of one group of eight hexadecimal - * digits, followed by three groups of four hexadecimal digits each, followed - * by one group of twelve hexadecimal digits. GUIDs are Microsoft's - * implementation of the distributed computing environment (DCE) universally - * unique identifier (UUID). - * - * Example of a GUID: - * 1F010768-5A73-BC91-0010-A52216A7227B - */ -typedef struct { - u32 data1; /* The first eight hexadecimal digits of the GUID. */ - u16 data2; /* The first group of four hexadecimal digits. */ - u16 data3; /* The second group of four hexadecimal digits. */ - u8 data4[8]; /* The first two bytes are the third group of four - hexadecimal digits. The remaining six bytes are the - final 12 hexadecimal digits. */ -} __attribute__((__packed__)) GUID; - -/** - * struct OBJ_ID_INDEX_DATA - FILE_Extend/$ObjId contains an index named $O. - * - * This index contains all object_ids present on the volume as the index keys - * and the corresponding mft_record numbers as the index entry data parts. - * - * The data part (defined below) also contains three other object_ids: - * birth_volume_id - object_id of FILE_Volume on which the file was first - * created. Optional (i.e. can be zero). - * birth_object_id - object_id of file when it was first created. Usually - * equals the object_id. Optional (i.e. can be zero). - * domain_id - Reserved (always zero). - */ -typedef struct { - MFT_REF mft_reference; /* Mft record containing the object_id in - the index entry key. */ - union { - struct { - GUID birth_volume_id; - GUID birth_object_id; - GUID domain_id; - } __attribute__((__packed__)); - u8 extended_info[48]; - } __attribute__((__packed__)); -} __attribute__((__packed__)) OBJ_ID_INDEX_DATA; - -/** - * struct OBJECT_ID_ATTR - Attribute: Object id (NTFS 3.0+) (0x40). - * - * NOTE: Always resident. - */ -typedef struct { - GUID object_id; /* Unique id assigned to the - file.*/ - /* The following fields are optional. The attribute value size is 16 - bytes, i.e. sizeof(GUID), if these are not present at all. Note, - the entries can be present but one or more (or all) can be zero - meaning that that particular value(s) is(are) not defined. Note, - when the fields are missing here, it is well possible that they are - to be found within the $Extend/$ObjId system file indexed under the - above object_id. */ - union { - struct { - GUID birth_volume_id; /* Unique id of volume on which - the file was first created.*/ - GUID birth_object_id; /* Unique id of file when it was - first created. */ - GUID domain_id; /* Reserved, zero. */ - } __attribute__((__packed__)); - u8 extended_info[48]; - } __attribute__((__packed__)); -} __attribute__((__packed__)) OBJECT_ID_ATTR; - -#if 0 -/** - * enum IDENTIFIER_AUTHORITIES - - * - * The pre-defined IDENTIFIER_AUTHORITIES used as SID_IDENTIFIER_AUTHORITY in - * the SID structure (see below). - */ -typedef enum { /* SID string prefix. */ - SECURITY_NULL_SID_AUTHORITY = {0, 0, 0, 0, 0, 0}, /* S-1-0 */ - SECURITY_WORLD_SID_AUTHORITY = {0, 0, 0, 0, 0, 1}, /* S-1-1 */ - SECURITY_LOCAL_SID_AUTHORITY = {0, 0, 0, 0, 0, 2}, /* S-1-2 */ - SECURITY_CREATOR_SID_AUTHORITY = {0, 0, 0, 0, 0, 3}, /* S-1-3 */ - SECURITY_NON_UNIQUE_AUTHORITY = {0, 0, 0, 0, 0, 4}, /* S-1-4 */ - SECURITY_NT_SID_AUTHORITY = {0, 0, 0, 0, 0, 5}, /* S-1-5 */ -} IDENTIFIER_AUTHORITIES; -#endif - -/** - * enum RELATIVE_IDENTIFIERS - - * - * These relative identifiers (RIDs) are used with the above identifier - * authorities to make up universal well-known SIDs. - * - * Note: The relative identifier (RID) refers to the portion of a SID, which - * identifies a user or group in relation to the authority that issued the SID. - * For example, the universal well-known SID Creator Owner ID (S-1-3-0) is - * made up of the identifier authority SECURITY_CREATOR_SID_AUTHORITY (3) and - * the relative identifier SECURITY_CREATOR_OWNER_RID (0). - */ -typedef enum { /* Identifier authority. */ - SECURITY_NULL_RID = 0, /* S-1-0 */ - SECURITY_WORLD_RID = 0, /* S-1-1 */ - SECURITY_LOCAL_RID = 0, /* S-1-2 */ - - SECURITY_CREATOR_OWNER_RID = 0, /* S-1-3 */ - SECURITY_CREATOR_GROUP_RID = 1, /* S-1-3 */ - - SECURITY_CREATOR_OWNER_SERVER_RID = 2, /* S-1-3 */ - SECURITY_CREATOR_GROUP_SERVER_RID = 3, /* S-1-3 */ - - SECURITY_DIALUP_RID = 1, - SECURITY_NETWORK_RID = 2, - SECURITY_BATCH_RID = 3, - SECURITY_INTERACTIVE_RID = 4, - SECURITY_SERVICE_RID = 6, - SECURITY_ANONYMOUS_LOGON_RID = 7, - SECURITY_PROXY_RID = 8, - SECURITY_ENTERPRISE_CONTROLLERS_RID=9, - SECURITY_SERVER_LOGON_RID = 9, - SECURITY_PRINCIPAL_SELF_RID = 0xa, - SECURITY_AUTHENTICATED_USER_RID = 0xb, - SECURITY_RESTRICTED_CODE_RID = 0xc, - SECURITY_TERMINAL_SERVER_RID = 0xd, - - SECURITY_LOGON_IDS_RID = 5, - SECURITY_LOGON_IDS_RID_COUNT = 3, - - SECURITY_LOCAL_SYSTEM_RID = 0x12, - - SECURITY_NT_NON_UNIQUE = 0x15, - - SECURITY_BUILTIN_DOMAIN_RID = 0x20, - - /* - * Well-known domain relative sub-authority values (RIDs). - */ - - /* Users. */ - DOMAIN_USER_RID_ADMIN = 0x1f4, - DOMAIN_USER_RID_GUEST = 0x1f5, - DOMAIN_USER_RID_KRBTGT = 0x1f6, - - /* Groups. */ - DOMAIN_GROUP_RID_ADMINS = 0x200, - DOMAIN_GROUP_RID_USERS = 0x201, - DOMAIN_GROUP_RID_GUESTS = 0x202, - DOMAIN_GROUP_RID_COMPUTERS = 0x203, - DOMAIN_GROUP_RID_CONTROLLERS = 0x204, - DOMAIN_GROUP_RID_CERT_ADMINS = 0x205, - DOMAIN_GROUP_RID_SCHEMA_ADMINS = 0x206, - DOMAIN_GROUP_RID_ENTERPRISE_ADMINS= 0x207, - DOMAIN_GROUP_RID_POLICY_ADMINS = 0x208, - - /* Aliases. */ - DOMAIN_ALIAS_RID_ADMINS = 0x220, - DOMAIN_ALIAS_RID_USERS = 0x221, - DOMAIN_ALIAS_RID_GUESTS = 0x222, - DOMAIN_ALIAS_RID_POWER_USERS = 0x223, - - DOMAIN_ALIAS_RID_ACCOUNT_OPS = 0x224, - DOMAIN_ALIAS_RID_SYSTEM_OPS = 0x225, - DOMAIN_ALIAS_RID_PRINT_OPS = 0x226, - DOMAIN_ALIAS_RID_BACKUP_OPS = 0x227, - - DOMAIN_ALIAS_RID_REPLICATOR = 0x228, - DOMAIN_ALIAS_RID_RAS_SERVERS = 0x229, - DOMAIN_ALIAS_RID_PREW2KCOMPACCESS = 0x22a, -} RELATIVE_IDENTIFIERS; - -/* - * The universal well-known SIDs: - * - * NULL_SID S-1-0-0 - * WORLD_SID S-1-1-0 - * LOCAL_SID S-1-2-0 - * CREATOR_OWNER_SID S-1-3-0 - * CREATOR_GROUP_SID S-1-3-1 - * CREATOR_OWNER_SERVER_SID S-1-3-2 - * CREATOR_GROUP_SERVER_SID S-1-3-3 - * - * (Non-unique IDs) S-1-4 - * - * NT well-known SIDs: - * - * NT_AUTHORITY_SID S-1-5 - * DIALUP_SID S-1-5-1 - * - * NETWORD_SID S-1-5-2 - * BATCH_SID S-1-5-3 - * INTERACTIVE_SID S-1-5-4 - * SERVICE_SID S-1-5-6 - * ANONYMOUS_LOGON_SID S-1-5-7 (aka null logon session) - * PROXY_SID S-1-5-8 - * SERVER_LOGON_SID S-1-5-9 (aka domain controller account) - * SELF_SID S-1-5-10 (self RID) - * AUTHENTICATED_USER_SID S-1-5-11 - * RESTRICTED_CODE_SID S-1-5-12 (running restricted code) - * TERMINAL_SERVER_SID S-1-5-13 (running on terminal server) - * - * (Logon IDs) S-1-5-5-X-Y - * - * (NT non-unique IDs) S-1-5-0x15-... - * - * (Built-in domain) S-1-5-0x20 - */ - -/** - * union SID_IDENTIFIER_AUTHORITY - A 48-bit value used in the SID structure - * - * NOTE: This is stored as a big endian number. - */ -typedef union { - struct { - u16 high_part; /* High 16-bits. */ - u32 low_part; /* Low 32-bits. */ - } __attribute__((__packed__)); - u8 value[6]; /* Value as individual bytes. */ -} __attribute__((__packed__)) SID_IDENTIFIER_AUTHORITY; - -/** - * struct SID - - * - * The SID structure is a variable-length structure used to uniquely identify - * users or groups. SID stands for security identifier. - * - * The standard textual representation of the SID is of the form: - * S-R-I-S-S... - * Where: - * - The first "S" is the literal character 'S' identifying the following - * digits as a SID. - * - R is the revision level of the SID expressed as a sequence of digits - * in decimal. - * - I is the 48-bit identifier_authority, expressed as digits in decimal, - * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32. - * - S... is one or more sub_authority values, expressed as digits in - * decimal. - * - * Example SID; the domain-relative SID of the local Administrators group on - * Windows NT/2k: - * S-1-5-32-544 - * This translates to a SID with: - * revision = 1, - * sub_authority_count = 2, - * identifier_authority = {0,0,0,0,0,5}, // SECURITY_NT_AUTHORITY - * sub_authority[0] = 32, // SECURITY_BUILTIN_DOMAIN_RID - * sub_authority[1] = 544 // DOMAIN_ALIAS_RID_ADMINS - */ -typedef struct { - u8 revision; - u8 sub_authority_count; - SID_IDENTIFIER_AUTHORITY identifier_authority; - u32 sub_authority[1]; /* At least one sub_authority. */ -} __attribute__((__packed__)) SID; - -/** - * enum SID_CONSTANTS - Current constants for SIDs. - */ -typedef enum { - SID_REVISION = 1, /* Current revision level. */ - SID_MAX_SUB_AUTHORITIES = 15, /* Maximum number of those. */ - SID_RECOMMENDED_SUB_AUTHORITIES = 1, /* Will change to around 6 in - a future revision. */ -} SID_CONSTANTS; - -/** - * enum ACE_TYPES - The predefined ACE types (8-bit, see below). - */ -typedef enum { - ACCESS_MIN_MS_ACE_TYPE = 0, - ACCESS_ALLOWED_ACE_TYPE = 0, - ACCESS_DENIED_ACE_TYPE = 1, - SYSTEM_AUDIT_ACE_TYPE = 2, - SYSTEM_ALARM_ACE_TYPE = 3, /* Not implemented as of Win2k. */ - ACCESS_MAX_MS_V2_ACE_TYPE = 3, - - ACCESS_ALLOWED_COMPOUND_ACE_TYPE= 4, - ACCESS_MAX_MS_V3_ACE_TYPE = 4, - - /* The following are Win2k only. */ - ACCESS_MIN_MS_OBJECT_ACE_TYPE = 5, - ACCESS_ALLOWED_OBJECT_ACE_TYPE = 5, - ACCESS_DENIED_OBJECT_ACE_TYPE = 6, - SYSTEM_AUDIT_OBJECT_ACE_TYPE = 7, - SYSTEM_ALARM_OBJECT_ACE_TYPE = 8, - ACCESS_MAX_MS_OBJECT_ACE_TYPE = 8, - - ACCESS_MAX_MS_V4_ACE_TYPE = 8, - - /* This one is for WinNT&2k. */ - ACCESS_MAX_MS_ACE_TYPE = 8, -} __attribute__((__packed__)) ACE_TYPES; - -/** - * enum ACE_FLAGS - The ACE flags (8-bit) for audit and inheritance. - * - * SUCCESSFUL_ACCESS_ACE_FLAG is only used with system audit and alarm ACE - * types to indicate that a message is generated (in Windows!) for successful - * accesses. - * - * FAILED_ACCESS_ACE_FLAG is only used with system audit and alarm ACE types - * to indicate that a message is generated (in Windows!) for failed accesses. - */ -typedef enum { - /* The inheritance flags. */ - OBJECT_INHERIT_ACE = 0x01, - CONTAINER_INHERIT_ACE = 0x02, - NO_PROPAGATE_INHERIT_ACE = 0x04, - INHERIT_ONLY_ACE = 0x08, - INHERITED_ACE = 0x10, /* Win2k only. */ - VALID_INHERIT_FLAGS = 0x1f, - - /* The audit flags. */ - SUCCESSFUL_ACCESS_ACE_FLAG = 0x40, - FAILED_ACCESS_ACE_FLAG = 0x80, -} __attribute__((__packed__)) ACE_FLAGS; - -/** - * struct ACE_HEADER - - * - * An ACE is an access-control entry in an access-control list (ACL). - * An ACE defines access to an object for a specific user or group or defines - * the types of access that generate system-administration messages or alarms - * for a specific user or group. The user or group is identified by a security - * identifier (SID). - * - * Each ACE starts with an ACE_HEADER structure (aligned on 4-byte boundary), - * which specifies the type and size of the ACE. The format of the subsequent - * data depends on the ACE type. - */ -typedef struct { - ACE_TYPES type; /* Type of the ACE. */ - ACE_FLAGS flags; /* Flags describing the ACE. */ - u16 size; /* Size in bytes of the ACE. */ -} __attribute__((__packed__)) ACE_HEADER; - -/** - * enum ACCESS_MASK - The access mask (32-bit). - * - * Defines the access rights. - */ -typedef enum { - /* - * The specific rights (bits 0 to 15). Depend on the type of the - * object being secured by the ACE. - */ - - /* Specific rights for files and directories are as follows: */ - - /* Right to read data from the file. (FILE) */ - FILE_READ_DATA = const_cpu_to_le32(0x00000001), - /* Right to list contents of a directory. (DIRECTORY) */ - FILE_LIST_DIRECTORY = const_cpu_to_le32(0x00000001), - - /* Right to write data to the file. (FILE) */ - FILE_WRITE_DATA = const_cpu_to_le32(0x00000002), - /* Right to create a file in the directory. (DIRECTORY) */ - FILE_ADD_FILE = const_cpu_to_le32(0x00000002), - - /* Right to append data to the file. (FILE) */ - FILE_APPEND_DATA = const_cpu_to_le32(0x00000004), - /* Right to create a subdirectory. (DIRECTORY) */ - FILE_ADD_SUBDIRECTORY = const_cpu_to_le32(0x00000004), - - /* Right to read extended attributes. (FILE/DIRECTORY) */ - FILE_READ_EA = const_cpu_to_le32(0x00000008), - - /* Right to write extended attributes. (FILE/DIRECTORY) */ - FILE_WRITE_EA = const_cpu_to_le32(0x00000010), - - /* Right to execute a file. (FILE) */ - FILE_EXECUTE = const_cpu_to_le32(0x00000020), - /* Right to traverse the directory. (DIRECTORY) */ - FILE_TRAVERSE = const_cpu_to_le32(0x00000020), - - /* - * Right to delete a directory and all the files it contains (its - * children), even if the files are read-only. (DIRECTORY) - */ - FILE_DELETE_CHILD = const_cpu_to_le32(0x00000040), - - /* Right to read file attributes. (FILE/DIRECTORY) */ - FILE_READ_ATTRIBUTES = const_cpu_to_le32(0x00000080), - - /* Right to change file attributes. (FILE/DIRECTORY) */ - FILE_WRITE_ATTRIBUTES = const_cpu_to_le32(0x00000100), - - /* - * The standard rights (bits 16 to 23). Are independent of the type of - * object being secured. - */ - - /* Right to delete the object. */ - DELETE = const_cpu_to_le32(0x00010000), - - /* - * Right to read the information in the object's security descriptor, - * not including the information in the SACL. I.e. right to read the - * security descriptor and owner. - */ - READ_CONTROL = const_cpu_to_le32(0x00020000), - - /* Right to modify the DACL in the object's security descriptor. */ - WRITE_DAC = const_cpu_to_le32(0x00040000), - - /* Right to change the owner in the object's security descriptor. */ - WRITE_OWNER = const_cpu_to_le32(0x00080000), - - /* - * Right to use the object for synchronization. Enables a process to - * wait until the object is in the signalled state. Some object types - * do not support this access right. - */ - SYNCHRONIZE = const_cpu_to_le32(0x00100000), - - /* - * The following STANDARD_RIGHTS_* are combinations of the above for - * convenience and are defined by the Win32 API. - */ - - /* These are currently defined to READ_CONTROL. */ - STANDARD_RIGHTS_READ = const_cpu_to_le32(0x00020000), - STANDARD_RIGHTS_WRITE = const_cpu_to_le32(0x00020000), - STANDARD_RIGHTS_EXECUTE = const_cpu_to_le32(0x00020000), - - /* Combines DELETE, READ_CONTROL, WRITE_DAC, and WRITE_OWNER access. */ - STANDARD_RIGHTS_REQUIRED = const_cpu_to_le32(0x000f0000), - - /* - * Combines DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER, and - * SYNCHRONIZE access. - */ - STANDARD_RIGHTS_ALL = const_cpu_to_le32(0x001f0000), - - /* - * The access system ACL and maximum allowed access types (bits 24 to - * 25, bits 26 to 27 are reserved). - */ - ACCESS_SYSTEM_SECURITY = const_cpu_to_le32(0x01000000), - MAXIMUM_ALLOWED = const_cpu_to_le32(0x02000000), - - /* - * The generic rights (bits 28 to 31). These map onto the standard and - * specific rights. - */ - - /* Read, write, and execute access. */ - GENERIC_ALL = const_cpu_to_le32(0x10000000), - - /* Execute access. */ - GENERIC_EXECUTE = const_cpu_to_le32(0x20000000), - - /* - * Write access. For files, this maps onto: - * FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | - * FILE_WRITE_EA | STANDARD_RIGHTS_WRITE | SYNCHRONIZE - * For directories, the mapping has the same numerical value. See - * above for the descriptions of the rights granted. - */ - GENERIC_WRITE = const_cpu_to_le32(0x40000000), - - /* - * Read access. For files, this maps onto: - * FILE_READ_ATTRIBUTES | FILE_READ_DATA | FILE_READ_EA | - * STANDARD_RIGHTS_READ | SYNCHRONIZE - * For directories, the mapping has the same numerical value. See - * above for the descriptions of the rights granted. - */ - GENERIC_READ = const_cpu_to_le32(0x80000000), -} ACCESS_MASK; - -/** - * struct GENERIC_MAPPING - - * - * The generic mapping array. Used to denote the mapping of each generic - * access right to a specific access mask. - * - * FIXME: What exactly is this and what is it for? (AIA) - */ -typedef struct { - ACCESS_MASK generic_read; - ACCESS_MASK generic_write; - ACCESS_MASK generic_execute; - ACCESS_MASK generic_all; -} __attribute__((__packed__)) GENERIC_MAPPING; - -/* - * The predefined ACE type structures are as defined below. - */ - -/** - * struct ACCESS_DENIED_ACE - - * - * ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE, SYSTEM_AUDIT_ACE, SYSTEM_ALARM_ACE - */ -typedef struct { -/* 0 ACE_HEADER; -- Unfolded here as gcc doesn't like unnamed structs. */ - ACE_TYPES type; /* Type of the ACE. */ - ACE_FLAGS flags; /* Flags describing the ACE. */ - u16 size; /* Size in bytes of the ACE. */ - -/* 4*/ ACCESS_MASK mask; /* Access mask associated with the ACE. */ -/* 8*/ SID sid; /* The SID associated with the ACE. */ -} __attribute__((__packed__)) ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE, - SYSTEM_AUDIT_ACE, SYSTEM_ALARM_ACE; - -/** - * enum OBJECT_ACE_FLAGS - The object ACE flags (32-bit). - */ -typedef enum { - ACE_OBJECT_TYPE_PRESENT = const_cpu_to_le32(1), - ACE_INHERITED_OBJECT_TYPE_PRESENT = const_cpu_to_le32(2), -} OBJECT_ACE_FLAGS; - -/** - * struct ACCESS_ALLOWED_OBJECT_ACE - - */ -typedef struct { -/* 0 ACE_HEADER; -- Unfolded here as gcc doesn't like unnamed structs. */ - ACE_TYPES type; /* Type of the ACE. */ - ACE_FLAGS flags; /* Flags describing the ACE. */ - u16 size; /* Size in bytes of the ACE. */ - -/* 4*/ ACCESS_MASK mask; /* Access mask associated with the ACE. */ -/* 8*/ OBJECT_ACE_FLAGS object_flags; /* Flags describing the object ACE. */ -/* 12*/ GUID object_type; -/* 28*/ GUID inherited_object_type; -/* 44*/ SID sid; /* The SID associated with the ACE. */ -} __attribute__((__packed__)) ACCESS_ALLOWED_OBJECT_ACE, - ACCESS_DENIED_OBJECT_ACE, - SYSTEM_AUDIT_OBJECT_ACE, - SYSTEM_ALARM_OBJECT_ACE; - -/** - * struct ACL - An ACL is an access-control list (ACL). - * - * An ACL starts with an ACL header structure, which specifies the size of - * the ACL and the number of ACEs it contains. The ACL header is followed by - * zero or more access control entries (ACEs). The ACL as well as each ACE - * are aligned on 4-byte boundaries. - */ -typedef struct { - u8 revision; /* Revision of this ACL. */ - u8 alignment1; - u16 size; /* Allocated space in bytes for ACL. Includes this - header, the ACEs and the remaining free space. */ - u16 ace_count; /* Number of ACEs in the ACL. */ - u16 alignment2; -/* sizeof() = 8 bytes */ -} __attribute__((__packed__)) ACL; - -/** - * enum ACL_CONSTANTS - Current constants for ACLs. - */ -typedef enum { - /* Current revision. */ - ACL_REVISION = 2, - ACL_REVISION_DS = 4, - - /* History of revisions. */ - ACL_REVISION1 = 1, - MIN_ACL_REVISION = 2, - ACL_REVISION2 = 2, - ACL_REVISION3 = 3, - ACL_REVISION4 = 4, - MAX_ACL_REVISION = 4, -} ACL_CONSTANTS; - -/** - * enum SECURITY_DESCRIPTOR_CONTROL - - * - * The security descriptor control flags (16-bit). - * - * SE_OWNER_DEFAULTED - This boolean flag, when set, indicates that the - * SID pointed to by the Owner field was provided by a - * defaulting mechanism rather than explicitly provided by the - * original provider of the security descriptor. This may - * affect the treatment of the SID with respect to inheritance - * of an owner. - * - * SE_GROUP_DEFAULTED - This boolean flag, when set, indicates that the - * SID in the Group field was provided by a defaulting mechanism - * rather than explicitly provided by the original provider of - * the security descriptor. This may affect the treatment of - * the SID with respect to inheritance of a primary group. - * - * SE_DACL_PRESENT - This boolean flag, when set, indicates that the - * security descriptor contains a discretionary ACL. If this - * flag is set and the Dacl field of the SECURITY_DESCRIPTOR is - * null, then a null ACL is explicitly being specified. - * - * SE_DACL_DEFAULTED - This boolean flag, when set, indicates that the - * ACL pointed to by the Dacl field was provided by a defaulting - * mechanism rather than explicitly provided by the original - * provider of the security descriptor. This may affect the - * treatment of the ACL with respect to inheritance of an ACL. - * This flag is ignored if the DaclPresent flag is not set. - * - * SE_SACL_PRESENT - This boolean flag, when set, indicates that the - * security descriptor contains a system ACL pointed to by the - * Sacl field. If this flag is set and the Sacl field of the - * SECURITY_DESCRIPTOR is null, then an empty (but present) - * ACL is being specified. - * - * SE_SACL_DEFAULTED - This boolean flag, when set, indicates that the - * ACL pointed to by the Sacl field was provided by a defaulting - * mechanism rather than explicitly provided by the original - * provider of the security descriptor. This may affect the - * treatment of the ACL with respect to inheritance of an ACL. - * This flag is ignored if the SaclPresent flag is not set. - * - * SE_SELF_RELATIVE - This boolean flag, when set, indicates that the - * security descriptor is in self-relative form. In this form, - * all fields of the security descriptor are contiguous in memory - * and all pointer fields are expressed as offsets from the - * beginning of the security descriptor. - */ -typedef enum { - SE_OWNER_DEFAULTED = const_cpu_to_le16(0x0001), - SE_GROUP_DEFAULTED = const_cpu_to_le16(0x0002), - SE_DACL_PRESENT = const_cpu_to_le16(0x0004), - SE_DACL_DEFAULTED = const_cpu_to_le16(0x0008), - SE_SACL_PRESENT = const_cpu_to_le16(0x0010), - SE_SACL_DEFAULTED = const_cpu_to_le16(0x0020), - SE_DACL_AUTO_INHERIT_REQ = const_cpu_to_le16(0x0100), - SE_SACL_AUTO_INHERIT_REQ = const_cpu_to_le16(0x0200), - SE_DACL_AUTO_INHERITED = const_cpu_to_le16(0x0400), - SE_SACL_AUTO_INHERITED = const_cpu_to_le16(0x0800), - SE_DACL_PROTECTED = const_cpu_to_le16(0x1000), - SE_SACL_PROTECTED = const_cpu_to_le16(0x2000), - SE_RM_CONTROL_VALID = const_cpu_to_le16(0x4000), - SE_SELF_RELATIVE = const_cpu_to_le16(0x8000), -} __attribute__((__packed__)) SECURITY_DESCRIPTOR_CONTROL; - -/** - * struct SECURITY_DESCRIPTOR_RELATIVE - - * - * Self-relative security descriptor. Contains the owner and group SIDs as well - * as the sacl and dacl ACLs inside the security descriptor itself. - */ -typedef struct { - u8 revision; /* Revision level of the security descriptor. */ - u8 alignment; - SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of - the descriptor as well as the following fields. */ - u32 owner; /* Byte offset to a SID representing an object's - owner. If this is NULL, no owner SID is present in - the descriptor. */ - u32 group; /* Byte offset to a SID representing an object's - primary group. If this is NULL, no primary group - SID is present in the descriptor. */ - u32 sacl; /* Byte offset to a system ACL. Only valid, if - SE_SACL_PRESENT is set in the control field. If - SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL - is specified. */ - u32 dacl; /* Byte offset to a discretionary ACL. Only valid, if - SE_DACL_PRESENT is set in the control field. If - SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL - (unconditionally granting access) is specified. */ -/* sizeof() = 0x14 bytes */ -} __attribute__((__packed__)) SECURITY_DESCRIPTOR_RELATIVE; - -/** - * struct SECURITY_DESCRIPTOR - Absolute security descriptor. - * - * Does not contain the owner and group SIDs, nor the sacl and dacl ACLs inside - * the security descriptor. Instead, it contains pointers to these structures - * in memory. Obviously, absolute security descriptors are only useful for in - * memory representations of security descriptors. - * - * On disk, a self-relative security descriptor is used. - */ -typedef struct { - u8 revision; /* Revision level of the security descriptor. */ - u8 alignment; - SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of - the descriptor as well as the following fields. */ - SID *owner; /* Points to a SID representing an object's owner. If - this is NULL, no owner SID is present in the - descriptor. */ - SID *group; /* Points to a SID representing an object's primary - group. If this is NULL, no primary group SID is - present in the descriptor. */ - ACL *sacl; /* Points to a system ACL. Only valid, if - SE_SACL_PRESENT is set in the control field. If - SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL - is specified. */ - ACL *dacl; /* Points to a discretionary ACL. Only valid, if - SE_DACL_PRESENT is set in the control field. If - SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL - (unconditionally granting access) is specified. */ -} __attribute__((__packed__)) SECURITY_DESCRIPTOR; - -/** - * enum SECURITY_DESCRIPTOR_CONSTANTS - - * - * Current constants for security descriptors. - */ -typedef enum { - /* Current revision. */ - SECURITY_DESCRIPTOR_REVISION = 1, - SECURITY_DESCRIPTOR_REVISION1 = 1, - - /* The sizes of both the absolute and relative security descriptors is - the same as pointers, at least on ia32 architecture are 32-bit. */ - SECURITY_DESCRIPTOR_MIN_LENGTH = sizeof(SECURITY_DESCRIPTOR), -} SECURITY_DESCRIPTOR_CONSTANTS; - -/* - * Attribute: Security descriptor (0x50). - * - * A standard self-relative security descriptor. - * - * NOTE: Can be resident or non-resident. - * NOTE: Not used in NTFS 3.0+, as security descriptors are stored centrally - * in FILE_Secure and the correct descriptor is found using the security_id - * from the standard information attribute. - */ -typedef SECURITY_DESCRIPTOR_RELATIVE SECURITY_DESCRIPTOR_ATTR; - -/* - * On NTFS 3.0+, all security descriptors are stored in FILE_Secure. Only one - * referenced instance of each unique security descriptor is stored. - * - * FILE_Secure contains no unnamed data attribute, i.e. it has zero length. It - * does, however, contain two indexes ($SDH and $SII) as well as a named data - * stream ($SDS). - * - * Every unique security descriptor is assigned a unique security identifier - * (security_id, not to be confused with a SID). The security_id is unique for - * the NTFS volume and is used as an index into the $SII index, which maps - * security_ids to the security descriptor's storage location within the $SDS - * data attribute. The $SII index is sorted by ascending security_id. - * - * A simple hash is computed from each security descriptor. This hash is used - * as an index into the $SDH index, which maps security descriptor hashes to - * the security descriptor's storage location within the $SDS data attribute. - * The $SDH index is sorted by security descriptor hash and is stored in a B+ - * tree. When searching $SDH (with the intent of determining whether or not a - * new security descriptor is already present in the $SDS data stream), if a - * matching hash is found, but the security descriptors do not match, the - * search in the $SDH index is continued, searching for a next matching hash. - * - * When a precise match is found, the security_id corresponding to the security - * descriptor in the $SDS attribute is read from the found $SDH index entry and - * is stored in the $STANDARD_INFORMATION attribute of the file/directory to - * which the security descriptor is being applied. The $STANDARD_INFORMATION - * attribute is present in all base mft records (i.e. in all files and - * directories). - * - * If a match is not found, the security descriptor is assigned a new unique - * security_id and is added to the $SDS data attribute. Then, entries - * referencing the this security descriptor in the $SDS data attribute are - * added to the $SDH and $SII indexes. - * - * Note: Entries are never deleted from FILE_Secure, even if nothing - * references an entry any more. - */ - -/** - * struct SECURITY_DESCRIPTOR_HEADER - - * - * This header precedes each security descriptor in the $SDS data stream. - * This is also the index entry data part of both the $SII and $SDH indexes. - */ -typedef struct { - u32 hash; /* Hash of the security descriptor. */ - u32 security_id; /* The security_id assigned to the descriptor. */ - u64 offset; /* Byte offset of this entry in the $SDS stream. */ - u32 length; /* Size in bytes of this entry in $SDS stream. */ -} __attribute__((__packed__)) SECURITY_DESCRIPTOR_HEADER; - -/** - * struct SDH_INDEX_DATA - - */ -typedef struct { - u32 hash; /* Hash of the security descriptor. */ - u32 security_id; /* The security_id assigned to the descriptor. */ - u64 offset; /* Byte offset of this entry in the $SDS stream. */ - u32 length; /* Size in bytes of this entry in $SDS stream. */ - u32 reserved_II; /* Padding - always unicode "II" or zero. This field - isn't counted in INDEX_ENTRY's data_length. */ -} __attribute__((__packed__)) SDH_INDEX_DATA; - -/** - * struct SII_INDEX_DATA - - */ -typedef SECURITY_DESCRIPTOR_HEADER SII_INDEX_DATA; - -/** - * struct SDS_ENTRY - - * - * The $SDS data stream contains the security descriptors, aligned on 16-byte - * boundaries, sorted by security_id in a B+ tree. Security descriptors cannot - * cross 256kib boundaries (this restriction is imposed by the Windows cache - * manager). Each security descriptor is contained in a SDS_ENTRY structure. - * Also, each security descriptor is stored twice in the $SDS stream with a - * fixed offset of 0x40000 bytes (256kib, the Windows cache manager's max size) - * between them; i.e. if a SDS_ENTRY specifies an offset of 0x51d0, then the - * the first copy of the security descriptor will be at offset 0x51d0 in the - * $SDS data stream and the second copy will be at offset 0x451d0. - */ -typedef struct { -/* 0 SECURITY_DESCRIPTOR_HEADER; -- Unfolded here as gcc doesn't like - unnamed structs. */ - u32 hash; /* Hash of the security descriptor. */ - u32 security_id; /* The security_id assigned to the descriptor. */ - u64 offset; /* Byte offset of this entry in the $SDS stream. */ - u32 length; /* Size in bytes of this entry in $SDS stream. */ -/* 20*/ SECURITY_DESCRIPTOR_RELATIVE sid; /* The self-relative security - descriptor. */ -} __attribute__((__packed__)) SDS_ENTRY; - -/** - * struct SII_INDEX_KEY - The index entry key used in the $SII index. - * - * The collation type is COLLATION_NTOFS_ULONG. - */ -typedef struct { - u32 security_id; /* The security_id assigned to the descriptor. */ -} __attribute__((__packed__)) SII_INDEX_KEY; - -/** - * struct SDH_INDEX_KEY - The index entry key used in the $SDH index. - * - * The keys are sorted first by hash and then by security_id. - * The collation rule is COLLATION_NTOFS_SECURITY_HASH. - */ -typedef struct { - u32 hash; /* Hash of the security descriptor. */ - u32 security_id; /* The security_id assigned to the descriptor. */ -} __attribute__((__packed__)) SDH_INDEX_KEY; - -/** - * struct VOLUME_NAME - Attribute: Volume name (0x60). - * - * NOTE: Always resident. - * NOTE: Present only in FILE_Volume. - */ -typedef struct { - ntfschar name[0]; /* The name of the volume in Unicode. */ -} __attribute__((__packed__)) VOLUME_NAME; - -/** - * enum VOLUME_FLAGS - Possible flags for the volume (16-bit). - */ -typedef enum { - VOLUME_IS_DIRTY = const_cpu_to_le16(0x0001), - VOLUME_RESIZE_LOG_FILE = const_cpu_to_le16(0x0002), - VOLUME_UPGRADE_ON_MOUNT = const_cpu_to_le16(0x0004), - VOLUME_MOUNTED_ON_NT4 = const_cpu_to_le16(0x0008), - VOLUME_DELETE_USN_UNDERWAY = const_cpu_to_le16(0x0010), - VOLUME_REPAIR_OBJECT_ID = const_cpu_to_le16(0x0020), - VOLUME_CHKDSK_UNDERWAY = const_cpu_to_le16(0x4000), - VOLUME_MODIFIED_BY_CHKDSK = const_cpu_to_le16(0x8000), - VOLUME_FLAGS_MASK = const_cpu_to_le16(0xc03f), -} __attribute__((__packed__)) VOLUME_FLAGS; - -/** - * struct VOLUME_INFORMATION - Attribute: Volume information (0x70). - * - * NOTE: Always resident. - * NOTE: Present only in FILE_Volume. - * NOTE: Windows 2000 uses NTFS 3.0 while Windows NT4 service pack 6a uses - * NTFS 1.2. I haven't personally seen other values yet. - */ -typedef struct { - u64 reserved; /* Not used (yet?). */ - u8 major_ver; /* Major version of the ntfs format. */ - u8 minor_ver; /* Minor version of the ntfs format. */ - VOLUME_FLAGS flags; /* Bit array of VOLUME_* flags. */ -} __attribute__((__packed__)) VOLUME_INFORMATION; - -/** - * struct DATA_ATTR - Attribute: Data attribute (0x80). - * - * NOTE: Can be resident or non-resident. - * - * Data contents of a file (i.e. the unnamed stream) or of a named stream. - */ -typedef struct { - u8 data[0]; /* The file's data contents. */ -} __attribute__((__packed__)) DATA_ATTR; - -/** - * enum INDEX_HEADER_FLAGS - Index header flags (8-bit). - */ -typedef enum { - /* When index header is in an index root attribute: */ - SMALL_INDEX = 0, /* The index is small enough to fit inside the - index root attribute and there is no index - allocation attribute present. */ - LARGE_INDEX = 1, /* The index is too large to fit in the index - root attribute and/or an index allocation - attribute is present. */ - /* - * When index header is in an index block, i.e. is part of index - * allocation attribute: - */ - LEAF_NODE = 0, /* This is a leaf node, i.e. there are no more - nodes branching off it. */ - INDEX_NODE = 1, /* This node indexes other nodes, i.e. is not a - leaf node. */ - NODE_MASK = 1, /* Mask for accessing the *_NODE bits. */ -} __attribute__((__packed__)) INDEX_HEADER_FLAGS; - -/** - * struct INDEX_HEADER - - * - * This is the header for indexes, describing the INDEX_ENTRY records, which - * follow the INDEX_HEADER. Together the index header and the index entries - * make up a complete index. - * - * IMPORTANT NOTE: The offset, length and size structure members are counted - * relative to the start of the index header structure and not relative to the - * start of the index root or index allocation structures themselves. - */ -typedef struct { - u32 entries_offset; /* Byte offset to first INDEX_ENTRY - aligned to 8-byte boundary. */ - u32 index_length; /* Data size of the index in bytes, - i.e. bytes used from allocated - size, aligned to 8-byte boundary. */ - u32 allocated_size; /* Byte size of this index (block), - multiple of 8 bytes. */ - /* NOTE: For the index root attribute, the above two numbers are always - equal, as the attribute is resident and it is resized as needed. In - the case of the index allocation attribute the attribute is not - resident and hence the allocated_size is a fixed value and must - equal the index_block_size specified by the INDEX_ROOT attribute - corresponding to the INDEX_ALLOCATION attribute this INDEX_BLOCK - belongs to. */ - INDEX_HEADER_FLAGS flags; /* Bit field of INDEX_HEADER_FLAGS. */ - u8 reserved[3]; /* Reserved/align to 8-byte boundary. */ -} __attribute__((__packed__)) INDEX_HEADER; - -/** - * struct INDEX_ROOT - Attribute: Index root (0x90). - * - * NOTE: Always resident. - * - * This is followed by a sequence of index entries (INDEX_ENTRY structures) - * as described by the index header. - * - * When a directory is small enough to fit inside the index root then this - * is the only attribute describing the directory. When the directory is too - * large to fit in the index root, on the other hand, two additional attributes - * are present: an index allocation attribute, containing sub-nodes of the B+ - * directory tree (see below), and a bitmap attribute, describing which virtual - * cluster numbers (vcns) in the index allocation attribute are in use by an - * index block. - * - * NOTE: The root directory (FILE_root) contains an entry for itself. Other - * directories do not contain entries for themselves, though. - */ -typedef struct { - ATTR_TYPES type; /* Type of the indexed attribute. Is - $FILE_NAME for directories, zero - for view indexes. No other values - allowed. */ - COLLATION_RULES collation_rule; /* Collation rule used to sort the - index entries. If type is $FILE_NAME, - this must be COLLATION_FILE_NAME. */ - u32 index_block_size; /* Size of each index block in bytes (in - the index allocation attribute). */ - s8 clusters_per_index_block; /* Cluster size of each index block (in - the index allocation attribute), when - an index block is >= than a cluster, - otherwise this will be the -log of - the size (like how the encoding of - the mft record size and the index - record size found in the boot sector - work). Has to be a power of 2. */ - u8 reserved[3]; /* Reserved/align to 8-byte boundary. */ - INDEX_HEADER index; /* Index header describing the - following index entries. */ -} __attribute__((__packed__)) INDEX_ROOT; - -/** - * struct INDEX_BLOCK - Attribute: Index allocation (0xa0). - * - * NOTE: Always non-resident (doesn't make sense to be resident anyway!). - * - * This is an array of index blocks. Each index block starts with an - * INDEX_BLOCK structure containing an index header, followed by a sequence of - * index entries (INDEX_ENTRY structures), as described by the INDEX_HEADER. - */ -typedef struct { -/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ - NTFS_RECORD_TYPES magic;/* Magic is "INDX". */ - u16 usa_ofs; /* See NTFS_RECORD definition. */ - u16 usa_count; /* See NTFS_RECORD definition. */ - -/* 8*/ LSN lsn; /* $LogFile sequence number of the last - modification of this index block. */ -/* 16*/ VCN index_block_vcn; /* Virtual cluster number of the index block. */ -/* 24*/ INDEX_HEADER index; /* Describes the following index entries. */ -/* sizeof()= 40 (0x28) bytes */ -/* - * When creating the index block, we place the update sequence array at this - * offset, i.e. before we start with the index entries. This also makes sense, - * otherwise we could run into problems with the update sequence array - * containing in itself the last two bytes of a sector which would mean that - * multi sector transfer protection wouldn't work. As you can't protect data - * by overwriting it since you then can't get it back... - * When reading use the data from the ntfs record header. - */ -} __attribute__((__packed__)) INDEX_BLOCK; - -typedef INDEX_BLOCK INDEX_ALLOCATION; - -/** - * struct REPARSE_INDEX_KEY - - * - * The system file FILE_Extend/$Reparse contains an index named $R listing - * all reparse points on the volume. The index entry keys are as defined - * below. Note, that there is no index data associated with the index entries. - * - * The index entries are sorted by the index key file_id. The collation rule is - * COLLATION_NTOFS_ULONGS. FIXME: Verify whether the reparse_tag is not the - * primary key / is not a key at all. (AIA) - */ -typedef struct { - u32 reparse_tag; /* Reparse point type (inc. flags). */ - MFT_REF file_id; /* Mft record of the file containing the - reparse point attribute. */ -} __attribute__((__packed__)) REPARSE_INDEX_KEY; - -/** - * enum QUOTA_FLAGS - Quota flags (32-bit). - */ -typedef enum { - /* The user quota flags. Names explain meaning. */ - QUOTA_FLAG_DEFAULT_LIMITS = const_cpu_to_le32(0x00000001), - QUOTA_FLAG_LIMIT_REACHED = const_cpu_to_le32(0x00000002), - QUOTA_FLAG_ID_DELETED = const_cpu_to_le32(0x00000004), - - QUOTA_FLAG_USER_MASK = const_cpu_to_le32(0x00000007), - /* Bit mask for user quota flags. */ - - /* These flags are only present in the quota defaults index entry, - i.e. in the entry where owner_id = QUOTA_DEFAULTS_ID. */ - QUOTA_FLAG_TRACKING_ENABLED = const_cpu_to_le32(0x00000010), - QUOTA_FLAG_ENFORCEMENT_ENABLED = const_cpu_to_le32(0x00000020), - QUOTA_FLAG_TRACKING_REQUESTED = const_cpu_to_le32(0x00000040), - QUOTA_FLAG_LOG_THRESHOLD = const_cpu_to_le32(0x00000080), - QUOTA_FLAG_LOG_LIMIT = const_cpu_to_le32(0x00000100), - QUOTA_FLAG_OUT_OF_DATE = const_cpu_to_le32(0x00000200), - QUOTA_FLAG_CORRUPT = const_cpu_to_le32(0x00000400), - QUOTA_FLAG_PENDING_DELETES = const_cpu_to_le32(0x00000800), -} QUOTA_FLAGS; - -/** - * struct QUOTA_CONTROL_ENTRY - - * - * The system file FILE_Extend/$Quota contains two indexes $O and $Q. Quotas - * are on a per volume and per user basis. - * - * The $Q index contains one entry for each existing user_id on the volume. The - * index key is the user_id of the user/group owning this quota control entry, - * i.e. the key is the owner_id. The user_id of the owner of a file, i.e. the - * owner_id, is found in the standard information attribute. The collation rule - * for $Q is COLLATION_NTOFS_ULONG. - * - * The $O index contains one entry for each user/group who has been assigned - * a quota on that volume. The index key holds the SID of the user_id the - * entry belongs to, i.e. the owner_id. The collation rule for $O is - * COLLATION_NTOFS_SID. - * - * The $O index entry data is the user_id of the user corresponding to the SID. - * This user_id is used as an index into $Q to find the quota control entry - * associated with the SID. - * - * The $Q index entry data is the quota control entry and is defined below. - */ -typedef struct { - u32 version; /* Currently equals 2. */ - QUOTA_FLAGS flags; /* Flags describing this quota entry. */ - u64 bytes_used; /* How many bytes of the quota are in use. */ - s64 change_time; /* Last time this quota entry was changed. */ - s64 threshold; /* Soft quota (-1 if not limited). */ - s64 limit; /* Hard quota (-1 if not limited). */ - s64 exceeded_time; /* How long the soft quota has been exceeded. */ -/* The below field is NOT present for the quota defaults entry. */ - SID sid; /* The SID of the user/object associated with - this quota entry. If this field is missing - then the INDEX_ENTRY is padded with zeros - to multiply of 8 which are not counted in - the data_length field. If the sid is present - then this structure is padded with zeros to - multiply of 8 and the padding is counted in - the INDEX_ENTRY's data_length. */ -} __attribute__((__packed__)) QUOTA_CONTROL_ENTRY; - -/** - * struct QUOTA_O_INDEX_DATA - - */ -typedef struct { - u32 owner_id; - u32 unknown; /* Always 32. Seems to be padding and it's not - counted in the INDEX_ENTRY's data_length. - This field shouldn't be really here. */ -} __attribute__((__packed__)) QUOTA_O_INDEX_DATA; - -/** - * enum PREDEFINED_OWNER_IDS - Predefined owner_id values (32-bit). - */ -typedef enum { - QUOTA_INVALID_ID = const_cpu_to_le32(0x00000000), - QUOTA_DEFAULTS_ID = const_cpu_to_le32(0x00000001), - QUOTA_FIRST_USER_ID = const_cpu_to_le32(0x00000100), -} PREDEFINED_OWNER_IDS; - -/** - * enum INDEX_ENTRY_FLAGS - Index entry flags (16-bit). - */ -typedef enum { - INDEX_ENTRY_NODE = const_cpu_to_le16(1), /* This entry contains a - sub-node, i.e. a reference to an index - block in form of a virtual cluster - number (see below). */ - INDEX_ENTRY_END = const_cpu_to_le16(2), /* This signifies the last - entry in an index block. The index - entry does not represent a file but it - can point to a sub-node. */ - INDEX_ENTRY_SPACE_FILLER = 0xffff, /* Just to force 16-bit width. */ -} __attribute__((__packed__)) INDEX_ENTRY_FLAGS; - -/** - * struct INDEX_ENTRY_HEADER - This the index entry header (see below). - */ -typedef struct { -/* 0*/ union { /* Only valid when INDEX_ENTRY_END is not set. */ - MFT_REF indexed_file; /* The mft reference of the file - described by this index - entry. Used for directory - indexes. */ - struct { /* Used for views/indexes to find the entry's data. */ - u16 data_offset; /* Data byte offset from this - INDEX_ENTRY. Follows the - index key. */ - u16 data_length; /* Data length in bytes. */ - u32 reservedV; /* Reserved (zero). */ - } __attribute__((__packed__)); - } __attribute__((__packed__)); -/* 8*/ u16 length; /* Byte size of this index entry, multiple of - 8-bytes. */ -/* 10*/ u16 key_length; /* Byte size of the key value, which is in the - index entry. It follows field reserved. Not - multiple of 8-bytes. */ -/* 12*/ INDEX_ENTRY_FLAGS flags; /* Bit field of INDEX_ENTRY_* flags. */ -/* 14*/ u16 reserved; /* Reserved/align to 8-byte boundary. */ -/* sizeof() = 16 bytes */ -} __attribute__((__packed__)) INDEX_ENTRY_HEADER; - -/** - * struct INDEX_ENTRY - This is an index entry. - * - * A sequence of such entries follows each INDEX_HEADER structure. Together - * they make up a complete index. The index follows either an index root - * attribute or an index allocation attribute. - * - * NOTE: Before NTFS 3.0 only filename attributes were indexed. - */ -typedef struct { -/* 0 INDEX_ENTRY_HEADER; -- Unfolded here as gcc dislikes unnamed structs. */ - union { /* Only valid when INDEX_ENTRY_END is not set. */ - MFT_REF indexed_file; /* The mft reference of the file - described by this index - entry. Used for directory - indexes. */ - struct { /* Used for views/indexes to find the entry's data. */ - u16 data_offset; /* Data byte offset from this - INDEX_ENTRY. Follows the - index key. */ - u16 data_length; /* Data length in bytes. */ - u32 reservedV; /* Reserved (zero). */ - } __attribute__((__packed__)); - } __attribute__((__packed__)); - u16 length; /* Byte size of this index entry, multiple of - 8-bytes. */ - u16 key_length; /* Byte size of the key value, which is in the - index entry. It follows field reserved. Not - multiple of 8-bytes. */ - INDEX_ENTRY_FLAGS flags; /* Bit field of INDEX_ENTRY_* flags. */ - u16 reserved; /* Reserved/align to 8-byte boundary. */ - -/* 16*/ union { /* The key of the indexed attribute. NOTE: Only present - if INDEX_ENTRY_END bit in flags is not set. NOTE: On - NTFS versions before 3.0 the only valid key is the - FILE_NAME_ATTR. On NTFS 3.0+ the following - additional index keys are defined: */ - FILE_NAME_ATTR file_name;/* $I30 index in directories. */ - SII_INDEX_KEY sii; /* $SII index in $Secure. */ - SDH_INDEX_KEY sdh; /* $SDH index in $Secure. */ - GUID object_id; /* $O index in FILE_Extend/$ObjId: The - object_id of the mft record found in - the data part of the index. */ - REPARSE_INDEX_KEY reparse; /* $R index in - FILE_Extend/$Reparse. */ - SID sid; /* $O index in FILE_Extend/$Quota: - SID of the owner of the user_id. */ - u32 owner_id; /* $Q index in FILE_Extend/$Quota: - user_id of the owner of the quota - control entry in the data part of - the index. */ - } __attribute__((__packed__)) key; - /* The (optional) index data is inserted here when creating. */ - // VCN vcn; /* If INDEX_ENTRY_NODE bit in flags is set, the last - // eight bytes of this index entry contain the virtual - // cluster number of the index block that holds the - // entries immediately preceding the current entry (the - // vcn references the corresponding cluster in the data - // of the non-resident index allocation attribute). If - // the key_length is zero, then the vcn immediately - // follows the INDEX_ENTRY_HEADER. Regardless of - // key_length, the address of the 8-byte boundary - // aligned vcn of INDEX_ENTRY{_HEADER} *ie is given by - // (char*)ie + le16_to_cpu(ie->length) - sizeof(VCN), - // where sizeof(VCN) can be hardcoded as 8 if wanted. */ -} __attribute__((__packed__)) INDEX_ENTRY; - -/** - * struct BITMAP_ATTR - Attribute: Bitmap (0xb0). - * - * Contains an array of bits (aka a bitfield). - * - * When used in conjunction with the index allocation attribute, each bit - * corresponds to one index block within the index allocation attribute. Thus - * the number of bits in the bitmap * index block size / cluster size is the - * number of clusters in the index allocation attribute. - */ -typedef struct { - u8 bitmap[0]; /* Array of bits. */ -} __attribute__((__packed__)) BITMAP_ATTR; - -/** - * enum PREDEFINED_REPARSE_TAGS - - * - * The reparse point tag defines the type of the reparse point. It also - * includes several flags, which further describe the reparse point. - * - * The reparse point tag is an unsigned 32-bit value divided in three parts: - * - * 1. The least significant 16 bits (i.e. bits 0 to 15) specify the type of - * the reparse point. - * 2. The 13 bits after this (i.e. bits 16 to 28) are reserved for future use. - * 3. The most significant three bits are flags describing the reparse point. - * They are defined as follows: - * bit 29: Name surrogate bit. If set, the filename is an alias for - * another object in the system. - * bit 30: High-latency bit. If set, accessing the first byte of data will - * be slow. (E.g. the data is stored on a tape drive.) - * bit 31: Microsoft bit. If set, the tag is owned by Microsoft. User - * defined tags have to use zero here. - */ -typedef enum { - IO_REPARSE_TAG_IS_ALIAS = const_cpu_to_le32(0x20000000), - IO_REPARSE_TAG_IS_HIGH_LATENCY = const_cpu_to_le32(0x40000000), - IO_REPARSE_TAG_IS_MICROSOFT = const_cpu_to_le32(0x80000000), - - IO_REPARSE_TAG_RESERVED_ZERO = const_cpu_to_le32(0x00000000), - IO_REPARSE_TAG_RESERVED_ONE = const_cpu_to_le32(0x00000001), - IO_REPARSE_TAG_RESERVED_RANGE = const_cpu_to_le32(0x00000001), - - IO_REPARSE_TAG_NSS = const_cpu_to_le32(0x68000005), - IO_REPARSE_TAG_NSS_RECOVER = const_cpu_to_le32(0x68000006), - IO_REPARSE_TAG_SIS = const_cpu_to_le32(0x68000007), - IO_REPARSE_TAG_DFS = const_cpu_to_le32(0x68000008), - - IO_REPARSE_TAG_MOUNT_POINT = const_cpu_to_le32(0x88000003), - - IO_REPARSE_TAG_HSM = const_cpu_to_le32(0xa8000004), - - IO_REPARSE_TAG_SYMBOLIC_LINK = const_cpu_to_le32(0xe8000000), - - IO_REPARSE_TAG_VALID_VALUES = const_cpu_to_le32(0xe000ffff), -} PREDEFINED_REPARSE_TAGS; - -/** - * struct REPARSE_POINT - Attribute: Reparse point (0xc0). - * - * NOTE: Can be resident or non-resident. - */ -typedef struct { - u32 reparse_tag; /* Reparse point type (inc. flags). */ - u16 reparse_data_length; /* Byte size of reparse data. */ - u16 reserved; /* Align to 8-byte boundary. */ - u8 reparse_data[0]; /* Meaning depends on reparse_tag. */ -} __attribute__((__packed__)) REPARSE_POINT; - -/** - * struct EA_INFORMATION - Attribute: Extended attribute information (0xd0). - * - * NOTE: Always resident. - */ -typedef struct { - u16 ea_length; /* Byte size of the packed extended - attributes. */ - u16 need_ea_count; /* The number of extended attributes which have - the NEED_EA bit set. */ - u32 ea_query_length; /* Byte size of the buffer required to query - the extended attributes when calling - ZwQueryEaFile() in Windows NT/2k. I.e. the - byte size of the unpacked extended - attributes. */ -} __attribute__((__packed__)) EA_INFORMATION; - -/** - * enum EA_FLAGS - Extended attribute flags (8-bit). - */ -typedef enum { - NEED_EA = 0x80, /* Indicate that the file to which the EA - belongs cannot be interpreted without - understanding the associated extended - attributes. */ -} __attribute__((__packed__)) EA_FLAGS; - -/** - * struct EA_ATTR - Attribute: Extended attribute (EA) (0xe0). - * - * Like the attribute list and the index buffer list, the EA attribute value is - * a sequence of EA_ATTR variable length records. - * - * FIXME: It appears weird that the EA name is not Unicode. Is it true? - * FIXME: It seems that name is always uppercased. Is it true? - */ -typedef struct { - u32 next_entry_offset; /* Offset to the next EA_ATTR. */ - EA_FLAGS flags; /* Flags describing the EA. */ - u8 name_length; /* Length of the name of the extended - attribute in bytes. */ - u16 value_length; /* Byte size of the EA's value. */ - u8 name[0]; /* Name of the EA. */ - u8 value[0]; /* The value of the EA. Immediately - follows the name. */ -} __attribute__((__packed__)) EA_ATTR; - -/** - * struct PROPERTY_SET - Attribute: Property set (0xf0). - * - * Intended to support Native Structure Storage (NSS) - a feature removed from - * NTFS 3.0 during beta testing. - */ -typedef struct { - /* Irrelevant as feature unused. */ -} __attribute__((__packed__)) PROPERTY_SET; - -/** - * struct LOGGED_UTILITY_STREAM - Attribute: Logged utility stream (0x100). - * - * NOTE: Can be resident or non-resident. - * - * Operations on this attribute are logged to the journal ($LogFile) like - * normal metadata changes. - * - * Used by the Encrypting File System (EFS). All encrypted files have this - * attribute with the name $EFS. See below for the relevant structures. - */ -typedef struct { - /* Can be anything the creator chooses. */ -} __attribute__((__packed__)) LOGGED_UTILITY_STREAM; - -/* - * $EFS Data Structure: - * - * The following information is about the data structures that are contained - * inside a logged utility stream (0x100) with a name of "$EFS". - * - * The stream starts with an instance of EFS_ATTR_HEADER. - * - * Next, at offsets offset_to_ddf_array and offset_to_drf_array (unless any of - * them is 0) there is a EFS_DF_ARRAY_HEADER immediately followed by a sequence - * of multiple data decryption/recovery fields. - * - * Each data decryption/recovery field starts with a EFS_DF_HEADER and the next - * one (if it exists) can be found by adding EFS_DF_HEADER->df_length bytes to - * the offset of the beginning of the current EFS_DF_HEADER. - * - * The data decryption/recovery field contains an EFS_DF_CERTIFICATE_HEADER, a - * SID, an optional GUID, an optional container name, a non-optional user name, - * and the encrypted FEK. - * - * Note all the below are best guesses so may have mistakes/inaccuracies. - * Corrections/clarifications/additions are always welcome! - * - * Ntfs.sys takes an EFS value length of <= 0x54 or > 0x40000 to BSOD, i.e. it - * is invalid. - */ - -/** - * struct EFS_ATTR_HEADER - "$EFS" header. - * - * The header of the Logged utility stream (0x100) attribute named "$EFS". - */ -typedef struct { -/* 0*/ u32 length; /* Length of EFS attribute in bytes. */ - u32 state; /* Always 0? */ - u32 version; /* Efs version. Always 2? */ - u32 crypto_api_version; /* Always 0? */ -/* 16*/ u8 unknown4[16]; /* MD5 hash of decrypted FEK? This field is - created with a call to UuidCreate() so is - unlikely to be an MD5 hash and is more - likely to be GUID of this encrytped file - or something like that. */ -/* 32*/ u8 unknown5[16]; /* MD5 hash of DDFs? */ -/* 48*/ u8 unknown6[16]; /* MD5 hash of DRFs? */ -/* 64*/ u32 offset_to_ddf_array;/* Offset in bytes to the array of data - decryption fields (DDF), see below. Zero if - no DDFs are present. */ - u32 offset_to_drf_array;/* Offset in bytes to the array of data - recovery fields (DRF), see below. Zero if - no DRFs are present. */ - u32 reserved; /* Reserved. */ -} __attribute__((__packed__)) EFS_ATTR_HEADER; - -/** - * struct EFS_DF_ARRAY_HEADER - - */ -typedef struct { - u32 df_count; /* Number of data decryption/recovery fields in - the array. */ -} __attribute__((__packed__)) EFS_DF_ARRAY_HEADER; - -/** - * struct EFS_DF_HEADER - - */ -typedef struct { -/* 0*/ u32 df_length; /* Length of this data decryption/recovery - field in bytes. */ - u32 cred_header_offset; /* Offset in bytes to the credential header. */ - u32 fek_size; /* Size in bytes of the encrypted file - encryption key (FEK). */ - u32 fek_offset; /* Offset in bytes to the FEK from the start of - the data decryption/recovery field. */ -/* 16*/ u32 unknown1; /* always 0? Might be just padding. */ -} __attribute__((__packed__)) EFS_DF_HEADER; - -/** - * struct EFS_DF_CREDENTIAL_HEADER - - */ -typedef struct { -/* 0*/ u32 cred_length; /* Length of this credential in bytes. */ - u32 sid_offset; /* Offset in bytes to the user's sid from start - of this structure. Zero if no sid is - present. */ -/* 8*/ u32 type; /* Type of this credential: - 1 = CryptoAPI container. - 2 = Unexpected type. - 3 = Certificate thumbprint. - other = Unknown type. */ - union { - /* CryptoAPI container. */ - struct { -/* 12*/ u32 container_name_offset; /* Offset in bytes to - the name of the container from start of this - structure (may not be zero). */ -/* 16*/ u32 provider_name_offset; /* Offset in bytes to - the name of the provider from start of this - structure (may not be zero). */ - u32 public_key_blob_offset; /* Offset in bytes to - the public key blob from start of this - structure. */ -/* 24*/ u32 public_key_blob_size; /* Size in bytes of - public key blob. */ - } __attribute__((__packed__)); - /* Certificate thumbprint. */ - struct { -/* 12*/ u32 cert_thumbprint_header_size; /* Size in - bytes of the header of the certificate - thumbprint. */ -/* 16*/ u32 cert_thumbprint_header_offset; /* Offset in - bytes to the header of the certificate - thumbprint from start of this structure. */ - u32 unknown1; /* Always 0? Might be padding... */ - u32 unknown2; /* Always 0? Might be padding... */ - } __attribute__((__packed__)); - } __attribute__((__packed__)); -} __attribute__((__packed__)) EFS_DF_CREDENTIAL_HEADER; - -typedef EFS_DF_CREDENTIAL_HEADER EFS_DF_CRED_HEADER; - -/** - * struct EFS_DF_CERTIFICATE_THUMBPRINT_HEADER - - */ -typedef struct { -/* 0*/ u32 thumbprint_offset; /* Offset in bytes to the thumbprint. */ - u32 thumbprint_size; /* Size of thumbprint in bytes. */ -/* 8*/ u32 container_name_offset; /* Offset in bytes to the name of the - container from start of this - structure or 0 if no name present. */ - u32 provider_name_offset; /* Offset in bytes to the name of the - cryptographic provider from start of - this structure or 0 if no name - present. */ -/* 16*/ u32 user_name_offset; /* Offset in bytes to the user name - from start of this structure or 0 if - no user name present. (This is also - known as lpDisplayInformation.) */ -} __attribute__((__packed__)) EFS_DF_CERTIFICATE_THUMBPRINT_HEADER; - -typedef EFS_DF_CERTIFICATE_THUMBPRINT_HEADER EFS_DF_CERT_THUMBPRINT_HEADER; - -typedef enum { - INTX_SYMBOLIC_LINK = - const_cpu_to_le64(0x014B4E4C78746E49ULL), /* "IntxLNK\1" */ - INTX_CHARACTER_DEVICE = - const_cpu_to_le64(0x0052484378746E49ULL), /* "IntxCHR\0" */ - INTX_BLOCK_DEVICE = - const_cpu_to_le64(0x004B4C4278746E49ULL), /* "IntxBLK\0" */ -} INTX_FILE_TYPES; - -typedef struct { - INTX_FILE_TYPES magic; /* Intx file magic. */ - union { - /* For character and block devices. */ - struct { - u64 major; /* Major device number. */ - u64 minor; /* Minor device number. */ - void *device_end[0]; /* Marker for offsetof(). */ - } __attribute__((__packed__)); - /* For symbolic links. */ - ntfschar target[0]; - } __attribute__((__packed__)); -} __attribute__((__packed__)) INTX_FILE; - -#endif /* defined _NTFS_LAYOUT_H */ diff --git a/include/ntfs/lcnalloc.h b/include/ntfs/lcnalloc.h deleted file mode 100644 index 07ab020d..00000000 --- a/include/ntfs/lcnalloc.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * lcnalloc.h - Exports for cluster (de)allocation. Part of the Linux-NTFS - * project. - * - * Copyright (c) 2002 Anton Altaparmakov - * Copyright (c) 2004 Yura Pakhuchiy - * - * 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 _NTFS_LCNALLOC_H -#define _NTFS_LCNALLOC_H - -#include "types.h" -#include "runlist.h" -#include "volume.h" - -/** - * enum NTFS_CLUSTER_ALLOCATION_ZONES - - */ -typedef enum { - FIRST_ZONE = 0, /* For sanity checking. */ - MFT_ZONE = 0, /* Allocate from $MFT zone. */ - DATA_ZONE = 1, /* Allocate from $DATA zone. */ - LAST_ZONE = 1, /* For sanity checking. */ -} NTFS_CLUSTER_ALLOCATION_ZONES; - -extern runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, - LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone); - -extern int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl); - -extern int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, - s64 count); - -#endif /* defined _NTFS_LCNALLOC_H */ - diff --git a/include/ntfs/logfile.h b/include/ntfs/logfile.h deleted file mode 100644 index 8aadc8c1..00000000 --- a/include/ntfs/logfile.h +++ /dev/null @@ -1,394 +0,0 @@ -/* - * logfile.h - Exports for $LogFile handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2005 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 _NTFS_LOGFILE_H -#define _NTFS_LOGFILE_H - -#include "types.h" -#include "endians.h" -#include "layout.h" - -/* - * Journal ($LogFile) organization: - * - * Two restart areas present in the first two pages (restart pages, one restart - * area in each page). When the volume is dismounted they should be identical, - * except for the update sequence array which usually has a different update - * sequence number. - * - * These are followed by log records organized in pages headed by a log record - * header going up to log file size. Not all pages contain log records when a - * volume is first formatted, but as the volume ages, all records will be used. - * When the log file fills up, the records at the beginning are purged (by - * modifying the oldest_lsn to a higher value presumably) and writing begins - * at the beginning of the file. Effectively, the log file is viewed as a - * circular entity. - * - * NOTE: Windows NT, 2000, and XP all use log file version 1.1 but they accept - * versions <= 1.x, including 0.-1. (Yes, that is a minus one in there!) We - * probably only want to support 1.1 as this seems to be the current version - * and we don't know how that differs from the older versions. The only - * exception is if the journal is clean as marked by the two restart pages - * then it doesn't matter whether we are on an earlier version. We can just - * reinitialize the logfile and start again with version 1.1. - */ - -/* Some $LogFile related constants. */ -#define MaxLogFileSize 0x100000000ULL -#define DefaultLogPageSize 4096 -#define MinLogRecordPages 48 - -/** - * struct RESTART_PAGE_HEADER - Log file restart page header. - * - * Begins the restart area. - */ -typedef struct { -/*Ofs*/ -/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ -/* 0*/ NTFS_RECORD_TYPES magic;/* The magic is "RSTR". */ -/* 4*/ le16 usa_ofs; /* See NTFS_RECORD definition in layout.h. - When creating, set this to be immediately - after this header structure (without any - alignment). */ -/* 6*/ le16 usa_count; /* See NTFS_RECORD definition in layout.h. */ - -/* 8*/ leLSN chkdsk_lsn; /* The last log file sequence number found by - chkdsk. Only used when the magic is changed - to "CHKD". Otherwise this is zero. */ -/* 16*/ le32 system_page_size; /* Byte size of system pages when the log file - was created, has to be >= 512 and a power of - 2. Use this to calculate the required size - of the usa (usa_count) and add it to usa_ofs. - Then verify that the result is less than the - value of the restart_area_offset. */ -/* 20*/ le32 log_page_size; /* Byte size of log file pages, has to be >= - 512 and a power of 2. The default is 4096 - and is used when the system page size is - between 4096 and 8192. Otherwise this is - set to the system page size instead. */ -/* 24*/ le16 restart_area_offset;/* Byte offset from the start of this header to - the RESTART_AREA. Value has to be aligned - to 8-byte boundary. When creating, set this - to be after the usa. */ -/* 26*/ sle16 minor_ver; /* Log file minor version. Only check if major - version is 1. */ -/* 28*/ sle16 major_ver; /* Log file major version. We only support - version 1.1. */ -/* sizeof() = 30 (0x1e) bytes */ -} __attribute__((__packed__)) RESTART_PAGE_HEADER; - -/* - * Constant for the log client indices meaning that there are no client records - * in this particular client array. Also inside the client records themselves, - * this means that there are no client records preceding or following this one. - */ -#define LOGFILE_NO_CLIENT const_cpu_to_le16(0xffff) -#define LOGFILE_NO_CLIENT_CPU 0xffff - -/* - * These are the so far known RESTART_AREA_* flags (16-bit) which contain - * information about the log file in which they are present. - */ -enum { - RESTART_VOLUME_IS_CLEAN = const_cpu_to_le16(0x0002), - RESTART_SPACE_FILLER = 0xffff, /* gcc: Force enum bit width to 16. */ -} __attribute__((__packed__)); - -typedef le16 RESTART_AREA_FLAGS; - -/** - * struct RESTART_AREA - Log file restart area record. - * - * The offset of this record is found by adding the offset of the - * RESTART_PAGE_HEADER to the restart_area_offset value found in it. - * See notes at restart_area_offset above. - */ -typedef struct { -/*Ofs*/ -/* 0*/ leLSN current_lsn; /* The current, i.e. last LSN inside the log - when the restart area was last written. - This happens often but what is the interval? - Is it just fixed time or is it every time a - check point is written or something else? - On create set to 0. */ -/* 8*/ le16 log_clients; /* Number of log client records in the array of - log client records which follows this - restart area. Must be 1. */ -/* 10*/ le16 client_free_list; /* The index of the first free log client record - in the array of log client records. - LOGFILE_NO_CLIENT means that there are no - free log client records in the array. - If != LOGFILE_NO_CLIENT, check that - log_clients > client_free_list. On Win2k - and presumably earlier, on a clean volume - this is != LOGFILE_NO_CLIENT, and it should - be 0, i.e. the first (and only) client - record is free and thus the logfile is - closed and hence clean. A dirty volume - would have left the logfile open and hence - this would be LOGFILE_NO_CLIENT. On WinXP - and presumably later, the logfile is always - open, even on clean shutdown so this should - always be LOGFILE_NO_CLIENT. */ -/* 12*/ le16 client_in_use_list;/* The index of the first in-use log client - record in the array of log client records. - LOGFILE_NO_CLIENT means that there are no - in-use log client records in the array. If - != LOGFILE_NO_CLIENT check that log_clients - > client_in_use_list. On Win2k and - presumably earlier, on a clean volume this - is LOGFILE_NO_CLIENT, i.e. there are no - client records in use and thus the logfile - is closed and hence clean. A dirty volume - would have left the logfile open and hence - this would be != LOGFILE_NO_CLIENT, and it - should be 0, i.e. the first (and only) - client record is in use. On WinXP and - presumably later, the logfile is always - open, even on clean shutdown so this should - always be 0. */ -/* 14*/ RESTART_AREA_FLAGS flags;/* Flags modifying LFS behaviour. On Win2k - and presumably earlier this is always 0. On - WinXP and presumably later, if the logfile - was shutdown cleanly, the second bit, - RESTART_VOLUME_IS_CLEAN, is set. This bit - is cleared when the volume is mounted by - WinXP and set when the volume is dismounted, - thus if the logfile is dirty, this bit is - clear. Thus we don't need to check the - Windows version to determine if the logfile - is clean. Instead if the logfile is closed, - we know it must be clean. If it is open and - this bit is set, we also know it must be - clean. If on the other hand the logfile is - open and this bit is clear, we can be almost - certain that the logfile is dirty. */ -/* 16*/ le32 seq_number_bits; /* How many bits to use for the sequence - number. This is calculated as 67 - the - number of bits required to store the logfile - size in bytes and this can be used in with - the specified file_size as a consistency - check. */ -/* 20*/ le16 restart_area_length;/* Length of the restart area including the - client array. Following checks required if - version matches. Otherwise, skip them. - restart_area_offset + restart_area_length - has to be <= system_page_size. Also, - restart_area_length has to be >= - client_array_offset + (log_clients * - sizeof(log client record)). */ -/* 22*/ le16 client_array_offset;/* Offset from the start of this record to - the first log client record if versions are - matched. When creating, set this to be - after this restart area structure, aligned - to 8-bytes boundary. If the versions do not - match, this is ignored and the offset is - assumed to be (sizeof(RESTART_AREA) + 7) & - ~7, i.e. rounded up to first 8-byte - boundary. Either way, client_array_offset - has to be aligned to an 8-byte boundary. - Also, restart_area_offset + - client_array_offset has to be <= 510. - Finally, client_array_offset + (log_clients - * sizeof(log client record)) has to be <= - system_page_size. On Win2k and presumably - earlier, this is 0x30, i.e. immediately - following this record. On WinXP and - presumably later, this is 0x40, i.e. there - are 16 extra bytes between this record and - the client array. This probably means that - the RESTART_AREA record is actually bigger - in WinXP and later. */ -/* 24*/ sle64 file_size; /* Usable byte size of the log file. If the - restart_area_offset + the offset of the - file_size are > 510 then corruption has - occurred. This is the very first check when - starting with the restart_area as if it - fails it means that some of the above values - will be corrupted by the multi sector - transfer protection. The file_size has to - be rounded down to be a multiple of the - log_page_size in the RESTART_PAGE_HEADER and - then it has to be at least big enough to - store the two restart pages and 48 (0x30) - log record pages. */ -/* 32*/ le32 last_lsn_data_length;/* Length of data of last LSN, not including - the log record header. On create set to - 0. */ -/* 36*/ le16 log_record_header_length;/* Byte size of the log record header. - If the version matches then check that the - value of log_record_header_length is a - multiple of 8, i.e. - (log_record_header_length + 7) & ~7 == - log_record_header_length. When creating set - it to sizeof(LOG_RECORD_HEADER), aligned to - 8 bytes. */ -/* 38*/ le16 log_page_data_offset;/* Offset to the start of data in a log record - page. Must be a multiple of 8. On create - set it to immediately after the update - sequence array of the log record page. */ -/* 40*/ le32 restart_log_open_count;/* A counter that gets incremented every - time the logfile is restarted which happens - at mount time when the logfile is opened. - When creating set to a random value. Win2k - sets it to the low 32 bits of the current - system time in NTFS format (see time.h). */ -/* 44*/ le32 reserved; /* Reserved/alignment to 8-byte boundary. */ -/* sizeof() = 48 (0x30) bytes */ -} __attribute__((__packed__)) RESTART_AREA; - -/** - * struct LOG_CLIENT_RECORD - Log client record. - * - * The offset of this record is found by adding the offset of the - * RESTART_AREA to the client_array_offset value found in it. - */ -typedef struct { -/*Ofs*/ -/* 0*/ leLSN oldest_lsn; /* Oldest LSN needed by this client. On create - set to 0. */ -/* 8*/ leLSN client_restart_lsn;/* LSN at which this client needs to restart - the volume, i.e. the current position within - the log file. At present, if clean this - should = current_lsn in restart area but it - probably also = current_lsn when dirty most - of the time. At create set to 0. */ -/* 16*/ le16 prev_client; /* The offset to the previous log client record - in the array of log client records. - LOGFILE_NO_CLIENT means there is no previous - client record, i.e. this is the first one. - This is always LOGFILE_NO_CLIENT. */ -/* 18*/ le16 next_client; /* The offset to the next log client record in - the array of log client records. - LOGFILE_NO_CLIENT means there are no next - client records, i.e. this is the last one. - This is always LOGFILE_NO_CLIENT. */ -/* 20*/ le16 seq_number; /* On Win2k and presumably earlier, this is set - to zero every time the logfile is restarted - and it is incremented when the logfile is - closed at dismount time. Thus it is 0 when - dirty and 1 when clean. On WinXP and - presumably later, this is always 0. */ -/* 22*/ u8 reserved[6]; /* Reserved/alignment. */ -/* 28*/ le32 client_name_length;/* Length of client name in bytes. Should - always be 8. */ -/* 32*/ ntfschar client_name[64];/* Name of the client in Unicode. Should - always be "NTFS" with the remaining bytes - set to 0. */ -/* sizeof() = 160 (0xa0) bytes */ -} __attribute__((__packed__)) LOG_CLIENT_RECORD; - -/** - * struct RECORD_PAGE_HEADER - Log page record page header. - * - * Each log page begins with this header and is followed by several LOG_RECORD - * structures, starting at offset 0x40 (the size of this structure and the - * following update sequence array and then aligned to 8 byte boundary, but is - * this specified anywhere?). - */ -typedef struct { -/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ - NTFS_RECORD_TYPES magic;/* Usually the magic is "RCRD". */ - u16 usa_ofs; /* See NTFS_RECORD definition in layout.h. - When creating, set this to be immediately - after this header structure (without any - alignment). */ - u16 usa_count; /* See NTFS_RECORD definition in layout.h. */ - - union { - LSN last_lsn; - s64 file_offset; - } __attribute__((__packed__)) copy; - u32 flags; - u16 page_count; - u16 page_position; - union { - struct { - u16 next_record_offset; - u8 reserved[6]; - LSN last_end_lsn; - } __attribute__((__packed__)) packed; - } __attribute__((__packed__)) header; -} __attribute__((__packed__)) RECORD_PAGE_HEADER; - -/** - * enum LOG_RECORD_FLAGS - Possible 16-bit flags for log records. - * - * (Or is it log record pages?) - */ -typedef enum { - LOG_RECORD_MULTI_PAGE = const_cpu_to_le16(0x0001), /* ??? */ - LOG_RECORD_SIZE_PLACE_HOLDER = 0xffff, - /* This has nothing to do with the log record. It is only so - gcc knows to make the flags 16-bit. */ -} __attribute__((__packed__)) LOG_RECORD_FLAGS; - -/** - * struct LOG_CLIENT_ID - The log client id structure identifying a log client. - */ -typedef struct { - u16 seq_number; - u16 client_index; -} __attribute__((__packed__)) LOG_CLIENT_ID; - -/** - * struct LOG_RECORD - Log record header. - * - * Each log record seems to have a constant size of 0x70 bytes. - */ -typedef struct { - LSN this_lsn; - LSN client_previous_lsn; - LSN client_undo_next_lsn; - u32 client_data_length; - LOG_CLIENT_ID client_id; - u32 record_type; - u32 transaction_id; - u16 flags; - u16 reserved_or_alignment[3]; -/* Now are at ofs 0x30 into struct. */ - u16 redo_operation; - u16 undo_operation; - u16 redo_offset; - u16 redo_length; - u16 undo_offset; - u16 undo_length; - u16 target_attribute; - u16 lcns_to_follow; /* Number of lcn_list entries - following this entry. */ -/* Now at ofs 0x40. */ - u16 record_offset; - u16 attribute_offset; - u32 alignment_or_reserved; - VCN target_vcn; -/* Now at ofs 0x50. */ - struct { /* Only present if lcns_to_follow - is not 0. */ - LCN lcn; - } __attribute__((__packed__)) lcn_list[0]; -} __attribute__((__packed__)) LOG_RECORD; - -extern BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp); -extern BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp); -extern int ntfs_empty_logfile(ntfs_attr *na); - -#endif /* defined _NTFS_LOGFILE_H */ diff --git a/include/ntfs/logging.h b/include/ntfs/logging.h deleted file mode 100644 index 0a66dbb3..00000000 --- a/include/ntfs/logging.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * logging.h - Centralised logging. Part of the Linux-NTFS project. - * - * Copyright (c) 2005 Richard Russon - * - * 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 _LOGGING_H_ -#define _LOGGING_H_ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDARG_H -#include -#endif - -#include "types.h" - -/* Function prototype for the logging handlers */ -typedef int (ntfs_log_handler)(const char *function, const char *file, int line, - u32 level, void *data, const char *format, va_list args); - -/* Set the logging handler from one of the functions, below. */ -void ntfs_log_set_handler(ntfs_log_handler *handler); - -/* Logging handlers */ -ntfs_log_handler ntfs_log_handler_syslog __attribute__((format(printf, 6, 0))); -ntfs_log_handler ntfs_log_handler_fprintf __attribute__((format(printf, 6, 0))); -ntfs_log_handler ntfs_log_handler_null __attribute__((format(printf, 6, 0))); -ntfs_log_handler ntfs_log_handler_stdout __attribute__((format(printf, 6, 0))); -ntfs_log_handler ntfs_log_handler_outerr __attribute__((format(printf, 6, 0))); -ntfs_log_handler ntfs_log_handler_stderr __attribute__((format(printf, 6, 0))); - -/* Enable/disable certain log levels */ -u32 ntfs_log_set_levels(u32 levels); -u32 ntfs_log_clear_levels(u32 levels); -u32 ntfs_log_get_levels(void); - -/* Enable/disable certain log flags */ -u32 ntfs_log_set_flags(u32 flags); -u32 ntfs_log_clear_flags(u32 flags); -u32 ntfs_log_get_flags(void); - -/* Turn command-line options into logging flags */ -BOOL ntfs_log_parse_option(const char *option); - -int ntfs_log_redirect(const char *function, const char *file, int line, - u32 level, void *data, const char *format, ...) - __attribute__((format(printf, 6, 7))); - -/* Logging levels - Determine what gets logged */ -#define NTFS_LOG_LEVEL_DEBUG (1 << 0) /* x = 42 */ -#define NTFS_LOG_LEVEL_TRACE (1 << 1) /* Entering function x() */ -#define NTFS_LOG_LEVEL_QUIET (1 << 2) /* Quietable output */ -#define NTFS_LOG_LEVEL_INFO (1 << 3) /* Volume needs defragmenting */ -#define NTFS_LOG_LEVEL_VERBOSE (1 << 4) /* Forced to continue */ -#define NTFS_LOG_LEVEL_PROGRESS (1 << 5) /* 54% complete */ -#define NTFS_LOG_LEVEL_WARNING (1 << 6) /* You should backup before starting */ -#define NTFS_LOG_LEVEL_ERROR (1 << 7) /* Operation failed, no damage done */ -#define NTFS_LOG_LEVEL_PERROR (1 << 8) /* Message : standard error description */ -#define NTFS_LOG_LEVEL_CRITICAL (1 << 9) /* Operation failed,damage may have occurred */ -#define NTFS_LOG_LEVEL_REASON (1 << 10) /* Human readable reason for failure */ - -/* Logging style flags - Manage the style of the output */ -#define NTFS_LOG_FLAG_PREFIX (1 << 0) /* Prefix messages with "ERROR: ", etc */ -#define NTFS_LOG_FLAG_FILENAME (1 << 1) /* Show the file origin of the message */ -#define NTFS_LOG_FLAG_LINE (1 << 2) /* Show the line number of the message */ -#define NTFS_LOG_FLAG_FUNCTION (1 << 3) /* Show the function name containing the message */ -#define NTFS_LOG_FLAG_ONLYNAME (1 << 4) /* Only display the filename, not the pathname */ -#define NTFS_LOG_FLAG_COLOUR (1 << 5) /* Colour highlight some messages */ - -/* Macros to simplify logging. One for each level defined above. - * Note, if NTFS_DISABLE_DEBUG_LOGGING is defined, then ntfs_log_debug/trace have no effect. - */ -#define ntfs_log_critical(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_CRITICAL,NULL,FORMAT,##ARGS) -#define ntfs_log_error(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ERROR,NULL,FORMAT,##ARGS) -#define ntfs_log_info(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_INFO,NULL,FORMAT,##ARGS) -#define ntfs_log_perror(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PERROR,NULL,FORMAT,##ARGS) -#define ntfs_log_progress(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PROGRESS,NULL,FORMAT,##ARGS) -#define ntfs_log_quiet(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_QUIET,NULL,FORMAT,##ARGS) -#define ntfs_log_verbose(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_VERBOSE,NULL,FORMAT,##ARGS) -#define ntfs_log_warning(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_WARNING,NULL,FORMAT,##ARGS) -#define ntfs_log_reason(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_REASON,NULL,FORMAT,##ARGS) - -/* By default debug and trace messages are compiled into the program, - * but not displayed. - */ -#ifdef NTFS_DISABLE_DEBUG_LOGGING -#define ntfs_log_debug(FORMAT, ARGS...)do {} while (0) -#define ntfs_log_trace(FORMAT, ARGS...)do {} while (0) -#else -#define ntfs_log_debug(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_DEBUG,NULL,FORMAT,##ARGS) -#define ntfs_log_trace(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_TRACE,NULL,FORMAT,##ARGS) -#endif /* NTFS_DISABLE_DEBUG_LOGGING */ - -#endif /* _LOGGING_H_ */ - diff --git a/include/ntfs/mft.h b/include/ntfs/mft.h deleted file mode 100644 index ce481c21..00000000 --- a/include/ntfs/mft.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * mft.h - Exports for MFT record handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2002 Anton Altaparmakov - * Copyright (c) 2004-2005 Richard Russon - * - * 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 _NTFS_MFT_H -#define _NTFS_MFT_H - -#include "volume.h" -#include "inode.h" -#include "layout.h" - -extern int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, - const s64 count, MFT_RECORD *b); - -/** - * ntfs_mft_record_read - read a record from the mft - * @vol: volume to read from - * @mref: mft record number to read - * @b: output data buffer - * - * Read the mft record specified by @mref from volume @vol into buffer @b. - * Return 0 on success or -1 on error, with errno set to the error code. - * - * The read mft record is mst deprotected and is hence ready to use. The caller - * should check the record with is_baad_record() in case mst deprotection - * failed. - * - * NOTE: @b has to be at least of size vol->mft_record_size. - */ -static __inline__ int ntfs_mft_record_read(const ntfs_volume *vol, - const MFT_REF mref, MFT_RECORD *b) -{ - return ntfs_mft_records_read(vol, mref, 1, b); -} - -extern int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, - MFT_RECORD **mrec, ATTR_RECORD **attr); - -extern int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, - const s64 count, MFT_RECORD *b); - -/** - * ntfs_mft_record_write - write an mft record to disk - * @vol: volume to write to - * @mref: mft record number to write - * @b: data buffer containing the mft record to write - * - * Write the mft record specified by @mref from buffer @b to volume @vol. - * Return 0 on success or -1 on error, with errno set to the error code. - * - * Before the mft record is written, it is mst protected. After the write, it - * is deprotected again, thus resulting in an increase in the update sequence - * number inside the buffer @b. - * - * NOTE: @b has to be at least of size vol->mft_record_size. - */ -static __inline__ int ntfs_mft_record_write(const ntfs_volume *vol, - const MFT_REF mref, MFT_RECORD *b) -{ - return ntfs_mft_records_write(vol, mref, 1, b); -} - -/** - * ntfs_mft_record_get_data_size - return number of bytes used in mft record @b - * @m: mft record to get the data size of - * - * Takes the mft record @m and returns the number of bytes used in the record - * or 0 on error (i.e. @m is not a valid mft record). Zero is not a valid size - * for an mft record as it at least has to have the MFT_RECORD itself and a - * zero length attribute of type AT_END, thus making the minimum size 56 bytes. - * - * Aside: The size is independent of NTFS versions 1.x/3.x because the 8-byte - * alignment of the first attribute mask the difference in MFT_RECORD size - * between NTFS 1.x and 3.x. Also, you would expect every mft record to - * contain an update sequence array as well but that could in theory be - * non-existent (don't know if Windows' NTFS driver/chkdsk wouldn't view this - * as corruption in itself though). - */ -static __inline__ u32 ntfs_mft_record_get_data_size(const MFT_RECORD *m) -{ - if (!m || !ntfs_is_mft_record(m->magic)) - return 0; - /* Get the number of used bytes and return it. */ - return le32_to_cpu(m->bytes_in_use); -} - -extern int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, - MFT_RECORD *mrec); - -extern int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref); - -extern ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni); - -extern int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni); - -extern int ntfs_mft_usn_dec(MFT_RECORD *mrec); - -#ifdef NTFS_RICH - -#include "bitmap.h" -#include "dir.h" - -int ntfs_mft_remove_attr(struct ntfs_bmp *bmp, ntfs_inode *inode, ATTR_TYPES type); -ATTR_RECORD * ntfs_mft_add_attr(ntfs_inode *inode, ATTR_TYPES type, u8 *data, int data_len); -int ntfs_mft_resize_resident(ntfs_inode *inode, ATTR_TYPES type, ntfschar *name, int name_len, u8 *data, int data_len); -int ntfs_mft_free_space(struct ntfs_dir *dir); -int ntfs_mft_add_index(struct ntfs_dir *dir); - -#endif /* NTFS_RICH */ - -#endif /* defined _NTFS_MFT_H */ - diff --git a/include/ntfs/mst.h b/include/ntfs/mst.h deleted file mode 100644 index 0808b3c1..00000000 --- a/include/ntfs/mst.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * mst.h - Exports for multi sector transfer fixup functions. Part of the - * Linux-NTFS project. - * - * Copyright (c) 2000-2002 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 _NTFS_MST_H -#define _NTFS_MST_H - -#include "types.h" -#include "layout.h" - -extern int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size); -extern int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size); -extern void ntfs_mst_post_write_fixup(NTFS_RECORD *b); - -#endif /* defined _NTFS_MST_H */ - diff --git a/include/ntfs/ntfstime.h b/include/ntfs/ntfstime.h deleted file mode 100644 index e0d1ae20..00000000 --- a/include/ntfs/ntfstime.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * ntfstime.h - NTFS time related functions. Part of the Linux-NTFS project. - * - * Copyright (c) 2005 Anton Altaparmakov - * Copyright (c) 2005 Yura Pakhuchiy - * - * 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 _NTFS_NTFSTIME_H -#define _NTFS_NTFSTIME_H - -#ifdef HAVE_TIME_H -#include -#endif - -#include "types.h" - -#define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000) - -/** - * ntfs2utc - Convert an NTFS time to Unix time - * @ntfs_time: An NTFS time in 100ns units since 1601 - * - * NTFS stores times as the number of 100ns intervals since January 1st 1601 at - * 00:00 UTC. This system will not suffer from Y2K problems until ~57000AD. - * - * Return: n A Unix time (number of seconds since 1970) - */ -static __inline__ time_t ntfs2utc(s64 ntfs_time) -{ - return (sle64_to_cpu(ntfs_time) - (NTFS_TIME_OFFSET)) / 10000000; -} - -/** - * utc2ntfs - Convert Linux time to NTFS time - * @utc_time: Linux time to convert to NTFS - * - * Convert the Linux time @utc_time to its corresponding NTFS time. - * - * Linux stores time in a long at present and measures it as the number of - * 1-second intervals since 1st January 1970, 00:00:00 UTC. - * - * NTFS uses Microsoft's standard time format which is stored in a s64 and is - * measured as the number of 100 nano-second intervals since 1st January 1601, - * 00:00:00 UTC. - * - * Return: n An NTFS time (100ns units since Jan 1601) - */ -static __inline__ s64 utc2ntfs(time_t utc_time) -{ - /* Convert to 100ns intervals and then add the NTFS time offset. */ - return cpu_to_sle64((s64)utc_time * 10000000 + NTFS_TIME_OFFSET); -} - -#endif /* _NTFS_NTFSTIME_H */ diff --git a/include/ntfs/runlist.h b/include/ntfs/runlist.h deleted file mode 100644 index 726a28dc..00000000 --- a/include/ntfs/runlist.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * runlist.h - Exports for runlist handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2002 Anton Altaparmakov - * Copyright (c) 2002 Richard Russon - * - * 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 _NTFS_RUNLIST_H -#define _NTFS_RUNLIST_H - -#include "types.h" - -/* Forward declarations */ -typedef struct _runlist_element runlist_element; -typedef runlist_element runlist; - -#include "attrib.h" -#include "volume.h" - -/** - * struct _runlist_element - in memory vcn to lcn mapping array element. - * @vcn: starting vcn of the current array element - * @lcn: starting lcn of the current array element - * @length: length in clusters of the current array element - * - * The last vcn (in fact the last vcn + 1) is reached when length == 0. - * - * When lcn == -1 this means that the count vcns starting at vcn are not - * physically allocated (i.e. this is a hole / data is sparse). - */ -struct _runlist_element {/* In memory vcn to lcn mapping structure element. */ - VCN vcn; /* vcn = Starting virtual cluster number. */ - LCN lcn; /* lcn = Starting logical cluster number. */ - s64 length; /* Run length in clusters. */ -}; - -extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn); - -extern s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl, - const s64 pos, s64 count, void *b); -extern s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl, - const s64 pos, s64 count, void *b); - -extern runlist_element *ntfs_runlists_merge(runlist_element *drl, - runlist_element *srl); - -extern runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, - const ATTR_RECORD *attr, runlist_element *old_rl); - -extern int ntfs_get_nr_significant_bytes(const s64 n); - -extern int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, - const runlist_element *rl, const VCN start_vcn); - -extern int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max, - const s64 n); - -extern int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst, - const int dst_len, const runlist_element *rl, - const VCN start_vcn, VCN *const stop_vcn); - -extern int ntfs_rl_truncate(runlist **arl, const VCN start_vcn); - -extern int ntfs_rl_sparse(runlist *rl); -extern s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl); - -#ifdef NTFS_TEST -int test_rl_main(int argc, char *argv[]); -#endif - -#endif /* defined _NTFS_RUNLIST_H */ - diff --git a/include/ntfs/security.h b/include/ntfs/security.h deleted file mode 100644 index a61aabd7..00000000 --- a/include/ntfs/security.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * security.h - Exports for handling security/ACLs in NTFS. Part of the - * Linux-NTFS project. - * - * Copyright (c) 2004 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 _NTFS_SECURITY_H -#define _NTFS_SECURITY_H - -#include "types.h" -#include "layout.h" - -extern const GUID *const zero_guid; - -extern BOOL ntfs_guid_is_zero(const GUID *guid); -extern char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str); - -/** - * ntfs_sid_is_valid - determine if a SID is valid - * @sid: SID for which to determine if it is valid - * - * Determine if the SID pointed to by @sid is valid. - * - * Return TRUE if it is valid and FALSE otherwise. - */ -static __inline__ BOOL ntfs_sid_is_valid(const SID *sid) -{ - if (!sid || sid->revision != SID_REVISION || - sid->sub_authority_count > SID_MAX_SUB_AUTHORITIES) - return FALSE; - return TRUE; -} - -extern int ntfs_sid_to_mbs_size(const SID *sid); -extern char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, - size_t sid_str_size); -extern void ntfs_generate_guid(GUID *guid); - -#endif /* defined _NTFS_SECURITY_H */ diff --git a/include/ntfs/support.h b/include/ntfs/support.h deleted file mode 100644 index 76114d6a..00000000 --- a/include/ntfs/support.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * support.h - Useful definitions and macros. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2004 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 _NTFS_SUPPORT_H -#define _NTFS_SUPPORT_H - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDDEF_H -#include -#endif - -/* - * Our mailing list. Use this define to prevent typos in email address. - */ -#define NTFS_DEV_LIST "linux-ntfs-dev@lists.sf.net" - -/* - * Generic macro to convert pointers to values for comparison purposes. - */ -#ifndef p2n -#define p2n(p) ((ptrdiff_t)((ptrdiff_t*)(p))) -#endif - -/* - * The classic min and max macros. - */ -#ifndef min -#define min(a,b) ((a) <= (b) ? (a) : (b)) -#endif - -#ifndef max -#define max(a,b) ((a) >= (b) ? (a) : (b)) -#endif - -/* - * Useful macro for determining the offset of a struct member. - */ -#ifndef offsetof -#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) -#endif - -/* - * Simple bit operation macros. NOTE: These are NOT atomic. - */ -#define test_bit(bit, var) ((var) & (1 << (bit))) -#define set_bit(bit, var) (var) |= 1 << (bit) -#define clear_bit(bit, var) (var) &= ~(1 << (bit)) - -#define test_and_set_bit(bit, var) \ -({ \ - const BOOL old_state = test_bit(bit, var); \ - set_bit(bit, var); \ - old_state; \ -}) - -#define test_and_clear_bit(bit, var) \ -({ \ - const BOOL old_state = test_bit(bit, var); \ - clear_bit(bit, var); \ - old_state; \ -}) - -#endif /* defined _NTFS_SUPPORT_H */ - diff --git a/include/ntfs/types.h b/include/ntfs/types.h deleted file mode 100644 index 4987d785..00000000 --- a/include/ntfs/types.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * types.h - Misc type definitions not related to on-disk structure. Part of - * the Linux-NTFS project. - * - * Copyright (c) 2000-2004 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 _NTFS_TYPES_H -#define _NTFS_TYPES_H - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#if HAVE_STDINT_H || !HAVE_CONFIG_H -#include -#endif -#ifdef HAVE_SYS_TYPES_H -#include -#endif - -typedef uint8_t u8; /* Unsigned types of an exact size */ -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; - -typedef int8_t s8; /* Signed types of an exact size */ -typedef int16_t s16; -typedef int32_t s32; -typedef int64_t s64; - -typedef u16 le16; -typedef u32 le32; -typedef u64 le64; - -/* - * Declare sle{16,32,64} to be unsigned because we do not want sign extension - * on BE architectures. - */ -typedef u16 sle16; -typedef u32 sle32; -typedef u64 sle64; - -typedef u16 ntfschar; /* 2-byte Unicode character type. */ -#define UCHAR_T_SIZE_BITS 1 - -/* - * Clusters are signed 64-bit values on NTFS volumes. We define two types, LCN - * and VCN, to allow for type checking and better code readability. - */ -typedef s64 VCN; -typedef sle64 leVCN; -typedef s64 LCN; -typedef sle64 leLCN; - -/* - * The NTFS journal $LogFile uses log sequence numbers which are signed 64-bit - * values. We define our own type LSN, to allow for type checking and better - * code readability. - */ -typedef s64 LSN; -typedef sle64 leLSN; - -/* - * Cygwin has a collision between our BOOL and 's - * As long as this file will be included after were fine. - */ -#ifndef _WINDEF_H -/** - * enum BOOL - These are just to make the code more readable... - */ -typedef enum { -#ifndef FALSE - FALSE = 0, -#endif -#ifndef NO - NO = 0, -#endif -#ifndef ZERO - ZERO = 0, -#endif -#ifndef TRUE - TRUE = 1, -#endif -#ifndef YES - YES = 1, -#endif -#ifndef ONE - ONE = 1, -#endif -} BOOL; -#endif /* defined _WINDEF_H */ - -/** - * enum IGNORE_CASE_BOOL - - */ -typedef enum { - CASE_SENSITIVE = 0, - IGNORE_CASE = 1, -} IGNORE_CASE_BOOL; - -#endif /* defined _NTFS_TYPES_H */ - diff --git a/include/ntfs/unistr.h b/include/ntfs/unistr.h deleted file mode 100644 index 81450f89..00000000 --- a/include/ntfs/unistr.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * unistr.h - Exports for Unicode string handling. Part of the Linux-NTFS - * project. - * - * Copyright (c) 2000-2004 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 _NTFS_UNISTR_H -#define _NTFS_UNISTR_H - -#include "types.h" -#include "layout.h" - -extern BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len, - const ntfschar *s2, size_t s2_len, const IGNORE_CASE_BOOL ic, - const ntfschar *upcase, const u32 upcase_size); - -extern int ntfs_names_collate(const ntfschar *name1, const u32 name1_len, - const ntfschar *name2, const u32 name2_len, - const int err_val, const IGNORE_CASE_BOOL ic, - const ntfschar *upcase, const u32 upcase_len); - -extern int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n); - -extern int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, - const ntfschar *upcase, const u32 upcase_size); - -extern u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen); - -extern ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen); - -extern void ntfs_name_upcase(ntfschar *name, u32 name_len, - const ntfschar *upcase, const u32 upcase_len); - -extern void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr, - const ntfschar *upcase, const u32 upcase_len); - -extern int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1, - const FILE_NAME_ATTR *file_name_attr2, - const int err_val, const IGNORE_CASE_BOOL ic, - const ntfschar *upcase, const u32 upcase_len); - -extern int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, - int outs_len); -extern int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len); - -extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len); - -extern ntfschar *ntfs_str2ucs(const char *s, int *len); - -extern void ntfs_ucsfree(ntfschar *ucs); - -#endif /* defined _NTFS_UNISTR_H */ - diff --git a/include/ntfs/version.h b/include/ntfs/version.h deleted file mode 100644 index ec6dbdca..00000000 --- a/include/ntfs/version.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * version.h - Info about the NTFS library. Part of the Linux-NTFS project. - * - * Copyright (c) 2005 Anton Altaparmakov - * Copyright (c) 2005 Richard Russon - * - * 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 _NTFS_VERSION_H_ -#define _NTFS_VERSION_H_ - -extern const char *ntfs_libntfs_version(void); - -#endif /* _NTFS_VERSION_H_ */ - diff --git a/include/ntfs/volume.h b/include/ntfs/volume.h deleted file mode 100644 index c884f494..00000000 --- a/include/ntfs/volume.h +++ /dev/null @@ -1,243 +0,0 @@ -/* - * volume.h - Exports for NTFS volume handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2004 Anton Altaparmakov - * Copyright (c) 2005-2006 Yura Pakhuchiy - * Copyright (c) 2004-2005 Richard Russon - * - * 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 _NTFS_VOLUME_H -#define _NTFS_VOLUME_H - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDIO_H -#include -#endif -#ifdef HAVE_SYS_PARAM_H -#include -#endif -#ifdef HAVE_SYS_MOUNT_H -#include -#endif -#ifdef HAVE_MNTENT_H -#include -#endif - -/* - * Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY and MS_NOATIME, - * so we define them ourselves. - */ -#ifndef MS_RDONLY -#define MS_RDONLY 1 -#endif -/* - * Solaris defines MS_RDONLY but not MS_NOATIME thus we need to carefully - * define MS_NOATIME. - */ -#ifndef MS_NOATIME -#if (MS_RDONLY != 1) -# define MS_NOATIME 1 -#else -# define MS_NOATIME 2 -#endif -#endif - -/* Forward declaration */ -typedef struct _ntfs_volume ntfs_volume; - -#include "types.h" -#include "support.h" -#include "device.h" -#include "inode.h" -#include "attrib.h" - -/** - * enum ntfs_mount_flags - - * - * Flags returned by the ntfs_check_if_mounted() function. - */ -typedef enum { - NTFS_MF_MOUNTED = 1, /* Device is mounted. */ - NTFS_MF_ISROOT = 2, /* Device is mounted as system root. */ - NTFS_MF_READONLY = 4, /* Device is mounted read-only. */ -} ntfs_mount_flags; - -extern int ntfs_check_if_mounted(const char *file, unsigned long *mnt_flags); - -/** - * enum ntfs_volume_state_bits - - * - * Defined bits for the state field in the ntfs_volume structure. - */ -typedef enum { - NV_ReadOnly, /* 1: Volume is read-only. */ - NV_CaseSensitive, /* 1: Volume is mounted case-sensitive. */ - NV_LogFileEmpty, /* 1: $logFile journal is empty. */ - NV_NoATime, /* 1: Do not update access time. */ -} ntfs_volume_state_bits; - -#define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state) -#define set_nvol_flag(nv, flag) set_bit(NV_##flag, (nv)->state) -#define clear_nvol_flag(nv, flag) clear_bit(NV_##flag, (nv)->state) - -#define NVolReadOnly(nv) test_nvol_flag(nv, ReadOnly) -#define NVolSetReadOnly(nv) set_nvol_flag(nv, ReadOnly) -#define NVolClearReadOnly(nv) clear_nvol_flag(nv, ReadOnly) - -#define NVolCaseSensitive(nv) test_nvol_flag(nv, CaseSensitive) -#define NVolSetCaseSensitive(nv) set_nvol_flag(nv, CaseSensitive) -#define NVolClearCaseSensitive(nv) clear_nvol_flag(nv, CaseSensitive) - -#define NVolLogFileEmpty(nv) test_nvol_flag(nv, LogFileEmpty) -#define NVolSetLogFileEmpty(nv) set_nvol_flag(nv, LogFileEmpty) -#define NVolClearLogFileEmpty(nv) clear_nvol_flag(nv, LogFileEmpty) - -#define NVolNoATime(nv) test_nvol_flag(nv, NoATime) -#define NVolSetNoATime(nv) set_nvol_flag(nv, NoATime) -#define NVolClearNoATime(nv) clear_nvol_flag(nv, NoATime) - -/* - * NTFS version 1.1 and 1.2 are used by Windows NT4. - * NTFS version 2.x is used by Windows 2000 Beta - * NTFS version 3.0 is used by Windows 2000. - * NTFS version 3.1 is used by Windows XP, 2003 and Vista. - */ - -#define NTFS_V1_1(major, minor) ((major) == 1 && (minor) == 1) -#define NTFS_V1_2(major, minor) ((major) == 1 && (minor) == 2) -#define NTFS_V2_X(major, minor) ((major) == 2) -#define NTFS_V3_0(major, minor) ((major) == 3 && (minor) == 0) -#define NTFS_V3_1(major, minor) ((major) == 3 && (minor) == 1) - -#define NTFS_BUF_SIZE 8192 - -/** - * struct _ntfs_volume - structure describing an open volume in memory. - */ -struct _ntfs_volume { - union { - struct ntfs_device *dev; /* NTFS device associated with - the volume. */ - void *sb; /* For kernel porting compatibility. */ - }; - char *vol_name; /* Name of the volume. */ - unsigned long state; /* NTFS specific flags describing this volume. - See ntfs_volume_state_bits above. */ - - ntfs_inode *vol_ni; /* ntfs_inode structure for FILE_Volume. */ - u8 major_ver; /* Ntfs major version of volume. */ - u8 minor_ver; /* Ntfs minor version of volume. */ - u16 flags; /* Bit array of VOLUME_* flags. */ - - u16 sector_size; /* Byte size of a sector. */ - u8 sector_size_bits; /* Log(2) of the byte size of a sector. */ - u32 cluster_size; /* Byte size of a cluster. */ - u32 mft_record_size; /* Byte size of a mft record. */ - u32 indx_record_size; /* Byte size of a INDX record. */ - u8 cluster_size_bits; /* Log(2) of the byte size of a cluster. */ - u8 mft_record_size_bits;/* Log(2) of the byte size of a mft record. */ - u8 indx_record_size_bits;/* Log(2) of the byte size of a INDX record. */ - - /* Variables used by the cluster and mft allocators. */ - u8 mft_zone_multiplier; /* Initial mft zone multiplier. */ - s64 mft_data_pos; /* Mft record number at which to allocate the - next mft record. */ - LCN mft_zone_start; /* First cluster of the mft zone. */ - LCN mft_zone_end; /* First cluster beyond the mft zone. */ - LCN mft_zone_pos; /* Current position in the mft zone. */ - LCN data1_zone_pos; /* Current position in the first data zone. */ - LCN data2_zone_pos; /* Current position in the second data zone. */ - - s64 nr_clusters; /* Volume size in clusters, hence also the - number of bits in lcn_bitmap. */ - ntfs_inode *lcnbmp_ni; /* ntfs_inode structure for FILE_Bitmap. */ - ntfs_attr *lcnbmp_na; /* ntfs_attr structure for the data attribute - of FILE_Bitmap. Each bit represents a - cluster on the volume, bit 0 representing - lcn 0 and so on. A set bit means that the - cluster and vice versa. */ - - LCN mft_lcn; /* Logical cluster number of the data attribute - for FILE_MFT. */ - ntfs_inode *mft_ni; /* ntfs_inode structure for FILE_MFT. */ - ntfs_attr *mft_na; /* ntfs_attr structure for the data attribute - of FILE_MFT. */ - ntfs_attr *mftbmp_na; /* ntfs_attr structure for the bitmap attribute - of FILE_MFT. Each bit represents an mft - record in the $DATA attribute, bit 0 - representing mft record 0 and so on. A set - bit means that the mft record is in use and - vice versa. */ - - int mftmirr_size; /* Size of the FILE_MFTMirr in mft records. */ - LCN mftmirr_lcn; /* Logical cluster number of the data attribute - for FILE_MFTMirr. */ - ntfs_inode *mftmirr_ni; /* ntfs_inode structure for FILE_MFTMirr. */ - ntfs_attr *mftmirr_na; /* ntfs_attr structure for the data attribute - of FILE_MFTMirr. */ - - ntfschar *upcase; /* Upper case equivalents of all 65536 2-byte - Unicode characters. Obtained from - FILE_UpCase. */ - u32 upcase_len; /* Length in Unicode characters of the upcase - table. */ - - ATTR_DEF *attrdef; /* Attribute definitions. Obtained from - FILE_AttrDef. */ - s32 attrdef_len; /* Size of the attribute definition table in - bytes. */ - - /* Temp: for directory handling */ - void *private_data; /* ntfs_dir for . */ - void *private_bmp1; /* ntfs_bmp for $MFT/$BITMAP */ - void *private_bmp2; /* ntfs_bmp for $Bitmap */ -}; - -extern ntfs_volume *ntfs_volume_alloc(void); - -extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, - unsigned long flags); - -extern ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, - unsigned long flags); -extern int ntfs_device_umount(ntfs_volume *vol, const BOOL force); - -extern ntfs_volume *ntfs_mount(const char *name, unsigned long flags); -extern int ntfs_umount(ntfs_volume *vol, const BOOL force); - -extern int ntfs_version_is_supported(ntfs_volume *vol); -extern int ntfs_logfile_reset(ntfs_volume *vol); - -extern int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags); - -#ifdef NTFS_RICH - -int ntfs_volume_commit(ntfs_volume *vol); -int ntfs_volume_rollback(ntfs_volume *vol); -int ntfs_volume_umount2(ntfs_volume *vol, const BOOL force); -ntfs_volume * ntfs_volume_mount2(const char *device, unsigned long flags, BOOL force); -int utils_valid_device(const char *name, int force); -ntfs_volume * utils_mount_volume(const char *device, unsigned long flags, BOOL force); - -#endif /* NTFS_RICH */ - -#endif /* defined _NTFS_VOLUME_H */ - diff --git a/libntfs/Makefile.am b/libntfs/Makefile.am index ac784c67..450d043a 100644 --- a/libntfs/Makefile.am +++ b/libntfs/Makefile.am @@ -31,39 +31,6 @@ LTVERSION_LIBNTFS_GNOMEVFS = 1:0:0 linux_ntfsincludedir = -I$(top_srcdir)/include/ntfs -lib_LTLIBRARIES = libntfs.la -libntfs_la_LDFLAGS = -version-info $(LTVERSION_LIBNTFS) -no-undefined -libntfs_la_CFLAGS = $(LIBNTFS_CFLAGS) \ - -DLTVERSION_LIBNTFS=\"$(LTVERSION_LIBNTFS)\" -libntfs_la_SOURCES = \ - attrib.c \ - attrlist.c \ - bitmap.c \ - bootsect.c \ - collate.c \ - compat.c \ - compress.c \ - debug.c \ - device.c \ - device_io.c \ - dir.c \ - index.c \ - inode.c \ - lcnalloc.c \ - logfile.c \ - logging.c \ - mft.c \ - mst.c \ - runlist.c \ - security.c \ - unistr.c \ - version.c \ - volume.c - -if ENABLE_RICH -libntfs_la_SOURCES += rich.c tree.c -endif - if ENABLE_GNOME_VFS gnomevfsmoduleslibdir = $(libdir)/gnome-vfs-2.0/modules @@ -75,7 +42,7 @@ gnomevfsmodulesconf_DATA = libntfs.conf endif libntfs_gnomevfs_la_LDFLAGS = -version-info $(LTVERSION_LIBNTFS_GNOMEVFS) -libntfs_gnomevfs_la_LIBADD = libntfs.la +libntfs_gnomevfs_la_LIBADD = libntfs-3g.la libntfs_gnomevfs_la_LIBS = $(LIBNTFS_GNOMEVFS_LIBS) libntfs_gnomevfs_la_CFLAGS = $(LIBNTFS_GNOMEVFS_CFLAGS) libntfs_gnomevfs_la_SOURCES = \ @@ -86,7 +53,7 @@ man_MANS = libntfs-gnomevfs.8 AM_CPPFLAGS = $(linux_ntfsincludedir) $(all_includes) -EXTRA_DIST = unix_io.c win32_io.c libntfs.conf.in +EXTRA_DIST = libntfs.conf.in MAINTAINERCLEANFILES = Makefile.in diff --git a/libntfs/attrib.c b/libntfs/attrib.c deleted file mode 100644 index 904aa20a..00000000 --- a/libntfs/attrib.c +++ /dev/null @@ -1,5045 +0,0 @@ -/** - * attrib.c - Attribute handling code. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2005 Anton Altaparmakov - * Copyright (c) 2002-2005 Richard Russon - * Copyright (c) 2004-2006 Yura Pakhuchiy - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDIO_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif - -#include "compat.h" -#include "attrib.h" -#include "attrlist.h" -#include "device.h" -#include "mft.h" -#include "debug.h" -#include "mst.h" -#include "volume.h" -#include "types.h" -#include "layout.h" -#include "inode.h" -#include "runlist.h" -#include "lcnalloc.h" -#include "dir.h" -#include "compress.h" -#include "bitmap.h" -#include "logging.h" - -ntfschar AT_UNNAMED[] = { const_cpu_to_le16('\0') }; - -/** - * ntfs_get_attribute_value_length - Find the length of an attribute - * @a: - * - * Description... - * - * Returns: - */ -s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a) -{ - if (!a) { - errno = EINVAL; - return 0; - } - errno = 0; - if (a->non_resident) - return sle64_to_cpu(a->data_size); - else - return (s64)le32_to_cpu(a->value_length); - errno = EINVAL; - return 0; -} - -/** - * ntfs_get_attribute_value - Get a copy of an attribute - * @vol: - * @a: - * @b: - * - * Description... - * - * Returns: - */ -s64 ntfs_get_attribute_value(const ntfs_volume *vol, - const ATTR_RECORD *a, u8 *b) -{ - runlist *rl; - s64 total, r; - int i; - - /* Sanity checks. */ - if (!vol || !a || !b) { - errno = EINVAL; - return 0; - } - /* Complex attribute? */ - /* - * Ignore the flags in case they are not zero for an attribute list - * attribute. Windows does not complain about invalid flags and chkdsk - * does not detect or fix them so we need to cope with it, too. - */ - if (a->type != AT_ATTRIBUTE_LIST && a->flags) { - ntfs_log_error("Non-zero (%04x) attribute flags. Cannot handle " - "this yet.\n", le16_to_cpu(a->flags)); - errno = EOPNOTSUPP; - return 0; - } - if (!a->non_resident) { - /* Attribute is resident. */ - - /* Sanity check. */ - if (le32_to_cpu(a->value_length) + le16_to_cpu(a->value_offset) - > le32_to_cpu(a->length)) { - return 0; - } - - memcpy(b, (const char*)a + le16_to_cpu(a->value_offset), - le32_to_cpu(a->value_length)); - errno = 0; - return (s64)le32_to_cpu(a->value_length); - } - - /* Attribute is not resident. */ - - /* If no data, return 0. */ - if (!(a->data_size)) { - errno = 0; - return 0; - } - /* - * FIXME: What about attribute lists?!? (AIA) - */ - /* Decompress the mapping pairs array into a runlist. */ - rl = ntfs_mapping_pairs_decompress(vol, a, NULL); - if (!rl) { - errno = EINVAL; - return 0; - } - /* - * FIXED: We were overflowing here in a nasty fashion when we - * reach the last cluster in the runlist as the buffer will - * only be big enough to hold data_size bytes while we are - * reading in allocated_size bytes which is usually larger - * than data_size, since the actual data is unlikely to have a - * size equal to a multiple of the cluster size! - * FIXED2: We were also overflowing here in the same fashion - * when the data_size was more than one run smaller than the - * allocated size which happens with Windows XP sometimes. - */ - /* Now load all clusters in the runlist into b. */ - for (i = 0, total = 0; rl[i].length; i++) { - if (total + (rl[i].length << vol->cluster_size_bits) >= - sle64_to_cpu(a->data_size)) { - unsigned char *intbuf = NULL; - /* - * We have reached the last run so we were going to - * overflow when executing the ntfs_pread() which is - * BAAAAAAAD! - * Temporary fix: - * Allocate a new buffer with size: - * rl[i].length << vol->cluster_size_bits, do the - * read into our buffer, then memcpy the correct - * amount of data into the caller supplied buffer, - * free our buffer, and continue. - * We have reached the end of data size so we were - * going to overflow in the same fashion. - * Temporary fix: same as above. - */ - intbuf = malloc(rl[i].length << vol->cluster_size_bits); - if (!intbuf) { - int eo = errno; - ntfs_log_perror("Couldn't allocate memory for " - "internal buffer."); - free(rl); - errno = eo; - return 0; - } - /* - * FIXME: If compressed file: Only read if lcn != -1. - * Otherwise, we are dealing with a sparse run and we - * just memset the user buffer to 0 for the length of - * the run, which should be 16 (= compression unit - * size). - * FIXME: Really only when file is compressed, or can - * we have sparse runs in uncompressed files as well? - * - Yes we can, in sparse files! But not necessarily - * size of 16, just run length. - */ - r = ntfs_pread(vol->dev, rl[i].lcn << - vol->cluster_size_bits, rl[i].length << - vol->cluster_size_bits, intbuf); - if (r != rl[i].length << vol->cluster_size_bits) { -#define ESTR "Error reading attribute value" - if (r == -1) { - int eo = errno; - ntfs_log_perror(ESTR); - errno = eo; - } else if (r < rl[i].length << - vol->cluster_size_bits) { - ntfs_log_debug(ESTR ": Ran out of input data.\n"); - errno = EIO; - } else { - ntfs_log_debug(ESTR ": unknown error\n"); - errno = EIO; - } -#undef ESTR - free(rl); - free(intbuf); - return 0; - } - memcpy(b + total, intbuf, sle64_to_cpu(a->data_size) - - total); - free(intbuf); - total = sle64_to_cpu(a->data_size); - break; - } - /* - * FIXME: If compressed file: Only read if lcn != -1. - * Otherwise, we are dealing with a sparse run and we just - * memset the user buffer to 0 for the length of the run, which - * should be 16 (= compression unit size). - * FIXME: Really only when file is compressed, or can - * we have sparse runs in uncompressed files as well? - * - Yes we can, in sparse files! But not necessarily size of - * 16, just run length. - */ - r = ntfs_pread(vol->dev, rl[i].lcn << vol->cluster_size_bits, - rl[i].length << vol->cluster_size_bits, - b + total); - if (r != rl[i].length << vol->cluster_size_bits) { -#define ESTR "Error reading attribute value" - if (r == -1) { - int eo = errno; - ntfs_log_perror(ESTR); - errno = eo; - } else if (r < rl[i].length << vol->cluster_size_bits) { - ntfs_log_debug(ESTR ": Ran out of input data.\n"); - errno = EIO; - } else { - ntfs_log_debug(ESTR ": unknown error\n"); - errno = EIO; - } -#undef ESTR - return 0; - } - total += r; - } - free(rl); - return total; -} - -/* Already cleaned up code below, but still look for FIXME:... */ - -/** - * __ntfs_attr_init - primary initialization of an ntfs attribute structure - * @na: ntfs attribute to initialize - * @ni: ntfs inode with which to initialize the ntfs attribute - * @type: attribute type - * @name: attribute name in little endian Unicode or NULL - * @name_len: length of attribute @name in Unicode characters (if @name given) - * - * Initialize the ntfs attribute @na with @ni, @type, @name, and @name_len. - */ -static __inline__ void __ntfs_attr_init(ntfs_attr *na, ntfs_inode *ni, - const ATTR_TYPES type, ntfschar *name, const u32 name_len) -{ - na->rl = NULL; - na->ni = ni; - na->type = type; - na->name = name; - if (name) - na->name_len = name_len; - else - na->name_len = 0; -} - -/** - * ntfs_attr_init - initialize an ntfs_attr with data sizes and status - * @na: - * @non_resident: - * @compressed: - * @encrypted: - * @sparse: - * @allocated_size: - * @data_size: - * @initialized_size: - * @compressed_size: - * @compression_unit: - * - * Final initialization for an ntfs attribute. - */ -void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident, - const BOOL compressed, const BOOL encrypted, const BOOL sparse, - const s64 allocated_size, const s64 data_size, - const s64 initialized_size, const s64 compressed_size, - const u8 compression_unit) -{ - if (!NAttrInitialized(na)) { - if (non_resident) - NAttrSetNonResident(na); - if (compressed) - NAttrSetCompressed(na); - if (encrypted) - NAttrSetEncrypted(na); - if (sparse) - NAttrSetSparse(na); - na->allocated_size = allocated_size; - na->data_size = data_size; - na->initialized_size = initialized_size; - if (compressed || sparse) { - ntfs_volume *vol = na->ni->vol; - - na->compressed_size = compressed_size; - na->compression_block_clusters = 1 << compression_unit; - na->compression_block_size = 1 << (compression_unit + - vol->cluster_size_bits); - na->compression_block_size_bits = ffs( - na->compression_block_size) - 1; - } - NAttrSetInitialized(na); - } -} - -/** - * ntfs_attr_open - open an ntfs attribute for access - * @ni: open ntfs inode in which the ntfs attribute resides - * @type: attribute type - * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL - * @name_len: length of attribute @name in Unicode characters (if @name given) - * - * Allocate a new ntfs attribute structure, initialize it with @ni, @type, - * @name, and @name_len, then return it. Return NULL on error with - * errno set to the error code. - * - * If @name is AT_UNNAMED look specifically for an unnamed attribute. If you - * do not care whether the attribute is named or not set @name to NULL. In - * both those cases @name_len is not used at all. - */ -ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, - ntfschar *name, u32 name_len) -{ - ntfs_attr_search_ctx *ctx; - ntfs_attr *na; - ATTR_RECORD *a; - int err; - BOOL cs; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", - (unsigned long long)ni->mft_no, type); - if (!ni || !ni->vol || !ni->mrec) { - errno = EINVAL; - return NULL; - } - na = calloc(sizeof(ntfs_attr), 1); - if (!na) - return NULL; - if (name && name != AT_UNNAMED && name != NTFS_INDEX_I30) { - name = ntfs_ucsndup(name, name_len); - if (!name) { - err = errno; - free(na); - errno = err; - return NULL; - } - } - - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) { - err = errno; - goto err_out; - } - if (ntfs_attr_lookup(type, name, name_len, 0, 0, NULL, 0, ctx)) { - err = errno; - goto put_err_out; - } - - a = ctx->attr; - /* - * Wipe the flags in case they are not zero for an attribute list - * attribute. Windows does not complain about invalid flags and chkdsk - * does not detect or fix them so we need to cope with it, too. - */ - if (type == AT_ATTRIBUTE_LIST) - a->flags = 0; - cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); - if (!name) { - if (a->name_length) { - name = ntfs_ucsndup((ntfschar*)((u8*)a + le16_to_cpu( - a->name_offset)), a->name_length); - if (!name) { - err = errno; - goto put_err_out; - } - name_len = a->name_length; - } else { - name = AT_UNNAMED; - name_len = 0; - } - } - __ntfs_attr_init(na, ni, type, name, name_len); - if (a->non_resident) { - ntfs_attr_init(na, TRUE, a->flags & ATTR_IS_COMPRESSED, - a->flags & ATTR_IS_ENCRYPTED, - a->flags & ATTR_IS_SPARSE, - sle64_to_cpu(a->allocated_size), - sle64_to_cpu(a->data_size), - sle64_to_cpu(a->initialized_size), - cs ? sle64_to_cpu(a->compressed_size) : 0, - cs ? a->compression_unit : 0); - } else { - s64 l = le32_to_cpu(a->value_length); - ntfs_attr_init(na, FALSE, a->flags & ATTR_IS_COMPRESSED, - a->flags & ATTR_IS_ENCRYPTED, - a->flags & ATTR_IS_SPARSE, (l + 7) & ~7, l, l, - cs ? (l + 7) & ~7 : 0, 0); - } - ntfs_attr_put_search_ctx(ctx); - return na; -put_err_out: - ntfs_attr_put_search_ctx(ctx); -err_out: - free(na); - errno = err; - return NULL; -} - -/** - * ntfs_attr_close - free an ntfs attribute structure - * @na: ntfs attribute structure to free - * - * Release all memory associated with the ntfs attribute @na and then release - * @na itself. - */ -void ntfs_attr_close(ntfs_attr *na) -{ - if (!na) - return; - if (NAttrNonResident(na) && na->rl) - free(na->rl); - /* Don't release if using an internal constant. */ - if (na->name != AT_UNNAMED && na->name != NTFS_INDEX_I30) - free(na->name); - free(na); -} - -/** - * ntfs_attr_map_runlist - map (a part of) a runlist of an ntfs attribute - * @na: ntfs attribute for which to map (part of) a runlist - * @vcn: map runlist part containing this vcn - * - * Map the part of a runlist containing the @vcn of an the ntfs attribute @na. - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn) -{ - LCN lcn; - ntfs_attr_search_ctx *ctx; - int err; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn 0x%llx.\n", - (unsigned long long)na->ni->mft_no, na->type, (long long)vcn); - - lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); - if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT) - return 0; - - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - return -1; - - /* Find the attribute in the mft record. */ - if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, - vcn, NULL, 0, ctx)) { - runlist_element *rl; - - /* Decode the runlist. */ - rl = ntfs_mapping_pairs_decompress(na->ni->vol, ctx->attr, - na->rl); - if (rl) { - na->rl = rl; - - ntfs_attr_put_search_ctx(ctx); - return 0; - } - } - err = errno; - ntfs_attr_put_search_ctx(ctx); - errno = err; - return -1; -} - -/** - * ntfs_attr_map_whole_runlist - map the whole runlist of an ntfs attribute - * @na: ntfs attribute for which to map the runlist - * - * Map the whole runlist of an the ntfs attribute @na. For an attribute made - * up of only one attribute extent this is the same as calling - * ntfs_attr_map_runlist(na, 0) but for an attribute with multiple extents this - * will map the runlist fragments from each of the extents thus giving access - * to the entirety of the disk allocation of an attribute. - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -int ntfs_attr_map_whole_runlist(ntfs_attr *na) -{ - VCN next_vcn, last_vcn, highest_vcn; - ntfs_attr_search_ctx *ctx; - ntfs_volume *vol = na->ni->vol; - ATTR_RECORD *a; - int err; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", - (unsigned long long)na->ni->mft_no, na->type); - - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - return -1; - - /* Map all attribute extents one by one. */ - next_vcn = last_vcn = highest_vcn = 0; - a = NULL; - while (1) { - runlist_element *rl; - - int not_mapped = 0; - if (ntfs_rl_vcn_to_lcn(na->rl, next_vcn) == LCN_RL_NOT_MAPPED) - not_mapped = 1; - - if (ntfs_attr_lookup(na->type, na->name, na->name_len, - CASE_SENSITIVE, next_vcn, NULL, 0, ctx)) - break; - - a = ctx->attr; - - if (not_mapped) { - /* Decode the runlist. */ - rl = ntfs_mapping_pairs_decompress(na->ni->vol, - a, na->rl); - if (!rl) - goto err_out; - na->rl = rl; - } - - /* Are we in the first extent? */ - if (!next_vcn) { - if (a->lowest_vcn) { - ntfs_log_trace("First extent of attribute has non " - "zero lowest_vcn. Inode is corrupt.\n"); - errno = EIO; - goto err_out; - } - /* Get the last vcn in the attribute. */ - last_vcn = sle64_to_cpu(a->allocated_size) >> - vol->cluster_size_bits; - } - - /* Get the lowest vcn for the next extent. */ - highest_vcn = sle64_to_cpu(a->highest_vcn); - next_vcn = highest_vcn + 1; - - /* Only one extent or error, which we catch below. */ - if (next_vcn <= 0) { - errno = ENOENT; - break; - } - - /* Avoid endless loops due to corruption. */ - if (next_vcn < sle64_to_cpu(a->lowest_vcn)) { - ntfs_log_trace("Inode has corrupt attribute list " - "attribute.\n"); - errno = EIO; - goto err_out; - } - } - if (!a) { - err = errno; - if (err == ENOENT) - ntfs_log_trace("Attribute not found. Inode is corrupt.\n"); - else - ntfs_log_trace("Inode is corrupt.\n"); - errno = err; - goto err_out; - } - if (highest_vcn && highest_vcn != last_vcn - 1) { - ntfs_log_trace("Failed to load the complete run list for the " - "attribute. Bug or corrupt inode.\n"); - ntfs_log_trace("highest_vcn = 0x%llx, last_vcn - 1 = 0x%llx\n", - (long long)highest_vcn, (long long)last_vcn - 1); - errno = EIO; - goto err_out; - } - err = errno; - ntfs_attr_put_search_ctx(ctx); - if (err == ENOENT) - return 0; -out_now: - errno = err; - return -1; -err_out: - err = errno; - ntfs_attr_put_search_ctx(ctx); - goto out_now; -} - -/** - * ntfs_attr_vcn_to_lcn - convert a vcn into a lcn given an ntfs attribute - * @na: ntfs attribute whose runlist to use for conversion - * @vcn: vcn to convert - * - * Convert the virtual cluster number @vcn of an attribute into a logical - * cluster number (lcn) of a device using the runlist @na->rl to map vcns to - * their corresponding lcns. - * - * If the @vcn is not mapped yet, attempt to map the attribute extent - * containing the @vcn and retry the vcn to lcn conversion. - * - * Since lcns must be >= 0, we use negative return values with special meaning: - * - * Return value Meaning / Description - * ========================================== - * -1 = LCN_HOLE Hole / not allocated on disk. - * -3 = LCN_ENOENT There is no such vcn in the attribute. - * -4 = LCN_EINVAL Input parameter error. - * -5 = LCN_EIO Corrupt fs, disk i/o error, or not enough memory. - */ -LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn) -{ - LCN lcn; - BOOL is_retry = FALSE; - - if (!na || !NAttrNonResident(na) || vcn < 0) - return (LCN)LCN_EINVAL; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long - long)na->ni->mft_no, na->type); -retry: - /* Convert vcn to lcn. If that fails map the runlist and retry once. */ - lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); - if (lcn >= 0) - return lcn; - if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) { - is_retry = TRUE; - goto retry; - } - /* - * If the attempt to map the runlist failed, or we are getting - * LCN_RL_NOT_MAPPED despite having mapped the attribute extent - * successfully, something is really badly wrong... - */ - if (!is_retry || lcn == (LCN)LCN_RL_NOT_MAPPED) - return (LCN)LCN_EIO; - /* lcn contains the appropriate error code. */ - return lcn; -} - -/** - * ntfs_attr_find_vcn - find a vcn in the runlist of an ntfs attribute - * @na: ntfs attribute whose runlist to search - * @vcn: vcn to find - * - * Find the virtual cluster number @vcn in the runlist of the ntfs attribute - * @na and return the the address of the runlist element containing the @vcn. - * - * Note you need to distinguish between the lcn of the returned runlist - * element being >= 0 and LCN_HOLE. In the later case you have to return zeroes - * on read and allocate clusters on write. You need to update the runlist, the - * attribute itself as well as write the modified mft record to disk. - * - * If there is an error return NULL with errno set to the error code. The - * following error codes are defined: - * EINVAL Input parameter error. - * ENOENT There is no such vcn in the runlist. - * ENOMEM Not enough memory. - * EIO I/O error or corrupt metadata. - */ -runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn) -{ - runlist_element *rl; - BOOL is_retry = FALSE; - - if (!na || !NAttrNonResident(na) || vcn < 0) { - errno = EINVAL; - return NULL; - } - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn %llx\n", - (unsigned long long)na->ni->mft_no, na->type, - (long long)vcn); -retry: - rl = na->rl; - if (!rl) - goto map_rl; - if (vcn < rl[0].vcn) - goto map_rl; - while (rl->length) { - if (vcn < rl[1].vcn) { - if (rl->lcn >= (LCN)LCN_HOLE) - return rl; - break; - } - rl++; - } - switch (rl->lcn) { - case (LCN)LCN_RL_NOT_MAPPED: - goto map_rl; - case (LCN)LCN_ENOENT: - errno = ENOENT; - break; - case (LCN)LCN_EINVAL: - errno = EINVAL; - break; - default: - errno = EIO; - break; - } - return NULL; -map_rl: - /* The @vcn is in an unmapped region, map the runlist and retry. */ - if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) { - is_retry = TRUE; - goto retry; - } - /* - * If we already retried or the mapping attempt failed something has - * gone badly wrong. EINVAL and ENOENT coming from a failed mapping - * attempt are equivalent to errors for us as they should not happen - * in our code paths. - */ - if (is_retry || errno == EINVAL || errno == ENOENT) - errno = EIO; - return NULL; -} - -/** - * ntfs_attr_pread - read from an attribute specified by an ntfs_attr structure - * @na: ntfs attribute to read from - * @pos: byte position in the attribute to begin reading from - * @count: number of bytes to read - * @b: output data buffer - * - * This function will read @count bytes starting at offset @pos from the ntfs - * attribute @na into the data buffer @b. - * - * On success, return the number of successfully read bytes. If this number is - * lower than @count this means that the read reached end of file or that an - * error was encountered during the read so that the read is partial. 0 means - * end of file or nothing was read (also return 0 when @count is 0). - * - * On error and nothing has been read, return -1 with errno set appropriately - * to the return code of ntfs_pread(), or to EINVAL in case of invalid - * arguments. - */ -s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) -{ - s64 br, to_read, ofs, total, total2; - ntfs_volume *vol; - runlist_element *rl; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count " - "0x%llx.\n", (unsigned long long)na->ni->mft_no, - na->type, (long long)pos, (long long)count); - if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { - errno = EINVAL; - return -1; - } - /* - * If this is a compressed attribute it needs special treatment, but - * only if it is non-resident. - */ - if (NAttrCompressed(na) && NAttrNonResident(na)) - return ntfs_compressed_attr_pread(na, pos, count, b); - /* - * Encrypted non-resident attributes are not supported. We return - * access denied, which is what Windows NT4 does, too. - */ - if (NAttrEncrypted(na) && NAttrNonResident(na)) { - errno = EACCES; - return -1; - } - vol = na->ni->vol; - /* Update access time if needed. */ - if (na->type == AT_DATA || na->type == AT_INDEX_ROOT || - na->type == AT_INDEX_ALLOCATION) - ntfs_inode_update_atime(na->ni); - if (!count) - return 0; - /* Truncate reads beyond end of attribute. */ - if (pos + count > na->data_size) { - if (pos >= na->data_size) - return 0; - count = na->data_size - pos; - } - /* If it is a resident attribute, get the value from the mft record. */ - if (!NAttrNonResident(na)) { - ntfs_attr_search_ctx *ctx; - char *val; - - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - return -1; - if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, - 0, NULL, 0, ctx)) { - int eo; -res_err_out: - eo = errno; - ntfs_attr_put_search_ctx(ctx); - errno = eo; - return -1; - } - val = (char*)ctx->attr + le16_to_cpu(ctx->attr->value_offset); - if (val < (char*)ctx->attr || val + - le32_to_cpu(ctx->attr->value_length) > - (char*)ctx->mrec + vol->mft_record_size) { - errno = EIO; - goto res_err_out; - } - memcpy(b, val + pos, count); - ntfs_attr_put_search_ctx(ctx); - return count; - } - total = total2 = 0; - /* Zero out reads beyond initialized size. */ - if (pos + count > na->initialized_size) { - if (pos >= na->initialized_size) { - memset(b, 0, count); - return count; - } - total2 = pos + count - na->initialized_size; - count -= total2; - memset((u8*)b + count, 0, total2); - } - /* Find the runlist element containing the vcn. */ - rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); - if (!rl) { - /* - * If the vcn is not present it is an out of bounds read. - * However, we already truncated the read to the data_size, - * so getting this here is an error. - */ - if (errno == ENOENT) - errno = EIO; - return -1; - } - /* - * Gather the requested data into the linear destination buffer. Note, - * a partial final vcn is taken care of by the @count capping of read - * length. - */ - ofs = pos - (rl->vcn << vol->cluster_size_bits); - for (; count; rl++, ofs = 0) { - if (rl->lcn == LCN_RL_NOT_MAPPED) { - rl = ntfs_attr_find_vcn(na, rl->vcn); - if (!rl) { - if (errno == ENOENT) - errno = EIO; - goto rl_err_out; - } - /* Needed for case when runs merged. */ - ofs = pos + total - (rl->vcn << vol->cluster_size_bits); - } - if (!rl->length) - goto rl_err_out; - if (rl->lcn < (LCN)0) { - if (rl->lcn != (LCN)LCN_HOLE) - goto rl_err_out; - /* It is a hole, just zero the matching @b range. */ - to_read = min(count, (rl->length << - vol->cluster_size_bits) - ofs); - memset(b, 0, to_read); - /* Update progress counters. */ - total += to_read; - count -= to_read; - b = (u8*)b + to_read; - continue; - } - /* It is a real lcn, read it into @dst. */ - to_read = min(count, (rl->length << vol->cluster_size_bits) - - ofs); -retry: - ntfs_log_trace("Reading 0x%llx bytes from vcn 0x%llx, lcn 0x%llx, " - "ofs 0x%llx.\n", to_read, rl->vcn, rl->lcn, ofs); - br = ntfs_pread(vol->dev, (rl->lcn << vol->cluster_size_bits) + - ofs, to_read, b); - /* If everything ok, update progress counters and continue. */ - if (br > 0) { - total += br; - count -= br; - b = (u8*)b + br; - continue; - } - /* If the syscall was interrupted, try again. */ - if (br == (s64)-1 && errno == EINTR) - goto retry; - if (total) - return total; - if (!br) - errno = EIO; - return -1; - } - /* Finally, return the number of bytes read. */ - return total + total2; -rl_err_out: - if (total) - return total; - errno = EIO; - return -1; -} - -/** - * ntfs_attr_pwrite - positioned write to an ntfs attribute - * @na: ntfs attribute to write to - * @pos: position in the attribute to write to - * @count: number of bytes to write - * @b: data buffer to write to disk - * - * This function will write @count bytes from data buffer @b to ntfs attribute - * @na at position @pos. - * - * On success, return the number of successfully written bytes. If this number - * is lower than @count this means that an error was encountered during the - * write so that the write is partial. 0 means nothing was written (also return - * 0 when @count is 0). - * - * On error and nothing has been written, return -1 with errno set - * appropriately to the return code of ntfs_pwrite(), or to EINVAL in case of - * invalid arguments. - */ -s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) -{ - s64 written, to_write, ofs, total, old_initialized_size, old_data_size; - VCN update_from = -1; - ntfs_volume *vol; - ntfs_attr_search_ctx *ctx = NULL; - runlist_element *rl; - int eo; - struct { - unsigned int undo_initialized_size : 1; - unsigned int undo_data_size : 1; - unsigned int update_mapping_pairs : 1; - } need_to = { 0, 0, 0 }; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count " - "0x%llx.\n", na->ni->mft_no, na->type, (long long)pos, - (long long)count); - if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { - errno = EINVAL; - return -1; - } - vol = na->ni->vol; - /* - * Encrypted non-resident attributes are not supported. We return - * access denied, which is what Windows NT4 does, too. - */ - if (NAttrEncrypted(na) && NAttrNonResident(na)) { - errno = EACCES; - return -1; - } - /* If this is a compressed attribute it needs special treatment. */ - if (NAttrCompressed(na)) { - // TODO: Implement writing compressed attributes! (AIA) - // return ntfs_attr_pwrite_compressed(ntfs_attr *na, - // const s64 pos, s64 count, void *b); - errno = EOPNOTSUPP; - return -1; - } - /* Update access and change times if needed. */ - if (na->type == AT_DATA || na->type == AT_INDEX_ROOT || - na->type == AT_INDEX_ALLOCATION) - ntfs_inode_update_time(na->ni); - if (!count) - return 0; - /* If the write reaches beyond the end, extend the attribute. */ - old_data_size = na->data_size; - if (pos + count > na->data_size) { - if (ntfs_attr_truncate(na, pos + count)) { - eo = errno; - ntfs_log_trace("Attribute extend failed.\n"); - errno = eo; - return -1; - } - need_to.undo_data_size = 1; - } - old_initialized_size = na->initialized_size; - /* If it is a resident attribute, write the data to the mft record. */ - if (!NAttrNonResident(na)) { - char *val; - - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - goto err_out; - if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, - 0, NULL, 0, ctx)) - goto err_out; - val = (char*)ctx->attr + le16_to_cpu(ctx->attr->value_offset); - if (val < (char*)ctx->attr || val + - le32_to_cpu(ctx->attr->value_length) > - (char*)ctx->mrec + vol->mft_record_size) { - errno = EIO; - goto err_out; - } - memcpy(val + pos, b, count); - if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, - ctx->mrec)) { - /* - * NOTE: We are in a bad state at this moment. We have - * dirtied the mft record but we failed to commit it to - * disk. Since we have read the mft record ok before, - * it is unlikely to fail writing it, so is ok to just - * return error here... (AIA) - */ - goto err_out; - } - ntfs_attr_put_search_ctx(ctx); - return count; - } - total = 0; - /* Handle writes beyond initialized_size. */ - if (pos + count > na->initialized_size) { - if (ntfs_attr_map_whole_runlist(na)) - goto err_out; - /* Set initialized_size to @pos + @count. */ - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - goto err_out; - if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, - 0, NULL, 0, ctx)) - goto err_out; - /* If write starts beyond initialized_size, zero the gap. */ - if (pos > na->initialized_size) { - char *buf; - int err; - - buf = malloc(NTFS_BUF_SIZE); - if (!buf) { - err = errno; - ntfs_log_trace("Not enough memory.\n"); - errno = err; - goto err_out; - } - memset(buf, 0, NTFS_BUF_SIZE); - ofs = na->initialized_size; - while (ofs < pos) { - to_write = min(pos - ofs, NTFS_BUF_SIZE); - written = ntfs_rl_pwrite(vol, na->rl, ofs, - to_write, buf); - if (written <= 0) { - err = errno; - ntfs_log_trace("Failed to zero space between " - "initialized size and @pos.\n"); - free(buf); - errno = err; - goto err_out; - } - ofs += written; - } - free(buf); - } - ctx->attr->initialized_size = cpu_to_sle64(pos + count); - if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, - ctx->mrec)) { - /* - * Undo the change in the in-memory copy and send it - * back for writing. - */ - ctx->attr->initialized_size = - cpu_to_sle64(old_initialized_size); - ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, - ctx->mrec); - goto err_out; - } - na->initialized_size = pos + count; - ntfs_attr_put_search_ctx(ctx); - ctx = NULL; - /* - * NOTE: At this point the initialized_size in the mft record - * has been updated BUT there is random data on disk thus if - * we decide to abort, we MUST change the initialized_size - * again. - */ - need_to.undo_initialized_size = 1; - } - /* Find the runlist element containing the vcn. */ - rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); - if (!rl) { - /* - * If the vcn is not present it is an out of bounds write. - * However, we already extended the size of the attribute, - * so getting this here must be an error of some kind. - */ - if (errno == ENOENT) - errno = EIO; - goto err_out; - } - /* - * Scatter the data from the linear data buffer to the volume. Note, a - * partial final vcn is taken care of by the @count capping of write - * length. - */ - ofs = pos - (rl->vcn << vol->cluster_size_bits); - for (; count; rl++, ofs = 0) { - if (rl->lcn == LCN_RL_NOT_MAPPED) { - rl = ntfs_attr_find_vcn(na, rl->vcn); - if (!rl) { - if (errno == ENOENT) - errno = EIO; - goto rl_err_out; - } - /* Needed for case when runs merged. */ - ofs = pos + total - (rl->vcn << vol->cluster_size_bits); - } - if (!rl->length) { - errno = EIO; - goto rl_err_out; - } - if (rl->lcn < (LCN)0) { - LCN lcn_seek_from = -1; - runlist *rlc; - VCN cur_vcn, from_vcn; - s64 t; - int cnt; - - if (rl->lcn != (LCN)LCN_HOLE) { - errno = EIO; - goto rl_err_out; - } - /* - * It is a hole. Check if the data buffer is zero in - * this region and if not instantiate the hole. - */ - to_write = min(count, (rl->length << - vol->cluster_size_bits) - ofs); - written = to_write / sizeof(unsigned long); - eo = 0; - for (t = 0; t < written; t++) { - if (((const unsigned long*)b)[t]) { - eo = 1; - break; - } - } - cnt = to_write & (sizeof(unsigned long) - 1); - if (cnt && !eo) { - int i; - const u8 *b2; - - b2 = (const u8*)b + (to_write & - ~(sizeof(unsigned long) - 1)); - for (i = 0; i < cnt; i++) { - if (b2[i]) { - eo = 1; - break; - } - } - } - if (!eo) { - /* - * The buffer region is zero, update progress - * counters and proceed with next run. - */ - total += to_write; - count -= to_write; - b = (const u8*)b + to_write; - continue; - } - /* The buffer is non zero, instantiate the hole. */ - cur_vcn = rl->vcn; - from_vcn = rl->vcn + (ofs >> vol->cluster_size_bits); - ntfs_log_trace("Instantiate the hole with vcn 0x%llx.\n", - cur_vcn); - /* - * Map whole runlist to be able update mapping pairs - * later. - */ - if (ntfs_attr_map_whole_runlist(na)) - goto err_out; - /* - * Restore @rl, it probably get lost during runlist - * mapping. - */ - rl = ntfs_attr_find_vcn(na, cur_vcn); - if (!rl) { - ntfs_log_error("BUG! Failed to find run after " - "mapping whole runlist. Please " - "report to the %s.\n", - NTFS_DEV_LIST); - errno = EIO; - goto err_out; - } - /* - * Search backwards to find the best lcn to start - * seek from. - */ - rlc = rl; - while (rlc->vcn) { - rlc--; - if (rlc->lcn >= 0) { - lcn_seek_from = rlc->lcn + - (from_vcn - rlc->vcn); - break; - } - } - if (lcn_seek_from == -1) { - /* Backwards search failed, search forwards. */ - rlc = rl; - while (rlc->length) { - rlc++; - if (rlc->lcn >= 0) { - lcn_seek_from = rlc->lcn - - (rlc->vcn - from_vcn); - break; - } - } - } - /* Allocate clusters to instantiate the hole. */ - rlc = ntfs_cluster_alloc(vol, from_vcn, - ((ofs + to_write - 1) >> - vol->cluster_size_bits) + 1 + - rl->vcn - from_vcn, - lcn_seek_from, DATA_ZONE); - if (!rlc) { - eo = errno; - ntfs_log_trace("Failed to allocate clusters for hole " - "instantiating.\n"); - errno = eo; - goto err_out; - } - /* Merge runlists. */ - rl = ntfs_runlists_merge(na->rl, rlc); - if (!rl) { - eo = errno; - ntfs_log_trace("Failed to merge runlists.\n"); - if (ntfs_cluster_free_from_rl(vol, rlc)) { - ntfs_log_trace("Failed to free just " - "allocated clusters. Leaving " - "inconstant metadata. Run chkdsk\n"); - } - errno = eo; - goto err_out; - } - na->rl = rl; - need_to.update_mapping_pairs = 1; - if (update_from == -1) - update_from = from_vcn; - rl = ntfs_attr_find_vcn(na, cur_vcn); - if (!rl) { - /* - * It's definitely a BUG, if we failed to find - * @cur_vcn, because we missed it during - * instantiating of the hole. - */ - ntfs_log_error("BUG! Failed to find run after " - "instantiating. Please report " - "to the %s.\n", NTFS_DEV_LIST); - errno = EIO; - goto err_out; - } - /* If leaved part of the hole go to the next run. */ - if (rl->lcn < 0) - rl++; - /* Now LCN shoudn't be less than 0. */ - if (rl->lcn < 0) { - ntfs_log_error("BUG! LCN is lesser than 0. " - "Please report to the %s.\n", - NTFS_DEV_LIST); - errno = EIO; - goto err_out; - } - if (ofs) { - /* - * Need to clear region between start of - * @cur_vcn cluster and @ofs. - */ - char *buf; - - buf = malloc(ofs); - if (!buf) { - ntfs_log_trace("Not enough memory to " - "allocate %lld bytes.\n", ofs); - errno = ENOMEM; - goto err_out; - } - memset(buf, 0, ofs); - if (ntfs_rl_pwrite(vol, na->rl, cur_vcn << - vol->cluster_size_bits, - ofs, buf) < 0) { - eo = errno; - ntfs_log_trace("Failed to zero area.\n"); - free(buf); - errno = eo; - goto err_out; - } - free(buf); - } - if (rl->vcn < cur_vcn) { - /* - * Clusters that replaced hole are merged with - * previous run, so we need to update offset. - */ - ofs += (cur_vcn - rl->vcn) << - vol->cluster_size_bits; - } - if (rl->vcn > cur_vcn) { - /* - * We left part of the hole, so update we need - * to update offset - */ - ofs -= (rl->vcn - cur_vcn) << - vol->cluster_size_bits; - } - } - /* It is a real lcn, write it to the volume. */ - to_write = min(count, (rl->length << vol->cluster_size_bits) - - ofs); -retry: - ntfs_log_trace("Writing 0x%llx bytes to vcn 0x%llx, lcn 0x%llx," - " ofs 0x%llx.\n", to_write, rl->vcn, rl->lcn, - ofs); - if (!NVolReadOnly(vol)) - written = ntfs_pwrite(vol->dev, (rl->lcn << - vol->cluster_size_bits) + ofs, - to_write, b); - else - written = to_write; - /* If everything ok, update progress counters and continue. */ - if (written > 0) { - total += written; - count -= written; - b = (const u8*)b + written; - continue; - } - /* If the syscall was interrupted, try again. */ - if (written == (s64)-1 && errno == EINTR) - goto retry; - if (!written) - errno = EIO; - goto rl_err_out; - } -done: - if (ctx) - ntfs_attr_put_search_ctx(ctx); - /* Update mapping pairs if needed. */ - if (need_to.update_mapping_pairs) - ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/); - /* Finally, return the number of bytes written. */ - return total; -rl_err_out: - eo = errno; - if (total) { - if (need_to.undo_initialized_size) { - if (pos + total > na->initialized_size) - goto done; - /* - * TODO: Need to try to change initialized_size. If it - * succeeds goto done, otherwise goto err_out. (AIA) - */ - errno = EOPNOTSUPP; - goto err_out; - } - goto done; - } - errno = eo; -err_out: - eo = errno; - if (need_to.undo_initialized_size) { - int err; - - err = 0; - if (!ctx) { - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - err = 1; - } else - ntfs_attr_reinit_search_ctx(ctx); - if (!err) { - err = ntfs_attr_lookup(na->type, na->name, - na->name_len, 0, 0, NULL, 0, ctx); - if (!err) { - na->initialized_size = old_initialized_size; - ctx->attr->initialized_size = cpu_to_sle64( - old_initialized_size); - err = ntfs_mft_record_write(vol, - ctx->ntfs_ino->mft_no, - ctx->mrec); - } - } - if (err) { - /* - * FIXME: At this stage could try to recover by filling - * old_initialized_size -> new_initialized_size with - * data or at least zeroes. (AIA) - */ - ntfs_log_error("Eeek! Failed to recover from error. " - "Leaving metadata in inconsistent " - "state! Run chkdsk!\n"); - } - } - if (ctx) - ntfs_attr_put_search_ctx(ctx); - /* Update mapping pairs if needed. */ - if (need_to.update_mapping_pairs) - ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/); - /* Restore original data_size if needed. */ - if (need_to.undo_data_size && ntfs_attr_truncate(na, old_data_size)) - ntfs_log_trace("Failed to restore data_size.\n"); - errno = eo; - return -1; -} - -/** - * ntfs_attr_mst_pread - multi sector transfer protected ntfs attribute read - * @na: multi sector transfer protected ntfs attribute to read from - * @pos: byte position in the attribute to begin reading from - * @bk_cnt: number of mst protected blocks to read - * @bk_size: size of each mst protected block in bytes - * @dst: output data buffer - * - * This function will read @bk_cnt blocks of size @bk_size bytes each starting - * at offset @pos from the ntfs attribute @na into the data buffer @b. - * - * On success, the multi sector transfer fixups are applied and the number of - * read blocks is returned. If this number is lower than @bk_cnt this means - * that the read has either reached end of attribute or that an error was - * encountered during the read so that the read is partial. 0 means end of - * attribute or nothing to read (also return 0 when @bk_cnt or @bk_size are 0). - * - * On error and nothing has been read, return -1 with errno set appropriately - * to the return code of ntfs_attr_pread() or to EINVAL in case of invalid - * arguments. - * - * NOTE: If an incomplete multi sector transfer is detected the magic is - * changed to BAAD but no error is returned, i.e. it is possible that any of - * the returned blocks have multi sector transfer errors. This should be - * detected by the caller by checking each block with is_baad_recordp(&block). - * The reasoning is that we want to fixup as many blocks as possible and we - * want to return even bad ones to the caller so, e.g. in case of ntfsck, the - * errors can be repaired. - */ -s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, - const u32 bk_size, void *dst) -{ - s64 br; - u8 *end; - - ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n", - (unsigned long long)na->ni->mft_no, na->type, - (long long)pos); - if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { - errno = EINVAL; - return -1; - } - br = ntfs_attr_pread(na, pos, bk_cnt * bk_size, dst); - if (br <= 0) - return br; - br /= bk_size; - for (end = (u8*)dst + br * bk_size; (u8*)dst < end; dst = (u8*)dst + - bk_size) - ntfs_mst_post_read_fixup((NTFS_RECORD*)dst, bk_size); - /* Finally, return the number of blocks read. */ - return br; -} - -/** - * ntfs_attr_mst_pwrite - multi sector transfer protected ntfs attribute write - * @na: multi sector transfer protected ntfs attribute to write to - * @pos: position in the attribute to write to - * @bk_cnt: number of mst protected blocks to write - * @bk_size: size of each mst protected block in bytes - * @src: data buffer to write to disk - * - * This function will write @bk_cnt blocks of size @bk_size bytes each from - * data buffer @b to multi sector transfer (mst) protected ntfs attribute @na - * at position @pos. - * - * On success, return the number of successfully written blocks. If this number - * is lower than @bk_cnt this means that an error was encountered during the - * write so that the write is partial. 0 means nothing was written (also - * return 0 when @bk_cnt or @bk_size are 0). - * - * On error and nothing has been written, return -1 with errno set - * appropriately to the return code of ntfs_attr_pwrite(), or to EINVAL in case - * of invalid arguments. - * - * NOTE: We mst protect the data, write it, then mst deprotect it using a quick - * deprotect algorithm (no checking). This saves us from making a copy before - * the write and at the same time causes the usn to be incremented in the - * buffer. This conceptually fits in better with the idea that cached data is - * always deprotected and protection is performed when the data is actually - * going to hit the disk and the cache is immediately deprotected again - * simulating an mst read on the written data. This way cache coherency is - * achieved. - */ -s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, s64 bk_cnt, - const u32 bk_size, void *src) -{ - s64 written, i; - - ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n", - (unsigned long long)na->ni->mft_no, na->type, - (long long)pos); - if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { - errno = EINVAL; - return -1; - } - if (!bk_cnt) - return 0; - /* Prepare data for writing. */ - for (i = 0; i < bk_cnt; ++i) { - int err; - - err = ntfs_mst_pre_write_fixup((NTFS_RECORD*) - ((u8*)src + i * bk_size), bk_size); - if (err < 0) { - /* Abort write at this position. */ - if (!i) - return err; - bk_cnt = i; - break; - } - } - /* Write the prepared data. */ - written = ntfs_attr_pwrite(na, pos, bk_cnt * bk_size, src); - /* Quickly deprotect the data again. */ - for (i = 0; i < bk_cnt; ++i) - ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)src + i * - bk_size)); - if (written <= 0) - return written; - /* Finally, return the number of complete blocks written. */ - return written / bk_size; -} - -/** - * ntfs_attr_find - find (next) attribute in mft record - * @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 or CASE_SENSITIVE (ignored if @name not present) - * @val: attribute value to find (optional, resident attributes only) - * @val_len: attribute value length - * @ctx: search context with mft record and attribute to search from - * - * You shouldn't need to call this function directly. Use lookup_attr() instead. - * - * ntfs_attr_find() takes a search context @ctx as parameter and searches the - * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an - * attribute of @type, optionally @name and @val. If found, ntfs_attr_find() - * returns 0 and @ctx->attr will point to the found attribute. - * - * If not found, ntfs_attr_find() returns -1, with errno set to ENOENT and - * @ctx->attr will point to the attribute before which the attribute being - * searched for would need to be inserted if such an action were to be desired. - * - * On actual error, ntfs_attr_find() returns -1 with errno set to the error - * code but not to ENOENT. In this case @ctx->attr is undefined and in - * particular do not rely on it not changing. - * - * If @ctx->is_first is TRUE, the search begins with @ctx->attr itself. If it - * is FALSE, the search begins after @ctx->attr. - * - * If @type is AT_UNUSED, return the first found attribute, i.e. one can - * enumerate all attributes by setting @type to AT_UNUSED and then calling - * ntfs_attr_find() repeatedly until it returns -1 with errno set to ENOENT to - * indicate that there are no more entries. During the enumeration, each - * successful call of ntfs_attr_find() will return the next attribute in the - * mft record @ctx->mrec. - * - * If @type is AT_END, seek to the end and return -1 with errno set to ENOENT. - * AT_END is not a valid attribute, its length is zero for example, thus it is - * safer to return error instead of success in this case. This also allows us - * to interoperate cleanly with ntfs_external_attr_find(). - * - * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present - * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, - * match both named and unnamed attributes. - * - * If @ic is IGNORE_CASE, the @name comparison is not case sensitive and - * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record - * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at - * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case - * sensitive. When @name is present, @name_len is the @name length in Unicode - * characters. - * - * If @name is not present (NULL), we assume that the unnamed attribute is - * being searched for. - * - * Finally, the resident attribute value @val is looked for, if present. - * If @val is not present (NULL), @val_len is ignored. - * - * ntfs_attr_find() only searches the specified mft record and it ignores the - * presence of an attribute list attribute (unless it is the one being searched - * for, obviously). If you need to take attribute lists into consideration, use - * ntfs_attr_lookup() instead (see below). This also means that you cannot use - * ntfs_attr_find() to search for extent records of non-resident attributes, as - * extents with lowest_vcn != 0 are usually described by the attribute list - * attribute only. - Note that it is possible that the first extent is only in - * the attribute list while the last extent is in the base mft record, so don't - * rely on being able to find the first extent in the base mft record. - * - * Warning: Never use @val when looking for attribute types which can be - * non-resident as this most likely will result in a crash! - */ -static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx) -{ - ATTR_RECORD *a; - ntfs_volume *vol; - ntfschar *upcase; - u32 upcase_len; - - ntfs_log_trace("Entering.\n"); - - if (ctx->ntfs_ino) { - vol = ctx->ntfs_ino->vol; - upcase = vol->upcase; - upcase_len = vol->upcase_len; - } else { - if (name && name != AT_UNNAMED) { - errno = EINVAL; - return -1; - } - vol = NULL; - upcase = NULL; - upcase_len = 0; - } - /* - * Iterate over attributes in mft record starting at @ctx->attr, or the - * attribute following that, if @ctx->is_first is TRUE. - */ - if (ctx->is_first) { - a = ctx->attr; - ctx->is_first = FALSE; - } else - a = (ATTR_RECORD*)((char*)ctx->attr + - le32_to_cpu(ctx->attr->length)); - for (;; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) { - if (p2n(a) < p2n(ctx->mrec) || (char*)a > (char*)ctx->mrec + - le32_to_cpu(ctx->mrec->bytes_allocated)) - break; - ctx->attr = a; - if (((type != AT_UNUSED) && (le32_to_cpu(a->type) > - le32_to_cpu(type))) || - (a->type == AT_END)) { - errno = ENOENT; - return -1; - } - if (!a->length) - break; - /* If this is an enumeration return this attribute. */ - if (type == AT_UNUSED) - return 0; - if (a->type != type) - continue; - /* - * If @name is AT_UNNAMED we want an unnamed attribute. - * If @name is present, compare the two names. - * Otherwise, match any attribute. - */ - if (name == AT_UNNAMED) { - /* The search failed if the found attribute is named. */ - if (a->name_length) { - errno = ENOENT; - return -1; - } - } else if (name && !ntfs_names_are_equal(name, name_len, - (ntfschar*)((char*)a + le16_to_cpu(a->name_offset)), - a->name_length, ic, upcase, upcase_len)) { - register int rc; - - rc = ntfs_names_collate(name, name_len, - (ntfschar*)((char*)a + - le16_to_cpu(a->name_offset)), - a->name_length, 1, IGNORE_CASE, - upcase, upcase_len); - /* - * If @name collates before a->name, there is no - * matching attribute. - */ - if (rc == -1) { - errno = ENOENT; - return -1; - } - /* If the strings are not equal, continue search. */ - if (rc) - continue; - rc = ntfs_names_collate(name, name_len, - (ntfschar*)((char*)a + - le16_to_cpu(a->name_offset)), - a->name_length, 1, CASE_SENSITIVE, - upcase, upcase_len); - if (rc == -1) { - errno = ENOENT; - return -1; - } - if (rc) - continue; - } - /* - * The names match or @name not present and attribute is - * unnamed. If no @val specified, we have found the attribute - * and are done. - */ - if (!val) - return 0; - /* @val is present; compare values. */ - else { - register int rc; - - rc = memcmp(val, (char*)a +le16_to_cpu(a->value_offset), - min(val_len, - le32_to_cpu(a->value_length))); - /* - * If @val collates before the current attribute's - * value, there is no matching attribute. - */ - if (!rc) { - register u32 avl; - avl = le32_to_cpu(a->value_length); - if (val_len == avl) - return 0; - if (val_len < avl) { - errno = ENOENT; - return -1; - } - } else if (rc < 0) { - errno = ENOENT; - return -1; - } - } - } - ntfs_log_debug("ntfs_attr_find(): File is corrupt. Run chkdsk.\n"); - errno = EIO; - return -1; -} - -/** - * ntfs_external_attr_find - find an attribute in the attribute list of an inode - * @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 or CASE_SENSITIVE (ignored if @name not present) - * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) - * @val: attribute value to find (optional, resident attributes only) - * @val_len: attribute value length - * @ctx: search context with mft record and attribute to search from - * - * You shouldn't need to call this function directly. Use ntfs_attr_lookup() - * instead. - * - * Find an attribute by searching the attribute list for the corresponding - * attribute list entry. Having found the entry, map the mft record for read - * if the attribute is in a different mft record/inode, find the attribute in - * there and return it. - * - * If @type is AT_UNUSED, return the first found attribute, i.e. one can - * enumerate all attributes by setting @type to AT_UNUSED and then calling - * ntfs_external_attr_find() repeatedly until it returns -1 with errno set to - * ENOENT to indicate that there are no more entries. During the enumeration, - * each successful call of ntfs_external_attr_find() will return the next - * attribute described by the attribute list of the base mft record described - * by the search context @ctx. - * - * If @type is AT_END, seek to the end of the base mft record ignoring the - * attribute list completely and return -1 with errno set to ENOENT. AT_END is - * not a valid attribute, its length is zero for example, thus it is safer to - * return error instead of success in this case. - * - * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present - * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, - * match both named and unnamed attributes. - * - * On first search @ctx->ntfs_ino must be the inode of the base mft record and - * @ctx must have been obtained from a call to ntfs_attr_get_search_ctx(). - * On subsequent calls, @ctx->ntfs_ino can be any extent inode, too - * (@ctx->base_ntfs_ino is then the base inode). - * - * After finishing with the attribute/mft record you need to call - * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any - * mapped extent inodes, etc). - * - * Return 0 if the search was successful and -1 if not, with errno set to the - * error code. - * - * On success, @ctx->attr is the found attribute, it is in mft record - * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this - * attribute with @ctx->base_* being the base mft record to which @ctx->attr - * belongs. - * - * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the - * attribute which collates just after the attribute being searched for in the - * base ntfs inode, i.e. if one wants to add the attribute to the mft record - * this is the correct place to insert it into, and if there is not enough - * space, the attribute should be placed in an extent mft record. - * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list - * at which the new attribute's attribute list entry should be inserted. The - * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. - * The only exception to this is when @type is AT_END, in which case - * @ctx->al_entry is set to NULL also (see above). - * - * The following error codes are defined: - * ENOENT Attribute not found, not an error as such. - * EINVAL Invalid arguments. - * EIO I/O error or corrupt data structures found. - * ENOMEM Not enough memory to allocate necessary buffers. - */ -static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const VCN lowest_vcn, const u8 *val, const u32 val_len, - ntfs_attr_search_ctx *ctx) -{ - ntfs_inode *base_ni, *ni; - ntfs_volume *vol; - ATTR_LIST_ENTRY *al_entry, *next_al_entry; - u8 *al_start, *al_end; - ATTR_RECORD *a; - ntfschar *al_name; - u32 al_name_len; - BOOL is_first_search = FALSE; - - ni = ctx->ntfs_ino; - base_ni = ctx->base_ntfs_ino; - ntfs_log_trace("Entering for inode 0x%llx, attribute type 0x%x.\n", - (unsigned long long)ni->mft_no, type); - if (!base_ni) { - /* First call happens with the base mft record. */ - base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino; - ctx->base_mrec = ctx->mrec; - } - if (ni == base_ni) - ctx->base_attr = ctx->attr; - if (type == AT_END) - goto not_found; - vol = base_ni->vol; - al_start = base_ni->attr_list; - al_end = al_start + base_ni->attr_list_size; - if (!ctx->al_entry) { - ctx->al_entry = (ATTR_LIST_ENTRY*)al_start; - is_first_search = TRUE; - } - /* - * Iterate over entries in attribute list starting at @ctx->al_entry, - * or the entry following that, if @ctx->is_first is TRUE. - */ - if (ctx->is_first) { - al_entry = ctx->al_entry; - ctx->is_first = FALSE; - /* - * If an enumeration and the first attribute is higher than - * the attribute list itself, need to return the attribute list - * attribute. - */ - if ((type == AT_UNUSED) && is_first_search && - le32_to_cpu(al_entry->type) > - le32_to_cpu(AT_ATTRIBUTE_LIST)) - goto find_attr_list_attr; - } else { - al_entry = (ATTR_LIST_ENTRY*)((char*)ctx->al_entry + - le16_to_cpu(ctx->al_entry->length)); - /* - * If this is an enumeration and the attribute list attribute - * is the next one in the enumeration sequence, just return the - * attribute list attribute from the base mft record as it is - * not listed in the attribute list itself. - */ - if ((type == AT_UNUSED) && le32_to_cpu(ctx->al_entry->type) < - le32_to_cpu(AT_ATTRIBUTE_LIST) && - le32_to_cpu(al_entry->type) > - le32_to_cpu(AT_ATTRIBUTE_LIST)) { - int rc; -find_attr_list_attr: - - /* Check for bogus calls. */ - if (name || name_len || val || val_len || lowest_vcn) { - errno = EINVAL; - return -1; - } - - /* We want the base record. */ - ctx->ntfs_ino = base_ni; - ctx->mrec = ctx->base_mrec; - ctx->is_first = TRUE; - /* Sanity checks are performed elsewhere. */ - ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + - le16_to_cpu(ctx->mrec->attrs_offset)); - - /* Find the attribute list attribute. */ - rc = ntfs_attr_find(AT_ATTRIBUTE_LIST, NULL, 0, - IGNORE_CASE, NULL, 0, ctx); - - /* - * Setup the search context so the correct - * attribute is returned next time round. - */ - ctx->al_entry = al_entry; - ctx->is_first = TRUE; - - /* Got it. Done. */ - if (!rc) - return 0; - - /* Error! If other than not found return it. */ - if (errno != ENOENT) - return rc; - - /* Not found?!? Absurd! Must be a bug... )-: */ - ntfs_log_trace("BUG! Attribute list attribute not found but " - "it exists! Returning error (EINVAL).\n"); - errno = EINVAL; - return -1; - } - } - for (;; al_entry = next_al_entry) { - /* Out of bounds check. */ - if ((u8*)al_entry < base_ni->attr_list || - (u8*)al_entry > al_end) - break; /* Inode is corrupt. */ - ctx->al_entry = al_entry; - /* Catch the end of the attribute list. */ - if ((u8*)al_entry == al_end) - goto not_found; - if (!al_entry->length) - break; - if ((u8*)al_entry + 6 > al_end || (u8*)al_entry + - le16_to_cpu(al_entry->length) > al_end) - break; - next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry + - le16_to_cpu(al_entry->length)); - if (type != AT_UNUSED) { - if (le32_to_cpu(al_entry->type) > le32_to_cpu(type)) - goto not_found; - if (type != al_entry->type) - continue; - } - al_name_len = al_entry->name_length; - al_name = (ntfschar*)((u8*)al_entry + al_entry->name_offset); - /* - * If !@type we want the attribute represented by this - * attribute list entry. - */ - if (type == AT_UNUSED) - goto is_enumeration; - /* - * If @name is AT_UNNAMED we want an unnamed attribute. - * If @name is present, compare the two names. - * Otherwise, match any attribute. - */ - if (name == AT_UNNAMED) { - if (al_name_len) - goto not_found; - } else if (name && !ntfs_names_are_equal(al_name, al_name_len, - name, name_len, ic, vol->upcase, - vol->upcase_len)) { - register int rc; - - rc = ntfs_names_collate(name, name_len, al_name, - al_name_len, 1, IGNORE_CASE, - vol->upcase, vol->upcase_len); - /* - * If @name collates before al_name, there is no - * matching attribute. - */ - if (rc == -1) - goto not_found; - /* If the strings are not equal, continue search. */ - if (rc) - continue; - /* - * FIXME: Reverse engineering showed 0, IGNORE_CASE but - * that is inconsistent with ntfs_attr_find(). The - * subsequent rc checks were also different. Perhaps I - * made a mistake in one of the two. Need to recheck - * which is correct or at least see what is going - * on... (AIA) - */ - rc = ntfs_names_collate(name, name_len, al_name, - al_name_len, 1, CASE_SENSITIVE, - vol->upcase, vol->upcase_len); - if (rc == -1) - goto not_found; - if (rc) - continue; - } - /* - * The names match or @name not present and attribute is - * unnamed. Now check @lowest_vcn. Continue search if the - * next attribute list entry still fits @lowest_vcn. Otherwise - * we have reached the right one or the search has failed. - */ - if (lowest_vcn && (u8*)next_al_entry >= al_start && - (u8*)next_al_entry + 6 < al_end && - (u8*)next_al_entry + le16_to_cpu( - next_al_entry->length) <= al_end && - sle64_to_cpu(next_al_entry->lowest_vcn) <= - lowest_vcn && - next_al_entry->type == al_entry->type && - next_al_entry->name_length == al_name_len && - ntfs_names_are_equal((ntfschar*)((char*) - next_al_entry + - next_al_entry->name_offset), - next_al_entry->name_length, - al_name, al_name_len, CASE_SENSITIVE, - vol->upcase, vol->upcase_len)) - continue; -is_enumeration: - if (MREF_LE(al_entry->mft_reference) == ni->mft_no) { - if (MSEQNO_LE(al_entry->mft_reference) != - le16_to_cpu( - ni->mrec->sequence_number)) { - ntfs_log_debug("Found stale mft reference in " - "attribute list!\n"); - break; - } - } else { /* Mft references do not match. */ - /* Do we want the base record back? */ - if (MREF_LE(al_entry->mft_reference) == - base_ni->mft_no) { - ni = ctx->ntfs_ino = base_ni; - ctx->mrec = ctx->base_mrec; - } else { - /* We want an extent record. */ - ni = ntfs_extent_inode_open(base_ni, - al_entry->mft_reference); - if (!ni) { - ntfs_log_perror("Failed to map extent inode"); - break; - } - ctx->ntfs_ino = ni; - ctx->mrec = ni->mrec; - } - ctx->attr = (ATTR_RECORD*)((char*)ctx->mrec + - le16_to_cpu(ctx->mrec->attrs_offset)); - } - /* - * ctx->ntfs_ino, ctx->mrec, and ctx->attr now point to the - * mft record containing the attribute represented by the - * current al_entry. - */ - /* - * We could call into ntfs_attr_find() to find the right - * attribute in this mft record but this would be less - * efficient and not quite accurate as ntfs_attr_find() ignores - * the attribute instance numbers for example which become - * important when one plays with attribute lists. Also, because - * a proper match has been found in the attribute list entry - * above, the comparison can now be optimized. So it is worth - * re-implementing a simplified ntfs_attr_find() here. - */ - a = ctx->attr; - /* - * Use a manual loop so we can still use break and continue - * with the same meanings as above. - */ -do_next_attr_loop: - if ((char*)a < (char*)ctx->mrec || (char*)a > (char*)ctx->mrec + - le32_to_cpu(ctx->mrec->bytes_allocated)) - break; - if (a->type == AT_END) - continue; - if (!a->length) - break; - if (al_entry->instance != a->instance) - goto do_next_attr; - /* - * If the type and/or the name are/is mismatched between the - * attribute list entry and the attribute record, there is - * corruption so we break and return error EIO. - */ - if (al_entry->type != a->type) - break; - if (!ntfs_names_are_equal((ntfschar*)((char*)a + - le16_to_cpu(a->name_offset)), - a->name_length, al_name, - al_name_len, CASE_SENSITIVE, - vol->upcase, vol->upcase_len)) - break; - ctx->attr = a; - /* - * If no @val specified or @val specified and it matches, we - * have found it! Also, if !@type, it is an enumeration, so we - * want the current attribute. - */ - if ((type == AT_UNUSED) || !val || (!a->non_resident && - le32_to_cpu(a->value_length) == val_len && - !memcmp((char*)a + le16_to_cpu(a->value_offset), - val, val_len))) { - return 0; - } -do_next_attr: - /* Proceed to the next attribute in the current mft record. */ - a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); - goto do_next_attr_loop; - } - if (ni != base_ni) { - ctx->ntfs_ino = base_ni; - ctx->mrec = ctx->base_mrec; - ctx->attr = ctx->base_attr; - } - ntfs_log_debug("Inode is corrupt.\n"); - errno = EIO; - return -1; -not_found: - /* - * If we were looking for AT_END or we were enumerating and reached the - * end, we reset the search context @ctx and use ntfs_attr_find() to - * seek to the end of the base mft record. - */ - if (type == AT_UNUSED || type == AT_END) { - ntfs_attr_reinit_search_ctx(ctx); - return ntfs_attr_find(AT_END, name, name_len, ic, val, val_len, - ctx); - } - /* - * The attribute wasn't found. Before we return, we want to ensure - * @ctx->mrec and @ctx->attr indicate the position at which the - * attribute should be inserted in the base mft record. Since we also - * want to preserve @ctx->al_entry we cannot reinitialize the search - * context using ntfs_attr_reinit_search_ctx() as this would set - * @ctx->al_entry to NULL. Thus we do the necessary bits manually (see - * ntfs_attr_init_search_ctx() below). Note, we _only_ preserve - * @ctx->al_entry as the remaining fields (base_*) are identical to - * their non base_ counterparts and we cannot set @ctx->base_attr - * correctly yet as we do not know what @ctx->attr will be set to by - * the call to ntfs_attr_find() below. - */ - ctx->mrec = ctx->base_mrec; - ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + - le16_to_cpu(ctx->mrec->attrs_offset)); - ctx->is_first = TRUE; - ctx->ntfs_ino = ctx->base_ntfs_ino; - ctx->base_ntfs_ino = NULL; - ctx->base_mrec = NULL; - ctx->base_attr = NULL; - /* - * In case there are multiple matches in the base mft record, need to - * keep enumerating until we get an attribute not found response (or - * another error), otherwise we would keep returning the same attribute - * over and over again and all programs using us for enumeration would - * lock up in a tight loop. - */ - { - int ret; - - do { - ret = ntfs_attr_find(type, name, name_len, ic, val, - val_len, ctx); - } while (!ret); - return ret; - } -} - -/** - * ntfs_attr_lookup - find an attribute in an ntfs inode - * @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 or CASE_SENSITIVE (ignored if @name not present) - * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) - * @val: attribute value to find (optional, resident attributes only) - * @val_len: attribute value length - * @ctx: search context with mft record and attribute to search from - * - * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must - * be the base mft record and @ctx must have been obtained from a call to - * ntfs_attr_get_search_ctx(). - * - * This function transparently handles attribute lists and @ctx is used to - * continue searches where they were left off at. - * - * If @type is AT_UNUSED, return the first found attribute, i.e. one can - * enumerate all attributes by setting @type to AT_UNUSED and then calling - * ntfs_attr_lookup() repeatedly until it returns -1 with errno set to ENOENT - * to indicate that there are no more entries. During the enumeration, each - * successful call of ntfs_attr_lookup() will return the next attribute, with - * the current attribute being described by the search context @ctx. - * - * If @type is AT_END, seek to the end of the base mft record ignoring the - * attribute list completely and return -1 with errno set to ENOENT. AT_END is - * not a valid attribute, its length is zero for example, thus it is safer to - * return error instead of success in this case. It should never be needed to - * do this, but we implement the functionality because it allows for simpler - * code inside ntfs_external_attr_find(). - * - * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present - * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, - * match both named and unnamed attributes. - * - * After finishing with the attribute/mft record you need to call - * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any - * mapped extent inodes, etc). - * - * Return 0 if the search was successful and -1 if not, with errno set to the - * error code. - * - * On success, @ctx->attr is the found attribute, it is in mft record - * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this - * attribute with @ctx->base_* being the base mft record to which @ctx->attr - * belongs. If no attribute list attribute is present @ctx->al_entry and - * @ctx->base_* are NULL. - * - * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the - * attribute which collates just after the attribute being searched for in the - * base ntfs inode, i.e. if one wants to add the attribute to the mft record - * this is the correct place to insert it into, and if there is not enough - * space, the attribute should be placed in an extent mft record. - * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list - * at which the new attribute's attribute list entry should be inserted. The - * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. - * The only exception to this is when @type is AT_END, in which case - * @ctx->al_entry is set to NULL also (see above). - * - * - * The following error codes are defined: - * ENOENT Attribute not found, not an error as such. - * EINVAL Invalid arguments. - * EIO I/O error or corrupt data structures found. - * ENOMEM Not enough memory to allocate necessary buffers. - */ -int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const VCN lowest_vcn, const u8 *val, const u32 val_len, - ntfs_attr_search_ctx *ctx) -{ - ntfs_volume *vol; - ntfs_inode *base_ni; - - if (!ctx || !ctx->mrec || !ctx->attr || (name && name != AT_UNNAMED && - (!ctx->ntfs_ino || !(vol = ctx->ntfs_ino->vol) || - !vol->upcase || !vol->upcase_len))) { - errno = EINVAL; - return -1; - } - if (ctx->base_ntfs_ino) - base_ni = ctx->base_ntfs_ino; - else - base_ni = ctx->ntfs_ino; - if (!base_ni || !NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST) - return ntfs_attr_find(type, name, name_len, ic, val, val_len, - ctx); - return ntfs_external_attr_find(type, name, name_len, ic, lowest_vcn, - val, val_len, ctx); -} - -/** - * ntfs_attr_init_search_ctx - initialize an attribute search context - * @ctx: attribute search context to initialize - * @ni: ntfs inode with which to initialize the search context - * @mrec: mft record with which to initialize the search context - * - * Initialize the attribute search context @ctx with @ni and @mrec. - */ -static __inline__ void ntfs_attr_init_search_ctx(ntfs_attr_search_ctx *ctx, - ntfs_inode *ni, MFT_RECORD *mrec) -{ - if (!mrec) - mrec = ni->mrec; - ctx->mrec = mrec; - /* Sanity checks are performed elsewhere. */ - ctx->attr = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset)); - ctx->is_first = TRUE; - ctx->ntfs_ino = ni; - ctx->al_entry = NULL; - ctx->base_ntfs_ino = NULL; - ctx->base_mrec = NULL; - ctx->base_attr = NULL; -} - -/** - * ntfs_attr_reinit_search_ctx - reinitialize an attribute search context - * @ctx: attribute search context to reinitialize - * - * Reinitialize the attribute search context @ctx. - * - * This is used when a search for a new attribute is being started to reset - * the search context to the beginning. - */ -void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx) -{ - if (!ctx->base_ntfs_ino) { - /* No attribute list. */ - ctx->is_first = TRUE; - /* Sanity checks are performed elsewhere. */ - ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + - le16_to_cpu(ctx->mrec->attrs_offset)); - /* - * This needs resetting due to ntfs_external_attr_find() which - * can leave it set despite having zeroed ctx->base_ntfs_ino. - */ - ctx->al_entry = NULL; - return; - } /* Attribute list. */ - ntfs_attr_init_search_ctx(ctx, ctx->base_ntfs_ino, ctx->base_mrec); - return; -} - -/** - * ntfs_attr_get_search_ctx - allocate/initialize a new attribute search context - * @ni: ntfs inode with which to initialize the search context - * @mrec: mft record with which to initialize the search context - * - * Allocate a new attribute search context, initialize it with @ni and @mrec, - * and return it. Return NULL on error with errno set to ENOMEM. - * - * @mrec can be NULL, in which case the mft record is taken from @ni. - * - * Note: For low level utilities which know what they are doing we allow @ni to - * be NULL and @mrec to be set. Do NOT do this unless you understand the - * implications!!! For example it is no longer safe to call ntfs_attr_lookup() - * if you - */ -ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec) -{ - ntfs_attr_search_ctx *ctx; - - if (!ni && !mrec) { - errno = EINVAL; - return NULL; - } - ctx = malloc(sizeof(ntfs_attr_search_ctx)); - if (ctx) - ntfs_attr_init_search_ctx(ctx, ni, mrec); - return ctx; -} - -/** - * ntfs_attr_put_search_ctx - release an attribute search context - * @ctx: attribute search context to free - * - * Release the attribute search context @ctx. - */ -void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx) -{ - free(ctx); -} - -/** - * ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file - * @vol: ntfs volume to which the attribute belongs - * @type: attribute type which to find - * - * Search for the attribute definition record corresponding to the attribute - * @type in the $AttrDef system file. - * - * Return the attribute type definition record if found and NULL if not found - * or an error occurred. On error the error code is stored in errno. The - * following error codes are defined: - * ENOENT - The attribute @type is not specified in $AttrDef. - * EINVAL - Invalid parameters (e.g. @vol is not valid). - */ -ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, - const ATTR_TYPES type) -{ - ATTR_DEF *ad; - - if (!vol || !vol->attrdef || !type) { - errno = EINVAL; - return NULL; - } - for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef < - vol->attrdef_len && ad->type; ++ad) { - /* We haven't found it yet, carry on searching. */ - if (le32_to_cpu(ad->type) < le32_to_cpu(type)) - continue; - /* We found the attribute; return it. */ - if (ad->type == type) - return ad; - /* We have gone too far already. No point in continuing. */ - break; - } - /* Attribute not found?!? */ - errno = ENOENT; - return NULL; -} - -/** - * ntfs_attr_size_bounds_check - check a size of an attribute type for validity - * @vol: ntfs volume to which the attribute belongs - * @type: attribute type which to check - * @size: size which to check - * - * Check whether the @size in bytes is valid for an attribute of @type on the - * ntfs volume @vol. This information is obtained from $AttrDef system file. - * - * Return 0 if valid and -1 if not valid or an error occurred. On error the - * error code is stored in errno. The following error codes are defined: - * ERANGE - @size is not valid for the attribute @type. - * ENOENT - The attribute @type is not specified in $AttrDef. - * EINVAL - Invalid parameters (e.g. @size is < 0 or @vol is not valid). - */ -int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type, - const s64 size) -{ - ATTR_DEF *ad; - - if (size < 0) { - errno = EINVAL; - return -1; - } - - /* - * $ATTRIBUTE_LIST should be not greater than 0x40000, but this is not - * listed in the AttrDef. - */ - if (type == AT_ATTRIBUTE_LIST && size > 0x40000) { - errno = ERANGE; - return -1; - } - - ad = ntfs_attr_find_in_attrdef(vol, type); - if (!ad) - return -1; - /* We found the attribute. - Do the bounds check. */ - if ((sle64_to_cpu(ad->min_size) && size < - sle64_to_cpu(ad->min_size)) || - ((sle64_to_cpu(ad->max_size) > 0) && size > - sle64_to_cpu(ad->max_size))) { - /* @size is out of range! */ - errno = ERANGE; - return -1; - } - return 0; -} - -/** - * ntfs_attr_can_be_non_resident - check if an attribute can be non-resident - * @vol: ntfs volume to which the attribute belongs - * @type: attribute type which to check - * - * Check whether the attribute of @type on the ntfs volume @vol is allowed to - * be non-resident. This information is obtained from $AttrDef system file. - * - * Return 0 if the attribute is allowed to be non-resident and -1 if not or an - * error occurred. On error the error code is stored in errno. The following - * error codes are defined: - * EPERM - The attribute is not allowed to be non-resident. - * ENOENT - The attribute @type is not specified in $AttrDef. - * EINVAL - Invalid parameters (e.g. @vol is not valid). - */ -int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPES type) -{ - ATTR_DEF *ad; - - /* Find the attribute definition record in $AttrDef. */ - ad = ntfs_attr_find_in_attrdef(vol, type); - if (!ad) - return -1; - /* Check the flags and return the result. */ - if (ad->flags & ATTR_DEF_RESIDENT) { - errno = EPERM; - return -1; - } - return 0; -} - -/** - * ntfs_attr_can_be_resident - check if an attribute can be resident - * @vol: ntfs volume to which the attribute belongs - * @type: attribute type which to check - * - * Check whether the attribute of @type on the ntfs volume @vol is allowed to - * be resident. This information is derived from our ntfs knowledge and may - * not be completely accurate, especially when user defined attributes are - * present. Basically we allow everything to be resident except for index - * allocation and extended attribute attributes. - * - * Return 0 if the attribute is allowed to be resident and -1 if not or an - * error occurred. On error the error code is stored in errno. The following - * error codes are defined: - * EPERM - The attribute is not allowed to be resident. - * EINVAL - Invalid parameters (e.g. @vol is not valid). - * - * Warning: In the system file $MFT the attribute $Bitmap must be non-resident - * otherwise windows will not boot (blue screen of death)! We cannot - * check for this here as we don't know which inode's $Bitmap is being - * asked about so the caller needs to special case this. - */ -int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPES type) -{ - if (!vol || !vol->attrdef || !type) { - errno = EINVAL; - return -1; - } - if (type != AT_INDEX_ALLOCATION) - return 0; - errno = EPERM; - return -1; -} - -/** - * ntfs_make_room_for_attr - make room for an attribute inside an mft record - * @m: mft record - * @pos: position at which to make space - * @size: byte size to make available at this position - * - * @pos points to the attribute in front of which we want to make space. - * - * Return 0 on success or -1 on error. On error the error code is stored in - * errno. Possible error codes are: - * ENOSPC - There is not enough space available to complete operation. The - * caller has to make space before calling this. - * EINVAL - Input parameters were faulty. - */ -int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size) -{ - u32 biu; - - ntfs_log_trace("Entering for pos 0x%d, size %u.\n", - (int)(pos - (u8*)m), (unsigned) size); - - /* Make size 8-byte alignment. */ - size = (size + 7) & ~7; - - /* Rigorous consistency checks. */ - if (!m || !pos || pos < (u8*)m || pos + size > - (u8*)m + le32_to_cpu(m->bytes_allocated)) { - errno = EINVAL; - return -1; - } - /* The -8 is for the attribute terminator. */ - if (pos - (u8*)m > (int)le32_to_cpu(m->bytes_in_use) - 8) { - errno = EINVAL; - return -1; - } - /* Nothing to do. */ - if (!size) - return 0; - - biu = le32_to_cpu(m->bytes_in_use); - /* Do we have enough space? */ - if (biu + size > le32_to_cpu(m->bytes_allocated)) { - errno = ENOSPC; - return -1; - } - /* Move everything after pos to pos + size. */ - memmove(pos + size, pos, biu - (pos - (u8*)m)); - /* Update mft record. */ - m->bytes_in_use = cpu_to_le32(biu + size); - return 0; -} - -/** - * ntfs_resident_attr_record_add - add resident attribute to inode - * @ni: opened ntfs inode to which MFT record add attribute - * @type: type of the new attribute - * @name: name of the new attribute - * @name_len: name length of the new attribute - * @val: value of the new attribute - * @size: size of new attribute (length of @val, if @val != NULL) - * @flags: flags of the new attribute - * - * Return offset to attribute from the beginning of the mft record on success - * and -1 on error. On error the error code is stored in errno. - * Possible error codes are: - * EINVAL - Invalid arguments passed to function. - * EEXIST - Attribute of such type and with same name already exists. - * EIO - I/O error occurred or damaged filesystem. - */ -int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, u8 *val, u32 size, - ATTR_FLAGS flags) -{ - ntfs_attr_search_ctx *ctx; - u32 length; - ATTR_RECORD *a; - MFT_RECORD *m; - int err, offset; - ntfs_inode *base_ni; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, flags 0x%x.\n", - (long long) ni->mft_no, (unsigned) type, (unsigned) flags); - - if (!ni || (!name && name_len)) { - errno = EINVAL; - return -1; - } - - if (ntfs_attr_can_be_resident(ni->vol, type)) { - err = errno; - if (errno == EPERM) - ntfs_log_trace("Attribute can't be resident.\n"); - else - ntfs_log_trace("ntfs_attr_can_be_resident failed.\n"); - errno = err; - return -1; - } - - /* Locate place where record should be. */ - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) - return -1; - /* - * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for - * attribute in @ni->mrec, not any extent inode in case if @ni is base - * file record. - */ - if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, val, size, - ctx)) { - err = EEXIST; - ntfs_log_trace("Attribute already present.\n"); - goto put_err_out; - } - if (errno != ENOENT) { - err = EIO; - goto put_err_out; - } - a = ctx->attr; - m = ctx->mrec; - - /* Make room for attribute. */ - length = offsetof(ATTR_RECORD, resident_end) + - ((name_len * sizeof(ntfschar) + 7) & ~7) + - ((size + 7) & ~7); - if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) { - err = errno; - ntfs_log_trace("Failed to make room for attribute.\n"); - goto put_err_out; - } - - /* Setup record fields. */ - offset = ((u8*)a - (u8*)m); - a->type = type; - a->length = cpu_to_le32(length); - a->non_resident = 0; - a->name_length = name_len; - a->name_offset = cpu_to_le16(offsetof(ATTR_RECORD, resident_end)); - a->flags = flags; - a->instance = m->next_attr_instance; - a->value_length = cpu_to_le32(size); - a->value_offset = cpu_to_le16(length - ((size + 7) & ~7)); - if (val) - memcpy((u8*)a + le16_to_cpu(a->value_offset), val, size); - else - memset((u8*)a + le16_to_cpu(a->value_offset), 0, size); - if (type == AT_FILE_NAME) - a->resident_flags = RESIDENT_ATTR_IS_INDEXED; - else - a->resident_flags = 0; - if (name_len) - memcpy((u8*)a + le16_to_cpu(a->name_offset), - name, sizeof(ntfschar) * name_len); - m->next_attr_instance = - cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff); - if (ni->nr_extents == -1) - base_ni = ni->base_ni; - else - base_ni = ni; - if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { - if (ntfs_attrlist_entry_add(ni, a)) { - err = errno; - ntfs_attr_record_resize(m, a, 0); - ntfs_log_trace("Failed add attribute entry to " - "ATTRIBUTE_LIST.\n"); - goto put_err_out; - } - } - ntfs_inode_mark_dirty(ni); - ntfs_attr_put_search_ctx(ctx); - return offset; -put_err_out: - ntfs_attr_put_search_ctx(ctx); - errno = err; - return -1; -} - -/** - * ntfs_non_resident_attr_record_add - add extent of non-resident attribute - * @ni: opened ntfs inode to which MFT record add attribute - * @type: type of the new attribute extent - * @name: name of the new attribute extent - * @name_len: name length of the new attribute extent - * @lowest_vcn: lowest vcn of the new attribute extent - * @dataruns_size: dataruns size of the new attribute extent - * @flags: flags of the new attribute extent - * - * Return offset to attribute from the beginning of the mft record on success - * and -1 on error. On error the error code is stored in errno. - * Possible error codes are: - * EINVAL - Invalid arguments passed to function. - * EEXIST - Attribute of such type, with same lowest vcn and with same - * name already exists. - * EIO - I/O error occurred or damaged filesystem. - */ -int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size, - ATTR_FLAGS flags) -{ - ntfs_attr_search_ctx *ctx; - u32 length; - ATTR_RECORD *a; - MFT_RECORD *m; - ntfs_inode *base_ni; - int err, offset; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld, " - "dataruns_size %d, flags 0x%x.\n", - (long long) ni->mft_no, (unsigned) type, - (long long) lowest_vcn, dataruns_size, (unsigned) flags); - - if (!ni || dataruns_size <= 0 || (!name && name_len)) { - errno = EINVAL; - return -1; - } - - if (ntfs_attr_can_be_non_resident(ni->vol, type)) { - err = errno; - if (errno == EPERM) - ntfs_log_trace("Attribute can't be non resident.\n"); - else - ntfs_log_trace("ntfs_attr_can_be_non_resident failed.\n"); - errno = err; - return -1; - } - - /* Locate place where record should be. */ - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) - return -1; - /* - * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for - * attribute in @ni->mrec, not any extent inode in case if @ni is base - * file record. - */ - if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, NULL, 0, - ctx)) { - err = EEXIST; - ntfs_log_trace("Attribute already present.\n"); - goto put_err_out; - } - if (errno != ENOENT) { - err = EIO; - goto put_err_out; - } - a = ctx->attr; - m = ctx->mrec; - - /* Make room for attribute. */ - dataruns_size = (dataruns_size + 7) & ~7; - length = offsetof(ATTR_RECORD, compressed_size) + ((sizeof(ntfschar) * - name_len + 7) & ~7) + dataruns_size + - ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? - sizeof(a->compressed_size) : 0); - if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) { - err = errno; - ntfs_log_trace("Failed to make room for attribute.\n"); - goto put_err_out; - } - - /* Setup record fields. */ - a->type = type; - a->length = cpu_to_le32(length); - a->non_resident = 1; - a->name_length = name_len; - a->name_offset = cpu_to_le16(offsetof(ATTR_RECORD, compressed_size) + - ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? - sizeof(a->compressed_size) : 0)); - a->flags = flags; - a->instance = m->next_attr_instance; - a->lowest_vcn = cpu_to_sle64(lowest_vcn); - a->mapping_pairs_offset = cpu_to_le16(length - dataruns_size); - a->compression_unit = (flags & ATTR_IS_COMPRESSED) ? 4 : 0; - /* If @lowest_vcn == 0, than setup empty attribute. */ - if (!lowest_vcn) { - a->highest_vcn = cpu_to_sle64(-1); - a->allocated_size = 0; - a->data_size = 0; - a->initialized_size = 0; - /* Set empty mapping pairs. */ - *((u8*)a + le16_to_cpu(a->mapping_pairs_offset)) = 0; - } - if (name_len) - memcpy((u8*)a + le16_to_cpu(a->name_offset), - name, sizeof(ntfschar) * name_len); - m->next_attr_instance = - cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff); - if (ni->nr_extents == -1) - base_ni = ni->base_ni; - else - base_ni = ni; - if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { - if (ntfs_attrlist_entry_add(ni, a)) { - err = errno; - ntfs_attr_record_resize(m, a, 0); - ntfs_log_trace("Failed add attribute entry to " - "ATTRIBUTE_LIST.\n"); - goto put_err_out; - } - } - ntfs_inode_mark_dirty(ni); - /* - * Locate offset from start of the MFT record where new attribute is - * placed. We need relookup it, because record maybe moved during - * update of attribute list. - */ - ntfs_attr_reinit_search_ctx(ctx); - if (ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, - lowest_vcn, NULL, 0, ctx)) { - err = errno; - ntfs_log_trace("Attribute lookup failed. Probably leaving inconstant " - "metadata.\n"); - ntfs_attr_put_search_ctx(ctx); - errno = err; - return -1; - - } - offset = (u8*)ctx->attr - (u8*)ctx->mrec; - ntfs_attr_put_search_ctx(ctx); - return offset; -put_err_out: - ntfs_attr_put_search_ctx(ctx); - errno = err; - return -1; -} - -/** - * ntfs_attr_record_rm - remove attribute extent - * @ctx: search context describing the attribute which should be removed - * - * If this function succeed, user should reinit search context if he/she wants - * use it anymore. - * - * Return 0 on success and -1 on error. On error the error code is stored in - * errno. Possible error codes are: - * EINVAL - Invalid arguments passed to function. - * EIO - I/O error occurred or damaged filesystem. - */ -int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx) -{ - ntfs_inode *base_ni, *ni; - ATTR_TYPES type; - int err; - - if (!ctx || !ctx->ntfs_ino || !ctx->mrec || !ctx->attr) { - errno = EINVAL; - return -1; - } - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", - (long long) ctx->ntfs_ino->mft_no, - (unsigned) le32_to_cpu(ctx->attr->type)); - type = ctx->attr->type; - ni = ctx->ntfs_ino; - if (ctx->base_ntfs_ino) - base_ni = ctx->base_ntfs_ino; - else - base_ni = ctx->ntfs_ino; - - /* Remove attribute itself. */ - if (ntfs_attr_record_resize(ctx->mrec, ctx->attr, 0)) { - ntfs_log_trace("Couldn't remove attribute record. Bug or damaged MFT " - "record.\n"); - if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) - if (ntfs_attrlist_entry_add(ni, ctx->attr)) - ntfs_log_trace("Rollback failed. Leaving inconstant " - "metadata.\n"); - err = EIO; - return -1; - } - ntfs_inode_mark_dirty(ni); - - /* - * Remove record from $ATTRIBUTE_LIST if present and we don't want - * delete $ATTRIBUTE_LIST itself. - */ - if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) { - if (ntfs_attrlist_entry_rm(ctx)) { - err = errno; - ntfs_log_trace("Couldn't delete record from " - "$ATTRIBUTE_LIST.\n"); - errno = err; - return -1; - } - } - - /* Post $ATTRIBUTE_LIST delete setup. */ - if (type == AT_ATTRIBUTE_LIST) { - if (NInoAttrList(base_ni) && base_ni->attr_list) - free(base_ni->attr_list); - base_ni->attr_list = NULL; - NInoClearAttrList(base_ni); - NInoAttrListClearDirty(base_ni); - } - - /* Free MFT record, if it isn't contain attributes. */ - if (le32_to_cpu(ctx->mrec->bytes_in_use) - - le16_to_cpu(ctx->mrec->attrs_offset) == 8) { - if (ntfs_mft_record_free(ni->vol, ni)) { - // FIXME: We need rollback here. - ntfs_log_trace("Couldn't free MFT record.\n"); - errno = EIO; - return -1; - } - /* Remove done if we freed base inode. */ - if (ni == base_ni) - return 0; - } - - if (type == AT_ATTRIBUTE_LIST || !NInoAttrList(base_ni)) - return 0; - - /* Remove attribute list if we don't need it any more. */ - if (!ntfs_attrlist_need(base_ni)) { - ntfs_attr_reinit_search_ctx(ctx); - if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, CASE_SENSITIVE, - 0, NULL, 0, ctx)) { - /* - * FIXME: Should we succeed here? Definitely something - * goes wrong because NInoAttrList(base_ni) returned - * that we have got attribute list. - */ - ntfs_log_trace("Couldn't find attribute list. Succeed " - "anyway.\n"); - return 0; - } - /* Deallocate clusters. */ - if (ctx->attr->non_resident) { - runlist *al_rl; - - al_rl = ntfs_mapping_pairs_decompress(base_ni->vol, - ctx->attr, NULL); - if (!al_rl) { - ntfs_log_trace("Couldn't decompress attribute list " - "runlist. Succeed anyway.\n"); - return 0; - } - if (ntfs_cluster_free_from_rl(base_ni->vol, al_rl)) { - ntfs_log_trace("Leaking clusters! Run chkdsk. " - "Couldn't free clusters from " - "attribute list runlist.\n"); - } - free(al_rl); - } - /* Remove attribute record itself. */ - if (ntfs_attr_record_rm(ctx)) { - /* - * FIXME: Should we succeed here? BTW, chkdsk doesn't - * complain if it find MFT record with attribute list, - * but without extents. - */ - ntfs_log_trace("Couldn't remove attribute list. Succeed " - "anyway.\n"); - return 0; - } - } - return 0; -} - -/** - * ntfs_attr_add - add attribute to inode - * @ni: opened ntfs inode to which add attribute - * @type: type of the new attribute - * @name: name in unicode of the new attribute - * @name_len: name length in unicode characters of the new attribute - * @val: value of new attribute - * @size: size of the new attribute / length of @val (if specified) - * - * @val should always be specified for always resident attributes (eg. FILE_NAME - * attribute), for attributes that can become non-resident @val can be NULL - * (eg. DATA attribute). @size can be specified even if @val is NULL, in this - * case data size will be equal to @size and initialized size will be equal - * to 0. - * - * If inode haven't got enough space to add attribute, add attribute to one of - * it extents, if no extents present or no one of them have enough space, than - * allocate new extent and add attribute to it. - * - * If on one of this steps attribute list is needed but not present, than it is - * added transparently to caller. So, this function should not be called with - * @type == AT_ATTRIBUTE_LIST, if you really need to add attribute list call - * ntfs_inode_add_attrlist instead. - * - * On success return 0. On error return -1 with errno set to the error code. - */ -int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, u8 *val, s64 size) -{ - u32 attr_rec_size; - int err, i, offset; - BOOL is_resident; - ntfs_inode *attr_ni; - ntfs_attr *na; - - if (!ni || size < 0 || type == AT_ATTRIBUTE_LIST) { - ntfs_log_trace("Invalid arguments passed.\n"); - errno = EINVAL; - return -1; - } - - ntfs_log_trace("Entering for inode 0x%llx, attr %x, size %lld.\n", - (long long) ni->mft_no, type, size); - - if (ni->nr_extents == -1) - ni = ni->base_ni; - - /* Check the attribute type and the size. */ - if (ntfs_attr_size_bounds_check(ni->vol, type, size)) { - err = errno; - if (err == ERANGE) { - ntfs_log_trace("Size bounds check failed. Aborting...\n"); - } else if (err == ENOENT) { - ntfs_log_trace("Invalid attribute type. Aborting...\n"); - err = EIO; - } - errno = err; - return -1; - } - - /* Sanity checks for always resident attributes. */ - if (ntfs_attr_can_be_non_resident(ni->vol, type)) { - if (errno != EPERM) { - err = errno; - ntfs_log_trace("ntfs_attr_can_be_non_resident failed.\n"); - goto err_out; - } - /* @val is mandatory. */ - if (!val) { - ntfs_log_trace("val is mandatory for always resident " - "attributes.\n"); - errno = EINVAL; - return -1; - } - if (size > ni->vol->mft_record_size) { - ntfs_log_trace("Attribute is too big.\n"); - errno = ERANGE; - return -1; - } - } - - /* - * Determine resident or not will be new attribute. We add 8 to size in - * non resident case for mapping pairs. - */ - if (!ntfs_attr_can_be_resident(ni->vol, type)) { - /* Attribute can be resident. */ - is_resident = TRUE; - /* Check if it is better to make attribute non resident. */ - if (!ntfs_attr_can_be_non_resident(ni->vol, type) && - offsetof(ATTR_RECORD, resident_end) + size >= - offsetof(ATTR_RECORD, non_resident_end) + 8) - /* Make it non resident. */ - is_resident = FALSE; - } else { - if (errno != EPERM) { - err = errno; - ntfs_log_trace("ntfs_attr_can_be_resident failed.\n"); - goto err_out; - } - /* Attribute can't be resident. */ - is_resident = FALSE; - } - /* Calculate attribute record size. */ - if (is_resident) - attr_rec_size = offsetof(ATTR_RECORD, resident_end) + - ((name_len * sizeof(ntfschar) + 7) & ~7) + - ((size + 7) & ~7); - else - attr_rec_size = offsetof(ATTR_RECORD, non_resident_end) + - ((name_len * sizeof(ntfschar) + 7) & ~7) + 8; - - /* - * If we have enough free space for the new attribute in the base MFT - * record, then add attribute to it. - */ - if (le32_to_cpu(ni->mrec->bytes_allocated) - - le32_to_cpu(ni->mrec->bytes_in_use) >= attr_rec_size) { - attr_ni = ni; - goto add_attr_record; - } - - /* Try to add to extent inodes. */ - if (ntfs_inode_attach_all_extents(ni)) { - err = errno; - ntfs_log_trace("Failed to attach all extents to inode.\n"); - goto err_out; - } - for (i = 0; i < ni->nr_extents; i++) { - attr_ni = ni->extent_nis[i]; - if (le32_to_cpu(attr_ni->mrec->bytes_allocated) - - le32_to_cpu(attr_ni->mrec->bytes_in_use) >= - attr_rec_size) - goto add_attr_record; - } - - /* There is no extent that contain enough space for new attribute. */ - if (!NInoAttrList(ni)) { - /* Add attribute list not present, add it and retry. */ - if (ntfs_inode_add_attrlist(ni)) { - err = errno; - ntfs_log_trace("Failed to add attribute list.\n"); - goto err_out; - } - return ntfs_attr_add(ni, type, name, name_len, val, size); - } - /* Allocate new extent. */ - attr_ni = ntfs_mft_record_alloc(ni->vol, ni); - if (!attr_ni) { - err = errno; - ntfs_log_trace("Failed to allocate extent record.\n"); - goto err_out; - } - -add_attr_record: - if (is_resident) { - /* Add resident attribute. */ - offset = ntfs_resident_attr_record_add(attr_ni, type, name, - name_len, val, size, 0); - if (offset < 0) { - err = errno; - ntfs_log_trace("Failed to add resident attribute.\n"); - goto free_err_out; - } - return 0; - } - - /* Add non resident attribute. */ - offset = ntfs_non_resident_attr_record_add(attr_ni, type, name, - name_len, 0, 8, 0); - if (offset < 0) { - err = errno; - ntfs_log_trace("Failed to add non resident attribute.\n"); - goto free_err_out; - } - - /* If @size == 0, we are done. */ - if (!size) - return 0; - - /* Open new attribute and resize it. */ - na = ntfs_attr_open(ni, type, name, name_len); - if (!na) { - err = errno; - ntfs_log_trace("Failed to open just added attribute.\n"); - goto rm_attr_err_out; - } - /* Resize and set attribute value. */ - if (ntfs_attr_truncate(na, size) || - (val && (ntfs_attr_pwrite(na, 0, size, val) != size))) { - err = errno; - ntfs_log_trace("Failed to initialize just added attribute.\n"); - if (ntfs_attr_rm(na)) { - ntfs_log_trace("Failed to remove just added attribute. " - "Probably leaving inconstant metadata.\n"); - ntfs_attr_close(na); - } - goto err_out; - } - ntfs_attr_close(na); - /* Done !*/ - return 0; - -rm_attr_err_out: - /* Remove just added attribute. */ - if (ntfs_attr_record_resize(attr_ni->mrec, - (ATTR_RECORD*)((u8*)attr_ni->mrec + offset), 0)) { - ntfs_log_trace("Failed to remove just added attribute.\n"); - } -free_err_out: - /* Free MFT record, if it isn't contain attributes. */ - if (le32_to_cpu(attr_ni->mrec->bytes_in_use) - - le32_to_cpu(attr_ni->mrec->attrs_offset) == 8) { - if (ntfs_mft_record_free(attr_ni->vol, attr_ni)) { - ntfs_log_trace("Failed to free MFT record. Leaving " - "inconstant metadata.\n"); - } - } -err_out: - errno = err; - return -1; -} - -/** - * ntfs_attr_rm - remove attribute from ntfs inode - * @na: opened ntfs attribute to delete - * - * Remove attribute and all it's extents from ntfs inode. If attribute was non - * resident also free all clusters allocated by attribute. - * - * Return 0 on success or -1 on error with errno set to the error code. - */ -int ntfs_attr_rm(ntfs_attr *na) -{ - ntfs_attr_search_ctx *ctx; - int ret = 0; - - if (!na) { - ntfs_log_trace("Invalid arguments passed.\n"); - errno = EINVAL; - return -1; - } - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", - (long long) na->ni->mft_no, na->type); - - /* Free cluster allocation. */ - if (NAttrNonResident(na)) { - if (ntfs_attr_map_whole_runlist(na)) - return -1; - if (ntfs_cluster_free(na->ni->vol, na, 0, -1) < 0) { - ntfs_log_trace("Failed to free cluster allocation. Leaving " - "inconstant metadata.\n"); - ret = -1; - } - } - - /* Search for attribute extents and remove them all. */ - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - return -1; - while (!ntfs_attr_lookup(na->type, na->name, na->name_len, - CASE_SENSITIVE, 0, NULL, 0, ctx)) { - if (ntfs_attr_record_rm(ctx)) { - ntfs_log_trace("Failed to remove attribute extent. Leaving " - "inconstant metadata.\n"); - ret = -1; - } - ntfs_attr_reinit_search_ctx(ctx); - } - if (errno != ENOENT) { - ntfs_log_trace("Attribute lookup failed. Probably leaving inconstant " - "metadata.\n"); - ret = -1; - } - - /* Throw away now non-exist attribute. */ - ntfs_attr_close(na); - /* Done. */ - return ret; -} - -/** - * ntfs_attr_record_resize - resize an attribute record - * @m: mft record containing attribute record - * @a: attribute record to resize - * @new_size: new size in bytes to which to resize the attribute record @a - * - * Resize the attribute record @a, i.e. the resident part of the attribute, in - * the mft record @m to @new_size bytes. - * - * Return 0 on success and -1 on error with errno set to the error code. - * The following error codes are defined: - * ENOSPC - Not enough space in the mft record @m to perform the resize. - * Note that on error no modifications have been performed whatsoever. - * - * Warning: If you make a record smaller without having copied all the data you - * are interested in the data may be overwritten! - */ -int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) -{ - ntfs_log_trace("Entering for new_size %u.\n", (unsigned) new_size); - /* Align to 8 bytes, just in case the caller hasn't. */ - new_size = (new_size + 7) & ~7; - /* If the actual attribute length has changed, move things around. */ - if (new_size != le32_to_cpu(a->length)) { - u32 new_muse = le32_to_cpu(m->bytes_in_use) - - le32_to_cpu(a->length) + new_size; - /* Not enough space in this mft record. */ - if (new_muse > le32_to_cpu(m->bytes_allocated)) { - errno = ENOSPC; - return -1; - } - /* Move attributes following @a to their new location. */ - memmove((u8*)a + new_size, (u8*)a + le32_to_cpu(a->length), - le32_to_cpu(m->bytes_in_use) - ((u8*)a - - (u8*)m) - le32_to_cpu(a->length)); - /* Adjust @m to reflect the change in used space. */ - m->bytes_in_use = cpu_to_le32(new_muse); - /* Adjust @a to reflect the new size. */ - if (new_size >= offsetof(ATTR_REC, length) + sizeof(a->length)) - a->length = cpu_to_le32(new_size); - } - return 0; -} - -/** - * ntfs_resident_attr_value_resize - resize the value of a resident attribute - * @m: mft record containing attribute record - * @a: attribute record whose value to resize - * @new_size: new size in bytes to which to resize the attribute value of @a - * - * Resize the value of the attribute @a in the mft record @m to @new_size bytes. - * If the value is made bigger, the newly "allocated" space is cleared. - * - * Return 0 on success and -1 on error with errno set to the error code. - * The following error codes are defined: - * ENOSPC - Not enough space in the mft record @m to perform the resize. - * Note that on error no modifications have been performed whatsoever. - */ -int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, - const u32 new_size) -{ - ntfs_log_trace("Entering for new size %u.\n", (unsigned)new_size); - - /* - * Check that the attribute name hasn't been placed after the - * attribute value. Chkdsk treat this as corruption. - */ - if (a->name_length && le16_to_cpu(a->name_offset) >= - le16_to_cpu(a->value_offset)) { - ntfs_log_trace("Eeek! Name is placed after the attribute value. " - "Corrupted inode. Run chkdsk. Aborting...\n"); - errno = EIO; - return -1; - } - /* Resize the resident part of the attribute record. */ - if (ntfs_attr_record_resize(m, a, (le16_to_cpu(a->value_offset) + - new_size + 7) & ~7) < 0) { - if (errno != ENOSPC) { - int eo = errno; - ntfs_log_trace("Eeek! Attribute record resize failed. " - "Aborting...\n"); - errno = eo; - } - return -1; - } - /* - * If we made the attribute value bigger, clear the area between the - * old size and @new_size. - */ - if (new_size > le32_to_cpu(a->value_length)) - memset((u8*)a + le16_to_cpu(a->value_offset) + - le32_to_cpu(a->value_length), 0, new_size - - le32_to_cpu(a->value_length)); - /* Finally update the length of the attribute value. */ - a->value_length = cpu_to_le32(new_size); - return 0; -} - -/** - * ntfs_attr_record_move_to - move attribute record to target inode - * @ctx: attribute search context describing the attribute record - * @ni: opened ntfs inode to which move attribute record - * - * If this function succeed, user should reinit search context if he/she wants - * use it anymore. - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni) -{ - ntfs_attr_search_ctx *nctx; - ATTR_RECORD *a; - int err; - - if (!ctx || !ctx->attr || !ctx->ntfs_ino || !ni) { - ntfs_log_trace("Invalid arguments passed.\n"); - errno = EINVAL; - return -1; - } - - ntfs_log_trace("Entering for ctx->attr->type 0x%x, ctx->ntfs_ino->mft_no " - "0x%llx, ni->mft_no 0x%llx.\n", - (unsigned) le32_to_cpu(ctx->attr->type), - (long long) ctx->ntfs_ino->mft_no, - (long long) ni->mft_no); - - if (ctx->ntfs_ino == ni) - return 0; - - if (!ctx->al_entry) { - ntfs_log_trace("Inode should contain attribute list to use this " - "function.\n"); - errno = EINVAL; - return -1; - } - - /* Find place in MFT record where attribute will be moved. */ - a = ctx->attr; - nctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!nctx) { - err = errno; - ntfs_log_trace("Couldn't obtain search context.\n"); - errno = err; - return -1; - } - /* - * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for - * attribute in @ni->mrec, not any extent inode in case if @ni is base - * file record. - */ - if (!ntfs_attr_find(a->type, (ntfschar*)((u8*)a + le16_to_cpu( - a->name_offset)), a->name_length, CASE_SENSITIVE, NULL, - 0, nctx)) { - ntfs_log_trace("Attribute of such type, with same name already " - "present in this MFT record.\n"); - err = EEXIST; - goto put_err_out; - } - if (errno != ENOENT) { - err = errno; - ntfs_log_debug("Attribute lookup failed.\n"); - goto put_err_out; - } - - /* Make space and move attribute. */ - if (ntfs_make_room_for_attr(ni->mrec, (u8*) nctx->attr, - le32_to_cpu(a->length))) { - err = errno; - ntfs_log_trace("Couldn't make space for attribute.\n"); - goto put_err_out; - } - memcpy(nctx->attr, a, le32_to_cpu(a->length)); - nctx->attr->instance = nctx->mrec->next_attr_instance; - nctx->mrec->next_attr_instance = cpu_to_le16( - (le16_to_cpu(nctx->mrec->next_attr_instance) + 1) & 0xffff); - ntfs_attr_record_resize(ctx->mrec, a, 0); - ntfs_inode_mark_dirty(ctx->ntfs_ino); - ntfs_inode_mark_dirty(ni); - - /* Update attribute list. */ - ctx->al_entry->mft_reference = - MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); - ctx->al_entry->instance = nctx->attr->instance; - ntfs_attrlist_mark_dirty(ni); - - ntfs_attr_put_search_ctx(nctx); - return 0; -put_err_out: - ntfs_attr_put_search_ctx(nctx); - errno = err; - return -1; -} - -/** - * ntfs_attr_record_move_away - move away attribute record from it's mft record - * @ctx: attribute search context describing the attribute record - * @extra: minimum amount of free space in the new holder of record - * - * New attribute record holder must have free @extra bytes after moving - * attribute record to it. - * - * If this function succeed, user should reinit search context if he/she wants - * use it anymore. - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra) -{ - ntfs_inode *base_ni, *ni; - MFT_RECORD *m; - int i, err; - - if (!ctx || !ctx->attr || !ctx->ntfs_ino || extra < 0) { - ntfs_log_trace("Invalid arguments passed.\n"); - errno = EINVAL; - return -1; - } - - ntfs_log_trace("Entering for attr 0x%x, inode 0x%llx.\n", - (unsigned) le32_to_cpu(ctx->attr->type), - (long long) ctx->ntfs_ino->mft_no); - - if (ctx->ntfs_ino->nr_extents == -1) - base_ni = ctx->base_ntfs_ino; - else - base_ni = ctx->ntfs_ino; - - if (!NInoAttrList(base_ni)) { - ntfs_log_trace("Inode should contain attribute list to use this " - "function.\n"); - errno = EINVAL; - return -1; - } - - if (ntfs_inode_attach_all_extents(ctx->ntfs_ino)) { - err = errno; - ntfs_log_trace("Couldn't attach extent inode.\n"); - errno = err; - return -1; - } - - /* Walk through all extents and try to move attribute to them. */ - for (i = 0; i < base_ni->nr_extents; i++) { - ni = base_ni->extent_nis[i]; - m = ni->mrec; - - if (ctx->ntfs_ino->mft_no == ni->mft_no) - continue; - - if (le32_to_cpu(m->bytes_allocated) - - le32_to_cpu(m->bytes_in_use) < - le32_to_cpu(ctx->attr->length) + extra) - continue; - - /* - * ntfs_attr_record_move_to can fail if extent with other lowest - * VCN already present in inode we trying move record to. So, - * do not return error. - */ - if (!ntfs_attr_record_move_to(ctx, ni)) - return 0; - } - - /* - * Failed to move attribute to one of the current extents, so allocate - * new extent and move attribute to it. - */ - ni = ntfs_mft_record_alloc(base_ni->vol, base_ni); - if (!ni) { - err = errno; - ntfs_log_trace("Couldn't allocate new MFT record.\n"); - errno = err; - return -1; - } - if (ntfs_attr_record_move_to(ctx, ni)) { - err = errno; - ntfs_log_trace("Couldn't move attribute to new MFT record.\n"); - errno = err; - return -1; - } - return 0; -} - -/** - * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute - * @na: open ntfs attribute to make non-resident - * @ctx: ntfs search context describing the attribute - * - * Convert a resident ntfs attribute to a non-resident one. - * - * Return 0 on success and -1 on error with errno set to the error code. The - * following error codes are defined: - * EPERM - The attribute is not allowed to be non-resident. - * TODO: others... - * - * NOTE to self: No changes in the attribute list are required to move from - * a resident to a non-resident attribute. - * - * Warning: We do not set the inode dirty and we do not write out anything! - * We expect the caller to do this as this is a fairly low level - * function and it is likely there will be further changes made. - */ -static int ntfs_attr_make_non_resident(ntfs_attr *na, - ntfs_attr_search_ctx *ctx) -{ - s64 new_allocated_size, bw; - ntfs_volume *vol = na->ni->vol; - ATTR_REC *a = ctx->attr; - runlist *rl; - int mp_size, mp_ofs, name_ofs, arec_size, err; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long - long)na->ni->mft_no, na->type); - - /* Some preliminary sanity checking. */ - if (NAttrNonResident(na)) { - ntfs_log_trace("Eeek! Trying to make non-resident attribute " - "non-resident. Aborting...\n"); - errno = EINVAL; - return -1; - } - - /* Check that the attribute is allowed to be non-resident. */ - if (ntfs_attr_can_be_non_resident(vol, na->type)) - return -1; - - /* - * Check that the attribute name hasn't been placed after the - * attribute value. Chkdsk treat this as corruption. - */ - if (a->name_length && le16_to_cpu(a->name_offset) >= - le16_to_cpu(a->value_offset)) { - ntfs_log_trace("Eeek! Name is placed after the attribute value. " - "Corrupted inode. Run chkdsk. Aborting...\n"); - errno = EIO; - return -1; - } - - new_allocated_size = (le32_to_cpu(a->value_length) + vol->cluster_size - - 1) & ~(vol->cluster_size - 1); - - if (new_allocated_size > 0) { - /* Start by allocating clusters to hold the attribute value. */ - rl = ntfs_cluster_alloc(vol, 0, new_allocated_size >> - vol->cluster_size_bits, -1, DATA_ZONE); - if (!rl) { - if (errno != ENOSPC) { - int eo = errno; - - ntfs_log_trace("Eeek! Failed to allocate " - "cluster(s). Aborting...\n"); - errno = eo; - } - return -1; - } - } else - rl = NULL; - /* - * Setup the in-memory attribute structure to be non-resident so that - * we can use ntfs_attr_pwrite(). - */ - NAttrSetNonResident(na); - na->rl = rl; - na->allocated_size = new_allocated_size; - na->data_size = na->initialized_size = le32_to_cpu(a->value_length); - /* - * FIXME: For now just clear all of these as we don't support them when - * writing. - */ - NAttrClearCompressed(na); - NAttrClearSparse(na); - NAttrClearEncrypted(na); - - if (rl) { - /* Now copy the attribute value to the allocated cluster(s). */ - bw = ntfs_attr_pwrite(na, 0, le32_to_cpu(a->value_length), - (u8*)a + le16_to_cpu(a->value_offset)); - if (bw != le32_to_cpu(a->value_length)) { - err = errno; - ntfs_log_debug("Eeek! Failed to write out attribute value " - "(bw = %lli, errno = %i). " - "Aborting...\n", (long long)bw, err); - if (bw >= 0) - err = EIO; - goto cluster_free_err_out; - } - } - /* Determine the size of the mapping pairs array. */ - mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0); - if (mp_size < 0) { - err = errno; - ntfs_log_debug("Eeek! Failed to get size for mapping pairs array. " - "Aborting...\n"); - goto cluster_free_err_out; - } - /* Calculate new offsets for the name and the mapping pairs array. */ - name_ofs = (sizeof(ATTR_REC) - sizeof(a->compressed_size) + 7) & ~7; - mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7; - /* - * Determine the size of the resident part of the non-resident - * attribute record. (Not compressed thus no compressed_size element - * present.) - */ - arec_size = (mp_ofs + mp_size + 7) & ~7; - - /* Resize the resident part of the attribute record. */ - if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) { - err = errno; - if (err != ENOSPC) { - ntfs_log_trace("Eeek! Failed to resize attribute record. " - "Aborting...\n"); - } - goto cluster_free_err_out; - } - - /* - * Convert the resident part of the attribute record to describe a - * non-resident attribute. - */ - a->non_resident = 1; - - /* Move the attribute name if it exists and update the offset. */ - if (a->name_length) - memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), - a->name_length * sizeof(ntfschar)); - a->name_offset = cpu_to_le16(name_ofs); - - /* Update the flags to match the in-memory ones. */ - a->flags &= ~(ATTR_IS_SPARSE | ATTR_IS_ENCRYPTED | - ATTR_COMPRESSION_MASK); - - /* Setup the fields specific to non-resident attributes. */ - a->lowest_vcn = cpu_to_sle64(0); - a->highest_vcn = cpu_to_sle64((new_allocated_size - 1) >> - vol->cluster_size_bits); - - a->mapping_pairs_offset = cpu_to_le16(mp_ofs); - - a->compression_unit = 0; - - memset(&a->reserved1, 0, sizeof(a->reserved1)); - - a->allocated_size = cpu_to_sle64(new_allocated_size); - a->data_size = a->initialized_size = cpu_to_sle64(na->data_size); - - /* Generate the mapping pairs array in the attribute record. */ - if (ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs, arec_size - mp_ofs, - rl, 0, NULL) < 0) { - err = errno; - // FIXME: Eeek! We need rollback! (AIA) - ntfs_log_trace("Eeek! Failed to build mapping pairs. Leaving " - "corrupt attribute record on disk. In memory " - "runlist is still intact! Error code is %i. " - "FIXME: Need to rollback instead!\n", err); - errno = err; - return -1; - } - - /* Done! */ - return 0; - -cluster_free_err_out: - if (rl && ntfs_cluster_free(vol, na, 0, -1) < 0) - ntfs_log_trace("Eeek! Failed to release allocated clusters in error " - "code path. Leaving inconsistent metadata...\n"); - NAttrClearNonResident(na); - na->allocated_size = na->data_size; - na->rl = NULL; - free(rl); - errno = err; - return -1; -} - -/** - * ntfs_resident_attr_resize - resize a resident, open ntfs attribute - * @na: resident ntfs attribute to resize - * @newsize: new size (in bytes) to which to resize the attribute - * - * Change the size of a resident, open ntfs attribute @na to @newsize bytes. - * - * On success return 0 and on error return -1 with errno set to the error code. - * The following error codes are defined: - * ENOMEM - Not enough memory to complete operation. - * ERANGE - @newsize is not valid for the attribute type of @na. - * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. - */ -static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) -{ - ntfs_attr_search_ctx *ctx; - ntfs_volume *vol; - ntfs_inode *ni; - int err; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long - long)na->ni->mft_no, na->type); - - /* Get the attribute record that needs modification. */ - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - return -1; - if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 0, NULL, 0, - ctx)) { - err = errno; - goto put_err_out; - } - vol = na->ni->vol; - /* - * Check the attribute type and the corresponding minimum and maximum - * sizes against @newsize and fail if @newsize is out of bounds. - */ - if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { - err = errno; - if (err == ERANGE) { - ntfs_log_trace("Eeek! Size bounds check failed. " - "Aborting...\n"); - } else if (err == ENOENT) - err = EIO; - goto put_err_out; - } - /* - * If @newsize is bigger than the mft record we need to make the - * attribute non-resident if the attribute type supports it. If it is - * smaller we can go ahead and attempt the resize. - */ - if (newsize < vol->mft_record_size) { - /* Perform the resize of the attribute record. */ - if (!ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, - newsize)) { - /* Update attribute size everywhere. */ - na->data_size = na->initialized_size = newsize; - na->allocated_size = (newsize + 7) & ~7; - if (NAttrCompressed(na) || NAttrSparse(na)) - na->compressed_size = na->allocated_size; - if (na->type == AT_DATA && na->name == AT_UNNAMED) { - na->ni->data_size = na->data_size; - na->ni->allocated_size = na->allocated_size; - NInoFileNameSetDirty(na->ni); - } - goto resize_done; - } - /* Error! If not enough space, just continue. */ - if (errno != ENOSPC) { - err = errno; - ntfs_log_trace("Eeek! Failed to resize resident part of " - "attribute. Aborting...\n"); - goto put_err_out; - } - } - /* There is not enough space in the mft record to perform the resize. */ - - /* Make the attribute non-resident if possible. */ - if (!ntfs_attr_make_non_resident(na, ctx)) { - ntfs_inode_mark_dirty(ctx->ntfs_ino); - ntfs_attr_put_search_ctx(ctx); - /* Resize non-resident attribute */ - return ntfs_attr_truncate(na, newsize); - } else if (errno != ENOSPC && errno != EPERM) { - err = errno; - ntfs_log_trace("Eeek! Failed to make attribute non-resident. " - "Aborting...\n"); - goto put_err_out; - } - - /* Try to make other attributes non-resident and retry each time. */ - ntfs_attr_init_search_ctx(ctx, NULL, na->ni->mrec); - while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { - ntfs_attr *tna; - ATTR_RECORD *a; - - a = ctx->attr; - if (a->non_resident) - continue; - - /* - * Check out whether convert is reasonable. Assume that mapping - * pairs will take 8 bytes. - */ - if (le32_to_cpu(a->length) <= offsetof(ATTR_RECORD, - compressed_size) + ((a->name_length * - sizeof(ntfschar) + 7) & ~7) + 8) - continue; - - tna = ntfs_attr_open(na->ni, a->type, (ntfschar*)((u8*)a + - le16_to_cpu(a->name_offset)), a->name_length); - if (!tna) { - err = errno; - ntfs_log_trace("Couldn't open attribute.\n"); - goto put_err_out; - } - if (ntfs_attr_make_non_resident(tna, ctx)) { - ntfs_attr_close(tna); - continue; - } - ntfs_inode_mark_dirty(tna->ni); - ntfs_attr_close(tna); - ntfs_attr_put_search_ctx(ctx); - return ntfs_resident_attr_resize(na, newsize); - } - /* Check whether error occurred. */ - if (errno != ENOENT) { - err = errno; - ntfs_log_trace("Attribute lookup failed.\n"); - goto put_err_out; - } - - /* We can't move out attribute list, thus move out others. */ - if (na->type == AT_ATTRIBUTE_LIST) { - ntfs_attr_put_search_ctx(ctx); - if (ntfs_inode_free_space(na->ni, offsetof(ATTR_RECORD, - non_resident_end) + 8)) { - err = errno; - ntfs_log_trace("Couldn't free space in the MFT record to " - "make attribute list non resident.\n"); - errno = err; - return -1; - } - return ntfs_resident_attr_resize(na, newsize); - } - - /* - * Move the attribute to a new mft record, creating an attribute list - * attribute or modifying it if it is already present. - */ - - /* Point search context back to attribute which we need resize. */ - ntfs_attr_init_search_ctx(ctx, na->ni, NULL); - if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, - 0, NULL, 0, ctx)) { - ntfs_log_trace("Attribute lookup failed.\n"); - err = errno; - goto put_err_out; - } - - /* - * Check whether attribute is already single in the this MFT record. - * 8 added for the attribute terminator. - */ - if (le32_to_cpu(ctx->mrec->bytes_in_use) == - le16_to_cpu(ctx->mrec->attrs_offset) + - le32_to_cpu(ctx->attr->length) + 8) { - err = ENOSPC; - goto put_err_out; - } - - /* Add attribute list if not present. */ - if (na->ni->nr_extents == -1) - ni = na->ni->base_ni; - else - ni = na->ni; - if (!NInoAttrList(ni)) { - ntfs_attr_put_search_ctx(ctx); - if (ntfs_inode_add_attrlist(ni)) - return -1; - return ntfs_resident_attr_resize(na, newsize); - } - /* Allocate new mft record. */ - ni = ntfs_mft_record_alloc(vol, ni); - if (!ni) { - err = errno; - ntfs_log_trace("Couldn't allocate new MFT record.\n"); - goto put_err_out; - } - /* Move attribute to it. */ - if (ntfs_attr_record_move_to(ctx, ni)) { - err = errno; - ntfs_log_trace("Couldn't move attribute to new MFT record.\n"); - goto put_err_out; - } - /* Update ntfs attribute. */ - if (na->ni->nr_extents == -1) - na->ni = ni; - - ntfs_attr_put_search_ctx(ctx); - /* Try to perform resize once again. */ - return ntfs_resident_attr_resize(na, newsize); - -resize_done: - /* - * Set the inode (and its base inode if it exists) dirty so it is - * written out later. - */ - ntfs_inode_mark_dirty(ctx->ntfs_ino); - /* Done! */ - ntfs_attr_put_search_ctx(ctx); - return 0; -put_err_out: - ntfs_attr_put_search_ctx(ctx); - errno = err; - return -1; -} - -/** - * ntfs_attr_make_resident - convert a non-resident to a resident attribute - * @na: open ntfs attribute to make resident - * @ctx: ntfs search context describing the attribute - * - * Convert a non-resident ntfs attribute to a resident one. - * - * Return 0 on success and -1 on error with errno set to the error code. The - * following error codes are defined: - * EINVAL - Invalid arguments passed. - * EPERM - The attribute is not allowed to be resident. - * EIO - I/O error, damaged inode or bug. - * ENOSPC - There is no enough space to perform conversion. - * EOPNOTSUPP - Requested conversion is not supported yet. - * - * Warning: We do not set the inode dirty and we do not write out anything! - * We expect the caller to do this as this is a fairly low level - * function and it is likely there will be further changes made. - */ -static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) -{ - ntfs_volume *vol = na->ni->vol; - ATTR_REC *a = ctx->attr; - int name_ofs, val_ofs, err = EIO; - s64 arec_size, bytes_read; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long - long)na->ni->mft_no, na->type); - - /* Should be called for the first extent of the attribute. */ - if (sle64_to_cpu(a->lowest_vcn)) { - ntfs_log_trace("Eeek! Should be called for the first extent of the " - "attribute. Aborting...\n"); - err = EINVAL; - return -1; - } - - /* Some preliminary sanity checking. */ - if (!NAttrNonResident(na)) { - ntfs_log_trace("Eeek! Trying to make resident attribute resident. " - "Aborting...\n"); - errno = EINVAL; - return -1; - } - - /* Make sure this is not $MFT/$BITMAP or Windows will not boot! */ - if (na->type == AT_BITMAP && na->ni->mft_no == FILE_MFT) { - errno = EPERM; - return -1; - } - - /* Check that the attribute is allowed to be resident. */ - if (ntfs_attr_can_be_resident(vol, na->type)) - return -1; - - /* - * Check that the attribute name hasn't been placed after the - * mapping pairs array. Chkdsk treat this as corruption. - */ - if (a->name_length && le16_to_cpu(a->name_offset) >= - le16_to_cpu(a->mapping_pairs_offset)) { - ntfs_log_trace("Eeek! Damaged attribute. Name is placed after the " - "mapping pairs array. Run chkdsk. Aborting...\n"); - errno = EIO; - return -1; - } - - if (NAttrCompressed(na) || NAttrEncrypted(na)) { - ntfs_log_trace("Making compressed or encrypted files resident is not " - "implemented yet.\n"); - errno = EOPNOTSUPP; - return -1; - } - - /* Work out offsets into and size of the resident attribute. */ - name_ofs = 24; /* = sizeof(resident_ATTR_REC); */ - val_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7; - arec_size = (val_ofs + na->data_size + 7) & ~7; - - /* Sanity check the size before we start modifying the attribute. */ - if (le32_to_cpu(ctx->mrec->bytes_in_use) - le32_to_cpu(a->length) + - arec_size > le32_to_cpu(ctx->mrec->bytes_allocated)) { - errno = ENOSPC; - return -1; - } - - /* Read and cache the whole runlist if not already done. */ - if (ntfs_attr_map_whole_runlist(na)) - return -1; - - /* Move the attribute name if it exists and update the offset. */ - if (a->name_length) { - memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), - a->name_length * sizeof(ntfschar)); - } - a->name_offset = cpu_to_le16(name_ofs); - - /* Resize the resident part of the attribute record. */ - if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) { - /* - * Bug, because ntfs_attr_record_resize should not fail (we - * already checked that attribute fits MFT record). - */ - ntfs_log_error("BUG! Failed to resize attribute record. " - "Please report to the %s. Aborting...\n", - NTFS_DEV_LIST); - errno = EIO; - return -1; - } - - /* Convert the attribute record to describe a resident attribute. */ - a->non_resident = 0; - a->flags = 0; - a->value_length = cpu_to_le32(na->data_size); - a->value_offset = cpu_to_le16(val_ofs); - /* - * File names cannot be non-resident so we would never see this here - * but at least it serves as a reminder that there may be attributes - * for which we do need to set this flag. (AIA) - */ - if (a->type == AT_FILE_NAME) - a->resident_flags = RESIDENT_ATTR_IS_INDEXED; - else - a->resident_flags = 0; - a->reservedR = 0; - - /* Sanity fixup... Shouldn't really happen. (AIA) */ - if (na->initialized_size > na->data_size) - na->initialized_size = na->data_size; - - /* Copy data from run list to resident attribute value. */ - bytes_read = ntfs_rl_pread(vol, na->rl, 0, na->initialized_size, - (u8*)a + val_ofs); - if (bytes_read != na->initialized_size) { - if (bytes_read < 0) - err = errno; - ntfs_log_trace("Eeek! Failed to read attribute data. Leaving " - "inconstant metadata. Run chkdsk. " - "Aborting...\n"); - errno = err; - return -1; - } - - /* Clear memory in gap between initialized_size and data_size. */ - if (na->initialized_size < na->data_size) - memset((u8*)a + val_ofs + na->initialized_size, 0, - na->data_size - na->initialized_size); - - /* - * Deallocate clusters from the runlist. - * - * NOTE: We can use ntfs_cluster_free() because we have already mapped - * the whole run list and thus it doesn't matter that the attribute - * record is in a transiently corrupted state at this moment in time. - */ - if (ntfs_cluster_free(vol, na, 0, -1) < 0) { - err = errno; - ntfs_log_perror("Eeek! Failed to release allocated clusters"); - ntfs_log_trace("Ignoring error and leaving behind wasted " - "clusters.\n"); - } - - /* Throw away the now unused runlist. */ - free(na->rl); - na->rl = NULL; - - /* Update in-memory struct ntfs_attr. */ - NAttrClearNonResident(na); - NAttrClearCompressed(na); - NAttrClearSparse(na); - NAttrClearEncrypted(na); - na->initialized_size = na->data_size; - na->allocated_size = na->compressed_size = (na->data_size + 7) & ~7; - na->compression_block_size = 0; - na->compression_block_size_bits = na->compression_block_clusters = 0; - return 0; -} - -#define NTFS_VCN_DELETE_MARK -2 -/** - * ntfs_attr_update_mapping_pairs - update mapping pairs for ntfs attribute - * @na: non-resident ntfs open attribute for which we need update - * @from_vcn: update runlist starting this VCN - * - * Build mapping pairs from @na->rl and write them to the disk. Also, this - * function updates sparse bit, allocated and compressed size (allocates/frees - * space for this field if required). - * - * @na->allocated_size should be set to correct value for the new runlist before - * call to this function. Vice-versa @na->compressed_size will be calculated and - * set to correct value during this function. - * - * FIXME: This function does not update sparse bit and compressed size correctly - * if called with @from_vcn != 0. - * - * FIXME: Rewrite without using NTFS_VCN_DELETE_MARK define. - * - * On success return 0 and on error return -1 with errno set to the error code. - * The following error codes are defined: - * EINVAL - Invalid arguments passed. - * ENOMEM - Not enough memory to complete operation. - * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST - * or there is no free MFT records left to allocate. - */ -int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn) -{ - ntfs_attr_search_ctx *ctx; - ntfs_inode *ni, *base_ni; - MFT_RECORD *m; - ATTR_RECORD *a; - VCN stop_vcn; - int err, mp_size, cur_max_mp_size, exp_max_mp_size; - BOOL finished_build; - -retry: - if (!na || !na->rl || from_vcn) { - ntfs_log_trace("Invalid parameters passed.\n"); - errno = EINVAL; - return -1; - } - - if (!NAttrNonResident(na)) { - ntfs_log_trace("Attribute should be non resident.\n"); - errno = EINVAL; - return -1; - } - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long - long)na->ni->mft_no, na->type); - - if (na->ni->nr_extents == -1) - base_ni = na->ni->base_ni; - else - base_ni = na->ni; - - ctx = ntfs_attr_get_search_ctx(base_ni, NULL); - if (!ctx) { - err = errno; - ntfs_log_trace("Couldn't get search context.\n"); - errno = err; - return -1; - } - - /* Fill attribute records with new mapping pairs. */ - stop_vcn = 0; - finished_build = FALSE; - while (!ntfs_attr_lookup(na->type, na->name, na->name_len, - CASE_SENSITIVE, from_vcn, NULL, 0, ctx)) { - a = ctx->attr; - m = ctx->mrec; - /* - * If runlist is updating not from the beginning, then set - * @stop_vcn properly, i.e. to the lowest vcn of record that - * contain @from_vcn. Also we do not need @from_vcn anymore, - * set it to 0 to make ntfs_attr_lookup enumerate attributes. - */ - if (from_vcn) { - LCN first_lcn; - - stop_vcn = sle64_to_cpu(a->lowest_vcn); - from_vcn = 0; - /* - * Check whether the first run we need to update is - * the last run in runlist, if so, then deallocate - * all attrubute extents starting this one. - */ - first_lcn = ntfs_rl_vcn_to_lcn(na->rl, stop_vcn); - if (first_lcn == LCN_EINVAL) { - ntfs_log_trace("BUG! Incorrect runlist.\n"); - err = EIO; - goto put_err_out; - } - if (first_lcn == LCN_ENOENT || - first_lcn == LCN_RL_NOT_MAPPED) - finished_build = TRUE; - } - - /* - * Check whether we finished mapping pairs build, if so mark - * extent as need to delete (by setting highest vcn to - * NTFS_VCN_DELETE_MARK (-2), we shall check it later and - * delete extent) and continue search. - */ - if (finished_build) { - ntfs_log_trace("Mark attr 0x%x for delete in inode " - "0x%llx.\n", (unsigned)le32_to_cpu( - a->type), ctx->ntfs_ino->mft_no); - a->highest_vcn = cpu_to_sle64(NTFS_VCN_DELETE_MARK); - ntfs_inode_mark_dirty(ctx->ntfs_ino); - continue; - } - - /* - * Check that the attribute name hasn't been placed after the - * mapping pairs array. Windows treat this as a corruption. - */ - if (a->name_length) { - if (le16_to_cpu(a->name_offset) >= - le16_to_cpu(a->mapping_pairs_offset)) { - ntfs_log_error("Damaged attribute. Name is " - "placed after the mapping " - "pairs array. Run chkdsk.\n"); - err = EIO; - goto put_err_out; - } - } - /* - * If we in the first extent, then set/clean sparse bit, - * update allocated and compressed size. - */ - if (!a->lowest_vcn) { - int sparse; - - /* Update allocated size. */ - a->allocated_size = cpu_to_sle64(na->allocated_size); - /* Update sparse bit. */ - sparse = ntfs_rl_sparse(na->rl); - if (sparse == -1) { - ntfs_log_trace("Bad runlist.\n"); - err = EIO; - goto put_err_out; - } - /* Attribute become sparse. */ - if (sparse && !(a->flags & (ATTR_IS_SPARSE | - ATTR_IS_COMPRESSED))) { - /* - * We need to move attribute to another mft - * record, if attribute is to small to add - * compressed_size field to it and we have no - * free space in the current mft record. - */ - if ((le32_to_cpu(a->length) - le16_to_cpu( - a->mapping_pairs_offset) - == 8) && !(le32_to_cpu( - m->bytes_allocated) - - le32_to_cpu(m->bytes_in_use))) { - if (!NInoAttrList(na->ni)) { - ntfs_attr_put_search_ctx(ctx); - if (ntfs_inode_add_attrlist( - na->ni)) - return -1; - goto retry; - } - if (ntfs_attr_record_move_away(ctx, - 8)) { - ntfs_log_trace("Failed to move " - "attribute to another " - "extent. Aborting..\n"); - err = errno; - goto put_err_out; - } - ntfs_attr_put_search_ctx(ctx); - goto retry; - } - if (!(le32_to_cpu(a->length) - le16_to_cpu( - a->mapping_pairs_offset))) { - ntfs_log_trace("Size of the space " - "allocated for mapping " - "pairs should not be 0." - " Aborting ...\n"); - err = EIO; - goto put_err_out; - } - NAttrSetSparse(na); - a->flags |= ATTR_IS_SPARSE; - a->compression_unit = 4; /* Windows set it so, - even if attribute - is not actually - compressed. */ - memmove((u8*)a + le16_to_cpu(a->name_offset) + - 8, (u8*)a + le16_to_cpu(a->name_offset), - a->name_length * sizeof(ntfschar)); - a->name_offset = cpu_to_le16(le16_to_cpu( - a->name_offset) + 8); - a->mapping_pairs_offset = - cpu_to_le16(le16_to_cpu( - a->mapping_pairs_offset) + 8); - } - /* Attribute no longer sparse. */ - if (!sparse && (a->flags & ATTR_IS_SPARSE) && - !(a->flags & ATTR_IS_COMPRESSED)) { - NAttrClearSparse(na); - a->flags &= ~ATTR_IS_SPARSE; - a->compression_unit = 0; - memmove((u8*)a + le16_to_cpu(a->name_offset) - - 8, (u8*)a + le16_to_cpu(a->name_offset), - a->name_length * sizeof(ntfschar)); - a->name_offset = cpu_to_le16(le16_to_cpu( - a->name_offset) - 8); - a->mapping_pairs_offset = - cpu_to_le16(le16_to_cpu( - a->mapping_pairs_offset) - 8); - } - /* Update compressed size if required. */ - if (sparse) { - s64 new_compr_size; - - new_compr_size = ntfs_rl_get_compressed_size( - na->ni->vol, na->rl); - if (new_compr_size == -1) { - err = errno; - ntfs_log_trace("BUG! Leaving inconstant" - " metadata.\n"); - goto put_err_out; - } - na->compressed_size = new_compr_size; - a->compressed_size = cpu_to_sle64( - new_compr_size); - } - /* - * Set FILE_NAME dirty flag, to update sparse bit and - * allocated size in the index. - */ - if (na->type == AT_DATA && na->name == AT_UNNAMED) { - if (sparse) - na->ni->allocated_size = - na->compressed_size; - else - na->ni->allocated_size = - na->allocated_size; - NInoFileNameSetDirty(na->ni); - } - } - /* Get the size for the rest of mapping pairs array. */ - mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, na->rl, - stop_vcn); - if (mp_size <= 0) { - err = errno; - ntfs_log_trace("Get size for mapping pairs failed.\n"); - goto put_err_out; - } - /* - * Determine maximum possible length of mapping pairs, - * if we shall *not* expand space for mapping pairs. - */ - cur_max_mp_size = le32_to_cpu(a->length) - - le16_to_cpu(a->mapping_pairs_offset); - /* - * Determine maximum possible length of mapping pairs in the - * current mft record, if we shall expand space for mapping - * pairs. - */ - exp_max_mp_size = le32_to_cpu(m->bytes_allocated) - - le32_to_cpu(m->bytes_in_use) + cur_max_mp_size; - /* Test mapping pairs for fitting in the current mft record. */ - if (mp_size > exp_max_mp_size) { - /* - * Mapping pairs of $ATTRIBUTE_LIST attribute must fit - * in the base mft record. Try to move out other - * attributes and try again. - */ - if (na->type == AT_ATTRIBUTE_LIST) { - ntfs_attr_put_search_ctx(ctx); - if (ntfs_inode_free_space(na->ni, mp_size - - cur_max_mp_size)) { - if (errno != ENOSPC) - return -1; - ntfs_log_error("Attribute list mapping " - "pairs size to big, " - "can't fit them in the " - "base MFT record. " - "Defragment volume and " - "try once again.\n"); - errno = ENOSPC; - return -1; - } - goto retry; - } - - /* Add attribute list if it isn't present, and retry. */ - if (!NInoAttrList(base_ni)) { - ntfs_attr_put_search_ctx(ctx); - if (ntfs_inode_add_attrlist(base_ni)) { - err = errno; - ntfs_log_trace("Couldn't add attribute " - "list.\n"); - errno = err; - return -1; - } - goto retry; - } - - /* - * Set mapping pairs size to maximum possible for this - * mft record. We shall write the rest of mapping pairs - * to another MFT records. - */ - mp_size = exp_max_mp_size; - } - - /* Change space for mapping pairs if we need it. */ - if (((mp_size + 7) & ~7) != cur_max_mp_size) { - if (ntfs_attr_record_resize(m, a, - le16_to_cpu(a->mapping_pairs_offset) + - mp_size)) { - ntfs_log_error("BUG! Ran out of space in mft " - "record. Please run chkdsk and " - "if that doesn't find any " - "errors please report you saw " - "this message to %s.\n", - NTFS_DEV_LIST); - err = EIO; - goto put_err_out; - } - } - - /* Update lowest vcn. */ - a->lowest_vcn = cpu_to_sle64(stop_vcn); - ntfs_inode_mark_dirty(ctx->ntfs_ino); - if ((ctx->ntfs_ino->nr_extents == -1 || - NInoAttrList(ctx->ntfs_ino)) && - ctx->attr->type != AT_ATTRIBUTE_LIST) { - ctx->al_entry->lowest_vcn = cpu_to_sle64(stop_vcn); - ntfs_attrlist_mark_dirty(ctx->ntfs_ino); - } - - /* - * Generate the new mapping pairs array directly into the - * correct destination, i.e. the attribute record itself. - */ - if (!ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + le16_to_cpu( - a->mapping_pairs_offset), mp_size, na->rl, - stop_vcn, &stop_vcn)) - finished_build = TRUE; - if (!finished_build && errno != ENOSPC) { - err = errno; - ntfs_log_error("BUG! Mapping pairs build failed. " - "Please run chkdsk and if that doesn't " - "find any errors please report you saw " - "this message to %s.\n", NTFS_DEV_LIST); - goto put_err_out; - } - a->highest_vcn = cpu_to_sle64(stop_vcn - 1); - } - /* Check whether error occurred. */ - if (errno != ENOENT) { - err = errno; - ntfs_log_trace("Attribute lookup failed.\n"); - goto put_err_out; - } - - /* Deallocate not used attribute extents and return with success. */ - if (finished_build) { - ntfs_attr_reinit_search_ctx(ctx); - ntfs_log_trace("Deallocate marked extents.\n"); - while (!ntfs_attr_lookup(na->type, na->name, na->name_len, - CASE_SENSITIVE, 0, NULL, 0, ctx)) { - if (sle64_to_cpu(ctx->attr->highest_vcn) != - NTFS_VCN_DELETE_MARK) - continue; - /* Remove unused attribute record. */ - if (ntfs_attr_record_rm(ctx)) { - err = errno; - ntfs_log_trace("Couldn't remove unused " - "attribute record.\n"); - goto put_err_out; - } - ntfs_attr_reinit_search_ctx(ctx); - } - if (errno != ENOENT) { - err = errno; - ntfs_log_trace("Attribute lookup failed.\n"); - goto put_err_out; - } - ntfs_log_trace("Deallocate done.\n"); - ntfs_attr_put_search_ctx(ctx); - return 0; - } - ntfs_attr_put_search_ctx(ctx); - ctx = NULL; - - /* Allocate new MFT records for the rest of mapping pairs. */ - while (1) { - /* Calculate size of rest mapping pairs. */ - mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, - na->rl, stop_vcn); - if (mp_size <= 0) { - err = errno; - ntfs_log_trace("Get size for mapping pairs failed.\n"); - goto put_err_out; - } - /* Allocate new mft record. */ - ni = ntfs_mft_record_alloc(na->ni->vol, base_ni); - if (!ni) { - err = errno; - ntfs_log_trace("Couldn't allocate new MFT record.\n"); - goto put_err_out; - } - m = ni->mrec; - /* - * If mapping size exceed available space, set them to - * possible maximum. - */ - cur_max_mp_size = le32_to_cpu(m->bytes_allocated) - - le32_to_cpu(m->bytes_in_use) - - (offsetof(ATTR_RECORD, compressed_size) + - ((NAttrCompressed(na) || NAttrSparse(na)) ? - sizeof(a->compressed_size) : 0)) - - ((sizeof(ntfschar) * na->name_len + 7) & ~7); - if (mp_size > cur_max_mp_size) - mp_size = cur_max_mp_size; - /* Add attribute extent to new record. */ - err = ntfs_non_resident_attr_record_add(ni, na->type, - na->name, na->name_len, stop_vcn, mp_size, 0); - if (err == -1) { - err = errno; - ntfs_log_trace("Couldn't add attribute extent into the " - "MFT record.\n"); - if (ntfs_mft_record_free(na->ni->vol, ni)) { - ntfs_log_trace("Couldn't free MFT record.\n"); - } - goto put_err_out; - } - a = (ATTR_RECORD*)((u8*)m + err); - - err = ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + - le16_to_cpu(a->mapping_pairs_offset), mp_size, na->rl, - stop_vcn, &stop_vcn); - if (err < 0 && errno != ENOSPC) { - err = errno; - ntfs_log_error("BUG! Mapping pairs build failed. " - "Please run chkdsk and if that doesn't " - "find any errors please report you saw " - "this message to %s.\n", NTFS_DEV_LIST); - if (ntfs_mft_record_free(na->ni->vol, ni)) - ntfs_log_trace("Couldn't free MFT record.\n"); - goto put_err_out; - } - a->highest_vcn = cpu_to_sle64(stop_vcn - 1); - ntfs_inode_mark_dirty(ni); - /* All mapping pairs has been written. */ - if (!err) - break; - } - return 0; -put_err_out: - if (ctx) - ntfs_attr_put_search_ctx(ctx); - errno = err; - return -1; -} -#undef NTFS_VCN_DELETE_MARK - -/** - * ntfs_non_resident_attr_shrink - shrink a non-resident, open ntfs attribute - * @na: non-resident ntfs attribute to shrink - * @newsize: new size (in bytes) to which to shrink the attribute - * - * Reduce the size of a non-resident, open ntfs attribute @na to @newsize bytes. - * - * On success return 0 and on error return -1 with errno set to the error code. - * The following error codes are defined: - * ENOMEM - Not enough memory to complete operation. - * ERANGE - @newsize is not valid for the attribute type of @na. - */ -static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) -{ - ntfs_volume *vol; - ntfs_attr_search_ctx *ctx; - VCN first_free_vcn; - s64 nr_freed_clusters; - int err; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long - long)na->ni->mft_no, na->type); - - vol = na->ni->vol; - - /* - * Check the attribute type and the corresponding minimum size - * against @newsize and fail if @newsize is too small. - */ - if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { - err = errno; - if (err == ERANGE) { - ntfs_log_trace("Eeek! Size bounds check failed. " - "Aborting...\n"); - } else if (err == ENOENT) - err = EIO; - errno = err; - return -1; - } - - /* The first cluster outside the new allocation. */ - first_free_vcn = (newsize + vol->cluster_size - 1) >> - vol->cluster_size_bits; - /* - * Compare the new allocation with the old one and only deallocate - * clusters if there is a change. - */ - if ((na->allocated_size >> vol->cluster_size_bits) != first_free_vcn) { - if (ntfs_attr_map_whole_runlist(na)) { - err = errno; - ntfs_log_trace("Eeek! ntfs_attr_map_whole_runlist " - "failed.\n"); - errno = err; - return -1; - } - /* Deallocate all clusters starting with the first free one. */ - nr_freed_clusters = ntfs_cluster_free(vol, na, first_free_vcn, - -1); - if (nr_freed_clusters < 0) { - err = errno; - ntfs_log_trace("Eeek! Freeing of clusters failed. " - "Aborting...\n"); - errno = err; - return -1; - } - - /* Truncate the runlist itself. */ - if (ntfs_rl_truncate(&na->rl, first_free_vcn)) { - err = errno; - /* - * Failed to truncate the runlist, so just throw it - * away, it will be mapped afresh on next use. - */ - free(na->rl); - na->rl = NULL; - ntfs_log_trace("Eeek! Run list truncation failed.\n"); - errno = err; - return -1; - } - - /* Prepare to mapping pairs update. */ - na->allocated_size = first_free_vcn << vol->cluster_size_bits; - /* Write mapping pairs for new runlist. */ - if (ntfs_attr_update_mapping_pairs(na, 0 /*first_free_vcn*/)) { - err = errno; - ntfs_log_trace("Eeek! Mapping pairs update failed. " - "Leaving inconstant metadata. " - "Run chkdsk.\n"); - errno = err; - return -1; - } - } - - /* Get the first attribute record. */ - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) { - err = errno; - ntfs_log_trace("Couldn't get attribute search context.\n"); - errno = err; - return -1; - } - if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, - 0, NULL, 0, ctx)) { - err = errno; - if (err == ENOENT) - err = EIO; - ntfs_log_trace("Eeek! Lookup of first attribute extent failed. " - "Leaving inconstant metadata.\n"); - goto put_err_out; - } - - /* Update data and initialized size. */ - na->data_size = newsize; - ctx->attr->data_size = cpu_to_sle64(newsize); - if (newsize < na->initialized_size) { - na->initialized_size = newsize; - ctx->attr->initialized_size = cpu_to_sle64(newsize); - } - /* Update data size in the index. */ - if (na->type == AT_DATA && na->name == AT_UNNAMED) { - na->ni->data_size = na->data_size; - NInoFileNameSetDirty(na->ni); - } - - /* If the attribute now has zero size, make it resident. */ - if (!newsize) { - if (ntfs_attr_make_resident(na, ctx)) { - /* If couldn't make resident, just continue. */ - if (errno != EPERM) - ntfs_log_error("Failed to make attribute " - "resident. Leaving as is...\n"); - } - } - - /* Set the inode dirty so it is written out later. */ - ntfs_inode_mark_dirty(ctx->ntfs_ino); - /* Done! */ - ntfs_attr_put_search_ctx(ctx); - return 0; -put_err_out: - ntfs_attr_put_search_ctx(ctx); - errno = err; - return -1; -} - -/** - * ntfs_non_resident_attr_expand - expand a non-resident, open ntfs attribute - * @na: non-resident ntfs attribute to expand - * @newsize: new size (in bytes) to which to expand the attribute - * - * Expand the size of a non-resident, open ntfs attribute @na to @newsize bytes, - * by allocating new clusters. - * - * On success return 0 and on error return -1 with errno set to the error code. - * The following error codes are defined: - * ENOMEM - Not enough memory to complete operation. - * ERANGE - @newsize is not valid for the attribute type of @na. - * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. - */ -static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) -{ - LCN lcn_seek_from; - VCN first_free_vcn; - ntfs_volume *vol; - ntfs_attr_search_ctx *ctx; - runlist *rl, *rln; - s64 org_alloc_size; - int err; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, new size %lld, " - "current size %lld.\n", - (unsigned long long)na->ni->mft_no, na->type, - (long long)newsize, (long long)na->data_size); - - vol = na->ni->vol; - - /* - * Check the attribute type and the corresponding maximum size - * against @newsize and fail if @newsize is too big. - */ - if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { - err = errno; - if (err == ERANGE) { - ntfs_log_trace("Eeek! Size bounds check failed. " - "Aborting...\n"); - } else if (err == ENOENT) - err = EIO; - errno = err; - return -1; - } - - /* Save for future use. */ - org_alloc_size = na->allocated_size; - /* The first cluster outside the new allocation. */ - first_free_vcn = (newsize + vol->cluster_size - 1) >> - vol->cluster_size_bits; - /* - * Compare the new allocation with the old one and only allocate - * clusters if there is a change. - */ - if ((na->allocated_size >> vol->cluster_size_bits) < first_free_vcn) { - if (ntfs_attr_map_whole_runlist(na)) { - err = errno; - ntfs_log_trace("Eeek! ntfs_attr_map_whole_runlist " - "failed.\n"); - errno = err; - return -1; - } - - /* - * If we extend $DATA attribute on NTFS 3+ volume, we can add - * sparse runs instead of real allocation of clusters. - */ - if (na->type == AT_DATA && vol->major_ver >= 3) { - rl = malloc(0x1000); - if (!rl) { - ntfs_log_trace("Not enough memory.\n"); - err = ENOMEM; - return -1; - } - rl[0].vcn = (na->allocated_size >> - vol->cluster_size_bits); - rl[0].lcn = LCN_HOLE; - rl[0].length = first_free_vcn - - (na->allocated_size >> vol->cluster_size_bits); - rl[1].vcn = first_free_vcn; - rl[1].lcn = LCN_ENOENT; - rl[1].length = 0; - } else { - /* - * Determine first after last LCN of attribute. - * We will start seek clusters from this LCN to avoid - * fragmentation. If there are no valid LCNs in the - * attribute let the cluster allocator choose the - * starting LCN. - */ - lcn_seek_from = -1; - if (na->rl->length) { - /* Seek to the last run list element. */ - for (rl = na->rl; (rl + 1)->length; rl++) - ; - /* - * If the last LCN is a hole or similar seek - * back to last valid LCN. - */ - while (rl->lcn < 0 && rl != na->rl) - rl--; - /* - * Only set lcn_seek_from it the LCN is valid. - */ - if (rl->lcn >= 0) - lcn_seek_from = rl->lcn + rl->length; - } - - rl = ntfs_cluster_alloc(vol, na->allocated_size >> - vol->cluster_size_bits, first_free_vcn - - (na->allocated_size >> - vol->cluster_size_bits), lcn_seek_from, - DATA_ZONE); - if (!rl) { - err = errno; - ntfs_log_trace("Eeek! Cluster allocation failed.\n"); - errno = err; - return -1; - } - } - - /* Append new clusters to attribute runlist. */ - rln = ntfs_runlists_merge(na->rl, rl); - if (!rln) { - /* Failed, free just allocated clusters. */ - err = errno; - ntfs_log_trace("Eeek! Run list merge failed.\n"); - ntfs_cluster_free_from_rl(vol, rl); - free(rl); - errno = err; - return -1; - } - na->rl = rln; - - /* Prepare to mapping pairs update. */ - na->allocated_size = first_free_vcn << vol->cluster_size_bits; - /* Write mapping pairs for new runlist. */ - if (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >> - vol->cluster_size_bits*/)) { - err = errno; - ntfs_log_trace("Eeek! Mapping pairs update failed.\n"); - goto rollback; - } - } - - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) { - err = errno; - ntfs_log_trace("Failed to get search context.\n"); - if (na->allocated_size == org_alloc_size) { - errno = err; - return -1; - } else - goto rollback; - } - - if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, - 0, NULL, 0, ctx)) { - err = errno; - ntfs_log_trace("Lookup of first attribute extent failed.\n"); - if (err == ENOENT) - err = EIO; - if (na->allocated_size != org_alloc_size) { - ntfs_attr_put_search_ctx(ctx); - goto rollback; - } else - goto put_err_out; - } - - /* Update data size. */ - na->data_size = newsize; - ctx->attr->data_size = cpu_to_sle64(newsize); - /* Update data size in the index. */ - if (na->type == AT_DATA && na->name == AT_UNNAMED) { - na->ni->data_size = na->data_size; - NInoFileNameSetDirty(na->ni); - } - /* Set the inode dirty so it is written out later. */ - ntfs_inode_mark_dirty(ctx->ntfs_ino); - /* Done! */ - ntfs_attr_put_search_ctx(ctx); - return 0; -rollback: - /* Free allocated clusters. */ - if (ntfs_cluster_free(vol, na, org_alloc_size >> - vol->cluster_size_bits, -1) < 0) { - ntfs_log_trace("Eeek! Leaking clusters. Run chkdsk!\n"); - err = EIO; - } - /* Now, truncate the runlist itself. */ - if (ntfs_rl_truncate(&na->rl, org_alloc_size >> - vol->cluster_size_bits)) { - /* - * Failed to truncate the runlist, so just throw it away, it - * will be mapped afresh on next use. - */ - free(na->rl); - na->rl = NULL; - ntfs_log_trace("Couldn't truncate runlist. Rollback failed.\n"); - } else { - /* Prepare to mapping pairs update. */ - na->allocated_size = org_alloc_size << vol->cluster_size_bits; - /* Restore mapping pairs. */ - if (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >> - vol->cluster_size_bits*/)) { - ntfs_log_trace("Failed to restore old mapping pairs. " - "Rollback failed.\n"); - } - } - errno = err; - return -1; -put_err_out: - ntfs_attr_put_search_ctx(ctx); - errno = err; - return -1; -} - -/** - * ntfs_attr_truncate - resize an ntfs attribute - * @na: open ntfs attribute to resize - * @newsize: new size (in bytes) to which to resize the attribute - * - * Change the size of an open ntfs attribute @na to @newsize bytes. If the - * attribute is made bigger and the attribute is resident the newly - * "allocated" space is cleared and if the attribute is non-resident the - * newly allocated space is marked as not initialised and no real allocation - * on disk is performed. - * - * On success return 0 and on error return -1 with errno set to the error code. - * The following error codes are defined: - * EINVAL - Invalid arguments were passed to the function. - * EOPNOTSUPP - The desired resize is not implemented yet. - */ -int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) -{ - int ret; - - if (!na || newsize < 0 || - (na->ni->mft_no == FILE_MFT && na->type == AT_DATA)) { - ntfs_log_trace("Invalid arguments passed.\n"); - errno = EINVAL; - return -1; - } - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long - long)na->ni->mft_no, na->type); - - if (na->data_size == newsize) - return 0; - /* - * Encrypted attributes are not supported. We return access denied, - * which is what Windows NT4 does, too. - */ - if (NAttrEncrypted(na)) { - errno = EACCES; - return -1; - } - /* - * TODO: Implement making handling of compressed attributes. - */ - if (NAttrCompressed(na)) { - errno = EOPNOTSUPP; - return -1; - } - if (NAttrNonResident(na)) { - if (newsize > na->data_size) - ret = ntfs_non_resident_attr_expand(na, newsize); - else - ret = ntfs_non_resident_attr_shrink(na, newsize); - } else - ret = ntfs_resident_attr_resize(na, newsize); - /* Update access and change times if needed. */ - if (na->type == AT_DATA || na->type == AT_INDEX_ROOT || - na->type == AT_INDEX_ALLOCATION) - ntfs_inode_update_time(na->ni); - return ret; -} - -/** - * ntfs_attr_readall - read the entire data from an ntfs attribute - * @ni: open ntfs inode in which the ntfs attribute resides - * @type: attribute type - * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL - * @name_len: length of attribute @name in Unicode characters (if @name given) - * @data_size: if non-NULL then store here the data size - * - * This function will read the entire content of an ntfs attribute. - * If @name is AT_UNNAMED then look specifically for an unnamed attribute. - * If @name is NULL then the attribute could be either named or not. - * In both those cases @name_len is not used at all. - * - * On success a buffer is allocated with the content of the attribute - * and which needs to be freed when it's not needed anymore. If the - * @data_size parameter is non-NULL then the data size is set there. - * - * On error NULL is returned with errno set to the error code. - */ -void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, - ntfschar *name, u32 name_len, s64 *data_size) -{ - ntfs_attr *na; - void *data, *ret = NULL; - s64 size; - - na = ntfs_attr_open(ni, type, name, name_len); - if (!na) { - ntfs_log_perror("ntfs_attr_open failed"); - return NULL; - } - data = malloc(na->data_size); - if (!data) { - ntfs_log_perror("malloc failed"); - goto out; - } - size = ntfs_attr_pread(na, 0, na->data_size, data); - if (size != na->data_size) { - ntfs_log_perror("ntfs_attr_pread failed"); - free(data); - goto out; - } - ret = data; - if (data_size) - *data_size = size; -out: - ntfs_attr_close(na); - return ret; -} - diff --git a/libntfs/attrib_RE.txt b/libntfs/attrib_RE.txt deleted file mode 100644 index 7df610a2..00000000 --- a/libntfs/attrib_RE.txt +++ /dev/null @@ -1,449 +0,0 @@ -/* Reverse engineered functions in more or less modified form. find_attr() - * is quite heavily modified but should be functionally equivalent to original. - * lookup and lookup_external are less modified. Both should be functionally - * equivalent to originals. */ - -/* - * attr_search_context - used in attribute search functions - * @mrec: buffer containing mft record to search - * @attr: attribute record in @mrec where to begin/continue search - * @alist_mrec: mft record containing attribute list (i.e. base mft record) - * @alist_attr: attribute list attribute record - * @alist_val: attribute list value (if alist is resident in @alist_mrec) - * @alist_val_end: end of attribute list value + 1 - * @alist_val_len: length of attribute list in bytes - * @is_first: if true lookup_attr() begins search with @attr, else after @attr - * - * Structure must be initialized to zero before the first call to one of the - * attribute search functions. If the mft record in which to search has already - * been loaded into memory, then initialize @base and @mrec to point to it, - * @attr to point to the first attribute within @mrec, and set @is_first to - * TRUE. - * - * @is_first is only honoured in lookup_attr() and only when called with @mrec - * not NULL. Then, if @is_first is TRUE, lookup_attr() begins the search with - * @attr. If @is_first is FALSE, lookup_attr() begins the search after @attr. - * This is so that, after the first call to lookup_attr(), we can call - * lookup_attr() again, without any modification of the search context, to - * automagically get the next matching attribute. - * - * In contrast, find_attr() ignores @is_first and always begins the search with - * @attr. find_attr() shouldn't really be called directly; it is just for - * internal use. FIXME: Might want to change this behaviour later, but not - * before I am finished with lookup_external_attr(). (AIA) - */ -typedef struct { - u8 *base; - MFT_RECORD *mrec; - ATTR_RECORD *attr; - - u8 *alist_val_base; - MFT_RECORD *alist_mrec; - ATTR_RECORD *alist_attr; - ATTR_LIST_ENTRY *alist_val; - ATTR_LIST_ENTRY *alist_val_end; - u32 alist_val_len; - IS_FIRST_BOOL is_first; - u8 *alist_old_base; -} attr_search_context; - -BOOL attr_find(const ntfs_volume *vol, const ATTR_TYPES type, - const wchar_t *name, const u32 name_len, - const IGNORE_CASE_BOOL ic, const u8 *val, const u32 val_len, - ntfs_attr_search_ctx *ctx) -{ - ATTR_RECORD *a; - -#ifdef DEBUG - if (!vol || !ctx || !ctx->mrec || !ctx->attr) { - printf(stderr, "attr_find() received NULL pointer!\n"); - return FALSE; - } -#endif - a = ctx->attr; - /* - * Iterate over attributes in mft record starting at @ctx->attr. - * Note: Not using while/do/for loops so the comparison code - * does not get indented out of the 80 characters wide screen... (AIA) - */ - goto search_loop; -do_next: - a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); - if (a < ctx->mrec || a > (char*)ctx->mrec + vol->mft_record_size) - goto file_corrupt; - ctx->attr = a; -search_loop: - /* We catch $END with this more general check, too... */ - if (le32_to_cpu(a->type) > le32_to_cpu(type)) - goto not_found; - if (!a->length) - goto file_corrupt; - if (a->type != type) - goto do_next; - /* If no @name is specified, check for @val. */ - if (!name) { - register int rv; - /* If no @val specified, we are done. */ - if (!val) { -found_it: - return TRUE; - } - rv = memcmp(val, (char*)a + le16_to_cpu(a->value_offset), - min(val_len, le32_to_cpu(a->value_length))); - /* If @val collates after the current attribute's value, - continue searching as a matching attribute might follow. */ - if (!rv) { - register u32 avl = le32_to_cpu(a->value_length); - if (val_len == avl) - goto found_it; - if (val_len > avl) - goto do_next; - } else if (rv > 0) - goto do_next; - goto not_found; - } - if (ntfs_names_are_equal(name, name_len, - (wchar_t*)((char*)a + le16_to_cpu(a->name_offset)), - a->name_length, ic, vol->upcase, vol->upcase_len)) - goto found_it; - { register int rc = ntfs_names_collate(vol->upcase, - vol->upcase_len, name, name_len, - (wchar_t*)((char*)a + le16_to_cpu(a->name_offset)), - a->name_length, IGNORE_CASE, 1); - /* If case insensitive collation of names collates @name - before a->name, there is no matching attribute. */ - if (rc == -1) - goto not_found; - /* If the strings are not equal, continue search. */ - if (rc) - goto do_next; - } - /* If case sensitive collation of names doesn't collate @name before - a->name, we continue the search. Otherwise we haven't found it. */ - if (ntfs_names_collate(vol->upcase, vol->upcase_len, name, name_len, - (wchar_t*)((char*)a + le16_to_cpu(a->name_offset)), - a->name_length, CASE_SENSITIVE, 1) != -1) - goto do_next; -not_found: - return FALSE; -file_corrupt: -#ifdef DEBUG - printf(stderr, "find_attr(): File is corrupt. Run chkdsk.\n"); -#endif - goto not_found; -} - -BOOL external_attr_lookup(const ntfs_volume *vol, const MFT_REFERENCE mref, - const ATTR_TYPES type, const wchar_t *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const s64 lowest_vcn, const u8 *val, - const u32 val_len, ntfs_attr_search_ctx *ctx) -{ - ATTR_LIST_ENTRY *al_pos, **al_val, *al_val_start, *al_next_pos; - ATTR_RECORD *attr_pos; - MFT_RECORD *mrec, *m; - u8 var1 = 0; - u8 var2 = 0; - u8 var3; - int rc; - wchar_t *al_name; - u32 al_name_len; - - al_val = &ctx->alist_val; - if (ctx->alist_val_end <= *al_val && !ctx->is_first) - goto file_corrupt; - al_val_start = 0; - if (ctx->base) { - if (ctx->is_first) - goto already_have_the_base_and_is_first; - al_val_start = *al_val; - al_pos = (char*)*al_val + le16_to_cpu((*al_val)->length); - } else - al_pos = *al_val; -do_next: - if (al_pos < ctx->alist_val_end) - goto al_pos_below_alist_val_end; - var1 = var2 = 1; - al_pos = *al_val; -do_next_2: - *al_val = al_pos; - if (!type || var1 || type == al_pos->type) - goto compare_names; - if (le32_to_cpu(al_pos->type) > le32_to_cpu(type)) - goto gone_too_far; - al_pos = al_next_pos; - goto do_next; -already_have_the_base_and_is_first: - ctx->is_first = FALSE; - if (*al_val < ctx->alist_val_end) - goto do_next; - if (ctx->base) { - // FIXME: CcUnpinData(ctx->base); - ctx->base = NULL; - } - if (!type) - return FALSE; - if (ntfs_file_record_read(vol, mref, &ctx->mrec, &ctx->attr) < 0) - return FALSE; - ctx->base = ctx->mrec; - attr_find(vol, type, name, name_len, ic, val, val_len, ctx); - return FALSE; -al_pos_below_alist_val_end: - if (al_pos < ctx->alist_val) - goto file_corrupt; - if (al_pos >= ctx->alist_val_end) - goto file_corrupt; - if (!al_pos->length) - goto file_corrupt; - al_next_pos = (ATTR_LIST_ENTRY*)((char*)al_pos + - le16_to_cpu(al_pos->length)); - goto do_next_2; -gone_too_far: - var1 = 1; -compare_names: - al_name_len = al_pos->name_length; - al_name = (wchar_t*)((char*)al_pos + al_pos->name_offset); - if (!name || var1) - goto compare_lowest_vcn; - if (ic == CASE_SENSITIVE) { - if (name_len == al_name_len && - !memcmp(al_name, name, al_name_len << 1)) - rc = TRUE; - else - rc = FALSE; - } else /* IGNORE_CASE */ - rc = ntfs_names_are_equal(al_name, al_name_len, name, name_len, - ic, vol->upcase, vol->upcase_len); - if (rc) - goto compare_lowest_vcn; - rc = ntfs_names_collate(vol->upcase, vol->upcase_len, name, name_len, - al_name, al_name_len, IGNORE_CASE, 1); - if (rc == -1) - goto name_collates_before_al_name; - if (!rc && ntfs_names_collate(vol->upcase, vol->upcase_len, name, - name_len, al_name, al_name_len, - IGNORE_CASE, 0) == -1) - goto name_collates_before_al_name; - al_pos = al_next_pos; - goto do_next; -name_collates_before_al_name: - var1 = 1; -compare_lowest_vcn: - if (lowest_vcn && !var1 && al_next_pos < ctx->alist_val_end && - sle64_to_cpu(al_next_pos->lowest_vcn) <= sle64_to_cpu(lowest_vcn) && - al_next_pos->type == al_pos->type && - al_next_pos->name_length == al_name_len && - !memcmp((char*)al_next_pos + al_next_pos->name_offset, al_name, - al_name_len << 1)) { - al_pos = al_next_pos; - goto do_next; - } - /* Don't mask the sequence number. If it isn't equal, the ref is stale. - */ - if (al_val_start && - al_pos->mft_reference == al_val_start->mft_reference) { - mrec = ctx->mrec; - attr_pos = (ATTR_RECORD*)((char*)mrec + - le16_to_cpu(mrec->attrs_offset)); - } else { - if (ctx->base) { - // FIXME: CcUnpinData(ctx->base); - ctx->base = 0; - } - if (ntfs_file_record_read(vol, - le64_to_cpu(al_pos->mft_reference), - &m, &attr_pos) < 0) - return FALSE; - mrec = ctx->mrec; - ctx->base = ctx->mrec = m; - } - var3 = 0; -do_next_attr_loop_start: - if (attr_pos < mrec || attr_pos > (char*)mrec + vol->mft_record_size) - goto file_corrupt; - if (attr_pos->type == AT_END) - goto do_next_al_entry; - if (!attr_pos->length) - goto file_corrupt; - if (al_pos->instance != attr_pos->instance) - goto do_next_attr; - if (al_pos->type != attr_pos->type) - goto do_next_al_entry; - if (!name) - goto skip_name_comparison; - if (attr_pos->name_length != al_name_len) - goto do_next_al_entry; - if (memcmp((wchar_t*)((char*)attr_pos + - le16_to_cpu(attr_pos->name_offset)), al_name, - attr_pos->name_length << 1)) - goto do_next_al_entry; -skip_name_comparison: - var3 = 1; - ctx->attr = attr_pos; - if (var1) - goto loc_5217c; - if (!val) - return TRUE; - if (attr_pos->non_resident) - goto do_next_attr; - if (le32_to_cpu(attr_pos->value_length) != val_len) - goto do_next_attr; - if (!memcmp((char*)attr_pos + le16_to_cpu(attr_pos->value_offset), - val, val_len)) - return TRUE; -do_next_attr: - attr_pos = (ATTR_RECORD*)((char*)attr_pos + - le32_to_cpu(attr_pos->length)); - goto do_next_attr_loop_start; -do_next_al_entry: - if (!var3) - goto file_corrupt; - al_pos = (ATTR_RECORD*)((char*)al_pos + le16_to_cpu(al_pos->length)); - goto do_next; -loc_5217c: - if (var2) - *al_val = (ATTR_RECORD*)((char*)al_pos + - le16_to_cpu(al_pos->length)); - if (ctx->base) { - // FIXME: CcUnpinData(ctx->base); - ctx->base = 0; - } - if (!type) - return FALSE; - if (ntfs_file_record_read(vol, mref, &mrec, &ctx->attr) < 0) - return FALSE; - ctx->base = mrec; - attr_find(vol, type, name, name_len, ic, val, val_len, ctx); - return FALSE; -file_corrupt: -#ifdef DEBUG - fprintf(stderr, "lookup_attr() encountered corrupt file record.\n"); -#endif - return FALSE; -} - -BOOL attr_lookup(const ntfs_volume *vol, const MFT_REFERENCE *mref, - const ATTR_TYPES type, const wchar_t *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const s64 lowest_vcn, const u8 *val, const u32 val_len, - ntfs_attr_search_ctx *ctx) -{ - MFT_RECORD *m; - ATTR_RECORD *a; - s64 len; - - if (!vol || !ctx) { -#ifdef DEBUG - printf(stderr, "lookup_attr() received NULL pointer!\n"); -#endif - return FALSE; - } - if (ctx->base) - goto already_have_the_base; - if (ntfs_file_record_read(vol, mref, &m, &a) < 0) - return FALSE; - ctx->base = ctx->mrec = m; - ctx->attr = a; - ctx->alist_mrec = ctx->alist_attr = ctx->alist_val = NULL; - /* - * Look for an attribute list and at the same time check for attributes - * which collate before the attribute list (i.e. $STANDARD_INFORMATION). - */ - if (le32_to_cpu(a->type) > le32_to_cpu(AT_ATTRIBUTE_LIST)) - goto no_attr_list; -do_next: - if (!a->length) - goto file_corrupt; - if (a->type == AT_ATTRIBUTE_LIST) - goto attr_list_present; - a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); - if (a < m || a > (char*)m + vol->mft_record_size) - goto file_corrupt; - if (le32_to_cpu(a->type) <= le32_to_cpu(AT_ATTRIBUTE_LIST)) - goto do_next; -no_attr_list: - if (!type || type == AT_STANDARD_INFORMATION && - a->type == AT_STANDARD_INFORMATION) - goto found_it; -call_find_attr: - return attr_find(vol, type, name, name_len, ic, val, val_len, ctx); -found_it: - ctx->attr = a; - return TRUE; -already_have_the_base: - /* - * If ctx->is_first, search starting with ctx->attr. Otherwise - * continue search after ctx->attr. - */ - if (ctx->is_first) { - a = ctx->attr; - ctx->is_first = 0; - } else - a = (ATTR_RECORD*)((char*)ctx->attr + - le32_to_cpu(ctx->attr->length)); - if (a < m || a > (char*)m + vol->mft_record_size) - goto file_corrupt; - if (a->type == AT_END) - return FALSE; - if (!a->length) - goto file_corrupt; - if (type) - goto call_find_attr; - goto found_it; -attr_list_present: - /* - * Looking for zero means we return the first attribute, which will - * be the first one listed in the attribute list. - */ - ctx->attr = a; - if (!type) - goto search_attr_list; - if (type == AT_ATTRIBUTE_LIST) - return TRUE; -search_attr_list: - /* - * "a" contains the attribute list attribute at this stage. - */ - ctx->alist_attr = a; - len = ntfs_get_attribute_value_length(a); -#ifdef DEBUG - if (len > 0x40000LL) { - printf(stderr, "lookup_attr() found corrupt attribute list.\n"); - return FALSE; - } -#endif - ctx->alist_val_len = len; - if (!(ctx->alist_val = malloc(ctx->alist_val_len))) { -#ifdef DEBUG - printf(stderr, "lookup_attr() failed to allocate memory for " - "attribute list value.\n"); -#endif - return FALSE; - } - if (ntfs_get_attribute_value(vol, ctx->mrec, a, ctx->alist_val) != - ctx->alist_val_len) { -#ifdef DEBUG - printf(stderr, "lookup_attr() failed to read attribute list " - "value.\n"); -#endif - return FALSE; - } - ctx->alist_val_end = (char*)ctx->alist_val + ctx->alist_val_len; - if (a->non_resident) { - ctx->alist_old_base = ctx->alist_val_base; - ctx->alist_val_base = ctx->base; - ctx->base = NULL; - } else if (ctx->base) { - // FIXME: CcUnpinData(ctx->base); - ctx->base = NULL; - } -lookup_external: - return external_attr_lookup(vol, mref, type, name, name_len, ic, - lowest_vcn, val, val_len, ctx); -file_corrupt: -#ifdef DEBUG - fprintf(stderr, "attr_lookup() encountered corrupt file record.\n"); -#endif - return FALSE; -} - diff --git a/libntfs/attrlist.c b/libntfs/attrlist.c deleted file mode 100644 index f09d4b42..00000000 --- a/libntfs/attrlist.c +++ /dev/null @@ -1,319 +0,0 @@ -/** - * attrlist.c - Attribute list attribute handling code. Part of the Linux-NTFS - * project. - * - * Copyright (c) 2004-2005 Anton Altaparmakov - * Copyright (c) 2004-2005 Yura Pakhuchiy - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif - -#include "types.h" -#include "layout.h" -#include "attrib.h" -#include "attrlist.h" -#include "debug.h" -#include "unistr.h" -#include "logging.h" - -/** - * ntfs_attrlist_need - check whether inode need attribute list - * @ni: opened ntfs inode for which perform check - * - * Check whether all are attributes belong to one MFT record, in that case - * attribute list is not needed. - * - * Return 1 if inode need attribute list, 0 if not, -1 on error with errno set - * to the error code. If function succeed errno set to 0. The following error - * codes are defined: - * EINVAL - Invalid arguments passed to function or attribute haven't got - * attribute list. - */ -int ntfs_attrlist_need(ntfs_inode *ni) -{ - ATTR_LIST_ENTRY *ale; - - if (!ni) { - ntfs_log_trace("Invalid arguments.\n"); - errno = EINVAL; - return -1; - } - - ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); - - if (!NInoAttrList(ni)) { - ntfs_log_trace("Inode haven't got attribute list.\n"); - errno = EINVAL; - return -1; - } - - if (!ni->attr_list) { - ntfs_log_trace("Corrupt in-memory struct.\n"); - errno = EINVAL; - return -1; - } - - errno = 0; - ale = (ATTR_LIST_ENTRY *)ni->attr_list; - while ((u8*)ale < ni->attr_list + ni->attr_list_size) { - if (MREF_LE(ale->mft_reference) != ni->mft_no) - return 1; - ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length)); - } - return 0; -} - -/** - * ntfs_attrlist_entry_add - add an attribute list attribute entry - * @ni: opened ntfs inode, which contains that attribute - * @attr: attribute record to add to attribute list - * - * Return 0 on success and -1 on error with errno set to the error code. The - * following error codes are defined: - * EINVAL - Invalid arguments passed to function. - * ENOMEM - Not enough memory to allocate necessary buffers. - * EIO - I/O error occurred or damaged filesystem. - * EEXIST - Such attribute already present in attribute list. - */ -int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) -{ - ATTR_LIST_ENTRY *ale; - MFT_REF mref; - ntfs_attr *na = NULL; - ntfs_attr_search_ctx *ctx; - u8 *new_al; - int entry_len, entry_offset, err; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", - (long long) ni->mft_no, - (unsigned) le32_to_cpu(attr->type)); - - if (!ni || !attr) { - ntfs_log_trace("Invalid arguments.\n"); - errno = EINVAL; - return -1; - } - - mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); - - if (ni->nr_extents == -1) - ni = ni->base_ni; - - if (!NInoAttrList(ni)) { - ntfs_log_trace("Attribute list isn't present.\n"); - errno = ENOENT; - return -1; - } - - /* Determine size and allocate memory for new attribute list. */ - entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * - attr->name_length + 7) & ~7; - new_al = malloc(ni->attr_list_size + entry_len); - if (!new_al) { - ntfs_log_trace("Not enough memory.\n"); - err = ENOMEM; - return -1; - } - - /* Find place for the new entry. */ - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) { - err = errno; - ntfs_log_trace("Failed to obtain attribute search context.\n"); - goto err_out; - } - if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*) - ((u8*)attr + le16_to_cpu(attr->name_offset)) : - AT_UNNAMED, attr->name_length, CASE_SENSITIVE, - (attr->non_resident) ? le64_to_cpu(attr->lowest_vcn) : - 0, (attr->non_resident) ? NULL : ((u8*)attr + - le16_to_cpu(attr->value_offset)), (attr->non_resident) ? - 0 : le32_to_cpu(attr->value_length), ctx)) { - /* Found some extent, check it to be before new extent. */ - if (ctx->al_entry->lowest_vcn == attr->lowest_vcn) { - err = EEXIST; - ntfs_log_trace("Such attribute already present in the " - "attribute list.\n"); - ntfs_attr_put_search_ctx(ctx); - goto err_out; - } - /* Add new entry after this extent. */ - ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry + - le16_to_cpu(ctx->al_entry->length)); - } else { - /* Check for real errors. */ - if (errno != ENOENT) { - err = errno; - ntfs_log_trace("Attribute lookup failed.\n"); - ntfs_attr_put_search_ctx(ctx); - goto err_out; - } - /* No previous extents found. */ - ale = ctx->al_entry; - } - /* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */ - ntfs_attr_put_search_ctx(ctx); - - /* Determine new entry offset. */ - entry_offset = ((u8 *)ale - ni->attr_list); - /* Set pointer to new entry. */ - ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset); - /* Zero it to fix valgrind warning. */ - memset(ale, 0, entry_len); - /* Form new entry. */ - ale->type = attr->type; - ale->length = cpu_to_le16(entry_len); - ale->name_length = attr->name_length; - ale->name_offset = offsetof(ATTR_LIST_ENTRY, name); - if (attr->non_resident) - ale->lowest_vcn = attr->lowest_vcn; - else - ale->lowest_vcn = 0; - ale->mft_reference = mref; - ale->instance = attr->instance; - memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset), - attr->name_length * sizeof(ntfschar)); - - /* Resize $ATTRIBUTE_LIST to new length. */ - na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); - if (!na) { - err = errno; - ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); - goto err_out; - } - if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) { - err = errno; - ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); - goto err_out; - } - - /* Copy entries from old attribute list to new. */ - memcpy(new_al, ni->attr_list, entry_offset); - memcpy(new_al + entry_offset + entry_len, ni->attr_list + - entry_offset, ni->attr_list_size - entry_offset); - - /* Set new runlist. */ - free(ni->attr_list); - ni->attr_list = new_al; - ni->attr_list_size = ni->attr_list_size + entry_len; - NInoAttrListSetDirty(ni); - /* Done! */ - ntfs_attr_close(na); - return 0; -err_out: - if (na) - ntfs_attr_close(na); - free(new_al); - errno = err; - return -1; -} - -/** - * ntfs_attrlist_entry_rm - remove an attribute list attribute entry - * @ctx: attribute search context describing the attribute list entry - * - * Remove the attribute list entry @ctx->al_entry from the attribute list. - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx) -{ - u8 *new_al; - int new_al_len; - ntfs_inode *base_ni; - ntfs_attr *na; - ATTR_LIST_ENTRY *ale; - int err; - - if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) { - ntfs_log_trace("Invalid arguments.\n"); - errno = EINVAL; - return -1; - } - - if (ctx->base_ntfs_ino) - base_ni = ctx->base_ntfs_ino; - else - base_ni = ctx->ntfs_ino; - ale = ctx->al_entry; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n", - (long long) ctx->ntfs_ino->mft_no, - (unsigned) le32_to_cpu(ctx->al_entry->type), - (long long) le64_to_cpu(ctx->al_entry->lowest_vcn)); - - if (!NInoAttrList(base_ni)) { - ntfs_log_trace("Attribute list isn't present.\n"); - errno = ENOENT; - return -1; - } - - /* Allocate memory for new attribute list. */ - new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length); - new_al = malloc(new_al_len); - if (!new_al) { - ntfs_log_trace("Not enough memory.\n"); - errno = ENOMEM; - return -1; - } - - /* Reisze $ATTRIBUTE_LIST to new length. */ - na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); - if (!na) { - err = errno; - ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); - goto err_out; - } - if (ntfs_attr_truncate(na, new_al_len)) { - err = errno; - ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); - goto err_out; - } - - /* Copy entries from old attribute list to new. */ - memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list); - memcpy(new_al + ((u8*)ale - base_ni->attr_list), (u8*)ale + le16_to_cpu( - ale->length), new_al_len - ((u8*)ale - base_ni->attr_list)); - - /* Set new runlist. */ - free(base_ni->attr_list); - base_ni->attr_list = new_al; - base_ni->attr_list_size = new_al_len; - NInoAttrListSetDirty(base_ni); - /* Done! */ - ntfs_attr_close(na); - return 0; -err_out: - if (na) - ntfs_attr_close(na); - free(new_al); - errno = err; - return -1; -} diff --git a/libntfs/bitmap.c b/libntfs/bitmap.c deleted file mode 100644 index 4076ad90..00000000 --- a/libntfs/bitmap.c +++ /dev/null @@ -1,674 +0,0 @@ -/** - * bitmap.c - Bitmap handling code. Part of the Linux-NTFS project. - * - * Copyright (c) 2002-2006 Anton Altaparmakov - * Copyright (c) 2004-2005 Richard Russon - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_STDIO_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif - -#include "types.h" -#include "attrib.h" -#include "bitmap.h" -#include "debug.h" -#include "logging.h" - -/** - * ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value - * @na: attribute containing the bitmap - * @start_bit: first bit to set - * @count: number of bits to set - * @value: value to set the bits to (i.e. 0 or 1) - * - * Set @count bits starting at bit @start_bit in the bitmap described by the - * attribute @na to @value, where @value is either 0 or 1. - * - * On success return 0 and on error return -1 with errno set to the error code. - */ -static __inline__ int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, - s64 count, int value) -{ - s64 bufsize, br; - u8 *buf, *lastbyte_buf; - int bit, firstbyte, lastbyte, lastbyte_pos, tmp, err; - - if (!na || start_bit < 0 || count < 0) { - errno = EINVAL; - return -1; - } - - bit = start_bit & 7; - if (bit) - firstbyte = 1; - else - firstbyte = 0; - - /* Calculate the required buffer size in bytes, capping it at 8kiB. */ - bufsize = ((count - (bit ? 8 - bit : 0) + 7) >> 3) + firstbyte; - if (bufsize > 8192) - bufsize = 8192; - - /* Allocate memory. */ - buf = (u8*)malloc(bufsize); - if (!buf) - return -1; - /* Depending on @value, zero or set all bits in the allocated buffer. */ - memset(buf, value ? 0xff : 0, bufsize); - - /* If there is a first partial byte... */ - if (bit) { - /* read it in... */ - br = ntfs_attr_pread(na, start_bit >> 3, 1, buf); - if (br != 1) { - free(buf); - errno = EIO; - return -1; - } - /* and set or clear the appropriate bits in it. */ - while ((bit & 7) && count--) { - if (value) - *buf |= 1 << bit++; - else - *buf &= ~(1 << bit++); - } - /* Update @start_bit to the new position. */ - start_bit = (start_bit + 7) & ~7; - } - - /* Loop until @count reaches zero. */ - lastbyte = 0; - lastbyte_buf = NULL; - bit = count & 7; - do { - /* If there is a last partial byte... */ - if (count > 0 && bit) { - lastbyte_pos = ((count + 7) >> 3) + firstbyte; - if (!lastbyte_pos) { - // FIXME: Eeek! BUG! - ntfs_log_trace("Eeek! lastbyte is zero. Leaving " - "inconsistent metadata.\n"); - err = EIO; - goto free_err_out; - } - /* and it is in the currently loaded bitmap window... */ - if (lastbyte_pos <= bufsize) { - lastbyte_buf = buf + lastbyte_pos - 1; - - /* read the byte in... */ - br = ntfs_attr_pread(na, (start_bit + count) >> - 3, 1, lastbyte_buf); - if (br != 1) { - // FIXME: Eeek! We need rollback! (AIA) - ntfs_log_trace("Eeek! Read of last byte " - "failed. Leaving " - "inconsistent metadata.\n"); - err = EIO; - goto free_err_out; - } - /* and set/clear the appropriate bits in it. */ - while (bit && count--) { - if (value) - *lastbyte_buf |= 1 << --bit; - else - *lastbyte_buf &= ~(1 << --bit); - } - /* We don't want to come back here... */ - bit = 0; - /* We have a last byte that we have handled. */ - lastbyte = 1; - } - } - - /* Write the prepared buffer to disk. */ - tmp = (start_bit >> 3) - firstbyte; - br = ntfs_attr_pwrite(na, tmp, bufsize, buf); - if (br != bufsize) { - // FIXME: Eeek! We need rollback! (AIA) - ntfs_log_trace("Eeek! Failed to write buffer to bitmap. " - "Leaving inconsistent metadata.\n"); - err = EIO; - goto free_err_out; - } - - /* Update counters. */ - tmp = (bufsize - firstbyte - lastbyte) << 3; - if (firstbyte) { - firstbyte = 0; - /* - * Re-set the partial first byte so a subsequent write - * of the buffer does not have stale, incorrect bits. - */ - *buf = value ? 0xff : 0; - } - start_bit += tmp; - count -= tmp; - if (bufsize > (tmp = (count + 7) >> 3)) - bufsize = tmp; - - if (lastbyte && count != 0) { - // FIXME: Eeek! BUG! - ntfs_log_trace("Eeek! Last buffer but count is not zero (= " - "%lli). Leaving inconsistent metadata.\n", - (long long)count); - err = EIO; - goto free_err_out; - } - } while (count > 0); - - /* Done! */ - free(buf); - return 0; - -free_err_out: - free(buf); - errno = err; - return -1; -} - -/** - * ntfs_bitmap_set_run - set a run of bits in a bitmap - * @na: attribute containing the bitmap - * @start_bit: first bit to set - * @count: number of bits to set - * - * Set @count bits starting at bit @start_bit in the bitmap described by the - * attribute @na. - * - * On success return 0 and on error return -1 with errno set to the error code. - */ -int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count) -{ - return ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1); -} - -/** - * ntfs_bitmap_clear_run - clear a run of bits in a bitmap - * @na: attribute containing the bitmap - * @start_bit: first bit to clear - * @count: number of bits to clear - * - * Clear @count bits starting at bit @start_bit in the bitmap described by the - * attribute @na. - * - * On success return 0 and on error return -1 with errno set to the error code. - */ -int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count) -{ - ntfs_log_trace("Dealloc from bit 0x%llx, count 0x%llx.\n", - (long long)start_bit, (long long)count); - - return ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0); -} - - -#ifdef NTFS_RICH - -#include "layout.h" -#include "volume.h" -#include "rich.h" - -/** - * ntfs_bmp_rollback - Discard the in-memory bitmap changes - * @bmp: - * - * Description... - * - * Returns: - */ -int ntfs_bmp_rollback(struct ntfs_bmp *bmp) -{ - int i; - - if ((!bmp) || (bmp->count == 0)) - return 0; - - ntfs_log_trace ("bmp %p, records %d, attr %lld/%02X\n", bmp, bmp->count, MREF(bmp->attr->ni->mft_no), bmp->attr->type); - - for (i = 0; i < bmp->count; i++) - free(bmp->data[i]); - - free(bmp->data); - free(bmp->data_vcn); - bmp->data = NULL; - bmp->data_vcn = NULL; - bmp->count = 0; - - return 0; -} - -/** - * ntfs_bmp_commit - Write the cached bitmap data to disk - * @bmp: - * - * Description... - * - * Returns: - */ -int ntfs_bmp_commit(struct ntfs_bmp *bmp) -{ - int i; - u32 cs; - u32 ws; // write size - - if (!bmp) - return 0; - if (bmp->count == 0) - return 0; - - ntfs_log_trace ("bmp %p, records %d, attr %lld/%02X\n", bmp, bmp->count, MREF(bmp->attr->ni->mft_no), bmp->attr->type); -#if 0 - ntfs_log_debug("attr = 0x%02X\n", bmp->attr->type); - ntfs_log_debug("resident = %d\n", !NAttrNonResident(bmp->attr)); - ntfs_log_debug("\ta size = %lld\n", bmp->attr->allocated_size); - ntfs_log_debug("\td size = %lld\n", bmp->attr->data_size); - ntfs_log_debug("\ti size = %lld\n", bmp->attr->initialized_size); -#endif - - ntfs_log_debug("commit bmp inode %lld, 0x%02X (%sresident)\n", bmp->attr->ni->mft_no, bmp->attr->type, NAttrNonResident(bmp->attr) ? "non-" : ""); - - if (NAttrNonResident(bmp->attr)) { - cs = bmp->vol->cluster_size; - - // non-resident - for (i = 0; i < bmp->count; i++) { - if (((bmp->data_vcn[i]+1) * cs) < bmp->attr->data_size) - ws = cs; - else - ws = bmp->attr->data_size & (cs - 1); - //ntfs_log_debug("writing %d bytes\n", ws); - ntfs_attr_pwrite(bmp->attr, bmp->data_vcn[i] * cs, ws, bmp->data[i]); // XXX retval - ntfs_log_warning("\tntfs_attr_pwrite(vcn %lld)\n", bmp->data_vcn[i]); - } - } else { - // resident - ntfs_attr_pwrite(bmp->attr, bmp->data_vcn[0], bmp->attr->data_size, bmp->data[0]); // XXX retval - ntfs_log_warning("\tntfs_attr_pwrite resident (%lld)\n", bmp->attr->data_size); - } - - ntfs_bmp_rollback(bmp); - - return 0; -} - -/** - * ntfs_bmp_free - Destroy a bitmap object - * @bmp: - * - * Description... - * - * Returns: - */ -void ntfs_bmp_free(struct ntfs_bmp *bmp) -{ - if (!bmp) - return; - - ntfs_log_trace ("bmp %p, records %d, attr %lld/%02X\n", bmp, bmp->count, MREF(bmp->attr->ni->mft_no), bmp->attr->type); - - ntfs_bmp_rollback(bmp); - ntfs_attr_close(bmp->attr); - - free(bmp); -} - -/** - * ntfs_bmp_create - Create a representation of a bitmap - * @inode: - * @type: - * @name: - * @name_len: - * - * Description... - * - * Returns: - */ -struct ntfs_bmp * ntfs_bmp_create(ntfs_inode *inode, ATTR_TYPES type, ntfschar *name, int name_len) -{ - struct ntfs_bmp *bmp; - ntfs_attr *attr; - - if (!inode) - return NULL; - - ntfs_log_trace ("\n"); - attr = ntfs_attr_open(inode, type, name, name_len); - if (!attr) - return NULL; - - bmp = calloc(1, sizeof(*bmp)); - if (!bmp) { - ntfs_attr_close(attr); - return NULL; - } - - ntfs_log_critical("bmp = %p, attr = %p, inode = %p, attr->ni->mft_no = %lld\n", bmp, attr, inode, MREF(attr->ni->mft_no)); - bmp->vol = inode->vol; - bmp->attr = attr; - bmp->data = NULL; - bmp->data_vcn = NULL; - bmp->count = 0; - - return bmp; -} - -/** - * ntfs_bmp_add_data - Add a bitmap block to the current cache - * @bmp: - * @vcn: - * @data: - * - * Description... - * - * Returns: - */ -int ntfs_bmp_add_data(struct ntfs_bmp *bmp, VCN vcn, u8 *data) -{ - int i = 0; - int old; - int new; - - if (!bmp || !data) - return -1; - - ntfs_log_trace ("\n"); - old = ROUND_UP(bmp->count, 16); - bmp->count++; - new = ROUND_UP(bmp->count, 16); - - if (old != new) { - bmp->data = realloc(bmp->data, new * sizeof(*bmp->data)); - bmp->data_vcn = realloc(bmp->data_vcn , new * sizeof(*bmp->data_vcn)); - } - - for (i = 0; i < bmp->count-1; i++) - if (bmp->data_vcn[i] > vcn) - break; - - if ((bmp->count-i) > 0) { - memmove(&bmp->data[i+1], &bmp->data[i], (bmp->count-i) * sizeof(*bmp->data)); - memmove(&bmp->data_vcn[i+1], &bmp->data_vcn[i], (bmp->count-i) * sizeof(*bmp->data_vcn)); - } - - bmp->data[i] = data; - bmp->data_vcn[i] = vcn; - - return bmp->count; -} - -/** - * ntfs_bmp_get_data - Ask for a bitmap block from the cache - * @bmp: - * @vcn: - * - * Description... - * - * Returns: - */ -u8 * ntfs_bmp_get_data(struct ntfs_bmp *bmp, VCN vcn) -{ - u8 *buffer; - int i; - int cs; - int cb; - - if (!bmp) - return NULL; - - ntfs_log_trace ("\n"); - cs = bmp->vol->cluster_size; - cb = bmp->vol->cluster_size_bits; - - // XXX range check against vol,attr - // never compressed, so data = init - - vcn >>= (cb + 3); // convert to bitmap clusters - - for (i = 0; i < bmp->count; i++) { - if (vcn == bmp->data_vcn[i]) { - //ntfs_log_debug("reusing bitmap cluster %lld\n", vcn); - return bmp->data[i]; - } - } - - buffer = calloc(1, cs); // XXX could be smaller if attr size < cluster size - if (!buffer) - return NULL; - - //ntfs_log_debug("loading from bitmap cluster %lld\n", vcn); - //ntfs_log_debug("loading from bitmap byte %lld\n", vcn<attr, vcn<vol->cluster_size << 3; - - vcn_start = vcn; - vcn_finish = vcn + length - 1; - - //ntfs_log_debug("vcn_start = %d, vcn_finish = %d\n", vcn_start, vcn_finish); - a = ROUND_DOWN(vcn_start, csib); - b = ROUND_DOWN(vcn_finish, csib) + 1; - - //ntfs_log_debug("a = %lld, b = %lld\n", a, b); - - for (i = a; i < b; i += csib) { - //ntfs_log_debug("ntfs_bmp_get_data %lld\n", i); - buffer = ntfs_bmp_get_data(bmp, i); - if (!buffer) - return -1; - - block_start = i; - block_finish = block_start + csib - 1; - - mask_start = (0xFF << (vcn_start & 7)); - mask_finish = (0xFF >> (7 - (vcn_finish & 7))); - - if ((vcn_start >= block_start) && (vcn_start <= block_finish)) { - byte_start = (vcn_start - block_start) >> 3; - } else { - byte_start = 0; - mask_start = 0xFF; - } - - if ((vcn_finish >= block_start) && (vcn_finish <= block_finish)) { - byte_finish = (vcn_finish - block_start) >> 3; - } else { - byte_finish = bmp->vol->cluster_size - 1; - mask_finish = 0xFF; - } - - if ((byte_finish - byte_start) > 1) { - memset(buffer+byte_start+1, value, byte_finish-byte_start-1); - } else if (byte_finish == byte_start) { - mask_start &= mask_finish; - mask_finish = 0x00; - } - - if (value) { - buffer[byte_start] |= mask_start; - buffer[byte_finish] |= mask_finish; - } else { - buffer[byte_start] &= (~mask_start); - buffer[byte_finish] &= (~mask_finish); - } - } - -#if 1 - ntfs_log_debug("Modified: inode %lld, ", bmp->attr->ni->mft_no); - switch (bmp->attr->type) { - case AT_BITMAP: ntfs_log_debug("$BITMAP"); break; - case AT_DATA: ntfs_log_debug("$DATA"); break; - default: break; - } - ntfs_log_debug(" vcn %lld-%lld\n", vcn>>12, (vcn+length-1)>>12); -#endif - return 1; -} - -/** - * ntfs_bmp_find_last_set - Find the last set bit in the bitmap - * @bmp: - * - * Description... - * - * Returns: - */ -s64 ntfs_bmp_find_last_set(struct ntfs_bmp *bmp) -{ - s64 clust_count; - s64 byte_count; - s64 clust; - int byte; - int bit; - int note; - u8 *buffer; - - if (!bmp) - return -2; - - ntfs_log_trace ("\n"); - // find byte size of bmp - // find cluster size of bmp - - byte_count = bmp->attr->data_size; - clust_count = ROUND_UP(byte_count, bmp->vol->cluster_size) >> bmp->vol->cluster_size_bits; - - //ntfs_log_debug("bitmap = %lld bytes\n", byte_count); - //ntfs_log_debug("bitmap = %lld buffers\n", clust_count); - - // for each cluster backwards - for (clust = clust_count-1; clust >= 0; clust--) { - //ntfs_log_debug("cluster %lld\n", clust); - //ntfs_log_debug("get vcn %lld\n", clust << (bmp->vol->cluster_size_bits + 3)); - buffer = ntfs_bmp_get_data(bmp, clust << (bmp->vol->cluster_size_bits + 3)); - //utils_dump_mem(buffer, 0, 8, DM_NO_ASCII); - if (!buffer) - return -2; - if ((clust == (clust_count-1) && ((byte_count % bmp->vol->cluster_size) != 0))) { - byte = byte_count % bmp->vol->cluster_size; - } else { - byte = bmp->vol->cluster_size; - } - //ntfs_log_debug("start byte = %d\n", byte); - // for each byte backward - for (byte--; byte >= 0; byte--) { - //ntfs_log_debug("\tbyte %d (%d)\n", byte, buffer[byte]); - // for each bit shift up - note = -1; - for (bit = 7; bit >= 0; bit--) { - //ntfs_log_debug("\t\tbit %d (%d)\n", (1<= 0) { - // if note, return value - //ntfs_log_debug("match %lld (c=%lld,b=%d,n=%d)\n", (((clust << bmp->vol->cluster_size_bits) + byte) << 3) + note, clust, byte, note); - return ((((clust << bmp->vol->cluster_size_bits) + byte) << 3) + note); - } - } - } - - return -1; -} - -/** - * ntfs_bmp_find_space - Find an unused block of bits in a bitmap - * @bmp: - * @start: - * @size: - * - * Description... - * - * Returns: - */ -int ntfs_bmp_find_space(struct ntfs_bmp *bmp, LCN start, long size) -{ - if (!bmp) - return 0; - - ntfs_log_trace ("\n"); - start = 0; - size = 0; - - /* - bmp find space - uncached bmp's - $Bitmap/$DATA free space on volume - dir/$BITMAP free index record - $MFT/$BITMAP free record in mft - */ - return 0; -} - - -#endif /* NTFS_RICH */ - diff --git a/libntfs/bootsect.c b/libntfs/bootsect.c deleted file mode 100644 index 93d95ff4..00000000 --- a/libntfs/bootsect.c +++ /dev/null @@ -1,272 +0,0 @@ -/** - * bootsect.c - Boot sector handling code. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2006 Anton Altaparmakov - * Copyright (c) 2005 Yura Pakhuchiy - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDIO_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif - -#include "compat.h" -#include "bootsect.h" -#include "debug.h" -#include "logging.h" - -/** - * ntfs_boot_sector_is_ntfs - check if buffer contains a valid ntfs boot sector - * @b: buffer containing putative boot sector to analyze - * @silent: if zero, output progress messages to stderr - * - * Check if the buffer @b contains a valid ntfs boot sector. The buffer @b - * must be at least 512 bytes in size. - * - * If @silent is zero, output progress messages to stderr. Otherwise, do not - * output any messages (except when configured with --enable-debug in which - * case warning/debug messages may be displayed). - * - * Return TRUE if @b contains a valid ntfs boot sector and FALSE if not. - */ -BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b, const BOOL silent __attribute__((unused))) -{ - u32 i; - - ntfs_log_debug("\nBeginning bootsector check...\n"); - - /* Calculate the checksum. Note, this is just a simple addition of - all u32 values in the bootsector starting at the beginning and - finishing at the offset of the checksum itself (i.e. not including - the checksum...). */ - if ((void*)b < (void*)&b->checksum) { - u32 *u = (u32 *)b; - u32 *bi = (u32 *)(&b->checksum); - - ntfs_log_debug("Calculating bootsector checksum... "); - - for (i = 0; u < bi; ++u) - i += le32_to_cpup(u); - - if (le32_to_cpu(b->checksum) && le32_to_cpu(b->checksum) != i) - goto not_ntfs; - ntfs_log_debug("OK\n"); - } - - /* Check OEMidentifier is "NTFS " */ - ntfs_log_debug("Checking OEMid... "); - if (b->oem_id != cpu_to_le64(0x202020205346544eULL)) /* "NTFS " */ - goto not_ntfs; - ntfs_log_debug("OK\n"); - - /* Check bytes per sector value is between 256 and 4096. */ - ntfs_log_debug("Checking bytes per sector... "); - if (le16_to_cpu(b->bpb.bytes_per_sector) < 0x100 || - le16_to_cpu(b->bpb.bytes_per_sector) > 0x1000) - goto not_ntfs; - ntfs_log_debug("OK\n"); - - /* Check sectors per cluster value is valid. */ - ntfs_log_debug("Checking sectors per cluster... "); - switch (b->bpb.sectors_per_cluster) { - case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: - break; - default: - goto not_ntfs; - } - ntfs_log_debug("OK\n"); - - /* Check the cluster size is not above 65536 bytes. */ - ntfs_log_debug("Checking cluster size... "); - if ((u32)le16_to_cpu(b->bpb.bytes_per_sector) * - b->bpb.sectors_per_cluster > 0x10000) - goto not_ntfs; - ntfs_log_debug("OK\n"); - - /* Check reserved/unused fields are really zero. */ - ntfs_log_debug("Checking reserved fields are zero... "); - if (le16_to_cpu(b->bpb.reserved_sectors) || - le16_to_cpu(b->bpb.root_entries) || - le16_to_cpu(b->bpb.sectors) || - le16_to_cpu(b->bpb.sectors_per_fat) || - le32_to_cpu(b->bpb.large_sectors) || - b->bpb.fats) - goto not_ntfs; - ntfs_log_debug("OK\n"); - - /* Check clusters per file mft record value is valid. */ - ntfs_log_debug("Checking clusters per mft record... "); - if ((u8)b->clusters_per_mft_record < 0xe1 || - (u8)b->clusters_per_mft_record > 0xf7) { - switch (b->clusters_per_mft_record) { - case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: - break; - default: - goto not_ntfs; - } - } - ntfs_log_debug("OK\n"); - - /* Check clusters per index block value is valid. */ - ntfs_log_debug("Checking clusters per index block... "); - if ((u8)b->clusters_per_index_record < 0xe1 || - (u8)b->clusters_per_index_record > 0xf7) { - switch (b->clusters_per_index_record) { - case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: - break; - default: - goto not_ntfs; - } - } - ntfs_log_debug("OK\n"); - - if (b->end_of_sector_marker != cpu_to_le16(0xaa55)) - ntfs_log_debug("Warning: Bootsector has invalid end of sector marker.\n"); - - ntfs_log_debug("Bootsector check completed successfully.\n"); - - return TRUE; -not_ntfs: - ntfs_log_debug("FAILED\n"); - ntfs_log_debug("Bootsector check failed. Aborting...\n"); - return FALSE; -} - -/** - * ntfs_boot_sector_parse - setup an ntfs volume from an ntfs boot sector - * @vol: ntfs_volume to setup - * @bs: buffer containing ntfs boot sector to parse - * - * Parse the ntfs bootsector @bs and setup the ntfs volume @vol with the - * obtained values. - * - * Return 0 on success or -1 on error with errno set to the error code EINVAL. - */ -int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) -{ - u8 sectors_per_cluster; - s8 c; - - /* We return -1 with errno = EINVAL on error. */ - errno = EINVAL; - - vol->sector_size = le16_to_cpu(bs->bpb.bytes_per_sector); - vol->sector_size_bits = ffs(vol->sector_size) - 1; - ntfs_log_debug("SectorSize = 0x%x\n", vol->sector_size); - ntfs_log_debug("SectorSizeBits = %u\n", vol->sector_size_bits); - /* - * The bounds checks on mft_lcn and mft_mirr_lcn (i.e. them being - * below or equal the number_of_clusters) really belong in the - * ntfs_boot_sector_is_ntfs but in this way we can just do this once. - */ - sectors_per_cluster = bs->bpb.sectors_per_cluster; - ntfs_log_debug("NumberOfSectors = %lli\n", sle64_to_cpu(bs->number_of_sectors)); - ntfs_log_debug("SectorsPerCluster = 0x%x\n", sectors_per_cluster); - if (sectors_per_cluster & (sectors_per_cluster - 1)) { - ntfs_log_debug("Error: %s is not a valid NTFS partition! " - "sectors_per_cluster is not a power of 2.\n", - vol->dev->d_name); - return -1; - } - vol->nr_clusters = sle64_to_cpu(bs->number_of_sectors) >> - (ffs(sectors_per_cluster) - 1); - - vol->mft_lcn = sle64_to_cpu(bs->mft_lcn); - vol->mftmirr_lcn = sle64_to_cpu(bs->mftmirr_lcn); - ntfs_log_debug("MFT LCN = 0x%llx\n", vol->mft_lcn); - ntfs_log_debug("MFTMirr LCN = 0x%llx\n", vol->mftmirr_lcn); - if (vol->mft_lcn > vol->nr_clusters || - vol->mftmirr_lcn > vol->nr_clusters) { - ntfs_log_debug("Error: %s is not a valid NTFS partition!\n", - vol->dev->d_name); - ntfs_log_debug("($Mft LCN or $MftMirr LCN is greater than the " - "number of clusters!)\n"); - return -1; - } - vol->cluster_size = sectors_per_cluster * vol->sector_size; - if (vol->cluster_size & (vol->cluster_size - 1)) { - ntfs_log_debug("Error: %s is not a valid NTFS partition! " - "cluster_size is not a power of 2.\n", - vol->dev->d_name); - return -1; - } - vol->cluster_size_bits = ffs(vol->cluster_size) - 1; - /* - * Need to get the clusters per mft record and handle it if it is - * negative. Then calculate the mft_record_size. A value of 0x80 is - * illegal, thus signed char is actually ok! - */ - c = bs->clusters_per_mft_record; - ntfs_log_debug("ClusterSize = 0x%x\n", (unsigned)vol->cluster_size); - ntfs_log_debug("ClusterSizeBits = %u\n", vol->cluster_size_bits); - ntfs_log_debug("ClustersPerMftRecord = 0x%x\n", c); - /* - * When clusters_per_mft_record is negative, it means that it is to - * be taken to be the negative base 2 logarithm of the mft_record_size - * min bytes. Then: - * mft_record_size = 2^(-clusters_per_mft_record) bytes. - */ - if (c < 0) - vol->mft_record_size = 1 << -c; - else - vol->mft_record_size = c << vol->cluster_size_bits; - if (vol->mft_record_size & (vol->mft_record_size - 1)) { - ntfs_log_debug("Error: %s is not a valid NTFS partition! " - "mft_record_size is not a power of 2.\n", - vol->dev->d_name); - return -1; - } - vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1; - ntfs_log_debug("MftRecordSize = 0x%x\n", (unsigned)vol->mft_record_size); - ntfs_log_debug("MftRecordSizeBits = %u\n", vol->mft_record_size_bits); - /* Same as above for INDX record. */ - c = bs->clusters_per_index_record; - ntfs_log_debug("ClustersPerINDXRecord = 0x%x\n", c); - if (c < 0) - vol->indx_record_size = 1 << -c; - else - vol->indx_record_size = c << vol->cluster_size_bits; - vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1; - ntfs_log_debug("INDXRecordSize = 0x%x\n", (unsigned)vol->indx_record_size); - ntfs_log_debug("INDXRecordSizeBits = %u\n", vol->indx_record_size_bits); - /* - * Work out the size of the MFT mirror in number of mft records. If the - * cluster size is less than or equal to the size taken by four mft - * records, the mft mirror stores the first four mft records. If the - * cluster size is bigger than the size taken by four mft records, the - * mft mirror contains as many mft records as will fit into one - * cluster. - */ - if (vol->cluster_size <= 4 * vol->mft_record_size) - vol->mftmirr_size = 4; - else - vol->mftmirr_size = vol->cluster_size / vol->mft_record_size; - return 0; -} diff --git a/libntfs/collate.c b/libntfs/collate.c deleted file mode 100644 index 2cab50f3..00000000 --- a/libntfs/collate.c +++ /dev/null @@ -1,201 +0,0 @@ -/** - * collate.c - NTFS collation handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2004 Anton Altaparmakov - * Copyright (c) 2005 Yura Pakhuchiy - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STRING_H -#include -#endif - -#include "collate.h" -#include "debug.h" -#include "unistr.h" -#include "logging.h" - -/** - * ntfs_collate_binary - Which of two binary objects should be listed first - * @vol: unused - * @data1: - * @data1_len: - * @data2: - * @data2_len: - * - * Description... - * - * Returns: - */ -static int ntfs_collate_binary(ntfs_volume *vol __attribute__((unused)), - const void *data1, const int data1_len, - const void *data2, const int data2_len) -{ - int rc; - - ntfs_log_trace("Entering.\n"); - rc = memcmp(data1, data2, min(data1_len, data2_len)); - if (!rc && (data1_len != data2_len)) { - if (data1_len < data2_len) - rc = -1; - else - rc = 1; - } - ntfs_log_trace("Done, returning %i.\n", rc); - return rc; -} - -/** - * ntfs_collate_ntofs_ulong - Which of two long ints should be listed first - * @vol: unused - * @data1: - * @data1_len: - * @data2: - * @data2_len: - * - * Description... - * - * Returns: - */ -static int ntfs_collate_ntofs_ulong(ntfs_volume *vol __attribute__((unused)), - const void *data1, const int data1_len, - const void *data2, const int data2_len) -{ - int rc; - u32 d1, d2; - - ntfs_log_trace("Entering.\n"); - if (data1_len != data2_len || data1_len != 4) { - ntfs_log_error("data1_len or/and data2_len not equal to 4.\n"); - return NTFS_COLLATION_ERROR; - } - d1 = le32_to_cpup(data1); - d2 = le32_to_cpup(data2); - if (d1 < d2) - rc = -1; - else { - if (d1 == d2) - rc = 0; - else - rc = 1; - } - ntfs_log_trace("Done, returning %i.\n", rc); - return rc; -} - -/** - * ntfs_collate_file_name - Which of two filenames should be listed first - * @vol: - * @data1: - * @data1_len: unused - * @data2: - * @data2_len: unused - * - * Description... - * - * Returns: - */ -static int ntfs_collate_file_name(ntfs_volume *vol, - const void *data1, const int data1_len __attribute__((unused)), - const void *data2, const int data2_len __attribute__((unused))) -{ - int rc; - - ntfs_log_trace("Entering.\n"); - rc = ntfs_file_values_compare(data1, data2, NTFS_COLLATION_ERROR, - IGNORE_CASE, vol->upcase, vol->upcase_len); - if (!rc) - rc = ntfs_file_values_compare(data1, data2, - NTFS_COLLATION_ERROR, CASE_SENSITIVE, - vol->upcase, vol->upcase_len); - ntfs_log_trace("Done, returning %i.\n", rc); - return rc; -} - -typedef int (*ntfs_collate_func_t)(ntfs_volume *, const void *, const int, - const void *, const int); - -static ntfs_collate_func_t ntfs_do_collate0x0[3] = { - ntfs_collate_binary, - ntfs_collate_file_name, - NULL/*ntfs_collate_unicode_string*/, -}; - -static ntfs_collate_func_t ntfs_do_collate0x1[4] = { - ntfs_collate_ntofs_ulong, - NULL/*ntfs_collate_ntofs_sid*/, - NULL/*ntfs_collate_ntofs_security_hash*/, - NULL/*ntfs_collate_ntofs_ulongs*/, -}; - -/** - * ntfs_collate - collate two data items using a specified collation rule - * @vol: ntfs volume to which the data items belong - * @cr: collation rule to use when comparing the items - * @data1: first data item to collate - * @data1_len: length in bytes of @data1 - * @data2: second data item to collate - * @data2_len: length in bytes of @data2 - * - * Collate the two data items @data1 and @data2 using the collation rule @cr - * and return -1, 0, or 1 if @data1 is found, respectively, to collate before, - * to match, or to collate after @data2. - * - * For speed we use the collation rule @cr as an index into two tables of - * function pointers to call the appropriate collation function. - * - * Return NTFS_COLLATION_ERROR if error occurred. - */ -int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr, - const void *data1, const int data1_len, - const void *data2, const int data2_len) -{ - int i; - - ntfs_log_trace("Entering.\n"); - if (!vol || !data1 || !data2 || data1_len < 0 || data2_len < 0) { - ntfs_log_error("Invalid arguments passed.\n"); - return NTFS_COLLATION_ERROR; - } - /* - * FIXME: At the moment we only support COLLATION_BINARY, - * COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME so we return error - * for everything else. - */ - if (cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG && - cr != COLLATION_FILE_NAME) - goto err; - i = le32_to_cpu(cr); - if (i < 0) - goto err; - if (i <= 0x02) - return ntfs_do_collate0x0[i](vol, data1, data1_len, - data2, data2_len); - if (i < 0x10) - goto err; - i -= 0x10; - if (i <= 3) - return ntfs_do_collate0x1[i](vol, data1, data1_len, - data2, data2_len); -err: - ntfs_log_debug("Unknown collation rule.\n"); - return NTFS_COLLATION_ERROR; -} diff --git a/libntfs/compat.c b/libntfs/compat.c deleted file mode 100644 index acdf4db7..00000000 --- a/libntfs/compat.c +++ /dev/null @@ -1,73 +0,0 @@ -/** - * compat.c - Tweaks for Windows compatibility - * - * Copyright (c) 2002 Richard Russon - * Copyright (c) 2002-2004 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 - */ - -#ifdef WINDOWS - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "compat.h" - -/* TODO: Add check for FFS in the configure script... (AIA) */ - -#ifndef HAVE_FFS -/** - * ffs - Find the first set bit in an int - * @x: - * - * Description... - * - * Returns: - */ -int ffs(int x) -{ - int r = 1; - - if (!x) - return 0; - if (!(x & 0xffff)) { - x >>= 16; - r += 16; - } - if (!(x & 0xff)) { - x >>= 8; - r += 8; - } - if (!(x & 0xf)) { - x >>= 4; - r += 4; - } - if (!(x & 3)) { - x >>= 2; - r += 2; - } - if (!(x & 1)) { - x >>= 1; - r += 1; - } - return r; -} -#endif /* HAVE_FFS */ - -#endif /* WINDOWS */ - diff --git a/libntfs/compress.c b/libntfs/compress.c deleted file mode 100644 index 737186e3..00000000 --- a/libntfs/compress.c +++ /dev/null @@ -1,550 +0,0 @@ -/** - * compress.c - Compressed attribute handling code. Part of the Linux-NTFS - * project. - * - * Copyright (c) 2004-2005 Anton Altaparmakov - * Copyright (c) 2005 Yura Pakhuchiy - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDIO_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif - -#include "attrib.h" -#include "debug.h" -#include "volume.h" -#include "types.h" -#include "layout.h" -#include "runlist.h" -#include "compress.h" -#include "logging.h" - -/** - * enum ntfs_compression_constants - constants used in the compression code - */ -typedef enum { - /* Token types and access mask. */ - NTFS_SYMBOL_TOKEN = 0, - NTFS_PHRASE_TOKEN = 1, - NTFS_TOKEN_MASK = 1, - - /* Compression sub-block constants. */ - NTFS_SB_SIZE_MASK = 0x0fff, - NTFS_SB_SIZE = 0x1000, - NTFS_SB_IS_COMPRESSED = 0x8000, -} ntfs_compression_constants; - -/** - * ntfs_decompress - decompress a compression block into an array of pages - * @dest: buffer to which to write the decompressed data - * @dest_size: size of buffer @dest in bytes - * @cb_start: compression block to decompress - * @cb_size: size of compression block @cb_start in bytes - * - * This decompresses the compression block @cb_start into the destination - * buffer @dest. - * - * @cb_start is a pointer to the compression block which needs decompressing - * and @cb_size is the size of @cb_start in bytes (8-64kiB). - * - * Return 0 if success or -EOVERFLOW on error in the compressed stream. - */ -static int ntfs_decompress(u8 *dest, const u32 dest_size, - u8 *const cb_start, const u32 cb_size) -{ - /* - * Pointers into the compressed data, i.e. the compression block (cb), - * and the therein contained sub-blocks (sb). - */ - u8 *cb_end = cb_start + cb_size; /* End of cb. */ - u8 *cb = cb_start; /* Current position in cb. */ - u8 *cb_sb_start = cb; /* Beginning of the current sb in the cb. */ - u8 *cb_sb_end; /* End of current sb / beginning of next sb. */ - /* Variables for uncompressed data / destination. */ - u8 *dest_end = dest + dest_size; /* End of dest buffer. */ - u8 *dest_sb_start; /* Start of current sub-block in dest. */ - u8 *dest_sb_end; /* End of current sb in dest. */ - /* Variables for tag and token parsing. */ - u8 tag; /* Current tag. */ - int token; /* Loop counter for the eight tokens in tag. */ - - ntfs_log_trace("Entering, cb_size = 0x%x.\n", (unsigned)cb_size); -do_next_sb: - ntfs_log_debug("Beginning sub-block at offset = 0x%x in the cb.\n", - cb - cb_start); - /* - * Have we reached the end of the compression block or the end of the - * decompressed data? The latter can happen for example if the current - * position in the compression block is one byte before its end so the - * first two checks do not detect it. - */ - if (cb == cb_end || !le16_to_cpup((u16*)cb) || dest == dest_end) { - ntfs_log_debug("Completed. Returning success (0).\n"); - return 0; - } - /* Setup offset for the current sub-block destination. */ - dest_sb_start = dest; - dest_sb_end = dest + NTFS_SB_SIZE; - /* Check that we are still within allowed boundaries. */ - if (dest_sb_end > dest_end) - goto return_overflow; - /* Does the minimum size of a compressed sb overflow valid range? */ - if (cb + 6 > cb_end) - goto return_overflow; - /* Setup the current sub-block source pointers and validate range. */ - cb_sb_start = cb; - cb_sb_end = cb_sb_start + (le16_to_cpup((u16*)cb) & NTFS_SB_SIZE_MASK) - + 3; - if (cb_sb_end > cb_end) - goto return_overflow; - /* Now, we are ready to process the current sub-block (sb). */ - if (!(le16_to_cpup((u16*)cb) & NTFS_SB_IS_COMPRESSED)) { - ntfs_log_debug("Found uncompressed sub-block.\n"); - /* This sb is not compressed, just copy it into destination. */ - /* Advance source position to first data byte. */ - cb += 2; - /* An uncompressed sb must be full size. */ - if (cb_sb_end - cb != NTFS_SB_SIZE) - goto return_overflow; - /* Copy the block and advance the source position. */ - memcpy(dest, cb, NTFS_SB_SIZE); - cb += NTFS_SB_SIZE; - /* Advance destination position to next sub-block. */ - dest += NTFS_SB_SIZE; - goto do_next_sb; - } - ntfs_log_debug("Found compressed sub-block.\n"); - /* This sb is compressed, decompress it into destination. */ - /* Forward to the first tag in the sub-block. */ - cb += 2; -do_next_tag: - if (cb == cb_sb_end) { - /* Check if the decompressed sub-block was not full-length. */ - if (dest < dest_sb_end) { - int nr_bytes = dest_sb_end - dest; - - ntfs_log_debug("Filling incomplete sub-block with zeroes.\n"); - /* Zero remainder and update destination position. */ - memset(dest, 0, nr_bytes); - dest += nr_bytes; - } - /* We have finished the current sub-block. */ - goto do_next_sb; - } - /* Check we are still in range. */ - if (cb > cb_sb_end || dest > dest_sb_end) - goto return_overflow; - /* Get the next tag and advance to first token. */ - tag = *cb++; - /* Parse the eight tokens described by the tag. */ - for (token = 0; token < 8; token++, tag >>= 1) { - u16 lg, pt, length, max_non_overlap; - register u16 i; - u8 *dest_back_addr; - - /* Check if we are done / still in range. */ - if (cb >= cb_sb_end || dest > dest_sb_end) - break; - /* Determine token type and parse appropriately.*/ - if ((tag & NTFS_TOKEN_MASK) == NTFS_SYMBOL_TOKEN) { - /* - * We have a symbol token, copy the symbol across, and - * advance the source and destination positions. - */ - *dest++ = *cb++; - /* Continue with the next token. */ - continue; - } - /* - * We have a phrase token. Make sure it is not the first tag in - * the sb as this is illegal and would confuse the code below. - */ - if (dest == dest_sb_start) - goto return_overflow; - /* - * Determine the number of bytes to go back (p) and the number - * of bytes to copy (l). We use an optimized algorithm in which - * we first calculate log2(current destination position in sb), - * which allows determination of l and p in O(1) rather than - * O(n). We just need an arch-optimized log2() function now. - */ - lg = 0; - for (i = dest - dest_sb_start - 1; i >= 0x10; i >>= 1) - lg++; - /* Get the phrase token into i. */ - pt = le16_to_cpup((u16*)cb); - /* - * Calculate starting position of the byte sequence in - * the destination using the fact that p = (pt >> (12 - lg)) + 1 - * and make sure we don't go too far back. - */ - dest_back_addr = dest - (pt >> (12 - lg)) - 1; - if (dest_back_addr < dest_sb_start) - goto return_overflow; - /* Now calculate the length of the byte sequence. */ - length = (pt & (0xfff >> lg)) + 3; - /* Verify destination is in range. */ - if (dest + length > dest_sb_end) - goto return_overflow; - /* The number of non-overlapping bytes. */ - max_non_overlap = dest - dest_back_addr; - if (length <= max_non_overlap) { - /* The byte sequence doesn't overlap, just copy it. */ - memcpy(dest, dest_back_addr, length); - /* Advance destination pointer. */ - dest += length; - } else { - /* - * The byte sequence does overlap, copy non-overlapping - * part and then do a slow byte by byte copy for the - * overlapping part. Also, advance the destination - * pointer. - */ - memcpy(dest, dest_back_addr, max_non_overlap); - dest += max_non_overlap; - dest_back_addr += max_non_overlap; - length -= max_non_overlap; - while (length--) - *dest++ = *dest_back_addr++; - } - /* Advance source position and continue with the next token. */ - cb += 2; - } - /* No tokens left in the current tag. Continue with the next tag. */ - goto do_next_tag; -return_overflow: - ntfs_log_debug("Failed. Returning -EOVERFLOW.\n"); - errno = EOVERFLOW; - return -1; -} - -/** - * ntfs_is_cb_compressed - internal function, do not use - * - * This is a very specialised function determining if a cb is compressed or - * uncompressed. It is assumed that checking for a sparse cb has already been - * performed and that the cb is not sparse. It makes all sorts of other - * assumptions as well and hence it is not useful anywhere other than where it - * is used at the moment. Please, do not make this function available for use - * outside of compress.c as it is bound to confuse people and not do what they - * want. - * - * Return TRUE on errors so that the error will be detected later on in the - * code. Might be a bit confusing to debug but there really should never be - * errors coming from here. - */ -static __inline__ BOOL ntfs_is_cb_compressed(ntfs_attr *na, - runlist_element *rl, VCN cb_start_vcn, int cb_clusters) -{ - /* - * The simplest case: the run starting at @cb_start_vcn contains - * @cb_clusters clusters which are all not sparse, thus the cb is not - * compressed. - */ -restart: - cb_clusters -= rl->length - (cb_start_vcn - rl->vcn); - while (cb_clusters > 0) { - /* Go to the next run. */ - rl++; - /* Map the next runlist fragment if it is not mapped. */ - if (rl->lcn < LCN_HOLE || !rl->length) { - cb_start_vcn = rl->vcn; - rl = ntfs_attr_find_vcn(na, rl->vcn); - if (!rl || rl->lcn < LCN_HOLE || !rl->length) - return TRUE; - /* - * If the runs were merged need to deal with the - * resulting partial run so simply restart. - */ - if (rl->vcn < cb_start_vcn) - goto restart; - } - /* If the current run is sparse, the cb is compressed. */ - if (rl->lcn == LCN_HOLE) - return TRUE; - /* If the whole cb is not sparse, it is not compressed. */ - if (rl->length >= cb_clusters) - return FALSE; - cb_clusters -= rl->length; - }; - /* All cb_clusters were not sparse thus the cb is not compressed. */ - return FALSE; -} - -/** - * ntfs_compressed_attr_pread - read from a compressed attribute - * @na: ntfs attribute to read from - * @pos: byte position in the attribute to begin reading from - * @count: number of bytes to read - * @b: output data buffer - * - * NOTE: You probably want to be using attrib.c::ntfs_attr_pread() instead. - * - * This function will read @count bytes starting at offset @pos from the - * compressed ntfs attribute @na into the data buffer @b. - * - * On success, return the number of successfully read bytes. If this number - * is lower than @count this means that the read reached end of file or that - * an error was encountered during the read so that the read is partial. - * 0 means end of file or nothing was read (also return 0 when @count is 0). - * - * On error and nothing has been read, return -1 with errno set appropriately - * to the return code of ntfs_pread(), or to EINVAL in case of invalid - * arguments. - */ -s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b) -{ - s64 br, to_read, ofs, total, total2; - u64 cb_size_mask; - VCN start_vcn, vcn, end_vcn; - ntfs_volume *vol; - runlist_element *rl; - u8 *dest, *cb, *cb_pos, *cb_end; - u32 cb_size; - int err; - unsigned int nr_cbs, cb_clusters; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count 0x%llx.\n", - (unsigned long long)na->ni->mft_no, na->type, - (long long)pos, (long long)count); - if (!na || !NAttrCompressed(na) || !na->ni || !na->ni->vol || !b || - pos < 0 || count < 0) { - errno = EINVAL; - return -1; - } - /* - * Encrypted attributes are not supported. We return access denied, - * which is what Windows NT4 does, too. - */ - if (NAttrEncrypted(na)) { - errno = EACCES; - return -1; - } - if (!count) - return 0; - /* Truncate reads beyond end of attribute. */ - if (pos + count > na->data_size) { - if (pos >= na->data_size) { - return 0; - } - count = na->data_size - pos; - } - /* If it is a resident attribute, simply use ntfs_attr_pread(). */ - if (!NAttrNonResident(na)) - return ntfs_attr_pread(na, pos, count, b); - total = total2 = 0; - /* Zero out reads beyond initialized size. */ - if (pos + count > na->initialized_size) { - if (pos >= na->initialized_size) { - memset(b, 0, count); - return count; - } - total2 = pos + count - na->initialized_size; - count -= total2; - memset((u8*)b + count, 0, total2); - } - vol = na->ni->vol; - cb_size = na->compression_block_size; - cb_size_mask = cb_size - 1UL; - cb_clusters = na->compression_block_clusters; - /* Need a temporary buffer for each loaded compression block. */ - cb = malloc(cb_size); - if (!cb) - return -1; - /* Need a temporary buffer for each uncompressed block. */ - dest = malloc(cb_size); - if (!dest) { - err = errno; - free(cb); - errno = err; - return -1; - } - /* - * The first vcn in the first compression block (cb) which we need to - * decompress. - */ - start_vcn = (pos & ~cb_size_mask) >> vol->cluster_size_bits; - /* Offset in the uncompressed cb at which to start reading data. */ - ofs = pos & cb_size_mask; - /* - * The first vcn in the cb after the last cb which we need to - * decompress. - */ - end_vcn = ((pos + count + cb_size - 1) & ~cb_size_mask) >> - vol->cluster_size_bits; - /* Number of compression blocks (cbs) in the wanted vcn range. */ - nr_cbs = (end_vcn - start_vcn) << vol->cluster_size_bits >> - na->compression_block_size_bits; - cb_end = cb + cb_size; -do_next_cb: - nr_cbs--; - cb_pos = cb; - vcn = start_vcn; - start_vcn += cb_clusters; - - /* Check whether the compression block is sparse. */ - rl = ntfs_attr_find_vcn(na, vcn); - if (!rl || rl->lcn < LCN_HOLE) { - free(cb); - free(dest); - if (total) - return total; - /* FIXME: Do we want EIO or the error code? (AIA) */ - errno = EIO; - return -1; - } - if (rl->lcn == LCN_HOLE) { - /* Sparse cb, zero out destination range overlapping the cb. */ - ntfs_log_debug("Found sparse compression block.\n"); - to_read = min(count, cb_size - ofs); - memset(b, 0, to_read); - ofs = 0; - total += to_read; - count -= to_read; - b = (u8*)b + to_read; - } else if (!ntfs_is_cb_compressed(na, rl, vcn, cb_clusters)) { - s64 tdata_size, tinitialized_size; - /* - * Uncompressed cb, read it straight into the destination range - * overlapping the cb. - */ - ntfs_log_debug("Found uncompressed compression block.\n"); - /* - * Read the uncompressed data into the destination buffer. - * NOTE: We cheat a little bit here by marking the attribute as - * not compressed in the ntfs_attr structure so that we can - * read the data by simply using ntfs_attr_pread(). (-8 - * NOTE: we have to modify data_size and initialized_size - * temporarily as well... - */ - to_read = min(count, cb_size - ofs); - ofs += vcn << vol->cluster_size_bits; - NAttrClearCompressed(na); - tdata_size = na->data_size; - tinitialized_size = na->initialized_size; - na->data_size = na->initialized_size = na->allocated_size; - do { - br = ntfs_attr_pread(na, ofs, to_read, b); - if (br < 0) { - err = errno; - na->data_size = tdata_size; - na->initialized_size = tinitialized_size; - NAttrSetCompressed(na); - free(cb); - free(dest); - if (total) - return total; - errno = err; - return br; - } - total += br; - count -= br; - b = (u8*)b + br; - to_read -= br; - ofs += br; - } while (to_read > 0); - na->data_size = tdata_size; - na->initialized_size = tinitialized_size; - NAttrSetCompressed(na); - ofs = 0; - } else { - s64 tdata_size, tinitialized_size; - - /* - * Compressed cb, decompress it into the temporary buffer, then - * copy the data to the destination range overlapping the cb. - */ - ntfs_log_debug("Found compressed compression block.\n"); - /* - * Read the compressed data into the temporary buffer. - * NOTE: We cheat a little bit here by marking the attribute as - * not compressed in the ntfs_attr structure so that we can - * read the raw, compressed data by simply using - * ntfs_attr_pread(). (-8 - * NOTE: We have to modify data_size and initialized_size - * temporarily as well... - */ - to_read = cb_size; - NAttrClearCompressed(na); - tdata_size = na->data_size; - tinitialized_size = na->initialized_size; - na->data_size = na->initialized_size = na->allocated_size; - do { - br = ntfs_attr_pread(na, - (vcn << vol->cluster_size_bits) + - (cb_pos - cb), to_read, cb_pos); - if (br < 0) { - err = errno; - na->data_size = tdata_size; - na->initialized_size = tinitialized_size; - NAttrSetCompressed(na); - free(cb); - free(dest); - if (total) - return total; - errno = err; - return br; - } - cb_pos += br; - to_read -= br; - } while (to_read > 0); - na->data_size = tdata_size; - na->initialized_size = tinitialized_size; - NAttrSetCompressed(na); - /* Just a precaution. */ - if (cb_pos + 2 <= cb_end) - *(u16*)cb_pos = 0; - ntfs_log_debug("Successfully read the compression block.\n"); - if (ntfs_decompress(dest, cb_size, cb, cb_size) < 0) { - err = errno; - free(cb); - free(dest); - if (total) - return total; - errno = err; - return -1; - } - to_read = min(count, cb_size - ofs); - memcpy(b, dest + ofs, to_read); - total += to_read; - count -= to_read; - b = (u8*)b + to_read; - ofs = 0; - } - /* Do we have more work to do? */ - if (nr_cbs) - goto do_next_cb; - /* We no longer need the buffers. */ - free(cb); - free(dest); - /* Return number of bytes read. */ - return total + total2; -} diff --git a/libntfs/debug.c b/libntfs/debug.c deleted file mode 100644 index c4443023..00000000 --- a/libntfs/debug.c +++ /dev/null @@ -1,72 +0,0 @@ -/** - * debug.c - Debugging output functions. Part of the Linux-NTFS project. - * - * Copyright (c) 2002-2004 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_ERRNO_H -#include -#endif - -#include "types.h" -#include "runlist.h" -#include "debug.h" -#include "logging.h" - -#ifndef NTFS_DISABLE_DEBUG_LOGGING -/** - * ntfs_debug_runlist_dump - Dump a runlist. - * @rl: - * - * Description... - * - * Returns: - */ -void ntfs_debug_runlist_dump(const runlist_element *rl) -{ - int i = 0; - const char *lcn_str[5] = { "LCN_HOLE ", "LCN_RL_NOT_MAPPED", - "LCN_ENOENT ", "LCN_EINVAL ", - "LCN_unknown " }; - - ntfs_log_debug("NTFS-fs DEBUG: Dumping runlist (values in hex):\n"); - if (!rl) { - ntfs_log_debug("Run list not present.\n"); - return; - } - ntfs_log_debug("VCN LCN Run length\n"); - do { - LCN lcn = (rl + i)->lcn; - - if (lcn < (LCN)0) { - int idx = -lcn - 1; - - if (idx > -LCN_EINVAL - 1) - idx = 4; - ntfs_log_debug("%-16llx %s %-16llx%s\n", rl[i].vcn, lcn_str[idx], rl[i].length, rl[i].length ? "" : " (runlist end)"); - } else - ntfs_log_debug("%-16llx %-16llx %-16llx%s\n", rl[i].vcn, rl[i].lcn, rl[i].length, rl[i].length ? "" : " (runlist end)"); - } while (rl[i++].length); -} - -#endif - diff --git a/libntfs/device.c b/libntfs/device.c deleted file mode 100644 index 23d2c511..00000000 --- a/libntfs/device.c +++ /dev/null @@ -1,727 +0,0 @@ -/** - * device.c - Low level device io functions. Part of the Linux-NTFS project. - * - * Copyright (c) 2004-2006 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_STDIO_H -#include -#endif -#ifdef HAVE_SYS_TYPES_H -#include -#endif -#ifdef HAVE_SYS_STAT_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include -#endif -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#ifdef HAVE_SYS_PARAM_H -#include -#endif -#ifdef HAVE_SYS_MOUNT_H -#include -#endif -#ifdef HAVE_LINUX_FD_H -#include -#endif -#ifdef HAVE_LINUX_HDREG_H -#include -#endif - -#include "types.h" -#include "mst.h" -#include "debug.h" -#include "device.h" -#include "logging.h" - -#if defined(linux) && defined(_IO) && !defined(BLKGETSIZE) -#define BLKGETSIZE _IO(0x12,96) /* Get device size in 512-byte blocks. */ -#endif -#if defined(linux) && defined(_IOR) && !defined(BLKGETSIZE64) -#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* Get device size in bytes. */ -#endif -#if defined(linux) && !defined(HDIO_GETGEO) -#define HDIO_GETGEO 0x0301 /* Get device geometry. */ -#endif -#if defined(linux) && defined(_IO) && !defined(BLKSSZGET) -# define BLKSSZGET _IO(0x12,104) /* Get device sector size in bytes. */ -#endif -#if defined(linux) && defined(_IO) && !defined(BLKBSZSET) -# define BLKBSZSET _IOW(0x12,113,size_t) /* Set device block size in bytes. */ -#endif - -/** - * ntfs_device_alloc - allocate an ntfs device structure and pre-initialize it - * @name: name of the device (must be present) - * @state: initial device state (usually zero) - * @dops: ntfs device operations to use with the device (must be present) - * @priv_data: pointer to private data (optional) - * - * Allocate an ntfs device structure and pre-initialize it with the user- - * specified device operations @dops, device state @state, device name @name, - * and optional private data @priv_data. - * - * Note, @name is copied and can hence be freed after this functions returns. - * - * On success return a pointer to the allocated ntfs device structure and on - * error return NULL with errno set to the error code returned by malloc(). - */ -struct ntfs_device *ntfs_device_alloc(const char *name, const long state, - struct ntfs_device_operations *dops, void *priv_data) -{ - struct ntfs_device *dev; - - if (!name) { - errno = EINVAL; - return NULL; - } - - dev = (struct ntfs_device *)malloc(sizeof(struct ntfs_device)); - if (dev) { - if (!(dev->d_name = strdup(name))) { - int eo = errno; - free(dev); - errno = eo; - return NULL; - } - dev->d_ops = dops; - dev->d_state = state; - dev->d_private = priv_data; - } - return dev; -} - -/** - * ntfs_device_free - free an ntfs device structure - * @dev: ntfs device structure to free - * - * Free the ntfs device structure @dev. - * - * Return 0 on success or -1 on error with errno set to the error code. The - * following error codes are defined: - * EINVAL Invalid pointer @dev. - * EBUSY Device is still open. Close it before freeing it! - */ -int ntfs_device_free(struct ntfs_device *dev) -{ - if (!dev) { - errno = EINVAL; - return -1; - } - if (NDevOpen(dev)) { - errno = EBUSY; - return -1; - } - free(dev->d_name); - free(dev); - return 0; -} - -/** - * ntfs_pread - positioned read from disk - * @dev: device to read from - * @pos: position in device to read from - * @count: number of bytes to read - * @b: output data buffer - * - * This function will read @count bytes from device @dev at position @pos into - * the data buffer @b. - * - * On success, return the number of successfully read bytes. If this number is - * lower than @count this means that we have either reached end of file or - * encountered an error during the read so that the read is partial. 0 means - * end of file or nothing to read (@count is 0). - * - * On error and nothing has been read, return -1 with errno set appropriately - * to the return code of either seek, read, or set to EINVAL in case of - * invalid arguments. - */ -s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, void *b) -{ - s64 br, total; - struct ntfs_device_operations *dops; - - ntfs_log_trace("Entering for pos 0x%llx, count 0x%llx.\n", pos, count); - if (!b || count < 0 || pos < 0) { - errno = EINVAL; - return -1; - } - if (!count) - return 0; - dops = dev->d_ops; - /* Locate to position. */ - if (dops->seek(dev, pos, SEEK_SET) == (off_t)-1) { - ntfs_log_perror("ntfs_pread: device seek to 0x%llx returned error", - pos); - return -1; - } - /* Read the data. */ - for (total = 0; count; count -= br, total += br) { - br = dops->read(dev, (char*)b + total, count); - /* If everything ok, continue. */ - if (br > 0) - continue; - /* If EOF or error return number of bytes read. */ - if (!br || total) - return total; - /* Nothing read and error, return error status. */ - return br; - } - /* Finally, return the number of bytes read. */ - return total; -} - -/** - * ntfs_pwrite - positioned write to disk - * @dev: device to write to - * @pos: position in file descriptor to write to - * @count: number of bytes to write - * @b: data buffer to write to disk - * - * This function will write @count bytes from data buffer @b to the device @dev - * at position @pos. - * - * On success, return the number of successfully written bytes. If this number - * is lower than @count this means that the write has been interrupted in - * flight or that an error was encountered during the write so that the write - * is partial. 0 means nothing was written (also return 0 when @count is 0). - * - * On error and nothing has been written, return -1 with errno set - * appropriately to the return code of either seek, write, or set - * to EINVAL in case of invalid arguments. - */ -s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, - const void *b) -{ - s64 written, total; - struct ntfs_device_operations *dops; - - ntfs_log_trace("Entering for pos 0x%llx, count 0x%llx.\n", pos, count); - if (!b || count < 0 || pos < 0) { - errno = EINVAL; - return -1; - } - if (!count) - return 0; - if (NDevReadOnly(dev)) { - errno = EROFS; - return -1; - } - dops = dev->d_ops; - /* Locate to position. */ - if (dops->seek(dev, pos, SEEK_SET) == (off_t)-1) { - ntfs_log_perror("ntfs_pwrite: seek to 0x%llx returned error", - pos); - return -1; - } - NDevSetDirty(dev); - /* Write the data. */ - for (total = 0; count; count -= written, total += written) { - written = dops->write(dev, (const char*)b + total, count); - /* If everything ok, continue. */ - if (written > 0) - continue; - /* - * If nothing written or error return number of bytes written. - */ - if (!written || total) - break; - /* Nothing written and error, return error status. */ - return written; - } - /* Finally, return the number of bytes written. */ - return total; -} - -/** - * ntfs_mst_pread - multi sector transfer (mst) positioned read - * @dev: device to read from - * @pos: position in file descriptor to read from - * @count: number of blocks to read - * @bksize: size of each block that needs mst deprotecting - * @b: output data buffer - * - * Multi sector transfer (mst) positioned read. This function will read @count - * blocks of size @bksize bytes each from device @dev at position @pos into the - * the data buffer @b. - * - * On success, return the number of successfully read blocks. If this number is - * lower than @count this means that we have reached end of file, that the read - * was interrupted, or that an error was encountered during the read so that - * the read is partial. 0 means end of file or nothing was read (also return 0 - * when @count or @bksize are 0). - * - * On error and nothing was read, return -1 with errno set appropriately to the - * return code of either seek, read, or set to EINVAL in case of invalid - * arguments. - * - * NOTE: If an incomplete multi sector transfer has been detected the magic - * will have been changed to magic_BAAD but no error will be returned. Thus it - * is possible that we return count blocks as being read but that any number - * (between zero and count!) of these blocks is actually subject to a multi - * sector transfer error. This should be detected by the caller by checking for - * the magic being "BAAD". - */ -s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count, - const u32 bksize, void *b) -{ - s64 br, i; - - if (bksize & (bksize - 1) || bksize % NTFS_BLOCK_SIZE) { - errno = EINVAL; - return -1; - } - /* Do the read. */ - br = ntfs_pread(dev, pos, count * bksize, b); - if (br < 0) - return br; - /* - * Apply fixups to successfully read data, disregarding any errors - * returned from the MST fixup function. This is because we want to - * fixup everything possible and we rely on the fact that the "BAAD" - * magic will be detected later on. - */ - count = br / bksize; - for (i = 0; i < count; ++i) - ntfs_mst_post_read_fixup((NTFS_RECORD*) - ((u8*)b + i * bksize), bksize); - /* Finally, return the number of complete blocks read. */ - return count; -} - -/** - * ntfs_mst_pwrite - multi sector transfer (mst) positioned write - * @dev: device to write to - * @pos: position in file descriptor to write to - * @count: number of blocks to write - * @bksize: size of each block that needs mst protecting - * @b: data buffer to write to disk - * - * Multi sector transfer (mst) positioned write. This function will write - * @count blocks of size @bksize bytes each from data buffer @b to the device - * @dev at position @pos. - * - * On success, return the number of successfully written blocks. If this number - * is lower than @count this means that the write has been interrupted or that - * an error was encountered during the write so that the write is partial. 0 - * means nothing was written (also return 0 when @count or @bksize are 0). - * - * On error and nothing has been written, return -1 with errno set - * appropriately to the return code of either seek, write, or set - * to EINVAL in case of invalid arguments. - * - * NOTE: We mst protect the data, write it, then mst deprotect it using a quick - * deprotect algorithm (no checking). This saves us from making a copy before - * the write and at the same time causes the usn to be incremented in the - * buffer. This conceptually fits in better with the idea that cached data is - * always deprotected and protection is performed when the data is actually - * going to hit the disk and the cache is immediately deprotected again - * simulating an mst read on the written data. This way cache coherency is - * achieved. - */ -s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, - const u32 bksize, void *b) -{ - s64 written, i; - - if (count < 0 || bksize % NTFS_BLOCK_SIZE) { - errno = EINVAL; - return -1; - } - if (!count) - return 0; - /* Prepare data for writing. */ - for (i = 0; i < count; ++i) { - int err; - - err = ntfs_mst_pre_write_fixup((NTFS_RECORD*) - ((u8*)b + i * bksize), bksize); - if (err < 0) { - /* Abort write at this position. */ - if (!i) - return err; - count = i; - break; - } - } - /* Write the prepared data. */ - written = ntfs_pwrite(dev, pos, count * bksize, b); - /* Quickly deprotect the data again. */ - for (i = 0; i < count; ++i) - ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)b + i * bksize)); - if (written <= 0) - return written; - /* Finally, return the number of complete blocks written. */ - return written / bksize; -} - -/** - * ntfs_cluster_read - read ntfs clusters - * @vol: volume to read from - * @lcn: starting logical cluster number - * @count: number of clusters to read - * @b: output data buffer - * - * Read @count ntfs clusters starting at logical cluster number @lcn from - * volume @vol into buffer @b. Return number of clusters read or -1 on error, - * with errno set to the error code. - */ -s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, const s64 count, - void *b) -{ - s64 br; - - if (!vol || lcn < 0 || count < 0) { - errno = EINVAL; - return -1; - } - if (vol->nr_clusters < lcn + count) { - errno = ESPIPE; - return -1; - } - br = ntfs_pread(vol->dev, lcn << vol->cluster_size_bits, - count << vol->cluster_size_bits, b); - if (br < 0) { - ntfs_log_perror("Error reading cluster(s)"); - return br; - } - return br >> vol->cluster_size_bits; -} - -/** - * ntfs_cluster_write - write ntfs clusters - * @vol: volume to write to - * @lcn: starting logical cluster number - * @count: number of clusters to write - * @b: data buffer to write to disk - * - * Write @count ntfs clusters starting at logical cluster number @lcn from - * buffer @b to volume @vol. Return the number of clusters written or -1 on - * error, with errno set to the error code. - */ -s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn, - const s64 count, const void *b) -{ - s64 bw; - - if (!vol || lcn < 0 || count < 0) { - errno = EINVAL; - return -1; - } - if (vol->nr_clusters < lcn + count) { - errno = ESPIPE; - return -1; - } - if (!NVolReadOnly(vol)) - bw = ntfs_pwrite(vol->dev, lcn << vol->cluster_size_bits, - count << vol->cluster_size_bits, b); - else - bw = count << vol->cluster_size_bits; - if (bw < 0) { - ntfs_log_perror("Error writing cluster(s)"); - return bw; - } - return bw >> vol->cluster_size_bits; -} - -/** - * ntfs_device_offset_valid - test if a device offset is valid - * @dev: open device - * @ofs: offset to test for validity - * - * Test if the offset @ofs is an existing location on the device described - * by the open device structure @dev. - * - * Return 0 if it is valid and -1 if it is not valid. - */ -static inline int ntfs_device_offset_valid(struct ntfs_device *dev, s64 ofs) -{ - char ch; - - if (dev->d_ops->seek(dev, ofs, SEEK_SET) >= 0 && - dev->d_ops->read(dev, &ch, 1) == 1) - return 0; - return -1; -} - -/** - * ntfs_device_size_get - return the size of a device in blocks - * @dev: open device - * @block_size: block size in bytes in which to return the result - * - * Return the number of @block_size sized blocks in the device described by the - * open device @dev. - * - * Adapted from e2fsutils-1.19, Copyright (C) 1995 Theodore Ts'o. - * - * On error return -1 with errno set to the error code. - */ -s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size) -{ - s64 high, low; - - if (!dev || block_size <= 0 || (block_size - 1) & block_size) { - errno = EINVAL; - return -1; - } -#ifdef BLKGETSIZE64 - { u64 size; - - if (dev->d_ops->ioctl(dev, BLKGETSIZE64, &size) >= 0) { - ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu (0x%llx)\n", - (unsigned long long)size, - (unsigned long long)size); - return (s64)size / block_size; - } - } -#endif -#ifdef BLKGETSIZE - { unsigned long size; - - if (dev->d_ops->ioctl(dev, BLKGETSIZE, &size) >= 0) { - ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu (0x%lx)\n", - size, size); - return (s64)size * 512 / block_size; - } - } -#endif -#ifdef FDGETPRM - { struct floppy_struct this_floppy; - - if (dev->d_ops->ioctl(dev, FDGETPRM, &this_floppy) >= 0) { - ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu (0x%lx)\n", - (unsigned long)this_floppy.size, - (unsigned long)this_floppy.size); - return (s64)this_floppy.size * 512 / block_size; - } - } -#endif - /* - * We couldn't figure it out by using a specialized ioctl, - * so do binary search to find the size of the device. - */ - low = 0LL; - for (high = 1024LL; !ntfs_device_offset_valid(dev, high); high <<= 1) - low = high; - while (low < high - 1LL) { - const s64 mid = (low + high) / 2; - - if (!ntfs_device_offset_valid(dev, mid)) - low = mid; - else - high = mid; - } - dev->d_ops->seek(dev, 0LL, SEEK_SET); - return (low + 1LL) / block_size; -} - -/** - * ntfs_device_partition_start_sector_get - get starting sector of a partition - * @dev: open device - * - * On success, return the starting sector of the partition @dev in the parent - * block device of @dev. On error return -1 with errno set to the error code. - * - * The following error codes are defined: - * EINVAL Input parameter error - * EOPNOTSUPP System does not support HDIO_GETGEO ioctl - * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO - */ -s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev) -{ - if (!dev) { - errno = EINVAL; - return -1; - } -#ifdef HDIO_GETGEO - { struct hd_geometry geo; - - if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { - ntfs_log_debug("HDIO_GETGEO start_sect = %lu (0x%lx)\n", - geo.start, geo.start); - return geo.start; - } - } -#else - errno = EOPNOTSUPP; -#endif - return -1; -} - -/** - * ntfs_device_heads_get - get number of heads of device - * @dev: open device - * - * On success, return the number of heads on the device @dev. On error return - * -1 with errno set to the error code. - * - * The following error codes are defined: - * EINVAL Input parameter error - * EOPNOTSUPP System does not support HDIO_GETGEO ioctl - * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO - */ -int ntfs_device_heads_get(struct ntfs_device *dev) -{ - if (!dev) { - errno = EINVAL; - return -1; - } -#ifdef HDIO_GETGEO - { struct hd_geometry geo; - - if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { - ntfs_log_debug("HDIO_GETGEO heads = %u (0x%x)\n", - (unsigned)geo.heads, - (unsigned)geo.heads); - return geo.heads; - } - } -#else - errno = EOPNOTSUPP; -#endif - return -1; -} - -/** - * ntfs_device_sectors_per_track_get - get number of sectors per track of device - * @dev: open device - * - * On success, return the number of sectors per track on the device @dev. On - * error return -1 with errno set to the error code. - * - * The following error codes are defined: - * EINVAL Input parameter error - * EOPNOTSUPP System does not support HDIO_GETGEO ioctl - * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO - */ -int ntfs_device_sectors_per_track_get(struct ntfs_device *dev) -{ - if (!dev) { - errno = EINVAL; - return -1; - } -#ifdef HDIO_GETGEO - { struct hd_geometry geo; - - if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { - ntfs_log_debug("HDIO_GETGEO sectors_per_track = %u (0x%x)\n", - (unsigned)geo.sectors, - (unsigned)geo.sectors); - return geo.sectors; - } - } -#else - errno = EOPNOTSUPP; -#endif - return -1; -} - -/** - * ntfs_device_sector_size_get - get sector size of a device - * @dev: open device - * - * On success, return the sector size in bytes of the device @dev. - * On error return -1 with errno set to the error code. - * - * The following error codes are defined: - * EINVAL Input parameter error - * EOPNOTSUPP System does not support BLKSSZGET ioctl - * ENOTTY @dev is a file or a device not supporting BLKSSZGET - */ -int ntfs_device_sector_size_get(struct ntfs_device *dev) -{ - if (!dev) { - errno = EINVAL; - return -1; - } -#ifdef BLKSSZGET - { - int sect_size = 0; - - if (!dev->d_ops->ioctl(dev, BLKSSZGET, §_size)) { - ntfs_log_debug("BLKSSZGET sector size = %d bytes\n", - sect_size); - return sect_size; - } - } -#else - errno = EOPNOTSUPP; -#endif - return -1; -} - -/** - * ntfs_device_block_size_set - set block size of a device - * @dev: open device - * @block_size: block size to set @dev to - * - * On success, return 0. - * On error return -1 with errno set to the error code. - * - * The following error codes are defined: - * EINVAL Input parameter error - * EOPNOTSUPP System does not support BLKBSZSET ioctl - * ENOTTY @dev is a file or a device not supporting BLKBSZSET - */ -int ntfs_device_block_size_set(struct ntfs_device *dev, - int block_size __attribute__((unused))) -{ - if (!dev) { - errno = EINVAL; - return -1; - } -#ifdef BLKBSZSET - { - size_t s_block_size = block_size; - if (!dev->d_ops->ioctl(dev, BLKBSZSET, &s_block_size)) { - ntfs_log_debug("Used BLKBSZSET to set block size to " - "%d bytes.\n", block_size); - return 0; - } - /* If not a block device, pretend it was successful. */ - if (!NDevBlock(dev)) - return 0; - } -#else - /* If not a block device, pretend it was successful. */ - if (!NDevBlock(dev)) - return 0; - errno = EOPNOTSUPP; -#endif - return -1; -} diff --git a/libntfs/device_io.c b/libntfs/device_io.c deleted file mode 100644 index 9a044d61..00000000 --- a/libntfs/device_io.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * device_io.c - Default device io operations. Part of the Linux-NTFS project. - * - * Copyright (c) 2003 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 "config.h" - -#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS - -#ifndef __CYGWIN32__ - -/* Not on Cygwin; use standard Unix style low level device operations. */ -#include "unix_io.c" - -#else /* __CYGWIN32__ */ - -/* On Cygwin; use Win32 low level device operations. */ -#include "win32_io.c" - -#endif /* __CYGWIN32__ */ - -#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */ diff --git a/libntfs/dir.c b/libntfs/dir.c deleted file mode 100644 index aef26af0..00000000 --- a/libntfs/dir.c +++ /dev/null @@ -1,2125 +0,0 @@ -/** - * dir.c - Directory handling code. Part of the Linux-NTFS project. - * - * Copyright (c) 2002-2005 Anton Altaparmakov - * Copyright (c) 2005-2006 Yura Pakhuchiy - * Copyright (c) 2004-2005 Richard Russon - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_SYS_STAT_H -#include -#endif - -#ifdef HAVE_SYS_SYSMACROS_H -#include -#endif - -#include "types.h" -#include "debug.h" -#include "attrib.h" -#include "inode.h" -#include "dir.h" -#include "volume.h" -#include "mft.h" -#include "index.h" -#include "ntfstime.h" -#include "lcnalloc.h" -#include "logging.h" - -/* - * The little endian Unicode strings "$I30", "$SII", "$SDH", "$O" - * and "$Q" as global constants. - */ -ntfschar NTFS_INDEX_I30[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('I'), - const_cpu_to_le16('3'), const_cpu_to_le16('0'), - const_cpu_to_le16('\0') }; -ntfschar NTFS_INDEX_SII[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'), - const_cpu_to_le16('I'), const_cpu_to_le16('I'), - const_cpu_to_le16('\0') }; -ntfschar NTFS_INDEX_SDH[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'), - const_cpu_to_le16('D'), const_cpu_to_le16('H'), - const_cpu_to_le16('\0') }; -ntfschar NTFS_INDEX_O[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('O'), - const_cpu_to_le16('\0') }; -ntfschar NTFS_INDEX_Q[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('Q'), - const_cpu_to_le16('\0') }; -ntfschar NTFS_INDEX_R[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('R'), - const_cpu_to_le16('\0') }; - -/** - * ntfs_inode_lookup_by_name - find an inode in a directory given its name - * @dir_ni: ntfs inode of the directory in which to search for the name - * @uname: Unicode name for which to search in the directory - * @uname_len: length of the name @uname in Unicode characters - * - * Look for an inode with name @uname in the directory with inode @dir_ni. - * ntfs_inode_lookup_by_name() walks the contents of the directory looking for - * the Unicode name. If the name is found in the directory, the corresponding - * inode number (>= 0) is returned as a mft reference in cpu format, i.e. it - * is a 64-bit number containing the sequence number. - * - * On error, return -1 with errno set to the error code. If the inode is is not - * found errno is ENOENT. - * - * Note, @uname_len does not include the (optional) terminating NULL character. - * - * Note, we look for a case sensitive match first but we also look for a case - * insensitive match at the same time. If we find a case insensitive match, we - * save that for the case that we don't find an exact match, where we return - * the mft reference of the case insensitive match. - * - * If the volume is mounted with the case sensitive flag set, then we only - * allow exact matches. - */ -u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, - const int uname_len) -{ - VCN vcn; - u64 mref = 0; - s64 br; - ntfs_volume *vol = dir_ni->vol; - ntfs_attr_search_ctx *ctx; - INDEX_ROOT *ir; - INDEX_ENTRY *ie; - INDEX_ALLOCATION *ia; - u8 *index_end; - ntfs_attr *ia_na; - int eo, rc; - u32 index_block_size, index_vcn_size; - u8 index_vcn_size_bits; - - if (!dir_ni || !dir_ni->mrec || !uname || uname_len <= 0) { - errno = EINVAL; - return -1; - } - - ctx = ntfs_attr_get_search_ctx(dir_ni, NULL); - if (!ctx) - return -1; - - /* Find the index root attribute in the mft record. */ - if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, - 0, ctx)) { - ntfs_log_perror("Index root attribute missing in directory inode " - "0x%llx", (unsigned long long)dir_ni->mft_no); - goto put_err_out; - } - /* Get to the index root value. */ - ir = (INDEX_ROOT*)((u8*)ctx->attr + - le16_to_cpu(ctx->attr->value_offset)); - index_block_size = le32_to_cpu(ir->index_block_size); - if (index_block_size < NTFS_BLOCK_SIZE || - index_block_size & (index_block_size - 1)) { - ntfs_log_debug("Index block size %u is invalid.\n", - (unsigned)index_block_size); - goto put_err_out; - } - index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); - /* The first index entry. */ - ie = (INDEX_ENTRY*)((u8*)&ir->index + - le32_to_cpu(ir->index.entries_offset)); - /* - * Loop until we exceed valid memory (corruption case) or until we - * reach the last entry. - */ - for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { - /* Bounds checks. */ - if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + - sizeof(INDEX_ENTRY_HEADER) > index_end || - (u8*)ie + le16_to_cpu(ie->key_length) > - index_end) - goto put_err_out; - /* - * The last entry cannot contain a name. It can however contain - * a pointer to a child node in the B+tree so we just break out. - */ - if (ie->flags & INDEX_ENTRY_END) - break; - /* - * We perform a case sensitive comparison and if that matches - * we are done and return the mft reference of the inode (i.e. - * the inode number together with the sequence number for - * consistency checking). We convert it to cpu format before - * returning. - */ - if (ntfs_names_are_equal(uname, uname_len, - (ntfschar*)&ie->key.file_name.file_name, - ie->key.file_name.file_name_length, - CASE_SENSITIVE, vol->upcase, vol->upcase_len)) { -found_it: - /* - * We have a perfect match, so we don't need to care - * about having matched imperfectly before. - */ - mref = le64_to_cpu(ie->indexed_file); - ntfs_attr_put_search_ctx(ctx); - return mref; - } - /* - * For a case insensitive mount, we also perform a case - * insensitive comparison (provided the file name is not in the - * POSIX namespace). If the comparison matches, we cache the - * mft reference in mref. - */ - if (!NVolCaseSensitive(vol) && - ie->key.file_name.file_name_type && - ntfs_names_are_equal(uname, uname_len, - (ntfschar*)&ie->key.file_name.file_name, - ie->key.file_name.file_name_length, - IGNORE_CASE, vol->upcase, vol->upcase_len)) { - /* Only one case insensitive matching name allowed. */ - if (mref) { - ntfs_log_error("Found already cached mft " - "reference in phase 1. Please " - "run chkdsk and if that doesn't" - " find any errors please report" - " you saw this message to %s\n", - NTFS_DEV_LIST); - goto put_err_out; - } - mref = le64_to_cpu(ie->indexed_file); - } - /* - * Not a perfect match, need to do full blown collation so we - * know which way in the B+tree we have to go. - */ - rc = ntfs_names_collate(uname, uname_len, - (ntfschar*)&ie->key.file_name.file_name, - ie->key.file_name.file_name_length, 1, - IGNORE_CASE, vol->upcase, vol->upcase_len); - /* - * If uname collates before the name of the current entry, there - * is definitely no such name in this index but we might need to - * descend into the B+tree so we just break out of the loop. - */ - if (rc == -1) - break; - /* The names are not equal, continue the search. */ - if (rc) - continue; - /* - * Names match with case insensitive comparison, now try the - * case sensitive comparison, which is required for proper - * collation. - */ - rc = ntfs_names_collate(uname, uname_len, - (ntfschar*)&ie->key.file_name.file_name, - ie->key.file_name.file_name_length, 1, - CASE_SENSITIVE, vol->upcase, vol->upcase_len); - if (rc == -1) - break; - if (rc) - continue; - /* - * Perfect match, this will never happen as the - * ntfs_are_names_equal() call will have gotten a match but we - * still treat it correctly. - */ - goto found_it; - } - /* - * We have finished with this index without success. Check for the - * presence of a child node and if not present return error code - * ENOENT, unless we have got the mft reference of a matching name - * cached in mref in which case return mref. - */ - if (!(ie->flags & INDEX_ENTRY_NODE)) { - ntfs_attr_put_search_ctx(ctx); - if (mref) - return mref; - ntfs_log_debug("Entry not found.\n"); - errno = ENOENT; - return -1; - } /* Child node present, descend into it. */ - - /* Open the index allocation attribute. */ - ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); - if (!ia_na) { - ntfs_log_perror("Failed to open index allocation attribute. Directory " - "inode 0x%llx is corrupt or driver bug", - (unsigned long long)dir_ni->mft_no); - goto put_err_out; - } - - /* Allocate a buffer for the current index block. */ - ia = (INDEX_ALLOCATION*)malloc(index_block_size); - if (!ia) { - ntfs_log_perror("Failed to allocate buffer for index block"); - ntfs_attr_close(ia_na); - goto put_err_out; - } - - /* Determine the size of a vcn in the directory index. */ - if (vol->cluster_size <= index_block_size) { - index_vcn_size = vol->cluster_size; - index_vcn_size_bits = vol->cluster_size_bits; - } else { - index_vcn_size = vol->sector_size; - index_vcn_size_bits = vol->sector_size_bits; - } - - /* Get the starting vcn of the index_block holding the child node. */ - vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); - -descend_into_child_node: - - /* Read the index block starting at vcn. */ - br = ntfs_attr_mst_pread(ia_na, vcn << index_vcn_size_bits, 1, - index_block_size, ia); - if (br != 1) { - if (br != -1) - errno = EIO; - ntfs_log_perror("Failed to read vcn 0x%llx", vcn); - goto close_err_out; - } - - if (sle64_to_cpu(ia->index_block_vcn) != vcn) { - ntfs_log_debug("Actual VCN (0x%llx) of index buffer is different " - "from expected VCN (0x%llx).\n", - (long long)sle64_to_cpu(ia->index_block_vcn), - (long long)vcn); - errno = EIO; - goto close_err_out; - } - if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { - ntfs_log_debug("Index buffer (VCN 0x%llx) of directory inode 0x%llx " - "has a size (%u) differing from the directory " - "specified size (%u).\n", (long long)vcn, - (unsigned long long)dir_ni->mft_no, - (unsigned) le32_to_cpu(ia->index.allocated_size) + 0x18, - (unsigned)index_block_size); - errno = EIO; - goto close_err_out; - } - index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); - if (index_end > (u8*)ia + index_block_size) { - ntfs_log_debug("Size of index buffer (VCN 0x%llx) of directory inode " - "0x%llx exceeds maximum size.\n", - (long long)vcn, (unsigned long long)dir_ni->mft_no); - errno = EIO; - goto close_err_out; - } - - /* The first index entry. */ - ie = (INDEX_ENTRY*)((u8*)&ia->index + - le32_to_cpu(ia->index.entries_offset)); - /* - * Iterate similar to above big loop but applied to index buffer, thus - * loop until we exceed valid memory (corruption case) or until we - * reach the last entry. - */ - for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { - /* Bounds check. */ - if ((u8*)ie < (u8*)ia || (u8*)ie + - sizeof(INDEX_ENTRY_HEADER) > index_end || - (u8*)ie + le16_to_cpu(ie->key_length) > - index_end) { - ntfs_log_debug("Index entry out of bounds in directory " - "inode 0x%llx.\n", - (unsigned long long)dir_ni->mft_no); - errno = EIO; - goto close_err_out; - } - /* - * The last entry cannot contain a name. It can however contain - * a pointer to a child node in the B+tree so we just break out. - */ - if (ie->flags & INDEX_ENTRY_END) - break; - /* - * We perform a case sensitive comparison and if that matches - * we are done and return the mft reference of the inode (i.e. - * the inode number together with the sequence number for - * consistency checking). We convert it to cpu format before - * returning. - */ - if (ntfs_names_are_equal(uname, uname_len, - (ntfschar*)&ie->key.file_name.file_name, - ie->key.file_name.file_name_length, - CASE_SENSITIVE, vol->upcase, vol->upcase_len)) { -found_it2: - /* - * We have a perfect match, so we don't need to care - * about having matched imperfectly before. - */ - mref = le64_to_cpu(ie->indexed_file); - free(ia); - ntfs_attr_close(ia_na); - ntfs_attr_put_search_ctx(ctx); - return mref; - } - /* - * For a case insensitive mount, we also perform a case - * insensitive comparison (provided the file name is not in the - * POSIX namespace). If the comparison matches, we cache the - * mft reference in mref. - */ - if (!NVolCaseSensitive(vol) && - ie->key.file_name.file_name_type && - ntfs_names_are_equal(uname, uname_len, - (ntfschar*)&ie->key.file_name.file_name, - ie->key.file_name.file_name_length, - IGNORE_CASE, vol->upcase, vol->upcase_len)) { - /* Only one case insensitive matching name allowed. */ - if (mref) { - ntfs_log_error("Found already cached mft " - "reference in phase 2. Please " - "run chkdsk and if that doesn't" - " find any errors please report" - " you saw this message to %s\n", - NTFS_DEV_LIST); - goto close_err_out; - } - mref = le64_to_cpu(ie->indexed_file); - } - /* - * Not a perfect match, need to do full blown collation so we - * know which way in the B+tree we have to go. - */ - rc = ntfs_names_collate(uname, uname_len, - (ntfschar*)&ie->key.file_name.file_name, - ie->key.file_name.file_name_length, 1, - IGNORE_CASE, vol->upcase, vol->upcase_len); - /* - * If uname collates before the name of the current entry, there - * is definitely no such name in this index but we might need to - * descend into the B+tree so we just break out of the loop. - */ - if (rc == -1) - break; - /* The names are not equal, continue the search. */ - if (rc) - continue; - /* - * Names match with case insensitive comparison, now try the - * case sensitive comparison, which is required for proper - * collation. - */ - rc = ntfs_names_collate(uname, uname_len, - (ntfschar*)&ie->key.file_name.file_name, - ie->key.file_name.file_name_length, 1, - CASE_SENSITIVE, vol->upcase, vol->upcase_len); - if (rc == -1) - break; - if (rc) - continue; - /* - * Perfect match, this will never happen as the - * ntfs_are_names_equal() call will have gotten a match but we - * still treat it correctly. - */ - goto found_it2; - } - /* - * We have finished with this index buffer without success. Check for - * the presence of a child node. - */ - if (ie->flags & INDEX_ENTRY_NODE) { - if ((ia->index.flags & NODE_MASK) == LEAF_NODE) { - ntfs_log_debug("Index entry with child node found in a leaf " - "node in directory inode 0x%llx.\n", - (unsigned long long)dir_ni->mft_no); - errno = EIO; - goto close_err_out; - } - /* Child node present, descend into it. */ - vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); - if (vcn >= 0) - goto descend_into_child_node; - ntfs_log_debug("Negative child node vcn in directory inode " - "0x%llx.\n", (unsigned long long)dir_ni->mft_no); - errno = EIO; - goto close_err_out; - } - free(ia); - ntfs_attr_close(ia_na); - ntfs_attr_put_search_ctx(ctx); - /* - * No child node present, return error code ENOENT, unless we have got - * the mft reference of a matching name cached in mref in which case - * return mref. - */ - if (mref) - return mref; - ntfs_log_debug("Entry not found.\n"); - errno = ENOENT; - return -1; -put_err_out: - eo = EIO; - ntfs_log_debug("Corrupt directory. Aborting lookup.\n"); -eo_put_err_out: - ntfs_attr_put_search_ctx(ctx); - errno = eo; - return -1; -close_err_out: - eo = errno; - free(ia); - ntfs_attr_close(ia_na); - goto eo_put_err_out; -} - -/** - * ntfs_pathname_to_inode - Find the inode which represents the given pathname - * @vol: An ntfs volume obtained from ntfs_mount - * @parent: A directory inode to begin the search (may be NULL) - * @pathname: Pathname to be located - * - * Take an ASCII pathname and find the inode that represents it. The function - * splits the path and then descends the directory tree. If @parent is NULL, - * then the root directory '.' will be used as the base for the search. - * - * Return: inode Success, the pathname was valid - * NULL Error, the pathname was invalid, or some other error occurred - */ -ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, - const char *pathname) -{ - u64 inum; - int len, err = 0; - char *p, *q; - ntfs_inode *ni; - ntfs_inode *result = NULL; - ntfschar *unicode = NULL; - char *ascii = NULL; - - if (!vol || !pathname) { - errno = EINVAL; - return NULL; - } - - if (parent) { - ni = parent; - } else { - ni = ntfs_inode_open(vol, FILE_root); - if (!ni) { - ntfs_log_debug("Couldn't open the inode of the root " - "directory.\n"); - err = EIO; - goto close; - } - } - - unicode = calloc(1, MAX_PATH); - ascii = strdup(pathname); - if (!unicode || !ascii) { - ntfs_log_debug("Out of memory.\n"); - err = ENOMEM; - goto close; - } - - p = ascii; - /* Remove leading /'s. */ - while (p && *p && *p == PATH_SEP) - p++; - while (p && *p) { - /* Find the end of the first token. */ - q = strchr(p, PATH_SEP); - if (q != NULL) { - *q = '\0'; - q++; - } - - len = ntfs_mbstoucs(p, &unicode, MAX_PATH); - if (len < 0) { - ntfs_log_debug("Couldn't convert name to Unicode: %s.\n", p); - err = EILSEQ; - goto close; - } - - inum = ntfs_inode_lookup_by_name(ni, unicode, len); - if (inum == (u64) -1) { - ntfs_log_debug("Couldn't find name '%s' in pathname " - "'%s'.\n", p, pathname); - err = ENOENT; - goto close; - } - - if (ni != parent) - ntfs_inode_close(ni); - - inum = MREF(inum); - ni = ntfs_inode_open(vol, inum); - if (!ni) { - ntfs_log_debug("Cannot open inode %llu: %s.\n", - (unsigned long long)inum, p); - err = EIO; - goto close; - } - - p = q; - while (p && *p && *p == PATH_SEP) - p++; - } - - result = ni; - ni = NULL; -close: - if (ni && (ni != parent)) - ntfs_inode_close(ni); - free(ascii); - free(unicode); - if (err) - errno = err; - return result; -} - -/* - * The little endian Unicode string ".." for ntfs_readdir(). - */ -static const ntfschar dotdot[3] = { const_cpu_to_le16('.'), - const_cpu_to_le16('.'), - const_cpu_to_le16('\0') }; - -/* - * union index_union - - * More helpers for ntfs_readdir(). - */ -typedef union { - INDEX_ROOT *ir; - INDEX_ALLOCATION *ia; -} index_union __attribute__((__transparent_union__)); - -/** - * enum INDEX_TYPE - - * More helpers for ntfs_readdir(). - */ -typedef enum { - INDEX_TYPE_ROOT, /* index root */ - INDEX_TYPE_ALLOCATION, /* index allocation */ -} INDEX_TYPE; - -/** - * ntfs_filldir - ntfs specific filldir method - * @dir_ni: ntfs inode of current directory - * @pos: current position in directory - * @ivcn_bits: log(2) of index vcn size - * @index_type: specifies whether @iu is an index root or an index allocation - * @iu: index root or index block to which @ie belongs - * @ie: current index entry - * @dirent: context for filldir callback supplied by the caller - * @filldir: filldir callback supplied by the caller - * - * Pass information specifying the current directory entry @ie to the @filldir - * callback. - */ -static inline int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits, - const INDEX_TYPE index_type, index_union iu, INDEX_ENTRY *ie, - void *dirent, ntfs_filldir_t filldir) -{ - FILE_NAME_ATTR *fn = &ie->key.file_name; - unsigned dt_type; - - /* Advance the position even if going to skip the entry. */ - if (index_type == INDEX_TYPE_ALLOCATION) - *pos = (u8*)ie - (u8*)iu.ia + (sle64_to_cpu( - iu.ia->index_block_vcn) << ivcn_bits) + - dir_ni->vol->mft_record_size; - else /* if (index_type == INDEX_TYPE_ROOT) */ - *pos = (u8*)ie - (u8*)iu.ir; - /* Skip root directory self reference entry. */ - if (MREF_LE(ie->indexed_file) == FILE_root) - return 0; - if (ie->key.file_name.file_attributes & FILE_ATTR_I30_INDEX_PRESENT) - dt_type = NTFS_DT_DIR; - else - dt_type = NTFS_DT_REG; - return filldir(dirent, fn->file_name, fn->file_name_length, - fn->file_name_type, *pos, - le64_to_cpu(ie->indexed_file), dt_type); -} - -/** - * ntfs_mft_get_parent_ref - find mft reference of parent directory of an inode - * @ni: ntfs inode whose parent directory to find - * - * Find the parent directory of the ntfs inode @ni. To do this, find the first - * file name attribute in the mft record of @ni and return the parent mft - * reference from that. - * - * Note this only makes sense for directories, since files can be hard linked - * from multiple directories and there is no way for us to tell which one is - * being looked for. - * - * Technically directories can have hard links, too, but we consider that as - * illegal as Linux/UNIX do not support directory hard links. - * - * Return the mft reference of the parent directory on success or -1 on error - * with errno set to the error code. - */ -static MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni) -{ - MFT_REF mref; - ntfs_attr_search_ctx *ctx; - FILE_NAME_ATTR *fn; - int eo; - - if (!ni) { - errno = EINVAL; - return ERR_MREF(-1); - } - - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) - return ERR_MREF(-1); - if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { - ntfs_log_debug("No file name found in inode 0x%llx. Corrupt " - "inode.\n", (unsigned long long)ni->mft_no); - goto err_out; - } - if (ctx->attr->non_resident) { - ntfs_log_debug("File name attribute must be resident. Corrupt inode " - "0x%llx.\n", (unsigned long long)ni->mft_no); - goto io_err_out; - } - fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + - le16_to_cpu(ctx->attr->value_offset)); - if ((u8*)fn + le32_to_cpu(ctx->attr->value_length) > - (u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) { - ntfs_log_debug("Corrupt file name attribute in inode 0x%llx.\n", - (unsigned long long)ni->mft_no); - goto io_err_out; - } - mref = le64_to_cpu(fn->parent_directory); - ntfs_attr_put_search_ctx(ctx); - return mref; -io_err_out: - errno = EIO; -err_out: - eo = errno; - ntfs_attr_put_search_ctx(ctx); - errno = eo; - return ERR_MREF(-1); -} - -/** - * ntfs_readdir - read the contents of an ntfs directory - * @dir_ni: ntfs inode of current directory - * @pos: current position in directory - * @dirent: context for filldir callback supplied by the caller - * @filldir: filldir callback supplied by the caller - * - * Parse the index root and the index blocks that are marked in use in the - * index bitmap and hand each found directory entry to the @filldir callback - * supplied by the caller. - * - * Return 0 on success or -1 on error with errno set to the error code. - * - * Note: Index blocks are parsed in ascending vcn order, from which follows - * that the directory entries are not returned sorted. - */ -int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, - void *dirent, ntfs_filldir_t filldir) -{ - s64 i_size, br, ia_pos, bmp_pos, ia_start; - ntfs_volume *vol; - ntfs_attr *ia_na, *bmp_na = NULL; - ntfs_attr_search_ctx *ctx = NULL; - u8 *index_end, *bmp = NULL; - INDEX_ROOT *ir; - INDEX_ENTRY *ie; - INDEX_ALLOCATION *ia = NULL; - int rc, ir_pos, bmp_buf_size, bmp_buf_pos, eo; - u32 index_block_size, index_vcn_size; - u8 index_block_size_bits, index_vcn_size_bits; - - if (!dir_ni || !pos || !filldir) { - errno = EINVAL; - return -1; - } - - if (!(dir_ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { - errno = ENOTDIR; - return -1; - } - - vol = dir_ni->vol; - - ntfs_log_trace("Entering for inode 0x%llx, *pos 0x%llx.\n", - (unsigned long long)dir_ni->mft_no, (long long)*pos); - - /* Open the index allocation attribute. */ - ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); - if (!ia_na) { - if (errno != ENOENT) { - ntfs_log_perror("Failed to open index allocation attribute. " - "Directory inode 0x%llx is corrupt or bug", - (unsigned long long)dir_ni->mft_no); - return -1; - } - i_size = 0; - } else - i_size = ia_na->data_size; - - rc = 0; - - /* Are we at end of dir yet? */ - if (*pos >= i_size + vol->mft_record_size) - goto done; - - /* Emulate . and .. for all directories. */ - if (!*pos) { - rc = filldir(dirent, dotdot, 1, FILE_NAME_POSIX, *pos, - MK_MREF(dir_ni->mft_no, - le16_to_cpu(dir_ni->mrec->sequence_number)), - NTFS_DT_DIR); - if (rc) - goto done; - ++*pos; - } - if (*pos == 1) { - MFT_REF parent_mref; - - parent_mref = ntfs_mft_get_parent_ref(dir_ni); - if (parent_mref == ERR_MREF(-1)) { - ntfs_log_perror("Parent directory not found"); - goto dir_err_out; - } - - rc = filldir(dirent, dotdot, 2, FILE_NAME_POSIX, *pos, - parent_mref, NTFS_DT_DIR); - if (rc) - goto done; - ++*pos; - } - - ctx = ntfs_attr_get_search_ctx(dir_ni, NULL); - if (!ctx) - goto err_out; - - /* Get the offset into the index root attribute. */ - ir_pos = (int)*pos; - /* Find the index root attribute in the mft record. */ - if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, - 0, ctx)) { - ntfs_log_debug("Index root attribute missing in directory inode " - "0x%llx.\n", (unsigned long long)dir_ni->mft_no); - goto dir_err_out; - } - /* Get to the index root value. */ - ir = (INDEX_ROOT*)((u8*)ctx->attr + - le16_to_cpu(ctx->attr->value_offset)); - - /* Determine the size of a vcn in the directory index. */ - index_block_size = le32_to_cpu(ir->index_block_size); - if (index_block_size < NTFS_BLOCK_SIZE || - index_block_size & (index_block_size - 1)) { - ntfs_log_debug("Index block size %u is invalid.\n", - (unsigned)index_block_size); - goto dir_err_out; - } - index_block_size_bits = ffs(index_block_size) - 1; - if (vol->cluster_size <= index_block_size) { - index_vcn_size = vol->cluster_size; - index_vcn_size_bits = vol->cluster_size_bits; - } else { - index_vcn_size = vol->sector_size; - index_vcn_size_bits = vol->sector_size_bits; - } - - /* Are we jumping straight into the index allocation attribute? */ - if (*pos >= vol->mft_record_size) { - ntfs_attr_put_search_ctx(ctx); - ctx = NULL; - goto skip_index_root; - } - - index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); - /* The first index entry. */ - ie = (INDEX_ENTRY*)((u8*)&ir->index + - le32_to_cpu(ir->index.entries_offset)); - /* - * Loop until we exceed valid memory (corruption case) or until we - * reach the last entry or until filldir tells us it has had enough - * or signals an error (both covered by the rc test). - */ - for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { - ntfs_log_debug("In index root, offset 0x%x.\n", (u8*)ie - (u8*)ir); - /* Bounds checks. */ - if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + - sizeof(INDEX_ENTRY_HEADER) > index_end || - (u8*)ie + le16_to_cpu(ie->key_length) > - index_end) - goto dir_err_out; - /* The last entry cannot contain a name. */ - if (ie->flags & INDEX_ENTRY_END) - break; - /* Skip index root entry if continuing previous readdir. */ - if (ir_pos > (u8*)ie - (u8*)ir) - continue; - /* - * Submit the directory entry to ntfs_filldir(), which will - * invoke the filldir() callback as appropriate. - */ - rc = ntfs_filldir(dir_ni, pos, index_vcn_size_bits, - INDEX_TYPE_ROOT, ir, ie, dirent, filldir); - if (rc) { - ntfs_attr_put_search_ctx(ctx); - ctx = NULL; - goto done; - } - } - ntfs_attr_put_search_ctx(ctx); - ctx = NULL; - - /* If there is no index allocation attribute we are finished. */ - if (!ia_na) - goto EOD; - - /* Advance *pos to the beginning of the index allocation. */ - *pos = vol->mft_record_size; - -skip_index_root: - - if (!ia_na) - goto done; - - /* Allocate a buffer for the current index block. */ - ia = (INDEX_ALLOCATION*)malloc(index_block_size); - if (!ia) { - ntfs_log_perror("Failed to allocate buffer for index block"); - goto err_out; - } - - bmp_na = ntfs_attr_open(dir_ni, AT_BITMAP, NTFS_INDEX_I30, 4); - if (!bmp_na) { - ntfs_log_perror("Failed to open index bitmap attribute"); - goto dir_err_out; - } - - /* Get the offset into the index allocation attribute. */ - ia_pos = *pos - vol->mft_record_size; - - bmp_pos = ia_pos >> index_block_size_bits; - if (bmp_pos >> 3 >= bmp_na->data_size) { - ntfs_log_debug("Current index position exceeds index bitmap " - "size.\n"); - goto dir_err_out; - } - - bmp_buf_size = min(bmp_na->data_size - (bmp_pos >> 3), 4096); - bmp = (u8*)malloc(bmp_buf_size); - if (!bmp) { - ntfs_log_perror("Failed to allocate bitmap buffer"); - goto err_out; - } - - br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp); - if (br != bmp_buf_size) { - if (br != -1) - errno = EIO; - ntfs_log_perror("Failed to read from index bitmap attribute"); - goto err_out; - } - - bmp_buf_pos = 0; - /* If the index block is not in use find the next one that is. */ - while (!(bmp[bmp_buf_pos >> 3] & (1 << (bmp_buf_pos & 7)))) { -find_next_index_buffer: - bmp_pos++; - bmp_buf_pos++; - /* If we have reached the end of the bitmap, we are done. */ - if (bmp_pos >> 3 >= bmp_na->data_size) - goto EOD; - ia_pos = bmp_pos << index_block_size_bits; - if (bmp_buf_pos >> 3 < bmp_buf_size) - continue; - /* Read next chunk from the index bitmap. */ - if ((bmp_pos >> 3) + bmp_buf_size > bmp_na->data_size) - bmp_buf_size = bmp_na->data_size - (bmp_pos >> 3); - br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp); - if (br != bmp_buf_size) { - if (br != -1) - errno = EIO; - ntfs_log_perror("Failed to read from index bitmap attribute"); - goto err_out; - } - } - - ntfs_log_debug("Handling index block 0x%llx.\n", (long long)bmp_pos); - - /* Read the index block starting at bmp_pos. */ - br = ntfs_attr_mst_pread(ia_na, bmp_pos << index_block_size_bits, 1, - index_block_size, ia); - if (br != 1) { - if (br != -1) - errno = EIO; - ntfs_log_perror("Failed to read index block"); - goto err_out; - } - - ia_start = ia_pos & ~(s64)(index_block_size - 1); - if (sle64_to_cpu(ia->index_block_vcn) != ia_start >> - index_vcn_size_bits) { - ntfs_log_debug("Actual VCN (0x%llx) of index buffer is different " - "from expected VCN (0x%llx) in inode 0x%llx.\n", - (long long)sle64_to_cpu(ia->index_block_vcn), - (long long)ia_start >> index_vcn_size_bits, - (unsigned long long)dir_ni->mft_no); - goto dir_err_out; - } - if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { - ntfs_log_debug("Index buffer (VCN 0x%llx) of directory inode 0x%llx " - "has a size (%u) differing from the directory " - "specified size (%u).\n", (long long)ia_start >> - index_vcn_size_bits, - (unsigned long long)dir_ni->mft_no, - (unsigned) le32_to_cpu(ia->index.allocated_size) - + 0x18, (unsigned)index_block_size); - goto dir_err_out; - } - index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); - if (index_end > (u8*)ia + index_block_size) { - ntfs_log_debug("Size of index buffer (VCN 0x%llx) of directory inode " - "0x%llx exceeds maximum size.\n", - (long long)ia_start >> index_vcn_size_bits, - (unsigned long long)dir_ni->mft_no); - goto dir_err_out; - } - /* The first index entry. */ - ie = (INDEX_ENTRY*)((u8*)&ia->index + - le32_to_cpu(ia->index.entries_offset)); - /* - * Loop until we exceed valid memory (corruption case) or until we - * reach the last entry or until ntfs_filldir tells us it has had - * enough or signals an error (both covered by the rc test). - */ - for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { - ntfs_log_debug("In index allocation, offset 0x%llx.\n", - (long long)ia_start + ((u8*)ie - (u8*)ia)); - /* Bounds checks. */ - if ((u8*)ie < (u8*)ia || (u8*)ie + - sizeof(INDEX_ENTRY_HEADER) > index_end || - (u8*)ie + le16_to_cpu(ie->key_length) > - index_end) { - ntfs_log_debug("Index entry out of bounds in directory inode " - "0x%llx.\n", (unsigned long long)dir_ni->mft_no); - goto dir_err_out; - } - /* The last entry cannot contain a name. */ - if (ie->flags & INDEX_ENTRY_END) - break; - /* Skip index entry if continuing previous readdir. */ - if (ia_pos - ia_start > (u8*)ie - (u8*)ia) - continue; - /* - * Submit the directory entry to ntfs_filldir(), which will - * invoke the filldir() callback as appropriate. - */ - rc = ntfs_filldir(dir_ni, pos, index_vcn_size_bits, - INDEX_TYPE_ALLOCATION, ia, ie, dirent, filldir); - if (rc) - goto done; - } - goto find_next_index_buffer; -EOD: - /* We are finished, set *pos to EOD. */ - *pos = i_size + vol->mft_record_size; -done: - free(ia); - free(bmp); - if (bmp_na) - ntfs_attr_close(bmp_na); - if (ia_na) - ntfs_attr_close(ia_na); -#ifdef DEBUG - if (!rc) - ntfs_log_debug("EOD, *pos 0x%llx, returning 0.\n", (long long)*pos); - else - ntfs_log_debug("filldir returned %i, *pos 0x%llx, returning 0.\n", - rc, (long long)*pos); -#endif - return 0; -dir_err_out: - errno = EIO; -err_out: - eo = errno; - ntfs_log_trace("failed.\n"); - if (ctx) - ntfs_attr_put_search_ctx(ctx); - free(ia); - free(bmp); - if (bmp_na) - ntfs_attr_close(bmp_na); - if (ia_na) - ntfs_attr_close(ia_na); - errno = eo; - return -1; -} - -/** - * __ntfs_create - create object on ntfs volume - * @dir_ni: ntfs inode for directory in which create new object - * @name: unicode name of new object - * @name_len: length of the name in unicode characters - * @type: type of the object to create - * @dev: major and minor device numbers (obtained from makedev()) - * @target: target in unicode (only for symlinks) - * @target_len: length of target in unicode charcters - * - * Internal, use ntfs_create{,_device,_symlink} wrappers instead. - * - * @type can be: - * S_IFREG to create regular file - * S_IFDIR to create directory - * S_IFBLK to create block device - * S_IFCHR to create character device - * S_IFLNK to create symbolic link - * S_IFIFO to create FIFO - * S_IFSOCK to create socket - * other values are invalid. - * - * @dev is used only if @type is S_IFBLK or S_IFCHR, in other cases its value - * ignored. - * - * @target and @target_len are used only if @type is S_IFLNK, in other cases - * their value ignored. - * - * Return opened ntfs inode that describes created object on success or NULL - * on error with errno set to the error code. - */ -static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, - ntfschar *name, u8 name_len, dev_t type, dev_t dev, - ntfschar *target, u8 target_len) -{ - ntfs_inode *ni; - int rollback_data = 0; - FILE_NAME_ATTR *fn = NULL; - STANDARD_INFORMATION *si = NULL; - int err, fn_len, si_len; - - ntfs_log_trace("Entering.\n"); - /* Sanity checks. */ - if (!dir_ni || !name || !name_len) { - ntfs_log_error("Invalid arguments.\n"); - errno = EINVAL; - return NULL; - } - /* Allocate MFT record for new file. */ - ni = ntfs_mft_record_alloc(dir_ni->vol, NULL); - if (!ni) { - err = errno; - ntfs_log_error("Failed to allocate new MFT record: %s.\n", - strerror(err)); - errno = err; - return NULL; - } - /* - * Create STANDARD_INFORMATION attribute. Write STANDARD_INFORMATION - * version 1.2, windows will upgrade it to version 3 if needed. - */ - si_len = offsetof(STANDARD_INFORMATION, v1_end); - si = calloc(1, si_len); - if (!si) { - err = errno; - ntfs_log_error("Not enough memory.\n"); - goto err_out; - } - si->creation_time = utc2ntfs(ni->creation_time); - si->last_data_change_time = utc2ntfs(ni->last_data_change_time); - si->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - si->last_access_time = utc2ntfs(ni->last_access_time); - if (!S_ISREG(type) && !S_ISDIR(type)) { - si->file_attributes = FILE_ATTR_SYSTEM; - ni->flags = FILE_ATTR_SYSTEM; - } - /* Add STANDARD_INFORMATION to inode. */ - if (ntfs_attr_add(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, - (u8*)si, si_len)) { - err = errno; - ntfs_log_error("Failed to add STANDARD_INFORMATION " - "attribute.\n"); - goto err_out; - } - if (S_ISDIR(type)) { - INDEX_ROOT *ir = NULL; - INDEX_ENTRY *ie; - int ir_len, index_len; - - /* Create INDEX_ROOT attribute. */ - index_len = sizeof(INDEX_HEADER) + sizeof(INDEX_ENTRY_HEADER); - ir_len = offsetof(INDEX_ROOT, index) + index_len; - ir = calloc(1, ir_len); - if (!ir) { - err = errno; - ntfs_log_error("Not enough memory.\n"); - goto err_out; - } - ir->type = AT_FILE_NAME; - ir->collation_rule = COLLATION_FILE_NAME; - ir->index_block_size = cpu_to_le32(ni->vol->indx_record_size); - if (ni->vol->cluster_size <= ni->vol->indx_record_size) - ir->clusters_per_index_block = - ni->vol->indx_record_size >> - ni->vol->cluster_size_bits; - else - ir->clusters_per_index_block = - -ni->vol->indx_record_size_bits; - ir->index.entries_offset = cpu_to_le32(sizeof(INDEX_HEADER)); - ir->index.index_length = cpu_to_le32(index_len); - ir->index.allocated_size = cpu_to_le32(index_len); - ie = (INDEX_ENTRY*)((u8*)ir + sizeof(INDEX_ROOT)); - ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER)); - ie->key_length = 0; - ie->flags = INDEX_ENTRY_END; - /* Add INDEX_ROOT attribute to inode. */ - if (ntfs_attr_add(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4, - (u8*)ir, ir_len)) { - err = errno; - free(ir); - ntfs_log_error("Failed to add INDEX_ROOT attribute.\n"); - goto err_out; - } - } else { - INTX_FILE *data; - int data_len; - - switch (type) { - case S_IFBLK: - case S_IFCHR: - data_len = offsetof(INTX_FILE, device_end); - data = malloc(data_len); - if (!data) { - err = errno; - ntfs_log_error("Not enough memory for " - "content of DATA " - "attribute.\n"); - goto err_out; - } - data->major = cpu_to_le64(major(dev)); - data->minor = cpu_to_le64(minor(dev)); - if (type == S_IFBLK) - data->magic = INTX_BLOCK_DEVICE; - if (type == S_IFCHR) - data->magic = INTX_CHARACTER_DEVICE; - break; - case S_IFLNK: - data_len = sizeof(INTX_FILE_TYPES) + - target_len * sizeof(ntfschar); - data = malloc(data_len); - if (!data) { - err = errno; - ntfs_log_error("Not enough memory for " - "content of DATA " - "attribute.\n"); - goto err_out; - } - data->magic = INTX_SYMBOLIC_LINK; - memcpy(data->target, target, - target_len * sizeof(ntfschar)); - break; - case S_IFSOCK: - data = NULL; - data_len = 1; - break; - default: /* FIFO or regular file. */ - data = NULL; - data_len = 0; - break; - } - /* Add DATA attribute to inode. */ - if (ntfs_attr_add(ni, AT_DATA, AT_UNNAMED, 0, (u8*)data, - data_len)) { - err = errno; - ntfs_log_error("Failed to add DATA attribute.\n"); - goto err_out; - } - rollback_data = 1; - free(data); - } - /* Create FILE_NAME attribute. */ - fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar); - fn = calloc(1, fn_len); - if (!fn) { - err = errno; - ntfs_log_error("Not enough memory.\n"); - goto err_out; - } - fn->parent_directory = MK_LE_MREF(dir_ni->mft_no, - le16_to_cpu(dir_ni->mrec->sequence_number)); - fn->file_name_length = name_len; - fn->file_name_type = FILE_NAME_POSIX; - if (S_ISDIR(type)) - fn->file_attributes = FILE_ATTR_I30_INDEX_PRESENT; - if (!S_ISREG(type) && !S_ISDIR(type)) - fn->file_attributes = FILE_ATTR_SYSTEM; - fn->creation_time = utc2ntfs(ni->creation_time); - fn->last_data_change_time = utc2ntfs(ni->last_data_change_time); - fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - fn->last_access_time = utc2ntfs(ni->last_access_time); - memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); - /* Add FILE_NAME attribute to inode. */ - if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { - err = errno; - ntfs_log_error("Failed to add FILE_NAME attribute.\n"); - goto err_out; - } - /* Add FILE_NAME attribute to index. */ - if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, - le16_to_cpu(ni->mrec->sequence_number)))) { - err = errno; - ntfs_log_error("Failed to add entry to the index: %s.\n", - strerror(err)); - goto err_out; - } - /* Set hard links count and directory flag. */ - ni->mrec->link_count = cpu_to_le16(1); - if (S_ISDIR(type)) - ni->mrec->flags |= MFT_RECORD_IS_DIRECTORY; - ntfs_inode_mark_dirty(ni); - /* Done! */ - free(fn); - free(si); - ntfs_log_trace("Done.\n"); - return ni; -err_out: - ntfs_log_trace("Failed.\n"); - if (rollback_data) { - ntfs_attr *na; - - na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); - if (!na) - ntfs_log_perror("Failed to open data attribute of " - " inode 0x%llx. Run chkdsk.\n", - (unsigned long long)ni->mft_no); - else if (ntfs_attr_rm(na)) - ntfs_log_perror("Failed to remove data attribute of " - "inode 0x%llx. Run chkdsk.\n", - (unsigned long long)ni->mft_no); - } - /* - * Free extent MFT records (should not exist any with current - * ntfs_create implementation, but for any case if something will be - * changed in the future). - */ - while (ni->nr_extents) - if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) { - err = errno; - ntfs_log_error("Failed to free extent MFT record. " - "Leaving inconsistent metadata.\n"); - } - if (ntfs_mft_record_free(ni->vol, ni)) - ntfs_log_error("Failed to free MFT record. " - "Leaving inconsistent metadata. Run chkdsk.\n"); - free(fn); - free(si); - errno = err; - return NULL; -} - -/** - * Some wrappers around __ntfs_create() ... - */ - -ntfs_inode *ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, - dev_t type) -{ - if (type != S_IFREG && type != S_IFDIR && type != S_IFIFO && - type != S_IFSOCK) { - ntfs_log_error("Invalid arguments.\n"); - return NULL; - } - return __ntfs_create(dir_ni, name, name_len, type, 0, NULL, 0); -} - -ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, - dev_t type, dev_t dev) -{ - if (type != S_IFCHR && type != S_IFBLK) { - ntfs_log_error("Invalid arguments.\n"); - return NULL; - } - return __ntfs_create(dir_ni, name, name_len, type, dev, NULL, 0); -} - -ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, - ntfschar *target, u8 target_len) -{ - if (!target || !target_len) { - ntfs_log_error("Invalid arguments.\n"); - return NULL; - } - return __ntfs_create(dir_ni, name, name_len, S_IFLNK, 0, - target, target_len); -} - -/** - * ntfs_delete - delete file or directory from ntfs volume - * @ni: ntfs inode for object to delte - * @dir_ni: ntfs inode for directory in which delete object - * @name: unicode name of the object to delete - * @name_len: length of the name in unicode characters - * - * @ni is always closed after the call to this function (even if it failed), - * user does not need to call ntfs_inode_close himself. - * - * Return 0 on success or -1 on error with errno set to the error code. - */ -int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) -{ - ntfs_attr_search_ctx *actx = NULL; - ntfs_index_context *ictx = NULL; - FILE_NAME_ATTR *fn = NULL; - BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE; - BOOL case_sensitive_match = TRUE; - int err = 0; - - ntfs_log_trace("Entering.\n"); - if (!ni || !dir_ni || !name || !name_len) { - ntfs_log_error("Invalid arguments.\n"); - errno = EINVAL; - goto err_out; - } - if (ni->nr_extents == -1) - ni = ni->base_ni; - if (dir_ni->nr_extents == -1) - dir_ni = dir_ni->base_ni; - /* - * Search for FILE_NAME attribute with such name. If it's in POSIX or - * WIN32_AND_DOS namespace, then simply remove it from index and inode. - * If filename in DOS or in WIN32 namespace, then remove DOS name first, - * only then remove WIN32 name. Mark WIN32 name as POSIX name to prevent - * chkdsk to complain about DOS name absentation, in case if DOS name - * had been successfully deleted, but WIN32 name removing failed. - */ - actx = ntfs_attr_get_search_ctx(ni, NULL); - if (!actx) - goto err_out; -search: - while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, - 0, NULL, 0, actx)) { - errno = 0; - fn = (FILE_NAME_ATTR*)((u8*)actx->attr + - le16_to_cpu(actx->attr->value_offset)); - if (looking_for_dos_name) { - if (fn->file_name_type == FILE_NAME_DOS) - break; - else - continue; - } - if (looking_for_win32_name) { - if (fn->file_name_type == FILE_NAME_WIN32) - break; - else - continue; - } - if (dir_ni->mft_no == MREF_LE(fn->parent_directory) && - ntfs_names_are_equal(fn->file_name, - fn->file_name_length, name, - name_len, (fn->file_name_type == - FILE_NAME_POSIX || case_sensitive_match) ? - CASE_SENSITIVE : IGNORE_CASE, ni->vol->upcase, - ni->vol->upcase_len)) { - if (fn->file_name_type == FILE_NAME_WIN32) { - looking_for_dos_name = TRUE; - ntfs_attr_reinit_search_ctx(actx); - continue; - } - if (fn->file_name_type == FILE_NAME_DOS) - looking_for_dos_name = TRUE; - break; - } - } - if (errno) { - /* - * If case sensitive search failed, then try once again - * ignoring case. - */ - if (errno == ENOENT && case_sensitive_match) { - case_sensitive_match = FALSE; - ntfs_attr_reinit_search_ctx(actx); - goto search; - } - goto err_out; - } - /* If deleting directory check it to be empty. */ - if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { - ntfs_attr *na; - - na = ntfs_attr_open(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4); - if (!na) { - ntfs_log_error("Corrupt directory or library bug.\n"); - errno = EIO; - goto err_out; - } - /* - * Do not allow non-empty directory deletion if hard links count - * is 1 (always) or 2 (in case if filename in DOS namespace, - * because we delete it first in filen which have both WIN32 and - * DOS names). - */ - if ((na->data_size != sizeof(INDEX_ROOT) + sizeof( - INDEX_ENTRY_HEADER)) && (le16_to_cpu( - ni->mrec->link_count) == 1 || - (le16_to_cpu(ni->mrec->link_count) == 2 && - fn->file_name_type == FILE_NAME_DOS))) { - ntfs_attr_close(na); - ntfs_log_error("Directory is not empty.\n"); - errno = ENOTEMPTY; - goto err_out; - } - ntfs_attr_close(na); - } - /* Search for such FILE_NAME in index. */ - ictx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); - if (!ictx) - goto err_out; - if (ntfs_index_lookup(fn, le32_to_cpu(actx->attr->value_length), ictx)) - goto err_out; - /* Set namespace to POSIX for WIN32 name. */ - if (fn->file_name_type == FILE_NAME_WIN32) { - fn->file_name_type = FILE_NAME_POSIX; - ntfs_inode_mark_dirty(actx->ntfs_ino); - ((FILE_NAME_ATTR*)ictx->data)->file_name_type = FILE_NAME_POSIX; - ntfs_index_entry_mark_dirty(ictx); - } - /* Do not support reparse point deletion yet. */ - if (((FILE_NAME_ATTR*)ictx->data)->file_attributes & - FILE_ATTR_REPARSE_POINT) { - errno = EOPNOTSUPP; - goto err_out; - } - /* Remove FILE_NAME from index. */ - if (ntfs_index_rm(ictx)) - goto err_out; - ictx = NULL; - /* Remove FILE_NAME from inode. */ - if (ntfs_attr_record_rm(actx)) - goto err_out; - /* Decerement hard link count. */ - ni->mrec->link_count = cpu_to_le16(le16_to_cpu( - ni->mrec->link_count) - 1); - ntfs_inode_mark_dirty(ni); - if (looking_for_dos_name) { - looking_for_dos_name = FALSE; - looking_for_win32_name = TRUE; - ntfs_attr_reinit_search_ctx(actx); - goto search; - } - /* TODO: Update object id, quota and securiry indexes if required. */ - /* - * If hard link count is not equal to zero then we are done. In other - * case there are no reference to this inode left, so we should free all - * non-resident attributes and mark all MFT record as not in use. - */ - if (ni->mrec->link_count) - goto out; - ntfs_attr_reinit_search_ctx(actx); - while (!ntfs_attrs_walk(actx)) { - if (actx->attr->non_resident) { - runlist *rl; - - rl = ntfs_mapping_pairs_decompress(ni->vol, actx->attr, - NULL); - if (!rl) { - err = errno; - ntfs_log_error("Failed to decompress runlist. " - "Leaving inconsistent metadata.\n"); - continue; - } - if (ntfs_cluster_free_from_rl(ni->vol, rl)) { - err = errno; - ntfs_log_error("Failed to free clusters. " - "Leaving inconsistent metadata.\n"); - continue; - } - free(rl); - } - } - if (errno != ENOENT) { - err = errno; - ntfs_log_error("Attribute enumeration failed. " - "Probably leaving inconsistent metadata.\n"); - } - /* All extents should be attached after attribute walk. */ - while (ni->nr_extents) - if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) { - err = errno; - ntfs_log_error("Failed to free extent MFT record. " - "Leaving inconsistent metadata.\n"); - } - if (ntfs_mft_record_free(ni->vol, ni)) { - err = errno; - ntfs_log_error("Failed to free base MFT record. " - "Leaving inconsistent metadata.\n"); - } - ni = NULL; -out: - if (actx) - ntfs_attr_put_search_ctx(actx); - if (ictx) - ntfs_index_ctx_put(ictx); - if (ni) - ntfs_inode_close(ni); - if (err) { - ntfs_log_error("Failed.\n"); - errno = err; - return -1; - } - ntfs_log_trace("Done.\n"); - return 0; -err_out: - err = errno; - goto out; -} - -/** - * ntfs_link - create hard link for file or directory - * @ni: ntfs inode for object to create hard link - * @dir_ni: ntfs inode for directory in which new link should be placed - * @name: unicode name of the new link - * @name_len: length of the name in unicode characters - * - * NOTE: At present we allow creating hardlinks to directories, we use them - * in a temporary state during rename. But it's defenitely bad idea to have - * hard links to directories as a result of operation. - * FIXME: Create internal __ntfs_link that allows hard links to a directories - * and external ntfs_link that do not. Write ntfs_rename that uses __ntfs_link. - * - * Return 0 on success or -1 on error with errno set to the error code. - */ -int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) -{ - FILE_NAME_ATTR *fn = NULL; - int fn_len, err; - - ntfs_log_trace("Entering.\n"); - if (!ni || !dir_ni || !name || !name_len || - ni->mft_no == dir_ni->mft_no) { - err = errno; - ntfs_log_error("Invalid arguments.\n"); - goto err_out; - } - /* Create FILE_NAME attribute. */ - fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar); - fn = calloc(1, fn_len); - if (!fn) { - err = errno; - ntfs_log_error("Not enough memory.\n"); - goto err_out; - } - fn->parent_directory = MK_LE_MREF(dir_ni->mft_no, - le16_to_cpu(dir_ni->mrec->sequence_number)); - fn->file_name_length = name_len; - fn->file_name_type = FILE_NAME_POSIX; - fn->file_attributes = ni->flags; - if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) - fn->file_attributes |= FILE_ATTR_I30_INDEX_PRESENT; - fn->allocated_size = cpu_to_sle64(ni->allocated_size); - fn->data_size = cpu_to_sle64(ni->data_size); - fn->creation_time = utc2ntfs(ni->creation_time); - fn->last_data_change_time = utc2ntfs(ni->last_data_change_time); - fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - fn->last_access_time = utc2ntfs(ni->last_access_time); - memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); - /* Add FILE_NAME attribute to index. */ - if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, - le16_to_cpu(ni->mrec->sequence_number)))) { - err = errno; - ntfs_log_error("Failed to add entry to the index.\n"); - goto err_out; - } - /* Add FILE_NAME attribute to inode. */ - if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { - ntfs_index_context *ictx; - - err = errno; - ntfs_log_error("Failed to add FILE_NAME attribute.\n"); - /* Try to remove just added attribute from index. */ - ictx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); - if (!ictx) - goto rollback_failed; - if (ntfs_index_lookup(fn, fn_len, ictx)) { - ntfs_index_ctx_put(ictx); - goto rollback_failed; - } - if (ntfs_index_rm(ictx)) { - ntfs_index_ctx_put(ictx); - goto rollback_failed; - } - goto err_out; - } - /* Increment hard links count. */ - ni->mrec->link_count = cpu_to_le16(le16_to_cpu( - ni->mrec->link_count) + 1); - /* Done! */ - ntfs_inode_mark_dirty(ni); - free(fn); - ntfs_log_trace("Done.\n"); - return 0; -rollback_failed: - ntfs_log_error("Rollback failed. Leaving inconsistent metadata.\n"); -err_out: - ntfs_log_error("Failed.\n"); - free(fn); - errno = err; - return -1; -} - - -#ifdef NTFS_RICH - -#include "layout.h" -#include "tree.h" -#include "bitmap.h" -#include "rich.h" - -/** - * ntfs_dir_rollback - Discard the in-memory directory changes - * @dir: - * - * Description... - * - * Returns: - */ -int ntfs_dir_rollback(struct ntfs_dir *dir) -{ - int i; - - if (!dir) - return -1; - - ntfs_log_trace ("dir %p, inode %lld, children %d\n", dir, - dir ? MREF(dir->mft_num) : 0, dir ? dir->child_count : 0); - - if (ntfs_dt_rollback(dir->index) < 0) - return -1; - - if (ntfs_bmp_rollback(dir->bitmap) < 0) - return -1; - - for (i = 0; i < dir->child_count; i++) { - if (ntfs_dir_rollback(dir->children[i]) < 0) - return -1; - } - - return 0; -} - -/** - * ntfs_dir_truncate - Shrink an index allocation - * @vol: - * @dir: - * - * Description... - * - * Returns: - */ -int ntfs_dir_truncate(ntfs_volume *vol, struct ntfs_dir *dir) -{ - //int i; - //u8 *buffer; - //int buf_count; - s64 last_bit; - INDEX_ENTRY *ie; - - if (!vol || !dir) - return -1; - - ntfs_log_trace ("dir %p, inode %lld, children %d\n", dir, - dir ? MREF(dir->mft_num) : 0, dir ? dir->child_count : 0); - - if ((dir->ialloc == NULL) || (dir->bitmap == NULL)) - return 0; - -#if 0 - buf_count = ROUND_UP(dir->bitmap->attr->allocated_size, vol->cluster_size) >> vol->cluster_size_bits; - ntfs_log_debug("alloc = %lld bytes\n", dir->ialloc->allocated_size); - ntfs_log_debug("alloc = %lld clusters\n", dir->ialloc->allocated_size >> vol->cluster_size_bits); - ntfs_log_debug("bitmap bytes 0 to %lld\n", ((dir->ialloc->allocated_size >> vol->cluster_size_bits)-1)>>3); - ntfs_log_debug("bitmap = %p\n", dir->bitmap); - ntfs_log_debug("bitmap = %lld bytes\n", dir->bitmap->attr->allocated_size); - ntfs_log_debug("bitmap = %d buffers\n", buf_count); -#endif - - last_bit = ntfs_bmp_find_last_set(dir->bitmap); - if (dir->ialloc->allocated_size == (dir->index_size * (last_bit + 1))) { - //ntfs_log_debug("nothing to do\n"); - return 0; - } - - ntfs_log_debug("Truncation needed\n"); - -#if 0 - ntfs_log_debug("\tlast bit = %lld\n", last_bit); - ntfs_log_debug("\tactual IALLOC size = %lld\n", dir->ialloc->allocated_size); - ntfs_log_debug("\tshould IALLOC size = %lld\n", dir->index_size * (last_bit + 1)); -#endif - - if ((dir->index_size * (last_bit + 1)) == 0) { - ntfs_log_debug("root dt %d, vcn = %lld\n", dir->index->changed, dir->index->vcn); - //rollback all dts - //ntfs_dt_rollback(dir->index); - //dir->index = NULL; - // What about the ROOT dt? - - ie = ntfs_ie_copy(dir->index->children[0]); - if (!ie) { - ntfs_log_warning("IE copy failed\n"); - return -1; - } - - ie = ntfs_ie_remove_vcn(ie); - if (!ie) { - ntfs_log_warning("IE remove vcn failed\n"); - return -1; - } - - //utils_dump_mem(dir->index->data, 0, dir->index->data_len, DM_DEFAULTS); ntfs_log_debug("\n"); - //utils_dump_mem(ie, 0, ie->length, DM_DEFAULTS); ntfs_log_debug("\n"); - ntfs_dt_root_replace(dir->index, 0, dir->index->children[0], ie); - //utils_dump_mem(dir->index->data, 0, dir->index->data_len, DM_DEFAULTS); ntfs_log_debug("\n"); - //ntfs_log_debug("root dt %d, vcn = %lld\n", dir->index->changed, dir->index->vcn); - - ntfs_ie_free(ie); - ie = NULL; - - //index flags remove LARGE_INDEX - dir->index->header->flags = 0; - - //rollback dir's bmp - ntfs_bmp_free(dir->bitmap); - dir->bitmap = NULL; - - /* - for (i = 0; i < dir->index->child_count; i++) { - ntfs_dt_rollback(dir->index->sub_nodes[i]); - dir->index->sub_nodes[i] = NULL; - } - */ - - //ntfs_log_debug("dir->index->inodes[0] = %p\n", dir->index->inodes[0]); - - //remove 0xA0 attribute - ntfs_mft_remove_attr(vol->private_bmp2, dir->inode, AT_INDEX_ALLOCATION); - - //remove 0xB0 attribute - ntfs_mft_remove_attr(vol->private_bmp2, dir->inode, AT_BITMAP); - } else { - ntfs_log_warning("Cannot shrink directory\n"); - //ntfs_dir_shrink_alloc - //ntfs_dir_shrink_bitmap - //make bitmap resident? - } - - /* - * Remove - * dt -> dead - * bitmap updated - * rollback dead dts - * commit bitmap - * commit dts - * commit dir - */ - /* - * Reuse - * search for lowest dead - * update bitmap - * init dt - * remove from dead - * insert into tree - * init INDX - */ - -#if 0 - buffer = ntfs_bmp_get_data(dir->bitmap, 0); - if (!buffer) - return -1; - - utils_dump_mem(buffer, 0, 8, DM_NO_ASCII); - for (i = buf_count-1; i >= 0; i--) { - if (buffer[i]) { - ntfs_log_debug("alloc in use\n"); - return 0; - } - } -#endif - - // /$BITMAP($I30) - // /$INDEX_ALLOCATION($I30) - // $Bitmap - - // Find the highest set bit in the directory bitmap - // can we free any clusters of the alloc? - // if yes, resize attribute - - // Are *any* bits set? - // If not remove ialloc - - return 0; -} - -/** - * ntfs_dir_commit - Write to disk the in-memory directory changes - * @dir: - * - * Description... - * - * Returns: - */ -int ntfs_dir_commit(struct ntfs_dir *dir) -{ - int i; - - if (!dir) - return 0; - - ntfs_log_trace ("dir %p, inode %lld, children %d\n", dir, - dir ? MREF(dir->mft_num) : 0, dir ? dir->child_count : 0); - - if (NInoDirty(dir->inode)) { - ntfs_inode_sync(dir->inode); - ntfs_log_warning("\tntfs_inode_sync %llu\n", dir->inode->mft_no); - } - - if (ntfs_dt_commit(dir->index) < 0) - return -1; - - if (ntfs_bmp_commit(dir->bitmap) < 0) - return -1; - - for (i = 0; i < dir->child_count; i++) { - if (ntfs_dir_commit(dir->children[i]) < 0) - return -1; - } - - return 0; -} - -/** - * ntfs_dir_free - Destroy a directory object - * @dir: - * - * Description... - * - * Returns: - */ -void ntfs_dir_free(struct ntfs_dir *dir) -{ - struct ntfs_dir *parent; - int i; - - if (!dir) - return; - - ntfs_log_trace ("dir %p, inode %lld, children %d\n", dir, - dir ? MREF(dir->mft_num) : 0, dir ? dir->child_count : 0); - - ntfs_dir_rollback(dir); - - parent = dir->parent; - if (parent) { - for (i = 0; i < parent->child_count; i++) { - if (parent->children[i] == dir) { - parent->children[i] = NULL; - } - } - } - - ntfs_attr_close(dir->iroot); - ntfs_attr_close(dir->ialloc); - ntfs_inode_close2(dir->inode); - - ntfs_dt_free (dir->index); - ntfs_bmp_free(dir->bitmap); - - for (i = 0; i < dir->child_count; i++) - ntfs_dir_free(dir->children[i]); - - free(dir->name); - free(dir->children); - free(dir); -} - -/** - * ntfs_dir_create - Create a representation of a directory - * @vol: - * @mft_num: - * - * Description... - * - * Returns: - */ -struct ntfs_dir * ntfs_dir_create(ntfs_volume *vol, MFT_REF mft_num) -{ - struct ntfs_dir *dir = NULL; - ntfs_inode *inode = NULL; - ATTR_RECORD *rec = NULL; - INDEX_ROOT *ir = NULL; - FILE_NAME_ATTR *name = NULL; - - if (!vol) - return NULL; - - ntfs_log_trace ("inode %lld\n", MREF(mft_num)); - - inode = ntfs_inode_open2(vol, mft_num); - if (!inode) - return NULL; - - dir = calloc(1, sizeof(*dir)); - if (!dir) { - ntfs_inode_close2(inode); - return NULL; - } - - dir->inode = inode; - dir->iroot = ntfs_attr_open(inode, AT_INDEX_ROOT, NTFS_INDEX_I30, 4); - dir->ialloc = ntfs_attr_open(inode, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); - - if (!dir->iroot) { - ntfs_dir_free(dir); - return NULL; - } - - dir->vol = vol; - dir->parent = NULL; - dir->name = NULL; - dir->name_len = 0; - dir->index = NULL; - dir->children = NULL; - dir->child_count = 0; - dir->mft_num = mft_num; - - // This may not exist - dir->bitmap = ntfs_bmp_create(inode, AT_BITMAP, NTFS_INDEX_I30, 4); - - if (dir->iroot) { - rec = find_first_attribute(AT_INDEX_ROOT, inode->mrec); - ir = (INDEX_ROOT*) ((u8*)rec + rec->value_offset); - dir->index_size = ir->index_block_size; - ntfs_log_debug("dir size = %d\n", dir->index_size); - } else { - // XXX !iroot? - dir->index_size = 0; - } - - // Finally, find the dir's name - rec = find_first_attribute(AT_FILE_NAME, inode->mrec); - name = (FILE_NAME_ATTR*) ((u8*)rec + rec->value_offset); - - dir->name_len = name->file_name_length; - dir->name = malloc(sizeof(ntfschar) * dir->name_len); - memcpy(dir->name, name->file_name, sizeof(ntfschar) * dir->name_len); - - return dir; -} - -/** - * ntfs_dir_add - Add a directory to another as a child - * @parent: - * @child: - * - * Description... - * - * Returns: - */ -void ntfs_dir_add(struct ntfs_dir *parent, struct ntfs_dir *child) -{ - if (!parent || !child) - return; - - ntfs_log_trace ("parent %p, inode %lld, children %d\n", parent, - parent ? MREF(parent->mft_num) : 0, parent ? parent->child_count : 0); - ntfs_log_trace ("child %p, inode %lld, children %d\n", child, - child ? MREF(child->mft_num) : 0, child ? child->child_count : 0); - - parent->child_count++; - //ntfs_log_debug("child count = %d\n", parent->child_count); - parent->children = realloc(parent->children, parent->child_count * sizeof(struct ntfs_dir*)); - child->parent = parent; - - parent->children[parent->child_count-1] = child; -} - -/** - * ntfs_dir_find2 - Find a directory by name - * @dir: - * @name: - * @name_len: - * - * Description... - * - * Returns: - */ -struct ntfs_dir * ntfs_dir_find2(struct ntfs_dir *dir, ntfschar *name, int name_len) -{ - int i; - struct ntfs_dir *child = NULL; - struct ntfs_dt *dt = NULL; - int dt_num = 0; - INDEX_ENTRY *ie; - MFT_REF mft_num; - - if (!dir || !name) - return NULL; - - ntfs_log_trace ("dir %p, inode %lld, children %d\n", dir, - dir ? MREF(dir->mft_num) : 0, dir ? dir->child_count : 0); - - if (!dir->index) { // XXX when will this happen? - ntfs_log_debug("ntfs_dir_find2 - directory has no index\n"); - return NULL; - } - - for (i = 0; i < dir->child_count; i++) { - if (0 == ntfs_names_collate(name, name_len, - dir->children[i]->name, - dir->children[i]->name_len, - 2, IGNORE_CASE, - dir->vol->upcase, - dir->vol->upcase_len)) - return dir->children[i]; - } - - dt = ntfs_dt_find2(dir->index, name, name_len, &dt_num); - if (!dt) { - ntfs_log_debug("can't find name in dir\n"); - return NULL; - } - - ie = dt->children[dt_num]; - - mft_num = ie->indexed_file; - - child = ntfs_dir_create(dir->vol, mft_num); - if (!child) - return NULL; - - child->index = ntfs_dt_create(child, NULL, -1); - - ntfs_dir_add(dir, child); - - return child; -} - - -#endif /* NTFS_RICH */ - diff --git a/libntfs/index.c b/libntfs/index.c deleted file mode 100644 index a33926f1..00000000 --- a/libntfs/index.c +++ /dev/null @@ -1,982 +0,0 @@ -/** - * index.c - NTFS index handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2004-2005 Anton Altaparmakov - * Copyright (c) 2005 Yura Pakhuchiy - * Copyright (c) 2004-2005 Richard Russon - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif - -#include "attrib.h" -#include "collate.h" -#include "debug.h" -#include "index.h" -#include "mst.h" -#include "dir.h" -#include "logging.h" - -/** - * ntfs_index_ctx_get - allocate and initialize a new index context - * @ni: ntfs inode with which to initialize the context - * @name: name of the which context describes - * @name_len: length of the index name - * - * Allocate a new index context, initialize it with @ni and return it. - * Return NULL if allocation failed. - */ -ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni, - ntfschar *name, u32 name_len) -{ - ntfs_index_context *ictx; - - if (!ni) { - errno = EINVAL; - return NULL; - } - if (ni->nr_extents == -1) - ni = ni->base_ni; - ictx = malloc(sizeof(ntfs_index_context)); - if (ictx) - *ictx = (ntfs_index_context) { - .ni = ni, - .name = name, - .name_len = name_len, - }; - return ictx; -} - -/** - * ntfs_index_ctx_put - release an index context - * @ictx: index context to free - * - * Release the index context @ictx, releasing all associated resources. - */ -void ntfs_index_ctx_put(ntfs_index_context *ictx) -{ - if (ictx->entry) { - if (ictx->is_in_root) { - if (ictx->actx) - ntfs_attr_put_search_ctx(ictx->actx); - } else { - /* Write out index block it it's dirty. */ - if (ictx->ia_dirty) { - if (ntfs_attr_mst_pwrite(ictx->ia_na, - ictx->ia_vcn << - ictx->vcn_size_bits, - 1, ictx->block_size, - ictx->ia) != 1) - ntfs_log_error("Failed to write out " - "index block.\n"); - } - /* Free resources. */ - free(ictx->ia); - ntfs_attr_close(ictx->ia_na); - } - } - free(ictx); -} - -/** - * ntfs_index_ctx_reinit - reinitialize an index context - * @ictx: index context to reinitialize - * - * Reinitialize the index context @ictx so it can be used for ntfs_index_lookup. - */ -void ntfs_index_ctx_reinit(ntfs_index_context *ictx) -{ - if (ictx->entry) { - if (ictx->is_in_root) { - if (ictx->actx) - ntfs_attr_put_search_ctx(ictx->actx); - } else { - /* Write out index block it it's dirty. */ - if (ictx->ia_dirty) { - if (ntfs_attr_mst_pwrite(ictx->ia_na, - ictx->ia_vcn << - ictx->vcn_size_bits, - 1, ictx->block_size, - ictx->ia) != 1) - ntfs_log_error("Failed to write out " - "index block.\n"); - } - /* Free resources. */ - free(ictx->ia); - ntfs_attr_close(ictx->ia_na); - } - } - *ictx = (ntfs_index_context) { - .ni = ictx->ni, - .name = ictx->name, - .name_len = ictx->name_len, - }; -} - -/** - * ntfs_index_lookup - find a key in an index and return its index entry - * @key: [IN] key for which to search in the index - * @key_len: [IN] length of @key in bytes - * @ictx: [IN/OUT] context describing the index and the returned entry - * - * Before calling ntfs_index_lookup(), @ictx must have been obtained from a - * call to ntfs_index_ctx_get(). - * - * Look for the @key in the index specified by the index lookup context @ictx. - * ntfs_index_lookup() walks the contents of the index looking for the @key. - * - * If the @key is found in the index, 0 is returned and @ictx is setup to - * describe the index entry containing the matching @key. @ictx->entry is the - * index entry and @ictx->data and @ictx->data_len are the index entry data and - * its length in bytes, respectively. - * - * If the @key is not found in the index, -1 is returned, errno = ENOENT and - * @ictx is setup to describe the index entry whose key collates immediately - * after the search @key, i.e. this is the position in the index at which - * an index entry with a key of @key would need to be inserted. - * - * If an error occurs return -1, set errno to error code and @ictx is left - * untouched. - * - * When finished with the entry and its data, call ntfs_index_ctx_put() to free - * the context and other associated resources. - * - * If the index entry was modified, call ntfs_index_entry_mark_dirty() before - * the call to ntfs_index_ctx_put() to ensure that the changes are written - * to disk. - */ -int ntfs_index_lookup(const void *key, const int key_len, - ntfs_index_context *ictx) -{ - COLLATION_RULES cr; - VCN vcn; - ntfs_inode *ni = ictx->ni; - ntfs_volume *vol = ni->vol; - INDEX_ROOT *ir; - INDEX_ENTRY *ie; - INDEX_ALLOCATION *ia = NULL; - u8 *index_end; - ntfs_attr_search_ctx *actx; - ntfs_attr *na = NULL; - int rc, err = 0; - - ntfs_log_trace("Entering.\n"); - if (!key || key_len <= 0) { - errno = EINVAL; - return -1; - } - - actx = ntfs_attr_get_search_ctx(ni, NULL); - if (!actx) { - err = ENOMEM; - goto err_out; - } - - /* Find the index root attribute in the mft record. */ - err = ntfs_attr_lookup(AT_INDEX_ROOT, ictx->name, ictx->name_len, - CASE_SENSITIVE, 0, NULL, 0, actx); - if (err) { - if (errno == ENOENT) { - ntfs_log_error("Index root attribute missing in inode " - "0x%llx.\n", ni->mft_no); - err = EIO; - } else - err = errno; - goto err_out; - } - /* Get to the index root value (it has been verified in read_inode). */ - ir = (INDEX_ROOT*)((u8*)actx->attr + - le16_to_cpu(actx->attr->value_offset)); - index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); - /* Save index block size for future use. */ - ictx->block_size = le32_to_cpu(ir->index_block_size); - /* Determine the size of a vcn in the directory index. */ - if (vol->cluster_size <= ictx->block_size) { - ictx->vcn_size = vol->cluster_size; - ictx->vcn_size_bits = vol->cluster_size_bits; - } else { - ictx->vcn_size = vol->sector_size; - ictx->vcn_size_bits = vol->sector_size_bits; - } - /* Get collation rule type and validate it. */ - cr = ir->collation_rule; - if (!ntfs_is_collation_rule_supported(cr)) { - ntfs_log_error("Index uses unsupported collation rule 0x%x. " - "Aborting lookup.\n", (unsigned)le32_to_cpu(cr)); - err = EOPNOTSUPP; - goto err_out; - } - /* The first index entry. */ - ie = (INDEX_ENTRY*)((u8*)&ir->index + - le32_to_cpu(ir->index.entries_offset)); - /* - * Loop until we exceed valid memory (corruption case) or until we - * reach the last entry. - */ - for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { - /* Bounds checks. */ - if ((u8*)ie < (u8*)actx->mrec || (u8*)ie + - sizeof(INDEX_ENTRY_HEADER) > index_end || - (u8*)ie + le16_to_cpu(ie->length) > index_end) - goto idx_err_out; - /* - * The last entry cannot contain a key. It can however contain - * a pointer to a child node in the B+tree so we just break out. - */ - if (ie->flags & INDEX_ENTRY_END) - break; - /* If the keys match perfectly, we setup @ictx and return 0. */ - if ((key_len == le16_to_cpu(ie->key_length)) && !memcmp(key, - &ie->key, key_len)) { -ir_done: - ictx->is_in_root = TRUE; - ictx->actx = actx; - ictx->ia = NULL; - ictx->ir = ir; -done: - ictx->entry = ie; - ictx->data = (u8*)ie + offsetof(INDEX_ENTRY, key); - ictx->data_len = le16_to_cpu(ie->key_length); - ntfs_log_trace("Done.\n"); - if (err) { - errno = err; - return -1; - } - return 0; - } - /* - * Not a perfect match, need to do full blown collation so we - * know which way in the B+tree we have to go. - */ - rc = ntfs_collate(vol, cr, key, key_len, &ie->key, - le16_to_cpu(ie->key_length)); - if (rc == NTFS_COLLATION_ERROR) { - ntfs_log_error("Collation error. Probably filename " - "contain invalid characters.\n"); - err = ERANGE; - goto err_out; - } - /* - * If @key collates before the key of the current entry, there - * is definitely no such key in this index but we might need to - * descend into the B+tree so we just break out of the loop. - */ - if (rc == -1) - break; - /* - * A match should never happen as the memcmp() call should have - * caught it, but we still treat it correctly. - */ - if (!rc) - goto ir_done; - /* The keys are not equal, continue the search. */ - } - /* - * We have finished with this index without success. Check for the - * presence of a child node and if not present setup @ictx and return - * -1 with errno ENOENT. - */ - if (!(ie->flags & INDEX_ENTRY_NODE)) { - ntfs_log_debug("Entry not found.\n"); - err = ENOENT; - goto ir_done; - } /* Child node present, descend into it. */ - /* Get the starting vcn of the index_block holding the child node. */ - vcn = sle64_to_cpup((sle64*)((u8*)ie + le16_to_cpu(ie->length) - 8)); - /* We are done with the index root. Release attribute search ctx. */ - ntfs_attr_put_search_ctx(actx); - actx = NULL; - /* Open INDEX_ALLOCATION. */ - na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, - ictx->name, ictx->name_len); - if (!na) { - ntfs_log_error("No index allocation attribute but index entry " - "requires one. Inode 0x%llx is corrupt or " - "library bug.\n", ni->mft_no); - goto err_out; - } - /* Allocate memory to store index block. */ - ia = malloc(ictx->block_size); - if (!ia) { - ntfs_log_error("Not enough memory to allocate buffer for index" - " allocation.\n"); - err = ENOMEM; - goto err_out; - } -descend_into_child_node: - ntfs_log_debug("Descend into node with VCN %lld.\n", vcn); - /* Read index allocation block. */ - if (ntfs_attr_mst_pread(na, vcn << ictx->vcn_size_bits, 1, - ictx->block_size, ia) != 1) { - ntfs_log_error("Failed to read index allocation.\n"); - goto err_out; - } - /* Catch multi sector transfer fixup errors. */ - if (!ntfs_is_indx_record(ia->magic)) { - ntfs_log_error("Index record with vcn 0x%llx is corrupt. " - "Corrupt inode 0x%llx. Run chkdsk.\n", - (long long)vcn, ni->mft_no); - goto err_out; - } - if (sle64_to_cpu(ia->index_block_vcn) != vcn) { - ntfs_log_error("Actual VCN (0x%llx) of index buffer is " - "different from expected VCN (0x%llx). Inode " - "0x%llx is corrupt or driver bug.\n", - (unsigned long long) - sle64_to_cpu(ia->index_block_vcn), - (unsigned long long)vcn, ni->mft_no); - goto err_out; - } - if (le32_to_cpu(ia->index.allocated_size) + 0x18 != ictx->block_size) { - ntfs_log_error("Index buffer (VCN 0x%llx) of inode 0x%llx has " - "a size (%u) differing from the index " - "specified size (%u). Inode is corrupt or " - "driver bug.\n", (unsigned long long)vcn, - ni->mft_no, (unsigned) - le32_to_cpu(ia->index.allocated_size) + 0x18, - (unsigned)ictx->block_size); - goto err_out; - } - index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); - if (index_end > (u8*)ia + ictx->block_size) { - ntfs_log_error("Size of index buffer (VCN 0x%llx) of inode " - "0x%llx exceeds maximum size.\n", - (unsigned long long)vcn, ni->mft_no); - goto err_out; - } - /* The first index entry. */ - ie = (INDEX_ENTRY*)((u8*)&ia->index + - le32_to_cpu(ia->index.entries_offset)); - /* - * Iterate similar to above big loop but applied to index buffer, thus - * loop until we exceed valid memory (corruption case) or until we - * reach the last entry. - */ - for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { - /* Bounds checks. */ - if ((u8*)ie < (u8*)ia || (u8*)ie + - sizeof(INDEX_ENTRY_HEADER) > index_end || - (u8*)ie + le16_to_cpu(ie->length) > index_end) { - ntfs_log_error("Index entry out of bounds in inode " - "0x%llx.\n", ni->mft_no); - goto err_out; - } - /* - * The last entry cannot contain a key. It can however contain - * a pointer to a child node in the B+tree so we just break out. - */ - if (ie->flags & INDEX_ENTRY_END) - break; - /* If the keys match perfectly, we setup @ictx and return 0. */ - if ((key_len == le16_to_cpu(ie->key_length)) && !memcmp(key, - &ie->key, key_len)) { -ia_done: - ictx->is_in_root = FALSE; - ictx->actx = NULL; - ictx->ia = ia; - ictx->ia_vcn = vcn; - ictx->ia_na = na; - goto done; - } - /* - * Not a perfect match, need to do full blown collation so we - * know which way in the B+tree we have to go. - */ - rc = ntfs_collate(vol, cr, key, key_len, &ie->key, - le16_to_cpu(ie->key_length)); - if (rc == NTFS_COLLATION_ERROR) { - ntfs_log_error("Collation error. Probably filename " - "contain invalid characters.\n"); - err = ERANGE; - goto err_out; - } - /* - * If @key collates before the key of the current entry, there - * is definitely no such key in this index but we might need to - * descend into the B+tree so we just break out of the loop. - */ - if (rc == -1) - break; - /* - * A match should never happen as the memcmp() call should have - * caught it, but we still treat it correctly. - */ - if (!rc) - goto ia_done; - /* The keys are not equal, continue the search. */ - } - /* - * We have finished with this index buffer without success. Check for - * the presence of a child node and if not present return ENOENT. - */ - if (!(ie->flags & INDEX_ENTRY_NODE)) { - ntfs_log_debug("Entry not found.\n"); - err = ENOENT; - goto ia_done; - } - if ((ia->index.flags & NODE_MASK) == LEAF_NODE) { - ntfs_log_error("Index entry with child node found in a leaf " - "node in inode 0x%llx.\n", ni->mft_no); - goto err_out; - } - /* Child node present, descend into it. */ - vcn = sle64_to_cpup((sle64*)((u8*)ie + le16_to_cpu(ie->length) - 8)); - if (vcn >= 0) - goto descend_into_child_node; - ntfs_log_error("Negative child node vcn in inode 0x%llx.\n", ni->mft_no); -err_out: - if (na) - ntfs_attr_close(na); - free(ia); - if (!err) - err = EIO; - if (actx) - ntfs_attr_put_search_ctx(actx); - errno = err; - return -1; -idx_err_out: - ntfs_log_error("Corrupt index. Aborting lookup.\n"); - goto err_out; -} - -/** - * ntfs_index_add_filename - add filename to directory index - * @ni: ntfs inode describing directory to which index add filename - * @fn: FILE_NAME attribute to add - * @mref: reference of the inode which @fn describes - * - * NOTE: This function does not support all cases, so it can fail with - * EOPNOTSUPP error code. - * - * Return 0 on success or -1 on error with errno set to the error code. - */ -int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, MFT_REF mref) -{ - ntfs_index_context *ictx; - INDEX_ENTRY *ie; - INDEX_HEADER *ih; - int err, fn_size, ie_size, allocated_size = 0; - - ntfs_log_trace("Entering.\n"); - if (!ni || !fn) { - ntfs_log_error("Invalid arguments.\n"); - errno = EINVAL; - return -1; - } - ictx = ntfs_index_ctx_get(ni, NTFS_INDEX_I30, 4); - if (!ictx) - return -1; - fn_size = (fn->file_name_length * sizeof(ntfschar)) + - sizeof(FILE_NAME_ATTR); - ie_size = (sizeof(INDEX_ENTRY_HEADER) + fn_size + 7) & ~7; -retry: - /* Find place where insert new entry. */ - if (!ntfs_index_lookup(fn, fn_size, ictx)) { - err = EEXIST; - ntfs_log_error("Index already have such entry.\n"); - goto err_out; - } - if (errno != ENOENT) { - err = errno; - ntfs_log_error("Failed to find place where to insert new entry.\n"); - goto err_out; - } - /* Some setup. */ - if (ictx->is_in_root) - ih = &ictx->ir->index; - else - ih = &ictx->ia->index; - if (!allocated_size) - allocated_size = le16_to_cpu(ih->allocated_size); - /* Check whether we have enough space in the index. */ - if (le16_to_cpu(ih->index_length) + ie_size > allocated_size) { - /* If we in the index root try to resize it. */ - if (ictx->is_in_root) { - ntfs_attr *na; - - allocated_size = le16_to_cpu(ih->index_length) + - ie_size; - na = ntfs_attr_open(ictx->ni, AT_INDEX_ROOT, ictx->name, - ictx->name_len); - if (!na) { - err = errno; - ntfs_log_error("Failed to open INDEX_ROOT.\n"); - goto err_out; - } - if (ntfs_attr_truncate(na, allocated_size + offsetof( - INDEX_ROOT, index))) { - err = EOPNOTSUPP; - ntfs_attr_close(na); - ntfs_log_debug("Failed to truncate " - "INDEX_ROOT.\n"); - goto err_out; - } - ntfs_attr_close(na); - ntfs_index_ctx_reinit(ictx); - goto retry; - } - ntfs_log_debug("Not implemented case.\n"); - err = EOPNOTSUPP; - goto err_out; - } - /* Update allocated size if we in INDEX_ROOT. */ - if (ictx->is_in_root) - ih->allocated_size = cpu_to_le16(allocated_size); - /* Create entry. */ - ie = calloc(1, ie_size); - if (!ie) { - err = errno; - goto err_out; - } - ie->indexed_file = cpu_to_le64(mref); - ie->length = cpu_to_le16(ie_size); - ie->key_length = cpu_to_le16(fn_size); - memcpy(&ie->key, fn, fn_size); - /* Update index length, move following entries forard and copy entry. */ - ih->index_length = cpu_to_le16(le16_to_cpu(ih->index_length) + ie_size); - memmove((u8*)ictx->entry + ie_size, ictx->entry, - le16_to_cpu(ih->index_length) - - ((u8*)ictx->entry - (u8*)ih) - ie_size); - memcpy(ictx->entry, ie, ie_size); - /* Done! */ - ntfs_index_entry_mark_dirty(ictx); - ntfs_index_ctx_put(ictx); - free(ie); - ntfs_log_trace("Done.\n"); - return 0; -err_out: - ntfs_log_trace("Failed.\n"); - ntfs_index_ctx_put(ictx); - errno = err; - return -1; -} - -/** - * ntfs_index_rm - remove entry from the index - * @ictx: index context describing entry to delete - * - * Delete entry described by @ictx from the index. NOTE: This function does not - * support all cases, so it can fail with EOPNOTSUPP error code. In any case - * index context is always reinitialized after use of this function, so it can - * be used for index lookup once again. - * - * Return 0 on success or -1 on error with errno set to the error code. - */ -int ntfs_index_rm(ntfs_index_context *ictx) -{ - INDEX_HEADER *ih; - u32 new_index_length; - int err; - - ntfs_log_trace("Entering.\n"); - if (!ictx || (!ictx->ia && !ictx->ir) || - ictx->entry->flags & INDEX_ENTRY_END) { - ntfs_log_error("Invalid arguments.\n"); - err = EINVAL; - goto err_out; - } - if (ictx->is_in_root) - ih = &ictx->ir->index; - else - ih = &ictx->ia->index; - /* Don't support deletion of entries with subnodes yet. */ - if (ictx->entry->flags & INDEX_ENTRY_NODE) { - err = EOPNOTSUPP; - goto err_out; - } - /* Calculate new length of the index. */ - new_index_length = le32_to_cpu(ih->index_length) - - le16_to_cpu(ictx->entry->length); - /* Don't support deletion of the last entry in the allocation block. */ - if (!ictx->is_in_root && (new_index_length <= - le32_to_cpu(ih->entries_offset) + - sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN))) { - err = EOPNOTSUPP; - goto err_out; - } - /* Update index length and remove index entry. */ - ih->index_length = cpu_to_le32(new_index_length); - if (ictx->is_in_root) - ih->allocated_size = ih->index_length; - memmove(ictx->entry, (u8*)ictx->entry + le16_to_cpu( - ictx->entry->length), new_index_length - - ((u8*)ictx->entry - (u8*)ih)); - ntfs_index_entry_mark_dirty(ictx); - /* Resize INDEX_ROOT attribute. */ - if (ictx->is_in_root) { - ntfs_attr *na; - - na = ntfs_attr_open(ictx->ni, AT_INDEX_ROOT, ictx->name, - ictx->name_len); - if (!na) { - err = errno; - ntfs_log_error("Failed to open INDEX_ROOT attribute. " - "Leaving inconsistent metadata.\n"); - goto err_out; - } - if (ntfs_attr_truncate(na, new_index_length + offsetof( - INDEX_ROOT, index))) { - err = errno; - ntfs_log_error("Failed to truncate INDEX_ROOT " - "attribute. Leaving inconsistent " - "metadata.\n"); - goto err_out; - } - ntfs_attr_close(na); - } - ntfs_index_ctx_reinit(ictx); - ntfs_log_trace("Done.\n"); - return 0; -err_out: - ntfs_index_ctx_reinit(ictx); - ntfs_log_trace("Failed.\n"); - errno = err; - return -1; -} - - -#ifdef NTFS_RICH - -#ifdef HAVE_STRING_H -#include -#endif -#include "rich.h" - -/** - * ntfs_ie_free - Destroy an index entry object - * @ie: - * - * Description... - * - * Returns: - */ -void ntfs_ie_free(INDEX_ENTRY *ie) -{ - ntfs_log_trace ("ie %p, inode %lld\n", ie, MREF(ie->indexed_file)); - free(ie); -} - -/** - * ntfs_ie_create - Create a representation of an directory index entry - * - * Description... - * - * Returns: - */ -INDEX_ENTRY * ntfs_ie_create(void) -{ - int length; - INDEX_ENTRY *ie; - - ntfs_log_trace ("\n"); - length = 16; - ie = calloc(1, length); - if (!ie) - return NULL; - - ie->indexed_file = 0; - ie->length = length; - ie->key_length = 0; - ie->flags = INDEX_ENTRY_END; - ie->reserved = 0; - return ie; -} - -/** - * ntfs_ie_get_vcn - Get the VCN associated with an index entry - * @ie: - * - * Description... - * - * Returns: - */ -VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie) -{ - if (!ie) - return -1; - if (!(ie->flags & INDEX_ENTRY_NODE)) - return -1; - - ntfs_log_trace ("\n"); - return *((VCN*) ((u8*) ie + ie->length - 8)); -} - -/** - * ntfs_ie_copy - Create a copy of an index entry - * @ie: - * - * Description... - * - * Returns: - */ -INDEX_ENTRY * ntfs_ie_copy(INDEX_ENTRY *ie) -{ - INDEX_ENTRY *copy = NULL; - - if (!ie) - return NULL; - - ntfs_log_trace ("\n"); - copy = malloc(ie->length); - if (!copy) - return NULL; - memcpy(copy, ie, ie->length); - - return copy; -} - -/** - * ntfs_ie_set_vcn - Set VCN associated with an index entry - * @ie: - * @vcn: - * - * Description... - * - * Returns: - */ -INDEX_ENTRY * ntfs_ie_set_vcn(INDEX_ENTRY *ie, VCN vcn) -{ - if (!ie) - return 0; - - ntfs_log_trace ("\n"); - if (!(ie->flags & INDEX_ENTRY_NODE)) { - ie->length += 8; - ie = realloc(ie, ie->length); - if (!ie) - return NULL; - - ie->flags |= INDEX_ENTRY_NODE; - } - - *((VCN*) ((u8*) ie + ie->length - 8)) = vcn; - return ie; -} - -/** - * ntfs_ie_remove_vcn - Remove the VCN associated with an index entry - * @ie: - * - * Description... - * - * Returns: - */ -INDEX_ENTRY * ntfs_ie_remove_vcn(INDEX_ENTRY *ie) -{ - if (!ie) - return NULL; - if (!(ie->flags & INDEX_ENTRY_NODE)) - return ie; - - ntfs_log_trace ("\n"); - ie->length -= 8; - ie->flags &= ~INDEX_ENTRY_NODE; - ie = realloc(ie, ie->length); - return ie; -} - -/** - * ntfs_ie_set_name - Associate a name with an index entry - * @ie: - * @name: - * @namelen: - * @nametype: - * - * Description... - * - * Returns: - */ -INDEX_ENTRY * ntfs_ie_set_name(INDEX_ENTRY *ie, ntfschar *name, int namelen, FILE_NAME_TYPE_FLAGS nametype) -{ - FILE_NAME_ATTR *file; - int need; - BOOL wipe = FALSE; - VCN vcn = 0; - - if (!ie || !name) - return NULL; - - ntfs_log_trace ("\n"); - /* - * INDEX_ENTRY - * MFT_REF indexed_file; - * u16 length; - * u16 key_length; - * INDEX_ENTRY_FLAGS flags; - * u16 reserved; - * - * FILENAME - * MFT_REF parent_directory; - * s64 creation_time; - * s64 last_data_change_time; - * s64 last_mft_change_time; - * s64 last_access_time; - * s64 allocated_size; - * s64 data_size; - * FILE_ATTR_FLAGS file_attributes; - * u32 reserved; - * u8 file_name_length; - * FILE_NAME_TYPE_FLAGS file_name_type; - * ntfschar file_name[l]; - * u8 reserved[n] - * - * VCN vcn; - */ - - //ntfs_log_debug("key length = 0x%02X\n", ie->key_length); - //ntfs_log_debug("new name length = %d\n", namelen); - if (ie->key_length > 0) { - file = &ie->key.file_name; - //ntfs_log_debug("filename, length %d\n", file->file_name_length); - need = ATTR_SIZE(namelen * sizeof(ntfschar) + 2) - - ATTR_SIZE(file->file_name_length * sizeof(ntfschar) + 2); - } else { - //ntfs_log_debug("no filename\n"); - need = ATTR_SIZE(sizeof(FILE_NAME_ATTR) + (namelen * sizeof(ntfschar))); - wipe = TRUE; - } - - //ntfs_log_debug("need 0x%02X bytes\n", need); - - if (need != 0) { - if (ie->flags & INDEX_ENTRY_NODE) - vcn = ntfs_ie_get_vcn(ie); - - ie->length += need; - ie->key_length += need; - - //ntfs_log_debug("realloc 0x%02X\n", ie->length); - ie = realloc(ie, ie->length); - if (!ie) - return NULL; - - if (ie->flags & INDEX_ENTRY_NODE) - ie = ntfs_ie_set_vcn(ie, vcn); - - if (wipe) - memset(&ie->key.file_name, 0, sizeof(FILE_NAME_ATTR)); - if (need > 0) - memset((u8*)ie + ie->length - need, 0, need); - } - - memcpy(ie->key.file_name.file_name, name, namelen * sizeof(ntfschar)); - - ie->key.file_name.file_name_length = namelen; - ie->key.file_name.file_name_type = nametype; - ie->flags &= ~INDEX_ENTRY_END; - - //ntfs_log_debug("ie->length = 0x%02X\n", ie->length); - //ntfs_log_debug("ie->key_length = 0x%02X\n", ie->key_length); - - return ie; -} - -/** - * ntfs_ie_remove_name - Remove the name from an index-entry - * @ie: - * - * Description... - * - * Returns: - */ -INDEX_ENTRY * ntfs_ie_remove_name(INDEX_ENTRY *ie) -{ - VCN vcn = 0; - - if (!ie) - return NULL; - if (ie->key_length == 0) - return ie; - - ntfs_log_trace ("\n"); - if (ie->flags & INDEX_ENTRY_NODE) - vcn = ntfs_ie_get_vcn(ie); - - ie->length -= ATTR_SIZE(ie->key_length); - ie->key_length = 0; - ie->flags |= INDEX_ENTRY_END; - - ie = realloc(ie, ie->length); - if (!ie) - return NULL; - - if (ie->flags & INDEX_ENTRY_NODE) - ie = ntfs_ie_set_vcn(ie, vcn); - return ie; -} - - -#endif /* NTFS_RICH */ - -/** - * ntfs_index_root_get - read the index root of an attribute - * @ni: open ntfs inode in which the ntfs attribute resides - * @attr: attribute for which we want its index root - * - * This function will read the related index root an ntfs attribute. - * - * On success a buffer is allocated with the content of the index root - * and which needs to be freed when it's not needed anymore. - * - * On error NULL is returned with errno set to the error code. - */ -INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr) -{ - ntfs_attr_search_ctx *ctx; - ntfschar *name; - INDEX_ROOT *root = NULL; - - name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); - - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) { - ntfs_log_perror("ntfs_get_search_ctx failed"); - return NULL; - } - - if (ntfs_attr_lookup(AT_INDEX_ROOT, name, attr->name_length, 0, 0, NULL, - 0, ctx)) { - ntfs_log_perror("ntfs_attr_lookup failed"); - goto out; - } - - root = malloc(sizeof(INDEX_ROOT)); - if (!root) { - ntfs_log_perror("malloc failed"); - goto out; - } - - *root = *((INDEX_ROOT *)((u8 *)ctx->attr + - le16_to_cpu(ctx->attr->value_offset))); -out: - ntfs_attr_put_search_ctx(ctx); - return root; -} - - diff --git a/libntfs/inode.c b/libntfs/inode.c deleted file mode 100644 index 2fef1f6a..00000000 --- a/libntfs/inode.c +++ /dev/null @@ -1,1288 +0,0 @@ -/** - * inode.c - Inode handling code. Part of the Linux-NTFS project. - * - * Copyright (c) 2002-2005 Anton Altaparmakov - * Copyright (c) 2004-2005 Yura Pakhuchiy - * Copyright (c) 2004-2005 Richard Russon - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif - -#include "compat.h" -#include "types.h" -#include "attrib.h" -#include "inode.h" -#include "debug.h" -#include "mft.h" -#include "attrlist.h" -#include "runlist.h" -#include "lcnalloc.h" -#include "index.h" -#include "dir.h" -#include "ntfstime.h" -#include "logging.h" - -/** - * __ntfs_inode_allocate - Create and initialise an NTFS inode object - * @vol: - * - * Description... - * - * Returns: - */ -static __inline__ ntfs_inode *__ntfs_inode_allocate(ntfs_volume *vol) -{ - ntfs_inode *ni; - - ni = (ntfs_inode*)calloc(1, sizeof(ntfs_inode)); - if (ni) - ni->vol = vol; - return ni; -} - -/** - * ntfs_inode_allocate - Create an NTFS inode object - * @vol: - * - * Description... - * - * Returns: - */ -ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol) -{ - return __ntfs_inode_allocate(vol); -} - -/** - * __ntfs_inode_release - Destroy an NTFS inode object - * @ni: - * - * Description... - * - * Returns: - */ -static __inline__ int __ntfs_inode_release(ntfs_inode *ni) -{ - if (NInoDirty(ni)) - ntfs_log_debug("Eeek. Discarding dirty inode!\n"); - if (NInoAttrList(ni) && ni->attr_list) - free(ni->attr_list); - free(ni->mrec); - free(ni); - return 0; -} - -/** - * ntfs_inode_open - open an inode ready for access - * @vol: volume to get the inode from - * @mref: inode number / mft record number to open - * - * Allocate an ntfs_inode structure and initialize it for the given inode - * specified by @mref. @mref specifies the inode number / mft record to read, - * including the sequence number, which can be 0 if no sequence number checking - * is to be performed. - * - * Then, allocate a buffer for the mft record, read the mft record from the - * volume @vol, and attach it to the ntfs_inode structure (->mrec). The - * mft record is mst deprotected and sanity checked for validity and we abort - * if deprotection or checks fail. - * - * Finally, search for an attribute list attribute in the mft record and if one - * is found, load the attribute list attribute value and attach it to the - * ntfs_inode structure (->attr_list). Also set the NI_AttrList bit to indicate - * this. - * - * Return a pointer to the ntfs_inode structure on success or NULL on error, - * with errno set to the error code. - */ -ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) -{ - s64 l; - ntfs_inode *ni; - ntfs_attr_search_ctx *ctx; - int err = 0; - STANDARD_INFORMATION *std_info; - - ntfs_log_trace("Entering for inode 0x%llx.\n", MREF(mref)); - if (!vol) { - errno = EINVAL; - return NULL; - } - ni = __ntfs_inode_allocate(vol); - if (!ni) - return NULL; - if (ntfs_file_record_read(vol, mref, &ni->mrec, NULL)) - goto err_out; - if (!(ni->mrec->flags & MFT_RECORD_IN_USE)) { - err = ENOENT; - goto err_out; - } - ni->mft_no = MREF(mref); - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) - goto err_out; - /* Receive some basic information about inode. */ - if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, - 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { - err = errno; - ntfs_log_trace("Failed to receive STANDARD_INFORMATION " - "attribute.\n"); - goto put_err_out; - } - std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + - le16_to_cpu(ctx->attr->value_offset)); - ni->flags = std_info->file_attributes; - ni->creation_time = ntfs2utc(std_info->creation_time); - ni->last_data_change_time = ntfs2utc(std_info->last_data_change_time); - ni->last_mft_change_time = ntfs2utc(std_info->last_mft_change_time); - ni->last_access_time = ntfs2utc(std_info->last_access_time); - /* Set attribute list information. */ - if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0, - ctx)) { - if (errno != ENOENT) - goto put_err_out; - /* Attribute list attribute does not present. */ - goto get_size; - } - NInoSetAttrList(ni); - l = ntfs_get_attribute_value_length(ctx->attr); - if (!l) - goto put_err_out; - if (l > 0x40000) { - err = EIO; - goto put_err_out; - } - ni->attr_list_size = l; - ni->attr_list = malloc(ni->attr_list_size); - if (!ni->attr_list) - goto put_err_out; - l = ntfs_get_attribute_value(vol, ctx->attr, ni->attr_list); - if (!l) - goto put_err_out; - if (l != ni->attr_list_size) { - err = EIO; - goto put_err_out; - } -get_size: - if (ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { - if (errno != ENOENT) - goto put_err_out; - /* Directory or special file. */ - ni->data_size = ni->allocated_size = 0; - } else { - if (ctx->attr->non_resident) { - ni->data_size = sle64_to_cpu(ctx->attr->data_size); - if (ctx->attr->flags & - (ATTR_IS_COMPRESSED & ATTR_IS_SPARSE)) - ni->allocated_size = sle64_to_cpu( - ctx->attr->compressed_size); - else - ni->allocated_size = sle64_to_cpu( - ctx->attr->allocated_size); - } else { - ni->data_size = le32_to_cpu(ctx->attr->value_length); - ni->allocated_size = (ni->data_size + 7) & ~7; - } - } - ntfs_attr_put_search_ctx(ctx); - return ni; -put_err_out: - if (!err) - err = errno; - ntfs_attr_put_search_ctx(ctx); -err_out: - if (!err) - err = errno; - __ntfs_inode_release(ni); - errno = err; - return NULL; -} - -/** - * ntfs_inode_close - close an ntfs inode and free all associated memory - * @ni: ntfs inode to close - * - * Make sure the ntfs inode @ni is clean. - * - * If the ntfs inode @ni is a base inode, close all associated extent inodes, - * then deallocate all memory attached to it, and finally free the ntfs inode - * structure itself. - * - * If it is an extent inode, we disconnect it from its base inode before we - * destroy it. - * - * Return 0 on success or -1 on error with errno set to the error code. On - * error, @ni has not been freed. The user should attempt to handle the error - * and call ntfs_inode_close() again. The following error codes are defined: - * - * EBUSY @ni and/or its attribute list runlist is/are dirty and the - * attempt to write it/them to disk failed. - * EINVAL @ni is invalid (probably it is an extent inode). - * EIO I/O error while trying to write inode to disk. - */ -int ntfs_inode_close(ntfs_inode *ni) -{ - if (!ni) - return 0; - - ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); - - /* If we have dirty metadata, write it out. */ - if (NInoDirty(ni) || NInoAttrListDirty(ni)) { - if (ntfs_inode_sync(ni)) { - if (errno != EIO) - errno = EBUSY; - return -1; - } - } - /* Is this a base inode with mapped extent inodes? */ - if (ni->nr_extents > 0) { - while (ni->nr_extents > 0) { - if (ntfs_inode_close(ni->extent_nis[0])) { - if (errno != EIO) - errno = EBUSY; - return -1; - } - } - } else if (ni->nr_extents == -1) { - ntfs_inode **tmp_nis; - ntfs_inode *base_ni; - s32 i; - - /* - * If the inode is an extent inode, disconnect it from the - * base inode before destroying it. - */ - base_ni = ni->base_ni; - for (i = 0; i < base_ni->nr_extents; ++i) { - tmp_nis = base_ni->extent_nis; - if (tmp_nis[i] != ni) - continue; - /* Found it. Disconnect. */ - memmove(tmp_nis + i, tmp_nis + i + 1, - (base_ni->nr_extents - i - 1) * - sizeof(ntfs_inode *)); - /* Buffer should be for multiple of four extents. */ - if ((--base_ni->nr_extents) & 3) { - i = -1; - break; - } - /* - * ElectricFence is unhappy with realloc(x,0) as free(x) - * thus we explicitly separate these two cases. - */ - if (base_ni->nr_extents) { - /* Resize the memory buffer. */ - tmp_nis = realloc(tmp_nis, base_ni->nr_extents * - sizeof(ntfs_inode *)); - /* Ignore errors, they don't really matter. */ - if (tmp_nis) - base_ni->extent_nis = tmp_nis; - } else if (tmp_nis) - free(tmp_nis); - /* Allow for error checking. */ - i = -1; - break; - } - if (i != -1) - ntfs_log_debug("Extent inode was not attached to base inode! " - "Weird! Continuing regardless.\n"); - } - return __ntfs_inode_release(ni); -} - -/** - * ntfs_extent_inode_open - load an extent inode and attach it to its base - * @base_ni: base ntfs inode - * @mref: mft reference of the extent inode to load (in little endian) - * - * First check if the extent inode @mref is already attached to the base ntfs - * inode @base_ni, and if so, return a pointer to the attached extent inode. - * - * If the extent inode is not already attached to the base inode, allocate an - * ntfs_inode structure and initialize it for the given inode @mref. @mref - * specifies the inode number / mft record to read, including the sequence - * number, which can be 0 if no sequence number checking is to be performed. - * - * Then, allocate a buffer for the mft record, read the mft record from the - * volume @base_ni->vol, and attach it to the ntfs_inode structure (->mrec). - * The mft record is mst deprotected and sanity checked for validity and we - * abort if deprotection or checks fail. - * - * Finally attach the ntfs inode to its base inode @base_ni and return a - * pointer to the ntfs_inode structure on success or NULL on error, with errno - * set to the error code. - * - * Note, extent inodes are never closed directly. They are automatically - * disposed off by the closing of the base inode. - */ -ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) -{ - u64 mft_no = MREF_LE(mref); - ntfs_inode *ni; - ntfs_inode **extent_nis; - int i; - - if (!base_ni) { - errno = EINVAL; - return NULL; - } - ntfs_log_trace("Opening extent inode 0x%llx (base mft record 0x%llx).\n", - (unsigned long long)mft_no, - (unsigned long long)base_ni->mft_no); - /* Is the extent inode already open and attached to the base inode? */ - if (base_ni->nr_extents > 0) { - extent_nis = base_ni->extent_nis; - for (i = 0; i < base_ni->nr_extents; i++) { - u16 seq_no; - - ni = extent_nis[i]; - if (mft_no != ni->mft_no) - continue; - /* Verify the sequence number if given. */ - seq_no = MSEQNO_LE(mref); - if (seq_no && seq_no != le16_to_cpu( - ni->mrec->sequence_number)) { - ntfs_log_debug("Found stale extent mft reference! " - "Corrupt file system. Run chkdsk.\n"); - errno = EIO; - return NULL; - } - /* We are done, return the extent inode. */ - return ni; - } - } - /* Wasn't there, we need to load the extent inode. */ - ni = __ntfs_inode_allocate(base_ni->vol); - if (!ni) - return NULL; - if (ntfs_file_record_read(base_ni->vol, le64_to_cpu(mref), &ni->mrec, - NULL)) - goto err_out; - ni->mft_no = mft_no; - ni->nr_extents = -1; - ni->base_ni = base_ni; - /* Attach extent inode to base inode, reallocating memory if needed. */ - if (!(base_ni->nr_extents & 3)) { - i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); - - extent_nis = (ntfs_inode**)malloc(i); - if (!extent_nis) - goto err_out; - if (base_ni->nr_extents) { - memcpy(extent_nis, base_ni->extent_nis, - i - 4 * sizeof(ntfs_inode *)); - free(base_ni->extent_nis); - } - base_ni->extent_nis = extent_nis; - } - base_ni->extent_nis[base_ni->nr_extents++] = ni; - return ni; -err_out: - i = errno; - __ntfs_inode_release(ni); - errno = i; - ntfs_log_perror("Failed to open extent inode"); - return NULL; -} - -/** - * ntfs_inode_attach_all_extents - attach all extents for target inode - * @ni: opened ntfs inode for which perform attach - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -int ntfs_inode_attach_all_extents(ntfs_inode *ni) -{ - ATTR_LIST_ENTRY *ale; - u64 prev_attached = 0; - - if (!ni) { - ntfs_log_trace("Invalid arguments.\n"); - errno = EINVAL; - return -1; - } - - if (ni->nr_extents == -1) - ni = ni->base_ni; - - ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); - - /* Inode haven't got attribute list, thus nothing to attach. */ - if (!NInoAttrList(ni)) - return 0; - - if (!ni->attr_list) { - ntfs_log_trace("Corrupt in-memory struct.\n"); - errno = EINVAL; - return -1; - } - - /* Walk through attribute list and attach all extents. */ - errno = 0; - ale = (ATTR_LIST_ENTRY *)ni->attr_list; - while ((u8*)ale < ni->attr_list + ni->attr_list_size) { - if (ni->mft_no != MREF_LE(ale->mft_reference) && - prev_attached != MREF_LE(ale->mft_reference)) { - if (!ntfs_extent_inode_open(ni, - MREF_LE(ale->mft_reference))) { - ntfs_log_trace("Couldn't attach extent inode.\n"); - return -1; - } - prev_attached = MREF_LE(ale->mft_reference); - } - ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length)); - } - return 0; -} - -/** - * ntfs_inode_sync_standard_information - update standard information attribute - * @ni: ntfs inode to update standard information - * - * Return 0 on success or -1 on error with errno set to the error code. - */ -static int ntfs_inode_sync_standard_information(ntfs_inode *ni) -{ - ntfs_attr_search_ctx *ctx; - STANDARD_INFORMATION *std_info; - int err; - - ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); - - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) - return -1; - if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, - 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { - err = errno; - ntfs_log_trace("Failed to receive STANDARD_INFORMATION " - "attribute.\n"); - ntfs_attr_put_search_ctx(ctx); - errno = err; - return -1; - } - std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + - le16_to_cpu(ctx->attr->value_offset)); - std_info->file_attributes = ni->flags; - std_info->creation_time = utc2ntfs(ni->creation_time); - std_info->last_data_change_time = utc2ntfs(ni->last_data_change_time); - std_info->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - std_info->last_access_time = utc2ntfs(ni->last_access_time); - ntfs_inode_mark_dirty(ctx->ntfs_ino); - ntfs_attr_put_search_ctx(ctx); - return 0; -} - -/** - * ntfs_inode_sync_file_name - update FILE_NAME attributes - * @ni: ntfs inode to update FILE_NAME attributes - * - * Update all FILE_NAME attributes for inode @ni in the index. - * - * Return 0 on success or -1 on error with errno set to the error code. - */ -static int ntfs_inode_sync_file_name(ntfs_inode *ni) -{ - ntfs_attr_search_ctx *ctx = NULL; - ntfs_index_context *ictx; - ntfs_inode *index_ni; - FILE_NAME_ATTR *fn; - int err = 0; - - ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); - - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) { - err = errno; - ntfs_log_trace("Failed to get attribute search context.\n"); - goto err_out; - } - /* Walk through all FILE_NAME attributes and update them. */ - while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) { - fn = (FILE_NAME_ATTR *)((u8 *)ctx->attr + - le16_to_cpu(ctx->attr->value_offset)); - if (MREF_LE(fn->parent_directory) == ni->mft_no) { - /* - * WARNING: We cheater here and obtain 2 attribute - * search contexts for one inode (first we obtained - * above, second will be obtained inside - * ntfs_index_lookup), it's acceptable for library, - * but will lock kernel. - */ - index_ni = ni; - } else - index_ni = ntfs_inode_open(ni->vol, - le64_to_cpu(fn->parent_directory)); - if (!index_ni) { - if (!err) - err = errno; - ntfs_log_trace("Failed to open inode with index.\n"); - continue; - } - ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4); - if (!ictx) { - if (!err) - err = errno; - ntfs_log_trace("Failed to get index context.\n"); - ntfs_inode_close(index_ni); - continue; - } - if (ntfs_index_lookup(fn, sizeof(FILE_NAME_ATTR), ictx)) { - if (!err) { - if (errno == ENOENT) - err = EIO; - else - err = errno; - } - ntfs_log_trace("Index lookup failed.\n"); - ntfs_index_ctx_put(ictx); - ntfs_inode_close(index_ni); - continue; - } - /* Update flags and file size. */ - fn = (FILE_NAME_ATTR *)ictx->data; - fn->file_attributes = - (fn->file_attributes & ~FILE_ATTR_VALID_FLAGS) | - (ni->flags & FILE_ATTR_VALID_FLAGS); - fn->allocated_size = cpu_to_sle64(ni->allocated_size); - fn->data_size = cpu_to_sle64(ni->data_size); - fn->creation_time = utc2ntfs(ni->creation_time); - fn->last_data_change_time = utc2ntfs(ni->last_data_change_time); - fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - fn->last_access_time = utc2ntfs(ni->last_access_time); - ntfs_index_entry_mark_dirty(ictx); - ntfs_index_ctx_put(ictx); - if (ni != index_ni) - ntfs_inode_close(index_ni); - } - /* Check for real error occurred. */ - if (errno != ENOENT) { - err = errno; - ntfs_log_trace("Attribute lookup failed.\n"); - goto err_out; - } - ntfs_attr_put_search_ctx(ctx); - if (err) { - errno = err; - return -1; - } - return 0; -err_out: - if (ctx) - ntfs_attr_put_search_ctx(ctx); - errno = err; - return -1; -} - -/** - * ntfs_inode_sync - write the inode (and its dirty extents) to disk - * @ni: ntfs inode to write - * - * Write the inode @ni to disk as well as its dirty extent inodes if such - * exist and @ni is a base inode. If @ni is an extent inode, only @ni is - * written completely disregarding its base inode and any other extent inodes. - * - * For a base inode with dirty extent inodes if any writes fail for whatever - * reason, the failing inode is skipped and the sync process is continued. At - * the end the error condition that brought about the failure is returned. Thus - * the smallest amount of data loss possible occurs. - * - * Return 0 on success or -1 on error with errno set to the error code. - * The following error codes are defined: - * EINVAL - Invalid arguments were passed to the function. - * EBUSY - Inode and/or one of its extents is busy, try again later. - * EIO - I/O error while writing the inode (or one of its extents). - */ -int ntfs_inode_sync(ntfs_inode *ni) -{ - int err = 0; - - if (!ni) { - errno = EINVAL; - return -1; - } - - ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); - - /* Update STANDARD_INFORMATION. */ - if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && - ntfs_inode_sync_standard_information(ni)) { - if (!err || errno == EIO) { - err = errno; - if (err != EIO) - err = EBUSY; - } - ntfs_log_trace("Failed to sync standard information.\n"); - } - - /* Update FILE_NAME's in the index. */ - if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && - NInoFileNameTestAndClearDirty(ni) && - ntfs_inode_sync_file_name(ni)) { - if (!err || errno == EIO) { - err = errno; - if (err != EIO) - err = EBUSY; - } - ntfs_log_trace("Failed to sync FILE_NAME attributes.\n"); - NInoFileNameSetDirty(ni); - } - - /* Write out attribute list from cache to disk. */ - if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && - NInoAttrList(ni) && NInoAttrListTestAndClearDirty(ni)) { - ntfs_attr *na; - - na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); - if (!na) { - if (!err || errno == EIO) { - err = errno; - if (err != EIO) - err = EBUSY; - ntfs_log_trace("Attribute list sync failed (open " - "failed).\n"); - } - NInoAttrListSetDirty(ni); - } else { - if (na->data_size == ni->attr_list_size) { - if (ntfs_attr_pwrite(na, 0, ni->attr_list_size, - ni->attr_list) != - ni->attr_list_size) { - if (!err || errno == EIO) { - err = errno; - if (err != EIO) - err = EBUSY; - ntfs_log_trace("Attribute list sync " - "failed (write failed).\n"); - } - NInoAttrListSetDirty(ni); - } - } else { - err = EIO; - ntfs_log_trace("Attribute list sync failed (invalid size).\n"); - NInoAttrListSetDirty(ni); - } - ntfs_attr_close(na); - } - } - - /* Write this inode out to the $MFT (and $MFTMirr if applicable). */ - if (NInoTestAndClearDirty(ni)) { - if (ntfs_mft_record_write(ni->vol, ni->mft_no, ni->mrec)) { - if (!err || errno == EIO) { - err = errno; - if (err != EIO) - err = EBUSY; - } - NInoSetDirty(ni); - ntfs_log_trace("Base MFT record sync failed.\n"); - } - } - - /* If this is a base inode with extents write all dirty extents, too. */ - if (ni->nr_extents > 0) { - s32 i; - - for (i = 0; i < ni->nr_extents; ++i) { - ntfs_inode *eni; - - eni = ni->extent_nis[i]; - if (NInoTestAndClearDirty(eni)) { - if (ntfs_mft_record_write(eni->vol, eni->mft_no, - eni->mrec)) { - if (!err || errno == EIO) { - err = errno; - if (err != EIO) - err = EBUSY; - } - NInoSetDirty(eni); - ntfs_log_trace("Extent MFT record sync " - "failed.\n"); - } - } - } - } - - if (!err) - return 0; - errno = err; - return -1; -} - -/** - * ntfs_inode_add_attrlist - add attribute list to inode and fill it - * @ni: opened ntfs inode to which add attribute list - * - * Return 0 on success or -1 on error with errno set to the error code. - * The following error codes are defined: - * EINVAL - Invalid arguments were passed to the function. - * EEXIST - Attribute list already exist. - * EIO - Input/Ouput error occurred. - * ENOMEM - Not enough memory to perform add. - */ -int ntfs_inode_add_attrlist(ntfs_inode *ni) -{ - int err; - ntfs_attr_search_ctx *ctx; - u8 *al, *aln; - int al_len, al_allocated; - ATTR_LIST_ENTRY *ale; - ntfs_attr *na; - - if (!ni) { - ntfs_log_trace("Invalid arguments.\n"); - errno = EINVAL; - return -1; - } - - ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); - - if (NInoAttrList(ni) || ni->nr_extents) { - ntfs_log_trace("Inode already has got attribute list.\n"); - errno = EEXIST; - return -1; - } - - al_allocated = 0x40; - al_len = 0; - al = malloc(al_allocated); - ale = (ATTR_LIST_ENTRY *) al; - if (!al) { - ntfs_log_trace("Not enough memory.\n"); - errno = ENOMEM; - return -1; - } - - /* Form attribute list. */ - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) { - err = errno; - ntfs_log_trace("Couldn't get search context.\n"); - goto err_out; - } - /* Walk through all attributes. */ - while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { - if (ctx->attr->type == AT_ATTRIBUTE_LIST) { - err = EIO; - ntfs_log_trace("Eeek! Attribute list already present.\n"); - goto put_err_out; - } - /* Calculate new length of attribute list. */ - al_len += (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * - ctx->attr->name_length + 7) & ~7; - /* Allocate more memory if needed. */ - while (al_len > al_allocated) { - al_allocated += 0x40; - aln = realloc(al, al_allocated); - if (!aln) { - ntfs_log_trace("Not enough memory.\n"); - err = ENOMEM; - goto put_err_out; - } - ale = (ATTR_LIST_ENTRY *)(aln + ((u8 *)ale - al)); - al = aln; - } - /* Add attribute to attribute list. */ - ale->type = ctx->attr->type; - ale->length = cpu_to_le16((sizeof(ATTR_LIST_ENTRY) + - sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7); - ale->name_length = ctx->attr->name_length; - ale->name_offset = (u8 *)ale->name - (u8 *)ale; - if (ctx->attr->non_resident) - ale->lowest_vcn = ctx->attr->lowest_vcn; - else - ale->lowest_vcn = 0; - ale->mft_reference = MK_LE_MREF(ni->mft_no, - le16_to_cpu(ni->mrec->sequence_number)); - ale->instance = ctx->attr->instance; - memcpy(ale->name, (u8 *)ctx->attr + - le16_to_cpu(ctx->attr->name_offset), - ctx->attr->name_length * sizeof(ntfschar)); - ale = (ATTR_LIST_ENTRY *)(al + al_len); - } - /* Check for real error occurred. */ - if (errno != ENOENT) { - err = errno; - ntfs_log_trace("Attribute lookup failed.\n"); - goto put_err_out; - } - /* Deallocate trailing memory. */ - aln = realloc(al, al_len); - if (!aln) { - err = errno; - ntfs_log_trace("realloc() failed.\n"); - goto put_err_out; - } - al = aln; - - /* Set in-memory attribute list. */ - ni->attr_list = al; - ni->attr_list_size = al_len; - NInoSetAttrList(ni); - NInoAttrListSetDirty(ni); - - /* Free space if there is not enough it for $ATTRIBUTE_LIST. */ - if (le32_to_cpu(ni->mrec->bytes_allocated) - - le32_to_cpu(ni->mrec->bytes_in_use) < - offsetof(ATTR_RECORD, resident_end)) { - if (ntfs_inode_free_space(ni, - offsetof(ATTR_RECORD, resident_end))) { - /* Failed to free space. */ - err = errno; - ntfs_log_trace("Failed to free space for " - "$ATTRIBUTE_LIST.\n"); - goto rollback; - } - } - - /* Add $ATTRIBUTE_LIST to mft record. */ - if (ntfs_resident_attr_record_add(ni, - AT_ATTRIBUTE_LIST, NULL, 0, NULL, 0, 0) < 0) { - err = errno; - ntfs_log_trace("Couldn't add $ATTRIBUTE_LIST to MFT record.\n"); - goto rollback; - } - - /* Resize it. */ - na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); - if (!na) { - err = errno; - ntfs_log_trace("Failed to open just added $ATTRIBUTE_LIST.\n"); - goto remove_attrlist_record; - } - if (ntfs_attr_truncate(na, al_len)) { - err = errno; - ntfs_log_trace("Failed to resize just added $ATTRIBUTE_LIST.\n"); - ntfs_attr_close(na); - goto remove_attrlist_record;; - } - /* Done! */ - ntfs_attr_close(na); - return 0; -remove_attrlist_record: - /* Prevent ntfs_attr_recorm_rm from freeing attribute list. */ - ni->attr_list = NULL; - NInoClearAttrList(ni); - /* Remove $ATTRIBUTE_LIST record. */ - ntfs_attr_reinit_search_ctx(ctx); - if (!ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, - CASE_SENSITIVE, 0, NULL, 0, ctx)) { - if (ntfs_attr_record_rm(ctx)) - ntfs_log_trace("Rollback failed. Failed to remove attribute " - "list record.\n"); - } else - ntfs_log_trace("Rollback failed. Couldn't find attribute list " - "record.\n"); - /* Setup back in-memory runlist. */ - ni->attr_list = al; - ni->attr_list_size = al_len; - NInoSetAttrList(ni); -rollback: - /* - * Scan attribute list for attributes that placed not in the base MFT - * record and move them to it. - */ - ntfs_attr_reinit_search_ctx(ctx); - ale = (ATTR_LIST_ENTRY*)al; - while ((u8*)ale < al + al_len) { - if (MREF_LE(ale->mft_reference) != ni->mft_no) { - if (!ntfs_attr_lookup(ale->type, ale->name, - ale->name_length, - CASE_SENSITIVE, - sle64_to_cpu(ale->lowest_vcn), - NULL, 0, ctx)) { - if (ntfs_attr_record_move_to(ctx, ni)) - ntfs_log_trace("Rollback failed. Couldn't " - "back attribute to base MFT record.\n"); - } else - ntfs_log_trace("Rollback failed. ntfs_attr_lookup " - "failed.\n"); - ntfs_attr_reinit_search_ctx(ctx); - } - ale = (ATTR_LIST_ENTRY*)((u8*)ale + le16_to_cpu(ale->length)); - } - /* Remove in-memory attribute list. */ - ni->attr_list = NULL; - ni->attr_list_size = 0; - NInoClearAttrList(ni); - NInoAttrListClearDirty(ni); -put_err_out: - ntfs_attr_put_search_ctx(ctx); -err_out: - free(al); - errno = err; - return -1; -} - -/** - * ntfs_inode_free_space - free space in the MFT record of inode - * @ni: ntfs inode in which MFT record free space - * @size: amount of space needed to free - * - * Return 0 on success or -1 on error with errno set to the error code. - */ -int ntfs_inode_free_space(ntfs_inode *ni, int size) -{ - ntfs_attr_search_ctx *ctx; - int freed, err; - - if (!ni || size < 0) { - ntfs_log_trace("Invalid arguments.\n"); - errno = EINVAL; - return -1; - } - - ntfs_log_trace("Entering for inode 0x%llx, size %d.\n", - (long long) ni->mft_no, size); - - freed = (le32_to_cpu(ni->mrec->bytes_allocated) - - le32_to_cpu(ni->mrec->bytes_in_use)); - - if (size <= freed) - return 0; - - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) { - err = errno; - ntfs_log_trace("Failed to get attribute search context.\n"); - errno = err; - return -1; - } - - /* - * Chkdsk complain if $STANDARD_INFORMATION is not in the base MFT - * record. FIXME: I'm not sure in this, need to recheck. For now simply - * do not move $STANDARD_INFORMATION at all. - * - * Also we can't move $ATTRIBUTE_LIST from base MFT_RECORD, so position - * search context on first attribute after $STANDARD_INFORMATION and - * $ATTRIBUTE_LIST. - * - * Why we reposition instead of simply skip this attributes during - * enumeration? Because in case we have got only in-memory attribute - * list ntfs_attr_lookup will fail when it will try to find - * $ATTRIBUTE_LIST. - */ - if (ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, NULL, - 0, ctx)) { - if (errno != ENOENT) { - err = errno; - ntfs_log_trace("Attribute lookup failed.\n"); - goto put_err_out; - } - if (ctx->attr->type == AT_END) { - err = ENOSPC; - goto put_err_out; - } - } - - while (1) { - int record_size; - - /* - * Check whether attribute is from different MFT record. If so, - * find next, because we don't need such. - */ - while (ctx->ntfs_ino->mft_no != ni->mft_no) { - if (ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, - 0, NULL, 0, ctx)) { - err = errno; - if (errno != ENOENT) { - ntfs_log_trace("Attribute lookup failed.\n"); - } else - err = ENOSPC; - goto put_err_out; - } - } - - record_size = le32_to_cpu(ctx->attr->length); - - /* Move away attribute. */ - if (ntfs_attr_record_move_away(ctx, 0)) { - err = errno; - ntfs_log_trace("Failed to move out attribute.\n"); - break; - } - freed += record_size; - - /* Check whether we done. */ - if (size <= freed) { - ntfs_attr_put_search_ctx(ctx); - return 0; - } - - /* - * Reposition to first attribute after $STANDARD_INFORMATION and - * $ATTRIBUTE_LIST (see comments upwards). - */ - ntfs_attr_reinit_search_ctx(ctx); - if (ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, - NULL, 0, ctx)) { - if (errno != ENOENT) { - err = errno; - ntfs_log_trace("Attribute lookup failed.\n"); - break; - } - if (ctx->attr->type == AT_END) { - err = ENOSPC; - break; - } - } - } -put_err_out: - ntfs_attr_put_search_ctx(ctx); - if (err == ENOSPC) - ntfs_log_trace("No attributes left that can be moved out.\n"); - errno = err; - return -1; -} - -/** - * ntfs_inode_update_atime - update access time for ntfs inode - * @ni: ntfs inode for which update access time - * - * This function usually get called when user read not metadata from inode. - * Do not update time for system files. - */ -void ntfs_inode_update_atime(ntfs_inode *ni) -{ - if (!NVolReadOnly(ni->vol) && !NVolNoATime(ni->vol) && (ni->mft_no >= - FILE_first_user || ni->mft_no == FILE_root)) { - ni->last_access_time = time(NULL); - NInoFileNameSetDirty(ni); - NInoSetDirty(ni); - } -} - -/** - * ntfs_inode_update_time - update all times for ntfs inode - * @ni: ntfs inode for which update times - * - * This function updates last access, mft and data change times. Usually - * get called when user write not metadata to inode. Do not update time for - * system files. - */ -void ntfs_inode_update_time(ntfs_inode *ni) -{ - if (!NVolReadOnly(ni->vol) && !NVolNoATime(ni->vol) && (ni->mft_no >= - FILE_first_user || ni->mft_no == FILE_root)) { - time_t now; - - now = time(NULL); - ni->last_access_time = now; - ni->last_data_change_time = now; - ni->last_mft_change_time = now; - NInoFileNameSetDirty(ni); - NInoSetDirty(ni); - } -} - -/** - * ntfs_inode_badclus_bad - check for $Badclus:$Bad data attribute - * @mft_no: mft record number where @attr is present - * @attr: attribute record used to check for the $Bad attribute - * - * Check if the mft record given by @mft_no and @attr contains the bad sector - * list. Please note that mft record numbers describing $Badclus extent inodes - * will not match the current $Badclus:$Bad check. - * - * On success return 1 if the file is $Badclus:$Bad, otherwise return 0. - * On error return -1 with errno set to the error code. - */ -int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *attr) -{ - int len, ret = 0; - ntfschar *ustr; - - if (!attr) { - ntfs_log_error("Invalid argument.\n"); - errno = EINVAL; - return -1; - } - - if (mft_no != FILE_BadClus) - return 0; - - if (attr->type != AT_DATA) - return 0; - - if ((ustr = ntfs_str2ucs("$Bad", &len)) == NULL) { - ntfs_log_perror("Couldn't convert '$Bad' to Unicode"); - return -1; - } - - if (ustr && ntfs_names_are_equal(ustr, len, - (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)), - attr->name_length, 0, NULL, 0)) - ret = 1; - - ntfs_ucsfree(ustr); - - return ret; -} - -#ifdef NTFS_RICH - -#include "rich.h" - -/** - * ntfs_inode_close2 - Close an inode, freeing any resources - * @ni: - * - * Description... - * - * Returns: - */ -int ntfs_inode_close2(ntfs_inode *ni) -{ - if (!ni) - return 0; - - ntfs_log_trace ("inode %p, mft %lld, refcount %d\n", ni, MREF(ni->mft_no), ni->ref_count); - - ni->ref_count--; - if (ni->ref_count > 0) - return 0; - - // unlink - // ino->private_data - - // XXX temporary until we have commit/rollback - NInoClearDirty(ni); - - return ntfs_inode_close(ni); -} - -/** - * ntfs_inode_open2 - Open an inode and initialise it - * @vol: - * @mref: - * - * Description... - * - * Returns: - */ -ntfs_inode * ntfs_inode_open2(ntfs_volume *vol, const MFT_REF mref) -{ - ntfs_inode *ino = NULL; - struct ntfs_dir *dir; - - if (!vol) - return NULL; - - ntfs_log_trace ("\n"); - switch (mref) { - case FILE_Bitmap: ino = vol->lcnbmp_ni; break; - case FILE_MFT: ino = vol->mft_ni; break; - case FILE_MFTMirr: ino = vol->mftmirr_ni; break; - case FILE_Volume: ino = vol->vol_ni; break; - case FILE_root: - dir = vol->private_data; - if (dir) - ino = dir->inode; - break; - } - - if (ino) { - ntfs_log_debug("inode reuse %lld\n", mref); - ino->ref_count++; - return ino; - } - - ino = ntfs_inode_open(vol, mref); - if (!ino) - return NULL; - - /* - if (mref != FILE_root) - ntfs_inode_dir_map (ino); - */ - - // link - // ino->private_data - - ino->private_data = NULL; - ino->ref_count = 1; - - ntfs_log_debug("inode open %lld, 0x%llx\n", MREF(mref), mref); - return ino; -} - -/** - * ntfs_inode_open3 - Open an inode and initialise it - * @vol: - * @mref: - * - * Description... - * - * Returns: - */ -ntfs_inode * ntfs_inode_open3(ntfs_volume *vol, const MFT_REF mref) -{ - ntfs_inode *ino = NULL; - - if (!vol) - return NULL; - - ntfs_log_trace ("\n"); - ino = calloc(1, sizeof(*ino)); - if (!ino) - return NULL; - - ino->mrec = malloc(vol->mft_record_size); - if (!ino->mrec) { - free(ino); - return NULL; - } - - ino->mft_no = mref; - ino->vol = vol; - - ino->data_size = -1; - ino->allocated_size = -1; - - ino->private_data = NULL; - ino->ref_count = 1; - - if (1 != ntfs_attr_mst_pread(vol->mft_na, MREF(mref) * vol->mft_record_size, 1, vol->mft_record_size, ino->mrec)) { - //ntfs_inode_close2(ino); ??? - free(ino->mrec); - free(ino); - return NULL; - } - - NInoSetDirty(ino); - return ino; -} - - -#endif /* NTFS_RICH */ - diff --git a/libntfs/lcnalloc.c b/libntfs/lcnalloc.c deleted file mode 100644 index d8bc95eb..00000000 --- a/libntfs/lcnalloc.c +++ /dev/null @@ -1,856 +0,0 @@ -/** - * lcnalloc.c - Cluster (de)allocation code. Part of the Linux-NTFS project. - * - * Copyright (c) 2002-2004 Anton Altaparmakov - * Copyright (c) 2004 Yura Pakhuchiy - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_STDIO_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif - -#include "types.h" -#include "attrib.h" -#include "bitmap.h" -#include "debug.h" -#include "runlist.h" -#include "volume.h" -#include "lcnalloc.h" -#include "logging.h" - -/** - * ntfs_cluster_alloc - allocate clusters on an ntfs volume - * @vol: mounted ntfs volume on which to allocate the clusters - * @start_vcn: vcn to use for the first allocated cluster - * @count: number of clusters to allocate - * @start_lcn: starting lcn at which to allocate the clusters (or -1 if none) - * @zone: zone from which to allocate the clusters - * - * Allocate @count clusters preferably starting at cluster @start_lcn or at the - * current allocator position if @start_lcn is -1, on the mounted ntfs volume - * @vol. @zone is either DATA_ZONE for allocation of normal clusters and - * MFT_ZONE for allocation of clusters for the master file table, i.e. the - * $MFT/$DATA attribute. - * - * On success return a runlist describing the allocated cluster(s). - * - * On error return NULL with errno set to the error code. - * - * Notes on the allocation algorithm - * ================================= - * - * 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 NTFS 1.x - * volumes, the second data zone doesn't exist due to the mft zone being - * expanded to cover the start of the 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 up to 8192 bytes at a time, so we - * need to cope with crossing over boundaries of two buffers. 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: 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 it might even be correct not to do 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) - */ -runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, - LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone) -{ - LCN zone_start, zone_end, bmp_pos, bmp_initial_pos, last_read_pos, lcn; - LCN prev_lcn = 0, prev_run_len = 0, mft_zone_size; - s64 clusters, br; - runlist *rl = NULL, *trl; - u8 *buf, *byte; - int err = 0, rlpos, rlsize, buf_size; - u8 pass, done_zones, search_zone, need_writeback, bit; - - ntfs_log_trace("Entering with count = 0x%llx, start_lcn = 0x%llx, zone = " - "%s_ZONE.\n", (long long)count, (long long)start_lcn, - zone == MFT_ZONE ? "MFT" : "DATA"); - if (!vol || count < 0 || start_lcn < -1 || !vol->lcnbmp_na || - (s8)zone < FIRST_ZONE || zone > LAST_ZONE) { - ntfs_log_trace("Invalid arguments!\n"); - errno = EINVAL; - return NULL; - } - - /* Return empty runlist if @count == 0 */ - if (!count) { - rl = malloc(0x1000); - if (!rl) - return NULL; - rl[0].vcn = start_vcn; - rl[0].lcn = LCN_RL_NOT_MAPPED; - rl[0].length = 0; - return rl; - } - - /* Allocate memory. */ - buf = (u8*)malloc(8192); - if (!buf) - return NULL; - /* - * If no specific @start_lcn was requested, use the current data zone - * position, otherwise use the requested @start_lcn 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, - * we either start at the current position within the mft zone or at - * the specified position. 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 = start_lcn; - 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 == DATA_ZONE && zone_start >= vol->mft_zone_start && - zone_start < vol->mft_zone_end) { - zone_start = vol->mft_zone_end; - /* - * Starting at beginning of data1_zone which means a single - * pass in this zone is sufficient. - */ - pass = 2; - } else if (zone == MFT_ZONE && (zone_start < vol->mft_zone_start || - zone_start >= vol->mft_zone_end)) { - zone_start = vol->mft_lcn; - if (!vol->mft_zone_end) - zone_start = 0; - /* - * Starting at beginning of volume which means a single pass - * is sufficient. - */ - pass = 2; - } - if (zone == MFT_ZONE) { - zone_end = vol->mft_zone_end; - search_zone = 1; - } else /* 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; - } - } - /* - * bmp_pos is the current bit position inside the bitmap. We use - * bmp_initial_pos to determine whether or not to do a zone switch. - */ - bmp_pos = bmp_initial_pos = zone_start; - - /* Loop until all clusters are allocated, i.e. clusters == 0. */ - clusters = count; - rlpos = rlsize = 0; - while (1) { - ntfs_log_trace("Start of outer while loop: done_zones = 0x%x, " - "search_zone = %i, pass = %i, zone_start = " - "0x%llx, zone_end = 0x%llx, bmp_initial_pos = " - "0x%llx, bmp_pos = 0x%llx, rlpos = %i, rlsize = " - "%i.\n", done_zones, search_zone, pass, - (long long)zone_start, (long long)zone_end, - (long long)bmp_initial_pos, (long long)bmp_pos, - rlpos, rlsize); - /* Loop until we run out of free clusters. */ - last_read_pos = bmp_pos >> 3; - ntfs_log_trace("last_read_pos = 0x%llx.\n", (long long)last_read_pos); - br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos, 8192, buf); - if (br <= 0) { - if (!br) { - /* Reached end of attribute. */ - ntfs_log_trace("End of attribute reached. Skipping " - "to zone_pass_done.\n"); - goto zone_pass_done; - } - err = errno; - ntfs_log_trace("ntfs_attr_pread() failed. Aborting.\n"); - goto err_ret; - } - /* - * We might have read less than 8192 bytes if we are close to - * the end of the attribute. - */ - buf_size = (int)br << 3; - lcn = bmp_pos & 7; - bmp_pos &= ~7; - need_writeback = 0; - ntfs_log_trace("Before inner while loop: buf_size = %i, lcn = " - "0x%llx, bmp_pos = 0x%llx, need_writeback = %i.\n", - buf_size, (long long)lcn, (long long)bmp_pos, - need_writeback); - while (lcn < buf_size && lcn + bmp_pos < zone_end) { - byte = buf + (lcn >> 3); - ntfs_log_trace("In inner while loop: buf_size = %i, lcn = " - "0x%llx, bmp_pos = 0x%llx, " - "need_writeback = %i, byte ofs = 0x%x, " - "*byte = 0x%x.\n", buf_size, - (long long)lcn, (long long)bmp_pos, - need_writeback, (unsigned int)(lcn >> 3), - (unsigned int)*byte); - /* Skip full bytes. */ - if (*byte == 0xff) { - lcn = (lcn + 8) & ~7; - ntfs_log_trace("continuing while loop 1.\n"); - continue; - } - bit = 1 << (lcn & 7); - ntfs_log_trace("bit = %i.\n", bit); - /* If the bit is already set, go onto the next one. */ - if (*byte & bit) { - lcn++; - ntfs_log_trace("continuing while loop 2.\n"); - continue; - } - /* Reallocate memory if necessary. */ - if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) { - ntfs_log_trace("Reallocating space.\n"); - if (!rl) - ntfs_log_trace("First free bit is at LCN = " - "0x%llx.\n", (long long)(lcn + bmp_pos)); - rlsize += 4096; - trl = (runlist*)realloc(rl, rlsize); - if (!trl) { - err = ENOMEM; - ntfs_log_trace("Failed to allocate memory, " - "going to wb_err_ret.\n"); - goto wb_err_ret; - } - rl = trl; - ntfs_log_trace("Reallocated memory, rlsize = " - "0x%x.\n", rlsize); - } - /* Allocate the bitmap bit. */ - *byte |= bit; - /* We need to write this bitmap buffer back to disk! */ - need_writeback = 1; - ntfs_log_trace("*byte = 0x%x, need_writeback is set.\n", - (unsigned int)*byte); - /* - * Coalesce with previous run if adjacent LCNs. - * Otherwise, append a new run. - */ - ntfs_log_trace("Adding run (lcn 0x%llx, len 0x%llx), " - "prev_lcn = 0x%llx, lcn = 0x%llx, " - "bmp_pos = 0x%llx, prev_run_len = " - "0x%llx, rlpos = %i.\n", - (long long)(lcn + bmp_pos), 1LL, - (long long)prev_lcn, (long long)lcn, - (long long)bmp_pos, - (long long)prev_run_len, rlpos); - if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) { - ntfs_log_trace("Coalescing to run (lcn 0x%llx, len " - "0x%llx).\n", - (long long)rl[rlpos - 1].lcn, - (long long) rl[rlpos - 1].length); - rl[rlpos - 1].length = ++prev_run_len; - ntfs_log_trace("Run now (lcn 0x%llx, len 0x%llx), " - "prev_run_len = 0x%llx.\n", - (long long)rl[rlpos - 1].lcn, - (long long)rl[rlpos - 1].length, - (long long)prev_run_len); - } else { - if (rlpos) { - ntfs_log_trace("Adding new run, (previous " - "run lcn 0x%llx, len 0x%llx).\n", - (long long) rl[rlpos - 1].lcn, - (long long) rl[rlpos - 1].length); - rl[rlpos].vcn = rl[rlpos - 1].vcn + - prev_run_len; - } else { - ntfs_log_trace("Adding new run, is first run.\n"); - rl[rlpos].vcn = start_vcn; - } - rl[rlpos].lcn = prev_lcn = lcn + bmp_pos; - rl[rlpos].length = prev_run_len = 1; - rlpos++; - } - /* Done? */ - if (!--clusters) { - LCN tc; - /* - * Update the current zone position. Positions - * of already scanned zones have been updated - * during the respective zone switches. - */ - tc = lcn + bmp_pos + 1; - ntfs_log_trace("Done. Updating current zone " - "position, tc = 0x%llx, search_zone = %i.\n", - (long long)tc, search_zone); - switch (search_zone) { - case 1: - ntfs_log_trace("Before checks, vol->mft_zone_pos = 0x%llx.\n", - (long long) 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 = 0; - } else if ((bmp_initial_pos >= - vol->mft_zone_pos || - tc > vol->mft_zone_pos) - && tc >= vol->mft_lcn) - vol->mft_zone_pos = tc; - ntfs_log_trace("After checks, vol->mft_zone_pos = 0x%llx.\n", - (long long) vol->mft_zone_pos); - break; - case 2: - ntfs_log_trace("Before checks, vol->data1_zone_pos = 0x%llx.\n", - (long long) vol->data1_zone_pos); - if (tc >= vol->nr_clusters) - vol->data1_zone_pos = - vol->mft_zone_end; - else if ((bmp_initial_pos >= - vol->data1_zone_pos || - tc > vol->data1_zone_pos) - && tc >= vol->mft_zone_end) - vol->data1_zone_pos = tc; - ntfs_log_trace("After checks, vol->data1_zone_pos = 0x%llx.\n", - (long long) vol->data1_zone_pos); - break; - case 4: - ntfs_log_trace("Before checks, vol->data2_zone_pos = 0x%llx.\n", - (long long) vol->data2_zone_pos); - if (tc >= vol->mft_zone_start) - vol->data2_zone_pos = 0; - else if (bmp_initial_pos >= - vol->data2_zone_pos || - tc > vol->data2_zone_pos) - vol->data2_zone_pos = tc; - ntfs_log_trace("After checks, vol->data2_zone_pos = 0x%llx.\n", - (long long) vol->data2_zone_pos); - break; - default: - free(rl); - free(buf); - NTFS_BUG("switch (search_zone) 1"); - return NULL; - } - ntfs_log_trace("Going to done_ret.\n"); - goto done_ret; - } - lcn++; - } - bmp_pos += buf_size; - ntfs_log_trace("After inner while loop: buf_size = 0x%x, lcn = " - "0x%llx, bmp_pos = 0x%llx, need_writeback = %i.\n", - buf_size, (long long)lcn, - (long long)bmp_pos, need_writeback); - if (need_writeback) { - s64 bw; - ntfs_log_trace("Writing back.\n"); - need_writeback = 0; - bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos, - br, buf); - if (bw != br) { - if (bw == -1) - err = errno; - else - err = EIO; - ntfs_log_trace("Bitmap writeback failed in read next " - "buffer code path with error code %i.\n", err); - goto err_ret; - } - } - if (bmp_pos < zone_end) { - ntfs_log_trace("Continuing outer while loop, bmp_pos = " - "0x%llx, zone_end = 0x%llx.\n", - (long long)bmp_pos, - (long long)zone_end); - continue; - } -zone_pass_done: /* Finished with the current zone pass. */ - ntfs_log_trace("At zone_pass_done, pass = %i.\n", 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 = 0; - break; - default: - NTFS_BUG("switch (search_zone) 2"); - } - /* Sanity check. */ - if (zone_end < zone_start) - zone_end = zone_start; - bmp_pos = zone_start; - ntfs_log_trace("Continuing outer while loop, pass = 2, " - "zone_start = 0x%llx, zone_end = " - "0x%llx, bmp_pos = 0x%llx.\n", - zone_start, zone_end, bmp_pos); - continue; - } /* pass == 2 */ -done_zones_check: - ntfs_log_trace("At done_zones_check, search_zone = %i, done_zones " - "before = 0x%x, done_zones after = 0x%x.\n", - search_zone, done_zones, done_zones | search_zone); - done_zones |= search_zone; - if (done_zones < 7) { - ntfs_log_trace("Switching zone.\n"); - /* Now switch to the next zone we haven't done yet. */ - pass = 1; - switch (search_zone) { - case 1: - ntfs_log_trace("Switching from mft zone to data1 " - "zone.\n"); - /* Update mft zone position. */ - if (rlpos) { - LCN tc; - ntfs_log_trace("Before checks, vol->mft_zone_pos = 0x%llx.\n", - (long long) vol->mft_zone_pos); - tc = rl[rlpos - 1].lcn + - rl[rlpos - 1].length; - if (tc >= vol->mft_zone_end) { - vol->mft_zone_pos = - vol->mft_lcn; - if (!vol->mft_zone_end) - vol->mft_zone_pos = 0; - } else if ((bmp_initial_pos >= - vol->mft_zone_pos || - tc > vol->mft_zone_pos) - && tc >= vol->mft_lcn) - vol->mft_zone_pos = tc; - ntfs_log_trace("After checks, vol->mft_zone_pos = 0x%llx.\n", - (long long) vol->mft_zone_pos); - } - /* Switch from mft zone to data1 zone. */ -switch_to_data1_zone: search_zone = 2; - zone_start = bmp_initial_pos = - 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_log_trace("Switching from data1 zone to data2 " - "zone.\n"); - /* Update data1 zone position. */ - if (rlpos) { - LCN tc; - ntfs_log_trace("Before checks, vol->data1_zone_pos = 0x%llx.\n", - (long long) vol->data1_zone_pos); - tc = rl[rlpos - 1].lcn + - rl[rlpos - 1].length; - if (tc >= vol->nr_clusters) - vol->data1_zone_pos = - vol->mft_zone_end; - else if ((bmp_initial_pos >= - vol->data1_zone_pos || - tc > vol->data1_zone_pos) - && tc >= vol->mft_zone_end) - vol->data1_zone_pos = tc; - ntfs_log_trace("After checks, vol->data1_zone_pos = 0x%llx.\n", - (long long) vol->data1_zone_pos); - } - /* Switch from data1 zone to data2 zone. */ - search_zone = 4; - zone_start = bmp_initial_pos = - 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 = - bmp_initial_pos = 0; - pass = 2; - } - break; - case 4: - ntfs_log_debug("Switching from data2 zone to data1 " - "zone.\n"); - /* Update data2 zone position. */ - if (rlpos) { - LCN tc; - ntfs_log_trace("Before checks, vol->data2_zone_pos = 0x%llx.\n", - (long long) vol->data2_zone_pos); - tc = rl[rlpos - 1].lcn + - rl[rlpos - 1].length; - if (tc >= vol->mft_zone_start) - vol->data2_zone_pos = 0; - else if (bmp_initial_pos >= - vol->data2_zone_pos || - tc > vol->data2_zone_pos) - vol->data2_zone_pos = tc; - ntfs_log_trace("After checks, vol->data2_zone_pos = 0x%llx.\n", - (long long) vol->data2_zone_pos); - } - /* Switch from data2 zone to data1 zone. */ - goto switch_to_data1_zone; /* See above. */ - default: - NTFS_BUG("switch (search_zone) 3"); - } - ntfs_log_trace("After zone switch, search_zone = %i, pass = " - "%i, bmp_initial_pos = 0x%llx, " - "zone_start = 0x%llx, zone_end = " - "0x%llx.\n", search_zone, pass, - (long long)bmp_initial_pos, - (long long)zone_start, - (long long)zone_end); - bmp_pos = zone_start; - if (zone_start == zone_end) { - ntfs_log_trace("Empty zone, going to " - "done_zones_check.\n"); - /* Empty zone. Don't bother searching it. */ - goto done_zones_check; - } - ntfs_log_trace("Continuing outer while loop.\n"); - continue; - } /* done_zones == 7 */ - ntfs_log_trace("All zones are finished.\n"); - /* - * 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_log_trace("vol->mft_zone_start = 0x%llx, vol->mft_zone_end = " - "0x%llx, mft_zone_size = 0x%llx.\n", - (long long)vol->mft_zone_start, - (long long)vol->mft_zone_end, - (long long)mft_zone_size); - if (zone == MFT_ZONE || mft_zone_size <= 0) { - ntfs_log_trace("No free clusters left, going to err_ret.\n"); - /* Really no more space left on device. */ - err = ENOSPC; - goto err_ret; - } /* zone == DATA_ZONE && mft_zone_size > 0 */ - ntfs_log_trace("Shrinking mft zone.\n"); - zone_end = vol->mft_zone_end; - mft_zone_size >>= 1; - if (mft_zone_size > 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 = 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 = 0; - } - bmp_pos = zone_start = bmp_initial_pos = - vol->data1_zone_pos = vol->mft_zone_end; - search_zone = 2; - pass = 2; - done_zones &= ~2; - ntfs_log_trace("After shrinking mft zone, mft_zone_size = 0x%llx, " - "vol->mft_zone_start = 0x%llx, " - "vol->mft_zone_end = 0x%llx, vol->mft_zone_pos " - "= 0x%llx, search_zone = 2, pass = 2, " - "dones_zones = 0x%x, zone_start = 0x%llx, " - "zone_end = 0x%llx, vol->data1_zone_pos = " - "0x%llx, continuing outer while loop.\n", - (long long)mft_zone_size, - (long long)vol->mft_zone_start, - (long long)vol->mft_zone_end, - (long long)vol->mft_zone_pos, - done_zones, - (long long)zone_start, - (long long)zone_end, - (long long)vol->data1_zone_pos); - } - ntfs_log_debug("After outer while loop.\n"); -done_ret: - ntfs_log_debug("At done_ret.\n"); - /* Add runlist terminator element. */ - rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; - rl[rlpos].lcn = LCN_RL_NOT_MAPPED; - rl[rlpos].length = 0; - if (need_writeback) { - s64 bw; - ntfs_log_trace("Writing back.\n"); - need_writeback = 0; - bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos, br, buf); - if (bw != br) { - if (bw < 0) - err = errno; - else - err = EIO; - ntfs_log_trace("Bitmap writeback failed in done code path " - "with error code %i.\n", err); - goto err_ret; - } - } -done_err_ret: - ntfs_log_debug("At done_err_ret (follows done_ret).\n"); - free(buf); - /* Done! */ - if (!err) - return rl; - ntfs_log_trace("Failed to allocate clusters. Returning with error code " - "%i.\n", err); - errno = err; - return NULL; -wb_err_ret: - ntfs_log_trace("At wb_err_ret.\n"); - if (need_writeback) { - s64 bw; - ntfs_log_trace("Writing back.\n"); - need_writeback = 0; - bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos, br, buf); - if (bw != br) { - if (bw < 0) - err = errno; - else - err = EIO; - ntfs_log_trace("Bitmap writeback failed in error code path " - "with error code %i.\n", err); - } - } -err_ret: - ntfs_log_trace("At err_ret.\n"); - if (rl) { - if (err == ENOSPC) { - ntfs_log_trace("err = ENOSPC, first free lcn = 0x%llx, could " - "allocate up to = 0x%llx clusters.\n", - (long long)rl[0].lcn, - (long long)count - clusters); - } - /* Add runlist terminator element. */ - rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; - rl[rlpos].lcn = LCN_RL_NOT_MAPPED; - rl[rlpos].length = 0; - /* Deallocate all allocated clusters. */ - ntfs_log_trace("Deallocating allocated clusters.\n"); - ntfs_cluster_free_from_rl(vol, rl); - /* Free the runlist. */ - free(rl); - rl = NULL; - } else { - if (err == ENOSPC) { - ntfs_log_trace("No space left at all, err = ENOSPC, first " - "free lcn = 0x%llx.\n", - (long long)vol->data1_zone_pos); - } - } - ntfs_log_trace("rl = NULL, going to done_err_ret.\n"); - goto done_err_ret; -} - -/** - * ntfs_cluster_free_from_rl - free clusters from runlist - * @vol: mounted ntfs volume on which to free the clusters - * @rl: runlist from which deallocate clusters - * - * On success return 0 and on error return -1 with errno set to the error code. - */ -int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl) -{ - ntfs_log_trace("Entering.\n"); - - for (; rl->length; rl++) { - - ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n", - (long long)rl->lcn, (long long)rl->length); - - if (rl->lcn >= 0 && ntfs_bitmap_clear_run(vol->lcnbmp_na, - rl->lcn, rl->length)) { - int eo = errno; - ntfs_log_trace("Eeek! Deallocation of clusters failed.\n"); - errno = eo; - return -1; - } - } - return 0; -} - -/** - * ntfs_cluster_free - free clusters on an ntfs volume - * @vol: mounted ntfs volume on which to free the clusters - * @na: attribute whose runlist describes the clusters to free - * @start_vcn: vcn in @rl at which to start freeing clusters - * @count: number of clusters to free or -1 for all clusters - * - * Free @count clusters starting at the cluster @start_vcn in the runlist - * described by the attribute @na from the mounted ntfs volume @vol. - * - * If @count is -1, all clusters from @start_vcn to the end of the runlist - * are deallocated. - * - * On success return the number of deallocated clusters (not counting sparse - * clusters) and on error return -1 with errno set to the error code. - */ -int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) -{ - runlist *rl; - s64 nr_freed, delta, to_free; - - if (!vol || !vol->lcnbmp_na || !na || start_vcn < 0 || - (count < 0 && count != -1)) { - ntfs_log_trace("Invalid arguments!\n"); - errno = EINVAL; - return -1; - } - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, " - "vcn 0x%llx.\n", (unsigned long long)na->ni->mft_no, - na->type, (long long)count, (long long)start_vcn); - - rl = ntfs_attr_find_vcn(na, start_vcn); - if (!rl) { - if (errno == ENOENT) - return 0; - else - return -1; - } - - if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { - errno = EIO; - return -1; - } - - /* Find the starting cluster inside the run that needs freeing. */ - delta = start_vcn - rl->vcn; - - /* The number of clusters in this run that need freeing. */ - to_free = rl->length - delta; - if (count >= 0 && to_free > count) - to_free = count; - - if (rl->lcn != LCN_HOLE) { - /* Do the actual freeing of the clusters in this run. */ - if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn + delta, - to_free)) - return -1; - /* We have freed @to_free real clusters. */ - nr_freed = to_free; - } else { - /* No real clusters were freed. */ - nr_freed = 0; - } - - /* Go to the next run and adjust the number of clusters left to free. */ - ++rl; - if (count >= 0) - count -= to_free; - - /* - * Loop over the remaining runs, using @count as a capping value, and - * free them. - */ - for (; rl->length && count != 0; ++rl) { - // FIXME: Need to try ntfs_attr_map_runlist() for attribute - // list support! (AIA) - if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { - // FIXME: Eeek! We need rollback! (AIA) - ntfs_log_trace("Eeek! invalid lcn (= %lli). Should attempt " - "to map runlist! Leaving inconsistent " - "metadata!\n", (long long)rl->lcn); - errno = EIO; - return -1; - } - - /* The number of clusters in this run that need freeing. */ - to_free = rl->length; - if (count >= 0 && to_free > count) - to_free = count; - - if (rl->lcn != LCN_HOLE) { - /* Do the actual freeing of the clusters in the run. */ - if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, - to_free)) { - int eo = errno; - - // FIXME: Eeek! We need rollback! (AIA) - ntfs_log_trace("Eeek! bitmap clear run failed. " - "Leaving inconsistent metadata!\n"); - errno = eo; - return -1; - } - /* We have freed @to_free real clusters. */ - nr_freed += to_free; - } - - if (count >= 0) - count -= to_free; - } - - if (count != -1 && count != 0) { - // FIXME: Eeek! BUG() - ntfs_log_trace("Eeek! count still not zero (= %lli). Leaving " - "inconsistent metadata!\n", (long long)count); - errno = EIO; - return -1; - } - - /* Done. Return the number of actual clusters that were freed. */ - return nr_freed; -} diff --git a/libntfs/logfile.c b/libntfs/logfile.c deleted file mode 100644 index a429ce68..00000000 --- a/libntfs/logfile.c +++ /dev/null @@ -1,775 +0,0 @@ -/** - * logfile.c - NTFS journal handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2002-2005 Anton Altaparmakov - * Copyright (c) 2005 Yura Pakhuchiy - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif - -#include "attrib.h" -#include "debug.h" -#include "logfile.h" -#include "volume.h" -#include "mst.h" -#include "logging.h" - -/** - * ntfs_check_restart_page_header - check the page header for consistency - * @rp: restart page header to check - * @pos: position in logfile at which the restart page header resides - * - * Check the restart page header @rp for consistency and return TRUE if it is - * consistent and FALSE otherwise. - * - * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not - * require the full restart page. - */ -static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos) -{ - u32 logfile_system_page_size, logfile_log_page_size; - u16 ra_ofs, usa_count, usa_ofs, usa_end = 0; - BOOL have_usa = TRUE; - - ntfs_log_trace("Entering.\n"); - /* - * If the system or log page sizes are smaller than the ntfs block size - * or either is not a power of 2 we cannot handle this log file. - */ - logfile_system_page_size = le32_to_cpu(rp->system_page_size); - logfile_log_page_size = le32_to_cpu(rp->log_page_size); - if (logfile_system_page_size < NTFS_BLOCK_SIZE || - logfile_log_page_size < NTFS_BLOCK_SIZE || - logfile_system_page_size & - (logfile_system_page_size - 1) || - logfile_log_page_size & (logfile_log_page_size - 1)) { - ntfs_log_error("$LogFile uses unsupported page size.\n"); - return FALSE; - } - /* - * We must be either at !pos (1st restart page) or at pos = system page - * size (2nd restart page). - */ - if (pos && pos != logfile_system_page_size) { - ntfs_log_error("Found restart area in incorrect " - "position in $LogFile.\n"); - return FALSE; - } - /* We only know how to handle version 1.1. */ - if (sle16_to_cpu(rp->major_ver) != 1 || - sle16_to_cpu(rp->minor_ver) != 1) { - ntfs_log_error("$LogFile version %i.%i is not " - "supported. (This driver supports version " - "1.1 only.)\n", (int)sle16_to_cpu(rp->major_ver), - (int)sle16_to_cpu(rp->minor_ver)); - return FALSE; - } - /* - * If chkdsk has been run the restart page may not be protected by an - * update sequence array. - */ - if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) { - have_usa = FALSE; - goto skip_usa_checks; - } - /* Verify the size of the update sequence array. */ - usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS); - if (usa_count != le16_to_cpu(rp->usa_count)) { - ntfs_log_error("$LogFile restart page specifies " - "inconsistent update sequence array count.\n"); - return FALSE; - } - /* Verify the position of the update sequence array. */ - usa_ofs = le16_to_cpu(rp->usa_ofs); - usa_end = usa_ofs + usa_count * sizeof(u16); - if (usa_ofs < sizeof(RESTART_PAGE_HEADER) || - usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) { - ntfs_log_error("$LogFile restart page specifies " - "inconsistent update sequence array offset.\n"); - return FALSE; - } -skip_usa_checks: - /* - * Verify the position of the restart area. It must be: - * - aligned to 8-byte boundary, - * - after the update sequence array, and - * - within the system page size. - */ - ra_ofs = le16_to_cpu(rp->restart_area_offset); - if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end : - ra_ofs < sizeof(RESTART_PAGE_HEADER)) || - ra_ofs > logfile_system_page_size) { - ntfs_log_error("$LogFile restart page specifies " - "inconsistent restart area offset.\n"); - return FALSE; - } - /* - * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn - * set. - */ - if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) { - ntfs_log_error("$LogFile restart page is not modified " - "by chkdsk but a chkdsk LSN is specified.\n"); - return FALSE; - } - ntfs_log_trace("Done.\n"); - return TRUE; -} - -/** - * ntfs_check_restart_area - check the restart area for consistency - * @rp: restart page whose restart area to check - * - * Check the restart area of the restart page @rp for consistency and return - * TRUE if it is consistent and FALSE otherwise. - * - * This function assumes that the restart page header has already been - * consistency checked. - * - * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not - * require the full restart page. - */ -static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp) -{ - u64 file_size; - RESTART_AREA *ra; - u16 ra_ofs, ra_len, ca_ofs; - u8 fs_bits; - - ntfs_log_trace("Entering.\n"); - ra_ofs = le16_to_cpu(rp->restart_area_offset); - ra = (RESTART_AREA*)((u8*)rp + ra_ofs); - /* - * Everything before ra->file_size must be before the first word - * protected by an update sequence number. This ensures that it is - * safe to access ra->client_array_offset. - */ - if (ra_ofs + offsetof(RESTART_AREA, file_size) > - NTFS_BLOCK_SIZE - sizeof(u16)) { - ntfs_log_error("$LogFile restart area specifies " - "inconsistent file offset.\n"); - return FALSE; - } - /* - * Now that we can access ra->client_array_offset, make sure everything - * up to the log client array is before the first word protected by an - * update sequence number. This ensures we can access all of the - * restart area elements safely. Also, the client array offset must be - * aligned to an 8-byte boundary. - */ - ca_ofs = le16_to_cpu(ra->client_array_offset); - if (((ca_ofs + 7) & ~7) != ca_ofs || - ra_ofs + ca_ofs > (u16)(NTFS_BLOCK_SIZE - - sizeof(u16))) { - ntfs_log_error("$LogFile restart area specifies " - "inconsistent client array offset.\n"); - return FALSE; - } - /* - * The restart area must end within the system page size both when - * calculated manually and as specified by ra->restart_area_length. - * Also, the calculated length must not exceed the specified length. - */ - ra_len = ca_ofs + le16_to_cpu(ra->log_clients) * - sizeof(LOG_CLIENT_RECORD); - if ((u32)(ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) || - (u32)(ra_ofs + le16_to_cpu(ra->restart_area_length)) > - le32_to_cpu(rp->system_page_size) || - ra_len > le16_to_cpu(ra->restart_area_length)) { - ntfs_log_error("$LogFile restart area is out of bounds " - "of the system page size specified by the " - "restart page header and/or the specified " - "restart area length is inconsistent.\n"); - return FALSE; - } - /* - * The ra->client_free_list and ra->client_in_use_list must be either - * LOGFILE_NO_CLIENT or less than ra->log_clients or they are - * overflowing the client array. - */ - if ((ra->client_free_list != LOGFILE_NO_CLIENT && - le16_to_cpu(ra->client_free_list) >= - le16_to_cpu(ra->log_clients)) || - (ra->client_in_use_list != LOGFILE_NO_CLIENT && - le16_to_cpu(ra->client_in_use_list) >= - le16_to_cpu(ra->log_clients))) { - ntfs_log_error("$LogFile restart area specifies " - "overflowing client free and/or in use lists.\n"); - return FALSE; - } - /* - * Check ra->seq_number_bits against ra->file_size for consistency. - * We cannot just use ffs() because the file size is not a power of 2. - */ - file_size = (u64)sle64_to_cpu(ra->file_size); - fs_bits = 0; - while (file_size) { - file_size >>= 1; - fs_bits++; - } - if (le32_to_cpu(ra->seq_number_bits) != (u32)(67 - fs_bits)) { - ntfs_log_error("$LogFile restart area specifies " - "inconsistent sequence number bits.\n"); - return FALSE; - } - /* The log record header length must be a multiple of 8. */ - if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) != - le16_to_cpu(ra->log_record_header_length)) { - ntfs_log_error("$LogFile restart area specifies " - "inconsistent log record header length.\n"); - return FALSE; - } - /* Ditto for the log page data offset. */ - if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) != - le16_to_cpu(ra->log_page_data_offset)) { - ntfs_log_error("$LogFile restart area specifies " - "inconsistent log page data offset.\n"); - return FALSE; - } - ntfs_log_trace("Done.\n"); - return TRUE; -} - -/** - * ntfs_check_log_client_array - check the log client array for consistency - * @rp: restart page whose log client array to check - * - * Check the log client array of the restart page @rp for consistency and - * return TRUE if it is consistent and FALSE otherwise. - * - * This function assumes that the restart page header and the restart area have - * already been consistency checked. - * - * Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this - * function needs @rp->system_page_size bytes in @rp, i.e. it requires the full - * restart page and the page must be multi sector transfer deprotected. - */ -static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp) -{ - RESTART_AREA *ra; - LOG_CLIENT_RECORD *ca, *cr; - u16 nr_clients, idx; - BOOL in_free_list, idx_is_first; - - ntfs_log_trace("Entering.\n"); - ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); - ca = (LOG_CLIENT_RECORD*)((u8*)ra + - le16_to_cpu(ra->client_array_offset)); - /* - * Check the ra->client_free_list first and then check the - * ra->client_in_use_list. Check each of the log client records in - * each of the lists and check that the array does not overflow the - * ra->log_clients value. Also keep track of the number of records - * visited as there cannot be more than ra->log_clients records and - * that way we detect eventual loops in within a list. - */ - nr_clients = le16_to_cpu(ra->log_clients); - idx = le16_to_cpu(ra->client_free_list); - in_free_list = TRUE; -check_list: - for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--, - idx = le16_to_cpu(cr->next_client)) { - if (!nr_clients || idx >= le16_to_cpu(ra->log_clients)) - goto err_out; - /* Set @cr to the current log client record. */ - cr = ca + idx; - /* The first log client record must not have a prev_client. */ - if (idx_is_first) { - if (cr->prev_client != LOGFILE_NO_CLIENT) - goto err_out; - idx_is_first = FALSE; - } - } - /* Switch to and check the in use list if we just did the free list. */ - if (in_free_list) { - in_free_list = FALSE; - idx = le16_to_cpu(ra->client_in_use_list); - goto check_list; - } - ntfs_log_trace("Done.\n"); - return TRUE; -err_out: - ntfs_log_error("$LogFile log client array is corrupt.\n"); - return FALSE; -} - -/** - * ntfs_check_and_load_restart_page - check the restart page for consistency - * @log_na: opened ntfs attribute for journal $LogFile - * @rp: restart page to check - * @pos: position in @log_na at which the restart page resides - * @wrp: [OUT] copy of the multi sector transfer deprotected restart page - * @lsn: [OUT] set to the current logfile lsn on success - * - * Check the restart page @rp for consistency and return 0 if it is consistent - * and errno otherwise. The restart page may have been modified by chkdsk in - * which case its magic is CHKD instead of RSTR. - * - * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not - * require the full restart page. - * - * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a - * copy of the complete multi sector transfer deprotected page. On failure, - * *@wrp is undefined. - * - * Similarly, if @lsn is not NULL, on success *@lsn will be set to the current - * logfile lsn according to this restart page. On failure, *@lsn is undefined. - * - * The following error codes are defined: - * EINVAL - The restart page is inconsistent. - * ENOMEM - Not enough memory to load the restart page. - * EIO - Failed to reading from $LogFile. - */ -static int ntfs_check_and_load_restart_page(ntfs_attr *log_na, - RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp, - LSN *lsn) -{ - RESTART_AREA *ra; - RESTART_PAGE_HEADER *trp; - int err; - - ntfs_log_trace("Entering.\n"); - /* Check the restart page header for consistency. */ - if (!ntfs_check_restart_page_header(rp, pos)) { - /* Error output already done inside the function. */ - return EINVAL; - } - /* Check the restart area for consistency. */ - if (!ntfs_check_restart_area(rp)) { - /* Error output already done inside the function. */ - return EINVAL; - } - ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); - /* - * Allocate a buffer to store the whole restart page so we can multi - * sector transfer deprotect it. - */ - trp = malloc(le32_to_cpu(rp->system_page_size)); - if (!trp) { - ntfs_log_error("Failed to allocate memory for $LogFile " - "restart page buffer.\n"); - return ENOMEM; - } - /* - * Read the whole of the restart page into the buffer. If it fits - * completely inside @rp, just copy it from there. Otherwise read it - * from disk. - */ - if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE) - memcpy(trp, rp, le32_to_cpu(rp->system_page_size)); - else if (ntfs_attr_pread(log_na, pos, - le32_to_cpu(rp->system_page_size), trp) != - le32_to_cpu(rp->system_page_size)) { - err = errno; - ntfs_log_error("Failed to read whole restart page into the " - "buffer.\n"); - if (err != ENOMEM) - err = EIO; - goto err_out; - } - /* - * Perform the multi sector transfer deprotection on the buffer if the - * restart page is protected. - */ - if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count)) - && ntfs_mst_post_read_fixup((NTFS_RECORD*)trp, - le32_to_cpu(rp->system_page_size))) { - /* - * A multi sector tranfer error was detected. We only need to - * abort if the restart page contents exceed the multi sector - * transfer fixup of the first sector. - */ - if (le16_to_cpu(rp->restart_area_offset) + - le16_to_cpu(ra->restart_area_length) > - NTFS_BLOCK_SIZE - (int)sizeof(u16)) { - ntfs_log_error("Multi sector transfer error " - "detected in $LogFile restart page.\n"); - err = EINVAL; - goto err_out; - } - } - /* - * If the restart page is modified by chkdsk or there are no active - * logfile clients, the logfile is consistent. Otherwise, need to - * check the log client records for consistency, too. - */ - err = 0; - if (ntfs_is_rstr_record(rp->magic) && - ra->client_in_use_list != LOGFILE_NO_CLIENT) { - if (!ntfs_check_log_client_array(trp)) { - err = EINVAL; - goto err_out; - } - } - if (lsn) { - if (ntfs_is_rstr_record(rp->magic)) - *lsn = sle64_to_cpu(ra->current_lsn); - else /* if (ntfs_is_chkd_record(rp->magic)) */ - *lsn = sle64_to_cpu(rp->chkdsk_lsn); - } - ntfs_log_trace("Done.\n"); - if (wrp) - *wrp = trp; - else { -err_out: - free(trp); - } - return err; -} - -/** - * ntfs_check_logfile - check in the journal if the volume is consistent - * @log_na: ntfs attribute of loaded journal $LogFile to check - * @rp: [OUT] on success this is a copy of the current restart page - * - * Check the $LogFile journal for consistency and return TRUE if it is - * consistent and FALSE if not. On success, the current restart page is - * returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it. - * - * At present we only check the two restart pages and ignore the log record - * pages. - * - * Note that the MstProtected flag is not set on the $LogFile inode and hence - * when reading pages they are not deprotected. This is because we do not know - * if the $LogFile was created on a system with a different page size to ours - * yet and mst deprotection would fail if our page size is smaller. - */ -BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp) -{ - s64 size, pos; - LSN rstr1_lsn, rstr2_lsn; - ntfs_volume *vol = log_na->ni->vol; - u8 *kaddr = NULL; - RESTART_PAGE_HEADER *rstr1_ph = NULL; - RESTART_PAGE_HEADER *rstr2_ph = NULL; - int log_page_size, log_page_mask, err; - BOOL logfile_is_empty = TRUE; - u8 log_page_bits; - - ntfs_log_trace("Entering.\n"); - /* An empty $LogFile must have been clean before it got emptied. */ - if (NVolLogFileEmpty(vol)) - goto is_empty; - size = log_na->data_size; - /* Make sure the file doesn't exceed the maximum allowed size. */ - if (size > (s64)MaxLogFileSize) - size = MaxLogFileSize; - log_page_size = DefaultLogPageSize; - log_page_mask = log_page_size - 1; - /* - * Use generic_ffs() instead of ffs() to enable the compiler to - * optimize log_page_size and log_page_bits into constants. - */ - log_page_bits = ffs(log_page_size) - 1; - size &= ~(log_page_size - 1); - - /* - * Ensure the log file is big enough to store at least the two restart - * pages and the minimum number of log record pages. - */ - if (size < log_page_size * 2 || (size - log_page_size * 2) >> - log_page_bits < MinLogRecordPages) { - ntfs_log_error("$LogFile is too small.\n"); - return FALSE; - } - /* Allocate memory for restart page. */ - kaddr = malloc(NTFS_BLOCK_SIZE); - if (!kaddr) { - ntfs_log_error("Not enough memory.\n"); - return FALSE; - } - /* - * Read through the file looking for a restart page. Since the restart - * page header is at the beginning of a page we only need to search at - * what could be the beginning of a page (for each page size) rather - * than scanning the whole file byte by byte. If all potential places - * contain empty and uninitialized records, the log file can be assumed - * to be empty. - */ - for (pos = 0; pos < size; pos <<= 1) { - /* - * Read first NTFS_BLOCK_SIZE bytes of potential restart page. - */ - if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) != - NTFS_BLOCK_SIZE) { - ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE " - "bytes of potential restart page.\n"); - goto err_out; - } - - /* - * A non-empty block means the logfile is not empty while an - * empty block after a non-empty block has been encountered - * means we are done. - */ - if (!ntfs_is_empty_recordp((le32*)kaddr)) - logfile_is_empty = FALSE; - else if (!logfile_is_empty) - break; - /* - * A log record page means there cannot be a restart page after - * this so no need to continue searching. - */ - if (ntfs_is_rcrd_recordp((le32*)kaddr)) - break; - /* If not a (modified by chkdsk) restart page, continue. */ - if (!ntfs_is_rstr_recordp((le32*)kaddr) && - !ntfs_is_chkd_recordp((le32*)kaddr)) { - if (!pos) - pos = NTFS_BLOCK_SIZE >> 1; - continue; - } - /* - * Check the (modified by chkdsk) restart page for consistency - * and get a copy of the complete multi sector transfer - * deprotected restart page. - */ - err = ntfs_check_and_load_restart_page(log_na, - (RESTART_PAGE_HEADER*)kaddr, pos, - !rstr1_ph ? &rstr1_ph : &rstr2_ph, - !rstr1_ph ? &rstr1_lsn : &rstr2_lsn); - if (!err) { - /* - * If we have now found the first (modified by chkdsk) - * restart page, continue looking for the second one. - */ - if (!pos) { - pos = NTFS_BLOCK_SIZE >> 1; - continue; - } - /* - * We have now found the second (modified by chkdsk) - * restart page, so we can stop looking. - */ - break; - } - /* - * Error output already done inside the function. Note, we do - * not abort if the restart page was invalid as we might still - * find a valid one further in the file. - */ - if (err != EINVAL) - goto err_out; - /* Continue looking. */ - if (!pos) - pos = NTFS_BLOCK_SIZE >> 1; - } - if (kaddr) { - free(kaddr); - kaddr = NULL; - } - if (logfile_is_empty) { - NVolSetLogFileEmpty(vol); -is_empty: - ntfs_log_trace("Done. ($LogFile is empty.)\n"); - return TRUE; - } - if (!rstr1_ph) { - if (rstr2_ph) - ntfs_log_error("BUG: rstr2_ph isn't NULL!\n"); - ntfs_log_error("Did not find any restart pages in " - "$LogFile and it was not empty.\n"); - return FALSE; - } - /* If both restart pages were found, use the more recent one. */ - if (rstr2_ph) { - /* - * If the second restart area is more recent, switch to it. - * Otherwise just throw it away. - */ - if (rstr2_lsn > rstr1_lsn) { - ntfs_log_debug("Using second restart page as it is more " - "recent.\n"); - free(rstr1_ph); - rstr1_ph = rstr2_ph; - /* rstr1_lsn = rstr2_lsn; */ - } else { - ntfs_log_debug("Using first restart page as it is more " - "recent.\n"); - free(rstr2_ph); - } - rstr2_ph = NULL; - } - /* All consistency checks passed. */ - if (rp) - *rp = rstr1_ph; - else - free(rstr1_ph); - ntfs_log_trace("Done.\n"); - return TRUE; -err_out: - free(kaddr); - free(rstr1_ph); - free(rstr2_ph); - return FALSE; -} - -/** - * ntfs_is_logfile_clean - check in the journal if the volume is clean - * @log_na: ntfs attribute of loaded journal $LogFile to check - * @rp: copy of the current restart page - * - * Analyze the $LogFile journal and return TRUE if it indicates the volume was - * shutdown cleanly and FALSE if not. - * - * At present we only look at the two restart pages and ignore the log record - * pages. This is a little bit crude in that there will be a very small number - * of cases where we think that a volume is dirty when in fact it is clean. - * This should only affect volumes that have not been shutdown cleanly but did - * not have any pending, non-check-pointed i/o, i.e. they were completely idle - * at least for the five seconds preceding the unclean shutdown. - * - * This function assumes that the $LogFile journal has already been consistency - * checked by a call to ntfs_check_logfile() and in particular if the $LogFile - * is empty this function requires that NVolLogFileEmpty() is true otherwise an - * empty volume will be reported as dirty. - */ -BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp) -{ - RESTART_AREA *ra; - - ntfs_log_trace("Entering.\n"); - /* An empty $LogFile must have been clean before it got emptied. */ - if (NVolLogFileEmpty(log_na->ni->vol)) { - ntfs_log_trace("Done. ($LogFile is empty.)\n"); - return TRUE; - } - if (!rp) { - ntfs_log_error("Restart page header is NULL.\n"); - return FALSE; - } - if (!ntfs_is_rstr_record(rp->magic) && - !ntfs_is_chkd_record(rp->magic)) { - ntfs_log_error("Restart page buffer is invalid. This is " - "probably a bug in that the $LogFile should " - "have been consistency checked before calling " - "this function.\n"); - return FALSE; - } - - ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); - /* - * If the $LogFile has active clients, i.e. it is open, and we do not - * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags, - * we assume there was an unclean shutdown. - */ - if (ra->client_in_use_list != LOGFILE_NO_CLIENT && - !(ra->flags & RESTART_VOLUME_IS_CLEAN)) { - ntfs_log_debug("Done. $LogFile indicates a dirty shutdown.\n"); - return FALSE; - } - /* $LogFile indicates a clean shutdown. */ - ntfs_log_trace("Done. $LogFile indicates a clean shutdown.\n"); - return TRUE; -} - -/** - * ntfs_empty_logfile - empty the contents of the $LogFile journal - * @na: ntfs attribute of journal $LogFile to empty - * - * Empty the contents of the $LogFile journal @na and return 0 on success and - * -1 on error. - * - * This function assumes that the $LogFile journal has already been consistency - * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean() - * has been used to ensure that the $LogFile is clean. - */ -int ntfs_empty_logfile(ntfs_attr *na) -{ - s64 len, pos, count; - char buf[NTFS_BUF_SIZE]; - int err; - - ntfs_log_trace("Entering.\n"); - if (NVolLogFileEmpty(na->ni->vol)) - goto done; - - /* The $DATA attribute of the $LogFile has to be non-resident. */ - if (!NAttrNonResident(na)) { - err = EIO; - ntfs_log_debug("$LogFile $DATA attribute is resident!?!\n"); - goto io_error_exit; - } - - /* Get length of $LogFile contents. */ - len = na->data_size; - if (!len) { - ntfs_log_debug("$LogFile has zero length, no disk write " - "needed.\n"); - return 0; - } - - /* Read $LogFile until its end. We do this as a check for correct - length thus making sure we are decompressing the mapping pairs - array correctly and hence writing below is safe as well. */ - pos = 0; - while ((count = ntfs_attr_pread(na, pos, NTFS_BUF_SIZE, buf)) > 0) - pos += count; - - if (count == -1 || pos != len) { - err = errno; - ntfs_log_debug("Amount of $LogFile data read does not " - "correspond to expected length!\n"); - if (count != -1) - err = EIO; - goto io_error_exit; - } - - /* Fill the buffer with 0xff's. */ - memset(buf, -1, NTFS_BUF_SIZE); - - /* Set the $DATA attribute. */ - pos = 0; - while ((count = len - pos) > 0) { - if (count > NTFS_BUF_SIZE) - count = NTFS_BUF_SIZE; - - if ((count = ntfs_attr_pwrite(na, pos, count, buf)) <= 0) { - err = errno; - ntfs_log_debug("Failed to set the $LogFile attribute " - "value.\n"); - if (count != -1) - err = EIO; - goto io_error_exit; - } - pos += count; - } - - /* Set the flag so we do not have to do it again on remount. */ - NVolSetLogFileEmpty(na->ni->vol); -done: - ntfs_log_trace("Done.\n"); - return 0; -io_error_exit: - ntfs_attr_close(na); - ntfs_inode_close(na->ni); - errno = err; - return -1; -} diff --git a/libntfs/logging.c b/libntfs/logging.c deleted file mode 100644 index c79128a6..00000000 --- a/libntfs/logging.c +++ /dev/null @@ -1,670 +0,0 @@ -/** - * logging.c - Centralised logging. Part of the Linux-NTFS project. - * - * Copyright (c) 2005 Richard Russon - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDIO_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_STDARG_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_SYSLOG_H -#include -#endif - -#include "logging.h" - -#ifndef PATH_SEP -#define PATH_SEP '/' -#endif - -/* Colour prefixes and a suffix */ -static const char *col_green = "\e[32m"; -static const char *col_cyan = "\e[36m"; -static const char *col_yellow = "\e[01;33m"; -static const char *col_red = "\e[01;31m"; -static const char *col_redinv = "\e[01;07;31m"; -static const char *col_end = "\e[0m"; - -/** - * struct ntfs_logging - Control info for the logging system - * @levels: Bitfield of logging levels - * @flags: Flags which affect the output style - * @handler: Function to perform the actual logging - */ -struct ntfs_logging { - u32 levels; - u32 flags; - ntfs_log_handler *handler; -}; - -/** - * ntfs_log - * This struct controls all the logging within the library and tools. - */ -static struct ntfs_logging ntfs_log = { -#ifdef DEBUG - NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | -#endif - NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_WARNING | - NTFS_LOG_LEVEL_ERROR | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL | - NTFS_LOG_LEVEL_REASON | NTFS_LOG_LEVEL_PROGRESS, - NTFS_LOG_FLAG_ONLYNAME, -#ifdef DEBUG - ntfs_log_handler_outerr -#else - ntfs_log_handler_null -#endif -}; - - -/** - * ntfs_log_get_levels - Get a list of the current logging levels - * - * Find out which logging levels are enabled. - * - * Returns: Log levels in a 32-bit field - */ -u32 ntfs_log_get_levels(void) -{ - return ntfs_log.levels; -} - -/** - * ntfs_log_set_levels - Enable extra logging levels - * @levels: 32-bit field of log levels to set - * - * Enable one or more logging levels. - * The logging levels are named: NTFS_LOG_LEVEL_*. - * - * Returns: Log levels that were enabled before the call - */ -u32 ntfs_log_set_levels(u32 levels) -{ - u32 old; - old = ntfs_log.levels; - ntfs_log.levels |= levels; - return old; -} - -/** - * ntfs_log_clear_levels - Disable some logging levels - * @levels: 32-bit field of log levels to clear - * - * Disable one or more logging levels. - * The logging levels are named: NTFS_LOG_LEVEL_*. - * - * Returns: Log levels that were enabled before the call - */ -u32 ntfs_log_clear_levels(u32 levels) -{ - u32 old; - old = ntfs_log.levels; - ntfs_log.levels &= (~levels); - return old; -} - - -/** - * ntfs_log_get_flags - Get a list of logging style flags - * - * Find out which logging flags are enabled. - * - * Returns: Logging flags in a 32-bit field - */ -u32 ntfs_log_get_flags(void) -{ - return ntfs_log.flags; -} - -/** - * ntfs_log_set_flags - Enable extra logging style flags - * @flags: 32-bit field of logging flags to set - * - * Enable one or more logging flags. - * The log flags are named: NTFS_LOG_LEVEL_*. - * - * Returns: Logging flags that were enabled before the call - */ -u32 ntfs_log_set_flags(u32 flags) -{ - u32 old; - old = ntfs_log.flags; - ntfs_log.flags |= flags; - return old; -} - -/** - * ntfs_log_clear_flags - Disable some logging styles - * @flags: 32-bit field of logging flags to clear - * - * Disable one or more logging flags. - * The log flags are named: NTFS_LOG_LEVEL_*. - * - * Returns: Logging flags that were enabled before the call - */ -u32 ntfs_log_clear_flags(u32 flags) -{ - u32 old; - old = ntfs_log.flags; - ntfs_log.flags &= (~flags); - return old; -} - - -/** - * ntfs_log_get_stream - Default output streams for logging levels - * @level: Log level - * - * By default, urgent messages are sent to "stderr". - * Other messages are sent to "stdout". - * - * Returns: "string" Prefix to be used - */ -static FILE * ntfs_log_get_stream(u32 level) -{ - FILE *stream; - - switch (level) { - case NTFS_LOG_LEVEL_INFO: - case NTFS_LOG_LEVEL_QUIET: - case NTFS_LOG_LEVEL_PROGRESS: - case NTFS_LOG_LEVEL_VERBOSE: - stream = stdout; - break; - - case NTFS_LOG_LEVEL_DEBUG: - case NTFS_LOG_LEVEL_TRACE: - case NTFS_LOG_LEVEL_WARNING: - case NTFS_LOG_LEVEL_ERROR: - case NTFS_LOG_LEVEL_CRITICAL: - case NTFS_LOG_LEVEL_PERROR: - default: - stream = stderr; - break; - } - - return stream; -} - -/** - * ntfs_log_get_prefix - Default prefixes for logging levels - * @level: Log level to be prefixed - * - * Prefixing the logging output can make it easier to parse. - * - * Returns: "string" Prefix to be used - */ -static const char * ntfs_log_get_prefix(u32 level) -{ - const char *prefix; - - switch (level) { - case NTFS_LOG_LEVEL_DEBUG: - prefix = "DEBUG: "; - break; - case NTFS_LOG_LEVEL_TRACE: - prefix = "TRACE: "; - break; - case NTFS_LOG_LEVEL_QUIET: - prefix = "QUIET: "; - break; - case NTFS_LOG_LEVEL_INFO: - prefix = "INFO: "; - break; - case NTFS_LOG_LEVEL_VERBOSE: - prefix = "VERBOSE: "; - break; - case NTFS_LOG_LEVEL_PROGRESS: - prefix = "PROGRESS: "; - break; - case NTFS_LOG_LEVEL_WARNING: - prefix = "WARNING: "; - break; - case NTFS_LOG_LEVEL_ERROR: - prefix = "ERROR: "; - break; - case NTFS_LOG_LEVEL_PERROR: - prefix = "ERROR: "; - break; - case NTFS_LOG_LEVEL_CRITICAL: - prefix = "CRITICAL: "; - break; - default: - prefix = ""; - break; - } - - return prefix; -} - - -/** - * ntfs_log_set_handler - Provide an alternate logging handler - * @handler: function to perform the logging - * - * This alternate handler will be called for all future logging requests. - * If no @handler is specified, logging will revert to the default handler. - */ -void ntfs_log_set_handler(ntfs_log_handler *handler) -{ - if (handler) { - ntfs_log.handler = handler; -#ifdef HAVE_SYSLOG_H - if (handler == ntfs_log_handler_syslog) - openlog("libntfs", LOG_PID, LOG_USER); -#endif - } else - ntfs_log.handler = ntfs_log_handler_null; -} - -/** - * ntfs_log_redirect - Pass on the request to the real handler - * @function: Function in which the log line occurred - * @file: File in which the log line occurred - * @line: Line number on which the log line occurred - * @level: Level at which the line is logged - * @data: User specified data, possibly specific to a handler - * @format: printf-style formatting string - * @...: Arguments to be formatted - * - * This is just a redirector function. The arguments are simply passed to the - * main logging handler (as defined in the global logging struct @ntfs_log). - * - * Returns: -1 Error occurred - * 0 Message wasn't logged - * num Number of output characters - */ -int ntfs_log_redirect(const char *function, const char *file, - int line, u32 level, void *data, const char *format, ...) -{ - int olderr = errno; - int ret; - va_list args; - - if (!(ntfs_log.levels & level)) /* Don't log this message */ - return 0; - - va_start(args, format); - errno = olderr; - ret = ntfs_log.handler(function, file, line, level, data, format, args); - va_end(args); - - errno = olderr; - return ret; -} - - -/** - * ntfs_log_handler_syslog - syslog logging handler - * @function: Function in which the log line occurred - * @file: File in which the log line occurred - * @line: Line number on which the log line occurred - * @level: Level at which the line is logged - * @data: User specified data, possibly specific to a handler - * @format: printf-style formatting string - * @args: Arguments to be formatted - * - * A simple syslog logging handler. Ignores colors. - * - * Returns: -1 Error occurred - * 0 Message wasn't logged - * num Number of output characters - */ - -#ifdef HAVE_SYSLOG_H -int ntfs_log_handler_syslog(const char *function __attribute__((unused)), - const char *file, __attribute__((unused)) int line, u32 level, - void *data __attribute__((unused)), const char *format, va_list args) -{ - const int reason_size = 128; - static char *reason = NULL; - int ret = 0; - int olderr = errno; - - if (level == NTFS_LOG_LEVEL_REASON) { - if (!reason) - reason = malloc(reason_size); - if (reason) { - memset(reason, 0, reason_size); - return vsnprintf(reason, reason_size, format, args); - } else { - /* Rather than call ourselves, just drop through */ - level = NTFS_LOG_LEVEL_PERROR; - format = "Couldn't create reason"; - args = NULL; - olderr = errno; - } - } - - if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && - (strchr(file, PATH_SEP))) /* Abbreviate the filename */ - file = strrchr(file, PATH_SEP) + 1; -#if 0 /* FIXME: Implement this all. */ - if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */ - ret += fprintf(stream, "%s", ntfs_log_get_prefix(level)); - - if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */ - ret += fprintf(stream, "%s ", file); - - if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */ - ret += fprintf(stream, "(%d) ", line); - - if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */ - (level & NTFS_LOG_LEVEL_TRACE)) - ret += fprintf(stream, "%s(): ", function); - - ret += vfprintf(stream, format, args); - - if (level & NTFS_LOG_LEVEL_PERROR) { - if (reason) - ret += fprintf(stream, " : %s\n", reason); - else - ret += fprintf(stream, " : %s\n", strerror(olderr)); - } -#endif - vsyslog(LOG_NOTICE, format, args); - ret = 1; /* FIXME: caclulate how many bytes had been written. */ - - errno = olderr; - return ret; -} -#endif - -/** - * ntfs_log_handler_fprintf - Basic logging handler - * @function: Function in which the log line occurred - * @file: File in which the log line occurred - * @line: Line number on which the log line occurred - * @level: Level at which the line is logged - * @data: User specified data, possibly specific to a handler - * @format: printf-style formatting string - * @args: Arguments to be formatted - * - * A simple logging handler. This is where the log line is finally displayed. - * It is more likely that you will want to set the handler to either - * ntfs_log_handler_outerr or ntfs_log_handler_stderr. - * - * Note: For this handler, @data is a pointer to a FILE output stream. - * If @data is NULL, nothing will be displayed. - * - * Returns: -1 Error occurred - * 0 Message wasn't logged - * num Number of output characters - */ -int ntfs_log_handler_fprintf(const char *function, const char *file, - int line, u32 level, void *data, const char *format, va_list args) -{ - const int reason_size = 128; - static char *reason = NULL; - int ret = 0; - int olderr = errno; - FILE *stream; - const char *col_prefix = NULL; - const char *col_suffix = NULL; - - if (!data) /* Interpret data as a FILE stream. */ - return 0; /* If it's NULL, we can't do anything. */ - stream = (FILE*)data; - - if (level == NTFS_LOG_LEVEL_REASON) { - if (!reason) - reason = malloc(reason_size); - if (reason) { - memset(reason, 0, reason_size); - return vsnprintf(reason, reason_size, format, args); - } else { - /* Rather than call ourselves, just drop through */ - level = NTFS_LOG_LEVEL_PERROR; - format = "Couldn't create reason"; - args = NULL; - olderr = errno; - } - } - - if (ntfs_log.flags & NTFS_LOG_FLAG_COLOUR) { - /* Pick a colour determined by the log level */ - switch (level) { - case NTFS_LOG_LEVEL_DEBUG: - col_prefix = col_green; - col_suffix = col_end; - break; - case NTFS_LOG_LEVEL_TRACE: - col_prefix = col_cyan; - col_suffix = col_end; - break; - case NTFS_LOG_LEVEL_WARNING: - col_prefix = col_yellow; - col_suffix = col_end; - break; - case NTFS_LOG_LEVEL_ERROR: - case NTFS_LOG_LEVEL_PERROR: - col_prefix = col_red; - col_suffix = col_end; - break; - case NTFS_LOG_LEVEL_CRITICAL: - col_prefix = col_redinv; - col_suffix = col_end; - break; - } - } - - if (col_prefix) - ret += fprintf(stream, col_prefix); - - if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && - (strchr(file, PATH_SEP))) /* Abbreviate the filename */ - file = strrchr(file, PATH_SEP) + 1; - - if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */ - ret += fprintf(stream, "%s", ntfs_log_get_prefix(level)); - - if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */ - ret += fprintf(stream, "%s ", file); - - if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */ - ret += fprintf(stream, "(%d) ", line); - - if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */ - (level & NTFS_LOG_LEVEL_TRACE)) - ret += fprintf(stream, "%s(): ", function); - - ret += vfprintf(stream, format, args); - - if (level & NTFS_LOG_LEVEL_PERROR) { - if (reason) - ret += fprintf(stream, " : %s\n", reason); - else - ret += fprintf(stream, " : %s\n", strerror(olderr)); - } - - if (col_suffix) - ret += fprintf(stream, col_suffix); - - - fflush(stream); - errno = olderr; - return ret; -} - -/** - * ntfs_log_handler_null - Null logging handler (no output) - * @function: Function in which the log line occurred - * @file: File in which the log line occurred - * @line: Line number on which the log line occurred - * @level: Level at which the line is logged - * @data: User specified data, possibly specific to a handler - * @format: printf-style formatting string - * @args: Arguments to be formatted - * - * This handler produces no output. It provides a way to temporarily disable - * logging, without having to change the levels and flags. - * - * Returns: 0 Message wasn't logged - */ -int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)), - int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)), - const char *format __attribute__((unused)), va_list args __attribute__((unused))) -{ - return 0; -} - -/** - * ntfs_log_handler_stdout - All logs go to stdout - * @function: Function in which the log line occurred - * @file: File in which the log line occurred - * @line: Line number on which the log line occurred - * @level: Level at which the line is logged - * @data: User specified data, possibly specific to a handler - * @format: printf-style formatting string - * @args: Arguments to be formatted - * - * Display a log message to stdout. - * - * Note: For this handler, @data is a pointer to a FILE output stream. - * If @data is NULL, then stdout will be used. - * - * Note: This function calls ntfs_log_handler_fprintf to do the main work. - * - * Returns: -1 Error occurred - * 0 Message wasn't logged - * num Number of output characters - */ -int ntfs_log_handler_stdout(const char *function, const char *file, - int line, u32 level, void *data, const char *format, va_list args) -{ - if (!data) - data = stdout; - - return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); -} - -/** - * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level - * @function: Function in which the log line occurred - * @file: File in which the log line occurred - * @line: Line number on which the log line occurred - * @level: Level at which the line is logged - * @data: User specified data, possibly specific to a handler - * @format: printf-style formatting string - * @args: Arguments to be formatted - * - * Display a log message. The output stream will be determined by the log - * level. - * - * Note: For this handler, @data is a pointer to a FILE output stream. - * If @data is NULL, the function ntfs_log_get_stream will be called - * - * Note: This function calls ntfs_log_handler_fprintf to do the main work. - * - * Returns: -1 Error occurred - * 0 Message wasn't logged - * num Number of output characters - */ -int ntfs_log_handler_outerr(const char *function, const char *file, - int line, u32 level, void *data, const char *format, va_list args) -{ - if (!data) - data = ntfs_log_get_stream(level); - - return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); -} - -/** - * ntfs_log_handler_stderr - All logs go to stderr - * @function: Function in which the log line occurred - * @file: File in which the log line occurred - * @line: Line number on which the log line occurred - * @level: Level at which the line is logged - * @data: User specified data, possibly specific to a handler - * @format: printf-style formatting string - * @args: Arguments to be formatted - * - * Display a log message to stderr. - * - * Note: For this handler, @data is a pointer to a FILE output stream. - * If @data is NULL, then stdout will be used. - * - * Note: This function calls ntfs_log_handler_fprintf to do the main work. - * - * Returns: -1 Error occurred - * 0 Message wasn't logged - * num Number of output characters - */ -int ntfs_log_handler_stderr(const char *function, const char *file, - int line, u32 level, void *data, const char *format, va_list args) -{ - if (!data) - data = stderr; - - return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); -} - - -/** - * ntfs_log_parse_option - Act upon command line options - * @option: Option flag - * - * Delegate some of the work of parsing the command line. All the options begin - * with "--log-". Options cause log levels to be enabled in @ntfs_log (the - * global logging structure). - * - * Note: The "colour" option changes the logging handler. - * - * Returns: TRUE Option understood - * FALSE Invalid log option - */ -BOOL ntfs_log_parse_option(const char *option) -{ - if (strcmp(option, "--log-debug") == 0) { - ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG); - return TRUE; - } else if (strcmp(option, "--log-verbose") == 0) { - ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); - return TRUE; - } else if (strcmp(option, "--log-quiet") == 0) { - ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); - return TRUE; - } else if (strcmp(option, "--log-trace") == 0) { - ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); - return TRUE; - } else if ((strcmp(option, "--log-colour") == 0) || - (strcmp(option, "--log-color") == 0)) { - ntfs_log_set_flags(NTFS_LOG_FLAG_COLOUR); - return TRUE; - } - - ntfs_log_debug("Unknown logging option '%s'\n", option); - return FALSE; -} - diff --git a/libntfs/mft.c b/libntfs/mft.c deleted file mode 100644 index 302784bc..00000000 --- a/libntfs/mft.c +++ /dev/null @@ -1,1960 +0,0 @@ -/** - * mft.c - Mft record handling code. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2004 Anton Altaparmakov - * Copyright (c) 2005 Yura Pakhuchiy - * Copyright (c) 2004-2005 Richard Russon - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_STDIO_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#include - -#include "compat.h" -#include "types.h" -#include "device.h" -#include "debug.h" -#include "bitmap.h" -#include "attrib.h" -#include "inode.h" -#include "volume.h" -#include "layout.h" -#include "lcnalloc.h" -#include "mft.h" -#include "logging.h" - -/** - * ntfs_mft_records_read - read records from the mft from disk - * @vol: volume to read from - * @mref: starting mft record number to read - * @count: number of mft records to read - * @b: output data buffer - * - * Read @count mft records starting at @mref from volume @vol into buffer - * @b. Return 0 on success or -1 on error, with errno set to the error - * code. - * - * If any of the records exceed the initialized size of the $MFT/$DATA - * attribute, i.e. they cannot possibly be allocated mft records, assume this - * is a bug and return error code ESPIPE. - * - * The read mft records are mst deprotected and are hence ready to use. The - * caller should check each record with is_baad_record() in case mst - * deprotection failed. - * - * NOTE: @b has to be at least of size @count * vol->mft_record_size. - */ -int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, - const s64 count, MFT_RECORD *b) -{ - s64 br; - VCN m; - - ntfs_log_trace("Entering for inode 0x%llx.\n", MREF(mref)); - if (!vol || !vol->mft_na || !b || count < 0) { - errno = EINVAL; - return -1; - } - m = MREF(mref); - /* Refuse to read non-allocated mft records. */ - if (m + count > vol->mft_na->initialized_size >> - vol->mft_record_size_bits) { - errno = ESPIPE; - return -1; - } - br = ntfs_attr_mst_pread(vol->mft_na, m << vol->mft_record_size_bits, - count, vol->mft_record_size, b); - if (br != count) { - if (br != -1) - errno = EIO; - if (br >= 0) - ntfs_log_debug("Error: partition is smaller than it should " - "be!\n"); - else - ntfs_log_perror("Error reading $Mft record(s)"); - return -1; - } - return 0; -} - -/** - * ntfs_mft_records_write - write mft records to disk - * @vol: volume to write to - * @mref: starting mft record number to write - * @count: number of mft records to write - * @b: data buffer containing the mft records to write - * - * Write @count mft records starting at @mref from data buffer @b to volume - * @vol. Return 0 on success or -1 on error, with errno set to the error code. - * - * If any of the records exceed the initialized size of the $MFT/$DATA - * attribute, i.e. they cannot possibly be allocated mft records, assume this - * is a bug and return error code ESPIPE. - * - * Before the mft records are written, they are mst protected. After the write, - * they are deprotected again, thus resulting in an increase in the update - * sequence number inside the data buffer @b. - * - * If any mft records are written which are also represented in the mft mirror - * $MFTMirr, we make a copy of the relevant parts of the data buffer @b into a - * temporary buffer before we do the actual write. Then if at least one mft - * record was successfully written, we write the appropriate mft records from - * the copied buffer to the mft mirror, too. - */ -int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, - const s64 count, MFT_RECORD *b) -{ - s64 bw; - VCN m; - void *bmirr = NULL; - int cnt = 0, res = 0; - - ntfs_log_trace("Entering for inode 0x%llx.\n", MREF(mref)); - if (!vol || !vol->mft_na || vol->mftmirr_size <= 0 || !b || count < 0) { - errno = EINVAL; - return -1; - } - m = MREF(mref); - /* Refuse to write non-allocated mft records. */ - if (m + count > vol->mft_na->initialized_size >> - vol->mft_record_size_bits) { - errno = ESPIPE; - return -1; - } - if (m < vol->mftmirr_size) { - if (!vol->mftmirr_na) { - errno = EINVAL; - return -1; - } - cnt = vol->mftmirr_size - m; - if (cnt > count) - cnt = count; - bmirr = malloc(cnt * vol->mft_record_size); - if (!bmirr) - return -1; - memcpy(bmirr, b, cnt * vol->mft_record_size); - } - bw = ntfs_attr_mst_pwrite(vol->mft_na, m << vol->mft_record_size_bits, - count, vol->mft_record_size, b); - if (bw != count) { - if (bw != -1) - errno = EIO; - if (bw >= 0) - ntfs_log_debug("Error: partial write while writing $Mft " - "record(s)!\n"); - else - ntfs_log_perror("Error writing $Mft record(s)"); - res = errno; - } - if (bmirr && bw > 0) { - if (bw < cnt) - cnt = bw; - bw = ntfs_attr_mst_pwrite(vol->mftmirr_na, - m << vol->mft_record_size_bits, cnt, - vol->mft_record_size, bmirr); - if (bw != cnt) { - if (bw != -1) - errno = EIO; - ntfs_log_debug("Error: failed to sync $MFTMirr! Run " - "chkdsk.\n"); - res = errno; - } - } - free(bmirr); - if (!res) - return res; - errno = res; - return -1; -} - -/** - * ntfs_file_record_read - read a FILE record from the mft from disk - * @vol: volume to read from - * @mref: mft reference specifying mft record to read - * @mrec: address of pointer in which to return the mft record - * @attr: address of pointer in which to return the first attribute - * - * Read a FILE record from the mft of @vol from the storage medium. @mref - * specifies the mft record to read, including the sequence number, which can - * be 0 if no sequence number checking is to be performed. - * - * The function allocates a buffer large enough to hold the mft record and - * reads the record into the buffer (mst deprotecting it in the process). - * *@mrec is then set to point to the buffer. - * - * If @attr is not NULL, *@attr is set to point to the first attribute in the - * mft record, i.e. *@attr is a pointer into *@mrec. - * - * Return 0 on success, or -1 on error, with errno set to the error code. - * - * The read mft record is checked for having the magic FILE, - * and for having a matching sequence number (if MSEQNO(*@mref) != 0). - * If either of these fails, -1 is returned and errno is set to EIO. If you get - * this, but you still want to read the mft record (e.g. in order to correct - * it), use ntfs_mft_record_read() directly. - * - * Note: Caller has to free *@mrec when finished. - * - * Note: We do not check if the mft record is flagged in use. The caller can - * check if desired. - */ -int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, - MFT_RECORD **mrec, ATTR_RECORD **attr) -{ - MFT_RECORD *m; - ATTR_RECORD *a; - int err; - - if (!vol || !mrec) { - errno = EINVAL; - return -1; - } - m = *mrec; - if (!m) { - m = (MFT_RECORD*)malloc(vol->mft_record_size); - if (!m) - return -1; - } - if (ntfs_mft_record_read(vol, mref, m)) { - err = errno; - goto read_failed; - } - if (!ntfs_is_file_record(m->magic)) - goto file_corrupt; - if (MSEQNO(mref) && MSEQNO(mref) != le16_to_cpu(m->sequence_number)) - goto file_corrupt; - a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); - if (p2n(a) < p2n(m) || (char*)a > (char*)m + vol->mft_record_size) - goto file_corrupt; - *mrec = m; - if (attr) - *attr = a; - return 0; -file_corrupt: - ntfs_log_debug("ntfs_file_record_read(): file is corrupt.\n"); - err = EIO; -read_failed: - if (m != *mrec) - free(m); - errno = err; - return -1; -} - -/** - * ntfs_mft_record_layout - layout an mft record into a memory buffer - * @vol: volume to which the mft record will belong - * @mref: mft reference specifying the mft record number - * @mrec: destination buffer of size >= @vol->mft_record_size bytes - * - * Layout an empty, unused mft record with the mft reference @mref into the - * buffer @m. The volume @vol is needed because the mft record structure was - * modified in NTFS 3.1 so we need to know which volume version this mft record - * will be used on. - * - * On success return 0 and on error return -1 with errno set to the error code. - */ -int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, - MFT_RECORD *mrec) -{ - ATTR_RECORD *a; - - if (!vol || !mrec) { - errno = EINVAL; - return -1; - } - /* Aligned to 2-byte boundary. */ - if (vol->major_ver < 3 || (vol->major_ver == 3 && !vol->minor_ver)) - mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD_OLD) + 1) & ~1); - else { - /* Abort if mref is > 32 bits. */ - if (MREF(mref) & 0x0000ffff00000000ull) { - ntfs_log_debug("Mft reference exceeds 32 bits!\n"); - errno = ERANGE; - return -1; - } - mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD) + 1) & ~1); - /* - * Set the NTFS 3.1+ specific fields while we know that the - * volume version is 3.1+. - */ - mrec->reserved = cpu_to_le16(0); - mrec->mft_record_number = cpu_to_le32(MREF(mref)); - } - mrec->magic = magic_FILE; - if (vol->mft_record_size >= NTFS_BLOCK_SIZE) - mrec->usa_count = cpu_to_le16(vol->mft_record_size / - NTFS_BLOCK_SIZE + 1); - else { - mrec->usa_count = cpu_to_le16(1); - ntfs_log_error("Sector size is bigger than MFT record size. " - "Setting usa_count to 1. If Windows chkdsk " - "reports this as corruption, please email %s " - "stating that you saw this message and that " - "the file system created was corrupt. " - "Thank you.\n", NTFS_DEV_LIST); - } - /* Set the update sequence number to 1. */ - *(u16*)((u8*)mrec + le16_to_cpu(mrec->usa_ofs)) = cpu_to_le16(1); - mrec->lsn = cpu_to_le64(0ull); - mrec->sequence_number = cpu_to_le16(1); - mrec->link_count = cpu_to_le16(0); - /* Aligned to 8-byte boundary. */ - mrec->attrs_offset = cpu_to_le16((le16_to_cpu(mrec->usa_ofs) + - (le16_to_cpu(mrec->usa_count) << 1) + 7) & ~7); - mrec->flags = cpu_to_le16(0); - /* - * Using attrs_offset plus eight bytes (for the termination attribute), - * aligned to 8-byte boundary. - */ - mrec->bytes_in_use = cpu_to_le32((le16_to_cpu(mrec->attrs_offset) + 8 + - 7) & ~7); - mrec->bytes_allocated = cpu_to_le32(vol->mft_record_size); - mrec->base_mft_record = cpu_to_le64((MFT_REF)0); - mrec->next_attr_instance = cpu_to_le16(0); - a = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset)); - a->type = AT_END; - a->length = cpu_to_le32(0); - /* Finally, clear the unused part of the mft record. */ - memset((u8*)a + 8, 0, vol->mft_record_size - ((u8*)a + 8 - (u8*)mrec)); - return 0; -} - -/** - * ntfs_mft_record_format - format an mft record on an ntfs volume - * @vol: volume on which to format the mft record - * @mref: mft reference specifying mft record to format - * - * Format the mft record with the mft reference @mref in $MFT/$DATA, i.e. lay - * out an empty, unused mft record in memory and write it to the volume @vol. - * - * On success return 0 and on error return -1 with errno set to the error code. - */ -int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref) -{ - MFT_RECORD *m; - int err; - - if (!vol || !vol->mft_na) { - errno = EINVAL; - return -1; - } - m = malloc(vol->mft_record_size); - if (!m) - return -1; - if (ntfs_mft_record_layout(vol, mref, m)) { - err = errno; - free(m); - errno = err; - return -1; - } - if (ntfs_mft_record_write(vol, mref, m)) { - err = errno; - free(m); - errno = err; - return -1; - } - free(m); - return 0; -} - -#ifndef NTFS_DISABLE_DEBUG_LOGGING -static const char *es = " Leaving inconsistent metadata. Run chkdsk."; -#endif - -/** - * ntfs_ffz - Find the first unset (zero) bit in a word - * @word: - * - * Description... - * - * Returns: - */ -static inline unsigned int ntfs_ffz(unsigned int word) -{ - return ffs(~word) - 1; -} - -#ifndef PAGE_SIZE -#define PAGE_SIZE 4096 -#endif - -/** - * ntfs_mft_bitmap_find_free_rec - find a free mft record in the mft bitmap - * @vol: volume on which to search for a free mft record - * @base_ni: open base inode if allocating an extent mft record or NULL - * - * Search for a free mft record in the mft bitmap attribute on the ntfs volume - * @vol. - * - * If @base_ni is NULL start the search at the default allocator position. - * - * If @base_ni is not NULL start the search at the mft record after the base - * mft record @base_ni. - * - * Return the free mft record on success and -1 on error with errno set to the - * error code. An error code of ENOSPC means that there are no free mft - * records in the currently initialized mft bitmap. - */ -static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) -{ - s64 pass_end, ll, data_pos, pass_start, ofs, bit; - ntfs_attr *mftbmp_na; - u8 *buf, *byte; - unsigned int size; - u8 pass, b; - - mftbmp_na = vol->mftbmp_na; - /* - * Set the end of the pass making sure we do not overflow the mft - * bitmap. - */ - size = PAGE_SIZE; - pass_end = vol->mft_na->allocated_size >> vol->mft_record_size_bits; - ll = mftbmp_na->initialized_size << 3; - if (pass_end > ll) - pass_end = ll; - pass = 1; - if (!base_ni) - data_pos = vol->mft_data_pos; - else - data_pos = base_ni->mft_no + 1; - if (data_pos < 24) - data_pos = 24; - if (data_pos >= pass_end) { - data_pos = 24; - pass = 2; - /* This happens on a freshly formatted volume. */ - if (data_pos >= pass_end) { - errno = ENOSPC; - return -1; - } - } - pass_start = data_pos; - buf = (u8*)malloc(PAGE_SIZE); - if (!buf) - return -1; - ntfs_log_debug("Starting bitmap search: pass %u, pass_start 0x%llx, " - "pass_end 0x%llx, data_pos 0x%llx.\n", pass, - (long long)pass_start, (long long)pass_end, - (long long)data_pos); -#ifndef NTFS_DISABLE_DEBUG_LOGGING - byte = NULL; - b = 0; -#endif - /* Loop until a free mft record is found. */ - for (; pass <= 2; size = PAGE_SIZE) { - /* Cap size to pass_end. */ - ofs = data_pos >> 3; - ll = ((pass_end + 7) >> 3) - ofs; - if (size > ll) - size = ll; - ll = ntfs_attr_pread(mftbmp_na, ofs, size, buf); - if (ll < 0) { - ntfs_log_error("Failed to read mft bitmap " - "attribute, aborting.\n"); - free(buf); - return -1; - } - ntfs_log_debug("Read 0x%llx bytes.\n", (long long)ll); - /* If we read at least one byte, search @buf for a zero bit. */ - if (ll) { - size = ll << 3; - bit = data_pos & 7; - data_pos &= ~7ull; - ntfs_log_debug("Before inner for loop: size 0x%x, " - "data_pos 0x%llx, bit 0x%llx, " - "*byte 0x%hhx, b %u.\n", size, - (long long)data_pos, (long long)bit, - byte ? *byte : -1, b); - for (; bit < size && data_pos + bit < pass_end; - bit &= ~7ull, bit += 8) { - byte = buf + (bit >> 3); - if (*byte == 0xff) - continue; - /* Note: ffz() result must be zero based. */ - b = ntfs_ffz((unsigned long)*byte); - if (b < 8 && b >= (bit & 7)) { - free(buf); - return data_pos + (bit & ~7ull) + b; - } - } - ntfs_log_debug("After inner for loop: size 0x%x, " - "data_pos 0x%llx, bit 0x%llx, " - "*byte 0x%hhx, b %u.\n", size, - (long long)data_pos, (long long)bit, - byte ? *byte : -1, b); - data_pos += size; - /* - * If the end of the pass has not been reached yet, - * continue searching the mft bitmap for a zero bit. - */ - if (data_pos < pass_end) - continue; - } - /* Do the next pass. */ - pass++; - if (pass == 2) { - /* - * Starting the second pass, in which we scan the first - * part of the zone which we omitted earlier. - */ - pass_end = pass_start; - data_pos = pass_start = 24; - ntfs_log_debug("pass %i, pass_start 0x%llx, pass_end " - "0x%llx.\n", pass, (long long)pass_start, - (long long)pass_end); - if (data_pos >= pass_end) - break; - } - } - /* No free mft records in currently initialized mft bitmap. */ - free(buf); - errno = ENOSPC; - return -1; -} - -/** - * ntfs_mft_bitmap_extend_allocation - extend mft bitmap attribute by a cluster - * @vol: volume on which to extend the mft bitmap attribute - * - * Extend the mft bitmap attribute on the ntfs volume @vol by one cluster. - * - * Note: Only changes allocated_size, i.e. does not touch initialized_size or - * data_size. - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol) -{ - LCN lcn; - s64 ll = 0; /* silence compiler warning */ - ntfs_attr *mftbmp_na, *lcnbmp_na; - runlist_element *rl, *rl2 = NULL; /* silence compiler warning */ - ntfs_attr_search_ctx *ctx; - MFT_RECORD *m = NULL; /* silence compiler warning */ - ATTR_RECORD *a = NULL; /* silence compiler warning */ - int ret, mp_size; - u32 old_alen = 0; /* silence compiler warning */ - u8 b, tb; - struct { - u8 added_cluster:1; - u8 added_run:1; - u8 mp_rebuilt:1; - } status = { 0, 0, 0 }; - - mftbmp_na = vol->mftbmp_na; - lcnbmp_na = vol->lcnbmp_na; - /* - * Determine the last lcn of the mft bitmap. The allocated size of the - * mft bitmap cannot be zero so we are ok to do this. - */ - rl = ntfs_attr_find_vcn(mftbmp_na, (mftbmp_na->allocated_size - 1) >> - vol->cluster_size_bits); - if (!rl || !rl->length || rl->lcn < 0) { - ntfs_log_error("Failed to determine last allocated " - "cluster of mft bitmap attribute.\n"); - if (rl) - errno = EIO; - return -1; - } - lcn = rl->lcn + rl->length; - /* - * Attempt to get the cluster following the last allocated cluster by - * hand as it may be in the MFT zone so the allocator would not give it - * to us. - */ - ret = (int)ntfs_attr_pread(lcnbmp_na, lcn >> 3, 1, &b); - if (ret < 0) { - ntfs_log_error("Failed to read from lcn bitmap.\n"); - return -1; - } - ntfs_log_debug("Read %i byte%s.\n", ret, ret == 1 ? "" : "s"); - tb = 1 << (lcn & 7ull); - if (ret == 1 && b != 0xff && !(b & tb)) { - /* Next cluster is free, allocate it. */ - b |= tb; - ret = (int)ntfs_attr_pwrite(lcnbmp_na, lcn >> 3, 1, &b); - if (ret < 1) { - ntfs_log_error("Failed to write to lcn " - "bitmap.\n"); - if (!ret) - errno = EIO; - return -1; - } - /* Update the mft bitmap runlist. */ - rl->length++; - rl[1].vcn++; - status.added_cluster = 1; - ntfs_log_debug("Appending one cluster to mft bitmap.\n"); - } else { - /* Allocate a cluster from the DATA_ZONE. */ - rl2 = ntfs_cluster_alloc(vol, rl[1].vcn, 1, lcn, DATA_ZONE); - if (!rl2) { - ntfs_log_error("Failed to allocate a cluster for " - "the mft bitmap.\n"); - return -1; - } - rl = ntfs_runlists_merge(mftbmp_na->rl, rl2); - if (!rl) { - ret = errno; - ntfs_log_error("Failed to merge runlists for mft " - "bitmap.\n"); - if (ntfs_cluster_free_from_rl(vol, rl2)) - ntfs_log_error("Failed to deallocate " - "cluster.%s\n", es); - free(rl2); - errno = ret; - return -1; - } - mftbmp_na->rl = rl; - status.added_run = 1; - ntfs_log_debug("Adding one run to mft bitmap.\n"); - /* Find the last run in the new runlist. */ - for (; rl[1].length; rl++) - ; - } - /* - * Update the attribute record as well. Note: @rl is the last - * (non-terminator) runlist element of mft bitmap. - */ - ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); - if (!ctx) { - ntfs_log_error("Failed to get search context.\n"); - goto undo_alloc; - } - if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, - mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) { - ntfs_log_error("Failed to find last attribute extent of " - "mft bitmap attribute.\n"); - goto undo_alloc; - } - m = ctx->mrec; - a = ctx->attr; - ll = sle64_to_cpu(a->lowest_vcn); - rl2 = ntfs_attr_find_vcn(mftbmp_na, ll); - if (!rl2 || !rl2->length) { - ntfs_log_error("Failed to determine previous last " - "allocated cluster of mft bitmap attribute.\n"); - if (rl2) - errno = EIO; - goto undo_alloc; - } - /* Get the size for the new mapping pairs array for this extent. */ - mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll); - if (mp_size <= 0) { - ntfs_log_error("Get size for mapping pairs failed for " - "mft bitmap attribute extent.\n"); - goto undo_alloc; - } - /* Expand the attribute record if necessary. */ - old_alen = le32_to_cpu(a->length); - if (ntfs_attr_record_resize(m, a, mp_size + - le16_to_cpu(a->mapping_pairs_offset))) { - if (errno != ENOSPC) { - ntfs_log_error("Failed to resize attribute " - "record for mft bitmap attribute.\n"); - goto undo_alloc; - } - // TODO: Deal with this by moving this extent to a new mft - // record or by starting a new extent in a new mft record. - ntfs_log_error("Not enough space in this mft record to " - "accommodate extended mft bitmap attribute " - "extent. Cannot handle this yet.\n"); - errno = EOPNOTSUPP; - goto undo_alloc; - } - status.mp_rebuilt = 1; - /* Generate the mapping pairs array directly into the attr record. */ - if (ntfs_mapping_pairs_build(vol, (u8*)a + - le16_to_cpu(a->mapping_pairs_offset), mp_size, rl2, ll, - NULL)) { - ntfs_log_error("Failed to build mapping pairs array for " - "mft bitmap attribute.\n"); - errno = EIO; - goto undo_alloc; - } - /* Update the highest_vcn. */ - a->highest_vcn = cpu_to_sle64(rl[1].vcn - 1); - /* - * We now have extended the mft bitmap allocated_size by one cluster. - * Reflect this in the ntfs_attr structure and the attribute record. - */ - if (a->lowest_vcn) { - /* - * We are not in the first attribute extent, switch to it, but - * first ensure the changes will make it to disk later. - */ - ntfs_inode_mark_dirty(ctx->ntfs_ino); - ntfs_attr_reinit_search_ctx(ctx); - if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, - mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { - ntfs_log_error("Failed to find first attribute " - "extent of mft bitmap attribute.\n"); - goto restore_undo_alloc; - } - a = ctx->attr; - } - mftbmp_na->allocated_size += vol->cluster_size; - a->allocated_size = cpu_to_sle64(mftbmp_na->allocated_size); - /* Ensure the changes make it to disk. */ - ntfs_inode_mark_dirty(ctx->ntfs_ino); - ntfs_attr_put_search_ctx(ctx); - return 0; -restore_undo_alloc: - ret = errno; - ntfs_attr_reinit_search_ctx(ctx); - if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, - mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) { - ntfs_log_error("Failed to find last attribute extent of " - "mft bitmap attribute.%s\n", es); - ntfs_attr_put_search_ctx(ctx); - mftbmp_na->allocated_size += vol->cluster_size; - /* - * The only thing that is now wrong is ->allocated_size of the - * base attribute extent which chkdsk should be able to fix. - */ - errno = ret; - return -1; - } - m = ctx->mrec; - a = ctx->attr; - a->highest_vcn = cpu_to_sle64(rl[1].vcn - 2); - errno = ret; -undo_alloc: - ret = errno; - if (status.added_cluster) { - /* Truncate the last run in the runlist by one cluster. */ - rl->length--; - rl[1].vcn--; - } else if (status.added_run) { - lcn = rl->lcn; - /* Remove the last run from the runlist. */ - rl->lcn = rl[1].lcn; - rl->length = 0; - } - /* Deallocate the cluster. */ - if (ntfs_bitmap_clear_bit(lcnbmp_na, lcn)) - ntfs_log_error("Failed to free cluster.%s\n", es); - if (status.mp_rebuilt) { - if (ntfs_mapping_pairs_build(vol, (u8*)a + - le16_to_cpu(a->mapping_pairs_offset), - old_alen - le16_to_cpu(a->mapping_pairs_offset), - rl2, ll, NULL)) - ntfs_log_error("Failed to restore mapping " - "pairs array.%s\n", es); - if (ntfs_attr_record_resize(m, a, old_alen)) - ntfs_log_error("Failed to restore attribute " - "record.%s\n", es); - ntfs_inode_mark_dirty(ctx->ntfs_ino); - } - if (ctx) - ntfs_attr_put_search_ctx(ctx); - errno = ret; - return -1; -} - -/** - * ntfs_mft_bitmap_extend_initialized - extend mft bitmap initialized data - * @vol: volume on which to extend the mft bitmap attribute - * - * Extend the initialized portion of the mft bitmap attribute on the ntfs - * volume @vol by 8 bytes. - * - * Note: Only changes initialized_size and data_size, i.e. requires that - * allocated_size is big enough to fit the new initialized_size. - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -static int ntfs_mft_bitmap_extend_initialized(ntfs_volume *vol) -{ - s64 old_data_size, old_initialized_size, ll; - ntfs_attr *mftbmp_na; - ntfs_attr_search_ctx *ctx; - ATTR_RECORD *a; - int err; - - mftbmp_na = vol->mftbmp_na; - ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); - if (!ctx) { - ntfs_log_error("Failed to get search context.\n"); - return -1; - } - if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, - mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { - ntfs_log_error("Failed to find first attribute extent of " - "mft bitmap attribute.\n"); - err = errno; - goto put_err_out; - } - a = ctx->attr; - old_data_size = mftbmp_na->data_size; - old_initialized_size = mftbmp_na->initialized_size; - mftbmp_na->initialized_size += 8; - a->initialized_size = cpu_to_sle64(mftbmp_na->initialized_size); - if (mftbmp_na->initialized_size > mftbmp_na->data_size) { - mftbmp_na->data_size = mftbmp_na->initialized_size; - a->data_size = cpu_to_sle64(mftbmp_na->data_size); - } - /* Ensure the changes make it to disk. */ - ntfs_inode_mark_dirty(ctx->ntfs_ino); - ntfs_attr_put_search_ctx(ctx); - /* Initialize the mft bitmap attribute value with zeroes. */ - ll = 0; - ll = ntfs_attr_pwrite(mftbmp_na, old_initialized_size, 8, &ll); - if (ll == 8) { - ntfs_log_debug("Wrote eight initialized bytes to mft bitmap.\n"); - return 0; - } - ntfs_log_error("Failed to write to mft bitmap.\n"); - err = errno; - if (ll >= 0) - err = EIO; - /* Try to recover from the error. */ - ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); - if (!ctx) { - ntfs_log_error("Failed to get search context.%s\n", es); - goto err_out; - } - if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, - mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { - ntfs_log_error("Failed to find first attribute extent of " - "mft bitmap attribute.%s\n", es); -put_err_out: - ntfs_attr_put_search_ctx(ctx); - goto err_out; - } - a = ctx->attr; - mftbmp_na->initialized_size = old_initialized_size; - a->initialized_size = cpu_to_sle64(old_initialized_size); - if (mftbmp_na->data_size != old_data_size) { - mftbmp_na->data_size = old_data_size; - a->data_size = cpu_to_sle64(old_data_size); - } - ntfs_inode_mark_dirty(ctx->ntfs_ino); - ntfs_attr_put_search_ctx(ctx); - ntfs_log_debug("Restored status of mftbmp: allocated_size 0x%llx, " - "data_size 0x%llx, initialized_size 0x%llx.\n", - (long long)mftbmp_na->allocated_size, - (long long)mftbmp_na->data_size, - (long long)mftbmp_na->initialized_size); -err_out: - errno = err; - return -1; -} - -/** - * ntfs_mft_data_extend_allocation - extend mft data attribute - * @vol: volume on which to extend the mft data attribute - * - * Extend the mft data attribute on the ntfs volume @vol by 16 mft records - * worth of clusters or if not enough space for this by one mft record worth - * of clusters. - * - * Note: Only changes allocated_size, i.e. does not touch initialized_size or - * data_size. - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) -{ - LCN lcn; - VCN old_last_vcn; - s64 min_nr, nr, ll = 0; /* silence compiler warning */ - ntfs_attr *mft_na; - runlist_element *rl, *rl2; - ntfs_attr_search_ctx *ctx; - MFT_RECORD *m = NULL; /* silence compiler warning */ - ATTR_RECORD *a = NULL; /* silence compiler warning */ - int err, mp_size; - u32 old_alen = 0; /* silence compiler warning */ - BOOL mp_rebuilt = FALSE; - - ntfs_log_debug("Extending mft data allocation.\n"); - mft_na = vol->mft_na; - /* - * Determine the preferred allocation location, i.e. the last lcn of - * the mft data attribute. The allocated size of the mft data - * attribute cannot be zero so we are ok to do this. - */ - rl = ntfs_attr_find_vcn(mft_na, - (mft_na->allocated_size - 1) >> vol->cluster_size_bits); - if (!rl || !rl->length || rl->lcn < 0) { - ntfs_log_error("Failed to determine last allocated " - "cluster of mft data attribute.\n"); - if (rl) - errno = EIO; - return -1; - } - lcn = rl->lcn + rl->length; - ntfs_log_debug("Last lcn of mft data attribute is 0x%llx.\n", (long long)lcn); - /* Minimum allocation is one mft record worth of clusters. */ - min_nr = vol->mft_record_size >> vol->cluster_size_bits; - if (!min_nr) - min_nr = 1; - /* Want to allocate 16 mft records worth of clusters. */ - nr = vol->mft_record_size << 4 >> vol->cluster_size_bits; - if (!nr) - nr = min_nr; - ntfs_log_debug("Trying mft data allocation with default cluster count " - "%lli.\n", (long long)nr); - old_last_vcn = rl[1].vcn; - do { - rl2 = ntfs_cluster_alloc(vol, old_last_vcn, nr, lcn, MFT_ZONE); - if (rl2) - break; - if (errno != ENOSPC || nr == min_nr) { - ntfs_log_error("Failed to allocate the minimal " - "number of clusters (%lli) for the " - "mft data attribute.\n", (long long)nr); - return -1; - } - /* - * There is not enough space to do the allocation, but there - * might be enough space to do a minimal allocation so try that - * before failing. - */ - nr = min_nr; - ntfs_log_debug("Retrying mft data allocation with minimal cluster " - "count %lli.\n", (long long)nr); - } while (1); - rl = ntfs_runlists_merge(mft_na->rl, rl2); - if (!rl) { - err = errno; - ntfs_log_error("Failed to merge runlists for mft data " - "attribute.\n"); - if (ntfs_cluster_free_from_rl(vol, rl2)) - ntfs_log_error("Failed to deallocate clusters " - "from the mft data attribute.%s\n", es); - free(rl2); - errno = err; - return -1; - } - mft_na->rl = rl; - ntfs_log_debug("Allocated %lli clusters.\n", nr); - /* Find the last run in the new runlist. */ - for (; rl[1].length; rl++) - ; - /* Update the attribute record as well. */ - ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); - if (!ctx) { - ntfs_log_error("Failed to get search context.\n"); - goto undo_alloc; - } - if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, - rl[1].vcn, NULL, 0, ctx)) { - ntfs_log_error("Failed to find last attribute extent of " - "mft data attribute.\n"); - goto undo_alloc; - } - m = ctx->mrec; - a = ctx->attr; - ll = sle64_to_cpu(a->lowest_vcn); - rl2 = ntfs_attr_find_vcn(mft_na, ll); - if (!rl2 || !rl2->length) { - ntfs_log_error("Failed to determine previous last " - "allocated cluster of mft data attribute.\n"); - if (rl2) - errno = EIO; - goto undo_alloc; - } - /* Get the size for the new mapping pairs array for this extent. */ - mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll); - if (mp_size <= 0) { - ntfs_log_error("Get size for mapping pairs failed for " - "mft data attribute extent.\n"); - goto undo_alloc; - } - /* Expand the attribute record if necessary. */ - old_alen = le32_to_cpu(a->length); - if (ntfs_attr_record_resize(m, a, - mp_size + le16_to_cpu(a->mapping_pairs_offset))) { - if (errno != ENOSPC) { - ntfs_log_error("Failed to resize attribute " - "record for mft data attribute.\n"); - goto undo_alloc; - } - // TODO: Deal with this by moving this extent to a new mft - // record or by starting a new extent in a new mft record. - // Note: Use the special reserved mft records and ensure that - // this extent is not required to find the mft record in - // question. - ntfs_log_error("Not enough space in this mft record to " - "accommodate extended mft data attribute " - "extent. Cannot handle this yet.\n"); - errno = EOPNOTSUPP; - goto undo_alloc; - } - mp_rebuilt = TRUE; - /* - * Generate the mapping pairs array directly into the attribute record. - */ - if (ntfs_mapping_pairs_build(vol, - (u8*)a + le16_to_cpu(a->mapping_pairs_offset), mp_size, - rl2, ll, NULL)) { - ntfs_log_error("Failed to build mapping pairs array of " - "mft data attribute.\n"); - errno = EIO; - goto undo_alloc; - } - /* Update the highest_vcn. */ - a->highest_vcn = cpu_to_sle64(rl[1].vcn - 1); - /* - * We now have extended the mft data allocated_size by nr clusters. - * Reflect this in the ntfs_attr structure and the attribute record. - * @rl is the last (non-terminator) runlist element of mft data - * attribute. - */ - if (a->lowest_vcn) { - /* - * We are not in the first attribute extent, switch to it, but - * first ensure the changes will make it to disk later. - */ - ntfs_inode_mark_dirty(ctx->ntfs_ino); - ntfs_attr_reinit_search_ctx(ctx); - if (ntfs_attr_lookup(mft_na->type, mft_na->name, - mft_na->name_len, 0, 0, NULL, 0, ctx)) { - ntfs_log_error("Failed to find first attribute " - "extent of mft data attribute.\n"); - goto restore_undo_alloc; - } - a = ctx->attr; - } - mft_na->allocated_size += nr << vol->cluster_size_bits; - a->allocated_size = cpu_to_sle64(mft_na->allocated_size); - /* Ensure the changes make it to disk. */ - ntfs_inode_mark_dirty(ctx->ntfs_ino); - ntfs_attr_put_search_ctx(ctx); - return 0; -restore_undo_alloc: - err = errno; - ntfs_attr_reinit_search_ctx(ctx); - if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, - rl[1].vcn, NULL, 0, ctx)) { - ntfs_log_error("Failed to find last attribute extent of " - "mft data attribute.%s\n", es); - ntfs_attr_put_search_ctx(ctx); - mft_na->allocated_size += nr << vol->cluster_size_bits; - /* - * The only thing that is now wrong is ->allocated_size of the - * base attribute extent which chkdsk should be able to fix. - */ - errno = err; - return -1; - } - m = ctx->mrec; - a = ctx->attr; - a->highest_vcn = cpu_to_sle64(old_last_vcn - 1); - errno = err; -undo_alloc: - err = errno; - if (ntfs_cluster_free(vol, mft_na, old_last_vcn, -1) < 0) - ntfs_log_error("Failed to free clusters from mft data " - "attribute.%s\n", es); - if (ntfs_rl_truncate(&mft_na->rl, old_last_vcn)) - ntfs_log_error("Failed to truncate mft data attribute " - "runlist.%s\n", es); - if (mp_rebuilt) { - if (ntfs_mapping_pairs_build(vol, (u8*)a + - le16_to_cpu(a->mapping_pairs_offset), - old_alen - le16_to_cpu(a->mapping_pairs_offset), - rl2, ll, NULL)) - ntfs_log_error("Failed to restore mapping pairs " - "array.%s\n", es); - if (ntfs_attr_record_resize(m, a, old_alen)) - ntfs_log_error("Failed to restore attribute " - "record.%s\n", es); - ntfs_inode_mark_dirty(ctx->ntfs_ino); - } - if (ctx) - ntfs_attr_put_search_ctx(ctx); - errno = err; - return -1; -} - -/** - * ntfs_mft_record_alloc - allocate an mft record on an ntfs volume - * @vol: volume on which to allocate the mft record - * @base_ni: open base inode if allocating an extent mft record or NULL - * - * Allocate an mft record in $MFT/$DATA of an open ntfs volume @vol. - * - * If @base_ni is NULL make the mft record a base mft record and allocate it at - * the default allocator position. - * - * If @base_ni is not NULL make the allocated mft record an extent record, - * allocate it starting at the mft record after the base mft record and attach - * the allocated and opened ntfs inode to the base inode @base_ni. - * - * On success return the now opened ntfs (extent) inode of the mft record. - * - * On error return NULL with errno set to the error code. - * - * To find a free mft record, we scan the mft bitmap for a zero bit. To - * optimize this we start scanning at the place specified by @base_ni or if - * @base_ni is NULL we start 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 the $DATA attribute of $MFT. 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, it does not 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 attribute 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 - * is not sufficient space to do this, we try to extend by a single mft record - * or one cluster, if cluster size is above the 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 - * accordingly, open an ntfs_inode for it and return it to the caller, 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 - * superfluous bits are padded with zeroes. - * - * Thus, when we return successfully (return value non-zero), 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, - * - open an ntfs_inode for the allocated mft record, and we will - * - return the ntfs_inode. - * - * On error (return value zero), nothing will have changed. If we had changed - * anything before the error occurred, 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 consistent state when returning from - * this function. - * - * 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 does not 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. - */ -ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) -{ - s64 ll, bit, old_data_initialized, old_data_size; - ntfs_attr *mft_na, *mftbmp_na; - ntfs_attr_search_ctx *ctx; - MFT_RECORD *m; - ATTR_RECORD *a; - ntfs_inode *ni; - int err; - u16 seq_no, usn; - - if (base_ni) - ntfs_log_trace("Entering (allocating an extent mft record for " - "base mft record 0x%llx).\n", - (long long)base_ni->mft_no); - else - ntfs_log_trace("Entering (allocating a base mft record).\n"); - if (!vol || !vol->mft_na || !vol->mftbmp_na) { - errno = EINVAL; - return NULL; - } - mft_na = vol->mft_na; - mftbmp_na = vol->mftbmp_na; - bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); - if (bit >= 0) { - ntfs_log_debug("Found free record (#1), bit 0x%llx.\n", - (long long)bit); - goto found_free_rec; - } - if (errno != ENOSPC) - return NULL; - /* - * No free mft records left. If the mft bitmap already covers more - * than the currently used mft records, the next records are all free, - * so we can simply allocate the first unused mft record. - * Note: We also have to make sure that the mft bitmap at least covers - * the first 24 mft records as they are special and whilst they may not - * be in use, we do not allocate from them. - */ - ll = mft_na->initialized_size >> vol->mft_record_size_bits; - if (mftbmp_na->initialized_size << 3 > ll && - mftbmp_na->initialized_size > 3) { - bit = ll; - if (bit < 24) - bit = 24; - ntfs_log_debug("Found free record (#2), bit 0x%llx.\n", - (long long)bit); - goto found_free_rec; - } - /* - * The mft bitmap needs to be expanded until it covers the first unused - * mft record that we can allocate. - * Note: The smallest mft record we allocate is mft record 24. - */ - ntfs_log_debug("Status of mftbmp before extension: allocated_size 0x%llx, " - "data_size 0x%llx, initialized_size 0x%llx.\n", - (long long)mftbmp_na->allocated_size, - (long long)mftbmp_na->data_size, - (long long)mftbmp_na->initialized_size); - if (mftbmp_na->initialized_size + 8 > mftbmp_na->allocated_size) { - /* Need to extend bitmap by one more cluster. */ - ntfs_log_debug("mftbmp: initialized_size + 8 > allocated_size.\n"); - if (ntfs_mft_bitmap_extend_allocation(vol)) - goto err_out; - ntfs_log_debug("Status of mftbmp after allocation extension: " - "allocated_size 0x%llx, data_size 0x%llx, " - "initialized_size 0x%llx.\n", - (long long)mftbmp_na->allocated_size, - (long long)mftbmp_na->data_size, - (long long)mftbmp_na->initialized_size); - } - /* - * We now have sufficient allocated space, extend the initialized_size - * as well as the data_size if necessary and fill the new space with - * zeroes. - */ - bit = mftbmp_na->initialized_size << 3; - if (ntfs_mft_bitmap_extend_initialized(vol)) - goto err_out; - ntfs_log_debug("Status of mftbmp after initialized extension: " - "allocated_size 0x%llx, data_size 0x%llx, " - "initialized_size 0x%llx.\n", - (long long)mftbmp_na->allocated_size, - (long long)mftbmp_na->data_size, - (long long)mftbmp_na->initialized_size); - ntfs_log_debug("Found free record (#3), bit 0x%llx.\n", (long long)bit); -found_free_rec: - /* @bit is the found free mft record, allocate it in the mft bitmap. */ - ntfs_log_debug("At found_free_rec.\n"); - if (ntfs_bitmap_set_bit(mftbmp_na, bit)) { - ntfs_log_error("Failed to allocate bit in mft bitmap.\n"); - goto err_out; - } - ntfs_log_debug("Set bit 0x%llx in mft bitmap.\n", (long long)bit); - /* The mft bitmap is now uptodate. Deal with mft data attribute now. */ - ll = (bit + 1) << vol->mft_record_size_bits; - if (ll <= mft_na->initialized_size) { - ntfs_log_debug("Allocated mft record already initialized.\n"); - goto mft_rec_already_initialized; - } - ntfs_log_debug("Initializing allocated mft record.\n"); - /* - * The mft record is outside the initialized data. Extend the mft data - * attribute until it covers the allocated record. The loop is only - * actually traversed more than once when a freshly formatted volume is - * first written to so it optimizes away nicely in the common case. - */ - ntfs_log_debug("Status of mft data before extension: " - "allocated_size 0x%llx, data_size 0x%llx, " - "initialized_size 0x%llx.\n", - (long long)mft_na->allocated_size, - (long long)mft_na->data_size, - (long long)mft_na->initialized_size); - while (ll > mft_na->allocated_size) { - if (ntfs_mft_data_extend_allocation(vol)) - goto undo_mftbmp_alloc; - ntfs_log_debug("Status of mft data after allocation extension: " - "allocated_size 0x%llx, data_size 0x%llx, " - "initialized_size 0x%llx.\n", - (long long)mft_na->allocated_size, - (long long)mft_na->data_size, - (long long)mft_na->initialized_size); - } - old_data_initialized = mft_na->initialized_size; - old_data_size = mft_na->data_size; - /* - * Extend mft data initialized size (and data size of course) to reach - * the allocated mft record, formatting the mft records along the way. - * Note: We only modify the ntfs_attr structure as that is all that is - * needed by ntfs_mft_record_format(). We will update the attribute - * record itself in one fell swoop later on. - */ - while (ll > mft_na->initialized_size) { - s64 ll2 = mft_na->initialized_size >> vol->mft_record_size_bits; - mft_na->initialized_size += vol->mft_record_size; - if (mft_na->initialized_size > mft_na->data_size) - mft_na->data_size = mft_na->initialized_size; - ntfs_log_debug("Initializing mft record 0x%llx.\n", (long long)ll2); - err = ntfs_mft_record_format(vol, ll2); - if (err) { - ntfs_log_error("Failed to format mft record.\n"); - goto undo_data_init; - } - } - /* Update the mft data attribute record to reflect the new sizes. */ - ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); - if (!ctx) { - ntfs_log_error("Failed to get search context.\n"); - goto undo_data_init; - } - if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, - 0, NULL, 0, ctx)) { - ntfs_log_error("Failed to find first attribute extent of " - "mft data attribute.\n"); - ntfs_attr_put_search_ctx(ctx); - goto undo_data_init; - } - a = ctx->attr; - a->initialized_size = cpu_to_sle64(mft_na->initialized_size); - a->data_size = cpu_to_sle64(mft_na->data_size); - /* Ensure the changes make it to disk. */ - ntfs_inode_mark_dirty(ctx->ntfs_ino); - ntfs_attr_put_search_ctx(ctx); - ntfs_log_debug("Status of mft data after mft record initialization: " - "allocated_size 0x%llx, data_size 0x%llx, " - "initialized_size 0x%llx.\n", - (long long)mft_na->allocated_size, - (long long)mft_na->data_size, - (long long)mft_na->initialized_size); - /* Sanity checks. */ - if (mft_na->data_size > mft_na->allocated_size || - mft_na->initialized_size > mft_na->data_size) - NTFS_BUG("mft_na sanity checks failed"); - // BUG_ON(mft_na->initialized_size > mft_na->data_size); - // BUG_ON(mft_na->data_size > mft_na->allocated_size); -mft_rec_already_initialized: - /* - * We now have allocated and initialized the mft record. Need to read - * it from disk and re-format it, preserving the sequence number if it - * is not zero as well as the update sequence number if it is not zero - * or -1 (0xffff). - */ - m = (MFT_RECORD*)malloc(vol->mft_record_size); - if (!m) { - ntfs_log_error("Failed to allocate buffer for mft " - "record.\n"); - goto undo_mftbmp_alloc; - } - if (ntfs_mft_record_read(vol, bit, m)) { - err = errno; - ntfs_log_error("Failed to read mft record.\n"); - free(m); - errno = err; - goto undo_mftbmp_alloc; - } - /* Sanity check that the mft record is really not in use. */ - if (ntfs_is_file_record(m->magic) && (m->flags & MFT_RECORD_IN_USE)) { - ntfs_log_error("Mft record 0x%llx was marked unused in " - "mft bitmap but is marked used itself. " - "Corrupt filesystem or library bug! " - "Run chkdsk immediately!\n", (long long)bit); - free(m); - errno = EIO; - goto undo_mftbmp_alloc; - } - seq_no = m->sequence_number; - usn = *(u16*)((u8*)m + le16_to_cpu(m->usa_ofs)); - if (ntfs_mft_record_layout(vol, bit, m)) { - err = errno; - ntfs_log_error("Failed to re-format mft record.\n"); - free(m); - errno = err; - goto undo_mftbmp_alloc; - } - if (le16_to_cpu(seq_no)) - m->sequence_number = seq_no; - seq_no = le16_to_cpu(usn); - if (seq_no && seq_no != 0xffff) - *(u16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn; - /* Set the mft record itself in use. */ - m->flags |= MFT_RECORD_IN_USE; - /* Now need to open an ntfs inode for the mft record. */ - ni = ntfs_inode_allocate(vol); - if (!ni) { - err = errno; - ntfs_log_error("Failed to allocate buffer for inode.\n"); - free(m); - errno = err; - goto undo_mftbmp_alloc; - } - ni->mft_no = bit; - ni->mrec = m; - /* - * If we are allocating an extent mft record, make the opened inode an - * extent inode and attach it to the base inode. Also, set the base - * mft record reference in the extent inode. - */ - if (base_ni) { - ni->nr_extents = -1; - ni->base_ni = base_ni; - m->base_mft_record = MK_LE_MREF(base_ni->mft_no, - le16_to_cpu(base_ni->mrec->sequence_number)); - /* - * Attach the extent inode to the base inode, reallocating - * memory if needed. - */ - if (!(base_ni->nr_extents & 3)) { - ntfs_inode **extent_nis; - int i; - - i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); - extent_nis = (ntfs_inode**)malloc(i); - if (!extent_nis) { - err = errno; - ntfs_log_error("Failed to allocate " - "buffer for extent inodes " - "array.\n"); - free(m); - free(ni); - errno = err; - goto undo_mftbmp_alloc; - } - if (base_ni->extent_nis) { - memcpy(extent_nis, base_ni->extent_nis, - i - 4 * sizeof(ntfs_inode *)); - free(base_ni->extent_nis); - } - base_ni->extent_nis = extent_nis; - } - base_ni->extent_nis[base_ni->nr_extents++] = ni; - } - /* Make sure the allocated inode is written out to disk later. */ - ntfs_inode_mark_dirty(ni); - /* Initialize time, allocated and data size in ntfs_inode struct. */ - ni->data_size = ni->allocated_size = 0; - ni->flags = 0; - ni->creation_time = ni->last_data_change_time = - ni->last_mft_change_time = - ni->last_access_time = time(NULL); - /* Update the default mft allocation position if it was used. */ - if (!base_ni) - vol->mft_data_pos = bit + 1; - /* Return the opened, allocated inode of the allocated mft record. */ - ntfs_log_debug("Returning opened, allocated %sinode 0x%llx.\n", - base_ni ? "extent " : "", (long long)bit); - return ni; -undo_data_init: - mft_na->initialized_size = old_data_initialized; - mft_na->data_size = old_data_size; -undo_mftbmp_alloc: - err = errno; - if (ntfs_bitmap_clear_bit(mftbmp_na, bit)) - ntfs_log_error("Failed to clear bit in mft bitmap.%s\n", es); - errno = err; -err_out: - if (!errno) - errno = EIO; - return NULL; -} - -/** - * ntfs_mft_record_free - free an mft record on an ntfs volume - * @vol: volume on which to free the mft record - * @ni: open ntfs inode of the mft record to free - * - * Free the mft record of the open inode @ni on the mounted ntfs volume @vol. - * Note that this function calls ntfs_inode_close() internally and hence you - * cannot use the pointer @ni any more after this function returns success. - * - * On success return 0 and on error return -1 with errno set to the error code. - */ -int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni) -{ - u64 mft_no; - int err; - u16 seq_no, old_seq_no; - - ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); - - if (!vol || !vol->mftbmp_na || !ni) { - errno = EINVAL; - return -1; - } - - /* Cache the mft reference for later. */ - mft_no = ni->mft_no; - - /* Mark the mft record as not in use. */ - ni->mrec->flags &= ~MFT_RECORD_IN_USE; - - /* Increment the sequence number, skipping zero, if it is not zero. */ - old_seq_no = ni->mrec->sequence_number; - seq_no = le16_to_cpu(old_seq_no); - if (seq_no == 0xffff) - seq_no = 1; - else if (seq_no) - seq_no++; - ni->mrec->sequence_number = cpu_to_le16(seq_no); - - /* Set the inode dirty and write it out. */ - ntfs_inode_mark_dirty(ni); - if (ntfs_inode_sync(ni)) { - err = errno; - goto sync_rollback; - } - - /* Clear the bit in the $MFT/$BITMAP corresponding to this record. */ - if (ntfs_bitmap_clear_bit(vol->mftbmp_na, mft_no)) { - err = errno; - // FIXME: If ntfs_bitmap_clear_run() guarantees rollback on - // error, this could be changed to goto sync_rollback; - goto bitmap_rollback; - } - - /* Throw away the now freed inode. */ - if (!ntfs_inode_close(ni)) - return 0; - err = errno; - - /* Rollback what we did... */ -bitmap_rollback: - if (ntfs_bitmap_set_bit(vol->mftbmp_na, mft_no)) - ntfs_log_debug("Eeek! Rollback failed in ntfs_mft_record_free(). " - "Leaving inconsistent metadata!\n"); -sync_rollback: - ni->mrec->flags |= MFT_RECORD_IN_USE; - ni->mrec->sequence_number = old_seq_no; - ntfs_inode_mark_dirty(ni); - errno = err; - return -1; -} - - -#ifdef NTFS_RICH - -#include "bitmap.h" -#include "dir.h" -#include "tree.h" -#include "index.h" -#include "rich.h" - -/** - * ntfs_mft_remove_attr - Remove an attribute from an MFT record - * @bmp: - * @inode: - * @type: - * - * Description... - * - * Returns: - */ -int ntfs_mft_remove_attr(struct ntfs_bmp *bmp, ntfs_inode *inode, ATTR_TYPES type) -{ - ATTR_RECORD *attr20, *attrXX; - MFT_RECORD *mft; - u8 *src, *dst; - int len; - - if (!inode) - return 1; - - ntfs_log_trace ("\n"); - attr20 = find_first_attribute(AT_ATTRIBUTE_LIST, inode->mrec); - if (attr20) - return 1; - - ntfs_log_debug("remove inode %lld, attr 0x%02X\n", inode->mft_no, type); - - attrXX = find_first_attribute(type, inode->mrec); - if (!attrXX) - return 1; - - if (utils_free_non_residents3(bmp, inode, attrXX)) - return 1; - - // remove entry - // inode->mrec - - mft = inode->mrec; - //utils_dump_mem(mft, 0, mft->bytes_in_use, DM_DEFAULTS); ntfs_log_debug("\n"); - - //utils_dump_mem(attrXX, 0, attrXX->length, DM_DEFAULTS); ntfs_log_debug("\n"); - - //ntfs_log_debug("mrec = %p, attr = %p, diff = %d (0x%02X)\n", mft, attrXX, (u8*)attrXX - (u8*)mft, (u8*)attrXX - (u8*)mft); - // memmove - - dst = (u8*) attrXX; - src = dst + attrXX->length; - len = (((u8*) mft + mft->bytes_in_use) - src); - - // fix mft header - mft->bytes_in_use -= attrXX->length; - -#if 0 - ntfs_log_debug("dst = 0x%02X, src = 0x%02X, len = 0x%02X\n", (dst - (u8*)mft), (src - (u8*)mft), len); - ntfs_log_debug("attr %02X, len = 0x%02X\n", attrXX->type, attrXX->length); - ntfs_log_debug("bytes in use = 0x%02X\n", mft->bytes_in_use); - ntfs_log_debug("\n"); -#endif - - memmove(dst, src, len); - //utils_dump_mem(mft, 0, mft->bytes_in_use, DM_DEFAULTS); ntfs_log_debug("\n"); - - NInoSetDirty(inode); - return 0; -} - -/** - * ntfs_mft_add_attr - Add an attribute to an MFT record - * @inode: - * @type: - * @data: - * @data_len: - * - * Description... - * - * Returns: - */ -ATTR_RECORD * ntfs_mft_add_attr(ntfs_inode *inode, ATTR_TYPES type, u8 *data, int data_len) -{ - MFT_RECORD *mrec; - ATTR_RECORD *attr; - u8 *ptr; - u8 *src; - u8 *dst; - int len; - int attr_size; - - if (!inode) - return NULL; - if (!data) - return NULL; - - ntfs_log_trace ("inode %p, mft %lld, attr 0x%02x, len %d\n", inode, inode->mft_no, type, data_len); - attr_size = ATTR_SIZE(data_len); - - mrec = inode->mrec; - if (!mrec) - return NULL; - - if ((mrec->bytes_in_use + attr_size + 0x18) > mrec->bytes_allocated) { - ntfs_log_debug("attribute is too big to fit in the record\n"); - return NULL; - } - - ptr = (u8*) inode->mrec + mrec->attrs_offset; - while (1) { - attr = (ATTR_RECORD*) ptr; - - if (type < attr->type) - break; - - ptr += attr->length; - } - - //ntfs_log_debug("insert before attr 0x%02X\n", attr->type); - - len = ((u8*) mrec + mrec->bytes_in_use) - ((u8*) attr); - src = (u8*) attr; - dst = src + attr_size + 0x18; - - memmove(dst, src, len); - - src = data; - dst = (u8*) attr + 0x18; - len = data_len; - - // XXX wipe slack space after attr? - - memcpy(dst, src, len); - - mrec->bytes_in_use += attr_size + 0x18; - - memset(attr, 0, 0x18); - *(u32*)((u8*) attr + 0x00) = type; - *(u32*)((u8*) attr + 0x04) = attr_size + 0x18; - *(u16*)((u8*) attr + 0x0E) = mrec->next_attr_instance; - *(u32*)((u8*) attr + 0x10) = data_len; - *(u32*)((u8*) attr + 0x14) = 0x18; - - mrec->next_attr_instance++; - - return attr; -} - -/** - * ntfs_mft_resize_resident - Resize a resident attribute in an MFT record - * @inode: - * @type: - * @name: - * @name_len: - * @data: - * @data_len: - * - * Description... - * - * Returns: - */ -int ntfs_mft_resize_resident(ntfs_inode *inode, ATTR_TYPES type, ntfschar *name, int name_len, u8 *data, int data_len) -{ - int mft_size; - int mft_usage; - int mft_free; - int attr_orig; - int attr_new; - u8 *src; - u8 *dst; - u8 *end; - int len; - ntfs_attr_search_ctx *ctx = NULL; - ATTR_RECORD *arec = NULL; - MFT_RECORD *mrec = NULL; - int res = -1; - - ntfs_log_trace ("\n"); - // XXX only works when attr is in base inode - - if ((!inode) || (!inode->mrec)) - return -1; - if ((!data) || (data_len < 0)) - return -1; - - mrec = inode->mrec; - ntfs_log_debug("inode = %lld\n", MREF(inode->mft_no)); - //utils_dump_mem(mrec, 0, 1024, DM_DEFAULTS); - - mft_size = mrec->bytes_allocated; - mft_usage = mrec->bytes_in_use; - mft_free = mft_size - mft_usage; - - //ntfs_log_debug("mft_size = %d\n", mft_size); - //ntfs_log_debug("mft_usage = %d\n", mft_usage); - //ntfs_log_debug("mft_free = %d\n", mft_free); - //ntfs_log_debug("\n"); - - ctx = ntfs_attr_get_search_ctx(inode, NULL); - if (!ctx) - goto done; - - ntfs_name_print(name, name_len); - ntfs_log_debug(" type = 0x%02x\n", type); - if (ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, 0, NULL, 0, ctx) != 0) - goto done; - - arec = ctx->attr; - - if (arec->non_resident) { - ntfs_log_debug("attribute isn't resident\n"); - goto done; - } - - attr_orig = arec->value_length; - attr_new = data_len; - - ntfs_log_debug("attr orig = %d\n", attr_orig); - ntfs_log_debug("attr new = %d\n", attr_new); - //ntfs_log_debug("\n"); - - if ((attr_new - attr_orig + mft_usage) > mft_size) { - ntfs_log_debug("attribute won't fit into mft record\n"); - goto done; - } - - //ntfs_log_debug("new free space = %d\n", mft_size - (attr_new - attr_orig + mft_usage)); - - src = (u8*)arec + arec->length; - dst = src + (attr_new - attr_orig); - end = (u8*)mrec + mft_usage; - len = end - src; - - //ntfs_log_debug("src = %d\n", src - (u8*)mrec); - //ntfs_log_debug("dst = %d\n", dst - (u8*)mrec); - //ntfs_log_debug("end = %d\n", end - (u8*)mrec); - //ntfs_log_debug("len = %d\n", len); - - if (src != dst) - memmove(dst, src, len); - - memcpy((u8*)arec + arec->value_offset, data, data_len); - - mrec->bytes_in_use += (attr_new - attr_orig); - arec->length += (attr_new - attr_orig); - arec->value_length += (attr_new - attr_orig); - - memset((u8*)mrec + mrec->bytes_in_use, 0, mft_size - mrec->bytes_in_use); - - mft_usage += (attr_new - attr_orig); - //utils_dump_mem(mrec, 0, mft_size, DM_DEFAULTS); - res = 0; -done: - ntfs_attr_put_search_ctx(ctx); - return res; -} - -/** - * ntfs_mft_free_space - Calculate the free space (bytes) in an MFT record - * @dir: - * - * Description... - * - * Returns: - */ -int ntfs_mft_free_space(struct ntfs_dir *dir) -{ - int res = 0; - MFT_RECORD *mft; - - if ((!dir) || (!dir->inode)) - return -1; - - ntfs_log_trace ("\n"); - mft = (MFT_RECORD*) dir->inode->mrec; - - res = mft->bytes_allocated - mft->bytes_in_use; - - return res; -} - -/** - * ntfs_mft_add_index - Add an index (directory) to an MFT record - * @dir: - * - * Description... - * - * Returns: - */ -int ntfs_mft_add_index(struct ntfs_dir *dir) -{ - ntfs_volume *vol; - u8 *buffer = NULL; - ATTR_RECORD *attr = NULL; - struct ntfs_dt *dt = NULL; - INDEX_ENTRY *ie = NULL; - - if (!dir) - return 1; - if (dir->bitmap && dir->ialloc) - return 0; - if (dir->bitmap || dir->ialloc) - return 1; - if (dir->index_size < 512) - return 1; - - ntfs_log_trace ("\n"); - vol = dir->vol; - ntfs_log_debug("add two attrs to "); ntfs_name_print(dir->name, dir->name_len); ntfs_log_debug("\n"); - ntfs_log_debug("index size = %d\n", dir->index_size); - - buffer = malloc(dir->index_size); - if (!buffer) - return 1; - - dt = ntfs_dt_create(dir, dir->index, -1); - if (!dt) - return 1; - - dt->vcn = 0; // New alloc record - - ie = ntfs_ie_copy(dir->index->children[dir->index->child_count-1]); - ie = ntfs_ie_set_vcn(ie, dt->vcn); - - // can't replace ie yet, there may not be room - ntfs_ie_free(ie); - - ntfs_dt_transfer2(dir->index, dt, 0, dir->index->child_count - 1); - - ntfs_log_debug("root has %d children\n", dir->index->child_count); - ntfs_log_debug("node has %d children\n", dt->child_count); - - ntfs_dt_free(dt); - - // create a new dt - // attach dt to dir - // move entries into alloc - // shrink root - - // transfer keys to new node - // hook up root & alloc dts - - // need disk allocation before here - - // Index Allocation - memset(buffer, 0, 128); - attr = ntfs_mft_add_attr(dir->inode, AT_INDEX_ALLOCATION, buffer, 0x48); - - // Bitmap - memset(buffer, 0, 8); - buffer[0] = 0x01; - //ntfs_log_debug("inode = %p\n", dir->inode); - attr = ntfs_mft_add_attr(dir->inode, AT_BITMAP, buffer, 8); - - // attach alloc and bitmap to dir - // need to create ntfs_attr's for them - - // one indx record - // 8 bits of bitmap - - if (0) ntfs_bmp_find_space(NULL, 0, 0); - - //ntfs_log_debug("m1 = %lld\n", vol->mft_zone_start); - //ntfs_log_debug("m2 = %lld\n", vol->mft_zone_end); - //ntfs_log_debug("m3 = %lld\n", vol->mft_zone_pos); - //ntfs_log_debug("z1 = %lld\n", vol->data1_zone_pos); - //ntfs_log_debug("z2 = %lld\n", vol->data2_zone_pos); - - free(buffer); - return 0; -} - - -#endif /* NTFS_RICH */ - -/** - * ntfs_mft_usn_dec - Decrement USN by one - * @mrec: pointer to an mft record - * - * On success return 0 and on error return -1 with errno set. - */ -int ntfs_mft_usn_dec(MFT_RECORD *mrec) -{ - u16 usn, *usnp; - - if (!mrec) { - errno = EINVAL; - return -1; - } - usnp = (u16 *)((char *)mrec + le16_to_cpu(mrec->usa_ofs)); - usn = le16_to_cpup(usnp); - if (usn-- <= 1) - usn = 0xfffe; - *usnp = cpu_to_le16(usn); - - return 0; -} - diff --git a/libntfs/mst.c b/libntfs/mst.c deleted file mode 100644 index 140700ea..00000000 --- a/libntfs/mst.c +++ /dev/null @@ -1,214 +0,0 @@ -/** - * mst.c - Multi sector fixup handling code. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2004 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_ERRNO_H -#include -#endif - -#include "mst.h" - -/** - * ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data - * @b: pointer to the data to deprotect - * @size: size in bytes of @b - * - * Perform the necessary post read multi sector transfer fixups and detect the - * presence of incomplete multi sector transfers. - In that case, overwrite the - * magic of the ntfs record header being processed with "BAAD" (in memory only!) - * and abort processing. - * - * Return 0 on success and -1 on error, with errno set to the error code. The - * following error codes are defined: - * EINVAL Invalid arguments or invalid NTFS record in buffer @b. - * EIO Multi sector transfer error was detected. Magic of the NTFS - * record in @b will have been set to "BAAD". - */ -int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) -{ - u16 usa_ofs, usa_count, usn; - u16 *usa_pos, *data_pos; - - /* Setup the variables. */ - usa_ofs = le16_to_cpu(b->usa_ofs); - /* Decrement usa_count to get number of fixups. */ - usa_count = le16_to_cpu(b->usa_count) - 1; - /* Size and alignment checks. */ - if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || - (u32)(usa_ofs + (usa_count * 2)) > size || - (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { - errno = EINVAL; - return -1; - } - /* Position of usn in update sequence array. */ - usa_pos = (u16*)b + usa_ofs/sizeof(u16); - /* - * The update sequence number which has to be equal to each of the - * u16 values before they are fixed up. Note no need to care for - * endianness since we are comparing and moving data for on disk - * structures which means the data is consistent. - If it is - * consistency the wrong endianness it doesn't make any difference. - */ - usn = *usa_pos; - /* - * Position in protected data of first u16 that needs fixing up. - */ - data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; - /* - * Check for incomplete multi sector transfer(s). - */ - while (usa_count--) { - if (*data_pos != usn) { - /* - * Incomplete multi sector transfer detected! )-: - * Set the magic to "BAAD" and return failure. - * Note that magic_BAAD is already converted to le32. - */ - b->magic = magic_BAAD; - errno = EIO; - return -1; - } - data_pos += NTFS_BLOCK_SIZE/sizeof(u16); - } - /* Re-setup the variables. */ - usa_count = le16_to_cpu(b->usa_count) - 1; - data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; - /* Fixup all sectors. */ - while (usa_count--) { - /* - * Increment position in usa and restore original data from - * the usa into the data buffer. - */ - *data_pos = *(++usa_pos); - /* Increment position in data as well. */ - data_pos += NTFS_BLOCK_SIZE/sizeof(u16); - } - return 0; -} - -/** - * ntfs_mst_pre_write_fixup - apply multi sector transfer protection - * @b: pointer to the data to protect - * @size: size in bytes of @b - * - * Perform the necessary pre write multi sector transfer fixup on the data - * pointer to by @b of @size. - * - * Return 0 if fixups applied successfully or -1 if no fixups were performed - * due to errors. In that case errno i set to the error code (EINVAL). - * - * NOTE: We consider the absence / invalidity of an update sequence array to - * mean error. This means that you have to create a valid update sequence - * array header in the ntfs record before calling this function, otherwise it - * will fail (the header needs to contain the position of the update sequence - * array together with the number of elements in the array). You also need to - * initialise the update sequence number before calling this function - * otherwise a random word will be used (whatever was in the record at that - * position at that time). - */ -int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) -{ - u16 usa_ofs, usa_count, usn; - u16 *usa_pos, *data_pos; - - /* Sanity check + only fixup if it makes sense. */ - if (!b || ntfs_is_baad_record(b->magic) || - ntfs_is_hole_record(b->magic)) { - errno = EINVAL; - return -1; - } - /* Setup the variables. */ - usa_ofs = le16_to_cpu(b->usa_ofs); - /* Decrement usa_count to get number of fixups. */ - usa_count = le16_to_cpu(b->usa_count) - 1; - /* Size and alignment checks. */ - if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || - (u32)(usa_ofs + (usa_count * 2)) > size || - (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { - errno = EINVAL; - return -1; - } - /* Position of usn in update sequence array. */ - usa_pos = (u16*)((u8*)b + usa_ofs); - /* - * Cyclically increment the update sequence number - * (skipping 0 and -1, i.e. 0xffff). - */ - usn = le16_to_cpup(usa_pos) + 1; - if (usn == 0xffff || !usn) - usn = 1; - usn = cpu_to_le16(usn); - *usa_pos = usn; - /* Position in data of first u16 that needs fixing up. */ - data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; - /* Fixup all sectors. */ - while (usa_count--) { - /* - * Increment the position in the usa and save the - * original data from the data buffer into the usa. - */ - *(++usa_pos) = *data_pos; - /* Apply fixup to data. */ - *data_pos = usn; - /* Increment position in data as well. */ - data_pos += NTFS_BLOCK_SIZE/sizeof(u16); - } - return 0; -} - -/** - * ntfs_mst_post_write_fixup - deprotect multi sector transfer protected data - * @b: pointer to the data to deprotect - * - * Perform the necessary post write multi sector transfer fixup, not checking - * for any errors, because we assume we have just used - * ntfs_mst_pre_write_fixup(), thus the data will be fine or we would never - * have gotten here. - */ -void ntfs_mst_post_write_fixup(NTFS_RECORD *b) -{ - u16 *usa_pos, *data_pos; - - u16 usa_ofs = le16_to_cpu(b->usa_ofs); - u16 usa_count = le16_to_cpu(b->usa_count) - 1; - - /* Position of usn in update sequence array. */ - usa_pos = (u16*)b + usa_ofs/sizeof(u16); - - /* Position in protected data of first u16 that needs fixing up. */ - data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; - - /* Fixup all sectors. */ - while (usa_count--) { - /* - * Increment position in usa and restore original data from - * the usa into the data buffer. - */ - *data_pos = *(++usa_pos); - - /* Increment position in data as well. */ - data_pos += NTFS_BLOCK_SIZE/sizeof(u16); - } -} - diff --git a/libntfs/runlist.c b/libntfs/runlist.c deleted file mode 100644 index 130dda88..00000000 --- a/libntfs/runlist.c +++ /dev/null @@ -1,2126 +0,0 @@ -/** - * runlist.c - Run list handling code. Part of the Linux-NTFS project. - * - * Copyright (c) 2002-2005 Anton Altaparmakov - * Copyright (c) 2002-2005 Richard Russon - * Copyright (c) 2004 Yura Pakhuchiy - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDIO_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif - -#include "compat.h" -#include "types.h" -#include "volume.h" -#include "layout.h" -#include "debug.h" -#include "device.h" -#include "logging.h" - -/** - * ntfs_rl_mm - runlist memmove - * @base: - * @dst: - * @src: - * @size: - * - * Description... - * - * Returns: - */ -static __inline__ void ntfs_rl_mm(runlist_element *base, int dst, int src, - int size) -{ - if ((dst != src) && (size > 0)) - memmove(base + dst, base + src, size * sizeof(*base)); -} - -/** - * ntfs_rl_mc - runlist memory copy - * @dstbase: - * @dst: - * @srcbase: - * @src: - * @size: - * - * Description... - * - * Returns: - */ -static __inline__ void ntfs_rl_mc(runlist_element *dstbase, int dst, - runlist_element *srcbase, int src, int size) -{ - if (size > 0) - memcpy(dstbase + dst, srcbase + src, size * sizeof(*dstbase)); -} - -/** - * ntfs_rl_realloc - Reallocate memory for runlists - * @rl: original runlist - * @old_size: number of runlist elements in the original runlist @rl - * @new_size: number of runlist elements we need space for - * - * As the runlists grow, more memory will be required. To prevent large - * numbers of small reallocations of memory, this function returns a 4kiB block - * of memory. - * - * N.B. If the new allocation doesn't require a different number of 4kiB - * blocks in memory, the function will return the original pointer. - * - * On success, return a pointer to the newly allocated, or recycled, memory. - * On error, return NULL with errno set to the error code. - */ -static __inline__ runlist_element *ntfs_rl_realloc(runlist_element *rl, - int old_size, int new_size) -{ - old_size = (old_size * sizeof(runlist_element) + 0xfff) & ~0xfff; - new_size = (new_size * sizeof(runlist_element) + 0xfff) & ~0xfff; - if (old_size == new_size) - return rl; - return realloc(rl, new_size); -} - -/** - * ntfs_rl_are_mergeable - test if two runlists can be joined together - * @dst: original runlist - * @src: new runlist to test for mergeability with @dst - * - * Test if two runlists can be joined together. For this, their VCNs and LCNs - * must be adjacent. - * - * Return: TRUE Success, the runlists can be merged. - * FALSE Failure, the runlists cannot be merged. - */ -static __inline__ BOOL ntfs_rl_are_mergeable(runlist_element *dst, - runlist_element *src) -{ - if (!dst || !src) { - ntfs_log_debug("Eeek. ntfs_rl_are_mergeable() invoked with NULL " - "pointer!\n"); - return FALSE; - } - - /* We can merge unmapped regions even if they are misaligned. */ - if ((dst->lcn == LCN_RL_NOT_MAPPED) && (src->lcn == LCN_RL_NOT_MAPPED)) - return TRUE; - /* If the runs are misaligned, we cannot merge them. */ - if ((dst->vcn + dst->length) != src->vcn) - return FALSE; - /* If both runs are non-sparse and contiguous, we can merge them. */ - if ((dst->lcn >= 0) && (src->lcn >= 0) && - ((dst->lcn + dst->length) == src->lcn)) - return TRUE; - /* If we are merging two holes, we can merge them. */ - if ((dst->lcn == LCN_HOLE) && (src->lcn == LCN_HOLE)) - return TRUE; - /* Cannot merge. */ - return FALSE; -} - -/** - * __ntfs_rl_merge - merge two runlists without testing if they can be merged - * @dst: original, destination runlist - * @src: new runlist to merge with @dst - * - * Merge the two runlists, writing into the destination runlist @dst. The - * caller must make sure the runlists can be merged or this will corrupt the - * destination runlist. - */ -static __inline__ void __ntfs_rl_merge(runlist_element *dst, - runlist_element *src) -{ - dst->length += src->length; -} - -/** - * ntfs_rl_append - append a runlist after a given element - * @dst: original runlist to be worked on - * @dsize: number of elements in @dst (including end marker) - * @src: runlist to be inserted into @dst - * @ssize: number of elements in @src (excluding end marker) - * @loc: append the new runlist @src after this element in @dst - * - * Append the runlist @src after element @loc in @dst. Merge the right end of - * the new runlist, if necessary. Adjust the size of the hole before the - * appended runlist. - * - * On success, return a pointer to the new, combined, runlist. Note, both - * runlists @dst and @src are deallocated before returning so you cannot use - * the pointers for anything any more. (Strictly speaking the returned runlist - * may be the same as @dst but this is irrelevant.) - * - * On error, return NULL, with errno set to the error code. Both runlists are - * left unmodified. - */ -static __inline__ runlist_element *ntfs_rl_append(runlist_element *dst, - int dsize, runlist_element *src, int ssize, int loc) -{ - BOOL right = FALSE; /* Right end of @src needs merging */ - int marker; /* End of the inserted runs */ - - if (!dst || !src) { - ntfs_log_debug("Eeek. ntfs_rl_append() invoked with NULL " - "pointer!\n"); - errno = EINVAL; - return NULL; - } - - /* First, check if the right hand end needs merging. */ - if ((loc + 1) < dsize) - right = ntfs_rl_are_mergeable(src + ssize - 1, dst + loc + 1); - - /* Space required: @dst size + @src size, less one if we merged. */ - dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - right); - if (!dst) - return NULL; - /* - * We are guaranteed to succeed from here so can start modifying the - * original runlists. - */ - - /* First, merge the right hand end, if necessary. */ - if (right) - __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); - - /* marker - First run after the @src runs that have been inserted */ - marker = loc + ssize + 1; - - /* Move the tail of @dst out of the way, then copy in @src. */ - ntfs_rl_mm(dst, marker, loc + 1 + right, dsize - loc - 1 - right); - ntfs_rl_mc(dst, loc + 1, src, 0, ssize); - - /* Adjust the size of the preceding hole. */ - dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; - - /* We may have changed the length of the file, so fix the end marker */ - if (dst[marker].lcn == LCN_ENOENT) - dst[marker].vcn = dst[marker-1].vcn + dst[marker-1].length; - - return dst; -} - -/** - * ntfs_rl_insert - insert a runlist into another - * @dst: original runlist to be worked on - * @dsize: number of elements in @dst (including end marker) - * @src: new runlist to be inserted - * @ssize: number of elements in @src (excluding end marker) - * @loc: insert the new runlist @src before this element in @dst - * - * Insert the runlist @src before element @loc in the runlist @dst. Merge the - * left end of the new runlist, if necessary. Adjust the size of the hole - * after the inserted runlist. - * - * On success, return a pointer to the new, combined, runlist. Note, both - * runlists @dst and @src are deallocated before returning so you cannot use - * the pointers for anything any more. (Strictly speaking the returned runlist - * may be the same as @dst but this is irrelevant.) - * - * On error, return NULL, with errno set to the error code. Both runlists are - * left unmodified. - */ -static __inline__ runlist_element *ntfs_rl_insert(runlist_element *dst, - int dsize, runlist_element *src, int ssize, int loc) -{ - BOOL left = FALSE; /* Left end of @src needs merging */ - BOOL disc = FALSE; /* Discontinuity between @dst and @src */ - int marker; /* End of the inserted runs */ - - if (!dst || !src) { - ntfs_log_debug("Eeek. ntfs_rl_insert() invoked with NULL " - "pointer!\n"); - errno = EINVAL; - return NULL; - } - - /* disc => Discontinuity between the end of @dst and the start of @src. - * This means we might need to insert a "notmapped" run. - */ - if (loc == 0) - disc = (src[0].vcn > 0); - else { - s64 merged_length; - - left = ntfs_rl_are_mergeable(dst + loc - 1, src); - - merged_length = dst[loc - 1].length; - if (left) - merged_length += src->length; - - disc = (src[0].vcn > dst[loc - 1].vcn + merged_length); - } - - /* Space required: @dst size + @src size, less one if we merged, plus - * one if there was a discontinuity. - */ - dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left + disc); - if (!dst) - return NULL; - /* - * We are guaranteed to succeed from here so can start modifying the - * original runlist. - */ - - if (left) - __ntfs_rl_merge(dst + loc - 1, src); - - /* - * marker - First run after the @src runs that have been inserted - * Nominally: marker = @loc + @ssize (location + number of runs in @src) - * If "left", then the first run in @src has been merged with one in @dst. - * If "disc", then @dst and @src don't meet and we need an extra run to fill the gap. - */ - marker = loc + ssize - left + disc; - - /* Move the tail of @dst out of the way, then copy in @src. */ - ntfs_rl_mm(dst, marker, loc, dsize - loc); - ntfs_rl_mc(dst, loc + disc, src, left, ssize - left); - - /* Adjust the VCN of the first run after the insertion ... */ - dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; - /* ... and the length. */ - if (dst[marker].lcn == LCN_HOLE || dst[marker].lcn == LCN_RL_NOT_MAPPED) - dst[marker].length = dst[marker + 1].vcn - dst[marker].vcn; - - /* Writing beyond the end of the file and there's a discontinuity. */ - if (disc) { - if (loc > 0) { - dst[loc].vcn = dst[loc - 1].vcn + dst[loc - 1].length; - dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; - } else { - dst[loc].vcn = 0; - dst[loc].length = dst[loc + 1].vcn; - } - dst[loc].lcn = LCN_RL_NOT_MAPPED; - } - return dst; -} - -/** - * ntfs_rl_replace - overwrite a runlist element with another runlist - * @dst: original runlist to be worked on - * @dsize: number of elements in @dst (including end marker) - * @src: new runlist to be inserted - * @ssize: number of elements in @src (excluding end marker) - * @loc: index in runlist @dst to overwrite with @src - * - * Replace the runlist element @dst at @loc with @src. Merge the left and - * right ends of the inserted runlist, if necessary. - * - * On success, return a pointer to the new, combined, runlist. Note, both - * runlists @dst and @src are deallocated before returning so you cannot use - * the pointers for anything any more. (Strictly speaking the returned runlist - * may be the same as @dst but this is irrelevant.) - * - * On error, return NULL, with errno set to the error code. Both runlists are - * left unmodified. - */ -static __inline__ runlist_element *ntfs_rl_replace(runlist_element *dst, - int dsize, runlist_element *src, int ssize, int loc) -{ - signed delta; - BOOL left = FALSE; /* Left end of @src needs merging */ - BOOL right = FALSE; /* Right end of @src needs merging */ - int tail; /* Start of tail of @dst */ - int marker; /* End of the inserted runs */ - - if (!dst || !src) { - ntfs_log_debug("Eeek. ntfs_rl_replace() invoked with NULL " - "pointer!\n"); - errno = EINVAL; - return NULL; - } - - /* First, see if the left and right ends need merging. */ - if ((loc + 1) < dsize) - right = ntfs_rl_are_mergeable(src + ssize - 1, dst + loc + 1); - if (loc > 0) - left = ntfs_rl_are_mergeable(dst + loc - 1, src); - - /* Allocate some space. We'll need less if the left, right, or both - * ends get merged. The -1 accounts for the run being replaced. - */ - delta = ssize - 1 - left - right; - if (delta > 0) { - dst = ntfs_rl_realloc(dst, dsize, dsize + delta); - if (!dst) - return NULL; - } - /* - * We are guaranteed to succeed from here so can start modifying the - * original runlists. - */ - - /* First, merge the left and right ends, if necessary. */ - if (right) - __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); - if (left) - __ntfs_rl_merge(dst + loc - 1, src); - - /* - * tail - Offset of the tail of @dst - * Nominally: @tail = @loc + 1 (location, skipping the replaced run) - * If "right", then one of @dst's runs is already merged into @src. - */ - tail = loc + right + 1; - - /* - * marker - First run after the @src runs that have been inserted - * Nominally: @marker = @loc + @ssize (location + number of runs in @src) - * If "left", then the first run in @src has been merged with one in @dst. - */ - marker = loc + ssize - left; - - /* Move the tail of @dst out of the way, then copy in @src. */ - ntfs_rl_mm(dst, marker, tail, dsize - tail); - ntfs_rl_mc(dst, loc, src, left, ssize - left); - - /* We may have changed the length of the file, so fix the end marker */ - if (((dsize - tail) > 0) && (dst[marker].lcn == LCN_ENOENT)) - dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; - - return dst; -} - -/** - * ntfs_rl_split - insert a runlist into the centre of a hole - * @dst: original runlist to be worked on - * @dsize: number of elements in @dst (including end marker) - * @src: new runlist to be inserted - * @ssize: number of elements in @src (excluding end marker) - * @loc: index in runlist @dst at which to split and insert @src - * - * Split the runlist @dst at @loc into two and insert @new in between the two - * fragments. No merging of runlists is necessary. Adjust the size of the - * holes either side. - * - * On success, return a pointer to the new, combined, runlist. Note, both - * runlists @dst and @src are deallocated before returning so you cannot use - * the pointers for anything any more. (Strictly speaking the returned runlist - * may be the same as @dst but this is irrelevant.) - * - * On error, return NULL, with errno set to the error code. Both runlists are - * left unmodified. - */ -static __inline__ runlist_element *ntfs_rl_split(runlist_element *dst, - int dsize, runlist_element *src, int ssize, int loc) -{ - if (!dst || !src) { - ntfs_log_debug("Eeek. ntfs_rl_split() invoked with NULL pointer!\n"); - errno = EINVAL; - return NULL; - } - - /* Space required: @dst size + @src size + one new hole. */ - dst = ntfs_rl_realloc(dst, dsize, dsize + ssize + 1); - if (!dst) - return dst; - /* - * We are guaranteed to succeed from here so can start modifying the - * original runlists. - */ - - /* Move the tail of @dst out of the way, then copy in @src. */ - ntfs_rl_mm(dst, loc + 1 + ssize, loc, dsize - loc); - ntfs_rl_mc(dst, loc + 1, src, 0, ssize); - - /* Adjust the size of the holes either size of @src. */ - dst[loc].length = dst[loc+1].vcn - dst[loc].vcn; - dst[loc+ssize+1].vcn = dst[loc+ssize].vcn + dst[loc+ssize].length; - dst[loc+ssize+1].length = dst[loc+ssize+2].vcn - dst[loc+ssize+1].vcn; - - return dst; -} - - -/** - * ntfs_runlists_merge - merge two runlists into one - * @drl: original runlist to be worked on - * @srl: new runlist to be merged into @drl - * - * First we sanity check the two runlists @srl and @drl to make sure that they - * are sensible and can be merged. The runlist @srl must be either after the - * runlist @drl or completely within a hole (or unmapped region) in @drl. - * - * Merging of runlists is necessary in two cases: - * 1. When attribute lists are used and a further extent is being mapped. - * 2. When new clusters are allocated to fill a hole or extend a file. - * - * There are four possible ways @srl can be merged. It can: - * - be inserted at the beginning of a hole, - * - split the hole in two and be inserted between the two fragments, - * - be appended at the end of a hole, or it can - * - replace the whole hole. - * It can also be appended to the end of the runlist, which is just a variant - * of the insert case. - * - * On success, return a pointer to the new, combined, runlist. Note, both - * runlists @drl and @srl are deallocated before returning so you cannot use - * the pointers for anything any more. (Strictly speaking the returned runlist - * may be the same as @dst but this is irrelevant.) - * - * On error, return NULL, with errno set to the error code. Both runlists are - * left unmodified. The following error codes are defined: - * ENOMEM Not enough memory to allocate runlist array. - * EINVAL Invalid parameters were passed in. - * ERANGE The runlists overlap and cannot be merged. - */ -runlist_element *ntfs_runlists_merge(runlist_element *drl, - runlist_element *srl) -{ - int di, si; /* Current index into @[ds]rl. */ - int sstart; /* First index with lcn > LCN_RL_NOT_MAPPED. */ - int dins; /* Index into @drl at which to insert @srl. */ - int dend, send; /* Last index into @[ds]rl. */ - int dfinal, sfinal; /* The last index into @[ds]rl with - lcn >= LCN_HOLE. */ - int marker = 0; - VCN marker_vcn = 0; - - ntfs_log_debug("dst:\n"); - ntfs_debug_runlist_dump(drl); - ntfs_log_debug("src:\n"); - ntfs_debug_runlist_dump(srl); - - /* Check for silly calling... */ - if (!srl) - return drl; - - /* Check for the case where the first mapping is being done now. */ - if (!drl) { - drl = srl; - /* Complete the source runlist if necessary. */ - if (drl[0].vcn) { - /* Scan to the end of the source runlist. */ - for (dend = 0; drl[dend].length; dend++) - ; - dend++; - drl = ntfs_rl_realloc(drl, dend, dend + 1); - if (!drl) - return drl; - /* Insert start element at the front of the runlist. */ - ntfs_rl_mm(drl, 1, 0, dend); - drl[0].vcn = 0; - drl[0].lcn = LCN_RL_NOT_MAPPED; - drl[0].length = drl[1].vcn; - } - goto finished; - } - - si = di = 0; - - /* Skip any unmapped start element(s) in the source runlist. */ - while (srl[si].length && srl[si].lcn < (LCN)LCN_HOLE) - si++; - - /* Can't have an entirely unmapped source runlist. */ - if (!srl[si].length) { - ntfs_log_debug("Eeek! ntfs_runlists_merge() received entirely " - "unmapped source runlist.\n"); - errno = EINVAL; - return NULL; - } - - /* Record the starting points. */ - sstart = si; - - /* - * Skip forward in @drl until we reach the position where @srl needs to - * be inserted. If we reach the end of @drl, @srl just needs to be - * appended to @drl. - */ - for (; drl[di].length; di++) { - if (drl[di].vcn + drl[di].length > srl[sstart].vcn) - break; - } - dins = di; - - /* Sanity check for illegal overlaps. */ - if ((drl[di].vcn == srl[si].vcn) && (drl[di].lcn >= 0) && - (srl[si].lcn >= 0)) { - ntfs_log_debug("Run lists overlap. Cannot merge!\n"); - errno = ERANGE; - return NULL; - } - - /* Scan to the end of both runlists in order to know their sizes. */ - for (send = si; srl[send].length; send++) - ; - for (dend = di; drl[dend].length; dend++) - ; - - if (srl[send].lcn == (LCN)LCN_ENOENT) - marker_vcn = srl[marker = send].vcn; - - /* Scan to the last element with lcn >= LCN_HOLE. */ - for (sfinal = send; sfinal >= 0 && srl[sfinal].lcn < LCN_HOLE; sfinal--) - ; - for (dfinal = dend; dfinal >= 0 && drl[dfinal].lcn < LCN_HOLE; dfinal--) - ; - - { - BOOL start; - BOOL finish; - int ds = dend + 1; /* Number of elements in drl & srl */ - int ss = sfinal - sstart + 1; - - start = ((drl[dins].lcn < LCN_RL_NOT_MAPPED) || /* End of file */ - (drl[dins].vcn == srl[sstart].vcn)); /* Start of hole */ - finish = ((drl[dins].lcn >= LCN_RL_NOT_MAPPED) && /* End of file */ - ((drl[dins].vcn + drl[dins].length) <= /* End of hole */ - (srl[send - 1].vcn + srl[send - 1].length))); - - /* Or we'll lose an end marker */ - if (finish && !drl[dins].length) - ss++; - if (marker && (drl[dins].vcn + drl[dins].length > srl[send - 1].vcn)) - finish = FALSE; - - ntfs_log_debug("dfinal = %i, dend = %i\n", dfinal, dend); - ntfs_log_debug("sstart = %i, sfinal = %i, send = %i\n", sstart, sfinal, send); - ntfs_log_debug("start = %i, finish = %i\n", start, finish); - ntfs_log_debug("ds = %i, ss = %i, dins = %i\n", ds, ss, dins); - - if (start) { - if (finish) - drl = ntfs_rl_replace(drl, ds, srl + sstart, ss, dins); - else - drl = ntfs_rl_insert(drl, ds, srl + sstart, ss, dins); - } else { - if (finish) - drl = ntfs_rl_append(drl, ds, srl + sstart, ss, dins); - else - drl = ntfs_rl_split(drl, ds, srl + sstart, ss, dins); - } - if (!drl) { - ntfs_log_perror("Merge failed"); - return drl; - } - free(srl); - if (marker) { - ntfs_log_debug("Triggering marker code.\n"); - for (ds = dend; drl[ds].length; ds++) - ; - /* We only need to care if @srl ended after @drl. */ - if (drl[ds].vcn <= marker_vcn) { - int slots = 0; - - if (drl[ds].vcn == marker_vcn) { - ntfs_log_debug("Old marker = %lli, replacing with " - "LCN_ENOENT.\n", - (long long)drl[ds].lcn); - drl[ds].lcn = (LCN)LCN_ENOENT; - goto finished; - } - /* - * We need to create an unmapped runlist element in - * @drl or extend an existing one before adding the - * ENOENT terminator. - */ - if (drl[ds].lcn == (LCN)LCN_ENOENT) { - ds--; - slots = 1; - } - if (drl[ds].lcn != (LCN)LCN_RL_NOT_MAPPED) { - /* Add an unmapped runlist element. */ - if (!slots) { - /* FIXME/TODO: We need to have the - * extra memory already! (AIA) - */ - drl = ntfs_rl_realloc(drl, ds, ds + 2); - if (!drl) - goto critical_error; - slots = 2; - } - ds++; - /* Need to set vcn if it isn't set already. */ - if (slots != 1) - drl[ds].vcn = drl[ds - 1].vcn + - drl[ds - 1].length; - drl[ds].lcn = (LCN)LCN_RL_NOT_MAPPED; - /* We now used up a slot. */ - slots--; - } - drl[ds].length = marker_vcn - drl[ds].vcn; - /* Finally add the ENOENT terminator. */ - ds++; - if (!slots) { - /* FIXME/TODO: We need to have the extra - * memory already! (AIA) - */ - drl = ntfs_rl_realloc(drl, ds, ds + 1); - if (!drl) - goto critical_error; - } - drl[ds].vcn = marker_vcn; - drl[ds].lcn = (LCN)LCN_ENOENT; - drl[ds].length = (s64)0; - } - } - } - -finished: - /* The merge was completed successfully. */ - ntfs_log_debug("Merged runlist:\n"); - ntfs_debug_runlist_dump(drl); - return drl; - -critical_error: - /* Critical error! We cannot afford to fail here. */ - ntfs_log_perror("libntfs: Critical error"); - ntfs_log_debug("Forcing segmentation fault!\n"); - marker_vcn = ((runlist*)NULL)->lcn; - return drl; -} - -/** - * ntfs_mapping_pairs_decompress - convert mapping pairs array to runlist - * @vol: ntfs volume on which the attribute resides - * @attr: attribute record whose mapping pairs array to decompress - * @old_rl: optional runlist in which to insert @attr's runlist - * - * Decompress the attribute @attr's mapping pairs array into a runlist. On - * success, return the decompressed runlist. - * - * If @old_rl is not NULL, decompressed runlist is inserted into the - * appropriate place in @old_rl and the resultant, combined runlist is - * returned. The original @old_rl is deallocated. - * - * On error, return NULL with errno set to the error code. @old_rl is left - * unmodified in that case. - * - * The following error codes are defined: - * ENOMEM Not enough memory to allocate runlist array. - * EIO Corrupt runlist. - * EINVAL Invalid parameters were passed in. - * ERANGE The two runlists overlap. - * - * FIXME: For now we take the conceptionally simplest approach of creating the - * new runlist disregarding the already existing one and then splicing the - * two into one, if that is possible (we check for overlap and discard the new - * runlist if overlap present before returning NULL, with errno = ERANGE). - */ -runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, - const ATTR_RECORD *attr, runlist_element *old_rl) -{ - VCN vcn; /* Current vcn. */ - LCN lcn; /* Current lcn. */ - s64 deltaxcn; /* Change in [vl]cn. */ - runlist_element *rl; /* The output runlist. */ - const u8 *buf; /* Current position in mapping pairs array. */ - const u8 *attr_end; /* End of attribute. */ - int err, rlsize; /* Size of runlist buffer. */ - u16 rlpos; /* Current runlist position in units of - runlist_elements. */ - u8 b; /* Current byte offset in buf. */ - - ntfs_log_trace("Entering for attr 0x%x.\n", - (unsigned)le32_to_cpu(attr->type)); - /* Make sure attr exists and is non-resident. */ - if (!attr || !attr->non_resident || - sle64_to_cpu(attr->lowest_vcn) < (VCN)0) { - errno = EINVAL; - return NULL; - } - /* Start at vcn = lowest_vcn and lcn 0. */ - vcn = sle64_to_cpu(attr->lowest_vcn); - lcn = 0; - /* Get start of the mapping pairs array. */ - buf = (const u8*)attr + le16_to_cpu(attr->mapping_pairs_offset); - attr_end = (const u8*)attr + le32_to_cpu(attr->length); - if (buf < (const u8*)attr || buf > attr_end) { - ntfs_log_debug("Corrupt attribute.\n"); - errno = EIO; - return NULL; - } - /* Current position in runlist array. */ - rlpos = 0; - /* Allocate first 4kiB block and set current runlist size to 4kiB. */ - rl = malloc(rlsize = 0x1000); - if (!rl) - return NULL; - /* Insert unmapped starting element if necessary. */ - if (vcn) { - rl->vcn = (VCN)0; - rl->lcn = (LCN)LCN_RL_NOT_MAPPED; - rl->length = vcn; - rlpos++; - } - while (buf < attr_end && *buf) { - /* - * Allocate more memory if needed, including space for the - * not-mapped and terminator elements. - */ - if ((int)((rlpos + 3) * sizeof(*old_rl)) > rlsize) { - runlist_element *rl2; - - rlsize += 0x1000; - rl2 = realloc(rl, rlsize); - if (!rl2) { - int eo = errno; - free(rl); - errno = eo; - return NULL; - } - rl = rl2; - } - /* Enter the current vcn into the current runlist element. */ - rl[rlpos].vcn = vcn; - /* - * Get the change in vcn, i.e. the run length in clusters. - * Doing it this way ensures that we signextend negative values. - * A negative run length doesn't make any sense, but hey, I - * didn't make up the NTFS specs and Windows NT4 treats the run - * length as a signed value so that's how it is... - */ - b = *buf & 0xf; - if (b) { - if (buf + b > attr_end) - goto io_error; - for (deltaxcn = (s8)buf[b--]; b; b--) - deltaxcn = (deltaxcn << 8) + buf[b]; - } else { /* The length entry is compulsory. */ - ntfs_log_debug("Missing length entry in mapping pairs " - "array.\n"); - deltaxcn = (s64)-1; - } - /* - * Assume a negative length to indicate data corruption and - * hence clean-up and return NULL. - */ - if (deltaxcn < 0) { - ntfs_log_debug("Invalid length in mapping pairs array.\n"); - goto err_out; - } - /* - * Enter the current run length into the current runlist - * element. - */ - rl[rlpos].length = deltaxcn; - /* Increment the current vcn by the current run length. */ - vcn += deltaxcn; - /* - * There might be no lcn change at all, as is the case for - * sparse clusters on NTFS 3.0+, in which case we set the lcn - * to LCN_HOLE. - */ - if (!(*buf & 0xf0)) - rl[rlpos].lcn = (LCN)LCN_HOLE; - else { - /* Get the lcn change which really can be negative. */ - u8 b2 = *buf & 0xf; - b = b2 + ((*buf >> 4) & 0xf); - if (buf + b > attr_end) - goto io_error; - for (deltaxcn = (s8)buf[b--]; b > b2; b--) - deltaxcn = (deltaxcn << 8) + buf[b]; - /* Change the current lcn to it's new value. */ - lcn += deltaxcn; -#ifdef DEBUG - /* - * On NTFS 1.2-, apparently can have lcn == -1 to - * indicate a hole. But we haven't verified ourselves - * whether it is really the lcn or the deltaxcn that is - * -1. So if either is found give us a message so we - * can investigate it further! - */ - if (vol->major_ver < 3) { - if (deltaxcn == (LCN)-1) - ntfs_log_debug("lcn delta == -1\n"); - if (lcn == (LCN)-1) - ntfs_log_debug("lcn == -1\n"); - } -#endif - /* Check lcn is not below -1. */ - if (lcn < (LCN)-1) { - ntfs_log_debug("Invalid LCN < -1 in mapping pairs " - "array.\n"); - goto err_out; - } - /* Enter the current lcn into the runlist element. */ - rl[rlpos].lcn = lcn; - } - /* Get to the next runlist element. */ - rlpos++; - /* Increment the buffer position to the next mapping pair. */ - buf += (*buf & 0xf) + ((*buf >> 4) & 0xf) + 1; - } - if (buf >= attr_end) - goto io_error; - /* - * If there is a highest_vcn specified, it must be equal to the final - * vcn in the runlist - 1, or something has gone badly wrong. - */ - deltaxcn = sle64_to_cpu(attr->highest_vcn); - if (deltaxcn && vcn - 1 != deltaxcn) { -mpa_err: - ntfs_log_debug("Corrupt mapping pairs array in non-resident " - "attribute.\n"); - goto err_out; - } - /* Setup not mapped runlist element if this is the base extent. */ - if (!attr->lowest_vcn) { - VCN max_cluster; - - max_cluster = ((sle64_to_cpu(attr->allocated_size) + - vol->cluster_size - 1) >> - vol->cluster_size_bits) - 1; - /* - * A highest_vcn of zero means this is a single extent - * attribute so simply terminate the runlist with LCN_ENOENT). - */ - if (deltaxcn) { - /* - * If there is a difference between the highest_vcn and - * the highest cluster, the runlist is either corrupt - * or, more likely, there are more extents following - * this one. - */ - if (deltaxcn < max_cluster) { - ntfs_log_debug("More extents to follow; deltaxcn = " - "0x%llx, max_cluster = 0x%llx\n", - (long long)deltaxcn, - (long long)max_cluster); - rl[rlpos].vcn = vcn; - vcn += rl[rlpos].length = max_cluster - deltaxcn; - rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED; - rlpos++; - } else if (deltaxcn > max_cluster) { - ntfs_log_debug("Corrupt attribute. deltaxcn = " - "0x%llx, max_cluster = 0x%llx\n", - (long long)deltaxcn, - (long long)max_cluster); - goto mpa_err; - } - } - rl[rlpos].lcn = (LCN)LCN_ENOENT; - } else /* Not the base extent. There may be more extents to follow. */ - rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED; - - /* Setup terminating runlist element. */ - rl[rlpos].vcn = vcn; - rl[rlpos].length = (s64)0; - /* If no existing runlist was specified, we are done. */ - if (!old_rl) { - ntfs_log_debug("Mapping pairs array successfully decompressed:\n"); - ntfs_debug_runlist_dump(rl); - return rl; - } - /* Now combine the new and old runlists checking for overlaps. */ - old_rl = ntfs_runlists_merge(old_rl, rl); - if (old_rl) - return old_rl; - err = errno; - free(rl); - ntfs_log_debug("Failed to merge runlists.\n"); - errno = err; - return NULL; -io_error: - ntfs_log_debug("Corrupt attribute.\n"); -err_out: - free(rl); - errno = EIO; - return NULL; -} - -/** - * ntfs_rl_vcn_to_lcn - convert a vcn into a lcn given a runlist - * @rl: runlist to use for conversion - * @vcn: vcn to convert - * - * Convert the virtual cluster number @vcn of an attribute into a logical - * cluster number (lcn) of a device using the runlist @rl to map vcns to their - * corresponding lcns. - * - * Since lcns must be >= 0, we use negative return values with special meaning: - * - * Return value Meaning / Description - * ================================================== - * -1 = LCN_HOLE Hole / not allocated on disk. - * -2 = LCN_RL_NOT_MAPPED This is part of the runlist which has not been - * inserted into the runlist yet. - * -3 = LCN_ENOENT There is no such vcn in the attribute. - * -4 = LCN_EINVAL Input parameter error. - */ -LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn) -{ - int i; - - if (vcn < (VCN)0) - return (LCN)LCN_EINVAL; - /* - * If rl is NULL, assume that we have found an unmapped runlist. The - * caller can then attempt to map it and fail appropriately if - * necessary. - */ - if (!rl) - return (LCN)LCN_RL_NOT_MAPPED; - - /* Catch out of lower bounds vcn. */ - if (vcn < rl[0].vcn) - return (LCN)LCN_ENOENT; - - for (i = 0; rl[i].length; i++) { - if (vcn < rl[i+1].vcn) { - if (rl[i].lcn >= (LCN)0) - return rl[i].lcn + (vcn - rl[i].vcn); - return rl[i].lcn; - } - } - /* - * The terminator element is setup to the correct value, i.e. one of - * LCN_HOLE, LCN_RL_NOT_MAPPED, or LCN_ENOENT. - */ - if (rl[i].lcn < (LCN)0) - return rl[i].lcn; - /* Just in case... We could replace this with BUG() some day. */ - return (LCN)LCN_ENOENT; -} - -/** - * ntfs_rl_pread - gather read from disk - * @vol: ntfs volume to read from - * @rl: runlist specifying where to read the data from - * @pos: byte position within runlist @rl at which to begin the read - * @count: number of bytes to read - * @b: data buffer into which to read from disk - * - * This function will read @count bytes from the volume @vol to the data buffer - * @b gathering the data as specified by the runlist @rl. The read begins at - * offset @pos into the runlist @rl. - * - * On success, return the number of successfully read bytes. If this number is - * lower than @count this means that the read reached end of file or that an - * error was encountered during the read so that the read is partial. 0 means - * nothing was read (also return 0 when @count is 0). - * - * On error and nothing has been read, return -1 with errno set appropriately - * to the return code of ntfs_pread(), or to EINVAL in case of invalid - * arguments. - * - * NOTE: If we encounter EOF while reading we return EIO because we assume that - * the run list must point to valid locations within the ntfs volume. - */ -s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl, - const s64 pos, s64 count, void *b) -{ - s64 bytes_read, to_read, ofs, total; - int err = EIO; - - if (!vol || !rl || pos < 0 || count < 0) { - errno = EINVAL; - return -1; - } - if (!count) - return count; - /* Seek in @rl to the run containing @pos. */ - for (ofs = 0; rl->length && (ofs + (rl->length << - vol->cluster_size_bits) <= pos); rl++) - ofs += (rl->length << vol->cluster_size_bits); - /* Offset in the run at which to begin reading. */ - ofs = pos - ofs; - for (total = 0LL; count; rl++, ofs = 0) { - if (!rl->length) - goto rl_err_out; - if (rl->lcn < (LCN)0) { - if (rl->lcn != (LCN)LCN_HOLE) - goto rl_err_out; - /* It is a hole. Just fill buffer @b with zeroes. */ - to_read = min(count, (rl->length << - vol->cluster_size_bits) - ofs); - memset(b, 0, to_read); - /* Update counters and proceed with next run. */ - total += to_read; - count -= to_read; - b = (u8*)b + to_read; - continue; - } - /* It is a real lcn, read it from the volume. */ - to_read = min(count, (rl->length << vol->cluster_size_bits) - - ofs); -retry: - bytes_read = ntfs_pread(vol->dev, (rl->lcn << - vol->cluster_size_bits) + ofs, to_read, b); - /* If everything ok, update progress counters and continue. */ - if (bytes_read > 0) { - total += bytes_read; - count -= bytes_read; - b = (u8*)b + bytes_read; - continue; - } - /* If the syscall was interrupted, try again. */ - if (bytes_read == (s64)-1 && errno == EINTR) - goto retry; - if (bytes_read == (s64)-1) - err = errno; - goto rl_err_out; - } - /* Finally, return the number of bytes read. */ - return total; -rl_err_out: - if (total) - return total; - errno = err; - return -1; -} - -/** - * ntfs_rl_pwrite - scatter write to disk - * @vol: ntfs volume to write to - * @rl: runlist specifying where to write the data to - * @pos: byte position within runlist @rl at which to begin the write - * @count: number of bytes to write - * @b: data buffer to write to disk - * - * This function will write @count bytes from data buffer @b to the volume @vol - * scattering the data as specified by the runlist @rl. The write begins at - * offset @pos into the runlist @rl. - * - * On success, return the number of successfully written bytes. If this number - * is lower than @count this means that the write has been interrupted in - * flight or that an error was encountered during the write so that the write - * is partial. 0 means nothing was written (also return 0 when @count is 0). - * - * On error and nothing has been written, return -1 with errno set - * appropriately to the return code of ntfs_pwrite(), or to to EINVAL in case - * of invalid arguments. - */ -s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl, - const s64 pos, s64 count, void *b) -{ - s64 written, to_write, ofs, total; - int err = EIO; - - if (!vol || !rl || pos < 0 || count < 0) { - errno = EINVAL; - return -1; - } - if (!count) - return count; - /* Seek in @rl to the run containing @pos. */ - for (ofs = 0; rl->length && (ofs + (rl->length << - vol->cluster_size_bits) <= pos); rl++) - ofs += (rl->length << vol->cluster_size_bits); - /* Offset in the run at which to begin writing. */ - ofs = pos - ofs; - for (total = 0LL; count; rl++, ofs = 0) { - if (!rl->length) - goto rl_err_out; - if (rl->lcn < (LCN)0) { - s64 t; - int cnt; - - if (rl->lcn != (LCN)LCN_HOLE) - goto rl_err_out; - /* - * It is a hole. Check if the buffer is zero in this - * region and if not abort with error. - */ - to_write = min(count, (rl->length << - vol->cluster_size_bits) - ofs); - written = to_write / sizeof(unsigned long); - for (t = 0; t < written; t++) { - if (((unsigned long*)b)[t]) - goto rl_err_out; - } - cnt = to_write & (sizeof(unsigned long) - 1); - if (cnt) { - int i; - u8 *b2; - - b2 = (u8*)b + (to_write & - ~(sizeof(unsigned long) - 1)); - for (i = 0; i < cnt; i++) { - if (b2[i]) - goto rl_err_out; - } - } - /* - * The buffer region is zero, update progress counters - * and proceed with next run. - */ - total += to_write; - count -= to_write; - b = (u8*)b + to_write; - continue; - } - /* It is a real lcn, write it to the volume. */ - to_write = min(count, (rl->length << vol->cluster_size_bits) - - ofs); -retry: - if (!NVolReadOnly(vol)) - written = ntfs_pwrite(vol->dev, (rl->lcn << - vol->cluster_size_bits) + ofs, - to_write, b); - else - written = to_write; - /* If everything ok, update progress counters and continue. */ - if (written > 0) { - total += written; - count -= written; - b = (u8*)b + written; - continue; - } - /* If the syscall was interrupted, try again. */ - if (written == (s64)-1 && errno == EINTR) - goto retry; - if (written == (s64)-1) - err = errno; - goto rl_err_out; - } - /* Finally, return the number of bytes written. */ - return total; -rl_err_out: - if (total) - return total; - errno = err; - return -1; -} - -/** - * ntfs_get_nr_significant_bytes - get number of bytes needed to store a number - * @n: number for which to get the number of bytes for - * - * Return the number of bytes required to store @n unambiguously as - * a signed number. - * - * This is used in the context of the mapping pairs array to determine how - * many bytes will be needed in the array to store a given logical cluster - * number (lcn) or a specific run length. - * - * Return the number of bytes written. This function cannot fail. - */ -__inline__ int ntfs_get_nr_significant_bytes(const s64 n) -{ - s64 l = n; - int i; - s8 j; - - i = 0; - do { - l >>= 8; - i++; - } while (l != 0LL && l != -1LL); - j = (n >> 8 * (i - 1)) & 0xff; - /* If the sign bit is wrong, we need an extra byte. */ - if ((n < 0LL && j >= 0) || (n > 0LL && j < 0)) - i++; - return i; -} - -/** - * ntfs_get_size_for_mapping_pairs - get bytes needed for mapping pairs array - * @vol: ntfs volume (needed for the ntfs version) - * @rl: runlist for which to determine the size of the mapping pairs - * @start_vcn: vcn at which to start the mapping pairs array - * - * Walk the runlist @rl and calculate the size in bytes of the mapping pairs - * array corresponding to the runlist @rl, starting at vcn @start_vcn. This - * for example allows us to allocate a buffer of the right size when building - * the mapping pairs array. - * - * If @rl is NULL, just return 1 (for the single terminator byte). - * - * Return the calculated size in bytes on success. On error, return -1 with - * errno set to the error code. The following error codes are defined: - * EINVAL - Run list contains unmapped elements. Make sure to only pass - * fully mapped runlists to this function. - * - @start_vcn is invalid. - * EIO - The runlist is corrupt. - */ -int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, - const runlist_element *rl, const VCN start_vcn) -{ - LCN prev_lcn; - int rls; - - if (start_vcn < 0) { - ntfs_log_trace("start_vcn %lld (should be >= 0)\n", - (long long) start_vcn); - errno = EINVAL; - return -1; - } - if (!rl) { - if (start_vcn) { - ntfs_log_trace("rl NULL, start_vcn %lld (should be > 0)\n", - (long long) start_vcn); - errno = EINVAL; - return -1; - } - return 1; - } - /* Skip to runlist element containing @start_vcn. */ - while (rl->length && start_vcn >= rl[1].vcn) - rl++; - if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn) { - errno = EINVAL; - return -1; - } - prev_lcn = 0; - /* Always need the terminating zero byte. */ - rls = 1; - /* Do the first partial run if present. */ - if (start_vcn > rl->vcn) { - s64 delta; - - /* We know rl->length != 0 already. */ - if (rl->length < 0 || rl->lcn < LCN_HOLE) - goto err_out; - delta = start_vcn - rl->vcn; - /* Header byte + length. */ - rls += 1 + ntfs_get_nr_significant_bytes(rl->length - delta); - /* - * If the logical cluster number (lcn) denotes a hole and we - * are on NTFS 3.0+, we don't store it at all, i.e. we need - * zero space. On earlier NTFS versions we just store the lcn. - * Note: this assumes that on NTFS 1.2-, holes are stored with - * an lcn of -1 and not a delta_lcn of -1 (unless both are -1). - */ - if (rl->lcn >= 0 || vol->major_ver < 3) { - prev_lcn = rl->lcn; - if (rl->lcn >= 0) - prev_lcn += delta; - /* Change in lcn. */ - rls += ntfs_get_nr_significant_bytes(prev_lcn); - } - /* Go to next runlist element. */ - rl++; - } - /* Do the full runs. */ - for (; rl->length; rl++) { - if (rl->length < 0 || rl->lcn < LCN_HOLE) - goto err_out; - /* Header byte + length. */ - rls += 1 + ntfs_get_nr_significant_bytes(rl->length); - /* - * If the logical cluster number (lcn) denotes a hole and we - * are on NTFS 3.0+, we don't store it at all, i.e. we need - * zero space. On earlier NTFS versions we just store the lcn. - * Note: this assumes that on NTFS 1.2-, holes are stored with - * an lcn of -1 and not a delta_lcn of -1 (unless both are -1). - */ - if (rl->lcn >= 0 || vol->major_ver < 3) { - /* Change in lcn. */ - rls += ntfs_get_nr_significant_bytes(rl->lcn - - prev_lcn); - prev_lcn = rl->lcn; - } - } - return rls; -err_out: - if (rl->lcn == LCN_RL_NOT_MAPPED) - errno = EINVAL; - else - errno = EIO; - return -1; -} - -/** - * ntfs_write_significant_bytes - write the significant bytes of a number - * @dst: destination buffer to write to - * @dst_max: pointer to last byte of destination buffer for bounds checking - * @n: number whose significant bytes to write - * - * Store in @dst, the minimum bytes of the number @n which are required to - * identify @n unambiguously as a signed number, taking care not to exceed - * @dest_max, the maximum position within @dst to which we are allowed to - * write. - * - * This is used when building the mapping pairs array of a runlist to compress - * a given logical cluster number (lcn) or a specific run length to the minimum - * size possible. - * - * Return the number of bytes written on success. On error, i.e. the - * destination buffer @dst is too small, return -1 with errno set ENOSPC. - */ -__inline__ int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max, - const s64 n) -{ - s64 l = n; - int i; - s8 j; - - i = 0; - do { - if (dst > dst_max) - goto err_out; - *dst++ = l & 0xffLL; - l >>= 8; - i++; - } while (l != 0LL && l != -1LL); - j = (n >> 8 * (i - 1)) & 0xff; - /* If the sign bit is wrong, we need an extra byte. */ - if (n < 0LL && j >= 0) { - if (dst > dst_max) - goto err_out; - i++; - *dst = (u8)-1; - } else if (n > 0LL && j < 0) { - if (dst > dst_max) - goto err_out; - i++; - *dst = 0; - } - return i; -err_out: - errno = ENOSPC; - return -1; -} - -/** - * ntfs_mapping_pairs_build - build the mapping pairs array from a runlist - * @vol: ntfs volume (needed for the ntfs version) - * @dst: destination buffer to which to write the mapping pairs array - * @dst_len: size of destination buffer @dst in bytes - * @rl: runlist for which to build the mapping pairs array - * @start_vcn: vcn at which to start the mapping pairs array - * @stop_vcn: first vcn outside destination buffer on success or ENOSPC error - * - * Create the mapping pairs array from the runlist @rl, starting at vcn - * @start_vcn and save the array in @dst. @dst_len is the size of @dst in - * bytes and it should be at least equal to the value obtained by calling - * ntfs_get_size_for_mapping_pairs(). - * - * If @rl is NULL, just write a single terminator byte to @dst. - * - * On success or ENOSPC error, if @stop_vcn is not NULL, *@stop_vcn is set to - * the first vcn outside the destination buffer. Note that on error @dst has - * been filled with all the mapping pairs that will fit, thus it can be treated - * as partial success, in that a new attribute extent needs to be created or the - * next extent has to be used and the mapping pairs build has to be continued - * with @start_vcn set to *@stop_vcn. - * - * Return 0 on success. On error, return -1 with errno set to the error code. - * The following error codes are defined: - * EINVAL - Run list contains unmapped elements. Make sure to only pass - * fully mapped runlists to this function. - * - @start_vcn is invalid. - * EIO - The runlist is corrupt. - * ENOSPC - The destination buffer is too small. - */ -int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst, - const int dst_len, const runlist_element *rl, - const VCN start_vcn, VCN *const stop_vcn) -{ - LCN prev_lcn; - u8 *dst_max, *dst_next; - s8 len_len, lcn_len; - - if (start_vcn < 0) - goto val_err; - if (!rl) { - if (start_vcn) - goto val_err; - if (stop_vcn) - *stop_vcn = 0; - if (dst_len < 1) { - errno = ENOSPC; - return -1; - } - /* Terminator byte. */ - *dst = 0; - return 0; - } - /* Skip to runlist element containing @start_vcn. */ - while (rl->length && start_vcn >= rl[1].vcn) - rl++; - if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn) - goto val_err; - /* - * @dst_max is used for bounds checking in - * ntfs_write_significant_bytes(). - */ - dst_max = dst + dst_len - 1; - prev_lcn = 0; - /* Do the first partial run if present. */ - if (start_vcn > rl->vcn) { - s64 delta; - - /* We know rl->length != 0 already. */ - if (rl->length < 0 || rl->lcn < LCN_HOLE) - goto err_out; - delta = start_vcn - rl->vcn; - /* Write length. */ - len_len = ntfs_write_significant_bytes(dst + 1, dst_max, - rl->length - delta); - if (len_len < 0) - goto size_err; - /* - * If the logical cluster number (lcn) denotes a hole and we - * are on NTFS 3.0+, we don't store it at all, i.e. we need - * zero space. On earlier NTFS versions we just write the lcn - * change. FIXME: Do we need to write the lcn change or just - * the lcn in that case? Not sure as I have never seen this - * case on NT4. - We assume that we just need to write the lcn - * change until someone tells us otherwise... (AIA) - */ - if (rl->lcn >= 0 || vol->major_ver < 3) { - prev_lcn = rl->lcn; - if (rl->lcn >= 0) - prev_lcn += delta; - /* Write change in lcn. */ - lcn_len = ntfs_write_significant_bytes(dst + 1 + - len_len, dst_max, prev_lcn); - if (lcn_len < 0) - goto size_err; - } else - lcn_len = 0; - dst_next = dst + len_len + lcn_len + 1; - if (dst_next > dst_max) - goto size_err; - /* Update header byte. */ - *dst = lcn_len << 4 | len_len; - /* Position at next mapping pairs array element. */ - dst = dst_next; - /* Go to next runlist element. */ - rl++; - } - /* Do the full runs. */ - for (; rl->length; rl++) { - if (rl->length < 0 || rl->lcn < LCN_HOLE) - goto err_out; - /* Write length. */ - len_len = ntfs_write_significant_bytes(dst + 1, dst_max, - rl->length); - if (len_len < 0) - goto size_err; - /* - * If the logical cluster number (lcn) denotes a hole and we - * are on NTFS 3.0+, we don't store it at all, i.e. we need - * zero space. On earlier NTFS versions we just write the lcn - * change. FIXME: Do we need to write the lcn change or just - * the lcn in that case? Not sure as I have never seen this - * case on NT4. - We assume that we just need to write the lcn - * change until someone tells us otherwise... (AIA) - */ - if (rl->lcn >= 0 || vol->major_ver < 3) { - /* Write change in lcn. */ - lcn_len = ntfs_write_significant_bytes(dst + 1 + - len_len, dst_max, rl->lcn - prev_lcn); - if (lcn_len < 0) - goto size_err; - prev_lcn = rl->lcn; - } else - lcn_len = 0; - dst_next = dst + len_len + lcn_len + 1; - if (dst_next > dst_max) - goto size_err; - /* Update header byte. */ - *dst = lcn_len << 4 | len_len; - /* Position at next mapping pairs array element. */ - dst += 1 + len_len + lcn_len; - } - /* Set stop vcn. */ - if (stop_vcn) - *stop_vcn = rl->vcn; - /* Add terminator byte. */ - *dst = 0; - return 0; -size_err: - /* Set stop vcn. */ - if (stop_vcn) - *stop_vcn = rl->vcn; - /* Add terminator byte. */ - *dst = 0; - errno = ENOSPC; - return -1; -val_err: - errno = EINVAL; - return -1; -err_out: - if (rl->lcn == LCN_RL_NOT_MAPPED) - errno = EINVAL; - else - errno = EIO; - return -1; -} - -/** - * ntfs_rl_truncate - truncate a runlist starting at a specified vcn - * @arl: address of runlist to truncate - * @start_vcn: first vcn which should be cut off - * - * Truncate the runlist *@arl starting at vcn @start_vcn as well as the memory - * buffer holding the runlist. - * - * Return 0 on success and -1 on error with errno set to the error code. - * - * NOTE: @arl is the address of the runlist. We need the address so we can - * modify the pointer to the runlist with the new, reallocated memory buffer. - */ -int ntfs_rl_truncate(runlist **arl, const VCN start_vcn) -{ - runlist *rl; - BOOL is_end; - - if (!arl || !*arl) { - errno = EINVAL; - return -1; - } - rl = *arl; - if (start_vcn < rl->vcn) { - // FIXME: Eeek! BUG() - ntfs_log_trace("Eeek! start_vcn lies outside front of runlist! " - "Aborting.\n"); - errno = EIO; - return -1; - } - /* Find the starting vcn in the run list. */ - while (rl->length) { - if (start_vcn < rl[1].vcn) - break; - rl++; - } - if (!rl->length) { - // FIXME: Weird, probably a BUG()! - ntfs_log_trace("Weird! Asking to truncate already truncated " - "runlist?!? Abort.\n"); - errno = EIO; - return -1; - } - if (start_vcn < rl->vcn) { - // FIXME: Eeek! BUG() - ntfs_log_trace("Eeek! start_vcn < rl->vcn! Aborting.\n"); - errno = EIO; - return -1; - } - if (rl->length) { - is_end = FALSE; - /* Truncate the run. */ - rl->length = start_vcn - rl->vcn; - /* - * If a run was partially truncated, make the following runlist - * element a terminator instead of the truncated runlist - * element itself. - */ - if (rl->length) { - ++rl; - if (!rl->length) - is_end = TRUE; - rl->vcn = start_vcn; - rl->length = 0; - } - } else - is_end = TRUE; - rl->lcn = (LCN)LCN_ENOENT; - /* Reallocate memory if necessary. */ - if (!is_end) { - size_t new_size = (rl - *arl + 1) * sizeof(runlist_element); - rl = realloc(*arl, new_size); - if (rl) - *arl = rl; - else if (!new_size) - *arl = NULL; - else { - // FIXME: Eeek! - ntfs_log_trace("Eeek! Failed to reallocate runlist buffer! " - "Continuing regardless and returning success.\n"); - } - } - /* Done! */ - return 0; -} - -/** - * ntfs_rl_sparse - check whether runlist have sparse regions or not. - * @rl: runlist to check - * - * Return 1 if have, 0 if not, -1 on error with errno set to the error code. - */ -int ntfs_rl_sparse(runlist *rl) -{ - runlist *rlc; - - if (!rl) { - ntfs_log_trace("Invalid argument passed.\n"); - errno = EINVAL; - return -1; - } - - for (rlc = rl; rlc->length; rlc++) - if (rlc->lcn < 0) { - if (rlc->lcn != LCN_HOLE) { - ntfs_log_trace("Received unmapped runlist.\n"); - errno = EINVAL; - return -1; - } - return 1; - } - return 0; -} - -/** - * ntfs_rl_get_compressed_size - calculate length of non sparse regions - * @vol: ntfs volume (need for cluster size) - * @rl: runlist to calculate for - * - * Return compressed size or -1 on error with errno set to the error code. - */ -s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl) -{ - runlist *rlc; - s64 ret = 0; - - if (!rl) { - ntfs_log_trace("Invalid argument passed.\n"); - errno = EINVAL; - return -1; - } - - for (rlc = rl; rlc->length; rlc++) { - if (rlc->lcn < 0) { - if (rlc->lcn != LCN_HOLE) { - ntfs_log_trace("Received unmapped runlist.\n"); - errno = EINVAL; - return -1; - } - } else - ret += rlc->length; - } - return ret << vol->cluster_size_bits; -} - - -#ifdef NTFS_TEST -/** - * test_rl_helper - */ -#define MKRL(R,V,L,S) \ - (R)->vcn = V; \ - (R)->lcn = L; \ - (R)->length = S; -/* -} -*/ -/** - * test_rl_dump_runlist - Runlist test: Display the contents of a runlist - * @rl: - * - * Description... - * - * Returns: - */ -static void test_rl_dump_runlist(const runlist_element *rl) -{ - int abbr = 0; /* abbreviate long lists */ - int len = 0; - int i; - const char *lcn_str[5] = { "HOLE", "NOTMAP", "ENOENT", "XXXX" }; - - if (!rl) { - printf(" Run list not present.\n"); - return; - } - - if (abbr) - for (len = 0; rl[len].length; len++) ; - - printf(" VCN LCN len\n"); - for (i = 0; ; i++, rl++) { - LCN lcn = rl->lcn; - - if ((abbr) && (len > 20)) { - if (i == 4) - printf(" ...\n"); - if ((i > 3) && (i < (len - 3))) - continue; - } - - if (lcn < (LCN)0) { - int ind = -lcn - 1; - - if (ind > -LCN_ENOENT - 1) - ind = 3; - printf("%8lld %8s %8lld\n", - rl->vcn, lcn_str[ind], rl->length); - } else - printf("%8lld %8lld %8lld\n", - rl->vcn, rl->lcn, rl->length); - if (!rl->length) - break; - } - if ((abbr) && (len > 20)) - printf(" (%d entries)\n", len+1); - printf("\n"); -} - -/** - * test_rl_runlists_merge - Runlist test: Merge two runlists - * @drl: - * @srl: - * - * Description... - * - * Returns: - */ -static runlist_element * test_rl_runlists_merge(runlist_element *drl, runlist_element *srl) -{ - runlist_element *res = NULL; - - printf("dst:\n"); - test_rl_dump_runlist(drl); - printf("src:\n"); - test_rl_dump_runlist(srl); - - res = ntfs_runlists_merge(drl, srl); - - printf("res:\n"); - test_rl_dump_runlist(res); - - return res; -} - -/** - * test_rl_read_buffer - Runlist test: Read a file containing a runlist - * @file: - * @buf: - * @bufsize: - * - * Description... - * - * Returns: - */ -static int test_rl_read_buffer(const char *file, u8 *buf, int bufsize) -{ - FILE *fptr; - - fptr = fopen(file, "r"); - if (!fptr) { - printf("open %s\n", file); - return 0; - } - - if (fread(buf, bufsize, 1, fptr) == 99) { - printf("read %s\n", file); - return 0; - } - - fclose(fptr); - return 1; -} - -/** - * test_rl_pure_src - Runlist test: Complicate the simple tests a little - * @contig: - * @multi: - * @vcn: - * @len: - * - * Description... - * - * Returns: - */ -static runlist_element * test_rl_pure_src(BOOL contig, BOOL multi, int vcn, int len) -{ - runlist_element *result; - int fudge; - - if (contig) - fudge = 0; - else - fudge = 999; - - result = malloc(4096); - if (multi) { - MKRL(result+0, vcn + (0*len/4), fudge + vcn + 1000 + (0*len/4), len / 4) - MKRL(result+1, vcn + (1*len/4), fudge + vcn + 1000 + (1*len/4), len / 4) - MKRL(result+2, vcn + (2*len/4), fudge + vcn + 1000 + (2*len/4), len / 4) - MKRL(result+3, vcn + (3*len/4), fudge + vcn + 1000 + (3*len/4), len / 4) - MKRL(result+4, vcn + (4*len/4), LCN_RL_NOT_MAPPED, 0) - } else { - MKRL(result+0, vcn, fudge + vcn + 1000, len) - MKRL(result+1, vcn + len, LCN_RL_NOT_MAPPED, 0) - } - return result; -} - -/** - * test_rl_pure_test - Runlist test: Perform tests using simple runlists - * @test: - * @contig: - * @multi: - * @vcn: - * @len: - * @file: - * @size: - * - * Description... - * - * Returns: - */ -static void test_rl_pure_test(int test, BOOL contig, BOOL multi, int vcn, int len, runlist_element *file, int size) -{ - runlist_element *src; - runlist_element *dst; - runlist_element *res; - - src = test_rl_pure_src(contig, multi, vcn, len); - dst = malloc(4096); - - memcpy(dst, file, size); - - printf("Test %2d ----------\n", test); - res = test_rl_runlists_merge(dst, src); - - free(res); -} - -/** - * test_rl_pure - Runlist test: Create tests using simple runlists - * @contig: - * @multi: - * - * Description... - * - * Returns: - */ -static void test_rl_pure(char *contig, char *multi) -{ - /* VCN, LCN, len */ - static runlist_element file1[] = { - { 0, -1, 100 }, /* HOLE */ - { 100, 1100, 100 }, /* DATA */ - { 200, -1, 100 }, /* HOLE */ - { 300, 1300, 100 }, /* DATA */ - { 400, -1, 100 }, /* HOLE */ - { 500, -3, 0 } /* NOENT */ - }; - static runlist_element file2[] = { - { 0, 1000, 100 }, /* DATA */ - { 100, -1, 100 }, /* HOLE */ - { 200, -3, 0 } /* NOENT */ - }; - static runlist_element file3[] = { - { 0, 1000, 100 }, /* DATA */ - { 100, -3, 0 } /* NOENT */ - }; - static runlist_element file4[] = { - { 0, -3, 0 } /* NOENT */ - }; - static runlist_element file5[] = { - { 0, -2, 100 }, /* NOTMAP */ - { 100, 1100, 100 }, /* DATA */ - { 200, -2, 100 }, /* NOTMAP */ - { 300, 1300, 100 }, /* DATA */ - { 400, -2, 100 }, /* NOTMAP */ - { 500, -3, 0 } /* NOENT */ - }; - static runlist_element file6[] = { - { 0, 1000, 100 }, /* DATA */ - { 100, -2, 100 }, /* NOTMAP */ - { 200, -3, 0 } /* NOENT */ - }; - BOOL c, m; - - if (strcmp(contig, "contig") == 0) - c = TRUE; - else if (strcmp(contig, "noncontig") == 0) - c = FALSE; - else { - printf("rl pure [contig|noncontig] [single|multi]\n"); - return; - } - if (strcmp(multi, "multi") == 0) - m = TRUE; - else if (strcmp(multi, "single") == 0) - m = FALSE; - else { - printf("rl pure [contig|noncontig] [single|multi]\n"); - return; - } - - test_rl_pure_test(1, c, m, 0, 40, file1, sizeof(file1)); - test_rl_pure_test(2, c, m, 40, 40, file1, sizeof(file1)); - test_rl_pure_test(3, c, m, 60, 40, file1, sizeof(file1)); - test_rl_pure_test(4, c, m, 0, 100, file1, sizeof(file1)); - test_rl_pure_test(5, c, m, 200, 40, file1, sizeof(file1)); - test_rl_pure_test(6, c, m, 240, 40, file1, sizeof(file1)); - test_rl_pure_test(7, c, m, 260, 40, file1, sizeof(file1)); - test_rl_pure_test(8, c, m, 200, 100, file1, sizeof(file1)); - test_rl_pure_test(9, c, m, 400, 40, file1, sizeof(file1)); - test_rl_pure_test(10, c, m, 440, 40, file1, sizeof(file1)); - test_rl_pure_test(11, c, m, 460, 40, file1, sizeof(file1)); - test_rl_pure_test(12, c, m, 400, 100, file1, sizeof(file1)); - test_rl_pure_test(13, c, m, 160, 100, file2, sizeof(file2)); - test_rl_pure_test(14, c, m, 100, 140, file2, sizeof(file2)); - test_rl_pure_test(15, c, m, 200, 40, file2, sizeof(file2)); - test_rl_pure_test(16, c, m, 240, 40, file2, sizeof(file2)); - test_rl_pure_test(17, c, m, 100, 40, file3, sizeof(file3)); - test_rl_pure_test(18, c, m, 140, 40, file3, sizeof(file3)); - test_rl_pure_test(19, c, m, 0, 40, file4, sizeof(file4)); - test_rl_pure_test(20, c, m, 40, 40, file4, sizeof(file4)); - test_rl_pure_test(21, c, m, 0, 40, file5, sizeof(file5)); - test_rl_pure_test(22, c, m, 40, 40, file5, sizeof(file5)); - test_rl_pure_test(23, c, m, 60, 40, file5, sizeof(file5)); - test_rl_pure_test(24, c, m, 0, 100, file5, sizeof(file5)); - test_rl_pure_test(25, c, m, 200, 40, file5, sizeof(file5)); - test_rl_pure_test(26, c, m, 240, 40, file5, sizeof(file5)); - test_rl_pure_test(27, c, m, 260, 40, file5, sizeof(file5)); - test_rl_pure_test(28, c, m, 200, 100, file5, sizeof(file5)); - test_rl_pure_test(29, c, m, 400, 40, file5, sizeof(file5)); - test_rl_pure_test(30, c, m, 440, 40, file5, sizeof(file5)); - test_rl_pure_test(31, c, m, 460, 40, file5, sizeof(file5)); - test_rl_pure_test(32, c, m, 400, 100, file5, sizeof(file5)); - test_rl_pure_test(33, c, m, 160, 100, file6, sizeof(file6)); - test_rl_pure_test(34, c, m, 100, 140, file6, sizeof(file6)); -} - -/** - * test_rl_zero - Runlist test: Merge a zero-length runlist - * - * Description... - * - * Returns: - */ -static void test_rl_zero(void) -{ - runlist_element *jim = NULL; - runlist_element *bob = NULL; - - bob = calloc(3, sizeof(runlist_element)); - if (!bob) - return; - - MKRL(bob+0, 10, 99, 5) - MKRL(bob+1, 15, LCN_RL_NOT_MAPPED, 0) - - jim = test_rl_runlists_merge(jim, bob); - if (!jim) - return; - - free(jim); -} - -/** - * test_rl_frag_combine - Runlist test: Perform tests using fragmented files - * @vol: - * @attr1: - * @attr2: - * @attr3: - * - * Description... - * - * Returns: - */ -static void test_rl_frag_combine(ntfs_volume *vol, ATTR_RECORD *attr1, ATTR_RECORD *attr2, ATTR_RECORD *attr3) -{ - runlist_element *run1; - runlist_element *run2; - runlist_element *run3; - - run1 = ntfs_mapping_pairs_decompress(vol, attr1, NULL); - if (!run1) - return; - - run2 = ntfs_mapping_pairs_decompress(vol, attr2, NULL); - if (!run2) - return; - - run1 = test_rl_runlists_merge(run1, run2); - - run3 = ntfs_mapping_pairs_decompress(vol, attr3, NULL); - if (!run3) - return; - - run1 = test_rl_runlists_merge(run1, run3); - - free(run1); -} - -/** - * test_rl_frag - Runlist test: Create tests using very fragmented files - * @test: - * - * Description... - * - * Returns: - */ -static void test_rl_frag(char *test) -{ - ntfs_volume vol; - ATTR_RECORD *attr1 = malloc(1024); - ATTR_RECORD *attr2 = malloc(1024); - ATTR_RECORD *attr3 = malloc(1024); - - if (!attr1 || !attr2 || !attr3) - goto out; - - vol.sb = NULL; - vol.sector_size_bits = 9; - vol.cluster_size = 2048; - vol.cluster_size_bits = 11; - vol.major_ver = 3; - - if (!test_rl_read_buffer("runlist-data/attr1.bin", (u8*) attr1, 1024)) - goto out; - if (!test_rl_read_buffer("runlist-data/attr2.bin", (u8*) attr2, 1024)) - goto out; - if (!test_rl_read_buffer("runlist-data/attr3.bin", (u8*) attr3, 1024)) - goto out; - - if (strcmp(test, "123") == 0) test_rl_frag_combine(&vol, attr1, attr2, attr3); - else if (strcmp(test, "132") == 0) test_rl_frag_combine(&vol, attr1, attr3, attr2); - else if (strcmp(test, "213") == 0) test_rl_frag_combine(&vol, attr2, attr1, attr3); - else if (strcmp(test, "231") == 0) test_rl_frag_combine(&vol, attr2, attr3, attr1); - else if (strcmp(test, "312") == 0) test_rl_frag_combine(&vol, attr3, attr1, attr2); - else if (strcmp(test, "321") == 0) test_rl_frag_combine(&vol, attr3, attr2, attr1); - else - printf("Frag: No such test '%s'\n", test); - -out: - free(attr1); - free(attr2); - free(attr3); -} - -/** - * test_rl_main - Runlist test: Program start (main) - * @argc: - * @argv: - * - * Description... - * - * Returns: - */ -int test_rl_main(int argc, char *argv[]) -{ - if ((argc == 2) && (strcmp(argv[1], "zero") == 0)) test_rl_zero(); - else if ((argc == 3) && (strcmp(argv[1], "frag") == 0)) test_rl_frag(argv[2]); - else if ((argc == 4) && (strcmp(argv[1], "pure") == 0)) test_rl_pure(argv[2], argv[3]); - else - printf("rl [zero|frag|pure] {args}\n"); - - return 0; -} - -#endif - diff --git a/libntfs/security.c b/libntfs/security.c deleted file mode 100644 index 62f9bedd..00000000 --- a/libntfs/security.c +++ /dev/null @@ -1,272 +0,0 @@ -/** - * security.c - Handling security/ACLs in NTFS. Part of the Linux-NTFS project. - * - * Copyright (c) 2004 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDIO_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif - -#include "types.h" -#include "layout.h" -#include "security.h" - -/* - * The zero GUID. - */ -static const GUID __zero_guid = { const_cpu_to_le32(0), const_cpu_to_le16(0), - const_cpu_to_le16(0), { 0, 0, 0, 0, 0, 0, 0, 0 } }; -const GUID *const zero_guid = &__zero_guid; - -/** - * ntfs_guid_is_zero - check if a GUID is zero - * @guid: [IN] guid to check - * - * Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID - * and FALSE otherwise. - */ -BOOL ntfs_guid_is_zero(const GUID *guid) -{ - return (memcmp(guid, zero_guid, sizeof(*zero_guid))); -} - -/** - * ntfs_guid_to_mbs - convert a GUID to a multi byte string - * @guid: [IN] guid to convert - * @guid_str: [OUT] string in which to return the GUID (optional) - * - * Convert the GUID pointed to by @guid to a multi byte string of the form - * "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX". Therefore, @guid_str (if not NULL) - * needs to be able to store at least 37 bytes. - * - * If @guid_str is not NULL it will contain the converted GUID on return. If - * it is NULL a string will be allocated and this will be returned. The caller - * is responsible for free()ing the string in that case. - * - * On success return the converted string and on failure return NULL with errno - * set to the error code. - */ -char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str) -{ - char *_guid_str; - int res; - - if (!guid) { - errno = EINVAL; - return NULL; - } - _guid_str = guid_str; - if (!_guid_str) { - _guid_str = malloc(37); - if (!_guid_str) - return _guid_str; - } - res = snprintf(_guid_str, 37, - "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", - (unsigned int)le32_to_cpu(guid->data1), - le16_to_cpu(guid->data2), le16_to_cpu(guid->data3), - guid->data4[0], guid->data4[1], - guid->data4[2], guid->data4[3], guid->data4[4], - guid->data4[5], guid->data4[6], guid->data4[7]); - if (res == 36) - return _guid_str; - if (!guid_str) - free(_guid_str); - errno = EINVAL; - return NULL; -} - -/** - * ntfs_sid_to_mbs_size - determine maximum size for the string of a SID - * @sid: [IN] SID for which to determine the maximum string size - * - * Determine the maximum multi byte string size in bytes which is needed to - * store the standard textual representation of the SID pointed to by @sid. - * See ntfs_sid_to_mbs(), below. - * - * On success return the maximum number of bytes needed to store the multi byte - * string and on failure return -1 with errno set to the error code. - */ -int ntfs_sid_to_mbs_size(const SID *sid) -{ - int size, i; - - if (!ntfs_sid_is_valid(sid)) { - errno = EINVAL; - return -1; - } - /* Start with "S-". */ - size = 2; - /* - * Add the SID_REVISION. Hopefully the compiler will optimize this - * away as SID_REVISION is a constant. - */ - for (i = SID_REVISION; i > 0; i /= 10) - size++; - /* Add the "-". */ - size++; - /* - * Add the identifier authority. If it needs to be in decimal, the - * maximum is 2^32-1 = 4294967295 = 10 characters. If it needs to be - * in hexadecimal, then maximum is 0x665544332211 = 14 characters. - */ - if (!sid->identifier_authority.high_part) - size += 10; - else - size += 14; - /* - * Finally, add the sub authorities. For each we have a "-" followed - * by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters. - */ - size += (1 + 10) * sid->sub_authority_count; - /* We need the zero byte at the end, too. */ - size++; - return size * sizeof(char); -} - -/** - * ntfs_sid_to_mbs - convert a SID to a multi byte string - * @sid: [IN] SID to convert - * @sid_str: [OUT] string in which to return the SID (optional) - * @sid_str_size: [IN] size in bytes of @sid_str - * - * Convert the SID pointed to by @sid to its standard textual representation. - * @sid_str (if not NULL) needs to be able to store at least - * ntfs_sid_to_mbs_size() bytes. @sid_str_size is the size in bytes of - * @sid_str if @sid_str is not NULL. - * - * The standard textual representation of the SID is of the form: - * S-R-I-S-S... - * Where: - * - The first "S" is the literal character 'S' identifying the following - * digits as a SID. - * - R is the revision level of the SID expressed as a sequence of digits - * in decimal. - * - I is the 48-bit identifier_authority, expressed as digits in decimal, - * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32. - * - S... is one or more sub_authority values, expressed as digits in - * decimal. - * - * If @sid_str is not NULL it will contain the converted SUID on return. If it - * is NULL a string will be allocated and this will be returned. The caller is - * responsible for free()ing the string in that case. - * - * On success return the converted string and on failure return NULL with errno - * set to the error code. - */ -char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size) -{ - u64 u; - char *s; - int i, j, cnt; - - /* - * No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will - * check @sid, too. 8 is the minimum SID string size. - */ - if (sid_str && (sid_str_size < 8 || !ntfs_sid_is_valid(sid))) { - errno = EINVAL; - return NULL; - } - /* Allocate string if not provided. */ - if (!sid_str) { - cnt = ntfs_sid_to_mbs_size(sid); - if (cnt < 0) - return NULL; - s = malloc(cnt); - if (!s) - return s; - sid_str = s; - /* So we know we allocated it. */ - sid_str_size = 0; - } else { - s = sid_str; - cnt = sid_str_size; - } - /* Start with "S-R-". */ - i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision); - if (i < 0 || i >= cnt) - goto err_out; - s += i; - cnt -= i; - /* Add the identifier authority. */ - for (u = i = 0, j = 40; i < 6; i++, j -= 8) - u += (u64)sid->identifier_authority.value[i] << j; - if (!sid->identifier_authority.high_part) - i = snprintf(s, cnt, "%lu", (unsigned long)u); - else - i = snprintf(s, cnt, "0x%llx", (unsigned long long)u); - if (i < 0 || i >= cnt) - goto err_out; - s += i; - cnt -= i; - /* Finally, add the sub authorities. */ - for (j = 0; j < sid->sub_authority_count; j++) { - i = snprintf(s, cnt, "-%u", (unsigned int) - le32_to_cpu(sid->sub_authority[j])); - if (i < 0 || i >= cnt) - goto err_out; - s += i; - cnt -= i; - } - return sid_str; -err_out: - if (i >= cnt) - i = EMSGSIZE; - else - i = errno; - if (!sid_str_size) - free(sid_str); - errno = i; - return NULL; -} - -/** - * ntfs_generate_guid - generatates a random current guid. - * @guid: [OUT] pointer to a GUID struct to hold the generated guid. - * - * perhaps not a very good random number generator though... - */ -void ntfs_generate_guid(GUID *guid) -{ - unsigned int i; - u8 *p = (u8 *)guid; - - for (i = 0; i < sizeof(GUID); i++) { - p[i] = (u8)(random() & 0xFF); - if (i == 7) - p[7] = (p[7] & 0x0F) | 0x40; - if (i == 8) - p[8] = (p[8] & 0x3F) | 0x80; - } -} - diff --git a/libntfs/unistr.c b/libntfs/unistr.c deleted file mode 100644 index 906a850d..00000000 --- a/libntfs/unistr.c +++ /dev/null @@ -1,755 +0,0 @@ -/** - * unistr.c - Unicode string handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2004 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDIO_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_WCHAR_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif - -#include "attrib.h" -#include "types.h" -#include "unistr.h" -#include "debug.h" -#include "logging.h" - -/* - * IMPORTANT - * ========= - * - * All these routines assume that the Unicode characters are in little endian - * encoding inside the strings!!! - */ - -/* - * This is used by the name collation functions to quickly determine what - * characters are (in)valid. - */ -#if 0 -static 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, -}; -#endif - -/** - * ntfs_names_are_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 perform a case insensitive comparison. - */ -BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len, - const ntfschar *s2, size_t s2_len, - const IGNORE_CASE_BOOL ic, - const ntfschar *upcase, const u32 upcase_size) -{ - if (s1_len != s2_len) - return FALSE; - if (!s1_len) - return TRUE; - if (ic == CASE_SENSITIVE) - return ntfs_ucsncmp(s1, s2, s1_len) ? FALSE: TRUE; - return ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size) ? FALSE: - TRUE; -} - -/** - * ntfs_names_collate - collate two Unicode names - * @name1: first Unicode name to compare - * @name1_len: length of first Unicode name to compare - * @name2: second Unicode name to compare - * @name2_len: length of second Unicode name to compare - * @err_val: if @name1 contains an invalid character return this value - * @ic: either CASE_SENSITIVE or IGNORE_CASE - * @upcase: upcase table (ignored if @ic is CASE_SENSITIVE) - * @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE) - * - * ntfs_names_collate() 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 - * @err_val if an invalid character is found in @name1 during the comparison. - * - * The following characters are considered invalid: '"', '*', '<', '>' and '?'. - */ -int ntfs_names_collate(const ntfschar *name1, const u32 name1_len, - const ntfschar *name2, const u32 name2_len, - const int err_val __attribute__((unused)), - const IGNORE_CASE_BOOL ic, const ntfschar *upcase, - const u32 upcase_len) -{ - u32 cnt; - ntfschar c1, c2; - -#ifdef DEBUG - if (!name1 || !name2 || (ic && (!upcase || !upcase_len))) { - ntfs_log_debug("ntfs_names_collate received NULL pointer!\n"); - exit(1); - } -#endif - for (cnt = 0; cnt < min(name1_len, name2_len); ++cnt) { - c1 = le16_to_cpu(*name1); - name1++; - c2 = le16_to_cpu(*name2); - name2++; - if (ic) { - if (c1 < upcase_len) - c1 = le16_to_cpu(upcase[c1]); - if (c2 < upcase_len) - c2 = le16_to_cpu(upcase[c2]); - } -#if 0 - if (c1 < 64 && legal_ansi_char_array[c1] & 8) - return err_val; -#endif - if (c1 < c2) - return -1; - if (c1 > c2) - return 1; - } - if (name1_len < name2_len) - return -1; - if (name1_len == name2_len) - return 0; - /* name1_len > name2_len */ -#if 0 - c1 = le16_to_cpu(*name1); - if (c1 < 64 && legal_ansi_char_array[c1] & 8) - return err_val; -#endif - return 1; -} - -/** - * ntfs_ucsncmp - compare two little endian Unicode strings - * @s1: first string - * @s2: second string - * @n: maximum unicode characters to compare - * - * Compare the first @n characters of the Unicode strings @s1 and @s2, - * The strings in little endian format and appropriate le16_to_cpu() - * conversion is performed on non-little endian machines. - * - * 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_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n) -{ - ntfschar c1, c2; - size_t i; - -#ifdef DEBUG - if (!s1 || !s2) { - ntfs_log_debug("ntfs_wcsncmp() received NULL pointer!\n"); - exit(1); - } -#endif - for (i = 0; i < n; ++i) { - c1 = le16_to_cpu(s1[i]); - c2 = le16_to_cpu(s2[i]); - if (c1 < c2) - return -1; - if (c1 > c2) - return 1; - if (!c1) - break; - } - return 0; -} - -/** - * ntfs_ucsncasecmp - 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_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, - const ntfschar *upcase, const u32 upcase_size) -{ - ntfschar c1, c2; - size_t i; - -#ifdef DEBUG - if (!s1 || !s2 || !upcase) { - ntfs_log_debug("ntfs_wcsncasecmp() received NULL pointer!\n"); - exit(1); - } -#endif - 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; -} - -/** - * ntfs_ucsnlen - determine the length of a little endian Unicode string - * @s: pointer to Unicode string - * @maxlen: maximum length of string @s - * - * Return the number of Unicode characters in the little endian Unicode - * string @s up to a maximum of maxlen Unicode characters, not including - * the terminating (ntfschar)'\0'. If there is no (ntfschar)'\0' between @s - * and @s + @maxlen, @maxlen is returned. - * - * This function never looks beyond @s + @maxlen. - */ -u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen) -{ - u32 i; - - for (i = 0; i < maxlen; i++) { - if (!le16_to_cpu(s[i])) - break; - } - return i; -} - -/** - * ntfs_ucsndup - duplicate little endian Unicode string - * @s: pointer to Unicode string - * @maxlen: maximum length of string @s - * - * Return a pointer to a new little endian Unicode string which is a duplicate - * of the string s. Memory for the new string is obtained with malloc(3), and - * can be freed with free(3). - * - * A maximum of @maxlen Unicode characters are copied and a terminating - * (ntfschar)'\0' little endian Unicode character is added. - * - * This function never looks beyond @s + @maxlen. - * - * Return a pointer to the new little endian Unicode string on success and NULL - * on failure with errno set to the error code. - */ -ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen) -{ - ntfschar *dst; - u32 len; - - len = ntfs_ucsnlen(s, maxlen); - dst = malloc((len + 1) * sizeof(ntfschar)); - if (dst) { - memcpy(dst, s, len * sizeof(ntfschar)); - dst[len] = cpu_to_le16(L'\0'); - } - return dst; -} - -/** - * ntfs_name_upcase - Map an Unicode name to its uppercase equivalent - * @name: - * @name_len: - * @upcase: - * @upcase_len: - * - * Description... - * - * Returns: - */ -void ntfs_name_upcase(ntfschar *name, u32 name_len, const ntfschar *upcase, - const u32 upcase_len) -{ - u32 i; - ntfschar u; - - for (i = 0; i < name_len; i++) - if ((u = le16_to_cpu(name[i])) < upcase_len) - name[i] = upcase[u]; -} - -/** - * ntfs_file_value_upcase - Convert a filename to upper case - * @file_name_attr: - * @upcase: - * @upcase_len: - * - * Description... - * - * Returns: - */ -void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr, - const ntfschar *upcase, const u32 upcase_len) -{ - ntfs_name_upcase((ntfschar*)&file_name_attr->file_name, - file_name_attr->file_name_length, upcase, upcase_len); -} - -/** - * ntfs_file_values_compare - Which of two filenames should be listed first - * @file_name_attr1: - * @file_name_attr2: - * @err_val: - * @ic: - * @upcase: - * @upcase_len: - * - * Description... - * - * Returns: - */ -int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1, - const FILE_NAME_ATTR *file_name_attr2, - const int err_val, const IGNORE_CASE_BOOL ic, - const ntfschar *upcase, const u32 upcase_len) -{ - return ntfs_names_collate((ntfschar*)&file_name_attr1->file_name, - file_name_attr1->file_name_length, - (ntfschar*)&file_name_attr2->file_name, - file_name_attr2->file_name_length, - err_val, ic, upcase, upcase_len); -} - -/** - * ntfs_ucstombs - convert a little endian Unicode string to a multibyte string - * @ins: input Unicode string buffer - * @ins_len: length of input string in Unicode characters - * @outs: on return contains the (allocated) output multibyte string - * @outs_len: length of output buffer in bytes - * - * Convert the input little endian, 2-byte Unicode string @ins, of length - * @ins_len into the multibyte string format dictated by the current locale. - * - * If *@outs is NULL, the function allocates the string and the caller is - * responsible for calling free(*@outs); when finished with it. - * - * On success the function returns the number of bytes written to the output - * string *@outs (>= 0), not counting the terminating NULL byte. If the output - * string buffer was allocated, *@outs is set to it. - * - * On error, -1 is returned, and errno is set to the error code. The following - * error codes can be expected: - * EINVAL Invalid arguments (e.g. @ins or @outs is NULL). - * EILSEQ The input string cannot be represented as a multibyte - * sequence according to the current locale. - * ENAMETOOLONG Destination buffer is too small for input string. - * ENOMEM Not enough memory to allocate destination buffer. - */ -int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, - int outs_len) -{ - char *mbs; - wchar_t wc; - int i, o, mbs_len; - int cnt = 0; -#ifdef HAVE_MBSINIT - mbstate_t mbstate; -#endif - - if (!ins || !outs) { - errno = EINVAL; - return -1; - } - mbs = *outs; - mbs_len = outs_len; - if (mbs && !mbs_len) { - errno = ENAMETOOLONG; - return -1; - } - if (!mbs) { - mbs_len = (ins_len + 1) * MB_CUR_MAX; - mbs = (char*)malloc(mbs_len); - if (!mbs) - return -1; - } -#ifdef HAVE_MBSINIT - memset(&mbstate, 0, sizeof(mbstate)); -#else - wctomb(NULL, 0); -#endif - for (i = o = 0; i < ins_len; i++) { - /* Reallocate memory if necessary or abort. */ - if ((int)(o + MB_CUR_MAX) > mbs_len) { - char *tc; - if (mbs == *outs) { - errno = ENAMETOOLONG; - return -1; - } - tc = (char*)malloc((mbs_len + 64) & ~63); - if (!tc) - goto err_out; - memcpy(tc, mbs, mbs_len); - mbs_len = (mbs_len + 64) & ~63; - free(mbs); - mbs = tc; - } - /* Convert the LE Unicode character to a CPU wide character. */ - wc = (wchar_t)le16_to_cpu(ins[i]); - if (!wc) - break; - /* Convert the CPU endian wide character to multibyte. */ -#ifdef HAVE_MBSINIT - cnt = wcrtomb(mbs + o, wc, &mbstate); -#else - cnt = wctomb(mbs + o, wc); -#endif - if (cnt == -1) - goto err_out; - if (cnt <= 0) { - ntfs_log_debug("Eeek. cnt <= 0, cnt = %i\n", cnt); - errno = EINVAL; - goto err_out; - } - o += cnt; - } -#ifdef HAVE_MBSINIT - /* Make sure we are back in the initial state. */ - if (!mbsinit(&mbstate)) { - ntfs_log_debug("Eeek. mbstate not in initial state!\n"); - errno = EILSEQ; - goto err_out; - } -#endif - /* Now write the NULL character. */ - mbs[o] = '\0'; - if (*outs != mbs) - *outs = mbs; - return o; -err_out: - if (mbs != *outs) { - int eo = errno; - free(mbs); - errno = eo; - } - return -1; -} - -/** - * ntfs_mbstoucs - convert a multibyte string to a little endian Unicode string - * @ins: input multibyte string buffer - * @outs: on return contains the (allocated) output Unicode string - * @outs_len: length of output buffer in Unicode characters - * - * Convert the input multibyte string @ins, from the current locale into the - * corresponding little endian, 2-byte Unicode string. - * - * If *@outs is NULL, the function allocates the string and the caller is - * responsible for calling free(*@outs); when finished with it. - * - * On success the function returns the number of Unicode characters written to - * the output string *@outs (>= 0), not counting the terminating Unicode NULL - * character. If the output string buffer was allocated, *@outs is set to it. - * - * On error, -1 is returned, and errno is set to the error code. The following - * error codes can be expected: - * EINVAL Invalid arguments (e.g. @ins or @outs is NULL). - * EILSEQ The input string cannot be represented as a Unicode - * string according to the current locale. - * ENAMETOOLONG Destination buffer is too small for input string. - * ENOMEM Not enough memory to allocate destination buffer. - */ -int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len) -{ - ntfschar *ucs; - const char *s; - wchar_t wc; - int i, o, cnt, ins_len, ucs_len, ins_size; -#ifdef HAVE_MBSINIT - mbstate_t mbstate; -#endif - - if (!ins || !outs) { - errno = EINVAL; - return -1; - } - ucs = *outs; - ucs_len = outs_len; - if (ucs && !ucs_len) { - errno = ENAMETOOLONG; - return -1; - } - /* Determine the size of the multi-byte string in bytes. */ - ins_size = strlen(ins); - /* Determine the length of the multi-byte string. */ - s = ins; -#if defined(HAVE_MBSINIT) - memset(&mbstate, 0, sizeof(mbstate)); - ins_len = mbsrtowcs(NULL, (const char **)&s, 0, &mbstate); -#ifdef __CYGWIN32__ - if (!ins_len && *ins) { - /* Older Cygwin had broken mbsrtowcs() implementation. */ - ins_len = strlen(ins); - } -#endif -#elif !defined(DJGPP) - ins_len = mbstowcs(NULL, s, 0); -#else - /* Eeek!!! DJGPP has broken mbstowcs() implementation!!! */ - ins_len = strlen(ins); -#endif - if (ins_len == -1) - return ins_len; -#ifdef HAVE_MBSINIT - if ((s != ins) || !mbsinit(&mbstate)) { -#else - if (s != ins) { -#endif - errno = EILSEQ; - return -1; - } - /* Add the NULL terminator. */ - ins_len++; - if (!ucs) { - ucs_len = ins_len; - ucs = (ntfschar*)malloc(ucs_len * sizeof(ntfschar)); - if (!ucs) - return -1; - } -#ifdef HAVE_MBSINIT - memset(&mbstate, 0, sizeof(mbstate)); -#else - mbtowc(NULL, NULL, 0); -#endif - for (i = o = cnt = 0; i < ins_size; i += cnt, o++) { - /* Reallocate memory if necessary or abort. */ - if (o >= ucs_len) { - ntfschar *tc; - if (ucs == *outs) { - errno = ENAMETOOLONG; - return -1; - } - /* - * We will never get here but hey, it's only a bit of - * extra code... - */ - ucs_len = (ucs_len * sizeof(ntfschar) + 64) & ~63; - tc = (ntfschar*)realloc(ucs, ucs_len); - if (!tc) - goto err_out; - ucs = tc; - ucs_len /= sizeof(ntfschar); - } - /* Convert the multibyte character to a wide character. */ -#ifdef HAVE_MBSINIT - cnt = mbrtowc(&wc, ins + i, ins_size - i, &mbstate); -#else - cnt = mbtowc(&wc, ins + i, ins_size - i); -#endif - if (!cnt) - break; - if (cnt == -1) - goto err_out; - if (cnt < -1) { - ntfs_log_trace("Eeek. cnt = %i\n", cnt); - errno = EINVAL; - goto err_out; - } - /* Make sure we are not overflowing the NTFS Unicode set. */ - if ((unsigned long)wc >= (unsigned long)(1 << - (8 * sizeof(ntfschar)))) { - errno = EILSEQ; - goto err_out; - } - /* Convert the CPU wide character to a LE Unicode character. */ - ucs[o] = cpu_to_le16(wc); - } -#ifdef HAVE_MBSINIT - /* Make sure we are back in the initial state. */ - if (!mbsinit(&mbstate)) { - ntfs_log_trace("Eeek. mbstate not in initial state!\n"); - errno = EILSEQ; - goto err_out; - } -#endif - /* Now write the NULL character. */ - ucs[o] = cpu_to_le16(L'\0'); - if (*outs != ucs) - *outs = ucs; - return o; -err_out: - if (ucs != *outs) { - int eo = errno; - free(ucs); - errno = eo; - } - return -1; -} - -/** - * ntfs_upcase_table_build - build the default upcase table for NTFS - * @uc: destination buffer where to store the built table - * @uc_len: size of destination buffer in bytes - * - * ntfs_upcase_table_build() builds the default upcase table for NTFS and - * stores it in the caller supplied buffer @uc of size @uc_len. - * - * Note, @uc_len must be at least 128kiB in size or bad things will happen! - */ -void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len) -{ - static int uc_run_table[][3] = { /* Start, End, Add */ - {0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74}, - {0x00E0, 0x00F7, -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76, 86}, - {0x00F8, 0x00FF, -32}, {0x0561, 0x0587, -48}, {0x1F76, 0x1F78, 100}, - {0x0256, 0x0258, -205}, {0x1F00, 0x1F08, 8}, {0x1F78, 0x1F7A, 128}, - {0x028A, 0x028C, -217}, {0x1F10, 0x1F16, 8}, {0x1F7A, 0x1F7C, 112}, - {0x03AC, 0x03AD, -38}, {0x1F20, 0x1F28, 8}, {0x1F7C, 0x1F7E, 126}, - {0x03AD, 0x03B0, -37}, {0x1F30, 0x1F38, 8}, {0x1FB0, 0x1FB2, 8}, - {0x03B1, 0x03C2, -32}, {0x1F40, 0x1F46, 8}, {0x1FD0, 0x1FD2, 8}, - {0x03C2, 0x03C3, -31}, {0x1F51, 0x1F52, 8}, {0x1FE0, 0x1FE2, 8}, - {0x03C3, 0x03CC, -32}, {0x1F53, 0x1F54, 8}, {0x1FE5, 0x1FE6, 7}, - {0x03CC, 0x03CD, -64}, {0x1F55, 0x1F56, 8}, {0x2170, 0x2180, -16}, - {0x03CD, 0x03CF, -63}, {0x1F57, 0x1F58, 8}, {0x24D0, 0x24EA, -26}, - {0x0430, 0x0450, -32}, {0x1F60, 0x1F68, 8}, {0xFF41, 0xFF5B, -32}, - {0} - }; - static int uc_dup_table[][2] = { /* Start, End */ - {0x0100, 0x012F}, {0x01A0, 0x01A6}, {0x03E2, 0x03EF}, {0x04CB, 0x04CC}, - {0x0132, 0x0137}, {0x01B3, 0x01B7}, {0x0460, 0x0481}, {0x04D0, 0x04EB}, - {0x0139, 0x0149}, {0x01CD, 0x01DD}, {0x0490, 0x04BF}, {0x04EE, 0x04F5}, - {0x014A, 0x0178}, {0x01DE, 0x01EF}, {0x04BF, 0x04BF}, {0x04F8, 0x04F9}, - {0x0179, 0x017E}, {0x01F4, 0x01F5}, {0x04C1, 0x04C4}, {0x1E00, 0x1E95}, - {0x018B, 0x018B}, {0x01FA, 0x0218}, {0x04C7, 0x04C8}, {0x1EA0, 0x1EF9}, - {0} - }; - static int uc_byte_table[][2] = { /* Offset, Value */ - {0x00FF, 0x0178}, {0x01AD, 0x01AC}, {0x01F3, 0x01F1}, {0x0269, 0x0196}, - {0x0183, 0x0182}, {0x01B0, 0x01AF}, {0x0253, 0x0181}, {0x026F, 0x019C}, - {0x0185, 0x0184}, {0x01B9, 0x01B8}, {0x0254, 0x0186}, {0x0272, 0x019D}, - {0x0188, 0x0187}, {0x01BD, 0x01BC}, {0x0259, 0x018F}, {0x0275, 0x019F}, - {0x018C, 0x018B}, {0x01C6, 0x01C4}, {0x025B, 0x0190}, {0x0283, 0x01A9}, - {0x0192, 0x0191}, {0x01C9, 0x01C7}, {0x0260, 0x0193}, {0x0288, 0x01AE}, - {0x0199, 0x0198}, {0x01CC, 0x01CA}, {0x0263, 0x0194}, {0x0292, 0x01B7}, - {0x01A8, 0x01A7}, {0x01DD, 0x018E}, {0x0268, 0x0197}, - {0} - }; - int i, r; - - memset((char*)uc, 0, uc_len); - uc_len >>= 1; - if (uc_len > 65536) - uc_len = 65536; - for (i = 0; (u32)i < uc_len; i++) - uc[i] = i; - for (r = 0; uc_run_table[r][0]; r++) - for (i = uc_run_table[r][0]; i < uc_run_table[r][1]; i++) - uc[i] += uc_run_table[r][2]; - for (r = 0; uc_dup_table[r][0]; r++) - for (i = uc_dup_table[r][0]; i < uc_dup_table[r][1]; i += 2) - uc[i + 1]--; - for (r = 0; uc_byte_table[r][0]; r++) - uc[uc_byte_table[r][0]] = uc_byte_table[r][1]; -} - -/** - * ntfs_str2ucs - convert a string to a valid NTFS file name - * @s: input string - * @len: length of output buffer in Unicode characters - * - * Convert the input @s string into the corresponding little endian, - * 2-byte Unicode string. The length of the converted string is less - * or equal to the maximum length allowed by the NTFS format (255). - * - * If @s is NULL then return AT_UNNAMED. - * - * On success the function returns the Unicode string in an allocated - * buffer and the caller is responsible to free it when it's not needed - * anymore. - * - * On error NULL is returned and errno is set to the error code. - */ -ntfschar *ntfs_str2ucs(const char *s, int *len) -{ - ntfschar *ucs = NULL; - - if (s && ((*len = ntfs_mbstoucs(s, &ucs, 0)) == -1)) { - ntfs_log_perror("Couldn't convert '%s' to Unicode", s); - return NULL; - } - if (*len > 0xff) { - free(ucs); - errno = ENAMETOOLONG; - return NULL; - } - if (!ucs || !*len) { - ucs = AT_UNNAMED; - *len = 0; - } - return ucs; -} - -/** - * ntfs_ucsfree - free memory allocated by ntfs_str2ucs() - * @ucs input string to be freed - * - * Free memory at @ucs and which was allocated by ntfs_str2ucs. - * - * Return value: none. - */ -void ntfs_ucsfree(ntfschar *ucs) -{ - if (ucs && (ucs != AT_UNNAMED)) - free(ucs); -} - diff --git a/libntfs/unix_io.c b/libntfs/unix_io.c deleted file mode 100644 index ee8d35db..00000000 --- a/libntfs/unix_io.c +++ /dev/null @@ -1,325 +0,0 @@ -/** - * unix_io.c - Unix style disk io functions. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2006 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_STDIO_H -#include -#endif -#ifdef HAVE_SYS_TYPES_H -#include -#endif -#ifdef HAVE_SYS_STAT_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include -#endif -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#ifdef HAVE_LINUX_FD_H -#include -#endif - -#include "types.h" -#include "mst.h" -#include "debug.h" -#include "device.h" -#include "logging.h" - -#define DEV_FD(dev) (*(int *)dev->d_private) - -/* Define to nothing if not present on this system. */ -#ifndef O_EXCL -# define O_EXCL 0 -#endif - -/** - * ntfs_device_unix_io_open - Open a device and lock it exclusively - * @dev: - * @flags: - * - * Description... - * - * Returns: - */ -static int ntfs_device_unix_io_open(struct ntfs_device *dev, int flags) -{ - struct flock flk; - struct stat sbuf; - int err; - - if (NDevOpen(dev)) { - errno = EBUSY; - return -1; - } - if (!(dev->d_private = malloc(sizeof(int)))) - return -1; - /* - * Open the device/file obtaining the file descriptor for exclusive - * access (but only if mounting r/w). - */ - if ((flags & O_RDWR) == O_RDWR) - flags |= O_EXCL; - *(int*)dev->d_private = open(dev->d_name, flags); - if (*(int*)dev->d_private == -1) { - err = errno; - goto err_out; - } - /* Setup our read-only flag. */ - if ((flags & O_RDWR) != O_RDWR) - NDevSetReadOnly(dev); - /* Acquire exclusive (mandatory) lock on the whole device. */ - memset(&flk, 0, sizeof(flk)); - if (NDevReadOnly(dev)) - flk.l_type = F_RDLCK; - else - flk.l_type = F_WRLCK; - flk.l_whence = SEEK_SET; - flk.l_start = flk.l_len = 0LL; - if (fcntl(DEV_FD(dev), F_SETLK, &flk)) { - err = errno; - ntfs_log_debug("ntfs_device_unix_io_open: Could not lock %s for %s\n", - dev->d_name, NDevReadOnly(dev) ? "reading" : "writing"); - if (close(DEV_FD(dev))) - ntfs_log_perror("ntfs_device_unix_io_open: Warning: Could not " - "close %s", dev->d_name); - goto err_out; - } - /* Determine if device is a block device or not, ignoring errors. */ - if (!fstat(DEV_FD(dev), &sbuf) && S_ISBLK(sbuf.st_mode)) - NDevSetBlock(dev); - /* Set our open flag. */ - NDevSetOpen(dev); - return 0; -err_out: - free(dev->d_private); - dev->d_private = NULL; - errno = err; - return -1; -} - -/** - * ntfs_device_unix_io_close - Close the device, releasing the lock - * @dev: - * - * Description... - * - * Returns: - */ -static int ntfs_device_unix_io_close(struct ntfs_device *dev) -{ - struct flock flk; - - if (!NDevOpen(dev)) { - errno = EBADF; - return -1; - } - if (NDevDirty(dev)) - fsync(DEV_FD(dev)); - /* Release exclusive (mandatory) lock on the whole device. */ - memset(&flk, 0, sizeof(flk)); - flk.l_type = F_UNLCK; - flk.l_whence = SEEK_SET; - flk.l_start = flk.l_len = 0LL; - if (fcntl(DEV_FD(dev), F_SETLK, &flk)) - ntfs_log_perror("ntfs_device_unix_io_close: Warning: Could not " - "unlock %s", dev->d_name); - /* Close the file descriptor and clear our open flag. */ - if (close(DEV_FD(dev))) - return -1; - NDevClearOpen(dev); - free(dev->d_private); - dev->d_private = NULL; - return 0; -} - -/** - * ntfs_device_unix_io_seek - Seek to a place on the device - * @dev: - * @offset: - * @whence: - * - * Description... - * - * Returns: - */ -static s64 ntfs_device_unix_io_seek(struct ntfs_device *dev, s64 offset, - int whence) -{ - return lseek(DEV_FD(dev), offset, whence); -} - -/** - * ntfs_device_unix_io_read - Read from the device, from the current location - * @dev: - * @buf: - * @count: - * - * Description... - * - * Returns: - */ -static s64 ntfs_device_unix_io_read(struct ntfs_device *dev, void *buf, - s64 count) -{ - return read(DEV_FD(dev), buf, count); -} - -/** - * ntfs_device_unix_io_write - Write to the device, at the current location - * @dev: - * @buf: - * @count: - * - * Description... - * - * Returns: - */ -static s64 ntfs_device_unix_io_write(struct ntfs_device *dev, const void *buf, - s64 count) -{ - if (NDevReadOnly(dev)) { - errno = EROFS; - return -1; - } - NDevSetDirty(dev); - return write(DEV_FD(dev), buf, count); -} - -/** - * ntfs_device_unix_io_pread - Perform a positioned read from the device - * @dev: - * @buf: - * @count: - * @offset: - * - * Description... - * - * Returns: - */ -static s64 ntfs_device_unix_io_pread(struct ntfs_device *dev, void *buf, - s64 count, s64 offset) -{ - return ntfs_pread(dev, offset, count, buf); -} - -/** - * ntfs_device_unix_io_pwrite - Perform a positioned write to the device - * @dev: - * @buf: - * @count: - * @offset: - * - * Description... - * - * Returns: - */ -static s64 ntfs_device_unix_io_pwrite(struct ntfs_device *dev, const void *buf, - s64 count, s64 offset) -{ - if (NDevReadOnly(dev)) { - errno = EROFS; - return -1; - } - NDevSetDirty(dev); - return ntfs_pwrite(dev, offset, count, buf); -} - -/** - * ntfs_device_unix_io_sync - Flush any buffered changes to the device - * @dev: - * - * Description... - * - * Returns: - */ -static int ntfs_device_unix_io_sync(struct ntfs_device *dev) -{ - if (!NDevReadOnly(dev) && NDevDirty(dev)) { - int res = fsync(DEV_FD(dev)); - if (!res) - NDevClearDirty(dev); - return res; - } - return 0; -} - -/** - * ntfs_device_unix_io_stat - Get information about the device - * @dev: - * @buf: - * - * Description... - * - * Returns: - */ -static int ntfs_device_unix_io_stat(struct ntfs_device *dev, struct stat *buf) -{ - return fstat(DEV_FD(dev), buf); -} - -/** - * ntfs_device_unix_io_ioctl - Perform an ioctl on the device - * @dev: - * @request: - * @argp: - * - * Description... - * - * Returns: - */ -static int ntfs_device_unix_io_ioctl(struct ntfs_device *dev, int request, - void *argp) -{ - return ioctl(DEV_FD(dev), request, argp); -} - -/** - * Device operations for working with unix style devices and files. - */ -struct ntfs_device_operations ntfs_device_unix_io_ops = { - .open = ntfs_device_unix_io_open, - .close = ntfs_device_unix_io_close, - .seek = ntfs_device_unix_io_seek, - .read = ntfs_device_unix_io_read, - .write = ntfs_device_unix_io_write, - .pread = ntfs_device_unix_io_pread, - .pwrite = ntfs_device_unix_io_pwrite, - .sync = ntfs_device_unix_io_sync, - .stat = ntfs_device_unix_io_stat, - .ioctl = ntfs_device_unix_io_ioctl, -}; diff --git a/libntfs/version.c b/libntfs/version.c deleted file mode 100644 index 7882e717..00000000 --- a/libntfs/version.c +++ /dev/null @@ -1,45 +0,0 @@ -/** - * version.c - Info about the NTFS library. Part of the Linux-NTFS project. - * - * Copyright (c) 2005 Anton Altaparmakov - * Copyright (c) 2005 Richard Russon - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "version.h" - -#ifdef LTVERSION_LIBNTFS -#define LIBNTFS_VERSION_STRING LTVERSION_LIBNTFS -#else -#define LIBNTFS_VERSION_STRING "unknown" -#endif - -static const char *libntfs_version_string = LIBNTFS_VERSION_STRING; - -/** - * ntfs_libntfs_version - query version number of the ntfs library libntfs - * - * Returns pointer to a text string representing the version of libntfs. - */ -const char *ntfs_libntfs_version(void) -{ - return libntfs_version_string; -} diff --git a/libntfs/volume.c b/libntfs/volume.c deleted file mode 100644 index 9477a9cd..00000000 --- a/libntfs/volume.c +++ /dev/null @@ -1,1901 +0,0 @@ -/** - * volume.c - NTFS volume handling code. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2006 Anton Altaparmakov - * Copyright (c) 2002-2005 Szabolcs Szakacsits - * Copyright (c) 2004-2005 Richard Russon - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_STDIO_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_SYS_STAT_H -#include -#endif -#ifdef HAVE_LIMITS_H -#include -#endif - -#include "volume.h" -#include "attrib.h" -#include "mft.h" -#include "bootsect.h" -#include "device.h" -#include "debug.h" -#include "inode.h" -#include "runlist.h" -#include "logfile.h" -#include "dir.h" -#include "logging.h" - -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif - -/** - * ntfs_volume_alloc - Create an NTFS volume object and initialise it - * - * Description... - * - * Returns: - */ -ntfs_volume *ntfs_volume_alloc(void) -{ - return calloc(1, sizeof(ntfs_volume)); -} - -/** - * __ntfs_volume_release - Destroy an NTFS volume object - * @v: - * - * Description... - * - * Returns: - */ -static void __ntfs_volume_release(ntfs_volume *v) -{ - if (v->lcnbmp_ni && NInoDirty(v->lcnbmp_ni)) - ntfs_inode_sync(v->lcnbmp_ni); - if (v->vol_ni) - ntfs_inode_close(v->vol_ni); - if (v->lcnbmp_na) - ntfs_attr_close(v->lcnbmp_na); - if (v->lcnbmp_ni) - ntfs_inode_close(v->lcnbmp_ni); - if (v->mft_ni && NInoDirty(v->mft_ni)) - ntfs_inode_sync(v->mft_ni); - if (v->mftbmp_na) - ntfs_attr_close(v->mftbmp_na); - if (v->mft_na) - ntfs_attr_close(v->mft_na); - if (v->mft_ni) - ntfs_inode_close(v->mft_ni); - if (v->mftmirr_ni && NInoDirty(v->mftmirr_ni)) - ntfs_inode_sync(v->mftmirr_ni); - if (v->mftmirr_na) - ntfs_attr_close(v->mftmirr_na); - if (v->mftmirr_ni) - ntfs_inode_close(v->mftmirr_ni); - if (v->dev) { - struct ntfs_device *dev = v->dev; - - if (NDevDirty(dev)) - dev->d_ops->sync(dev); - if (dev->d_ops->close(dev)) - ntfs_log_perror("Eeek! Failed to close the device. Error: "); - } - free(v->vol_name); - free(v->upcase); - free(v->attrdef); - free(v); -} - -/** - * ntfs_mft_load - load the $MFT and setup the ntfs volume with it - * @vol: ntfs volume whose $MFT to load - * - * Load $MFT from @vol and setup @vol with it. After calling this function the - * volume @vol is ready for use by all read access functions provided by the - * ntfs library. - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -static int ntfs_mft_load(ntfs_volume *vol) -{ - VCN next_vcn, last_vcn, highest_vcn; - s64 l; - MFT_RECORD *mb = NULL; - ntfs_attr_search_ctx *ctx = NULL; - ATTR_RECORD *a; - int eo; - - /* Manually setup an ntfs_inode. */ - vol->mft_ni = ntfs_inode_allocate(vol); - mb = (MFT_RECORD*)malloc(vol->mft_record_size); - if (!vol->mft_ni || !mb) { - ntfs_log_perror("Error allocating memory for $MFT"); - goto error_exit; - } - vol->mft_ni->mft_no = 0; - vol->mft_ni->mrec = mb; - /* Can't use any of the higher level functions yet! */ - l = ntfs_mst_pread(vol->dev, vol->mft_lcn << vol->cluster_size_bits, 1, - vol->mft_record_size, mb); - if (l != 1) { - if (l != -1) - errno = EIO; - ntfs_log_perror("Error reading $MFT"); - goto error_exit; - } - if (ntfs_is_baad_record(mb->magic)) { - ntfs_log_debug("Error: Incomplete multi sector transfer detected in " - "$MFT.\n"); - goto io_error_exit; - } - if (!ntfs_is_mft_record(mb->magic)) { - ntfs_log_debug("Error: $MFT has invalid magic.\n"); - goto io_error_exit; - } - ctx = ntfs_attr_get_search_ctx(vol->mft_ni, NULL); - if (!ctx) { - ntfs_log_perror("Failed to allocate attribute search context"); - goto error_exit; - } - if (p2n(ctx->attr) < p2n(mb) || - (char*)ctx->attr > (char*)mb + vol->mft_record_size) { - ntfs_log_debug("Error: $MFT is corrupt.\n"); - goto io_error_exit; - } - /* Find the $ATTRIBUTE_LIST attribute in $MFT if present. */ - if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0, - ctx)) { - if (errno != ENOENT) { - ntfs_log_debug("Error: $MFT has corrupt attribute list.\n"); - goto io_error_exit; - } - goto mft_has_no_attr_list; - } - NInoSetAttrList(vol->mft_ni); - l = ntfs_get_attribute_value_length(ctx->attr); - if (l <= 0 || l > 0x40000) { - ntfs_log_debug("Error: $MFT/$ATTRIBUTE_LIST has invalid length.\n"); - goto io_error_exit; - } - vol->mft_ni->attr_list_size = l; - vol->mft_ni->attr_list = malloc(l); - if (!vol->mft_ni->attr_list) { - ntfs_log_debug("Error: failed to allocate buffer for attribute " - "list.\n"); - goto error_exit; - } - l = ntfs_get_attribute_value(vol, ctx->attr, vol->mft_ni->attr_list); - if (!l) { - ntfs_log_debug("Error: failed to get value of " - "$MFT/$ATTRIBUTE_LIST.\n"); - goto io_error_exit; - } - if (l != vol->mft_ni->attr_list_size) { - ntfs_log_debug("Error: got unexpected amount of data when reading " - "$MFT/$ATTRIBUTE_LIST.\n"); - goto io_error_exit; - } -mft_has_no_attr_list: - /* We now have a fully setup ntfs inode for $MFT in vol->mft_ni. */ - - /* Get an ntfs attribute for $MFT/$DATA and set it up, too. */ - vol->mft_na = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0); - if (!vol->mft_na) { - ntfs_log_perror("Failed to open ntfs attribute"); - goto error_exit; - } - /* Read all extents from the $DATA attribute in $MFT. */ - ntfs_attr_reinit_search_ctx(ctx); - last_vcn = vol->mft_na->allocated_size >> vol->cluster_size_bits; - highest_vcn = next_vcn = 0; - a = NULL; - while (!ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, next_vcn, NULL, 0, - ctx)) { - runlist_element *nrl; - - a = ctx->attr; - /* $MFT must be non-resident. */ - if (!a->non_resident) { - ntfs_log_debug("$MFT must be non-resident but a resident " - "extent was found. $MFT is corrupt. Run " - "chkdsk.\n"); - goto io_error_exit; - } - /* $MFT must be uncompressed and unencrypted. */ - if (a->flags & ATTR_COMPRESSION_MASK || - a->flags & ATTR_IS_ENCRYPTED) { - ntfs_log_debug("$MFT must be uncompressed and unencrypted " - "but a compressed/encrypted extent was " - "found. $MFT is corrupt. Run chkdsk.\n"); - goto io_error_exit; - } - /* - * Decompress the mapping pairs array of this extent and merge - * the result into the existing runlist. No need for locking - * as we have exclusive access to the inode at this time and we - * are a mount in progress task, too. - */ - nrl = ntfs_mapping_pairs_decompress(vol, a, vol->mft_na->rl); - if (!nrl) { - ntfs_log_perror("ntfs_mapping_pairs_decompress() failed"); - goto error_exit; - } - vol->mft_na->rl = nrl; - - /* Get the lowest vcn for the next extent. */ - highest_vcn = sle64_to_cpu(a->highest_vcn); - next_vcn = highest_vcn + 1; - - /* Only one extent or error, which we catch below. */ - if (next_vcn <= 0) - break; - - /* Avoid endless loops due to corruption. */ - if (next_vcn < sle64_to_cpu(a->lowest_vcn)) { - ntfs_log_debug("$MFT has corrupt attribute list attribute. " - "Run chkdsk.\n"); - goto io_error_exit; - } - } - if (!a) { - ntfs_log_debug("$MFT/$DATA attribute not found. $MFT is corrupt. Run " - "chkdsk.\n"); - goto io_error_exit; - } - if (highest_vcn && highest_vcn != last_vcn - 1) { - ntfs_log_debug("Failed to load the complete runlist for $MFT/$DATA. " - "Bug or corrupt $MFT. Run chkdsk.\n"); - ntfs_log_debug("highest_vcn = 0x%llx, last_vcn - 1 = 0x%llx\n", - (long long)highest_vcn, (long long)last_vcn - 1); - goto io_error_exit; - } - /* Done with the $Mft mft record. */ - ntfs_attr_put_search_ctx(ctx); - ctx = NULL; - /* - * The volume is now setup so we can use all read access functions. - */ - vol->mftbmp_na = ntfs_attr_open(vol->mft_ni, AT_BITMAP, AT_UNNAMED, 0); - if (!vol->mftbmp_na) { - ntfs_log_perror("Failed to open $MFT/$BITMAP"); - goto error_exit; - } - return 0; -io_error_exit: - errno = EIO; -error_exit: - eo = errno; - if (ctx) - ntfs_attr_put_search_ctx(ctx); - if (vol->mft_na) { - ntfs_attr_close(vol->mft_na); - vol->mft_na = NULL; - } - if (vol->mft_ni) { - ntfs_inode_close(vol->mft_ni); - vol->mft_ni = NULL; - } - errno = eo; - return -1; -} - -/** - * ntfs_mftmirr_load - load the $MFTMirr and setup the ntfs volume with it - * @vol: ntfs volume whose $MFTMirr to load - * - * Load $MFTMirr from @vol and setup @vol with it. After calling this function - * the volume @vol is ready for use by all write access functions provided by - * the ntfs library (assuming ntfs_mft_load() has been called successfully - * beforehand). - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -static int ntfs_mftmirr_load(ntfs_volume *vol) -{ - int i; - runlist_element rl[2]; - - vol->mftmirr_ni = ntfs_inode_open(vol, FILE_MFTMirr); - if (!vol->mftmirr_ni) { - ntfs_log_perror("Failed to open inode $MFTMirr"); - return -1; - } - /* Get an ntfs attribute for $MFTMirr/$DATA, too. */ - vol->mftmirr_na = ntfs_attr_open(vol->mftmirr_ni, AT_DATA, AT_UNNAMED, 0); - if (!vol->mftmirr_na) { - ntfs_log_perror("Failed to open $MFTMirr/$DATA"); - goto error_exit; - } - if (ntfs_attr_map_runlist(vol->mftmirr_na, 0) < 0) { - ntfs_log_perror("Failed to map runlist of $MFTMirr/$DATA"); - goto error_exit; - } - /* Construct the mft mirror runlist. */ - rl[0].vcn = 0; - rl[0].lcn = vol->mftmirr_lcn; - rl[0].length = (vol->mftmirr_size * vol->mft_record_size + - vol->cluster_size - 1) / vol->cluster_size; - rl[1].vcn = rl[0].length; - rl[1].lcn = LCN_ENOENT; - rl[1].length = 0; - /* Compare the two runlists. They must be identical. */ - i = 0; - do { - if (rl[i].vcn != vol->mftmirr_na->rl[i].vcn || - rl[i].lcn != vol->mftmirr_na->rl[i].lcn || - rl[i].length != vol->mftmirr_na->rl[i].length) { - ntfs_log_debug("Error: $MFTMirr location mismatch! Run " - "chkdsk.\n"); - errno = EIO; - goto error_exit; - } - } while (rl[i++].length); - return 0; -error_exit: - i = errno; - if (vol->mftmirr_na) { - ntfs_attr_close(vol->mftmirr_na); - vol->mftmirr_na = NULL; - } - ntfs_inode_close(vol->mftmirr_ni); - vol->mftmirr_ni = NULL; - errno = i; - return -1; -} - -/** - * ntfs_volume_startup - allocate and setup an ntfs volume - * @dev: device to open - * @flags: optional mount flags - * - * Load, verify, and parse bootsector; load and setup $MFT and $MFTMirr. After - * calling this function, the volume is setup sufficiently to call all read - * and write access functions provided by the library. - * - * Return the allocated volume structure on success and NULL on error with - * errno set to the error code. - */ -ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) -{ - LCN mft_zone_size, mft_lcn; - s64 br; - ntfs_volume *vol; - NTFS_BOOT_SECTOR *bs; - int eo; -#ifndef NTFS_DISABLE_DEBUG_LOGGING - const char *OK = "OK\n"; - const char *FAILED = "FAILED\n"; - BOOL debug = 1; -#else - BOOL debug = 0; -#endif - - if (!dev || !dev->d_ops || !dev->d_name) { - errno = EINVAL; - return NULL; - } - - /* Allocate the boot sector structure. */ - if (!(bs = (NTFS_BOOT_SECTOR *)malloc(sizeof(NTFS_BOOT_SECTOR)))) - return NULL; - /* Allocate the volume structure. */ - vol = ntfs_volume_alloc(); - if (!vol) - goto error_exit; - /* Create the default upcase table. */ - vol->upcase_len = 65536; - vol->upcase = (ntfschar*)malloc(vol->upcase_len * sizeof(ntfschar)); - if (!vol->upcase) { - ntfs_log_perror("Error allocating memory for upcase table."); - goto error_exit; - } - ntfs_upcase_table_build(vol->upcase, - vol->upcase_len * sizeof(ntfschar)); - if (flags & MS_RDONLY) - NVolSetReadOnly(vol); - if (flags & MS_NOATIME) - NVolSetNoATime(vol); - ntfs_log_debug("Reading bootsector... "); - if (dev->d_ops->open(dev, NVolReadOnly(vol) ? O_RDONLY: O_RDWR)) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Error opening partition device"); - goto error_exit; - } - /* Attach the device to the volume. */ - vol->dev = dev; - /* Now read the bootsector. */ - br = ntfs_pread(dev, 0, sizeof(NTFS_BOOT_SECTOR), bs); - if (br != sizeof(NTFS_BOOT_SECTOR)) { - ntfs_log_debug(FAILED); - if (br != -1) - errno = EINVAL; - if (!br) - ntfs_log_debug("Error: partition is smaller than bootsector " - "size. Weird!\n"); - else - ntfs_log_perror("Error reading bootsector"); - goto error_exit; - } - ntfs_log_debug(OK); - if (!ntfs_boot_sector_is_ntfs(bs, !debug)) { - ntfs_log_debug("Error: %s is not a valid NTFS partition!\n", - dev->d_name); - errno = EINVAL; - goto error_exit; - } - if (ntfs_boot_sector_parse(vol, bs) < 0) { - ntfs_log_perror("Failed to parse ntfs bootsector"); - goto error_exit; - } - free(bs); - bs = NULL; - /* Now set the device block size to the sector size. */ - if (ntfs_device_block_size_set(vol->dev, vol->sector_size)) - ntfs_log_debug("Failed to set the device block size to the " - "sector size. This may affect performance " - "but should be harmless otherwise. Error: " - "%s\n", strerror(errno)); - /* - * We now initialize the cluster allocator. - * - * FIXME: Move this to its own function? (AIA) - */ - - // TODO: Make this tunable at mount time. (AIA) - vol->mft_zone_multiplier = 1; - - /* Determine the size of the MFT zone. */ - 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 the mft zone. */ - vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn; - ntfs_log_debug("mft_zone_pos = 0x%llx\n", (long long)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 the overhead would be - * higher than the speed increase we would get by doing it. - */ - mft_lcn = (8192 + 2 * vol->cluster_size - 1) / vol->cluster_size; - if (mft_lcn * vol->cluster_size < 16 * 1024) - mft_lcn = (16 * 1024 + vol->cluster_size - 1) / - vol->cluster_size; - if (vol->mft_zone_start <= mft_lcn) - vol->mft_zone_start = 0; - ntfs_log_debug("mft_zone_start = 0x%llx\n", (long long)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_log_debug("mft_zone_end = 0x%llx\n", (long long)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_log_debug("data1_zone_pos = 0x%llx\n", vol->data1_zone_pos); - vol->data2_zone_pos = 0; - ntfs_log_debug("data2_zone_pos = 0x%llx\n", vol->data2_zone_pos); - - /* Set the mft data allocation position to mft record 24. */ - vol->mft_data_pos = 24; - - /* - * The cluster allocator is now fully operational. - */ - - /* Need to setup $MFT so we can use the library read functions. */ - ntfs_log_debug("Loading $MFT... "); - if (ntfs_mft_load(vol) < 0) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to load $MFT"); - goto error_exit; - } - ntfs_log_debug(OK); - - /* Need to setup $MFTMirr so we can use the write functions, too. */ - ntfs_log_debug("Loading $MFTMirr... "); - if (ntfs_mftmirr_load(vol) < 0) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to load $MFTMirr"); - goto error_exit; - } - ntfs_log_debug(OK); - return vol; -error_exit: - eo = errno; - free(bs); - if (vol) - __ntfs_volume_release(vol); - errno = eo; - return NULL; -} - -/** - * ntfs_volume_check_logfile - check logfile on target volume - * @vol: volume on which to check logfile - * - * Return 0 on success and -1 on error with errno set error code. - */ -static int ntfs_volume_check_logfile(ntfs_volume *vol) -{ - ntfs_inode *ni; - ntfs_attr *na = NULL; - RESTART_PAGE_HEADER *rp = NULL; - int err = 0; - - if ((ni = ntfs_inode_open(vol, FILE_LogFile)) == NULL) { - ntfs_log_debug("Failed to open inode FILE_LogFile.\n"); - errno = EIO; - return -1; - } - if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) { - ntfs_log_debug("Failed to open $FILE_LogFile/$DATA\n"); - err = EIO; - goto exit; - } - if (!ntfs_check_logfile(na, &rp) || !ntfs_is_logfile_clean(na, rp)) - err = EOPNOTSUPP; - free(rp); -exit: - if (na) - ntfs_attr_close(na); - ntfs_inode_close(ni); - if (err) { - errno = err; - return -1; - } - return 0; -} - -/** - * ntfs_hiberfile_open - Find and open '/hiberfil.sys' - * @vol: An ntfs volume obtained from ntfs_mount - * - * Return: inode Success, hiberfil.sys is valid - * NULL hiberfil.sys doesn't exist or some other error occurred - */ -static ntfs_inode *ntfs_hiberfile_open(ntfs_volume *vol) -{ - u64 inode; - ntfs_inode *ni_root; - ntfs_inode *ni_hibr = NULL; - ntfschar *unicode = NULL; - int unicode_len; - const char *hiberfile = "hiberfil.sys"; - - if (!vol) { - errno = EINVAL; - return NULL; - } - - ni_root = ntfs_inode_open(vol, FILE_root); - if (!ni_root) { - ntfs_log_debug("Couldn't open the root directory.\n"); - return NULL; - } - - unicode_len = ntfs_mbstoucs(hiberfile, &unicode, 0); - if (unicode_len < 0) { - ntfs_log_perror("Couldn't convert 'hiberfil.sys' to Unicode"); - goto out; - } - - inode = ntfs_inode_lookup_by_name(ni_root, unicode, unicode_len); - if (inode == (u64)-1) { - ntfs_log_debug("Couldn't find file '%s'.\n", hiberfile); - goto out; - } - - inode = MREF(inode); - ni_hibr = ntfs_inode_open(vol, inode); - if (!ni_hibr) { - ntfs_log_debug("Couldn't open inode %lld.\n", (long long)inode); - goto out; - } -out: - ntfs_inode_close(ni_root); - free(unicode); - return ni_hibr; -} - - -#define NTFS_HIBERFILE_HEADER_SIZE 4096 - -/** - * ntfs_volume_check_hiberfile - check hiberfil.sys whether Windows is - * hibernated on the target volume - * @vol: volume on which to check hiberfil.sys - * - * Return: 0 if Windows isn't hibernated for sure - * -1 otherwise and errno is set to the appropriate value - */ -static int ntfs_volume_check_hiberfile(ntfs_volume *vol) -{ - ntfs_inode *ni; - ntfs_attr *na = NULL; - int i, bytes_read, ret = -1; - char *buf = NULL; - - ni = ntfs_hiberfile_open(vol); - if (!ni) { - if (errno == ENOENT) - return 0; - return -1; - } - - buf = malloc(NTFS_HIBERFILE_HEADER_SIZE); - if (!buf) { - ntfs_log_perror("Error allocating memory for hiberfile.sys header"); - goto out; - } - - na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); - if (!na) { - ntfs_log_perror("Failed to open hiberfil.sys data attribute"); - goto out; - } - - bytes_read = ntfs_attr_pread(na, 0, NTFS_HIBERFILE_HEADER_SIZE, buf); - if (bytes_read == -1) { - ntfs_log_perror("Failed to read hiberfil.sys"); - goto out; - } - if (bytes_read < NTFS_HIBERFILE_HEADER_SIZE) { - ntfs_log_debug("Hibernated non-system partition, refused to " - "mount!\n"); - errno = EPERM; - goto out; - } - if (memcmp(buf, "hibr", 4) == 0) { - ntfs_log_debug("Windows is hibernated, refused to mount!\n"); - errno = EPERM; - goto out; - } - for (i = 0; i < NTFS_HIBERFILE_HEADER_SIZE; i++) { - if (buf[i]) { - ntfs_log_debug("Windows is hibernated, won't mount!\n"); - errno = EPERM; - goto out; - } - } - /* All right, all header bytes are zero */ - ret = 0; -out: - if (na) - ntfs_attr_close(na); - free(buf); - ntfs_inode_close(ni); - return ret; -} - -/** - * ntfs_device_mount - open ntfs volume - * @dev: device to open - * @flags: optional mount flags - * - * This function mounts an ntfs volume. @dev should describe the device which - * to mount as the ntfs volume. - * - * @flags is an optional second parameter. The same flags are used as for - * the mount system call (man 2 mount). Currently only the following flags - * are implemented: - * MS_RDONLY - mount volume read-only - * MS_NOATIME - do not update access time - * - * The function opens the device @dev and verifies that it contains a valid - * bootsector. Then, it allocates an ntfs_volume structure and initializes - * some of the values inside the structure from the information stored in the - * bootsector. It proceeds to load the necessary system files and completes - * setting up the structure. - * - * Return the allocated volume structure on success and NULL on error with - * errno set to the error code. - */ -ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) -{ - s64 l; -#ifndef NTFS_DISABLE_DEBUG_LOGGING - const char *OK = "OK\n"; - const char *FAILED = "FAILED\n"; -#endif - ntfs_volume *vol; - u8 *m = NULL, *m2 = NULL; - ntfs_attr_search_ctx *ctx = NULL; - ntfs_inode *ni; - ntfs_attr *na; - ATTR_RECORD *a; - VOLUME_INFORMATION *vinf; - ntfschar *vname; - int i, j, eo; - u32 u; - - vol = ntfs_volume_startup(dev, flags); - if (!vol) { - ntfs_log_perror("Failed to startup volume"); - return NULL; - } - - /* Load data from $MFT and $MFTMirr and compare the contents. */ - m = (u8*)malloc(vol->mftmirr_size << vol->mft_record_size_bits); - m2 = (u8*)malloc(vol->mftmirr_size << vol->mft_record_size_bits); - if (!m || !m2) { - ntfs_log_perror("Failed to allocate memory"); - goto error_exit; - } - - l = ntfs_attr_mst_pread(vol->mft_na, 0, vol->mftmirr_size, - vol->mft_record_size, m); - if (l != vol->mftmirr_size) { - if (l == -1) - ntfs_log_perror("Failed to read $MFT"); - else { - ntfs_log_debug("Failed to read $MFT, unexpected length " - "(%d != %lld).\n", vol->mftmirr_size, l); - errno = EIO; - } - goto error_exit; - } - l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size, - vol->mft_record_size, m2); - if (l != vol->mftmirr_size) { - if (l == -1) - ntfs_log_perror("Failed to read $MFTMirr"); - else { - ntfs_log_debug("Failed to read $MFTMirr, unexpected " - "length (%d != %lld).\n", - vol->mftmirr_size, l); - errno = EIO; - } - goto error_exit; - } - ntfs_log_debug("Comparing $MFTMirr to $MFT... "); - for (i = 0; i < vol->mftmirr_size; ++i) { - MFT_RECORD *mrec, *mrec2; - const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", - "$Volume", "$AttrDef", "root directory", "$Bitmap", - "$Boot", "$BadClus", "$Secure", "$UpCase", "$Extend" }; - const char *s; - - if (i < 12) - s = ESTR[i]; - else if (i < 16) - s = "system file"; - else - s = "mft record"; - - mrec = (MFT_RECORD*)(m + i * vol->mft_record_size); - if (mrec->flags & MFT_RECORD_IN_USE) { - if (ntfs_is_baad_recordp(mrec)) { - ntfs_log_debug("FAILED\n"); - ntfs_log_debug("$MFT error: Incomplete multi " - "sector transfer detected in " - "%s.\n", s); - goto io_error_exit; - } - if (!ntfs_is_mft_recordp(mrec)) { - ntfs_log_debug("FAILED\n"); - ntfs_log_debug("$MFT error: Invalid mft " - "record for %s.\n", s); - goto io_error_exit; - } - } - mrec2 = (MFT_RECORD*)(m2 + i * vol->mft_record_size); - if (mrec2->flags & MFT_RECORD_IN_USE) { - if (ntfs_is_baad_recordp(mrec2)) { - ntfs_log_debug("FAILED\n"); - ntfs_log_debug("$MFTMirr error: Incomplete " - "multi sector transfer " - "detected in %s.\n", s); - goto io_error_exit; - } - if (!ntfs_is_mft_recordp(mrec2)) { - ntfs_log_debug("FAILED\n"); - ntfs_log_debug("$MFTMirr error: Invalid mft " - "record for %s.\n", s); - goto io_error_exit; - } - } - if (memcmp(mrec, mrec2, ntfs_mft_record_get_data_size(mrec))) { - ntfs_log_debug(FAILED); - ntfs_log_debug("$MFTMirr does not match $MFT. Run " - "chkdsk.\n"); - goto io_error_exit; - } - } - ntfs_log_debug(OK); - - free(m2); - free(m); - m = m2 = NULL; - - /* Now load the bitmap from $Bitmap. */ - ntfs_log_debug("Loading $Bitmap... "); - vol->lcnbmp_ni = ntfs_inode_open(vol, FILE_Bitmap); - if (!vol->lcnbmp_ni) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to open inode"); - goto error_exit; - } - /* Get an ntfs attribute for $Bitmap/$DATA. */ - vol->lcnbmp_na = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0); - if (!vol->lcnbmp_na) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to open ntfs attribute"); - goto error_exit; - } - /* Done with the $Bitmap mft record. */ - ntfs_log_debug(OK); - - /* Now load the upcase table from $UpCase. */ - ntfs_log_debug("Loading $UpCase... "); - ni = ntfs_inode_open(vol, FILE_UpCase); - if (!ni) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to open inode"); - goto error_exit; - } - /* Get an ntfs attribute for $UpCase/$DATA. */ - na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); - if (!na) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to open ntfs attribute"); - goto error_exit; - } - /* - * Note: Normally, the upcase table has a length equal to 65536 - * 2-byte Unicode characters but allow for different cases, so no - * checks done. Just check we don't overflow 32-bits worth of Unicode - * characters. - */ - if (na->data_size & ~0x1ffffffffULL) { - ntfs_log_debug(FAILED); - ntfs_log_debug("Error: Upcase table is too big (max 32-bit " - "allowed).\n"); - errno = EINVAL; - goto error_exit; - } - if (vol->upcase_len != na->data_size >> 1) { - vol->upcase_len = na->data_size >> 1; - /* Throw away default table. */ - free(vol->upcase); - vol->upcase = (ntfschar*)malloc(na->data_size); - if (!vol->upcase) { - ntfs_log_debug(FAILED); - ntfs_log_debug("Not enough memory to load $UpCase.\n"); - goto error_exit; - } - } - /* Read in the $DATA attribute value into the buffer. */ - l = ntfs_attr_pread(na, 0, na->data_size, vol->upcase); - if (l != na->data_size) { - ntfs_log_debug(FAILED); - ntfs_log_debug("Amount of data read does not correspond to expected " - "length!\n"); - errno = EIO; - goto error_exit; - } - /* Done with the $UpCase mft record. */ - ntfs_log_debug(OK); - ntfs_attr_close(na); - if (ntfs_inode_close(ni)) - ntfs_log_perror("Failed to close inode, leaking memory"); - - /* - * Now load $Volume and set the version information and flags in the - * vol structure accordingly. - */ - ntfs_log_debug("Loading $Volume... "); - vol->vol_ni = ntfs_inode_open(vol, FILE_Volume); - if (!vol->vol_ni) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to open inode"); - goto error_exit; - } - /* Get a search context for the $Volume/$VOLUME_INFORMATION lookup. */ - ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); - if (!ctx) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to allocate attribute search context"); - goto error_exit; - } - /* Find the $VOLUME_INFORMATION attribute. */ - if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, - 0, ctx)) { - ntfs_log_debug(FAILED); - ntfs_log_debug("$VOLUME_INFORMATION attribute not found in " - "$Volume?!?\n"); - goto error_exit; - } - a = ctx->attr; - /* Has to be resident. */ - if (a->non_resident) { - ntfs_log_debug(FAILED); - ntfs_log_debug("Error: Attribute $VOLUME_INFORMATION must be " - "resident (and it isn't)!\n"); - errno = EIO; - goto error_exit; - } - /* Get a pointer to the value of the attribute. */ - vinf = (VOLUME_INFORMATION*)(le16_to_cpu(a->value_offset) + (char*)a); - /* Sanity checks. */ - if ((char*)vinf + le32_to_cpu(a->value_length) > (char*)ctx->mrec + - le32_to_cpu(ctx->mrec->bytes_in_use) || - le16_to_cpu(a->value_offset) + le32_to_cpu( - a->value_length) > le32_to_cpu(a->length)) { - ntfs_log_debug(FAILED); - ntfs_log_debug("Error: Attribute $VOLUME_INFORMATION in $Volume is " - "corrupt!\n"); - errno = EIO; - goto error_exit; - } - /* Setup vol from the volume information attribute value. */ - vol->major_ver = vinf->major_ver; - vol->minor_ver = vinf->minor_ver; - /* Do not use le16_to_cpu() macro here as our VOLUME_FLAGS are - defined using cpu_to_le16() macro and hence are consistent. */ - vol->flags = vinf->flags; - /* - * Reinitialize the search context for the $Volume/$VOLUME_NAME lookup. - */ - ntfs_attr_reinit_search_ctx(ctx); - if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, - ctx)) { - if (errno != ENOENT) { - ntfs_log_debug(FAILED); - ntfs_log_debug("Error: Lookup of $VOLUME_NAME attribute in " - "$Volume failed. This probably means " - "something is corrupt. Run chkdsk.\n"); - goto error_exit; - } - /* - * Attribute not present. This has been seen in the field. - * Treat this the same way as if the attribute was present but - * had zero length. - */ - vol->vol_name = malloc(1); - if (!vol->vol_name) { - ntfs_log_debug(FAILED); - ntfs_log_debug("Error: Unable to allocate memory for volume " - "name!\n"); - goto error_exit; - } - vol->vol_name[0] = '\0'; - } else { - a = ctx->attr; - /* Has to be resident. */ - if (a->non_resident) { - ntfs_log_debug(FAILED); - ntfs_log_debug("Error: Attribute $VOLUME_NAME must be " - "resident!\n"); - errno = EIO; - goto error_exit; - } - /* Get a pointer to the value of the attribute. */ - vname = (ntfschar*)(le16_to_cpu(a->value_offset) + (char*)a); - u = le32_to_cpu(a->value_length) / 2; - /* - * Convert Unicode volume name to current locale multibyte - * format. - */ - vol->vol_name = NULL; - if (ntfs_ucstombs(vname, u, &vol->vol_name, 0) == -1) { - ntfs_log_perror("Error: Volume name could not be converted " - "to current locale"); - ntfs_log_debug("Forcing name into ASCII by replacing " - "non-ASCII characters with underscores.\n"); - vol->vol_name = malloc(u + 1); - if (!vol->vol_name) { - ntfs_log_debug(FAILED); - ntfs_log_debug("Error: Unable to allocate memory for " - "volume name!\n"); - goto error_exit; - } - for (j = 0; j < (s32)u; j++) { - ntfschar uc = le16_to_cpu(vname[j]); - if (uc > 0xff) - uc = (ntfschar)'_'; - vol->vol_name[j] = (char)uc; - } - vol->vol_name[u] = '\0'; - } - } - ntfs_log_debug(OK); - ntfs_attr_put_search_ctx(ctx); - ctx = NULL; - /* Now load the attribute definitions from $AttrDef. */ - ntfs_log_debug("Loading $AttrDef... "); - ni = ntfs_inode_open(vol, FILE_AttrDef); - if (!ni) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to open inode"); - goto error_exit; - } - /* Get an ntfs attribute for $AttrDef/$DATA. */ - na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); - if (!na) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to open ntfs attribute"); - goto error_exit; - } - /* Check we don't overflow 32-bits. */ - if (na->data_size > 0xffffffffLL) { - ntfs_log_debug(FAILED); - ntfs_log_debug("Error: Attribute definition table is too big (max " - "32-bit allowed).\n"); - errno = EINVAL; - goto error_exit; - } - vol->attrdef_len = na->data_size; - vol->attrdef = (ATTR_DEF*)malloc(na->data_size); - if (!vol->attrdef) { - ntfs_log_debug(FAILED); - ntfs_log_debug("Not enough memory to load $AttrDef.\n"); - goto error_exit; - } - /* Read in the $DATA attribute value into the buffer. */ - l = ntfs_attr_pread(na, 0, na->data_size, vol->attrdef); - if (l != na->data_size) { - ntfs_log_debug(FAILED); - ntfs_log_debug("Amount of data read does not correspond to expected " - "length!\n"); - errno = EIO; - goto error_exit; - } - /* Done with the $AttrDef mft record. */ - ntfs_log_debug(OK); - ntfs_attr_close(na); - if (ntfs_inode_close(ni)) - ntfs_log_perror("Failed to close inode, leaking memory"); - /* - * Check for dirty logfile and hibernated Windows. - * We care only about read-write mounts. - */ - if (!(flags & MS_RDONLY)) { - if (ntfs_volume_check_logfile(vol) < 0) - goto error_exit; - if (ntfs_volume_check_hiberfile(vol) < 0) - goto error_exit; - } - - return vol; -io_error_exit: - errno = EIO; -error_exit: - eo = errno; - if (ctx) - ntfs_attr_put_search_ctx(ctx); - free(m); - free(m2); - __ntfs_volume_release(vol); - errno = eo; - return NULL; -} - -/** - * ntfs_mount - open ntfs volume - * @name: name of device/file to open - * @flags: optional mount flags - * - * This function mounts an ntfs volume. @name should contain the name of the - * device/file to mount as the ntfs volume. - * - * @flags is an optional second parameter. The same flags are used as for - * the mount system call (man 2 mount). Currently only the following flags - * are implemented: - * MS_RDONLY - mount volume read-only - * MS_NOATIME - do not update access time - * - * The function opens the device or file @name and verifies that it contains a - * valid bootsector. Then, it allocates an ntfs_volume structure and initializes - * some of the values inside the structure from the information stored in the - * bootsector. It proceeds to load the necessary system files and completes - * setting up the structure. - * - * Return the allocated volume structure on success and NULL on error with - * errno set to the error code. - * - * Note, that a copy is made of @name, and hence it can be discarded as - * soon as the function returns. - */ -ntfs_volume *ntfs_mount(const char *name __attribute__((unused)), - unsigned long flags __attribute__((unused))) -{ -#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS - struct ntfs_device *dev; - ntfs_volume *vol; - - /* Allocate an ntfs_device structure. */ - dev = ntfs_device_alloc(name, 0, &ntfs_device_default_io_ops, NULL); - if (!dev) - return NULL; - /* Call ntfs_device_mount() to do the actual mount. */ - vol = ntfs_device_mount(dev, flags); - if (!vol) { - int eo = errno; - ntfs_device_free(dev); - errno = eo; - } - return vol; -#else - /* - * ntfs_mount() makes no sense if NO_NTFS_DEVICE_DEFAULT_IO_OPS is - * defined as there are no device operations available in libntfs in - * this case. - */ - errno = EOPNOTSUPP; - return NULL; -#endif -} - -/** - * ntfs_device_umount - close ntfs volume - * @vol: address of ntfs_volume structure of volume to close - * @force: if true force close the volume even if it is busy - * - * Deallocate all structures (including @vol itself) associated with the ntfs - * volume @vol. - * - * Note it is up to the caller to destroy the device associated with the volume - * being unmounted after this function returns. - * - * Return 0 on success. On error return -1 with errno set appropriately - * (most likely to one of EAGAIN, EBUSY or EINVAL). The EAGAIN error means that - * an operation is in progress and if you try the close later the operation - * might be completed and the close succeed. - * - * If @force is true (i.e. not zero) this function will close the volume even - * if this means that data might be lost. - * - * @vol must have previously been returned by a call to ntfs_device_mount(). - * - * @vol itself is deallocated and should no longer be dereferenced after this - * function returns success. If it returns an error then nothing has been done - * so it is safe to continue using @vol. - */ -int ntfs_device_umount(ntfs_volume *vol, - const BOOL force __attribute__((unused))) -{ - if (!vol) { - errno = EINVAL; - return -1; - } - __ntfs_volume_release(vol); - return 0; -} - -/** - * ntfs_umount - close ntfs volume - * @vol: address of ntfs_volume structure of volume to close - * @force: if true force close the volume even if it is busy - * - * Deallocate all structures (including @vol itself) associated with the ntfs - * volume @vol. - * - * Return 0 on success. On error return -1 with errno set appropriately - * (most likely to one of EAGAIN, EBUSY or EINVAL). The EAGAIN error means that - * an operation is in progress and if you try the close later the operation - * might be completed and the close succeed. - * - * If @force is true (i.e. not zero) this function will close the volume even - * if this means that data might be lost. - * - * @vol must have previously been returned by a call to ntfs_mount(). - * - * @vol itself is deallocated and should no longer be dereferenced after this - * function returns success. If it returns an error then nothing has been done - * so it is safe to continue using @vol. - */ -int ntfs_umount(ntfs_volume *vol, - const BOOL force __attribute__((unused))) -{ - struct ntfs_device *dev; - - if (!vol) { - errno = EINVAL; - return -1; - } - dev = vol->dev; - __ntfs_volume_release(vol); - ntfs_device_free(dev); - return 0; -} - -#ifdef HAVE_MNTENT_H - -#ifndef HAVE_REALPATH -/** - * realpath - If there is no realpath on the system - */ -static char *realpath(const char *path, char *resolved_path) -{ - strncpy(resolved_path, path, PATH_MAX); - resolved_path[PATH_MAX] = '\0'; - return resolved_path; -} -#endif - -/** - * ntfs_mntent_check - desc - * - * If you are wanting to use this, you actually wanted to use - * ntfs_check_if_mounted(), you just didn't realize. (-: - * - * See description of ntfs_check_if_mounted(), below. - */ -static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags) -{ - struct mntent *mnt; - char *real_file = NULL, *real_fsname = NULL; - FILE *f; - int err = 0; - - real_file = malloc(PATH_MAX + 1); - if (!real_file) - return -1; - real_fsname = malloc(PATH_MAX + 1); - if (!real_fsname) { - err = errno; - goto exit; - } - if (!realpath(file, real_file)) { - err = errno; - goto exit; - } - if (!(f = setmntent(MOUNTED, "r"))) { - err = errno; - goto exit; - } - while ((mnt = getmntent(f))) { - if (!realpath(mnt->mnt_fsname, real_fsname)) - continue; - if (!strcmp(real_file, real_fsname)) - break; - } - endmntent(f); - if (!mnt) - goto exit; - *mnt_flags = NTFS_MF_MOUNTED; - if (!strcmp(mnt->mnt_dir, "/")) - *mnt_flags |= NTFS_MF_ISROOT; -#ifdef HAVE_HASMNTOPT - if (hasmntopt(mnt, "ro") && !hasmntopt(mnt, "rw")) - *mnt_flags |= NTFS_MF_READONLY; -#endif -exit: - free(real_file); - free(real_fsname); - if (err) { - errno = err; - return -1; - } - return 0; -} -#endif /* HAVE_MNTENT_H */ - -/** - * ntfs_check_if_mounted - check if an ntfs volume is currently mounted - * @file: device file to check - * @mnt_flags: pointer into which to return the ntfs mount flags (see volume.h) - * - * If the running system does not support the {set,get,end}mntent() calls, - * just return 0 and set *@mnt_flags to zero. - * - * When the system does support the calls, ntfs_check_if_mounted() first tries - * to find the device @file in /etc/mtab (or wherever this is kept on the - * running system). If it is not found, assume the device is not mounted and - * return 0 and set *@mnt_flags to zero. - * - * If the device @file is found, set the NTFS_MF_MOUNTED flags in *@mnt_flags. - * - * Further if @file is mounted as the file system root ("/"), set the flag - * NTFS_MF_ISROOT in *@mnt_flags. - * - * Finally, check if the file system is mounted read-only, and if so set the - * NTFS_MF_READONLY flag in *@mnt_flags. - * - * On success return 0 with *@mnt_flags set to the ntfs mount flags. - * - * On error return -1 with errno set to the error code. - */ -int ntfs_check_if_mounted(const char *file __attribute__((unused)), - unsigned long *mnt_flags) -{ - *mnt_flags = 0; -#ifdef HAVE_MNTENT_H - return ntfs_mntent_check(file, mnt_flags); -#else - return 0; -#endif -} - -/** - * ntfs_version_is_supported - check if NTFS version is supported. - * @vol: ntfs volume whose version we're interested in. - * - * The function checks if the NTFS volume version is known or not. - * Version 1.1 and 1.2 are used by Windows NT3.x and NT4. - * Version 2.x is used by Windows 2000 Betas. - * Version 3.0 is used by Windows 2000. - * Version 3.1 is used by Windows XP, Windows Server 2003 and Longhorn. - * - * Return 0 if NTFS version is supported otherwise -1 with errno set. - * - * The following error codes are defined: - * EOPNOTSUPP - Unknown NTFS version - * EINVAL - Invalid argument - */ -int ntfs_version_is_supported(ntfs_volume *vol) -{ - u8 major, minor; - - if (!vol) { - errno = EINVAL; - return -1; - } - - major = vol->major_ver; - minor = vol->minor_ver; - - if (NTFS_V1_1(major, minor) || NTFS_V1_2(major, minor)) - return 0; - - if (NTFS_V2_X(major, minor)) - return 0; - - if (NTFS_V3_0(major, minor) || NTFS_V3_1(major, minor)) - return 0; - - errno = EOPNOTSUPP; - return -1; -} - -/** - * ntfs_logfile_reset - "empty" $LogFile data attribute value - * @vol: ntfs volume whose $LogFile we intend to reset. - * - * Fill the value of the $LogFile data attribute, i.e. the contents of - * the file, with 0xff's, thus marking the journal as empty. - * - * FIXME(?): We might need to zero the LSN field of every single mft - * record as well. (But, first try without doing that and see what - * happens, since chkdsk might pickup the pieces and do it for us...) - * - * On success return 0. - * - * On error return -1 with errno set to the error code. - */ -int ntfs_logfile_reset(ntfs_volume *vol) -{ - ntfs_inode *ni; - ntfs_attr *na; - int eo; - - if (!vol) { - errno = EINVAL; - return -1; - } - - if ((ni = ntfs_inode_open(vol, FILE_LogFile)) == NULL) { - ntfs_log_perror("Failed to open inode FILE_LogFile."); - return -1; - } - - if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) { - eo = errno; - ntfs_log_perror("Failed to open $FILE_LogFile/$DATA"); - goto error_exit; - } - - if (ntfs_empty_logfile(na)) { - eo = errno; - ntfs_log_perror("Failed to empty $FILE_LogFile/$DATA"); - ntfs_attr_close(na); - goto error_exit; - } - ntfs_attr_close(na); - return ntfs_inode_close(ni); - -error_exit: - ntfs_inode_close(ni); - errno = eo; - return -1; -} - -/** - * ntfs_volume_write_flags - set the flags of an ntfs volume - * @vol: ntfs volume where we set the volume flags - * @flags: new flags - * - * Set the on-disk volume flags in the mft record of $Volume and - * on volume @vol to @flags. - * - * Return 0 if successful and -1 if not with errno set to the error code. - */ -int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags) -{ - ATTR_RECORD *a; - VOLUME_INFORMATION *c; - ntfs_attr_search_ctx *ctx; - int ret = -1; /* failure */ - - if (!vol || !vol->vol_ni) { - errno = EINVAL; - return -1; - } - /* Get a pointer to the volume information attribute. */ - ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); - if (!ctx) { - ntfs_log_perror("Failed to allocate attribute search context"); - return -1; - } - if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, - 0, ctx)) { - ntfs_log_debug("Error: Attribute $VOLUME_INFORMATION was not found " - "in $Volume!\n"); - goto err_out; - } - a = ctx->attr; - /* Sanity check. */ - if (a->non_resident) { - ntfs_log_debug("Error: Attribute $VOLUME_INFORMATION must be " - "resident (and it isn't)!\n"); - errno = EIO; - goto err_out; - } - /* Get a pointer to the value of the attribute. */ - c = (VOLUME_INFORMATION*)(le16_to_cpu(a->value_offset) + (char*)a); - /* Sanity checks. */ - if ((char*)c + le32_to_cpu(a->value_length) > (char*)ctx->mrec + - le32_to_cpu(ctx->mrec->bytes_in_use) || - le16_to_cpu(a->value_offset) + - le32_to_cpu(a->value_length) > le32_to_cpu(a->length)) { - ntfs_log_debug("Error: Attribute $VOLUME_INFORMATION in $Volume is " - "corrupt!\n"); - errno = EIO; - goto err_out; - } - /* Set the volume flags. */ - vol->flags = c->flags = flags & VOLUME_FLAGS_MASK; - /* Write them to disk. */ - ntfs_inode_mark_dirty(vol->vol_ni); - if (ntfs_inode_sync(vol->vol_ni)) { - ntfs_log_perror("Error writing $Volume"); - goto err_out; - } - ret = 0; /* success */ -err_out: - ntfs_attr_put_search_ctx(ctx); - return ret; -} - - -#ifdef NTFS_RICH - -#include "tree.h" -#include "rich.h" - -/** - * utils_valid_device - Perform some safety checks on the device before we start - * @name: Full pathname of the device/file to work with - * @force: Continue regardless of problems - * - * Check that the name refers to a device and that is isn't already mounted. - * These checks can be overridden by using the force option. - * - * Return: 1 Success, we can continue - * 0 Error, we cannot use this device - */ -int utils_valid_device(const char *name, int force) -{ - unsigned long mnt_flags = 0; - struct stat st; - -#ifdef __CYGWIN32__ - /* FIXME: This doesn't work for Cygwin, so just return success for now... */ - return 1; -#endif - if (!name) { - errno = EINVAL; - return 0; - } - ntfs_log_trace ("\n"); - - if (stat(name, &st) == -1) { - if (errno == ENOENT) { - ntfs_log_error("The device %s doesn't exist\n", name); - } else { - ntfs_log_perror("Error getting information about %s", name); - } - return 0; - } - - if (!S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode)) { - ntfs_log_warning("%s is not a block device, " - "nor regular file.\n", name); - if (!force) { - ntfs_log_error("Use the force option to work with other" - " file types, for your own risk!\n"); - return 0; - } - ntfs_log_warning("Forced to continue.\n"); - } - - /* Make sure the file system is not mounted. */ - if (ntfs_check_if_mounted(name, &mnt_flags)) { - ntfs_log_perror("Failed to determine whether %s is mounted", name); - if (!force) { - ntfs_log_error("Use the force option to ignore this error.\n"); - return 0; - } - ntfs_log_warning("Forced to continue.\n"); - } else if (mnt_flags & NTFS_MF_MOUNTED) { - ntfs_log_warning("The device %s, is mounted.\n", name); - if (!force) { - ntfs_log_error("Use the force option to work a mounted filesystem.\n"); - return 0; - } - ntfs_log_warning("Forced to continue.\n"); - } - - return 1; -} - -/** - * utils_mount_volume - Mount an NTFS volume - * @device: - * @flags: - * @force: - * - * Description... - * - * Returns: - */ -ntfs_volume * utils_mount_volume(const char *device, unsigned long flags, BOOL force) -{ - ntfs_volume *vol; - - if (!device) { - errno = EINVAL; - return NULL; - } - ntfs_log_trace ("\n"); - - if (!utils_valid_device(device, force)) - return NULL; - - vol = ntfs_mount(device, flags); - if (!vol) { - int err; - - err = errno; - ntfs_log_perror("Couldn't mount device '%s'", device); - if (err == EPERM) - ntfs_log_error("Windows was hibernated. Try to mount " - "volume in windows, shut down and try " - "again.\n"); - if (err == EOPNOTSUPP) - ntfs_log_error("Windows did not shut down properly. " - "Try to mount volume in windows, " - "shut down and try again.\n"); - return NULL; - } - - if (vol->flags & VOLUME_IS_DIRTY) { - ntfs_log_warning("Volume is dirty.\n"); - if (!force) { - ntfs_log_error("Run chkdsk and try again, or use the " - "force option.\n"); - ntfs_umount(vol, FALSE); - return NULL; - } - ntfs_log_quiet("Forced to continue.\n"); - } - - return vol; -} - -/** - * ntfs_volume_commit - Write to disk the in-memory volume changes - * @vol: - * - * Description... - * - * Returns: - */ -int ntfs_volume_commit(ntfs_volume *vol) -{ - if (!vol) - return -1; - - ntfs_log_trace ("\n"); - ntfs_log_debug("commit volume\n"); - if (ntfs_bmp_commit(vol->private_bmp1) < 0) - return -1; - - if (ntfs_bmp_commit(vol->private_bmp2) < 0) - return -1; - - if (ntfs_dir_commit(vol->private_data) < 0) - return -1; - - return 0; -} - -/** - * ntfs_volume_rollback - Discard the in-memory volume changes - * @vol: - * - * Description... - * - * Returns: - */ -int ntfs_volume_rollback(ntfs_volume *vol) -{ - if (!vol) - return -1; - ntfs_log_trace ("\n"); - - if (ntfs_bmp_rollback(vol->private_bmp1) < 0) - return -1; - - if (ntfs_bmp_rollback(vol->private_bmp2) < 0) - return -1; - - if (ntfs_dir_rollback(vol->private_data) < 0) - return -1; - - return 0; -} - -/** - * ntfs_volume_umount2 - Unmount an NTFS volume, using the new directory support - * @vol: - * @force: - * - * Description... - * - * Returns: - */ -int ntfs_volume_umount2(ntfs_volume *vol, const BOOL force) -{ - struct ntfs_dir *dir; - struct ntfs_bmp *bmp; - - if (!vol) - return 0; - ntfs_log_trace ("\n"); - - ntfs_volume_rollback(vol); - - dir = (struct ntfs_dir *) vol->private_data; - vol->private_data = NULL; - ntfs_dir_free(dir); - - bmp = (struct ntfs_bmp *) vol->private_bmp1; - vol->private_bmp1 = NULL; - ntfs_bmp_free(bmp); - - bmp = (struct ntfs_bmp *) vol->private_bmp2; - vol->private_bmp2 = NULL; - ntfs_bmp_free(bmp); - - return ntfs_umount(vol, force); -} - -/** - * ntfs_volume_mount2 - Mount an NTFS volume, using the new directory support - * @device: - * @flags: - * @force: - * - * Description... - * - * Returns: - */ -ntfs_volume * ntfs_volume_mount2(const char *device, unsigned long flags, BOOL force) -{ - // XXX can we replace these and search by mft number? Hmm... NO. - // unless I have a recursive search for an MFT number - static ntfschar bmp[8] = { - const_cpu_to_le16('$'), - const_cpu_to_le16('B'), - const_cpu_to_le16('i'), - const_cpu_to_le16('t'), - const_cpu_to_le16('m'), - const_cpu_to_le16('a'), - const_cpu_to_le16('p'), - const_cpu_to_le16(0) - }; - - static ntfschar mft[5] = { - const_cpu_to_le16('$'), - const_cpu_to_le16('M'), - const_cpu_to_le16('F'), - const_cpu_to_le16('T'), - const_cpu_to_le16(0) - }; - - static ntfschar mftmirr[9] = { - const_cpu_to_le16('$'), - const_cpu_to_le16('M'), - const_cpu_to_le16('F'), - const_cpu_to_le16('T'), - const_cpu_to_le16('M'), - const_cpu_to_le16('i'), - const_cpu_to_le16('r'), - const_cpu_to_le16('r'), - const_cpu_to_le16(0) - }; - - static ntfschar dot[2] = { - const_cpu_to_le16('.'), - const_cpu_to_le16(0) - }; - - ntfs_volume *vol; - struct ntfs_dir *dir; - struct ntfs_dt *root; - struct ntfs_dt *found; - int num; - - ntfs_log_trace ("\n"); - vol = utils_mount_volume(device, flags, force); - if (!vol) - return NULL; - - vol->lcnbmp_ni ->ref_count = 1; - vol->mft_ni ->ref_count = 1; - vol->mftmirr_ni->ref_count = 1; - - vol->lcnbmp_ni ->private_data = NULL; - vol->mft_ni ->private_data = NULL; - vol->mftmirr_ni->private_data = NULL; - - dir = ntfs_dir_create(vol, FILE_root); - if (!dir) { - ntfs_volume_umount2(vol, FALSE); - vol = NULL; - goto done; - } - - dir->index = ntfs_dt_create(dir, NULL, -1); - - root = dir->index; - - //$Bitmap - num = -1; - found = ntfs_dt_find2(root, bmp, (sizeof(bmp)/sizeof(ntfschar)) - 1, &num); - if ((!found) || (num < 0)) { - ntfs_log_debug("can't find $Bitmap\n"); - ntfs_volume_umount2(vol, FALSE); - vol = NULL; - goto done; - } - vol->lcnbmp_ni->ref_count++; - vol->lcnbmp_ni->private_data = found->dir; - found->inodes[num] = vol->lcnbmp_ni; - - //$MFT - num = -1; - found = ntfs_dt_find2(root, mft, (sizeof(mft)/sizeof(ntfschar)) - 1, &num); - if ((!found) || (num < 0)) { - ntfs_log_debug("can't find $MFT\n"); - ntfs_volume_umount2(vol, FALSE); - vol = NULL; - goto done; - } - vol->mft_ni->ref_count++; - vol->mft_ni->private_data = found->dir; - found->inodes[num] = vol->mft_ni; - - //$MFTMirr - num = -1; - found = ntfs_dt_find2(root, mftmirr, (sizeof(mftmirr)/sizeof(ntfschar)) - 1, &num); - if ((!found) || (num < 0)) { - ntfs_log_debug("can't find $MFTMirr\n"); - ntfs_volume_umount2(vol, FALSE); - vol = NULL; - goto done; - } - vol->mftmirr_ni->ref_count++; - vol->mftmirr_ni->private_data = found->dir; - found->inodes[num] = vol->mftmirr_ni; - - // root directory - num = -1; - found = ntfs_dt_find2(root, dot, (sizeof(dot)/sizeof(ntfschar)) - 1, &num); - if ((!found) || (num < 0)) { - ntfs_log_debug("can't find the root directory\n"); - ntfs_volume_umount2(vol, FALSE); - vol = NULL; - goto done; - } - - vol->private_data = found->dir; - found->inodes[num] = dir->inode; - dir->inode->private_data = found; - dir->inode->ref_count = 2; - - vol->private_bmp1 = ntfs_bmp_create(vol->mft_ni, AT_BITMAP, NULL, 0); - vol->private_bmp2 = ntfs_bmp_create(vol->lcnbmp_ni, AT_DATA, NULL, 0); - - if (!vol->private_bmp1 || !vol->private_bmp2) { - ntfs_log_debug("can't find the bitmaps\n"); - ntfs_volume_umount2(vol, FALSE); - vol = NULL; - goto done; - } - -done: - return vol; -} - - -#endif /* NTFS_RICH */ - diff --git a/libntfs/win32_io.c b/libntfs/win32_io.c deleted file mode 100644 index c7a3e8e8..00000000 --- a/libntfs/win32_io.c +++ /dev/null @@ -1,1473 +0,0 @@ -/* - * win32_io.c - A stdio-like disk I/O implementation for low-level disk access - * on Win32. Can access an NTFS volume while it is mounted. - * Part of the Linux-NTFS project. - * - * Copyright (c) 2003-2004 Lode Leroy - * Copyright (c) 2003-2006 Anton Altaparmakov - * Copyright (c) 2004-2005 Yuval Fledel - * - * 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 "config.h" - -#ifdef HAVE_WINDOWS_H -#include -#endif -#include - -#ifdef HAVE_STDIO_H -#include -#endif -#ifdef HAVE_CTYPE_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include -#endif - -/* Prevent volume.h from being be loaded, as it conflicts with winnt.h. */ -#define _NTFS_VOLUME_H -struct ntfs_volume; -typedef struct ntfs_volume ntfs_volume; - -#include "debug.h" -#include "types.h" -#include "device.h" - -#ifndef NTFS_BLOCK_SIZE -#define NTFS_BLOCK_SIZE 512 -#define NTFS_BLOCK_SIZE_BITS 9 -#endif - -#ifndef IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS -#define IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS 5636096 -#endif - -/* Windows 2k+ imports. */ -typedef HANDLE (WINAPI *LPFN_FINDFIRSTVOLUME)(LPTSTR, DWORD); -typedef BOOL (WINAPI *LPFN_FINDNEXTVOLUME)(HANDLE, LPTSTR, DWORD); -typedef BOOL (WINAPI *LPFN_FINDVOLUMECLOSE)(HANDLE); -typedef BOOL (WINAPI *LPFN_SETFILEPOINTEREX)(HANDLE, LARGE_INTEGER, - PLARGE_INTEGER, DWORD); - -static LPFN_FINDFIRSTVOLUME fnFindFirstVolume = NULL; -static LPFN_FINDNEXTVOLUME fnFindNextVolume = NULL; -static LPFN_FINDVOLUMECLOSE fnFindVolumeClose = NULL; -static LPFN_SETFILEPOINTEREX fnSetFilePointerEx = NULL; - -#ifdef UNICODE -#define FNPOSTFIX "W" -#else -#define FNPOSTFIX "A" -#endif - -/** - * struct win32_fd - - */ -typedef struct { - HANDLE handle; - s64 pos; /* Logical current position on the volume. */ - s64 part_start; - s64 part_length; - int part_hidden_sectors; - s64 geo_size, geo_cylinders; - DWORD geo_sectors, geo_heads; - HANDLE vol_handle; -} win32_fd; - -/** - * ntfs_w32error_to_errno - convert a win32 error code to the unix one - * @w32error: the win32 error code - * - * Limited to a relatively small but useful number of codes. - */ -static int ntfs_w32error_to_errno(unsigned int w32error) -{ - ntfs_log_trace("Converting w32error 0x%x.\n",w32error); - switch (w32error) { - case ERROR_INVALID_FUNCTION: - return EBADRQC; - case ERROR_FILE_NOT_FOUND: - case ERROR_PATH_NOT_FOUND: - case ERROR_INVALID_NAME: - return ENOENT; - case ERROR_TOO_MANY_OPEN_FILES: - return EMFILE; - case ERROR_ACCESS_DENIED: - return EACCES; - case ERROR_INVALID_HANDLE: - return EBADF; - case ERROR_NOT_ENOUGH_MEMORY: - return ENOMEM; - case ERROR_OUTOFMEMORY: - return ENOSPC; - case ERROR_INVALID_DRIVE: - case ERROR_BAD_UNIT: - return ENODEV; - case ERROR_WRITE_PROTECT: - return EROFS; - case ERROR_NOT_READY: - case ERROR_SHARING_VIOLATION: - return EBUSY; - case ERROR_BAD_COMMAND: - return EINVAL; - case ERROR_SEEK: - case ERROR_NEGATIVE_SEEK: - return ESPIPE; - case ERROR_NOT_SUPPORTED: - return EOPNOTSUPP; - case ERROR_BAD_NETPATH: - return ENOSHARE; - default: - /* generic message */ - return ENOMSG; - } -} - -/** - * libntfs_SetFilePointerEx - emulation for SetFilePointerEx() - * - * We use this to emulate SetFilePointerEx() when it is not present. This can - * happen since SetFilePointerEx() only exists in Win2k+. - */ -static BOOL WINAPI libntfs_SetFilePointerEx(HANDLE hFile, - LARGE_INTEGER liDistanceToMove, - PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod) -{ - liDistanceToMove.LowPart = SetFilePointer(hFile, - liDistanceToMove.LowPart, &liDistanceToMove.HighPart, - dwMoveMethod); - if (liDistanceToMove.LowPart == INVALID_SET_FILE_POINTER && - GetLastError() != NO_ERROR) { - if (lpNewFilePointer) - lpNewFilePointer->QuadPart = -1; - return FALSE; - } - if (lpNewFilePointer) - lpNewFilePointer->QuadPart = liDistanceToMove.QuadPart; - return TRUE; -} - -/** - * ntfs_device_win32_init_imports - initialize the function pointers - * - * The Find*Volume and SetFilePointerEx functions exist only on win2k+, as such - * we cannot just staticly import them. - * - * This function initializes the imports if the functions do exist and in the - * SetFilePointerEx case, we emulate the function ourselves if it is not - * present. - * - * Note: The values are cached, do be afraid to run it more than once. - */ -static void ntfs_device_win32_init_imports(void) -{ - HMODULE kernel32 = GetModuleHandle("kernel32"); - if (!kernel32) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("kernel32.dll could not be imported.\n"); - } - if (!fnSetFilePointerEx) { - if (kernel32) - fnSetFilePointerEx = (LPFN_SETFILEPOINTEREX) - GetProcAddress(kernel32, - "SetFilePointerEx"); - /* - * If we did not get kernel32.dll or it is not Win2k+, emulate - * SetFilePointerEx(). - */ - if (!fnSetFilePointerEx) { - ntfs_log_debug("SetFilePonterEx() not found in " - "kernel32.dll: Enabling emulation.\n"); - fnSetFilePointerEx = libntfs_SetFilePointerEx; - } - } - /* Cannot do lookups if we could not get kernel32.dll... */ - if (!kernel32) - return; - if (!fnFindFirstVolume) - fnFindFirstVolume = (LPFN_FINDFIRSTVOLUME) - GetProcAddress(kernel32, "FindFirstVolume" - FNPOSTFIX); - if (!fnFindNextVolume) - fnFindNextVolume = (LPFN_FINDNEXTVOLUME) - GetProcAddress(kernel32, "FindNextVolume" - FNPOSTFIX); - if (!fnFindVolumeClose) - fnFindVolumeClose = (LPFN_FINDVOLUMECLOSE) - GetProcAddress(kernel32, "FindVolumeClose"); -} - -/** - * ntfs_device_unix_status_flags_to_win32 - convert unix->win32 open flags - * @flags: unix open status flags - * - * Supported flags are O_RDONLY, O_WRONLY and O_RDWR. - */ -static __inline__ int ntfs_device_unix_status_flags_to_win32(int flags) -{ - int win_mode; - - switch (flags & O_ACCMODE) { - case O_RDONLY: - win_mode = FILE_READ_DATA; - break; - case O_WRONLY: - win_mode = FILE_WRITE_DATA; - break; - case O_RDWR: - win_mode = FILE_READ_DATA | FILE_WRITE_DATA; - break; - default: - /* error */ - ntfs_log_trace("Unknown status flags.\n"); - win_mode = 0; - } - return win_mode; -} - - -/** - * ntfs_device_win32_simple_open_file - just open a file via win32 API - * @filename: name of the file to open - * @handle: pointer the a HANDLE in which to put the result - * @flags: unix open status flags - * @locking: will the function gain an exclusive lock on the file? - * - * Supported flags are O_RDONLY, O_WRONLY and O_RDWR. - * - * Return 0 if o.k. - * -1 if not, and errno set. In this case handle is trashed. - */ -static int ntfs_device_win32_simple_open_file(const char *filename, - HANDLE *handle, int flags, BOOL locking) -{ - *handle = CreateFile(filename, - ntfs_device_unix_status_flags_to_win32(flags), - locking ? 0 : (FILE_SHARE_WRITE | FILE_SHARE_READ), - NULL, OPEN_EXISTING, 0, NULL); - if (*handle == INVALID_HANDLE_VALUE) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("CreateFile(%s) failed.\n", filename); - return -1; - } - return 0; -} - -/** - * ntfs_device_win32_lock - lock the volume - * @handle: a win32 HANDLE for a volume to lock - * - * Locking a volume means no one can access its contents. - * Exiting the process automatically unlocks the volume, except in old NT4s. - * - * Return 0 if o.k. - * -1 if not, and errno set. - */ -static int ntfs_device_win32_lock(HANDLE handle) -{ - DWORD i; - - if (!DeviceIoControl(handle, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &i, - NULL)) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("Couldn't lock volume.\n"); - return -1; - } - ntfs_log_debug("Volume locked.\n"); - return 0; -} - -/** - * ntfs_device_win32_unlock - unlock the volume - * @handle: the win32 HANDLE which the volume was locked with - * - * Return 0 if o.k. - * -1 if not, and errno set. - */ -static int ntfs_device_win32_unlock(HANDLE handle) -{ - DWORD i; - - if (!DeviceIoControl(handle, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &i, - NULL)) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("Couldn't unlock volume.\n"); - return -1; - } - ntfs_log_debug("Volume unlocked.\n"); - return 0; -} - -/** - * ntfs_device_win32_dismount - dismount a volume - * @handle: a win32 HANDLE for a volume to dismount - * - * Dismounting means the system will refresh the volume in the first change it - * gets. Usefull after altering the file structures. - * The volume must be locked by the current process while dismounting. - * A side effect is that the volume is also unlocked, but you must not rely om - * this. - * - * Return 0 if o.k. - * -1 if not, and errno set. - */ -static int ntfs_device_win32_dismount(HANDLE handle) -{ - DWORD i; - - if (!DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, - &i, NULL)) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("Couldn't dismount volume.\n"); - return -1; - } - ntfs_log_debug("Volume dismounted.\n"); - return 0; -} - -/** - * ntfs_device_win32_getsize - get file size via win32 API - * @handle: pointer the file HANDLE obtained via open - * - * Only works on ordinary files. - * - * Return The file size if o.k. - * -1 if not, and errno set. - */ -static s64 ntfs_device_win32_getsize(HANDLE handle) -{ - DWORD loword, hiword; - - loword = GetFileSize(handle, &hiword); - if (loword == INVALID_FILE_SIZE) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("Couldn't get file size.\n"); - return -1; - } - return ((s64)hiword << 32) + loword; -} - -/** - * ntfs_device_win32_getdisklength - get disk size via win32 API - * @handle: pointer the file HANDLE obtained via open - * @argp: pointer to result buffer - * - * Only works on PhysicalDriveX type handles. - * - * Return The disk size if o.k. - * -1 if not, and errno set. - */ -static s64 ntfs_device_win32_getdisklength(HANDLE handle) -{ - GET_LENGTH_INFORMATION buf; - DWORD i; - - if (!DeviceIoControl(handle, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &buf, - sizeof(buf), &i, NULL)) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("Couldn't get disk length.\n"); - return -1; - } - ntfs_log_debug("Disk length: %lld.\n", buf.Length.QuadPart); - return buf.Length.QuadPart; -} - -/** - * ntfs_device_win32_getntfssize - get NTFS volume size via win32 API - * @handle: pointer the file HANDLE obtained via open - * @argp: pointer to result buffer - * - * Only works on NTFS volume handles. - * An annoying bug in windows is that an NTFS volume does not occupy the entire - * partition, namely not the last sector (which holds the backup boot sector, - * and normally not interesting). - * Use this function to get the length of the accessible space through a given - * volume handle. - * - * Return The volume size if o.k. - * -1 if not, and errno set. - */ -static s64 ntfs_device_win32_getntfssize(HANDLE handle) -{ - s64 rvl; -#ifdef FSCTL_GET_NTFS_VOLUME_DATA - DWORD i; - NTFS_VOLUME_DATA_BUFFER buf; - - if (!DeviceIoControl(handle, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0, &buf, - sizeof(buf), &i, NULL)) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("Couldn't get NTFS volume length.\n"); - return -1; - } - rvl = buf.NumberSectors.QuadPart * buf.BytesPerSector; - ntfs_log_debug("NTFS volume length: 0x%llx.\n", (long long)rvl); -#else - errno = EINVAL; - rvl = -1; -#endif - return rvl; -} - -/** - * ntfs_device_win32_getgeo - get CHS information of a drive - * @handle: an open handle to the PhysicalDevice - * @fd: a win_fd structure that will be filled - * - * Return 0 if o.k. - * -1 if not, and errno set. - * - * In Windows NT+: fills size, sectors, and cylinders and sets heads to -1. - * In Windows XP+: fills size, sectors, cylinders, and heads. - * - * Note: In pre XP, this requires write permission, even though nothing is - * actually written. - * - * If fails, sets sectors, cylinders, heads, and size to -1. - */ -static int ntfs_device_win32_getgeo(HANDLE handle, win32_fd *fd) -{ - DWORD i; - BOOL rvl; - BYTE b[sizeof(DISK_GEOMETRY) + sizeof(DISK_PARTITION_INFO) + - sizeof(DISK_DETECTION_INFO) + 512]; - - rvl = DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, - 0, &b, sizeof(b), &i, NULL); - if (rvl) { - ntfs_log_debug("GET_DRIVE_GEOMETRY_EX detected.\n"); - DISK_DETECTION_INFO *ddi = (PDISK_DETECTION_INFO) - (((PBYTE)(&((PDISK_GEOMETRY_EX)b)->Data)) + - (((PDISK_PARTITION_INFO) - (&((PDISK_GEOMETRY_EX)b)->Data))-> - SizeOfPartitionInfo)); - fd->geo_cylinders = ((DISK_GEOMETRY*)&b)->Cylinders.QuadPart; - fd->geo_sectors = ((DISK_GEOMETRY*)&b)->SectorsPerTrack; - fd->geo_size = ((DISK_GEOMETRY_EX*)&b)->DiskSize.QuadPart; - switch (ddi->DetectionType) { - case DetectInt13: - fd->geo_cylinders = ddi->Int13.MaxCylinders; - fd->geo_sectors = ddi->Int13.SectorsPerTrack; - fd->geo_heads = ddi->Int13.MaxHeads; - return 0; - case DetectExInt13: - fd->geo_cylinders = ddi->ExInt13.ExCylinders; - fd->geo_sectors = ddi->ExInt13.ExSectorsPerTrack; - fd->geo_heads = ddi->ExInt13.ExHeads; - return 0; - case DetectNone: - default: - break; - } - } else - fd->geo_heads = -1; - rvl = DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, - &b, sizeof(b), &i, NULL); - if (rvl) { - ntfs_log_debug("GET_DRIVE_GEOMETRY detected.\n"); - fd->geo_cylinders = ((DISK_GEOMETRY*)&b)->Cylinders.QuadPart; - fd->geo_sectors = ((DISK_GEOMETRY*)&b)->SectorsPerTrack; - fd->geo_size = fd->geo_cylinders * fd->geo_sectors * - ((DISK_GEOMETRY*)&b)->TracksPerCylinder * - ((DISK_GEOMETRY*)&b)->BytesPerSector; - return 0; - } - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("Couldn't retrieve disk geometry.\n"); - fd->geo_cylinders = -1; - fd->geo_sectors = -1; - fd->geo_size = -1; - return -1; -} - -/** - * ntfs_device_win32_open_file - open a file via win32 API - * @filename: name of the file to open - * @fd: pointer to win32 file device in which to put the result - * @flags: unix open status flags - * - * Return 0 if o.k. - * -1 if not, and errno set. - */ -static __inline__ int ntfs_device_win32_open_file(char *filename, win32_fd *fd, - int flags) -{ - HANDLE handle; - - if (ntfs_device_win32_simple_open_file(filename, &handle, flags, - FALSE)) { - /* open error */ - return -1; - } - /* fill fd */ - fd->handle = handle; - fd->part_start = 0; - fd->part_length = ntfs_device_win32_getsize(handle); - fd->pos = 0; - fd->part_hidden_sectors = -1; - fd->geo_size = -1; /* used as a marker that this is a file */ - fd->vol_handle = INVALID_HANDLE_VALUE; - return 0; -} - -/** - * ntfs_device_win32_open_drive - open a drive via win32 API - * @drive_id: drive to open - * @fd: pointer to win32 file device in which to put the result - * @flags: unix open status flags - * - * return 0 if o.k. - * -1 if not, and errno set. - */ -static __inline__ int ntfs_device_win32_open_drive(int drive_id, win32_fd *fd, - int flags) -{ - HANDLE handle; - int err; - char filename[MAX_PATH]; - - sprintf(filename, "\\\\.\\PhysicalDrive%d", drive_id); - if ((err = ntfs_device_win32_simple_open_file(filename, &handle, flags, - TRUE))) { - /* open error */ - return err; - } - /* store the drive geometry */ - ntfs_device_win32_getgeo(handle, fd); - /* Just to be sure */ - if (fd->geo_size == -1) - fd->geo_size = ntfs_device_win32_getdisklength(handle); - /* fill fd */ - fd->handle = handle; - fd->part_start = 0; - fd->part_length = fd->geo_size; - fd->pos = 0; - fd->part_hidden_sectors = -1; - fd->vol_handle = INVALID_HANDLE_VALUE; - return 0; -} - -/** - * ntfs_device_win32_open_volume_for_partition - find and open a volume - * - * Windows NT/2k/XP handles volumes instead of partitions. - * This function gets the partition details and return an open volume handle. - * That volume is the one whose only physical location on disk is the described - * partition. - * - * The function required Windows 2k/XP, otherwise it fails (gracefully). - * - * Return success: a valid open volume handle. - * fail : INVALID_HANDLE_VALUE - */ -static HANDLE ntfs_device_win32_open_volume_for_partition(unsigned int drive_id, - s64 part_offset, s64 part_length, int flags) -{ - HANDLE vol_find_handle; - TCHAR vol_name[MAX_PATH]; - - /* Make sure all the required imports exist. */ - if (!fnFindFirstVolume || !fnFindNextVolume || !fnFindVolumeClose) { - ntfs_log_trace("Required dll imports not found.\n"); - return INVALID_HANDLE_VALUE; - } - /* Start iterating through volumes. */ - ntfs_log_trace("Entering with drive_id=%d, part_offset=%lld, " - "path_length=%lld, flags=%d.\n", drive_id, - (unsigned long long)part_offset, - (unsigned long long)part_length, flags); - vol_find_handle = fnFindFirstVolume(vol_name, MAX_PATH); - /* If a valid handle could not be aquired, reply with "don't know". */ - if (vol_find_handle == INVALID_HANDLE_VALUE) { - ntfs_log_trace("FindFirstVolume failed.\n"); - return INVALID_HANDLE_VALUE; - } - do { - int vol_name_length; - HANDLE handle; - - /* remove trailing '/' from vol_name */ -#ifdef UNICODE - vol_name_length = wcslen(vol_name); -#else - vol_name_length = strlen(vol_name); -#endif - if (vol_name_length>0) - vol_name[vol_name_length-1]=0; - - ntfs_log_debug("Processing %s.\n", vol_name); - /* open the file */ - handle = CreateFile(vol_name, - ntfs_device_unix_status_flags_to_win32(flags), - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, - OPEN_EXISTING, 0, NULL); - if (handle != INVALID_HANDLE_VALUE) { - DWORD bytesReturned; -#define EXTENTS_SIZE sizeof(VOLUME_DISK_EXTENTS) + 9 * sizeof(DISK_EXTENT) - char extents[EXTENTS_SIZE]; - - /* Check physical locations. */ - if (DeviceIoControl(handle, - IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, - NULL, 0, extents, EXTENTS_SIZE, - &bytesReturned, NULL)) { - if (((VOLUME_DISK_EXTENTS *)extents)-> - NumberOfDiskExtents == 1) { - DISK_EXTENT *extent = &(( - VOLUME_DISK_EXTENTS *) - extents)->Extents[0]; - if ((extent->DiskNumber==drive_id) && - (extent->StartingOffset. - QuadPart==part_offset) - && (extent-> - ExtentLength.QuadPart - == part_length)) { - /* - * Eureka! (Archimedes, 287 BC, - * "I have found it!") - */ - fnFindVolumeClose( - vol_find_handle); - return handle; - } - } - } - } else - ntfs_log_trace("getExtents() Failed.\n"); - } while (fnFindNextVolume(vol_find_handle, vol_name, MAX_PATH)); - /* End of iteration through volumes. */ - ntfs_log_trace("Closing, volume was not found.\n"); - fnFindVolumeClose(vol_find_handle); - return INVALID_HANDLE_VALUE; -} - -/** - * ntfs_device_win32_find_partition - locates partition details by id. - * @handle: HANDLE to the PhysicalDrive - * @partition_id: the partition number to locate - * @part_offset: pointer to where to put the offset to the partition - * @part_length: pointer to where to put the length of the partition - * @hidden_sectors: pointer to where to put the hidden sectors - * - * This function requires an open PhysicalDrive handle and a partition_id. - * If a partition with the required id is found on the supplied device, - * the partition attributes are returned back. - * - * Returns: TRUE if found, and sets the output parameters. - * FALSE if not and errno is set to the error code. - */ -static BOOL ntfs_device_win32_find_partition(HANDLE handle, DWORD partition_id, - s64 *part_offset, s64 *part_length, int *hidden_sectors) -{ - DRIVE_LAYOUT_INFORMATION *drive_layout; - unsigned int err, buf_size, part_count; - DWORD i; - - /* - * There is no way to know the required buffer, so if the ioctl fails, - * try doubling the buffer size each time until the ioctl succeeds. - */ - part_count = 8; - do { - buf_size = sizeof(DRIVE_LAYOUT_INFORMATION) + - part_count * sizeof(PARTITION_INFORMATION); - drive_layout = malloc(buf_size); - if (!drive_layout) { - errno = ENOMEM; - return FALSE; - } - if (DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL, - 0, (BYTE*)drive_layout, buf_size, &i, NULL)) - break; - err = GetLastError(); - free(drive_layout); - if (err != ERROR_INSUFFICIENT_BUFFER) { - ntfs_log_trace("GetDriveLayout failed.\n"); - errno = ntfs_w32error_to_errno(err); - return FALSE; - } - ntfs_log_debug("More than %u partitions.\n", part_count); - part_count <<= 1; - if (part_count > 512) { - ntfs_log_trace("GetDriveLayout failed: More than 512 " - "partitions?\n"); - errno = ENOBUFS; - return FALSE; - } - } while (1); - for (i = 0; i < drive_layout->PartitionCount; i++) { - if (drive_layout->PartitionEntry[i].PartitionNumber == - partition_id) { - *part_offset = drive_layout->PartitionEntry[i]. - StartingOffset.QuadPart; - *part_length = drive_layout->PartitionEntry[i]. - PartitionLength.QuadPart; - *hidden_sectors = drive_layout->PartitionEntry[i]. - HiddenSectors; - free(drive_layout); - return TRUE; - } - } - free(drive_layout); - errno = ENOENT; - return FALSE; -} - -/** - * ntfs_device_win32_open_partition - open a partition via win32 API - * @drive_id: drive to open - * @partition_id: partition to open - * @fd: win32 file device to return - * @flags: unix open status flags - * - * Return 0 if o.k. - * -1 if not, and errno set. - * - * When fails, fd contents may have not been preserved. - */ -static int ntfs_device_win32_open_partition(int drive_id, - unsigned int partition_id, win32_fd *fd, int flags) -{ - s64 part_start, part_length; - HANDLE handle; - int err, hidden_sectors; - char drive_name[MAX_PATH]; - - sprintf(drive_name, "\\\\.\\PhysicalDrive%d", drive_id); - /* Open the entire device without locking, ask questions later */ - if ((err = ntfs_device_win32_simple_open_file(drive_name, &handle, - flags, FALSE))) { - /* error */ - return err; - } - if (ntfs_device_win32_find_partition(handle, partition_id, &part_start, - &part_length, &hidden_sectors)) { - s64 tmp; - HANDLE vol_handle = ntfs_device_win32_open_volume_for_partition( - drive_id, part_start, part_length, flags); - /* Store the drive geometry. */ - ntfs_device_win32_getgeo(handle, fd); - fd->handle = handle; - fd->pos = 0; - fd->part_start = part_start; - fd->part_length = part_length; - fd->part_hidden_sectors = hidden_sectors; - tmp = ntfs_device_win32_getntfssize(vol_handle); - if (tmp > 0) - fd->geo_size = tmp; - else - fd->geo_size = fd->part_length; - if (vol_handle != INVALID_HANDLE_VALUE) { - if (((flags & O_RDWR) == O_RDWR) && - ntfs_device_win32_lock(vol_handle)) { - CloseHandle(vol_handle); - CloseHandle(handle); - return -1; - } - fd->vol_handle = vol_handle; - } else { - if ((flags & O_RDWR) == O_RDWR) { - /* Access if read-write, no volume found. */ - ntfs_log_trace("Partitions containing Spanned/" - "Mirrored volumes are not " - "supported in R/W status " - "yet.\n"); - CloseHandle(handle); - errno = EOPNOTSUPP; - return -1; - } - fd->vol_handle = INVALID_HANDLE_VALUE; - } - return 0; - } else { - ntfs_log_debug("Partition %u not found on drive %d.\n", - partition_id, drive_id); - CloseHandle(handle); - errno = ENODEV; - return -1; - } -} - -/** - * ntfs_device_win32_open - open a device - * @dev: a pointer to the NTFS_DEVICE to open - * @flags: unix open status flags - * - * @dev->d_name must hold the device name, the rest is ignored. - * Supported flags are O_RDONLY, O_WRONLY and O_RDWR. - * - * If name is in format "(hd[0-9],[0-9])" then open a partition. - * If name is in format "(hd[0-9])" then open a volume. - * Otherwise open a file. - */ -static int ntfs_device_win32_open(struct ntfs_device *dev, int flags) -{ - int drive_id = 0, numparams; - unsigned int part = 0; - char drive_char; - win32_fd fd; - int err; - - if (NDevOpen(dev)) { - errno = EBUSY; - return -1; - } - ntfs_device_win32_init_imports(); - numparams = sscanf(dev->d_name, "/dev/hd%c%u", &drive_char, &part); - drive_id = toupper(drive_char) - 'A'; - switch (numparams) { - case 0: - ntfs_log_debug("win32_open(%s) -> file.\n", dev->d_name); - err = ntfs_device_win32_open_file(dev->d_name, &fd, flags); - break; - case 1: - ntfs_log_debug("win32_open(%s) -> drive %d.\n", dev->d_name, - drive_id); - err = ntfs_device_win32_open_drive(drive_id, &fd, flags); - break; - case 2: - ntfs_log_debug("win32_open(%s) -> drive %d, part %u.\n", - dev->d_name, drive_id, part); - err = ntfs_device_win32_open_partition(drive_id, part, &fd, - flags); - break; - default: - ntfs_log_debug("win32_open(%s) -> unknwon file format.\n", - dev->d_name); - err = -1; - } - if (err) - return err; - ntfs_log_debug("win32_open(%s) -> %p, offset 0x%llx.\n", dev->d_name, - dev, fd.part_start); - /* Setup our read-only flag. */ - if ((flags & O_RDWR) != O_RDWR) - NDevSetReadOnly(dev); - dev->d_private = malloc(sizeof(win32_fd)); - memcpy(dev->d_private, &fd, sizeof(win32_fd)); - NDevSetOpen(dev); - NDevClearDirty(dev); - return 0; -} - -/** - * ntfs_device_win32_seek - change current logical file position - * @dev: ntfs device obtained via ->open - * @offset: required offset from the whence anchor - * @whence: whence anchor specifying what @offset is relative to - * - * Return the new position on the volume on success and -1 on error with errno - * set to the error code. - * - * @whence may be one of the following: - * SEEK_SET - Offset is relative to file start. - * SEEK_CUR - Offset is relative to current position. - * SEEK_END - Offset is relative to end of file. - */ -static s64 ntfs_device_win32_seek(struct ntfs_device *dev, s64 offset, - int whence) -{ - s64 abs_ofs; - win32_fd *fd = (win32_fd *)dev->d_private; - - ntfs_log_trace("seek offset = 0x%llx, whence = %d.\n", offset, whence); - switch (whence) { - case SEEK_SET: - abs_ofs = offset; - break; - case SEEK_CUR: - abs_ofs = fd->pos + offset; - break; - case SEEK_END: - /* End of partition != end of disk. */ - if (fd->part_length == -1) { - ntfs_log_trace("Position relative to end of disk not " - "implemented.\n"); - errno = EOPNOTSUPP; - return -1; - } - abs_ofs = fd->part_length + offset; - break; - default: - ntfs_log_trace("Wrong mode %d.\n", whence); - errno = EINVAL; - return -1; - } - if (abs_ofs < 0 || abs_ofs > fd->part_length) { - ntfs_log_trace("Seeking outsize seekable area.\n"); - errno = EINVAL; - return -1; - } - fd->pos = abs_ofs; - return abs_ofs; -} - -/** - * ntfs_device_win32_pio - positioned low level i/o - * @fd: win32 device descriptor obtained via ->open - * @pos: at which position to do i/o from/to - * @count: how many bytes should be transfered - * @b: source/destination buffer - * @write: TRUE if write transfer and FALSE if read transfer - * - * On success returns the number of bytes transfered (can be < @count) and on - * error returns -1 and errno set. Transfer starts from position @pos on @fd. - * - * Notes: - * - @pos, @buf, and @count must be aligned to NTFS_BLOCK_SIZE. - * - When dealing with volumes, a single call must not span both volume - * and disk extents. - * - Does not use/set @fd->pos. - */ -static s64 ntfs_device_win32_pio(win32_fd *fd, const s64 pos, - const s64 count, void *b, const BOOL write) -{ - LARGE_INTEGER li; - HANDLE handle; - DWORD bt; - BOOL res; - - ntfs_log_trace("pos = 0x%llx, count = 0x%llx, direction = %s.\n", - (long long)pos, (long long)count, write ? "write" : - "read"); - li.QuadPart = pos; - if (fd->vol_handle != INVALID_HANDLE_VALUE && pos < fd->geo_size) { - ntfs_log_debug("Transfering via vol_handle.\n"); - handle = fd->vol_handle; - } else { - ntfs_log_debug("Transfering via handle.\n"); - handle = fd->handle; - li.QuadPart += fd->part_start; - } - if (!fnSetFilePointerEx(handle, li, NULL, FILE_BEGIN)) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("SetFilePointer failed.\n"); - return -1; - } - if (write) - res = WriteFile(handle, b, count, &bt, NULL); - else - res = ReadFile(handle, b, count, &bt, NULL); - if (!res) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("%sFile() failed.\n", write ? "Write" : "Read"); - return -1; - } - return bt; -} - -/** - * ntfs_device_win32_pread_simple - positioned simple read - * @fd: win32 device descriptor obtained via ->open - * @pos: at which position to read from - * @count: how many bytes should be read - * @b: a pointer to where to put the contents - * - * On success returns the number of bytes read (can be < @count) and on error - * returns -1 and errno set. Read starts from position @pos. - * - * Notes: - * - @pos, @buf, and @count must be aligned to NTFS_BLOCK_SIZE. - * - When dealing with volumes, a single call must not span both volume - * and disk extents. - * - Does not use/set @fd->pos. - */ -static inline s64 ntfs_device_win32_pread_simple(win32_fd *fd, const s64 pos, - const s64 count, void *b) -{ - return ntfs_device_win32_pio(fd, pos, count, b, FALSE); -} - -/** - * ntfs_device_win32_read - read bytes from an ntfs device - * @dev: ntfs device obtained via ->open - * @b: pointer to where to put the contents - * @count: how many bytes should be read - * - * On success returns the number of bytes actually read (can be < @count). - * On error returns -1 with errno set. - */ -static s64 ntfs_device_win32_read(struct ntfs_device *dev, void *b, s64 count) -{ - s64 old_pos, to_read, i, br = 0; - win32_fd *fd = (win32_fd *)dev->d_private; - BYTE *alignedbuffer; - int old_ofs, ofs; - - old_pos = fd->pos; - old_ofs = ofs = old_pos & (NTFS_BLOCK_SIZE - 1); - to_read = (ofs + count + NTFS_BLOCK_SIZE - 1) & - ~(s64)(NTFS_BLOCK_SIZE - 1); - /* Impose maximum of 2GB to be on the safe side. */ - if (to_read > 0x80000000) { - int delta = to_read - count; - to_read = 0x80000000; - count = to_read - delta; - } - ntfs_log_trace("fd = %p, b = %p, count = 0x%llx, pos = 0x%llx, " - "ofs = %i, to_read = 0x%llx.\n", fd, b, - (long long)count, (long long)old_pos, ofs, - (long long)to_read); - if (!((unsigned long)b & (NTFS_BLOCK_SIZE - 1)) && !old_ofs && - !(count & (NTFS_BLOCK_SIZE - 1))) - alignedbuffer = b; - else { - alignedbuffer = (BYTE *)VirtualAlloc(NULL, to_read, MEM_COMMIT, - PAGE_READWRITE); - if (!alignedbuffer) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("VirtualAlloc failed for read.\n"); - return -1; - } - } - if (fd->vol_handle != INVALID_HANDLE_VALUE && old_pos < fd->geo_size) { - s64 vol_to_read = fd->geo_size - old_pos; - if (count > vol_to_read) { - br = ntfs_device_win32_pread_simple(fd, - old_pos & ~(s64)(NTFS_BLOCK_SIZE - 1), - ofs + vol_to_read, alignedbuffer); - if (br == -1) - goto read_error; - to_read -= br; - if (br < ofs) { - br = 0; - goto read_partial; - } - br -= ofs; - fd->pos += br; - ofs = fd->pos & (NTFS_BLOCK_SIZE - 1); - if (br != vol_to_read) - goto read_partial; - } - } - i = ntfs_device_win32_pread_simple(fd, - fd->pos & ~(s64)(NTFS_BLOCK_SIZE - 1), to_read, - alignedbuffer + br); - if (i == -1) { - if (br) - goto read_partial; - goto read_error; - } - if (i < ofs) - goto read_partial; - i -= ofs; - br += i; - if (br > count) - br = count; - fd->pos = old_pos + br; -read_partial: - if (alignedbuffer != b) { - memcpy((void*)b, alignedbuffer + old_ofs, br); - VirtualFree(alignedbuffer, 0, MEM_RELEASE); - } - return br; -read_error: - if (alignedbuffer != b) - VirtualFree(alignedbuffer, 0, MEM_RELEASE); - return -1; -} - -/** - * ntfs_device_win32_close - close an open ntfs deivce - * @dev: ntfs device obtained via ->open - * - * Return 0 if o.k. - * -1 if not, and errno set. Note if error fd->vol_handle is trashed. - */ -static int ntfs_device_win32_close(struct ntfs_device *dev) -{ - win32_fd *fd = (win32_fd *)dev->d_private; - BOOL rvl; - - ntfs_log_trace("Closing device %p.\n", dev); - if (!NDevOpen(dev)) { - errno = EBADF; - return -1; - } - if (fd->vol_handle != INVALID_HANDLE_VALUE) { - if (!NDevReadOnly(dev)) { - ntfs_device_win32_dismount(fd->vol_handle); - ntfs_device_win32_unlock(fd->vol_handle); - } - if (!CloseHandle(fd->vol_handle)) - ntfs_log_trace("CloseHandle() failed for volume.\n"); - } - rvl = CloseHandle(fd->handle); - free(fd); - if (!rvl) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("CloseHandle() failed.\n"); - return -1; - } - return 0; -} - -/** - * ntfs_device_win32_sync - flush write buffers to disk - * @dev: ntfs device obtained via ->open - * - * Return 0 if o.k. - * -1 if not, and errno set. - * - * Note: Volume syncing works differently in windows. - * Disk cannot be synced in windows. - */ -static int ntfs_device_win32_sync(struct ntfs_device *dev) -{ - int err = 0; - BOOL to_clear = TRUE; - - if (!NDevReadOnly(dev) && NDevDirty(dev)) { - win32_fd *fd = (win32_fd *)dev->d_private; - - if ((fd->vol_handle != INVALID_HANDLE_VALUE) && - !FlushFileBuffers(fd->vol_handle)) { - to_clear = FALSE; - err = ntfs_w32error_to_errno(GetLastError()); - } - if (!FlushFileBuffers(fd->handle)) { - to_clear = FALSE; - if (!err) - err = ntfs_w32error_to_errno(GetLastError()); - } - if (!to_clear) { - ntfs_log_trace("Could not sync.\n"); - errno = err; - return -1; - } - NDevClearDirty(dev); - } - return 0; -} - -/** - * ntfs_device_win32_pwrite_simple - positioned simple write - * @fd: win32 device descriptor obtained via ->open - * @pos: at which position to write to - * @count: how many bytes should be written - * @b: a pointer to the data to write - * - * On success returns the number of bytes written and on error returns -1 and - * errno set. Write starts from position @pos. - * - * Notes: - * - @pos, @buf, and @count must be aligned to NTFS_BLOCK_SIZE. - * - When dealing with volumes, a single call must not span both volume - * and disk extents. - * - Does not use/set @fd->pos. - */ -static inline s64 ntfs_device_win32_pwrite_simple(win32_fd *fd, const s64 pos, - const s64 count, const void *b) -{ - return ntfs_device_win32_pio(fd, pos, count, (void *)b, TRUE); -} - -/** - * ntfs_device_win32_write - write bytes to an ntfs device - * @dev: ntfs device obtained via ->open - * @b: pointer to the data to write - * @count: how many bytes should be written - * - * On success returns the number of bytes actually written. - * On error returns -1 with errno set. - */ -static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, - s64 count) -{ - s64 old_pos, to_write, i, bw = 0; - win32_fd *fd = (win32_fd *)dev->d_private; - BYTE *alignedbuffer; - int old_ofs, ofs; - - old_pos = fd->pos; - old_ofs = ofs = old_pos & (NTFS_BLOCK_SIZE - 1); - to_write = (ofs + count + NTFS_BLOCK_SIZE - 1) & - ~(s64)(NTFS_BLOCK_SIZE - 1); - /* Impose maximum of 2GB to be on the safe side. */ - if (to_write > 0x80000000) { - int delta = to_write - count; - to_write = 0x80000000; - count = to_write - delta; - } - ntfs_log_trace("fd = %p, b = %p, count = 0x%llx, pos = 0x%llx, " - "ofs = %i, to_write = 0x%llx.\n", fd, b, - (long long)count, (long long)old_pos, ofs, - (long long)to_write); - if (NDevReadOnly(dev)) { - ntfs_log_trace("Can't write on a R/O device.\n"); - errno = EROFS; - return -1; - } - if (!count) - return 0; - NDevSetDirty(dev); - if (!((unsigned long)b & (NTFS_BLOCK_SIZE - 1)) && !old_ofs && - !(count & (NTFS_BLOCK_SIZE - 1))) - alignedbuffer = (BYTE *)b; - else { - s64 end; - - alignedbuffer = (BYTE *)VirtualAlloc(NULL, to_write, - MEM_COMMIT, PAGE_READWRITE); - if (!alignedbuffer) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("VirtualAlloc failed for write.\n"); - return -1; - } - /* Read first sector if start of write not sector aligned. */ - if (ofs) { - i = ntfs_device_win32_pread_simple(fd, - old_pos & ~(s64)(NTFS_BLOCK_SIZE - 1), - NTFS_BLOCK_SIZE, alignedbuffer); - if (i != NTFS_BLOCK_SIZE) { - if (i >= 0) - errno = EIO; - goto write_error; - } - } - /* - * Read last sector if end of write not sector aligned and last - * sector is either not the same as the first sector or it is - * the same as the first sector but this has not been read in - * yet, i.e. the start of the write is sector aligned. - */ - end = old_pos + count; - if ((end & (NTFS_BLOCK_SIZE - 1)) && - ((to_write > NTFS_BLOCK_SIZE) || !ofs)) { - i = ntfs_device_win32_pread_simple(fd, - end & ~(s64)(NTFS_BLOCK_SIZE - 1), - NTFS_BLOCK_SIZE, alignedbuffer + - to_write - NTFS_BLOCK_SIZE); - if (i != NTFS_BLOCK_SIZE) { - if (i >= 0) - errno = EIO; - goto write_error; - } - } - /* Copy the data to be written into @alignedbuffer. */ - memcpy(alignedbuffer + ofs, b, count); - } - if (fd->vol_handle != INVALID_HANDLE_VALUE && old_pos < fd->geo_size) { - s64 vol_to_write = fd->geo_size - old_pos; - if (count > vol_to_write) { - bw = ntfs_device_win32_pwrite_simple(fd, - old_pos & ~(s64)(NTFS_BLOCK_SIZE - 1), - ofs + vol_to_write, alignedbuffer); - if (bw == -1) - goto write_error; - to_write -= bw; - if (bw < ofs) { - bw = 0; - goto write_partial; - } - bw -= ofs; - fd->pos += bw; - ofs = fd->pos & (NTFS_BLOCK_SIZE - 1); - if (bw != vol_to_write) - goto write_partial; - } - } - i = ntfs_device_win32_pwrite_simple(fd, - fd->pos & ~(s64)(NTFS_BLOCK_SIZE - 1), to_write, - alignedbuffer + bw); - if (i == -1) { - if (bw) - goto write_partial; - goto write_error; - } - if (i < ofs) - goto write_partial; - i -= ofs; - bw += i; - if (bw > count) - bw = count; - fd->pos = old_pos + bw; -write_partial: - if (alignedbuffer != b) - VirtualFree(alignedbuffer, 0, MEM_RELEASE); - return bw; -write_error: - bw = -1; - goto write_partial; -} - -/** - * ntfs_device_win32_stat - get a unix-like stat structure for an ntfs device - * @dev: ntfs device obtained via ->open - * @buf: pointer to the stat structure to fill - * - * Note: Only st_mode, st_size, and st_blocks are filled. - * - * Return 0 if o.k. - * -1 if not and errno set. in this case handle is trashed. - */ -static int ntfs_device_win32_stat(struct ntfs_device *dev, struct stat *buf) -{ - win32_fd *fd = (win32_fd *)dev->d_private; - mode_t st_mode; - - switch (GetFileType(fd->handle)) { - case FILE_TYPE_CHAR: - st_mode = S_IFCHR; - break; - case FILE_TYPE_DISK: - st_mode = S_IFBLK; - break; - case FILE_TYPE_PIPE: - st_mode = S_IFIFO; - break; - default: - st_mode = 0; - } - memset(buf, 0, sizeof(struct stat)); - buf->st_mode = st_mode; - buf->st_size = fd->part_length; - if (buf->st_size != -1) - buf->st_blocks = buf->st_size >> 9; - else - buf->st_size = 0; - return 0; -} - -/** - * ntfs_win32_hdio_getgeo - get drive geometry - * @dev: ntfs device obtained via ->open - * @argp: pointer to where to put the output - * - * Note: Works on windows NT/2k/XP only. - * - * Return 0 if o.k. - * -1 if not, and errno set. Note if error fd->handle is trashed. - */ -static __inline__ int ntfs_win32_hdio_getgeo(struct ntfs_device *dev, - struct hd_geometry *argp) -{ - win32_fd *fd = (win32_fd *)dev->d_private; - - argp->heads = fd->geo_heads; - argp->sectors = fd->geo_sectors; - argp->cylinders = fd->geo_cylinders; - argp->start = fd->part_hidden_sectors; - return 0; -} - -/** - * ntfs_win32_blksszget - get block device sector size - * @dev: ntfs device obtained via ->open - * @argp: pointer to where to put the output - * - * Note: Works on windows NT/2k/XP only. - * - * Return 0 if o.k. - * -1 if not, and errno set. Note if error fd->handle is trashed. - */ -static __inline__ int ntfs_win32_blksszget(struct ntfs_device *dev,int *argp) -{ - win32_fd *fd = (win32_fd *)dev->d_private; - DWORD bytesReturned; - DISK_GEOMETRY dg; - - if (DeviceIoControl(fd->handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, - &dg, sizeof(DISK_GEOMETRY), &bytesReturned, NULL)) { - /* success */ - *argp = dg.BytesPerSector; - return 0; - } - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("GET_DRIVE_GEOMETRY failed.\n"); - return -1; -} - -static int ntfs_device_win32_ioctl(struct ntfs_device *dev, int request, - void *argp) -{ - win32_fd *fd = (win32_fd *)dev->d_private; - - ntfs_log_trace("win32_ioctl(%d) called.\n", request); - switch (request) { -#if defined(BLKGETSIZE) - case BLKGETSIZE: - ntfs_log_debug("BLKGETSIZE detected.\n"); - if (fd->part_length >= 0) { - *(int *)argp = (int)(fd->part_length / 512); - return 0; - } - errno = EOPNOTSUPP; - return -1; -#endif -#if defined(BLKGETSIZE64) - case BLKGETSIZE64: - ntfs_log_debug("BLKGETSIZE64 detected.\n"); - if (fd->part_length >= 0) { - *(s64 *)argp = fd->part_length; - return 0; - } - errno = EOPNOTSUPP; - return -1; -#endif -#ifdef HDIO_GETGEO - case HDIO_GETGEO: - ntfs_log_debug("HDIO_GETGEO detected.\n"); - return ntfs_win32_hdio_getgeo(dev, (struct hd_geometry *)argp); -#endif -#ifdef BLKSSZGET - case BLKSSZGET: - ntfs_log_debug("BLKSSZGET detected.\n"); - return ntfs_win32_blksszget(dev, (int *)argp); -#endif -#ifdef BLKBSZSET - case BLKBSZSET: - ntfs_log_debug("BLKBSZSET detected.\n"); - /* Nothing to do on Windows. */ - return 0; -#endif - default: - ntfs_log_debug("unimplemented ioctl %d.\n", request); - errno = EOPNOTSUPP; - return -1; - } -} - -static s64 ntfs_device_win32_pread(struct ntfs_device *dev, void *b, - s64 count, s64 offset) -{ - return ntfs_pread(dev, offset, count, b); -} - -static s64 ntfs_device_win32_pwrite(struct ntfs_device *dev, const void *b, - s64 count, s64 offset) -{ - return ntfs_pwrite(dev, offset, count, b); -} - -struct ntfs_device_operations ntfs_device_win32_io_ops = { - .open = ntfs_device_win32_open, - .close = ntfs_device_win32_close, - .seek = ntfs_device_win32_seek, - .read = ntfs_device_win32_read, - .write = ntfs_device_win32_write, - .pread = ntfs_device_win32_pread, - .pwrite = ntfs_device_win32_pwrite, - .sync = ntfs_device_win32_sync, - .stat = ntfs_device_win32_stat, - .ioctl = ntfs_device_win32_ioctl -};