diff options
Diffstat (limited to 'usr/src/lib/libntfs/common')
58 files changed, 0 insertions, 30653 deletions
diff --git a/usr/src/lib/libntfs/common/include/ntfs/attrib.h b/usr/src/lib/libntfs/common/include/ntfs/attrib.h deleted file mode 100644 index dcca5427ea..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/attrib.h +++ /dev/null @@ -1,382 +0,0 @@ -/* - * attrib.h - Exports for attribute handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2004 Anton Altaparmakov - * Copyright (c) 2004-2007 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 "list.h" -#include "types.h" -#include "inode.h" -#include "unistr.h" -#include "runlist.h" -#include "volume.h" -#include "debug.h" -#include "logging.h" -#include "crypto.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 - * @crypto: (valid only for encrypted) see description below - * - * 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. - * - * @crypto points to private structure of crypto code. You should not access - * fields of this structure, but you can check whether it is NULL or not. If it - * is not NULL, then we successfully obtained FEK (File Encryption Key) and - * ntfs_attr_p{read,write} calls probably would succeed. If it is NULL, then we - * failed to obtain FEK (do not have corresponding PFX file, wrong password, - * etc..) or library was compiled without crypto support. Attribute size can be - * changed without knowledge of FEK, so you can use ntfs_attr_truncate in any - * case. - * NOTE: This field valid only if attribute encrypted (eg., NAttrEncrypted - * returns non-zero). - */ -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; - ntfs_crypto_attr *crypto; - struct list_head list_entry; - int nr_references; -}; - -/** - * 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) ? 1 : 0; \ - 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) - -#ifndef __sun -/** - * 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; -#endif /* __sun */ - -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_runlist_range(ntfs_attr *na, VCN from_vcn, VCN to_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, BOOL sparse); -extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize); - -extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, - ntfschar *name, u32 name_len); - -static __inline__ ntfschar *ntfs_attr_get_name(ATTR_RECORD *attr) -{ - return (ntfschar*)((u8*)attr + le16_to_cpu(attr->name_offset)); -} - -// 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/usr/src/lib/libntfs/common/include/ntfs/attrlist.h b/usr/src/lib/libntfs/common/include/ntfs/attrlist.h deleted file mode 100644 index ff450d09bf..0000000000 --- a/usr/src/lib/libntfs/common/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->u.base_ni); - else - NInoAttrListSetDirty(ni); -} - -#endif /* defined _NTFS_ATTRLIST_H */ diff --git a/usr/src/lib/libntfs/common/include/ntfs/bitmap.h b/usr/src/lib/libntfs/common/include/ntfs/bitmap.h deleted file mode 100644 index f6d16f1923..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/bitmap.h +++ /dev/null @@ -1,134 +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); -} - -#endif /* defined _NTFS_BITMAP_H */ diff --git a/usr/src/lib/libntfs/common/include/ntfs/bootsect.h b/usr/src/lib/libntfs/common/include/ntfs/bootsect.h deleted file mode 100644 index af0da7a945..0000000000 --- a/usr/src/lib/libntfs/common/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/usr/src/lib/libntfs/common/include/ntfs/collate.h b/usr/src/lib/libntfs/common/include/ntfs/collate.h deleted file mode 100644 index 1c00ebd77e..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/collate.h +++ /dev/null @@ -1,38 +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) - -extern BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr); - -extern int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr, - const void *data1, size_t data1_len, - const void *data2, size_t data2_len); - -#endif /* _NTFS_COLLATE_H */ diff --git a/usr/src/lib/libntfs/common/include/ntfs/compat.h b/usr/src/lib/libntfs/common/include/ntfs/compat.h deleted file mode 100644 index 7c1f5f11fe..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/compat.h +++ /dev/null @@ -1,58 +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 */ - -#ifdef __sun -#define __attribute__(X) /*nothing*/ -#endif /* __sun */ - -#endif /* defined _NTFS_COMPAT_H */ - diff --git a/usr/src/lib/libntfs/common/include/ntfs/compress.h b/usr/src/lib/libntfs/common/include/ntfs/compress.h deleted file mode 100644 index 93df37afc8..0000000000 --- a/usr/src/lib/libntfs/common/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/usr/src/lib/libntfs/common/include/ntfs/config.h b/usr/src/lib/libntfs/common/include/ntfs/config.h deleted file mode 100644 index 4c6c18efff..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/config.h +++ /dev/null @@ -1,313 +0,0 @@ -/* config.h. Generated from config.h.in by configure. */ -/* config.h.in. Generated from configure.ac by autoheader. */ - -/* Define this to 1 if you want to enable support of encrypted files in - libntfs and utilities. */ -/* #undef ENABLE_CRYPTO */ - -/* Define this to 1 if you want to enable generation of DCE compliant UUIDs. - */ -#define ENABLE_UUID 1 - -/* Define to 1 if you have the `atexit' function. */ -#define HAVE_ATEXIT 1 - -/* Define to 1 if you have the `basename' function. */ -#define HAVE_BASENAME 1 - -/* Define to 1 if you have the <byteswap.h> header file. */ -/* #undef HAVE_BYTESWAP_H */ - -/* Define to 1 if you have the <ctype.h> header file. */ -#define HAVE_CTYPE_H 1 - -/* Define to 1 if you have the <dlfcn.h> header file. */ -#define HAVE_DLFCN_H 1 - -/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ -/* #undef HAVE_DOPRNT */ - -/* Define to 1 if you have the `dup2' function. */ -#define HAVE_DUP2 1 - -/* Define to 1 if you have the <endian.h> header file. */ -/* #undef HAVE_ENDIAN_H */ - -/* Define to 1 if you have the <errno.h> header file. */ -#define HAVE_ERRNO_H 1 - -/* Define to 1 if you have the <fcntl.h> header file. */ -#define HAVE_FCNTL_H 1 - -/* Define to 1 if you have the `fdatasync' function. */ -#define HAVE_FDATASYNC 1 - -/* Define to 1 if you have the <features.h> header file. */ -/* #undef HAVE_FEATURES_H */ - -/* Define to 1 if you have the `getmntent' function. */ -#define HAVE_GETMNTENT - -/* Define to 1 if you have the <getopt.h> header file. */ -#define HAVE_GETOPT_H 1 - -/* Define to 1 if you have the `getopt_long' function. */ -#define HAVE_GETOPT_LONG 1 - -/* Define to 1 if you have the `hasmntopt' function. */ -#define HAVE_HASMNTOPT 1 - -/* Define to 1 if you have the <inttypes.h> header file. */ -#define HAVE_INTTYPES_H 1 - -/* Define to 1 if you have the <libgen.h> header file. */ -#define HAVE_LIBGEN_H 1 - -/* Define to 1 if you have the <libintl.h> header file. */ -#define HAVE_LIBINTL_H 1 - -/* Define to 1 if you have the <limits.h> header file. */ -#define HAVE_LIMITS_H 1 - -/* Define to 1 if you have the <linux/fd.h> header file. */ -/* #undef HAVE_LINUX_FD_H */ - -/* Define to 1 if you have the <linux/hdreg.h> header file. */ -/* #undef HAVE_LINUX_HDREG_H */ - -/* Define to 1 if you have the <linux/major.h> header file. */ -/* #undef HAVE_LINUX_MAJOR_H */ - -/* Define to 1 if you have the <locale.h> header file. */ -#define HAVE_LOCALE_H 1 - -/* Define to 1 if you have the <machine/endian.h> header file. */ -/* #undef HAVE_MACHINE_ENDIAN_H */ - -/* Define to 1 if mbrtowc and mbstate_t are properly declared. */ -#define HAVE_MBRTOWC 1 - -/* Define to 1 if you have the `mbsinit' function. */ -#define HAVE_MBSINIT 1 - -/* Define to 1 if you have the `memmove' function. */ -/* #undef HAVE_MEMMOVE */ - -/* Define to 1 if you have the <memory.h> header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have the `memset' function. */ -/* #undef HAVE_MEMSET */ - -/* Define to 1 if you have the <mntent.h> header file. */ -/* #undef HAVE_MNTENT_H */ - -/* Define to 1 if you have the <pwd.h> header file. */ -#define HAVE_PWD_H 1 - -/* Define to 1 if you have the `realpath' function. */ -#define HAVE_REALPATH 1 - -/* Define to 1 if you have the `regcomp' function. */ -#define HAVE_REGCOMP 1 - -/* Define to 1 if you have the `setlocale' function. */ -#define HAVE_SETLOCALE 1 - -/* Define to 1 if you have the `setxattr' function. */ -/* #undef HAVE_SETXATTR */ - -/* Define to 1 if `stat' has the bug that it succeeds when given the - zero-length file name argument. */ -/* #undef HAVE_STAT_EMPTY_STRING_BUG */ - -/* Define to 1 if you have the <stdarg.h> header file. */ -#define HAVE_STDARG_H 1 - -/* Define to 1 if stdbool.h conforms to C99. */ -#define HAVE_STDBOOL_H 1 - -/* Define to 1 if you have the <stddef.h> header file. */ -#define HAVE_STDDEF_H 1 - -/* Define to 1 if you have the <stdint.h> header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the <stdio.h> header file. */ -#define HAVE_STDIO_H 1 - -/* Define to 1 if you have the <stdlib.h> header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the `strcasecmp' function. */ -#define HAVE_STRCASECMP 1 - -/* Define to 1 if you have the `strchr' function. */ -/* #undef HAVE_STRCHR */ - -/* Define to 1 if you have the `strdup' function. */ -/* #undef HAVE_STRDUP */ - -/* Define to 1 if you have the `strerror' function. */ -#define HAVE_STRERROR 1 - -/* Define to 1 if you have the `strftime' function. */ -/* #undef HAVE_STRFTIME */ - -/* Define to 1 if you have the <strings.h> header file. */ -#define HAVE_STRINGS_H 1 - -/* Define to 1 if you have the <string.h> header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the `strnlen' function. */ -#define HAVE_STRNLEN 1 - -/* Define to 1 if you have the `strtol' function. */ -#define HAVE_STRTOL 1 - -/* Define to 1 if you have the `strtoul' function. */ -#define HAVE_STRTOUL 1 - -/* Define to 1 if `st_blocks' is member of `struct stat'. */ -#define HAVE_STRUCT_STAT_ST_BLOCKS 1 - -/* Define to 1 if `st_rdev' is member of `struct stat'. */ -#define HAVE_STRUCT_STAT_ST_RDEV 1 - -/* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use - `HAVE_STRUCT_STAT_ST_BLOCKS' instead. */ -#define HAVE_ST_BLOCKS 1 - -/* Define to 1 if you have the `sysconf' function. */ -#define HAVE_SYSCONF 1 - -/* Define to 1 if you have the <syslog.h> header file. */ -#define HAVE_SYSLOG_H 1 - -/* Define to 1 if you have the <sys/byteorder.h> header file. */ -#define HAVE_SYS_BYTEORDER_H 1 - -/* Define to 1 if you have the <sys/endian.h> header file. */ -/* #undef HAVE_SYS_ENDIAN_H */ - -/* Define to 1 if you have the <sys/ioctl.h> header file. */ -#define HAVE_SYS_IOCTL_H 1 - -/* Define to 1 if you have the <sys/mount.h> header file. */ -#define HAVE_SYS_MOUNT_H 1 - -/* Define to 1 if you have the <sys/param.h> header file. */ -#define HAVE_SYS_PARAM_H 1 - -/* Define to 1 if you have the <sys/statvfs.h> header file. */ -#define HAVE_SYS_STATVFS_H 1 - -/* Define to 1 if you have the <sys/stat.h> header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the <sys/sysmacros.h> header file. */ -#define HAVE_SYS_SYSMACROS_H 1 - -/* Define to 1 if you have the <sys/types.h> header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the <sys/vfs.h> header file. */ -#define HAVE_SYS_VFS_H 1 - -/* Define to 1 if you have the <time.h> header file. */ -#define HAVE_TIME_H 1 - -/* Define to 1 if you have the <unistd.h> header file. */ -#define HAVE_UNISTD_H 1 - -/* Define to 1 if you have the `utime' function. */ -#define HAVE_UTIME 1 - -/* Define to 1 if you have the <utime.h> header file. */ -#define HAVE_UTIME_H 1 - -/* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */ -#define HAVE_UTIME_NULL 1 - -/* Define to 1 if you have the `vprintf' function. */ -/* #undef HAVE_VPRINTF */ - -/* Define to 1 if you have the <wchar.h> header file. */ -#define HAVE_WCHAR_H 1 - -/* Define to 1 if you have the <windows.h> header file. */ -/* #undef HAVE_WINDOWS_H */ - -/* Define to 1 if the system has the type `_Bool'. */ -#define HAVE__BOOL 1 - -/* Define to 1 if `lstat' dereferences a symlink specified with a trailing - slash. */ -#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 - -/* Define to 1 if your C compiler doesn't accept -c and -o together. */ -/* #undef NO_MINUS_C_MINUS_O */ - -/* Define this if you do not want the NTFS library to provide default device - io operations. This means that you cannot use ntfs_mount() but have to use - ntfs_device_mount() and provide your own device operations. */ -/* #undef NO_NTFS_DEVICE_DEFAULT_IO_OPS */ - -/* Name of package */ -#define PACKAGE "ntfsprogs" - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "linux-ntfs-dev@lists.sourceforge.net" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "ntfsprogs" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "ntfsprogs 2.0.0" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "ntfsprogs" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "2.0.0" - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Version number of package */ -#define VERSION "2.0.0" - -/* Define to 1 if your processor stores words with the most significant byte - first (like Motorola and SPARC, unlike Intel and VAX). */ -/* #undef WORDS_BIGENDIAN */ - -/* Define to 1 if your processor stores words with the least significant byte - first (like Intel and VAX, unlike Motorola and SPARC). */ -#define WORDS_LITTLEENDIAN 1 - -/* Number of bits in a file offset, on hosts where this is settable. */ -#define _FILE_OFFSET_BITS 64 - -/* Enable GNU extensions on systems that have them. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -#endif - -/* Define for large files, on AIX-style hosts. */ -/* #undef _LARGE_FILES */ - -/* Define to empty if `const' does not conform to ANSI C. */ -/* #undef const */ - -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -/* #undef inline */ -#endif - -/* Define to `long int' if <sys/types.h> does not define. */ -/* #undef off_t */ - -/* Define to `unsigned int' if <sys/types.h> does not define. */ -/* #undef size_t */ diff --git a/usr/src/lib/libntfs/common/include/ntfs/crypto.h b/usr/src/lib/libntfs/common/include/ntfs/crypto.h deleted file mode 100644 index a4b72435c1..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/crypto.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * crypto.h - Exports for dealing with encrypted files. Part of the - * Linux-NTFS project. - * - * Copyright (c) 2007 Yura Pakhuchiy - * - * This program 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 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_CRYPTO_H -#define _NTFS_CRYPTO_H - -extern ntfschar NTFS_EFS[5]; - -/* - * This is our Big Secret (TM) structure, so do not allow anyone even read it - * values. ;-) In fact, it is private because exist only in libntfs version - * compiled with cryptography support, so users can not depend on it. - */ -typedef struct _ntfs_crypto_attr ntfs_crypto_attr; - -/* - * These functions should not be used directly. They are called for encrypted - * attributes from corresponding functions without _crypto_ part. - */ - -extern int ntfs_crypto_attr_open(ntfs_attr *na); -extern void ntfs_crypto_attr_close(ntfs_attr *na); - -extern s64 ntfs_crypto_attr_pread(ntfs_attr *na, const s64 pos, s64 count, - void *b); - -#endif /* _NTFS_CRYPTO_H */ diff --git a/usr/src/lib/libntfs/common/include/ntfs/debug.h b/usr/src/lib/libntfs/common/include/ntfs/debug.h deleted file mode 100644 index 0dd411420b..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/debug.h +++ /dev/null @@ -1,63 +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; - -#ifndef DEBUG -static __inline__ void ntfs_debug_runlist_dump(const struct _runlist_element *rl __attribute__((unused))) {} -#define NTFS_ON_DEBUG(x) -#else -extern void ntfs_debug_runlist_dump(const struct _runlist_element *rl); -#define NTFS_ON_DEBUG(x) (x) -#endif - -#if defined(__GNUC__) - -#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]; \ -} - -#else /* not __GNUC__ */ - -#define NTFS_BUG(msg) \ -{ \ - int ___i; \ - ntfs_log_critical("Bug in %s(): %s\n", "unknown", msg); \ - ntfs_log_debug("Forcing segmentation fault!"); \ - ___i = ((int*)NULL)[1]; \ -} - -#endif /* __GNUC__ */ - -#endif /* defined _NTFS_DEBUG_H */ diff --git a/usr/src/lib/libntfs/common/include/ntfs/device.h b/usr/src/lib/libntfs/common/include/ntfs/device.h deleted file mode 100644 index eeadf13e7a..0000000000 --- a/usr/src/lib/libntfs/common/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/usr/src/lib/libntfs/common/include/ntfs/device_io.h b/usr/src/lib/libntfs/common/include/ntfs/device_io.h deleted file mode 100644 index 6665b68050..0000000000 --- a/usr/src/lib/libntfs/common/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/usr/src/lib/libntfs/common/include/ntfs/dir.h b/usr/src/lib/libntfs/common/include/ntfs/dir.h deleted file mode 100644 index 5299861b81..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/dir.h +++ /dev/null @@ -1,112 +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 u64 ntfs_pathname_to_inode_num(ntfs_volume *vol, ntfs_inode *parent, - const char *pathname); -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 **pni, 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 <linux/fs.h>) - */ -#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); - -#endif /* defined _NTFS_DIR_H */ diff --git a/usr/src/lib/libntfs/common/include/ntfs/endians.h b/usr/src/lib/libntfs/common/include/ntfs/endians.h deleted file mode 100644 index b3426df30e..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/endians.h +++ /dev/null @@ -1,248 +0,0 @@ -/* - * endians.h - Definitions related to handling of byte ordering. Part of the - * Linux-NTFS project. - * - * Copyright (c) 2000-2005 Anton Altaparmakov - * Copyright (c) 2007 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_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 <endian.h> -#endif -#ifdef HAVE_SYS_ENDIAN_H -#include <sys/endian.h> -#endif -#ifdef HAVE_MACHINE_ENDIAN_H -#include <machine/endian.h> -#endif -#ifdef HAVE_SYS_BYTEORDER_H -#include <sys/byteorder.h> -#endif -#ifdef HAVE_SYS_PARAM_H -#include <sys/param.h> -#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 <byteswap.h> -#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) ((__force u16)(x)) -#define __le32_to_cpu(x) ((__force u32)(x)) -#define __le64_to_cpu(x) ((__force u64)(x)) - -#define __cpu_to_le16(x) ((__force le16)(x)) -#define __cpu_to_le32(x) ((__force le32)(x)) -#define __cpu_to_le64(x) ((__force le64)(x)) - -#define __constant_le16_to_cpu(x) ((__force u16)(x)) -#define __constant_le32_to_cpu(x) ((__force u32)(x)) -#define __constant_le64_to_cpu(x) ((__force u64)(x)) - -#define __constant_cpu_to_le16(x) ((__force le16)(x)) -#define __constant_cpu_to_le32(x) ((__force le32)(x)) -#define __constant_cpu_to_le64(x) ((__force le64)(x)) - -#elif defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN) - -#define __le16_to_cpu(x) bswap_16((__force u16)(x)) -#define __le32_to_cpu(x) bswap_32((__force u16)(x)) -#define __le64_to_cpu(x) bswap_64((__force u16)(x)) - -#define __cpu_to_le16(x) (__force le16)bswap_16((__force u16)(x)) -#define __cpu_to_le32(x) (__force le32)bswap_32((__force u32)(x)) -#define __cpu_to_le64(x) (__force le64)bswap_64((__force u64)(x)) - -#define __constant_le16_to_cpu(x) __ntfs_bswap_constant_16((__force u16)(x)) -#define __constant_le32_to_cpu(x) __ntfs_bswap_constant_32((__force u32)(x)) -#define __constant_le64_to_cpu(x) __ntfs_bswap_constant_64((__force u64)(x)) - -#define __constant_cpu_to_le16(x) \ - (__force le16)__ntfs_bswap_constant_16((__force u16)(x)) -#define __constant_cpu_to_le32(x) \ - (__force le32)__ntfs_bswap_constant_32((__force u32)(x)) -#define __constant_cpu_to_le64(x) \ - (__force le64)__ntfs_bswap_constant_64((__force 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((le16)(x)) -#define le32_to_cpu(x) (u32)__le32_to_cpu((le32)(x)) -#define le64_to_cpu(x) (u64)__le64_to_cpu((le64)(x)) - -#define le16_to_cpup(x) (u16)__le16_to_cpu(*(const le16*)(x)) -#define le32_to_cpup(x) (u32)__le32_to_cpu(*(const le32*)(x)) -#define le64_to_cpup(x) (u64)__le64_to_cpu(*(const le64*)(x)) - -/* Signed from LE to CPU conversion. */ - -#define sle16_to_cpu(x) (s16)__le16_to_cpu((sle16)(x)) -#define sle32_to_cpu(x) (s32)__le32_to_cpu((sle32)(x)) -#define sle64_to_cpu(x) (s64)__le64_to_cpu((sle64)(x)) - -#define sle16_to_cpup(x) (s16)__le16_to_cpu(*(const sle16*)(x)) -#define sle32_to_cpup(x) (s32)__le32_to_cpu(*(const sle32*)(x)) -#define sle64_to_cpup(x) (s64)__le64_to_cpu(*(const sle64*)(x)) - -/* Unsigned from CPU to LE conversion. */ - -#define cpu_to_le16(x) (le16)__cpu_to_le16((u16)(x)) -#define cpu_to_le32(x) (le32)__cpu_to_le32((u32)(x)) -#define cpu_to_le64(x) (le64)__cpu_to_le64((u64)(x)) - -#define cpu_to_le16p(x) (le16)__cpu_to_le16(*(const u16*)(x)) -#define cpu_to_le32p(x) (le32)__cpu_to_le32(*(const u32*)(x)) -#define cpu_to_le64p(x) (le64)__cpu_to_le64(*(const u64*)(x)) - -/* Signed from CPU to LE conversion. */ - -#define cpu_to_sle16(x) (__force sle16)__cpu_to_le16((s16)(x)) -#define cpu_to_sle32(x) (__force sle32)__cpu_to_le32((s32)(x)) -#define cpu_to_sle64(x) (__force sle64)__cpu_to_le64((s64)(x)) - -#define cpu_to_sle16p(x) (__force sle16)__cpu_to_le16(*(const s16*)(x)) -#define cpu_to_sle32p(x) (__force sle32)__cpu_to_le32(*(const s32*)(x)) -#define cpu_to_sle64p(x) (__force sle64)__cpu_to_le64(*(const s64*)(x)) - -/* Constant endianness conversion defines. */ - -#define const_le16_to_cpu(x) (u16)__constant_le16_to_cpu((le16)(x)) -#define const_le32_to_cpu(x) (u32)__constant_le32_to_cpu((le32)(x)) -#define const_le64_to_cpu(x) (u64)__constant_le64_to_cpu((le64)(x)) - -#define const_cpu_to_le16(x) (le16)__constant_cpu_to_le16((u16)(x)) -#define const_cpu_to_le32(x) (le32)__constant_cpu_to_le32((u32)(x)) -#define const_cpu_to_le64(x) (le64)__constant_cpu_to_le64((u64)(x)) - -#ifdef __CHECKER__ -static void ntfs_endian_self_test(void) -{ - /* Should not generate warnings. */ - (le16)cpu_to_le16((u16)1); - (le32)cpu_to_le32((u32)1); - (le64)cpu_to_le64((u64)1); - (sle16)cpu_to_sle16((s16)1); - (sle32)cpu_to_sle32((s32)1); - (sle64)cpu_to_sle64((s64)1); - (u16)le16_to_cpu((__force le16)1); - (u32)le32_to_cpu((__force le32)1); - (u64)le64_to_cpu((__force le64)1); - (s16)sle16_to_cpu((__force sle16)1); - (s32)sle32_to_cpu((__force sle32)1); - (s64)sle64_to_cpu((__force sle64)1); - (le16)const_cpu_to_le16((u16)1); - (le32)const_cpu_to_le32((u32)1); - (le64)const_cpu_to_le64((u64)1); - (u16)const_le16_to_cpu((__force le16)1); - (u32)const_le32_to_cpu((__force le32)1); - (u64)const_le64_to_cpu((__force le64)1); - - /* - * TODO: Need some how to test that warnings are actually generated, - * but without flooding output with them and vice-versa print warning - * in case if some one warning is not triggered, but should. (Yura) - * - * I think it can only be done in a ./configure like script / shell - * script that will compile known good and known bad code and pipe the - * output from sparse to a file, then grep the file for the wanted - * warnings/lack thereof and then it would say "Tests: PASS " or - * "Tests: FAILED" or whatever. And you can then hook that into a - * "make test" make target or similar so it is only done when one - * wants to do it... (Anton) - * - * Also we can look on sparse self test script. (Yura) - */ -} -#endif - -#endif /* defined _NTFS_ENDIANS_H */ diff --git a/usr/src/lib/libntfs/common/include/ntfs/index.h b/usr/src/lib/libntfs/common/include/ntfs/index.h deleted file mode 100644 index 75e23e2a4e..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/index.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * index.h - Defines for NTFS index handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2004 Anton Altaparmakov - * Copyright (c) 2004-2005 Richard Russon - * Copyright (c) 2005-2006 Yura Pakhuchiy - * Copyright (c) 2006 Szabolcs Szakacsits - * - * 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" - -#define VCN_INDEX_ROOT_PARENT ((VCN)-2) - -#define MAX_PARENT_VCN 32 - -/** - * 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 @ib) - * @data: index entry data (points into @entry) - * @data_len: length in bytes of @data - * @cr: - * @is_in_root: TRUE if @entry is in @ir or FALSE if it is in @ib - * @ir: index root if @is_in_root or NULL otherwise - * @actx: attribute search context if in root or NULL otherwise - * @ia_na: opened INDEX_ALLOCATION attribute - * @ib: index block if @is_in_root is FALSE or NULL otherwise - * @ib_vcn: VCN from which @ib where read from - * @ib_dirty: TRUE if index block was changed - * @parent_pos: parent entries' positions in the index block - * @parent_vcn: entry's parent nodes or VCN_INDEX_ROOT_PARENT for root - * @max_depth: number of the parent nodes - * @pindex: maximum it's the number of the parent nodes - * @block_size: index block size - * @vcn_size_bits: VCN size bits for this index block - * - * @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. @ib, @ib_vcn and - * @ib_dirty are undefined in this case. - * - * If @is_in_root is FALSE, @entry is in the index allocation attribute and @ib - * and @ib_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. @ib_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; - COLLATION_RULES cr; - BOOL is_in_root; - INDEX_ROOT *ir; - ntfs_attr_search_ctx *actx; - ntfs_attr *ia_na; - INDEX_BLOCK *ib; - VCN ib_vcn; - BOOL ib_dirty; - int parent_pos[MAX_PARENT_VCN]; - VCN parent_vcn[MAX_PARENT_VCN]; - int max_depth; - int pindex; - u32 block_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); - -extern VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie); - -extern char *ntfs_ie_filename_get(INDEX_ENTRY *ie); -extern void ntfs_ie_filename_dump(INDEX_ENTRY *ie); -extern void ntfs_ih_filename_dump(INDEX_HEADER *ih); - -extern void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx); - -#endif /* _NTFS_INDEX_H */ diff --git a/usr/src/lib/libntfs/common/include/ntfs/inode.h b/usr/src/lib/libntfs/common/include/ntfs/inode.h deleted file mode 100644 index 90c2113116..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/inode.h +++ /dev/null @@ -1,215 +0,0 @@ -/* - * inode.h - Defines for NTFS inode handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2001,2002 Anton Altaparmakov - * Copyright (c) 2004-2007 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 "list.h" -#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. */ - - /* Below fields 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. */ - } u; - - /* Below fields are valid only for base inode. */ - - /* - * These two fields are used to sync filename index and guaranteed to be - * correct, however value in index itself maybe wrong (windows itself - * do not update them properly). - */ - s64 data_size; /* Data size of unnamed DATA attribute. */ - 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.) */ - - /* - * These four fields are copy of relevant fields from - * STANDARD_INFORMATION attribute and used to sync it and FILE_NAME - * attribute in the index. - */ - time_t creation_time; - time_t last_data_change_time; - time_t last_mft_change_time; - time_t last_access_time; - - /* These 2 fields are used to keep track of opened inodes. */ - struct list_head list_entry; /* Keep pointers to the next/prev list - entry. */ - int nr_references; /* How many times this inode was - opened. We really close inode only - when this reaches zero. */ - - struct list_head attr_cache; /* List of opened attributes. */ -}; - -extern void __ntfs_inode_add_to_cache(ntfs_inode *ni); - -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 leMFT_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->u.base_ni); -} - -typedef enum { - NTFS_UPDATE_ATIME = 1 << 0, - NTFS_UPDATE_MTIME = 1 << 1, - NTFS_UPDATE_CTIME = 1 << 2, -} ntfs_time_update_flags; - -extern void ntfs_inode_update_times(ntfs_inode *ni, - ntfs_time_update_flags mask); - -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); - -#endif /* defined _NTFS_INODE_H */ diff --git a/usr/src/lib/libntfs/common/include/ntfs/layout.h b/usr/src/lib/libntfs/common/include/ntfs/layout.h deleted file mode 100644 index 7ae239cccd..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/layout.h +++ /dev/null @@ -1,3063 +0,0 @@ -/* - * layout.h - Ntfs on-disk layout structures. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2005 Anton Altaparmakov - * Copyright (c) 2005-2007 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 "NTFS " */ -#define NTFS_SB_MAGIC const_cpu_to_le64(0x202020205346544eULL) - -/* - * Location of boot sector 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 boot sector 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - le16 bytes_per_sector; /* Size of a sector in bytes. */ - u8 sectors_per_cluster; /* Size of a cluster in sectors. */ - le16 reserved_sectors; /* zero */ - u8 fats; /* zero */ - le16 root_entries; /* zero */ - le16 sectors; /* zero */ - u8 media_type; /* 0xf8 = hard disk */ - le16 sectors_per_fat; /* zero */ -/*0x0d*/le16 sectors_per_track; /* Required to boot Windows. */ -/*0x0f*/le16 heads; /* Required to boot Windows. */ -/*0x11*/le32 hidden_sectors; /* Offset to the start of the partition - relative to the disk in sectors. - Required to boot Windows. */ -/*0x15*/le32 large_sectors; /* zero */ -/* sizeof() = 25 (0x19) bytes */ -} __attribute__((__packed__)) BIOS_PARAMETER_BLOCK; -#ifdef __sun -#pragma pack() -#endif - -/** - * struct NTFS_BOOT_SECTOR - NTFS boot sector structure. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - u8 jump[3]; /* Irrelevant (jump to boot up code).*/ - le64 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*/sle64 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. (-; */ - sle64 mft_lcn; /* Cluster location of mft data. */ - sle64 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 */ - le64 volume_serial_number; /* Irrelevant (serial number). */ - le32 checksum; /* Boot sector checksum. */ -/*0x54*/u8 bootstrap[426]; /* Irrelevant (boot up code). */ - le16 end_of_sector_marker; /* End of boot sector magic. Always is - 0xaa55 in little endian. */ -/* sizeof() = 512 (0x200) bytes */ -} __attribute__((__packed__)) NTFS_BOOT_SECTOR; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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 - */ - -static inline BOOL __ntfs_is_magic(le32 x, NTFS_RECORD_TYPES r) -{ - return (x == (__force le32)r); -} -#define ntfs_is_magic(x, m) __ntfs_is_magic(x, magic_##m) - -static inline BOOL __ntfs_is_magicp(le32 *p, NTFS_RECORD_TYPES r) -{ - return (*p == (__force le32)r); -} -#define ntfs_is_magicp(p, m) __ntfs_is_magicp(p, 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 le16 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 le16'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 le16 in the array has to - * (obviously) finish before the last le16 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - NTFS_RECORD_TYPES magic;/* A four-byte magic identifying the - record type and/or status. */ - le16 usa_ofs; /* Offset to the Update Sequence Array (USA) - from the start of the ntfs record. */ - le16 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; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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 NTFS 3.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. - */ -#ifdef __sun -typedef uint16_t MFT_RECORD_FLAGS; -#define MFT_RECORD_IN_USE (const_cpu_to_le16(0x0001)) -#define MFT_RECORD_IS_DIRECTORY (const_cpu_to_le16(0x0002)) -#define MFT_RECORD_IS_4 (const_cpu_to_le16(0x0004)) -#define MFT_RECORD_IS_VIEW_INDEX (const_cpu_to_le16(0x0008)) -#else /* not __sun */ -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 = const_cpu_to_le16(0xffff), - /* Just to make flags 16-bit. */ -} __attribute__((__packed__)) MFT_RECORD_FLAGS; -#endif /* __sun */ - -/* - * 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; -typedef le64 leMFT_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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { -/*Ofs*/ -/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ - NTFS_RECORD_TYPES magic;/* Usually the magic is "FILE". */ - le16 usa_ofs; /* See NTFS_RECORD definition above. */ - le16 usa_count; /* See NTFS_RECORD definition above. */ - -/* 8*/ leLSN lsn; /* $LogFile sequence number for this record. - Changed every time the record is modified. */ -/* 16*/ le16 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*/ le16 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*/ le16 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*/ le32 bytes_in_use; /* Number of bytes used in this mft record. - NOTE: Must be aligned to 8-byte boundary. */ -/* 28*/ le32 bytes_allocated; /* Number of bytes allocated for this mft - record. This should be equal to the mft - record size. */ -/* 32*/ leMFT_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*/ le16 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*/ le16 reserved; /* Reserved/alignment. */ -/* 44*/ le32 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; -#ifdef __sun -#pragma pack() -#endif - -/** - * struct MFT_RECORD_OLD - An MFT record layout (NTFS <=3.0) - * - * This is the version without the NTFS 3.1+ specific fields. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { -/*Ofs*/ -/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ - NTFS_RECORD_TYPES magic;/* Usually the magic is "FILE". */ - le16 usa_ofs; /* See NTFS_RECORD definition above. */ - le16 usa_count; /* See NTFS_RECORD definition above. */ - -/* 8*/ leLSN lsn; /* $LogFile sequence number for this record. - Changed every time the record is modified. */ -/* 16*/ le16 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*/ le16 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*/ le16 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*/ le32 bytes_in_use; /* Number of bytes used in this mft record. - NOTE: Must be aligned to 8-byte boundary. */ -/* 28*/ le32 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*/ le16 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; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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 le32 key - * values. E.g. used for $SII index in FILE_Secure, which sorts by - * security_id (le32). - * 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 - * le32 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 - * le32 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 le32 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 le32 value of the 1st object_id is less than the first le32 of - * the 2nd object_id. If the first le32 values of both object_ids were - * equal then the second le32 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { -/*hex ofs*/ -/* 0*/ ntfschar name[0x40]; /* Unicode name of the attribute. Zero - terminated. */ -/* 80*/ ATTR_TYPES type; /* Type of the attribute. */ -/* 84*/ le32 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*/ sle64 min_size; /* Optional minimum attribute size. */ -/* 98*/ sle64 max_size; /* Maximum size of attribute. */ -/* sizeof() = 0xa0 or 160 bytes */ -} __attribute__((__packed__)) ATTR_DEF; -#ifdef __sun -#pragma pack() -#endif - -/** - * enum ATTR_FLAGS - Attribute flags (16-bit). - */ -#ifdef __sun -typedef uint16_t ATTR_FLAGS; -#define ATTR_IS_COMPRESSED (const_cpu_to_le16(0x0001)) -#define ATTR_COMPRESSION_MASK (const_cpu_to_le16(0x00ff)) -#define ATTR_IS_ENCRYPTED (const_cpu_to_le16(0x4000)) -#define ATTR_IS_SPARSE (const_cpu_to_le16(0x8000)) -#else /* not __sun */ -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; -#endif /* __sun */ - -/* - * 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). - */ -#ifdef __sun -typedef uint8_t RESIDENT_ATTR_FLAGS; -#define RESIDENT_ATTR_IS_INDEXED (0x01) -#else /* not __sun */ -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; -#endif /* __sun */ - -/** - * struct ATTR_RECORD - Attribute record header. - * - * Always aligned to 8-byte boundary. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { -/*Ofs*/ -/* 0*/ ATTR_TYPES type; /* The (32-bit) type of the attribute. */ -/* 4*/ le32 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*/ le16 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*/ le16 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 */ le32 value_length; /* Byte size of attribute value. */ -/* 20 */ le16 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[]; /* Use offsetof(ATTR_RECORD, - resident_end) to get size of - a resident attribute. */ - } __attribute__((__packed__)) res; - - /* Non-resident attributes. */ - struct { -/* 16*/ leVCN 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*/ leVCN 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*/ le16 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*/ sle64 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*/ sle64 data_size; /* Byte size of the attribute - value. Can be larger than allocated_size if - attribute value is compressed or sparse. */ -/* 56*/ sle64 initialized_size; /* Byte size of initialized - portion of the attribute value. Usually equals - data_size. */ -#ifdef __sun -/* 64 */ -#define non_resident_end compressed_size -#else /* not __sun */ -/* 64 */ void *non_resident_end[0]; /* Use offsetof(ATTR_RECORD, - non_resident_end) to get - size of a non resident - attribute. */ -#endif /* __sun */ -/* sizeof(uncompressed attr) = 64*/ -/* 64*/ sle64 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[]; - /* Use offsetof(ATTR_RECORD, compressed_end) to - get size of a compressed attribute. */ -/* sizeof(compressed attr) = 72*/ - } __attribute__((__packed__)) nonres; - } __attribute__((__packed__)) u; -} __attribute__((__packed__)) ATTR_RECORD; -#ifdef __sun -#pragma pack() -#endif - -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 valid. 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { -/*Ofs*/ -/* 0*/ sle64 creation_time; /* Time file was created. Updated when - a filename is changed(?). */ -/* 8*/ sle64 last_data_change_time; /* Time the data attribute was last - modified. */ -/* 16*/ sle64 last_mft_change_time; /* Time this mft record was last - modified. */ -/* 24*/ sle64 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[]; /* Marker for offsetof(). */ - } __attribute__((__packed__)) v12; -/* 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*/ le32 maximum_versions; /* Maximum allowed versions for - file. Zero if version numbering is disabled. */ - /* 40*/ le32 version_number; /* This file's version (if any). - Set to zero if maximum_versions is zero. */ - /* 44*/ le32 class_id; /* Class id from bidirectional - class id index (?). */ - /* 48*/ le32 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*/ le32 security_id; /* Security_id for the file. - Translate via $SII index and $SDS data stream - in FILE_Secure to the security descriptor. */ - /* 56*/ le64 quota_charged; /* Byte size of the charge to - the quota for all streams of the file. Note: Is - zero if quotas are disabled. */ - /* 64*/ le64 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[]; /* Marker for offsetof(). */ - } __attribute__((__packed__)) v30; - } __attribute__((__packed__)) u; -/* sizeof() = 72 bytes (NTFS 3.0) */ -} __attribute__((__packed__)) STANDARD_INFORMATION; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { -/*Ofs*/ -/* 0*/ ATTR_TYPES type; /* Type of referenced attribute. */ -/* 4*/ le16 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*/ leVCN 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*/ leMFT_REF mft_reference;/* The reference of the mft record holding - the ATTR_RECORD for this portion of the - attribute value. */ -/* 24*/ le16 instance; /* If lowest_vcn = 0, the instance of the - attribute being referenced; otherwise 0. */ -/* 26*/ ntfschar name[]; /* 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; -#ifdef __sun -#pragma pack() -#endif - -/* - * 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). - */ -#ifdef __sun -typedef uint8_t FILE_NAME_TYPE_FLAGS; -#define FILE_NAME_POSIX (0x00) -#define FILE_NAME_WIN32 (0x01) -#define FILE_NAME_DOS (0x02) -#define FILE_NAME_WIN32_AND_DOS (0x03) -#else /* not __sun */ -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; -#endif /* __sun */ - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { -/*hex ofs*/ -/* 0*/ leMFT_REF parent_directory; /* Directory this filename is - referenced from. */ -/* 8*/ sle64 creation_time; /* Time file was created. */ -/* 10*/ sle64 last_data_change_time; /* Time the data attribute was last - modified. */ -/* 18*/ sle64 last_mft_change_time; /* Time this mft record was last - modified. */ -/* 20*/ sle64 last_access_time; /* Last time this mft record was - accessed. */ -/* 28*/ sle64 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*/ sle64 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*/ le16 packed_ea_size; /* Size of the buffer needed to - pack the extended attributes - (EAs), if such are present.*/ - /* 3e*/ le16 reserved; /* Reserved for alignment. */ - } __attribute__((__packed__)) s; - /* 3c*/ le32 reparse_point_tag; /* Type of reparse point, - present only in reparse - points and only if there are - no EAs. */ - } __attribute__((__packed__)) u; -/* 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[]; /* File name in Unicode. */ -} __attribute__((__packed__)) FILE_NAME_ATTR; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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 in string format: - * 1F010768-5A73-BC91-0010-A52216A7227B - * And the same in binary: - * 1F0107685A73BC910010A52216A7227B - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef union { - struct { - le32 data1; /* The first eight hexadecimal digits of the - GUID. */ - le16 data2; /* The first group of four hexadecimal - digits. */ - le16 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__)) s; - u8 raw[16]; /* Raw binary for ease of access. */ -} __attribute__((__packed__)) GUID; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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). - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - leMFT_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__)) s; - u8 extended_info[48]; - } __attribute__((__packed__)) u; -} __attribute__((__packed__)) OBJ_ID_INDEX_DATA; -#ifdef __sun -#pragma pack() -#endif - -/** - * struct OBJECT_ID_ATTR - Attribute: Object id (NTFS 3.0+) (0x40). - * - * NOTE: Always resident. - */ -#ifdef __sun -#pragma pack(1) -#endif -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__)) s; - u8 extended_info[48]; - } __attribute__((__packed__)) u; -} __attribute__((__packed__)) OBJECT_ID_ATTR; -#ifdef __sun -#pragma pack() -#endif - -#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 - * - * NETWORK_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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef union { - struct { - be16 high_part; /* High 16-bits. */ - be32 low_part; /* Low 32-bits. */ - } __attribute__((__packed__)) s; - u8 value[6]; /* Value as individual bytes. */ -} __attribute__((__packed__)) SID_IDENTIFIER_AUTHORITY; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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 - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - u8 revision; - u8 sub_authority_count; - SID_IDENTIFIER_AUTHORITY identifier_authority; - le32 sub_authority[1]; /* At least one sub_authority. */ -} __attribute__((__packed__)) SID; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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). - */ -#ifdef __sun -typedef uint8_t ACE_TYPES; -#define ACCESS_ALLOWED_ACE_TYPE (0) -#define ACCESS_DENIED_ACE_TYPE (1) -#define SYSTEM_AUDIT_ACE_TYPE (2) -#else /* not __sun */ -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; -#endif /* __sun */ - -/** - * 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. - */ -#ifdef __sun -typedef uint8_t ACE_FLAGS; -#define OBJECT_INHERIT_ACE (0x01) -#define CONTAINER_INHERIT_ACE (0x02) -#define INHERIT_ONLY_ACE (0x08) -#else /* not __sun */ -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; -#endif /* __sun */ - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - ACE_TYPES type; /* Type of the ACE. */ - ACE_FLAGS flags; /* Flags describing the ACE. */ - le16 size; /* Size in bytes of the ACE. */ -} __attribute__((__packed__)) ACE_HEADER; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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) - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - ACCESS_MASK generic_read; - ACCESS_MASK generic_write; - ACCESS_MASK generic_execute; - ACCESS_MASK generic_all; -} __attribute__((__packed__)) GENERIC_MAPPING; -#ifdef __sun -#pragma pack() -#endif - -/* - * 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 - */ -#ifdef __sun -#pragma pack(1) -#endif -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. */ - le16 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; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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 - - */ -#ifdef __sun -#pragma pack(1) -#endif -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. */ - le16 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; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - u8 revision; /* Revision of this ACL. */ - u8 alignment1; - le16 size; /* Allocated space in bytes for ACL. Includes this - header, the ACEs and the remaining free space. */ - le16 ace_count; /* Number of ACEs in the ACL. */ - le16 alignment2; -/* sizeof() = 8 bytes */ -} __attribute__((__packed__)) ACL; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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. - */ -#ifdef __sun -typedef uint16_t SECURITY_DESCRIPTOR_CONTROL; -#define SE_DACL_PRESENT (const_cpu_to_le16(0x0004)) -#define SE_DACL_DEFAULTED (const_cpu_to_le16(0x0008)) -#define SE_SACL_PRESENT (const_cpu_to_le16(0x0010)) -#define SE_SACL_DEFAULTED (const_cpu_to_le16(0x0020)) -#define SE_SELF_RELATIVE (const_cpu_to_le16(0x8000)) -#else /* not __sun */ -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; -#endif /* __sun */ - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -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. */ - le32 owner; /* Byte offset to a SID representing an object's - owner. If this is NULL, no owner SID is present in - the descriptor. */ - le32 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. */ - le32 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. */ - le32 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; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -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; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - le32 hash; /* Hash of the security descriptor. */ - le32 security_id; /* The security_id assigned to the descriptor. */ - le64 offset; /* Byte offset of this entry in the $SDS stream. */ - le32 length; /* Size in bytes of this entry in $SDS stream. */ -} __attribute__((__packed__)) SECURITY_DESCRIPTOR_HEADER; -#ifdef __sun -#pragma pack() -#endif - -/** - * struct SDH_INDEX_DATA - - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - le32 hash; /* Hash of the security descriptor. */ - le32 security_id; /* The security_id assigned to the descriptor. */ - le64 offset; /* Byte offset of this entry in the $SDS stream. */ - le32 length; /* Size in bytes of this entry in $SDS stream. */ - le32 reserved_II; /* Padding - always unicode "II" or zero. This field - isn't counted in INDEX_ENTRY's data_length. */ -} __attribute__((__packed__)) SDH_INDEX_DATA; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { -/* 0 SECURITY_DESCRIPTOR_HEADER; -- Unfolded here as gcc doesn't like - unnamed structs. */ - le32 hash; /* Hash of the security descriptor. */ - le32 security_id; /* The security_id assigned to the descriptor. */ - le64 offset; /* Byte offset of this entry in the $SDS stream. */ - le32 length; /* Size in bytes of this entry in $SDS stream. */ -/* 20*/ SECURITY_DESCRIPTOR_RELATIVE sid; /* The self-relative security - descriptor. */ -} __attribute__((__packed__)) SDS_ENTRY; -#ifdef __sun -#pragma pack() -#endif - -/** - * struct SII_INDEX_KEY - The index entry key used in the $SII index. - * - * The collation type is COLLATION_NTOFS_ULONG. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - le32 security_id; /* The security_id assigned to the descriptor. */ -} __attribute__((__packed__)) SII_INDEX_KEY; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - le32 hash; /* Hash of the security descriptor. */ - le32 security_id; /* The security_id assigned to the descriptor. */ -} __attribute__((__packed__)) SDH_INDEX_KEY; -#ifdef __sun -#pragma pack() -#endif - -#ifndef __sun -/** - * struct VOLUME_NAME - Attribute: Volume name (0x60). - * - * NOTE: Always resident. - * NOTE: Present only in FILE_Volume. - */ -typedef struct { - ntfschar name[]; /* The name of the volume in Unicode. */ -} __attribute__((__packed__)) VOLUME_NAME; -#endif - -/** - * enum VOLUME_FLAGS - Possible flags for the volume (16-bit). - * - * WARNING: Setting VOLUME_MOUNTED_ON_NT4 on a Volume causes Windows Vista to - * fail to boot (it hangs on a black screen). - */ -#ifdef __sun -typedef uint16_t VOLUME_FLAGS; -#define VOLUME_IS_DIRTY (const_cpu_to_le16(0x0001)) -#define VOLUME_RESIZE_LOG_FILE (const_cpu_to_le16(0x0002)) -#define VOLUME_UPGRADE_ON_MOUNT (const_cpu_to_le16(0x0004)) -#define VOLUME_MOUNTED_ON_NT4 (const_cpu_to_le16(0x0008)) -#define VOLUME_DELETE_USN_UNDERWAY (const_cpu_to_le16(0x0010)) -#define VOLUME_REPAIR_OBJECT_ID (const_cpu_to_le16(0x0020)) -#define VOLUME_CHKDSK_UNDERWAY (const_cpu_to_le16(0x4000)) -#define VOLUME_MODIFIED_BY_CHKDSK (const_cpu_to_le16(0x8000)) -#define VOLUME_FLAGS_MASK (const_cpu_to_le16(0xc03f)) -#else /* not __sun */ -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; -#endif /* __sun */ - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - le64 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; -#ifdef __sun -#pragma pack() -#endif - -#ifndef __sun -/** - * 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[]; /* The file's data contents. */ -} __attribute__((__packed__)) DATA_ATTR; -#endif - -/** - * enum INDEX_HEADER_FLAGS - Index header flags (8-bit). - */ -#ifdef __sun -typedef uint8_t INDEX_HEADER_FLAGS; -#define SMALL_INDEX (0) -#define LARGE_INDEX (1) -#define LEAF_NODE (0) -#define INDEX_NODE (1) -#define NODE_MASK (1) -#else /* not __sun */ -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; -#endif /* __sun */ - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - le32 entries_offset; /* Byte offset to first INDEX_ENTRY - aligned to 8-byte boundary. */ - le32 index_length; /* Data size of the index in bytes, - i.e. bytes used from allocated - size, aligned to 8-byte boundary. */ - le32 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; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -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. */ - le32 index_block_size; /* Size of each index block in bytes (in - the index allocation attribute). */ - u8 clusters_per_index_block; /* Cluster size of each index block (in - the index allocation attribute), when - an index block is >= than a cluster, - otherwise sectors per index block. */ - u8 reserved[3]; /* Reserved/align to 8-byte boundary. */ - INDEX_HEADER index; /* Index header describing the - following index entries. */ -} __attribute__((__packed__)) INDEX_ROOT; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { -/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ - NTFS_RECORD_TYPES magic;/* Magic is "INDX". */ - le16 usa_ofs; /* See NTFS_RECORD definition. */ - le16 usa_count; /* See NTFS_RECORD definition. */ - -/* 8*/ leLSN lsn; /* $LogFile sequence number of the last - modification of this index block. */ -/* 16*/ leVCN 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; -#ifdef __sun -#pragma pack() -#endif - -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) - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - le32 reparse_tag; /* Reparse point type (inc. flags). */ - leMFT_REF file_id; /* Mft record of the file containing the - reparse point attribute. */ -} __attribute__((__packed__)) REPARSE_INDEX_KEY; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - le32 version; /* Currently equals 2. */ - QUOTA_FLAGS flags; /* Flags describing this quota entry. */ - le64 bytes_used; /* How many bytes of the quota are in use. */ - sle64 change_time; /* Last time this quota entry was changed. */ - sle64 threshold; /* Soft quota (-1 if not limited). */ - sle64 limit; /* Hard quota (-1 if not limited). */ - sle64 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; -#ifdef __sun -#pragma pack() -#endif - -/** - * struct QUOTA_O_INDEX_DATA - - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - le32 owner_id; - le32 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; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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). - */ -#ifdef __sun -typedef uint16_t INDEX_ENTRY_FLAGS; -#define INDEX_ENTRY_NODE (const_cpu_to_le16(1)) -#define INDEX_ENTRY_END (const_cpu_to_le16(2)) -#else /* not __sun */ -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 = const_cpu_to_le16(0xffff), - /* Just to force 16-bit width. */ -} __attribute__((__packed__)) INDEX_ENTRY_FLAGS; -#endif /* __sun */ - -/** - * struct INDEX_ENTRY_HEADER - This the index entry header (see below). - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { -/* 0*/ union { /* Only valid when INDEX_ENTRY_END is not set. */ - leMFT_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. */ - le16 data_offset; /* Data byte offset from this - INDEX_ENTRY. Follows the - index key. */ - le16 data_length; /* Data length in bytes. */ - le32 reservedV; /* Reserved (zero). */ - } __attribute__((__packed__)) s; - } __attribute__((__packed__)) u; -/* 8*/ le16 length; /* Byte size of this index entry, multiple of - 8-bytes. */ -/* 10*/ le16 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*/ le16 reserved; /* Reserved/align to 8-byte boundary. */ -/* sizeof() = 16 bytes */ -} __attribute__((__packed__)) INDEX_ENTRY_HEADER; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { -/* 0 INDEX_ENTRY_HEADER; -- Unfolded here as gcc dislikes unnamed structs. */ - union { /* Only valid when INDEX_ENTRY_END is not set. */ - leMFT_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. */ - le16 data_offset; /* Data byte offset from this - INDEX_ENTRY. Follows the - index key. */ - le16 data_length; /* Data length in bytes. */ - le32 reservedV; /* Reserved (zero). */ - } __attribute__((__packed__)) s; - } __attribute__((__packed__)) u; - le16 length; /* Byte size of this index entry, multiple of - 8-bytes. */ - le16 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. */ - le16 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. */ - le32 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; -#ifdef __sun -#pragma pack() -#endif - -#ifndef __sun -/** - * 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[]; /* Array of bits. */ -} __attribute__((__packed__)) BITMAP_ATTR; -#endif - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - le32 reparse_tag; /* Reparse point type (inc. flags). */ - le16 reparse_data_length; /* Byte size of reparse data. */ - le16 reserved; /* Align to 8-byte boundary. */ - u8 reparse_data[]; /* Meaning depends on reparse_tag. */ -} __attribute__((__packed__)) REPARSE_POINT; -#ifdef __sun -#pragma pack() -#endif - -/** - * struct EA_INFORMATION - Attribute: Extended attribute information (0xd0). - * - * NOTE: Always resident. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - le16 ea_length; /* Byte size of the packed extended - attributes. */ - le16 need_ea_count; /* The number of extended attributes which have - the NEED_EA bit set. */ - le32 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; -#ifdef __sun -#pragma pack() -#endif - -#ifdef __sun -typedef uint8_t EA_FLAGS; -#define NEED_EA (0x80) -#else /* not __sun */ -/** - * 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; -#endif /* __sun */ - -/** - * 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? - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - le32 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. */ - le16 value_length; /* Byte size of the EA's value. */ - u8 name[]; /* Name of the EA. */ -#ifndef __sun - u8 value[]; /* The value of the EA. Immediately - follows the name. */ -#endif -} __attribute__((__packed__)) EA_ATTR; -#ifdef __sun -#pragma pack() -#endif - -#ifndef __sun -/** - * 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; -#endif - -#ifndef __sun -/** - * 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; -#endif - -/* - * $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". - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { -/* 0*/ le32 length; /* Length of EFS attribute in bytes. */ - le32 state; /* Always 0? */ - le32 version; /* Efs version. Always 2? */ - le32 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*/ le32 offset_to_ddf_array;/* Offset in bytes to the array of data - decryption fields (DDF), see below. Zero if - no DDFs are present. */ - le32 offset_to_drf_array;/* Offset in bytes to the array of data - recovery fields (DRF), see below. Zero if - no DRFs are present. */ - le32 reserved; /* Reserved. */ -} __attribute__((__packed__)) EFS_ATTR_HEADER; -#ifdef __sun -#pragma pack() -#endif - -/** - * struct EFS_DF_ARRAY_HEADER - - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - le32 df_count; /* Number of data decryption/recovery fields in - the array. */ -} __attribute__((__packed__)) EFS_DF_ARRAY_HEADER; -#ifdef __sun -#pragma pack() -#endif - -/** - * struct EFS_DF_HEADER - - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { -/* 0*/ le32 df_length; /* Length of this data decryption/recovery - field in bytes. */ - le32 cred_header_offset;/* Offset in bytes to the credential header. */ - le32 fek_size; /* Size in bytes of the encrypted file - encryption key (FEK). */ - le32 fek_offset; /* Offset in bytes to the FEK from the start of - the data decryption/recovery field. */ -/* 16*/ le32 unknown1; /* always 0? Might be just padding. */ -} __attribute__((__packed__)) EFS_DF_HEADER; -#ifdef __sun -#pragma pack() -#endif - -/** - * struct EFS_DF_CREDENTIAL_HEADER - - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { -/* 0*/ le32 cred_length; /* Length of this credential in bytes. */ - le32 sid_offset; /* Offset in bytes to the user's sid from start - of this structure. Zero if no sid is - present. */ -/* 8*/ le32 type; /* Type of this credential: - 1 = CryptoAPI container. - 2 = Unexpected type. - 3 = Certificate thumbprint. - other = Unknown type. */ - union { - /* CryptoAPI container. */ - struct { -/* 12*/ le32 container_name_offset; /* Offset in bytes to - the name of the container from start of this - structure (may not be zero). */ -/* 16*/ le32 provider_name_offset; /* Offset in bytes to - the name of the provider from start of this - structure (may not be zero). */ - le32 public_key_blob_offset; /* Offset in bytes to - the public key blob from start of this - structure. */ -/* 24*/ le32 public_key_blob_size; /* Size in bytes of - public key blob. */ - } __attribute__((__packed__)) crypt; - /* Certificate thumbprint. */ - struct { -/* 12*/ le32 cert_thumbprint_header_size; /* Size in - bytes of the header of the certificate - thumbprint. */ -/* 16*/ le32 cert_thumbprint_header_offset; /* Offset in - bytes to the header of the certificate - thumbprint from start of this structure. */ - le32 unknown1; /* Always 0? Might be padding... */ - le32 unknown2; /* Always 0? Might be padding... */ - } __attribute__((__packed__)) cert; - } __attribute__((__packed__)) u; -} __attribute__((__packed__)) EFS_DF_CREDENTIAL_HEADER; -#ifdef __sun -#pragma pack() -#endif - -typedef EFS_DF_CREDENTIAL_HEADER EFS_DF_CRED_HEADER; - -/** - * struct EFS_DF_CERTIFICATE_THUMBPRINT_HEADER - - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { -/* 0*/ le32 thumbprint_offset; /* Offset in bytes to the thumbprint. */ - le32 thumbprint_size; /* Size of thumbprint in bytes. */ -/* 8*/ le32 container_name_offset; /* Offset in bytes to the name of the - container from start of this - structure or 0 if no name present. */ - le32 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*/ le32 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; -#ifdef __sun -#pragma pack() -#endif - -typedef EFS_DF_CERTIFICATE_THUMBPRINT_HEADER EFS_DF_CERT_THUMBPRINT_HEADER; - -#ifdef __sun -typedef uint64_t INTX_FILE_TYPES; -#define INTX_SYMBOLIC_LINK (const_cpu_to_le64(0x014B4E4C78746E49ULL)) -#define INTX_CHARACTER_DEVICE (const_cpu_to_le64(0x0052484378746E49ULL)) -#define INTX_BLOCK_DEVICE (const_cpu_to_le64(0x004B4C4278746E49ULL)) -#else /* not __sun */ -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; -#endif /* __sun */ - -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - INTX_FILE_TYPES magic; /* Intx file magic. */ - union { - /* For character and block devices. */ - struct { - le64 major; /* Major device number. */ - le64 minor; /* Minor device number. */ - char device_end; /* Marker for offsetof(). */ - } __attribute__((__packed__)) s; - /* For symbolic links. */ - ntfschar target[1]; - } __attribute__((__packed__)) u; -} __attribute__((__packed__)) INTX_FILE; -#ifdef __sun -#pragma pack() -#endif - -#endif /* defined _NTFS_LAYOUT_H */ diff --git a/usr/src/lib/libntfs/common/include/ntfs/lcnalloc.h b/usr/src/lib/libntfs/common/include/ntfs/lcnalloc.h deleted file mode 100644 index 07ab020d2d..0000000000 --- a/usr/src/lib/libntfs/common/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/usr/src/lib/libntfs/common/include/ntfs/list.h b/usr/src/lib/libntfs/common/include/ntfs/list.h deleted file mode 100644 index e3c774423d..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/list.h +++ /dev/null @@ -1,192 +0,0 @@ -/* - * list.h - Linked list implementation. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2002 Anton Altaparmakov and others - * - * 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_LIST_H -#define _NTFS_LIST_H - -/** - * struct list_head - Simple doubly linked list implementation. - * - * Copied from Linux kernel 2.4.2-ac18 into Linux-NTFS (with minor - * modifications). - AIA - * - * Some of the internal functions ("__xxx") are useful when - * manipulating whole lists rather than single entries, as - * sometimes we already know the next/prev entries and we can - * generate better code by using them directly rather than - * using the generic single-entry routines. - */ -struct list_head { - struct list_head *next, *prev; -}; - -#define LIST_HEAD_INIT(name) { &(name), &(name) } - -#define LIST_HEAD(name) \ - struct list_head name = LIST_HEAD_INIT(name) - -#define INIT_LIST_HEAD(ptr) do { \ - (ptr)->next = (ptr); (ptr)->prev = (ptr); \ -} while (0) - -/** - * __list_add - Insert a new entry between two known consecutive entries. - * @new: - * @prev: - * @next: - * - * This is only for internal list manipulation where we know the prev/next - * entries already! - */ -static __inline__ void __list_add(struct list_head * new, - struct list_head * prev, struct list_head * next) -{ - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; -} - -/** - * list_add - add a new entry - * @new: new entry to be added - * @head: list head to add it after - * - * Insert a new entry after the specified head. - * This is good for implementing stacks. - */ -static __inline__ void list_add(struct list_head *new, struct list_head *head) -{ - __list_add(new, head, head->next); -} - -/** - * list_add_tail - add a new entry - * @new: new entry to be added - * @head: list head to add it before - * - * Insert a new entry before the specified head. - * This is useful for implementing queues. - */ -static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) -{ - __list_add(new, head->prev, head); -} - -/** - * __list_del - - * @prev: - * @next: - * - * Delete a list entry by making the prev/next entries point to each other. - * - * This is only for internal list manipulation where we know the prev/next - * entries already! - */ -static __inline__ void __list_del(struct list_head * prev, - struct list_head * next) -{ - next->prev = prev; - prev->next = next; -} - -/** - * list_del - deletes entry from list. - * @entry: the element to delete from the list. - * - * Note: list_empty on entry does not return true after this, the entry is in - * an undefined state. - */ -static __inline__ void list_del(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); -} - -/** - * list_del_init - deletes entry from list and reinitialize it. - * @entry: the element to delete from the list. - */ -static __inline__ void list_del_init(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); - INIT_LIST_HEAD(entry); -} - -/** - * list_empty - tests whether a list is empty - * @head: the list to test. - */ -static __inline__ int list_empty(struct list_head *head) -{ - return head->next == head; -} - -/** - * list_splice - join two lists - * @list: the new list to add. - * @head: the place to add it in the first list. - */ -static __inline__ void list_splice(struct list_head *list, - struct list_head *head) -{ - struct list_head *first = list->next; - - if (first != list) { - struct list_head *last = list->prev; - struct list_head *at = head->next; - - first->prev = head; - head->next = first; - - last->next = at; - at->prev = last; - } -} - -/** - * list_entry - get the struct for this entry - * @ptr: the &struct list_head pointer. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_struct within the struct. - */ -#define list_entry(ptr, type, member) \ - ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) - -/** - * list_for_each - iterate over a list - * @pos: the &struct list_head to use as a loop counter. - * @head: the head for your list. - */ -#define list_for_each(pos, head) \ - for (pos = (head)->next; pos != (head); pos = pos->next) - -/** - * list_for_each_safe - iterate over a list safe against removal of list entry - * @pos: the &struct list_head to use as a loop counter. - * @n: another &struct list_head to use as temporary storage - * @head: the head for your list. - */ -#define list_for_each_safe(pos, n, head) \ - for (pos = (head)->next, n = pos->next; pos != (head); \ - pos = n, n = pos->next) - -#endif /* defined _NTFS_LIST_H */ - diff --git a/usr/src/lib/libntfs/common/include/ntfs/logfile.h b/usr/src/lib/libntfs/common/include/ntfs/logfile.h deleted file mode 100644 index 9c597ecb4d..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/logfile.h +++ /dev/null @@ -1,441 +0,0 @@ -/* - * logfile.h - Exports for $LogFile handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2005 Anton Altaparmakov - * Copyright (c) 2005-2007 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_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. - */ -#ifdef __sun -#pragma pack(1) -#endif -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; -#ifdef __sun -#pragma pack() -#endif - -/* - * 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 - -#ifdef __sun -#define RESTART_VOLUME_IS_CLEAN (const_cpu_to_le16(0x0002)) -#else /* not __sun */ -/* - * 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 = const_cpu_to_le16(0xffff), - /* gcc: Force enum bit width to 16. */ -} __attribute__((__packed__)); -#endif /* __sun */ - -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. - */ -#ifdef __sun -#pragma pack(1) -#endif -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; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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. - */ -#ifdef __sun -#pragma pack(1) -#endif -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; -#ifdef __sun -#pragma pack() -#endif - -/** - * 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?). - */ -#ifdef __sun -#pragma pack(1) -#endif -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; -#ifdef __sun -#pragma pack() -#endif - -/** - * enum LOG_RECORD_FLAGS - Possible 16-bit flags for log records. - * - * (Or is it log record pages?) - */ -#ifdef __sun -typedef const uint16_t LOG_RECORD_FLAGS; -#define LOG_RECORD_MULTI_PAGE (const_cpu_to_le16(0x0001)) -#else /* not __sun */ -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; -#endif /* __sun */ - -/** - * struct LOG_CLIENT_ID - The log client id structure identifying a log client. - */ -#ifdef __sun -#pragma pack(1) -#endif -typedef struct { - u16 seq_number; - u16 client_index; -} __attribute__((__packed__)) LOG_CLIENT_ID; -#ifdef __sun -#pragma pack() -#endif - -/** - * struct LOG_RECORD - Log record header. - * - * Each log record seems to have a constant size of 0x70 bytes. - */ -#ifdef __sun -#pragma pack(1) -#endif -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[]; -} __attribute__((__packed__)) LOG_RECORD; -#ifdef __sun -#pragma pack() -#endif - -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/usr/src/lib/libntfs/common/include/ntfs/logging.h b/usr/src/lib/libntfs/common/include/ntfs/logging.h deleted file mode 100644 index 524643a149..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/logging.h +++ /dev/null @@ -1,143 +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 <stdarg.h> -#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 ((u32)1 << 0) /* x = 42 */ -#define NTFS_LOG_LEVEL_TRACE ((u32)1 << 1) /* Entering function x() */ -#define NTFS_LOG_LEVEL_QUIET ((u32)1 << 2) /* Quietable output */ -#define NTFS_LOG_LEVEL_INFO ((u32)1 << 3) /* Volume needs defragmenting */ -#define NTFS_LOG_LEVEL_VERBOSE ((u32)1 << 4) /* Forced to continue */ -#define NTFS_LOG_LEVEL_PROGRESS ((u32)1 << 5) /* 54% complete */ -#define NTFS_LOG_LEVEL_WARNING ((u32)1 << 6) /* You should backup before starting */ -#define NTFS_LOG_LEVEL_ERROR ((u32)1 << 7) /* Operation failed, no damage done */ -#define NTFS_LOG_LEVEL_PERROR ((u32)1 << 8) /* Message : standard error description */ -#define NTFS_LOG_LEVEL_CRITICAL ((u32)1 << 9) /* Operation failed,damage may have occurred */ - -/* Logging style flags - Manage the style of the output */ -#define NTFS_LOG_FLAG_PREFIX ((u32)1 << 0) /* Prefix messages with "ERROR: ", etc */ -#define NTFS_LOG_FLAG_FILENAME ((u32)1 << 1) /* Show the file origin of the message */ -#define NTFS_LOG_FLAG_LINE ((u32)1 << 2) /* Show the line number of the message */ -#define NTFS_LOG_FLAG_FUNCTION ((u32)1 << 3) /* Show the function name containing the message */ -#define NTFS_LOG_FLAG_ONLYNAME ((u32)1 << 4) /* Only display the filename, not the pathname */ -#define NTFS_LOG_FLAG_COLOUR ((u32)1 << 5) /* Colour highlight some messages */ - -/* Macros to simplify logging. One for each level defined above. - * Note, if DEBUG is not defined, then ntfs_log_debug/trace have no effect. - */ -#if defined(__GNUC__) - -#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) - -#else /* not __GNUC__ */ - -#define PRINT(...) printf(__VA_ARGS__) - -#define ntfs_log_critical(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_CRITICAL,NULL,__VA_ARGS__) -#define ntfs_log_error(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_ERROR,NULL,__VA_ARGS__) -#define ntfs_log_info(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_INFO,NULL,__VA_ARGS__) -#define ntfs_log_perror(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_PERROR,NULL,__VA_ARGS__) -#define ntfs_log_progress(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_PROGRESS,NULL,__VA_ARGS__) -#define ntfs_log_quiet(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_QUIET,NULL,__VA_ARGS__) -#define ntfs_log_verbose(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_VERBOSE,NULL,__VA_ARGS__) -#define ntfs_log_warning(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_WARNING,NULL,__VA_ARGS__) - -#endif /* __GNUC__ */ - -/* - * By default debug and trace messages are compiled into the program, - * but not displayed. - */ -#if defined(__GNUC__) - -#ifndef DEBUG -#define ntfs_log_debug(FORMAT, ARGS...)do {} while (0) -#define ntfs_log_trace(FORMAT, ARGS...)do {} while (0) -#else /* !DEBUG */ -#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 /* DEBUG */ - -#else /* not __GNUC__ */ - -#ifndef DEBUG -#define ntfs_log_debug(...) do {} while (0) -#define ntfs_log_trace(...) do {} while (0) -#else /* !DEBUG */ -#define ntfs_log_debug(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_DEBUG,NULL,__VA_ARGS__) -#define ntfs_log_trace(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_TRACE,NULL,__VA_ARGS__) -#endif /* DEBUG */ - -#endif /* __GNUC__ */ - -#endif /* _LOGGING_H_ */ - diff --git a/usr/src/lib/libntfs/common/include/ntfs/mft.h b/usr/src/lib/libntfs/common/include/ntfs/mft.h deleted file mode 100644 index 180f61531d..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/mft.h +++ /dev/null @@ -1,116 +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); - -#endif /* defined _NTFS_MFT_H */ diff --git a/usr/src/lib/libntfs/common/include/ntfs/mst.h b/usr/src/lib/libntfs/common/include/ntfs/mst.h deleted file mode 100644 index 0808b3c115..0000000000 --- a/usr/src/lib/libntfs/common/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/usr/src/lib/libntfs/common/include/ntfs/ntfstime.h b/usr/src/lib/libntfs/common/include/ntfs/ntfstime.h deleted file mode 100644 index 2fb85a5413..0000000000 --- a/usr/src/lib/libntfs/common/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-2007 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 <time.h> -#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(sle64 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__ sle64 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/usr/src/lib/libntfs/common/include/ntfs/runlist.h b/usr/src/lib/libntfs/common/include/ntfs/runlist.h deleted file mode 100644 index f35202971f..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/runlist.h +++ /dev/null @@ -1,90 +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 int ntfs_rl_fill_zero(const ntfs_volume *vol, const runlist *rl, - s64 pos, const s64 count); - -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/usr/src/lib/libntfs/common/include/ntfs/security.h b/usr/src/lib/libntfs/common/include/ntfs/security.h deleted file mode 100644 index a61aabd754..0000000000 --- a/usr/src/lib/libntfs/common/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/usr/src/lib/libntfs/common/include/ntfs/support.h b/usr/src/lib/libntfs/common/include/ntfs/support.h deleted file mode 100644 index 7c1eed632a..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/support.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * support.h - Various useful things. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2004 Anton Altaparmakov - * Copyright (c) 2006 Szabolcs Szakacsits - * Copyright (c) 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 - */ - -#ifndef _NTFS_SUPPORT_H -#define _NTFS_SUPPORT_H - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDDEF_H -#include <stddef.h> -#endif - -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif - -#include "logging.h" - -/* - * 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 - -/* - * Round up and down @num to 2 in power of @order. - */ -#define ROUND_UP(num,order) (((num) + ((1 << (order)) - 1)) & \ - ~((1 << (order)) - 1)) -#define ROUND_DOWN(num,order) ((num) & ~((1 << (order)) - 1)) - -/* - * 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)) - -#ifdef __sun -#define test_and_set_bit(bit, var) _test_and_set_bit(bit, &var) -static __inline__ BOOL _test_and_set_bit(unsigned long bit, unsigned long *var) -{ - const BOOL old_state = test_bit(bit, *var); - set_bit(bit, *var); - return old_state; -} - -#define test_and_clear_bit(bit, var) _test_and_clear_bit(bit, &var) -static __inline__ BOOL _test_and_clear_bit(unsigned long bit, unsigned long *var) -{ - const BOOL old_state = test_bit(bit, *var); - clear_bit(bit, *var); - return old_state; -} -#else /* !__sun */ -#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 /* __sun */ - -/* Memory allocation with logging. */ -extern void *ntfs_calloc(size_t size); -extern void *ntfs_malloc(size_t size); - -#endif /* defined _NTFS_SUPPORT_H */ diff --git a/usr/src/lib/libntfs/common/include/ntfs/types.h b/usr/src/lib/libntfs/common/include/ntfs/types.h deleted file mode 100644 index cd9a9a998d..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/types.h +++ /dev/null @@ -1,142 +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 - * Copyright (c) 2006 Szabolcs Szakacsits - * Copyright (c) 2007 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_TYPES_H -#define _NTFS_TYPES_H - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#if HAVE_STDINT_H || !HAVE_CONFIG_H -#include <stdint.h> -#endif -#ifdef HAVE_SYS_TYPES_H -#include <sys/types.h> -#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; - -#if defined(__CHECKER__) && !defined(NTFS_DO_NOT_CHECK_ENDIANS) - #undef __bitwise - #undef __force - #define __bitwise __attribute__((bitwise)) - #define __force __attribute__((force)) -#else - #undef __bitwise - #undef __force - #define __bitwise - #define __force -#endif - -typedef u16 __bitwise le16; -typedef u32 __bitwise le32; -typedef u64 __bitwise le64; - -/* - * Declare sle{16,32,64} to be unsigned because we do not want sign extension - * on BE architectures. - */ -typedef u16 __bitwise sle16; -typedef u32 __bitwise sle32; -typedef u64 __bitwise sle64; - -typedef u16 __bitwise be16; -typedef u32 __bitwise be32; -typedef u64 __bitwise be64; - -typedef le16 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 <windef.h>'s - * As long as this file will be included after <windows.h> we're 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; - -#define STATUS_OK (0) -#define STATUS_ERROR (-1) -#define STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT (-2) -#define STATUS_KEEP_SEARCHING (-3) -#define STATUS_NOT_FOUND (-4) - -#endif /* defined _NTFS_TYPES_H */ - diff --git a/usr/src/lib/libntfs/common/include/ntfs/unistr.h b/usr/src/lib/libntfs/common/include/ntfs/unistr.h deleted file mode 100644 index 2c5fd5548c..0000000000 --- a/usr/src/lib/libntfs/common/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-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_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/usr/src/lib/libntfs/common/include/ntfs/version.h b/usr/src/lib/libntfs/common/include/ntfs/version.h deleted file mode 100644 index ec6dbdca32..0000000000 --- a/usr/src/lib/libntfs/common/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/usr/src/lib/libntfs/common/include/ntfs/volume.h b/usr/src/lib/libntfs/common/include/ntfs/volume.h deleted file mode 100644 index 3183d69b13..0000000000 --- a/usr/src/lib/libntfs/common/include/ntfs/volume.h +++ /dev/null @@ -1,247 +0,0 @@ -/* - * volume.h - Exports for NTFS volume handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2004 Anton Altaparmakov - * Copyright (c) 2005-2007 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 <stdio.h> -#endif -#ifdef HAVE_SYS_PARAM_H -#include <sys/param.h> -#endif -#ifdef HAVE_SYS_MOUNT_H -#include <sys/mount.h> -#endif -#ifdef HAVE_MNTENT_H -#include <mntent.h> -#endif - -/* Forward declaration */ -typedef struct _ntfs_volume ntfs_volume; - -#include "list.h" -#include "types.h" -#include "support.h" -#include "device.h" -#include "inode.h" -#include "attrib.h" - -/** - * enum ntfs_mount_flags - - * - * Flags for the ntfs_mount() function. - */ -typedef enum { - NTFS_MNT_RDONLY = 1, - NTFS_MNT_FORENSIC = 2, - NTFS_MNT_CASE_SENSITIVE = 4, - NTFS_MNT_NOT_EXCLUSIVE = 8, - NTFS_MNT_FORCE = 16, - NTFS_MNT_INTERIX = 32, -} ntfs_mount_flags; - -/** - * enum ntfs_mounted_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_mounted_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. */ - NV_WasDirty, /* 1: Volume was marked dirty before we mounted - it. */ - NV_ForensicMount, /* 1: Mount is forensic, i.e. no modifications - are to be done by mount/umount. */ - NV_Interix, /* 1: Make libntfs recognize Interix special - files. */ -} 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 NVolWasDirty(nv) test_nvol_flag(nv, WasDirty) -#define NVolSetWasDirty(nv) set_nvol_flag(nv, WasDirty) -#define NVolClearWasDirty(nv) clear_nvol_flag(nv, WasDirty) - -#define NVolForensicMount(nv) test_nvol_flag(nv, ForensicMount) -#define NVolSetForensicMount(nv) set_nvol_flag(nv, ForensicMount) -#define NVolClearForensicMount(nv) clear_nvol_flag(nv, ForensicMount) - -#define NVolInterix(nv) test_nvol_flag(nv, Interix) -#define NVolSetInterix(nv) set_nvol_flag(nv, Interix) -#define NVolClearInterix(nv) clear_nvol_flag(nv, Interix) - -/* - * 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 - -#define NTFS_INODE_CACHE_SIZE 512 /* WARNING: This should be power of 2. */ -#define NTFS_INODE_CACHE_SIZE_BITS (NTFS_INODE_CACHE_SIZE - 1) - -/** - * 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. */ - } u; - 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. */ - le16 flags; /* Bit array of VOLUME_* flags. */ - GUID guid; /* The volume guid if present (otherwise it is - a NULL guid). */ - - 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. */ - - long nr_free_clusters; /* This two are self explaining. */ - long nr_free_mft_records; - - struct list_head inode_cache[NTFS_INODE_CACHE_SIZE]; /* List of opened - inodes. */ -}; - -extern ntfs_volume *ntfs_volume_alloc(void); - -extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, - ntfs_mount_flags flags); - -extern ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, - ntfs_mount_flags flags); -extern int ntfs_device_umount(ntfs_volume *vol, const BOOL force); - -extern ntfs_volume *ntfs_mount(const char *name, ntfs_mount_flags 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 le16 flags); - -#endif /* defined _NTFS_VOLUME_H */ diff --git a/usr/src/lib/libntfs/common/libntfs/attrib.c b/usr/src/lib/libntfs/common/libntfs/attrib.c deleted file mode 100644 index 3c239bdaa3..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/attrib.c +++ /dev/null @@ -1,5234 +0,0 @@ -/** - * attrib.c - Attribute handling code. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2006 Anton Altaparmakov - * Copyright (c) 2002-2005 Richard Russon - * Copyright (c) 2002-2006 Szabolcs Szakacsits - * Copyright (c) 2004-2007 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 <stdio.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#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" -#include "support.h" -#include "crypto.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->u.nonres.data_size); - return (s64)le32_to_cpu(a->u.res.value_length); -} - -/** - * 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->u.res.value_length) + le16_to_cpu(a->u.res.value_offset) - > le32_to_cpu(a->length)) { - return 0; - } - - memcpy(b, (const char*)a + le16_to_cpu(a->u.res.value_offset), - le32_to_cpu(a->u.res.value_length)); - errno = 0; - return (s64)le32_to_cpu(a->u.res.value_length); - } - - /* Attribute is not resident. */ - - /* If no data, return 0. */ - if (!(a->u.nonres.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->u.nonres.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 = ntfs_malloc(rl[i].length << - vol->cluster_size_bits); - if (!intbuf) { - int eo = errno; - 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->u.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->u.nonres.data_size) - - total); - free(intbuf); - total = sle64_to_cpu(a->u.nonres.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->u.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 - free(rl); - 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 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; - struct list_head *pos; - 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; - } - /* Check cache, maybe this attribute already opened? */ - list_for_each(pos, &ni->attr_cache) { - ntfs_attr *tmp_na; - - tmp_na = list_entry(pos, ntfs_attr, list_entry); - if (tmp_na->type == type && tmp_na->name_len == name_len && - !ntfs_ucsncmp(tmp_na->name, name, name_len)) { - ntfs_log_trace("Found this attribute in cache, " - "increment reference count and " - "return it.\n"); - tmp_na->nr_references++; - return tmp_na; - } - } - /* Search failed. Properly open attrbute. */ - 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)) ? 1 : 0; - 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)? 1 : 0, - (a->flags & ATTR_IS_ENCRYPTED) ? 1 : 0, - (a->flags & ATTR_IS_SPARSE) ? 1 : 0, - sle64_to_cpu(a->u.nonres.allocated_size), - sle64_to_cpu(a->u.nonres.data_size), - sle64_to_cpu(a->u.nonres.initialized_size), - cs ? sle64_to_cpu(a->u.nonres.compressed_size) : 0, - cs ? a->u.nonres.compression_unit : 0); - } else { - s64 l = le32_to_cpu(a->u.res.value_length); - ntfs_attr_init(na, FALSE, (a->flags & ATTR_IS_COMPRESSED) ? 1:0, - (a->flags & ATTR_IS_ENCRYPTED) ? 1 : 0, - (a->flags & ATTR_IS_SPARSE) ? 1 : 0, - (l + 7) & ~7, l, l, cs ? (l + 7) & ~7 : 0, 0); - } - ntfs_attr_put_search_ctx(ctx); - if (NAttrEncrypted(na)) - ntfs_crypto_attr_open(na); - list_add_tail(&na->list_entry, &ni->attr_cache); - na->nr_references = 1; - 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; - na->nr_references--; - if (na->nr_references) { - ntfs_log_trace("There are %d more references left to " - "this attribute.\n", na->nr_references); - return; - } - ntfs_log_trace("There are no more references left to this attribute\n"); - list_del(&na->list_entry); - if (NAttrEncrypted(na)) - ntfs_crypto_attr_close(na); - 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 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; - - 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; - } - } - ntfs_attr_put_search_ctx(ctx); - return -1; -} - -/** - * ntfs_attr_map_runlist_range - map (a part of) a runlist of an ntfs attribute - * @na: ntfs attribute for which to map (part of) a runlist - * @from_vcn: map runlist part starting this vcn - * @to_vcn: map runlist part ending this vcn - * - * Map the part of a runlist from containing the @from_vcn to containing the - * @to_vcn of an ntfs attribute @na. It is OK for @to_vcn to be beyond last run. - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -int ntfs_attr_map_runlist_range(ntfs_attr *na, VCN from_vcn, VCN to_vcn) -{ - ntfs_attr_search_ctx *ctx = NULL; - runlist *rl; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, " - "from_vcn 0x%llx, to_vcn 0x%llx.\n", - (unsigned long long)na->ni->mft_no, na->type, - (long long)from_vcn, (long long)to_vcn); - - /* Map extent with @from_vcn. */ - if (ntfs_attr_map_runlist(na, from_vcn)) - goto err_out; - - for (rl = na->rl; rl->vcn <= to_vcn;) { - /* Skip not interesting to us runs. */ - if (rl->lcn >= 0 || rl->lcn == LCN_HOLE || (rl->vcn + - rl->length < from_vcn && - rl->lcn == LCN_RL_NOT_MAPPED)) { - rl++; - continue; - } - - /* We reached the end of runlist, just exit. */ - if (rl->lcn == LCN_ENOENT) - break; - - /* Check for errors. */ - if (rl->lcn < 0 && rl->lcn != LCN_RL_NOT_MAPPED) { - errno = EIO; - goto err_out; - } - - /* Runlist is not mapped here. */ - if (!ctx) { - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - goto err_out; - } - /* Find the attribute in the mft record. */ - if (ntfs_attr_lookup(na->type, na->name, na->name_len, - CASE_SENSITIVE, rl->vcn, NULL, 0, - ctx)) - goto err_out; - - /* Decode the runlist. */ - rl = ntfs_mapping_pairs_decompress(na->ni->vol, ctx->attr, - na->rl); - if (!rl) - goto err_out; - na->rl = rl; - } - - ntfs_attr_put_search_ctx(ctx); - ntfs_log_trace("Done.\n"); - return 0; -err_out: - ntfs_attr_put_search_ctx(ctx); - ntfs_log_trace("Failed.\n"); - 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 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->u.nonres.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->u.nonres.allocated_size) >> - vol->cluster_size_bits; - } - - /* Get the lowest vcn for the next extent. */ - highest_vcn = sle64_to_cpu(a->u.nonres.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->u.nonres.lowest_vcn)) { - ntfs_log_trace("Inode has corrupt attribute list " - "attribute.\n"); - errno = EIO; - goto err_out; - } - } - if (!a) { - if (errno == ENOENT) - ntfs_log_trace("Attribute not found. " - "Inode is corrupt.\n"); - else - ntfs_log_trace("Inode is corrupt.\n"); - 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)) - return ntfs_crypto_attr_pread(na, pos, count, b); - - vol = na->ni->vol; - 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->u.res.value_offset); - if (val < (char*)ctx->attr || val + - le32_to_cpu(ctx->attr->u.res.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->u.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; - } - 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, FALSE)) { - 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->u.res.value_offset); - if (val < (char*)ctx->attr || val + - le32_to_cpu(ctx->attr->u.res.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) { - /* - * Map runlist between initialized size and place we start - * writing at. - */ - if (ntfs_attr_map_runlist_range(na, na->initialized_size >> - vol->cluster_size_bits, - pos >> vol->cluster_size_bits)) - 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 && ntfs_rl_fill_zero(vol, - na->rl, na->initialized_size, - pos - na->initialized_size)) - goto err_out; - - ctx->attr->u.nonres.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->u.nonres.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; - - if (rl->lcn != (LCN)LCN_HOLE) { - errno = EIO; - goto rl_err_out; - } - - to_write = min(count, (rl->length << - vol->cluster_size_bits) - ofs); - - /* Instantiate the hole. */ - cur_vcn = rl->vcn; - from_vcn = rl->vcn + (ofs >> vol->cluster_size_bits); - ntfs_log_trace("Instantiate 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); - if (lcn_seek_from < -1) - lcn_seek_from = -1; - 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 " - "inconsistent 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 (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; - } - /* - * Clear region between start of @rl->vcn cluster and - * @ofs if necessary. - */ - if (ofs && ntfs_rl_fill_zero(vol, na->rl, rl->vcn << - vol->cluster_size_bits, ofs)) - goto err_out; - } - /* 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)) { - s64 pos = (rl->lcn << vol->cluster_size_bits) + ofs; - int bsize = 4096; /* FIXME: Test whether we need - PAGE_SIZE here. Eg., on IA64. */ - /* - * Write 4096 size blocks if it's possible. This will - * cause the kernel not to seek and read disk blocks for - * filling the end of the buffer which increases write - * speed. - */ - if (vol->cluster_size >= bsize && !(ofs % bsize) && - (to_write % bsize) && ofs + to_write == - na->initialized_size) { - char *cb; - s64 rounded = (to_write + bsize - 1) & - ~(bsize - 1); - - cb = ntfs_malloc(rounded); - if (!cb) - goto err_out; - memcpy(cb, b, to_write); - memset(cb + to_write, 0, rounded - to_write); - written = ntfs_pwrite(vol->u.dev, pos, rounded, - cb); - if (written > to_write) - written = to_write; - free(cb); - } else - written = ntfs_pwrite(vol->u.dev, pos, 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) { - if (ntfs_attr_update_mapping_pairs(na, update_from)) { - /* FIXME: We want rollback here. */ - ntfs_log_perror("%s(): Failed to update mapping pairs. " - "Leaving inconsistent metadata. " - "Run chkdsk!", "ntfs_attr_pwrite"); - errno = EIO; - return -1; - } - } - /* 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 (ctx) { - 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->u.nonres.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, 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 for attribute type 0x%x.\n", type); - - 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->u.res.value_offset), - min(val_len, - le32_to_cpu(a->u.res.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->u.res.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; - } - } - a = 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. - * - * 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->u.res.value_length) == val_len && - !memcmp((char*)a + le16_to_cpu(a->u.res.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 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 = ntfs_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. This function does not change - * errno and doing nothing if NULL passed to it. - */ -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) { - ntfs_log_trace("Attribute can't be non-resident\n"); - 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; - - ntfs_log_trace("Attribute can't be resident\n"); - 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)) { - ntfs_log_trace("Not enough space in the MFT record\n"); - 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, le32_to_cpu(type), le16_to_cpu(flags)); - - if (!ni || (!name && name_len)) { - errno = EINVAL; - return -1; - } - - if (ntfs_attr_can_be_resident(ni->vol, type)) { - if (errno == EPERM) - ntfs_log_trace("Attribute can't be resident.\n"); - else - ntfs_log_trace("ntfs_attr_can_be_resident failed.\n"); - 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, u.res.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, u.res.resident_end)); - a->flags = flags; - a->instance = m->next_attr_instance; - a->u.res.value_length = cpu_to_le32(size); - a->u.res.value_offset = cpu_to_le16(length - ((size + 7) & ~7)); - if (val) - memcpy((u8*)a + le16_to_cpu(a->u.res.value_offset), val, size); - else - memset((u8*)a + le16_to_cpu(a->u.res.value_offset), 0, size); - if (type == AT_FILE_NAME) - a->u.res.resident_flags = RESIDENT_ATTR_IS_INDEXED; - else - a->u.res.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->u.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, le32_to_cpu(type), - (long long) lowest_vcn, dataruns_size, - le16_to_cpu(flags)); - - if (!ni || dataruns_size <= 0 || (!name && name_len)) { - errno = EINVAL; - return -1; - } - - if (ntfs_attr_can_be_non_resident(ni->vol, type)) { - 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"); - 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, u.nonres.compressed_size) + ((sizeof(ntfschar) * - name_len + 7) & ~7) + dataruns_size + - ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? - sizeof(a->u.nonres.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, u.nonres.compressed_size) + - ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? - sizeof(a->u.nonres.compressed_size) : 0)); - a->flags = flags; - a->instance = m->next_attr_instance; - a->u.nonres.lowest_vcn = cpu_to_sle64(lowest_vcn); - a->u.nonres.mapping_pairs_offset = cpu_to_le16(length - dataruns_size); - a->u.nonres.compression_unit = (flags & ATTR_IS_COMPRESSED) ? 4 : 0; - /* If @lowest_vcn == 0, than setup empty attribute. */ - if (!lowest_vcn) { - a->u.nonres.highest_vcn = cpu_to_sle64(-1); - a->u.nonres.allocated_size = 0; - a->u.nonres.data_size = 0; - a->u.nonres.initialized_size = 0; - /* Set empty mapping pairs. */ - *((u8*)a + le16_to_cpu(a->u.nonres.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->u.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 " - "inconsistent 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 " - "inconsistent 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)) { - ntfs_log_trace("Couldn't delete record from " - "$ATTRIBUTE_LIST.\n"); - 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 = TRUE; - BOOL always_non_resident = FALSE, always_resident = FALSE; - 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->u.base_ni; - - /* Check the attribute type and the size. */ - if (ntfs_attr_size_bounds_check(ni->vol, type, size)) { - if (errno == ERANGE) { - ntfs_log_trace("Size bounds check failed.\n"); - } else if (errno == ENOENT) { - ntfs_log_trace("Invalid attribute type. Aborting...\n"); - errno = EIO; - } - 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; - } - always_resident = TRUE; - } - - /* Check whether attribute can be resident. */ - if (ntfs_attr_can_be_resident(ni->vol, type)) { - if (errno != EPERM) { - err = errno; - ntfs_log_trace("ntfs_attr_can_be_resident() failed.\n"); - goto err_out; - } - is_resident = FALSE; - always_non_resident = TRUE; - } - -retry: - /* Calculate attribute record size. */ - if (is_resident) - attr_rec_size = offsetof(ATTR_RECORD, u.res.resident_end) + - ROUND_UP(name_len * sizeof(ntfschar), 3) + - ROUND_UP(size, 3); - else /* We add 8 for space for mapping pairs. */ - attr_rec_size = offsetof(ATTR_RECORD, u.nonres.non_resident_end) + - ROUND_UP(name_len * sizeof(ntfschar), 3) + 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->u.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; - } - - /* - * If failed to find space for resident attribute, then try to find - * space for non resident one. - */ - if (is_resident && !always_resident) { - is_resident = FALSE; - goto retry; - } - - /* - * FIXME: Try to make other attributes non-resident here. Factor out - * code from ntfs_resident_attr_resize. - */ - - /* 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 for attribute. */ - 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; - } - - /* - * Determine resident or not will be attribute using heuristics and - * calculate attribute record size. FIXME: small code duplication here. - */ - if (always_resident || (!always_non_resident && size < 256)) { - is_resident = TRUE; - attr_rec_size = offsetof(ATTR_RECORD, u.res.resident_end) + - ROUND_UP(name_len * sizeof(ntfschar), 3) + - ROUND_UP(size, 3); - } else { /* We add 8 for space for mapping pairs. */ - is_resident = FALSE; - attr_rec_size = offsetof(ATTR_RECORD, u.nonres.non_resident_end) + - ROUND_UP(name_len * sizeof(ntfschar), 3) + 8; - } - -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 inconsistent " - "metadata.\n"); - 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) - - le16_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 " - "inconsistent 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. This function always - * closes @na upon exit (both on success and failure). - * - * 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)) { - ntfs_attr_close(na); - return -1; - } - if (ntfs_cluster_free(na->ni->vol, na, 0, -1) < 0) { - ntfs_log_trace("Failed to free cluster allocation. " - "Leaving inconsistent metadata.\n"); - ret = -1; - } - } - - /* Search for attribute extents and remove them all. */ - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) { - ntfs_attr_close(na); - 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 inconsistent metadata.\n"); - ret = -1; - } - ntfs_attr_reinit_search_ctx(ctx); - } - if (errno != ENOENT) { - ntfs_log_trace("Attribute lookup failed. " - "Probably leaving inconsistent 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->u.res.value_offset)) { - ntfs_log_trace("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->u.res.value_offset) + - new_size + 7) & ~7) < 0) { - if (errno != ENOSPC) { - int eo = errno; - ntfs_log_trace("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->u.res.value_length)) - memset((u8*)a + le16_to_cpu(a->u.res.value_offset) + - le32_to_cpu(a->u.res.value_length), 0, new_size - - le32_to_cpu(a->u.res.value_length)); - /* Finally update the length of the attribute value. */ - a->u.res.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) { - ntfs_log_trace("Couldn't obtain search context.\n"); - 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; - - 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)) { - ntfs_log_trace("Couldn't attach extent inode.\n"); - 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->u.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) { - ntfs_log_trace("Couldn't allocate new MFT record.\n"); - return -1; - } - if (ntfs_attr_record_move_to(ctx, ni)) { - ntfs_log_trace("Couldn't move attribute to new MFT record.\n"); - 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; - - 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->u.res.value_offset)) { - ntfs_log_trace("Name is placed after the attribute value. " - "Corrupted inode. Run chkdsk. Aborting...\n"); - errno = EIO; - return -1; - } - - new_allocated_size = (le32_to_cpu(a->u.res.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) { - ntfs_log_trace("Eeek! Failed to allocate " - "cluster(s). Aborting...\n"); - } - 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->u.res.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->u.res.value_length), - (u8*)a + le16_to_cpu(a->u.res.value_offset)); - if (bw != le32_to_cpu(a->u.res.value_length)) { - ntfs_log_debug("Failed to write out attribute value " - "(bw = %lli, errno = %i). " - "Aborting...\n", (long long)bw, errno); - if (bw >= 0) - errno = 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) { - ntfs_log_debug("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->u.nonres.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) { - if (errno != ENOSPC) { - ntfs_log_trace("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->u.nonres.lowest_vcn = cpu_to_sle64(0); - a->u.nonres.highest_vcn = cpu_to_sle64((new_allocated_size - 1) >> - vol->cluster_size_bits); - - a->u.nonres.mapping_pairs_offset = cpu_to_le16(mp_ofs); - - a->u.nonres.compression_unit = 0; - - memset(&a->u.nonres.reserved1, 0, sizeof(a->u.nonres.reserved1)); - - a->u.nonres.allocated_size = cpu_to_sle64(new_allocated_size); - a->u.nonres.data_size = a->u.nonres.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) { - // 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", errno); - return -1; - } - - /* Done! */ - return 0; - -cluster_free_err_out: - if (rl && ntfs_cluster_free(vol, na, 0, -1) < 0) - ntfs_log_trace("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); - 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 on the volume to allocate - * new clusters or in base mft to resize $ATTRIBUTE_LIST. - * EOVERFLOW - Resident attribute can not become non resident and - * already filled whole MFT record, but had not reached - * @newsize bytes length. - */ -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, new size %lld.\n", - (unsigned long long)na->ni->mft_no, na->type, - (long long)newsize); - - /* 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("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 = ROUND_UP(newsize, 3); - 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("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("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, - u.nonres.compressed_size) + ROUND_UP(a->name_length * - sizeof(ntfschar), 3) + 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, - u.nonres.non_resident_end) + 8)) { - ntfs_log_trace("Couldn't free space in the MFT record " - "to make attribute list non " - "resident.\n"); - 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; - } - - /* - * Force index allocation creation instead of moving out index root - * from the base MFT record. - */ - if (na->type == AT_INDEX_ROOT && na->data_size > sizeof(INDEX_ROOT) + - sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN)) { - INDEX_ROOT *ir; - - ir = (INDEX_ROOT*)((u8*)ctx->attr + - le16_to_cpu(ctx->attr->u.res.value_offset)); - if (!(ir->index.flags & LARGE_INDEX)) { - err = EOVERFLOW; - 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 = EOVERFLOW; - goto put_err_out; - } - - /* Add attribute list if not present. */ - if (na->ni->nr_extents == -1) - ni = na->ni->u.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; - 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->u.nonres.lowest_vcn)) { - ntfs_log_trace("Should be called for the first extent of the " - "attribute. Aborting...\n"); - errno = EINVAL; - return -1; - } - - /* Some preliminary sanity checking. */ - if (!NAttrNonResident(na)) { - ntfs_log_trace("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->u.nonres.mapping_pairs_offset)) { - ntfs_log_trace("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)) { - ntfs_log_trace("Not enough space to make attribute resident\n"); - 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->u.res.value_length = cpu_to_le32(na->data_size); - a->u.res.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->u.res.resident_flags = RESIDENT_ATTR_IS_INDEXED; - else - a->u.res.resident_flags = 0; - a->u.res.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) - errno = EIO; - ntfs_log_trace("Eeek! Failed to read attribute data. Leaving " - "inconsistent metadata. Run chkdsk. " - "Aborting...\n"); - 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) { - 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. - * - * New runlist should be fully formed starting @from_vcn. Runs before @from_vcn - * can be mapped or not, but on-disk structures should not be modified before - * call to this function so they can be mapped if necessary. - * - * FIXME: Make it O(1) for sparse files too, not only for normal. - * - * FIXME: Rewrite without using NTFS_VCN_DELETE_MARK define. - * - * NOTE: Be careful in the future with updating bits on compressed files (at - * present assumed that on-disk flag is already set/cleared before call to - * this function). - * - * 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) { - 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, from vcn 0x%lld." - "\n", (unsigned long long)na->ni->mft_no, na->type, - from_vcn); - - if (na->ni->nr_extents == -1) - base_ni = na->ni->u.base_ni; - else - base_ni = na->ni; - - ctx = ntfs_attr_get_search_ctx(base_ni, NULL); - if (!ctx) { - ntfs_log_trace("Couldn't get search context.\n"); - 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, ctx->is_first ? 0 : 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 && a->u.nonres.lowest_vcn) { - LCN first_lcn; - - stop_vcn = sle64_to_cpu(a->u.nonres.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 attribute 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->u.nonres.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->u.nonres.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->u.nonres.lowest_vcn) { - int sparse; - - /* Update allocated size. */ - a->u.nonres.allocated_size = cpu_to_sle64(na->allocated_size); - /* - * Check whether part of runlist we are updating is - * sparse. - */ - sparse = ntfs_rl_sparse(na->rl); - if (sparse == -1) { - ntfs_log_trace("Bad runlist.\n"); - err = errno; - goto put_err_out; - } - /* - * If new part or on-disk attribute is not sparse, then - * we should fully map runlist to make final decision. - */ - if (sparse || (a->flags & ATTR_IS_SPARSE)) { - if (from_vcn && ntfs_attr_map_runlist_range(na, - 0, from_vcn - 1)) { - ntfs_log_trace("Failed to map runlist " - "before @from_vcn.\n"); - err = errno; - goto put_err_out; - } - /* - * Reconsider whether whole runlist is sparse - * if new part is not. - */ - if (!sparse) { - sparse = ntfs_rl_sparse(na->rl); - if (sparse == -1) { - ntfs_log_trace("Bad " - "runlist.\n"); - err = errno; - goto put_err_out; - } - } - } - /* Attribute becomes sparse/compressed. */ - 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->u.nonres.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->u.nonres.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->u.nonres.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->u.nonres.mapping_pairs_offset = - cpu_to_le16(le16_to_cpu( - a->u.nonres.mapping_pairs_offset) + 8); - /* - * We should update all mapping pairs, because - * we shifted their starting position. - */ - from_vcn = 0; - } - /* Attribute becomes normal. */ - if (!sparse && (a->flags & ATTR_IS_SPARSE) && - !(a->flags & ATTR_IS_COMPRESSED)) { - NAttrClearSparse(na); - a->flags &= ~ATTR_IS_SPARSE; - a->u.nonres.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)); - /* - * Windows defragmentation tool do not update - * name offset correctly for unnamed - * attributes, but chkdsk do not like when it - * negative, so do not change it at all if it - * would become negative. - */ - if (le16_to_cpu(a->name_offset) >= 8) - a->name_offset = cpu_to_le16( - le16_to_cpu( - a->name_offset) - 8); - a->u.nonres.mapping_pairs_offset = - cpu_to_le16(le16_to_cpu( - a->u.nonres.mapping_pairs_offset) - 8); - /* - * We should update all mapping pairs, because - * we shifted their starting position. - */ - from_vcn = 0; - } - /* Update compressed size if required. */ - if (sparse || (a->flags & ATTR_IS_COMPRESSED)) { - 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 " - "inconsistent " - "metadata.\n"); - goto put_err_out; - } - na->compressed_size = new_compr_size; - a->u.nonres.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); - } - - /* - * We do want to do anything for the first extent in - * case we are updating mapping pairs not from the - * begging. - */ - if (!a->u.nonres.highest_vcn || from_vcn <= - sle64_to_cpu(a->u.nonres.highest_vcn) + 1) - from_vcn = 0; - else { - if (from_vcn) - continue; - } - } - - /* 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->u.nonres.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)) { - ntfs_log_trace("Couldn't add attribute " - "list.\n"); - 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->u.nonres.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->u.nonres.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->u.nonres.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->u.nonres.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; - } - /* Sanity check. */ - if (from_vcn) { - err = ENOMSG; - ntfs_log_error("Library BUG! @from_vcn is nonzero, please " - "report to %s.\n", NTFS_DEV_LIST); - 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->u.nonres.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); - ntfs_log_trace("Done!"); - 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, u.nonres.compressed_size) + - ((NAttrCompressed(na) || NAttrSparse(na)) ? - sizeof(a->u.nonres.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->u.nonres.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->u.nonres.highest_vcn = cpu_to_sle64(stop_vcn - 1); - ntfs_inode_mark_dirty(ni); - /* All mapping pairs has been written. */ - if (!err) - break; - } - ntfs_log_trace("Done!\n"); - 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, newsize %lld.\n", - (unsigned long long)na->ni->mft_no, na->type, - (long long)newsize); - - 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) { - if (errno == ERANGE) { - ntfs_log_trace("Eeek! Size bounds check failed. " - "Aborting...\n"); - } else if (errno == ENOENT) - errno = EIO; - 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)) { - ntfs_log_trace("Eeek! ntfs_attr_map_whole_runlist " - "failed.\n"); - 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) { - ntfs_log_trace("Eeek! Freeing of clusters failed. " - "Aborting...\n"); - 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, first_free_vcn)) { - ntfs_log_trace("Eeek! Mapping pairs update failed. " - "Leaving inconsistent metadata. " - "Run chkdsk.\n"); - return -1; - } - } - - /* Get the first attribute record. */ - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) { - ntfs_log_trace("Couldn't get attribute search context.\n"); - 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 inconsistent metadata.\n"); - goto put_err_out; - } - - /* Update data and initialized size. */ - na->data_size = newsize; - ctx->attr->u.nonres.data_size = cpu_to_sle64(newsize); - if (newsize < na->initialized_size) { - na->initialized_size = newsize; - ctx->attr->u.nonres.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 - * @sparse: if TRUE then will create hole if possible - * - * 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, - BOOL sparse) -{ - 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) { - if (errno == ERANGE) { - ntfs_log_trace("Eeek! Size bounds check failed. " - "Aborting...\n"); - } else if (errno == ENOENT) - errno = EIO; - 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) { - /* Map required part of runlist. */ - if (ntfs_attr_map_runlist(na, na->allocated_size >> - vol->cluster_size_bits)) { - ntfs_log_error("Failed to map runlist.\n"); - 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 && sparse) { - rl = ntfs_malloc(0x1000); - if (!rl) - 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 lcn_seek_from; - - 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) { - ntfs_log_trace("Cluster allocation failed.\n"); - 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("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, org_alloc_size >> - vol->cluster_size_bits)) { - err = errno; - ntfs_log_trace("Mapping pairs update failed.\n"); - goto rollback; - } - } - - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) { - ntfs_log_trace("Failed to get search context.\n"); - if (na->allocated_size == org_alloc_size) { - return -1; - } - err = errno; - 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->u.nonres.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; - /* Restore mapping pairs. */ - if (ntfs_attr_update_mapping_pairs(na, 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 - * @sparse: if TRUE then will create hole if possible - * - * 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. - * EACCES - Attribute is encrypted. - * ERANGE - @newsize is not valid for the attribute type of @na. - * ENOSPC - There is no enough space on the volume to allocate - * new clusters or in base mft to resize $ATTRIBUTE_LIST. - * EOVERFLOW - Resident attribute can not become non resident and - * already filled whole MFT record, but had not reached - * @newsize bytes length. - * EOPNOTSUPP - The desired resize is not implemented yet. - */ -int __ntfs_attr_truncate(ntfs_attr *na, const s64 newsize, BOOL sparse) -{ - 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; - ntfs_log_trace("Failed (encrypted).\n"); - return -1; - } - /* - * TODO: Implement making handling of compressed attributes. - */ - if (NAttrCompressed(na)) { - errno = EOPNOTSUPP; - ntfs_log_trace("Failed (compressed).\n"); - return -1; - } - if (NAttrNonResident(na)) { - if (newsize > na->data_size) - ret = ntfs_non_resident_attr_expand(na, newsize, - sparse); - else - ret = ntfs_non_resident_attr_shrink(na, newsize); - } else - ret = ntfs_resident_attr_resize(na, newsize); - if (!ret) - ntfs_log_trace("Done!\n"); - else - ntfs_log_trace("Failed.\n"); - return ret; -} - - -/** - * Wrapper around __ntfs_attr_truncate that always tries to creates hole - */ -int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) -{ - return __ntfs_attr_truncate(na, newsize, TRUE); -} - - -/** - * 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 = ntfs_malloc(na->data_size); - if (!data) - 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; -} - -/** - * ntfs_attr_exist - FIXME: description - */ -int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, - u32 name_len) -{ - ntfs_attr_search_ctx *ctx; - int ret; - - ntfs_log_trace("Entering.\n"); - - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) - return 0; - - ret = ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, 0, NULL, 0, - ctx); - - ntfs_attr_put_search_ctx(ctx); - return !ret; -} diff --git a/usr/src/lib/libntfs/common/libntfs/attrlist.c b/usr/src/lib/libntfs/common/libntfs/attrlist.c deleted file mode 100644 index 3bbc6a3ca8..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/attrlist.c +++ /dev/null @@ -1,320 +0,0 @@ -/** - * attrlist.c - Attribute list attribute handling code. Part of the Linux-NTFS - * project. - * - * Copyright (c) 2004-2005 Anton Altaparmakov - * Copyright (c) 2004-2007 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 <string.h> -#endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif - -#include "compat.h" -#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; - leMFT_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->u.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) ? sle64_to_cpu(attr->u.nonres.lowest_vcn) : - 0, (attr->non_resident) ? NULL : ((u8*)attr + - le16_to_cpu(attr->u.res.value_offset)), (attr->non_resident) ? - 0 : le32_to_cpu(attr->u.res.value_length), ctx)) { - /* Found some extent, check it to be before new extent. */ - if (ctx->al_entry->lowest_vcn == attr->u.nonres.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); - /* 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->u.nonres.lowest_vcn; - else - ale->lowest_vcn = 0; - ale->mft_reference = mref; - ale->instance = attr->instance; - NTFS_ON_DEBUG(memset(ale->name, 0, ((u8*)((u8*)ale + entry_len)) - - ((u8*)ale->name))); /* Shut up, valgrind. */ - 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) sle64_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/usr/src/lib/libntfs/common/libntfs/bitmap.c b/usr/src/lib/libntfs/common/libntfs/bitmap.c deleted file mode 100644 index 2f7d6bb84a..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/bitmap.c +++ /dev/null @@ -1,249 +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 <stdlib.h> -#endif -#ifdef HAVE_STDIO_H -#include <stdio.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif - -#include "compat.h" -#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 int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, - s64 count, int value) -{ - ntfs_volume *vol = na->ni->vol; - s64 bufsize, br, left = count; - 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; - - buf = (u8*)ntfs_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) && left--) { - if (value) - *buf |= 1 << bit++; - else - *buf &= ~(1 << bit++); - } - /* Update @start_bit to the new position. */ - start_bit = (start_bit + 7) & ~7; - } - - /* Loop until @left reaches zero. */ - lastbyte = 0; - lastbyte_buf = NULL; - bit = left & 7; - do { - /* If there is a last partial byte... */ - if (left > 0 && bit) { - lastbyte_pos = ((left + 7) >> 3) + firstbyte; - if (!lastbyte_pos) { - // FIXME: Eeek! BUG! - ntfs_log_trace("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 + left) >> - 3, 1, lastbyte_buf); - if (br != 1) { - // FIXME: Eeek! We need rollback! (AIA) - ntfs_log_trace("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 && left--) { - 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("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; - left -= tmp; - if (bufsize > (tmp = (left + 7) >> 3)) - bufsize = tmp; - - if (lastbyte && left != 0) { - // FIXME: Eeek! BUG! - ntfs_log_trace("Last buffer but count is not zero (= " - "%lli). Leaving inconsistent metadata." - "\n", (long long)left); - err = EIO; - goto free_err_out; - } - } while (left > 0); - - /* Update free clusters and MFT records. */ - if (na == vol->mftbmp_na) { - if (value) - vol->nr_free_mft_records -= count; - else - vol->nr_free_mft_records += count; - } - if (na == vol->lcnbmp_na) { - if (value) - vol->nr_free_clusters -= count; - else - vol->nr_free_clusters += count; - } - - /* 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); -} - diff --git a/usr/src/lib/libntfs/common/libntfs/bootsect.c b/usr/src/lib/libntfs/common/libntfs/bootsect.c deleted file mode 100644 index 3c4e9ca9b2..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/bootsect.c +++ /dev/null @@ -1,273 +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 <stdio.h> -#endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#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"); - - /* - * Check that checksum == sum of u32 values from b to the checksum - * field. If checksum is zero, no checking is done. We will work when - * the checksum test fails, since some utilities update the boot sector - * ignoring the checksum which leaves the checksum out-of-date. We - * report a warning if this is the case. - */ - if ((void*)b < (void*)&b->checksum && 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) { - ntfs_log_debug("FAILED\n"); - ntfs_log_debug("The NTFS bootsector contains an " - "incorrect checksum."); - } else - ntfs_log_debug("OK\n"); - } - - /* Check OEMidentifier is "NTFS " */ - ntfs_log_debug("Checking OEMid... "); - if (b->oem_id != NTFS_SB_MAGIC) /* "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->u.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->u.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->u.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->u.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); - /* - * Windows cares only about first 4 records in $MFTMirr and inores - * everything beyend them. - */ - vol->mftmirr_size = 4; - return 0; -} diff --git a/usr/src/lib/libntfs/common/libntfs/collate.c b/usr/src/lib/libntfs/common/libntfs/collate.c deleted file mode 100644 index 566ceef475..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/collate.c +++ /dev/null @@ -1,220 +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 <string.h> -#endif - -#include "compat.h" -#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, size_t data1_len, - const void *data2, size_t 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, size_t data1_len, - const void *data2, size_t 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, size_t data1_len __attribute__((unused)), - const void *data2, size_t 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 *, size_t, - const void *, size_t); - -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_is_collation_rule_supported - Check if a collation rule is implemented. - * @cr: The to-be-checked collation rule - * - * Use this function to know if @cr is supported by libntfs. - * - * 7 collation rules are known to be supported by NTFS as defined - * in layout.h. However, libntfs only support 3 of them ATM. - * - * Return TRUE if @cr is supported. FALSE otherwise. - */ -BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr) -{ - return (cr == COLLATION_BINARY || cr == COLLATION_NTOFS_ULONG || - cr == COLLATION_FILE_NAME); - /* - * FIXME: At the moment we only support COLLATION_BINARY, - * COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME. - * The correct future implementation of this function should be: - * - * u32 i = le32_to_cpu(cr); - * return ((i <= 0x02) || ((i >= 0x10) && (i <= 0x13))); - */ -} - -/** - * 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, size_t data1_len, - const void *data2, size_t data2_len) -{ - u32 i; - - ntfs_log_trace("Entering.\n"); - if (!vol || !data1 || !data2) { - ntfs_log_error("Invalid arguments passed.\n"); - return NTFS_COLLATION_ERROR; - } - - if (!ntfs_is_collation_rule_supported(cr)) - goto err; - i = le32_to_cpu(cr); - 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/usr/src/lib/libntfs/common/libntfs/compat.c b/usr/src/lib/libntfs/common/libntfs/compat.c deleted file mode 100644 index acdf4db7ce..0000000000 --- a/usr/src/lib/libntfs/common/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/usr/src/lib/libntfs/common/libntfs/compress.c b/usr/src/lib/libntfs/common/libntfs/compress.c deleted file mode 100644 index def04e46eb..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/compress.c +++ /dev/null @@ -1,553 +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 <stdio.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif - -#include "compat.h" -#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 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 = ntfs_malloc(cb_size); - if (!cb) - return -1; - - /* Need a temporary buffer for each uncompressed block. */ - dest = ntfs_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/usr/src/lib/libntfs/common/libntfs/crypto.c b/usr/src/lib/libntfs/common/libntfs/crypto.c deleted file mode 100644 index 850e0705c6..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/crypto.c +++ /dev/null @@ -1,1519 +0,0 @@ -/** - * crypto.c - Routines for dealing with encrypted files. Part of the - * Linux-NTFS project. - * - * Copyright (c) 2005 Yuval Fledel - * Copyright (c) 2005-2007 Anton Altaparmakov - * Copyright (c) 2007 Yura Pakhuchiy - * - * This program 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 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 - * - * TODO: Cleanup this file. Write nice descriptions for non-exported functions - * and maybe clean up namespace (not necessary for all functions to belong to - * ntfs_crypto, we can have ntfs_fek, ntfs_rsa, etc.., but there should be - * maximum 2-3 namespaces, not every function begins with it own namespace - * like now). - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_SYS_TYPES_H -#include <sys/types.h> -#endif -#ifdef HAVE_SYS_STAT_H -#include <sys/stat.h> -#endif -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif -#ifdef HAVE_STDIO_H -#include <stdio.h> -#endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif - -#include "compat.h" -#include "attrib.h" -#include "types.h" -#include "volume.h" -#include "debug.h" -#include "dir.h" -#include "layout.h" -#include "crypto.h" - -#ifdef ENABLE_CRYPTO - -#include <gcrypt.h> -#include <gnutls/pkcs12.h> -#include <gnutls/x509.h> - -#include <libconfig.h> - -#define NTFS_CONFIG_PATH_SYSTEM "/etc/libntfs/config" -#define NTFS_CONFIG_PATH_USER ".libntfs/config" - -#define NTFS_SHA1_THUMBPRINT_SIZE 0x14 - -#define NTFS_CRED_TYPE_CERT_THUMBPRINT const_cpu_to_le32(3) - -#define NTFS_EFS_CERT_PURPOSE_OID_DDF "1.3.6.1.4.1.311.10.3.4" -#define NTFS_EFS_CERT_PURPOSE_OID_DRF "1.3.6.1.4.1.311.10.3.4.1" - -#define NTFS_EFS_SECTOR_SIZE 512 - -typedef enum { - DF_TYPE_UNKNOWN, - DF_TYPE_DDF, - DF_TYPE_DRF, -} NTFS_DF_TYPES; - -/** - * enum NTFS_CRYPTO_ALGORITHMS - List of crypto algorithms used by EFS (32 bit) - * - * To choose which one is used in Windows, create or set the REG_DWORD registry - * key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\EFS\ - * AlgorithmID to the value of your chosen crypto algorithm, e.g. to use DesX, - * set AlgorithmID to 0x6604. - * - * Note that the Windows versions I have tried so far (all are high crypto - * enabled) ignore the AlgorithmID value if it is not one of CALG_3DES, - * CALG_DESX, or CALG_AES_256, i.e. you cannot select CALG_DES at all using - * this registry key. It would be interesting to check out encryption on one - * of the "crippled" crypto Windows versions... - */ -typedef enum { - CALG_DES = const_cpu_to_le32(0x6601), - /* If not one of the below three, fall back to standard Des. */ - CALG_3DES = const_cpu_to_le32(0x6603), - CALG_DESX = const_cpu_to_le32(0x6604), - CALG_AES_256 = const_cpu_to_le32(0x6610), -} NTFS_CRYPTO_ALGORITHMS; - -/** - * struct ntfs_fek - Decrypted, in-memory file encryption key. - */ -struct _ntfs_fek { - gcry_cipher_hd_t gcry_cipher_hd; - le32 alg_id; - u8 *key_data; - gcry_cipher_hd_t *des_gcry_cipher_hd_ptr; -}; - -typedef struct _ntfs_fek ntfs_fek; - -struct _ntfs_crypto_attr { - ntfs_fek *fek; -}; - -typedef struct { - u64 in_whitening, out_whitening; - gcry_cipher_hd_t gcry_cipher_hd; -} ntfs_desx_ctx; - -ntfschar NTFS_EFS[5] = { - const_cpu_to_le16('$'), const_cpu_to_le16('E'), const_cpu_to_le16('F'), - const_cpu_to_le16('S'), const_cpu_to_le16(0) -}; - -typedef struct { - gcry_sexp_t key; - NTFS_DF_TYPES df_type; - char thumbprint[NTFS_SHA1_THUMBPRINT_SIZE]; -} ntfs_rsa_private_key_t; - -/* - * Yes, global variables sucks, but we need to keep whether we performed - * gcrypt/gnutls global initialization and keep user's RSA keys. - */ -typedef struct { - int initialized; - int desx_alg_id; - gcry_module_t desx_module; - ntfs_rsa_private_key_t **rsa_key; - int nr_rsa_keys; -} ntfs_crypto_ctx_t; - -static ntfs_crypto_ctx_t ntfs_crypto_ctx = { - .desx_alg_id = -1, - .desx_module = NULL, -}; - -/** - * ntfs_pkcs12_load_pfxfile - */ -static int ntfs_pkcs12_load_pfxfile(const char *keyfile, u8 **pfx, - unsigned *pfx_size) -{ - int f, to_read, total, attempts, br; - struct stat key_stat; - - if (!keyfile || !pfx || !pfx_size) { - ntfs_log_error("You have to specify the key file, a pointer " - "to hold the key file contents, and a pointer " - "to hold the size of the key file contents.\n"); - return -1; - } - f = open(keyfile, O_RDONLY); - if (f == -1) { - ntfs_log_perror("Failed to open key file"); - return -1; - } - if (fstat(f, &key_stat) == -1) { - ntfs_log_perror("Failed to stat key file"); - goto file_out; - } - if (!S_ISREG(key_stat.st_mode)) { - ntfs_log_error("Key file is not a regular file, cannot read " - "it.\n"); - goto file_out; - } - if (!key_stat.st_size) { - ntfs_log_error("Key file has zero size.\n"); - goto file_out; - } - *pfx = malloc(key_stat.st_size + 1); - if (!*pfx) { - ntfs_log_perror("Failed to allocate buffer for key file " - "contents"); - goto file_out; - } - to_read = key_stat.st_size; - total = attempts = 0; - do { - br = read(f, *pfx + total, to_read); - if (br == -1) { - ntfs_log_perror("Failed to read from key file"); - goto free_out; - } - if (!br) - attempts++; - to_read -= br; - total += br; - } while (to_read > 0 && attempts < 3); - close(f); - /* Make sure it is zero terminated. */ - (*pfx)[key_stat.st_size] = 0; - *pfx_size = key_stat.st_size; - return 0; -free_out: - free(*pfx); -file_out: - close(f); - return -1; -} - -/** - * ntfs_rsa_private_key_import_from_gnutls - */ -static gcry_sexp_t ntfs_rsa_private_key_import_from_gnutls( - gnutls_x509_privkey_t priv_key) -{ - int i, j; - size_t tmp_size; - gnutls_datum_t rd[6]; - gcry_mpi_t rm[6]; - gcry_sexp_t rsa_key; - - /* Extract the RSA parameters from the GNU TLS private key. */ - if (gnutls_x509_privkey_export_rsa_raw(priv_key, &rd[0], &rd[1], - &rd[2], &rd[3], &rd[4], &rd[5])) { - ntfs_log_error("Failed to export rsa parameters. (Is the " - "key an RSA private key?)\n"); - return NULL; - } - /* Convert each RSA parameter to MPI format. */ - for (i = 0; i < 6; i++) { - if (gcry_mpi_scan(&rm[i], GCRYMPI_FMT_USG, rd[i].data, - rd[i].size, &tmp_size) != GPG_ERR_NO_ERROR) { - ntfs_log_error("Failed to convert RSA parameter %i " - "to mpi format (size %d)\n", i, - rd[i].size); - rsa_key = NULL; - break; - } - } - /* Release the no longer needed datum values. */ - for (j = 0; j < 6; j++) { - if (rd[j].data && rd[j].size) - gnutls_free(rd[j].data); - } - /* - * Build the gcrypt private key, note libgcrypt uses p and q inversed - * to what gnutls uses. - */ - if (i == 6 && gcry_sexp_build(&rsa_key, NULL, - "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", - rm[0], rm[1], rm[2], rm[4], rm[3], rm[5]) != - GPG_ERR_NO_ERROR) { - ntfs_log_error("Failed to build RSA private key s-exp.\n"); - rsa_key = NULL; - } - /* Release the no longer needed MPI values. */ - for (j = 0; j < i; j++) - gcry_mpi_release(rm[j]); - return rsa_key; -} - -/** - * ntfs_rsa_private_key_release - */ -static void ntfs_rsa_private_key_release(ntfs_rsa_private_key_t *rsa_key) -{ - if (rsa_key) { - if (rsa_key->key) - gcry_sexp_release(rsa_key->key); - free(rsa_key); - } -} - -/** - * ntfs_pkcs12_extract_rsa_key - */ -static ntfs_rsa_private_key_t *ntfs_pkcs12_extract_rsa_key(u8 *pfx, - int pfx_size, const char *password) -{ - int err, bag_index, flags; - gnutls_datum_t dpfx, dkey; - gnutls_pkcs12_t pkcs12 = NULL; - gnutls_pkcs12_bag_t bag = NULL; - gnutls_x509_privkey_t pkey = NULL; - gnutls_x509_crt_t crt = NULL; - ntfs_rsa_private_key_t *rsa_key = NULL; - char purpose_oid[100]; - size_t purpose_oid_size = sizeof(purpose_oid); - size_t tp_size; - BOOL have_thumbprint = FALSE; - - rsa_key = malloc(sizeof(ntfs_rsa_private_key_t)); - if (!rsa_key) { - ntfs_log_perror("%s", "ntfs_pkcs12_extract_rsa_key"); - return NULL; - } - rsa_key->df_type = DF_TYPE_UNKNOWN; - rsa_key->key = NULL; - tp_size = sizeof(rsa_key->thumbprint); - /* Create a pkcs12 structure. */ - err = gnutls_pkcs12_init(&pkcs12); - if (err) { - ntfs_log_error("Failed to initialize PKCS#12 structure: %s\n", - gnutls_strerror(err)); - goto err; - } - /* Convert the PFX file (DER format) to native pkcs12 format. */ - dpfx.data = pfx; - dpfx.size = pfx_size; - err = gnutls_pkcs12_import(pkcs12, &dpfx, GNUTLS_X509_FMT_DER, 0); - if (err) { - ntfs_log_error("Failed to convert the PFX file from DER to " - "native PKCS#12 format: %s\n", - gnutls_strerror(err)); - goto err; - } - /* - * Verify that the password is correct and that the key file has not - * been tampered with. Note if the password has zero length and the - * verification fails, retry with password set to NULL. This is needed - * to get password less .pfx files generated with Windows XP SP1 (and - * probably earlier versions of Windows) to work. - */ -retry_verify: - err = gnutls_pkcs12_verify_mac(pkcs12, password); - if (err) { - if (err == GNUTLS_E_MAC_VERIFY_FAILED && - password && !strlen(password)) { - password = NULL; - goto retry_verify; - } - ntfs_log_error("You are probably misspelled password to PFX " - "file.\n"); - goto err; - } - for (bag_index = 0; ; bag_index++) { - err = gnutls_pkcs12_bag_init(&bag); - if (err) { - ntfs_log_error("Failed to initialize PKCS#12 Bag " - "structure: %s\n", - gnutls_strerror(err)); - goto err; - } - err = gnutls_pkcs12_get_bag(pkcs12, bag_index, bag); - if (err) { - if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { - err = 0; - break; - } - ntfs_log_error("Failed to obtain Bag from PKCS#12 " - "structure: %s\n", - gnutls_strerror(err)); - goto err; - } -check_again: - err = gnutls_pkcs12_bag_get_count(bag); - if (err < 0) { - ntfs_log_error("Failed to obtain Bag count: %s\n", - gnutls_strerror(err)); - goto err; - } - err = gnutls_pkcs12_bag_get_type(bag, 0); - if (err < 0) { - ntfs_log_error("Failed to determine Bag type: %s\n", - gnutls_strerror(err)); - goto err; - } - flags = 0; - switch (err) { - case GNUTLS_BAG_PKCS8_KEY: - flags = GNUTLS_PKCS_PLAIN; - case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: - err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey); - if (err < 0) { - ntfs_log_error("Failed to obtain Bag data: " - "%s\n", gnutls_strerror(err)); - goto err; - } - err = gnutls_x509_privkey_init(&pkey); - if (err) { - ntfs_log_error("Failed to initialized " - "private key structure: %s\n", - gnutls_strerror(err)); - goto err; - } - /* Decrypt the private key into GNU TLS format. */ - err = gnutls_x509_privkey_import_pkcs8(pkey, &dkey, - GNUTLS_X509_FMT_DER, password, flags); - if (err) { - ntfs_log_error("Failed to convert private " - "key from DER to GNU TLS " - "format: %s\n", - gnutls_strerror(err)); - goto err; - } -#if 0 - /* - * Export the key again, but unencrypted, and output it - * to stderr. Note the output has an RSA header so to - * compare to openssl pkcs12 -nodes -in myfile.pfx - * output need to ignore the part of the key between - * the first "MII..." up to the second "MII...". The - * actual RSA private key begins at the second "MII..." - * and in my testing at least was identical to openssl - * output and was also identical both on big and little - * endian so gnutls should be endianness safe. - */ - char *buf = malloc(8192); - size_t bufsize = 8192; - err = gnutls_x509_privkey_export_pkcs8(pkey, - GNUTLS_X509_FMT_PEM, "", GNUTLS_PKCS_PLAIN, buf, - &bufsize); - if (err) { - ntfs_log_error("eek1\n"); - exit(1); - } - ntfs_log_error("%s\n", buf); - free(buf); -#endif - /* Convert the private key to our internal format. */ - rsa_key->key = - ntfs_rsa_private_key_import_from_gnutls(pkey); - if (!rsa_key->key) - goto err; - break; - case GNUTLS_BAG_ENCRYPTED: - err = gnutls_pkcs12_bag_decrypt(bag, password); - if (err) { - ntfs_log_error("Failed to decrypt Bag: %s\n", - gnutls_strerror(err)); - goto err; - } - goto check_again; - case GNUTLS_BAG_CERTIFICATE: - err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey); - if (err < 0) { - ntfs_log_error("Failed to obtain Bag data: " - "%s\n", gnutls_strerror(err)); - goto err; - } - err = gnutls_x509_crt_init(&crt); - if (err) { - ntfs_log_error("Failed to initialize " - "certificate structure: %s\n", - gnutls_strerror(err)); - goto err; - } - err = gnutls_x509_crt_import(crt, &dkey, - GNUTLS_X509_FMT_DER); - if (err) { - ntfs_log_error("Failed to convert certificate " - "from DER to GNU TLS format: " - "%s\n", gnutls_strerror(err)); - goto err; - } - err = gnutls_x509_crt_get_key_purpose_oid(crt, 0, - purpose_oid, &purpose_oid_size, NULL); - if (err) { - ntfs_log_error("Failed to get key purpose " - "OID: %s\n", - gnutls_strerror(err)); - goto err; - } - purpose_oid[purpose_oid_size - 1] = 0; - if (!strcmp(purpose_oid, - NTFS_EFS_CERT_PURPOSE_OID_DRF)) - rsa_key->df_type = DF_TYPE_DRF; - else if (!strcmp(purpose_oid, - NTFS_EFS_CERT_PURPOSE_OID_DDF)) - rsa_key->df_type = DF_TYPE_DDF; - else { - ntfs_log_error("Certificate has unknown " - "purpose OID %s.\n", - purpose_oid); - err = EINVAL; - goto err; - } - /* Return the thumbprint to the caller. */ - err = gnutls_x509_crt_get_fingerprint(crt, - GNUTLS_DIG_SHA1, rsa_key->thumbprint, - &tp_size); - if (err) { - ntfs_log_error("Failed to get thumbprint: " - "%s\n", gnutls_strerror(err)); - goto err; - } - if (tp_size != NTFS_SHA1_THUMBPRINT_SIZE) { - ntfs_log_error("Invalid thumbprint size %zd. " - "Should be %d.\n", tp_size, - sizeof(rsa_key->thumbprint)); - err = EINVAL; - goto err; - } - have_thumbprint = TRUE; - gnutls_x509_crt_deinit(crt); - crt = NULL; - break; - default: - /* We do not care about other types. */ - break; - } - gnutls_pkcs12_bag_deinit(bag); - } -err: - if (err || !rsa_key->key || rsa_key->df_type == DF_TYPE_UNKNOWN || - !have_thumbprint) { - if (!err) - ntfs_log_error("Key type or thumbprint not found, " - "aborting.\n"); - ntfs_rsa_private_key_release(rsa_key); - rsa_key = NULL; - } - if (crt) - gnutls_x509_crt_deinit(crt); - if (pkey) - gnutls_x509_privkey_deinit(pkey); - if (bag) - gnutls_pkcs12_bag_deinit(bag); - if (pkcs12) - gnutls_pkcs12_deinit(pkcs12); - return rsa_key; -} - -/** - * ntfs_buffer_reverse - - * - * This is a utility function for reversing the order of a buffer in place. - * Users of this function should be very careful not to sweep byte order - * problems under the rug. - */ -static inline void ntfs_buffer_reverse(u8 *buf, unsigned buf_size) -{ - unsigned i; - u8 t; - - for (i = 0; i < buf_size / 2; i++) { - t = buf[i]; - buf[i] = buf[buf_size - i - 1]; - buf[buf_size - i - 1] = t; - } -} - -#ifndef HAVE_STRNLEN -/** - * strnlen - strnlen is a gnu extension so emulate it if not present - */ -static size_t strnlen(const char *s, size_t maxlen) -{ - const char *p, *end; - - /* Look for a '\0' character. */ - for (p = s, end = s + maxlen; p < end && *p; p++) - ; - return p - s; -} -#endif /* ! HAVE_STRNLEN */ - -/** - * ntfs_raw_fek_decrypt - - * - * Note: decrypting into the input buffer. - */ -static unsigned ntfs_raw_fek_decrypt(u8 *fek, u32 fek_size, - ntfs_rsa_private_key_t *rsa_key) -{ - gcry_mpi_t fek_mpi; - gcry_sexp_t fek_sexp, fek_sexp2; - gcry_error_t err; - size_t size, padding; - - /* Reverse the raw FEK. */ - ntfs_buffer_reverse(fek, fek_size); - /* Convert the FEK to internal MPI format. */ - err = gcry_mpi_scan(&fek_mpi, GCRYMPI_FMT_USG, fek, fek_size, NULL); - if (err != GPG_ERR_NO_ERROR) { - ntfs_log_error("Failed to convert file encryption key to " - "internal MPI format: %s\n", - gcry_strerror(err)); - return 0; - } - /* Create an internal S-expression from the FEK. */ - err = gcry_sexp_build(&fek_sexp, NULL, - "(enc-val (flags) (rsa (a %m)))", fek_mpi); - gcry_mpi_release(fek_mpi); - if (err != GPG_ERR_NO_ERROR) { - ntfs_log_error("Failed to create internal S-expression of " - "the file encryption key: %s\n", - gcry_strerror(err)); - return 0; - } - /* Decrypt the FEK. */ - err = gcry_pk_decrypt(&fek_sexp2, fek_sexp, rsa_key->key); - gcry_sexp_release(fek_sexp); - if (err != GPG_ERR_NO_ERROR) { - ntfs_log_error("Failed to decrypt the file encryption key: " - "%s\n", gcry_strerror(err)); - return 0; - } - /* Extract the actual FEK from the decrypted raw S-expression. */ - fek_sexp = gcry_sexp_find_token(fek_sexp2, "value", 0); - gcry_sexp_release(fek_sexp2); - if (!fek_sexp) { - ntfs_log_error("Failed to find the decrypted file encryption " - "key in the internal S-expression.\n"); - return 0; - } - /* Convert the decrypted FEK S-expression into MPI format. */ - fek_mpi = gcry_sexp_nth_mpi(fek_sexp, 1, GCRYMPI_FMT_USG); - gcry_sexp_release(fek_sexp); - if (!fek_mpi) { - ntfs_log_error("Failed to convert the decrypted file " - "encryption key S-expression to internal MPI " - "format.\n"); - return 0; - } - /* Convert the decrypted FEK from MPI format to binary data. */ - err = gcry_mpi_print(GCRYMPI_FMT_USG, fek, fek_size, &size, fek_mpi); - gcry_mpi_release(fek_mpi); - if (err != GPG_ERR_NO_ERROR || !size) { - ntfs_log_error("Failed to convert decrypted file encryption " - "key from internal MPI format to binary data: " - "%s\n", gcry_strerror(err)); - return 0; - } - /* - * Finally, remove the PKCS#1 padding and return the size of the - * decrypted FEK. - */ - padding = strnlen((char *)fek, size) + 1; - if (padding > size) { - ntfs_log_error("Failed to remove PKCS#1 padding from " - "decrypted file encryption key.\n"); - return 0; - } - size -= padding; - memmove(fek, fek + padding, size); - return size; -} - -/** - * ntfs_desx_key_expand - expand a 128-bit desx key to the needed 192-bit key - * @src: source buffer containing 128-bit key - * - * Expands the on-disk 128-bit desx key to the needed des key, the in-, and the - * out-whitening keys required to perform desx {de,en}cryption. - */ -static gcry_error_t ntfs_desx_key_expand(const u8 *src, u32 *des_key, - u64 *out_whitening, u64 *in_whitening) -{ - static const u8 *salt1 = (const u8*)"Dan Simon "; - static const u8 *salt2 = (const u8*)"Scott Field"; - static const int salt_len = 12; - gcry_md_hd_t hd1, hd2; - u32 *md; - gcry_error_t err; - - err = gcry_md_open(&hd1, GCRY_MD_MD5, 0); - if (err != GPG_ERR_NO_ERROR) { - ntfs_log_error("Failed to open MD5 digest.\n"); - return err; - } - /* Hash the on-disk key. */ - gcry_md_write(hd1, src, 128 / 8); - /* Copy the current hash for efficiency. */ - err = gcry_md_copy(&hd2, hd1); - if (err != GPG_ERR_NO_ERROR) { - ntfs_log_error("Failed to copy MD5 digest object.\n"); - goto out; - } - /* Hash with the first salt and store the result. */ - gcry_md_write(hd1, salt1, salt_len); - md = (u32*)gcry_md_read(hd1, 0); - des_key[0] = md[0] ^ md[1]; - des_key[1] = md[2] ^ md[3]; - /* Hash with the second salt and store the result. */ - gcry_md_write(hd2, salt2, salt_len); - md = (u32*)gcry_md_read(hd2, 0); - *out_whitening = *(u64*)md; - *in_whitening = *(u64*)(md + 2); - gcry_md_close(hd2); -out: - gcry_md_close(hd1); - return err; -} - -/** - * ntfs_desx_setkey - libgcrypt set_key implementation for DES-X-MS128 - * @context: pointer to a variable of type ntfs_desx_ctx - * @key: the 128 bit DES-X-MS128 key, concated with the DES handle - * @keylen: must always be 16 - * - * This is the libgcrypt set_key implementation for DES-X-MS128. - */ -static gcry_err_code_t ntfs_desx_setkey(void *context, const u8 *key, - unsigned keylen) -{ - ntfs_desx_ctx *ctx = context; - gcry_error_t err; - u8 des_key[8]; - - if (keylen != 16) { - ntfs_log_error("Key length for desx must be 16.\n"); - return GPG_ERR_INV_KEYLEN; - } - err = gcry_cipher_open(&ctx->gcry_cipher_hd, GCRY_CIPHER_DES, - GCRY_CIPHER_MODE_ECB, 0); - if (err != GPG_ERR_NO_ERROR) { - ntfs_log_error("Failed to open des cipher (error 0x%x).\n", - err); - return err; - } - err = ntfs_desx_key_expand(key, (u32*)des_key, &ctx->out_whitening, - &ctx->in_whitening); - if (err != GPG_ERR_NO_ERROR) { - ntfs_log_error("Failed to expand desx key (error 0x%x).\n", - err); - gcry_cipher_close(ctx->gcry_cipher_hd); - return err; - } - err = gcry_cipher_setkey(ctx->gcry_cipher_hd, des_key, sizeof(des_key)); - if (err != GPG_ERR_NO_ERROR) { - ntfs_log_error("Failed to set des key (error 0x%x).\n", err); - gcry_cipher_close(ctx->gcry_cipher_hd); - return err; - } - /* - * Take a note of the ctx->gcry_cipher_hd since we need to close it at - * ntfs_decrypt_data_key_close() time. - */ - **(gcry_cipher_hd_t***)(key + ((keylen + 7) & ~7)) = - &ctx->gcry_cipher_hd; - return GPG_ERR_NO_ERROR; -} - -/** - * ntfs_desx_decrypt - */ -static void ntfs_desx_decrypt(void *context, u8 *outbuf, const u8 *inbuf) -{ - ntfs_desx_ctx *ctx = context; - gcry_error_t err; - - err = gcry_cipher_reset(ctx->gcry_cipher_hd); - if (err != GPG_ERR_NO_ERROR) - ntfs_log_error("Failed to reset des cipher (error 0x%x).\n", - err); - *(u64*)outbuf = *(const u64*)inbuf ^ ctx->out_whitening; - err = gcry_cipher_encrypt(ctx->gcry_cipher_hd, outbuf, 8, NULL, 0); - if (err != GPG_ERR_NO_ERROR) - ntfs_log_error("Des decryption failed (error 0x%x).\n", err); - *(u64*)outbuf ^= ctx->in_whitening; -} - -static gcry_cipher_spec_t ntfs_desx_cipher = { - .name = "DES-X-MS128", - .blocksize = 8, - .keylen = 128, - .contextsize = sizeof(ntfs_desx_ctx), - .setkey = ntfs_desx_setkey, - .decrypt = ntfs_desx_decrypt, -}; - -#ifdef NTFS_TEST -/* - * Do not remove this test code from this file! (AIA) - * It would be nice to move all tests (these and runlist) out of the library - * (at least, into the separate file{,s}), so they would not annoy eyes. (Yura) - */ - -/** - * ntfs_desx_key_expand_test - */ -static BOOL ntfs_desx_key_expand_test(void) -{ - const u8 known_desx_on_disk_key[16] = { - 0xa1, 0xf9, 0xe0, 0xb2, 0x53, 0x23, 0x9e, 0x8f, - 0x0f, 0x91, 0x45, 0xd9, 0x8e, 0x20, 0xec, 0x30 - }; - const u8 known_des_key[8] = { - 0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f, - }; - const u8 known_out_whitening[8] = { - 0xed, 0xda, 0x4c, 0x47, 0x60, 0x49, 0xdb, 0x8d, - }; - const u8 known_in_whitening[8] = { - 0x75, 0xf6, 0xa0, 0x1a, 0xc0, 0xca, 0x28, 0x1e - }; - u64 test_out_whitening, test_in_whitening; - union { - u64 u64; - u32 u32[2]; - } test_des_key; - gcry_error_t err; - BOOL res; - - err = ntfs_desx_key_expand(known_desx_on_disk_key, test_des_key.u32, - &test_out_whitening, &test_in_whitening); - if (err != GPG_ERR_NO_ERROR) - res = FALSE; - else - res = test_des_key.u64 == *(u64*)known_des_key && - test_out_whitening == - *(u64*)known_out_whitening && - test_in_whitening == - *(u64*)known_in_whitening; - ntfs_log_error("Testing whether ntfs_desx_key_expand() works: %s\n", - res ? "SUCCESS" : "FAILED"); - return res; -} - -/** - * ntfs_des_test - */ -static BOOL ntfs_des_test(void) -{ - const u8 known_des_key[8] = { - 0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f - }; - const u8 known_des_encrypted_data[8] = { - 0xdc, 0xf7, 0x68, 0x2a, 0xaf, 0x48, 0x53, 0x0f - }; - const u8 known_decrypted_data[8] = { - 0xd8, 0xd9, 0x15, 0x23, 0x5b, 0x88, 0x0e, 0x09 - }; - u8 test_decrypted_data[8]; - int res; - gcry_error_t err; - gcry_cipher_hd_t gcry_cipher_hd; - - err = gcry_cipher_open(&gcry_cipher_hd, GCRY_CIPHER_DES, - GCRY_CIPHER_MODE_ECB, 0); - if (err != GPG_ERR_NO_ERROR) { - ntfs_log_error("Failed to open des cipher (error 0x%x).\n", - err); - return FALSE; - } - err = gcry_cipher_setkey(gcry_cipher_hd, known_des_key, - sizeof(known_des_key)); - if (err != GPG_ERR_NO_ERROR) { - ntfs_log_error("Failed to set des key (error 0x%x.\n", err); - gcry_cipher_close(gcry_cipher_hd); - return FALSE; - } - /* - * Apply DES decryption (ntfs actually uses encryption when decrypting). - */ - err = gcry_cipher_encrypt(gcry_cipher_hd, test_decrypted_data, - sizeof(test_decrypted_data), known_des_encrypted_data, - sizeof(known_des_encrypted_data)); - gcry_cipher_close(gcry_cipher_hd); - if (err) { - ntfs_log_error("Failed to des decrypt test data (error " - "0x%x).\n", err); - return FALSE; - } - res = !memcmp(test_decrypted_data, known_decrypted_data, - sizeof(known_decrypted_data)); - ntfs_log_error("Testing whether des decryption works: %s\n", - res ? "SUCCESS" : "FAILED"); - return res; -} - -#else /* !defined(NTFS_TEST) */ - -/** - * ntfs_desx_key_expand_test - */ -static inline BOOL ntfs_desx_key_expand_test(void) -{ - return TRUE; -} - -/** - * ntfs_des_test - */ -static inline BOOL ntfs_des_test(void) -{ - return TRUE; -} - -#endif /* !defined(NTFS_TEST) */ - -/** - * ntfs_fek_import_from_raw - */ -static ntfs_fek *ntfs_fek_import_from_raw(u8 *fek_buf, - unsigned fek_size) -{ - ntfs_fek *fek; - u32 key_size, wanted_key_size, gcry_algo; - gcry_error_t err; - - key_size = le32_to_cpup(fek_buf); - ntfs_log_debug("key_size 0x%x\n", key_size); - if (key_size + 16 > fek_size) { - ntfs_log_debug("Invalid FEK. It was probably decrypted with " - "the incorrect RSA key."); - errno = EINVAL; - return NULL; - } - fek = malloc(((((sizeof(*fek) + 7) & ~7) + key_size + 7) & ~7) + - sizeof(gcry_cipher_hd_t)); - if (!fek) { - errno = ENOMEM; - return NULL; - } - fek->alg_id = *(le32*)(fek_buf + 8); - ntfs_log_debug("algorithm_id 0x%x\n", le32_to_cpu(fek->alg_id)); - fek->key_data = (u8*)fek + ((sizeof(*fek) + 7) & ~7); - memcpy(fek->key_data, fek_buf + 16, key_size); - fek->des_gcry_cipher_hd_ptr = NULL; - *(gcry_cipher_hd_t***)(fek->key_data + ((key_size + 7) & ~7)) = - &fek->des_gcry_cipher_hd_ptr; - switch (fek->alg_id) { - case CALG_DESX: - if (!ntfs_crypto_ctx.desx_module) { - if (!ntfs_desx_key_expand_test() || !ntfs_des_test()) { - err = EINVAL; - goto out; - } - err = gcry_cipher_register(&ntfs_desx_cipher, - &ntfs_crypto_ctx.desx_alg_id, - &ntfs_crypto_ctx.desx_module); - if (err != GPG_ERR_NO_ERROR) { - ntfs_log_error("Failed to register desx " - "cipher: %s\n", - gcry_strerror(err)); - err = EINVAL; - goto out; - } - } - wanted_key_size = 16; - gcry_algo = ntfs_crypto_ctx.desx_alg_id; - break; - case CALG_3DES: - wanted_key_size = 24; - gcry_algo = GCRY_CIPHER_3DES; - break; - case CALG_AES_256: - wanted_key_size = 32; - gcry_algo = GCRY_CIPHER_AES256; - break; - default: - wanted_key_size = 8; - gcry_algo = GCRY_CIPHER_DES; - if (fek->alg_id == CALG_DES) - ntfs_log_error("DES is not supported at present\n"); - else - ntfs_log_error("Unknown crypto algorithm 0x%x\n", - le32_to_cpu(fek->alg_id)); - ntfs_log_error(". Please email %s and say that you saw this " - "message. We will then try to implement " - "support for this algorithm.\n", NTFS_DEV_LIST); - err = EOPNOTSUPP; - goto out; - } - if (key_size != wanted_key_size) { - ntfs_log_error("%s key of %u bytes but needed size is %u " - "bytes, assuming corrupt or incorrect key. " - "Aborting.\n", - gcry_cipher_algo_name(gcry_algo), - (unsigned)key_size, (unsigned)wanted_key_size); - err = EIO; - goto out; - } - err = gcry_cipher_open(&fek->gcry_cipher_hd, gcry_algo, - GCRY_CIPHER_MODE_CBC, 0); - if (err != GPG_ERR_NO_ERROR) { - ntfs_log_error("gcry_cipher_open() failed: %s\n", - gcry_strerror(err)); - err = EINVAL; - goto out; - } - err = gcry_cipher_setkey(fek->gcry_cipher_hd, fek->key_data, key_size); - if (err != GPG_ERR_NO_ERROR) { - ntfs_log_error("gcry_cipher_setkey() failed: %s\n", - gcry_strerror(err)); - gcry_cipher_close(fek->gcry_cipher_hd); - err = EINVAL; - goto out; - } - return fek; -out: - free(fek); - errno = err; - return NULL; -} - -/** - * ntfs_fek_release - */ -static void ntfs_fek_release(ntfs_fek *fek) -{ - if (fek->des_gcry_cipher_hd_ptr) - gcry_cipher_close(*fek->des_gcry_cipher_hd_ptr); - gcry_cipher_close(fek->gcry_cipher_hd); - free(fek); -} - -/** - * ntfs_df_array_fek_get - */ -static ntfs_fek *ntfs_df_array_fek_get(EFS_DF_ARRAY_HEADER *df_array, - ntfs_rsa_private_key_t *rsa_key) -{ - EFS_DF_HEADER *df_header; - EFS_DF_CREDENTIAL_HEADER *df_cred; - EFS_DF_CERT_THUMBPRINT_HEADER *df_cert; - u8 *fek_buf; - ntfs_fek *fek; - u32 df_count, fek_size; - unsigned i, thumbprint_size = sizeof(rsa_key->thumbprint); - - df_count = le32_to_cpu(df_array->df_count); - if (!df_count) - ntfs_log_error("There are no elements in the DF array.\n"); - df_header = (EFS_DF_HEADER*)(df_array + 1); - for (i = 0; i < df_count; i++, df_header = (EFS_DF_HEADER*)( - (u8*)df_header + le32_to_cpu(df_header->df_length))) { - df_cred = (EFS_DF_CREDENTIAL_HEADER*)((u8*)df_header + - le32_to_cpu(df_header->cred_header_offset)); - if (df_cred->type != NTFS_CRED_TYPE_CERT_THUMBPRINT) { - ntfs_log_debug("Credential type is not certificate " - "thumbprint, skipping DF entry.\n"); - continue; - } - df_cert = (EFS_DF_CERT_THUMBPRINT_HEADER*)((u8*)df_cred + - le32_to_cpu( - df_cred->cert_thumbprint_header_offset)); - if (le32_to_cpu(df_cert->thumbprint_size) != thumbprint_size) { - ntfs_log_error("Thumbprint size %d is not valid " - "(should be %d), skipping this DF " - "entry.\n", - le32_to_cpu(df_cert->thumbprint_size), - thumbprint_size); - continue; - } - if (memcmp((u8*)df_cert + - le32_to_cpu(df_cert->thumbprint_offset), - rsa_key->thumbprint, thumbprint_size)) { - ntfs_log_debug("Thumbprints do not match, skipping " - "this DF entry.\n"); - continue; - } - /* - * The thumbprints match so this is probably the DF entry - * matching the RSA key. Try to decrypt the FEK with it. - */ - fek_size = le32_to_cpu(df_header->fek_size); - fek_buf = (u8*)df_header + le32_to_cpu(df_header->fek_offset); - /* Decrypt the FEK. Note: This is done in place. */ - fek_size = ntfs_raw_fek_decrypt(fek_buf, fek_size, rsa_key); - if (fek_size) { - /* Convert the FEK to our internal format. */ - fek = ntfs_fek_import_from_raw(fek_buf, fek_size); - if (fek) - return fek; - ntfs_log_error("Failed to convert the decrypted file " - "encryption key to internal format.\n"); - } else - ntfs_log_error("Failed to decrypt the file " - "encryption key.\n"); - } - return NULL; -} - -/** - * ntfs_inode_fek_get - - */ -static ntfs_fek *ntfs_inode_fek_get(ntfs_inode *inode, - ntfs_rsa_private_key_t *rsa_key) -{ - EFS_ATTR_HEADER *efs; - EFS_DF_ARRAY_HEADER *df_array = NULL; - ntfs_fek *fek = NULL; - - /* Obtain the $EFS contents. */ - efs = ntfs_attr_readall(inode, AT_LOGGED_UTILITY_STREAM, NTFS_EFS, 4, - NULL); - if (!efs) { - ntfs_log_perror("Failed to read $EFS attribute"); - return NULL; - } - /* - * Depending on whether the key is a normal key or a data recovery key, - * iterate through the DDF or DRF array, respectively. - */ - if (rsa_key->df_type == DF_TYPE_DDF) { - if (efs->offset_to_ddf_array) - df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs + - le32_to_cpu(efs->offset_to_ddf_array)); - else - ntfs_log_error("There are no entries in the DDF " - "array.\n"); - } else if (rsa_key->df_type == DF_TYPE_DRF) { - if (efs->offset_to_drf_array) - df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs + - le32_to_cpu(efs->offset_to_drf_array)); - else - ntfs_log_error("There are no entries in the DRF " - "array.\n"); - } else - ntfs_log_error("Invalid DF type.\n"); - if (df_array) - fek = ntfs_df_array_fek_get(df_array, rsa_key); - free(efs); - return fek; -} - -/** - * ntfs_fek_decrypt_sector - */ -static int ntfs_fek_decrypt_sector(ntfs_fek *fek, u8 *data, const u64 offset) -{ - gcry_error_t err; - - err = gcry_cipher_reset(fek->gcry_cipher_hd); - if (err != GPG_ERR_NO_ERROR) { - ntfs_log_error("Failed to reset cipher: %s\n", - gcry_strerror(err)); - return -1; - } - /* - * Note: You may wonder why we are not calling gcry_cipher_setiv() here - * instead of doing it by hand after the decryption. The answer is - * that gcry_cipher_setiv() wants an iv of length 8 bytes but we give - * it a length of 16 for AES256 so it does not like it. - */ - err = gcry_cipher_decrypt(fek->gcry_cipher_hd, data, 512, NULL, 0); - if (err != GPG_ERR_NO_ERROR) { - ntfs_log_error("Decryption failed: %s\n", gcry_strerror(err)); - return -1; - } - /* Apply the IV. */ - if (fek->alg_id == CALG_AES_256) { - ((le64*)data)[0] ^= cpu_to_le64(0x5816657be9161312ULL + offset); - ((le64*)data)[1] ^= cpu_to_le64(0x1989adbe44918961ULL + offset); - } else { - /* All other algorithms (Des, 3Des, DesX) use the same IV. */ - ((le64*)data)[0] ^= cpu_to_le64(0x169119629891ad13ULL + offset); - } - return 512; -} - -/** - * ntfs_crypto_deinit - perform library-wide crypto deinitialization - */ -static void ntfs_crypto_deinit(void) -{ - int i; - - if (!ntfs_crypto_ctx.initialized) - return; - - for (i = 0; i < ntfs_crypto_ctx.nr_rsa_keys; i++) - ntfs_rsa_private_key_release(ntfs_crypto_ctx.rsa_key[i]); - free(ntfs_crypto_ctx.rsa_key); - ntfs_crypto_ctx.rsa_key = NULL; - ntfs_crypto_ctx.nr_rsa_keys = 0; - gnutls_global_deinit(); - if (ntfs_crypto_ctx.desx_module) { - gcry_cipher_unregister(ntfs_crypto_ctx.desx_module); - ntfs_crypto_ctx.desx_module = NULL; - ntfs_crypto_ctx.desx_alg_id = -1; - } - ntfs_crypto_ctx.initialized = 0; -} - - -static void ntfs_crypto_parse_config(struct config_t *cfg) -{ - ntfs_crypto_ctx_t *ctx = &ntfs_crypto_ctx; - config_setting_t *cfg_keys, *cfg_key; - const char *pfx_file, *pfx_pwd; - ntfs_rsa_private_key_t *key; - u8 *pfx_buf; - unsigned pfx_size; - int i; - - /* Search for crypto.keys list. */ - cfg_keys = config_lookup(cfg, "crypto.keys"); - if (!cfg_keys) { - ntfs_log_error("Unable to find crypto.keys in config file.\n"); - return; - } - /* Iterate trough list of records about keys. */ - for (i = 0; (cfg_key = config_setting_get_elem(cfg_keys, i)); i++) { - /* Get path and password to key. */ - pfx_file = config_setting_get_string_elem(cfg_key, 0); - pfx_pwd = config_setting_get_string_elem(cfg_key, 1); - if (!pfx_file) { - ntfs_log_error("Entry number %d in section crypto.keys " - "of configuration file formed " - "incorrectly.\n", i + 1); - continue; - } - if (!pfx_pwd) - pfx_pwd = ""; - /* Load the PKCS#12 file containing the user's private key. */ - if (ntfs_pkcs12_load_pfxfile(pfx_file, &pfx_buf, &pfx_size)) { - ntfs_log_error("Failed to load key file %s.\n", - pfx_file); - continue; - } - /* - * Check whether we need to allocate memory for new key pointer. - * If yes, allocate memory for it and for 3 more pointers. - */ - if (!(ctx->nr_rsa_keys % 4)) { - ntfs_rsa_private_key_t **new; - - new = realloc(ctx->rsa_key, - sizeof(ntfs_rsa_private_key_t *) * - (ctx->nr_rsa_keys + 4)); - if (!new) { - ntfs_log_perror("Unable to store all keys"); - break; - } - ctx->rsa_key = new; - } - /* Obtain the user's private RSA key from the key file. */ - key = ntfs_pkcs12_extract_rsa_key(pfx_buf, pfx_size, pfx_pwd); - if (key) - ctx->rsa_key[ctx->nr_rsa_keys++] = key; - else - ntfs_log_error("Failed to obtain RSA key from %s\n", - pfx_file); - /* No longer need the pfx file contents. */ - free(pfx_buf); - } -} - - -static void ntfs_crypto_read_configs(void) -{ - struct config_t cfg; - char *home; - int fd = -1; - - config_init(&cfg); - /* Load system configuration file. */ - if (config_read_file(&cfg, NTFS_CONFIG_PATH_SYSTEM)) - ntfs_crypto_parse_config(&cfg); - else - if (config_error_line(&cfg)) /* Do not cry if file absent. */ - ntfs_log_error("Failed to read system configuration " - "file: %s (line %d).\n", - config_error_text(&cfg), - config_error_line(&cfg)); - /* Load user configuration file. */ - fd = open(".", O_RDONLY); /* Save current working directory. */ - if (fd == -1) { - ntfs_log_error("Failed to open working directory.\n"); - goto out; - } - home = getenv("HOME"); - if (!home) { - ntfs_log_error("Environment variable HOME is not set.\n"); - goto out; - } - if (chdir(home) == -1) { - ntfs_log_perror("chdir() to home directory failed"); - goto out; - } - if (config_read_file(&cfg, NTFS_CONFIG_PATH_USER)) - ntfs_crypto_parse_config(&cfg); - else - if (config_error_line(&cfg)) /* Do not cry if file absent. */ - ntfs_log_error("Failed to read user configuration " - "file: %s (line %d).\n", - config_error_text(&cfg), - config_error_line(&cfg)); - if (fchdir(fd) == -1) - ntfs_log_error("Failed to restore original working " - "directory.\n"); -out: - if (fd != -1) - close(fd); - config_destroy(&cfg); -} - -/** - * ntfs_crypto_init - perform library-wide crypto initializations - * - * This function is called during first call of ntfs_crypto_attr_open and - * performs gcrypt and GNU TLS initializations, then read list of PFX files - * from configuration files and load RSA keys from them. - */ -static int ntfs_crypto_init(void) -{ - int err; - - if (ntfs_crypto_ctx.initialized) - return 0; - - /* Initialize gcrypt library. Note: Must come before GNU TLS init. */ - if (gcry_control(GCRYCTL_DISABLE_SECMEM, 0) != GPG_ERR_NO_ERROR) { - ntfs_log_error("Failed to initialize the gcrypt library.\n"); - return -1; - } - /* Initialize GNU TLS library. Note: Must come after libgcrypt init. */ - err = gnutls_global_init(); - if (err < 0) { - ntfs_log_error("Failed to initialize GNU TLS library: %s\n", - gnutls_strerror(err)); - return -1; - } - /* Read crypto related sections of libntfs configuration files. */ - ntfs_crypto_read_configs(); - - ntfs_crypto_ctx.initialized = 1; - atexit(ntfs_crypto_deinit); - return 0; -} - - -/** - * ntfs_crypto_attr_open - perform crypto related initialization for attribute - * @na: ntfs attribute to perform initialization for - * - * This function is called from ntfs_attr_open for encrypted attributes and - * tries to decrypt FEK enumerating all user submitted RSA keys. If we - * successfully obtained FEK, then @na->crypto is allocated and FEK stored - * inside. In the other case @na->crypto is set to NULL. - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -int ntfs_crypto_attr_open(ntfs_attr *na) -{ - ntfs_fek *fek; - int i; - - na->crypto = NULL; - if (!na || !NAttrEncrypted(na)) { - errno = EINVAL; - return -1; - } - if (ntfs_crypto_init()) { - errno = EACCES; - return -1; - } - - for (i = 0; i < ntfs_crypto_ctx.nr_rsa_keys; i++) { - fek = ntfs_inode_fek_get(na->ni, ntfs_crypto_ctx.rsa_key[i]); - if (fek) { - na->crypto = ntfs_malloc(sizeof(ntfs_crypto_attr)); - if (!na->crypto) - return -1; - na->crypto->fek = fek; - return 0; - } - } - - errno = EACCES; - return -1; -} - - -/** - * ntfs_crypto_attr_close - perform crypto related deinit for attribute - * @na: ntfs attribute to perform deinitialization for - * - * This function is called from ntfs_attr_close for encrypted attributes and - * frees memory that were allocated for it handling. - */ -void ntfs_crypto_attr_close(ntfs_attr *na) -{ - if (!na || !NAttrEncrypted(na)) - return; - - if (na->crypto) { - ntfs_fek_release(na->crypto->fek); - free(na->crypto); - } -} - - -/** - * ntfs_crypto_attr_pread - read from an encrypted 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 - * - * This function is called from ntfs_attr_pread for encrypted attributes and - * should behave as described in ntfs_attr_pread description. - */ -s64 ntfs_crypto_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) -{ - unsigned char *buffer; - s64 bytes_read, offset, total, length; - int i; - - if (!na || pos < 0 || count < 0 || !b || !NAttrEncrypted(na)) { - errno = EINVAL; - return -1; - } - if (!count) - return 0; - - if (!na->crypto) { - errno = EACCES; - return -1; - } - - buffer = malloc(NTFS_EFS_SECTOR_SIZE); - if (!buffer) - return -1; - - ntfs_attr_map_runlist_range(na, pos >> na->ni->vol->cluster_size_bits, - (pos + count - 1) >> na->ni->vol->cluster_size_bits); - - total = 0; - offset = ROUND_DOWN(pos, 9); - while (total < count && offset < na->data_size) { - /* Calculate number of bytes we actually want. */ - length = NTFS_EFS_SECTOR_SIZE; - if (offset + length > pos + count) - length = pos + count - offset; - if (offset + length > na->data_size) - length = na->data_size - offset; - - if (length < 0) { - total = -1; - errno = EIO; - ntfs_log_error("LIBRARY BUG!!! Please report that you " - "saw this message to %s. Thanks!", - NTFS_DEV_LIST); - break; - } - - /* Just write zeros if @offset fully beyond initialized size. */ - if (offset >= na->initialized_size) { - memset(b + total, 0, length); - total += length; - continue; - } - - bytes_read = ntfs_rl_pread(na->ni->vol, na->rl, offset, - NTFS_EFS_SECTOR_SIZE, buffer); - if (!bytes_read) - break; - if (bytes_read != NTFS_EFS_SECTOR_SIZE) { - ntfs_log_perror("%s(): ntfs_rl_pread returned %lld " - "bytes", "ntfs_crypto_attr_pread", bytes_read); - break; - } - if ((i = ntfs_fek_decrypt_sector(na->crypto->fek, buffer, - offset)) < bytes_read) { - ntfs_log_error("%s(): Couldn't decrypt all data " - "(%u/%lld/%lld/%lld)!", "ntfs_crypto_attr_pread", - i, (long long)bytes_read, - (long long)offset, (long long)total); - break; - } - - /* Handle partially in initialized size situation. */ - if (offset + length > na->initialized_size) - memset(buffer + (na->initialized_size - offset), 0, - offset + length - na->initialized_size); - - if (offset >= pos) - memcpy(b + total, buffer, length); - else { - length -= (pos - offset); - memcpy(b + total, buffer + (pos - offset), length); - } - total += length; - offset += bytes_read; - } - - free(buffer); - return total; -} - -#else /* !ENABLE_CRYPTO */ - -/* Stubs for crypto-disabled version of libntfs. */ - -int ntfs_crypto_attr_open(ntfs_attr *na) -{ - na->crypto = NULL; - errno = EACCES; - return -1; -} - -void ntfs_crypto_attr_close(ntfs_attr *na) -{ -} - -s64 ntfs_crypto_attr_pread(ntfs_attr *na, const s64 pos, s64 count, - void *b) -{ - errno = EACCES; - return -1; -} - -#endif /* !ENABLE_CRYPTO */ - diff --git a/usr/src/lib/libntfs/common/libntfs/debug.c b/usr/src/lib/libntfs/common/libntfs/debug.c deleted file mode 100644 index 8962051006..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/debug.c +++ /dev/null @@ -1,73 +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 <errno.h> -#endif - -#include "compat.h" -#include "types.h" -#include "runlist.h" -#include "debug.h" -#include "logging.h" - -#ifdef DEBUG -/** - * 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/usr/src/lib/libntfs/common/libntfs/device.c b/usr/src/lib/libntfs/common/libntfs/device.c deleted file mode 100644 index 89c2b1b2dc..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/device.c +++ /dev/null @@ -1,796 +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 <unistd.h> -#endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif -#ifdef HAVE_STDIO_H -#include <stdio.h> -#endif -#ifdef HAVE_SYS_TYPES_H -#include <sys/types.h> -#endif -#ifdef HAVE_SYS_STAT_H -#include <sys/stat.h> -#endif -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif -#ifdef HAVE_SYS_PARAM_H -#include <sys/param.h> -#endif -#ifdef HAVE_SYS_MOUNT_H -#include <sys/mount.h> -#endif -#ifdef HAVE_LINUX_FD_H -#include <linux/fd.h> -#endif -#ifdef HAVE_LINUX_HDREG_H -#include <linux/hdreg.h> -#endif - -#include "compat.h" -#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 *)ntfs_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; -} - -/** - * fake_pread - read operation disguised as pread - * @dev: device to read from - * @b: output data buffer - * @count: number of bytes to read - * @pos: position in device to read from - * - * Auxiliary function, used when we emulate pread by seek() + a sequence of - * read()s. - */ -static s64 fake_pread(struct ntfs_device *dev, void *b, s64 count, - s64 pos __attribute__((unused))) -{ - return dev->d_ops->read(dev, b, count); -} - -/** - * 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; - s64 (*_pread)(struct ntfs_device *, void *, s64, s64); - - 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; - _pread = dops->pread; - if (!_pread) - _pread = fake_pread; -seek: - /* Locate to position if pread is to be emulated by seek() + read(). */ - if (_pread == fake_pread && - 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 = _pread(dev, (char*)b + total, count, pos + total); - /* If everything ok, continue. */ - if (br > 0) - continue; - /* If EOF or error return number of bytes read. */ - if (!br || total) - return total; - /* - * If pread is not supported by the OS, fall back to emulating - * it by seek() + read() and set the device pread() pointer to - * NULL so we automatically use seek() + read() from now on. - */ - if (errno == ENOSYS && _pread != fake_pread) { - _pread = fake_pread; - dops->pread = NULL; - goto seek; - } - /* Nothing read and error, return error status. */ - return br; - } - /* Finally, return the number of bytes read. */ - return total; -} - -/** - * fake_pwrite - write operation disguised as pwrite - * @dev: device to write to - * @b: input data buffer - * @count: number of bytes to write - * @pos: position in device to write to - * - * Auxiliary function, used when we emulate pwrite by seek() + a sequence of - * write()s. - */ -static s64 fake_pwrite(struct ntfs_device *dev, const void *b, s64 count, - s64 pos __attribute__((unused))) -{ - return dev->d_ops->write(dev, b, count); -} - -/** - * 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; - s64 (*_pwrite)(struct ntfs_device *, const void *, s64, s64); - - 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; - _pwrite = dops->pwrite; - if (!_pwrite) - _pwrite = fake_pwrite; -seek: - /* - * Locate to position if pwrite is to be emulated by seek() + write(). - */ - if (_pwrite == fake_pwrite && - 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 = _pwrite(dev, (const char*)b + total, count, - pos + total); - /* If everything ok, continue. */ - if (written > 0) - continue; - /* - * If nothing written or error return number of bytes written. - */ - if (!written || total) - break; - /* - * If pwrite is not supported by the OS, fall back to emulating - * it by seek() + write() and set the device pwrite() pointer - * to NULL so we automatically use seek() + write() from now - * on. - */ - if (errno == ENOSYS && _pwrite != fake_pwrite) { - _pwrite = fake_pwrite; - dops->pwrite = NULL; - goto seek; - } - /* 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->u.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->u.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 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/usr/src/lib/libntfs/common/libntfs/device_io.c b/usr/src/lib/libntfs/common/libntfs/device_io.c deleted file mode 100644 index 706e935f34..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/device_io.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * device_io.c - Default device io operations. Part of the Linux-NTFS project. - * - * Copyright (c) 2003-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 - */ - -#include "config.h" - -#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS - -#if defined(__CYGWIN32__) - -/* On Cygwin; use Win32 low level device operations. */ -#include "win32_io.c" - -#elif defined(__FreeBSD__) - -/* On FreeBSD; need to use sector aligned i/o. */ -#include "freebsd_io.c" - -#else - -/* - * Not on Cygwin or FreeBSD; use standard Unix style low level device - * operations. - */ -#include "unix_io.c" - -#endif - -#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */ diff --git a/usr/src/lib/libntfs/common/libntfs/dir.c b/usr/src/lib/libntfs/common/libntfs/dir.c deleted file mode 100644 index b112c73205..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/dir.c +++ /dev/null @@ -1,1773 +0,0 @@ -/** - * dir.c - Directory handling code. Part of the Linux-NTFS project. - * - * Copyright (c) 2002-2005 Anton Altaparmakov - * Copyright (c) 2005-2007 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 <stdlib.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_SYS_STAT_H -#include <sys/stat.h> -#endif - -#ifdef HAVE_SYS_SYSMACROS_H -#include <sys/sysmacros.h> -#endif - -#include "compat.h" -#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->u.res.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->u.indexed_file); - ntfs_attr_put_search_ctx(ctx); - return mref; - } - /* - * For a case insensitive mount, we also perform a case - * insensitive comparison. If the comparison matches, we cache - * the mft reference in mref. Use first case insensitive match - * in case if no name matches case sensitive, but several names - * matches case insensitive. - */ - if (!mref && !NVolCaseSensitive(vol) && - 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)) - mref = le64_to_cpu(ie->u.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", - (unsigned long long)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->u.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. If the comparison matches, we cache - * the mft reference in mref. Use first case insensitive match - * in case if no name matches case sensitive, but several names - * matches case insensitive. - */ - if (!mref && !NVolCaseSensitive(vol) && - 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)) - mref = le64_to_cpu(ie->u.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_num - find the inode number 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: -1 Error, the pathname was invalid, or some other error occurred - * else Success, the pathname was valid - */ -u64 ntfs_pathname_to_inode_num(ntfs_volume *vol, ntfs_inode *parent, - const char *pathname) -{ - u64 inum, result; - int len, err = 0; - char *p, *q; - ntfs_inode *ni = NULL; - ntfschar *unicode = NULL; - char *ascii = NULL; - - inum = result = (u64)-1; - if (!vol || !pathname) { - err = EINVAL; - goto close; - } - ntfs_log_trace("Path: '%s'\n", pathname); - if (parent) { - ni = parent; - } else - inum = FILE_root; - unicode = calloc(1, MAX_PATH); - ascii = strdup(pathname); - if (!unicode || !ascii) { - ntfs_log_error("Out of memory.\n"); - err = ENOMEM; - goto close; - } - p = ascii; - /* Remove leading /'s. */ - while (p && *p == PATH_SEP) - p++; - while (p && *p) { - if (!ni) { - ni = ntfs_inode_open(vol, inum); - if (!ni) { - ntfs_log_debug("Cannot open inode %llu.\n", - (unsigned long long)inum); - err = EIO; - goto close; - } - } - /* 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; - } - inum = MREF(inum); - if (ni != parent) - ntfs_inode_close(ni); - ni = NULL; - p = q; - while (p && *p == PATH_SEP) - p++; - } - result = inum; -close: - if (ni && (ni != parent)) - ntfs_inode_close(ni); - free(ascii); - free(unicode); - if (err) - errno = err; - return result; -} - -/** - * 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; - - inum = ntfs_pathname_to_inode_num(vol, parent, pathname); - if (inum == (u64)-1) - return NULL; - return ntfs_inode_open(vol, inum); -} - -/* - * 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') }; - -/** - * ntfs_filldir - ntfs specific filldir method - * @vol: ntfs volume with wjich we are working - * @pos: current position in directory - * @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 int ntfs_filldir(ntfs_volume *vol, s64 *pos, INDEX_ENTRY *ie, - void *dirent, ntfs_filldir_t filldir) -{ - FILE_NAME_ATTR *fn = &ie->key.file_name; - unsigned dt_type; - - ntfs_log_trace("Entering.\n"); - - /* Skip root directory self reference entry. */ - if (MREF_LE(ie->u.indexed_file) == FILE_root) - return 0; - if (ie->key.file_name.file_attributes & FILE_ATTR_I30_INDEX_PRESENT) - dt_type = NTFS_DT_DIR; - else { - if (NVolInterix(vol) && fn->file_attributes & FILE_ATTR_SYSTEM) - dt_type = NTFS_DT_UNKNOWN; - 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->u.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; - - ntfs_log_trace("Entering.\n"); - - 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->u.res.value_offset)); - if ((u8*)fn + le32_to_cpu(ctx->attr->u.res.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; - - ntfs_log_trace("Entering.\n"); - - 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 err_out; - ++*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 err_out; - ++*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->u.res.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; - /* Advance the position even if going to skip the entry. */ - *pos = (u8*)ie - (u8*)ir; - /* - * Submit the directory entry to ntfs_filldir(), which will - * invoke the filldir() callback as appropriate. - */ - rc = ntfs_filldir(vol, pos, ie, dirent, filldir); - if (rc) - goto err_out; - } - 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; - /* Advance the position even if going to skip the entry. */ - *pos = (u8*)ie - (u8*)ia + (sle64_to_cpu( - ia->index_block_vcn) << index_vcn_size_bits) + - dir_ni->vol->mft_record_size; - /* - * Submit the directory entry to ntfs_filldir(), which will - * invoke the filldir() callback as appropriate. - */ - rc = ntfs_filldir(vol, pos, ie, dirent, filldir); - if (rc) - goto err_out; - } - 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); - ntfs_log_debug("EOD, *pos 0x%llx, returning 0.\n", (long long)*pos); - return 0; -dir_err_out: - errno = EIO; -err_out: - eo = errno; - if (rc) - ntfs_log_trace("filldir returned %i, *pos 0x%llx.", rc, - (long long)*pos); - 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 characters - * - * 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, rollback_sd = 0; - FILE_NAME_ATTR *fn = NULL; - STANDARD_INFORMATION *si = NULL; - SECURITY_DESCRIPTOR_ATTR *sd = NULL; - ACL *acl; - ACCESS_ALLOWED_ACE *ace; - SID *sid; - int err, fn_len, si_len, sd_len; - - ntfs_log_trace("Entering.\n"); - - /* Sanity checks. */ - if (!dir_ni || !name || !name_len) { - ntfs_log_error("Invalid arguments.\n"); - errno = EINVAL; - return NULL; - } - /* FIXME: Reparse points requires special handling. */ - if (dir_ni->flags & FILE_ATTR_REPARSE_POINT) { - errno = EOPNOTSUPP; - return NULL; - } - /* Allocate MFT record for new file. */ - ni = ntfs_mft_record_alloc(dir_ni->vol, NULL); - if (!ni) { - ntfs_log_error("Failed to allocate new MFT record: %s.\n", - strerror(errno)); - 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, u.v12.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; - } - /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */ - /* - * Calculate security descriptor length. We have 2 sub-authorities in - * owner and group SIDs, but structure SID contain only one, so add - * 4 bytes to every SID. - */ - sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) + - sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE); - sd = calloc(1, sd_len); - if (!sd) { - err = errno; - ntfs_log_error("Not enough memory.\n"); - goto err_out; - } - sd->revision = 1; - sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE; - sid = (SID*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_ATTR)); - sd->owner = cpu_to_le32((u8*)sid - (u8*)sd); - sid->revision = 1; - sid->sub_authority_count = 2; - sid->sub_authority[0] = cpu_to_le32(32); - sid->sub_authority[1] = cpu_to_le32(544); - sid->identifier_authority.value[5] = 5; - sid = (SID*)((u8*)sid + sizeof(SID) + 4); - sd->group = cpu_to_le32((u8*)sid - (u8*)sd); - sid->revision = 1; - sid->sub_authority_count = 2; - sid->sub_authority[0] = cpu_to_le32(32); - sid->sub_authority[1] = cpu_to_le32(544); - sid->identifier_authority.value[5] = 5; - acl = (ACL*)((u8*)sid + sizeof(SID) + 4); - sd->dacl = cpu_to_le32((u8*)acl - (u8*)sd); - acl->revision = 2; - acl->size = cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE)); - acl->ace_count = cpu_to_le16(1); - ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL)); - ace->type = ACCESS_ALLOWED_ACE_TYPE; - ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; - ace->size = cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE)); - ace->mask = cpu_to_le32(0x1f01ff); /* FIXME */ - ace->sid.revision = 1; - ace->sid.sub_authority_count = 1; - ace->sid.sub_authority[0] = 0; - ace->sid.identifier_authority.value[5] = 1; - /* Add SECURITY_DESCRIPTOR attribute to inode. */ - if (ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, - (u8*)sd, sd_len)) { - err = errno; - ntfs_log_error("Failed to add SECURITY_DESCRIPTOR " - "attribute.\n"); - goto err_out; - } - rollback_sd = 1; - /* Add DATA/INDEX_ROOT attribute. */ - 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 >> - ni->vol->sector_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; - } - free(ir); - } else { - INTX_FILE *data; - int data_len; - - switch (type) { - case S_IFBLK: - case S_IFCHR: - data_len = offsetof(INTX_FILE, u.s.device_end); - data = ntfs_malloc(data_len); - if (!data) { - err = errno; - goto err_out; - } - data->u.s.major = cpu_to_le64(major(dev)); - data->u.s.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 = ntfs_malloc(data_len); - if (!data) { - err = errno; - goto err_out; - } - data->magic = INTX_SYMBOLIC_LINK; - memcpy(data->u.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; - free(data); - 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 = ntfs_calloc(fn_len); - if (!fn) { - err = errno; - 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_perror("Failed to add entry to the index"); - 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); - free(sd); - ntfs_log_trace("Done.\n"); - return ni; -err_out: - ntfs_log_trace("Failed.\n"); - if (rollback_sd) { - ntfs_attr *na; - - na = ntfs_attr_open(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0); - if (!na) - ntfs_log_perror("Failed to open SD (0x50) 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 SD (0x50) attribute " - "of inode 0x%llx. Run chkdsk.\n", - (unsigned long long)ni->mft_no); - } - 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->u.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); - free(sd); - 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 - * @pni: ntfs inode for object to delete - * @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 - * - * @pni is pointer to pointer to ntfs_inode structure. Upon successful - * completion and if inode is really deleted (there are no more links left to - * it) this function will close @*pni and set it to NULL, in the other cases - * @*pni will stay opened. - * - * Return 0 on success or -1 on error with errno set to the error code. - */ -int ntfs_delete(ntfs_inode **pni, ntfs_inode *dir_ni, ntfschar *name, - u8 name_len) -{ - ntfs_attr_search_ctx *actx = NULL; - ntfs_index_context *ictx = NULL; - ntfs_inode *ni; - 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 (!pni || !(ni = *pni) || !dir_ni || !name || !name_len || - ni->nr_extents == -1 || dir_ni->nr_extents == -1) { - ntfs_log_error("Invalid arguments.\n"); - errno = EINVAL; - goto err_out; - } - if (ni->nr_references > 1 && le16_to_cpu(ni->mrec->link_count) == 1) { - ntfs_log_error("Trying to deleting inode with left " - "references.\n"); - errno = EINVAL; - goto err_out; - } - /* - * 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 absence in case if DOS name had - * been successfully deleted, but WIN32 name remove 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->u.res.value_offset)); - ntfs_log_trace("Found filename with instance number %d.\n", - le16_to_cpu(actx->attr->instance)); - 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, 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); - ntfs_log_trace("Restart search. " - "Looking for DOS name.\n"); - continue; - } - if (fn->file_name_type == FILE_NAME_DOS) - looking_for_dos_name = TRUE; - break; - } - } - if (errno) { - /* - * If case sensitive search failed and volume mounted case - * insensitive, then try once again ignoring case. - */ - if (errno == ENOENT && !NVolCaseSensitive(ni->vol) && - case_sensitive_match) { - case_sensitive_match = FALSE; - ntfs_attr_reinit_search_ctx(actx); - ntfs_log_trace("Restart search. Ignore case."); - goto search; - } - ntfs_log_error("Failed to find requested filename in FILE_NAME " - "attributes that belong to this inode.\n"); - 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 file 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); - } - /* One more sanity check. */ - if (ni->nr_references > 1 && looking_for_dos_name && - le16_to_cpu(ni->mrec->link_count) == 2) { - ntfs_log_error("Trying to deleting inode with left " - "references.\n"); - errno = EINVAL; - goto err_out; - } - ntfs_log_trace("Found!\n"); - /* 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->u.res.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; - - /* Remove FILE_NAME from inode. */ - if (ntfs_attr_record_rm(actx)) - goto err_out; - /* Decrement 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); - ntfs_log_trace("DOS name deleted. " - "Now search for WIN32 name.\n"); - goto search; - } else - ntfs_log_trace("Deleted.\n"); - /* TODO: Update object id, quota and security 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->u.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"); - } - *pni = NULL; -out: - if (actx) - ntfs_attr_put_search_ctx(actx); - if (ictx) - ntfs_index_ctx_put(ictx); - if (err) { - ntfs_log_error("%s(): Failed.\n", "ntfs_delete"); - 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 hard links to directories, we use them - * in a temporary state during rename. But it's definitely 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 = EINVAL; - ntfs_log_error("Invalid arguments."); - goto err_out; - } - /* FIXME: Reparse points requires special handling. */ - if (ni->flags & FILE_ATTR_REPARSE_POINT) { - err = EOPNOTSUPP; - goto err_out; - } - /* Create FILE_NAME attribute. */ - fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar); - fn = ntfs_calloc(fn_len); - if (!fn) { - err = errno; - 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("%s(): Failed.\n", "ntfs_link"); - free(fn); - errno = err; - return -1; -} - diff --git a/usr/src/lib/libntfs/common/libntfs/index.c b/usr/src/lib/libntfs/common/libntfs/index.c deleted file mode 100644 index efa4ae5913..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/index.c +++ /dev/null @@ -1,1862 +0,0 @@ -/** - * index.c - NTFS index handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2004-2005 Anton Altaparmakov - * Copyright (c) 2004-2005 Richard Russon - * Copyright (c) 2005-2007 Yura Pakhuchiy - * Copyright (c) 2005-2006 Szabolcs Szakacsits - * - * 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 <stdlib.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif - -#include "compat.h" -#include "attrib.h" -#include "collate.h" -#include "debug.h" -#include "index.h" -#include "mst.h" -#include "dir.h" -#include "logging.h" -#include "bitmap.h" -#include "support.h" - -/** - * 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 ib_dirty to TRUE, thus index block will be updated during - * ntfs_index_ctx_put. - */ -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->ib_dirty = TRUE; -} - -static s64 ntfs_ib_vcn_to_pos(ntfs_index_context *icx, VCN vcn) -{ - return vcn << icx->vcn_size_bits; -} - -static VCN ntfs_ib_pos_to_vcn(ntfs_index_context *icx, s64 pos) -{ - return pos >> icx->vcn_size_bits; -} - -static int ntfs_ib_write(ntfs_index_context *icx, VCN vcn, void *buf) -{ - s64 ret; - - ntfs_log_trace("vcn: %lld\n", vcn); - - ret = ntfs_attr_mst_pwrite(icx->ia_na, ntfs_ib_vcn_to_pos(icx, vcn), - 1, icx->block_size, buf); - if (ret != 1) { - ntfs_log_perror("Failed to write index block %lld of inode " - "%llu", (long long)vcn, - (unsigned long long)icx->ni->mft_no); - return STATUS_ERROR; - } - return STATUS_OK; -} - -static int ntfs_icx_ib_write(ntfs_index_context *icx) -{ - if (ntfs_ib_write(icx, icx->ib_vcn, icx->ib)) - return STATUS_ERROR; - - icx->ib_dirty = FALSE; - - return STATUS_OK; -} - -/** - * 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 *icx; - - ntfs_log_trace("Entering.\n"); - - if (!ni) { - errno = EINVAL; - return NULL; - } - if (ni->nr_extents == -1) - ni = ni->u.base_ni; - icx = ntfs_calloc(sizeof(ntfs_index_context)); - if (icx) - *icx = (ntfs_index_context) { - .ni = ni, - .name = name, - .name_len = name_len, - }; - return icx; -} - -static void ntfs_index_ctx_free(ntfs_index_context *icx) -{ - ntfs_log_trace("Entering.\n"); - - if (!icx->entry) - return; - - if (icx->actx) - ntfs_attr_put_search_ctx(icx->actx); - - if (icx->is_in_root) { - if (icx->ia_na) - ntfs_attr_close(icx->ia_na); - return; - } - - if (icx->ib_dirty) { - /* FIXME: Error handling!!! */ - ntfs_ib_write(icx, icx->ib_vcn, icx->ib); - } - - free(icx->ib); - ntfs_attr_close(icx->ia_na); -} - -/** - * ntfs_index_ctx_put - release an index context - * @icx: index context to free - * - * Release the index context @icx, releasing all associated resources. - */ -void ntfs_index_ctx_put(ntfs_index_context *icx) -{ - ntfs_index_ctx_free(icx); - free(icx); -} - -/** - * ntfs_index_ctx_reinit - reinitialize an index context - * @icx: index context to reinitialize - * - * Reinitialize the index context @icx so it can be used for ntfs_index_lookup. - */ -void ntfs_index_ctx_reinit(ntfs_index_context *icx) -{ - ntfs_log_trace("Entering.\n"); - - ntfs_index_ctx_free(icx); - - *icx = (ntfs_index_context) { - .ni = icx->ni, - .name = icx->name, - .name_len = icx->name_len, - }; -} - -static leVCN *ntfs_ie_get_vcn_addr(INDEX_ENTRY *ie) -{ - return (leVCN *)((u8 *)ie + le16_to_cpu(ie->length) - sizeof(VCN)); -} - -/** - * Get the subnode vcn to which the index entry refers. - */ -VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie) -{ - return sle64_to_cpup(ntfs_ie_get_vcn_addr(ie)); -} - -static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih) -{ - return (INDEX_ENTRY *)((u8 *)ih + le32_to_cpu(ih->entries_offset)); -} - -static INDEX_ENTRY *ntfs_ie_get_next(INDEX_ENTRY *ie) -{ - return (INDEX_ENTRY *)((char *)ie + le16_to_cpu(ie->length)); -} - -static u8 *ntfs_ie_get_end(INDEX_HEADER *ih) -{ - /* FIXME: check if it isn't overflowing the index block size */ - return (u8 *)ih + le32_to_cpu(ih->index_length); -} - -static int ntfs_ie_end(INDEX_ENTRY *ie) -{ - return (ie->flags & INDEX_ENTRY_END) ? 1 : 0; -} - -/** - * Find the last entry in the index block - */ -static INDEX_ENTRY *ntfs_ie_get_last(INDEX_ENTRY *ie, char *ies_end) -{ - ntfs_log_trace("Entering.\n"); - - while ((char *)ie < ies_end && !ntfs_ie_end(ie)) - ie = ntfs_ie_get_next(ie); - return ie; -} - -static INDEX_ENTRY *ntfs_ie_get_by_pos(INDEX_HEADER *ih, int pos) -{ - INDEX_ENTRY *ie; - - ntfs_log_trace("pos: %d\n", pos); - - ie = ntfs_ie_get_first(ih); - - while (pos-- > 0) - ie = ntfs_ie_get_next(ie); - return ie; -} - -static INDEX_ENTRY *ntfs_ie_prev(INDEX_HEADER *ih, INDEX_ENTRY *ie) -{ - INDEX_ENTRY *ie_prev, *tmp; - - ntfs_log_trace("Entering.\n"); - - ie_prev = NULL; - tmp = ntfs_ie_get_first(ih); - - while (tmp != ie) { - ie_prev = tmp; - tmp = ntfs_ie_get_next(tmp); - } - return ie_prev; -} - -char *ntfs_ie_filename_get(INDEX_ENTRY *ie) -{ - FILE_NAME_ATTR *fn; - char *name = NULL; - int name_len; - - fn = (FILE_NAME_ATTR *)&ie->key; - name_len = ntfs_ucstombs(fn->file_name, fn->file_name_length, &name, 0); - if (name_len < 0) { - ntfs_log_perror("ntfs_ucstombs"); - return NULL; - } else if (name_len > 0) - return name; - free(name); - return NULL; -} - -void ntfs_ie_filename_dump(INDEX_ENTRY *ie) -{ - char *s; - - s = ntfs_ie_filename_get(ie); - ntfs_log_debug("'%s' ", s); - free(s); -} - -void ntfs_ih_filename_dump(INDEX_HEADER *ih) -{ - INDEX_ENTRY *ie; - - ntfs_log_trace("Entering.\n"); - - ie = ntfs_ie_get_first(ih); - while (!ntfs_ie_end(ie)) { - ntfs_ie_filename_dump(ie); - ie = ntfs_ie_get_next(ie); - } -} - -static int ntfs_ih_numof_entries(INDEX_HEADER *ih) -{ - int n; - INDEX_ENTRY *ie; - - ntfs_log_trace("Entering.\n"); - - ie = ntfs_ie_get_first(ih); - for (n = 0; !ntfs_ie_end(ie); n++) - ie = ntfs_ie_get_next(ie); - return n; -} - -static int ntfs_ih_one_entry(INDEX_HEADER *ih) -{ - return (ntfs_ih_numof_entries(ih) == 1); -} - -static int ntfs_ih_zero_entry(INDEX_HEADER *ih) -{ - return (ntfs_ih_numof_entries(ih) == 0); -} - -static void ntfs_ie_delete(INDEX_HEADER *ih, INDEX_ENTRY *ie) -{ - u32 new_size; - - ntfs_log_trace("Entering.\n"); - - new_size = le32_to_cpu(ih->index_length) - le16_to_cpu(ie->length); - ih->index_length = cpu_to_le32(new_size); - memmove(ie, (u8 *)ie + le16_to_cpu(ie->length), - new_size - ((u8 *)ie - (u8 *)ih)); -} - -static void ntfs_ie_set_vcn(INDEX_ENTRY *ie, VCN vcn) -{ - *ntfs_ie_get_vcn_addr(ie) = cpu_to_sle64(vcn); -} - -/** - * Insert @ie index entry at @pos entry. Used @ih values should be ok already. - */ -static void ntfs_ie_insert(INDEX_HEADER *ih, INDEX_ENTRY *ie, INDEX_ENTRY *pos) -{ - int ie_size = le16_to_cpu(ie->length); - - ntfs_log_trace("Entering.\n"); - - ih->index_length = cpu_to_le32(le32_to_cpu(ih->index_length) + ie_size); - memmove((u8 *)pos + ie_size, pos, - le32_to_cpu(ih->index_length) - ((u8 *)pos - (u8 *)ih) - - ie_size); - memcpy(pos, ie, ie_size); -} - -static INDEX_ENTRY *ntfs_ie_dup(INDEX_ENTRY *ie) -{ - INDEX_ENTRY *dup; - - ntfs_log_trace("Entering.\n"); - - dup = ntfs_malloc(le16_to_cpu(ie->length)); - if (dup) - memcpy(dup, ie, le16_to_cpu(ie->length)); - return dup; -} - -static INDEX_ENTRY *ntfs_ie_dup_novcn(INDEX_ENTRY *ie) -{ - INDEX_ENTRY *dup; - int size = le16_to_cpu(ie->length); - - ntfs_log_trace("Entering.\n"); - - if (ie->flags & INDEX_ENTRY_NODE) - size -= sizeof(VCN); - - dup = ntfs_malloc(size); - if (dup) { - memcpy(dup, ie, size); - dup->flags &= ~INDEX_ENTRY_NODE; - dup->length = cpu_to_le16(size); - } - return dup; -} - -static int ntfs_ia_check(ntfs_index_context *icx, INDEX_BLOCK *ib, VCN vcn) -{ - u32 ib_size = (unsigned)le32_to_cpu(ib->index.allocated_size) + 0x18; - - ntfs_log_trace("Entering.\n"); - - if (!ntfs_is_indx_record(ib->magic)) { - - ntfs_log_error("Corrupt index block signature: vcn %lld inode " - "%llu\n", (long long)vcn, - (unsigned long long)icx->ni->mft_no); - return -1; - } - - if (sle64_to_cpu(ib->index_block_vcn) != vcn) { - - ntfs_log_error("Corrupt index block: VCN (%lld) is different " - "from expected VCN (%lld) in inode %llu\n", - (long long)sle64_to_cpu(ib->index_block_vcn), - (long long)vcn, - (unsigned long long)icx->ni->mft_no); - return -1; - } - - if (ib_size != icx->block_size) { - - ntfs_log_error("Corrupt index block : VCN (%lld) of inode %llu " - "has a size (%u) differing from the index " - "specified size (%u)\n", (long long)vcn, - icx->ni->mft_no, (unsigned)ib_size, - (unsigned)icx->block_size); - return -1; - } - return 0; -} - -static INDEX_ROOT *ntfs_ir_lookup(ntfs_inode *ni, ntfschar *name, - u32 name_len, ntfs_attr_search_ctx **ctx) -{ - ATTR_RECORD *a; - INDEX_ROOT *ir = NULL; - - ntfs_log_trace("Entering.\n"); - - *ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!*ctx) { - ntfs_log_perror("Failed to get $INDEX_ROOT search context"); - return NULL; - } - - if (ntfs_attr_lookup(AT_INDEX_ROOT, name, name_len, CASE_SENSITIVE, - 0, NULL, 0, *ctx)) { - ntfs_log_perror("Failed to lookup $INDEX_ROOT"); - goto err_out; - } - - a = (*ctx)->attr; - if (a->non_resident) { - errno = EINVAL; - ntfs_log_perror("Non-resident $INDEX_ROOT detected"); - goto err_out; - } - - ir = (INDEX_ROOT *)((char *)a + le16_to_cpu(a->u.res.value_offset)); -err_out: - if (!ir) - ntfs_attr_put_search_ctx(*ctx); - return ir; -} - -/** - * Find a key in the index block. - * - * Return values: - * STATUS_OK with errno set to ESUCCESS if we know for sure that the - * entry exists and @ie_out points to this entry. - * STATUS_NOT_FOUND with errno set to ENOENT if we know for sure the - * entry doesn't exist and @ie_out is the insertion point. - * STATUS_KEEP_SEARCHING if we can't answer the above question and - * @vcn will contain the node index block. - * STATUS_ERROR with errno set if on unexpected error during lookup. - */ -static int ntfs_ie_lookup(const void *key, const int key_len, - ntfs_index_context *icx, INDEX_HEADER *ih, - VCN *vcn, INDEX_ENTRY **ie_out) -{ - INDEX_ENTRY *ie; - u8 *index_end; - int rc, item = 0; - - ntfs_log_trace("Entering.\n"); - - index_end = ntfs_ie_get_end(ih); - - /* - * Loop until we exceed valid memory (corruption case) or until we - * reach the last entry. - */ - for (ie = ntfs_ie_get_first(ih); ; ie = ntfs_ie_get_next(ie)) { - /* Bounds checks. */ - if ((u8 *)ie + sizeof(INDEX_ENTRY_HEADER) > index_end || - (u8 *)ie + le16_to_cpu(ie->length) > index_end) { - errno = ERANGE; - ntfs_log_error("Index entry out of bounds in inode " - "%llu.\n", - (unsigned long long)icx->ni->mft_no); - return STATUS_ERROR; - } - /* - * 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 (ntfs_ie_end(ie)) - break; - /* - * 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(icx->ni->vol, icx->cr, key, key_len, &ie->key, - le16_to_cpu(ie->key_length)); - if (rc == NTFS_COLLATION_ERROR) { - ntfs_log_error("Collation error. Perhaps a filename " - "contains invalid characters?\n"); - errno = ERANGE; - return STATUS_ERROR; - } - /* - * 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; - - if (!rc) { - *ie_out = ie; - errno = 0; - icx->parent_pos[icx->pindex] = item; - return STATUS_OK; - } - - item++; - } - /* - * We have finished with this index block without success. Check for the - * presence of a child node and if not present return with errno ENOENT, - * otherwise we will keep searching in another index block. - */ - if (!(ie->flags & INDEX_ENTRY_NODE)) { - ntfs_log_debug("Index entry wasn't found.\n"); - *ie_out = ie; - errno = ENOENT; - return STATUS_NOT_FOUND; - } - - /* Get the starting vcn of the index_block holding the child node. */ - *vcn = ntfs_ie_get_vcn(ie); - if (*vcn < 0) { - errno = EINVAL; - ntfs_log_perror("Negative vcn in inode %llu\n", - icx->ni->mft_no); - return STATUS_ERROR; - } - - ntfs_log_trace("Parent entry number %d\n", item); - icx->parent_pos[icx->pindex] = item; - return STATUS_KEEP_SEARCHING; -} - -static ntfs_attr *ntfs_ia_open(ntfs_index_context *icx, ntfs_inode *ni) -{ - ntfs_attr *na; - - na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, icx->name, icx->name_len); - if (!na) { - ntfs_log_perror("Failed to open index allocation of inode " - "%llu", (unsigned long long)ni->mft_no); - return NULL; - } - return na; -} - -static int ntfs_ib_read(ntfs_index_context *icx, VCN vcn, INDEX_BLOCK *dst) -{ - s64 pos, ret; - - ntfs_log_trace("vcn: %lld\n", vcn); - - pos = ntfs_ib_vcn_to_pos(icx, vcn); - - ret = ntfs_attr_mst_pread(icx->ia_na, pos, 1, icx->block_size, - (u8 *)dst); - if (ret != 1) { - if (ret == -1) - ntfs_log_perror("Failed to read index block"); - else - ntfs_log_error("Failed to read full index block at " - "%lld\n", (long long)pos); - return -1; - } - - if (ntfs_ia_check(icx, dst, vcn)) - return -1; - return 0; -} - -static int ntfs_icx_parent_inc(ntfs_index_context *icx) -{ - icx->pindex++; - if (icx->pindex >= MAX_PARENT_VCN) { - errno = EOPNOTSUPP; - ntfs_log_perror("Index is over %d level deep", MAX_PARENT_VCN); - return STATUS_ERROR; - } - return STATUS_OK; -} - -static int ntfs_icx_parent_dec(ntfs_index_context *icx) -{ - icx->pindex--; - if (icx->pindex < 0) { - errno = EINVAL; - ntfs_log_perror("Corrupt index pointer (%d)", icx->pindex); - return STATUS_ERROR; - } - return STATUS_OK; -} - -/** - * 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 - * @icx: [IN/OUT] context describing the index and the returned entry - * - * Before calling ntfs_index_lookup(), @icx 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 @icx. - * 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 @icx is setup to - * describe the index entry containing the matching @key. @icx->entry is the - * index entry and @icx->data and @icx->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 - * @icx 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 @icx 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 *icx) -{ - VCN old_vcn, vcn; - ntfs_inode *ni = icx->ni; - INDEX_ROOT *ir; - INDEX_ENTRY *ie; - INDEX_BLOCK *ib = NULL; - ntfs_attr_search_ctx *actx; - int ret, err = 0; - - ntfs_log_trace("Entering.\n"); - - if (!key || key_len <= 0) { - errno = EINVAL; - ntfs_log_perror("key: %p key_len: %d", key, key_len); - return -1; - } - - ir = ntfs_ir_lookup(ni, icx->name, icx->name_len, &actx); - if (!ir) { - if (errno == ENOENT) - errno = EIO; - return -1; - } - - icx->block_size = le32_to_cpu(ir->index_block_size); - if (icx->block_size < NTFS_BLOCK_SIZE) { - errno = EINVAL; - ntfs_log_perror("Index block size (%u) is smaller than the " - "sector size (%d)", (unsigned)icx->block_size, - NTFS_BLOCK_SIZE); - return -1; - } - - if (ni->vol->cluster_size <= icx->block_size) - icx->vcn_size_bits = ni->vol->cluster_size_bits; - else - icx->vcn_size_bits = ni->vol->sector_size_bits; - - icx->cr = ir->collation_rule; - if (!ntfs_is_collation_rule_supported(icx->cr)) { - err = errno = EOPNOTSUPP; - ntfs_log_perror("Unknown collation rule 0x%x", - (unsigned)le32_to_cpu(icx->cr)); - goto err_out; - } - - old_vcn = VCN_INDEX_ROOT_PARENT; - /* - * FIXME: check for both ir and ib that the first index entry is - * within the index block. - */ - ret = ntfs_ie_lookup(key, key_len, icx, &ir->index, &vcn, &ie); - if (ret == STATUS_ERROR) { - err = errno; - goto err_out; - } - - icx->actx = actx; - icx->ir = ir; - - if (ret != STATUS_KEEP_SEARCHING) { - /* STATUS_OK or STATUS_NOT_FOUND */ - err = errno; - icx->is_in_root = TRUE; - icx->parent_vcn[icx->pindex] = old_vcn; - goto done; - } - - /* Child node present, descend into it. */ - icx->ia_na = ntfs_ia_open(icx, ni); - if (!icx->ia_na) - goto err_out; - - ib = ntfs_malloc(icx->block_size); - if (!ib) { - err = errno; - goto err_out; - } - -descend_into_child_node: - icx->parent_vcn[icx->pindex] = old_vcn; - if (ntfs_icx_parent_inc(icx)) { - err = errno; - goto err_out; - } - old_vcn = vcn; - - ntfs_log_debug("Descend into node with VCN %lld.\n", vcn); - - if (ntfs_ib_read(icx, vcn, ib)) - goto err_out; - - ret = ntfs_ie_lookup(key, key_len, icx, &ib->index, &vcn, &ie); - if (ret != STATUS_KEEP_SEARCHING) { - err = errno; - if (ret == STATUS_ERROR) - goto err_out; - - /* STATUS_OK or STATUS_NOT_FOUND */ - icx->is_in_root = FALSE; - icx->ib = ib; - icx->parent_vcn[icx->pindex] = icx->ib_vcn = vcn; - goto done; - } - - if ((ib->index.flags & NODE_MASK) == LEAF_NODE) { - ntfs_log_error("Index entry with child node found in a leaf " - "node in inode 0x%llx.\n", - (unsigned long long)ni->mft_no); - goto err_out; - } - - goto descend_into_child_node; -err_out: - if (icx->ia_na) { - ntfs_attr_close(icx->ia_na); - icx->ia_na = NULL; - } - free(ib); - if (!err) - err = EIO; - if (actx) - ntfs_attr_put_search_ctx(actx); - errno = err; - return -1; -done: - icx->entry = ie; - icx->data = (u8 *)ie + offsetof(INDEX_ENTRY, key); - icx->data_len = le16_to_cpu(ie->key_length); - icx->max_depth = icx->pindex; - ntfs_log_trace("Done.\n"); - if (err) { - errno = err; - return -1; - } - return 0; -} - -static INDEX_BLOCK *ntfs_ib_alloc(VCN ib_vcn, u32 ib_size, - INDEX_HEADER_FLAGS node_type) -{ - INDEX_BLOCK *ib; - int ih_size = sizeof(INDEX_HEADER); - - ntfs_log_trace("Entering ib_vcn = %lld ib_size = %u\n", ib_vcn, - ib_size); - - ib = ntfs_calloc(ib_size); - if (!ib) - return NULL; - - ib->magic = magic_INDX; - ib->usa_ofs = cpu_to_le16(sizeof(INDEX_BLOCK)); - ib->usa_count = cpu_to_le16(ib_size / NTFS_BLOCK_SIZE + 1); - /* Set USN to 1 */ - *(le16 *)((char *)ib + le16_to_cpu(ib->usa_ofs)) = cpu_to_le16(1); - ib->lsn = 0; - - ib->index_block_vcn = cpu_to_sle64(ib_vcn); - - ib->index.entries_offset = cpu_to_le32((ih_size + - le16_to_cpu(ib->usa_count) * 2 + 7) & ~7); - ib->index.index_length = 0; - ib->index.allocated_size = cpu_to_le32(ib_size - - (sizeof(INDEX_BLOCK) - ih_size)); - ib->index.flags = node_type; - return ib; -} - -/** - * Find the median by going through all the entries - */ -static INDEX_ENTRY *ntfs_ie_get_median(INDEX_HEADER *ih) -{ - INDEX_ENTRY *ie, *ie_start; - u8 *ie_end; - int i = 0, median; - - ntfs_log_trace("Entering.\n"); - - ie = ie_start = ntfs_ie_get_first(ih); - ie_end = (u8 *)ntfs_ie_get_end(ih); - - while ((u8 *)ie < ie_end && !ntfs_ie_end(ie)) { - ie = ntfs_ie_get_next(ie); - i++; - } - /* - * NOTE: this could be also the entry at the half of the index block. - */ - median = i / 2 - 1; - - ntfs_log_trace("Entries: %d median: %d\n", i, median); - - for (i = 0, ie = ie_start; i <= median; i++) - ie = ntfs_ie_get_next(ie); - - return ie; -} - -static s64 ntfs_ibm_vcn_to_pos(ntfs_index_context *icx, VCN vcn) -{ - return ntfs_ib_vcn_to_pos(icx, vcn) / icx->block_size; -} - -static s64 ntfs_ibm_pos_to_vcn(ntfs_index_context *icx, s64 pos) -{ - return ntfs_ib_pos_to_vcn(icx, pos * icx->block_size); -} - -static int ntfs_ibm_add(ntfs_index_context *icx) -{ - u8 bmp[8]; - - ntfs_log_trace("Entering.\n"); - - if (ntfs_attr_exist(icx->ni, AT_BITMAP, icx->name, icx->name_len)) - return STATUS_OK; - /* - * AT_BITMAP must be at least 8 bytes. - */ - memset(bmp, 0, sizeof(bmp)); - if (ntfs_attr_add(icx->ni, AT_BITMAP, icx->name, icx->name_len, - bmp, sizeof(bmp))) { - ntfs_log_perror("Failed to add AT_BITMAP"); - return STATUS_ERROR; - } - return STATUS_OK; -} - -static int ntfs_ibm_modify(ntfs_index_context *icx, VCN vcn, int set) -{ - u8 byte; - s64 pos = ntfs_ibm_vcn_to_pos(icx, vcn); - u32 bpos = pos / 8; - u32 bit = 1 << (pos % 8); - ntfs_attr *na; - int ret = STATUS_ERROR; - - ntfs_log_trace("%s vcn: %lld\n", set ? "set" : "clear", vcn); - - na = ntfs_attr_open(icx->ni, AT_BITMAP, icx->name, icx->name_len); - if (!na) { - ntfs_log_perror("Failed to open $BITMAP attribute"); - return -1; - } - - if (set) { - if (na->data_size < bpos + 1) { - if (ntfs_attr_truncate(na, (na->data_size + 8) & ~7)) { - ntfs_log_perror("Failed to truncate AT_BITMAP"); - goto err_na; - } - } - } - - if (ntfs_attr_pread(na, bpos, 1, &byte) != 1) { - ntfs_log_perror("Failed to read $BITMAP"); - goto err_na; - } - - if (set) - byte |= bit; - else - byte &= ~bit; - - if (ntfs_attr_pwrite(na, bpos, 1, &byte) != 1) { - ntfs_log_perror("Failed to write $Bitmap"); - goto err_na; - } - - ret = STATUS_OK; -err_na: - ntfs_attr_close(na); - return ret; -} - - -static int ntfs_ibm_set(ntfs_index_context *icx, VCN vcn) -{ - return ntfs_ibm_modify(icx, vcn, 1); -} - -static int ntfs_ibm_clear(ntfs_index_context *icx, VCN vcn) -{ - return ntfs_ibm_modify(icx, vcn, 0); -} - -static VCN ntfs_ibm_get_free(ntfs_index_context *icx) -{ - u8 *bm; - int bit; - s64 vcn, byte, size; - - ntfs_log_trace("Entering.\n"); - - bm = ntfs_attr_readall(icx->ni, AT_BITMAP, icx->name, icx->name_len, - &size); - if (!bm) - return (VCN)-1; - - for (byte = 0; byte < size; byte++) { - - if (bm[byte] == 255) - continue; - - for (bit = 0; bit < 8; bit++) { - if (!(bm[byte] & (1 << bit))) { - vcn = ntfs_ibm_pos_to_vcn(icx, byte * 8 + bit); - goto out; - } - } - } - - vcn = ntfs_ibm_pos_to_vcn(icx, size * 8); -out: - ntfs_log_trace("allocated vcn: %lld\n", vcn); - - if (ntfs_ibm_set(icx, vcn)) - vcn = (VCN)-1; - - free(bm); - return vcn; -} - -static INDEX_BLOCK *ntfs_ir_to_ib(INDEX_ROOT *ir, VCN ib_vcn) -{ - INDEX_BLOCK *ib; - INDEX_ENTRY *ie_last; - char *ies_start, *ies_end; - int i; - - ntfs_log_trace("Entering.\n"); - - if (!(ib = ntfs_ib_alloc(ib_vcn, le32_to_cpu(ir->index_block_size), - LEAF_NODE))) - return NULL; - - ies_start = (char *)ntfs_ie_get_first(&ir->index); - ies_end = (char *)ntfs_ie_get_end(&ir->index); - - ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); - /* - * Copy all entries, including the termination entry - * as well, which can never have any data. - */ - i = (char *)ie_last - ies_start + le16_to_cpu(ie_last->length); - memcpy(ntfs_ie_get_first(&ib->index), ies_start, i); - - ib->index.flags = ir->index.flags; - ib->index.index_length = cpu_to_le32(i + - le32_to_cpu(ib->index.entries_offset)); - return ib; -} - -static void ntfs_ir_nill(INDEX_ROOT *ir) -{ - INDEX_ENTRY *ie_last; - char *ies_start, *ies_end; - - ntfs_log_trace("Entering\n"); - /* TODO: This function could be much simpler. */ - ies_start = (char *)ntfs_ie_get_first(&ir->index); - ies_end = (char *)ntfs_ie_get_end(&ir->index); - ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); - /* Move the index root termination entry forward. */ - if ((char *)ie_last > ies_start) { - memmove(ies_start, (char *)ie_last, le16_to_cpu( - ie_last->length)); - ie_last = (INDEX_ENTRY *)ies_start; - } -} - -static int ntfs_ib_copy_tail(ntfs_index_context *icx, INDEX_BLOCK *src, - INDEX_ENTRY *median, VCN new_vcn) -{ - u8 *ies_end; - INDEX_ENTRY *ie_head; /* first entry after the median */ - int tail_size, ret; - INDEX_BLOCK *dst; - - ntfs_log_trace("Entering.\n"); - - dst = ntfs_ib_alloc(new_vcn, icx->block_size, - src->index.flags & NODE_MASK); - if (!dst) - return STATUS_ERROR; - - ie_head = ntfs_ie_get_next(median); - - ies_end = (u8 *)ntfs_ie_get_end(&src->index); - tail_size = ies_end - (u8 *)ie_head; - memcpy(ntfs_ie_get_first(&dst->index), ie_head, tail_size); - - dst->index.index_length = cpu_to_le32(tail_size + - le32_to_cpu(dst->index.entries_offset)); - - ret = ntfs_ib_write(icx, new_vcn, dst); - - free(dst); - return ret; -} - -static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *src, - INDEX_ENTRY *ie) -{ - char *ies_start, *ies_end; - INDEX_ENTRY *ie_last; - - ntfs_log_trace("Entering.\n"); - - ies_start = (char *)ntfs_ie_get_first(&src->index); - ies_end = (char *)ntfs_ie_get_end(&src->index); - - ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); - if (ie_last->flags & INDEX_ENTRY_NODE) - ntfs_ie_set_vcn(ie_last, ntfs_ie_get_vcn(ie)); - - memcpy(ie, ie_last, le16_to_cpu(ie_last->length)); - - src->index.index_length = cpu_to_le32(((char *)ie - ies_start) + - le16_to_cpu(ie->length) + - le32_to_cpu(src->index.entries_offset)); - - if (ntfs_ib_write(icx, icx->parent_vcn[icx->pindex + 1], src)) - return STATUS_ERROR; - - return STATUS_OK; -} - -static int ntfs_ia_add(ntfs_index_context *icx) -{ - ntfs_log_trace("Entering.\n"); - - if (ntfs_ibm_add(icx)) - return -1; - - if (!ntfs_attr_exist(icx->ni, AT_INDEX_ALLOCATION, icx->name, - icx->name_len)) { - if (ntfs_attr_add(icx->ni, AT_INDEX_ALLOCATION, icx->name, - icx->name_len, NULL, 0)) { - ntfs_log_perror("Failed to add AT_INDEX_ALLOCATION"); - return -1; - } - } - - icx->ia_na = ntfs_ia_open(icx, icx->ni); - if (!icx->ia_na) - return -1; - return 0; -} - -static INDEX_ROOT *ntfs_ir_lookup2(ntfs_inode *ni, ntfschar *name, u32 len) -{ - ntfs_attr_search_ctx *ctx; - INDEX_ROOT *ir; - - ir = ntfs_ir_lookup(ni, name, len, &ctx); - if (ir) - ntfs_attr_put_search_ctx(ctx); - return ir; -} - -static int ntfs_ir_reparent(ntfs_index_context *icx) -{ - ntfs_attr_search_ctx *ctx; - INDEX_ROOT *ir; - INDEX_ENTRY *ie; - INDEX_BLOCK *ib = NULL; - VCN new_ib_vcn; - int ret = STATUS_ERROR; - - ntfs_log_trace("Entering.\n"); - - if (!(icx->ia_na)) - if (ntfs_ia_add(icx)) - return -1; - - ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, &ctx); - if (!ir) - return -1; - - new_ib_vcn = ntfs_ibm_get_free(icx); - if (new_ib_vcn == -1) - goto err_out; - - ib = ntfs_ir_to_ib(ir, new_ib_vcn); - if (ib == NULL) { - ntfs_log_perror("Failed to move index root to index block"); - goto clear_bmp; - } - - if (ntfs_ib_write(icx, new_ib_vcn, ib)) - goto clear_bmp; - - ntfs_ir_nill(ir); - - ie = ntfs_ie_get_first(&ir->index); - ie->flags |= INDEX_ENTRY_NODE; - ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN)); - ntfs_ie_set_vcn(ie, new_ib_vcn); - - ir->index.flags = LARGE_INDEX; - ir->index.index_length = cpu_to_le32(le32_to_cpu( - ir->index.entries_offset) + le16_to_cpu(ie->length)); - ir->index.allocated_size = ir->index.index_length; - - if (ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, - sizeof(INDEX_ROOT) - sizeof(INDEX_HEADER) + - le32_to_cpu(ir->index.allocated_size))) - /* FIXME: revert bitmap, index root */ - goto err_out; - ntfs_inode_mark_dirty(ctx->ntfs_ino); - - ret = STATUS_OK; -err_out: - ntfs_attr_put_search_ctx(ctx); - free(ib); - return ret; -clear_bmp: - ntfs_ibm_clear(icx, new_ib_vcn); - goto err_out; -} - -/** - * ntfs_ir_truncate - Truncate index root attribute - * - * Returns STATUS_OK, STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT or STATUS_ERROR. - */ -static int ntfs_ir_truncate(ntfs_index_context *icx, int data_size) -{ - ntfs_attr *na; - int ret; - - ntfs_log_trace("Entering.\n"); - - na = ntfs_attr_open(icx->ni, AT_INDEX_ROOT, icx->name, icx->name_len); - if (!na) { - ntfs_log_perror("Failed to open INDEX_ROOT"); - return STATUS_ERROR; - } - /* - * INDEX_ROOT must be resident and its entries can be moved to - * INDEX_BLOCK, so ENOSPC isn't a real error. - */ - ret = ntfs_attr_truncate(na, data_size + offsetof(INDEX_ROOT, index)); - if (ret == STATUS_OK) { - icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); - if (!icx->ir) - return STATUS_ERROR; - - icx->ir->index.allocated_size = cpu_to_le32(data_size); - } else { - if (errno != ENOSPC && errno != EOVERFLOW) - ntfs_log_trace("Failed to truncate INDEX_ROOT"); - if (errno == EOVERFLOW) - ret = STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT; - } - - ntfs_attr_close(na); - return ret; -} - -/** - * ntfs_ir_make_space - Make more space for the index root attribute - * - * On success return STATUS_OK or STATUS_KEEP_SEARCHING. - * On error return STATUS_ERROR. - */ -static int ntfs_ir_make_space(ntfs_index_context *icx, int data_size) -{ - int ret; - - ntfs_log_trace("Entering.\n"); - - ret = ntfs_ir_truncate(icx, data_size); - if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) { - ret = ntfs_ir_reparent(icx); - if (ret == STATUS_OK) - ret = STATUS_KEEP_SEARCHING; - else - ntfs_log_perror("Failed to nodify INDEX_ROOT"); - } - return ret; -} - -/* - * NOTE: 'ie' must be a copy of a real index entry. - */ -static int ntfs_ie_add_vcn(INDEX_ENTRY **ie) -{ - INDEX_ENTRY *p, *old = *ie; - - old->length = cpu_to_le16(le16_to_cpu(old->length) + sizeof(VCN)); - p = realloc(old, le16_to_cpu(old->length)); - if (!p) - return STATUS_ERROR; - - p->flags |= INDEX_ENTRY_NODE; - *ie = p; - return STATUS_OK; -} - -static int ntfs_ih_insert(INDEX_HEADER *ih, INDEX_ENTRY *orig_ie, VCN new_vcn, - int pos) -{ - INDEX_ENTRY *ie_node, *ie; - int ret = STATUS_ERROR; - VCN old_vcn; - - ntfs_log_trace("Entering.\n"); - - ie = ntfs_ie_dup(orig_ie); - if (!ie) - return STATUS_ERROR; - - if (!(ie->flags & INDEX_ENTRY_NODE)) - if (ntfs_ie_add_vcn(&ie)) - goto out; - - ie_node = ntfs_ie_get_by_pos(ih, pos); - old_vcn = ntfs_ie_get_vcn(ie_node); - ntfs_ie_set_vcn(ie_node, new_vcn); - - ntfs_ie_insert(ih, ie, ie_node); - ntfs_ie_set_vcn(ie_node, old_vcn); - ret = STATUS_OK; -out: - free(ie); - return ret; -} - -static VCN ntfs_icx_parent_vcn(ntfs_index_context *icx) -{ - return icx->parent_vcn[icx->pindex]; -} - -static VCN ntfs_icx_parent_pos(ntfs_index_context *icx) -{ - return icx->parent_pos[icx->pindex]; -} - -static int ntfs_ir_insert_median(ntfs_index_context *icx, INDEX_ENTRY *median, - VCN new_vcn) -{ - u32 new_size; - int ret; - - ntfs_log_trace("Entering.\n"); - - icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); - if (!icx->ir) - return STATUS_ERROR; - - new_size = le32_to_cpu(icx->ir->index.index_length) + - le16_to_cpu(median->length); - if (!(median->flags & INDEX_ENTRY_NODE)) - new_size += sizeof(VCN); - - ret = ntfs_ir_make_space(icx, new_size); - if (ret != STATUS_OK) - return ret; - - icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); - if (!icx->ir) - return STATUS_ERROR; - - return ntfs_ih_insert(&icx->ir->index, median, new_vcn, - ntfs_icx_parent_pos(icx)); -} - -static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib); - -/** - * ntfs_ib_insert - insert an index block to an index context. - * - * On success return STATUS_OK or STATUS_KEEP_SEARCHING. - * On error return STATUS_ERROR. - */ -static int ntfs_ib_insert(ntfs_index_context *icx, INDEX_ENTRY *ie, VCN new_vcn) -{ - INDEX_BLOCK *ib; - u32 idx_size, allocated_size; - int err = STATUS_ERROR; - VCN old_vcn; - - ntfs_log_trace("Entering.\n"); - - ib = ntfs_malloc(icx->block_size); - if (!ib) - return -1; - - old_vcn = ntfs_icx_parent_vcn(icx); - - if (ntfs_ib_read(icx, old_vcn, ib)) - goto err_out; - - idx_size = le32_to_cpu(ib->index.index_length); - allocated_size = le32_to_cpu(ib->index.allocated_size); - /* FIXME: sizeof(VCN) should be included only if ie has no VCN */ - if (idx_size + le16_to_cpu(ie->length) + sizeof(VCN) > allocated_size) { - err = ntfs_ib_split(icx, ib); - if (err == STATUS_OK) - err = STATUS_KEEP_SEARCHING; - goto err_out; - } - - if (ntfs_ih_insert(&ib->index, ie, new_vcn, ntfs_icx_parent_pos(icx))) - goto err_out; - - if (ntfs_ib_write(icx, old_vcn, ib)) - goto err_out; - - err = STATUS_OK; -err_out: - free(ib); - return err; -} - -/** - * ntfs_ib_split - Split index allocation attribute - * - * On success return STATUS_OK or STATUS_KEEP_SEARCHING. - * On error return is STATUS_ERROR. - */ -static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib) -{ - INDEX_ENTRY *median; - VCN new_vcn; - int ret; - - ntfs_log_trace("Entering.\n"); - - if (ntfs_icx_parent_dec(icx)) - return STATUS_ERROR; - - median = ntfs_ie_get_median(&ib->index); - new_vcn = ntfs_ibm_get_free(icx); - if (new_vcn == -1) - return STATUS_ERROR; - - if (ntfs_ib_copy_tail(icx, ib, median, new_vcn)) { - ntfs_ibm_clear(icx, new_vcn); - return STATUS_ERROR; - } - - if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) - ret = ntfs_ir_insert_median(icx, median, new_vcn); - else - ret = ntfs_ib_insert(icx, median, new_vcn); - - ntfs_inode_mark_dirty(icx->actx->ntfs_ino); - - if (ret != STATUS_OK) { - ntfs_ibm_clear(icx, new_vcn); - return ret; - } - - ret = ntfs_ib_cut_tail(icx, ib, median); - return ret; -} - -static int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie) -{ - INDEX_HEADER *ih; - int allocated_size, new_size; - int ret = STATUS_ERROR; - -#ifdef DEBUG - char *fn; - fn = ntfs_ie_filename_get(ie); - ntfs_log_trace("file: '%s'\n", fn); - free(fn); -#endif - - while (1) { - if (!ntfs_index_lookup(&ie->key, le16_to_cpu(ie->key_length), - icx)) { - errno = EEXIST; - ntfs_log_error("Index already have such entry.\n"); - goto err_out; - } - if (errno != ENOENT) { - ntfs_log_perror("Failed to find place for new entry"); - goto err_out; - } - - if (icx->is_in_root) - ih = &icx->ir->index; - else - ih = &icx->ib->index; - - allocated_size = le32_to_cpu(ih->allocated_size); - new_size = le32_to_cpu(ih->index_length) + - le16_to_cpu(ie->length); - - if (new_size <= allocated_size) - break; - - ntfs_log_trace("index block sizes: allocated: %d needed: %d\n", - allocated_size, new_size); - - if (icx->is_in_root) { - if (ntfs_ir_make_space(icx, new_size) == STATUS_ERROR) - goto err_out; - } else { - if (ntfs_ib_split(icx, icx->ib) == STATUS_ERROR) - goto err_out; - } - ntfs_inode_mark_dirty(icx->actx->ntfs_ino); - ntfs_index_ctx_reinit(icx); - } - - ntfs_ie_insert(ih, ie, icx->entry); - ntfs_index_entry_mark_dirty(icx); - - ret = STATUS_OK; -err_out: - ntfs_log_trace("%s\n", ret ? "Failed" : "Done"); - return ret; -} - -/** - * 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 - * - * 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) -{ - INDEX_ENTRY *ie; - ntfs_index_context *icx; - int fn_size, ie_size, ret = -1, err; - - ntfs_log_trace("Entering.\n"); - - if (!ni || !fn) { - ntfs_log_error("Invalid arguments.\n"); - errno = EINVAL; - return -1; - } - - fn_size = (fn->file_name_length * sizeof(ntfschar)) + - sizeof(FILE_NAME_ATTR); - ie_size = (sizeof(INDEX_ENTRY_HEADER) + fn_size + 7) & ~7; - - ie = ntfs_calloc(ie_size); - if (!ie) - return -1; - - ie->u.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); - - icx = ntfs_index_ctx_get(ni, NTFS_INDEX_I30, 4); - if (!icx) - goto out; - - err = errno; - ret = ntfs_ie_add(icx, ie); - errno = err; - - ntfs_index_ctx_put(icx); -out: - free(ie); - return ret; -} - -static int ntfs_ih_takeout(ntfs_index_context *icx, INDEX_HEADER *ih, - INDEX_ENTRY *ie, INDEX_BLOCK *ib) -{ - INDEX_ENTRY *ie_roam; - int ret = STATUS_ERROR; - - ntfs_log_trace("Entering.\n"); - - ie_roam = ntfs_ie_dup_novcn(ie); - if (!ie_roam) - return STATUS_ERROR; - - ntfs_ie_delete(ih, ie); - - if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) - ntfs_inode_mark_dirty(icx->actx->ntfs_ino); - else - if (ntfs_ib_write(icx, ntfs_icx_parent_vcn(icx), ib)) - goto out; - - ntfs_index_ctx_reinit(icx); - - ret = ntfs_ie_add(icx, ie_roam); -out: - free(ie_roam); - return ret; -} - -/** - * ntfs_ir_leafify - - * - * Used if an empty index block to be deleted has END entry as the parent - * in the INDEX_ROOT which is the only one there. - */ -static void ntfs_ir_leafify(ntfs_index_context *icx, INDEX_HEADER *ih) -{ - INDEX_ENTRY *ie; - - ntfs_log_trace("Entering.\n"); - - ie = ntfs_ie_get_first(ih); - ie->flags &= ~INDEX_ENTRY_NODE; - ie->length = cpu_to_le16(le16_to_cpu(ie->length) - sizeof(VCN)); - - ih->index_length = cpu_to_le32(le32_to_cpu(ih->index_length) - - sizeof(VCN)); - ih->flags &= ~LARGE_INDEX; - - /* Not fatal error */ - ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length)); - - ntfs_inode_mark_dirty(icx->actx->ntfs_ino); - ntfs_index_ctx_reinit(icx); -} - -/** - * ntfs_ih_reparent_end - - * - * Used if an empty index block to be deleted has END entry as the parent - * in the INDEX_ROOT which is not the only one there. - */ -static int ntfs_ih_reparent_end(ntfs_index_context *icx, INDEX_HEADER *ih, - INDEX_BLOCK *ib) -{ - INDEX_ENTRY *ie, *ie_prev; - - ntfs_log_trace("Entering.\n"); - - ie = ntfs_ie_get_by_pos(ih, ntfs_icx_parent_pos(icx)); - ie_prev = ntfs_ie_prev(ih, ie); - - ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(ie_prev)); - return ntfs_ih_takeout(icx, ih, ie_prev, ib); -} - -static int ntfs_index_rm_leaf(ntfs_index_context *icx) -{ - INDEX_BLOCK *ib = NULL; - INDEX_HEADER *parent_ih; - INDEX_ENTRY *ie; - int ret = STATUS_ERROR; - - ntfs_log_trace("pindex: %d\n", icx->pindex); - - if (ntfs_icx_parent_dec(icx)) - return STATUS_ERROR; - - if (ntfs_ibm_clear(icx, icx->parent_vcn[icx->pindex + 1])) - return STATUS_ERROR; - - if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) - parent_ih = &icx->ir->index; - else { - ib = ntfs_malloc(icx->block_size); - if (!ib) - return STATUS_ERROR; - - if (ntfs_ib_read(icx, ntfs_icx_parent_vcn(icx), ib)) - goto out; - - parent_ih = &ib->index; - } - - ie = ntfs_ie_get_by_pos(parent_ih, ntfs_icx_parent_pos(icx)); - if (!ntfs_ie_end(ie)) { - ret = ntfs_ih_takeout(icx, parent_ih, ie, ib); - goto out; - } - - if (ntfs_ih_zero_entry(parent_ih)) { - - if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) { - ntfs_ir_leafify(icx, parent_ih); - goto ok; - } - - ret = ntfs_index_rm_leaf(icx); - goto out; - } - - if (ntfs_ih_reparent_end(icx, parent_ih, ib)) - goto out; -ok: - ret = STATUS_OK; -out: - free(ib); - return ret; -} - -static int ntfs_index_rm_node(ntfs_index_context *icx) -{ - int entry_pos; - VCN vcn; - INDEX_BLOCK *ib = NULL; - INDEX_ENTRY *ie_succ, *ie, *entry = icx->entry; - INDEX_HEADER *ih; - u32 new_size; - int delta, ret = STATUS_ERROR; - - ntfs_log_trace("Entering.\n"); - - if (!icx->ia_na) { - icx->ia_na = ntfs_ia_open(icx, icx->ni); - if (!icx->ia_na) - return STATUS_ERROR; - } - - ib = ntfs_malloc(icx->block_size); - if (!ib) - return STATUS_ERROR; - - ie_succ = ntfs_ie_get_next(icx->entry); - entry_pos = icx->parent_pos[icx->pindex]++; -descend: - vcn = ntfs_ie_get_vcn(ie_succ); - if (ntfs_ib_read(icx, vcn, ib)) - goto out; - - ie_succ = ntfs_ie_get_first(&ib->index); - - if (ntfs_icx_parent_inc(icx)) - goto out; - - icx->parent_vcn[icx->pindex] = vcn; - icx->parent_pos[icx->pindex] = 0; - - if ((ib->index.flags & NODE_MASK) == INDEX_NODE) - goto descend; - - if (ntfs_ih_zero_entry(&ib->index)) { - errno = EOPNOTSUPP; - ntfs_log_perror("Failed to find any entry in an index block. " - "Please run chkdsk."); - goto out; - } - - ie = ntfs_ie_dup(ie_succ); - if (!ie) - goto out; - - if (ntfs_ie_add_vcn(&ie)) - goto out2; - - ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(icx->entry)); - - if (icx->is_in_root) - ih = &icx->ir->index; - else - ih = &icx->ib->index; - - delta = le16_to_cpu(ie->length) - le16_to_cpu(icx->entry->length); - new_size = le32_to_cpu(ih->index_length) + delta; - if (delta > 0) { - if (icx->is_in_root) { - if (ntfs_ir_truncate(icx, new_size)) { - errno = EOPNOTSUPP; - ntfs_log_perror("Denied to truncate INDEX_ROOT" - " during entry removal"); - goto out2; - } - ih = &icx->ir->index; - entry = ntfs_ie_get_by_pos(ih, entry_pos); - } else if (new_size > le32_to_cpu(ih->allocated_size)) { - errno = EOPNOTSUPP; - ntfs_log_perror("Denied to split INDEX_BLOCK during " - "entry removal"); - goto out2; - } - } - - ntfs_ie_delete(ih, entry); - ntfs_ie_insert(ih, ie, entry); - - if (icx->is_in_root) { - if (ntfs_ir_truncate(icx, new_size)) - goto out2; - ntfs_inode_mark_dirty(icx->actx->ntfs_ino); - } else - if (ntfs_icx_ib_write(icx)) - goto out2; - - ntfs_ie_delete(&ib->index, ie_succ); - - if (ntfs_ih_zero_entry(&ib->index)) { - if (ntfs_index_rm_leaf(icx)) - goto out2; - } else - if (ntfs_ib_write(icx, vcn, ib)) - goto out2; - - ret = STATUS_OK; -out2: - free(ie); -out: - free(ib); - return ret; -} - -/** - * ntfs_index_rm - remove entry from the index - * @icx: index context describing entry to delete - * - * Delete entry described by @icx from the index. 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 *icx) -{ - INDEX_HEADER *ih; - int err; - - ntfs_log_trace("Entering.\n"); - - if (!icx || (!icx->ib && !icx->ir) || ntfs_ie_end(icx->entry)) { - ntfs_log_error("Invalid arguments.\n"); - errno = EINVAL; - goto err_out; - } - if (icx->is_in_root) - ih = &icx->ir->index; - else - ih = &icx->ib->index; - - if (icx->entry->flags & INDEX_ENTRY_NODE) { - - if (ntfs_index_rm_node(icx)) - goto err_out; - - } else if (icx->is_in_root || !ntfs_ih_one_entry(ih)) { - - ntfs_ie_delete(ih, icx->entry); - - if (icx->is_in_root) { - err = ntfs_ir_truncate(icx, - le32_to_cpu(ih->index_length)); - if (err != STATUS_OK) - goto err_out; - } else - if (ntfs_icx_ib_write(icx)) - goto err_out; - } else { - if (ntfs_index_rm_leaf(icx)) - goto err_out; - } - - ntfs_index_ctx_reinit(icx); - ntfs_log_trace("Done.\n"); - return 0; -err_out: - err = errno; - ntfs_index_ctx_reinit(icx); - errno = err; - ntfs_log_trace("Failed.\n"); - return -1; -} - -/** - * 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)); - - if (!ntfs_ir_lookup(ni, name, attr->name_length, &ctx)) - return NULL; - - root = ntfs_malloc(sizeof(INDEX_ROOT)); - if (!root) - goto out; - - *root = *((INDEX_ROOT *)((u8 *)ctx->attr + - le16_to_cpu(ctx->attr->u.res.value_offset))); -out: - ntfs_attr_put_search_ctx(ctx); - return root; -} - diff --git a/usr/src/lib/libntfs/common/libntfs/inode.c b/usr/src/lib/libntfs/common/libntfs/inode.c deleted file mode 100644 index 9ea1d34524..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/inode.c +++ /dev/null @@ -1,1200 +0,0 @@ -/** - * inode.c - Inode handling code. Part of the Linux-NTFS project. - * - * Copyright (c) 2002-2005 Anton Altaparmakov - * Copyright (c) 2004-2007 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 <stdlib.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#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 ntfs_inode *__ntfs_inode_allocate(ntfs_volume *vol) -{ - ntfs_inode *ni; - - ni = (ntfs_inode*)calloc(1, sizeof(ntfs_inode)); - if (ni) { - ni->vol = vol; - INIT_LIST_HEAD(&ni->attr_cache); - } - 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 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_add_to_cache - do not use me! Only for internal library use. - */ -void __ntfs_inode_add_to_cache(ntfs_inode *ni) -{ - list_add_tail(&ni->list_entry, &ni->vol->inode_cache[ - ni->mft_no & NTFS_INODE_CACHE_SIZE_BITS]); - ni->nr_references = 1; -} - -/** - * 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; - struct list_head *pos; - - ntfs_log_trace("Entering for inode 0x%llx.\n", MREF(mref)); - if (!vol) { - errno = EINVAL; - return NULL; - } - /* Check cache, maybe this inode already opened? */ - list_for_each(pos, &vol->inode_cache[MREF(mref) & - NTFS_INODE_CACHE_SIZE_BITS]) { - ntfs_inode *tmp_ni; - - tmp_ni = list_entry(pos, ntfs_inode, list_entry); - if (tmp_ni->mft_no == MREF(mref)) { - ntfs_log_trace("Found this inode in cache, increment " - "reference count and return it.\n"); - tmp_ni->nr_references++; - return tmp_ni; - } - } - /* Search failed. Properly open inode. */ - 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->u.res.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 = ntfs_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->u.nonres.data_size); - if (ctx->attr->flags & - (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) - ni->allocated_size = sle64_to_cpu( - ctx->attr->u.nonres.compressed_size); - else - ni->allocated_size = sle64_to_cpu( - ctx->attr->u.nonres.allocated_size); - } else { - ni->data_size = le32_to_cpu(ctx->attr->u.res.value_length); - ni->allocated_size = (ni->data_size + 7) & ~7; - } - } - ntfs_attr_put_search_ctx(ctx); - __ntfs_inode_add_to_cache(ni); - 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. - * - * It is OK to pass NULL to this function, it is just noop in this case. - * - * 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); - - /* Decrement number of users. If there are left then just return. */ - if (ni->nr_extents != -1) { - ni->nr_references--; - if (ni->nr_references) { - ntfs_log_trace("There are %d more references left to " - "this inode.\n", - ni->nr_references); - return 0; - } else - ntfs_log_trace("There are no more references left to " - "this inode.\n"); - } - /* Check whether all attributes of this inode are closed. */ - if (!list_empty(&ni->attr_cache)) - ntfs_log_error("%s(): Not all attributes are closed. " - "We definitely have memory leak. " - "Continue anyway.\n", "ntfs_inode_close"); - /* 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->u.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->u.base_ni; - for (i = 0; i < base_ni->nr_extents; ++i) { - tmp_nis = base_ni->u.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->u.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! Continuing regardless.\n"); - } - /* Remove inode from the list of opened inodes. */ - if (ni->nr_extents != -1) - list_del(&ni->list_entry); - 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 leMFT_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->u.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->u.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**)ntfs_malloc(i); - if (!extent_nis) - goto err_out; - if (base_ni->nr_extents) { - memcpy(extent_nis, base_ni->u.extent_nis, - i - 4 * sizeof(ntfs_inode *)); - free(base_ni->u.extent_nis); - } - base_ni->u.extent_nis = extent_nis; - } - base_ni->u.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->u.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("Corrupted in-memory structure.\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, ale->mft_reference)) { - ntfs_log_trace("Couldn't attach extent " - "inode (attr type 0x%x " - "references to it).\n", - le32_to_cpu(ale->type)); - 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->u.res.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_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->u.res.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 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).\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)) { - /* 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"); - } - /* Write MFT record. */ - 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->u.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); - NTFS_ON_DEBUG(memset(al, 0, 0x40)); /* Valgrind. */ - 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("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); - NTFS_ON_DEBUG(memset(aln + al_allocated - 0x40, 0, - 0x40)); /* Valgrind. */ - 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->u.nonres.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, u.res.resident_end)) { - if (ntfs_inode_free_space(ni, - offsetof(ATTR_RECORD, u.res.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_put_search_ctx(ctx); - 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) { - ntfs_log_trace("Failed to get attribute search context.\n"); - 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_times - update selected time fields for ntfs inode - * @ni: ntfs inode for which update time fields - * @mask: select which time fields should be updated - * - * This function updates time fields to current time. Fields to update are - * selected using @mask (see enum @ntfs_time_update_flags for posssible values). - */ -void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) -{ - time_t now; - - if (!ni) { - ntfs_log_error("%s(): Invalid arguments.\n", "ntfs_inode_update_times"); - return; - } - if ((ni->mft_no < FILE_first_user && ni->mft_no != FILE_root) || - NVolReadOnly(ni->vol) || !mask) - return; - - now = time(NULL); - if (mask & NTFS_UPDATE_ATIME) - ni->last_access_time = now; - if (mask & NTFS_UPDATE_MTIME) - ni->last_data_change_time = now; - if (mask & NTFS_UPDATE_CTIME) - 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; -} diff --git a/usr/src/lib/libntfs/common/libntfs/lcnalloc.c b/usr/src/lib/libntfs/common/libntfs/lcnalloc.c deleted file mode 100644 index 7f42f8a564..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/lcnalloc.c +++ /dev/null @@ -1,858 +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 <stdlib.h> -#endif -#ifdef HAVE_STDIO_H -#include <stdio.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif - -#include "compat.h" -#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 = ntfs_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*)ntfs_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; - vol->nr_free_clusters--; - /* 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/usr/src/lib/libntfs/common/libntfs/logfile.c b/usr/src/lib/libntfs/common/libntfs/logfile.c deleted file mode 100644 index 054bd2f088..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/logfile.c +++ /dev/null @@ -1,769 +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 <stdlib.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif - -#include "compat.h" -#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 = ntfs_malloc(le32_to_cpu(rp->system_page_size)); - if (!trp) - 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 = ntfs_malloc(NTFS_BLOCK_SIZE); - if (!kaddr) - 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: - errno = err; - return -1; -} diff --git a/usr/src/lib/libntfs/common/libntfs/logging.c b/usr/src/lib/libntfs/common/libntfs/logging.c deleted file mode 100644 index eb447e493f..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/logging.c +++ /dev/null @@ -1,644 +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 <stdio.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif -#ifdef HAVE_STDARG_H -#include <stdarg.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_SYSLOG_H -#include <syslog.h> -#endif - -#include "compat.h" -#include "logging.h" - -#ifndef PATH_SEP -#define PATH_SEP '/' -#endif - -/* Colour prefixes and a suffix */ -#ifdef __sun -static const char *col_green = "\033[32m"; -static const char *col_cyan = "\033[36m"; -static const char *col_yellow = "\033[01;33m"; -static const char *col_red = "\033[01;31m"; -static const char *col_redinv = "\033[01;07;31m"; -static const char *col_end = "\033[0m"; -#else /* ! __sun */ -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"; -#endif /* __sun */ - -/** - * 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 in the library and tools. - */ -static struct ntfs_logging ntfs_log = { - .levels = 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_PROGRESS | - 0, - .flags = NTFS_LOG_FLAG_ONLYNAME, - .handler = ntfs_log_handler_null, -}; - - -/** - * 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; -} - - -#ifdef HAVE_SYSLOG_H -/** - * 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 syslog logging handler. Ignores colors and truncates output after 512 - * bytes. - * - * Returns: -1 Error occurred - * 0 Message wasn't logged - * num Number of output characters - */ -int ntfs_log_handler_syslog(const char *function, const char *file, int line, - u32 level, void *data __attribute__((unused)), - const char *format, va_list args) -{ - char buffer[512]; - int ret = 0, olderr = errno; - - if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && - (strchr(file, PATH_SEP))) /* Abbreviate the filename */ - file = strrchr(file, PATH_SEP) + 1; - - /* Prefix the output */ - if (ret < sizeof(buffer) && ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) - ret += snprintf(buffer + ret, sizeof(buffer) - ret, "%s", - ntfs_log_get_prefix(level)); - - /* Source filename */ - if (ret < sizeof(buffer) && ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) - ret += snprintf(buffer + ret, sizeof(buffer) - ret, "%s ", - file); - - /* Source line number */ - if (ret < sizeof(buffer) && ntfs_log.flags & NTFS_LOG_FLAG_LINE) - ret += snprintf(buffer + ret, sizeof(buffer) - ret, "(%d) ", - line); - - /* Source function */ - if (ret < sizeof(buffer) && ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) - || (level & NTFS_LOG_LEVEL_TRACE))) - ret += snprintf(buffer + ret, sizeof(buffer) - ret, "%s(): ", - function); - - /* Message itself */ - if (ret < sizeof(buffer)) - ret += vsnprintf(buffer + ret, sizeof(buffer) - ret, format, - args); - - /* Append errno */ - if (ret < sizeof(buffer) && level & NTFS_LOG_LEVEL_PERROR) - ret += snprintf(buffer + ret, sizeof(buffer) - ret, ": %s.\n", - strerror(olderr)); - - syslog(LOG_NOTICE, "%s", buffer); - - 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) -{ - 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 (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) - 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/usr/src/lib/libntfs/common/libntfs/mft.c b/usr/src/lib/libntfs/common/libntfs/mft.c deleted file mode 100644 index 49034707ba..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/mft.c +++ /dev/null @@ -1,1583 +0,0 @@ -/** - * mft.c - Mft record handling code. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2004 Anton Altaparmakov - * Copyright (c) 2005-2007 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 <stdlib.h> -#endif -#ifdef HAVE_STDIO_H -#include <stdio.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#include <time.h> - -#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 = ntfs_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_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*)ntfs_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. */ - *(le16*)((u8*)mrec + le16_to_cpu(mrec->usa_ofs)) = cpu_to_le16(1); - mrec->lsn = 0; - 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 = ntfs_calloc(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; -} - -static const char *es = " Leaving inconsistent metadata. Run chkdsk."; - -/** - * 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*)ntfs_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); -#ifdef DEBUG - 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; - } - vol->nr_free_clusters--; - /* 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->u.nonres.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->u.nonres.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->u.nonres.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->u.nonres.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->u.nonres.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->u.nonres.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->u.nonres.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->u.nonres.mapping_pairs_offset), - old_alen - le16_to_cpu(a->u.nonres.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->u.nonres.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->u.nonres.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; - } - vol->nr_free_mft_records += 64; /* 8 bytes x 8 bits each. */ - 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->u.nonres.initialized_size = cpu_to_sle64(old_initialized_size); - if (mftbmp_na->data_size != old_data_size) { - mftbmp_na->data_size = old_data_size; - a->u.nonres.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->u.nonres.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->u.nonres.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->u.nonres.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->u.nonres.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->u.nonres.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->u.nonres.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->u.nonres.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->u.nonres.mapping_pairs_offset), - old_alen - le16_to_cpu(a->u.nonres.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; - le16 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->u.nonres.initialized_size = cpu_to_sle64(mft_na->initialized_size); - a->u.nonres.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"); - /* Sync MFT to disk now in order to minimize data-loss. */ - if (ntfs_inode_sync(mft_na->ni)) { - ntfs_log_debug("mft sync after extension failed. rolling back."); - goto undo_data_init; - } -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*)ntfs_malloc(vol->mft_record_size); - if (!m) - 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 = *(le16*)((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 (seq_no) - m->sequence_number = seq_no; - if (usn && le16_to_cpu(usn) != 0xffff) - *(le16*)((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->u.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**)ntfs_malloc(i); - if (!extent_nis) { - err = errno; - free(m); - free(ni); - errno = err; - goto undo_mftbmp_alloc; - } - if (base_ni->u.extent_nis) { - memcpy(extent_nis, base_ni->u.extent_nis, - i - 4 * sizeof(ntfs_inode *)); - free(base_ni->u.extent_nis); - } - base_ni->u.extent_nis = extent_nis; - } - base_ni->u.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); - if (!base_ni) { - /* Update the default mft allocation position if it was used. */ - vol->mft_data_pos = bit + 1; - /* Add inode to cache. */ - __ntfs_inode_add_to_cache(ni); - } - /* 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; - le16 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; -} - -/** - * 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; - le16 *usnp; - - if (!mrec) { - errno = EINVAL; - return -1; - } - usnp = (le16 *)((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/usr/src/lib/libntfs/common/libntfs/misc.c b/usr/src/lib/libntfs/common/libntfs/misc.c deleted file mode 100644 index 26a51c13c9..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/misc.c +++ /dev/null @@ -1,64 +0,0 @@ -/** - * misc.c - Miscellaneous functions. Part of the Linux-NTFS project. - * - * Copyright (c) 2006 Szabolcs Szakacsits - * - * 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 <stdlib.h> -#endif - -#include "compat.h" -#include "support.h" -#include "logging.h" - -/** - * ntfs_calloc - A logging supported calloc(3) - * - * Return a pointer to the allocated memory or NULL if the request fails. - * Memory is initialized with zeros. - */ -void *ntfs_calloc(size_t size) -{ - void *p; - - p = calloc(1, size); - if (!p) - ntfs_log_perror("Failed to calloc %lld bytes", (long long)size); - return p; -} - -/** - * ntfs_malloc - A logging supported malloc(3) - * - * Return a pointer to the allocated memory or NULL if the request fails. - * Memory is uninitialized. - */ -void *ntfs_malloc(size_t size) -{ - void *p; - - p = malloc(size); - if (!p) - ntfs_log_perror("Failed to malloc %lld bytes", (long long)size); - return p; -} diff --git a/usr/src/lib/libntfs/common/libntfs/mst.c b/usr/src/lib/libntfs/common/libntfs/mst.c deleted file mode 100644 index 2b48e992c0..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/mst.c +++ /dev/null @@ -1,216 +0,0 @@ -/** - * mst.c - Multi sector fixup handling code. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2004 Anton Altaparmakov - * Copyright (c) 2007 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_ERRNO_H -#include <errno.h> -#endif - -#include "compat.h" -#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; - le16 *usa_pos, *data_pos, usnle; - - /* 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 = (le16*)((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; - usnle = cpu_to_le16(usn); - *usa_pos = usnle; - /* Position in data of first u16 that needs fixing up. */ - data_pos = (le16*)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 = usnle; - /* 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/usr/src/lib/libntfs/common/libntfs/runlist.c b/usr/src/lib/libntfs/common/libntfs/runlist.c deleted file mode 100644 index d70b643a88..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/runlist.c +++ /dev/null @@ -1,2154 +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) 2002-2006 Szabolcs Szakacsits - * Copyright (c) 2004-2007 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 <stdio.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#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 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 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 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 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 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 runlist_element *ntfs_rl_split(runlist_element *dst, - int dsize, runlist_element *src, int ssize, int loc) -{ - if (!dst || !src) { - ntfs_log_trace("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->u.nonres.lowest_vcn) < (VCN)0) { - errno = EINVAL; - return NULL; - } - /* Start at vcn = lowest_vcn and lcn 0. */ - vcn = sle64_to_cpu(attr->u.nonres.lowest_vcn); - lcn = 0; - /* Get start of the mapping pairs array. */ - buf = (const u8*)attr + le16_to_cpu(attr->u.nonres.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. */ - rlsize = 0x1000; - rl = ntfs_malloc(rlsize); - 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->u.nonres.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->u.nonres.lowest_vcn) { - VCN max_cluster; - - max_cluster = ((sle64_to_cpu(attr->u.nonres.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->u.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->u.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_rl_fill_zero - fill given region with zeroes - * @vol: ntfs volume to write to - * @rl: runlist specifying where to write zeroes to - * @pos: byte position within runlist @rl at which to begin the zeroing - * @count: number of bytes to fill with zeros - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -int ntfs_rl_fill_zero(const ntfs_volume *vol, const runlist *rl, s64 pos, - const s64 count) -{ - char *buf; - s64 written, size, end = pos + count; - int ret = 0; - - ntfs_log_trace("pos %lld, count %lld\n", (long long)pos, - (long long)count); - - if (!vol || !rl || pos < 0 || count < 0) { - errno = EINVAL; - return -1; - } - - buf = ntfs_calloc(NTFS_BUF_SIZE); - if (!buf) - return -1; - - while (pos < end) { - size = min(end - pos, NTFS_BUF_SIZE); - written = ntfs_rl_pwrite(vol, rl, pos, size, buf); - if (written <= 0) { - ntfs_log_perror("Failed to zero space"); - ret = -1; - break; - } - pos += written; - } - free(buf); - return ret; -} - -/** - * 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. - */ -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. - */ -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; - - if (!arl || !*arl) { - errno = EINVAL; - ntfs_log_perror("rl_truncate error: arl: %p *arl: %p", arl, *arl); - return -1; - } - - rl = *arl; - - if (start_vcn < rl->vcn) { - errno = EINVAL; - ntfs_log_perror("Start_vcn lies outside front of runlist"); - return -1; - } - - /* Find the starting vcn in the run list. */ - while (rl->length) { - if (start_vcn < rl[1].vcn) - break; - rl++; - } - - if (!rl->length) { - errno = EIO; - ntfs_log_trace("Truncating already truncated runlist?\n"); - return -1; - } - - /* 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; - rl->vcn = start_vcn; - rl->length = 0; - } - rl->lcn = (LCN)LCN_ENOENT; - return 0; -} - -/** - * ntfs_rl_sparse - check whether runlist have sparse regions or not. - * @rl: runlist to check - * - * This function just skips not mapped regions assuming they are not sparse, - * so you need to ensure that runlist is fully mapped if you want perform full - * 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_RL_NOT_MAPPED) - continue; - if (rlc->lcn != LCN_HOLE) { - ntfs_log_trace("Bad runlist.\n"); - errno = EIO; - 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 = ntfs_malloc(4096); - if (!result) - return NULL; - - 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 = ntfs_malloc(1024); - ATTR_RECORD *attr2 = ntfs_malloc(1024); - ATTR_RECORD *attr3 = ntfs_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/usr/src/lib/libntfs/common/libntfs/security.c b/usr/src/lib/libntfs/common/libntfs/security.c deleted file mode 100644 index ed02148179..0000000000 --- a/usr/src/lib/libntfs/common/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 <stdio.h> -#endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif - -#include "compat.h" -#include "types.h" -#include "layout.h" -#include "security.h" - -/* - * The zero GUID. - */ -static const GUID __zero_guid = { { 0, 0, 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 = ntfs_malloc(37); - if (!_guid_str) - return _guid_str; - } - res = snprintf(_guid_str, 37, "%02x%02x%02x%02x-%02x%02x-%02x%02x-" - "%02x%02x-%02x%02x%02x%02x%02x%02x", guid->raw[0], - guid->raw[1], guid->raw[2], guid->raw[3], guid->raw[4], - guid->raw[5], guid->raw[6], guid->raw[7], guid->raw[8], - guid->raw[9], guid->raw[10], guid->raw[11], - guid->raw[12], guid->raw[13], guid->raw[14], - guid->raw[15]); - 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.s.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 = ntfs_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.s.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/usr/src/lib/libntfs/common/libntfs/unistr.c b/usr/src/lib/libntfs/common/libntfs/unistr.c deleted file mode 100644 index d5fd2eeb99..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/unistr.c +++ /dev/null @@ -1,776 +0,0 @@ -/** - * unistr.c - Unicode string handling. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2006 Anton Altaparmakov - * Copyright (c) 2005-2007 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 <stdio.h> -#endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_WCHAR_H -#include <wchar.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif - -#include "compat.h" -#include "attrib.h" -#include "endians.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; - u16 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) -{ - u16 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) -{ - u16 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 = ntfs_malloc((len + 1) * sizeof(ntfschar)); - if (dst) { - memcpy(dst, s, len * sizeof(ntfschar)); - dst[len] = 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; - u16 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*)ntfs_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*)ntfs_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) - free(mbs); - 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*)ntfs_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] = 0; - if (*outs != ucs) - *outs = ucs; - return o; -err_out: - if (ucs != *outs) - free(ucs); - 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. - * - * The generated $UpCase table is the one used by Windows Vista. - * - * 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) -{ - /* - * "Start" is inclusive and "End" is exclusive, every value has the - * value of "Add" added to it. - */ - static int add[][3] = { /* Start, End, Add */ - {0x0061, 0x007b, -32}, {0x00e0, 0x00f7, -32}, {0x00f8, 0x00ff, -32}, - {0x0256, 0x0258, -205}, {0x028a, 0x028c, -217}, {0x037b, 0x037e, 130}, - {0x03ac, 0x03ad, -38}, {0x03ad, 0x03b0, -37}, {0x03b1, 0x03c2, -32}, - {0x03c2, 0x03c3, -31}, {0x03c3, 0x03cc, -32}, {0x03cc, 0x03cd, -64}, - {0x03cd, 0x03cf, -63}, {0x0430, 0x0450, -32}, {0x0450, 0x0460, -80}, - {0x0561, 0x0587, -48}, {0x1f00, 0x1f08, 8}, {0x1f10, 0x1f16, 8}, - {0x1f20, 0x1f28, 8}, {0x1f30, 0x1f38, 8}, {0x1f40, 0x1f46, 8}, - {0x1f51, 0x1f52, 8}, {0x1f53, 0x1f54, 8}, {0x1f55, 0x1f56, 8}, - {0x1f57, 0x1f58, 8}, {0x1f60, 0x1f68, 8}, {0x1f70, 0x1f72, 74}, - {0x1f72, 0x1f76, 86}, {0x1f76, 0x1f78, 100}, {0x1f78, 0x1f7a, 128}, - {0x1f7a, 0x1f7c, 112}, {0x1f7c, 0x1f7e, 126}, {0x1f80, 0x1f88, 8}, - {0x1f90, 0x1f98, 8}, {0x1fa0, 0x1fa8, 8}, {0x1fb0, 0x1fb2, 8}, - {0x1fb3, 0x1fb4, 9}, {0x1fcc, 0x1fcd, -9}, {0x1fd0, 0x1fd2, 8}, - {0x1fe0, 0x1fe2, 8}, {0x1fe5, 0x1fe6, 7}, {0x1ffc, 0x1ffd, -9}, - {0x2170, 0x2180, -16}, {0x24d0, 0x24ea, -26}, {0x2c30, 0x2c5f, -48}, - {0x2d00, 0x2d26, -7264}, {0xff41, 0xff5b, -32}, {0} - }; - /* - * "Start" is exclusive and "End" is inclusive, every second value is - * decremented by one. - */ - static int skip_dec[][2] = { /* Start, End */ - {0x0100, 0x012f}, {0x0132, 0x0137}, {0x0139, 0x0149}, {0x014a, 0x0178}, - {0x0179, 0x017e}, {0x01a0, 0x01a6}, {0x01b3, 0x01b7}, {0x01cd, 0x01dd}, - {0x01de, 0x01ef}, {0x01f4, 0x01f5}, {0x01f8, 0x01f9}, {0x01fa, 0x0220}, - {0x0222, 0x0234}, {0x023b, 0x023c}, {0x0241, 0x0242}, {0x0246, 0x024f}, - {0x03d8, 0x03ef}, {0x03f7, 0x03f8}, {0x03fa, 0x03fb}, {0x0460, 0x0481}, - {0x048a, 0x04bf}, {0x04c1, 0x04c4}, {0x04c5, 0x04c8}, {0x04c9, 0x04ce}, - {0x04ec, 0x04ed}, {0x04d0, 0x04eb}, {0x04ee, 0x04f5}, {0x04f6, 0x0513}, - {0x1e00, 0x1e95}, {0x1ea0, 0x1ef9}, {0x2183, 0x2184}, {0x2c60, 0x2c61}, - {0x2c67, 0x2c6c}, {0x2c75, 0x2c76}, {0x2c80, 0x2ce3}, {0} - }; - /* - * Set the Unicode character at offset "Offset" to "Value". Note, - * "Value" is host endian. - */ - static int set[][2] = { /* Offset, Value */ - {0x00ff, 0x0178}, {0x0180, 0x0243}, {0x0183, 0x0182}, {0x0185, 0x0184}, - {0x0188, 0x0187}, {0x018c, 0x018b}, {0x0192, 0x0191}, {0x0195, 0x01f6}, - {0x0199, 0x0198}, {0x019a, 0x023d}, {0x019e, 0x0220}, {0x01a8, 0x01a7}, - {0x01ad, 0x01ac}, {0x01b0, 0x01af}, {0x01b9, 0x01b8}, {0x01bd, 0x01bc}, - {0x01bf, 0x01f7}, {0x01c6, 0x01c4}, {0x01c9, 0x01c7}, {0x01cc, 0x01ca}, - {0x01dd, 0x018e}, {0x01f3, 0x01f1}, {0x023a, 0x2c65}, {0x023e, 0x2c66}, - {0x0253, 0x0181}, {0x0254, 0x0186}, {0x0259, 0x018f}, {0x025b, 0x0190}, - {0x0260, 0x0193}, {0x0263, 0x0194}, {0x0268, 0x0197}, {0x0269, 0x0196}, - {0x026b, 0x2c62}, {0x026f, 0x019c}, {0x0272, 0x019d}, {0x0275, 0x019f}, - {0x027d, 0x2c64}, {0x0280, 0x01a6}, {0x0283, 0x01a9}, {0x0288, 0x01ae}, - {0x0289, 0x0244}, {0x028c, 0x0245}, {0x0292, 0x01b7}, {0x03f2, 0x03f9}, - {0x04cf, 0x04c0}, {0x1d7d, 0x2c63}, {0x214e, 0x2132}, {0} - }; - unsigned i, r; - - memset(uc, 0, uc_len); - uc_len /= 2; - /* Start with a one-to-one mapping, i.e. no upcasing happens at all. */ - for (i = 0; i < uc_len; i++) - uc[i] = cpu_to_le16(i); - /* Adjust specified runs by the specified amount. */ - for (r = 0; add[r][0]; r++) - for (i = add[r][0]; i < add[r][1]; i++) - uc[i] = cpu_to_le16(le16_to_cpu(uc[i]) + add[r][2]); - /* Decrement every second value in specified runs. */ - for (r = 0; skip_dec[r][0]; r++) - for (i = skip_dec[r][0]; i < skip_dec[r][1]; - i += 2) - uc[i + 1] = cpu_to_le16(le16_to_cpu(uc[i + 1]) - 1); - /* Set specified characters to specified values. */ - for (r = 0; set[r][0]; r++) - uc[set[r][0]] = cpu_to_le16(set[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 > NTFS_MAX_NAME_LEN) { - 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/usr/src/lib/libntfs/common/libntfs/unix_io.c b/usr/src/lib/libntfs/common/libntfs/unix_io.c deleted file mode 100644 index 9299284c17..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/unix_io.c +++ /dev/null @@ -1,321 +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 <unistd.h> -#endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif -#ifdef HAVE_STDIO_H -#include <stdio.h> -#endif -#ifdef HAVE_SYS_TYPES_H -#include <sys/types.h> -#endif -#ifdef HAVE_SYS_STAT_H -#include <sys/stat.h> -#endif -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif -#ifdef HAVE_LINUX_FD_H -#include <linux/fd.h> -#endif - -#include "compat.h" -#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 = ntfs_malloc(sizeof(int)))) - return -1; - *(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 pread(DEV_FD(dev), buf, count, offset); -} - -/** - * 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 pwrite(DEV_FD(dev), buf, count, offset); -} - -/** - * 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/usr/src/lib/libntfs/common/libntfs/version.c b/usr/src/lib/libntfs/common/libntfs/version.c deleted file mode 100644 index 7882e7177b..0000000000 --- a/usr/src/lib/libntfs/common/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/usr/src/lib/libntfs/common/libntfs/volume.c b/usr/src/lib/libntfs/common/libntfs/volume.c deleted file mode 100644 index 2a783dddad..0000000000 --- a/usr/src/lib/libntfs/common/libntfs/volume.c +++ /dev/null @@ -1,1689 +0,0 @@ -/** - * volume.c - NTFS volume handling code. Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2006 Anton Altaparmakov - * Copyright (c) 2002-2006 Szabolcs Szakacsits - * Copyright (c) 2004-2005 Richard Russon - * Copyright (c) 2005-2007 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 <stdlib.h> -#endif -#ifdef HAVE_STDIO_H -#include <stdio.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif -#ifdef HAVE_SYS_STAT_H -#include <sys/stat.h> -#endif -#ifdef HAVE_LIMITS_H -#include <limits.h> -#endif - -#include "compat.h" -#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) -{ - ntfs_volume *vol; - int i; - - vol = calloc(1, sizeof(ntfs_volume)); - if (vol) { - for (i = 0; i < NTFS_INODE_CACHE_SIZE; i++) - INIT_LIST_HEAD(&vol->inode_cache[i]); - } - return vol; -} - -/** - * __ntfs_volume_release - Destroy an NTFS volume object - * @v: - * - * Description... - * - * Returns: - */ -static void __ntfs_volume_release(ntfs_volume *v) -{ - struct list_head *pos, *tmp; - int i; - - /* Sync and print error about not detached inodes. */ - for (i = 0; i < NTFS_INODE_CACHE_SIZE; i++) - list_for_each_safe(pos, tmp, &v->inode_cache[i]) { - ntfs_inode *ni = - list_entry(pos, ntfs_inode, list_entry); - - switch (ni->mft_no) { - case FILE_Volume: - case FILE_Bitmap: - case FILE_MFT: - case FILE_MFTMirr: - if (ni->nr_references == 1) - continue; - break; - } - - ntfs_log_error("%s(): Inode %llu still have %d " - "references.\n", "__ntfs_volume_release", - ni->mft_no, ni->nr_references); - ntfs_inode_sync(ni); - } - /* - * Clear the dirty bit if it was not set before we mounted and this is - * not a forensic mount. - */ - if (!NVolReadOnly(v) && !NVolWasDirty(v) && !NVolForensicMount(v)) { - v->flags &= ~VOLUME_IS_DIRTY; - (void)ntfs_volume_write_flags(v, v->flags); - } - 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->u.dev) { - struct ntfs_device *dev = v->u.dev; - - if (NDevDirty(dev)) - dev->d_ops->sync(dev); - if (dev->d_ops->close(dev)) - ntfs_log_perror("Failed to close the device"); - } - 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; - STANDARD_INFORMATION *std_info; - int eo; - - /* Manually setup an ntfs_inode. */ - vol->mft_ni = ntfs_inode_allocate(vol); - mb = (MFT_RECORD*)ntfs_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; - __ntfs_inode_add_to_cache(vol->mft_ni); - /* Can't use any of the higher level functions yet! */ - l = ntfs_mst_pread(vol->u.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_error("Incomplete multi sector transfer detected in " - "$MFT.\n"); - goto io_error_exit; - } - if (!ntfs_is_mft_record(mb->magic)) { - ntfs_log_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_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_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_error("$MFT/$ATTRIBUTE_LIST has invalid length.\n"); - goto io_error_exit; - } - vol->mft_ni->attr_list_size = l; - vol->mft_ni->attr_list = ntfs_malloc(l); - if (!vol->mft_ni->attr_list) - goto error_exit; - - l = ntfs_get_attribute_value(vol, ctx->attr, vol->mft_ni->attr_list); - if (!l) { - ntfs_log_error("Failed to get value of " - "$MFT/$ATTRIBUTE_LIST.\n"); - goto io_error_exit; - } - if (l != vol->mft_ni->attr_list_size) { - ntfs_log_error("Got unexpected amount of data when " - "reading $MFT/$ATTRIBUTE_LIST.\n"); - goto io_error_exit; - } -mft_has_no_attr_list: - /* Receive attributes from STANDARD_INFORMATION. */ - std_info = ntfs_attr_readall(vol->mft_ni, AT_STANDARD_INFORMATION, - AT_UNNAMED, 0, NULL); - vol->mft_ni->flags = std_info->file_attributes; - free(std_info); - - /* 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_error("$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_error("$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->u.nonres.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->u.nonres.lowest_vcn)) { - ntfs_log_error("$MFT has corrupt attribute list " - "attribute. Run chkdsk.\n"); - goto io_error_exit; - } - } - if (!a) { - ntfs_log_error("$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_error("Failed to load the complete runlist for " - "$MFT/$DATA. Bug or corrupt $MFT. " - "Run chkdsk.\n 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; - } - ntfs_log_error("%s(): Failed.\n", "ntfs_mft_load"); - 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 err; - - 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; - } - /* Check $MFTMirr runlist. */ - if (vol->mftmirr_na->rl[0].lcn != vol->mftmirr_lcn || - vol->mftmirr_na->rl[0].length < (vol->mftmirr_size * - vol->mft_record_size + vol->cluster_size - 1) / - vol->cluster_size) { - ntfs_log_error("$MFTMirr location mismatch or first 4 records " - "are fragmented. Run chkdsk.\n"); - errno = EIO; - goto error_exit; - - } - return 0; -error_exit: - err = 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 = err; - 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, - ntfs_mount_flags flags) -{ - LCN mft_zone_size, mft_lcn; - s64 br; - ntfs_volume *vol; - NTFS_BOOT_SECTOR *bs; - int eo; -#ifdef DEBUG - 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; - } - - if (!(bs = (NTFS_BOOT_SECTOR *)ntfs_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*)ntfs_malloc(vol->upcase_len * - sizeof(ntfschar)); - if (!vol->upcase) - goto error_exit; - ntfs_upcase_table_build(vol->upcase, - vol->upcase_len * sizeof(ntfschar)); - if (flags & NTFS_MNT_RDONLY) - NVolSetReadOnly(vol); - if (flags & NTFS_MNT_CASE_SENSITIVE) - NVolSetCaseSensitive(vol); - if (flags & NTFS_MNT_INTERIX) - NVolSetInterix(vol); - ntfs_log_debug("Reading bootsector... "); - if (dev->d_ops->open(dev, NVolReadOnly(vol) ? O_RDONLY : - ((flags & NTFS_MNT_NOT_EXCLUSIVE) ? O_RDWR : - (O_RDWR | O_EXCL)))) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Error opening partition device"); - goto error_exit; - } - /* Attach the device to the volume. */ - vol->u.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->u.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 bytes_read, ret = -1; - char *buf = NULL; - - ni = ntfs_hiberfile_open(vol); - if (!ni) { - if (errno == ENOENT) - return 0; - return -1; - } - - buf = ntfs_malloc(NTFS_HIBERFILE_HEADER_SIZE); - if (!buf) - 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; - } - ret = 0; -out: - if (na) - ntfs_attr_close(na); - free(buf); - ntfs_inode_close(ni); - return ret; -} - -/** - * ntfs_volume_get_nr_free_mft_records - calculate number of free MFT records - * vol: ntfs volume for which perform calculations. - * - * This function initializes @vol->nr_free_mft_records. @vol->mftbmp_na should - * be already opened upon call to this function. - * - * Return 0 on success. On error return -1 with errno set appropriately and - * @vol->nr_free_mft_records is not touched in this case. - */ -static int ntfs_volume_get_nr_free_mft_records(ntfs_volume *vol) -{ - long nr_free = vol->mft_na->data_size >> vol->mft_record_size_bits; - s64 br, total = 0; - u8 *buf; - - buf = ntfs_malloc(vol->cluster_size); - if (!buf) - return -1; - while (1) { - int i, j; - - br = ntfs_attr_pread(vol->mftbmp_na, total, - vol->cluster_size, buf); - if (br <= 0) - break; - total += br; - for (i = 0; i < br; i++) - for (j = 0; j < 8; j++) - if ((buf[i] >> j) & 1) - nr_free--; - } - free(buf); - if (!total || br < 0) { - ntfs_log_error("pread: %s\n", strerror(errno)); - return -1; - } - vol->nr_free_mft_records = nr_free; - return 0; -} - -/** - * ntfs_volume_get_nr_free_clusters - calculate number of free clusters - * vol: ntfs volume for which perform calculations. - * - * This function initializes @vol->nr_free_clusters. @vol->lcnbmp_na should be - * already opened upon call to this function. - * - * Return 0 on success. On error return -1 with errno set appropriately and - * @vol->nr_free_clusters is not touched in this case. - */ -static long ntfs_volume_get_nr_free_clusters(ntfs_volume *vol) -{ - long nr_free = vol->nr_clusters; - s64 br, total = 0; - u8 *buf; - - buf = ntfs_malloc(vol->cluster_size); - if (!buf) - return -1; - while (1) { - int i, j; - - br = ntfs_attr_pread(vol->lcnbmp_na, total, - vol->cluster_size, buf); - if (br <= 0) - break; - total += br; - for (i = 0; i < br; i++) - for (j = 0; j < 8; j++) - if ((buf[i] >> j) & 1) - nr_free--; - } - free(buf); - if (!total || br < 0) { - ntfs_log_error("pread: %s\n", strerror(errno)); - return -1; - } - vol->nr_free_clusters = nr_free; - return 0; -} - -/** - * 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. Some flags are similar to flags used - * as for the mount system call (man 2 mount). Currently the following flags - * are implemented: - * NTFS_MNT_RDONLY - mount volume read-only - * NTFS_MNT_CASE_SENSITIVE - treat filenames as case sensitive even if - * they are not in POSIX namespace - * NTFS_MNT_NOT_EXCLUSIVE - (unix only) do not open volume exclusively - * NTFS_MNT_FORENSIC - mount for forensic purposes, i.e. do not do - * any writing at all during the mount, i.e. no - * journal emptying, no dirty bit setting, etc. - * NTFS_MNT_INTERIX - make libntfs recognize special Interix files - * - * 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, ntfs_mount_flags flags) -{ - s64 l; -#ifdef DEBUG - 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; - } - /* Record whether this is a forensic mount. */ - if (flags & NTFS_MNT_FORENSIC) - NVolSetForensicMount(vol); - /* Load data from $MFT and $MFTMirr and compare the contents. */ - m = (u8*)ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits); - m2 = (u8*)ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits); - if (!m || !m2) - 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_record(mrec->magic)) { - 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_record(mrec->magic)) { - 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_record(mrec2->magic)) { - 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_record(mrec2->magic)) { - 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*)ntfs_malloc(na->data_size); - if (!vol->upcase) { - ntfs_log_debug(FAILED); - 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->u.res.value_offset) + (char*)a); - /* Sanity checks. */ - if ((char*)vinf + le32_to_cpu(a->u.res.value_length) > (char*)ctx->mrec + - le32_to_cpu(ctx->mrec->bytes_in_use) || - le16_to_cpu(a->u.res.value_offset) + le32_to_cpu( - a->u.res.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; - /* Record whether the volume was dirty or not. */ - if (vol->flags & VOLUME_IS_DIRTY) - NVolSetWasDirty(vol); - /* - * 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 = ntfs_malloc(1); - if (!vol->vol_name) { - ntfs_log_debug(FAILED); - 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->u.res.value_offset) + (char*)a); - u = le32_to_cpu(a->u.res.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 = ntfs_malloc(u + 1); - if (!vol->vol_name) { - ntfs_log_debug(FAILED); - goto error_exit; - } - for (j = 0; j < (s32)u; j++) { - u16 uc = le16_to_cpu(vname[j]); - if (uc > 0xff) - uc = (u16)'_'; - 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*)ntfs_malloc(na->data_size); - if (!vol->attrdef) { - ntfs_log_debug(FAILED); - 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"); - /* Initialize number of free clusters and MFT records. */ - if (ntfs_volume_get_nr_free_mft_records(vol)) { - ntfs_log_perror("Failed to calculate number of free MFTs"); - goto error_exit; - } - if (ntfs_volume_get_nr_free_clusters(vol)) { - ntfs_log_perror("Failed to calculate number of free clusters"); - goto error_exit; - } - /* - * Check for dirty logfile and hibernated Windows. - * We care only about read-write mounts. - * - * If all is ok, reset the logfile and set the dirty bit on the volume. - * - * But do not do that if this is a FORENSIC mount. - */ - if (!(flags & NTFS_MNT_RDONLY)) { - if (ntfs_volume_check_hiberfile(vol) < 0) - goto error_exit; - if (ntfs_volume_check_logfile(vol) < 0) { - if (errno != EOPNOTSUPP || !(flags & NTFS_MNT_FORCE)) - goto error_exit; - ntfs_log_warning("WARNING: $LogFile is not clean, " - "forced to continue.\n"); - NVolSetWasDirty(vol); /* Leave volume dirty since we - empted logfile. */ - } - if (!NVolForensicMount(vol)) { - if (ntfs_logfile_reset(vol) < 0) - goto error_exit; - if (!(vol->flags & VOLUME_IS_DIRTY)) { - vol->flags |= VOLUME_IS_DIRTY; - if (ntfs_volume_write_flags(vol, vol->flags) < - 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. See ntfs_device_mount comment for - * description. - * - * 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)), - ntfs_mount_flags 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->u.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 = ntfs_malloc(PATH_MAX + 1); - if (!real_file) - return -1; - real_fsname = ntfs_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 Vista. - * - * 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 le16 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_error("Attribute $VOLUME_INFORMATION was not found " - "in $Volume!\n"); - goto err_out; - } - a = ctx->attr; - /* Sanity check. */ - if (a->non_resident) { - ntfs_log_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->u.res.value_offset) + (char*)a); - /* Sanity checks. */ - if ((char*)c + le32_to_cpu(a->u.res.value_length) > (char*)ctx->mrec + - le32_to_cpu(ctx->mrec->bytes_in_use) || - le16_to_cpu(a->u.res.value_offset) + - le32_to_cpu(a->u.res.value_length) > le32_to_cpu(a->length)) { - ntfs_log_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); - if (ret) - ntfs_log_error("%s(): Failed.\n", "ntfs_volume_write_flags"); - return ret; -} - diff --git a/usr/src/lib/libntfs/common/mapfile-vers b/usr/src/lib/libntfs/common/mapfile-vers deleted file mode 100644 index f0e4c9f3d4..0000000000 --- a/usr/src/lib/libntfs/common/mapfile-vers +++ /dev/null @@ -1,134 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# -# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. -# - -# -# MAPFILE HEADER START -# -# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. -# Object versioning must comply with the rules detailed in -# -# usr/src/lib/README.mapfiles -# -# You should not be making modifications here until you've read the most current -# copy of that file. If you need help, contact a gatekeeper for guidance. -# -# MAPFILE HEADER END -# - -$mapfile_version 2 - -SYMBOL_VERSION SUNW_10.1 { - global: - AT_UNNAMED; - NTFS_INDEX_I30; - NTFS_INDEX_O; - NTFS_INDEX_Q; - NTFS_INDEX_R; - NTFS_INDEX_SDH; - NTFS_INDEX_SII; - ntfs_attr_add; - ntfs_attr_close; - ntfs_attr_find_in_attrdef; - ntfs_attr_get_search_ctx; - ntfs_attr_lookup; - ntfs_attr_mst_pread; - ntfs_attr_open; - ntfs_attr_pread; - ntfs_attr_put_search_ctx; - ntfs_attr_pwrite; - ntfs_attr_readall; - __ntfs_attr_truncate; - ntfs_boot_sector_is_ntfs; - ntfs_calloc; - ntfs_check_if_mounted; - ntfs_cluster_read; - ntfs_create; - ntfs_device_alloc; - ntfs_device_block_size_set; - ntfs_device_free; - ntfs_device_heads_get; - ntfs_device_partition_start_sector_get; - ntfs_device_sector_size_get; - ntfs_device_sectors_per_track_get; - ntfs_device_size_get; - ntfs_device_unix_io_ops; - ntfs_file_record_read; - ntfs_file_record_read; - ntfs_file_values_compare; - ntfs_get_attribute_value; - ntfs_get_attribute_value_length; - ntfs_get_size_for_mapping_pairs; - ntfs_guid_is_zero; - ntfs_guid_to_mbs; - ntfs_ie_get_vcn; - ntfs_index_root_get; - ntfs_inode_badclus_bad; - ntfs_inode_close; - ntfs_inode_open; - ntfs_inode_sync; - ntfs_libntfs_version; - ntfs_log_clear_levels; - ntfs_logfile_reset; - ntfs_log_get_levels; - ntfs_log_handler_outerr; - ntfs_log_handler_stderr; - ntfs_log_parse_option; - ntfs_log_redirect; - ntfs_log_set_handler; - ntfs_log_set_levels; - ntfs_malloc; - ntfs_mapping_pairs_build; - ntfs_mapping_pairs_decompress; - ntfs_mbstoucs; - ntfs_mft_record_layout; - ntfs_mft_records_write; - ntfs_mft_usn_dec; - ntfs_mount; - ntfs_mst_post_read_fixup; - ntfs_mst_post_write_fixup; - ntfs_mst_pre_write_fixup; - ntfs_mst_pwrite; - ntfs_names_are_equal; - ntfs_names_collate; - ntfs_pathname_to_inode; - ntfs_readdir; - ntfs_resident_attr_value_resize; - ntfs_rl_pread; - ntfs_rl_pwrite; - ntfs_rl_truncate; - ntfs_rl_vcn_to_lcn; - ntfs_sid_to_mbs; - ntfs_str2ucs; - ntfs_ucsfree; - ntfs_ucsnlen; - ntfs_ucstombs; - ntfs_umount; - ntfs_upcase_table_build; - ntfs_version_is_supported; - ntfs_volume_alloc; - ntfs_volume_startup; - ntfs_volume_write_flags; - local: - *; -}; |