/** * ntfs_debug_attr_list_dump - dump an attribute list attribute * @al: attribute list attribute value to dump * @size: size of attribute list attribute value * * Dump the attribute list attribute value @al of size @size bytes. */ void ntfs_debug_attr_list_dump(const u8 *al, const unsigned size) { const u8 *end; ATTR_LIST_ENTRY *entry; unsigned u; if (!ntfs_debug_messages) return; end = al + size; printf("NTFS-fs DEBUG: Dumping attribute list (size 0x%x):\n", size); for (entry = (ATTR_LIST_ENTRY*)al, u = 1; (u8*)entry < end; entry = (ATTR_LIST_ENTRY*)((u8*)entry + le16_to_cpu(entry->length)), u++) { printf("--------------- Entry %u ---------------\n", u); printf("Attribute type: 0x%x\n", (unsigned)le32_to_cpu(entry->type)); printf("Record length: 0x%x\n", (unsigned)le16_to_cpu(entry->length)); printf("Name length: 0x%x\n", (unsigned)entry->name_length); printf("Name offset: 0x%x\n", (unsigned)entry->name_offset); printf("Starting VCN: 0x%llx\n", (unsigned long long) sle64_to_cpu(entry->lowest_vcn)); printf("MFT reference: 0x%llx\n", (unsigned long long) MREF_LE(entry->mft_reference)); printf("Instance: 0x%x\n", (unsigned)le16_to_cpu(entry->instance)); } printf("--------------- End of attribute list ---------------\n"); }
/** * ntfs_attrlist_need - check whether inode need attribute list * @ni: opened ntfs inode for which perform check * * Check whether all are attributes belong to one MFT record, in that case * attribute list is not needed. * * Return 1 if inode need attribute list, 0 if not, -1 on error with errno set * to the error code. If function succeed errno set to 0. The following error * codes are defined: * EINVAL - Invalid arguments passed to function or attribute haven't got * attribute list. */ int ntfs_attrlist_need(ntfs_inode *ni) { ATTR_LIST_ENTRY *ale; if (!ni) { ntfs_log_trace("Invalid arguments.\n"); errno = EINVAL; return -1; } ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); if (!NInoAttrList(ni)) { ntfs_log_trace("Inode haven't got attribute list.\n"); errno = EINVAL; return -1; } if (!ni->attr_list) { ntfs_log_trace("Corrupt in-memory struct.\n"); errno = EINVAL; return -1; } errno = 0; ale = (ATTR_LIST_ENTRY *)ni->attr_list; while ((u8*)ale < ni->attr_list + ni->attr_list_size) { if (MREF_LE(ale->mft_reference) != ni->mft_no) return 1; ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length)); } return 0; }
/** * ntfs_inode_attach_all_extents - attach all extents for target inode * @ni: opened ntfs inode for which perform attach * * Return 0 on success and -1 on error with errno set to the error code. */ int ntfs_inode_attach_all_extents(ntfs_inode *ni) { ATTR_LIST_ENTRY *ale; u64 prev_attached = 0; if (!ni) { ntfs_log_trace("Invalid arguments.\n"); errno = EINVAL; return -1; } if (ni->nr_extents == -1) ni = ni->base_ni; ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); /* Inode haven't got attribute list, thus nothing to attach. */ if (!NInoAttrList(ni)) return 0; if (!ni->attr_list) { ntfs_log_trace("Corrupt in-memory struct.\n"); errno = EINVAL; return -1; } /* Walk through attribute list and attach all extents. */ errno = 0; ale = (ATTR_LIST_ENTRY *)ni->attr_list; while ((u8*)ale < ni->attr_list + ni->attr_list_size) { if (ni->mft_no != MREF_LE(ale->mft_reference) && prev_attached != MREF_LE(ale->mft_reference)) { if (!ntfs_extent_inode_open(ni, ale->mft_reference)) { ntfs_log_trace("Couldn't attach extent inode.\n"); return -1; } prev_attached = MREF_LE(ale->mft_reference); } ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length)); } return 0; }
static struct dentry *ntfs_get_parent(struct dentry *child_dent) { struct inode *vi = child_dent->d_inode; ntfs_inode *ni = NTFS_I(vi); MFT_RECORD *mrec; ntfs_attr_search_ctx *ctx; ATTR_RECORD *attr; FILE_NAME_ATTR *fn; unsigned long parent_ino; int err; ntfs_debug("Entering for inode 0x%lx.", vi->i_ino); mrec = map_mft_record(ni); if (IS_ERR(mrec)) return (struct dentry *)mrec; ctx = ntfs_attr_get_search_ctx(ni, mrec); if (unlikely(!ctx)) { unmap_mft_record(ni); return ERR_PTR(-ENOMEM); } try_next: err = ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx); if (unlikely(err)) { ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); if (err == -ENOENT) ntfs_error(vi->i_sb, "Inode 0x%lx does not have a " "file name attribute. Run chkdsk.", vi->i_ino); return ERR_PTR(err); } attr = ctx->attr; if (unlikely(attr->non_resident)) goto try_next; fn = (FILE_NAME_ATTR *)((u8 *)attr + le16_to_cpu(attr->data.resident.value_offset)); if (unlikely((u8 *)fn + le32_to_cpu(attr->data.resident.value_length) > (u8*)attr + le32_to_cpu(attr->length))) goto try_next; parent_ino = MREF_LE(fn->parent_directory); ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); return d_obtain_alias(ntfs_iget(vi->i_sb, parent_ino)); }
/** * ntfs_get_parent - find the dentry of the parent of a given directory dentry * @child_dent: dentry of the directory whose parent directory to find * * Find the dentry for the parent directory of the directory specified by the * dentry @child_dent. This function is called from * fs/exportfs/expfs.c::find_exported_dentry() which in turn is called from the * default ->decode_fh() which is export_decode_fh() in the same file. * * The code is based on the ext3 ->get_parent() implementation found in * fs/ext3/namei.c::ext3_get_parent(). * * Note: ntfs_get_parent() is called with @child_dent->d_inode->i_mutex down. * * Return the dentry of the parent directory on success or the error code on * error (IS_ERR() is true). */ static struct dentry *ntfs_get_parent(struct dentry *child_dent) { struct inode *vi = child_dent->d_inode; ntfs_inode *ni = NTFS_I(vi); MFT_RECORD *mrec; ntfs_attr_search_ctx *ctx; ATTR_RECORD *attr; FILE_NAME_ATTR *fn; struct inode *parent_vi; struct dentry *parent_dent; unsigned long parent_ino; int err; ntfs_debug("Entering for inode 0x%lx.", vi->i_ino); /* Get the mft record of the inode belonging to the child dentry. */ mrec = map_mft_record(ni); if (IS_ERR(mrec)) return (struct dentry *)mrec; /* Find the first file name attribute in the mft record. */ ctx = ntfs_attr_get_search_ctx(ni, mrec); if (unlikely(!ctx)) { unmap_mft_record(ni); return ERR_PTR(-ENOMEM); } try_next: err = ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx); if (unlikely(err)) { ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); if (err == -ENOENT) ntfs_error(vi->i_sb, "Inode 0x%lx does not have a " "file name attribute. Run chkdsk.", vi->i_ino); return ERR_PTR(err); } attr = ctx->attr; if (unlikely(attr->non_resident)) goto try_next; fn = (FILE_NAME_ATTR *)((u8 *)attr + le16_to_cpu(attr->data.resident.value_offset)); if (unlikely((u8 *)fn + le32_to_cpu(attr->data.resident.value_length) > (u8*)attr + le32_to_cpu(attr->length))) goto try_next; /* Get the inode number of the parent directory. */ parent_ino = MREF_LE(fn->parent_directory); /* Release the search context and the mft record of the child. */ ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); /* Get the inode of the parent directory. */ parent_vi = ntfs_iget(vi->i_sb, parent_ino); if (IS_ERR(parent_vi) || unlikely(is_bad_inode(parent_vi))) { if (!IS_ERR(parent_vi)) iput(parent_vi); ntfs_error(vi->i_sb, "Failed to get parent directory inode " "0x%lx of child inode 0x%lx.", parent_ino, vi->i_ino); return ERR_PTR(-EACCES); } /* Finally get a dentry for the parent directory and return it. */ parent_dent = d_alloc_anon(parent_vi); if (unlikely(!parent_dent)) { iput(parent_vi); return ERR_PTR(-ENOMEM); } ntfs_debug("Done for inode 0x%lx.", vi->i_ino); return parent_dent; }
/** * ntfs_inode_add_attrlist - add attribute list to inode and fill it * @ni: opened ntfs inode to which add attribute list * * Return 0 on success or -1 on error with errno set to the error code. * The following error codes are defined: * EINVAL - Invalid arguments were passed to the function. * EEXIST - Attribute list already exist. * EIO - Input/Ouput error occurred. * ENOMEM - Not enough memory to perform add. */ int ntfs_inode_add_attrlist(ntfs_inode *ni) { int err; ntfs_attr_search_ctx *ctx; u8 *al = NULL, *aln; int al_len = 0; ATTR_LIST_ENTRY *ale = NULL; ntfs_attr *na; if (!ni) { errno = EINVAL; ntfs_log_perror("%s", __FUNCTION__); return -1; } ntfs_log_trace("inode %llu\n", (unsigned long long) ni->mft_no); if (NInoAttrList(ni) || ni->nr_extents) { errno = EEXIST; ntfs_log_perror("Inode already has attribute list"); return -1; } /* Form attribute list. */ ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { err = errno; goto err_out; } /* Walk through all attributes. */ while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { int ale_size; if (ctx->attr->type == AT_ATTRIBUTE_LIST) { err = EIO; ntfs_log_perror("Attribute list already present"); goto put_err_out; } ale_size = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7; al_len += ale_size; aln = realloc(al, al_len); if (!aln) { err = errno; ntfs_log_perror("Failed to realloc %d bytes", al_len); goto put_err_out; } ale = (ATTR_LIST_ENTRY *)(aln + ((u8 *)ale - al)); al = aln; memset(ale, 0, ale_size); /* Add attribute to attribute list. */ ale->type = ctx->attr->type; ale->length = cpu_to_le16((sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7); ale->name_length = ctx->attr->name_length; ale->name_offset = (u8 *)ale->name - (u8 *)ale; if (ctx->attr->non_resident) ale->lowest_vcn = ctx->attr->lowest_vcn; else ale->lowest_vcn = 0; ale->mft_reference = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); ale->instance = ctx->attr->instance; memcpy(ale->name, (u8 *)ctx->attr + le16_to_cpu(ctx->attr->name_offset), ctx->attr->name_length * sizeof(ntfschar)); ale = (ATTR_LIST_ENTRY *)(al + al_len); } /* Check for real error occurred. */ if (errno != ENOENT) { err = errno; ntfs_log_perror("%s: Attribute lookup failed, inode %lld", __FUNCTION__, (long long)ni->mft_no); goto put_err_out; } /* Set in-memory attribute list. */ ni->attr_list = al; ni->attr_list_size = al_len; NInoSetAttrList(ni); NInoAttrListSetDirty(ni); /* Free space if there is not enough it for $ATTRIBUTE_LIST. */ if (le32_to_cpu(ni->mrec->bytes_allocated) - le32_to_cpu(ni->mrec->bytes_in_use) < offsetof(ATTR_RECORD, resident_end)) { if (ntfs_inode_free_space(ni, offsetof(ATTR_RECORD, resident_end))) { /* Failed to free space. */ err = errno; ntfs_log_perror("Failed to free space for attrlist"); goto rollback; } } /* Add $ATTRIBUTE_LIST to mft record. */ if (ntfs_resident_attr_record_add(ni, AT_ATTRIBUTE_LIST, NULL, 0, NULL, 0, 0) < 0) { err = errno; ntfs_log_perror("Couldn't add $ATTRIBUTE_LIST to MFT"); goto rollback; } /* Resize it. */ na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); if (!na) { err = errno; ntfs_log_perror("Failed to open just added $ATTRIBUTE_LIST"); goto remove_attrlist_record; } if (ntfs_attr_truncate(na, al_len)) { err = errno; ntfs_log_perror("Failed to resize just added $ATTRIBUTE_LIST"); ntfs_attr_close(na); goto remove_attrlist_record;; } ntfs_attr_put_search_ctx(ctx); ntfs_attr_close(na); return 0; remove_attrlist_record: /* Prevent ntfs_attr_recorm_rm from freeing attribute list. */ ni->attr_list = NULL; NInoClearAttrList(ni); /* Remove $ATTRIBUTE_LIST record. */ ntfs_attr_reinit_search_ctx(ctx); if (!ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { if (ntfs_attr_record_rm(ctx)) ntfs_log_perror("Rollback failed to remove attrlist"); } else ntfs_log_perror("Rollback failed to find attrlist"); /* Setup back in-memory runlist. */ ni->attr_list = al; ni->attr_list_size = al_len; NInoSetAttrList(ni); rollback: /* * Scan attribute list for attributes that placed not in the base MFT * record and move them to it. */ ntfs_attr_reinit_search_ctx(ctx); ale = (ATTR_LIST_ENTRY*)al; while ((u8*)ale < al + al_len) { if (MREF_LE(ale->mft_reference) != ni->mft_no) { if (!ntfs_attr_lookup(ale->type, ale->name, ale->name_length, CASE_SENSITIVE, sle64_to_cpu(ale->lowest_vcn), NULL, 0, ctx)) { if (ntfs_attr_record_move_to(ctx, ni)) ntfs_log_perror("Rollback failed to " "move attribute"); } else ntfs_log_perror("Rollback failed to find attr"); ntfs_attr_reinit_search_ctx(ctx); } ale = (ATTR_LIST_ENTRY*)((u8*)ale + le16_to_cpu(ale->length)); } /* Remove in-memory attribute list. */ ni->attr_list = NULL; ni->attr_list_size = 0; NInoClearAttrList(ni); NInoAttrListClearDirty(ni); put_err_out: ntfs_attr_put_search_ctx(ctx); err_out: free(al); errno = err; return -1; }
/** * ntfs_inode_sync_file_name - update FILE_NAME attributes * @ni: ntfs inode to update FILE_NAME attributes * * Update all FILE_NAME attributes for inode @ni in the index. * * Return 0 on success or -1 on error with errno set to the error code. */ static int ntfs_inode_sync_file_name(ntfs_inode *ni) { ntfs_attr_search_ctx *ctx = NULL; ntfs_index_context *ictx; ntfs_inode *index_ni; FILE_NAME_ATTR *fn; int err = 0; ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { err = errno; goto err_out; } /* Walk through all FILE_NAME attributes and update them. */ while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) { fn = (FILE_NAME_ATTR *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); if (MREF_LE(fn->parent_directory) == ni->mft_no) { /* * WARNING: We cheat here and obtain 2 attribute * search contexts for one inode (first we obtained * above, second will be obtained inside * ntfs_index_lookup), it's acceptable for library, * but will deadlock in the kernel. */ index_ni = ni; } else index_ni = ntfs_inode_open(ni->vol, le64_to_cpu(fn->parent_directory)); if (!index_ni) { if (!err) err = errno; ntfs_log_perror("Failed to open inode %lld with index", (long long)le64_to_cpu(fn->parent_directory)); continue; } ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4); if (!ictx) { if (!err) err = errno; ntfs_log_perror("Failed to get index ctx, inode %lld", (long long)index_ni->mft_no); if (ni != index_ni && ntfs_inode_close(index_ni) && !err) err = errno; continue; } if (ntfs_index_lookup(fn, sizeof(FILE_NAME_ATTR), ictx)) { if (!err) { if (errno == ENOENT) err = EIO; else err = errno; } ntfs_log_perror("Index lookup failed, inode %lld", (long long)index_ni->mft_no); ntfs_index_ctx_put(ictx); if (ni != index_ni && ntfs_inode_close(index_ni) && !err) err = errno; continue; } /* Update flags and file size. */ fn = (FILE_NAME_ATTR *)ictx->data; fn->file_attributes = (fn->file_attributes & ~FILE_ATTR_VALID_FLAGS) | (ni->flags & FILE_ATTR_VALID_FLAGS); fn->allocated_size = cpu_to_sle64(ni->allocated_size); fn->data_size = cpu_to_sle64(ni->data_size); if (test_nino_flag(ni, TimesDirty)) { fn->creation_time = utc2ntfs(ni->creation_time); fn->last_data_change_time = utc2ntfs(ni->last_data_change_time); fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); fn->last_access_time = utc2ntfs(ni->last_access_time); } ntfs_index_entry_mark_dirty(ictx); ntfs_index_ctx_put(ictx); if ((ni != index_ni) && ntfs_inode_close(index_ni) && !err) err = errno; } /* Check for real error occurred. */ if (errno != ENOENT) { err = errno; ntfs_log_perror("Attribute lookup failed, inode %lld", (long long)ni->mft_no); goto err_out; } ntfs_attr_put_search_ctx(ctx); if (err) { errno = err; return -1; } return 0; err_out: if (ctx) ntfs_attr_put_search_ctx(ctx); errno = err; return -1; }
/** * ntfs_extent_inode_open - load an extent inode and attach it to its base * @base_ni: base ntfs inode * @mref: mft reference of the extent inode to load (in little endian) * * First check if the extent inode @mref is already attached to the base ntfs * inode @base_ni, and if so, return a pointer to the attached extent inode. * * If the extent inode is not already attached to the base inode, allocate an * ntfs_inode structure and initialize it for the given inode @mref. @mref * specifies the inode number / mft record to read, including the sequence * number, which can be 0 if no sequence number checking is to be performed. * * Then, allocate a buffer for the mft record, read the mft record from the * volume @base_ni->vol, and attach it to the ntfs_inode structure (->mrec). * The mft record is mst deprotected and sanity checked for validity and we * abort if deprotection or checks fail. * * Finally attach the ntfs inode to its base inode @base_ni and return a * pointer to the ntfs_inode structure on success or NULL on error, with errno * set to the error code. * * Note, extent inodes are never closed directly. They are automatically * disposed off by the closing of the base inode. */ ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) { u64 mft_no = MREF_LE(mref); ntfs_inode *ni = NULL; ntfs_inode **extent_nis; int i; if (!base_ni) { errno = EINVAL; ntfs_log_perror("%s", __FUNCTION__); return NULL; } ntfs_log_enter("Opening extent inode %lld (base mft record %lld).\n", (unsigned long long)mft_no, (unsigned long long)base_ni->mft_no); /* Is the extent inode already open and attached to the base inode? */ if (base_ni->nr_extents > 0) { extent_nis = base_ni->extent_nis; for (i = 0; i < base_ni->nr_extents; i++) { u16 seq_no; ni = extent_nis[i]; if (mft_no != ni->mft_no) continue; /* Verify the sequence number if given. */ seq_no = MSEQNO_LE(mref); if (seq_no && seq_no != le16_to_cpu( ni->mrec->sequence_number)) { errno = EIO; ntfs_log_perror("Found stale extent mft " "reference mft=%lld", (long long)ni->mft_no); goto out; } goto out; } } /* Wasn't there, we need to load the extent inode. */ ni = __ntfs_inode_allocate(base_ni->vol); if (!ni) goto out; if (ntfs_file_record_read(base_ni->vol, le64_to_cpu(mref), &ni->mrec, NULL)) goto err_out; ni->mft_no = mft_no; ni->nr_extents = -1; ni->base_ni = base_ni; /* Attach extent inode to base inode, reallocating memory if needed. */ if (!(base_ni->nr_extents & 3)) { i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); extent_nis = ntfs_malloc(i); if (!extent_nis) goto err_out; if (base_ni->nr_extents) { 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; out: ntfs_log_leave("\n"); return ni; err_out: __ntfs_inode_release(ni); ni = NULL; goto out; }
/** * ntfs_inode_sync_file_name - update FILE_NAME attributes * @ni: ntfs inode to update FILE_NAME attributes * * Update all FILE_NAME attributes for inode @ni in the index. * * Return 0 on success or -1 on error with errno set to the error code. */ static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni) { ntfs_attr_search_ctx *ctx = NULL; ntfs_index_context *ictx; ntfs_inode *index_ni; FILE_NAME_ATTR *fn; FILE_NAME_ATTR *fnx; REPARSE_POINT *rpp; le32 reparse_tag; int err = 0; ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { err = errno; goto err_out; } /* Collect the reparse tag, if any */ reparse_tag = cpu_to_le32(0); if (ni->flags & FILE_ATTR_REPARSE_POINT) { if (!ntfs_attr_lookup(AT_REPARSE_POINT, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { rpp = (REPARSE_POINT*)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); reparse_tag = rpp->reparse_tag; } ntfs_attr_reinit_search_ctx(ctx); } /* Walk through all FILE_NAME attributes and update them. */ while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) { fn = (FILE_NAME_ATTR *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); if (MREF_LE(fn->parent_directory) == ni->mft_no) { /* * WARNING: We cheat here and obtain 2 attribute * search contexts for one inode (first we obtained * above, second will be obtained inside * ntfs_index_lookup), it's acceptable for library, * but will deadlock in the kernel. */ index_ni = ni; } else if (dir_ni) index_ni = dir_ni; else index_ni = ntfs_inode_open(ni->vol, le64_to_cpu(fn->parent_directory)); if (!index_ni) { if (!err) err = errno; ntfs_log_perror("Failed to open inode %lld with index", (long long)le64_to_cpu(fn->parent_directory)); continue; } ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4); if (!ictx) { if (!err) err = errno; ntfs_log_perror("Failed to get index ctx, inode %lld", (long long)index_ni->mft_no); if ((ni != index_ni) && !dir_ni && ntfs_inode_close(index_ni) && !err) err = errno; continue; } if (ntfs_index_lookup(fn, sizeof(FILE_NAME_ATTR), ictx)) { if (!err) { if (errno == ENOENT) err = EIO; else err = errno; } ntfs_log_perror("Index lookup failed, inode %lld", (long long)index_ni->mft_no); ntfs_index_ctx_put(ictx); if (ni != index_ni && ntfs_inode_close(index_ni) && !err) err = errno; continue; } /* Update flags and file size. */ fnx = (FILE_NAME_ATTR *)ictx->data; fnx->file_attributes = (fnx->file_attributes & ~FILE_ATTR_VALID_FLAGS) | (ni->flags & FILE_ATTR_VALID_FLAGS); if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) fnx->data_size = fnx->allocated_size = const_cpu_to_le64(0); else { fnx->allocated_size = cpu_to_sle64(ni->allocated_size); fnx->data_size = cpu_to_sle64(ni->data_size); /* * The file name record has also to be fixed if some * attribute update implied the unnamed data to be * made non-resident */ fn->allocated_size = fnx->allocated_size; } /* update or clear the reparse tag in the index */ fnx->reparse_point_tag = reparse_tag; if (!test_nino_flag(ni, TimesSet)) { fnx->creation_time = ni->creation_time; fnx->last_data_change_time = ni->last_data_change_time; fnx->last_mft_change_time = ni->last_mft_change_time; fnx->last_access_time = ni->last_access_time; } else { fnx->creation_time = fn->creation_time; fnx->last_data_change_time = fn->last_data_change_time; fnx->last_mft_change_time = fn->last_mft_change_time; fnx->last_access_time = fn->last_access_time; } ntfs_index_entry_mark_dirty(ictx); ntfs_index_ctx_put(ictx); if ((ni != index_ni) && !dir_ni && ntfs_inode_close(index_ni) && !err) err = errno; } /* Check for real error occurred. */ if (errno != ENOENT) { err = errno; ntfs_log_perror("Attribute lookup failed, inode %lld", (long long)ni->mft_no); goto err_out; } ntfs_attr_put_search_ctx(ctx); if (err) { errno = err; return -1; } return 0; err_out: if (ctx) ntfs_attr_put_search_ctx(ctx); errno = err; return -1; }
/** * map_extent_mft_record - load an extent inode and attach it to its base * @base_ni: base ntfs inode * @mref: mft reference of the extent inode to load (in little endian) * @ntfs_ino: on successful return, pointer to the ntfs_inode structure * * Load the extent mft record @mref and attach it to its base inode @base_ni. * Return the mapped extent mft record if IS_ERR(result) is false. Otherwise * PTR_ERR(result) gives the negative error code. * * On successful return, @ntfs_ino contains a pointer to the ntfs_inode * structure of the mapped extent inode. */ MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref, ntfs_inode **ntfs_ino) { MFT_RECORD *m; ntfs_inode *ni = NULL; ntfs_inode **extent_nis = NULL; int i; unsigned long mft_no = MREF_LE(mref); u16 seq_no = MSEQNO_LE(mref); BOOL destroy_ni = FALSE; ntfs_debug("Mapping extent mft record 0x%lx (base mft record 0x%lx).", mft_no, base_ni->mft_no); /* Make sure the base ntfs inode doesn't go away. */ atomic_inc(&base_ni->count); /* * Check if this extent inode has already been added to the base inode, * in which case just return it. If not found, add it to the base * inode before returning it. */ down(&base_ni->extent_lock); if (base_ni->nr_extents > 0) { extent_nis = base_ni->ext.extent_ntfs_inos; for (i = 0; i < base_ni->nr_extents; i++) { if (mft_no != extent_nis[i]->mft_no) continue; ni = extent_nis[i]; /* Make sure the ntfs inode doesn't go away. */ atomic_inc(&ni->count); break; } } if (likely(ni != NULL)) { up(&base_ni->extent_lock); atomic_dec(&base_ni->count); /* We found the record; just have to map and return it. */ m = map_mft_record(ni); /* map_mft_record() has incremented this on success. */ atomic_dec(&ni->count); if (likely(!IS_ERR(m))) { /* Verify the sequence number. */ if (likely(le16_to_cpu(m->sequence_number) == seq_no)) { ntfs_debug("Done 1."); *ntfs_ino = ni; return m; } unmap_mft_record(ni); ntfs_error(base_ni->vol->sb, "Found stale extent mft " "reference! Corrupt file system. " "Run chkdsk."); return ERR_PTR(-EIO); } map_err_out: ntfs_error(base_ni->vol->sb, "Failed to map extent " "mft record, error code %ld.", -PTR_ERR(m)); return m; } /* Record wasn't there. Get a new ntfs inode and initialize it. */ ni = ntfs_new_extent_inode(base_ni->vol->sb, mft_no); if (unlikely(!ni)) { up(&base_ni->extent_lock); atomic_dec(&base_ni->count); return ERR_PTR(-ENOMEM); } ni->vol = base_ni->vol; ni->seq_no = seq_no; ni->nr_extents = -1; ni->ext.base_ntfs_ino = base_ni; /* Now map the record. */ m = map_mft_record(ni); if (unlikely(IS_ERR(m))) { up(&base_ni->extent_lock); atomic_dec(&base_ni->count); ntfs_clear_extent_inode(ni); goto map_err_out; } /* Verify the sequence number. */ if (unlikely(le16_to_cpu(m->sequence_number) != seq_no)) { ntfs_error(base_ni->vol->sb, "Found stale extent mft " "reference! Corrupt file system. Run chkdsk."); destroy_ni = TRUE; m = ERR_PTR(-EIO); goto unm_err_out; } /* Attach extent inode to base inode, reallocating memory if needed. */ if (!(base_ni->nr_extents & 3)) { ntfs_inode **tmp; int new_size = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); tmp = (ntfs_inode **)kmalloc(new_size, GFP_NOFS); if (unlikely(!tmp)) { ntfs_error(base_ni->vol->sb, "Failed to allocate " "internal buffer."); destroy_ni = TRUE; m = ERR_PTR(-ENOMEM); goto unm_err_out; } if (base_ni->ext.extent_ntfs_inos) { memcpy(tmp, base_ni->ext.extent_ntfs_inos, new_size - 4 * sizeof(ntfs_inode *)); kfree(base_ni->ext.extent_ntfs_inos); } base_ni->ext.extent_ntfs_inos = tmp; } base_ni->ext.extent_ntfs_inos[base_ni->nr_extents++] = ni; up(&base_ni->extent_lock); atomic_dec(&base_ni->count); ntfs_debug("Done 2."); *ntfs_ino = ni; return m; unm_err_out: unmap_mft_record(ni); up(&base_ni->extent_lock); atomic_dec(&base_ni->count); /* * If the extent inode was not attached to the base inode we need to * release it or we will leak memory. */ if (destroy_ni) ntfs_clear_extent_inode(ni); return m; }
/** * ntfs_mft_writepage - check if a metadata page contains dirty mft records * @page: metadata page possibly containing dirty mft records * @wbc: writeback control structure * * This is called from the VM when it wants to have a dirty $MFT/$DATA metadata * page cache page cleaned. The VM has already locked the page and marked it * clean. Instead of writing the page as a conventional ->writepage function * would do, we check if the page still contains any dirty mft records (it must * have done at some point in the past since the page was marked dirty) and if * none are found, i.e. all mft records are clean, we unlock the page and * return. The VM is then free to do with the page as it pleases. If on the * other hand we do find any dirty mft records in the page, we redirty the page * before unlocking it and returning so the VM knows that the page is still * busy and cannot be thrown out. * * Note, we do not actually write any dirty mft records here because they are * dirty inodes and hence will be written by the VFS inode dirty code paths. * There is no need to write them from the VM page dirty code paths, too and in * fact once we implement journalling it would be a complete nightmare having * two code paths leading to mft record writeout. */ static int ntfs_mft_writepage(struct page *page, struct writeback_control *wbc) { struct inode *mft_vi = page->mapping->host; struct super_block *sb = mft_vi->i_sb; ntfs_volume *vol = NTFS_SB(sb); u8 *maddr; MFT_RECORD *m; ntfs_inode **extent_nis; unsigned long mft_no; int nr, i, j; BOOL is_dirty = FALSE; BUG_ON(!PageLocked(page)); BUG_ON(PageWriteback(page)); BUG_ON(mft_vi != vol->mft_ino); /* The first mft record number in the page. */ mft_no = page->index << (PAGE_CACHE_SHIFT - vol->mft_record_size_bits); /* Number of mft records in the page. */ nr = PAGE_CACHE_SIZE >> vol->mft_record_size_bits; BUG_ON(!nr); ntfs_debug("Entering for %i inodes starting at 0x%lx.", nr, mft_no); /* Iterate over the mft records in the page looking for a dirty one. */ maddr = (u8*)kmap(page); for (i = 0; i < nr; ++i, ++mft_no, maddr += vol->mft_record_size) { struct inode *vi; ntfs_inode *ni, *eni; ntfs_attr na; na.mft_no = mft_no; na.name = NULL; na.name_len = 0; na.type = AT_UNUSED; /* * Check if the inode corresponding to this mft record is in * the VFS inode cache and obtain a reference to it if it is. */ ntfs_debug("Looking for inode 0x%lx in icache.", mft_no); /* * For inode 0, i.e. $MFT itself, we cannot use ilookup5() from * here or we deadlock because the inode is already locked by * the kernel (fs/fs-writeback.c::__sync_single_inode()) and * ilookup5() waits until the inode is unlocked before * returning it and it never gets unlocked because * ntfs_mft_writepage() never returns. )-: Fortunately, we * have inode 0 pinned in icache for the duration of the mount * so we can access it directly. */ if (!mft_no) { /* Balance the below iput(). */ vi = igrab(mft_vi); BUG_ON(vi != mft_vi); } else vi = ilookup5(sb, mft_no, (test_t)ntfs_test_inode, &na); if (vi) { ntfs_debug("Inode 0x%lx is in icache.", mft_no); /* The inode is in icache. Check if it is dirty. */ ni = NTFS_I(vi); if (!NInoDirty(ni)) { /* The inode is not dirty, skip this record. */ ntfs_debug("Inode 0x%lx is not dirty, " "continuing search.", mft_no); iput(vi); continue; } ntfs_debug("Inode 0x%lx is dirty, aborting search.", mft_no); /* The inode is dirty, no need to search further. */ iput(vi); is_dirty = TRUE; break; } ntfs_debug("Inode 0x%lx is not in icache.", mft_no); /* The inode is not in icache. */ /* Skip the record if it is not a mft record (type "FILE"). */ if (!ntfs_is_mft_recordp(maddr)) { ntfs_debug("Mft record 0x%lx is not a FILE record, " "continuing search.", mft_no); continue; } m = (MFT_RECORD*)maddr; /* * Skip the mft record if it is not in use. FIXME: What about * deleted/deallocated (extent) inodes? (AIA) */ if (!(m->flags & MFT_RECORD_IN_USE)) { ntfs_debug("Mft record 0x%lx is not in use, " "continuing search.", mft_no); continue; } /* Skip the mft record if it is a base inode. */ if (!m->base_mft_record) { ntfs_debug("Mft record 0x%lx is a base record, " "continuing search.", mft_no); continue; } /* * This is an extent mft record. Check if the inode * corresponding to its base mft record is in icache. */ na.mft_no = MREF_LE(m->base_mft_record); ntfs_debug("Mft record 0x%lx is an extent record. Looking " "for base inode 0x%lx in icache.", mft_no, na.mft_no); vi = ilookup5(sb, na.mft_no, (test_t)ntfs_test_inode, &na); if (!vi) { /* * The base inode is not in icache. Skip this extent * mft record. */ ntfs_debug("Base inode 0x%lx is not in icache, " "continuing search.", na.mft_no); continue; } ntfs_debug("Base inode 0x%lx is in icache.", na.mft_no); /* * The base inode is in icache. Check if it has the extent * inode corresponding to this extent mft record attached. */ ni = NTFS_I(vi); down(&ni->extent_lock); if (ni->nr_extents <= 0) { /* * The base inode has no attached extent inodes. Skip * this extent mft record. */ up(&ni->extent_lock); iput(vi); continue; } /* Iterate over the attached extent inodes. */ extent_nis = ni->ext.extent_ntfs_inos; for (eni = NULL, j = 0; j < ni->nr_extents; ++j) { if (mft_no == extent_nis[j]->mft_no) { /* * Found the extent inode corresponding to this * extent mft record. */ eni = extent_nis[j]; break; } } /* * If the extent inode was not attached to the base inode, skip * this extent mft record. */ if (!eni) { up(&ni->extent_lock); iput(vi); continue; } /* * Found the extent inode corrsponding to this extent mft * record. If it is dirty, no need to search further. */ if (NInoDirty(eni)) { up(&ni->extent_lock); iput(vi); is_dirty = TRUE; break; } /* The extent inode is not dirty, so do the next record. */ up(&ni->extent_lock); iput(vi); } kunmap(page); /* If a dirty mft record was found, redirty the page. */ if (is_dirty) { ntfs_debug("Inode 0x%lx is dirty. Redirtying the page " "starting at inode 0x%lx.", mft_no, page->index << (PAGE_CACHE_SHIFT - vol->mft_record_size_bits)); redirty_page_for_writepage(wbc, page); unlock_page(page); } else { /* * Keep the VM happy. This must be done otherwise the * radix-tree tag PAGECACHE_TAG_DIRTY remains set even though * the page is clean. */ BUG_ON(PageWriteback(page)); set_page_writeback(page); unlock_page(page); end_page_writeback(page); } ntfs_debug("Done."); return 0; }