#include #include #include #include #include #include #include #include #include #include #include #include #include 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; }