Received: by 2002:a25:ad19:0:0:0:0:0 with SMTP id y25csp846629ybi; Fri, 12 Jul 2019 05:37:40 -0700 (PDT) X-Google-Smtp-Source: APXvYqx+voVHuVlyp1G7iCcWYeEYX1vtWNUyuh9oYcycpTH6hk2Wj9L12J6+9a8gfBVKeF1JXgXw X-Received: by 2002:a65:464d:: with SMTP id k13mr9537146pgr.99.1562935060637; Fri, 12 Jul 2019 05:37:40 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1562935060; cv=none; d=google.com; s=arc-20160816; b=yMMeopHRpKDb2vkkR8hJttkLLZp+5+q8RVbciRdjzCDP7UWCQczUtm5bdwkL5til5s FeHXCPuR+LkKV/kYQYGgwKff+SV2Rdqnyqu6F0Oi82vQdEgddUqe9J0HSDhCon0QCNhD aPh0Rqay4zB5vL6OqHdUlXxILKNsC07ovqTTBVN1OUgJfTuW0wEuqZbjRmLA3IUEF4F3 fFOAj0f+kR59hdwGCJniuFS1vkFg7/41v0ggnUZKk6y61bxOKrAu4EPJSYEDM5D61d3D U8JzNc1AHks+0ESbjgQAo5kJqGAbIU7TuuMI0R7SXYEfMozuOa4mTfOXzV6Afo8c8oOv h5yQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=RFC06PF9cHgG1EFqy2dFJE8YJgURA5d6fnbB479gj90=; b=kKKEeV0ho6FTP1BDnfjU09OshmZxWXa7GMlSPBy0uZy5Xw+NvbQoT5EMGwXmFdOJoW bIEwLJBUPF44qk3B5T6tqHcUARP9lVG3IQMINVvykPHobRzgJVuo0Iwqjmbb81cJrQtJ 7t0i7NhkiqvpjSvnG54g/uwfVZFDum3UmgGHNyH5uywV4pMtAm33kRFVCJYECdW7A9BY tG1cK+sS3pxFv2B53A05eLzT45L/+op8QtRfOb+AqfmA7hWjhWAVCOa9WbL/9oJCFDbX U7cNnNa4md5XH/B2P/9g2zsxeu+DAtbXcBH2RAVQI0ZqxSQrxnsFGELeD3Wynxfplg/V v2nw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=cN2Ui7O5; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id bj12si7310633plb.378.2019.07.12.05.37.25; Fri, 12 Jul 2019 05:37:40 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=cN2Ui7O5; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729488AbfGLMfr (ORCPT + 99 others); Fri, 12 Jul 2019 08:35:47 -0400 Received: from mail.kernel.org ([198.145.29.99]:53392 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729668AbfGLMdx (ORCPT ); Fri, 12 Jul 2019 08:33:53 -0400 Received: from localhost (83-86-89-107.cable.dynamic.v4.ziggo.nl [83.86.89.107]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 106302166E; Fri, 12 Jul 2019 12:33:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1562934832; bh=/T3rBfyNMd4s89OXKH3VPrhhGzsi43ZonvJrirmgypo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cN2Ui7O5nTS/hiYi3q29ibwrqTAFjJ7e/Ijk8VbSqTfAOWlAAkPTB10+65CHfMJ0h 90hrl5MxdC+UkCipge1F84D8izpXN+oZ0X3bsmPTMhmAD56et9CnXNF3JYgvC8vHka YIY+XO7EUnFuJ4+ENbB3Mu9UabAKdrnuj8/qVciU= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, "Steven J. Magnani" , Jan Kara Subject: [PATCH 5.2 05/61] udf: Fix incorrect final NOT_ALLOCATED (hole) extent length Date: Fri, 12 Jul 2019 14:19:18 +0200 Message-Id: <20190712121620.907410989@linuxfoundation.org> X-Mailer: git-send-email 2.22.0 In-Reply-To: <20190712121620.632595223@linuxfoundation.org> References: <20190712121620.632595223@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Steven J. Magnani commit fa33cdbf3eceb0206a4f844fe91aeebcf6ff2b7a upstream. In some cases, using the 'truncate' command to extend a UDF file results in a mismatch between the length of the file's extents (specifically, due to incorrect length of the final NOT_ALLOCATED extent) and the information (file) length. The discrepancy can prevent other operating systems (i.e., Windows 10) from opening the file. Two particular errors have been observed when extending a file: 1. The final extent is larger than it should be, having been rounded up to a multiple of the block size. B. The final extent is not shorter than it should be, due to not having been updated when the file's information length was increased. [JK: simplified udf_do_extend_final_block(), fixed up some types] Fixes: 2c948b3f86e5 ("udf: Avoid IO in udf_clear_inode") CC: stable@vger.kernel.org Signed-off-by: Steven J. Magnani Link: https://lore.kernel.org/r/1561948775-5878-1-git-send-email-steve@digidescorp.com Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/udf/inode.c | 93 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 33 deletions(-) --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -470,13 +470,15 @@ static struct buffer_head *udf_getblk(st return NULL; } -/* Extend the file by 'blocks' blocks, return the number of extents added */ +/* Extend the file with new blocks totaling 'new_block_bytes', + * return the number of extents added + */ static int udf_do_extend_file(struct inode *inode, struct extent_position *last_pos, struct kernel_long_ad *last_ext, - sector_t blocks) + loff_t new_block_bytes) { - sector_t add; + uint32_t add; int count = 0, fake = !(last_ext->extLength & UDF_EXTENT_LENGTH_MASK); struct super_block *sb = inode->i_sb; struct kernel_lb_addr prealloc_loc = {}; @@ -486,7 +488,7 @@ static int udf_do_extend_file(struct ino /* The previous extent is fake and we should not extend by anything * - there's nothing to do... */ - if (!blocks && fake) + if (!new_block_bytes && fake) return 0; iinfo = UDF_I(inode); @@ -517,13 +519,12 @@ static int udf_do_extend_file(struct ino /* Can we merge with the previous extent? */ if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) == EXT_NOT_RECORDED_NOT_ALLOCATED) { - add = ((1 << 30) - sb->s_blocksize - - (last_ext->extLength & UDF_EXTENT_LENGTH_MASK)) >> - sb->s_blocksize_bits; - if (add > blocks) - add = blocks; - blocks -= add; - last_ext->extLength += add << sb->s_blocksize_bits; + add = (1 << 30) - sb->s_blocksize - + (last_ext->extLength & UDF_EXTENT_LENGTH_MASK); + if (add > new_block_bytes) + add = new_block_bytes; + new_block_bytes -= add; + last_ext->extLength += add; } if (fake) { @@ -544,28 +545,27 @@ static int udf_do_extend_file(struct ino } /* Managed to do everything necessary? */ - if (!blocks) + if (!new_block_bytes) goto out; /* All further extents will be NOT_RECORDED_NOT_ALLOCATED */ last_ext->extLocation.logicalBlockNum = 0; last_ext->extLocation.partitionReferenceNum = 0; - add = (1 << (30-sb->s_blocksize_bits)) - 1; - last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | - (add << sb->s_blocksize_bits); + add = (1 << 30) - sb->s_blocksize; + last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | add; /* Create enough extents to cover the whole hole */ - while (blocks > add) { - blocks -= add; + while (new_block_bytes > add) { + new_block_bytes -= add; err = udf_add_aext(inode, last_pos, &last_ext->extLocation, last_ext->extLength, 1); if (err) return err; count++; } - if (blocks) { + if (new_block_bytes) { last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | - (blocks << sb->s_blocksize_bits); + new_block_bytes; err = udf_add_aext(inode, last_pos, &last_ext->extLocation, last_ext->extLength, 1); if (err) @@ -596,6 +596,24 @@ out: return count; } +/* Extend the final block of the file to final_block_len bytes */ +static void udf_do_extend_final_block(struct inode *inode, + struct extent_position *last_pos, + struct kernel_long_ad *last_ext, + uint32_t final_block_len) +{ + struct super_block *sb = inode->i_sb; + uint32_t added_bytes; + + added_bytes = final_block_len - + (last_ext->extLength & (sb->s_blocksize - 1)); + last_ext->extLength += added_bytes; + UDF_I(inode)->i_lenExtents += added_bytes; + + udf_write_aext(inode, last_pos, &last_ext->extLocation, + last_ext->extLength, 1); +} + static int udf_extend_file(struct inode *inode, loff_t newsize) { @@ -605,10 +623,12 @@ static int udf_extend_file(struct inode int8_t etype; struct super_block *sb = inode->i_sb; sector_t first_block = newsize >> sb->s_blocksize_bits, offset; + unsigned long partial_final_block; int adsize; struct udf_inode_info *iinfo = UDF_I(inode); struct kernel_long_ad extent; - int err; + int err = 0; + int within_final_block; if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) adsize = sizeof(struct short_ad); @@ -618,18 +638,8 @@ static int udf_extend_file(struct inode BUG(); etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset); + within_final_block = (etype != -1); - /* File has extent covering the new size (could happen when extending - * inside a block)? */ - if (etype != -1) - return 0; - if (newsize & (sb->s_blocksize - 1)) - offset++; - /* Extended file just to the boundary of the last file block? */ - if (offset == 0) - return 0; - - /* Truncate is extending the file by 'offset' blocks */ if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) || (epos.bh && epos.offset == sizeof(struct allocExtDesc))) { /* File has no extents at all or has empty last @@ -643,7 +653,22 @@ static int udf_extend_file(struct inode &extent.extLength, 0); extent.extLength |= etype << 30; } - err = udf_do_extend_file(inode, &epos, &extent, offset); + + partial_final_block = newsize & (sb->s_blocksize - 1); + + /* File has extent covering the new size (could happen when extending + * inside a block)? + */ + if (within_final_block) { + /* Extending file within the last file block */ + udf_do_extend_final_block(inode, &epos, &extent, + partial_final_block); + } else { + loff_t add = ((loff_t)offset << sb->s_blocksize_bits) | + partial_final_block; + err = udf_do_extend_file(inode, &epos, &extent, add); + } + if (err < 0) goto out; err = 0; @@ -745,6 +770,7 @@ static sector_t inode_getblk(struct inod /* Are we beyond EOF? */ if (etype == -1) { int ret; + loff_t hole_len; isBeyondEOF = true; if (count) { if (c) @@ -760,7 +786,8 @@ static sector_t inode_getblk(struct inod startnum = (offset > 0); } /* Create extents for the hole between EOF and offset */ - ret = udf_do_extend_file(inode, &prev_epos, laarr, offset); + hole_len = (loff_t)offset << inode->i_blkbits; + ret = udf_do_extend_file(inode, &prev_epos, laarr, hole_len); if (ret < 0) { *err = ret; newblock = 0;