+ /* Map, pin, and lock the (base) mft record. */
+ if (!NInoAttr(ni))
+ base_ni = ni;
+ else
+ base_ni = ni->ext.base_ntfs_ino;
+ m = map_mft_record(base_ni);
+ if (IS_ERR(m)) {
+ err = PTR_ERR(m);
+ m = NULL;
+ ctx = NULL;
+ goto err_out;
+ }
+ ctx = ntfs_attr_get_search_ctx(base_ni, m);
+ if (unlikely(!ctx)) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+ CASE_SENSITIVE, 0, NULL, 0, ctx);
+ if (unlikely(err)) {
+ if (err == -ENOENT)
+ err = -EIO;
+ goto err_out;
+ }
+ m = ctx->mrec;
+ a = ctx->attr;
+ /* The total length of the attribute value. */
+ attr_len = le32_to_cpu(a->data.resident.value_length);
+ BUG_ON(vi->i_size != attr_len);
+ /* Check if new size is allowed in $AttrDef. */
+ err = ntfs_attr_size_bounds_check(vol, ni->type, new_size);
+ if (unlikely(err)) {
+ if (err == -ERANGE) {
+ ntfs_error(vol->sb, "Write would cause the inode "
+ "0x%lx to exceed the maximum size for "
+ "its attribute type (0x%x). Aborting "
+ "write.", vi->i_ino,
+ le32_to_cpu(ni->type));
+ } else {
+ ntfs_error(vol->sb, "Inode 0x%lx has unknown "
+ "attribute type 0x%x. Aborting "
+ "write.", vi->i_ino,
+ le32_to_cpu(ni->type));
+ err = -EIO;
+ }
+ goto err_out2;
+ }
+ /*
+ * Extend the attribute record to be able to store the new attribute
+ * size.
+ */
+ if (new_size >= vol->mft_record_size || ntfs_attr_record_resize(m, a,
+ le16_to_cpu(a->data.resident.value_offset) +
+ new_size)) {
+ /* Not enough space in the mft record. */
+ ntfs_error(vol->sb, "Not enough space in the mft record for "
+ "the resized attribute value. This is not "
+ "supported yet. Aborting write.");
+ err = -EOPNOTSUPP;
+ goto err_out2;
+ }
+ /*
+ * We have enough space in the mft record to fit the write. This
+ * implies the attribute is smaller than the mft record and hence the
+ * attribute must be in a single page and hence page->index must be 0.
+ */
+ BUG_ON(page->index);
+ /*
+ * If the beginning of the write is past the old size, enlarge the
+ * attribute value up to the beginning of the write and fill it with
+ * zeroes.
+ */
+ if (from > attr_len) {
+ memset((u8*)a + le16_to_cpu(a->data.resident.value_offset) +
+ attr_len, 0, from - attr_len);
+ a->data.resident.value_length = cpu_to_le32(from);
+ /* Zero the corresponding area in the page as well. */
+ if (PageUptodate(page)) {
+ kaddr = kmap_atomic(page, KM_USER0);
+ memset(kaddr + attr_len, 0, from - attr_len);
+ kunmap_atomic(kaddr, KM_USER0);
+ flush_dcache_page(page);
+ }
+ }
+ flush_dcache_mft_record_page(ctx->ntfs_ino);
+ mark_mft_record_dirty(ctx->ntfs_ino);
+ ntfs_attr_put_search_ctx(ctx);
+ unmap_mft_record(base_ni);