#include <sys/buf.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ucred.h>
#include <sys/ubc.h>
#include <sys/vnode.h>
#include <string.h>
#include <libkern/libkern.h>
#include <libkern/OSAtomic.h>
#include <libkern/OSMalloc.h>
#include <kern/debug.h>
#include <kern/locks.h>
#include "ntfs.h"
#include "ntfs_attr.h"
#include "ntfs_bitmap.h"
#include "ntfs_debug.h"
#include "ntfs_dir.h"
#include "ntfs_endian.h"
#include "ntfs_hash.h"
#include "ntfs_inode.h"
#include "ntfs_layout.h"
#include "ntfs_lcnalloc.h"
#include "ntfs_mft.h"
#include "ntfs_page.h"
#include "ntfs_secure.h"
#include "ntfs_time.h"
#include "ntfs_types.h"
#include "ntfs_volume.h"
errno_t ntfs_mft_record_map_ext(ntfs_inode *ni, MFT_RECORD **mrec,
const BOOL mft_is_locked)
{
ntfs_volume *vol;
ntfs_inode *mft_ni;
buf_t buf;
MFT_RECORD *m;
errno_t err;
ntfs_debug("Entering for mft_no 0x%llx (mft is %slocked).",
(unsigned long long)ni->mft_no,
mft_is_locked ? "" : "not ");
if (NInoAttr(ni))
panic("%s(): Called for attribute inode.\n", __FUNCTION__);
vol = ni->vol;
mft_ni = vol->mft_ni;
if (!mft_ni) {
ntfs_error(vol->mp, "The volume is being unmounted, bailing "
"out (you can ignore any errors following "
"this one).");
return EINVAL;
}
err = vnode_get(mft_ni->vn);
if (err) {
ntfs_error(vol->mp, "Failed to get vnode for $MFT.");
return err;
}
if (!mft_is_locked)
lck_rw_lock_shared(&mft_ni->lock);
lck_spin_lock(&mft_ni->size_lock);
if (ni->mft_no > (ino64_t)(mft_ni->data_size >>
vol->mft_record_size_shift)) {
lck_spin_unlock(&mft_ni->size_lock);
ntfs_error(vol->mp, "Attempt to read mft record 0x%llx, which "
"is beyond the end of the mft.",
(unsigned long long)ni->mft_no);
err = ENOENT;
goto err;
}
lck_spin_unlock(&mft_ni->size_lock);
ntfs_debug("Calling buf_meta_bread().");
err = buf_meta_bread(mft_ni->vn, ni->mft_no, vol->mft_record_size,
NOCRED, &buf);
ntfs_debug("After buf_meta_bread().");
if (err) {
ntfs_error(vol->mp, "Failed to read buffer of mft record "
"0x%llx (error %d).",
(unsigned long long)ni->mft_no, err);
goto buf_err;
}
err = buf_map(buf, (caddr_t*)&m);
if (err) {
ntfs_error(vol->mp, "Failed to map buffer of mft record "
"0x%llx (error %d).",
(unsigned long long)ni->mft_no, err);
goto buf_err;
}
if (!m)
panic("%s(): buf_map() returned NULL.\n", __FUNCTION__);
if (ni->m_buf || ni->m)
panic("%s(): Mft record 0x%llx is already mapped.\n",
__FUNCTION__, (unsigned long long)ni->mft_no);
if (ntfs_is_mft_record(m->magic)) {
if (!mft_is_locked)
lck_rw_unlock_shared(&mft_ni->lock);
ni->mft_ni = mft_ni;
ni->m_buf = buf;
ni->m = m;
*mrec = m;
ntfs_debug("Done.");
return 0;
}
ntfs_error(vol->mp, "Mft record 0x%llx is corrupt. Run chkdsk.",
(unsigned long long)ni->mft_no);
NVolSetErrors(vol);
err = buf_unmap(buf);
if (err)
ntfs_error(vol->mp, "Failed to unmap buffer of mft record "
"0x%llx (error %d).",
(unsigned long long)ni->mft_no, err);
err = EIO;
buf_err:
buf_brelse(buf);
err:
if (!mft_is_locked)
lck_rw_unlock_shared(&mft_ni->lock);
(void)vnode_put(mft_ni->vn);
return err;
}
void ntfs_mft_record_unmap(ntfs_inode *ni)
{
ntfs_inode *mft_ni;
buf_t buf;
errno_t err;
ntfs_debug("Entering for mft_no 0x%llx.",
(unsigned long long)ni->mft_no);
mft_ni = ni->mft_ni;
buf = ni->m_buf;
if (!mft_ni || !buf || !ni->m)
panic("%s(): Mft record 0x%llx is not mapped.\n", __FUNCTION__,
(unsigned long long)ni->mft_no);
ni->mft_ni = NULL;
ni->m_buf = NULL;
ni->m = NULL;
err = buf_unmap(buf);
if (err)
ntfs_error(ni->vol->mp, "Failed to unmap buffer of mft record "
"0x%llx (error %d).",
(unsigned long long)ni->mft_no, err);
if (NInoTestClearMrecNeedsDirtying(ni)) {
err = buf_bdwrite(buf);
if (err) {
ntfs_error(ni->vol->mp, "Failed to write buffer of "
"mft record 0x%llx (error %d). Run "
"chkdsk.",
(unsigned long long)ni->mft_no, err);
NVolSetErrors(ni->vol);
}
} else
buf_brelse(buf);
(void)vnode_put(mft_ni->vn);
ntfs_debug("Done.");
}
errno_t ntfs_extent_mft_record_map_ext(ntfs_inode *base_ni, MFT_REF mref,
ntfs_inode **ext_ni, MFT_RECORD **ext_mrec,
const BOOL mft_is_locked)
{
ino64_t mft_no;
ntfs_inode **extent_nis = NULL;
ntfs_inode *ni = NULL;
MFT_RECORD *m;
errno_t err;
unsigned seq_no;
int i;
BOOL need_reclaim;
mft_no = MREF(mref);
seq_no = MSEQNO(mref);
ntfs_debug("Mapping extent mft record 0x%llx (base mft record "
"0x%llx).", (unsigned long long)mft_no,
(unsigned long long)base_ni->mft_no);
lck_mtx_lock(&base_ni->extent_lock);
if (base_ni->nr_extents > 0) {
extent_nis = base_ni->extent_nis;
for (i = 0; i < base_ni->nr_extents; i++) {
if (mft_no != extent_nis[i]->mft_no)
continue;
ni = extent_nis[i];
break;
}
}
if (ni) {
lck_mtx_unlock(&base_ni->extent_lock);
err = ntfs_mft_record_map_ext(ni, &m, mft_is_locked);
if (!err) {
if (!seq_no || le16_to_cpu(m->sequence_number) ==
seq_no) {
ntfs_debug("Done 1.");
*ext_ni = ni;
*ext_mrec = m;
return err;
}
ntfs_mft_record_unmap(ni);
ntfs_error(base_ni->vol->mp, "Found stale extent mft "
"reference! Corrupt file system. "
"Run chkdsk.");
return EIO;
}
map_err_out:
ntfs_error(base_ni->vol->mp, "Failed to map extent mft "
"record (error %d).", (int)err);
return err;
}
err = ntfs_extent_inode_get(base_ni, mref, &ni);
if (err) {
lck_mtx_unlock(&base_ni->extent_lock);
return err;
}
err = ntfs_mft_record_map_ext(ni, &m, mft_is_locked);
if (err) {
lck_mtx_unlock(&base_ni->extent_lock);
ntfs_inode_reclaim(ni);
goto map_err_out;
}
need_reclaim = FALSE;
if (seq_no) {
if (le16_to_cpu(m->sequence_number) != seq_no) {
ntfs_error(base_ni->vol->mp, "Found stale extent mft "
"reference! Corrupt file system. "
"Run chkdsk.");
need_reclaim = TRUE;
err = EIO;
goto unm_err_out;
}
} else {
ni->seq_no = le16_to_cpu(m->sequence_number);
}
if ((base_ni->nr_extents + 1) * sizeof(ntfs_inode *) >
base_ni->extent_alloc) {
ntfs_inode **tmp;
int new_size;
new_size = base_ni->extent_alloc + 4 * sizeof(ntfs_inode *);
tmp = OSMalloc(new_size, ntfs_malloc_tag);
if (!tmp) {
ntfs_error(base_ni->vol->mp, "Failed to allocate "
"internal buffer.");
need_reclaim = TRUE;
err = ENOMEM;
goto unm_err_out;
}
if (base_ni->extent_alloc) {
if (base_ni->nr_extents > 0)
memcpy(tmp, base_ni->extent_nis,
base_ni->nr_extents *
sizeof(ntfs_inode *));
OSFree(base_ni->extent_nis, base_ni->extent_alloc,
ntfs_malloc_tag);
}
base_ni->extent_alloc = new_size;
base_ni->extent_nis = tmp;
}
base_ni->extent_nis[base_ni->nr_extents++] = ni;
lck_mtx_unlock(&base_ni->extent_lock);
ntfs_debug("Done 2.");
*ext_ni = ni;
*ext_mrec = m;
return err;
unm_err_out:
ntfs_mft_record_unmap(ni);
lck_mtx_unlock(&base_ni->extent_lock);
if (need_reclaim)
ntfs_inode_reclaim(ni);
return err;
}
static const char es[] = " Leaving inconsistent metadata. Unmount and run "
"chkdsk.";
errno_t ntfs_mft_record_sync(ntfs_inode *ni)
{
ntfs_volume *vol;
ntfs_inode *mft_ni;
buf_t buf;
errno_t err;
if (NInoAttr(ni))
panic("%s(): Called for attribute inode.\n", __FUNCTION__);
ntfs_debug("Entering for mft record of %s inode 0x%llx.",
(ni->nr_extents >= 0) ? "base" : "extent",
(unsigned long long)ni->mft_no);
vol = ni->vol;
mft_ni = vol->mft_ni;
if (!mft_ni) {
ntfs_warning(vol->mp, "$MFT inode is missing from volume.");
return ENOTSUP;
}
err = vnode_get(mft_ni->vn);
if (err) {
ntfs_error(vol->mp, "Failed to get vnode for $MFT.");
return err;
}
lck_rw_lock_shared(&mft_ni->lock);
buf = buf_getblk(mft_ni->vn, ni->mft_no, vol->mft_record_size, 0, 0,
BLK_META | BLK_ONLYVALID);
lck_rw_unlock_shared(&mft_ni->lock);
(void)vnode_put(mft_ni->vn);
if (!buf) {
ntfs_debug("Mft record 0x%llx is not in cache, nothing to do.",
(unsigned long long)ni->mft_no);
return 0;
}
if (buf_size(buf) != vol->mft_record_size)
panic("%s(): Buffer containing mft record 0x%llx has wrong "
"size (0x%x instead of 0x%x).", __FUNCTION__,
(unsigned long long)ni->mft_no,
buf_size(buf), vol->mft_record_size);
if (!(buf_flags(buf) & B_DELWRI)) {
ntfs_debug("Mft record 0x%llx is in cache but not dirty, "
"nothing to do.",
(unsigned long long)ni->mft_no);
buf_brelse(buf);
return 0;
}
err = buf_bwrite(buf);
if (!err)
ntfs_debug("Done.");
else
ntfs_error(vol->mp, "Failed to write mft record 0x%llx (error "
"%d).", (unsigned long long)ni->mft_no, err);
return err;
}
errno_t ntfs_mft_mirror_sync(ntfs_volume *vol, const s64 rec_no,
const MFT_RECORD *m, const BOOL sync)
{
s64 data_size;
ntfs_inode *mirr_ni;
vnode_t mirr_vn;
buf_t buf;
MFT_RECORD *mirr;
errno_t err;
ntfs_debug("Entering for rec_no 0x%llx.", (unsigned long long)rec_no);
mirr_ni = vol->mftmirr_ni;
if (!mirr_ni) {
ntfs_error(vol->mp, "Umount time mft mirror syncing is not "
"implemented yet. %s", ntfs_please_email);
return ENOTSUP;
}
mirr_vn = mirr_ni->vn;
lck_rw_lock_shared(&mirr_ni->lock);
if (rec_no >= vol->mftmirr_size)
panic("%s(): rec_no >= vol->mftmirr_size\n", __FUNCTION__);
err = vnode_get(mirr_vn);
if (err) {
ntfs_error(vol->mp, "Failed to get vnode for mft mirror.");
goto err;
}
lck_spin_lock(&mirr_ni->size_lock);
data_size = ubc_getsize(mirr_vn);
if (data_size > mirr_ni->data_size)
data_size = mirr_ni->data_size;
if ((rec_no << vol->mft_record_size_shift) + vol->mft_record_size >
mirr_ni->initialized_size) {
lck_spin_unlock(&mirr_ni->size_lock);
ntfs_error(vol->mp, "Write past the initialized size of mft "
"mirror.");
err = EIO;
goto put;
}
lck_spin_unlock(&mirr_ni->size_lock);
buf = buf_getblk(mirr_vn, rec_no, vol->mft_record_size, 0, 0, BLK_META);
if (!buf)
panic("%s(): buf_getblk() returned NULL.\n", __FUNCTION__);
err = buf_map(buf, (caddr_t*)&mirr);
if (err) {
ntfs_error(vol->mp, "Failed to map buffer of mft mirror "
"record %lld (error %d).",
(unsigned long long)rec_no, err);
buf_brelse(buf);
goto put;
}
memcpy(mirr, m, vol->mft_record_size);
err = buf_unmap(buf);
if (err)
ntfs_error(vol->mp, "Failed to unmap buffer of mft mirror "
"record %lld (error %d).",
(unsigned long long)rec_no, err);
if (sync)
err = buf_bwrite(buf);
else
err = buf_bawrite(buf);
if (err)
ntfs_error(vol->mp, "Failed to write buffer of mft mirror "
"record %lld (error %d).",
(unsigned long long)rec_no, err);
put:
(void)vnode_put(mirr_vn);
err:
lck_rw_unlock_shared(&mirr_ni->lock);
if (!err)
ntfs_debug("Done.");
else {
ntfs_error(vol->mp, "Failed to synchronize mft mirror (error "
"code %d). Volume will be left marked dirty "
"on unmount. Run chkdsk.", err);
NVolSetErrors(vol);
}
return err;
}
static errno_t ntfs_mft_bitmap_find_and_alloc_free_rec_nolock(ntfs_volume *vol,
ntfs_inode *base_ni, s64 *mft_no)
{
s64 pass_end, ll, data_pos, pass_start, ofs;
ntfs_inode *mftbmp_ni;
upl_t upl;
upl_page_info_array_t pl;
u8 *buf, *byte;
unsigned page_ofs, size, bit;
u8 pass, b;
ntfs_debug("Searching for free mft record in the currently "
"initialized mft bitmap.");
mftbmp_ni = vol->mftbmp_ni;
if (!mftbmp_ni)
panic("%s: !mftbmp_ni\n", __FUNCTION__);
if (!vol->mft_ni)
panic("%s: !mft_ni\n", __FUNCTION__);
lck_spin_lock(&vol->mft_ni->size_lock);
pass_end = vol->mft_ni->allocated_size >> vol->mft_record_size_shift;
lck_spin_unlock(&vol->mft_ni->size_lock);
lck_spin_lock(&mftbmp_ni->size_lock);
ll = mftbmp_ni->initialized_size << 3;
lck_spin_unlock(&mftbmp_ni->size_lock);
if (pass_end > ll)
pass_end = ll;
pass = 1;
if (!base_ni)
data_pos = vol->mft_data_pos;
else
data_pos = base_ni->mft_no + 1;
if (data_pos < 24)
data_pos = 24;
if (data_pos >= pass_end) {
data_pos = 24;
pass = 2;
if (data_pos >= pass_end)
goto no_space;
}
pass_start = data_pos;
ntfs_debug("Starting bitmap search: pass %u, pass_start 0x%llx, "
"pass_end 0x%llx, data_pos 0x%llx.", (unsigned)pass,
(unsigned long long)pass_start,
(unsigned long long)pass_end,
(unsigned long long)data_pos);
do {
ofs = data_pos >> 3;
page_ofs = (unsigned)ofs & PAGE_MASK;
size = PAGE_SIZE - page_ofs;
ll = ((pass_end + 7) >> 3) - ofs;
if (size > ll)
size = ll;
size <<= 3;
if (size) {
errno_t err;
err = ntfs_page_map(mftbmp_ni, ofs & ~PAGE_MASK_64,
&upl, &pl, &buf, TRUE);
if (err) {
ntfs_error(vol->mp, "Failed to read mft "
"bitmap, aborting.");
return err;
}
buf += page_ofs;
bit = (unsigned)data_pos & 7;
data_pos &= ~7ULL;
ntfs_debug("Before inner for loop: size 0x%x, "
"data_pos 0x%llx, bit 0x%x", size,
(unsigned long long)data_pos, bit);
for (; bit < size && data_pos + bit < pass_end;
bit &= ~7, bit += 8) {
byte = buf + (bit >> 3);
if (*byte == 0xff)
continue;
b = ffs(~(unsigned long)*byte) - 1;
if (b < 8 && b >= (bit & 7)) {
ll = data_pos + (bit & ~7) + b;
if (ll > (1LL << 32)) {
ntfs_page_unmap(mftbmp_ni,
upl, pl, FALSE);
goto no_space;
}
*byte |= 1 << b;
ntfs_page_unmap(mftbmp_ni, upl, pl,
TRUE);
ntfs_debug("Done. (Found and "
"allocated mft record "
"0x%llx.)",
(unsigned long long)ll);
*mft_no = ll;
return 0;
}
}
ntfs_debug("After inner for loop: size 0x%x, "
"data_pos 0x%llx, bit 0x%x", size,
(unsigned long long)data_pos, bit);
data_pos += size;
ntfs_page_unmap(mftbmp_ni, upl, pl, FALSE);
continue;
}
if (pass >= 2)
break;
pass++;
pass_end = pass_start;
data_pos = pass_start = 24;
ntfs_debug("pass %u, pass_start 0x%llx, pass_end 0x%llx.",
(unsigned)pass, (unsigned long long)pass_start,
(unsigned long long)pass_end);
} while (data_pos < pass_end);
no_space:
ntfs_debug("Done. (No free mft records left in currently initialized "
"mft bitmap.)");
return ENOSPC;
}
static errno_t ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
{
VCN vcn, lowest_vcn = 0;
LCN lcn;
s64 allocated_size, ll;
ntfs_inode *mft_ni, *mftbmp_ni, *lcnbmp_ni;
ntfs_rl_element *rl;
upl_t upl;
upl_page_info_array_t pl;
u8 *kaddr, *b;
MFT_RECORD *m;
ntfs_attr_search_ctx *ctx;
ATTR_RECORD *a;
unsigned mp_size, attr_len = 0;
errno_t err, err2;
BOOL mp_rebuilt = FALSE;
u8 tb;
ntfs_debug("Extending mft bitmap allocation.");
mft_ni = vol->mft_ni;
mftbmp_ni = vol->mftbmp_ni;
lcnbmp_ni = vol->lcnbmp_ni;
lck_rw_lock_exclusive(&mftbmp_ni->rl.lock);
lck_spin_lock(&mftbmp_ni->size_lock);
allocated_size = mftbmp_ni->allocated_size;
lck_spin_unlock(&mftbmp_ni->size_lock);
vcn = (allocated_size - 1) >> vol->cluster_size_shift;
err = ntfs_attr_find_vcn_nolock(mftbmp_ni, vcn, &rl, NULL);
if (err || !rl || !rl->length || rl->lcn < 0 || rl[1].length ||
rl[1].vcn != vcn + 1) {
lck_rw_unlock_exclusive(&mftbmp_ni->rl.lock);
ntfs_error(vol->mp, "Failed to determine last allocated "
"cluster of mft bitmap attribute.");
if (!err)
err = EIO;
return err;
}
lcn = rl->lcn + rl->length;
ntfs_debug("Last lcn of mft bitmap attribute is 0x%llx.",
(unsigned long long)lcn);
lck_rw_lock_exclusive(&vol->lcnbmp_lock);
err = vnode_get(lcnbmp_ni->vn);
if (err) {
ntfs_error(vol->mp, "Failed to get vnode for $Bitmap.");
lck_rw_unlock_exclusive(&vol->lcnbmp_lock);
lck_rw_unlock_exclusive(&mftbmp_ni->rl.lock);
return err;
}
lck_rw_lock_shared(&lcnbmp_ni->lock);
ll = lcn >> 3;
err = ntfs_page_map(lcnbmp_ni, ll & ~PAGE_MASK_64, &upl, &pl, &kaddr,
TRUE);
if (err) {
lck_rw_unlock_shared(&lcnbmp_ni->lock);
(void)vnode_put(lcnbmp_ni->vn);
lck_rw_unlock_exclusive(&vol->lcnbmp_lock);
lck_rw_unlock_exclusive(&mftbmp_ni->rl.lock);
ntfs_error(vol->mp, "Failed to read from lcn bitmap.");
return err;
}
b = kaddr + ((unsigned)ll & PAGE_MASK);
tb = 1 << ((unsigned)lcn & 7);
if (*b != 0xff && !(*b & tb)) {
*b |= tb;
vol->nr_free_clusters--;
if (vol->nr_free_clusters < 0)
vol->nr_free_clusters = 0;
ntfs_page_unmap(lcnbmp_ni, upl, pl, TRUE);
lck_rw_unlock_shared(&lcnbmp_ni->lock);
(void)vnode_put(lcnbmp_ni->vn);
lck_rw_unlock_exclusive(&vol->lcnbmp_lock);
rl->length++;
rl[1].vcn++;
ntfs_debug("Appending one cluster to mft bitmap.");
} else {
ntfs_runlist runlist;
ntfs_page_unmap(lcnbmp_ni, upl, pl, FALSE);
lck_rw_unlock_shared(&lcnbmp_ni->lock);
(void)vnode_put(lcnbmp_ni->vn);
lck_rw_unlock_exclusive(&vol->lcnbmp_lock);
runlist.rl = NULL;
runlist.alloc = runlist.elements = 0;
err = ntfs_cluster_alloc(vol, vcn + 1, 1, lcn, DATA_ZONE,
TRUE, &runlist);
if (err) {
lck_rw_unlock_exclusive(&mftbmp_ni->rl.lock);
ntfs_error(vol->mp, "Failed to allocate a cluster for "
"the mft bitmap.");
if (err != ENOMEM && err != ENOSPC)
err = EIO;
return err;
}
err = ntfs_rl_merge(&mftbmp_ni->rl, &runlist);
if (err) {
lck_rw_unlock_exclusive(&mftbmp_ni->rl.lock);
ntfs_error(vol->mp, "Failed to merge runlists for mft "
"bitmap.");
if (err != ENOMEM)
err = EIO;
err2 = ntfs_cluster_free_from_rl(vol, runlist.rl, 0,
-1, NULL);
if (err2) {
ntfs_error(vol->mp, "Failed to release "
"allocated cluster (error "
"%d).%s", err2, es);
NVolSetErrors(vol);
}
OSFree(runlist.rl, runlist.alloc, ntfs_malloc_tag);
return err;
}
ntfs_debug("Adding one run to mft bitmap.");
}
err = ntfs_mft_record_map(mft_ni, &m);
if (err) {
ntfs_error(vol->mp, "Failed to map mft record.");
m = NULL;
ctx = NULL;
goto undo_alloc;
}
ctx = ntfs_attr_search_ctx_get(mft_ni, m);
if (!ctx) {
ntfs_error(vol->mp, "Failed to get search context.");
err = ENOMEM;
goto undo_alloc;
}
err = ntfs_attr_lookup(mftbmp_ni->type, mftbmp_ni->name,
mftbmp_ni->name_len, vcn, NULL, 0, ctx);
if (err) {
ntfs_error(vol->mp, "Failed to find last attribute extent of "
"mft bitmap attribute.");
if (err == ENOENT)
err = EIO;
goto undo_alloc;
}
m = ctx->m;
a = ctx->a;
lowest_vcn = sle64_to_cpu(a->lowest_vcn);
rl = ntfs_rl_find_vcn_nolock(mftbmp_ni->rl.rl, lowest_vcn);
if (!rl)
panic("%s(): !rl\n", __FUNCTION__);
if (!rl->length)
panic("%s(): !rl->length\n", __FUNCTION__);
if (rl->lcn < LCN_HOLE)
panic("%s(): rl->lcn < LCN_HOLE\n", __FUNCTION__);
err = ntfs_get_size_for_mapping_pairs(vol, rl, lowest_vcn, -1,
&mp_size);
if (err) {
ntfs_error(vol->mp, "Get size for mapping pairs failed for "
"mft bitmap attribute extent.");
goto undo_alloc;
}
attr_len = le32_to_cpu(a->length);
err = ntfs_attr_record_resize(m, a, mp_size +
le16_to_cpu(a->mapping_pairs_offset));
if (err) {
if (err != ENOSPC) {
ntfs_error(vol->mp, "Failed to resize attribute "
"record for mft bitmap attribute.");
goto undo_alloc;
}
ntfs_error(vol->mp, "Not enough space in this mft record to "
"accomodate extended mft bitmap attribute "
"extent. Cannot handle this yet.");
err = ENOTSUP;
goto undo_alloc;
}
mp_rebuilt = TRUE;
err = ntfs_mapping_pairs_build(vol, (s8*)a +
le16_to_cpu(a->mapping_pairs_offset), mp_size, rl,
lowest_vcn, -1, NULL);
if (err) {
ntfs_error(vol->mp, "Failed to build mapping pairs array for "
"mft bitmap attribute (error %d).", err);
err = EIO;
goto dirty_undo_alloc;
}
a->highest_vcn = cpu_to_sle64(vcn + 1);
if (a->lowest_vcn) {
NInoSetMrecNeedsDirtying(ctx->ni);
ntfs_attr_search_ctx_reinit(ctx);
err = ntfs_attr_lookup(mftbmp_ni->type, mftbmp_ni->name,
mftbmp_ni->name_len, 0, NULL, 0, ctx);
if (err)
goto restore_undo_alloc;
a = ctx->a;
}
lck_spin_lock(&mftbmp_ni->size_lock);
mftbmp_ni->allocated_size += vol->cluster_size;
a->allocated_size = cpu_to_sle64(mftbmp_ni->allocated_size);
lck_spin_unlock(&mftbmp_ni->size_lock);
NInoSetMrecNeedsDirtying(ctx->ni);
ntfs_attr_search_ctx_put(ctx);
ntfs_mft_record_unmap(mft_ni);
lck_rw_unlock_exclusive(&mftbmp_ni->rl.lock);
ntfs_debug("Done.");
return 0;
restore_undo_alloc:
ntfs_error(vol->mp, "Failed to find first attribute extent of mft "
"bitmap attribute.");
if (err == ENOENT)
err = EIO;
ntfs_attr_search_ctx_reinit(ctx);
err2 = ntfs_attr_lookup(mftbmp_ni->type, mftbmp_ni->name,
mftbmp_ni->name_len, vcn, NULL, 0, ctx);
if (err2) {
ntfs_error(vol->mp, "Failed to find last attribute extent of "
"mft bitmap attribute (error %d).%s", err2, es);
lck_spin_lock(&mftbmp_ni->size_lock);
mftbmp_ni->allocated_size += vol->cluster_size;
lck_spin_unlock(&mftbmp_ni->size_lock);
ntfs_attr_search_ctx_put(ctx);
ntfs_mft_record_unmap(mft_ni);
lck_rw_unlock_exclusive(&mftbmp_ni->rl.lock);
NVolSetErrors(vol);
return err;
}
ctx->a->highest_vcn = cpu_to_sle64(vcn);
dirty_undo_alloc:
NInoSetMrecNeedsDirtying(ctx->ni);
undo_alloc:
err2 = ntfs_cluster_free(mftbmp_ni, vcn + 1, -1, ctx, NULL);
if (err2 || ctx->is_error) {
ntfs_error(vol->mp, "Failed to release allocated cluster in "
"error code path (error %d).%s",
ctx->is_error ? ctx->error : err2, es);
NVolSetErrors(vol);
}
err2 = ntfs_rl_truncate_nolock(vol, &mftbmp_ni->rl, vcn + 1);
if (err2) {
ntfs_error(vol->mp, "Failed to truncate attribute runlist s "
"in error code path (error %d).%s", err2, es);
NVolSetErrors(vol);
} else if (mp_rebuilt) {
a = ctx->a;
err2 = ntfs_attr_record_resize(ctx->m, a, attr_len);
if (err2) {
ntfs_error(vol->mp, "Failed to restore attribute "
"record in error code path (error "
"%d).%s", err2, es);
NVolSetErrors(vol);
} else {
u16 mp_ofs = le16_to_cpu(a->mapping_pairs_offset);
err2 = ntfs_mapping_pairs_build(vol, (s8*)a + mp_ofs,
attr_len - mp_ofs, mftbmp_ni->rl.rl,
lowest_vcn, -1, NULL);
if (err2) {
ntfs_error(vol->mp, "Failed to restore "
"mapping pairs array in error "
"code path (error %d).%s",
err2, es);
NVolSetErrors(vol);
}
NInoSetMrecNeedsDirtying(ctx->ni);
}
}
if (ctx)
ntfs_attr_search_ctx_put(ctx);
if (m)
ntfs_mft_record_unmap(mft_ni);
lck_rw_unlock_exclusive(&mftbmp_ni->rl.lock);
return err;
}
static errno_t ntfs_mft_bitmap_extend_initialized_nolock(ntfs_volume *vol)
{
s64 old_data_size, old_initialized_size;
ntfs_inode *mft_ni, *mftbmp_ni;
MFT_RECORD *m;
ntfs_attr_search_ctx *ctx;
ATTR_RECORD *a;
errno_t err, err2;
ntfs_debug("Extending mft bitmap initiailized (and data) size.");
mft_ni = vol->mft_ni;
mftbmp_ni = vol->mftbmp_ni;
err = ntfs_mft_record_map(mft_ni, &m);
if (err) {
ntfs_error(vol->mp, "Failed to map mft record.");
return err;
}
ctx = ntfs_attr_search_ctx_get(mft_ni, m);
if (!ctx) {
ntfs_error(vol->mp, "Failed to get search context.");
err = ENOMEM;
goto unm_err;
}
err = ntfs_attr_lookup(mftbmp_ni->type, mftbmp_ni->name,
mftbmp_ni->name_len, 0, NULL, 0, ctx);
if (err) {
ntfs_error(vol->mp, "Failed to find first attribute extent of "
"mft bitmap attribute.");
if (err == ENOENT)
err = EIO;
goto put_err;
}
a = ctx->a;
lck_spin_lock(&mftbmp_ni->size_lock);
old_data_size = mftbmp_ni->data_size;
old_initialized_size = mftbmp_ni->initialized_size;
mftbmp_ni->initialized_size += 8;
a->initialized_size = cpu_to_sle64(mftbmp_ni->initialized_size);
if (mftbmp_ni->initialized_size > old_data_size) {
const s64 init_size = mftbmp_ni->initialized_size;
mftbmp_ni->data_size = init_size;
a->data_size = cpu_to_sle64(init_size);
lck_spin_unlock(&mftbmp_ni->size_lock);
if (!ubc_setsize(mftbmp_ni->vn, init_size))
panic("%s(): !ubc_setsize(mftbmp_ni->vn, init_size)\n",
__FUNCTION__);
} else
lck_spin_unlock(&mftbmp_ni->size_lock);
NInoSetMrecNeedsDirtying(ctx->ni);
ntfs_attr_search_ctx_put(ctx);
ntfs_mft_record_unmap(mft_ni);
err = ntfs_attr_set(mftbmp_ni, old_initialized_size, 8, 0);
if (!err) {
ntfs_debug("Done. (Wrote eight initialized bytes to mft "
"bitmap.");
return 0;
}
ntfs_error(vol->mp, "Failed to write to mft bitmap.");
err2 = ntfs_mft_record_map(mft_ni, &m);
if (err2) {
ntfs_error(vol->mp, "Failed to map mft record in error code "
"path (error %d).%s", err2, es);
NVolSetErrors(vol);
return err;
}
ctx = ntfs_attr_search_ctx_get(mft_ni, m);
if (!ctx) {
ntfs_error(vol->mp, "Failed to get search context.%s", es);
NVolSetErrors(vol);
goto unm_err;
}
err2 = ntfs_attr_lookup(mftbmp_ni->type, mftbmp_ni->name,
mftbmp_ni->name_len, 0, NULL, 0, ctx);
if (err2) {
ntfs_error(vol->mp, "Failed to find first attribute extent of "
"mft bitmap attribute in error code path "
"(error %d).%s", err2, es);
NVolSetErrors(vol);
goto put_err;
}
a = ctx->a;
lck_spin_lock(&mftbmp_ni->size_lock);
mftbmp_ni->initialized_size = old_initialized_size;
a->initialized_size = cpu_to_sle64(old_initialized_size);
if (ubc_getsize(mftbmp_ni->vn) != old_data_size) {
mftbmp_ni->data_size = old_data_size;
a->data_size = cpu_to_sle64(old_data_size);
lck_spin_unlock(&mftbmp_ni->size_lock);
if (!ubc_setsize(mftbmp_ni->vn, old_data_size))
ntfs_error(vol->mp, "Failed to restore UBC size. "
"Leaving UBC size out of sync with "
"attribute data size.");
} else
lck_spin_unlock(&mftbmp_ni->size_lock);
NInoSetMrecNeedsDirtying(ctx->ni);
#ifdef DEBUG
lck_spin_lock(&mftbmp_ni->size_lock);
ntfs_debug("Restored status of mftbmp: allocated_size 0x%llx, "
"data_size 0x%llx, initialized_size 0x%llx.",
(unsigned long long)mftbmp_ni->allocated_size,
(unsigned long long)mftbmp_ni->data_size,
(unsigned long long)mftbmp_ni->initialized_size);
lck_spin_unlock(&mftbmp_ni->size_lock);
#endif
put_err:
ntfs_attr_search_ctx_put(ctx);
unm_err:
ntfs_mft_record_unmap(mft_ni);
return err;
}
static errno_t ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
{
VCN vcn, lowest_vcn = 0;
LCN lcn;
s64 allocated_size, min_nr, nr;
ntfs_inode *mft_ni;
ntfs_rl_element *rl;
MFT_RECORD *m;
ntfs_attr_search_ctx *ctx;
ATTR_RECORD *a;
unsigned mp_size, attr_len = 0;
errno_t err, err2;
BOOL mp_rebuilt = FALSE;
ntfs_runlist runlist;
ntfs_debug("Extending mft data allocation.");
mft_ni = vol->mft_ni;
lck_spin_lock(&mft_ni->size_lock);
allocated_size = mft_ni->allocated_size;
lck_spin_unlock(&mft_ni->size_lock);
vcn = (allocated_size - 1) >> vol->cluster_size_shift;
lck_rw_lock_exclusive(&mft_ni->rl.lock);
if (mft_ni->rl.elements > 1)
rl = &mft_ni->rl.rl[mft_ni->rl.elements - 2];
else
rl = mft_ni->rl.rl;
if (!rl || !rl->length || rl->lcn < 0 || rl[1].length ||
rl[1].vcn != vcn + 1) {
ntfs_error(vol->mp, "Failed to determine last allocated "
"cluster of mft data attribute.");
lck_rw_unlock_exclusive(&mft_ni->rl.lock);
return EIO;
}
lcn = rl->lcn + rl->length;
ntfs_debug("Last lcn of mft data attribute is 0x%llx.",
(unsigned long long)lcn);
min_nr = vol->mft_record_size >> vol->cluster_size_shift;
if (!min_nr)
min_nr = 1;
nr = (vol->mft_record_size * 16) / vol->cluster_size;
if (!nr)
nr = min_nr;
if ((allocated_size + (nr << vol->cluster_size_shift)) >>
vol->mft_record_size_shift >= (1LL << 32)) {
nr = min_nr;
if ((allocated_size + (nr << vol->cluster_size_shift)) >>
vol->mft_record_size_shift >= (1LL << 32)) {
ntfs_warning(vol->mp, "Cannot allocate mft record "
"because the maximum number of inodes "
"(2^32) has already been reached.");
lck_rw_unlock_exclusive(&mft_ni->rl.lock);
return ENOSPC;
}
}
ntfs_debug("Trying mft data allocation with %s cluster count %lld.",
nr > min_nr ? "default" : "minimal", (long long)nr);
do {
runlist.rl = NULL;
runlist.alloc = runlist.elements = 0;
err = ntfs_cluster_alloc(vol, vcn + 1, nr, lcn, MFT_ZONE,
TRUE, &runlist);
if (!err)
break;
if (err != ENOSPC || nr == min_nr) {
if (err != ENOMEM && err != ENOSPC)
err = EIO;
ntfs_error(vol->mp, "Failed to allocate the minimal "
"number of clusters (%lld) for the "
"mft data attribute.", (long long)nr);
lck_rw_unlock_exclusive(&mft_ni->rl.lock);
return err;
}
nr = min_nr;
ntfs_debug("Retrying mft data allocation with minimal cluster "
"count %lld.", (long long)nr);
} while (1);
err = ntfs_rl_merge(&mft_ni->rl, &runlist);
if (err) {
lck_rw_unlock_exclusive(&mft_ni->rl.lock);
ntfs_error(vol->mp, "Failed to merge runlists for mft data "
"attribute.");
if (err != ENOMEM)
err = EIO;
err2 = ntfs_cluster_free_from_rl(vol, runlist.rl, 0, -1, NULL);
if (err2) {
ntfs_error(vol->mp, "Failed to release allocated "
"cluster(s) (error %d).%s", err2, es);
NVolSetErrors(vol);
}
OSFree(runlist.rl, runlist.alloc, ntfs_malloc_tag);
return err;
}
ntfs_debug("Allocated %lld clusters.", (long long)nr);
lck_spin_lock(&mft_ni->size_lock);
mft_ni->allocated_size += nr << vol->cluster_size_shift;
lck_spin_unlock(&mft_ni->size_lock);
lck_rw_unlock_exclusive(&mft_ni->rl.lock);
err = ntfs_mft_record_map_ext(mft_ni, &m, TRUE);
if (err) {
ntfs_error(vol->mp, "Failed to map mft record.");
m = NULL;
ctx = NULL;
goto undo_alloc;
}
ctx = ntfs_attr_search_ctx_get(mft_ni, m);
if (!ctx) {
ntfs_error(vol->mp, "Failed to get search context.");
err = ENOMEM;
goto undo_alloc;
}
ctx->is_mft_locked = 1;
err = ntfs_attr_lookup(mft_ni->type, mft_ni->name, mft_ni->name_len,
vcn, NULL, 0, ctx);
if (err) {
ntfs_error(vol->mp, "Failed to find last attribute extent of "
"mft data attribute.");
if (err == ENOENT)
err = EIO;
goto undo_alloc;
}
m = ctx->m;
a = ctx->a;
lowest_vcn = sle64_to_cpu(a->lowest_vcn);
rl = ntfs_rl_find_vcn_nolock(mft_ni->rl.rl, lowest_vcn);
if (!rl)
panic("%s(): !rl\n", __FUNCTION__);
if (!rl->length)
panic("%s(): !rl->length\n", __FUNCTION__);
if (rl->lcn < LCN_HOLE)
panic("%s(): rl->lcn < LCN_HOLE\n", __FUNCTION__);
err = ntfs_get_size_for_mapping_pairs(vol, rl, lowest_vcn, -1,
&mp_size);
if (err) {
ntfs_error(vol->mp, "Get size for mapping pairs failed for "
"mft data attribute extent.");
goto undo_alloc;
}
attr_len = (int)le32_to_cpu(a->length);
err = ntfs_attr_record_resize(m, a, mp_size +
le16_to_cpu(a->mapping_pairs_offset));
if (err) {
if (err != ENOSPC) {
ntfs_error(vol->mp, "Failed to resize attribute "
"record for mft data attribute.");
goto undo_alloc;
}
ntfs_error(vol->mp, "Not enough space in this mft record to "
"accomodate extended mft data attribute "
"extent. Cannot handle this yet.");
err = ENOTSUP;
goto undo_alloc;
}
mp_rebuilt = TRUE;
err = ntfs_mapping_pairs_build(vol, (s8*)a +
le16_to_cpu(a->mapping_pairs_offset), mp_size, rl,
lowest_vcn, -1, NULL);
if (err) {
ntfs_error(vol->mp, "Failed to build mapping pairs array of "
"mft data attribute (error %d).", err);
err = EIO;
goto dirty_undo_alloc;
}
a->highest_vcn = cpu_to_sle64(vcn + nr);
if (a->lowest_vcn) {
NInoSetMrecNeedsDirtying(ctx->ni);
ntfs_attr_search_ctx_reinit(ctx);
err = ntfs_attr_lookup(mft_ni->type, mft_ni->name,
mft_ni->name_len, 0, NULL, 0, ctx);
if (err)
goto restore_undo_alloc;
a = ctx->a;
}
a->allocated_size = cpu_to_sle64(mft_ni->allocated_size);
NInoSetMrecNeedsDirtying(ctx->ni);
ntfs_attr_search_ctx_put(ctx);
ntfs_mft_record_unmap(mft_ni);
NInoSetDirtySizes(mft_ni);
ntfs_debug("Done.");
return 0;
restore_undo_alloc:
ntfs_error(vol->mp, "Failed to find first attribute extent of mft "
"data attribute.");
if (err == ENOENT)
err = EIO;
ntfs_attr_search_ctx_reinit(ctx);
err2 = ntfs_attr_lookup(mft_ni->type, mft_ni->name, mft_ni->name_len,
vcn, NULL, 0, ctx);
if (err2) {
ntfs_error(vol->mp, "Failed to find last attribute extent of "
"mft data attribute (error %d).%s", err2, es);
ntfs_attr_search_ctx_put(ctx);
ntfs_mft_record_unmap(mft_ni);
NVolSetErrors(vol);
return err;
}
ctx->a->highest_vcn = cpu_to_sle64(vcn);
dirty_undo_alloc:
NInoSetMrecNeedsDirtying(ctx->ni);
undo_alloc:
err2 = ntfs_cluster_free(mft_ni, vcn + 1, -1, ctx, NULL);
if (err2 || ctx->is_error) {
ntfs_error(vol->mp, "Failed to release allocated cluster(s) "
"in error code path (error %d).%s",
ctx->is_error ? ctx->error : err2, es);
NVolSetErrors(vol);
}
lck_rw_lock_exclusive(&mft_ni->rl.lock);
lck_spin_lock(&mft_ni->size_lock);
mft_ni->allocated_size -= nr << vol->cluster_size_shift;
lck_spin_unlock(&mft_ni->size_lock);
err2 = ntfs_rl_truncate_nolock(vol, &mft_ni->rl, vcn + 1);
lck_rw_unlock_exclusive(&mft_ni->rl.lock);
if (err2) {
ntfs_error(vol->mp, "Failed to truncate attribute runlist s "
"in error code path (error %d).%s", err2, es);
NVolSetErrors(vol);
} else if (mp_rebuilt) {
a = ctx->a;
err2 = ntfs_attr_record_resize(ctx->m, a, attr_len);
if (err2) {
ntfs_error(vol->mp, "Failed to restore attribute "
"record in error code path (error "
"%d).%s", err2, es);
NVolSetErrors(vol);
} else {
u16 mp_ofs = le16_to_cpu(a->mapping_pairs_offset);
err2 = ntfs_mapping_pairs_build(vol, (s8*)a + mp_ofs,
attr_len - mp_ofs, mft_ni->rl.rl,
lowest_vcn, -1, NULL);
if (err2) {
ntfs_error(vol->mp, "Failed to restore "
"mapping pairs array in error "
"code path (error %d).%s",
err2, es);
NVolSetErrors(vol);
}
NInoSetMrecNeedsDirtying(ctx->ni);
}
}
if (ctx)
ntfs_attr_search_ctx_put(ctx);
if (m)
ntfs_mft_record_unmap(mft_ni);
return err;
}
static errno_t ntfs_mft_record_lay_out(const ntfs_volume *vol,
const s64 mft_no, MFT_RECORD *m)
{
ATTR_RECORD *a;
ntfs_debug("Entering for mft record 0x%llx.",
(unsigned long long)mft_no);
if (mft_no >= (1LL << 32)) {
ntfs_error(vol->mp, "Mft record number 0x%llx exceeds "
"maximum of 2^32.",
(unsigned long long)mft_no);
return ERANGE;
}
if (vol->mft_record_size < NTFS_BLOCK_SIZE)
panic("%s(): vol->mft_record_size < NTFS_BLOCK_SIZE\n",
__FUNCTION__);
bzero(m, vol->mft_record_size);
if (vol->major_ver < 3 || (vol->major_ver == 3 && !vol->minor_ver))
m->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD_OLD) + 1) & ~1);
else {
m->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD) + 1) & ~1);
m->mft_record_number = cpu_to_le32((u32)mft_no);
}
m->magic = magic_FILE;
m->usa_count = cpu_to_le16(1 + vol->mft_record_size / NTFS_BLOCK_SIZE);
*(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = cpu_to_le16(1);
m->sequence_number = cpu_to_le16(1);
m->attrs_offset = cpu_to_le16((le16_to_cpu(m->usa_ofs) +
(le16_to_cpu(m->usa_count) << 1) + 7) & ~7);
m->bytes_in_use = cpu_to_le32(le16_to_cpu(m->attrs_offset) + 8);
m->bytes_allocated = cpu_to_le32(vol->mft_record_size);
a = (ATTR_RECORD*)((u8*)m + le16_to_cpu(m->attrs_offset));
a->type = AT_END;
ntfs_debug("Done.");
return 0;
}
static errno_t ntfs_mft_record_format(ntfs_volume *vol, const s64 mft_no,
const s64 new_initialized_size)
{
ntfs_inode *mft_ni;
buf_t buf;
MFT_RECORD *m;
errno_t err, err2;
ntfs_debug("Entering for mft record 0x%llx.",
(unsigned long long)mft_no);
mft_ni = vol->mft_ni;
if ((mft_no << vol->mft_record_size_shift) + vol->mft_record_size >
ubc_getsize(mft_ni->vn)) {
ntfs_error(vol->mp, "Tried to format non-existing mft "
"record 0x%llx.", (unsigned long long)mft_no);
return ENOENT;
}
err = buf_meta_bread(mft_ni->vn, mft_no, vol->mft_record_size, NOCRED,
&buf);
if (err) {
ntfs_error(vol->mp, "Failed to read buffer of mft record "
"0x%llx (error %d).",
(unsigned long long)mft_no, err);
goto brelse;
}
err = buf_map(buf, (caddr_t*)&m);
if (err) {
ntfs_error(vol->mp, "Failed to map buffer of mft record "
"0x%llx (error %d).",
(unsigned long long)mft_no, err);
goto brelse;
}
err = ntfs_mft_record_lay_out(vol, mft_no, m);
if (err) {
ntfs_error(vol->mp, "Failed to lay out mft record 0x%llx "
"(error %d).", (unsigned long long)mft_no, err);
goto unmap;
}
err = buf_unmap(buf);
if (err) {
ntfs_error(vol->mp, "Failed to unmap buffer of mft record "
"0x%llx (error %d).",
(unsigned long long)mft_no, err);
goto brelse;
}
lck_spin_lock(&mft_ni->size_lock);
if (new_initialized_size < mft_ni->initialized_size ||
new_initialized_size > mft_ni->data_size)
panic("%s(): new_initialized_size < mft_ni->initialized_size "
"|| new_initialized_size > mft_ni->data_size\n",
__FUNCTION__);
mft_ni->initialized_size = new_initialized_size;
lck_spin_unlock(&mft_ni->size_lock);
err = buf_bdwrite(buf);
if (!err) {
ntfs_debug("Done.");
return 0;
}
ntfs_error(vol->mp, "Failed to write buffer of mft record 0x%llx "
"(error %d). Run chkdsk.", (unsigned long long)mft_no,
err);
NVolSetErrors(vol);
return err;
unmap:
err2 = buf_unmap(buf);
if (err2)
ntfs_error(vol->mp, "Failed to unmap buffer of mft record "
"0x%llx in error code path (error %d).",
(unsigned long long)mft_no, err2);
brelse:
buf_brelse(buf);
return err;
}
static void ntfs_standard_info_attribute_insert(MFT_RECORD *m, ATTR_RECORD *a,
const FILE_ATTR_FLAGS file_attrs, const le32 security_id,
struct timespec *create_time)
{
STANDARD_INFORMATION *si;
u32 size;
ntfs_debug("Entering.");
size = sizeof(STANDARD_INFORMATION);
if (!security_id)
size = offsetof(STANDARD_INFORMATION, reserved12) +
sizeof(si->reserved12);
if (ntfs_resident_attr_record_insert_internal(m, a,
AT_STANDARD_INFORMATION, NULL, 0, size))
panic("%s(): Failed to insert standard information "
"attribute.\n", __FUNCTION__);
si = (STANDARD_INFORMATION*)((u8*)a + le16_to_cpu(a->value_offset));
si->last_access_time = si->last_mft_change_time =
si->last_data_change_time = si->creation_time =
utc2ntfs(*create_time);
si->file_attributes = file_attrs;
if (security_id)
si->security_id = security_id;
ntfs_debug("Done (used %s style standard information attribute).",
security_id ? "Win2k+" : "NT4");
}
static void ntfs_sd_attribute_insert(ntfs_volume *vol, MFT_RECORD *m,
ATTR_RECORD *a, const struct vnode_attr *va)
{
SDS_ENTRY *sds;
u32 sd_size;
ntfs_debug("Entering.");
if (vol->major_ver > 1) {
if (va->va_type == VDIR)
sds = ntfs_dir_sds_entry;
else
sds = ntfs_file_sds_entry;
} else {
if (va->va_type == VDIR)
sds = ntfs_dir_sds_entry_old;
else
sds = ntfs_file_sds_entry_old;
}
sd_size = le32_to_cpu(sds->length) - sizeof(SDS_ENTRY_HEADER);
if (ntfs_resident_attr_record_insert_internal(m, a,
AT_SECURITY_DESCRIPTOR, NULL, 0, sd_size))
panic("%s(): Failed to insert security descriptor "
"attribute.\n", __FUNCTION__);
memcpy((u8*)a + le16_to_cpu(a->value_offset), &sds->sd, sd_size);
ntfs_debug("Done.");
}
static void ntfs_index_root_attribute_insert(ntfs_volume *vol, MFT_RECORD *m,
ATTR_RECORD *a)
{
INDEX_ROOT *ir;
INDEX_ENTRY_HEADER *ieh;
ntfs_debug("Entering.");
if (ntfs_resident_attr_record_insert_internal(m, a, AT_INDEX_ROOT, I30,
4, sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY_HEADER)))
panic("%s(): Failed to insert index root attribute.\n",
__FUNCTION__);
ir = (INDEX_ROOT*)((u8*)a + le16_to_cpu(a->value_offset));
ir->type = AT_FILENAME;
ir->collation_rule = COLLATION_FILENAME;
ir->index_block_size = cpu_to_le32(vol->index_block_size);
ir->blocks_per_index_block = vol->blocks_per_index_block;
ir->index.entries_offset = const_cpu_to_le32(sizeof(INDEX_HEADER));
ir->index.allocated_size = ir->index.index_length = const_cpu_to_le32(
sizeof(INDEX_HEADER) + sizeof(INDEX_ENTRY_HEADER));
ieh = (INDEX_ENTRY_HEADER*)((u8*)ir + sizeof(INDEX_ROOT));
ieh->length = const_cpu_to_le16(sizeof(INDEX_ENTRY_HEADER));
ieh->flags = INDEX_ENTRY_END;
ntfs_debug("Done.");
}
errno_t ntfs_mft_record_alloc(ntfs_volume *vol, struct vnode_attr *va,
struct componentname *cn, ntfs_inode *base_ni,
ntfs_inode **new_ni, MFT_RECORD **new_m,
ATTR_RECORD **new_a)
{
s64 bit, ll, old_data_initialized, old_data_size, old_mft_data_pos;
s64 nr_mft_records_added;
ntfs_inode *mft_ni, *mftbmp_ni, *ni;
MFT_RECORD *m;
ntfs_attr_search_ctx *ctx;
ATTR_RECORD *a;
buf_t buf;
errno_t err, err2;
le16 seq_no, usn;
BOOL record_formatted, mark_sizes_dirty, dirty_buf;
BOOL mft_ni_write_locked;
ntfs_debug("Entering (allocating a%s mft record, %s 0x%llx).",
va ? " base" : "n extent",
va ? "parent directory" : "base mft record",
(unsigned long long)base_ni->mft_no);
if (!new_ni || !new_m || !new_a)
panic("%s(): !new_ni || !new_m || !new_a\n", __FUNCTION__);
if (!base_ni)
panic("%s(): !base_ni\n", __FUNCTION__);
lck_rw_lock_exclusive(&vol->mftbmp_lock);
mft_ni = vol->mft_ni;
if (va) {
err = vnode_get(mft_ni->vn);
if (err) {
ntfs_error(vol->mp, "Failed to get vnode for $MFT.");
lck_rw_unlock_exclusive(&vol->mftbmp_lock);
return err;
}
}
mftbmp_ni = vol->mftbmp_ni;
err = vnode_get(mftbmp_ni->vn);
if (err) {
ntfs_error(vol->mp, "Failed to get vnode for $MFT/$Bitmap.");
if (va)
(void)vnode_put(mft_ni->vn);
lck_rw_unlock_exclusive(&vol->mftbmp_lock);
return err;
}
retry_mftbmp_alloc:
record_formatted = mark_sizes_dirty = dirty_buf = FALSE;
lck_rw_lock_exclusive(&mftbmp_ni->lock);
err = ntfs_mft_bitmap_find_and_alloc_free_rec_nolock(vol,
va ? NULL : base_ni, &bit);
if (!err) {
ntfs_debug("Found and allocated free record (#1), bit 0x%llx.",
(unsigned long long)bit);
goto have_alloc_rec;
}
if (err != ENOSPC)
goto unl_err;
lck_spin_lock(&mft_ni->size_lock);
ll = mft_ni->initialized_size >> vol->mft_record_size_shift;
lck_spin_unlock(&mft_ni->size_lock);
lck_spin_lock(&mftbmp_ni->size_lock);
old_data_initialized = mftbmp_ni->initialized_size;
lck_spin_unlock(&mftbmp_ni->size_lock);
if (old_data_initialized << 3 > ll && old_data_initialized > 3) {
bit = ll;
if (bit < 24)
bit = 24;
if (bit >= (1LL << 32))
goto max_err;
ntfs_debug("Found free record (#2), bit 0x%llx.",
(unsigned long long)bit);
goto found_free_rec;
}
bit = old_data_initialized << 3;
if (bit >= (1LL << 32))
goto max_err;
lck_spin_lock(&mftbmp_ni->size_lock);
old_data_size = mftbmp_ni->allocated_size;
ntfs_debug("Status of mftbmp before extension: allocated_size 0x%llx, "
"data_size 0x%llx, initialized_size 0x%llx.",
(unsigned long long)old_data_size,
(unsigned long long)mftbmp_ni->data_size,
(unsigned long long)old_data_initialized);
lck_spin_unlock(&mftbmp_ni->size_lock);
if (old_data_initialized + 8 > old_data_size) {
ntfs_debug("mftbmp: initialized_size + 8 > allocated_size.");
err = ntfs_mft_bitmap_extend_allocation_nolock(vol);
if (err)
goto unl_err;
#ifdef DEBUG
lck_spin_lock(&mftbmp_ni->size_lock);
ntfs_debug("Status of mftbmp after allocation extension: "
"allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.",
(unsigned long long)mftbmp_ni->allocated_size,
(unsigned long long)mftbmp_ni->data_size,
(unsigned long long)
mftbmp_ni->initialized_size);
lck_spin_unlock(&mftbmp_ni->size_lock);
#endif
}
err = ntfs_mft_bitmap_extend_initialized_nolock(vol);
if (err)
goto unl_err;
#ifdef DEBUG
lck_spin_lock(&mftbmp_ni->size_lock);
ntfs_debug("Status of mftbmp after initialized extension: "
"allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.",
(unsigned long long)mftbmp_ni->allocated_size,
(unsigned long long)mftbmp_ni->data_size,
(unsigned long long)mftbmp_ni->initialized_size);
lck_spin_unlock(&mftbmp_ni->size_lock);
#endif
ntfs_debug("Found free record (#3), bit 0x%llx.",
(unsigned long long)bit);
found_free_rec:
ntfs_debug("At found_free_rec.");
err = ntfs_bitmap_set_bit(mftbmp_ni, bit);
if (err) {
ntfs_error(vol->mp, "Failed to allocate bit in mft bitmap.");
goto unl_err;
}
ntfs_debug("Set bit 0x%llx in mft bitmap.", (unsigned long long)bit);
have_alloc_rec:
lck_rw_unlock_exclusive(&mftbmp_ni->lock);
lck_rw_lock_shared(&mft_ni->lock);
mft_ni_write_locked = FALSE;
mft_relocked:
ll = (bit + 1) << vol->mft_record_size_shift;
lck_spin_lock(&mft_ni->size_lock);
old_data_initialized = mft_ni->initialized_size;
lck_spin_unlock(&mft_ni->size_lock);
if (ll <= old_data_initialized) {
ntfs_debug("Allocated mft record already initialized.");
goto mft_rec_already_initialized;
}
if (!mft_ni_write_locked) {
mft_ni_write_locked = TRUE;
if (!lck_rw_lock_shared_to_exclusive(&mft_ni->lock)) {
lck_rw_lock_exclusive(&mft_ni->lock);
goto mft_relocked;
}
}
ntfs_debug("Initializing allocated mft record.");
lck_spin_lock(&mft_ni->size_lock);
ntfs_debug("Status of mft data before extension: "
"allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.",
(unsigned long long)mft_ni->allocated_size,
(unsigned long long)mft_ni->data_size,
(unsigned long long)mft_ni->initialized_size);
while (ll > mft_ni->allocated_size) {
lck_spin_unlock(&mft_ni->size_lock);
err = ntfs_mft_data_extend_allocation_nolock(vol);
if (err) {
ntfs_error(vol->mp, "Failed to extend mft data "
"allocation.");
lck_rw_unlock_exclusive(&mft_ni->lock);
goto undo_mftbmp_alloc_locked;
}
lck_spin_lock(&mft_ni->size_lock);
ntfs_debug("Status of mft data after allocation extension: "
"allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.",
(unsigned long long)mft_ni->allocated_size,
(unsigned long long)mft_ni->data_size,
(unsigned long long)mft_ni->initialized_size);
}
lck_spin_unlock(&mft_ni->size_lock);
lck_spin_lock(&mft_ni->size_lock);
old_data_initialized = mft_ni->initialized_size;
old_data_size = mft_ni->data_size;
nr_mft_records_added = 0;
if (old_data_size != ubc_getsize(mft_ni->vn))
panic("%s(): old_data_size != ubc_getsize(mft_ni->vn)\n",
__FUNCTION__);
while (ll > mft_ni->initialized_size) {
s64 new_initialized_size, mft_no;
new_initialized_size = mft_ni->initialized_size +
vol->mft_record_size;
mft_no = mft_ni->initialized_size >> vol->mft_record_size_shift;
ntfs_debug("mft_no 0x%llx, new_initialized_size 0x%llx, "
"initialized_size 0x%llx, data_size 0x%llx.",
(unsigned long long)mft_no,
(unsigned long long)new_initialized_size,
(unsigned long long)mft_ni->initialized_size,
(unsigned long long)mft_ni->data_size);
if (new_initialized_size > mft_ni->data_size) {
nr_mft_records_added += (new_initialized_size -
mft_ni->data_size) >>
vol->mft_record_size_shift;
ntfs_debug("Updating data size and ubc size, "
"nr_mft_records_added %lld.",
(long long)nr_mft_records_added);
mft_ni->data_size = new_initialized_size;
lck_spin_unlock(&mft_ni->size_lock);
if (!ubc_setsize(mft_ni->vn, new_initialized_size))
panic("%s(): ubc_setsize() failed.\n",
__FUNCTION__);
mark_sizes_dirty = TRUE;
} else
lck_spin_unlock(&mft_ni->size_lock);
ntfs_debug("Initializing mft record 0x%llx.",
(unsigned long long)mft_no);
err = ntfs_mft_record_format(vol, mft_no, new_initialized_size);
if (err) {
ntfs_error(vol->mp, "Failed to format mft record.");
goto undo_data_init;
}
lck_spin_lock(&mft_ni->size_lock);
}
lck_spin_unlock(&mft_ni->size_lock);
record_formatted = TRUE;
err = ntfs_mft_record_map_ext(mft_ni, &m, TRUE);
if (err) {
ntfs_error(vol->mp, "Failed to map mft record.");
goto undo_data_init;
}
ctx = ntfs_attr_search_ctx_get(mft_ni, m);
if (!ctx) {
ntfs_error(vol->mp, "Failed to get search context.");
err = ENOMEM;
ntfs_mft_record_unmap(mft_ni);
goto undo_data_init;
}
ctx->is_mft_locked = 1;
err = ntfs_attr_lookup(mft_ni->type, mft_ni->name, mft_ni->name_len,
0, NULL, 0, ctx);
if (err) {
ntfs_error(vol->mp, "Failed to find first attribute extent of "
"mft data attribute.");
ntfs_attr_search_ctx_put(ctx);
ntfs_mft_record_unmap(mft_ni);
goto undo_data_init;
}
a = ctx->a;
lck_spin_lock(&mft_ni->size_lock);
a->initialized_size = cpu_to_sle64(mft_ni->initialized_size);
a->data_size = cpu_to_sle64(mft_ni->data_size);
vol->nr_mft_records = mft_ni->data_size >> vol->mft_record_size_shift;
vol->nr_free_mft_records += nr_mft_records_added;
if (vol->nr_free_mft_records >= vol->nr_mft_records)
panic("%s(): vol->nr_free_mft_records > vol->nr_mft_records\n",
__FUNCTION__);
lck_spin_unlock(&mft_ni->size_lock);
NInoSetMrecNeedsDirtying(ctx->ni);
ntfs_attr_search_ctx_put(ctx);
ntfs_mft_record_unmap(mft_ni);
if (mark_sizes_dirty)
NInoSetDirtySizes(mft_ni);
lck_spin_lock(&mft_ni->size_lock);
ntfs_debug("Status of mft data after mft record initialization: "
"allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.",
(unsigned long long)mft_ni->allocated_size,
(unsigned long long)mft_ni->data_size,
(unsigned long long)mft_ni->initialized_size);
if (mft_ni->data_size != ubc_getsize(mft_ni->vn))
panic("%s(): mft_ni->data_size != ubc_getsize(mft_ni->vn)\n",
__FUNCTION__);
if (mft_ni->data_size > mft_ni->allocated_size)
panic("%s(): mft_ni->data_size > mft_ni->allocated_size\n",
__FUNCTION__);
if (mft_ni->initialized_size > mft_ni->data_size)
panic("%s(): mft_ni->initialized_size > mft_ni->data_size\n",
__FUNCTION__);
lck_spin_unlock(&mft_ni->size_lock);
lck_rw_lock_exclusive_to_shared(&mft_ni->lock);
mft_rec_already_initialized:
old_mft_data_pos = vol->mft_data_pos;
vol->mft_data_pos = bit + 1;
vol->nr_free_mft_records--;
if (vol->nr_free_mft_records < 0)
vol->nr_free_mft_records = 0;
lck_rw_unlock_exclusive(&vol->mftbmp_lock);
err = buf_meta_bread(mft_ni->vn, bit, vol->mft_record_size, NOCRED,
&buf);
if (err) {
ntfs_error(vol->mp, "Failed to read buffer of mft record "
"0x%llx (error %d).", (unsigned long long)bit,
err);
goto undo_mftbmp_alloc;
}
err = buf_map(buf, (caddr_t*)&m);
if (err) {
ntfs_error(vol->mp, "Failed to map buffer of mft record "
"0x%llx (error %d).", (unsigned long long)bit,
err);
goto undo_mftbmp_alloc;
}
if (!record_formatted) {
if (ntfs_is_file_record(m->magic) &&
m->flags & MFT_RECORD_IN_USE) {
ntfs_warning(vol->mp, "Mft record 0x%llx was marked "
"free in mft bitmap but is marked "
"used itself. Marking it used in mft "
"bitmap. This indicates a corrupt "
"file system. Unmount and run "
"chkdsk.", (unsigned long long)bit);
err = buf_unmap(buf);
if (err)
ntfs_error(vol->mp, "Failed to unmap buffer "
"of mft record 0x%llx (error "
"%d).",
(unsigned long long)bit, err);
buf_brelse(buf);
lck_rw_unlock_shared(&mft_ni->lock);
lck_rw_lock_exclusive(&vol->mftbmp_lock);
NVolSetErrors(vol);
goto retry_mftbmp_alloc;
}
seq_no = m->sequence_number;
usn = 0;
if (le16_to_cpu(m->usa_ofs) < NTFS_BLOCK_SIZE - sizeof(u16))
usn = *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs));
err = ntfs_mft_record_lay_out(vol, bit, m);
if (err) {
ntfs_error(vol->mp, "Failed to lay out allocated mft "
"record 0x%llx.",
(unsigned long long)bit);
goto unmap_undo_mftbmp_alloc;
}
if (seq_no)
m->sequence_number = seq_no;
if (usn && usn != 0xffff)
*(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn;
}
m->flags |= MFT_RECORD_IN_USE;
if (!va) {
seq_no = m->sequence_number;
m->base_mft_record = MK_LE_MREF(base_ni->mft_no,
base_ni->seq_no);
err = buf_unmap(buf);
if (err)
ntfs_error(vol->mp, "Failed to unmap buffer of mft "
"record 0x%llx (error %d).",
(unsigned long long)bit, err);
err = buf_bdwrite(buf);
if (err) {
ntfs_error(vol->mp, "Failed to write buffer of mft "
"record 0x%llx (error %d). Run "
"chkdsk.", (unsigned long long)bit,
err);
NVolSetErrors(vol);
lck_rw_unlock_shared(&mft_ni->lock);
goto free_undo_mftbmp_alloc;
}
err = ntfs_extent_mft_record_map_ext(base_ni, MK_MREF(bit,
le16_to_cpu(seq_no)), &ni, &m, TRUE);
lck_rw_unlock_shared(&mft_ni->lock);
if (err) {
ntfs_error(vol->mp, "Failed to map allocated mft "
"record 0x%llx (error %d).",
(unsigned long long)bit, err);
goto free_undo_mftbmp_alloc;
}
*new_a = (ATTR_RECORD*)((u8*)m + le16_to_cpu(m->attrs_offset));
} else {
FILE_ATTR_FLAGS file_attrs;
le32 security_id;
ntfs_attr na;
file_attrs = base_ni->file_attributes & (FILE_ATTR_ENCRYPTED |
FILE_ATTR_NOT_CONTENT_INDEXED |
FILE_ATTR_COMPRESSED | FILE_ATTR_SPARSE_FILE);
switch (va->va_type) {
case VDIR:
m->flags |= MFT_RECORD_IS_DIRECTORY;
break;
case VSOCK:
case VFIFO:
case VBLK:
case VCHR:
file_attrs |= FILE_ATTR_SYSTEM;
file_attrs &= ~(FILE_ATTR_ENCRYPTED |
FILE_ATTR_COMPRESSED);
default:
file_attrs |= FILE_ATTR_ARCHIVE;
file_attrs &= ~(FILE_ATTR_ENCRYPTED |
FILE_ATTR_COMPRESSED);
}
if (NVolUseSDAttr(vol))
security_id = 0;
else {
BOOL is_retry = FALSE;
retry:
lck_spin_lock(&vol->security_id_lock);
if (va->va_type == VDIR)
security_id = vol->default_dir_security_id;
else
security_id = vol->default_file_security_id;
lck_spin_unlock(&vol->security_id_lock);
if (!security_id && !is_retry) {
if (!ntfs_default_security_id_init(vol, va)) {
is_retry = TRUE;
goto retry;
}
}
}
a = (ATTR_RECORD*)((u8*)m + le16_to_cpu(m->attrs_offset));
ntfs_standard_info_attribute_insert(m, a, file_attrs,
security_id, &va->va_create_time);
a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length));
if (!security_id) {
ntfs_sd_attribute_insert(vol, m, a, va);
a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length));
}
if (va->va_type == VDIR) {
ntfs_index_root_attribute_insert(vol, m, a);
} else {
INTX_FILE *ix;
u32 data_len;
if (file_attrs & FILE_ATTR_ENCRYPTED)
panic("%s(): file_attrs & "
"FILE_ATTR_ENCRYPTED\n",
__FUNCTION__);
switch (va->va_type) {
case VBLK:
case VCHR:
data_len = offsetof(INTX_FILE, device) +
sizeof(ix->device);
break;
case VSOCK:
data_len = 1;
break;
case VFIFO:
default:
data_len = 0;
break;
}
if (ntfs_resident_attr_record_insert_internal(m, a,
AT_DATA, NULL, 0, data_len))
panic("%s(): Failed to insert resident data "
"attribute.\n", __FUNCTION__);
if (va->va_type == VBLK || va->va_type == VCHR) {
ix = (INTX_FILE*)((u8*)a +
le16_to_cpu(a->value_offset));
if (va->va_type == VBLK)
ix->magic = INTX_BLOCK_DEVICE;
else
ix->magic = INTX_CHAR_DEVICE;
ix->device.major = cpu_to_le64(
major(va->va_rdev));
ix->device.minor = cpu_to_le64(
minor(va->va_rdev));
}
}
na = (ntfs_attr) {
.mft_no = bit,
.type = AT_UNUSED,
.raw = FALSE,
};
ni = ntfs_inode_hash_get(vol, &na);
if (!ni) {
ntfs_error(vol->mp, "Failed to allocate ntfs inode "
"(ENOMEM).");
err = ENOMEM;
m->flags &= ~MFT_RECORD_IN_USE;
dirty_buf = TRUE;
goto unmap_undo_mftbmp_alloc;
}
if (!NInoAlloc(ni))
panic("%s(): !NInoAlloc(ni)\n", __FUNCTION__);
ni->seq_no = le16_to_cpu(m->sequence_number);
ni->mode |= ACCESSPERMS;
if (va->va_type == VDIR) {
ni->mode |= S_IFDIR;
ni->mode &= ~vol->dmask;
NInoSetMstProtected(ni);
ni->type = AT_INDEX_ALLOCATION;
ni->name = I30;
ni->name_len = 4;
ni->vcn_size = 0;
ni->collation_rule = 0;
ni->vcn_size_shift = 0;
} else {
switch (va->va_type) {
case VREG:
ni->mode |= S_IFREG;
break;
case VLNK:
ni->mode |= S_IFLNK;
break;
case VSOCK:
ni->mode |= S_IFSOCK;
break;
case VFIFO:
ni->mode |= S_IFIFO;
break;
case VBLK:
ni->mode |= S_IFBLK;
ni->rdev = va->va_rdev;
break;
case VCHR:
ni->mode |= S_IFCHR;
ni->rdev = va->va_rdev;
break;
default:
panic("%s(): Should never have gotten here "
"for va->va_type 0x%x.\n",
__FUNCTION__, va->va_type);
}
if (!S_ISLNK(ni->mode))
ni->mode &= ~vol->fmask;
ni->type = AT_DATA;
if (file_attrs & FILE_ATTR_COMPRESSED) {
panic("%s(): file_attrs & "
"FILE_ATTR_COMPRESSED\n",
__FUNCTION__);
}
}
ni->file_attributes = file_attrs;
if (file_attrs & FILE_ATTR_COMPRESSED)
NInoSetCompressed(ni);
if (file_attrs & FILE_ATTR_ENCRYPTED)
NInoSetEncrypted(ni);
if (file_attrs & FILE_ATTR_SPARSE_FILE)
NInoSetSparse(ni);
ni->last_access_time = ni->last_mft_change_time =
ni->last_data_change_time = ni->creation_time =
va->va_create_time;
ntfs_inode_afpinfo_cache(ni, NULL, 0);
if (va->va_type == VLNK) {
ni->finder_info.type = FINDER_TYPE_SYMBOLIC_LINK;
ni->finder_info.creator = FINDER_CREATOR_SYMBOLIC_LINK;
NInoSetDirtyFinderInfo(ni);
}
va->va_mode = ni->mode;
va->va_flags = 0;
if (file_attrs & FILE_ATTR_READONLY)
va->va_flags |= UF_IMMUTABLE;
if (file_attrs & FILE_ATTR_HIDDEN)
va->va_flags |= UF_HIDDEN;
if (!(file_attrs & FILE_ATTR_ARCHIVE))
va->va_flags |= SF_ARCHIVED;
err = ntfs_inode_add_vnode(ni, FALSE, base_ni->vn, cn);
if (err) {
ntfs_inode_reclaim(ni);
m->flags &= ~MFT_RECORD_IN_USE;
dirty_buf = TRUE;
goto unmap_undo_mftbmp_alloc;
}
err = buf_unmap(buf);
if (err)
ntfs_error(vol->mp, "Failed to unmap buffer of mft "
"record 0x%llx (error %d).",
(unsigned long long)bit, err);
err = buf_bdwrite(buf);
if (err) {
ntfs_error(vol->mp, "Failed to write buffer of mft "
"record 0x%llx (error %d). Run "
"chkdsk.", (unsigned long long)bit,
err);
NVolSetErrors(vol);
lck_rw_unlock_shared(&mft_ni->lock);
ntfs_inode_unlock_alloc(ni);
(void)vnode_recycle(ni->vn);
(void)vnode_put(ni->vn);
goto free_undo_mftbmp_alloc;
}
err = ntfs_mft_record_map_ext(ni, &m, TRUE);
lck_rw_unlock_shared(&mft_ni->lock);
if (err) {
ntfs_inode_unlock_alloc(ni);
(void)vnode_recycle(ni->vn);
(void)vnode_put(ni->vn);
goto free_undo_mftbmp_alloc;
}
a = (ATTR_RECORD*)((u8*)m + le16_to_cpu(m->attrs_offset));
if (a->type != AT_STANDARD_INFORMATION)
panic("%s(): a->type != AT_STANDARD_INFORMATION\n",
__FUNCTION__);
a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length));
if (le32_to_cpu(a->type) <= const_le32_to_cpu(AT_FILENAME))
panic("%s(): a->type <= AT_FILENAME\n", __FUNCTION__);
*new_a = a;
}
NInoSetMrecNeedsDirtying(ni);
(void)vnode_put(mftbmp_ni->vn);
if (va)
(void)vnode_put(mft_ni->vn);
ntfs_debug("Returning allocated %sntfs inode (mft_no 0x%llx).",
va ? "" : "extent ", (unsigned long long)bit);
*new_ni = ni;
*new_m = m;
return err;
undo_data_init:
lck_spin_lock(&mft_ni->size_lock);
mft_ni->initialized_size = old_data_initialized;
lck_spin_unlock(&mft_ni->size_lock);
if (!ubc_setsize(mft_ni->vn, old_data_size))
panic("%s(): !ubc_setsize(mft_ni->vn, old_data_size)\n",
__FUNCTION__);
lck_spin_lock(&mft_ni->size_lock);
mft_ni->data_size = old_data_size;
lck_spin_unlock(&mft_ni->size_lock);
lck_rw_unlock_exclusive(&mft_ni->lock);
goto undo_mftbmp_alloc_locked;
free_undo_mftbmp_alloc:
lck_rw_lock_shared(&mft_ni->lock);
err2 = buf_meta_bread(mft_ni->vn, bit, vol->mft_record_size, NOCRED,
&buf);
if (err2) {
ntfs_error(vol->mp, "Failed to re-read buffer of mft record "
"0x%llx in error code path (error %d).%s",
(unsigned long long)bit, err2, es);
NVolSetErrors(vol);
goto undo_mftbmp_alloc;
}
err2 = buf_map(buf, (caddr_t*)&m);
if (err2) {
ntfs_error(vol->mp, "Failed to re-map buffer of mft record "
"0x%llx in error code path (error %d).%s",
(unsigned long long)bit, err2, es);
NVolSetErrors(vol);
goto undo_mftbmp_alloc;
}
m->flags &= ~MFT_RECORD_IN_USE;
dirty_buf = TRUE;
unmap_undo_mftbmp_alloc:
err2 = buf_unmap(buf);
if (err2)
ntfs_error(vol->mp, "Failed to unmap buffer of mft record "
"0x%llx (error %d).", (unsigned long long)bit,
err2);
undo_mftbmp_alloc:
if (dirty_buf) {
err2 = buf_bdwrite(buf);
if (err2)
ntfs_error(vol->mp, "Failed to write buffer of mft "
"record 0x%llx in error code path "
"(error %d).", (unsigned long long)bit,
err2);
} else
buf_brelse(buf);
lck_rw_unlock_shared(&mft_ni->lock);
lck_rw_lock_exclusive(&vol->mftbmp_lock);
vol->nr_free_mft_records++;
if (old_mft_data_pos < vol->mft_data_pos)
vol->mft_data_pos = old_mft_data_pos;
undo_mftbmp_alloc_locked:
lck_rw_lock_shared(&mftbmp_ni->lock);
if (ntfs_bitmap_clear_bit(mftbmp_ni, bit)) {
ntfs_error(vol->mp, "Failed to clear bit in mft bitmap.%s", es);
NVolSetErrors(vol);
vol->nr_free_mft_records--;
if (vol->nr_free_mft_records < 0)
vol->nr_free_mft_records = 0;
}
lck_rw_unlock_shared(&mftbmp_ni->lock);
err:
lck_rw_unlock_exclusive(&vol->mftbmp_lock);
(void)vnode_put(mftbmp_ni->vn);
if (va)
(void)vnode_put(mft_ni->vn);
return err;
max_err:
ntfs_warning(vol->mp, "Cannot allocate mft record because the maximum "
"number of inodes (2^32) has already been reached.");
err = ENOSPC;
unl_err:
lck_rw_unlock_exclusive(&mftbmp_ni->lock);
goto err;
}
errno_t ntfs_extent_mft_record_free(ntfs_inode *base_ni, ntfs_inode *ni,
MFT_RECORD *m)
{
ino64_t mft_no = ni->mft_no;
ntfs_volume *vol = ni->vol;
ntfs_inode **extent_nis;
int i;
errno_t err;
u16 seq_no;
ntfs_debug("Entering for extent mft_no 0x%llx, base mft_no 0x%llx.\n",
(unsigned long long)mft_no,
(unsigned long long)base_ni->mft_no);
if (NInoAttr(ni))
panic("%s(): NInoAttr(ni)\n", __FUNCTION__);
if (ni->nr_extents != -1)
panic("%s(): ni->nr_extents != -1\n", __FUNCTION__);
if (base_ni->nr_extents <= 0)
panic("%s(): base_ni->nr_extents <= 0\n", __FUNCTION__);
lck_mtx_lock(&base_ni->extent_lock);
extent_nis = base_ni->extent_nis;
err = ENOENT;
for (i = 0; i < base_ni->nr_extents; i++) {
if (ni != extent_nis[i])
continue;
extent_nis += i;
base_ni->nr_extents--;
if (base_ni->nr_extents > 0) {
memmove(extent_nis, extent_nis + 1,
(base_ni->nr_extents - i) *
sizeof(ntfs_inode*));
} else {
if (base_ni->nr_extents < 0)
panic("%s(): base_ni->nr_extents < 0\n",
__FUNCTION__);
OSFree(base_ni->extent_nis, base_ni->extent_alloc,
ntfs_malloc_tag);
base_ni->extent_alloc = 0;
}
err = 0;
break;
}
lck_mtx_unlock(&base_ni->extent_lock);
if (err)
panic("%s(): Extent mft_no 0x%llx is not attached to "
"its base mft_no 0x%llx.\n", __FUNCTION__,
(unsigned long long)mft_no,
(unsigned long long)base_ni->mft_no);
m->flags &= ~MFT_RECORD_IN_USE;
seq_no = le16_to_cpu(m->sequence_number);
if (seq_no == 0xffff)
seq_no = 1;
else if (seq_no)
seq_no++;
m->sequence_number = cpu_to_le16(seq_no);
NInoSetMrecNeedsDirtying(ni);
ntfs_extent_mft_record_unmap(ni);
ntfs_inode_reclaim(ni);
lck_rw_lock_exclusive(&vol->mftbmp_lock);
err = vnode_get(vol->mftbmp_ni->vn);
if (err)
ntfs_error(vol->mp, "Failed to get vnode for $MFT/$BITMAP.");
else {
lck_rw_lock_shared(&vol->mftbmp_ni->lock);
err = ntfs_bitmap_clear_bit(vol->mftbmp_ni, mft_no);
lck_rw_unlock_shared(&vol->mftbmp_ni->lock);
(void)vnode_put(vol->mftbmp_ni->vn);
if (!err) {
vol->nr_free_mft_records++;
if (vol->nr_free_mft_records >= vol->nr_mft_records)
panic("%s(): vol->nr_free_mft_records > "
"vol->nr_mft_records\n",
__FUNCTION__);
}
}
lck_rw_unlock_exclusive(&vol->mftbmp_lock);
if (err) {
ntfs_error(vol->mp, "Failed to mark extent mft record as "
"unused in mft bitmap.%s", es);
NVolSetErrors(vol);
}
return 0;
}