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

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
...@@ -24,10 +24,13 @@ ToDo/Notes: ...@@ -24,10 +24,13 @@ ToDo/Notes:
2.1.25-WIP 2.1.25-WIP
- Change ntfs_map_runlist_nolock() and ntfs_attr_find_vcn_nolock() to - Change ntfs_map_runlist_nolock(), ntfs_attr_find_vcn_nolock() and
also take an optional attribute search context as argument. This {__,}ntfs_cluster_free() to also take an optional attribute search
allows calling these functions with the mft record mapped. Update context as argument. This allows calling these functions with the
all callers. 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. 2.1.24 - Lots of bug fixes and support more clean journal states.
......
...@@ -782,6 +782,7 @@ switch_to_data1_zone: search_zone = 2; ...@@ -782,6 +782,7 @@ switch_to_data1_zone: search_zone = 2;
* @ni: ntfs inode whose runlist describes the clusters to free * @ni: ntfs inode whose runlist describes the clusters to free
* @start_vcn: vcn in the runlist of @ni at which to start freeing clusters * @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 * @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 * @is_rollback: true if this is a rollback operation
* *
* Free @count clusters starting at the cluster @start_vcn in the runlist * Free @count clusters starting at the cluster @start_vcn in the runlist
...@@ -791,15 +792,39 @@ switch_to_data1_zone: search_zone = 2; ...@@ -791,15 +792,39 @@ switch_to_data1_zone: search_zone = 2;
* deallocated. Thus, to completely free all clusters in a runlist, use * deallocated. Thus, to completely free all clusters in a runlist, use
* @start_vcn = 0 and @count = -1. * @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 * @is_rollback should always be FALSE, it is for internal use to rollback
* errors. You probably want to use ntfs_cluster_free() instead. * errors. You probably want to use ntfs_cluster_free() instead.
* *
* Note, ntfs_cluster_free() does not modify the runlist at all, so the caller * Note, __ntfs_cluster_free() does not modify the runlist, so you have to
* has to deal with it later. * remove from the runlist or mark sparse the freed runs later.
* *
* Return the number of deallocated clusters (not counting sparse ones) on * Return the number of deallocated clusters (not counting sparse ones) on
* success and -errno on error. * 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 * 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 * and is locked on return. Note the runlist may be modified when
* needed runlist fragments need to be mapped. * needed runlist fragments need to be mapped.
...@@ -807,9 +832,13 @@ switch_to_data1_zone: search_zone = 2; ...@@ -807,9 +832,13 @@ switch_to_data1_zone: search_zone = 2;
* on return. * on return.
* - This function takes the volume lcn bitmap lock for writing and * - This function takes the volume lcn bitmap lock for writing and
* modifies the bitmap contents. * 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, 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; s64 delta, to_free, total_freed, real_freed;
ntfs_volume *vol; ntfs_volume *vol;
...@@ -839,7 +868,7 @@ s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, s64 count, ...@@ -839,7 +868,7 @@ s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, s64 count,
total_freed = real_freed = 0; 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_ERR(rl)) {
if (!is_rollback) if (!is_rollback)
ntfs_error(vol->sb, "Failed to find first runlist " 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, ...@@ -893,7 +922,7 @@ s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, s64 count,
/* Attempt to map runlist. */ /* Attempt to map runlist. */
vcn = rl->vcn; 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)) { if (IS_ERR(rl)) {
err = PTR_ERR(rl); err = PTR_ERR(rl);
if (!is_rollback) if (!is_rollback)
...@@ -961,7 +990,7 @@ s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, s64 count, ...@@ -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 * If rollback fails, set the volume errors flag, emit an error
* message, and return the error code. * 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) { if (delta < 0) {
ntfs_error(vol->sb, "Failed to rollback (error %i). Leaving " ntfs_error(vol->sb, "Failed to rollback (error %i). Leaving "
"inconsistent metadata! Unmount and run " "inconsistent metadata! Unmount and run "
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include "attrib.h"
#include "types.h" #include "types.h"
#include "inode.h" #include "inode.h"
#include "runlist.h" #include "runlist.h"
...@@ -44,13 +45,14 @@ extern runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, ...@@ -44,13 +45,14 @@ extern runlist_element *ntfs_cluster_alloc(ntfs_volume *vol,
const NTFS_CLUSTER_ALLOCATION_ZONES zone); const NTFS_CLUSTER_ALLOCATION_ZONES zone);
extern s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, 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 * ntfs_cluster_free - free clusters on an ntfs volume
* @ni: ntfs inode whose runlist describes the clusters to free * @ni: ntfs inode whose runlist describes the clusters to free
* @start_vcn: vcn in the runlist of @ni at which to start freeing clusters * @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 * @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 * Free @count clusters starting at the cluster @start_vcn in the runlist
* described by the ntfs inode @ni. * described by the ntfs inode @ni.
...@@ -59,12 +61,36 @@ extern s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, ...@@ -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 * deallocated. Thus, to completely free all clusters in a runlist, use
* @start_vcn = 0 and @count = -1. * @start_vcn = 0 and @count = -1.
* *
* Note, ntfs_cluster_free() does not modify the runlist at all, so the caller * If @ctx is specified, it is an active search context of @ni and its base mft
* has to deal with it later. * 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 * Return the number of deallocated clusters (not counting sparse ones) on
* success and -errno on error. * 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 * 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 * and is locked on return. Note the runlist may be modified when
* needed runlist fragments need to be mapped. * needed runlist fragments need to be mapped.
...@@ -72,11 +98,15 @@ extern s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, ...@@ -72,11 +98,15 @@ extern s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn,
* on return. * on return.
* - This function takes the volume lcn bitmap lock for writing and * - This function takes the volume lcn bitmap lock for writing and
* modifies the bitmap contents. * 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, 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, extern int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol,
......
...@@ -1952,20 +1952,21 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1952,20 +1952,21 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
NVolSetErrors(vol); NVolSetErrors(vol);
return ret; return ret;
} }
a = ctx->attr; ctx->attr->data.non_resident.highest_vcn =
a->data.non_resident.highest_vcn = cpu_to_sle64(old_last_vcn - 1); cpu_to_sle64(old_last_vcn - 1);
undo_alloc: 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 " ntfs_error(vol->sb, "Failed to free clusters from mft data "
"attribute.%s", es); "attribute.%s", es);
NVolSetErrors(vol); NVolSetErrors(vol);
} }
a = ctx->attr;
if (ntfs_rl_truncate_nolock(vol, &mft_ni->runlist, old_last_vcn)) { if (ntfs_rl_truncate_nolock(vol, &mft_ni->runlist, old_last_vcn)) {
ntfs_error(vol->sb, "Failed to truncate mft data attribute " ntfs_error(vol->sb, "Failed to truncate mft data attribute "
"runlist.%s", es); "runlist.%s", es);
NVolSetErrors(vol); NVolSetErrors(vol);
} }
if (mp_rebuilt) { if (mp_rebuilt && !IS_ERR(ctx->mrec)) {
if (ntfs_mapping_pairs_build(vol, (u8*)a + le16_to_cpu( if (ntfs_mapping_pairs_build(vol, (u8*)a + le16_to_cpu(
a->data.non_resident.mapping_pairs_offset), a->data.non_resident.mapping_pairs_offset),
old_alen - le16_to_cpu( old_alen - le16_to_cpu(
...@@ -1982,6 +1983,10 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol) ...@@ -1982,6 +1983,10 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
} }
flush_dcache_mft_record_page(ctx->ntfs_ino); flush_dcache_mft_record_page(ctx->ntfs_ino);
mark_mft_record_dirty(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) if (ctx)
ntfs_attr_put_search_ctx(ctx); ntfs_attr_put_search_ctx(ctx);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment