Date: Mon, 14 May 2012 14:04:02 +0000 From: vbotton@FreeBSD.org To: svn-soc-all@FreeBSD.org Subject: socsvn commit: r235762 - soc2012/vbotton/ntfs_apple Message-ID: <20120514140402.9FDF01065670@hub.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: vbotton Date: Mon May 14 14:04:01 2012 New Revision: 235762 URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=235762 Log: Some works towards portability, essentially endianness related functions Added: soc2012/vbotton/ntfs_apple/ soc2012/vbotton/ntfs_apple/ntfs.h soc2012/vbotton/ntfs_apple/ntfs_attr.c soc2012/vbotton/ntfs_apple/ntfs_attr.h soc2012/vbotton/ntfs_apple/ntfs_attr_list.c soc2012/vbotton/ntfs_apple/ntfs_attr_list.h soc2012/vbotton/ntfs_apple/ntfs_bitmap.c soc2012/vbotton/ntfs_apple/ntfs_bitmap.h soc2012/vbotton/ntfs_apple/ntfs_collate.c soc2012/vbotton/ntfs_apple/ntfs_collate.h soc2012/vbotton/ntfs_apple/ntfs_compress.c soc2012/vbotton/ntfs_apple/ntfs_compress.h soc2012/vbotton/ntfs_apple/ntfs_debug.c soc2012/vbotton/ntfs_apple/ntfs_debug.h soc2012/vbotton/ntfs_apple/ntfs_dir.c soc2012/vbotton/ntfs_apple/ntfs_dir.h soc2012/vbotton/ntfs_apple/ntfs_endian.h soc2012/vbotton/ntfs_apple/ntfs_hash.c soc2012/vbotton/ntfs_apple/ntfs_hash.h soc2012/vbotton/ntfs_apple/ntfs_index.c soc2012/vbotton/ntfs_apple/ntfs_index.h soc2012/vbotton/ntfs_apple/ntfs_inode.c soc2012/vbotton/ntfs_apple/ntfs_inode.h soc2012/vbotton/ntfs_apple/ntfs_layout.h soc2012/vbotton/ntfs_apple/ntfs_lcnalloc.c soc2012/vbotton/ntfs_apple/ntfs_lcnalloc.h soc2012/vbotton/ntfs_apple/ntfs_logfile.c soc2012/vbotton/ntfs_apple/ntfs_logfile.h soc2012/vbotton/ntfs_apple/ntfs_mft.c soc2012/vbotton/ntfs_apple/ntfs_mft.h soc2012/vbotton/ntfs_apple/ntfs_mst.c soc2012/vbotton/ntfs_apple/ntfs_mst.h soc2012/vbotton/ntfs_apple/ntfs_page.c soc2012/vbotton/ntfs_apple/ntfs_page.h soc2012/vbotton/ntfs_apple/ntfs_quota.c soc2012/vbotton/ntfs_apple/ntfs_quota.h soc2012/vbotton/ntfs_apple/ntfs_runlist.c soc2012/vbotton/ntfs_apple/ntfs_runlist.h soc2012/vbotton/ntfs_apple/ntfs_secure.c soc2012/vbotton/ntfs_apple/ntfs_secure.h soc2012/vbotton/ntfs_apple/ntfs_sfm.c soc2012/vbotton/ntfs_apple/ntfs_sfm.h soc2012/vbotton/ntfs_apple/ntfs_time.h soc2012/vbotton/ntfs_apple/ntfs_types.h soc2012/vbotton/ntfs_apple/ntfs_unistr.c soc2012/vbotton/ntfs_apple/ntfs_unistr.h soc2012/vbotton/ntfs_apple/ntfs_usnjrnl.c soc2012/vbotton/ntfs_apple/ntfs_usnjrnl.h soc2012/vbotton/ntfs_apple/ntfs_vfsops.c soc2012/vbotton/ntfs_apple/ntfs_vnops.c soc2012/vbotton/ntfs_apple/ntfs_vnops.h soc2012/vbotton/ntfs_apple/ntfs_volume.h Added: soc2012/vbotton/ntfs_apple/ntfs.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ soc2012/vbotton/ntfs_apple/ntfs.h Mon May 14 14:04:01 2012 (r235762) @@ -0,0 +1,167 @@ +/* + * ntfs.h - Some generic defines for the NTFS kernel driver. + * + * Copyright (c) 2006-2008 Anton Altaparmakov. All Rights Reserved. + * Portions Copyright (c) 2006-2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ALTERNATIVELY, provided that this notice and licensing terms are retained in + * full, this file may be redistributed and/or modified under the terms of the + * GNU General Public License (GPL) Version 2, in which case the provisions of + * that version of the GPL will apply to you instead of the license terms + * above. You can obtain a copy of the GPL Version 2 at + * http://developer.apple.com/opensource/licenses/gpl-2.txt. + */ + +#ifndef _OSX_NTFS_H +#define _OSX_NTFS_H + +#ifdef KERNEL + +#include <sys/mount.h> +#include <kern/locks.h> + +/* The email address of the NTFS developers. */ +__private_extern__ const char ntfs_dev_email[]; +__private_extern__ const char ntfs_please_email[]; + +/* + * Lock group and lock attribute for de-/initialization of locks (defined + * in ntfs_vfsops.c). + */ +__private_extern__ lck_grp_t *ntfs_lock_grp; +__private_extern__ lck_attr_t *ntfs_lock_attr; + +/* + * A tag for allocation and freeing of memory (defined in ntfs_vfsops.c). + */ +__private_extern__ OSMallocTag ntfs_malloc_tag; + +#include "ntfs_volume.h" + +/** + * NTFS_MP - return the NTFS volume given a vfs mount + * @mp: VFS mount + * + * NTFS_MP() returns the NTFS volume associated with the VFS mount @mp. + */ +static inline ntfs_volume *NTFS_MP(mount_t mp) +{ + return (ntfs_volume*)vfs_fsprivate(mp); +} + +__private_extern__ void ntfs_do_postponed_release(ntfs_volume *vol); + +#endif /* KERNEL */ + +#include "ntfs_endian.h" +#include "ntfs_types.h" + +/* Some useful constants to do with NTFS. */ +enum { + NTFS_BLOCK_SIZE = 512, + NTFS_BLOCK_SIZE_SHIFT = 9, + NTFS_MAX_NAME_LEN = 255, + NTFS_MAX_ATTR_NAME_LEN = 255, + NTFS_MAX_SECTOR_SIZE = 4096, /* 4kiB */ + NTFS_MAX_CLUSTER_SIZE = 64 * 1024, /* 64kiB */ + NTFS_ALLOC_BLOCK = 1024, + NTFS_MAX_HARD_LINKS = 65535, /* 2^16 - 1 */ + NTFS_MAX_ATTR_LIST_SIZE = 256 * 1024, /* 256kiB, corresponding to the + VACB_MAPPING_GRANULARITY on + Windows. */ + NTFS_COMPRESSION_UNIT = 4, +}; + +/* + * The maximum attribute size on NTFS is 2^63 - 1 bytes as it is stored in a + * signed 64 bit type (s64). + */ +#define NTFS_MAX_ATTRIBUTE_SIZE 0x7fffffffffffffffULL + +/* + * The maximum number of MFT records allowed on NTFS is 2^32 as described in + * various documentation to be found on the Microsoft web site. This is an + * imposed limit rather than an inherent NTFS format limit. + */ +#define NTFS_MAX_NR_MFT_RECORDS 0x100000000ULL + +// TODO: Constants so ntfs_vfsops.c compiles for now... +enum { + /* One of these must be present, default is ON_ERRORS_CONTINUE. */ + ON_ERRORS_PANIC = 0x01, + ON_ERRORS_REMOUNT_RO = 0x02, + ON_ERRORS_CONTINUE = 0x04, + /* Optional, can be combined with any of the above. */ + ON_ERRORS_RECOVER = 0x10, +}; + +/* + * The NTFS mount options header passed in from user space. + */ +typedef struct { +#ifndef KERNEL + char *fspec; /* Path of device to mount, consumed by mount(2). */ +#endif /* !KERNEL */ + u8 major_ver; /* The major version of the mount options structure. */ + u8 minor_ver; /* The minor version of the mount options structure. */ +} __attribute__((__packed__)) ntfs_mount_options_header; + +/* + * The NTFS mount options passed in from user space. This follows the + * ntfs_mount_options_header aligned to an eight byte boundary. + * + * This is major version 0, minor version 0, which does not have any options, + * i.e. is empty. + */ +typedef struct { + /* Mount options version 0.0 does not have any ntfs options. */ +} __attribute__((__packed__)) ntfs_mount_options_0_0; + +/* + * The currently defined flags for the ntfs mount options structure. + */ +enum { + /* Below flag(s) appeared in mount options version 1.0. */ + NTFS_MNT_OPT_CASE_SENSITIVE = htole32(0x00000001), + /* Below flag(s) appeared in mount options version x.y. */ + // TODO: Add NTFS specific mount options flags here. +}; + +typedef le32 NTFS_MNT_OPTS; + +/* + * The NTFS mount options passed in from user space. This follows the + * ntfs_mount_options_header aligned to an eight byte boundary. + * + * This is major version 1, minor version 0, which has only one option, a + * little endian, 32-bit flags option. + */ +typedef struct { + NTFS_MNT_OPTS flags; + // TODO: Add NTFS specific mount options here. +} __attribute__((__packed__)) ntfs_mount_options_1_0; + +#endif /* !_OSX_NTFS_H */ Added: soc2012/vbotton/ntfs_apple/ntfs_attr.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ soc2012/vbotton/ntfs_apple/ntfs_attr.c Mon May 14 14:04:01 2012 (r235762) @@ -0,0 +1,9096 @@ +/* + * ntfs_attr.c - NTFS kernel attribute operations. + * + * Copyright (c) 2006-2011 Anton Altaparmakov. All Rights Reserved. + * Portions Copyright (c) 2006-2011 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ALTERNATIVELY, provided that this notice and licensing terms are retained in + * full, this file may be redistributed and/or modified under the terms of the + * GNU General Public License (GPL) Version 2, in which case the provisions of + * that version of the GPL will apply to you instead of the license terms + * above. You can obtain a copy of the GPL Version 2 at + * http://developer.apple.com/opensource/licenses/gpl-2.txt. + */ + +#include <sys/errno.h> +#include <sys/stat.h> +#include <sys/ucred.h> +/*#include <sys/ubc.h>*/ + +#include <string.h> + +/*#include <libkern/libkern.h> +#include <libkern/OSMalloc.h>*/ + +//#include <kern/debug.h> +//#include <kern/sched_prim.h> + +#include "ntfs.h" +#include "ntfs_attr.h" +#include "ntfs_attr_list.h" +#include "ntfs_debug.h" +#include "ntfs_dir.h" +#include "ntfs_endian.h" +#include "ntfs_index.h" +#include "ntfs_inode.h" +#include "ntfs_layout.h" +#include "ntfs_lcnalloc.h" +#include "ntfs_mft.h" +#include "ntfs_page.h" +#include "ntfs_runlist.h" +#include "ntfs_time.h" +#include "ntfs_types.h" +#include "ntfs_unistr.h" + +ntfschar AT_UNNAMED[1] = { 0 }; + +/** + * ntfs_attr_map_runlist - map the whole runlist of an ntfs inode + * @ni: ntfs inode for which to map the whole runlist + * + * Map the whole runlist of the ntfs inode @ni. + * + * Return 0 on success and errno on error. + * + * Note this function requires the runlist not to be mapped yet at all. This + * limitation is ok because we only use this function at mount time to map the + * runlist of some system files thus we are guaranteed that they will not have + * any runlist fragments mapped yet. + * + * Note the runlist can be NULL after this function returns if the attribute + * has zero allocated size, i.e. there simply is no runlist. + */ +errno_t ntfs_attr_map_runlist(ntfs_inode *ni) +{ + VCN vcn, end_vcn; + ntfs_inode *base_ni; + MFT_RECORD *m; + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + errno_t err = 0; + + ntfs_debug("Entering for mft_no 0x%llx, type 0x%x.", + (unsigned long long)ni->mft_no, + (unsigned)le32_to_cpu(ni->type)); + /* If the attribute is resident there is nothing to do. */ + if (!NInoNonResident(ni)) { + ntfs_debug("Done (resident, nothing to do)."); + return 0; + } + lck_rw_lock_exclusive(&ni->rl.lock); + /* Verify that the runlist is not mapped yet. */ + if (ni->rl.alloc && ni->rl.elements) + panic("%s(): ni->rl.alloc && ni->rl.elements\n", __FUNCTION__); + base_ni = ni; + if (NInoAttr(ni)) + base_ni = ni->base_ni; + err = ntfs_mft_record_map(base_ni, &m); + if (err) + goto err; + ctx = ntfs_attr_search_ctx_get(base_ni, m); + if (!ctx) { + err = ENOMEM; + goto unm_err; + } + vcn = 0; + end_vcn = ni->allocated_size >> ni->vol->cluster_size_shift; + do { + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, vcn, + NULL, 0, ctx); + if (err) { + if (err == ENOENT) + err = EIO; + break; + } + a = ctx->a; + if (!a->non_resident) { +corrupt_err: + ntfs_error(ni->vol->mp, "Inode 0x%llx contains corrupt " + "attribute extent, run chkdsk.", + (unsigned long long)base_ni->mft_no); + NVolSetErrors(ni->vol); + err = EIO; + break; + } + /* + * If we are in the first attribute extent, verify the cached + * allocated size is correct. + */ + if (!a->lowest_vcn) + if (sle64_to_cpu(a->allocated_size) != + ni->allocated_size) + panic("%s(): sle64_to_cpu(a->allocated_size) " + "!= ni->allocated_size\n", + __FUNCTION__); + /* + * Sanity check the lowest_vcn of the attribute is equal to the + * vcn we looked up and that the highest_vcn of the attribute + * is above the current vcn. + */ + if (sle64_to_cpu(a->lowest_vcn) != vcn || (vcn && + sle64_to_cpu(a->highest_vcn) < vcn)) + goto corrupt_err; + /* Determine the next vcn. */ + vcn = sle64_to_cpu(a->highest_vcn) + 1; + /* + * Finally, map the runlist fragment contained in this + * attribute extent. + */ + err = ntfs_mapping_pairs_decompress(ni->vol, a, &ni->rl); + } while (!err && vcn < end_vcn); +unm_err: + ntfs_attr_search_ctx_put(ctx); + ntfs_mft_record_unmap(base_ni); +err: + lck_rw_unlock_exclusive(&ni->rl.lock); + if (!err) + ntfs_debug("Done."); + else + ntfs_error(ni->vol->mp, "Failed (error %d).", (int)err); + return err; +} + +/** + * ntfs_map_runlist_nolock - map (a part of) a runlist of an ntfs inode + * @ni: ntfs inode for which to map (part of) a runlist + * @vcn: map runlist part containing this vcn + * @ctx: active attribute search context if present or NULL if not + * + * Map the part of a runlist containing the @vcn of the ntfs inode @ni. + * + * If @ctx is specified, it is an active search context of @ni and its base mft + * record. This is needed when ntfs_map_runlist_nolock() encounters unmapped + * runlist fragments and allows their mapping. If you do not have the mft + * record mapped, you can specify @ctx as NULL and ntfs_map_runlist_nolock() + * will perform the necessary mapping and unmapping. + * + * Note, ntfs_map_runlist_nolock() saves the state of @ctx on entry and + * restores it before returning. Thus, @ctx will be left pointing to the same + * attribute on return as on entry. However, the actual pointers in @ctx may + * point to different memory locations on return, so you must remember to reset + * any cached pointers from the @ctx, i.e. after the call to + * ntfs_map_runlist_nolock(), you will probably want to do: + * m = ctx->m; + * a = ctx->a; + * Assuming you cache ctx->a in a variable @a of type ATTR_RECORD * and that + * you cache ctx->m in a variable @m of type MFT_RECORD *. + * + * Return 0 on success and errno on error. There is one special error code + * which is not an error as such. This is ENOENT. It means that @vcn is out + * of bounds of the runlist. + * + * Note the runlist can be NULL after this function returns if @vcn is zero and + * the attribute has zero allocated size, i.e. there simply is no runlist. + * + * WARNING: If @ctx is supplied, regardless of whether success or failure is + * returned, you need to check @ctx->is_error and if 1 the @ctx is no + * longer valid, i.e. you need to either call + * ntfs_attr_search_ctx_reinit() or ntfs_attr_search_ctx_put() on it. + * In that case @ctx->error will give you the error code for why the + * mapping of the old inode failed. + * Also if @ctx is supplied and the current attribute (or the mft + * record it is in) has been modified then the caller must call + * NInoSetMrecNeedsDirtying(ctx->ni); before calling + * ntfs_map_runlist_nolock() or the changes may be lost. + * + * Locking: - The runlist described by @ni must be locked for writing on entry + * and is locked on return. Note the runlist will be modified. + * - If @ctx is NULL, the base mft record of @ni must not be mapped on + * entry and it will be left unmapped on return. + * - If @ctx is not NULL, the base mft record must be mapped on entry + * and it will be left mapped on return. + */ +errno_t ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn, + ntfs_attr_search_ctx *ctx) +{ + VCN end_vcn; + ntfs_inode *base_ni; + MFT_RECORD *m; + ATTR_RECORD *a; + errno_t err = 0; + BOOL ctx_is_temporary, ctx_needs_reset; + ntfs_attr_search_ctx old_ctx = { { NULL, }, }; + + ntfs_debug("Entering for mft_no 0x%llx, vcn 0x%llx.", + (unsigned long long)ni->mft_no, + (unsigned long long)vcn); + base_ni = ni; + if (NInoAttr(ni)) + base_ni = ni->base_ni; + if (!ctx) { + ctx_is_temporary = ctx_needs_reset = TRUE; + err = ntfs_mft_record_map(base_ni, &m); + if (err) + goto done; + ctx = ntfs_attr_search_ctx_get(base_ni, m); + if (!ctx) { + err = ENOMEM; + goto err; + } + } else { + VCN allocated_size_vcn; + + if (ctx->is_error) + panic("%s(): ctx->is_error\n", __FUNCTION__); + a = ctx->a; + if (!a->non_resident) + panic("%s(): !a->non_resident\n", __FUNCTION__); + ctx_is_temporary = FALSE; + end_vcn = sle64_to_cpu(a->highest_vcn); + lck_spin_lock(&ni->size_lock); + allocated_size_vcn = ni->allocated_size >> + ni->vol->cluster_size_shift; + lck_spin_unlock(&ni->size_lock); + /* + * If we already have the attribute extent containing @vcn in + * @ctx, no need to look it up again. We slightly cheat in + * that if vcn exceeds the allocated size, we will refuse to + * map the runlist below, so there is definitely no need to get + * the right attribute extent. + */ + if (vcn >= allocated_size_vcn || (a->type == ni->type && + a->name_length == ni->name_len && + !bcmp((u8*)a + le16_to_cpu(a->name_offset), + ni->name, ni->name_len) && + sle64_to_cpu(a->lowest_vcn) <= vcn && + end_vcn >= vcn)) + ctx_needs_reset = FALSE; + else { + /* Save the old search context. */ + old_ctx = *ctx; + /* + * Reinitialize the search context so we can lookup the + * needed attribute extent. + */ + ntfs_attr_search_ctx_reinit(ctx); + ctx_needs_reset = TRUE; + } + } + if (ctx_needs_reset) { + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, vcn, + NULL, 0, ctx); + if (err) { + if (err == ENOENT) + err = EIO; + goto err; + } + if (!ctx->a->non_resident) + panic("%s(): !a->non_resident!\n", __FUNCTION__); + } + a = ctx->a; + /* + * Only decompress the mapping pairs if @vcn is inside it. Otherwise + * we get into problems when we try to map an out of bounds vcn because + * we then try to map the already mapped runlist fragment and + * ntfs_mapping_pairs_decompress() fails. + */ + end_vcn = sle64_to_cpu(a->highest_vcn) + 1; + if (vcn && vcn >= end_vcn) { + err = ENOENT; + goto err; + } + err = ntfs_mapping_pairs_decompress(ni->vol, a, &ni->rl); +err: + if (ctx_is_temporary) { + if (ctx) + ntfs_attr_search_ctx_put(ctx); + ntfs_mft_record_unmap(base_ni); + } else if (ctx_needs_reset) { + /* + * If there is no attribute list, restoring the search context + * is acomplished simply by copying the saved context back over + * the caller supplied context. If there is an attribute list, + * things are more complicated as we need to deal with mapping + * of mft records and resulting potential changes in pointers. + */ + if (NInoAttrList(base_ni)) { + /* + * If the currently mapped (extent) inode is not the + * one we had before, we need to unmap it and map the + * old one. + */ + if (ctx->ni != old_ctx.ni) { + /* + * If the currently mapped inode is not the + * base inode, unmap it. + */ + if (ctx->base_ni && ctx->ni != ctx->base_ni) { + ntfs_extent_mft_record_unmap(ctx->ni); + ctx->m = ctx->base_m; + if (!ctx->m) + panic("%s(): !ctx->m\n", + __FUNCTION__); + } + /* + * If the old mapped inode is not the base + * inode, map it. + */ + if (old_ctx.base_ni && old_ctx.ni != + old_ctx.base_ni) { + errno_t err2; +retry_map: + err2 = ntfs_mft_record_map(old_ctx.ni, + &ctx->m); + /* + * Something bad has happened. If out + * of memory retry till it succeeds. + * Any other errors are fatal and we + * return the error code in ctx->m. + * Let the caller deal with it... We + * just need to fudge things so the + * caller can reinit and/or put the + * search context safely. + */ + if (err2) { + if (err2 == ENOMEM) { + (void)thread_block( + THREAD_CONTINUE_NULL); + goto retry_map; + } + ctx->is_error = 1; + ctx->error = err2; + old_ctx.ni = old_ctx.base_ni; + } + } + } + if (ctx->is_error) { + old_ctx.is_error = 1; + old_ctx.error = ctx->error; + } else if (ctx->m != old_ctx.m) { + /* + * Update the changed pointers in the saved + * context. + */ + old_ctx.a = (ATTR_RECORD*)((u8*)ctx->m + + ((u8*)old_ctx.a - + (u8*)old_ctx.m)); + old_ctx.m = ctx->m; + } + } + /* Restore the search context to the saved one. */ + *ctx = old_ctx; + } +done: + ntfs_debug("Done (error %d).", (int)err); + return err; +} + +/** + * ntfs_attr_vcn_to_lcn_nolock - convert a vcn into a lcn given an ntfs inode + * @ni: ntfs inode of the attribute whose runlist to search + * @vcn: vcn to convert + * @write_locked: true if the runlist is locked for writing + * @clusters: optional destination for number of contiguous clusters + * + * Find the virtual cluster number @vcn in the runlist of the ntfs attribute + * described by the ntfs inode @ni and return the corresponding logical cluster + * number (lcn). + * + * If the @vcn is not mapped yet, the attempt is made to map the attribute + * extent containing the @vcn and the vcn to lcn conversion is retried. + * + * If @write_locked is true the caller has locked the runlist for writing and + * if false for reading. + * + * If @clusters is not NULL, on success (i.e. we return >= LCN_HOLE) we return + * the number of contiguous clusters after the returned lcn in *@clusters. + * + * Since lcns must be >= 0, we use negative return codes with special meaning: + * + * Return code Meaning / Description + * ========================================== + * LCN_HOLE Hole / not allocated on disk. + * LCN_ENOENT There is no such vcn in the runlist, i.e. @vcn is out of bounds. + * LCN_ENOMEM Not enough memory to map runlist. + * LCN_EIO Critical error (runlist/file is corrupt, i/o error, etc). + * + * Locking: - The runlist must be locked on entry and is left locked on return. + * - If @write_locked is FALSE, i.e. the runlist is locked for reading, + * the lock may be dropped inside the function so you cannot rely on + * the runlist still being the same when this function returns. + */ +LCN ntfs_attr_vcn_to_lcn_nolock(ntfs_inode *ni, const VCN vcn, + const BOOL write_locked, s64 *clusters) +{ + LCN lcn; + BOOL need_lock_switch = FALSE; + BOOL is_retry = FALSE; + + ntfs_debug("Entering for mft_no 0x%llx, vcn 0x%llx, %s_locked.", + (unsigned long long)ni->mft_no, + (unsigned long long)vcn, + write_locked ? "write" : "read"); + if (!NInoNonResident(ni)) + panic("%s(): !NInoNonResident(ni)\n", __FUNCTION__); + if (vcn < 0) + panic("%s(): vcn < 0\n", __FUNCTION__); +retry_remap: + if (!ni->rl.elements) { + lck_spin_lock(&ni->size_lock); + if (!ni->allocated_size) { + lck_spin_unlock(&ni->size_lock); + lcn = LCN_ENOENT; + goto lcn_enoent; + } + lck_spin_unlock(&ni->size_lock); + if (!is_retry) + goto try_to_map; + lcn = LCN_EIO; + goto lcn_eio; + } + /* Convert vcn to lcn. If that fails map the runlist and retry once. */ + lcn = ntfs_rl_vcn_to_lcn(ni->rl.rl, vcn, clusters); + if (lcn >= LCN_HOLE) { + if (need_lock_switch) + lck_rw_lock_exclusive_to_shared(&ni->rl.lock); + ntfs_debug("Done (lcn 0x%llx, clusters 0x%llx).", + (unsigned long long)lcn, + clusters ? (unsigned long long)*clusters : 0); + return lcn; + } + if (lcn != LCN_RL_NOT_MAPPED) { + if (lcn != LCN_ENOENT) + lcn = LCN_EIO; + } else if (!is_retry) { + errno_t err; + +try_to_map: + if (!write_locked && !need_lock_switch) { + need_lock_switch = TRUE; + /* + * If converting the lock from shared to exclusive + * fails, need to take the lock for writing and retry + * in case the racing process did the mapping for us. + */ + if (!lck_rw_lock_shared_to_exclusive(&ni->rl.lock)) { + lck_rw_lock_exclusive(&ni->rl.lock); + goto retry_remap; + } + } + err = ntfs_map_runlist_nolock(ni, vcn, NULL); + if (!err) { + is_retry = TRUE; + goto retry_remap; + } + switch (err) { + case ENOENT: + lcn = LCN_ENOENT; + break; + case ENOMEM: + lcn = LCN_ENOMEM; + break; + default: + lcn = LCN_EIO; + } + } +lcn_eio: + if (need_lock_switch) + lck_rw_lock_exclusive_to_shared(&ni->rl.lock); + if (lcn == LCN_ENOENT) { +lcn_enoent: + ntfs_debug("Done (LCN_ENOENT)."); + } else + ntfs_error(ni->vol->mp, "Failed (error %lld).", (long long)lcn); + return lcn; +} + +/** + * ntfs_attr_find_vcn_nolock - find a vcn in the runlist of an ntfs inode + * @ni: ntfs inode of the attribute whose runlist to search + * @vcn: vcn to find + * @run: return pointer for the found runlist element + * @ctx: active attribute search context if present or NULL if not + * + * Find the virtual cluster number @vcn in the runlist of the ntfs attribute + * described by the ntfs inode @ni and return the address of the runlist + * element containing the @vcn in *@run. + * + * If the @vcn is not mapped yet, the attempt is made to map the attribute + * extent containing the @vcn and the vcn to lcn conversion is retried. + * + * If @ctx is specified, it is an active search context of @ni and its base mft + * record. This is needed when ntfs_attr_find_vcn_nolock() encounters unmapped + * runlist fragments and allows their mapping. If you do not have the mft + * record mapped, you can specify @ctx as NULL and ntfs_attr_find_vcn_nolock() + * will perform the necessary mapping and unmapping. + * + * Note, ntfs_attr_find_vcn_nolock() saves the state of @ctx on entry and + * restores it before returning. Thus, @ctx will be left pointing to the same + * attribute on return as on entry. However, the actual pointers in @ctx may + * point to different memory locations on return, so you must remember to reset + * any cached pointers from the @ctx, i.e. after the call to + * ntfs_attr_find_vcn_nolock(), you will probably want to do: + * m = ctx->m; + * a = ctx->a; + * Assuming you cache ctx->a in a variable @a of type ATTR_RECORD * and that + * you cache ctx->m in a variable @m of type MFT_RECORD *. + * 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. + * + * Return 0 on success and errno on error. + * + * The possible error return codes are: + * ENOENT - No such vcn in the runlist, i.e. @vcn is out of bounds. + * ENOMEM - Not enough memory to map runlist. + * EIO - Critical error (runlist/file is corrupt, i/o error, etc). + * + * WARNING: If @ctx is supplied, regardless of whether success or failure is + * returned, you need to check @ctx->is_error and if 1 the @ctx is no + * longer valid, i.e. you need to either call + * ntfs_attr_search_ctx_reinit() or ntfs_attr_search_ctx_put() on it. + * In that case @ctx->error will give you the error code for why the + * mapping of the old inode failed. + * Also if @ctx is supplied and the current attribute (or the mft + * record it is in) has been modified then the caller must call + * NInoSetMrecNeedsDirtying(ctx->ni); before calling + * ntfs_map_runlist_nolock() or the changes may be lost. + * + * Locking: - The runlist described by @ni must be locked for writing on entry + * and is locked on return. Note the runlist may be modified when + * needed runlist fragments need to be mapped. + * - If @ctx is NULL, the base mft record of @ni must not be mapped on + * entry and it will be left unmapped on return. + * - If @ctx is not NULL, the base mft record must be mapped on entry + * and it will be left mapped on return. + */ +errno_t ntfs_attr_find_vcn_nolock(ntfs_inode *ni, const VCN vcn, + ntfs_rl_element **run, ntfs_attr_search_ctx *ctx) +{ + ntfs_rl_element *rl; + errno_t err = 0; + BOOL is_retry = FALSE; + + ntfs_debug("Entering for mft_no 0x%llx, vcn 0x%llx, with%s ctx.", + (unsigned long long)ni->mft_no, + (unsigned long long)vcn, ctx ? "" : "out"); + if (!NInoNonResident(ni)) + panic("%s(): !NInoNonResident(ni)\n", __FUNCTION__); + if (vcn < 0) + panic("%s(): vcn < 0\n", __FUNCTION__); +retry_remap: + if (!ni->rl.elements) { + lck_spin_lock(&ni->size_lock); + if (!ni->allocated_size) { + lck_spin_unlock(&ni->size_lock); + return LCN_ENOENT; + } + lck_spin_unlock(&ni->size_lock); + if (!is_retry) + goto try_to_map; + err = EIO; + goto err; + } + rl = ni->rl.rl; + if (vcn >= rl[0].vcn) { + while (rl->length) { + if (vcn < rl[1].vcn) { + if (rl->lcn >= LCN_HOLE) { + ntfs_debug("Done."); + *run = rl; + return 0; + } + break; + } + rl++; + } + if (rl->lcn != LCN_RL_NOT_MAPPED) { + if (rl->lcn == LCN_ENOENT) + err = ENOENT; + else + err = EIO; + } + } + if (!err && !is_retry) { + /* + * If the search context is invalid we cannot map the unmapped + * region. + */ + if (ctx->is_error) + err = ctx->error; + else { +try_to_map: + /* + * The @vcn is in an unmapped region, map the runlist + * and retry. + */ + err = ntfs_map_runlist_nolock(ni, vcn, ctx); + if (!err) { + is_retry = TRUE; + goto retry_remap; + } + } + if (err == EINVAL) + err = EIO; + } else if (!err) + err = EIO; +err: + if (err != ENOENT) + ntfs_error(ni->vol->mp, "Failed (error %d).", err); + return err; +} + +/** + * ntfs_attr_search_ctx_reinit - reinitialize an attribute search context + * @ctx: attribute search context to reinitialize + * + * Reinitialize the attribute search context @ctx, unmapping an associated + * extent mft record if present, and initialize the search context again. + * + * This is used when a search for a new attribute is being started to reset + * the search context to the beginning. + * + * Note: We preserve the content of @ctx->is_mft_locked so that reinitializing + * a search context can also be done when dealing with the mft itself. + */ +void ntfs_attr_search_ctx_reinit(ntfs_attr_search_ctx *ctx) +{ + const BOOL mft_is_locked = ctx->is_mft_locked; + + if (!ctx->base_ni) { + /* No attribute list. */ + ctx->is_first = 1; + ctx->is_iteration = 0; + /* Sanity checks are performed elsewhere. */ + ctx->a = (ATTR_RECORD*)((u8*)ctx->m + + le16_to_cpu(ctx->m->attrs_offset)); + /* + * This needs resetting due to + * ntfs_attr_find_in_attribute_list() which can leave it set + * despite having zeroed ctx->base_ni. + */ + ctx->al_entry = NULL; + return; + } + /* Attribute list. */ + if (ctx->ni != ctx->base_ni) + ntfs_extent_mft_record_unmap(ctx->ni); + ntfs_attr_search_ctx_init(ctx, ctx->base_ni, ctx->base_m); + if (mft_is_locked) + ctx->is_mft_locked = 1; +} + +/** + * ntfs_attr_search_ctx_get - allocate and init a new attribute search context + * @ni: ntfs inode with which to initialize the search context + * @m: mft record with which to initialize the search context + * + * Allocate a new attribute search context, initialize it with @ni and @m, and + * return it. Return NULL if allocation failed. + */ +ntfs_attr_search_ctx *ntfs_attr_search_ctx_get(ntfs_inode *ni, MFT_RECORD *m) +{ + ntfs_attr_search_ctx *ctx; + + ctx = OSMalloc(sizeof(ntfs_attr_search_ctx), ntfs_malloc_tag); + if (ctx) + ntfs_attr_search_ctx_init(ctx, ni, m); + return ctx; +} + +/** + * ntfs_attr_search_ctx_put - release an attribute search context + * @ctx: attribute search context to free + * + * Release the attribute search context @ctx, unmapping an associated extent + * mft record if present. + */ +void ntfs_attr_search_ctx_put(ntfs_attr_search_ctx *ctx) +{ + if (ctx->base_ni && ctx->ni != ctx->base_ni) + ntfs_extent_mft_record_unmap(ctx->ni); + OSFree(ctx, sizeof(ntfs_attr_search_ctx), ntfs_malloc_tag); +} + +/** + * ntfs_attr_find_in_mft_record - find (next) attribute in mft record + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means do not care) + * @name_len: attribute name length (only needed if @name present) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length (only needed if @val present) + * @ctx: search context with mft record and attribute to search from + * + * You should not need to call this function directly. Use ntfs_attr_lookup() + * instead. + * + * ntfs_attr_find_in_mft_record() takes a search context @ctx as parameter and + * searches the mft record specified by @ctx->m, beginning at @ctx->a, for an + * attribute of @type, optionally @name and @val. + * + * If the attribute is found, ntfs_attr_find_in_mft_record() returns 0 and + * @ctx->a is set to point to the found attribute. + * + * If the attribute is not found, ENOENT is returned and @ctx->a is set to + * 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_in_mft_record() returns EIO. In this case + * @ctx->a is undefined and in particular do not rely on it not having changed. + * + * If @ctx->is_first is 1, the search begins with @ctx->a itself. If it is 0, + * the search begins after @ctx->a. + * + * If @ctx->is_iteration is 1 and @type is AT_UNUSED this is not a search but + * an iteration in which case each attribute in the mft record is returned in + * turn with each call to ntfs_attr_find_in_mft_record(). Note all attributes + * are returned including the attribute list attribute, unlike when + * @ctx->is_iteration is 0 when it is not returned unless it is specifically + * looked for. + * + * Similarly to the above, when @ctx->is_iterations is 1 and @type is not + * AT_UNUSED all attributes of type @type are returned one after the other. + * + * 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. + * + * Finally, the resident attribute value @val is looked for, if present. If + * @val is not present (NULL), @val_len is ignored. + * + * ntfs_attr_find_in_mft_record() 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_in_mft_record() 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 do not 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! + * + * Note if the volume is mounted case sensitive we treat attribute names as + * being case sensitive and vice versa if the volume is not mounted case + * sensitive we treat attribute names as being case insensitive also. + */ +errno_t ntfs_attr_find_in_mft_record(const ATTR_TYPE type, + const ntfschar *name, const u32 name_len, + const void *val, const u32 val_len, ntfs_attr_search_ctx *ctx) +{ + ATTR_RECORD *a; + ntfs_volume *vol = ctx->ni->vol; + const ntfschar *upcase = vol->upcase; + const u32 upcase_len = vol->upcase_len; + const BOOL case_sensitive = NVolCaseSensitive(vol); + const BOOL is_iteration = ctx->is_iteration; + + /* + * Iterate over attributes in mft record starting at @ctx->a, or the + * attribute following that, if @ctx->is_first is true. + */ + if (ctx->is_first) { + a = ctx->a; + ctx->is_first = 0; + } else + a = (ATTR_RECORD*)((u8*)ctx->a + le32_to_cpu(ctx->a->length)); + for (;; a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length))) { + if ((u8*)a < (u8*)ctx->m || (u8*)a > (u8*)ctx->m + + le32_to_cpu(ctx->m->bytes_allocated)) + break; + ctx->a = a; + if (((!is_iteration || type != AT_UNUSED) && + le32_to_cpu(a->type) > le32_to_cpu(type)) || + a->type == AT_END) + return ENOENT; + if (!a->length) + break; + if (is_iteration) { + if (type == AT_UNUSED || type == a->type) + return 0; + } + if (a->type != type) *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20120514140402.9FDF01065670>