Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * truncate.c
0003  *
0004  * PURPOSE
0005  *  Truncate handling routines for the OSTA-UDF(tm) filesystem.
0006  *
0007  * COPYRIGHT
0008  *  This file is distributed under the terms of the GNU General Public
0009  *  License (GPL). Copies of the GPL can be obtained from:
0010  *      ftp://prep.ai.mit.edu/pub/gnu/GPL
0011  *  Each contributing author retains all rights to their own work.
0012  *
0013  *  (C) 1999-2004 Ben Fennema
0014  *  (C) 1999 Stelias Computing Inc
0015  *
0016  * HISTORY
0017  *
0018  *  02/24/99 blf  Created.
0019  *
0020  */
0021 
0022 #include "udfdecl.h"
0023 #include <linux/fs.h>
0024 #include <linux/mm.h>
0025 
0026 #include "udf_i.h"
0027 #include "udf_sb.h"
0028 
0029 static void extent_trunc(struct inode *inode, struct extent_position *epos,
0030              struct kernel_lb_addr *eloc, int8_t etype, uint32_t elen,
0031              uint32_t nelen)
0032 {
0033     struct kernel_lb_addr neloc = {};
0034     int last_block = (elen + inode->i_sb->s_blocksize - 1) >>
0035         inode->i_sb->s_blocksize_bits;
0036     int first_block = (nelen + inode->i_sb->s_blocksize - 1) >>
0037         inode->i_sb->s_blocksize_bits;
0038 
0039     if (nelen) {
0040         if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) {
0041             udf_free_blocks(inode->i_sb, inode, eloc, 0,
0042                     last_block);
0043             etype = (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30);
0044         } else
0045             neloc = *eloc;
0046         nelen = (etype << 30) | nelen;
0047     }
0048 
0049     if (elen != nelen) {
0050         udf_write_aext(inode, epos, &neloc, nelen, 0);
0051         if (last_block > first_block) {
0052             if (etype == (EXT_RECORDED_ALLOCATED >> 30))
0053                 mark_inode_dirty(inode);
0054 
0055             if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
0056                 udf_free_blocks(inode->i_sb, inode, eloc,
0057                         first_block,
0058                         last_block - first_block);
0059         }
0060     }
0061 }
0062 
0063 /*
0064  * Truncate the last extent to match i_size. This function assumes
0065  * that preallocation extent is already truncated.
0066  */
0067 void udf_truncate_tail_extent(struct inode *inode)
0068 {
0069     struct extent_position epos = {};
0070     struct kernel_lb_addr eloc;
0071     uint32_t elen, nelen;
0072     uint64_t lbcount = 0;
0073     int8_t etype = -1, netype;
0074     int adsize;
0075     struct udf_inode_info *iinfo = UDF_I(inode);
0076 
0077     if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB ||
0078         inode->i_size == iinfo->i_lenExtents)
0079         return;
0080     /* Are we going to delete the file anyway? */
0081     if (inode->i_nlink == 0)
0082         return;
0083 
0084     if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
0085         adsize = sizeof(struct short_ad);
0086     else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
0087         adsize = sizeof(struct long_ad);
0088     else
0089         BUG();
0090 
0091     /* Find the last extent in the file */
0092     while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) {
0093         etype = netype;
0094         lbcount += elen;
0095         if (lbcount > inode->i_size) {
0096             if (lbcount - inode->i_size >= inode->i_sb->s_blocksize)
0097                 udf_warn(inode->i_sb,
0098                      "Too long extent after EOF in inode %u: i_size: %lld lbcount: %lld extent %u+%u\n",
0099                      (unsigned)inode->i_ino,
0100                      (long long)inode->i_size,
0101                      (long long)lbcount,
0102                      (unsigned)eloc.logicalBlockNum,
0103                      (unsigned)elen);
0104             nelen = elen - (lbcount - inode->i_size);
0105             epos.offset -= adsize;
0106             extent_trunc(inode, &epos, &eloc, etype, elen, nelen);
0107             epos.offset += adsize;
0108             if (udf_next_aext(inode, &epos, &eloc, &elen, 1) != -1)
0109                 udf_err(inode->i_sb,
0110                     "Extent after EOF in inode %u\n",
0111                     (unsigned)inode->i_ino);
0112             break;
0113         }
0114     }
0115     /* This inode entry is in-memory only and thus we don't have to mark
0116      * the inode dirty */
0117     iinfo->i_lenExtents = inode->i_size;
0118     brelse(epos.bh);
0119 }
0120 
0121 void udf_discard_prealloc(struct inode *inode)
0122 {
0123     struct extent_position epos = { NULL, 0, {0, 0} };
0124     struct kernel_lb_addr eloc;
0125     uint32_t elen;
0126     uint64_t lbcount = 0;
0127     int8_t etype = -1, netype;
0128     int adsize;
0129     struct udf_inode_info *iinfo = UDF_I(inode);
0130 
0131     if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB ||
0132         inode->i_size == iinfo->i_lenExtents)
0133         return;
0134 
0135     if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
0136         adsize = sizeof(struct short_ad);
0137     else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
0138         adsize = sizeof(struct long_ad);
0139     else
0140         adsize = 0;
0141 
0142     epos.block = iinfo->i_location;
0143 
0144     /* Find the last extent in the file */
0145     while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) {
0146         etype = netype;
0147         lbcount += elen;
0148     }
0149     if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) {
0150         epos.offset -= adsize;
0151         lbcount -= elen;
0152         extent_trunc(inode, &epos, &eloc, etype, elen, 0);
0153         if (!epos.bh) {
0154             iinfo->i_lenAlloc =
0155                 epos.offset -
0156                 udf_file_entry_alloc_offset(inode);
0157             mark_inode_dirty(inode);
0158         } else {
0159             struct allocExtDesc *aed =
0160                 (struct allocExtDesc *)(epos.bh->b_data);
0161             aed->lengthAllocDescs =
0162                 cpu_to_le32(epos.offset -
0163                         sizeof(struct allocExtDesc));
0164             if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) ||
0165                 UDF_SB(inode->i_sb)->s_udfrev >= 0x0201)
0166                 udf_update_tag(epos.bh->b_data, epos.offset);
0167             else
0168                 udf_update_tag(epos.bh->b_data,
0169                            sizeof(struct allocExtDesc));
0170             mark_buffer_dirty_inode(epos.bh, inode);
0171         }
0172     }
0173     /* This inode entry is in-memory only and thus we don't have to mark
0174      * the inode dirty */
0175     iinfo->i_lenExtents = lbcount;
0176     brelse(epos.bh);
0177 }
0178 
0179 static void udf_update_alloc_ext_desc(struct inode *inode,
0180                       struct extent_position *epos,
0181                       u32 lenalloc)
0182 {
0183     struct super_block *sb = inode->i_sb;
0184     struct udf_sb_info *sbi = UDF_SB(sb);
0185 
0186     struct allocExtDesc *aed = (struct allocExtDesc *) (epos->bh->b_data);
0187     int len = sizeof(struct allocExtDesc);
0188 
0189     aed->lengthAllocDescs = cpu_to_le32(lenalloc);
0190     if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT) || sbi->s_udfrev >= 0x0201)
0191         len += lenalloc;
0192 
0193     udf_update_tag(epos->bh->b_data, len);
0194     mark_buffer_dirty_inode(epos->bh, inode);
0195 }
0196 
0197 /*
0198  * Truncate extents of inode to inode->i_size. This function can be used only
0199  * for making file shorter. For making file longer, udf_extend_file() has to
0200  * be used.
0201  */
0202 int udf_truncate_extents(struct inode *inode)
0203 {
0204     struct extent_position epos;
0205     struct kernel_lb_addr eloc, neloc = {};
0206     uint32_t elen, nelen = 0, indirect_ext_len = 0, lenalloc;
0207     int8_t etype;
0208     struct super_block *sb = inode->i_sb;
0209     sector_t first_block = inode->i_size >> sb->s_blocksize_bits, offset;
0210     loff_t byte_offset;
0211     int adsize;
0212     struct udf_inode_info *iinfo = UDF_I(inode);
0213 
0214     if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
0215         adsize = sizeof(struct short_ad);
0216     else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
0217         adsize = sizeof(struct long_ad);
0218     else
0219         BUG();
0220 
0221     etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
0222     byte_offset = (offset << sb->s_blocksize_bits) +
0223         (inode->i_size & (sb->s_blocksize - 1));
0224     if (etype == -1) {
0225         /* We should extend the file? */
0226         WARN_ON(byte_offset);
0227         return 0;
0228     }
0229     epos.offset -= adsize;
0230     extent_trunc(inode, &epos, &eloc, etype, elen, byte_offset);
0231     epos.offset += adsize;
0232     if (byte_offset)
0233         lenalloc = epos.offset;
0234     else
0235         lenalloc = epos.offset - adsize;
0236 
0237     if (!epos.bh)
0238         lenalloc -= udf_file_entry_alloc_offset(inode);
0239     else
0240         lenalloc -= sizeof(struct allocExtDesc);
0241 
0242     while ((etype = udf_current_aext(inode, &epos, &eloc,
0243                      &elen, 0)) != -1) {
0244         if (etype == (EXT_NEXT_EXTENT_ALLOCDESCS >> 30)) {
0245             udf_write_aext(inode, &epos, &neloc, nelen, 0);
0246             if (indirect_ext_len) {
0247                 /* We managed to free all extents in the
0248                  * indirect extent - free it too */
0249                 BUG_ON(!epos.bh);
0250                 udf_free_blocks(sb, NULL, &epos.block,
0251                         0, indirect_ext_len);
0252             } else if (!epos.bh) {
0253                 iinfo->i_lenAlloc = lenalloc;
0254                 mark_inode_dirty(inode);
0255             } else
0256                 udf_update_alloc_ext_desc(inode,
0257                         &epos, lenalloc);
0258             brelse(epos.bh);
0259             epos.offset = sizeof(struct allocExtDesc);
0260             epos.block = eloc;
0261             epos.bh = udf_tread(sb,
0262                     udf_get_lb_pblock(sb, &eloc, 0));
0263             /* Error reading indirect block? */
0264             if (!epos.bh)
0265                 return -EIO;
0266             if (elen)
0267                 indirect_ext_len =
0268                     (elen + sb->s_blocksize - 1) >>
0269                     sb->s_blocksize_bits;
0270             else
0271                 indirect_ext_len = 1;
0272         } else {
0273             extent_trunc(inode, &epos, &eloc, etype, elen, 0);
0274             epos.offset += adsize;
0275         }
0276     }
0277 
0278     if (indirect_ext_len) {
0279         BUG_ON(!epos.bh);
0280         udf_free_blocks(sb, NULL, &epos.block, 0, indirect_ext_len);
0281     } else if (!epos.bh) {
0282         iinfo->i_lenAlloc = lenalloc;
0283         mark_inode_dirty(inode);
0284     } else
0285         udf_update_alloc_ext_desc(inode, &epos, lenalloc);
0286     iinfo->i_lenExtents = inode->i_size;
0287 
0288     brelse(epos.bh);
0289     return 0;
0290 }