/** * 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_release - Destroy an NTFS inode object * @ni: * * Description... * * Returns: */ static void __ntfs_inode_release(ntfs_inode *ni) { if (NInoDirty(ni)) ntfs_log_error("Releasing dirty inode %lld!\n", (long long)ni->mft_no); if (NInoAttrList(ni) && ni->attr_list) free(ni->attr_list); free(ni->mrec); free(ni); return; }
/** * 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; }
/** * ntfs_attrlist_entry_rm - remove an attribute list attribute entry * @ctx: attribute search context describing the attribute list entry * * Remove the attribute list entry @ctx->al_entry from the attribute list. * * Return 0 on success and -1 on error with errno set to the error code. */ int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx) { u8 *new_al; int new_al_len; ntfs_inode *base_ni; ntfs_attr *na; ATTR_LIST_ENTRY *ale; int err; if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) { ntfs_log_trace("Invalid arguments.\n"); errno = EINVAL; return -1; } if (ctx->base_ntfs_ino) base_ni = ctx->base_ntfs_ino; else base_ni = ctx->ntfs_ino; ale = ctx->al_entry; ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld." "\n", (long long) ctx->ntfs_ino->mft_no, (unsigned) le32_to_cpu(ctx->al_entry->type), (long long) sle64_to_cpu(ctx->al_entry->lowest_vcn)); if (!NInoAttrList(base_ni)) { ntfs_log_trace("Attribute list isn't present.\n"); errno = ENOENT; return -1; } /* Allocate memory for new attribute list. */ new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length); new_al = malloc(new_al_len); if (!new_al) { ntfs_log_trace("Not enough memory.\n"); errno = ENOMEM; return -1; } /* Reisze $ATTRIBUTE_LIST to new length. */ na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); if (!na) { err = errno; ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); goto err_out; } if (ntfs_attr_truncate(na, new_al_len)) { err = errno; ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); goto err_out; } /* Copy entries from old attribute list to new. */ memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list); memcpy(new_al + ((u8*)ale - base_ni->attr_list), (u8*)ale + le16_to_cpu( ale->length), new_al_len - ((u8*)ale - base_ni->attr_list)); /* Set new runlist. */ free(base_ni->attr_list); base_ni->attr_list = new_al; base_ni->attr_list_size = new_al_len; NInoAttrListSetDirty(base_ni); /* Done! */ ntfs_attr_close(na); return 0; err_out: if (na) ntfs_attr_close(na); free(new_al); errno = err; return -1; }
/** * ntfs_attrlist_entry_add - add an attribute list attribute entry * @ni: opened ntfs inode, which contains that attribute * @attr: attribute record to add to attribute list * * Return 0 on success and -1 on error with errno set to the error code. The * following error codes are defined: * EINVAL - Invalid arguments passed to function. * ENOMEM - Not enough memory to allocate necessary buffers. * EIO - I/O error occurred or damaged filesystem. * EEXIST - Such attribute already present in attribute list. */ int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) { ATTR_LIST_ENTRY *ale; leMFT_REF mref; ntfs_attr *na = NULL; ntfs_attr_search_ctx *ctx; u8 *new_al; int entry_len, entry_offset, err; ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (long long) ni->mft_no, (unsigned) le32_to_cpu(attr->type)); if (!ni || !attr) { ntfs_log_trace("Invalid arguments.\n"); errno = EINVAL; return -1; } mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); if (ni->nr_extents == -1) ni = ni->u.base_ni; if (!NInoAttrList(ni)) { ntfs_log_trace("Attribute list isn't present.\n"); errno = ENOENT; return -1; } /* Determine size and allocate memory for new attribute list. */ entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * attr->name_length + 7) & ~7; new_al = malloc(ni->attr_list_size + entry_len); if (!new_al) { ntfs_log_trace("Not enough memory.\n"); err = ENOMEM; return -1; } /* Find place for the new entry. */ ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { err = errno; ntfs_log_trace("Failed to obtain attribute search context.\n"); goto err_out; } if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*) ((u8*)attr + le16_to_cpu(attr->name_offset)) : AT_UNNAMED, attr->name_length, CASE_SENSITIVE, (attr->non_resident) ? sle64_to_cpu(attr->u.nonres.lowest_vcn) : 0, (attr->non_resident) ? NULL : ((u8*)attr + le16_to_cpu(attr->u.res.value_offset)), (attr->non_resident) ? 0 : le32_to_cpu(attr->u.res.value_length), ctx)) { /* Found some extent, check it to be before new extent. */ if (ctx->al_entry->lowest_vcn == attr->u.nonres.lowest_vcn) { err = EEXIST; ntfs_log_trace("Such attribute already present in the " "attribute list.\n"); ntfs_attr_put_search_ctx(ctx); goto err_out; } /* Add new entry after this extent. */ ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry + le16_to_cpu(ctx->al_entry->length)); } else { /* Check for real errors. */ if (errno != ENOENT) { err = errno; ntfs_log_trace("Attribute lookup failed.\n"); ntfs_attr_put_search_ctx(ctx); goto err_out; } /* No previous extents found. */ ale = ctx->al_entry; } /* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */ ntfs_attr_put_search_ctx(ctx); /* Determine new entry offset. */ entry_offset = ((u8 *)ale - ni->attr_list); /* Set pointer to new entry. */ ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset); /* Form new entry. */ ale->type = attr->type; ale->length = cpu_to_le16(entry_len); ale->name_length = attr->name_length; ale->name_offset = offsetof(ATTR_LIST_ENTRY, name); if (attr->non_resident) ale->lowest_vcn = attr->u.nonres.lowest_vcn; else ale->lowest_vcn = 0; ale->mft_reference = mref; ale->instance = attr->instance; NTFS_ON_DEBUG(memset(ale->name, 0, ((u8*)((u8*)ale + entry_len)) - ((u8*)ale->name))); /* Shut up, valgrind. */ memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset), attr->name_length * sizeof(ntfschar)); /* Resize $ATTRIBUTE_LIST to new length. */ na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); if (!na) { err = errno; ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); goto err_out; } if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) { err = errno; ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); goto err_out; } /* Copy entries from old attribute list to new. */ memcpy(new_al, ni->attr_list, entry_offset); memcpy(new_al + entry_offset + entry_len, ni->attr_list + entry_offset, ni->attr_list_size - entry_offset); /* Set new runlist. */ free(ni->attr_list); ni->attr_list = new_al; ni->attr_list_size = ni->attr_list_size + entry_len; NInoAttrListSetDirty(ni); /* Done! */ ntfs_attr_close(na); return 0; err_out: if (na) ntfs_attr_close(na); free(new_al); errno = err; return -1; }
/** * 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 - write the inode (and its dirty extents) to disk * @ni: ntfs inode to write * * Write the inode @ni to disk as well as its dirty extent inodes if such * exist and @ni is a base inode. If @ni is an extent inode, only @ni is * written completely disregarding its base inode and any other extent inodes. * * For a base inode with dirty extent inodes if any writes fail for whatever * reason, the failing inode is skipped and the sync process is continued. At * the end the error condition that brought about the failure is returned. Thus * the smallest amount of data loss possible occurs. * * 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. * EBUSY - Inode and/or one of its extents is busy, try again later. * EIO - I/O error while writing the inode (or one of its extents). */ int ntfs_inode_sync(ntfs_inode *ni) { int ret = 0; int err = 0; if (!ni) { errno = EINVAL; ntfs_log_error("Failed to sync NULL inode\n"); return -1; } ntfs_log_enter("Entering for inode %lld\n", (long long)ni->mft_no); /* Update STANDARD_INFORMATION. */ if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && ntfs_inode_sync_standard_information(ni)) { if (!err || errno == EIO) { err = errno; if (err != EIO) err = EBUSY; } } /* Update FILE_NAME's in the index. */ if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && NInoFileNameTestAndClearDirty(ni) && ntfs_inode_sync_file_name(ni)) { if (!err || errno == EIO) { err = errno; if (err != EIO) err = EBUSY; } ntfs_log_perror("Failed to sync FILE_NAME (inode %lld)", (long long)ni->mft_no); NInoFileNameSetDirty(ni); } /* Write out attribute list from cache to disk. */ if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && NInoAttrList(ni) && NInoAttrListTestAndClearDirty(ni)) { ntfs_attr *na; na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); if (!na) { if (!err || errno == EIO) { err = errno; if (err != EIO) err = EBUSY; ntfs_log_perror("Attribute list sync failed " "(open, inode %lld)", (long long)ni->mft_no); } NInoAttrListSetDirty(ni); goto sync_inode; } if (na->data_size == ni->attr_list_size) { if (ntfs_attr_pwrite(na, 0, ni->attr_list_size, ni->attr_list) != ni->attr_list_size) { if (!err || errno == EIO) { err = errno; if (err != EIO) err = EBUSY; ntfs_log_perror("Attribute list sync " "failed (write, inode %lld)", (long long)ni->mft_no); } NInoAttrListSetDirty(ni); } } else { err = EIO; ntfs_log_error("Attribute list sync failed (bad size, " "inode %lld)\n", (long long)ni->mft_no); NInoAttrListSetDirty(ni); } ntfs_attr_close(na); } sync_inode: /* Write this inode out to the $MFT (and $MFTMirr if applicable). */ if (NInoTestAndClearDirty(ni)) { if (ntfs_mft_record_write(ni->vol, ni->mft_no, ni->mrec)) { if (!err || errno == EIO) { err = errno; if (err != EIO) err = EBUSY; } NInoSetDirty(ni); ntfs_log_perror("MFT record sync failed, inode %lld", (long long)ni->mft_no); } } /* If this is a base inode with extents write all dirty extents, too. */ if (ni->nr_extents > 0) { s32 i; for (i = 0; i < ni->nr_extents; ++i) { ntfs_inode *eni; eni = ni->extent_nis[i]; if (!NInoTestAndClearDirty(eni)) continue; if (ntfs_mft_record_write(eni->vol, eni->mft_no, eni->mrec)) { if (!err || errno == EIO) { err = errno; if (err != EIO) err = EBUSY; } NInoSetDirty(eni); ntfs_log_perror("Extent MFT record sync failed," " inode %lld/%lld", (long long)ni->mft_no, (long long)eni->mft_no); } } } if (err) { errno = err; ret = -1; } ntfs_log_leave("\n"); return ret; }