/* * ntfs_index.h - Defines for index handling in 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_INDEX_H #define _OSX_NTFS_INDEX_H #include #include #include /* Foward declaration. */ typedef struct _ntfs_index_context ntfs_index_context; #include "ntfs_attr.h" #include "ntfs_inode.h" #include "ntfs_layout.h" #include "ntfs_types.h" /** * @up: pointer to index context located directly above in the tree * @down: pointer to index context located directly below in the tree * @idx_ni: inode containing the @entry described by this context * @base_ni: base inode * @entry: if @is_locked this is the index entry (points into @ir or @ia) * @is_root: 1 if @entry is in @ir and 0 if it is in @ia * @ir: index root if @is_root and @is_locked and NULL otherwise * @actx: attribute search context if @is_root and @is_locked or NULL * @ia: index block if @is_root is 0 and @is_locked or NULL * @upl_ofs: byte offset into the index allocation at which @upl begins * @upl: page list if @is_root is 0 and @is_locked or NULL * @pl: array of pages containing the page itself if @is_root is 0 * @addr: mapped address of the page data if @is_root is 0 and @is_locked * * @up and @down are pointers used to chain the various index contexts together * into a path through the B+tree index. This path is used to describe the * index nodes traversed during a lookup. * * @up points to the index context that is immediately above in the tree or if * this is the top of the tree then @up is the bottom of the tree. * * @down points to the index context that is immediately below in the tree or * if this is the bottom of the tree then @down is the top of the tree. * * If this index context is the only node then @up == @down == this index * context. * * @idx_ni is the index inode this context belongs to and @base_ni is the base * inode to which @idx_ni belongs. * * @entry is the index entry described by this context. It is only valid when * @is_locked is true. * * If @is_rootis 1, @entry is in the index root attribute @ir described by the * attribute search context @actx and the base inode @base_ni. @ia, @upl, @pl, * and @addr do not exist in this case as they are in a union with @ir and * @actx. * * If @is_root is 0, @entry is in the index allocation attribute and @ia, * @upl_ofs, @upl, @pl, and @addr point to the index allocation block and the * mapped, locked page it is in, respectively. @ir and @actx do not exist in * this case. Note @upl_ofs is the byte offset inside the index allocation * attribute at which the page list @upl begins. This is used when an index * context is relocked/remapped to determine if it now is at a different * virtual memory address and thus all pointers in the index context need to be * updated for the new page address @addr. * * To obtain a context call ntfs_index_ctx_get(). * * We use this context to allow ntfs_index_lookup() to return the found index * @entry without having to allocate a buffer and copy the @entry and/or its * data into it. * * 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. */ struct _ntfs_index_context { ntfs_index_context *up; /* Pointer to the index context that is immediately above in the tree. */ ntfs_index_context *down; /* Pointer to the index context that is immediately below in the tree. */ ntfs_inode *idx_ni; /* Index inode. */ ntfs_inode *base_ni; /* Base inode of the index inode @idx_ni. */ union { /* Use @ie if @is_match is 1 and @follow_ie if it is 0. */ INDEX_ENTRY *entry; /* Index entry matched by lookup. */ INDEX_ENTRY *follow_entry; /* Index entry whose sub-node needs to be descended into if present or index entry in front of which to insert the new entry. */ }; unsigned entry_nr; /* Index of the @entry in the @entries array. */ struct { unsigned is_dirty:1; /* If 1 the page has been modified. */ unsigned is_root:1; /* If 1 the node is the index root. */ unsigned is_locked:1; /* If 1 the node is locked. */ unsigned is_match:1; /* If 1 @ie matches the looked up entry. */ unsigned promote_inserted:1; /* If 0 insert the @insert_entry index entry in front of @entry. */ unsigned split_node:1; /* If 1 the node needs to be split. */ unsigned right_is_locked:1; /* If 1 the right node, i.e. @right_page is locked. */ unsigned insert_to_add:1; /* If 1 the entry to be inserted is the entry to be added, i.e. the entry with which the function was invoked. */ unsigned bmp_is_locked:1; /* If 1 the index bitmap inode is locked for writing. This is set in ntfs_index_block_alloc() to cope with the need for recursion into itself. */ }; union { /* Use @ir if @is_root is 1 and @ia if it is 0. */ INDEX_ROOT *ir; /* The index root attribute value. */ struct { INDEX_ALLOCATION *ia; /* The index allocation block. */ VCN vcn; /* The vcn of this index block. */ }; }; INDEX_HEADER *index; /* The index header of the node. */ union { /* * If @is_root is 1 then use @actx if @is_locked is also 1. * If @is_locked is 0, then @actx is NULL. In the unlocked * case we have to revalidate the pointers after mapping the * mft record and looking up the index root attribute as it is * possible that some attribute resizing operation caused the * index root to be moved to a different mft record or within * the same mft record and it is also possible that the VM * paged out the page and then loaded it into a different place * in memory. * * If @is_root is 0 then use @upl_ofs, @upl, @pl, and @addr. */ ntfs_attr_search_ctx *actx; struct { s64 upl_ofs; upl_t upl; upl_page_info_array_t pl; u8 *addr; }; }; unsigned bytes_free; /* Number of bytes free in this node. */ INDEX_ENTRY **entries; /* Pointers to the index entries in the node. */ unsigned nr_entries; /* Current number of entries in @entries. */ unsigned max_entries; /* Maximum number of entries in @entries. */ /* * These fields are used when splitting nodes when inserting new index * entries. */ /* * If @promote_inserted is 0 then @insert_entry_nr, @insert_entry_size, * and @insert_ictx describe the index entry of a child index node to * be inserted into this index block in front of the index entry * @entry. * * If @insert_to_add is 1 then the entry that is to be inserted into * this node is the entry being added to the index with the current * call to ntfs_index_entry_add(), i.e. it does not come from a child * node. In that case @insert_ictx and @insert_entry_nr are not valid. * * If @promote_inserted is 1 then nothing needs to be inserted into * this node, i.e. the node just needs to be split. This happens when * the index entry to be inserted is the median entry and it is being * promoted to our parent node. */ ntfs_index_context *insert_ictx; unsigned insert_entry_nr; u32 insert_entry_size; /* * If @split_node is 1 then @promote_entry_nr and @promote_inserted * describe the index entry being promoted and thus describe the * position in the index at which the node is to be split. * * If @cur_ictx->promote_inserted is 1 we have to keep the entry * @cur_ictx->promote_entry_nr and move it together with all following * entries to the right-hand node. And if @cur_ictx->promote_inserted * is 0 we have to remove the entry @cur_ictx->promote_entry_nr and * move all following entries to the right-hand node. * * @right_vcn, @right_ia, @right_upl_ofs, @right_upl, @right_pl, and * @right_addr in turn describe the allocated index block to be used as * the destination for the index entries that are split away from the * right-hand side of the median of the index block as described above. */ unsigned promote_entry_nr; VCN right_vcn; INDEX_ALLOCATION *right_ia; s64 right_upl_ofs; upl_t right_upl; upl_page_info_array_t right_pl; u8 *right_addr; }; /** * ntfs_index_ctx_alloc - allocate an index context * * Allocate an index context and return it. */ static inline ntfs_index_context *ntfs_index_ctx_alloc(void) { return OSMalloc(sizeof(ntfs_index_context), ntfs_malloc_tag); } /** * ntfs_index_ctx_init - initialize an index context * @ictx: index context to initialize * @idx_ni: ntfs index inode with which to initialize the context * * Initialize the index context @ictx with @idx_ni and its base inode and set * its @up and @down pointers to point to itself. * * Locking: Caller must hold @idx_ni->lock on the index inode. */ static inline void ntfs_index_ctx_init(ntfs_index_context *ictx, ntfs_inode *idx_ni) { *ictx = (ntfs_index_context) { .up = ictx, .down = ictx, .idx_ni = idx_ni, .base_ni = NInoAttr(idx_ni) ? idx_ni->base_ni : idx_ni, }; } __private_extern__ ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *idx_ni); __private_extern__ void ntfs_index_ctx_put_reuse_single( ntfs_index_context *ictx); __private_extern__ void ntfs_index_ctx_put_reuse(ntfs_index_context *ictx); /** * ntfs_index_ctx_reinit - re-initialize an index context * @ictx: index context to re-initialize * @idx_ni: ntfs index inode with which to initialize the context * * Re-initialize the index context @ictx with @idx_ni and its base inode. * * To do this the existing index context is first put for reuse and then * initialized from scratch with the index inode @idx_ni. * * Locking: Caller must hold @idx_ni->lock on the index inode. */ static inline void ntfs_index_ctx_reinit(ntfs_index_context *ictx, ntfs_inode *idx_ni) { ntfs_index_ctx_put_reuse(ictx); ntfs_index_ctx_init(ictx, idx_ni); } /** * ntfs_index_ctx_disconnect - disconnect an index context from its tree path * @ictx: index context to disconnect * * Disconnect the index context @ictx from its tree path. This function leaves * @ictx in an invalid state. We only use it when we are about to throw away * @ictx thus do not care what state it is left in. * * Locking: Caller must hold @ictx->idx_ni->lock on the index inode. */ static inline void ntfs_index_ctx_disconnect(ntfs_index_context *ictx) { ictx->up->down = ictx->down; ictx->down->up = ictx->up; } /** * ntfs_index_ctx_free - free an index context * @ictx: index context to free * * Free the index context @ictx. */ static inline void ntfs_index_ctx_free(ntfs_index_context *ictx) { OSFree(ictx, sizeof(*ictx), ntfs_malloc_tag); } /** * ntfs_index_ctx_put_single - release a single index context * @ictx: index context to free * * Release the index context @ictx, disconnecting it from its tree path and * releasing all associated resources. * * Locking: Caller must hold @ictx->idx_ni->lock on the index inode. */ static inline void ntfs_index_ctx_put_single(ntfs_index_context *ictx) { /* Disconnect the current index context from the tree. */ ntfs_index_ctx_disconnect(ictx); /* Release all resources held by the current index context. */ ntfs_index_ctx_put_reuse_single(ictx); /* Deallocate the current index context. */ ntfs_index_ctx_free(ictx); } /** * ntfs_index_ctx_put - release an index context * @ictx: index context to free * * Release the index context @ictx, releasing all associated resources. * * Locking: Caller must hold @ictx->idx_ni->lock on the index inode. */ static inline void ntfs_index_ctx_put(ntfs_index_context *ictx) { ntfs_index_ctx_put_reuse(ictx); ntfs_index_ctx_free(ictx); } __private_extern__ void ntfs_index_ctx_unlock(ntfs_index_context *ictx); __private_extern__ errno_t ntfs_index_ctx_relock(ntfs_index_context *ictx); __private_extern__ errno_t ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context **ictx); __private_extern__ errno_t ntfs_index_lookup_by_position(const s64 pos, const int key_len, ntfs_index_context **index_ctx); __private_extern__ errno_t ntfs_index_lookup_next( ntfs_index_context **index_ctx); __private_extern__ void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx); __private_extern__ errno_t ntfs_index_move_root_to_allocation_block( ntfs_index_context *ictx); __private_extern__ int ntfs_index_entry_delete(ntfs_index_context *ictx); __private_extern__ errno_t ntfs_index_entry_add_or_node_split( ntfs_index_context *ictx, const BOOL split_only, u32 entry_size, const void *key, const u32 key_len, const void *data, const u32 data_len); /** * ntfs_index_entry_add - add a key to an index * @ictx: index context specifying the position at which to add * @key: key to add to the directory or view index * @key_len: size of key @key in bytes * @data: data to associate with the key @key if a view index * @data_len: size of data @data in bytes if a view index * * If @ictx belongs to a directory index, insert the filename attribute @key of * length @key_len bytes in the directory index at the position specified by * the index context @ictx and point the inserted index entry at the mft * reference *@data which is the mft reference of the inode to which the * filename @fn belongs. @data_len must be zero in this case. * * If @ictx belongs to a view index, insert the key @key of length @key_len * bytes in the view index at the position specified by the index context @ictx * and associate the data @data of size @data_len bytes with the key @key. * * Return 0 on success and errno on error. On error the index context is no * longer usable and must be released or reinitialized. * * Note that we do not update the array of index entry pointers nor the number * of entries in the array because on success it means that the index entry has * been added and the function is done, i.e. the array of index entry pointers * will not be used any more and on error the index context becomes invalid so * there is no need to update any of the pointers. * * Locking: - Caller must hold @ictx->idx_ni->lock on the index inode for * writing. * - The index context @ictx must be locked. */ static inline errno_t ntfs_index_entry_add(ntfs_index_context *ictx, const void *key, const u32 key_len, const void *data, const u32 data_len) { return ntfs_index_entry_add_or_node_split(ictx, FALSE, 0, key, key_len, data, data_len); } #endif /* _OSX_NTFS_INDEX_H */