273 lines
8.0 KiB
Plaintext
273 lines
8.0 KiB
Plaintext
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
#include <limits.h>
|
|
#include <float.h>
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
#include <locale.h>
|
|
#include <errno.h>
|
|
<signal.h> <setjmp.h> <time.h> <complex.h> <fenv.h> <inttypes.h> <stdbool.h> <stdalign.h> <stdatomic.h> <stdnoreturn.h>
|
|
auto bool break case char const continue default do double else enum
|
|
extern false float for goto if inline int long register
|
|
restrict return short signed sizeof static static_assert struct switch
|
|
typedef union unsigned void volatile while
|
|
#define #undef #if #ifdef #ifndef #elif #else #endif #include #define #undef #line #error #pragma
|
|
uint8_t uint16_t uint32_t uint64_t int8_t int16_t int32_t int64_t
|
|
fseek(f, 0, SEEK_END); size = ftell(f); rewind(f);
|
|
assert(size + B <= MEM);
|
|
assert(fread(&m[B], sizeof(unsigned char), size, f) == size);
|
|
fclose(f);
|
|
static int32_t mrlec(unsigned char * in, int32_t inlen, unsigned char * out) {
|
|
unsigned char * ip = in;
|
|
unsigned char * in_end = in + inlen;
|
|
int32_t op = 0;
|
|
int32_t c, pc = -1;
|
|
int32_t t[256] = { 0 };
|
|
int32_t run = 0;
|
|
while ((c = (ip < in_end ? *ip++ : -1)) != -1) {
|
|
if (c == pc)
|
|
t[c] += (++run % 255) != 0;
|
|
else
|
|
--t[c], run = 0;
|
|
pc = c;
|
|
}
|
|
for (int32_t i = 0; i < 32; ++i) {
|
|
c = 0;
|
|
for (int32_t j = 0; j < 8; ++j) c += (t[i * 8 + j] > 0) << j;
|
|
out[op++] = c;
|
|
}
|
|
ip = in;
|
|
c = pc = -1;
|
|
run = 0;
|
|
do {
|
|
c = ip < in_end ? *ip++ : -1;
|
|
if (c == pc)
|
|
++run;
|
|
else if (run > 0 && t[pc] > 0) {
|
|
out[op++] = pc;
|
|
for (; run > 255; run -= 255) out[op++] = 255;
|
|
out[op++] = run - 1;
|
|
run = 1;
|
|
} else
|
|
for (++run; run > 1; --run) out[op++] = pc;
|
|
pc = c;
|
|
} while (c != -1);
|
|
|
|
return op;
|
|
}
|
|
void put_int(int i) {
|
|
if (i < 0) {
|
|
putchar('-');
|
|
i *= -1;
|
|
}
|
|
char buf[10];
|
|
int j = 0;
|
|
do {
|
|
buf[j++] = i % 10 + '0';
|
|
i /= 10;
|
|
} while (i > 0);
|
|
while (j > 0) putchar(buf[--j]);
|
|
}
|
|
int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn, ntfs_attr_search_ctx *ctx)
|
|
{
|
|
VCN end_vcn;
|
|
unsigned long flags;
|
|
ntfs_inode *base_ni;
|
|
MFT_RECORD *m;
|
|
ATTR_RECORD *a;
|
|
runlist_element *rl;
|
|
struct page *put_this_page = NULL;
|
|
int err = 0;
|
|
bool ctx_is_temporary, ctx_needs_reset;
|
|
ntfs_attr_search_ctx old_ctx = { NULL, };
|
|
|
|
ntfs_debug("Mapping runlist part containing vcn 0x%llx.",
|
|
(unsigned long long)vcn);
|
|
if (!NInoAttr(ni))
|
|
base_ni = ni;
|
|
else
|
|
base_ni = ni->ext.base_ntfs_ino;
|
|
if (!ctx) {
|
|
ctx_is_temporary = ctx_needs_reset = true;
|
|
m = map_mft_record(base_ni);
|
|
if (IS_ERR(m))
|
|
return PTR_ERR(m);
|
|
ctx = ntfs_attr_get_search_ctx(base_ni, m);
|
|
if (unlikely(!ctx)) {
|
|
err = -ENOMEM;
|
|
goto err_out;
|
|
}
|
|
} else {
|
|
VCN allocated_size_vcn;
|
|
|
|
BUG_ON(IS_ERR(ctx->mrec));
|
|
a = ctx->attr;
|
|
BUG_ON(!a->non_resident);
|
|
ctx_is_temporary = false;
|
|
end_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn);
|
|
read_lock_irqsave(&ni->size_lock, flags);
|
|
allocated_size_vcn = ni->allocated_size >>
|
|
ni->vol->cluster_size_bits;
|
|
read_unlock_irqrestore(&ni->size_lock, flags);
|
|
if (!a->data.non_resident.lowest_vcn && end_vcn <= 0)
|
|
end_vcn = allocated_size_vcn - 1;
|
|
/*
|
|
* 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 &&
|
|
!memcmp((u8*)a + le16_to_cpu(a->name_offset),
|
|
ni->name, ni->name_len) &&
|
|
sle64_to_cpu(a->data.non_resident.lowest_vcn)
|
|
<= vcn && end_vcn >= vcn))
|
|
ctx_needs_reset = false;
|
|
else {
|
|
/* Save the old search context. */
|
|
old_ctx = *ctx;
|
|
/*
|
|
* If the currently mapped (extent) inode is not the
|
|
* base inode we will unmap it when we reinitialize the
|
|
* search context which means we need to get a
|
|
* reference to the page containing the mapped mft
|
|
* record so we do not accidentally drop changes to the
|
|
* mft record when it has not been marked dirty yet.
|
|
*/
|
|
if (old_ctx.base_ntfs_ino && old_ctx.ntfs_ino !=
|
|
old_ctx.base_ntfs_ino) {
|
|
put_this_page = old_ctx.ntfs_ino->page;
|
|
get_page(put_this_page);
|
|
}
|
|
/*
|
|
* Reinitialize the search context so we can lookup the
|
|
* needed attribute extent.
|
|
*/
|
|
ntfs_attr_reinit_search_ctx(ctx);
|
|
ctx_needs_reset = true;
|
|
}
|
|
}
|
|
if (ctx_needs_reset) {
|
|
err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
|
|
CASE_SENSITIVE, vcn, NULL, 0, ctx);
|
|
if (unlikely(err)) {
|
|
if (err == -ENOENT)
|
|
err = -EIO;
|
|
goto err_out;
|
|
}
|
|
BUG_ON(!ctx->attr->non_resident);
|
|
}
|
|
a = ctx->attr;
|
|
/*
|
|
* 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->data.non_resident.highest_vcn) + 1;
|
|
if (unlikely(vcn && vcn >= end_vcn)) {
|
|
err = -ENOENT;
|
|
goto err_out;
|
|
}
|
|
rl = ntfs_mapping_pairs_decompress(ni->vol, a, ni->runlist.rl);
|
|
if (IS_ERR(rl))
|
|
err = PTR_ERR(rl);
|
|
else
|
|
ni->runlist.rl = rl;
|
|
err_out:
|
|
if (ctx_is_temporary) {
|
|
if (likely(ctx))
|
|
ntfs_attr_put_search_ctx(ctx);
|
|
unmap_mft_record(base_ni);
|
|
} else if (ctx_needs_reset) {
|
|
/*
|
|
* If there is no attribute list, restoring the search context
|
|
* is accomplished 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->ntfs_ino != old_ctx.ntfs_ino) {
|
|
/*
|
|
* If the currently mapped inode is not the
|
|
* base inode, unmap it.
|
|
*/
|
|
if (ctx->base_ntfs_ino && ctx->ntfs_ino !=
|
|
ctx->base_ntfs_ino) {
|
|
unmap_extent_mft_record(ctx->ntfs_ino);
|
|
ctx->mrec = ctx->base_mrec;
|
|
BUG_ON(!ctx->mrec);
|
|
}
|
|
/*
|
|
* If the old mapped inode is not the base
|
|
* inode, map it.
|
|
*/
|
|
if (old_ctx.base_ntfs_ino &&
|
|
old_ctx.ntfs_ino !=
|
|
old_ctx.base_ntfs_ino) {
|
|
retry_map:
|
|
ctx->mrec = map_mft_record(
|
|
old_ctx.ntfs_ino);
|
|
/*
|
|
* 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->mrec.
|
|
* 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 (IS_ERR(ctx->mrec)) {
|
|
if (PTR_ERR(ctx->mrec) ==
|
|
-ENOMEM) {
|
|
schedule();
|
|
goto retry_map;
|
|
} else
|
|
old_ctx.ntfs_ino =
|
|
old_ctx.
|
|
base_ntfs_ino;
|
|
}
|
|
}
|
|
}
|
|
/* Update the changed pointers in the saved context. */
|
|
if (ctx->mrec != old_ctx.mrec) {
|
|
if (!IS_ERR(ctx->mrec))
|
|
old_ctx.attr = (ATTR_RECORD*)(
|
|
(u8*)ctx->mrec +
|
|
((u8*)old_ctx.attr -
|
|
(u8*)old_ctx.mrec));
|
|
old_ctx.mrec = ctx->mrec;
|
|
}
|
|
}
|
|
/* Restore the search context to the saved one. */
|
|
*ctx = old_ctx;
|
|
/*
|
|
* We drop the reference on the page we took earlier. In the
|
|
* case that IS_ERR(ctx->mrec) is true this means we might lose
|
|
* some changes to the mft record that had been made between
|
|
* the last time it was marked dirty/written out and now. This
|
|
* at this stage is not a problem as the mapping error is fatal
|
|
* enough that the mft record cannot be written out anyway and
|
|
* the caller is very likely to shutdown the whole inode
|
|
* immediately and mark the volume dirty for chkdsk to pick up
|
|
* the pieces anyway.
|
|
*/
|
|
if (put_this_page)
|
|
put_page(put_this_page);
|
|
}
|
|
return err;
|
|
} |