attrib.c

来自「一个类似windows」· C语言 代码 · 共 1,722 行 · 第 1/4 页

C
1,722
字号
/**
 * attrib.c - NTFS attribute operations. Part of the Linux-NTFS project.
 *
 * Copyright (c) 2001-2003 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
 */

#include <linux/buffer_head.h>
#include "ntfs.h"
#include "dir.h"

/* Temporary helper functions -- might become macros */

/**
 * ntfs_rl_mm - run_list memmove
 *
 * It is up to the caller to serialize access to the run list @base.
 */
static inline void ntfs_rl_mm(run_list_element *base, int dst, int src,
		int size)
{
	if (likely((dst != src) && (size > 0)))
		memmove(base + dst, base + src, size * sizeof (*base));
}

/**
 * ntfs_rl_mc - run_list memory copy
 *
 * It is up to the caller to serialize access to the run lists @dstbase and
 * @srcbase.
 */
static inline void ntfs_rl_mc(run_list_element *dstbase, int dst,
		run_list_element *srcbase, int src, int size)
{
	if (likely(size > 0))
		memcpy(dstbase + dst, srcbase + src, size * sizeof(*dstbase));
}

/**
 * ntfs_rl_realloc - Reallocate memory for run_lists
 * @rl:		original run list
 * @old_size:	number of run list elements in the original run list @rl
 * @new_size:	number of run list elements we need space for
 *
 * As the run_lists grow, more memory will be required.  To prevent the
 * kernel having to allocate and reallocate large numbers of small bits of
 * memory, this function returns and entire page of memory.
 *
 * It is up to the caller to serialize access to the run list @rl.
 *
 * N.B.  If the new allocation doesn't require a different number of pages in
 *       memory, the function will return the original pointer.
 *
 * On success, return a pointer to the newly allocated, or recycled, memory.
 * On error, return -errno. The following error codes are defined:
 *	-ENOMEM	- Not enough memory to allocate run list array.
 *	-EINVAL	- Invalid parameters were passed in.
 */
static inline run_list_element *ntfs_rl_realloc(run_list_element *rl,
		int old_size, int new_size)
{
	run_list_element *new_rl;

	old_size = PAGE_ALIGN(old_size * sizeof(*rl));
	new_size = PAGE_ALIGN(new_size * sizeof(*rl));
	if (old_size == new_size)
		return rl;

	new_rl = ntfs_malloc_nofs(new_size);
	if (unlikely(!new_rl))
		return ERR_PTR(-ENOMEM);

	if (likely(rl != NULL)) {
		if (unlikely(old_size > new_size))
			old_size = new_size;
		memcpy(new_rl, rl, old_size);
		ntfs_free(rl);
	}
	return new_rl;
}

/**
 * ntfs_are_rl_mergeable - test if two run lists can be joined together
 * @dst:	original run list
 * @src:	new run list to test for mergeability with @dst
 *
 * Test if two run lists can be joined together. For this, their VCNs and LCNs
 * must be adjacent.
 *
 * It is up to the caller to serialize access to the run lists @dst and @src.
 *
 * Return: TRUE   Success, the run lists can be merged.
 *         FALSE  Failure, the run lists cannot be merged.
 */
static inline BOOL ntfs_are_rl_mergeable(run_list_element *dst,
		run_list_element *src)
{
	BUG_ON(!dst);
	BUG_ON(!src);

	if ((dst->lcn < 0) || (src->lcn < 0))     /* Are we merging holes? */
		return FALSE;
	if ((dst->lcn + dst->length) != src->lcn) /* Are the runs contiguous? */
		return FALSE;
	if ((dst->vcn + dst->length) != src->vcn) /* Are the runs misaligned? */
		return FALSE;

	return TRUE;
}

/**
 * __ntfs_rl_merge - merge two run lists without testing if they can be merged
 * @dst:	original, destination run list
 * @src:	new run list to merge with @dst
 *
 * Merge the two run lists, writing into the destination run list @dst. The
 * caller must make sure the run lists can be merged or this will corrupt the
 * destination run list.
 *
 * It is up to the caller to serialize access to the run lists @dst and @src.
 */
static inline void __ntfs_rl_merge(run_list_element *dst, run_list_element *src)
{
	dst->length += src->length;
}

/**
 * ntfs_rl_merge - test if two run lists can be joined together and merge them
 * @dst:	original, destination run list
 * @src:	new run list to merge with @dst
 *
 * Test if two run lists can be joined together. For this, their VCNs and LCNs
 * must be adjacent. If they can be merged, perform the merge, writing into
 * the destination run list @dst.
 *
 * It is up to the caller to serialize access to the run lists @dst and @src.
 *
 * Return: TRUE   Success, the run lists have been merged.
 *         FALSE  Failure, the run lists cannot be merged and have not been
 *		  modified.
 */
static inline BOOL ntfs_rl_merge(run_list_element *dst, run_list_element *src)
{
	BOOL merge = ntfs_are_rl_mergeable(dst, src);

	if (merge)
		__ntfs_rl_merge(dst, src);
	return merge;
}

/**
 * ntfs_rl_append - append a run list after a given element
 * @dst:	original run list to be worked on
 * @dsize:	number of elements in @dst (including end marker)
 * @src:	run list to be inserted into @dst
 * @ssize:	number of elements in @src (excluding end marker)
 * @loc:	append the new run list @src after this element in @dst
 *
 * Append the run list @src after element @loc in @dst.  Merge the right end of
 * the new run list, if necessary. Adjust the size of the hole before the
 * appended run list.
 *
 * It is up to the caller to serialize access to the run lists @dst and @src.
 *
 * On success, return a pointer to the new, combined, run list. Note, both
 * run lists @dst and @src are deallocated before returning so you cannot use
 * the pointers for anything any more. (Strictly speaking the returned run list
 * may be the same as @dst but this is irrelevant.)
 *
 * On error, return -errno. Both run lists are left unmodified. The following
 * error codes are defined:
 *	-ENOMEM	- Not enough memory to allocate run list array.
 *	-EINVAL	- Invalid parameters were passed in.
 */
static inline run_list_element *ntfs_rl_append(run_list_element *dst,
		int dsize, run_list_element *src, int ssize, int loc)
{
	BOOL right;
	int magic;

	BUG_ON(!dst);
	BUG_ON(!src);

	/* First, check if the right hand end needs merging. */
	right = ntfs_are_rl_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 (IS_ERR(dst))
		return dst;
	/*
	 * We are guaranteed to succeed from here so can start modifying the
	 * original run lists.
	 */

	/* First, merge the right hand end, if necessary. */
	if (right)
		__ntfs_rl_merge(src + ssize - 1, dst + loc + 1);

	magic = loc + ssize;

	/* Move the tail of @dst out of the way, then copy in @src. */
	ntfs_rl_mm(dst, magic + 1, 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[magic + 1].lcn == LCN_ENOENT)
		dst[magic + 1].vcn = dst[magic].vcn + dst[magic].length;

	return dst;
}

/**
 * ntfs_rl_insert - insert a run list into another
 * @dst:	original run list to be worked on
 * @dsize:	number of elements in @dst (including end marker)
 * @src:	new run list to be inserted
 * @ssize:	number of elements in @src (excluding end marker)
 * @loc:	insert the new run list @src before this element in @dst
 *
 * Insert the run list @src before element @loc in the run list @dst. Merge the
 * left end of the new run list, if necessary. Adjust the size of the hole
 * after the inserted run list.
 *
 * It is up to the caller to serialize access to the run lists @dst and @src.
 *
 * On success, return a pointer to the new, combined, run list. Note, both
 * run lists @dst and @src are deallocated before returning so you cannot use
 * the pointers for anything any more. (Strictly speaking the returned run list
 * may be the same as @dst but this is irrelevant.)
 *
 * On error, return -errno. Both run lists are left unmodified. The following
 * error codes are defined:
 *	-ENOMEM	- Not enough memory to allocate run list array.
 *	-EINVAL	- Invalid parameters were passed in.
 */
static inline run_list_element *ntfs_rl_insert(run_list_element *dst,
		int dsize, run_list_element *src, int ssize, int loc)
{
	BOOL left = FALSE;
	BOOL disc = FALSE;	/* Discontinuity */
	BOOL hole = FALSE;	/* Following a hole */
	int magic;

	BUG_ON(!dst);
	BUG_ON(!src);

	/* disc => Discontinuity between the end of @dst and the start of @src.
	 *         This means we might need to insert a hole.
	 * hole => @dst ends with a hole or an unmapped region which we can
	 *         extend to match the discontinuity. */
	if (loc == 0)
		disc = (src[0].vcn > 0);
	else {
		s64 merged_length;

		left = ntfs_are_rl_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);
		if (disc)
			hole = (dst[loc - 1].lcn == LCN_HOLE);
	}

	/* Space required: @dst size + @src size, less one if we merged, plus
	 * one if there was a discontinuity, less one for a trailing hole. */
	dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left + disc - hole);
	if (IS_ERR(dst))
		return dst;
	/*
	 * We are guaranteed to succeed from here so can start modifying the
	 * original run list.
	 */

	if (left)
		__ntfs_rl_merge(dst + loc - 1, src);

	magic = loc + ssize - left + disc - hole;

	/* Move the tail of @dst out of the way, then copy in @src. */
	ntfs_rl_mm(dst, magic, loc, dsize - loc);
	ntfs_rl_mc(dst, loc + disc - hole, src, left, ssize - left);

	/* Adjust the VCN of the last run ... */
	if (dst[magic].lcn <= LCN_HOLE)
		dst[magic].vcn = dst[magic - 1].vcn + dst[magic - 1].length;
	/* ... and the length. */
	if (dst[magic].lcn == LCN_HOLE || dst[magic].lcn == LCN_RL_NOT_MAPPED)
		dst[magic].length = dst[magic + 1].vcn - dst[magic].vcn;

	/* Writing beyond the end of the file and there's a discontinuity. */
	if (disc) {
		if (hole)
			dst[loc - 1].length = dst[loc].vcn - dst[loc - 1].vcn;
		else {
			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;
		}

		magic += hole;

		if (dst[magic].lcn == LCN_ENOENT)
			dst[magic].vcn = dst[magic - 1].vcn +
					dst[magic - 1].length;
	}
	return dst;
}

/**
 * ntfs_rl_replace - overwrite a run_list element with another run list
 * @dst:	original run list to be worked on
 * @dsize:	number of elements in @dst (including end marker)
 * @src:	new run list to be inserted
 * @ssize:	number of elements in @src (excluding end marker)
 * @loc:	index in run list @dst to overwrite with @src
 *
 * Replace the run list element @dst at @loc with @src. Merge the left and
 * right ends of the inserted run list, if necessary.
 *
 * It is up to the caller to serialize access to the run lists @dst and @src.
 *
 * On success, return a pointer to the new, combined, run list. Note, both
 * run lists @dst and @src are deallocated before returning so you cannot use
 * the pointers for anything any more. (Strictly speaking the returned run list
 * may be the same as @dst but this is irrelevant.)
 *
 * On error, return -errno. Both run lists are left unmodified. The following
 * error codes are defined:
 *	-ENOMEM	- Not enough memory to allocate run list array.
 *	-EINVAL	- Invalid parameters were passed in.
 */
static inline run_list_element *ntfs_rl_replace(run_list_element *dst,
		int dsize, run_list_element *src, int ssize, int loc)
{
	BOOL left = FALSE;
	BOOL right;
	int magic;

	BUG_ON(!dst);
	BUG_ON(!src);

	/* First, merge the left and right ends, if necessary. */
	right = ntfs_are_rl_mergeable(src + ssize - 1, dst + loc + 1);
	if (loc > 0)
		left = ntfs_are_rl_mergeable(dst + loc - 1, src);

	/* Allocate some space. We'll need less if the left, right, or both
	 * ends were merged. */
	dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left - right);
	if (IS_ERR(dst))
		return dst;
	/*
	 * We are guaranteed to succeed from here so can start modifying the
	 * original run lists.
	 */
	if (right)
		__ntfs_rl_merge(src + ssize - 1, dst + loc + 1);
	if (left)
		__ntfs_rl_merge(dst + loc - 1, src);

	/* FIXME: What does this mean? (AIA) */
	magic = loc + ssize - left;

	/* Move the tail of @dst out of the way, then copy in @src. */
	ntfs_rl_mm(dst, magic, loc + right + 1, dsize - loc - right - 1);
	ntfs_rl_mc(dst, loc, src, left, ssize - left);

	/* We may have changed the length of the file, so fix the end marker */
	if (dst[magic].lcn == LCN_ENOENT)
		dst[magic].vcn = dst[magic - 1].vcn + dst[magic - 1].length;
	return dst;
}

/**
 * ntfs_rl_split - insert a run list into the centre of a hole
 * @dst:	original run list to be worked on
 * @dsize:	number of elements in @dst (including end marker)
 * @src:	new run list to be inserted
 * @ssize:	number of elements in @src (excluding end marker)
 * @loc:	index in run list @dst at which to split and insert @src
 *
 * Split the run list @dst at @loc into two and insert @new in between the two
 * fragments. No merging of run lists is necessary. Adjust the size of the
 * holes either side.
 *
 * It is up to the caller to serialize access to the run lists @dst and @src.
 *
 * On success, return a pointer to the new, combined, run list. Note, both
 * run lists @dst and @src are deallocated before returning so you cannot use
 * the pointers for anything any more. (Strictly speaking the returned run list
 * may be the same as @dst but this is irrelevant.)
 *
 * On error, return -errno. Both run lists are left unmodified. The following
 * error codes are defined:
 *	-ENOMEM	- Not enough memory to allocate run list array.
 *	-EINVAL	- Invalid parameters were passed in.
 */
static inline run_list_element *ntfs_rl_split(run_list_element *dst, int dsize,
		run_list_element *src, int ssize, int loc)
{
	BUG_ON(!dst);
	BUG_ON(!src);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?