Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 511bea5e authored by Anton Altaparmakov's avatar Anton Altaparmakov
Browse files

NTFS: - Change {__,}ntfs_cluster_free() to also take an optional attribute


        search context as argument.  This allows calling it with the mft
        record mapped.  Update all callers.
      - Fix potential deadlock in ntfs_mft_data_extend_allocation_nolock()
	error handling by passing in the active search context when calling
	ntfs_cluster_free().

Signed-off-by: default avatarAnton Altaparmakov <aia21@cantab.net>
parent 69b41e3c
Loading
Loading
Loading
Loading
+7 −4
Original line number Diff line number Diff line
@@ -24,10 +24,13 @@ ToDo/Notes:

2.1.25-WIP

	- Change ntfs_map_runlist_nolock() and ntfs_attr_find_vcn_nolock() to
	  also take an optional attribute search context as argument.  This
	  allows calling these functions with the mft record mapped.  Update
	  all callers.
	- Change ntfs_map_runlist_nolock(), ntfs_attr_find_vcn_nolock() and
	  {__,}ntfs_cluster_free() to also take an optional attribute search
	  context as argument.  This allows calling these functions with the
	  mft record mapped.  Update all callers.
	- Fix potential deadlock in ntfs_mft_data_extend_allocation_nolock()
	  error handling by passing in the active search context when calling
	  ntfs_cluster_free().

2.1.24 - Lots of bug fixes and support more clean journal states.

+35 −6
Original line number Diff line number Diff line
@@ -782,6 +782,7 @@ switch_to_data1_zone: search_zone = 2;
 * @ni:		ntfs inode whose runlist describes the clusters to free
 * @start_vcn:	vcn in the runlist of @ni at which to start freeing clusters
 * @count:	number of clusters to free or -1 for all clusters
 * @ctx:	active attribute search context if present or NULL if not
 * @is_rollback:	true if this is a rollback operation
 *
 * Free @count clusters starting at the cluster @start_vcn in the runlist
@@ -791,15 +792,39 @@ switch_to_data1_zone: search_zone = 2;
 * deallocated.  Thus, to completely free all clusters in a runlist, use
 * @start_vcn = 0 and @count = -1.
 *
 * If @ctx is specified, it is an active search context of @ni and its base mft
 * record.  This is needed when __ntfs_cluster_free() 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_cluster_free() will
 * perform the necessary mapping and unmapping.
 *
 * Note, __ntfs_cluster_free() 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_cluster_free(),
 * you will probably want to do:
 *	m = ctx->mrec;
 *	a = ctx->attr;
 * Assuming you cache ctx->attr in a variable @a of type ATTR_RECORD * and that
 * you cache ctx->mrec in a variable @m of type MFT_RECORD *.
 *
 * @is_rollback should always be FALSE, it is for internal use to rollback
 * errors.  You probably want to use ntfs_cluster_free() instead.
 *
 * Note, ntfs_cluster_free() does not modify the runlist at all, so the caller
 * has to deal with it later.
 * Note, __ntfs_cluster_free() does not modify the runlist, so you have to
 * remove from the runlist or mark sparse the freed runs later.
 *
 * Return the number of deallocated clusters (not counting sparse ones) on
 * success and -errno on error.
 *
 * WARNING: If @ctx is supplied, regardless of whether success or failure is
 *	    returned, you need to check IS_ERR(@ctx->mrec) and if TRUE the @ctx
 *	    is no longer valid, i.e. you need to either call
 *	    ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it.
 *	    In that case PTR_ERR(@ctx->mrec) will give you the error code for
 *	    why the mapping of the old inode failed.
 *
 * 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.
@@ -807,9 +832,13 @@ switch_to_data1_zone: search_zone = 2;
 *	      on return.
 *	    - This function takes the volume lcn bitmap lock for writing and
 *	      modifies the bitmap contents.
 *	    - 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.
 */
s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, s64 count,
		const BOOL is_rollback)
		ntfs_attr_search_ctx *ctx, const BOOL is_rollback)
{
	s64 delta, to_free, total_freed, real_freed;
	ntfs_volume *vol;
@@ -839,7 +868,7 @@ s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, s64 count,

	total_freed = real_freed = 0;

	rl = ntfs_attr_find_vcn_nolock(ni, start_vcn, NULL);
	rl = ntfs_attr_find_vcn_nolock(ni, start_vcn, ctx);
	if (IS_ERR(rl)) {
		if (!is_rollback)
			ntfs_error(vol->sb, "Failed to find first runlist "
@@ -893,7 +922,7 @@ s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, s64 count,

			/* Attempt to map runlist. */
			vcn = rl->vcn;
			rl = ntfs_attr_find_vcn_nolock(ni, vcn, NULL);
			rl = ntfs_attr_find_vcn_nolock(ni, vcn, ctx);
			if (IS_ERR(rl)) {
				err = PTR_ERR(rl);
				if (!is_rollback)
@@ -961,7 +990,7 @@ s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, s64 count,
	 * If rollback fails, set the volume errors flag, emit an error
	 * message, and return the error code.
	 */
	delta = __ntfs_cluster_free(ni, start_vcn, total_freed, TRUE);
	delta = __ntfs_cluster_free(ni, start_vcn, total_freed, ctx, TRUE);
	if (delta < 0) {
		ntfs_error(vol->sb, "Failed to rollback (error %i).  Leaving "
				"inconsistent metadata!  Unmount and run "
+35 −5
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@

#include <linux/fs.h>

#include "attrib.h"
#include "types.h"
#include "inode.h"
#include "runlist.h"
@@ -44,13 +45,14 @@ extern runlist_element *ntfs_cluster_alloc(ntfs_volume *vol,
		const NTFS_CLUSTER_ALLOCATION_ZONES zone);

extern s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn,
		s64 count, const BOOL is_rollback);
		s64 count, ntfs_attr_search_ctx *ctx, const BOOL is_rollback);

/**
 * ntfs_cluster_free - free clusters on an ntfs volume
 * @ni:		ntfs inode whose runlist describes the clusters to free
 * @start_vcn:	vcn in the runlist of @ni at which to start freeing clusters
 * @count:	number of clusters to free or -1 for all clusters
 * @ctx:	active attribute search context if present or NULL if not
 *
 * Free @count clusters starting at the cluster @start_vcn in the runlist
 * described by the ntfs inode @ni.
@@ -59,12 +61,36 @@ extern s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn,
 * deallocated.  Thus, to completely free all clusters in a runlist, use
 * @start_vcn = 0 and @count = -1.
 *
 * Note, ntfs_cluster_free() does not modify the runlist at all, so the caller
 * has to deal with it later.
 * If @ctx is specified, it is an active search context of @ni and its base mft
 * record.  This is needed when ntfs_cluster_free() 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_cluster_free() will perform
 * the necessary mapping and unmapping.
 *
 * Note, ntfs_cluster_free() 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_cluster_free(),
 * you will probably want to do:
 *	m = ctx->mrec;
 *	a = ctx->attr;
 * Assuming you cache ctx->attr in a variable @a of type ATTR_RECORD * and that
 * you cache ctx->mrec in a variable @m of type MFT_RECORD *.
 *
 * Note, ntfs_cluster_free() does not modify the runlist, so you have to remove
 * from the runlist or mark sparse the freed runs later.
 *
 * Return the number of deallocated clusters (not counting sparse ones) on
 * success and -errno on error.
 *
 * WARNING: If @ctx is supplied, regardless of whether success or failure is
 *	    returned, you need to check IS_ERR(@ctx->mrec) and if TRUE the @ctx
 *	    is no longer valid, i.e. you need to either call
 *	    ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it.
 *	    In that case PTR_ERR(@ctx->mrec) will give you the error code for
 *	    why the mapping of the old inode failed.
 *
 * 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.
@@ -72,11 +98,15 @@ extern s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn,
 *	      on return.
 *	    - This function takes the volume lcn bitmap lock for writing and
 *	      modifies the bitmap contents.
 *	    - 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.
 */
static inline s64 ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn,
		s64 count)
		s64 count, ntfs_attr_search_ctx *ctx)
{
	return __ntfs_cluster_free(ni, start_vcn, count, FALSE);
	return __ntfs_cluster_free(ni, start_vcn, count, ctx, FALSE);
}

extern int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol,
+9 −4
Original line number Diff line number Diff line
@@ -1952,20 +1952,21 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
		NVolSetErrors(vol);
		return ret;
	}
	a = ctx->attr;
	a->data.non_resident.highest_vcn = cpu_to_sle64(old_last_vcn - 1);
	ctx->attr->data.non_resident.highest_vcn =
			cpu_to_sle64(old_last_vcn - 1);
undo_alloc:
	if (ntfs_cluster_free(mft_ni, old_last_vcn, -1) < 0) {
	if (ntfs_cluster_free(mft_ni, old_last_vcn, -1, ctx) < 0) {
		ntfs_error(vol->sb, "Failed to free clusters from mft data "
				"attribute.%s", es);
		NVolSetErrors(vol);
	}
	a = ctx->attr;
	if (ntfs_rl_truncate_nolock(vol, &mft_ni->runlist, old_last_vcn)) {
		ntfs_error(vol->sb, "Failed to truncate mft data attribute "
				"runlist.%s", es);
		NVolSetErrors(vol);
	}
	if (mp_rebuilt) {
	if (mp_rebuilt && !IS_ERR(ctx->mrec)) {
		if (ntfs_mapping_pairs_build(vol, (u8*)a + le16_to_cpu(
				a->data.non_resident.mapping_pairs_offset),
				old_alen - le16_to_cpu(
@@ -1982,6 +1983,10 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
		}
		flush_dcache_mft_record_page(ctx->ntfs_ino);
		mark_mft_record_dirty(ctx->ntfs_ino);
	} else if (IS_ERR(ctx->mrec)) {
		ntfs_error(vol->sb, "Failed to restore attribute search "
				"context.%s", es);
		NVolSetErrors(vol);
	}
	if (ctx)
		ntfs_attr_put_search_ctx(ctx);