📄 mft.c
字号:
s64 ll, bit, old_data_initialized, old_data_size; ntfs_attr *mft_na, *mftbmp_na; ntfs_attr_search_ctx *ctx; MFT_RECORD *m; ATTR_RECORD *a; ntfs_inode *ni; int err; u16 seq_no, usn; if (base_ni) ntfs_log_trace("Entering (allocating an extent mft record for " "base mft record 0x%llx).\n", (long long)base_ni->mft_no); else ntfs_log_trace("Entering (allocating a base mft record).\n"); if (!vol || !vol->mft_na || !vol->mftbmp_na) { errno = EINVAL; return NULL; } mft_na = vol->mft_na; mftbmp_na = vol->mftbmp_na; bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); if (bit >= 0) { ntfs_log_debug("Found free record (#1), bit 0x%llx.\n", (long long)bit); goto found_free_rec; } if (errno != ENOSPC) return NULL; /* * No free mft records left. If the mft bitmap already covers more * than the currently used mft records, the next records are all free, * so we can simply allocate the first unused mft record. * Note: We also have to make sure that the mft bitmap at least covers * the first 24 mft records as they are special and whilst they may not * be in use, we do not allocate from them. */ ll = mft_na->initialized_size >> vol->mft_record_size_bits; if (mftbmp_na->initialized_size << 3 > ll && mftbmp_na->initialized_size > 3) { bit = ll; if (bit < 24) bit = 24; ntfs_log_debug("Found free record (#2), bit 0x%llx.\n", (long long)bit); goto found_free_rec; } /* * The mft bitmap needs to be expanded until it covers the first unused * mft record that we can allocate. * Note: The smallest mft record we allocate is mft record 24. */ ntfs_log_debug("Status of mftbmp before extension: allocated_size 0x%llx, " "data_size 0x%llx, initialized_size 0x%llx.\n", (long long)mftbmp_na->allocated_size, (long long)mftbmp_na->data_size, (long long)mftbmp_na->initialized_size); if (mftbmp_na->initialized_size + 8 > mftbmp_na->allocated_size) { /* Need to extend bitmap by one more cluster. */ ntfs_log_debug("mftbmp: initialized_size + 8 > allocated_size.\n"); if (ntfs_mft_bitmap_extend_allocation(vol)) goto err_out; ntfs_log_debug("Status of mftbmp after allocation extension: " "allocated_size 0x%llx, data_size 0x%llx, " "initialized_size 0x%llx.\n", (long long)mftbmp_na->allocated_size, (long long)mftbmp_na->data_size, (long long)mftbmp_na->initialized_size); } /* * We now have sufficient allocated space, extend the initialized_size * as well as the data_size if necessary and fill the new space with * zeroes. */ bit = mftbmp_na->initialized_size << 3; if (ntfs_mft_bitmap_extend_initialized(vol)) goto err_out; ntfs_log_debug("Status of mftbmp after initialized extension: " "allocated_size 0x%llx, data_size 0x%llx, " "initialized_size 0x%llx.\n", (long long)mftbmp_na->allocated_size, (long long)mftbmp_na->data_size, (long long)mftbmp_na->initialized_size); ntfs_log_debug("Found free record (#3), bit 0x%llx.\n", (long long)bit);found_free_rec: /* @bit is the found free mft record, allocate it in the mft bitmap. */ ntfs_log_debug("At found_free_rec.\n"); if (ntfs_bitmap_set_bit(mftbmp_na, bit)) { ntfs_log_error("Failed to allocate bit in mft bitmap.\n"); goto err_out; } ntfs_log_debug("Set bit 0x%llx in mft bitmap.\n", (long long)bit); /* The mft bitmap is now uptodate. Deal with mft data attribute now. */ ll = (bit + 1) << vol->mft_record_size_bits; if (ll <= mft_na->initialized_size) { ntfs_log_debug("Allocated mft record already initialized.\n"); goto mft_rec_already_initialized; } ntfs_log_debug("Initializing allocated mft record.\n"); /* * The mft record is outside the initialized data. Extend the mft data * attribute until it covers the allocated record. The loop is only * actually traversed more than once when a freshly formatted volume is * first written to so it optimizes away nicely in the common case. */ ntfs_log_debug("Status of mft data before extension: " "allocated_size 0x%llx, data_size 0x%llx, " "initialized_size 0x%llx.\n", (long long)mft_na->allocated_size, (long long)mft_na->data_size, (long long)mft_na->initialized_size); while (ll > mft_na->allocated_size) { if (ntfs_mft_data_extend_allocation(vol)) goto undo_mftbmp_alloc; ntfs_log_debug("Status of mft data after allocation extension: " "allocated_size 0x%llx, data_size 0x%llx, " "initialized_size 0x%llx.\n", (long long)mft_na->allocated_size, (long long)mft_na->data_size, (long long)mft_na->initialized_size); } old_data_initialized = mft_na->initialized_size; old_data_size = mft_na->data_size; /* * Extend mft data initialized size (and data size of course) to reach * the allocated mft record, formatting the mft records along the way. * Note: We only modify the ntfs_attr structure as that is all that is * needed by ntfs_mft_record_format(). We will update the attribute * record itself in one fell swoop later on. */ while (ll > mft_na->initialized_size) { s64 ll2 = mft_na->initialized_size >> vol->mft_record_size_bits; mft_na->initialized_size += vol->mft_record_size; if (mft_na->initialized_size > mft_na->data_size) mft_na->data_size = mft_na->initialized_size; ntfs_log_debug("Initializing mft record 0x%llx.\n", (long long)ll2); err = ntfs_mft_record_format(vol, ll2); if (err) { ntfs_log_error("Failed to format mft record.\n"); goto undo_data_init; } } /* Update the mft data attribute record to reflect the new sizes. */ ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); if (!ctx) { ntfs_log_error("Failed to get search context.\n"); goto undo_data_init; } if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, 0, NULL, 0, ctx)) { ntfs_log_error("Failed to find first attribute extent of " "mft data attribute.\n"); ntfs_attr_put_search_ctx(ctx); goto undo_data_init; } a = ctx->attr; a->initialized_size = cpu_to_sle64(mft_na->initialized_size); a->data_size = cpu_to_sle64(mft_na->data_size); /* Ensure the changes make it to disk. */ ntfs_inode_mark_dirty(ctx->ntfs_ino); ntfs_attr_put_search_ctx(ctx); ntfs_log_debug("Status of mft data after mft record initialization: " "allocated_size 0x%llx, data_size 0x%llx, " "initialized_size 0x%llx.\n", (long long)mft_na->allocated_size, (long long)mft_na->data_size, (long long)mft_na->initialized_size); /* Sanity checks. */ if (mft_na->data_size > mft_na->allocated_size || mft_na->initialized_size > mft_na->data_size) NTFS_BUG("mft_na sanity checks failed"); // BUG_ON(mft_na->initialized_size > mft_na->data_size); // BUG_ON(mft_na->data_size > mft_na->allocated_size); /* Sync MFT to minimize data loss if there won't be clean unmount. */ if (ntfs_inode_sync(mft_na->ni)) { ntfs_log_error("Failed to sync $MFT."); goto undo_data_init; }mft_rec_already_initialized: /* * We now have allocated and initialized the mft record. Need to read * it from disk and re-format it, preserving the sequence number if it * is not zero as well as the update sequence number if it is not zero * or -1 (0xffff). */ m = ntfs_malloc(vol->mft_record_size); if (!m) goto undo_mftbmp_alloc; if (ntfs_mft_record_read(vol, bit, m)) { err = errno; ntfs_log_error("Failed to read mft record.\n"); free(m); errno = err; goto undo_mftbmp_alloc; } /* Sanity check that the mft record is really not in use. */ if (ntfs_is_file_record(m->magic) && (m->flags & MFT_RECORD_IN_USE)) { ntfs_log_error("Mft record 0x%llx was marked unused in " "mft bitmap but is marked used itself. " "Corrupt filesystem or library bug! " "Run chkdsk immediately!\n", (long long)bit); free(m); errno = EIO; goto undo_mftbmp_alloc; } seq_no = m->sequence_number; usn = *(u16*)((u8*)m + le16_to_cpu(m->usa_ofs)); if (ntfs_mft_record_layout(vol, bit, m)) { err = errno; ntfs_log_error("Failed to re-format mft record.\n"); free(m); errno = err; goto undo_mftbmp_alloc; } if (le16_to_cpu(seq_no)) m->sequence_number = seq_no; seq_no = le16_to_cpu(usn); if (seq_no && seq_no != 0xffff) *(u16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn; /* Set the mft record itself in use. */ m->flags |= MFT_RECORD_IN_USE; /* Now need to open an ntfs inode for the mft record. */ ni = ntfs_inode_allocate(vol); if (!ni) { err = errno; ntfs_log_error("Failed to allocate buffer for inode.\n"); free(m); errno = err; goto undo_mftbmp_alloc; } ni->mft_no = bit; ni->mrec = m; /* * If we are allocating an extent mft record, make the opened inode an * extent inode and attach it to the base inode. Also, set the base * mft record reference in the extent inode. */ if (base_ni) { ni->nr_extents = -1; ni->base_ni = base_ni; m->base_mft_record = MK_LE_MREF(base_ni->mft_no, le16_to_cpu(base_ni->mrec->sequence_number)); /* * Attach the extent inode to the base inode, reallocating * memory if needed. */ if (!(base_ni->nr_extents & 3)) { ntfs_inode **extent_nis; int i; i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); extent_nis = ntfs_malloc(i); if (!extent_nis) { free(m); free(ni); goto undo_mftbmp_alloc; } if (base_ni->extent_nis) { memcpy(extent_nis, base_ni->extent_nis, i - 4 * sizeof(ntfs_inode *)); free(base_ni->extent_nis); } base_ni->extent_nis = extent_nis; } base_ni->extent_nis[base_ni->nr_extents++] = ni; } /* Make sure the allocated inode is written out to disk later. */ ntfs_inode_mark_dirty(ni); /* Initialize time, allocated and data size in ntfs_inode struct. */ ni->data_size = ni->allocated_size = 0; ni->flags = 0; ni->creation_time = ni->last_data_change_time = ni->last_mft_change_time = ni->last_access_time = time(NULL); /* Update the default mft allocation position if it was used. */ if (!base_ni) vol->mft_data_pos = bit + 1; /* Return the opened, allocated inode of the allocated mft record. */ ntfs_log_debug("Returning opened, allocated %sinode 0x%llx.\n", base_ni ? "extent " : "", (long long)bit); return ni;undo_data_init: mft_na->initialized_size = old_data_initialized; mft_na->data_size = old_data_size;undo_mftbmp_alloc: err = errno; if (ntfs_bitmap_clear_bit(mftbmp_na, bit)) ntfs_log_error("Failed to clear bit in mft bitmap.%s\n", es); errno = err;err_out: if (!errno) errno = EIO; return NULL;}/** * ntfs_mft_record_free - free an mft record on an ntfs volume * @vol: volume on which to free the mft record * @ni: open ntfs inode of the mft record to free * * Free the mft record of the open inode @ni on the mounted ntfs volume @vol. * Note that this function calls ntfs_inode_close() internally and hence you * cannot use the pointer @ni any more after this function returns success. * * On success return 0 and on error return -1 with errno set to the error code. */int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni){ u64 mft_no; int err; u16 seq_no, old_seq_no; ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); if (!vol || !vol->mftbmp_na || !ni) { errno = EINVAL; return -1; } /* Cache the mft reference for later. */ mft_no = ni->mft_no; /* Mark the mft record as not in use. */ ni->mrec->flags &= ~MFT_RECORD_IN_USE; /* Increment the sequence number, skipping zero, if it is not zero. */ old_seq_no = ni->mrec->sequence_number; seq_no = le16_to_cpu(old_seq_no); if (seq_no == 0xffff) seq_no = 1; else if (seq_no) seq_no++; ni->mrec->sequence_number = cpu_to_le16(seq_no); /* Set the inode dirty and write it out. */ ntfs_inode_mark_dirty(ni); if (ntfs_inode_sync(ni)) { err = errno; goto sync_rollback; } /* Clear the bit in the $MFT/$BITMAP corresponding to this record. */ if (ntfs_bitmap_clear_bit(vol->mftbmp_na, mft_no)) { err = errno; // FIXME: If ntfs_bitmap_clear_run() guarantees rollback on // error, this could be changed to goto sync_rollback; goto bitmap_rollback; } /* Throw away the now freed inode. */ if (!ntfs_inode_close(ni)) return 0; err = errno; /* Rollback what we did... */bitmap_rollback: if (ntfs_bitmap_set_bit(vol->mftbmp_na, mft_no)) ntfs_log_debug("Eeek! Rollback failed in ntfs_mft_record_free(). " "Leaving inconsistent metadata!\n");sync_rollback: ni->mrec->flags |= MFT_RECORD_IN_USE; ni->mrec->sequence_number = old_seq_no; ntfs_inode_mark_dirty(ni); errno = err; return -1;}/** * ntfs_mft_usn_dec - Decrement USN by one * @mrec: pointer to an mft record * * On success return 0 and on error return -1 with errno set. */int ntfs_mft_usn_dec(MFT_RECORD *mrec){ u16 usn, *usnp; if (!mrec) { errno = EINVAL; return -1; } usnp = (u16 *)((char *)mrec + le16_to_cpu(mrec->usa_ofs)); usn = le16_to_cpup(usnp); if (usn-- <= 1) usn = 0xfffe; *usnp = cpu_to_le16(usn); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -